diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index a5b4f223641d909cac3bd09777a9502840530f13..8127a08e366d8f96b05f0dacf71cd55634f68a1d 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -199,7 +199,7 @@ Description: What: /sys/bus/iio/devices/iio:deviceX/in_positionrelative_x_raw What: /sys/bus/iio/devices/iio:deviceX/in_positionrelative_y_raw -KernelVersion: 4.18 +KernelVersion: 4.19 Contact: linux-iio@vger.kernel.org Description: Relative position in direction x or y on a pad (may be diff --git a/Documentation/ABI/testing/sysfs-bus-mei b/Documentation/ABI/testing/sysfs-bus-mei index 6bd45346ac7e4d20ffb445723fe94b5e32d98b3a..3f8701e8fa241d4a2a74ea29ca1cd9772d9360c9 100644 --- a/Documentation/ABI/testing/sysfs-bus-mei +++ b/Documentation/ABI/testing/sysfs-bus-mei @@ -4,7 +4,7 @@ KernelVersion: 3.10 Contact: Samuel Ortiz linux-mei@linux.intel.com Description: Stores the same MODALIAS value emitted by uevent - Format: mei::: + Format: mei::: What: /sys/bus/mei/devices/.../name Date: May 2015 diff --git a/Documentation/ABI/testing/sysfs-class-devfreq b/Documentation/ABI/testing/sysfs-class-devfreq index ee39acacf6f8512cb81eedaf0d06685bbf004144..335595a79866c6b7ce6b8d50679098e3c23681e2 100644 --- a/Documentation/ABI/testing/sysfs-class-devfreq +++ b/Documentation/ABI/testing/sysfs-class-devfreq @@ -7,6 +7,13 @@ Description: The name of devfreq object denoted as ... is same as the name of device using devfreq. +What: /sys/class/devfreq/.../name +Date: November 2019 +Contact: Chanwoo Choi +Description: + The /sys/class/devfreq/.../name shows the name of device + of the corresponding devfreq object. + What: /sys/class/devfreq/.../governor Date: September 2011 Contact: MyungJoo Ham diff --git a/Documentation/ABI/testing/sysfs-class-wakeup b/Documentation/ABI/testing/sysfs-class-wakeup new file mode 100644 index 0000000000000000000000000000000000000000..754aab8b6dcd6005b8ab64c38282c54df481d3a6 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-wakeup @@ -0,0 +1,76 @@ +What: /sys/class/wakeup/ +Date: June 2019 +Contact: Tri Vo +Description: + The /sys/class/wakeup/ directory contains pointers to all + wakeup sources in the kernel at that moment in time. + +What: /sys/class/wakeup/.../name +Date: June 2019 +Contact: Tri Vo +Description: + This file contains the name of the wakeup source. + +What: /sys/class/wakeup/.../active_count +Date: June 2019 +Contact: Tri Vo +Description: + This file contains the number of times the wakeup source was + activated. + +What: /sys/class/wakeup/.../event_count +Date: June 2019 +Contact: Tri Vo +Description: + This file contains the number of signaled wakeup events + associated with the wakeup source. + +What: /sys/class/wakeup/.../wakeup_count +Date: June 2019 +Contact: Tri Vo +Description: + This file contains the number of times the wakeup source might + abort suspend. + +What: /sys/class/wakeup/.../expire_count +Date: June 2019 +Contact: Tri Vo +Description: + This file contains the number of times the wakeup source's + timeout has expired. + +What: /sys/class/wakeup/.../active_time_ms +Date: June 2019 +Contact: Tri Vo +Description: + This file contains the amount of time the wakeup source has + been continuously active, in milliseconds. If the wakeup + source is not active, this file contains '0'. + +What: /sys/class/wakeup/.../total_time_ms +Date: June 2019 +Contact: Tri Vo +Description: + This file contains the total amount of time this wakeup source + has been active, in milliseconds. + +What: /sys/class/wakeup/.../max_time_ms +Date: June 2019 +Contact: Tri Vo +Description: + This file contains the maximum amount of time this wakeup + source has been continuously active, in milliseconds. + +What: /sys/class/wakeup/.../last_change_ms +Date: June 2019 +Contact: Tri Vo +Description: + This file contains the monotonic clock time when the wakeup + source was touched last time, in milliseconds. + +What: /sys/class/wakeup/.../prevent_suspend_time_ms +Date: June 2019 +Contact: Tri Vo +Description: + The file contains the total amount of time this wakeup source + has been preventing autosleep, in milliseconds. diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs index aedeae1e8ec180f699f32a7f05b6b6b81105372c..1a6cd539712928f8f75bb04e340540bddc2c9300 100644 --- a/Documentation/ABI/testing/sysfs-fs-f2fs +++ b/Documentation/ABI/testing/sysfs-fs-f2fs @@ -1,37 +1,40 @@ What: /sys/fs/f2fs//gc_max_sleep_time Date: July 2013 Contact: "Namjae Jeon" -Description: - Controls the maximun sleep time for gc_thread. Time - is in milliseconds. +Description: Controls the maximum sleep time for gc_thread. Time + is in milliseconds. What: /sys/fs/f2fs//gc_min_sleep_time Date: July 2013 Contact: "Namjae Jeon" -Description: - Controls the minimum sleep time for gc_thread. Time - is in milliseconds. +Description: Controls the minimum sleep time for gc_thread. Time + is in milliseconds. What: /sys/fs/f2fs//gc_no_gc_sleep_time Date: July 2013 Contact: "Namjae Jeon" -Description: - Controls the default sleep time for gc_thread. Time - is in milliseconds. +Description: Controls the default sleep time for gc_thread. Time + is in milliseconds. What: /sys/fs/f2fs//gc_idle Date: July 2013 Contact: "Namjae Jeon" -Description: - Controls the victim selection policy for garbage collection. +Description: Controls the victim selection policy for garbage collection. + Setting gc_idle = 0(default) will disable this option. Setting + gc_idle = 1 will select the Cost Benefit approach & setting + gc_idle = 2 will select the greedy approach. What: /sys/fs/f2fs//reclaim_segments Date: October 2013 Contact: "Jaegeuk Kim" -Description: - Controls the issue rate of segment discard commands. - -What: /sys/fs/f2fs//max_blkaddr +Description: This parameter controls the number of prefree segments to be + reclaimed. If the number of prefree segments is larger than + the number of segments in the proportion to the percentage + over total volume size, f2fs tries to conduct checkpoint to + reclaim the prefree segments to free segments. + By default, 5% over total # of segments. + +What: /sys/fs/f2fs//main_blkaddr Date: November 2019 Contact: "Ramon Pantin" Description: @@ -40,227 +43,278 @@ Description: What: /sys/fs/f2fs//ipu_policy Date: November 2013 Contact: "Jaegeuk Kim" -Description: - Controls the in-place-update policy. +Description: Controls the in-place-update policy. + updates in f2fs. User can set: + 0x01: F2FS_IPU_FORCE, 0x02: F2FS_IPU_SSR, + 0x04: F2FS_IPU_UTIL, 0x08: F2FS_IPU_SSR_UTIL, + 0x10: F2FS_IPU_FSYNC, 0x20: F2FS_IPU_ASYNC, + 0x40: F2FS_IPU_NOCACHE. + Refer segment.h for details. What: /sys/fs/f2fs//min_ipu_util Date: November 2013 Contact: "Jaegeuk Kim" -Description: - Controls the FS utilization condition for the in-place-update - policies. +Description: Controls the FS utilization condition for the in-place-update + policies. It is used by F2FS_IPU_UTIL and F2FS_IPU_SSR_UTIL policies. What: /sys/fs/f2fs//min_fsync_blocks Date: September 2014 Contact: "Jaegeuk Kim" -Description: - Controls the dirty page count condition for the in-place-update - policies. +Description: Controls the dirty page count condition for the in-place-update + policies. What: /sys/fs/f2fs//min_seq_blocks Date: August 2018 Contact: "Jaegeuk Kim" -Description: - Controls the dirty page count condition for batched sequential - writes in ->writepages. - +Description: Controls the dirty page count condition for batched sequential + writes in writepages. What: /sys/fs/f2fs//min_hot_blocks Date: March 2017 Contact: "Jaegeuk Kim" -Description: - Controls the dirty page count condition for redefining hot data. +Description: Controls the dirty page count condition for redefining hot data. What: /sys/fs/f2fs//min_ssr_sections Date: October 2017 Contact: "Chao Yu" -Description: - Controls the fee section threshold to trigger SSR allocation. +Description: Controls the free section threshold to trigger SSR allocation. + If this is large, SSR mode will be enabled early. What: /sys/fs/f2fs//max_small_discards Date: November 2013 Contact: "Jaegeuk Kim" -Description: - Controls the issue rate of small discard commands. +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. -What: /sys/fs/f2fs//discard_granularity -Date: July 2017 -Contact: "Chao Yu" -Description: - Controls discard granularity of inner discard thread, inner thread +What: /sys/fs/f2fs//discard_granularity +Date: July 2017 +Contact: "Chao Yu" +Description: Controls discard granularity of inner discard thread. Inner thread will not issue discards with size that is smaller than granularity. - The unit size is one block, now only support configuring in range - of [1, 512]. + The unit size is one block(4KB), now only support configuring + in range of [1, 512]. Default value is 4(=16KB). -What: /sys/fs/f2fs//umount_discard_timeout -Date: January 2019 -Contact: "Jaegeuk Kim" -Description: - Set timeout to issue discard commands during umount. - Default: 5 secs +What: /sys/fs/f2fs//umount_discard_timeout +Date: January 2019 +Contact: "Jaegeuk Kim" +Description: Set timeout to issue discard commands during umount. + Default: 5 secs What: /sys/fs/f2fs//max_victim_search Date: January 2014 Contact: "Jaegeuk Kim" -Description: - Controls the number of trials to find a victim segment. +Description: Controls the number of trials to find a victim segment + when conducting SSR and cleaning operations. The default value + is 4096 which covers 8GB block address range. What: /sys/fs/f2fs//migration_granularity Date: October 2018 Contact: "Chao Yu" -Description: - Controls migration granularity of garbage collection on large - section, it can let GC move partial segment{s} of one section - in one GC cycle, so that dispersing heavy overhead GC to - multiple lightweight one. +Description: Controls migration granularity of garbage collection on large + section, it can let GC move partial segment{s} of one section + in one GC cycle, so that dispersing heavy overhead GC to + multiple lightweight one. What: /sys/fs/f2fs//dir_level Date: March 2014 Contact: "Jaegeuk Kim" -Description: - Controls the directory level for large directory. +Description: Controls the directory level for large directory. If a + directory has a number of files, it can reduce the file lookup + latency by increasing this dir_level value. Otherwise, it + needs to decrease this value to reduce the space overhead. + The default value is 0. What: /sys/fs/f2fs//ram_thresh Date: March 2014 Contact: "Jaegeuk Kim" -Description: - Controls the memory footprint used by f2fs. +Description: Controls the memory footprint used by free nids and cached + nat entries. By default, 1 is set, which indicates + 10 MB / 1 GB RAM. What: /sys/fs/f2fs//batched_trim_sections Date: February 2015 Contact: "Jaegeuk Kim" -Description: - Controls the trimming rate in batch mode. - +Description: Controls the trimming rate in batch mode. + What: /sys/fs/f2fs//cp_interval Date: October 2015 Contact: "Jaegeuk Kim" -Description: - Controls the checkpoint timing. +Description: Controls the checkpoint timing, set to 60 seconds by default. What: /sys/fs/f2fs//idle_interval Date: January 2016 Contact: "Jaegeuk Kim" -Description: - Controls the idle timing for all paths other than - discard and gc path. +Description: Controls the idle timing of system, if there is no FS operation + during given interval. + Set to 5 seconds by default. What: /sys/fs/f2fs//discard_idle_interval Date: September 2018 Contact: "Chao Yu" Contact: "Sahitya Tummala" -Description: - Controls the idle timing for discard path. +Description: Controls the idle timing of discard thread given + this time interval. + Default is 5 secs. What: /sys/fs/f2fs//gc_idle_interval Date: September 2018 Contact: "Chao Yu" Contact: "Sahitya Tummala" -Description: - Controls the idle timing for gc path. +Description: Controls the idle timing for gc path. Set to 5 seconds by default. What: /sys/fs/f2fs//iostat_enable Date: August 2017 Contact: "Chao Yu" -Description: - Controls to enable/disable IO stat. +Description: Controls to enable/disable IO stat. What: /sys/fs/f2fs//ra_nid_pages Date: October 2015 Contact: "Chao Yu" -Description: - Controls the count of nid pages to be readaheaded. +Description: Controls the count of nid pages to be readaheaded. + When building free nids, F2FS reads NAT blocks ahead for + speed up. Default is 0. What: /sys/fs/f2fs//dirty_nats_ratio Date: January 2016 Contact: "Chao Yu" -Description: - Controls dirty nat entries ratio threshold, if current - ratio exceeds configured threshold, checkpoint will - be triggered for flushing dirty nat entries. +Description: Controls dirty nat entries ratio threshold, if current + ratio exceeds configured threshold, checkpoint will + be triggered for flushing dirty nat entries. What: /sys/fs/f2fs//lifetime_write_kbytes Date: January 2016 Contact: "Shuoran Liu" -Description: - Shows total written kbytes issued to disk. +Description: Shows total written kbytes issued to disk. What: /sys/fs/f2fs//features Date: July 2017 Contact: "Jaegeuk Kim" -Description: - Shows all enabled features in current device. +Description: Shows all enabled features in current device. What: /sys/fs/f2fs//inject_rate Date: May 2016 Contact: "Sheng Yong" -Description: - Controls the injection rate. +Description: Controls the injection rate of arbitrary faults. What: /sys/fs/f2fs//inject_type Date: May 2016 Contact: "Sheng Yong" -Description: - Controls the injection type. +Description: Controls the injection type of arbitrary faults. + +What: /sys/fs/f2fs//dirty_segments +Date: October 2017 +Contact: "Jaegeuk Kim" +Description: Shows the number of dirty segments. What: /sys/fs/f2fs//reserved_blocks Date: June 2017 Contact: "Chao Yu" -Description: - Controls target reserved blocks in system, the threshold - is soft, it could exceed current available user space. +Description: Controls target reserved blocks in system, the threshold + is soft, it could exceed current available user space. What: /sys/fs/f2fs//current_reserved_blocks Date: October 2017 Contact: "Yunlong Song" Contact: "Chao Yu" -Description: - Shows current reserved blocks in system, it may be temporarily - smaller than target_reserved_blocks, but will gradually - increase to target_reserved_blocks when more free blocks are - freed by user later. +Description: Shows current reserved blocks in system, it may be temporarily + smaller than target_reserved_blocks, but will gradually + increase to target_reserved_blocks when more free blocks are + freed by user later. What: /sys/fs/f2fs//gc_urgent Date: August 2017 Contact: "Jaegeuk Kim" -Description: - Do background GC agressively +Description: Do background GC agressively when set. When gc_urgent = 1, + background thread starts to do GC by given gc_urgent_sleep_time + interval. It is set to 0 by default. What: /sys/fs/f2fs//gc_urgent_sleep_time Date: August 2017 Contact: "Jaegeuk Kim" -Description: - Controls sleep time of GC urgent mode +Description: Controls sleep time of GC urgent mode. Set to 500ms by default. What: /sys/fs/f2fs//readdir_ra Date: November 2017 Contact: "Sheng Yong" -Description: - Controls readahead inode block in readdir. +Description: Controls readahead inode block in readdir. Enabled by default. + +What: /sys/fs/f2fs//gc_pin_file_thresh +Date: January 2018 +Contact: Jaegeuk Kim +Description: This indicates how many GC can be failed for the pinned + file. If it exceeds this, F2FS doesn't guarantee its pinning + state. 2048 trials is set by default. What: /sys/fs/f2fs//extension_list Date: Feburary 2018 Contact: "Chao Yu" -Description: - Used to control configure extension list: - - Query: cat /sys/fs/f2fs//extension_list - - Add: echo '[h/c]extension' > /sys/fs/f2fs//extension_list - - Del: echo '[h/c]!extension' > /sys/fs/f2fs//extension_list - - [h] means add/del hot file extension - - [c] means add/del cold file extension +Description: Used to control configure extension list: + - Query: cat /sys/fs/f2fs//extension_list + - Add: echo '[h/c]extension' > /sys/fs/f2fs//extension_list + - Del: echo '[h/c]!extension' > /sys/fs/f2fs//extension_list + - [h] means add/del hot file extension + - [c] means add/del cold file extension What: /sys/fs/f2fs//unusable Date April 2019 Contact: "Daniel Rosenberg" -Description: - If checkpoint=disable, it displays the number of blocks that are unusable. - If checkpoint=enable it displays the enumber of blocks that would be unusable - if checkpoint=disable were to be set. +Description: If checkpoint=disable, it displays the number of blocks that + are unusable. + If checkpoint=enable it displays the enumber of blocks that + would be unusable if checkpoint=disable were to be set. What: /sys/fs/f2fs//encoding Date July 2019 Contact: "Daniel Rosenberg" -Description: - Displays name and version of the encoding set for the filesystem. - If no encoding is set, displays (none) +Description: Displays name and version of the encoding set for the filesystem. + If no encoding is set, displays (none) + +What: /sys/fs/f2fs//free_segments +Date: September 2019 +Contact: "Hridya Valsaraju" +Description: Number of free segments in disk. + +What: /sys/fs/f2fs//cp_foreground_calls +Date: September 2019 +Contact: "Hridya Valsaraju" +Description: Number of checkpoint operations performed on demand. Available when + CONFIG_F2FS_STAT_FS=y. + +What: /sys/fs/f2fs//cp_background_calls +Date: September 2019 +Contact: "Hridya Valsaraju" +Description: Number of checkpoint operations performed in the background to + free segments. Available when CONFIG_F2FS_STAT_FS=y. + +What: /sys/fs/f2fs//gc_foreground_calls +Date: September 2019 +Contact: "Hridya Valsaraju" +Description: Number of garbage collection operations performed on demand. + Available when CONFIG_F2FS_STAT_FS=y. + +What: /sys/fs/f2fs//gc_background_calls +Date: September 2019 +Contact: "Hridya Valsaraju" +Description: Number of garbage collection operations triggered in background. + Available when CONFIG_F2FS_STAT_FS=y. + +What: /sys/fs/f2fs//moved_blocks_foreground +Date: September 2019 +Contact: "Hridya Valsaraju" +Description: Number of blocks moved by garbage collection in foreground. + Available when CONFIG_F2FS_STAT_FS=y. + +What: /sys/fs/f2fs//moved_blocks_background +Date: September 2019 +Contact: "Hridya Valsaraju" +Description: Number of blocks moved by garbage collection in background. + Available when CONFIG_F2FS_STAT_FS=y. + +What: /sys/fs/f2fs//avg_vblocks +Date: September 2019 +Contact: "Hridya Valsaraju" +Description: Average number of valid blocks. + Available when CONFIG_F2FS_STAT_FS=y. diff --git a/Documentation/admin-guide/cgroup-v2.rst b/Documentation/admin-guide/cgroup-v2.rst index 52d093b0a25074ef1af005a861d5f4a374c59cb0..15779d9ff681e2e5b9e96f41141c2547d1646d95 100644 --- a/Documentation/admin-guide/cgroup-v2.rst +++ b/Documentation/admin-guide/cgroup-v2.rst @@ -694,6 +694,12 @@ Conventions informational files on the root cgroup which end up showing global information available elsewhere shouldn't exist. +- The default time unit is microseconds. If a different unit is ever + used, an explicit unit suffix must be present. + +- A parts-per quantity should use a percentage decimal with at least + two digit fractional part - e.g. 13.40. + - If a controller implements weight based resource distribution, its interface file should be named "weight" and have the range [1, 10000] with 100 as the default. The values are chosen to allow @@ -907,6 +913,13 @@ controller implements weight and absolute bandwidth limit models for normal scheduling policy and absolute bandwidth allocation model for realtime scheduling policy. +In all the above models, cycles distribution is defined only on a temporal +base and it does not account for the frequency at which tasks are executed. +The (optional) utilization clamping support allows to hint the schedutil +cpufreq governor about the minimum desired frequency which should always be +provided by a CPU, as well as the maximum desired frequency, which should not +be exceeded by a CPU. + WARNING: cgroup2 doesn't yet support control of realtime processes and the cpu controller can only be enabled when all RT processes are in the root cgroup. Be aware that system management software may already @@ -972,6 +985,33 @@ All time durations are in microseconds. Shows pressure stall information for CPU. See Documentation/accounting/psi.txt for details. + cpu.uclamp.min + A read-write single value file which exists on non-root cgroups. + The default is "0", i.e. no utilization boosting. + + The requested minimum utilization (protection) as a percentage + rational number, e.g. 12.34 for 12.34%. + + This interface allows reading and setting minimum utilization clamp + values similar to the sched_setattr(2). This minimum utilization + value is used to clamp the task specific minimum utilization clamp. + + The requested minimum utilization (protection) is always capped by + the current value for the maximum utilization (limit), i.e. + `cpu.uclamp.max`. + + cpu.uclamp.max + A read-write single value file which exists on non-root cgroups. + The default is "max". i.e. no utilization capping + + The requested maximum utilization (limit) as a percentage rational + number, e.g. 98.76 for 98.76%. + + This interface allows reading and setting maximum utilization clamp + values similar to the sched_setattr(2). This maximum utilization + value is used to clamp the task specific maximum utilization clamp. + + Memory ------ diff --git a/Documentation/admin-guide/dynamic-debug-howto.rst b/Documentation/admin-guide/dynamic-debug-howto.rst index fdf72429f8019cd68ab8e7557cdb4c7376e50176..1c9b4ac82862e29321e1bbe524ecd17c231dc820 100644 --- a/Documentation/admin-guide/dynamic-debug-howto.rst +++ b/Documentation/admin-guide/dynamic-debug-howto.rst @@ -54,6 +54,9 @@ If you make a mistake with the syntax, the write will fail thus:: /dynamic_debug/control -bash: echo: write error: Invalid argument +Note, for systems without 'debugfs' enabled, the control file can be +found in ``/proc/dynamic_debug/control``. + Viewing Dynamic Debug Behaviour =============================== diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 804b2283e8e81790a20239476116b3ca0a1fe4b1..e1b10cc48c902ded874f318f7f7bdfd6408250e9 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1962,6 +1962,12 @@ Built with CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y, the default is off. + kpti= [ARM64] Control page table isolation of user + and kernel address spaces. + Default: enabled on cores which need mitigation. + 0: force disabled + 1: force enabled + kvm.ignore_msrs=[KVM] Ignore guest accesses to unhandled MSRs. Default is 0 (don't ignore, but inject #GP) diff --git a/Documentation/arm64/tagged-address-abi.rst b/Documentation/arm64/tagged-address-abi.rst index d4a85d535bf99e09cfe5c3dc41652eaa3b64f21b..4a9d9c794ee5d889638d1a3af008dec06d11feaf 100644 --- a/Documentation/arm64/tagged-address-abi.rst +++ b/Documentation/arm64/tagged-address-abi.rst @@ -44,8 +44,15 @@ The AArch64 Tagged Address ABI has two stages of relaxation depending how the user addresses are used by the kernel: 1. User addresses not accessed by the kernel but used for address space - management (e.g. ``mmap()``, ``mprotect()``, ``madvise()``). The use - of valid tagged pointers in this context is always allowed. + management (e.g. ``mprotect()``, ``madvise()``). The use of valid + tagged pointers in this context is allowed with the exception of + ``brk()``, ``mmap()`` and the ``new_address`` argument to + ``mremap()`` as these have the potential to alias with existing + user addresses. + + NOTE: This behaviour changed in v5.6 and so some earlier kernels may + incorrectly accept valid tagged pointers for the ``brk()``, + ``mmap()`` and ``mremap()`` system calls. 2. User addresses accessed by the kernel (e.g. ``write()``). This ABI relaxation is disabled by default and the application thread needs to diff --git a/Documentation/dev-tools/kcov.rst b/Documentation/dev-tools/kcov.rst index c2f6452e38ed000edc63440ad66b49da63aed481..f254173b180f804ab7921673b0046436f1d804ab 100644 --- a/Documentation/dev-tools/kcov.rst +++ b/Documentation/dev-tools/kcov.rst @@ -34,6 +34,7 @@ Profiling data will only become accessible once debugfs has been mounted:: Coverage collection ------------------- + The following program demonstrates coverage collection from within a test program using kcov: @@ -128,6 +129,7 @@ only need to enable coverage (disable happens automatically on thread end). Comparison operands collection ------------------------------ + Comparison operands collection is similar to coverage collection: .. code-block:: c @@ -202,3 +204,130 @@ Comparison operands collection is similar to coverage collection: Note that the kcov modes (coverage collection or comparison operands) are mutually exclusive. + +Remote coverage collection +-------------------------- + +With KCOV_ENABLE coverage is collected only for syscalls that are issued +from the current process. With KCOV_REMOTE_ENABLE it's possible to collect +coverage for arbitrary parts of the kernel code, provided that those parts +are annotated with kcov_remote_start()/kcov_remote_stop(). + +This allows to collect coverage from two types of kernel background +threads: the global ones, that are spawned during kernel boot in a limited +number of instances (e.g. one USB hub_event() worker thread is spawned per +USB HCD); and the local ones, that are spawned when a user interacts with +some kernel interface (e.g. vhost workers). + +To enable collecting coverage from a global background thread, a unique +global handle must be assigned and passed to the corresponding +kcov_remote_start() call. Then a userspace process can pass a list of such +handles to the KCOV_REMOTE_ENABLE ioctl in the handles array field of the +kcov_remote_arg struct. This will attach the used kcov device to the code +sections, that are referenced by those handles. + +Since there might be many local background threads spawned from different +userspace processes, we can't use a single global handle per annotation. +Instead, the userspace process passes a non-zero handle through the +common_handle field of the kcov_remote_arg struct. This common handle gets +saved to the kcov_handle field in the current task_struct and needs to be +passed to the newly spawned threads via custom annotations. Those threads +should in turn be annotated with kcov_remote_start()/kcov_remote_stop(). + +Internally kcov stores handles as u64 integers. The top byte of a handle +is used to denote the id of a subsystem that this handle belongs to, and +the lower 4 bytes are used to denote the id of a thread instance within +that subsystem. A reserved value 0 is used as a subsystem id for common +handles as they don't belong to a particular subsystem. The bytes 4-7 are +currently reserved and must be zero. In the future the number of bytes +used for the subsystem or handle ids might be increased. + +When a particular userspace proccess collects coverage by via a common +handle, kcov will collect coverage for each code section that is annotated +to use the common handle obtained as kcov_handle from the current +task_struct. However non common handles allow to collect coverage +selectively from different subsystems. + +.. code-block:: c + + struct kcov_remote_arg { + __u32 trace_mode; + __u32 area_size; + __u32 num_handles; + __aligned_u64 common_handle; + __aligned_u64 handles[0]; + }; + + #define KCOV_INIT_TRACE _IOR('c', 1, unsigned long) + #define KCOV_DISABLE _IO('c', 101) + #define KCOV_REMOTE_ENABLE _IOW('c', 102, struct kcov_remote_arg) + + #define COVER_SIZE (64 << 10) + + #define KCOV_TRACE_PC 0 + + #define KCOV_SUBSYSTEM_COMMON (0x00ull << 56) + #define KCOV_SUBSYSTEM_USB (0x01ull << 56) + + #define KCOV_SUBSYSTEM_MASK (0xffull << 56) + #define KCOV_INSTANCE_MASK (0xffffffffull) + + static inline __u64 kcov_remote_handle(__u64 subsys, __u64 inst) + { + if (subsys & ~KCOV_SUBSYSTEM_MASK || inst & ~KCOV_INSTANCE_MASK) + return 0; + return subsys | inst; + } + + #define KCOV_COMMON_ID 0x42 + #define KCOV_USB_BUS_NUM 1 + + int main(int argc, char **argv) + { + int fd; + unsigned long *cover, n, i; + struct kcov_remote_arg *arg; + + fd = open("/sys/kernel/debug/kcov", O_RDWR); + if (fd == -1) + perror("open"), exit(1); + if (ioctl(fd, KCOV_INIT_TRACE, COVER_SIZE)) + perror("ioctl"), exit(1); + cover = (unsigned long*)mmap(NULL, COVER_SIZE * sizeof(unsigned long), + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if ((void*)cover == MAP_FAILED) + perror("mmap"), exit(1); + + /* Enable coverage collection via common handle and from USB bus #1. */ + arg = calloc(1, sizeof(*arg) + sizeof(uint64_t)); + if (!arg) + perror("calloc"), exit(1); + arg->trace_mode = KCOV_TRACE_PC; + arg->area_size = COVER_SIZE; + arg->num_handles = 1; + arg->common_handle = kcov_remote_handle(KCOV_SUBSYSTEM_COMMON, + KCOV_COMMON_ID); + arg->handles[0] = kcov_remote_handle(KCOV_SUBSYSTEM_USB, + KCOV_USB_BUS_NUM); + if (ioctl(fd, KCOV_REMOTE_ENABLE, arg)) + perror("ioctl"), free(arg), exit(1); + free(arg); + + /* + * Here the user needs to trigger execution of a kernel code section + * that is either annotated with the common handle, or to trigger some + * activity on USB bus #1. + */ + sleep(2); + + n = __atomic_load_n(&cover[0], __ATOMIC_RELAXED); + for (i = 0; i < n; i++) + printf("0x%lx\n", cover[i + 1]); + if (ioctl(fd, KCOV_DISABLE, 0)) + perror("ioctl"), exit(1); + if (munmap(cover, COVER_SIZE * sizeof(unsigned long))) + perror("munmap"), exit(1); + if (close(fd)) + perror("close"), exit(1); + return 0; + } diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt index 3135b80df6daf29c68198c3a9e2647a18c4cbfed..4eb3e2ddd00ec082e8ce8cee6b7b252a535e95e1 100644 --- a/Documentation/filesystems/f2fs.txt +++ b/Documentation/filesystems/f2fs.txt @@ -235,6 +235,17 @@ checkpoint=%s[:%u[%]] Set to "disable" to turn off checkpointing. Set to "en hide up to all remaining free space. The actual space that would be unusable can be viewed at /sys/fs/f2fs//unusable This space is reclaimed once checkpoint=enable. +compress_algorithm=%s Control compress algorithm, currently f2fs supports "lzo" + and "lz4" algorithm. +compress_log_size=%u Support configuring compress cluster size, the size will + be 4KB * (1 << %u), 16KB is minimum size, also it's + default size. +compress_extension=%s Support adding specified extension, so that f2fs can enable + compression on those corresponding files, e.g. if all files + with '.ext' has high compression rate, we can set the '.ext' + on compression extension list and enable compression on + these file by default rather than to enable it via ioctl. + For other files, we can still enable compression via ioctl. ================================================================================ DEBUGFS ENTRIES @@ -259,170 +270,6 @@ The files in each per-device directory are shown in table below. Files in /sys/fs/f2fs/ (see also Documentation/ABI/testing/sysfs-fs-f2fs) -.............................................................................. - File Content - - gc_urgent_sleep_time This parameter controls sleep time for gc_urgent. - 500 ms is set by default. See above gc_urgent. - - gc_min_sleep_time This tuning parameter controls the minimum sleep - time for the garbage collection thread. Time is - in milliseconds. - - gc_max_sleep_time This tuning parameter controls the maximum sleep - time for the garbage collection thread. Time is - in milliseconds. - - gc_no_gc_sleep_time This tuning parameter controls the default sleep - time for the garbage collection thread. Time is - in milliseconds. - - gc_idle This parameter controls the selection of victim - policy for garbage collection. Setting gc_idle = 0 - (default) will disable this option. Setting - gc_idle = 1 will select the Cost Benefit approach - & setting gc_idle = 2 will select the greedy approach. - - gc_urgent This parameter controls triggering background GCs - urgently or not. Setting gc_urgent = 0 [default] - makes back to default behavior, while if it is set - to 1, background thread starts to do GC by given - gc_urgent_sleep_time interval. - - reclaim_segments This parameter controls the number of prefree - segments to be reclaimed. If the number of prefree - segments is larger than the number of segments - in the proportion to the percentage over total - volume size, f2fs tries to conduct checkpoint to - reclaim the prefree segments to free segments. - By default, 5% over total # of segments. - - main_blkaddr This value gives the first block address of - MAIN area in the partition. - - max_small_discards This parameter controls the number of discard - commands that consist 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. - - discard_granularity This parameter controls the granularity of discard - command size. It will issue discard commands iif - the size is larger than given granularity. Its - unit size is 4KB, and 4 (=16KB) is set by default. - The maximum value is 128 (=512KB). - - reserved_blocks This parameter indicates the number of blocks that - f2fs reserves internally for root. - - batched_trim_sections This parameter controls the number of sections - to be trimmed out in batch mode when FITRIM - conducts. 32 sections is set by default. - - ipu_policy This parameter controls the policy of in-place - updates in f2fs. There are five policies: - 0x01: F2FS_IPU_FORCE, 0x02: F2FS_IPU_SSR, - 0x04: F2FS_IPU_UTIL, 0x08: F2FS_IPU_SSR_UTIL, - 0x10: F2FS_IPU_FSYNC. - - min_ipu_util This parameter controls the threshold to trigger - in-place-updates. The number indicates percentage - of the filesystem utilization, and used by - F2FS_IPU_UTIL and F2FS_IPU_SSR_UTIL policies. - - min_fsync_blocks This parameter controls the threshold to trigger - in-place-updates when F2FS_IPU_FSYNC mode is set. - The number indicates the number of dirty pages - when fsync needs to flush on its call path. If - the number is less than this value, it triggers - in-place-updates. - - min_seq_blocks This parameter controls the threshold to serialize - write IOs issued by multiple threads in parallel. - - min_hot_blocks This parameter controls the threshold to allocate - a hot data log for pending data blocks to write. - - min_ssr_sections This parameter adds the threshold when deciding - SSR block allocation. If this is large, SSR mode - will be enabled early. - - ram_thresh This parameter controls the memory footprint used - by free nids and cached nat entries. By default, - 1 is set, which indicates 10 MB / 1 GB RAM. - - ra_nid_pages When building free nids, F2FS reads NAT blocks - ahead for speed up. Default is 0. - - dirty_nats_ratio Given dirty ratio of cached nat entries, F2FS - determines flushing them in background. - - max_victim_search This parameter controls the number of trials to - find a victim segment when conducting SSR and - cleaning operations. The default value is 4096 - which covers 8GB block address range. - - migration_granularity For large-sized sections, F2FS can stop GC given - this granularity instead of reclaiming entire - section. - - dir_level This parameter controls the directory level to - support large directory. If a directory has a - number of files, it can reduce the file lookup - latency by increasing this dir_level value. - Otherwise, it needs to decrease this value to - reduce the space overhead. The default value is 0. - - cp_interval F2FS tries to do checkpoint periodically, 60 secs - by default. - - idle_interval F2FS detects system is idle, if there's no F2FS - operations during given interval, 5 secs by - default. - - discard_idle_interval F2FS detects the discard thread is idle, given - time interval. Default is 5 secs. - - gc_idle_interval F2FS detects the GC thread is idle, given time - interval. Default is 5 secs. - - umount_discard_timeout When unmounting the disk, F2FS waits for finishing - queued discard commands which can take huge time. - This gives time out for it, 5 secs by default. - - iostat_enable This controls to enable/disable iostat in F2FS. - - readdir_ra This enables/disabled readahead of inode blocks - in readdir, and default is enabled. - - gc_pin_file_thresh This indicates how many GC can be failed for the - pinned file. If it exceeds this, F2FS doesn't - guarantee its pinning state. 2048 trials is set - by default. - - extension_list This enables to change extension_list for hot/cold - files in runtime. - - inject_rate This controls injection rate of arbitrary faults. - - inject_type This controls injection type of arbitrary faults. - - dirty_segments This shows # of dirty segments. - - lifetime_write_kbytes This shows # of data written to the disk. - - features This shows current features enabled on F2FS. - - current_reserved_blocks This shows # of blocks currently reserved. - - unusable If checkpoint=disable, this shows the number of - blocks that are unusable. - If checkpoint=enable it shows the number of blocks - that would be unusable if checkpoint=disable were - to be set. - -encoding This shows the encoding used for casefolding. - If casefolding is not enabled, returns (none) ================================================================================ USAGE @@ -840,3 +687,44 @@ zero or random data, which is useful to the below scenario where: 4. address = fibmap(fd, offset) 5. open(blkdev) 6. write(blkdev, address) + +Compression implementation +-------------------------- + +- New term named cluster is defined as basic unit of compression, file can +be divided into multiple clusters logically. One cluster includes 4 << n +(n >= 0) logical pages, compression size is also cluster size, each of +cluster can be compressed or not. + +- In cluster metadata layout, one special block address is used to indicate +cluster is compressed one or normal one, for compressed cluster, following +metadata maps cluster to [1, 4 << n - 1] physical blocks, in where f2fs +stores data including compress header and compressed data. + +- In order to eliminate write amplification during overwrite, F2FS only +support compression on write-once file, data can be compressed only when +all logical blocks in file are valid and cluster compress ratio is lower +than specified threshold. + +- To enable compression on regular inode, there are three ways: +* chattr +c file +* chattr +c dir; touch dir/file +* mount w/ -o compress_extension=ext; touch file.ext + +Compress metadata layout: + [Dnode Structure] + +-----------------------------------------------+ + | cluster 1 | cluster 2 | ......... | cluster N | + +-----------------------------------------------+ + . . . . + . . . . + . Compressed Cluster . . Normal Cluster . ++----------+---------+---------+---------+ +---------+---------+---------+---------+ +|compr flag| block 1 | block 2 | block 3 | | block 1 | block 2 | block 3 | block 4 | ++----------+---------+---------+---------+ +---------+---------+---------+---------+ + . . + . . + . . + +-------------+-------------+----------+----------------------------+ + | data length | data chksum | reserved | compressed data | + +-------------+-------------+----------+----------------------------+ diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesystems/fscrypt.rst index 4ed9d58ea0ab5bb474badd89382af19e46a19631..fbcd185d15dab5c0ab384979326ef3969b7f3623 100644 --- a/Documentation/filesystems/fscrypt.rst +++ b/Documentation/filesystems/fscrypt.rst @@ -234,8 +234,8 @@ HKDF is more flexible, is nonreversible, and evenly distributes entropy from the master key. HKDF is also standardized and widely used by other software, whereas the AES-128-ECB based KDF is ad-hoc. -Per-file keys -------------- +Per-file encryption keys +------------------------ Since each master key can protect many files, it is necessary to "tweak" the encryption of each file so that the same plaintext in two @@ -268,9 +268,9 @@ is greater than that of an AES-256-XTS key. Therefore, to improve performance and save memory, for Adiantum a "direct key" configuration is supported. When the user has enabled this by setting FSCRYPT_POLICY_FLAG_DIRECT_KEY in the fscrypt policy, -per-file keys are not used. Instead, whenever any data (contents or -filenames) is encrypted, the file's 16-byte nonce is included in the -IV. Moreover: +per-file encryption keys are not used. Instead, whenever any data +(contents or filenames) is encrypted, the file's 16-byte nonce is +included in the IV. Moreover: - For v1 encryption policies, the encryption is done directly with the master key. Because of this, users **must not** use the same master @@ -302,6 +302,16 @@ For master keys used for v2 encryption policies, a unique 16-byte "key identifier" is also derived using the KDF. This value is stored in the clear, since it is needed to reliably identify the key itself. +Dirhash keys +------------ + +For directories that are indexed using a secret-keyed dirhash over the +plaintext filenames, the KDF is also used to derive a 128-bit +SipHash-2-4 key per directory in order to hash filenames. This works +just like deriving a per-file encryption key, except that a different +KDF context is used. Currently, only casefolded ("case-insensitive") +encrypted directories use this style of hashing. + Encryption modes and usage ========================== @@ -325,11 +335,11 @@ used. Adiantum is a (primarily) stream cipher-based mode that is fast even on CPUs without dedicated crypto instructions. It's also a true wide-block mode, unlike XTS. It can also eliminate the need to derive -per-file keys. However, it depends on the security of two primitives, -XChaCha12 and AES-256, rather than just one. See the paper -"Adiantum: length-preserving encryption for entry-level processors" -(https://eprint.iacr.org/2018/720.pdf) for more details. To use -Adiantum, CONFIG_CRYPTO_ADIANTUM must be enabled. Also, fast +per-file encryption keys. However, it depends on the security of two +primitives, XChaCha12 and AES-256, rather than just one. See the +paper "Adiantum: length-preserving encryption for entry-level +processors" (https://eprint.iacr.org/2018/720.pdf) for more details. +To use Adiantum, CONFIG_CRYPTO_ADIANTUM must be enabled. Also, fast implementations of ChaCha and NHPoly1305 should be enabled, e.g. CONFIG_CRYPTO_CHACHA20_NEON and CONFIG_CRYPTO_NHPOLY1305_NEON for ARM. @@ -513,7 +523,9 @@ FS_IOC_SET_ENCRYPTION_POLICY can fail with the following errors: - ``EEXIST``: the file is already encrypted with an encryption policy different from the one specified - ``EINVAL``: an invalid encryption policy was specified (invalid - version, mode(s), or flags; or reserved bits were set) + version, mode(s), or flags; or reserved bits were set); or a v1 + encryption policy was specified but the directory has the casefold + flag enabled (casefolding is incompatible with v1 policies). - ``ENOKEY``: a v2 encryption policy was specified, but the key with the specified ``master_key_identifier`` has not been added, nor does the process have the CAP_FOWNER capability in the initial user @@ -1137,8 +1149,8 @@ The context structs contain the same information as the corresponding policy structs (see `Setting an encryption policy`_), except that the context structs also contain a nonce. The nonce is randomly generated by the kernel and is used as KDF input or as a tweak to cause -different files to be encrypted differently; see `Per-file keys`_ and -`DIRECT_KEY policies`_. +different files to be encrypted differently; see `Per-file encryption +keys`_ and `DIRECT_KEY policies`_. Data path changes ----------------- @@ -1190,7 +1202,7 @@ filesystem-specific hash(es) needed for directory lookups. This allows the filesystem to still, with a high degree of confidence, map the filename given in ->lookup() back to a particular directory entry that was previously listed by readdir(). See :c:type:`struct -fscrypt_digested_name` in the source for more details. +fscrypt_nokey_name` in the source for more details. Note that the precise way that filenames are presented to userspace without the key is subject to change in the future. It is only meant diff --git a/Documentation/filesystems/fsverity.rst b/Documentation/filesystems/fsverity.rst index 42a0b6dd9e0b68539b0ec92db3a7bd455410b145..a95536b6443c8a31b3a512aafac8f965e96ac2fc 100644 --- a/Documentation/filesystems/fsverity.rst +++ b/Documentation/filesystems/fsverity.rst @@ -226,6 +226,14 @@ To do so, check for FS_VERITY_FL (0x00100000) in the returned flags. The verity flag is not settable via FS_IOC_SETFLAGS. You must use FS_IOC_ENABLE_VERITY instead, since parameters must be provided. +statx +----- + +Since Linux v5.5, the statx() system call sets STATX_ATTR_VERITY if +the file has fs-verity enabled. This can perform better than +FS_IOC_GETFLAGS and FS_IOC_MEASURE_VERITY because it doesn't require +opening the file, and opening verity files can be expensive. + Accessing verity files ====================== @@ -398,7 +406,7 @@ pages have been read into the pagecache. (See `Verifying data`_.) ext4 ---- -ext4 supports fs-verity since Linux TODO and e2fsprogs v1.45.2. +ext4 supports fs-verity since Linux v5.4 and e2fsprogs v1.45.2. To create verity files on an ext4 filesystem, the filesystem must have been formatted with ``-O verity`` or had ``tune2fs -O verity`` run on @@ -434,7 +442,7 @@ also only supports extent-based files. f2fs ---- -f2fs supports fs-verity since Linux TODO and f2fs-tools v1.11.0. +f2fs supports fs-verity since Linux v5.4 and f2fs-tools v1.11.0. To create verity files on an f2fs filesystem, the filesystem must have been formatted with ``-O verity``. diff --git a/Documentation/networking/nf_flowtable.txt b/Documentation/networking/nf_flowtable.txt index 54128c50d508ef27e5c6f2026fc5dddd0df47ead..b01c91893481fd44e42835ced658773778ac11ed 100644 --- a/Documentation/networking/nf_flowtable.txt +++ b/Documentation/networking/nf_flowtable.txt @@ -76,7 +76,7 @@ flowtable and add one rule to your forward chain. table inet x { flowtable f { - hook ingress priority 0 devices = { eth0, eth1 }; + hook ingress priority 0; devices = { eth0, eth1 }; } chain y { type filter hook forward priority 0; policy accept; diff --git a/Documentation/scheduler/sched-tune.txt b/Documentation/scheduler/sched-tune.txt index 1a103715f7bdc750ae3e4f4cc2da29f23727eb9d..be728705fe25d6a683832a3acdd8ee22a5ed9c5b 100644 --- a/Documentation/scheduler/sched-tune.txt +++ b/Documentation/scheduler/sched-tune.txt @@ -233,9 +233,9 @@ Thus, with the sched_cfs_boost enabled we have the following main functions to get the current utilization of a CPU: cpu_util() - boosted_cpu_util() + stune_util() -The new boosted_cpu_util() is similar to the first but returns a boosted +The new stune_util() is similar to the first but returns a boosted utilization signal which is a function of the sched_cfs_boost value. This function is used in the CFS scheduler code paths where schedutil needs to diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt index 184fb62ecc4bd0d99956cba71e6e5900bf9199fd..ba12088a96d34f21af7bef72c408dc0fcdc67820 100644 --- a/Documentation/sysctl/kernel.txt +++ b/Documentation/sysctl/kernel.txt @@ -80,6 +80,7 @@ show up in /proc/sys/kernel: - reboot-cmd [ SPARC only ] - rtsig-max - rtsig-nr +- sched_energy_aware - seccomp/ ==> Documentation/userspace-api/seccomp_filter.rst - sem - sem_next_id [ sysv ipc ] @@ -900,6 +901,17 @@ rtsig-nr shows the number of RT signals currently queued. ============================================================== +sched_energy_aware: + +Enables/disables Energy Aware Scheduling (EAS). EAS starts +automatically on platforms where it can run (that is, +platforms with asymmetric CPU topologies and having an Energy +Model available). If your platform happens to meet the +requirements for EAS but you do not want to use it, change +this value to 0. + +============================================================== + sched_schedstats: Enables/disables scheduler statistics. Enabling this feature diff --git a/Documentation/sysctl/vm.txt b/Documentation/sysctl/vm.txt index b04c20df606bb6202b9a79d4f1211eef992ab7a4..f7432c5dcd9b39732bf4ab81378a3d923fe96645 100644 --- a/Documentation/sysctl/vm.txt +++ b/Documentation/sysctl/vm.txt @@ -925,14 +925,14 @@ The intent is that compaction has less work to do in the future and to increase the success rate of future high-order allocations such as SLUB allocations, THP and hugetlbfs pages. -To make it sensible with respect to the watermark_scale_factor -parameter, the unit is in fractions of 10,000. The default value of -15,000 on !DISCONTIGMEM configurations means that up to 150% of the high -watermark will be reclaimed in the event of a pageblock being mixed due -to fragmentation. The level of reclaim is determined by the number of -fragmentation events that occurred in the recent past. If this value is -smaller than a pageblock then a pageblocks worth of pages will be reclaimed -(e.g. 2MB on 64-bit x86). A boost factor of 0 will disable the feature. +To make it sensible with respect to the watermark_scale_factor parameter, +the unit is in fractions of 10,000. The default value of 15,000 means +that up to 150% of the high watermark will be reclaimed in the event of +a pageblock being mixed due to fragmentation. The level of reclaim is +determined by the number of fragmentation events that occurred in the +recent past. If this value is smaller than a pageblock then a pageblocks +worth of pages will be reclaimed (e.g. 2MB on 64-bit x86). A boost factor +of 0 will disable the feature. ============================================================= diff --git a/MAINTAINERS b/MAINTAINERS index 7bd11ad4df90535f79c8e58bcdc01c85661df362..8a9c4ce954ee9fd7729f95353ae6fe7cf54b2e22 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7367,7 +7367,7 @@ M: Joonas Lahtinen M: Rodrigo Vivi L: intel-gfx@lists.freedesktop.org W: https://01.org/linuxgraphics/ -B: https://01.org/linuxgraphics/documentation/how-report-bugs +B: https://gitlab.freedesktop.org/drm/intel/-/wikis/How-to-file-i915-bugs C: irc://chat.freenode.net/intel-gfx Q: http://patchwork.freedesktop.org/project/intel-gfx/ T: git git://anongit.freedesktop.org/drm-intel @@ -10312,6 +10312,12 @@ S: Maintained F: Documentation/scsi/NinjaSCSI.txt F: drivers/scsi/nsp32* +NINTENDO HID DRIVER +M: Daniel J. Ogorchock +L: linux-input@vger.kernel.org +S: Maintained +F: drivers/hid/hid-nintendo* + NIOS2 ARCHITECTURE M: Ley Foon Tan L: nios2-dev@lists.rocketboards.org (moderated for non-subscribers) diff --git a/Makefile b/Makefile index cb967bd93ac9e33f5fa6c69e2aada0016d932eea..2852c29458b359a60151e8e255f5d24d869e7b91 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 4 PATCHLEVEL = 19 -SUBLEVEL = 95 +SUBLEVEL = 110 EXTRAVERSION = NAME = "People's Front" @@ -869,7 +869,8 @@ export LTO_CFLAGS DISABLE_LTO endif ifdef CONFIG_CFI_CLANG -cfi-clang-flags += -fsanitize=cfi -fno-sanitize-cfi-canonical-jump-tables +cfi-clang-flags += -fsanitize=cfi -fno-sanitize-cfi-canonical-jump-tables \ + -fno-sanitize-blacklist DISABLE_CFI_CLANG := -fno-sanitize=cfi ifdef CONFIG_MODULES cfi-clang-flags += -fsanitize-cfi-cross-dso @@ -1081,6 +1082,7 @@ ifdef CONFIG_STACK_VALIDATION endif endif +PHONY += prepare0 ifeq ($(KBUILD_EXTMOD),) core-y += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/ @@ -1128,9 +1130,12 @@ endif autoksyms_h := $(if $(CONFIG_TRIM_UNUSED_KSYMS), include/generated/autoksyms.h) +quiet_cmd_autoksyms_h = GEN $@ + cmd_autoksyms_h = mkdir -p $(dir $@); \ + $(CONFIG_SHELL) $(srctree)/scripts/gen_autoksyms.sh $@ + $(autoksyms_h): - $(Q)mkdir -p $(dir $@) - $(Q)touch $@ + $(call cmd,autoksyms_h) ARCH_POSTLINK := $(wildcard $(srctree)/arch/$(SRCARCH)/Makefile.postlink) @@ -1189,8 +1194,7 @@ scripts: scripts_basic asm-generic gcc-plugins $(autoksyms_h) # archprepare is used in arch Makefiles and when processed asm symlink, # version.h and scripts_basic is processed / created. -# Listed in dependency order -PHONY += prepare archprepare prepare0 prepare1 prepare2 prepare3 +PHONY += prepare archprepare prepare1 prepare2 prepare3 # prepare3 is used to check if we are building in a separate output directory, # and if so do: diff --git a/abi_gki_aarch64.xml b/abi_gki_aarch64.xml index 1bba8b45cf6354e7a75720fad1002aedb9e486da..05cb911f71212eca123ace7366db8feff7345fcb 100644 --- a/abi_gki_aarch64.xml +++ b/abi_gki_aarch64.xml @@ -2,48 +2,86 @@ + - - + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + - + + + + + + + + + + + - + + @@ -52,113 +90,194 @@ + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + - + + + + + + + + - + - - - - - - - - - - + + + + + + + + + + + + + + + + @@ -169,118 +288,374 @@ - - + + + + + + + + + + + + + + + + + + + + + - + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + + - + + + + - + - + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -292,14 +667,33 @@ + + + + + + + + + + + + + + + + + + + @@ -308,29 +702,92 @@ + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -338,6 +795,12 @@ + + + + + + @@ -350,7 +813,14 @@ + + + + + + + @@ -359,22 +829,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -382,85 +908,384 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + @@ -470,26 +1295,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -518,108 +1392,372 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -629,37 +1767,85 @@ + + - + + + + + + + + + + + + + + + + + + + + + - + - - + + + + + + + + + + + + + + + + + + - - - - - + + + + + + + + + + + + + + + @@ -675,77 +1861,277 @@ + + + + + + + + + + + + + + + + + + - + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + - - + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -753,31 +2139,118 @@ + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -785,25 +2258,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -814,57480 +2351,90554 @@ + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + - + - - - - + - - - - + - - - - + - - - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - - - - - - - - - - - - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + + + - - + + - - + + - - + + - - + + - + - + - + - + - + - + - - + + + + + + + - + - + - + - + - - + + - - + + + + - - + + - - + + - - + + - - + + - + + - + - + + + + + + + - + - + - + - + - + - - - - + - + - + - + + + + - + - + - - - - - - - + - + - + + + + - + - + - + - - + + - - + + - + - + + + + + + + - + - + - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + - - - + + + - - + + + + + + + + + + + - - - - + + + + + + + + - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - + + + - - - + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - + + + + + + + + - + + + + + + + - - + + + + + - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - + + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - + + + - - - + + + + - - - - - - - - - - - - - - - - - + + + - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + - + - + - + - + - + - + - + - + - + - + - - - - + - + - + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + - + - + - + - + - - - - + - - - - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - + - - - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + - + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - + + - + - - - - + - - - - + - + - - - - - - + - - + + - - - - + + - + + + + + + + + + + + + + + + + + + - - - - + - + - + - + + + + - + + + + + + + + - + - + + + + - + + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + + + + + + + - + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - - - - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - + - - - - + - + - - - - - - - - - - - - - - - - - + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - - + - + - + - + - + - - - - - - - - - + - + + - + - - - - + - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - + - - - - + - - - - - - - + - - - - - - - - - - + - - - - + - + + + + + + + - + - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - + - - + + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - - + + - - + + - + - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + - - - - - - - - - - - + + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + - + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - - - - - - - - - - - - - + - + - + - - - - + - - + + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - - + + - + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + + + + - + - + - - + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - - - - + + - - + + - - + + + + - + - + + + + + + + + + + + + + - + - + - - + + - - + + + + + + + - - - - - - - - - - - - - + + + + + + + + - - - - - - - - - - + - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - - - - - + + - - + + + + - - + + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + - + - + - - + + - - + + + + - - + + - - + + + + + - + - + - - + + - - - - + + - - + + - - + + - - - - + + - - + + - - - + + + - - + + + + + + + + + + + - + - - + + - - + + - - + + - - - - - - - - - - + + - - + + - + + + + - + - + - - - - - - - - - + - - + + - - - - + + - - - - + - - - - - - - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - + - + - - + + + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - + - - - - + - - - - + - + + + + + + - + + + + - + - + - - + + - - - - + + + + + + + + - + - + - + - - - - - - - - - - - + + - - + + - - - + + + - - + + + + + - + - + - + - + + + + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - + + - - + + - - + + - - - - + + - - + + + + + - + - + - - + + - - + + - - - - - - - - - - - - + + - - + + - - + + - - + + + + + + + + + + + + + + - - + - + - + - + - + - + - + + + + + + + + + + + + + + + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + - - + + - + - + - + - - - - - - - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - + + + + - + + + + - + - + - + - - + + - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + - + + + - + - + - + - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + + + + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + + + + - + - + - + - - - - - - - - - - - - + + + + + + + + + + + + - - - - - + - + - + - - - - + - - - - - - - + - + - - - - - - - + - - - - - + + - + - + - + - + - - - - + - - - - + - - - - + - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - + + + + + + - - + - - - - - - - + - - + + - + - - - - + - - + + - - + + - + - - - - + - + - + - + + + + - + - - - - + - - - - + - + - - - - + - - - - + - + - + - - - - + - + - - - - + - + - + - - - - + - - - - + - - - - + - + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + - + - + - + - + - + + + + - - + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - - + + + + + + + + + + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + + + + + + + + + + - + + + + - + + + + + + + - + + + + - + + + + - + + + + + + + + + + + + + + + + - + - - + + - - + + - - + + - - + + - + - - - - + - + - - - - + - + - - - - - - - + - - - - - + + - + - + + + + - + - + - + - + - - - - + - + - + - - - - + - - - - + - + - + - + - + - + - + - + - + - + - + - - - - - - - + - - - - - + + + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - + - + - - + + - - + + - - + + - - + + - - + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + + + + - + + + + - + + + + + + + - + - + + + + - + + + + - + - - + + - - + + - + - + - + - + - - - - + - + - - + + + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - + + - - + + - - - - - - - - - - - - - - + + - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + + + + - - + + - - + + - - + + + + + - - + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + - - + + - - + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - + + + + + + + + + + + + + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - + - + - + - + + + + - + - + - + - + - + - - + + - + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + - - - - + + - - - - - - - - - - - - - - - - - - - - - + - + - + - + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - + + - - - - + + - - + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + - + - + + + + - + + + + - + - + - + - + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + - + - + - - + + + + + - + - + - + - - + + - - + + - - - - + + - - + + - - - + + - + - - + + - + - + - - - - + - - + + - - + + - - + + - - + + - - + + + + - + - - - - + + - - + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - - - + + - - + + - + - + - - - - - - + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - + + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - + + - - + + - - + + - + + + + + + + + - + - + - + - - - - - + + + + - - + + - - + + + + - - + + - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - + + + + - + - - + + - - + + - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - - - - - - - - - - - - - - - - - - - + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - - - + + - - + + - - - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + - - - + + + + - - - + + + + - - - + + + + - - - - + + + + + - - - + + + + - - - + + + + + - - - + + + + + + + + + + + + + + - - - + + + + - - - + + + + - - - + + + + - - - - - - + + + + - - - - + + + - - + + + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + - - - - - + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + - - - + + + + + + + + + + + + + + + + - - - + + + + + + + + - - - - - - - - - - + + + + + - - - - - - - - - + + + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + + + + - + + + + - + + + + - + + + + + + + + + + - + + + + - + - - + + - - + + + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - + - - - - + - - - - + - + - + - + - + + + + + + + + + + + + + + + + - + + + + - + - + - + - + - + - + - + + + + - + - + - + - - - - - + + - - - - + + - - - - + + - - + + - - - - + + - - + + - - + + - - - - + + - - + + - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + - - - + + + - - + + - - - - + + - - + + - - + + - - + + + + + + + + - + - + - + - - + + - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + + + + - + + - + - + - - - - + - + - + - - + + + + + + + + + + - + - + - + - - - - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + - + + + + - + - + - + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + - + - - - - + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - + - + + + + - + + - + + + + - + - - - - + + - - + + - - - - + + - - + + - + - + - + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - - + + - + - + + + + - + + + + - + + + + - + - + - - - - - - - - + + - - + - + - - - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - - - + + - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - + - - - - + - + - + - + - + - + + + + - + - + - + - - + + + + - - + + - - + + + + + + + + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - - - - + - - + + - - + + - - + + - - + + - + - - + + - - + + - - - - + - - - - + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - + + - - - - - - - + + - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - + + - - + + - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + + + + - + - + - - + + - - + + + + - + - - - - + - - - - + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - + - - + + + + + + + + - + - + - + - + - + - + - + - - + + - - + + - - + + + + - - + + - - + + - - + + - + - + - + - - - - - - - - - - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - + - + - + + + + - + - + - + - + - - - - - - - - - - - - - - - - + - - - - + + - - + + - - + + - + - - - - - + + - - - - + + - - - - + + - - - - - + + - - - - - - - - - - - - - - + + - - + + - - - - + + - - + + + + + - + - + - + - - - - - - + - + - - + + - - + + - - + + - + - + - + - + - - - - + - + - - - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - - - + + - - + + - - + + - - - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - - - + + - - + + - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - - - - - - - - + + + + + + + - - - - - - - - - - + - - - - - - - - - - - - - - - - - - + - - + + - + + + + - + + + + - + - - - - + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - - - - - + + - - - - + + - + + + + + + + + + + - + - - - - + - + - - - - - + + - - + + - + - + - - - - + + - - + + - - + + + + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + - + - + - + - + - + - + - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - - - - + - - - - - - - - - - + - + - + + + + - + + + + - + - + - + + + + - + - + - + - + - + - + + + + - + - + - - + + + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + - + - + - - + + + + + + + + - + - + - + - + - + - + - + - + - + - + - - - - + - + - + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + + + + + - - + - + + + + - - + - + - - - - + - - + + - - - - - - - - - - + - + - - - - + - + - + - - - - + - + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + - - + + - - + + - - + + - - + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - + - + - + - + - - + - + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + - + - + - - - - - + + + + - - + + - - + + + + - - + + + + - + - + - + - + - + - + + + + - + - + - + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + - - + - + - + - + - + - - + + + + + + + + + + + + + + + + - + - - + + + + - + - + + + + + + + - + - - - - + - + - + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - + - + - - + + - - + + - - + + + + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + + + + + + + + + + - - - - - - - + - + + + + - + - + - + + + + - + + + + - + - - + + - + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + + + + - + + + + + + + - + - + - + - - - - - - + + - - + + - - - - - + + - + - + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + - + + + + - + - + - + + + + - + - - + + - + - - - - + - + + + + - + - - - - - - - - - - + + - - + + - - - - + + - - + + - - - - - + + - - + + + + + + + + + + + + + + - - - - + - + - + - + - + + + + - + - - - - - - + + - - + + - - + + - - - - + - - + + - + - + - - - - + + - + + + + + + + + + + - + - + - + + + + + + + + + + - + - + - + + + + + + + - + + + + - + - + - + - + - + - + - + - + - + - + - + - - + + - - + + + + + + + + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - + - + + + + - + - - - - + + - - + + - - - - + + - - + + - - + + + + + - + - + - - + + - - - - + + - + - - - - - - - - - + - - - - - - - - - - + - + - + - - + + - + - - - - - - - - - - - - - + - - + + - - + + - - + + - + - + - + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + - + - + - - - - + - + - + - + - - + + - - + + - - - - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - + + - - + + - - - - + + - - + + - + - + + + + - + - - + + + + - - + + - - + + + + + - - - + + + - - + + - - + + - - + + - + - + - + - + - - - - + - - - - + - - - - + - - - - + - - - - + - + - + - + - + - - - - - - - + - - - - - - - - - - - + + + + - - + + - - + + - - + + - - + + - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - + - - - - - - - - - - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + - + - - - - - + + - - - - + + - - - - - - + - + - + - + - - + + - - - - + + - - + + - - + + - - + - + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + + + + - + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - - - - - - - - - - + - + - + - + + + + + + + + + + - + + + + + + + + + + - + + + + - + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + - + - - + + - + - + - + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + - - + + - - + + - + - + - - - - + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + + + - - + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + + + - - + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - + + + + + + + + - - + - + - + - - - - + - + - - - - - - - - - - + - + - + - + - + - + - - - - + - + - + - - - - - - - + - + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - - - - + - - - - - - - - - - - - - - - - - - - - - - - + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - + + - - + + + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + - + - + - - - - - + + - + - + - + - + - + - - - - - - - - + + - + - - - - - - - + + - - + + - - - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - - - - + - - - + - - - - + - - - - - - - - + + - - + + - - - - + + - + - - + + - - - - + + + + - + - + - - + + + + - - + + - - + + - - + + - - - - - + + - - + + - - + - + - - + + - - - - - + + + + + - + - - + + - - + + - - + + + + - - + + - - + + + + + - - + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - - - - - + + - - + + - - + + - - + - - - - + - + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + + + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + + + + - + - + - + - - + + - + - + - + - - - - + + - - + + - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - + - - - - - + + - - + + - - + + - - + + - - + + - + + + + + + + - + - - - - + - + - - + + - - - - + + - + - + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + + - - + + - - + + - - + + + + + + + + + + - + - + - - - - - - + - - + + - - - - + + - - - - + + - - - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - - - + + - - + + - + + + + + + + + + + - + - - - - - - - + - + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + + + + - + - + - + - + - + - + - + - + - - - - + - + - + - + - - - - - - - - - - - - - + - + + + + - + - - - - - - - - - - + - + - - - - - - - - - - - - - + - + - - - - + - + - + - - - - + - - + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - + + + + + + + + + + - - - - + - - - - - - - - - - + - - - - - - - - - - + - + - - - - - - - - - - + - + - - + + - + - - - - + - - + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + - + - - - - + - + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - - - - + + - - + + - - + + - - + + - - + + + + + + + + - + - - - - + - - - - + - - - - + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - - - - + - - - - + - + - - + + - - + + - - + + - - + + - + - - - - + - + - + - + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - + + - - + + - - + + - + - + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - + - + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - + + + + + + + + + + - - - - - - - - - - + - - - - + + - - - - - - - + - - + + - - - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - + - + - - - - + + - - + + + + + + + - - + + - - + + - - + + - - + + + + - - + + - + - + - - + + - - + + - - + + + + + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + - + - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + - + - + - + - - - - + + - - + + - - + + - - + + + + + - + - + - + - + - + - + - - - - + + - - + + - - + + - - + - + - - + + + + + - + - + - - + + - + - + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - - - + + - - + + - - + + - - - - + + - - + + - - + + + + + + + + - + - + - - - - + - + - - - - - - - - + + + + + - + - + - - + + - - + + - - + + - + - + - + - - - - + - - + + + + + + + + + + + + + + + + + + - - - - + - + - - + + - - - - + + + + + - - - - - - - - - - - + + + + - + - + - + - + - - + + + + + + + + - + - - - - + - + - - + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + - + + + + - + + + + - + - + - + - + - + - - - - + - + + + + - + - + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - - - - + - + - + - + - - + + - - + + - + + + + + + + + + + + + + + + + + + + + + - + - + - + - - + + - - + + + + + + + - + + + + - + + + + + + + + + + - + - - + + - - - - + - + - + + + + + + + - + - - + + + + + + + + + + + + + + + + + - + - + - + - + - - + + - - + + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - - - - + - + - - - - - - - - - - - - - - - - - - - - - - + - - + + - - + + - - + + - - + + - + - - - - - - - - - - - - - + - - + + - - + + - - + + - - + + - - + + - + - - + + - - + + + + + - + + + + + + + + + - - - - - - - + - + - + - - - - + - + - + - + - + - - - - + - + - + - + - - - - - - - - - - - - - - - - - - + - - + + - - + + - - + + - + - - - - + - - + + - - + + - - + + - + - - - - - - - + - + - + - + - + - + - - - - + + - - + + - + - + - - + + - - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + + + + - - - + + + - - + + - - - - + + - - + + - - - - + + - - + + + + + + + + - + - - - - + - - - - + - + - + + + + - + - - - - + - - - - - - - - - - - - - - - - - + - + - + - + - + - + - - - - + + - - + + - - + + + + + + + + - + - + - + + + + + + + + + + - + - + - + - - - - + - - + + + + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - + + + + + + + + + - + - + - + - + - + - + + + + - + - + - + - - - - + - - + + - + - + + + + + + + + + + - + - - + + + + - - + + - - + + - - + + - + - + + + + - + + + + - + - + - - - - - - - + - - - - - - - - + + - - + + + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + - + - + - + - + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + - - + + - + - + - + - + - + - - - - + - + - + - + - + - + - + - + - + - + - - - - - - - - - - - - - - - - - - - + - - + - - - - - - - + - + - - - - + - - - - - - - - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - + - - - - + - - + + + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - + - + + + + - + - + - - + + + + - - - - - - - - - - - - - - - - - - - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - + - + - + - + + + + - + + + + + + + + + + + + - + - - + + - - + + + + - - + + - - + + - + + + + + + - + - + - + - - - - - - - - - - + - - + + + + - - + + + + - - + + - - + + - - + + + + + + + + + + + - - + + - - + + - - + + - - + + + + - - + + - - + - + - - - - + - + - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + + + + - + - - - - + - - + + - - + + - + - + - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - - - - + - + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - - - - + + - - - - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - - - - - - - - - - - - + + - - + + - - + + - - - - + + - - + + - - + + - - - - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - + + - - - - - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + - - + - - - - + - + - - + + - - + + - - + + + + + + + + - - - - - - - - - - - - - - - + - + + + + - + + + + + + + + + + - + - + - + - - - + + + - - + + - - + + - - + + + + + + + + + + + + + + + + + - + + + + - + + + + - + + + + - + + + + - + + + + - + - - + + - - + + + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + + + + + + + - + - + - + - + - - - - - - + + + - - + + - - + + - - - - + - + + + + - + - + - + - + - - - - - + + + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + - + - + - - + + - - + + - - + + - - + + + + - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - - - - + - + - - - + + - + - - - - - - - + + - - - - + - - + + - - - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - - - + + - - + + - + - - + + - - - - + + - - - - + + - - - - + + - - - - + + - + - + - - - - + + - - - - + + - - - - + + - - + + - + - + + + + - + + + + - + - + + + + - + + + + - + + + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + + + - - + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + + + - - + + + + - - + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + + + - - + + - - + + + + - - + + + + - - + + - - + + + + - - + + - - + + - - + + - + + + + - + - + - + - - - - - - - + - + - + + + + - + - - - - - - - + - - + + - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - - - - - - - - + - + - + - + - - - - - - - - - - - - - - - - - - + - - - - - - - - - + - - - - + - + - + - + - - - - + - + - - + + + + + + + - + - - - - - - - + - - - - + - - + + - - - - + - - - - + - - - - + + - + - + - - - - + - - + + - - + + - - + + + + - - + + - - + + - + - + - - - - - - - - - - - - - - - - - - - - + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - + - + - + - - + + - - + + - - + + + + + + + + + + + + + + + + + - + - - - - + - + - + - - + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + - - + + - - + + + + - + + - + - + - + + + + - + + + + + + + + + + + + + + + + - + - + - + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - - - + - - + + - - + + - - + + - - + + - - + + - - - + + + - - + + - - + + - - + + - - - - + - - - - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - + - + - - + + - + + + + - + + + + + + + - - - + + + - - + + - - + + - - - - + + - - - - + + - - + + + + + + + - + - + - + - - - - + - + - + - + - + - + - - + + + + - - + + - - + + + + - + - - - - - - - - - - - - - - - - - - - + - + - - - - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - - - - - + - + - - - - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - + + - + - + - + - + + + + + + - + - - - - + + - - + + - + - + - - - - + - + - - + + - - + + - - - - - - - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - + + - - + + - - + + - - + + - + - + + + + + + + - + - + - - + + - - + + - - + + + + + + + + + + + + + + - + - - - - + - + - - + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + - - + + - + + + + + + + + + - + - + + + + - + - + - - + + - - - - + + + + + + + + + + + - + - + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + - + - - - - + - + + + + + + + + + + + + + + + + + + + - + - - + + - - + + - + - + - + - - - - - - - - - - + - + - - - - - - - - - - + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + - - + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - + + - + - - - - + - - - - + - - - - - + + - - + + - - + + - - + + - - + + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - - + + - - + + - - - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - - + + - + - - - - - + + - - + + + + + - + - + - + + + + + + + + + + + + + + + - + + + + - + - - + + - + + + + - + - + - - + + + + + - + - + - - - - - + + - + - - - - - - - - - - - - - + + - + - - - - + - - - - + + - + - + - + - + - + + + + - + + + + - + - - + + - - - - + + - - + + + + + - + - + - + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - - + + + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + + + + - + + + + - + - + - + + + + + + + - + + + + + + + - + - - - - + - + + + + + + + - + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + - - + - + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + - + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - + - + + + + - + - - - - - + + + + + + + + - + - + - + - - - - + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + - + - + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - - + + - - + + - - + + - - + + + + + - + - + - + - + - + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - - - - - - - - - - - - - + - - - - + + - + + + + + + + + + + - - - - - - - + - - - - - + + - + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - + + - - + + - - - - - - - - - - - + - + - - - - - + + - + - - - - + - + - + - - - - - - - - - - - - - - - - - - + - - - - - - - + + - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - + + - - - - - + + - + - + - - - - - - - - - - - - - - - - - + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - + + - - - - + + - - + + - - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + - - - - + - - + + - + - + - + + + + - + + + + - + - - - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - + - - - - - - - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - + - + - + - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - - - + + - - - - - + + - - + + - - + + - - - - + + - - + + - - + + - - - - + + - - + + - - - - + + - - - - + + - - + + + + + + + + + + + - - - + - + - - - - + - + - + - + - + - + - + - + - + - + - - - - - - - - - - - - - - - - + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - + - + + + + - + - + - - - - - - - + + - - - + - + - + - + - + - - + + - - + + - - + + + + + + + + + + + - + - + - + - + + + + - + - - - - - + + - - + + - - + + - - + + - + - - + + - + + + + + + - + - + - - - - + - - + + - + - + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - - - - + + - - + + - - - - + + - - + + - - + + - - - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - + - + - + - + - + - + - - + + - + - + - - - - - - - - + + - - + + - - + + - - + + - - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - - - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - + + - - - - - + + - - - - - - - - + - + - + - + - + - + - + + + + - + - - + + - - + + - - + - - - - + - + - - + + - - + + + + + + + + - + - + - + + + + - + + + + - + + + + - + + + + - + + + + - + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + - + - + - + - + - + - + - + - - + + + + - - + + - + - + - + - + - + - + - + - + - + - + - - - - + - + - + - + - + - - - - - - + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - - + + - - + + + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - - - - - - - + - + - + - + + + + + + + - + - + - + - + - + - + - + - - - - + - + + + + - + - - + + - - + + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - + + - - - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - + + - - - - - + + - - + - + - + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - - - + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - + - + - + + + + + + + - + - + - - - - + + - - + + - + - + + + + - + + + + - + - + - + - + - + - + - + - + - + - + - + - + + + + - + + + + - + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - + + - + - + + + + - + + + + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - + + + + + - - - + - + - - + + + + + - + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - - - - - - + - + - + - + - - - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + - + - + - + - + - + - + - + - + - + - - - - + + - - + + - - + + - - - - + + - - - - + + - - + + - - - - + + - - + + - - + + - - + + - - - - + + - - - - + + - - + + - - - - + + - - + + - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - - - - + - - + + - + + + + - + - + - - - - + + - - + + - - - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + - - - - + + + + + - - - - + + + + - - - - + + + + - - - + + + + - - - - + + + + - - - - - + + + + - - - + + + + + - - - + + + + + - - - - + + + + + + + - - - + + + - - - + + + - - - - + + + + + + - - - - + + + + - - - - - - + + + + + - - - + + + - - - - + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + + + - - - - + + + + - - - - - - + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + - - - - - - - + + + - - - - - - + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + - - - - + + + + - - - - + + + - - - + + + + - - - - - + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + - - - + + + + + - - - + + + + - - - - - + + + + - - - + + + + - - - - - + + + + + - - - + + + + + + - - - + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + - - - - - + + + - - - - + + - - - - - + + + + - - - - - + + + - - - - + + + - - - - + + + + - - - - + + + - - - - + + + - - - - + + + + - - - - + + + - - - - - + + + - - - - + + + - - - - + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + - - - - + + + + - - - - + + + + + + + + + + + + + + + + + + + + - - - - + + + + + - - - + + + + - - - + + + + - - - + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + - - - + + + - - - + + + + - - - - - + + + + - - - + + + + + - - - - - + + + + + - - - + + + + - - - - - + + + - - - + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + - - - - + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + - - - + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - + + + - - + + - - + + + + + - - - - + + - - + + - - - - + + - - + + - - - - + + - - + + + + - - - - + + - - + + - - - - + + - - + + - - - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + - - + + - - - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + + - - - - + + - - + + - - - - - + + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - + + - - + + + + + - + - + - - + + - + - + - + - + - - - + + + - - + + - - + + - + - + - + + + + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - - - - + - - - - - - - - - - - - - - - - - - - - + + + - + - + - + - - + + - - - - + + - + + + + + + + + + + + + + - + + + + + + + + + + - + - + + + + + + + + + + - + - + - - + + - - + + - - + + - - + + - - + + - - + + + + + - - + + + + + - - + + - - + + + + - - + + - - + + + + + + + - + - + - - + + - - + + + + - - + + - - + + + + + - + + + + + + + + + + + + + - - - - - - - - + + + + + + + + + + + + + + + + + - + - + - + - + - + - - + + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + - + - + - + + + + - + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + + + - - + + + + - - + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + - - + + - - + + - - + + - + + - + - + - - - - + - - + + - + - + + + + - + - + + - + - + - - - - + + - - - - + + - + - + - - + + + + + + - - + + + + + - - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + - + + + + + + + + + + + - + - - + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + - + - + - + - - - - - - - - - - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + - - + + - - - - + + - + - + - + - - - - - - - - - - + - - + + - + - + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + - + - + - + + + + - + - - + + + + - - + + - - + + - - + + + + + + + + + + + + + + + + - + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + - + - + - - - - + - + - + - + + + + + + + - + + + + + + + - + + + + - + - - + + - - + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + - + - + - + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - - + + + + - - + + - + - + - - - - + - + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + - - + + + + - - - - + + - - + + + + - - + + - - - - + + + + - - + + - - + + - - - - + + + + - - + + - - + + + + - - - - + + - - + + + + - - + + - - - - + + - - + + + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + - - + + + + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - - - + - - - - + - - - - - - - - - - - - - - - - + - + - + - + - + - - - - + - + - + - + - + - + - - - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - - - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + - - + + + + + + + + + + - - - - + + + + + + + + + + - - - + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - + + - - - - + + - - + + - - + + - - + + + + + - + - - + + - - + + - + - + - - + + - - + + - - + + - + - - + + - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + + - - + + + + + - - + + - + - - - - + - - - - + - + - + - + - - + + - - + + - + + + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - - - + + - - + + - - + + - - - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - - - + + + + + + + + + + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + - - - - + - - + + - - - - - - - - - - - - + + + - + + + + - + - - + + - - + + - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - + - + - + - - + + + + + + + + + + + + + + + - + + + + + + + - + - + - + - - - - + - + + + + + + + + + + - + - - + + - - - - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - - - - - - - - - - - - - - - - - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + - + - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - - - - + - + - - - - + - + + + + + + + + + + + + + - + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - + - - - + + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + - - - + - + - + - + - + - - + + + + + + + + + + + + + + + + + + + + + + - + - + - + + + + + + + + + + - - - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - + + + + - + - + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + + + + + + + + + + + + + + + + + + + + + + - - - + + + + - - + + + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + + + + + + + + + + - + - + - + + + + + + + + + + - + - - - - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + - + + - + - - + + - - + + + + - - + + - - + + + + - - + + + + - - + + - - + + - - + + - - + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - + + + + - - + + - - + + - - + + - + + + + + + + - + - + - + - + - + - + - - + + - - + + + + - - + + - - + + + + - - + + - + + - + - + - + - - + + - - - - + - + - - + + + + + - + - - - - + - - + + - - + + - - + + - - + - + - + - + - + + + + + + + - + - + - - + + - - + + + + + + + + + + + + + + + + + + + + - + - + + + + - + - + - + - - - - - - + - - + + - + - + - - - - - + + - - + + - + - + - - + + - + - + - + - - - - - - - - - - - - + - + - + - + - - + + + + - - + + - - + + - + - + - + - + - + - + - - - - + - + - - - - - - - - - - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + - + + + + - + + + + - + - + - + - + - + - - - - - - - - - - - - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - + - - + + + + + - - - + + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + - + - + + + + + + + + + + - + + + + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - - + + - - + + - - - - + + - - + + + + + + + + + + + + + + + + + - + - - - - + - + - + - - + + - - + + - - + + - - + + + + + - + + + + + + + + + + + + + + + - + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + + + - - + + - + - + - + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - + + + + - + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - + + + + + + + + + + + + + - + - - + + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - - - - - - - + - - - - - - - - - - - - - - - - + - - + + - - + + - - + + - - + + + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - + - - + + - - + + + + - - + + + + - - - - + - - + + + + - - + + - - + + - - + + - - + + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - - - - - - + + - - + + - - + + - - - - + + - - + + - - + + - - - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + + + + + + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - + - - - - + - - + + - - + + - - + + - - + - + + + + - + - - + + + + + + + + + + + + + + + + - + - - + + + + + - + - + - + - - + + + + + + + + + + + + + - + - - + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + - + - + + + + - + + + + - + - - + + - + - - + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + + + + - + + + + - + + + + - + + + + - + - - + + + + - - + + - - + + - - + + - - + + - - + + + + + + + - + - + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - - - + + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - + - + - - + + + + + - + - + - - + + - - + + - + - + - + - + - + - - + + + + + + + + + + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + + + + + + + + + + + + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + - - + + + + + - + - - - - + - - - - + - + - - - - + - + - + - - - - + - + - - - - + - - - - + - - - - + - + - + - + - - - - + - - - - - - - - - - - - - - - - + - - + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - + - + + + + - + - + - + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + + + + + + + + + + - + - - + + - + - - - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + + + + - + - + - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - - - - + + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + + + - - + + - - + + + + + + + + + + + + + + - + + + + - + - + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - + - + + + + + + + - + - + - - - - - - + - - - - - + + - + - + - - - - - - - + + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - + - + - - - - - - - - - - - - - - - - + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - - - - + - + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - + - + - - - - - - - - - - - - - + - - + + + + - - + + - - + + - - + + - - - - - - - - - - - + - + - + - - + + + + + + + - - + - + - + - - - - + - - - - - - - - - - + - + + + + + + + + + + + + + + + - + - + - + - + - + - + - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - - + - - - - - + + + + + - - + + - - + + + + + - - + + - - + + + + + - - + + - - + + - + - + - + - - - - + - + + + + + + + + + - + - - - - - - - - - + + + + + + + + + + + + + + + - + - + - + - + - + - + - - - - + - + + + + - + + + + - + - + - - - - + - - - - + - - + + - + - - + + - - + + - + - + - - + + - - + + - - + + - - + + - - + + + + + - + + + + + + + + + + - + - + - + - + - + - + - + - - - - + + - - + + + + + + + + + + + - + - + - + - - - - + - - - - + - - - - + - + - - - - + - - - - + - + - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + - + - + - + - + - + - + - + - + - + - + + + + - + - + + + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - - - - - - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + + - + - + - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - + + + + + + + + - + + + + + + + + + - + - - + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + - - - - + - + - + - + - + - + - - - - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + - + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + - + - + - - - - + - + + + - + - + - + - + - + + + + + + + + + - + - + - + - + - + - - + + + + + + + + + + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + - - - + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + - - - - + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + - - - - + + + + + - - - + + + + - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + + + - - - - + + - - + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + - - - + + + + + - - - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + - + - + - + - + - + - + - + - + - + - + + + + - + - - - - - - - - - - + - - + + - - + + - - - - + - + - + - - - - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + - + - + - - - - + - - - - + - - - - + - + - + - - - - + - + - + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - - + + + - - + + - - + + + + - - + + - - + + - - + + + + + + + + - + - + - + - - - - - - - + - + - - - - + - + - - - - + - - - - + - - - - - - - - - - + - + - - + + - - + + - + - + - - - - + - - - - - - - - - - - + + + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + + + + - + - - - - - - - - - - + - - + + - - + + - - - - + - + - + - + - - - - - - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + + + - - + + - - + + - - - - - - + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + + + - - + + - + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - + + + + - + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - - - - - - - + - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + + + + + + + + + + + - + - + - - + + - - + + - + + + + - + + + + - + - - - - - + + - - - - - - - - - - - - - - - + + + + + + + - + - - - - + - + - + - + - + - + - + + + + - + + + + - + - - - - - - - - - - - - - - - - - - + + - - + + - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - + - + - + - + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - + + - + - + - + - - - - + - - - - - - - - + + - - + + - - + + + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - + + - + - + - + - - - - - - - - - - - - - + + - - - + + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - + - - + + - + + + + + + + + + + + + + + + + + + + + - + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - + + + - - - + + + + + + + - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + - - - - - - - - - - - - - - - - + - + - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + + + - - + + - - + + + + - - + + - - - - - - - - - + + + + + + + + - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - + + - - + + - - + + - + - + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + - + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + + + + - + - + - - + + - - + + - + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + - + + + + - + - - - - + - - - - + - + - + - + - + - - - - - - - + - - - - - - - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + - - - - + - + - + - + + + + - + - - - - + - - - - - - - + - + - - - - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - + - + - + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + + + - - - - + - - + - + - - + + + + - - + + + + - - + + + + - - + + + + - - + + + + - - + + + + - + - + - + - - + + + + + + + - + - + + + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - - - - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - - - - + - - - - + - + - + - + + + + + + + - + - - - - + - + - + - - - - - - - - - - - - - + + - - + + + + + + + + - + - + - - - - - - - - - - + - - + + - + - + - - - - + - - - - + - + - - - - + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + + + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + + + + + - - + + - - + + - - + + - - + + - + - - - - + - - - - - - - - + + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - - - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + + + + - - + + - - + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + - + - + - - + + + + + - + - + - - + + - - + + - - + + + + - - + + - + + - + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + + + + - + - - - - + - - - - + - - - - + - - - - - + + - - + + - - - - + + - - - - + + - + + + + - + + + + - + + + + - + + + + - + + + + - + - + - - - - + - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + + + + - + - + + + + - + - + - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + + + + - + - + - - - - - - - - + + - - + + - + - + - + - + - - + + - - + + - - + + - - + + + + + + + + - + - + - - + + - - + + - - + + + + - - + + + + + - + + + + + + + + + + - + - + - + - + - + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + + + - - + + - - + + + + - - + + - - + + - - + + - - - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - + - - + + - - + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - + - + - - + + - - + + + + - + - - + + + + - - + + - - + + - - + + + + - - + + - - + + + + - - + + + + - - + + - - + + + + + - + - - - - + - + - + - - - - + - + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + - + - + - + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - - - - + - + - + - + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - + + + + + + + - + - - + + - - - - - + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - - - - + - + - + - + - + - + - + - + - + - + - + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - + - + - + - + + + + - + + + + - + + + + - + - - - - - - - - - - - - + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + + - + - + - - + + - - + + - - + + - - + + - - + + + + + + + + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - - - + + - - - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - + + + + + + + + + - - - - + - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + + + + + + + + + - + - + - + - - - - - - - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - - - + - - + + - - + + - - + + - - + + - - - - + - - - - - - - - - - - - - - - - + - + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + - + - + - + - - - - + - - - - + - - - - + - + + - - - - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - - - - - - - + - + - - + + + + - - + + - - + + + + - - + + + + - - + + + + + - + - - - - - - + - - + + - - + + - - + + - - + + - - + + - + - + - - - - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + + + + - + - - - - - - - - - - + - - + + - - + + - - - - + - + - + - + - + - - - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - - - - - - - - - - + - + - + - + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - + - + - - + + + + + - + - + - + - + - - + + - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - + - - + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + - - - - - - - - - - + - + - - - - - - - + - - + + - + - + - - + + - - + + - + - + - - + + + + + - + - + - + - + - + - + - + + + + - + + + + - + + + + - + + + + - + + + + - + + + + - + - + + + + - + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + - + - + - + + + + - + - + - - - - - - - - - - - - - - - - - - - - - + - + - + - + - + - + - + - + + + + - + - - + + + + - - + + - - + + - - - - + - - + + - - + + - + - - - - + - + - + - + - - + + + + + + + + + - + + + + - + + + + - + - + - + - + - - - - - - - - + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + - - - - - - - - - - - - + - - - - + - + - - + + + + + + + + - - + - + - + - + + + + - + - - + + - - + + - - + + - + - + - - + + - - + + + + + + + + + + + + + - + - - - - + - + - - - - - - - - - - + - + - - - - - - - - - - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - + + + + + - - - + + + - - - + + + + - - - + + + + + + - - - - + + + + - - + + + + + + + + + + + + + + + + + + - + - - - - + - - - - - + + - + + + + - + - + - - + + - + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + + + + - + + + + + + - + - + - + - + - - - - - - + - - - - - - - - - - - + + - - - - + + - - + + - - + + - - + + - + - + - - + + - + - + - - - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + - - - - - - - - - - - + + + + + + - - - + + + + + + + + + + + + + + - - - - + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + - - - - + + + - - - + + + - - - + + + + + - - - - + + + - - - - - - - - + + + - - - - - - + + + + + - - - + + + + + + + + + + + + - + + + - + - + - + - + - - - - - - - + - - + + - - + + - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + + + + - + - + - + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - + - + - - + + - - + + - - + + - - - - + + - - + + - - + + + + + - + + + + - + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + - + - - - - + - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - + - - - - + - + - - - - - - - - - - - - - + + - - + + - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - + - + - - - - - - - - - - - - - - - - - - + + + + + + - + - + - + - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - - + + + + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + + + + + + + + + + - + - + - - + + + + + + + + - + - + - + - - - - - + + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - - - + + + + + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + - + - + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + + + + - + - + - + - + - + - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - + - - + + - - + + - - + + + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - + + + + + + - - - - + - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - - - - - - - - - - - - - - - - - - - - - - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + + + + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + + + + + + + + + + + + + - + - + + + + - + - + - - - - - - - + - + - + - + - + - - + + + + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - + - - + + - - + + - - + + + + - - + + - - + + - + - - - - + + - - + + - - + + + + + - + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - - - - - - - - - + - - - - - - - + - + - + - - - - - - - - - - + - + - - - - + + - - + + - - + + - - + + - + - + - - + + - - + + + + - - - - - - - - - - + - - - - + - + - - + + - - + + - - + + - - + + + + + - + - + - + - + - + + + + - + + + + - + - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + + + + + + + + + + + + + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - - - - - - - - - - - - + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - + - + - + - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + + + + + + + - + - + - + - + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + - - - - - - - - + - + - - + + + + - + + + + - - - - - - - + - + - - + + - + - + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - - - + - + - - - - + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + - + - - + + - - + + - - + + + + - - + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + + + + - + - + - + - + + + + - + - + - + - + - + - + - - - - + - - - - - + + - - + + - - + + - - - - - - - - - - - - - + - + - - - - - - - + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - + - + - - + + - - - - - - - - - - - - - - - - - - - - - + + - - + + - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - + - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + + + + + + + + - - - - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - - + + - - + + + + - - - - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - + + + + + + - - - - + + + + + - - - - + + + + + - - - + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - + - + - + - + + + + + + + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + + + + - + - - - - - - - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + - + - + + + + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + - + + + + + + + + + + + + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + + + + + + + + + + + + + + + + + + - + - + - - + + - - + + + + + + + + + + + + + - + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - - + + + + - - + + - - + + - - + + - + - + - - + + - - + + - - + + + + - - + + - - + + + + + - + - + - + + + + + + + - + - + - - + + + + + - - - + + + - - + + - - + + - - + + - - + + - - - - + + - - + + - + - + - - + + + + + + + + + + + + + + + + - + - - + + - + - + - - + + - - + + + + + - + - + - - + + - - + + - - + + + + - + - - + + - - - - + - + - + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + + + + + + + + + + - + - - - - + - - - - + - + - + - + - - - - + + + + + - + - - + + + + + - + - + - + - - - - + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + - + + + + - + - + - - - - + - - - - + - + - + - + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - + + - - + + + + + + + + + + - + - + + + + + + + + + + - + - - + + + + - - + + - - + + + + - + - + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + - - - - - - - - + + + + + - - - + + + + - - - + + + + - - - + + + + + + - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + - - - - + + + + + - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - + + + + + - - - - + + + + + - - - + + + + + - - - + + + + + - - - - + + + + + - - - + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + + + + + + + - - - - - - - - - - + + - - - - + - - - - + - - - - + - - - - - - - + - - - - - + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - + - + - + - + - + - + - + - - - - - - - - - - - - - + - - + + - + - + - - - - - - - - - - - - + - - - - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + - - - - + + + + - - - - - + + + + + + - - - - + + + + + - - + + + + + - + - + + + + + + + + + - + - + - - - - - + + + + - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + + - - + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - - - + - + + + + - + + + + + + + + + - + - + + + + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - + + + - - - - - - - - - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - + + + + - + - - + + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - + - - - - + - - + + - - + + + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + - - - - - + + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - + + - - + + - + - + - + - - - - - - - - - - - - - - - - - - - + + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + + + - - + + - - + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - + - + - + - - - - - - - - - - - - - - + - + - - + + - - + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + - + + + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - + + - - + + - - - - - + + - - + + - + - + - + - + - + - + - + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + - - + + + + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - + - + - - - - + - - + + - - + + - - + + + + - - + + - - + + - - + + - + - - - - + - - + + - - + + - - + + - - + + - - + + + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - - + + + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - - - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + - + - - - - + - - - + + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - + - + - + - + + + + + + + - + - - + + - - + + - - + + - - + + - + - + - - - - + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + - - - - + - + - + - + - - - - - - - - - - - - - - - - - - - - + - - - - + - + - - - - + - + - + - + - - + + - - + + - + - + + + + - + + + + + + + - + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + + + - - + + - - + + - - - - + + - - + + - + - - - - + - - - - + + - - + + - + - + - - - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - + - + - + - - - - - - - + - - + + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - - + + + - - + + - - + + - + - + - - - - + - - + + + + - - + + - - + + - - - - - - - - - - + - + - + - + + + + - + - + - + - + - + - + + + + - + - + - + - - + + + + + - + - - + + + + + - + - + - + - + - - - - + - + - - + + - - + + - + - - + + + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + - + - + - + - - + + - - - - + + - - + + - - - - - + + - - - - + + - - + + - - - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + - + - + - + + + + + + + + + + - + + + + + + + + + - + - - + + - - + + - - - - + + - - + + - - - - + + + + + + + + + + + + + + + + - + + + + - + + + + - + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + - - + + - - + + - - + + + + - - + + - - + + - - + + - + + + + + + + + + + + + - + - + + + + + + + - + - + + + + - + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + + - - + + - - + + - - + + + + + - + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + + + + - + - + + + + - + - - + + + + - - + + + + + + + + - + - + - - + + - + - + - - + + - - + + + + + - + - + - - + + + + + + + - + - + + + + - + + + + - + - - - - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - - - - + + - - + + - + - + - - + + - - + + - - + + - - + + + + + - + - + + + + - + - - + + + + - - + + + + + + + - + - - + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + - + - + - + - - + + + + - - + + - + - + - + - + - + - + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - - - - - - - - - - - - - + - - - - - - - - - - - + + - - + + - - + + - - + + - - + + + + - - + + - - - - - - - + - - + + - - + + - - + + - - + + - - + + - + + + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + - + - + + + + - + - + - + - + - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - + + - - + + - - + + - - + + - + + + + + + + - + - - + + - - + + + + + - + - + - - + + + + + - + - - - - + + - - + + - - + + - - + + - + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + - + + + + + + - + - + - + - - + + + + + + + + - + + + + + + + - + + + + - + + + + - + + + + - + + + + - + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - + - + + + + + + + - + - + - + - + - - - - + - - - - - + + + + - - + + - - + + - - + + + + - - + + - + - - - - + - - + + - - + + - - - - + + - - + + - - + + - + - + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + + + + - + - + - + - + - + - + - + - - - - - - - - - + - - - - - + + - - + + - + - + + + + - + - - + + - - - - + + - - + + - - + + - - - - + + - - + + - + - + - - - - + + - - + + - - + + - - + + - - + + - - + + - + + + + + + + + + + - + + + + - + + + + + + + - + - - + + - + - - - - - - - + - + - + + + + + + + + + + + + + - + - + - - + + - - + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + - + - + - + - + - + - - - - - - - - - - + - - - - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - + + + + + + + + + - + - + + + + - + - + - + - + - - - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - + + + + - + - + - + - + + + + - + + + + - - - + + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - - + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + - + + + + + + + - + - + - - + + + + + + + + + + + + + - - + + - - + + - + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - - - + + - + + + + + + + - + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + - - + - + - - - - + - + - + - + - - - - + - - - - + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - + + + + + + + + + - + + + + - + - - + + + + + + + + + + + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + - - - + + + - - + + - - + + - - + + - - - - + - - + + - - - + + + - - + + - - + + - + - - + + - - + - + - + - - + + + + + + + + + + + + + + + + + - + - + - + - + - - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - + + + + + + + + + + + + + - + - - + + - + - + - + - + - + - - + + + + - - + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + - + - + - + + + + - + - + - + + + + + + + + + + + + + + + + + + + + + - + - + + + + - + - + - + - - + + + + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - - + - + - + - + + + + + + + + + + + + + + + + - + - + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + - + + + + + + + - + - - + + + + - - + + - - + + - - + + - - + + + + - - + + + + - - + + - - + + - - + + - - + + + + - - + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + - - + + + + + + + - + - + + + + - + - + - - + + - + - + - - + + - - + + + + - - + + - - + + - - + + - - + + + + - - + + - + - + - - + + + + + - - - - - - - + - + + + + + + + + + + - + - - + + + + - - + + - - + + + + - - + + - - + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - + - + - - + + + + + + + + + + + + + + + + + - + - - - - + - + + + + + + + + + + + + + + + + - + + + + + + + + - + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - + + + + - - + + - - + + - + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + + + - - + + - - + + + + - - + + - - + + - + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + + + - - + + - - - - + + - - + + - - + + - - - - + + - - + + - - + + + + + - + - + - + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - - - - + - + - + - - - - + - - + + - - - - + - - + + - + - - - - + - + - - - - - - - - + + + + + + + + + + + + + - - + + - - + + - + - + - - + + - - - - - - - - - - + + - - + + + + + + + + + + + - + - - - - + + - - + + + + + + + + + + + - + - + - + - - - - - - - - + - + - + - + + + + + + + - + - - + + + + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + - - + + - - + + - - + + + + + + + + - + - - - - + - - + + + + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + - + - - + + - - + + + + + + + + - - - + + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - + + + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + + + - - + + + + + + + + - + + + + + + + + + + + - + - + + + + + + - + - + - + - - - - - - - + - - - - - + + - - + + - - + + - - + + - + - + - - - - + - - - - + - - - - + - + - + - - - - + - + - + - + + + + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - - - - - - - - - + - - + + - - + + - - + + - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - + - + - + - - - - + - + - - + + - - + + - - + + + + + + + + - + + + + - + - + - + - - + + - - + + - - - - + + - - + + - - + + + + - + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - - - - - - - - - - + + - + + + + + + + + + + + + + + + + + + + + + + - - - - + - - + + - - + + - + - + - - + + - + - - - - - - - - - - + - - - - - - - - - + - - - - + - - - - + - + - - - - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - - + + - + - - + + - - - - + + - - + + - - + + - - + + + + + - + - - + + + + + + + + - + - - - - + - + - + - + - - + + - - - - + + - - + + + + + + + + - + - + - - + + - - + + - - + + - - - - + + - - + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + + + - + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - - - - + + - - + + - - - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + - + - + - - - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - - + + - - + + - - - - + - - - - + - + - - - - + - + - + - + - + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + - + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - - + - + - - - - + - + - + - + - + - + - - - - + - + - + - + - + - + - + - + - + - + - + + + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - + + + + + + + + + + - + - + + + + - + + + + + + + + + + - + + + + + + + - + + + + + + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - + - + - + - + - + - + + + + - + - + - + - - + + - - + + + + + + + + + - - + + - - + + - - - - - - - + + + + + + + + + - + - + - + - + + + + + + + + + + + + + - + - + + + + - + - - + + - - + + + + - - + + - - + + - - + + - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + - - - - - - - - - + + + + + - + - + - + - - + + - - + + + + + + + - - - - - - - + - + - - + + - - + + + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - + + - - + + - - + + - + - + - - - - + - + - + - - - - + - - - - + - - - - - - - - + + - + - + - - + + - - + + - - - - + - + - + - + - - - - - + + - + + + + - + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - - + + - - + + - - - - + + - - + + - - + + + + + + + + + + + + + + + + - + - + - + - + - + - + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + - + - + - + - + - - + + - - + + - - + + - - + + + + - - + + - - + + - + - + - - + + + + + + + + + + + + + + + + + + - + - - + + + + + + + - + - - + + - - + + - - + + + + - - + + - - - - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + - + - + - + - + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + - + - + - + - - - - - + + + + - - + + - - - - - - - - - - + + - + + + + - + - + - - + + + + - - + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + + + - - + + - - + + + + - - + + + + + + + + - + + + + - + - - + + - - + + - - + + - - + + - - - - + + - - + + - - + + - + - - - - - - - + - - + + - - + + - - + + - - + + - - + + - - + + - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + - + - + + + + - + - - + + - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + + + + - + - + + + + - + + + + + + + + + + + + + - + + + + + + + + + + - + + + + - + + + + - + + + + - + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + - - + + - - + + - - - - - - - + - + - - + + + + + - - - - - - - - - - + - + - + - + - + - + - + - - + + + + - - + + + + - - + + - - + + - - + + + + + + + + + + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + - + - + + + + + + + + + + - - - - - - - + - + - + - + - + - + - + - + - + - - + + - + - + - + - + - + - + - - - - + - + - + - - - - + - + - - - - - - - - - - - - - + - + - + - + - + - - - - - - - - - + - + - + - - - - - - - - - - - - - - + - - + + - + - + - + - - - - + - - - - - - + - - - - - - - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - - - - - - - + - - - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - + + - - + + - - - - + + - - + + - - + + - - + + - + - - - - + - - + + - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + - + + + + + + + - + - + - + - + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - - - - - - - - - - - - + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - - - - + + + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - + - - - - + - - - - + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + - - - - + + + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/abi_gki_aarch64_cuttlefish_whitelist b/abi_gki_aarch64_cuttlefish_whitelist new file mode 100644 index 0000000000000000000000000000000000000000..b795c2c9f18ec4c99e93f1dcd60c59d637f7c978 --- /dev/null +++ b/abi_gki_aarch64_cuttlefish_whitelist @@ -0,0 +1,835 @@ +[abi_whitelist] +# commonly used symbols + add_wait_queue + alloc_etherdev_mqs + __alloc_pages_nodemask + __alloc_skb + __alloc_workqueue_key + __arch_copy_from_user + __arch_copy_to_user + arm64_const_caps_ready + bus_register + bus_unregister + cancel_delayed_work_sync + capable + __check_object_size + __const_udelay + consume_skb + cpu_hwcap_keys + cpu_hwcaps + cpumask_next + cpu_number + __cpu_online_mask + cpus_read_lock + cpus_read_unlock + debug_smp_processor_id + delayed_work_timer_fn + destroy_workqueue + _dev_err + device_create_file + device_register + device_unregister + _dev_info + __dev_kfree_skb_any + devm_kmalloc + dev_queue_xmit + dev_set_name + _dev_warn + dma_alloc_from_dev_coherent + dma_buf_export + dma_buf_fd + dma_buf_put + dma_fence_release + dma_fence_wait_timeout + dma_release_from_dev_coherent + down_write + dummy_dma_ops + ethtool_op_get_link + eth_validate_addr + event_triggers_call + fd_install + finish_wait + flush_work + fput + free_irq + free_netdev + __free_pages + get_random_bytes + get_unused_fd_flags + ida_alloc_range + ida_free + init_net + init_timer_key + init_wait_entry + __init_waitqueue_head + jiffies + kfree + kfree_skb + __kmalloc + kmalloc_caches + kmem_cache_alloc_trace + kobject_put + kvfree + kvmalloc_node + __ll_sc_atomic64_add + __ll_sc_atomic64_andnot + __ll_sc_atomic64_or + __ll_sc_atomic_add + __ll_sc_atomic_sub_return + __ll_sc___cmpxchg_case_mb_4 + __local_bh_enable_ip + lock_sock_nested + memcpy + memset + memstart_addr + misc_deregister + misc_register + module_put + msleep + __mutex_init + mutex_lock + mutex_lock_interruptible + mutex_trylock + mutex_unlock + netdev_err + netdev_info + netdev_rx_handler_register + netdev_rx_handler_unregister + netdev_upper_dev_unlink + netif_carrier_off + netif_carrier_on + netif_device_detach + netif_tx_stop_all_queues + netif_tx_wake_queue + noop_llseek + nr_cpu_ids + param_ops_bool + param_ops_charp + param_ops_int + param_ops_uint + passthru_features_check + pci_disable_device + pci_enable_device + pci_read_config_dword + __pci_register_driver + pci_set_master + pci_unregister_driver + __per_cpu_offset + perf_trace_buf_alloc + perf_trace_run_bpf_submit + platform_device_add + platform_device_alloc + platform_device_del + platform_device_put + platform_device_unregister + __platform_driver_register + platform_driver_unregister + preempt_count_add + preempt_count_sub + preempt_schedule + preempt_schedule_notrace + prepare_to_wait_event + printk + put_device + __put_page + queue_delayed_work_on + queue_work_on + _raw_read_lock + _raw_read_unlock + _raw_spin_lock + _raw_spin_lock_bh + _raw_spin_lock_irq + _raw_spin_lock_irqsave + _raw_spin_unlock + _raw_spin_unlock_bh + _raw_spin_unlock_irq + _raw_spin_unlock_irqrestore + __rcu_read_lock + __rcu_read_unlock + refcount_dec_and_test_checked + refcount_inc_checked + register_netdev + register_shrinker + release_sock + remove_wait_queue + request_threaded_irq + reservation_object_add_excl_fence + rtnl_lock + rtnl_unlock + schedule + schedule_timeout + schedule_timeout_uninterruptible + seq_printf + sg_free_table + sg_init_one + sg_init_table + sg_next + skb_clone + skb_put + sk_free + snd_device_new + snd_info_create_card_entry + snd_pcm_alt_chmaps + snprintf + sprintf + sscanf + __stack_chk_fail + __stack_chk_guard + strcmp + strcpy + strlcpy + strlen + strncpy + strstr + synchronize_irq + synchronize_net + system_wq + trace_define_field + trace_event_buffer_commit + trace_event_buffer_reserve + trace_event_ignore_this_pid + trace_event_raw_init + trace_event_reg + trace_handle_return + trace_raw_output_prep + trace_seq_printf + unregister_netdev + unregister_netdevice_queue + up_read + up_write + vmap + vunmap + wait_woken + __wake_up + __warn_printk + woken_wake_function + ww_mutex_lock_interruptible + ww_mutex_unlock + +# required by binfmt_misc.ko + bin2hex + bprm_change_interp + clear_inode + __close_fd + copy_strings_kernel + _ctype + current_time + d_drop + default_llseek + dentry_open + d_instantiate + dput + drop_nlink + filp_close + free_pages + __get_free_pages + get_next_ino + iput + kernel_read + kill_litter_super + kstrtoint + lockref_get + lookup_one_len + mount_single + new_inode + open_exec + prepare_binprm + _raw_write_lock + _raw_write_unlock + __register_binfmt + register_filesystem + remove_arg_zero + search_binary_handler + simple_fill_super + simple_pin_fs + simple_read_from_buffer + simple_release_fs + simple_statfs + strchr + string_unescape + strrchr + unregister_binfmt + unregister_filesystem + would_dump + +# required by blk-mq-virtio.ko + blk_mq_map_queues + +# required by dummy-cpufreq.ko + cpufreq_generic_attr + cpufreq_register_driver + cpufreq_unregister_driver + +# required by dummy_hcd.ko + device_remove_file + ktime_get_ts64 + memzero_explicit + mod_timer + platform_device_add_data + scnprintf + sg_miter_next + sg_miter_start + sg_miter_stop + usb_add_gadget_udc + usb_add_hcd + usb_create_hcd + usb_create_shared_hcd + usb_del_gadget_udc + usb_disabled + usb_ep_set_maxpacket_limit + usb_gadget_giveback_request + usb_gadget_udc_reset + usb_get_dev + usb_hcd_check_unlink_urb + usb_hcd_giveback_urb + usb_hcd_is_primary_hcd + usb_hcd_link_urb_to_ep + usb_hcd_poll_rh_status + usb_hcd_resume_root_hub + usb_hcd_unlink_urb_from_ep + usb_put_dev + usb_put_hcd + usb_remove_hcd + usleep_range + +# required by failover.ko + netdev_master_upper_dev_link + register_netdevice_notifier + rtnl_is_locked + unregister_netdevice_notifier + +# required by gnss-cmdline.ko + bus_find_device_by_name + device_find_child + kstrdup + platform_bus_type + strsep + +# required by gnss-serial.ko + gnss_allocate_device + gnss_deregister_device + gnss_insert_raw + gnss_put_device + gnss_register_device + of_property_read_variable_u32_array + __pm_runtime_disable + pm_runtime_enable + __pm_runtime_idle + __pm_runtime_resume + serdev_device_close + serdev_device_open + serdev_device_set_baudrate + serdev_device_set_flow_control + serdev_device_wait_until_sent + serdev_device_write + serdev_device_write_wakeup + +# required by ion-alloc.ko + contig_page_data + down_read + freezing_slow_path + __init_rwsem + kernel_kobj + kobject_create_and_add + kthread_create_on_node + __ll_sc_atomic64_sub + mod_node_page_state + plist_add + rb_erase + rb_insert_color + __refrigerator + remap_pfn_range + sched_setscheduler + sg_alloc_table + __sg_page_iter_next + __sg_page_iter_start + split_page + sysfs_create_groups + system_freezing_cnt + totalram_pages + vfree + vmalloc + vm_map_ram + vm_unmap_ram + wake_up_process + +# required by nd_virtio.ko + bio_alloc_bioset + bio_chain + fs_bio_set + submit_bio + +# required by net_failover.ko + call_netdevice_notifiers + dev_close + dev_get_stats + dev_mc_sync_multiple + dev_mc_unsync + dev_open + dev_set_mtu + dev_uc_sync_multiple + dev_uc_unsync + __ethtool_get_link_ksettings + netdev_change_features + netdev_increment_features + netdev_lower_state_changed + pci_bus_type + +# required by rtc-test.ko + add_timer + del_timer + devm_rtc_allocate_device + ktime_get_real_seconds + __rtc_register_device + rtc_time64_to_tm + rtc_tm_to_time64 + rtc_update_irq + +# required by snd-ac97-codec.ko + pci_read_config_word + simple_strtoul + snd_component_add + snd_ctl_add + _snd_ctl_add_slave + snd_ctl_boolean_mono_info + snd_ctl_enum_info + snd_ctl_find_id + snd_ctl_make_virtual_master + snd_ctl_new1 + snd_ctl_remove_id + snd_info_free_entry + snd_info_register + snd_interval_refine + snd_pcm_hw_rule_add + snd_pcm_std_chmaps + strcat + +# required by snd-intel8x0.ko + ktime_get + param_ops_bint + pci_iomap + pci_release_regions + pci_request_regions + pci_write_config_dword + snd_card_disconnect + snd_card_free + snd_card_new + snd_card_register + snd_dma_alloc_pages + snd_dma_free_pages + snd_pci_quirk_lookup + snd_pcm_add_chmap_ctls + snd_pcm_hw_constraint_integer + snd_pcm_hw_constraint_list + snd_pcm_hw_constraint_msbits + snd_pcm_lib_free_pages + snd_pcm_lib_ioctl + snd_pcm_lib_malloc_pages + snd_pcm_lib_preallocate_pages_for_all + snd_pcm_limit_hw_rates + snd_pcm_new + snd_pcm_period_elapsed + snd_pcm_set_ops + snd_pcm_suspend_all + +# required by ttm.ko + clear_page + copy_page + dev_driver_string + devres_add + __devres_alloc_node + devres_destroy + devres_free + dma_buf_get + dma_fence_enable_sw_signaling + dma_fence_signal + drm_class_device_register + drm_class_device_unregister + drm_clflush_pages + drm_ht_create + drm_ht_find_item + drm_ht_insert_item + drm_ht_just_insert_please + drm_ht_remove + drm_ht_remove_item + drm_mm_init + drm_mm_insert_node_in_range + drm_mm_print + drm_mm_remove_node + drm_mm_takedown + __drm_printfn_debug + drm_vma_offset_add + drm_vma_offset_lookup_locked + drm_vma_offset_manager_destroy + drm_vma_offset_manager_init + drm_vma_offset_remove + flush_workqueue + __ioremap + __iounmap + kfree_call_rcu + kobject_del + kobject_init_and_add + __ll_sc_atomic64_add_return_relaxed + __ll_sc_atomic_sub + __ll_sc___cmpxchg_case_mb_8 + mark_page_accessed + __memcpy_fromio + __memcpy_toio + __memset_io + nr_swap_pages + refcount_inc_not_zero_checked + reservation_object_add_shared_fence + reservation_object_copy_fences + reservation_object_reserve_shared + reservation_object_test_signaled_rcu + reservation_object_wait_timeout_rcu + reservation_ww_class + send_sig + set_page_dirty + shmem_file_setup + shmem_read_mapping_page_gfp + si_mem_available + si_meminfo + unmap_mapping_range + unregister_shrinker + vmalloc_to_page + vm_get_page_prot + vm_insert_mixed + vm_insert_pfn + ww_mutex_lock + +# required by virt_wifi.ko + cfg80211_connect_done + cfg80211_disconnected + cfg80211_inform_bss_data + cfg80211_put_bss + cfg80211_scan_done + __dev_get_by_index + dev_printk + ether_setup + ktime_get_with_offset + netdev_upper_dev_link + netif_stacked_transfer_operstate + register_netdevice + rtnl_link_register + rtnl_link_unregister + wiphy_free + wiphy_new_nm + wiphy_register + wiphy_unregister + +# required by virtio-gpu.ko + bpf_trace_run2 + dma_fence_context_alloc + dma_fence_init + dma_fence_match_context + dma_fence_signal_locked + drm_add_edid_modes + drm_add_modes_noedid + drm_atomic_helper_check + drm_atomic_helper_cleanup_planes + drm_atomic_helper_commit + drm_atomic_helper_commit_hw_done + drm_atomic_helper_commit_modeset_disables + drm_atomic_helper_commit_modeset_enables + drm_atomic_helper_commit_planes + drm_atomic_helper_connector_destroy_state + drm_atomic_helper_connector_duplicate_state + drm_atomic_helper_connector_reset + drm_atomic_helper_crtc_destroy_state + drm_atomic_helper_crtc_duplicate_state + drm_atomic_helper_crtc_reset + drm_atomic_helper_disable_plane + drm_atomic_helper_page_flip + drm_atomic_helper_plane_destroy_state + drm_atomic_helper_plane_duplicate_state + drm_atomic_helper_plane_reset + drm_atomic_helper_set_config + drm_atomic_helper_shutdown + drm_atomic_helper_update_plane + drm_atomic_helper_wait_for_vblanks + drm_compat_ioctl + drm_connector_attach_edid_property + drm_connector_attach_encoder + drm_connector_cleanup + drm_connector_init + drm_connector_register + drm_connector_unregister + drm_connector_update_edid_property + drm_crtc_cleanup + drm_crtc_init_with_planes + drm_crtc_send_vblank_event + drm_cvt_mode + drm_dbg + drm_dev_alloc + drm_dev_put + drm_dev_register + drm_dev_set_unique + drm_do_get_edid + drm_encoder_cleanup + drm_encoder_init + drm_err + drm_framebuffer_init + drm_gem_fb_create_handle + drm_gem_fb_destroy + drm_gem_handle_create + drm_gem_object_init + drm_gem_object_lookup + drm_gem_object_put_unlocked + drm_gem_object_release + drm_gem_prime_export + drm_gem_prime_fd_to_handle + drm_gem_prime_handle_to_fd + drm_gem_prime_import + drm_gem_prime_mmap + drm_global_item_ref + drm_global_item_unref + drm_helper_hpd_irq_event + drm_helper_mode_fill_fb_struct + drm_helper_probe_single_connector_modes + drm_ioctl + drm_kms_helper_hotplug_event + drm_mode_config_cleanup + drm_mode_config_init + drm_mode_config_reset + drm_mode_probed_add + drm_open + drm_plane_cleanup + drm_poll + drm_prime_pages_to_sg + drm_put_dev + drm_read + drm_release + drm_set_preferred_mode + drm_universal_plane_init + __get_task_comm + kmalloc_order_trace + kmem_cache_alloc + kmem_cache_create + kmem_cache_destroy + kmem_cache_free + memdup_user + put_unused_fd + remove_conflicting_framebuffers + sg_alloc_table_from_pages + sync_file_create + sync_file_get_fence + __tracepoint_dma_fence_emit + +# required by virtio-rng.ko + complete + hwrng_register + hwrng_unregister + wait_for_completion + wait_for_completion_killable + +# required by virtio.ko + add_uevent_var + device_add + device_initialize + driver_register + driver_unregister + ida_destroy + panic + +# required by virtio_blk.ko + __alloc_disk_node + blk_cleanup_queue + blk_execute_rq + blk_get_request + blk_mq_alloc_tag_set + blk_mq_complete_request + blk_mq_end_request + blk_mq_free_tag_set + blk_mq_init_queue + blk_mq_quiesce_queue + blk_mq_start_request + blk_mq_start_stopped_hw_queues + blk_mq_stop_hw_queue + blk_mq_unquiesce_queue + blk_put_request + blk_queue_alignment_offset + blk_queue_io_min + blk_queue_io_opt + blk_queue_logical_block_size + blk_queue_max_hw_sectors + blk_queue_max_segments + blk_queue_max_segment_size + blk_queue_physical_block_size + blk_queue_write_cache + blk_rq_map_kern + blk_rq_map_sg + blk_status_to_errno + del_gendisk + device_add_disk + _dev_notice + kobject_uevent_env + memmove + put_disk + register_blkdev + revalidate_disk + set_disk_ro + string_get_size + __sysfs_match_string + unregister_blkdev + +# required by virtio_crypto.ko + cpu_bit_bitmap + cpu_topology + crypto_ablkcipher_type + crypto_engine_alloc_init + crypto_engine_exit + crypto_engine_start + crypto_finalize_ablkcipher_request + crypto_register_alg + crypto_transfer_ablkcipher_request_to_engine + crypto_unregister_alg + kzfree + __ll_sc_atomic_add_return + scatterwalk_map_and_copy + sg_nents + sg_nents_for_len + try_module_get + +# required by virtio_input.ko + input_alloc_absinfo + input_allocate_device + input_event + input_free_device + input_mt_init_slots + input_register_device + input_set_abs_params + input_unregister_device + +# required by virtio_mmio.ko + device_for_each_child + devm_ioremap + devm_kfree + __devm_request_region + iomem_resource + memparse + platform_device_register_full + platform_get_irq + platform_get_resource + +# required by virtio_net.ko + bpf_prog_add + bpf_prog_put + bpf_prog_sub + bpf_warn_invalid_xdp_action + build_skb + __cpuhp_remove_state + __cpuhp_setup_state + __cpuhp_state_add_instance + __cpuhp_state_remove_instance + cpumask_next_wrap + eth_commit_mac_addr_change + eth_prepare_mac_addr_change + ethtool_op_get_ts_info + eth_type_trans + flow_keys_basic_dissector + kmemdup + __napi_alloc_skb + napi_complete_done + napi_consume_skb + napi_disable + napi_gro_receive + napi_hash_del + __napi_schedule + napi_schedule_prep + netdev_notify_peers + netdev_warn + netif_device_attach + netif_napi_add + netif_napi_del + netif_schedule_queue + netif_set_real_num_rx_queues + netif_set_real_num_tx_queues + __netif_set_xps_queue + net_ratelimit + nf_conntrack_destroy + _raw_spin_trylock + skb_add_rx_frag + skb_coalesce_rx_frag + __skb_flow_dissect + skb_page_frag_refill + skb_partial_csum_set + skb_to_sgvec + skb_tstamp_tx + __sw_hweight64 + __tracepoint_xdp_exception + xdp_do_flush_map + xdp_do_redirect + xdp_return_frame + xdp_return_frame_rx_napi + xdp_rxq_info_reg + xdp_rxq_info_reg_mem_model + xdp_rxq_info_unreg + +# required by virtio_pci.ko + get_device + irq_set_affinity_hint + pci_alloc_irq_vectors_affinity + pci_find_capability + pci_find_ext_capability + pci_find_next_capability + pci_free_irq_vectors + pci_iomap_range + pci_irq_get_affinity + pci_irq_vector + pci_read_config_byte + pci_release_selected_regions + pci_request_selected_regions + +# required by virtio_pmem.ko + nvdimm_bus_register + nvdimm_bus_unregister + nvdimm_pmem_region_create + +# required by virtio_ring.ko + alloc_pages_exact + free_pages_exact + kimage_voffset + +# required by vmw_vsock_virtio_transport_common.ko + bpf_trace_run10 + bpf_trace_run8 + cancel_delayed_work + _copy_from_iter_full + _copy_to_iter + trace_print_symbols_seq + +# required by vsock.ko + autoremove_wake_function + __module_get + nonseekable_open + prandom_u32 + prepare_to_wait + proto_register + proto_unregister + __put_cred + _raw_write_lock_bh + _raw_write_unlock_bh + security_sock_graft + sk_alloc + skb_dequeue + sock_init_data + sock_no_accept + sock_no_getsockopt + sock_no_ioctl + sock_no_listen + sock_no_mmap + sock_no_sendpage + sock_no_setsockopt + sock_no_socketpair + sock_queue_rcv_skb + sock_register + sock_unregister + +# required by vsock_diag.ko + __netlink_dump_start + __nlmsg_put + sock_diag_register + sock_diag_save_cookie + sock_diag_unregister + sock_i_ino diff --git a/abi_gki_aarch64_qcom_whitelist b/abi_gki_aarch64_qcom_whitelist new file mode 100644 index 0000000000000000000000000000000000000000..d90d69ca4d0af2a1e8de0044e1dd5606b9733fae --- /dev/null +++ b/abi_gki_aarch64_qcom_whitelist @@ -0,0 +1,2677 @@ +[abi_whitelist] +# commonly used symbols + add_timer + add_uevent_var + alarm_cancel + alarm_init + alarm_start_relative + alarmtimer_get_rtcdev + alarm_try_to_cancel + alloc_chrdev_region + alloc_netdev_mqs + __alloc_pages_nodemask + __alloc_percpu + __alloc_skb + __alloc_workqueue_key + __arch_copy_from_user + __arch_copy_to_user + arch_setup_dma_ops + arch_timer_read_ool_enabled + arm64_const_caps_ready + atomic_notifier_call_chain + atomic_notifier_chain_register + atomic_notifier_chain_unregister + autoremove_wake_function + bin2hex + __bitmap_clear + bitmap_find_next_zero_area_off + __bitmap_set + blocking_notifier_call_chain + blocking_notifier_chain_register + blocking_notifier_chain_unregister + bpf_trace_run1 + bpf_trace_run2 + bpf_trace_run3 + bpf_trace_run4 + bpf_trace_run5 + bpf_trace_run6 + bpf_trace_run7 + bpf_trace_run8 + bus_find_device + bus_for_each_dev + bus_register + bus_unregister + cancel_delayed_work + cancel_delayed_work_sync + cancel_work_sync + cdev_add + cdev_del + cdev_init + __check_object_size + __class_create + class_destroy + clk_disable + clk_enable + clk_fixed_factor_ops + clk_get + clk_get_rate + clk_hw_get_name + clk_hw_get_num_parents + clk_hw_get_rate + __clk_is_enabled + __clk_mux_determine_rate_closest + clk_prepare + clk_put + clk_round_rate + clk_set_flags + clk_set_parent + clk_set_rate + clk_sync_state + clk_unprepare + clk_unvote_vdd_level + clk_vote_vdd_level + cma_alloc + cma_get_base + cma_get_size + cma_release + compat_alloc_user_space + complete + complete_all + completion_done + component_add + component_bind_all + component_master_add_with_match + component_master_del + component_match_add_release + component_unbind_all + config_ep_by_speed + config_group_init_type_name + __const_udelay + consume_skb + contig_page_data + cpu_bit_bitmap + __cpuhp_setup_state + cpu_hwcap_keys + cpu_hwcaps + __cpu_isolated_mask + cpumask_next + cpu_number + __cpu_online_mask + __cpu_possible_mask + cpu_subsys + crypto_alloc_shash + crypto_destroy_tfm + crypto_shash_setkey + _ctype + debugfs_attr_read + debugfs_attr_write + debugfs_create_atomic_t + debugfs_create_bool + debugfs_create_dir + debugfs_create_file + debugfs_create_file_unsafe + debugfs_create_u16 + debugfs_create_u32 + debugfs_create_u64 + debugfs_create_u8 + debugfs_create_x32 + debugfs_create_x8 + debugfs_print_regs32 + debugfs_remove + debugfs_remove_recursive + delayed_work_timer_fn + del_timer + del_timer_sync + destroy_workqueue + _dev_crit + dev_driver_string + _dev_err + devfreq_add_device + devfreq_add_governor + devfreq_interval_update + devfreq_monitor_resume + devfreq_monitor_start + devfreq_monitor_stop + devfreq_monitor_suspend + devfreq_recommended_opp + devfreq_remove_device + devfreq_remove_governor + devfreq_resume_device + devfreq_suspend_device + dev_get_regmap + device_add + device_create + device_create_file + device_del + device_destroy + device_find_child + device_for_each_child + device_initialize + device_init_wakeup + device_property_present + device_property_read_u32_array + device_register + device_remove_file + device_unregister + _dev_info + __dev_kfree_skb_any + devm_backlight_device_register + devm_clk_get + devm_clk_hw_register + devm_clk_put + devm_clk_register + devm_extcon_dev_allocate + devm_extcon_dev_register + devm_free_irq + devm_gpiod_get + devm_gpiod_get_optional + devm_gpio_request + devm_gpio_request_one + devm_iio_device_alloc + __devm_iio_device_register + devm_ioremap + devm_ioremap_nocache + devm_ioremap_resource + devm_iounmap + devm_kasprintf + devm_kfree + devm_kmalloc + devm_kmemdup + devm_kstrdup + devm_nvmem_register + devm_of_clk_add_hw_provider + devm_of_platform_populate + devm_phy_get + devm_pinctrl_get + devm_pinctrl_put + devm_pinctrl_register + devm_power_supply_register + __devm_regmap_init + __devm_regmap_init_i2c + __devm_regmap_init_mmio_clk + devm_regulator_bulk_get + devm_regulator_get + devm_regulator_get_optional + devm_regulator_put + devm_regulator_register + __devm_request_region + devm_request_threaded_irq + __devm_reset_control_get + devm_reset_controller_register + devm_snd_soc_register_component + devm_thermal_zone_of_sensor_register + devm_usb_get_phy_by_phandle + dev_pm_opp_add + dev_pm_opp_find_freq_ceil + dev_pm_opp_find_freq_floor + dev_pm_opp_get_opp_count + dev_pm_opp_get_voltage + dev_pm_opp_of_add_table + dev_pm_opp_put + dev_set_name + _dev_warn + disable_irq + disable_irq_nosync + dma_alloc_from_dev_coherent + dma_buf_attach + dma_buf_begin_cpu_access + dma_buf_begin_cpu_access_partial + dma_buf_detach + dma_buf_end_cpu_access + dma_buf_end_cpu_access_partial + dma_buf_fd + dma_buf_get + dma_buf_get_flags + dma_buf_kmap + dma_buf_kunmap + dma_buf_map_attachment + dma_buf_put + dma_buf_unmap_attachment + dma_buf_vmap + dma_buf_vunmap + dma_common_get_sgtable + dma_common_mmap + dma_contiguous_default_area + dma_fence_context_alloc + dma_fence_default_wait + dma_fence_init + dma_fence_release + dma_fence_signal + dma_fence_signal_locked + __dma_flush_area + __dma_inv_area + dma_release_from_dev_coherent + dma_request_slave_channel + do_exit + down_read + down_write + drain_workqueue + driver_register + driver_unregister + drm_panel_notifier_register + drm_panel_notifier_unregister + dst_release + dummy_dma_ops + __dynamic_dev_dbg + __dynamic_pr_debug + enable_irq + ether_setup + eth_type_trans + event_triggers_call + extcon_get_edev_by_phandle + extcon_get_state + extcon_register_blocking_notifier + extcon_register_notifier + extcon_set_property + extcon_set_property_capability + extcon_set_state_sync + fd_install + find_get_pid + find_last_bit + find_next_bit + find_next_zero_bit + find_vma + finish_wait + flush_delayed_work + flush_work + flush_workqueue + fput + free_irq + free_netdev + __free_pages + free_pages + free_percpu + freezing_slow_path + fwnode_property_read_u32_array + gcd + generic_file_llseek + generic_handle_irq + geni_abort_m_cmd + geni_cancel_m_cmd + geni_se_dump_dbg_regs + geni_se_init + geni_se_iommu_map_buf + geni_se_iommu_unmap_buf + geni_se_resources_init + geni_se_rx_dma_prep + geni_se_rx_dma_unprep + geni_se_select_mode + geni_setup_m_cmd + geni_se_tx_dma_prep + geni_se_tx_dma_unprep + geni_write_reg + genlmsg_put + gen_pool_add_virt + gen_pool_alloc + gen_pool_create + gen_pool_destroy + gen_pool_free + get_cpu_device + get_device + __get_free_pages + get_pid_task + get_random_bytes + get_se_proto + __get_task_comm + get_tx_fifo_depth + get_unused_fd_flags + gpiochip_add_data_with_key + gpiochip_add_pin_range + gpiochip_generic_free + gpiochip_generic_request + gpiochip_get_data + gpiochip_remove + gpiod_direction_input + gpiod_direction_output + gpiod_direction_output_raw + gpiod_get_raw_value + gpiod_get_raw_value_cansleep + gpiod_get_value + gpiod_set_raw_value + gpiod_set_value + gpiod_set_value_cansleep + gpiod_to_irq + gpio_free + gpio_request + gpio_request_one + gpio_to_desc + handle_edge_irq + handle_level_irq + handle_nested_irq + hex2bin + hex_dump_to_buffer + hrtimer_active + hrtimer_cancel + hrtimer_forward + hrtimer_init + hrtimer_start_range_ns + i2c_del_driver + i2c_register_driver + i2c_transfer + i2c_transfer_buffer_flags + ida_alloc_range + ida_free + idr_alloc + idr_alloc_cyclic + idr_destroy + idr_find + idr_get_next + idr_preload + idr_remove + iio_channel_get + iio_read_channel_processed + init_dummy_netdev + init_net + __init_rwsem + init_timer_key + init_wait_entry + __init_waitqueue_head + input_allocate_device + input_close_device + input_event + input_free_device + input_mt_init_slots + input_mt_report_slot_state + input_open_device + input_register_device + input_register_handle + input_register_handler + input_set_abs_params + input_set_capability + input_set_timestamp + input_unregister_device + input_unregister_handle + input_unregister_handler + int_sqrt + iomem_resource + iommu_attach_device + iommu_detach_device + iommu_domain_alloc + iommu_domain_free + iommu_domain_get_attr + iommu_domain_set_attr + iommu_get_domain_for_dev + iommu_map + iommu_map_sg + iommu_present + iommu_set_fault_handler + iommu_unmap + __ioremap + __iounmap + ipc_log_context_create + ipc_log_context_destroy + ipc_log_string + ip_route_output_flow + __ipv6_addr_type + irq_chip_disable_parent + irq_chip_enable_parent + irq_chip_eoi_parent + irq_chip_mask_parent + irq_chip_set_affinity_parent + irq_chip_set_parent_state + irq_chip_set_type_parent + irq_chip_set_vcpu_affinity_parent + irq_chip_unmask_parent + __irq_domain_add + irq_domain_alloc_irqs_parent + irq_domain_remove + irq_domain_set_hwirq_and_chip + irq_find_mapping + irq_find_matching_fwspec + irq_get_irq_data + irq_modify_status + irq_of_parse_and_map + irq_set_chip_and_handler_name + irq_set_chip_data + irq_set_irq_type + irq_set_irq_wake + irq_to_desc + jiffies + jiffies_to_msecs + jiffies_to_usecs + kasprintf + kernel_connect + kernel_kobj + kernfs_find_and_get_ns + kfree + kfree_skb + kimage_voffset + __kmalloc + kmalloc_caches + kmalloc_order_trace + kmem_cache_alloc + kmem_cache_alloc_trace + kmem_cache_create + kmem_cache_create_usercopy + kmem_cache_destroy + kmem_cache_free + kmemdup + kobject_create_and_add + kobject_del + kobject_init_and_add + kobject_put + kobject_uevent + kobject_uevent_env + krealloc + kset_find_obj + ksize + kstrdup + kstrndup + kstrtobool + kstrtoint + kstrtoll + kstrtos8 + kstrtos8_from_user + kstrtou16 + kstrtou8 + kstrtou8_from_user + kstrtouint + kstrtouint_from_user + kstrtoull + kthread_cancel_work_sync + kthread_create_on_node + kthread_flush_worker + __kthread_init_worker + kthread_queue_work + kthread_should_stop + kthread_stop + kthread_worker_fn + ktime_get + ktime_get_coarse_real_ts64 + ktime_get_real_ts64 + ktime_get_with_offset + kvfree + kvmalloc_node + kzfree + led_classdev_unregister + __list_add_valid + __list_del_entry_valid + list_sort + __local_bh_disable_ip + __local_bh_enable_ip + lock_sock_nested + mbox_client_txdone + mbox_controller_register + mbox_controller_unregister + mbox_free_channel + mbox_request_channel + mbox_send_message + _mcount + media_device_init + __media_device_register + media_device_unregister + media_entity_pads_init + memchr + memcmp + memcpy + __memcpy_fromio + __memcpy_toio + memdup_user + memmove + memremap + memset + __memset_io + memstart_addr + memzero_explicit + __might_fault + __might_sleep + misc_deregister + misc_register + mod_delayed_work_on + mod_node_page_state + mod_timer + module_kset + module_layout + module_put + __msecs_to_jiffies + msleep + msleep_interruptible + __mutex_init + mutex_lock + mutex_lock_interruptible + mutex_trylock + mutex_unlock + napi_complete_done + napi_disable + napi_gro_flush + napi_gro_receive + __napi_schedule + napi_schedule_prep + __netdev_alloc_skb + netif_carrier_off + netif_carrier_on + netif_napi_add + netif_napi_del + netif_receive_skb + netif_rx + netif_rx_ni + netif_tx_wake_queue + netlink_unicast + net_ratelimit + nf_register_net_hooks + nf_unregister_net_hooks + nla_memcpy + nla_put + __nlmsg_put + no_llseek + nonseekable_open + nr_cpu_ids + ns_capable + ns_to_timespec + nvmem_cell_get + nvmem_cell_put + nvmem_cell_read + nvmem_cell_read_u32 + nvmem_device_read + nvmem_device_write + of_address_to_resource + of_alias_get_id + of_clk_add_provider + of_clk_get + of_clk_src_onecell_get + of_count_phandle_with_args + of_device_get_match_data + of_device_is_available + of_device_is_compatible + of_dma_configure + of_drm_find_panel + of_fdt_get_ddrtype + of_find_compatible_node + of_find_device_by_node + of_find_node_by_name + of_find_node_opts_by_path + of_find_node_with_property + of_find_property + of_fwnode_ops + of_genpd_del_provider + of_get_address + of_get_child_by_name + of_get_cpu_node + of_get_named_gpio_flags + of_get_next_available_child + of_get_next_child + of_get_parent + of_get_property + of_get_regulator_init_data + of_graph_get_remote_node + of_iomap + of_irq_get + of_irq_get_byname + of_led_classdev_register + of_machine_is_compatible + of_match_device + of_match_node + of_modalias_node + of_parse_phandle + of_parse_phandle_with_args + of_parse_phandle_with_fixed_args + of_platform_depopulate + of_platform_device_create + of_platform_populate + of_property_count_elems_of_size + of_property_match_string + of_property_read_string + of_property_read_string_helper + of_property_read_u32_index + of_property_read_u64 + of_property_read_variable_u32_array + of_property_read_variable_u8_array + of_prop_next_string + of_thermal_handle_trip + of_thermal_handle_trip_temp + of_translate_address + panic + panic_notifier_list + param_get_int + param_ops_bool + param_ops_charp + param_ops_int + param_ops_long + param_ops_string + param_ops_uint + param_set_bool + param_set_int + pci_bus_type + pci_set_master + __per_cpu_offset + perf_trace_buf_alloc + perf_trace_run_bpf_submit + pfn_valid + phy_exit + phy_init + phy_power_off + phy_power_on + pinconf_generic_dt_node_to_map + pinctrl_dev_get_drvdata + pinctrl_lookup_state + pinctrl_select_state + pinctrl_utils_free_map + platform_bus_type + platform_device_add + platform_device_add_data + platform_device_add_properties + platform_device_add_resources + platform_device_alloc + platform_device_put + platform_device_register_full + platform_device_unregister + __platform_driver_register + platform_driver_unregister + platform_get_irq + platform_get_irq_byname + platform_get_resource + platform_get_resource_byname + platform_irq_count + plist_add + pm_generic_resume + pm_generic_suspend + pm_genpd_init + pm_power_off + pm_qos_add_request + pm_qos_remove_request + pm_qos_request_active + pm_qos_update_request + pm_qos_update_request_timeout + __pm_relax + pm_relax + pm_runtime_allow + __pm_runtime_disable + pm_runtime_enable + pm_runtime_forbid + __pm_runtime_idle + pm_runtime_no_callbacks + __pm_runtime_resume + pm_runtime_set_autosuspend_delay + __pm_runtime_set_status + __pm_runtime_suspend + __pm_runtime_use_autosuspend + __pm_stay_awake + pm_stay_awake + pm_wakeup_dev_event + pm_wakeup_ws_event + power_kobj + power_supply_changed + power_supply_get_by_name + power_supply_get_drvdata + power_supply_get_property + power_supply_put + power_supply_reg_notifier + power_supply_set_property + power_supply_unreg_notifier + preempt_schedule + preempt_schedule_notrace + prepare_to_wait_event + print_hex_dump + printk + proc_dointvec + proc_mkdir_data + pskb_expand_head + __pskb_pull_tail + put_device + __put_page + put_pid + __put_task_struct + put_unused_fd + qcom_smem_state_get + qcom_smem_state_update_bits + qmi_add_lookup + qmi_add_server + qmi_handle_init + qmi_handle_release + qmi_response_type_v01_ei + qmi_send_indication + qmi_send_request + qmi_send_response + qmi_txn_cancel + qmi_txn_init + qmi_txn_wait + queue_delayed_work_on + queue_work_on + ___ratelimit + _raw_read_lock + _raw_read_lock_bh + _raw_read_unlock + _raw_read_unlock_bh + _raw_spin_lock + _raw_spin_lock_bh + _raw_spin_lock_irq + _raw_spin_lock_irqsave + _raw_spin_trylock_bh + _raw_spin_unlock + _raw_spin_unlock_bh + _raw_spin_unlock_irq + _raw_spin_unlock_irqrestore + _raw_write_lock + _raw_write_lock_bh + _raw_write_unlock + _raw_write_unlock_bh + rb_erase + rb_first + rb_insert_color + rb_next + __rcu_read_lock + __rcu_read_unlock + rdev_get_drvdata + refcount_add_checked + refcount_dec_and_test_checked + refcount_dec_checked + refcount_inc_checked + refcount_inc_not_zero_checked + refcount_sub_and_test_checked + __refrigerator + regcache_cache_only + regcache_mark_dirty + regcache_sync + regcache_sync_region + __register_chrdev + register_inet6addr_notifier + register_inetaddr_notifier + register_netdev + register_netdevice + register_netdevice_notifier + register_net_sysctl + register_pernet_subsys + register_pm_notifier + register_shrinker + register_syscore_ops + regmap_bulk_read + regmap_bulk_write + __regmap_init + regmap_multi_reg_write + regmap_raw_read + regmap_read + regmap_update_bits_base + regmap_write + regulator_bulk_disable + regulator_count_voltages + regulator_disable + regulator_enable + regulator_get + regulator_is_enabled + regulator_put + regulator_set_load + regulator_set_mode + regulator_set_voltage + regulator_sync_state + release_firmware + release_sock + remap_pfn_range + remove_proc_entry + request_firmware + request_firmware_into_buf + request_firmware_nowait + request_threaded_irq + reset_control_assert + reset_control_deassert + rtc_time64_to_tm + rtc_tm_to_time64 + rtc_valid_tm + rt_mutex_lock + rt_mutex_unlock + rtnl_is_locked + rtnl_unlock + saved_command_line + sched_clock + sched_setscheduler + schedule + schedule_timeout + scnprintf + se_config_packing + se_geni_clks_off + se_geni_resources_off + se_geni_resources_on + seq_hex_dump + seq_lseek + seq_open + seq_printf + seq_putc + seq_puts + seq_read + seq_release + seq_write + set_normalized_timespec + set_user_nice + sg_alloc_table + sg_free_table + sg_init_one + sg_init_table + sg_next + __sg_page_iter_next + __sg_page_iter_start + simple_attr_open + simple_attr_read + simple_attr_release + simple_attr_write + simple_open + simple_read_from_buffer + simple_write_to_buffer + single_open + single_release + skb_add_rx_frag + skb_clone + skb_copy + skb_copy_bits + skb_copy_expand + skb_dequeue + skb_pull + skb_push + skb_put + skb_queue_tail + skb_trim + snd_ctl_boolean_mono_info + snd_hwdep_new + snd_info_create_card_entry + snd_info_create_subdir + snd_info_free_entry + snd_info_register + snd_pcm_format_width + snd_pcm_hw_constraint_list + snd_pcm_period_elapsed + snd_soc_add_component_controls + snd_soc_bytes_tlv_callback + snd_soc_component_init_regmap + snd_soc_component_read32 + snd_soc_component_update_bits + snd_soc_component_write + snd_soc_dapm_add_routes + snd_soc_dapm_get_enum_double + snd_soc_dapm_get_volsw + snd_soc_dapm_ignore_suspend + snd_soc_dapm_kcontrol_widget + snd_soc_dapm_mixer_update_power + snd_soc_dapm_mux_update_power + snd_soc_dapm_new_controls + snd_soc_dapm_new_widgets + snd_soc_dapm_put_enum_double + snd_soc_dapm_put_volsw + snd_soc_dapm_sync + snd_soc_get_volsw + snd_soc_get_volsw_sx + snd_soc_info_enum_double + snd_soc_info_multi_ext + snd_soc_info_volsw + snd_soc_info_volsw_sx + snd_soc_put_volsw + snd_soc_put_volsw_sx + snd_soc_register_component + snd_soc_rtdcom_lookup + snd_soc_unregister_component + snprintf + sock_create + sock_release + sort + __spi_register_driver + spi_setup + spi_sync + sprintf + sprint_symbol + srcu_init_notifier_head + srcu_notifier_call_chain + srcu_notifier_chain_register + srcu_notifier_chain_unregister + sscanf + __stack_chk_fail + __stack_chk_guard + strcasecmp + strchr + strcmp + strcpy + strim + strlcat + strlcpy + strlen + strncasecmp + strnchr + strncmp + strncpy + strnlen + strnstr + strpbrk + strrchr + strsep + __sw_hweight32 + __sw_hweight64 + __sw_hweight8 + __symbol_get + __symbol_put + sync_file_create + sync_file_get_fence + synchronize_irq + syscon_node_to_regmap + sysfs_create_file_ns + sysfs_create_files + sysfs_create_group + sysfs_create_groups + sysfs_create_link + sysfs_notify + sysfs_remove_file_ns + sysfs_remove_group + sysfs_remove_groups + sysfs_streq + system_freezing_cnt + system_highpri_wq + system_long_wq + system_unbound_wq + system_wq + sys_tz + __tasklet_hi_schedule + tasklet_init + tasklet_kill + __tasklet_schedule + tbn_cleanup + tbn_init + tbn_release_bus + tbn_request_bus + thermal_cdev_update + thermal_cooling_device_unregister + thermal_of_cooling_device_register + thermal_zone_device_update + thermal_zone_get_temp + thermal_zone_get_zone_by_name + thermal_zone_of_sensor_register + thermal_zone_of_sensor_unregister + timer_unstable_counter_workaround + totalram_pages + __trace_bprintk + trace_define_field + trace_event_buffer_commit + trace_event_buffer_reserve + trace_event_ignore_this_pid + trace_event_raw_init + trace_event_reg + trace_handle_return + trace_hardirqs_off + trace_hardirqs_on + trace_print_flags_seq + trace_raw_output_prep + trace_seq_printf + try_module_get + typec_register_partner + typec_register_port + typec_set_data_role + typec_set_pwr_role + typec_unregister_partner + __udelay + uncached_logk + __unregister_chrdev + unregister_chrdev_region + unregister_inet6addr_notifier + unregister_inetaddr_notifier + unregister_netdev + unregister_netdevice_notifier + unregister_netdevice_queue + unregister_net_sysctl_table + unregister_pernet_subsys + unregister_pm_notifier + update_devfreq + up_read + up_write + usb_add_phy_dev + usb_assign_descriptors + usb_ep_alloc_request + usb_ep_autoconfig + usb_ep_disable + usb_ep_enable + usb_ep_free_request + usb_ep_queue + usb_free_all_descriptors + usb_function_register + usb_function_unregister + usb_gadget_wakeup + usb_get_dr_mode + usb_interface_id + usb_put_function_instance + usb_remove_phy + usb_string_id + __usecs_to_jiffies + usleep_range + v4l2_device_register + v4l2_device_unregister + v4l2_event_pending + v4l2_event_queue + v4l2_event_subscribe + v4l2_event_unsubscribe + v4l2_fh_open + v4l2_fh_release + vb2_buffer_done + vb2_queue_init + vfree + video_devdata + video_device_alloc + video_device_release + video_ioctl2 + __video_register_device + video_unregister_device + vmalloc + vmalloc_to_page + vmap + vm_mmap + vm_munmap + vscnprintf + vsnprintf + vunmap + vzalloc + wait_for_completion + wait_for_completion_interruptible + wait_for_completion_interruptible_timeout + wait_for_completion_timeout + __wake_up + wake_up_process + wakeup_source_add + wakeup_source_drop + wakeup_source_prepare + wakeup_source_register + wakeup_source_remove + wakeup_source_unregister + __warn_printk + work_busy + +# required by adc_tm.ko + iio_channel_get_all + +# required by adsprpc.ko + try_wait_for_completion + vfs_statx + +# required by apr_dlkm.ko + strstr + +# required by arm-memlat-mon.ko + perf_event_create_kernel_counter + perf_event_enable + perf_event_read_value + perf_event_release_kernel + +# required by arm-smmu.ko + alloc_io_pgtable_ops + alloc_pages_exact + amba_bustype + bus_set_iommu + device_link_add + device_link_del + _dev_notice + driver_find_device + driver_for_each_device + fast_smmu_put_dma_cookie + free_io_pgtable_ops + free_pages_exact + generic_device_group + iommu_alloc_resv_region + iommu_debugfs_top + iommu_device_register + iommu_dma_get_resv_regions + iommu_fwspec_add_ids + iommu_fwspec_free + iommu_fwspec_init + iommu_group_get + iommu_group_get_for_dev + iommu_group_get_iommudata + iommu_group_put + iommu_group_ref_get + iommu_group_remove_device + iommu_group_set_iommudata + iommu_put_dma_cookie + kstrtoull_from_user + of_dma_is_coherent + of_phandle_iterator_args + of_phandle_iterator_init + of_phandle_iterator_next + pci_device_group + pci_for_each_dma_alias + pci_request_acs + regulator_disable_deferred + report_iommu_fault + __tracepoint_smmu_init + __tracepoint_tlbi_end + __tracepoint_tlbi_start + __tracepoint_tlbsync_timeout + +# required by at24.ko + i2c_match_id + i2c_new_dummy + i2c_unregister_device + +# required by bluetooth_power.ko + rfkill_alloc + rfkill_destroy + rfkill_init_sw_state + rfkill_register + rfkill_unregister + +# required by br_netfilter.ko + br_dev_queue_push_xmit + br_forward_finish + br_handle_frame_finish + ip_do_fragment + ip_route_input_noref + neigh_destroy + nf_br_ops + nf_hook_slow + nf_ipv6_ops + pskb_trim_rcsum_slow + skb_pull_rcsum + +# required by cam-sync.ko + media_device_cleanup + platform_device_register + +# required by cam_cci.ko + down + up + video_usercopy + +# required by cam_ife_csid.ko + ns_to_timespec64 + +# required by cam_req_mgr.ko + v4l2_device_register_subdev + v4l2_device_register_subdev_nodes + v4l2_device_unregister_subdev + v4l2_subdev_fops + v4l2_subdev_init + +# required by cam_res_mgr.ko + gpiod_set_raw_value_cansleep + led_trigger_event + led_trigger_register_simple + led_trigger_unregister_simple + +# required by cam_utils.ko + gpio_free_array + of_clk_get_from_provider + __release_region + __request_region + +# required by citadel-spi.ko + spi_bus_lock + spi_bus_unlock + spi_sync_locked + +# required by clk-qcom.ko + clk_aggregate_rate + clk_debug_print_hw + __clk_determine_rate + clk_fixed_rate_ops + clk_hw_get_flags + clk_hw_get_parent + clk_hw_get_parent_by_index + clk_hw_is_enabled + clk_hw_is_prepared + clk_hw_round_rate + clk_unvote_rate_vdd + clk_vote_rate_vdd + devm_add_action + divider_get_val + divider_recalc_rate + divider_ro_round_rate_parent + divider_round_rate_parent + of_genpd_add_provider_onecell + pm_genpd_add_subdomain + pm_genpd_remove_subdomain + rational_best_approximation + regulator_get_drvdata + syscon_regmap_lookup_by_phandle + +# required by clk-spmi-pmic-div.ko + __ndelay + of_clk_get_parent_name + +# required by cmd-db.ko + of_reserved_mem_lookup + +# required by cpu_isolate.ko + device_online + sched_isolate_cpu + sched_unisolate_cpu + sched_unisolate_cpu_unlocked + +# required by ddr_stats.ko + arch_timer_get_rate + +# required by devfreq_devbw.ko + dev_pm_opp_set_supported_hw + +# required by devfreq_qcom_fw.ko + devm_devfreq_add_device + dev_pm_opp_remove_table + +# required by diagchar.ko + cdev_alloc + crc_ccitt + crc_ccitt_table + kernel_getsockname + kernel_recvmsg + kernel_restart + kernel_sendmsg + kernel_setsockopt + mempool_alloc + mempool_create + mempool_destroy + mempool_free + mempool_kfree + mempool_kmalloc + send_sig_info + time64_to_tm + +# required by dm-default-key.ko + bio_crypt_alloc_ctx + blk_crypto_evict_key + blk_crypto_init_key + dm_get_device + dm_put_device + dm_read_arg_group + dm_register_target + dm_shift_arg + dm_table_get_mode + dm_unregister_target + +# required by dwc3-haps.ko + pcim_enable_device + __pci_register_driver + pci_unregister_driver + +# required by dwc3-of-simple.ko + of_reset_control_array_get + reset_control_put + reset_control_reset + +# required by dwc3-qcom.ko + devm_extcon_register_notifier + devm_reset_control_array_get + +# required by dwc3.ko + clk_bulk_disable + clk_bulk_enable + clk_bulk_get + clk_bulk_prepare + clk_bulk_put + clk_bulk_unprepare + device_property_read_string + device_property_read_u8_array + devm_usb_get_phy + extcon_find_edev_by_node + extcon_unregister_notifier + of_usb_get_phy_mode + phy_create_lookup + phy_pm_runtime_get_sync + phy_pm_runtime_put_sync + phy_remove_lookup + phy_set_mode + pinctrl_pm_select_default_state + pinctrl_pm_select_sleep_state + strcat + system_freezable_wq + usb_add_gadget_udc + usb_del_gadget_udc + usb_ep_set_maxpacket_limit + usb_gadget_giveback_request + usb_gadget_map_request_by_dev + usb_gadget_set_state + usb_gadget_udc_reset + usb_gadget_unmap_request_by_dev + usb_gadget_vbus_draw + usb_get_maximum_speed + +# required by early_random.ko + add_hwgenerator_randomness + +# required by ebtable_broute.ko + br_should_route_hook + synchronize_net + +# required by ebtables.ko + audit_enabled + audit_log + nf_register_sockopt + nf_unregister_sockopt + __request_module + strscpy + __vmalloc + xt_check_match + xt_check_target + xt_compat_add_offset + xt_compat_calc_jump + xt_compat_flush_offsets + xt_compat_init_offsets + xt_compat_lock + xt_compat_match_offset + xt_compat_target_offset + xt_compat_unlock + xt_data_to_user + xt_find_match + xt_register_target + xt_request_find_match + xt_request_find_target + xt_unregister_target + +# required by eud.ko + tty_flip_buffer_push + uart_add_one_port + uart_insert_char + uart_register_driver + uart_remove_one_port + uart_unregister_driver + +# required by fsa4480-i2c.ko + of_find_i2c_device_by_node + +# required by ftm5.ko + proc_create + +# required by google-battery.ko + simple_strtoull + +# required by google-bms.ko + full_name_hash + +# required by governor_bw_hwmon.ko + argv_free + argv_split + __tracepoint_bw_hwmon_meas + __tracepoint_bw_hwmon_update + +# required by governor_memlat.ko + __tracepoint_memlat_dev_meas + __tracepoint_memlat_dev_update + +# required by gpi.ko + dma_async_device_register + dma_async_tx_descriptor_init + dma_get_slave_channel + of_dma_controller_register + _raw_read_lock_irqsave + _raw_read_unlock_irqrestore + _raw_write_lock_irq + _raw_write_unlock_irq + +# required by heatmap.ko + vb2_fop_mmap + vb2_fop_poll + vb2_fop_read + vb2_fop_release + vb2_ioctl_create_bufs + vb2_ioctl_dqbuf + vb2_ioctl_expbuf + vb2_ioctl_qbuf + vb2_ioctl_querybuf + vb2_ioctl_reqbufs + vb2_ioctl_streamoff + vb2_ioctl_streamon + vb2_ops_wait_finish + vb2_ops_wait_prepare + vb2_plane_vaddr + vb2_vmalloc_memops + video_device_release_empty + +# required by i2c-qcom-geni.ko + i2c_add_adapter + i2c_del_adapter + i2c_get_dma_safe_msg_buf + i2c_put_dma_safe_msg_buf + +# required by ion-alloc.ko + dentry_path + dma_buf_export + dma_get_device_base + dma_get_size + __next_zones_zonelist + sched_setattr + split_page + vm_map_ram + vm_unmap_ram + zone_watermark_ok_safe + +# required by ipa3.ko + add_wait_queue + alloc_etherdev_mqs + eth_mac_addr + pci_clear_master + pci_disable_device + pci_enable_device + pci_release_region + pci_request_region + remove_wait_queue + vm_iomap_memory + wait_woken + woken_wake_function + +# required by lcd.ko + devres_add + devres_alloc_node + devres_free + devres_release + fb_register_client + fb_unregister_client + +# required by lkdtm.ko + access_process_vm + __flush_icache_range + force_sig + kgdb_connected + kick_all_cpus_sync + refcount_add_not_zero_checked + register_kprobe + unregister_kprobe + +# required by lpm-stats.ko + kobject_get + module_ktype + on_each_cpu + +# required by machine_dlkm.ko + devm_snd_soc_register_card + snd_soc_card_change_online_state + snd_soc_component_set_sysclk + snd_soc_dai_get_channel_map + snd_soc_dai_set_channel_map + snd_soc_dai_set_fmt + snd_soc_dai_set_pll + snd_soc_dai_set_sysclk + snd_soc_dai_set_tdm_slot + snd_soc_of_get_dai_link_codecs + snd_soc_of_parse_audio_routing + snd_soc_of_parse_card_name + snd_soc_pm_ops + snd_soc_unregister_card + soc_find_component + +# required by mbhc_dlkm.ko + snd_jack_set_key + snd_soc_card_jack_new + snd_soc_jack_report + +# required by mem-offline.ko + add_memory + bootloader_memory_limit + create_pgtable_mapping + init_mm + max_pfn + memblock_end_of_DRAM + memory_block_size_bytes + register_memory_notifier + set_memory_valid + +# required by minidump_log.ko + __bss_stop + log_buf_addr_get + log_buf_len_get + __per_cpu_end + per_cpu_ptr_to_phys + __per_cpu_start + _sdata + vendor_panic_cb + +# required by msm-poweroff.ko + arm_pm_restart + flush_cache_all + kimage_vaddr + register_die_notifier + +# required by msm-vidc.ko + v4l2_ctrl_find + v4l2_ctrl_get_name + v4l2_ctrl_handler_free + v4l2_ctrl_handler_init_class + __v4l2_ctrl_modify_range + v4l2_ctrl_new_custom + v4l2_ctrl_new_std + v4l2_ctrl_new_std_menu + __v4l2_ctrl_s_ctrl + v4l2_event_dequeue + v4l2_event_queue_fh + v4l2_fh_add + v4l2_fh_del + v4l2_fh_exit + v4l2_fh_init + v4l2_s_ctrl + vb2_dqbuf + vb2_qbuf + vb2_queue_release + vb2_reqbufs + vb2_streamoff + vb2_streamon + +# required by msm_adreno.ko + bpf_trace_run10 + _cleanup_srcu_struct + __clk_get_name + debugfs_lookup + devfreq_cooling_unregister + device_show_int + device_store_int + dev_pm_opp_disable + dev_pm_opp_find_freq_exact + dev_pm_opp_register_notifier + dev_pm_opp_unregister_notifier + __dma_clean_area + dma_fence_add_callback + dma_fence_array_ops + dma_fence_remove_callback + getboottime64 + get_random_u32 + get_task_mm + get_unmapped_area + get_user_pages + idr_for_each + idr_replace + init_uts_ns + iterate_fd + kern_addr_valid + kernfs_create_link + ktime_get_real_seconds + mmap_min_addr + mmput + noop_llseek + of_devfreq_cooling_register + plist_del + rb_last + rb_prev + security_mmap_addr + set_page_dirty_lock + sg_alloc_table_from_pages + sysfs_create_bin_file + sysfs_remove_bin_file + sysfs_remove_files + trace_print_symbols_seq + unmapped_area_topdown + unregister_shrinker + vm_insert_page + vm_insert_pfn + +# required by msm_bus_rpmh.ko + __msm_bus_scale_client_update_request_cb + __msm_bus_scale_register_cb + __msm_bus_scale_register_client_cb + __msm_bus_scale_update_bw_cb + of_clk_get_by_name + raw_notifier_call_chain + raw_notifier_chain_register + raw_notifier_chain_unregister + +# required by msm_drm.ko + adjust_managed_page_count + bpf_trace_run11 + bpf_trace_run12 + __clk_get_hw + clk_get_parent + debugfs_create_size_t + device_create_with_groups + devm_clk_bulk_get + devm_of_pwm_get + devm_pwm_put + dma_fence_wait_timeout + dma_release_declared_memory + do_wait_intr + drm_add_edid_modes + drm_add_modes_noedid + drm_atomic_commit + drm_atomic_get_connector_state + drm_atomic_get_crtc_state + drm_atomic_get_plane_state + drm_atomic_helper_check + drm_atomic_helper_cleanup_planes + drm_atomic_helper_commit_duplicated_state + drm_atomic_helper_commit_hw_done + drm_atomic_helper_commit_modeset_disables + drm_atomic_helper_commit_modeset_enables + drm_atomic_helper_commit_planes + __drm_atomic_helper_connector_destroy_state + __drm_atomic_helper_connector_duplicate_state + __drm_atomic_helper_connector_reset + __drm_atomic_helper_crtc_destroy_state + __drm_atomic_helper_crtc_duplicate_state + __drm_atomic_helper_disable_plane + drm_atomic_helper_disable_plane + drm_atomic_helper_duplicate_state + drm_atomic_helper_page_flip + __drm_atomic_helper_plane_duplicate_state + drm_atomic_helper_prepare_planes + __drm_atomic_helper_set_config + drm_atomic_helper_set_config + drm_atomic_helper_swap_state + drm_atomic_helper_update_legacy_modeset_state + drm_atomic_helper_update_plane + drm_atomic_helper_wait_for_fences + drm_atomic_helper_wait_for_vblanks + drm_atomic_set_crtc_for_connector + drm_atomic_set_fence_for_plane + drm_atomic_set_mode_for_crtc + drm_atomic_state_alloc + drm_atomic_state_clear + drm_atomic_state_default_clear + drm_atomic_state_default_release + __drm_atomic_state_free + drm_atomic_state_init + drm_bridge_attach + drm_bridge_disable + drm_bridge_enable + drm_bridge_mode_set + drm_bridge_post_disable + drm_bridge_pre_enable + drm_compat_ioctl + drm_connector_attach_encoder + drm_connector_cleanup + drm_connector_init + drm_connector_list_iter_begin + drm_connector_list_iter_end + drm_connector_list_iter_next + drm_connector_register + drm_connector_unregister + drm_connector_update_edid_property + drm_crtc_cleanup + drm_crtc_handle_vblank + drm_crtc_init_with_planes + drm_crtc_send_vblank_event + drm_crtc_vblank_off + drm_crtc_vblank_on + drm_crtc_vblank_reset + drm_crtc_wait_one_vblank + drm_dbg + drm_debug + drm_detect_hdmi_monitor + drm_detect_monitor_audio + drm_dev_alloc + drm_dev_dbg + drm_dev_printk + drm_dev_put + drm_dev_register + drm_dev_unregister + drm_dp_atomic_find_vcpi_slots + drm_dp_atomic_release_vcpi_slots + drm_dp_aux_register + drm_dp_aux_unregister + drm_dp_bw_code_to_link_rate + drm_dp_calc_pbn_mode + drm_dp_channel_eq_ok + drm_dp_check_act_status + drm_dp_clock_recovery_ok + drm_dp_dpcd_read + drm_dp_dpcd_read_link_status + drm_dp_dpcd_write + drm_dp_find_vcpi_slots + drm_dp_get_adjust_request_pre_emphasis + drm_dp_get_adjust_request_voltage + drm_dp_link_configure + drm_dp_link_power_down + drm_dp_link_power_up + drm_dp_link_probe + drm_dp_link_rate_to_bw_code + drm_dp_link_train_channel_eq_delay + drm_dp_link_train_clock_recovery_delay + drm_dp_mst_allocate_vcpi + drm_dp_mst_deallocate_vcpi + drm_dp_mst_detect_port + drm_dp_mst_get_edid + drm_dp_mst_hpd_irq + drm_dp_mst_reset_vcpi_slots + drm_dp_mst_topology_mgr_destroy + drm_dp_mst_topology_mgr_init + drm_dp_mst_topology_mgr_set_mst + drm_dp_send_power_updown_phy + drm_dp_update_payload_part1 + drm_dp_update_payload_part2 + drm_edid_duplicate + drm_encoder_cleanup + drm_encoder_init + drm_err + drm_event_reserve_init_locked + drm_format_horz_chroma_subsampling + drm_format_info + drm_format_num_planes + drm_format_plane_cpp + drm_format_vert_chroma_subsampling + drm_framebuffer_init + drm_framebuffer_lookup + drm_framebuffer_remove + drm_framebuffer_unregister_private + drm_gem_create_mmap_offset + drm_gem_fb_create_handle + drm_gem_fb_destroy + drm_gem_fb_get_obj + drm_gem_free_mmap_offset + drm_gem_get_pages + drm_gem_handle_create + drm_gem_mmap + drm_gem_mmap_obj + drm_gem_object_init + drm_gem_object_lookup + drm_gem_object_put + drm_gem_object_put_unlocked + drm_gem_object_release + drm_gem_prime_export + drm_gem_prime_fd_to_handle + drm_gem_prime_handle_to_fd + drm_gem_private_object_init + drm_gem_put_pages + drm_gem_vm_close + drm_gem_vm_open + drm_get_connector_status_name + drm_get_edid + drm_helper_hpd_irq_event + drm_helper_mode_fill_fb_struct + drm_helper_probe_single_connector_modes + drm_ioctl + drm_irq_install + drm_irq_uninstall + drm_kms_helper_poll_disable + drm_kms_helper_poll_enable + drm_kms_helper_poll_fini + drm_kms_helper_poll_init + drm_match_cea_mode + drm_mm_init + drm_mm_insert_node_in_range + drm_mm_remove_node + drm_mm_takedown + drm_mode_config_cleanup + drm_mode_config_init + drm_mode_config_reset + drm_mode_convert_umode + drm_mode_copy + drm_mode_create + drm_mode_create_colorspace_property + drm_mode_debug_printmodeline + drm_mode_duplicate + drm_mode_equal + drm_mode_object_find + drm_mode_object_get + drm_mode_object_put + drm_mode_parse_command_line_for_connector + drm_mode_probed_add + drm_modeset_acquire_fini + drm_modeset_acquire_init + drm_modeset_backoff + drm_mode_set_crtcinfo + drm_modeset_drop_locks + drm_modeset_lock + drm_modeset_lock_all + drm_modeset_lock_all_ctx + drm_mode_set_name + drm_modeset_unlock_all + drm_mode_vrefresh + drm_object_attach_property + drm_object_property_set_value + drm_of_component_match_add + drm_open + drm_panel_add + drm_panel_init + drm_panel_notifier_call_chain + drm_panel_remove + drm_plane_cleanup + drm_plane_create_rotation_property + drm_plane_helper_disable + drm_poll + drm_prime_gem_destroy + drm_prime_pages_to_sg + drm_property_blob_get + drm_property_blob_put + drm_property_create + drm_property_create_bitmask + drm_property_create_blob + drm_property_create_enum + drm_property_create_range + drm_property_lookup_blob + drm_read + drm_release + drm_rotation_simplify + drm_send_event_locked + drm_set_preferred_mode + drm_universal_plane_init + drm_vblank_init + drm_wait_one_vblank + get_vm_area + invalidate_mapping_pages + ioremap_page_range + irq_domain_xlate_onecell + irq_set_affinity_notifier + kernfs_notify + kernfs_put + kthread_cancel_delayed_work_sync + kthread_delayed_work_timer_fn + kthread_flush_work + kthread_mod_delayed_work + kthread_park + kthread_parkme + kthread_queue_delayed_work + kthread_should_park + kthread_unpark + memblock_free + memparse + mipi_dsi_create_packet + mipi_dsi_dcs_read + mipi_dsi_dcs_set_display_brightness + mipi_dsi_dcs_write_buffer + mipi_dsi_host_register + mipi_dsi_host_unregister + nsecs_to_jiffies + of_drm_find_bridge + of_genpd_add_provider_simple + of_graph_get_next_endpoint + of_graph_get_remote_port_parent + of_graph_parse_endpoint + pm_genpd_remove + regulator_get_mode + regulator_get_optional + reservation_object_wait_timeout_rcu + reservation_ww_class + shmem_truncate_range + strreplace + timespec64_to_jiffies + tracing_off + unmap_kernel_range + unmap_mapping_range + vm_get_page_prot + vm_insert_mixed + __wake_up_locked + +# required by msm_ext_display.ko + devm_extcon_dev_unregister + +# required by msm_gsi.ko + kstrtoint_from_user + +# required by msm_icnss.ko + dmam_alloc_coherent + +# required by msm_lmh_dcvs.ko + cpufreq_cpu_get + cpufreq_platform_cooling_register + __tracepoint_clock_set_rate + +# required by msm_minidump.ko + linux_banner + +# required by msm_pm.ko + arm_cpuidle_suspend + clock_debug_print_enabled + cpu_do_idle + cpuidle_dev + cpuidle_register_device + cpuidle_register_driver + cpuidle_register_governor + cpuidle_unregister_driver + cpu_pm_enter + cpu_pm_exit + cpus_read_lock + cpus_read_unlock + get_next_event_cpu + get_next_event_time + __hrtimer_get_remaining + hrtimer_try_to_cancel + param_get_bool + param_get_uint + pending_ipi + pm_gpio_debug_mask + pm_qos_request_for_cpu + pm_qos_request_for_cpumask + regulator_debug_print_enabled + s2idle_set_ops + set_update_ipi_history_callback + start_critical_timings + stop_critical_timings + suspend_set_ops + suspend_valid_only_mem + tick_broadcast_oneshot_control + tick_nohz_get_sleep_length + +# required by msm_qmp.ko + mbox_chan_received_data + mbox_chan_txdone + +# required by msm_rng.ko + crypto_register_rngs + crypto_unregister_rngs + hwrng_register + hwrng_unregister + +# required by msm_rtb.ko + arch_timer_read_counter + set_uncached_logk_func + +# required by msm_scm.ko + __arm_smccc_smc + +# required by msm_sharedmem.ko + __uio_register_device + uio_unregister_device + +# required by pac193x.ko + try_to_del_timer_sync + +# required by peripheral-loader.ko + __iowrite32_copy + memblock_overlaps_memory + of_prop_next_u32 + +# required by phy-generic.ko + regulator_set_current_limit + +# required by phy-qcom-ufs.ko + __devm_of_phy_provider_register + devm_phy_create + of_phy_simple_xlate + +# required by pinctrl-msm.ko + device_property_read_u16_array + gpiochip_line_is_valid + gpiochip_set_chained_irqchip + handle_bad_irq + irq_chip_set_wake_parent + irq_create_fwspec_mapping + irq_domain_free_irqs_top + msm_gpio_dump_builtin_cb + of_irq_domain_map + register_restart_handler + unregister_restart_handler + unregister_syscore_ops + +# required by pinctrl-spmi-gpio.ko + pmic_gpio_dump_builtin_cb + +# required by platform_dlkm.ko + of_property_read_variable_u16_array + snd_ctl_add + snd_ctl_enum_info + snd_ctl_new1 + snd_ctl_notify + snd_ctl_remove + snd_pcm_add_chmap_ctls + snd_pcm_add_usr_ctls + snd_pcm_add_volume_ctls + snd_pcm_hw_constraint_integer + snd_pcm_hw_constraint_minmax + snd_pcm_hw_constraint_step + snd_pcm_lib_ioctl + snd_pcm_std_chmaps + snd_pcm_stop + snd_soc_card_get_kcontrol + snd_soc_new_compress + snd_timer_interrupt + +# required by pwm-qti-lpg.ko + devm_nvmem_device_get + pwmchip_add + pwmchip_remove + +# required by q6_dlkm.ko + kset_create_and_add + kset_unregister + ktime_get_raw_ts64 + ktime_get_ts64 + register_chrdev_region + +# required by qcom-cpufreq-hw.ko + arch_set_freq_scale + cpufreq_cpu_get_raw + cpufreq_freq_attr_scaling_available_freqs + cpufreq_freq_attr_scaling_boost_freqs + cpufreq_generic_frequency_table_verify + cpufreq_register_driver + em_register_perf_domain + of_cpufreq_cooling_register + of_dev_pm_opp_get_cpu_power + +# required by qcom-pdc.ko + irq_chip_get_parent_state + irq_chip_retrigger_hierarchy + irq_domain_create_hierarchy + irq_domain_free_irqs_common + irq_domain_update_bus_token + of_irq_find_parent + +# required by qcom-spmi-pmic.ko + __spmi_driver_register + +# required by qcom-spmi-sdam.ko + nvmem_register + nvmem_unregister + +# required by qcom-spmi-temp-alarm.ko + devm_iio_channel_get + +# required by qcom-spmi-wled.ko + devm_of_led_classdev_register + +# required by qcom_edac.ko + edac_device_add_device + edac_device_alloc_ctl_info + edac_device_alloc_index + edac_device_del_device + edac_device_free_ctl_info + edac_device_handle_ce + edac_device_handle_ue + +# required by qcom_glink_native.ko + __irq_set_affinity + sched_setaffinity + +# required by qcom_hwspinlock.ko + devm_regmap_field_alloc + hwspin_lock_register + hwspin_lock_unregister + regmap_field_read + regmap_field_update_bits_base + +# required by qcom_llcc_pmu.ko + perf_pmu_register + +# required by qcom_rpmh.ko + irq_get_irqchip_state + +# required by qcom_socinfo.ko + arch_read_hardware_id + soc_device_register + soc_device_to_device + +# required by qcrypto.ko + crypto_ablkcipher_type + crypto_ahash_digest + crypto_ahash_setkey + crypto_ahash_type + crypto_alloc_ahash + crypto_alloc_sync_skcipher + crypto_dequeue_request + crypto_enqueue_request + crypto_init_queue + crypto_register_aead + crypto_register_ahash + crypto_register_alg + crypto_unregister_aead + crypto_unregister_ahash + crypto_unregister_alg + des_ekey + llist_add_batch + scatterwalk_ffwd + scatterwalk_map_and_copy + sg_copy_from_buffer + sg_copy_to_buffer + +# required by qpnp-battery.ko + __class_register + +# required by qpnp-power-on.ko + boot_reason + cold_boot + _dev_emerg + devm_input_allocate_device + +# required by qpnp-qgauge.ko + rtc_class_close + rtc_class_open + rtc_read_time + +# required by qpnp-smb5-charger.ko + iio_channel_release + of_find_node_by_phandle + +# required by qpnp_pdphy.ko + device_get_named_child_node + hrtimer_init_sleeper + +# required by qrtr-smd.ko + qrtr_endpoint_post + qrtr_endpoint_register + qrtr_endpoint_unregister + +# required by qseecom.ko + __arch_copy_in_user + firmware_request_nowarn + get_option + sigprocmask + +# required by qtee_shm_bridge.ko + do_tlb_conf_fault_cb + __flush_dcache_area + gen_pool_best_fit + gen_pool_set_algo + gen_pool_virt_to_phys + +# required by regmap-spmi.ko + spmi_ext_register_read + spmi_ext_register_readl + spmi_ext_register_write + spmi_ext_register_writel + spmi_register_read + spmi_register_write + spmi_register_zero_write + +# required by rmnet.ko + build_skb + csum_ipv6_magic + csum_partial + csum_tcpudp_nofold + __dev_get_by_index + dev_queue_xmit + __dynamic_netdev_dbg + genl_register_family + genl_unregister_family + get_current_napi_context + gro_cells_destroy + gro_cells_init + gro_cells_receive + ip_compute_csum + ipv6_ext_hdr + ipv6_skip_exthdr + ___might_sleep + netdev_rx_handler_register + netdev_rx_handler_unregister + netif_schedule_queue + ___pskb_trim + qdisc_reset + rtnl_link_register + rtnl_link_unregister + rtnl_trylock + set_task_boost + skb_append_pagefrags + skb_checksum + synchronize_rcu + trace_print_hex_seq + unregister_netdevice_many + +# required by rndis.ko + dev_get_stats + +# required by roles.ko + class_find_device + device_connection_find_match + __sysfs_match_string + +# required by rpmsg_core.ko + dev_pm_domain_attach + dev_pm_domain_detach + of_device_modalias + of_device_uevent_modalias + strcspn + +# required by rtc-pm8xxx.ko + devm_request_any_context_irq + devm_rtc_device_register + rtc_update_irq + +# required by sctp.ko + __bitmap_shift_right + __bitmap_weight + call_rcu + compat_ip_getsockopt + compat_ip_setsockopt + compat_ipv6_getsockopt + compat_ipv6_setsockopt + compat_sock_common_getsockopt + compat_sock_common_setsockopt + _copy_from_iter_full + crc32c + crc32c_csum_stub + __crc32c_le_shift + crypto_shash_digest + dev_get_by_index_rcu + fl6_sock_lookup + fl6_update_dst + flex_array_alloc + flex_array_free + flex_array_get + flex_array_prealloc + flex_array_put + icmp_err_convert + icmpv6_err_convert + in6_dev_finish_destroy + inet6_add_offload + inet6_add_protocol + inet6_bind + inet6_del_protocol + inet6_destroy_sock + inet6_getname + inet6_ioctl + inet6_register_protosw + inet6_release + inet6_unregister_protosw + inet_accept + inet_add_offload + inet_add_protocol + inet_addr_type + inet_bind + inet_ctl_sock_create + inet_del_offload + inet_del_protocol + inet_get_local_port_range + inet_getname + inet_ioctl + inet_recvmsg + inet_register_protosw + inet_release + inet_sendmsg + inet_shutdown + inet_sk_set_state + inet_sock_destruct + inet_unregister_protosw + iov_iter_revert + ip6_dst_lookup_flow + ip6_xmit + __ip_dev_find + ip_getsockopt + __ip_queue_xmit + ip_setsockopt + ipv6_chk_addr + ipv6_dup_options + ipv6_getsockopt + ipv6_setsockopt + kfree_call_rcu + napi_busy_loop + net_enable_timestamp + nf_conntrack_destroy + nr_free_buffer_pages + overflowuid + percpu_counter_add_batch + percpu_counter_batch + percpu_counter_destroy + __percpu_counter_init + prandom_u32 + prepare_to_wait + prepare_to_wait_exclusive + proc_create_net_data + proc_create_net_single + proc_dointvec_minmax + proc_dostring + proc_doulongvec_minmax + proto_register + proto_unregister + put_cmsg + rcu_barrier + remove_proc_subtree + rfs_needed + rhashtable_free_and_destroy + rhashtable_insert_slow + rhashtable_walk_enter + rhashtable_walk_exit + rhashtable_walk_next + rhashtable_walk_start_check + rhashtable_walk_stop + rhltable_init + rht_bucket_nested + rht_bucket_nested_insert + rps_cpu_mask + rps_sock_flow_table + security_inet_conn_established + security_sctp_assoc_request + security_sctp_bind_connect + security_sctp_sk_clone + send_sig + sk_alloc + __skb_checksum + skb_copy_datagram_iter + skb_queue_head + skb_segment + skb_set_owner_w + sk_busy_loop_end + sk_common_release + sk_filter_trim_cap + sk_free + __sk_mem_reclaim + __sk_mem_schedule + sk_setup_caps + snmp_get_cpu_field + sock_alloc_file + sock_common_getsockopt + sock_common_setsockopt + sock_i_ino + sock_init_data + sock_i_uid + sock_kmalloc + sock_no_mmap + sock_no_sendpage + sock_no_socketpair + sock_prot_inuse_add + __sock_recv_ts_and_drops + sock_wake_async + sock_wfree + __wake_up_sync_key + __xfrm_policy_check + +# required by sctp_diag.ko + inet_diag_msg_attrs_fill + inet_diag_msg_common_fill + inet_diag_register + inet_diag_unregister + netlink_net_capable + nla_reserve_64bit + nla_reserve + sock_diag_check_cookie + sock_diag_save_cookie + +# required by sec_touch.ko + filp_close + filp_open + input_mt_destroy_slots + strncat + sysfs_remove_link + vfs_read + +# required by secure_buffer.ko + trace_print_array_seq + +# required by slg51000-regulator.ko + rdev_get_id + regulator_disable_regmap + regulator_enable_regmap + regulator_get_voltage_sel_regmap + regulator_is_enabled_regmap + regulator_list_voltage_linear + regulator_map_voltage_linear + regulator_notifier_call_chain + regulator_set_voltage_sel_regmap + +# required by smcinvoke.ko + anon_inode_getfile + fget + +# required by smem.ko + devm_ioremap_wc + hwspin_lock_free + hwspin_lock_request_specific + __hwspin_lock_timeout + __hwspin_unlock + of_hwspin_lock_get_id + +# required by smp2p.ko + irq_domain_xlate_twocell + irq_set_parent + qcom_smem_state_register + qcom_smem_state_unregister + +# required by snd-soc-cs35l41.ko + regcache_drop_region + regmap_multi_reg_write_bypassed + regulator_bulk_enable + snd_pcm_format_physical_width + snd_soc_get_enum_double + snd_soc_get_volsw_range + snd_soc_info_volsw_range + snd_soc_put_enum_double + snd_soc_put_volsw_range + +# required by snd-soc-rt5514-spi.ko + _snd_pcm_lib_alloc_vmalloc_buffer + snd_pcm_lib_free_vmalloc_buffer + snd_pcm_lib_get_vmalloc_page + snd_power_wait + snd_soc_set_runtime_hwparams + +# required by snd-soc-rt5514.ko + regmap_register_patch + snd_soc_bytes_info_ext + snd_soc_params_to_frame_size + +# required by snd-soc-wm-adsp.ko + regmap_async_complete + regmap_raw_write + regmap_raw_write_async + snd_compr_stop_error + snd_soc_component_disable_pin + snd_soc_component_force_enable_pin + +# required by spi-geni-qcom.ko + dma_release_channel + geni_read_reg + geni_se_clk_freq_match + geni_se_qupv3_hw_version + get_rx_fifo_depth + get_tx_fifo_width + se_geni_clks_on + se_get_packing_config + __spi_alloc_controller + spi_register_controller + spi_unregister_controller + +# required by spmi-pmic-arb.ko + irq_set_chained_handler_and_data + spmi_controller_add + spmi_controller_alloc + spmi_controller_remove + +# required by st21nfc.ko + desc_to_gpio + device_set_wakeup_capable + +# required by subsystem-restart.ko + system_state + +# required by system_pm.ko + arch_timer_mem_get_cval + +# required by tcpm.ko + dev_fwnode + fwnode_property_present + fwnode_property_read_string + typec_altmode_attention + typec_altmode_notify + typec_altmode_update_active + typec_altmode_vdm + typec_find_port_data_role + typec_find_port_power_role + typec_find_power_role + typec_match_altmode + typec_partner_register_altmode + typec_partner_set_identity + typec_port_register_altmode + typec_set_mode + typec_set_orientation + typec_set_pwr_opmode + typec_set_vconn_role + typec_unregister_altmode + typec_unregister_port + usb_debug_root + +# required by thermal-tsens.ko + devm_thermal_of_virtual_sensor_register + +# required by tps-regulator.ko + devm_gpiod_get_index + +# required by ufs_qcom.ko + ufsdbg_pr_buf_to_std + ufshcd_dme_get_attr + ufshcd_dme_set_attr + ufshcd_get_local_unipro_ver + ufshcd_hold + ufshcd_pltfrm_init + ufshcd_pltfrm_resume + ufshcd_pltfrm_runtime_idle + ufshcd_pltfrm_runtime_resume + ufshcd_pltfrm_runtime_suspend + ufshcd_pltfrm_shutdown + ufshcd_pltfrm_suspend + ufshcd_release + ufshcd_remove + +# required by wlan.ko + bitmap_print_to_pagebuf + capable + __cfg80211_alloc_event_skb + __cfg80211_alloc_reply_skb + cfg80211_ap_stopped + cfg80211_assoc_timeout + cfg80211_calculate_bitrate + cfg80211_chandef_create + cfg80211_ch_switch_notify + cfg80211_connect_done + cfg80211_del_sta_sinfo + cfg80211_disconnected + cfg80211_external_auth_request + cfg80211_ft_event + cfg80211_get_bss + cfg80211_gtk_rekey_notify + cfg80211_ibss_joined + cfg80211_inform_bss_frame_data + cfg80211_mgmt_tx_status + cfg80211_michael_mic_failure + cfg80211_new_sta + cfg80211_pmksa_candidate_notify + cfg80211_put_bss + cfg80211_ready_on_channel + cfg80211_remain_on_channel_expired + cfg80211_roamed + cfg80211_rx_mgmt + cfg80211_rx_unprot_mlme_mgmt + cfg80211_scan_done + cfg80211_sched_scan_results + __cfg80211_send_event_skb + cfg80211_tdls_oper_request + cfg80211_unlink_bss + cfg80211_update_owe_info_event + cfg80211_vendor_cmd_reply + cld80211_get_genl_family + complete_and_exit + cpufreq_quick_get_max + __cpuhp_remove_state + cpu_topology + crypto_aead_setauthsize + crypto_aead_setkey + crypto_alloc_aead + crypto_alloc_base + crypto_alloc_skcipher + crypto_shash_final + crypto_shash_update + default_llseek + deregister_cld_cmd_cb + dev_alloc_name + dump_stack + hex_to_bin + ieee80211_channel_to_frequency + ieee80211_frequency_to_channel + ieee80211_get_channel + ieee80211_hdrlen + irq_set_affinity_hint + mac_pton + netif_tx_stop_all_queues + netlink_broadcast + __netlink_kernel_create + netlink_kernel_release + nla_parse + nla_put_64bit + nla_strlcpy + param_get_string + param_ops_byte + param_set_copystring + pci_read_config_dword + pci_read_config_word + pci_write_config_dword + pci_write_config_word + PDE_DATA + pm_freezing + pm_system_wakeup + print_stack_trace + proc_create_data + proc_mkdir + _raw_spin_trylock + register_cld_cmd_cb + register_netevent_notifier + register_sysctl_table + regulatory_set_wiphy_regd + rtnl_lock + save_stack_trace_tsk + schedule_timeout_interruptible + seq_vprintf + set_cpus_allowed_ptr + skb_queue_purge + skip_spaces + strchrnul + unregister_netevent_notifier + unregister_sysctl_table + vprintk + wcnss_prealloc_get + wcnss_prealloc_put + wiphy_free + wiphy_new_nm + wiphy_register + wiphy_unregister + wireless_send_event + +# required by usb-audio-qmi.ko + find_snd_usb_substream + snd_usb_enable_audio_stream + snd_usb_find_csint_desc + usb_alloc_coherent + usb_free_coherent + usb_get_controller_id + usb_get_sec_event_ring_phys_addr + usb_get_xfer_ring_phys_addr + usb_ifnum_to_if + usb_sec_event_ring_cleanup + usb_sec_event_ring_setup + usb_stop_endpoint + +# required by usb-dwc3-msm.ko + device_wakeup_disable + extcon_get_property + irq_set_irqchip_state + pm_runtime_barrier + regulator_register_notifier + regulator_unregister_notifier + usb_gadget_vbus_connect + usb_gadget_vbus_disconnect + usb_register_atomic_notify + usb_register_notify + usb_speed_string + usb_unregister_atomic_notify + usb_unregister_notify + +# required by usb_f_diag.ko + refcount_dec_and_lock + +# required by usb_f_gsi.ko + dev_get_by_name + kstrtou16_from_user + usb_composite_setup_continue + usb_ep_autoconfig_by_name + usb_ep_set_halt + usb_func_ep_queue + usb_func_wakeup + usb_gsi_ep_op + +# required by usb_f_qdss.ko + usb_ep_autoconfig_ss + usb_ep_dequeue + +# required by usf_dlkm.ko + strncpy_from_user + +# required by vd6281_module.ko + i2c_smbus_read_byte_data + i2c_smbus_write_byte_data + +# required by watchdog_v2.ko + cpu_pm_register_notifier + cpu_pm_unregister_notifier + __cpu_present_mask + disable_percpu_irq + enable_percpu_irq + free_percpu_irq + panic_timeout + __request_percpu_irq + smp_call_function_single + +# required by wcd938x_slave_dlkm.ko + component_del + +# required by wcd_core_dlkm.ko + devm_regmap_add_irq_chip + devm_regmap_del_irq_chip + handle_simple_irq + irq_create_mapping + regmap_irq_get_virq + +# required by wsa881x_dlkm.ko + thermal_zone_device_register + thermal_zone_device_unregister + +# required by xhci-plat-hcd.ko + device_wakeup_enable + usb_add_hcd + __usb_create_hcd + usb_disabled + usb_hcd_is_primary_hcd + usb_put_hcd + usb_remove_hcd + xhci_gen_setup + xhci_init_driver + xhci_resume + xhci_run + xhci_suspend diff --git a/abi_gki_aarch64_whitelist b/abi_gki_aarch64_whitelist index 8c68d31d0bac992fa0066b875219e6ba508606a1..233ea230c8b57a8c7d7d23ec7ad87eb8752bbb88 100644 --- a/abi_gki_aarch64_whitelist +++ b/abi_gki_aarch64_whitelist @@ -1,857 +1,4 @@ [abi_whitelist] - add_timer - add_uevent_var - add_wait_queue - alloc_chrdev_region - __alloc_disk_node - alloc_etherdev_mqs - alloc_netdev_mqs - alloc_pages_exact - __alloc_pages_nodemask - __alloc_percpu - __alloc_skb - __alloc_workqueue_key - arch_bpf_jit_check_func - __arch_copy_from_user - __arch_copy_to_user - arm64_const_caps_ready - autoremove_wake_function - bcmp - blk_cleanup_queue - blk_execute_rq - blk_get_queue - blk_get_request - blk_mq_alloc_tag_set - blk_mq_complete_request - __blk_mq_end_request - blk_mq_end_request - blk_mq_free_tag_set - blk_mq_init_queue - blk_mq_quiesce_queue - blk_mq_requeue_request - blk_mq_run_hw_queues - blk_mq_start_request - blk_mq_start_stopped_hw_queues - blk_mq_stop_hw_queue - blk_mq_unquiesce_queue - blk_mq_virtio_map_queues - blk_put_queue - blk_put_request - blk_queue_alignment_offset - blk_queue_bounce_limit - blk_queue_flag_clear - blk_queue_flag_set - blk_queue_io_min - blk_queue_io_opt - blk_queue_logical_block_size - blk_queue_max_discard_sectors - blk_queue_max_hw_sectors - blk_queue_max_segments - blk_queue_max_segment_size - blk_queue_physical_block_size - blk_queue_rq_timeout - blk_queue_write_cache - blk_rq_map_kern - blk_rq_map_sg - blk_status_to_errno - blk_update_request - bpf_prog_add - bpf_prog_put - bpf_prog_sub - bpf_trace_run10 - bpf_trace_run2 - bpf_trace_run8 - bpf_warn_invalid_xdp_action - build_skb - bus_register - bus_unregister - call_netdevice_notifiers - call_rcu - cancel_delayed_work - cancel_delayed_work_sync - cancel_work_sync - capable - cdev_add - cdev_alloc - cdev_del - cdev_device_add - cdev_device_del - cdev_init - cfg80211_connect_done - cfg80211_disconnected - cfg80211_inform_bss_data - cfg80211_put_bss - cfg80211_scan_done - __cfi_slowpath - check_disk_change - __check_object_size - __class_create - class_destroy - __class_register - class_unregister - clear_page - clk_disable - clk_enable - clk_get_rate - clk_prepare - clk_unprepare - complete - complete_all - completion_done - console_suspend_enabled - __const_udelay - consume_skb - _copy_from_iter_full - copy_page - _copy_to_iter - cpu_bit_bitmap - cpufreq_generic_attr - cpufreq_register_driver - cpufreq_unregister_driver - __cpuhp_remove_state - __cpuhp_setup_state - __cpuhp_state_add_instance - __cpuhp_state_remove_instance - cpu_hwcap_keys - cpu_hwcaps - cpumask_next - cpumask_next_wrap - cpu_number - __cpu_online_mask - cpus_read_lock - cpus_read_unlock - cpu_topology - crypto_ablkcipher_type - crypto_dequeue_request - crypto_enqueue_request - crypto_init_queue - crypto_register_alg - crypto_unregister_alg - datagram_poll - debugfs_create_dir - debugfs_create_file - debugfs_create_x32 - debugfs_remove - debugfs_remove_recursive - debug_smp_processor_id - default_llseek - default_wake_function - delayed_work_timer_fn - del_gendisk - del_timer - del_timer_sync - destroy_workqueue - dev_add_pack - dev_close - dev_driver_string - _dev_err - dev_fwnode - __dev_get_by_index - dev_get_by_index - dev_get_by_index_rcu - dev_get_stats - device_add - device_add_disk - device_create - device_create_file - device_del - device_destroy - device_for_each_child - device_initialize - device_property_present - device_property_read_u32_array - device_register - device_remove_file - device_unregister - _dev_info - __dev_kfree_skb_any - devm_clk_get - dev_mc_sync_multiple - dev_mc_unsync - devm_gpiod_get_index - devm_gpio_request_one - devm_ioremap - devm_ioremap_resource - devm_kfree - devm_kmalloc - devm_regulator_get_optional - __devm_request_region - devm_request_threaded_irq - __devm_reset_control_get - devm_rtc_allocate_device - _dev_notice - dev_open - dev_pm_domain_attach - dev_pm_domain_detach - dev_printk - dev_queue_xmit - dev_remove_pack - devres_add - __devres_alloc_node - devres_destroy - devres_free - dev_set_mtu - dev_set_name - dev_uc_sync_multiple - dev_uc_unsync - _dev_warn - disable_irq - dma_alloc_from_dev_coherent - dma_buf_export - dma_buf_fd - dma_buf_get - dma_buf_put - dma_fence_context_alloc - dma_fence_enable_sw_signaling - dma_fence_init - dma_fence_match_context - dma_fence_release - dma_fence_signal - dma_fence_signal_locked - dma_fence_wait_timeout - dma_release_from_dev_coherent - driver_register - driver_unregister - drm_add_edid_modes - drm_add_modes_noedid - drm_atomic_helper_check - drm_atomic_helper_cleanup_planes - drm_atomic_helper_commit - drm_atomic_helper_commit_hw_done - drm_atomic_helper_commit_modeset_disables - drm_atomic_helper_commit_modeset_enables - drm_atomic_helper_commit_planes - drm_atomic_helper_connector_destroy_state - drm_atomic_helper_connector_duplicate_state - drm_atomic_helper_connector_reset - drm_atomic_helper_crtc_destroy_state - drm_atomic_helper_crtc_duplicate_state - drm_atomic_helper_crtc_reset - drm_atomic_helper_disable_plane - drm_atomic_helper_page_flip - drm_atomic_helper_plane_destroy_state - drm_atomic_helper_plane_duplicate_state - drm_atomic_helper_plane_reset - drm_atomic_helper_set_config - drm_atomic_helper_shutdown - drm_atomic_helper_update_plane - drm_atomic_helper_wait_for_vblanks - drm_class_device_register - drm_class_device_unregister - drm_clflush_pages - drm_compat_ioctl - drm_connector_attach_edid_property - drm_connector_attach_encoder - drm_connector_cleanup - drm_connector_init - drm_connector_register - drm_connector_unregister - drm_connector_update_edid_property - drm_crtc_cleanup - drm_crtc_init_with_planes - drm_crtc_send_vblank_event - drm_cvt_mode - drm_dbg - drm_debugfs_create_files - drm_dev_alloc - drm_dev_put - drm_dev_register - drm_dev_set_unique - drm_do_get_edid - drm_encoder_cleanup - drm_encoder_init - drm_err - drm_framebuffer_init - drm_gem_fb_create_handle - drm_gem_fb_destroy - drm_gem_handle_create - drm_gem_object_init - drm_gem_object_lookup - drm_gem_object_put_unlocked - drm_gem_object_release - drm_gem_prime_export - drm_gem_prime_fd_to_handle - drm_gem_prime_handle_to_fd - drm_gem_prime_import - drm_gem_prime_mmap - drm_global_item_ref - drm_global_item_unref - drm_helper_hpd_irq_event - drm_helper_mode_fill_fb_struct - drm_helper_probe_single_connector_modes - drm_ht_create - drm_ht_find_item - drm_ht_insert_item - drm_ht_just_insert_please - drm_ht_remove - drm_ht_remove_item - drm_ioctl - drm_kms_helper_hotplug_event - drm_mm_init - drm_mm_insert_node_in_range - drm_mm_print - drm_mm_remove_node - drm_mm_takedown - drm_mode_config_cleanup - drm_mode_config_init - drm_mode_config_reset - drm_mode_probed_add - drm_open - drm_plane_cleanup - drm_poll - drm_prime_pages_to_sg - __drm_printfn_debug - drm_put_dev - drm_read - drm_release - drm_set_preferred_mode - drm_universal_plane_init - drm_vma_offset_add - drm_vma_offset_lookup_locked - drm_vma_offset_manager_destroy - drm_vma_offset_manager_init - drm_vma_offset_remove - dummy_dma_ops - eth_commit_mac_addr_change - ether_setup - eth_prepare_mac_addr_change - __ethtool_get_link_ksettings - ethtool_op_get_link - ethtool_op_get_ts_info - eth_type_trans - eth_validate_addr - event_triggers_call - fasync_helper - fd_install - finish_wait - flow_keys_basic_dissector - flush_work - flush_workqueue - fput - free_irq - free_netdev - __free_pages - free_pages_exact - free_percpu - freezing_slow_path - fsl8250_handle_irq - generic_file_llseek - get_device - get_random_bytes - __get_task_comm - get_unused_fd_flags - gpiod_cansleep - gpiod_get_raw_value - gpiod_get_raw_value_cansleep - gpiod_get_value - gpiod_get_value_cansleep - gpiod_is_active_low - gpiod_set_debounce - gpiod_to_irq - gpio_to_desc - hrtimer_active - hrtimer_cancel - hrtimer_forward - hrtimer_init - hrtimer_start_range_ns - hvc_alloc - hvc_instantiate - hvc_kick - hvc_poll - hvc_remove - __hvc_resize - hwrng_register - hwrng_unregister - ida_alloc_range - ida_destroy - ida_free - init_net - init_timer_key - init_wait_entry - __init_waitqueue_head - input_alloc_absinfo - input_allocate_device - input_event - input_free_device - input_mt_init_slots - input_register_device - input_set_abs_params - input_unregister_device - iomem_resource - __ioremap - __iounmap - irq_dispose_mapping - irq_set_affinity_hint - irq_set_irq_wake - jiffies - jiffies_to_msecs - kernel_kobj - kfree - kfree_call_rcu - kfree_skb - kill_fasync - kimage_voffset - __kmalloc - kmalloc_caches - kmalloc_order_trace - kmem_cache_alloc - kmem_cache_alloc_trace - kmem_cache_create - kmem_cache_destroy - kmem_cache_free - kmemdup - kobject_del - kobject_init_and_add - kobject_put - kobject_uevent - kobject_uevent_env - kstrtoull - kthread_create_on_node - kthread_create_worker - kthread_destroy_worker - kthread_queue_work - kthread_should_stop - kthread_stop - ktime_get - ktime_get_real_seconds - ktime_get_ts64 - ktime_get_with_offset - kvfree - kvmalloc_node - kzfree - led_classdev_unregister - led_trigger_event - led_trigger_register_simple - led_trigger_unregister_simple - __ll_sc_atomic64_add - __ll_sc_atomic64_add_return_relaxed - __ll_sc_atomic64_andnot - __ll_sc_atomic64_fetch_or - __ll_sc_atomic64_or - __ll_sc_atomic_add - __ll_sc_atomic_add_return - __ll_sc_atomic_sub - __ll_sc_atomic_sub_return - __ll_sc___cmpxchg_case_mb_4 - __ll_sc___cmpxchg_case_mb_8 - __local_bh_enable_ip - lock_sock_nested - mark_page_accessed - memcpy - __memcpy_fromio - __memcpy_toio - memdup_user - memmove - memparse - memset - __memset_io - memstart_addr - memzero_explicit - misc_deregister - misc_register - mod_timer - __module_get - module_put - __msecs_to_jiffies - msleep - __mutex_init - mutex_lock - mutex_lock_interruptible - mutex_trylock - mutex_unlock - __napi_alloc_skb - napi_complete_done - napi_consume_skb - napi_disable - napi_gro_receive - napi_hash_del - __napi_schedule - napi_schedule_prep - __netdev_alloc_skb - netdev_change_features - netdev_err - netdev_increment_features - netdev_info - netdev_lower_state_changed - netdev_master_upper_dev_link - netdev_notify_peers - netdev_rx_handler_register - netdev_rx_handler_unregister - netdev_upper_dev_link - netdev_upper_dev_unlink - netdev_warn - netif_carrier_off - netif_carrier_on - netif_device_attach - netif_device_detach - netif_napi_add - netif_napi_del - netif_receive_skb - netif_rx - netif_rx_ni - netif_schedule_queue - netif_set_real_num_rx_queues - netif_set_real_num_tx_queues - __netif_set_xps_queue - netif_stacked_transfer_operstate - netif_tx_stop_all_queues - netif_tx_wake_queue - netlink_capable - __netlink_dump_start - net_ratelimit - nf_conntrack_destroy - nla_memcpy - nla_parse - nla_put - __nlmsg_put - no_llseek - nonseekable_open - noop_llseek - nr_cpu_ids - nr_swap_pages - nsecs_to_jiffies - of_address_to_resource - of_alias_get_id - of_device_is_big_endian - of_device_is_compatible - of_find_property - of_get_child_by_name - of_get_next_child - of_get_property - of_irq_get - of_led_classdev_register - of_match_device - of_parse_phandle - of_property_read_u64 - of_property_read_variable_u32_array - panic - param_ops_bool - param_ops_int - param_ops_uint - passthru_features_check - pci_alloc_irq_vectors_affinity - pci_bus_type - pci_disable_device - pci_enable_device - pci_find_capability - pci_find_ext_capability - pci_find_next_capability - pci_free_irq_vectors - pci_iomap_range - pci_irq_get_affinity - pci_irq_vector - pci_read_config_byte - pci_read_config_dword - __pci_register_driver - pci_release_selected_regions - pci_request_selected_regions - pci_set_master - pci_unregister_driver - PDE_DATA - __per_cpu_offset - perf_trace_buf_alloc - perf_trace_run_bpf_submit - pipe_lock - pipe_unlock - platform_device_add - platform_device_add_data - platform_device_alloc - platform_device_del - platform_device_put - platform_device_register_full - platform_device_unregister - __platform_driver_register - platform_driver_unregister - platform_get_irq - platform_get_resource - pm_generic_resume - pm_generic_runtime_resume - pm_generic_runtime_suspend - pm_generic_suspend - __pm_runtime_disable - pm_runtime_enable - __pm_runtime_idle - __pm_runtime_resume - pm_runtime_set_autosuspend_delay - __pm_runtime_set_status - __pm_runtime_suspend - __pm_runtime_use_autosuspend - pm_wakeup_dev_event - prandom_u32 - preempt_count_add - preempt_count_sub - preempt_schedule - preempt_schedule_notrace - prepare_to_wait - prepare_to_wait_event - printk - proc_create_net_single - proc_mkdir_data - proto_register - proto_unregister - __put_cred - put_device - put_disk - __put_page - put_unused_fd - queue_delayed_work_on - queue_work_on - ___ratelimit - _raw_read_lock - _raw_read_unlock - _raw_spin_lock - _raw_spin_lock_bh - _raw_spin_lock_irq - _raw_spin_lock_irqsave - _raw_spin_trylock - _raw_spin_unlock - _raw_spin_unlock_bh - _raw_spin_unlock_irq - _raw_spin_unlock_irqrestore - _raw_write_lock_bh - _raw_write_unlock_bh - rcu_barrier - __rcu_read_lock - __rcu_read_unlock - refcount_dec_and_test_checked - refcount_inc_checked - refcount_inc_not_zero_checked - __refrigerator - register_blkdev - __register_chrdev - register_netdev - register_netdevice - register_netdevice_notifier - register_pernet_subsys - register_pm_notifier - register_shrinker - regulator_count_voltages - regulator_disable - regulator_enable - regulator_get_current_limit - regulator_get_voltage - regulator_is_supported_voltage - regulator_list_voltage - regulator_set_voltage - release_sock - remove_conflicting_framebuffers - remove_proc_entry - remove_wait_queue - __request_module - request_threaded_irq - reservation_object_add_excl_fence - reservation_object_add_shared_fence - reservation_object_copy_fences - reservation_object_reserve_shared - reservation_object_test_signaled_rcu - reservation_object_wait_timeout_rcu - reservation_ww_class - reset_control_assert - reset_control_deassert - revalidate_disk - round_jiffies - __rtc_register_device - rtc_time64_to_tm - rtc_tm_to_time64 - rtc_update_irq - rtnl_is_locked - rtnl_link_register - rtnl_link_unregister - rtnl_lock - rtnl_register_module - rtnl_unlock - rtnl_unregister - rtnl_unregister_all - sched_setscheduler - schedule - schedule_timeout - scnprintf - security_sock_graft - send_sig - seq_lseek - seq_printf - seq_putc - seq_puts - seq_read - serial8250_get_port - serial8250_register_8250_port - serial8250_resume_port - serial8250_suspend_port - serial8250_unregister_port - set_disk_ro - set_page_dirty - sg_alloc_table - sg_alloc_table_from_pages - sg_copy_from_buffer - sg_copy_to_buffer - sg_free_table - sg_init_one - sg_init_table - sg_miter_next - sg_miter_start - sg_miter_stop - sg_nents - sg_nents_for_len - sg_next - shmem_file_setup - shmem_read_mapping_page_gfp - si_mem_available - si_meminfo - simple_attr_open - simple_attr_read - simple_attr_release - simple_attr_write - simple_read_from_buffer - simple_strtoul - single_open - single_release - sk_alloc - skb_add_rx_frag - skb_clone - skb_coalesce_rx_frag - skb_copy - skb_dequeue - __skb_flow_dissect - skb_free_datagram - skb_page_frag_refill - skb_partial_csum_set - skb_put - skb_queue_purge - skb_queue_tail - skb_recv_datagram - skb_to_sgvec - skb_trim - skb_tstamp_tx - sk_free - snprintf - sock_alloc_send_skb - sock_diag_register - sock_diag_save_cookie - sock_diag_unregister - sock_efree - sock_get_timestamp - sock_i_ino - sock_init_data - sock_no_accept - sock_no_bind - sock_no_connect - sock_no_getname - sock_no_getsockopt - sock_no_ioctl - sock_no_listen - sock_no_mmap - sock_no_sendpage - sock_no_setsockopt - sock_no_shutdown - sock_no_socketpair - sock_queue_rcv_skb - __sock_recv_ts_and_drops - sock_register - __sock_tx_timestamp - sock_unregister - __splice_from_pipe - sprintf - sscanf - __stack_chk_fail - __stack_chk_guard - strcmp - strcpy - string_get_size - strlcpy - strlen - strncmp - strncpy - strstr - __sw_hweight64 - swiotlb_max_segment - sync_file_create - sync_file_get_fence - synchronize_hardirq - synchronize_irq - synchronize_net - synchronize_rcu - sysfs_create_bin_file - sysfs_create_group - __sysfs_match_string - sysfs_remove_bin_file - sysfs_remove_group - system_freezable_wq - system_freezing_cnt - system_wq - __tasklet_hi_schedule - tasklet_init - tasklet_kill - __tasklet_schedule - trace_define_field - trace_event_buffer_commit - trace_event_buffer_reserve - trace_event_ignore_this_pid - trace_event_raw_init - trace_event_reg - trace_handle_return - __tracepoint_dma_fence_emit - __tracepoint_xdp_exception - trace_print_symbols_seq - trace_raw_output_prep - trace_seq_printf - try_module_get - unlock_page - unmap_mapping_range - unregister_blkdev - __unregister_chrdev - unregister_chrdev_region - unregister_netdev - unregister_netdevice_notifier - unregister_netdevice_queue - unregister_pernet_subsys - unregister_pm_notifier - unregister_shrinker - up_read - usb_add_gadget_udc - usb_add_hcd - usb_create_hcd - usb_create_shared_hcd - usb_del_gadget_udc - usb_disabled - usb_ep_set_maxpacket_limit - usb_gadget_giveback_request - usb_gadget_udc_reset - usb_get_dev - usb_hcd_check_unlink_urb - usb_hcd_giveback_urb - usb_hcd_is_primary_hcd - usb_hcd_link_urb_to_ep - usb_hcd_poll_rh_status - usb_hcd_resume_root_hub - usb_hcd_unlink_urb_from_ep - usb_put_dev - usb_put_hcd - usb_remove_hcd - usleep_range - vmalloc_to_page - vmap - vm_get_page_prot - vm_insert_mixed - vm_insert_pfn - vunmap - wait_for_completion - wait_for_completion_killable - wait_woken - __wake_up - wake_up_process - __warn_printk - wiphy_free - wiphy_new_nm - wiphy_register - wiphy_unregister - woken_wake_function - ww_mutex_lock - ww_mutex_lock_interruptible - ww_mutex_unlock - xdp_do_flush_map - xdp_do_redirect - xdp_return_frame - xdp_return_frame_rx_napi - xdp_rxq_info_reg - xdp_rxq_info_reg_mem_model - xdp_rxq_info_unreg \ No newline at end of file +# commonly used symbols + module_layout + __put_task_struct diff --git a/arch/arc/boot/dts/axs10x_mb.dtsi b/arch/arc/boot/dts/axs10x_mb.dtsi index 37bafd44e36d0fed9b85e80ea356cd78df0c1872..da10a569adf7366a29f6598b81e89ab50f6fe7d3 100644 --- a/arch/arc/boot/dts/axs10x_mb.dtsi +++ b/arch/arc/boot/dts/axs10x_mb.dtsi @@ -80,6 +80,7 @@ interrupt-names = "macirq"; phy-mode = "rgmii"; snps,pbl = < 32 >; + snps,multicast-filter-bins = <256>; clocks = <&apbclk>; clock-names = "stmmaceth"; max-speed = <100>; diff --git a/arch/arc/plat-eznps/Kconfig b/arch/arc/plat-eznps/Kconfig index 8eff057efcaebeae04b1fb801c003418090551eb..ce908e2c5282434db3643ec54674544b533877d2 100644 --- a/arch/arc/plat-eznps/Kconfig +++ b/arch/arc/plat-eznps/Kconfig @@ -7,7 +7,7 @@ menuconfig ARC_PLAT_EZNPS bool "\"EZchip\" ARC dev platform" select CPU_BIG_ENDIAN - select CLKSRC_NPS + select CLKSRC_NPS if !PHYS_ADDR_T_64BIT select EZNPS_GIC select EZCHIP_NPS_MANAGEMENT_ENET if ETHERNET help diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index be580d4df564ef205f3dd15ac84b10a1fd1c684a..1042622183193740ab702bf9df20252eed153abe 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -62,7 +62,7 @@ config ARM select HAVE_EBPF_JIT if !CPU_ENDIAN_BE32 select HAVE_CONTEXT_TRACKING select HAVE_C_RECORDMCOUNT - select HAVE_DEBUG_KMEMLEAK + select HAVE_DEBUG_KMEMLEAK if !XIP_KERNEL select HAVE_DMA_CONTIGUOUS if MMU select HAVE_DYNAMIC_FTRACE if (!XIP_KERNEL) && !CPU_ENDIAN_BE32 && MMU select HAVE_DYNAMIC_FTRACE_WITH_REGS if HAVE_DYNAMIC_FTRACE @@ -2082,7 +2082,7 @@ config XIP_DEFLATED_DATA config KEXEC bool "Kexec system call (EXPERIMENTAL)" depends on (!SMP || PM_SLEEP_SMP) - depends on !CPU_V7M + depends on MMU select KEXEC_CORE help kexec is a system call that implements the ability to shutdown your diff --git a/arch/arm/boot/dts/am335x-boneblack-common.dtsi b/arch/arm/boot/dts/am335x-boneblack-common.dtsi index 21bc1173fa6b9f6c1ea5aa107535a1e9d191b416..cb4267edde63d778145ac47e58ee39d6433bcef9 100644 --- a/arch/arm/boot/dts/am335x-boneblack-common.dtsi +++ b/arch/arm/boot/dts/am335x-boneblack-common.dtsi @@ -131,6 +131,11 @@ }; / { + memory@80000000 { + device_type = "memory"; + reg = <0x80000000 0x20000000>; /* 512 MB */ + }; + clk_mcasp0_fixed: clk_mcasp0_fixed { #clock-cells = <0>; compatible = "fixed-clock"; diff --git a/arch/arm/boot/dts/am437x-idk-evm.dts b/arch/arm/boot/dts/am437x-idk-evm.dts index 20132477a87114e45f2f8a317c32e6f807b47085..80603af33cab14bf383170d9f1bdeeedc39041f2 100644 --- a/arch/arm/boot/dts/am437x-idk-evm.dts +++ b/arch/arm/boot/dts/am437x-idk-evm.dts @@ -525,11 +525,11 @@ * Supply voltage supervisor on board will not allow opp50 so * disable it and set opp100 as suspend OPP. */ - opp50@300000000 { + opp50-300000000 { status = "disabled"; }; - opp100@600000000 { + opp100-600000000 { opp-suspend; }; }; diff --git a/arch/arm/boot/dts/am43x-epos-evm.dts b/arch/arm/boot/dts/am43x-epos-evm.dts index 12735cf9674bb67d24c91a6da6724fca7b5a7d87..b6950eee550b275272b57d9c15d707f06eba6e57 100644 --- a/arch/arm/boot/dts/am43x-epos-evm.dts +++ b/arch/arm/boot/dts/am43x-epos-evm.dts @@ -839,6 +839,7 @@ pinctrl-names = "default", "sleep"; pinctrl-0 = <&spi0_pins_default>; pinctrl-1 = <&spi0_pins_sleep>; + ti,pindir-d0-out-d1-in = <1>; }; &spi1 { @@ -846,6 +847,7 @@ pinctrl-names = "default", "sleep"; pinctrl-0 = <&spi1_pins_default>; pinctrl-1 = <&spi1_pins_sleep>; + ti,pindir-d0-out-d1-in = <1>; }; &usb2_phy1 { diff --git a/arch/arm/boot/dts/am43xx-clocks.dtsi b/arch/arm/boot/dts/am43xx-clocks.dtsi index a7037a4b4fd488d63788beb0a208486312bac3fc..ce3c4196f173c4c42f271360dfb5fa5ea27a823a 100644 --- a/arch/arm/boot/dts/am43xx-clocks.dtsi +++ b/arch/arm/boot/dts/am43xx-clocks.dtsi @@ -707,6 +707,60 @@ ti,bit-shift = <8>; reg = <0x2a48>; }; + + clkout1_osc_div_ck: clkout1-osc-div-ck { + #clock-cells = <0>; + compatible = "ti,divider-clock"; + clocks = <&sys_clkin_ck>; + ti,bit-shift = <20>; + ti,max-div = <4>; + reg = <0x4100>; + }; + + clkout1_src2_mux_ck: clkout1-src2-mux-ck { + #clock-cells = <0>; + compatible = "ti,mux-clock"; + clocks = <&clk_rc32k_ck>, <&sysclk_div>, <&dpll_ddr_m2_ck>, + <&dpll_per_m2_ck>, <&dpll_disp_m2_ck>, + <&dpll_mpu_m2_ck>; + reg = <0x4100>; + }; + + clkout1_src2_pre_div_ck: clkout1-src2-pre-div-ck { + #clock-cells = <0>; + compatible = "ti,divider-clock"; + clocks = <&clkout1_src2_mux_ck>; + ti,bit-shift = <4>; + ti,max-div = <8>; + reg = <0x4100>; + }; + + clkout1_src2_post_div_ck: clkout1-src2-post-div-ck { + #clock-cells = <0>; + compatible = "ti,divider-clock"; + clocks = <&clkout1_src2_pre_div_ck>; + ti,bit-shift = <8>; + ti,max-div = <32>; + ti,index-power-of-two; + reg = <0x4100>; + }; + + clkout1_mux_ck: clkout1-mux-ck { + #clock-cells = <0>; + compatible = "ti,mux-clock"; + clocks = <&clkout1_osc_div_ck>, <&clk_rc32k_ck>, + <&clkout1_src2_post_div_ck>, <&dpll_extdev_m2_ck>; + ti,bit-shift = <16>; + reg = <0x4100>; + }; + + clkout1_ck: clkout1-ck { + #clock-cells = <0>; + compatible = "ti,gate-clock"; + clocks = <&clkout1_mux_ck>; + ti,bit-shift = <23>; + reg = <0x4100>; + }; }; &prcm { diff --git a/arch/arm/boot/dts/am571x-idk.dts b/arch/arm/boot/dts/am571x-idk.dts index 6bebedfc0f35a68243bf7bc1927638aedce6afd8..510f61d20b6d62eeb012545be4881bb2f7b73adb 100644 --- a/arch/arm/boot/dts/am571x-idk.dts +++ b/arch/arm/boot/dts/am571x-idk.dts @@ -90,11 +90,7 @@ &pcie1_rc { status = "okay"; - gpios = <&gpio3 23 GPIO_ACTIVE_HIGH>; -}; - -&pcie1_ep { - gpios = <&gpio3 23 GPIO_ACTIVE_HIGH>; + gpios = <&gpio5 18 GPIO_ACTIVE_HIGH>; }; &mmc1 { diff --git a/arch/arm/boot/dts/am572x-idk-common.dtsi b/arch/arm/boot/dts/am572x-idk-common.dtsi index 784639ddf4513a316971ec181b3a1ade74fe18f1..8a7d34c8ae11523252fc9c193b788c1c95e04381 100644 --- a/arch/arm/boot/dts/am572x-idk-common.dtsi +++ b/arch/arm/boot/dts/am572x-idk-common.dtsi @@ -71,10 +71,6 @@ gpios = <&gpio3 23 GPIO_ACTIVE_HIGH>; }; -&pcie1_ep { - gpios = <&gpio3 23 GPIO_ACTIVE_HIGH>; -}; - &mailbox5 { status = "okay"; mbox_ipu1_ipc3x: mbox_ipu1_ipc3x { diff --git a/arch/arm/boot/dts/am57xx-beagle-x15-common.dtsi b/arch/arm/boot/dts/am57xx-beagle-x15-common.dtsi index d53532b479475212b62b17472cb843e82a6f3bff..0051b2e05c2d54e40a122e2ffd5d0ce372046107 100644 --- a/arch/arm/boot/dts/am57xx-beagle-x15-common.dtsi +++ b/arch/arm/boot/dts/am57xx-beagle-x15-common.dtsi @@ -32,6 +32,27 @@ reg = <0x0 0x80000000 0x0 0x80000000>; }; + main_12v0: fixedregulator-main_12v0 { + /* main supply */ + compatible = "regulator-fixed"; + regulator-name = "main_12v0"; + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + regulator-always-on; + regulator-boot-on; + }; + + evm_5v0: fixedregulator-evm_5v0 { + /* Output of TPS54531D */ + compatible = "regulator-fixed"; + regulator-name = "evm_5v0"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <&main_12v0>; + regulator-always-on; + regulator-boot-on; + }; + vdd_3v3: fixedregulator-vdd_3v3 { compatible = "regulator-fixed"; regulator-name = "vdd_3v3"; @@ -550,10 +571,6 @@ gpios = <&gpio2 8 GPIO_ACTIVE_LOW>; }; -&pcie1_ep { - gpios = <&gpio2 8 GPIO_ACTIVE_LOW>; -}; - &mcasp3 { #sound-dai-cells = <0>; assigned-clocks = <&l4per_clkctrl DRA7_MCASP3_CLKCTRL 24>; diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi index d107459fc0f89417f7d7adde30d0155da86dc929..f2e1015d75ab480daddbe1a9cd75608aec5476f5 100644 --- a/arch/arm/boot/dts/aspeed-g5.dtsi +++ b/arch/arm/boot/dts/aspeed-g5.dtsi @@ -247,7 +247,7 @@ compatible = "aspeed,ast2500-gpio"; reg = <0x1e780000 0x1000>; interrupts = <20>; - gpio-ranges = <&pinctrl 0 0 220>; + gpio-ranges = <&pinctrl 0 0 232>; clocks = <&syscon ASPEED_CLK_APB>; interrupt-controller; }; diff --git a/arch/arm/boot/dts/at91-nattis-2-natte-2.dts b/arch/arm/boot/dts/at91-nattis-2-natte-2.dts index af9f38456d04e7a41964f400504f799140c6c760..4308a07b792ea8358591b591a7892fa9c932a08d 100644 --- a/arch/arm/boot/dts/at91-nattis-2-natte-2.dts +++ b/arch/arm/boot/dts/at91-nattis-2-natte-2.dts @@ -38,14 +38,16 @@ atmel,pins = ; + (AT91_PINCTRL_OUTPUT | + AT91_PINCTRL_OUTPUT_VAL(0))>; }; pinctrl_lcd_hipow0: lcd_hipow0 { atmel,pins = ; + (AT91_PINCTRL_OUTPUT | + AT91_PINCTRL_OUTPUT_VAL(0))>; }; }; }; @@ -219,6 +221,7 @@ reg = <0>; bus-width = <4>; cd-gpios = <&pioD 5 GPIO_ACTIVE_HIGH>; + cd-inverted; }; }; diff --git a/arch/arm/boot/dts/at91sam9260.dtsi b/arch/arm/boot/dts/at91sam9260.dtsi index 9118e29b6d6ad28313ac005377a88174eb4766fb..3fa6b9fbb8226aa52f590ce9e7a2c63f205d153c 100644 --- a/arch/arm/boot/dts/at91sam9260.dtsi +++ b/arch/arm/boot/dts/at91sam9260.dtsi @@ -434,7 +434,7 @@ usart0 { pinctrl_usart0: usart0-0 { atmel,pins = - ; }; @@ -468,7 +468,7 @@ usart1 { pinctrl_usart1: usart1-0 { atmel,pins = - ; }; @@ -486,7 +486,7 @@ usart2 { pinctrl_usart2: usart2-0 { atmel,pins = - ; }; @@ -504,7 +504,7 @@ usart3 { pinctrl_usart3: usart3-0 { atmel,pins = - ; }; @@ -522,7 +522,7 @@ uart0 { pinctrl_uart0: uart0-0 { atmel,pins = - ; }; }; @@ -530,7 +530,7 @@ uart1 { pinctrl_uart1: uart1-0 { atmel,pins = - ; }; }; diff --git a/arch/arm/boot/dts/at91sam9261.dtsi b/arch/arm/boot/dts/at91sam9261.dtsi index 33f09d5ea0201ed80cd3456363e8f0070ca8470d..590d288529978228bd36d2c8181f5e2d7125c498 100644 --- a/arch/arm/boot/dts/at91sam9261.dtsi +++ b/arch/arm/boot/dts/at91sam9261.dtsi @@ -328,7 +328,7 @@ usart0 { pinctrl_usart0: usart0-0 { atmel,pins = - , + , ; }; @@ -346,7 +346,7 @@ usart1 { pinctrl_usart1: usart1-0 { atmel,pins = - , + , ; }; @@ -364,7 +364,7 @@ usart2 { pinctrl_usart2: usart2-0 { atmel,pins = - , + , ; }; diff --git a/arch/arm/boot/dts/at91sam9263.dtsi b/arch/arm/boot/dts/at91sam9263.dtsi index af68a86c99731f4c93e18310f8fb814d36215bff..745918b9778602cf8e72cb94df0598f09a9c0fb5 100644 --- a/arch/arm/boot/dts/at91sam9263.dtsi +++ b/arch/arm/boot/dts/at91sam9263.dtsi @@ -437,7 +437,7 @@ usart0 { pinctrl_usart0: usart0-0 { atmel,pins = - ; }; @@ -455,7 +455,7 @@ usart1 { pinctrl_usart1: usart1-0 { atmel,pins = - ; }; @@ -473,7 +473,7 @@ usart2 { pinctrl_usart2: usart2-0 { atmel,pins = - ; }; diff --git a/arch/arm/boot/dts/at91sam9g45.dtsi b/arch/arm/boot/dts/at91sam9g45.dtsi index d16db1fa7e15c69dba0060dfc647211e440f607d..ea80a5a12760949e323a3248c4622b18de2d6744 100644 --- a/arch/arm/boot/dts/at91sam9g45.dtsi +++ b/arch/arm/boot/dts/at91sam9g45.dtsi @@ -555,7 +555,7 @@ usart0 { pinctrl_usart0: usart0-0 { atmel,pins = - ; }; @@ -573,7 +573,7 @@ usart1 { pinctrl_usart1: usart1-0 { atmel,pins = - ; }; @@ -591,7 +591,7 @@ usart2 { pinctrl_usart2: usart2-0 { atmel,pins = - ; }; @@ -609,7 +609,7 @@ usart3 { pinctrl_usart3: usart3-0 { atmel,pins = - ; }; diff --git a/arch/arm/boot/dts/at91sam9rl.dtsi b/arch/arm/boot/dts/at91sam9rl.dtsi index 8fb22030f00bc2b761fa0604fe317a1212f82cf3..ad495f5a5790f4f9dcd661fedd20120e72e28e86 100644 --- a/arch/arm/boot/dts/at91sam9rl.dtsi +++ b/arch/arm/boot/dts/at91sam9rl.dtsi @@ -681,7 +681,7 @@ usart0 { pinctrl_usart0: usart0-0 { atmel,pins = - , + , ; }; @@ -720,7 +720,7 @@ usart1 { pinctrl_usart1: usart1-0 { atmel,pins = - , + , ; }; @@ -743,7 +743,7 @@ usart2 { pinctrl_usart2: usart2-0 { atmel,pins = - , + , ; }; @@ -766,7 +766,7 @@ usart3 { pinctrl_usart3: usart3-0 { atmel,pins = - , + , ; }; diff --git a/arch/arm/boot/dts/bcm2835-rpi.dtsi b/arch/arm/boot/dts/bcm2835-rpi.dtsi index cb2d6d78a7fbf5d9a1ef3f70563fcb94f2ce8bef..c481eab1bd7c0a20c749cd38542f12247c276869 100644 --- a/arch/arm/boot/dts/bcm2835-rpi.dtsi +++ b/arch/arm/boot/dts/bcm2835-rpi.dtsi @@ -32,7 +32,7 @@ mailbox@7e00b840 { compatible = "brcm,bcm2835-vchiq"; - reg = <0x7e00b840 0xf>; + reg = <0x7e00b840 0x3c>; interrupts = <0 2>; }; }; diff --git a/arch/arm/boot/dts/dra76x.dtsi b/arch/arm/boot/dts/dra76x.dtsi index 613e4dc0ed3ec26225173c9f62657c3d5150889b..216e1d1a69c7d4a334379277e5a05eaaa466e7cc 100644 --- a/arch/arm/boot/dts/dra76x.dtsi +++ b/arch/arm/boot/dts/dra76x.dtsi @@ -81,3 +81,8 @@ reg = <0x3fc>; }; }; + +&mmc3 { + /* dra76x is not affected by i887 */ + max-frequency = <96000000>; +}; diff --git a/arch/arm/boot/dts/imx6dl-icore-mipi.dts b/arch/arm/boot/dts/imx6dl-icore-mipi.dts index bf53f0552aa17cd5f5525ff5e8e0ee0f0a80deed..f88ad5e993edd5d6dcbb75ed0f6f40c642ab3582 100644 --- a/arch/arm/boot/dts/imx6dl-icore-mipi.dts +++ b/arch/arm/boot/dts/imx6dl-icore-mipi.dts @@ -8,7 +8,7 @@ /dts-v1/; #include "imx6dl.dtsi" -#include "imx6qdl-icore.dtsi" +#include "imx6qdl-icore-1.5.dtsi" / { model = "Engicam i.CoreM6 DualLite/Solo MIPI Starter Kit"; diff --git a/arch/arm/boot/dts/imx6q-dhcom-pdk2.dts b/arch/arm/boot/dts/imx6q-dhcom-pdk2.dts index 9c61e3be2d9a37613f42efaf72cb13e982e830af..1c46df6827f503c4fbaa904afc926b0445fceebc 100644 --- a/arch/arm/boot/dts/imx6q-dhcom-pdk2.dts +++ b/arch/arm/boot/dts/imx6q-dhcom-pdk2.dts @@ -55,7 +55,7 @@ #sound-dai-cells = <0>; clocks = <&clk_ext_audio_codec>; VDDA-supply = <®_3p3v>; - VDDIO-supply = <®_3p3v>; + VDDIO-supply = <&sw2_reg>; }; }; diff --git a/arch/arm/boot/dts/imx6q-dhcom-som.dtsi b/arch/arm/boot/dts/imx6q-dhcom-som.dtsi index bbba0671f0f414401aad0bbbc4193b7ad970dfb6..5b4d78999f80913fd285c22b4fb791af793f3384 100644 --- a/arch/arm/boot/dts/imx6q-dhcom-som.dtsi +++ b/arch/arm/boot/dts/imx6q-dhcom-som.dtsi @@ -205,7 +205,7 @@ }; rtc@56 { - compatible = "rv3029c2"; + compatible = "microcrystal,rv3029"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_rtc_hw300>; reg = <0x56>; diff --git a/arch/arm/boot/dts/imx6qdl-icore-1.5.dtsi b/arch/arm/boot/dts/imx6qdl-icore-1.5.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..d91d46b5898f17b5a7ca9a3573b0491979617038 --- /dev/null +++ b/arch/arm/boot/dts/imx6qdl-icore-1.5.dtsi @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018 Jacopo Mondi + */ + +#include "imx6qdl-icore.dtsi" + +&iomuxc { + pinctrl_enet: enetgrp { + fsl,pins = < + MX6QDL_PAD_ENET_CRS_DV__ENET_RX_EN 0x1b0b0 + MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0b0 + MX6QDL_PAD_ENET_TX_EN__ENET_TX_EN 0x1b0b0 + MX6QDL_PAD_ENET_RXD1__ENET_RX_DATA1 0x1b0b0 + MX6QDL_PAD_ENET_RXD0__ENET_RX_DATA0 0x1b0b0 + MX6QDL_PAD_ENET_TXD1__ENET_TX_DATA1 0x1b0b0 + MX6QDL_PAD_ENET_TXD0__ENET_TX_DATA0 0x1b0b0 + MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 + MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 + MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x1b0b0 + >; + }; +}; + +&fec { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_enet>; + phy-reset-gpios = <&gpio7 12 GPIO_ACTIVE_LOW>; + clocks = <&clks IMX6QDL_CLK_ENET>, + <&clks IMX6QDL_CLK_ENET>, + <&clks IMX6QDL_CLK_ENET_REF>; + phy-mode = "rmii"; + status = "okay"; +}; diff --git a/arch/arm/boot/dts/imx6qdl-phytec-phycore-som.dtsi b/arch/arm/boot/dts/imx6qdl-phytec-phycore-som.dtsi index 6486df3e2942da7e68e0ef94202b426ba43eb2a1..881cea0b61ba287078a69a0881e552bf7a84d33d 100644 --- a/arch/arm/boot/dts/imx6qdl-phytec-phycore-som.dtsi +++ b/arch/arm/boot/dts/imx6qdl-phytec-phycore-som.dtsi @@ -183,7 +183,6 @@ pinctrl-0 = <&pinctrl_usdhc4>; bus-width = <8>; non-removable; - vmmc-supply = <&vdd_emmc_1p8>; status = "disabled"; }; diff --git a/arch/arm/boot/dts/imx6qdl-zii-rdu2.dtsi b/arch/arm/boot/dts/imx6qdl-zii-rdu2.dtsi index 315d0e7615f335efd3e7e120ce2360b149de7acf..bc5f2de02d4335332fd562745eb1bc75bea1a3e7 100644 --- a/arch/arm/boot/dts/imx6qdl-zii-rdu2.dtsi +++ b/arch/arm/boot/dts/imx6qdl-zii-rdu2.dtsi @@ -657,7 +657,7 @@ pinctrl-0 = <&pinctrl_usdhc2>; bus-width = <4>; cd-gpios = <&gpio2 2 GPIO_ACTIVE_LOW>; - wp-gpios = <&gpio2 3 GPIO_ACTIVE_HIGH>; + disable-wp; vmmc-supply = <®_3p3v_sd>; vqmmc-supply = <®_3p3v>; no-1-8-v; @@ -670,7 +670,7 @@ pinctrl-0 = <&pinctrl_usdhc3>; bus-width = <4>; cd-gpios = <&gpio2 0 GPIO_ACTIVE_LOW>; - wp-gpios = <&gpio2 1 GPIO_ACTIVE_HIGH>; + disable-wp; vmmc-supply = <®_3p3v_sd>; vqmmc-supply = <®_3p3v>; no-1-8-v; @@ -804,6 +804,7 @@ &usbh1 { vbus-supply = <®_5p0v_main>; disable-over-current; + maximum-speed = "full-speed"; status = "okay"; }; @@ -1081,7 +1082,6 @@ MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x17059 MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x17059 MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x17059 - MX6QDL_PAD_NANDF_D3__GPIO2_IO03 0x40010040 MX6QDL_PAD_NANDF_D2__GPIO2_IO02 0x40010040 >; }; @@ -1094,7 +1094,6 @@ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 - MX6QDL_PAD_NANDF_D1__GPIO2_IO01 0x40010040 MX6QDL_PAD_NANDF_D0__GPIO2_IO00 0x40010040 >; diff --git a/arch/arm/boot/dts/imx7-colibri.dtsi b/arch/arm/boot/dts/imx7-colibri.dtsi index c1ed83131b495b7f2811c03f783a086ec48c7800..37aeba999be391cbad77417cecbf50d1e0966bc0 100644 --- a/arch/arm/boot/dts/imx7-colibri.dtsi +++ b/arch/arm/boot/dts/imx7-colibri.dtsi @@ -319,7 +319,6 @@ assigned-clock-rates = <400000000>; bus-width = <8>; fsl,tuning-step = <2>; - max-frequency = <100000000>; vmmc-supply = <®_module_3v3>; vqmmc-supply = <®_DCDC3>; non-removable; diff --git a/arch/arm/boot/dts/imx7s-colibri.dtsi b/arch/arm/boot/dts/imx7s-colibri.dtsi index 1fb1ec5d3d70717f08026f47e42d9bfee0c81ca8..6d16e32aed899122efcb4279581844714e05f0ca 100644 --- a/arch/arm/boot/dts/imx7s-colibri.dtsi +++ b/arch/arm/boot/dts/imx7s-colibri.dtsi @@ -49,3 +49,7 @@ reg = <0x80000000 0x10000000>; }; }; + +&gpmi { + status = "okay"; +}; diff --git a/arch/arm/boot/dts/iwg20d-q7-common.dtsi b/arch/arm/boot/dts/iwg20d-q7-common.dtsi index 5cae74eb6cddfb41c9d53317c24d389321af0750..a2c9a1e88c1a3a198296534c8efe8f411f1bedcd 100644 --- a/arch/arm/boot/dts/iwg20d-q7-common.dtsi +++ b/arch/arm/boot/dts/iwg20d-q7-common.dtsi @@ -87,7 +87,7 @@ regulator-min-microvolt = <1800000>; regulator-max-microvolt = <3300000>; - gpios = <&gpio2 30 GPIO_ACTIVE_LOW>; + gpios = <&gpio2 30 GPIO_ACTIVE_HIGH>; gpios-states = <1>; states = <3300000 1 1800000 0>; diff --git a/arch/arm/boot/dts/logicpd-som-lv-baseboard.dtsi b/arch/arm/boot/dts/logicpd-som-lv-baseboard.dtsi index 4990ed90dcea44020002b7de85c951d8396acfc5..3e39b9a1f35d02b9d87aa9d0b9840a65b75157bc 100644 --- a/arch/arm/boot/dts/logicpd-som-lv-baseboard.dtsi +++ b/arch/arm/boot/dts/logicpd-som-lv-baseboard.dtsi @@ -153,7 +153,7 @@ pinctrl-names = "default"; pinctrl-0 = <&mmc1_pins>; wp-gpios = <&gpio4 30 GPIO_ACTIVE_HIGH>; /* gpio_126 */ - cd-gpios = <&gpio4 14 IRQ_TYPE_LEVEL_LOW>; /* gpio_110 */ + cd-gpios = <&gpio4 14 GPIO_ACTIVE_LOW>; /* gpio_110 */ vmmc-supply = <&vmmc1>; bus-width = <4>; cap-power-off-card; diff --git a/arch/arm/boot/dts/logicpd-som-lv.dtsi b/arch/arm/boot/dts/logicpd-som-lv.dtsi index 98b682a8080cc334b40f44cc643f15b191e7a336..c5d54c4d374769bc0826aa614f8b8159fab42521 100644 --- a/arch/arm/boot/dts/logicpd-som-lv.dtsi +++ b/arch/arm/boot/dts/logicpd-som-lv.dtsi @@ -232,6 +232,20 @@ >; }; + i2c2_pins: pinmux_i2c2_pins { + pinctrl-single,pins = < + OMAP3_CORE1_IOPAD(0x21be, PIN_INPUT | MUX_MODE0) /* i2c2_scl */ + OMAP3_CORE1_IOPAD(0x21c0, PIN_INPUT | MUX_MODE0) /* i2c2_sda */ + >; + }; + + i2c3_pins: pinmux_i2c3_pins { + pinctrl-single,pins = < + OMAP3_CORE1_IOPAD(0x21c2, PIN_INPUT | MUX_MODE0) /* i2c3_scl */ + OMAP3_CORE1_IOPAD(0x21c4, PIN_INPUT | MUX_MODE0) /* i2c3_sda */ + >; + }; + tsc2004_pins: pinmux_tsc2004_pins { pinctrl-single,pins = < OMAP3_CORE1_IOPAD(0x2186, PIN_INPUT | MUX_MODE4) /* mcbsp4_dr.gpio_153 */ @@ -253,18 +267,6 @@ OMAP3_WKUP_IOPAD(0x2a0c, PIN_OUTPUT | MUX_MODE4) /* sys_boot1.gpio_3 */ >; }; - i2c2_pins: pinmux_i2c2_pins { - pinctrl-single,pins = < - OMAP3_CORE1_IOPAD(0x21be, PIN_INPUT | MUX_MODE0) /* i2c2_scl */ - OMAP3_CORE1_IOPAD(0x21c0, PIN_INPUT | MUX_MODE0) /* i2c2_sda */ - >; - }; - i2c3_pins: pinmux_i2c3_pins { - pinctrl-single,pins = < - OMAP3_CORE1_IOPAD(0x21c2, PIN_INPUT | MUX_MODE0) /* i2c3_scl */ - OMAP3_CORE1_IOPAD(0x21c4, PIN_INPUT | MUX_MODE0) /* i2c3_sda */ - >; - }; }; &omap3_pmx_core2 { diff --git a/arch/arm/boot/dts/lpc3250-phy3250.dts b/arch/arm/boot/dts/lpc3250-phy3250.dts index 1e1c2f517a82b582b3a2e73fa7cf7d299bfc128b..ffcf78631b226c6635334c50c25560640b51d788 100644 --- a/arch/arm/boot/dts/lpc3250-phy3250.dts +++ b/arch/arm/boot/dts/lpc3250-phy3250.dts @@ -49,8 +49,8 @@ sd_reg: regulator@2 { compatible = "regulator-fixed"; regulator-name = "sd_reg"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; gpio = <&gpio 5 5 0>; enable-active-high; }; diff --git a/arch/arm/boot/dts/lpc32xx.dtsi b/arch/arm/boot/dts/lpc32xx.dtsi index ed0d6fb20122a4e3c50ac17685cf0617467ea6d5..9ad3df11db0d868d0c591e2fa947ee2f71aa7a8a 100644 --- a/arch/arm/boot/dts/lpc32xx.dtsi +++ b/arch/arm/boot/dts/lpc32xx.dtsi @@ -139,11 +139,11 @@ }; clcd: clcd@31040000 { - compatible = "arm,pl110", "arm,primecell"; + compatible = "arm,pl111", "arm,primecell"; reg = <0x31040000 0x1000>; interrupts = <14 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&clk LPC32XX_CLK_LCD>; - clock-names = "apb_pclk"; + clocks = <&clk LPC32XX_CLK_LCD>, <&clk LPC32XX_CLK_LCD>; + clock-names = "clcdclk", "apb_pclk"; status = "disabled"; }; @@ -462,7 +462,9 @@ key: key@40050000 { compatible = "nxp,lpc3220-key"; reg = <0x40050000 0x1000>; - interrupts = <54 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk LPC32XX_CLK_KEY>; + interrupt-parent = <&sic1>; + interrupts = <22 IRQ_TYPE_LEVEL_HIGH>; status = "disabled"; }; diff --git a/arch/arm/boot/dts/ls1021a-twr.dts b/arch/arm/boot/dts/ls1021a-twr.dts index f0c949d748331939be497ddb4d93c2364a85347e..ec5afad3efd8e065bc4534f0c0f0f0b4b62bdbfd 100644 --- a/arch/arm/boot/dts/ls1021a-twr.dts +++ b/arch/arm/boot/dts/ls1021a-twr.dts @@ -143,7 +143,7 @@ }; &enet0 { - tbi-handle = <&tbi1>; + tbi-handle = <&tbi0>; phy-handle = <&sgmii_phy2>; phy-connection-type = "sgmii"; status = "okay"; @@ -222,6 +222,13 @@ sgmii_phy2: ethernet-phy@2 { reg = <0x2>; }; + tbi0: tbi-phy@1f { + reg = <0x1f>; + device_type = "tbi-phy"; + }; +}; + +&mdio1 { tbi1: tbi-phy@1f { reg = <0x1f>; device_type = "tbi-phy"; diff --git a/arch/arm/boot/dts/ls1021a.dtsi b/arch/arm/boot/dts/ls1021a.dtsi index f18490548c785eed2b4909bc6f8d258135e0ebba..074b4ec520c638a8f5b1e5c63adf5825b150b247 100644 --- a/arch/arm/boot/dts/ls1021a.dtsi +++ b/arch/arm/boot/dts/ls1021a.dtsi @@ -592,6 +592,15 @@ <0x0 0x2d10030 0x0 0x4>; }; + mdio1: mdio@2d64000 { + compatible = "gianfar"; + device_type = "mdio"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x2d64000 0x0 0x4000>, + <0x0 0x2d50030 0x0 0x4>; + }; + ptp_clock@2d10e00 { compatible = "fsl,etsec-ptp"; reg = <0x0 0x2d10e00 0x0 0xb0>; diff --git a/arch/arm/boot/dts/meson8.dtsi b/arch/arm/boot/dts/meson8.dtsi index 7162e0ca05b0a5d477fd1369f127e8bef67e8413..ba78170e7027480f37506e76736444cda339d044 100644 --- a/arch/arm/boot/dts/meson8.dtsi +++ b/arch/arm/boot/dts/meson8.dtsi @@ -139,7 +139,7 @@ &aobus { pmu: pmu@e0 { compatible = "amlogic,meson8-pmu", "syscon"; - reg = <0xe0 0x8>; + reg = <0xe0 0x18>; }; pinctrl_aobus: pinctrl@84 { diff --git a/arch/arm/boot/dts/omap3-n900.dts b/arch/arm/boot/dts/omap3-n900.dts index 182a53991c901a387317db9710bceb9d0d1d5bb6..37785e7d1238cddf913c8e4b1fca45b8f8c87f80 100644 --- a/arch/arm/boot/dts/omap3-n900.dts +++ b/arch/arm/boot/dts/omap3-n900.dts @@ -158,6 +158,12 @@ pwms = <&pwm9 0 26316 0>; /* 38000 Hz */ }; + rom_rng: rng { + compatible = "nokia,n900-rom-rng"; + clocks = <&rng_ick>; + clock-names = "ick"; + }; + /* controlled (enabled/disabled) directly by bcm2048 and wl1251 */ vctcxo: vctcxo { compatible = "fixed-clock"; diff --git a/arch/arm/boot/dts/r8a7743.dtsi b/arch/arm/boot/dts/r8a7743.dtsi index 24715f74ae08c7c2139ac505821041efea05e564..5015e2273d82f79e734a31bba28076eff7d8f289 100644 --- a/arch/arm/boot/dts/r8a7743.dtsi +++ b/arch/arm/boot/dts/r8a7743.dtsi @@ -565,9 +565,7 @@ /* doesn't need pinmux */ #address-cells = <1>; #size-cells = <0>; - compatible = "renesas,iic-r8a7743", - "renesas,rcar-gen2-iic", - "renesas,rmobile-iic"; + compatible = "renesas,iic-r8a7743"; reg = <0 0xe60b0000 0 0x425>; interrupts = ; clocks = <&cpg CPG_MOD 926>; diff --git a/arch/arm/boot/dts/r8a7779.dtsi b/arch/arm/boot/dts/r8a7779.dtsi index 03919714645ae56d22c8e9e59a6836beea8f74f8..f1c9b2bc542c5be92f2ff4f866e46ba04bb472d7 100644 --- a/arch/arm/boot/dts/r8a7779.dtsi +++ b/arch/arm/boot/dts/r8a7779.dtsi @@ -68,6 +68,14 @@ <0xf0000100 0x100>; }; + timer@f0000200 { + compatible = "arm,cortex-a9-global-timer"; + reg = <0xf0000200 0x100>; + interrupts = ; + clocks = <&cpg_clocks R8A7779_CLK_ZS>; + }; + timer@f0000600 { compatible = "arm,cortex-a9-twd-timer"; reg = <0xf0000600 0x20>; diff --git a/arch/arm/boot/dts/sama5d3.dtsi b/arch/arm/boot/dts/sama5d3.dtsi index 1408fa4a62e4410708804f06826cbf3a7a691065..d01464bfd5fc464f4ed9eae1d45b9d9bcd5126b6 100644 --- a/arch/arm/boot/dts/sama5d3.dtsi +++ b/arch/arm/boot/dts/sama5d3.dtsi @@ -1187,49 +1187,49 @@ usart0_clk: usart0_clk { #clock-cells = <0>; reg = <12>; - atmel,clk-output-range = <0 66000000>; + atmel,clk-output-range = <0 83000000>; }; usart1_clk: usart1_clk { #clock-cells = <0>; reg = <13>; - atmel,clk-output-range = <0 66000000>; + atmel,clk-output-range = <0 83000000>; }; usart2_clk: usart2_clk { #clock-cells = <0>; reg = <14>; - atmel,clk-output-range = <0 66000000>; + atmel,clk-output-range = <0 83000000>; }; usart3_clk: usart3_clk { #clock-cells = <0>; reg = <15>; - atmel,clk-output-range = <0 66000000>; + atmel,clk-output-range = <0 83000000>; }; uart0_clk: uart0_clk { #clock-cells = <0>; reg = <16>; - atmel,clk-output-range = <0 66000000>; + atmel,clk-output-range = <0 83000000>; }; twi0_clk: twi0_clk { reg = <18>; #clock-cells = <0>; - atmel,clk-output-range = <0 16625000>; + atmel,clk-output-range = <0 41500000>; }; twi1_clk: twi1_clk { #clock-cells = <0>; reg = <19>; - atmel,clk-output-range = <0 16625000>; + atmel,clk-output-range = <0 41500000>; }; twi2_clk: twi2_clk { #clock-cells = <0>; reg = <20>; - atmel,clk-output-range = <0 16625000>; + atmel,clk-output-range = <0 41500000>; }; mci0_clk: mci0_clk { @@ -1245,19 +1245,19 @@ spi0_clk: spi0_clk { #clock-cells = <0>; reg = <24>; - atmel,clk-output-range = <0 133000000>; + atmel,clk-output-range = <0 166000000>; }; spi1_clk: spi1_clk { #clock-cells = <0>; reg = <25>; - atmel,clk-output-range = <0 133000000>; + atmel,clk-output-range = <0 166000000>; }; tcb0_clk: tcb0_clk { #clock-cells = <0>; reg = <26>; - atmel,clk-output-range = <0 133000000>; + atmel,clk-output-range = <0 166000000>; }; pwm_clk: pwm_clk { @@ -1268,7 +1268,7 @@ adc_clk: adc_clk { #clock-cells = <0>; reg = <29>; - atmel,clk-output-range = <0 66000000>; + atmel,clk-output-range = <0 83000000>; }; dma0_clk: dma0_clk { @@ -1299,13 +1299,13 @@ ssc0_clk: ssc0_clk { #clock-cells = <0>; reg = <38>; - atmel,clk-output-range = <0 66000000>; + atmel,clk-output-range = <0 83000000>; }; ssc1_clk: ssc1_clk { #clock-cells = <0>; reg = <39>; - atmel,clk-output-range = <0 66000000>; + atmel,clk-output-range = <0 83000000>; }; sha_clk: sha_clk { diff --git a/arch/arm/boot/dts/sama5d3_can.dtsi b/arch/arm/boot/dts/sama5d3_can.dtsi index c5a3772741bf610b300a10147e76bcc882b973fb..0fac79f75c06ccd3008673a0147a3692656f095f 100644 --- a/arch/arm/boot/dts/sama5d3_can.dtsi +++ b/arch/arm/boot/dts/sama5d3_can.dtsi @@ -37,13 +37,13 @@ can0_clk: can0_clk { #clock-cells = <0>; reg = <40>; - atmel,clk-output-range = <0 66000000>; + atmel,clk-output-range = <0 83000000>; }; can1_clk: can1_clk { #clock-cells = <0>; reg = <41>; - atmel,clk-output-range = <0 66000000>; + atmel,clk-output-range = <0 83000000>; }; }; }; diff --git a/arch/arm/boot/dts/sama5d3_tcb1.dtsi b/arch/arm/boot/dts/sama5d3_tcb1.dtsi index cb30bdb1a9ca15b3361cf86e79cb0aaf2cc11360..7a68d355ff720c197deff706074c703d809109b0 100644 --- a/arch/arm/boot/dts/sama5d3_tcb1.dtsi +++ b/arch/arm/boot/dts/sama5d3_tcb1.dtsi @@ -23,6 +23,7 @@ tcb1_clk: tcb1_clk { #clock-cells = <0>; reg = <27>; + atmel,clk-output-range = <0 166000000>; }; }; }; diff --git a/arch/arm/boot/dts/sama5d3_uart.dtsi b/arch/arm/boot/dts/sama5d3_uart.dtsi index f599f8a5f664f138bea4b956760debecdee34ca5..f6d301c16cb1ba4a76c8eca5d78181183c106b37 100644 --- a/arch/arm/boot/dts/sama5d3_uart.dtsi +++ b/arch/arm/boot/dts/sama5d3_uart.dtsi @@ -42,13 +42,13 @@ uart0_clk: uart0_clk { #clock-cells = <0>; reg = <16>; - atmel,clk-output-range = <0 66000000>; + atmel,clk-output-range = <0 83000000>; }; uart1_clk: uart1_clk { #clock-cells = <0>; reg = <17>; - atmel,clk-output-range = <0 66000000>; + atmel,clk-output-range = <0 83000000>; }; }; }; diff --git a/arch/arm/boot/dts/stihxxx-b2120.dtsi b/arch/arm/boot/dts/stihxxx-b2120.dtsi index 4dedfcb0fcb304c4968b82f815df31892e63f23a..ac42d3c6bda0b3285a3b22f59c8af85cb63cea7c 100644 --- a/arch/arm/boot/dts/stihxxx-b2120.dtsi +++ b/arch/arm/boot/dts/stihxxx-b2120.dtsi @@ -45,7 +45,7 @@ /* DAC */ format = "i2s"; mclk-fs = <256>; - frame-inversion = <1>; + frame-inversion; cpu { sound-dai = <&sti_uni_player2>; }; diff --git a/arch/arm/boot/dts/stm32f469-disco.dts b/arch/arm/boot/dts/stm32f469-disco.dts index 3ee768cb86fc96acf9f1e6a214e1348ead71395c..eea979ef5512fb91f6c42fff1d7f0bc1d73ccbae 100644 --- a/arch/arm/boot/dts/stm32f469-disco.dts +++ b/arch/arm/boot/dts/stm32f469-disco.dts @@ -75,6 +75,13 @@ regulator-max-microvolt = <3300000>; }; + vdd_dsi: vdd-dsi { + compatible = "regulator-fixed"; + regulator-name = "vdd_dsi"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + soc { dma-ranges = <0xc0000000 0x0 0x10000000>; }; @@ -154,6 +161,7 @@ compatible = "orisetech,otm8009a"; reg = <0>; /* dsi virtual channel (0..3) */ reset-gpios = <&gpioh 7 GPIO_ACTIVE_LOW>; + power-supply = <&vdd_dsi>; status = "okay"; port { diff --git a/arch/arm/boot/dts/stm32h743i-eval.dts b/arch/arm/boot/dts/stm32h743i-eval.dts index 3f8e0c4a998d0cec39c592bc8f7688e9327bf12b..5bf64e63cdf3544d23572d488917347700d610a6 100644 --- a/arch/arm/boot/dts/stm32h743i-eval.dts +++ b/arch/arm/boot/dts/stm32h743i-eval.dts @@ -79,6 +79,7 @@ }; &adc_12 { + vdda-supply = <&vdda>; vref-supply = <&vdda>; status = "okay"; adc1: adc@0 { diff --git a/arch/arm/boot/dts/sun8i-a23-a33.dtsi b/arch/arm/boot/dts/sun8i-a23-a33.dtsi index c16ffcc4db7da0831dc16a84e4c2fcb093c90987..1efad1a6bcfd993797cbf366c95a3994dac016ce 100644 --- a/arch/arm/boot/dts/sun8i-a23-a33.dtsi +++ b/arch/arm/boot/dts/sun8i-a23-a33.dtsi @@ -155,6 +155,21 @@ #dma-cells = <1>; }; + nfc: nand@1c03000 { + compatible = "allwinner,sun4i-a10-nand"; + reg = <0x01c03000 0x1000>; + interrupts = ; + clocks = <&ccu CLK_BUS_NAND>, <&ccu CLK_NAND>; + clock-names = "ahb", "mod"; + resets = <&ccu RST_BUS_NAND>; + reset-names = "ahb"; + pinctrl-names = "default"; + pinctrl-0 = <&nand_pins &nand_pins_cs0 &nand_pins_rb0>; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; + }; + mmc0: mmc@1c0f000 { compatible = "allwinner,sun7i-a20-mmc"; reg = <0x01c0f000 0x1000>; @@ -212,21 +227,6 @@ #size-cells = <0>; }; - nfc: nand@1c03000 { - compatible = "allwinner,sun4i-a10-nand"; - reg = <0x01c03000 0x1000>; - interrupts = ; - clocks = <&ccu CLK_BUS_NAND>, <&ccu CLK_NAND>; - clock-names = "ahb", "mod"; - resets = <&ccu RST_BUS_NAND>; - reset-names = "ahb"; - pinctrl-names = "default"; - pinctrl-0 = <&nand_pins &nand_pins_cs0 &nand_pins_rb0>; - status = "disabled"; - #address-cells = <1>; - #size-cells = <0>; - }; - usb_otg: usb@1c19000 { /* compatible gets set in SoC specific dtsi file */ reg = <0x01c19000 0x0400>; diff --git a/arch/arm/boot/dts/sun8i-a83t-cubietruck-plus.dts b/arch/arm/boot/dts/sun8i-a83t-cubietruck-plus.dts index e5f0645e53a7b3173b2dcf29cbdcd368ee49cd24..7e74ba83f8095eaee26a7308ddd78164761eb228 100644 --- a/arch/arm/boot/dts/sun8i-a83t-cubietruck-plus.dts +++ b/arch/arm/boot/dts/sun8i-a83t-cubietruck-plus.dts @@ -90,7 +90,7 @@ initial-mode = <1>; /* initialize in HUB mode */ disabled-ports = <1>; intn-gpios = <&pio 7 5 GPIO_ACTIVE_HIGH>; /* PH5 */ - reset-gpios = <&pio 4 16 GPIO_ACTIVE_HIGH>; /* PE16 */ + reset-gpios = <&pio 4 16 GPIO_ACTIVE_LOW>; /* PE16 */ connect-gpios = <&pio 4 17 GPIO_ACTIVE_HIGH>; /* PE17 */ refclk-frequency = <19200000>; }; diff --git a/arch/arm/boot/dts/sun8i-h3-beelink-x2.dts b/arch/arm/boot/dts/sun8i-h3-beelink-x2.dts index 25540b7694d590dea1c3c54c5453d4a7217d9f37..6523d81dd9c49613803f3520516a81325357c373 100644 --- a/arch/arm/boot/dts/sun8i-h3-beelink-x2.dts +++ b/arch/arm/boot/dts/sun8i-h3-beelink-x2.dts @@ -90,6 +90,8 @@ wifi_pwrseq: wifi_pwrseq { compatible = "mmc-pwrseq-simple"; reset-gpios = <&r_pio 0 7 GPIO_ACTIVE_LOW>; /* PL7 */ + clocks = <&rtc 1>; + clock-names = "ext_clock"; }; sound_spdif { @@ -155,6 +157,8 @@ &mmc1 { vmmc-supply = <®_vcc3v3>; + vqmmc-supply = <®_vcc3v3>; + mmc-pwrseq = <&wifi_pwrseq>; bus-width = <4>; non-removable; status = "okay"; diff --git a/arch/arm/boot/dts/sun8i-h3.dtsi b/arch/arm/boot/dts/sun8i-h3.dtsi index 9233ba30a857c7b2f77da960da00aab19b0eaff2..11172fbdc03aac3656e83dce16990b7b09324688 100644 --- a/arch/arm/boot/dts/sun8i-h3.dtsi +++ b/arch/arm/boot/dts/sun8i-h3.dtsi @@ -80,7 +80,7 @@ #cooling-cells = <2>; }; - cpu@1 { + cpu1: cpu@1 { compatible = "arm,cortex-a7"; device_type = "cpu"; reg = <1>; @@ -90,7 +90,7 @@ #cooling-cells = <2>; }; - cpu@2 { + cpu2: cpu@2 { compatible = "arm,cortex-a7"; device_type = "cpu"; reg = <2>; @@ -100,7 +100,7 @@ #cooling-cells = <2>; }; - cpu@3 { + cpu3: cpu@3 { compatible = "arm,cortex-a7"; device_type = "cpu"; reg = <3>; @@ -111,6 +111,15 @@ }; }; + pmu { + compatible = "arm,cortex-a7-pmu"; + interrupts = , + , + , + ; + interrupt-affinity = <&cpu0>, <&cpu1>, <&cpu2>, <&cpu3>; + }; + timer { compatible = "arm,armv7-timer"; interrupts = , diff --git a/arch/arm/boot/dts/sun9i-a80-optimus.dts b/arch/arm/boot/dts/sun9i-a80-optimus.dts index 58a199b0e4943424443043a72f1e430279fa8ea3..d1e58a6a434320141f4e2f811571dd0203a6c8b6 100644 --- a/arch/arm/boot/dts/sun9i-a80-optimus.dts +++ b/arch/arm/boot/dts/sun9i-a80-optimus.dts @@ -82,7 +82,7 @@ reg_usb1_vbus: usb1-vbus { compatible = "regulator-fixed"; - pinctrl-names = "default"; + regulator-name = "usb1-vbus"; regulator-min-microvolt = <5000000>; regulator-max-microvolt = <5000000>; enable-active-high; @@ -91,7 +91,7 @@ reg_usb3_vbus: usb3-vbus { compatible = "regulator-fixed"; - pinctrl-names = "default"; + regulator-name = "usb3-vbus"; regulator-min-microvolt = <5000000>; regulator-max-microvolt = <5000000>; enable-active-high; diff --git a/arch/arm/common/mcpm_entry.c b/arch/arm/common/mcpm_entry.c index ad574d20415c219f408bb494a1bec3de6a7fcbac..1b1b82b37ce035f97e17e40be6d81d913ee27e61 100644 --- a/arch/arm/common/mcpm_entry.c +++ b/arch/arm/common/mcpm_entry.c @@ -381,7 +381,7 @@ static int __init nocache_trampoline(unsigned long _arg) unsigned int cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); phys_reset_t phys_reset; - mcpm_set_entry_vector(cpu, cluster, cpu_resume); + mcpm_set_entry_vector(cpu, cluster, cpu_resume_no_hyp); setup_mm_for_reboot(); __mcpm_cpu_going_down(cpu, cluster); diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig index 6aa7046fb91ffeba3e67ab481e730da1ec8e8fa4..bd6440f234939eb450cd47af85e292429eaf0821 100644 --- a/arch/arm/configs/qcom_defconfig +++ b/arch/arm/configs/qcom_defconfig @@ -207,6 +207,7 @@ CONFIG_MSM_MMCC_8974=y CONFIG_MSM_IOMMU=y CONFIG_HWSPINLOCK=y CONFIG_HWSPINLOCK_QCOM=y +CONFIG_MAILBOX=y CONFIG_REMOTEPROC=y CONFIG_QCOM_ADSP_PIL=y CONFIG_QCOM_Q6V5_PIL=y diff --git a/arch/arm/configs/vendor/bengal-perf_defconfig b/arch/arm/configs/vendor/bengal-perf_defconfig index dc26d22134e00c0fb9035e477f67d8aa41c23904..76befafd53f41bcb852eac9ca403f3b728979813 100644 --- a/arch/arm/configs/vendor/bengal-perf_defconfig +++ b/arch/arm/configs/vendor/bengal-perf_defconfig @@ -127,7 +127,6 @@ CONFIG_IPV6_VTI=y CONFIG_IPV6_MULTIPLE_TABLES=y CONFIG_IPV6_SUBTREES=y CONFIG_NETFILTER=y -# CONFIG_BRIDGE_NETFILTER is not set CONFIG_NF_CONNTRACK=y CONFIG_NF_CONNTRACK_SECMARK=y CONFIG_NF_CONNTRACK_EVENTS=y @@ -379,7 +378,6 @@ CONFIG_VIDEO_FIXED_MINOR_RANGES=y CONFIG_V4L_PLATFORM_DRIVERS=y CONFIG_VIDEO_V4L2_VIDEOBUF2_CORE=y CONFIG_DRM=y -# CONFIG_DRM_MSM is not set CONFIG_BACKLIGHT_LCD_SUPPORT=y CONFIG_BACKLIGHT_CLASS_DEVICE=y CONFIG_LOGO=y diff --git a/arch/arm/configs/vendor/bengal_defconfig b/arch/arm/configs/vendor/bengal_defconfig index 61d4f2d06ef199611fedc31eb47cc65deec90b1c..803b096f7f855d01a386427c1793e3e1b08c7bed 100644 --- a/arch/arm/configs/vendor/bengal_defconfig +++ b/arch/arm/configs/vendor/bengal_defconfig @@ -410,7 +410,6 @@ CONFIG_SOC_CAMERA=y CONFIG_SOC_CAMERA_PLATFORM=y CONFIG_VIDEO_V4L2_VIDEOBUF2_CORE=y CONFIG_DRM=y -# CONFIG_DRM_MSM is not set CONFIG_BACKLIGHT_LCD_SUPPORT=y CONFIG_BACKLIGHT_CLASS_DEVICE=y CONFIG_BACKLIGHT_GENERIC=m diff --git a/arch/arm/include/asm/kvm_emulate.h b/arch/arm/include/asm/kvm_emulate.h index 77121b713bef3adbc4b2c77c559dab1886dec96b..7d2ca035d6c8fe58235e2b0ad3e3330c8efcf0c1 100644 --- a/arch/arm/include/asm/kvm_emulate.h +++ b/arch/arm/include/asm/kvm_emulate.h @@ -26,13 +26,25 @@ #include /* arm64 compatibility macros */ +#define PSR_AA32_MODE_FIQ FIQ_MODE +#define PSR_AA32_MODE_SVC SVC_MODE #define PSR_AA32_MODE_ABT ABT_MODE #define PSR_AA32_MODE_UND UND_MODE #define PSR_AA32_T_BIT PSR_T_BIT +#define PSR_AA32_F_BIT PSR_F_BIT #define PSR_AA32_I_BIT PSR_I_BIT #define PSR_AA32_A_BIT PSR_A_BIT #define PSR_AA32_E_BIT PSR_E_BIT #define PSR_AA32_IT_MASK PSR_IT_MASK +#define PSR_AA32_GE_MASK 0x000f0000 +#define PSR_AA32_DIT_BIT 0x00200000 +#define PSR_AA32_PAN_BIT 0x00400000 +#define PSR_AA32_SSBS_BIT 0x00800000 +#define PSR_AA32_Q_BIT PSR_Q_BIT +#define PSR_AA32_V_BIT PSR_V_BIT +#define PSR_AA32_C_BIT PSR_C_BIT +#define PSR_AA32_Z_BIT PSR_Z_BIT +#define PSR_AA32_N_BIT PSR_N_BIT unsigned long *vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num); @@ -53,6 +65,11 @@ static inline void vcpu_write_spsr(struct kvm_vcpu *vcpu, unsigned long v) *__vcpu_spsr(vcpu) = v; } +static inline unsigned long host_spsr_to_spsr32(unsigned long spsr) +{ + return spsr; +} + static inline unsigned long vcpu_get_reg(struct kvm_vcpu *vcpu, u8 reg_num) { @@ -189,6 +206,11 @@ static inline bool kvm_vcpu_dabt_issext(struct kvm_vcpu *vcpu) return kvm_vcpu_get_hsr(vcpu) & HSR_SSE; } +static inline bool kvm_vcpu_dabt_issf(const struct kvm_vcpu *vcpu) +{ + return false; +} + static inline int kvm_vcpu_dabt_get_rd(struct kvm_vcpu *vcpu) { return (kvm_vcpu_get_hsr(vcpu) & HSR_SRT_MASK) >> HSR_SRT_SHIFT; diff --git a/arch/arm/include/asm/kvm_mmio.h b/arch/arm/include/asm/kvm_mmio.h index f3a7de71f515016bf9ab0b2525bfe622794975a7..848339d76f9a169b332e7b2535f3d877f968b8a1 100644 --- a/arch/arm/include/asm/kvm_mmio.h +++ b/arch/arm/include/asm/kvm_mmio.h @@ -26,6 +26,8 @@ struct kvm_decode { unsigned long rt; bool sign_extend; + /* Not used on 32-bit arm */ + bool sixty_four; }; void kvm_mmio_write_buf(void *buf, unsigned int len, unsigned long data); diff --git a/arch/arm/include/asm/suspend.h b/arch/arm/include/asm/suspend.h index 452bbdcbcc835fc838b4c957f2da3e6dafd2fdc9..506314265c6f1a24e50641dbbea1d41367d322d5 100644 --- a/arch/arm/include/asm/suspend.h +++ b/arch/arm/include/asm/suspend.h @@ -10,6 +10,7 @@ struct sleep_save_sp { }; extern void cpu_resume(void); +extern void cpu_resume_no_hyp(void); extern void cpu_resume_arm(void); extern int cpu_suspend(unsigned long, int (*)(unsigned long)); diff --git a/arch/arm/kernel/head-nommu.S b/arch/arm/kernel/head-nommu.S index 326a97aa3ea0cfaf390d06a267cbcdc280d18164..22efcf48604cd7aac20dc2c4b67631926000b70c 100644 --- a/arch/arm/kernel/head-nommu.S +++ b/arch/arm/kernel/head-nommu.S @@ -441,8 +441,8 @@ M_CLASS(str r6, [r12, #PMSAv8_RLAR_A(3)]) str r5, [r12, #PMSAv8_RBAR_A(0)] str r6, [r12, #PMSAv8_RLAR_A(0)] #else - mcr p15, 0, r5, c6, c10, 1 @ PRBAR4 - mcr p15, 0, r6, c6, c10, 2 @ PRLAR4 + mcr p15, 0, r5, c6, c10, 0 @ PRBAR4 + mcr p15, 0, r6, c6, c10, 1 @ PRLAR4 #endif #endif ret lr diff --git a/arch/arm/kernel/hyp-stub.S b/arch/arm/kernel/hyp-stub.S index 60146e32619a5912bf12b5277397f2e19213b2a8..83e463c05dcdb456e4bfb0381e2e31ac9f6be07c 100644 --- a/arch/arm/kernel/hyp-stub.S +++ b/arch/arm/kernel/hyp-stub.S @@ -159,10 +159,9 @@ ARM_BE8(orr r7, r7, #(1 << 25)) @ HSCTLR.EE #if !defined(ZIMAGE) && defined(CONFIG_ARM_ARCH_TIMER) @ make CNTP_* and CNTPCT accessible from PL1 mrc p15, 0, r7, c0, c1, 1 @ ID_PFR1 - lsr r7, #16 - and r7, #0xf - cmp r7, #1 - bne 1f + ubfx r7, r7, #16, #4 + teq r7, #0 + beq 1f mrc p15, 4, r7, c14, c1, 0 @ CNTHCTL orr r7, r7, #3 @ PL1PCEN | PL1PCTEN mcr p15, 4, r7, c14, c1, 0 @ CNTHCTL @@ -180,8 +179,8 @@ ARM_BE8(orr r7, r7, #(1 << 25)) @ HSCTLR.EE @ Check whether GICv3 system registers are available mrc p15, 0, r7, c0, c1, 1 @ ID_PFR1 ubfx r7, r7, #28, #4 - cmp r7, #1 - bne 2f + teq r7, #0 + beq 2f @ Enable system register accesses mrc p15, 4, r7, c12, c9, 5 @ ICC_HSRE diff --git a/arch/arm/kernel/sleep.S b/arch/arm/kernel/sleep.S index a8257fc9cf2a908c4dd2b5f19c819b4c7fc40588..5dc8b80bb69383643eddec5ba62164e0458b4512 100644 --- a/arch/arm/kernel/sleep.S +++ b/arch/arm/kernel/sleep.S @@ -120,6 +120,14 @@ ENDPROC(cpu_resume_after_mmu) .text .align +#ifdef CONFIG_MCPM + .arm +THUMB( .thumb ) +ENTRY(cpu_resume_no_hyp) +ARM_BE8(setend be) @ ensure we are in BE mode + b no_hyp +#endif + #ifdef CONFIG_MMU .arm ENTRY(cpu_resume_arm) @@ -135,6 +143,7 @@ ARM_BE8(setend be) @ ensure we are in BE mode bl __hyp_stub_install_secondary #endif safe_svcmode_maskall r1 +no_hyp: mov r1, #0 ALT_SMP(mrc p15, 0, r0, c0, c0, 5) ALT_UP_B(1f) @@ -163,6 +172,9 @@ ENDPROC(cpu_resume) #ifdef CONFIG_MMU ENDPROC(cpu_resume_arm) +#endif +#ifdef CONFIG_MCPM +ENDPROC(cpu_resume_no_hyp) #endif .align 2 diff --git a/arch/arm/kernel/vdso.c b/arch/arm/kernel/vdso.c index f4dd7f9663c10a704d3858de6beb538e5023cf78..e8cda5e02b4ea7bd6c91861a184c848be7e4fee7 100644 --- a/arch/arm/kernel/vdso.c +++ b/arch/arm/kernel/vdso.c @@ -205,7 +205,6 @@ static int __init vdso_init(void) } text_pages = (vdso_end - vdso_start) >> PAGE_SHIFT; - pr_debug("vdso: %i text pages at base %p\n", text_pages, vdso_start); /* Allocate the VDSO text pagelist */ vdso_text_pagelist = kcalloc(text_pages, sizeof(struct page *), diff --git a/arch/arm/lib/copy_from_user.S b/arch/arm/lib/copy_from_user.S index 6709a8d33963b679b0a0f6ec683e3291a7f07a71..f1e34f16cfab9ad98db4628c78bbf558544cebc5 100644 --- a/arch/arm/lib/copy_from_user.S +++ b/arch/arm/lib/copy_from_user.S @@ -100,7 +100,7 @@ ENTRY(arm_copy_from_user) ENDPROC(arm_copy_from_user) - .pushsection .fixup,"ax" + .pushsection .text.fixup,"ax" .align 0 copy_abort_preamble ldmfd sp!, {r1, r2, r3} diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile index bae179af21f6dc211ea656264d6aa10192de84f6..e9cfe8e86f332c0e081bfb8002cd0c0e112b8197 100644 --- a/arch/arm/mach-imx/Makefile +++ b/arch/arm/mach-imx/Makefile @@ -89,6 +89,8 @@ AFLAGS_suspend-imx6.o :=-Wa,-march=armv7-a obj-$(CONFIG_SOC_IMX6) += suspend-imx6.o obj-$(CONFIG_SOC_IMX53) += suspend-imx53.o endif +AFLAGS_resume-imx6.o :=-Wa,-march=armv7-a +obj-$(CONFIG_SOC_IMX6) += resume-imx6.o obj-$(CONFIG_SOC_IMX6) += pm-imx6.o obj-$(CONFIG_SOC_IMX1) += mach-imx1.o diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h index 423dd76bb6b812e408f106e2367386820b13a8a4..9728e39cd18293f387f9eb690481c3a9fb876f29 100644 --- a/arch/arm/mach-imx/common.h +++ b/arch/arm/mach-imx/common.h @@ -103,17 +103,17 @@ void imx_cpu_die(unsigned int cpu); int imx_cpu_kill(unsigned int cpu); #ifdef CONFIG_SUSPEND -void v7_cpu_resume(void); void imx53_suspend(void __iomem *ocram_vbase); extern const u32 imx53_suspend_sz; void imx6_suspend(void __iomem *ocram_vbase); #else -static inline void v7_cpu_resume(void) {} static inline void imx53_suspend(void __iomem *ocram_vbase) {} static const u32 imx53_suspend_sz; static inline void imx6_suspend(void __iomem *ocram_vbase) {} #endif +void v7_cpu_resume(void); + void imx6_pm_ccm_init(const char *ccm_compat); void imx6q_pm_init(void); void imx6dl_pm_init(void); diff --git a/arch/arm/mach-imx/resume-imx6.S b/arch/arm/mach-imx/resume-imx6.S new file mode 100644 index 0000000000000000000000000000000000000000..5bd1ba7ef15b61cb98d1bd2d3af4e85acc5ec632 --- /dev/null +++ b/arch/arm/mach-imx/resume-imx6.S @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright 2014 Freescale Semiconductor, Inc. + */ + +#include +#include +#include +#include +#include "hardware.h" + +/* + * The following code must assume it is running from physical address + * where absolute virtual addresses to the data section have to be + * turned into relative ones. + */ + +ENTRY(v7_cpu_resume) + bl v7_invalidate_l1 +#ifdef CONFIG_CACHE_L2X0 + bl l2c310_early_resume +#endif + b cpu_resume +ENDPROC(v7_cpu_resume) diff --git a/arch/arm/mach-imx/suspend-imx6.S b/arch/arm/mach-imx/suspend-imx6.S index 76ee2ceec8d546d0ca3f51aebcb8f40509213ba6..7d84b617af4815817d3ffbb60b8c067bcbb8a41b 100644 --- a/arch/arm/mach-imx/suspend-imx6.S +++ b/arch/arm/mach-imx/suspend-imx6.S @@ -333,17 +333,3 @@ resume: ret lr ENDPROC(imx6_suspend) - -/* - * The following code must assume it is running from physical address - * where absolute virtual addresses to the data section have to be - * turned into relative ones. - */ - -ENTRY(v7_cpu_resume) - bl v7_invalidate_l1 -#ifdef CONFIG_CACHE_L2X0 - bl l2c310_early_resume -#endif - b cpu_resume -ENDPROC(v7_cpu_resume) diff --git a/arch/arm/mach-npcm/Kconfig b/arch/arm/mach-npcm/Kconfig index 684c9c9a32bd6f9ef0fffadf6661c72917e27a01..1d17515deb4e5bfeb6317059fc080855a9d24bd3 100644 --- a/arch/arm/mach-npcm/Kconfig +++ b/arch/arm/mach-npcm/Kconfig @@ -10,7 +10,7 @@ config ARCH_NPCM7XX depends on ARCH_MULTI_V7 select PINCTRL_NPCM7XX select NPCM7XX_TIMER - select ARCH_REQUIRE_GPIOLIB + select GPIOLIB select CACHE_L2X0 select ARM_GIC select HAVE_ARM_TWD if SMP diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c index ec3789ba17b8b7a73e88e2724dc48c54fba55e73..a8269f0a87ced398f108977ebdf1529235825641 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -2430,7 +2430,7 @@ static void _setup_iclk_autoidle(struct omap_hwmod *oh) */ static int _setup_reset(struct omap_hwmod *oh) { - int r; + int r = 0; if (oh->_state != _HWMOD_STATE_INITIALIZED) return -EINVAL; diff --git a/arch/arm/mach-omap2/pdata-quirks.c b/arch/arm/mach-omap2/pdata-quirks.c index dae726228770421bea84353400dda87e390fcb0e..b57faa2310a2e8bc455bffca2c2d8322b7f59036 100644 --- a/arch/arm/mach-omap2/pdata-quirks.c +++ b/arch/arm/mach-omap2/pdata-quirks.c @@ -263,14 +263,6 @@ static void __init am3517_evm_legacy_init(void) am35xx_emac_reset(); } -static struct platform_device omap3_rom_rng_device = { - .name = "omap3-rom-rng", - .id = -1, - .dev = { - .platform_data = rx51_secure_rng_call, - }, -}; - static void __init nokia_n900_legacy_init(void) { hsmmc2_internal_input_clk(); @@ -286,9 +278,6 @@ static void __init nokia_n900_legacy_init(void) pr_warn("RX-51: Not enabling ARM errata 430973 workaround\n"); pr_warn("Thumb binaries may crash randomly without this workaround\n"); } - - pr_info("RX-51: Registering OMAP3 HWRNG device\n"); - platform_device_register(&omap3_rom_rng_device); } } @@ -466,6 +455,7 @@ static struct of_dev_auxdata omap_auxdata_lookup[] = { OF_DEV_AUXDATA("ti,davinci_mdio", 0x5c030000, "davinci_mdio.0", NULL), OF_DEV_AUXDATA("ti,am3517-emac", 0x5c000000, "davinci_emac.0", &am35xx_emac_pdata), + OF_DEV_AUXDATA("nokia,n900-rom-rng", 0, NULL, rx51_secure_rng_call), /* McBSP modules with sidetone core */ #if IS_ENABLED(CONFIG_SND_OMAP_SOC_MCBSP) OF_DEV_AUXDATA("ti,omap3-mcbsp", 0x49022000, "49022000.mcbsp", &mcbsp_pdata), diff --git a/arch/arm/mach-rpc/irq.c b/arch/arm/mach-rpc/irq.c index b8a61cb112073643256dbb693a8351e07c1f4335..7f0f40178634451f007fcf570397bd329c333f5d 100644 --- a/arch/arm/mach-rpc/irq.c +++ b/arch/arm/mach-rpc/irq.c @@ -118,7 +118,7 @@ extern unsigned char rpc_default_fiq_start, rpc_default_fiq_end; void __init rpc_init_irq(void) { - unsigned int irq, clr, set = 0; + unsigned int irq, clr, set; iomd_writeb(0, IOMD_IRQMASKA); iomd_writeb(0, IOMD_IRQMASKB); @@ -130,6 +130,7 @@ void __init rpc_init_irq(void) for (irq = 0; irq < NR_IRQS; irq++) { clr = IRQ_NOREQUEST; + set = 0; if (irq <= 6 || (irq >= 9 && irq <= 15)) clr |= IRQ_NOPROBE; diff --git a/arch/arm/mach-stm32/Kconfig b/arch/arm/mach-stm32/Kconfig index 713c068b953fb36187512bdfc1e2906e929dcf89..adca4368d67c447149fe2a8414d01a2b827f0a06 100644 --- a/arch/arm/mach-stm32/Kconfig +++ b/arch/arm/mach-stm32/Kconfig @@ -1,5 +1,6 @@ menuconfig ARCH_STM32 - bool "STMicroelectronics STM32 family" if ARM_SINGLE_ARMV7M || ARCH_MULTI_V7 + bool "STMicroelectronics STM32 family" + depends on ARM_SINGLE_ARMV7M || ARCH_MULTI_V7 select ARMV7M_SYSTICK if ARM_SINGLE_ARMV7M select HAVE_ARM_ARCH_TIMER if ARCH_MULTI_V7 select ARM_GIC if ARCH_MULTI_V7 diff --git a/arch/arm/mach-tegra/sleep-tegra30.S b/arch/arm/mach-tegra/sleep-tegra30.S index dd4a67dabd91692dffae18fd07245c372770840f..b7cd41461e7d2fb3175dc0e8e45be9aacb7b73c0 100644 --- a/arch/arm/mach-tegra/sleep-tegra30.S +++ b/arch/arm/mach-tegra/sleep-tegra30.S @@ -382,6 +382,14 @@ _pll_m_c_x_done: pll_locked r1, r0, CLK_RESET_PLLC_BASE pll_locked r1, r0, CLK_RESET_PLLX_BASE + tegra_get_soc_id TEGRA_APB_MISC_BASE, r1 + cmp r1, #TEGRA30 + beq 1f + ldr r1, [r0, #CLK_RESET_PLLP_BASE] + bic r1, r1, #(1<<31) @ disable PllP bypass + str r1, [r0, #CLK_RESET_PLLP_BASE] +1: + mov32 r7, TEGRA_TMRUS_BASE ldr r1, [r7] add r1, r1, #LOCK_DELAY @@ -641,7 +649,10 @@ tegra30_switch_cpu_to_clk32k: str r0, [r4, #PMC_PLLP_WB0_OVERRIDE] /* disable PLLP, PLLA, PLLC and PLLX */ + tegra_get_soc_id TEGRA_APB_MISC_BASE, r1 + cmp r1, #TEGRA30 ldr r0, [r5, #CLK_RESET_PLLP_BASE] + orrne r0, r0, #(1 << 31) @ enable PllP bypass on fast cluster bic r0, r0, #(1 << 30) str r0, [r5, #CLK_RESET_PLLP_BASE] ldr r0, [r5, #CLK_RESET_PLLA_BASE] diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 5d85f595b7dd09ad950e035435bf8860b88c28ec..aa4e580bc708d5e67b8e343956c8194607b0a454 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -357,7 +357,7 @@ static inline void poison_init_mem(void *s, size_t count) *p++ = 0xe7fddef0; } -static inline void +static inline void __init free_memmap(unsigned long start_pfn, unsigned long end_pfn) { struct page *start_pg, *end_pg; diff --git a/arch/arm/plat-pxa/ssp.c b/arch/arm/plat-pxa/ssp.c index f519199741837664df922fc60e31c42de8eab145..bf25f780c1c9ebad4b059e6b4607322378c4a063 100644 --- a/arch/arm/plat-pxa/ssp.c +++ b/arch/arm/plat-pxa/ssp.c @@ -183,18 +183,12 @@ static int pxa_ssp_probe(struct platform_device *pdev) static int pxa_ssp_remove(struct platform_device *pdev) { - struct resource *res; struct ssp_device *ssp; ssp = platform_get_drvdata(pdev); if (ssp == NULL) return -ENODEV; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, resource_size(res)); - - clk_put(ssp->clk); - mutex_lock(&ssp_lock); list_del(&ssp->node); mutex_unlock(&ssp_lock); diff --git a/arch/arm64/boot/Makefile b/arch/arm64/boot/Makefile index ee0625da0461c97b12cc580afc2cb551713bac09..4715d40a6d4b1e7dcd0767ecc5e9e7323090400f 100644 --- a/arch/arm64/boot/Makefile +++ b/arch/arm64/boot/Makefile @@ -18,7 +18,7 @@ include $(srctree)/arch/arm64/boot/dts/Makefile OBJCOPYFLAGS_Image :=-O binary -R .note -R .note.gnu.build-id -R .comment -S -targets := Image Image.gz +targets := Image Image.bz2 Image.gz Image.lz4 Image.lzma Image.lzo DTB_NAMES := $(subst $\",,$(CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE_NAMES)) ifneq ($(DTB_NAMES),) diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-olinuxino.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-olinuxino.dts index b3f186434f363f36834e0d3bccd1d671d2ba0fe5..185913de774d7842ca86f1ce0237e559066232c8 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-a64-olinuxino.dts +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-olinuxino.dts @@ -77,7 +77,7 @@ &mmc1 { pinctrl-names = "default"; pinctrl-0 = <&mmc1_pins>; - vmmc-supply = <®_aldo2>; + vmmc-supply = <®_dcdc1>; vqmmc-supply = <®_dldo4>; mmc-pwrseq = <&wifi_pwrseq>; bus-width = <4>; diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi index d3daf90a8715c50b844eb6e6f577aeb2c756991b..7abc4ea30541096a21720b42a8d9d30aa5b16266 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi @@ -366,7 +366,8 @@ interrupts = , , ; - clocks = <&ccu 58>; + clocks = <&ccu 58>, <&osc24M>, <&rtc 0>; + clock-names = "apb", "hosc", "losc"; gpio-controller; #gpio-cells = <3>; interrupt-controller; diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6-pine-h64.dts b/arch/arm64/boot/dts/allwinner/sun50i-h6-pine-h64.dts index 48daec7f78ba7cf4e674687fca73ed976c3b5b4b..6c3a47d90c793d4ffad219f2776eea181222b163 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-h6-pine-h64.dts +++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-pine-h64.dts @@ -176,6 +176,8 @@ pcf8563: rtc@51 { compatible = "nxp,pcf8563"; reg = <0x51>; + interrupt-parent = <&r_intc>; + interrupts = <0 IRQ_TYPE_LEVEL_LOW>; #clock-cells = <0>; }; }; diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi index cfa5fffcf62b437c60a9fde4b601767ecc7548b3..bd43912696111be8cbcdcc5aceb2e7ee8704d09e 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi @@ -69,6 +69,16 @@ clock-output-names = "osc32k"; }; + pmu { + compatible = "arm,cortex-a53-pmu", + "arm,armv8-pmuv3"; + interrupts = , + , + , + ; + interrupt-affinity = <&cpu0>, <&cpu1>, <&cpu2>, <&cpu3>; + }; + psci { compatible = "arm,psci-0.2"; method = "smc"; @@ -101,17 +111,6 @@ #reset-cells = <1>; }; - gic: interrupt-controller@3021000 { - compatible = "arm,gic-400"; - reg = <0x03021000 0x1000>, - <0x03022000 0x2000>, - <0x03024000 0x2000>, - <0x03026000 0x2000>; - interrupts = ; - interrupt-controller; - #interrupt-cells = <3>; - }; - pio: pinctrl@300b000 { compatible = "allwinner,sun50i-h6-pinctrl"; reg = <0x0300b000 0x400>; @@ -149,6 +148,17 @@ }; }; + gic: interrupt-controller@3021000 { + compatible = "arm,gic-400"; + reg = <0x03021000 0x1000>, + <0x03022000 0x2000>, + <0x03024000 0x2000>, + <0x03026000 0x2000>; + interrupts = ; + interrupt-controller; + #interrupt-cells = <3>; + }; + mmc0: mmc@4020000 { compatible = "allwinner,sun50i-h6-mmc", "allwinner,sun50i-a64-mmc"; diff --git a/arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi b/arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi index 9a1ea8a464057a970c4eefbca99999e283043143..85fffe03b96bf4040bb50e366f3c9de541669f88 100644 --- a/arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi +++ b/arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi @@ -59,10 +59,10 @@ pmu { compatible = "arm,armv8-pmuv3"; - interrupts = <0 120 8>, - <0 121 8>, - <0 122 8>, - <0 123 8>; + interrupts = <0 170 4>, + <0 171 4>, + <0 172 4>, + <0 173 4>; interrupt-affinity = <&cpu0>, <&cpu1>, <&cpu2>, diff --git a/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi b/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi index 765247bc4f2477e423a32d4c0efa7d83b0b3a4e9..e14e0ce7e89fed32970003859a30841915fcc9fc 100644 --- a/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi +++ b/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi @@ -125,6 +125,7 @@ status = "okay"; pinctrl-0 = <&hdmi_hpd_pins>, <&hdmi_i2c_pins>; pinctrl-names = "default"; + hdmi-supply = <&hdmi_5v>; }; &hdmi_tx_tmds_port { diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dts index 864ef0111b01ac93025edacdf0ffd048e73bcb07..062e12aa4677017e22d3f6232035e6bdbe475470 100644 --- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dts +++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dts @@ -33,11 +33,9 @@ gpio-keys-polled { compatible = "gpio-keys-polled"; - #address-cells = <1>; - #size-cells = <0>; poll-interval = <100>; - button@0 { + power-button { label = "power"; linux,code = ; gpios = <&gpio_ao GPIOAO_2 GPIO_ACTIVE_LOW>; @@ -78,6 +76,7 @@ status = "okay"; pinctrl-0 = <&hdmi_hpd_pins>, <&hdmi_i2c_pins>; pinctrl-names = "default"; + hdmi-supply = <&hdmi_5v>; }; &hdmi_tx_tmds_port { diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts index b4dfb9afdef86926d8fec69bf034cdb630ccde8a..daad007fac1f660df0e6cd7edd38c6667efd4eb8 100644 --- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts +++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts @@ -155,6 +155,7 @@ status = "okay"; pinctrl-0 = <&hdmi_hpd_pins>, <&hdmi_i2c_pins>; pinctrl-names = "default"; + hdmi-supply = <&hdmi_5v>; }; &hdmi_tx_tmds_port { @@ -255,7 +256,6 @@ cap-mmc-highspeed; mmc-ddr-3_3v; max-frequency = <50000000>; - non-removable; disable-wp; mmc-pwrseq = <&emmc_pwrseq>; diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts index 5896e8a5d86bc657b822c1524704ce6ab4026444..2602940c2077b3dfbf306caec92d13d428f006f3 100644 --- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts +++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts @@ -51,6 +51,7 @@ status = "okay"; pinctrl-0 = <&hdmi_hpd_pins>, <&hdmi_i2c_pins>; pinctrl-names = "default"; + hdmi-supply = <&hdmi_5v>; }; &hdmi_tx_tmds_port { diff --git a/arch/arm64/boot/dts/amlogic/meson-gxm-khadas-vim2.dts b/arch/arm64/boot/dts/amlogic/meson-gxm-khadas-vim2.dts index 313f88f8759e1d5f9ea7ad9e6b0b777a82a7cccb..bdf7c6c5983ce4875744c8432973b1f4b7037780 100644 --- a/arch/arm64/boot/dts/amlogic/meson-gxm-khadas-vim2.dts +++ b/arch/arm64/boot/dts/amlogic/meson-gxm-khadas-vim2.dts @@ -18,7 +18,6 @@ aliases { serial0 = &uart_AO; - serial1 = &uart_A; serial2 = &uart_AO_B; }; @@ -63,11 +62,9 @@ gpio-keys-polled { compatible = "gpio-keys-polled"; - #address-cells = <1>; - #size-cells = <0>; poll-interval = <100>; - button@0 { + power-button { label = "power"; linux,code = ; gpios = <&gpio_ao GPIOAO_2 GPIO_ACTIVE_LOW>; @@ -271,6 +268,7 @@ status = "okay"; pinctrl-0 = <&hdmi_hpd_pins>, <&hdmi_i2c_pins>; pinctrl-names = "default"; + hdmi-supply = <&hdmi_5v>; }; &hdmi_tx_tmds_port { @@ -408,8 +406,17 @@ /* This one is connected to the Bluetooth module */ &uart_A { status = "okay"; - pinctrl-0 = <&uart_a_pins>; + pinctrl-0 = <&uart_a_pins>, <&uart_a_cts_rts_pins>; pinctrl-names = "default"; + uart-has-rtscts; + + bluetooth { + compatible = "brcm,bcm43438-bt"; + shutdown-gpios = <&gpio GPIOX_17 GPIO_ACTIVE_HIGH>; + max-speed = <2000000>; + clocks = <&wifi32k>; + clock-names = "lpo"; + }; }; /* This is brought out on the Linux_RX (18) and Linux_TX (19) pins: */ diff --git a/arch/arm64/boot/dts/arm/juno-base.dtsi b/arch/arm64/boot/dts/arm/juno-base.dtsi index ce56a4acda4fa07bb7eedebfeeb55f05f29c0c8d..b6f486737589a7083dd1e6893f952dea7da60424 100644 --- a/arch/arm64/boot/dts/arm/juno-base.dtsi +++ b/arch/arm64/boot/dts/arm/juno-base.dtsi @@ -6,7 +6,6 @@ /* * Devices shared by all Juno boards */ - dma-ranges = <0 0 0 0 0x100 0>; memtimer: timer@2a810000 { compatible = "arm,armv7-timer-mem"; diff --git a/arch/arm64/boot/dts/arm/juno-clocks.dtsi b/arch/arm64/boot/dts/arm/juno-clocks.dtsi index e5e265dfa902500e8a139d08e57663d462a7d154..2870b5eeb198430aca0ee9301411a56e5aafaa2d 100644 --- a/arch/arm64/boot/dts/arm/juno-clocks.dtsi +++ b/arch/arm64/boot/dts/arm/juno-clocks.dtsi @@ -8,10 +8,10 @@ */ / { /* SoC fixed clocks */ - soc_uartclk: refclk7273800hz { + soc_uartclk: refclk7372800hz { compatible = "fixed-clock"; #clock-cells = <0>; - clock-frequency = <7273800>; + clock-frequency = <7372800>; clock-output-names = "juno:uartclk"; }; diff --git a/arch/arm64/boot/dts/marvell/armada-cp110.dtsi b/arch/arm64/boot/dts/marvell/armada-cp110.dtsi index 840c8454d03e3e81770a718e8e6aa17f53298ec6..571bede7bdf69849c4d913ae1b2ae5451594aff3 100644 --- a/arch/arm64/boot/dts/marvell/armada-cp110.dtsi +++ b/arch/arm64/boot/dts/marvell/armada-cp110.dtsi @@ -359,10 +359,10 @@ CP110_LABEL(nand_controller): nand@720000 { /* - * Due to the limitation of the pins available - * this controller is only usable on the CPM - * for A7K and on the CPS for A8K. - */ + * Due to the limitation of the pins available + * this controller is only usable on the CPM + * for A7K and on the CPS for A8K. + */ compatible = "marvell,armada-8k-nand-controller", "marvell,armada370-nand-controller"; reg = <0x720000 0x54>; diff --git a/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi b/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi index 78ce3979ef096fd1dc3059c9d8e9e9f44ca56ba9..f38b815e696d8a081bae01d09f7283fc662aefda 100644 --- a/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi +++ b/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi @@ -630,6 +630,8 @@ l11 { regulator-min-microvolt = <1750000>; regulator-max-microvolt = <3337000>; + regulator-allow-set-load; + regulator-system-load = <200000>; }; l12 { diff --git a/arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi b/arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi index 0ef90c6554a9885260c41dff9bbb6b627dc6ddac..9b41d77aa39b58c725169b7d69b4ce530dbe36fe 100644 --- a/arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi +++ b/arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi @@ -580,6 +580,8 @@ l21 { regulator-min-microvolt = <2950000>; regulator-max-microvolt = <2950000>; + regulator-allow-set-load; + regulator-system-load = <200000>; }; l22 { regulator-min-microvolt = <3300000>; diff --git a/arch/arm64/boot/dts/qcom/msm8916.dtsi b/arch/arm64/boot/dts/qcom/msm8916.dtsi index 7b32b8990d62fb22acdbbf287929162fb826f480..8011e564a234b27ac72628bf68b2d41367d9c338 100644 --- a/arch/arm64/boot/dts/qcom/msm8916.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8916.dtsi @@ -114,7 +114,7 @@ next-level-cache = <&L2_0>; enable-method = "psci"; cpu-idle-states = <&CPU_SPC>; - clocks = <&apcs 0>; + clocks = <&apcs>; operating-points-v2 = <&cpu_opp_table>; #cooling-cells = <2>; }; @@ -126,7 +126,7 @@ next-level-cache = <&L2_0>; enable-method = "psci"; cpu-idle-states = <&CPU_SPC>; - clocks = <&apcs 0>; + clocks = <&apcs>; operating-points-v2 = <&cpu_opp_table>; #cooling-cells = <2>; }; @@ -138,7 +138,7 @@ next-level-cache = <&L2_0>; enable-method = "psci"; cpu-idle-states = <&CPU_SPC>; - clocks = <&apcs 0>; + clocks = <&apcs>; operating-points-v2 = <&cpu_opp_table>; #cooling-cells = <2>; }; @@ -150,7 +150,7 @@ next-level-cache = <&L2_0>; enable-method = "psci"; cpu-idle-states = <&CPU_SPC>; - clocks = <&apcs 0>; + clocks = <&apcs>; operating-points-v2 = <&cpu_opp_table>; #cooling-cells = <2>; }; diff --git a/arch/arm64/boot/dts/qcom/msm8996.dtsi b/arch/arm64/boot/dts/qcom/msm8996.dtsi index 8c86c41a0d25fb3728bc10b2caa518dad5ea7354..3e7baabf64507c5ec9985ee8fb31c7181b9c1e9e 100644 --- a/arch/arm64/boot/dts/qcom/msm8996.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8996.dtsi @@ -918,6 +918,8 @@ interrupts = <0 138 IRQ_TYPE_LEVEL_HIGH>; phys = <&hsusb_phy2>; phy-names = "usb2-phy"; + snps,dis_u2_susphy_quirk; + snps,dis_enblslpm_quirk; }; }; @@ -947,6 +949,8 @@ interrupts = <0 131 IRQ_TYPE_LEVEL_HIGH>; phys = <&hsusb_phy1>, <&ssusb_phy_0>; phy-names = "usb2-phy", "usb3-phy"; + snps,dis_u2_susphy_quirk; + snps,dis_enblslpm_quirk; }; }; diff --git a/arch/arm64/boot/dts/renesas/r8a7795-es1.dtsi b/arch/arm64/boot/dts/renesas/r8a7795-es1.dtsi index 7b2fbaec9aef8d71fb1b9fbb04f0f8b1630b0bab..3dc61b7e1d08a9ee07bcb10746f1a0f7efa19363 100644 --- a/arch/arm64/boot/dts/renesas/r8a7795-es1.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a7795-es1.dtsi @@ -28,6 +28,7 @@ compatible = "renesas,ipmmu-r8a7795"; reg = <0 0xec680000 0 0x1000>; renesas,ipmmu-main = <&ipmmu_mm 5>; + power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; #iommu-cells = <1>; }; @@ -35,6 +36,7 @@ compatible = "renesas,ipmmu-r8a7795"; reg = <0 0xe7730000 0 0x1000>; renesas,ipmmu-main = <&ipmmu_mm 8>; + power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; #iommu-cells = <1>; }; diff --git a/arch/arm64/boot/dts/renesas/r8a77990-ebisu.dts b/arch/arm64/boot/dts/renesas/r8a77990-ebisu.dts index 2bc3a4884b0031f713391f046dabc2712f54f0aa..470c2a35a5aff5a0d019e511aa3db43ab50dfd35 100644 --- a/arch/arm64/boot/dts/renesas/r8a77990-ebisu.dts +++ b/arch/arm64/boot/dts/renesas/r8a77990-ebisu.dts @@ -33,7 +33,6 @@ &avb { pinctrl-0 = <&avb_pins>; pinctrl-names = "default"; - renesas,no-ether-link; phy-handle = <&phy0>; phy-mode = "rgmii-txid"; status = "okay"; diff --git a/arch/arm64/boot/dts/renesas/r8a77995.dtsi b/arch/arm64/boot/dts/renesas/r8a77995.dtsi index fe77bc43c4474d1579502652b5c88c469c3201b6..fb3ecb2c385d118c5fb6bc2ee47ce9eb481ce41b 100644 --- a/arch/arm64/boot/dts/renesas/r8a77995.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a77995.dtsi @@ -938,7 +938,7 @@ du: display@feb00000 { compatible = "renesas,du-r8a77995"; - reg = <0 0xfeb00000 0 0x80000>; + reg = <0 0xfeb00000 0 0x40000>; interrupts = , ; clocks = <&cpg CPG_MOD 724>, diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 2fc45d5505a4febc51d400bc4ed6811d644cf786..0b7f5a349943a8c5f93dd96c50c935381a9dcbc4 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -372,6 +372,7 @@ CONFIG_THERMAL_EMULATION=y CONFIG_ROCKCHIP_THERMAL=m CONFIG_RCAR_GEN3_THERMAL=y CONFIG_ARMADA_THERMAL=y +CONFIG_BCM2835_THERMAL=m CONFIG_BRCMSTB_THERMAL=m CONFIG_EXYNOS_THERMAL=y CONFIG_TEGRA_BPMP_THERMAL=m diff --git a/arch/arm64/configs/gki_defconfig b/arch/arm64/configs/gki_defconfig index b4b41a34ed88076b95e13f88976a74ba3e81713b..b3532e3698879ee57d9a1d7f171861b53f617e8e 100644 --- a/arch/arm64/configs/gki_defconfig +++ b/arch/arm64/configs/gki_defconfig @@ -8,15 +8,17 @@ CONFIG_TASK_IO_ACCOUNTING=y CONFIG_PSI=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y -CONFIG_IKHEADERS=m +CONFIG_IKHEADERS=y CONFIG_MEMCG=y CONFIG_MEMCG_SWAP=y CONFIG_BLK_CGROUP=y -CONFIG_RT_GROUP_SCHED=y CONFIG_CGROUP_FREEZER=y CONFIG_CPUSETS=y CONFIG_CGROUP_CPUACCT=y CONFIG_CGROUP_BPF=y +CONFIG_NAMESPACES=y +# CONFIG_UTS_NS is not set +# CONFIG_PID_NS is not set CONFIG_SCHED_AUTOGROUP=y CONFIG_SCHED_TUNE=y CONFIG_BLK_DEV_INITRD=y @@ -24,7 +26,6 @@ CONFIG_BLK_DEV_INITRD=y # CONFIG_RD_LZMA is not set # CONFIG_RD_XZ is not set # CONFIG_RD_LZO is not set -# CONFIG_RD_LZ4 is not set # CONFIG_SYSFS_SYSCALL is not set # CONFIG_FHANDLE is not set CONFIG_KALLSYMS_ALL=y @@ -32,12 +33,12 @@ CONFIG_BPF_SYSCALL=y CONFIG_BPF_JIT_ALWAYS_ON=y # CONFIG_RSEQ is not set CONFIG_EMBEDDED=y -# CONFIG_VM_EVENT_COUNTERS is not set # CONFIG_COMPAT_BRK is not set # CONFIG_SLAB_MERGE_DEFAULT is not set CONFIG_SLAB_FREELIST_RANDOM=y CONFIG_SLAB_FREELIST_HARDENED=y CONFIG_PROFILING=y +# CONFIG_ZONE_DMA32 is not set CONFIG_ARCH_HISI=y CONFIG_ARCH_QCOM=y CONFIG_PCI=y @@ -55,6 +56,9 @@ CONFIG_ARM64_SW_TTBR0_PAN=y CONFIG_RANDOMIZE_BASE=y # CONFIG_DMI is not set CONFIG_COMPAT=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=0 +# CONFIG_PM_WAKELOCKS_GC is not set CONFIG_ENERGY_MODEL=y CONFIG_CPU_IDLE=y CONFIG_ARM_CPUIDLE=y @@ -63,7 +67,6 @@ CONFIG_CPU_FREQ_TIMES=y CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL=y CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y -CONFIG_CPUFREQ_DUMMY=m CONFIG_ARM_SCPI_CPUFREQ=y CONFIG_ARM_SCMI_CPUFREQ=y CONFIG_ARM_SCMI_PROTOCOL=y @@ -74,17 +77,17 @@ CONFIG_ARM_SCPI_PROTOCOL=y CONFIG_ARM64_CRYPTO=y CONFIG_CRYPTO_SHA2_ARM64_CE=y CONFIG_CRYPTO_AES_ARM64_CE_BLK=y -CONFIG_KPROBES=y CONFIG_LTO_CLANG=y -CONFIG_CFI_CLANG=y CONFIG_SHADOW_CALL_STACK=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_MODVERSIONS=y CONFIG_BLK_INLINE_ENCRYPTION=y +CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK=y CONFIG_GKI_HACKS_TO_FIX=y # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set -CONFIG_BINFMT_MISC=m +CONFIG_BINFMT_MISC=y +CONFIG_CLEANCACHE=y CONFIG_CMA=y CONFIG_CMA_AREAS=16 CONFIG_ZSMALLOC=y @@ -95,6 +98,7 @@ CONFIG_XFRM_USER=y CONFIG_XFRM_INTERFACE=y CONFIG_XFRM_STATISTICS=y CONFIG_NET_KEY=y +CONFIG_XDP_SOCKETS=y CONFIG_INET=y CONFIG_IP_MULTICAST=y CONFIG_IP_ADVANCED_ROUTER=y @@ -113,7 +117,6 @@ CONFIG_IPV6_MIP6=y CONFIG_IPV6_VTI=y CONFIG_IPV6_MULTIPLE_TABLES=y CONFIG_NETFILTER=y -# CONFIG_BRIDGE_NETFILTER is not set CONFIG_NF_CONNTRACK=y CONFIG_NF_CONNTRACK_SECMARK=y CONFIG_NF_CONNTRACK_EVENTS=y @@ -155,6 +158,7 @@ CONFIG_NETFILTER_XT_MATCH_POLICY=y CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y CONFIG_NETFILTER_XT_MATCH_QUOTA=y CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y CONFIG_NETFILTER_XT_MATCH_SOCKET=y CONFIG_NETFILTER_XT_MATCH_STATE=y CONFIG_NETFILTER_XT_MATCH_STATISTIC=y @@ -184,7 +188,6 @@ CONFIG_IP6_NF_MANGLE=y CONFIG_IP6_NF_RAW=y CONFIG_TIPC=y CONFIG_L2TP=y -CONFIG_BRIDGE=y CONFIG_NET_SCHED=y CONFIG_NET_SCH_HTB=y CONFIG_NET_SCH_INGRESS=y @@ -193,30 +196,22 @@ CONFIG_NET_CLS_BPF=y CONFIG_NET_EMATCH=y CONFIG_NET_EMATCH_U32=y CONFIG_NET_CLS_ACT=y -CONFIG_VSOCKETS=m -CONFIG_VIRTIO_VSOCKETS=m CONFIG_BPF_JIT=y -CONFIG_CAN=m -CONFIG_CAN_VCAN=m CONFIG_BT=y CONFIG_CFG80211=y # CONFIG_CFG80211_DEFAULT_PS is not set # CONFIG_CFG80211_CRDA_SUPPORT is not set CONFIG_MAC80211=y -# CONFIG_MAC80211_RC_MINSTREL is not set CONFIG_RFKILL=y # CONFIG_UEVENT_HELPER is not set # CONFIG_FW_CACHE is not set # CONFIG_ALLOW_DEV_COREDUMP is not set -CONFIG_DEBUG_DEVRES=y CONFIG_DMA_CMA=y CONFIG_GNSS=y -CONFIG_GNSS_CMDLINE_SERIAL=m CONFIG_ZRAM=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 -CONFIG_VIRTIO_BLK=m CONFIG_UID_SYS_STATS=y CONFIG_SCSI=y # CONFIG_SCSI_PROC_FS is not set @@ -227,6 +222,7 @@ CONFIG_SCSI_UFS_CRYPTO=y CONFIG_MD=y CONFIG_BLK_DEV_DM=y CONFIG_DM_CRYPT=y +CONFIG_DM_DEFAULT_KEY=y CONFIG_DM_SNAPSHOT=y CONFIG_DM_UEVENT=y CONFIG_DM_VERITY=y @@ -234,8 +230,8 @@ CONFIG_DM_VERITY_AVB=y CONFIG_DM_VERITY_FEC=y CONFIG_DM_BOW=y CONFIG_NETDEVICES=y +CONFIG_DUMMY=y CONFIG_TUN=y -CONFIG_VIRTIO_NET=m # CONFIG_ETHERNET is not set CONFIG_PHYLIB=y CONFIG_PPP=y @@ -269,12 +265,12 @@ CONFIG_USB_USBNET=y # CONFIG_WLAN_VENDOR_TI is not set # CONFIG_WLAN_VENDOR_ZYDAS is not set # CONFIG_WLAN_VENDOR_QUANTENNA is not set -CONFIG_VIRT_WIFI=m CONFIG_INPUT_FF_MEMLESS=y CONFIG_INPUT_EVDEV=y CONFIG_KEYBOARD_GPIO=y # CONFIG_INPUT_MOUSE is not set CONFIG_INPUT_JOYSTICK=y +CONFIG_JOYSTICK_XPAD=y CONFIG_INPUT_MISC=y CONFIG_INPUT_UINPUT=y # CONFIG_VT is not set @@ -284,13 +280,11 @@ CONFIG_SERIAL_8250=y # CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set CONFIG_SERIAL_8250_CONSOLE=y # CONFIG_SERIAL_8250_EXAR is not set -CONFIG_SERIAL_OF_PLATFORM=m +CONFIG_SERIAL_OF_PLATFORM=y CONFIG_SERIAL_AMBA_PL011=y CONFIG_SERIAL_AMBA_PL011_CONSOLE=y CONFIG_SERIAL_DEV_BUS=y -CONFIG_VIRTIO_CONSOLE=m CONFIG_HW_RANDOM=y -CONFIG_HW_RANDOM_VIRTIO=m # CONFIG_HW_RANDOM_CAVIUM is not set # CONFIG_DEVPORT is not set # CONFIG_I2C_COMPAT is not set @@ -306,8 +300,10 @@ CONFIG_THERMAL_GOV_USER_SPACE=y CONFIG_CPU_THERMAL=y CONFIG_DEVFREQ_THERMAL=y CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y CONFIG_MFD_ACT8945A=y CONFIG_MFD_SYSCON=y +CONFIG_REGULATOR=y CONFIG_REGULATOR_FIXED_VOLTAGE=y CONFIG_MEDIA_SUPPORT=y CONFIG_MEDIA_CAMERA_SUPPORT=y @@ -315,7 +311,6 @@ CONFIG_MEDIA_CONTROLLER=y # CONFIG_VGA_ARB is not set CONFIG_DRM=y # CONFIG_DRM_FBDEV_EMULATION is not set -CONFIG_DRM_VIRTIO_GPU=m CONFIG_SOUND=y CONFIG_SND=y CONFIG_SND_HRTIMER=y @@ -323,7 +318,6 @@ CONFIG_SND_DYNAMIC_MINORS=y # CONFIG_SND_SUPPORT_OLD_API is not set # CONFIG_SND_VERBOSE_PROCFS is not set # CONFIG_SND_DRIVERS is not set -CONFIG_SND_INTEL8X0=y CONFIG_SND_USB_AUDIO=y CONFIG_SND_SOC=y CONFIG_HIDRAW=y @@ -333,50 +327,43 @@ CONFIG_HID_ELECOM=y CONFIG_HID_MAGICMOUSE=y CONFIG_HID_MICROSOFT=y CONFIG_HID_MULTITOUCH=y +CONFIG_HID_NINTENDO=y +CONFIG_HID_SONY=y +CONFIG_HID_STEAM=y CONFIG_USB_HIDDEV=y -CONFIG_USB=y CONFIG_USB_OTG=y CONFIG_USB_GADGET=y -CONFIG_USB_DUMMY_HCD=m CONFIG_USB_CONFIGFS=y CONFIG_USB_CONFIGFS_UEVENT=y +CONFIG_USB_CONFIGFS_MASS_STORAGE=y CONFIG_USB_CONFIGFS_F_FS=y CONFIG_USB_CONFIGFS_F_ACC=y CONFIG_USB_CONFIGFS_F_AUDIO_SRC=y CONFIG_USB_CONFIGFS_F_MIDI=y -CONFIG_MMC=m +CONFIG_TYPEC=y +CONFIG_MMC=y # CONFIG_PWRSEQ_EMMC is not set # CONFIG_PWRSEQ_SIMPLE is not set -CONFIG_MMC_SDHCI=m -CONFIG_MMC_SDHCI_PLTFM=m +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y CONFIG_NEW_LEDS=y CONFIG_LEDS_CLASS=y CONFIG_LEDS_TRIGGERS=y CONFIG_EDAC=y CONFIG_RTC_CLASS=y # CONFIG_RTC_SYSTOHC is not set -CONFIG_RTC_DRV_TEST=m CONFIG_RTC_DRV_PL030=y CONFIG_RTC_DRV_PL031=y CONFIG_DMADEVICES=y CONFIG_UIO=y -CONFIG_VIRTIO_PCI=m -# CONFIG_VIRTIO_PCI_LEGACY is not set -CONFIG_VIRTIO_INPUT=m -CONFIG_VIRTIO_MMIO=m -CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y CONFIG_STAGING=y CONFIG_ASHMEM=y CONFIG_ANDROID_VSOC=y -CONFIG_ION=y -CONFIG_ION_SYSTEM_HEAP=y CONFIG_COMMON_CLK_SCPI=y # CONFIG_COMMON_CLK_XGENE is not set CONFIG_HWSPINLOCK=y CONFIG_MAILBOX=y CONFIG_ARM_SMMU=y -CONFIG_QCOM_COMMAND_DB=y -CONFIG_QCOM_RPMH=y CONFIG_DEVFREQ_GOV_PERFORMANCE=y CONFIG_DEVFREQ_GOV_POWERSAVE=y CONFIG_DEVFREQ_GOV_USERSPACE=y @@ -389,6 +376,9 @@ CONFIG_GENERIC_PHY=y CONFIG_RAS=y CONFIG_ANDROID=y CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_BINDERFS=y +CONFIG_LIBNVDIMM=y +# CONFIG_ND_BLK is not set CONFIG_EXT4_FS=y CONFIG_EXT4_FS_SECURITY=y CONFIG_F2FS_FS=y @@ -402,11 +392,12 @@ CONFIG_QUOTA=y CONFIG_QFMT_V2=y CONFIG_FUSE_FS=y CONFIG_OVERLAY_FS=y +CONFIG_INCREMENTAL_FS=y CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y +CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y # CONFIG_EFIVAR_FS is not set -CONFIG_SDCARD_FS=y CONFIG_PSTORE=y CONFIG_PSTORE_CONSOLE=y CONFIG_PSTORE_RAM=y @@ -459,13 +450,16 @@ CONFIG_NLS_MAC_INUIT=y CONFIG_NLS_MAC_ROMANIAN=y CONFIG_NLS_MAC_TURKISH=y CONFIG_NLS_UTF8=y +CONFIG_UNICODE=y CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y CONFIG_SECURITY=y +CONFIG_SECURITYFS=y CONFIG_SECURITY_NETWORK=y CONFIG_HARDENED_USERCOPY=y CONFIG_SECURITY_SELINUX=y +CONFIG_INIT_STACK_ALL=y +CONFIG_INIT_ON_ALLOC_DEFAULT_ON=y CONFIG_CRYPTO_ADIANTUM=y -CONFIG_CRYPTO_MD4=y CONFIG_CRYPTO_LZ4=y CONFIG_CRYPTO_ZSTD=y CONFIG_CRYPTO_ANSI_CPRNG=y @@ -483,6 +477,6 @@ CONFIG_SOFTLOCKUP_DETECTOR=y # CONFIG_DETECT_HUNG_TASK is not set CONFIG_PANIC_TIMEOUT=5 CONFIG_SCHEDSTATS=y -# CONFIG_RUNTIME_TESTING_MENU is not set +# CONFIG_DEBUG_PREEMPT is not set CONFIG_CORESIGHT=y CONFIG_CORESIGHT_STM=y diff --git a/arch/arm64/configs/vendor/bengal-perf_defconfig b/arch/arm64/configs/vendor/bengal-perf_defconfig index 3592739b3dc12a31e269d36a74472baf3e9ce83a..e1228bee5228fb2a8a368fdb5dd13624b61eafa0 100644 --- a/arch/arm64/configs/vendor/bengal-perf_defconfig +++ b/arch/arm64/configs/vendor/bengal-perf_defconfig @@ -80,7 +80,6 @@ CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y CONFIG_CPU_BOOST=y CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y CONFIG_ARM_QCOM_CPUFREQ_HW=y -CONFIG_MSM_TZ_LOG=y CONFIG_ARM64_CRYPTO=y CONFIG_CRYPTO_SHA1_ARM64_CE=y CONFIG_CRYPTO_SHA2_ARM64_CE=y @@ -392,6 +391,7 @@ CONFIG_QTI_CX_IPEAK_COOLING_DEVICE=y CONFIG_MFD_I2C_PMIC=y CONFIG_MFD_SPMI_PMIC=y CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y CONFIG_REGULATOR_PROXY_CONSUMER=y CONFIG_REGULATOR_QCOM_SMD_RPM=y CONFIG_REGULATOR_QPNP_LCDB=y @@ -411,7 +411,6 @@ CONFIG_DVB_MPQ_DEMUX=m CONFIG_DVB_MPQ_SW=y CONFIG_VIDEO_V4L2_VIDEOBUF2_CORE=y CONFIG_DRM=y -# CONFIG_DRM_MSM is not set CONFIG_BACKLIGHT_LCD_SUPPORT=y CONFIG_BACKLIGHT_CLASS_DEVICE=y CONFIG_LOGO=y @@ -429,6 +428,7 @@ CONFIG_HID_ELECOM=y CONFIG_HID_MAGICMOUSE=y CONFIG_HID_MICROSOFT=y CONFIG_HID_MULTITOUCH=y +CONFIG_HID_NINTENDO=y CONFIG_HID_PLANTRONICS=y CONFIG_HID_SONY=y CONFIG_USB_HIDDEV=y diff --git a/arch/arm64/configs/vendor/bengal_defconfig b/arch/arm64/configs/vendor/bengal_defconfig index 4467b2111d0ffb9a3014b9eee82d5398f3010650..1af739ece5fa5e6cfdf2d4068c8a964dbd442d15 100644 --- a/arch/arm64/configs/vendor/bengal_defconfig +++ b/arch/arm64/configs/vendor/bengal_defconfig @@ -404,6 +404,7 @@ CONFIG_QTI_CX_IPEAK_COOLING_DEVICE=y CONFIG_MFD_I2C_PMIC=y CONFIG_MFD_SPMI_PMIC=y CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y CONFIG_REGULATOR_PROXY_CONSUMER=y CONFIG_REGULATOR_QCOM_SMD_RPM=y CONFIG_REGULATOR_QPNP_LCDB=y @@ -423,7 +424,6 @@ CONFIG_DVB_MPQ_DEMUX=m CONFIG_DVB_MPQ_SW=y CONFIG_VIDEO_V4L2_VIDEOBUF2_CORE=y CONFIG_DRM=y -# CONFIG_DRM_MSM is not set CONFIG_BACKLIGHT_LCD_SUPPORT=y CONFIG_BACKLIGHT_CLASS_DEVICE=y CONFIG_LOGO=y @@ -441,6 +441,7 @@ CONFIG_HID_ELECOM=y CONFIG_HID_MAGICMOUSE=y CONFIG_HID_MICROSOFT=y CONFIG_HID_MULTITOUCH=y +CONFIG_HID_NINTENDO=y CONFIG_HID_PLANTRONICS=y CONFIG_HID_SONY=y CONFIG_USB_HIDDEV=y diff --git a/arch/arm64/configs/vendor/debugfs.config b/arch/arm64/configs/vendor/debugfs.config new file mode 100644 index 0000000000000000000000000000000000000000..059013b6a88ad5f04baff671ae526dcf03bf7547 --- /dev/null +++ b/arch/arm64/configs/vendor/debugfs.config @@ -0,0 +1,2 @@ +CONFIG_PAGE_OWNER=n +CONFIG_DEBUG_FS=n diff --git a/arch/arm64/configs/vendor/kona-iot-perf_defconfig b/arch/arm64/configs/vendor/kona-iot-perf_defconfig index 1b4e76fa5a039755e7f1f56d7415a9ae21e91501..cd7797f8b71354171ecf862e06e9b0c4cfcd4494 100644 --- a/arch/arm64/configs/vendor/kona-iot-perf_defconfig +++ b/arch/arm64/configs/vendor/kona-iot-perf_defconfig @@ -426,7 +426,6 @@ CONFIG_DVB_MPQ_SW=y CONFIG_VIDEO_V4L2_VIDEOBUF2_CORE=y CONFIG_I2C_RTC6226_QCA=y CONFIG_DRM=y -# CONFIG_DRM_MSM is not set CONFIG_DRM_LONTIUM_LT9611UXC=y CONFIG_FB_ARMCLCD=y CONFIG_BACKLIGHT_QCOM_SPMI_WLED=y diff --git a/arch/arm64/configs/vendor/kona-iot_defconfig b/arch/arm64/configs/vendor/kona-iot_defconfig index dbce12ec9f52f3bf16b0b7123ab40431afea310c..9a0731d976345edf5a21c043c6dc32c2a86b5142 100644 --- a/arch/arm64/configs/vendor/kona-iot_defconfig +++ b/arch/arm64/configs/vendor/kona-iot_defconfig @@ -442,7 +442,6 @@ CONFIG_DVB_MPQ_SW=y CONFIG_VIDEO_V4L2_VIDEOBUF2_CORE=y CONFIG_I2C_RTC6226_QCA=y CONFIG_DRM=y -# CONFIG_DRM_MSM is not set CONFIG_DRM_LONTIUM_LT9611UXC=y CONFIG_BACKLIGHT_LCD_SUPPORT=y CONFIG_BACKLIGHT_CLASS_DEVICE=y diff --git a/arch/arm64/configs/vendor/kona-perf_defconfig b/arch/arm64/configs/vendor/kona-perf_defconfig index 57134577d28873004b1c1e7e27dda5757111b4d6..33267aca6d96757c9c8932d9c2305548773e0f1b 100644 --- a/arch/arm64/configs/vendor/kona-perf_defconfig +++ b/arch/arm64/configs/vendor/kona-perf_defconfig @@ -81,7 +81,6 @@ CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y CONFIG_CPU_BOOST=y CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y CONFIG_ARM_QCOM_CPUFREQ_HW=y -CONFIG_MSM_TZ_LOG=y CONFIG_ARM64_CRYPTO=y CONFIG_CRYPTO_SHA1_ARM64_CE=y CONFIG_CRYPTO_SHA2_ARM64_CE=y @@ -431,7 +430,6 @@ CONFIG_DVB_MPQ_SW=y CONFIG_VIDEO_V4L2_VIDEOBUF2_CORE=y CONFIG_I2C_RTC6226_QCA=y CONFIG_DRM=y -# CONFIG_DRM_MSM is not set CONFIG_DRM_LONTIUM_LT9611UXC=y CONFIG_FB_ARMCLCD=y CONFIG_BACKLIGHT_QCOM_SPMI_WLED=y @@ -450,6 +448,7 @@ CONFIG_HID_ELECOM=y CONFIG_HID_MAGICMOUSE=y CONFIG_HID_MICROSOFT=y CONFIG_HID_MULTITOUCH=y +CONFIG_HID_NINTENDO=y CONFIG_HID_PLANTRONICS=y CONFIG_HID_SONY=y CONFIG_HID_QVR=y @@ -597,6 +596,7 @@ CONFIG_QCOM_EUD=y CONFIG_QCOM_MINIDUMP=y CONFIG_QCOM_FSA4480_I2C=y CONFIG_QCOM_WATCHDOG_V2=y +CONFIG_QCOM_INITIAL_LOGBUF=y CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y CONFIG_QCOM_BUS_SCALING=y CONFIG_QCOM_BUS_CONFIG_RPMH=y diff --git a/arch/arm64/configs/vendor/kona_defconfig b/arch/arm64/configs/vendor/kona_defconfig index 5033e4ae27b194b88111a5660b7983fdf0f02f52..1ef048208580d540d1ab745c707dc30f3a45a548 100644 --- a/arch/arm64/configs/vendor/kona_defconfig +++ b/arch/arm64/configs/vendor/kona_defconfig @@ -447,7 +447,6 @@ CONFIG_DVB_MPQ_SW=y CONFIG_VIDEO_V4L2_VIDEOBUF2_CORE=y CONFIG_I2C_RTC6226_QCA=y CONFIG_DRM=y -# CONFIG_DRM_MSM is not set CONFIG_DRM_LONTIUM_LT9611UXC=y CONFIG_BACKLIGHT_LCD_SUPPORT=y CONFIG_BACKLIGHT_CLASS_DEVICE=y @@ -467,6 +466,7 @@ CONFIG_HID_ELECOM=y CONFIG_HID_MAGICMOUSE=y CONFIG_HID_MICROSOFT=y CONFIG_HID_MULTITOUCH=y +CONFIG_HID_NINTENDO=y CONFIG_HID_PLANTRONICS=y CONFIG_HID_SONY=y CONFIG_HID_QVR=y @@ -626,6 +626,7 @@ CONFIG_QCOM_MINIDUMP=y CONFIG_MSM_CORE_HANG_DETECT=y CONFIG_QCOM_FSA4480_I2C=y CONFIG_QCOM_WATCHDOG_V2=y +CONFIG_QCOM_INITIAL_LOGBUF=y CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y CONFIG_QCOM_WDOG_IPI_ENABLE=y CONFIG_QCOM_BUS_SCALING=y diff --git a/arch/arm64/configs/vendor/lito-perf_defconfig b/arch/arm64/configs/vendor/lito-perf_defconfig index 8aa068cde94d7650379d17386c41f447fb00a6a7..eb3408c64e52f350ed90c1b13f2095f2b8443442 100644 --- a/arch/arm64/configs/vendor/lito-perf_defconfig +++ b/arch/arm64/configs/vendor/lito-perf_defconfig @@ -79,7 +79,6 @@ CONFIG_CPU_BOOST=y CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y CONFIG_ARM_QCOM_CPUFREQ_HW=y CONFIG_ARM_QCOM_CPUFREQ_HW_DEBUG=y -CONFIG_MSM_TZ_LOG=y CONFIG_ARM64_CRYPTO=y CONFIG_CRYPTO_SHA1_ARM64_CE=y CONFIG_CRYPTO_SHA2_ARM64_CE=y @@ -281,6 +280,7 @@ CONFIG_BLK_DEV_RAM_SIZE=8192 CONFIG_HDCP_QSEECOM=y CONFIG_QSEECOM=y CONFIG_UID_SYS_STATS=y +CONFIG_FPR_FPC=y CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y CONFIG_CHR_DEV_SG=y @@ -423,7 +423,6 @@ CONFIG_DVB_MPQ_DEMUX=m CONFIG_DVB_MPQ_SW=y CONFIG_VIDEO_V4L2_VIDEOBUF2_CORE=y CONFIG_DRM=y -# CONFIG_DRM_MSM is not set CONFIG_BACKLIGHT_LCD_SUPPORT=y CONFIG_BACKLIGHT_CLASS_DEVICE=y CONFIG_BACKLIGHT_QCOM_SPMI_WLED=y @@ -442,6 +441,7 @@ CONFIG_HID_ELECOM=y CONFIG_HID_MAGICMOUSE=y CONFIG_HID_MICROSOFT=y CONFIG_HID_MULTITOUCH=y +CONFIG_HID_NINTENDO=y CONFIG_HID_PLANTRONICS=y CONFIG_HID_SONY=y CONFIG_USB_HIDDEV=y @@ -589,6 +589,7 @@ CONFIG_QCOM_EUD=y CONFIG_QCOM_MINIDUMP=y CONFIG_QCOM_FSA4480_I2C=y CONFIG_QCOM_WATCHDOG_V2=y +CONFIG_QCOM_INITIAL_LOGBUF=y CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y CONFIG_QCOM_BUS_SCALING=y CONFIG_QCOM_BUS_CONFIG_RPMH=y diff --git a/arch/arm64/configs/vendor/lito_defconfig b/arch/arm64/configs/vendor/lito_defconfig index 6d687facc357bd8283ecbaf9ccc553d110374c31..c88be5121468014b4d55459b56408745967a3434 100644 --- a/arch/arm64/configs/vendor/lito_defconfig +++ b/arch/arm64/configs/vendor/lito_defconfig @@ -287,6 +287,7 @@ CONFIG_BLK_DEV_RAM_SIZE=8192 CONFIG_HDCP_QSEECOM=y CONFIG_QSEECOM=y CONFIG_UID_SYS_STATS=y +CONFIG_FPR_FPC=y CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y CONFIG_CHR_DEV_SG=y @@ -431,7 +432,6 @@ CONFIG_DVB_MPQ_DEMUX=m CONFIG_DVB_MPQ_SW=y CONFIG_VIDEO_V4L2_VIDEOBUF2_CORE=y CONFIG_DRM=y -# CONFIG_DRM_MSM is not set CONFIG_BACKLIGHT_LCD_SUPPORT=y CONFIG_BACKLIGHT_CLASS_DEVICE=y CONFIG_BACKLIGHT_QCOM_SPMI_WLED=y @@ -450,6 +450,7 @@ CONFIG_HID_ELECOM=y CONFIG_HID_MAGICMOUSE=y CONFIG_HID_MICROSOFT=y CONFIG_HID_MULTITOUCH=y +CONFIG_HID_NINTENDO=y CONFIG_HID_PLANTRONICS=y CONFIG_HID_SONY=y CONFIG_USB_HIDDEV=y @@ -608,6 +609,7 @@ CONFIG_MSM_CORE_HANG_DETECT=y CONFIG_MSM_GLADIATOR_HANG_DETECT=y CONFIG_QCOM_FSA4480_I2C=y CONFIG_QCOM_WATCHDOG_V2=y +CONFIG_QCOM_INITIAL_LOGBUF=y CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y CONFIG_QCOM_BUS_SCALING=y CONFIG_QCOM_BUS_CONFIG_RPMH=y diff --git a/arch/arm64/include/asm/alternative.h b/arch/arm64/include/asm/alternative.h index c3a08d68a6d2ddce26962ee47e7b144554659d6b..887a8512bf10b440f00a41cea992c6a3a256d235 100644 --- a/arch/arm64/include/asm/alternative.h +++ b/arch/arm64/include/asm/alternative.h @@ -42,7 +42,7 @@ static inline void apply_alternatives_module(void *start, size_t length) { } " .byte 662b-661b\n" /* source len */ \ " .byte 664f-663f\n" /* replacement len */ -#define ALTINSTR_ENTRY_CB(feature,cb) \ +#define ALTINSTR_ENTRY_CB(feature, cb) \ " .word 661b - .\n" /* label */ \ " .word " __stringify(cb) "- .\n" /* callback */ \ " .hword " __stringify(feature) "\n" /* feature bit */ \ @@ -88,7 +88,7 @@ static inline void apply_alternatives_module(void *start, size_t length) { } oldinstr "\n" \ "662:\n" \ ".pushsection .altinstructions,\"a\"\n" \ - ALTINSTR_ENTRY_CB(feature,cb) \ + ALTINSTR_ENTRY_CB(feature, cb) \ ".popsection\n" \ "663:\n\t" \ "664:\n\t" \ diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h index 6106a85ae0be70f91f8ad7b64bd7723e236843e5..778cb4f868d9b6e839ae374ce2b09335371bdd43 100644 --- a/arch/arm64/include/asm/kvm_emulate.h +++ b/arch/arm64/include/asm/kvm_emulate.h @@ -202,6 +202,38 @@ static inline void vcpu_write_spsr(struct kvm_vcpu *vcpu, unsigned long v) vcpu_gp_regs(vcpu)->spsr[KVM_SPSR_EL1] = v; } +/* + * The layout of SPSR for an AArch32 state is different when observed from an + * AArch64 SPSR_ELx or an AArch32 SPSR_*. This function generates the AArch32 + * view given an AArch64 view. + * + * In ARM DDI 0487E.a see: + * + * - The AArch64 view (SPSR_EL2) in section C5.2.18, page C5-426 + * - The AArch32 view (SPSR_abt) in section G8.2.126, page G8-6256 + * - The AArch32 view (SPSR_und) in section G8.2.132, page G8-6280 + * + * Which show the following differences: + * + * | Bit | AA64 | AA32 | Notes | + * +-----+------+------+-----------------------------| + * | 24 | DIT | J | J is RES0 in ARMv8 | + * | 21 | SS | DIT | SS doesn't exist in AArch32 | + * + * ... and all other bits are (currently) common. + */ +static inline unsigned long host_spsr_to_spsr32(unsigned long spsr) +{ + const unsigned long overlap = BIT(24) | BIT(21); + unsigned long dit = !!(spsr & PSR_AA32_DIT_BIT); + + spsr &= ~overlap; + + spsr |= dit << 21; + + return spsr; +} + static inline bool vcpu_mode_priv(const struct kvm_vcpu *vcpu) { u32 mode; @@ -261,6 +293,11 @@ static inline bool kvm_vcpu_dabt_issext(const struct kvm_vcpu *vcpu) return !!(kvm_vcpu_get_hsr(vcpu) & ESR_ELx_SSE); } +static inline bool kvm_vcpu_dabt_issf(const struct kvm_vcpu *vcpu) +{ + return !!(kvm_vcpu_get_hsr(vcpu) & ESR_ELx_SF); +} + static inline int kvm_vcpu_dabt_get_rd(const struct kvm_vcpu *vcpu) { return (kvm_vcpu_get_hsr(vcpu) & ESR_ELx_SRT_MASK) >> ESR_ELx_SRT_SHIFT; diff --git a/arch/arm64/include/asm/kvm_mmio.h b/arch/arm64/include/asm/kvm_mmio.h index 75ea4207975760f92a1b8d091a7e04b8c7dd0e7a..0240290cf7641fb5c3d331ede431c7f6b7fc3712 100644 --- a/arch/arm64/include/asm/kvm_mmio.h +++ b/arch/arm64/include/asm/kvm_mmio.h @@ -21,13 +21,11 @@ #include #include -/* - * This is annoying. The mmio code requires this, even if we don't - * need any decoding. To be fixed. - */ struct kvm_decode { unsigned long rt; bool sign_extend; + /* Witdth of the register accessed by the faulting instruction is 64-bits */ + bool sixty_four; }; void kvm_mmio_write_buf(void *buf, unsigned int len, unsigned long data); diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h index 7feb23fc2bacd5080955648330fa2ea7126b1172..957336254fc8e87bc7519497b28d5f8caa42e4ee 100644 --- a/arch/arm64/include/asm/memory.h +++ b/arch/arm64/include/asm/memory.h @@ -223,7 +223,7 @@ static inline unsigned long kaslr_offset(void) ((__force __typeof__(addr))sign_extend64((__force u64)(addr), 55)) #define untagged_addr(addr) ({ \ - u64 __addr = (__force u64)addr; \ + u64 __addr = (__force u64)(addr); \ __addr &= __untagged_addr(__addr); \ (__force __typeof__(addr))__addr; \ }) diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h index 64ce0887887efeb4a1a02059663d4fa8079e0ea2..c102d76a1cf6271cc4558d873404547755882be8 100644 --- a/arch/arm64/include/asm/mmu.h +++ b/arch/arm64/include/asm/mmu.h @@ -92,7 +92,7 @@ extern void init_mem_pgprot(void); extern void create_pgd_mapping(struct mm_struct *mm, phys_addr_t phys, unsigned long virt, phys_addr_t size, pgprot_t prot, bool page_mappings_only); -extern void *fixmap_remap_fdt(phys_addr_t dt_phys); +extern void *fixmap_remap_fdt(phys_addr_t dt_phys, int *size, pgprot_t prot); extern void mark_linear_text_alias_ro(void); #ifdef CONFIG_MEMORY_HOTPLUG extern void hotplug_paging(phys_addr_t start, phys_addr_t size); diff --git a/arch/arm64/include/asm/ptrace.h b/arch/arm64/include/asm/ptrace.h index 6bc43889d11e46cbfb79882da231eb9728e6b7e7..163970d5c4b118d14920c0e7aca3433fcc5ab2f2 100644 --- a/arch/arm64/include/asm/ptrace.h +++ b/arch/arm64/include/asm/ptrace.h @@ -50,6 +50,7 @@ #define PSR_AA32_I_BIT 0x00000080 #define PSR_AA32_A_BIT 0x00000100 #define PSR_AA32_E_BIT 0x00000200 +#define PSR_AA32_PAN_BIT 0x00400000 #define PSR_AA32_SSBS_BIT 0x00800000 #define PSR_AA32_DIT_BIT 0x01000000 #define PSR_AA32_Q_BIT 0x08000000 diff --git a/arch/arm64/include/uapi/asm/ptrace.h b/arch/arm64/include/uapi/asm/ptrace.h index b0fd1d3001543f9462d33a4ca1f0b342a7b7a68f..978ff79fba2b594862a4c92b2b11072292cf747b 100644 --- a/arch/arm64/include/uapi/asm/ptrace.h +++ b/arch/arm64/include/uapi/asm/ptrace.h @@ -49,6 +49,7 @@ #define PSR_SSBS_BIT 0x00001000 #define PSR_PAN_BIT 0x00400000 #define PSR_UAO_BIT 0x00800000 +#define PSR_DIT_BIT 0x01000000 #define PSR_V_BIT 0x10000000 #define PSR_C_BIT 0x20000000 #define PSR_Z_BIT 0x40000000 diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index 045251b6629dca22f0d85e397ff29e1b66fc1eab..49c903f5c8accdd14a02281d3d4798af6e9d7294 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -42,9 +42,7 @@ EXPORT_SYMBOL_GPL(elf_hwcap); #define COMPAT_ELF_HWCAP_DEFAULT \ (COMPAT_HWCAP_HALF|COMPAT_HWCAP_THUMB|\ COMPAT_HWCAP_FAST_MULT|COMPAT_HWCAP_EDSP|\ - COMPAT_HWCAP_TLS|COMPAT_HWCAP_VFP|\ - COMPAT_HWCAP_VFPv3|COMPAT_HWCAP_VFPv4|\ - COMPAT_HWCAP_NEON|COMPAT_HWCAP_IDIV|\ + COMPAT_HWCAP_TLS|COMPAT_HWCAP_IDIV|\ COMPAT_HWCAP_LPAE) unsigned int compat_elf_hwcap __read_mostly = COMPAT_ELF_HWCAP_DEFAULT; unsigned int compat_elf_hwcap2 __read_mostly; @@ -907,6 +905,7 @@ static bool unmap_kernel_at_el0(const struct arm64_cpu_capabilities *entry, MIDR_ALL_VERSIONS(MIDR_CORTEX_A57), MIDR_ALL_VERSIONS(MIDR_CORTEX_A72), MIDR_ALL_VERSIONS(MIDR_CORTEX_A73), + MIDR_ALL_VERSIONS(MIDR_KRYO5S), MIDR_ALL_VERSIONS(MIDR_HISI_TSV110), { /* sentinel */ } }; @@ -1243,7 +1242,7 @@ static const struct arm64_cpu_capabilities arm64_features[] = { { /* FP/SIMD is not implemented */ .capability = ARM64_HAS_NO_FPSIMD, - .type = ARM64_CPUCAP_SYSTEM_FEATURE, + .type = ARM64_CPUCAP_BOOT_RESTRICTED_CPU_LOCAL_FEATURE, .min_field_value = 0, .matches = has_no_fpsimd, }, @@ -1343,17 +1342,30 @@ static const struct arm64_cpu_capabilities arm64_features[] = { {}, }; -#define HWCAP_CAP(reg, field, s, min_value, cap_type, cap) \ - { \ - .desc = #cap, \ - .type = ARM64_CPUCAP_SYSTEM_FEATURE, \ + +#define HWCAP_CPUID_MATCH(reg, field, s, min_value) \ .matches = has_cpuid_feature, \ .sys_reg = reg, \ .field_pos = field, \ .sign = s, \ .min_field_value = min_value, \ + +#define __HWCAP_CAP(name, cap_type, cap) \ + .desc = name, \ + .type = ARM64_CPUCAP_SYSTEM_FEATURE, \ .hwcap_type = cap_type, \ .hwcap = cap, \ + +#define HWCAP_CAP(reg, field, s, min_value, cap_type, cap) \ + { \ + __HWCAP_CAP(#cap, cap_type, cap) \ + HWCAP_CPUID_MATCH(reg, field, s, min_value) \ + } + +#define HWCAP_CAP_MATCH(match, cap_type, cap) \ + { \ + __HWCAP_CAP(#cap, cap_type, cap) \ + .matches = match, \ } static const struct arm64_cpu_capabilities arm64_elf_hwcaps[] = { @@ -1389,8 +1401,35 @@ static const struct arm64_cpu_capabilities arm64_elf_hwcaps[] = { {}, }; +#ifdef CONFIG_COMPAT +static bool compat_has_neon(const struct arm64_cpu_capabilities *cap, int scope) +{ + /* + * Check that all of MVFR1_EL1.{SIMDSP, SIMDInt, SIMDLS} are available, + * in line with that of arm32 as in vfp_init(). We make sure that the + * check is future proof, by making sure value is non-zero. + */ + u32 mvfr1; + + WARN_ON(scope == SCOPE_LOCAL_CPU && preemptible()); + if (scope == SCOPE_SYSTEM) + mvfr1 = read_sanitised_ftr_reg(SYS_MVFR1_EL1); + else + mvfr1 = read_sysreg_s(SYS_MVFR1_EL1); + + return cpuid_feature_extract_unsigned_field(mvfr1, MVFR1_SIMDSP_SHIFT) && + cpuid_feature_extract_unsigned_field(mvfr1, MVFR1_SIMDINT_SHIFT) && + cpuid_feature_extract_unsigned_field(mvfr1, MVFR1_SIMDLS_SHIFT); +} +#endif + static const struct arm64_cpu_capabilities compat_elf_hwcaps[] = { #ifdef CONFIG_COMPAT + HWCAP_CAP_MATCH(compat_has_neon, CAP_COMPAT_HWCAP, COMPAT_HWCAP_NEON), + HWCAP_CAP(SYS_MVFR1_EL1, MVFR1_SIMDFMAC_SHIFT, FTR_UNSIGNED, 1, CAP_COMPAT_HWCAP, COMPAT_HWCAP_VFPv4), + /* Arm v8 mandates MVFR0.FPDP == {0, 2}. So, piggy back on this for the presence of VFP support */ + HWCAP_CAP(SYS_MVFR0_EL1, MVFR0_FPDP_SHIFT, FTR_UNSIGNED, 2, CAP_COMPAT_HWCAP, COMPAT_HWCAP_VFP), + HWCAP_CAP(SYS_MVFR0_EL1, MVFR0_FPDP_SHIFT, FTR_UNSIGNED, 2, CAP_COMPAT_HWCAP, COMPAT_HWCAP_VFPv3), HWCAP_CAP(SYS_ID_ISAR5_EL1, ID_ISAR5_AES_SHIFT, FTR_UNSIGNED, 2, CAP_COMPAT_HWCAP2, COMPAT_HWCAP2_PMULL), HWCAP_CAP(SYS_ID_ISAR5_EL1, ID_ISAR5_AES_SHIFT, FTR_UNSIGNED, 1, CAP_COMPAT_HWCAP2, COMPAT_HWCAP2_AES), HWCAP_CAP(SYS_ID_ISAR5_EL1, ID_ISAR5_SHA1_SHIFT, FTR_UNSIGNED, 1, CAP_COMPAT_HWCAP2, COMPAT_HWCAP2_SHA1), diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c index 58c53bc969289ac9dc85951286986db4d58af38b..14fdbaa6ee3aba950a1d6808982f5c966d2e38b5 100644 --- a/arch/arm64/kernel/fpsimd.c +++ b/arch/arm64/kernel/fpsimd.c @@ -218,6 +218,7 @@ static void sve_free(struct task_struct *task) static void task_fpsimd_load(void) { WARN_ON(!in_softirq() && !irqs_disabled()); + WARN_ON(!system_supports_fpsimd()); if (system_supports_sve() && test_thread_flag(TIF_SVE)) sve_load_state(sve_pffr(¤t->thread), @@ -238,6 +239,7 @@ void fpsimd_save(void) struct user_fpsimd_state *st = __this_cpu_read(fpsimd_last_state.st); /* set by fpsimd_bind_task_to_cpu() or fpsimd_bind_state_to_cpu() */ + WARN_ON(!system_supports_fpsimd()); WARN_ON(!in_softirq() && !irqs_disabled()); if (!test_thread_flag(TIF_FOREIGN_FPSTATE)) { @@ -977,6 +979,7 @@ void fpsimd_bind_task_to_cpu(void) struct fpsimd_last_state_struct *last = this_cpu_ptr(&fpsimd_last_state); + WARN_ON(!system_supports_fpsimd()); last->st = ¤t->thread.uw.fpsimd_state; current->thread.fpsimd_cpu = smp_processor_id(); @@ -996,6 +999,7 @@ void fpsimd_bind_state_to_cpu(struct user_fpsimd_state *st) struct fpsimd_last_state_struct *last = this_cpu_ptr(&fpsimd_last_state); + WARN_ON(!system_supports_fpsimd()); WARN_ON(!in_softirq() && !irqs_disabled()); last->st = st; @@ -1008,8 +1012,19 @@ void fpsimd_bind_state_to_cpu(struct user_fpsimd_state *st) */ void fpsimd_restore_current_state(void) { - if (!system_supports_fpsimd()) + /* + * For the tasks that were created before we detected the absence of + * FP/SIMD, the TIF_FOREIGN_FPSTATE could be set via fpsimd_thread_switch(), + * e.g, init. This could be then inherited by the children processes. + * If we later detect that the system doesn't support FP/SIMD, + * we must clear the flag for all the tasks to indicate that the + * FPSTATE is clean (as we can't have one) to avoid looping for ever in + * do_notify_resume(). + */ + if (!system_supports_fpsimd()) { + clear_thread_flag(TIF_FOREIGN_FPSTATE); return; + } local_bh_disable(); @@ -1028,7 +1043,7 @@ void fpsimd_restore_current_state(void) */ void fpsimd_update_current_state(struct user_fpsimd_state const *state) { - if (!system_supports_fpsimd()) + if (WARN_ON(!system_supports_fpsimd())) return; local_bh_disable(); @@ -1055,6 +1070,7 @@ void fpsimd_flush_task_state(struct task_struct *t) void fpsimd_flush_cpu_state(void) { + WARN_ON(!system_supports_fpsimd()); __this_cpu_write(fpsimd_last_state.st, NULL); set_thread_flag(TIF_FOREIGN_FPSTATE); } diff --git a/arch/arm64/kernel/hibernate.c b/arch/arm64/kernel/hibernate.c index 9859e1178e6bed174835ccb90b355f9f43c0a5f8..dbeeeffdb9c9e876a9a1e6a8925959a57e82d3ea 100644 --- a/arch/arm64/kernel/hibernate.c +++ b/arch/arm64/kernel/hibernate.c @@ -202,6 +202,7 @@ static int create_safe_exec_page(void *src_start, size_t length, gfp_t mask) { int rc = 0; + pgd_t *trans_pgd; pgd_t *pgdp; pud_t *pudp; pmd_t *pmdp; @@ -216,7 +217,13 @@ static int create_safe_exec_page(void *src_start, size_t length, memcpy((void *)dst, src_start, length); __flush_icache_range(dst, dst + length); - pgdp = pgd_offset_raw(allocator(mask), dst_addr); + trans_pgd = allocator(mask); + if (!trans_pgd) { + rc = -ENOMEM; + goto out; + } + + pgdp = pgd_offset_raw(trans_pgd, dst_addr); if (pgd_none(READ_ONCE(*pgdp))) { pudp = allocator(mask); if (!pudp) { diff --git a/arch/arm64/kernel/kaslr.c b/arch/arm64/kernel/kaslr.c index 06941c1fe418e0b2df2a8916ea90835ac748e6f6..92bb53460401c0660667340ed36f87a5f6a48a40 100644 --- a/arch/arm64/kernel/kaslr.c +++ b/arch/arm64/kernel/kaslr.c @@ -65,9 +65,6 @@ static __init const u8 *kaslr_get_cmdline(void *fdt) return default_cmdline; } -extern void *__init __fixmap_remap_fdt(phys_addr_t dt_phys, int *size, - pgprot_t prot); - /* * This routine will be executed with the kernel mapped at its default virtual * address, and if it returns successfully, the kernel will be remapped, and @@ -96,7 +93,7 @@ u64 __init kaslr_early_init(u64 dt_phys) * attempt at mapping the FDT in setup_machine() */ early_fixmap_init(); - fdt = __fixmap_remap_fdt(dt_phys, &size, PAGE_KERNEL); + fdt = fixmap_remap_fdt(dt_phys, &size, PAGE_KERNEL); if (!fdt) return 0; diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index fe9762440d27d2a80c4cbff50467b80f68133b58..1e3fe201f92c8aea3f1977452dee46a1552cef38 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -496,6 +496,13 @@ static void ssbs_thread_switch(struct task_struct *next) if (unlikely(next->flags & PF_KTHREAD)) return; + /* + * If all CPUs implement the SSBS extension, then we just need to + * context-switch the PSTATE field. + */ + if (cpu_have_feature(cpu_feature(SSBS))) + return; + /* If the mitigation is enabled, then we leave SSBS clear. */ if ((arm64_get_ssbd_state() == ARM64_SSBD_FORCE_ENABLE) || test_tsk_thread_flag(next, TIF_SSBD)) diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index 0211c3c7533b0a194e53cfbaca9fa3e7abd0d435..38aab5b34cc498f57d928c733404d18045caaf92 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -627,6 +627,13 @@ static int gpr_set(struct task_struct *target, const struct user_regset *regset, return 0; } +static int fpr_active(struct task_struct *target, const struct user_regset *regset) +{ + if (!system_supports_fpsimd()) + return -ENODEV; + return regset->n; +} + /* * TODO: update fp accessors for lazy context switching (sync/flush hwstate) */ @@ -649,6 +656,9 @@ static int fpr_get(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) { + if (!system_supports_fpsimd()) + return -EINVAL; + if (target == current) fpsimd_preserve_current_state(); @@ -688,6 +698,9 @@ static int fpr_set(struct task_struct *target, const struct user_regset *regset, { int ret; + if (!system_supports_fpsimd()) + return -EINVAL; + ret = __fpr_set(target, regset, pos, count, kbuf, ubuf, 0); if (ret) return ret; @@ -990,6 +1003,7 @@ static const struct user_regset aarch64_regsets[] = { */ .size = sizeof(u32), .align = sizeof(u32), + .active = fpr_active, .get = fpr_get, .set = fpr_set }, @@ -1176,6 +1190,9 @@ static int compat_vfp_get(struct task_struct *target, compat_ulong_t fpscr; int ret, vregs_end_pos; + if (!system_supports_fpsimd()) + return -EINVAL; + uregs = &target->thread.uw.fpsimd_state; if (target == current) @@ -1209,6 +1226,9 @@ static int compat_vfp_set(struct task_struct *target, compat_ulong_t fpscr; int ret, vregs_end_pos; + if (!system_supports_fpsimd()) + return -EINVAL; + uregs = &target->thread.uw.fpsimd_state; vregs_end_pos = VFP_STATE_SIZE - sizeof(compat_ulong_t); @@ -1266,6 +1286,7 @@ static const struct user_regset aarch32_regsets[] = { .n = VFP_STATE_SIZE / sizeof(compat_ulong_t), .size = sizeof(compat_ulong_t), .align = sizeof(compat_ulong_t), + .active = fpr_active, .get = compat_vfp_get, .set = compat_vfp_set }, diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index f049e548077c1e19a2a31f7114506157392bbbe9..6860d8b91904dbfed61fb89a9b3a28a4bae0d799 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -189,9 +189,13 @@ static void __init smp_build_mpidr_hash(void) static void __init setup_machine_fdt(phys_addr_t dt_phys) { - void *dt_virt = fixmap_remap_fdt(dt_phys); + int size; + void *dt_virt = fixmap_remap_fdt(dt_phys, &size, PAGE_KERNEL); const char *name; + if (dt_virt) + memblock_reserve(dt_phys, size); + if (!dt_virt || !early_init_dt_scan(dt_virt)) { pr_crit("\n" "Error: invalid device tree blob at physical address %pa (virtual address 0x%p)\n" @@ -203,6 +207,9 @@ static void __init setup_machine_fdt(phys_addr_t dt_phys) cpu_relax(); } + /* Early fixups are done, map the FDT as read-only now */ + fixmap_remap_fdt(dt_phys, &size, PAGE_KERNEL_RO); + name = of_flat_dt_get_machine_name(); if (!name) return; diff --git a/arch/arm64/kernel/vdso.c b/arch/arm64/kernel/vdso.c index ec0bb588d7553e0fa43290e3df75f7e22011b144..42b7082029e1d5003e509ffdbf4d916cbeb43dc7 100644 --- a/arch/arm64/kernel/vdso.c +++ b/arch/arm64/kernel/vdso.c @@ -146,8 +146,6 @@ static int __init vdso_init(void) } vdso_pages = (vdso_end - vdso_start) >> PAGE_SHIFT; - pr_info("vdso: %ld pages (%ld code @ %p, %ld data @ %p)\n", - vdso_pages + 1, vdso_pages, vdso_start, 1L, vdso_data); /* Allocate the vDSO pagelist, plus a page for the data. */ vdso_pagelist = kcalloc(vdso_pages + 1, sizeof(struct page *), diff --git a/arch/arm64/kvm/debug.c b/arch/arm64/kvm/debug.c index 00d422336a45225ea8f9b16c6a99ebf042ebf68c..4e722d73a3c34f0473bef535192049b53e04ab2e 100644 --- a/arch/arm64/kvm/debug.c +++ b/arch/arm64/kvm/debug.c @@ -112,7 +112,7 @@ void kvm_arm_reset_debug_ptr(struct kvm_vcpu *vcpu) void kvm_arm_setup_debug(struct kvm_vcpu *vcpu) { bool trap_debug = !(vcpu->arch.flags & KVM_ARM64_DEBUG_DIRTY); - unsigned long mdscr; + unsigned long mdscr, orig_mdcr_el2 = vcpu->arch.mdcr_el2; trace_kvm_arm_setup_debug(vcpu, vcpu->guest_debug); @@ -208,6 +208,10 @@ void kvm_arm_setup_debug(struct kvm_vcpu *vcpu) if (vcpu_read_sys_reg(vcpu, MDSCR_EL1) & (DBG_MDSCR_KDE | DBG_MDSCR_MDE)) vcpu->arch.flags |= KVM_ARM64_DEBUG_DIRTY; + /* Write mdcr_el2 changes since vcpu_load on VHE systems */ + if (has_vhe() && orig_mdcr_el2 != vcpu->arch.mdcr_el2) + write_sysreg(vcpu->arch.mdcr_el2, mdcr_el2); + trace_kvm_arm_set_dreg32("MDCR_EL2", vcpu->arch.mdcr_el2); trace_kvm_arm_set_dreg32("MDSCR_EL1", vcpu_read_sys_reg(vcpu, MDSCR_EL1)); } diff --git a/arch/arm64/kvm/hyp/switch.c b/arch/arm64/kvm/hyp/switch.c index 6290a4e81d57a26ae480659e0e944d22f64aa3b0..f3978931aaf4076028a6411cf286479e70c08570 100644 --- a/arch/arm64/kvm/hyp/switch.c +++ b/arch/arm64/kvm/hyp/switch.c @@ -37,7 +37,15 @@ /* Check whether the FP regs were dirtied while in the host-side run loop: */ static bool __hyp_text update_fp_enabled(struct kvm_vcpu *vcpu) { - if (vcpu->arch.host_thread_info->flags & _TIF_FOREIGN_FPSTATE) + /* + * When the system doesn't support FP/SIMD, we cannot rely on + * the _TIF_FOREIGN_FPSTATE flag. However, we always inject an + * abort on the very first access to FP and thus we should never + * see KVM_ARM64_FP_ENABLED. For added safety, make sure we always + * trap the accesses. + */ + if (!system_supports_fpsimd() || + vcpu->arch.host_thread_info->flags & _TIF_FOREIGN_FPSTATE) vcpu->arch.flags &= ~(KVM_ARM64_FP_ENABLED | KVM_ARM64_FP_HOST); diff --git a/arch/arm64/kvm/inject_fault.c b/arch/arm64/kvm/inject_fault.c index a55e91dfcf8fe8279c2dbe40c9747d26cb086e26..41c80c311367189b2e2f9ba4cd4f63c639377e3d 100644 --- a/arch/arm64/kvm/inject_fault.c +++ b/arch/arm64/kvm/inject_fault.c @@ -25,9 +25,6 @@ #include #include -#define PSTATE_FAULT_BITS_64 (PSR_MODE_EL1h | PSR_A_BIT | PSR_F_BIT | \ - PSR_I_BIT | PSR_D_BIT) - #define CURRENT_EL_SP_EL0_VECTOR 0x0 #define CURRENT_EL_SP_ELx_VECTOR 0x200 #define LOWER_EL_AArch64_VECTOR 0x400 @@ -61,6 +58,69 @@ static u64 get_except_vector(struct kvm_vcpu *vcpu, enum exception_type type) return vcpu_read_sys_reg(vcpu, VBAR_EL1) + exc_offset + type; } +/* + * When an exception is taken, most PSTATE fields are left unchanged in the + * handler. However, some are explicitly overridden (e.g. M[4:0]). Luckily all + * of the inherited bits have the same position in the AArch64/AArch32 SPSR_ELx + * layouts, so we don't need to shuffle these for exceptions from AArch32 EL0. + * + * For the SPSR_ELx layout for AArch64, see ARM DDI 0487E.a page C5-429. + * For the SPSR_ELx layout for AArch32, see ARM DDI 0487E.a page C5-426. + * + * Here we manipulate the fields in order of the AArch64 SPSR_ELx layout, from + * MSB to LSB. + */ +static unsigned long get_except64_pstate(struct kvm_vcpu *vcpu) +{ + unsigned long sctlr = vcpu_read_sys_reg(vcpu, SCTLR_EL1); + unsigned long old, new; + + old = *vcpu_cpsr(vcpu); + new = 0; + + new |= (old & PSR_N_BIT); + new |= (old & PSR_Z_BIT); + new |= (old & PSR_C_BIT); + new |= (old & PSR_V_BIT); + + // TODO: TCO (if/when ARMv8.5-MemTag is exposed to guests) + + new |= (old & PSR_DIT_BIT); + + // PSTATE.UAO is set to zero upon any exception to AArch64 + // See ARM DDI 0487E.a, page D5-2579. + + // PSTATE.PAN is unchanged unless SCTLR_ELx.SPAN == 0b0 + // SCTLR_ELx.SPAN is RES1 when ARMv8.1-PAN is not implemented + // See ARM DDI 0487E.a, page D5-2578. + new |= (old & PSR_PAN_BIT); + if (!(sctlr & SCTLR_EL1_SPAN)) + new |= PSR_PAN_BIT; + + // PSTATE.SS is set to zero upon any exception to AArch64 + // See ARM DDI 0487E.a, page D2-2452. + + // PSTATE.IL is set to zero upon any exception to AArch64 + // See ARM DDI 0487E.a, page D1-2306. + + // PSTATE.SSBS is set to SCTLR_ELx.DSSBS upon any exception to AArch64 + // See ARM DDI 0487E.a, page D13-3258 + if (sctlr & SCTLR_ELx_DSSBS) + new |= PSR_SSBS_BIT; + + // PSTATE.BTYPE is set to zero upon any exception to AArch64 + // See ARM DDI 0487E.a, pages D1-2293 to D1-2294. + + new |= PSR_D_BIT; + new |= PSR_A_BIT; + new |= PSR_I_BIT; + new |= PSR_F_BIT; + + new |= PSR_MODE_EL1h; + + return new; +} + static void inject_abt64(struct kvm_vcpu *vcpu, bool is_iabt, unsigned long addr) { unsigned long cpsr = *vcpu_cpsr(vcpu); @@ -70,7 +130,7 @@ static void inject_abt64(struct kvm_vcpu *vcpu, bool is_iabt, unsigned long addr vcpu_write_elr_el1(vcpu, *vcpu_pc(vcpu)); *vcpu_pc(vcpu) = get_except_vector(vcpu, except_type_sync); - *vcpu_cpsr(vcpu) = PSTATE_FAULT_BITS_64; + *vcpu_cpsr(vcpu) = get_except64_pstate(vcpu); vcpu_write_spsr(vcpu, cpsr); vcpu_write_sys_reg(vcpu, addr, FAR_EL1); @@ -105,7 +165,7 @@ static void inject_undef64(struct kvm_vcpu *vcpu) vcpu_write_elr_el1(vcpu, *vcpu_pc(vcpu)); *vcpu_pc(vcpu) = get_except_vector(vcpu, except_type_sync); - *vcpu_cpsr(vcpu) = PSTATE_FAULT_BITS_64; + *vcpu_cpsr(vcpu) = get_except64_pstate(vcpu); vcpu_write_spsr(vcpu, cpsr); /* diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c index 16a3d42c8caa7b5abe3e74d786d14c145364938c..4d0eba05fcaa36a5bf7fb3f9b082e82e31480eb4 100644 --- a/arch/arm64/mm/init.c +++ b/arch/arm64/mm/init.c @@ -896,21 +896,15 @@ static void kernel_physical_mapping_remove(unsigned long start, } -int arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap) +void arch_remove_memory(int nid, u64 start, u64 size, + struct vmem_altmap *altmap) { unsigned long start_pfn = start >> PAGE_SHIFT; unsigned long nr_pages = size >> PAGE_SHIFT; - struct page *page = pfn_to_page(start_pfn); - struct zone *zone; - int ret = 0; - zone = page_zone(page); - ret = __remove_pages(zone, start_pfn, nr_pages, altmap); - WARN_ON_ONCE(ret); + __remove_pages(start_pfn, nr_pages, altmap); kernel_physical_mapping_remove(start, start + size); - - return ret; } #endif /* CONFIG_MEMORY_HOTREMOVE */ diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index 70861832f9891b6dd4584dcb0c00518e2769e787..a5d0f1d1756589e874bb5529328a5e675fbd27d9 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -1303,7 +1303,7 @@ void __set_fixmap(enum fixed_addresses idx, } } -void *__init __fixmap_remap_fdt(phys_addr_t dt_phys, int *size, pgprot_t prot) +void *__init fixmap_remap_fdt(phys_addr_t dt_phys, int *size, pgprot_t prot) { const u64 dt_virt_base = __fix_to_virt(FIX_FDT); int offset; @@ -1356,25 +1356,9 @@ void *__init __fixmap_remap_fdt(phys_addr_t dt_phys, int *size, pgprot_t prot) return dt_virt; } -void *__init fixmap_remap_fdt(phys_addr_t dt_phys) +int __init arch_ioremap_p4d_supported(void) { - void *dt_virt; - int size; - - dt_virt = __fixmap_remap_fdt(dt_phys, &size, PAGE_KERNEL_RO); - if (!dt_virt) - return NULL; - - memblock_reserve(dt_phys, size); - - /* - * memblock_dbg is not up because of parse_early_param get called after - * setup_machine_fd. To capture fdt reserved info below pr_info is - * added. - */ - pr_info("memblock_reserve: 0x%x %pS\n", size - 1, (void *) _RET_IP_); - - return dt_virt; + return 0; } int __init arch_ioremap_pud_supported(void) diff --git a/arch/hexagon/include/asm/atomic.h b/arch/hexagon/include/asm/atomic.h index 311b9894ccc8d85d6a8b24078485c9f2ff3a16c3..15cca7f1fdedc482eefd391bf56e0a62f6a31b22 100644 --- a/arch/hexagon/include/asm/atomic.h +++ b/arch/hexagon/include/asm/atomic.h @@ -105,7 +105,7 @@ static inline void atomic_##op(int i, atomic_t *v) \ "1: %0 = memw_locked(%1);\n" \ " %0 = "#op "(%0,%2);\n" \ " memw_locked(%1,P3)=%0;\n" \ - " if !P3 jump 1b;\n" \ + " if (!P3) jump 1b;\n" \ : "=&r" (output) \ : "r" (&v->counter), "r" (i) \ : "memory", "p3" \ @@ -121,7 +121,7 @@ static inline int atomic_##op##_return(int i, atomic_t *v) \ "1: %0 = memw_locked(%1);\n" \ " %0 = "#op "(%0,%2);\n" \ " memw_locked(%1,P3)=%0;\n" \ - " if !P3 jump 1b;\n" \ + " if (!P3) jump 1b;\n" \ : "=&r" (output) \ : "r" (&v->counter), "r" (i) \ : "memory", "p3" \ @@ -138,7 +138,7 @@ static inline int atomic_fetch_##op(int i, atomic_t *v) \ "1: %0 = memw_locked(%2);\n" \ " %1 = "#op "(%0,%3);\n" \ " memw_locked(%2,P3)=%1;\n" \ - " if !P3 jump 1b;\n" \ + " if (!P3) jump 1b;\n" \ : "=&r" (output), "=&r" (val) \ : "r" (&v->counter), "r" (i) \ : "memory", "p3" \ @@ -187,7 +187,7 @@ static inline int atomic_fetch_add_unless(atomic_t *v, int a, int u) " }" " memw_locked(%2, p3) = %1;" " {" - " if !p3 jump 1b;" + " if (!p3) jump 1b;" " }" "2:" : "=&r" (__oldval), "=&r" (tmp) diff --git a/arch/hexagon/include/asm/bitops.h b/arch/hexagon/include/asm/bitops.h index 2691a1857d203db2522ae178fe744225c71d2da9..634306cda00675b0b172e87ad6162f6bf23df722 100644 --- a/arch/hexagon/include/asm/bitops.h +++ b/arch/hexagon/include/asm/bitops.h @@ -52,7 +52,7 @@ static inline int test_and_clear_bit(int nr, volatile void *addr) "1: R12 = memw_locked(R10);\n" " { P0 = tstbit(R12,R11); R12 = clrbit(R12,R11); }\n" " memw_locked(R10,P1) = R12;\n" - " {if !P1 jump 1b; %0 = mux(P0,#1,#0);}\n" + " {if (!P1) jump 1b; %0 = mux(P0,#1,#0);}\n" : "=&r" (oldval) : "r" (addr), "r" (nr) : "r10", "r11", "r12", "p0", "p1", "memory" @@ -76,7 +76,7 @@ static inline int test_and_set_bit(int nr, volatile void *addr) "1: R12 = memw_locked(R10);\n" " { P0 = tstbit(R12,R11); R12 = setbit(R12,R11); }\n" " memw_locked(R10,P1) = R12;\n" - " {if !P1 jump 1b; %0 = mux(P0,#1,#0);}\n" + " {if (!P1) jump 1b; %0 = mux(P0,#1,#0);}\n" : "=&r" (oldval) : "r" (addr), "r" (nr) : "r10", "r11", "r12", "p0", "p1", "memory" @@ -102,7 +102,7 @@ static inline int test_and_change_bit(int nr, volatile void *addr) "1: R12 = memw_locked(R10);\n" " { P0 = tstbit(R12,R11); R12 = togglebit(R12,R11); }\n" " memw_locked(R10,P1) = R12;\n" - " {if !P1 jump 1b; %0 = mux(P0,#1,#0);}\n" + " {if (!P1) jump 1b; %0 = mux(P0,#1,#0);}\n" : "=&r" (oldval) : "r" (addr), "r" (nr) : "r10", "r11", "r12", "p0", "p1", "memory" @@ -237,7 +237,7 @@ static inline int ffs(int x) int r; asm("{ P0 = cmp.eq(%1,#0); %0 = ct0(%1);}\n" - "{ if P0 %0 = #0; if !P0 %0 = add(%0,#1);}\n" + "{ if (P0) %0 = #0; if (!P0) %0 = add(%0,#1);}\n" : "=&r" (r) : "r" (x) : "p0"); diff --git a/arch/hexagon/include/asm/cmpxchg.h b/arch/hexagon/include/asm/cmpxchg.h index a6e34e2acbbaf6cc07f377db35faa7c744ba23e3..db258424059f4f5ab5257d928bd39e4071fe4a03 100644 --- a/arch/hexagon/include/asm/cmpxchg.h +++ b/arch/hexagon/include/asm/cmpxchg.h @@ -44,7 +44,7 @@ static inline unsigned long __xchg(unsigned long x, volatile void *ptr, __asm__ __volatile__ ( "1: %0 = memw_locked(%1);\n" /* load into retval */ " memw_locked(%1,P0) = %2;\n" /* store into memory */ - " if !P0 jump 1b;\n" + " if (!P0) jump 1b;\n" : "=&r" (retval) : "r" (ptr), "r" (x) : "memory", "p0" diff --git a/arch/hexagon/include/asm/futex.h b/arch/hexagon/include/asm/futex.h index c889f5993ecd35f1646e06fa1fab846860655ac6..e8e5e47afb377ad697d9b9de2ec0a9bd203c8df6 100644 --- a/arch/hexagon/include/asm/futex.h +++ b/arch/hexagon/include/asm/futex.h @@ -16,7 +16,7 @@ /* For example: %1 = %4 */ \ insn \ "2: memw_locked(%3,p2) = %1;\n" \ - " if !p2 jump 1b;\n" \ + " if (!p2) jump 1b;\n" \ " %1 = #0;\n" \ "3:\n" \ ".section .fixup,\"ax\"\n" \ @@ -84,10 +84,10 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, u32 oldval, "1: %1 = memw_locked(%3)\n" " {\n" " p2 = cmp.eq(%1,%4)\n" - " if !p2.new jump:NT 3f\n" + " if (!p2.new) jump:NT 3f\n" " }\n" "2: memw_locked(%3,p2) = %5\n" - " if !p2 jump 1b\n" + " if (!p2) jump 1b\n" "3:\n" ".section .fixup,\"ax\"\n" "4: %0 = #%6\n" diff --git a/arch/hexagon/include/asm/spinlock.h b/arch/hexagon/include/asm/spinlock.h index 48020863f53a875b2d372ee70c32a148153deac9..e090f6a151c2b0008adee34aeb4c378fbb0f1c41 100644 --- a/arch/hexagon/include/asm/spinlock.h +++ b/arch/hexagon/include/asm/spinlock.h @@ -44,9 +44,9 @@ static inline void arch_read_lock(arch_rwlock_t *lock) __asm__ __volatile__( "1: R6 = memw_locked(%0);\n" " { P3 = cmp.ge(R6,#0); R6 = add(R6,#1);}\n" - " { if !P3 jump 1b; }\n" + " { if (!P3) jump 1b; }\n" " memw_locked(%0,P3) = R6;\n" - " { if !P3 jump 1b; }\n" + " { if (!P3) jump 1b; }\n" : : "r" (&lock->lock) : "memory", "r6", "p3" @@ -60,7 +60,7 @@ static inline void arch_read_unlock(arch_rwlock_t *lock) "1: R6 = memw_locked(%0);\n" " R6 = add(R6,#-1);\n" " memw_locked(%0,P3) = R6\n" - " if !P3 jump 1b;\n" + " if (!P3) jump 1b;\n" : : "r" (&lock->lock) : "memory", "r6", "p3" @@ -75,7 +75,7 @@ static inline int arch_read_trylock(arch_rwlock_t *lock) __asm__ __volatile__( " R6 = memw_locked(%1);\n" " { %0 = #0; P3 = cmp.ge(R6,#0); R6 = add(R6,#1);}\n" - " { if !P3 jump 1f; }\n" + " { if (!P3) jump 1f; }\n" " memw_locked(%1,P3) = R6;\n" " { %0 = P3 }\n" "1:\n" @@ -92,9 +92,9 @@ static inline void arch_write_lock(arch_rwlock_t *lock) __asm__ __volatile__( "1: R6 = memw_locked(%0)\n" " { P3 = cmp.eq(R6,#0); R6 = #-1;}\n" - " { if !P3 jump 1b; }\n" + " { if (!P3) jump 1b; }\n" " memw_locked(%0,P3) = R6;\n" - " { if !P3 jump 1b; }\n" + " { if (!P3) jump 1b; }\n" : : "r" (&lock->lock) : "memory", "r6", "p3" @@ -108,7 +108,7 @@ static inline int arch_write_trylock(arch_rwlock_t *lock) __asm__ __volatile__( " R6 = memw_locked(%1)\n" " { %0 = #0; P3 = cmp.eq(R6,#0); R6 = #-1;}\n" - " { if !P3 jump 1f; }\n" + " { if (!P3) jump 1f; }\n" " memw_locked(%1,P3) = R6;\n" " %0 = P3;\n" "1:\n" @@ -131,9 +131,9 @@ static inline void arch_spin_lock(arch_spinlock_t *lock) __asm__ __volatile__( "1: R6 = memw_locked(%0);\n" " P3 = cmp.eq(R6,#0);\n" - " { if !P3 jump 1b; R6 = #1; }\n" + " { if (!P3) jump 1b; R6 = #1; }\n" " memw_locked(%0,P3) = R6;\n" - " { if !P3 jump 1b; }\n" + " { if (!P3) jump 1b; }\n" : : "r" (&lock->lock) : "memory", "r6", "p3" @@ -153,7 +153,7 @@ static inline unsigned int arch_spin_trylock(arch_spinlock_t *lock) __asm__ __volatile__( " R6 = memw_locked(%1);\n" " P3 = cmp.eq(R6,#0);\n" - " { if !P3 jump 1f; R6 = #1; %0 = #0; }\n" + " { if (!P3) jump 1f; R6 = #1; %0 = #0; }\n" " memw_locked(%1,P3) = R6;\n" " %0 = P3;\n" "1:\n" diff --git a/arch/hexagon/kernel/stacktrace.c b/arch/hexagon/kernel/stacktrace.c index 41866a06adf7f394c784e2d6703b6aa1665f4ab8..ec4ef682923d970e499b1cd1317f4dc112c69fa3 100644 --- a/arch/hexagon/kernel/stacktrace.c +++ b/arch/hexagon/kernel/stacktrace.c @@ -24,8 +24,6 @@ #include #include -register unsigned long current_frame_pointer asm("r30"); - struct stackframe { unsigned long fp; unsigned long rets; @@ -43,7 +41,7 @@ void save_stack_trace(struct stack_trace *trace) low = (unsigned long)task_stack_page(current); high = low + THREAD_SIZE; - fp = current_frame_pointer; + fp = (unsigned long)__builtin_frame_address(0); while (fp >= low && fp <= (high - sizeof(*frame))) { frame = (struct stackframe *)fp; diff --git a/arch/hexagon/kernel/vm_entry.S b/arch/hexagon/kernel/vm_entry.S index 67c6ccc14770320c21f334b16c13e1c480a99cd7..9f4a73ff720300dd3641e65df84ecb9d501a16fe 100644 --- a/arch/hexagon/kernel/vm_entry.S +++ b/arch/hexagon/kernel/vm_entry.S @@ -382,7 +382,7 @@ ret_from_fork: R26.L = #LO(do_work_pending); R0 = #VM_INT_DISABLE; } - if P0 jump check_work_pending + if (P0) jump check_work_pending { R0 = R25; callr R24 diff --git a/arch/ia64/kernel/signal.c b/arch/ia64/kernel/signal.c index d1234a5ba4c55e79a1b43366b4093925ad085698..9a960829a01d9d1901b1dfa0c226ec42a87fc532 100644 --- a/arch/ia64/kernel/signal.c +++ b/arch/ia64/kernel/signal.c @@ -110,7 +110,6 @@ ia64_rt_sigreturn (struct sigscratch *scr) { extern char ia64_strace_leave_kernel, ia64_leave_kernel; struct sigcontext __user *sc; - struct siginfo si; sigset_t set; long retval; @@ -153,14 +152,7 @@ ia64_rt_sigreturn (struct sigscratch *scr) return retval; give_sigsegv: - clear_siginfo(&si); - si.si_signo = SIGSEGV; - si.si_errno = 0; - si.si_code = SI_KERNEL; - si.si_pid = task_pid_vnr(current); - si.si_uid = from_kuid_munged(current_user_ns(), current_uid()); - si.si_addr = sc; - force_sig_info(SIGSEGV, &si, current); + force_sig(SIGSEGV, current); return retval; } @@ -231,37 +223,6 @@ rbs_on_sig_stack (unsigned long bsp) return (bsp - current->sas_ss_sp < current->sas_ss_size); } -static long -force_sigsegv_info (int sig, void __user *addr) -{ - unsigned long flags; - struct siginfo si; - - clear_siginfo(&si); - if (sig == SIGSEGV) { - /* - * Acquiring siglock around the sa_handler-update is almost - * certainly overkill, but this isn't a - * performance-critical path and I'd rather play it safe - * here than having to debug a nasty race if and when - * something changes in kernel/signal.c that would make it - * no longer safe to modify sa_handler without holding the - * lock. - */ - spin_lock_irqsave(¤t->sighand->siglock, flags); - current->sighand->action[sig - 1].sa.sa_handler = SIG_DFL; - spin_unlock_irqrestore(¤t->sighand->siglock, flags); - } - si.si_signo = SIGSEGV; - si.si_errno = 0; - si.si_code = SI_KERNEL; - si.si_pid = task_pid_vnr(current); - si.si_uid = from_kuid_munged(current_user_ns(), current_uid()); - si.si_addr = addr; - force_sig_info(SIGSEGV, &si, current); - return 1; -} - static long setup_frame(struct ksignal *ksig, sigset_t *set, struct sigscratch *scr) { @@ -295,15 +256,18 @@ setup_frame(struct ksignal *ksig, sigset_t *set, struct sigscratch *scr) * instead so we will die with SIGSEGV. */ check_sp = (new_sp - sizeof(*frame)) & -STACK_ALIGN; - if (!likely(on_sig_stack(check_sp))) - return force_sigsegv_info(ksig->sig, (void __user *) - check_sp); + if (!likely(on_sig_stack(check_sp))) { + force_sigsegv(ksig->sig, current); + return 1; + } } } frame = (void __user *) ((new_sp - sizeof(*frame)) & -STACK_ALIGN); - if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) - return force_sigsegv_info(ksig->sig, frame); + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) { + force_sigsegv(ksig->sig, current); + return 1; + } err = __put_user(ksig->sig, &frame->arg0); err |= __put_user(&frame->info, &frame->arg1); @@ -317,8 +281,10 @@ setup_frame(struct ksignal *ksig, sigset_t *set, struct sigscratch *scr) err |= __save_altstack(&frame->sc.sc_stack, scr->pt.r12); err |= setup_sigcontext(&frame->sc, set, scr); - if (unlikely(err)) - return force_sigsegv_info(ksig->sig, frame); + if (unlikely(err)) { + force_sigsegv(ksig->sig, current); + return 1; + } scr->pt.r12 = (unsigned long) frame - 16; /* new stack pointer */ scr->pt.ar_fpsr = FPSR_DEFAULT; /* reset fpsr for signal handler */ diff --git a/arch/ia64/mm/init.c b/arch/ia64/mm/init.c index 3b85c3ecac38d2ae0860bc0d5aedd79f1cc96a39..79e5cc70f1fddbda61199f449b3b2c32732330e4 100644 --- a/arch/ia64/mm/init.c +++ b/arch/ia64/mm/init.c @@ -661,21 +661,12 @@ int arch_add_memory(int nid, u64 start, u64 size, struct vmem_altmap *altmap, return ret; } -#ifdef CONFIG_MEMORY_HOTREMOVE -int arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap) +void arch_remove_memory(int nid, u64 start, u64 size, + struct vmem_altmap *altmap) { unsigned long start_pfn = start >> PAGE_SHIFT; unsigned long nr_pages = size >> PAGE_SHIFT; - struct zone *zone; - int ret; - - zone = page_zone(pfn_to_page(start_pfn)); - ret = __remove_pages(zone, start_pfn, nr_pages, altmap); - if (ret) - pr_warn("%s: Problem encountered in __remove_pages() as" - " ret=%d\n", __func__, ret); - return ret; + __remove_pages(start_pfn, nr_pages, altmap); } #endif -#endif diff --git a/arch/m68k/amiga/cia.c b/arch/m68k/amiga/cia.c index 2081b8cd5591c6d77385de8594e737c9d421f7be..b9aee983e6f4cda91fcc709abb85471d2f5f4b63 100644 --- a/arch/m68k/amiga/cia.c +++ b/arch/m68k/amiga/cia.c @@ -88,10 +88,19 @@ static irqreturn_t cia_handler(int irq, void *dev_id) struct ciabase *base = dev_id; int mach_irq; unsigned char ints; + unsigned long flags; + /* Interrupts get disabled while the timer irq flag is cleared and + * the timer interrupt serviced. + */ mach_irq = base->cia_irq; + local_irq_save(flags); ints = cia_set_irq(base, CIA_ICR_ALL); amiga_custom.intreq = base->int_mask; + if (ints & 1) + generic_handle_irq(mach_irq); + local_irq_restore(flags); + mach_irq++, ints >>= 1; for (; ints; mach_irq++, ints >>= 1) { if (ints & 1) generic_handle_irq(mach_irq); diff --git a/arch/m68k/atari/ataints.c b/arch/m68k/atari/ataints.c index 3d2b63bedf0589c67cdf0471007157bb913f18ac..56f02ea2c248d844e43fb88a4e3742315d741400 100644 --- a/arch/m68k/atari/ataints.c +++ b/arch/m68k/atari/ataints.c @@ -142,7 +142,7 @@ struct mfptimerbase { .name = "MFP Timer D" }; -static irqreturn_t mfptimer_handler(int irq, void *dev_id) +static irqreturn_t mfp_timer_d_handler(int irq, void *dev_id) { struct mfptimerbase *base = dev_id; int mach_irq; @@ -344,7 +344,7 @@ void __init atari_init_IRQ(void) st_mfp.tim_ct_cd = (st_mfp.tim_ct_cd & 0xf0) | 0x6; /* request timer D dispatch handler */ - if (request_irq(IRQ_MFP_TIMD, mfptimer_handler, IRQF_SHARED, + if (request_irq(IRQ_MFP_TIMD, mfp_timer_d_handler, IRQF_SHARED, stmfp_base.name, &stmfp_base)) pr_err("Couldn't register %s interrupt\n", stmfp_base.name); diff --git a/arch/m68k/atari/time.c b/arch/m68k/atari/time.c index 9cca64286464c31ae9b961609f67c3afbbb25d73..fafa20f75ab9a969fecb66b00726e8f0297b4eac 100644 --- a/arch/m68k/atari/time.c +++ b/arch/m68k/atari/time.c @@ -24,6 +24,18 @@ DEFINE_SPINLOCK(rtc_lock); EXPORT_SYMBOL_GPL(rtc_lock); +static irqreturn_t mfp_timer_c_handler(int irq, void *dev_id) +{ + irq_handler_t timer_routine = dev_id; + unsigned long flags; + + local_irq_save(flags); + timer_routine(0, NULL); + local_irq_restore(flags); + + return IRQ_HANDLED; +} + void __init atari_sched_init(irq_handler_t timer_routine) { @@ -32,7 +44,8 @@ atari_sched_init(irq_handler_t timer_routine) /* start timer C, div = 1:100 */ st_mfp.tim_ct_cd = (st_mfp.tim_ct_cd & 15) | 0x60; /* install interrupt service routine for MFP Timer C */ - if (request_irq(IRQ_MFP_TIMC, timer_routine, 0, "timer", timer_routine)) + if (request_irq(IRQ_MFP_TIMC, mfp_timer_c_handler, 0, "timer", + timer_routine)) pr_err("Couldn't register timer interrupt\n"); } diff --git a/arch/m68k/bvme6000/config.c b/arch/m68k/bvme6000/config.c index 143ee9fa3893ecce16e13c87309354a6d5c6211e..0e5efed4da8653170597d83368ff0eda02165e7a 100644 --- a/arch/m68k/bvme6000/config.c +++ b/arch/m68k/bvme6000/config.c @@ -44,11 +44,6 @@ extern int bvme6000_hwclk (int, struct rtc_time *); extern void bvme6000_reset (void); void bvme6000_set_vectors (void); -/* Save tick handler routine pointer, will point to xtime_update() in - * kernel/timer/timekeeping.c, called via bvme6000_process_int() */ - -static irq_handler_t tick_handler; - int __init bvme6000_parse_bootinfo(const struct bi_record *bi) { @@ -157,12 +152,18 @@ irqreturn_t bvme6000_abort_int (int irq, void *dev_id) static irqreturn_t bvme6000_timer_int (int irq, void *dev_id) { + irq_handler_t timer_routine = dev_id; + unsigned long flags; volatile RtcPtr_t rtc = (RtcPtr_t)BVME_RTC_BASE; - unsigned char msr = rtc->msr & 0xc0; + unsigned char msr; + local_irq_save(flags); + msr = rtc->msr & 0xc0; rtc->msr = msr | 0x20; /* Ack the interrupt */ + timer_routine(0, NULL); + local_irq_restore(flags); - return tick_handler(irq, dev_id); + return IRQ_HANDLED; } /* @@ -181,9 +182,8 @@ void bvme6000_sched_init (irq_handler_t timer_routine) rtc->msr = 0; /* Ensure timer registers accessible */ - tick_handler = timer_routine; - if (request_irq(BVME_IRQ_RTC, bvme6000_timer_int, 0, - "timer", bvme6000_timer_int)) + if (request_irq(BVME_IRQ_RTC, bvme6000_timer_int, 0, "timer", + timer_routine)) panic ("Couldn't register timer int"); rtc->t1cr_omr = 0x04; /* Mode 2, ext clk */ diff --git a/arch/m68k/hp300/time.c b/arch/m68k/hp300/time.c index 289d928a46cbe1c2cbcce1f5e94dc9fbd43db331..d30b03ea93a27e39c1ea0637b2aec5198d28841a 100644 --- a/arch/m68k/hp300/time.c +++ b/arch/m68k/hp300/time.c @@ -38,13 +38,19 @@ static irqreturn_t hp300_tick(int irq, void *dev_id) { + irq_handler_t timer_routine = dev_id; + unsigned long flags; unsigned long tmp; - irq_handler_t vector = dev_id; + + local_irq_save(flags); in_8(CLOCKBASE + CLKSR); asm volatile ("movpw %1@(5),%0" : "=d" (tmp) : "a" (CLOCKBASE)); + timer_routine(0, NULL); + local_irq_restore(flags); + /* Turn off the network and SCSI leds */ blinken_leds(0, 0xe0); - return vector(irq, NULL); + return IRQ_HANDLED; } u32 hp300_gettimeoffset(void) diff --git a/arch/m68k/mac/via.c b/arch/m68k/mac/via.c index acdabbeecfd2e3c9a82ad528f8d2c05d1a335980..038d5a1c4d4844e534b62e91d4310f7460c27e93 100644 --- a/arch/m68k/mac/via.c +++ b/arch/m68k/mac/via.c @@ -54,16 +54,6 @@ static __u8 rbv_clear; static int gIER,gIFR,gBufA,gBufB; -/* - * Timer defs. - */ - -#define TICK_SIZE 10000 -#define MAC_CLOCK_TICK (783300/HZ) /* ticks per HZ */ -#define MAC_CLOCK_LOW (MAC_CLOCK_TICK&0xFF) -#define MAC_CLOCK_HIGH (MAC_CLOCK_TICK>>8) - - /* * On Macs with a genuine VIA chip there is no way to mask an individual slot * interrupt. This limitation also seems to apply to VIA clone logic cores in @@ -267,22 +257,6 @@ void __init via_init(void) } } -/* - * Start the 100 Hz clock - */ - -void __init via_init_clock(irq_handler_t func) -{ - via1[vACR] |= 0x40; - via1[vT1LL] = MAC_CLOCK_LOW; - via1[vT1LH] = MAC_CLOCK_HIGH; - via1[vT1CL] = MAC_CLOCK_LOW; - via1[vT1CH] = MAC_CLOCK_HIGH; - - if (request_irq(IRQ_MAC_TIMER_1, func, 0, "timer", func)) - pr_err("Couldn't register %s interrupt\n", "timer"); -} - /* * Debugging dump, used in various places to see what's going on. */ @@ -310,29 +284,6 @@ void via_debug_dump(void) } } -/* - * This is always executed with interrupts disabled. - * - * TBI: get time offset between scheduling timer ticks - */ - -u32 mac_gettimeoffset(void) -{ - unsigned long ticks, offset = 0; - - /* read VIA1 timer 2 current value */ - ticks = via1[vT1CL] | (via1[vT1CH] << 8); - /* The probability of underflow is less than 2% */ - if (ticks > MAC_CLOCK_TICK - MAC_CLOCK_TICK / 50) - /* Check for pending timer interrupt in VIA1 IFR */ - if (via1[vIFR] & 0x40) offset = TICK_SIZE; - - ticks = MAC_CLOCK_TICK - ticks; - ticks = ticks * 10000L / MAC_CLOCK_TICK; - - return (ticks + offset) * 1000; -} - /* * Flush the L2 cache on Macs that have it by flipping * the system into 24-bit mode for an instant. @@ -436,6 +387,8 @@ void via_nubus_irq_shutdown(int irq) * via6522.c :-), disable/pending masks added. */ +#define VIA_TIMER_1_INT BIT(6) + void via1_irq(struct irq_desc *desc) { int irq_num; @@ -445,6 +398,21 @@ void via1_irq(struct irq_desc *desc) if (!events) return; + irq_num = IRQ_MAC_TIMER_1; + irq_bit = VIA_TIMER_1_INT; + if (events & irq_bit) { + unsigned long flags; + + local_irq_save(flags); + via1[vIFR] = irq_bit; + generic_handle_irq(irq_num); + local_irq_restore(flags); + + events &= ~irq_bit; + if (!events) + return; + } + irq_num = VIA1_SOURCE_BASE; irq_bit = 1; do { @@ -601,3 +569,56 @@ int via2_scsi_drq_pending(void) return via2[gIFR] & (1 << IRQ_IDX(IRQ_MAC_SCSIDRQ)); } EXPORT_SYMBOL(via2_scsi_drq_pending); + +/* timer and clock source */ + +#define VIA_CLOCK_FREQ 783360 /* VIA "phase 2" clock in Hz */ +#define VIA_TIMER_INTERVAL (1000000 / HZ) /* microseconds per jiffy */ +#define VIA_TIMER_CYCLES (VIA_CLOCK_FREQ / HZ) /* clock cycles per jiffy */ + +#define VIA_TC (VIA_TIMER_CYCLES - 2) /* including 0 and -1 */ +#define VIA_TC_LOW (VIA_TC & 0xFF) +#define VIA_TC_HIGH (VIA_TC >> 8) + +void __init via_init_clock(irq_handler_t timer_routine) +{ + if (request_irq(IRQ_MAC_TIMER_1, timer_routine, 0, "timer", NULL)) { + pr_err("Couldn't register %s interrupt\n", "timer"); + return; + } + + via1[vT1LL] = VIA_TC_LOW; + via1[vT1LH] = VIA_TC_HIGH; + via1[vT1CL] = VIA_TC_LOW; + via1[vT1CH] = VIA_TC_HIGH; + via1[vACR] |= 0x40; +} + +u32 mac_gettimeoffset(void) +{ + unsigned long flags; + u8 count_high; + u16 count, offset = 0; + + /* + * Timer counter wrap-around is detected with the timer interrupt flag + * but reading the counter low byte (vT1CL) would reset the flag. + * Also, accessing both counter registers is essentially a data race. + * These problems are avoided by ignoring the low byte. Clock accuracy + * is 256 times worse (error can reach 0.327 ms) but CPU overhead is + * reduced by avoiding slow VIA register accesses. + */ + + local_irq_save(flags); + count_high = via1[vT1CH]; + if (count_high == 0xFF) + count_high = 0; + if (count_high > 0 && (via1[vIFR] & VIA_TIMER_1_INT)) + offset = VIA_TIMER_CYCLES; + local_irq_restore(flags); + + count = count_high << 8; + count = VIA_TIMER_CYCLES - count + offset; + + return ((count * VIA_TIMER_INTERVAL) / VIA_TIMER_CYCLES) * 1000; +} diff --git a/arch/m68k/mvme147/config.c b/arch/m68k/mvme147/config.c index adea549d240e9eb555200c7ec7db49582373e2b7..93c68d2b8e0ea25ecdc3852e1a1b784c040f2477 100644 --- a/arch/m68k/mvme147/config.c +++ b/arch/m68k/mvme147/config.c @@ -45,11 +45,6 @@ extern void mvme147_reset (void); static int bcd2int (unsigned char b); -/* Save tick handler routine pointer, will point to xtime_update() in - * kernel/time/timekeeping.c, called via mvme147_process_int() */ - -irq_handler_t tick_handler; - int __init mvme147_parse_bootinfo(const struct bi_record *bi) { @@ -104,16 +99,23 @@ void __init config_mvme147(void) static irqreturn_t mvme147_timer_int (int irq, void *dev_id) { + irq_handler_t timer_routine = dev_id; + unsigned long flags; + + local_irq_save(flags); m147_pcc->t1_int_cntrl = PCC_TIMER_INT_CLR; m147_pcc->t1_int_cntrl = PCC_INT_ENAB|PCC_LEVEL_TIMER1; - return tick_handler(irq, dev_id); + timer_routine(0, NULL); + local_irq_restore(flags); + + return IRQ_HANDLED; } void mvme147_sched_init (irq_handler_t timer_routine) { - tick_handler = timer_routine; - if (request_irq(PCC_IRQ_TIMER1, mvme147_timer_int, 0, "timer 1", NULL)) + if (request_irq(PCC_IRQ_TIMER1, mvme147_timer_int, 0, "timer 1", + timer_routine)) pr_err("Couldn't register timer interrupt\n"); /* Init the clock with a value */ diff --git a/arch/m68k/mvme16x/config.c b/arch/m68k/mvme16x/config.c index 6ee36a5b528d80b9b519c8ea97775be38114704f..5feb3ab484d081d62a7c3770947d6692cdde1ff4 100644 --- a/arch/m68k/mvme16x/config.c +++ b/arch/m68k/mvme16x/config.c @@ -50,11 +50,6 @@ extern void mvme16x_reset (void); int bcd2int (unsigned char b); -/* Save tick handler routine pointer, will point to xtime_update() in - * kernel/time/timekeeping.c, called via mvme16x_process_int() */ - -static irq_handler_t tick_handler; - unsigned short mvme16x_config; EXPORT_SYMBOL(mvme16x_config); @@ -352,8 +347,15 @@ static irqreturn_t mvme16x_abort_int (int irq, void *dev_id) static irqreturn_t mvme16x_timer_int (int irq, void *dev_id) { - *(volatile unsigned char *)0xfff4201b |= 8; - return tick_handler(irq, dev_id); + irq_handler_t timer_routine = dev_id; + unsigned long flags; + + local_irq_save(flags); + *(volatile unsigned char *)0xfff4201b |= 8; + timer_routine(0, NULL); + local_irq_restore(flags); + + return IRQ_HANDLED; } void mvme16x_sched_init (irq_handler_t timer_routine) @@ -361,14 +363,13 @@ void mvme16x_sched_init (irq_handler_t timer_routine) uint16_t brdno = be16_to_cpu(mvme_bdid.brdno); int irq; - tick_handler = timer_routine; /* Using PCCchip2 or MC2 chip tick timer 1 */ *(volatile unsigned long *)0xfff42008 = 0; *(volatile unsigned long *)0xfff42004 = 10000; /* 10ms */ *(volatile unsigned char *)0xfff42017 |= 3; *(volatile unsigned char *)0xfff4201b = 0x16; - if (request_irq(MVME16x_IRQ_TIMER, mvme16x_timer_int, 0, - "timer", mvme16x_timer_int)) + if (request_irq(MVME16x_IRQ_TIMER, mvme16x_timer_int, 0, "timer", + timer_routine)) panic ("Couldn't register timer int"); if (brdno == 0x0162 || brdno == 0x172) diff --git a/arch/m68k/q40/q40ints.c b/arch/m68k/q40/q40ints.c index 3e7603202977e715a6afc07e4ca0f3d713e0c3ed..1c696906c159f5acb5d39f773324d93bfa5508b5 100644 --- a/arch/m68k/q40/q40ints.c +++ b/arch/m68k/q40/q40ints.c @@ -127,10 +127,10 @@ void q40_mksound(unsigned int hz, unsigned int ticks) sound_ticks = ticks << 1; } -static irq_handler_t q40_timer_routine; - -static irqreturn_t q40_timer_int (int irq, void * dev) +static irqreturn_t q40_timer_int(int irq, void *dev_id) { + irq_handler_t timer_routine = dev_id; + ql_ticks = ql_ticks ? 0 : 1; if (sound_ticks) { unsigned char sval=(sound_ticks & 1) ? 128-SVOL : 128+SVOL; @@ -139,8 +139,13 @@ static irqreturn_t q40_timer_int (int irq, void * dev) *DAC_RIGHT=sval; } - if (!ql_ticks) - q40_timer_routine(irq, dev); + if (!ql_ticks) { + unsigned long flags; + + local_irq_save(flags); + timer_routine(0, NULL); + local_irq_restore(flags); + } return IRQ_HANDLED; } @@ -148,11 +153,9 @@ void q40_sched_init (irq_handler_t timer_routine) { int timer_irq; - q40_timer_routine = timer_routine; timer_irq = Q40_IRQ_FRAME; - if (request_irq(timer_irq, q40_timer_int, 0, - "timer", q40_timer_int)) + if (request_irq(timer_irq, q40_timer_int, 0, "timer", timer_routine)) panic("Couldn't register timer int"); master_outb(-1, FRAME_CLEAR_REG); diff --git a/arch/m68k/sun3/sun3ints.c b/arch/m68k/sun3/sun3ints.c index 6bbca30c91884850e45cc07db24b4b17a00b06e5..a5824abb4a39c2f75a42d3ae8a8a93ee038da465 100644 --- a/arch/m68k/sun3/sun3ints.c +++ b/arch/m68k/sun3/sun3ints.c @@ -61,8 +61,10 @@ static irqreturn_t sun3_int7(int irq, void *dev_id) static irqreturn_t sun3_int5(int irq, void *dev_id) { + unsigned long flags; unsigned int cnt; + local_irq_save(flags); #ifdef CONFIG_SUN3 intersil_clear(); #endif @@ -76,6 +78,7 @@ static irqreturn_t sun3_int5(int irq, void *dev_id) cnt = kstat_irqs_cpu(irq, 0); if (!(cnt % 20)) sun3_leds(led_pattern[cnt % 160 / 20]); + local_irq_restore(flags); return IRQ_HANDLED; } diff --git a/arch/m68k/sun3x/time.c b/arch/m68k/sun3x/time.c index 047e2bcee3d7a5710b42e561b0c1a9cc03d3a97e..3c8a86d0850842b4f17bdc6eb4e9e77f25580311 100644 --- a/arch/m68k/sun3x/time.c +++ b/arch/m68k/sun3x/time.c @@ -80,15 +80,19 @@ u32 sun3x_gettimeoffset(void) } #if 0 -static void sun3x_timer_tick(int irq, void *dev_id, struct pt_regs *regs) +static irqreturn_t sun3x_timer_tick(int irq, void *dev_id) { - void (*vector)(int, void *, struct pt_regs *) = dev_id; + irq_handler_t timer_routine = dev_id; + unsigned long flags; - /* Clear the pending interrupt - pulse the enable line low */ - disable_irq(5); - enable_irq(5); + local_irq_save(flags); + /* Clear the pending interrupt - pulse the enable line low */ + disable_irq(5); + enable_irq(5); + timer_routine(0, NULL); + local_irq_restore(flags); - vector(irq, NULL, regs); + return IRQ_HANDLED; } #endif diff --git a/arch/microblaze/kernel/cpu/cache.c b/arch/microblaze/kernel/cpu/cache.c index 0bde47e4fa694264256c1e8d2498436acc66320b..dcba53803fa5f9867eba2a0178c48ee5ed924c20 100644 --- a/arch/microblaze/kernel/cpu/cache.c +++ b/arch/microblaze/kernel/cpu/cache.c @@ -92,7 +92,8 @@ static inline void __disable_dcache_nomsr(void) #define CACHE_LOOP_LIMITS(start, end, cache_line_length, cache_size) \ do { \ int align = ~(cache_line_length - 1); \ - end = min(start + cache_size, end); \ + if (start < UINT_MAX - cache_size) \ + end = min(start + cache_size, end); \ start &= align; \ } while (0) diff --git a/arch/mips/Makefile.postlink b/arch/mips/Makefile.postlink index 4eea4188cb204a2b789ef0bdb4d8ce11d4d201f4..13e0beb9eee3330a6107d5e9b0d0cd9156e2f184 100644 --- a/arch/mips/Makefile.postlink +++ b/arch/mips/Makefile.postlink @@ -12,7 +12,7 @@ __archpost: include scripts/Kbuild.include CMD_RELOCS = arch/mips/boot/tools/relocs -quiet_cmd_relocs = RELOCS $@ +quiet_cmd_relocs = RELOCS $@ cmd_relocs = $(CMD_RELOCS) $@ # `@true` prevents complaint when there is nothing to be done diff --git a/arch/mips/bcm63xx/Makefile b/arch/mips/bcm63xx/Makefile index c69f297fc1df3d29d3ddd9c1147f58a7cbb59847..d89651e538f64042857c63692b7ffc2b3ae35271 100644 --- a/arch/mips/bcm63xx/Makefile +++ b/arch/mips/bcm63xx/Makefile @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 obj-y += clk.o cpu.o cs.o gpio.o irq.o nvram.o prom.o reset.o \ - setup.o timer.o dev-dsp.o dev-enet.o dev-flash.o \ - dev-pcmcia.o dev-rng.o dev-spi.o dev-hsspi.o dev-uart.o \ - dev-wdt.o dev-usb-usbd.o + setup.o timer.o dev-enet.o dev-flash.o dev-pcmcia.o \ + dev-rng.o dev-spi.o dev-hsspi.o dev-uart.o dev-wdt.o \ + dev-usb-usbd.o obj-$(CONFIG_EARLY_PRINTK) += early_printk.o obj-y += boards/ diff --git a/arch/mips/bcm63xx/boards/board_bcm963xx.c b/arch/mips/bcm63xx/boards/board_bcm963xx.c index b2097c0d2ed78b554ab321c6620767d3ccd29f71..36ec3dc2c999a94c9771408f3efc5e3c72972f22 100644 --- a/arch/mips/bcm63xx/boards/board_bcm963xx.c +++ b/arch/mips/bcm63xx/boards/board_bcm963xx.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -289,14 +288,6 @@ static struct board_info __initdata board_96348gw_10 = { .has_pccard = 1, .has_ehci0 = 1, - .has_dsp = 1, - .dsp = { - .gpio_rst = 6, - .gpio_int = 34, - .cs = 2, - .ext_irq = 2, - }, - .leds = { { .name = "adsl-fail", @@ -401,14 +392,6 @@ static struct board_info __initdata board_96348gw = { .has_ohci0 = 1, - .has_dsp = 1, - .dsp = { - .gpio_rst = 6, - .gpio_int = 34, - .ext_irq = 2, - .cs = 2, - }, - .leds = { { .name = "adsl-fail", @@ -898,9 +881,6 @@ int __init board_register_devices(void) if (board.has_usbd) bcm63xx_usbd_register(&board.usbd); - if (board.has_dsp) - bcm63xx_dsp_register(&board.dsp); - /* Generate MAC address for WLAN and register our SPROM, * do this after registering enet devices */ diff --git a/arch/mips/bcm63xx/dev-dsp.c b/arch/mips/bcm63xx/dev-dsp.c deleted file mode 100644 index 5bb5b154c9bd3cb4aec195f3c1e991b6011d8e96..0000000000000000000000000000000000000000 --- a/arch/mips/bcm63xx/dev-dsp.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Broadcom BCM63xx VoIP DSP registration - * - * 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) 2009 Florian Fainelli - */ - -#include -#include -#include - -#include -#include -#include -#include - -static struct resource voip_dsp_resources[] = { - { - .start = -1, /* filled at runtime */ - .end = -1, /* filled at runtime */ - .flags = IORESOURCE_MEM, - }, - { - .start = -1, /* filled at runtime */ - .flags = IORESOURCE_IRQ, - }, -}; - -static struct platform_device bcm63xx_voip_dsp_device = { - .name = "bcm63xx-voip-dsp", - .id = -1, - .num_resources = ARRAY_SIZE(voip_dsp_resources), - .resource = voip_dsp_resources, -}; - -int __init bcm63xx_dsp_register(const struct bcm63xx_dsp_platform_data *pd) -{ - struct bcm63xx_dsp_platform_data *dpd; - u32 val; - - /* Get the memory window */ - val = bcm_mpi_readl(MPI_CSBASE_REG(pd->cs - 1)); - val &= MPI_CSBASE_BASE_MASK; - voip_dsp_resources[0].start = val; - voip_dsp_resources[0].end = val + 0xFFFFFFF; - voip_dsp_resources[1].start = pd->ext_irq; - - /* copy given platform data */ - dpd = bcm63xx_voip_dsp_device.dev.platform_data; - memcpy(dpd, pd, sizeof (*pd)); - - return platform_device_register(&bcm63xx_voip_dsp_device); -} diff --git a/arch/mips/boot/Makefile b/arch/mips/boot/Makefile index 35704c28a28b45e7edf6812ec7772251b63cbb52..0ccc20320099263aef72c273dcc258b1ce683f3a 100644 --- a/arch/mips/boot/Makefile +++ b/arch/mips/boot/Makefile @@ -123,7 +123,7 @@ $(obj)/vmlinux.its.S: $(addprefix $(srctree)/arch/mips/$(PLATFORM)/,$(ITS_INPUTS targets += vmlinux.its targets += vmlinux.gz.its targets += vmlinux.bz2.its -targets += vmlinux.lzmo.its +targets += vmlinux.lzma.its targets += vmlinux.lzo.its quiet_cmd_cpp_its_S = ITS $@ diff --git a/arch/mips/boot/compressed/Makefile b/arch/mips/boot/compressed/Makefile index 172801ed35b89994f6a52e492a7c486cb517d821..d859f079b771a158208b0cef571a22a22c3f67ac 100644 --- a/arch/mips/boot/compressed/Makefile +++ b/arch/mips/boot/compressed/Makefile @@ -29,6 +29,9 @@ KBUILD_AFLAGS := $(KBUILD_AFLAGS) -D__ASSEMBLY__ \ -DBOOT_HEAP_SIZE=$(BOOT_HEAP_SIZE) \ -DKERNEL_ENTRY=$(VMLINUX_ENTRY_ADDRESS) +# Prevents link failures: __sanitizer_cov_trace_pc() is not linked in. +KCOV_INSTRUMENT := n + # decompressor objects (linked with vmlinuz) vmlinuzobjs-y := $(obj)/head.o $(obj)/decompress.o $(obj)/string.o diff --git a/arch/mips/include/asm/io.h b/arch/mips/include/asm/io.h index 54c730aed32718e828d204b73e9747a18db87713..df1eaa3652794930e89efd05b650ccd44d3cf592 100644 --- a/arch/mips/include/asm/io.h +++ b/arch/mips/include/asm/io.h @@ -62,21 +62,11 @@ * instruction, so the lower 16 bits must be zero. Should be true on * on any sane architecture; generic code does not use this assumption. */ -extern const unsigned long mips_io_port_base; +extern unsigned long mips_io_port_base; -/* - * Gcc will generate code to load the value of mips_io_port_base after each - * function call which may be fairly wasteful in some cases. So we don't - * play quite by the book. We tell gcc mips_io_port_base is a long variable - * which solves the code generation issue. Now we need to violate the - * aliasing rules a little to make initialization possible and finally we - * will need the barrier() to fight side effects of the aliasing chat. - * This trickery will eventually collapse under gcc's optimizer. Oh well. - */ static inline void set_io_port_base(unsigned long base) { - * (unsigned long *) &mips_io_port_base = base; - barrier(); + mips_io_port_base = base; } /* diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_dsp.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_dsp.h deleted file mode 100644 index 4e4970787371a6f4d035d78d040f196d25c0d486..0000000000000000000000000000000000000000 --- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_dsp.h +++ /dev/null @@ -1,14 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __BCM63XX_DSP_H -#define __BCM63XX_DSP_H - -struct bcm63xx_dsp_platform_data { - unsigned gpio_rst; - unsigned gpio_int; - unsigned cs; - unsigned ext_irq; -}; - -int __init bcm63xx_dsp_register(const struct bcm63xx_dsp_platform_data *pd); - -#endif /* __BCM63XX_DSP_H */ diff --git a/arch/mips/include/asm/mach-bcm63xx/board_bcm963xx.h b/arch/mips/include/asm/mach-bcm63xx/board_bcm963xx.h index 5e5b1bc4a32478534f106256309124fc3126c2ee..830f53f28e3f786bc8d2f132e64e330243fbad23 100644 --- a/arch/mips/include/asm/mach-bcm63xx/board_bcm963xx.h +++ b/arch/mips/include/asm/mach-bcm63xx/board_bcm963xx.h @@ -7,7 +7,6 @@ #include #include #include -#include /* * flash mapping @@ -31,7 +30,6 @@ struct board_info { unsigned int has_ohci0:1; unsigned int has_ehci0:1; unsigned int has_usbd:1; - unsigned int has_dsp:1; unsigned int has_uart0:1; unsigned int has_uart1:1; @@ -43,9 +41,6 @@ struct board_info { /* USB config */ struct bcm63xx_usbd_platform_data usbd; - /* DSP config */ - struct bcm63xx_dsp_platform_data dsp; - /* GPIO LEDs */ struct gpio_led leds[5]; diff --git a/arch/mips/kernel/cacheinfo.c b/arch/mips/kernel/cacheinfo.c index 428ef218920398c6162b82e5688ab61dfe311580..3ea95568ece4950908cdd79308d0035568ade367 100644 --- a/arch/mips/kernel/cacheinfo.c +++ b/arch/mips/kernel/cacheinfo.c @@ -61,6 +61,25 @@ static int __init_cache_level(unsigned int cpu) return 0; } +static void fill_cpumask_siblings(int cpu, cpumask_t *cpu_map) +{ + int cpu1; + + for_each_possible_cpu(cpu1) + if (cpus_are_siblings(cpu, cpu1)) + cpumask_set_cpu(cpu1, cpu_map); +} + +static void fill_cpumask_cluster(int cpu, cpumask_t *cpu_map) +{ + int cpu1; + int cluster = cpu_cluster(&cpu_data[cpu]); + + for_each_possible_cpu(cpu1) + if (cpu_cluster(&cpu_data[cpu1]) == cluster) + cpumask_set_cpu(cpu1, cpu_map); +} + static int __populate_cache_leaves(unsigned int cpu) { struct cpuinfo_mips *c = ¤t_cpu_data; @@ -68,14 +87,20 @@ static int __populate_cache_leaves(unsigned int cpu) struct cacheinfo *this_leaf = this_cpu_ci->info_list; if (c->icache.waysize) { + /* L1 caches are per core */ + fill_cpumask_siblings(cpu, &this_leaf->shared_cpu_map); populate_cache(dcache, this_leaf, 1, CACHE_TYPE_DATA); + fill_cpumask_siblings(cpu, &this_leaf->shared_cpu_map); populate_cache(icache, this_leaf, 1, CACHE_TYPE_INST); } else { populate_cache(dcache, this_leaf, 1, CACHE_TYPE_UNIFIED); } - if (c->scache.waysize) + if (c->scache.waysize) { + /* L2 cache is per cluster */ + fill_cpumask_cluster(cpu, &this_leaf->shared_cpu_map); populate_cache(scache, this_leaf, 2, CACHE_TYPE_UNIFIED); + } if (c->tcache.waysize) populate_cache(tcache, this_leaf, 3, CACHE_TYPE_UNIFIED); diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index 8aaaa42f91ed6626809366e4ca7e533dbeb43bfc..e87c98b8a72c03d07862b024b4133d6e026cfb0d 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -76,7 +76,7 @@ static char __initdata builtin_cmdline[COMMAND_LINE_SIZE] = CONFIG_CMDLINE; * mips_io_port_base is the begin of the address space to which x86 style * I/O ports are mapped. */ -const unsigned long mips_io_port_base = -1; +unsigned long mips_io_port_base = -1; EXPORT_SYMBOL(mips_io_port_base); static struct resource code_resource = { .name = "Kernel code", }; diff --git a/arch/mips/kernel/vpe.c b/arch/mips/kernel/vpe.c index 0bef238d2c0c6fda901e5f20682a26cb33e217eb..0d5f9c8f5bdac30435ea3d7bc697c3553582275d 100644 --- a/arch/mips/kernel/vpe.c +++ b/arch/mips/kernel/vpe.c @@ -134,7 +134,7 @@ void release_vpe(struct vpe *v) { list_del(&v->list); if (v->load_addr) - release_progmem(v); + release_progmem(v->load_addr); kfree(v); } diff --git a/arch/mips/loongson64/loongson-3/platform.c b/arch/mips/loongson64/loongson-3/platform.c index 25a97cc0ee33664f79f16e3ffb4d350a4b033dae..0db4cc3196ebdcc92b98791e139f652636b83c9f 100644 --- a/arch/mips/loongson64/loongson-3/platform.c +++ b/arch/mips/loongson64/loongson-3/platform.c @@ -31,6 +31,9 @@ static int __init loongson3_platform_init(void) continue; pdev = kzalloc(sizeof(struct platform_device), GFP_KERNEL); + if (!pdev) + return -ENOMEM; + pdev->name = loongson_sysconf.sensors[i].name; pdev->id = loongson_sysconf.sensors[i].id; pdev->dev.platform_data = &loongson_sysconf.sensors[i]; diff --git a/arch/nios2/kernel/nios2_ksyms.c b/arch/nios2/kernel/nios2_ksyms.c index bf2f55d10a4d84f6896a70da6993731368708c6b..4e704046a150c164379d42cada3d09d6f6910107 100644 --- a/arch/nios2/kernel/nios2_ksyms.c +++ b/arch/nios2/kernel/nios2_ksyms.c @@ -9,12 +9,20 @@ #include #include +#include +#include + /* string functions */ EXPORT_SYMBOL(memcpy); EXPORT_SYMBOL(memset); EXPORT_SYMBOL(memmove); +/* memory management */ + +EXPORT_SYMBOL(empty_zero_page); +EXPORT_SYMBOL(flush_icache_range); + /* * libgcc functions - functions that are used internally by the * compiler... (prototypes are not correct though, but that @@ -31,3 +39,7 @@ DECLARE_EXPORT(__udivsi3); DECLARE_EXPORT(__umoddi3); DECLARE_EXPORT(__umodsi3); DECLARE_EXPORT(__muldi3); +DECLARE_EXPORT(__ucmpdi2); +DECLARE_EXPORT(__lshrdi3); +DECLARE_EXPORT(__ashldi3); +DECLARE_EXPORT(__ashrdi3); diff --git a/arch/parisc/kernel/drivers.c b/arch/parisc/kernel/drivers.c index a1a5e4c59e6b2a7034b1610a173e3cb4a992450a..4d5ad9cb0f69278cd951d688ff5eb347d4051027 100644 --- a/arch/parisc/kernel/drivers.c +++ b/arch/parisc/kernel/drivers.c @@ -868,8 +868,8 @@ static void print_parisc_device(struct parisc_device *dev) static int count; print_pa_hwpath(dev, hw_path); - pr_info("%d. %s at 0x%px [%s] { %d, 0x%x, 0x%.3x, 0x%.5x }", - ++count, dev->name, (void*) dev->hpa.start, hw_path, dev->id.hw_type, + pr_info("%d. %s at %pap [%s] { %d, 0x%x, 0x%.3x, 0x%.5x }", + ++count, dev->name, &(dev->hpa.start), hw_path, dev->id.hw_type, dev->id.hversion_rev, dev->id.hversion, dev->id.sversion); if (dev->num_addrs) { diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index a80669209155383343ba8adbb90b3e8427e2afdb..6f475dc5829b65d8c2886c0424201349cab24a8d 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -230,6 +230,7 @@ config PPC select NEED_SG_DMA_LENGTH select NO_BOOTMEM select OF + select OF_DMA_DEFAULT_COHERENT if !NOT_COHERENT_CACHE select OF_EARLY_FLATTREE select OF_RESERVED_MEM select OLD_SIGACTION if PPC32 diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile index e43321f46a3bef0d4cf6744a4bbcf9867a81787f..8954108df4570728c892922227445070cab1ebc0 100644 --- a/arch/powerpc/Makefile +++ b/arch/powerpc/Makefile @@ -412,7 +412,9 @@ vdso_install: ifdef CONFIG_PPC64 $(Q)$(MAKE) $(build)=arch/$(ARCH)/kernel/vdso64 $@ endif +ifdef CONFIG_VDSO32 $(Q)$(MAKE) $(build)=arch/$(ARCH)/kernel/vdso32 $@ +endif archclean: $(Q)$(MAKE) $(clean)=$(boot) diff --git a/arch/powerpc/boot/4xx.c b/arch/powerpc/boot/4xx.c index f7da651691249f30798f3f5dc0e4d569b6a0e45b..3c8774163c7ec684beeb6fb21d649d4d51ed1c1c 100644 --- a/arch/powerpc/boot/4xx.c +++ b/arch/powerpc/boot/4xx.c @@ -232,7 +232,7 @@ void ibm4xx_denali_fixup_memsize(void) dpath = 8; /* 64 bits */ /* get address pins (rows) */ - val = SDRAM0_READ(DDR0_42); + val = SDRAM0_READ(DDR0_42); row = DDR_GET_VAL(val, DDR_APIN, DDR_APIN_SHIFT); if (row > max_row) diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-0-best-effort.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-0-best-effort.dtsi index e1a961f05dcd5b04b640827810cd198ff6fcb753..baa0c503e741b87f89609ca66c907fd5b36ecea7 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-0-best-effort.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-0-best-effort.dtsi @@ -63,6 +63,7 @@ fman@400000 { #size-cells = <0>; compatible = "fsl,fman-memac-mdio", "fsl,fman-xmdio"; reg = <0xe1000 0x1000>; + fsl,erratum-a011043; /* must ignore read errors */ pcsphy0: ethernet-phy@0 { reg = <0x0>; diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-0.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-0.dtsi index c288f3c6c6378dc5fd3c20cf1605ecd031013c89..93095600e8086f617b515047a4c70275d1cf88f8 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-0.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-0.dtsi @@ -60,6 +60,7 @@ fman@400000 { #size-cells = <0>; compatible = "fsl,fman-memac-mdio", "fsl,fman-xmdio"; reg = <0xf1000 0x1000>; + fsl,erratum-a011043; /* must ignore read errors */ pcsphy6: ethernet-phy@0 { reg = <0x0>; diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-1-best-effort.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-1-best-effort.dtsi index 94f3e71750124b5c8a58861909d5d017b05262ca..ff4bd38f064599947096249e51149f8a0e43383f 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-1-best-effort.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-1-best-effort.dtsi @@ -63,6 +63,7 @@ fman@400000 { #size-cells = <0>; compatible = "fsl,fman-memac-mdio", "fsl,fman-xmdio"; reg = <0xe3000 0x1000>; + fsl,erratum-a011043; /* must ignore read errors */ pcsphy1: ethernet-phy@0 { reg = <0x0>; diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-1.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-1.dtsi index 94a76982d214b7b963d4b66a9f5161793a2b8b55..1fa38ed6f59e269c70a6de0e8ca41d40024bcb62 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-1.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-10g-1.dtsi @@ -60,6 +60,7 @@ fman@400000 { #size-cells = <0>; compatible = "fsl,fman-memac-mdio", "fsl,fman-xmdio"; reg = <0xf3000 0x1000>; + fsl,erratum-a011043; /* must ignore read errors */ pcsphy7: ethernet-phy@0 { reg = <0x0>; diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-0.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-0.dtsi index b5ff5f71c6b8b01874dd1b436e416409c2ac0759..a8cc9780c0c4225ac48c3fba7ca096089bf1c2f3 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-0.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-0.dtsi @@ -59,6 +59,7 @@ fman@400000 { #size-cells = <0>; compatible = "fsl,fman-memac-mdio", "fsl,fman-xmdio"; reg = <0xe1000 0x1000>; + fsl,erratum-a011043; /* must ignore read errors */ pcsphy0: ethernet-phy@0 { reg = <0x0>; diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-1.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-1.dtsi index ee44182c634853566c9740b04631c3564e8bd020..8b8bd70c93823df4ca8fc71574f6e9417c7bf1cb 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-1.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-1.dtsi @@ -59,6 +59,7 @@ fman@400000 { #size-cells = <0>; compatible = "fsl,fman-memac-mdio", "fsl,fman-xmdio"; reg = <0xe3000 0x1000>; + fsl,erratum-a011043; /* must ignore read errors */ pcsphy1: ethernet-phy@0 { reg = <0x0>; diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-2.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-2.dtsi index f05f0d775039b53aab57cdcd28fa8ac3ebde3490..619c880b54d8d39202cb58579974992ef2bf919f 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-2.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-2.dtsi @@ -59,6 +59,7 @@ fman@400000 { #size-cells = <0>; compatible = "fsl,fman-memac-mdio", "fsl,fman-xmdio"; reg = <0xe5000 0x1000>; + fsl,erratum-a011043; /* must ignore read errors */ pcsphy2: ethernet-phy@0 { reg = <0x0>; diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-3.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-3.dtsi index a9114ec510759e74036766b1ec1dbcaf42bcfa07..d7ebb73a400d06978aaedadd2efd62770159393c 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-3.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-3.dtsi @@ -59,6 +59,7 @@ fman@400000 { #size-cells = <0>; compatible = "fsl,fman-memac-mdio", "fsl,fman-xmdio"; reg = <0xe7000 0x1000>; + fsl,erratum-a011043; /* must ignore read errors */ pcsphy3: ethernet-phy@0 { reg = <0x0>; diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-4.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-4.dtsi index 44dd00ac7367fd6a100131eec3b7388741062383..b151d696a0699b24e90a5c290dba6cc41e6acccf 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-4.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-4.dtsi @@ -59,6 +59,7 @@ fman@400000 { #size-cells = <0>; compatible = "fsl,fman-memac-mdio", "fsl,fman-xmdio"; reg = <0xe9000 0x1000>; + fsl,erratum-a011043; /* must ignore read errors */ pcsphy4: ethernet-phy@0 { reg = <0x0>; diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-5.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-5.dtsi index 5b1b84b58602fa46e06cebb7be6b390d59372434..adc0ae0013a3ce89f8d1e26bd14a6f6f15af4526 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-5.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-0-1g-5.dtsi @@ -59,6 +59,7 @@ fman@400000 { #size-cells = <0>; compatible = "fsl,fman-memac-mdio", "fsl,fman-xmdio"; reg = <0xeb000 0x1000>; + fsl,erratum-a011043; /* must ignore read errors */ pcsphy5: ethernet-phy@0 { reg = <0x0>; diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-10g-0.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-10g-0.dtsi index 0e1daaef9e74b8fe2b6f51369a11c66b238e7dfd..435047e0e250e2e95d73d0e3de6b605060beb378 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-10g-0.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-10g-0.dtsi @@ -60,6 +60,7 @@ fman@500000 { #size-cells = <0>; compatible = "fsl,fman-memac-mdio", "fsl,fman-xmdio"; reg = <0xf1000 0x1000>; + fsl,erratum-a011043; /* must ignore read errors */ pcsphy14: ethernet-phy@0 { reg = <0x0>; diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-10g-1.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-10g-1.dtsi index 68c5ef779266a8692e712a0ddcfa51db0822d945..c098657cca0a7b8330590ef69b129af4b06b8e4d 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-10g-1.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-10g-1.dtsi @@ -60,6 +60,7 @@ fman@500000 { #size-cells = <0>; compatible = "fsl,fman-memac-mdio", "fsl,fman-xmdio"; reg = <0xf3000 0x1000>; + fsl,erratum-a011043; /* must ignore read errors */ pcsphy15: ethernet-phy@0 { reg = <0x0>; diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-0.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-0.dtsi index 605363cc1117fbf38b334e68a9b21815325cfe74..9d06824815f342ca7b1d30c5e94159da76e936e9 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-0.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-0.dtsi @@ -59,6 +59,7 @@ fman@500000 { #size-cells = <0>; compatible = "fsl,fman-memac-mdio", "fsl,fman-xmdio"; reg = <0xe1000 0x1000>; + fsl,erratum-a011043; /* must ignore read errors */ pcsphy8: ethernet-phy@0 { reg = <0x0>; diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-1.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-1.dtsi index 1955dfa136348f12ea29e0c4fdd3abb7438784fe..70e947730c4ba72b715763fb1fe2ddf6c6ef0fec 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-1.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-1.dtsi @@ -59,6 +59,7 @@ fman@500000 { #size-cells = <0>; compatible = "fsl,fman-memac-mdio", "fsl,fman-xmdio"; reg = <0xe3000 0x1000>; + fsl,erratum-a011043; /* must ignore read errors */ pcsphy9: ethernet-phy@0 { reg = <0x0>; diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-2.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-2.dtsi index 2c1476454ee01bb81069c325ed7c02c20f70963d..ad96e6529595960f14be688feee58d3e3a6b84e9 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-2.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-2.dtsi @@ -59,6 +59,7 @@ fman@500000 { #size-cells = <0>; compatible = "fsl,fman-memac-mdio", "fsl,fman-xmdio"; reg = <0xe5000 0x1000>; + fsl,erratum-a011043; /* must ignore read errors */ pcsphy10: ethernet-phy@0 { reg = <0x0>; diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-3.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-3.dtsi index b8b541ff5fb034e597c427431e15b4e01ed0de26..034bc4b71f7a506a064b37c320beea2bedd0f390 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-3.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-3.dtsi @@ -59,6 +59,7 @@ fman@500000 { #size-cells = <0>; compatible = "fsl,fman-memac-mdio", "fsl,fman-xmdio"; reg = <0xe7000 0x1000>; + fsl,erratum-a011043; /* must ignore read errors */ pcsphy11: ethernet-phy@0 { reg = <0x0>; diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-4.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-4.dtsi index 4b2cfddd1b1553bbb34d53a236cd399fb96441b1..93ca23d82b39ba9cfdf0558afe1da18d0518550d 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-4.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-4.dtsi @@ -59,6 +59,7 @@ fman@500000 { #size-cells = <0>; compatible = "fsl,fman-memac-mdio", "fsl,fman-xmdio"; reg = <0xe9000 0x1000>; + fsl,erratum-a011043; /* must ignore read errors */ pcsphy12: ethernet-phy@0 { reg = <0x0>; diff --git a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-5.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-5.dtsi index 0a52ddf7cc171e6b830bc2707bc0be9d8ac26ee0..23b3117a2fd2a3871627bc34a68e14d0159e52ca 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-5.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-fman3-1-1g-5.dtsi @@ -59,6 +59,7 @@ fman@500000 { #size-cells = <0>; compatible = "fsl,fman-memac-mdio", "fsl,fman-xmdio"; reg = <0xeb000 0x1000>; + fsl,erratum-a011043; /* must ignore read errors */ pcsphy13: ethernet-phy@0 { reg = <0x0>; diff --git a/arch/powerpc/include/asm/archrandom.h b/arch/powerpc/include/asm/archrandom.h index 9c63b596e6ce549885dd9bc9b3c4982ccef50f21..a09595f00cabeed16bc0f57ebe13cd798170f434 100644 --- a/arch/powerpc/include/asm/archrandom.h +++ b/arch/powerpc/include/asm/archrandom.h @@ -28,7 +28,7 @@ static inline int arch_get_random_seed_int(unsigned int *v) unsigned long val; int rc; - rc = arch_get_random_long(&val); + rc = arch_get_random_seed_long(&val); if (rc) *v = val; diff --git a/arch/powerpc/include/asm/kgdb.h b/arch/powerpc/include/asm/kgdb.h index 9db24e77b9f4bab53b471950b76ac626884e9960..a9e098a3b881f29f0d18d6c96a6f4f4a31376173 100644 --- a/arch/powerpc/include/asm/kgdb.h +++ b/arch/powerpc/include/asm/kgdb.h @@ -26,9 +26,12 @@ #define BREAK_INSTR_SIZE 4 #define BUFMAX ((NUMREGBYTES * 2) + 512) #define OUTBUFMAX ((NUMREGBYTES * 2) + 512) + +#define BREAK_INSTR 0x7d821008 /* twge r2, r2 */ + static inline void arch_kgdb_breakpoint(void) { - asm(".long 0x7d821008"); /* twge r2, r2 */ + asm(stringify_in_c(.long BREAK_INSTR)); } #define CACHE_FLUSH_IS_SAFE 1 #define DBG_MAX_REG_NUM 70 diff --git a/arch/powerpc/kernel/cacheinfo.c b/arch/powerpc/kernel/cacheinfo.c index a8f20e5928e132588a4387b6260ef1884a7a920e..9edb454301336efb69fa8289fc7d2e49242dc0fb 100644 --- a/arch/powerpc/kernel/cacheinfo.c +++ b/arch/powerpc/kernel/cacheinfo.c @@ -865,4 +865,25 @@ void cacheinfo_cpu_offline(unsigned int cpu_id) if (cache) cache_cpu_clear(cache, cpu_id); } + +void cacheinfo_teardown(void) +{ + unsigned int cpu; + + lockdep_assert_cpus_held(); + + for_each_online_cpu(cpu) + cacheinfo_cpu_offline(cpu); +} + +void cacheinfo_rebuild(void) +{ + unsigned int cpu; + + lockdep_assert_cpus_held(); + + for_each_online_cpu(cpu) + cacheinfo_cpu_online(cpu); +} + #endif /* (CONFIG_PPC_PSERIES && CONFIG_SUSPEND) || CONFIG_HOTPLUG_CPU */ diff --git a/arch/powerpc/kernel/cacheinfo.h b/arch/powerpc/kernel/cacheinfo.h index 955f5e999f1b81e8faad5a653f2777b0f65912c3..52bd3fc6642da11329abb3ec465f6709b897a651 100644 --- a/arch/powerpc/kernel/cacheinfo.h +++ b/arch/powerpc/kernel/cacheinfo.h @@ -6,4 +6,8 @@ extern void cacheinfo_cpu_online(unsigned int cpu_id); extern void cacheinfo_cpu_offline(unsigned int cpu_id); +/* Allow migration/suspend to tear down and rebuild the hierarchy. */ +extern void cacheinfo_teardown(void); +extern void cacheinfo_rebuild(void); + #endif /* _PPC_CACHEINFO_H */ diff --git a/arch/powerpc/kernel/cputable.c b/arch/powerpc/kernel/cputable.c index 1eab54bc6ee9385c8de90ac0b9cde6b7d6da1302..a2183bb54a29a0c5fa9919148d1b16714bc9809c 100644 --- a/arch/powerpc/kernel/cputable.c +++ b/arch/powerpc/kernel/cputable.c @@ -2188,11 +2188,13 @@ static struct cpu_spec * __init setup_cpu_spec(unsigned long offset, * oprofile_cpu_type already has a value, then we are * possibly overriding a real PVR with a logical one, * and, in that case, keep the current value for - * oprofile_cpu_type. + * oprofile_cpu_type. Futhermore, let's ensure that the + * fix for the PMAO bug is enabled on compatibility mode. */ if (old.oprofile_cpu_type != NULL) { t->oprofile_cpu_type = old.oprofile_cpu_type; t->oprofile_type = old.oprofile_type; + t->cpu_features |= old.cpu_features & CPU_FTR_PMAO_BUG; } } diff --git a/arch/powerpc/kernel/dt_cpu_ftrs.c b/arch/powerpc/kernel/dt_cpu_ftrs.c index c6f41907f0d716fca6298715f7bf845c1f5411ac..a4b31e17492d38252644d3243e73af3283ce261a 100644 --- a/arch/powerpc/kernel/dt_cpu_ftrs.c +++ b/arch/powerpc/kernel/dt_cpu_ftrs.c @@ -666,8 +666,10 @@ static bool __init cpufeatures_process_feature(struct dt_cpu_feature *f) m = &dt_cpu_feature_match_table[i]; if (!strcmp(f->name, m->name)) { known = true; - if (m->enable(f)) + if (m->enable(f)) { + cur_cpu_spec->cpu_features |= m->cpu_ftr_bit_mask; break; + } pr_info("not enabling: %s (disabled or unsupported by kernel)\n", f->name); @@ -675,17 +677,12 @@ static bool __init cpufeatures_process_feature(struct dt_cpu_feature *f) } } - if (!known && enable_unknown) { - if (!feat_try_enable_unknown(f)) { - pr_info("not enabling: %s (unknown and unsupported by kernel)\n", - f->name); - return false; - } + if (!known && (!enable_unknown || !feat_try_enable_unknown(f))) { + pr_info("not enabling: %s (unknown and unsupported by kernel)\n", + f->name); + return false; } - if (m->cpu_ftr_bit_mask) - cur_cpu_spec->cpu_features |= m->cpu_ftr_bit_mask; - if (known) pr_debug("enabling: %s\n", f->name); else diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c index af1f3d5f9a0f71c08a1e38d296615acf502560d7..377d23f58197aced98ade18ac5e7d91f54629893 100644 --- a/arch/powerpc/kernel/eeh_driver.c +++ b/arch/powerpc/kernel/eeh_driver.c @@ -554,12 +554,6 @@ static void *eeh_rmv_device(struct eeh_dev *edev, void *userdata) pci_iov_remove_virtfn(edev->physfn, pdn->vf_index); edev->pdev = NULL; - - /* - * We have to set the VF PE number to invalid one, which is - * required to plug the VF successfully. - */ - pdn->pe_number = IODA_INVALID_PE; #endif if (rmv_data) list_add(&edev->rmv_list, &rmv_data->edev_list); diff --git a/arch/powerpc/kernel/kgdb.c b/arch/powerpc/kernel/kgdb.c index 35e240a0a40871f9492a769604895a350e7cbce6..59c578f865aa69c9fe8dce7d47962fe58877d079 100644 --- a/arch/powerpc/kernel/kgdb.c +++ b/arch/powerpc/kernel/kgdb.c @@ -24,6 +24,7 @@ #include #include #include +#include #include /* @@ -144,7 +145,7 @@ static int kgdb_handle_breakpoint(struct pt_regs *regs) if (kgdb_handle_exception(1, SIGTRAP, 0, regs) != 0) return 0; - if (*(u32 *) (regs->nip) == *(u32 *) (&arch_kgdb_ops.gdb_bpt_instr)) + if (*(u32 *)regs->nip == BREAK_INSTR) regs->nip += BREAK_INSTR_SIZE; return 1; @@ -441,16 +442,42 @@ int kgdb_arch_handle_exception(int vector, int signo, int err_code, return -1; } +int kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt) +{ + int err; + unsigned int instr; + unsigned int *addr = (unsigned int *)bpt->bpt_addr; + + err = probe_kernel_address(addr, instr); + if (err) + return err; + + err = patch_instruction(addr, BREAK_INSTR); + if (err) + return -EFAULT; + + *(unsigned int *)bpt->saved_instr = instr; + + return 0; +} + +int kgdb_arch_remove_breakpoint(struct kgdb_bkpt *bpt) +{ + int err; + unsigned int instr = *(unsigned int *)bpt->saved_instr; + unsigned int *addr = (unsigned int *)bpt->bpt_addr; + + err = patch_instruction(addr, instr); + if (err) + return -EFAULT; + + return 0; +} + /* * Global data */ -struct kgdb_arch arch_kgdb_ops = { -#ifdef __LITTLE_ENDIAN__ - .gdb_bpt_instr = {0x08, 0x10, 0x82, 0x7d}, -#else - .gdb_bpt_instr = {0x7d, 0x82, 0x10, 0x08}, -#endif -}; +struct kgdb_arch arch_kgdb_ops; static int kgdb_not_implemented(struct pt_regs *regs) { diff --git a/arch/powerpc/kernel/mce_power.c b/arch/powerpc/kernel/mce_power.c index 37a110b8e7e1724571ee7e3d22036b5b9a51832b..ecb3750406378799d97af1c79c729d90f37c513e 100644 --- a/arch/powerpc/kernel/mce_power.c +++ b/arch/powerpc/kernel/mce_power.c @@ -40,7 +40,7 @@ static unsigned long addr_to_pfn(struct pt_regs *regs, unsigned long addr) { pte_t *ptep; unsigned int shift; - unsigned long flags; + unsigned long pfn, flags; struct mm_struct *mm; if (user_mode(regs)) @@ -50,18 +50,22 @@ static unsigned long addr_to_pfn(struct pt_regs *regs, unsigned long addr) local_irq_save(flags); ptep = __find_linux_pte(mm->pgd, addr, NULL, &shift); - local_irq_restore(flags); - if (!ptep || pte_special(*ptep)) - return ULONG_MAX; + if (!ptep || pte_special(*ptep)) { + pfn = ULONG_MAX; + goto out; + } - if (shift > PAGE_SHIFT) { + if (shift <= PAGE_SHIFT) + pfn = pte_pfn(*ptep); + else { unsigned long rpnmask = (1ul << shift) - PAGE_SIZE; - - return pte_pfn(__pte(pte_val(*ptep) | (addr & rpnmask))); + pfn = pte_pfn(__pte(pte_val(*ptep) | (addr & rpnmask))); } - return pte_pfn(*ptep); +out: + local_irq_restore(flags); + return pfn; } /* flush SLBs and reload */ diff --git a/arch/powerpc/kernel/pci_dn.c b/arch/powerpc/kernel/pci_dn.c index ab147a1909c8b98e22e254fbaf62bd488b3c9d63..7cecc3bd953b7455fc1ec2fd2dddaf845d2e217e 100644 --- a/arch/powerpc/kernel/pci_dn.c +++ b/arch/powerpc/kernel/pci_dn.c @@ -257,9 +257,22 @@ void remove_dev_pci_data(struct pci_dev *pdev) continue; #ifdef CONFIG_EEH - /* Release EEH device for the VF */ + /* + * Release EEH state for this VF. The PCI core + * has already torn down the pci_dev for this VF, but + * we're responsible to removing the eeh_dev since it + * has the same lifetime as the pci_dn that spawned it. + */ edev = pdn_to_eeh_dev(pdn); if (edev) { + /* + * We allocate pci_dn's for the totalvfs count, + * but only only the vfs that were activated + * have a configured PE. + */ + if (edev->pe) + eeh_rmv_from_parent_pe(edev); + pdn->edev = NULL; kfree(edev); } diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index 9b38a2e5dd3576cd5023093e2655c4d6f6835a9d..af1e38febe49667983ec6d646fb8c848f14f9b1c 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -904,7 +904,7 @@ struct ibm_arch_vec __cacheline_aligned ibm_architecture_vec = { .reserved2 = 0, .reserved3 = 0, .subprocessors = 1, - .byte22 = OV5_FEAT(OV5_DRMEM_V2), + .byte22 = OV5_FEAT(OV5_DRMEM_V2) | OV5_FEAT(OV5_DRC_INFO), .intarch = 0, .mmu = 0, .hash_ext = 0, diff --git a/arch/powerpc/kernel/signal.c b/arch/powerpc/kernel/signal.c index b3e8db376ecde459bb8b5a1cd00b10c9606df289..57b3745f7f1babdf79d199c2c87a7e53ed3b1b2c 100644 --- a/arch/powerpc/kernel/signal.c +++ b/arch/powerpc/kernel/signal.c @@ -200,14 +200,27 @@ unsigned long get_tm_stackpointer(struct task_struct *tsk) * normal/non-checkpointed stack pointer. */ + unsigned long ret = tsk->thread.regs->gpr[1]; + #ifdef CONFIG_PPC_TRANSACTIONAL_MEM BUG_ON(tsk != current); if (MSR_TM_ACTIVE(tsk->thread.regs->msr)) { + preempt_disable(); tm_reclaim_current(TM_CAUSE_SIGNAL); if (MSR_TM_TRANSACTIONAL(tsk->thread.regs->msr)) - return tsk->thread.ckpt_regs.gpr[1]; + ret = tsk->thread.ckpt_regs.gpr[1]; + + /* + * If we treclaim, we must clear the current thread's TM bits + * before re-enabling preemption. Otherwise we might be + * preempted and have the live MSR[TS] changed behind our back + * (tm_recheckpoint_new_task() would recheckpoint). Besides, we + * enter the signal handler in non-transactional state. + */ + tsk->thread.regs->msr &= ~MSR_TS_MASK; + preempt_enable(); } #endif - return tsk->thread.regs->gpr[1]; + return ret; } diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c index 906b05c2adae3f2d0770d7c6ae6a9603168e5776..06b4b828d2588c2b121289d4359c85b4cf981101 100644 --- a/arch/powerpc/kernel/signal_32.c +++ b/arch/powerpc/kernel/signal_32.c @@ -493,19 +493,11 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame, */ static int save_tm_user_regs(struct pt_regs *regs, struct mcontext __user *frame, - struct mcontext __user *tm_frame, int sigret) + struct mcontext __user *tm_frame, int sigret, + unsigned long msr) { - unsigned long msr = regs->msr; - WARN_ON(tm_suspend_disabled); - /* Remove TM bits from thread's MSR. The MSR in the sigcontext - * just indicates to userland that we were doing a transaction, but we - * don't want to return in transactional state. This also ensures - * that flush_fp_to_thread won't set TIF_RESTORE_TM again. - */ - regs->msr &= ~MSR_TS_MASK; - /* Save both sets of general registers */ if (save_general_regs(¤t->thread.ckpt_regs, frame) || save_general_regs(regs, tm_frame)) @@ -916,6 +908,10 @@ int handle_rt_signal32(struct ksignal *ksig, sigset_t *oldset, int sigret; unsigned long tramp; struct pt_regs *regs = tsk->thread.regs; +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + /* Save the thread's msr before get_tm_stackpointer() changes it */ + unsigned long msr = regs->msr; +#endif BUG_ON(tsk != current); @@ -948,13 +944,13 @@ int handle_rt_signal32(struct ksignal *ksig, sigset_t *oldset, #ifdef CONFIG_PPC_TRANSACTIONAL_MEM tm_frame = &rt_sf->uc_transact.uc_mcontext; - if (MSR_TM_ACTIVE(regs->msr)) { + if (MSR_TM_ACTIVE(msr)) { if (__put_user((unsigned long)&rt_sf->uc_transact, &rt_sf->uc.uc_link) || __put_user((unsigned long)tm_frame, &rt_sf->uc_transact.uc_regs)) goto badframe; - if (save_tm_user_regs(regs, frame, tm_frame, sigret)) + if (save_tm_user_regs(regs, frame, tm_frame, sigret, msr)) goto badframe; } else @@ -1365,6 +1361,10 @@ int handle_signal32(struct ksignal *ksig, sigset_t *oldset, int sigret; unsigned long tramp; struct pt_regs *regs = tsk->thread.regs; +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + /* Save the thread's msr before get_tm_stackpointer() changes it */ + unsigned long msr = regs->msr; +#endif BUG_ON(tsk != current); @@ -1398,9 +1398,9 @@ int handle_signal32(struct ksignal *ksig, sigset_t *oldset, #ifdef CONFIG_PPC_TRANSACTIONAL_MEM tm_mctx = &frame->mctx_transact; - if (MSR_TM_ACTIVE(regs->msr)) { + if (MSR_TM_ACTIVE(msr)) { if (save_tm_user_regs(regs, &frame->mctx, &frame->mctx_transact, - sigret)) + sigret, msr)) goto badframe; } else diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c index b5933d7219db60623868286ee386891c63b2cd4f..b088b0700d0dd4d62a492c984b1e8366e05e6f0d 100644 --- a/arch/powerpc/kernel/signal_64.c +++ b/arch/powerpc/kernel/signal_64.c @@ -196,7 +196,8 @@ static long setup_sigcontext(struct sigcontext __user *sc, static long setup_tm_sigcontexts(struct sigcontext __user *sc, struct sigcontext __user *tm_sc, struct task_struct *tsk, - int signr, sigset_t *set, unsigned long handler) + int signr, sigset_t *set, unsigned long handler, + unsigned long msr) { /* When CONFIG_ALTIVEC is set, we _always_ setup v_regs even if the * process never used altivec yet (MSR_VEC is zero in pt_regs of @@ -211,12 +212,11 @@ static long setup_tm_sigcontexts(struct sigcontext __user *sc, elf_vrreg_t __user *tm_v_regs = sigcontext_vmx_regs(tm_sc); #endif struct pt_regs *regs = tsk->thread.regs; - unsigned long msr = tsk->thread.regs->msr; long err = 0; BUG_ON(tsk != current); - BUG_ON(!MSR_TM_ACTIVE(regs->msr)); + BUG_ON(!MSR_TM_ACTIVE(msr)); WARN_ON(tm_suspend_disabled); @@ -226,13 +226,6 @@ static long setup_tm_sigcontexts(struct sigcontext __user *sc, */ msr |= tsk->thread.ckpt_regs.msr & (MSR_FP | MSR_VEC | MSR_VSX); - /* Remove TM bits from thread's MSR. The MSR in the sigcontext - * just indicates to userland that we were doing a transaction, but we - * don't want to return in transactional state. This also ensures - * that flush_fp_to_thread won't set TIF_RESTORE_TM again. - */ - regs->msr &= ~MSR_TS_MASK; - #ifdef CONFIG_ALTIVEC err |= __put_user(v_regs, &sc->v_regs); err |= __put_user(tm_v_regs, &tm_sc->v_regs); @@ -803,6 +796,10 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set, unsigned long newsp = 0; long err = 0; struct pt_regs *regs = tsk->thread.regs; +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + /* Save the thread's msr before get_tm_stackpointer() changes it */ + unsigned long msr = regs->msr; +#endif BUG_ON(tsk != current); @@ -820,7 +817,7 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set, err |= __put_user(0, &frame->uc.uc_flags); err |= __save_altstack(&frame->uc.uc_stack, regs->gpr[1]); #ifdef CONFIG_PPC_TRANSACTIONAL_MEM - if (MSR_TM_ACTIVE(regs->msr)) { + if (MSR_TM_ACTIVE(msr)) { /* The ucontext_t passed to userland points to the second * ucontext_t (for transactional state) with its uc_link ptr. */ @@ -828,7 +825,8 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set, err |= setup_tm_sigcontexts(&frame->uc.uc_mcontext, &frame->uc_transact.uc_mcontext, tsk, ksig->sig, NULL, - (unsigned long)ksig->ka.sa.sa_handler); + (unsigned long)ksig->ka.sa.sa_handler, + msr); } else #endif { diff --git a/arch/powerpc/kvm/book3s_64_vio.c b/arch/powerpc/kvm/book3s_64_vio.c index 65486c3d029b58b6eca6769a439c4663f188f544..26b03af71abd2622a5187311f5c131eedab691ce 100644 --- a/arch/powerpc/kvm/book3s_64_vio.c +++ b/arch/powerpc/kvm/book3s_64_vio.c @@ -133,7 +133,6 @@ extern void kvm_spapr_tce_release_iommu_group(struct kvm *kvm, continue; kref_put(&stit->kref, kvm_spapr_tce_liobn_put); - return; } } } diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index 3ae3e8d141e3e19f593fbc5acfe0392c3f646d07..5dc592fb4f5f7a5820719b42791608da60d74c6a 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -2065,7 +2065,7 @@ static struct kvm_vcpu *kvmppc_core_vcpu_create_hv(struct kvm *kvm, mutex_unlock(&kvm->lock); if (!vcore) - goto free_vcpu; + goto uninit_vcpu; spin_lock(&vcore->lock); ++vcore->num_threads; @@ -2082,6 +2082,8 @@ static struct kvm_vcpu *kvmppc_core_vcpu_create_hv(struct kvm *kvm, return vcpu; +uninit_vcpu: + kvm_vcpu_uninit(vcpu); free_vcpu: kmem_cache_free(kvm_vcpu_cache, vcpu); out: @@ -2993,25 +2995,26 @@ static noinline void kvmppc_run_core(struct kvmppc_vcore *vc) } } - /* - * Interrupts will be enabled once we get into the guest, - * so tell lockdep that we're about to enable interrupts. - */ - trace_hardirqs_on(); - guest_enter_irqoff(); srcu_idx = srcu_read_lock(&vc->kvm->srcu); this_cpu_disable_ftrace(); + /* + * Interrupts will be enabled once we get into the guest, + * so tell lockdep that we're about to enable interrupts. + */ + trace_hardirqs_on(); + trap = __kvmppc_vcore_entry(); + trace_hardirqs_off(); + this_cpu_enable_ftrace(); srcu_read_unlock(&vc->kvm->srcu, srcu_idx); - trace_hardirqs_off(); set_irq_happened(trap); spin_lock(&vc->lock); diff --git a/arch/powerpc/kvm/book3s_pr.c b/arch/powerpc/kvm/book3s_pr.c index de9702219dee9442e23a59eeced647d99b476465..7869112a8f3c64e824d94b9111ba6cd0f46a98fa 100644 --- a/arch/powerpc/kvm/book3s_pr.c +++ b/arch/powerpc/kvm/book3s_pr.c @@ -1772,10 +1772,12 @@ static struct kvm_vcpu *kvmppc_core_vcpu_create_pr(struct kvm *kvm, err = kvmppc_mmu_init(vcpu); if (err < 0) - goto uninit_vcpu; + goto free_shared_page; return vcpu; +free_shared_page: + free_page((unsigned long)vcpu->arch.shared); uninit_vcpu: kvm_vcpu_uninit(vcpu); free_shadow_vcpu: diff --git a/arch/powerpc/mm/dump_hashpagetable.c b/arch/powerpc/mm/dump_hashpagetable.c index 86929469504852998d030a4b2de06fe21bf6b151..b430e4e08af69d435f446fadcd2dc2242f51fa8c 100644 --- a/arch/powerpc/mm/dump_hashpagetable.c +++ b/arch/powerpc/mm/dump_hashpagetable.c @@ -342,7 +342,7 @@ static unsigned long hpte_find(struct pg_state *st, unsigned long ea, int psize) /* Look in secondary table */ if (slot == -1) - slot = base_hpte_find(ea, psize, true, &v, &r); + slot = base_hpte_find(ea, psize, false, &v, &r); /* No entry found */ if (slot == -1) diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c index 9a6afd9f3f9b294f4fa64fb521f45296bd2173f7..84a012e42a7ed51533e8f08f61ba31ade575ce9c 100644 --- a/arch/powerpc/mm/mem.c +++ b/arch/powerpc/mm/mem.c @@ -118,8 +118,8 @@ int __weak remove_section_mapping(unsigned long start, unsigned long end) return -ENODEV; } -int __meminit arch_add_memory(int nid, u64 start, u64 size, struct vmem_altmap *altmap, - bool want_memblock) +int __ref arch_add_memory(int nid, u64 start, u64 size, struct vmem_altmap *altmap, + bool want_memblock) { unsigned long start_pfn = start >> PAGE_SHIFT; unsigned long nr_pages = size >> PAGE_SHIFT; @@ -139,30 +139,20 @@ int __meminit arch_add_memory(int nid, u64 start, u64 size, struct vmem_altmap * return __add_pages(nid, start_pfn, nr_pages, altmap, want_memblock); } -#ifdef CONFIG_MEMORY_HOTREMOVE -int __meminit arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap) +void __ref arch_remove_memory(int nid, u64 start, u64 size, + struct vmem_altmap *altmap) { unsigned long start_pfn = start >> PAGE_SHIFT; unsigned long nr_pages = size >> PAGE_SHIFT; - struct page *page; int ret; - /* - * If we have an altmap then we need to skip over any reserved PFNs - * when querying the zone. - */ - page = pfn_to_page(start_pfn); - if (altmap) - page += vmem_altmap_offset(altmap); - - ret = __remove_pages(page_zone(page), start_pfn, nr_pages, altmap); - if (ret) - return ret; + __remove_pages(start_pfn, nr_pages, altmap); /* Remove htab bolted mappings for this section of memory */ start = (unsigned long)__va(start); flush_inval_dcache_range(start, start + size); ret = remove_section_mapping(start, start + size); + WARN_ON_ONCE(ret); /* Ensure all vmalloc mappings are flushed in case they also * hit that section of memory @@ -170,11 +160,8 @@ int __meminit arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap vm_unmap_aliases(); resize_hpt_for_hotplug(memblock_phys_mem_size()); - - return ret; } #endif -#endif /* CONFIG_MEMORY_HOTPLUG */ /* * walk_memory_resource() needs to make sure there is no holes in a given diff --git a/arch/powerpc/mm/pgtable-radix.c b/arch/powerpc/mm/pgtable-radix.c index 69caeb5bccb213f836b353989c31e81e0303a788..5404a631d5834cc00de142454f3d523b12c6c061 100644 --- a/arch/powerpc/mm/pgtable-radix.c +++ b/arch/powerpc/mm/pgtable-radix.c @@ -717,8 +717,8 @@ static int __meminit stop_machine_change_mapping(void *data) spin_unlock(&init_mm.page_table_lock); pte_clear(&init_mm, params->aligned_start, params->pte); - create_physical_mapping(params->aligned_start, params->start, -1); - create_physical_mapping(params->end, params->aligned_end, -1); + create_physical_mapping(__pa(params->aligned_start), __pa(params->start), -1); + create_physical_mapping(__pa(params->end), __pa(params->aligned_end), -1); spin_lock(&init_mm.page_table_lock); return 0; } diff --git a/arch/powerpc/platforms/powernv/memtrace.c b/arch/powerpc/platforms/powernv/memtrace.c index dd3cc4632b9ae6732d79e774fda50ae0c2171813..84d038ed3882a7794987ba3f222aab7351895d8f 100644 --- a/arch/powerpc/platforms/powernv/memtrace.c +++ b/arch/powerpc/platforms/powernv/memtrace.c @@ -122,7 +122,7 @@ static u64 memtrace_alloc_node(u32 nid, u64 size) */ end_pfn = base_pfn + nr_pages; for (pfn = base_pfn; pfn < end_pfn; pfn += bytes>> PAGE_SHIFT) { - remove_memory(nid, pfn << PAGE_SHIFT, bytes); + __remove_memory(nid, pfn << PAGE_SHIFT, bytes); } unlock_device_hotplug(); return base_pfn << PAGE_SHIFT; diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index ee63749a2d47eaee50cb45bb0da47e9b8ccaab4e..ecd211c5f24a58b2a668d18b630be76dcd305b10 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -1552,6 +1552,10 @@ static void pnv_ioda_setup_vf_PE(struct pci_dev *pdev, u16 num_vfs) /* Reserve PE for each VF */ for (vf_index = 0; vf_index < num_vfs; vf_index++) { + int vf_devfn = pci_iov_virtfn_devfn(pdev, vf_index); + int vf_bus = pci_iov_virtfn_bus(pdev, vf_index); + struct pci_dn *vf_pdn; + if (pdn->m64_single_mode) pe_num = pdn->pe_num_map[vf_index]; else @@ -1564,13 +1568,11 @@ static void pnv_ioda_setup_vf_PE(struct pci_dev *pdev, u16 num_vfs) pe->pbus = NULL; pe->parent_dev = pdev; pe->mve_number = -1; - pe->rid = (pci_iov_virtfn_bus(pdev, vf_index) << 8) | - pci_iov_virtfn_devfn(pdev, vf_index); + pe->rid = (vf_bus << 8) | vf_devfn; pe_info(pe, "VF %04d:%02d:%02d.%d associated with PE#%x\n", hose->global_number, pdev->bus->number, - PCI_SLOT(pci_iov_virtfn_devfn(pdev, vf_index)), - PCI_FUNC(pci_iov_virtfn_devfn(pdev, vf_index)), pe_num); + PCI_SLOT(vf_devfn), PCI_FUNC(vf_devfn), pe_num); if (pnv_ioda_configure_pe(phb, pe)) { /* XXX What do we do here ? */ @@ -1584,6 +1586,15 @@ static void pnv_ioda_setup_vf_PE(struct pci_dev *pdev, u16 num_vfs) list_add_tail(&pe->list, &phb->ioda.pe_list); mutex_unlock(&phb->ioda.pe_list_mutex); + /* associate this pe to it's pdn */ + list_for_each_entry(vf_pdn, &pdn->parent->child_list, list) { + if (vf_pdn->busno == vf_bus && + vf_pdn->devfn == vf_devfn) { + vf_pdn->pe_number = pe_num; + break; + } + } + pnv_pci_ioda2_setup_dma_pe(phb, pe); } } @@ -3004,9 +3015,6 @@ static void pnv_pci_ioda_fixup_iov_resources(struct pci_dev *pdev) struct pci_dn *pdn; int mul, total_vfs; - if (!pdev->is_physfn || pci_dev_is_added(pdev)) - return; - pdn = pci_get_pdn(pdev); pdn->vfs_expanded = 0; pdn->m64_single_mode = false; @@ -3081,6 +3089,30 @@ static void pnv_pci_ioda_fixup_iov_resources(struct pci_dev *pdev) res->end = res->start - 1; } } + +static void pnv_pci_ioda_fixup_iov(struct pci_dev *pdev) +{ + if (WARN_ON(pci_dev_is_added(pdev))) + return; + + if (pdev->is_virtfn) { + struct pnv_ioda_pe *pe = pnv_ioda_get_pe(pdev); + + /* + * VF PEs are single-device PEs so their pdev pointer needs to + * be set. The pdev doesn't exist when the PE is allocated (in + * (pcibios_sriov_enable()) so we fix it up here. + */ + pe->pdev = pdev; + WARN_ON(!(pe->flags & PNV_IODA_PE_VF)); + } else if (pdev->is_physfn) { + /* + * For PFs adjust their allocated IOV resources to match what + * the PHB can support using it's M64 BAR table. + */ + pnv_pci_ioda_fixup_iov_resources(pdev); + } +} #endif /* CONFIG_PCI_IOV */ static void pnv_ioda_setup_pe_res(struct pnv_ioda_pe *pe, @@ -3974,7 +4006,7 @@ static void __init pnv_pci_init_ioda_phb(struct device_node *np, ppc_md.pcibios_default_alignment = pnv_pci_default_alignment; #ifdef CONFIG_PCI_IOV - ppc_md.pcibios_fixup_sriov = pnv_pci_ioda_fixup_iov_resources; + ppc_md.pcibios_fixup_sriov = pnv_pci_ioda_fixup_iov; ppc_md.pcibios_iov_resource_alignment = pnv_pci_iov_resource_alignment; ppc_md.pcibios_sriov_enable = pnv_pcibios_sriov_enable; ppc_md.pcibios_sriov_disable = pnv_pcibios_sriov_disable; diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c index db230a35609bfef59f97fdee53aed43cce843f9c..b6fa900af5da595dd7b771147890016f783bf6a6 100644 --- a/arch/powerpc/platforms/powernv/pci.c +++ b/arch/powerpc/platforms/powernv/pci.c @@ -820,24 +820,6 @@ void pnv_pci_dma_dev_setup(struct pci_dev *pdev) { struct pci_controller *hose = pci_bus_to_host(pdev->bus); struct pnv_phb *phb = hose->private_data; -#ifdef CONFIG_PCI_IOV - struct pnv_ioda_pe *pe; - struct pci_dn *pdn; - - /* Fix the VF pdn PE number */ - if (pdev->is_virtfn) { - pdn = pci_get_pdn(pdev); - WARN_ON(pdn->pe_number != IODA_INVALID_PE); - list_for_each_entry(pe, &phb->ioda.pe_list, list) { - if (pe->rid == ((pdev->bus->number << 8) | - (pdev->devfn & 0xff))) { - pdn->pe_number = pe->pe_number; - pe->pdev = pdev; - break; - } - } - } -#endif /* CONFIG_PCI_IOV */ if (phb && phb->dma_dev_setup) phb->dma_dev_setup(phb, pdev); @@ -1095,6 +1077,23 @@ void __init pnv_pci_init(void) if (!firmware_has_feature(FW_FEATURE_OPAL)) return; +#ifdef CONFIG_PCIEPORTBUS + /* + * On PowerNV PCIe devices are (currently) managed in cooperation + * with firmware. This isn't *strictly* required, but there's enough + * assumptions baked into both firmware and the platform code that + * it's unwise to allow the portbus services to be used. + * + * We need to fix this eventually, but for now set this flag to disable + * the portbus driver. The AER service isn't required since that AER + * events are handled via EEH. The pciehp hotplug driver can't work + * without kernel changes (and portbus binding breaks pnv_php). The + * other services also require some thinking about how we're going + * to integrate them. + */ + pcie_ports_disabled = true; +#endif + /* Look for IODA IO-Hubs. */ for_each_compatible_node(np, NULL, "ibm,ioda-hub") { pnv_pci_init_ioda_hub(np); diff --git a/arch/powerpc/platforms/pseries/hotplug-memory.c b/arch/powerpc/platforms/pseries/hotplug-memory.c index 7f86bc3eaadecc2e2496b400e790dd9fbcbe7b27..fc01a2c0f8ed43253e610b427710f3df84cce45f 100644 --- a/arch/powerpc/platforms/pseries/hotplug-memory.c +++ b/arch/powerpc/platforms/pseries/hotplug-memory.c @@ -101,11 +101,12 @@ static struct property *dlpar_clone_property(struct property *prop, return new_prop; } -static u32 find_aa_index(struct device_node *dr_node, - struct property *ala_prop, const u32 *lmb_assoc) +static bool find_aa_index(struct device_node *dr_node, + struct property *ala_prop, + const u32 *lmb_assoc, u32 *aa_index) { - u32 *assoc_arrays; - u32 aa_index; + u32 *assoc_arrays, new_prop_size; + struct property *new_prop; int aa_arrays, aa_array_entries, aa_array_sz; int i, index; @@ -121,46 +122,39 @@ static u32 find_aa_index(struct device_node *dr_node, aa_array_entries = be32_to_cpu(assoc_arrays[1]); aa_array_sz = aa_array_entries * sizeof(u32); - aa_index = -1; for (i = 0; i < aa_arrays; i++) { index = (i * aa_array_entries) + 2; if (memcmp(&assoc_arrays[index], &lmb_assoc[1], aa_array_sz)) continue; - aa_index = i; - break; + *aa_index = i; + return true; } - if (aa_index == -1) { - struct property *new_prop; - u32 new_prop_size; - - new_prop_size = ala_prop->length + aa_array_sz; - new_prop = dlpar_clone_property(ala_prop, new_prop_size); - if (!new_prop) - return -1; - - assoc_arrays = new_prop->value; + new_prop_size = ala_prop->length + aa_array_sz; + new_prop = dlpar_clone_property(ala_prop, new_prop_size); + if (!new_prop) + return false; - /* increment the number of entries in the lookup array */ - assoc_arrays[0] = cpu_to_be32(aa_arrays + 1); + assoc_arrays = new_prop->value; - /* copy the new associativity into the lookup array */ - index = aa_arrays * aa_array_entries + 2; - memcpy(&assoc_arrays[index], &lmb_assoc[1], aa_array_sz); + /* increment the number of entries in the lookup array */ + assoc_arrays[0] = cpu_to_be32(aa_arrays + 1); - of_update_property(dr_node, new_prop); + /* copy the new associativity into the lookup array */ + index = aa_arrays * aa_array_entries + 2; + memcpy(&assoc_arrays[index], &lmb_assoc[1], aa_array_sz); - /* - * The associativity lookup array index for this lmb is - * number of entries - 1 since we added its associativity - * to the end of the lookup array. - */ - aa_index = be32_to_cpu(assoc_arrays[0]) - 1; - } + of_update_property(dr_node, new_prop); - return aa_index; + /* + * The associativity lookup array index for this lmb is + * number of entries - 1 since we added its associativity + * to the end of the lookup array. + */ + *aa_index = be32_to_cpu(assoc_arrays[0]) - 1; + return true; } static int update_lmb_associativity_index(struct drmem_lmb *lmb) @@ -169,6 +163,7 @@ static int update_lmb_associativity_index(struct drmem_lmb *lmb) struct property *ala_prop; const u32 *lmb_assoc; u32 aa_index; + bool found; parent = of_find_node_by_path("/"); if (!parent) @@ -200,12 +195,12 @@ static int update_lmb_associativity_index(struct drmem_lmb *lmb) return -ENODEV; } - aa_index = find_aa_index(dr_node, ala_prop, lmb_assoc); + found = find_aa_index(dr_node, ala_prop, lmb_assoc, &aa_index); of_node_put(dr_node); dlpar_free_cc_nodes(lmb_node); - if (aa_index < 0) { + if (!found) { pr_err("Could not find LMB associativity\n"); return -1; } @@ -306,7 +301,7 @@ static int pseries_remove_memblock(unsigned long base, unsigned int memblock_siz nid = memory_add_physaddr_to_nid(base); for (i = 0; i < sections_per_block; i++) { - remove_memory(nid, base, MIN_MEMORY_BLOCK_SIZE); + __remove_memory(nid, base, MIN_MEMORY_BLOCK_SIZE); base += MIN_MEMORY_BLOCK_SIZE; } @@ -371,8 +366,10 @@ static bool lmb_is_removable(struct drmem_lmb *lmb) for (i = 0; i < scns_per_block; i++) { pfn = PFN_DOWN(phys_addr); - if (!pfn_present(pfn)) + if (!pfn_present(pfn)) { + phys_addr += MIN_MEMORY_BLOCK_SIZE; continue; + } rc &= is_mem_section_removable(pfn, PAGES_PER_SECTION); phys_addr += MIN_MEMORY_BLOCK_SIZE; @@ -398,7 +395,7 @@ static int dlpar_remove_lmb(struct drmem_lmb *lmb) block_sz = pseries_memory_block_size(); nid = memory_add_physaddr_to_nid(lmb->base_addr); - remove_memory(nid, lmb->base_addr, block_sz); + __remove_memory(nid, lmb->base_addr, block_sz); /* Update memory regions for memory remove */ memblock_remove(lmb->base_addr, block_sz); @@ -685,7 +682,7 @@ static int dlpar_add_lmb(struct drmem_lmb *lmb) rc = dlpar_online_lmb(lmb); if (rc) { - remove_memory(nid, lmb->base_addr, block_sz); + __remove_memory(nid, lmb->base_addr, block_sz); invalidate_lmb_associativity_index(lmb); } else { lmb->flags |= DRCONF_MEM_ASSIGNED; diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index 06f02960b4392386375b5b4d87eb604cf7465438..b1a08cb760e0b6ab8c6c5b08a22d8c9e485babe9 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c @@ -167,10 +167,10 @@ static unsigned long tce_get_pseries(struct iommu_table *tbl, long index) return be64_to_cpu(*tcep); } -static void tce_free_pSeriesLP(struct iommu_table*, long, long); +static void tce_free_pSeriesLP(unsigned long liobn, long, long); static void tce_freemulti_pSeriesLP(struct iommu_table*, long, long); -static int tce_build_pSeriesLP(struct iommu_table *tbl, long tcenum, +static int tce_build_pSeriesLP(unsigned long liobn, long tcenum, long tceshift, long npages, unsigned long uaddr, enum dma_data_direction direction, unsigned long attrs) @@ -181,25 +181,25 @@ static int tce_build_pSeriesLP(struct iommu_table *tbl, long tcenum, int ret = 0; long tcenum_start = tcenum, npages_start = npages; - rpn = __pa(uaddr) >> TCE_SHIFT; + rpn = __pa(uaddr) >> tceshift; proto_tce = TCE_PCI_READ; if (direction != DMA_TO_DEVICE) proto_tce |= TCE_PCI_WRITE; while (npages--) { - tce = proto_tce | (rpn & TCE_RPN_MASK) << TCE_RPN_SHIFT; - rc = plpar_tce_put((u64)tbl->it_index, (u64)tcenum << 12, tce); + tce = proto_tce | (rpn & TCE_RPN_MASK) << tceshift; + rc = plpar_tce_put((u64)liobn, (u64)tcenum << tceshift, tce); if (unlikely(rc == H_NOT_ENOUGH_RESOURCES)) { ret = (int)rc; - tce_free_pSeriesLP(tbl, tcenum_start, + tce_free_pSeriesLP(liobn, tcenum_start, (npages_start - (npages + 1))); break; } if (rc && printk_ratelimit()) { printk("tce_build_pSeriesLP: plpar_tce_put failed. rc=%lld\n", rc); - printk("\tindex = 0x%llx\n", (u64)tbl->it_index); + printk("\tindex = 0x%llx\n", (u64)liobn); printk("\ttcenum = 0x%llx\n", (u64)tcenum); printk("\ttce val = 0x%llx\n", tce ); dump_stack(); @@ -228,7 +228,8 @@ static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum, unsigned long flags; if ((npages == 1) || !firmware_has_feature(FW_FEATURE_MULTITCE)) { - return tce_build_pSeriesLP(tbl, tcenum, npages, uaddr, + return tce_build_pSeriesLP(tbl->it_index, tcenum, + tbl->it_page_shift, npages, uaddr, direction, attrs); } @@ -244,8 +245,9 @@ static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum, /* If allocation fails, fall back to the loop implementation */ if (!tcep) { local_irq_restore(flags); - return tce_build_pSeriesLP(tbl, tcenum, npages, uaddr, - direction, attrs); + return tce_build_pSeriesLP(tbl->it_index, tcenum, + tbl->it_page_shift, + npages, uaddr, direction, attrs); } __this_cpu_write(tce_page, tcep); } @@ -296,16 +298,16 @@ static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum, return ret; } -static void tce_free_pSeriesLP(struct iommu_table *tbl, long tcenum, long npages) +static void tce_free_pSeriesLP(unsigned long liobn, long tcenum, long npages) { u64 rc; while (npages--) { - rc = plpar_tce_put((u64)tbl->it_index, (u64)tcenum << 12, 0); + rc = plpar_tce_put((u64)liobn, (u64)tcenum << 12, 0); if (rc && printk_ratelimit()) { printk("tce_free_pSeriesLP: plpar_tce_put failed. rc=%lld\n", rc); - printk("\tindex = 0x%llx\n", (u64)tbl->it_index); + printk("\tindex = 0x%llx\n", (u64)liobn); printk("\ttcenum = 0x%llx\n", (u64)tcenum); dump_stack(); } @@ -320,7 +322,7 @@ static void tce_freemulti_pSeriesLP(struct iommu_table *tbl, long tcenum, long n u64 rc; if (!firmware_has_feature(FW_FEATURE_MULTITCE)) - return tce_free_pSeriesLP(tbl, tcenum, npages); + return tce_free_pSeriesLP(tbl->it_index, tcenum, npages); rc = plpar_tce_stuff((u64)tbl->it_index, (u64)tcenum << 12, 0, npages); @@ -435,6 +437,19 @@ static int tce_setrange_multi_pSeriesLP(unsigned long start_pfn, u64 rc = 0; long l, limit; + if (!firmware_has_feature(FW_FEATURE_MULTITCE)) { + unsigned long tceshift = be32_to_cpu(maprange->tce_shift); + unsigned long dmastart = (start_pfn << PAGE_SHIFT) + + be64_to_cpu(maprange->dma_base); + unsigned long tcenum = dmastart >> tceshift; + unsigned long npages = num_pfn << PAGE_SHIFT >> tceshift; + void *uaddr = __va(start_pfn << PAGE_SHIFT); + + return tce_build_pSeriesLP(be32_to_cpu(maprange->liobn), + tcenum, tceshift, npages, (unsigned long) uaddr, + DMA_BIDIRECTIONAL, 0); + } + local_irq_disable(); /* to protect tcep and the page behind it */ tcep = __this_cpu_read(tce_page); diff --git a/arch/powerpc/platforms/pseries/mobility.c b/arch/powerpc/platforms/pseries/mobility.c index e4ea713833832273bbc3e1b0a5fc9b13c214bb6b..70744b4fbd9ed019a3ce6d425cd4d412fb18c1f1 100644 --- a/arch/powerpc/platforms/pseries/mobility.c +++ b/arch/powerpc/platforms/pseries/mobility.c @@ -24,6 +24,7 @@ #include #include #include "pseries.h" +#include "../../kernel/cacheinfo.h" static struct kobject *mobility_kobj; @@ -360,11 +361,20 @@ void post_mobility_fixup(void) */ cpus_read_lock(); + /* + * It's common for the destination firmware to replace cache + * nodes. Release all of the cacheinfo hierarchy's references + * before updating the device tree. + */ + cacheinfo_teardown(); + rc = pseries_devicetree_update(MIGRATION_SCOPE); if (rc) printk(KERN_ERR "Post-mobility device tree update " "failed: %d\n", rc); + cacheinfo_rebuild(); + cpus_read_unlock(); /* Possibly switch to a new RFI flush type */ diff --git a/arch/powerpc/platforms/pseries/vio.c b/arch/powerpc/platforms/pseries/vio.c index 49e04ec19238a82ca8a371b818615163f44ab754..0e7778be4c490e3201bef29fc85d7b6b4d35fba3 100644 --- a/arch/powerpc/platforms/pseries/vio.c +++ b/arch/powerpc/platforms/pseries/vio.c @@ -1195,6 +1195,8 @@ static struct iommu_table *vio_build_iommu_table(struct vio_dev *dev) if (tbl == NULL) return NULL; + kref_init(&tbl->it_kref); + of_parse_dma_window(dev->dev.of_node, dma_window, &tbl->it_index, &offset, &size); diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c index f0fa22e7d36c78b0011e4a7932266a9609f825bb..3291e5fb94bcc3675d307abbb4463183af6eec60 100644 --- a/arch/powerpc/xmon/xmon.c +++ b/arch/powerpc/xmon/xmon.c @@ -1878,15 +1878,14 @@ static void dump_300_sprs(void) printf("pidr = %.16lx tidr = %.16lx\n", mfspr(SPRN_PID), mfspr(SPRN_TIDR)); - printf("asdr = %.16lx psscr = %.16lx\n", - mfspr(SPRN_ASDR), hv ? mfspr(SPRN_PSSCR) - : mfspr(SPRN_PSSCR_PR)); + printf("psscr = %.16lx\n", + hv ? mfspr(SPRN_PSSCR) : mfspr(SPRN_PSSCR_PR)); if (!hv) return; - printf("ptcr = %.16lx\n", - mfspr(SPRN_PTCR)); + printf("ptcr = %.16lx asdr = %.16lx\n", + mfspr(SPRN_PTCR), mfspr(SPRN_ASDR)); #endif } diff --git a/arch/riscv/kernel/vdso/Makefile b/arch/riscv/kernel/vdso/Makefile index eed1c137f6183a1d4f4f8303df8a0e8c12cf1f2b..87f71a6cd3ef84c71750c9a43935a624b91aacba 100644 --- a/arch/riscv/kernel/vdso/Makefile +++ b/arch/riscv/kernel/vdso/Makefile @@ -55,7 +55,8 @@ quiet_cmd_vdsold = VDSOLD $@ cmd_vdsold = $(CC) $(KBUILD_CFLAGS) $(call cc-option, -no-pie) -nostdlib -nostartfiles $(SYSCFLAGS_$(@F)) \ -Wl,-T,$(filter-out FORCE,$^) -o $@.tmp && \ $(CROSS_COMPILE)objcopy \ - $(patsubst %, -G __vdso_%, $(vdso-syms)) $@.tmp $@ + $(patsubst %, -G __vdso_%, $(vdso-syms)) $@.tmp $@ && \ + rm $@.tmp # install commands for the unstripped file quiet_cmd_vdso_install = INSTALL $@ diff --git a/arch/s390/Makefile b/arch/s390/Makefile index e6c2e8925fefa3dc5e45322438d8a38cb21dbc7a..9a3a698c8fca5595a76e147d8d71da1ec09fc615 100644 --- a/arch/s390/Makefile +++ b/arch/s390/Makefile @@ -63,7 +63,7 @@ cflags-y += -Wa,-I$(srctree)/arch/$(ARCH)/include # cflags-$(CONFIG_FRAME_POINTER) += -fno-optimize-sibling-calls -ifeq ($(call cc-option-yn,-mpacked-stack),y) +ifeq ($(call cc-option-yn,-mpacked-stack -mbackchain -msoft-float),y) cflags-$(CONFIG_PACK_STACK) += -mpacked-stack -D__PACK_STACK aflags-$(CONFIG_PACK_STACK) += -D__PACK_STACK endif @@ -140,7 +140,7 @@ all: bzImage #KBUILD_IMAGE is necessary for packaging targets like rpm-pkg, deb-pkg... KBUILD_IMAGE := $(boot)/bzImage -install: vmlinux +install: $(Q)$(MAKE) $(build)=$(boot) $@ bzImage: vmlinux diff --git a/arch/s390/boot/Makefile b/arch/s390/boot/Makefile index f6a9b0c2035530fef55f9cf579d63a396c4598a9..45c72d1f9e7d270eba70a76310feb46387853e68 100644 --- a/arch/s390/boot/Makefile +++ b/arch/s390/boot/Makefile @@ -46,7 +46,7 @@ quiet_cmd_ar = AR $@ $(obj)/startup.a: $(OBJECTS) FORCE $(call if_changed,ar) -install: $(CONFIGURE) $(obj)/bzImage +install: sh -x $(srctree)/$(obj)/install.sh $(KERNELRELEASE) $(obj)/bzImage \ System.map "$(INSTALL_PATH)" diff --git a/arch/s390/include/asm/page.h b/arch/s390/include/asm/page.h index 0d753291c43c0f2427a2234b2fde9074b290ae3c..349b1c1ef7794a85c5328edd257d80d819a7aeca 100644 --- a/arch/s390/include/asm/page.h +++ b/arch/s390/include/asm/page.h @@ -33,6 +33,8 @@ #define ARCH_HAS_PREPARE_HUGEPAGE #define ARCH_HAS_HUGEPAGE_CLEAR_FLUSH +#define HAVE_ARCH_HUGETLB_UNMAPPED_AREA + #include #ifndef __ASSEMBLY__ @@ -40,7 +42,7 @@ void __storage_key_init_range(unsigned long start, unsigned long end); static inline void storage_key_init_range(unsigned long start, unsigned long end) { - if (PAGE_DEFAULT_KEY) + if (PAGE_DEFAULT_KEY != 0) __storage_key_init_range(start, end); } diff --git a/arch/s390/include/asm/qdio.h b/arch/s390/include/asm/qdio.h index 9c9970a5dfb10798ddd459dbcff7beae2d9ea42c..1f2cd98dcb058b9877463e31ba9990592f564c01 100644 --- a/arch/s390/include/asm/qdio.h +++ b/arch/s390/include/asm/qdio.h @@ -228,7 +228,7 @@ struct qdio_buffer { * @sbal: absolute SBAL address */ struct sl_element { - unsigned long sbal; + u64 sbal; } __attribute__ ((packed)); /** diff --git a/arch/s390/include/asm/timex.h b/arch/s390/include/asm/timex.h index 2dc9eb4e1acca26f09f5aad4d2cb45a5e19833a2..b6a4ce9dafafbfbbf96cfaab42e4348a5277ca39 100644 --- a/arch/s390/include/asm/timex.h +++ b/arch/s390/include/asm/timex.h @@ -155,7 +155,7 @@ static inline void get_tod_clock_ext(char *clk) static inline unsigned long long get_tod_clock(void) { - unsigned char clk[STORE_CLOCK_EXT_SIZE]; + char clk[STORE_CLOCK_EXT_SIZE]; get_tod_clock_ext(clk); return *((unsigned long long *)&clk[1]); diff --git a/arch/s390/kernel/kexec_elf.c b/arch/s390/kernel/kexec_elf.c index 602e7cc26d1181e8869bd9f4e51f240b286066c0..5cf340b778f188928e799db4c1ac773074797521 100644 --- a/arch/s390/kernel/kexec_elf.c +++ b/arch/s390/kernel/kexec_elf.c @@ -58,7 +58,7 @@ static int kexec_file_add_elf_kernel(struct kimage *image, if (ret) return ret; - data->memsz += buf.memsz; + data->memsz = ALIGN(data->memsz, phdr->p_align) + buf.memsz; } return 0; diff --git a/arch/s390/kernel/mcount.S b/arch/s390/kernel/mcount.S index e93fbf02490cf2fc24da579d46eaae59e225d9e2..83afd5b78e16ac28d46ac804b00d724b7c89baf6 100644 --- a/arch/s390/kernel/mcount.S +++ b/arch/s390/kernel/mcount.S @@ -25,6 +25,12 @@ ENTRY(ftrace_stub) #define STACK_PTREGS (STACK_FRAME_OVERHEAD) #define STACK_PTREGS_GPRS (STACK_PTREGS + __PT_GPRS) #define STACK_PTREGS_PSW (STACK_PTREGS + __PT_PSW) +#ifdef __PACK_STACK +/* allocate just enough for r14, r15 and backchain */ +#define TRACED_FUNC_FRAME_SIZE 24 +#else +#define TRACED_FUNC_FRAME_SIZE STACK_FRAME_OVERHEAD +#endif ENTRY(_mcount) BR_EX %r14 @@ -38,9 +44,16 @@ ENTRY(ftrace_caller) #if !(defined(CC_USING_HOTPATCH) || defined(CC_USING_NOP_MCOUNT)) aghi %r0,MCOUNT_RETURN_FIXUP #endif - aghi %r15,-STACK_FRAME_SIZE + # allocate stack frame for ftrace_caller to contain traced function + aghi %r15,-TRACED_FUNC_FRAME_SIZE stg %r1,__SF_BACKCHAIN(%r15) + stg %r0,(__SF_GPRS+8*8)(%r15) + stg %r15,(__SF_GPRS+9*8)(%r15) + # allocate pt_regs and stack frame for ftrace_trace_function + aghi %r15,-STACK_FRAME_SIZE stg %r1,(STACK_PTREGS_GPRS+15*8)(%r15) + aghi %r1,-TRACED_FUNC_FRAME_SIZE + stg %r1,__SF_BACKCHAIN(%r15) stg %r0,(STACK_PTREGS_PSW+8)(%r15) stmg %r2,%r14,(STACK_PTREGS_GPRS+2*8)(%r15) #ifdef CONFIG_HAVE_MARCH_Z196_FEATURES diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index 05ea466b9e403d23f29562523a4154ce2a31e6d8..3515f2b55eb9ec876c8161344b77201c3765440f 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -2109,7 +2109,7 @@ static int flic_ais_mode_get_all(struct kvm *kvm, struct kvm_device_attr *attr) return -EINVAL; if (!test_kvm_facility(kvm, 72)) - return -ENOTSUPP; + return -EOPNOTSUPP; mutex_lock(&fi->ais_lock); ais.simm = fi->simm; @@ -2412,7 +2412,7 @@ static int modify_ais_mode(struct kvm *kvm, struct kvm_device_attr *attr) int ret = 0; if (!test_kvm_facility(kvm, 72)) - return -ENOTSUPP; + return -EOPNOTSUPP; if (copy_from_user(&req, (void __user *)attr->addr, sizeof(req))) return -EFAULT; @@ -2492,7 +2492,7 @@ static int flic_ais_mode_set_all(struct kvm *kvm, struct kvm_device_attr *attr) struct kvm_s390_ais_all ais; if (!test_kvm_facility(kvm, 72)) - return -ENOTSUPP; + return -EOPNOTSUPP; if (copy_from_user(&ais, (void __user *)attr->addr, sizeof(ais))) return -EFAULT; diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index db3196aebaa1c82b8bc47ccc7727c374b3da8226..11c3cd906ab44a71a4701717425aace7f3676ef0 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -2564,9 +2564,7 @@ static void kvm_s390_vcpu_initial_reset(struct kvm_vcpu *vcpu) vcpu->arch.sie_block->gcr[14] = CR14_UNUSED_32 | CR14_UNUSED_33 | CR14_EXTERNAL_DAMAGE_SUBMASK; - /* make sure the new fpc will be lazily loaded */ - save_fpu_regs(); - current->thread.fpu.fpc = 0; + vcpu->run->s.regs.fpc = 0; vcpu->arch.sie_block->gbea = 1; vcpu->arch.sie_block->pp = 0; vcpu->arch.sie_block->fpf &= ~FPF_BPBC; @@ -3994,7 +3992,7 @@ long kvm_arch_vcpu_ioctl(struct file *filp, switch (ioctl) { case KVM_S390_STORE_STATUS: idx = srcu_read_lock(&vcpu->kvm->srcu); - r = kvm_s390_vcpu_store_status(vcpu, arg); + r = kvm_s390_store_status_unloaded(vcpu, arg); srcu_read_unlock(&vcpu->kvm->srcu, idx); break; case KVM_S390_SET_INITIAL_PSW: { diff --git a/arch/s390/mm/hugetlbpage.c b/arch/s390/mm/hugetlbpage.c index b0246c705a192aac8462ee99f7a7e32081a9c6ce..5674710a4841ce718ee3549a8d7e4f655e6eff1c 100644 --- a/arch/s390/mm/hugetlbpage.c +++ b/arch/s390/mm/hugetlbpage.c @@ -2,7 +2,7 @@ /* * IBM System z Huge TLB Page Support for Kernel. * - * Copyright IBM Corp. 2007,2016 + * Copyright IBM Corp. 2007,2020 * Author(s): Gerald Schaefer */ @@ -11,6 +11,9 @@ #include #include +#include +#include +#include /* * If the bit selected by single-bit bitmask "a" is set within "x", move @@ -267,3 +270,98 @@ static __init int setup_hugepagesz(char *opt) return 1; } __setup("hugepagesz=", setup_hugepagesz); + +static unsigned long hugetlb_get_unmapped_area_bottomup(struct file *file, + unsigned long addr, unsigned long len, + unsigned long pgoff, unsigned long flags) +{ + struct hstate *h = hstate_file(file); + struct vm_unmapped_area_info info; + + info.flags = 0; + info.length = len; + info.low_limit = current->mm->mmap_base; + info.high_limit = TASK_SIZE; + info.align_mask = PAGE_MASK & ~huge_page_mask(h); + info.align_offset = 0; + return vm_unmapped_area(&info); +} + +static unsigned long hugetlb_get_unmapped_area_topdown(struct file *file, + unsigned long addr0, unsigned long len, + unsigned long pgoff, unsigned long flags) +{ + struct hstate *h = hstate_file(file); + struct vm_unmapped_area_info info; + unsigned long addr; + + info.flags = VM_UNMAPPED_AREA_TOPDOWN; + info.length = len; + info.low_limit = max(PAGE_SIZE, mmap_min_addr); + info.high_limit = current->mm->mmap_base; + info.align_mask = PAGE_MASK & ~huge_page_mask(h); + info.align_offset = 0; + addr = vm_unmapped_area(&info); + + /* + * A failed mmap() very likely causes application failure, + * so fall back to the bottom-up function here. This scenario + * can happen with large stack limits and large mmap() + * allocations. + */ + if (addr & ~PAGE_MASK) { + VM_BUG_ON(addr != -ENOMEM); + info.flags = 0; + info.low_limit = TASK_UNMAPPED_BASE; + info.high_limit = TASK_SIZE; + addr = vm_unmapped_area(&info); + } + + return addr; +} + +unsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr, + unsigned long len, unsigned long pgoff, unsigned long flags) +{ + struct hstate *h = hstate_file(file); + struct mm_struct *mm = current->mm; + struct vm_area_struct *vma; + int rc; + + if (len & ~huge_page_mask(h)) + return -EINVAL; + if (len > TASK_SIZE - mmap_min_addr) + return -ENOMEM; + + if (flags & MAP_FIXED) { + if (prepare_hugepage_range(file, addr, len)) + return -EINVAL; + goto check_asce_limit; + } + + if (addr) { + addr = ALIGN(addr, huge_page_size(h)); + vma = find_vma(mm, addr); + if (TASK_SIZE - len >= addr && addr >= mmap_min_addr && + (!vma || addr + len <= vm_start_gap(vma))) + goto check_asce_limit; + } + + if (mm->get_unmapped_area == arch_get_unmapped_area) + addr = hugetlb_get_unmapped_area_bottomup(file, addr, len, + pgoff, flags); + else + addr = hugetlb_get_unmapped_area_topdown(file, addr, len, + pgoff, flags); + if (addr & ~PAGE_MASK) + return addr; + +check_asce_limit: + if (addr + len > current->mm->context.asce_limit && + addr + len <= TASK_SIZE) { + rc = crst_table_upgrade(mm, addr + len); + if (rc) + return (unsigned long) rc; + } + return addr; +} diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c index 3fa3e532361227ad134f32b46c6c0db58d9d1240..379a925d9e82f35507780fa62d70a5718c100cf0 100644 --- a/arch/s390/mm/init.c +++ b/arch/s390/mm/init.c @@ -239,15 +239,13 @@ int arch_add_memory(int nid, u64 start, u64 size, struct vmem_altmap *altmap, return rc; } -#ifdef CONFIG_MEMORY_HOTREMOVE -int arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap) +void arch_remove_memory(int nid, u64 start, u64 size, + struct vmem_altmap *altmap) { - /* - * There is no hardware or firmware interface which could trigger a - * hot memory remove on s390. So there is nothing that needs to be - * implemented. - */ - return -EBUSY; + unsigned long start_pfn = start >> PAGE_SHIFT; + unsigned long nr_pages = size >> PAGE_SHIFT; + + __remove_pages(start_pfn, nr_pages, altmap); + vmem_remove_mapping(start, size); } -#endif #endif /* CONFIG_MEMORY_HOTPLUG */ diff --git a/arch/s390/pci/pci_sysfs.c b/arch/s390/pci/pci_sysfs.c index 430c14b006d17b71854ac24345a5e90140e8fdf1..0e11fc023fe788859b520de866fcb57cee09dffa 100644 --- a/arch/s390/pci/pci_sysfs.c +++ b/arch/s390/pci/pci_sysfs.c @@ -13,6 +13,8 @@ #include #include +#include "../../../drivers/pci/pci.h" + #include #define zpci_attr(name, fmt, member) \ @@ -40,31 +42,50 @@ zpci_attr(segment3, "0x%02x\n", pfip[3]); static ssize_t recover_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { + struct kernfs_node *kn; struct pci_dev *pdev = to_pci_dev(dev); struct zpci_dev *zdev = to_zpci(pdev); - int ret; - - if (!device_remove_file_self(dev, attr)) - return count; - + int ret = 0; + + /* Can't use device_remove_self() here as that would lead us to lock + * the pci_rescan_remove_lock while holding the device' kernfs lock. + * This would create a possible deadlock with disable_slot() which is + * not directly protected by the device' kernfs lock but takes it + * during the device removal which happens under + * pci_rescan_remove_lock. + * + * This is analogous to sdev_store_delete() in + * drivers/scsi/scsi_sysfs.c + */ + kn = sysfs_break_active_protection(&dev->kobj, &attr->attr); + WARN_ON_ONCE(!kn); + /* device_remove_file() serializes concurrent calls ignoring all but + * the first + */ + device_remove_file(dev, attr); + + /* A concurrent call to recover_store() may slip between + * sysfs_break_active_protection() and the sysfs file removal. + * Once it unblocks from pci_lock_rescan_remove() the original pdev + * will already be removed. + */ pci_lock_rescan_remove(); - pci_stop_and_remove_bus_device(pdev); - ret = zpci_disable_device(zdev); - if (ret) - goto error; - - ret = zpci_enable_device(zdev); - if (ret) - goto error; - - pci_rescan_bus(zdev->bus); + if (pci_dev_is_added(pdev)) { + pci_stop_and_remove_bus_device(pdev); + ret = zpci_disable_device(zdev); + if (ret) + goto out; + + ret = zpci_enable_device(zdev); + if (ret) + goto out; + pci_rescan_bus(zdev->bus); + } +out: pci_unlock_rescan_remove(); - - return count; - -error: - pci_unlock_rescan_remove(); - return ret; + if (kn) + sysfs_unbreak_active_protection(kn); + return ret ? ret : count; } static DEVICE_ATTR_WO(recover); diff --git a/arch/sh/boards/mach-migor/setup.c b/arch/sh/boards/mach-migor/setup.c index 254f2c66270362845524352e5ad73a1b6bdecf9b..6cd3cd468047c62664bce7b3a79a2faa00258cb5 100644 --- a/arch/sh/boards/mach-migor/setup.c +++ b/arch/sh/boards/mach-migor/setup.c @@ -5,6 +5,7 @@ * Copyright (C) 2008 Magnus Damm */ #include +#include #include #include #include diff --git a/arch/sh/include/cpu-sh2a/cpu/sh7269.h b/arch/sh/include/cpu-sh2a/cpu/sh7269.h index d516e5d488180db7b83bd7bc9cf8c0e80f75f4cf..b887cc402b71255712c54b29b26100f7b24c99fa 100644 --- a/arch/sh/include/cpu-sh2a/cpu/sh7269.h +++ b/arch/sh/include/cpu-sh2a/cpu/sh7269.h @@ -78,8 +78,15 @@ enum { GPIO_FN_WDTOVF, /* CAN */ - GPIO_FN_CTX1, GPIO_FN_CRX1, GPIO_FN_CTX0, GPIO_FN_CTX0_CTX1, - GPIO_FN_CRX0, GPIO_FN_CRX0_CRX1, GPIO_FN_CRX0_CRX1_CRX2, + GPIO_FN_CTX2, GPIO_FN_CRX2, + GPIO_FN_CTX1, GPIO_FN_CRX1, + GPIO_FN_CTX0, GPIO_FN_CRX0, + GPIO_FN_CTX0_CTX1, GPIO_FN_CRX0_CRX1, + GPIO_FN_CTX0_CTX1_CTX2, GPIO_FN_CRX0_CRX1_CRX2, + GPIO_FN_CTX2_PJ21, GPIO_FN_CRX2_PJ20, + GPIO_FN_CTX1_PJ23, GPIO_FN_CRX1_PJ22, + GPIO_FN_CTX0_CTX1_PJ23, GPIO_FN_CRX0_CRX1_PJ22, + GPIO_FN_CTX0_CTX1_CTX2_PJ21, GPIO_FN_CRX0_CRX1_CRX2_PJ20, /* DMAC */ GPIO_FN_TEND0, GPIO_FN_DACK0, GPIO_FN_DREQ0, diff --git a/arch/sh/mm/init.c b/arch/sh/mm/init.c index 7713c084d0402ac5c5f3ce5619cdafd1b12e37c3..47882be91121d1338dc87fc9732d47211cf5f856 100644 --- a/arch/sh/mm/init.c +++ b/arch/sh/mm/init.c @@ -443,21 +443,12 @@ int memory_add_physaddr_to_nid(u64 addr) EXPORT_SYMBOL_GPL(memory_add_physaddr_to_nid); #endif -#ifdef CONFIG_MEMORY_HOTREMOVE -int arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap) +void arch_remove_memory(int nid, u64 start, u64 size, + struct vmem_altmap *altmap) { unsigned long start_pfn = PFN_DOWN(start); unsigned long nr_pages = size >> PAGE_SHIFT; - struct zone *zone; - int ret; - - zone = page_zone(pfn_to_page(start_pfn)); - ret = __remove_pages(zone, start_pfn, nr_pages, altmap); - if (unlikely(ret)) - pr_warn("%s: Failed, __remove_pages() == %d\n", __func__, - ret); - return ret; + __remove_pages(start_pfn, nr_pages, altmap); } -#endif #endif /* CONFIG_MEMORY_HOTPLUG */ diff --git a/arch/sparc/include/uapi/asm/ipcbuf.h b/arch/sparc/include/uapi/asm/ipcbuf.h index 9d0d125500e240bcd586eeb97a675e4f29ce5414..084b8949ddff6bbcb81a728160bdef5e5f58abad 100644 --- a/arch/sparc/include/uapi/asm/ipcbuf.h +++ b/arch/sparc/include/uapi/asm/ipcbuf.h @@ -15,19 +15,19 @@ struct ipc64_perm { - __kernel_key_t key; - __kernel_uid_t uid; - __kernel_gid_t gid; - __kernel_uid_t cuid; - __kernel_gid_t cgid; + __kernel_key_t key; + __kernel_uid32_t uid; + __kernel_gid32_t gid; + __kernel_uid32_t cuid; + __kernel_gid32_t cgid; #ifndef __arch64__ - unsigned short __pad0; + unsigned short __pad0; #endif - __kernel_mode_t mode; - unsigned short __pad1; - unsigned short seq; - unsigned long long __unused1; - unsigned long long __unused2; + __kernel_mode_t mode; + unsigned short __pad1; + unsigned short seq; + unsigned long long __unused1; + unsigned long long __unused2; }; #endif /* __SPARC_IPCBUF_H */ diff --git a/arch/sparc/kernel/vmlinux.lds.S b/arch/sparc/kernel/vmlinux.lds.S index 61afd787bd0c7a7f82fef18f69aa7da6c1898425..59b6df13ddeadf0f08bc693132617c439ac3f9da 100644 --- a/arch/sparc/kernel/vmlinux.lds.S +++ b/arch/sparc/kernel/vmlinux.lds.S @@ -172,12 +172,14 @@ SECTIONS } PERCPU_SECTION(SMP_CACHE_BYTES) -#ifdef CONFIG_JUMP_LABEL . = ALIGN(PAGE_SIZE); .exit.text : { EXIT_TEXT } -#endif + + .exit.data : { + EXIT_DATA + } . = ALIGN(PAGE_SIZE); __init_end = .; diff --git a/arch/um/drivers/chan_kern.c b/arch/um/drivers/chan_kern.c index 05588f9466c7ff1a1b028cb710cab37b65b0d7ba..13ba195f9c9c8b1b3ec9344c60f126c0b374d3f0 100644 --- a/arch/um/drivers/chan_kern.c +++ b/arch/um/drivers/chan_kern.c @@ -171,19 +171,55 @@ int enable_chan(struct line *line) return err; } +/* Items are added in IRQ context, when free_irq can't be called, and + * removed in process context, when it can. + * This handles interrupt sources which disappear, and which need to + * be permanently disabled. This is discovered in IRQ context, but + * the freeing of the IRQ must be done later. + */ +static DEFINE_SPINLOCK(irqs_to_free_lock); +static LIST_HEAD(irqs_to_free); + +void free_irqs(void) +{ + struct chan *chan; + LIST_HEAD(list); + struct list_head *ele; + unsigned long flags; + + spin_lock_irqsave(&irqs_to_free_lock, flags); + list_splice_init(&irqs_to_free, &list); + spin_unlock_irqrestore(&irqs_to_free_lock, flags); + + list_for_each(ele, &list) { + chan = list_entry(ele, struct chan, free_list); + + if (chan->input && chan->enabled) + um_free_irq(chan->line->driver->read_irq, chan); + if (chan->output && chan->enabled) + um_free_irq(chan->line->driver->write_irq, chan); + chan->enabled = 0; + } +} + static void close_one_chan(struct chan *chan, int delay_free_irq) { + unsigned long flags; + if (!chan->opened) return; - /* we can safely call free now - it will be marked - * as free and freed once the IRQ stopped processing - */ - if (chan->input && chan->enabled) - um_free_irq(chan->line->driver->read_irq, chan); - if (chan->output && chan->enabled) - um_free_irq(chan->line->driver->write_irq, chan); - chan->enabled = 0; + if (delay_free_irq) { + spin_lock_irqsave(&irqs_to_free_lock, flags); + list_add(&chan->free_list, &irqs_to_free); + spin_unlock_irqrestore(&irqs_to_free_lock, flags); + } else { + if (chan->input && chan->enabled) + um_free_irq(chan->line->driver->read_irq, chan); + if (chan->output && chan->enabled) + um_free_irq(chan->line->driver->write_irq, chan); + chan->enabled = 0; + } if (chan->ops->close != NULL) (*chan->ops->close)(chan->fd, chan->data); diff --git a/arch/um/include/asm/irq.h b/arch/um/include/asm/irq.h index 49ed3e35b35adb1a5b679cfcbfe4f72d2cb97d09..ce7a78c3bcf218614371602992fbf41ac4d8f170 100644 --- a/arch/um/include/asm/irq.h +++ b/arch/um/include/asm/irq.h @@ -23,7 +23,7 @@ #define VECTOR_BASE_IRQ 15 #define VECTOR_IRQ_SPACE 8 -#define LAST_IRQ (VECTOR_IRQ_SPACE + VECTOR_BASE_IRQ) +#define LAST_IRQ (VECTOR_IRQ_SPACE + VECTOR_BASE_IRQ - 1) #else diff --git a/arch/um/kernel/irq.c b/arch/um/kernel/irq.c index 6b7f3827d6e4add1993315c220bf96217bfb8986..2753718d31b9d150d48433676af8bc5b5c93105a 100644 --- a/arch/um/kernel/irq.c +++ b/arch/um/kernel/irq.c @@ -21,6 +21,8 @@ #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_fd array for each fd - @@ -100,6 +102,8 @@ void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs) } } } + + free_irqs(); } static int assign_epoll_events_to_irq(struct irq_entry *irq_entry) diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug index 7d68f0c7cfb1ef603f9791cd2154d217ce647beb..687cd1a213d50f15ab1c82664c0cc351c4f691c7 100644 --- a/arch/x86/Kconfig.debug +++ b/arch/x86/Kconfig.debug @@ -181,7 +181,7 @@ config HAVE_MMIOTRACE_SUPPORT config X86_DECODER_SELFTEST bool "x86 instruction decoder selftest" - depends on DEBUG_KERNEL && KPROBES + depends on DEBUG_KERNEL && INSTRUCTION_DECODER depends on !COMPILE_TEST ---help--- Perform x86 instruction decoder selftests at build time. diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S index f62e347862ccc61ba417d80dabee304ef28b6ec7..4eaa724afce3446bd2d7b24b43aa730347e05557 100644 --- a/arch/x86/boot/compressed/head_64.S +++ b/arch/x86/boot/compressed/head_64.S @@ -244,6 +244,11 @@ ENTRY(efi32_stub_entry) leal efi32_config(%ebp), %eax movl %eax, efi_config(%ebp) + /* Disable paging */ + movl %cr0, %eax + btrl $X86_CR0_PG_BIT, %eax + movl %eax, %cr0 + jmp startup_32 ENDPROC(efi32_stub_entry) #endif diff --git a/arch/x86/boot/compressed/kaslr_64.c b/arch/x86/boot/compressed/kaslr_64.c index 748456c365f4691af041753c63d6991f9bc8b4b8..9557c5a15b91e29a6465e502599960aad87e3e60 100644 --- a/arch/x86/boot/compressed/kaslr_64.c +++ b/arch/x86/boot/compressed/kaslr_64.c @@ -29,9 +29,6 @@ #define __PAGE_OFFSET __PAGE_OFFSET_BASE #include "../../mm/ident_map.c" -/* Used by pgtable.h asm code to force instruction serialization. */ -unsigned long __force_order; - /* Used to track our page table allocation area. */ struct alloc_pgt_data { unsigned char *pgt_buf; diff --git a/arch/x86/configs/gki_defconfig b/arch/x86/configs/gki_defconfig index f2e9c4a105a5135f8ef8cea9e894b3ae05c170bd..0f5ab5ebc3a78fc449e75061ab4d771f65ab44b0 100644 --- a/arch/x86/configs/gki_defconfig +++ b/arch/x86/configs/gki_defconfig @@ -9,14 +9,16 @@ CONFIG_TASK_IO_ACCOUNTING=y CONFIG_PSI=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y -CONFIG_IKHEADERS=m +CONFIG_IKHEADERS=y CONFIG_MEMCG=y CONFIG_MEMCG_SWAP=y -CONFIG_RT_GROUP_SCHED=y CONFIG_CGROUP_FREEZER=y CONFIG_CPUSETS=y CONFIG_CGROUP_CPUACCT=y CONFIG_CGROUP_BPF=y +CONFIG_NAMESPACES=y +# CONFIG_UTS_NS is not set +# CONFIG_PID_NS is not set CONFIG_SCHED_AUTOGROUP=y CONFIG_SCHED_TUNE=y CONFIG_BLK_DEV_INITRD=y @@ -24,7 +26,6 @@ CONFIG_BLK_DEV_INITRD=y # CONFIG_RD_LZMA is not set # CONFIG_RD_XZ is not set # CONFIG_RD_LZO is not set -# CONFIG_RD_LZ4 is not set # CONFIG_SYSFS_SYSCALL is not set # CONFIG_FHANDLE is not set CONFIG_KALLSYMS_ALL=y @@ -32,7 +33,6 @@ CONFIG_BPF_SYSCALL=y CONFIG_BPF_JIT_ALWAYS_ON=y # CONFIG_RSEQ is not set CONFIG_EMBEDDED=y -# CONFIG_VM_EVENT_COUNTERS is not set # CONFIG_COMPAT_BRK is not set # CONFIG_SLAB_MERGE_DEFAULT is not set CONFIG_PROFILING=y @@ -41,19 +41,25 @@ CONFIG_HYPERVISOR_GUEST=y CONFIG_PARAVIRT=y CONFIG_NR_CPUS=32 CONFIG_EFI=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=0 +# CONFIG_PM_WAKELOCKS_GC is not set CONFIG_CPU_FREQ_TIMES=y CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y -CONFIG_CPUFREQ_DUMMY=m +CONFIG_PCI_MSI=y CONFIG_IA32_EMULATION=y -CONFIG_KPROBES=y +CONFIG_LTO_CLANG=y +CONFIG_CFI_CLANG=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_MODVERSIONS=y CONFIG_BLK_INLINE_ENCRYPTION=y +CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK=y CONFIG_GKI_HACKS_TO_FIX=y # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set -CONFIG_BINFMT_MISC=m +CONFIG_BINFMT_MISC=y +CONFIG_CLEANCACHE=y CONFIG_ZSMALLOC=y CONFIG_NET=y CONFIG_PACKET=y @@ -62,6 +68,7 @@ CONFIG_XFRM_USER=y CONFIG_XFRM_INTERFACE=y CONFIG_XFRM_STATISTICS=y CONFIG_NET_KEY=y +CONFIG_XDP_SOCKETS=y CONFIG_INET=y CONFIG_IP_MULTICAST=y CONFIG_IP_ADVANCED_ROUTER=y @@ -122,6 +129,7 @@ CONFIG_NETFILTER_XT_MATCH_POLICY=y CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y CONFIG_NETFILTER_XT_MATCH_QUOTA=y CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y CONFIG_NETFILTER_XT_MATCH_SOCKET=y CONFIG_NETFILTER_XT_MATCH_STATE=y CONFIG_NETFILTER_XT_MATCH_STATISTIC=y @@ -159,31 +167,21 @@ CONFIG_NET_CLS_BPF=y CONFIG_NET_EMATCH=y CONFIG_NET_EMATCH_U32=y CONFIG_NET_CLS_ACT=y -CONFIG_VSOCKETS=m -CONFIG_VIRTIO_VSOCKETS=m CONFIG_BPF_JIT=y -CONFIG_CAN=m -# CONFIG_CAN_BCM is not set -# CONFIG_CAN_GW is not set -CONFIG_CAN_VCAN=m CONFIG_CFG80211=y # CONFIG_CFG80211_DEFAULT_PS is not set # CONFIG_CFG80211_CRDA_SUPPORT is not set CONFIG_MAC80211=y -# CONFIG_MAC80211_RC_MINSTREL is not set CONFIG_RFKILL=y # CONFIG_UEVENT_HELPER is not set # CONFIG_FW_CACHE is not set # CONFIG_ALLOW_DEV_COREDUMP is not set -CONFIG_DEBUG_DEVRES=y CONFIG_GNSS=y -CONFIG_GNSS_CMDLINE_SERIAL=m CONFIG_OF=y CONFIG_ZRAM=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 -CONFIG_VIRTIO_BLK=m CONFIG_UID_SYS_STATS=y CONFIG_SCSI=y # CONFIG_SCSI_MQ_DEFAULT is not set @@ -192,6 +190,7 @@ CONFIG_BLK_DEV_SD=y CONFIG_MD=y CONFIG_BLK_DEV_DM=y CONFIG_DM_CRYPT=y +CONFIG_DM_DEFAULT_KEY=y CONFIG_DM_SNAPSHOT=y CONFIG_DM_UEVENT=y CONFIG_DM_VERITY=y @@ -199,8 +198,8 @@ CONFIG_DM_VERITY_AVB=y CONFIG_DM_VERITY_FEC=y CONFIG_DM_BOW=y CONFIG_NETDEVICES=y +CONFIG_DUMMY=y CONFIG_TUN=y -CONFIG_VIRTIO_NET=m # CONFIG_ETHERNET is not set CONFIG_PHYLIB=y CONFIG_PPP=y @@ -234,11 +233,11 @@ CONFIG_USB_USBNET=y # CONFIG_WLAN_VENDOR_TI is not set # CONFIG_WLAN_VENDOR_ZYDAS is not set # CONFIG_WLAN_VENDOR_QUANTENNA is not set -CONFIG_VIRT_WIFI=m CONFIG_INPUT_EVDEV=y CONFIG_KEYBOARD_GPIO=y # CONFIG_INPUT_MOUSE is not set CONFIG_INPUT_JOYSTICK=y +CONFIG_JOYSTICK_XPAD=y CONFIG_INPUT_MISC=y CONFIG_INPUT_UINPUT=y # CONFIG_VT is not set @@ -248,11 +247,10 @@ CONFIG_SERIAL_8250=y # CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set CONFIG_SERIAL_8250_CONSOLE=y # CONFIG_SERIAL_8250_EXAR is not set -CONFIG_SERIAL_OF_PLATFORM=m +CONFIG_SERIAL_OF_PLATFORM=y CONFIG_SERIAL_DEV_BUS=y -CONFIG_VIRTIO_CONSOLE=m CONFIG_HW_RANDOM=y -CONFIG_HW_RANDOM_VIRTIO=m +CONFIG_HPET=y # CONFIG_DEVPORT is not set # CONFIG_I2C_COMPAT is not set # CONFIG_I2C_HELPER_AUTO is not set @@ -261,6 +259,9 @@ CONFIG_GPIOLIB=y # CONFIG_HWMON is not set CONFIG_DEVFREQ_THERMAL=y # CONFIG_X86_PKG_TEMP_THERMAL is not set +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +CONFIG_MFD_SYSCON=y CONFIG_REGULATOR=y CONFIG_REGULATOR_FIXED_VOLTAGE=y CONFIG_MEDIA_SUPPORT=y @@ -268,7 +269,6 @@ CONFIG_MEDIA_CAMERA_SUPPORT=y # CONFIG_VGA_ARB is not set CONFIG_DRM=y # CONFIG_DRM_FBDEV_EMULATION is not set -CONFIG_DRM_VIRTIO_GPU=m CONFIG_BACKLIGHT_LCD_SUPPORT=y # CONFIG_LCD_CLASS_DEVICE is not set CONFIG_BACKLIGHT_CLASS_DEVICE=y @@ -279,8 +279,8 @@ CONFIG_SND_DYNAMIC_MINORS=y # CONFIG_SND_SUPPORT_OLD_API is not set # CONFIG_SND_VERBOSE_PROCFS is not set # CONFIG_SND_DRIVERS is not set -CONFIG_SND_INTEL8X0=m -# CONFIG_SND_USB is not set +CONFIG_SND_USB_AUDIO=y +CONFIG_SND_SOC=y CONFIG_HIDRAW=y CONFIG_UHID=y CONFIG_HID_APPLE=y @@ -288,42 +288,38 @@ CONFIG_HID_ELECOM=y CONFIG_HID_MAGICMOUSE=y CONFIG_HID_MICROSOFT=y CONFIG_HID_MULTITOUCH=y +CONFIG_HID_NINTENDO=y +CONFIG_HID_SONY=y +CONFIG_HID_STEAM=y CONFIG_USB_HIDDEV=y -CONFIG_USB=y CONFIG_USB_GADGET=y -CONFIG_USB_DUMMY_HCD=m CONFIG_USB_CONFIGFS=y CONFIG_USB_CONFIGFS_UEVENT=y +CONFIG_USB_CONFIGFS_MASS_STORAGE=y CONFIG_USB_CONFIGFS_F_FS=y CONFIG_USB_CONFIGFS_F_ACC=y CONFIG_USB_CONFIGFS_F_AUDIO_SRC=y CONFIG_USB_CONFIGFS_F_MIDI=y -CONFIG_MMC=m +CONFIG_MMC=y # CONFIG_PWRSEQ_EMMC is not set # CONFIG_PWRSEQ_SIMPLE is not set -CONFIG_MMC_SDHCI=m -CONFIG_MMC_SDHCI_PLTFM=m +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y CONFIG_NEW_LEDS=y CONFIG_LEDS_CLASS=y CONFIG_LEDS_TRIGGERS=y CONFIG_RTC_CLASS=y # CONFIG_RTC_SYSTOHC is not set -CONFIG_RTC_DRV_TEST=m CONFIG_UIO=y -CONFIG_VIRTIO_PCI=m -# CONFIG_VIRTIO_PCI_LEGACY is not set -CONFIG_VIRTIO_BALLOON=m -CONFIG_VIRTIO_INPUT=m -CONFIG_VIRTIO_MMIO=m -CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y CONFIG_STAGING=y CONFIG_ASHMEM=y -CONFIG_ION=y -CONFIG_ION_SYSTEM_HEAP=y CONFIG_PM_DEVFREQ=y CONFIG_IIO=y CONFIG_ANDROID=y CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_BINDERFS=y +CONFIG_LIBNVDIMM=y +# CONFIG_ND_BLK is not set CONFIG_EXT4_FS=y CONFIG_EXT4_FS_SECURITY=y CONFIG_EXT4_ENCRYPTION=y @@ -338,12 +334,12 @@ CONFIG_QUOTA=y CONFIG_QFMT_V2=y CONFIG_FUSE_FS=y CONFIG_OVERLAY_FS=y +CONFIG_INCREMENTAL_FS=y CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y # CONFIG_EFIVAR_FS is not set -CONFIG_SDCARD_FS=y CONFIG_PSTORE=y CONFIG_PSTORE_CONSOLE=y CONFIG_PSTORE_RAM=y @@ -396,11 +392,15 @@ CONFIG_NLS_MAC_INUIT=y CONFIG_NLS_MAC_ROMANIAN=y CONFIG_NLS_MAC_TURKISH=y CONFIG_NLS_UTF8=y +CONFIG_UNICODE=y CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y CONFIG_SECURITY=y +CONFIG_SECURITYFS=y CONFIG_SECURITY_NETWORK=y CONFIG_HARDENED_USERCOPY=y CONFIG_SECURITY_SELINUX=y +CONFIG_INIT_STACK_ALL=y +CONFIG_INIT_ON_ALLOC_DEFAULT_ON=y CONFIG_CRYPTO_ADIANTUM=y CONFIG_CRYPTO_SHA256_SSSE3=y CONFIG_CRYPTO_AES_NI_INTEL=y @@ -420,5 +420,4 @@ CONFIG_SOFTLOCKUP_DETECTOR=y # CONFIG_DETECT_HUNG_TASK is not set CONFIG_PANIC_TIMEOUT=5 CONFIG_SCHEDSTATS=y -# CONFIG_RUNTIME_TESTING_MENU is not set CONFIG_UNWINDER_FRAME_POINTER=y diff --git a/arch/x86/crypto/sha1_avx2_x86_64_asm.S b/arch/x86/crypto/sha1_avx2_x86_64_asm.S index 9f712a7dfd797cc499e1ef52ba7999078530494a..7e578fa5d0a7423f2804a5706d86529736e8a278 100644 --- a/arch/x86/crypto/sha1_avx2_x86_64_asm.S +++ b/arch/x86/crypto/sha1_avx2_x86_64_asm.S @@ -62,11 +62,11 @@ *Visit http://software.intel.com/en-us/articles/ *and refer to improving-the-performance-of-the-secure-hash-algorithm-1/ * - *Updates 20-byte SHA-1 record in 'hash' for even number of - *'num_blocks' consecutive 64-byte blocks + *Updates 20-byte SHA-1 record at start of 'state', from 'input', for + *even number of 'blocks' consecutive 64-byte blocks. * *extern "C" void sha1_transform_avx2( - * int *hash, const char* input, size_t num_blocks ); + * struct sha1_state *state, const u8* input, int blocks ); */ #include diff --git a/arch/x86/crypto/sha1_ssse3_asm.S b/arch/x86/crypto/sha1_ssse3_asm.S index 613d0bfc3d840f93fb2b047479f6c5eb6c2056be..5a2f85782c4ac31c547f48a6b1297af72ed87824 100644 --- a/arch/x86/crypto/sha1_ssse3_asm.S +++ b/arch/x86/crypto/sha1_ssse3_asm.S @@ -461,9 +461,13 @@ W_PRECALC_SSSE3 movdqu \a,\b .endm -/* SSSE3 optimized implementation: - * extern "C" void sha1_transform_ssse3(u32 *digest, const char *data, u32 *ws, - * unsigned int rounds); +/* + * SSSE3 optimized implementation: + * + * extern "C" void sha1_transform_ssse3(struct sha1_state *state, + * const u8 *data, int blocks); + * + * Note that struct sha1_state is assumed to begin with u32 state[5]. */ SHA1_VECTOR_ASM sha1_transform_ssse3 @@ -549,8 +553,8 @@ W_PRECALC_AVX /* AVX optimized implementation: - * extern "C" void sha1_transform_avx(u32 *digest, const char *data, u32 *ws, - * unsigned int rounds); + * extern "C" void sha1_transform_avx(struct sha1_state *state, + * const u8 *data, int blocks); */ SHA1_VECTOR_ASM sha1_transform_avx diff --git a/arch/x86/crypto/sha1_ssse3_glue.c b/arch/x86/crypto/sha1_ssse3_glue.c index 7391c7de72c7cabdbab895726de8ec4db7e2f7e9..cb15a7a62e1e9bcabcabcdaff0236fc74022ba4e 100644 --- a/arch/x86/crypto/sha1_ssse3_glue.c +++ b/arch/x86/crypto/sha1_ssse3_glue.c @@ -31,11 +31,8 @@ #include #include -typedef void (sha1_transform_fn)(u32 *digest, const char *data, - unsigned int rounds); - static int sha1_update(struct shash_desc *desc, const u8 *data, - unsigned int len, sha1_transform_fn *sha1_xform) + unsigned int len, sha1_block_fn *sha1_xform) { struct sha1_state *sctx = shash_desc_ctx(desc); @@ -43,48 +40,47 @@ static int sha1_update(struct shash_desc *desc, const u8 *data, (sctx->count % SHA1_BLOCK_SIZE) + len < SHA1_BLOCK_SIZE) return crypto_sha1_update(desc, data, len); - /* make sure casting to sha1_block_fn() is safe */ + /* + * Make sure struct sha1_state begins directly with the SHA1 + * 160-bit internal state, as this is what the asm functions expect. + */ BUILD_BUG_ON(offsetof(struct sha1_state, state) != 0); kernel_fpu_begin(); - sha1_base_do_update(desc, data, len, - (sha1_block_fn *)sha1_xform); + sha1_base_do_update(desc, data, len, sha1_xform); kernel_fpu_end(); return 0; } static int sha1_finup(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out, sha1_transform_fn *sha1_xform) + unsigned int len, u8 *out, sha1_block_fn *sha1_xform) { if (!irq_fpu_usable()) return crypto_sha1_finup(desc, data, len, out); kernel_fpu_begin(); if (len) - sha1_base_do_update(desc, data, len, - (sha1_block_fn *)sha1_xform); - sha1_base_do_finalize(desc, (sha1_block_fn *)sha1_xform); + sha1_base_do_update(desc, data, len, sha1_xform); + sha1_base_do_finalize(desc, sha1_xform); kernel_fpu_end(); return sha1_base_finish(desc, out); } -asmlinkage void sha1_transform_ssse3(u32 *digest, const char *data, - unsigned int rounds); +asmlinkage void sha1_transform_ssse3(struct sha1_state *state, + const u8 *data, int blocks); static int sha1_ssse3_update(struct shash_desc *desc, const u8 *data, unsigned int len) { - return sha1_update(desc, data, len, - (sha1_transform_fn *) sha1_transform_ssse3); + return sha1_update(desc, data, len, sha1_transform_ssse3); } static int sha1_ssse3_finup(struct shash_desc *desc, const u8 *data, unsigned int len, u8 *out) { - return sha1_finup(desc, data, len, out, - (sha1_transform_fn *) sha1_transform_ssse3); + return sha1_finup(desc, data, len, out, sha1_transform_ssse3); } /* Add padding and return the message digest. */ @@ -123,21 +119,19 @@ static void unregister_sha1_ssse3(void) } #ifdef CONFIG_AS_AVX -asmlinkage void sha1_transform_avx(u32 *digest, const char *data, - unsigned int rounds); +asmlinkage void sha1_transform_avx(struct sha1_state *state, + const u8 *data, int blocks); static int sha1_avx_update(struct shash_desc *desc, const u8 *data, unsigned int len) { - return sha1_update(desc, data, len, - (sha1_transform_fn *) sha1_transform_avx); + return sha1_update(desc, data, len, sha1_transform_avx); } static int sha1_avx_finup(struct shash_desc *desc, const u8 *data, unsigned int len, u8 *out) { - return sha1_finup(desc, data, len, out, - (sha1_transform_fn *) sha1_transform_avx); + return sha1_finup(desc, data, len, out, sha1_transform_avx); } static int sha1_avx_final(struct shash_desc *desc, u8 *out) @@ -194,8 +188,8 @@ static inline void unregister_sha1_avx(void) { } #if defined(CONFIG_AS_AVX2) && (CONFIG_AS_AVX) #define SHA1_AVX2_BLOCK_OPTSIZE 4 /* optimal 4*64 bytes of SHA1 blocks */ -asmlinkage void sha1_transform_avx2(u32 *digest, const char *data, - unsigned int rounds); +asmlinkage void sha1_transform_avx2(struct sha1_state *state, + const u8 *data, int blocks); static bool avx2_usable(void) { @@ -207,28 +201,26 @@ static bool avx2_usable(void) return false; } -static void sha1_apply_transform_avx2(u32 *digest, const char *data, - unsigned int rounds) +static void sha1_apply_transform_avx2(struct sha1_state *state, + const u8 *data, int blocks) { /* Select the optimal transform based on data block size */ - if (rounds >= SHA1_AVX2_BLOCK_OPTSIZE) - sha1_transform_avx2(digest, data, rounds); + if (blocks >= SHA1_AVX2_BLOCK_OPTSIZE) + sha1_transform_avx2(state, data, blocks); else - sha1_transform_avx(digest, data, rounds); + sha1_transform_avx(state, data, blocks); } static int sha1_avx2_update(struct shash_desc *desc, const u8 *data, unsigned int len) { - return sha1_update(desc, data, len, - (sha1_transform_fn *) sha1_apply_transform_avx2); + return sha1_update(desc, data, len, sha1_apply_transform_avx2); } static int sha1_avx2_finup(struct shash_desc *desc, const u8 *data, unsigned int len, u8 *out) { - return sha1_finup(desc, data, len, out, - (sha1_transform_fn *) sha1_apply_transform_avx2); + return sha1_finup(desc, data, len, out, sha1_apply_transform_avx2); } static int sha1_avx2_final(struct shash_desc *desc, u8 *out) @@ -271,21 +263,19 @@ static inline void unregister_sha1_avx2(void) { } #endif #ifdef CONFIG_AS_SHA1_NI -asmlinkage void sha1_ni_transform(u32 *digest, const char *data, - unsigned int rounds); +asmlinkage void sha1_ni_transform(struct sha1_state *digest, const u8 *data, + int rounds); static int sha1_ni_update(struct shash_desc *desc, const u8 *data, unsigned int len) { - return sha1_update(desc, data, len, - (sha1_transform_fn *) sha1_ni_transform); + return sha1_update(desc, data, len, sha1_ni_transform); } static int sha1_ni_finup(struct shash_desc *desc, const u8 *data, unsigned int len, u8 *out) { - return sha1_finup(desc, data, len, out, - (sha1_transform_fn *) sha1_ni_transform); + return sha1_finup(desc, data, len, out, sha1_ni_transform); } static int sha1_ni_final(struct shash_desc *desc, u8 *out) diff --git a/arch/x86/crypto/sha256-avx-asm.S b/arch/x86/crypto/sha256-avx-asm.S index 001bbcf93c79ba08628abcf30dd49ad77158420d..b6e037ee66613eadc410a956b4aed01be422870f 100644 --- a/arch/x86/crypto/sha256-avx-asm.S +++ b/arch/x86/crypto/sha256-avx-asm.S @@ -341,8 +341,8 @@ a = TMP_ .endm ######################################################################## -## void sha256_transform_avx(void *input_data, UINT32 digest[8], UINT64 num_blks) -## arg 1 : pointer to digest +## void sha256_transform_avx(state sha256_state *state, const u8 *data, int blocks) +## arg 1 : pointer to state ## arg 2 : pointer to input data ## arg 3 : Num blocks ######################################################################## diff --git a/arch/x86/crypto/sha256-avx2-asm.S b/arch/x86/crypto/sha256-avx2-asm.S index 1420db15dcddc8505b57faad5e6e6701f972b517..2e6ebc904a3a9fb5ff718d818f0539477b7d8d71 100644 --- a/arch/x86/crypto/sha256-avx2-asm.S +++ b/arch/x86/crypto/sha256-avx2-asm.S @@ -520,8 +520,8 @@ STACK_SIZE = _RSP + _RSP_SIZE .endm ######################################################################## -## void sha256_transform_rorx(void *input_data, UINT32 digest[8], UINT64 num_blks) -## arg 1 : pointer to digest +## void sha256_transform_rorx(struct sha256_state *state, const u8 *data, int blocks) +## arg 1 : pointer to state ## arg 2 : pointer to input data ## arg 3 : Num blocks ######################################################################## diff --git a/arch/x86/crypto/sha256-ssse3-asm.S b/arch/x86/crypto/sha256-ssse3-asm.S index c6c05ed2c16a593390ddf7617070f45e58a40ad4..ab7d9f05ff789399ad1feee5f77c3ea601e201e9 100644 --- a/arch/x86/crypto/sha256-ssse3-asm.S +++ b/arch/x86/crypto/sha256-ssse3-asm.S @@ -347,8 +347,10 @@ a = TMP_ .endm ######################################################################## -## void sha256_transform_ssse3(void *input_data, UINT32 digest[8], UINT64 num_blks) -## arg 1 : pointer to digest +## void sha256_transform_ssse3(struct sha256_state *state, const u8 *data, +## int blocks); +## arg 1 : pointer to state +## (struct sha256_state is assumed to begin with u32 state[8]) ## arg 2 : pointer to input data ## arg 3 : Num blocks ######################################################################## diff --git a/arch/x86/crypto/sha256_ssse3_glue.c b/arch/x86/crypto/sha256_ssse3_glue.c index 773a873d2b28a31cdcaa3b1d085cb8ddf94664d6..2251b62f8f17d14113e9acf12b140b265de07436 100644 --- a/arch/x86/crypto/sha256_ssse3_glue.c +++ b/arch/x86/crypto/sha256_ssse3_glue.c @@ -40,12 +40,11 @@ #include #include -asmlinkage void sha256_transform_ssse3(u32 *digest, const char *data, - u64 rounds); -typedef void (sha256_transform_fn)(u32 *digest, const char *data, u64 rounds); +asmlinkage void sha256_transform_ssse3(struct sha256_state *state, + const u8 *data, int blocks); -static int sha256_update(struct shash_desc *desc, const u8 *data, - unsigned int len, sha256_transform_fn *sha256_xform) +static int _sha256_update(struct shash_desc *desc, const u8 *data, + unsigned int len, sha256_block_fn *sha256_xform) { struct sha256_state *sctx = shash_desc_ctx(desc); @@ -53,28 +52,29 @@ static int sha256_update(struct shash_desc *desc, const u8 *data, (sctx->count % SHA256_BLOCK_SIZE) + len < SHA256_BLOCK_SIZE) return crypto_sha256_update(desc, data, len); - /* make sure casting to sha256_block_fn() is safe */ + /* + * Make sure struct sha256_state begins directly with the SHA256 + * 256-bit internal state, as this is what the asm functions expect. + */ BUILD_BUG_ON(offsetof(struct sha256_state, state) != 0); kernel_fpu_begin(); - sha256_base_do_update(desc, data, len, - (sha256_block_fn *)sha256_xform); + sha256_base_do_update(desc, data, len, sha256_xform); kernel_fpu_end(); return 0; } static int sha256_finup(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out, sha256_transform_fn *sha256_xform) + unsigned int len, u8 *out, sha256_block_fn *sha256_xform) { if (!irq_fpu_usable()) return crypto_sha256_finup(desc, data, len, out); kernel_fpu_begin(); if (len) - sha256_base_do_update(desc, data, len, - (sha256_block_fn *)sha256_xform); - sha256_base_do_finalize(desc, (sha256_block_fn *)sha256_xform); + sha256_base_do_update(desc, data, len, sha256_xform); + sha256_base_do_finalize(desc, sha256_xform); kernel_fpu_end(); return sha256_base_finish(desc, out); @@ -83,7 +83,7 @@ static int sha256_finup(struct shash_desc *desc, const u8 *data, static int sha256_ssse3_update(struct shash_desc *desc, const u8 *data, unsigned int len) { - return sha256_update(desc, data, len, sha256_transform_ssse3); + return _sha256_update(desc, data, len, sha256_transform_ssse3); } static int sha256_ssse3_finup(struct shash_desc *desc, const u8 *data, @@ -144,13 +144,13 @@ static void unregister_sha256_ssse3(void) } #ifdef CONFIG_AS_AVX -asmlinkage void sha256_transform_avx(u32 *digest, const char *data, - u64 rounds); +asmlinkage void sha256_transform_avx(struct sha256_state *state, + const u8 *data, int blocks); static int sha256_avx_update(struct shash_desc *desc, const u8 *data, unsigned int len) { - return sha256_update(desc, data, len, sha256_transform_avx); + return _sha256_update(desc, data, len, sha256_transform_avx); } static int sha256_avx_finup(struct shash_desc *desc, const u8 *data, @@ -226,13 +226,13 @@ static inline void unregister_sha256_avx(void) { } #endif #if defined(CONFIG_AS_AVX2) && defined(CONFIG_AS_AVX) -asmlinkage void sha256_transform_rorx(u32 *digest, const char *data, - u64 rounds); +asmlinkage void sha256_transform_rorx(struct sha256_state *state, + const u8 *data, int blocks); static int sha256_avx2_update(struct shash_desc *desc, const u8 *data, unsigned int len) { - return sha256_update(desc, data, len, sha256_transform_rorx); + return _sha256_update(desc, data, len, sha256_transform_rorx); } static int sha256_avx2_finup(struct shash_desc *desc, const u8 *data, @@ -306,13 +306,13 @@ static inline void unregister_sha256_avx2(void) { } #endif #ifdef CONFIG_AS_SHA256_NI -asmlinkage void sha256_ni_transform(u32 *digest, const char *data, - u64 rounds); /*unsigned int rounds);*/ +asmlinkage void sha256_ni_transform(struct sha256_state *digest, + const u8 *data, int rounds); static int sha256_ni_update(struct shash_desc *desc, const u8 *data, unsigned int len) { - return sha256_update(desc, data, len, sha256_ni_transform); + return _sha256_update(desc, data, len, sha256_ni_transform); } static int sha256_ni_finup(struct shash_desc *desc, const u8 *data, diff --git a/arch/x86/crypto/sha512-avx-asm.S b/arch/x86/crypto/sha512-avx-asm.S index 39235fefe6f7c0c8e15c39c5828d9ff93e2609d6..8f6fe09cba54601f921811e2a09ca2405d74beaf 100644 --- a/arch/x86/crypto/sha512-avx-asm.S +++ b/arch/x86/crypto/sha512-avx-asm.S @@ -271,11 +271,12 @@ frame_size = frame_GPRSAVE + GPRSAVE_SIZE .endm ######################################################################## -# void sha512_transform_avx(void* D, const void* M, u64 L) -# Purpose: Updates the SHA512 digest stored at D with the message stored in M. -# The size of the message pointed to by M must be an integer multiple of SHA512 -# message blocks. -# L is the message length in SHA512 blocks +# void sha512_transform_avx(sha512_state *state, const u8 *data, int blocks) +# Purpose: Updates the SHA512 digest stored at "state" with the message +# stored in "data". +# The size of the message pointed to by "data" must be an integer multiple +# of SHA512 message blocks. +# "blocks" is the message length in SHA512 blocks ######################################################################## ENTRY(sha512_transform_avx) cmp $0, msglen diff --git a/arch/x86/crypto/sha512-avx2-asm.S b/arch/x86/crypto/sha512-avx2-asm.S index b16d560051629a1c6d518321b93be2672b861c9e..43d4d641804c73752b5c131282f755a167b2dce7 100644 --- a/arch/x86/crypto/sha512-avx2-asm.S +++ b/arch/x86/crypto/sha512-avx2-asm.S @@ -563,11 +563,12 @@ frame_size = frame_GPRSAVE + GPRSAVE_SIZE .endm ######################################################################## -# void sha512_transform_rorx(void* D, const void* M, uint64_t L)# -# Purpose: Updates the SHA512 digest stored at D with the message stored in M. -# The size of the message pointed to by M must be an integer multiple of SHA512 -# message blocks. -# L is the message length in SHA512 blocks +# void sha512_transform_rorx(sha512_state *state, const u8 *data, int blocks) +# Purpose: Updates the SHA512 digest stored at "state" with the message +# stored in "data". +# The size of the message pointed to by "data" must be an integer multiple +# of SHA512 message blocks. +# "blocks" is the message length in SHA512 blocks ######################################################################## ENTRY(sha512_transform_rorx) # Allocate Stack Space diff --git a/arch/x86/crypto/sha512-ssse3-asm.S b/arch/x86/crypto/sha512-ssse3-asm.S index 66bbd9058a90d67f51a44340e66ddfcd3801ec60..46da903f5538f4d9470154fde12338935328518c 100644 --- a/arch/x86/crypto/sha512-ssse3-asm.S +++ b/arch/x86/crypto/sha512-ssse3-asm.S @@ -269,11 +269,14 @@ frame_size = frame_GPRSAVE + GPRSAVE_SIZE .endm ######################################################################## -# void sha512_transform_ssse3(void* D, const void* M, u64 L)# -# Purpose: Updates the SHA512 digest stored at D with the message stored in M. -# The size of the message pointed to by M must be an integer multiple of SHA512 -# message blocks. -# L is the message length in SHA512 blocks. +## void sha512_transform_ssse3(struct sha512_state *state, const u8 *data, +## int blocks); +# (struct sha512_state is assumed to begin with u64 state[8]) +# Purpose: Updates the SHA512 digest stored at "state" with the message +# stored in "data". +# The size of the message pointed to by "data" must be an integer multiple +# of SHA512 message blocks. +# "blocks" is the message length in SHA512 blocks. ######################################################################## ENTRY(sha512_transform_ssse3) diff --git a/arch/x86/crypto/sha512_ssse3_glue.c b/arch/x86/crypto/sha512_ssse3_glue.c index f1b811b60ba6f852968274cb6ecbf8ea976406b4..3403816c509a0a757e6f69e969cab4634dcc33be 100644 --- a/arch/x86/crypto/sha512_ssse3_glue.c +++ b/arch/x86/crypto/sha512_ssse3_glue.c @@ -39,13 +39,11 @@ #include -asmlinkage void sha512_transform_ssse3(u64 *digest, const char *data, - u64 rounds); - -typedef void (sha512_transform_fn)(u64 *digest, const char *data, u64 rounds); +asmlinkage void sha512_transform_ssse3(struct sha512_state *state, + const u8 *data, int blocks); static int sha512_update(struct shash_desc *desc, const u8 *data, - unsigned int len, sha512_transform_fn *sha512_xform) + unsigned int len, sha512_block_fn *sha512_xform) { struct sha512_state *sctx = shash_desc_ctx(desc); @@ -53,28 +51,29 @@ static int sha512_update(struct shash_desc *desc, const u8 *data, (sctx->count[0] % SHA512_BLOCK_SIZE) + len < SHA512_BLOCK_SIZE) return crypto_sha512_update(desc, data, len); - /* make sure casting to sha512_block_fn() is safe */ + /* + * Make sure struct sha512_state begins directly with the SHA512 + * 512-bit internal state, as this is what the asm functions expect. + */ BUILD_BUG_ON(offsetof(struct sha512_state, state) != 0); kernel_fpu_begin(); - sha512_base_do_update(desc, data, len, - (sha512_block_fn *)sha512_xform); + sha512_base_do_update(desc, data, len, sha512_xform); kernel_fpu_end(); return 0; } static int sha512_finup(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out, sha512_transform_fn *sha512_xform) + unsigned int len, u8 *out, sha512_block_fn *sha512_xform) { if (!irq_fpu_usable()) return crypto_sha512_finup(desc, data, len, out); kernel_fpu_begin(); if (len) - sha512_base_do_update(desc, data, len, - (sha512_block_fn *)sha512_xform); - sha512_base_do_finalize(desc, (sha512_block_fn *)sha512_xform); + sha512_base_do_update(desc, data, len, sha512_xform); + sha512_base_do_finalize(desc, sha512_xform); kernel_fpu_end(); return sha512_base_finish(desc, out); @@ -144,8 +143,8 @@ static void unregister_sha512_ssse3(void) } #ifdef CONFIG_AS_AVX -asmlinkage void sha512_transform_avx(u64 *digest, const char *data, - u64 rounds); +asmlinkage void sha512_transform_avx(struct sha512_state *state, + const u8 *data, int blocks); static bool avx_usable(void) { if (!cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM, NULL)) { @@ -225,8 +224,8 @@ static inline void unregister_sha512_avx(void) { } #endif #if defined(CONFIG_AS_AVX2) && defined(CONFIG_AS_AVX) -asmlinkage void sha512_transform_rorx(u64 *digest, const char *data, - u64 rounds); +asmlinkage void sha512_transform_rorx(struct sha512_state *state, + const u8 *data, int blocks); static int sha512_avx2_update(struct shash_desc *desc, const u8 *data, unsigned int len) diff --git a/arch/x86/entry/calling.h b/arch/x86/entry/calling.h index 578b5455334f01cbd7daba2c8acc3196caac2461..31fbb4a7d9f6716997611c8d10a12c2b9264f150 100644 --- a/arch/x86/entry/calling.h +++ b/arch/x86/entry/calling.h @@ -172,21 +172,6 @@ For 32-bit we have the following conventions - kernel is built with .endif .endm -/* - * This is a sneaky trick to help the unwinder find pt_regs on the stack. The - * frame pointer is replaced with an encoded pointer to pt_regs. The encoding - * is just setting the LSB, which makes it an invalid stack address and is also - * a signal to the unwinder that it's a pt_regs pointer in disguise. - * - * NOTE: This macro must be used *after* PUSH_AND_CLEAR_REGS because it corrupts - * the original rbp. - */ -.macro ENCODE_FRAME_POINTER ptregs_offset=0 -#ifdef CONFIG_FRAME_POINTER - leaq 1+\ptregs_offset(%rsp), %rbp -#endif -.endm - #ifdef CONFIG_PAGE_TABLE_ISOLATION /* diff --git a/arch/x86/entry/entry_32.S b/arch/x86/entry/entry_32.S index 8059d4fd915c923423f0907923dd564489c86ebf..d07432062ee679c922203a475871a658cbc928c6 100644 --- a/arch/x86/entry/entry_32.S +++ b/arch/x86/entry/entry_32.S @@ -245,22 +245,6 @@ .Lend_\@: .endm -/* - * This is a sneaky trick to help the unwinder find pt_regs on the stack. The - * frame pointer is replaced with an encoded pointer to pt_regs. The encoding - * is just clearing the MSB, which makes it an invalid stack address and is also - * a signal to the unwinder that it's a pt_regs pointer in disguise. - * - * NOTE: This macro must be used *after* SAVE_ALL because it corrupts the - * original rbp. - */ -.macro ENCODE_FRAME_POINTER -#ifdef CONFIG_FRAME_POINTER - mov %esp, %ebp - andl $0x7fffffff, %ebp -#endif -.endm - .macro RESTORE_INT_REGS popl %ebx popl %ecx diff --git a/arch/x86/entry/vdso/vdso32-setup.c b/arch/x86/entry/vdso/vdso32-setup.c index 42d4c89f990ed1d752b24c641329671587313ba0..ddff0ca6f50981bfc53cfae86d81e9d2200a92a0 100644 --- a/arch/x86/entry/vdso/vdso32-setup.c +++ b/arch/x86/entry/vdso/vdso32-setup.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include diff --git a/arch/x86/events/amd/core.c b/arch/x86/events/amd/core.c index defb536aebce2144db706672e62d3e5ee82fdbe3..c3ec535fd36b84d4e92c1ccea8d6d3ce64073ce9 100644 --- a/arch/x86/events/amd/core.c +++ b/arch/x86/events/amd/core.c @@ -245,6 +245,7 @@ static const u64 amd_f17h_perfmon_event_map[PERF_COUNT_HW_MAX] = [PERF_COUNT_HW_CPU_CYCLES] = 0x0076, [PERF_COUNT_HW_INSTRUCTIONS] = 0x00c0, [PERF_COUNT_HW_CACHE_REFERENCES] = 0xff60, + [PERF_COUNT_HW_CACHE_MISSES] = 0x0964, [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x00c2, [PERF_COUNT_HW_BRANCH_MISSES] = 0x00c3, [PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = 0x0287, diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c index e91814d1a27f3455513000f68289d4c493983af9..79caeba8b6f087c3cd5efff8a22d7b3eaf62dff9 100644 --- a/arch/x86/events/intel/ds.c +++ b/arch/x86/events/intel/ds.c @@ -1402,6 +1402,8 @@ intel_pmu_save_and_restart_reload(struct perf_event *event, int count) old = ((s64)(prev_raw_count << shift) >> shift); local64_add(new - old + count * period, &event->count); + local64_set(&hwc->period_left, -new); + perf_event_update_userpage(event); return 0; diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h index 050368db9d35763d3afae17bf2a28cb2343f4548..3c1e51ead0722e958b75a69d279d0e3a7cc0eafe 100644 --- a/arch/x86/include/asm/apic.h +++ b/arch/x86/include/asm/apic.h @@ -448,6 +448,14 @@ static inline void ack_APIC_irq(void) apic_eoi(); } + +static inline bool lapic_vector_set_in_irr(unsigned int vector) +{ + u32 irr = apic_read(APIC_IRR + (vector / 32 * 0x10)); + + return !!(irr & (1U << (vector % 32))); +} + static inline unsigned default_get_apic_id(unsigned long x) { unsigned int ver = GET_APIC_VERSION(apic_read(APIC_LVR)); diff --git a/arch/x86/include/asm/frame.h b/arch/x86/include/asm/frame.h index 5cbce6fbb534bc0c4c6a8ca710de678ee9850d28..296b346184b27bba25836516f4f24ffa29694a5f 100644 --- a/arch/x86/include/asm/frame.h +++ b/arch/x86/include/asm/frame.h @@ -22,6 +22,35 @@ pop %_ASM_BP .endm +#ifdef CONFIG_X86_64 +/* + * This is a sneaky trick to help the unwinder find pt_regs on the stack. The + * frame pointer is replaced with an encoded pointer to pt_regs. The encoding + * is just setting the LSB, which makes it an invalid stack address and is also + * a signal to the unwinder that it's a pt_regs pointer in disguise. + * + * NOTE: This macro must be used *after* PUSH_AND_CLEAR_REGS because it corrupts + * the original rbp. + */ +.macro ENCODE_FRAME_POINTER ptregs_offset=0 + leaq 1+\ptregs_offset(%rsp), %rbp +.endm +#else /* !CONFIG_X86_64 */ +/* + * This is a sneaky trick to help the unwinder find pt_regs on the stack. The + * frame pointer is replaced with an encoded pointer to pt_regs. The encoding + * is just clearing the MSB, which makes it an invalid stack address and is also + * a signal to the unwinder that it's a pt_regs pointer in disguise. + * + * NOTE: This macro must be used *after* SAVE_ALL because it corrupts the + * original ebp. + */ +.macro ENCODE_FRAME_POINTER + mov %esp, %ebp + andl $0x7fffffff, %ebp +.endm +#endif /* CONFIG_X86_64 */ + #else /* !__ASSEMBLY__ */ #define FRAME_BEGIN \ @@ -30,12 +59,32 @@ #define FRAME_END "pop %" _ASM_BP "\n" +#ifdef CONFIG_X86_64 +#define ENCODE_FRAME_POINTER \ + "lea 1(%rsp), %rbp\n\t" +#else /* !CONFIG_X86_64 */ +#define ENCODE_FRAME_POINTER \ + "movl %esp, %ebp\n\t" \ + "andl $0x7fffffff, %ebp\n\t" +#endif /* CONFIG_X86_64 */ + #endif /* __ASSEMBLY__ */ #define FRAME_OFFSET __ASM_SEL(4, 8) #else /* !CONFIG_FRAME_POINTER */ +#ifdef __ASSEMBLY__ + +.macro ENCODE_FRAME_POINTER ptregs_offset=0 +.endm + +#else /* !__ASSEMBLY */ + +#define ENCODE_FRAME_POINTER + +#endif + #define FRAME_BEGIN #define FRAME_END #define FRAME_OFFSET 0 diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 155be8adb934e4e2f70db6c7c9e3aa84c4f7dde0..067288d4ef6e29d6f5e28cdeeaf925c8d2c1334e 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -350,12 +350,12 @@ struct kvm_mmu { void (*set_cr3)(struct kvm_vcpu *vcpu, unsigned long root); unsigned long (*get_cr3)(struct kvm_vcpu *vcpu); u64 (*get_pdptr)(struct kvm_vcpu *vcpu, int index); - int (*page_fault)(struct kvm_vcpu *vcpu, gva_t gva, u32 err, + int (*page_fault)(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, u32 err, bool prefault); void (*inject_page_fault)(struct kvm_vcpu *vcpu, struct x86_exception *fault); - gpa_t (*gva_to_gpa)(struct kvm_vcpu *vcpu, gva_t gva, u32 access, - struct x86_exception *exception); + gpa_t (*gva_to_gpa)(struct kvm_vcpu *vcpu, gpa_t gva_or_gpa, + u32 access, struct x86_exception *exception); gpa_t (*translate_gpa)(struct kvm_vcpu *vcpu, gpa_t gpa, u32 access, struct x86_exception *exception); int (*sync_page)(struct kvm_vcpu *vcpu, @@ -1040,7 +1040,7 @@ struct kvm_x86_ops { void (*load_eoi_exitmap)(struct kvm_vcpu *vcpu, u64 *eoi_exit_bitmap); void (*set_virtual_apic_mode)(struct kvm_vcpu *vcpu); void (*set_apic_access_page_addr)(struct kvm_vcpu *vcpu, hpa_t hpa); - void (*deliver_posted_interrupt)(struct kvm_vcpu *vcpu, int vector); + int (*deliver_posted_interrupt)(struct kvm_vcpu *vcpu, int vector); int (*sync_pir_to_irr)(struct kvm_vcpu *vcpu); int (*set_tss_addr)(struct kvm *kvm, unsigned int addr); int (*set_identity_map_addr)(struct kvm *kvm, u64 ident_addr); @@ -1354,7 +1354,7 @@ void kvm_vcpu_deactivate_apicv(struct kvm_vcpu *vcpu); int kvm_emulate_hypercall(struct kvm_vcpu *vcpu); -int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t gva, u64 error_code, +int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, u64 error_code, void *insn, int insn_len); void kvm_mmu_invlpg(struct kvm_vcpu *vcpu, gva_t gva); void kvm_mmu_invpcid_gva(struct kvm_vcpu *vcpu, gva_t gva, unsigned long pcid); diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index 0f4feee6d082eddca270ccf2f6a526c3b9fbf523..d2c25a13e1cef3ce165ed3ac9b6a1a08a11cd90f 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h @@ -455,6 +455,8 @@ #define MSR_K7_HWCR 0xc0010015 #define MSR_K7_HWCR_SMMLOCK_BIT 0 #define MSR_K7_HWCR_SMMLOCK BIT_ULL(MSR_K7_HWCR_SMMLOCK_BIT) +#define MSR_K7_HWCR_IRPERF_EN_BIT 30 +#define MSR_K7_HWCR_IRPERF_EN BIT_ULL(MSR_K7_HWCR_IRPERF_EN_BIT) #define MSR_K7_FID_VID_CTL 0xc0010041 #define MSR_K7_FID_VID_STATUS 0xc0010042 diff --git a/arch/x86/include/asm/nmi.h b/arch/x86/include/asm/nmi.h index 75ded1d13d98d6cde6f01ab8602587145e010707..9d5d949e662e14bcb90934f1ba312123a2b10bd1 100644 --- a/arch/x86/include/asm/nmi.h +++ b/arch/x86/include/asm/nmi.h @@ -41,7 +41,6 @@ struct nmiaction { struct list_head list; nmi_handler_t handler; u64 max_duration; - struct irq_work irq_work; unsigned long flags; const char *name; }; diff --git a/arch/x86/include/asm/pgtable_32.h b/arch/x86/include/asm/pgtable_32.h index b3ec519e39827e58eaeb8a567303e37a6bc2e919..71e1df86017652f6e4124dbf4325725b22696168 100644 --- a/arch/x86/include/asm/pgtable_32.h +++ b/arch/x86/include/asm/pgtable_32.h @@ -106,6 +106,6 @@ do { \ * with only a host target support using a 32-bit type for internal * representation. */ -#define LOWMEM_PAGES ((((2<<31) - __PAGE_OFFSET) >> PAGE_SHIFT)) +#define LOWMEM_PAGES ((((_ULL(2)<<31) - __PAGE_OFFSET) >> PAGE_SHIFT)) #endif /* _ASM_X86_PGTABLE_32_H */ diff --git a/arch/x86/include/asm/sections.h b/arch/x86/include/asm/sections.h index 4a911a382adedfdcd332b8b4884db6ff06e10533..dde9f925b4744212cf660c3410de2776981d4033 100644 --- a/arch/x86/include/asm/sections.h +++ b/arch/x86/include/asm/sections.h @@ -6,7 +6,7 @@ #include extern char __brk_base[], __brk_limit[]; -extern struct exception_table_entry __stop___ex_table[]; +extern char __cfi_jt_start[], __cfi_jt_end[]; extern char __end_rodata_aligned[]; #if defined(CONFIG_X86_64) diff --git a/arch/x86/kernel/apic/msi.c b/arch/x86/kernel/apic/msi.c index 72a94401f9e03487f37dea32f762afe524db0578..1f5df339e48ffbc053d80c1ad09cdfb4c4a9e8e6 100644 --- a/arch/x86/kernel/apic/msi.c +++ b/arch/x86/kernel/apic/msi.c @@ -26,10 +26,8 @@ static struct irq_domain *msi_default_domain; -static void irq_msi_compose_msg(struct irq_data *data, struct msi_msg *msg) +static void __irq_msi_compose_msg(struct irq_cfg *cfg, struct msi_msg *msg) { - struct irq_cfg *cfg = irqd_cfg(data); - msg->address_hi = MSI_ADDR_BASE_HI; if (x2apic_enabled()) @@ -50,6 +48,127 @@ static void irq_msi_compose_msg(struct irq_data *data, struct msi_msg *msg) MSI_DATA_VECTOR(cfg->vector); } +static void irq_msi_compose_msg(struct irq_data *data, struct msi_msg *msg) +{ + __irq_msi_compose_msg(irqd_cfg(data), msg); +} + +static void irq_msi_update_msg(struct irq_data *irqd, struct irq_cfg *cfg) +{ + struct msi_msg msg[2] = { [1] = { }, }; + + __irq_msi_compose_msg(cfg, msg); + irq_data_get_irq_chip(irqd)->irq_write_msi_msg(irqd, msg); +} + +static int +msi_set_affinity(struct irq_data *irqd, const struct cpumask *mask, bool force) +{ + struct irq_cfg old_cfg, *cfg = irqd_cfg(irqd); + struct irq_data *parent = irqd->parent_data; + unsigned int cpu; + int ret; + + /* Save the current configuration */ + cpu = cpumask_first(irq_data_get_effective_affinity_mask(irqd)); + old_cfg = *cfg; + + /* Allocate a new target vector */ + ret = parent->chip->irq_set_affinity(parent, mask, force); + if (ret < 0 || ret == IRQ_SET_MASK_OK_DONE) + return ret; + + /* + * For non-maskable and non-remapped MSI interrupts the migration + * to a different destination CPU and a different vector has to be + * done careful to handle the possible stray interrupt which can be + * caused by the non-atomic update of the address/data pair. + * + * Direct update is possible when: + * - The MSI is maskable (remapped MSI does not use this code path)). + * The quirk bit is not set in this case. + * - The new vector is the same as the old vector + * - The old vector is MANAGED_IRQ_SHUTDOWN_VECTOR (interrupt starts up) + * - The new destination CPU is the same as the old destination CPU + */ + if (!irqd_msi_nomask_quirk(irqd) || + cfg->vector == old_cfg.vector || + old_cfg.vector == MANAGED_IRQ_SHUTDOWN_VECTOR || + cfg->dest_apicid == old_cfg.dest_apicid) { + irq_msi_update_msg(irqd, cfg); + return ret; + } + + /* + * Paranoia: Validate that the interrupt target is the local + * CPU. + */ + if (WARN_ON_ONCE(cpu != smp_processor_id())) { + irq_msi_update_msg(irqd, cfg); + return ret; + } + + /* + * Redirect the interrupt to the new vector on the current CPU + * first. This might cause a spurious interrupt on this vector if + * the device raises an interrupt right between this update and the + * update to the final destination CPU. + * + * If the vector is in use then the installed device handler will + * denote it as spurious which is no harm as this is a rare event + * and interrupt handlers have to cope with spurious interrupts + * anyway. If the vector is unused, then it is marked so it won't + * trigger the 'No irq handler for vector' warning in do_IRQ(). + * + * This requires to hold vector lock to prevent concurrent updates to + * the affected vector. + */ + lock_vector_lock(); + + /* + * Mark the new target vector on the local CPU if it is currently + * unused. Reuse the VECTOR_RETRIGGERED state which is also used in + * the CPU hotplug path for a similar purpose. This cannot be + * undone here as the current CPU has interrupts disabled and + * cannot handle the interrupt before the whole set_affinity() + * section is done. In the CPU unplug case, the current CPU is + * about to vanish and will not handle any interrupts anymore. The + * vector is cleaned up when the CPU comes online again. + */ + if (IS_ERR_OR_NULL(this_cpu_read(vector_irq[cfg->vector]))) + this_cpu_write(vector_irq[cfg->vector], VECTOR_RETRIGGERED); + + /* Redirect it to the new vector on the local CPU temporarily */ + old_cfg.vector = cfg->vector; + irq_msi_update_msg(irqd, &old_cfg); + + /* Now transition it to the target CPU */ + irq_msi_update_msg(irqd, cfg); + + /* + * All interrupts after this point are now targeted at the new + * vector/CPU. + * + * Drop vector lock before testing whether the temporary assignment + * to the local CPU was hit by an interrupt raised in the device, + * because the retrigger function acquires vector lock again. + */ + unlock_vector_lock(); + + /* + * Check whether the transition raced with a device interrupt and + * is pending in the local APICs IRR. It is safe to do this outside + * of vector lock as the irq_desc::lock of this interrupt is still + * held and interrupts are disabled: The check is not accessing the + * underlying vector store. It's just checking the local APIC's + * IRR. + */ + if (lapic_vector_set_in_irr(cfg->vector)) + irq_data_get_irq_chip(irqd)->irq_retrigger(irqd); + + return ret; +} + /* * IRQ Chip for MSI PCI/PCI-X/PCI-Express Devices, * which implement the MSI or MSI-X Capability Structure. @@ -61,6 +180,7 @@ static struct irq_chip pci_msi_controller = { .irq_ack = irq_chip_ack_parent, .irq_retrigger = irq_chip_retrigger_hierarchy, .irq_compose_msi_msg = irq_msi_compose_msg, + .irq_set_affinity = msi_set_affinity, .flags = IRQCHIP_SKIP_SET_WAKE, }; @@ -149,6 +269,8 @@ void __init arch_init_msi_domain(struct irq_domain *parent) } if (!msi_default_domain) pr_warn("failed to initialize irqdomain for MSI/MSI-x.\n"); + else + msi_default_domain->flags |= IRQ_DOMAIN_MSI_NOMASK_QUIRK; } #ifdef CONFIG_IRQ_REMAP diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index f86f912ce215867cfb54a5c827c1947cc4bba4d3..120769955687a54250bca1981339fa3e70f0bffb 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -25,6 +25,7 @@ static const int amd_erratum_383[]; static const int amd_erratum_400[]; +static const int amd_erratum_1054[]; static bool cpu_has_amd_erratum(struct cpuinfo_x86 *cpu, const int *erratum); /* @@ -609,9 +610,9 @@ static void early_detect_mem_encrypt(struct cpuinfo_x86 *c) return; clear_all: - clear_cpu_cap(c, X86_FEATURE_SME); + setup_clear_cpu_cap(X86_FEATURE_SME); clear_sev: - clear_cpu_cap(c, X86_FEATURE_SEV); + setup_clear_cpu_cap(X86_FEATURE_SEV); } } @@ -983,6 +984,15 @@ static void init_amd(struct cpuinfo_x86 *c) /* AMD CPUs don't reset SS attributes on SYSRET, Xen does. */ if (!cpu_has(c, X86_FEATURE_XENPV)) set_cpu_bug(c, X86_BUG_SYSRET_SS_ATTRS); + + /* + * Turn on the Instructions Retired free counter on machines not + * susceptible to erratum #1054 "Instructions Retired Performance + * Counter May Be Inaccurate". + */ + if (cpu_has(c, X86_FEATURE_IRPERF) && + !cpu_has_amd_erratum(c, amd_erratum_1054)) + msr_set_bit(MSR_K7_HWCR, MSR_K7_HWCR_IRPERF_EN_BIT); } #ifdef CONFIG_X86_32 @@ -1110,6 +1120,10 @@ static const int amd_erratum_400[] = static const int amd_erratum_383[] = AMD_OSVW_ERRATUM(3, AMD_MODEL_RANGE(0x10, 0, 0, 0xff, 0xf)); +/* #1054: Instructions Retired Performance Counter May Be Inaccurate */ +static const int amd_erratum_1054[] = + AMD_OSVW_ERRATUM(0, AMD_MODEL_RANGE(0x17, 0, 0, 0x2f, 0xf)); + static bool cpu_has_amd_erratum(struct cpuinfo_x86 *cpu, const int *erratum) { diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index a6458ab499c213800168e7e7dced4edecebc7c17..7f43eba8d0c105b5736738285b7d23c97cf992a3 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -387,7 +387,7 @@ static __always_inline void setup_pku(struct cpuinfo_x86 *c) * cpuid bit to be set. We need to ensure that we * update that bit in this CPU's "cpu_info". */ - get_cpu_cap(c); + set_cpu_cap(c, X86_FEATURE_OSPKE); } #ifdef CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS diff --git a/arch/x86/kernel/cpu/intel_rdt.c b/arch/x86/kernel/cpu/intel_rdt.c index b99a04da70f618d1668d66390a9bb749e364c798..1c92cd08c3b4e55e241428edf16677f09306af66 100644 --- a/arch/x86/kernel/cpu/intel_rdt.c +++ b/arch/x86/kernel/cpu/intel_rdt.c @@ -595,7 +595,7 @@ static void domain_remove_cpu(int cpu, struct rdt_resource *r) if (static_branch_unlikely(&rdt_mon_enable_key)) rmdir_mondata_subdir_allrdtgrp(r, d->id); list_del(&d->list); - if (is_mbm_enabled()) + if (r->mon_capable && is_mbm_enabled()) cancel_delayed_work(&d->mbm_over); if (is_llc_occupancy_enabled() && has_busy_rmid(r, d)) { /* diff --git a/arch/x86/kernel/cpu/intel_rdt_rdtgroup.c b/arch/x86/kernel/cpu/intel_rdt_rdtgroup.c index a2d7e6646cce8034a632a78eb1095f53edcd42c3..11c5accfa4db5a5f47b229cfbd094516a9578783 100644 --- a/arch/x86/kernel/cpu/intel_rdt_rdtgroup.c +++ b/arch/x86/kernel/cpu/intel_rdt_rdtgroup.c @@ -1749,9 +1749,6 @@ static int set_cache_qos_cfg(int level, bool enable) struct rdt_domain *d; int cpu; - if (!zalloc_cpumask_var(&cpu_mask, GFP_KERNEL)) - return -ENOMEM; - if (level == RDT_RESOURCE_L3) update = l3_qos_cfg_update; else if (level == RDT_RESOURCE_L2) @@ -1759,6 +1756,9 @@ static int set_cache_qos_cfg(int level, bool enable) else return -EINVAL; + if (!zalloc_cpumask_var(&cpu_mask, GFP_KERNEL)) + return -ENOMEM; + r_l = &rdt_resources_all[level]; list_for_each_entry(d, &r_l->domains, list) { /* Pick one CPU from each domain instance to update MSR */ @@ -2005,7 +2005,7 @@ static struct dentry *rdt_mount(struct file_system_type *fs_type, if (rdt_mon_capable) { ret = mongroup_create_dir(rdtgroup_default.kn, - NULL, "mon_groups", + &rdtgroup_default, "mon_groups", &kn_mongrp); if (ret) { dentry = ERR_PTR(ret); @@ -2167,7 +2167,11 @@ static void free_all_child_rdtgrp(struct rdtgroup *rdtgrp) list_for_each_entry_safe(sentry, stmp, head, mon.crdtgrp_list) { free_rmid(sentry->mon.rmid); list_del(&sentry->mon.crdtgrp_list); - kfree(sentry); + + if (atomic_read(&sentry->waitcount) != 0) + sentry->flags = RDT_DELETED; + else + kfree(sentry); } } @@ -2205,7 +2209,11 @@ static void rmdir_all_sub(void) kernfs_remove(rdtgrp->kn); list_del(&rdtgrp->rdtgroup_list); - kfree(rdtgrp); + + if (atomic_read(&rdtgrp->waitcount) != 0) + rdtgrp->flags = RDT_DELETED; + else + kfree(rdtgrp); } /* Notify online CPUs to update per cpu storage and PQR_ASSOC MSR */ update_closid_rmid(cpu_online_mask, &rdtgroup_default); @@ -2407,7 +2415,7 @@ static int mkdir_mondata_all(struct kernfs_node *parent_kn, /* * Create the mon_data directory first. */ - ret = mongroup_create_dir(parent_kn, NULL, "mon_data", &kn); + ret = mongroup_create_dir(parent_kn, prgrp, "mon_data", &kn); if (ret) return ret; @@ -2560,7 +2568,7 @@ static int mkdir_rdt_prepare(struct kernfs_node *parent_kn, uint files = 0; int ret; - prdtgrp = rdtgroup_kn_lock_live(prgrp_kn); + prdtgrp = rdtgroup_kn_lock_live(parent_kn); rdt_last_cmd_clear(); if (!prdtgrp) { ret = -ENODEV; @@ -2635,7 +2643,7 @@ static int mkdir_rdt_prepare(struct kernfs_node *parent_kn, kernfs_activate(kn); /* - * The caller unlocks the prgrp_kn upon success. + * The caller unlocks the parent_kn upon success. */ return 0; @@ -2646,7 +2654,7 @@ static int mkdir_rdt_prepare(struct kernfs_node *parent_kn, out_free_rgrp: kfree(rdtgrp); out_unlock: - rdtgroup_kn_unlock(prgrp_kn); + rdtgroup_kn_unlock(parent_kn); return ret; } @@ -2684,7 +2692,7 @@ static int rdtgroup_mkdir_mon(struct kernfs_node *parent_kn, */ list_add_tail(&rdtgrp->mon.crdtgrp_list, &prgrp->mon.crdtgrp_list); - rdtgroup_kn_unlock(prgrp_kn); + rdtgroup_kn_unlock(parent_kn); return ret; } @@ -2727,7 +2735,7 @@ static int rdtgroup_mkdir_ctrl_mon(struct kernfs_node *parent_kn, * Create an empty mon_groups directory to hold the subset * of tasks and cpus to monitor. */ - ret = mongroup_create_dir(kn, NULL, "mon_groups", NULL); + ret = mongroup_create_dir(kn, rdtgrp, "mon_groups", NULL); if (ret) { rdt_last_cmd_puts("kernfs subdir error\n"); goto out_del_list; @@ -2743,7 +2751,7 @@ static int rdtgroup_mkdir_ctrl_mon(struct kernfs_node *parent_kn, out_common_fail: mkdir_rdt_prepare_clean(rdtgrp); out_unlock: - rdtgroup_kn_unlock(prgrp_kn); + rdtgroup_kn_unlock(parent_kn); return ret; } @@ -2869,13 +2877,13 @@ static int rdtgroup_rmdir_ctrl(struct kernfs_node *kn, struct rdtgroup *rdtgrp, closid_free(rdtgrp->closid); free_rmid(rdtgrp->mon.rmid); + rdtgroup_ctrl_remove(kn, rdtgrp); + /* * Free all the child monitor group rmids. */ free_all_child_rdtgrp(rdtgrp); - rdtgroup_ctrl_remove(kn, rdtgrp); - return 0; } diff --git a/arch/x86/kernel/cpu/mcheck/mce_amd.c b/arch/x86/kernel/cpu/mcheck/mce_amd.c index da0b6967349aa57debd13fe12fff18b4434e5eb5..f878d24ff3c11e42d4b47a961b8e579e8294de4d 100644 --- a/arch/x86/kernel/cpu/mcheck/mce_amd.c +++ b/arch/x86/kernel/cpu/mcheck/mce_amd.c @@ -1117,9 +1117,12 @@ static const struct sysfs_ops threshold_ops = { .store = store, }; +static void threshold_block_release(struct kobject *kobj); + static struct kobj_type threshold_ktype = { .sysfs_ops = &threshold_ops, .default_attrs = default_attrs, + .release = threshold_block_release, }; static const char *get_name(unsigned int bank, struct threshold_block *b) @@ -1152,8 +1155,9 @@ static const char *get_name(unsigned int bank, struct threshold_block *b) return buf_mcatype; } -static int allocate_threshold_blocks(unsigned int cpu, unsigned int bank, - unsigned int block, u32 address) +static int allocate_threshold_blocks(unsigned int cpu, struct threshold_bank *tb, + unsigned int bank, unsigned int block, + u32 address) { struct threshold_block *b = NULL; u32 low, high; @@ -1197,16 +1201,12 @@ static int allocate_threshold_blocks(unsigned int cpu, unsigned int bank, INIT_LIST_HEAD(&b->miscj); - if (per_cpu(threshold_banks, cpu)[bank]->blocks) { - list_add(&b->miscj, - &per_cpu(threshold_banks, cpu)[bank]->blocks->miscj); - } else { - per_cpu(threshold_banks, cpu)[bank]->blocks = b; - } + if (tb->blocks) + list_add(&b->miscj, &tb->blocks->miscj); + else + tb->blocks = b; - err = kobject_init_and_add(&b->kobj, &threshold_ktype, - per_cpu(threshold_banks, cpu)[bank]->kobj, - get_name(bank, b)); + err = kobject_init_and_add(&b->kobj, &threshold_ktype, tb->kobj, get_name(bank, b)); if (err) goto out_free; recurse: @@ -1214,7 +1214,7 @@ static int allocate_threshold_blocks(unsigned int cpu, unsigned int bank, if (!address) return 0; - err = allocate_threshold_blocks(cpu, bank, block, address); + err = allocate_threshold_blocks(cpu, tb, bank, block, address); if (err) goto out_free; @@ -1299,8 +1299,6 @@ static int threshold_create_bank(unsigned int cpu, unsigned int bank) goto out_free; } - per_cpu(threshold_banks, cpu)[bank] = b; - if (is_shared_bank(bank)) { refcount_set(&b->cpus, 1); @@ -1311,9 +1309,13 @@ static int threshold_create_bank(unsigned int cpu, unsigned int bank) } } - err = allocate_threshold_blocks(cpu, bank, 0, msr_ops.misc(bank)); - if (!err) - goto out; + err = allocate_threshold_blocks(cpu, b, bank, 0, msr_ops.misc(bank)); + if (err) + goto out_free; + + per_cpu(threshold_banks, cpu)[bank] = b; + + return 0; out_free: kfree(b); @@ -1322,8 +1324,12 @@ static int threshold_create_bank(unsigned int cpu, unsigned int bank) return err; } -static void deallocate_threshold_block(unsigned int cpu, - unsigned int bank) +static void threshold_block_release(struct kobject *kobj) +{ + kfree(to_block(kobj)); +} + +static void deallocate_threshold_block(unsigned int cpu, unsigned int bank) { struct threshold_block *pos = NULL; struct threshold_block *tmp = NULL; @@ -1333,13 +1339,11 @@ static void deallocate_threshold_block(unsigned int cpu, return; list_for_each_entry_safe(pos, tmp, &head->blocks->miscj, miscj) { - kobject_put(&pos->kobj); list_del(&pos->miscj); - kfree(pos); + kobject_put(&pos->kobj); } - kfree(per_cpu(threshold_banks, cpu)[bank]->blocks); - per_cpu(threshold_banks, cpu)[bank]->blocks = NULL; + kobject_put(&head->blocks->kobj); } static void __threshold_remove_blocks(struct threshold_bank *b) diff --git a/arch/x86/kernel/cpu/tsx.c b/arch/x86/kernel/cpu/tsx.c index 3e20d322bc98b636cde2747394d26fcec8783a0c..032509adf9de9f664e808083955de464a35f9d34 100644 --- a/arch/x86/kernel/cpu/tsx.c +++ b/arch/x86/kernel/cpu/tsx.c @@ -115,11 +115,12 @@ void __init tsx_init(void) tsx_disable(); /* - * tsx_disable() will change the state of the - * RTM CPUID bit. Clear it here since it is now - * expected to be not set. + * tsx_disable() will change the state of the RTM and HLE CPUID + * bits. Clear them here since they are now expected to be not + * set. */ setup_clear_cpu_cap(X86_FEATURE_RTM); + setup_clear_cpu_cap(X86_FEATURE_HLE); } else if (tsx_ctrl_state == TSX_CTRL_ENABLE) { /* @@ -131,10 +132,10 @@ void __init tsx_init(void) tsx_enable(); /* - * tsx_enable() will change the state of the - * RTM CPUID bit. Force it here since it is now - * expected to be set. + * tsx_enable() will change the state of the RTM and HLE CPUID + * bits. Force them here since they are now expected to be set. */ setup_force_cpu_cap(X86_FEATURE_RTM); + setup_force_cpu_cap(X86_FEATURE_HLE); } } diff --git a/arch/x86/kernel/ftrace_32.S b/arch/x86/kernel/ftrace_32.S index 4c8440de33559942a98245cf123a5ed10a30e406..83f18e829ac794b909b6cd3cc8c01e96cab69946 100644 --- a/arch/x86/kernel/ftrace_32.S +++ b/arch/x86/kernel/ftrace_32.S @@ -9,6 +9,7 @@ #include #include #include +#include #ifdef CC_USING_FENTRY # define function_hook __fentry__ @@ -131,6 +132,8 @@ ENTRY(ftrace_regs_caller) pushl %ecx pushl %ebx + ENCODE_FRAME_POINTER + movl 12*4(%esp), %eax /* Load ip (1st parameter) */ subl $MCOUNT_INSN_SIZE, %eax /* Adjust ip */ #ifdef CC_USING_FENTRY diff --git a/arch/x86/kernel/ftrace_64.S b/arch/x86/kernel/ftrace_64.S index 75f2b36b41a6956814a82be59c2665798d23fbb4..24b9abf718e8101c6f6b16c9b3f5fc97e65f4163 100644 --- a/arch/x86/kernel/ftrace_64.S +++ b/arch/x86/kernel/ftrace_64.S @@ -9,6 +9,7 @@ #include #include #include +#include .code64 .section .entry.text, "ax" @@ -222,6 +223,8 @@ GLOBAL(ftrace_regs_caller_op_ptr) leaq MCOUNT_REG_SIZE+8*2(%rsp), %rcx movq %rcx, RSP(%rsp) + ENCODE_FRAME_POINTER + /* regs go into 4th parameter */ leaq (%rsp), %rcx diff --git a/arch/x86/kernel/kgdb.c b/arch/x86/kernel/kgdb.c index 8e36f249646e25d20bc2bcc04b7a0ccc292e498c..904e18bb38c52d50b9e3913a417984652614b325 100644 --- a/arch/x86/kernel/kgdb.c +++ b/arch/x86/kernel/kgdb.c @@ -438,7 +438,7 @@ static void kgdb_disable_hw_debug(struct pt_regs *regs) */ void kgdb_roundup_cpus(unsigned long flags) { - apic->send_IPI_allbutself(APIC_DM_NMI); + apic->send_IPI_allbutself(NMI_VECTOR); } #endif diff --git a/arch/x86/kernel/module.c b/arch/x86/kernel/module.c index 6645f123419c62f65125851b414e8a6ed9e7cf40..6bf351f1f1806e85528f1bb34555a1019de4e503 100644 --- a/arch/x86/kernel/module.c +++ b/arch/x86/kernel/module.c @@ -201,6 +201,10 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, goto overflow; #endif break; + case R_X86_64_8: + if (!strncmp(strtab + sym->st_name, "__typeid__", 10)) + break; + /* fallthrough */ default: pr_err("%s: Unknown rela relocation: %llu\n", me->name, ELF64_R_TYPE(rel[i].r_info)); diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c index 086cf1d1d71d820e13baca26091d6104abea9eb9..0f8b9b900b0e7e0a86eca925f2bf2aec58171677 100644 --- a/arch/x86/kernel/nmi.c +++ b/arch/x86/kernel/nmi.c @@ -102,18 +102,22 @@ static int __init nmi_warning_debugfs(void) } fs_initcall(nmi_warning_debugfs); -static void nmi_max_handler(struct irq_work *w) +static void nmi_check_duration(struct nmiaction *action, u64 duration) { - struct nmiaction *a = container_of(w, struct nmiaction, irq_work); + u64 whole_msecs = READ_ONCE(action->max_duration); int remainder_ns, decimal_msecs; - u64 whole_msecs = READ_ONCE(a->max_duration); + + if (duration < nmi_longest_ns || duration < action->max_duration) + return; + + action->max_duration = duration; remainder_ns = do_div(whole_msecs, (1000 * 1000)); decimal_msecs = remainder_ns / 1000; printk_ratelimited(KERN_INFO "INFO: NMI handler (%ps) took too long to run: %lld.%03d msecs\n", - a->handler, whole_msecs, decimal_msecs); + action->handler, whole_msecs, decimal_msecs); } static int nmi_handle(unsigned int type, struct pt_regs *regs) @@ -140,11 +144,7 @@ static int nmi_handle(unsigned int type, struct pt_regs *regs) delta = sched_clock() - delta; trace_nmi_handler(a->handler, (int)delta, thishandled); - if (delta < nmi_longest_ns || delta < a->max_duration) - continue; - - a->max_duration = delta; - irq_work_queue(&a->irq_work); + nmi_check_duration(a, delta); } rcu_read_unlock(); @@ -162,8 +162,6 @@ int __register_nmi_handler(unsigned int type, struct nmiaction *action) if (!action->handler) return -EINVAL; - init_irq_work(&action->irq_work, nmi_max_handler); - raw_spin_lock_irqsave(&desc->lock, flags); /* diff --git a/arch/x86/kernel/sysfb_simplefb.c b/arch/x86/kernel/sysfb_simplefb.c index 85195d447a922785857db0caacc73d6bc9b9490c..f3215346e47fd8d822191d9ff879896e1f5e7a4f 100644 --- a/arch/x86/kernel/sysfb_simplefb.c +++ b/arch/x86/kernel/sysfb_simplefb.c @@ -94,11 +94,11 @@ __init int create_simplefb(const struct screen_info *si, if (si->orig_video_isVGA == VIDEO_TYPE_VLFB) size <<= 16; length = mode->height * mode->stride; - length = PAGE_ALIGN(length); if (length > size) { printk(KERN_WARNING "sysfb: VRAM smaller than advertised\n"); return -EINVAL; } + length = PAGE_ALIGN(length); /* setup IORESOURCE_MEM as framebuffer memory */ memset(&res, 0, sizeof(res)); diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S index 85e6d5620188e57544d008afd40f76e04960bd20..8eb0c6658de3406c76af0d579cff5af25e1cbfe7 100644 --- a/arch/x86/kernel/vmlinux.lds.S +++ b/arch/x86/kernel/vmlinux.lds.S @@ -152,16 +152,22 @@ SECTIONS __indirect_thunk_end = .; #endif - /* End of text section */ - _etext = .; +#ifdef CONFIG_CFI_CLANG + . = ALIGN(PAGE_SIZE); + __cfi_jt_start = .; + *(.text..L.cfi.jumptable .text..L.cfi.jumptable.*) + __cfi_jt_end = .; +#endif } :text = 0x9090 NOTES :text :note EXCEPTION_TABLE(16) :text = 0x9090 - /* .text should occupy whole number of pages */ + /* End of text section, which should occupy whole number of pages */ + _etext = .; . = ALIGN(PAGE_SIZE); + X86_ALIGN_RODATA_BEGIN RO_DATA(PAGE_SIZE) X86_ALIGN_RODATA_END @@ -435,3 +441,7 @@ INIT_PER_CPU(irq_stack_union); "kexec control code size is too big"); #endif +#ifdef CONFIG_CFI_CLANG +. = ASSERT((__cfi_jt_end - __cfi_jt_start > 0), + "CFI jump table is empty"); +#endif diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index e699f4d2a450dbcd1a47eb61e69ef676e4a4c993..c91431bc476e1aad2ccab46e865083a4054c5350 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -5164,16 +5164,28 @@ int x86_decode_insn(struct x86_emulate_ctxt *ctxt, void *insn, int insn_len) ctxt->ad_bytes = def_ad_bytes ^ 6; break; case 0x26: /* ES override */ + has_seg_override = true; + ctxt->seg_override = VCPU_SREG_ES; + break; case 0x2e: /* CS override */ + has_seg_override = true; + ctxt->seg_override = VCPU_SREG_CS; + break; case 0x36: /* SS override */ + has_seg_override = true; + ctxt->seg_override = VCPU_SREG_SS; + break; case 0x3e: /* DS override */ has_seg_override = true; - ctxt->seg_override = (ctxt->b >> 3) & 3; + ctxt->seg_override = VCPU_SREG_DS; break; case 0x64: /* FS override */ + has_seg_override = true; + ctxt->seg_override = VCPU_SREG_FS; + break; case 0x65: /* GS override */ has_seg_override = true; - ctxt->seg_override = ctxt->b & 7; + ctxt->seg_override = VCPU_SREG_GS; break; case 0x40 ... 0x4f: /* REX */ if (mode != X86EMUL_MODE_PROT64) @@ -5257,10 +5269,15 @@ int x86_decode_insn(struct x86_emulate_ctxt *ctxt, void *insn, int insn_len) } break; case Escape: - if (ctxt->modrm > 0xbf) - opcode = opcode.u.esc->high[ctxt->modrm - 0xc0]; - else + if (ctxt->modrm > 0xbf) { + size_t size = ARRAY_SIZE(opcode.u.esc->high); + u32 index = array_index_nospec( + ctxt->modrm - 0xc0, size); + + opcode = opcode.u.esc->high[index]; + } else { opcode = opcode.u.esc->op[(ctxt->modrm >> 3) & 7]; + } break; case InstrDual: if ((ctxt->modrm >> 6) == 3) diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c index 5842c5f587fe910b9358eda88cb619bd958413c4..3fd6c4b2c2b7bf8d66bf8a6fd708b2398d5a797c 100644 --- a/arch/x86/kvm/hyperv.c +++ b/arch/x86/kvm/hyperv.c @@ -792,11 +792,12 @@ static int kvm_hv_msr_get_crash_data(struct kvm_vcpu *vcpu, u32 index, u64 *pdata) { struct kvm_hv *hv = &vcpu->kvm->arch.hyperv; + size_t size = ARRAY_SIZE(hv->hv_crash_param); - if (WARN_ON_ONCE(index >= ARRAY_SIZE(hv->hv_crash_param))) + if (WARN_ON_ONCE(index >= size)) return -EINVAL; - *pdata = hv->hv_crash_param[index]; + *pdata = hv->hv_crash_param[array_index_nospec(index, size)]; return 0; } @@ -835,11 +836,12 @@ static int kvm_hv_msr_set_crash_data(struct kvm_vcpu *vcpu, u32 index, u64 data) { struct kvm_hv *hv = &vcpu->kvm->arch.hyperv; + size_t size = ARRAY_SIZE(hv->hv_crash_param); - if (WARN_ON_ONCE(index >= ARRAY_SIZE(hv->hv_crash_param))) + if (WARN_ON_ONCE(index >= size)) return -EINVAL; - hv->hv_crash_param[index] = data; + hv->hv_crash_param[array_index_nospec(index, size)] = data; return 0; } diff --git a/arch/x86/kvm/i8259.c b/arch/x86/kvm/i8259.c index bdcd4139eca9233bbd9e82615a1ed3c45c2ad060..38a36a1cc87fe14746a40055b1a5c998abd315a4 100644 --- a/arch/x86/kvm/i8259.c +++ b/arch/x86/kvm/i8259.c @@ -460,10 +460,14 @@ static int picdev_write(struct kvm_pic *s, switch (addr) { case 0x20: case 0x21: + pic_lock(s); + pic_ioport_write(&s->pics[0], addr, data); + pic_unlock(s); + break; case 0xa0: case 0xa1: pic_lock(s); - pic_ioport_write(&s->pics[addr >> 7], addr, data); + pic_ioport_write(&s->pics[1], addr, data); pic_unlock(s); break; case 0x4d0: diff --git a/arch/x86/kvm/ioapic.c b/arch/x86/kvm/ioapic.c index 4e822ad363f37f613d14ab94f35609bcf3539bf7..bac2ec9b4443bf1933a66d234b0189b43e2ba344 100644 --- a/arch/x86/kvm/ioapic.c +++ b/arch/x86/kvm/ioapic.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -73,13 +74,14 @@ static unsigned long ioapic_read_indirect(struct kvm_ioapic *ioapic, default: { u32 redir_index = (ioapic->ioregsel - 0x10) >> 1; - u64 redir_content; + u64 redir_content = ~0ULL; - if (redir_index < IOAPIC_NUM_PINS) - redir_content = - ioapic->redirtbl[redir_index].bits; - else - redir_content = ~0ULL; + if (redir_index < IOAPIC_NUM_PINS) { + u32 index = array_index_nospec( + redir_index, IOAPIC_NUM_PINS); + + redir_content = ioapic->redirtbl[index].bits; + } result = (ioapic->ioregsel & 0x1) ? (redir_content >> 32) & 0xffffffff : @@ -297,6 +299,7 @@ static void ioapic_write_indirect(struct kvm_ioapic *ioapic, u32 val) ioapic_debug("change redir index %x val %x\n", index, val); if (index >= IOAPIC_NUM_PINS) return; + index = array_index_nospec(index, IOAPIC_NUM_PINS); e = &ioapic->redirtbl[index]; mask_before = e->fields.mask; /* Preserve read-only fields */ diff --git a/arch/x86/kvm/irq_comm.c b/arch/x86/kvm/irq_comm.c index 3cc3b2d130a0c277e6b4cb6f59aa2a391d2253f6..4d000aea05e09bd0d511796f757f4a485194d05e 100644 --- a/arch/x86/kvm/irq_comm.c +++ b/arch/x86/kvm/irq_comm.c @@ -427,7 +427,7 @@ void kvm_scan_ioapic_routes(struct kvm_vcpu *vcpu, kvm_set_msi_irq(vcpu->kvm, entry, &irq); - if (irq.level && kvm_apic_match_dest(vcpu, NULL, 0, + if (irq.trig_mode && kvm_apic_match_dest(vcpu, NULL, 0, irq.dest_id, irq.dest_mode)) __set_bit(irq.vector, ioapic_handled_vectors); } diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 262e49301cae61caf76b7e875769f20301a71c83..8c6392534d145fd1e70ca221547854e2615e6ee7 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -633,9 +633,11 @@ static inline bool pv_eoi_enabled(struct kvm_vcpu *vcpu) static bool pv_eoi_get_pending(struct kvm_vcpu *vcpu) { u8 val; - if (pv_eoi_get_user(vcpu, &val) < 0) + if (pv_eoi_get_user(vcpu, &val) < 0) { apic_debug("Can't read EOI MSR value: 0x%llx\n", (unsigned long long)vcpu->arch.pv_eoi.msr_val); + return false; + } return val & 0x1; } @@ -1060,11 +1062,8 @@ static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode, apic_clear_vector(vector, apic->regs + APIC_TMR); } - if (vcpu->arch.apicv_active) - kvm_x86_ops->deliver_posted_interrupt(vcpu, vector); - else { + if (kvm_x86_ops->deliver_posted_interrupt(vcpu, vector)) { kvm_lapic_set_irr(vector, apic); - kvm_make_request(KVM_REQ_EVENT, vcpu); kvm_vcpu_kick(vcpu); } @@ -1862,15 +1861,20 @@ int kvm_lapic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val) case APIC_LVTTHMR: case APIC_LVTPC: case APIC_LVT1: - case APIC_LVTERR: + case APIC_LVTERR: { /* TODO: Check vector */ + size_t size; + u32 index; + if (!kvm_apic_sw_enabled(apic)) val |= APIC_LVT_MASKED; - - val &= apic_lvt_mask[(reg - APIC_LVTT) >> 4]; + size = ARRAY_SIZE(apic_lvt_mask); + index = array_index_nospec( + (reg - APIC_LVTT) >> 4, size); + val &= apic_lvt_mask[index]; kvm_lapic_set_reg(apic, reg, val); - break; + } case APIC_LVTT: if (!kvm_apic_sw_enabled(apic)) diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index eddf91a0e363eab11933ed8e2b3bd01e956be982..62f1e4663bc3ca568e6ea66e250843d632b14916 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -1184,12 +1184,12 @@ static bool mmu_gfn_lpage_is_disallowed(struct kvm_vcpu *vcpu, gfn_t gfn, return __mmu_gfn_lpage_is_disallowed(gfn, level, slot); } -static int host_mapping_level(struct kvm *kvm, gfn_t gfn) +static int host_mapping_level(struct kvm_vcpu *vcpu, gfn_t gfn) { unsigned long page_size; int i, ret = 0; - page_size = kvm_host_page_size(kvm, gfn); + page_size = kvm_host_page_size(vcpu, gfn); for (i = PT_PAGE_TABLE_LEVEL; i <= PT_MAX_HUGEPAGE_LEVEL; ++i) { if (page_size >= KVM_HPAGE_SIZE(i)) @@ -1239,7 +1239,7 @@ static int mapping_level(struct kvm_vcpu *vcpu, gfn_t large_gfn, if (unlikely(*force_pt_level)) return PT_PAGE_TABLE_LEVEL; - host_level = host_mapping_level(vcpu->kvm, large_gfn); + host_level = host_mapping_level(vcpu, large_gfn); if (host_level == PT_PAGE_TABLE_LEVEL) return host_level; @@ -3390,7 +3390,7 @@ static bool is_access_allowed(u32 fault_err_code, u64 spte) * - true: let the vcpu to access on the same address again. * - false: let the real page fault path to fix it. */ -static bool fast_page_fault(struct kvm_vcpu *vcpu, gva_t gva, int level, +static bool fast_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, int level, u32 error_code) { struct kvm_shadow_walk_iterator iterator; @@ -3410,7 +3410,7 @@ static bool fast_page_fault(struct kvm_vcpu *vcpu, gva_t gva, int level, do { u64 new_spte; - for_each_shadow_entry_lockless(vcpu, gva, iterator, spte) + for_each_shadow_entry_lockless(vcpu, cr2_or_gpa, iterator, spte) if (!is_shadow_present_pte(spte) || iterator.level < level) break; @@ -3488,7 +3488,7 @@ static bool fast_page_fault(struct kvm_vcpu *vcpu, gva_t gva, int level, } while (true); - trace_fast_page_fault(vcpu, gva, error_code, iterator.sptep, + trace_fast_page_fault(vcpu, cr2_or_gpa, error_code, iterator.sptep, spte, fault_handled); walk_shadow_page_lockless_end(vcpu); @@ -3496,10 +3496,11 @@ static bool fast_page_fault(struct kvm_vcpu *vcpu, gva_t gva, int level, } static bool try_async_pf(struct kvm_vcpu *vcpu, bool prefault, gfn_t gfn, - gva_t gva, kvm_pfn_t *pfn, bool write, bool *writable); + gpa_t cr2_or_gpa, kvm_pfn_t *pfn, bool write, + bool *writable); static int make_mmu_pages_available(struct kvm_vcpu *vcpu); -static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, u32 error_code, +static int nonpaging_map(struct kvm_vcpu *vcpu, gpa_t gpa, u32 error_code, gfn_t gfn, bool prefault) { int r; @@ -3525,16 +3526,16 @@ static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, u32 error_code, gfn &= ~(KVM_PAGES_PER_HPAGE(level) - 1); } - if (fast_page_fault(vcpu, v, level, error_code)) + if (fast_page_fault(vcpu, gpa, level, error_code)) return RET_PF_RETRY; mmu_seq = vcpu->kvm->mmu_notifier_seq; smp_rmb(); - if (try_async_pf(vcpu, prefault, gfn, v, &pfn, write, &map_writable)) + if (try_async_pf(vcpu, prefault, gfn, gpa, &pfn, write, &map_writable)) return RET_PF_RETRY; - if (handle_abnormal_pfn(vcpu, v, gfn, pfn, ACC_ALL, &r)) + if (handle_abnormal_pfn(vcpu, gpa, gfn, pfn, ACC_ALL, &r)) return r; r = RET_PF_RETRY; @@ -3545,7 +3546,7 @@ static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, u32 error_code, goto out_unlock; if (likely(!force_pt_level)) transparent_hugepage_adjust(vcpu, gfn, &pfn, &level); - r = __direct_map(vcpu, v, write, map_writable, level, pfn, + r = __direct_map(vcpu, gpa, write, map_writable, level, pfn, prefault, false); out_unlock: spin_unlock(&vcpu->kvm->mmu_lock); @@ -3838,7 +3839,7 @@ void kvm_mmu_sync_roots(struct kvm_vcpu *vcpu) } EXPORT_SYMBOL_GPL(kvm_mmu_sync_roots); -static gpa_t nonpaging_gva_to_gpa(struct kvm_vcpu *vcpu, gva_t vaddr, +static gpa_t nonpaging_gva_to_gpa(struct kvm_vcpu *vcpu, gpa_t vaddr, u32 access, struct x86_exception *exception) { if (exception) @@ -3846,7 +3847,7 @@ static gpa_t nonpaging_gva_to_gpa(struct kvm_vcpu *vcpu, gva_t vaddr, return vaddr; } -static gpa_t nonpaging_gva_to_gpa_nested(struct kvm_vcpu *vcpu, gva_t vaddr, +static gpa_t nonpaging_gva_to_gpa_nested(struct kvm_vcpu *vcpu, gpa_t vaddr, u32 access, struct x86_exception *exception) { @@ -4006,13 +4007,14 @@ static void shadow_page_table_clear_flood(struct kvm_vcpu *vcpu, gva_t addr) walk_shadow_page_lockless_end(vcpu); } -static int nonpaging_page_fault(struct kvm_vcpu *vcpu, gva_t gva, +static int nonpaging_page_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u32 error_code, bool prefault) { - gfn_t gfn = gva >> PAGE_SHIFT; + gfn_t gfn = gpa >> PAGE_SHIFT; int r; - pgprintk("%s: gva %lx error %x\n", __func__, gva, error_code); + /* Note, paging is disabled, ergo gva == gpa. */ + pgprintk("%s: gva %lx error %x\n", __func__, gpa, error_code); if (page_fault_handle_page_track(vcpu, error_code, gfn)) return RET_PF_EMULATE; @@ -4024,11 +4026,12 @@ static int nonpaging_page_fault(struct kvm_vcpu *vcpu, gva_t gva, MMU_WARN_ON(!VALID_PAGE(vcpu->arch.mmu.root_hpa)); - return nonpaging_map(vcpu, gva & PAGE_MASK, + return nonpaging_map(vcpu, gpa & PAGE_MASK, error_code, gfn, prefault); } -static int kvm_arch_setup_async_pf(struct kvm_vcpu *vcpu, gva_t gva, gfn_t gfn) +static int kvm_arch_setup_async_pf(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, + gfn_t gfn) { struct kvm_arch_async_pf arch; @@ -4037,7 +4040,8 @@ static int kvm_arch_setup_async_pf(struct kvm_vcpu *vcpu, gva_t gva, gfn_t gfn) arch.direct_map = vcpu->arch.mmu.direct_map; arch.cr3 = vcpu->arch.mmu.get_cr3(vcpu); - return kvm_setup_async_pf(vcpu, gva, kvm_vcpu_gfn_to_hva(vcpu, gfn), &arch); + return kvm_setup_async_pf(vcpu, cr2_or_gpa, + kvm_vcpu_gfn_to_hva(vcpu, gfn), &arch); } bool kvm_can_do_async_pf(struct kvm_vcpu *vcpu) @@ -4054,7 +4058,8 @@ bool kvm_can_do_async_pf(struct kvm_vcpu *vcpu) } static bool try_async_pf(struct kvm_vcpu *vcpu, bool prefault, gfn_t gfn, - gva_t gva, kvm_pfn_t *pfn, bool write, bool *writable) + gpa_t cr2_or_gpa, kvm_pfn_t *pfn, bool write, + bool *writable) { struct kvm_memory_slot *slot; bool async; @@ -4074,12 +4079,12 @@ static bool try_async_pf(struct kvm_vcpu *vcpu, bool prefault, gfn_t gfn, return false; /* *pfn has correct page already */ if (!prefault && kvm_can_do_async_pf(vcpu)) { - trace_kvm_try_async_get_page(gva, gfn); + trace_kvm_try_async_get_page(cr2_or_gpa, gfn); if (kvm_find_async_pf_gfn(vcpu, gfn)) { - trace_kvm_async_pf_doublefault(gva, gfn); + trace_kvm_async_pf_doublefault(cr2_or_gpa, gfn); kvm_make_request(KVM_REQ_APF_HALT, vcpu); return true; - } else if (kvm_arch_setup_async_pf(vcpu, gva, gfn)) + } else if (kvm_arch_setup_async_pf(vcpu, cr2_or_gpa, gfn)) return true; } @@ -4092,6 +4097,12 @@ int kvm_handle_page_fault(struct kvm_vcpu *vcpu, u64 error_code, { int r = 1; +#ifndef CONFIG_X86_64 + /* A 64-bit CR2 should be impossible on 32-bit KVM. */ + if (WARN_ON_ONCE(fault_address >> 32)) + return -EFAULT; +#endif + vcpu->arch.l1tf_flush_l1d = true; switch (vcpu->arch.apf.host_apf_reason) { default: @@ -4129,7 +4140,7 @@ check_hugepage_cache_consistency(struct kvm_vcpu *vcpu, gfn_t gfn, int level) return kvm_mtrr_check_gfn_range_consistency(vcpu, gfn, page_num); } -static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa, u32 error_code, +static int tdp_page_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u32 error_code, bool prefault) { kvm_pfn_t pfn; @@ -5307,7 +5318,7 @@ static int make_mmu_pages_available(struct kvm_vcpu *vcpu) return 0; } -int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t cr2, u64 error_code, +int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, u64 error_code, void *insn, int insn_len) { int r, emulation_type = 0; @@ -5317,19 +5328,20 @@ int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t cr2, u64 error_code, /* With shadow page tables, fault_address contains a GVA or nGPA. */ if (vcpu->arch.mmu.direct_map) { vcpu->arch.gpa_available = true; - vcpu->arch.gpa_val = cr2; + vcpu->arch.gpa_val = cr2_or_gpa; } r = RET_PF_INVALID; if (unlikely(error_code & PFERR_RSVD_MASK)) { - r = handle_mmio_page_fault(vcpu, cr2, direct); + r = handle_mmio_page_fault(vcpu, cr2_or_gpa, direct); if (r == RET_PF_EMULATE) goto emulate; } if (r == RET_PF_INVALID) { - r = vcpu->arch.mmu.page_fault(vcpu, cr2, lower_32_bits(error_code), - false); + r = vcpu->arch.mmu.page_fault(vcpu, cr2_or_gpa, + lower_32_bits(error_code), + false); WARN_ON(r == RET_PF_INVALID); } @@ -5347,7 +5359,7 @@ int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t cr2, u64 error_code, */ if (vcpu->arch.mmu.direct_map && (error_code & PFERR_NESTED_GUEST_PAGE) == PFERR_NESTED_GUEST_PAGE) { - kvm_mmu_unprotect_page(vcpu->kvm, gpa_to_gfn(cr2)); + kvm_mmu_unprotect_page(vcpu->kvm, gpa_to_gfn(cr2_or_gpa)); return 1; } @@ -5362,7 +5374,7 @@ int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t cr2, u64 error_code, * explicitly shadowing L1's page tables, i.e. unprotecting something * for L1 isn't going to magically fix whatever issue cause L2 to fail. */ - if (!mmio_info_in_cache(vcpu, cr2, direct) && !is_guest_mode(vcpu)) + if (!mmio_info_in_cache(vcpu, cr2_or_gpa, direct) && !is_guest_mode(vcpu)) emulation_type = EMULTYPE_ALLOW_RETRY; emulate: /* @@ -5375,7 +5387,7 @@ int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t cr2, u64 error_code, if (unlikely(insn && !insn_len)) return 1; - er = x86_emulate_instruction(vcpu, cr2, emulation_type, insn, insn_len); + er = x86_emulate_instruction(vcpu, cr2_or_gpa, emulation_type, insn, insn_len); switch (er) { case EMULATE_DONE: diff --git a/arch/x86/kvm/mmutrace.h b/arch/x86/kvm/mmutrace.h index 918b0d5bf2724c66953addcc6b0bfd5b776c6c45..cb41b036eb2646c98c806672b71b15a834fca5be 100644 --- a/arch/x86/kvm/mmutrace.h +++ b/arch/x86/kvm/mmutrace.h @@ -249,13 +249,13 @@ TRACE_EVENT( TRACE_EVENT( fast_page_fault, - TP_PROTO(struct kvm_vcpu *vcpu, gva_t gva, u32 error_code, + TP_PROTO(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, u32 error_code, u64 *sptep, u64 old_spte, bool retry), - TP_ARGS(vcpu, gva, error_code, sptep, old_spte, retry), + TP_ARGS(vcpu, cr2_or_gpa, error_code, sptep, old_spte, retry), TP_STRUCT__entry( __field(int, vcpu_id) - __field(gva_t, gva) + __field(gpa_t, cr2_or_gpa) __field(u32, error_code) __field(u64 *, sptep) __field(u64, old_spte) @@ -265,7 +265,7 @@ TRACE_EVENT( TP_fast_assign( __entry->vcpu_id = vcpu->vcpu_id; - __entry->gva = gva; + __entry->cr2_or_gpa = cr2_or_gpa; __entry->error_code = error_code; __entry->sptep = sptep; __entry->old_spte = old_spte; @@ -273,9 +273,9 @@ TRACE_EVENT( __entry->retry = retry; ), - TP_printk("vcpu %d gva %lx error_code %s sptep %p old %#llx" + TP_printk("vcpu %d gva %llx error_code %s sptep %p old %#llx" " new %llx spurious %d fixed %d", __entry->vcpu_id, - __entry->gva, __print_flags(__entry->error_code, "|", + __entry->cr2_or_gpa, __print_flags(__entry->error_code, "|", kvm_mmu_trace_pferr_flags), __entry->sptep, __entry->old_spte, __entry->new_spte, __spte_satisfied(old_spte), __spte_satisfied(new_spte) diff --git a/arch/x86/kvm/mtrr.c b/arch/x86/kvm/mtrr.c index 9f72cc427158e637b1852e9843c5eddfcf78c43a..fabce87697e52fac5ad2eb220c22688caa093b39 100644 --- a/arch/x86/kvm/mtrr.c +++ b/arch/x86/kvm/mtrr.c @@ -194,11 +194,15 @@ static bool fixed_msr_to_seg_unit(u32 msr, int *seg, int *unit) break; case MSR_MTRRfix16K_80000 ... MSR_MTRRfix16K_A0000: *seg = 1; - *unit = msr - MSR_MTRRfix16K_80000; + *unit = array_index_nospec( + msr - MSR_MTRRfix16K_80000, + MSR_MTRRfix16K_A0000 - MSR_MTRRfix16K_80000 + 1); break; case MSR_MTRRfix4K_C0000 ... MSR_MTRRfix4K_F8000: *seg = 2; - *unit = msr - MSR_MTRRfix4K_C0000; + *unit = array_index_nospec( + msr - MSR_MTRRfix4K_C0000, + MSR_MTRRfix4K_F8000 - MSR_MTRRfix4K_C0000 + 1); break; default: return false; diff --git a/arch/x86/kvm/paging_tmpl.h b/arch/x86/kvm/paging_tmpl.h index adf42dc8d38b05bab3722558fbb740eb931c98f4..61f10a4fd8074f0af1175de1ee2f4af98c807a6e 100644 --- a/arch/x86/kvm/paging_tmpl.h +++ b/arch/x86/kvm/paging_tmpl.h @@ -36,7 +36,7 @@ #define PT_GUEST_ACCESSED_SHIFT PT_ACCESSED_SHIFT #define PT_HAVE_ACCESSED_DIRTY(mmu) true #ifdef CONFIG_X86_64 - #define PT_MAX_FULL_LEVELS 4 + #define PT_MAX_FULL_LEVELS PT64_ROOT_MAX_LEVEL #define CMPXCHG cmpxchg #else #define CMPXCHG cmpxchg64 @@ -273,11 +273,11 @@ static inline unsigned FNAME(gpte_pkeys)(struct kvm_vcpu *vcpu, u64 gpte) } /* - * Fetch a guest pte for a guest virtual address + * Fetch a guest pte for a guest virtual address, or for an L2's GPA. */ static int FNAME(walk_addr_generic)(struct guest_walker *walker, struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, - gva_t addr, u32 access) + gpa_t addr, u32 access) { int ret; pt_element_t pte; @@ -478,7 +478,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker, } static int FNAME(walk_addr)(struct guest_walker *walker, - struct kvm_vcpu *vcpu, gva_t addr, u32 access) + struct kvm_vcpu *vcpu, gpa_t addr, u32 access) { return FNAME(walk_addr_generic)(walker, vcpu, &vcpu->arch.mmu, addr, access); @@ -593,7 +593,7 @@ static void FNAME(pte_prefetch)(struct kvm_vcpu *vcpu, struct guest_walker *gw, * If the guest tries to write a write-protected page, we need to * emulate this operation, return 1 to indicate this case. */ -static int FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr, +static int FNAME(fetch)(struct kvm_vcpu *vcpu, gpa_t addr, struct guest_walker *gw, int write_fault, int hlevel, kvm_pfn_t pfn, bool map_writable, bool prefault, @@ -747,7 +747,7 @@ FNAME(is_self_change_mapping)(struct kvm_vcpu *vcpu, * Returns: 1 if we need to emulate the instruction, 0 otherwise, or * a negative value on error. */ -static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, u32 error_code, +static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gpa_t addr, u32 error_code, bool prefault) { int write_fault = error_code & PFERR_WRITE_MASK; @@ -926,18 +926,19 @@ static void FNAME(invlpg)(struct kvm_vcpu *vcpu, gva_t gva, hpa_t root_hpa) spin_unlock(&vcpu->kvm->mmu_lock); } -static gpa_t FNAME(gva_to_gpa)(struct kvm_vcpu *vcpu, gva_t vaddr, u32 access, +/* Note, @addr is a GPA when gva_to_gpa() translates an L2 GPA to an L1 GPA. */ +static gpa_t FNAME(gva_to_gpa)(struct kvm_vcpu *vcpu, gpa_t addr, u32 access, struct x86_exception *exception) { struct guest_walker walker; gpa_t gpa = UNMAPPED_GVA; int r; - r = FNAME(walk_addr)(&walker, vcpu, vaddr, access); + r = FNAME(walk_addr)(&walker, vcpu, addr, access); if (r) { gpa = gfn_to_gpa(walker.gfn); - gpa |= vaddr & ~PAGE_MASK; + gpa |= addr & ~PAGE_MASK; } else if (exception) *exception = walker.fault; @@ -945,7 +946,8 @@ static gpa_t FNAME(gva_to_gpa)(struct kvm_vcpu *vcpu, gva_t vaddr, u32 access, } #if PTTYPE != PTTYPE_EPT -static gpa_t FNAME(gva_to_gpa_nested)(struct kvm_vcpu *vcpu, gva_t vaddr, +/* Note, gva_to_gpa_nested() is only used to translate L2 GVAs. */ +static gpa_t FNAME(gva_to_gpa_nested)(struct kvm_vcpu *vcpu, gpa_t vaddr, u32 access, struct x86_exception *exception) { @@ -953,6 +955,11 @@ static gpa_t FNAME(gva_to_gpa_nested)(struct kvm_vcpu *vcpu, gva_t vaddr, gpa_t gpa = UNMAPPED_GVA; int r; +#ifndef CONFIG_X86_64 + /* A 64-bit GVA should be impossible on 32-bit KVM. */ + WARN_ON_ONCE(vaddr >> 32); +#endif + r = FNAME(walk_addr_nested)(&walker, vcpu, vaddr, access); if (r) { diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h index 22dff661145a1bcbf5996b7cb0c1fba660a5859e..7b4828e50ab3f287a6900a2cf0a471b70478ac47 100644 --- a/arch/x86/kvm/pmu.h +++ b/arch/x86/kvm/pmu.h @@ -2,6 +2,8 @@ #ifndef __KVM_X86_PMU_H #define __KVM_X86_PMU_H +#include + #define vcpu_to_pmu(vcpu) (&(vcpu)->arch.pmu) #define pmu_to_vcpu(pmu) (container_of((pmu), struct kvm_vcpu, arch.pmu)) #define pmc_to_pmu(pmc) (&(pmc)->vcpu->arch.pmu) @@ -86,8 +88,12 @@ static inline bool pmc_is_enabled(struct kvm_pmc *pmc) static inline struct kvm_pmc *get_gp_pmc(struct kvm_pmu *pmu, u32 msr, u32 base) { - if (msr >= base && msr < base + pmu->nr_arch_gp_counters) - return &pmu->gp_counters[msr - base]; + if (msr >= base && msr < base + pmu->nr_arch_gp_counters) { + u32 index = array_index_nospec(msr - base, + pmu->nr_arch_gp_counters); + + return &pmu->gp_counters[index]; + } return NULL; } @@ -97,8 +103,12 @@ static inline struct kvm_pmc *get_fixed_pmc(struct kvm_pmu *pmu, u32 msr) { int base = MSR_CORE_PERF_FIXED_CTR0; - if (msr >= base && msr < base + pmu->nr_arch_fixed_counters) - return &pmu->fixed_counters[msr - base]; + if (msr >= base && msr < base + pmu->nr_arch_fixed_counters) { + u32 index = array_index_nospec(msr - base, + pmu->nr_arch_fixed_counters); + + return &pmu->fixed_counters[index]; + } return NULL; } diff --git a/arch/x86/kvm/pmu_intel.c b/arch/x86/kvm/pmu_intel.c index c3f103e2b08e15b7286d81306cb8a5694db8e75f..2ab8c20c8bfada19011396fa1826e761b1c8986d 100644 --- a/arch/x86/kvm/pmu_intel.c +++ b/arch/x86/kvm/pmu_intel.c @@ -87,10 +87,14 @@ static unsigned intel_find_arch_event(struct kvm_pmu *pmu, static unsigned intel_find_fixed_event(int idx) { - if (idx >= ARRAY_SIZE(fixed_pmc_events)) + u32 event; + size_t size = ARRAY_SIZE(fixed_pmc_events); + + if (idx >= size) return PERF_COUNT_HW_MAX; - return intel_arch_events[fixed_pmc_events[idx]].event_type; + event = fixed_pmc_events[array_index_nospec(idx, size)]; + return intel_arch_events[event].event_type; } /* check if a PMC is enabled by comparing it with globl_ctrl bits. */ @@ -131,16 +135,20 @@ static struct kvm_pmc *intel_msr_idx_to_pmc(struct kvm_vcpu *vcpu, struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); bool fixed = idx & (1u << 30); struct kvm_pmc *counters; + unsigned int num_counters; idx &= ~(3u << 30); - if (!fixed && idx >= pmu->nr_arch_gp_counters) - return NULL; - if (fixed && idx >= pmu->nr_arch_fixed_counters) + if (fixed) { + counters = pmu->fixed_counters; + num_counters = pmu->nr_arch_fixed_counters; + } else { + counters = pmu->gp_counters; + num_counters = pmu->nr_arch_gp_counters; + } + if (idx >= num_counters) return NULL; - counters = fixed ? pmu->fixed_counters : pmu->gp_counters; *mask &= pmu->counter_bitmask[fixed ? KVM_PMC_FIXED : KVM_PMC_GP]; - - return &counters[idx]; + return &counters[array_index_nospec(idx, num_counters)]; } static bool intel_is_valid_msr(struct kvm_vcpu *vcpu, u32 msr) diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c index 7657dcd72134bd607fdd38ad62faf6e45db4241b..cc8f3b41a1b2e68de6308963e404ab5dcf1567b2 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c @@ -1298,6 +1298,47 @@ static void shrink_ple_window(struct kvm_vcpu *vcpu) control->pause_filter_count, old); } +/* + * The default MMIO mask is a single bit (excluding the present bit), + * which could conflict with the memory encryption bit. Check for + * memory encryption support and override the default MMIO mask if + * memory encryption is enabled. + */ +static __init void svm_adjust_mmio_mask(void) +{ + unsigned int enc_bit, mask_bit; + u64 msr, mask; + + /* If there is no memory encryption support, use existing mask */ + if (cpuid_eax(0x80000000) < 0x8000001f) + return; + + /* If memory encryption is not enabled, use existing mask */ + rdmsrl(MSR_K8_SYSCFG, msr); + if (!(msr & MSR_K8_SYSCFG_MEM_ENCRYPT)) + return; + + enc_bit = cpuid_ebx(0x8000001f) & 0x3f; + mask_bit = boot_cpu_data.x86_phys_bits; + + /* Increment the mask bit if it is the same as the encryption bit */ + if (enc_bit == mask_bit) + mask_bit++; + + /* + * If the mask bit location is below 52, then some bits above the + * physical addressing limit will always be reserved, so use the + * rsvd_bits() function to generate the mask. This mask, along with + * the present bit, will be used to generate a page fault with + * PFER.RSV = 1. + * + * If the mask bit location is 52 (or above), then clear the mask. + */ + mask = (mask_bit < 52) ? rsvd_bits(mask_bit, 51) | PT_PRESENT_MASK : 0; + + kvm_mmu_set_mmio_spte_mask(mask, mask); +} + static __init int svm_hardware_setup(void) { int cpu; @@ -1352,6 +1393,8 @@ static __init int svm_hardware_setup(void) } } + svm_adjust_mmio_mask(); + for_each_possible_cpu(cpu) { r = svm_cpu_init(cpu); if (r) @@ -5140,8 +5183,11 @@ static void svm_load_eoi_exitmap(struct kvm_vcpu *vcpu, u64 *eoi_exit_bitmap) return; } -static void svm_deliver_avic_intr(struct kvm_vcpu *vcpu, int vec) +static int svm_deliver_avic_intr(struct kvm_vcpu *vcpu, int vec) { + if (!vcpu->arch.apicv_active) + return -1; + kvm_lapic_set_irr(vec, vcpu->arch.apic); smp_mb__after_atomic(); @@ -5150,6 +5196,8 @@ static void svm_deliver_avic_intr(struct kvm_vcpu *vcpu, int vec) kvm_cpu_get_apicid(vcpu->cpu)); else kvm_vcpu_wake_up(vcpu); + + return 0; } static bool svm_dy_apicv_has_pending_interrupt(struct kvm_vcpu *vcpu) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index fa2abed1a14dabd6a781bd7b184f0a05981ebd9b..a81d7d9ce9d695a0c1847a75cfcc19469baebcfe 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -5302,6 +5302,9 @@ static void vmx_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0) static int get_ept_level(struct kvm_vcpu *vcpu) { + /* Nested EPT currently only supports 4-level walks. */ + if (is_guest_mode(vcpu) && nested_cpu_has_ept(get_vmcs12(vcpu))) + return 4; if (cpu_has_vmx_ept_5levels() && (cpuid_maxphyaddr(vcpu) > 48)) return 5; return 4; @@ -5722,6 +5725,26 @@ static bool cs_ss_rpl_check(struct kvm_vcpu *vcpu) (ss.selector & SEGMENT_RPL_MASK)); } +static bool nested_vmx_check_io_bitmaps(struct kvm_vcpu *vcpu, + unsigned int port, int size); +static bool nested_vmx_exit_handled_io(struct kvm_vcpu *vcpu, + struct vmcs12 *vmcs12) +{ + unsigned long exit_qualification; + unsigned short port; + int size; + + if (!nested_cpu_has(vmcs12, CPU_BASED_USE_IO_BITMAPS)) + return nested_cpu_has(vmcs12, CPU_BASED_UNCOND_IO_EXITING); + + exit_qualification = vmcs_readl(EXIT_QUALIFICATION); + + port = exit_qualification >> 16; + size = (exit_qualification & 7) + 1; + + return nested_vmx_check_io_bitmaps(vcpu, port, size); +} + /* * Check if guest state is valid. Returns true if valid, false if * not. @@ -6261,24 +6284,29 @@ static int vmx_deliver_nested_posted_interrupt(struct kvm_vcpu *vcpu, * 2. If target vcpu isn't running(root mode), kick it to pick up the * interrupt from PIR in next vmentry. */ -static void vmx_deliver_posted_interrupt(struct kvm_vcpu *vcpu, int vector) +static int vmx_deliver_posted_interrupt(struct kvm_vcpu *vcpu, int vector) { struct vcpu_vmx *vmx = to_vmx(vcpu); int r; r = vmx_deliver_nested_posted_interrupt(vcpu, vector); if (!r) - return; + return 0; + + if (!vcpu->arch.apicv_active) + return -1; if (pi_test_and_set_pir(vector, &vmx->pi_desc)) - return; + return 0; /* If a previous notification has sent the IPI, nothing to do. */ if (pi_test_and_set_on(&vmx->pi_desc)) - return; + return 0; if (!kvm_vcpu_trigger_posted_interrupt(vcpu, false)) kvm_vcpu_kick(vcpu); + + return 0; } /* @@ -8793,8 +8821,10 @@ static int handle_vmread(struct kvm_vcpu *vcpu) /* _system ok, nested_vmx_check_permission has verified cpl=0 */ if (kvm_write_guest_virt_system(vcpu, gva, &field_value, (is_long_mode(vcpu) ? 8 : 4), - &e)) + &e)) { kvm_inject_page_fault(vcpu, &e); + return 1; + } } nested_vmx_succeed(vcpu); @@ -9464,23 +9494,17 @@ static int (*const kvm_vmx_exit_handlers[])(struct kvm_vcpu *vcpu) = { static const int kvm_vmx_max_exit_handlers = ARRAY_SIZE(kvm_vmx_exit_handlers); -static bool nested_vmx_exit_handled_io(struct kvm_vcpu *vcpu, - struct vmcs12 *vmcs12) +/* + * Return true if an IO instruction with the specified port and size should cause + * a VM-exit into L1. + */ +bool nested_vmx_check_io_bitmaps(struct kvm_vcpu *vcpu, unsigned int port, + int size) { - unsigned long exit_qualification; + struct vmcs12 *vmcs12 = get_vmcs12(vcpu); gpa_t bitmap, last_bitmap; - unsigned int port; - int size; u8 b; - if (!nested_cpu_has(vmcs12, CPU_BASED_USE_IO_BITMAPS)) - return nested_cpu_has(vmcs12, CPU_BASED_UNCOND_IO_EXITING); - - exit_qualification = vmcs_readl(EXIT_QUALIFICATION); - - port = exit_qualification >> 16; - size = (exit_qualification & 7) + 1; - last_bitmap = (gpa_t)-1; b = -1; @@ -13670,6 +13694,40 @@ static void nested_vmx_entry_failure(struct kvm_vcpu *vcpu, to_vmx(vcpu)->nested.sync_shadow_vmcs = true; } +static int vmx_check_intercept_io(struct kvm_vcpu *vcpu, + struct x86_instruction_info *info) +{ + struct vmcs12 *vmcs12 = get_vmcs12(vcpu); + unsigned short port; + bool intercept; + int size; + + if (info->intercept == x86_intercept_in || + info->intercept == x86_intercept_ins) { + port = info->src_val; + size = info->dst_bytes; + } else { + port = info->dst_val; + size = info->src_bytes; + } + + /* + * If the 'use IO bitmaps' VM-execution control is 0, IO instruction + * VM-exits depend on the 'unconditional IO exiting' VM-execution + * control. + * + * Otherwise, IO instruction VM-exits are controlled by the IO bitmaps. + */ + if (!nested_cpu_has(vmcs12, CPU_BASED_USE_IO_BITMAPS)) + intercept = nested_cpu_has(vmcs12, + CPU_BASED_UNCOND_IO_EXITING); + else + intercept = nested_vmx_check_io_bitmaps(vcpu, port, size); + + /* FIXME: produce nested vmexit and return X86EMUL_INTERCEPTED. */ + return intercept ? X86EMUL_UNHANDLEABLE : X86EMUL_CONTINUE; +} + static int vmx_check_intercept(struct kvm_vcpu *vcpu, struct x86_instruction_info *info, enum x86_intercept_stage stage) @@ -13677,19 +13735,45 @@ static int vmx_check_intercept(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12 = get_vmcs12(vcpu); struct x86_emulate_ctxt *ctxt = &vcpu->arch.emulate_ctxt; + switch (info->intercept) { /* * RDPID causes #UD if disabled through secondary execution controls. * Because it is marked as EmulateOnUD, we need to intercept it here. */ - if (info->intercept == x86_intercept_rdtscp && - !nested_cpu_has2(vmcs12, SECONDARY_EXEC_RDTSCP)) { - ctxt->exception.vector = UD_VECTOR; - ctxt->exception.error_code_valid = false; - return X86EMUL_PROPAGATE_FAULT; - } + case x86_intercept_rdtscp: + if (!nested_cpu_has2(vmcs12, SECONDARY_EXEC_RDTSCP)) { + ctxt->exception.vector = UD_VECTOR; + ctxt->exception.error_code_valid = false; + return X86EMUL_PROPAGATE_FAULT; + } + break; + + case x86_intercept_in: + case x86_intercept_ins: + case x86_intercept_out: + case x86_intercept_outs: + return vmx_check_intercept_io(vcpu, info); + + case x86_intercept_lgdt: + case x86_intercept_lidt: + case x86_intercept_lldt: + case x86_intercept_ltr: + case x86_intercept_sgdt: + case x86_intercept_sidt: + case x86_intercept_sldt: + case x86_intercept_str: + if (!nested_cpu_has2(vmcs12, SECONDARY_EXEC_DESC)) + return X86EMUL_CONTINUE; + + /* FIXME: produce nested vmexit and return X86EMUL_INTERCEPTED. */ + break; /* TODO: check more intercepts... */ - return X86EMUL_CONTINUE; + default: + break; + } + + return X86EMUL_UNHANDLEABLE; } #ifdef CONFIG_X86_64 diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 353f63f3b262673571dc0e94e46a8426c81b2962..2cb379e261c0fc6af3908a8181b7d72cc6bdfb2d 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -92,6 +92,8 @@ u64 __read_mostly efer_reserved_bits = ~((u64)(EFER_SCE | EFER_LME | EFER_LMA)); static u64 __read_mostly efer_reserved_bits = ~((u64)EFER_SCE); #endif +static u64 __read_mostly cr4_reserved_bits = CR4_RESERVED_BITS; + #define VM_STAT(x, ...) offsetof(struct kvm, stat.x), KVM_STAT_VM, ## __VA_ARGS__ #define VCPU_STAT(x, ...) offsetof(struct kvm_vcpu, stat.x), KVM_STAT_VCPU, ## __VA_ARGS__ @@ -793,9 +795,38 @@ int kvm_set_xcr(struct kvm_vcpu *vcpu, u32 index, u64 xcr) } EXPORT_SYMBOL_GPL(kvm_set_xcr); +static u64 kvm_host_cr4_reserved_bits(struct cpuinfo_x86 *c) +{ + u64 reserved_bits = CR4_RESERVED_BITS; + + if (!cpu_has(c, X86_FEATURE_XSAVE)) + reserved_bits |= X86_CR4_OSXSAVE; + + if (!cpu_has(c, X86_FEATURE_SMEP)) + reserved_bits |= X86_CR4_SMEP; + + if (!cpu_has(c, X86_FEATURE_SMAP)) + reserved_bits |= X86_CR4_SMAP; + + if (!cpu_has(c, X86_FEATURE_FSGSBASE)) + reserved_bits |= X86_CR4_FSGSBASE; + + if (!cpu_has(c, X86_FEATURE_PKU)) + reserved_bits |= X86_CR4_PKE; + + if (!cpu_has(c, X86_FEATURE_LA57) && + !(cpuid_ecx(0x7) & bit(X86_FEATURE_LA57))) + reserved_bits |= X86_CR4_LA57; + + if (!cpu_has(c, X86_FEATURE_UMIP) && !kvm_x86_ops->umip_emulated()) + reserved_bits |= X86_CR4_UMIP; + + return reserved_bits; +} + static int kvm_valid_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) { - if (cr4 & CR4_RESERVED_BITS) + if (cr4 & cr4_reserved_bits) return -EINVAL; if (!guest_cpuid_has(vcpu, X86_FEATURE_XSAVE) && (cr4 & X86_CR4_OSXSAVE)) @@ -961,9 +992,11 @@ static u64 kvm_dr6_fixed(struct kvm_vcpu *vcpu) static int __kvm_set_dr(struct kvm_vcpu *vcpu, int dr, unsigned long val) { + size_t size = ARRAY_SIZE(vcpu->arch.db); + switch (dr) { case 0 ... 3: - vcpu->arch.db[dr] = val; + vcpu->arch.db[array_index_nospec(dr, size)] = val; if (!(vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP)) vcpu->arch.eff_db[dr] = val; break; @@ -1000,9 +1033,11 @@ EXPORT_SYMBOL_GPL(kvm_set_dr); int kvm_get_dr(struct kvm_vcpu *vcpu, int dr, unsigned long *val) { + size_t size = ARRAY_SIZE(vcpu->arch.db); + switch (dr) { case 0 ... 3: - *val = vcpu->arch.db[dr]; + *val = vcpu->arch.db[array_index_nospec(dr, size)]; break; case 4: /* fall through */ @@ -2269,7 +2304,10 @@ static int set_msr_mce(struct kvm_vcpu *vcpu, struct msr_data *msr_info) default: if (msr >= MSR_IA32_MC0_CTL && msr < MSR_IA32_MCx_CTL(bank_num)) { - u32 offset = msr - MSR_IA32_MC0_CTL; + u32 offset = array_index_nospec( + msr - MSR_IA32_MC0_CTL, + MSR_IA32_MCx_CTL(bank_num) - MSR_IA32_MC0_CTL); + /* only 0 or all 1s can be written to IA32_MCi_CTL * some Linux kernels though clear bit 10 in bank 4 to * workaround a BIOS/GART TBL issue on AMD K8s, ignore @@ -2681,7 +2719,10 @@ static int get_msr_mce(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata, bool host) default: if (msr >= MSR_IA32_MC0_CTL && msr < MSR_IA32_MCx_CTL(bank_num)) { - u32 offset = msr - MSR_IA32_MC0_CTL; + u32 offset = array_index_nospec( + msr - MSR_IA32_MC0_CTL, + MSR_IA32_MCx_CTL(bank_num) - MSR_IA32_MC0_CTL); + data = vcpu->arch.mce_banks[offset]; break; } @@ -3234,6 +3275,9 @@ static void kvm_steal_time_set_preempted(struct kvm_vcpu *vcpu) if (!(vcpu->arch.st.msr_val & KVM_MSR_ENABLED)) return; + if (vcpu->arch.st.steal.preempted) + return; + vcpu->arch.st.steal.preempted = KVM_VCPU_PREEMPTED; kvm_write_guest_offset_cached(vcpu->kvm, &vcpu->arch.st.stime, @@ -5977,11 +6021,11 @@ static int handle_emulation_failure(struct kvm_vcpu *vcpu, int emulation_type) return r; } -static bool reexecute_instruction(struct kvm_vcpu *vcpu, gva_t cr2, +static bool reexecute_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, bool write_fault_to_shadow_pgtable, int emulation_type) { - gpa_t gpa = cr2; + gpa_t gpa = cr2_or_gpa; kvm_pfn_t pfn; if (!(emulation_type & EMULTYPE_ALLOW_RETRY)) @@ -5995,7 +6039,7 @@ static bool reexecute_instruction(struct kvm_vcpu *vcpu, gva_t cr2, * Write permission should be allowed since only * write access need to be emulated. */ - gpa = kvm_mmu_gva_to_gpa_write(vcpu, cr2, NULL); + gpa = kvm_mmu_gva_to_gpa_write(vcpu, cr2_or_gpa, NULL); /* * If the mapping is invalid in guest, let cpu retry @@ -6052,10 +6096,10 @@ static bool reexecute_instruction(struct kvm_vcpu *vcpu, gva_t cr2, } static bool retry_instruction(struct x86_emulate_ctxt *ctxt, - unsigned long cr2, int emulation_type) + gpa_t cr2_or_gpa, int emulation_type) { struct kvm_vcpu *vcpu = emul_to_vcpu(ctxt); - unsigned long last_retry_eip, last_retry_addr, gpa = cr2; + unsigned long last_retry_eip, last_retry_addr, gpa = cr2_or_gpa; last_retry_eip = vcpu->arch.last_retry_eip; last_retry_addr = vcpu->arch.last_retry_addr; @@ -6084,14 +6128,14 @@ static bool retry_instruction(struct x86_emulate_ctxt *ctxt, if (x86_page_table_writing_insn(ctxt)) return false; - if (ctxt->eip == last_retry_eip && last_retry_addr == cr2) + if (ctxt->eip == last_retry_eip && last_retry_addr == cr2_or_gpa) return false; vcpu->arch.last_retry_eip = ctxt->eip; - vcpu->arch.last_retry_addr = cr2; + vcpu->arch.last_retry_addr = cr2_or_gpa; if (!vcpu->arch.mmu.direct_map) - gpa = kvm_mmu_gva_to_gpa_write(vcpu, cr2, NULL); + gpa = kvm_mmu_gva_to_gpa_write(vcpu, cr2_or_gpa, NULL); kvm_mmu_unprotect_page(vcpu->kvm, gpa_to_gfn(gpa)); @@ -6252,11 +6296,8 @@ static bool is_vmware_backdoor_opcode(struct x86_emulate_ctxt *ctxt) return false; } -int x86_emulate_instruction(struct kvm_vcpu *vcpu, - unsigned long cr2, - int emulation_type, - void *insn, - int insn_len) +int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, + int emulation_type, void *insn, int insn_len) { int r; struct x86_emulate_ctxt *ctxt = &vcpu->arch.emulate_ctxt; @@ -6299,7 +6340,7 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, if (r != EMULATION_OK) { if (emulation_type & EMULTYPE_TRAP_UD) return EMULATE_FAIL; - if (reexecute_instruction(vcpu, cr2, write_fault_to_spt, + if (reexecute_instruction(vcpu, cr2_or_gpa, write_fault_to_spt, emulation_type)) return EMULATE_DONE; if (ctxt->have_exception) { @@ -6329,7 +6370,7 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, return EMULATE_DONE; } - if (retry_instruction(ctxt, cr2, emulation_type)) + if (retry_instruction(ctxt, cr2_or_gpa, emulation_type)) return EMULATE_DONE; /* this is needed for vmware backdoor interface to work since it @@ -6341,7 +6382,7 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, restart: /* Save the faulting GPA (cr2) in the address field */ - ctxt->exception.address = cr2; + ctxt->exception.address = cr2_or_gpa; r = x86_emulate_insn(ctxt); @@ -6349,7 +6390,7 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, return EMULATE_DONE; if (r == EMULATION_FAILED) { - if (reexecute_instruction(vcpu, cr2, write_fault_to_spt, + if (reexecute_instruction(vcpu, cr2_or_gpa, write_fault_to_spt, emulation_type)) return EMULATE_DONE; @@ -6753,7 +6794,7 @@ static void kvm_set_mmio_spte_mask(void) * If reserved bit is not supported, clear the present bit to disable * mmio page fault. */ - if (IS_ENABLED(CONFIG_X86_64) && maxphyaddr == 52) + if (maxphyaddr == 52) mask &= ~1ull; kvm_mmu_set_mmio_spte_mask(mask, mask); @@ -8225,6 +8266,8 @@ int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu, struct kvm_mp_state *mp_state) { vcpu_load(vcpu); + if (kvm_mpx_supported()) + kvm_load_guest_fpu(vcpu); kvm_apic_accept_events(vcpu); if (vcpu->arch.mp_state == KVM_MP_STATE_HALTED && @@ -8233,6 +8276,8 @@ int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu, else mp_state->mp_state = vcpu->arch.mp_state; + if (kvm_mpx_supported()) + kvm_put_guest_fpu(vcpu); vcpu_put(vcpu); return 0; } @@ -8648,13 +8693,7 @@ void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu) void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) { - vcpu->arch.apf.msr_val = 0; - - vcpu_load(vcpu); - kvm_mmu_unload(vcpu); - vcpu_put(vcpu); - - kvm_x86_ops->vcpu_free(vcpu); + kvm_arch_vcpu_free(vcpu); } void kvm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event) @@ -8847,6 +8886,8 @@ int kvm_arch_hardware_setup(void) if (r != 0) return r; + cr4_reserved_bits = kvm_host_cr4_reserved_bits(&boot_cpu_data); + if (kvm_has_tsc_control) { /* * Make sure the user can only configure tsc_khz values that @@ -9505,7 +9546,7 @@ void kvm_arch_async_page_ready(struct kvm_vcpu *vcpu, struct kvm_async_pf *work) work->arch.cr3 != vcpu->arch.mmu.get_cr3(vcpu)) return; - vcpu->arch.mmu.page_fault(vcpu, work->gva, 0, true); + vcpu->arch.mmu.page_fault(vcpu, work->cr2_or_gpa, 0, true); } static inline u32 kvm_async_pf_hash_fn(gfn_t gfn) @@ -9588,7 +9629,7 @@ void kvm_arch_async_page_not_present(struct kvm_vcpu *vcpu, { struct x86_exception fault; - trace_kvm_async_pf_not_present(work->arch.token, work->gva); + trace_kvm_async_pf_not_present(work->arch.token, work->cr2_or_gpa); kvm_add_async_pf_gfn(vcpu, work->arch.gfn); if (!(vcpu->arch.apf.msr_val & KVM_ASYNC_PF_ENABLED) || @@ -9616,7 +9657,7 @@ void kvm_arch_async_page_present(struct kvm_vcpu *vcpu, work->arch.token = ~0; /* broadcast wakeup */ else kvm_del_async_pf_gfn(vcpu, work->arch.gfn); - trace_kvm_async_pf_ready(work->arch.token, work->gva); + trace_kvm_async_pf_ready(work->arch.token, work->cr2_or_gpa); if (vcpu->arch.apf.msr_val & KVM_ASYNC_PF_ENABLED && !apf_get_user(vcpu, &val)) { diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h index 608e5f8c5d0a56c34680babca4a041799a024bd8..422331b257d3a9909f475d2d4c89cd39799260bd 100644 --- a/arch/x86/kvm/x86.h +++ b/arch/x86/kvm/x86.h @@ -284,7 +284,7 @@ int kvm_mtrr_get_msr(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata); bool kvm_mtrr_check_gfn_range_consistency(struct kvm_vcpu *vcpu, gfn_t gfn, int page_num); bool kvm_vector_hashing_enabled(void); -int x86_emulate_instruction(struct kvm_vcpu *vcpu, unsigned long cr2, +int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, int emulation_type, void *insn, int insn_len); #define KVM_SUPPORTED_XCR0 (XFEATURE_MASK_FP | XFEATURE_MASK_SSE \ diff --git a/arch/x86/lib/x86-opcode-map.txt b/arch/x86/lib/x86-opcode-map.txt index 0a0e9112f2842e5b40e4e2284b2c061c4d98370a..5cb9f009f2be3f32c2bd8316d0e5f9805f6b4ea5 100644 --- a/arch/x86/lib/x86-opcode-map.txt +++ b/arch/x86/lib/x86-opcode-map.txt @@ -909,7 +909,7 @@ EndTable GrpTable: Grp3_2 0: TEST Ev,Iz -1: +1: TEST Ev,Iz 2: NOT Ev 3: NEG Ev 4: MUL rAX,Ev diff --git a/arch/x86/mm/extable.c b/arch/x86/mm/extable.c index 45f5d6cf65aed0bf30f0885648df743c449c61a7..79e99553d862700c4bd6acc7ae4eb1ba1cf80b65 100644 --- a/arch/x86/mm/extable.c +++ b/arch/x86/mm/extable.c @@ -170,6 +170,7 @@ __visible bool ex_has_fault_handler(unsigned long ip) return handler == ex_handler_fault; } +__nocfi int fixup_exception(struct pt_regs *regs, int trapnr) { const struct exception_table_entry *e; diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c index 979e0a02cbe1a12d67fad130592a6e68c98925c5..79b95910fd9ffcf73a2bb33ccd3090dcb3112271 100644 --- a/arch/x86/mm/init_32.c +++ b/arch/x86/mm/init_32.c @@ -860,18 +860,15 @@ int arch_add_memory(int nid, u64 start, u64 size, struct vmem_altmap *altmap, return __add_pages(nid, start_pfn, nr_pages, altmap, want_memblock); } -#ifdef CONFIG_MEMORY_HOTREMOVE -int arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap) +void arch_remove_memory(int nid, u64 start, u64 size, + struct vmem_altmap *altmap) { unsigned long start_pfn = start >> PAGE_SHIFT; unsigned long nr_pages = size >> PAGE_SHIFT; - struct zone *zone; - zone = page_zone(pfn_to_page(start_pfn)); - return __remove_pages(zone, start_pfn, nr_pages, altmap); + __remove_pages(start_pfn, nr_pages, altmap); } #endif -#endif int kernel_set_to_readonly __read_mostly; diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index a3e9c6ee3cf2b086fa837d84f62fafaa8429f0bf..366a11fc2b45352c504cb52ae4b1432ce26bbb16 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -1132,7 +1132,6 @@ void __ref vmemmap_free(unsigned long start, unsigned long end, remove_pagetable(start, end, false, altmap); } -#ifdef CONFIG_MEMORY_HOTREMOVE static void __meminit kernel_physical_mapping_remove(unsigned long start, unsigned long end) { @@ -1142,25 +1141,15 @@ kernel_physical_mapping_remove(unsigned long start, unsigned long end) remove_pagetable(start, end, true, NULL); } -int __ref arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap) +void __ref arch_remove_memory(int nid, u64 start, u64 size, + struct vmem_altmap *altmap) { unsigned long start_pfn = start >> PAGE_SHIFT; unsigned long nr_pages = size >> PAGE_SHIFT; - struct page *page = pfn_to_page(start_pfn); - struct zone *zone; - int ret; - /* With altmap the first mapped page is offset from @start */ - if (altmap) - page += vmem_altmap_offset(altmap); - zone = page_zone(page); - ret = __remove_pages(zone, start_pfn, nr_pages, altmap); - WARN_ON_ONCE(ret); + __remove_pages(start_pfn, nr_pages, altmap); kernel_physical_mapping_remove(start, start + size); - - return ret; } -#endif #endif /* CONFIG_MEMORY_HOTPLUG */ static struct kcore_list kcore_vsyscall; @@ -1206,7 +1195,7 @@ int kernel_set_to_readonly; void set_kernel_text_rw(void) { unsigned long start = PFN_ALIGN(_text); - unsigned long end = PFN_ALIGN(__stop___ex_table); + unsigned long end = PFN_ALIGN(_etext); if (!kernel_set_to_readonly) return; @@ -1225,7 +1214,7 @@ void set_kernel_text_rw(void) void set_kernel_text_ro(void) { unsigned long start = PFN_ALIGN(_text); - unsigned long end = PFN_ALIGN(__stop___ex_table); + unsigned long end = PFN_ALIGN(_etext); if (!kernel_set_to_readonly) return; @@ -1244,7 +1233,7 @@ void mark_rodata_ro(void) unsigned long start = PFN_ALIGN(_text); unsigned long rodata_start = PFN_ALIGN(__start_rodata); unsigned long end = (unsigned long) &__end_rodata_hpage_align; - unsigned long text_end = PFN_ALIGN(&__stop___ex_table); + unsigned long text_end = PFN_ALIGN(&_etext); unsigned long rodata_end = PFN_ALIGN(&__end_rodata); unsigned long all_end; diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index e2d4b25c7aa44360595d2f6eefa028a5dfbeb482..101f3ad0d6ad14714891fad928ce8c7471db7d43 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c @@ -2126,19 +2126,13 @@ int kernel_map_pages_in_pgd(pgd_t *pgd, u64 pfn, unsigned long address, .pgd = pgd, .numpages = numpages, .mask_set = __pgprot(0), - .mask_clr = __pgprot(0), + .mask_clr = __pgprot(~page_flags & (_PAGE_NX|_PAGE_RW)), .flags = 0, }; if (!(__supported_pte_mask & _PAGE_NX)) goto out; - if (!(page_flags & _PAGE_NX)) - cpa.mask_clr = __pgprot(_PAGE_NX); - - if (!(page_flags & _PAGE_RW)) - cpa.mask_clr = __pgprot(_PAGE_RW); - if (!(page_flags & _PAGE_ENC)) cpa.mask_clr = pgprot_encrypted(cpa.mask_clr); diff --git a/arch/x86/mm/pti.c b/arch/x86/mm/pti.c index 622d5968c9795198471e8f2271a52762322b12f4..8316cdb407a009e43d0f5c1c7925b5bc2ab1d546 100644 --- a/arch/x86/mm/pti.c +++ b/arch/x86/mm/pti.c @@ -482,6 +482,15 @@ static void pti_clone_entry_text(void) pti_clone_pgtable((unsigned long) __entry_text_start, (unsigned long) __irqentry_text_end, PTI_CLONE_PMD); + + /* + * If CFI is enabled, also map jump tables, so the entry code can + * make indirect calls. + */ + if (IS_ENABLED(CONFIG_CFI_CLANG)) + pti_clone_pgtable((unsigned long) __cfi_jt_start, + (unsigned long) __cfi_jt_end, + PTI_CLONE_PMD); } /* @@ -551,7 +560,7 @@ static void pti_clone_kernel_text(void) */ unsigned long start = PFN_ALIGN(_text); unsigned long end_clone = (unsigned long)__end_rodata_aligned; - unsigned long end_global = PFN_ALIGN((unsigned long)__stop___ex_table); + unsigned long end_global = PFN_ALIGN((unsigned long)_etext); if (!pti_kernel_image_global_ok()) return; diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c index a6836ab0fcc7348dce7b4bdf1db07761c42351e3..b72296bd04a2976eafd425771fa20f56841dc535 100644 --- a/arch/x86/mm/tlb.c +++ b/arch/x86/mm/tlb.c @@ -664,9 +664,6 @@ void native_flush_tlb_others(const struct cpumask *cpumask, * that UV should be updated so that smp_call_function_many(), * etc, are optimal on UV. */ - unsigned int cpu; - - cpu = smp_processor_id(); cpumask = uv_flush_tlb_others(cpumask, info); if (cpumask) smp_call_function_many(cpumask, flush_tlb_func_remote, diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 335a62e74a2e9ad8d1751bd1aef1bc556d2ce172..e7f19dec16b9704927681dab9c74715f32b75faa 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -480,7 +480,6 @@ void __init efi_init(void) efi_char16_t *c16; char vendor[100] = "unknown"; int i = 0; - void *tmp; #ifdef CONFIG_X86_32 if (boot_params.efi_info.efi_systab_hi || @@ -505,14 +504,16 @@ void __init efi_init(void) /* * Show what we know for posterity */ - c16 = tmp = early_memremap(efi.systab->fw_vendor, 2); + c16 = early_memremap_ro(efi.systab->fw_vendor, + sizeof(vendor) * sizeof(efi_char16_t)); if (c16) { - for (i = 0; i < sizeof(vendor) - 1 && *c16; ++i) - vendor[i] = *c16++; + for (i = 0; i < sizeof(vendor) - 1 && c16[i]; ++i) + vendor[i] = c16[i]; vendor[i] = '\0'; - } else + early_memunmap(c16, sizeof(vendor) * sizeof(efi_char16_t)); + } else { pr_err("Could not map the firmware vendor!\n"); - early_memunmap(tmp, 2); + } pr_info("EFI v%u.%.02u by %s\n", efi.systab->hdr.revision >> 16, @@ -929,16 +930,14 @@ static void __init __efi_enter_virtual_mode(void) if (efi_alloc_page_tables()) { pr_err("Failed to allocate EFI page tables\n"); - clear_bit(EFI_RUNTIME_SERVICES, &efi.flags); - return; + goto err; } efi_merge_regions(); new_memmap = efi_map_regions(&count, &pg_shift); if (!new_memmap) { pr_err("Error reallocating memory, EFI runtime non-functional!\n"); - clear_bit(EFI_RUNTIME_SERVICES, &efi.flags); - return; + goto err; } pa = __pa(new_memmap); @@ -952,8 +951,7 @@ static void __init __efi_enter_virtual_mode(void) if (efi_memmap_init_late(pa, efi.memmap.desc_size * count)) { pr_err("Failed to remap late EFI memory map\n"); - clear_bit(EFI_RUNTIME_SERVICES, &efi.flags); - return; + goto err; } if (efi_enabled(EFI_DBG)) { @@ -961,12 +959,11 @@ static void __init __efi_enter_virtual_mode(void) efi_print_memmap(); } - BUG_ON(!efi.systab); + if (WARN_ON(!efi.systab)) + goto err; - if (efi_setup_page_tables(pa, 1 << pg_shift)) { - clear_bit(EFI_RUNTIME_SERVICES, &efi.flags); - return; - } + if (efi_setup_page_tables(pa, 1 << pg_shift)) + goto err; efi_sync_low_kernel_mappings(); @@ -986,9 +983,9 @@ static void __init __efi_enter_virtual_mode(void) } if (status != EFI_SUCCESS) { - pr_alert("Unable to switch EFI into virtual mode (status=%lx)!\n", - status); - panic("EFI call to SetVirtualAddressMap() failed!"); + pr_err("Unable to switch EFI into virtual mode (status=%lx)!\n", + status); + goto err; } /* @@ -1015,6 +1012,10 @@ static void __init __efi_enter_virtual_mode(void) /* clean DUMMY object */ efi_delete_dummy_variable(); + return; + +err: + clear_bit(EFI_RUNTIME_SERVICES, &efi.flags); } void __init efi_enter_virtual_mode(void) diff --git a/arch/x86/platform/efi/efi_64.c b/arch/x86/platform/efi/efi_64.c index ee5d08f25ce45f21aa81550ce317760a2c745900..2a9a703ef4a07ea2759344e5f5cefc805fb16b8c 100644 --- a/arch/x86/platform/efi/efi_64.c +++ b/arch/x86/platform/efi/efi_64.c @@ -313,7 +313,7 @@ void efi_sync_low_kernel_mappings(void) static inline phys_addr_t virt_to_phys_or_null_size(void *va, unsigned long size) { - bool bad_size; + phys_addr_t pa; if (!va) return 0; @@ -321,16 +321,13 @@ virt_to_phys_or_null_size(void *va, unsigned long size) if (virt_addr_valid(va)) return virt_to_phys(va); - /* - * A fully aligned variable on the stack is guaranteed not to - * cross a page bounary. Try to catch strings on the stack by - * checking that 'size' is a power of two. - */ - bad_size = size > PAGE_SIZE || !is_power_of_2(size); + pa = slow_virt_to_phys(va); - WARN_ON(!IS_ALIGNED((unsigned long)va, size) || bad_size); + /* check if the object crosses a page boundary */ + if (WARN_ON((pa ^ (pa + size - 1)) & PAGE_MASK)) + return 0; - return slow_virt_to_phys(va); + return pa; } #define virt_to_phys_or_null(addr) \ @@ -389,11 +386,12 @@ int __init efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages) return 0; page = alloc_page(GFP_KERNEL|__GFP_DMA32); - if (!page) - panic("Unable to allocate EFI runtime stack < 4GB\n"); + if (!page) { + pr_err("Unable to allocate EFI runtime stack < 4GB\n"); + return 1; + } - efi_scratch.phys_stack = virt_to_phys(page_address(page)); - efi_scratch.phys_stack += PAGE_SIZE; /* stack grows down */ + efi_scratch.phys_stack = page_to_phys(page + 1); /* stack grows down */ npages = (_etext - _text) >> PAGE_SHIFT; text = __pa(_text); @@ -789,6 +787,8 @@ static efi_status_t efi_thunk_get_variable(efi_char16_t *name, efi_guid_t *vendor, u32 *attr, unsigned long *data_size, void *data) { + u8 buf[24] __aligned(8); + efi_guid_t *vnd = PTR_ALIGN((efi_guid_t *)buf, sizeof(*vnd)); efi_status_t status; u32 phys_name, phys_vendor, phys_attr; u32 phys_data_size, phys_data; @@ -796,14 +796,19 @@ efi_thunk_get_variable(efi_char16_t *name, efi_guid_t *vendor, spin_lock_irqsave(&efi_runtime_lock, flags); + *vnd = *vendor; + phys_data_size = virt_to_phys_or_null(data_size); - phys_vendor = virt_to_phys_or_null(vendor); + phys_vendor = virt_to_phys_or_null(vnd); phys_name = virt_to_phys_or_null_size(name, efi_name_size(name)); phys_attr = virt_to_phys_or_null(attr); phys_data = virt_to_phys_or_null_size(data, *data_size); - status = efi_thunk(get_variable, phys_name, phys_vendor, - phys_attr, phys_data_size, phys_data); + if (!phys_name || (data && !phys_data)) + status = EFI_INVALID_PARAMETER; + else + status = efi_thunk(get_variable, phys_name, phys_vendor, + phys_attr, phys_data_size, phys_data); spin_unlock_irqrestore(&efi_runtime_lock, flags); @@ -814,19 +819,25 @@ static efi_status_t efi_thunk_set_variable(efi_char16_t *name, efi_guid_t *vendor, u32 attr, unsigned long data_size, void *data) { + u8 buf[24] __aligned(8); + efi_guid_t *vnd = PTR_ALIGN((efi_guid_t *)buf, sizeof(*vnd)); u32 phys_name, phys_vendor, phys_data; efi_status_t status; unsigned long flags; spin_lock_irqsave(&efi_runtime_lock, flags); + *vnd = *vendor; + phys_name = virt_to_phys_or_null_size(name, efi_name_size(name)); - phys_vendor = virt_to_phys_or_null(vendor); + phys_vendor = virt_to_phys_or_null(vnd); phys_data = virt_to_phys_or_null_size(data, data_size); - /* If data_size is > sizeof(u32) we've got problems */ - status = efi_thunk(set_variable, phys_name, phys_vendor, - attr, data_size, phys_data); + if (!phys_name || !phys_data) + status = EFI_INVALID_PARAMETER; + else + status = efi_thunk(set_variable, phys_name, phys_vendor, + attr, data_size, phys_data); spin_unlock_irqrestore(&efi_runtime_lock, flags); @@ -838,6 +849,8 @@ efi_thunk_set_variable_nonblocking(efi_char16_t *name, efi_guid_t *vendor, u32 attr, unsigned long data_size, void *data) { + u8 buf[24] __aligned(8); + efi_guid_t *vnd = PTR_ALIGN((efi_guid_t *)buf, sizeof(*vnd)); u32 phys_name, phys_vendor, phys_data; efi_status_t status; unsigned long flags; @@ -845,13 +858,17 @@ efi_thunk_set_variable_nonblocking(efi_char16_t *name, efi_guid_t *vendor, if (!spin_trylock_irqsave(&efi_runtime_lock, flags)) return EFI_NOT_READY; + *vnd = *vendor; + phys_name = virt_to_phys_or_null_size(name, efi_name_size(name)); - phys_vendor = virt_to_phys_or_null(vendor); + phys_vendor = virt_to_phys_or_null(vnd); phys_data = virt_to_phys_or_null_size(data, data_size); - /* If data_size is > sizeof(u32) we've got problems */ - status = efi_thunk(set_variable, phys_name, phys_vendor, - attr, data_size, phys_data); + if (!phys_name || !phys_data) + status = EFI_INVALID_PARAMETER; + else + status = efi_thunk(set_variable, phys_name, phys_vendor, + attr, data_size, phys_data); spin_unlock_irqrestore(&efi_runtime_lock, flags); @@ -863,21 +880,29 @@ efi_thunk_get_next_variable(unsigned long *name_size, efi_char16_t *name, efi_guid_t *vendor) { + u8 buf[24] __aligned(8); + efi_guid_t *vnd = PTR_ALIGN((efi_guid_t *)buf, sizeof(*vnd)); efi_status_t status; u32 phys_name_size, phys_name, phys_vendor; unsigned long flags; spin_lock_irqsave(&efi_runtime_lock, flags); + *vnd = *vendor; + phys_name_size = virt_to_phys_or_null(name_size); - phys_vendor = virt_to_phys_or_null(vendor); + phys_vendor = virt_to_phys_or_null(vnd); phys_name = virt_to_phys_or_null_size(name, *name_size); - status = efi_thunk(get_next_variable, phys_name_size, - phys_name, phys_vendor); + if (!phys_name) + status = EFI_INVALID_PARAMETER; + else + status = efi_thunk(get_next_variable, phys_name_size, + phys_name, phys_vendor); spin_unlock_irqrestore(&efi_runtime_lock, flags); + *vendor = *vnd; return status; } diff --git a/arch/x86/tools/relocs.c b/arch/x86/tools/relocs.c index 3a6c8ebc8032eb5c95e70af2e5e4f44000e29350..494a8840b102d4a7b357a8671292e12a579e92a3 100644 --- a/arch/x86/tools/relocs.c +++ b/arch/x86/tools/relocs.c @@ -46,6 +46,7 @@ static const char * const sym_regex_kernel[S_NSYMTYPES] = { "^(xen_irq_disable_direct_reloc$|" "xen_save_fl_direct_reloc$|" "VDSO|" + "__typeid__|" "__crc_)", /* @@ -782,6 +783,12 @@ static int do_reloc64(struct section *sec, Elf_Rel *rel, ElfW(Sym) *sym, add_reloc(&relocs32neg, offset); break; + case R_X86_64_8: + if (!shn_abs || !is_reloc(S_ABS, symname)) + die("Non-whitelisted %s relocation: %s\n", + rel_type(r_type), symname); + break; + case R_X86_64_32: case R_X86_64_32S: case R_X86_64_64: diff --git a/arch/x86/xen/enlighten_pv.c b/arch/x86/xen/enlighten_pv.c index 1730a26ff6abcc2a1f4a682748b88c1ff0de8752..76864ea591605bf285641a2428346329249a42af 100644 --- a/arch/x86/xen/enlighten_pv.c +++ b/arch/x86/xen/enlighten_pv.c @@ -908,14 +908,15 @@ static u64 xen_read_msr_safe(unsigned int msr, int *err) static int xen_write_msr_safe(unsigned int msr, unsigned low, unsigned high) { int ret; +#ifdef CONFIG_X86_64 + unsigned int which; + u64 base; +#endif ret = 0; switch (msr) { #ifdef CONFIG_X86_64 - unsigned which; - u64 base; - case MSR_FS_BASE: which = SEGBASE_FS; goto set; case MSR_KERNEL_GS_BASE: which = SEGBASE_GS_USER; goto set; case MSR_GS_BASE: which = SEGBASE_GS_KERNEL; goto set; diff --git a/block/Kconfig b/block/Kconfig index 1a4929c49f0804b5758e67bf6f2dd701e6f50379..d4f96c78862e1b694860afa890a728433a7f92bf 100644 --- a/block/Kconfig +++ b/block/Kconfig @@ -236,7 +236,7 @@ config BLK_MQ_PCI default y config BLK_MQ_VIRTIO - bool + tristate depends on BLOCK && VIRTIO default y diff --git a/block/blk-crypto-fallback.c b/block/blk-crypto-fallback.c index 945d23dbd0d027b111aaa18cc7786e9198ad1354..195b04b5df0d73dccde79fde41051ec2d2a28ff2 100644 --- a/block/blk-crypto-fallback.c +++ b/block/blk-crypto-fallback.c @@ -614,7 +614,7 @@ int __init blk_crypto_fallback_init(void) crypto_mode_supported[i] = 0xFFFFFFFF; crypto_mode_supported[BLK_ENCRYPTION_MODE_INVALID] = 0; - blk_crypto_ksm = keyslot_manager_create(blk_crypto_num_keyslots, + blk_crypto_ksm = keyslot_manager_create(NULL, blk_crypto_num_keyslots, &blk_crypto_ksm_ll_ops, crypto_mode_supported, NULL); if (!blk_crypto_ksm) diff --git a/block/blk-lib.c b/block/blk-lib.c index 1f196cf0aa5de34715cefde262b9dd1dc53e5751..0dbc9e2ab9a3ff662997b94a3419af0e083a35d8 100644 --- a/block/blk-lib.c +++ b/block/blk-lib.c @@ -52,15 +52,14 @@ int __blkdev_issue_discard(struct block_device *bdev, sector_t sector, if ((sector | nr_sects) & bs_mask) return -EINVAL; - while (nr_sects) { - unsigned int req_sects = nr_sects; - sector_t end_sect; + if (!nr_sects) + return -EINVAL; - if (!req_sects) - goto fail; - req_sects = min(req_sects, bio_allowed_max_sectors(q)); + while (nr_sects) { + sector_t req_sects = min_t(sector_t, nr_sects, + bio_allowed_max_sectors(q)); - end_sect = sector + req_sects; + WARN_ON_ONCE((req_sects << 9) > UINT_MAX); bio = next_bio(bio, 0, gfp_mask); bio->bi_iter.bi_sector = sector; @@ -68,8 +67,8 @@ int __blkdev_issue_discard(struct block_device *bdev, sector_t sector, bio_set_op_attrs(bio, op, 0); bio->bi_iter.bi_size = req_sects << 9; + sector += req_sects; nr_sects -= req_sects; - sector = end_sect; /* * We can loop for a long time in here, if someone does @@ -82,14 +81,6 @@ int __blkdev_issue_discard(struct block_device *bdev, sector_t sector, *biop = bio; return 0; - -fail: - if (bio) { - submit_bio_wait(bio); - bio_put(bio); - } - *biop = NULL; - return -EOPNOTSUPP; } EXPORT_SYMBOL(__blkdev_issue_discard); diff --git a/block/blk-merge.c b/block/blk-merge.c index ac7ff16f462e16ba1fcb58ce80faed30103d85b0..c534d44b6f1723c58a8a84f59f9dc4f6d509edcb 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c @@ -309,13 +309,7 @@ void blk_recalc_rq_segments(struct request *rq) void blk_recount_segments(struct request_queue *q, struct bio *bio) { - unsigned short seg_cnt; - - /* estimate segment number by bi_vcnt for non-cloned bio */ - if (bio_flagged(bio, BIO_CLONED)) - seg_cnt = bio_segments(bio); - else - seg_cnt = bio->bi_vcnt; + unsigned short seg_cnt = bio_segments(bio); if (test_bit(QUEUE_FLAG_NO_SG_MERGE, &q->queue_flags) && (seg_cnt < queue_max_segments(q))) diff --git a/block/blk-mq-virtio.c b/block/blk-mq-virtio.c index c3afbca1129956348389c3e6d26b1402c03fd6c3..a111b065eca0e42cd582ac7690ce75ebdae5f454 100644 --- a/block/blk-mq-virtio.c +++ b/block/blk-mq-virtio.c @@ -52,3 +52,6 @@ int blk_mq_virtio_map_queues(struct blk_mq_tag_set *set, return blk_mq_map_queues(set); } EXPORT_SYMBOL_GPL(blk_mq_virtio_map_queues); + +MODULE_DESCRIPTION("Virtio Device Default Queue Mapping"); +MODULE_LICENSE("GPL v2"); diff --git a/block/blk-settings.c b/block/blk-settings.c index ffd459969689df0821bf377d1c433f039d502de1..be9b39caadbd23bdd284ab8f035bb19ba2620391 100644 --- a/block/blk-settings.c +++ b/block/blk-settings.c @@ -379,7 +379,7 @@ EXPORT_SYMBOL(blk_queue_max_segment_size); * storage device can address. The default of 512 covers most * hardware. **/ -void blk_queue_logical_block_size(struct request_queue *q, unsigned short size) +void blk_queue_logical_block_size(struct request_queue *q, unsigned int size) { q->limits.logical_block_size = size; diff --git a/block/keyslot-manager.c b/block/keyslot-manager.c index 14364261dab5606340399ca1b2686e97f68a39c5..6b563c8602d0d4b0e6d899748e77bfc95df21b20 100644 --- a/block/keyslot-manager.c +++ b/block/keyslot-manager.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -45,6 +46,11 @@ struct keyslot_manager { unsigned int crypto_mode_supported[BLK_ENCRYPTION_MODE_MAX]; void *ll_priv_data; +#ifdef CONFIG_PM + /* Device for runtime power management (NULL if none) */ + struct device *dev; +#endif + /* Protects programming and evicting keys from the device */ struct rw_semaphore lock; @@ -71,8 +77,60 @@ static inline bool keyslot_manager_is_passthrough(struct keyslot_manager *ksm) return ksm->num_slots == 0; } +#ifdef CONFIG_PM +static inline void keyslot_manager_set_dev(struct keyslot_manager *ksm, + struct device *dev) +{ + ksm->dev = dev; +} + +/* If there's an underlying device and it's suspended, resume it. */ +static inline void keyslot_manager_pm_get(struct keyslot_manager *ksm) +{ + if (ksm->dev) + pm_runtime_get_sync(ksm->dev); +} + +static inline void keyslot_manager_pm_put(struct keyslot_manager *ksm) +{ + if (ksm->dev) + pm_runtime_put_sync(ksm->dev); +} +#else /* CONFIG_PM */ +static inline void keyslot_manager_set_dev(struct keyslot_manager *ksm, + struct device *dev) +{ +} + +static inline void keyslot_manager_pm_get(struct keyslot_manager *ksm) +{ +} + +static inline void keyslot_manager_pm_put(struct keyslot_manager *ksm) +{ +} +#endif /* !CONFIG_PM */ + +static inline void keyslot_manager_hw_enter(struct keyslot_manager *ksm) +{ + /* + * Calling into the driver requires ksm->lock held and the device + * resumed. But we must resume the device first, since that can acquire + * and release ksm->lock via keyslot_manager_reprogram_all_keys(). + */ + keyslot_manager_pm_get(ksm); + down_write(&ksm->lock); +} + +static inline void keyslot_manager_hw_exit(struct keyslot_manager *ksm) +{ + up_write(&ksm->lock); + keyslot_manager_pm_put(ksm); +} + /** * keyslot_manager_create() - Create a keyslot manager + * @dev: Device for runtime power management (NULL if none) * @num_slots: The number of key slots to manage. * @ksm_ll_ops: The struct keyslot_mgmt_ll_ops for the device that this keyslot * manager will use to perform operations like programming and @@ -92,7 +150,9 @@ static inline bool keyslot_manager_is_passthrough(struct keyslot_manager *ksm) * Context: May sleep * Return: Pointer to constructed keyslot manager or NULL on error. */ -struct keyslot_manager *keyslot_manager_create(unsigned int num_slots, +struct keyslot_manager *keyslot_manager_create( + struct device *dev, + unsigned int num_slots, const struct keyslot_mgmt_ll_ops *ksm_ll_ops, const unsigned int crypto_mode_supported[BLK_ENCRYPTION_MODE_MAX], void *ll_priv_data) @@ -118,6 +178,7 @@ struct keyslot_manager *keyslot_manager_create(unsigned int num_slots, memcpy(ksm->crypto_mode_supported, crypto_mode_supported, sizeof(ksm->crypto_mode_supported)); ksm->ll_priv_data = ll_priv_data; + keyslot_manager_set_dev(ksm, dev); init_rwsem(&ksm->lock); @@ -226,10 +287,10 @@ int keyslot_manager_get_slot_for_key(struct keyslot_manager *ksm, return slot; for (;;) { - down_write(&ksm->lock); + keyslot_manager_hw_enter(ksm); slot = find_and_grab_keyslot(ksm, key); if (slot != -ENOKEY) { - up_write(&ksm->lock); + keyslot_manager_hw_exit(ksm); return slot; } @@ -240,7 +301,7 @@ int keyslot_manager_get_slot_for_key(struct keyslot_manager *ksm, if (!list_empty(&ksm->idle_slots)) break; - up_write(&ksm->lock); + keyslot_manager_hw_exit(ksm); wait_event(ksm->idle_slots_wait_queue, !list_empty(&ksm->idle_slots)); } @@ -252,7 +313,7 @@ int keyslot_manager_get_slot_for_key(struct keyslot_manager *ksm, err = ksm->ksm_ll_ops.keyslot_program(ksm, key, slot); if (err) { wake_up(&ksm->idle_slots_wait_queue); - up_write(&ksm->lock); + keyslot_manager_hw_exit(ksm); return err; } @@ -266,7 +327,7 @@ int keyslot_manager_get_slot_for_key(struct keyslot_manager *ksm, remove_slot_from_lru_list(ksm, slot); - up_write(&ksm->lock); + keyslot_manager_hw_exit(ksm); return slot; } @@ -368,15 +429,16 @@ int keyslot_manager_evict_key(struct keyslot_manager *ksm, if (keyslot_manager_is_passthrough(ksm)) { if (ksm->ksm_ll_ops.keyslot_evict) { - down_write(&ksm->lock); + keyslot_manager_hw_enter(ksm); err = ksm->ksm_ll_ops.keyslot_evict(ksm, key, -1); - up_write(&ksm->lock); + keyslot_manager_hw_exit(ksm); return err; } return 0; } - down_write(&ksm->lock); + keyslot_manager_hw_enter(ksm); + slot = find_keyslot(ksm, key); if (slot < 0) { err = slot; @@ -396,7 +458,7 @@ int keyslot_manager_evict_key(struct keyslot_manager *ksm, memzero_explicit(&slotp->key, sizeof(slotp->key)); err = 0; out_unlock: - up_write(&ksm->lock); + keyslot_manager_hw_exit(ksm); return err; } @@ -416,6 +478,7 @@ void keyslot_manager_reprogram_all_keys(struct keyslot_manager *ksm) if (WARN_ON(keyslot_manager_is_passthrough(ksm))) return; + /* This is for device initialization, so don't resume the device */ down_write(&ksm->lock); for (slot = 0; slot < ksm->num_slots; slot++) { const struct keyslot *slotp = &ksm->slots[slot]; @@ -455,6 +518,7 @@ EXPORT_SYMBOL_GPL(keyslot_manager_destroy); /** * keyslot_manager_create_passthrough() - Create a passthrough keyslot manager + * @dev: Device for runtime power management (NULL if none) * @ksm_ll_ops: The struct keyslot_mgmt_ll_ops * @crypto_mode_supported: Bitmasks for supported encryption modes * @ll_priv_data: Private data passed as is to the functions in ksm_ll_ops. @@ -471,6 +535,7 @@ EXPORT_SYMBOL_GPL(keyslot_manager_destroy); * Return: Pointer to constructed keyslot manager or NULL on error. */ struct keyslot_manager *keyslot_manager_create_passthrough( + struct device *dev, const struct keyslot_mgmt_ll_ops *ksm_ll_ops, const unsigned int crypto_mode_supported[BLK_ENCRYPTION_MODE_MAX], void *ll_priv_data) @@ -485,6 +550,7 @@ struct keyslot_manager *keyslot_manager_create_passthrough( memcpy(ksm->crypto_mode_supported, crypto_mode_supported, sizeof(ksm->crypto_mode_supported)); ksm->ll_priv_data = ll_priv_data; + keyslot_manager_set_dev(ksm, dev); init_rwsem(&ksm->lock); @@ -544,15 +610,15 @@ int keyslot_manager_derive_raw_secret(struct keyslot_manager *ksm, { int err; - down_write(&ksm->lock); if (ksm->ksm_ll_ops.derive_raw_secret) { + keyslot_manager_hw_enter(ksm); err = ksm->ksm_ll_ops.derive_raw_secret(ksm, wrapped_key, wrapped_key_size, secret, secret_size); + keyslot_manager_hw_exit(ksm); } else { err = -EOPNOTSUPP; } - up_write(&ksm->lock); return err; } diff --git a/build.config.common b/build.config.common index d75e5659708dea8c4bbbe2ff97938f224d9ff1ff..82a971288e450582c2419708cebb09059c47fb8a 100644 --- a/build.config.common +++ b/build.config.common @@ -5,7 +5,7 @@ CC=clang LD=ld.lld NM=llvm-nm OBJCOPY=llvm-objcopy -CLANG_PREBUILT_BIN=prebuilts-master/clang/host/linux-x86/clang-r370808/bin +CLANG_PREBUILT_BIN=prebuilts-master/clang/host/linux-x86/clang-r377782b/bin EXTRA_CMDS='' STOP_SHIP_TRACEPRINTK=1 diff --git a/build.config.cuttlefish.aarch64 b/build.config.cuttlefish.aarch64 new file mode 100644 index 0000000000000000000000000000000000000000..9a685095e934de18c93f8d19b86f534780b73adf --- /dev/null +++ b/build.config.cuttlefish.aarch64 @@ -0,0 +1,11 @@ +. ${ROOT_DIR}/common/build.config.common +. ${ROOT_DIR}/common/build.config.aarch64 +. ${ROOT_DIR}/common/build.config.gki + +BUILD_INITRAMFS=1 +DEFCONFIG=cf_aarch_64_gki_defconfig +PRE_DEFCONFIG_CMDS="KCONFIG_CONFIG=${ROOT_DIR}/common/arch/arm64/configs/${DEFCONFIG} ${ROOT_DIR}/common/scripts/kconfig/merge_config.sh -m -r ${ROOT_DIR}/common/arch/arm64/configs/gki_defconfig ${ROOT_DIR}/common/cuttlefish.fragment" +POST_DEFCONFIG_CMDS="rm ${ROOT_DIR}/common/arch/arm64/configs/${DEFCONFIG}" + +# Not saving any kernel images. This build step is meant purely to generate the .kos. +FILES="" diff --git a/build.config.cuttlefish.x86_64 b/build.config.cuttlefish.x86_64 new file mode 100644 index 0000000000000000000000000000000000000000..d6ce5c3a7306cd46b031027507128929fc3a6921 --- /dev/null +++ b/build.config.cuttlefish.x86_64 @@ -0,0 +1,11 @@ +. ${ROOT_DIR}/common/build.config.common +. ${ROOT_DIR}/common/build.config.x86_64 +. ${ROOT_DIR}/common/build.config.gki + +BUILD_INITRAMFS=1 +DEFCONFIG=cf_x86_64_gki_defconfig +PRE_DEFCONFIG_CMDS="KCONFIG_CONFIG=${ROOT_DIR}/common/arch/x86/configs/${DEFCONFIG} ${ROOT_DIR}/common/scripts/kconfig/merge_config.sh -m -r ${ROOT_DIR}/common/arch/x86/configs/gki_defconfig ${ROOT_DIR}/common/cuttlefish.fragment" +POST_DEFCONFIG_CMDS="rm ${ROOT_DIR}/common/arch/x86/configs/${DEFCONFIG}" + +# Not saving any kernel images. This build step is meant purely to generate the .kos. +FILES="" diff --git a/build.config.gki b/build.config.gki index 66f6eb8baadbd0644db737e914149f22d9a56ac8..44d4ed1bee1fdc292d5925b3e3ef2ab5de5cd433 100644 --- a/build.config.gki +++ b/build.config.gki @@ -1,4 +1,3 @@ DEFCONFIG=gki_defconfig POST_DEFCONFIG_CMDS="check_defconfig" -BUILD_INITRAMFS=1 diff --git a/build.config.gki.aarch64 b/build.config.gki.aarch64 index dade4408295ea58ceac8ae36f0d55988ef5d3673..03a98c665fc982cdd671ce67adacb4141ee7b088 100644 --- a/build.config.gki.aarch64 +++ b/build.config.gki.aarch64 @@ -4,3 +4,8 @@ ABI_DEFINITION=abi_gki_aarch64.xml KMI_WHITELIST=abi_gki_aarch64_whitelist +ADDITIONAL_KMI_WHITELISTS=" +abi_gki_aarch64_cuttlefish_whitelist +abi_gki_aarch64_qcom_whitelist +" +TRIM_NONLISTED_KMI=1 diff --git a/crypto/af_alg.c b/crypto/af_alg.c index ed643ce12278962a3336cf09922064cd99139d5d..4fc8e6a7abb2e9a87447ba247beb56c12c680a81 100644 --- a/crypto/af_alg.c +++ b/crypto/af_alg.c @@ -139,11 +139,13 @@ void af_alg_release_parent(struct sock *sk) sk = ask->parent; ask = alg_sk(sk); - lock_sock(sk); + local_bh_disable(); + bh_lock_sock(sk); ask->nokey_refcnt -= nokey; if (!last) last = !--ask->refcnt; - release_sock(sk); + bh_unlock_sock(sk); + local_bh_enable(); if (last) sock_put(sk); diff --git a/crypto/algapi.c b/crypto/algapi.c index 496fc51bf215c230b201658e7411942822a2b474..c95abdcad84b7a78985874c4ac2d68a85ce56245 100644 --- a/crypto/algapi.c +++ b/crypto/algapi.c @@ -654,11 +654,9 @@ EXPORT_SYMBOL_GPL(crypto_grab_spawn); void crypto_drop_spawn(struct crypto_spawn *spawn) { - if (!spawn->alg) - return; - down_write(&crypto_alg_sem); - list_del(&spawn->list); + if (spawn->alg) + list_del(&spawn->list); up_write(&crypto_alg_sem); } EXPORT_SYMBOL_GPL(crypto_drop_spawn); @@ -666,22 +664,16 @@ EXPORT_SYMBOL_GPL(crypto_drop_spawn); static struct crypto_alg *crypto_spawn_alg(struct crypto_spawn *spawn) { struct crypto_alg *alg; - struct crypto_alg *alg2; down_read(&crypto_alg_sem); alg = spawn->alg; - alg2 = alg; - if (alg2) - alg2 = crypto_mod_get(alg2); - up_read(&crypto_alg_sem); - - if (!alg2) { - if (alg) - crypto_shoot_alg(alg); - return ERR_PTR(-EAGAIN); + if (alg && !crypto_mod_get(alg)) { + alg->cra_flags |= CRYPTO_ALG_DYING; + alg = NULL; } + up_read(&crypto_alg_sem); - return alg; + return alg ?: ERR_PTR(-EAGAIN); } struct crypto_tfm *crypto_spawn_tfm(struct crypto_spawn *spawn, u32 type, diff --git a/crypto/api.c b/crypto/api.c index 7aca9f86c5f397f0d0ef021c6bbfcaae09fd2b32..1909195b2c70b32e9931efa35c47521bf24e8b29 100644 --- a/crypto/api.c +++ b/crypto/api.c @@ -349,13 +349,12 @@ static unsigned int crypto_ctxsize(struct crypto_alg *alg, u32 type, u32 mask) return len; } -void crypto_shoot_alg(struct crypto_alg *alg) +static void crypto_shoot_alg(struct crypto_alg *alg) { down_write(&crypto_alg_sem); alg->cra_flags |= CRYPTO_ALG_DYING; up_write(&crypto_alg_sem); } -EXPORT_SYMBOL_GPL(crypto_shoot_alg); struct crypto_tfm *__crypto_alloc_tfm(struct crypto_alg *alg, u32 type, u32 mask) diff --git a/crypto/internal.h b/crypto/internal.h index 9a3f39939fba8a3a91444ffcac1caabb9efb138e..f8d6efaffef90f14f6810960a7eaa426069a0b30 100644 --- a/crypto/internal.h +++ b/crypto/internal.h @@ -79,7 +79,6 @@ void crypto_alg_tested(const char *name, int err); void crypto_remove_spawns(struct crypto_alg *alg, struct list_head *list, struct crypto_alg *nalg); void crypto_remove_final(struct list_head *list); -void crypto_shoot_alg(struct crypto_alg *alg); struct crypto_tfm *__crypto_alloc_tfm(struct crypto_alg *alg, u32 type, u32 mask); void *crypto_create_tfm(struct crypto_alg *alg, diff --git a/crypto/pcrypt.c b/crypto/pcrypt.c index f8ec3d4ba4a80f8eefed739d9e8a852865a7ac02..85082574c515402581c072e238fefb6b62bc1de8 100644 --- a/crypto/pcrypt.c +++ b/crypto/pcrypt.c @@ -130,7 +130,6 @@ static void pcrypt_aead_done(struct crypto_async_request *areq, int err) struct padata_priv *padata = pcrypt_request_padata(preq); padata->info = err; - req->base.flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; padata_do_serial(padata); } @@ -394,7 +393,7 @@ static int pcrypt_sysfs_add(struct padata_instance *pinst, const char *name) int ret; pinst->kobj.kset = pcrypt_kset; - ret = kobject_add(&pinst->kobj, NULL, name); + ret = kobject_add(&pinst->kobj, NULL, "%s", name); if (!ret) kobject_uevent(&pinst->kobj, KOBJ_ADD); @@ -505,11 +504,12 @@ static int __init pcrypt_init(void) static void __exit pcrypt_exit(void) { + crypto_unregister_template(&pcrypt_tmpl); + pcrypt_fini_padata(&pencrypt); pcrypt_fini_padata(&pdecrypt); kset_unregister(pcrypt_kset); - crypto_unregister_template(&pcrypt_tmpl); } module_init(pcrypt_init); diff --git a/crypto/tgr192.c b/crypto/tgr192.c index 022d3dd76c3b22c27c16137d1c686dbb0abce97a..f8e1d9f9938f5f847f880a90ffa3f7a95023afcc 100644 --- a/crypto/tgr192.c +++ b/crypto/tgr192.c @@ -25,8 +25,9 @@ #include #include #include -#include #include +#include +#include #define TGR192_DIGEST_SIZE 24 #define TGR160_DIGEST_SIZE 20 @@ -468,10 +469,9 @@ static void tgr192_transform(struct tgr192_ctx *tctx, const u8 * data) u64 a, b, c, aa, bb, cc; u64 x[8]; int i; - const __le64 *ptr = (const __le64 *)data; for (i = 0; i < 8; i++) - x[i] = le64_to_cpu(ptr[i]); + x[i] = get_unaligned_le64(data + i * sizeof(__le64)); /* save */ a = aa = tctx->a; diff --git a/cuttlefish.fragment b/cuttlefish.fragment new file mode 100644 index 0000000000000000000000000000000000000000..b2b17f532f6a0489ea6c76f5144be4e3176dfdc0 --- /dev/null +++ b/cuttlefish.fragment @@ -0,0 +1,24 @@ +CONFIG_CPUFREQ_DUMMY=m +CONFIG_VSOCKETS=m +CONFIG_VIRTIO_VSOCKETS=m +CONFIG_GNSS_CMDLINE_SERIAL=m +CONFIG_VIRTIO_BLK=m +CONFIG_VIRTIO_NET=m +CONFIG_VIRT_WIFI=m +CONFIG_HW_RANDOM_VIRTIO=m +CONFIG_DRM_VIRTIO_GPU=m +CONFIG_SND_INTEL8X0=m +CONFIG_USB_DUMMY_HCD=m +CONFIG_RTC_DRV_TEST=m +CONFIG_VIRTIO_PCI=m +CONFIG_VIRTIO_PMEM=m +CONFIG_VIRTIO_INPUT=m +CONFIG_VIRTIO_MMIO=m +CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y +CONFIG_ION=m +CONFIG_ION_SYSTEM_HEAP=y +CONFIG_TEST_STACKINIT=m +CONFIG_TEST_MEMINIT=m +CONFIG_SDCARD_FS=m +CONFIG_TCG_TPM=m +CONFIG_TCG_VTPM_PROXY=m diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 30ccd94f87d247d16903f9fb3952514d423662ce..ded6c5c17fd73bc855cf3bce7f899d357267ecc3 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -1056,6 +1056,13 @@ static int acpi_lpss_suspend_noirq(struct device *dev) int ret; if (pdata->dev_desc->resume_from_noirq) { + /* + * The driver's ->suspend_late callback will be invoked by + * acpi_lpss_do_suspend_late(), with the assumption that the + * driver really wanted to run that code in ->suspend_noirq, but + * it could not run after acpi_dev_suspend() and the driver + * expected the latter to be called in the "late" phase. + */ ret = acpi_lpss_do_suspend_late(dev); if (ret) return ret; @@ -1086,16 +1093,99 @@ static int acpi_lpss_resume_noirq(struct device *dev) struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); int ret; - ret = acpi_subsys_resume_noirq(dev); + /* Follow acpi_subsys_resume_noirq(). */ + if (dev_pm_may_skip_resume(dev)) + return 0; + + if (dev_pm_smart_suspend_and_suspended(dev)) + pm_runtime_set_active(dev); + + ret = pm_generic_resume_noirq(dev); if (ret) return ret; - if (!dev_pm_may_skip_resume(dev) && pdata->dev_desc->resume_from_noirq) - ret = acpi_lpss_do_resume_early(dev); + if (!pdata->dev_desc->resume_from_noirq) + return 0; - return ret; + /* + * The driver's ->resume_early callback will be invoked by + * acpi_lpss_do_resume_early(), with the assumption that the driver + * really wanted to run that code in ->resume_noirq, but it could not + * run before acpi_dev_resume() and the driver expected the latter to be + * called in the "early" phase. + */ + return acpi_lpss_do_resume_early(dev); +} + +static int acpi_lpss_do_restore_early(struct device *dev) +{ + int ret = acpi_lpss_resume(dev); + + return ret ? ret : pm_generic_restore_early(dev); } +static int acpi_lpss_restore_early(struct device *dev) +{ + struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); + + if (pdata->dev_desc->resume_from_noirq) + return 0; + + return acpi_lpss_do_restore_early(dev); +} + +static int acpi_lpss_restore_noirq(struct device *dev) +{ + struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); + int ret; + + ret = pm_generic_restore_noirq(dev); + if (ret) + return ret; + + if (!pdata->dev_desc->resume_from_noirq) + return 0; + + /* This is analogous to what happens in acpi_lpss_resume_noirq(). */ + return acpi_lpss_do_restore_early(dev); +} + +static int acpi_lpss_do_poweroff_late(struct device *dev) +{ + int ret = pm_generic_poweroff_late(dev); + + return ret ? ret : acpi_lpss_suspend(dev, device_may_wakeup(dev)); +} + +static int acpi_lpss_poweroff_late(struct device *dev) +{ + struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); + + if (dev_pm_smart_suspend_and_suspended(dev)) + return 0; + + if (pdata->dev_desc->resume_from_noirq) + return 0; + + return acpi_lpss_do_poweroff_late(dev); +} + +static int acpi_lpss_poweroff_noirq(struct device *dev) +{ + struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); + + if (dev_pm_smart_suspend_and_suspended(dev)) + return 0; + + if (pdata->dev_desc->resume_from_noirq) { + /* This is analogous to the acpi_lpss_suspend_noirq() case. */ + int ret = acpi_lpss_do_poweroff_late(dev); + if (ret) + return ret; + } + + return pm_generic_poweroff_noirq(dev); +} #endif /* CONFIG_PM_SLEEP */ static int acpi_lpss_runtime_suspend(struct device *dev) @@ -1129,14 +1219,11 @@ static struct dev_pm_domain acpi_lpss_pm_domain = { .resume_noirq = acpi_lpss_resume_noirq, .resume_early = acpi_lpss_resume_early, .freeze = acpi_subsys_freeze, - .freeze_late = acpi_subsys_freeze_late, - .freeze_noirq = acpi_subsys_freeze_noirq, - .thaw_noirq = acpi_subsys_thaw_noirq, - .poweroff = acpi_subsys_suspend, - .poweroff_late = acpi_lpss_suspend_late, - .poweroff_noirq = acpi_lpss_suspend_noirq, - .restore_noirq = acpi_lpss_resume_noirq, - .restore_early = acpi_lpss_resume_early, + .poweroff = acpi_subsys_poweroff, + .poweroff_late = acpi_lpss_poweroff_late, + .poweroff_noirq = acpi_lpss_poweroff_noirq, + .restore_noirq = acpi_lpss_restore_noirq, + .restore_early = acpi_lpss_restore_early, #endif .runtime_suspend = acpi_lpss_runtime_suspend, .runtime_resume = acpi_lpss_runtime_resume, diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c index 2ccfbb61ca89987d67d68da320251ba291a6982b..8fe0960ea572495d927ead4053c783e23a94631b 100644 --- a/drivers/acpi/acpi_memhotplug.c +++ b/drivers/acpi/acpi_memhotplug.c @@ -282,7 +282,7 @@ static void acpi_memory_remove_memory(struct acpi_memory_device *mem_device) nid = memory_add_physaddr_to_nid(info->start_addr); acpi_unbind_memory_blocks(info); - remove_memory(nid, info->start_addr, info->length); + __remove_memory(nid, info->start_addr, info->length); list_del(&info->list); kfree(info); } diff --git a/drivers/acpi/acpi_watchdog.c b/drivers/acpi/acpi_watchdog.c index 95600309ce420a21d32f50f6b8095a6fe335fb78..23cde3d8e8fbbdca91c48f0629a1d599f95accc3 100644 --- a/drivers/acpi/acpi_watchdog.c +++ b/drivers/acpi/acpi_watchdog.c @@ -129,12 +129,11 @@ void __init acpi_watchdog_init(void) gas = &entries[i].register_region; res.start = gas->address; + res.end = res.start + ACPI_ACCESS_BYTE_WIDTH(gas->access_width) - 1; if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { res.flags = IORESOURCE_MEM; - res.end = res.start + ALIGN(gas->access_width, 4) - 1; } else if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO) { res.flags = IORESOURCE_IO; - res.end = res.start + gas->access_width - 1; } else { pr_warn("Unsupported address space: %u\n", gas->space_id); diff --git a/drivers/acpi/acpica/dsfield.c b/drivers/acpi/acpica/dsfield.c index 30fe89545d6ab614a32d8d563e2d335dc25613b2..bcc6a7acc576272763df04453bf780a29fd73c3e 100644 --- a/drivers/acpi/acpica/dsfield.c +++ b/drivers/acpi/acpica/dsfield.c @@ -244,7 +244,7 @@ acpi_ds_create_buffer_field(union acpi_parse_object *op, * FUNCTION: acpi_ds_get_field_names * * PARAMETERS: info - create_field info structure - * ` walk_state - Current method state + * walk_state - Current method state * arg - First parser arg for the field name list * * RETURN: Status diff --git a/drivers/acpi/acpica/dswload.c b/drivers/acpi/acpica/dswload.c index d06c414462822dfb0ceb49691b0643309fa653e6..ba53662f121799a5c1d12945341dcc4eb1357184 100644 --- a/drivers/acpi/acpica/dswload.c +++ b/drivers/acpi/acpica/dswload.c @@ -412,6 +412,27 @@ acpi_status acpi_ds_load1_end_op(struct acpi_walk_state *walk_state) ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, "Op=%p State=%p\n", op, walk_state)); + /* + * Disassembler: handle create field operators here. + * + * create_buffer_field is a deferred op that is typically processed in load + * pass 2. However, disassembly of control method contents walk the parse + * tree with ACPI_PARSE_LOAD_PASS1 and AML_CREATE operators are processed + * in a later walk. This is a problem when there is a control method that + * has the same name as the AML_CREATE object. In this case, any use of the + * name segment will be detected as a method call rather than a reference + * to a buffer field. + * + * This earlier creation during disassembly solves this issue by inserting + * the named object in the ACPI namespace so that references to this name + * would be a name string rather than a method call. + */ + if ((walk_state->parse_flags & ACPI_PARSE_DISASSEMBLE) && + (walk_state->op_info->flags & AML_CREATE)) { + status = acpi_ds_create_buffer_field(op, walk_state); + return_ACPI_STATUS(status); + } + /* We are only interested in opcodes that have an associated name */ if (!(walk_state->op_info->flags & (AML_NAMED | AML_FIELD))) { diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index cb97b6105f5286fa6c11c8bf787a3e1b49baf52c..674a0e92b798f5fffb40b298b4d803c7d7721f9d 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -51,6 +51,8 @@ #define PREFIX "ACPI: " #define ACPI_BATTERY_VALUE_UNKNOWN 0xFFFFFFFF +#define ACPI_BATTERY_CAPACITY_VALID(capacity) \ + ((capacity) != 0 && (capacity) != ACPI_BATTERY_VALUE_UNKNOWN) #define ACPI_BATTERY_DEVICE_NAME "Battery" @@ -205,7 +207,8 @@ static int acpi_battery_is_charged(struct acpi_battery *battery) static bool acpi_battery_is_degraded(struct acpi_battery *battery) { - return battery->full_charge_capacity && battery->design_capacity && + return ACPI_BATTERY_CAPACITY_VALID(battery->full_charge_capacity) && + ACPI_BATTERY_CAPACITY_VALID(battery->design_capacity) && battery->full_charge_capacity < battery->design_capacity; } @@ -227,7 +230,7 @@ static int acpi_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - int ret = 0; + int full_capacity = ACPI_BATTERY_VALUE_UNKNOWN, ret = 0; struct acpi_battery *battery = to_acpi_battery(psy); if (acpi_battery_present(battery)) { @@ -276,14 +279,14 @@ static int acpi_battery_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: - if (battery->design_capacity == ACPI_BATTERY_VALUE_UNKNOWN) + if (!ACPI_BATTERY_CAPACITY_VALID(battery->design_capacity)) ret = -ENODEV; else val->intval = battery->design_capacity * 1000; break; case POWER_SUPPLY_PROP_CHARGE_FULL: case POWER_SUPPLY_PROP_ENERGY_FULL: - if (battery->full_charge_capacity == ACPI_BATTERY_VALUE_UNKNOWN) + if (!ACPI_BATTERY_CAPACITY_VALID(battery->full_charge_capacity)) ret = -ENODEV; else val->intval = battery->full_charge_capacity * 1000; @@ -296,11 +299,17 @@ static int acpi_battery_get_property(struct power_supply *psy, val->intval = battery->capacity_now * 1000; break; case POWER_SUPPLY_PROP_CAPACITY: - if (battery->capacity_now && battery->full_charge_capacity) - val->intval = battery->capacity_now * 100/ - battery->full_charge_capacity; + if (ACPI_BATTERY_CAPACITY_VALID(battery->full_charge_capacity)) + full_capacity = battery->full_charge_capacity; + else if (ACPI_BATTERY_CAPACITY_VALID(battery->design_capacity)) + full_capacity = battery->design_capacity; + + if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN || + full_capacity == ACPI_BATTERY_VALUE_UNKNOWN) + ret = -ENODEV; else - val->intval = 0; + val->intval = battery->capacity_now * 100/ + full_capacity; break; case POWER_SUPPLY_PROP_CAPACITY_LEVEL: if (battery->state & ACPI_BATTERY_STATE_CRITICAL) @@ -346,6 +355,20 @@ static enum power_supply_property charge_battery_props[] = { POWER_SUPPLY_PROP_SERIAL_NUMBER, }; +static enum power_supply_property charge_battery_full_cap_broken_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_SERIAL_NUMBER, +}; + static enum power_supply_property energy_battery_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, @@ -807,20 +830,34 @@ static void __exit battery_hook_exit(void) static int sysfs_add_battery(struct acpi_battery *battery) { struct power_supply_config psy_cfg = { .drv_data = battery, }; + bool full_cap_broken = false; + + if (!ACPI_BATTERY_CAPACITY_VALID(battery->full_charge_capacity) && + !ACPI_BATTERY_CAPACITY_VALID(battery->design_capacity)) + full_cap_broken = true; if (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) { - battery->bat_desc.properties = charge_battery_props; - battery->bat_desc.num_properties = - ARRAY_SIZE(charge_battery_props); - } else if (battery->full_charge_capacity == 0) { - battery->bat_desc.properties = - energy_battery_full_cap_broken_props; - battery->bat_desc.num_properties = - ARRAY_SIZE(energy_battery_full_cap_broken_props); + if (full_cap_broken) { + battery->bat_desc.properties = + charge_battery_full_cap_broken_props; + battery->bat_desc.num_properties = + ARRAY_SIZE(charge_battery_full_cap_broken_props); + } else { + battery->bat_desc.properties = charge_battery_props; + battery->bat_desc.num_properties = + ARRAY_SIZE(charge_battery_props); + } } else { - battery->bat_desc.properties = energy_battery_props; - battery->bat_desc.num_properties = - ARRAY_SIZE(energy_battery_props); + if (full_cap_broken) { + battery->bat_desc.properties = + energy_battery_full_cap_broken_props; + battery->bat_desc.num_properties = + ARRAY_SIZE(energy_battery_full_cap_broken_props); + } else { + battery->bat_desc.properties = energy_battery_props; + battery->bat_desc.num_properties = + ARRAY_SIZE(energy_battery_props); + } } battery->bat_desc.name = acpi_device_bid(battery->device); diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index 870eb5c7516a5b55dbb4fc03f81abec8ae286525..d5c19e25ddf593802402c3613d3699185faec77f 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -102,6 +102,17 @@ static const struct dmi_system_id lid_blacklst[] = { }, .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN, }, + { + /* + * Razer Blade Stealth 13 late 2019, notification of the LID device + * only happens on close, not on open and _LID always returns closed. + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Razer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Razer Blade Stealth 13 Late 2019"), + }, + .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN, + }, {} }; @@ -467,8 +478,11 @@ static int acpi_button_resume(struct device *dev) struct acpi_button *button = acpi_driver_data(device); button->suspended = false; - if (button->type == ACPI_BUTTON_TYPE_LID && button->input->users) + if (button->type == ACPI_BUTTON_TYPE_LID && button->input->users) { + button->last_state = !!acpi_lid_evaluate_state(device); + button->last_time = ktime_get(); acpi_lid_initialize_state(device); + } return 0; } #endif diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index e0927c5fd282188a4ca3d04a874401c0b86c3a8c..19f5f7cc6311ae4d0a08135e6f79982ed8889cff 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -455,7 +455,8 @@ acpi_status acpi_add_pm_notifier(struct acpi_device *adev, struct device *dev, goto out; mutex_lock(&acpi_pm_notifier_lock); - adev->wakeup.ws = wakeup_source_register(dev_name(&adev->dev)); + adev->wakeup.ws = wakeup_source_register(&adev->dev, + dev_name(&adev->dev)); adev->wakeup.context.dev = dev; adev->wakeup.context.func = func; adev->wakeup.flags.notifier_present = true; @@ -1077,7 +1078,7 @@ EXPORT_SYMBOL_GPL(acpi_subsys_suspend_noirq); * acpi_subsys_resume_noirq - Run the device driver's "noirq" resume callback. * @dev: Device to handle. */ -int acpi_subsys_resume_noirq(struct device *dev) +static int acpi_subsys_resume_noirq(struct device *dev) { if (dev_pm_may_skip_resume(dev)) return 0; @@ -1092,7 +1093,6 @@ int acpi_subsys_resume_noirq(struct device *dev) return pm_generic_resume_noirq(dev); } -EXPORT_SYMBOL_GPL(acpi_subsys_resume_noirq); /** * acpi_subsys_resume_early - Resume device using ACPI. @@ -1102,12 +1102,11 @@ EXPORT_SYMBOL_GPL(acpi_subsys_resume_noirq); * generic early resume procedure for it during system transition into the * working state. */ -int acpi_subsys_resume_early(struct device *dev) +static int acpi_subsys_resume_early(struct device *dev) { int ret = acpi_dev_resume(dev); return ret ? ret : pm_generic_resume_early(dev); } -EXPORT_SYMBOL_GPL(acpi_subsys_resume_early); /** * acpi_subsys_freeze - Run the device driver's freeze callback. @@ -1116,65 +1115,81 @@ EXPORT_SYMBOL_GPL(acpi_subsys_resume_early); int acpi_subsys_freeze(struct device *dev) { /* - * This used to be done in acpi_subsys_prepare() for all devices and - * some drivers may depend on it, so do it here. Ideally, however, - * runtime-suspended devices should not be touched during freeze/thaw - * transitions. + * Resume all runtime-suspended devices before creating a snapshot + * image of system memory, because the restore kernel generally cannot + * be expected to always handle them consistently and they need to be + * put into the runtime-active metastate during system resume anyway, + * so it is better to ensure that the state saved in the image will be + * always consistent with that. */ - if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND)) - pm_runtime_resume(dev); + pm_runtime_resume(dev); return pm_generic_freeze(dev); } EXPORT_SYMBOL_GPL(acpi_subsys_freeze); /** - * acpi_subsys_freeze_late - Run the device driver's "late" freeze callback. - * @dev: Device to handle. + * acpi_subsys_restore_early - Restore device using ACPI. + * @dev: Device to restore. */ -int acpi_subsys_freeze_late(struct device *dev) +int acpi_subsys_restore_early(struct device *dev) { + int ret = acpi_dev_resume(dev); + return ret ? ret : pm_generic_restore_early(dev); +} +EXPORT_SYMBOL_GPL(acpi_subsys_restore_early); - if (dev_pm_smart_suspend_and_suspended(dev)) - return 0; +/** + * acpi_subsys_poweroff - Run the device driver's poweroff callback. + * @dev: Device to handle. + * + * Follow PCI and resume devices from runtime suspend before running their + * system poweroff callbacks, unless the driver can cope with runtime-suspended + * devices during system suspend and there are no ACPI-specific reasons for + * resuming them. + */ +int acpi_subsys_poweroff(struct device *dev) +{ + if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) || + acpi_dev_needs_resume(dev, ACPI_COMPANION(dev))) + pm_runtime_resume(dev); - return pm_generic_freeze_late(dev); + return pm_generic_poweroff(dev); } -EXPORT_SYMBOL_GPL(acpi_subsys_freeze_late); +EXPORT_SYMBOL_GPL(acpi_subsys_poweroff); /** - * acpi_subsys_freeze_noirq - Run the device driver's "noirq" freeze callback. + * acpi_subsys_poweroff_late - Run the device driver's poweroff callback. * @dev: Device to handle. + * + * Carry out the generic late poweroff procedure for @dev and use ACPI to put + * it into a low-power state during system transition into a sleep state. */ -int acpi_subsys_freeze_noirq(struct device *dev) +static int acpi_subsys_poweroff_late(struct device *dev) { + int ret; if (dev_pm_smart_suspend_and_suspended(dev)) return 0; - return pm_generic_freeze_noirq(dev); + ret = pm_generic_poweroff_late(dev); + if (ret) + return ret; + + return acpi_dev_suspend(dev, device_may_wakeup(dev)); } -EXPORT_SYMBOL_GPL(acpi_subsys_freeze_noirq); /** - * acpi_subsys_thaw_noirq - Run the device driver's "noirq" thaw callback. - * @dev: Device to handle. + * acpi_subsys_poweroff_noirq - Run the driver's "noirq" poweroff callback. + * @dev: Device to suspend. */ -int acpi_subsys_thaw_noirq(struct device *dev) +static int acpi_subsys_poweroff_noirq(struct device *dev) { - /* - * If the device is in runtime suspend, the "thaw" code may not work - * correctly with it, so skip the driver callback and make the PM core - * skip all of the subsequent "thaw" callbacks for the device. - */ - if (dev_pm_smart_suspend_and_suspended(dev)) { - dev_pm_skip_next_resume_phases(dev); + if (dev_pm_smart_suspend_and_suspended(dev)) return 0; - } - return pm_generic_thaw_noirq(dev); + return pm_generic_poweroff_noirq(dev); } -EXPORT_SYMBOL_GPL(acpi_subsys_thaw_noirq); #endif /* CONFIG_PM_SLEEP */ static struct dev_pm_domain acpi_general_pm_domain = { @@ -1190,14 +1205,10 @@ static struct dev_pm_domain acpi_general_pm_domain = { .resume_noirq = acpi_subsys_resume_noirq, .resume_early = acpi_subsys_resume_early, .freeze = acpi_subsys_freeze, - .freeze_late = acpi_subsys_freeze_late, - .freeze_noirq = acpi_subsys_freeze_noirq, - .thaw_noirq = acpi_subsys_thaw_noirq, - .poweroff = acpi_subsys_suspend, - .poweroff_late = acpi_subsys_suspend_late, - .poweroff_noirq = acpi_subsys_suspend_noirq, - .restore_noirq = acpi_subsys_resume_noirq, - .restore_early = acpi_subsys_resume_early, + .poweroff = acpi_subsys_poweroff, + .poweroff_late = acpi_subsys_poweroff_late, + .poweroff_noirq = acpi_subsys_poweroff_noirq, + .restore_early = acpi_subsys_restore_early, #endif }, }; diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 8340c81b258b7b2c11b83d3b16e67fc9d2312299..ec374db57247d8d22ff073dc4a3788d9bb7e9a37 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -2282,7 +2282,7 @@ static void write_blk_ctl(struct nfit_blk *nfit_blk, unsigned int bw, offset = to_interleave_offset(offset, mmio); writeq(cmd, mmio->addr.base + offset); - nvdimm_flush(nfit_blk->nd_region); + nvdimm_flush(nfit_blk->nd_region, NULL); if (nfit_blk->dimm_flags & NFIT_BLK_DCR_LATCH) readq(mmio->addr.base + offset); @@ -2331,7 +2331,7 @@ static int acpi_nfit_blk_single_io(struct nfit_blk *nfit_blk, } if (rw) - nvdimm_flush(nfit_blk->nd_region); + nvdimm_flush(nfit_blk->nd_region, NULL); rc = read_blk_stat(nfit_blk, lane) ? -EIO : 0; return rc; diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index 43587ac680e472afe6ac5cbb0bbbfa3b0c9f93e7..214c4e2e8ade1db216621f9921e389ca50215df2 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -328,6 +328,11 @@ static const struct dmi_system_id video_detect_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Precision 7510"), }, }, + + /* + * Desktops which falsely report a backlight and which our heuristics + * for this do not catch. + */ { .callback = video_detect_force_none, .ident = "Dell OptiPlex 9020M", @@ -336,6 +341,14 @@ static const struct dmi_system_id video_detect_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 9020M"), }, }, + { + .callback = video_detect_force_none, + .ident = "MSI MS-7721", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "MSI"), + DMI_MATCH(DMI_PRODUCT_NAME, "MS-7721"), + }, + }, { }, }; diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 4cdbd7b8736127a368c848ac3b25652a085e9875..814120f4329f0ab3ee5bfffcf726bdcc854083e0 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -5215,6 +5215,7 @@ static int binder_open(struct inode *nodp, struct file *filp) binder_dev = container_of(filp->private_data, struct binder_device, miscdev); } + refcount_inc(&binder_dev->ref); proc->context = &binder_dev->context; binder_alloc_init(&proc->alloc); @@ -5392,6 +5393,7 @@ static int binder_node_release(struct binder_node *node, int refs) static void binder_deferred_release(struct binder_proc *proc) { struct binder_context *context = proc->context; + struct binder_device *device; struct rb_node *n; int threads, nodes, incoming_refs, outgoing_refs, active_transactions; @@ -5410,6 +5412,12 @@ static void binder_deferred_release(struct binder_proc *proc) context->binder_context_mgr_node = NULL; } mutex_unlock(&context->context_mgr_node_lock); + device = container_of(proc->context, struct binder_device, context); + if (refcount_dec_and_test(&device->ref)) { + kfree(context->name); + kfree(device); + } + proc->context = NULL; binder_inner_proc_lock(proc); /* * Make sure proc stays alive after we @@ -6081,6 +6089,7 @@ static int __init init_binder_device(const char *name) binder_device->miscdev.minor = MISC_DYNAMIC_MINOR; binder_device->miscdev.name = name; + refcount_set(&binder_device->ref, 1); binder_device->context.binder_context_mgr_uid = INVALID_UID; binder_device->context.name = name; mutex_init(&binder_device->context.context_mgr_node_lock); diff --git a/drivers/android/binder_internal.h b/drivers/android/binder_internal.h index bd47f7f72075a9676e84380d7c2f9f875878ebca..8d0bffcc9e271fd649a65a7e0df68eee1cd7bfcd 100644 --- a/drivers/android/binder_internal.h +++ b/drivers/android/binder_internal.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,7 @@ struct binder_device { struct miscdevice miscdev; struct binder_context context; struct inode *binderfs_inode; + refcount_t ref; }; /** diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c index e2580e5316a2f5747f5db96d3cbbd907f7442822..110e41f920c27ec1413195abb1bcc934795171f2 100644 --- a/drivers/android/binderfs.c +++ b/drivers/android/binderfs.c @@ -154,6 +154,7 @@ static int binderfs_binder_device_create(struct inode *ref_inode, if (!name) goto err; + refcount_set(&device->ref, 1); device->binderfs_inode = inode; device->context.binder_context_mgr_uid = INVALID_UID; device->context.name = name; @@ -257,8 +258,10 @@ static void binderfs_evict_inode(struct inode *inode) ida_free(&binderfs_minors, device->miscdev.minor); mutex_unlock(&binderfs_minors_mutex); - kfree(device->context.name); - kfree(device); + if (refcount_dec_and_test(&device->ref)) { + kfree(device->context.name); + kfree(device); + } } /** diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index fa1c5a4429579c6a8396ed075852c2e33c8135ec..bbc8710704e2b0ed9f6ab0bbc76391af4733022d 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -96,6 +96,7 @@ enum board_ids { static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); static void ahci_remove_one(struct pci_dev *dev); +static void ahci_shutdown_one(struct pci_dev *dev); static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline); static int ahci_avn_hardreset(struct ata_link *link, unsigned int *class, @@ -609,6 +610,7 @@ static struct pci_driver ahci_pci_driver = { .id_table = ahci_pci_tbl, .probe = ahci_init_one, .remove = ahci_remove_one, + .shutdown = ahci_shutdown_one, .driver = { .pm = &ahci_pci_pm_ops, }, @@ -1897,6 +1899,11 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) return 0; } +static void ahci_shutdown_one(struct pci_dev *pdev) +{ + ata_pci_shutdown_one(pdev); +} + static void ahci_remove_one(struct pci_dev *pdev) { pm_runtime_get_noresume(&pdev->dev); diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index b5f57c69c48786e7b795b399b8a828a111534ec9..2bdb250a2142c031a720ff327040db2b45ee28f2 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -191,7 +191,6 @@ struct ata_port_operations ahci_pmp_retry_srst_ops = { EXPORT_SYMBOL_GPL(ahci_pmp_retry_srst_ops); static bool ahci_em_messages __read_mostly = true; -EXPORT_SYMBOL_GPL(ahci_em_messages); module_param(ahci_em_messages, bool, 0444); /* add other LED protocol types when they become supported */ MODULE_PARM_DESC(ahci_em_messages, diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index b45b6f7722ce8d10a3471144006c3aa5a1544e5a..75d582ca917ffecdb8299c4d11c2a6ef9c98d428 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -6780,6 +6780,26 @@ void ata_pci_remove_one(struct pci_dev *pdev) ata_host_detach(host); } +void ata_pci_shutdown_one(struct pci_dev *pdev) +{ + struct ata_host *host = pci_get_drvdata(pdev); + int i; + + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap = host->ports[i]; + + ap->pflags |= ATA_PFLAG_FROZEN; + + /* Disable port interrupts */ + if (ap->ops->freeze) + ap->ops->freeze(ap); + + /* Stop the port DMA engines */ + if (ap->ops->port_stop) + ap->ops->port_stop(ap); + } +} + /* move to PCI subsystem */ int pci_test_config_bits(struct pci_dev *pdev, const struct pci_bits *bits) { @@ -7400,6 +7420,7 @@ EXPORT_SYMBOL_GPL(ata_timing_cycle2mode); #ifdef CONFIG_PCI EXPORT_SYMBOL_GPL(pci_test_config_bits); +EXPORT_SYMBOL_GPL(ata_pci_shutdown_one); EXPORT_SYMBOL_GPL(ata_pci_remove_one); #ifdef CONFIG_PM EXPORT_SYMBOL_GPL(ata_pci_device_do_suspend); diff --git a/drivers/atm/eni.c b/drivers/atm/eni.c index 6470e3c4c9907a7566213e60ccd47ba335b08858..7323e9210f4b17b9a24e1ef7ab348afb7d03534c 100644 --- a/drivers/atm/eni.c +++ b/drivers/atm/eni.c @@ -372,7 +372,7 @@ static int do_rx_dma(struct atm_vcc *vcc,struct sk_buff *skb, here = (eni_vcc->descr+skip) & (eni_vcc->words-1); dma[j++] = (here << MID_DMA_COUNT_SHIFT) | (vcc->vci << MID_DMA_VCI_SHIFT) | MID_DT_JK; - j++; + dma[j++] = 0; } here = (eni_vcc->descr+size+skip) & (eni_vcc->words-1); if (!eff) size += skip; @@ -445,7 +445,7 @@ static int do_rx_dma(struct atm_vcc *vcc,struct sk_buff *skb, if (size != eff) { dma[j++] = (here << MID_DMA_COUNT_SHIFT) | (vcc->vci << MID_DMA_VCI_SHIFT) | MID_DT_JK; - j++; + dma[j++] = 0; } if (!j || j > 2*RX_DMA_BUF) { printk(KERN_CRIT DEV_LABEL "!j or j too big!!!\n"); diff --git a/drivers/atm/firestream.c b/drivers/atm/firestream.c index 4e46dc9e41ad01142ffdcfe1c38921df9db0ebda..112b1001c26943e33698a157404c169d1fcdc1f4 100644 --- a/drivers/atm/firestream.c +++ b/drivers/atm/firestream.c @@ -927,6 +927,7 @@ static int fs_open(struct atm_vcc *atm_vcc) } if (!to) { printk ("No more free channels for FS50..\n"); + kfree(vcc); return -EBUSY; } vcc->channo = dev->channo; @@ -937,6 +938,7 @@ static int fs_open(struct atm_vcc *atm_vcc) if (((DO_DIRECTION(rxtp) && dev->atm_vccs[vcc->channo])) || ( DO_DIRECTION(txtp) && test_bit (vcc->channo, dev->tx_inuse))) { printk ("Channel is in use for FS155.\n"); + kfree(vcc); return -EBUSY; } } @@ -950,6 +952,7 @@ static int fs_open(struct atm_vcc *atm_vcc) tc, sizeof (struct fs_transmit_config)); if (!tc) { fs_dprintk (FS_DEBUG_OPEN, "fs: can't alloc transmit_config.\n"); + kfree(vcc); return -ENOMEM; } diff --git a/drivers/atm/fore200e.c b/drivers/atm/fore200e.c index 99a38115b0a8fcdf4fc3037802f050a89b9d8ee1..86aab14872fd01fcea22dc0b1d234c0f25cf7906 100644 --- a/drivers/atm/fore200e.c +++ b/drivers/atm/fore200e.c @@ -1504,12 +1504,14 @@ fore200e_open(struct atm_vcc *vcc) static void fore200e_close(struct atm_vcc* vcc) { - struct fore200e* fore200e = FORE200E_DEV(vcc->dev); struct fore200e_vcc* fore200e_vcc; + struct fore200e* fore200e; struct fore200e_vc_map* vc_map; unsigned long flags; ASSERT(vcc); + fore200e = FORE200E_DEV(vcc->dev); + ASSERT((vcc->vpi >= 0) && (vcc->vpi < 1<vci >= 0) && (vcc->vci < 1<dev); - struct fore200e_vcc* fore200e_vcc = FORE200E_VCC(vcc); + struct fore200e* fore200e; + struct fore200e_vcc* fore200e_vcc; struct fore200e_vc_map* vc_map; - struct host_txq* txq = &fore200e->host_txq; + struct host_txq* txq; struct host_txq_entry* entry; struct tpd* tpd; struct tpd_haddr tpd_haddr; @@ -1570,9 +1572,18 @@ fore200e_send(struct atm_vcc *vcc, struct sk_buff *skb) unsigned char* data; unsigned long flags; - ASSERT(vcc); - ASSERT(fore200e); - ASSERT(fore200e_vcc); + if (!vcc) + return -EINVAL; + + fore200e = FORE200E_DEV(vcc->dev); + fore200e_vcc = FORE200E_VCC(vcc); + + if (!fore200e) + return -EINVAL; + + txq = &fore200e->host_txq; + if (!fore200e_vcc) + return -EINVAL; if (!test_bit(ATM_VF_READY, &vcc->flags)) { DPRINTK(1, "VC %d.%d.%d not ready for tx\n", vcc->itf, vcc->vpi, vcc->vpi); diff --git a/drivers/base/component.c b/drivers/base/component.c index e8d676fad0c95600f426d36796c0ffac270ae98d..7f7c4233cd3142d559d1ea27b5b8eb3bf36e1910 100644 --- a/drivers/base/component.c +++ b/drivers/base/component.c @@ -74,11 +74,11 @@ static int component_devices_show(struct seq_file *s, void *data) seq_printf(s, "%-40s %20s\n", "device name", "status"); seq_puts(s, "-------------------------------------------------------------\n"); for (i = 0; i < match->num; i++) { - struct device *d = (struct device *)match->compare[i].data; + struct component *component = match->compare[i].component; - seq_printf(s, "%-40s %20s\n", dev_name(d), - match->compare[i].component ? - "registered" : "not registered"); + seq_printf(s, "%-40s %20s\n", + component ? dev_name(component->dev) : "(unknown)", + component ? (component->bound ? "bound" : "not bound") : "not registered"); } mutex_unlock(&component_mutex); diff --git a/drivers/base/core.c b/drivers/base/core.c index 5abecfb01fa65118ccee1cc6502a9ca3390f4fe4..52f639a5d1a162921d1322669f365c6f7de7f7f8 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -189,10 +189,14 @@ void device_pm_move_to_tail(struct device *dev) * of the link. If DL_FLAG_PM_RUNTIME is not set, DL_FLAG_RPM_ACTIVE will be * ignored. * - * If the DL_FLAG_AUTOREMOVE_CONSUMER is set, the link will be removed - * automatically when the consumer device driver unbinds from it. - * The combination of both DL_FLAG_AUTOREMOVE_CONSUMER and DL_FLAG_STATELESS - * set is invalid and will cause NULL to be returned. + * If the DL_FLAG_AUTOREMOVE_CONSUMER flag is set, the link will be removed + * automatically when the consumer device driver unbinds from it. Analogously, + * if DL_FLAG_AUTOREMOVE_SUPPLIER is set in @flags, the link will be removed + * automatically when the supplier device driver unbinds from it. + * + * The combination of DL_FLAG_STATELESS and either DL_FLAG_AUTOREMOVE_CONSUMER + * or DL_FLAG_AUTOREMOVE_SUPPLIER set in @flags at the same time is invalid and + * will cause NULL to be returned upfront. * * A side effect of the link creation is re-ordering of dpm_list and the * devices_kset list by moving the consumer device and all devices depending @@ -211,10 +215,17 @@ struct device_link *device_link_add(struct device *consumer, if (!consumer || !supplier || (flags & DL_FLAG_SYNC_STATE_ONLY && flags != DL_FLAG_SYNC_STATE_ONLY) || - ((flags & DL_FLAG_STATELESS) && - (flags & DL_FLAG_AUTOREMOVE_CONSUMER))) + (flags & DL_FLAG_STATELESS && + flags & (DL_FLAG_AUTOREMOVE_CONSUMER | DL_FLAG_AUTOREMOVE_SUPPLIER))) return NULL; + if (flags & DL_FLAG_PM_RUNTIME && flags & DL_FLAG_RPM_ACTIVE) { + if (pm_runtime_get_sync(supplier) < 0) { + pm_runtime_put_noidle(supplier); + return NULL; + } + } + device_links_write_lock(); device_pm_lock(); @@ -232,40 +243,57 @@ struct device_link *device_link_add(struct device *consumer, goto out; } - list_for_each_entry(link, &supplier->links.consumers, s_node) - if (link->consumer == consumer) { - kref_get(&link->kref); - if (link->flags & DL_FLAG_SYNC_STATE_ONLY && - !(flags & DL_FLAG_SYNC_STATE_ONLY)) { - link->flags &= ~DL_FLAG_SYNC_STATE_ONLY; - goto reorder; - } + list_for_each_entry(link, &supplier->links.consumers, s_node) { + if (link->consumer != consumer) + continue; + + /* + * Don't return a stateless link if the caller wants a stateful + * one and vice versa. + */ + if (WARN_ON((flags & DL_FLAG_STATELESS) != (link->flags & DL_FLAG_STATELESS))) { + link = NULL; goto out; } + if (flags & DL_FLAG_AUTOREMOVE_CONSUMER) + link->flags |= DL_FLAG_AUTOREMOVE_CONSUMER; + + if (flags & DL_FLAG_AUTOREMOVE_SUPPLIER) + link->flags |= DL_FLAG_AUTOREMOVE_SUPPLIER; + + if (flags & DL_FLAG_PM_RUNTIME) { + if (!(link->flags & DL_FLAG_PM_RUNTIME)) { + pm_runtime_new_link(consumer); + link->flags |= DL_FLAG_PM_RUNTIME; + } + if (flags & DL_FLAG_RPM_ACTIVE) + refcount_inc(&link->rpm_active); + } + + kref_get(&link->kref); + + if (link->flags & DL_FLAG_SYNC_STATE_ONLY && + !(flags & DL_FLAG_SYNC_STATE_ONLY)) { + link->flags &= ~DL_FLAG_SYNC_STATE_ONLY; + goto reorder; + } + goto out; + } + link = kzalloc(sizeof(*link), GFP_KERNEL); if (!link) goto out; + refcount_set(&link->rpm_active, 1); + if (flags & DL_FLAG_PM_RUNTIME) { - if (flags & DL_FLAG_RPM_ACTIVE) { - if (pm_runtime_get_sync(supplier) < 0) { - pm_runtime_put_noidle(supplier); - kfree(link); - link = NULL; - goto out; - } - link->rpm_active = true; - } + if (flags & DL_FLAG_RPM_ACTIVE) + refcount_inc(&link->rpm_active); + pm_runtime_new_link(consumer); - /* - * If the link is being added by the consumer driver at probe - * time, balance the decrementation of the supplier's runtime PM - * usage counter after consumer probe in driver_probe_device(). - */ - if (consumer->links.status == DL_DEV_PROBING) - pm_runtime_get_noresume(supplier); } + get_device(supplier); link->supplier = supplier; INIT_LIST_HEAD(&link->s_node); @@ -330,6 +358,10 @@ struct device_link *device_link_add(struct device *consumer, out: device_pm_unlock(); device_links_write_unlock(); + + if ((flags & DL_FLAG_PM_RUNTIME && flags & DL_FLAG_RPM_ACTIVE) && !link) + pm_runtime_put(supplier); + return link; } EXPORT_SYMBOL_GPL(device_link_add); @@ -392,14 +424,21 @@ static void device_link_add_missing_supplier_links(void) mutex_lock(&wfs_lock); list_for_each_entry_safe(dev, tmp, &wait_for_suppliers, - links.needs_suppliers) - if (!fwnode_call_int_op(dev->fwnode, add_links, dev)) + links.needs_suppliers) { + int ret = fwnode_call_int_op(dev->fwnode, add_links, dev); + if (!ret) list_del_init(&dev->links.needs_suppliers); + else if (ret != -ENODEV) + dev->links.need_for_probe = false; + } mutex_unlock(&wfs_lock); } static void device_link_free(struct device_link *link) { + while (refcount_dec_not_one(&link->rpm_active)) + pm_runtime_put(link->supplier); + put_device(link->consumer); put_device(link->supplier); kfree(link); @@ -603,25 +642,31 @@ static void __device_links_queue_sync_state(struct device *dev, /** * device_links_flush_sync_list - Call sync_state() on a list of devices * @list: List of devices to call sync_state() on + * @dont_lock_dev: Device for which lock is already held by the caller * * Calls sync_state() on all the devices that have been queued for it. This - * function is used in conjunction with __device_links_queue_sync_state(). + * function is used in conjunction with __device_links_queue_sync_state(). The + * @dont_lock_dev parameter is useful when this function is called from a + * context where a device lock is already held. */ -static void device_links_flush_sync_list(struct list_head *list) +static void device_links_flush_sync_list(struct list_head *list, + struct device *dont_lock_dev) { struct device *dev, *tmp; list_for_each_entry_safe(dev, tmp, list, links.defer_sync) { list_del_init(&dev->links.defer_sync); - device_lock(dev); + if (dev != dont_lock_dev) + device_lock(dev); if (dev->bus->sync_state) dev->bus->sync_state(dev); else if (dev->driver && dev->driver->sync_state) dev->driver->sync_state(dev); - device_unlock(dev); + if (dev != dont_lock_dev) + device_unlock(dev); put_device(dev); } @@ -659,7 +704,7 @@ void device_links_supplier_sync_state_resume(void) out: device_links_write_unlock(); - device_links_flush_sync_list(&sync_list); + device_links_flush_sync_list(&sync_list, NULL); } static int sync_state_resume_initcall(void) @@ -710,6 +755,11 @@ void device_links_driver_bound(struct device *dev) WRITE_ONCE(link->status, DL_STATE_AVAILABLE); } + if (defer_sync_state_count) + __device_links_supplier_defer_sync(dev); + else + __device_links_queue_sync_state(dev, &sync_list); + list_for_each_entry(link, &dev->links.suppliers, c_node) { if (link->flags & DL_FLAG_STATELESS) continue; @@ -728,7 +778,7 @@ void device_links_driver_bound(struct device *dev) device_links_write_unlock(); - device_links_flush_sync_list(&sync_list); + device_links_flush_sync_list(&sync_list, dev); } /** @@ -779,11 +829,11 @@ void device_links_no_driver(struct device *dev) */ void device_links_driver_cleanup(struct device *dev) { - struct device_link *link; + struct device_link *link, *ln; device_links_write_lock(); - list_for_each_entry(link, &dev->links.consumers, s_node) { + list_for_each_entry_safe(link, ln, &dev->links.consumers, s_node) { if (link->flags & DL_FLAG_STATELESS) continue; diff --git a/drivers/base/dd.c b/drivers/base/dd.c index b7e156ffc5906a72d48825b1dce18f3dbc75b176..e7376fc63af5804dd04256e1195a64c7eb714c2d 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -491,7 +491,10 @@ static int really_probe(struct device *dev, struct device_driver *drv) atomic_inc(&probe_count); pr_debug("bus: '%s': %s: probing driver %s with device %s\n", drv->bus->name, __func__, drv->name, dev_name(dev)); - WARN_ON(!list_empty(&dev->devres_head)); + if (!list_empty(&dev->devres_head)) { + dev_crit(dev, "Resources present before probing\n"); + return -EBUSY; + } re_probe: dev->driver = drv; diff --git a/drivers/base/memory.c b/drivers/base/memory.c index b964fe1f6828529ceee71fe28cbcad3ee3360a38..25090f9284f729e877ae9a5afaa1cb83dafd593a 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -39,6 +39,11 @@ static inline int base_memory_block_id(int section_nr) return section_nr / sections_per_block; } +static inline int pfn_to_block_id(unsigned long pfn) +{ + return base_memory_block_id(pfn_to_section_nr(pfn)); +} + static int memory_subsys_online(struct device *dev); static int memory_subsys_offline(struct device *dev); @@ -230,13 +235,14 @@ static bool pages_correctly_probed(unsigned long start_pfn) * OK to have direct references to sparsemem variables in here. */ static int -memory_block_action(unsigned long phys_index, unsigned long action, int online_type) +memory_block_action(unsigned long start_section_nr, unsigned long action, + int online_type) { unsigned long start_pfn; unsigned long nr_pages = PAGES_PER_SECTION * sections_per_block; int ret; - start_pfn = section_nr_to_pfn(phys_index); + start_pfn = section_nr_to_pfn(start_section_nr); switch (action) { case MEM_ONLINE: @@ -250,7 +256,7 @@ memory_block_action(unsigned long phys_index, unsigned long action, int online_t break; default: WARN(1, KERN_WARNING "%s(%ld, %ld) unknown action: " - "%ld\n", __func__, phys_index, action, action); + "%ld\n", __func__, start_section_nr, action, action); ret = -EINVAL; } @@ -671,10 +677,9 @@ int __weak arch_get_memory_phys_device(unsigned long start_pfn) * A reference for the returned object is held and the reference for the * hinted object is released. */ -struct memory_block *find_memory_block_hinted(struct mem_section *section, - struct memory_block *hint) +static struct memory_block *find_memory_block_by_id(int block_id, + struct memory_block *hint) { - int block_id = base_memory_block_id(__section_nr(section)); struct device *hintdev = hint ? &hint->dev : NULL; struct device *dev; @@ -686,6 +691,14 @@ struct memory_block *find_memory_block_hinted(struct mem_section *section, return to_memory_block(dev); } +struct memory_block *find_memory_block_hinted(struct mem_section *section, + struct memory_block *hint) +{ + int block_id = base_memory_block_id(__section_nr(section)); + + return find_memory_block_by_id(block_id, hint); +} + /* * For now, we have a linear search to go find the appropriate * memory_block corresponding to a particular phys_index. If @@ -743,25 +756,28 @@ int register_memory(struct memory_block *memory) return ret; } -static int init_memory_block(struct memory_block **memory, - struct mem_section *section, unsigned long state) +static int init_memory_block(struct memory_block **memory, int block_id, + unsigned long state) { struct memory_block *mem; unsigned long start_pfn; - int scn_nr; int ret = 0; + mem = find_memory_block_by_id(block_id, NULL); + if (mem) { + put_device(&mem->dev); + return -EEXIST; + } mem = kzalloc(sizeof(*mem), GFP_KERNEL); if (!mem) return -ENOMEM; - scn_nr = __section_nr(section); - mem->start_section_nr = - base_memory_block_id(scn_nr) * sections_per_block; + mem->start_section_nr = block_id * sections_per_block; mem->end_section_nr = mem->start_section_nr + sections_per_block - 1; mem->state = state; start_pfn = section_nr_to_pfn(mem->start_section_nr); mem->phys_device = arch_get_memory_phys_device(start_pfn); + mem->nid = NUMA_NO_NODE; ret = register_memory(mem); @@ -772,102 +788,101 @@ static int init_memory_block(struct memory_block **memory, static int add_memory_block(int base_section_nr) { struct memory_block *mem; - int i, ret, section_count = 0, section_nr; + int i, ret, section_count = 0; for (i = base_section_nr; - (i < base_section_nr + sections_per_block) && i < NR_MEM_SECTIONS; - i++) { - if (!present_section_nr(i)) - continue; - if (section_count == 0) - section_nr = i; - section_count++; - } + i < base_section_nr + sections_per_block; + i++) + if (present_section_nr(i)) + section_count++; if (section_count == 0) return 0; - ret = init_memory_block(&mem, __nr_to_section(section_nr), MEM_ONLINE); + ret = init_memory_block(&mem, base_memory_block_id(base_section_nr), + MEM_ONLINE); if (ret) return ret; mem->section_count = section_count; return 0; } +static void unregister_memory(struct memory_block *memory) +{ + if (WARN_ON_ONCE(memory->dev.bus != &memory_subsys)) + return; + + /* drop the ref. we got via find_memory_block() */ + put_device(&memory->dev); + device_unregister(&memory->dev); +} + /* - * need an interface for the VM to add new memory regions, - * but without onlining it. + * Create memory block devices for the given memory area. Start and size + * have to be aligned to memory block granularity. Memory block devices + * will be initialized as offline. */ -int hotplug_memory_register(int nid, struct mem_section *section) +int create_memory_block_devices(unsigned long start, unsigned long size) { - int ret = 0; + const int start_block_id = pfn_to_block_id(PFN_DOWN(start)); + int end_block_id = pfn_to_block_id(PFN_DOWN(start + size)); struct memory_block *mem; + unsigned long block_id; + int ret = 0; - mutex_lock(&mem_sysfs_mutex); + if (WARN_ON_ONCE(!IS_ALIGNED(start, memory_block_size_bytes()) || + !IS_ALIGNED(size, memory_block_size_bytes()))) + return -EINVAL; - mem = find_memory_block(section); - if (mem) { - mem->section_count++; - put_device(&mem->dev); - } else { - ret = init_memory_block(&mem, section, MEM_OFFLINE); + mutex_lock(&mem_sysfs_mutex); + for (block_id = start_block_id; block_id != end_block_id; block_id++) { + ret = init_memory_block(&mem, block_id, MEM_OFFLINE); if (ret) - goto out; - mem->section_count++; + break; + mem->section_count = sections_per_block; + } + if (ret) { + end_block_id = block_id; + for (block_id = start_block_id; block_id != end_block_id; + block_id++) { + mem = find_memory_block_by_id(block_id, NULL); + if (WARN_ON_ONCE(!mem)) + continue; + mem->section_count = 0; + unregister_memory(mem); + } } - -out: mutex_unlock(&mem_sysfs_mutex); return ret; } -#ifdef CONFIG_MEMORY_HOTREMOVE -static void -unregister_memory(struct memory_block *memory) -{ - BUG_ON(memory->dev.bus != &memory_subsys); - - /* drop the ref. we got in remove_memory_block() */ - put_device(&memory->dev); - device_unregister(&memory->dev); -} - -static int remove_memory_section(unsigned long node_id, - struct mem_section *section, int phys_device) +/* + * Remove memory block devices for the given memory area. Start and size + * have to be aligned to memory block granularity. Memory block devices + * have to be offline. + */ +void remove_memory_block_devices(unsigned long start, unsigned long size) { + const int start_block_id = pfn_to_block_id(PFN_DOWN(start)); + const int end_block_id = pfn_to_block_id(PFN_DOWN(start + size)); struct memory_block *mem; + int block_id; - mutex_lock(&mem_sysfs_mutex); - - /* - * Some users of the memory hotplug do not want/need memblock to - * track all sections. Skip over those. - */ - mem = find_memory_block(section); - if (!mem) - goto out_unlock; - - unregister_mem_sect_under_nodes(mem, __section_nr(section)); + if (WARN_ON_ONCE(!IS_ALIGNED(start, memory_block_size_bytes()) || + !IS_ALIGNED(size, memory_block_size_bytes()))) + return; - mem->section_count--; - if (mem->section_count == 0) + mutex_lock(&mem_sysfs_mutex); + for (block_id = start_block_id; block_id != end_block_id; block_id++) { + mem = find_memory_block_by_id(block_id, NULL); + if (WARN_ON_ONCE(!mem)) + continue; + mem->section_count = 0; + unregister_memory_block_under_nodes(mem); unregister_memory(mem); - else - put_device(&mem->dev); - -out_unlock: + } mutex_unlock(&mem_sysfs_mutex); - return 0; } -int unregister_memory_section(struct mem_section *section) -{ - if (!present_section(section)) - return -EINVAL; - - return remove_memory_section(0, section, 0); -} -#endif /* CONFIG_MEMORY_HOTREMOVE */ - /* return true if the memory block is offlined, otherwise, return false */ bool is_memblock_offlined(struct memory_block *mem) { @@ -936,3 +951,39 @@ int __init memory_dev_init(void) printk(KERN_ERR "%s() failed: %d\n", __func__, ret); return ret; } + +struct for_each_memory_block_cb_data { + walk_memory_blocks_func_t func; + void *arg; +}; + +static int for_each_memory_block_cb(struct device *dev, void *data) +{ + struct memory_block *mem = to_memory_block(dev); + struct for_each_memory_block_cb_data *cb_data = data; + + return cb_data->func(mem, cb_data->arg); +} + +/** + * for_each_memory_block - walk through all present memory blocks + * + * @arg: argument passed to func + * @func: callback for each memory block walked + * + * This function walks through all present memory blocks, calling func on + * each memory block. + * + * In case func() returns an error, walking is aborted and the error is + * returned. + */ +int for_each_memory_block(void *arg, walk_memory_blocks_func_t func) +{ + struct for_each_memory_block_cb_data cb_data = { + .func = func, + .arg = arg, + }; + + return bus_for_each_dev(&memory_subsys, NULL, &cb_data, + for_each_memory_block_cb); +} diff --git a/drivers/base/node.c b/drivers/base/node.c index ce27be2d001d9a983ce3916f4a9d7e4017ad8762..9fe33d3e2d3b856966104387dc366c8ef146b1b4 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -420,8 +420,6 @@ int register_mem_sect_under_node(struct memory_block *mem_blk, void *arg) int ret, nid = *(int *)arg; unsigned long pfn, sect_start_pfn, sect_end_pfn; - mem_blk->nid = nid; - sect_start_pfn = section_nr_to_pfn(mem_blk->start_section_nr); sect_end_pfn = section_nr_to_pfn(mem_blk->end_section_nr); sect_end_pfn += PAGES_PER_SECTION - 1; @@ -450,6 +448,13 @@ int register_mem_sect_under_node(struct memory_block *mem_blk, void *arg) if (page_nid != nid) continue; } + + /* + * If this memory block spans multiple nodes, we only indicate + * the last processed node. + */ + mem_blk->nid = nid; + ret = sysfs_create_link_nowarn(&node_devices[nid]->dev.kobj, &mem_blk->dev.kobj, kobject_name(&mem_blk->dev.kobj)); @@ -464,40 +469,19 @@ int register_mem_sect_under_node(struct memory_block *mem_blk, void *arg) return 0; } -/* unregister memory section under all nodes that it spans */ -int unregister_mem_sect_under_nodes(struct memory_block *mem_blk, - unsigned long phys_index) +/* + * Unregister a memory block device under the node it spans. Memory blocks + * with multiple nodes cannot be offlined and therefore also never be removed. + */ +void unregister_memory_block_under_nodes(struct memory_block *mem_blk) { - NODEMASK_ALLOC(nodemask_t, unlinked_nodes, GFP_KERNEL); - unsigned long pfn, sect_start_pfn, sect_end_pfn; - - if (!mem_blk) { - NODEMASK_FREE(unlinked_nodes); - return -EFAULT; - } - if (!unlinked_nodes) - return -ENOMEM; - nodes_clear(*unlinked_nodes); - - sect_start_pfn = section_nr_to_pfn(phys_index); - sect_end_pfn = sect_start_pfn + PAGES_PER_SECTION - 1; - for (pfn = sect_start_pfn; pfn <= sect_end_pfn; pfn++) { - int nid; + if (mem_blk->nid == NUMA_NO_NODE) + return; - nid = get_nid_for_pfn(pfn); - if (nid < 0) - continue; - if (!node_online(nid)) - continue; - if (node_test_and_set(nid, *unlinked_nodes)) - continue; - sysfs_remove_link(&node_devices[nid]->dev.kobj, - kobject_name(&mem_blk->dev.kobj)); - sysfs_remove_link(&mem_blk->dev.kobj, - kobject_name(&node_devices[nid]->dev.kobj)); - } - NODEMASK_FREE(unlinked_nodes); - return 0; + sysfs_remove_link(&node_devices[mem_blk->nid]->dev.kobj, + kobject_name(&mem_blk->dev.kobj)); + sysfs_remove_link(&mem_blk->dev.kobj, + kobject_name(&node_devices[mem_blk->nid]->dev.kobj)); } int link_mem_sections(int nid, unsigned long start_pfn, unsigned long end_pfn) diff --git a/drivers/base/platform.c b/drivers/base/platform.c index e9be1f56929af334202df5f06fb34e7111081100..d1f901b58f7552eabc24ce366489bb7c136d3b24 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "base.h" #include "power/power.h" @@ -67,7 +68,7 @@ void __weak arch_setup_pdev_archdata(struct platform_device *pdev) struct resource *platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num) { - int i; + u32 i; for (i = 0; i < dev->num_resources; i++) { struct resource *r = &dev->resource[i]; @@ -162,7 +163,7 @@ struct resource *platform_get_resource_byname(struct platform_device *dev, unsigned int type, const char *name) { - int i; + u32 i; for (i = 0; i < dev->num_resources; i++) { struct resource *r = &dev->resource[i]; @@ -359,7 +360,8 @@ EXPORT_SYMBOL_GPL(platform_device_add_properties); */ int platform_device_add(struct platform_device *pdev) { - int i, ret; + u32 i; + int ret; if (!pdev) return -EINVAL; @@ -425,7 +427,7 @@ int platform_device_add(struct platform_device *pdev) pdev->id = PLATFORM_DEVID_AUTO; } - while (--i >= 0) { + while (i--) { struct resource *r = &pdev->resource[i]; if (r->parent) release_resource(r); @@ -446,7 +448,7 @@ EXPORT_SYMBOL_GPL(platform_device_add); */ void platform_device_del(struct platform_device *pdev) { - int i; + u32 i; if (pdev) { device_remove_properties(&pdev->dev); diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile index e1bb691cf8f14632ed40cf8d570578bf42616d91..ec5bb190b9d0c85b83517b6bc3c31cddc35170ac 100644 --- a/drivers/base/power/Makefile +++ b/drivers/base/power/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o qos.o runtime.o wakeirq.o -obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o +obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o wakeup_stats.o obj-$(CONFIG_PM_TRACE_RTC) += trace.o obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o domain_governor.o obj-$(CONFIG_HAVE_CLK) += clock_ops.o diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 7663f1ae9737f307e0043a3ac0dca2306b15658a..957cd9a18a23297e856eb3979b0f3ab5e8881219 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -273,10 +273,38 @@ static void dpm_wait_for_suppliers(struct device *dev, bool async) device_links_read_unlock(idx); } -static void dpm_wait_for_superior(struct device *dev, bool async) +static bool dpm_wait_for_superior(struct device *dev, bool async) { - dpm_wait(dev->parent, async); + struct device *parent; + + /* + * If the device is resumed asynchronously and the parent's callback + * deletes both the device and the parent itself, the parent object may + * be freed while this function is running, so avoid that by reference + * counting the parent once more unless the device has been deleted + * already (in which case return right away). + */ + mutex_lock(&dpm_list_mtx); + + if (!device_pm_initialized(dev)) { + mutex_unlock(&dpm_list_mtx); + return false; + } + + parent = get_device(dev->parent); + + mutex_unlock(&dpm_list_mtx); + + dpm_wait(parent, async); + put_device(parent); + dpm_wait_for_suppliers(dev, async); + + /* + * If the parent's callback has deleted the device, attempting to resume + * it would be invalid, so avoid doing that then. + */ + return device_pm_initialized(dev); } static void dpm_wait_for_consumers(struct device *dev, bool async) @@ -636,7 +664,8 @@ static int device_resume_noirq(struct device *dev, pm_message_t state, bool asyn if (!dev->power.is_noirq_suspended) goto Out; - dpm_wait_for_superior(dev, async); + if (!dpm_wait_for_superior(dev, async)) + goto Out; skip_resume = dev_pm_may_skip_resume(dev); @@ -837,7 +866,8 @@ static int device_resume_early(struct device *dev, pm_message_t state, bool asyn if (!dev->power.is_late_suspended) goto Out; - dpm_wait_for_superior(dev, async); + if (!dpm_wait_for_superior(dev, async)) + goto Out; callback = dpm_subsys_resume_early_cb(dev, state, &info); @@ -957,7 +987,9 @@ static int device_resume(struct device *dev, pm_message_t state, bool async) goto Complete; } - dpm_wait_for_superior(dev, async); + if (!dpm_wait_for_superior(dev, async)) + goto Complete; + dpm_watchdog_set(&wd, dev); device_lock(dev); diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index a4d7b6112a1e0dff224f089402643f14047c8f76..d5008fee9667d6aac474e02b4bb7cbf356efc2fe 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -154,3 +154,21 @@ static inline void device_pm_init(struct device *dev) device_pm_sleep_init(dev); pm_runtime_init(dev); } + +#ifdef CONFIG_PM_SLEEP + +/* drivers/base/power/wakeup_stats.c */ +extern int wakeup_source_sysfs_add(struct device *parent, + struct wakeup_source *ws); +extern void wakeup_source_sysfs_remove(struct wakeup_source *ws); + +extern int pm_wakeup_source_sysfs_add(struct device *parent); + +#else /* !CONFIG_PM_SLEEP */ + +static inline int pm_wakeup_source_sysfs_add(struct device *parent) +{ + return 0; +} + +#endif /* CONFIG_PM_SLEEP */ diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index beb85c31f3fa3b9f997a4f21c72891f40e7535ed..303ce7d54a30685fe20d5aa3344e2ed684c80c3b 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -268,11 +268,8 @@ static int rpm_get_suppliers(struct device *dev) list_for_each_entry_rcu(link, &dev->links.suppliers, c_node) { int retval; - if (!(link->flags & DL_FLAG_PM_RUNTIME)) - continue; - - if (READ_ONCE(link->status) == DL_STATE_SUPPLIER_UNBIND || - link->rpm_active) + if (!(link->flags & DL_FLAG_PM_RUNTIME) || + READ_ONCE(link->status) == DL_STATE_SUPPLIER_UNBIND) continue; retval = pm_runtime_get_sync(link->supplier); @@ -281,7 +278,7 @@ static int rpm_get_suppliers(struct device *dev) pm_runtime_put_noidle(link->supplier); return retval; } - link->rpm_active = true; + refcount_inc(&link->rpm_active); } return 0; } @@ -290,12 +287,13 @@ static void rpm_put_suppliers(struct device *dev) { struct device_link *link; - list_for_each_entry_rcu(link, &dev->links.suppliers, c_node) - if (link->rpm_active && - READ_ONCE(link->status) != DL_STATE_SUPPLIER_UNBIND) { + list_for_each_entry_rcu(link, &dev->links.suppliers, c_node) { + if (READ_ONCE(link->status) == DL_STATE_SUPPLIER_UNBIND) + continue; + + while (refcount_dec_not_one(&link->rpm_active)) pm_runtime_put(link->supplier); - link->rpm_active = false; - } + } } /** @@ -1531,7 +1529,7 @@ void pm_runtime_remove(struct device *dev) * * Check links from this device to any consumers and if any of them have active * runtime PM references to the device, drop the usage counter of the device - * (once per link). + * (as many times as needed). * * Links with the DL_FLAG_STATELESS flag set are ignored. * @@ -1553,10 +1551,8 @@ void pm_runtime_clean_up_links(struct device *dev) if (link->flags & DL_FLAG_STATELESS) continue; - if (link->rpm_active) { + while (refcount_dec_not_one(&link->rpm_active)) pm_runtime_put_noidle(dev); - link->rpm_active = false; - } } device_links_read_unlock(idx); @@ -1574,8 +1570,11 @@ void pm_runtime_get_suppliers(struct device *dev) idx = device_links_read_lock(); list_for_each_entry_rcu(link, &dev->links.suppliers, c_node) - if (link->flags & DL_FLAG_PM_RUNTIME) + if (link->flags & DL_FLAG_PM_RUNTIME) { + link->supplier_preactivated = true; + refcount_inc(&link->rpm_active); pm_runtime_get_sync(link->supplier); + } device_links_read_unlock(idx); } @@ -1592,8 +1591,11 @@ void pm_runtime_put_suppliers(struct device *dev) idx = device_links_read_lock(); list_for_each_entry_rcu(link, &dev->links.suppliers, c_node) - if (link->flags & DL_FLAG_PM_RUNTIME) - pm_runtime_put(link->supplier); + if (link->supplier_preactivated) { + link->supplier_preactivated = false; + if (refcount_dec_not_one(&link->rpm_active)) + pm_runtime_put(link->supplier); + } device_links_read_unlock(idx); } @@ -1607,8 +1609,6 @@ void pm_runtime_new_link(struct device *dev) void pm_runtime_drop_link(struct device *dev) { - rpm_put_suppliers(dev); - spin_lock_irq(&dev->power.lock); WARN_ON(dev->power.links_count == 0); dev->power.links_count--; diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index fc036148e63ce24867de8bd831efdd3daf8537fe..48ddeefd69328b510c0fd785a311faa81177ca85 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include "power.h" @@ -672,8 +673,13 @@ int dpm_sysfs_add(struct device *dev) if (rc) goto err_wakeup; } + rc = pm_wakeup_source_sysfs_add(dev); + if (rc) + goto err_latency; return 0; + err_latency: + sysfs_unmerge_group(&dev->kobj, &pm_qos_latency_tolerance_attr_group); err_wakeup: sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group); err_runtime: diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index 292d984cba8f6af518f2ae3552f9b26d739abfda..5ad1ebf794b2796c6b25d813535c75de87691c33 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -76,22 +76,7 @@ static struct wakeup_source deleted_ws = { .lock = __SPIN_LOCK_UNLOCKED(deleted_ws.lock), }; -/** - * wakeup_source_prepare - Prepare a new wakeup source for initialization. - * @ws: Wakeup source to prepare. - * @name: Pointer to the name of the new wakeup source. - * - * Callers must ensure that the @name string won't be freed when @ws is still in - * use. - */ -void wakeup_source_prepare(struct wakeup_source *ws, const char *name) -{ - if (ws) { - memset(ws, 0, sizeof(*ws)); - ws->name = name; - } -} -EXPORT_SYMBOL_GPL(wakeup_source_prepare); +static DEFINE_IDA(wakeup_ida); /** * wakeup_source_create - Create a struct wakeup_source object. @@ -100,31 +85,33 @@ EXPORT_SYMBOL_GPL(wakeup_source_prepare); struct wakeup_source *wakeup_source_create(const char *name) { struct wakeup_source *ws; + const char *ws_name; + int id; - ws = kmalloc(sizeof(*ws), GFP_KERNEL); + ws = kzalloc(sizeof(*ws), GFP_KERNEL); if (!ws) - return NULL; + goto err_ws; - wakeup_source_prepare(ws, name ? kstrdup_const(name, GFP_KERNEL) : NULL); - return ws; -} -EXPORT_SYMBOL_GPL(wakeup_source_create); + ws_name = kstrdup_const(name, GFP_KERNEL); + if (!ws_name) + goto err_name; + ws->name = ws_name; -/** - * wakeup_source_drop - Prepare a struct wakeup_source object for destruction. - * @ws: Wakeup source to prepare for destruction. - * - * Callers must ensure that __pm_stay_awake() or __pm_wakeup_event() will never - * be run in parallel with this function for the same wakeup source object. - */ -void wakeup_source_drop(struct wakeup_source *ws) -{ - if (!ws) - return; + id = ida_alloc(&wakeup_ida, GFP_KERNEL); + if (id < 0) + goto err_id; + ws->id = id; - __pm_relax(ws); + return ws; + +err_id: + kfree_const(ws->name); +err_name: + kfree(ws); +err_ws: + return NULL; } -EXPORT_SYMBOL_GPL(wakeup_source_drop); +EXPORT_SYMBOL_GPL(wakeup_source_create); /* * Record wakeup_source statistics being deleted into a dummy wakeup_source. @@ -154,6 +141,13 @@ static void wakeup_source_record(struct wakeup_source *ws) spin_unlock_irqrestore(&deleted_ws.lock, flags); } +static void wakeup_source_free(struct wakeup_source *ws) +{ + ida_free(&wakeup_ida, ws->id); + kfree_const(ws->name); + kfree(ws); +} + /** * wakeup_source_destroy - Destroy a struct wakeup_source object. * @ws: Wakeup source to destroy. @@ -165,10 +159,9 @@ void wakeup_source_destroy(struct wakeup_source *ws) if (!ws) return; - wakeup_source_drop(ws); + __pm_relax(ws); wakeup_source_record(ws); - kfree_const(ws->name); - kfree(ws); + wakeup_source_free(ws); } EXPORT_SYMBOL_GPL(wakeup_source_destroy); @@ -220,16 +213,26 @@ EXPORT_SYMBOL_GPL(wakeup_source_remove); /** * wakeup_source_register - Create wakeup source and add it to the list. + * @dev: Device this wakeup source is associated with (or NULL if virtual). * @name: Name of the wakeup source to register. */ -struct wakeup_source *wakeup_source_register(const char *name) +struct wakeup_source *wakeup_source_register(struct device *dev, + const char *name) { struct wakeup_source *ws; + int ret; ws = wakeup_source_create(name); - if (ws) + if (ws) { + if (!dev || device_is_registered(dev)) { + ret = wakeup_source_sysfs_add(dev, ws); + if (ret) { + wakeup_source_free(ws); + return NULL; + } + } wakeup_source_add(ws); - + } return ws; } EXPORT_SYMBOL_GPL(wakeup_source_register); @@ -242,6 +245,9 @@ void wakeup_source_unregister(struct wakeup_source *ws) { if (ws) { wakeup_source_remove(ws); + if (ws->dev) + wakeup_source_sysfs_remove(ws); + wakeup_source_destroy(ws); } } @@ -285,7 +291,7 @@ int device_wakeup_enable(struct device *dev) if (pm_suspend_target_state != PM_SUSPEND_ON) dev_dbg(dev, "Suspicious %s() during system transition!\n", __func__); - ws = wakeup_source_register(dev_name(dev)); + ws = wakeup_source_register(dev, dev_name(dev)); if (!ws) return -ENOMEM; @@ -910,7 +916,7 @@ EXPORT_SYMBOL_GPL(pm_system_wakeup); void pm_system_cancel_wakeup(void) { - atomic_dec(&pm_abort_suspend); + atomic_dec_if_positive(&pm_abort_suspend); } void pm_wakeup_clear(bool reset) diff --git a/drivers/base/power/wakeup_stats.c b/drivers/base/power/wakeup_stats.c new file mode 100644 index 0000000000000000000000000000000000000000..c7734914d9144bc4d2591b137123159a244267f0 --- /dev/null +++ b/drivers/base/power/wakeup_stats.c @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Wakeup statistics in sysfs + * + * Copyright (c) 2019 Linux Foundation + * Copyright (c) 2019 Greg Kroah-Hartman + * Copyright (c) 2019 Google Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "power.h" + +static struct class *wakeup_class; + +#define wakeup_attr(_name) \ +static ssize_t _name##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct wakeup_source *ws = dev_get_drvdata(dev); \ + \ + return sprintf(buf, "%lu\n", ws->_name); \ +} \ +static DEVICE_ATTR_RO(_name) + +wakeup_attr(active_count); +wakeup_attr(event_count); +wakeup_attr(wakeup_count); +wakeup_attr(expire_count); + +static ssize_t active_time_ms_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct wakeup_source *ws = dev_get_drvdata(dev); + ktime_t active_time = + ws->active ? ktime_sub(ktime_get(), ws->last_time) : 0; + + return sprintf(buf, "%lld\n", ktime_to_ms(active_time)); +} +static DEVICE_ATTR_RO(active_time_ms); + +static ssize_t total_time_ms_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct wakeup_source *ws = dev_get_drvdata(dev); + ktime_t active_time; + ktime_t total_time = ws->total_time; + + if (ws->active) { + active_time = ktime_sub(ktime_get(), ws->last_time); + total_time = ktime_add(total_time, active_time); + } + return sprintf(buf, "%lld\n", ktime_to_ms(total_time)); +} +static DEVICE_ATTR_RO(total_time_ms); + +static ssize_t max_time_ms_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct wakeup_source *ws = dev_get_drvdata(dev); + ktime_t active_time; + ktime_t max_time = ws->max_time; + + if (ws->active) { + active_time = ktime_sub(ktime_get(), ws->last_time); + if (active_time > max_time) + max_time = active_time; + } + return sprintf(buf, "%lld\n", ktime_to_ms(max_time)); +} +static DEVICE_ATTR_RO(max_time_ms); + +static ssize_t last_change_ms_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct wakeup_source *ws = dev_get_drvdata(dev); + + return sprintf(buf, "%lld\n", ktime_to_ms(ws->last_time)); +} +static DEVICE_ATTR_RO(last_change_ms); + +static ssize_t name_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct wakeup_source *ws = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", ws->name); +} +static DEVICE_ATTR_RO(name); + +static ssize_t prevent_suspend_time_ms_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct wakeup_source *ws = dev_get_drvdata(dev); + ktime_t prevent_sleep_time = ws->prevent_sleep_time; + + if (ws->active && ws->autosleep_enabled) { + prevent_sleep_time = ktime_add(prevent_sleep_time, + ktime_sub(ktime_get(), ws->start_prevent_time)); + } + return sprintf(buf, "%lld\n", ktime_to_ms(prevent_sleep_time)); +} +static DEVICE_ATTR_RO(prevent_suspend_time_ms); + +static struct attribute *wakeup_source_attrs[] = { + &dev_attr_name.attr, + &dev_attr_active_count.attr, + &dev_attr_event_count.attr, + &dev_attr_wakeup_count.attr, + &dev_attr_expire_count.attr, + &dev_attr_active_time_ms.attr, + &dev_attr_total_time_ms.attr, + &dev_attr_max_time_ms.attr, + &dev_attr_last_change_ms.attr, + &dev_attr_prevent_suspend_time_ms.attr, + NULL, +}; +ATTRIBUTE_GROUPS(wakeup_source); + +static void device_create_release(struct device *dev) +{ + kfree(dev); +} + +static struct device *wakeup_source_device_create(struct device *parent, + struct wakeup_source *ws) +{ + struct device *dev = NULL; + int retval = -ENODEV; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + retval = -ENOMEM; + goto error; + } + + device_initialize(dev); + dev->devt = MKDEV(0, 0); + dev->class = wakeup_class; + dev->parent = parent; + dev->groups = wakeup_source_groups; + dev->release = device_create_release; + dev_set_drvdata(dev, ws); + device_set_pm_not_required(dev); + + retval = kobject_set_name(&dev->kobj, "wakeup%d", ws->id); + if (retval) + goto error; + + retval = device_add(dev); + if (retval) + goto error; + + return dev; + +error: + put_device(dev); + return ERR_PTR(retval); +} + +/** + * wakeup_source_sysfs_add - Add wakeup_source attributes to sysfs. + * @parent: Device given wakeup source is associated with (or NULL if virtual). + * @ws: Wakeup source to be added in sysfs. + */ +int wakeup_source_sysfs_add(struct device *parent, struct wakeup_source *ws) +{ + struct device *dev; + + dev = wakeup_source_device_create(parent, ws); + if (IS_ERR(dev)) + return PTR_ERR(dev); + ws->dev = dev; + + return 0; +} + +/** + * pm_wakeup_source_sysfs_add - Add wakeup_source attributes to sysfs + * for a device if they're missing. + * @parent: Device given wakeup source is associated with + */ +int pm_wakeup_source_sysfs_add(struct device *parent) +{ + if (!parent->power.wakeup || parent->power.wakeup->dev) + return 0; + + return wakeup_source_sysfs_add(parent, parent->power.wakeup); +} + +/** + * wakeup_source_sysfs_remove - Remove wakeup_source attributes from sysfs. + * @ws: Wakeup source to be removed from sysfs. + */ +void wakeup_source_sysfs_remove(struct wakeup_source *ws) +{ + device_unregister(ws->dev); +} + +static int __init wakeup_sources_sysfs_init(void) +{ + wakeup_class = class_create(THIS_MODULE, "wakeup"); + + return PTR_ERR_OR_ZERO(wakeup_class); +} +postcore_initcall(wakeup_sources_sysfs_init); diff --git a/drivers/bcma/driver_pci.c b/drivers/bcma/driver_pci.c index f499a469e66d08d7dfcb50e123c82fc920f32ea9..12b2cc9a3fbe8a1050133582e7f553b599fbcbbf 100644 --- a/drivers/bcma/driver_pci.c +++ b/drivers/bcma/driver_pci.c @@ -78,7 +78,7 @@ static u16 bcma_pcie_mdio_read(struct bcma_drv_pci *pc, u16 device, u8 address) v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF_OLD); } - v = BCMA_CORE_PCI_MDIODATA_START; + v |= BCMA_CORE_PCI_MDIODATA_START; v |= BCMA_CORE_PCI_MDIODATA_READ; v |= BCMA_CORE_PCI_MDIODATA_TA; @@ -121,7 +121,7 @@ static void bcma_pcie_mdio_write(struct bcma_drv_pci *pc, u16 device, v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF_OLD); } - v = BCMA_CORE_PCI_MDIODATA_START; + v |= BCMA_CORE_PCI_MDIODATA_START; v |= BCMA_CORE_PCI_MDIODATA_WRITE; v |= BCMA_CORE_PCI_MDIODATA_TA; v |= data; diff --git a/drivers/block/brd.c b/drivers/block/brd.c index 17defbf4f332c5267b5cc38e94c25e54a5246d6a..02e8fff3f8283eaa7b723a4b55a5303d8c0f9a0a 100644 --- a/drivers/block/brd.c +++ b/drivers/block/brd.c @@ -463,6 +463,25 @@ static struct kobject *brd_probe(dev_t dev, int *part, void *data) return kobj; } +static inline void brd_check_and_reset_par(void) +{ + if (unlikely(!max_part)) + max_part = 1; + + /* + * make sure 'max_part' can be divided exactly by (1U << MINORBITS), + * otherwise, it is possiable to get same dev_t when adding partitions. + */ + if ((1U << MINORBITS) % max_part != 0) + max_part = 1UL << fls(max_part); + + if (max_part > DISK_MAX_PARTS) { + pr_info("brd: max_part can't be larger than %d, reset max_part = %d.\n", + DISK_MAX_PARTS, DISK_MAX_PARTS); + max_part = DISK_MAX_PARTS; + } +} + static int __init brd_init(void) { struct brd_device *brd, *next; @@ -486,8 +505,7 @@ static int __init brd_init(void) if (register_blkdev(RAMDISK_MAJOR, "ramdisk")) return -EIO; - if (unlikely(!max_part)) - max_part = 1; + brd_check_and_reset_par(); for (i = 0; i < rd_nr; i++) { brd = brd_alloc(i); diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c index 19d3598a0e3f52b13f0cf1e1739ef8b20c49f5d9..1bf83a1f7e26efc9f3f8afc7258a1f5cfecbaadb 100644 --- a/drivers/block/drbd/drbd_main.c +++ b/drivers/block/drbd/drbd_main.c @@ -334,6 +334,8 @@ static int drbd_thread_setup(void *arg) thi->name[0], resource->name); + allow_kernel_signal(DRBD_SIGKILL); + allow_kernel_signal(SIGXCPU); restart: retval = thi->function(thi); diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index e71589e244fb24c3b97640c38d6acfd25c712d6e..bf222c4b2f82f3e2220111d23f336850657beeb2 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -852,14 +852,17 @@ static void reset_fdc_info(int mode) /* selects the fdc and drive, and enables the fdc's input/dma. */ static void set_fdc(int drive) { + unsigned int new_fdc = fdc; + if (drive >= 0 && drive < N_DRIVE) { - fdc = FDC(drive); + new_fdc = FDC(drive); current_drive = drive; } - if (fdc != 1 && fdc != 0) { + if (new_fdc >= N_FDC) { pr_info("bad fdc value\n"); return; } + fdc = new_fdc; set_dor(fdc, ~0, 8); #if N_FDC > 1 set_dor(1 - fdc, ~8, 0); diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index b9d321bdaa8a7eb91a0834c0398351f97fc36920..226103af30f05b0de9200a7020751dfe7917420d 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -1216,6 +1216,16 @@ static int nbd_start_device(struct nbd_device *nbd) args = kzalloc(sizeof(*args), GFP_KERNEL); if (!args) { sock_shutdown(nbd); + /* + * If num_connections is m (2 < m), + * and NO.1 ~ NO.n(1 < n < m) kzallocs are successful. + * But NO.(n + 1) failed. We still have n recv threads. + * So, add flush_workqueue here to prevent recv threads + * dropping the last config_refs and trying to destroy + * the workqueue from inside the workqueue. + */ + if (i) + flush_workqueue(nbd->recv_workq); return -ENOMEM; } sk_set_memalloc(config->socks[i]->sock->sk); diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 585378bc988cdd7e30c612537da0bf61e6e528f1..d3ad1b8c133e6f39190a5effdf2c8d984fa6eaae 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -2097,7 +2097,7 @@ static int rbd_img_fill_nodata(struct rbd_img_request *img_req, u64 off, u64 len) { struct ceph_file_extent ex = { off, len }; - union rbd_img_fill_iter dummy; + union rbd_img_fill_iter dummy = {}; struct rbd_img_fill_ctx fctx = { .pos_type = OBJ_REQUEST_NODATA, .pos = &dummy, @@ -2506,6 +2506,7 @@ static bool rbd_obj_handle_write(struct rbd_obj_request *obj_req) ret = rbd_obj_issue_copyup(obj_req, obj_req->xferred); if (ret) { obj_req->result = ret; + obj_req->xferred = 0; return true; } return false; diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index 5830d94178862803802505c1e34409f9ce77c123..9a57af79f330c1210e305553d6c86f2a17b6fc34 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -1113,8 +1113,8 @@ static int xlvbd_alloc_gendisk(blkif_sector_t capacity, if (!VDEV_IS_EXTENDED(info->vdevice)) { err = xen_translate_vdev(info->vdevice, &minor, &offset); if (err) - return err; - nr_parts = PARTS_PER_DISK; + return err; + nr_parts = PARTS_PER_DISK; } else { minor = BLKIF_MINOR_EXT(info->vdevice); nr_parts = PARTS_PER_EXT_DISK; diff --git a/drivers/bluetooth/btfm_slim.c b/drivers/bluetooth/btfm_slim.c index 9ed65d8378484e6c73638bd1a9644c22fde98c13..b4b313238afc153ca25fe09ff5023cf544af9723 100644 --- a/drivers/bluetooth/btfm_slim.c +++ b/drivers/bluetooth/btfm_slim.c @@ -128,9 +128,7 @@ int btfm_slim_enable_ch(struct btfmslim *btfmslim, struct btfmslim_ch *ch, SLIM_PUSH : SLIM_AUTO_ISO; prop.baser = ((rates == 44100) || (rates == 88200)) ? SLIM_RATE_11025HZ : SLIM_RATE_4000HZ; - prop.dataf = ((rates == 48000) || (rates == 44100) || - (rates == 88200) || (rates == 96000)) ? - SLIM_CH_DATAF_NOT_DEFINED : SLIM_CH_DATAF_LPCM_AUDIO; + prop.dataf = SLIM_CH_DATAF_NOT_DEFINED; /* for feedback channel, PCM bit should not be set */ if (btfm_feedback_ch_setting) { diff --git a/drivers/bus/mhi/controllers/mhi_qcom.c b/drivers/bus/mhi/controllers/mhi_qcom.c index 61de2b18b1b91b682cc59ce24c7f310568c8533c..715fa8660cbef606f32835f750b8853300d0f30d 100644 --- a/drivers/bus/mhi/controllers/mhi_qcom.c +++ b/drivers/bus/mhi/controllers/mhi_qcom.c @@ -124,6 +124,7 @@ static int mhi_init_pci_dev(struct mhi_controller *mhi_cntrl) MHI_CNTRL_ERR("Error ioremap region\n"); goto error_ioremap; } + mhi_cntrl->len = len; ret = pci_alloc_irq_vectors(pci_dev, mhi_cntrl->msi_required, mhi_cntrl->msi_required, PCI_IRQ_MSI); diff --git a/drivers/bus/mhi/core/mhi_boot.c b/drivers/bus/mhi/core/mhi_boot.c index e720028c1303380028582bfb1e37d14c1213c663..14c780ed2ce3bfc0e208b88c1d3a981d4de140a3 100644 --- a/drivers/bus/mhi/core/mhi_boot.c +++ b/drivers/bus/mhi/core/mhi_boot.c @@ -428,17 +428,22 @@ static int mhi_fw_load_sbl(struct mhi_controller *mhi_cntrl, } void mhi_free_bhie_table(struct mhi_controller *mhi_cntrl, - struct image_info *image_info) + struct image_info **image_info) { int i; - struct mhi_buf *mhi_buf = image_info->mhi_buf; + struct mhi_buf *mhi_buf = (*image_info)->mhi_buf; - for (i = 0; i < image_info->entries; i++, mhi_buf++) + if (mhi_cntrl->img_pre_alloc) + return; + + for (i = 0; i < (*image_info)->entries; i++, mhi_buf++) mhi_free_contig_coherent(mhi_cntrl, mhi_buf->len, mhi_buf->buf, mhi_buf->dma_addr); - kfree(image_info->mhi_buf); - kfree(image_info); + kfree((*image_info)->mhi_buf); + kfree(*image_info); + + *image_info = NULL; } int mhi_alloc_bhie_table(struct mhi_controller *mhi_cntrl, @@ -452,6 +457,9 @@ int mhi_alloc_bhie_table(struct mhi_controller *mhi_cntrl, struct image_info *img_info; struct mhi_buf *mhi_buf; + if (mhi_cntrl->img_pre_alloc) + return 0; + MHI_CNTRL_LOG("Allocating bytes:%zu seg_size:%zu total_seg:%u\n", alloc_size, seg_size, segments); @@ -670,9 +678,15 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl) return; error_read: - mhi_free_bhie_table(mhi_cntrl, mhi_cntrl->fbc_image); - mhi_cntrl->fbc_image = NULL; + mhi_free_bhie_table(mhi_cntrl, &mhi_cntrl->fbc_image); error_alloc_fw_table: release_firmware(firmware); } + +void mhi_perform_soc_reset(struct mhi_controller *mhi_cntrl) +{ + mhi_cntrl->write_reg(mhi_cntrl, mhi_cntrl->regs, + MHI_SOC_RESET_REQ_OFFSET, + MHI_SOC_RESET_REQ); +} diff --git a/drivers/bus/mhi/core/mhi_init.c b/drivers/bus/mhi/core/mhi_init.c index 32d62856f8c5f15627f9478e45c15fbdce24ca98..6bc6fd48e97e034392e60d7ebd16af21e5f11be4 100644 --- a/drivers/bus/mhi/core/mhi_init.c +++ b/drivers/bus/mhi/core/mhi_init.c @@ -346,6 +346,9 @@ void mhi_destroy_sysfs(struct mhi_controller *mhi_cntrl) } spin_unlock(&mhi_tsync->lock); + if (mhi_tsync->db_response_pending) + complete(&mhi_tsync->db_completion); + kfree(mhi_cntrl->mhi_tsync); mhi_cntrl->mhi_tsync = NULL; mutex_unlock(&mhi_cntrl->tsync_mutex); @@ -520,6 +523,12 @@ static int mhi_init_debugfs_mhi_vote_open(struct inode *inode, struct file *fp) return single_open(fp, mhi_debugfs_mhi_vote_show, inode->i_private); } +static int mhi_init_debugfs_mhi_regdump_open(struct inode *inode, + struct file *fp) +{ + return single_open(fp, mhi_debugfs_mhi_regdump_show, inode->i_private); +} + static const struct file_operations debugfs_state_ops = { .open = mhi_init_debugfs_mhi_states_open, .release = single_release, @@ -544,9 +553,18 @@ static const struct file_operations debugfs_vote_ops = { .read = seq_read, }; +static const struct file_operations debugfs_regdump_ops = { + .open = mhi_init_debugfs_mhi_regdump_open, + .release = single_release, + .read = seq_read, +}; + DEFINE_DEBUGFS_ATTRIBUTE(debugfs_trigger_reset_fops, NULL, mhi_debugfs_trigger_reset, "%llu\n"); +DEFINE_DEBUGFS_ATTRIBUTE(debugfs_trigger_soc_reset_fops, NULL, + mhi_debugfs_trigger_soc_reset, "%llu\n"); + void mhi_init_debugfs(struct mhi_controller *mhi_cntrl) { struct dentry *dentry; @@ -573,6 +591,11 @@ void mhi_init_debugfs(struct mhi_controller *mhi_cntrl) &debugfs_vote_ops); debugfs_create_file_unsafe("reset", 0444, dentry, mhi_cntrl, &debugfs_trigger_reset_fops); + debugfs_create_file_unsafe("regdump", 0444, dentry, mhi_cntrl, + &debugfs_regdump_ops); + debugfs_create_file_unsafe("soc_reset", 0444, dentry, mhi_cntrl, + &debugfs_trigger_soc_reset_fops); + mhi_cntrl->dentry = dentry; } @@ -1770,10 +1793,8 @@ int mhi_prepare_for_power_up(struct mhi_controller *mhi_cntrl) return 0; bhie_error: - if (mhi_cntrl->rddm_image) { - mhi_free_bhie_table(mhi_cntrl, mhi_cntrl->rddm_image); - mhi_cntrl->rddm_image = NULL; - } + if (mhi_cntrl->rddm_image) + mhi_free_bhie_table(mhi_cntrl, &mhi_cntrl->rddm_image); error_dev_ctxt: mutex_unlock(&mhi_cntrl->pm_mutex); @@ -1784,15 +1805,11 @@ EXPORT_SYMBOL(mhi_prepare_for_power_up); void mhi_unprepare_after_power_down(struct mhi_controller *mhi_cntrl) { - if (mhi_cntrl->fbc_image) { - mhi_free_bhie_table(mhi_cntrl, mhi_cntrl->fbc_image); - mhi_cntrl->fbc_image = NULL; - } + if (mhi_cntrl->fbc_image) + mhi_free_bhie_table(mhi_cntrl, &mhi_cntrl->fbc_image); - if (mhi_cntrl->rddm_image) { - mhi_free_bhie_table(mhi_cntrl, mhi_cntrl->rddm_image); - mhi_cntrl->rddm_image = NULL; - } + if (mhi_cntrl->rddm_image) + mhi_free_bhie_table(mhi_cntrl, &mhi_cntrl->rddm_image); mhi_deinit_dev_ctxt(mhi_cntrl); mhi_cntrl->pre_init = false; diff --git a/drivers/bus/mhi/core/mhi_internal.h b/drivers/bus/mhi/core/mhi_internal.h index c2b99e80215858127d96d663f8c0717546313b97..99e7b2366bfaa93783b44cbc95fddf4af696276e 100644 --- a/drivers/bus/mhi/core/mhi_internal.h +++ b/drivers/bus/mhi/core/mhi_internal.h @@ -727,8 +727,10 @@ struct mhi_timesync { void __iomem *time_reg; u32 int_sequence; u64 local_time; + u64 remote_time; bool db_support; bool db_response_pending; + struct completion db_completion; spinlock_t lock; /* list protection */ struct list_head head; }; @@ -749,16 +751,20 @@ struct mhi_bus { /* default MHI timeout */ #define MHI_TIMEOUT_MS (1000) +#define MHI_FORCE_WAKE_DELAY_US (100) + extern struct mhi_bus mhi_bus; struct mhi_controller *find_mhi_controller_by_name(const char *name); /* debug fs related functions */ +int mhi_debugfs_mhi_regdump_show(struct seq_file *m, void *d); int mhi_debugfs_mhi_vote_show(struct seq_file *m, void *d); int mhi_debugfs_mhi_chan_show(struct seq_file *m, void *d); int mhi_debugfs_mhi_event_show(struct seq_file *m, void *d); int mhi_debugfs_mhi_states_show(struct seq_file *m, void *d); int mhi_debugfs_trigger_reset(void *data, u64 val); +int mhi_debugfs_trigger_soc_reset(void *data, u64 val); void mhi_deinit_debugfs(struct mhi_controller *mhi_cntrl); void mhi_init_debugfs(struct mhi_controller *mhi_cntrl); @@ -936,7 +942,7 @@ void mhi_create_devices(struct mhi_controller *mhi_cntrl); int mhi_alloc_bhie_table(struct mhi_controller *mhi_cntrl, struct image_info **image_info, size_t alloc_size); void mhi_free_bhie_table(struct mhi_controller *mhi_cntrl, - struct image_info *image_info); + struct image_info **image_info); int mhi_map_single_no_bb(struct mhi_controller *mhi_cntrl, struct mhi_buf_info *buf_info); @@ -965,6 +971,7 @@ int mhi_prepare_channel(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan); void mhi_reset_reg_write_q(struct mhi_controller *mhi_cntrl); void mhi_force_reg_write(struct mhi_controller *mhi_cntrl); +void mhi_perform_soc_reset(struct mhi_controller *mhi_cntrl); /* isr handlers */ irqreturn_t mhi_msi_handlr(int irq_number, void *dev); diff --git a/drivers/bus/mhi/core/mhi_main.c b/drivers/bus/mhi/core/mhi_main.c index f09c333996a1dec245434727cc7000dcb27aa556..498239f2efc1d6fb743de22a71e3d18f64915542 100644 --- a/drivers/bus/mhi/core/mhi_main.c +++ b/drivers/bus/mhi/core/mhi_main.c @@ -1402,6 +1402,10 @@ int mhi_process_tsync_ev_ring(struct mhi_controller *mhi_cntrl, if (unlikely(mhi_tsync->int_sequence != sequence)) { MHI_ASSERT(1, "Unexpected response:0x%llx Expected:0x%llx\n", sequence, mhi_tsync->int_sequence); + + mhi_device_put(mhi_cntrl->mhi_dev, + MHI_VOTE_DEVICE | MHI_VOTE_BUS); + mutex_unlock(&mhi_cntrl->tsync_mutex); goto exit_tsync_process; } @@ -1427,6 +1431,11 @@ int mhi_process_tsync_ev_ring(struct mhi_controller *mhi_cntrl, } while (true); mhi_tsync->db_response_pending = false; + mhi_tsync->remote_time = remote_time; + complete(&mhi_tsync->db_completion); + + mhi_device_put(mhi_cntrl->mhi_dev, MHI_VOTE_DEVICE | MHI_VOTE_BUS); + mutex_unlock(&mhi_cntrl->tsync_mutex); exit_tsync_process: @@ -1484,13 +1493,10 @@ int mhi_process_bw_scale_ev_ring(struct mhi_controller *mhi_cntrl, read_unlock_bh(&mhi_cntrl->pm_lock); spin_unlock_bh(&mhi_event->lock); - atomic_inc(&mhi_cntrl->pending_pkts); ret = mhi_device_get_sync(mhi_cntrl->mhi_dev, MHI_VOTE_DEVICE | MHI_VOTE_BUS); - if (ret) { - atomic_dec(&mhi_cntrl->pending_pkts); + if (ret) goto exit_bw_scale_process; - } mutex_lock(&mhi_cntrl->pm_mutex); @@ -1508,7 +1514,6 @@ int mhi_process_bw_scale_ev_ring(struct mhi_controller *mhi_cntrl, read_unlock_bh(&mhi_cntrl->pm_lock); mhi_device_put(mhi_cntrl->mhi_dev, MHI_VOTE_DEVICE | MHI_VOTE_BUS); - atomic_dec(&mhi_cntrl->pending_pkts); mutex_unlock(&mhi_cntrl->pm_mutex); @@ -2108,6 +2113,64 @@ static void __mhi_unprepare_channel(struct mhi_controller *mhi_cntrl, mutex_unlock(&mhi_chan->mutex); } +int mhi_debugfs_mhi_regdump_show(struct seq_file *m, void *d) +{ + struct mhi_controller *mhi_cntrl = m->private; + enum mhi_dev_state state; + enum mhi_ee ee; + int i, ret; + u32 val; + void __iomem *mhi_base = mhi_cntrl->regs; + void __iomem *bhi_base = mhi_cntrl->bhi; + void __iomem *bhie_base = mhi_cntrl->bhie; + void __iomem *wake_db = mhi_cntrl->wake_db; + struct { + const char *name; + int offset; + void __iomem *base; + } debug_reg[] = { + { "MHI_CNTRL", MHICTRL, mhi_base}, + { "MHI_STATUS", MHISTATUS, mhi_base}, + { "MHI_WAKE_DB", 0, wake_db}, + { "BHI_EXECENV", BHI_EXECENV, bhi_base}, + { "BHI_STATUS", BHI_STATUS, bhi_base}, + { "BHI_ERRCODE", BHI_ERRCODE, bhi_base}, + { "BHI_ERRDBG1", BHI_ERRDBG1, bhi_base}, + { "BHI_ERRDBG2", BHI_ERRDBG2, bhi_base}, + { "BHI_ERRDBG3", BHI_ERRDBG3, bhi_base}, + { "BHIE_TXVEC_DB", BHIE_TXVECDB_OFFS, bhie_base}, + { "BHIE_TXVEC_STATUS", BHIE_TXVECSTATUS_OFFS, bhie_base}, + { "BHIE_RXVEC_DB", BHIE_RXVECDB_OFFS, bhie_base}, + { "BHIE_RXVEC_STATUS", BHIE_RXVECSTATUS_OFFS, bhie_base}, + { NULL }, + }; + + if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) + return -EIO; + + seq_printf(m, "host pm_state:%s dev_state:%s ee:%s\n", + to_mhi_pm_state_str(mhi_cntrl->pm_state), + TO_MHI_STATE_STR(mhi_cntrl->dev_state), + TO_MHI_EXEC_STR(mhi_cntrl->ee)); + + state = mhi_get_mhi_state(mhi_cntrl); + ee = mhi_get_exec_env(mhi_cntrl); + + seq_printf(m, "device ee:%s dev_state:%s\n", TO_MHI_EXEC_STR(ee), + TO_MHI_STATE_STR(state)); + + for (i = 0; debug_reg[i].name; i++) { + if (!debug_reg[i].base) + continue; + ret = mhi_read_reg(mhi_cntrl, debug_reg[i].base, + debug_reg[i].offset, &val); + seq_printf(m, "reg:%s val:0x%x, ret:%d\n", debug_reg[i].name, + val, ret); + } + + return 0; +} + int mhi_debugfs_mhi_states_show(struct seq_file *m, void *d) { struct mhi_controller *mhi_cntrl = m->private; @@ -2502,13 +2565,37 @@ int mhi_get_remote_time_sync(struct mhi_device *mhi_dev, { struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; struct mhi_timesync *mhi_tsync = mhi_cntrl->mhi_tsync; + u64 local_time; int ret; - mutex_lock(&mhi_cntrl->tsync_mutex); /* not all devices support time features */ - if (!mhi_tsync) { - ret = -EIO; - goto error_unlock; + if (!mhi_tsync) + return -EINVAL; + + if (unlikely(MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))) { + MHI_ERR("MHI is not in active state, pm_state:%s\n", + to_mhi_pm_state_str(mhi_cntrl->pm_state)); + return -EIO; + } + + mutex_lock(&mhi_cntrl->tsync_mutex); + + /* return times from last async request completion */ + if (mhi_tsync->db_response_pending) { + local_time = mhi_tsync->local_time; + mutex_unlock(&mhi_cntrl->tsync_mutex); + + ret = wait_for_completion_timeout(&mhi_tsync->db_completion, + msecs_to_jiffies(mhi_cntrl->timeout_ms)); + if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) || !ret) { + MHI_ERR("Pending DB request did not complete, abort\n"); + return -EAGAIN; + } + + *t_host = local_time; + *t_dev = mhi_tsync->remote_time; + + return 0; } /* bring to M0 state */ @@ -2581,14 +2668,13 @@ int mhi_get_remote_time(struct mhi_device *mhi_dev, int ret = 0; /* not all devices support all time features */ + if (!mhi_tsync || !mhi_tsync->db_support) + return -EINVAL; + mutex_lock(&mhi_cntrl->tsync_mutex); - if (!mhi_tsync || !mhi_tsync->db_support) { - ret = -EIO; - goto error_unlock; - } - /* tsync db can only be rung in M0 state */ - ret = __mhi_device_get_sync(mhi_cntrl); + ret = mhi_device_get_sync(mhi_cntrl->mhi_dev, + MHI_VOTE_DEVICE | MHI_VOTE_BUS); if (ret) goto error_unlock; @@ -2656,21 +2742,21 @@ int mhi_get_remote_time(struct mhi_device *mhi_dev, MHI_VERB("time DB request with seq:0x%llx\n", mhi_tsync->int_sequence); mhi_tsync->db_response_pending = true; + init_completion(&mhi_tsync->db_completion); skip_tsync_db: spin_lock(&mhi_tsync->lock); list_add_tail(&tsync_node->node, &mhi_tsync->head); spin_unlock(&mhi_tsync->lock); - ret = 0; + mutex_unlock(&mhi_cntrl->tsync_mutex); + + return 0; error_invalid_state: - if (ret) - kfree(tsync_node); + kfree(tsync_node); error_no_mem: - read_lock_bh(&mhi_cntrl->pm_lock); - mhi_cntrl->wake_put(mhi_cntrl, false); - read_unlock_bh(&mhi_cntrl->pm_lock); + mhi_device_put(mhi_cntrl->mhi_dev, MHI_VOTE_DEVICE | MHI_VOTE_BUS); error_unlock: mutex_unlock(&mhi_cntrl->tsync_mutex); return ret; @@ -2690,7 +2776,7 @@ void mhi_debug_reg_dump(struct mhi_controller *mhi_cntrl) struct { const char *name; int offset; - void *base; + void __iomem *base; } debug_reg[] = { { "MHI_CNTRL", MHICTRL, mhi_base}, { "MHI_STATUS", MHISTATUS, mhi_base}, @@ -2720,6 +2806,8 @@ void mhi_debug_reg_dump(struct mhi_controller *mhi_cntrl) TO_MHI_STATE_STR(state)); for (i = 0; debug_reg[i].name; i++) { + if (!debug_reg[i].base) + continue; ret = mhi_read_reg(mhi_cntrl, debug_reg[i].base, debug_reg[i].offset, &val); MHI_LOG("reg:%s val:0x%x, ret:%d\n", debug_reg[i].name, val, diff --git a/drivers/bus/mhi/core/mhi_pm.c b/drivers/bus/mhi/core/mhi_pm.c index a4e63bc9456b9b27c89c0ea4e3113ca66a4a5d63..e9a7a5a676e7c1e1d1d5cf9ac5948363b5d283cd 100644 --- a/drivers/bus/mhi/core/mhi_pm.c +++ b/drivers/bus/mhi/core/mhi_pm.c @@ -405,35 +405,42 @@ void mhi_pm_m1_transition(struct mhi_controller *mhi_cntrl) enum MHI_PM_STATE state; write_lock_irq(&mhi_cntrl->pm_lock); + /* Just check if we are racing with device_wake assertion */ + if (atomic_read(&mhi_cntrl->dev_wake)) + MHI_VERB("M2 transition request post dev_wake:%d\n", + atomic_read(&mhi_cntrl->dev_wake)); + /* if it fails, means we transition to M3 */ state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_M2); - if (state == MHI_PM_M2) { - MHI_VERB("Entered M2 State\n"); - mhi_set_mhi_state(mhi_cntrl, MHI_STATE_M2); - mhi_cntrl->dev_state = MHI_STATE_M2; - mhi_cntrl->M2++; - - write_unlock_irq(&mhi_cntrl->pm_lock); - wake_up_all(&mhi_cntrl->state_event); - - /* transfer pending, exit M2 immediately */ - if (unlikely(atomic_read(&mhi_cntrl->pending_pkts) || - atomic_read(&mhi_cntrl->dev_wake))) { - MHI_VERB( - "Exiting M2 Immediately, pending_pkts:%d dev_wake:%d\n", - atomic_read(&mhi_cntrl->pending_pkts), - atomic_read(&mhi_cntrl->dev_wake)); - read_lock_bh(&mhi_cntrl->pm_lock); - mhi_cntrl->wake_get(mhi_cntrl, true); - mhi_cntrl->wake_put(mhi_cntrl, true); - read_unlock_bh(&mhi_cntrl->pm_lock); - } else { - mhi_cntrl->status_cb(mhi_cntrl, mhi_cntrl->priv_data, - MHI_CB_IDLE); - } - } else { + if (state != MHI_PM_M2) { + /* Nothing to be done, handle M3 transition later */ write_unlock_irq(&mhi_cntrl->pm_lock); + return; + } + + MHI_VERB("Entered M2 State\n"); + mhi_set_mhi_state(mhi_cntrl, MHI_STATE_M2); + mhi_cntrl->dev_state = MHI_STATE_M2; + mhi_cntrl->M2++; + + write_unlock_irq(&mhi_cntrl->pm_lock); + wake_up_all(&mhi_cntrl->state_event); + + /* transfer pending, exit M2 immediately */ + if (unlikely(atomic_read(&mhi_cntrl->pending_pkts) || + atomic_read(&mhi_cntrl->dev_wake))) { + MHI_VERB( + "Exiting M2 Immediately, pending_pkts:%d dev_wake:%d\n", + atomic_read(&mhi_cntrl->pending_pkts), + atomic_read(&mhi_cntrl->dev_wake)); + read_lock_bh(&mhi_cntrl->pm_lock); + mhi_cntrl->wake_get(mhi_cntrl, true); + mhi_cntrl->wake_put(mhi_cntrl, true); + read_unlock_bh(&mhi_cntrl->pm_lock); + return; } + + mhi_cntrl->status_cb(mhi_cntrl, mhi_cntrl->priv_data, MHI_CB_IDLE); } int mhi_pm_m3_transition(struct mhi_controller *mhi_cntrl) @@ -716,6 +723,17 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl, mutex_unlock(&mhi_cntrl->pm_mutex); } +int mhi_debugfs_trigger_soc_reset(void *data, u64 val) +{ + struct mhi_controller *mhi_cntrl = data; + + MHI_LOG("Trigger MHI SOC Reset\n"); + + mhi_perform_soc_reset(mhi_cntrl); + + return 0; +} + int mhi_debugfs_trigger_reset(void *data, u64 val) { struct mhi_controller *mhi_cntrl = data; @@ -947,6 +965,12 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl) goto error_bhi_offset; } + if (val >= mhi_cntrl->len) { + write_unlock_irq(&mhi_cntrl->pm_lock); + MHI_ERR("Invalid bhi offset:%x\n", val); + goto error_bhi_offset; + } + mhi_cntrl->bhi = mhi_cntrl->regs + val; /* setup bhie offset if not set */ @@ -958,6 +982,12 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl) goto error_bhi_offset; } + if (val >= mhi_cntrl->len) { + write_unlock_irq(&mhi_cntrl->pm_lock); + MHI_ERR("Invalid bhie offset:%x\n", val); + goto error_bhi_offset; + } + mhi_cntrl->bhie = mhi_cntrl->regs + val; } @@ -1087,10 +1117,9 @@ void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful) if (!mhi_cntrl->pre_init) { /* free all allocated resources */ - if (mhi_cntrl->fbc_image) { - mhi_free_bhie_table(mhi_cntrl, mhi_cntrl->fbc_image); - mhi_cntrl->fbc_image = NULL; - } + if (mhi_cntrl->fbc_image) + mhi_free_bhie_table(mhi_cntrl, &mhi_cntrl->fbc_image); + mhi_deinit_dev_ctxt(mhi_cntrl); } } @@ -1229,6 +1258,7 @@ int mhi_pm_fast_suspend(struct mhi_controller *mhi_cntrl, bool notify_client) int ret; enum MHI_PM_STATE new_state; struct mhi_chan *itr, *tmp; + struct mhi_device *mhi_dev = mhi_cntrl->mhi_dev; read_lock_bh(&mhi_cntrl->pm_lock); if (mhi_cntrl->pm_state == MHI_PM_DISABLE) { @@ -1243,7 +1273,8 @@ int mhi_pm_fast_suspend(struct mhi_controller *mhi_cntrl, bool notify_client) read_unlock_bh(&mhi_cntrl->pm_lock); /* do a quick check to see if any pending votes to keep us busy */ - if (atomic_read(&mhi_cntrl->pending_pkts)) { + if (atomic_read(&mhi_cntrl->pending_pkts) || + atomic_read(&mhi_dev->bus_vote)) { MHI_VERB("Busy, aborting M3\n"); return -EBUSY; } @@ -1262,7 +1293,8 @@ int mhi_pm_fast_suspend(struct mhi_controller *mhi_cntrl, bool notify_client) * Check the votes once more to see if we should abort * suspend. */ - if (atomic_read(&mhi_cntrl->pending_pkts)) { + if (atomic_read(&mhi_cntrl->pending_pkts) || + atomic_read(&mhi_dev->bus_vote)) { MHI_VERB("Busy, aborting M3\n"); ret = -EBUSY; goto error_suspend; @@ -1562,6 +1594,7 @@ void mhi_device_get(struct mhi_device *mhi_dev, int vote) if (vote & MHI_VOTE_DEVICE) { read_lock_bh(&mhi_cntrl->pm_lock); + mhi_trigger_resume(mhi_cntrl); mhi_cntrl->wake_get(mhi_cntrl, true); MHI_LOG("dev_wake %d\n", atomic_read(&mhi_cntrl->dev_wake)); read_unlock_bh(&mhi_cntrl->pm_lock); @@ -1606,6 +1639,57 @@ int mhi_device_get_sync(struct mhi_device *mhi_dev, int vote) } EXPORT_SYMBOL(mhi_device_get_sync); +int mhi_device_get_sync_atomic(struct mhi_device *mhi_dev, int timeout_us) +{ + struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; + + read_lock_bh(&mhi_cntrl->pm_lock); + if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { + read_unlock_bh(&mhi_cntrl->pm_lock); + return -EIO; + } + + mhi_cntrl->wake_get(mhi_cntrl, true); + read_unlock_bh(&mhi_cntrl->pm_lock); + + atomic_inc(&mhi_dev->dev_vote); + pm_wakeup_hard_event(&mhi_cntrl->mhi_dev->dev); + mhi_cntrl->runtime_get(mhi_cntrl, mhi_cntrl->priv_data); + + /* Return if client doesn't want us to wait */ + if (!timeout_us) { + if (mhi_cntrl->pm_state != MHI_PM_M0) + MHI_ERR("Return without waiting for M0\n"); + + mhi_cntrl->runtime_put(mhi_cntrl, mhi_cntrl->priv_data); + return 0; + } + + while (mhi_cntrl->pm_state != MHI_PM_M0 && + !MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) && + timeout_us > 0) { + udelay(MHI_FORCE_WAKE_DELAY_US); + timeout_us -= MHI_FORCE_WAKE_DELAY_US; + } + + if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) || timeout_us <= 0) { + MHI_ERR("Did not enter M0 state, cur_state:%s pm_state:%s\n", + TO_MHI_STATE_STR(mhi_cntrl->dev_state), + to_mhi_pm_state_str(mhi_cntrl->pm_state)); + read_lock_bh(&mhi_cntrl->pm_lock); + mhi_cntrl->wake_put(mhi_cntrl, false); + read_unlock_bh(&mhi_cntrl->pm_lock); + atomic_dec(&mhi_dev->dev_vote); + mhi_cntrl->runtime_put(mhi_cntrl, mhi_cntrl->priv_data); + return -ETIMEDOUT; + } + + mhi_cntrl->runtime_put(mhi_cntrl, mhi_cntrl->priv_data); + + return 0; +} +EXPORT_SYMBOL(mhi_device_get_sync_atomic); + void mhi_device_put(struct mhi_device *mhi_dev, int vote) { struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c index b6f63e7620214a7baf02fc491ea7fe118204958f..54c8c8644df2ec4bf280cdd81df650fa928c6e7e 100644 --- a/drivers/bus/ti-sysc.c +++ b/drivers/bus/ti-sysc.c @@ -888,10 +888,10 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = { SYSC_QUIRK("smartreflex", 0, -1, 0x38, -1, 0x00000000, 0xffffffff, SYSC_QUIRK_LEGACY_IDLE), SYSC_QUIRK("timer", 0, 0, 0x10, 0x14, 0x00000015, 0xffffffff, - SYSC_QUIRK_LEGACY_IDLE), + 0), /* Some timers on omap4 and later */ SYSC_QUIRK("timer", 0, 0, 0x10, -1, 0x4fff1301, 0xffffffff, - SYSC_QUIRK_LEGACY_IDLE), + 0), SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000052, 0xffffffff, SYSC_QUIRK_LEGACY_IDLE), /* Uarts on omap4 and later */ @@ -1400,6 +1400,9 @@ static void sysc_unprepare(struct sysc *ddata) { int i; + if (!ddata->clocks) + return; + for (i = 0; i < SYSC_MAX_CLOCKS; i++) { if (!IS_ERR_OR_NULL(ddata->clocks[i])) clk_unprepare(ddata->clocks[i]); @@ -1593,6 +1596,16 @@ static const struct sysc_regbits sysc_regbits_omap4_mcasp = { static const struct sysc_capabilities sysc_omap4_mcasp = { .type = TI_SYSC_OMAP4_MCASP, .regbits = &sysc_regbits_omap4_mcasp, + .mod_quirks = SYSC_QUIRK_OPT_CLKS_NEEDED, +}; + +/* + * McASP found on dra7 and later + */ +static const struct sysc_capabilities sysc_dra7_mcasp = { + .type = TI_SYSC_OMAP4_SIMPLE, + .regbits = &sysc_regbits_omap4_simple, + .mod_quirks = SYSC_QUIRK_OPT_CLKS_NEEDED, }; /* @@ -1821,6 +1834,7 @@ static const struct of_device_id sysc_match[] = { { .compatible = "ti,sysc-omap3-sham", .data = &sysc_omap3_sham, }, { .compatible = "ti,sysc-omap-aes", .data = &sysc_omap3_aes, }, { .compatible = "ti,sysc-mcasp", .data = &sysc_omap4_mcasp, }, + { .compatible = "ti,sysc-dra7-mcasp", .data = &sysc_dra7_mcasp, }, { .compatible = "ti,sysc-usb-host-fs", .data = &sysc_omap4_usb_host_fs, }, { .compatible = "ti,sysc-dra7-mcan", .data = &sysc_dra7_mcan, }, diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index f8c47e950c83d5760cae4fd982bc1af5a88ee5fa..bd5b84e09a5065a39fe18bbeb3d509ee0e0cad08 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -650,3 +650,12 @@ config VSERVICES_VTTY_COUNT help The maximum number of Virtual Services serial devices to support. This limit applies to both the client and server. + +config RANDOM_TRUST_BOOTLOADER + bool "Trust the bootloader to initialize Linux's CRNG" + help + Some bootloaders can provide entropy to increase the kernel's initial + device randomness. Say Y here to assume the entropy provided by the + booloader is trustworthy so it will be added to the kernel's entropy + pool. Otherwise, say N here so it will be regarded as device input that + only mixes the entropy pool. diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index 883c872eadfadf45e98bbd1759f7f6b2a26c5b98..c8c50546fecf4721ef9164f0e0545df901e40d9b 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -34,7 +34,6 @@ #include #include #include -#include #include "adsprpc_compat.h" #include "adsprpc_shared.h" #include @@ -75,6 +74,9 @@ #define SENSORS_PDR_SLPI_SERVICE_NAME SENSORS_PDR_ADSP_SERVICE_NAME #define SLPI_SENSORPD_NAME "msm/slpi/sensor_pd" +#define FASTRPC_SECURE_WAKE_SOURCE_CLIENT_NAME "adsprpc-secure" +#define FASTRPC_NON_SECURE_WAKE_SOURCE_CLIENT_NAME "adsprpc-non_secure" + #define RPC_TIMEOUT (5 * HZ) #define BALIGN 128 #define NUM_CHANNELS 4 /* adsp, mdsp, slpi, cdsp*/ @@ -148,10 +150,10 @@ #define PERF(enb, cnt, ff) \ {\ - struct timespec startT = {0};\ + struct timespec64 startT = {0};\ int64_t *counter = cnt;\ if (enb && counter) {\ - getnstimeofday(&startT);\ + ktime_get_real_ts64(&startT);\ } \ ff ;\ if (enb && counter) {\ @@ -382,6 +384,11 @@ struct fastrpc_apps { bool legacy_remote_heap; /* Unique job id for each message */ uint64_t jobid[NUM_CHANNELS]; + struct device *secure_dev; + struct device *non_secure_dev; + /* Secure subsystems like ADSP/SLPI will use secure client */ + struct wakeup_source *wake_source_secure; + /* Non-secure subsystem like CDSP will use regular client */ struct wakeup_source *wake_source; struct qos_cores silvercores; uint32_t max_size_limit; @@ -468,7 +475,6 @@ struct fastrpc_file { char *debug_buf; /* Flag to enable PM wake/relax voting for every remote invoke */ int wake_enable; - struct wakeup_source *wake_source; uint32_t ws_timeout; }; @@ -542,16 +548,16 @@ static struct fastrpc_channel_ctx gcinfo[NUM_CHANNELS] = { static int hlosvm[1] = {VMID_HLOS}; static int hlosvmperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC}; -static void fastrpc_pm_awake(struct fastrpc_file *fl); +static void fastrpc_pm_awake(struct fastrpc_file *fl, int channel_type); -static inline int64_t getnstimediff(struct timespec *start) +static inline int64_t getnstimediff(struct timespec64 *start) { int64_t ns; - struct timespec ts, b; + struct timespec64 ts, b; - getnstimeofday(&ts); - b = timespec_sub(ts, *start); - ns = timespec_to_ns(&b); + ktime_get_real_ts64(&ts); + b = timespec64_sub(ts, *start); + ns = timespec64_to_ns(&b); return ns; } @@ -844,12 +850,23 @@ static void fastrpc_mmap_free(struct fastrpc_mmap *map, uint32_t flags) { struct fastrpc_apps *me = &gfa; struct fastrpc_file *fl; - int vmid; + int vmid, cid = -1, err = 0; struct fastrpc_session_ctx *sess; if (!map) return; fl = map->fl; + if (fl && !(map->flags == ADSP_MMAP_HEAP_ADDR || + map->flags == ADSP_MMAP_REMOTE_HEAP_ADDR)) { + cid = fl->cid; + VERIFY(err, cid >= ADSP_DOMAIN_ID && cid < NUM_CHANNELS); + if (err) { + err = -ECHRNG; + pr_err("adsprpc: ERROR:%s, Invalid channel id: %d, err:%d\n", + __func__, cid, err); + return; + } + } if (map->flags == ADSP_MMAP_HEAP_ADDR || map->flags == ADSP_MMAP_REMOTE_HEAP_ADDR) { map->refs--; @@ -1480,7 +1497,7 @@ static void context_free(struct smq_invoke_ctx *ctx) static void context_notify_user(struct smq_invoke_ctx *ctx, int retval, uint32_t rspFlags, uint32_t earlyWakeTime) { - fastrpc_pm_awake(ctx->fl); + fastrpc_pm_awake(ctx->fl, gcinfo[ctx->fl->cid].secure); ctx->retval = retval; switch (rspFlags) { case NORMAL_RESPONSE: @@ -1934,10 +1951,7 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx) ctx->overps[oix]->mstart, rpra[i].buf.len, map->size); } - } else - dmac_flush_range(uint64_to_ptr(rpra[i].buf.pv), - uint64_to_ptr(rpra[i].buf.pv - + rpra[i].buf.len)); + } } } PERF_END); @@ -2094,11 +2108,8 @@ static void inv_args(struct smq_invoke_ctx *ctx) offset, ctx->overps[i]->mend, ctx->overps[i]->mstart, rpra[over].buf.len, map->size); + } } - } else - dmac_inv_range((char *)uint64_to_ptr(rpra[over].buf.pv), - (char *)uint64_to_ptr(rpra[over].buf.pv - + rpra[over].buf.len)); } } bail: @@ -2110,8 +2121,16 @@ static int fastrpc_invoke_send(struct smq_invoke_ctx *ctx, { struct smq_msg *msg = &ctx->msg; struct fastrpc_file *fl = ctx->fl; - struct fastrpc_channel_ctx *channel_ctx = &fl->apps->channel[fl->cid]; - int err = 0; + struct fastrpc_channel_ctx *channel_ctx = NULL; + int err = 0, cid = -1; + + channel_ctx = &fl->apps->channel[fl->cid]; + cid = fl->cid; + VERIFY(err, cid >= ADSP_DOMAIN_ID && cid < NUM_CHANNELS); + if (err) { + err = -ECHRNG; + goto bail; + } mutex_lock(&channel_ctx->smd_mutex); msg->pid = fl->tgid; @@ -2174,15 +2193,22 @@ static void fastrpc_init(struct fastrpc_apps *me) me->channel[CDSP_DOMAIN_ID].secure = NON_SECURE_CHANNEL; } -static inline void fastrpc_pm_awake(struct fastrpc_file *fl) +static inline void fastrpc_pm_awake(struct fastrpc_file *fl, int channel_type) { - if (!fl->wake_enable || !fl->wake_source) - return; + struct fastrpc_apps *me = &gfa; + struct wakeup_source *wake_source = NULL; + /* * Vote with PM to abort any suspend in progress and * keep system awake for specified timeout */ - pm_wakeup_ws_event(fl->wake_source, fl->ws_timeout, true); + if (channel_type == SECURE_CHANNEL) + wake_source = me->wake_source_secure; + else if (channel_type == NON_SECURE_CHANNEL) + wake_source = me->wake_source; + + if (wake_source) + pm_wakeup_ws_event(wake_source, fl->ws_timeout, true); } static inline int fastrpc_wait_for_response(struct smq_invoke_ctx *ctx, @@ -2278,7 +2304,7 @@ static void fastrpc_wait_for_completion(struct smq_invoke_ctx *ctx, } static void fastrpc_update_invoke_count(uint32_t handle, int64_t *perf_counter, - struct timespec *invoket) + struct timespec64 *invoket) { /* update invoke count for dynamic handles */ if (handle != FASTRPC_STATIC_HANDLE_LISTENER) { @@ -2301,13 +2327,26 @@ static int fastrpc_internal_invoke(struct fastrpc_file *fl, uint32_t mode, { struct smq_invoke_ctx *ctx = NULL; struct fastrpc_ioctl_invoke *invoke = &inv->inv; - int err = 0, interrupted = 0, cid = fl->cid; - struct timespec invoket = {0}; + int err = 0, interrupted = 0, cid = -1; + struct timespec64 invoket = {0}; int64_t *perf_counter = NULL; + cid = fl->cid; + VERIFY(err, cid >= ADSP_DOMAIN_ID && cid < NUM_CHANNELS); + if (err) { + err = -ECHRNG; + goto bail; + } + VERIFY(err, fl->sctx != NULL); + if (err) { + pr_err("adsprpc: ERROR: %s: user application %s domain is not set\n", + __func__, current->comm); + err = -EBADR; + goto bail; + } if (fl->profile) { perf_counter = getperfcounter(fl, PERF_COUNT); - getnstimeofday(&invoket); + ktime_get_real_ts64(&invoket); } if (!kernel) { @@ -2322,15 +2361,6 @@ static int fastrpc_internal_invoke(struct fastrpc_file *fl, uint32_t mode, } } - VERIFY(err, cid >= ADSP_DOMAIN_ID && cid < NUM_CHANNELS && - fl->sctx != NULL); - if (err) { - pr_err("adsprpc: ERROR: %s: kernel session not initialized yet for %s\n", - __func__, current->comm); - err = EBADR; - goto bail; - } - if (!kernel) { err = context_restore_interrupted(fl, inv, &ctx); if (err) @@ -3612,8 +3642,6 @@ static int fastrpc_file_free(struct fastrpc_file *fl) spin_lock(&fl->apps->hlock); hlist_del_init(&fl->hn); spin_unlock(&fl->apps->hlock); - if (fl->wake_source) - wakeup_source_unregister(fl->wake_source); kfree(fl->debug_buf); if (!fl->sctx) { @@ -3914,7 +3942,7 @@ static const struct file_operations debugfs_fops = { static int fastrpc_channel_open(struct fastrpc_file *fl) { struct fastrpc_apps *me = &gfa; - int cid, err = 0; + int cid = -1, err = 0; VERIFY(err, fl && fl->sctx && fl->cid >= 0 && fl->cid < NUM_CHANNELS); if (err) { @@ -3963,6 +3991,21 @@ static int fastrpc_channel_open(struct fastrpc_file *fl) return err; } +static inline void fastrpc_register_wakeup_source(struct device *dev, + const char *client_name, struct wakeup_source **device_wake_source) +{ + struct wakeup_source *wake_source = NULL; + + wake_source = wakeup_source_register(dev, client_name); + if (IS_ERR_OR_NULL(wake_source)) { + pr_err("adsprpc: Error: %s: %s: wakeup_source_register failed for dev %s, client %s with err %ld\n", + __func__, current->comm, dev_name(dev), + client_name, PTR_ERR(wake_source)); + return; + } + *device_wake_source = wake_source; +} + static int fastrpc_device_open(struct inode *inode, struct file *filp) { int err = 0; @@ -3986,11 +4029,6 @@ static int fastrpc_device_open(struct inode *inode, struct file *filp) if (err) return err; - fl->wake_source = wakeup_source_register(fl->debug_buf); - if (IS_ERR_OR_NULL(fl->wake_source)) { - pr_err("adsprpc: Error: %s: %s: wakeup_source_register failed with err %ld\n", - current->comm, __func__, PTR_ERR(fl->wake_source)); - } context_list_ctor(&fl->clst); spin_lock_init(&fl->hlock); INIT_HLIST_HEAD(&fl->maps); @@ -4155,7 +4193,7 @@ static int fastrpc_internal_control(struct fastrpc_file *fl, fl->ws_timeout = MAX_PM_TIMEOUT_MS; else fl->ws_timeout = cp->pm.timeout; - fastrpc_pm_awake(fl); + fastrpc_pm_awake(fl, gcinfo[fl->cid].secure); break; case FASTRPC_CONTROL_DSPPROCESS_CLEAN: (void)fastrpc_release_current_dsp_process(fl); @@ -4492,8 +4530,6 @@ static int fastrpc_restart_notifier_cb(struct notifier_block *nb, ctx->ssrcount++; ctx->issubsystemup = 0; mutex_unlock(&me->channel[cid].smd_mutex); - if (cid == RH_CID) - me->staticpd_flags = 0; } else if (code == SUBSYS_RAMDUMP_NOTIFICATION) { if (cid == RH_CID) { if (me->channel[RH_CID].rh_dump_dev && @@ -5112,13 +5148,13 @@ static int __init fastrpc_device_init(void) } me->rpmsg_register = 1; - me->wake_source = wakeup_source_register("adsprpc"); - VERIFY(err, !IS_ERR_OR_NULL(me->wake_source)); - if (err) { - pr_err("adsprpc: Error: %s: wakeup_source_register failed with err %d\n", - __func__, PTR_ERR(me->wake_source)); - goto device_create_bail; - } + fastrpc_register_wakeup_source(me->non_secure_dev, + FASTRPC_NON_SECURE_WAKE_SOURCE_CLIENT_NAME, + &me->wake_source); + fastrpc_register_wakeup_source(me->secure_dev, + FASTRPC_SECURE_WAKE_SOURCE_CLIENT_NAME, + &me->wake_source_secure); + return 0; device_create_bail: for (i = 0; i < NUM_CHANNELS; i++) { @@ -5171,6 +5207,8 @@ static void __exit fastrpc_device_exit(void) unregister_rpmsg_driver(&fastrpc_rpmsg_client); if (me->wake_source) wakeup_source_unregister(me->wake_source); + if (me->wake_source_secure) + wakeup_source_unregister(me->wake_source_secure); debugfs_remove_recursive(debugfs_root); } diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h index 880cbeb591efe4c3a11000420fc5e4b75a06c4ef..b8292512a2170f2f553d2e2ceaddc991cd966062 100644 --- a/drivers/char/diag/diagchar.h +++ b/drivers/char/diag/diagchar.h @@ -59,6 +59,8 @@ #define DIAG_CTRL_MSG_LOG_MS_MASK 37 #define DIAG_CTRL_MSG_EVENT_MS_MASK 38 +#define NON_HDLC_VERSION 1 +#define NON_HDLC_HEADER_SIZE 4 #define CONTROL_CHAR 0x7E #define DIAG_ID_ROOT_STRING "root" @@ -834,6 +836,7 @@ struct diagchar_dev { unsigned char *buf_feature_mask_update; uint8_t hdlc_disabled; uint8_t p_hdlc_disabled[NUM_MD_SESSIONS]; + uint8_t proc_hdlc_disabled[NUM_DIAG_MD_DEV]; struct mutex hdlc_disable_mutex; struct mutex hdlc_recovery_mutex; struct timer_list hdlc_reset_timer; diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c index d5b56fd369ee0eb2a69fc81609763c6a183d86b6..40c818efb9977af106203c11007c312a9d2daff5 100644 --- a/drivers/char/diag/diagchar_core.c +++ b/drivers/char/diag/diagchar_core.c @@ -1030,11 +1030,13 @@ static int diag_send_raw_data_remote(int proc, void *buf, int len, int max_len = 0; uint8_t retry_count = 0; uint8_t max_retries = 50; - uint16_t payload = 0; + uint16_t payload_len = 0; struct diag_send_desc_type send = { NULL, NULL, DIAG_STATE_START, 0 }; struct diag_hdlc_dest_type enc = { NULL, NULL, 0 }; int bridge_index = proc - 1; - uint8_t hdlc_disabled = 0; + unsigned char non_hdlc_header[NON_HDLC_HEADER_SIZE] = { + CONTROL_CHAR, NON_HDLC_VERSION, 0, 0 }; + unsigned char end_byte[1] = { CONTROL_CHAR }; if (!buf) return -EINVAL; @@ -1059,39 +1061,6 @@ static int diag_send_raw_data_remote(int proc, void *buf, int len, if (driver->hdlc_encode_buf_len != 0) return -EAGAIN; - mutex_lock(&driver->hdlc_disable_mutex); - hdlc_disabled = driver->p_hdlc_disabled[APPS_DATA]; - mutex_unlock(&driver->hdlc_disable_mutex); - if (hdlc_disabled) { - if (len < 4) { - pr_err("diag: In %s, invalid len: %d of non_hdlc pkt\n", - __func__, len); - return -EBADMSG; - } - payload = *(uint16_t *)(buf + 2); - if (payload > DIAG_MAX_HDLC_BUF_SIZE) { - pr_err("diag: Dropping packet, payload size is %d\n", - payload); - return -EBADMSG; - } - driver->hdlc_encode_buf_len = payload; - /* - * Adding 5 bytes for start (1 byte), version (1 byte), - * payload (2 bytes) and end (1 byte) - */ - if (len == (payload + 5)) { - /* - * Adding 4 bytes for start (1 byte), version (1 byte) - * and payload (2 bytes) - */ - memcpy(driver->hdlc_encode_buf, buf + 4, payload); - goto send_data; - } else { - pr_err("diag: In %s, invalid len: %d of non_hdlc pkt\n", - __func__, len); - return -EBADMSG; - } - } if (hdlc_flag) { if (len > DIAG_MAX_HDLC_BUF_SIZE) { @@ -1104,28 +1073,47 @@ static int diag_send_raw_data_remote(int proc, void *buf, int len, goto send_data; } - /* - * The worst case length will be twice as the incoming packet length. - * Add 3 bytes for CRC bytes (2 bytes) and delimiter (1 byte) - */ - max_len = (2 * len) + 3; - if (max_len > DIAG_MAX_HDLC_BUF_SIZE) { - pr_err("diag: Dropping packet, HDLC encoded packet payload size crosses buffer limit. Current payload size %d\n", - max_len); - return -EBADMSG; - } + if (driver->proc_hdlc_disabled[proc]) { + /* + * Adding 4 bytes for start (1 byte), version (1 byte) + * and payload (2 bytes) + */ + payload_len = len; + memcpy(non_hdlc_header+2, &payload_len, 2); + memcpy(driver->hdlc_encode_buf, + non_hdlc_header, NON_HDLC_HEADER_SIZE); + memcpy(driver->hdlc_encode_buf + + NON_HDLC_HEADER_SIZE, buf, len); + memcpy((driver->hdlc_encode_buf + + NON_HDLC_HEADER_SIZE + len), end_byte, 1); + driver->hdlc_encode_buf_len = + len + NON_HDLC_HEADER_SIZE + 1; + goto send_data; + } else { + /* + * The worst case length will be twice as the incoming packet + * length. + * Add 3 bytes for CRC bytes (2 bytes) and delimiter (1 byte) + */ + max_len = (2 * len) + 3; + if (max_len > DIAG_MAX_HDLC_BUF_SIZE) { + pr_err("diag: Dropping packet, HDLC encoded packet payload size crosses buffer limit. Current payload size %d\n", + max_len); + return -EBADMSG; + } - /* Perform HDLC encoding on incoming data */ - send.state = DIAG_STATE_START; - send.pkt = (void *)(buf); - send.last = (void *)(buf + len - 1); - send.terminate = 1; + /* Perform HDLC encoding on incoming data */ + send.state = DIAG_STATE_START; + send.pkt = (void *)(buf); + send.last = (void *)(buf + len - 1); + send.terminate = 1; - enc.dest = driver->hdlc_encode_buf; - enc.dest_last = (void *)(driver->hdlc_encode_buf + max_len - 1); - diag_hdlc_encode(&send, &enc); - driver->hdlc_encode_buf_len = (int)(enc.dest - - (void *)driver->hdlc_encode_buf); + enc.dest = driver->hdlc_encode_buf; + enc.dest_last = (void *)(driver->hdlc_encode_buf + max_len - 1); + diag_hdlc_encode(&send, &enc); + driver->hdlc_encode_buf_len = (int)(enc.dest - + (void *)driver->hdlc_encode_buf); + } send_data: err = diagfwd_bridge_write(bridge_index, driver->hdlc_encode_buf, @@ -2269,10 +2257,13 @@ static int diag_ioctl_hdlc_toggle(unsigned long ioarg) mutex_lock(&driver->hdlc_disable_mutex); mutex_lock(&driver->md_session_lock); session_info = diag_md_session_get_pid(current->tgid); - if (session_info) + if (session_info) { session_info->hdlc_disabled = hdlc_support; - else + driver->proc_hdlc_disabled[DIAG_LOCAL_PROC] = + hdlc_support; + } else { driver->hdlc_disabled = hdlc_support; + } peripheral = diag_md_session_match_pid_peripheral(DIAG_LOCAL_PROC, @@ -3026,6 +3017,8 @@ long diagchar_compat_ioctl(struct file *filp, unsigned int iocmd, unsigned long ioarg) { int result = -EINVAL; + uint8_t hdlc_support, i; + struct diag_md_session_t *session_info = NULL; if (iocmd == DIAG_IOCTL_COMMAND_REG) { result = diag_ioctl_cmd_reg_compat(ioarg); @@ -3049,6 +3042,18 @@ long diagchar_compat_ioctl(struct file *filp, } else if (iocmd >= DIAG_IOCTL_QUERY_PD_FEATUREMASK && iocmd <= DIAG_IOCTL_PASSTHRU_CONTROL) { result = diagchar_ioctl_hw_accel(filp, iocmd, ioarg); + } else if (iocmd == DIAG_IOCTL_MDM_HDLC_TOGGLE) { + if (copy_from_user(&hdlc_support, (void __user *)ioarg, + sizeof(uint8_t))) + return -EFAULT; + mutex_lock(&driver->md_session_lock); + session_info = diag_md_session_get_pid(current->tgid); + if (session_info) { + for (i = DIAG_LOCAL_PROC + 1; i < NUM_DIAG_MD_DEV; i++) + driver->proc_hdlc_disabled[i] = hdlc_support; + } + mutex_unlock(&driver->md_session_lock); + result = 0; } else { result = -EINVAL; } @@ -3060,6 +3065,8 @@ long diagchar_ioctl(struct file *filp, unsigned int iocmd, unsigned long ioarg) { int result = -EINVAL; + uint8_t hdlc_support, i; + struct diag_md_session_t *session_info = NULL; if (iocmd == DIAG_IOCTL_COMMAND_REG) { result = diag_ioctl_cmd_reg(ioarg); @@ -3083,6 +3090,18 @@ long diagchar_ioctl(struct file *filp, } else if (iocmd >= DIAG_IOCTL_QUERY_PD_FEATUREMASK && iocmd <= DIAG_IOCTL_PASSTHRU_CONTROL) { result = diagchar_ioctl_hw_accel(filp, iocmd, ioarg); + } else if (iocmd == DIAG_IOCTL_MDM_HDLC_TOGGLE) { + if (copy_from_user(&hdlc_support, (void __user *)ioarg, + sizeof(uint8_t))) + return -EFAULT; + mutex_lock(&driver->md_session_lock); + session_info = diag_md_session_get_pid(current->tgid); + if (session_info) { + for (i = DIAG_LOCAL_PROC + 1; i < NUM_DIAG_MD_DEV; i++) + driver->proc_hdlc_disabled[i] = hdlc_support; + } + mutex_unlock(&driver->md_session_lock); + result = 0; } else { result = -EINVAL; } @@ -4379,7 +4398,8 @@ static int diagchar_setup_cdev(dev_t devno) if (!driver->diag_dev) return -EIO; - driver->diag_dev->power.wakeup = wakeup_source_register("DIAG_WS"); + driver->diag_dev->power.wakeup = + wakeup_source_register(driver->diag_dev, "DIAG_WS"); return 0; } diff --git a/drivers/char/hw_random/bcm2835-rng.c b/drivers/char/hw_random/bcm2835-rng.c index 6767d965c36c5254e052c84d1b9577af7c0ca5e1..19bde680aee1d4c3270cd324701d5c8e5bb11f88 100644 --- a/drivers/char/hw_random/bcm2835-rng.c +++ b/drivers/char/hw_random/bcm2835-rng.c @@ -171,14 +171,16 @@ static int bcm2835_rng_probe(struct platform_device *pdev) priv->rng.read = bcm2835_rng_read; priv->rng.cleanup = bcm2835_rng_cleanup; - rng_id = of_match_node(bcm2835_rng_of_match, np); - if (!rng_id) - return -EINVAL; - - /* Check for rng init function, execute it */ - of_data = rng_id->data; - if (of_data) - priv->mask_interrupts = of_data->mask_interrupts; + if (dev_of_node(dev)) { + rng_id = of_match_node(bcm2835_rng_of_match, np); + if (!rng_id) + return -EINVAL; + + /* Check for rng init function, execute it */ + of_data = rng_id->data; + if (of_data) + priv->mask_interrupts = of_data->mask_interrupts; + } /* register driver */ err = devm_hwrng_register(dev, &priv->rng); diff --git a/drivers/char/hw_random/omap3-rom-rng.c b/drivers/char/hw_random/omap3-rom-rng.c index 648e39ce6bd95bcfb34de1624d5240ef343badd9..8df3cad7c97aeb7e622f39263ad8bde85eb9d679 100644 --- a/drivers/char/hw_random/omap3-rom-rng.c +++ b/drivers/char/hw_random/omap3-rom-rng.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #define RNG_RESET 0x01 @@ -86,14 +88,18 @@ static int omap3_rom_rng_read(struct hwrng *rng, void *data, size_t max, bool w) static struct hwrng omap3_rom_rng_ops = { .name = "omap3-rom", - .read = omap3_rom_rng_read, }; static int omap3_rom_rng_probe(struct platform_device *pdev) { int ret = 0; - pr_info("initializing\n"); + omap3_rom_rng_ops.read = of_device_get_match_data(&pdev->dev); + if (!omap3_rom_rng_ops.read) { + dev_err(&pdev->dev, "missing rom code handler\n"); + + return -ENODEV; + } omap3_rom_rng_call = pdev->dev.platform_data; if (!omap3_rom_rng_call) { @@ -126,9 +132,16 @@ static int omap3_rom_rng_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id omap_rom_rng_match[] = { + { .compatible = "nokia,n900-rom-rng", .data = omap3_rom_rng_read, }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, omap_rom_rng_match); + static struct platform_driver omap3_rom_rng_driver = { .driver = { .name = "omap3-rom-rng", + .of_match_table = omap_rom_rng_match, }, .probe = omap3_rom_rng_probe, .remove = omap3_rom_rng_remove, diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 91f2d9219489254731b0fa6c24d69a7a4e143cdf..980eb7c609528a99503a3d90ce7f8f5dc06e071f 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -2965,8 +2965,11 @@ static int __ipmi_bmc_register(struct ipmi_smi *intf, bmc->pdev.name = "ipmi_bmc"; rv = ida_simple_get(&ipmi_bmc_ida, 0, 0, GFP_KERNEL); - if (rv < 0) + if (rv < 0) { + kfree(bmc); goto out; + } + bmc->pdev.dev.driver = &ipmidriver.driver; bmc->pdev.id = rv; bmc->pdev.dev.release = release_bmc_device; diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index af44db2dfb68a837c90ced201124590625ffd37b..fec679433f72da3247238336964dea1053cfaf12 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -735,10 +735,14 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, flags = ipmi_ssif_lock_cond(ssif_info, &oflags); msg = ssif_info->curr_msg; if (msg) { + if (data) { + if (len > IPMI_MAX_MSG_LENGTH) + len = IPMI_MAX_MSG_LENGTH; + memcpy(msg->rsp, data, len); + } else { + len = 0; + } msg->rsp_size = len; - if (msg->rsp_size > IPMI_MAX_MSG_LENGTH) - msg->rsp_size = IPMI_MAX_MSG_LENGTH; - memcpy(msg->rsp, data, msg->rsp_size); ssif_info->curr_msg = NULL; } diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c index e6124bd548df211317e7a69fa42da99849382e69..ed4dc3b1843e3a06995750deda672b5bb830444a 100644 --- a/drivers/char/ipmi/kcs_bmc.c +++ b/drivers/char/ipmi/kcs_bmc.c @@ -440,12 +440,13 @@ struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, u32 channel) kcs_bmc->data_in = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL); kcs_bmc->data_out = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL); kcs_bmc->kbuffer = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL); - if (!kcs_bmc->data_in || !kcs_bmc->data_out || !kcs_bmc->kbuffer) - return NULL; kcs_bmc->miscdev.minor = MISC_DYNAMIC_MINOR; kcs_bmc->miscdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s%u", DEVICE_NAME, channel); + if (!kcs_bmc->data_in || !kcs_bmc->data_out || !kcs_bmc->kbuffer || + !kcs_bmc->miscdev.name) + return NULL; kcs_bmc->miscdev.fops = &kcs_bmc_fops; return kcs_bmc; diff --git a/drivers/char/random.c b/drivers/char/random.c index af6e240f98ff4613be9d670ce4873579e33ce601..d0be947c68427376bacfb4a89904a70eb7e76e51 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -101,15 +101,13 @@ * Exported interfaces ---- output * =============================== * - * There are three exported interfaces; the first is one designed to - * be used from within the kernel: + * There are four exported interfaces; two for use within the kernel, + * and two or use from userspace. * - * void get_random_bytes(void *buf, int nbytes); - * - * This interface will return the requested number of random bytes, - * and place it in the requested buffer. + * Exported interfaces ---- userspace output + * ----------------------------------------- * - * The two other interfaces are two character devices /dev/random and + * The userspace interfaces are two character devices /dev/random and * /dev/urandom. /dev/random is suitable for use when very high * quality randomness is desired (for example, for key generation or * one-time pads), as it will only return a maximum of the number of @@ -122,6 +120,77 @@ * this will result in random numbers that are merely cryptographically * strong. For many applications, however, this is acceptable. * + * Exported interfaces ---- kernel output + * -------------------------------------- + * + * The primary kernel interface is + * + * void get_random_bytes(void *buf, int nbytes); + * + * This interface will return the requested number of random bytes, + * and place it in the requested buffer. This is equivalent to a + * read from /dev/urandom. + * + * For less critical applications, there are the functions: + * + * u32 get_random_u32() + * u64 get_random_u64() + * unsigned int get_random_int() + * unsigned long get_random_long() + * + * These are produced by a cryptographic RNG seeded from get_random_bytes, + * and so do not deplete the entropy pool as much. These are recommended + * for most in-kernel operations *if the result is going to be stored in + * the kernel*. + * + * Specifically, the get_random_int() family do not attempt to do + * "anti-backtracking". If you capture the state of the kernel (e.g. + * by snapshotting the VM), you can figure out previous get_random_int() + * return values. But if the value is stored in the kernel anyway, + * this is not a problem. + * + * It *is* safe to expose get_random_int() output to attackers (e.g. as + * network cookies); given outputs 1..n, it's not feasible to predict + * outputs 0 or n+1. The only concern is an attacker who breaks into + * the kernel later; the get_random_int() engine is not reseeded as + * often as the get_random_bytes() one. + * + * get_random_bytes() is needed for keys that need to stay secret after + * they are erased from the kernel. For example, any key that will + * be wrapped and stored encrypted. And session encryption keys: we'd + * like to know that after the session is closed and the keys erased, + * the plaintext is unrecoverable to someone who recorded the ciphertext. + * + * But for network ports/cookies, stack canaries, PRNG seeds, address + * space layout randomization, session *authentication* keys, or other + * applications where the sensitive data is stored in the kernel in + * plaintext for as long as it's sensitive, the get_random_int() family + * is just fine. + * + * Consider ASLR. We want to keep the address space secret from an + * outside attacker while the process is running, but once the address + * space is torn down, it's of no use to an attacker any more. And it's + * stored in kernel data structures as long as it's alive, so worrying + * about an attacker's ability to extrapolate it from the get_random_int() + * CRNG is silly. + * + * Even some cryptographic keys are safe to generate with get_random_int(). + * In particular, keys for SipHash are generally fine. Here, knowledge + * of the key authorizes you to do something to a kernel object (inject + * packets to a network connection, or flood a hash table), and the + * key is stored with the object being protected. Once it goes away, + * we no longer care if anyone knows the key. + * + * prandom_u32() + * ------------- + * + * For even weaker applications, see the pseudorandom generator + * prandom_u32(), prandom_max(), and prandom_bytes(). If the random + * numbers aren't security-critical at all, these are *far* cheaper. + * Useful for self-tests, random error simulation, randomized backoffs, + * and any other application where you trust that nobody is trying to + * maliciously mess with you by guessing the "random" numbers. + * * Exported interfaces ---- input * ============================== * @@ -238,6 +307,8 @@ * Eastlake, Steve Crocker, and Jeff Schiller. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -285,7 +356,6 @@ #define INPUT_POOL_WORDS (1 << (INPUT_POOL_SHIFT-5)) #define OUTPUT_POOL_SHIFT 10 #define OUTPUT_POOL_WORDS (1 << (OUTPUT_POOL_SHIFT-5)) -#define SEC_XFER_SIZE 512 #define EXTRACT_SIZE 10 @@ -295,18 +365,12 @@ * To allow fractional bits to be tracked, the entropy_count field is * denominated in units of 1/8th bits. * - * 2*(ENTROPY_SHIFT + log2(poolbits)) must <= 31, or the multiply in + * 2*(ENTROPY_SHIFT + poolbitshift) must <= 31, or the multiply in * credit_entropy_bits() needs to be 64 bits wide. */ #define ENTROPY_SHIFT 3 #define ENTROPY_BITS(r) ((r)->entropy_count >> ENTROPY_SHIFT) -/* - * The minimum number of bits of entropy before we wake up a read on - * /dev/random. Should be enough to do a significant reseed. - */ -static int random_read_wakeup_bits = 64; - /* * If the entropy count falls under this number of bits, then we * should wake up processes which are selecting or polling on write @@ -359,50 +423,19 @@ static int random_write_wakeup_bits = 28 * OUTPUT_POOL_WORDS; * polynomial which improves the resulting TGFSR polynomial to be * irreducible, which we have made here. */ -static struct poolinfo { - int poolbitshift, poolwords, poolbytes, poolbits, poolfracbits; -#define S(x) ilog2(x)+5, (x), (x)*4, (x)*32, (x) << (ENTROPY_SHIFT+5) +static const struct poolinfo { + int poolbitshift, poolwords, poolbytes, poolfracbits; +#define S(x) ilog2(x)+5, (x), (x)*4, (x) << (ENTROPY_SHIFT+5) int tap1, tap2, tap3, tap4, tap5; } poolinfo_table[] = { /* was: x^128 + x^103 + x^76 + x^51 +x^25 + x + 1 */ /* x^128 + x^104 + x^76 + x^51 +x^25 + x + 1 */ { S(128), 104, 76, 51, 25, 1 }, - /* was: x^32 + x^26 + x^20 + x^14 + x^7 + x + 1 */ - /* x^32 + x^26 + x^19 + x^14 + x^7 + x + 1 */ - { S(32), 26, 19, 14, 7, 1 }, -#if 0 - /* x^2048 + x^1638 + x^1231 + x^819 + x^411 + x + 1 -- 115 */ - { S(2048), 1638, 1231, 819, 411, 1 }, - - /* x^1024 + x^817 + x^615 + x^412 + x^204 + x + 1 -- 290 */ - { S(1024), 817, 615, 412, 204, 1 }, - - /* x^1024 + x^819 + x^616 + x^410 + x^207 + x^2 + 1 -- 115 */ - { S(1024), 819, 616, 410, 207, 2 }, - - /* x^512 + x^411 + x^308 + x^208 + x^104 + x + 1 -- 225 */ - { S(512), 411, 308, 208, 104, 1 }, - - /* x^512 + x^409 + x^307 + x^206 + x^102 + x^2 + 1 -- 95 */ - { S(512), 409, 307, 206, 102, 2 }, - /* x^512 + x^409 + x^309 + x^205 + x^103 + x^2 + 1 -- 95 */ - { S(512), 409, 309, 205, 103, 2 }, - - /* x^256 + x^205 + x^155 + x^101 + x^52 + x + 1 -- 125 */ - { S(256), 205, 155, 101, 52, 1 }, - - /* x^128 + x^103 + x^78 + x^51 + x^27 + x^2 + 1 -- 70 */ - { S(128), 103, 78, 51, 27, 2 }, - - /* x^64 + x^52 + x^39 + x^26 + x^14 + x + 1 -- 15 */ - { S(64), 52, 39, 26, 14, 1 }, -#endif }; /* * Static global variables */ -static DECLARE_WAIT_QUEUE_HEAD(random_read_wait); static DECLARE_WAIT_QUEUE_HEAD(random_write_wait); static struct fasync_struct *fasync; @@ -415,7 +448,7 @@ struct crng_state { spinlock_t lock; }; -struct crng_state primary_crng = { +static struct crng_state primary_crng = { .lock = __SPIN_LOCK_UNLOCKED(primary_crng.lock), }; @@ -461,16 +494,12 @@ struct entropy_store { const struct poolinfo *poolinfo; __u32 *pool; const char *name; - struct entropy_store *pull; - struct work_struct push_work; /* read-write data: */ - unsigned long last_pulled; spinlock_t lock; unsigned short add_ptr; unsigned short input_rotate; int entropy_count; - int entropy_total; unsigned int initialized:1; unsigned int last_data_init:1; __u8 last_data[EXTRACT_SIZE]; @@ -482,9 +511,7 @@ static ssize_t _extract_entropy(struct entropy_store *r, void *buf, size_t nbytes, int fips); static void crng_reseed(struct crng_state *crng, struct entropy_store *r); -static void push_to_pool(struct work_struct *work); static __u32 input_pool_data[INPUT_POOL_WORDS] __latent_entropy; -static __u32 blocking_pool_data[OUTPUT_POOL_WORDS] __latent_entropy; static struct entropy_store input_pool = { .poolinfo = &poolinfo_table[0], @@ -493,16 +520,6 @@ static struct entropy_store input_pool = { .pool = input_pool_data }; -static struct entropy_store blocking_pool = { - .poolinfo = &poolinfo_table[1], - .name = "blocking", - .pull = &input_pool, - .lock = __SPIN_LOCK_UNLOCKED(blocking_pool.lock), - .pool = blocking_pool_data, - .push_work = __WORK_INITIALIZER(blocking_pool.push_work, - push_to_pool), -}; - static __u32 const twist_table[8] = { 0x00000000, 0x3b6e20c8, 0x76dc4190, 0x4db26158, 0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 }; @@ -643,7 +660,7 @@ static void process_random_ready_list(void) */ static void credit_entropy_bits(struct entropy_store *r, int nbits) { - int entropy_count, orig; + int entropy_count, orig, has_initialized = 0; const int pool_size = r->poolinfo->poolfracbits; int nfrac = nbits << ENTROPY_SHIFT; @@ -691,53 +708,31 @@ static void credit_entropy_bits(struct entropy_store *r, int nbits) } while (unlikely(entropy_count < pool_size-2 && pnfrac)); } - if (unlikely(entropy_count < 0)) { - pr_warn("random: negative entropy/overflow: pool %s count %d\n", + if (WARN_ON(entropy_count < 0)) { + pr_warn("negative entropy/overflow: pool %s count %d\n", r->name, entropy_count); - WARN_ON(1); entropy_count = 0; } else if (entropy_count > pool_size) entropy_count = pool_size; if (cmpxchg(&r->entropy_count, orig, entropy_count) != orig) goto retry; - r->entropy_total += nbits; - if (!r->initialized && r->entropy_total > 128) { + if (has_initialized) { r->initialized = 1; - r->entropy_total = 0; + kill_fasync(&fasync, SIGIO, POLL_IN); } trace_credit_entropy_bits(r->name, nbits, - entropy_count >> ENTROPY_SHIFT, - r->entropy_total, _RET_IP_); + entropy_count >> ENTROPY_SHIFT, _RET_IP_); if (r == &input_pool) { int entropy_bits = entropy_count >> ENTROPY_SHIFT; - if (crng_init < 2 && entropy_bits >= 128) { + if (crng_init < 2) { + if (entropy_bits < 128) + return; crng_reseed(&primary_crng, r); - entropy_bits = r->entropy_count >> ENTROPY_SHIFT; - } - - /* should we wake readers? */ - if (entropy_bits >= random_read_wakeup_bits && - wq_has_sleeper(&random_read_wait)) { - wake_up_interruptible(&random_read_wait); - kill_fasync(&fasync, SIGIO, POLL_IN); - } - /* If the input pool is getting full, send some - * entropy to the blocking pool until it is 75% full. - */ - if (entropy_bits > random_write_wakeup_bits && - r->initialized && - r->entropy_total >= 2*random_read_wakeup_bits) { - struct entropy_store *other = &blocking_pool; - - if (other->entropy_count <= - 3 * other->poolinfo->poolfracbits / 4) { - schedule_work(&other->push_work); - r->entropy_total = 0; - } + entropy_bits = ENTROPY_BITS(r); } } } @@ -810,7 +805,7 @@ static void crng_initialize(struct crng_state *crng) invalidate_batched_entropy(); numa_crng_init(); crng_init = 2; - pr_notice("random: crng done (trusting CPU's manufacturer)\n"); + pr_notice("crng done (trusting CPU's manufacturer)\n"); } crng->init_time = jiffies - CRNG_RESEED_INTERVAL - 1; } @@ -872,8 +867,7 @@ static int crng_fast_load(const char *cp, size_t len) if (crng_init_cnt >= CRNG_INIT_CNT_THRESH) { invalidate_batched_entropy(); crng_init = 1; - wake_up_interruptible(&crng_init_wait); - pr_notice("random: fast init done\n"); + pr_notice("fast init done\n"); } return 1; } @@ -958,16 +952,15 @@ static void crng_reseed(struct crng_state *crng, struct entropy_store *r) crng_init = 2; process_random_ready_list(); wake_up_interruptible(&crng_init_wait); - pr_notice("random: crng init done\n"); + kill_fasync(&fasync, SIGIO, POLL_IN); + pr_notice("crng init done\n"); if (unseeded_warning.missed) { - pr_notice("random: %d get_random_xx warning(s) missed " - "due to ratelimiting\n", + pr_notice("%d get_random_xx warning(s) missed due to ratelimiting\n", unseeded_warning.missed); unseeded_warning.missed = 0; } if (urandom_warning.missed) { - pr_notice("random: %d urandom warning(s) missed " - "due to ratelimiting\n", + pr_notice("%d urandom warning(s) missed due to ratelimiting\n", urandom_warning.missed); urandom_warning.missed = 0; } @@ -1172,7 +1165,7 @@ static void add_timer_randomness(struct timer_rand_state *state, unsigned num) /* * delta is now minimum absolute delta. * Round down by 1 bit on general principles, - * and limit entropy entimate to 12 bits. + * and limit entropy estimate to 12 bits. */ credit_entropy_bits(r, min_t(int, fls(delta>>1), 11)); } @@ -1315,57 +1308,6 @@ EXPORT_SYMBOL_GPL(add_disk_randomness); * *********************************************************************/ -/* - * This utility inline function is responsible for transferring entropy - * from the primary pool to the secondary extraction pool. We make - * sure we pull enough for a 'catastrophic reseed'. - */ -static void _xfer_secondary_pool(struct entropy_store *r, size_t nbytes); -static void xfer_secondary_pool(struct entropy_store *r, size_t nbytes) -{ - if (!r->pull || - r->entropy_count >= (nbytes << (ENTROPY_SHIFT + 3)) || - r->entropy_count > r->poolinfo->poolfracbits) - return; - - _xfer_secondary_pool(r, nbytes); -} - -static void _xfer_secondary_pool(struct entropy_store *r, size_t nbytes) -{ - __u32 tmp[OUTPUT_POOL_WORDS]; - - int bytes = nbytes; - - /* pull at least as much as a wakeup */ - bytes = max_t(int, bytes, random_read_wakeup_bits / 8); - /* but never more than the buffer size */ - bytes = min_t(int, bytes, sizeof(tmp)); - - trace_xfer_secondary_pool(r->name, bytes * 8, nbytes * 8, - ENTROPY_BITS(r), ENTROPY_BITS(r->pull)); - bytes = extract_entropy(r->pull, tmp, bytes, - random_read_wakeup_bits / 8, 0); - mix_pool_bytes(r, tmp, bytes); - credit_entropy_bits(r, bytes*8); -} - -/* - * Used as a workqueue function so that when the input pool is getting - * full, we can "spill over" some entropy to the output pools. That - * way the output pools can store some of the excess entropy instead - * of letting it go to waste. - */ -static void push_to_pool(struct work_struct *work) -{ - struct entropy_store *r = container_of(work, struct entropy_store, - push_work); - BUG_ON(!r); - _xfer_secondary_pool(r, random_read_wakeup_bits/8); - trace_push_to_pool(r->name, r->entropy_count >> ENTROPY_SHIFT, - r->pull->entropy_count >> ENTROPY_SHIFT); -} - /* * This function decides how many bytes to actually take from the * given pool, and also debits the entropy count accordingly. @@ -1391,10 +1333,9 @@ static size_t account(struct entropy_store *r, size_t nbytes, int min, if (ibytes < min) ibytes = 0; - if (unlikely(entropy_count < 0)) { - pr_warn("random: negative entropy count: pool %s count %d\n", + if (WARN_ON(entropy_count < 0)) { + pr_warn("negative entropy count: pool %s count %d\n", r->name, entropy_count); - WARN_ON(1); entropy_count = 0; } nfrac = ibytes << (ENTROPY_SHIFT + 3); @@ -1407,8 +1348,7 @@ static size_t account(struct entropy_store *r, size_t nbytes, int min, goto retry; trace_debit_entropy(r->name, 8 * ibytes); - if (ibytes && - (r->entropy_count >> ENTROPY_SHIFT) < random_write_wakeup_bits) { + if (ibytes && ENTROPY_BITS(r) < random_write_wakeup_bits) { wake_up_interruptible(&random_write_wait); kill_fasync(&fasync, SIGIO, POLL_OUT); } @@ -1529,7 +1469,6 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf, spin_unlock_irqrestore(&r->lock, flags); trace_extract_entropy(r->name, EXTRACT_SIZE, ENTROPY_BITS(r), _RET_IP_); - xfer_secondary_pool(r, EXTRACT_SIZE); extract_buf(r, tmp); spin_lock_irqsave(&r->lock, flags); memcpy(r->last_data, tmp, EXTRACT_SIZE); @@ -1538,55 +1477,11 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf, } trace_extract_entropy(r->name, nbytes, ENTROPY_BITS(r), _RET_IP_); - xfer_secondary_pool(r, nbytes); nbytes = account(r, nbytes, min, reserved); return _extract_entropy(r, buf, nbytes, fips_enabled); } -/* - * This function extracts randomness from the "entropy pool", and - * returns it in a userspace buffer. - */ -static ssize_t extract_entropy_user(struct entropy_store *r, void __user *buf, - size_t nbytes) -{ - ssize_t ret = 0, i; - __u8 tmp[EXTRACT_SIZE]; - int large_request = (nbytes > 256); - - trace_extract_entropy_user(r->name, nbytes, ENTROPY_BITS(r), _RET_IP_); - xfer_secondary_pool(r, nbytes); - nbytes = account(r, nbytes, 0, 0); - - while (nbytes) { - if (large_request && need_resched()) { - if (signal_pending(current)) { - if (ret == 0) - ret = -ERESTARTSYS; - break; - } - schedule(); - } - - extract_buf(r, tmp); - i = min_t(int, nbytes, EXTRACT_SIZE); - if (copy_to_user(buf, tmp, i)) { - ret = -EFAULT; - break; - } - - nbytes -= i; - buf += i; - ret += i; - } - - /* Wipe data just returned from memory */ - memzero_explicit(tmp, sizeof(tmp)); - - return ret; -} - #define warn_unseeded_randomness(previous) \ _warn_unseeded_randomness(__func__, (void *) _RET_IP_, (previous)) @@ -1652,6 +1547,56 @@ void get_random_bytes(void *buf, int nbytes) } EXPORT_SYMBOL(get_random_bytes); + +/* + * Each time the timer fires, we expect that we got an unpredictable + * jump in the cycle counter. Even if the timer is running on another + * CPU, the timer activity will be touching the stack of the CPU that is + * generating entropy.. + * + * Note that we don't re-arm the timer in the timer itself - we are + * happy to be scheduled away, since that just makes the load more + * complex, but we do not want the timer to keep ticking unless the + * entropy loop is running. + * + * So the re-arming always happens in the entropy loop itself. + */ +static void entropy_timer(struct timer_list *t) +{ + credit_entropy_bits(&input_pool, 1); +} + +/* + * If we have an actual cycle counter, see if we can + * generate enough entropy with timing noise + */ +static void try_to_generate_entropy(void) +{ + struct { + unsigned long now; + struct timer_list timer; + } stack; + + stack.now = random_get_entropy(); + + /* Slow counter - or none. Don't even bother */ + if (stack.now == random_get_entropy()) + return; + + timer_setup_on_stack(&stack.timer, entropy_timer, 0); + while (!crng_ready()) { + if (!timer_pending(&stack.timer)) + mod_timer(&stack.timer, jiffies+1); + mix_pool_bytes(&input_pool, &stack.now, sizeof(stack.now)); + schedule(); + stack.now = random_get_entropy(); + } + + del_timer_sync(&stack.timer); + destroy_timer_on_stack(&stack.timer); + mix_pool_bytes(&input_pool, &stack.now, sizeof(stack.now)); +} + /* * Wait for the urandom pool to be seeded and thus guaranteed to supply * cryptographically secure random numbers. This applies to: the /dev/urandom @@ -1666,7 +1611,17 @@ int wait_for_random_bytes(void) { if (likely(crng_ready())) return 0; - return wait_event_interruptible(crng_init_wait, crng_ready()); + + do { + int ret; + ret = wait_event_interruptible_timeout(crng_init_wait, crng_ready(), HZ); + if (ret) + return ret > 0 ? 0 : ret; + + try_to_generate_entropy(); + } while (!crng_ready()); + + return 0; } EXPORT_SYMBOL(wait_for_random_bytes); @@ -1786,13 +1741,12 @@ EXPORT_SYMBOL(get_random_bytes_arch); * data into the pool to prepare it for use. The pool is not cleared * as that can only decrease the entropy in the pool. */ -static void init_std_data(struct entropy_store *r) +static void __init init_std_data(struct entropy_store *r) { int i; ktime_t now = ktime_get_real(); unsigned long rv; - r->last_pulled = jiffies; mix_pool_bytes(r, &now, sizeof(now)); for (i = r->poolinfo->poolbytes; i > 0; i -= sizeof(rv)) { if (!arch_get_random_seed_long(&rv) && @@ -1813,10 +1767,9 @@ static void init_std_data(struct entropy_store *r) * take care not to overwrite the precious per platform data * we were given. */ -static int rand_initialize(void) +int __init rand_initialize(void) { init_std_data(&input_pool); - init_std_data(&blocking_pool); crng_initialize(&primary_crng); crng_global_init_time = jiffies; if (ratelimit_disable) { @@ -1825,7 +1778,6 @@ static int rand_initialize(void) } return 0; } -early_initcall(rand_initialize); #ifdef CONFIG_BLOCK void rand_initialize_disk(struct gendisk *disk) @@ -1845,40 +1797,15 @@ void rand_initialize_disk(struct gendisk *disk) #endif static ssize_t -_random_read(int nonblock, char __user *buf, size_t nbytes) +urandom_read_nowarn(struct file *file, char __user *buf, size_t nbytes, + loff_t *ppos) { - ssize_t n; - - if (nbytes == 0) - return 0; - - nbytes = min_t(size_t, nbytes, SEC_XFER_SIZE); - while (1) { - n = extract_entropy_user(&blocking_pool, buf, nbytes); - if (n < 0) - return n; - trace_random_read(n*8, (nbytes-n)*8, - ENTROPY_BITS(&blocking_pool), - ENTROPY_BITS(&input_pool)); - if (n > 0) - return n; - - /* Pool is (near) empty. Maybe wait and retry. */ - if (nonblock) - return -EAGAIN; - - wait_event_interruptible(random_read_wait, - ENTROPY_BITS(&input_pool) >= - random_read_wakeup_bits); - if (signal_pending(current)) - return -ERESTARTSYS; - } -} + int ret; -static ssize_t -random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) -{ - return _random_read(file->f_flags & O_NONBLOCK, buf, nbytes); + nbytes = min_t(size_t, nbytes, INT_MAX >> (ENTROPY_SHIFT + 3)); + ret = extract_crng_user(buf, nbytes); + trace_urandom_read(8 * nbytes, 0, ENTROPY_BITS(&input_pool)); + return ret; } static ssize_t @@ -1886,22 +1813,29 @@ urandom_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) { unsigned long flags; static int maxwarn = 10; - int ret; if (!crng_ready() && maxwarn > 0) { maxwarn--; if (__ratelimit(&urandom_warning)) - printk(KERN_NOTICE "random: %s: uninitialized " - "urandom read (%zd bytes read)\n", - current->comm, nbytes); + pr_notice("%s: uninitialized urandom read (%zd bytes read)\n", + current->comm, nbytes); spin_lock_irqsave(&primary_crng.lock, flags); crng_init_cnt = 0; spin_unlock_irqrestore(&primary_crng.lock, flags); } - nbytes = min_t(size_t, nbytes, INT_MAX >> (ENTROPY_SHIFT + 3)); - ret = extract_crng_user(buf, nbytes); - trace_urandom_read(8 * nbytes, 0, ENTROPY_BITS(&input_pool)); - return ret; + + return urandom_read_nowarn(file, buf, nbytes, ppos); +} + +static ssize_t +random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) +{ + int ret; + + ret = wait_for_random_bytes(); + if (ret != 0) + return ret; + return urandom_read_nowarn(file, buf, nbytes, ppos); } static __poll_t @@ -1909,10 +1843,10 @@ random_poll(struct file *file, poll_table * wait) { __poll_t mask; - poll_wait(file, &random_read_wait, wait); + poll_wait(file, &crng_init_wait, wait); poll_wait(file, &random_write_wait, wait); mask = 0; - if (ENTROPY_BITS(&input_pool) >= random_read_wakeup_bits) + if (crng_ready()) mask |= EPOLLIN | EPOLLRDNORM; if (ENTROPY_BITS(&input_pool) < random_write_wakeup_bits) mask |= EPOLLOUT | EPOLLWRNORM; @@ -2003,7 +1937,6 @@ static long random_ioctl(struct file *f, unsigned int cmd, unsigned long arg) if (!capable(CAP_SYS_ADMIN)) return -EPERM; input_pool.entropy_count = 0; - blocking_pool.entropy_count = 0; return 0; case RNDRESEEDCRNG: if (!capable(CAP_SYS_ADMIN)) @@ -2045,23 +1978,27 @@ SYSCALL_DEFINE3(getrandom, char __user *, buf, size_t, count, { int ret; - if (flags & ~(GRND_NONBLOCK|GRND_RANDOM)) + if (flags & ~(GRND_NONBLOCK|GRND_RANDOM|GRND_INSECURE)) + return -EINVAL; + + /* + * Requesting insecure and blocking randomness at the same time makes + * no sense. + */ + if ((flags & (GRND_INSECURE|GRND_RANDOM)) == (GRND_INSECURE|GRND_RANDOM)) return -EINVAL; if (count > INT_MAX) count = INT_MAX; - if (flags & GRND_RANDOM) - return _random_read(flags & GRND_NONBLOCK, buf, count); - - if (!crng_ready()) { + if (!(flags & GRND_INSECURE) && !crng_ready()) { if (flags & GRND_NONBLOCK) return -EAGAIN; ret = wait_for_random_bytes(); if (unlikely(ret)) return ret; } - return urandom_read(NULL, buf, count, NULL); + return urandom_read_nowarn(NULL, buf, count, NULL); } /******************************************************************** @@ -2074,8 +2011,7 @@ SYSCALL_DEFINE3(getrandom, char __user *, buf, size_t, count, #include -static int min_read_thresh = 8, min_write_thresh; -static int max_read_thresh = OUTPUT_POOL_WORDS * 32; +static int min_write_thresh; static int max_write_thresh = INPUT_POOL_WORDS * 32; static int random_min_urandom_seed = 60; static char sysctl_bootid[16]; @@ -2150,15 +2086,6 @@ struct ctl_table random_table[] = { .proc_handler = proc_do_entropy, .data = &input_pool.entropy_count, }, - { - .procname = "read_wakeup_threshold", - .data = &random_read_wakeup_bits, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_read_thresh, - .extra2 = &max_read_thresh, - }, { .procname = "write_wakeup_threshold", .data = &random_write_wakeup_bits, @@ -2367,3 +2294,17 @@ void add_hwgenerator_randomness(const char *buffer, size_t count, credit_entropy_bits(poolp, entropy); } EXPORT_SYMBOL_GPL(add_hwgenerator_randomness); + +/* Handle random seed passed by bootloader. + * If the seed is trustworthy, it would be regarded as hardware RNGs. Otherwise + * it would be regarded as device data. + * The decision is controlled by CONFIG_RANDOM_TRUST_BOOTLOADER. + */ +void add_bootloader_randomness(const void *buf, unsigned int size) +{ + if (IS_ENABLED(CONFIG_RANDOM_TRUST_BOOTLOADER)) + add_hwgenerator_randomness(buf, size, size * 8); + else + add_device_randomness(buf, size); +} +EXPORT_SYMBOL_GPL(add_bootloader_randomness); \ No newline at end of file diff --git a/drivers/char/ttyprintk.c b/drivers/char/ttyprintk.c index 67549ce88cc94251ad43e40074c7156f2736ea79..774748497acedf2da7d3d195b57d29fe485cf94f 100644 --- a/drivers/char/ttyprintk.c +++ b/drivers/char/ttyprintk.c @@ -18,10 +18,11 @@ #include #include #include +#include struct ttyprintk_port { struct tty_port port; - struct mutex port_write_mutex; + spinlock_t spinlock; }; static struct ttyprintk_port tpk_port; @@ -100,11 +101,12 @@ static int tpk_open(struct tty_struct *tty, struct file *filp) static void tpk_close(struct tty_struct *tty, struct file *filp) { struct ttyprintk_port *tpkp = tty->driver_data; + unsigned long flags; - mutex_lock(&tpkp->port_write_mutex); + spin_lock_irqsave(&tpkp->spinlock, flags); /* flush tpk_printk buffer */ tpk_printk(NULL, 0); - mutex_unlock(&tpkp->port_write_mutex); + spin_unlock_irqrestore(&tpkp->spinlock, flags); tty_port_close(&tpkp->port, tty, filp); } @@ -116,13 +118,14 @@ static int tpk_write(struct tty_struct *tty, const unsigned char *buf, int count) { struct ttyprintk_port *tpkp = tty->driver_data; + unsigned long flags; int ret; /* exclusive use of tpk_printk within this tty */ - mutex_lock(&tpkp->port_write_mutex); + spin_lock_irqsave(&tpkp->spinlock, flags); ret = tpk_printk(buf, count); - mutex_unlock(&tpkp->port_write_mutex); + spin_unlock_irqrestore(&tpkp->spinlock, flags); return ret; } @@ -172,7 +175,7 @@ static int __init ttyprintk_init(void) { int ret = -ENOMEM; - mutex_init(&tpk_port.port_write_mutex); + spin_lock_init(&tpk_port.spinlock); ttyprintk_driver = tty_alloc_driver(1, TTY_DRIVER_RESET_TERMIOS | diff --git a/drivers/clk/actions/owl-factor.c b/drivers/clk/actions/owl-factor.c index 317d4a9e112ee968f1833af7ca1272818ec20678..f15e2621fa185ee99fcd4de5219cd91feb0aa470 100644 --- a/drivers/clk/actions/owl-factor.c +++ b/drivers/clk/actions/owl-factor.c @@ -64,11 +64,10 @@ static unsigned int _get_table_val(const struct clk_factor_table *table, return val; } -static int clk_val_best(struct clk_hw *hw, unsigned long rate, +static int owl_clk_val_best(const struct owl_factor_hw *factor_hw, + struct clk_hw *hw, unsigned long rate, unsigned long *best_parent_rate) { - struct owl_factor *factor = hw_to_owl_factor(hw); - struct owl_factor_hw *factor_hw = &factor->factor_hw; const struct clk_factor_table *clkt = factor_hw->table; unsigned long parent_rate, try_parent_rate, best = 0, cur_rate; unsigned long parent_rate_saved = *best_parent_rate; @@ -126,7 +125,7 @@ long owl_factor_helper_round_rate(struct owl_clk_common *common, const struct clk_factor_table *clkt = factor_hw->table; unsigned int val, mul = 0, div = 1; - val = clk_val_best(&common->hw, rate, parent_rate); + val = owl_clk_val_best(factor_hw, &common->hw, rate, parent_rate); _get_table_div_mul(clkt, val, &mul, &div); return *parent_rate * mul / div; diff --git a/drivers/clk/clk-highbank.c b/drivers/clk/clk-highbank.c index 727ed8e1bb726fd424f97427507abe6f46fe84a9..8e4581004695c44f11ea66250773c60720f4bbb8 100644 --- a/drivers/clk/clk-highbank.c +++ b/drivers/clk/clk-highbank.c @@ -293,6 +293,7 @@ static __init struct clk *hb_clk_init(struct device_node *node, const struct clk /* Map system registers */ srnp = of_find_compatible_node(NULL, NULL, "calxeda,hb-sregs"); hb_clk->reg = of_iomap(srnp, 0); + of_node_put(srnp); BUG_ON(!hb_clk->reg); hb_clk->reg += reg; diff --git a/drivers/clk/clk-qoriq.c b/drivers/clk/clk-qoriq.c index 8abc5c8cb8b8c2535bc355f9c03ed7c9cd234b57..a0713b2a12f376c756f68ddfb9abf04e1d6b1a89 100644 --- a/drivers/clk/clk-qoriq.c +++ b/drivers/clk/clk-qoriq.c @@ -1389,6 +1389,7 @@ static void __init clockgen_init(struct device_node *np) pr_err("%s: Couldn't map %pOF regs\n", __func__, guts); } + of_node_put(guts); } } diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index c372a2418b3d6dfaf222a0f28d043e8325f041bc..5b463b486ec00b8952792ae32571db877fbfc484 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -82,6 +82,7 @@ struct clk_core { struct clk_core *new_child; unsigned long flags; bool orphan; + bool rpm_enabled; bool need_sync; bool boot_enabled; unsigned int enable_count; @@ -127,9 +128,9 @@ struct clk { /*** runtime pm ***/ static int clk_pm_runtime_get(struct clk_core *core) { - int ret = 0; + int ret; - if (!core->dev) + if (!core->rpm_enabled) return 0; ret = pm_runtime_get_sync(core->dev); @@ -138,7 +139,7 @@ static int clk_pm_runtime_get(struct clk_core *core) static void clk_pm_runtime_put(struct clk_core *core) { - if (!core->dev) + if (!core->rpm_enabled) return; pm_runtime_put_sync(core->dev); @@ -258,7 +259,7 @@ static bool clk_core_is_enabled(struct clk_core *core) * taking enable spinlock, but the below check is needed if one tries * to call it from other places. */ - if (core->dev) { + if (core->rpm_enabled) { pm_runtime_get_noresume(core->dev); if (!pm_runtime_active(core->dev)) { ret = false; @@ -268,7 +269,7 @@ static bool clk_core_is_enabled(struct clk_core *core) ret = core->ops->is_enabled(core->hw); done: - if (core->dev) + if (core->rpm_enabled) pm_runtime_put(core->dev); return ret; @@ -4187,11 +4188,17 @@ static int __clk_core_init(struct clk_core *core) if (core->flags & CLK_IS_CRITICAL) { unsigned long flags; - clk_core_prepare(core); + ret = clk_core_prepare(core); + if (ret) + goto out; flags = clk_enable_lock(); - clk_core_enable(core); + ret = clk_core_enable(core); clk_enable_unlock(flags); + if (ret) { + clk_core_unprepare(core); + goto out; + } } clk_core_hold_state(core); @@ -4354,7 +4361,8 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw) core->ops = hw->init->ops; if (dev && pm_runtime_enabled(dev)) - core->dev = dev; + core->rpm_enabled = true; + core->dev = dev; if (dev && dev->driver) core->owner = dev->driver->owner; core->hw = hw; diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c index c509324f63385c3690540bf4966865226fbbdbdb..8ddf9b1f1677cd3957361a2abdedb5806c90aa06 100644 --- a/drivers/clk/imx/clk-imx6q.c +++ b/drivers/clk/imx/clk-imx6q.c @@ -424,6 +424,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-anatop"); anatop_base = base = of_iomap(np, 0); WARN_ON(!base); + of_node_put(np); /* Audio/video PLL post dividers do not work on i.MX6q revision 1.0 */ if (clk_on_imx6q() && imx_get_soc_revision() == IMX_CHIP_REVISION_1_0) { diff --git a/drivers/clk/imx/clk-imx6sx.c b/drivers/clk/imx/clk-imx6sx.c index d9f2890ffe62bea5a42c2c3a485285e002cbc613..57ab96a477756d200586bbde9c6a8dce78e416ff 100644 --- a/drivers/clk/imx/clk-imx6sx.c +++ b/drivers/clk/imx/clk-imx6sx.c @@ -151,6 +151,7 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node) np = of_find_compatible_node(NULL, NULL, "fsl,imx6sx-anatop"); base = of_iomap(np, 0); WARN_ON(!base); + of_node_put(np); clks[IMX6SX_PLL1_BYPASS_SRC] = imx_clk_mux("pll1_bypass_src", base + 0x00, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); clks[IMX6SX_PLL2_BYPASS_SRC] = imx_clk_mux("pll2_bypass_src", base + 0x30, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); diff --git a/drivers/clk/imx/clk-imx7d.c b/drivers/clk/imx/clk-imx7d.c index 881b772c4ac9708c20371d8276475e55997cee85..83412bc36ebfb75aebd9eda89e86d2aa07ca9005 100644 --- a/drivers/clk/imx/clk-imx7d.c +++ b/drivers/clk/imx/clk-imx7d.c @@ -413,6 +413,7 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node) np = of_find_compatible_node(NULL, NULL, "fsl,imx7d-anatop"); base = of_iomap(np, 0); WARN_ON(!base); + of_node_put(np); clks[IMX7D_PLL_ARM_MAIN_SRC] = imx_clk_mux("pll_arm_main_src", base + 0x60, 14, 2, pll_bypass_src_sel, ARRAY_SIZE(pll_bypass_src_sel)); clks[IMX7D_PLL_DRAM_MAIN_SRC] = imx_clk_mux("pll_dram_main_src", base + 0x70, 14, 2, pll_bypass_src_sel, ARRAY_SIZE(pll_bypass_src_sel)); diff --git a/drivers/clk/imx/clk-vf610.c b/drivers/clk/imx/clk-vf610.c index 6dae54325a91ddb32bb733b9d7d5cf48d0ecc023..a334667c450a1edf446e301e6be13b375a64053d 100644 --- a/drivers/clk/imx/clk-vf610.c +++ b/drivers/clk/imx/clk-vf610.c @@ -203,6 +203,7 @@ static void __init vf610_clocks_init(struct device_node *ccm_node) np = of_find_compatible_node(NULL, NULL, "fsl,vf610-anatop"); anatop_base = of_iomap(np, 0); BUG_ON(!anatop_base); + of_node_put(np); np = ccm_node; ccm_base = of_iomap(np, 0); diff --git a/drivers/clk/ingenic/jz4740-cgu.c b/drivers/clk/ingenic/jz4740-cgu.c index 4479c102e8994bdb414038d117adc9544c971e6c..b86edd3282493388590e979eea386cd8b8cdbef9 100644 --- a/drivers/clk/ingenic/jz4740-cgu.c +++ b/drivers/clk/ingenic/jz4740-cgu.c @@ -165,7 +165,7 @@ static const struct ingenic_cgu_clk_info jz4740_cgu_clocks[] = { .parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL_HALF, -1, -1 }, .mux = { CGU_REG_CPCCR, 29, 1 }, .div = { CGU_REG_CPCCR, 23, 1, 6, -1, -1, -1 }, - .gate = { CGU_REG_SCR, 6 }, + .gate = { CGU_REG_SCR, 6, true }, }, /* Gate-only clocks */ diff --git a/drivers/clk/meson/axg.c b/drivers/clk/meson/axg.c index 02229d051d77879f0ebf19842c0e13e8695c39b7..4e7dac24948b8c9dfdd5f0bb6c455235abb8e885 100644 --- a/drivers/clk/meson/axg.c +++ b/drivers/clk/meson/axg.c @@ -461,11 +461,6 @@ static struct clk_regmap axg_mpll0_div = { .shift = 16, .width = 9, }, - .ssen = { - .reg_off = HHI_MPLL_CNTL, - .shift = 25, - .width = 1, - }, .misc = { .reg_off = HHI_PLL_TOP_MISC, .shift = 0, @@ -560,6 +555,11 @@ static struct clk_regmap axg_mpll2_div = { .shift = 16, .width = 9, }, + .ssen = { + .reg_off = HHI_MPLL_CNTL, + .shift = 25, + .width = 1, + }, .misc = { .reg_off = HHI_PLL_TOP_MISC, .shift = 2, diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c index b039909e03cf855e53874f6d871744b1dc715377..38ffa51a5baded35f7df80da41843e8cd9901b3b 100644 --- a/drivers/clk/meson/gxbb.c +++ b/drivers/clk/meson/gxbb.c @@ -650,11 +650,6 @@ static struct clk_regmap gxbb_mpll0_div = { .shift = 16, .width = 9, }, - .ssen = { - .reg_off = HHI_MPLL_CNTL, - .shift = 25, - .width = 1, - }, .lock = &meson_clk_lock, }, .hw.init = &(struct clk_init_data){ diff --git a/drivers/clk/mmp/clk-of-mmp2.c b/drivers/clk/mmp/clk-of-mmp2.c index d083b860f08333ad1caf8082664efdeaf1e0099d..10689d8cd3867024b6e83ec4fd37fcd694090cb7 100644 --- a/drivers/clk/mmp/clk-of-mmp2.c +++ b/drivers/clk/mmp/clk-of-mmp2.c @@ -134,7 +134,7 @@ static DEFINE_SPINLOCK(ssp3_lock); static const char *ssp_parent_names[] = {"vctcxo_4", "vctcxo_2", "vctcxo", "pll1_16"}; static DEFINE_SPINLOCK(timer_lock); -static const char *timer_parent_names[] = {"clk32", "vctcxo_2", "vctcxo_4", "vctcxo"}; +static const char *timer_parent_names[] = {"clk32", "vctcxo_4", "vctcxo_2", "vctcxo"}; static DEFINE_SPINLOCK(reset_lock); diff --git a/drivers/clk/mvebu/armada-370.c b/drivers/clk/mvebu/armada-370.c index 2c7c1085f88300ca84ac529831876af25376a047..8fdfa97900cd86e15863beb62be4a288cad9549f 100644 --- a/drivers/clk/mvebu/armada-370.c +++ b/drivers/clk/mvebu/armada-370.c @@ -177,8 +177,10 @@ static void __init a370_clk_init(struct device_node *np) mvebu_coreclk_setup(np, &a370_coreclks); - if (cgnp) + if (cgnp) { mvebu_clk_gating_setup(cgnp, a370_gating_desc); + of_node_put(cgnp); + } } CLK_OF_DECLARE(a370_clk, "marvell,armada-370-core-clock", a370_clk_init); diff --git a/drivers/clk/mvebu/armada-xp.c b/drivers/clk/mvebu/armada-xp.c index 0ec44ae9a2a2676ea383925d3841cffa0dd93a67..df529982adc972868e90b7cb4f39392244f4397d 100644 --- a/drivers/clk/mvebu/armada-xp.c +++ b/drivers/clk/mvebu/armada-xp.c @@ -228,7 +228,9 @@ static void __init axp_clk_init(struct device_node *np) mvebu_coreclk_setup(np, &axp_coreclks); - if (cgnp) + if (cgnp) { mvebu_clk_gating_setup(cgnp, axp_gating_desc); + of_node_put(cgnp); + } } CLK_OF_DECLARE(axp_clk, "marvell,armada-xp-core-clock", axp_clk_init); diff --git a/drivers/clk/mvebu/dove.c b/drivers/clk/mvebu/dove.c index 59fad9546c847946d71bd8348dcff615f936e0e4..5f258c9bb68bfc72a830d8ac56e428e75ccc19e0 100644 --- a/drivers/clk/mvebu/dove.c +++ b/drivers/clk/mvebu/dove.c @@ -190,10 +190,14 @@ static void __init dove_clk_init(struct device_node *np) mvebu_coreclk_setup(np, &dove_coreclks); - if (ddnp) + if (ddnp) { dove_divider_clk_init(ddnp); + of_node_put(ddnp); + } - if (cgnp) + if (cgnp) { mvebu_clk_gating_setup(cgnp, dove_gating_desc); + of_node_put(cgnp); + } } CLK_OF_DECLARE(dove_clk, "marvell,dove-core-clock", dove_clk_init); diff --git a/drivers/clk/mvebu/kirkwood.c b/drivers/clk/mvebu/kirkwood.c index a2a8d614039da91a1b3f99272aa98e5952b79037..890ebf623261b78d237881eb39a51c51e9074a73 100644 --- a/drivers/clk/mvebu/kirkwood.c +++ b/drivers/clk/mvebu/kirkwood.c @@ -333,6 +333,8 @@ static void __init kirkwood_clk_init(struct device_node *np) if (cgnp) { mvebu_clk_gating_setup(cgnp, kirkwood_gating_desc); kirkwood_clk_muxing_setup(cgnp, kirkwood_mux_desc); + + of_node_put(cgnp); } } CLK_OF_DECLARE(kirkwood_clk, "marvell,kirkwood-core-clock", diff --git a/drivers/clk/mvebu/mv98dx3236.c b/drivers/clk/mvebu/mv98dx3236.c index 6e203af73cac1dff2fd409c7700a02376186085a..c8a0d03d2cd60c78d416cd57e2a0be3de5999f54 100644 --- a/drivers/clk/mvebu/mv98dx3236.c +++ b/drivers/clk/mvebu/mv98dx3236.c @@ -174,7 +174,9 @@ static void __init mv98dx3236_clk_init(struct device_node *np) mvebu_coreclk_setup(np, &mv98dx3236_core_clocks); - if (cgnp) + if (cgnp) { mvebu_clk_gating_setup(cgnp, mv98dx3236_gating_desc); + of_node_put(cgnp); + } } CLK_OF_DECLARE(mv98dx3236_clk, "marvell,mv98dx3236-core-clock", mv98dx3236_clk_init); diff --git a/drivers/clk/qcom/camcc-lagoon.c b/drivers/clk/qcom/camcc-lagoon.c index a735541044df77b49c18c4802a42d882a1cc9a9c..7ec55d0a8d5e519e0bfc74e7bb77c57a6dfdb510 100644 --- a/drivers/clk/qcom/camcc-lagoon.c +++ b/drivers/clk/qcom/camcc-lagoon.c @@ -179,6 +179,7 @@ static struct pll_vco fabia_vco[] = { /* 600MHz configuration */ static const struct alpha_pll_config cam_cc_pll0_config = { .l = 0x1F, + .cal_l = 0x22, .alpha = 0x4000, .config_ctl_val = 0x20485699, .config_ctl_hi_val = 0x00002067, @@ -234,6 +235,7 @@ static struct clk_alpha_pll_postdiv cam_cc_pll0_out_even = { /* 808MHz configuration */ static const struct alpha_pll_config cam_cc_pll1_config = { .l = 0x2A, + .cal_l = 0x2C, .alpha = 0x1555, .config_ctl_val = 0x20485699, .config_ctl_hi_val = 0x00002067, @@ -357,6 +359,7 @@ static struct clk_alpha_pll_postdiv cam_cc_pll2_out_main = { /* 384MHz configuration */ static const struct alpha_pll_config cam_cc_pll3_config = { .l = 0x14, + .cal_l = 0x16, .alpha = 0x0, .config_ctl_val = 0x20485699, .config_ctl_hi_val = 0x00002067, diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c index cef64f9c5811a1994e7b5745402e4dca477b13be..50756cdbdbcb7bd58839bd7516efb9c3614fa190 100644 --- a/drivers/clk/qcom/clk-alpha-pll.c +++ b/drivers/clk/qcom/clk-alpha-pll.c @@ -904,12 +904,20 @@ static int alpha_pll_huayra_set_rate(struct clk_hw *hw, unsigned long rate, return 0; } -static long alpha_pll_huayra_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static int alpha_pll_huayra_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { + unsigned long rrate, prate; u32 l, a; - return alpha_huayra_pll_round_rate(rate, *prate, &l, &a); + prate = clk_hw_get_rate(clk_hw_get_parent(hw)); + rrate = alpha_huayra_pll_round_rate(req->rate, prate, &l, &a); + + req->best_parent_hw = clk_hw_get_parent(hw); + req->best_parent_rate = prate; + req->rate = rrate; + + return 0; } static void clk_huayra_pll_list_registers(struct seq_file *f, struct clk_hw *hw) @@ -1303,7 +1311,7 @@ const struct clk_ops clk_alpha_pll_huayra_ops = { .disable = clk_alpha_pll_disable, .is_enabled = clk_alpha_pll_is_enabled, .recalc_rate = alpha_pll_huayra_recalc_rate, - .round_rate = alpha_pll_huayra_round_rate, + .determine_rate = alpha_pll_huayra_determine_rate, .set_rate = alpha_pll_huayra_set_rate, .list_registers = clk_huayra_pll_list_registers, .bus_vote = clk_debug_bus_vote, diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h index 7993000c2566b082590b91796a15fca5aeea8234..1917001a343b7bd0c00d5ad5cc090915907ec458 100644 --- a/drivers/clk/qcom/clk-rcg.h +++ b/drivers/clk/qcom/clk-rcg.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0-only */ -/* Copyright (c) 2013, 2018-2019, The Linux Foundation. All rights reserved. */ +/* Copyright (c) 2013, 2018-2020, The Linux Foundation. All rights reserved. */ #ifndef __QCOM_CLK_RCG_H__ #define __QCOM_CLK_RCG_H__ @@ -173,6 +173,7 @@ extern const struct clk_ops clk_gfx3d_ops; extern const struct clk_ops clk_rcg2_shared_ops; extern const struct clk_ops clk_dp_ops; extern const struct clk_ops clk_rcg2_dependent_ops; +extern const struct clk_ops clk_gfx3d_src_ops; struct clk_rcg_dfs_data { struct clk_rcg2 *rcg; diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c index c33d2b6b8ab34dbb872e0b78575bc13e86116d41..1610148295d139071a4ad97c86220b6a7f7e1c62 100644 --- a/drivers/clk/qcom/clk-rcg2.c +++ b/drivers/clk/qcom/clk-rcg2.c @@ -239,19 +239,19 @@ static void disable_unprepare_rcg_srcs(struct clk *curr, struct clk *new) static unsigned long calc_rate(unsigned long rate, u32 m, u32 n, u32 mode, u32 hid_div) { + u64 tmp = rate; + if (hid_div) { - rate *= 2; - rate /= hid_div + 1; + tmp *= 2; + do_div(tmp, hid_div + 1); } if (mode) { - u64 tmp = rate; tmp *= m; do_div(tmp, n); - rate = tmp; } - return rate; + return tmp; } static unsigned long @@ -342,6 +342,7 @@ static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f, p = clk_hw_get_parent_by_index(hw, index); if (!p) return -EINVAL; + if (clk_flags & CLK_SET_RATE_PARENT) { rate = f->freq; if (f->pre_div) { @@ -1298,6 +1299,91 @@ const struct clk_ops clk_gfx3d_ops = { }; EXPORT_SYMBOL_GPL(clk_gfx3d_ops); +static int clk_gfx3d_src_set_rate_and_parent(struct clk_hw *hw, + unsigned long rate, unsigned long parent_rate, u8 index) +{ + struct clk_rcg2 *rcg = to_clk_rcg2(hw); + const struct freq_tbl *f; + u32 cfg, old_cfg; + int ret; + + /* Read back the old configuration */ + regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &old_cfg); + + cfg = rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT; + + f = qcom_find_freq(rcg->freq_tbl, rate); + if (!f) + return -EINVAL; + + /* Update the RCG-DIV */ + cfg |= f->pre_div << CFG_SRC_DIV_SHIFT; + + ret = regmap_write(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, cfg); + if (ret) + return ret; + + return update_config(rcg, old_cfg); +} + +static int clk_gfx3d_src_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_rcg2 *rcg = to_clk_rcg2(hw); + struct clk_rate_request parent_req = { }; + struct clk_hw *p1, *p3, *xo, *curr_p; + const struct freq_tbl *f; + int ret; + + xo = clk_hw_get_parent_by_index(hw, 0); + if (req->rate == clk_hw_get_rate(xo)) { + req->best_parent_hw = xo; + req->best_parent_rate = req->rate; + return 0; + } + + f = qcom_find_freq(rcg->freq_tbl, req->rate); + if (!f || (req->rate != f->freq)) + req->rate = f->freq; + + /* Indexes of source from the parent map */ + p1 = clk_hw_get_parent_by_index(hw, 1); + p3 = clk_hw_get_parent_by_index(hw, 2); + + curr_p = clk_hw_get_parent(hw); + parent_req.rate = f->src_freq; + + if (curr_p == xo || curr_p == p3) + req->best_parent_hw = p1; + else if (curr_p == p1) + req->best_parent_hw = p3; + + parent_req.best_parent_hw = req->best_parent_hw; + + ret = __clk_determine_rate(req->best_parent_hw, &parent_req); + if (ret) + return ret; + + req->best_parent_rate = parent_req.rate; + + return 0; +} + +const struct clk_ops clk_gfx3d_src_ops = { + .enable = clk_rcg2_enable, + .disable = clk_rcg2_disable, + .is_enabled = clk_rcg2_is_enabled, + .get_parent = clk_rcg2_get_parent, + .set_parent = clk_rcg2_set_parent, + .recalc_rate = clk_rcg2_recalc_rate, + .set_rate = clk_gfx3d_set_rate, + .set_rate_and_parent = clk_gfx3d_src_set_rate_and_parent, + .determine_rate = clk_gfx3d_src_determine_rate, + .list_rate = clk_rcg2_list_rate, + .list_registers = clk_rcg2_list_registers, +}; +EXPORT_SYMBOL(clk_gfx3d_src_ops); + static int clk_rcg2_shared_force_enable_clear(struct clk_hw *hw, const struct freq_tbl *f) { diff --git a/drivers/clk/qcom/dispcc-lagoon.c b/drivers/clk/qcom/dispcc-lagoon.c index fb88c51f7517962a0a2aac1e349ad3270de767a4..4b020036baf62f5d66d8d18c299c1ec5920086a2 100644 --- a/drivers/clk/qcom/dispcc-lagoon.c +++ b/drivers/clk/qcom/dispcc-lagoon.c @@ -120,7 +120,7 @@ static struct pll_vco fabia_vco[] = { static const struct alpha_pll_config disp_cc_pll0_config = { .l = 0x3A, - .cal_l = 0x3F, + .cal_l = 0x31, .alpha = 0x5555, .config_ctl_val = 0x20485699, .config_ctl_hi_val = 0x00002067, diff --git a/drivers/clk/qcom/gcc-bengal.c b/drivers/clk/qcom/gcc-bengal.c index 95137f6f44826b15b252345cdaf67264d628b981..6bb7c906e66749b632faaf19920b09665afc260a 100644 --- a/drivers/clk/qcom/gcc-bengal.c +++ b/drivers/clk/qcom/gcc-bengal.c @@ -1082,7 +1082,7 @@ static const struct freq_tbl ftbl_gcc_camss_ope_clk_src[] = { F(200000000, P_GPLL8_OUT_MAIN, 2, 0, 0), F(266600000, P_GPLL8_OUT_MAIN, 1, 0, 0), F(465000000, P_GPLL8_OUT_MAIN, 1, 0, 0), - F(580000000, P_GPLL8_OUT_EARLY, 1, 0, 0), + F(576000000, P_GPLL9_OUT_MAIN, 1, 0, 0), { } }; diff --git a/drivers/clk/qcom/gcc-msm8996.c b/drivers/clk/qcom/gcc-msm8996.c index 9a3290fdd01b18eb0b8eb6a7ab90a16550fd9c3c..bea55c461cee97bb8ac794cde09a18e731df87dc 100644 --- a/drivers/clk/qcom/gcc-msm8996.c +++ b/drivers/clk/qcom/gcc-msm8996.c @@ -138,22 +138,6 @@ static const char * const gcc_xo_gpll0_gpll4_gpll0_early_div[] = { "gpll0_early_div" }; -static const struct parent_map gcc_xo_gpll0_gpll2_gpll3_gpll0_early_div_map[] = { - { P_XO, 0 }, - { P_GPLL0, 1 }, - { P_GPLL2, 2 }, - { P_GPLL3, 3 }, - { P_GPLL0_EARLY_DIV, 6 } -}; - -static const char * const gcc_xo_gpll0_gpll2_gpll3_gpll0_early_div[] = { - "xo", - "gpll0", - "gpll2", - "gpll3", - "gpll0_early_div" -}; - static const struct parent_map gcc_xo_gpll0_gpll1_early_div_gpll1_gpll4_gpll0_early_div_map[] = { { P_XO, 0 }, { P_GPLL0, 1 }, @@ -192,26 +176,6 @@ static const char * const gcc_xo_gpll0_gpll2_gpll3_gpll1_gpll2_early_gpll0_early "gpll0_early_div" }; -static const struct parent_map gcc_xo_gpll0_gpll2_gpll3_gpll1_gpll4_gpll0_early_div_map[] = { - { P_XO, 0 }, - { P_GPLL0, 1 }, - { P_GPLL2, 2 }, - { P_GPLL3, 3 }, - { P_GPLL1, 4 }, - { P_GPLL4, 5 }, - { P_GPLL0_EARLY_DIV, 6 } -}; - -static const char * const gcc_xo_gpll0_gpll2_gpll3_gpll1_gpll4_gpll0_early_div[] = { - "xo", - "gpll0", - "gpll2", - "gpll3", - "gpll1", - "gpll4", - "gpll0_early_div" -}; - static struct clk_fixed_factor xo = { .mult = 1, .div = 1, diff --git a/drivers/clk/qcom/gcc-msm8998.c b/drivers/clk/qcom/gcc-msm8998.c index 4e23973b6cd161354193e61cd6bf6b65446f0d89..772a08101ddf2f234aab0e99cc8b42ccb847a8af 100644 --- a/drivers/clk/qcom/gcc-msm8998.c +++ b/drivers/clk/qcom/gcc-msm8998.c @@ -2144,7 +2144,7 @@ static struct clk_branch gcc_pcie_0_mstr_axi_clk = { static struct clk_branch gcc_pcie_0_pipe_clk = { .halt_reg = 0x6b018, - .halt_check = BRANCH_HALT, + .halt_check = BRANCH_HALT_SKIP, .clkr = { .enable_reg = 0x6b018, .enable_mask = BIT(0), diff --git a/drivers/clk/qcom/gcc-sdm845.c b/drivers/clk/qcom/gcc-sdm845.c index ada3e4aeb38f96d039dac687a44bd80bf8e28d2b..6bd96ddadbe311bd338fe41e5e301b94fa6563c8 100644 --- a/drivers/clk/qcom/gcc-sdm845.c +++ b/drivers/clk/qcom/gcc-sdm845.c @@ -3150,6 +3150,7 @@ static struct gdsc hlos1_vote_aggre_noc_mmu_audio_tbu_gdsc = { .name = "hlos1_vote_aggre_noc_mmu_audio_tbu_gdsc", }, .pwrsts = PWRSTS_OFF_ON, + .flags = VOTABLE, }; static struct gdsc hlos1_vote_aggre_noc_mmu_pcie_tbu_gdsc = { @@ -3158,6 +3159,7 @@ static struct gdsc hlos1_vote_aggre_noc_mmu_pcie_tbu_gdsc = { .name = "hlos1_vote_aggre_noc_mmu_pcie_tbu_gdsc", }, .pwrsts = PWRSTS_OFF_ON, + .flags = VOTABLE, }; static struct gdsc hlos1_vote_aggre_noc_mmu_tbu1_gdsc = { @@ -3166,6 +3168,7 @@ static struct gdsc hlos1_vote_aggre_noc_mmu_tbu1_gdsc = { .name = "hlos1_vote_aggre_noc_mmu_tbu1_gdsc", }, .pwrsts = PWRSTS_OFF_ON, + .flags = VOTABLE, }; static struct gdsc hlos1_vote_aggre_noc_mmu_tbu2_gdsc = { @@ -3174,6 +3177,7 @@ static struct gdsc hlos1_vote_aggre_noc_mmu_tbu2_gdsc = { .name = "hlos1_vote_aggre_noc_mmu_tbu2_gdsc", }, .pwrsts = PWRSTS_OFF_ON, + .flags = VOTABLE, }; static struct gdsc hlos1_vote_mmnoc_mmu_tbu_hf0_gdsc = { @@ -3182,6 +3186,7 @@ static struct gdsc hlos1_vote_mmnoc_mmu_tbu_hf0_gdsc = { .name = "hlos1_vote_mmnoc_mmu_tbu_hf0_gdsc", }, .pwrsts = PWRSTS_OFF_ON, + .flags = VOTABLE, }; static struct gdsc hlos1_vote_mmnoc_mmu_tbu_hf1_gdsc = { @@ -3190,6 +3195,7 @@ static struct gdsc hlos1_vote_mmnoc_mmu_tbu_hf1_gdsc = { .name = "hlos1_vote_mmnoc_mmu_tbu_hf1_gdsc", }, .pwrsts = PWRSTS_OFF_ON, + .flags = VOTABLE, }; static struct gdsc hlos1_vote_mmnoc_mmu_tbu_sf_gdsc = { @@ -3198,6 +3204,7 @@ static struct gdsc hlos1_vote_mmnoc_mmu_tbu_sf_gdsc = { .name = "hlos1_vote_mmnoc_mmu_tbu_sf_gdsc", }, .pwrsts = PWRSTS_OFF_ON, + .flags = VOTABLE, }; static struct clk_regmap *gcc_sdm845_clocks[] = { diff --git a/drivers/clk/qcom/gdsc-regulator.c b/drivers/clk/qcom/gdsc-regulator.c index e16a9e5baad6abe1228fdbf4d7c454b64acffa60..94ab96168b4151e464bb7b4082f418d23c95ad28 100644 --- a/drivers/clk/qcom/gdsc-regulator.c +++ b/drivers/clk/qcom/gdsc-regulator.c @@ -146,6 +146,9 @@ static int gdsc_is_enabled(struct regulator_dev *rdev) if (!sc->toggle_logic) return !sc->resets_asserted; + if (sc->skip_disable_before_enable) + return false; + if (sc->parent_regulator) { /* * The parent regulator for the GDSC is required to be on to @@ -209,6 +212,9 @@ static int gdsc_enable(struct regulator_dev *rdev) uint32_t regval, hw_ctrl_regval = 0x0; int i, ret = 0; + if (sc->skip_disable_before_enable) + return 0; + if (sc->parent_regulator) { ret = regulator_set_voltage(sc->parent_regulator, RPMH_REGULATOR_LEVEL_LOW_SVS, INT_MAX); @@ -367,7 +373,6 @@ static int gdsc_enable(struct regulator_dev *rdev) clk_disable_unprepare(sc->clocks[sc->root_clk_idx]); sc->is_gdsc_enabled = true; - sc->skip_disable_before_enable = false; end: if (ret && sc->bus_handle) { msm_bus_scale_client_update_request(sc->bus_handle, 0); @@ -386,16 +391,6 @@ static int gdsc_disable(struct regulator_dev *rdev) uint32_t regval; int i, ret = 0; - /* - * Protect GDSC against late_init disabling when the GDSC is enabled - * by an entity outside external to HLOS. - */ - if (sc->skip_disable_before_enable) { - dev_dbg(&rdev->dev, "Skip Disabling: %s\n", sc->rdesc.name); - sc->skip_disable_before_enable = false; - return 0; - } - if (sc->force_root_en) clk_prepare_enable(sc->clocks[sc->root_clk_idx]); diff --git a/drivers/clk/qcom/gpucc-lagoon.c b/drivers/clk/qcom/gpucc-lagoon.c index ec1bbcfe4414d99cdd9e94b8d5356320fa576ec8..1c74ecb99d3b44070c3a9141b1e99a4d03bc8b61 100644 --- a/drivers/clk/qcom/gpucc-lagoon.c +++ b/drivers/clk/qcom/gpucc-lagoon.c @@ -135,7 +135,7 @@ static struct clk_fixed_factor crc_div = { /* 514MHz Configuration*/ static const struct alpha_pll_config gpu_cc_pll1_config = { .l = 0x1A, - .cal_l = 0x3F, + .cal_l = 0x3D, .alpha = 0xC555, .config_ctl_val = 0x20485699, .config_ctl_hi_val = 0x00002067, diff --git a/drivers/clk/qcom/npucc-lagoon.c b/drivers/clk/qcom/npucc-lagoon.c index cd5adaaecf9f0e3252440974bcd96cfd249c4531..99c28e6826560cbf64d265ff1aebf60fa0e2b8ef 100644 --- a/drivers/clk/qcom/npucc-lagoon.c +++ b/drivers/clk/qcom/npucc-lagoon.c @@ -110,6 +110,7 @@ static struct pll_vco fabia_vco[] = { /* 537.60MHz Configuration */ static struct alpha_pll_config npu_cc_pll0_config = { .l = 0x1C, + .cal_l = 0x3F, .config_ctl_val = 0x20485699, .config_ctl_hi_val = 0x00002067, .test_ctl_val = 0x40000000, @@ -147,6 +148,7 @@ static struct clk_alpha_pll npu_cc_pll0 = { /* 300MHz Configuration */ static struct alpha_pll_config npu_cc_pll1_config = { .l = 0xF, + .cal_l = 0x33, .alpha = 0xA000, .config_ctl_val = 0x20485699, .config_ctl_hi_val = 0x00002067, @@ -182,6 +184,7 @@ static struct clk_alpha_pll npu_cc_pll1 = { /* 250MHz Configuration */ static struct alpha_pll_config npu_q6ss_pll_config = { .l = 0xD, + .cal_l = 0x1E, .alpha = 0x555, .config_ctl_val = 0x20485699, .config_ctl_hi_val = 0x00002067, @@ -228,11 +231,11 @@ static struct clk_fixed_factor npu_cc_crc_div = { static const struct freq_tbl ftbl_npu_cc_cal_hm0_clk_src[] = { F(100000000, P_NPU_CC_CRC_DIV, 1, 0, 0), + F(192000000, P_NPU_CC_CRC_DIV, 1, 0, 0), F(268800000, P_NPU_CC_CRC_DIV, 1, 0, 0), F(403200000, P_NPU_CC_CRC_DIV, 1, 0, 0), F(515000000, P_NPU_CC_CRC_DIV, 1, 0, 0), - F(650000000, P_NPU_CC_CRC_DIV, 1, 0, 0), - F(850000000, P_NPU_CC_CRC_DIV, 1, 0, 0), + F(748800000, P_NPU_CC_CRC_DIV, 1, 0, 0), { } }; @@ -253,11 +256,11 @@ static struct clk_rcg2 npu_cc_cal_hm0_clk_src = { .num_rate_max = VDD_NUM, .rate_max = (unsigned long[VDD_NUM]) { [VDD_MIN] = 100000000, - [VDD_LOWER] = 268800000, - [VDD_LOW] = 403200000, - [VDD_LOW_L1] = 515000000, - [VDD_NOMINAL] = 650000000, - [VDD_HIGH] = 850000000}, + [VDD_LOWER] = 192000000, + [VDD_LOW] = 268800000, + [VDD_LOW_L1] = 403200000, + [VDD_NOMINAL] = 515000000, + [VDD_HIGH] = 748800000}, }, }; diff --git a/drivers/clk/qcom/videocc-lagoon.c b/drivers/clk/qcom/videocc-lagoon.c index 32838a6f67d66f455124fd12d770c42069ffbef1..0f49c37f875622a76de7bbeb140624a19052ae4a 100644 --- a/drivers/clk/qcom/videocc-lagoon.c +++ b/drivers/clk/qcom/videocc-lagoon.c @@ -127,7 +127,6 @@ static struct clk_alpha_pll_postdiv video_pll0_out_even = { }; static const struct freq_tbl ftbl_video_cc_iris_clk_src[] = { - F(19200000, P_BI_TCXO, 1, 0, 0), F(133250000, P_VIDEO_PLL0_OUT_EVEN, 2, 0, 0), F(240000000, P_VIDEO_PLL0_OUT_EVEN, 1.5, 0, 0), F(300000000, P_VIDEO_PLL0_OUT_EVEN, 1, 0, 0), diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index 0421960eb96333048e709c4df6308c3e8338fb7b..442309b5692033f6527248cab302ee42af327817 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -1226,6 +1226,7 @@ static unsigned long __init exynos4_get_xom(void) xom = readl(chipid_base + 8); iounmap(chipid_base); + of_node_put(np); } return xom; diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c index d5af937212992e2308abc1e912e681e4af255f74..6473af8903c5847d796671b00f414db0c6f7c148 100644 --- a/drivers/clk/samsung/clk-exynos5420.c +++ b/drivers/clk/samsung/clk-exynos5420.c @@ -171,6 +171,8 @@ static const unsigned long exynos5x_clk_regs[] __initconst = { GATE_BUS_CPU, GATE_SCLK_CPU, CLKOUT_CMU_CPU, + APLL_CON0, + KPLL_CON0, CPLL_CON0, DPLL_CON0, EPLL_CON0, diff --git a/drivers/clk/socfpga/clk-pll-a10.c b/drivers/clk/socfpga/clk-pll-a10.c index 35fabe1a32c30e4b400a77205c892f150b3f5f7c..269467e8e07e13a38399899c75194a347873c658 100644 --- a/drivers/clk/socfpga/clk-pll-a10.c +++ b/drivers/clk/socfpga/clk-pll-a10.c @@ -95,6 +95,7 @@ static struct clk * __init __socfpga_pll_init(struct device_node *node, clkmgr_np = of_find_compatible_node(NULL, NULL, "altr,clk-mgr"); clk_mgr_a10_base_addr = of_iomap(clkmgr_np, 0); + of_node_put(clkmgr_np); BUG_ON(!clk_mgr_a10_base_addr); pll_clk->hw.reg = clk_mgr_a10_base_addr + reg; diff --git a/drivers/clk/socfpga/clk-pll.c b/drivers/clk/socfpga/clk-pll.c index c7f463172e4b93d0cec4b46e45558a301aa16234..b4b44e9b5901132a4849261d1563a6f3a797a469 100644 --- a/drivers/clk/socfpga/clk-pll.c +++ b/drivers/clk/socfpga/clk-pll.c @@ -100,6 +100,7 @@ static __init struct clk *__socfpga_pll_init(struct device_node *node, clkmgr_np = of_find_compatible_node(NULL, NULL, "altr,clk-mgr"); clk_mgr_base_addr = of_iomap(clkmgr_np, 0); + of_node_put(clkmgr_np); BUG_ON(!clk_mgr_base_addr); pll_clk->hw.reg = clk_mgr_base_addr + reg; diff --git a/drivers/clk/sprd/common.c b/drivers/clk/sprd/common.c index 8bdab1c3013b82859df8a0391b54c1f61bf607fa..c9b777592b2c8cfd187a0e294726535f93868d62 100644 --- a/drivers/clk/sprd/common.c +++ b/drivers/clk/sprd/common.c @@ -45,7 +45,7 @@ int sprd_clk_regmap_init(struct platform_device *pdev, if (of_find_property(node, "sprd,syscon", NULL)) { regmap = syscon_regmap_lookup_by_phandle(node, "sprd,syscon"); - if (IS_ERR_OR_NULL(regmap)) { + if (IS_ERR(regmap)) { pr_err("%s: failed to get syscon regmap\n", __func__); return PTR_ERR(regmap); } diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-a64.c b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c index dec4a130390a3d46e33543465e9a9dd40d374515..9ac6c299e0744304206c61ddea85d5dfef8f22f4 100644 --- a/drivers/clk/sunxi-ng/ccu-sun50i-a64.c +++ b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c @@ -901,11 +901,26 @@ static const struct sunxi_ccu_desc sun50i_a64_ccu_desc = { .num_resets = ARRAY_SIZE(sun50i_a64_ccu_resets), }; +static struct ccu_pll_nb sun50i_a64_pll_cpu_nb = { + .common = &pll_cpux_clk.common, + /* copy from pll_cpux_clk */ + .enable = BIT(31), + .lock = BIT(28), +}; + +static struct ccu_mux_nb sun50i_a64_cpu_nb = { + .common = &cpux_clk.common, + .cm = &cpux_clk.mux, + .delay_us = 1, /* > 8 clock cycles at 24 MHz */ + .bypass_index = 1, /* index of 24 MHz oscillator */ +}; + static int sun50i_a64_ccu_probe(struct platform_device *pdev) { struct resource *res; void __iomem *reg; u32 val; + int ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); reg = devm_ioremap_resource(&pdev->dev, res); @@ -919,7 +934,18 @@ static int sun50i_a64_ccu_probe(struct platform_device *pdev) writel(0x515, reg + SUN50I_A64_PLL_MIPI_REG); - return sunxi_ccu_probe(pdev->dev.of_node, reg, &sun50i_a64_ccu_desc); + ret = sunxi_ccu_probe(pdev->dev.of_node, reg, &sun50i_a64_ccu_desc); + if (ret) + return ret; + + /* Gate then ungate PLL CPU after any rate changes */ + ccu_pll_notifier_register(&sun50i_a64_pll_cpu_nb); + + /* Reparent CPU during PLL CPU rate changes */ + ccu_mux_notifier_register(pll_cpux_clk.common.hw.clk, + &sun50i_a64_cpu_nb); + + return 0; } static const struct of_device_id sun50i_a64_ccu_ids[] = { diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c b/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c index 27554eaf6929821af86a44162e0ee599cf80159c..28b84c701a7d229519861c9990073ff7f8817778 100644 --- a/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c +++ b/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c @@ -23,9 +23,9 @@ */ static const char * const ar100_r_apb2_parents[] = { "osc24M", "osc32k", - "pll-periph0", "iosc" }; + "iosc", "pll-periph0" }; static const struct ccu_mux_var_prediv ar100_r_apb2_predivs[] = { - { .index = 2, .shift = 0, .width = 5 }, + { .index = 3, .shift = 0, .width = 5 }, }; static struct ccu_div ar100_clk = { @@ -104,7 +104,7 @@ static SUNXI_CCU_GATE(r_apb2_i2c_clk, "r-apb2-i2c", "r-apb2", static SUNXI_CCU_GATE(r_apb1_ir_clk, "r-apb1-ir", "r-apb1", 0x1cc, BIT(0), 0); static SUNXI_CCU_GATE(r_apb1_w1_clk, "r-apb1-w1", "r-apb1", - 0x1cc, BIT(0), 0); + 0x1ec, BIT(0), 0); /* Information of IR(RX) mod clock is gathered from BSP source code */ static const char * const r_mod0_default_parents[] = { "osc32k", "osc24M" }; diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a23.c b/drivers/clk/sunxi-ng/ccu-sun8i-a23.c index a4fa2945f2302e8f3a41632b456db28018e0069b..4b5f8f4e4ab8c197f61b4fcde8b3e4fd7e1a5847 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-a23.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-a23.c @@ -144,7 +144,7 @@ static SUNXI_CCU_NKM_WITH_GATE_LOCK(pll_mipi_clk, "pll-mipi", 8, 4, /* N */ 4, 2, /* K */ 0, 4, /* M */ - BIT(31), /* gate */ + BIT(31) | BIT(23) | BIT(22), /* gate */ BIT(28), /* lock */ CLK_SET_RATE_UNGATE); diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c index 9e3f4088724b430f111c2996e8d5e51cf368877b..c7f9d974b10d55366df9af45843610f1f2f5e32f 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c @@ -84,7 +84,7 @@ static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_ve_clk, "pll-ve", BIT(28), /* lock */ 0); -static SUNXI_CCU_NKM_WITH_GATE_LOCK(pll_ddr_clk, "pll-ddr", +static SUNXI_CCU_NKM_WITH_GATE_LOCK(pll_ddr0_clk, "pll-ddr0", "osc24M", 0x020, 8, 5, /* N */ 4, 2, /* K */ @@ -123,6 +123,14 @@ static SUNXI_CCU_NK_WITH_GATE_LOCK_POSTDIV(pll_periph1_clk, "pll-periph1", 2, /* post-div */ 0); +static SUNXI_CCU_NM_WITH_GATE_LOCK(pll_ddr1_clk, "pll-ddr1", + "osc24M", 0x04c, + 8, 7, /* N */ + 0, 2, /* M */ + BIT(31), /* gate */ + BIT(28), /* lock */ + 0); + static const char * const cpu_parents[] = { "osc32k", "osc24M", "pll-cpu", "pll-cpu" }; static SUNXI_CCU_MUX(cpu_clk, "cpu", cpu_parents, @@ -310,7 +318,8 @@ static SUNXI_CCU_GATE(usb_phy0_clk, "usb-phy0", "osc24M", static SUNXI_CCU_GATE(usb_ohci0_clk, "usb-ohci0", "osc24M", 0x0cc, BIT(16), 0); -static const char * const dram_parents[] = { "pll-ddr", "pll-periph0-2x" }; +static const char * const dram_parents[] = { "pll-ddr0", "pll-ddr1", + "pll-periph0-2x" }; static SUNXI_CCU_M_WITH_MUX(dram_clk, "dram", dram_parents, 0x0f4, 0, 4, 20, 2, CLK_IS_CRITICAL); @@ -369,10 +378,11 @@ static struct ccu_common *sun8i_v3s_ccu_clks[] = { &pll_audio_base_clk.common, &pll_video_clk.common, &pll_ve_clk.common, - &pll_ddr_clk.common, + &pll_ddr0_clk.common, &pll_periph0_clk.common, &pll_isp_clk.common, &pll_periph1_clk.common, + &pll_ddr1_clk.common, &cpu_clk.common, &axi_clk.common, &ahb1_clk.common, @@ -457,11 +467,12 @@ static struct clk_hw_onecell_data sun8i_v3s_hw_clks = { [CLK_PLL_AUDIO_8X] = &pll_audio_8x_clk.hw, [CLK_PLL_VIDEO] = &pll_video_clk.common.hw, [CLK_PLL_VE] = &pll_ve_clk.common.hw, - [CLK_PLL_DDR] = &pll_ddr_clk.common.hw, + [CLK_PLL_DDR0] = &pll_ddr0_clk.common.hw, [CLK_PLL_PERIPH0] = &pll_periph0_clk.common.hw, [CLK_PLL_PERIPH0_2X] = &pll_periph0_2x_clk.hw, [CLK_PLL_ISP] = &pll_isp_clk.common.hw, [CLK_PLL_PERIPH1] = &pll_periph1_clk.common.hw, + [CLK_PLL_DDR1] = &pll_ddr1_clk.common.hw, [CLK_CPU] = &cpu_clk.common.hw, [CLK_AXI] = &axi_clk.common.hw, [CLK_AHB1] = &ahb1_clk.common.hw, diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-v3s.h b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.h index 4a4d36fdad9601b2d9139a3a55339f704402d387..a091b7217dfd5806d36008139fc5ad07d05b37a9 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-v3s.h +++ b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.h @@ -29,7 +29,7 @@ #define CLK_PLL_AUDIO_8X 5 #define CLK_PLL_VIDEO 6 #define CLK_PLL_VE 7 -#define CLK_PLL_DDR 8 +#define CLK_PLL_DDR0 8 #define CLK_PLL_PERIPH0 9 #define CLK_PLL_PERIPH0_2X 10 #define CLK_PLL_ISP 11 @@ -58,6 +58,8 @@ /* And the GPU module clock is exported */ -#define CLK_NUMBER (CLK_MIPI_CSI + 1) +#define CLK_PLL_DDR1 74 + +#define CLK_NUMBER (CLK_PLL_DDR1 + 1) #endif /* _CCU_SUN8I_H3_H_ */ diff --git a/drivers/clk/tegra/clk-tegra-periph.c b/drivers/clk/tegra/clk-tegra-periph.c index 38c4eb28c8bf692ed4ff28514e8bef3916d46c6e..b137c5d34eec49561845b3a72e2b81a50c550518 100644 --- a/drivers/clk/tegra/clk-tegra-periph.c +++ b/drivers/clk/tegra/clk-tegra-periph.c @@ -799,7 +799,11 @@ static struct tegra_periph_init_data gate_clks[] = { GATE("ahbdma", "hclk", 33, 0, tegra_clk_ahbdma, 0), GATE("apbdma", "pclk", 34, 0, tegra_clk_apbdma, 0), GATE("kbc", "clk_32k", 36, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, tegra_clk_kbc, 0), - GATE("fuse", "clk_m", 39, TEGRA_PERIPH_ON_APB, tegra_clk_fuse, 0), + /* + * Critical for RAM re-repair operation, which must occur on resume + * from LP1 system suspend and as part of CCPLEX cluster switching. + */ + GATE("fuse", "clk_m", 39, TEGRA_PERIPH_ON_APB, tegra_clk_fuse, CLK_IS_CRITICAL), GATE("fuse_burn", "clk_m", 39, TEGRA_PERIPH_ON_APB, tegra_clk_fuse_burn, 0), GATE("kfuse", "clk_m", 40, TEGRA_PERIPH_ON_APB, tegra_clk_kfuse, 0), GATE("apbif", "clk_m", 107, TEGRA_PERIPH_ON_APB, tegra_clk_apbif, 0), diff --git a/drivers/clk/ti/clk.c b/drivers/clk/ti/clk.c index 27e0979b315860dbddf032d11e7826891ede1bd9..0cc87c6ae91c96dc3ae57c92da9989430600825f 100644 --- a/drivers/clk/ti/clk.c +++ b/drivers/clk/ti/clk.c @@ -188,9 +188,13 @@ void __init ti_dt_clocks_register(struct ti_dt_clk oclks[]) clkdev_add(&c->lk); } else { if (num_args && !has_clkctrl_data) { - if (of_find_compatible_node(NULL, NULL, - "ti,clkctrl")) { + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, + "ti,clkctrl"); + if (np) { has_clkctrl_data = true; + of_node_put(np); } else { clkctrl_nodes_missing = true; diff --git a/drivers/clk/uniphier/clk-uniphier-peri.c b/drivers/clk/uniphier/clk-uniphier-peri.c index 89b3ac378b3f963286f10b03ab1c1a5538ba8d04..8b75dc116a98c762402633b56a00ad1976268fa6 100644 --- a/drivers/clk/uniphier/clk-uniphier-peri.c +++ b/drivers/clk/uniphier/clk-uniphier-peri.c @@ -27,8 +27,8 @@ #define UNIPHIER_PERI_CLK_FI2C(idx, ch) \ UNIPHIER_CLK_GATE("i2c" #ch, (idx), "i2c", 0x24, 24 + (ch)) -#define UNIPHIER_PERI_CLK_SCSSI(idx) \ - UNIPHIER_CLK_GATE("scssi", (idx), "spi", 0x20, 17) +#define UNIPHIER_PERI_CLK_SCSSI(idx, ch) \ + UNIPHIER_CLK_GATE("scssi" #ch, (idx), "spi", 0x20, 17 + (ch)) #define UNIPHIER_PERI_CLK_MCSSI(idx) \ UNIPHIER_CLK_GATE("mcssi", (idx), "spi", 0x24, 14) @@ -44,7 +44,7 @@ const struct uniphier_clk_data uniphier_ld4_peri_clk_data[] = { UNIPHIER_PERI_CLK_I2C(6, 2), UNIPHIER_PERI_CLK_I2C(7, 3), UNIPHIER_PERI_CLK_I2C(8, 4), - UNIPHIER_PERI_CLK_SCSSI(11), + UNIPHIER_PERI_CLK_SCSSI(11, 0), { /* sentinel */ } }; @@ -60,7 +60,10 @@ const struct uniphier_clk_data uniphier_pro4_peri_clk_data[] = { UNIPHIER_PERI_CLK_FI2C(8, 4), UNIPHIER_PERI_CLK_FI2C(9, 5), UNIPHIER_PERI_CLK_FI2C(10, 6), - UNIPHIER_PERI_CLK_SCSSI(11), - UNIPHIER_PERI_CLK_MCSSI(12), + UNIPHIER_PERI_CLK_SCSSI(11, 0), + UNIPHIER_PERI_CLK_SCSSI(12, 1), + UNIPHIER_PERI_CLK_SCSSI(13, 2), + UNIPHIER_PERI_CLK_SCSSI(14, 3), + UNIPHIER_PERI_CLK_MCSSI(15), { /* sentinel */ } }; diff --git a/drivers/clocksource/bcm2835_timer.c b/drivers/clocksource/bcm2835_timer.c index 60da2537bef9303308498187c4fa542bca68aac6..1082dcef17d17084c002d509013295615a57bc21 100644 --- a/drivers/clocksource/bcm2835_timer.c +++ b/drivers/clocksource/bcm2835_timer.c @@ -134,7 +134,7 @@ static int __init bcm2835_timer_init(struct device_node *node) ret = setup_irq(irq, &timer->act); if (ret) { pr_err("Can't set up timer IRQ\n"); - goto err_iounmap; + goto err_timer_free; } clockevents_config_and_register(&timer->evt, freq, 0xf, 0xffffffff); @@ -143,6 +143,9 @@ static int __init bcm2835_timer_init(struct device_node *node) return 0; +err_timer_free: + kfree(timer); + err_iounmap: iounmap(base); return ret; diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c index aaf5bfa9bd9c915efdca1b2ac5fba58d86199a01..e3ae041ac30e1af4472c26fdfce517bd38d12cc7 100644 --- a/drivers/clocksource/exynos_mct.c +++ b/drivers/clocksource/exynos_mct.c @@ -563,7 +563,19 @@ static int __init exynos4_timer_resources(struct device_node *np, void __iomem * return 0; out_irq: - free_percpu_irq(mct_irqs[MCT_L0_IRQ], &percpu_mct_tick); + if (mct_int_type == MCT_INT_PPI) { + free_percpu_irq(mct_irqs[MCT_L0_IRQ], &percpu_mct_tick); + } else { + for_each_possible_cpu(cpu) { + struct mct_clock_event_device *pcpu_mevt = + per_cpu_ptr(&percpu_mct_tick, cpu); + + if (pcpu_mevt->evt.irq != -1) { + free_irq(pcpu_mevt->evt.irq, pcpu_mevt); + pcpu_mevt->evt.irq = -1; + } + } + } return err; } diff --git a/drivers/clocksource/timer-sun5i.c b/drivers/clocksource/timer-sun5i.c index 3b56ea3f52afc8ee47ca21c38cd0c6aa7ec80021..552c5254390cbe3f9f74e7923c322991fa7b0bed 100644 --- a/drivers/clocksource/timer-sun5i.c +++ b/drivers/clocksource/timer-sun5i.c @@ -202,6 +202,11 @@ static int __init sun5i_setup_clocksource(struct device_node *node, } 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; @@ -275,6 +280,11 @@ static int __init sun5i_setup_clockevent(struct device_node *node, void __iomem } 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); diff --git a/drivers/clocksource/timer-ti-dm.c b/drivers/clocksource/timer-ti-dm.c index 3ecf84706640b4a39736a8e3720ecb2b39ea9970..23414dddc3bae1b5b9c66a03f9cc00d6001e4002 100644 --- a/drivers/clocksource/timer-ti-dm.c +++ b/drivers/clocksource/timer-ti-dm.c @@ -868,7 +868,6 @@ static int omap_dm_timer_probe(struct platform_device *pdev) timer->pdev = pdev; pm_runtime_enable(dev); - pm_runtime_irq_safe(dev); if (!timer->reserved) { ret = pm_runtime_get_sync(dev); diff --git a/drivers/cpufreq/brcmstb-avs-cpufreq.c b/drivers/cpufreq/brcmstb-avs-cpufreq.c index e6f9cbe5835f96883599d86006c54fd805f4e7e7..77b0e5d0fb1344b29c452cb96be05fcefec0d516 100644 --- a/drivers/cpufreq/brcmstb-avs-cpufreq.c +++ b/drivers/cpufreq/brcmstb-avs-cpufreq.c @@ -384,12 +384,12 @@ static int brcm_avs_set_pstate(struct private_data *priv, unsigned int pstate) return __issue_avs_command(priv, AVS_CMD_SET_PSTATE, true, args); } -static unsigned long brcm_avs_get_voltage(void __iomem *base) +static u32 brcm_avs_get_voltage(void __iomem *base) { return readl(base + AVS_MBOX_VOLTAGE1); } -static unsigned long brcm_avs_get_frequency(void __iomem *base) +static u32 brcm_avs_get_frequency(void __iomem *base) { return readl(base + AVS_MBOX_FREQUENCY) * 1000; /* in kHz */ } @@ -446,8 +446,8 @@ static bool brcm_avs_is_firmware_loaded(struct private_data *priv) rc = brcm_avs_get_pmap(priv, NULL); magic = readl(priv->base + AVS_MBOX_MAGIC); - return (magic == AVS_FIRMWARE_MAGIC) && (rc != -ENOTSUPP) && - (rc != -EINVAL); + return (magic == AVS_FIRMWARE_MAGIC) && ((rc != -ENOTSUPP) || + (rc != -EINVAL)); } static unsigned int brcm_avs_cpufreq_get(unsigned int cpu) @@ -653,14 +653,14 @@ static ssize_t show_brcm_avs_voltage(struct cpufreq_policy *policy, char *buf) { struct private_data *priv = policy->driver_data; - return sprintf(buf, "0x%08lx\n", brcm_avs_get_voltage(priv->base)); + return sprintf(buf, "0x%08x\n", brcm_avs_get_voltage(priv->base)); } static ssize_t show_brcm_avs_frequency(struct cpufreq_policy *policy, char *buf) { struct private_data *priv = policy->driver_data; - return sprintf(buf, "0x%08lx\n", brcm_avs_get_frequency(priv->base)); + return sprintf(buf, "0x%08x\n", brcm_avs_get_frequency(priv->base)); } cpufreq_freq_attr_ro(brcm_avs_pstate); diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index b5481aba2d0b9991605abea9ad0a1927938dcbbf..f58006521e6768e3d34ea79654d748eb2e4f1558 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -24,12 +24,17 @@ #define LUT_ROW_SIZE 32 #define CLK_HW_DIV 2 #define GT_IRQ_STATUS BIT(2) -#define MAX_FN_SIZE 12 +#define MAX_FN_SIZE 20 #define LIMITS_POLLING_DELAY_MS 10 #define CYCLE_CNTR_OFFSET(c, m, acc_count) \ (acc_count ? ((c - cpumask_first(m) + 1) * 4) : 0) +enum { + CPUFREQ_HW_LOW_TEMP_LEVEL, + CPUFREQ_HW_HIGH_TEMP_LEVEL, +}; + enum { REG_ENABLE, REG_FREQ_LUT_TABLE, @@ -48,17 +53,32 @@ static unsigned int lut_row_size = LUT_ROW_SIZE; static unsigned int lut_max_entries = LUT_MAX_ENTRIES; static bool accumulative_counter; +struct skipped_freq { + bool skip; + u32 freq; + u32 cc; + u32 prev_index; + u32 prev_freq; + u32 prev_cc; + u32 high_temp_index; + u32 low_temp_index; + u32 final_index; + spinlock_t lock; +}; + struct cpufreq_qcom { struct cpufreq_frequency_table *table; void __iomem *reg_bases[REG_ARRAY_SIZE]; cpumask_t related_cpus; unsigned int max_cores; + unsigned int lut_max_entries; unsigned long xo_rate; unsigned long cpu_hw_rate; unsigned long dcvsh_freq_limit; struct delayed_work freq_poll_work; struct mutex dcvsh_lock; struct device_attribute freq_limit_attr; + struct skipped_freq skip_data; int dcvsh_irq; char dcvsh_irq_name[MAX_FN_SIZE]; bool is_irq_enabled; @@ -71,6 +91,13 @@ struct cpufreq_counter { spinlock_t lock; }; +struct cpufreq_cooling_cdev { + int cpu_id; + bool cpu_cooling_state; + struct thermal_cooling_device *cdev; + struct device_node *np; +}; + static const u16 cpufreq_qcom_std_offsets[REG_ARRAY_SIZE] = { [REG_ENABLE] = 0x0, [REG_FREQ_LUT_TABLE] = 0x110, @@ -164,6 +191,32 @@ static void limits_dcvsh_poll(struct work_struct *work) mutex_unlock(&c->dcvsh_lock); } +static bool dcvsh_core_count_change(struct cpufreq_qcom *c) +{ + bool ret = false; + unsigned long freq, flags; + u32 index, regval; + + spin_lock_irqsave(&c->skip_data.lock, flags); + index = readl_relaxed(c->reg_bases[REG_PERF_STATE]); + + freq = readl_relaxed(c->reg_bases[REG_DOMAIN_STATE]) & GENMASK(7, 0); + freq = DIV_ROUND_CLOSEST_ULL(freq * c->xo_rate, 1000); + + if ((index == c->skip_data.final_index) && + (freq == c->skip_data.prev_freq)) { + regval = readl_relaxed(c->reg_bases[REG_INTR_CLR]); + regval |= GT_IRQ_STATUS; + writel_relaxed(regval, c->reg_bases[REG_INTR_CLR]); + pr_debug("core count change index IRQ received\n"); + ret = true; + } + + spin_unlock_irqrestore(&c->skip_data.lock, flags); + + return ret; +} + static irqreturn_t dcvsh_handle_isr(int irq, void *data) { struct cpufreq_qcom *c = data; @@ -176,6 +229,9 @@ static irqreturn_t dcvsh_handle_isr(int irq, void *data) mutex_lock(&c->dcvsh_lock); if (c->is_irq_enabled) { + if (c->skip_data.skip && dcvsh_core_count_change(c)) + goto done; + c->is_irq_enabled = false; disable_irq_nosync(c->dcvsh_irq); limits_mitigation_notify(c, true); @@ -183,7 +239,7 @@ static irqreturn_t dcvsh_handle_isr(int irq, void *data) msecs_to_jiffies(LIMITS_POLLING_DELAY_MS)); } - +done: mutex_unlock(&c->dcvsh_lock); return IRQ_HANDLED; @@ -228,8 +284,17 @@ qcom_cpufreq_hw_target_index(struct cpufreq_policy *policy, unsigned int index) { struct cpufreq_qcom *c = policy->driver_data; + unsigned long flags; + + if (c->skip_data.skip && index == c->skip_data.high_temp_index) { + spin_lock_irqsave(&c->skip_data.lock, flags); + writel_relaxed(c->skip_data.final_index, + c->reg_bases[REG_PERF_STATE]); + spin_unlock_irqrestore(&c->skip_data.lock, flags); + } else { + writel_relaxed(index, c->reg_bases[REG_PERF_STATE]); + } - writel_relaxed(index, c->reg_bases[REG_PERF_STATE]); arch_set_freq_scale(policy->related_cpus, policy->freq_table[index].frequency, policy->cpuinfo.max_freq); @@ -250,7 +315,7 @@ static unsigned int qcom_cpufreq_hw_get(unsigned int cpu) c = policy->driver_data; index = readl_relaxed(c->reg_bases[REG_PERF_STATE]); - index = min(index, lut_max_entries - 1); + index = min(index, c->lut_max_entries - 1); return policy->freq_table[index].frequency; } @@ -390,6 +455,7 @@ static int qcom_cpufreq_hw_read_lut(struct platform_device *pdev, if (!c->table) return -ENOMEM; + spin_lock_init(&c->skip_data.lock); base_freq = c->reg_bases[REG_FREQ_LUT_TABLE]; base_volt = c->reg_bases[REG_VOLT_LUT_TABLE]; @@ -414,8 +480,21 @@ static int qcom_cpufreq_hw_read_lut(struct platform_device *pdev, i, c->table[i].frequency, core_count); if (core_count != c->max_cores) { - cur_freq = CPUFREQ_ENTRY_INVALID; - c->table[i].flags = CPUFREQ_BOOST_FREQ; + if (core_count == (c->max_cores - 1)) { + c->skip_data.skip = true; + c->skip_data.high_temp_index = i; + c->skip_data.freq = cur_freq; + c->skip_data.cc = core_count; + c->skip_data.final_index = i + 1; + c->skip_data.low_temp_index = i + 1; + c->skip_data.prev_freq = + c->table[i-1].frequency; + c->skip_data.prev_index = i - 1; + c->skip_data.prev_cc = prev_cc; + } else { + cur_freq = CPUFREQ_ENTRY_INVALID; + c->table[i].flags = CPUFREQ_BOOST_FREQ; + } } /* @@ -423,13 +502,17 @@ static int qcom_cpufreq_hw_read_lut(struct platform_device *pdev, * end of table. */ if (i > 0 && c->table[i - 1].frequency == - c->table[i].frequency && prev_cc == core_count) { - struct cpufreq_frequency_table *prev = &c->table[i - 1]; - - if (prev_freq == CPUFREQ_ENTRY_INVALID) - prev->flags = CPUFREQ_BOOST_FREQ; + c->table[i].frequency) { + if (prev_cc == core_count) { + struct cpufreq_frequency_table *prev = + &c->table[i - 1]; + + if (prev_freq == CPUFREQ_ENTRY_INVALID) + prev->flags = CPUFREQ_BOOST_FREQ; + } break; } + prev_cc = core_count; prev_freq = cur_freq; @@ -442,8 +525,20 @@ static int qcom_cpufreq_hw_read_lut(struct platform_device *pdev, } } + c->lut_max_entries = i; c->table[i].frequency = CPUFREQ_TABLE_END; + if (c->skip_data.skip) { + pr_info("%s Skip: Index[%u], Frequency[%u], Core Count %u, Final Index %u Actual Index %u Prev_Freq[%u] Prev_Index[%u] Prev_CC[%u]\n", + __func__, c->skip_data.high_temp_index, + c->skip_data.freq, c->skip_data.cc, + c->skip_data.final_index, + c->skip_data.low_temp_index, + c->skip_data.prev_freq, + c->skip_data.prev_index, + c->skip_data.prev_cc); + } + return 0; } @@ -603,6 +698,128 @@ static int qcom_resources_init(struct platform_device *pdev) return 0; } +static int cpufreq_hw_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long state) +{ + struct cpufreq_cooling_cdev *cpu_cdev = cdev->devdata; + struct cpufreq_policy *policy; + struct cpufreq_qcom *c; + unsigned long flags; + + + if (cpu_cdev->cpu_id == -1) + return -ENODEV; + + if (state > CPUFREQ_HW_HIGH_TEMP_LEVEL) + return -EINVAL; + + if (cpu_cdev->cpu_cooling_state == state) + return 0; + + policy = cpufreq_cpu_get_raw(cpu_cdev->cpu_id); + if (!policy) + return 0; + + c = policy->driver_data; + cpu_cdev->cpu_cooling_state = state; + + if (state == CPUFREQ_HW_HIGH_TEMP_LEVEL) { + spin_lock_irqsave(&c->skip_data.lock, flags); + c->skip_data.final_index = c->skip_data.high_temp_index; + spin_unlock_irqrestore(&c->skip_data.lock, flags); + } else { + spin_lock_irqsave(&c->skip_data.lock, flags); + c->skip_data.final_index = c->skip_data.low_temp_index; + spin_unlock_irqrestore(&c->skip_data.lock, flags); + } + + if (policy->cur != c->skip_data.freq) + return 0; + + return qcom_cpufreq_hw_target_index(policy, + c->skip_data.high_temp_index); +} + +static int cpufreq_hw_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct cpufreq_cooling_cdev *cpu_cdev = cdev->devdata; + + *state = (cpu_cdev->cpu_cooling_state) ? + CPUFREQ_HW_HIGH_TEMP_LEVEL : CPUFREQ_HW_LOW_TEMP_LEVEL; + + return 0; +} + +static int cpufreq_hw_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + *state = CPUFREQ_HW_HIGH_TEMP_LEVEL; + return 0; +} + +static struct thermal_cooling_device_ops cpufreq_hw_cooling_ops = { + .get_max_state = cpufreq_hw_get_max_state, + .get_cur_state = cpufreq_hw_get_cur_state, + .set_cur_state = cpufreq_hw_set_cur_state, +}; + +static int cpufreq_hw_register_cooling_device(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node, *cpu_np, *phandle; + struct cpufreq_cooling_cdev *cpu_cdev = NULL; + struct device *cpu_dev; + struct cpufreq_policy *policy; + struct cpufreq_qcom *c; + char cdev_name[MAX_FN_SIZE] = ""; + int cpu; + + for_each_available_child_of_node(np, cpu_np) { + cpu_cdev = devm_kzalloc(&pdev->dev, sizeof(*cpu_cdev), + GFP_KERNEL); + if (!cpu_cdev) + return -ENOMEM; + cpu_cdev->cpu_id = -1; + cpu_cdev->cpu_cooling_state = false; + cpu_cdev->cdev = NULL; + cpu_cdev->np = cpu_np; + + phandle = of_parse_phandle(cpu_np, "qcom,cooling-cpu", 0); + for_each_possible_cpu(cpu) { + policy = cpufreq_cpu_get_raw(cpu); + if (!policy) + continue; + c = policy->driver_data; + if (!c->skip_data.skip) + continue; + cpu_dev = get_cpu_device(cpu); + if (cpu_dev && cpu_dev->of_node == phandle) { + cpu_cdev->cpu_id = cpu; + snprintf(cdev_name, sizeof(cdev_name), + "cpufreq-hw-%d", cpu); + cpu_cdev->cdev = + thermal_of_cooling_device_register( + cpu_cdev->np, cdev_name, + cpu_cdev, + &cpufreq_hw_cooling_ops); + if (IS_ERR(cpu_cdev->cdev)) { + pr_err("Cooling register failed for %s, ret: %d\n", + cdev_name, + PTR_ERR(cpu_cdev->cdev)); + c->skip_data.final_index = + c->skip_data.high_temp_index; + break; + } + pr_info("CPUFREQ-HW cooling device %d %s\n", + cpu, cdev_name); + break; + } + } + } + + return 0; +} + static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev) { struct cpu_cycle_counter_cb cycle_counter_cb = { @@ -635,6 +852,8 @@ static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "QCOM CPUFreq HW driver initialized\n"); of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + cpufreq_hw_register_cooling_device(pdev); + return 0; } diff --git a/drivers/crypto/amcc/crypto4xx_trng.h b/drivers/crypto/amcc/crypto4xx_trng.h index 931d22531f51590749c803d0dd694e820cab87d8..7bbda51b7337c3a0f5df5d3b9427c75e2d30908f 100644 --- a/drivers/crypto/amcc/crypto4xx_trng.h +++ b/drivers/crypto/amcc/crypto4xx_trng.h @@ -26,9 +26,9 @@ void ppc4xx_trng_probe(struct crypto4xx_core_device *core_dev); void ppc4xx_trng_remove(struct crypto4xx_core_device *core_dev); #else static inline void ppc4xx_trng_probe( - struct crypto4xx_device *dev __maybe_unused) { } + struct crypto4xx_core_device *dev __maybe_unused) { } static inline void ppc4xx_trng_remove( - struct crypto4xx_device *dev __maybe_unused) { } + struct crypto4xx_core_device *dev __maybe_unused) { } #endif #endif diff --git a/drivers/crypto/atmel-aes.c b/drivers/crypto/atmel-aes.c index 53a78035381d5c9098b882993fdebe89e19dcfa4..92060864e35699220948326c75683b31d9f20fab 100644 --- a/drivers/crypto/atmel-aes.c +++ b/drivers/crypto/atmel-aes.c @@ -91,7 +91,6 @@ struct atmel_aes_caps { bool has_dualbuff; bool has_cfb64; - bool has_ctr32; bool has_gcm; bool has_xts; bool has_authenc; @@ -1016,8 +1015,9 @@ static int atmel_aes_ctr_transfer(struct atmel_aes_dev *dd) struct atmel_aes_ctr_ctx *ctx = atmel_aes_ctr_ctx_cast(dd->ctx); struct ablkcipher_request *req = ablkcipher_request_cast(dd->areq); struct scatterlist *src, *dst; - u32 ctr, blocks; size_t datalen; + u32 ctr; + u16 blocks, start, end; bool use_dma, fragmented = false; /* Check for transfer completion. */ @@ -1029,27 +1029,17 @@ static int atmel_aes_ctr_transfer(struct atmel_aes_dev *dd) datalen = req->nbytes - ctx->offset; blocks = DIV_ROUND_UP(datalen, AES_BLOCK_SIZE); ctr = be32_to_cpu(ctx->iv[3]); - if (dd->caps.has_ctr32) { - /* Check 32bit counter overflow. */ - u32 start = ctr; - u32 end = start + blocks - 1; - - if (end < start) { - ctr |= 0xffffffff; - datalen = AES_BLOCK_SIZE * -start; - fragmented = true; - } - } else { - /* Check 16bit counter overflow. */ - u16 start = ctr & 0xffff; - u16 end = start + (u16)blocks - 1; - - if (blocks >> 16 || end < start) { - ctr |= 0xffff; - datalen = AES_BLOCK_SIZE * (0x10000-start); - fragmented = true; - } + + /* Check 16bit counter overflow. */ + start = ctr & 0xffff; + end = start + blocks - 1; + + if (blocks >> 16 || end < start) { + ctr |= 0xffff; + datalen = AES_BLOCK_SIZE * (0x10000 - start); + fragmented = true; } + use_dma = (datalen >= ATMEL_AES_DMA_THRESHOLD); /* Jump to offset. */ @@ -2553,7 +2543,6 @@ static void atmel_aes_get_cap(struct atmel_aes_dev *dd) { dd->caps.has_dualbuff = 0; dd->caps.has_cfb64 = 0; - dd->caps.has_ctr32 = 0; dd->caps.has_gcm = 0; dd->caps.has_xts = 0; dd->caps.has_authenc = 0; @@ -2564,7 +2553,6 @@ static void atmel_aes_get_cap(struct atmel_aes_dev *dd) case 0x500: dd->caps.has_dualbuff = 1; dd->caps.has_cfb64 = 1; - dd->caps.has_ctr32 = 1; dd->caps.has_gcm = 1; dd->caps.has_xts = 1; dd->caps.has_authenc = 1; @@ -2573,7 +2561,6 @@ static void atmel_aes_get_cap(struct atmel_aes_dev *dd) case 0x200: dd->caps.has_dualbuff = 1; dd->caps.has_cfb64 = 1; - dd->caps.has_ctr32 = 1; dd->caps.has_gcm = 1; dd->caps.max_burst_size = 4; break; diff --git a/drivers/crypto/atmel-sha.c b/drivers/crypto/atmel-sha.c index ef125d4be8fc4e65ca45baf6d7ad1abca40e7d22..cb548a0506c5429f3a7dfa948ff5e99d386dfbce 100644 --- a/drivers/crypto/atmel-sha.c +++ b/drivers/crypto/atmel-sha.c @@ -1921,12 +1921,7 @@ static int atmel_sha_hmac_setkey(struct crypto_ahash *tfm, const u8 *key, { struct atmel_sha_hmac_ctx *hmac = crypto_ahash_ctx(tfm); - if (atmel_sha_hmac_key_set(&hmac->hkey, key, keylen)) { - crypto_ahash_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN); - return -EINVAL; - } - - return 0; + return atmel_sha_hmac_key_set(&hmac->hkey, key, keylen); } static int atmel_sha_hmac_init(struct ahash_request *req) diff --git a/drivers/crypto/axis/artpec6_crypto.c b/drivers/crypto/axis/artpec6_crypto.c index fdcdc751d03bdd400457ee39493a5c349a8bc81b..3caf57ebdbff570907d27dea3bdb41d5dc44ce1e 100644 --- a/drivers/crypto/axis/artpec6_crypto.c +++ b/drivers/crypto/axis/artpec6_crypto.c @@ -1256,7 +1256,7 @@ static int artpec6_crypto_aead_set_key(struct crypto_aead *tfm, const u8 *key, if (len != 16 && len != 24 && len != 32) { crypto_aead_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN); - return -1; + return -EINVAL; } ctx->key_length = len; diff --git a/drivers/crypto/bcm/cipher.c b/drivers/crypto/bcm/cipher.c index 49c0097fa4749989306cbfb172e90ea1aa8b15ed..0b1fc5664b1d8a23ba04a8b2056a11709a036aba 100644 --- a/drivers/crypto/bcm/cipher.c +++ b/drivers/crypto/bcm/cipher.c @@ -717,7 +717,7 @@ static int handle_ahash_req(struct iproc_reqctx_s *rctx) */ unsigned int new_data_len; - unsigned int chunk_start = 0; + unsigned int __maybe_unused chunk_start = 0; u32 db_size; /* Length of data field, incl gcm and hash padding */ int pad_len = 0; /* total pad len, including gcm, hash, stat padding */ u32 data_pad_len = 0; /* length of GCM/CCM padding */ @@ -1675,8 +1675,6 @@ static void spu_rx_callback(struct mbox_client *cl, void *msg) struct spu_hw *spu = &iproc_priv.spu; struct brcm_message *mssg = msg; struct iproc_reqctx_s *rctx; - struct iproc_ctx_s *ctx; - struct crypto_async_request *areq; int err = 0; rctx = mssg->ctx; @@ -1686,8 +1684,6 @@ static void spu_rx_callback(struct mbox_client *cl, void *msg) err = -EFAULT; goto cb_finish; } - areq = rctx->parent; - ctx = rctx->ctx; /* process the SPU status */ err = spu->spu_status_process(rctx->msg_buf.rx_stat); diff --git a/drivers/crypto/caam/caamrng.c b/drivers/crypto/caam/caamrng.c index fde07d4ff0190efcc229fd0a561681fcd5c07b28..ff6718a11e9ec74d9409cf8c4c1cbf54d22456da 100644 --- a/drivers/crypto/caam/caamrng.c +++ b/drivers/crypto/caam/caamrng.c @@ -353,7 +353,10 @@ static int __init caam_rng_init(void) goto free_rng_ctx; dev_info(dev, "registering rng-caam\n"); - return hwrng_register(&caam_rng); + + err = hwrng_register(&caam_rng); + if (!err) + return err; free_rng_ctx: kfree(rng_ctx); diff --git a/drivers/crypto/caam/error.c b/drivers/crypto/caam/error.c index 8da88beb1abbe1def7582add0e49e88023c430dd..832ba2afdcd57f0e42cfddedbfcccad07a4cdd1b 100644 --- a/drivers/crypto/caam/error.c +++ b/drivers/crypto/caam/error.c @@ -22,7 +22,7 @@ void caam_dump_sg(const char *level, const char *prefix_str, int prefix_type, size_t len; void *buf; - for (it = sg; it && tlen > 0 ; it = sg_next(sg)) { + for (it = sg; it && tlen > 0 ; it = sg_next(it)) { /* * make sure the scatterlist's page * has a valid virtual memory mapping diff --git a/drivers/crypto/ccp/ccp-crypto-aes.c b/drivers/crypto/ccp/ccp-crypto-aes.c index 89291c15015cd3eb0218ec20747273db310a9dd4..3f768699332ba230d7518dee75ae7ada6b357d3a 100644 --- a/drivers/crypto/ccp/ccp-crypto-aes.c +++ b/drivers/crypto/ccp/ccp-crypto-aes.c @@ -1,7 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * AMD Cryptographic Coprocessor (CCP) AES crypto API support * - * Copyright (C) 2013,2016 Advanced Micro Devices, Inc. + * Copyright (C) 2013-2019 Advanced Micro Devices, Inc. * * Author: Tom Lendacky * @@ -79,8 +80,7 @@ static int ccp_aes_crypt(struct ablkcipher_request *req, bool encrypt) return -EINVAL; if (((ctx->u.aes.mode == CCP_AES_MODE_ECB) || - (ctx->u.aes.mode == CCP_AES_MODE_CBC) || - (ctx->u.aes.mode == CCP_AES_MODE_CFB)) && + (ctx->u.aes.mode == CCP_AES_MODE_CBC)) && (req->nbytes & (AES_BLOCK_SIZE - 1))) return -EINVAL; @@ -291,7 +291,7 @@ static struct ccp_aes_def aes_algs[] = { .version = CCP_VERSION(3, 0), .name = "cfb(aes)", .driver_name = "cfb-aes-ccp", - .blocksize = AES_BLOCK_SIZE, + .blocksize = 1, .ivsize = AES_BLOCK_SIZE, .alg_defaults = &ccp_aes_defaults, }, diff --git a/drivers/crypto/ccp/ccp-dev-v3.c b/drivers/crypto/ccp/ccp-dev-v3.c index 240bebbcb8ac7c52eb7dfa2b93a00b5b9cb53cc9..ae0cc0a4dc5c76e70fe9d7c5e82a46c2cea62def 100644 --- a/drivers/crypto/ccp/ccp-dev-v3.c +++ b/drivers/crypto/ccp/ccp-dev-v3.c @@ -590,6 +590,7 @@ const struct ccp_vdata ccpv3_platform = { .setup = NULL, .perform = &ccp3_actions, .offset = 0, + .rsamax = CCP_RSA_MAX_WIDTH, }; const struct ccp_vdata ccpv3 = { diff --git a/drivers/crypto/ccp/ccp-ops.c b/drivers/crypto/ccp/ccp-ops.c index 1e2e42106dee070d92ac543c8e1448327caffb8a..330853a2702f027ac82b5bab4a098ab4f2e8d0d7 100644 --- a/drivers/crypto/ccp/ccp-ops.c +++ b/drivers/crypto/ccp/ccp-ops.c @@ -458,8 +458,8 @@ static int ccp_copy_from_sb(struct ccp_cmd_queue *cmd_q, return ccp_copy_to_from_sb(cmd_q, wa, jobid, sb, byte_swap, true); } -static int ccp_run_aes_cmac_cmd(struct ccp_cmd_queue *cmd_q, - struct ccp_cmd *cmd) +static noinline_for_stack int +ccp_run_aes_cmac_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) { struct ccp_aes_engine *aes = &cmd->u.aes; struct ccp_dm_workarea key, ctx; @@ -614,8 +614,8 @@ static int ccp_run_aes_cmac_cmd(struct ccp_cmd_queue *cmd_q, return ret; } -static int ccp_run_aes_gcm_cmd(struct ccp_cmd_queue *cmd_q, - struct ccp_cmd *cmd) +static noinline_for_stack int +ccp_run_aes_gcm_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) { struct ccp_aes_engine *aes = &cmd->u.aes; struct ccp_dm_workarea key, ctx, final_wa, tag; @@ -897,7 +897,8 @@ static int ccp_run_aes_gcm_cmd(struct ccp_cmd_queue *cmd_q, return ret; } -static int ccp_run_aes_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) +static noinline_for_stack int +ccp_run_aes_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) { struct ccp_aes_engine *aes = &cmd->u.aes; struct ccp_dm_workarea key, ctx; @@ -907,12 +908,6 @@ static int ccp_run_aes_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) bool in_place = false; int ret; - if (aes->mode == CCP_AES_MODE_CMAC) - return ccp_run_aes_cmac_cmd(cmd_q, cmd); - - if (aes->mode == CCP_AES_MODE_GCM) - return ccp_run_aes_gcm_cmd(cmd_q, cmd); - if (!((aes->key_len == AES_KEYSIZE_128) || (aes->key_len == AES_KEYSIZE_192) || (aes->key_len == AES_KEYSIZE_256))) @@ -1080,8 +1075,8 @@ static int ccp_run_aes_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) return ret; } -static int ccp_run_xts_aes_cmd(struct ccp_cmd_queue *cmd_q, - struct ccp_cmd *cmd) +static noinline_for_stack int +ccp_run_xts_aes_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) { struct ccp_xts_aes_engine *xts = &cmd->u.xts; struct ccp_dm_workarea key, ctx; @@ -1280,7 +1275,8 @@ static int ccp_run_xts_aes_cmd(struct ccp_cmd_queue *cmd_q, return ret; } -static int ccp_run_des3_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) +static noinline_for_stack int +ccp_run_des3_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) { struct ccp_des3_engine *des3 = &cmd->u.des3; @@ -1293,6 +1289,9 @@ static int ccp_run_des3_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) int ret; /* Error checks */ + if (cmd_q->ccp->vdata->version < CCP_VERSION(5, 0)) + return -EINVAL; + if (!cmd_q->ccp->vdata->perform->des3) return -EINVAL; @@ -1375,8 +1374,6 @@ static int ccp_run_des3_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) * passthru option to convert from big endian to little endian. */ if (des3->mode != CCP_DES3_MODE_ECB) { - u32 load_mode; - op.sb_ctx = cmd_q->sb_ctx; ret = ccp_init_dm_workarea(&ctx, cmd_q, @@ -1392,12 +1389,8 @@ static int ccp_run_des3_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) if (ret) goto e_ctx; - if (cmd_q->ccp->vdata->version == CCP_VERSION(3, 0)) - load_mode = CCP_PASSTHRU_BYTESWAP_NOOP; - else - load_mode = CCP_PASSTHRU_BYTESWAP_256BIT; ret = ccp_copy_to_sb(cmd_q, &ctx, op.jobid, op.sb_ctx, - load_mode); + CCP_PASSTHRU_BYTESWAP_256BIT); if (ret) { cmd->engine_error = cmd_q->cmd_error; goto e_ctx; @@ -1459,10 +1452,6 @@ static int ccp_run_des3_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) } /* ...but we only need the last DES3_EDE_BLOCK_SIZE bytes */ - if (cmd_q->ccp->vdata->version == CCP_VERSION(3, 0)) - dm_offset = CCP_SB_BYTES - des3->iv_len; - else - dm_offset = 0; ccp_get_dm_area(&ctx, dm_offset, des3->iv, 0, DES3_EDE_BLOCK_SIZE); } @@ -1483,7 +1472,8 @@ static int ccp_run_des3_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) return ret; } -static int ccp_run_sha_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) +static noinline_for_stack int +ccp_run_sha_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) { struct ccp_sha_engine *sha = &cmd->u.sha; struct ccp_dm_workarea ctx; @@ -1827,7 +1817,8 @@ static int ccp_run_sha_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) return ret; } -static int ccp_run_rsa_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) +static noinline_for_stack int +ccp_run_rsa_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) { struct ccp_rsa_engine *rsa = &cmd->u.rsa; struct ccp_dm_workarea exp, src, dst; @@ -1958,8 +1949,8 @@ static int ccp_run_rsa_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) return ret; } -static int ccp_run_passthru_cmd(struct ccp_cmd_queue *cmd_q, - struct ccp_cmd *cmd) +static noinline_for_stack int +ccp_run_passthru_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) { struct ccp_passthru_engine *pt = &cmd->u.passthru; struct ccp_dm_workarea mask; @@ -2090,7 +2081,8 @@ static int ccp_run_passthru_cmd(struct ccp_cmd_queue *cmd_q, return ret; } -static int ccp_run_passthru_nomap_cmd(struct ccp_cmd_queue *cmd_q, +static noinline_for_stack int +ccp_run_passthru_nomap_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) { struct ccp_passthru_nomap_engine *pt = &cmd->u.passthru_nomap; @@ -2431,7 +2423,8 @@ static int ccp_run_ecc_pm_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) return ret; } -static int ccp_run_ecc_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) +static noinline_for_stack int +ccp_run_ecc_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) { struct ccp_ecc_engine *ecc = &cmd->u.ecc; @@ -2468,7 +2461,17 @@ int ccp_run_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) switch (cmd->engine) { case CCP_ENGINE_AES: - ret = ccp_run_aes_cmd(cmd_q, cmd); + switch (cmd->u.aes.mode) { + case CCP_AES_MODE_CMAC: + ret = ccp_run_aes_cmac_cmd(cmd_q, cmd); + break; + case CCP_AES_MODE_GCM: + ret = ccp_run_aes_gcm_cmd(cmd_q, cmd); + break; + default: + ret = ccp_run_aes_cmd(cmd_q, cmd); + break; + } break; case CCP_ENGINE_XTS_AES_128: ret = ccp_run_xts_aes_cmd(cmd_q, cmd); diff --git a/drivers/crypto/ccree/cc_cipher.c b/drivers/crypto/ccree/cc_cipher.c index 54a39164aab8f483d1c15ddbaa973eece6f6e506..28a5b8b38fa2f57c2e680984c70d13b6639fa06f 100644 --- a/drivers/crypto/ccree/cc_cipher.c +++ b/drivers/crypto/ccree/cc_cipher.c @@ -306,7 +306,6 @@ static int cc_cipher_setkey(struct crypto_skcipher *sktfm, const u8 *key, struct crypto_tfm *tfm = crypto_skcipher_tfm(sktfm); struct cc_cipher_ctx *ctx_p = crypto_tfm_ctx(tfm); struct device *dev = drvdata_to_dev(ctx_p->drvdata); - u32 tmp[DES3_EDE_EXPKEY_WORDS]; struct cc_crypto_alg *cc_alg = container_of(tfm->__crt_alg, struct cc_crypto_alg, skcipher_alg.base); @@ -332,6 +331,7 @@ static int cc_cipher_setkey(struct crypto_skcipher *sktfm, const u8 *key, * HW does the expansion on its own. */ if (ctx_p->flow_mode == S_DIN_to_DES) { + u32 tmp[DES3_EDE_EXPKEY_WORDS]; if (keylen == DES3_EDE_KEY_SIZE && __des3_ede_setkey(tmp, &tfm->crt_flags, key, DES3_EDE_KEY_SIZE)) { diff --git a/drivers/crypto/ccree/cc_driver.h b/drivers/crypto/ccree/cc_driver.h index be7f9bd5c55990f6215b23c2612131acd19d002e..d411939322075153480aa1524dbaf630ffd1b174 100644 --- a/drivers/crypto/ccree/cc_driver.h +++ b/drivers/crypto/ccree/cc_driver.h @@ -131,6 +131,7 @@ struct cc_drvdata { u32 axim_mon_offset; u32 sig_offset; u32 ver_offset; + bool pm_on; }; struct cc_crypto_alg { diff --git a/drivers/crypto/ccree/cc_pm.c b/drivers/crypto/ccree/cc_pm.c index 638082dff183ac7c92ed7f4c5fc183fff21dcab4..2df2c2ca8aaee25832b989bb2a3183e7b5c7ac3f 100644 --- a/drivers/crypto/ccree/cc_pm.c +++ b/drivers/crypto/ccree/cc_pm.c @@ -23,14 +23,8 @@ const struct dev_pm_ops ccree_pm = { int cc_pm_suspend(struct device *dev) { struct cc_drvdata *drvdata = dev_get_drvdata(dev); - int rc; dev_dbg(dev, "set HOST_POWER_DOWN_EN\n"); - rc = cc_suspend_req_queue(drvdata); - if (rc) { - dev_err(dev, "cc_suspend_req_queue (%x)\n", rc); - return rc; - } fini_cc_regs(drvdata); cc_iowrite(drvdata, CC_REG(HOST_POWER_DOWN_EN), POWER_DOWN_ENABLE); cc_clk_off(drvdata); @@ -59,13 +53,6 @@ int cc_pm_resume(struct device *dev) /* check if tee fips error occurred during power down */ cc_tee_handle_fips_error(drvdata); - rc = cc_resume_req_queue(drvdata); - if (rc) { - dev_err(dev, "cc_resume_req_queue (%x)\n", rc); - return rc; - } - - /* must be after the queue resuming as it uses the HW queue*/ cc_init_hash_sram(drvdata); cc_init_iv_sram(drvdata); @@ -77,12 +64,10 @@ int cc_pm_get(struct device *dev) int rc = 0; struct cc_drvdata *drvdata = dev_get_drvdata(dev); - if (cc_req_queue_suspended(drvdata)) + if (drvdata->pm_on) rc = pm_runtime_get_sync(dev); - else - pm_runtime_get_noresume(dev); - return rc; + return (rc == 1 ? 0 : rc); } int cc_pm_put_suspend(struct device *dev) @@ -90,14 +75,11 @@ int cc_pm_put_suspend(struct device *dev) int rc = 0; struct cc_drvdata *drvdata = dev_get_drvdata(dev); - if (!cc_req_queue_suspended(drvdata)) { + if (drvdata->pm_on) { pm_runtime_mark_last_busy(dev); rc = pm_runtime_put_autosuspend(dev); - } else { - /* Something wrong happens*/ - dev_err(dev, "request to suspend already suspended queue"); - rc = -EBUSY; } + return rc; } @@ -108,7 +90,7 @@ int cc_pm_init(struct cc_drvdata *drvdata) /* must be before the enabling to avoid resdundent suspending */ pm_runtime_set_autosuspend_delay(dev, CC_SUSPEND_TIMEOUT); pm_runtime_use_autosuspend(dev); - /* activate the PM module */ + /* set us as active - note we won't do PM ops until cc_pm_go()! */ return pm_runtime_set_active(dev); } @@ -116,9 +98,11 @@ int cc_pm_init(struct cc_drvdata *drvdata) void cc_pm_go(struct cc_drvdata *drvdata) { pm_runtime_enable(drvdata_to_dev(drvdata)); + drvdata->pm_on = true; } void cc_pm_fini(struct cc_drvdata *drvdata) { pm_runtime_disable(drvdata_to_dev(drvdata)); + drvdata->pm_on = false; } diff --git a/drivers/crypto/ccree/cc_request_mgr.c b/drivers/crypto/ccree/cc_request_mgr.c index 83a8aaae61c724ce1d89ba2c1c9f1abbd32c6317..1d88abc6d230eca0410269c030414d92b7bb96a5 100644 --- a/drivers/crypto/ccree/cc_request_mgr.c +++ b/drivers/crypto/ccree/cc_request_mgr.c @@ -41,7 +41,6 @@ struct cc_req_mgr_handle { #else struct tasklet_struct comptask; #endif - bool is_runtime_suspended; }; struct cc_bl_item { @@ -403,6 +402,7 @@ static void cc_proc_backlog(struct cc_drvdata *drvdata) spin_lock(&mgr->bl_lock); list_del(&bli->list); --mgr->bl_len; + kfree(bli); } spin_unlock(&mgr->bl_lock); @@ -660,52 +660,3 @@ static void comp_handler(unsigned long devarg) cc_proc_backlog(drvdata); } - -/* - * resume the queue configuration - no need to take the lock as this happens - * inside the spin lock protection - */ -#if defined(CONFIG_PM) -int cc_resume_req_queue(struct cc_drvdata *drvdata) -{ - struct cc_req_mgr_handle *request_mgr_handle = - drvdata->request_mgr_handle; - - spin_lock_bh(&request_mgr_handle->hw_lock); - request_mgr_handle->is_runtime_suspended = false; - spin_unlock_bh(&request_mgr_handle->hw_lock); - - return 0; -} - -/* - * suspend the queue configuration. Since it is used for the runtime suspend - * only verify that the queue can be suspended. - */ -int cc_suspend_req_queue(struct cc_drvdata *drvdata) -{ - struct cc_req_mgr_handle *request_mgr_handle = - drvdata->request_mgr_handle; - - /* lock the send_request */ - spin_lock_bh(&request_mgr_handle->hw_lock); - if (request_mgr_handle->req_queue_head != - request_mgr_handle->req_queue_tail) { - spin_unlock_bh(&request_mgr_handle->hw_lock); - return -EBUSY; - } - request_mgr_handle->is_runtime_suspended = true; - spin_unlock_bh(&request_mgr_handle->hw_lock); - - return 0; -} - -bool cc_req_queue_suspended(struct cc_drvdata *drvdata) -{ - struct cc_req_mgr_handle *request_mgr_handle = - drvdata->request_mgr_handle; - - return request_mgr_handle->is_runtime_suspended; -} - -#endif diff --git a/drivers/crypto/ccree/cc_request_mgr.h b/drivers/crypto/ccree/cc_request_mgr.h index 573cb97af085010d3c892d43ee8e76ea41fcf619..ae96abce25c9d48b95cc6794e410b0d21bf36fa6 100644 --- a/drivers/crypto/ccree/cc_request_mgr.h +++ b/drivers/crypto/ccree/cc_request_mgr.h @@ -40,12 +40,4 @@ void complete_request(struct cc_drvdata *drvdata); void cc_req_mgr_fini(struct cc_drvdata *drvdata); -#if defined(CONFIG_PM) -int cc_resume_req_queue(struct cc_drvdata *drvdata); - -int cc_suspend_req_queue(struct cc_drvdata *drvdata); - -bool cc_req_queue_suspended(struct cc_drvdata *drvdata); -#endif - #endif /*__REQUEST_MGR_H__*/ diff --git a/drivers/crypto/chelsio/chcr_algo.c b/drivers/crypto/chelsio/chcr_algo.c index d5e4816b2d3e6b39b04d298a7a23f1c91f54d5b3..2065aeb97de2ad18716aef3ebb0f84d02be3d8e9 100644 --- a/drivers/crypto/chelsio/chcr_algo.c +++ b/drivers/crypto/chelsio/chcr_algo.c @@ -3136,9 +3136,6 @@ static int chcr_gcm_setauthsize(struct crypto_aead *tfm, unsigned int authsize) aeadctx->mayverify = VERIFY_SW; break; default: - - crypto_tfm_set_flags((struct crypto_tfm *) tfm, - CRYPTO_TFM_RES_BAD_KEY_LEN); return -EINVAL; } return crypto_aead_setauthsize(aeadctx->sw_cipher, authsize); @@ -3163,8 +3160,6 @@ static int chcr_4106_4309_setauthsize(struct crypto_aead *tfm, aeadctx->mayverify = VERIFY_HW; break; default: - crypto_tfm_set_flags((struct crypto_tfm *)tfm, - CRYPTO_TFM_RES_BAD_KEY_LEN); return -EINVAL; } return crypto_aead_setauthsize(aeadctx->sw_cipher, authsize); @@ -3205,8 +3200,6 @@ static int chcr_ccm_setauthsize(struct crypto_aead *tfm, aeadctx->mayverify = VERIFY_HW; break; default: - crypto_tfm_set_flags((struct crypto_tfm *)tfm, - CRYPTO_TFM_RES_BAD_KEY_LEN); return -EINVAL; } return crypto_aead_setauthsize(aeadctx->sw_cipher, authsize); @@ -3231,8 +3224,7 @@ static int chcr_ccm_common_setkey(struct crypto_aead *aead, ck_size = CHCR_KEYCTX_CIPHER_KEY_SIZE_256; mk_size = CHCR_KEYCTX_MAC_KEY_SIZE_256; } else { - crypto_tfm_set_flags((struct crypto_tfm *)aead, - CRYPTO_TFM_RES_BAD_KEY_LEN); + crypto_aead_set_flags(aead, CRYPTO_TFM_RES_BAD_KEY_LEN); aeadctx->enckey_len = 0; return -EINVAL; } @@ -3270,8 +3262,7 @@ static int chcr_aead_rfc4309_setkey(struct crypto_aead *aead, const u8 *key, int error; if (keylen < 3) { - crypto_tfm_set_flags((struct crypto_tfm *)aead, - CRYPTO_TFM_RES_BAD_KEY_LEN); + crypto_aead_set_flags(aead, CRYPTO_TFM_RES_BAD_KEY_LEN); aeadctx->enckey_len = 0; return -EINVAL; } @@ -3321,8 +3312,7 @@ static int chcr_gcm_setkey(struct crypto_aead *aead, const u8 *key, } else if (keylen == AES_KEYSIZE_256) { ck_size = CHCR_KEYCTX_CIPHER_KEY_SIZE_256; } else { - crypto_tfm_set_flags((struct crypto_tfm *)aead, - CRYPTO_TFM_RES_BAD_KEY_LEN); + crypto_aead_set_flags(aead, CRYPTO_TFM_RES_BAD_KEY_LEN); pr_err("GCM: Invalid key length %d\n", keylen); ret = -EINVAL; goto out; diff --git a/drivers/crypto/chelsio/chtls/chtls_cm.c b/drivers/crypto/chelsio/chtls/chtls_cm.c index 8b749c721c8712c319e65841928221b2e1e2fadb..28d24118c6450d933e2e73877f34e0853da21b3e 100644 --- a/drivers/crypto/chelsio/chtls/chtls_cm.c +++ b/drivers/crypto/chelsio/chtls/chtls_cm.c @@ -731,6 +731,14 @@ static int chtls_close_listsrv_rpl(struct chtls_dev *cdev, struct sk_buff *skb) return 0; } +static void chtls_purge_wr_queue(struct sock *sk) +{ + struct sk_buff *skb; + + while ((skb = dequeue_wr(sk)) != NULL) + kfree_skb(skb); +} + static void chtls_release_resources(struct sock *sk) { struct chtls_sock *csk = rcu_dereference_sk_user_data(sk); @@ -745,6 +753,11 @@ static void chtls_release_resources(struct sock *sk) kfree_skb(csk->txdata_skb_cache); csk->txdata_skb_cache = NULL; + if (csk->wr_credits != csk->wr_max_credits) { + chtls_purge_wr_queue(sk); + chtls_reset_wr_list(csk); + } + if (csk->l2t_entry) { cxgb4_l2t_release(csk->l2t_entry); csk->l2t_entry = NULL; @@ -1714,6 +1727,7 @@ static void chtls_peer_close(struct sock *sk, struct sk_buff *skb) else sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN); } + kfree_skb(skb); } static void chtls_close_con_rpl(struct sock *sk, struct sk_buff *skb) @@ -2041,19 +2055,6 @@ static int chtls_conn_cpl(struct chtls_dev *cdev, struct sk_buff *skb) return 0; } -static struct sk_buff *dequeue_wr(struct sock *sk) -{ - struct chtls_sock *csk = rcu_dereference_sk_user_data(sk); - struct sk_buff *skb = csk->wr_skb_head; - - if (likely(skb)) { - /* Don't bother clearing the tail */ - csk->wr_skb_head = WR_SKB_CB(skb)->next_wr; - WR_SKB_CB(skb)->next_wr = NULL; - } - return skb; -} - static void chtls_rx_ack(struct sock *sk, struct sk_buff *skb) { struct cpl_fw4_ack *hdr = cplhdr(skb) + RSS_HDR; diff --git a/drivers/crypto/chelsio/chtls/chtls_cm.h b/drivers/crypto/chelsio/chtls/chtls_cm.h index 78eb3afa3a80ded899d6a3f58228f2a856fd98d4..4282d8a4eae48138035d5789887622a17fa95e03 100644 --- a/drivers/crypto/chelsio/chtls/chtls_cm.h +++ b/drivers/crypto/chelsio/chtls/chtls_cm.h @@ -188,6 +188,12 @@ static inline void chtls_kfree_skb(struct sock *sk, struct sk_buff *skb) kfree_skb(skb); } +static inline void chtls_reset_wr_list(struct chtls_sock *csk) +{ + csk->wr_skb_head = NULL; + csk->wr_skb_tail = NULL; +} + static inline void enqueue_wr(struct chtls_sock *csk, struct sk_buff *skb) { WR_SKB_CB(skb)->next_wr = NULL; @@ -200,4 +206,19 @@ static inline void enqueue_wr(struct chtls_sock *csk, struct sk_buff *skb) WR_SKB_CB(csk->wr_skb_tail)->next_wr = skb; csk->wr_skb_tail = skb; } + +static inline struct sk_buff *dequeue_wr(struct sock *sk) +{ + struct chtls_sock *csk = rcu_dereference_sk_user_data(sk); + struct sk_buff *skb = NULL; + + skb = csk->wr_skb_head; + + if (likely(skb)) { + /* Don't bother clearing the tail */ + csk->wr_skb_head = WR_SKB_CB(skb)->next_wr; + WR_SKB_CB(skb)->next_wr = NULL; + } + return skb; +} #endif diff --git a/drivers/crypto/chelsio/chtls/chtls_hw.c b/drivers/crypto/chelsio/chtls/chtls_hw.c index 490960755864468d33e33c4f59a9f558aa386f14..64d24823c65aa6d1f4cfdeb3a407b0ed87390134 100644 --- a/drivers/crypto/chelsio/chtls/chtls_hw.c +++ b/drivers/crypto/chelsio/chtls/chtls_hw.c @@ -361,6 +361,7 @@ int chtls_setkey(struct chtls_sock *csk, u32 keylen, u32 optname) kwr->sc_imm.cmd_more = cpu_to_be32(ULPTX_CMD_V(ULP_TX_SC_IMM)); kwr->sc_imm.len = cpu_to_be32(klen); + lock_sock(sk); /* key info */ kctx = (struct _key_ctx *)(kwr + 1); ret = chtls_key_info(csk, kctx, keylen, optname); @@ -399,8 +400,10 @@ int chtls_setkey(struct chtls_sock *csk, u32 keylen, u32 optname) csk->tlshws.txkey = keyid; } + release_sock(sk); return ret; out_notcb: + release_sock(sk); free_tls_keyid(sk); out_nokey: kfree_skb(skb); diff --git a/drivers/crypto/geode-aes.c b/drivers/crypto/geode-aes.c index eb2a0a73cbed17cd0c9bdc2f8ab847c4dcf4b9d3..0bd99c0decf580422633dc260aef24c5c774d40a 100644 --- a/drivers/crypto/geode-aes.c +++ b/drivers/crypto/geode-aes.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -27,12 +28,12 @@ static spinlock_t lock; /* Write a 128 bit field (either a writable key or IV) */ static inline void -_writefield(u32 offset, void *value) +_writefield(u32 offset, const void *value) { int i; for (i = 0; i < 4; i++) - iowrite32(((u32 *) value)[i], _iobase + offset + (i * 4)); + iowrite32(((const u32 *) value)[i], _iobase + offset + (i * 4)); } /* Read a 128 bit field (either a writable key or IV) */ @@ -46,12 +47,12 @@ _readfield(u32 offset, void *value) } static int -do_crypt(void *src, void *dst, int len, u32 flags) +do_crypt(const void *src, void *dst, u32 len, u32 flags) { u32 status; u32 counter = AES_OP_TIMEOUT; - iowrite32(virt_to_phys(src), _iobase + AES_SOURCEA_REG); + iowrite32(virt_to_phys((void *)src), _iobase + AES_SOURCEA_REG); iowrite32(virt_to_phys(dst), _iobase + AES_DSTA_REG); iowrite32(len, _iobase + AES_LENA_REG); @@ -68,16 +69,14 @@ do_crypt(void *src, void *dst, int len, u32 flags) return counter ? 0 : 1; } -static unsigned int -geode_aes_crypt(struct geode_aes_op *op) +static void +geode_aes_crypt(const struct geode_aes_tfm_ctx *tctx, const void *src, + void *dst, u32 len, u8 *iv, int mode, int dir) { u32 flags = 0; unsigned long iflags; int ret; - if (op->len == 0) - return 0; - /* If the source and destination is the same, then * we need to turn on the coherent flags, otherwise * we don't need to worry @@ -85,32 +84,28 @@ geode_aes_crypt(struct geode_aes_op *op) flags |= (AES_CTRL_DCA | AES_CTRL_SCA); - if (op->dir == AES_DIR_ENCRYPT) + if (dir == AES_DIR_ENCRYPT) flags |= AES_CTRL_ENCRYPT; /* Start the critical section */ spin_lock_irqsave(&lock, iflags); - if (op->mode == AES_MODE_CBC) { + if (mode == AES_MODE_CBC) { flags |= AES_CTRL_CBC; - _writefield(AES_WRITEIV0_REG, op->iv); + _writefield(AES_WRITEIV0_REG, iv); } - if (!(op->flags & AES_FLAGS_HIDDENKEY)) { - flags |= AES_CTRL_WRKEY; - _writefield(AES_WRITEKEY0_REG, op->key); - } + flags |= AES_CTRL_WRKEY; + _writefield(AES_WRITEKEY0_REG, tctx->key); - ret = do_crypt(op->src, op->dst, op->len, flags); + ret = do_crypt(src, dst, len, flags); BUG_ON(ret); - if (op->mode == AES_MODE_CBC) - _readfield(AES_WRITEIV0_REG, op->iv); + if (mode == AES_MODE_CBC) + _readfield(AES_WRITEIV0_REG, iv); spin_unlock_irqrestore(&lock, iflags); - - return op->len; } /* CRYPTO-API Functions */ @@ -118,13 +113,13 @@ geode_aes_crypt(struct geode_aes_op *op) static int geode_setkey_cip(struct crypto_tfm *tfm, const u8 *key, unsigned int len) { - struct geode_aes_op *op = crypto_tfm_ctx(tfm); + struct geode_aes_tfm_ctx *tctx = crypto_tfm_ctx(tfm); unsigned int ret; - op->keylen = len; + tctx->keylen = len; if (len == AES_KEYSIZE_128) { - memcpy(op->key, key, len); + memcpy(tctx->key, key, len); return 0; } @@ -137,135 +132,93 @@ static int geode_setkey_cip(struct crypto_tfm *tfm, const u8 *key, /* * The requested key size is not supported by HW, do a fallback */ - op->fallback.cip->base.crt_flags &= ~CRYPTO_TFM_REQ_MASK; - op->fallback.cip->base.crt_flags |= (tfm->crt_flags & CRYPTO_TFM_REQ_MASK); + tctx->fallback.cip->base.crt_flags &= ~CRYPTO_TFM_REQ_MASK; + tctx->fallback.cip->base.crt_flags |= + (tfm->crt_flags & CRYPTO_TFM_REQ_MASK); - ret = crypto_cipher_setkey(op->fallback.cip, key, len); + ret = crypto_cipher_setkey(tctx->fallback.cip, key, len); if (ret) { tfm->crt_flags &= ~CRYPTO_TFM_RES_MASK; - tfm->crt_flags |= (op->fallback.cip->base.crt_flags & CRYPTO_TFM_RES_MASK); + tfm->crt_flags |= (tctx->fallback.cip->base.crt_flags & + CRYPTO_TFM_RES_MASK); } return ret; } -static int geode_setkey_blk(struct crypto_tfm *tfm, const u8 *key, - unsigned int len) +static int geode_setkey_skcipher(struct crypto_skcipher *tfm, const u8 *key, + unsigned int len) { - struct geode_aes_op *op = crypto_tfm_ctx(tfm); + struct geode_aes_tfm_ctx *tctx = crypto_skcipher_ctx(tfm); unsigned int ret; - op->keylen = len; + tctx->keylen = len; if (len == AES_KEYSIZE_128) { - memcpy(op->key, key, len); + memcpy(tctx->key, key, len); return 0; } if (len != AES_KEYSIZE_192 && len != AES_KEYSIZE_256) { /* not supported at all */ - tfm->crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN; + crypto_skcipher_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN); return -EINVAL; } /* * The requested key size is not supported by HW, do a fallback */ - op->fallback.blk->base.crt_flags &= ~CRYPTO_TFM_REQ_MASK; - op->fallback.blk->base.crt_flags |= (tfm->crt_flags & CRYPTO_TFM_REQ_MASK); - - ret = crypto_blkcipher_setkey(op->fallback.blk, key, len); - if (ret) { - tfm->crt_flags &= ~CRYPTO_TFM_RES_MASK; - tfm->crt_flags |= (op->fallback.blk->base.crt_flags & CRYPTO_TFM_RES_MASK); - } - return ret; -} - -static int fallback_blk_dec(struct blkcipher_desc *desc, - struct scatterlist *dst, struct scatterlist *src, - unsigned int nbytes) -{ - unsigned int ret; - struct crypto_blkcipher *tfm; - struct geode_aes_op *op = crypto_blkcipher_ctx(desc->tfm); - - tfm = desc->tfm; - desc->tfm = op->fallback.blk; - - ret = crypto_blkcipher_decrypt_iv(desc, dst, src, nbytes); - - desc->tfm = tfm; - return ret; -} -static int fallback_blk_enc(struct blkcipher_desc *desc, - struct scatterlist *dst, struct scatterlist *src, - unsigned int nbytes) -{ - unsigned int ret; - struct crypto_blkcipher *tfm; - struct geode_aes_op *op = crypto_blkcipher_ctx(desc->tfm); - - tfm = desc->tfm; - desc->tfm = op->fallback.blk; - - ret = crypto_blkcipher_encrypt_iv(desc, dst, src, nbytes); - - desc->tfm = tfm; + crypto_skcipher_clear_flags(tctx->fallback.skcipher, + CRYPTO_TFM_REQ_MASK); + crypto_skcipher_set_flags(tctx->fallback.skcipher, + crypto_skcipher_get_flags(tfm) & + CRYPTO_TFM_REQ_MASK); + ret = crypto_skcipher_setkey(tctx->fallback.skcipher, key, len); + crypto_skcipher_set_flags(tfm, + crypto_skcipher_get_flags(tctx->fallback.skcipher) & + CRYPTO_TFM_RES_MASK); return ret; } static void geode_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) { - struct geode_aes_op *op = crypto_tfm_ctx(tfm); + const struct geode_aes_tfm_ctx *tctx = crypto_tfm_ctx(tfm); - if (unlikely(op->keylen != AES_KEYSIZE_128)) { - crypto_cipher_encrypt_one(op->fallback.cip, out, in); + if (unlikely(tctx->keylen != AES_KEYSIZE_128)) { + crypto_cipher_encrypt_one(tctx->fallback.cip, out, in); return; } - op->src = (void *) in; - op->dst = (void *) out; - op->mode = AES_MODE_ECB; - op->flags = 0; - op->len = AES_BLOCK_SIZE; - op->dir = AES_DIR_ENCRYPT; - - geode_aes_crypt(op); + geode_aes_crypt(tctx, in, out, AES_BLOCK_SIZE, NULL, + AES_MODE_ECB, AES_DIR_ENCRYPT); } static void geode_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) { - struct geode_aes_op *op = crypto_tfm_ctx(tfm); + const struct geode_aes_tfm_ctx *tctx = crypto_tfm_ctx(tfm); - if (unlikely(op->keylen != AES_KEYSIZE_128)) { - crypto_cipher_decrypt_one(op->fallback.cip, out, in); + if (unlikely(tctx->keylen != AES_KEYSIZE_128)) { + crypto_cipher_decrypt_one(tctx->fallback.cip, out, in); return; } - op->src = (void *) in; - op->dst = (void *) out; - op->mode = AES_MODE_ECB; - op->flags = 0; - op->len = AES_BLOCK_SIZE; - op->dir = AES_DIR_DECRYPT; - - geode_aes_crypt(op); + geode_aes_crypt(tctx, in, out, AES_BLOCK_SIZE, NULL, + AES_MODE_ECB, AES_DIR_DECRYPT); } static int fallback_init_cip(struct crypto_tfm *tfm) { const char *name = crypto_tfm_alg_name(tfm); - struct geode_aes_op *op = crypto_tfm_ctx(tfm); + struct geode_aes_tfm_ctx *tctx = crypto_tfm_ctx(tfm); - op->fallback.cip = crypto_alloc_cipher(name, 0, - CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK); + tctx->fallback.cip = crypto_alloc_cipher(name, 0, + CRYPTO_ALG_NEED_FALLBACK); - if (IS_ERR(op->fallback.cip)) { + if (IS_ERR(tctx->fallback.cip)) { printk(KERN_ERR "Error allocating fallback algo %s\n", name); - return PTR_ERR(op->fallback.cip); + return PTR_ERR(tctx->fallback.cip); } return 0; @@ -273,10 +226,9 @@ static int fallback_init_cip(struct crypto_tfm *tfm) static void fallback_exit_cip(struct crypto_tfm *tfm) { - struct geode_aes_op *op = crypto_tfm_ctx(tfm); + struct geode_aes_tfm_ctx *tctx = crypto_tfm_ctx(tfm); - crypto_free_cipher(op->fallback.cip); - op->fallback.cip = NULL; + crypto_free_cipher(tctx->fallback.cip); } static struct crypto_alg geode_alg = { @@ -289,7 +241,7 @@ static struct crypto_alg geode_alg = { .cra_init = fallback_init_cip, .cra_exit = fallback_exit_cip, .cra_blocksize = AES_BLOCK_SIZE, - .cra_ctxsize = sizeof(struct geode_aes_op), + .cra_ctxsize = sizeof(struct geode_aes_tfm_ctx), .cra_module = THIS_MODULE, .cra_u = { .cipher = { @@ -302,209 +254,126 @@ static struct crypto_alg geode_alg = { } }; -static int -geode_cbc_decrypt(struct blkcipher_desc *desc, - struct scatterlist *dst, struct scatterlist *src, - unsigned int nbytes) +static int geode_init_skcipher(struct crypto_skcipher *tfm) { - struct geode_aes_op *op = crypto_blkcipher_ctx(desc->tfm); - struct blkcipher_walk walk; - int err, ret; - - if (unlikely(op->keylen != AES_KEYSIZE_128)) - return fallback_blk_dec(desc, dst, src, nbytes); - - blkcipher_walk_init(&walk, dst, src, nbytes); - err = blkcipher_walk_virt(desc, &walk); - op->iv = walk.iv; - - while ((nbytes = walk.nbytes)) { - op->src = walk.src.virt.addr, - op->dst = walk.dst.virt.addr; - op->mode = AES_MODE_CBC; - op->len = nbytes - (nbytes % AES_BLOCK_SIZE); - op->dir = AES_DIR_DECRYPT; + const char *name = crypto_tfm_alg_name(&tfm->base); + struct geode_aes_tfm_ctx *tctx = crypto_skcipher_ctx(tfm); - ret = geode_aes_crypt(op); - - nbytes -= ret; - err = blkcipher_walk_done(desc, &walk, nbytes); + tctx->fallback.skcipher = + crypto_alloc_skcipher(name, 0, CRYPTO_ALG_NEED_FALLBACK | + CRYPTO_ALG_ASYNC); + if (IS_ERR(tctx->fallback.skcipher)) { + printk(KERN_ERR "Error allocating fallback algo %s\n", name); + return PTR_ERR(tctx->fallback.skcipher); } - return err; + crypto_skcipher_set_reqsize(tfm, sizeof(struct skcipher_request) + + crypto_skcipher_reqsize(tctx->fallback.skcipher)); + return 0; } -static int -geode_cbc_encrypt(struct blkcipher_desc *desc, - struct scatterlist *dst, struct scatterlist *src, - unsigned int nbytes) +static void geode_exit_skcipher(struct crypto_skcipher *tfm) { - struct geode_aes_op *op = crypto_blkcipher_ctx(desc->tfm); - struct blkcipher_walk walk; - int err, ret; - - if (unlikely(op->keylen != AES_KEYSIZE_128)) - return fallback_blk_enc(desc, dst, src, nbytes); - - blkcipher_walk_init(&walk, dst, src, nbytes); - err = blkcipher_walk_virt(desc, &walk); - op->iv = walk.iv; - - while ((nbytes = walk.nbytes)) { - op->src = walk.src.virt.addr, - op->dst = walk.dst.virt.addr; - op->mode = AES_MODE_CBC; - op->len = nbytes - (nbytes % AES_BLOCK_SIZE); - op->dir = AES_DIR_ENCRYPT; - - ret = geode_aes_crypt(op); - nbytes -= ret; - err = blkcipher_walk_done(desc, &walk, nbytes); - } + struct geode_aes_tfm_ctx *tctx = crypto_skcipher_ctx(tfm); - return err; + crypto_free_skcipher(tctx->fallback.skcipher); } -static int fallback_init_blk(struct crypto_tfm *tfm) +static int geode_skcipher_crypt(struct skcipher_request *req, int mode, int dir) { - const char *name = crypto_tfm_alg_name(tfm); - struct geode_aes_op *op = crypto_tfm_ctx(tfm); + struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); + const struct geode_aes_tfm_ctx *tctx = crypto_skcipher_ctx(tfm); + struct skcipher_walk walk; + unsigned int nbytes; + int err; + + if (unlikely(tctx->keylen != AES_KEYSIZE_128)) { + struct skcipher_request *subreq = skcipher_request_ctx(req); + + *subreq = *req; + skcipher_request_set_tfm(subreq, tctx->fallback.skcipher); + if (dir == AES_DIR_DECRYPT) + return crypto_skcipher_decrypt(subreq); + else + return crypto_skcipher_encrypt(subreq); + } - op->fallback.blk = crypto_alloc_blkcipher(name, 0, - CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK); + err = skcipher_walk_virt(&walk, req, false); - if (IS_ERR(op->fallback.blk)) { - printk(KERN_ERR "Error allocating fallback algo %s\n", name); - return PTR_ERR(op->fallback.blk); + while ((nbytes = walk.nbytes) != 0) { + geode_aes_crypt(tctx, walk.src.virt.addr, walk.dst.virt.addr, + round_down(nbytes, AES_BLOCK_SIZE), + walk.iv, mode, dir); + err = skcipher_walk_done(&walk, nbytes % AES_BLOCK_SIZE); } - return 0; + return err; } -static void fallback_exit_blk(struct crypto_tfm *tfm) +static int geode_cbc_encrypt(struct skcipher_request *req) { - struct geode_aes_op *op = crypto_tfm_ctx(tfm); - - crypto_free_blkcipher(op->fallback.blk); - op->fallback.blk = NULL; + return geode_skcipher_crypt(req, AES_MODE_CBC, AES_DIR_ENCRYPT); } -static struct crypto_alg geode_cbc_alg = { - .cra_name = "cbc(aes)", - .cra_driver_name = "cbc-aes-geode", - .cra_priority = 400, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | - CRYPTO_ALG_KERN_DRIVER_ONLY | - CRYPTO_ALG_NEED_FALLBACK, - .cra_init = fallback_init_blk, - .cra_exit = fallback_exit_blk, - .cra_blocksize = AES_BLOCK_SIZE, - .cra_ctxsize = sizeof(struct geode_aes_op), - .cra_alignmask = 15, - .cra_type = &crypto_blkcipher_type, - .cra_module = THIS_MODULE, - .cra_u = { - .blkcipher = { - .min_keysize = AES_MIN_KEY_SIZE, - .max_keysize = AES_MAX_KEY_SIZE, - .setkey = geode_setkey_blk, - .encrypt = geode_cbc_encrypt, - .decrypt = geode_cbc_decrypt, - .ivsize = AES_BLOCK_SIZE, - } - } -}; - -static int -geode_ecb_decrypt(struct blkcipher_desc *desc, - struct scatterlist *dst, struct scatterlist *src, - unsigned int nbytes) +static int geode_cbc_decrypt(struct skcipher_request *req) { - struct geode_aes_op *op = crypto_blkcipher_ctx(desc->tfm); - struct blkcipher_walk walk; - int err, ret; - - if (unlikely(op->keylen != AES_KEYSIZE_128)) - return fallback_blk_dec(desc, dst, src, nbytes); - - blkcipher_walk_init(&walk, dst, src, nbytes); - err = blkcipher_walk_virt(desc, &walk); - - while ((nbytes = walk.nbytes)) { - op->src = walk.src.virt.addr, - op->dst = walk.dst.virt.addr; - op->mode = AES_MODE_ECB; - op->len = nbytes - (nbytes % AES_BLOCK_SIZE); - op->dir = AES_DIR_DECRYPT; - - ret = geode_aes_crypt(op); - nbytes -= ret; - err = blkcipher_walk_done(desc, &walk, nbytes); - } - - return err; + return geode_skcipher_crypt(req, AES_MODE_CBC, AES_DIR_DECRYPT); } -static int -geode_ecb_encrypt(struct blkcipher_desc *desc, - struct scatterlist *dst, struct scatterlist *src, - unsigned int nbytes) +static int geode_ecb_encrypt(struct skcipher_request *req) { - struct geode_aes_op *op = crypto_blkcipher_ctx(desc->tfm); - struct blkcipher_walk walk; - int err, ret; - - if (unlikely(op->keylen != AES_KEYSIZE_128)) - return fallback_blk_enc(desc, dst, src, nbytes); - - blkcipher_walk_init(&walk, dst, src, nbytes); - err = blkcipher_walk_virt(desc, &walk); - - while ((nbytes = walk.nbytes)) { - op->src = walk.src.virt.addr, - op->dst = walk.dst.virt.addr; - op->mode = AES_MODE_ECB; - op->len = nbytes - (nbytes % AES_BLOCK_SIZE); - op->dir = AES_DIR_ENCRYPT; - - ret = geode_aes_crypt(op); - nbytes -= ret; - ret = blkcipher_walk_done(desc, &walk, nbytes); - } + return geode_skcipher_crypt(req, AES_MODE_ECB, AES_DIR_ENCRYPT); +} - return err; +static int geode_ecb_decrypt(struct skcipher_request *req) +{ + return geode_skcipher_crypt(req, AES_MODE_ECB, AES_DIR_DECRYPT); } -static struct crypto_alg geode_ecb_alg = { - .cra_name = "ecb(aes)", - .cra_driver_name = "ecb-aes-geode", - .cra_priority = 400, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | - CRYPTO_ALG_KERN_DRIVER_ONLY | - CRYPTO_ALG_NEED_FALLBACK, - .cra_init = fallback_init_blk, - .cra_exit = fallback_exit_blk, - .cra_blocksize = AES_BLOCK_SIZE, - .cra_ctxsize = sizeof(struct geode_aes_op), - .cra_alignmask = 15, - .cra_type = &crypto_blkcipher_type, - .cra_module = THIS_MODULE, - .cra_u = { - .blkcipher = { - .min_keysize = AES_MIN_KEY_SIZE, - .max_keysize = AES_MAX_KEY_SIZE, - .setkey = geode_setkey_blk, - .encrypt = geode_ecb_encrypt, - .decrypt = geode_ecb_decrypt, - } - } +static struct skcipher_alg geode_skcipher_algs[] = { + { + .base.cra_name = "cbc(aes)", + .base.cra_driver_name = "cbc-aes-geode", + .base.cra_priority = 400, + .base.cra_flags = CRYPTO_ALG_KERN_DRIVER_ONLY | + CRYPTO_ALG_NEED_FALLBACK, + .base.cra_blocksize = AES_BLOCK_SIZE, + .base.cra_ctxsize = sizeof(struct geode_aes_tfm_ctx), + .base.cra_alignmask = 15, + .base.cra_module = THIS_MODULE, + .init = geode_init_skcipher, + .exit = geode_exit_skcipher, + .setkey = geode_setkey_skcipher, + .encrypt = geode_cbc_encrypt, + .decrypt = geode_cbc_decrypt, + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + }, { + .base.cra_name = "ecb(aes)", + .base.cra_driver_name = "ecb-aes-geode", + .base.cra_priority = 400, + .base.cra_flags = CRYPTO_ALG_KERN_DRIVER_ONLY | + CRYPTO_ALG_NEED_FALLBACK, + .base.cra_blocksize = AES_BLOCK_SIZE, + .base.cra_ctxsize = sizeof(struct geode_aes_tfm_ctx), + .base.cra_alignmask = 15, + .base.cra_module = THIS_MODULE, + .init = geode_init_skcipher, + .exit = geode_exit_skcipher, + .setkey = geode_setkey_skcipher, + .encrypt = geode_ecb_encrypt, + .decrypt = geode_ecb_decrypt, + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + }, }; static void geode_aes_remove(struct pci_dev *dev) { crypto_unregister_alg(&geode_alg); - crypto_unregister_alg(&geode_ecb_alg); - crypto_unregister_alg(&geode_cbc_alg); + crypto_unregister_skciphers(geode_skcipher_algs, + ARRAY_SIZE(geode_skcipher_algs)); pci_iounmap(dev, _iobase); _iobase = NULL; @@ -542,20 +411,14 @@ static int geode_aes_probe(struct pci_dev *dev, const struct pci_device_id *id) if (ret) goto eiomap; - ret = crypto_register_alg(&geode_ecb_alg); + ret = crypto_register_skciphers(geode_skcipher_algs, + ARRAY_SIZE(geode_skcipher_algs)); if (ret) goto ealg; - ret = crypto_register_alg(&geode_cbc_alg); - if (ret) - goto eecb; - dev_notice(&dev->dev, "GEODE AES engine enabled.\n"); return 0; - eecb: - crypto_unregister_alg(&geode_ecb_alg); - ealg: crypto_unregister_alg(&geode_alg); diff --git a/drivers/crypto/geode-aes.h b/drivers/crypto/geode-aes.h index f442ca972e3c0c88aa87e7790e5ada0b510b0155..157443dc6d8aa544476a99308e67e7d264f6c79f 100644 --- a/drivers/crypto/geode-aes.h +++ b/drivers/crypto/geode-aes.h @@ -50,21 +50,10 @@ #define AES_OP_TIMEOUT 0x50000 -struct geode_aes_op { - - void *src; - void *dst; - - u32 mode; - u32 dir; - u32 flags; - int len; - +struct geode_aes_tfm_ctx { u8 key[AES_KEYSIZE_128]; - u8 *iv; - union { - struct crypto_blkcipher *blk; + struct crypto_skcipher *skcipher; struct crypto_cipher *cip; } fallback; u32 keylen; diff --git a/drivers/crypto/hisilicon/sec/sec_algs.c b/drivers/crypto/hisilicon/sec/sec_algs.c index db2983c51f1e667b40f7cc793eca6d4b877c21d5..bf9658800bda5b0b55525b89e87fb1656cc5b401 100644 --- a/drivers/crypto/hisilicon/sec/sec_algs.c +++ b/drivers/crypto/hisilicon/sec/sec_algs.c @@ -153,6 +153,24 @@ static void sec_alg_skcipher_init_context(struct crypto_skcipher *atfm, ctx->cipher_alg); } +static void sec_free_hw_sgl(struct sec_hw_sgl *hw_sgl, + dma_addr_t psec_sgl, struct sec_dev_info *info) +{ + struct sec_hw_sgl *sgl_current, *sgl_next; + dma_addr_t sgl_next_dma; + + sgl_current = hw_sgl; + while (sgl_current) { + sgl_next = sgl_current->next; + sgl_next_dma = sgl_current->next_sgl; + + dma_pool_free(info->hw_sgl_pool, sgl_current, psec_sgl); + + sgl_current = sgl_next; + psec_sgl = sgl_next_dma; + } +} + static int sec_alloc_and_fill_hw_sgl(struct sec_hw_sgl **sec_sgl, dma_addr_t *psec_sgl, struct scatterlist *sgl, @@ -199,36 +217,12 @@ static int sec_alloc_and_fill_hw_sgl(struct sec_hw_sgl **sec_sgl, return 0; err_free_hw_sgls: - sgl_current = *sec_sgl; - while (sgl_current) { - sgl_next = sgl_current->next; - dma_pool_free(info->hw_sgl_pool, sgl_current, - sgl_current->next_sgl); - sgl_current = sgl_next; - } + sec_free_hw_sgl(*sec_sgl, *psec_sgl, info); *psec_sgl = 0; return ret; } -static void sec_free_hw_sgl(struct sec_hw_sgl *hw_sgl, - dma_addr_t psec_sgl, struct sec_dev_info *info) -{ - struct sec_hw_sgl *sgl_current, *sgl_next; - dma_addr_t sgl_next_dma; - - sgl_current = hw_sgl; - while (sgl_current) { - sgl_next = sgl_current->next; - sgl_next_dma = sgl_current->next_sgl; - - dma_pool_free(info->hw_sgl_pool, sgl_current, psec_sgl); - - sgl_current = sgl_next; - psec_sgl = sgl_next_dma; - } -} - static int sec_alg_skcipher_setkey(struct crypto_skcipher *tfm, const u8 *key, unsigned int keylen, enum sec_cipher_alg alg) diff --git a/drivers/crypto/inside-secure/safexcel_hash.c b/drivers/crypto/inside-secure/safexcel_hash.c index ac9282c1a5ec13048037a8c84db819c27fc88fdf..f3b02c00b78463c78bb810c0649ab3d3468b4108 100644 --- a/drivers/crypto/inside-secure/safexcel_hash.c +++ b/drivers/crypto/inside-secure/safexcel_hash.c @@ -50,10 +50,12 @@ struct safexcel_ahash_req { static inline u64 safexcel_queued_len(struct safexcel_ahash_req *req) { - if (req->len[1] > req->processed[1]) - return 0xffffffff - (req->len[0] - req->processed[0]); + u64 len, processed; - return req->len[0] - req->processed[0]; + len = (0xffffffff * req->len[1]) + req->len[0]; + processed = (0xffffffff * req->processed[1]) + req->processed[0]; + + return len - processed; } static void safexcel_hash_token(struct safexcel_command_desc *cdesc, @@ -486,7 +488,7 @@ static int safexcel_ahash_exit_inv(struct crypto_tfm *tfm) struct safexcel_inv_result result = {}; int ring = ctx->base.ring; - memset(req, 0, sizeof(struct ahash_request)); + memset(req, 0, EIP197_AHASH_REQ_SIZE); /* create invalidation request */ init_completion(&result.completion); diff --git a/drivers/crypto/picoxcell_crypto.c b/drivers/crypto/picoxcell_crypto.c index a28f1d18fe01fda8626e20418e4b9bae2a6cb494..1895452c2c2bb51356affb3e3795a35819a72b9e 100644 --- a/drivers/crypto/picoxcell_crypto.c +++ b/drivers/crypto/picoxcell_crypto.c @@ -1615,6 +1615,11 @@ static const struct of_device_id spacc_of_id_table[] = { MODULE_DEVICE_TABLE(of, spacc_of_id_table); #endif /* CONFIG_OF */ +static void spacc_tasklet_kill(void *data) +{ + tasklet_kill(data); +} + static int spacc_probe(struct platform_device *pdev) { int i, err, ret; @@ -1658,6 +1663,14 @@ static int spacc_probe(struct platform_device *pdev) return -ENXIO; } + tasklet_init(&engine->complete, spacc_spacc_complete, + (unsigned long)engine); + + ret = devm_add_action(&pdev->dev, spacc_tasklet_kill, + &engine->complete); + if (ret) + return ret; + if (devm_request_irq(&pdev->dev, irq->start, spacc_spacc_irq, 0, engine->name, engine)) { dev_err(engine->dev, "failed to request IRQ\n"); @@ -1715,8 +1728,6 @@ static int spacc_probe(struct platform_device *pdev) INIT_LIST_HEAD(&engine->completed); INIT_LIST_HEAD(&engine->in_progress); engine->in_flight = 0; - tasklet_init(&engine->complete, spacc_spacc_complete, - (unsigned long)engine); platform_set_drvdata(pdev, engine); diff --git a/drivers/crypto/sunxi-ss/sun4i-ss-hash.c b/drivers/crypto/sunxi-ss/sun4i-ss-hash.c index 1a724263761bc52c5f2a518f8e9560e5262c9474..2d178e013535c3bdf7872147be8cae6a347b98bf 100644 --- a/drivers/crypto/sunxi-ss/sun4i-ss-hash.c +++ b/drivers/crypto/sunxi-ss/sun4i-ss-hash.c @@ -179,7 +179,7 @@ static int sun4i_hash(struct ahash_request *areq) */ unsigned int i = 0, end, fill, min_fill, nwait, nbw = 0, j = 0, todo; unsigned int in_i = 0; - u32 spaces, rx_cnt = SS_RX_DEFAULT, bf[32] = {0}, wb = 0, v, ivmode = 0; + u32 spaces, rx_cnt = SS_RX_DEFAULT, bf[32] = {0}, v, ivmode = 0; struct sun4i_req_ctx *op = ahash_request_ctx(areq); struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); struct sun4i_tfm_ctx *tfmctx = crypto_ahash_ctx(tfm); @@ -188,6 +188,7 @@ static int sun4i_hash(struct ahash_request *areq) struct sg_mapping_iter mi; int in_r, err = 0; size_t copied = 0; + __le32 wb = 0; dev_dbg(ss->dev, "%s %s bc=%llu len=%u mode=%x wl=%u h0=%0x", __func__, crypto_tfm_alg_name(areq->base.tfm), @@ -399,7 +400,7 @@ static int sun4i_hash(struct ahash_request *areq) nbw = op->len - 4 * nwait; if (nbw) { - wb = *(u32 *)(op->buf + nwait * 4); + wb = cpu_to_le32(*(u32 *)(op->buf + nwait * 4)); wb &= GENMASK((nbw * 8) - 1, 0); op->byte_count += nbw; @@ -408,7 +409,7 @@ static int sun4i_hash(struct ahash_request *areq) /* write the remaining bytes of the nbw buffer */ wb |= ((1 << 7) << (nbw * 8)); - bf[j++] = wb; + bf[j++] = le32_to_cpu(wb); /* * number of space to pad to obtain 64o minus 8(size) minus 4 (final 1) @@ -427,13 +428,13 @@ static int sun4i_hash(struct ahash_request *areq) /* write the length of data */ if (op->mode == SS_OP_SHA1) { - __be64 bits = cpu_to_be64(op->byte_count << 3); - bf[j++] = lower_32_bits(bits); - bf[j++] = upper_32_bits(bits); + __be64 *bits = (__be64 *)&bf[j]; + *bits = cpu_to_be64(op->byte_count << 3); + j += 2; } else { - __le64 bits = op->byte_count << 3; - bf[j++] = lower_32_bits(bits); - bf[j++] = upper_32_bits(bits); + __le64 *bits = (__le64 *)&bf[j]; + *bits = cpu_to_le64(op->byte_count << 3); + j += 2; } writesl(ss->base + SS_RXFIFO, bf, j); @@ -475,7 +476,7 @@ static int sun4i_hash(struct ahash_request *areq) } } else { for (i = 0; i < 4; i++) { - v = readl(ss->base + SS_MD0 + i * 4); + v = cpu_to_le32(readl(ss->base + SS_MD0 + i * 4)); memcpy(areq->result + i * 4, &v, 4); } } diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index 634ae487c372e0213ae9d8b123d8def8a6abcd80..db5f939f5aa35b57ffb57daf06bd82ccc5895794 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -974,8 +974,8 @@ static void ipsec_esp_unmap(struct device *dev, DMA_FROM_DEVICE); unmap_single_talitos_ptr(dev, civ_ptr, DMA_TO_DEVICE); - talitos_sg_unmap(dev, edesc, areq->src, areq->dst, cryptlen, - areq->assoclen); + talitos_sg_unmap(dev, edesc, areq->src, areq->dst, + cryptlen + authsize, areq->assoclen); if (edesc->dma_len) dma_unmap_single(dev, edesc->dma_link_tbl, edesc->dma_len, @@ -996,30 +996,15 @@ static void ipsec_esp_encrypt_done(struct device *dev, struct talitos_desc *desc, void *context, int err) { - struct talitos_private *priv = dev_get_drvdata(dev); - bool is_sec1 = has_ftr_sec1(priv); struct aead_request *areq = context; struct crypto_aead *authenc = crypto_aead_reqtfm(areq); - unsigned int authsize = crypto_aead_authsize(authenc); unsigned int ivsize = crypto_aead_ivsize(authenc); struct talitos_edesc *edesc; - void *icvdata; edesc = container_of(desc, struct talitos_edesc, desc); ipsec_esp_unmap(dev, edesc, areq, true); - /* copy the generated ICV to dst */ - if (edesc->icv_ool) { - if (is_sec1) - icvdata = edesc->buf + areq->assoclen + areq->cryptlen; - else - icvdata = &edesc->link_tbl[edesc->src_nents + - edesc->dst_nents + 2]; - sg_pcopy_from_buffer(areq->dst, edesc->dst_nents ? : 1, icvdata, - authsize, areq->assoclen + areq->cryptlen); - } - dma_unmap_single(dev, edesc->iv_dma, ivsize, DMA_TO_DEVICE); kfree(edesc); @@ -1036,39 +1021,15 @@ static void ipsec_esp_decrypt_swauth_done(struct device *dev, unsigned int authsize = crypto_aead_authsize(authenc); struct talitos_edesc *edesc; char *oicv, *icv; - struct talitos_private *priv = dev_get_drvdata(dev); - bool is_sec1 = has_ftr_sec1(priv); edesc = container_of(desc, struct talitos_edesc, desc); ipsec_esp_unmap(dev, edesc, req, false); if (!err) { - char icvdata[SHA512_DIGEST_SIZE]; - int nents = edesc->dst_nents ? : 1; - unsigned int len = req->assoclen + req->cryptlen; - /* auth check */ - if (nents > 1) { - sg_pcopy_to_buffer(req->dst, nents, icvdata, authsize, - len - authsize); - icv = icvdata; - } else { - icv = (char *)sg_virt(req->dst) + len - authsize; - } - - if (edesc->dma_len) { - if (is_sec1) - oicv = (char *)&edesc->dma_link_tbl + - req->assoclen + req->cryptlen; - else - oicv = (char *) - &edesc->link_tbl[edesc->src_nents + - edesc->dst_nents + 2]; - if (edesc->icv_ool) - icv = oicv + authsize; - } else - oicv = (char *)&edesc->link_tbl[0]; + oicv = edesc->buf + edesc->dma_len; + icv = oicv - authsize; err = crypto_memneq(oicv, icv, authsize) ? -EBADMSG : 0; } @@ -1104,11 +1065,12 @@ static void ipsec_esp_decrypt_hwauth_done(struct device *dev, * stop at cryptlen bytes */ static int sg_to_link_tbl_offset(struct scatterlist *sg, int sg_count, - unsigned int offset, int cryptlen, + unsigned int offset, int datalen, int elen, struct talitos_ptr *link_tbl_ptr) { - int n_sg = sg_count; + int n_sg = elen ? sg_count + 1 : sg_count; int count = 0; + int cryptlen = datalen + elen; while (cryptlen && sg && n_sg--) { unsigned int len = sg_dma_len(sg); @@ -1123,11 +1085,20 @@ static int sg_to_link_tbl_offset(struct scatterlist *sg, int sg_count, if (len > cryptlen) len = cryptlen; + if (datalen > 0 && len > datalen) { + to_talitos_ptr(link_tbl_ptr + count, + sg_dma_address(sg) + offset, datalen, 0); + to_talitos_ptr_ext_set(link_tbl_ptr + count, 0, 0); + count++; + len -= datalen; + offset += datalen; + } to_talitos_ptr(link_tbl_ptr + count, sg_dma_address(sg) + offset, len, 0); to_talitos_ptr_ext_set(link_tbl_ptr + count, 0, 0); count++; cryptlen -= len; + datalen -= len; offset = 0; next: @@ -1137,7 +1108,7 @@ static int sg_to_link_tbl_offset(struct scatterlist *sg, int sg_count, /* tag end of link table */ if (count > 0) to_talitos_ptr_ext_set(link_tbl_ptr + count - 1, - DESC_PTR_LNKTBL_RETURN, 0); + DESC_PTR_LNKTBL_RET, 0); return count; } @@ -1145,7 +1116,8 @@ static int sg_to_link_tbl_offset(struct scatterlist *sg, int sg_count, static int talitos_sg_map_ext(struct device *dev, struct scatterlist *src, unsigned int len, struct talitos_edesc *edesc, struct talitos_ptr *ptr, int sg_count, - unsigned int offset, int tbl_off, int elen) + unsigned int offset, int tbl_off, int elen, + bool force) { struct talitos_private *priv = dev_get_drvdata(dev); bool is_sec1 = has_ftr_sec1(priv); @@ -1155,7 +1127,7 @@ static int talitos_sg_map_ext(struct device *dev, struct scatterlist *src, return 1; } to_talitos_ptr_ext_set(ptr, elen, is_sec1); - if (sg_count == 1) { + if (sg_count == 1 && !force) { to_talitos_ptr(ptr, sg_dma_address(src) + offset, len, is_sec1); return sg_count; } @@ -1163,9 +1135,9 @@ static int talitos_sg_map_ext(struct device *dev, struct scatterlist *src, to_talitos_ptr(ptr, edesc->dma_link_tbl + offset, len, is_sec1); return sg_count; } - sg_count = sg_to_link_tbl_offset(src, sg_count, offset, len + elen, + sg_count = sg_to_link_tbl_offset(src, sg_count, offset, len, elen, &edesc->link_tbl[tbl_off]); - if (sg_count == 1) { + if (sg_count == 1 && !force) { /* Only one segment now, so no link tbl needed*/ copy_talitos_ptr(ptr, &edesc->link_tbl[tbl_off], is_sec1); return sg_count; @@ -1183,7 +1155,7 @@ static int talitos_sg_map(struct device *dev, struct scatterlist *src, unsigned int offset, int tbl_off) { return talitos_sg_map_ext(dev, src, len, edesc, ptr, sg_count, offset, - tbl_off, 0); + tbl_off, 0, false); } /* @@ -1211,6 +1183,7 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq, bool is_ipsec_esp = desc->hdr & DESC_HDR_TYPE_IPSEC_ESP; struct talitos_ptr *civ_ptr = &desc->ptr[is_ipsec_esp ? 2 : 3]; struct talitos_ptr *ckey_ptr = &desc->ptr[is_ipsec_esp ? 3 : 2]; + dma_addr_t dma_icv = edesc->dma_link_tbl + edesc->dma_len - authsize; /* hmac key */ to_talitos_ptr(&desc->ptr[0], ctx->dma_key, ctx->authkeylen, is_sec1); @@ -1250,7 +1223,8 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq, elen = authsize; ret = talitos_sg_map_ext(dev, areq->src, cryptlen, edesc, &desc->ptr[4], - sg_count, areq->assoclen, tbl_off, elen); + sg_count, areq->assoclen, tbl_off, elen, + false); if (ret > 1) { tbl_off += ret; @@ -1264,55 +1238,35 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq, dma_map_sg(dev, areq->dst, sg_count, DMA_FROM_DEVICE); } - ret = talitos_sg_map(dev, areq->dst, cryptlen, edesc, &desc->ptr[5], - sg_count, areq->assoclen, tbl_off); - - if (is_ipsec_esp) - to_talitos_ptr_ext_or(&desc->ptr[5], authsize, is_sec1); + if (is_ipsec_esp && encrypt) + elen = authsize; + else + elen = 0; + ret = talitos_sg_map_ext(dev, areq->dst, cryptlen, edesc, &desc->ptr[5], + sg_count, areq->assoclen, tbl_off, elen, + is_ipsec_esp && !encrypt); + tbl_off += ret; /* ICV data */ - if (ret > 1) { - tbl_off += ret; - edesc->icv_ool = true; - sync_needed = true; + edesc->icv_ool = !encrypt; - if (is_ipsec_esp) { - struct talitos_ptr *tbl_ptr = &edesc->link_tbl[tbl_off]; - int offset = (edesc->src_nents + edesc->dst_nents + 2) * - sizeof(struct talitos_ptr) + authsize; + if (!encrypt && is_ipsec_esp) { + struct talitos_ptr *tbl_ptr = &edesc->link_tbl[tbl_off]; - /* Add an entry to the link table for ICV data */ - to_talitos_ptr_ext_set(tbl_ptr - 1, 0, is_sec1); - to_talitos_ptr_ext_set(tbl_ptr, DESC_PTR_LNKTBL_RETURN, - is_sec1); + /* Add an entry to the link table for ICV data */ + to_talitos_ptr_ext_set(tbl_ptr - 1, 0, is_sec1); + to_talitos_ptr_ext_set(tbl_ptr, DESC_PTR_LNKTBL_RET, is_sec1); - /* icv data follows link tables */ - to_talitos_ptr(tbl_ptr, edesc->dma_link_tbl + offset, - authsize, is_sec1); - } else { - dma_addr_t addr = edesc->dma_link_tbl; - - if (is_sec1) - addr += areq->assoclen + cryptlen; - else - addr += sizeof(struct talitos_ptr) * tbl_off; - - to_talitos_ptr(&desc->ptr[6], addr, authsize, is_sec1); - } + /* icv data follows link tables */ + to_talitos_ptr(tbl_ptr, dma_icv, authsize, is_sec1); + to_talitos_ptr_ext_or(&desc->ptr[5], authsize, is_sec1); + sync_needed = true; + } else if (!encrypt) { + to_talitos_ptr(&desc->ptr[6], dma_icv, authsize, is_sec1); + sync_needed = true; } else if (!is_ipsec_esp) { - ret = talitos_sg_map(dev, areq->dst, authsize, edesc, - &desc->ptr[6], sg_count, areq->assoclen + - cryptlen, - tbl_off); - if (ret > 1) { - tbl_off += ret; - edesc->icv_ool = true; - sync_needed = true; - } else { - edesc->icv_ool = false; - } - } else { - edesc->icv_ool = false; + talitos_sg_map(dev, areq->dst, authsize, edesc, &desc->ptr[6], + sg_count, areq->assoclen + cryptlen, tbl_off); } /* iv out */ @@ -1395,18 +1349,18 @@ static struct talitos_edesc *talitos_edesc_alloc(struct device *dev, * and space for two sets of ICVs (stashed and generated) */ alloc_len = sizeof(struct talitos_edesc); - if (src_nents || dst_nents) { + if (src_nents || dst_nents || !encrypt) { if (is_sec1) dma_len = (src_nents ? src_len : 0) + - (dst_nents ? dst_len : 0); + (dst_nents ? dst_len : 0) + authsize; else dma_len = (src_nents + dst_nents + 2) * - sizeof(struct talitos_ptr) + authsize * 2; + sizeof(struct talitos_ptr) + authsize; alloc_len += dma_len; } else { dma_len = 0; - alloc_len += icv_stashing ? authsize : 0; } + alloc_len += icv_stashing ? authsize : 0; /* if its a ahash, add space for a second desc next to the first one */ if (is_sec1 && !dst) @@ -1500,11 +1454,7 @@ static int aead_decrypt(struct aead_request *req) edesc->desc.hdr = ctx->desc_hdr_template | DESC_HDR_DIR_INBOUND; /* stash incoming ICV for later cmp with ICV generated by the h/w */ - if (edesc->dma_len) - icvdata = (char *)&edesc->link_tbl[edesc->src_nents + - edesc->dst_nents + 2]; - else - icvdata = &edesc->link_tbl[0]; + icvdata = edesc->buf + edesc->dma_len; sg_pcopy_to_buffer(req->src, edesc->src_nents ? : 1, icvdata, authsize, req->assoclen + req->cryptlen - authsize); diff --git a/drivers/crypto/talitos.h b/drivers/crypto/talitos.h index 979f6a61e545f1aa54d9d895932b838572f3fd86..cb0137e131cc83942c713a325c3bb4c7ee1b3263 100644 --- a/drivers/crypto/talitos.h +++ b/drivers/crypto/talitos.h @@ -442,5 +442,5 @@ static inline bool has_ftr_sec1(struct talitos_private *priv) /* link table extent field bits */ #define DESC_PTR_LNKTBL_JUMP 0x80 -#define DESC_PTR_LNKTBL_RETURN 0x02 +#define DESC_PTR_LNKTBL_RET 0x02 #define DESC_PTR_LNKTBL_NEXT 0x01 diff --git a/drivers/crypto/virtio/Kconfig b/drivers/crypto/virtio/Kconfig index a4324b1383a43ee2b235475a48048b9e009f4e63..1df0bfa86eff40d1237fcc509cd6e1bd742d44d3 100644 --- a/drivers/crypto/virtio/Kconfig +++ b/drivers/crypto/virtio/Kconfig @@ -4,7 +4,6 @@ config CRYPTO_DEV_VIRTIO select CRYPTO_AEAD select CRYPTO_BLKCIPHER select CRYPTO_ENGINE - default m help This driver provides support for virtio crypto device. If you choose 'M' here, this module will be called virtio_crypto. diff --git a/drivers/crypto/virtio/virtio_crypto_algs.c b/drivers/crypto/virtio/virtio_crypto_algs.c index 523b712770ac50df05faea1f66f1b997a7fdc32a..38432721069f7b9a99b62405ff1f32356028c0c7 100644 --- a/drivers/crypto/virtio/virtio_crypto_algs.c +++ b/drivers/crypto/virtio/virtio_crypto_algs.c @@ -449,6 +449,11 @@ __virtio_crypto_ablkcipher_do_req(struct virtio_crypto_sym_request *vc_sym_req, goto free; } memcpy(iv, req->info, ivsize); + if (!vc_sym_req->encrypt) + scatterwalk_map_and_copy(req->info, req->src, + req->nbytes - AES_BLOCK_SIZE, + AES_BLOCK_SIZE, 0); + sg_init_one(&iv_sg, iv, ivsize); sgs[num_out++] = &iv_sg; vc_sym_req->iv = iv; @@ -585,6 +590,10 @@ static void virtio_crypto_ablkcipher_finalize_req( struct ablkcipher_request *req, int err) { + if (vc_sym_req->encrypt) + scatterwalk_map_and_copy(req->info, req->dst, + req->nbytes - AES_BLOCK_SIZE, + AES_BLOCK_SIZE, 0); crypto_finalize_ablkcipher_request(vc_sym_req->base.dataq->engine, req, err); kzfree(vc_sym_req->iv); diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index bc43003484b40186361c1be0bd6fe64429a6c682..80ab58fb6adf916534266ab31e1a3c349a527de6 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -177,7 +177,8 @@ config ARM_TEGRA_DEVFREQ config ARM_RK3399_DMC_DEVFREQ tristate "ARM RK3399 DMC DEVFREQ Driver" - depends on ARCH_ROCKCHIP + depends on (ARCH_ROCKCHIP && HAVE_ARM_SMCCC) || \ + (COMPILE_TEST && HAVE_ARM_SMCCC) select DEVFREQ_EVENT_ROCKCHIP_DFI select DEVFREQ_GOV_SIMPLE_ONDEMAND select PM_DEVFREQ_EVENT diff --git a/drivers/devfreq/arm-memlat-mon.c b/drivers/devfreq/arm-memlat-mon.c index 5065af695515676021f3686e7958c9ddceb8312e..db2ee56e7f3e1d6346d0a1f378e7102591387765 100644 --- a/drivers/devfreq/arm-memlat-mon.c +++ b/drivers/devfreq/arm-memlat-mon.c @@ -65,8 +65,20 @@ struct cpu_data { * defaults to using all of @cpu_grp's CPUs. * @miss_ev_id: The event code corresponding to the @miss_ev * perf event. Will be 0 for compute. + * @access_ev_id: The event code corresponding to the @access_ev + * perf event. Optional - only needed for writeback + * percent. + * @wb_ev_id: The event code corresponding to the @wb_ev perf + * event. Optional - only needed for writeback + * percent. * @miss_ev: The cache miss perf event exclusive to this * mon. Will be NULL for compute. + * @access_ev: The cache access perf event exclusive to this + * mon. Optional - only needed for writeback + * percent. + * @wb_ev: The cache writeback perf event exclusive to this + * mon. Optional - only needed for writeback + * percent. * @requested_update_ms: The mon's desired polling rate. The lowest * @requested_update_ms of all mons determines * @cpu_grp's update_ms. @@ -78,8 +90,12 @@ struct memlat_mon { bool is_active; cpumask_t cpus; unsigned int miss_ev_id; + unsigned int access_ev_id; + unsigned int wb_ev_id; unsigned int requested_update_ms; struct event_data *miss_ev; + struct event_data *access_ev; + struct event_data *wb_ev; struct memlat_hwmon hw; struct memlat_cpu_grp *cpu_grp; @@ -191,6 +207,11 @@ static void update_counts(struct memlat_cpu_grp *cpu_grp) unsigned int mon_idx = cpu - cpumask_first(&mon->cpus); read_event(&mon->miss_ev[mon_idx]); + + if (mon->wb_ev_id && mon->access_ev_id) { + read_event(&mon->wb_ev[mon_idx]); + read_event(&mon->access_ev[mon_idx]); + } } } } @@ -219,6 +240,13 @@ static unsigned long get_cnt(struct memlat_hwmon *hw) devstats->inst_count = 0; devstats->mem_count = 1; } + + if (mon->access_ev_id && mon->wb_ev_id) + devstats->wb_pct = + mult_frac(100, mon->wb_ev[mon_idx].last_delta, + mon->access_ev[mon_idx].last_delta); + else + devstats->wb_pct = 0; } return 0; @@ -365,6 +393,18 @@ static int start_hwmon(struct memlat_hwmon *hw) mon->miss_ev_id, attr); if (ret) goto unlock_out; + + if (mon->access_ev_id && mon->wb_ev_id) { + ret = set_event(&mon->access_ev[idx], cpu, + mon->access_ev_id, attr); + if (ret) + goto unlock_out; + + ret = set_event(&mon->wb_ev[idx], cpu, + mon->wb_ev_id, attr); + if (ret) + goto unlock_out; + } } } @@ -401,6 +441,7 @@ static void stop_hwmon(struct memlat_hwmon *hw) devstats->mem_count = 0; devstats->freq = 0; devstats->stall_pct = 0; + devstats->wb_pct = 0; } if (!cpu_grp->num_active_mons) { @@ -648,6 +689,8 @@ static int memlat_mon_probe(struct platform_device *pdev, bool is_compute) */ if (is_compute) { mon->miss_ev_id = 0; + mon->access_ev_id = 0; + mon->wb_ev_id = 0; ret = register_compute(dev, hw); } else { mon->miss_ev = @@ -668,6 +711,39 @@ static int memlat_mon_probe(struct platform_device *pdev, bool is_compute) } mon->miss_ev_id = event_id; + ret = of_property_read_u32(dev->of_node, "qcom,access-ev", + &event_id); + if (ret) + dev_dbg(dev, "Access event not specified. Skipping.\n"); + else + mon->access_ev_id = event_id; + + ret = of_property_read_u32(dev->of_node, "qcom,wb-ev", + &event_id); + if (ret) + dev_dbg(dev, "WB event not specified. Skipping.\n"); + else + mon->wb_ev_id = event_id; + + if (mon->wb_ev_id && mon->access_ev_id) { + mon->access_ev = + devm_kzalloc(dev, num_cpus * + sizeof(*mon->access_ev), + GFP_KERNEL); + if (!mon->access_ev) { + ret = -ENOMEM; + goto unlock_out; + } + + mon->wb_ev = + devm_kzalloc(dev, num_cpus * + sizeof(*mon->wb_ev), GFP_KERNEL); + if (!mon->wb_ev) { + ret = -ENOMEM; + goto unlock_out; + } + } + ret = register_memlat(dev, hw); } diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 25ef772a00dcc1e980e96a6f907d7fe62e2b26ed..234c14b8ec8d5293d2390eeb57ca33aba001981c 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -1033,6 +1033,14 @@ int devfreq_remove_governor(struct devfreq_governor *governor) } EXPORT_SYMBOL(devfreq_remove_governor); +static ssize_t name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct devfreq *devfreq = to_devfreq(dev); + return sprintf(buf, "%s\n", dev_name(devfreq->dev.parent)); +} +static DEVICE_ATTR_RO(name); + static ssize_t governor_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1368,6 +1376,7 @@ static ssize_t trans_stat_show(struct device *dev, static DEVICE_ATTR_RO(trans_stat); static struct attribute *devfreq_attrs[] = { + &dev_attr_name.attr, &dev_attr_governor.attr, &dev_attr_available_governors.attr, &dev_attr_cur_freq.attr, diff --git a/drivers/devfreq/event/Kconfig b/drivers/devfreq/event/Kconfig index cd949800eed962cffa34b599d3ce66f386763d11..8851bc4e8e3e17935b6fc109ff926722c3d68737 100644 --- a/drivers/devfreq/event/Kconfig +++ b/drivers/devfreq/event/Kconfig @@ -33,7 +33,7 @@ config DEVFREQ_EVENT_EXYNOS_PPMU config DEVFREQ_EVENT_ROCKCHIP_DFI tristate "ROCKCHIP DFI DEVFREQ event Driver" - depends on ARCH_ROCKCHIP + depends on ARCH_ROCKCHIP || COMPILE_TEST help This add the devfreq-event driver for Rockchip SoC. It provides DFI (DDR Monitor Module) driver to count ddr load. diff --git a/drivers/devfreq/governor_memlat.c b/drivers/devfreq/governor_memlat.c index ada844b672689bc56aec7fb205ddaf88769849a1..4948a144ab87109c03b37cd860e9dc5ca200753e 100644 --- a/drivers/devfreq/governor_memlat.c +++ b/drivers/devfreq/governor_memlat.c @@ -29,6 +29,8 @@ struct memlat_node { unsigned int ratio_ceil; unsigned int stall_floor; + unsigned int wb_pct_thres; + unsigned int wb_filter_ratio; bool mon_started; bool already_zero; struct list_head list; @@ -292,11 +294,14 @@ static int devfreq_memlat_get_freq(struct devfreq *df, hw->core_stats[i].inst_count, hw->core_stats[i].mem_count, hw->core_stats[i].freq, - hw->core_stats[i].stall_pct, ratio); - - if (ratio <= node->ratio_ceil - && hw->core_stats[i].stall_pct >= node->stall_floor - && hw->core_stats[i].freq > max_freq) { + hw->core_stats[i].stall_pct, + hw->core_stats[i].wb_pct, ratio); + + if (((ratio <= node->ratio_ceil + && hw->core_stats[i].stall_pct >= node->stall_floor) || + (hw->core_stats[i].wb_pct >= node->wb_pct_thres + && ratio <= node->wb_filter_ratio)) + && (hw->core_stats[i].freq > max_freq)) { lat_dev = i; max_freq = hw->core_stats[i].freq; } @@ -322,10 +327,14 @@ static int devfreq_memlat_get_freq(struct devfreq *df, gov_attr(ratio_ceil, 1U, 20000U); gov_attr(stall_floor, 0U, 100U); +gov_attr(wb_pct_thres, 0U, 100U); +gov_attr(wb_filter_ratio, 0U, 50000U); static struct attribute *memlat_dev_attr[] = { &dev_attr_ratio_ceil.attr, &dev_attr_stall_floor.attr, + &dev_attr_wb_pct_thres.attr, + &dev_attr_wb_filter_ratio.attr, &dev_attr_freq_map.attr, NULL, }; @@ -488,6 +497,8 @@ static struct memlat_node *register_common(struct device *dev, return ERR_PTR(-ENOMEM); node->ratio_ceil = 10; + node->wb_pct_thres = 100; + node->wb_filter_ratio = 25000; node->hw = hw; if (hw->get_child_of_node) { diff --git a/drivers/devfreq/governor_memlat.h b/drivers/devfreq/governor_memlat.h index 02989028ded619ba706ee2d6a508996f9f08048f..5cc7881e0f1a8d52da8237d71295115f135e4190 100644 --- a/drivers/devfreq/governor_memlat.h +++ b/drivers/devfreq/governor_memlat.h @@ -15,6 +15,9 @@ * @mem_count: Number of memory accesses made. * @freq: Effective frequency of the device in the * last interval. + * @wb_pct: The ratio of writebacks to accesses. Used as an + * indirect way to identify memory latency due to + * snoop activity. */ struct dev_stats { int id; @@ -22,6 +25,7 @@ struct dev_stats { unsigned long mem_count; unsigned long freq; unsigned long stall_pct; + unsigned long wb_pct; }; struct core_dev_map { diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index 7221983f18fc6023b80ec77410fc4a4e20791b2a..11ff54adb906f7e6844c8e05f14e286ac6ed9d82 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -92,10 +92,10 @@ static char *dmabuffs_dname(struct dentry *dentry, char *buffer, int buflen) goto out; } spin_unlock(&dentry->d_lock); - mutex_lock(&dmabuf->lock); + spin_lock(&dmabuf->name_lock); if (dmabuf->name) ret = strlcpy(name, dmabuf->name, DMA_BUF_NAME_LEN); - mutex_unlock(&dmabuf->lock); + spin_unlock(&dmabuf->name_lock); dmabuf_dent_put(dmabuf); out: return dynamic_dname(dentry, buffer, buflen, "/%s:%s", @@ -401,8 +401,10 @@ static long dma_buf_set_name(struct dma_buf *dmabuf, const char __user *buf) kfree(name); goto out_unlock; } + spin_lock(&dmabuf->name_lock); kfree(dmabuf->name); dmabuf->name = name; + spin_unlock(&dmabuf->name_lock); out_unlock: mutex_unlock(&dmabuf->lock); @@ -472,10 +474,10 @@ static void dma_buf_show_fdinfo(struct seq_file *m, struct file *file) /* Don't count the temporary reference taken inside procfs seq_show */ seq_printf(m, "count:\t%ld\n", file_count(dmabuf->file) - 1); seq_printf(m, "exp_name:\t%s\n", dmabuf->exp_name); - mutex_lock(&dmabuf->lock); + spin_lock(&dmabuf->name_lock); if (dmabuf->name) seq_printf(m, "name:\t%s\n", dmabuf->name); - mutex_unlock(&dmabuf->lock); + spin_unlock(&dmabuf->name_lock); } static const struct file_operations dma_buf_fops = { @@ -619,6 +621,7 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info) dmabuf->size = exp_info->size; dmabuf->exp_name = exp_info->exp_name; dmabuf->owner = exp_info->owner; + spin_lock_init(&dmabuf->name_lock); init_waitqueue_head(&dmabuf->poll); dmabuf->cb_excl.poll = dmabuf->cb_shared.poll = &dmabuf->poll; dmabuf->cb_excl.active = dmabuf->cb_shared.active = 0; diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index a4f95574eb9adc9cb4030c81e4f142e34b340642..ae115e6294a8613123f1486f0aac551ee8d7dff9 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -456,6 +456,16 @@ config PXA_DMA 16 to 32 channels for peripheral to memory or memory to memory transfers. +config QCOM_SPS_DMA + tristate "Qualcomm technologies inc DMA driver for sps-BAM" + depends on ARCH_QCOM + select DMA_ENGINE + help + Enable support for Qualcomm technologies inc, BAM DMA engine. + This DMA-engine-driver is a wrapper of the sps-BAM library. DMA + engine callbacks are implemented using the sps-BAM functionality + to access HW. + config SIRF_DMA tristate "CSR SiRFprimaII/SiRFmarco DMA support" depends on ARCH_SIRF diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index c91702d88b953be2f776da680e59858d05cb63e6..68ec7a6bd330e33e97e29ee50bdb0ea663bb13b8 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -72,6 +72,7 @@ obj-$(CONFIG_TIMB_DMA) += timb_dma.o obj-$(CONFIG_XGENE_DMA) += xgene-dma.o obj-$(CONFIG_ZX_DMA) += zx_dma.o obj-$(CONFIG_ST_FDMA) += st_fdma.o +obj-$(CONFIG_QCOM_SPS_DMA) += qcom-sps-dma.o obj-y += mediatek/ obj-y += qcom/ diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c index cee78f9c4794c387ec51b8f1a55bd8d2e20bc0fc..935d1697ec363028a8b236a99ceee14c151c5031 100644 --- a/drivers/dma/coh901318.c +++ b/drivers/dma/coh901318.c @@ -1944,8 +1944,6 @@ static void dma_tc_handle(struct coh901318_chan *cohc) return; } - spin_lock(&cohc->lock); - /* * When we reach this point, at least one queue item * should have been moved over from cohc->queue to @@ -1966,8 +1964,6 @@ static void dma_tc_handle(struct coh901318_chan *cohc) if (coh901318_queue_start(cohc) == NULL) cohc->busy = 0; - spin_unlock(&cohc->lock); - /* * This tasklet will remove items from cohc->active * and thus terminates them. diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c index 15b2453d2647fa49e790b6f063785a7d64d19497..b2c755b74bea00912dda847df7e7b6880d36fa03 100644 --- a/drivers/dma/dma-axi-dmac.c +++ b/drivers/dma/dma-axi-dmac.c @@ -486,7 +486,7 @@ static struct dma_async_tx_descriptor *axi_dmac_prep_interleaved( if (chan->hw_2d) { if (!axi_dmac_check_len(chan, xt->sgl[0].size) || - !axi_dmac_check_len(chan, xt->numf)) + xt->numf == 0) return NULL; if (xt->sgl[0].size + dst_icg > chan->max_length || xt->sgl[0].size + src_icg > chan->max_length) diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index f1a441ab395d7529ebbc4f8d59e891e20c61419d..8a52a5efee4f58be08cea7ecd0eda29c8a5bf904 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -190,7 +190,7 @@ __dma_device_satisfies_mask(struct dma_device *device, static struct module *dma_chan_to_owner(struct dma_chan *chan) { - return chan->device->dev->driver->owner; + return chan->device->owner; } /** @@ -923,6 +923,8 @@ int dma_async_device_register(struct dma_device *device) return -EIO; } + 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", diff --git a/drivers/dma/dw/platform.c b/drivers/dma/dw/platform.c index c299ff181bb68bcbc9ee53bd7bec96ad357ab059..62218ea0894c95294f1d96a309d45d438958d587 100644 --- a/drivers/dma/dw/platform.c +++ b/drivers/dma/dw/platform.c @@ -87,13 +87,20 @@ static void dw_dma_acpi_controller_register(struct dw_dma *dw) dma_cap_set(DMA_SLAVE, info->dma_cap); info->filter_fn = dw_dma_acpi_filter; - ret = devm_acpi_dma_controller_register(dev, acpi_dma_simple_xlate, - info); + ret = acpi_dma_controller_register(dev, acpi_dma_simple_xlate, info); if (ret) dev_err(dev, "could not register acpi_dma_controller\n"); } + +static void dw_dma_acpi_controller_free(struct dw_dma *dw) +{ + struct device *dev = dw->dma.dev; + + acpi_dma_controller_free(dev); +} #else /* !CONFIG_ACPI */ static inline void dw_dma_acpi_controller_register(struct dw_dma *dw) {} +static inline void dw_dma_acpi_controller_free(struct dw_dma *dw) {} #endif /* !CONFIG_ACPI */ #ifdef CONFIG_OF @@ -249,6 +256,9 @@ static int dw_remove(struct platform_device *pdev) { struct dw_dma_chip *chip = platform_get_drvdata(pdev); + if (ACPI_HANDLE(&pdev->dev)) + dw_dma_acpi_controller_free(chip->dw); + if (pdev->dev.of_node) of_dma_controller_free(pdev->dev.of_node); diff --git a/drivers/dma/hsu/hsu.c b/drivers/dma/hsu/hsu.c index 202ffa9f7611c6f5560187c4ab084d461a6713b5..18f155a974db624d6705e66267466c49ed2a7072 100644 --- a/drivers/dma/hsu/hsu.c +++ b/drivers/dma/hsu/hsu.c @@ -64,10 +64,10 @@ static void hsu_dma_chan_start(struct hsu_dma_chan *hsuc) if (hsuc->direction == DMA_MEM_TO_DEV) { bsr = config->dst_maxburst; - mtsr = config->src_addr_width; + mtsr = config->dst_addr_width; } else if (hsuc->direction == DMA_DEV_TO_MEM) { bsr = config->src_maxburst; - mtsr = config->dst_addr_width; + mtsr = config->src_addr_width; } hsu_chan_disable(hsuc); diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 3f5a01cb4ab45b05c37aaac65512be88587615db..eea89c3b54c1e4557925a7772609c6ef864f0ad6 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -335,6 +335,7 @@ struct sdma_desc { * @sdma: pointer to the SDMA engine for this channel * @channel: the channel number, matches dmaengine chan_id + 1 * @direction: transfer type. Needed for setting SDMA script + * @slave_config Slave configuration * @peripheral_type: Peripheral type. Needed for setting SDMA script * @event_id0: aka dma request line * @event_id1: for channels that use 2 events @@ -362,6 +363,7 @@ struct sdma_channel { struct sdma_engine *sdma; unsigned int channel; enum dma_transfer_direction direction; + struct dma_slave_config slave_config; enum sdma_peripheral_type peripheral_type; unsigned int event_id0; unsigned int event_id1; @@ -440,6 +442,10 @@ struct sdma_engine { struct sdma_buffer_descriptor *bd0; }; +static int sdma_config_write(struct dma_chan *chan, + struct dma_slave_config *dmaengine_cfg, + enum dma_transfer_direction direction); + static struct sdma_driver_data sdma_imx31 = { .chnenbl0 = SDMA_CHNENBL0_IMX31, .num_events = 32, @@ -1122,18 +1128,6 @@ static int sdma_config_channel(struct dma_chan *chan) sdmac->shp_addr = 0; sdmac->per_addr = 0; - if (sdmac->event_id0) { - if (sdmac->event_id0 >= sdmac->sdma->drvdata->num_events) - return -EINVAL; - sdma_event_enable(sdmac, sdmac->event_id0); - } - - if (sdmac->event_id1) { - if (sdmac->event_id1 >= sdmac->sdma->drvdata->num_events) - return -EINVAL; - sdma_event_enable(sdmac, sdmac->event_id1); - } - switch (sdmac->peripheral_type) { case IMX_DMATYPE_DSP: sdma_config_ownership(sdmac, false, true, true); @@ -1431,6 +1425,8 @@ static struct dma_async_tx_descriptor *sdma_prep_slave_sg( struct scatterlist *sg; struct sdma_desc *desc; + sdma_config_write(chan, &sdmac->slave_config, direction); + desc = sdma_transfer_init(sdmac, direction, sg_len); if (!desc) goto err_out; @@ -1515,6 +1511,8 @@ static struct dma_async_tx_descriptor *sdma_prep_dma_cyclic( dev_dbg(sdma->dev, "%s channel: %d\n", __func__, channel); + sdma_config_write(chan, &sdmac->slave_config, direction); + desc = sdma_transfer_init(sdmac, direction, num_periods); if (!desc) goto err_out; @@ -1570,17 +1568,18 @@ static struct dma_async_tx_descriptor *sdma_prep_dma_cyclic( return NULL; } -static int sdma_config(struct dma_chan *chan, - struct dma_slave_config *dmaengine_cfg) +static int sdma_config_write(struct dma_chan *chan, + struct dma_slave_config *dmaengine_cfg, + enum dma_transfer_direction direction) { struct sdma_channel *sdmac = to_sdma_chan(chan); - if (dmaengine_cfg->direction == DMA_DEV_TO_MEM) { + if (direction == DMA_DEV_TO_MEM) { sdmac->per_address = dmaengine_cfg->src_addr; sdmac->watermark_level = dmaengine_cfg->src_maxburst * dmaengine_cfg->src_addr_width; sdmac->word_size = dmaengine_cfg->src_addr_width; - } else if (dmaengine_cfg->direction == DMA_DEV_TO_DEV) { + } else if (direction == DMA_DEV_TO_DEV) { sdmac->per_address2 = dmaengine_cfg->src_addr; sdmac->per_address = dmaengine_cfg->dst_addr; sdmac->watermark_level = dmaengine_cfg->src_maxburst & @@ -1594,10 +1593,33 @@ static int sdma_config(struct dma_chan *chan, dmaengine_cfg->dst_addr_width; sdmac->word_size = dmaengine_cfg->dst_addr_width; } - sdmac->direction = dmaengine_cfg->direction; + sdmac->direction = direction; return sdma_config_channel(chan); } +static int sdma_config(struct dma_chan *chan, + struct dma_slave_config *dmaengine_cfg) +{ + struct sdma_channel *sdmac = to_sdma_chan(chan); + + memcpy(&sdmac->slave_config, dmaengine_cfg, sizeof(*dmaengine_cfg)); + + /* Set ENBLn earlier to make sure dma request triggered after that */ + if (sdmac->event_id0) { + if (sdmac->event_id0 >= sdmac->sdma->drvdata->num_events) + return -EINVAL; + sdma_event_enable(sdmac, sdmac->event_id0); + } + + if (sdmac->event_id1) { + if (sdmac->event_id1 >= sdmac->sdma->drvdata->num_events) + return -EINVAL; + sdma_event_enable(sdmac, sdmac->event_id1); + } + + return 0; +} + static enum dma_status sdma_tx_status(struct dma_chan *chan, dma_cookie_t cookie, struct dma_tx_state *txstate) @@ -1662,6 +1684,14 @@ static void sdma_add_scripts(struct sdma_engine *sdma, if (!sdma->script_number) sdma->script_number = SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1; + if (sdma->script_number > sizeof(struct sdma_script_start_addrs) + / sizeof(s32)) { + dev_err(sdma->dev, + "SDMA script number %d not match with firmware.\n", + sdma->script_number); + return; + } + for (i = 0; i < sdma->script_number; i++) if (addr_arr[i] > 0) saddr_arr[i] = addr_arr[i]; diff --git a/drivers/dma/ioat/dma.c b/drivers/dma/ioat/dma.c index 23fb2fa040002daeb3e248036efa261b63b3769e..b94cece58b984fb0ab1a09e50b33ea7a5901816b 100644 --- a/drivers/dma/ioat/dma.c +++ b/drivers/dma/ioat/dma.c @@ -388,10 +388,11 @@ ioat_alloc_ring(struct dma_chan *c, int order, gfp_t flags) descs->virt = dma_alloc_coherent(to_dev(ioat_chan), SZ_2M, &descs->hw, flags); - if (!descs->virt && (i > 0)) { + if (!descs->virt) { int idx; for (idx = 0; idx < i; idx++) { + descs = &ioat_chan->descs[idx]; dma_free_coherent(to_dev(ioat_chan), SZ_2M, descs->virt, descs->hw); descs->virt = NULL; diff --git a/drivers/dma/k3dma.c b/drivers/dma/k3dma.c index 6bfa217ed6d0de81d1ef51b37accdba9ae4cd04b..ba3c3791f9dc5e6dde0c5a34e39728500119ed25 100644 --- a/drivers/dma/k3dma.c +++ b/drivers/dma/k3dma.c @@ -222,9 +222,11 @@ static irqreturn_t k3_dma_int_handler(int irq, void *dev_id) c = p->vchan; if (c && (tc1 & BIT(i))) { spin_lock_irqsave(&c->vc.lock, flags); - vchan_cookie_complete(&p->ds_run->vd); - p->ds_done = p->ds_run; - p->ds_run = NULL; + if (p->ds_run != NULL) { + vchan_cookie_complete(&p->ds_run->vd); + p->ds_done = p->ds_run; + p->ds_run = NULL; + } spin_unlock_irqrestore(&c->vc.lock, flags); } if (c && (tc2 & BIT(i))) { @@ -264,6 +266,10 @@ static int k3_dma_start_txd(struct k3_dma_chan *c) if (BIT(c->phy->idx) & k3_dma_get_chan_stat(d)) return -EAGAIN; + /* Avoid losing track of ds_run if a transaction is in flight */ + if (c->phy->ds_run) + return -EAGAIN; + if (vd) { struct k3_dma_desc_sw *ds = container_of(vd, struct k3_dma_desc_sw, vd); diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index 969534c1a6c63339b1b5c48ae3568d275f3bb518..abc8d3e0487b87a0b9a39a711982191707cda16b 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -1059,6 +1059,7 @@ mv_xor_channel_add(struct mv_xor_device *xordev, mv_chan->op_in_desc = XOR_MODE_IN_DESC; dma_dev = &mv_chan->dmadev; + dma_dev->dev = &pdev->dev; mv_chan->xordev = xordev; /* @@ -1091,7 +1092,6 @@ mv_xor_channel_add(struct mv_xor_device *xordev, dma_dev->device_free_chan_resources = mv_xor_free_chan_resources; dma_dev->device_tx_status = mv_xor_status; dma_dev->device_issue_pending = mv_xor_issue_pending; - dma_dev->dev = &pdev->dev; /* set prep routines based on capability */ if (dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask)) diff --git a/drivers/dma/qcom-sps-dma.c b/drivers/dma/qcom-sps-dma.c new file mode 100644 index 0000000000000000000000000000000000000000..631516b91ccf707bd9952d2955eb91a1224ace10 --- /dev/null +++ b/drivers/dma/qcom-sps-dma.c @@ -0,0 +1,711 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014-2015,2017-2018, 2020, The Linux Foundation. All rights reserved. + */ + +/* + * Qualcomm technologies inc, DMA API for BAM (Bus Access Manager). + * This DMA driver uses sps-BAM API to access the HW, thus it is effectively a + * DMA engine wrapper of the sps-BAM API. + * + * Client channel configuration example: + * struct dma_slave_config config { + * .direction = DMA_MEM_TO_DEV; + * }; + * + * chan = dma_request_slave_channel(client_dev, "rx"); + * dmaengine_slave_config(chan, &config); + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dmaengine.h" + +#define QBAM_OF_SLAVE_N_ARGS (4) +#define QBAM_OF_MANAGE_LOCAL "qcom,managed-locally" +#define QBAM_OF_SUM_THRESHOLD "qcom,summing-threshold" +#define QBAM_MAX_DESCRIPTORS (0x100) +#define QBAM_MAX_CHANNELS (32) + +/* + * qbam_async_tx_descriptor - dma descriptor plus a list of xfer_bufs + * + * @sgl scatterlist of transfer buffers + * @sg_len size of that list + * @flags dma xfer flags + */ +struct qbam_async_tx_descriptor { + struct dma_async_tx_descriptor dma_desc; + struct scatterlist *sgl; + unsigned int sg_len; + unsigned long flags; +}; + +#define DMA_TO_QBAM_ASYNC_DESC(dma_async_desc) \ + container_of(dma_async_desc, struct qbam_async_tx_descriptor, dma_desc) + +struct qbam_channel; +/* + * qbam_device - top level device of current driver + * @handle bam sps handle. + * @regs bam register space virtual base address. + * @mem_resource bam register space resource. + * @deregister_required if bam is registered by this driver it need to be + * unregistered by this driver. + * @manage is bame managed locally or remotely, + * @summing_threshold event threshold. + * @irq bam interrupt line. + * @channels has the same channels as qbam_dev->dma_dev.channels but + * supports fast access by pipe index. + */ +struct qbam_device { + struct dma_device dma_dev; + void __iomem *regs; + struct resource *mem_resource; + ulong handle; + bool deregister_required; + u32 summing_threshold; + u32 manage; + int irq; + struct qbam_channel *channels[QBAM_MAX_CHANNELS]; +}; + +/* qbam_pipe: aggregate of bam pipe related entries of qbam_channel */ +struct qbam_pipe { + u32 index; + struct sps_pipe *handle; + struct sps_connect cfg; + u32 num_descriptors; + u32 sps_connect_flags; + u32 sps_register_event_flags; +}; + +/* + * qbam_channel - dma channel plus bam pipe info and current pending transfers + * + * @direction is a producer or consumer (MEM => DEV or DEV => MEM) + * @pending_desc next set of transfer to process + * @error last error that took place on the current pending_desc + */ +struct qbam_channel { + struct qbam_pipe bam_pipe; + + struct dma_chan chan; + enum dma_transfer_direction direction; + struct qbam_async_tx_descriptor pending_desc; + + struct qbam_device *qbam_dev; + struct mutex lock; + int error; +}; +#define DMA_TO_QBAM_CHAN(dma_chan) \ + container_of(dma_chan, struct qbam_channel, chan) +#define qbam_err(qbam_dev, fmt ...) dev_err(qbam_dev->dma_dev.dev, fmt) + +/* qbam_disconnect_chan - disconnect a channel */ +static int qbam_disconnect_chan(struct qbam_channel *qbam_chan) +{ + struct qbam_device *qbam_dev = qbam_chan->qbam_dev; + struct sps_pipe *pipe_handle = qbam_chan->bam_pipe.handle; + struct sps_connect pipe_config_no_irq = {.options = SPS_O_POLL}; + int ret; + + /* + * SW workaround: + * When disconnecting BAM pipe a spurious interrupt sometimes appears. + * To avoid that, we change the pipe setting from interrupt (default) + * to polling (SPS_O_POLL) before diconnecting the pipe. + */ + ret = sps_set_config(pipe_handle, &pipe_config_no_irq); + if (ret) + qbam_err(qbam_dev, + "error:%d sps_set_config(pipe:%d) before disconnect\n", + ret, qbam_chan->bam_pipe.index); + + ret = sps_disconnect(pipe_handle); + if (ret) + qbam_err(qbam_dev, "error:%d sps_disconnect(pipe:%d)\n", + ret, qbam_chan->bam_pipe.index); + + return ret; +} + +/* qbam_free_chan - disconnect channel and free its resources */ +static void qbam_free_chan(struct dma_chan *chan) +{ + struct qbam_channel *qbam_chan = DMA_TO_QBAM_CHAN(chan); + struct qbam_device *qbam_dev = qbam_chan->qbam_dev; + + mutex_lock(&qbam_chan->lock); + if (qbam_disconnect_chan(qbam_chan)) + qbam_err(qbam_dev, + "error free_chan() failed to disconnect(pipe:%d)\n", + qbam_chan->bam_pipe.index); + qbam_chan->pending_desc.sgl = NULL; + qbam_chan->pending_desc.sg_len = 0; + mutex_unlock(&qbam_chan->lock); +} + +static struct dma_chan *qbam_dma_xlate(struct of_phandle_args *dma_spec, + struct of_dma *of) +{ + struct qbam_device *qbam_dev = of->of_dma_data; + struct qbam_channel *qbam_chan; + u32 channel_index; + u32 num_descriptors; + + if (dma_spec->args_count != QBAM_OF_SLAVE_N_ARGS) { + qbam_err(qbam_dev, + "invalid number of dma arguments, expect:%d got:%d\n", + QBAM_OF_SLAVE_N_ARGS, dma_spec->args_count); + return NULL; + } + + channel_index = dma_spec->args[0]; + + if (channel_index >= QBAM_MAX_CHANNELS) { + qbam_err(qbam_dev, + "error: channel_index:%d out of bounds", + channel_index); + return NULL; + } + qbam_chan = qbam_dev->channels[channel_index]; + /* return qbam_chan if exists, or create one */ + if (qbam_chan) { + qbam_chan->chan.client_count = 1; + return &qbam_chan->chan; + } + + num_descriptors = dma_spec->args[1]; + if (!num_descriptors || (num_descriptors > QBAM_MAX_DESCRIPTORS)) { + qbam_err(qbam_dev, + "invalid number of descriptors, range[1..%d] got:%d\n", + QBAM_MAX_DESCRIPTORS, num_descriptors); + return NULL; + } + + /* allocate a channel */ + qbam_chan = kzalloc(sizeof(*qbam_chan), GFP_KERNEL); + if (!qbam_chan) + return NULL; + + /* allocate BAM resources for that channel */ + qbam_chan->bam_pipe.handle = sps_alloc_endpoint(); + if (!qbam_chan->bam_pipe.handle) { + qbam_err(qbam_dev, "error: sps_alloc_endpoint() return NULL\n"); + kfree(qbam_chan); + return NULL; + } + + /* init dma_chan */ + qbam_chan->chan.device = &qbam_dev->dma_dev; + dma_cookie_init(&qbam_chan->chan); + qbam_chan->chan.client_count = 1; + /* init qbam_chan */ + qbam_chan->bam_pipe.index = channel_index; + qbam_chan->bam_pipe.num_descriptors = num_descriptors; + qbam_chan->bam_pipe.sps_connect_flags = dma_spec->args[2]; + qbam_chan->bam_pipe.sps_register_event_flags = dma_spec->args[3]; + qbam_chan->qbam_dev = qbam_dev; + mutex_init(&qbam_chan->lock); + + /* add to dma_device list of channels */ + list_add(&qbam_chan->chan.device_node, &qbam_dev->dma_dev.channels); + qbam_dev->channels[channel_index] = qbam_chan; + + return &qbam_chan->chan; +} + +static enum dma_status qbam_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, struct dma_tx_state *state) +{ + struct qbam_channel *qbam_chan = DMA_TO_QBAM_CHAN(chan); + struct qbam_async_tx_descriptor *qbam_desc = &qbam_chan->pending_desc; + enum dma_status ret; + + mutex_lock(&qbam_chan->lock); + + if (qbam_chan->error) { + mutex_unlock(&qbam_chan->lock); + return DMA_ERROR; + } + + ret = dma_cookie_status(chan, cookie, state); + if (ret == DMA_IN_PROGRESS) { + struct scatterlist *sg; + int i; + u32 transfer_size = 0; + + for_each_sg(qbam_desc->sgl, sg, qbam_desc->sg_len, i) + transfer_size += sg_dma_len(sg); + + dma_set_residue(state, transfer_size); + } + mutex_unlock(&qbam_chan->lock); + + return ret; +} + +/* + * qbam_init_bam_handle - find or create bam handle. + * + * BAM device needs to be registered for each BLSP once and only once. if it + * was registered, then we find the handle to the registered bam and return + * it, otherwise we register it here. + * The module which registered BAM is responsible for deregistering it. + */ +static int qbam_init_bam_handle(struct qbam_device *qbam_dev) +{ + int ret = 0; + struct sps_bam_props bam_props = {0}; + + /* + * Check if BAM is already registred with SPS on the current + * BLSP. If it isn't then go ahead and register it. + */ + ret = sps_phy2h(qbam_dev->mem_resource->start, &qbam_dev->handle); + if (qbam_dev->handle) + return 0; + + qbam_dev->regs = devm_ioremap_resource(qbam_dev->dma_dev.dev, + qbam_dev->mem_resource); + if (IS_ERR(qbam_dev->regs)) { + qbam_err(qbam_dev, "error:%ld ioremap(phy:0x%lx len:0x%lx)\n", + PTR_ERR(qbam_dev->regs), + (ulong) qbam_dev->mem_resource->start, + (ulong) resource_size(qbam_dev->mem_resource)); + return PTR_ERR(qbam_dev->regs); + } + + bam_props.phys_addr = qbam_dev->mem_resource->start; + bam_props.virt_addr = qbam_dev->regs; + bam_props.summing_threshold = qbam_dev->summing_threshold; + bam_props.manage = qbam_dev->manage; + bam_props.irq = qbam_dev->irq; + + ret = sps_register_bam_device(&bam_props, &qbam_dev->handle); + if (ret) + qbam_err(qbam_dev, "error:%d sps_register_bam_device\n" + "(phy:0x%lx virt:0x%lx irq:%d)\n", + ret, (ulong) bam_props.phys_addr, + (ulong) bam_props.virt_addr, qbam_dev->irq); + else + qbam_dev->deregister_required = true; + + return ret; +} + + +static int qbam_alloc_chan(struct dma_chan *chan) +{ + return 0; +} + +static void qbam_eot_callback(struct sps_event_notify *notify) +{ + struct qbam_async_tx_descriptor *qbam_desc = notify->data.transfer.user; + struct dma_async_tx_descriptor *dma_desc = &qbam_desc->dma_desc; + dma_async_tx_callback callback = dma_desc->callback; + void *param = dma_desc->callback_param; + + if (callback) + callback(param); +} + +static void qbam_error_callback(struct sps_event_notify *notify) +{ + struct qbam_channel *qbam_chan = notify->user; + + qbam_err(qbam_chan->qbam_dev, "error: %s(pipe:%d\n)", + __func__, qbam_chan->bam_pipe.index); +} + +static int qbam_connect_chan(struct qbam_channel *qbam_chan) +{ + int ret = 0; + struct qbam_device *qbam_dev = qbam_chan->qbam_dev; + struct sps_register_event bam_eot_event = { + .mode = SPS_TRIGGER_CALLBACK, + .options = qbam_chan->bam_pipe.sps_register_event_flags, + .callback = qbam_eot_callback, + }; + struct sps_register_event bam_error_event = { + .mode = SPS_TRIGGER_CALLBACK, + .options = SPS_O_ERROR, + .callback = qbam_error_callback, + .user = qbam_chan, + }; + + ret = sps_connect(qbam_chan->bam_pipe.handle, &qbam_chan->bam_pipe.cfg); + if (ret) { + qbam_err(qbam_dev, "error:%d sps_connect(pipe:%d)\n", ret, + qbam_chan->bam_pipe.index); + return ret; + } + + ret = sps_register_event(qbam_chan->bam_pipe.handle, &bam_eot_event); + if (ret) { + qbam_err(qbam_dev, "error:%d sps_register_event(eot@pipe:%d)\n", + ret, qbam_chan->bam_pipe.index); + goto need_disconnect; + } + + ret = sps_register_event(qbam_chan->bam_pipe.handle, &bam_error_event); + if (ret) { + qbam_err(qbam_dev, "error:%d sps_register_event(err@pipe:%d)\n", + ret, qbam_chan->bam_pipe.index); + goto need_disconnect; + } + + return 0; + +need_disconnect: + ret = sps_disconnect(qbam_chan->bam_pipe.handle); + if (ret) + qbam_err(qbam_dev, "error:%d sps_disconnect(pipe:%d)\n", ret, + qbam_chan->bam_pipe.index); + return ret; +} + +/* + * qbam_slave_cfg - configure and connect a BAM pipe + * + * @cfg only cares about cfg->direction + */ +static int qbam_slave_cfg(struct dma_chan *chan, + struct dma_slave_config *cfg) +{ + int ret = 0; + struct qbam_channel *qbam_chan = DMA_TO_QBAM_CHAN(chan); + struct qbam_device *qbam_dev = qbam_chan->qbam_dev; + struct sps_connect *pipe_cfg = &qbam_chan->bam_pipe.cfg; + + if (!qbam_dev->handle) { + ret = qbam_init_bam_handle(qbam_dev); + if (ret) + return ret; + } + + if (qbam_chan->bam_pipe.cfg.desc.base) + goto cfg_done; + + ret = sps_get_config(qbam_chan->bam_pipe.handle, + &qbam_chan->bam_pipe.cfg); + if (ret) { + qbam_err(qbam_dev, "error:%d sps_get_config(0x%p)\n", + ret, qbam_chan->bam_pipe.handle); + return ret; + } + + qbam_chan->direction = cfg->direction; + if (cfg->direction == DMA_MEM_TO_DEV) { + pipe_cfg->source = SPS_DEV_HANDLE_MEM; + pipe_cfg->destination = qbam_dev->handle; + pipe_cfg->mode = SPS_MODE_DEST; + pipe_cfg->src_pipe_index = 0; + pipe_cfg->dest_pipe_index = qbam_chan->bam_pipe.index; + } else { + pipe_cfg->source = qbam_dev->handle; + pipe_cfg->destination = SPS_DEV_HANDLE_MEM; + pipe_cfg->mode = SPS_MODE_SRC; + pipe_cfg->src_pipe_index = qbam_chan->bam_pipe.index; + pipe_cfg->dest_pipe_index = 0; + } + pipe_cfg->options = qbam_chan->bam_pipe.sps_connect_flags; + pipe_cfg->desc.size = (qbam_chan->bam_pipe.num_descriptors + 1) * + sizeof(struct sps_iovec); + /* managed dma_alloc_coherent() */ + pipe_cfg->desc.base = dmam_alloc_coherent(qbam_dev->dma_dev.dev, + pipe_cfg->desc.size, + &pipe_cfg->desc.phys_base, + GFP_KERNEL); + if (!pipe_cfg->desc.base) { + qbam_err(qbam_dev, + "error dma_alloc_coherent(desc-sz:%llu * n-descs:%d)\n", + (u64) sizeof(struct sps_iovec), + qbam_chan->bam_pipe.num_descriptors); + return -ENOMEM; + } +cfg_done: + ret = qbam_connect_chan(qbam_chan); + if (ret) + dmam_free_coherent(qbam_dev->dma_dev.dev, pipe_cfg->desc.size, + pipe_cfg->desc.base, pipe_cfg->desc.phys_base); + + return ret; +} + +static int qbam_flush_chan(struct dma_chan *chan) +{ + struct qbam_channel *qbam_chan = DMA_TO_QBAM_CHAN(chan); + int ret = qbam_disconnect_chan(qbam_chan); + + if (ret) { + qbam_err(qbam_chan->qbam_dev, + "error: disconnect flush(pipe:%d\n)", + qbam_chan->bam_pipe.index); + return ret; + } + ret = qbam_connect_chan(qbam_chan); + if (ret) + qbam_err(qbam_chan->qbam_dev, + "error: reconnect flush(pipe:%d\n)", + qbam_chan->bam_pipe.index); + return ret; +} + +/* qbam_tx_submit - sets the descriptor as the next one to be executed */ +static dma_cookie_t qbam_tx_submit(struct dma_async_tx_descriptor *dma_desc) +{ + struct qbam_channel *qbam_chan = DMA_TO_QBAM_CHAN(dma_desc->chan); + dma_cookie_t ret; + + mutex_lock(&qbam_chan->lock); + + ret = dma_cookie_assign(dma_desc); + + mutex_unlock(&qbam_chan->lock); + + return ret; +} + +/* + * qbam_prep_slave_sg - creates qbam_xfer_buf from a list of sg + * + * @chan: dma channel + * @sgl: scatter gather list + * @sg_len: length of sg + * @direction: DMA transfer direction + * @flags: DMA flags + * @context: transfer context (unused) + * @return the newly created descriptor or negative ERR_PTR() on error + */ +static struct dma_async_tx_descriptor *qbam_prep_slave_sg(struct dma_chan *chan, + struct scatterlist *sgl, unsigned int sg_len, + enum dma_transfer_direction direction, unsigned long flags, + void *context) +{ + struct qbam_channel *qbam_chan = DMA_TO_QBAM_CHAN(chan); + struct qbam_device *qbam_dev = qbam_chan->qbam_dev; + struct qbam_async_tx_descriptor *qbam_desc = &qbam_chan->pending_desc; + + if (qbam_chan->direction != direction) { + qbam_err(qbam_dev, + "invalid dma transfer direction expected:%d given:%d\n", + qbam_chan->direction, direction); + return ERR_PTR(-EINVAL); + } + + qbam_desc->dma_desc.chan = &qbam_chan->chan; + qbam_desc->dma_desc.tx_submit = qbam_tx_submit; + qbam_desc->sgl = sgl; + qbam_desc->sg_len = sg_len; + qbam_desc->flags = flags; + return &qbam_desc->dma_desc; +} + +/* + * qbam_issue_pending - queue pending descriptor to BAM + * + * Iterate over the transfers of the pending descriptor and push them to bam + */ +static void qbam_issue_pending(struct dma_chan *chan) +{ + int i; + int ret = 0; + struct qbam_channel *qbam_chan = DMA_TO_QBAM_CHAN(chan); + struct qbam_device *qbam_dev = qbam_chan->qbam_dev; + struct qbam_async_tx_descriptor *qbam_desc = &qbam_chan->pending_desc; + struct scatterlist *sg; + + mutex_lock(&qbam_chan->lock); + if (!qbam_chan->pending_desc.sgl) { + qbam_err(qbam_dev, + "error %s() no pending descriptor pipe:%d\n", + __func__, qbam_chan->bam_pipe.index); + mutex_unlock(&qbam_chan->lock); + return; + } + + for_each_sg(qbam_desc->sgl, sg, qbam_desc->sg_len, i) { + + /* Add BAM flags only on the last buffer */ + bool is_last_buf = (i == ((qbam_desc->sg_len) - 1)); + + ret = sps_transfer_one(qbam_chan->bam_pipe.handle, + sg_dma_address(sg), sg_dma_len(sg), + qbam_desc, + (is_last_buf ? qbam_desc->flags : 0)); + if (ret < 0) { + qbam_chan->error = ret; + + qbam_err(qbam_dev, "erorr:%d sps_transfer_one\n" + "(addr:0x%lx len:%d flags:0x%lx pipe:%d)\n", + ret, (ulong) sg_dma_address(sg), sg_dma_len(sg), + qbam_desc->flags, qbam_chan->bam_pipe.index); + break; + } + } + + dma_cookie_complete(&qbam_desc->dma_desc); + qbam_chan->error = 0; + qbam_desc->sgl = NULL; + qbam_desc->sg_len = 0; + mutex_unlock(&qbam_chan->lock); +}; + +static int qbam_deregister_bam_dev(struct qbam_device *qbam_dev) +{ + int ret; + + if (!qbam_dev->handle) + return 0; + + ret = sps_deregister_bam_device(qbam_dev->handle); + if (ret) + qbam_err(qbam_dev, + "error:%d sps_deregister_bam_device(hndl:0x%lx) failed", + ret, qbam_dev->handle); + return ret; +} + +static void qbam_pipes_free(struct qbam_device *qbam_dev) +{ + struct qbam_channel *qbam_chan_cur, *qbam_chan_next; + + list_for_each_entry_safe(qbam_chan_cur, qbam_chan_next, + &qbam_dev->dma_dev.channels, chan.device_node) { + mutex_lock(&qbam_chan_cur->lock); + qbam_free_chan(&qbam_chan_cur->chan); + sps_free_endpoint(qbam_chan_cur->bam_pipe.handle); + list_del(&qbam_chan_cur->chan.device_node); + mutex_unlock(&qbam_chan_cur->lock); + kfree(qbam_chan_cur); + } +} + +static int qbam_probe(struct platform_device *pdev) +{ + struct qbam_device *qbam_dev; + int ret; + bool managed_locally; + struct device_node *of_node = pdev->dev.of_node; + + qbam_dev = devm_kzalloc(&pdev->dev, sizeof(*qbam_dev), GFP_KERNEL); + if (!qbam_dev) + return -ENOMEM; + + qbam_dev->dma_dev.dev = &pdev->dev; + platform_set_drvdata(pdev, qbam_dev); + + qbam_dev->mem_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!qbam_dev->mem_resource) { + qbam_err(qbam_dev, "missing 'reg' DT entry"); + return -ENODEV; + } + + qbam_dev->irq = platform_get_irq(pdev, 0); + if (qbam_dev->irq < 0) { + qbam_err(qbam_dev, "missing DT IRQ resource entry"); + return -EINVAL; + } + + ret = of_property_read_u32(of_node, QBAM_OF_SUM_THRESHOLD, + &qbam_dev->summing_threshold); + if (ret) { + qbam_err(qbam_dev, "missing '%s' DT entry", + QBAM_OF_SUM_THRESHOLD); + return ret; + } + + /* read from DT and set sps_bam_props.manage */ + managed_locally = of_property_read_bool(of_node, QBAM_OF_MANAGE_LOCAL); + qbam_dev->manage = managed_locally ? SPS_BAM_MGR_LOCAL : + SPS_BAM_MGR_DEVICE_REMOTE; + + /* Init channels */ + INIT_LIST_HEAD(&qbam_dev->dma_dev.channels); + + /* Set capabilities */ + dma_cap_zero(qbam_dev->dma_dev.cap_mask); + dma_cap_set(DMA_SLAVE, qbam_dev->dma_dev.cap_mask); + dma_cap_set(DMA_PRIVATE, qbam_dev->dma_dev.cap_mask); + + /* Initialize dmaengine callback apis */ + qbam_dev->dma_dev.device_alloc_chan_resources = qbam_alloc_chan; + qbam_dev->dma_dev.device_free_chan_resources = qbam_free_chan; + qbam_dev->dma_dev.device_prep_slave_sg = qbam_prep_slave_sg; + qbam_dev->dma_dev.device_terminate_all = qbam_flush_chan; + qbam_dev->dma_dev.device_config = qbam_slave_cfg; + qbam_dev->dma_dev.device_issue_pending = qbam_issue_pending; + qbam_dev->dma_dev.device_tx_status = qbam_tx_status; + + /* Regiser to DMA framework */ + dma_async_device_register(&qbam_dev->dma_dev); + + /* + * Do not return error in order to not break the existing + * way of requesting channels. + */ + ret = of_dma_controller_register(of_node, qbam_dma_xlate, qbam_dev); + if (ret) { + qbam_err(qbam_dev, "error:%d of_dma_controller_register()\n", + ret); + goto err_unregister_dma; + } + return 0; + +err_unregister_dma: + dma_async_device_unregister(&qbam_dev->dma_dev); + if (qbam_dev->deregister_required) + return qbam_deregister_bam_dev(qbam_dev); + + return ret; +} + +static int qbam_remove(struct platform_device *pdev) +{ + struct qbam_device *qbam_dev = platform_get_drvdata(pdev); + + dma_async_device_unregister(&qbam_dev->dma_dev); + + /* free BAM pipes resources */ + qbam_pipes_free(qbam_dev); + + if (qbam_dev->deregister_required) + return qbam_deregister_bam_dev(qbam_dev); + + return 0; +} + +static const struct of_device_id qbam_of_match[] = { + { .compatible = "qcom,sps-dma" }, + {} +}; +MODULE_DEVICE_TABLE(of, qbam_of_match); + +static struct platform_driver qbam_driver = { + .probe = qbam_probe, + .remove = qbam_remove, + .driver = { + .name = "qcom-sps-dma", + .of_match_table = qbam_of_match, + }, +}; + +module_platform_driver(qbam_driver); + +MODULE_DESCRIPTION("DMA-API driver to qcom BAM"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:qcom-sps-dma"); diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index fb23993430d31490b66da870954187f0b80e05f7..15481aeaeecd17b56646b2222db84e53296fa296 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -288,7 +288,7 @@ static struct tegra_dma_desc *tegra_dma_desc_get( /* Do not allocate if desc are waiting for ack */ list_for_each_entry(dma_desc, &tdc->free_dma_desc, node) { - if (async_tx_test_ack(&dma_desc->txd)) { + if (async_tx_test_ack(&dma_desc->txd) && !dma_desc->cb_count) { list_del(&dma_desc->node); spin_unlock_irqrestore(&tdc->lock, flags); dma_desc->txd.flags = 0; @@ -756,10 +756,6 @@ static int tegra_dma_terminate_all(struct dma_chan *dc) bool was_busy; spin_lock_irqsave(&tdc->lock, flags); - if (list_empty(&tdc->pending_sg_req)) { - spin_unlock_irqrestore(&tdc->lock, flags); - return 0; - } if (!tdc->busy) goto skip_dma_stop; diff --git a/drivers/dma/tegra210-adma.c b/drivers/dma/tegra210-adma.c index 09b6756366c30ec70c0976850abbfd9c703be0a8..4f4733d831a1ac9fc3043c4d8b8d6b5b2fd0ac8e 100644 --- a/drivers/dma/tegra210-adma.c +++ b/drivers/dma/tegra210-adma.c @@ -98,6 +98,7 @@ struct tegra_adma_chan_regs { unsigned int src_addr; unsigned int trg_addr; unsigned int fifo_ctrl; + unsigned int cmd; unsigned int tc; }; @@ -127,6 +128,7 @@ struct tegra_adma_chan { enum dma_transfer_direction sreq_dir; unsigned int sreq_index; bool sreq_reserved; + struct tegra_adma_chan_regs ch_regs; /* Transfer count and position info */ unsigned int tx_buf_count; @@ -635,8 +637,30 @@ static struct dma_chan *tegra_dma_of_xlate(struct of_phandle_args *dma_spec, static int tegra_adma_runtime_suspend(struct device *dev) { struct tegra_adma *tdma = dev_get_drvdata(dev); + struct tegra_adma_chan_regs *ch_reg; + struct tegra_adma_chan *tdc; + int i; tdma->global_cmd = tdma_read(tdma, ADMA_GLOBAL_CMD); + if (!tdma->global_cmd) + goto clk_disable; + + for (i = 0; i < tdma->nr_channels; i++) { + tdc = &tdma->channels[i]; + ch_reg = &tdc->ch_regs; + ch_reg->cmd = tdma_ch_read(tdc, ADMA_CH_CMD); + /* skip if channel is not active */ + if (!ch_reg->cmd) + continue; + ch_reg->tc = tdma_ch_read(tdc, ADMA_CH_TC); + ch_reg->src_addr = tdma_ch_read(tdc, ADMA_CH_LOWER_SRC_ADDR); + ch_reg->trg_addr = tdma_ch_read(tdc, ADMA_CH_LOWER_TRG_ADDR); + ch_reg->ctrl = tdma_ch_read(tdc, ADMA_CH_CTRL); + ch_reg->fifo_ctrl = tdma_ch_read(tdc, ADMA_CH_FIFO_CTRL); + ch_reg->config = tdma_ch_read(tdc, ADMA_CH_CONFIG); + } + +clk_disable: clk_disable_unprepare(tdma->ahub_clk); return 0; @@ -645,7 +669,9 @@ static int tegra_adma_runtime_suspend(struct device *dev) static int tegra_adma_runtime_resume(struct device *dev) { struct tegra_adma *tdma = dev_get_drvdata(dev); - int ret; + struct tegra_adma_chan_regs *ch_reg; + struct tegra_adma_chan *tdc; + int ret, i; ret = clk_prepare_enable(tdma->ahub_clk); if (ret) { @@ -654,6 +680,24 @@ static int tegra_adma_runtime_resume(struct device *dev) } tdma_write(tdma, ADMA_GLOBAL_CMD, tdma->global_cmd); + if (!tdma->global_cmd) + return 0; + + for (i = 0; i < tdma->nr_channels; i++) { + tdc = &tdma->channels[i]; + ch_reg = &tdc->ch_regs; + /* skip if channel was not active earlier */ + if (!ch_reg->cmd) + continue; + tdma_ch_write(tdc, ADMA_CH_TC, ch_reg->tc); + tdma_ch_write(tdc, ADMA_CH_LOWER_SRC_ADDR, ch_reg->src_addr); + tdma_ch_write(tdc, ADMA_CH_LOWER_TRG_ADDR, ch_reg->trg_addr); + tdma_ch_write(tdc, ADMA_CH_CTRL, ch_reg->ctrl); + tdma_ch_write(tdc, ADMA_CH_FIFO_CTRL, ch_reg->fifo_ctrl); + tdma_ch_write(tdc, ADMA_CH_CONFIG, ch_reg->config); + tdma_ch_write(tdc, ADMA_CH_CMD, ch_reg->cmd); + } + return 0; } @@ -700,16 +744,6 @@ static int tegra_adma_probe(struct platform_device *pdev) return PTR_ERR(tdma->ahub_clk); } - pm_runtime_enable(&pdev->dev); - - ret = pm_runtime_get_sync(&pdev->dev); - if (ret < 0) - goto rpm_disable; - - ret = tegra_adma_init(tdma); - if (ret) - goto rpm_put; - INIT_LIST_HEAD(&tdma->dma_dev.channels); for (i = 0; i < tdma->nr_channels; i++) { struct tegra_adma_chan *tdc = &tdma->channels[i]; @@ -727,6 +761,16 @@ static int tegra_adma_probe(struct platform_device *pdev) tdc->tdma = tdma; } + pm_runtime_enable(&pdev->dev); + + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) + goto rpm_disable; + + ret = tegra_adma_init(tdma); + if (ret) + goto rpm_put; + dma_cap_set(DMA_SLAVE, tdma->dma_dev.cap_mask); dma_cap_set(DMA_PRIVATE, tdma->dma_dev.cap_mask); dma_cap_set(DMA_CYCLIC, tdma->dma_dev.cap_mask); @@ -768,13 +812,13 @@ static int tegra_adma_probe(struct platform_device *pdev) dma_remove: dma_async_device_unregister(&tdma->dma_dev); -irq_dispose: - while (--i >= 0) - irq_dispose_mapping(tdma->channels[i].irq); rpm_put: pm_runtime_put_sync(&pdev->dev); rpm_disable: pm_runtime_disable(&pdev->dev); +irq_dispose: + while (--i >= 0) + irq_dispose_mapping(tdma->channels[i].irq); return ret; } diff --git a/drivers/dma/ti/edma.c b/drivers/dma/ti/edma.c index 982631d4e1f8af4b83d9952dcfe30512d185c639..44158fa859737c64d43ca2ca3c2fdd691fdcd6f9 100644 --- a/drivers/dma/ti/edma.c +++ b/drivers/dma/ti/edma.c @@ -2345,8 +2345,10 @@ static int edma_probe(struct platform_device *pdev) ecc->tc_list = devm_kcalloc(dev, ecc->num_tc, sizeof(*ecc->tc_list), GFP_KERNEL); - if (!ecc->tc_list) - return -ENOMEM; + if (!ecc->tc_list) { + ret = -ENOMEM; + goto err_reg1; + } for (i = 0;; i++) { ret = of_parse_phandle_with_fixed_args(node, "ti,tptcs", diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 94265e4385146558cef181a5d4637fccd6ea5efa..05d6f9c86ac3873e330e46b911f1bf08d77451c7 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -2863,6 +2863,7 @@ static int init_csrows(struct mem_ctl_info *mci) dimm = csrow->channels[j]->dimm; dimm->mtype = pvt->dram_type; dimm->edac_mode = edac_mode; + dimm->grain = 64; } } diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index f59511bd99261b5797b19bdc6c969516f756eef5..fd440b35d76ed29bd74e158fc7a70935627fb00d 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -681,22 +681,18 @@ static int del_mc_from_global_list(struct mem_ctl_info *mci) struct mem_ctl_info *edac_mc_find(int idx) { - struct mem_ctl_info *mci = NULL; + struct mem_ctl_info *mci; struct list_head *item; mutex_lock(&mem_ctls_mutex); list_for_each(item, &mc_devices) { mci = list_entry(item, struct mem_ctl_info, link); - - if (mci->mc_idx >= idx) { - if (mci->mc_idx == idx) { - goto unlock; - } - break; - } + if (mci->mc_idx == idx) + goto unlock; } + mci = NULL; unlock: mutex_unlock(&mem_ctls_mutex); return mci; diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index 9be7cc4ffb1b12fb9fcfce0a0d94d571c69f9330..06125b15b22ef3f02b8608a9034273d8948998fb 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -881,6 +881,17 @@ int extcon_set_property_capability(struct extcon_dev *edev, unsigned int id, } EXPORT_SYMBOL_GPL(extcon_set_property_capability); +int extcon_set_mutually_exclusive(struct extcon_dev *edev, + const u32 *exclusive) +{ + if (!edev) + return -EINVAL; + + edev->mutually_exclusive = exclusive; + return 0; +} +EXPORT_SYMBOL(extcon_set_mutually_exclusive); + /** * extcon_get_extcon_dev() - Get the extcon device instance from the name. * @extcon_name: the extcon name provided with extcon_dev_register() diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index 30fc04e284312e9f19c165994dad1170966304b8..0a194af924385bac2019bf976d7e6e8263db9eeb 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -185,6 +185,8 @@ scmi_clock_describe_rates_get(const struct scmi_handle *handle, u32 clk_id, if (rate_discrete) clk->list.num_rates = tot_rate_cnt; + clk->rate_discrete = rate_discrete; + err: scmi_xfer_put(handle, t); return ret; diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 09119e3f5c018b9495068eeda50ef6ad63534d89..effc4c17e0fb95b63a58490855bd3d841d46d442 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -662,9 +662,7 @@ static int scmi_xfer_info_init(struct scmi_info *sinfo) static int scmi_mailbox_check(struct device_node *np) { - struct of_phandle_args arg; - - return of_parse_phandle_with_args(np, "mboxes", "#mbox-cells", 0, &arg); + return of_parse_phandle_with_args(np, "mboxes", "#mbox-cells", 0, NULL); } static int scmi_mbox_free_channel(int id, void *p, void *data) diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c index b53d5cc9c9f6c57ebae04f6a43e42fb814fc27d9..c00287b5f2c237f8c73eb4ad482df6dc5ba3fd9d 100644 --- a/drivers/firmware/arm_scmi/sensors.c +++ b/drivers/firmware/arm_scmi/sensors.c @@ -30,10 +30,10 @@ struct scmi_msg_resp_sensor_description { __le32 id; __le32 attributes_low; #define SUPPORTS_ASYNC_READ(x) ((x) & BIT(31)) -#define NUM_TRIP_POINTS(x) (((x) >> 4) & 0xff) +#define NUM_TRIP_POINTS(x) ((x) & 0xff) __le32 attributes_high; #define SENSOR_TYPE(x) ((x) & 0xff) -#define SENSOR_SCALE(x) (((x) >> 11) & 0x3f) +#define SENSOR_SCALE(x) (((x) >> 11) & 0x1f) #define SENSOR_UPDATE_SCALE(x) (((x) >> 22) & 0x1f) #define SENSOR_UPDATE_BASE(x) (((x) >> 27) & 0x1f) u8 name[SCMI_MAX_STR_SIZE]; diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index f2483548cde92d692f748d6a9c7da0cbf98274a3..0dc0c78f1fdb2db40fd9dedb4b179999ac98fc60 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -407,7 +407,7 @@ static void __init save_mem_devices(const struct dmi_header *dm, void *v) bytes = ~0ull; else if (size & 0x8000) bytes = (u64)(size & 0x7fff) << 10; - else if (size != 0x7fff) + else if (size != 0x7fff || dm->length < 0x20) bytes = (u64)size << 20; else bytes = (u64)get_unaligned((u32 *)&d[0x1C]) << 20; diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c index 1606abead22ccd5285f493ea5ec28e7175488434..b0aeffd4e269ca84a71f9afbda61736e74e3f15a 100644 --- a/drivers/firmware/efi/runtime-wrappers.c +++ b/drivers/firmware/efi/runtime-wrappers.c @@ -95,7 +95,7 @@ struct efi_runtime_work { efi_rts_work.status = EFI_ABORTED; \ \ init_completion(&efi_rts_work.efi_rts_comp); \ - INIT_WORK(&efi_rts_work.work, efi_call_rts); \ + INIT_WORK_ONSTACK(&efi_rts_work.work, efi_call_rts); \ efi_rts_work.arg1 = _arg1; \ efi_rts_work.arg2 = _arg2; \ efi_rts_work.arg3 = _arg3; \ diff --git a/drivers/firmware/google/coreboot_table-of.c b/drivers/firmware/google/coreboot_table-of.c index f15bf404c579bf3d7a41a2d47ca381819362caf1..9b90c0fa4a0b41ad3f6b0a14aead30af7e582230 100644 --- a/drivers/firmware/google/coreboot_table-of.c +++ b/drivers/firmware/google/coreboot_table-of.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include "coreboot_table.h" @@ -30,7 +29,6 @@ static int coreboot_table_of_probe(struct platform_device *pdev) void __iomem *ptr; ptr = of_iomap(fw_dn, 0); - of_node_put(fw_dn); if (!ptr) return -ENOMEM; @@ -44,8 +42,9 @@ static int coreboot_table_of_remove(struct platform_device *pdev) static const struct of_device_id coreboot_of_match[] = { { .compatible = "coreboot" }, - {}, + {} }; +MODULE_DEVICE_TABLE(of, coreboot_of_match); static struct platform_driver coreboot_table_of_driver = { .probe = coreboot_table_of_probe, @@ -55,28 +54,7 @@ static struct platform_driver coreboot_table_of_driver = { .of_match_table = coreboot_of_match, }, }; - -static int __init platform_coreboot_table_of_init(void) -{ - struct platform_device *pdev; - struct device_node *of_node; - - /* Limit device creation to the presence of /firmware/coreboot node */ - of_node = of_find_node_by_path("/firmware/coreboot"); - if (!of_node) - return -ENODEV; - - if (!of_match_node(coreboot_of_match, of_node)) - return -ENODEV; - - pdev = of_platform_device_create(of_node, "coreboot_table_of", NULL); - if (!pdev) - return -ENODEV; - - return platform_driver_register(&coreboot_table_of_driver); -} - -module_init(platform_coreboot_table_of_init); +module_platform_driver(coreboot_table_of_driver); MODULE_AUTHOR("Google, Inc."); MODULE_LICENSE("GPL"); diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c index c6fa9b393e84baaca4fc0326902b2d5bba9b1799..bd62236d3f9754aa1893f018af8e5f7ce430601d 100644 --- a/drivers/fsi/fsi-core.c +++ b/drivers/fsi/fsi-core.c @@ -1060,6 +1060,14 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id) } + rc = fsi_slave_set_smode(slave); + if (rc) { + dev_warn(&master->dev, + "can't set smode on slave:%02x:%02x %d\n", + link, id, rc); + goto err_free; + } + /* Allocate a minor in the FSI space */ rc = __fsi_get_new_minor(slave, fsi_dev_cfam, &slave->dev.devt, &slave->cdev_idx); @@ -1071,17 +1079,14 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id) rc = cdev_device_add(&slave->cdev, &slave->dev); if (rc) { dev_err(&slave->dev, "Error %d creating slave device\n", rc); - goto err_free; + goto err_free_ida; } - rc = fsi_slave_set_smode(slave); - if (rc) { - dev_warn(&master->dev, - "can't set smode on slave:%02x:%02x %d\n", - link, id, rc); - kfree(slave); - return -ENODEV; - } + /* Now that we have the cdev registered with the core, any fatal + * failures beyond this point will need to clean up through + * cdev_device_del(). Fortunately though, nothing past here is fatal. + */ + if (master->link_config) master->link_config(master, link, slave->t_send_delay, @@ -1098,10 +1103,13 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id) dev_dbg(&master->dev, "failed during slave scan with: %d\n", rc); - return rc; + return 0; - err_free: - put_device(&slave->dev); +err_free_ida: + fsi_free_minor(slave->dev.devt); +err_free: + of_node_put(slave->dev.of_node); + kfree(slave); return rc; } diff --git a/drivers/fsi/fsi-sbefifo.c b/drivers/fsi/fsi-sbefifo.c index ae861342626e3516527167b18d96a7f5ac522169..9fa3959e085542abae2e543a8492776a537da821 100644 --- a/drivers/fsi/fsi-sbefifo.c +++ b/drivers/fsi/fsi-sbefifo.c @@ -289,11 +289,11 @@ static int sbefifo_check_sbe_state(struct sbefifo *sbefifo) switch ((sbm & CFAM_SBM_SBE_STATE_MASK) >> CFAM_SBM_SBE_STATE_SHIFT) { case SBE_STATE_UNKNOWN: return -ESHUTDOWN; + case SBE_STATE_DMT: + return -EBUSY; case SBE_STATE_IPLING: case SBE_STATE_ISTEP: case SBE_STATE_MPIPL: - case SBE_STATE_DMT: - return -EBUSY; case SBE_STATE_RUNTIME: case SBE_STATE_DUMP: /* Not sure about that one */ break; diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 4877999f083b7c908d3c42ad2fecd4778cf2314c..20466ee72b4cb6af6b8c95a2d2fa6668b0fc4d17 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1058,6 +1058,7 @@ config GPIO_MADERA config GPIO_MAX77620 tristate "GPIO support for PMIC MAX77620 and MAX20024" depends on MFD_MAX77620 + select GPIOLIB_IRQCHIP help GPIO driver for MAX77620 and MAX20024 PMIC from Maxim Semiconductor. MAX77620 PMIC has 8 pins that can be configured as GPIOs. The diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index b696ec35efb38a540c91a01f4c3d29b1954e539d..e627e0e9001ae2e0dd5ccecec849e900038c046a 100644 --- a/drivers/gpio/gpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed.c @@ -1199,7 +1199,7 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev) gpio->chip.irq.need_valid_mask = true; /* Allocate a cache of the output registers */ - banks = gpio->config->nr_gpios >> 5; + banks = DIV_ROUND_UP(gpio->config->nr_gpios, 32); gpio->dcache = devm_kcalloc(&pdev->dev, banks, sizeof(u32), GFP_KERNEL); if (!gpio->dcache) diff --git a/drivers/gpio/gpio-grgpio.c b/drivers/gpio/gpio-grgpio.c index 60a1556c570a45610c5abb99181452ea3782da95..c1be299e5567b28338906214da94a2ce50621a6a 100644 --- a/drivers/gpio/gpio-grgpio.c +++ b/drivers/gpio/gpio-grgpio.c @@ -258,17 +258,16 @@ static int grgpio_irq_map(struct irq_domain *d, unsigned int irq, lirq->irq = irq; uirq = &priv->uirqs[lirq->index]; if (uirq->refcnt == 0) { + spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); ret = request_irq(uirq->uirq, grgpio_irq_handler, 0, dev_name(priv->dev), priv); if (ret) { dev_err(priv->dev, "Could not request underlying irq %d\n", uirq->uirq); - - spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); - return ret; } + spin_lock_irqsave(&priv->gc.bgpio_lock, flags); } uirq->refcnt++; @@ -314,8 +313,11 @@ static void grgpio_irq_unmap(struct irq_domain *d, unsigned int irq) if (index >= 0) { uirq = &priv->uirqs[lirq->index]; uirq->refcnt--; - if (uirq->refcnt == 0) + if (uirq->refcnt == 0) { + spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); free_irq(uirq->uirq, priv); + return; + } } spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index 3f10f9599f2cb6b99b258d1817fe51b847322bb5..1899d172590b8c2d7d64d15e90bbb057313550fe 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -317,6 +317,7 @@ static int mpc8xxx_probe(struct platform_device *pdev) return -ENOMEM; gc = &mpc8xxx_gc->gc; + gc->parent = &pdev->dev; if (of_property_read_bool(np, "little-endian")) { ret = bgpio_init(gc, &pdev->dev, 4, diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index 3f5fcdd5a4291dd41c35e5032122a4b39873686d..5dec96155814bcce4aba564004e5ae8857617194 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -357,6 +357,28 @@ static int zynq_gpio_dir_out(struct gpio_chip *chip, unsigned int pin, return 0; } +/** + * zynq_gpio_get_direction - Read the direction of the specified GPIO pin + * @chip: gpio_chip instance to be worked on + * @pin: gpio pin number within the device + * + * This function returns the direction of the specified GPIO. + * + * Return: 0 for output, 1 for input + */ +static int zynq_gpio_get_direction(struct gpio_chip *chip, unsigned int pin) +{ + u32 reg; + unsigned int bank_num, bank_pin_num; + struct zynq_gpio *gpio = gpiochip_get_data(chip); + + zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio); + + reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); + + return !(reg & BIT(bank_pin_num)); +} + /** * zynq_gpio_irq_mask - Disable the interrupts for a gpio pin * @irq_data: per irq and chip data passed down to chip functions @@ -639,6 +661,8 @@ static void zynq_gpio_restore_context(struct zynq_gpio *gpio) unsigned int bank_num; for (bank_num = 0; bank_num < gpio->p_data->max_bank; bank_num++) { + writel_relaxed(ZYNQ_GPIO_IXR_DISABLE_ALL, gpio->base_addr + + ZYNQ_GPIO_INTDIS_OFFSET(bank_num)); writel_relaxed(gpio->context.datalsw[bank_num], gpio->base_addr + ZYNQ_GPIO_DATA_LSW_OFFSET(bank_num)); @@ -648,9 +672,6 @@ static void zynq_gpio_restore_context(struct zynq_gpio *gpio) writel_relaxed(gpio->context.dirm[bank_num], gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); - writel_relaxed(gpio->context.int_en[bank_num], - gpio->base_addr + - ZYNQ_GPIO_INTEN_OFFSET(bank_num)); writel_relaxed(gpio->context.int_type[bank_num], gpio->base_addr + ZYNQ_GPIO_INTTYPE_OFFSET(bank_num)); @@ -660,6 +681,9 @@ static void zynq_gpio_restore_context(struct zynq_gpio *gpio) writel_relaxed(gpio->context.int_any[bank_num], gpio->base_addr + ZYNQ_GPIO_INTANY_OFFSET(bank_num)); + writel_relaxed(~(gpio->context.int_en[bank_num]), + gpio->base_addr + + ZYNQ_GPIO_INTEN_OFFSET(bank_num)); } } @@ -827,6 +851,7 @@ static int zynq_gpio_probe(struct platform_device *pdev) chip->free = zynq_gpio_free; chip->direction_input = zynq_gpio_dir_in; chip->direction_output = zynq_gpio_dir_out; + chip->get_direction = zynq_gpio_get_direction; chip->base = of_alias_get_id(pdev->dev.of_node, "gpio"); chip->ngpio = gpio->p_data->ngpio; diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 8edbb3f0c1013bdabab88b20f9acc7facea60963..d872dc82725e84e5ea9787ecea7327632d702337 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -24,11 +24,19 @@ #include "gpiolib.h" +#define QUIRK_NO_EDGE_EVENTS_ON_BOOT 0x01l +#define QUIRK_NO_WAKEUP 0x02l + static int run_edge_events_on_boot = -1; module_param(run_edge_events_on_boot, int, 0444); MODULE_PARM_DESC(run_edge_events_on_boot, "Run edge _AEI event-handlers at boot: 0=no, 1=yes, -1=auto"); +static int honor_wakeup = -1; +module_param(honor_wakeup, int, 0444); +MODULE_PARM_DESC(honor_wakeup, + "Honor the ACPI wake-capable flag: 0=no, 1=yes, -1=auto"); + /** * struct acpi_gpio_event - ACPI GPIO event handler data * @@ -278,7 +286,7 @@ static acpi_status acpi_gpiochip_alloc_event(struct acpi_resource *ares, event->handle = evt_handle; event->handler = handler; event->irq = irq; - event->irq_is_wake = agpio->wake_capable == ACPI_WAKE_CAPABLE; + event->irq_is_wake = honor_wakeup && agpio->wake_capable == ACPI_WAKE_CAPABLE; event->pin = pin; event->desc = desc; @@ -1263,7 +1271,7 @@ static int acpi_gpio_handle_deferred_request_irqs(void) /* We must use _sync so that this runs after the first deferred_probe run */ late_initcall_sync(acpi_gpio_handle_deferred_request_irqs); -static const struct dmi_system_id run_edge_events_on_boot_blacklist[] = { +static const struct dmi_system_id gpiolib_acpi_quirks[] = { { /* * The Minix Neo Z83-4 has a micro-USB-B id-pin handler for @@ -1273,7 +1281,8 @@ static const struct dmi_system_id run_edge_events_on_boot_blacklist[] = { .matches = { DMI_MATCH(DMI_SYS_VENDOR, "MINIX"), DMI_MATCH(DMI_PRODUCT_NAME, "Z83-4"), - } + }, + .driver_data = (void *)QUIRK_NO_EDGE_EVENTS_ON_BOOT, }, { /* @@ -1285,20 +1294,52 @@ static const struct dmi_system_id run_edge_events_on_boot_blacklist[] = { .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Wortmann_AG"), DMI_MATCH(DMI_PRODUCT_NAME, "TERRA_PAD_1061"), - } + }, + .driver_data = (void *)QUIRK_NO_EDGE_EVENTS_ON_BOOT, + }, + { + /* + * Various HP X2 10 Cherry Trail models use an external + * embedded-controller connected via I2C + an ACPI GPIO + * event handler. The embedded controller generates various + * spurious wakeup events when suspended. So disable wakeup + * for its handler (it uses the only ACPI GPIO event handler). + * This breaks wakeup when opening the lid, the user needs + * to press the power-button to wakeup the system. The + * alternative is suspend simply not working, which is worse. + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HP"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP x2 Detachable 10-p0XX"), + }, + .driver_data = (void *)QUIRK_NO_WAKEUP, }, {} /* Terminating entry */ }; static int acpi_gpio_setup_params(void) { + const struct dmi_system_id *id; + long quirks = 0; + + id = dmi_first_match(gpiolib_acpi_quirks); + if (id) + quirks = (long)id->driver_data; + if (run_edge_events_on_boot < 0) { - if (dmi_check_system(run_edge_events_on_boot_blacklist)) + if (quirks & QUIRK_NO_EDGE_EVENTS_ON_BOOT) run_edge_events_on_boot = 0; else run_edge_events_on_boot = 1; } + if (honor_wakeup < 0) { + if (quirks & QUIRK_NO_WAKEUP) + honor_wakeup = 0; + else + honor_wakeup = 1; + } + return 0; } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 113852c39e5a59168513b7b72870805f0c9c3a98..602dce9a17595b400216b7cbb2e018b9d1e7cc83 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1909,7 +1909,9 @@ static int gpiochip_add_irqchip(struct gpio_chip *gpiochip, type = IRQ_TYPE_NONE; } +#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY if (!gpiochip->to_irq) +#endif gpiochip->to_irq = gpiochip_to_irq; gpiochip->irq.default_type = type; @@ -1921,11 +1923,13 @@ static int gpiochip_add_irqchip(struct gpio_chip *gpiochip, else ops = &gpiochip_domain_ops; +#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY if (gpiochip->irq.parent_domain) gpiochip->irq.domain = irq_domain_add_hierarchy(gpiochip->irq.parent_domain, 0, gpiochip->ngpio, np, ops, gpiochip); else +#endif gpiochip->irq.domain = irq_domain_add_simple(np, gpiochip->ngpio, gpiochip->irq.first, ops, gpiochip); @@ -3764,8 +3768,9 @@ static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id, if (chip->ngpio <= p->chip_hwnum) { dev_err(dev, - "requested GPIO %d is out of range [0..%d] for chip %s\n", - idx, chip->ngpio, chip->label); + "requested GPIO %u (%u) is out of range [0..%u] for chip %s\n", + idx, p->chip_hwnum, chip->ngpio - 1, + chip->label); return ERR_PTR(-EINVAL); } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c index bf872f694f5090da9ebc1fc5b227bb45364ef7d4..d1fbaea91f580a037d500ed3694977a382e5a1f3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c @@ -337,17 +337,9 @@ bool amdgpu_atombios_get_connector_info_from_object_table(struct amdgpu_device * path_size += le16_to_cpu(path->usSize); if (device_support & le16_to_cpu(path->usDeviceTag)) { - uint8_t con_obj_id, con_obj_num, con_obj_type; - - con_obj_id = + uint8_t con_obj_id = (le16_to_cpu(path->usConnObjectId) & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT; - con_obj_num = - (le16_to_cpu(path->usConnObjectId) & ENUM_ID_MASK) - >> ENUM_ID_SHIFT; - con_obj_type = - (le16_to_cpu(path->usConnObjectId) & - OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT; /* Skip TV/CV support */ if ((le16_to_cpu(path->usDeviceTag) == @@ -372,14 +364,7 @@ bool amdgpu_atombios_get_connector_info_from_object_table(struct amdgpu_device * router.ddc_valid = false; router.cd_valid = false; for (j = 0; j < ((le16_to_cpu(path->usSize) - 8) / 2); j++) { - uint8_t grph_obj_id, grph_obj_num, grph_obj_type; - - grph_obj_id = - (le16_to_cpu(path->usGraphicObjIds[j]) & - OBJECT_ID_MASK) >> OBJECT_ID_SHIFT; - grph_obj_num = - (le16_to_cpu(path->usGraphicObjIds[j]) & - ENUM_ID_MASK) >> ENUM_ID_SHIFT; + uint8_t grph_obj_type= grph_obj_type = (le16_to_cpu(path->usGraphicObjIds[j]) & OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h index bb5a47a45790726272ab4d30077498b8c6edfa3d..5c76a815396df9d2cd6d4354048f2994cf1d2fe0 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h @@ -97,6 +97,7 @@ struct amdgpu_gmc { uint32_t srbm_soft_reset; bool prt_warning; uint64_t stolen_size; + uint32_t sdpif_register; /* apertures */ u64 shared_aperture_start; u64 shared_aperture_end; diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c index ede27dab675facf137f2a80b2b738b485c864874..8b25940c1367a570957ffe503bff4209c9ce6ba2 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c @@ -992,6 +992,19 @@ static void gmc_v9_0_init_golden_registers(struct amdgpu_device *adev) } } +/** + * gmc_v9_0_restore_registers - restores regs + * + * @adev: amdgpu_device pointer + * + * This restores register values, saved at suspend. + */ +static void gmc_v9_0_restore_registers(struct amdgpu_device *adev) +{ + if (adev->asic_type == CHIP_RAVEN) + WREG32(mmDCHUBBUB_SDPIF_MMIO_CNTRL_0, adev->gmc.sdpif_register); +} + /** * gmc_v9_0_gart_enable - gart enable * @@ -1080,6 +1093,20 @@ static int gmc_v9_0_hw_init(void *handle) return r; } +/** + * gmc_v9_0_save_registers - saves regs + * + * @adev: amdgpu_device pointer + * + * This saves potential register values that should be + * restored upon resume + */ +static void gmc_v9_0_save_registers(struct amdgpu_device *adev) +{ + if (adev->asic_type == CHIP_RAVEN) + adev->gmc.sdpif_register = RREG32(mmDCHUBBUB_SDPIF_MMIO_CNTRL_0); +} + /** * gmc_v9_0_gart_disable - gart disable * @@ -1112,9 +1139,16 @@ static int gmc_v9_0_hw_fini(void *handle) static int gmc_v9_0_suspend(void *handle) { + int r; struct amdgpu_device *adev = (struct amdgpu_device *)handle; - return gmc_v9_0_hw_fini(adev); + r = gmc_v9_0_hw_fini(adev); + if (r) + return r; + + gmc_v9_0_save_registers(adev); + + return 0; } static int gmc_v9_0_resume(void *handle) @@ -1122,6 +1156,7 @@ static int gmc_v9_0_resume(void *handle) int r; struct amdgpu_device *adev = (struct amdgpu_device *)handle; + gmc_v9_0_restore_registers(adev); r = gmc_v9_0_hw_init(adev); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/soc15.c b/drivers/gpu/drm/amd/amdgpu/soc15.c index 83f2717fcf81a4077b7e7187ad0c0e2c7abdd046..9e74f4304313ce51481b4981b0155414783303c1 100644 --- a/drivers/gpu/drm/amd/amdgpu/soc15.c +++ b/drivers/gpu/drm/amd/amdgpu/soc15.c @@ -205,7 +205,12 @@ static u32 soc15_get_config_memsize(struct amdgpu_device *adev) static u32 soc15_get_xclk(struct amdgpu_device *adev) { - return adev->clock.spll.reference_freq; + u32 reference_clock = adev->clock.spll.reference_freq; + + if (adev->asic_type == CHIP_RAVEN) + return reference_clock / 4; + + return reference_clock; } diff --git a/drivers/gpu/drm/amd/amdgpu/soc15_common.h b/drivers/gpu/drm/amd/amdgpu/soc15_common.h index 0942f492d2e19379eedbaf818363fe534922b99c..9d444f7fdfcdd1ceed7b8ea141219c34ea2bf3c0 100644 --- a/drivers/gpu/drm/amd/amdgpu/soc15_common.h +++ b/drivers/gpu/drm/amd/amdgpu/soc15_common.h @@ -51,6 +51,7 @@ do { \ uint32_t tmp_ = RREG32(adev->reg_offset[ip##_HWIP][inst][reg##_BASE_IDX] + reg); \ uint32_t loop = adev->usec_timeout; \ + ret = 0; \ while ((tmp_ & (mask)) != (expected_value)) { \ udelay(2); \ tmp_ = RREG32(adev->reg_offset[ip##_HWIP][inst][reg##_BASE_IDX] + reg); \ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c index 8403b6a9a77bd0008ab30b67153db5392206a5b6..d8c53ddc23b4364ad88cdbff1ce25f9e6e1bcee1 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c @@ -248,7 +248,8 @@ bool dm_helpers_dp_mst_write_payload_allocation_table( drm_dp_mst_reset_vcpi_slots(mst_mgr, mst_port); } - ret = drm_dp_update_payload_part1(mst_mgr); + /* It's OK for this to fail */ + drm_dp_update_payload_part1(mst_mgr); /* mst_mgr->->payloads are VC payload notify MST branch using DPCD or * AUX message. The sequence is slot 1-63 allocated sequence for each @@ -257,9 +258,6 @@ bool dm_helpers_dp_mst_write_payload_allocation_table( get_payload_table(aconnector, proposed_table); - if (ret) - return false; - return true; } @@ -310,7 +308,6 @@ bool dm_helpers_dp_mst_send_payload_allocation( struct amdgpu_dm_connector *aconnector; struct drm_dp_mst_topology_mgr *mst_mgr; struct drm_dp_mst_port *mst_port; - int ret; aconnector = stream->sink->priv; @@ -324,10 +321,8 @@ bool dm_helpers_dp_mst_send_payload_allocation( if (!mst_mgr->mst_state) return false; - ret = drm_dp_update_payload_part2(mst_mgr); - - if (ret) - return false; + /* It's OK for this to fail */ + drm_dp_update_payload_part2(mst_mgr); if (!enable) drm_dp_mst_deallocate_vcpi(mst_mgr, mst_port); diff --git a/drivers/gpu/drm/amd/display/dc/calcs/dcn_calcs.c b/drivers/gpu/drm/amd/display/dc/calcs/dcn_calcs.c index 6342f649935123c7e442bd839ab6a6f93d694b6c..b0956c360393e94dd5b70f9b98493251ef7b93d4 100644 --- a/drivers/gpu/drm/amd/display/dc/calcs/dcn_calcs.c +++ b/drivers/gpu/drm/amd/display/dc/calcs/dcn_calcs.c @@ -1346,6 +1346,7 @@ void dcn_bw_update_from_pplib(struct dc *dc) struct dc_context *ctx = dc->ctx; struct dm_pp_clock_levels_with_voltage fclks = {0}, dcfclks = {0}; bool res; + unsigned vmin0p65_idx, vmid0p72_idx, vnom0p8_idx, vmax0p9_idx; /* TODO: This is not the proper way to obtain fabric_and_dram_bandwidth, should be min(fclk, memclk) */ res = dm_pp_get_clock_levels_by_type_with_voltage( @@ -1357,17 +1358,28 @@ void dcn_bw_update_from_pplib(struct dc *dc) res = verify_clock_values(&fclks); if (res) { - ASSERT(fclks.num_levels >= 3); - dc->dcn_soc->fabric_and_dram_bandwidth_vmin0p65 = 32 * (fclks.data[0].clocks_in_khz / 1000.0) / 1000.0; - dc->dcn_soc->fabric_and_dram_bandwidth_vmid0p72 = dc->dcn_soc->number_of_channels * - (fclks.data[fclks.num_levels - (fclks.num_levels > 2 ? 3 : 2)].clocks_in_khz / 1000.0) - * ddr4_dram_factor_single_Channel / 1000.0; - dc->dcn_soc->fabric_and_dram_bandwidth_vnom0p8 = dc->dcn_soc->number_of_channels * - (fclks.data[fclks.num_levels - 2].clocks_in_khz / 1000.0) - * ddr4_dram_factor_single_Channel / 1000.0; - dc->dcn_soc->fabric_and_dram_bandwidth_vmax0p9 = dc->dcn_soc->number_of_channels * - (fclks.data[fclks.num_levels - 1].clocks_in_khz / 1000.0) - * ddr4_dram_factor_single_Channel / 1000.0; + ASSERT(fclks.num_levels); + + vmin0p65_idx = 0; + vmid0p72_idx = fclks.num_levels - + (fclks.num_levels > 2 ? 3 : (fclks.num_levels > 1 ? 2 : 1)); + vnom0p8_idx = fclks.num_levels - (fclks.num_levels > 1 ? 2 : 1); + vmax0p9_idx = fclks.num_levels - 1; + + dc->dcn_soc->fabric_and_dram_bandwidth_vmin0p65 = + 32 * (fclks.data[vmin0p65_idx].clocks_in_khz / 1000.0) / 1000.0; + dc->dcn_soc->fabric_and_dram_bandwidth_vmid0p72 = + dc->dcn_soc->number_of_channels * + (fclks.data[vmid0p72_idx].clocks_in_khz / 1000.0) + * ddr4_dram_factor_single_Channel / 1000.0; + dc->dcn_soc->fabric_and_dram_bandwidth_vnom0p8 = + dc->dcn_soc->number_of_channels * + (fclks.data[vnom0p8_idx].clocks_in_khz / 1000.0) + * ddr4_dram_factor_single_Channel / 1000.0; + dc->dcn_soc->fabric_and_dram_bandwidth_vmax0p9 = + dc->dcn_soc->number_of_channels * + (fclks.data[vmax0p9_idx].clocks_in_khz / 1000.0) + * ddr4_dram_factor_single_Channel / 1000.0; } else BREAK_TO_DEBUGGER(); diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link.c b/drivers/gpu/drm/amd/display/dc/core/dc_link.c index 2f42964fb9f4541d88a8cdcac685636c2650fe0e..3abc0294c05f5fe6fa0aa6c2b6ed8b5752fd0d96 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_link.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link.c @@ -780,8 +780,7 @@ bool dc_link_detect(struct dc_link *link, enum dc_detect_reason reason) same_edid = is_same_edid(&prev_sink->dc_edid, &sink->dc_edid); if (link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT && - sink_caps.transaction_type == DDC_TRANSACTION_TYPE_I2C_OVER_AUX && - reason != DETECT_REASON_HPDRX) { + sink_caps.transaction_type == DDC_TRANSACTION_TYPE_I2C_OVER_AUX) { /* * TODO debug why Dell 2413 doesn't like * two link trainings diff --git a/drivers/gpu/drm/amd/display/dc/dml/dml_common_defs.c b/drivers/gpu/drm/amd/display/dc/dml/dml_common_defs.c index b953b02a151214b5adcb945c38dcd911944109ad..723af0b2dda04ba385e6c6c993ae49e14b076cad 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dml_common_defs.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dml_common_defs.c @@ -24,7 +24,7 @@ */ #include "dml_common_defs.h" -#include "../calcs/dcn_calc_math.h" +#include "dcn_calc_math.h" #include "dml_inline_defs.h" diff --git a/drivers/gpu/drm/amd/display/dc/dml/dml_inline_defs.h b/drivers/gpu/drm/amd/display/dc/dml/dml_inline_defs.h index e8ce08567cd8e909bd641fd9706e195a5b4de4b2..e4f595a3038c4d355ecca622b3ab54001c2bc901 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dml_inline_defs.h +++ b/drivers/gpu/drm/amd/display/dc/dml/dml_inline_defs.h @@ -27,7 +27,7 @@ #define __DML_INLINE_DEFS_H__ #include "dml_common_defs.h" -#include "../calcs/dcn_calc_math.h" +#include "dcn_calc_math.h" #include "dml_logger.h" static inline double dml_min(double a, double b) diff --git a/drivers/gpu/drm/amd/display/dc/calcs/dcn_calc_math.h b/drivers/gpu/drm/amd/display/dc/inc/dcn_calc_math.h similarity index 100% rename from drivers/gpu/drm/amd/display/dc/calcs/dcn_calc_math.h rename to drivers/gpu/drm/amd/display/dc/inc/dcn_calc_math.h diff --git a/drivers/gpu/drm/amd/include/asic_reg/dce/dce_12_0_offset.h b/drivers/gpu/drm/amd/include/asic_reg/dce/dce_12_0_offset.h index b6f74bf4af023fd53ee79ce44ecb29cf9f8cb43b..27bb8c1ab85876bed4ad23bfc6df7648eaa8a851 100644 --- a/drivers/gpu/drm/amd/include/asic_reg/dce/dce_12_0_offset.h +++ b/drivers/gpu/drm/amd/include/asic_reg/dce/dce_12_0_offset.h @@ -7376,6 +7376,8 @@ #define mmCRTC4_CRTC_DRR_CONTROL 0x0f3e #define mmCRTC4_CRTC_DRR_CONTROL_BASE_IDX 2 +#define mmDCHUBBUB_SDPIF_MMIO_CNTRL_0 0x395d +#define mmDCHUBBUB_SDPIF_MMIO_CNTRL_0_BASE_IDX 2 // addressBlock: dce_dc_fmt4_dispdec // base address: 0x2000 diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu10_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/smu10_hwmgr.c index 1546bc49004f8c84dbadd64c7ea32cae6a1e4aa9..48e31711bc68f00957f8cef242f7450980e3805e 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/smu10_hwmgr.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/smu10_hwmgr.c @@ -994,12 +994,15 @@ static int smu10_get_clock_by_type_with_latency(struct pp_hwmgr *hwmgr, clocks->num_levels = 0; for (i = 0; i < pclk_vol_table->count; i++) { - clocks->data[i].clocks_in_khz = pclk_vol_table->entries[i].clk * 10; - clocks->data[i].latency_in_us = latency_required ? - smu10_get_mem_latency(hwmgr, - pclk_vol_table->entries[i].clk) : - 0; - clocks->num_levels++; + if (pclk_vol_table->entries[i].clk) { + clocks->data[clocks->num_levels].clocks_in_khz = + pclk_vol_table->entries[i].clk * 10; + clocks->data[clocks->num_levels].latency_in_us = latency_required ? + smu10_get_mem_latency(hwmgr, + pclk_vol_table->entries[i].clk) : + 0; + clocks->num_levels++; + } } return 0; @@ -1045,9 +1048,11 @@ static int smu10_get_clock_by_type_with_voltage(struct pp_hwmgr *hwmgr, clocks->num_levels = 0; for (i = 0; i < pclk_vol_table->count; i++) { - clocks->data[i].clocks_in_khz = pclk_vol_table->entries[i].clk * 10; - clocks->data[i].voltage_in_mv = pclk_vol_table->entries[i].vol; - clocks->num_levels++; + if (pclk_vol_table->entries[i].clk) { + clocks->data[clocks->num_levels].clocks_in_khz = pclk_vol_table->entries[i].clk * 10; + clocks->data[clocks->num_levels].voltage_in_mv = pclk_vol_table->entries[i].vol; + clocks->num_levels++; + } } return 0; diff --git a/drivers/gpu/drm/arm/malidp_mw.c b/drivers/gpu/drm/arm/malidp_mw.c index 91472e5e0c8b8a3b0c19a2fb5f860fac83ffcb5b..7266d3c8b8f41866b50f324a09210f656bd8fd1c 100644 --- a/drivers/gpu/drm/arm/malidp_mw.c +++ b/drivers/gpu/drm/arm/malidp_mw.c @@ -55,7 +55,7 @@ malidp_mw_connector_mode_valid(struct drm_connector *connector, return MODE_OK; } -const struct drm_connector_helper_funcs malidp_mw_connector_helper_funcs = { +static const struct drm_connector_helper_funcs malidp_mw_connector_helper_funcs = { .get_modes = malidp_mw_connector_get_modes, .mode_valid = malidp_mw_connector_mode_valid, }; diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c index d73281095facabbaf7b54d6f309123f8b044821d..976109c20d493ccf5dd5a26542e93ec13b57b55b 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c @@ -79,7 +79,11 @@ static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c) struct videomode vm; unsigned long prate; unsigned int cfg; - int div; + int div, ret; + + ret = clk_prepare_enable(crtc->dc->hlcdc->sys_clk); + if (ret) + return; vm.vfront_porch = adj->crtc_vsync_start - adj->crtc_vdisplay; vm.vback_porch = adj->crtc_vtotal - adj->crtc_vsync_end; @@ -138,6 +142,8 @@ static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c) ATMEL_HLCDC_VSPSU | ATMEL_HLCDC_VSPHO | ATMEL_HLCDC_GUARDTIME_MASK | ATMEL_HLCDC_MODE_MASK, cfg); + + clk_disable_unprepare(crtc->dc->hlcdc->sys_clk); } static enum drm_mode_status diff --git a/drivers/gpu/drm/bridge/lt9611uxc.c b/drivers/gpu/drm/bridge/lt9611uxc.c index e37e77062b35e81cc43f3afd788e24cca5829b4c..6598129dae727ecfc1e3a09cf508a4b2d6b062c7 100644 --- a/drivers/gpu/drm/bridge/lt9611uxc.c +++ b/drivers/gpu/drm/bridge/lt9611uxc.c @@ -24,13 +24,14 @@ #include #include #include +#include #include #include #include #include #include #include -#include +#include #define CFG_HPD_INTERRUPTS BIT(0) #define CFG_EDID_INTERRUPTS BIT(1) @@ -139,6 +140,7 @@ struct lt9611_timing_info { }; static struct lt9611_timing_info lt9611_supp_timing_cfg[] = { + {3840, 2160, 24, 60, 4, 2}, /* 3840x2160 24bit 60Hz 4Lane 2ports */ {3840, 2160, 24, 30, 4, 2}, /* 3840x2160 24bit 30Hz 4Lane 2ports */ {1920, 1080, 24, 60, 4, 1}, /* 1080P 24bit 60Hz 4lane 1port */ {1920, 1080, 24, 30, 3, 1}, /* 1080P 24bit 30Hz 3lane 1port */ @@ -182,6 +184,11 @@ void lt9611_hpd_work(struct work_struct *work) envp[4] = NULL; kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, envp); + + if (dev->mode_config.funcs->output_poll_changed) + dev->mode_config.funcs->output_poll_changed(dev); + + drm_client_dev_hotplug(dev); } static struct lt9611 *bridge_to_lt9611(struct drm_bridge *bridge) @@ -893,7 +900,7 @@ static void lt9611_reset(struct lt9611 *pdata, bool on_off) gpio_set_value(pdata->reset_gpio, 0); msleep(20); gpio_set_value(pdata->reset_gpio, 1); - msleep(300); + msleep(180); } else { gpio_set_value(pdata->reset_gpio, 0); } diff --git a/drivers/gpu/drm/drm_context.c b/drivers/gpu/drm/drm_context.c index f973d287696a6febd621f8bf1ff32d5fc64bdca2..da5abf24f59ff7f02181a88f8452828473600532 100644 --- a/drivers/gpu/drm/drm_context.c +++ b/drivers/gpu/drm/drm_context.c @@ -361,23 +361,26 @@ int drm_legacy_addctx(struct drm_device *dev, void *data, { struct drm_ctx_list *ctx_entry; struct drm_ctx *ctx = data; + int tmp_handle; if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) && !drm_core_check_feature(dev, DRIVER_LEGACY)) return -EINVAL; - ctx->handle = drm_legacy_ctxbitmap_next(dev); - if (ctx->handle == DRM_KERNEL_CONTEXT) { + tmp_handle = drm_legacy_ctxbitmap_next(dev); + if (tmp_handle == DRM_KERNEL_CONTEXT) { /* Skip kernel's context and get a new one. */ - ctx->handle = drm_legacy_ctxbitmap_next(dev); + tmp_handle = drm_legacy_ctxbitmap_next(dev); } - DRM_DEBUG("%d\n", ctx->handle); - if (ctx->handle < 0) { + DRM_DEBUG("%d\n", tmp_handle); + if (tmp_handle < 0) { DRM_DEBUG("Not enough free contexts.\n"); /* Should this return -EBUSY instead? */ - return -ENOMEM; + return tmp_handle; } + ctx->handle = tmp_handle; + ctx_entry = kmalloc(sizeof(*ctx_entry), GFP_KERNEL); if (!ctx_entry) { DRM_DEBUG("out of memory\n"); diff --git a/drivers/gpu/drm/drm_debugfs_crc.c b/drivers/gpu/drm/drm_debugfs_crc.c index c88e5ff41add6a4898186672070b03293c3e9ffa..a3c756710845e25b4f38d1f3e1f0bc4b95c3d301 100644 --- a/drivers/gpu/drm/drm_debugfs_crc.c +++ b/drivers/gpu/drm/drm_debugfs_crc.c @@ -101,8 +101,8 @@ static ssize_t crc_control_write(struct file *file, const char __user *ubuf, if (IS_ERR(source)) return PTR_ERR(source); - if (source[len] == '\n') - source[len] = '\0'; + if (source[len - 1] == '\n') + source[len - 1] = '\0'; spin_lock_irq(&crc->lock); diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 7632933a79d6c7e7617670228fdcf302c5a87d90..b833512b708c3703ce6cf837b214a3aa154d73b6 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -270,7 +270,7 @@ static void drm_dp_encode_sideband_req(struct drm_dp_sideband_msg_req_body *req, memcpy(&buf[idx], req->u.i2c_read.transactions[i].bytes, req->u.i2c_read.transactions[i].num_bytes); idx += req->u.i2c_read.transactions[i].num_bytes; - buf[idx] = (req->u.i2c_read.transactions[i].no_stop_bit & 0x1) << 5; + buf[idx] = (req->u.i2c_read.transactions[i].no_stop_bit & 0x1) << 4; buf[idx] |= (req->u.i2c_read.transactions[i].i2c_transaction_delay & 0xf); idx++; } @@ -1021,9 +1021,20 @@ static struct drm_dp_mst_port *drm_dp_mst_get_port_ref_locked(struct drm_dp_mst_ static struct drm_dp_mst_port *drm_dp_get_validated_port_ref(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port) { struct drm_dp_mst_port *rport = NULL; + mutex_lock(&mgr->lock); - if (mgr->mst_primary) - rport = drm_dp_mst_get_port_ref_locked(mgr->mst_primary, port); + /* + * Port may or may not be 'valid' but we don't care about that when + * destroying the port and we are guaranteed that the port pointer + * will be valid until we've finished + */ + if (current_work() == &mgr->destroy_connector_work) { + kref_get(&port->kref); + rport = port; + } else if (mgr->mst_primary) { + rport = drm_dp_mst_get_port_ref_locked(mgr->mst_primary, + port); + } mutex_unlock(&mgr->lock); return rport; } @@ -2263,6 +2274,7 @@ static bool drm_dp_get_vc_payload_bw(int dp_link_bw, int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool mst_state) { int ret = 0; + int i = 0; struct drm_dp_mst_branch *mstb = NULL; u8 buf; u32 offset = DP_DPCD_REV; @@ -2338,10 +2350,21 @@ int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool ms /* this can fail if the device is gone */ drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, 0); ret = 0; + mutex_lock(&mgr->payload_lock); memset(mgr->payloads, 0, mgr->max_payloads * sizeof(struct drm_dp_payload)); mgr->payload_mask = 0; set_bit(0, &mgr->payload_mask); + for (i = 0; i < mgr->max_payloads; i++) { + struct drm_dp_vcpi *vcpi = mgr->proposed_vcpis[i]; + + if (vcpi) { + vcpi->vcpi = 0; + vcpi->num_slots = 0; + } + mgr->proposed_vcpis[i] = NULL; + } mgr->vcpi_mask = 0; + mutex_unlock(&mgr->payload_lock); } out_unlock: diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 8b546fde139d611179ebb6f1ce83c8ef4e77a828..da9a381d6b577d7813d72cd0db989f98ce8c90ff 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -1702,7 +1702,7 @@ int drm_fb_helper_check_var(struct fb_var_screeninfo *var, * Changes struct fb_var_screeninfo are currently not pushed back * to KMS, hence fail if different settings are requested. */ - if (var->bits_per_pixel != fb->format->cpp[0] * 8 || + if (var->bits_per_pixel > fb->format->cpp[0] * 8 || var->xres > fb->width || var->yres > fb->height || var->xres_virtual > fb->width || var->yres_virtual > fb->height) { DRM_DEBUG("fb requested width/height/bpp can't fit in current fb " @@ -1727,6 +1727,11 @@ int drm_fb_helper_check_var(struct fb_var_screeninfo *var, drm_fb_helper_fill_pixel_fmt(var, fb->format->depth); } + /* + * Likewise, bits_per_pixel should be rounded up to a supported value. + */ + var->bits_per_pixel = fb->format->cpp[0] * 8; + /* * drm fbdev emulation doesn't support changing the pixel format at all, * so reject all pixel format changing requests. @@ -2974,18 +2979,16 @@ static int drm_fbdev_fb_release(struct fb_info *info, int user) return 0; } -/* - * fb_ops.fb_destroy is called by the last put_fb_info() call at the end of - * unregister_framebuffer() or fb_release(). - */ -static void drm_fbdev_fb_destroy(struct fb_info *info) +static void drm_fbdev_cleanup(struct drm_fb_helper *fb_helper) { - struct drm_fb_helper *fb_helper = info->par; struct fb_info *fbi = fb_helper->fbdev; struct fb_ops *fbops = NULL; void *shadow = NULL; - if (fbi->fbdefio) { + if (!fb_helper->dev) + return; + + if (fbi && fbi->fbdefio) { fb_deferred_io_cleanup(fbi); shadow = fbi->screen_buffer; fbops = fbi->fbops; @@ -2999,6 +3002,12 @@ static void drm_fbdev_fb_destroy(struct fb_info *info) } drm_client_framebuffer_delete(fb_helper->buffer); +} + +static void drm_fbdev_release(struct drm_fb_helper *fb_helper) +{ + drm_fbdev_cleanup(fb_helper); + /* * FIXME: * Remove conditional when all CMA drivers have been moved over to using @@ -3010,6 +3019,15 @@ static void drm_fbdev_fb_destroy(struct fb_info *info) } } +/* + * fb_ops.fb_destroy is called by the last put_fb_info() call at the end of + * unregister_framebuffer() or fb_release(). + */ +static void drm_fbdev_fb_destroy(struct fb_info *info) +{ + drm_fbdev_release(info->par); +} + static int drm_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) { struct drm_fb_helper *fb_helper = info->par; @@ -3060,7 +3078,6 @@ int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper, struct drm_framebuffer *fb; struct fb_info *fbi; u32 format; - int ret; DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d)\n", sizes->surface_width, sizes->surface_height, @@ -3077,10 +3094,8 @@ int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper, fb = buffer->fb; fbi = drm_fb_helper_alloc_fbi(fb_helper); - if (IS_ERR(fbi)) { - ret = PTR_ERR(fbi); - goto err_free_buffer; - } + if (IS_ERR(fbi)) + return PTR_ERR(fbi); fbi->par = fb_helper; fbi->fbops = &drm_fbdev_fb_ops; @@ -3111,8 +3126,7 @@ int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper, if (!fbops || !shadow) { kfree(fbops); vfree(shadow); - ret = -ENOMEM; - goto err_fb_info_destroy; + return -ENOMEM; } *fbops = *fbi->fbops; @@ -3124,13 +3138,6 @@ int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper, } return 0; - -err_fb_info_destroy: - drm_fb_helper_fini(fb_helper); -err_free_buffer: - drm_client_framebuffer_delete(buffer); - - return ret; } EXPORT_SYMBOL(drm_fb_helper_generic_probe); @@ -3142,18 +3149,11 @@ static void drm_fbdev_client_unregister(struct drm_client_dev *client) { struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client); - if (fb_helper->fbdev) { - drm_fb_helper_unregister_fbi(fb_helper); + if (fb_helper->fbdev) /* drm_fbdev_fb_destroy() takes care of cleanup */ - return; - } - - /* Did drm_fb_helper_fbdev_setup() run? */ - if (fb_helper->dev) - drm_fb_helper_fini(fb_helper); - - drm_client_release(client); - kfree(fb_helper); + drm_fb_helper_unregister_fbi(fb_helper); + else + drm_fbdev_release(fb_helper); } static int drm_fbdev_client_restore(struct drm_client_dev *client) @@ -3169,7 +3169,7 @@ static int drm_fbdev_client_hotplug(struct drm_client_dev *client) struct drm_device *dev = client->dev; int ret; - /* If drm_fb_helper_fbdev_setup() failed, we only try once */ + /* Setup is not retried if it has failed */ if (!fb_helper->dev && fb_helper->funcs) return 0; @@ -3179,15 +3179,34 @@ static int drm_fbdev_client_hotplug(struct drm_client_dev *client) if (!dev->mode_config.num_connector) return 0; - ret = drm_fb_helper_fbdev_setup(dev, fb_helper, &drm_fb_helper_generic_funcs, - fb_helper->preferred_bpp, 0); - if (ret) { - fb_helper->dev = NULL; - fb_helper->fbdev = NULL; - return ret; - } + drm_fb_helper_prepare(dev, fb_helper, &drm_fb_helper_generic_funcs); + + ret = drm_fb_helper_init(dev, fb_helper, dev->mode_config.num_connector); + if (ret) + goto err; + + ret = drm_fb_helper_single_add_all_connectors(fb_helper); + if (ret) + goto err_cleanup; + + if (!drm_drv_uses_atomic_modeset(dev)) + drm_helper_disable_unused_functions(dev); + + ret = drm_fb_helper_initial_config(fb_helper, fb_helper->preferred_bpp); + if (ret) + goto err_cleanup; return 0; + +err_cleanup: + drm_fbdev_cleanup(fb_helper); +err: + fb_helper->dev = NULL; + fb_helper->fbdev = NULL; + + DRM_DEV_ERROR(dev->dev, "fbdev: Failed to setup generic emulation (ret=%d)\n", ret); + + return ret; } static const struct drm_client_funcs drm_fbdev_client_funcs = { @@ -3238,12 +3257,16 @@ int drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp) return ret; } - drm_client_add(&fb_helper->client); - + if (!preferred_bpp) + preferred_bpp = dev->mode_config.preferred_depth; + if (!preferred_bpp) + preferred_bpp = 32; fb_helper->preferred_bpp = preferred_bpp; drm_fbdev_client_hotplug(&fb_helper->client); + drm_client_add(&fb_helper->client); + return 0; } EXPORT_SYMBOL(drm_fbdev_generic_setup); diff --git a/drivers/gpu/drm/drm_rect.c b/drivers/gpu/drm/drm_rect.c index 8c057829b80423c93984f8ec78e9ed5a18ba0e5c..0f5a0c64c4c4aa2776629405c6076c080ef60900 100644 --- a/drivers/gpu/drm/drm_rect.c +++ b/drivers/gpu/drm/drm_rect.c @@ -52,7 +52,12 @@ EXPORT_SYMBOL(drm_rect_intersect); static u32 clip_scaled(u32 src, u32 dst, u32 clip) { - u64 tmp = mul_u32_u32(src, dst - clip); + u64 tmp; + + if (dst == 0) + return 0; + + tmp = mul_u32_u32(src, dst - clip); /* * Round toward 1.0 when clipping so that we don't accidentally diff --git a/drivers/gpu/drm/etnaviv/etnaviv_dump.c b/drivers/gpu/drm/etnaviv/etnaviv_dump.c index 468dff2f79040e32ed972a01481370c317d11b79..9d839b4fd8f78678535373ecab81603697248639 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_dump.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_dump.c @@ -217,7 +217,7 @@ void etnaviv_core_dump(struct etnaviv_gpu *gpu) mutex_lock(&obj->lock); pages = etnaviv_gem_get_pages(obj); mutex_unlock(&obj->lock); - if (pages) { + if (!IS_ERR(pages)) { int j; iter.hdr->data[0] = bomap - bomap_start; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c index 0566171f8df22f65e0236bf37afe204e828f688b..f21529e635e3d8d9ae16ffd440a425ae5a93abf0 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c @@ -15,7 +15,7 @@ struct sg_table *etnaviv_gem_prime_get_sg_table(struct drm_gem_object *obj) int npages = obj->size >> PAGE_SHIFT; if (WARN_ON(!etnaviv_obj->pages)) /* should have already pinned! */ - return NULL; + return ERR_PTR(-EINVAL); return drm_prime_pages_to_sg(etnaviv_obj->pages, npages); } diff --git a/drivers/gpu/drm/etnaviv/etnaviv_perfmon.c b/drivers/gpu/drm/etnaviv/etnaviv_perfmon.c index 9980d81a26e3cef9816646bc82733f89548dd778..4227a4006c34963800563838a90c067a9cee95c4 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_perfmon.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_perfmon.c @@ -113,7 +113,7 @@ static const struct etnaviv_pm_domain doms_3d[] = { .name = "PE", .profile_read = VIVS_MC_PROFILE_PE_READ, .profile_config = VIVS_MC_PROFILE_CONFIG0, - .nr_signals = 5, + .nr_signals = 4, .signal = (const struct etnaviv_pm_signal[]) { { "PIXEL_COUNT_KILLED_BY_COLOR_PIPE", @@ -435,7 +435,7 @@ int etnaviv_pm_query_sig(struct etnaviv_gpu *gpu, dom = meta->domains + signal->domain; - if (signal->iter > dom->nr_signals) + if (signal->iter >= dom->nr_signals) return -EINVAL; sig = &dom->signal[signal->iter]; @@ -461,7 +461,7 @@ int etnaviv_pm_req_validate(const struct drm_etnaviv_gem_submit_pmr *r, dom = meta->domains + r->domain; - if (r->signal > dom->nr_signals) + if (r->signal >= dom->nr_signals) return -EINVAL; return 0; diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index adefae58b5fcd6e09d513aed236c12d220e406c5..b4035ef72af8a86496fb158882785a9fcf7350b5 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -480,6 +480,7 @@ static int psbfb_probe(struct drm_fb_helper *helper, container_of(helper, struct psb_fbdev, psb_fb_helper); struct drm_device *dev = psb_fbdev->psb_fb_helper.dev; struct drm_psb_private *dev_priv = dev->dev_private; + unsigned int fb_size; int bytespp; bytespp = sizes->surface_bpp / 8; @@ -489,8 +490,11 @@ static int psbfb_probe(struct drm_fb_helper *helper, /* If the mode will not fit in 32bit then switch to 16bit to get a console on full resolution. The X mode setting server will allocate its own 32bit GEM framebuffer */ - if (ALIGN(sizes->fb_width * bytespp, 64) * sizes->fb_height > - dev_priv->vram_stolen_size) { + fb_size = ALIGN(sizes->surface_width * bytespp, 64) * + sizes->surface_height; + fb_size = ALIGN(fb_size, PAGE_SIZE); + + if (fb_size > dev_priv->vram_stolen_size) { sizes->surface_bpp = 16; sizes->surface_depth = 16; } diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c index 8bd29075ae4ebb9c0d865d00b92d7f5a7be9e163..edcca17615001cb4ba37e7ac3601d731189f36c8 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c @@ -71,7 +71,6 @@ static int hibmc_drm_fb_create(struct drm_fb_helper *helper, DRM_DEBUG_DRIVER("surface width(%d), height(%d) and bpp(%d)\n", sizes->surface_width, sizes->surface_height, sizes->surface_bpp); - sizes->surface_depth = 32; bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8); diff --git a/drivers/gpu/drm/i915/gvt/dmabuf.c b/drivers/gpu/drm/i915/gvt/dmabuf.c index 51ed99a37803310e2d9b7e84882ae7df97e3b59c..6053f5a93f588ae01aa07fcc7f919eb794bfd417 100644 --- a/drivers/gpu/drm/i915/gvt/dmabuf.c +++ b/drivers/gpu/drm/i915/gvt/dmabuf.c @@ -95,12 +95,12 @@ static void dmabuf_gem_object_free(struct kref *kref) dmabuf_obj = container_of(pos, struct intel_vgpu_dmabuf_obj, list); if (dmabuf_obj == obj) { + list_del(pos); intel_gvt_hypervisor_put_vfio_device(vgpu); idr_remove(&vgpu->object_idr, dmabuf_obj->dmabuf_id); kfree(dmabuf_obj->info); kfree(dmabuf_obj); - list_del(pos); break; } } diff --git a/drivers/gpu/drm/i915/gvt/vgpu.c b/drivers/gpu/drm/i915/gvt/vgpu.c index c628be05fbfe907a1bce89fd4727df79906fe63f..69bba88906cd088963841a1fdf3c6ae77827c471 100644 --- a/drivers/gpu/drm/i915/gvt/vgpu.c +++ b/drivers/gpu/drm/i915/gvt/vgpu.c @@ -556,9 +556,9 @@ void intel_gvt_reset_vgpu_locked(struct intel_vgpu *vgpu, bool dmlr, intel_vgpu_reset_mmio(vgpu, dmlr); populate_pvinfo_page(vgpu); - intel_vgpu_reset_display(vgpu); if (dmlr) { + intel_vgpu_reset_display(vgpu); intel_vgpu_reset_cfg_space(vgpu); /* only reset the failsafe mode when dmlr reset */ vgpu->failsafe = false; diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index 7a0e6dbbad2e4817132cd3b7c3bdfd87914bbcdc..ef383fd429885d8e52a2abb7626a9afb6ffd4fe8 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -770,18 +770,19 @@ int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data, if (args->ctx_id == DEFAULT_CONTEXT_HANDLE) return -ENOENT; + ret = i915_mutex_lock_interruptible(dev); + if (ret) + return ret; + ctx = i915_gem_context_lookup(file_priv, args->ctx_id); - if (!ctx) + if (!ctx) { + mutex_unlock(&dev->struct_mutex); return -ENOENT; - - ret = mutex_lock_interruptible(&dev->struct_mutex); - if (ret) - goto out; + } __destroy_hw_context(ctx, file_priv); mutex_unlock(&dev->struct_mutex); -out: i915_gem_context_put(ctx); return 0; } diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index 6937ef0b4bfc9d2a700262c0d01952efafd8b375..13e97faabaa74d808ede2f59b69a8bd4247111c9 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -1562,6 +1562,15 @@ static u32 *gen9_init_indirectctx_bb(struct intel_engine_cs *engine, u32 *batch) /* WaFlushCoherentL3CacheLinesAtContextSwitch:skl,bxt,glk */ batch = gen8_emit_flush_coherentl3_wa(engine, batch); + /* WaClearSlmSpaceAtContextSwitch:skl,bxt,kbl,glk,cfl */ + batch = gen8_emit_pipe_control(batch, + PIPE_CONTROL_FLUSH_L3 | + PIPE_CONTROL_GLOBAL_GTT_IVB | + PIPE_CONTROL_CS_STALL | + PIPE_CONTROL_QW_WRITE, + i915_ggtt_offset(engine->scratch) + + 2 * CACHELINE_BYTES); + batch = emit_lri(batch, lri, ARRAY_SIZE(lri)); /* WaClearSlmSpaceAtContextSwitch:kbl */ diff --git a/drivers/gpu/drm/i915/selftests/i915_random.h b/drivers/gpu/drm/i915/selftests/i915_random.h index 7dffedc501cadaf1294dbe1ca4515a5db62c1cdb..faaa3507fdeefe7b18b7d8ad162492aa2356b696 100644 --- a/drivers/gpu/drm/i915/selftests/i915_random.h +++ b/drivers/gpu/drm/i915/selftests/i915_random.h @@ -25,6 +25,7 @@ #ifndef __I915_SELFTESTS_RANDOM_H__ #define __I915_SELFTESTS_RANDOM_H__ +#include #include #include "../i915_selftest.h" diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c index 92ecb9bf982cfe7398eefe3993966fc2acf28b0c..b86ee7d25af36d04b22bf5707f6f7d68a7c09572 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c @@ -308,6 +308,7 @@ static int mtk_crtc_ddp_hw_init(struct mtk_drm_crtc *mtk_crtc) static void mtk_crtc_ddp_hw_fini(struct mtk_drm_crtc *mtk_crtc) { struct drm_device *drm = mtk_crtc->base.dev; + struct drm_crtc *crtc = &mtk_crtc->base; int i; DRM_DEBUG_DRIVER("%s\n", __func__); @@ -329,6 +330,13 @@ static void mtk_crtc_ddp_hw_fini(struct mtk_drm_crtc *mtk_crtc) mtk_disp_mutex_unprepare(mtk_crtc->mutex); pm_runtime_put(drm->dev); + + if (crtc->state->event && !crtc->state->active) { + spin_lock_irq(&crtc->dev->event_lock); + drm_crtc_send_vblank_event(crtc, crtc->state->event); + crtc->state->event = NULL; + spin_unlock_irq(&crtc->dev->event_lock); + } } static void mtk_crtc_ddp_config(struct drm_crtc *crtc) diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig index 843a9d40c05e33f5f13d1cbb7b09c751127940f2..db7a1d52a61dfb0156e766a101a527cbf257148e 100644 --- a/drivers/gpu/drm/msm/Kconfig +++ b/drivers/gpu/drm/msm/Kconfig @@ -12,11 +12,11 @@ config DRM_MSM select SHMEM select TMPFS select QCOM_SCM + select QCOM_COMMAND_DB if ARCH_QCOM select WANT_DEV_COREDUMP select SND_SOC_HDMI_CODEC if SND_SOC select SYNC_FILE select PM_OPP - default y help DRM/KMS driver for MSM/snapdragon. diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c index 669c2d4b070dea4c711e0e8df9585d68dd810cd9..5c068301d817c24ed56c9ccc61a0471b9b723cc2 100644 --- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c @@ -395,19 +395,17 @@ static const unsigned int a3xx_registers[] = { 0x2200, 0x2212, 0x2214, 0x2217, 0x221a, 0x221a, 0x2240, 0x227e, 0x2280, 0x228b, 0x22c0, 0x22c0, 0x22c4, 0x22ce, 0x22d0, 0x22d8, 0x22df, 0x22e6, 0x22e8, 0x22e9, 0x22ec, 0x22ec, 0x22f0, 0x22f7, - 0x22ff, 0x22ff, 0x2340, 0x2343, 0x2348, 0x2349, 0x2350, 0x2356, - 0x2360, 0x2360, 0x2440, 0x2440, 0x2444, 0x2444, 0x2448, 0x244d, - 0x2468, 0x2469, 0x246c, 0x246d, 0x2470, 0x2470, 0x2472, 0x2472, - 0x2474, 0x2475, 0x2479, 0x247a, 0x24c0, 0x24d3, 0x24e4, 0x24ef, - 0x2500, 0x2509, 0x250c, 0x250c, 0x250e, 0x250e, 0x2510, 0x2511, - 0x2514, 0x2515, 0x25e4, 0x25e4, 0x25ea, 0x25ea, 0x25ec, 0x25ed, - 0x25f0, 0x25f0, 0x2600, 0x2612, 0x2614, 0x2617, 0x261a, 0x261a, - 0x2640, 0x267e, 0x2680, 0x268b, 0x26c0, 0x26c0, 0x26c4, 0x26ce, - 0x26d0, 0x26d8, 0x26df, 0x26e6, 0x26e8, 0x26e9, 0x26ec, 0x26ec, - 0x26f0, 0x26f7, 0x26ff, 0x26ff, 0x2740, 0x2743, 0x2748, 0x2749, - 0x2750, 0x2756, 0x2760, 0x2760, 0x300c, 0x300e, 0x301c, 0x301d, - 0x302a, 0x302a, 0x302c, 0x302d, 0x3030, 0x3031, 0x3034, 0x3036, - 0x303c, 0x303c, 0x305e, 0x305f, + 0x22ff, 0x22ff, 0x2340, 0x2343, 0x2440, 0x2440, 0x2444, 0x2444, + 0x2448, 0x244d, 0x2468, 0x2469, 0x246c, 0x246d, 0x2470, 0x2470, + 0x2472, 0x2472, 0x2474, 0x2475, 0x2479, 0x247a, 0x24c0, 0x24d3, + 0x24e4, 0x24ef, 0x2500, 0x2509, 0x250c, 0x250c, 0x250e, 0x250e, + 0x2510, 0x2511, 0x2514, 0x2515, 0x25e4, 0x25e4, 0x25ea, 0x25ea, + 0x25ec, 0x25ed, 0x25f0, 0x25f0, 0x2600, 0x2612, 0x2614, 0x2617, + 0x261a, 0x261a, 0x2640, 0x267e, 0x2680, 0x268b, 0x26c0, 0x26c0, + 0x26c4, 0x26ce, 0x26d0, 0x26d8, 0x26df, 0x26e6, 0x26e8, 0x26e9, + 0x26ec, 0x26ec, 0x26f0, 0x26f7, 0x26ff, 0x26ff, 0x2740, 0x2743, + 0x300c, 0x300e, 0x301c, 0x301d, 0x302a, 0x302a, 0x302c, 0x302d, + 0x3030, 0x3031, 0x3034, 0x3036, 0x303c, 0x303c, 0x305e, 0x305f, ~0 /* sentinel */ }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c index 06be7cf7ce50537dbebd69babb45e98712382f65..79bafea66354288810bdb9fd9cd9e12e5e2d0bb4 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c @@ -310,7 +310,7 @@ static void dpu_hw_ctl_setup_blendstage(struct dpu_hw_ctl *ctx, u32 mixercfg = 0, mixercfg_ext = 0, mix, ext; u32 mixercfg_ext2 = 0, mixercfg_ext3 = 0; int i, j; - u8 stages; + int stages; int pipes_per_stage; stages = _mixer_stages(ctx->mixer_hw_caps, ctx->mixer_count, lm); diff --git a/drivers/gpu/drm/msm/disp/mdp4/mdp4_dsi_encoder.c b/drivers/gpu/drm/msm/disp/mdp4/mdp4_dsi_encoder.c index 6a1ebdace391d947e54b856e42c020e1f105d98f..6253717d2e0c3c908e0e7c4979b454e49172e9cc 100644 --- a/drivers/gpu/drm/msm/disp/mdp4/mdp4_dsi_encoder.c +++ b/drivers/gpu/drm/msm/disp/mdp4/mdp4_dsi_encoder.c @@ -139,7 +139,7 @@ static void mdp4_dsi_encoder_enable(struct drm_encoder *encoder) if (mdp4_dsi_encoder->enabled) return; - mdp4_crtc_set_config(encoder->crtc, + mdp4_crtc_set_config(encoder->crtc, MDP4_DMA_CONFIG_PACK_ALIGN_MSB | MDP4_DMA_CONFIG_DEFLKR_EN | MDP4_DMA_CONFIG_DITHER_EN | diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_cfg.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_cfg.c index 824067d2d4277d36699b1f15c6d58f74a97fe23f..42f0ecb0cf35fd406a4115355c81aac93f7310af 100644 --- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_cfg.c +++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_cfg.c @@ -635,7 +635,7 @@ struct mdp5_cfg_handler *mdp5_cfg_init(struct mdp5_kms *mdp5_kms, if (cfg_handler) mdp5_cfg_destroy(cfg_handler); - return NULL; + return ERR_PTR(ret); } static struct mdp5_cfg_platform *mdp5_get_config(struct platform_device *dev) diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c index b1da9ce54379099f3fc33e4f48a49cd007b74fa1..aa28a43ff842cdc2db0ae738daf295e0712666ad 100644 --- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c @@ -1118,8 +1118,8 @@ static void mdp5_crtc_wait_for_pp_done(struct drm_crtc *crtc) ret = wait_for_completion_timeout(&mdp5_crtc->pp_completion, msecs_to_jiffies(50)); if (ret == 0) - dev_warn(dev->dev, "pp done time out, lm=%d\n", - mdp5_cstate->pipeline.mixer->lm); + dev_warn_ratelimited(dev->dev, "pp done time out, lm=%d\n", + mdp5_cstate->pipeline.mixer->lm); } static void mdp5_crtc_wait_for_flush_done(struct drm_crtc *crtc) diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index cc4ea5502d6c3ac57a32728aea7857d823e630bf..3b78bca0bb4d4a1c9d534d9abccfce487c621bee 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -34,6 +34,8 @@ #include "dsi_cfg.h" #include "msm_kms.h" +#define DSI_RESET_TOGGLE_DELAY_MS 20 + static int dsi_get_version(const void __iomem *base, u32 *major, u32 *minor) { u32 ver; @@ -994,7 +996,7 @@ static void dsi_sw_reset(struct msm_dsi_host *msm_host) wmb(); /* clocks need to be enabled before reset */ dsi_write(msm_host, REG_DSI_RESET, 1); - wmb(); /* make sure reset happen */ + msleep(DSI_RESET_TOGGLE_DELAY_MS); /* make sure reset happen */ dsi_write(msm_host, REG_DSI_RESET, 0); } @@ -1402,7 +1404,7 @@ static void dsi_sw_reset_restore(struct msm_dsi_host *msm_host) /* dsi controller can only be reset while clocks are running */ dsi_write(msm_host, REG_DSI_RESET, 1); - wmb(); /* make sure reset happen */ + msleep(DSI_RESET_TOGGLE_DELAY_MS); /* make sure reset happen */ dsi_write(msm_host, REG_DSI_RESET, 0); wmb(); /* controller out of reset */ dsi_write(msm_host, REG_DSI_CTRL, data0); diff --git a/drivers/gpu/drm/msm/dsi/dsi_manager.c b/drivers/gpu/drm/msm/dsi/dsi_manager.c index 5224010d90e4ac0cb78a66b1e785586ff514087f..b01762a7778ae0a4d4ba296cb601e86aa8b4819f 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_manager.c +++ b/drivers/gpu/drm/msm/dsi/dsi_manager.c @@ -328,7 +328,7 @@ static int dsi_mgr_connector_get_modes(struct drm_connector *connector) return num; } -static int dsi_mgr_connector_mode_valid(struct drm_connector *connector, +static enum drm_mode_status dsi_mgr_connector_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { int id = dsi_mgr_connector_get_id(connector); @@ -471,6 +471,7 @@ static void dsi_mgr_bridge_post_disable(struct drm_bridge *bridge) struct msm_dsi *msm_dsi1 = dsi_mgr_get_dsi(DSI_1); struct mipi_dsi_host *host = msm_dsi->host; struct drm_panel *panel = msm_dsi->panel; + struct msm_dsi_pll *src_pll; bool is_dual_dsi = IS_DUAL_DSI(); int ret; @@ -511,6 +512,10 @@ static void dsi_mgr_bridge_post_disable(struct drm_bridge *bridge) id, ret); } + /* Save PLL status if it is a clock source */ + src_pll = msm_dsi_phy_get_pll(msm_dsi->phy); + msm_dsi_pll_save_state(src_pll); + ret = msm_dsi_host_power_off(host); if (ret) pr_err("%s: host %d power off failed,%d\n", __func__, id, ret); diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c index 9a9fa0c75a131083f32c57f5cd89a0e02c7b87d1..c630871de7c5bb1c31072706556481cfe3f2e104 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c @@ -726,10 +726,6 @@ void msm_dsi_phy_disable(struct msm_dsi_phy *phy) if (!phy || !phy->cfg->ops.disable) return; - /* Save PLL status if it is a clock source */ - if (phy->usecase != MSM_DSI_PHY_SLAVE) - msm_dsi_pll_save_state(phy->pll); - phy->cfg->ops.disable(phy); dsi_phy_regulator_disable(phy); diff --git a/drivers/gpu/drm/msm/dsi/pll/dsi_pll_10nm.c b/drivers/gpu/drm/msm/dsi/pll/dsi_pll_10nm.c index 31205625c7346220dab656e51a0730c16013c124..21a69b046625aa4711d28c9c70fb3d4b553370d1 100644 --- a/drivers/gpu/drm/msm/dsi/pll/dsi_pll_10nm.c +++ b/drivers/gpu/drm/msm/dsi/pll/dsi_pll_10nm.c @@ -406,6 +406,12 @@ static int dsi_pll_10nm_vco_prepare(struct clk_hw *hw) if (pll_10nm->slave) dsi_pll_enable_pll_bias(pll_10nm->slave); + rc = dsi_pll_10nm_vco_set_rate(hw,pll_10nm->vco_current_rate, 0); + if (rc) { + pr_err("vco_set_rate failed, rc=%d\n", rc); + return rc; + } + /* Start PLL */ pll_write(pll_10nm->phy_cmn_mmio + REG_DSI_10nm_PHY_CMN_PLL_CNTRL, 0x01); diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 610139beda18eb634790b4c964a69b34e4511a8e..e4afdb910a27cb9a0976e1b93a5852832d832d84 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -494,6 +494,14 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv) if (ret) goto err_msm_uninit; + if (!dev->dma_parms) { + dev->dma_parms = devm_kzalloc(dev, sizeof(*dev->dma_parms), + GFP_KERNEL); + if (!dev->dma_parms) + return -ENOMEM; + } + dma_set_max_seg_size(dev, DMA_BIT_MASK(32)); + msm_gem_shrinker_init(ddev); switch (get_mdp_ver(pdev)) { diff --git a/drivers/gpu/drm/nouveau/dispnv50/wndw.c b/drivers/gpu/drm/nouveau/dispnv50/wndw.c index b3db4553098d5736aeb09b27e5eda55d5e0bda61..d343ae66c64fed338111a119a36bda3bc35108e2 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/wndw.c +++ b/drivers/gpu/drm/nouveau/dispnv50/wndw.c @@ -405,6 +405,8 @@ nv50_wndw_atomic_check(struct drm_plane *plane, struct drm_plane_state *state) asyw->clr.ntfy = armw->ntfy.handle != 0; asyw->clr.sema = armw->sema.handle != 0; asyw->clr.xlut = armw->xlut.handle != 0; + if (asyw->clr.xlut && asyw->visible) + asyw->set.xlut = asyw->xlut.handle != 0; if (wndw->func->image_clr) asyw->clr.image = armw->image.handle[0] != 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_abi16.c b/drivers/gpu/drm/nouveau/nouveau_abi16.c index e67a471331b514b75321c4bf8a0fff1be7057113..6ec745873bc5530faf50e2b718fcd0f2d18fc375 100644 --- a/drivers/gpu/drm/nouveau/nouveau_abi16.c +++ b/drivers/gpu/drm/nouveau/nouveau_abi16.c @@ -214,6 +214,7 @@ nouveau_abi16_ioctl_getparam(ABI16_IOCTL_ARGS) WARN_ON(1); break; } + break; case NOUVEAU_GETPARAM_FB_SIZE: getparam->value = drm->gem.vram_available; break; diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c index 412d49bc6e56073fd9ebc8de2ca7fef73c03792d..ba3883aed45673caed5b5b2a854dfabef37c3c20 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.c +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c @@ -157,7 +157,7 @@ nouveau_fence_wait_uevent_handler(struct nvif_notify *notify) fence = list_entry(fctx->pending.next, typeof(*fence), head); chan = rcu_dereference_protected(fence->channel, lockdep_is_held(&fctx->lock)); - if (nouveau_fence_update(fence->channel, fctx)) + if (nouveau_fence_update(chan, fctx)) ret = NVIF_NOTIFY_DROP; } spin_unlock_irqrestore(&fctx->lock, flags); diff --git a/drivers/gpu/drm/nouveau/nouveau_ttm.c b/drivers/gpu/drm/nouveau/nouveau_ttm.c index e4b977cc8452895f6155212869b4c129640092bb..37715a2a2f3f9cd11f0ee93838527a182caf348b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_ttm.c +++ b/drivers/gpu/drm/nouveau/nouveau_ttm.c @@ -63,14 +63,12 @@ nouveau_vram_manager_new(struct ttm_mem_type_manager *man, { struct nouveau_bo *nvbo = nouveau_bo(bo); struct nouveau_drm *drm = nouveau_bdev(bo->bdev); - struct nouveau_mem *mem; int ret; if (drm->client.device.info.ram_size == 0) return -ENOMEM; ret = nouveau_mem_new(&drm->master, nvbo->kind, nvbo->comp, reg); - mem = nouveau_mem(reg); if (ret) return ret; @@ -103,11 +101,9 @@ nouveau_gart_manager_new(struct ttm_mem_type_manager *man, { struct nouveau_bo *nvbo = nouveau_bo(bo); struct nouveau_drm *drm = nouveau_bdev(bo->bdev); - struct nouveau_mem *mem; int ret; ret = nouveau_mem_new(&drm->master, nvbo->kind, nvbo->comp, reg); - mem = nouveau_mem(reg); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/nvkm/core/memory.c b/drivers/gpu/drm/nouveau/nvkm/core/memory.c index e85a08ecd9da578d51fd778f171ea07e55bf9bb1..4cc186262d3441fa051b8be0514ee453eb568d7b 100644 --- a/drivers/gpu/drm/nouveau/nvkm/core/memory.c +++ b/drivers/gpu/drm/nouveau/nvkm/core/memory.c @@ -91,8 +91,8 @@ nvkm_memory_tags_get(struct nvkm_memory *memory, struct nvkm_device *device, } refcount_set(&tags->refcount, 1); + *ptags = memory->tags = tags; mutex_unlock(&fb->subdev.mutex); - *ptags = tags; return 0; } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/channv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/channv50.c index bcf32d92ee5a9ec28de558715566e5eccc89432a..50e3539f33d22daa631e90a0bbcc8236f9899ff5 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/channv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/channv50.c @@ -74,6 +74,8 @@ nv50_disp_chan_mthd(struct nv50_disp_chan *chan, int debug) if (debug > subdev->debug) return; + if (!mthd) + return; for (i = 0; (list = mthd->data[i].mthd) != NULL; i++) { u32 base = chan->head * mthd->addr; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk20a.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk20a.c index 500cb08dd608011005f178b4b469987d336242ca..b57ab5cea9a105106b29cdf939cf0e752dc21f20 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk20a.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gk20a.c @@ -143,23 +143,24 @@ gk20a_gr_av_to_method(struct gf100_gr *gr, const char *fw_name, nent = (fuc.size / sizeof(struct gk20a_fw_av)); - pack = vzalloc((sizeof(*pack) * max_classes) + - (sizeof(*init) * (nent + 1))); + pack = vzalloc((sizeof(*pack) * (max_classes + 1)) + + (sizeof(*init) * (nent + max_classes + 1))); if (!pack) { ret = -ENOMEM; goto end; } - init = (void *)(pack + max_classes); + init = (void *)(pack + max_classes + 1); - for (i = 0; i < nent; i++) { - struct gf100_gr_init *ent = &init[i]; + for (i = 0; i < nent; i++, init++) { struct gk20a_fw_av *av = &((struct gk20a_fw_av *)fuc.data)[i]; u32 class = av->addr & 0xffff; u32 addr = (av->addr & 0xffff0000) >> 14; if (prevclass != class) { - pack[classidx].init = ent; + if (prevclass) /* Add terminator to the method list. */ + init++; + pack[classidx].init = init; pack[classidx].type = class; prevclass = class; if (++classidx >= max_classes) { @@ -169,10 +170,10 @@ gk20a_gr_av_to_method(struct gf100_gr *gr, const char *fw_name, } } - ent->addr = addr; - ent->data = av->data; - ent->count = 1; - ent->pitch = 1; + init->addr = addr; + init->data = av->data; + init->count = 1; + init->pitch = 1; } *ppack = pack; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c index a3ba7f50198be90ed7357a7c26f3ddb01a3c3955..a3dcb09a40ee374747901bfdd07c33a6859ad9b1 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c @@ -94,6 +94,8 @@ gf100_bar_oneinit_bar(struct gf100_bar *bar, struct gf100_barN *bar_vm, return ret; bar_len = device->func->resource_size(device, bar_nr); + if (!bar_len) + return -ENOMEM; if (bar_nr == 3 && bar->bar2_halve) bar_len >>= 1; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bar/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/nv50.c index 38c9c086754b689698950e44b801b38ed7fc671d..f23a0ccc2becca6d733089941d4850d3b410d567 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/bar/nv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/nv50.c @@ -174,6 +174,8 @@ nv50_bar_oneinit(struct nvkm_bar *base) ret = nvkm_vmm_new(device, start, limit-- - start, NULL, 0, &bar1_lock, "bar1", &bar->bar1_vmm); + if (ret) + return ret; atomic_inc(&bar->bar1_vmm->engref[NVKM_SUBDEV_BAR]); bar->bar1_vmm->debug = bar->base.subdev.debug; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fault/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fault/base.c index 16ad91c91a7beca11a8febfbb91a54f3b34ac008..f18ce6ff5b7ed54627669fffe4b77e68e628b1da 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/fault/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fault/base.c @@ -150,6 +150,7 @@ nvkm_fault_dtor(struct nvkm_subdev *subdev) struct nvkm_fault *fault = nvkm_fault(subdev); int i; + nvkm_notify_fini(&fault->nrpfb); nvkm_event_fini(&fault->event); for (i = 0; i < fault->buffer_nr; i++) { diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr3.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr3.c index 60ece0a8a2e1bc1e5acd5753fe3d37f08323ed8e..1d2d6bae73cd1f57aef57a31ed6ce80dfc57e3f0 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr3.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gddr3.c @@ -87,7 +87,7 @@ nvkm_gddr3_calc(struct nvkm_ram *ram) WR = (ram->next->bios.timing[2] & 0x007f0000) >> 16; /* XXX: Get these values from the VBIOS instead */ DLL = !(ram->mr[1] & 0x1); - RON = !(ram->mr[1] & 0x300) >> 8; + RON = !((ram->mr[1] & 0x300) >> 8); break; default: return -ENOSYS; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c index 7459def78d504f006a2f7f0b625ba3372238b7ec..5f8b8b399b97bc5d1223c77d3370867dd1575718 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c @@ -1423,7 +1423,7 @@ nvkm_vmm_get(struct nvkm_vmm *vmm, u8 page, u64 size, struct nvkm_vma **pvma) void nvkm_vmm_part(struct nvkm_vmm *vmm, struct nvkm_memory *inst) { - if (inst && vmm->func->part) { + if (inst && vmm && vmm->func->part) { mutex_lock(&vmm->mutex); vmm->func->part(vmm, inst); mutex_unlock(&vmm->mutex); diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/memx.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/memx.c index 11b28b086a062f7ba2cea541fb391095ea2cec43..7b052879af7287a33c7ce0585f9c46e5f9d99403 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/memx.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/memx.c @@ -88,10 +88,10 @@ nvkm_memx_fini(struct nvkm_memx **pmemx, bool exec) if (exec) { nvkm_pmu_send(pmu, reply, PROC_MEMX, MEMX_MSG_EXEC, memx->base, finish); + nvkm_debug(subdev, "Exec took %uns, PMU_IN %08x\n", + reply[0], reply[1]); } - nvkm_debug(subdev, "Exec took %uns, PMU_IN %08x\n", - reply[0], reply[1]); kfree(memx); return 0; } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gm20b.c b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gm20b.c index df8b919dcf09bc4b6470afa898fed69a1221c531..ace6fefba428007841adfd72c1082ddba697c907 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gm20b.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gm20b.c @@ -108,6 +108,7 @@ gm20b_secboot_new(struct nvkm_device *device, int index, struct gm200_secboot *gsb; struct nvkm_acr *acr; + *psb = NULL; acr = acr_r352_new(BIT(NVKM_SECBOOT_FALCON_FECS) | BIT(NVKM_SECBOOT_FALCON_PMU)); if (IS_ERR(acr)) @@ -116,10 +117,8 @@ gm20b_secboot_new(struct nvkm_device *device, int index, acr->optional_falcons = BIT(NVKM_SECBOOT_FALCON_PMU); gsb = kzalloc(sizeof(*gsb), GFP_KERNEL); - if (!gsb) { - psb = NULL; + if (!gsb) return -ENOMEM; - } *psb = &gsb->base; ret = nvkm_secboot_ctor(&gm20b_secboot, acr, device, index, &gsb->base); diff --git a/drivers/gpu/drm/panel/panel-lvds.c b/drivers/gpu/drm/panel/panel-lvds.c index 8a1687887ae912f310b7214de6ef779d905179ae..bd704a36c5d0e0d2bf4333da3316d624baa2c8aa 100644 --- a/drivers/gpu/drm/panel/panel-lvds.c +++ b/drivers/gpu/drm/panel/panel-lvds.c @@ -199,7 +199,6 @@ static int panel_lvds_parse_dt(struct panel_lvds *lvds) static int panel_lvds_probe(struct platform_device *pdev) { struct panel_lvds *lvds; - struct device_node *np; int ret; lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL); @@ -245,14 +244,9 @@ static int panel_lvds_probe(struct platform_device *pdev) return ret; } - np = of_parse_phandle(lvds->dev->of_node, "backlight", 0); - if (np) { - lvds->backlight = of_find_backlight_by_node(np); - of_node_put(np); - - if (!lvds->backlight) - return -EPROBE_DEFER; - } + lvds->backlight = devm_of_find_backlight(lvds->dev); + if (IS_ERR(lvds->backlight)) + return PTR_ERR(lvds->backlight); /* * TODO: Handle all power supplies specified in the DT node in a generic @@ -268,14 +262,10 @@ static int panel_lvds_probe(struct platform_device *pdev) ret = drm_panel_add(&lvds->panel); if (ret < 0) - goto error; + return ret; dev_set_drvdata(lvds->dev, lvds); return 0; - -error: - put_device(&lvds->backlight->dev); - return ret; } static int panel_lvds_remove(struct platform_device *pdev) @@ -286,9 +276,6 @@ static int panel_lvds_remove(struct platform_device *pdev) panel_lvds_disable(&lvds->panel); - if (lvds->backlight) - put_device(&lvds->backlight->dev); - return 0; } diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index ebce4601a3056a0e961f074067a2a762ae53bcda..827d551962d98e0a125fa96246e70cbe98be0c80 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -6965,8 +6965,8 @@ static int cik_irq_init(struct radeon_device *rdev) } /* setup interrupt control */ - /* XXX this should actually be a bus address, not an MC address. same on older asics */ - WREG32(INTERRUPT_CNTL2, rdev->ih.gpu_addr >> 8); + /* set dummy read address to dummy page address */ + WREG32(INTERRUPT_CNTL2, rdev->dummy_page.addr >> 8); interrupt_cntl = RREG32(INTERRUPT_CNTL); /* IH_DUMMY_RD_OVERRIDE=0 - dummy read disabled with msi, enabled without msi * IH_DUMMY_RD_OVERRIDE=1 - dummy read controlled by IH_DUMMY_RD_EN diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index e06e2d8feab397822361ba18a3e4b420cc2f0c4d..a724bb87cfad7f1dd043f5b51ecc61dc415d746b 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -3690,8 +3690,8 @@ int r600_irq_init(struct radeon_device *rdev) } /* setup interrupt control */ - /* set dummy read address to ring address */ - WREG32(INTERRUPT_CNTL2, rdev->ih.gpu_addr >> 8); + /* set dummy read address to dummy page address */ + WREG32(INTERRUPT_CNTL2, rdev->dummy_page.addr >> 8); interrupt_cntl = RREG32(INTERRUPT_CNTL); /* IH_DUMMY_RD_OVERRIDE=0 - dummy read disabled with msi, enabled without msi * IH_DUMMY_RD_OVERRIDE=1 - dummy read controlled by IH_DUMMY_RD_EN diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index d8e2d7b3b83658c9c5239bf142f02d1295a6bfe6..7d1e14f0140a2aef0b000afe2c42916ea5bc5ded 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -121,6 +121,8 @@ static void dce5_crtc_load_lut(struct drm_crtc *crtc) DRM_DEBUG_KMS("%d\n", radeon_crtc->crtc_id); + msleep(10); + WREG32(NI_INPUT_CSC_CONTROL + radeon_crtc->crtc_offset, (NI_INPUT_CSC_GRPH_MODE(NI_INPUT_CSC_BYPASS) | NI_INPUT_CSC_OVL_MODE(NI_INPUT_CSC_BYPASS))); diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c index 85c604d2923584370e59c88630bed491c2453b48..639f0698f961cdb9903cf90ab18bffc6cb9eea8e 100644 --- a/drivers/gpu/drm/radeon/si.c +++ b/drivers/gpu/drm/radeon/si.c @@ -5993,8 +5993,8 @@ static int si_irq_init(struct radeon_device *rdev) } /* setup interrupt control */ - /* set dummy read address to ring address */ - WREG32(INTERRUPT_CNTL2, rdev->ih.gpu_addr >> 8); + /* set dummy read address to dummy page address */ + WREG32(INTERRUPT_CNTL2, rdev->dummy_page.addr >> 8); interrupt_cntl = RREG32(INTERRUPT_CNTL); /* IH_DUMMY_RD_OVERRIDE=0 - dummy read disabled with msi, enabled without msi * IH_DUMMY_RD_OVERRIDE=1 - dummy read controlled by IH_DUMMY_RD_EN diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 15dc9caa128baeb7ed8c1f3ca133cb160cb739c9..212e5e11e4b7328429245b419ae5e3af270f0d35 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -837,7 +837,7 @@ static int rcar_du_crtc_set_crc_source(struct drm_crtc *crtc, drm_modeset_drop_locks(&ctx); drm_modeset_acquire_fini(&ctx); - return 0; + return ret; } static const struct drm_crtc_funcs crtc_funcs_gen2 = { diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index 0386b454e221847b26b6c33977245a812983fbcd..6a9578159c2b507c6c606840c44a3f7d4cc69cb6 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -544,7 +544,7 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) * Initialize vertical blanking interrupts handling. Start with vblank * disabled for all CRTCs. */ - ret = drm_vblank_init(dev, (1 << rcdu->num_crtcs) - 1); + ret = drm_vblank_init(dev, rcdu->num_crtcs); if (ret < 0) return ret; diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.c b/drivers/gpu/drm/rcar-du/rcar_lvds.c index 4c39de3f4f0f374b3d264cd2a18a8f69a569a689..b6dc91cdff68e1840b89fb5444fbe316e7562df7 100644 --- a/drivers/gpu/drm/rcar-du/rcar_lvds.c +++ b/drivers/gpu/drm/rcar-du/rcar_lvds.c @@ -59,11 +59,11 @@ struct rcar_lvds { enum rcar_lvds_mode mode; }; -#define bridge_to_rcar_lvds(bridge) \ - container_of(bridge, struct rcar_lvds, bridge) +#define bridge_to_rcar_lvds(b) \ + container_of(b, struct rcar_lvds, bridge) -#define connector_to_rcar_lvds(connector) \ - container_of(connector, struct rcar_lvds, connector) +#define connector_to_rcar_lvds(c) \ + container_of(c, struct rcar_lvds, connector) static void rcar_lvds_write(struct rcar_lvds *lvds, u32 reg, u32 data) { diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.c b/drivers/gpu/drm/shmobile/shmob_drm_drv.c index 592572554eb0ea1c102432af051bd72356e614b8..58d8a98c749b43e1f7ef554d4aeefae3459c4c51 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_drv.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_drv.c @@ -233,8 +233,8 @@ static int shmob_drm_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); sdev->mmio = devm_ioremap_resource(&pdev->dev, res); - if (sdev->mmio == NULL) - return -ENOMEM; + if (IS_ERR(sdev->mmio)) + return PTR_ERR(sdev->mmio); ret = shmob_drm_setup_clocks(sdev, pdata->clk_source); if (ret < 0) diff --git a/drivers/gpu/drm/sti/sti_hda.c b/drivers/gpu/drm/sti/sti_hda.c index 49438337f70dc5e66ac2c12a5b41737dc17fd491..19b9b5ed129700730e5c77ea5c37e9edbb0db5e0 100644 --- a/drivers/gpu/drm/sti/sti_hda.c +++ b/drivers/gpu/drm/sti/sti_hda.c @@ -721,7 +721,6 @@ static int sti_hda_bind(struct device *dev, struct device *master, void *data) return 0; err_sysfs: - drm_bridge_remove(bridge); return -EINVAL; } diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c index 34cdc4644435046711d388e41cd7ba71660a7fe1..ccf718404a1c23e557dd6ff097d779f3bff9729c 100644 --- a/drivers/gpu/drm/sti/sti_hdmi.c +++ b/drivers/gpu/drm/sti/sti_hdmi.c @@ -1315,7 +1315,6 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data) return 0; err_sysfs: - drm_bridge_remove(bridge); hdmi->drm_connector = NULL; return -EINVAL; } diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c index 3ecffa52c81466f6998575cf87b830a353478c72..a74adec6c5dcd3ee0590a429ca3181b24547b048 100644 --- a/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c @@ -52,7 +52,7 @@ static unsigned long sun4i_tmds_calc_divider(unsigned long rate, (rate - tmp_rate) < (rate - best_rate)) { best_rate = tmp_rate; best_m = m; - is_double = d; + is_double = (d == 2) ? true : false; } } } diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c index fda1ae12069a7fae1aff579bbc90af283a992eed..113c032a27206d2fa0294b03a501b8c41f69f348 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c @@ -423,7 +423,7 @@ static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon, WARN_ON(!tcon->quirks->has_channel_0); - tcon->dclk_min_div = 1; + tcon->dclk_min_div = tcon->quirks->dclk_min_div; tcon->dclk_max_div = 127; sun4i_tcon0_mode_set_common(tcon, mode); @@ -1249,12 +1249,14 @@ static int sun6i_tcon_set_mux(struct sun4i_tcon *tcon, static const struct sun4i_tcon_quirks sun4i_a10_quirks = { .has_channel_0 = true, .has_channel_1 = true, + .dclk_min_div = 4, .set_mux = sun4i_a10_tcon_set_mux, }; static const struct sun4i_tcon_quirks sun5i_a13_quirks = { .has_channel_0 = true, .has_channel_1 = true, + .dclk_min_div = 4, .set_mux = sun5i_a13_tcon_set_mux, }; @@ -1263,6 +1265,7 @@ static const struct sun4i_tcon_quirks sun6i_a31_quirks = { .has_channel_1 = true, .has_lvds_alt = true, .needs_de_be_mux = true, + .dclk_min_div = 1, .set_mux = sun6i_tcon_set_mux, }; @@ -1270,11 +1273,13 @@ static const struct sun4i_tcon_quirks sun6i_a31s_quirks = { .has_channel_0 = true, .has_channel_1 = true, .needs_de_be_mux = true, + .dclk_min_div = 1, }; static const struct sun4i_tcon_quirks sun7i_a20_quirks = { .has_channel_0 = true, .has_channel_1 = true, + .dclk_min_div = 4, /* Same display pipeline structure as A10 */ .set_mux = sun4i_a10_tcon_set_mux, }; @@ -1282,11 +1287,13 @@ static const struct sun4i_tcon_quirks sun7i_a20_quirks = { static const struct sun4i_tcon_quirks sun8i_a33_quirks = { .has_channel_0 = true, .has_lvds_alt = true, + .dclk_min_div = 1, }; static const struct sun4i_tcon_quirks sun8i_a83t_lcd_quirks = { .supports_lvds = true, .has_channel_0 = true, + .dclk_min_div = 1, }; static const struct sun4i_tcon_quirks sun8i_a83t_tv_quirks = { @@ -1295,11 +1302,13 @@ static const struct sun4i_tcon_quirks sun8i_a83t_tv_quirks = { static const struct sun4i_tcon_quirks sun8i_v3s_quirks = { .has_channel_0 = true, + .dclk_min_div = 1, }; static const struct sun4i_tcon_quirks sun9i_a80_tcon_lcd_quirks = { - .has_channel_0 = true, - .needs_edp_reset = true, + .has_channel_0 = true, + .needs_edp_reset = true, + .dclk_min_div = 1, }; static const struct sun4i_tcon_quirks sun9i_a80_tcon_tv_quirks = { diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.h b/drivers/gpu/drm/sun4i/sun4i_tcon.h index f6a071cd5a6fd969140b975cb6e570d74f6b5ad1..e05db6b19453bc83d631d8b1b5a3e2c209eabe54 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.h +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.h @@ -224,6 +224,7 @@ struct sun4i_tcon_quirks { bool needs_de_be_mux; /* sun6i needs mux to select backend */ bool needs_edp_reset; /* a80 edp reset needed for tcon0 access */ bool supports_lvds; /* Does the TCON support an LVDS output? */ + u8 dclk_min_div; /* minimum divider for TCON0 DCLK */ /* callback to handle tcon muxing options */ int (*set_mux)(struct sun4i_tcon *, const struct drm_encoder *); diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c index 97a0573cc51459aa81a9dff31534b35b7d581239..79eb11cd185d1eb1b3bdb60478b46fc4811c64d9 100644 --- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c +++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c @@ -357,8 +357,7 @@ static void sun6i_dsi_inst_init(struct sun6i_dsi *dsi, static u16 sun6i_dsi_get_video_start_delay(struct sun6i_dsi *dsi, struct drm_display_mode *mode) { - u16 start = clamp(mode->vtotal - mode->vdisplay - 10, 8, 100); - u16 delay = mode->vtotal - (mode->vsync_end - mode->vdisplay) + start; + u16 delay = mode->vtotal - (mode->vsync_end - mode->vdisplay) + 1; if (delay > mode->vtotal) delay = delay % mode->vtotal; diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c index cb65b0ed53fd0e520a77327e185d0011e619b915..71a798e5d55917b95b3cd5ffeca2aa2512e13f16 100644 --- a/drivers/gpu/drm/sun4i/sun8i_mixer.c +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c @@ -110,48 +110,104 @@ static const struct de2_fmt_info de2_formats[] = { .rgb = true, .csc = SUN8I_CSC_MODE_OFF, }, + { + /* for DE2 VI layer which ignores alpha */ + .drm_fmt = DRM_FORMAT_XRGB4444, + .de2_fmt = SUN8I_MIXER_FBFMT_ARGB4444, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, { .drm_fmt = DRM_FORMAT_ABGR4444, .de2_fmt = SUN8I_MIXER_FBFMT_ABGR4444, .rgb = true, .csc = SUN8I_CSC_MODE_OFF, }, + { + /* for DE2 VI layer which ignores alpha */ + .drm_fmt = DRM_FORMAT_XBGR4444, + .de2_fmt = SUN8I_MIXER_FBFMT_ABGR4444, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, { .drm_fmt = DRM_FORMAT_RGBA4444, .de2_fmt = SUN8I_MIXER_FBFMT_RGBA4444, .rgb = true, .csc = SUN8I_CSC_MODE_OFF, }, + { + /* for DE2 VI layer which ignores alpha */ + .drm_fmt = DRM_FORMAT_RGBX4444, + .de2_fmt = SUN8I_MIXER_FBFMT_RGBA4444, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, { .drm_fmt = DRM_FORMAT_BGRA4444, .de2_fmt = SUN8I_MIXER_FBFMT_BGRA4444, .rgb = true, .csc = SUN8I_CSC_MODE_OFF, }, + { + /* for DE2 VI layer which ignores alpha */ + .drm_fmt = DRM_FORMAT_BGRX4444, + .de2_fmt = SUN8I_MIXER_FBFMT_BGRA4444, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, { .drm_fmt = DRM_FORMAT_ARGB1555, .de2_fmt = SUN8I_MIXER_FBFMT_ARGB1555, .rgb = true, .csc = SUN8I_CSC_MODE_OFF, }, + { + /* for DE2 VI layer which ignores alpha */ + .drm_fmt = DRM_FORMAT_XRGB1555, + .de2_fmt = SUN8I_MIXER_FBFMT_ARGB1555, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, { .drm_fmt = DRM_FORMAT_ABGR1555, .de2_fmt = SUN8I_MIXER_FBFMT_ABGR1555, .rgb = true, .csc = SUN8I_CSC_MODE_OFF, }, + { + /* for DE2 VI layer which ignores alpha */ + .drm_fmt = DRM_FORMAT_XBGR1555, + .de2_fmt = SUN8I_MIXER_FBFMT_ABGR1555, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, { .drm_fmt = DRM_FORMAT_RGBA5551, .de2_fmt = SUN8I_MIXER_FBFMT_RGBA5551, .rgb = true, .csc = SUN8I_CSC_MODE_OFF, }, + { + /* for DE2 VI layer which ignores alpha */ + .drm_fmt = DRM_FORMAT_RGBX5551, + .de2_fmt = SUN8I_MIXER_FBFMT_RGBA5551, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, { .drm_fmt = DRM_FORMAT_BGRA5551, .de2_fmt = SUN8I_MIXER_FBFMT_BGRA5551, .rgb = true, .csc = SUN8I_CSC_MODE_OFF, }, + { + /* for DE2 VI layer which ignores alpha */ + .drm_fmt = DRM_FORMAT_BGRX5551, + .de2_fmt = SUN8I_MIXER_FBFMT_BGRA5551, + .rgb = true, + .csc = SUN8I_CSC_MODE_OFF, + }, { .drm_fmt = DRM_FORMAT_UYVY, .de2_fmt = SUN8I_MIXER_FBFMT_UYVY, @@ -200,12 +256,6 @@ static const struct de2_fmt_info de2_formats[] = { .rgb = false, .csc = SUN8I_CSC_MODE_YUV2RGB, }, - { - .drm_fmt = DRM_FORMAT_YUV444, - .de2_fmt = SUN8I_MIXER_FBFMT_RGB888, - .rgb = true, - .csc = SUN8I_CSC_MODE_YUV2RGB, - }, { .drm_fmt = DRM_FORMAT_YUV422, .de2_fmt = SUN8I_MIXER_FBFMT_YUV422, @@ -224,12 +274,6 @@ static const struct de2_fmt_info de2_formats[] = { .rgb = false, .csc = SUN8I_CSC_MODE_YUV2RGB, }, - { - .drm_fmt = DRM_FORMAT_YVU444, - .de2_fmt = SUN8I_MIXER_FBFMT_RGB888, - .rgb = true, - .csc = SUN8I_CSC_MODE_YVU2RGB, - }, { .drm_fmt = DRM_FORMAT_YVU422, .de2_fmt = SUN8I_MIXER_FBFMT_YUV422, diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c index f4fe97813f943f61b061c91604ad542b9446c4f8..15fc6363cc43c2eb0ce05196641b4e7d8a88a4e4 100644 --- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c +++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c @@ -330,26 +330,26 @@ static const struct drm_plane_funcs sun8i_vi_layer_funcs = { }; /* - * While all RGB formats are supported, VI planes don't support - * alpha blending, so there is no point having formats with alpha - * channel if their opaque analog exist. + * While DE2 VI layer supports same RGB formats as UI layer, alpha + * channel is ignored. This structure lists all unique variants + * where alpha channel is replaced with "don't care" (X) channel. */ static const u32 sun8i_vi_layer_formats[] = { - DRM_FORMAT_ABGR1555, - DRM_FORMAT_ABGR4444, - DRM_FORMAT_ARGB1555, - DRM_FORMAT_ARGB4444, DRM_FORMAT_BGR565, DRM_FORMAT_BGR888, - DRM_FORMAT_BGRA5551, - DRM_FORMAT_BGRA4444, + DRM_FORMAT_BGRX4444, + DRM_FORMAT_BGRX5551, DRM_FORMAT_BGRX8888, DRM_FORMAT_RGB565, DRM_FORMAT_RGB888, - DRM_FORMAT_RGBA4444, - DRM_FORMAT_RGBA5551, + DRM_FORMAT_RGBX4444, + DRM_FORMAT_RGBX5551, DRM_FORMAT_RGBX8888, + DRM_FORMAT_XBGR1555, + DRM_FORMAT_XBGR4444, DRM_FORMAT_XBGR8888, + DRM_FORMAT_XRGB1555, + DRM_FORMAT_XRGB4444, DRM_FORMAT_XRGB8888, DRM_FORMAT_NV16, @@ -363,11 +363,9 @@ static const u32 sun8i_vi_layer_formats[] = { DRM_FORMAT_YUV411, DRM_FORMAT_YUV420, DRM_FORMAT_YUV422, - DRM_FORMAT_YUV444, DRM_FORMAT_YVU411, DRM_FORMAT_YVU420, DRM_FORMAT_YVU422, - DRM_FORMAT_YVU444, }; struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm, diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc.c b/drivers/gpu/drm/ttm/ttm_page_alloc.c index f77c81db161b2e95b368c8c7118b6911993ded12..627f8dc91d0ed23e0958dfc39d106c967dcd376a 100644 --- a/drivers/gpu/drm/ttm/ttm_page_alloc.c +++ b/drivers/gpu/drm/ttm/ttm_page_alloc.c @@ -732,8 +732,8 @@ static void ttm_put_pages(struct page **pages, unsigned npages, int flags, #ifdef CONFIG_TRANSPARENT_HUGEPAGE if (!(flags & TTM_PAGE_FLAG_DMA32) && (npages - i) >= HPAGE_PMD_NR) { - for (j = 0; j < HPAGE_PMD_NR; ++j) - if (p++ != pages[i + j]) + for (j = 1; j < HPAGE_PMD_NR; ++j) + if (++p != pages[i + j]) break; if (j == HPAGE_PMD_NR) @@ -767,8 +767,8 @@ static void ttm_put_pages(struct page **pages, unsigned npages, int flags, if (!p) break; - for (j = 0; j < HPAGE_PMD_NR; ++j) - if (p++ != pages[i + j]) + for (j = 1; j < HPAGE_PMD_NR; ++j) + if (++p != pages[i + j]) break; if (j != HPAGE_PMD_NR) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c index e7e4655d3f36bf3680412b7e462cdb138fd13fbd..ce1ad7cd78996cf4fe652d76a259562b4c04f549 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c @@ -511,17 +511,14 @@ static void vmw_cmdbuf_work_func(struct work_struct *work) container_of(work, struct vmw_cmdbuf_man, work); struct vmw_cmdbuf_header *entry, *next; uint32_t dummy; - bool restart[SVGA_CB_CONTEXT_MAX]; bool send_fence = false; struct list_head restart_head[SVGA_CB_CONTEXT_MAX]; int i; struct vmw_cmdbuf_context *ctx; bool global_block = false; - for_each_cmdbuf_ctx(man, i, ctx) { + for_each_cmdbuf_ctx(man, i, ctx) INIT_LIST_HEAD(&restart_head[i]); - restart[i] = false; - } mutex_lock(&man->error_mutex); spin_lock(&man->lock); @@ -533,7 +530,6 @@ static void vmw_cmdbuf_work_func(struct work_struct *work) const char *cmd_name; list_del_init(&entry->list); - restart[entry->cb_context] = true; global_block = true; if (!vmw_cmd_describe(header, &error_cmd_size, &cmd_name)) { diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf_res.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf_res.c index 3b75af9bf85f353b3c7c0a6e1041dd119a92f5ce..f27bd7cff579d8329795ead850f679c51900e084 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf_res.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf_res.c @@ -210,8 +210,10 @@ int vmw_cmdbuf_res_add(struct vmw_cmdbuf_res_manager *man, cres->hash.key = user_key | (res_type << 24); ret = drm_ht_insert_item(&man->resources, &cres->hash); - if (unlikely(ret != 0)) + if (unlikely(ret != 0)) { + kfree(cres); goto out_invalid_key; + } cres->state = VMW_CMDBUF_RES_ADD; cres->res = vmw_resource_reference(res); diff --git a/drivers/gpu/drm/xen/xen_drm_front_gem.c b/drivers/gpu/drm/xen/xen_drm_front_gem.c index c85bfe7571cbfcf28896a3d8028e7e8c93849410..802662839e7ede6f0d4b48ec93e50696a87605e3 100644 --- a/drivers/gpu/drm/xen/xen_drm_front_gem.c +++ b/drivers/gpu/drm/xen/xen_drm_front_gem.c @@ -236,8 +236,14 @@ static int gem_mmap_obj(struct xen_gem_object *xen_obj, vma->vm_flags &= ~VM_PFNMAP; vma->vm_flags |= VM_MIXEDMAP; vma->vm_pgoff = 0; - vma->vm_page_prot = - pgprot_writecombine(vm_get_page_prot(vma->vm_flags)); + /* + * According to Xen on ARM ABI (xen/include/public/arch-arm.h): + * all memory which is shared with other entities in the system + * (including the hypervisor and other guests) must reside in memory + * which is mapped as Normal Inner Write-Back Outer Write-Back + * Inner-Shareable. + */ + vma->vm_page_prot = vm_get_page_prot(vma->vm_flags); /* * vm_operations_struct.fault handler will be called if CPU access @@ -283,8 +289,9 @@ void *xen_drm_front_gem_prime_vmap(struct drm_gem_object *gem_obj) if (!xen_obj->pages) return NULL; + /* Please see comment in gem_mmap_obj on mapping and attributes. */ return vmap(xen_obj->pages, xen_obj->num_pages, - VM_MAP, pgprot_writecombine(PAGE_KERNEL)); + VM_MAP, PAGE_KERNEL); } void xen_drm_front_gem_prime_vunmap(struct drm_gem_object *gem_obj, diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 7720bd142f79a6258bb6f5472bdd12df98c1035a..704891c0296a14ef821d8d28454051b2bdb63c91 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -3050,7 +3050,7 @@ void adreno_spin_idle_debug(struct adreno_device *adreno_dev, struct kgsl_device *device = &adreno_dev->dev; unsigned int rptr, wptr; unsigned int status, status3, intstatus; - unsigned int hwfault; + unsigned int hwfault, cx_status; dev_err(device->dev, str); @@ -3062,21 +3062,31 @@ void adreno_spin_idle_debug(struct adreno_device *adreno_dev, adreno_readreg(adreno_dev, ADRENO_REG_RBBM_INT_0_STATUS, &intstatus); adreno_readreg(adreno_dev, ADRENO_REG_CP_HW_FAULT, &hwfault); - dev_err(device->dev, - "rb=%d pos=%X/%X rbbm_status=%8.8X/%8.8X int_0_status=%8.8X\n", - adreno_dev->cur_rb->id, rptr, wptr, status, status3, intstatus); - - dev_err(device->dev, " hwfault=%8.8X\n", hwfault); /* * If CP is stuck, gmu may not perform as expected. So force a gmu * snapshot which captures entire state as well as sets the gmu fault * because things need to be reset anyway. */ - if (gmu_core_isenabled(device)) + if (gmu_core_isenabled(device)) { + gmu_core_regread(device, + A6XX_GPU_GMU_AO_GPU_CX_BUSY_STATUS, &cx_status); + dev_err(device->dev, + "rb=%d pos=%X/%X rbbm_status=%8.8X/%8.8X int_0_status=%8.8X cx_busy_status:%8.8X\n", + adreno_dev->cur_rb->id, rptr, wptr, status, + status3, intstatus, cx_status); + + dev_err(device->dev, " hwfault=%8.8X\n", hwfault); gmu_core_snapshot(device); - else + } else { + dev_err(device->dev, + "rb=%d pos=%X/%X rbbm_status=%8.8X/%8.8X int_0_status=%8.8X\n", + adreno_dev->cur_rb->id, rptr, wptr, status, + status3, intstatus); + + dev_err(device->dev, " hwfault=%8.8X\n", hwfault); kgsl_device_snapshot(device, NULL, false); + } } /** diff --git a/drivers/gpu/msm/adreno_a6xx.c b/drivers/gpu/msm/adreno_a6xx.c index b3cd958da7595d79ab45f4b20a95ce3de6a4c7bc..6f01901851b111379a57e9a7f092526cbf77a47b 100644 --- a/drivers/gpu/msm/adreno_a6xx.c +++ b/drivers/gpu/msm/adreno_a6xx.c @@ -88,10 +88,15 @@ static u32 a6xx_ifpc_pwrup_reglist[] = { A6XX_CP_AHB_CNTL, }; -/* a620 and a650 need to program A6XX_CP_PROTECT_REG_47 for the infinite span */ +/* Applicable to a620 and a650 */ static u32 a650_pwrup_reglist[] = { A6XX_RBBM_GBIF_CLIENT_QOS_CNTL, - A6XX_CP_PROTECT_REG + 47, + A6XX_CP_PROTECT_REG + 47, /* Programmed for infinite span */ + A6XX_TPL1_BICUBIC_WEIGHTS_TABLE_0, + A6XX_TPL1_BICUBIC_WEIGHTS_TABLE_1, + A6XX_TPL1_BICUBIC_WEIGHTS_TABLE_2, + A6XX_TPL1_BICUBIC_WEIGHTS_TABLE_3, + A6XX_TPL1_BICUBIC_WEIGHTS_TABLE_4, }; /* Applicable to a640 and a680 */ diff --git a/drivers/gpu/msm/adreno_a6xx_snapshot.c b/drivers/gpu/msm/adreno_a6xx_snapshot.c index ba1520f51c80603411f918ece83947e6ed1bc11f..b889764adf6543b1e80eb7b042c33698f5faa4e7 100644 --- a/drivers/gpu/msm/adreno_a6xx_snapshot.c +++ b/drivers/gpu/msm/adreno_a6xx_snapshot.c @@ -1819,7 +1819,7 @@ void a6xx_snapshot(struct adreno_device *adreno_dev, adreno_snapshot_registers(device, snapshot, a6xx_rscc_snapshot_registers, ARRAY_SIZE(a6xx_rscc_snapshot_registers) / 2); - } else if (adreno_is_a610(adreno_dev)) { + } else if (adreno_is_a610(adreno_dev) || adreno_is_a702(adreno_dev)) { adreno_snapshot_registers(device, snapshot, a6xx_gmu_wrapper_registers, ARRAY_SIZE(a6xx_gmu_wrapper_registers) / 2); diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index efdd2fb45d072951739c04f5b7e88170e40c2a6e..7bd01e177541ced07ef0d4b4bc82ac4c1ac72c33 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -5096,6 +5096,12 @@ int kgsl_device_platform_probe(struct kgsl_device *device) if (status) goto error_close_mmu; + /* Allocate memory for dma_parms and set the max_seg_size */ + device->dev->dma_parms = + kzalloc(sizeof(*device->dev->dma_parms), GFP_KERNEL); + + dma_set_max_seg_size(device->dev, KGSL_DMA_BIT_MASK); + /* Initialize the memory pools */ kgsl_init_page_pools(device->pdev); @@ -5159,6 +5165,9 @@ void kgsl_device_platform_remove(struct kgsl_device *device) { destroy_workqueue(device->events_wq); + kfree(device->dev->dma_parms); + device->dev->dma_parms = NULL; + kgsl_device_snapshot_close(device); kgsl_exit_page_pools(); diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c index e411f7e5814ed6b259ac54650e3490539e843cc3..4e2ea75dcc5951b2979d140b27b165e927721daf 100644 --- a/drivers/gpu/msm/kgsl_iommu.c +++ b/drivers/gpu/msm/kgsl_iommu.c @@ -135,8 +135,10 @@ static int kgsl_iommu_map_globals(struct kgsl_pagetable *pagetable) int ret = kgsl_mmu_map(pagetable, global_pt_entries[i].memdesc); - if (ret) + if (ret) { + kgsl_iommu_unmap_globals(pagetable); return ret; + } } } @@ -1011,6 +1013,8 @@ static void kgsl_iommu_destroy_pagetable(struct kgsl_pagetable *pt) } else { ctx = &iommu->ctx[KGSL_IOMMU_CONTEXT_USER]; kgsl_iommu_unmap_globals(pt); + if (pt->name == KGSL_MMU_GLOBAL_PT) + mmu->globalpt_mapped = false; } if (iommu_pt->domain) { @@ -1244,8 +1248,6 @@ static int _init_global_pt(struct kgsl_mmu *mmu, struct kgsl_pagetable *pt) goto done; } - ret = kgsl_iommu_map_globals(pt); - done: if (ret) _free_pt(ctx, pt); @@ -1529,6 +1531,18 @@ static int kgsl_iommu_init(struct kgsl_mmu *mmu) kgsl_setup_qdss_desc(device); kgsl_setup_qtimer_desc(device); + mmu->defaultpagetable = kgsl_mmu_getpagetable(mmu, + KGSL_MMU_GLOBAL_PT); + /* if we don't have a default pagetable, nothing will work */ + if (IS_ERR(mmu->defaultpagetable)) { + status = PTR_ERR(mmu->defaultpagetable); + mmu->defaultpagetable = NULL; + goto done; + } else if (mmu->defaultpagetable == NULL) { + status = -ENOMEM; + goto done; + } + if (!mmu->secured) goto done; @@ -1558,18 +1572,8 @@ static int _setup_user_context(struct kgsl_mmu *mmu) struct kgsl_iommu_pt *iommu_pt = NULL; unsigned int sctlr_val; - if (mmu->defaultpagetable == NULL) { - mmu->defaultpagetable = kgsl_mmu_getpagetable(mmu, - KGSL_MMU_GLOBAL_PT); - /* if we don't have a default pagetable, nothing will work */ - if (IS_ERR(mmu->defaultpagetable)) { - ret = PTR_ERR(mmu->defaultpagetable); - mmu->defaultpagetable = NULL; - return ret; - } else if (mmu->defaultpagetable == NULL) { - return -ENOMEM; - } - } + if (mmu->defaultpagetable == NULL) + return -ENOMEM; iommu_pt = mmu->defaultpagetable->priv; if (iommu_pt == NULL) @@ -1678,6 +1682,14 @@ static int kgsl_iommu_start(struct kgsl_mmu *mmu) wmb(); } + if (mmu->defaultpagetable != NULL && !mmu->globalpt_mapped) { + status = kgsl_iommu_map_globals(mmu->defaultpagetable); + if (status) + return status; + + mmu->globalpt_mapped = true; + } + /* Make sure the hardware is programmed to the default pagetable */ kgsl_iommu_set_pt(mmu, mmu->defaultpagetable); kgsl_iommu_disable_clk(mmu); diff --git a/drivers/gpu/msm/kgsl_mmu.h b/drivers/gpu/msm/kgsl_mmu.h index 3eb84f9ae77d886ee169e1f208fba663dd7396ab..0d5e75e0721d5c0f4f7f882498c46478f2be2d9a 100644 --- a/drivers/gpu/msm/kgsl_mmu.h +++ b/drivers/gpu/msm/kgsl_mmu.h @@ -146,6 +146,7 @@ struct kgsl_mmu_pt_ops { * @defaultpagetable: Default pagetable object for the MMU * @securepagetable: Default secure pagetable object for the MMU * @mmu_ops: Function pointers for the MMU sub-type + * @globalpt_mapped: True if global pagetable entries mapped * @secured: True if the MMU needs to be secured * @feature: Static list of MMU features * @secure_aligned_mask: Mask that secure buffers need to be aligned to @@ -158,6 +159,7 @@ struct kgsl_mmu { struct kgsl_pagetable *defaultpagetable; struct kgsl_pagetable *securepagetable; const struct kgsl_mmu_ops *mmu_ops; + bool globalpt_mapped; bool secured; unsigned long features; unsigned int secure_align_mask; diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index ca22326773bc59a2c1fcaa01e040b6dfb146217d..4eaeadedc479378e0b296be4c3dba0c55a3ded1d 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -313,6 +313,22 @@ void kgsl_pwrctrl_buslevel_update(struct kgsl_device *device, EXPORT_SYMBOL(kgsl_pwrctrl_buslevel_update); #if IS_ENABLED(CONFIG_QCOM_CX_IPEAK) +static int kgsl_pwr_cx_ipeak_freq_limit(void *ptr, unsigned int freq) +{ + struct kgsl_pwr_limit *cx_ipeak_pwr_limit = ptr; + + if (IS_ERR_OR_NULL(cx_ipeak_pwr_limit)) + return -EINVAL; + + /* CX-ipeak safe interrupt to remove freq limit */ + if (freq == 0) { + kgsl_pwr_limits_set_default(cx_ipeak_pwr_limit); + return 0; + } + + return kgsl_pwr_limits_set_freq(cx_ipeak_pwr_limit, freq); +} + static int kgsl_pwrctrl_cx_ipeak_vote(struct kgsl_device *device, u64 old_freq, u64 new_freq) { @@ -425,6 +441,30 @@ static int kgsl_pwrctrl_cx_ipeak_init(struct kgsl_device *device) ++i; } + /* cx_ipeak limits for GPU freq throttling */ + pwr->cx_ipeak_pwr_limit = kgsl_pwr_limits_add(KGSL_DEVICE_3D0); + if (IS_ERR_OR_NULL(pwr->cx_ipeak_pwr_limit)) { + dev_err(device->dev, + "Failed to get cx_ipeak power limit\n"); + ret = -EINVAL; + goto error; + } + + cx_ipeak_client = &pwr->gpu_ipeak_client[0]; + if (!IS_ERR_OR_NULL(cx_ipeak_client->client)) { + ret = cx_ipeak_victim_register(cx_ipeak_client->client, + kgsl_pwr_cx_ipeak_freq_limit, + pwr->cx_ipeak_pwr_limit); + if (ret) { + kgsl_pwr_limits_del(pwr->cx_ipeak_pwr_limit); + if (ret != -ENOENT) { + dev_err(device->dev, + "Failed to register GPU-CX-Ipeak victim\n"); + goto error; + } + } + } + of_node_put(node); return 0; @@ -2379,6 +2419,9 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) } } + INIT_LIST_HEAD(&pwr->limits); + spin_lock_init(&pwr->limits_lock); + result = kgsl_pwrctrl_cx_ipeak_init(device); if (result) goto error_cleanup_bus_ib; @@ -2386,8 +2429,6 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) INIT_WORK(&pwr->thermal_cycle_ws, kgsl_thermal_cycle); timer_setup(&pwr->thermal_timer, kgsl_thermal_timer, 0); - INIT_LIST_HEAD(&pwr->limits); - spin_lock_init(&pwr->limits_lock); pwr->sysfs_pwr_limit = kgsl_pwr_limits_add(KGSL_DEVICE_3D0); kgsl_pwrctrl_vbif_init(device); @@ -2418,6 +2459,12 @@ void kgsl_pwrctrl_close(struct kgsl_device *device) struct kgsl_pwrctrl *pwr = &device->pwrctrl; int i; + kgsl_pwr_limits_del(pwr->cx_ipeak_pwr_limit); + pwr->cx_ipeak_pwr_limit = NULL; + + if (!IS_ERR_OR_NULL(pwr->gpu_ipeak_client[0].client)) + cx_ipeak_victim_unregister(pwr->gpu_ipeak_client[0].client); + for (i = 0; i < ARRAY_SIZE(pwr->gpu_ipeak_client); i++) { if (!IS_ERR_OR_NULL(pwr->gpu_ipeak_client[i].client)) { cx_ipeak_unregister(pwr->gpu_ipeak_client[i].client); diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h index 15addf719aea4f91b9c85dd09dbf2a51876c5e1e..7283064c528dec700b3104a1a744e33fe2e3a1b8 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.h +++ b/drivers/gpu/msm/kgsl_pwrctrl.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2010-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2010-2020, The Linux Foundation. All rights reserved. */ #ifndef __KGSL_PWRCTRL_H #define __KGSL_PWRCTRL_H @@ -177,6 +177,7 @@ struct gpu_cx_ipeak_client { * @limits - list head for limits * @limits_lock - spin lock to protect limits list * @sysfs_pwr_limit - pointer to the sysfs limits node + * @cx_ipeak_pwr_limit - pointer to the cx_ipeak limits node * isense_clk_indx - index of isense clock, 0 if no isense * isense_clk_on_level - isense clock rate is XO rate below this level. * tzone_name - pointer to thermal zone name of GPU temperature sensor @@ -236,6 +237,7 @@ struct kgsl_pwrctrl { struct list_head limits; spinlock_t limits_lock; struct kgsl_pwr_limit *sysfs_pwr_limit; + struct kgsl_pwr_limit *cx_ipeak_pwr_limit; unsigned int gpu_bimc_int_clk_freq; bool gpu_bimc_interface_enabled; const char *tzone_name; diff --git a/drivers/gpu/msm/kgsl_sync.c b/drivers/gpu/msm/kgsl_sync.c index 38b5c88ae46a337d5cb454e834e039c02f0457cf..a90b9e359571473d75aae0b1efff660449da2b5e 100644 --- a/drivers/gpu/msm/kgsl_sync.c +++ b/drivers/gpu/msm/kgsl_sync.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. */ #include @@ -309,8 +309,7 @@ int kgsl_sync_timeline_create(struct kgsl_context *context) } kref_init(&ktimeline->kref); - snprintf(ktimeline->name, sizeof(ktimeline->name), - "%s_%d-%.15s(%d)-%.15s(%d)", + ktimeline->name = kasprintf(GFP_KERNEL, "%s_%d-%.15s(%d)-%.15s(%d)", context->device->name, context->id, current->group_leader->comm, current->group_leader->pid, current->comm, current->pid); @@ -354,7 +353,10 @@ static void kgsl_sync_timeline_signal(struct kgsl_sync_timeline *ktimeline, void kgsl_sync_timeline_destroy(struct kgsl_context *context) { - kfree(context->ktimeline); + struct kgsl_sync_timeline *ktimeline = context->ktimeline; + + kfree(ktimeline->name); + kfree(ktimeline); } static void kgsl_sync_timeline_release(struct kref *kref) diff --git a/drivers/gpu/msm/kgsl_sync.h b/drivers/gpu/msm/kgsl_sync.h index 989658d58eca3fb8db065cc205ef82ad896bfb4c..43209b1532cb5212811b0697a6d21c937e1ca3a0 100644 --- a/drivers/gpu/msm/kgsl_sync.h +++ b/drivers/gpu/msm/kgsl_sync.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2012-2014,2018-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2014,2018-2020 The Linux Foundation. All rights reserved. */ #ifndef __KGSL_SYNC_H #define __KGSL_SYNC_H @@ -21,7 +21,7 @@ */ struct kgsl_sync_timeline { struct kref kref; - char name[32]; + char *name; u64 fence_context; diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index acb00ae44eb4d6f8c497d67e494683ca43eeaac2..e1e11d6e8a7df4710fee4c48256f9d9fb448d8a1 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -652,6 +652,17 @@ config HID_MULTITOUCH To compile this driver as a module, choose M here: the module will be called hid-multitouch. +config HID_NINTENDO + tristate "Nintendo Joy-Con and Pro Controller support" + depends on HID + help + Adds support for the Nintendo Switch Joy-Cons and Pro Controller. + All controllers support bluetooth, and the Pro Controller also supports + its USB mode. + + To compile this driver as a module, choose M here: the + module will be called hid-nintendo. + config HID_NTI tristate "NTI keyboard adapters" ---help--- diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 86f9f6ccdb4082dc60db65dc8e7ef6969589323b..3fb5a1b73a874f25f29c1192a9213c87272db04d 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_HID_MAYFLASH) += hid-mf.o obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o obj-$(CONFIG_HID_MULTITOUCH) += hid-multitouch.o +obj-$(CONFIG_HID_NINTENDO) += hid-nintendo.o obj-$(CONFIG_HID_NTI) += hid-nti.o obj-$(CONFIG_HID_NTRIG) += hid-ntrig.o obj-$(CONFIG_HID_ORTEK) += hid-ortek.o diff --git a/drivers/hid/hid-alps.c b/drivers/hid/hid-alps.c index 3cd7229b6e5465b88759d42a9a12084cd948eeab..895f49b565ee1fab5db2e73022fdcce00dd861dc 100644 --- a/drivers/hid/hid-alps.c +++ b/drivers/hid/hid-alps.c @@ -734,7 +734,7 @@ static int alps_input_configured(struct hid_device *hdev, struct hid_input *hi) if (data->has_sp) { input2 = input_allocate_device(); if (!input2) { - input_free_device(input2); + ret = -ENOMEM; goto exit; } diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 3a359716fb386979e11d489333c3ae37c001a8c6..2c85d075daee13186ad079eea20aba37eefb7ad0 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -288,6 +288,12 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign offset = report->size; report->size += parser->global.report_size * parser->global.report_count; + /* Total size check: Allow for possible report index byte */ + if (report->size > (HID_MAX_BUFFER_SIZE - 1) << 3) { + hid_err(parser->device, "report is too long\n"); + return -1; + } + if (!parser->local.usage_index) /* Ignore padding fields */ return 0; @@ -1560,7 +1566,9 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size, rsize = ((report->size - 1) >> 3) + 1; - if (rsize > HID_MAX_BUFFER_SIZE) + if (report_enum->numbered && rsize >= HID_MAX_BUFFER_SIZE) + rsize = HID_MAX_BUFFER_SIZE - 1; + else if (rsize > HID_MAX_BUFFER_SIZE) rsize = HID_MAX_BUFFER_SIZE; if (csize < rsize) { diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 4f41adc99afcabc0b5eb7d38c5d66e77f0f3f7ea..2ee465aef0fd491442fdb294ea7d689d0aea455a 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -620,6 +620,7 @@ #define USB_VENDOR_ID_ITE 0x048d #define USB_DEVICE_ID_ITE_LENOVO_YOGA 0x8386 #define USB_DEVICE_ID_ITE_LENOVO_YOGA2 0x8350 +#define I2C_DEVICE_ID_ITE_LENOVO_LEGION_Y720 0x837a #define USB_DEVICE_ID_ITE_LENOVO_YOGA900 0x8396 #define USB_DEVICE_ID_ITE8595 0x8595 @@ -717,6 +718,7 @@ #define USB_DEVICE_ID_LG_MULTITOUCH 0x0064 #define USB_DEVICE_ID_LG_MELFAS_MT 0x6007 #define I2C_DEVICE_ID_LG_8001 0x8001 +#define I2C_DEVICE_ID_LG_7010 0x7010 #define USB_VENDOR_ID_LOGITECH 0x046d #define USB_DEVICE_ID_LOGITECH_AUDIOHUB 0x0a0e @@ -848,6 +850,9 @@ #define USB_VENDOR_ID_NINTENDO 0x057e #define USB_DEVICE_ID_NINTENDO_WIIMOTE 0x0306 #define USB_DEVICE_ID_NINTENDO_WIIMOTE2 0x0330 +#define USB_DEVICE_ID_NINTENDO_JOYCONL 0x2006 +#define USB_DEVICE_ID_NINTENDO_JOYCONR 0x2007 +#define USB_DEVICE_ID_NINTENDO_PROCON 0x2009 #define USB_VENDOR_ID_NOVATEK 0x0603 #define USB_DEVICE_ID_NOVATEK_PCT 0x0600 @@ -1074,6 +1079,7 @@ #define USB_DEVICE_ID_SYNAPTICS_LTS2 0x1d10 #define USB_DEVICE_ID_SYNAPTICS_HD 0x0ac3 #define USB_DEVICE_ID_SYNAPTICS_QUAD_HD 0x1ac3 +#define USB_DEVICE_ID_SYNAPTICS_ACER_SWITCH5_012 0x2968 #define USB_DEVICE_ID_SYNAPTICS_TP_V103 0x5710 #define USB_VENDOR_ID_TEXAS_INSTRUMENTS 0x2047 diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 01bed2f6862ee5a313d58cf82cc67c5366f135c4..dbb0cbe65fc98a9ada4bc1bce7ed940ae5577f99 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1125,9 +1125,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel } mapped: - if (device->driver->input_mapped && device->driver->input_mapped(device, - hidinput, field, usage, &bit, &max) < 0) - goto ignore; + if (device->driver->input_mapped && + device->driver->input_mapped(device, hidinput, field, usage, + &bit, &max) < 0) { + /* + * The driver indicated that no further generic handling + * of the usage is desired. + */ + return; + } set_bit(usage->type, input->evbit); @@ -1208,9 +1214,11 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel set_bit(MSC_SCAN, input->mscbit); } -ignore: return; +ignore: + usage->type = 0; + usage->code = 0; } void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value) diff --git a/drivers/hid/hid-ite.c b/drivers/hid/hid-ite.c index 98b059d79bc891948695fe53a6a774bf171376c8..f2e23f81601e7472fe2a3d8f2e26cc7432b50dbe 100644 --- a/drivers/hid/hid-ite.c +++ b/drivers/hid/hid-ite.c @@ -43,6 +43,10 @@ static int ite_event(struct hid_device *hdev, struct hid_field *field, static const struct hid_device_id ite_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ITE, USB_DEVICE_ID_ITE8595) }, { HID_USB_DEVICE(USB_VENDOR_ID_258A, USB_DEVICE_ID_258A_6A88) }, + /* ITE8595 USB kbd ctlr, with Synaptics touchpad connected to it. */ + { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, + USB_VENDOR_ID_SYNAPTICS, + USB_DEVICE_ID_SYNAPTICS_ACER_SWITCH5_012) }, { } }; MODULE_DEVICE_TABLE(hid, ite_devices); diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index f9167d0e095ceff56b6aec3422692ae328be57a4..19dfd8acd0dab5f01f23f0c2a04d9cc5b53d17fd 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -1007,7 +1007,7 @@ static int mt_process_slot(struct mt_device *td, struct input_dev *input, tool = MT_TOOL_DIAL; else if (unlikely(!confidence_state)) { tool = MT_TOOL_PALM; - if (!active && + if (!active && mt && input_mt_is_active(&mt->slots[slotnum])) { /* * The non-confidence was reported for @@ -1972,6 +1972,9 @@ static const struct hid_device_id mt_devices[] = { { .driver_data = MT_CLS_LG, HID_USB_DEVICE(USB_VENDOR_ID_LG, USB_DEVICE_ID_LG_MELFAS_MT) }, + { .driver_data = MT_CLS_LG, + HID_DEVICE(BUS_I2C, HID_GROUP_GENERIC, + USB_VENDOR_ID_LG, I2C_DEVICE_ID_LG_7010) }, /* MosArt panels */ { .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE, diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c new file mode 100644 index 0000000000000000000000000000000000000000..3695b96694bd4c9510e6e402d67fd9103071a78a --- /dev/null +++ b/drivers/hid/hid-nintendo.c @@ -0,0 +1,820 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * HID driver for Nintendo Switch Joy-Cons and Pro Controllers + * + * Copyright (c) 2019 Daniel J. Ogorchock + * + * The following resources/projects were referenced for this driver: + * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering + * https://gitlab.com/pjranki/joycon-linux-kernel (Peter Rankin) + * https://github.com/FrotBot/SwitchProConLinuxUSB + * https://github.com/MTCKC/ProconXInput + * hid-wiimote kernel hid driver + * hid-logitech-hidpp driver + * + * This driver supports the Nintendo Switch Joy-Cons and Pro Controllers. The + * Pro Controllers can either be used over USB or Bluetooth. + * + * The driver will retrieve the factory calibration info from the controllers, + * so little to no user calibration should be required. + * + */ + +#include "hid-ids.h" +#include +#include +#include +#include +#include +#include + +/* + * Reference the url below for the following HID report defines: + * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering + */ + +/* Output Reports */ +static const u8 JC_OUTPUT_RUMBLE_AND_SUBCMD = 0x01; +static const u8 JC_OUTPUT_FW_UPDATE_PKT = 0x03; +static const u8 JC_OUTPUT_RUMBLE_ONLY = 0x10; +static const u8 JC_OUTPUT_MCU_DATA = 0x11; +static const u8 JC_OUTPUT_USB_CMD = 0x80; + +/* Subcommand IDs */ +static const u8 JC_SUBCMD_STATE /*= 0x00*/; +static const u8 JC_SUBCMD_MANUAL_BT_PAIRING = 0x01; +static const u8 JC_SUBCMD_REQ_DEV_INFO = 0x02; +static const u8 JC_SUBCMD_SET_REPORT_MODE = 0x03; +static const u8 JC_SUBCMD_TRIGGERS_ELAPSED = 0x04; +static const u8 JC_SUBCMD_GET_PAGE_LIST_STATE = 0x05; +static const u8 JC_SUBCMD_SET_HCI_STATE = 0x06; +static const u8 JC_SUBCMD_RESET_PAIRING_INFO = 0x07; +static const u8 JC_SUBCMD_LOW_POWER_MODE = 0x08; +static const u8 JC_SUBCMD_SPI_FLASH_READ = 0x10; +static const u8 JC_SUBCMD_SPI_FLASH_WRITE = 0x11; +static const u8 JC_SUBCMD_RESET_MCU = 0x20; +static const u8 JC_SUBCMD_SET_MCU_CONFIG = 0x21; +static const u8 JC_SUBCMD_SET_MCU_STATE = 0x22; +static const u8 JC_SUBCMD_SET_PLAYER_LIGHTS = 0x30; +static const u8 JC_SUBCMD_GET_PLAYER_LIGHTS = 0x31; +static const u8 JC_SUBCMD_SET_HOME_LIGHT = 0x38; +static const u8 JC_SUBCMD_ENABLE_IMU = 0x40; +static const u8 JC_SUBCMD_SET_IMU_SENSITIVITY = 0x41; +static const u8 JC_SUBCMD_WRITE_IMU_REG = 0x42; +static const u8 JC_SUBCMD_READ_IMU_REG = 0x43; +static const u8 JC_SUBCMD_ENABLE_VIBRATION = 0x48; +static const u8 JC_SUBCMD_GET_REGULATED_VOLTAGE = 0x50; + +/* Input Reports */ +static const u8 JC_INPUT_BUTTON_EVENT = 0x3F; +static const u8 JC_INPUT_SUBCMD_REPLY = 0x21; +static const u8 JC_INPUT_IMU_DATA = 0x30; +static const u8 JC_INPUT_MCU_DATA = 0x31; +static const u8 JC_INPUT_USB_RESPONSE = 0x81; + +/* Feature Reports */ +static const u8 JC_FEATURE_LAST_SUBCMD = 0x02; +static const u8 JC_FEATURE_OTA_FW_UPGRADE = 0x70; +static const u8 JC_FEATURE_SETUP_MEM_READ = 0x71; +static const u8 JC_FEATURE_MEM_READ = 0x72; +static const u8 JC_FEATURE_ERASE_MEM_SECTOR = 0x73; +static const u8 JC_FEATURE_MEM_WRITE = 0x74; +static const u8 JC_FEATURE_LAUNCH = 0x75; + +/* USB Commands */ +static const u8 JC_USB_CMD_CONN_STATUS = 0x01; +static const u8 JC_USB_CMD_HANDSHAKE = 0x02; +static const u8 JC_USB_CMD_BAUDRATE_3M = 0x03; +static const u8 JC_USB_CMD_NO_TIMEOUT = 0x04; +static const u8 JC_USB_CMD_EN_TIMEOUT = 0x05; +static const u8 JC_USB_RESET = 0x06; +static const u8 JC_USB_PRE_HANDSHAKE = 0x91; +static const u8 JC_USB_SEND_UART = 0x92; + +/* SPI storage addresses of factory calibration data */ +static const u16 JC_CAL_DATA_START = 0x603d; +static const u16 JC_CAL_DATA_END = 0x604e; +#define JC_CAL_DATA_SIZE (JC_CAL_DATA_END - JC_CAL_DATA_START + 1) + + +/* The raw analog joystick values will be mapped in terms of this magnitude */ +static const u16 JC_MAX_STICK_MAG = 32767; +static const u16 JC_STICK_FUZZ = 250; +static const u16 JC_STICK_FLAT = 500; + +/* States for controller state machine */ +enum joycon_ctlr_state { + JOYCON_CTLR_STATE_INIT, + JOYCON_CTLR_STATE_READ, +}; + +struct joycon_stick_cal { + s32 max; + s32 min; + s32 center; +}; + +/* + * All the controller's button values are stored in a u32. + * They can be accessed with bitwise ANDs. + */ +static const u32 JC_BTN_Y = BIT(0); +static const u32 JC_BTN_X = BIT(1); +static const u32 JC_BTN_B = BIT(2); +static const u32 JC_BTN_A = BIT(3); +static const u32 JC_BTN_SR_R = BIT(4); +static const u32 JC_BTN_SL_R = BIT(5); +static const u32 JC_BTN_R = BIT(6); +static const u32 JC_BTN_ZR = BIT(7); +static const u32 JC_BTN_MINUS = BIT(8); +static const u32 JC_BTN_PLUS = BIT(9); +static const u32 JC_BTN_RSTICK = BIT(10); +static const u32 JC_BTN_LSTICK = BIT(11); +static const u32 JC_BTN_HOME = BIT(12); +static const u32 JC_BTN_CAP = BIT(13); /* capture button */ +static const u32 JC_BTN_DOWN = BIT(16); +static const u32 JC_BTN_UP = BIT(17); +static const u32 JC_BTN_RIGHT = BIT(18); +static const u32 JC_BTN_LEFT = BIT(19); +static const u32 JC_BTN_SR_L = BIT(20); +static const u32 JC_BTN_SL_L = BIT(21); +static const u32 JC_BTN_L = BIT(22); +static const u32 JC_BTN_ZL = BIT(23); + +enum joycon_msg_type { + JOYCON_MSG_TYPE_NONE, + JOYCON_MSG_TYPE_USB, + JOYCON_MSG_TYPE_SUBCMD, +}; + +struct joycon_subcmd_request { + u8 output_id; /* must be 0x01 for subcommand, 0x10 for rumble only */ + u8 packet_num; /* incremented every send */ + u8 rumble_data[8]; + u8 subcmd_id; + u8 data[0]; /* length depends on the subcommand */ +} __packed; + +struct joycon_subcmd_reply { + u8 ack; /* MSB 1 for ACK, 0 for NACK */ + u8 id; /* id of requested subcmd */ + u8 data[0]; /* will be at most 35 bytes */ +} __packed; + +struct joycon_input_report { + u8 id; + u8 timer; + u8 bat_con; /* battery and connection info */ + u8 button_status[3]; + u8 left_stick[3]; + u8 right_stick[3]; + u8 vibrator_report; + + /* + * If support for firmware updates, gyroscope data, and/or NFC/IR + * are added in the future, this can be swapped for a union. + */ + struct joycon_subcmd_reply reply; +} __packed; + +#define JC_MAX_RESP_SIZE (sizeof(struct joycon_input_report) + 35) + +/* Each physical controller is associated with a joycon_ctlr struct */ +struct joycon_ctlr { + struct hid_device *hdev; + struct input_dev *input; + enum joycon_ctlr_state ctlr_state; + + /* The following members are used for synchronous sends/receives */ + enum joycon_msg_type msg_type; + u8 subcmd_num; + struct mutex output_mutex; + u8 input_buf[JC_MAX_RESP_SIZE]; + wait_queue_head_t wait; + bool received_resp; + u8 usb_ack_match; + u8 subcmd_ack_match; + + /* factory calibration data */ + struct joycon_stick_cal left_stick_cal_x; + struct joycon_stick_cal left_stick_cal_y; + struct joycon_stick_cal right_stick_cal_x; + struct joycon_stick_cal right_stick_cal_y; + +}; + +static int __joycon_hid_send(struct hid_device *hdev, u8 *data, size_t len) +{ + u8 *buf; + int ret; + + buf = kmemdup(data, len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + ret = hid_hw_output_report(hdev, buf, len); + kfree(buf); + if (ret < 0) + hid_dbg(hdev, "Failed to send output report ret=%d\n", ret); + return ret; +} + +static int joycon_hid_send_sync(struct joycon_ctlr *ctlr, u8 *data, size_t len) +{ + int ret; + + ret = __joycon_hid_send(ctlr->hdev, data, len); + if (ret < 0) { + memset(ctlr->input_buf, 0, JC_MAX_RESP_SIZE); + return ret; + } + + if (!wait_event_timeout(ctlr->wait, ctlr->received_resp, HZ)) { + hid_dbg(ctlr->hdev, "synchronous send/receive timed out\n"); + memset(ctlr->input_buf, 0, JC_MAX_RESP_SIZE); + return -ETIMEDOUT; + } + + ctlr->received_resp = false; + return 0; +} + +static int joycon_send_usb(struct joycon_ctlr *ctlr, u8 cmd) +{ + int ret; + u8 buf[2] = {JC_OUTPUT_USB_CMD}; + + buf[1] = cmd; + ctlr->usb_ack_match = cmd; + ctlr->msg_type = JOYCON_MSG_TYPE_USB; + ret = joycon_hid_send_sync(ctlr, buf, sizeof(buf)); + if (ret) + hid_dbg(ctlr->hdev, "send usb command failed; ret=%d\n", ret); + return ret; +} + +static int joycon_send_subcmd(struct joycon_ctlr *ctlr, + struct joycon_subcmd_request *subcmd, + size_t data_len) +{ + int ret; + + subcmd->output_id = JC_OUTPUT_RUMBLE_AND_SUBCMD; + subcmd->packet_num = ctlr->subcmd_num; + if (++ctlr->subcmd_num > 0xF) + ctlr->subcmd_num = 0; + ctlr->subcmd_ack_match = subcmd->subcmd_id; + ctlr->msg_type = JOYCON_MSG_TYPE_SUBCMD; + + ret = joycon_hid_send_sync(ctlr, (u8 *)subcmd, + sizeof(*subcmd) + data_len); + if (ret < 0) + hid_dbg(ctlr->hdev, "send subcommand failed; ret=%d\n", ret); + else + ret = 0; + return ret; +} + +/* Supply nibbles for flash and on. Ones correspond to active */ +static int joycon_set_player_leds(struct joycon_ctlr *ctlr, u8 flash, u8 on) +{ + struct joycon_subcmd_request *req; + u8 buffer[sizeof(*req) + 1] = { 0 }; + + req = (struct joycon_subcmd_request *)buffer; + req->subcmd_id = JC_SUBCMD_SET_PLAYER_LIGHTS; + req->data[0] = (flash << 4) | on; + + hid_dbg(ctlr->hdev, "setting player leds\n"); + return joycon_send_subcmd(ctlr, req, 1); +} + +static const u16 DFLT_STICK_CAL_CEN = 2000; +static const u16 DFLT_STICK_CAL_MAX = 3500; +static const u16 DFLT_STICK_CAL_MIN = 500; +static int joycon_request_calibration(struct joycon_ctlr *ctlr) +{ + struct joycon_subcmd_request *req; + u8 buffer[sizeof(*req) + 5] = { 0 }; + struct joycon_input_report *report; + struct joycon_stick_cal *cal_x; + struct joycon_stick_cal *cal_y; + s32 x_max_above; + s32 x_min_below; + s32 y_max_above; + s32 y_min_below; + u8 *data; + u8 *raw_cal; + int ret; + + req = (struct joycon_subcmd_request *)buffer; + req->subcmd_id = JC_SUBCMD_SPI_FLASH_READ; + data = req->data; + data[0] = 0xFF & JC_CAL_DATA_START; + data[1] = 0xFF & (JC_CAL_DATA_START >> 8); + data[2] = 0xFF & (JC_CAL_DATA_START >> 16); + data[3] = 0xFF & (JC_CAL_DATA_START >> 24); + data[4] = JC_CAL_DATA_SIZE; + + hid_dbg(ctlr->hdev, "requesting cal data\n"); + ret = joycon_send_subcmd(ctlr, req, 5); + if (ret) { + hid_warn(ctlr->hdev, + "Failed to read stick cal, using defaults; ret=%d\n", + ret); + + ctlr->left_stick_cal_x.center = DFLT_STICK_CAL_CEN; + ctlr->left_stick_cal_x.max = DFLT_STICK_CAL_MAX; + ctlr->left_stick_cal_x.min = DFLT_STICK_CAL_MIN; + + ctlr->left_stick_cal_y.center = DFLT_STICK_CAL_CEN; + ctlr->left_stick_cal_y.max = DFLT_STICK_CAL_MAX; + ctlr->left_stick_cal_y.min = DFLT_STICK_CAL_MIN; + + ctlr->right_stick_cal_x.center = DFLT_STICK_CAL_CEN; + ctlr->right_stick_cal_x.max = DFLT_STICK_CAL_MAX; + ctlr->right_stick_cal_x.min = DFLT_STICK_CAL_MIN; + + ctlr->right_stick_cal_y.center = DFLT_STICK_CAL_CEN; + ctlr->right_stick_cal_y.max = DFLT_STICK_CAL_MAX; + ctlr->right_stick_cal_y.min = DFLT_STICK_CAL_MIN; + + return ret; + } + + report = (struct joycon_input_report *)ctlr->input_buf; + raw_cal = &report->reply.data[5]; + + /* left stick calibration parsing */ + cal_x = &ctlr->left_stick_cal_x; + cal_y = &ctlr->left_stick_cal_y; + + x_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 0), 0, 12); + y_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 1), 4, 12); + cal_x->center = hid_field_extract(ctlr->hdev, (raw_cal + 3), 0, 12); + cal_y->center = hid_field_extract(ctlr->hdev, (raw_cal + 4), 4, 12); + x_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 6), 0, 12); + y_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 7), 4, 12); + cal_x->max = cal_x->center + x_max_above; + cal_x->min = cal_x->center - x_min_below; + cal_y->max = cal_y->center + y_max_above; + cal_y->min = cal_y->center - y_min_below; + + /* right stick calibration parsing */ + raw_cal += 9; + cal_x = &ctlr->right_stick_cal_x; + cal_y = &ctlr->right_stick_cal_y; + + cal_x->center = hid_field_extract(ctlr->hdev, (raw_cal + 0), 0, 12); + cal_y->center = hid_field_extract(ctlr->hdev, (raw_cal + 1), 4, 12); + x_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 3), 0, 12); + y_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 4), 4, 12); + x_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 6), 0, 12); + y_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 7), 4, 12); + cal_x->max = cal_x->center + x_max_above; + cal_x->min = cal_x->center - x_min_below; + cal_y->max = cal_y->center + y_max_above; + cal_y->min = cal_y->center - y_min_below; + + hid_dbg(ctlr->hdev, "calibration:\n" + "l_x_c=%d l_x_max=%d l_x_min=%d\n" + "l_y_c=%d l_y_max=%d l_y_min=%d\n" + "r_x_c=%d r_x_max=%d r_x_min=%d\n" + "r_y_c=%d r_y_max=%d r_y_min=%d\n", + ctlr->left_stick_cal_x.center, + ctlr->left_stick_cal_x.max, + ctlr->left_stick_cal_x.min, + ctlr->left_stick_cal_y.center, + ctlr->left_stick_cal_y.max, + ctlr->left_stick_cal_y.min, + ctlr->right_stick_cal_x.center, + ctlr->right_stick_cal_x.max, + ctlr->right_stick_cal_x.min, + ctlr->right_stick_cal_y.center, + ctlr->right_stick_cal_y.max, + ctlr->right_stick_cal_y.min); + + return 0; +} + +static int joycon_set_report_mode(struct joycon_ctlr *ctlr) +{ + struct joycon_subcmd_request *req; + u8 buffer[sizeof(*req) + 1] = { 0 }; + + req = (struct joycon_subcmd_request *)buffer; + req->subcmd_id = JC_SUBCMD_SET_REPORT_MODE; + req->data[0] = 0x30; /* standard, full report mode */ + + hid_dbg(ctlr->hdev, "setting controller report mode\n"); + return joycon_send_subcmd(ctlr, req, 1); +} + +static s32 joycon_map_stick_val(struct joycon_stick_cal *cal, s32 val) +{ + s32 center = cal->center; + s32 min = cal->min; + s32 max = cal->max; + s32 new_val; + + if (val > center) { + new_val = (val - center) * JC_MAX_STICK_MAG; + new_val /= (max - center); + } else { + new_val = (center - val) * -JC_MAX_STICK_MAG; + new_val /= (center - min); + } + new_val = clamp(new_val, (s32)-JC_MAX_STICK_MAG, (s32)JC_MAX_STICK_MAG); + return new_val; +} + +static void joycon_parse_report(struct joycon_ctlr *ctlr, + struct joycon_input_report *rep) +{ + struct input_dev *dev = ctlr->input; + u32 btns; + u32 id = ctlr->hdev->product; + + btns = hid_field_extract(ctlr->hdev, rep->button_status, 0, 24); + + if (id != USB_DEVICE_ID_NINTENDO_JOYCONR) { + u16 raw_x; + u16 raw_y; + s32 x; + s32 y; + + /* get raw stick values */ + raw_x = hid_field_extract(ctlr->hdev, rep->left_stick, 0, 12); + raw_y = hid_field_extract(ctlr->hdev, + rep->left_stick + 1, 4, 12); + /* map the stick values */ + x = joycon_map_stick_val(&ctlr->left_stick_cal_x, raw_x); + y = -joycon_map_stick_val(&ctlr->left_stick_cal_y, raw_y); + /* report sticks */ + input_report_abs(dev, ABS_X, x); + input_report_abs(dev, ABS_Y, y); + + /* report buttons */ + input_report_key(dev, BTN_TL, btns & JC_BTN_L); + input_report_key(dev, BTN_TL2, btns & JC_BTN_ZL); + if (id != USB_DEVICE_ID_NINTENDO_PROCON) { + /* Report the S buttons as the non-existent triggers */ + input_report_key(dev, BTN_TR, btns & JC_BTN_SL_L); + input_report_key(dev, BTN_TR2, btns & JC_BTN_SR_L); + } + input_report_key(dev, BTN_SELECT, btns & JC_BTN_MINUS); + input_report_key(dev, BTN_THUMBL, btns & JC_BTN_LSTICK); + input_report_key(dev, BTN_Z, btns & JC_BTN_CAP); + input_report_key(dev, BTN_DPAD_DOWN, btns & JC_BTN_DOWN); + input_report_key(dev, BTN_DPAD_UP, btns & JC_BTN_UP); + input_report_key(dev, BTN_DPAD_RIGHT, btns & JC_BTN_RIGHT); + input_report_key(dev, BTN_DPAD_LEFT, btns & JC_BTN_LEFT); + } + if (id != USB_DEVICE_ID_NINTENDO_JOYCONL) { + u16 raw_x; + u16 raw_y; + s32 x; + s32 y; + + /* get raw stick values */ + raw_x = hid_field_extract(ctlr->hdev, rep->right_stick, 0, 12); + raw_y = hid_field_extract(ctlr->hdev, + rep->right_stick + 1, 4, 12); + /* map stick values */ + x = joycon_map_stick_val(&ctlr->right_stick_cal_x, raw_x); + y = -joycon_map_stick_val(&ctlr->right_stick_cal_y, raw_y); + /* report sticks */ + input_report_abs(dev, ABS_RX, x); + input_report_abs(dev, ABS_RY, y); + + /* report buttons */ + input_report_key(dev, BTN_TR, btns & JC_BTN_R); + input_report_key(dev, BTN_TR2, btns & JC_BTN_ZR); + if (id != USB_DEVICE_ID_NINTENDO_PROCON) { + /* Report the S buttons as the non-existent triggers */ + input_report_key(dev, BTN_TL, btns & JC_BTN_SL_R); + input_report_key(dev, BTN_TL2, btns & JC_BTN_SR_R); + } + input_report_key(dev, BTN_START, btns & JC_BTN_PLUS); + input_report_key(dev, BTN_THUMBR, btns & JC_BTN_RSTICK); + input_report_key(dev, BTN_MODE, btns & JC_BTN_HOME); + input_report_key(dev, BTN_WEST, btns & JC_BTN_Y); + input_report_key(dev, BTN_NORTH, btns & JC_BTN_X); + input_report_key(dev, BTN_EAST, btns & JC_BTN_A); + input_report_key(dev, BTN_SOUTH, btns & JC_BTN_B); + } + + input_sync(dev); +} + + +static const unsigned int joycon_button_inputs_l[] = { + BTN_SELECT, BTN_Z, BTN_THUMBL, + BTN_DPAD_UP, BTN_DPAD_DOWN, BTN_DPAD_LEFT, BTN_DPAD_RIGHT, + BTN_TL, BTN_TL2, + 0 /* 0 signals end of array */ +}; + +static const unsigned int joycon_button_inputs_r[] = { + BTN_START, BTN_MODE, BTN_THUMBR, + BTN_SOUTH, BTN_EAST, BTN_NORTH, BTN_WEST, + BTN_TR, BTN_TR2, + 0 /* 0 signals end of array */ +}; + +static DEFINE_MUTEX(joycon_input_num_mutex); +static int joycon_input_create(struct joycon_ctlr *ctlr) +{ + struct hid_device *hdev; + static int input_num = 1; + const char *name; + int ret; + int i; + + hdev = ctlr->hdev; + + switch (hdev->product) { + case USB_DEVICE_ID_NINTENDO_PROCON: + name = "Nintendo Switch Pro Controller"; + break; + case USB_DEVICE_ID_NINTENDO_JOYCONL: + name = "Nintendo Switch Left Joy-Con"; + break; + case USB_DEVICE_ID_NINTENDO_JOYCONR: + name = "Nintendo Switch Right Joy-Con"; + break; + default: /* Should be impossible */ + hid_err(hdev, "Invalid hid product\n"); + return -EINVAL; + } + + ctlr->input = devm_input_allocate_device(&hdev->dev); + if (!ctlr->input) + return -ENOMEM; + ctlr->input->id.bustype = hdev->bus; + ctlr->input->id.vendor = hdev->vendor; + ctlr->input->id.product = hdev->product; + ctlr->input->id.version = hdev->version; + ctlr->input->name = name; + input_set_drvdata(ctlr->input, ctlr); + + + /* set up sticks */ + if (hdev->product != USB_DEVICE_ID_NINTENDO_JOYCONR) { + input_set_abs_params(ctlr->input, ABS_X, + -JC_MAX_STICK_MAG, JC_MAX_STICK_MAG, + JC_STICK_FUZZ, JC_STICK_FLAT); + input_set_abs_params(ctlr->input, ABS_Y, + -JC_MAX_STICK_MAG, JC_MAX_STICK_MAG, + JC_STICK_FUZZ, JC_STICK_FLAT); + } + if (hdev->product != USB_DEVICE_ID_NINTENDO_JOYCONL) { + input_set_abs_params(ctlr->input, ABS_RX, + -JC_MAX_STICK_MAG, JC_MAX_STICK_MAG, + JC_STICK_FUZZ, JC_STICK_FLAT); + input_set_abs_params(ctlr->input, ABS_RY, + -JC_MAX_STICK_MAG, JC_MAX_STICK_MAG, + JC_STICK_FUZZ, JC_STICK_FLAT); + } + + /* set up buttons */ + if (hdev->product != USB_DEVICE_ID_NINTENDO_JOYCONR) { + for (i = 0; joycon_button_inputs_l[i] > 0; i++) + input_set_capability(ctlr->input, EV_KEY, + joycon_button_inputs_l[i]); + } + if (hdev->product != USB_DEVICE_ID_NINTENDO_JOYCONL) { + for (i = 0; joycon_button_inputs_r[i] > 0; i++) + input_set_capability(ctlr->input, EV_KEY, + joycon_button_inputs_r[i]); + } + + ret = input_register_device(ctlr->input); + if (ret) + return ret; + + /* Set the default controller player leds based on controller number */ + mutex_lock(&joycon_input_num_mutex); + mutex_lock(&ctlr->output_mutex); + ret = joycon_set_player_leds(ctlr, 0, 0xF >> (4 - input_num)); + if (ret) + hid_warn(ctlr->hdev, "Failed to set leds; ret=%d\n", ret); + mutex_unlock(&ctlr->output_mutex); + if (++input_num > 4) + input_num = 1; + mutex_unlock(&joycon_input_num_mutex); + + return 0; +} + +/* Common handler for parsing inputs */ +static int joycon_ctlr_read_handler(struct joycon_ctlr *ctlr, u8 *data, + int size) +{ + int ret = 0; + + if (data[0] == JC_INPUT_SUBCMD_REPLY || data[0] == JC_INPUT_IMU_DATA || + data[0] == JC_INPUT_MCU_DATA) { + if (size >= 12) /* make sure it contains the input report */ + joycon_parse_report(ctlr, + (struct joycon_input_report *)data); + } + + return ret; +} + +static int joycon_ctlr_handle_event(struct joycon_ctlr *ctlr, u8 *data, + int size) +{ + int ret = 0; + bool match = false; + struct joycon_input_report *report; + + if (unlikely(mutex_is_locked(&ctlr->output_mutex)) && + ctlr->msg_type != JOYCON_MSG_TYPE_NONE) { + switch (ctlr->msg_type) { + case JOYCON_MSG_TYPE_USB: + if (size < 2) + break; + if (data[0] == JC_INPUT_USB_RESPONSE && + data[1] == ctlr->usb_ack_match) + match = true; + break; + case JOYCON_MSG_TYPE_SUBCMD: + if (size < sizeof(struct joycon_input_report) || + data[0] != JC_INPUT_SUBCMD_REPLY) + break; + report = (struct joycon_input_report *)data; + if (report->reply.id == ctlr->subcmd_ack_match) + match = true; + break; + default: + break; + } + + if (match) { + memcpy(ctlr->input_buf, data, + min(size, (int)JC_MAX_RESP_SIZE)); + ctlr->msg_type = JOYCON_MSG_TYPE_NONE; + ctlr->received_resp = true; + wake_up(&ctlr->wait); + + /* This message has been handled */ + return 1; + } + } + + if (ctlr->ctlr_state == JOYCON_CTLR_STATE_READ) + ret = joycon_ctlr_read_handler(ctlr, data, size); + + return ret; +} + +static int nintendo_hid_event(struct hid_device *hdev, + struct hid_report *report, u8 *raw_data, int size) +{ + struct joycon_ctlr *ctlr = hid_get_drvdata(hdev); + + if (size < 1) + return -EINVAL; + + return joycon_ctlr_handle_event(ctlr, raw_data, size); +} + +static int nintendo_hid_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int ret; + struct joycon_ctlr *ctlr; + + hid_dbg(hdev, "probe - start\n"); + + ctlr = devm_kzalloc(&hdev->dev, sizeof(*ctlr), GFP_KERNEL); + if (!ctlr) { + ret = -ENOMEM; + goto err; + } + + ctlr->hdev = hdev; + ctlr->ctlr_state = JOYCON_CTLR_STATE_INIT; + hid_set_drvdata(hdev, ctlr); + mutex_init(&ctlr->output_mutex); + init_waitqueue_head(&ctlr->wait); + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "HID parse failed\n"); + goto err; + } + + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); + if (ret) { + hid_err(hdev, "HW start failed\n"); + goto err; + } + + ret = hid_hw_open(hdev); + if (ret) { + hid_err(hdev, "cannot start hardware I/O\n"); + goto err_stop; + } + + hid_device_io_start(hdev); + + /* Initialize the controller */ + mutex_lock(&ctlr->output_mutex); + /* if handshake command fails, assume ble pro controller */ + if (hdev->product == USB_DEVICE_ID_NINTENDO_PROCON && + !joycon_send_usb(ctlr, JC_USB_CMD_HANDSHAKE)) { + hid_dbg(hdev, "detected USB controller\n"); + /* set baudrate for improved latency */ + ret = joycon_send_usb(ctlr, JC_USB_CMD_BAUDRATE_3M); + if (ret) { + hid_err(hdev, "Failed to set baudrate; ret=%d\n", ret); + goto err_mutex; + } + /* handshake */ + ret = joycon_send_usb(ctlr, JC_USB_CMD_HANDSHAKE); + if (ret) { + hid_err(hdev, "Failed handshake; ret=%d\n", ret); + goto err_mutex; + } + /* + * Set no timeout (to keep controller in USB mode). + * This doesn't send a response, so ignore the timeout. + */ + joycon_send_usb(ctlr, JC_USB_CMD_NO_TIMEOUT); + } + + /* get controller calibration data, and parse it */ + ret = joycon_request_calibration(ctlr); + if (ret) { + /* + * We can function with default calibration, but it may be + * inaccurate. Provide a warning, and continue on. + */ + hid_warn(hdev, "Analog stick positions may be inaccurate\n"); + } + + /* Set the reporting mode to 0x30, which is the full report mode */ + ret = joycon_set_report_mode(ctlr); + if (ret) { + hid_err(hdev, "Failed to set report mode; ret=%d\n", ret); + goto err_mutex; + } + + mutex_unlock(&ctlr->output_mutex); + + ret = joycon_input_create(ctlr); + if (ret) { + hid_err(hdev, "Failed to create input device; ret=%d\n", ret); + goto err_close; + } + + ctlr->ctlr_state = JOYCON_CTLR_STATE_READ; + + hid_dbg(hdev, "probe - success\n"); + return 0; + +err_mutex: + mutex_unlock(&ctlr->output_mutex); +err_close: + hid_hw_close(hdev); +err_stop: + hid_hw_stop(hdev); +err: + hid_err(hdev, "probe - fail = %d\n", ret); + return ret; +} + +static void nintendo_hid_remove(struct hid_device *hdev) +{ + hid_dbg(hdev, "remove\n"); + hid_hw_close(hdev); + hid_hw_stop(hdev); +} + +static const struct hid_device_id nintendo_hid_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_NINTENDO, + USB_DEVICE_ID_NINTENDO_PROCON) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, + USB_DEVICE_ID_NINTENDO_PROCON) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, + USB_DEVICE_ID_NINTENDO_JOYCONL) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, + USB_DEVICE_ID_NINTENDO_JOYCONR) }, + { } +}; +MODULE_DEVICE_TABLE(hid, nintendo_hid_devices); + +static struct hid_driver nintendo_hid_driver = { + .name = "nintendo", + .id_table = nintendo_hid_devices, + .probe = nintendo_hid_probe, + .remove = nintendo_hid_remove, + .raw_event = nintendo_hid_event, +}; +module_hid_driver(nintendo_hid_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Daniel J. Ogorchock "); +MODULE_DESCRIPTION("Driver for Nintendo Switch Controllers"); diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c index e5aafda4f72c99bfaf2a985bd47c09332085558b..746a714fe55671d7d25915133d49bc5252e52e4b 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -178,6 +178,7 @@ static const struct hid_device_id hid_quirks[] = { { HID_USB_DEVICE(USB_VENDOR_ID_QVR5, USB_DEVICE_ID_QVR5), HID_QUIRK_HIDINPUT_FORCE | HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE }, { HID_USB_DEVICE(USB_VENDOR_ID_QVR32A, USB_DEVICE_ID_QVR32A), HID_QUIRK_HIDINPUT_FORCE | HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE }, { HID_USB_DEVICE(USB_VENDOR_ID_NREAL, USB_DEVICE_ID_NREAL), HID_QUIRK_HIDINPUT_FORCE | HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE }, + { HID_USB_DEVICE(USB_VENDOR_ID_XIN_MO, USB_DEVICE_ID_XIN_MO_DUAL_ARCADE), HID_QUIRK_MULTI_INPUT }, { 0 } }; diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c index 8dae0f9b819e011d6695462fea7e88e85cd16669..6286204d4c56029656f6cf68758e75781d9ff0ff 100644 --- a/drivers/hid/hid-steam.c +++ b/drivers/hid/hid-steam.c @@ -768,8 +768,12 @@ static int steam_probe(struct hid_device *hdev, if (steam->quirks & STEAM_QUIRK_WIRELESS) { hid_info(hdev, "Steam wireless receiver connected"); + /* If using a wireless adaptor ask for connection status */ + steam->connected = false; steam_request_conn_status(steam); } else { + /* A wired connection is always present */ + steam->connected = true; ret = steam_register(steam); if (ret) { hid_err(hdev, diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index c7cff929b4190874790602119498e9331537956d..c4ba2d28dd73145b471bb24f2064e04c4881f03b 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -257,13 +257,14 @@ static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t static __poll_t hidraw_poll(struct file *file, poll_table *wait) { struct hidraw_list *list = file->private_data; + __poll_t mask = EPOLLOUT | EPOLLWRNORM; /* hidraw is always writable */ poll_wait(file, &list->hidraw->wait, wait); if (list->head != list->tail) - return EPOLLIN | EPOLLRDNORM; + mask |= EPOLLIN | EPOLLRDNORM; if (!list->hidraw->exist) - return EPOLLERR | EPOLLHUP; - return 0; + mask |= EPOLLERR | EPOLLHUP; + return mask; } static int hidraw_open(struct inode *inode, struct file *file) diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c index 0a39e444e3080422ddd339eeacdb5526266a0b9c..f2c8c59fc5823a795c308a935b181f33cbbd0871 100644 --- a/drivers/hid/i2c-hid/i2c-hid-core.c +++ b/drivers/hid/i2c-hid/i2c-hid-core.c @@ -52,6 +52,8 @@ #define I2C_HID_QUIRK_DELAY_AFTER_SLEEP BIT(3) #define I2C_HID_QUIRK_BOGUS_IRQ BIT(4) #define I2C_HID_QUIRK_RESET_ON_RESUME BIT(5) +#define I2C_HID_QUIRK_BAD_INPUT_SIZE BIT(6) + /* flags */ #define I2C_HID_STARTED 0 @@ -185,6 +187,8 @@ static const struct i2c_hid_quirks { I2C_HID_QUIRK_BOGUS_IRQ }, { USB_VENDOR_ID_ALPS_JP, HID_ANY_ID, I2C_HID_QUIRK_RESET_ON_RESUME }, + { USB_VENDOR_ID_ITE, I2C_DEVICE_ID_ITE_LENOVO_LEGION_Y720, + I2C_HID_QUIRK_BAD_INPUT_SIZE }, { 0, 0 } }; @@ -516,9 +520,15 @@ static void i2c_hid_get_input(struct i2c_hid *ihid) } if ((ret_size > size) || (ret_size < 2)) { - dev_err(&ihid->client->dev, "%s: incomplete report (%d/%d)\n", - __func__, size, ret_size); - return; + if (ihid->quirks & I2C_HID_QUIRK_BAD_INPUT_SIZE) { + ihid->inbuf[0] = size & 0xff; + ihid->inbuf[1] = size >> 8; + ret_size = size; + } else { + dev_err(&ihid->client->dev, "%s: incomplete report (%d/%d)\n", + __func__, size, ret_size); + return; + } } i2c_hid_dbg(ihid, "input: %*ph\n", ret_size, ihid->inbuf); diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 840634e0f1e3cc46235cf456a04e547f2be65ba8..29e63330c1b53213793397b7ec0c1199fc542242 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -769,13 +769,14 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer, static __poll_t uhid_char_poll(struct file *file, poll_table *wait) { struct uhid_device *uhid = file->private_data; + __poll_t mask = EPOLLOUT | EPOLLWRNORM; /* uhid is always writable */ poll_wait(file, &uhid->waitq, wait); if (uhid->head != uhid->tail) - return EPOLLIN | EPOLLRDNORM; + mask |= EPOLLIN | EPOLLRDNORM; - return 0; + return mask; } static const struct file_operations uhid_fops = { diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index 5a949ca42b1d06c4279e08cbe0674f7fa85ef676..c34ef95d7cef338d0dec06f976ab7de7e9b86878 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -254,12 +254,51 @@ static int hiddev_release(struct inode * inode, struct file * file) return 0; } +static int __hiddev_open(struct hiddev *hiddev, struct file *file) +{ + struct hiddev_list *list; + int error; + + lockdep_assert_held(&hiddev->existancelock); + + list = vzalloc(sizeof(*list)); + if (!list) + return -ENOMEM; + + mutex_init(&list->thread_lock); + list->hiddev = hiddev; + + if (!hiddev->open++) { + error = hid_hw_power(hiddev->hid, PM_HINT_FULLON); + if (error < 0) + goto err_drop_count; + + error = hid_hw_open(hiddev->hid); + if (error < 0) + goto err_normal_power; + } + + spin_lock_irq(&hiddev->list_lock); + list_add_tail(&list->node, &hiddev->list); + spin_unlock_irq(&hiddev->list_lock); + + file->private_data = list; + + return 0; + +err_normal_power: + hid_hw_power(hiddev->hid, PM_HINT_NORMAL); +err_drop_count: + hiddev->open--; + vfree(list); + return error; +} + /* * open file op */ static int hiddev_open(struct inode *inode, struct file *file) { - struct hiddev_list *list; struct usb_interface *intf; struct hid_device *hid; struct hiddev *hiddev; @@ -268,66 +307,14 @@ static int hiddev_open(struct inode *inode, struct file *file) intf = usbhid_find_interface(iminor(inode)); if (!intf) return -ENODEV; + hid = usb_get_intfdata(intf); hiddev = hid->hiddev; - if (!(list = vzalloc(sizeof(struct hiddev_list)))) - return -ENOMEM; - mutex_init(&list->thread_lock); - list->hiddev = hiddev; - file->private_data = list; - - /* - * no need for locking because the USB major number - * is shared which usbcore guards against disconnect - */ - if (list->hiddev->exist) { - if (!list->hiddev->open++) { - res = hid_hw_open(hiddev->hid); - if (res < 0) - goto bail; - } - } else { - res = -ENODEV; - goto bail; - } - - spin_lock_irq(&list->hiddev->list_lock); - list_add_tail(&list->node, &hiddev->list); - spin_unlock_irq(&list->hiddev->list_lock); - mutex_lock(&hiddev->existancelock); - /* - * recheck exist with existance lock held to - * avoid opening a disconnected device - */ - if (!list->hiddev->exist) { - res = -ENODEV; - goto bail_unlock; - } - if (!list->hiddev->open++) - if (list->hiddev->exist) { - struct hid_device *hid = hiddev->hid; - res = hid_hw_power(hid, PM_HINT_FULLON); - if (res < 0) - goto bail_unlock; - res = hid_hw_open(hid); - if (res < 0) - goto bail_normal_power; - } - mutex_unlock(&hiddev->existancelock); - return 0; -bail_normal_power: - hid_hw_power(hid, PM_HINT_NORMAL); -bail_unlock: + res = hiddev->exist ? __hiddev_open(hiddev, file) : -ENODEV; mutex_unlock(&hiddev->existancelock); - spin_lock_irq(&list->hiddev->list_lock); - list_del(&list->node); - spin_unlock_irq(&list->hiddev->list_lock); -bail: - file->private_data = NULL; - vfree(list); return res; } @@ -967,9 +954,9 @@ void hiddev_disconnect(struct hid_device *hid) hiddev->exist = 0; if (hiddev->open) { - mutex_unlock(&hiddev->existancelock); hid_hw_close(hiddev->hid); wake_up_interruptible(&hiddev->wait); + mutex_unlock(&hiddev->existancelock); } else { mutex_unlock(&hiddev->existancelock); kfree(hiddev); diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index d2a735ac9ba1d996ec5675b4318f84bc2ffcaa3d..9ca0706a9d4029a79bd14047f8b9de6c91e126f9 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -1215,10 +1215,7 @@ static unsigned int alloc_balloon_pages(struct hv_dynmem_device *dm, unsigned int i = 0; struct page *pg; - if (num_pages < alloc_unit) - return 0; - - for (i = 0; (i * alloc_unit) < num_pages; i++) { + for (i = 0; i < num_pages / alloc_unit; i++) { if (bl_resp->hdr.size + sizeof(union dm_mem_page_range) > PAGE_SIZE) return i * alloc_unit; @@ -1252,7 +1249,7 @@ static unsigned int alloc_balloon_pages(struct hv_dynmem_device *dm, } - return num_pages; + return i * alloc_unit; } static void balloon_up(struct work_struct *dummy) @@ -1267,9 +1264,6 @@ static void balloon_up(struct work_struct *dummy) long avail_pages; unsigned long floor; - /* The host balloons pages in 2M granularity. */ - WARN_ON_ONCE(num_pages % PAGES_IN_2M != 0); - /* * We will attempt 2M allocations. However, if we fail to * allocate 2M chunks, we will go back to 4k allocations. @@ -1279,14 +1273,13 @@ static void balloon_up(struct work_struct *dummy) avail_pages = si_mem_available(); floor = compute_balloon_floor(); - /* Refuse to balloon below the floor, keep the 2M granularity. */ + /* Refuse to balloon below the floor. */ if (avail_pages < num_pages || avail_pages - num_pages < floor) { pr_warn("Balloon request will be partially fulfilled. %s\n", avail_pages < num_pages ? "Not enough memory." : "Balloon floor reached."); num_pages = avail_pages > floor ? (avail_pages - floor) : 0; - num_pages -= num_pages % PAGES_IN_2M; } while (!done) { diff --git a/drivers/hwmon/adt7462.c b/drivers/hwmon/adt7462.c index 19f2a6d48bac99d7232851ba27c8d121ab4007e5..bdd7679fd29873c61f3118b4d6f91091927e50b5 100644 --- a/drivers/hwmon/adt7462.c +++ b/drivers/hwmon/adt7462.c @@ -426,7 +426,7 @@ static int ADT7462_REG_VOLT(struct adt7462_data *data, int which) return 0x95; break; } - return -ENODEV; + return 0; } /* Provide labels for sysfs */ diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index f4c7516eb9891fa923a2a736bfcc45ef9b84a6b9..0a87c5b5128623de65065452695f29ede095a0a9 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -296,9 +296,10 @@ static inline u16 volt2reg(int channel, long volt, u8 bypass_attn) long reg; if (bypass_attn & (1 << channel)) - reg = (volt * 1024) / 2250; + reg = DIV_ROUND_CLOSEST(volt * 1024, 2250); else - reg = (volt * r[1] * 1024) / ((r[0] + r[1]) * 2250); + reg = DIV_ROUND_CLOSEST(volt * r[1] * 1024, + (r[0] + r[1]) * 2250); return clamp_val(reg, 0, 1023) & (0xff << 2); } diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 6b3559f58b67d8861f04add30aa35e191a5f945b..d34de21d43adb9b03d38db6fd76bd7db698e4e40 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -51,6 +51,7 @@ struct hwmon_device_attribute { #define to_hwmon_attr(d) \ container_of(d, struct hwmon_device_attribute, dev_attr) +#define to_dev_attr(a) container_of(a, struct device_attribute, attr) /* * Thermal zone information @@ -58,7 +59,7 @@ struct hwmon_device_attribute { * also provides the sensor index. */ struct hwmon_thermal_data { - struct hwmon_device *hwdev; /* Reference to hwmon device */ + struct device *dev; /* Reference to hwmon device */ int index; /* sensor index */ }; @@ -95,9 +96,27 @@ static const struct attribute_group *hwmon_dev_attr_groups[] = { NULL }; +static void hwmon_free_attrs(struct attribute **attrs) +{ + int i; + + for (i = 0; attrs[i]; i++) { + struct device_attribute *dattr = to_dev_attr(attrs[i]); + struct hwmon_device_attribute *hattr = to_hwmon_attr(dattr); + + kfree(hattr); + } + kfree(attrs); +} + static void hwmon_dev_release(struct device *dev) { - kfree(to_hwmon_device(dev)); + struct hwmon_device *hwdev = to_hwmon_device(dev); + + if (hwdev->group.attrs) + hwmon_free_attrs(hwdev->group.attrs); + kfree(hwdev->groups); + kfree(hwdev); } static struct class hwmon_class = { @@ -121,11 +140,11 @@ static DEFINE_IDA(hwmon_ida); static int hwmon_thermal_get_temp(void *data, int *temp) { struct hwmon_thermal_data *tdata = data; - struct hwmon_device *hwdev = tdata->hwdev; + struct hwmon_device *hwdev = to_hwmon_device(tdata->dev); int ret; long t; - ret = hwdev->chip->ops->read(&hwdev->dev, hwmon_temp, hwmon_temp_input, + ret = hwdev->chip->ops->read(tdata->dev, hwmon_temp, hwmon_temp_input, tdata->index, &t); if (ret < 0) return ret; @@ -139,8 +158,7 @@ static const struct thermal_zone_of_device_ops hwmon_thermal_ops = { .get_temp = hwmon_thermal_get_temp, }; -static int hwmon_thermal_add_sensor(struct device *dev, - struct hwmon_device *hwdev, int index) +static int hwmon_thermal_add_sensor(struct device *dev, int index) { struct hwmon_thermal_data *tdata; struct thermal_zone_device *tzd; @@ -149,10 +167,10 @@ static int hwmon_thermal_add_sensor(struct device *dev, if (!tdata) return -ENOMEM; - tdata->hwdev = hwdev; + tdata->dev = dev; tdata->index = index; - tzd = devm_thermal_zone_of_sensor_register(&hwdev->dev, index, tdata, + tzd = devm_thermal_zone_of_sensor_register(dev, index, tdata, &hwmon_thermal_ops); /* * If CONFIG_THERMAL_OF is disabled, this returns -ENODEV, @@ -164,8 +182,7 @@ static int hwmon_thermal_add_sensor(struct device *dev, return 0; } #else -static int hwmon_thermal_add_sensor(struct device *dev, - struct hwmon_device *hwdev, int index) +static int hwmon_thermal_add_sensor(struct device *dev, int index) { return 0; } @@ -242,8 +259,7 @@ static bool is_string_attr(enum hwmon_sensor_types type, u32 attr) (type == hwmon_fan && attr == hwmon_fan_label); } -static struct attribute *hwmon_genattr(struct device *dev, - const void *drvdata, +static struct attribute *hwmon_genattr(const void *drvdata, enum hwmon_sensor_types type, u32 attr, int index, @@ -271,7 +287,7 @@ static struct attribute *hwmon_genattr(struct device *dev, if ((mode & S_IWUGO) && !ops->write) return ERR_PTR(-EINVAL); - hattr = devm_kzalloc(dev, sizeof(*hattr), GFP_KERNEL); + hattr = kzalloc(sizeof(*hattr), GFP_KERNEL); if (!hattr) return ERR_PTR(-ENOMEM); @@ -478,8 +494,7 @@ static int hwmon_num_channel_attrs(const struct hwmon_channel_info *info) return n; } -static int hwmon_genattrs(struct device *dev, - const void *drvdata, +static int hwmon_genattrs(const void *drvdata, struct attribute **attrs, const struct hwmon_ops *ops, const struct hwmon_channel_info *info) @@ -505,7 +520,7 @@ static int hwmon_genattrs(struct device *dev, attr_mask &= ~BIT(attr); if (attr >= template_size) return -EINVAL; - a = hwmon_genattr(dev, drvdata, info->type, attr, i, + a = hwmon_genattr(drvdata, info->type, attr, i, templates[attr], ops); if (IS_ERR(a)) { if (PTR_ERR(a) != -ENOENT) @@ -519,8 +534,7 @@ static int hwmon_genattrs(struct device *dev, } static struct attribute ** -__hwmon_create_attrs(struct device *dev, const void *drvdata, - const struct hwmon_chip_info *chip) +__hwmon_create_attrs(const void *drvdata, const struct hwmon_chip_info *chip) { int ret, i, aindex = 0, nattrs = 0; struct attribute **attrs; @@ -531,15 +545,17 @@ __hwmon_create_attrs(struct device *dev, const void *drvdata, if (nattrs == 0) return ERR_PTR(-EINVAL); - attrs = devm_kcalloc(dev, nattrs + 1, sizeof(*attrs), GFP_KERNEL); + attrs = kcalloc(nattrs + 1, sizeof(*attrs), GFP_KERNEL); if (!attrs) return ERR_PTR(-ENOMEM); for (i = 0; chip->info[i]; i++) { - ret = hwmon_genattrs(dev, drvdata, &attrs[aindex], chip->ops, + ret = hwmon_genattrs(drvdata, &attrs[aindex], chip->ops, chip->info[i]); - if (ret < 0) + if (ret < 0) { + hwmon_free_attrs(attrs); return ERR_PTR(ret); + } aindex += ret; } @@ -581,14 +597,13 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata, for (i = 0; groups[i]; i++) ngroups++; - hwdev->groups = devm_kcalloc(dev, ngroups, sizeof(*groups), - GFP_KERNEL); + hwdev->groups = kcalloc(ngroups, sizeof(*groups), GFP_KERNEL); if (!hwdev->groups) { err = -ENOMEM; goto free_hwmon; } - attrs = __hwmon_create_attrs(dev, drvdata, chip); + attrs = __hwmon_create_attrs(drvdata, chip); if (IS_ERR(attrs)) { err = PTR_ERR(attrs); goto free_hwmon; @@ -633,8 +648,7 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata, hwmon_temp_input, j)) continue; if (info[i]->config[j] & HWMON_T_INPUT) { - err = hwmon_thermal_add_sensor(dev, - hwdev, j); + err = hwmon_thermal_add_sensor(hdev, j); if (err) { device_unregister(hdev); goto ida_remove; @@ -647,7 +661,7 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata, return hdev; free_hwmon: - kfree(hwdev); + hwmon_dev_release(hdev); ida_remove: ida_simple_remove(&hwmon_ida, id); return ERR_PTR(err); diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index 49f4b33a5685a82530df3149609902fce0db76be..7f28912c9abcfeb1ea379713036b4b009d6560a4 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -165,7 +165,7 @@ static int lm75_write(struct device *dev, enum hwmon_sensor_types type, temp = DIV_ROUND_CLOSEST(temp << (resolution - 8), 1000) << (16 - resolution); - return regmap_write(data->regmap, reg, temp); + return regmap_write(data->regmap, reg, (u16)temp); } static umode_t lm75_is_visible(const void *data, enum hwmon_sensor_types type, diff --git a/drivers/hwmon/nct7802.c b/drivers/hwmon/nct7802.c index 38ffbdb0a85fba288cdcac8e76cc7ff3eadec65d..779ec8fdfae082b640046bc36f4b2eb416e3b128 100644 --- a/drivers/hwmon/nct7802.c +++ b/drivers/hwmon/nct7802.c @@ -32,8 +32,8 @@ static const u8 REG_VOLTAGE[5] = { 0x09, 0x0a, 0x0c, 0x0d, 0x0e }; static const u8 REG_VOLTAGE_LIMIT_LSB[2][5] = { - { 0x40, 0x00, 0x42, 0x44, 0x46 }, - { 0x3f, 0x00, 0x41, 0x43, 0x45 }, + { 0x46, 0x00, 0x40, 0x42, 0x44 }, + { 0x45, 0x00, 0x3f, 0x41, 0x43 }, }; static const u8 REG_VOLTAGE_LIMIT_MSB[5] = { 0x48, 0x00, 0x47, 0x47, 0x48 }; diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c index 93d9a9ea112be6df26bbc9e48acd98236433ae45..edc70ac7d2d094574f0159c5d274108a2e05bd37 100644 --- a/drivers/hwmon/pmbus/ibm-cffps.c +++ b/drivers/hwmon/pmbus/ibm-cffps.c @@ -269,8 +269,8 @@ static int ibm_cffps_read_word_data(struct i2c_client *client, int page, return rc; } -static void ibm_cffps_led_brightness_set(struct led_classdev *led_cdev, - enum led_brightness brightness) +static int ibm_cffps_led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) { int rc; struct ibm_cffps *psu = container_of(led_cdev, struct ibm_cffps, led); @@ -286,9 +286,11 @@ static void ibm_cffps_led_brightness_set(struct led_classdev *led_cdev, rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD, psu->led_state); if (rc < 0) - return; + return rc; led_cdev->brightness = brightness; + + return 0; } static int ibm_cffps_led_blink_set(struct led_classdev *led_cdev, @@ -324,7 +326,7 @@ static void ibm_cffps_create_led_class(struct ibm_cffps *psu) client->addr); psu->led.name = psu->led_name; psu->led.max_brightness = LED_FULL; - psu->led.brightness_set = ibm_cffps_led_brightness_set; + psu->led.brightness_set_blocking = ibm_cffps_led_brightness_set; psu->led.blink_set = ibm_cffps_led_blink_set; rc = devm_led_classdev_register(dev, &psu->led); diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c index 58b789c28b483f062b17822a32487be6bdba14ee..94eea2ac62519625024a5bb0f4340304f65fc8c9 100644 --- a/drivers/hwmon/pmbus/ltc2978.c +++ b/drivers/hwmon/pmbus/ltc2978.c @@ -89,8 +89,8 @@ enum chips { ltc2974, ltc2975, ltc2977, ltc2978, ltc2980, ltc3880, ltc3882, #define LTC_POLL_TIMEOUT 100 /* in milli-seconds */ -#define LTC_NOT_BUSY BIT(5) -#define LTC_NOT_PENDING BIT(4) +#define LTC_NOT_BUSY BIT(6) +#define LTC_NOT_PENDING BIT(5) /* * LTC2978 clears peak data whenever the CLEAR_FAULTS command is executed, which diff --git a/drivers/hwmon/pmbus/tps53679.c b/drivers/hwmon/pmbus/tps53679.c index 85b515cd9df0ee804775d9610d68f8d927525475..2bc352c5357f446d1d7b9f34a779a610a25cb4cd 100644 --- a/drivers/hwmon/pmbus/tps53679.c +++ b/drivers/hwmon/pmbus/tps53679.c @@ -80,7 +80,14 @@ static struct pmbus_driver_info tps53679_info = { static int tps53679_probe(struct i2c_client *client, const struct i2c_device_id *id) { - return pmbus_do_probe(client, id, &tps53679_info); + struct pmbus_driver_info *info; + + info = devm_kmemdup(&client->dev, &tps53679_info, sizeof(*info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + + return pmbus_do_probe(client, id, info); } static const struct i2c_device_id tps53679_id[] = { diff --git a/drivers/hwmon/shtc1.c b/drivers/hwmon/shtc1.c index decd7df995abfa2b40381b7b1be251051b6023e5..2a18539591eaffec4f6e2c70e0a0a313ca77ad4f 100644 --- a/drivers/hwmon/shtc1.c +++ b/drivers/hwmon/shtc1.c @@ -38,7 +38,7 @@ static const unsigned char shtc1_cmd_read_id_reg[] = { 0xef, 0xc8 }; /* constants for reading the ID register */ #define SHTC1_ID 0x07 -#define SHTC1_ID_REG_MASK 0x1f +#define SHTC1_ID_REG_MASK 0x3f /* delays for non-blocking i2c commands, both in us */ #define SHTC1_NONBLOCKING_WAIT_TIME_HPM 14400 diff --git a/drivers/hwmon/w83627hf.c b/drivers/hwmon/w83627hf.c index 8ac89d0781ccc68336eff6f13270063ed69a3ee9..a575e1cdb81a80206a97ca2057c738ed7be17802 100644 --- a/drivers/hwmon/w83627hf.c +++ b/drivers/hwmon/w83627hf.c @@ -130,17 +130,23 @@ superio_select(struct w83627hf_sio_data *sio, int ld) outb(ld, sio->sioaddr + 1); } -static inline void +static inline int superio_enter(struct w83627hf_sio_data *sio) { + if (!request_muxed_region(sio->sioaddr, 2, DRVNAME)) + return -EBUSY; + outb(0x87, sio->sioaddr); outb(0x87, sio->sioaddr); + + return 0; } static inline void superio_exit(struct w83627hf_sio_data *sio) { outb(0xAA, sio->sioaddr); + release_region(sio->sioaddr, 2); } #define W627_DEVID 0x52 @@ -1278,7 +1284,7 @@ static DEVICE_ATTR_RO(name); static int __init w83627hf_find(int sioaddr, unsigned short *addr, struct w83627hf_sio_data *sio_data) { - int err = -ENODEV; + int err; u16 val; static __initconst char *const names[] = { @@ -1290,7 +1296,11 @@ static int __init w83627hf_find(int sioaddr, unsigned short *addr, }; sio_data->sioaddr = sioaddr; - superio_enter(sio_data); + err = superio_enter(sio_data); + if (err) + return err; + + err = -ENODEV; val = force_id ? force_id : superio_inb(sio_data, DEVID); switch (val) { case W627_DEVID: @@ -1644,9 +1654,21 @@ static int w83627thf_read_gpio5(struct platform_device *pdev) struct w83627hf_sio_data *sio_data = dev_get_platdata(&pdev->dev); int res = 0xff, sel; - superio_enter(sio_data); + if (superio_enter(sio_data)) { + /* + * Some other driver reserved the address space for itself. + * We don't want to fail driver instantiation because of that, + * so display a warning and keep going. + */ + dev_warn(&pdev->dev, + "Can not read VID data: Failed to enable SuperIO access\n"); + return res; + } + superio_select(sio_data, W83627HF_LD_GPIO5); + res = 0xff; + /* Make sure these GPIO pins are enabled */ if (!(superio_inb(sio_data, W83627THF_GPIO5_EN) & (1<<3))) { dev_dbg(&pdev->dev, "GPIO5 disabled, no VID function\n"); @@ -1677,7 +1699,17 @@ static int w83687thf_read_vid(struct platform_device *pdev) struct w83627hf_sio_data *sio_data = dev_get_platdata(&pdev->dev); int res = 0xff; - superio_enter(sio_data); + if (superio_enter(sio_data)) { + /* + * Some other driver reserved the address space for itself. + * We don't want to fail driver instantiation because of that, + * so display a warning and keep going. + */ + dev_warn(&pdev->dev, + "Can not read VID data: Failed to enable SuperIO access\n"); + return res; + } + superio_select(sio_data, W83627HF_LD_HWM); /* Make sure these GPIO pins are enabled */ diff --git a/drivers/hwtracing/coresight/coresight-byte-cntr.c b/drivers/hwtracing/coresight/coresight-byte-cntr.c index 6353106a048da5ebac4de902e0c6ec60fe5559d4..36cac86fca1373a268f1791461c819b72d84dd05 100644 --- a/drivers/hwtracing/coresight/coresight-byte-cntr.c +++ b/drivers/hwtracing/coresight/coresight-byte-cntr.c @@ -360,7 +360,7 @@ static int usb_transfer_small_packet(struct qdss_request *usb_req, devm_kfree(tmcdrvdata->dev, usb_req); usb_req = NULL; drvdata->usb_req = NULL; - dev_err(tmcdrvdata->dev, + dev_err_ratelimited(tmcdrvdata->dev, "Write data failed:%d\n", ret); goto out; } @@ -442,7 +442,8 @@ static void usb_read_work_fn(struct work_struct *work) usb_req->sg); devm_kfree(tmcdrvdata->dev, usb_req); usb_req = NULL; - dev_err(tmcdrvdata->dev, "No data in ETR\n"); + dev_err_ratelimited(tmcdrvdata->dev, + "No data in ETR\n"); return; } @@ -473,7 +474,7 @@ static void usb_read_work_fn(struct work_struct *work) devm_kfree(tmcdrvdata->dev, usb_req); usb_req = NULL; drvdata->usb_req = NULL; - dev_err(tmcdrvdata->dev, + dev_err_ratelimited(tmcdrvdata->dev, "Write data failed:%d\n", ret); if (ret == -EAGAIN) continue; @@ -517,6 +518,13 @@ void usb_bypass_notifier(void *priv, unsigned int event, if (!drvdata) return; + if (tmcdrvdata->out_mode != TMC_ETR_OUT_MODE_USB + || tmcdrvdata->mode == CS_MODE_DISABLED) { + dev_err(&tmcdrvdata->csdev->dev, + "%s: ETR is not USB mode, or ETR is disabled.\n", __func__); + return; + } + switch (event) { case USB_QDSS_CONNECT: usb_qdss_alloc_req(ch, USB_BUF_NUM); diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 31fcbc3b7de664c8bc0824d8a68843cc49c19cc8..42ae5ed6fb76ebac96121bbcbb0b00445052b9e8 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -358,9 +358,7 @@ static void *etb_alloc_buffer(struct coresight_device *csdev, int node, cpu = event->cpu; struct cs_buffers *buf; - if (cpu == -1) - cpu = smp_processor_id(); - node = cpu_to_node(cpu); + node = (cpu == -1) ? NUMA_NO_NODE : cpu_to_node(cpu); buf = kzalloc_node(sizeof(struct cs_buffers), GFP_KERNEL, node); if (!buf) diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index a327db8db7cf768397b073d4a4c98edf44f93ed5..98730389318f4dfe0de93cdd4ac95744c2d89efc 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -220,6 +220,8 @@ static void *etm_setup_aux(struct perf_event *event, void **pages, return NULL; INIT_WORK(&event_data->work, free_event_data); + mask = &event_data->mask; + /* First get the selected sink from user space. */ if (event->attr.config2) { id = (u32)event->attr.config2; @@ -228,11 +230,10 @@ static void *etm_setup_aux(struct perf_event *event, void **pages, sink = coresight_get_enabled_sink(true); } - if (!sink) + if (!sink) { + cpumask_clear(mask); goto err; - - mask = &event_data->mask; - + } /* * Setup the path for each CPU in a trace session. We try to build * trace path for each CPU in the mask. If we don't find an ETM diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 1ac9bfd09a544fd96f8ea59b80ff1e57d820210c..d1c0b954053feaaf26ff12a2068a2ef5b1a46b22 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -381,9 +381,7 @@ static void *tmc_alloc_etf_buffer(struct coresight_device *csdev, int node, cpu = event->cpu; struct cs_buffers *buf; - if (cpu == -1) - cpu = smp_processor_id(); - node = cpu_to_node(cpu); + node = (cpu == -1) ? NUMA_NO_NODE : cpu_to_node(cpu); /* Allocate memory structure for interaction with Perf */ buf = kzalloc_node(sizeof(struct cs_buffers), GFP_KERNEL, node); diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 7284e2845d89240c388105a259025eea778a4975..1c0765732ec134a4ce0530148dccac0a2a883df0 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -2168,15 +2168,19 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata) goto out; } + drvdata->reading = true; + /* Disable the TMC if we are trying to read from a running session */ if (drvdata->mode == CS_MODE_SYSFS) { spin_unlock_irqrestore(&drvdata->spinlock, flags); + mutex_unlock(&drvdata->mem_lock); coresight_disable_all_source_link(); + mutex_lock(&drvdata->mem_lock); spin_lock_irqsave(&drvdata->spinlock, flags); __tmc_etr_disable_hw(drvdata); } - drvdata->reading = true; + out: spin_unlock_irqrestore(&drvdata->spinlock, flags); mutex_unlock(&drvdata->mem_lock); @@ -2204,10 +2208,6 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata) * be NULL. */ __tmc_etr_enable_hw(drvdata); - - spin_unlock_irqrestore(&drvdata->spinlock, flags); - coresight_enable_all_source_link(); - spin_lock_irqsave(&drvdata->spinlock, flags); } else { /* * The ETR is not tracing and the buffer was just read. @@ -2224,5 +2224,9 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata) tmc_etr_free_sysfs_buf(sysfs_buf); mutex_unlock(&drvdata->mem_lock); + + if (drvdata->mode == CS_MODE_SYSFS) + coresight_enable_all_source_link(); + return 0; } diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index d8e69f8c9d2d284f1d38387476586e9ea691b22c..5b083e5f1b6e7b93cbef976330faae190dc3a355 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -1199,7 +1199,6 @@ static ssize_t enable_sink_store(struct device *dev, int ret; unsigned long val; struct coresight_device *csdev = to_coresight_device(dev); - struct coresight_device *sink = NULL; ret = kstrtoul(buf, 10, &val); if (ret) @@ -1207,10 +1206,7 @@ static ssize_t enable_sink_store(struct device *dev, mutex_lock(&coresight_mutex); if (val) { - sink = activated_sink ? activated_sink : - coresight_get_enabled_sink(false); - if (sink && strcmp(dev_name(&sink->dev), - dev_name(&csdev->dev))) + if (activated_sink) goto err; csdev->activated = true; } else { @@ -1570,6 +1566,8 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) } for (i = 0; i < csdev->nr_outport; i++) { + if (desc->pdata->child_names[i] == NULL) + continue; conns[i].outport = desc->pdata->outports[i]; conns[i].child_name = desc->pdata->child_names[i]; conns[i].child_port = desc->pdata->child_ports[i]; diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c index 32ac255c2fd84f3f86dd95e7f8ef54710b8b00ad..36c62dc963808ddd2cc38504cf296c58de6232b6 100644 --- a/drivers/hwtracing/coresight/of_coresight.c +++ b/drivers/hwtracing/coresight/of_coresight.c @@ -50,16 +50,22 @@ static void of_coresight_get_ports(const struct device_node *node, { struct device_node *ep = NULL; int in = 0, out = 0; + struct of_endpoint endpoint; do { ep = of_graph_get_next_endpoint(node, ep); if (!ep) break; + if (of_graph_parse_endpoint(ep, &endpoint)) + continue; + if (of_property_read_bool(ep, "slave-mode")) - in++; + in = (endpoint.port + 1 > in) ? + endpoint.port + 1 : in; else - out++; + out = (endpoint.port + 1) > out ? + endpoint.port + 1 : out; } while (ep); diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index ee6dd1b84fac82ca40a902971da6074cc3748e0e..a1c3f0fd7eead405d724eb5b35f6d0e87de2c93e 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -1340,6 +1340,17 @@ config I2C_OPAL This driver can also be built as a module. If so, the module will be called as i2c-opal. +config I2C_MSM_V2 + tristate "I2C_MSM_V2" + depends on ARCH_QCOM + help + If you say yes to this option, support will be included for the + built-in I2C interface and its DMA engine on the MSM family + processors. + + This driver can also be built as a module. If so, the module + will be called i2c-msm-v2. + config I2C_ZX2967 tristate "ZTE ZX2967 I2C support" depends on ARCH_ZX diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 18b26af82b1c5425a9dcec9c61cca3cdff694d60..0da8471672a28f442006524d953a5e37fc026940 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -115,6 +115,7 @@ obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o obj-$(CONFIG_I2C_XLR) += i2c-xlr.o obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o +obj-$(CONFIG_I2C_MSM_V2) += i2c-msm-v2.o obj-$(CONFIG_I2C_ZX2967) += i2c-zx2967.o # External I2C/SMBus adapter drivers diff --git a/drivers/i2c/busses/i2c-altera.c b/drivers/i2c/busses/i2c-altera.c index f5e1941e65b5a2dd6cebfd8a2f60798e258b986b..a1cdcfc74acf6f8d55d23e20c2adb4b92a388784 100644 --- a/drivers/i2c/busses/i2c-altera.c +++ b/drivers/i2c/busses/i2c-altera.c @@ -182,7 +182,7 @@ static void altr_i2c_init(struct altr_i2c_dev *idev) /* SCL Low Time */ writel(t_low, idev->base + ALTR_I2C_SCL_LOW); /* SDA Hold Time, 300ns */ - writel(div_u64(300 * clk_mhz, 1000), idev->base + ALTR_I2C_SDA_HOLD); + writel(3 * clk_mhz / 10, idev->base + ALTR_I2C_SDA_HOLD); /* Mask all master interrupt bits */ altr_i2c_int_enable(idev, ALTR_I2C_ALL_IRQ, false); diff --git a/drivers/i2c/busses/i2c-jz4780.c b/drivers/i2c/busses/i2c-jz4780.c index 30132c3957cdc3d2d76e03a5a82511d9b0a27829..41ca9ff7b5da75a7b418f61c7a87f028c28403c6 100644 --- a/drivers/i2c/busses/i2c-jz4780.c +++ b/drivers/i2c/busses/i2c-jz4780.c @@ -82,25 +82,6 @@ #define JZ4780_I2C_STA_TFNF BIT(1) #define JZ4780_I2C_STA_ACT BIT(0) -static const char * const jz4780_i2c_abrt_src[] = { - "ABRT_7B_ADDR_NOACK", - "ABRT_10ADDR1_NOACK", - "ABRT_10ADDR2_NOACK", - "ABRT_XDATA_NOACK", - "ABRT_GCALL_NOACK", - "ABRT_GCALL_READ", - "ABRT_HS_ACKD", - "SBYTE_ACKDET", - "ABRT_HS_NORSTRT", - "SBYTE_NORSTRT", - "ABRT_10B_RD_NORSTRT", - "ABRT_MASTER_DIS", - "ARB_LOST", - "SLVFLUSH_TXFIFO", - "SLV_ARBLOST", - "SLVRD_INTX", -}; - #define JZ4780_I2C_INTST_IGC BIT(11) #define JZ4780_I2C_INTST_ISTT BIT(10) #define JZ4780_I2C_INTST_ISTP BIT(9) @@ -538,21 +519,8 @@ static irqreturn_t jz4780_i2c_irq(int irqno, void *dev_id) static void jz4780_i2c_txabrt(struct jz4780_i2c *i2c, int src) { - int i; - - dev_err(&i2c->adap.dev, "txabrt: 0x%08x\n", src); - dev_err(&i2c->adap.dev, "device addr=%x\n", - jz4780_i2c_readw(i2c, JZ4780_I2C_TAR)); - dev_err(&i2c->adap.dev, "send cmd count:%d %d\n", - i2c->cmd, i2c->cmd_buf[i2c->cmd]); - dev_err(&i2c->adap.dev, "receive data count:%d %d\n", - i2c->cmd, i2c->data_buf[i2c->cmd]); - - for (i = 0; i < 16; i++) { - if (src & BIT(i)) - dev_dbg(&i2c->adap.dev, "I2C TXABRT[%d]=%s\n", - i, jz4780_i2c_abrt_src[i]); - } + dev_dbg(&i2c->adap.dev, "txabrt: 0x%08x, cmd: %d, send: %d, recv: %d\n", + src, i2c->cmd, i2c->cmd_buf[i2c->cmd], i2c->data_buf[i2c->cmd]); } static inline int jz4780_i2c_xfer_read(struct jz4780_i2c *i2c, diff --git a/drivers/i2c/busses/i2c-msm-v2.c b/drivers/i2c/busses/i2c-msm-v2.c new file mode 100644 index 0000000000000000000000000000000000000000..d6c0f458763e67788c0f50cdd3ff8522a0a21247 --- /dev/null +++ b/drivers/i2c/busses/i2c-msm-v2.c @@ -0,0 +1,3039 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014-2019, 2020, The Linux Foundation. All rights reserved. + */ + +/* + * I2C controller driver for Qualcomm Technologies Inc platforms + */ + +#define pr_fmt(fmt) "#%d " fmt "\n", __LINE__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DEBUG +static const enum msm_i2_debug_level DEFAULT_DBG_LVL = MSM_DBG; +#else +static const enum msm_i2_debug_level DEFAULT_DBG_LVL = MSM_ERR; +#endif + +/* Forward declarations */ +static bool i2c_msm_xfer_next_buf(struct i2c_msm_ctrl *ctrl); +static int i2c_msm_xfer_wait_for_completion(struct i2c_msm_ctrl *ctrl, + struct completion *complete); +static int i2c_msm_pm_resume(struct device *dev); +static void i2c_msm_pm_suspend(struct device *dev); +static void i2c_msm_clk_path_init(struct i2c_msm_ctrl *ctrl); +static struct pinctrl_state * + i2c_msm_rsrcs_gpio_get_state(struct i2c_msm_ctrl *ctrl, + const char *name); +static void i2c_msm_pm_pinctrl_state(struct i2c_msm_ctrl *ctrl, + bool runtime_active); + +/* string table for enum i2c_msm_xfer_mode_id */ +const char * const i2c_msm_mode_str_tbl[] = { + "FIFO", "BLOCK", "DMA", "None", +}; + +static const u32 i2c_msm_fifo_block_sz_tbl[] = {16, 16, 32, 0}; + +/* from enum i2c_msm_xfer_mode_id to qup_io_modes register values */ +static const u32 i2c_msm_mode_to_reg_tbl[] = { + 0x0, /* map I2C_MSM_XFER_MODE_FIFO -> binary 00 */ + 0x1, /* map I2C_MSM_XFER_MODE_BLOCK -> binary 01 */ + 0x3 /* map I2C_MSM_XFER_MODE_DMA -> binary 11 */ +}; + +const char *i2c_msm_err_str_table[] = { + [I2C_MSM_NO_ERR] = "NONE", + [I2C_MSM_ERR_NACK] = "NACK: slave not responding, ensure its powered", + [I2C_MSM_ERR_ARB_LOST] = "ARB_LOST", + [I2C_MSM_ERR_BUS_ERR] = "BUS ERROR:noisy bus/unexpected start/stop tag", + [I2C_MSM_ERR_TIMEOUT] = "TIMEOUT_ERROR", + [I2C_MSM_ERR_CORE_CLK] = "CLOCK OFF: Check Core Clock", + [I2C_MSM_ERR_OVR_UNDR_RUN] = "OVER_UNDER_RUN_ERROR", +}; + +static void i2c_msm_dbg_dump_diag(struct i2c_msm_ctrl *ctrl, + bool use_param_vals, u32 status, u32 qup_op) +{ + struct i2c_msm_xfer *xfer = &ctrl->xfer; + const char *str = i2c_msm_err_str_table[xfer->err]; + char buf[I2C_MSM_REG_2_STR_BUF_SZ]; + + if (!use_param_vals) { + void __iomem *base = ctrl->rsrcs.base; + + status = readl_relaxed(base + QUP_I2C_STATUS); + qup_op = readl_relaxed(base + QUP_OPERATIONAL); + } + + if (xfer->err == I2C_MSM_ERR_TIMEOUT) { + /* + * if we are not the bus master or SDA/SCL is low then it may be + * that slave is pulling the lines low. Otherwise it is likely a + * GPIO issue + */ + if (!(status & QUP_BUS_MASTER)) + snprintf(buf, I2C_MSM_REG_2_STR_BUF_SZ, + "%s(val:%dmsec) misconfigured GPIO or slave pulling bus line(s) low\n", + str, jiffies_to_msecs(xfer->timeout)); + else + snprintf(buf, I2C_MSM_REG_2_STR_BUF_SZ, + "%s(val:%dmsec)", str, jiffies_to_msecs(xfer->timeout)); + + str = buf; + } + + /* dump xfer details */ + dev_err(ctrl->dev, + "%s: msgs(n:%d cur:%d %s) bc(rx:%zu tx:%zu) mode:%s slv_addr:0x%0x MSTR_STS:0x%08x OPER:0x%08x\n", + str, xfer->msg_cnt, xfer->cur_buf.msg_idx, + xfer->cur_buf.is_rx ? "rx" : "tx", xfer->rx_cnt, xfer->tx_cnt, + i2c_msm_mode_str_tbl[xfer->mode_id], xfer->msgs->addr, + status, qup_op); +} + +static u32 i2c_msm_reg_io_modes_out_blk_sz(u32 qup_io_modes) +{ + return i2c_msm_fifo_block_sz_tbl[qup_io_modes & 0x3]; +} + +static u32 i2c_msm_reg_io_modes_in_blk_sz(u32 qup_io_modes) +{ + return i2c_msm_fifo_block_sz_tbl[BITS_AT(qup_io_modes, 5, 2)]; +} + +static const u32 i2c_msm_fifo_sz_table[] = {2, 4, 8, 16}; + +static void i2c_msm_qup_fifo_calc_size(struct i2c_msm_ctrl *ctrl) +{ + u32 reg_data, output_fifo_size, input_fifo_size; + struct i2c_msm_xfer_mode_fifo *fifo = &ctrl->xfer.fifo; + + /* Gurad to read fifo size only once. It hard wired and never changes */ + if (fifo->input_fifo_sz && fifo->output_fifo_sz) + return; + + reg_data = readl_relaxed(ctrl->rsrcs.base + QUP_IO_MODES); + output_fifo_size = BITS_AT(reg_data, 2, 2); + input_fifo_size = BITS_AT(reg_data, 7, 2); + + fifo->input_fifo_sz = i2c_msm_reg_io_modes_in_blk_sz(reg_data) * + i2c_msm_fifo_sz_table[input_fifo_size]; + fifo->output_fifo_sz = i2c_msm_reg_io_modes_out_blk_sz(reg_data) * + i2c_msm_fifo_sz_table[output_fifo_size]; + + i2c_msm_dbg(ctrl, MSM_PROF, "QUP input-sz:%zu, input-sz:%zu\n", + fifo->input_fifo_sz, fifo->output_fifo_sz); + +} + +/* + * i2c_msm_tag_byte: accessor for tag as four bytes array + */ +static u8 *i2c_msm_tag_byte(struct i2c_msm_tag *tag, int byte_n) +{ + return ((u8 *)tag) + byte_n; +} + +/* + * i2c_msm_buf_to_ptr: translates a xfer buf to a pointer into the i2c_msg data + */ +static u8 *i2c_msm_buf_to_ptr(struct i2c_msm_xfer_buf *buf) +{ + struct i2c_msm_xfer *xfer = + container_of(buf, struct i2c_msm_xfer, cur_buf); + struct i2c_msg *msg = xfer->msgs + buf->msg_idx; + + return msg->buf + buf->byte_idx; +} + +/* + * tag_lookup_table[is_new_addr][is_last][is_rx] + * @is_new_addr Is start tag required? (which requires two more bytes.) + * @is_last Use the XXXXX_N_STOP tag variant + * @is_rx READ/WRITE + */ +static const struct i2c_msm_tag tag_lookup_table[2][2][2] = { + {{{QUP_TAG2_DATA_WRITE, 2}, + {QUP_TAG2_DATA_READ, 2} }, + /* last buffer */ + {{QUP_TAG2_DATA_WRITE_N_STOP, 2}, + {QUP_TAG2_DATA_READ_N_STOP, 2} } }, + /* new addr */ + {{{QUP_TAG2_START | (QUP_TAG2_DATA_WRITE << 16), 4}, + {QUP_TAG2_START | (QUP_TAG2_DATA_READ << 16), 4} }, + /* last buffer + new addr */ + {{QUP_TAG2_START | (QUP_TAG2_DATA_WRITE_N_STOP << 16), 4}, + {QUP_TAG2_START | (QUP_TAG2_DATA_READ_N_STOP << 16), 4} } }, +}; + +/* + * i2c_msm_tag_create: format a qup tag ver2 + */ +static struct i2c_msm_tag i2c_msm_tag_create(bool is_new_addr, bool is_last_buf, + bool is_rx, u8 buf_len, u8 slave_addr) +{ + struct i2c_msm_tag tag; + /* Normalize booleans to 1 or 0 */ + is_new_addr = is_new_addr ? 1 : 0; + is_last_buf = is_last_buf ? 1 : 0; + is_rx = is_rx ? 1 : 0; + + tag = tag_lookup_table[is_new_addr][is_last_buf][is_rx]; + /* fill in the non-const value: the address and the length */ + if (tag.len == I2C_MSM_TAG2_MAX_LEN) { + *i2c_msm_tag_byte(&tag, 1) = slave_addr; + *i2c_msm_tag_byte(&tag, 3) = buf_len; + } else { + *i2c_msm_tag_byte(&tag, 1) = buf_len; + } + + return tag; +} + +static int +i2c_msm_qup_state_wait_valid(struct i2c_msm_ctrl *ctrl, + enum i2c_msm_qup_state state, bool only_valid) +{ + u32 status; + void __iomem *base = ctrl->rsrcs.base; + int ret = 0; + int read_cnt = 0; + + do { + status = readl_relaxed(base + QUP_STATE); + ++read_cnt; + + /* + * If only valid bit needs to be checked, requested state is + * 'don't care' + */ + if (status & QUP_STATE_VALID) { + if (only_valid) + goto poll_valid_end; + else if ((state & QUP_I2C_MAST_GEN) && + (status & QUP_I2C_MAST_GEN)) + goto poll_valid_end; + else if ((status & QUP_STATE_MASK) == state) + goto poll_valid_end; + } + + /* + * Sleeping for 1-1.5 ms for every 100 iterations and break if + * iterations crosses the 1500 marks allows roughly 10-15 msec + * of time to get the core to valid state. + */ + if (!(read_cnt % 100)) + usleep_range(1000, 1500); + } while (read_cnt <= 1500); + + ret = -ETIMEDOUT; + dev_err(ctrl->dev, + "error timeout on polling for valid state. check core_clk\n"); + +poll_valid_end: + if (!only_valid) + i2c_msm_prof_evnt_add(ctrl, MSM_DBG, I2C_MSM_VALID_END, + /* aggregate ret and state */ + (((-ret) & 0xff) | ((state & 0xf) << 16)), + read_cnt, status); + + return ret; +} + +static int i2c_msm_qup_state_set(struct i2c_msm_ctrl *ctrl, + enum i2c_msm_qup_state state) +{ + if (i2c_msm_qup_state_wait_valid(ctrl, 0, true)) + return -EIO; + + writel_relaxed(state, ctrl->rsrcs.base + QUP_STATE); + + if (i2c_msm_qup_state_wait_valid(ctrl, state, false)) + return -EIO; + + return 0; +} + +static int i2c_msm_qup_sw_reset(struct i2c_msm_ctrl *ctrl) +{ + int ret; + + writel_relaxed(1, ctrl->rsrcs.base + QUP_SW_RESET); + /* + * Ensure that QUP that reset state is written before waiting for a the + * reset state to be valid. + */ + wmb(); + ret = i2c_msm_qup_state_wait_valid(ctrl, QUP_STATE_RESET, false); + if (ret) { + if (atomic_read(&ctrl->xfer.is_active)) + ctrl->xfer.err = I2C_MSM_ERR_CORE_CLK; + dev_err(ctrl->dev, "error on issuing QUP software-reset\n"); + } + return ret; +} + +/* + * i2c_msm_qup_xfer_init_reset_state: setup QUP registers for the next run state + * @pre QUP must be in reset state. + * @pre xfer->mode_id is set to the chosen transfer state + * @post update values in QUP_MX_*_COUNT, QUP_CONFIG, QUP_IO_MODES, + * and QUP_OPERATIONAL_MASK registers + */ +static void +i2c_msm_qup_xfer_init_reset_state(struct i2c_msm_ctrl *ctrl) +{ + struct i2c_msm_xfer *xfer = &ctrl->xfer; + void __iomem * const base = ctrl->rsrcs.base; + u32 mx_rd_cnt = 0; + u32 mx_wr_cnt = 0; + u32 mx_in_cnt = 0; + u32 mx_out_cnt = 0; + u32 no_input = 0; + u32 no_output = 0; + u32 input_mode = i2c_msm_mode_to_reg_tbl[xfer->mode_id] << 12; + u32 output_mode = i2c_msm_mode_to_reg_tbl[xfer->mode_id] << 10; + u32 config_reg; + u32 io_modes_reg; + u32 op_mask; + u32 rx_cnt = 0; + u32 tx_cnt = 0; + /* + * DMA mode: + * 1. QUP_MX_*_COUNT must be zero in all cases. + * 2. both QUP_NO_INPUT and QUP_NO_OUTPUT are unset. + * FIFO mode: + * 1. QUP_MX_INPUT_COUNT and QUP_MX_OUTPUT_COUNT are zero + * 2. QUP_MX_READ_COUNT and QUP_MX_WRITE_COUNT reflect true count + * 3. QUP_NO_INPUT and QUP_NO_OUTPUT are set according to counts + */ + if (xfer->mode_id != I2C_MSM_XFER_MODE_DMA) { + rx_cnt = xfer->rx_cnt + xfer->rx_ovrhd_cnt; + tx_cnt = xfer->tx_cnt + xfer->tx_ovrhd_cnt; + no_input = rx_cnt ? 0 : QUP_NO_INPUT; + + switch (xfer->mode_id) { + case I2C_MSM_XFER_MODE_FIFO: + mx_rd_cnt = rx_cnt; + mx_wr_cnt = tx_cnt; + break; + case I2C_MSM_XFER_MODE_BLOCK: + mx_in_cnt = rx_cnt; + mx_out_cnt = tx_cnt; + break; + default: + break; + } + } + + /* init DMA/BLOCK modes counter */ + writel_relaxed(mx_in_cnt, base + QUP_MX_INPUT_COUNT); + writel_relaxed(mx_out_cnt, base + QUP_MX_OUTPUT_COUNT); + + /* int FIFO mode counter */ + writel_relaxed(mx_rd_cnt, base + QUP_MX_READ_COUNT); + writel_relaxed(mx_wr_cnt, base + QUP_MX_WRITE_COUNT); + + /* + * Set QUP mini-core to I2C tags ver-2 + * sets NO_INPUT / NO_OUTPUT as needed + */ + config_reg = readl_relaxed(base + QUP_CONFIG); + config_reg &= + ~(QUP_NO_INPUT | QUP_NO_OUTPUT | QUP_N_MASK | QUP_MINI_CORE_MASK); + config_reg |= (no_input | no_output | QUP_N_VAL | + QUP_MINI_CORE_I2C_VAL); + writel_relaxed(config_reg, base + QUP_CONFIG); + + /* + * Turns-on packing/unpacking + * sets NO_INPUT / NO_OUTPUT as needed + */ + io_modes_reg = readl_relaxed(base + QUP_IO_MODES); + io_modes_reg &= + ~(QUP_INPUT_MODE | QUP_OUTPUT_MODE | QUP_PACK_EN | QUP_UNPACK_EN + | QUP_OUTPUT_BIT_SHIFT_EN); + io_modes_reg |= + (input_mode | output_mode | QUP_PACK_EN | QUP_UNPACK_EN); + writel_relaxed(io_modes_reg, base + QUP_IO_MODES); + + /* + * mask INPUT and OUTPUT service flags in to prevent IRQs on FIFO status + * change on DMA-mode transfers + */ + op_mask = (xfer->mode_id == I2C_MSM_XFER_MODE_DMA) ? + (QUP_INPUT_SERVICE_MASK | QUP_OUTPUT_SERVICE_MASK) : 0; + writel_relaxed(op_mask, base + QUP_OPERATIONAL_MASK); + /* Ensure that QUP configuration is written before leaving this func */ + wmb(); +} + +/* + * i2c_msm_clk_div_fld: + * @clk_freq_out output clock frequency + * @fs_div fs divider value + * @ht_div high time divider value + */ +struct i2c_msm_clk_div_fld { + u32 clk_freq_out; + u8 fs_div; + u8 ht_div; +}; + +/* + * divider values as per HW Designers + */ +static struct i2c_msm_clk_div_fld i2c_msm_clk_div_map[] = { + {KHz(100), 124, 62}, + {KHz(400), 28, 14}, + {KHz(1000), 8, 5}, +}; + +/* + * @return zero on success + * @fs_div when zero use value from table above, otherwise use given value + * @ht_div when zero use value from table above, otherwise use given value + * + * Format the value to be configured into the clock divider register. This + * register is configured every time core is moved from reset to run state. + */ +static int i2c_msm_set_mstr_clk_ctl(struct i2c_msm_ctrl *ctrl, int fs_div, + int ht_div, int noise_rjct_scl, int noise_rjct_sda) +{ + int ret = 0; + int i; + u32 reg_val = 0; + struct i2c_msm_clk_div_fld *itr = i2c_msm_clk_div_map; + + /* set noise rejection values for scl and sda */ + reg_val = I2C_MSM_SCL_NOISE_REJECTION(reg_val, noise_rjct_scl); + reg_val = I2C_MSM_SDA_NOISE_REJECTION(reg_val, noise_rjct_sda); + + /* + * find matching freq and set divider values unless they are forced + * from parametr list + */ + for (i = 0; i < ARRAY_SIZE(i2c_msm_clk_div_map); ++i, ++itr) { + if (ctrl->rsrcs.clk_freq_out == itr->clk_freq_out) { + if (!fs_div) + fs_div = itr->fs_div; + if (!ht_div) + ht_div = itr->ht_div; + break; + } + } + + /* For non-standard clock freq, clk divider value + * fs_div should be supplied by client through device tree + */ + if (!fs_div) { + dev_err(ctrl->dev, "Missing clk divider value in DT for %dKHz\n", + (ctrl->rsrcs.clk_freq_out / 1000)); + return -EINVAL; + } + + /* format values in clk-ctl cache */ + ctrl->mstr_clk_ctl = (reg_val & (~0xff07ff)) | ((ht_div & 0xff) << 16) + |(fs_div & 0xff); + + return ret; +} + +/* + * i2c_msm_qup_xfer_init_run_state: set qup regs which must be set *after* reset + */ +static void i2c_msm_qup_xfer_init_run_state(struct i2c_msm_ctrl *ctrl) +{ + void __iomem *base = ctrl->rsrcs.base; + + writel_relaxed(ctrl->mstr_clk_ctl, base + QUP_I2C_MASTER_CLK_CTL); + + /* Ensure that QUP configuration is written before leaving this func */ + wmb(); + + if (ctrl->dbgfs.dbg_lvl == MSM_DBG) { + dev_info(ctrl->dev, + "QUP state after programming for next transfers\n"); + i2c_msm_dbg_qup_reg_dump(ctrl); + } +} + +static void i2c_msm_fifo_wr_word(struct i2c_msm_ctrl *ctrl, u32 data) +{ + writel_relaxed(data, ctrl->rsrcs.base + QUP_OUT_FIFO_BASE); + i2c_msm_dbg(ctrl, MSM_DBG, "OUT-FIFO:0x%08x\n", data); +} + +static u32 i2c_msm_fifo_rd_word(struct i2c_msm_ctrl *ctrl, u32 *data) +{ + u32 val; + + val = readl_relaxed(ctrl->rsrcs.base + QUP_IN_FIFO_BASE); + i2c_msm_dbg(ctrl, MSM_DBG, "IN-FIFO :0x%08x\n", val); + + if (data) + *data = val; + + return val; +} + +/* + * i2c_msm_fifo_wr_buf_flush: + */ +static void i2c_msm_fifo_wr_buf_flush(struct i2c_msm_ctrl *ctrl) +{ + struct i2c_msm_xfer_mode_fifo *fifo = &ctrl->xfer.fifo; + u32 *word; + + if (!fifo->out_buf_idx) + return; + + word = (u32 *) fifo->out_buf; + i2c_msm_fifo_wr_word(ctrl, *word); + fifo->out_buf_idx = 0; + *word = 0; +} + +/* + * i2c_msm_fifo_wr_buf: + * + * @len buf size (in bytes) + * @return number of bytes from buf which have been processed (written to + * FIFO or kept in out buffer and will be written later) + */ +static size_t +i2c_msm_fifo_wr_buf(struct i2c_msm_ctrl *ctrl, u8 *buf, size_t len) +{ + struct i2c_msm_xfer_mode_fifo *fifo = &ctrl->xfer.fifo; + int i; + + for (i = 0 ; i < len; ++i, ++buf) { + + fifo->out_buf[fifo->out_buf_idx] = *buf; + ++fifo->out_buf_idx; + + if (fifo->out_buf_idx == 4) { + u32 *word = (u32 *) fifo->out_buf; + + i2c_msm_fifo_wr_word(ctrl, *word); + fifo->out_buf_idx = 0; + *word = 0; + } + } + return i; +} + +static size_t i2c_msm_fifo_xfer_wr_tag(struct i2c_msm_ctrl *ctrl) +{ + struct i2c_msm_xfer_buf *buf = &ctrl->xfer.cur_buf; + size_t len = 0; + + if (ctrl->dbgfs.dbg_lvl >= MSM_DBG) { + char str[I2C_MSM_REG_2_STR_BUF_SZ]; + + dev_info(ctrl->dev, "tag.val:0x%llx tag.len:%d %s\n", + buf->out_tag.val, buf->out_tag.len, + i2c_msm_dbg_tag_to_str(&buf->out_tag, str, + sizeof(str))); + } + + if (buf->out_tag.len) { + len = i2c_msm_fifo_wr_buf(ctrl, (u8 *) &buf->out_tag.val, + buf->out_tag.len); + + if (len < buf->out_tag.len) + goto done; + + buf->out_tag = (struct i2c_msm_tag) {0}; + } +done: + return len; +} + +/* + * i2c_msm_fifo_read: reads up to fifo size into user's buf + */ +static void i2c_msm_fifo_read_xfer_buf(struct i2c_msm_ctrl *ctrl) +{ + struct i2c_msm_xfer_buf *buf = &ctrl->xfer.cur_buf; + struct i2c_msg *msg = ctrl->xfer.msgs + buf->msg_idx; + u8 *p_tag_val = (u8 *) &buf->in_tag.val; + int buf_need_bc = msg->len - buf->byte_idx; + u8 word[4]; + int copy_bc; + int word_idx; + int word_bc; + + if (!buf->is_rx) + return; + + while (buf_need_bc || buf->in_tag.len) { + i2c_msm_fifo_rd_word(ctrl, (u32 *) word); + word_bc = sizeof(word); + word_idx = 0; + + /* + * copy bytes from fifo word to tag. + * @note buf->in_tag.len (max 2bytes) < word_bc (4bytes) + */ + if (buf->in_tag.len) { + copy_bc = min_t(int, word_bc, buf->in_tag.len); + + memcpy(p_tag_val + buf->in_tag.len, word, copy_bc); + + word_idx += copy_bc; + word_bc -= copy_bc; + buf->in_tag.len -= copy_bc; + + if ((ctrl->dbgfs.dbg_lvl >= MSM_DBG) && + !buf->in_tag.len) { + char str[64]; + + dev_info(ctrl->dev, "%s\n", + i2c_msm_dbg_tag_to_str(&buf->in_tag, + str, sizeof(str))); + } + } + + /* copy bytes from fifo word to user's buffer */ + copy_bc = min_t(int, word_bc, buf_need_bc); + memcpy(msg->buf + buf->byte_idx, word + word_idx, copy_bc); + + buf->byte_idx += copy_bc; + buf_need_bc -= copy_bc; + } +} + +/* + * i2c_msm_fifo_write_xfer_buf: write xfer.cur_buf (user's-buf + tag) to fifo + */ +static void i2c_msm_fifo_write_xfer_buf(struct i2c_msm_ctrl *ctrl) +{ + struct i2c_msm_xfer_buf *buf = &ctrl->xfer.cur_buf; + size_t len; + size_t tag_len; + + tag_len = buf->out_tag.len; + len = i2c_msm_fifo_xfer_wr_tag(ctrl); + if (len < tag_len) { + dev_err(ctrl->dev, "error on writing tag to out FIFO\n"); + return; + } + + if (!buf->is_rx) { + if (ctrl->dbgfs.dbg_lvl >= MSM_DBG) { + char str[I2C_MSM_REG_2_STR_BUF_SZ]; + int offset = 0; + u8 *p = i2c_msm_buf_to_ptr(buf); + int i; + + for (i = 0 ; i < len; ++i, ++p) + offset += scnprintf(str + offset, + sizeof(str) - offset, + "0x%x ", *p); + dev_info(ctrl->dev, "data: %s\n", str); + } + + len = i2c_msm_fifo_wr_buf(ctrl, i2c_msm_buf_to_ptr(buf), + buf->len); + if (len < buf->len) + dev_err(ctrl->dev, "error on xfering buf with FIFO\n"); + } +} + +/* + * i2c_msm_fifo_xfer_process: + * + * @pre transfer size is less then or equal to fifo size. + * @pre QUP in run state/pause + * @return zero on success + */ +static int i2c_msm_fifo_xfer_process(struct i2c_msm_ctrl *ctrl) +{ + struct i2c_msm_xfer_buf first_buf = ctrl->xfer.cur_buf; + int ret; + + /* load fifo while in pause state to avoid race conditions */ + ret = i2c_msm_qup_state_set(ctrl, QUP_STATE_PAUSE); + if (ret < 0) + return ret; + + /* write all that goes to output fifo */ + while (i2c_msm_xfer_next_buf(ctrl)) + i2c_msm_fifo_write_xfer_buf(ctrl); + + i2c_msm_fifo_wr_buf_flush(ctrl); + + ctrl->xfer.cur_buf = first_buf; + + ret = i2c_msm_qup_state_set(ctrl, QUP_STATE_RUN); + if (ret < 0) + return ret; + + /* wait for input done interrupt */ + ret = i2c_msm_xfer_wait_for_completion(ctrl, &ctrl->xfer.complete); + if (ret < 0) + return ret; + + /* read all from input fifo */ + while (i2c_msm_xfer_next_buf(ctrl)) + i2c_msm_fifo_read_xfer_buf(ctrl); + + return 0; +} + +/* + * i2c_msm_fifo_xfer: process transfer using fifo mode + */ +static int i2c_msm_fifo_xfer(struct i2c_msm_ctrl *ctrl) +{ + int ret; + + i2c_msm_dbg(ctrl, MSM_DBG, "Starting FIFO transfer\n"); + + ret = i2c_msm_qup_state_set(ctrl, QUP_STATE_RESET); + if (ret < 0) + return ret; + + /* program qup registers */ + i2c_msm_qup_xfer_init_reset_state(ctrl); + + ret = i2c_msm_qup_state_set(ctrl, QUP_STATE_RUN); + if (ret < 0) + return ret; + + /* program qup registers which must be set *after* reset */ + i2c_msm_qup_xfer_init_run_state(ctrl); + + ret = i2c_msm_fifo_xfer_process(ctrl); + + return ret; +} + +/* + * i2c_msm_blk_init_struct: Allocate memory and initialize blk structure + * + * @return 0 on success or error code + */ +static int i2c_msm_blk_init_struct(struct i2c_msm_ctrl *ctrl) +{ + u32 reg_data = readl_relaxed(ctrl->rsrcs.base + QUP_IO_MODES); + int ret; + struct i2c_msm_xfer_mode_blk *blk = &ctrl->xfer.blk; + + blk->in_blk_sz = i2c_msm_reg_io_modes_in_blk_sz(reg_data), + blk->out_blk_sz = i2c_msm_reg_io_modes_out_blk_sz(reg_data), + + blk->tx_cache = kmalloc(blk->out_blk_sz, GFP_KERNEL); + if (!blk->tx_cache) { + ret = -ENOMEM; + goto out_buf_err; + } + + blk->rx_cache = kmalloc(blk->in_blk_sz, GFP_KERNEL); + if (!blk->tx_cache) { + ret = -ENOMEM; + goto in_buf_err; + } + + blk->is_init = true; + return 0; + +in_buf_err: + kfree(blk->tx_cache); +out_buf_err: + + return ret; +} + +/* + * i2c_msm_blk_wr_flush: flushes internal cached block to FIFO + * + * @return 0 on success or error code + */ +static int i2c_msm_blk_wr_flush(struct i2c_msm_ctrl *ctrl) +{ + int byte_num; + int ret = 0; + struct i2c_msm_xfer_mode_blk *blk = &ctrl->xfer.blk; + u32 *buf_u32_ptr; + + if (!blk->tx_cache_idx) + return 0; + + /* if no blocks available wait for interrupt */ + ret = i2c_msm_xfer_wait_for_completion(ctrl, &blk->wait_tx_blk); + if (ret) + return ret; + + /* + * pause the controller until we finish loading the block in order to + * avoid race conditions + */ + ret = i2c_msm_qup_state_set(ctrl, QUP_STATE_PAUSE); + if (ret < 0) + return ret; + i2c_msm_dbg(ctrl, MSM_DBG, "OUT-BLK:%*phC\n", blk->tx_cache_idx, + blk->tx_cache); + + for (byte_num = 0; byte_num < blk->tx_cache_idx; + byte_num += sizeof(u32)) { + buf_u32_ptr = (u32 *) (blk->tx_cache + byte_num); + writel_relaxed(*buf_u32_ptr, + ctrl->rsrcs.base + QUP_OUT_FIFO_BASE); + *buf_u32_ptr = 0; + } + + /* now cache is empty */ + blk->tx_cache_idx = 0; + ret = i2c_msm_qup_state_set(ctrl, QUP_STATE_RUN); + if (ret < 0) + return ret; + + return ret; +} + +/* + * i2c_msm_blk_wr_buf: + * + * @len buf size (in bytes) + * @return number of bytes from buf which have been processed (written to + * FIFO or kept in out buffer and will be written later) + */ +static int +i2c_msm_blk_wr_buf(struct i2c_msm_ctrl *ctrl, const u8 *buf, int len) +{ + struct i2c_msm_xfer_mode_blk *blk = &ctrl->xfer.blk; + int byte_num; + int ret = 0; + + for (byte_num = 0; byte_num < len; ++byte_num, ++buf) { + blk->tx_cache[blk->tx_cache_idx] = *buf; + ++blk->tx_cache_idx; + + /* flush cached buffer to HW FIFO when full */ + if (blk->tx_cache_idx == blk->out_blk_sz) { + ret = i2c_msm_blk_wr_flush(ctrl); + if (ret) + return ret; + } + } + return byte_num; +} + +/* + * i2c_msm_blk_xfer_wr_tag: buffered writing the tag of current buf + * @return zero on success + */ +static int i2c_msm_blk_xfer_wr_tag(struct i2c_msm_ctrl *ctrl) +{ + struct i2c_msm_xfer_buf *buf = &ctrl->xfer.cur_buf; + int len = 0; + + if (!buf->out_tag.len) + return 0; + + len = i2c_msm_blk_wr_buf(ctrl, (u8 *) &buf->out_tag.val, + buf->out_tag.len); + if (len != buf->out_tag.len) + return -EFAULT; + + buf->out_tag = (struct i2c_msm_tag) {0}; + return 0; +} + +/* + * i2c_msm_blk_wr_xfer_buf: writes ctrl->xfer.cur_buf to HW + * + * @return zero on success + */ +static int i2c_msm_blk_wr_xfer_buf(struct i2c_msm_ctrl *ctrl) +{ + struct i2c_msm_xfer_buf *buf = &ctrl->xfer.cur_buf; + int len; + int ret; + + ret = i2c_msm_blk_xfer_wr_tag(ctrl); + if (ret) + return ret; + + len = i2c_msm_blk_wr_buf(ctrl, i2c_msm_buf_to_ptr(buf), buf->len); + if (len < buf->len) + return -EFAULT; + + buf->byte_idx += len; + return 0; +} + +/* + * i2c_msm_blk_rd_blk: read a block from HW FIFO to internal cache + * + * @return number of bytes read or negative error value + * @need_bc number of bytes that we need + * + * uses internal counter to keep track of number of available blocks. When + * zero, waits for interrupt. + */ +static int i2c_msm_blk_rd_blk(struct i2c_msm_ctrl *ctrl, int need_bc) +{ + int byte_num; + int ret = 0; + struct i2c_msm_xfer_mode_blk *blk = &ctrl->xfer.blk; + u32 *cache_ptr = (u32 *) blk->rx_cache; + int read_bc = min_t(int, blk->in_blk_sz, need_bc); + + /* wait for block avialble interrupt */ + ret = i2c_msm_xfer_wait_for_completion(ctrl, &blk->wait_rx_blk); + if (ret) + return ret; + + /* Read block from HW to cache */ + for (byte_num = 0; byte_num < blk->in_blk_sz; + byte_num += sizeof(u32)) { + if (byte_num < read_bc) { + *cache_ptr = readl_relaxed(ctrl->rsrcs.base + + QUP_IN_FIFO_BASE); + ++cache_ptr; + } + } + blk->rx_cache_idx = 0; + return read_bc; +} + +/* + * i2c_msm_blk_rd_xfer_buf: fill in ctrl->xfer.cur_buf from HW + * + * @return zero on success + */ +static int i2c_msm_blk_rd_xfer_buf(struct i2c_msm_ctrl *ctrl) +{ + struct i2c_msm_xfer_mode_blk *blk = &ctrl->xfer.blk; + struct i2c_msm_xfer_buf *buf = &ctrl->xfer.cur_buf; + struct i2c_msg *msg = ctrl->xfer.msgs + buf->msg_idx; + int copy_bc; /* number of bytes to copy to user's buffer */ + int cache_avail_bc; + int ret = 0; + + /* write tag to out FIFO */ + ret = i2c_msm_blk_xfer_wr_tag(ctrl); + if (ret) + return ret; + i2c_msm_blk_wr_flush(ctrl); + + while (buf->len || buf->in_tag.len) { + cache_avail_bc = i2c_msm_blk_rd_blk(ctrl, + buf->len + buf->in_tag.len); + + i2c_msm_dbg(ctrl, MSM_DBG, "IN-BLK:%*phC\n", cache_avail_bc, + blk->rx_cache + blk->rx_cache_idx); + + if (cache_avail_bc < 0) + return cache_avail_bc; + + /* discard tag from input FIFO */ + if (buf->in_tag.len) { + int discard_bc = min_t(int, cache_avail_bc, + buf->in_tag.len); + blk->rx_cache_idx += discard_bc; + buf->in_tag.len -= discard_bc; + cache_avail_bc -= discard_bc; + } + + /* copy bytes from cached block to user's buffer */ + copy_bc = min_t(int, cache_avail_bc, buf->len); + memcpy(msg->buf + buf->byte_idx, + blk->rx_cache + blk->rx_cache_idx, copy_bc); + + blk->rx_cache_idx += copy_bc; + buf->len -= copy_bc; + buf->byte_idx += copy_bc; + } + return ret; +} + +/* + * i2c_msm_blk_xfer: process transfer using block mode + */ +static int i2c_msm_blk_xfer(struct i2c_msm_ctrl *ctrl) +{ + int ret = 0; + struct i2c_msm_xfer_buf *buf = &ctrl->xfer.cur_buf; + struct i2c_msm_xfer_mode_blk *blk = &ctrl->xfer.blk; + + if (!blk->is_init) { + ret = i2c_msm_blk_init_struct(ctrl); + if (!blk->is_init) + return ret; + } + + init_completion(&blk->wait_rx_blk); + init_completion(&blk->wait_tx_blk); + + /* tx_cnt > 0 always */ + blk->complete_mask = QUP_MAX_OUTPUT_DONE_FLAG; + if (ctrl->xfer.rx_cnt) + blk->complete_mask |= QUP_MAX_INPUT_DONE_FLAG; + + /* initialize block mode for new transfer */ + blk->tx_cache_idx = 0; + blk->rx_cache_idx = 0; + + ret = i2c_msm_qup_state_set(ctrl, QUP_STATE_RESET); + if (ret < 0) + return ret; + + /* program qup registers */ + i2c_msm_qup_xfer_init_reset_state(ctrl); + + ret = i2c_msm_qup_state_set(ctrl, QUP_STATE_RUN); + if (ret < 0) + return ret; + + /* program qup registers which must be set *after* reset */ + i2c_msm_qup_xfer_init_run_state(ctrl); + + while (i2c_msm_xfer_next_buf(ctrl)) { + if (buf->is_rx) { + ret = i2c_msm_blk_rd_xfer_buf(ctrl); + if (ret) + return ret; + /* + * SW workaround to wait for extra interrupt from + * hardware for last block in block mode for read + */ + if (buf->is_last) { + ret = i2c_msm_xfer_wait_for_completion(ctrl, + &blk->wait_rx_blk); + if (!ret) + complete(&ctrl->xfer.complete); + } + } else { + ret = i2c_msm_blk_wr_xfer_buf(ctrl); + if (ret) + return ret; + } + } + i2c_msm_blk_wr_flush(ctrl); + return i2c_msm_xfer_wait_for_completion(ctrl, &ctrl->xfer.complete); +} + +/* + * i2c_msm_dma_xfer_prepare: map DMA buffers, and create tags. + * @return zero on success or negative error value + */ +static int i2c_msm_dma_xfer_prepare(struct i2c_msm_ctrl *ctrl) +{ + struct i2c_msm_xfer_mode_dma *dma = &ctrl->xfer.dma; + struct i2c_msm_xfer_buf *buf = &ctrl->xfer.cur_buf; + struct i2c_msm_dma_chan *tx = &dma->chan[I2C_MSM_DMA_TX]; + struct i2c_msm_dma_chan *rx = &dma->chan[I2C_MSM_DMA_RX]; + struct i2c_msm_dma_buf *dma_buf; + int rem_buf_cnt = I2C_MSM_DMA_DESC_ARR_SIZ; + struct i2c_msg *cur_msg; + enum dma_data_direction buf_dma_dirctn; + struct i2c_msm_dma_mem data; + u8 *tag_arr_itr_vrtl_addr; + dma_addr_t tag_arr_itr_phy_addr; + + tx->desc_cnt_cur = 0; + rx->desc_cnt_cur = 0; + dma->buf_arr_cnt = 0; + dma_buf = dma->buf_arr; + tag_arr_itr_vrtl_addr = ((u8 *) dma->tag_arr.vrtl_addr); + tag_arr_itr_phy_addr = dma->tag_arr.phy_addr; + + for (; i2c_msm_xfer_next_buf(ctrl) && rem_buf_cnt; + ++dma_buf, + tag_arr_itr_phy_addr += sizeof(dma_addr_t), + tag_arr_itr_vrtl_addr += sizeof(dma_addr_t)) { + + /* dma-map the client's message */ + cur_msg = ctrl->xfer.msgs + buf->msg_idx; + data.vrtl_addr = cur_msg->buf + buf->byte_idx; + if (buf->is_rx) { + buf_dma_dirctn = DMA_FROM_DEVICE; + rx->desc_cnt_cur += 2; /* msg + tag */ + tx->desc_cnt_cur += 1; /* tag */ + } else { + buf_dma_dirctn = DMA_TO_DEVICE; + tx->desc_cnt_cur += 2; /* msg + tag */ + } + + /* for last buffer in a transfer msg */ + if (buf->is_last) { + /* add ovrhead byte cnt for tags specific to DMA mode */ + ctrl->xfer.rx_ovrhd_cnt += 2; /* EOT+FLUSH_STOP tags*/ + ctrl->xfer.tx_ovrhd_cnt += 2; /* EOT+FLUSH_STOP tags */ + + /* increment rx desc cnt to read off tags and + * increment tx desc cnt to queue EOT+FLUSH_STOP tags + */ + tx->desc_cnt_cur++; + rx->desc_cnt_cur++; + } + + if ((rx->desc_cnt_cur >= I2C_MSM_DMA_RX_SZ) || + (tx->desc_cnt_cur >= I2C_MSM_DMA_TX_SZ)) + return -ENOMEM; + + data.phy_addr = dma_map_single(ctrl->dev, data.vrtl_addr, + buf->len, buf_dma_dirctn); + + if (dma_mapping_error(ctrl->dev, data.phy_addr)) { + dev_err(ctrl->dev, + "error DMA mapping DMA buffers, err:%lld buf_vrtl:0x%pK data_len:%d dma_dir:%s\n", + (u64) data.phy_addr, data.vrtl_addr, buf->len, + ((buf_dma_dirctn == DMA_FROM_DEVICE) + ? "DMA_FROM_DEVICE" : "DMA_TO_DEVICE")); + return -EFAULT; + } + + /* copy 8 bytes. Only tag.len bytes will be used */ + *((u64 *)tag_arr_itr_vrtl_addr) = buf->out_tag.val; + + i2c_msm_dbg(ctrl, MSM_DBG, + "vrtl:0x%pK phy:0x%llx val:0x%llx sizeof(dma_addr_t):%zu\n", + tag_arr_itr_vrtl_addr, (u64) tag_arr_itr_phy_addr, + *((u64 *)tag_arr_itr_vrtl_addr), sizeof(dma_addr_t)); + + /* + * create dma buf, in the dma buf arr, based on the buf created + * by i2c_msm_xfer_next_buf() + */ + *dma_buf = (struct i2c_msm_dma_buf) { + .ptr = data, + .len = buf->len, + .dma_dir = buf_dma_dirctn, + .is_rx = buf->is_rx, + .is_last = buf->is_last, + .tag = (struct i2c_msm_dma_tag) { + .buf = tag_arr_itr_phy_addr, + .len = buf->out_tag.len, + }, + }; + ++dma->buf_arr_cnt; + --rem_buf_cnt; + } + return 0; +} + +/* + * i2c_msm_dma_xfer_unprepare: DAM unmap buffers. + */ +static void i2c_msm_dma_xfer_unprepare(struct i2c_msm_ctrl *ctrl) +{ + int i; + struct i2c_msm_dma_buf *buf_itr = ctrl->xfer.dma.buf_arr; + + for (i = 0 ; i < ctrl->xfer.dma.buf_arr_cnt ; ++i, ++buf_itr) + dma_unmap_single(ctrl->dev, buf_itr->ptr.phy_addr, buf_itr->len, + buf_itr->dma_dir); +} + +static void i2c_msm_dma_callback_tx_complete(void *dma_async_param) +{ + struct i2c_msm_ctrl *ctrl = dma_async_param; + + complete(&ctrl->xfer.complete); +} + +static void i2c_msm_dma_callback_rx_complete(void *dma_async_param) +{ + struct i2c_msm_ctrl *ctrl = dma_async_param; + + complete(&ctrl->xfer.rx_complete); +} + +/* + * i2c_msm_dma_xfer_process: Queue transfers to DMA + * @pre 1)QUP is in run state. 2) i2c_msm_dma_xfer_prepare() was called. + * @return zero on success or negative error value + */ +static int i2c_msm_dma_xfer_process(struct i2c_msm_ctrl *ctrl) +{ + struct i2c_msm_xfer_mode_dma *dma = &ctrl->xfer.dma; + struct i2c_msm_dma_chan *tx = &dma->chan[I2C_MSM_DMA_TX]; + struct i2c_msm_dma_chan *rx = &dma->chan[I2C_MSM_DMA_RX]; + struct scatterlist *sg_rx = NULL; + struct scatterlist *sg_rx_itr = NULL; + struct scatterlist *sg_tx = NULL; + struct scatterlist *sg_tx_itr = NULL; + struct dma_async_tx_descriptor *dma_desc_rx; + struct dma_async_tx_descriptor *dma_desc_tx; + struct i2c_msm_dma_buf *buf_itr; + int i; + int ret = 0; + + i2c_msm_dbg(ctrl, MSM_DBG, "Going to enqueue %zu buffers in DMA\n", + dma->buf_arr_cnt); + + /* Set the QUP State to pause while DMA completes the txn */ + ret = i2c_msm_qup_state_set(ctrl, QUP_STATE_PAUSE); + if (ret) { + dev_err(ctrl->dev, "transition to pause state failed before DMA transaction :%d\n", + ret); + return ret; + } + + sg_tx = kcalloc(tx->desc_cnt_cur, sizeof(struct scatterlist), + GFP_KERNEL); + if (!sg_tx) { + ret = -ENOMEM; + goto dma_xfer_end; + } + sg_init_table(sg_tx, tx->desc_cnt_cur); + sg_tx_itr = sg_tx; + + sg_rx = kcalloc(rx->desc_cnt_cur, sizeof(struct scatterlist), + GFP_KERNEL); + if (!sg_rx) { + ret = -ENOMEM; + goto dma_xfer_end; + } + sg_init_table(sg_rx, rx->desc_cnt_cur); + sg_rx_itr = sg_rx; + + buf_itr = dma->buf_arr; + + for (i = 0; i < dma->buf_arr_cnt ; ++i, ++buf_itr) { + /* Queue tag */ + sg_dma_address(sg_tx_itr) = buf_itr->tag.buf; + sg_dma_len(sg_tx_itr) = buf_itr->tag.len; + ++sg_tx_itr; + + /* read off tag + len bytes(don't care) in input FIFO + * on read transfer + */ + if (buf_itr->is_rx) { + /* rid of input tag */ + sg_dma_address(sg_rx_itr) = + ctrl->xfer.dma.input_tag.phy_addr; + sg_dma_len(sg_rx_itr) = QUP_BUF_OVERHD_BC; + ++sg_rx_itr; + + /* queue data buffer */ + sg_dma_address(sg_rx_itr) = buf_itr->ptr.phy_addr; + sg_dma_len(sg_rx_itr) = buf_itr->len; + ++sg_rx_itr; + } else { + sg_dma_address(sg_tx_itr) = buf_itr->ptr.phy_addr; + sg_dma_len(sg_tx_itr) = buf_itr->len; + ++sg_tx_itr; + } + } + + /* this tag will be copied to rx fifo */ + sg_dma_address(sg_tx_itr) = dma->eot_n_flush_stop_tags.phy_addr; + sg_dma_len(sg_tx_itr) = QUP_BUF_OVERHD_BC; + ++sg_tx_itr; + + /* + * Reading the tag off the input fifo has side effects and + * it is mandatory for getting the DMA's interrupt. + */ + sg_dma_address(sg_rx_itr) = ctrl->xfer.dma.input_tag.phy_addr; + sg_dma_len(sg_rx_itr) = QUP_BUF_OVERHD_BC; + ++sg_rx_itr; + + /* + * We only want a single BAM interrupt per transfer, and we always + * add a flush-stop i2c tag as the last tx sg entry. Since the dma + * driver puts the supplied BAM flags only on the last BAM descriptor, + * the flush stop will always be the one which generate that interrupt + * and invokes the callback. + */ + dma_desc_tx = dmaengine_prep_slave_sg(tx->dma_chan, + sg_tx, + sg_tx_itr - sg_tx, + tx->dir, + (SPS_IOVEC_FLAG_EOT | + SPS_IOVEC_FLAG_NWD)); + if (IS_ERR_OR_NULL(dma_desc_tx)) { + dev_err(ctrl->dev, "error dmaengine_prep_slave_sg tx:%ld\n", + PTR_ERR(dma_desc_tx)); + ret = dma_desc_tx ? PTR_ERR(dma_desc_tx) : -ENOMEM; + goto dma_xfer_end; + } + + /* callback defined for tx dma desc */ + dma_desc_tx->callback = i2c_msm_dma_callback_tx_complete; + dma_desc_tx->callback_param = ctrl; + dmaengine_submit(dma_desc_tx); + dma_async_issue_pending(tx->dma_chan); + + /* queue the rx dma desc */ + dma_desc_rx = dmaengine_prep_slave_sg(rx->dma_chan, sg_rx, + sg_rx_itr - sg_rx, rx->dir, + (SPS_IOVEC_FLAG_EOT | + SPS_IOVEC_FLAG_NWD)); + if (IS_ERR_OR_NULL(dma_desc_rx)) { + dev_err(ctrl->dev, + "error dmaengine_prep_slave_sg rx:%ld\n", + PTR_ERR(dma_desc_rx)); + ret = dma_desc_rx ? PTR_ERR(dma_desc_rx) : -ENOMEM; + goto dma_xfer_end; + } + + dma_desc_rx->callback = i2c_msm_dma_callback_rx_complete; + dma_desc_rx->callback_param = ctrl; + dmaengine_submit(dma_desc_rx); + dma_async_issue_pending(rx->dma_chan); + + /* Set the QUP State to Run when completes the txn */ + ret = i2c_msm_qup_state_set(ctrl, QUP_STATE_RUN); + if (ret) { + dev_err(ctrl->dev, "transition to run state failed before DMA transaction :%d\n", + ret); + goto dma_xfer_end; + } + + ret = i2c_msm_xfer_wait_for_completion(ctrl, &ctrl->xfer.complete); + if (!ret && ctrl->xfer.rx_cnt) + ret = i2c_msm_xfer_wait_for_completion(ctrl, + &ctrl->xfer.rx_complete); + +dma_xfer_end: + /* free scatter-gather lists */ + kfree(sg_tx); + kfree(sg_rx); + + return ret; +} + +static void i2c_msm_dma_free_channels(struct i2c_msm_ctrl *ctrl) +{ + int i; + + for (i = 0; i < I2C_MSM_DMA_CNT; ++i) { + struct i2c_msm_dma_chan *chan = &ctrl->xfer.dma.chan[i]; + + if (!chan->is_init) + continue; + + dma_release_channel(chan->dma_chan); + chan->is_init = false; + chan->dma_chan = NULL; + } + if (ctrl->xfer.dma.state > I2C_MSM_DMA_INIT_CORE) + ctrl->xfer.dma.state = I2C_MSM_DMA_INIT_CORE; +} + +static const char * const i2c_msm_dma_chan_name[] = {"tx", "rx"}; + +static int i2c_msm_dmaengine_dir[] = { + DMA_MEM_TO_DEV, DMA_DEV_TO_MEM +}; + +static int i2c_msm_dma_init_channels(struct i2c_msm_ctrl *ctrl) +{ + int ret = 0; + int i; + + /* Iterate over the dma channels to initialize them */ + for (i = 0; i < I2C_MSM_DMA_CNT; ++i) { + struct dma_slave_config cfg = {0}; + struct i2c_msm_dma_chan *chan = &ctrl->xfer.dma.chan[i]; + + if (chan->is_init) + continue; + + chan->name = i2c_msm_dma_chan_name[i]; + chan->dma_chan = dma_request_slave_channel(ctrl->dev, + chan->name); + if (!chan->dma_chan) { + dev_err(ctrl->dev, + "error dma_request_slave_channel(dev:%s chan:%s)\n", + dev_name(ctrl->dev), chan->name); + /* free the channels if allocated before */ + i2c_msm_dma_free_channels(ctrl); + return -ENODEV; + } + + chan->dir = cfg.direction = i2c_msm_dmaengine_dir[i]; + ret = dmaengine_slave_config(chan->dma_chan, &cfg); + if (ret) { + dev_err(ctrl->dev, + "error:%d dmaengine_slave_config(chan:%s)\n", + ret, chan->name); + dma_release_channel(chan->dma_chan); + chan->dma_chan = NULL; + i2c_msm_dma_free_channels(ctrl); + return ret; + } + chan->is_init = true; + } + ctrl->xfer.dma.state = I2C_MSM_DMA_INIT_CHAN; + return 0; +} + +static void i2c_msm_dma_teardown(struct i2c_msm_ctrl *ctrl) +{ + struct i2c_msm_xfer_mode_dma *dma = &ctrl->xfer.dma; + + i2c_msm_dma_free_channels(ctrl); + + if (dma->state > I2C_MSM_DMA_INIT_NONE) + dma_free_coherent(ctrl->dev, I2C_MSM_DMA_TAG_MEM_SZ, + dma->input_tag.vrtl_addr, + dma->input_tag.phy_addr); + + dma->state = I2C_MSM_DMA_INIT_NONE; +} + +static int i2c_msm_dma_init(struct i2c_msm_ctrl *ctrl) +{ + struct i2c_msm_xfer_mode_dma *dma = &ctrl->xfer.dma; + u8 *tags_space_virt_addr; + dma_addr_t tags_space_phy_addr; + + /* check if DMA core is initialized */ + if (dma->state > I2C_MSM_DMA_INIT_NONE) + goto dma_core_is_init; + + /* + * allocate dma memory for input_tag + eot_n_flush_stop_tags + tag_arr + * for more see: I2C_MSM_DMA_TAG_MEM_SZ definition + */ + tags_space_virt_addr = dma_alloc_coherent( + ctrl->dev, + I2C_MSM_DMA_TAG_MEM_SZ, + &tags_space_phy_addr, + GFP_KERNEL); + if (!tags_space_virt_addr) { + dev_err(ctrl->dev, + "error alloc %d bytes of DMAable memory for DMA tags space\n", + I2C_MSM_DMA_TAG_MEM_SZ); + return -ENOMEM; + } + + /* + * set the dma-tags virtual and physical addresses: + * 1) the first tag space is for the input (throw away) tag + */ + dma->input_tag.vrtl_addr = tags_space_virt_addr; + dma->input_tag.phy_addr = tags_space_phy_addr; + + /* 2) second tag space is for eot_flush_stop tag which is const value */ + tags_space_virt_addr += I2C_MSM_TAG2_MAX_LEN; + tags_space_phy_addr += I2C_MSM_TAG2_MAX_LEN; + dma->eot_n_flush_stop_tags.vrtl_addr = tags_space_virt_addr; + dma->eot_n_flush_stop_tags.phy_addr = tags_space_phy_addr; + + /* set eot_n_flush_stop_tags value */ + *((u16 *) dma->eot_n_flush_stop_tags.vrtl_addr) = + QUP_TAG2_INPUT_EOT | (QUP_TAG2_FLUSH_STOP << 8); + + /* 3) all other tag spaces are used for transfer tags */ + tags_space_virt_addr += I2C_MSM_TAG2_MAX_LEN; + tags_space_phy_addr += I2C_MSM_TAG2_MAX_LEN; + dma->tag_arr.vrtl_addr = tags_space_virt_addr; + dma->tag_arr.phy_addr = tags_space_phy_addr; + + dma->state = I2C_MSM_DMA_INIT_CORE; + +dma_core_is_init: + return i2c_msm_dma_init_channels(ctrl); +} + +static int i2c_msm_dma_xfer(struct i2c_msm_ctrl *ctrl) +{ + int ret; + + ret = i2c_msm_dma_init(ctrl); + if (ret) { + dev_err(ctrl->dev, "DMA Init Failed: %d\n", ret); + return ret; + } + + /* dma map user's buffers and create tags */ + ret = i2c_msm_dma_xfer_prepare(ctrl); + if (ret < 0) { + dev_err(ctrl->dev, "error on i2c_msm_dma_xfer_prepare():%d\n", + ret); + goto err_dma_xfer; + } + + ret = i2c_msm_qup_state_set(ctrl, QUP_STATE_RESET); + if (ret < 0) + goto err_dma_xfer; + + /* program qup registers */ + i2c_msm_qup_xfer_init_reset_state(ctrl); + + ret = i2c_msm_qup_state_set(ctrl, QUP_STATE_RUN); + if (ret < 0) + goto err_dma_xfer; + + /* program qup registers which must be set *after* reset */ + i2c_msm_qup_xfer_init_run_state(ctrl); + + /* enqueue transfer buffers */ + ret = i2c_msm_dma_xfer_process(ctrl); + if (ret) + dev_err(ctrl->dev, + "error i2c_msm_dma_xfer_process(n_bufs:%zu):%d\n", + ctrl->xfer.dma.buf_arr_cnt, ret); + +err_dma_xfer: + i2c_msm_dma_xfer_unprepare(ctrl); + return ret; +} + +/* + * i2c_msm_qup_slv_holds_bus: true when slave hold the SDA low + */ +static bool i2c_msm_qup_slv_holds_bus(struct i2c_msm_ctrl *ctrl) +{ + u32 status = readl_relaxed(ctrl->rsrcs.base + QUP_I2C_STATUS); + + bool slv_holds_bus = !(status & QUP_I2C_SDA) && + (status & QUP_BUS_ACTIVE) && + !(status & QUP_BUS_MASTER); + if (slv_holds_bus) + dev_info(ctrl->dev, + "bus lines held low by a slave detected\n"); + + return slv_holds_bus; +} + +/* + * i2c_msm_qup_poll_bus_active_unset: poll until QUP_BUS_ACTIVE is unset + * + * @return zero when bus inactive, or nonzero on timeout. + * + * Loop and reads QUP_I2C_MASTER_STATUS until bus is inactive or timeout + * reached. Used to avoid race condition due to gap between QUP completion + * interrupt and QUP issuing stop signal on the bus. + */ +static int i2c_msm_qup_poll_bus_active_unset(struct i2c_msm_ctrl *ctrl) +{ + void __iomem *base = ctrl->rsrcs.base; + ulong timeout = jiffies + msecs_to_jiffies(I2C_MSM_MAX_POLL_MSEC); + int ret = 0; + size_t read_cnt = 0; + + do { + if (!(readl_relaxed(base + QUP_I2C_STATUS) & QUP_BUS_ACTIVE)) + goto poll_active_end; + ++read_cnt; + } while (time_before_eq(jiffies, timeout)); + + ret = -EBUSY; + +poll_active_end: + /* second logged value is time-left before timeout or zero if expired */ + i2c_msm_prof_evnt_add(ctrl, MSM_DBG, I2C_MSM_ACTV_END, + ret, (ret ? 0 : (timeout - jiffies)), read_cnt); + + return ret; +} + +static void i2c_msm_clk_path_vote(struct i2c_msm_ctrl *ctrl) +{ + i2c_msm_clk_path_init(ctrl); + + if (ctrl->rsrcs.clk_path_vote.client_hdl) + msm_bus_scale_client_update_request( + ctrl->rsrcs.clk_path_vote.client_hdl, + I2C_MSM_CLK_PATH_RESUME_VEC); +} + +static void i2c_msm_clk_path_unvote(struct i2c_msm_ctrl *ctrl) +{ + if (ctrl->rsrcs.clk_path_vote.client_hdl) + msm_bus_scale_client_update_request( + ctrl->rsrcs.clk_path_vote.client_hdl, + I2C_MSM_CLK_PATH_SUSPEND_VEC); +} + +static void i2c_msm_clk_path_teardown(struct i2c_msm_ctrl *ctrl) +{ + if (ctrl->rsrcs.clk_path_vote.client_hdl) { + msm_bus_scale_unregister_client( + ctrl->rsrcs.clk_path_vote.client_hdl); + ctrl->rsrcs.clk_path_vote.client_hdl = 0; + } +} + +/* + * i2c_msm_clk_path_init_structs: internal impl detail of i2c_msm_clk_path_init + * + * allocates and initilizes the bus scaling vectors. + */ +static int i2c_msm_clk_path_init_structs(struct i2c_msm_ctrl *ctrl) +{ + struct msm_bus_vectors *paths = NULL; + struct msm_bus_paths *usecases = NULL; + + i2c_msm_dbg(ctrl, MSM_PROF, "initializes path clock voting structs\n"); + + paths = kzalloc(sizeof(*paths) * 2, GFP_KERNEL); + if (!paths) + return -ENOMEM; + + usecases = kzalloc(sizeof(*usecases) * 2, GFP_KERNEL); + if (!usecases) + goto path_init_err; + + ctrl->rsrcs.clk_path_vote.pdata = kzalloc( + sizeof(*ctrl->rsrcs.clk_path_vote.pdata), + GFP_KERNEL); + if (!ctrl->rsrcs.clk_path_vote.pdata) + goto path_init_err; + + paths[I2C_MSM_CLK_PATH_SUSPEND_VEC] = (struct msm_bus_vectors) { + .src = ctrl->rsrcs.clk_path_vote.mstr_id, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }; + + paths[I2C_MSM_CLK_PATH_RESUME_VEC] = (struct msm_bus_vectors) { + .src = ctrl->rsrcs.clk_path_vote.mstr_id, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = I2C_MSM_CLK_PATH_AVRG_BW(ctrl), + .ib = I2C_MSM_CLK_PATH_BRST_BW(ctrl), + }; + + usecases[I2C_MSM_CLK_PATH_SUSPEND_VEC] = (struct msm_bus_paths) { + .num_paths = 1, + .vectors = &paths[I2C_MSM_CLK_PATH_SUSPEND_VEC], + }; + + usecases[I2C_MSM_CLK_PATH_RESUME_VEC] = (struct msm_bus_paths) { + .num_paths = 1, + .vectors = &paths[I2C_MSM_CLK_PATH_RESUME_VEC], + }; + + *ctrl->rsrcs.clk_path_vote.pdata = (struct msm_bus_scale_pdata) { + .usecase = usecases, + .num_usecases = 2, + .name = dev_name(ctrl->dev), + }; + + return 0; + +path_init_err: + kfree(paths); + kfree(usecases); + kfree(ctrl->rsrcs.clk_path_vote.pdata); + ctrl->rsrcs.clk_path_vote.pdata = NULL; + return -ENOMEM; +} + +/* + * i2c_msm_clk_path_postponed_register: reg with bus-scaling after it is probed + * + * @return zero on success + * + * Workaround: i2c driver may be probed before the bus scaling driver. Calling + * msm_bus_scale_register_client() will fail if the bus scaling driver is not + * ready yet. Thus, this function should be called not from probe but from a + * later context. Also, this function may be called more then once before + * register succeed. At this case only one error message will be logged. At boot + * time all clocks are on, so earlier i2c transactions should succeed. + */ +static int i2c_msm_clk_path_postponed_register(struct i2c_msm_ctrl *ctrl) +{ + ctrl->rsrcs.clk_path_vote.client_hdl = + msm_bus_scale_register_client(ctrl->rsrcs.clk_path_vote.pdata); + + if (ctrl->rsrcs.clk_path_vote.client_hdl) { + if (ctrl->rsrcs.clk_path_vote.reg_err) { + /* log a success message if an error msg was logged */ + ctrl->rsrcs.clk_path_vote.reg_err = false; + dev_err(ctrl->dev, + "msm_bus_scale_register_client(mstr-id:%d):0x%x (ok)\n", + ctrl->rsrcs.clk_path_vote.mstr_id, + ctrl->rsrcs.clk_path_vote.client_hdl); + } + } else { + /* guard to log only one error on multiple failure */ + if (!ctrl->rsrcs.clk_path_vote.reg_err) { + ctrl->rsrcs.clk_path_vote.reg_err = true; + + dev_info(ctrl->dev, + "msm_bus_scale_register_client(mstr-id:%d):0 (not a problem)\n", + ctrl->rsrcs.clk_path_vote.mstr_id); + } + } + + return ctrl->rsrcs.clk_path_vote.client_hdl ? 0 : -EAGAIN; +} + +static void i2c_msm_clk_path_init(struct i2c_msm_ctrl *ctrl) +{ + /* + * bail out if path voting is diabled (master_id == 0) or if it is + * already registered (client_hdl != 0) + */ + if (!ctrl->rsrcs.clk_path_vote.mstr_id || + ctrl->rsrcs.clk_path_vote.client_hdl) + return; + + /* if fail once then try no more */ + if (!ctrl->rsrcs.clk_path_vote.pdata && + i2c_msm_clk_path_init_structs(ctrl)) { + ctrl->rsrcs.clk_path_vote.mstr_id = 0; + return; + } + + /* on failure try again later */ + if (i2c_msm_clk_path_postponed_register(ctrl)) + return; +} + +/* + * i2c_msm_qup_isr: QUP interrupt service routine + */ +static irqreturn_t i2c_msm_qup_isr(int irq, void *devid) +{ + struct i2c_msm_ctrl *ctrl = devid; + void __iomem *base = ctrl->rsrcs.base; + struct i2c_msm_xfer *xfer = &ctrl->xfer; + struct i2c_msm_xfer_mode_blk *blk = &ctrl->xfer.blk; + u32 err_flags = 0; + u32 clr_flds = 0; + bool log_event = false; + bool signal_complete = false; + bool need_wmb = false; + + i2c_msm_prof_evnt_add(ctrl, MSM_PROF, I2C_MSM_IRQ_BGN, irq, 0, 0); + + if (!atomic_read(&ctrl->xfer.is_active)) { + dev_info(ctrl->dev, "irq:%d when no active transfer\n", irq); + return IRQ_HANDLED; + } + + ctrl->i2c_sts_reg = readl_relaxed(base + QUP_I2C_STATUS); + err_flags = readl_relaxed(base + QUP_ERROR_FLAGS); + ctrl->qup_op_reg = readl_relaxed(base + QUP_OPERATIONAL); + + if (ctrl->i2c_sts_reg & QUP_MSTR_STTS_ERR_MASK) { + signal_complete = true; + log_event = true; + /* + * If there is more than 1 error here, last one sticks. + * The order of the error set here matters. + */ + if (ctrl->i2c_sts_reg & QUP_ARB_LOST) + ctrl->xfer.err = I2C_MSM_ERR_ARB_LOST; + + if (ctrl->i2c_sts_reg & QUP_BUS_ERROR) + ctrl->xfer.err = I2C_MSM_ERR_BUS_ERR; + + if (ctrl->i2c_sts_reg & QUP_PACKET_NACKED) + ctrl->xfer.err = I2C_MSM_ERR_NACK; + } + + /* check for FIFO over/under runs error */ + if (err_flags & QUP_ERR_FLGS_MASK) + ctrl->xfer.err = I2C_MSM_ERR_OVR_UNDR_RUN; + + /* Dump the register values before reset the core */ + if (ctrl->xfer.err && ctrl->dbgfs.dbg_lvl >= MSM_DBG) + i2c_msm_dbg_qup_reg_dump(ctrl); + + /* clear interrupts fields */ + clr_flds = ctrl->i2c_sts_reg & QUP_MSTR_STTS_ERR_MASK; + if (clr_flds) { + writel_relaxed(clr_flds, base + QUP_I2C_STATUS); + need_wmb = true; + } + + clr_flds = err_flags & QUP_ERR_FLGS_MASK; + if (clr_flds) { + writel_relaxed(clr_flds, base + QUP_ERROR_FLAGS); + need_wmb = true; + } + + clr_flds = ctrl->qup_op_reg & + (QUP_OUTPUT_SERVICE_FLAG | + QUP_INPUT_SERVICE_FLAG); + if (clr_flds) { + writel_relaxed(clr_flds, base + QUP_OPERATIONAL); + need_wmb = true; + } + + if (need_wmb) + /* + * flush writes that clear the interrupt flags before changing + * state to reset. + */ + wmb(); + + /* Reset and bail out on error */ + if (ctrl->xfer.err) { + /* Flush for the tags in case of an error and DMA Mode*/ + if (ctrl->xfer.mode_id == I2C_MSM_XFER_MODE_DMA) { + writel_relaxed(QUP_I2C_FLUSH, ctrl->rsrcs.base + + QUP_STATE); + /* + * Ensure that QUP_I2C_FLUSH is written before + * State reset + */ + wmb(); + } + + /* HW workaround: when interrupt is level triggerd, more + * than one interrupt may fire in error cases. Thus we + * change the QUP core state to Reset immediately in the + * ISR to ward off the next interrupt. + */ + writel_relaxed(QUP_STATE_RESET, ctrl->rsrcs.base + QUP_STATE); + + signal_complete = true; + log_event = true; + goto isr_end; + } + + /* handle data completion */ + if (xfer->mode_id == I2C_MSM_XFER_MODE_BLOCK) { + /* block ready for writing */ + if (ctrl->qup_op_reg & QUP_OUTPUT_SERVICE_FLAG) { + log_event = true; + if (ctrl->qup_op_reg & QUP_OUT_BLOCK_WRITE_REQ) + complete(&blk->wait_tx_blk); + + if ((ctrl->qup_op_reg & blk->complete_mask) + == blk->complete_mask) { + log_event = true; + signal_complete = true; + } + } + /* block ready for reading */ + if (ctrl->qup_op_reg & QUP_INPUT_SERVICE_FLAG) { + log_event = true; + complete(&blk->wait_rx_blk); + } + } else { + /* for FIFO/DMA Mode*/ + if (ctrl->qup_op_reg & QUP_MAX_INPUT_DONE_FLAG) { + log_event = true; + /* + * If last transaction is an input then the entire + * transfer is done + */ + if (ctrl->xfer.last_is_rx) + signal_complete = true; + } + /* + * Ideally, would like to check QUP_MAX_OUTPUT_DONE_FLAG. + * However, QUP_MAX_OUTPUT_DONE_FLAG is lagging behind + * QUP_OUTPUT_SERVICE_FLAG. The only reason for + * QUP_OUTPUT_SERVICE_FLAG to be set in FIFO mode is + * QUP_MAX_OUTPUT_DONE_FLAG condition. The code checking + * here QUP_OUTPUT_SERVICE_FLAG and assumes that + * QUP_MAX_OUTPUT_DONE_FLAG. + */ + if (ctrl->qup_op_reg & (QUP_OUTPUT_SERVICE_FLAG | + QUP_MAX_OUTPUT_DONE_FLAG)) { + log_event = true; + /* + * If last transaction is an output then the + * entire transfer is done + */ + if (!ctrl->xfer.last_is_rx) + signal_complete = true; + } + } + +isr_end: + if (log_event || (ctrl->dbgfs.dbg_lvl >= MSM_DBG)) + i2c_msm_prof_evnt_add(ctrl, MSM_PROF, + I2C_MSM_IRQ_END, + ctrl->i2c_sts_reg, ctrl->qup_op_reg, + err_flags); + + if (signal_complete) + complete(&ctrl->xfer.complete); + + return IRQ_HANDLED; +} + +static void i2x_msm_blk_free_cache(struct i2c_msm_ctrl *ctrl) +{ + kfree(ctrl->xfer.blk.tx_cache); + kfree(ctrl->xfer.blk.rx_cache); +} + +static void i2c_msm_qup_init(struct i2c_msm_ctrl *ctrl) +{ + u32 state; + void __iomem *base = ctrl->rsrcs.base; + + i2c_msm_prof_evnt_add(ctrl, MSM_PROF, I2C_MSM_PROF_RESET, 0, 0, 0); + + i2c_msm_qup_sw_reset(ctrl); + i2c_msm_qup_state_set(ctrl, QUP_STATE_RESET); + + writel_relaxed(QUP_N_VAL | QUP_MINI_CORE_I2C_VAL, base + QUP_CONFIG); + + writel_relaxed(QUP_OUTPUT_OVER_RUN_ERR_EN | QUP_INPUT_UNDER_RUN_ERR_EN + | QUP_OUTPUT_UNDER_RUN_ERR_EN | QUP_INPUT_OVER_RUN_ERR_EN, + base + QUP_ERROR_FLAGS_EN); + + writel_relaxed(QUP_INPUT_SERVICE_MASK | QUP_OUTPUT_SERVICE_MASK, + base + QUP_OPERATIONAL_MASK); + + writel_relaxed(QUP_EN_VERSION_TWO_TAG, base + QUP_I2C_MASTER_CONFIG); + + i2c_msm_qup_fifo_calc_size(ctrl); + /* + * Ensure that QUP configuration is written and that fifo size if read + * before leaving this function + */ + mb(); + + state = readl_relaxed(base + QUP_STATE); + + if (!(state & QUP_I2C_MAST_GEN)) + dev_err(ctrl->dev, + "error on verifying HW support (I2C_MAST_GEN=0)\n"); +} + +static void qup_i2c_recover_bit_bang(struct i2c_msm_ctrl *ctrl) +{ + int i, ret; + int gpio_clk; + int gpio_dat; + bool gpio_clk_status = false; + u32 status = readl_relaxed(ctrl->rsrcs.base + QUP_I2C_STATUS); + struct pinctrl_state *bitbang; + + dev_info(ctrl->dev, "Executing bus recovery procedure (9 clk pulse)\n"); + disable_irq(ctrl->rsrcs.irq); + if (!(status & (I2C_STATUS_BUS_ACTIVE)) || + (status & (I2C_STATUS_BUS_MASTER))) { + dev_warn(ctrl->dev, "unexpected i2c recovery call:0x%x\n", + status); + goto recovery_exit; + } + + gpio_clk = of_get_named_gpio(ctrl->adapter.dev.of_node, "qcom,i2c-clk", + 0); + gpio_dat = of_get_named_gpio(ctrl->adapter.dev.of_node, "qcom,i2c-dat", + 0); + + if (gpio_clk < 0 || gpio_dat < 0) { + dev_warn(ctrl->dev, "SW bigbang err: i2c gpios not known\n"); + goto recovery_exit; + } + + bitbang = i2c_msm_rsrcs_gpio_get_state(ctrl, "i2c_bitbang"); + if (bitbang) + ret = pinctrl_select_state(ctrl->rsrcs.pinctrl, bitbang); + if (!bitbang || ret) { + dev_err(ctrl->dev, "GPIO pins have no bitbang setting\n"); + goto recovery_exit; + } + for (i = 0; i < 10; i++) { + if (gpio_get_value(gpio_dat) && gpio_clk_status) + break; + gpio_direction_output(gpio_clk, 0); + udelay(5); + gpio_direction_output(gpio_dat, 0); + udelay(5); + gpio_direction_input(gpio_clk); + udelay(5); + if (!gpio_get_value(gpio_clk)) + udelay(20); + if (!gpio_get_value(gpio_clk)) + usleep_range(10000, 10001); + gpio_clk_status = gpio_get_value(gpio_clk); + gpio_direction_input(gpio_dat); + udelay(5); + } + + i2c_msm_pm_pinctrl_state(ctrl, true); + udelay(10); + + status = readl_relaxed(ctrl->rsrcs.base + QUP_I2C_STATUS); + if (!(status & I2C_STATUS_BUS_ACTIVE)) { + dev_info(ctrl->dev, + "Bus busy cleared after %d clock cycles, status %x\n", + i, status); + goto recovery_exit; + } + + dev_warn(ctrl->dev, "Bus still busy, status %x\n", status); + +recovery_exit: + enable_irq(ctrl->rsrcs.irq); +} + +static int i2c_msm_qup_post_xfer(struct i2c_msm_ctrl *ctrl, int err) +{ + /* poll until bus is released */ + if (i2c_msm_qup_poll_bus_active_unset(ctrl)) { + if ((ctrl->xfer.err == I2C_MSM_ERR_ARB_LOST) || + (ctrl->xfer.err == I2C_MSM_ERR_BUS_ERR) || + (ctrl->xfer.err == I2C_MSM_ERR_TIMEOUT)) { + if (i2c_msm_qup_slv_holds_bus(ctrl)) + qup_i2c_recover_bit_bang(ctrl); + + /* do not generalize error to EIO if its already set */ + if (!err) + err = -EIO; + } + } + + /* + * Disable the IRQ before change to reset state to avoid + * spurious interrupts. + * + */ + disable_irq(ctrl->rsrcs.irq); + + /* flush dma data and reset the qup core in timeout error. + * for other error case, its handled by the ISR + */ + if (ctrl->xfer.err & I2C_MSM_ERR_TIMEOUT) { + /* Flush for the DMA registers */ + if (ctrl->xfer.mode_id == I2C_MSM_XFER_MODE_DMA) + writel_relaxed(QUP_I2C_FLUSH, ctrl->rsrcs.base + + QUP_STATE); + + /* reset the qup core */ + i2c_msm_qup_state_set(ctrl, QUP_STATE_RESET); + err = -ETIMEDOUT; + } else if (ctrl->xfer.err == I2C_MSM_ERR_NACK) { + err = -ENOTCONN; + } + + return err; +} + +static enum i2c_msm_xfer_mode_id +i2c_msm_qup_choose_mode(struct i2c_msm_ctrl *ctrl) +{ + struct i2c_msm_xfer_mode_fifo *fifo = &ctrl->xfer.fifo; + struct i2c_msm_xfer *xfer = &ctrl->xfer; + size_t rx_cnt_sum = xfer->rx_cnt + xfer->rx_ovrhd_cnt; + size_t tx_cnt_sum = xfer->tx_cnt + xfer->tx_ovrhd_cnt; + + + if (ctrl->dbgfs.force_xfer_mode != I2C_MSM_XFER_MODE_NONE) + return ctrl->dbgfs.force_xfer_mode; + + if (((rx_cnt_sum < fifo->input_fifo_sz) && + (tx_cnt_sum < fifo->output_fifo_sz))) + return I2C_MSM_XFER_MODE_FIFO; + + if (ctrl->rsrcs.disable_dma) + return I2C_MSM_XFER_MODE_BLOCK; + + return I2C_MSM_XFER_MODE_DMA; +} + +/* + * i2c_msm_xfer_calc_timeout: calc maximum xfer time in jiffies + * + * Basically timeout = (bit_count / frequency) * safety_coefficient. + * The safety-coefficient also accounts for debugging delay (mostly from + * printk() calls). + */ +static void i2c_msm_xfer_calc_timeout(struct i2c_msm_ctrl *ctrl) +{ + size_t byte_cnt = ctrl->xfer.rx_cnt + ctrl->xfer.tx_cnt; + size_t bit_cnt = byte_cnt * 9; + size_t bit_usec = (bit_cnt * USEC_PER_SEC) / ctrl->rsrcs.clk_freq_out; + size_t loging_ovrhd_coef = ctrl->dbgfs.dbg_lvl + 1; + size_t safety_coef = I2C_MSM_TIMEOUT_SAFETY_COEF * loging_ovrhd_coef; + size_t xfer_max_usec = (bit_usec * safety_coef) + + I2C_MSM_TIMEOUT_MIN_USEC; + + ctrl->xfer.timeout = usecs_to_jiffies(xfer_max_usec); +} + +static int i2c_msm_xfer_wait_for_completion(struct i2c_msm_ctrl *ctrl, + struct completion *complete) +{ + struct i2c_msm_xfer *xfer = &ctrl->xfer; + long time_left; + int ret = 0; + + time_left = wait_for_completion_timeout(complete, + xfer->timeout); + if (!time_left) { + xfer->err = I2C_MSM_ERR_TIMEOUT; + i2c_msm_dbg_dump_diag(ctrl, false, 0, 0); + ret = -EIO; + i2c_msm_prof_evnt_add(ctrl, MSM_ERR, I2C_MSM_COMPLT_FL, + xfer->timeout, time_left, 0); + } else { + /* return an error if one detected by ISR */ + if (ctrl->xfer.err || + (ctrl->dbgfs.dbg_lvl >= MSM_DBG)) { + i2c_msm_dbg_dump_diag(ctrl, true, + ctrl->i2c_sts_reg, ctrl->qup_op_reg); + ret = -(xfer->err); + } + i2c_msm_prof_evnt_add(ctrl, MSM_DBG, I2C_MSM_COMPLT_OK, + xfer->timeout, time_left, 0); + } + + return ret; +} + +static u16 i2c_msm_slv_rd_wr_addr(u16 slv_addr, bool is_rx) +{ + return (slv_addr << 1) | (is_rx ? 0x1 : 0x0); +} + +/* + * @return true when the current transfer's buffer points to the last message + * of the user's request. + */ +static bool i2c_msm_xfer_msg_is_last(struct i2c_msm_ctrl *ctrl) +{ + return ctrl->xfer.cur_buf.msg_idx >= (ctrl->xfer.msg_cnt - 1); +} + +/* + * @return true when the current transfer's buffer points to the last + * transferable buffer (size =< QUP_MAX_BUF_SZ) of the last message of the + * user's request. + */ +static bool i2c_msm_xfer_buf_is_last(struct i2c_msm_ctrl *ctrl) +{ + struct i2c_msm_xfer_buf *cur_buf = &ctrl->xfer.cur_buf; + struct i2c_msg *cur_msg = ctrl->xfer.msgs + cur_buf->msg_idx; + + return i2c_msm_xfer_msg_is_last(ctrl) && + ((cur_buf->byte_idx + QUP_MAX_BUF_SZ) >= cur_msg->len); +} + +static void i2c_msm_xfer_create_cur_tag(struct i2c_msm_ctrl *ctrl, + bool start_req) +{ + struct i2c_msm_xfer_buf *cur_buf = &ctrl->xfer.cur_buf; + + cur_buf->out_tag = i2c_msm_tag_create(start_req, cur_buf->is_last, + cur_buf->is_rx, cur_buf->len, + cur_buf->slv_addr); + + cur_buf->in_tag.len = cur_buf->is_rx ? QUP_BUF_OVERHD_BC : 0; +} + +/* + * i2c_msm_xfer_next_buf: support cases when msg.len > 256 bytes + * + * @return true when next buffer exist, or false when no such buffer + */ +static bool i2c_msm_xfer_next_buf(struct i2c_msm_ctrl *ctrl) +{ + struct i2c_msm_xfer_buf *cur_buf = &ctrl->xfer.cur_buf; + struct i2c_msg *cur_msg = ctrl->xfer.msgs + cur_buf->msg_idx; + int bc_rem = cur_msg->len - cur_buf->end_idx; + + if (cur_buf->is_init && cur_buf->end_idx && bc_rem) { + /* not the first buffer in a message */ + + cur_buf->byte_idx = cur_buf->end_idx; + cur_buf->is_last = i2c_msm_xfer_buf_is_last(ctrl); + cur_buf->len = min_t(int, bc_rem, QUP_MAX_BUF_SZ); + cur_buf->end_idx += cur_buf->len; + + /* No Start is required if it is not a first buffer in msg */ + i2c_msm_xfer_create_cur_tag(ctrl, false); + } else { + /* first buffer in a new message */ + if (cur_buf->is_init) { + if (i2c_msm_xfer_msg_is_last(ctrl)) + return false; + + ++cur_buf->msg_idx; + ++cur_msg; + } else { + cur_buf->is_init = true; + } + cur_buf->byte_idx = 0; + cur_buf->is_last = i2c_msm_xfer_buf_is_last(ctrl); + cur_buf->len = min_t(int, cur_msg->len, QUP_MAX_BUF_SZ); + cur_buf->is_rx = (cur_msg->flags & I2C_M_RD); + cur_buf->end_idx = cur_buf->len; + cur_buf->slv_addr = i2c_msm_slv_rd_wr_addr(cur_msg->addr, + cur_buf->is_rx); + i2c_msm_xfer_create_cur_tag(ctrl, true); + } + i2c_msm_prof_evnt_add(ctrl, MSM_DBG, I2C_MSM_NEXT_BUF, cur_buf->msg_idx, + cur_buf->byte_idx, 0); + return true; +} + +static void i2c_msm_pm_clk_unprepare(struct i2c_msm_ctrl *ctrl) +{ + clk_unprepare(ctrl->rsrcs.core_clk); + clk_unprepare(ctrl->rsrcs.iface_clk); +} + +static int i2c_msm_pm_clk_prepare(struct i2c_msm_ctrl *ctrl) +{ + int ret; + + ret = clk_prepare(ctrl->rsrcs.iface_clk); + + if (ret) { + dev_err(ctrl->dev, + "error on clk_prepare(iface_clk):%d\n", ret); + return ret; + } + + ret = clk_prepare(ctrl->rsrcs.core_clk); + if (ret) { + clk_unprepare(ctrl->rsrcs.iface_clk); + dev_err(ctrl->dev, + "error clk_prepare(core_clk):%d\n", ret); + } + return ret; +} + +static void i2c_msm_pm_clk_disable(struct i2c_msm_ctrl *ctrl) +{ + clk_disable(ctrl->rsrcs.core_clk); + clk_disable(ctrl->rsrcs.iface_clk); +} + +static int i2c_msm_pm_clk_enable(struct i2c_msm_ctrl *ctrl) +{ + int ret; + + ret = clk_enable(ctrl->rsrcs.iface_clk); + if (ret) { + dev_err(ctrl->dev, + "error on clk_enable(iface_clk):%d\n", ret); + i2c_msm_pm_clk_unprepare(ctrl); + return ret; + } + ret = clk_enable(ctrl->rsrcs.core_clk); + if (ret) { + clk_disable(ctrl->rsrcs.iface_clk); + i2c_msm_pm_clk_unprepare(ctrl); + dev_err(ctrl->dev, + "error clk_enable(core_clk):%d\n", ret); + } + return ret; +} + +static int i2c_msm_pm_xfer_start(struct i2c_msm_ctrl *ctrl) +{ + int ret; + + mutex_lock(&ctrl->xfer.mtx); + + i2c_msm_pm_pinctrl_state(ctrl, true); + pm_runtime_get_sync(ctrl->dev); + /* + * if runtime PM callback was not invoked (when both runtime-pm + * and systme-pm are in transition concurrently) + */ + if (ctrl->pwr_state != I2C_MSM_PM_RT_ACTIVE) { + dev_info(ctrl->dev, "Runtime PM-callback was not invoked\n"); + i2c_msm_pm_resume(ctrl->dev); + } + + ret = i2c_msm_pm_clk_enable(ctrl); + if (ret) { + mutex_unlock(&ctrl->xfer.mtx); + return ret; + } + i2c_msm_qup_init(ctrl); + + /* Set xfer to active state (efectively enabling our ISR)*/ + atomic_set(&ctrl->xfer.is_active, 1); + + enable_irq(ctrl->rsrcs.irq); + return 0; +} + +static void i2c_msm_pm_xfer_end(struct i2c_msm_ctrl *ctrl) +{ + + atomic_set(&ctrl->xfer.is_active, 0); + + /* + * DMA resources are freed due to multi-EE use case. + * Other EEs can potentially use the DMA + * resources with in the same runtime PM vote. + */ + if (ctrl->xfer.mode_id == I2C_MSM_XFER_MODE_DMA) + i2c_msm_dma_free_channels(ctrl); + + i2c_msm_pm_clk_disable(ctrl); + + if (!pm_runtime_enabled(ctrl->dev)) + i2c_msm_pm_suspend(ctrl->dev); + + pm_runtime_mark_last_busy(ctrl->dev); + pm_runtime_put_autosuspend(ctrl->dev); + i2c_msm_pm_pinctrl_state(ctrl, false); + mutex_unlock(&ctrl->xfer.mtx); +} + +/* + * i2c_msm_xfer_scan: initial input scan + */ +static void i2c_msm_xfer_scan(struct i2c_msm_ctrl *ctrl) +{ + struct i2c_msm_xfer *xfer = &ctrl->xfer; + struct i2c_msm_xfer_buf *cur_buf = &xfer->cur_buf; + + while (i2c_msm_xfer_next_buf(ctrl)) { + + if (cur_buf->is_rx) + xfer->rx_cnt += cur_buf->len; + else + xfer->tx_cnt += cur_buf->len; + + xfer->rx_ovrhd_cnt += cur_buf->in_tag.len; + xfer->tx_ovrhd_cnt += cur_buf->out_tag.len; + + if (i2c_msm_xfer_msg_is_last(ctrl)) + xfer->last_is_rx = cur_buf->is_rx; + } + xfer->cur_buf = (struct i2c_msm_xfer_buf){0}; +} + +static int +i2c_msm_frmwrk_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) +{ + int ret = 0; + struct i2c_msm_ctrl *ctrl = i2c_get_adapdata(adap); + struct i2c_msm_xfer *xfer = &ctrl->xfer; + + if (IS_ERR_OR_NULL(msgs)) { + dev_err(ctrl->dev, " error on msgs Accessing invalid pointer location\n"); + return (msgs) ? PTR_ERR(msgs) : -EINVAL; + } + + /* if system is suspended just bail out */ + if (ctrl->pwr_state == I2C_MSM_PM_SYS_SUSPENDED) { + dev_err(ctrl->dev, + "slave:0x%x is calling xfer when system is suspended\n", + msgs->addr); + return -EIO; + } + + ret = i2c_msm_pm_xfer_start(ctrl); + if (ret) + return ret; + + /* init xfer */ + xfer->msgs = msgs; + xfer->msg_cnt = num; + xfer->mode_id = I2C_MSM_XFER_MODE_NONE; + xfer->err = 0; + xfer->rx_cnt = 0; + xfer->tx_cnt = 0; + xfer->rx_ovrhd_cnt = 0; + xfer->tx_ovrhd_cnt = 0; + atomic_set(&xfer->event_cnt, 0); + init_completion(&xfer->complete); + init_completion(&xfer->rx_complete); + + xfer->cur_buf.is_init = false; + xfer->cur_buf.msg_idx = 0; + + i2c_msm_prof_evnt_add(ctrl, MSM_PROF, I2C_MSM_XFER_BEG, num, + msgs->addr, 0); + + i2c_msm_xfer_scan(ctrl); + i2c_msm_xfer_calc_timeout(ctrl); + xfer->mode_id = i2c_msm_qup_choose_mode(ctrl); + + dev_dbg(ctrl->dev, "xfer() mode:%d msg_cnt:%d rx_cbt:%zu tx_cnt:%zu\n", + xfer->mode_id, xfer->msg_cnt, xfer->rx_cnt, xfer->tx_cnt); + + switch (xfer->mode_id) { + case I2C_MSM_XFER_MODE_FIFO: + ret = i2c_msm_fifo_xfer(ctrl); + break; + case I2C_MSM_XFER_MODE_BLOCK: + ret = i2c_msm_blk_xfer(ctrl); + break; + case I2C_MSM_XFER_MODE_DMA: + ret = i2c_msm_dma_xfer(ctrl); + break; + default: + ret = -EINTR; + } + + i2c_msm_prof_evnt_add(ctrl, MSM_PROF, I2C_MSM_SCAN_SUM, + ((xfer->rx_cnt & 0xff) | ((xfer->rx_ovrhd_cnt & 0xff) << 16)), + ((xfer->tx_cnt & 0xff) | ((xfer->tx_ovrhd_cnt & 0xff) << 16)), + ((ctrl->xfer.timeout & 0xfff) | ((xfer->mode_id & 0xf) << 24))); + + ret = i2c_msm_qup_post_xfer(ctrl, ret); + /* on success, return number of messages sent (which is index + 1)*/ + if (!ret) + ret = xfer->cur_buf.msg_idx + 1; + + i2c_msm_prof_evnt_add(ctrl, MSM_PROF, I2C_MSM_XFER_END, ret, xfer->err, + xfer->cur_buf.msg_idx + 1); + /* process and dump profiling data */ + if (xfer->err || (ctrl->dbgfs.dbg_lvl >= MSM_PROF)) + i2c_msm_prof_evnt_dump(ctrl); + + i2c_msm_pm_xfer_end(ctrl); + return ret; +} + +enum i2c_msm_dt_entry_status { + DT_REQ, /* Required: fail if missing */ + DT_SGST, /* Suggested: warn if missing */ + DT_OPT, /* Optional: don't warn if missing */ +}; + +enum i2c_msm_dt_entry_type { + DT_U32, + DT_BOOL, + DT_ID, /* of_alias_get_id() */ +}; + +struct i2c_msm_dt_to_pdata_map { + const char *dt_name; + void *ptr_data; + enum i2c_msm_dt_entry_status status; + enum i2c_msm_dt_entry_type type; + int default_val; +}; + +static int i2c_msm_dt_to_pdata_populate(struct i2c_msm_ctrl *ctrl, + struct platform_device *pdev, + struct i2c_msm_dt_to_pdata_map *itr) +{ + int ret, err = 0; + struct device_node *node = pdev->dev.of_node; + + for (; itr->dt_name ; ++itr) { + switch (itr->type) { + case DT_U32: + ret = of_property_read_u32(node, itr->dt_name, + (u32 *) itr->ptr_data); + break; + case DT_BOOL: + *((bool *) itr->ptr_data) = + of_property_read_bool(node, itr->dt_name); + ret = 0; + break; + case DT_ID: + ret = of_alias_get_id(node, itr->dt_name); + if (ret >= 0) { + *((int *) itr->ptr_data) = ret; + ret = 0; + } + break; + default: + dev_err(ctrl->dev, + "error %d is of unknown DT entry type\n", + itr->type); + ret = -EBADE; + } + + i2c_msm_dbg(ctrl, MSM_PROF, "DT entry ret:%d name:%s val:%d\n", + ret, itr->dt_name, *((int *)itr->ptr_data)); + + if (ret) { + *((int *)itr->ptr_data) = itr->default_val; + + if (itr->status < DT_OPT) { + dev_err(ctrl->dev, + "error Missing '%s' DT entry\n", + itr->dt_name); + + /* cont on err to dump all missing entries */ + if (itr->status == DT_REQ && !err) + err = ret; + } + } + } + + return err; +} + + +/* + * i2c_msm_rsrcs_process_dt: copy data from DT to platform data + * @return zero on success or negative error code + */ +static int i2c_msm_rsrcs_process_dt(struct i2c_msm_ctrl *ctrl, + struct platform_device *pdev) +{ + u32 fs_clk_div, ht_clk_div, noise_rjct_scl, noise_rjct_sda; + int ret; + + struct i2c_msm_dt_to_pdata_map map[] = { + {"i2c", &pdev->id, DT_REQ, DT_ID, -1}, + {"qcom,clk-freq-out", &ctrl->rsrcs.clk_freq_out, + DT_REQ, DT_U32, 0}, + {"qcom,clk-freq-in", &ctrl->rsrcs.clk_freq_in, + DT_REQ, DT_U32, 0}, + {"qcom,disable-dma", &(ctrl->rsrcs.disable_dma), + DT_OPT, DT_BOOL, 0}, + {"qcom,master-id", &(ctrl->rsrcs.clk_path_vote.mstr_id), + DT_SGST, DT_U32, 0}, + {"qcom,noise-rjct-scl", &noise_rjct_scl, + DT_OPT, DT_U32, 0}, + {"qcom,noise-rjct-sda", &noise_rjct_sda, + DT_OPT, DT_U32, 0}, + {"qcom,high-time-clk-div", &ht_clk_div, + DT_OPT, DT_U32, 0}, + {"qcom,fs-clk-div", &fs_clk_div, + DT_OPT, DT_U32, 0}, + {NULL, NULL, 0, 0, 0}, + }; + + ret = i2c_msm_dt_to_pdata_populate(ctrl, pdev, map); + if (ret) + return ret; + + /* set divider and noise reject values */ + return i2c_msm_set_mstr_clk_ctl(ctrl, fs_clk_div, ht_clk_div, + noise_rjct_scl, noise_rjct_sda); +} + +/* + * i2c_msm_rsrcs_mem_init: reads pdata request region and ioremap it + * @return zero on success or negative error code + */ +static int i2c_msm_rsrcs_mem_init(struct platform_device *pdev, + struct i2c_msm_ctrl *ctrl) +{ + struct resource *mem_region; + + ctrl->rsrcs.mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "qup_phys_addr"); + if (!ctrl->rsrcs.mem) { + dev_err(ctrl->dev, "error Missing 'qup_phys_addr' resource\n"); + return -ENODEV; + } + + mem_region = request_mem_region(ctrl->rsrcs.mem->start, + resource_size(ctrl->rsrcs.mem), + pdev->name); + if (!mem_region) { + dev_err(ctrl->dev, + "QUP physical memory region already claimed\n"); + return -EBUSY; + } + + ctrl->rsrcs.base = devm_ioremap(ctrl->dev, ctrl->rsrcs.mem->start, + resource_size(ctrl->rsrcs.mem)); + if (!ctrl->rsrcs.base) { + dev_err(ctrl->dev, + "error failed ioremap(base:0x%llx size:0x%llx\n)\n", + (u64) ctrl->rsrcs.mem->start, + (u64) resource_size(ctrl->rsrcs.mem)); + release_mem_region(ctrl->rsrcs.mem->start, + resource_size(ctrl->rsrcs.mem)); + return -ENOMEM; + } + + return 0; +} + +static void i2c_msm_rsrcs_mem_teardown(struct i2c_msm_ctrl *ctrl) +{ + release_mem_region(ctrl->rsrcs.mem->start, + resource_size(ctrl->rsrcs.mem)); +} + +/* + * i2c_msm_rsrcs_irq_init: finds irq num in pdata and requests it + * @return zero on success or negative error code + */ +static int i2c_msm_rsrcs_irq_init(struct platform_device *pdev, + struct i2c_msm_ctrl *ctrl) +{ + int ret, irq; + + irq = platform_get_irq_byname(pdev, "qup_irq"); + if (irq < 0) { + dev_err(ctrl->dev, "error reading irq resource\n"); + return irq; + } + + ret = request_irq(irq, i2c_msm_qup_isr, IRQF_TRIGGER_HIGH, + "i2c-msm-v2-irq", ctrl); + if (ret) { + dev_err(ctrl->dev, "error request_irq(irq_num:%d ) ret:%d\n", + irq, ret); + return ret; + } + + disable_irq(irq); + ctrl->rsrcs.irq = irq; + return 0; +} + +static void i2c_msm_rsrcs_irq_teardown(struct i2c_msm_ctrl *ctrl) +{ + free_irq(ctrl->rsrcs.irq, ctrl); +} + + +static struct pinctrl_state * +i2c_msm_rsrcs_gpio_get_state(struct i2c_msm_ctrl *ctrl, const char *name) +{ + struct pinctrl_state *pin_state + = pinctrl_lookup_state(ctrl->rsrcs.pinctrl, name); + + if (IS_ERR_OR_NULL(pin_state)) + dev_info(ctrl->dev, "note pinctrl_lookup_state(%s) err:%ld\n", + name, PTR_ERR(pin_state)); + return pin_state; +} + +/* + * i2c_msm_rsrcs_gpio_pinctrl_init: initializes the pinctrl for i2c gpios + * + * @pre platform data must be initialized + */ +static int i2c_msm_rsrcs_gpio_pinctrl_init(struct i2c_msm_ctrl *ctrl) +{ + ctrl->rsrcs.pinctrl = devm_pinctrl_get(ctrl->dev); + if (IS_ERR_OR_NULL(ctrl->rsrcs.pinctrl)) { + dev_err(ctrl->dev, "error devm_pinctrl_get() failed err:%ld\n", + PTR_ERR(ctrl->rsrcs.pinctrl)); + return PTR_ERR(ctrl->rsrcs.pinctrl); + } + + ctrl->rsrcs.gpio_state_active = + i2c_msm_rsrcs_gpio_get_state(ctrl, I2C_MSM_PINCTRL_ACTIVE); + + ctrl->rsrcs.gpio_state_suspend = + i2c_msm_rsrcs_gpio_get_state(ctrl, I2C_MSM_PINCTRL_SUSPEND); + + return 0; +} + +static void i2c_msm_pm_pinctrl_state(struct i2c_msm_ctrl *ctrl, + bool runtime_active) +{ + struct pinctrl_state *pins_state; + const char *pins_state_name; + + if (runtime_active) { + pins_state = ctrl->rsrcs.gpio_state_active; + pins_state_name = I2C_MSM_PINCTRL_ACTIVE; + } else { + pins_state = ctrl->rsrcs.gpio_state_suspend; + pins_state_name = I2C_MSM_PINCTRL_SUSPEND; + } + + if (!IS_ERR_OR_NULL(pins_state)) { + int ret = pinctrl_select_state(ctrl->rsrcs.pinctrl, pins_state); + + if (ret) + dev_err(ctrl->dev, + "error pinctrl_select_state(%s) err:%d\n", + pins_state_name, ret); + } else { + dev_err(ctrl->dev, + "error pinctrl state-name:'%s' is not configured\n", + pins_state_name); + } +} + +/* + * i2c_msm_rsrcs_clk_init: get clocks and set rate + * + * @return zero on success or negative error code + */ +static int i2c_msm_rsrcs_clk_init(struct i2c_msm_ctrl *ctrl) +{ + int ret = 0; + + if ((ctrl->rsrcs.clk_freq_out <= 0) || + (ctrl->rsrcs.clk_freq_out > I2C_MSM_CLK_FAST_PLUS_FREQ)) { + dev_err(ctrl->dev, + "error clock frequency %dKHZ is not supported\n", + (ctrl->rsrcs.clk_freq_out / 1000)); + return -EIO; + } + + ctrl->rsrcs.core_clk = clk_get(ctrl->dev, "core_clk"); + if (IS_ERR(ctrl->rsrcs.core_clk)) { + ret = PTR_ERR(ctrl->rsrcs.core_clk); + dev_err(ctrl->dev, "error on clk_get(core_clk):%d\n", ret); + return ret; + } + + ret = clk_set_rate(ctrl->rsrcs.core_clk, ctrl->rsrcs.clk_freq_in); + if (ret) { + dev_err(ctrl->dev, "error on clk_set_rate(core_clk, %dKHz):%d\n", + (ctrl->rsrcs.clk_freq_in / 1000), ret); + goto err_set_rate; + } + + ctrl->rsrcs.iface_clk = clk_get(ctrl->dev, "iface_clk"); + if (IS_ERR(ctrl->rsrcs.iface_clk)) { + ret = PTR_ERR(ctrl->rsrcs.iface_clk); + dev_err(ctrl->dev, "error on clk_get(iface_clk):%d\n", ret); + goto err_set_rate; + } + + return 0; + +err_set_rate: + clk_put(ctrl->rsrcs.core_clk); + ctrl->rsrcs.core_clk = NULL; + return ret; +} + +static void i2c_msm_rsrcs_clk_teardown(struct i2c_msm_ctrl *ctrl) +{ + clk_put(ctrl->rsrcs.core_clk); + clk_put(ctrl->rsrcs.iface_clk); + i2c_msm_clk_path_teardown(ctrl); +} + + + +static void i2c_msm_pm_suspend(struct device *dev) +{ + struct i2c_msm_ctrl *ctrl = dev_get_drvdata(dev); + + if (ctrl->pwr_state == I2C_MSM_PM_RT_SUSPENDED) { + dev_err(ctrl->dev, "attempt to suspend when suspended\n"); + return; + } + i2c_msm_dbg(ctrl, MSM_DBG, "suspending...\n"); + i2c_msm_pm_clk_unprepare(ctrl); + i2c_msm_clk_path_unvote(ctrl); + + /* + * We implement system and runtime suspend in the same way. However + * it is important for us to distinguish between them in when servicing + * a transfer requests. If we get transfer request while in runtime + * suspend we want to simply wake up and service that request. But if we + * get a transfer request while system is suspending we want to bail + * out on that request. This is why if we marked that we are in system + * suspend, we do not want to override that state with runtime suspend. + */ + if (ctrl->pwr_state != I2C_MSM_PM_SYS_SUSPENDED) + ctrl->pwr_state = I2C_MSM_PM_RT_SUSPENDED; +} + +static int i2c_msm_pm_resume(struct device *dev) +{ + struct i2c_msm_ctrl *ctrl = dev_get_drvdata(dev); + + if (ctrl->pwr_state == I2C_MSM_PM_RT_ACTIVE) + return 0; + + i2c_msm_dbg(ctrl, MSM_DBG, "resuming...\n"); + + i2c_msm_clk_path_vote(ctrl); + i2c_msm_pm_clk_prepare(ctrl); + ctrl->pwr_state = I2C_MSM_PM_RT_ACTIVE; + return 0; +} + +#ifdef CONFIG_PM +/* + * i2c_msm_pm_sys_suspend_noirq: system power management callback + */ +static int i2c_msm_pm_sys_suspend_noirq(struct device *dev) +{ + struct i2c_msm_ctrl *ctrl = dev_get_drvdata(dev); + enum i2c_msm_power_state prev_state = ctrl->pwr_state; + + i2c_msm_dbg(ctrl, MSM_DBG, "pm_sys_noirq: suspending...\n"); + + /* Acquire mutex to ensure current transaction is over */ + mutex_lock(&ctrl->xfer.mtx); + ctrl->pwr_state = I2C_MSM_PM_SYS_SUSPENDED; + mutex_unlock(&ctrl->xfer.mtx); + i2c_msm_dbg(ctrl, MSM_DBG, "pm_sys_noirq: suspending...\n"); + + if (prev_state == I2C_MSM_PM_RT_ACTIVE) { + i2c_msm_pm_suspend(dev); + /* + * Synchronize runtime-pm and system-pm states: + * at this point we are already suspended. However, the + * runtime-PM framework still thinks that we are active. + * The three calls below let the runtime-PM know that we are + * suspended already without re-invoking the suspend callback + */ + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_enable(dev); + } + + return 0; +} + +/* + * i2c_msm_pm_sys_resume: system power management callback + * shifts the controller's power state from system suspend to runtime suspend + */ +static int i2c_msm_pm_sys_resume_noirq(struct device *dev) +{ + struct i2c_msm_ctrl *ctrl = dev_get_drvdata(dev); + + i2c_msm_dbg(ctrl, MSM_DBG, "pm_sys_noirq: resuming...\n"); + mutex_lock(&ctrl->xfer.mtx); + ctrl->pwr_state = I2C_MSM_PM_RT_SUSPENDED; + mutex_unlock(&ctrl->xfer.mtx); + return 0; +} +#endif + +#ifdef CONFIG_PM +static void i2c_msm_pm_rt_init(struct device *dev) +{ + pm_runtime_set_suspended(dev); + pm_runtime_set_autosuspend_delay(dev, (MSEC_PER_SEC >> 2)); + pm_runtime_use_autosuspend(dev); + pm_runtime_enable(dev); +} + +/* + * i2c_msm_pm_rt_suspend: runtime power management callback + */ +static int i2c_msm_pm_rt_suspend(struct device *dev) +{ + struct i2c_msm_ctrl *ctrl = dev_get_drvdata(dev); + + i2c_msm_dbg(ctrl, MSM_DBG, "pm_runtime: suspending...\n"); + i2c_msm_pm_suspend(dev); + return 0; +} + +/* + * i2c_msm_pm_rt_resume: runtime power management callback + */ +static int i2c_msm_pm_rt_resume(struct device *dev) +{ + struct i2c_msm_ctrl *ctrl = dev_get_drvdata(dev); + + i2c_msm_dbg(ctrl, MSM_DBG, "pm_runtime: resuming...\n"); + return i2c_msm_pm_resume(dev); +} + +#else +static void i2c_msm_pm_rt_init(struct device *dev) {} +#define i2c_msm_pm_rt_suspend NULL +#define i2c_msm_pm_rt_resume NULL +#endif + +static const struct dev_pm_ops i2c_msm_pm_ops = { +#ifdef CONFIG_PM_SLEEP + .suspend_noirq = i2c_msm_pm_sys_suspend_noirq, + .resume_noirq = i2c_msm_pm_sys_resume_noirq, +#endif + SET_RUNTIME_PM_OPS(i2c_msm_pm_rt_suspend, + i2c_msm_pm_rt_resume, + NULL) +}; + +static u32 i2c_msm_frmwrk_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); +} + +static const struct i2c_algorithm i2c_msm_frmwrk_algrtm = { + .master_xfer = i2c_msm_frmwrk_xfer, + .functionality = i2c_msm_frmwrk_func, +}; + +static const char * const i2c_msm_adapter_name = "MSM-I2C-v2-adapter"; + +static int i2c_msm_frmwrk_reg(struct platform_device *pdev, + struct i2c_msm_ctrl *ctrl) +{ + int ret; + + i2c_set_adapdata(&ctrl->adapter, ctrl); + ctrl->adapter.algo = &i2c_msm_frmwrk_algrtm; + strlcpy(ctrl->adapter.name, i2c_msm_adapter_name, + sizeof(ctrl->adapter.name)); + + ctrl->adapter.nr = pdev->id; + ctrl->adapter.dev.parent = &pdev->dev; + ctrl->adapter.dev.of_node = pdev->dev.of_node; + ret = i2c_add_numbered_adapter(&ctrl->adapter); + if (ret) { + dev_err(ctrl->dev, "error i2c_add_adapter failed\n"); + return ret; + } + + return ret; +} + +static void i2c_msm_frmwrk_unreg(struct i2c_msm_ctrl *ctrl) +{ + i2c_del_adapter(&ctrl->adapter); +} + +static int i2c_msm_probe(struct platform_device *pdev) +{ + struct i2c_msm_ctrl *ctrl; + int ret = 0; + + ctrl = devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + ctrl->dev = &pdev->dev; + platform_set_drvdata(pdev, ctrl); + ctrl->dbgfs.dbg_lvl = DEFAULT_DBG_LVL; + ctrl->dbgfs.force_xfer_mode = I2C_MSM_XFER_MODE_NONE; + mutex_init(&ctrl->xfer.mtx); + ctrl->pwr_state = I2C_MSM_PM_RT_SUSPENDED; + + if (!pdev->dev.of_node) { + dev_err(&pdev->dev, "error: null device-tree node\n"); + return -EBADE; + } + + ret = i2c_msm_rsrcs_process_dt(ctrl, pdev); + if (ret) { + dev_err(ctrl->dev, "error in process device tree node\n"); + return ret; + } + + ret = i2c_msm_rsrcs_mem_init(pdev, ctrl); + if (ret) + goto mem_err; + + ret = i2c_msm_rsrcs_clk_init(ctrl); + if (ret) + goto clk_err; + + /* vote for clock to enable reading the version number off the HW */ + i2c_msm_clk_path_vote(ctrl); + + ret = i2c_msm_pm_clk_prepare(ctrl); + if (ret) + goto clk_err; + + ret = i2c_msm_pm_clk_enable(ctrl); + if (ret) { + i2c_msm_pm_clk_unprepare(ctrl); + goto clk_err; + } + + /* + * reset the core before registering for interrupts. This solves an + * interrupt storm issue when the bootloader leaves a pending interrupt. + */ + ret = i2c_msm_qup_sw_reset(ctrl); + if (ret) + dev_err(ctrl->dev, "error error on qup software reset\n"); + + i2c_msm_pm_clk_disable(ctrl); + i2c_msm_pm_clk_unprepare(ctrl); + i2c_msm_clk_path_unvote(ctrl); + + ret = i2c_msm_rsrcs_gpio_pinctrl_init(ctrl); + if (ret) + goto err_no_pinctrl; + + i2c_msm_pm_rt_init(ctrl->dev); + + ret = i2c_msm_rsrcs_irq_init(pdev, ctrl); + if (ret) + goto irq_err; + + i2c_msm_dbgfs_init(ctrl); + + ret = i2c_msm_frmwrk_reg(pdev, ctrl); + if (ret) + goto reg_err; + + i2c_msm_dbg(ctrl, MSM_PROF, "probe() completed with success\n"); + return 0; + +reg_err: + i2c_msm_dbgfs_teardown(ctrl); + i2c_msm_rsrcs_irq_teardown(ctrl); +irq_err: + i2x_msm_blk_free_cache(ctrl); +err_no_pinctrl: + i2c_msm_rsrcs_clk_teardown(ctrl); +clk_err: + i2c_msm_rsrcs_mem_teardown(ctrl); +mem_err: + dev_err(ctrl->dev, "error probe() failed with err:%d\n", ret); + return ret; +} + +static int i2c_msm_remove(struct platform_device *pdev) +{ + struct i2c_msm_ctrl *ctrl = platform_get_drvdata(pdev); + + /* Grab mutex to ensure ongoing transaction is over */ + mutex_lock(&ctrl->xfer.mtx); + ctrl->pwr_state = I2C_MSM_PM_SYS_SUSPENDED; + pm_runtime_disable(ctrl->dev); + /* no one can call a xfer after the next line */ + i2c_msm_frmwrk_unreg(ctrl); + mutex_unlock(&ctrl->xfer.mtx); + mutex_destroy(&ctrl->xfer.mtx); + + i2c_msm_dma_teardown(ctrl); + i2c_msm_dbgfs_teardown(ctrl); + i2c_msm_rsrcs_irq_teardown(ctrl); + i2c_msm_rsrcs_clk_teardown(ctrl); + i2c_msm_rsrcs_mem_teardown(ctrl); + i2x_msm_blk_free_cache(ctrl); + return 0; +} + +static const struct of_device_id i2c_msm_dt_match[] = { + { + .compatible = "qcom,i2c-msm-v2", + }, + {} +}; + +static struct platform_driver i2c_msm_driver = { + .probe = i2c_msm_probe, + .remove = i2c_msm_remove, + .driver = { + .name = "i2c-msm-v2", + .pm = &i2c_msm_pm_ops, + .of_match_table = i2c_msm_dt_match, + }, +}; + +static int i2c_msm_init(void) +{ + return platform_driver_register(&i2c_msm_driver); +} +subsys_initcall(i2c_msm_init); + +static void i2c_msm_exit(void) +{ + platform_driver_unregister(&i2c_msm_driver); +} +module_exit(i2c_msm_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:i2c-msm-v2"); diff --git a/drivers/i2c/busses/i2c-stm32.c b/drivers/i2c/busses/i2c-stm32.c index d75fbcbf02ef338fa2f7e9b0d322cc6948157309..667f8032f8ef69a99cd3b2beb8c451232ccb4bc7 100644 --- a/drivers/i2c/busses/i2c-stm32.c +++ b/drivers/i2c/busses/i2c-stm32.c @@ -21,13 +21,13 @@ struct stm32_i2c_dma *stm32_i2c_dma_request(struct device *dev, dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); if (!dma) - return NULL; + return ERR_PTR(-ENOMEM); /* Request and configure I2C TX dma channel */ - dma->chan_tx = dma_request_slave_channel(dev, "tx"); - if (!dma->chan_tx) { + dma->chan_tx = dma_request_chan(dev, "tx"); + if (IS_ERR(dma->chan_tx)) { dev_dbg(dev, "can't request DMA tx channel\n"); - ret = -EINVAL; + ret = PTR_ERR(dma->chan_tx); goto fail_al; } @@ -43,10 +43,10 @@ struct stm32_i2c_dma *stm32_i2c_dma_request(struct device *dev, } /* Request and configure I2C RX dma channel */ - dma->chan_rx = dma_request_slave_channel(dev, "rx"); - if (!dma->chan_rx) { + dma->chan_rx = dma_request_chan(dev, "rx"); + if (IS_ERR(dma->chan_rx)) { dev_err(dev, "can't request DMA rx channel\n"); - ret = -EINVAL; + ret = PTR_ERR(dma->chan_rx); goto fail_tx; } @@ -76,7 +76,7 @@ struct stm32_i2c_dma *stm32_i2c_dma_request(struct device *dev, devm_kfree(dev, dma); dev_info(dev, "can't use DMA\n"); - return NULL; + return ERR_PTR(ret); } void stm32_i2c_dma_free(struct stm32_i2c_dma *dma) diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c index f4e3613f9361b1e3b53d80ecd7012b63323fc863..eb7e533b0dd47b4bac853a89a1c9a5d5a76da22d 100644 --- a/drivers/i2c/busses/i2c-stm32f7.c +++ b/drivers/i2c/busses/i2c-stm32f7.c @@ -1252,8 +1252,8 @@ static int stm32f7_i2c_get_free_slave_id(struct stm32f7_i2c_dev *i2c_dev, * slave[0] supports 7-bit and 10-bit slave address * slave[1] supports 7-bit slave address only */ - for (i = 0; i < STM32F7_I2C_MAX_SLAVE; i++) { - if (i == 1 && (slave->flags & I2C_CLIENT_PEC)) + for (i = STM32F7_I2C_MAX_SLAVE - 1; i >= 0; i--) { + if (i == 1 && (slave->flags & I2C_CLIENT_TEN)) continue; if (!i2c_dev->slave[i]) { *id = i; @@ -1914,6 +1914,15 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) i2c_dev->dma = stm32_i2c_dma_request(i2c_dev->dev, phy_addr, STM32F7_I2C_TXDR, STM32F7_I2C_RXDR); + if (PTR_ERR(i2c_dev->dma) == -ENODEV) + i2c_dev->dma = NULL; + else if (IS_ERR(i2c_dev->dma)) { + ret = PTR_ERR(i2c_dev->dma); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, + "Failed to request dma error %i\n", ret); + goto clk_free; + } ret = i2c_add_adapter(adap); if (ret) diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 1de10e5c70d7b661809bf20a02f4d8d057b02d1e..f225bef1e043c9e8de71318f7ccfe1d9b726773a 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -194,10 +194,11 @@ int i2c_generic_scl_recovery(struct i2c_adapter *adap) * If we can set SDA, we will always create a STOP to ensure additional * pulses will do no harm. This is achieved by letting SDA follow SCL * half a cycle later. Check the 'incomplete_write_byte' fault injector - * for details. + * for details. Note that we must honour tsu:sto, 4us, but lets use 5us + * here for simplicity. */ bri->set_scl(adap, scl); - ndelay(RECOVERY_NDELAY / 2); + ndelay(RECOVERY_NDELAY); if (bri->set_sda) bri->set_sda(adap, scl); ndelay(RECOVERY_NDELAY / 2); @@ -219,7 +220,13 @@ int i2c_generic_scl_recovery(struct i2c_adapter *adap) scl = !scl; bri->set_scl(adap, scl); /* Creating STOP again, see above */ - ndelay(RECOVERY_NDELAY / 2); + if (scl) { + /* Honour minimum tsu:sto */ + ndelay(RECOVERY_NDELAY); + } else { + /* Honour minimum tf and thd:dat */ + ndelay(RECOVERY_NDELAY / 2); + } if (bri->set_sda) bri->set_sda(adap, scl); ndelay(RECOVERY_NDELAY / 2); diff --git a/drivers/i3c/master/i3c-master-qcom-geni.c b/drivers/i3c/master/i3c-master-qcom-geni.c index d9f111e52385aa6854a469759b35d34d4f82398a..b45032317d31790701de9ce2261e8e07ee61347a 100644 --- a/drivers/i3c/master/i3c-master-qcom-geni.c +++ b/drivers/i3c/master/i3c-master-qcom-geni.c @@ -273,7 +273,8 @@ struct geni_i3c_dev { struct geni_ibi ibi; struct workqueue_struct *hj_wq; struct work_struct hj_wd; - struct wakeup_source hj_wl; + struct wakeup_source *hj_wl; + struct pinctrl_state *i3c_gpio_disable; }; struct geni_i3c_i2c_dev_data { @@ -735,7 +736,7 @@ static int _i3c_geni_execute_command { dma_addr_t tx_dma = 0; dma_addr_t rx_dma = 0; - int ret, time_remaining = 0; + int ret = 0, time_remaining = 0; enum i3c_trans_dir rnw = gi3c->cur_rnw; u32 len = gi3c->cur_len; @@ -816,10 +817,12 @@ static int _i3c_geni_execute_command geni_se_tx_dma_unprep(gi3c->se.i3c_rsc.wrapper_dev, tx_dma, len); } - ret = gi3c->err; - if (gi3c->err) + + if (gi3c->err) { + ret = (gi3c->err == -EBUSY) ? I3C_ERROR_M2 : gi3c->err; GENI_SE_DBG(gi3c->ipcl, false, gi3c->se.dev, "I3C transaction error :%d\n", gi3c->err); + } gi3c->cur_buf = NULL; gi3c->cur_len = gi3c->cur_idx = 0; @@ -1523,10 +1526,8 @@ static int geni_i3c_master_request_ibi(struct i3c_dev_desc *dev, return -ENOSPC; } -static int qcom_deallocate_ibi_table_entry(struct i3c_dev_desc *dev) +static int qcom_deallocate_ibi_table_entry(struct geni_i3c_dev *gi3c) { - struct i3c_master_controller *m = i3c_dev_get_master(dev); - struct geni_i3c_dev *gi3c = to_geni_i3c_master(m); u32 i, timeout; for (i = 0; i < gi3c->ibi.num_slots; i++) { @@ -1558,16 +1559,14 @@ static int qcom_deallocate_ibi_table_entry(struct i3c_dev_desc *dev) return 0; } -static void qcom_geni_i3c_ibi_unconf(struct i3c_dev_desc *dev) +static void qcom_geni_i3c_ibi_unconf(struct geni_i3c_dev *gi3c) { - struct i3c_master_controller *m = i3c_dev_get_master(dev); - struct geni_i3c_dev *gi3c = to_geni_i3c_master(m); u32 val, timeout; int ret = 0; val = geni_read_reg(gi3c->se.ibi_base, IBI_ALLOCATED_ENTRIES_GPII(0)); if (val) { - ret = qcom_deallocate_ibi_table_entry(dev); + ret = qcom_deallocate_ibi_table_entry(gi3c); if (ret) return; } @@ -1619,7 +1618,7 @@ static void geni_i3c_master_free_ibi(struct i3c_dev_desc *dev) if (!gi3c->ibi.hw_support && !gi3c->ibi.is_init) return; - qcom_geni_i3c_ibi_unconf(dev); + qcom_geni_i3c_ibi_unconf(gi3c); spin_lock_irqsave(&gi3c->ibi.lock, flags); gi3c->ibi.slots[data->ibi] = NULL; @@ -1777,6 +1776,14 @@ static int i3c_geni_rsrcs_init(struct geni_i3c_dev *gi3c, ret = PTR_ERR(gi3c->se.i3c_rsc.geni_gpio_sleep); return ret; } + gi3c->i3c_gpio_disable = + pinctrl_lookup_state(gi3c->se.i3c_rsc.geni_pinctrl, "disable"); + if (IS_ERR(gi3c->i3c_gpio_disable)) { + GENI_SE_ERR(gi3c->ipcl, false, gi3c->se.dev, + "Error no pinctrl disable config specified\n"); + ret = PTR_ERR(gi3c->i3c_gpio_disable); + return ret; + } return 0; } @@ -1953,7 +1960,14 @@ static int geni_i3c_probe(struct platform_device *pdev) geni_se_init(gi3c->se.base, gi3c->tx_wm, tx_depth); se_config_packing(gi3c->se.base, BITS_PER_BYTE, PACKING_BYTES_PW, true); - wakeup_source_init(&gi3c->hj_wl, dev_name(gi3c->se.dev)); + gi3c->hj_wl = wakeup_source_register(gi3c->se.dev, + dev_name(gi3c->se.dev)); + if (!gi3c->hj_wl) { + GENI_SE_ERR(gi3c->ipcl, false, gi3c->se.dev, "wakeup source registration failed\n"); + se_geni_resources_off(&gi3c->se.i3c_rsc); + return -ENOMEM; + } + INIT_WORK(&gi3c->hj_wd, geni_i3c_hotjoin); gi3c->hj_wq = alloc_workqueue("%s", 0, 0, dev_name(gi3c->se.dev)); @@ -1976,23 +1990,38 @@ static int geni_i3c_probe(struct platform_device *pdev) ret = i3c_master_register(&gi3c->ctrlr, &pdev->dev, &geni_i3c_master_ops, false); if (ret) - return ret; + GENI_SE_ERR(gi3c->ipcl, false, gi3c->se.dev, + "i3c_master_register failed:%d\n", ret); + //enable hot-join IRQ also geni_write_reg(~0u, gi3c->se.ibi_base, IBI_GEN_IRQ_EN); GENI_SE_DBG(gi3c->ipcl, false, gi3c->se.dev, "I3C probed\n"); - return ret; + return 0; } static int geni_i3c_remove(struct platform_device *pdev) { struct geni_i3c_dev *gi3c = platform_get_drvdata(pdev); - int ret = 0; + int ret = 0, val = 0; + + //Disable hot-join, until next probe happens + val = geni_read_reg(gi3c->se.ibi_base, IBI_GEN_IRQ_EN); + val &= ~HOT_JOIN_IRQ_EN; + geni_write_reg(val, gi3c->se.ibi_base, IBI_GEN_IRQ_EN); + if (gi3c->ibi.is_init) + qcom_geni_i3c_ibi_unconf(gi3c); destroy_workqueue(gi3c->hj_wq); - wakeup_source_trash(&gi3c->hj_wl); - pm_runtime_disable(gi3c->se.dev); + wakeup_source_unregister(gi3c->hj_wl); + /*force suspend to avoid the auto suspend caused by driver removal*/ + pm_runtime_force_suspend(gi3c->se.dev); + ret = pinctrl_select_state(gi3c->se.i3c_rsc.geni_pinctrl, + gi3c->i3c_gpio_disable); + if (ret) + GENI_SE_DBG(gi3c->ipcl, false, gi3c->se.dev, + " i3c: pinctrl_select_state failed\n"); ret = i3c_master_unregister(&gi3c->ctrlr); if (gi3c->ipcl) ipc_log_context_destroy(gi3c->ipcl); @@ -2004,7 +2033,7 @@ static int geni_i3c_resume_noirq(struct device *dev) return 0; } -#ifdef CONFIG_PM +#if IS_ENABLED(CONFIG_PM) static int geni_i3c_runtime_suspend(struct device *dev) { struct geni_i3c_dev *gi3c = dev_get_drvdata(dev); @@ -2080,7 +2109,18 @@ static struct platform_driver geni_i3c_master = { }, }; -module_platform_driver(geni_i3c_master); +static int __init i3c_dev_init(void) +{ + return platform_driver_register(&geni_i3c_master); +} + +static void __exit i3c_dev_exit(void) +{ + platform_driver_unregister(&geni_i3c_master); +} + +module_init(i3c_dev_init); +module_exit(i3c_dev_exit); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:geni_i3c_master"); diff --git a/drivers/ide/cmd64x.c b/drivers/ide/cmd64x.c index b127ed60c73364f34a114a8a3033aa42c906b8ca..9dde8390da09bd95e91c2885d1253612c4b82ff4 100644 --- a/drivers/ide/cmd64x.c +++ b/drivers/ide/cmd64x.c @@ -65,6 +65,9 @@ static void cmd64x_program_timings(ide_drive_t *drive, u8 mode) struct ide_timing t; u8 arttim = 0; + if (drive->dn >= ARRAY_SIZE(drwtim_regs)) + return; + ide_timing_compute(drive, mode, &t, T, 0); /* diff --git a/drivers/ide/serverworks.c b/drivers/ide/serverworks.c index a97affca18abe153be1ba4b3285d25390171e69c..0f57d45484d1d97933ca6dba3fbedcf8cfcf081e 100644 --- a/drivers/ide/serverworks.c +++ b/drivers/ide/serverworks.c @@ -114,6 +114,9 @@ static void svwks_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) struct pci_dev *dev = to_pci_dev(hwif->dev); const u8 pio = drive->pio_mode - XFER_PIO_0; + if (drive->dn >= ARRAY_SIZE(drive_pci)) + return; + pci_write_config_byte(dev, drive_pci[drive->dn], pio_modes[pio]); if (svwks_csb_check(dev)) { @@ -140,6 +143,9 @@ static void svwks_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive) u8 ultra_enable = 0, ultra_timing = 0, dma_timing = 0; + if (drive->dn >= ARRAY_SIZE(drive_pci2)) + return; + pci_read_config_byte(dev, (0x56|hwif->channel), &ultra_timing); pci_read_config_byte(dev, 0x54, &ultra_enable); diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c index a71075f3a3dab862759d0a71c1dd2ac682f781cd..d5f06dffb7de5f87f4090aea94007bcc8bc0ead1 100644 --- a/drivers/iio/adc/qcom-spmi-adc5.c +++ b/drivers/iio/adc/qcom-spmi-adc5.c @@ -987,6 +987,8 @@ static const struct adc_channels adc_chans_rev2[ADC_MAX_CHANNEL] = { SCALE_HW_CALIB_THERM_100K_PULLUP) [ADC_XO_THERM_PU2] = ADC_CHAN_TEMP("xo_therm", 1, SCALE_HW_CALIB_THERM_100K_PULLUP) + [ANA_IN] = ADC_CHAN_TEMP("drax_temp", 1, + SCALE_HW_CALIB_PMIC_THERM) }; static int adc_get_dt_channel_data(struct adc_chip *adc, diff --git a/drivers/iio/dac/ad5380.c b/drivers/iio/dac/ad5380.c index 873c2bf637c0c6ab5d33259b91847294b1f68772..617c9f7fe59a113915bc18830654fa6c8ea02c6e 100644 --- a/drivers/iio/dac/ad5380.c +++ b/drivers/iio/dac/ad5380.c @@ -221,7 +221,7 @@ static int ad5380_read_raw(struct iio_dev *indio_dev, if (ret) return ret; *val >>= chan->scan_type.shift; - val -= (1 << chan->scan_type.realbits) / 2; + *val -= (1 << chan->scan_type.realbits) / 2; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: *val = 2 * st->vref; diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c index b31064ba37b9cdce0e80ad0b5a9fa442f71b19b3..02f6f263e3384cdc6267247262906abebfeb1c7e 100644 --- a/drivers/iio/gyro/st_gyro_core.c +++ b/drivers/iio/gyro/st_gyro_core.c @@ -141,7 +141,6 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = { [2] = LSM330DLC_GYRO_DEV_NAME, [3] = L3G4IS_GYRO_DEV_NAME, [4] = LSM330_GYRO_DEV_NAME, - [5] = LSM9DS0_GYRO_DEV_NAME, }, .ch = (struct iio_chan_spec *)st_gyro_16bit_channels, .odr = { @@ -211,6 +210,80 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = { .multi_read_bit = true, .bootime = 2, }, + { + .wai = 0xd4, + .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS, + .sensors_supported = { + [0] = LSM9DS0_GYRO_DEV_NAME, + }, + .ch = (struct iio_chan_spec *)st_gyro_16bit_channels, + .odr = { + .addr = 0x20, + .mask = GENMASK(7, 6), + .odr_avl = { + { .hz = 95, .value = 0x00, }, + { .hz = 190, .value = 0x01, }, + { .hz = 380, .value = 0x02, }, + { .hz = 760, .value = 0x03, }, + }, + }, + .pw = { + .addr = 0x20, + .mask = BIT(3), + .value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE, + .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, + }, + .enable_axis = { + .addr = ST_SENSORS_DEFAULT_AXIS_ADDR, + .mask = ST_SENSORS_DEFAULT_AXIS_MASK, + }, + .fs = { + .addr = 0x23, + .mask = GENMASK(5, 4), + .fs_avl = { + [0] = { + .num = ST_GYRO_FS_AVL_245DPS, + .value = 0x00, + .gain = IIO_DEGREE_TO_RAD(8750), + }, + [1] = { + .num = ST_GYRO_FS_AVL_500DPS, + .value = 0x01, + .gain = IIO_DEGREE_TO_RAD(17500), + }, + [2] = { + .num = ST_GYRO_FS_AVL_2000DPS, + .value = 0x02, + .gain = IIO_DEGREE_TO_RAD(70000), + }, + }, + }, + .bdu = { + .addr = 0x23, + .mask = BIT(7), + }, + .drdy_irq = { + .int2 = { + .addr = 0x22, + .mask = BIT(3), + }, + /* + * The sensor has IHL (active low) and open + * drain settings, but only for INT1 and not + * for the DRDY line on INT2. + */ + .stat_drdy = { + .addr = ST_SENSORS_DEFAULT_STAT_ADDR, + .mask = GENMASK(2, 0), + }, + }, + .sim = { + .addr = 0x23, + .value = BIT(0), + }, + .multi_read_bit = true, + .bootime = 2, + }, { .wai = 0xd7, .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS, diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c index ea24701beb3ea8f985846effcb79a0a1c46f3153..186f71927006a991628aded3b6a35a39823a8b6f 100644 --- a/drivers/iio/imu/adis16480.c +++ b/drivers/iio/imu/adis16480.c @@ -376,12 +376,14 @@ static int adis16480_get_calibbias(struct iio_dev *indio_dev, case IIO_MAGN: case IIO_PRESSURE: ret = adis_read_reg_16(&st->adis, reg, &val16); - *bias = sign_extend32(val16, 15); + if (ret == 0) + *bias = sign_extend32(val16, 15); break; case IIO_ANGL_VEL: case IIO_ACCEL: ret = adis_read_reg_32(&st->adis, reg, &val32); - *bias = sign_extend32(val32, 31); + if (ret == 0) + *bias = sign_extend32(val32, 31); break; default: ret = -EINVAL; diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index dadd921a4a30fdb527faf9e0b8e359ba6fa61bc0..a0d089afa1a217142cd82772bda687f1e0111915 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -570,7 +570,7 @@ static int iio_compute_scan_bytes(struct iio_dev *indio_dev, const unsigned long *mask, bool timestamp) { unsigned bytes = 0; - int length, i; + int length, i, largest = 0; /* How much space will the demuxed element take? */ for_each_set_bit(i, mask, @@ -578,13 +578,17 @@ static int iio_compute_scan_bytes(struct iio_dev *indio_dev, length = iio_storage_bytes_for_si(indio_dev, i); bytes = ALIGN(bytes, length); bytes += length; + largest = max(largest, length); } if (timestamp) { length = iio_storage_bytes_for_timestamp(indio_dev); bytes = ALIGN(bytes, length); bytes += length; + largest = max(largest, length); } + + bytes = ALIGN(bytes, largest); return bytes; } diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 06ca3f7fcc4455ccc6732304356c53920d43fbc4..053a18cd612b0c4747510f8138bbb45bf09924dd 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -934,3 +934,21 @@ ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr, chan->channel, buf, len); } EXPORT_SYMBOL_GPL(iio_write_channel_ext_info); + +int iio_write_channel_processed(struct iio_channel *chan, int val) +{ + int ret; + + mutex_lock(&chan->indio_dev->info_exist_lock); + if (chan->indio_dev->info == NULL) { + ret = -ENODEV; + goto err_unlock; + } + + ret = iio_channel_write(chan, val, 0, IIO_CHAN_INFO_PROCESSED); +err_unlock: + mutex_unlock(&chan->indio_dev->info_exist_lock); + + return ret; +} +EXPORT_SYMBOL(iio_write_channel_processed); diff --git a/drivers/iio/light/tsl2772.c b/drivers/iio/light/tsl2772.c index df5b2a0da96c4a9c311ddd6da57f990f6d1f821f..f2e308c6d6d7b495d6b9c2b36a31d9af1cd7a863 100644 --- a/drivers/iio/light/tsl2772.c +++ b/drivers/iio/light/tsl2772.c @@ -716,6 +716,13 @@ static int tsl2772_chip_off(struct iio_dev *indio_dev) return tsl2772_write_control_reg(chip, 0x00); } +static void tsl2772_chip_off_action(void *data) +{ + struct iio_dev *indio_dev = data; + + tsl2772_chip_off(indio_dev); +} + /** * tsl2772_invoke_change - power cycle the device to implement the user * parameters @@ -1711,9 +1718,14 @@ static int tsl2772_probe(struct i2c_client *clientp, if (ret < 0) return ret; + ret = devm_add_action_or_reset(&clientp->dev, + tsl2772_chip_off_action, + indio_dev); + if (ret < 0) + return ret; + ret = iio_device_register(indio_dev); if (ret) { - tsl2772_chip_off(indio_dev); dev_err(&clientp->dev, "%s: iio registration failed\n", __func__); return ret; @@ -1740,8 +1752,6 @@ static int tsl2772_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); - tsl2772_chip_off(indio_dev); - iio_device_unregister(indio_dev); return 0; diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c index 9e5f70e7122a01330228168b86fdebc58ec8a0c6..6e96a2fb97dc442eb7093db52644f91fddbe8456 100644 --- a/drivers/infiniband/core/addr.c +++ b/drivers/infiniband/core/addr.c @@ -136,7 +136,7 @@ int ib_nl_handle_ip_res_resp(struct sk_buff *skb, if (ib_nl_is_good_ip_resp(nlh)) ib_nl_process_good_ip_rsep(nlh); - return skb->len; + return 0; } static int ib_nl_ip_send_msg(struct rdma_dev_addr *dev_addr, diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c index 4c533275d1f206f44bd4daabc8343976aa6bb828..64f206e11d497a28af270cf4973a422be41f581b 100644 --- a/drivers/infiniband/core/cm.c +++ b/drivers/infiniband/core/cm.c @@ -1231,6 +1231,7 @@ struct ib_cm_id *ib_cm_insert_listen(struct ib_device *device, /* Sharing an ib_cm_id with different handlers is not * supported */ spin_unlock_irqrestore(&cm.lock, flags); + ib_destroy_cm_id(cm_id); return ERR_PTR(-EINVAL); } atomic_inc(&cm_id_priv->refcount); diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 319bfef00a4a8c6cf60040265b626aa5b5e79faf..e16872e0724ff130e9902382fad27997ad75540e 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -2889,7 +2889,7 @@ static void addr_handler(int status, struct sockaddr *src_addr, if (status) pr_debug_ratelimited("RDMA CM: ADDR_ERROR: failed to acquire device. status %d\n", status); - } else { + } else if (status) { pr_debug_ratelimited("RDMA CM: ADDR_ERROR: failed to resolve IP. status %d\n", status); } diff --git a/drivers/infiniband/core/iwcm.c b/drivers/infiniband/core/iwcm.c index 5d676cff41f496ce519f4dc000eda17f6fd43999..99dd8452724deb4c489274aedb09d8c765111ecb 100644 --- a/drivers/infiniband/core/iwcm.c +++ b/drivers/infiniband/core/iwcm.c @@ -158,8 +158,10 @@ static void dealloc_work_entries(struct iwcm_id_private *cm_id_priv) { struct list_head *e, *tmp; - list_for_each_safe(e, tmp, &cm_id_priv->work_free_list) + list_for_each_safe(e, tmp, &cm_id_priv->work_free_list) { + list_del(e); kfree(list_entry(e, struct iwcm_work, free_list)); + } } static int alloc_work_entries(struct iwcm_id_private *cm_id_priv, int count) diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c index 8be082edf986fd1e4d763c69bbd06ef3b3bd8cdc..9881e6fa9fe4db5ea5fa66d77b935b31eaba0d35 100644 --- a/drivers/infiniband/core/sa_query.c +++ b/drivers/infiniband/core/sa_query.c @@ -1078,7 +1078,7 @@ int ib_nl_handle_set_timeout(struct sk_buff *skb, } settimeout_out: - return skb->len; + return 0; } static inline int ib_nl_is_good_resolve_resp(const struct nlmsghdr *nlh) @@ -1149,7 +1149,7 @@ int ib_nl_handle_resolve_resp(struct sk_buff *skb, } resp_out: - return skb->len; + return 0; } static void free_sm_ah(struct kref *kref) diff --git a/drivers/infiniband/core/security.c b/drivers/infiniband/core/security.c index b79b61bd6ee4f2f256b4a5304f7c92cdfb4f87a6..f2c2e725375e4a3f88091ff312d52c426a8833c7 100644 --- a/drivers/infiniband/core/security.c +++ b/drivers/infiniband/core/security.c @@ -336,22 +336,20 @@ static struct ib_ports_pkeys *get_new_pps(const struct ib_qp *qp, if (!new_pps) return NULL; - if (qp_attr_mask & (IB_QP_PKEY_INDEX | IB_QP_PORT)) { - if (!qp_pps) { - new_pps->main.port_num = qp_attr->port_num; - new_pps->main.pkey_index = qp_attr->pkey_index; - } else { - new_pps->main.port_num = (qp_attr_mask & IB_QP_PORT) ? - qp_attr->port_num : - qp_pps->main.port_num; - - new_pps->main.pkey_index = - (qp_attr_mask & IB_QP_PKEY_INDEX) ? - qp_attr->pkey_index : - qp_pps->main.pkey_index; - } + if (qp_attr_mask & IB_QP_PORT) + new_pps->main.port_num = qp_attr->port_num; + else if (qp_pps) + new_pps->main.port_num = qp_pps->main.port_num; + + if (qp_attr_mask & IB_QP_PKEY_INDEX) + new_pps->main.pkey_index = qp_attr->pkey_index; + else if (qp_pps) + new_pps->main.pkey_index = qp_pps->main.pkey_index; + + if ((qp_attr_mask & IB_QP_PKEY_INDEX) && (qp_attr_mask & IB_QP_PORT)) new_pps->main.state = IB_PORT_PKEY_VALID; - } else if (qp_pps) { + + if (!(qp_attr_mask & (IB_QP_PKEY_INDEX | IB_QP_PORT)) && qp_pps) { new_pps->main.port_num = qp_pps->main.port_num; new_pps->main.pkey_index = qp_pps->main.pkey_index; if (qp_pps->main.state != IB_PORT_PKEY_NOT_VALID) diff --git a/drivers/infiniband/core/umem_odp.c b/drivers/infiniband/core/umem_odp.c index 6ec748eccff7e87d7691b92a6d50534d89d4625e..4bda1242df87ac31316b4168d061ce5a57e3d84c 100644 --- a/drivers/infiniband/core/umem_odp.c +++ b/drivers/infiniband/core/umem_odp.c @@ -689,7 +689,7 @@ int ib_umem_odp_map_dma_pages(struct ib_umem *umem, u64 user_virt, u64 bcnt, while (bcnt > 0) { const size_t gup_num_pages = min_t(size_t, - (bcnt + BIT(page_shift) - 1) >> page_shift, + ALIGN(bcnt, PAGE_SIZE) / PAGE_SIZE, PAGE_SIZE / sizeof(struct page *)); down_read(&owning_mm->mmap_sem); diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c index e012ca80f9d196ddbb8723691ec4087a55c0d863..5e10a40fd26d33cc183e78a21ee33da6fb6f6bce 100644 --- a/drivers/infiniband/core/uverbs_cmd.c +++ b/drivers/infiniband/core/uverbs_cmd.c @@ -2914,12 +2914,6 @@ static int kern_spec_to_ib_spec_action(struct ib_uverbs_file *ufile, return 0; } -static size_t kern_spec_filter_sz(const struct ib_uverbs_flow_spec_hdr *spec) -{ - /* Returns user space filter size, includes padding */ - return (spec->size - sizeof(struct ib_uverbs_flow_spec_hdr)) / 2; -} - static ssize_t spec_filter_size(const void *kern_spec_filter, u16 kern_filter_size, u16 ib_real_filter_sz) { @@ -3063,11 +3057,16 @@ int ib_uverbs_kern_spec_to_ib_spec_filter(enum ib_flow_spec_type type, static int kern_spec_to_ib_spec_filter(struct ib_uverbs_flow_spec *kern_spec, union ib_flow_spec *ib_spec) { - ssize_t kern_filter_sz; + size_t kern_filter_sz; void *kern_spec_mask; void *kern_spec_val; - kern_filter_sz = kern_spec_filter_sz(&kern_spec->hdr); + if (check_sub_overflow((size_t)kern_spec->hdr.size, + sizeof(struct ib_uverbs_flow_spec_hdr), + &kern_filter_sz)) + return -EINVAL; + + kern_filter_sz /= 2; kern_spec_val = (void *)kern_spec + sizeof(struct ib_uverbs_flow_spec_hdr); diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c index 357de3b4fdddff6ac2a8fea3d45712863587f0ab..5404717998b07a7787c7de88d7a6d28cf89c1487 100644 --- a/drivers/infiniband/core/uverbs_main.c +++ b/drivers/infiniband/core/uverbs_main.c @@ -273,7 +273,6 @@ void ib_uverbs_release_file(struct kref *ref) } static ssize_t ib_uverbs_event_read(struct ib_uverbs_event_queue *ev_queue, - struct ib_uverbs_file *uverbs_file, struct file *filp, char __user *buf, size_t count, loff_t *pos, size_t eventsz) @@ -291,19 +290,16 @@ static ssize_t ib_uverbs_event_read(struct ib_uverbs_event_queue *ev_queue, if (wait_event_interruptible(ev_queue->poll_wait, (!list_empty(&ev_queue->event_list) || - /* The barriers built into wait_event_interruptible() - * and wake_up() guarentee this will see the null set - * without using RCU - */ - !uverbs_file->device->ib_dev))) + ev_queue->is_closed))) return -ERESTARTSYS; + spin_lock_irq(&ev_queue->lock); + /* If device was disassociated and no event exists set an error */ - if (list_empty(&ev_queue->event_list) && - !uverbs_file->device->ib_dev) + if (list_empty(&ev_queue->event_list) && ev_queue->is_closed) { + spin_unlock_irq(&ev_queue->lock); return -EIO; - - spin_lock_irq(&ev_queue->lock); + } } event = list_entry(ev_queue->event_list.next, struct ib_uverbs_event, list); @@ -338,8 +334,7 @@ static ssize_t ib_uverbs_async_event_read(struct file *filp, char __user *buf, { struct ib_uverbs_async_event_file *file = filp->private_data; - return ib_uverbs_event_read(&file->ev_queue, file->uverbs_file, filp, - buf, count, pos, + return ib_uverbs_event_read(&file->ev_queue, filp, buf, count, pos, sizeof(struct ib_uverbs_async_event_desc)); } @@ -349,9 +344,8 @@ static ssize_t ib_uverbs_comp_event_read(struct file *filp, char __user *buf, struct ib_uverbs_completion_event_file *comp_ev_file = filp->private_data; - return ib_uverbs_event_read(&comp_ev_file->ev_queue, - comp_ev_file->uobj.ufile, filp, - buf, count, pos, + return ib_uverbs_event_read(&comp_ev_file->ev_queue, filp, buf, count, + pos, sizeof(struct ib_uverbs_comp_event_desc)); } @@ -374,7 +368,9 @@ static __poll_t ib_uverbs_event_poll(struct ib_uverbs_event_queue *ev_queue, static __poll_t ib_uverbs_async_event_poll(struct file *filp, struct poll_table_struct *wait) { - return ib_uverbs_event_poll(filp->private_data, filp, wait); + struct ib_uverbs_async_event_file *file = filp->private_data; + + return ib_uverbs_event_poll(&file->ev_queue, filp, wait); } static __poll_t ib_uverbs_comp_event_poll(struct file *filp, @@ -388,9 +384,9 @@ static __poll_t ib_uverbs_comp_event_poll(struct file *filp, static int ib_uverbs_async_event_fasync(int fd, struct file *filp, int on) { - struct ib_uverbs_event_queue *ev_queue = filp->private_data; + struct ib_uverbs_async_event_file *file = filp->private_data; - return fasync_helper(fd, filp, on, &ev_queue->async_queue); + return fasync_helper(fd, filp, on, &file->ev_queue.async_queue); } static int ib_uverbs_comp_event_fasync(int fd, struct file *filp, int on) diff --git a/drivers/infiniband/core/uverbs_uapi.c b/drivers/infiniband/core/uverbs_uapi.c index be854628a7c63149c05ed74f9f001a39fb1bc59d..959a3418a192d137dfc76383e72fc4b5c2465149 100644 --- a/drivers/infiniband/core/uverbs_uapi.c +++ b/drivers/infiniband/core/uverbs_uapi.c @@ -17,6 +17,8 @@ static void *uapi_add_elm(struct uverbs_api *uapi, u32 key, size_t alloc_size) return ERR_PTR(-EOVERFLOW); elm = kzalloc(alloc_size, GFP_KERNEL); + if (!elm) + return ERR_PTR(-ENOMEM); rc = radix_tree_insert(&uapi->radix, key, elm); if (rc) { kfree(elm); diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index bc2b9e03843903750aba02c0994430cd024cf4e3..c9af2d139f5cb6084fefb07c911095fa9d894bc0 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -1446,7 +1446,7 @@ struct ib_srq *bnxt_re_create_srq(struct ib_pd *ib_pd, dev_err(rdev_to_dev(rdev), "SRQ copy to udata failed!"); bnxt_qplib_destroy_srq(&rdev->qplib_res, &srq->qplib_srq); - goto exit; + goto fail; } } if (nq) @@ -2664,6 +2664,7 @@ struct ib_cq *bnxt_re_create_cq(struct ib_device *ibdev, nq->budget++; atomic_inc(&rdev->cq_count); + spin_lock_init(&cq->cq_lock); if (context) { struct bnxt_re_cq_resp resp; @@ -3368,8 +3369,10 @@ int bnxt_re_dereg_mr(struct ib_mr *ib_mr) int rc; rc = bnxt_qplib_free_mrw(&rdev->qplib_res, &mr->qplib_mr); - if (rc) + if (rc) { dev_err(rdev_to_dev(rdev), "Dereg MR failed: %#x\n", rc); + return rc; + } if (mr->pages) { rc = bnxt_qplib_free_fast_reg_page_list(&rdev->qplib_res, diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.c b/drivers/infiniband/hw/bnxt_re/qplib_fp.c index 249efa0a6aba7aacd0b38abaef760692086d67b3..60f2fb7e7dbfe4f1e483bc1ccaad8062caaa6486 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_fp.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.c @@ -1970,6 +1970,7 @@ int bnxt_qplib_create_cq(struct bnxt_qplib_res *res, struct bnxt_qplib_cq *cq) INIT_LIST_HEAD(&cq->sqf_head); INIT_LIST_HEAD(&cq->rqf_head); spin_lock_init(&cq->compl_lock); + spin_lock_init(&cq->flush_lock); bnxt_qplib_arm_cq_enable(cq); return 0; @@ -2273,13 +2274,13 @@ static int bnxt_qplib_cq_process_req(struct bnxt_qplib_cq *cq, /* Add qp to flush list of the CQ */ bnxt_qplib_add_flush_qp(qp); } else { + /* Before we complete, do WA 9060 */ + if (do_wa9060(qp, cq, cq_cons, sw_sq_cons, + cqe_sq_cons)) { + *lib_qp = qp; + goto out; + } if (swq->flags & SQ_SEND_FLAGS_SIGNAL_COMP) { - /* Before we complete, do WA 9060 */ - if (do_wa9060(qp, cq, cq_cons, sw_sq_cons, - cqe_sq_cons)) { - *lib_qp = qp; - goto out; - } cqe->status = CQ_REQ_STATUS_OK; cqe++; (*budget)--; diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 4dcc92d11609728b1bc3136068ffb6730ff04693..16145b0a145839349fd518d9a2bd24997d3faeaf 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -2076,7 +2076,7 @@ static int import_ep(struct c4iw_ep *ep, int iptype, __u8 *peer_ip, } else { pdev = get_real_dev(n->dev); ep->l2t = cxgb4_l2t_get(cdev->rdev.lldi.l2t, - n, pdev, 0); + n, pdev, rt_tos2priority(tos)); if (!ep->l2t) goto out; ep->mtu = dst_mtu(dst); @@ -2166,7 +2166,8 @@ static int c4iw_reconnect(struct c4iw_ep *ep) laddr6->sin6_addr.s6_addr, raddr6->sin6_addr.s6_addr, laddr6->sin6_port, - raddr6->sin6_port, 0, + raddr6->sin6_port, + ep->com.cm_id->tos, raddr6->sin6_scope_id); iptype = 6; ra = (__u8 *)&raddr6->sin6_addr; @@ -2947,15 +2948,18 @@ static int terminate(struct c4iw_dev *dev, struct sk_buff *skb) ep = get_ep_from_tid(dev, tid); - if (ep && ep->com.qp) { - pr_warn("TERM received tid %u qpid %u\n", - tid, ep->com.qp->wq.sq.qid); - attrs.next_state = C4IW_QP_STATE_TERMINATE; - c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp, - C4IW_QP_ATTR_NEXT_STATE, &attrs, 1); + if (ep) { + if (ep->com.qp) { + pr_warn("TERM received tid %u qpid %u\n", tid, + ep->com.qp->wq.sq.qid); + attrs.next_state = C4IW_QP_STATE_TERMINATE; + c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp, + C4IW_QP_ATTR_NEXT_STATE, &attrs, 1); + } + + c4iw_put_ep(&ep->com); } else pr_warn("TERM received tid %u no ep/qp\n", tid); - c4iw_put_ep(&ep->com); return 0; } @@ -3323,7 +3327,7 @@ int c4iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) laddr6->sin6_addr.s6_addr, raddr6->sin6_addr.s6_addr, laddr6->sin6_port, - raddr6->sin6_port, 0, + raddr6->sin6_port, cm_id->tos, raddr6->sin6_scope_id); } if (!ep->dst) { diff --git a/drivers/infiniband/hw/hfi1/affinity.c b/drivers/infiniband/hw/hfi1/affinity.c index bedd5fba33b080a075405c9a01fa8006f5b0a3f2..01ed0a667928d47c40f9ca4824a54ce6bcb07277 100644 --- a/drivers/infiniband/hw/hfi1/affinity.c +++ b/drivers/infiniband/hw/hfi1/affinity.c @@ -478,6 +478,8 @@ static int _dev_comp_vect_mappings_create(struct hfi1_devdata *dd, rvt_get_ibdev_name(&(dd)->verbs_dev.rdi), i, cpu); } + free_cpumask_var(available_cpus); + free_cpumask_var(non_intr_cpus); return 0; fail: diff --git a/drivers/infiniband/hw/hfi1/chip.c b/drivers/infiniband/hw/hfi1/chip.c index 6b89353611333169363c37c2bc80eec92f4e3220..1221faea75a68d69be30103ab0259514f22264d7 100644 --- a/drivers/infiniband/hw/hfi1/chip.c +++ b/drivers/infiniband/hw/hfi1/chip.c @@ -1687,6 +1687,14 @@ static u64 access_sw_pio_drain(const struct cntr_entry *entry, return dd->verbs_dev.n_piodrain; } +static u64 access_sw_ctx0_seq_drop(const struct cntr_entry *entry, + void *context, int vl, int mode, u64 data) +{ + struct hfi1_devdata *dd = context; + + return dd->ctx0_seq_drop; +} + static u64 access_sw_vtx_wait(const struct cntr_entry *entry, void *context, int vl, int mode, u64 data) { @@ -4247,6 +4255,8 @@ static struct cntr_entry dev_cntrs[DEV_CNTR_LAST] = { access_sw_cpu_intr), [C_SW_CPU_RCV_LIM] = CNTR_ELEM("RcvLimit", 0, 0, CNTR_NORMAL, access_sw_cpu_rcv_limit), +[C_SW_CTX0_SEQ_DROP] = CNTR_ELEM("SeqDrop0", 0, 0, CNTR_NORMAL, + access_sw_ctx0_seq_drop), [C_SW_VTX_WAIT] = CNTR_ELEM("vTxWait", 0, 0, CNTR_NORMAL, access_sw_vtx_wait), [C_SW_PIO_WAIT] = CNTR_ELEM("PioWait", 0, 0, CNTR_NORMAL, @@ -10580,12 +10590,29 @@ void set_link_down_reason(struct hfi1_pportdata *ppd, u8 lcl_reason, } } -/* - * Verify if BCT for data VLs is non-zero. +/** + * data_vls_operational() - Verify if data VL BCT credits and MTU + * are both set. + * @ppd: pointer to hfi1_pportdata structure + * + * Return: true - Ok, false -otherwise. */ static inline bool data_vls_operational(struct hfi1_pportdata *ppd) { - return !!ppd->actual_vls_operational; + int i; + u64 reg; + + if (!ppd->actual_vls_operational) + return false; + + for (i = 0; i < ppd->vls_supported; i++) { + reg = read_csr(ppd->dd, SEND_CM_CREDIT_VL + (8 * i)); + if ((reg && !ppd->dd->vld[i].mtu) || + (!reg && ppd->dd->vld[i].mtu)) + return false; + } + + return true; } /* @@ -10698,7 +10725,8 @@ int set_link_state(struct hfi1_pportdata *ppd, u32 state) if (!data_vls_operational(ppd)) { dd_dev_err(dd, - "%s: data VLs not operational\n", __func__); + "%s: Invalid data VL credits or mtu\n", + __func__); ret = -EINVAL; break; } diff --git a/drivers/infiniband/hw/hfi1/chip.h b/drivers/infiniband/hw/hfi1/chip.h index 36b04d6300e54d2ba86cce7fbbe101d9345d8984..c9a352d8a7e133901d0a40bb2d17d7bfeebb817b 100644 --- a/drivers/infiniband/hw/hfi1/chip.h +++ b/drivers/infiniband/hw/hfi1/chip.h @@ -909,6 +909,7 @@ enum { C_DC_PG_STS_TX_MBE_CNT, C_SW_CPU_INTR, C_SW_CPU_RCV_LIM, + C_SW_CTX0_SEQ_DROP, C_SW_VTX_WAIT, C_SW_PIO_WAIT, C_SW_PIO_DRAIN, diff --git a/drivers/infiniband/hw/hfi1/driver.c b/drivers/infiniband/hw/hfi1/driver.c index a41f85558312eeb86d9b046f4ac784765932804e..769e114567a03c006d23f822459597c77f07746e 100644 --- a/drivers/infiniband/hw/hfi1/driver.c +++ b/drivers/infiniband/hw/hfi1/driver.c @@ -430,40 +430,60 @@ static const hfi1_handle_cnp hfi1_handle_cnp_tbl[2] = { [HFI1_PKT_TYPE_16B] = &return_cnp_16B }; -void hfi1_process_ecn_slowpath(struct rvt_qp *qp, struct hfi1_packet *pkt, - bool do_cnp) +/** + * hfi1_process_ecn_slowpath - Process FECN or BECN bits + * @qp: The packet's destination QP + * @pkt: The packet itself. + * @prescan: Is the caller the RXQ prescan + * + * Process the packet's FECN or BECN bits. By now, the packet + * has already been evaluated whether processing of those bit should + * be done. + * The significance of the @prescan argument is that if the caller + * is the RXQ prescan, a CNP will be send out instead of waiting for the + * normal packet processing to send an ACK with BECN set (or a CNP). + */ +bool hfi1_process_ecn_slowpath(struct rvt_qp *qp, struct hfi1_packet *pkt, + bool prescan) { struct hfi1_ibport *ibp = to_iport(qp->ibqp.device, qp->port_num); struct hfi1_pportdata *ppd = ppd_from_ibp(ibp); struct ib_other_headers *ohdr = pkt->ohdr; struct ib_grh *grh = pkt->grh; - u32 rqpn = 0, bth1; + u32 rqpn = 0; u16 pkey; u32 rlid, slid, dlid = 0; - u8 hdr_type, sc, svc_type; - bool is_mcast = false; + u8 hdr_type, sc, svc_type, opcode; + bool is_mcast = false, ignore_fecn = false, do_cnp = false, + fecn, becn; /* can be called from prescan */ if (pkt->etype == RHF_RCV_TYPE_BYPASS) { - is_mcast = hfi1_is_16B_mcast(dlid); pkey = hfi1_16B_get_pkey(pkt->hdr); sc = hfi1_16B_get_sc(pkt->hdr); dlid = hfi1_16B_get_dlid(pkt->hdr); slid = hfi1_16B_get_slid(pkt->hdr); + is_mcast = hfi1_is_16B_mcast(dlid); + opcode = ib_bth_get_opcode(ohdr); hdr_type = HFI1_PKT_TYPE_16B; + fecn = hfi1_16B_get_fecn(pkt->hdr); + becn = hfi1_16B_get_becn(pkt->hdr); } else { - is_mcast = (dlid > be16_to_cpu(IB_MULTICAST_LID_BASE)) && - (dlid != be16_to_cpu(IB_LID_PERMISSIVE)); pkey = ib_bth_get_pkey(ohdr); sc = hfi1_9B_get_sc5(pkt->hdr, pkt->rhf); - dlid = ib_get_dlid(pkt->hdr); + dlid = qp->ibqp.qp_type != IB_QPT_UD ? ib_get_dlid(pkt->hdr) : + ppd->lid; slid = ib_get_slid(pkt->hdr); + is_mcast = (dlid > be16_to_cpu(IB_MULTICAST_LID_BASE)) && + (dlid != be16_to_cpu(IB_LID_PERMISSIVE)); + opcode = ib_bth_get_opcode(ohdr); hdr_type = HFI1_PKT_TYPE_9B; + fecn = ib_bth_get_fecn(ohdr); + becn = ib_bth_get_becn(ohdr); } switch (qp->ibqp.qp_type) { case IB_QPT_UD: - dlid = ppd->lid; rlid = slid; rqpn = ib_get_sqpn(pkt->ohdr); svc_type = IB_CC_SVCTYPE_UD; @@ -485,22 +505,31 @@ void hfi1_process_ecn_slowpath(struct rvt_qp *qp, struct hfi1_packet *pkt, svc_type = IB_CC_SVCTYPE_RC; break; default: - return; + return false; } - bth1 = be32_to_cpu(ohdr->bth[1]); + ignore_fecn = is_mcast || (opcode == IB_OPCODE_CNP) || + (opcode == IB_OPCODE_RC_ACKNOWLEDGE); + /* + * ACKNOWLEDGE packets do not get a CNP but this will be + * guarded by ignore_fecn above. + */ + do_cnp = prescan || + (opcode >= IB_OPCODE_RC_RDMA_READ_RESPONSE_FIRST && + opcode <= IB_OPCODE_RC_ATOMIC_ACKNOWLEDGE); + /* Call appropriate CNP handler */ - if (do_cnp && (bth1 & IB_FECN_SMASK)) + if (!ignore_fecn && do_cnp && fecn) hfi1_handle_cnp_tbl[hdr_type](ibp, qp, rqpn, pkey, dlid, rlid, sc, grh); - if (!is_mcast && (bth1 & IB_BECN_SMASK)) { - u32 lqpn = bth1 & RVT_QPN_MASK; + if (becn) { + u32 lqpn = be32_to_cpu(ohdr->bth[1]) & RVT_QPN_MASK; u8 sl = ibp->sc_to_sl[sc]; process_becn(ppd, sl, rlid, lqpn, rqpn, svc_type); } - + return !ignore_fecn && fecn; } struct ps_mdata { @@ -599,7 +628,6 @@ static void __prescan_rxq(struct hfi1_packet *packet) struct rvt_dev_info *rdi = &rcd->dd->verbs_dev.rdi; u64 rhf = rhf_to_cpu(rhf_addr); u32 etype = rhf_rcv_type(rhf), qpn, bth1; - int is_ecn = 0; u8 lnh; if (ps_done(&mdata, rhf, rcd)) @@ -625,12 +653,10 @@ static void __prescan_rxq(struct hfi1_packet *packet) goto next; /* just in case */ } - bth1 = be32_to_cpu(packet->ohdr->bth[1]); - is_ecn = !!(bth1 & (IB_FECN_SMASK | IB_BECN_SMASK)); - - if (!is_ecn) + if (!hfi1_may_ecn(packet)) goto next; + bth1 = be32_to_cpu(packet->ohdr->bth[1]); qpn = bth1 & RVT_QPN_MASK; rcu_read_lock(); qp = rvt_lookup_qpn(rdi, &ibp->rvp, qpn); @@ -640,7 +666,7 @@ static void __prescan_rxq(struct hfi1_packet *packet) goto next; } - process_ecn(qp, packet, true); + hfi1_process_ecn_slowpath(qp, packet, true); rcu_read_unlock(); /* turn off BECN, FECN */ @@ -708,6 +734,7 @@ static noinline int skip_rcv_packet(struct hfi1_packet *packet, int thread) { int ret; + packet->rcd->dd->ctx0_seq_drop++; /* Set up for the next packet */ packet->rhqoff += packet->rsize; if (packet->rhqoff >= packet->maxcnt) diff --git a/drivers/infiniband/hw/hfi1/file_ops.c b/drivers/infiniband/hw/hfi1/file_ops.c index 34ffca6184270d951344f7bc649fe215d59549ad..adeb259458de602c11dd28d760fcdefc21251d9e 100644 --- a/drivers/infiniband/hw/hfi1/file_ops.c +++ b/drivers/infiniband/hw/hfi1/file_ops.c @@ -200,23 +200,24 @@ static int hfi1_file_open(struct inode *inode, struct file *fp) fd = kzalloc(sizeof(*fd), GFP_KERNEL); - if (fd) { - fd->rec_cpu_num = -1; /* no cpu affinity by default */ - fd->mm = current->mm; - mmgrab(fd->mm); - fd->dd = dd; - kobject_get(&fd->dd->kobj); - fp->private_data = fd; - } else { - fp->private_data = NULL; - - if (atomic_dec_and_test(&dd->user_refcount)) - complete(&dd->user_comp); - - return -ENOMEM; - } - + if (!fd || init_srcu_struct(&fd->pq_srcu)) + goto nomem; + spin_lock_init(&fd->pq_rcu_lock); + spin_lock_init(&fd->tid_lock); + spin_lock_init(&fd->invalid_lock); + fd->rec_cpu_num = -1; /* no cpu affinity by default */ + fd->mm = current->mm; + mmgrab(fd->mm); + fd->dd = dd; + kobject_get(&fd->dd->kobj); + fp->private_data = fd; return 0; +nomem: + kfree(fd); + fp->private_data = NULL; + if (atomic_dec_and_test(&dd->user_refcount)) + complete(&dd->user_comp); + return -ENOMEM; } static long hfi1_file_ioctl(struct file *fp, unsigned int cmd, @@ -301,21 +302,30 @@ static long hfi1_file_ioctl(struct file *fp, unsigned int cmd, static ssize_t hfi1_write_iter(struct kiocb *kiocb, struct iov_iter *from) { struct hfi1_filedata *fd = kiocb->ki_filp->private_data; - struct hfi1_user_sdma_pkt_q *pq = fd->pq; + struct hfi1_user_sdma_pkt_q *pq; struct hfi1_user_sdma_comp_q *cq = fd->cq; int done = 0, reqs = 0; unsigned long dim = from->nr_segs; + int idx; - if (!cq || !pq) + idx = srcu_read_lock(&fd->pq_srcu); + pq = srcu_dereference(fd->pq, &fd->pq_srcu); + if (!cq || !pq) { + srcu_read_unlock(&fd->pq_srcu, idx); return -EIO; + } - if (!iter_is_iovec(from) || !dim) + if (!iter_is_iovec(from) || !dim) { + srcu_read_unlock(&fd->pq_srcu, idx); return -EINVAL; + } trace_hfi1_sdma_request(fd->dd, fd->uctxt->ctxt, fd->subctxt, dim); - if (atomic_read(&pq->n_reqs) == pq->n_max_reqs) + if (atomic_read(&pq->n_reqs) == pq->n_max_reqs) { + srcu_read_unlock(&fd->pq_srcu, idx); return -ENOSPC; + } while (dim) { int ret; @@ -333,6 +343,7 @@ static ssize_t hfi1_write_iter(struct kiocb *kiocb, struct iov_iter *from) reqs++; } + srcu_read_unlock(&fd->pq_srcu, idx); return reqs; } @@ -706,6 +717,7 @@ static int hfi1_file_close(struct inode *inode, struct file *fp) if (atomic_dec_and_test(&dd->user_refcount)) complete(&dd->user_comp); + cleanup_srcu_struct(&fdata->pq_srcu); kfree(fdata); return 0; } diff --git a/drivers/infiniband/hw/hfi1/hfi.h b/drivers/infiniband/hw/hfi1/hfi.h index 2ea42c04cfd25fefb9c68f1ed9dd07463514cf54..e38de547785d05fe8063e5c1a89cc44e532e6c4e 100644 --- a/drivers/infiniband/hw/hfi1/hfi.h +++ b/drivers/infiniband/hw/hfi1/hfi.h @@ -1093,6 +1093,8 @@ struct hfi1_devdata { char *boardname; /* human readable board info */ + u64 ctx0_seq_drop; + /* reset value */ u64 z_int_counter; u64 z_rcv_limit; @@ -1376,10 +1378,13 @@ struct mmu_rb_handler; /* Private data for file operations */ struct hfi1_filedata { + struct srcu_struct pq_srcu; struct hfi1_devdata *dd; struct hfi1_ctxtdata *uctxt; struct hfi1_user_sdma_comp_q *cq; - struct hfi1_user_sdma_pkt_q *pq; + /* update side lock for SRCU */ + spinlock_t pq_rcu_lock; + struct hfi1_user_sdma_pkt_q __rcu *pq; u16 subctxt; /* for cpu affinity; -1 if none */ int rec_cpu_num; @@ -1797,13 +1802,20 @@ static inline struct hfi1_ibport *rcd_to_iport(struct hfi1_ctxtdata *rcd) return &rcd->ppd->ibport_data; } -void hfi1_process_ecn_slowpath(struct rvt_qp *qp, struct hfi1_packet *pkt, - bool do_cnp); -static inline bool process_ecn(struct rvt_qp *qp, struct hfi1_packet *pkt, - bool do_cnp) +/** + * hfi1_may_ecn - Check whether FECN or BECN processing should be done + * @pkt: the packet to be evaluated + * + * Check whether the FECN or BECN bits in the packet's header are + * enabled, depending on packet type. + * + * This function only checks for FECN and BECN bits. Additional checks + * are done in the slowpath (hfi1_process_ecn_slowpath()) in order to + * ensure correct handling. + */ +static inline bool hfi1_may_ecn(struct hfi1_packet *pkt) { - bool becn; - bool fecn; + bool fecn, becn; if (pkt->etype == RHF_RCV_TYPE_BYPASS) { fecn = hfi1_16B_get_fecn(pkt->hdr); @@ -1812,10 +1824,18 @@ static inline bool process_ecn(struct rvt_qp *qp, struct hfi1_packet *pkt, fecn = ib_bth_get_fecn(pkt->ohdr); becn = ib_bth_get_becn(pkt->ohdr); } - if (unlikely(fecn || becn)) { - hfi1_process_ecn_slowpath(qp, pkt, do_cnp); - return fecn; - } + return fecn || becn; +} + +bool hfi1_process_ecn_slowpath(struct rvt_qp *qp, struct hfi1_packet *pkt, + bool prescan); +static inline bool process_ecn(struct rvt_qp *qp, struct hfi1_packet *pkt) +{ + bool do_work; + + do_work = hfi1_may_ecn(pkt); + if (unlikely(do_work)) + return hfi1_process_ecn_slowpath(qp, pkt, false); return false; } diff --git a/drivers/infiniband/hw/hfi1/pio.c b/drivers/infiniband/hw/hfi1/pio.c index 752057647f091734368f998c0173234d8a1f2dd0..3fcbf56f8be23da344294e400fb30035fe7c0c14 100644 --- a/drivers/infiniband/hw/hfi1/pio.c +++ b/drivers/infiniband/hw/hfi1/pio.c @@ -1434,7 +1434,8 @@ void sc_stop(struct send_context *sc, int flag) * @cb: optional callback to call when the buffer is finished sending * @arg: argument for cb * - * Return a pointer to a PIO buffer if successful, NULL if not enough room. + * Return a pointer to a PIO buffer, NULL if not enough room, -ECOMM + * when link is down. */ struct pio_buf *sc_buffer_alloc(struct send_context *sc, u32 dw_len, pio_release_cb cb, void *arg) @@ -1450,7 +1451,7 @@ struct pio_buf *sc_buffer_alloc(struct send_context *sc, u32 dw_len, spin_lock_irqsave(&sc->alloc_lock, flags); if (!(sc->flags & SCF_ENABLED)) { spin_unlock_irqrestore(&sc->alloc_lock, flags); - goto done; + return ERR_PTR(-ECOMM); } retry: diff --git a/drivers/infiniband/hw/hfi1/rc.c b/drivers/infiniband/hw/hfi1/rc.c index 770c78c6573067c61837a462ea8bead2b8178f79..7ed6fb407a689243cb55778ea4d8ca7c1f41ec7a 100644 --- a/drivers/infiniband/hw/hfi1/rc.c +++ b/drivers/infiniband/hw/hfi1/rc.c @@ -914,7 +914,7 @@ void hfi1_send_rc_ack(struct hfi1_packet *packet, bool is_fecn) pbc = create_pbc(ppd, pbc_flags, qp->srate_mbps, sc_to_vlt(ppd->dd, sc5), plen); pbuf = sc_buffer_alloc(rcd->sc, plen, NULL, NULL); - if (!pbuf) { + if (IS_ERR_OR_NULL(pbuf)) { /* * We have no room to send at the moment. Pass * responsibility for sending the ACK to the send engine @@ -2049,8 +2049,7 @@ void hfi1_rc_rcv(struct hfi1_packet *packet) struct ib_reth *reth; unsigned long flags; int ret; - bool is_fecn = false; - bool copy_last = false; + bool copy_last = false, fecn; u32 rkey; u8 extra_bytes = pad + packet->extra_byte + (SIZE_OF_CRC << 2); @@ -2059,7 +2058,7 @@ void hfi1_rc_rcv(struct hfi1_packet *packet) if (hfi1_ruc_check_hdr(ibp, packet)) return; - is_fecn = process_ecn(qp, packet, false); + fecn = process_ecn(qp, packet); /* * Process responses (ACKs) before anything else. Note that the @@ -2070,8 +2069,6 @@ void hfi1_rc_rcv(struct hfi1_packet *packet) if (opcode >= OP(RDMA_READ_RESPONSE_FIRST) && opcode <= OP(ATOMIC_ACKNOWLEDGE)) { rc_rcv_resp(packet); - if (is_fecn) - goto send_ack; return; } @@ -2347,11 +2344,11 @@ void hfi1_rc_rcv(struct hfi1_packet *packet) /* Schedule the send engine. */ qp->s_flags |= RVT_S_RESP_PENDING; + if (fecn) + qp->s_flags |= RVT_S_ECN; hfi1_schedule_send(qp); spin_unlock_irqrestore(&qp->s_lock, flags); - if (is_fecn) - goto send_ack; return; } @@ -2413,11 +2410,11 @@ void hfi1_rc_rcv(struct hfi1_packet *packet) /* Schedule the send engine. */ qp->s_flags |= RVT_S_RESP_PENDING; + if (fecn) + qp->s_flags |= RVT_S_ECN; hfi1_schedule_send(qp); spin_unlock_irqrestore(&qp->s_lock, flags); - if (is_fecn) - goto send_ack; return; } @@ -2430,16 +2427,9 @@ void hfi1_rc_rcv(struct hfi1_packet *packet) qp->r_ack_psn = psn; qp->r_nak_state = 0; /* Send an ACK if requested or required. */ - if (psn & IB_BTH_REQ_ACK) { - if (packet->numpkt == 0) { - rc_cancel_ack(qp); - goto send_ack; - } - if (qp->r_adefered >= HFI1_PSN_CREDIT) { - rc_cancel_ack(qp); - goto send_ack; - } - if (unlikely(is_fecn)) { + if (psn & IB_BTH_REQ_ACK || fecn) { + if (packet->numpkt == 0 || fecn || + qp->r_adefered >= HFI1_PSN_CREDIT) { rc_cancel_ack(qp); goto send_ack; } @@ -2480,7 +2470,7 @@ void hfi1_rc_rcv(struct hfi1_packet *packet) qp->r_nak_state = IB_NAK_REMOTE_ACCESS_ERROR; qp->r_ack_psn = qp->r_psn; send_ack: - hfi1_send_rc_ack(packet, is_fecn); + hfi1_send_rc_ack(packet, fecn); } void hfi1_rc_hdrerr( diff --git a/drivers/infiniband/hw/hfi1/uc.c b/drivers/infiniband/hw/hfi1/uc.c index e254dcec6f647067a0efce4cee6b47e9f76dbf9c..4121d1a93b1bbc1bda06eb23a64e445b48537bf0 100644 --- a/drivers/infiniband/hw/hfi1/uc.c +++ b/drivers/infiniband/hw/hfi1/uc.c @@ -321,7 +321,7 @@ void hfi1_uc_rcv(struct hfi1_packet *packet) if (hfi1_ruc_check_hdr(ibp, packet)) return; - process_ecn(qp, packet, true); + process_ecn(qp, packet); psn = ib_bth_get_psn(ohdr); /* Compare the PSN verses the expected PSN. */ diff --git a/drivers/infiniband/hw/hfi1/ud.c b/drivers/infiniband/hw/hfi1/ud.c index 54eb69564264926ab287c3e99015212f3b1f15f6..839593641e3fd4eb7ab131a84d0f373f22955bac 100644 --- a/drivers/infiniband/hw/hfi1/ud.c +++ b/drivers/infiniband/hw/hfi1/ud.c @@ -51,6 +51,7 @@ #include "hfi.h" #include "mad.h" #include "verbs_txreq.h" +#include "trace_ibhdrs.h" #include "qp.h" /* We support only two types - 9B and 16B for now */ @@ -656,18 +657,19 @@ void return_cnp_16B(struct hfi1_ibport *ibp, struct rvt_qp *qp, u32 bth0, plen, vl, hwords = 7; u16 len; u8 l4; - struct hfi1_16b_header hdr; + struct hfi1_opa_header hdr; struct ib_other_headers *ohdr; struct pio_buf *pbuf; struct send_context *ctxt = qp_to_send_context(qp, sc5); struct hfi1_pportdata *ppd = ppd_from_ibp(ibp); u32 nwords; + hdr.hdr_type = HFI1_PKT_TYPE_16B; /* Populate length */ nwords = ((hfi1_get_16b_padding(hwords << 2, 0) + SIZE_OF_LT) >> 2) + SIZE_OF_CRC; if (old_grh) { - struct ib_grh *grh = &hdr.u.l.grh; + struct ib_grh *grh = &hdr.opah.u.l.grh; grh->version_tclass_flow = old_grh->version_tclass_flow; grh->paylen = cpu_to_be16( @@ -675,11 +677,11 @@ void return_cnp_16B(struct hfi1_ibport *ibp, struct rvt_qp *qp, grh->hop_limit = 0xff; grh->sgid = old_grh->dgid; grh->dgid = old_grh->sgid; - ohdr = &hdr.u.l.oth; + ohdr = &hdr.opah.u.l.oth; l4 = OPA_16B_L4_IB_GLOBAL; hwords += sizeof(struct ib_grh) / sizeof(u32); } else { - ohdr = &hdr.u.oth; + ohdr = &hdr.opah.u.oth; l4 = OPA_16B_L4_IB_LOCAL; } @@ -693,7 +695,7 @@ void return_cnp_16B(struct hfi1_ibport *ibp, struct rvt_qp *qp, /* Convert dwords to flits */ len = (hwords + nwords) >> 1; - hfi1_make_16b_hdr(&hdr, slid, dlid, len, pkey, 1, 0, l4, sc5); + hfi1_make_16b_hdr(&hdr.opah, slid, dlid, len, pkey, 1, 0, l4, sc5); plen = 2 /* PBC */ + hwords + nwords; pbc_flags |= PBC_PACKET_BYPASS | PBC_INSERT_BYPASS_ICRC; @@ -701,9 +703,11 @@ void return_cnp_16B(struct hfi1_ibport *ibp, struct rvt_qp *qp, pbc = create_pbc(ppd, pbc_flags, qp->srate_mbps, vl, plen); if (ctxt) { pbuf = sc_buffer_alloc(ctxt, plen, NULL, NULL); - if (pbuf) + if (!IS_ERR_OR_NULL(pbuf)) { + trace_pio_output_ibhdr(ppd->dd, &hdr, sc5); ppd->dd->pio_inline_send(ppd->dd, pbuf, pbc, &hdr, hwords); + } } } @@ -715,14 +719,15 @@ void return_cnp(struct hfi1_ibport *ibp, struct rvt_qp *qp, u32 remote_qpn, u32 bth0, plen, vl, hwords = 5; u16 lrh0; u8 sl = ibp->sc_to_sl[sc5]; - struct ib_header hdr; + struct hfi1_opa_header hdr; struct ib_other_headers *ohdr; struct pio_buf *pbuf; struct send_context *ctxt = qp_to_send_context(qp, sc5); struct hfi1_pportdata *ppd = ppd_from_ibp(ibp); + hdr.hdr_type = HFI1_PKT_TYPE_9B; if (old_grh) { - struct ib_grh *grh = &hdr.u.l.grh; + struct ib_grh *grh = &hdr.ibh.u.l.grh; grh->version_tclass_flow = old_grh->version_tclass_flow; grh->paylen = cpu_to_be16( @@ -730,11 +735,11 @@ void return_cnp(struct hfi1_ibport *ibp, struct rvt_qp *qp, u32 remote_qpn, grh->hop_limit = 0xff; grh->sgid = old_grh->dgid; grh->dgid = old_grh->sgid; - ohdr = &hdr.u.l.oth; + ohdr = &hdr.ibh.u.l.oth; lrh0 = HFI1_LRH_GRH; hwords += sizeof(struct ib_grh) / sizeof(u32); } else { - ohdr = &hdr.u.oth; + ohdr = &hdr.ibh.u.oth; lrh0 = HFI1_LRH_BTH; } @@ -746,16 +751,18 @@ void return_cnp(struct hfi1_ibport *ibp, struct rvt_qp *qp, u32 remote_qpn, ohdr->bth[1] = cpu_to_be32(remote_qpn | (1 << IB_BECN_SHIFT)); ohdr->bth[2] = 0; /* PSN 0 */ - hfi1_make_ib_hdr(&hdr, lrh0, hwords + SIZE_OF_CRC, dlid, slid); + hfi1_make_ib_hdr(&hdr.ibh, lrh0, hwords + SIZE_OF_CRC, dlid, slid); plen = 2 /* PBC */ + hwords; pbc_flags |= (ib_is_sc5(sc5) << PBC_DC_INFO_SHIFT); vl = sc_to_vlt(ppd->dd, sc5); pbc = create_pbc(ppd, pbc_flags, qp->srate_mbps, vl, plen); if (ctxt) { pbuf = sc_buffer_alloc(ctxt, plen, NULL, NULL); - if (pbuf) + if (!IS_ERR_OR_NULL(pbuf)) { + trace_pio_output_ibhdr(ppd->dd, &hdr, sc5); ppd->dd->pio_inline_send(ppd->dd, pbuf, pbc, &hdr, hwords); + } } } @@ -912,7 +919,7 @@ void hfi1_ud_rcv(struct hfi1_packet *packet) src_qp = hfi1_16B_get_src_qpn(packet->mgmt); } - process_ecn(qp, packet, (opcode != IB_OPCODE_CNP)); + process_ecn(qp, packet); /* * Get the number of bytes the message was padded by * and drop incomplete packets. diff --git a/drivers/infiniband/hw/hfi1/user_exp_rcv.c b/drivers/infiniband/hw/hfi1/user_exp_rcv.c index 4e986ca4dd3544af1af660ec09ae0dfa541d6380..4e417ed08b099bffe48b1cf6ab4c400605ae7c5d 100644 --- a/drivers/infiniband/hw/hfi1/user_exp_rcv.c +++ b/drivers/infiniband/hw/hfi1/user_exp_rcv.c @@ -90,9 +90,6 @@ int hfi1_user_exp_rcv_init(struct hfi1_filedata *fd, struct hfi1_devdata *dd = uctxt->dd; int ret = 0; - spin_lock_init(&fd->tid_lock); - spin_lock_init(&fd->invalid_lock); - fd->entry_to_rb = kcalloc(uctxt->expected_count, sizeof(struct rb_node *), GFP_KERNEL); @@ -165,10 +162,12 @@ void hfi1_user_exp_rcv_free(struct hfi1_filedata *fd) if (fd->handler) { hfi1_mmu_rb_unregister(fd->handler); } else { + mutex_lock(&uctxt->exp_mutex); if (!EXP_TID_SET_EMPTY(uctxt->tid_full_list)) unlock_exp_tids(uctxt, &uctxt->tid_full_list, fd); if (!EXP_TID_SET_EMPTY(uctxt->tid_used_list)) unlock_exp_tids(uctxt, &uctxt->tid_used_list, fd); + mutex_unlock(&uctxt->exp_mutex); } kfree(fd->invalid_tids); diff --git a/drivers/infiniband/hw/hfi1/user_sdma.c b/drivers/infiniband/hw/hfi1/user_sdma.c index 684a298e150370af34024410ffdee906073c2b3c..a3b08a9ef5ff934f0ee21464b8815d27fe49cfd5 100644 --- a/drivers/infiniband/hw/hfi1/user_sdma.c +++ b/drivers/infiniband/hw/hfi1/user_sdma.c @@ -179,7 +179,6 @@ int hfi1_user_sdma_alloc_queues(struct hfi1_ctxtdata *uctxt, pq = kzalloc(sizeof(*pq), GFP_KERNEL); if (!pq) return -ENOMEM; - pq->dd = dd; pq->ctxt = uctxt->ctxt; pq->subctxt = fd->subctxt; @@ -236,7 +235,7 @@ int hfi1_user_sdma_alloc_queues(struct hfi1_ctxtdata *uctxt, goto pq_mmu_fail; } - fd->pq = pq; + rcu_assign_pointer(fd->pq, pq); fd->cq = cq; return 0; @@ -264,8 +263,14 @@ int hfi1_user_sdma_free_queues(struct hfi1_filedata *fd, trace_hfi1_sdma_user_free_queues(uctxt->dd, uctxt->ctxt, fd->subctxt); - pq = fd->pq; + spin_lock(&fd->pq_rcu_lock); + pq = srcu_dereference_check(fd->pq, &fd->pq_srcu, + lockdep_is_held(&fd->pq_rcu_lock)); if (pq) { + rcu_assign_pointer(fd->pq, NULL); + spin_unlock(&fd->pq_rcu_lock); + synchronize_srcu(&fd->pq_srcu); + /* at this point there can be no more new requests */ if (pq->handler) hfi1_mmu_rb_unregister(pq->handler); iowait_sdma_drain(&pq->busy); @@ -277,7 +282,8 @@ int hfi1_user_sdma_free_queues(struct hfi1_filedata *fd, kfree(pq->req_in_use); kmem_cache_destroy(pq->txreq_cache); kfree(pq); - fd->pq = NULL; + } else { + spin_unlock(&fd->pq_rcu_lock); } if (fd->cq) { vfree(fd->cq->comps); @@ -321,7 +327,8 @@ int hfi1_user_sdma_process_request(struct hfi1_filedata *fd, { int ret = 0, i; struct hfi1_ctxtdata *uctxt = fd->uctxt; - struct hfi1_user_sdma_pkt_q *pq = fd->pq; + struct hfi1_user_sdma_pkt_q *pq = + srcu_dereference(fd->pq, &fd->pq_srcu); struct hfi1_user_sdma_comp_q *cq = fd->cq; struct hfi1_devdata *dd = pq->dd; unsigned long idx = 0; diff --git a/drivers/infiniband/hw/hfi1/verbs.c b/drivers/infiniband/hw/hfi1/verbs.c index 4e7b3c027901bd21fec925ef029cb471a1cc887c..1cf1dfbf2596f150c9cf361a7817247725c94abf 100644 --- a/drivers/infiniband/hw/hfi1/verbs.c +++ b/drivers/infiniband/hw/hfi1/verbs.c @@ -595,10 +595,11 @@ static inline void hfi1_handle_packet(struct hfi1_packet *packet, opa_get_lid(packet->dlid, 9B)); if (!mcast) goto drop; + rcu_read_lock(); list_for_each_entry_rcu(p, &mcast->qp_list, list) { packet->qp = p->qp; if (hfi1_do_pkey_check(packet)) - goto drop; + goto unlock_drop; spin_lock_irqsave(&packet->qp->r_lock, flags); packet_handler = qp_ok(packet); if (likely(packet_handler)) @@ -607,6 +608,7 @@ static inline void hfi1_handle_packet(struct hfi1_packet *packet, ibp->rvp.n_pkt_drops++; spin_unlock_irqrestore(&packet->qp->r_lock, flags); } + rcu_read_unlock(); /* * Notify rvt_multicast_detach() if it is waiting for us * to finish. @@ -1096,10 +1098,10 @@ int hfi1_verbs_send_pio(struct rvt_qp *qp, struct hfi1_pkt_state *ps, if (cb) iowait_pio_inc(&priv->s_iowait); pbuf = sc_buffer_alloc(sc, plen, cb, qp); - if (unlikely(!pbuf)) { + if (unlikely(IS_ERR_OR_NULL(pbuf))) { if (cb) verbs_pio_complete(qp, 0); - if (ppd->host_link_state != HLS_UP_ACTIVE) { + if (IS_ERR(pbuf)) { /* * If we have filled the PIO buffers to capacity and are * not in an active state this request is not going to diff --git a/drivers/infiniband/hw/hns/hns_roce_hem.c b/drivers/infiniband/hw/hns/hns_roce_hem.c index a73d388b70930a8f640fbb5a14f5ed2a27e8bcff..31b9b99f81cb7c0992f51451752b2a50e3a4b8b7 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hem.c +++ b/drivers/infiniband/hw/hns/hns_roce_hem.c @@ -54,12 +54,13 @@ bool hns_roce_check_whether_mhop(struct hns_roce_dev *hr_dev, u32 type) EXPORT_SYMBOL_GPL(hns_roce_check_whether_mhop); static bool hns_roce_check_hem_null(struct hns_roce_hem **hem, u64 start_idx, - u32 bt_chunk_num) + u32 bt_chunk_num, u64 hem_max_num) { - int i; + u64 check_max_num = start_idx + bt_chunk_num; + u64 i; - for (i = 0; i < bt_chunk_num; i++) - if (hem[start_idx + i]) + for (i = start_idx; (i < check_max_num) && (i < hem_max_num); i++) + if (hem[i]) return false; return true; @@ -413,6 +414,12 @@ static int hns_roce_table_mhop_get(struct hns_roce_dev *hr_dev, return -EINVAL; } + if (unlikely(hem_idx >= table->num_hem)) { + dev_err(dev, "Table %d exceed hem limt idx = %llu,max = %lu!\n", + table->type, hem_idx, table->num_hem); + return -EINVAL; + } + mutex_lock(&table->mutex); if (table->hem[hem_idx]) { @@ -649,7 +656,7 @@ static void hns_roce_table_mhop_put(struct hns_roce_dev *hr_dev, if (check_whether_bt_num_2(table->type, hop_num)) { start_idx = mhop.l0_idx * chunk_ba_num; if (hns_roce_check_hem_null(table->hem, start_idx, - chunk_ba_num)) { + chunk_ba_num, table->num_hem)) { if (table->type < HEM_TYPE_MTT && hr_dev->hw->clear_hem(hr_dev, table, obj, 0)) dev_warn(dev, "Clear HEM base address failed.\n"); @@ -663,7 +670,7 @@ static void hns_roce_table_mhop_put(struct hns_roce_dev *hr_dev, start_idx = mhop.l0_idx * chunk_ba_num * chunk_ba_num + mhop.l1_idx * chunk_ba_num; if (hns_roce_check_hem_null(table->hem, start_idx, - chunk_ba_num)) { + chunk_ba_num, table->num_hem)) { if (hr_dev->hw->clear_hem(hr_dev, table, obj, 1)) dev_warn(dev, "Clear HEM base address failed.\n"); diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index 7021444f18b46d0bd539586e6bfd45582cab038e..417de7ac0d5e215cf54540d4f7b3bfc999b18168 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -4833,7 +4833,8 @@ static int hns_roce_mhop_alloc_eq(struct hns_roce_dev *hr_dev, break; } eq->cur_eqe_ba = eq->buf_dma[0]; - eq->nxt_eqe_ba = eq->buf_dma[1]; + if (ba_num > 1) + eq->nxt_eqe_ba = eq->buf_dma[1]; } else if (mhop_num == 2) { /* alloc L1 BT and buf */ @@ -4875,7 +4876,8 @@ static int hns_roce_mhop_alloc_eq(struct hns_roce_dev *hr_dev, break; } eq->cur_eqe_ba = eq->buf_dma[0]; - eq->nxt_eqe_ba = eq->buf_dma[1]; + if (ba_num > 1) + eq->nxt_eqe_ba = eq->buf_dma[1]; } eq->l0_last_num = i + 1; diff --git a/drivers/infiniband/hw/hns/hns_roce_qp.c b/drivers/infiniband/hw/hns/hns_roce_qp.c index af24698ff22627c78b26e510484327e906e7dd69..3012d7eb4ccb44065976de9bf4f13d19619d4f45 100644 --- a/drivers/infiniband/hw/hns/hns_roce_qp.c +++ b/drivers/infiniband/hw/hns/hns_roce_qp.c @@ -262,7 +262,6 @@ void hns_roce_qp_free(struct hns_roce_dev *hr_dev, struct hns_roce_qp *hr_qp) hns_roce_table_put(hr_dev, &qp_table->trrl_table, hr_qp->qpn); hns_roce_table_put(hr_dev, &qp_table->irrl_table, hr_qp->qpn); - hns_roce_table_put(hr_dev, &qp_table->qp_table, hr_qp->qpn); } } EXPORT_SYMBOL_GPL(hns_roce_qp_free); diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index 9386bb57b3d716f2b7b6f209ea5965eab6115c18..a19d3ad14dc374309ee1d1025ff5140aa45d0880 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -246,6 +246,13 @@ static int mlx4_ib_update_gids(struct gid_entry *gids, return mlx4_ib_update_gids_v1(gids, ibdev, port_num); } +static void free_gid_entry(struct gid_entry *entry) +{ + memset(&entry->gid, 0, sizeof(entry->gid)); + kfree(entry->ctx); + entry->ctx = NULL; +} + static int mlx4_ib_add_gid(const struct ib_gid_attr *attr, void **context) { struct mlx4_ib_dev *ibdev = to_mdev(attr->device); @@ -306,6 +313,8 @@ static int mlx4_ib_add_gid(const struct ib_gid_attr *attr, void **context) GFP_ATOMIC); if (!gids) { ret = -ENOMEM; + *context = NULL; + free_gid_entry(&port_gid_table->gids[free]); } else { for (i = 0; i < MLX4_MAX_PORT_GIDS; i++) { memcpy(&gids[i].gid, &port_gid_table->gids[i].gid, sizeof(union ib_gid)); @@ -317,6 +326,12 @@ static int mlx4_ib_add_gid(const struct ib_gid_attr *attr, void **context) if (!ret && hw_update) { ret = mlx4_ib_update_gids(gids, ibdev, attr->port_num); + if (ret) { + spin_lock_bh(&iboe->lock); + *context = NULL; + free_gid_entry(&port_gid_table->gids[free]); + spin_unlock_bh(&iboe->lock); + } kfree(gids); } @@ -346,10 +361,7 @@ static int mlx4_ib_del_gid(const struct ib_gid_attr *attr, void **context) if (!ctx->refcount) { unsigned int real_index = ctx->real_index; - memset(&port_gid_table->gids[real_index].gid, 0, - sizeof(port_gid_table->gids[real_index].gid)); - kfree(port_gid_table->gids[real_index].ctx); - port_gid_table->gids[real_index].ctx = NULL; + free_gid_entry(&port_gid_table->gids[real_index]); hw_update = 1; } } diff --git a/drivers/infiniband/hw/mlx5/gsi.c b/drivers/infiniband/hw/mlx5/gsi.c index 4950df3f71b6084e78b9b5c884c6197f306ee879..5c73c0a790fa1fd0c131f7af6cc39c67fd59ad2d 100644 --- a/drivers/infiniband/hw/mlx5/gsi.c +++ b/drivers/infiniband/hw/mlx5/gsi.c @@ -507,8 +507,7 @@ int mlx5_ib_gsi_post_send(struct ib_qp *qp, const struct ib_send_wr *wr, ret = ib_post_send(tx_qp, &cur_wr.wr, bad_wr); if (ret) { /* Undo the effect of adding the outstanding wr */ - gsi->outstanding_pi = (gsi->outstanding_pi - 1) % - gsi->cap.max_send_wr; + gsi->outstanding_pi--; goto err; } spin_unlock_irqrestore(&gsi->lock, flags); diff --git a/drivers/infiniband/hw/mlx5/ib_rep.c b/drivers/infiniband/hw/mlx5/ib_rep.c index 35a0e04c38f288803541b59ad0b24671413e38e6..b841589c27c9c40e898d06a4fc3f2aeaabc27b79 100644 --- a/drivers/infiniband/hw/mlx5/ib_rep.c +++ b/drivers/infiniband/hw/mlx5/ib_rep.c @@ -69,8 +69,10 @@ mlx5_ib_vport_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep) ibdev->mdev = dev; ibdev->num_ports = max(MLX5_CAP_GEN(dev, num_ports), MLX5_CAP_GEN(dev, num_vhca_ports)); - if (!__mlx5_ib_add(ibdev, &rep_profile)) + if (!__mlx5_ib_add(ibdev, &rep_profile)) { + ib_dealloc_device(&ibdev->ib_dev); return -EINVAL; + } rep->rep_if[REP_IB].priv = ibdev; diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index df5be462dd281dbd5c035e20eb4d53af1548d1cf..2db34f7b5ced1b781569565ea50498309b2e72c5 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -2390,10 +2390,29 @@ static u8 get_match_criteria_enable(u32 *match_criteria) return match_criteria_enable; } -static void set_proto(void *outer_c, void *outer_v, u8 mask, u8 val) +static int set_proto(void *outer_c, void *outer_v, u8 mask, u8 val) { - MLX5_SET(fte_match_set_lyr_2_4, outer_c, ip_protocol, mask); - MLX5_SET(fte_match_set_lyr_2_4, outer_v, ip_protocol, val); + u8 entry_mask; + u8 entry_val; + int err = 0; + + if (!mask) + goto out; + + entry_mask = MLX5_GET(fte_match_set_lyr_2_4, outer_c, + ip_protocol); + entry_val = MLX5_GET(fte_match_set_lyr_2_4, outer_v, + ip_protocol); + if (!entry_mask) { + MLX5_SET(fte_match_set_lyr_2_4, outer_c, ip_protocol, mask); + MLX5_SET(fte_match_set_lyr_2_4, outer_v, ip_protocol, val); + goto out; + } + /* Don't override existing ip protocol */ + if (mask != entry_mask || val != entry_val) + err = -EINVAL; +out: + return err; } static void set_flow_label(void *misc_c, void *misc_v, u32 mask, u32 val, @@ -2597,8 +2616,10 @@ static int parse_flow_attr(struct mlx5_core_dev *mdev, u32 *match_c, set_tos(headers_c, headers_v, ib_spec->ipv4.mask.tos, ib_spec->ipv4.val.tos); - set_proto(headers_c, headers_v, - ib_spec->ipv4.mask.proto, ib_spec->ipv4.val.proto); + if (set_proto(headers_c, headers_v, + ib_spec->ipv4.mask.proto, + ib_spec->ipv4.val.proto)) + return -EINVAL; break; case IB_FLOW_SPEC_IPV6: if (FIELDS_NOT_SUPPORTED(ib_spec->ipv6.mask, LAST_IPV6_FIELD)) @@ -2637,9 +2658,10 @@ static int parse_flow_attr(struct mlx5_core_dev *mdev, u32 *match_c, ib_spec->ipv6.mask.traffic_class, ib_spec->ipv6.val.traffic_class); - set_proto(headers_c, headers_v, - ib_spec->ipv6.mask.next_hdr, - ib_spec->ipv6.val.next_hdr); + if (set_proto(headers_c, headers_v, + ib_spec->ipv6.mask.next_hdr, + ib_spec->ipv6.val.next_hdr)) + return -EINVAL; set_flow_label(misc_params_c, misc_params_v, ntohl(ib_spec->ipv6.mask.flow_label), @@ -2660,10 +2682,8 @@ static int parse_flow_attr(struct mlx5_core_dev *mdev, u32 *match_c, LAST_TCP_UDP_FIELD)) return -EOPNOTSUPP; - MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_protocol, - 0xff); - MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_protocol, - IPPROTO_TCP); + if (set_proto(headers_c, headers_v, 0xff, IPPROTO_TCP)) + return -EINVAL; MLX5_SET(fte_match_set_lyr_2_4, headers_c, tcp_sport, ntohs(ib_spec->tcp_udp.mask.src_port)); @@ -2680,10 +2700,8 @@ static int parse_flow_attr(struct mlx5_core_dev *mdev, u32 *match_c, LAST_TCP_UDP_FIELD)) return -EOPNOTSUPP; - MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_protocol, - 0xff); - MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_protocol, - IPPROTO_UDP); + if (set_proto(headers_c, headers_v, 0xff, IPPROTO_UDP)) + return -EINVAL; MLX5_SET(fte_match_set_lyr_2_4, headers_c, udp_sport, ntohs(ib_spec->tcp_udp.mask.src_port)); @@ -2699,6 +2717,9 @@ static int parse_flow_attr(struct mlx5_core_dev *mdev, u32 *match_c, if (ib_spec->gre.mask.c_ks_res0_ver) return -EOPNOTSUPP; + if (set_proto(headers_c, headers_v, 0xff, IPPROTO_GRE)) + return -EINVAL; + MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_protocol, 0xff); MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_protocol, diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index bd1fdadf7ba01907078b9cae895be3a14fa4151e..18fd9aa6510f2835b525969ceeef561efdf6d63d 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -457,7 +457,7 @@ struct mlx5_ib_mr *mlx5_mr_cache_alloc(struct mlx5_ib_dev *dev, int entry) if (entry < 0 || entry >= MAX_MR_CACHE_ENTRIES) { mlx5_ib_err(dev, "cache entry %d is out of range\n", entry); - return NULL; + return ERR_PTR(-EINVAL); } ent = &cache->ent[entry]; diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index ef0f710587ad849e134f2a48e63d943f1937753a..4c0f0ce02d2f7471a9f88a7c3446b99a9f799327 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -2598,6 +2598,11 @@ static enum mlx5_qp_optpar opt_mask[MLX5_QP_NUM_STATE][MLX5_QP_NUM_STATE][MLX5_Q [MLX5_QP_ST_UD] = MLX5_QP_OPTPAR_PKEY_INDEX | MLX5_QP_OPTPAR_Q_KEY | MLX5_QP_OPTPAR_PRI_PORT, + [MLX5_QP_ST_XRC] = MLX5_QP_OPTPAR_RRE | + MLX5_QP_OPTPAR_RAE | + MLX5_QP_OPTPAR_RWE | + MLX5_QP_OPTPAR_PKEY_INDEX | + MLX5_QP_OPTPAR_PRI_PORT, }, [MLX5_QP_STATE_RTR] = { [MLX5_QP_ST_RC] = MLX5_QP_OPTPAR_ALT_ADDR_PATH | @@ -2631,6 +2636,12 @@ static enum mlx5_qp_optpar opt_mask[MLX5_QP_NUM_STATE][MLX5_QP_NUM_STATE][MLX5_Q MLX5_QP_OPTPAR_RWE | MLX5_QP_OPTPAR_PM_STATE, [MLX5_QP_ST_UD] = MLX5_QP_OPTPAR_Q_KEY, + [MLX5_QP_ST_XRC] = MLX5_QP_OPTPAR_ALT_ADDR_PATH | + MLX5_QP_OPTPAR_RRE | + MLX5_QP_OPTPAR_RAE | + MLX5_QP_OPTPAR_RWE | + MLX5_QP_OPTPAR_PM_STATE | + MLX5_QP_OPTPAR_RNR_TIMEOUT, }, }, [MLX5_QP_STATE_RTS] = { @@ -2647,6 +2658,12 @@ static enum mlx5_qp_optpar opt_mask[MLX5_QP_NUM_STATE][MLX5_QP_NUM_STATE][MLX5_Q [MLX5_QP_ST_UD] = MLX5_QP_OPTPAR_Q_KEY | MLX5_QP_OPTPAR_SRQN | MLX5_QP_OPTPAR_CQN_RCV, + [MLX5_QP_ST_XRC] = MLX5_QP_OPTPAR_RRE | + MLX5_QP_OPTPAR_RAE | + MLX5_QP_OPTPAR_RWE | + MLX5_QP_OPTPAR_RNR_TIMEOUT | + MLX5_QP_OPTPAR_PM_STATE | + MLX5_QP_OPTPAR_ALT_ADDR_PATH, }, }, [MLX5_QP_STATE_SQER] = { @@ -2658,6 +2675,10 @@ static enum mlx5_qp_optpar opt_mask[MLX5_QP_NUM_STATE][MLX5_QP_NUM_STATE][MLX5_Q MLX5_QP_OPTPAR_RWE | MLX5_QP_OPTPAR_RAE | MLX5_QP_OPTPAR_RRE, + [MLX5_QP_ST_XRC] = MLX5_QP_OPTPAR_RNR_TIMEOUT | + MLX5_QP_OPTPAR_RWE | + MLX5_QP_OPTPAR_RAE | + MLX5_QP_OPTPAR_RRE, }, }, }; diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c index c158ca9fde6dc89a8fa9e547f110fa5793b3283d..08271fce0b9e406b8fd94296379157f7c118ca14 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c @@ -55,7 +55,7 @@ int ocrdma_query_pkey(struct ib_device *ibdev, u8 port, u16 index, u16 *pkey) { - if (index > 1) + if (index > 0) return -EINVAL; *pkey = 0xffff; diff --git a/drivers/infiniband/hw/qedr/verbs.c b/drivers/infiniband/hw/qedr/verbs.c index 9167a1c40bcf5ff8b0164e861cae25a8aa0edb74..38fe2f74137574cb28e96aca32fc8ed2ec604bb1 100644 --- a/drivers/infiniband/hw/qedr/verbs.c +++ b/drivers/infiniband/hw/qedr/verbs.c @@ -67,7 +67,7 @@ static inline int qedr_ib_copy_to_udata(struct ib_udata *udata, void *src, int qedr_query_pkey(struct ib_device *ibdev, u8 port, u16 index, u16 *pkey) { - if (index > QEDR_ROCE_PKEY_TABLE_LEN) + if (index >= QEDR_ROCE_PKEY_TABLE_LEN) return -EINVAL; *pkey = QEDR_ROCE_PKEY_DEFAULT; @@ -158,54 +158,47 @@ int qedr_query_device(struct ib_device *ibdev, return 0; } -#define QEDR_SPEED_SDR (1) -#define QEDR_SPEED_DDR (2) -#define QEDR_SPEED_QDR (4) -#define QEDR_SPEED_FDR10 (8) -#define QEDR_SPEED_FDR (16) -#define QEDR_SPEED_EDR (32) - static inline void get_link_speed_and_width(int speed, u8 *ib_speed, u8 *ib_width) { switch (speed) { case 1000: - *ib_speed = QEDR_SPEED_SDR; + *ib_speed = IB_SPEED_SDR; *ib_width = IB_WIDTH_1X; break; case 10000: - *ib_speed = QEDR_SPEED_QDR; + *ib_speed = IB_SPEED_QDR; *ib_width = IB_WIDTH_1X; break; case 20000: - *ib_speed = QEDR_SPEED_DDR; + *ib_speed = IB_SPEED_DDR; *ib_width = IB_WIDTH_4X; break; case 25000: - *ib_speed = QEDR_SPEED_EDR; + *ib_speed = IB_SPEED_EDR; *ib_width = IB_WIDTH_1X; break; case 40000: - *ib_speed = QEDR_SPEED_QDR; + *ib_speed = IB_SPEED_QDR; *ib_width = IB_WIDTH_4X; break; case 50000: - *ib_speed = QEDR_SPEED_QDR; - *ib_width = IB_WIDTH_4X; + *ib_speed = IB_SPEED_HDR; + *ib_width = IB_WIDTH_1X; break; case 100000: - *ib_speed = QEDR_SPEED_EDR; + *ib_speed = IB_SPEED_EDR; *ib_width = IB_WIDTH_4X; break; default: /* Unsupported */ - *ib_speed = QEDR_SPEED_SDR; + *ib_speed = IB_SPEED_SDR; *ib_width = IB_WIDTH_1X; } } diff --git a/drivers/infiniband/hw/qib/qib_verbs.c b/drivers/infiniband/hw/qib/qib_verbs.c index 803c3544c75b5fbfbd3d8a35c96dcc3504e42ca8..5abbbb656a522831a3a92b557170461ecfa0c364 100644 --- a/drivers/infiniband/hw/qib/qib_verbs.c +++ b/drivers/infiniband/hw/qib/qib_verbs.c @@ -360,8 +360,10 @@ void qib_ib_rcv(struct qib_ctxtdata *rcd, void *rhdr, void *data, u32 tlen) if (mcast == NULL) goto drop; this_cpu_inc(ibp->pmastats->n_multicast_rcv); + rcu_read_lock(); list_for_each_entry_rcu(p, &mcast->qp_list, list) qib_qp_rcv(rcd, hdr, 1, data, tlen, p->qp); + rcu_read_unlock(); /* * Notify rvt_multicast_detach() if it is waiting for us * to finish. diff --git a/drivers/infiniband/hw/usnic/usnic_ib_verbs.c b/drivers/infiniband/hw/usnic/usnic_ib_verbs.c index 3db232429630e3899cab70f8d21d7b34ff2feb07..e611f133aa97b2e947c962edd8a7ad743bf1555c 100644 --- a/drivers/infiniband/hw/usnic/usnic_ib_verbs.c +++ b/drivers/infiniband/hw/usnic/usnic_ib_verbs.c @@ -447,7 +447,7 @@ struct net_device *usnic_get_netdev(struct ib_device *device, u8 port_num) int usnic_ib_query_pkey(struct ib_device *ibdev, u8 port, u16 index, u16 *pkey) { - if (index > 1) + if (index > 0) return -EINVAL; *pkey = 0xffff; diff --git a/drivers/infiniband/sw/rdmavt/qp.c b/drivers/infiniband/sw/rdmavt/qp.c index 7d03680afd9145c535f1a8dc45845cb50fbffb35..fbc316775669c47e839ac9f158aea27cb3fa30f1 100644 --- a/drivers/infiniband/sw/rdmavt/qp.c +++ b/drivers/infiniband/sw/rdmavt/qp.c @@ -58,6 +58,8 @@ #include "trace.h" static void rvt_rc_timeout(struct timer_list *t); +static void rvt_reset_qp(struct rvt_dev_info *rdi, struct rvt_qp *qp, + enum ib_qp_type type); /* * Convert the AETH RNR timeout code into the number of microseconds. @@ -268,40 +270,41 @@ int rvt_driver_qp_init(struct rvt_dev_info *rdi) } /** - * free_all_qps - check for QPs still in use + * rvt_free_qp_cb - callback function to reset a qp + * @qp: the qp to reset + * @v: a 64-bit value + * + * This function resets the qp and removes it from the + * qp hash table. + */ +static void rvt_free_qp_cb(struct rvt_qp *qp, u64 v) +{ + unsigned int *qp_inuse = (unsigned int *)v; + struct rvt_dev_info *rdi = ib_to_rvt(qp->ibqp.device); + + /* Reset the qp and remove it from the qp hash list */ + rvt_reset_qp(rdi, qp, qp->ibqp.qp_type); + + /* Increment the qp_inuse count */ + (*qp_inuse)++; +} + +/** + * rvt_free_all_qps - check for QPs still in use * @rdi: rvt device info structure * * There should not be any QPs still in use. * Free memory for table. + * Return the number of QPs still in use. */ static unsigned rvt_free_all_qps(struct rvt_dev_info *rdi) { - unsigned long flags; - struct rvt_qp *qp; - unsigned n, qp_inuse = 0; - spinlock_t *ql; /* work around too long line below */ - - if (rdi->driver_f.free_all_qps) - qp_inuse = rdi->driver_f.free_all_qps(rdi); + unsigned int qp_inuse = 0; qp_inuse += rvt_mcast_tree_empty(rdi); - if (!rdi->qp_dev) - return qp_inuse; + rvt_qp_iter(rdi, (u64)&qp_inuse, rvt_free_qp_cb); - ql = &rdi->qp_dev->qpt_lock; - spin_lock_irqsave(ql, flags); - for (n = 0; n < rdi->qp_dev->qp_table_size; n++) { - qp = rcu_dereference_protected(rdi->qp_dev->qp_table[n], - lockdep_is_held(ql)); - RCU_INIT_POINTER(rdi->qp_dev->qp_table[n], NULL); - - for (; qp; qp = rcu_dereference_protected(qp->next, - lockdep_is_held(ql))) - qp_inuse++; - } - spin_unlock_irqrestore(ql, flags); - synchronize_rcu(); return qp_inuse; } @@ -684,14 +687,14 @@ static void rvt_init_qp(struct rvt_dev_info *rdi, struct rvt_qp *qp, } /** - * rvt_reset_qp - initialize the QP state to the reset state + * _rvt_reset_qp - initialize the QP state to the reset state * @qp: the QP to reset * @type: the QP type * * r_lock, s_hlock, and s_lock are required to be held by the caller */ -static void rvt_reset_qp(struct rvt_dev_info *rdi, struct rvt_qp *qp, - enum ib_qp_type type) +static void _rvt_reset_qp(struct rvt_dev_info *rdi, struct rvt_qp *qp, + enum ib_qp_type type) __must_hold(&qp->s_lock) __must_hold(&qp->s_hlock) __must_hold(&qp->r_lock) @@ -737,6 +740,27 @@ static void rvt_reset_qp(struct rvt_dev_info *rdi, struct rvt_qp *qp, lockdep_assert_held(&qp->s_lock); } +/** + * rvt_reset_qp - initialize the QP state to the reset state + * @rdi: the device info + * @qp: the QP to reset + * @type: the QP type + * + * This is the wrapper function to acquire the r_lock, s_hlock, and s_lock + * before calling _rvt_reset_qp(). + */ +static void rvt_reset_qp(struct rvt_dev_info *rdi, struct rvt_qp *qp, + enum ib_qp_type type) +{ + spin_lock_irq(&qp->r_lock); + spin_lock(&qp->s_hlock); + spin_lock(&qp->s_lock); + _rvt_reset_qp(rdi, qp, type); + spin_unlock(&qp->s_lock); + spin_unlock(&qp->s_hlock); + spin_unlock_irq(&qp->r_lock); +} + /** rvt_free_qpn - Free a qpn from the bit map * @qpt: QP table * @qpn: queue pair number to free @@ -1285,7 +1309,7 @@ int rvt_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, switch (new_state) { case IB_QPS_RESET: if (qp->state != IB_QPS_RESET) - rvt_reset_qp(rdi, qp, ibqp->qp_type); + _rvt_reset_qp(rdi, qp, ibqp->qp_type); break; case IB_QPS_RTR: @@ -1434,13 +1458,7 @@ int rvt_destroy_qp(struct ib_qp *ibqp) struct rvt_qp *qp = ibqp_to_rvtqp(ibqp); struct rvt_dev_info *rdi = ib_to_rvt(ibqp->device); - spin_lock_irq(&qp->r_lock); - spin_lock(&qp->s_hlock); - spin_lock(&qp->s_lock); rvt_reset_qp(rdi, qp, ibqp->qp_type); - spin_unlock(&qp->s_lock); - spin_unlock(&qp->s_hlock); - spin_unlock_irq(&qp->r_lock); wait_event(qp->wait, !atomic_read(&qp->refcount)); /* qpn is now available for use again */ diff --git a/drivers/infiniband/sw/rxe/rxe_comp.c b/drivers/infiniband/sw/rxe/rxe_comp.c index ea089cb091ade455029956a12dada273ac2d1092..dc06e984437873724d6eb055ebf82cf9bb887bd3 100644 --- a/drivers/infiniband/sw/rxe/rxe_comp.c +++ b/drivers/infiniband/sw/rxe/rxe_comp.c @@ -329,7 +329,7 @@ static inline enum comp_state check_ack(struct rxe_qp *qp, qp->comp.psn = pkt->psn; if (qp->req.wait_psn) { qp->req.wait_psn = 0; - rxe_run_task(&qp->req.task, 1); + rxe_run_task(&qp->req.task, 0); } } return COMPST_ERROR_RETRY; @@ -457,7 +457,7 @@ static void do_complete(struct rxe_qp *qp, struct rxe_send_wqe *wqe) */ if (qp->req.wait_fence) { qp->req.wait_fence = 0; - rxe_run_task(&qp->req.task, 1); + rxe_run_task(&qp->req.task, 0); } } @@ -473,7 +473,7 @@ static inline enum comp_state complete_ack(struct rxe_qp *qp, if (qp->req.need_rd_atomic) { qp->comp.timeout_retry = 0; qp->req.need_rd_atomic = 0; - rxe_run_task(&qp->req.task, 1); + rxe_run_task(&qp->req.task, 0); } } @@ -719,7 +719,7 @@ int rxe_completer(void *arg) RXE_CNT_COMP_RETRY); qp->req.need_retry = 1; qp->comp.started_retry = 1; - rxe_run_task(&qp->req.task, 1); + rxe_run_task(&qp->req.task, 0); } if (pkt) { diff --git a/drivers/infiniband/sw/rxe/rxe_cq.c b/drivers/infiniband/sw/rxe/rxe_cq.c index 2ee4b08b00ea4ce87e5d526597fc3e8796cb31cc..a57276f2cb84938ca8b48904878e285e3d5165b6 100644 --- a/drivers/infiniband/sw/rxe/rxe_cq.c +++ b/drivers/infiniband/sw/rxe/rxe_cq.c @@ -30,7 +30,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - +#include #include "rxe.h" #include "rxe_loc.h" #include "rxe_queue.h" @@ -97,7 +97,7 @@ int rxe_cq_from_init(struct rxe_dev *rxe, struct rxe_cq *cq, int cqe, err = do_mmap_info(rxe, uresp ? &uresp->mi : NULL, context, cq->queue->buf, cq->queue->buf_size, &cq->queue->ip); if (err) { - kvfree(cq->queue->buf); + vfree(cq->queue->buf); kfree(cq->queue); return err; } diff --git a/drivers/infiniband/sw/rxe/rxe_net.c b/drivers/infiniband/sw/rxe/rxe_net.c index 8094cbaa54a9e2684bd41ae6774a886919461c46..54add70c22b5c370bf7c8648b30cc61df9b26644 100644 --- a/drivers/infiniband/sw/rxe/rxe_net.c +++ b/drivers/infiniband/sw/rxe/rxe_net.c @@ -533,8 +533,9 @@ struct sk_buff *rxe_init_packet(struct rxe_dev *rxe, struct rxe_av *av, if (unlikely(!skb)) goto out; - skb_reserve(skb, hdr_len + LL_RESERVED_SPACE(rxe->ndev)); + skb_reserve(skb, hdr_len + LL_RESERVED_SPACE(ndev)); + /* FIXME: hold reference to this netdev until life of this skb. */ skb->dev = ndev; if (av->network_type == RDMA_NETWORK_IPV4) skb->protocol = htons(ETH_P_IP); diff --git a/drivers/infiniband/sw/rxe/rxe_pool.c b/drivers/infiniband/sw/rxe/rxe_pool.c index b4a8acc7bb7d62fb479473df7ec6aaacde86e533..0e2425f2823351778c1b63bd135864c59b32d4af 100644 --- a/drivers/infiniband/sw/rxe/rxe_pool.c +++ b/drivers/infiniband/sw/rxe/rxe_pool.c @@ -112,6 +112,18 @@ static inline struct kmem_cache *pool_cache(struct rxe_pool *pool) return rxe_type_info[pool->type].cache; } +static void rxe_cache_clean(size_t cnt) +{ + int i; + struct rxe_type_info *type; + + for (i = 0; i < cnt; i++) { + type = &rxe_type_info[i]; + kmem_cache_destroy(type->cache); + type->cache = NULL; + } +} + int rxe_cache_init(void) { int err; @@ -136,24 +148,14 @@ int rxe_cache_init(void) return 0; err1: - while (--i >= 0) { - kmem_cache_destroy(type->cache); - type->cache = NULL; - } + rxe_cache_clean(i); return err; } void rxe_cache_exit(void) { - int i; - struct rxe_type_info *type; - - for (i = 0; i < RXE_NUM_TYPES; i++) { - type = &rxe_type_info[i]; - kmem_cache_destroy(type->cache); - type->cache = NULL; - } + rxe_cache_clean(RXE_NUM_TYPES); } static int rxe_pool_init_index(struct rxe_pool *pool, u32 max, u32 min) diff --git a/drivers/infiniband/sw/rxe/rxe_qp.c b/drivers/infiniband/sw/rxe/rxe_qp.c index c58452daffc7449875f362269b7cd51bb9f8803e..230697fa31fe36705cc9b5c6d05e2d4d963ebc36 100644 --- a/drivers/infiniband/sw/rxe/rxe_qp.c +++ b/drivers/infiniband/sw/rxe/rxe_qp.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "rxe.h" #include "rxe_loc.h" @@ -247,7 +248,7 @@ static int rxe_qp_init_req(struct rxe_dev *rxe, struct rxe_qp *qp, &qp->sq.queue->ip); if (err) { - kvfree(qp->sq.queue->buf); + vfree(qp->sq.queue->buf); kfree(qp->sq.queue); return err; } @@ -300,7 +301,7 @@ static int rxe_qp_init_resp(struct rxe_dev *rxe, struct rxe_qp *qp, qp->rq.queue->buf, qp->rq.queue->buf_size, &qp->rq.queue->ip); if (err) { - kvfree(qp->rq.queue->buf); + vfree(qp->rq.queue->buf); kfree(qp->rq.queue); return err; } diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.h b/drivers/infiniband/sw/rxe/rxe_verbs.h index 6a75f96b90962db0806613b7e31eba6d66d5a986..b4e24362edbb088523bfec416c909c6083a7e7a8 100644 --- a/drivers/infiniband/sw/rxe/rxe_verbs.h +++ b/drivers/infiniband/sw/rxe/rxe_verbs.h @@ -407,7 +407,7 @@ struct rxe_dev { struct list_head pending_mmaps; spinlock_t mmap_offset_lock; /* guard mmap_offset */ - int mmap_offset; + u64 mmap_offset; atomic64_t stats_counters[RXE_NUM_OF_COUNTERS]; diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.h b/drivers/infiniband/ulp/iser/iscsi_iser.h index 120b408295603d31cccceeffaeea9b7ee4778540..a7aeaa0c6fbc9281cde32678dfc8f087ba97ddfd 100644 --- a/drivers/infiniband/ulp/iser/iscsi_iser.h +++ b/drivers/infiniband/ulp/iser/iscsi_iser.h @@ -197,7 +197,7 @@ struct iser_data_buf { struct scatterlist *sg; int size; unsigned long data_len; - unsigned int dma_nents; + int dma_nents; }; /* fwd declarations */ diff --git a/drivers/infiniband/ulp/iser/iser_memory.c b/drivers/infiniband/ulp/iser/iser_memory.c index 009be8889d71d05c7eeeb7d6684276ae54c76911..379bc0dfc3885e3a3de626854bbe0f72ff539ba4 100644 --- a/drivers/infiniband/ulp/iser/iser_memory.c +++ b/drivers/infiniband/ulp/iser/iser_memory.c @@ -240,8 +240,8 @@ int iser_fast_reg_fmr(struct iscsi_iser_task *iser_task, page_vec->npages = 0; page_vec->fake_mr.page_size = SIZE_4K; plen = ib_sg_to_pages(&page_vec->fake_mr, mem->sg, - mem->size, NULL, iser_set_page); - if (unlikely(plen < mem->size)) { + mem->dma_nents, NULL, iser_set_page); + if (unlikely(plen < mem->dma_nents)) { iser_err("page vec too short to hold this SG\n"); iser_data_buf_dump(mem, device->ib_device); iser_dump_page_vec(page_vec); @@ -451,10 +451,10 @@ static int iser_fast_reg_mr(struct iscsi_iser_task *iser_task, ib_update_fast_reg_key(mr, ib_inc_rkey(mr->rkey)); - n = ib_map_mr_sg(mr, mem->sg, mem->size, NULL, SIZE_4K); - if (unlikely(n != mem->size)) { + n = ib_map_mr_sg(mr, mem->sg, mem->dma_nents, NULL, SIZE_4K); + if (unlikely(n != mem->dma_nents)) { iser_err("failed to map sg (%d/%d)\n", - n, mem->size); + n, mem->dma_nents); return n < 0 ? n : -EINVAL; } diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index ea4afdeb06a4ca6106ed44c9f40c0a532ce986bc..bc979a85a505d200f5680907756a53472cfd9bdf 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -1324,9 +1324,11 @@ static int srpt_build_cmd_rsp(struct srpt_rdma_ch *ch, struct srpt_send_ioctx *ioctx, u64 tag, int status) { + struct se_cmd *cmd = &ioctx->cmd; struct srp_rsp *srp_rsp; const u8 *sense_data; int sense_data_len, max_sense_len; + u32 resid = cmd->residual_count; /* * The lowest bit of all SAM-3 status codes is zero (see also @@ -1348,6 +1350,28 @@ static int srpt_build_cmd_rsp(struct srpt_rdma_ch *ch, srp_rsp->tag = tag; srp_rsp->status = status; + if (cmd->se_cmd_flags & SCF_UNDERFLOW_BIT) { + if (cmd->data_direction == DMA_TO_DEVICE) { + /* residual data from an underflow write */ + srp_rsp->flags = SRP_RSP_FLAG_DOUNDER; + srp_rsp->data_out_res_cnt = cpu_to_be32(resid); + } else if (cmd->data_direction == DMA_FROM_DEVICE) { + /* residual data from an underflow read */ + srp_rsp->flags = SRP_RSP_FLAG_DIUNDER; + srp_rsp->data_in_res_cnt = cpu_to_be32(resid); + } + } else if (cmd->se_cmd_flags & SCF_OVERFLOW_BIT) { + if (cmd->data_direction == DMA_TO_DEVICE) { + /* residual data from an overflow write */ + srp_rsp->flags = SRP_RSP_FLAG_DOOVER; + srp_rsp->data_out_res_cnt = cpu_to_be32(resid); + } else if (cmd->data_direction == DMA_FROM_DEVICE) { + /* residual data from an overflow read */ + srp_rsp->flags = SRP_RSP_FLAG_DIOVER; + srp_rsp->data_in_res_cnt = cpu_to_be32(resid); + } + } + if (sense_data_len) { BUILD_BUG_ON(MIN_MAX_RSP_SIZE <= sizeof(*srp_rsp)); max_sense_len = ch->max_ti_iu_len - sizeof(*srp_rsp); diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index f48369d6f3a0f36ef1ab412ac95a2b2b51d55b3d..65ca3e5529491ad9a6ba8dc55d94fd74e0f01365 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -28,13 +28,6 @@ #include #include "input-compat.h" -enum evdev_clock_type { - EV_CLK_REAL = 0, - EV_CLK_MONO, - EV_CLK_BOOT, - EV_CLK_MAX -}; - struct evdev { int open; struct input_handle handle; @@ -56,7 +49,7 @@ struct evdev_client { struct fasync_struct *fasync; struct evdev *evdev; struct list_head node; - unsigned int clk_type; + enum input_clock_type clk_type; bool revoked; unsigned long *evmasks[EV_CNT]; unsigned int bufsize; @@ -152,17 +145,10 @@ static void __evdev_flush_queue(struct evdev_client *client, unsigned int type) static void __evdev_queue_syn_dropped(struct evdev_client *client) { + ktime_t *ev_time = input_get_timestamp(client->evdev->handle.dev); + struct timespec64 ts = ktime_to_timespec64(ev_time[client->clk_type]); struct input_event ev; - ktime_t time; - struct timespec64 ts; - time = client->clk_type == EV_CLK_REAL ? - ktime_get_real() : - client->clk_type == EV_CLK_MONO ? - ktime_get() : - ktime_get_boottime(); - - ts = ktime_to_timespec64(time); ev.input_event_sec = ts.tv_sec; ev.input_event_usec = ts.tv_nsec / NSEC_PER_USEC; ev.type = EV_SYN; @@ -191,18 +177,18 @@ static void evdev_queue_syn_dropped(struct evdev_client *client) static int evdev_set_clk_type(struct evdev_client *client, unsigned int clkid) { unsigned long flags; - unsigned int clk_type; + enum input_clock_type clk_type; switch (clkid) { case CLOCK_REALTIME: - clk_type = EV_CLK_REAL; + clk_type = INPUT_CLK_REAL; break; case CLOCK_MONOTONIC: - clk_type = EV_CLK_MONO; + clk_type = INPUT_CLK_MONO; break; case CLOCK_BOOTTIME: - clk_type = EV_CLK_BOOT; + clk_type = INPUT_CLK_BOOT; break; default: return -EINVAL; @@ -241,13 +227,13 @@ static void __pass_event(struct evdev_client *client, */ client->tail = (client->head - 2) & (client->bufsize - 1); - client->buffer[client->tail].input_event_sec = - event->input_event_sec; - client->buffer[client->tail].input_event_usec = - event->input_event_usec; - client->buffer[client->tail].type = EV_SYN; - client->buffer[client->tail].code = SYN_DROPPED; - client->buffer[client->tail].value = 0; + client->buffer[client->tail] = (struct input_event) { + .input_event_sec = event->input_event_sec, + .input_event_usec = event->input_event_usec, + .type = EV_SYN, + .code = SYN_DROPPED, + .value = 0, + }; client->packet_head = client->tail; } @@ -310,12 +296,7 @@ static void evdev_events(struct input_handle *handle, { struct evdev *evdev = handle->private; struct evdev_client *client; - ktime_t ev_time[EV_CLK_MAX]; - - ev_time[EV_CLK_MONO] = ktime_get(); - ev_time[EV_CLK_REAL] = ktime_mono_to_real(ev_time[EV_CLK_MONO]); - ev_time[EV_CLK_BOOT] = ktime_mono_to_any(ev_time[EV_CLK_MONO], - TK_OFFS_BOOT); + ktime_t *ev_time = input_get_timestamp(handle->dev); rcu_read_lock(); diff --git a/drivers/input/input.c b/drivers/input/input.c index 3304aaaffe87df323c758c0dc645b1f34a3d3fd4..57072d5a6ae047b714fab0cfc09774a361b226c8 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -400,6 +400,13 @@ static void input_handle_event(struct input_dev *dev, if (dev->num_vals >= 2) input_pass_values(dev, dev->vals, dev->num_vals); dev->num_vals = 0; + /* + * Reset the timestamp on flush so we won't end up + * with a stale one. Note we only need to reset the + * monolithic one as we use its presence when deciding + * whether to generate a synthetic timestamp. + */ + dev->timestamp[INPUT_CLK_MONO] = ktime_set(0, 0); } else if (dev->num_vals >= dev->max_vals - 2) { dev->vals[dev->num_vals++] = input_value_sync; input_pass_values(dev, dev->vals, dev->num_vals); @@ -858,16 +865,18 @@ static int input_default_setkeycode(struct input_dev *dev, } } - __clear_bit(*old_keycode, dev->keybit); - __set_bit(ke->keycode, dev->keybit); - - for (i = 0; i < dev->keycodemax; i++) { - if (input_fetch_keycode(dev, i) == *old_keycode) { - __set_bit(*old_keycode, dev->keybit); - break; /* Setting the bit twice is useless, so break */ + if (*old_keycode <= KEY_MAX) { + __clear_bit(*old_keycode, dev->keybit); + for (i = 0; i < dev->keycodemax; i++) { + if (input_fetch_keycode(dev, i) == *old_keycode) { + __set_bit(*old_keycode, dev->keybit); + /* Setting the bit twice is useless, so break */ + break; + } } } + __set_bit(ke->keycode, dev->keybit); return 0; } @@ -923,9 +932,13 @@ int input_set_keycode(struct input_dev *dev, * Simulate keyup event if keycode is not present * in the keymap anymore */ - if (test_bit(EV_KEY, dev->evbit) && - !is_event_supported(old_keycode, dev->keybit, KEY_MAX) && - __test_and_clear_bit(old_keycode, dev->key)) { + if (old_keycode > KEY_MAX) { + dev_warn(dev->dev.parent ?: &dev->dev, + "%s: got too big old keycode %#x\n", + __func__, old_keycode); + } else if (test_bit(EV_KEY, dev->evbit) && + !is_event_supported(old_keycode, dev->keybit, KEY_MAX) && + __test_and_clear_bit(old_keycode, dev->key)) { struct input_value vals[] = { { EV_KEY, old_keycode, 0 }, input_value_sync @@ -1898,6 +1911,46 @@ void input_free_device(struct input_dev *dev) } EXPORT_SYMBOL(input_free_device); +/** + * input_set_timestamp - set timestamp for input events + * @dev: input device to set timestamp for + * @timestamp: the time at which the event has occurred + * in CLOCK_MONOTONIC + * + * This function is intended to provide to the input system a more + * accurate time of when an event actually occurred. The driver should + * call this function as soon as a timestamp is acquired ensuring + * clock conversions in input_set_timestamp are done correctly. + * + * The system entering suspend state between timestamp acquisition and + * calling input_set_timestamp can result in inaccurate conversions. + */ +void input_set_timestamp(struct input_dev *dev, ktime_t timestamp) +{ + dev->timestamp[INPUT_CLK_MONO] = timestamp; + dev->timestamp[INPUT_CLK_REAL] = ktime_mono_to_real(timestamp); + dev->timestamp[INPUT_CLK_BOOT] = ktime_mono_to_any(timestamp, + TK_OFFS_BOOT); +} +EXPORT_SYMBOL(input_set_timestamp); + +/** + * input_get_timestamp - get timestamp for input events + * @dev: input device to get timestamp from + * + * A valid timestamp is a timestamp of non-zero value. + */ +ktime_t *input_get_timestamp(struct input_dev *dev) +{ + const ktime_t invalid_timestamp = ktime_set(0, 0); + + if (!ktime_compare(dev->timestamp[INPUT_CLK_MONO], invalid_timestamp)) + input_set_timestamp(dev, ktime_get()); + + return dev->timestamp; +} +EXPORT_SYMBOL(input_get_timestamp); + /** * input_set_capability - mark device as capable of a certain event * @dev: device that is capable of emitting or accepting event diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index 8567ee47761e111b31f7c8270bdc6414de6668a1..ae3b04557074010bc708c48db877c486f9d1cd0a 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -100,7 +100,7 @@ static int __init ske_keypad_chip_init(struct ske_keypad *keypad) while ((readl(keypad->reg_base + SKE_RIS) != 0x00000000) && timeout--) cpu_relax(); - if (!timeout) + if (timeout == -1) return -EINVAL; /* diff --git a/drivers/input/keycombo.c b/drivers/input/keycombo.c index 2fba451b91d594a40c0a7223d457d1e6b346bfbf..bda480a4461669a1a59c23001bcab1dbc75ca56f 100644 --- a/drivers/input/keycombo.c +++ b/drivers/input/keycombo.c @@ -38,8 +38,8 @@ struct keycombo_state { void (*key_down_fn)(void *); void *priv; int key_is_down; - struct wakeup_source combo_held_wake_source; - struct wakeup_source combo_up_wake_source; + struct wakeup_source *combo_held_wake_source; + struct wakeup_source *combo_up_wake_source; }; static void do_key_down(struct work_struct *work) @@ -58,7 +58,7 @@ static void do_key_up(struct work_struct *work) key_up_work); if (state->key_up_fn) state->key_up_fn(state->priv); - __pm_relax(&state->combo_up_wake_source); + __pm_relax(state->combo_up_wake_source); } static void keycombo_event(struct input_handle *handle, unsigned int type, @@ -92,17 +92,17 @@ static void keycombo_event(struct input_handle *handle, unsigned int type, state->key_down--; } if (state->key_down == state->key_down_target && state->key_up == 0) { - __pm_stay_awake(&state->combo_held_wake_source); + __pm_stay_awake(state->combo_held_wake_source); state->key_is_down = 1; if (queue_delayed_work(state->wq, &state->key_down_work, state->delay)) pr_debug("Key down work already queued!"); } else if (state->key_is_down) { if (!cancel_delayed_work(&state->key_down_work)) { - __pm_stay_awake(&state->combo_up_wake_source); + __pm_stay_awake(state->combo_up_wake_source); queue_work(state->wq, &state->key_up_work); } - __pm_relax(&state->combo_held_wake_source); + __pm_relax(state->combo_held_wake_source); state->key_is_down = 0; } done: @@ -213,8 +213,21 @@ static int keycombo_probe(struct platform_device *pdev) state->key_up_fn = pdata->key_up_fn; INIT_WORK(&state->key_up_work, do_key_up); - wakeup_source_init(&state->combo_held_wake_source, "key combo"); - wakeup_source_init(&state->combo_up_wake_source, "key combo up"); + state->combo_held_wake_source = + wakeup_source_register(&pdev->dev, "key combo"); + if (!state->combo_held_wake_source) { + kfree(state); + return -ENOMEM; + } + + state->combo_up_wake_source = + wakeup_source_register(&pdev->dev, "key combo up"); + if (!state->combo_up_wake_source) { + kfree(state); + wakeup_source_unregister(state->combo_held_wake_source); + return -ENOMEM; + } + state->delay = msecs_to_jiffies(pdata->key_down_delay); state->input_handler.event = keycombo_event; @@ -225,6 +238,8 @@ static int keycombo_probe(struct platform_device *pdev) ret = input_register_handler(&state->input_handler); if (ret) { kfree(state); + wakeup_source_unregister(state->combo_up_wake_source); + wakeup_source_unregister(state->combo_held_wake_source); return ret; } platform_set_drvdata(pdev, state); @@ -235,6 +250,8 @@ int keycombo_remove(struct platform_device *pdev) { struct keycombo_state *state = platform_get_drvdata(pdev); input_unregister_handler(&state->input_handler); + wakeup_source_unregister(state->combo_up_wake_source); + wakeup_source_unregister(state->combo_held_wake_source); destroy_workqueue(state->wq); kfree(state); return 0; diff --git a/drivers/input/misc/gpio_input.c b/drivers/input/misc/gpio_input.c index 5875d739c55043dc98145cb3361ea11db87714c7..69c0fe7aaaf477d3bc780304c14e86ba6678b188 100644 --- a/drivers/input/misc/gpio_input.c +++ b/drivers/input/misc/gpio_input.c @@ -302,7 +302,8 @@ int gpio_event_input_func(struct gpio_event_input_devs *input_devs, input_devs->dev[0]->name, (input_devs->count > 1) ? "..." : ""); - ds->ws = wakeup_source_register(wlname); + ds->ws = wakeup_source_register(&input_devs->dev[0]->dev, + wlname); kfree(wlname); if (!ds->ws) { ret = -ENOMEM; diff --git a/drivers/input/misc/gpio_matrix.c b/drivers/input/misc/gpio_matrix.c index 08769dd88f56a99c81178420dbd2ab0933982ba2..e80885df142437b437212f40b4b2aa1b59948ceb 100644 --- a/drivers/input/misc/gpio_matrix.c +++ b/drivers/input/misc/gpio_matrix.c @@ -24,7 +24,7 @@ struct gpio_kp { struct gpio_event_input_devs *input_devs; struct gpio_event_matrix_info *keypad_info; struct hrtimer timer; - struct wakeup_source wake_src; + struct wakeup_source *wake_src; int current_output; unsigned int use_irq:1; unsigned int key_state_changed:1; @@ -214,7 +214,7 @@ static enum hrtimer_restart gpio_keypad_timer_func(struct hrtimer *timer) } for (in = 0; in < mi->ninputs; in++) enable_irq(gpio_to_irq(mi->input_gpios[in])); - __pm_relax(&kp->wake_src); + __pm_relax(kp->wake_src); return HRTIMER_NORESTART; } @@ -241,7 +241,7 @@ static irqreturn_t gpio_keypad_irq_handler(int irq_in, void *dev_id) else gpio_direction_input(mi->output_gpios[i]); } - __pm_stay_awake(&kp->wake_src); + __pm_stay_awake(kp->wake_src); hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL); return IRQ_HANDLED; } @@ -395,7 +395,14 @@ int gpio_event_matrix_func(struct gpio_event_input_devs *input_devs, hrtimer_init(&kp->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); kp->timer.function = gpio_keypad_timer_func; - wakeup_source_init(&kp->wake_src, "gpio_kp"); + + kp->wake_src = + wakeup_source_register(&input_devs->dev[0]->dev, "gpio_kp"); + if (!kp->wake_src) { + err = -ENOMEM; + goto err_wakeup_source_register_failed; + } + err = gpio_keypad_request_irqs(kp); kp->use_irq = err == 0; @@ -405,7 +412,7 @@ int gpio_event_matrix_func(struct gpio_event_input_devs *input_devs, kp->use_irq ? "interrupt" : "polling"); if (kp->use_irq) - __pm_stay_awake(&kp->wake_src); + __pm_stay_awake(kp->wake_src); hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL); return 0; @@ -419,8 +426,9 @@ int gpio_event_matrix_func(struct gpio_event_input_devs *input_devs, free_irq(gpio_to_irq(mi->input_gpios[i]), kp); hrtimer_cancel(&kp->timer); - wakeup_source_trash(&kp->wake_src); - for (i = mi->noutputs - 1; i >= 0; i--) { + wakeup_source_unregister(kp->wake_src); +err_wakeup_source_register_failed: + for (i = mi->ninputs - 1; i >= 0; i--) { err_gpio_direction_input_failed: gpio_free(mi->input_gpios[i]); err_request_input_gpio_failed: diff --git a/drivers/input/misc/keyspan_remote.c b/drivers/input/misc/keyspan_remote.c index a8937ceac66a88a4a9266493eb6db1dadacadb83..af4db135091574e998ba4752753457dfc67d2753 100644 --- a/drivers/input/misc/keyspan_remote.c +++ b/drivers/input/misc/keyspan_remote.c @@ -339,7 +339,8 @@ static int keyspan_setup(struct usb_device* dev) int retval = 0; retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - 0x11, 0x40, 0x5601, 0x0, NULL, 0, 0); + 0x11, 0x40, 0x5601, 0x0, NULL, 0, + USB_CTRL_SET_TIMEOUT); if (retval) { dev_dbg(&dev->dev, "%s - failed to set bit rate due to error: %d\n", __func__, retval); @@ -347,7 +348,8 @@ static int keyspan_setup(struct usb_device* dev) } retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - 0x44, 0x40, 0x0, 0x0, NULL, 0, 0); + 0x44, 0x40, 0x0, 0x0, NULL, 0, + USB_CTRL_SET_TIMEOUT); if (retval) { dev_dbg(&dev->dev, "%s - failed to set resume sensitivity due to error: %d\n", __func__, retval); @@ -355,7 +357,8 @@ static int keyspan_setup(struct usb_device* dev) } retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - 0x22, 0x40, 0x0, 0x0, NULL, 0, 0); + 0x22, 0x40, 0x0, 0x0, NULL, 0, + USB_CTRL_SET_TIMEOUT); if (retval) { dev_dbg(&dev->dev, "%s - failed to turn receive on due to error: %d\n", __func__, retval); diff --git a/drivers/input/misc/pm8xxx-vibrator.c b/drivers/input/misc/pm8xxx-vibrator.c index 7dd1c1fbe42a8d1079995f7ff1acb36974864fe9..27b3db154a33f542d82c9f8f61b1971da1d47e7e 100644 --- a/drivers/input/misc/pm8xxx-vibrator.c +++ b/drivers/input/misc/pm8xxx-vibrator.c @@ -98,7 +98,7 @@ static int pm8xxx_vib_set(struct pm8xxx_vib *vib, bool on) if (regs->enable_mask) rc = regmap_update_bits(vib->regmap, regs->enable_addr, - on ? regs->enable_mask : 0, val); + regs->enable_mask, on ? ~0 : 0); return rc; } diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 83d1499fe02152cfa75e8d097f6432d50632a6cc..e746920872a4700b54a1db99c496400db627f0c6 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -87,12 +87,16 @@ static int uinput_dev_event(struct input_dev *dev, struct uinput_device *udev = input_get_drvdata(dev); struct timespec64 ts; - udev->buff[udev->head].type = type; - udev->buff[udev->head].code = code; - udev->buff[udev->head].value = value; ktime_get_ts64(&ts); - udev->buff[udev->head].input_event_sec = ts.tv_sec; - udev->buff[udev->head].input_event_usec = ts.tv_nsec / NSEC_PER_USEC; + + udev->buff[udev->head] = (struct input_event) { + .input_event_sec = ts.tv_sec, + .input_event_usec = ts.tv_nsec / NSEC_PER_USEC, + .type = type, + .code = code, + .value = value, + }; + udev->head = (udev->head + 1) % UINPUT_BUFFER_SIZE; wake_up_interruptible(&udev->waitq); diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index e8d1134943c4fb155e3246d2c06d236fdabe7a26..f47e3fca403daeb76930188d02c119e1911925f8 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -149,7 +149,6 @@ static const char * const topbuttonpad_pnp_ids[] = { "LEN0042", /* Yoga */ "LEN0045", "LEN0047", - "LEN0049", "LEN2000", /* S540 */ "LEN2001", /* Edge E431 */ "LEN2002", /* Edge E531 */ @@ -169,9 +168,11 @@ static const char * const smbus_pnp_ids[] = { /* all of the topbuttonpad_pnp_ids are valid, we just add some extras */ "LEN0048", /* X1 Carbon 3 */ "LEN0046", /* X250 */ + "LEN0049", /* Yoga 11e */ "LEN004a", /* W541 */ "LEN005b", /* P50 */ "LEN005e", /* T560 */ + "LEN006c", /* T470s */ "LEN0071", /* T480 */ "LEN0072", /* X1 Carbon Gen 5 (2017) - Elan/ALPS trackpoint */ "LEN0073", /* X1 Carbon G5 (Elantech) */ @@ -182,6 +183,7 @@ static const char * const smbus_pnp_ids[] = { "LEN0097", /* X280 -> ALPS trackpoint */ "LEN009b", /* T580 */ "LEN200f", /* T450s */ + "LEN2044", /* L470 */ "LEN2054", /* E480 */ "LEN2055", /* E580 */ "SYN3052", /* HP EliteBook 840 G4 */ diff --git a/drivers/input/rmi4/rmi_smbus.c b/drivers/input/rmi4/rmi_smbus.c index 4b2466cf2fb1c9181ce6038750a9586f10ee983a..b6ccf39c6a7bb46bc67bf086952a799e27f4f930 100644 --- a/drivers/input/rmi4/rmi_smbus.c +++ b/drivers/input/rmi4/rmi_smbus.c @@ -166,6 +166,7 @@ static int rmi_smb_write_block(struct rmi_transport_dev *xport, u16 rmiaddr, /* prepare to write next block of bytes */ cur_len -= SMB_MAX_COUNT; databuff += SMB_MAX_COUNT; + rmiaddr += SMB_MAX_COUNT; } exit: mutex_unlock(&rmi_smb->page_mutex); @@ -217,6 +218,7 @@ static int rmi_smb_read_block(struct rmi_transport_dev *xport, u16 rmiaddr, /* prepare to read next block of bytes */ cur_len -= SMB_MAX_COUNT; databuff += SMB_MAX_COUNT; + rmiaddr += SMB_MAX_COUNT; } retval = 0; diff --git a/drivers/input/tablet/aiptek.c b/drivers/input/tablet/aiptek.c index c82cd5079d0e6207cc22a6ed5f2155f2e6d156e2..dc2ad1cc8fe1d4dddb105c501e3795e3d4aa8462 100644 --- a/drivers/input/tablet/aiptek.c +++ b/drivers/input/tablet/aiptek.c @@ -1815,14 +1815,14 @@ aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id) input_set_abs_params(inputdev, ABS_WHEEL, AIPTEK_WHEEL_MIN, AIPTEK_WHEEL_MAX - 1, 0, 0); /* Verify that a device really has an endpoint */ - if (intf->altsetting[0].desc.bNumEndpoints < 1) { + if (intf->cur_altsetting->desc.bNumEndpoints < 1) { dev_err(&intf->dev, "interface has %d endpoints, but must have minimum 1\n", - intf->altsetting[0].desc.bNumEndpoints); + intf->cur_altsetting->desc.bNumEndpoints); err = -EINVAL; goto fail3; } - endpoint = &intf->altsetting[0].endpoint[0].desc; + endpoint = &intf->cur_altsetting->endpoint[0].desc; /* Go set up our URB, which is called when the tablet receives * input. diff --git a/drivers/input/tablet/gtco.c b/drivers/input/tablet/gtco.c index 35031228a6d076cf0dd91dab411f049ec679df6b..799c94dda651aacf7acc76e6e38995d48c32ffb4 100644 --- a/drivers/input/tablet/gtco.c +++ b/drivers/input/tablet/gtco.c @@ -875,18 +875,14 @@ static int gtco_probe(struct usb_interface *usbinterface, } /* Sanity check that a device has an endpoint */ - if (usbinterface->altsetting[0].desc.bNumEndpoints < 1) { + if (usbinterface->cur_altsetting->desc.bNumEndpoints < 1) { dev_err(&usbinterface->dev, "Invalid number of endpoints\n"); error = -EINVAL; goto err_free_urb; } - /* - * The endpoint is always altsetting 0, we know this since we know - * this device only has one interrupt endpoint - */ - endpoint = &usbinterface->altsetting[0].endpoint[0].desc; + endpoint = &usbinterface->cur_altsetting->endpoint[0].desc; /* Some debug */ dev_dbg(&usbinterface->dev, "gtco # interfaces: %d\n", usbinterface->num_altsetting); @@ -973,7 +969,7 @@ static int gtco_probe(struct usb_interface *usbinterface, input_dev->dev.parent = &usbinterface->dev; /* Setup the URB, it will be posted later on open of input device */ - endpoint = &usbinterface->altsetting[0].endpoint[0].desc; + endpoint = &usbinterface->cur_altsetting->endpoint[0].desc; usb_fill_int_urb(gtco->urbinfo, udev, diff --git a/drivers/input/tablet/pegasus_notetaker.c b/drivers/input/tablet/pegasus_notetaker.c index ffd03cfe3131a4a977c6741c67e3c326ca37e99c..570cdaef3558409ee7e25d133e79b133b3e5ff0d 100644 --- a/drivers/input/tablet/pegasus_notetaker.c +++ b/drivers/input/tablet/pegasus_notetaker.c @@ -274,7 +274,7 @@ static int pegasus_probe(struct usb_interface *intf, return -ENODEV; /* Sanity check that the device has an endpoint */ - if (intf->altsetting[0].desc.bNumEndpoints < 1) { + if (intf->cur_altsetting->desc.bNumEndpoints < 1) { dev_err(&intf->dev, "Invalid number of endpoints\n"); return -EINVAL; } diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 1e18ca0d1b4e1ed5cb9492b30b5877c9ec0bb0dc..3fdaa644a82c14e85002eaaec4466a2e9423b73f 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -968,6 +968,7 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, { const struct edt_i2c_chip_data *chip_data; struct edt_ft5x06_ts_data *tsdata; + u8 buf[2] = { 0xfc, 0x00 }; struct input_dev *input; unsigned long irq_flags; int error; @@ -1037,6 +1038,12 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, return error; } + /* + * Dummy read access. EP0700MLP1 returns bogus data on the first + * register read access and ignores writes. + */ + edt_ft5x06_ts_readwrite(tsdata->client, 2, buf, 2, buf); + edt_ft5x06_ts_set_regs(tsdata); edt_ft5x06_ts_get_defaults(&client->dev, tsdata); edt_ft5x06_ts_get_parameters(tsdata); diff --git a/drivers/input/touchscreen/nt36xxx/nt36xxx.c b/drivers/input/touchscreen/nt36xxx/nt36xxx.c index de496cc1e4b8006bd7de3decf97b7c6c4aaa7d76..0ade7d19d7d336a04a36493b8296651698b0c7ad 100644 --- a/drivers/input/touchscreen/nt36xxx/nt36xxx.c +++ b/drivers/input/touchscreen/nt36xxx/nt36xxx.c @@ -1754,6 +1754,12 @@ static int32_t nvt_ts_suspend(struct device *dev) buf[0] = EVENT_MAP_HOST_CMD; buf[1] = 0x11; CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 2); + + nvt_set_page(I2C_FW_Address, 0x11a50); + buf[0] = 0x11a50 & 0xff; + buf[1] = 0x11; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 2); + #endif // WAKEUP_GESTURE mutex_unlock(&ts->lock); diff --git a/drivers/input/touchscreen/st/fts.c b/drivers/input/touchscreen/st/fts.c index ea87d1f7ed61d324f2010a2927cb41ad198ce9fc..e1dd7887c12040f37a848ad29116e3e362c82304 100644 --- a/drivers/input/touchscreen/st/fts.c +++ b/drivers/input/touchscreen/st/fts.c @@ -1596,7 +1596,7 @@ static ssize_t fts_gesture_mask_show(struct device *dev, if (mask[0] == 0) { res = ERROR_OP_NOT_ALLOW; logError(1, "%s %s:Call before echo enable/disable xx xx >", - tag), __func__; + tag, __func__); logError(1, "%s %s: gesture_mask with a correct number of ", tag, __func__); logError(1, "parameters! ERROR %08X\n", res); @@ -3263,7 +3263,7 @@ static void fts_event_handler(struct work_struct *work) * read all the FIFO and parsing events */ - __pm_wakeup_event(&info->wakeup_source, HZ); + __pm_wakeup_event(info->wakeup_source, HZ); regAdd = FIFO_CMD_READONE; for (count = 0; count < FIFO_DEPTH; count++) { @@ -4115,7 +4115,7 @@ static void fts_resume_work(struct work_struct *work) info = container_of(work, struct fts_ts_info, resume_work); - __pm_wakeup_event(&info->wakeup_source, HZ); + __pm_wakeup_event(info->wakeup_source, HZ); fts_chip_power_switch(info, true); @@ -4145,7 +4145,7 @@ static void fts_suspend_work(struct work_struct *work) info = container_of(work, struct fts_ts_info, suspend_work); - __pm_wakeup_event(&info->wakeup_source, HZ); + __pm_wakeup_event(info->wakeup_source, HZ); info->resume_bit = 0; @@ -4238,7 +4238,7 @@ static int fts_fb_state_chg_callback(struct notifier_block *nb, if (info->aoi_wake_on_suspend) { info->sensor_sleep = true; - __pm_stay_awake(&info->wakeup_source); + __pm_stay_awake(info->wakeup_source); } else { queue_work(info->event_wq, &info->suspend_work); } @@ -4246,7 +4246,7 @@ static int fts_fb_state_chg_callback(struct notifier_block *nb, case DRM_PANEL_BLANK_UNBLANK: if (info->aoi_wake_on_suspend) - __pm_relax(&info->wakeup_source); + __pm_relax(info->wakeup_source); if (!info->sensor_sleep) break; @@ -4710,7 +4710,8 @@ static int fts_probe_internal(struct i2c_client *client, INIT_DELAYED_WORK(&info->fwu_work, fts_fw_update_auto); logError(0, "%s SET Event Handler:\n", tag); - wakeup_source_init(&info->wakeup_source, "fts_tp"); + info->wakeup_source = wakeup_source_register(&client->dev, + dev_name(&client->dev)); info->event_wq = alloc_workqueue("fts-event-queue", WQ_UNBOUND|WQ_HIGHPRI|WQ_CPU_INTENSIVE, 1); if (!info->event_wq) { @@ -4963,7 +4964,7 @@ static int fts_probe_internal(struct i2c_client *client, ProbeErrorExit_4: destroy_workqueue(info->fwu_workqueue); - wakeup_source_trash(&info->wakeup_source); + wakeup_source_unregister(info->wakeup_source); ProbeErrorExit_3: if (info->ts_pinctrl) { @@ -5056,7 +5057,7 @@ static int fts_remove(struct i2c_client *client) /* Remove the work thread */ destroy_workqueue(info->event_wq); /* wake_lock_destroy(&info->wakelock); */ - wakeup_source_trash(&info->wakeup_source); + wakeup_source_unregister(info->wakeup_source); destroy_workqueue(info->fwu_workqueue); if (info->ts_pinctrl) { diff --git a/drivers/input/touchscreen/st/fts.h b/drivers/input/touchscreen/st/fts.h index 317007b8891bc3d7e5f4490884b8a87a11f1e7d3..b5ab38247e16e29888d5de735a4f3fc72c6cf462 100644 --- a/drivers/input/touchscreen/st/fts.h +++ b/drivers/input/touchscreen/st/fts.h @@ -295,7 +295,7 @@ struct fts_ts_info { struct notifier_block notifier; bool sensor_sleep; - struct wakeup_source wakeup_source; + struct wakeup_source *wakeup_source; /* input lock */ struct mutex input_report_mutex; diff --git a/drivers/input/touchscreen/st/fts_lib/ftsTime.c b/drivers/input/touchscreen/st/fts_lib/ftsTime.c index c908a198130b77adee866e410b086135b33c5770..07d1bf5dba8bb8e81379ebe19ddec7069c532cf5 100644 --- a/drivers/input/touchscreen/st/fts_lib/ftsTime.c +++ b/drivers/input/touchscreen/st/fts_lib/ftsTime.c @@ -57,6 +57,7 @@ #include #include #include +#include //#include #include "ftsCrossCompile.h" @@ -65,12 +66,12 @@ void startStopWatch(struct StopWatch *w) { - w->start = current_kernel_time(); + ktime_get_ts(&w->start); } void stopStopWatch(struct StopWatch *w) { - w->end = current_kernel_time(); + ktime_get_ts(&w->end); } int elapsedMillisecond(struct StopWatch *w) diff --git a/drivers/input/touchscreen/sun4i-ts.c b/drivers/input/touchscreen/sun4i-ts.c index d2e14d9e59753288168d50de4d8e2e880ed40ce3..ab44eb0352d060e7f7953924b069dd20fad6144f 100644 --- a/drivers/input/touchscreen/sun4i-ts.c +++ b/drivers/input/touchscreen/sun4i-ts.c @@ -246,6 +246,7 @@ static int sun4i_ts_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct device *hwmon; + struct thermal_zone_device *thermal; int error; u32 reg; bool ts_attached; @@ -365,7 +366,10 @@ static int sun4i_ts_probe(struct platform_device *pdev) if (IS_ERR(hwmon)) return PTR_ERR(hwmon); - devm_thermal_zone_of_sensor_register(ts->dev, 0, ts, &sun4i_ts_tz_ops); + thermal = devm_thermal_zone_of_sensor_register(ts->dev, 0, ts, + &sun4i_ts_tz_ops); + if (IS_ERR(thermal)) + return PTR_ERR(thermal); writel(TEMP_IRQ_EN(1), ts->base + TP_INT_FIFOC); diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index 894843a7ec7bad112e7bcb85a0c9da6206b9237c..caa3aca2ea541c40ac81d6d586b42935e2d86b61 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -657,7 +657,7 @@ static int sur40_probe(struct usb_interface *interface, int error; /* Check if we really have the right interface. */ - iface_desc = &interface->altsetting[0]; + iface_desc = interface->cur_altsetting; if (iface_desc->desc.bInterfaceClass != 0xFF) return -ENODEV; diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 9991386fb70000f02e4ab0b345ab5c74fcfed2f3..0783f44e9afe55f0b6e03d85f2e9ebf00f5ef81f 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -2153,6 +2153,8 @@ static int attach_device(struct device *dev, */ domain_flush_tlb_pde(domain); + domain_flush_complete(domain); + return ret; } @@ -3707,7 +3709,20 @@ static void set_remap_table_entry(struct amd_iommu *iommu, u16 devid, iommu_flush_dte(iommu, devid); } -static struct irq_remap_table *alloc_irq_table(u16 devid) +static int set_remap_table_entry_alias(struct pci_dev *pdev, u16 alias, + void *data) +{ + struct irq_remap_table *table = data; + + irq_lookup_table[alias] = table; + set_dte_irq_entry(alias, table); + + iommu_flush_dte(amd_iommu_rlookup_table[alias], alias); + + return 0; +} + +static struct irq_remap_table *alloc_irq_table(u16 devid, struct pci_dev *pdev) { struct irq_remap_table *table = NULL; struct irq_remap_table *new_table = NULL; @@ -3753,7 +3768,12 @@ static struct irq_remap_table *alloc_irq_table(u16 devid) table = new_table; new_table = NULL; - set_remap_table_entry(iommu, devid, table); + if (pdev) + pci_for_each_dma_alias(pdev, set_remap_table_entry_alias, + table); + else + set_remap_table_entry(iommu, devid, table); + if (devid != alias) set_remap_table_entry(iommu, alias, table); @@ -3770,7 +3790,8 @@ static struct irq_remap_table *alloc_irq_table(u16 devid) return table; } -static int alloc_irq_index(u16 devid, int count, bool align) +static int alloc_irq_index(u16 devid, int count, bool align, + struct pci_dev *pdev) { struct irq_remap_table *table; int index, c, alignment = 1; @@ -3780,7 +3801,7 @@ static int alloc_irq_index(u16 devid, int count, bool align) if (!iommu) return -ENODEV; - table = alloc_irq_table(devid); + table = alloc_irq_table(devid, pdev); if (!table) return -ENODEV; @@ -4213,7 +4234,7 @@ static int irq_remapping_alloc(struct irq_domain *domain, unsigned int virq, struct irq_remap_table *table; struct amd_iommu *iommu; - table = alloc_irq_table(devid); + table = alloc_irq_table(devid, NULL); if (table) { if (!table->min_index) { /* @@ -4230,11 +4251,15 @@ static int irq_remapping_alloc(struct irq_domain *domain, unsigned int virq, } else { index = -ENOMEM; } - } else { + } else if (info->type == X86_IRQ_ALLOC_TYPE_MSI || + info->type == X86_IRQ_ALLOC_TYPE_MSIX) { bool align = (info->type == X86_IRQ_ALLOC_TYPE_MSI); - index = alloc_irq_index(devid, nr_irqs, align); + index = alloc_irq_index(devid, nr_irqs, align, info->msi_dev); + } else { + index = alloc_irq_index(devid, nr_irqs, false, NULL); } + if (index < 0) { pr_warn("Failed to allocate IRTE\n"); ret = index; diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index 1e9a5da562f0d7dd28b2623ca3ec2e6e3900811c..465f28a7844c227f4f6f28243ab7ef5d66d02fa1 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c @@ -422,6 +422,9 @@ static void iommu_enable(struct amd_iommu *iommu) static void iommu_disable(struct amd_iommu *iommu) { + if (!iommu->mmio_base) + return; + /* Disable command buffer */ iommu_feature_disable(iommu, CONTROL_CMDBUF_EN); diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c index 14946a4358311ea74a7ebe177a50e2b5ef463cf1..c8660530daa071afc01492f566e31c903d168915 100644 --- a/drivers/iommu/arm-smmu-v3.c +++ b/drivers/iommu/arm-smmu-v3.c @@ -809,6 +809,7 @@ static int arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent) cmd[1] |= FIELD_PREP(CMDQ_CFGI_1_RANGE, 31); break; case CMDQ_OP_TLBI_NH_VA: + cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_VMID, ent->tlbi.vmid); cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_ASID, ent->tlbi.asid); cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_LEAF, ent->tlbi.leaf); cmd[1] |= ent->tlbi.addr & CMDQ_TLBI_1_VA_MASK; @@ -1183,7 +1184,8 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_device *smmu, u32 sid, } arm_smmu_sync_ste_for_sid(smmu, sid); - dst[0] = cpu_to_le64(val); + /* See comment in arm_smmu_write_ctx_desc() */ + WRITE_ONCE(dst[0], cpu_to_le64(val)); arm_smmu_sync_ste_for_sid(smmu, sid); /* It's likely that we'll want to use the new STE soon */ diff --git a/drivers/iommu/dma-mapping-fast.c b/drivers/iommu/dma-mapping-fast.c index b1e48ba50040aaf88b86448fd098f3ab7cf5558f..c3f1d26fb1e7c7eff64ca937ea0f6fd6fb7f0f84 100644 --- a/drivers/iommu/dma-mapping-fast.c +++ b/drivers/iommu/dma-mapping-fast.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. */ #include @@ -117,197 +117,69 @@ static struct dma_fast_smmu_mapping *dev_get_mapping(struct device *dev) return domain->iova_cookie; } -/* - * Checks if the allocated range (ending at @end) covered the upcoming - * stale bit. We don't need to know exactly where the range starts since - * we already know where the candidate search range started. If, starting - * from the beginning of the candidate search range, we had to step over - * (or landed directly on top of) the upcoming stale bit, then we return - * true. - * - * Due to wrapping, there are two scenarios we'll need to check: (1) if the - * range [search_start, upcoming_stale] spans 0 (i.e. search_start > - * upcoming_stale), and, (2) if the range: [search_start, upcoming_stale] - * does *not* span 0 (i.e. search_start <= upcoming_stale). And for each - * of those two scenarios we need to handle three cases: (1) the bit was - * found before wrapping or - */ -static bool __bit_covered_stale(unsigned long upcoming_stale, - unsigned long search_start, - unsigned long end) -{ - if (search_start > upcoming_stale) { - if (end >= search_start) { - /* - * We started searching above upcoming_stale and we - * didn't wrap, so we couldn't have crossed - * upcoming_stale. - */ - return false; - } - /* - * We wrapped. Did we cross (or land on top of) - * upcoming_stale? - */ - return end >= upcoming_stale; - } - - if (search_start <= upcoming_stale) { - if (end >= search_start) { - /* - * We didn't wrap. Did we cross (or land on top - * of) upcoming_stale? - */ - return end >= upcoming_stale; - } - /* - * We wrapped. So we must have crossed upcoming_stale - * (since we started searching below it). - */ - return true; - } - - /* we should have covered all logical combinations... */ - WARN_ON(1); - return true; -} - static dma_addr_t __fast_smmu_alloc_iova(struct dma_fast_smmu_mapping *mapping, unsigned long attrs, size_t size) { - unsigned long bit, prev_search_start, nbits = size >> FAST_PAGE_SHIFT; + unsigned long bit, nbits = size >> FAST_PAGE_SHIFT; unsigned long align = (1 << get_order(size)) - 1; - bit = bitmap_find_next_zero_area( - mapping->bitmap, mapping->num_4k_pages, mapping->next_start, - nbits, align); + bit = bitmap_find_next_zero_area(mapping->clean_bitmap, + mapping->num_4k_pages, + mapping->next_start, nbits, align); if (unlikely(bit > mapping->num_4k_pages)) { /* try wrapping */ bit = bitmap_find_next_zero_area( - mapping->bitmap, mapping->num_4k_pages, 0, nbits, + mapping->clean_bitmap, mapping->num_4k_pages, 0, nbits, align); - if (unlikely(bit > mapping->num_4k_pages)) - return DMA_ERROR_CODE; + if (unlikely(bit > mapping->num_4k_pages)) { + /* + * If we just re-allocated a VA whose TLB hasn't been + * invalidated since it was last used and unmapped, we + * need to invalidate it here. We actually invalidate + * the entire TLB so that we don't have to invalidate + * the TLB again until we wrap back around. + */ + if (mapping->have_stale_tlbs) { + bool skip_sync = (attrs & + DMA_ATTR_SKIP_CPU_SYNC); + struct iommu_domain_geometry *geometry = + &(mapping->domain->geometry); + + iommu_tlbiall(mapping->domain); + bitmap_copy(mapping->clean_bitmap, + mapping->bitmap, + mapping->num_4k_pages); + mapping->have_stale_tlbs = false; + av8l_fast_clear_stale_ptes(mapping->pgtbl_ops, + geometry->aperture_start, + mapping->base, + mapping->base + + mapping->size - 1, + skip_sync); + bit = bitmap_find_next_zero_area( + mapping->clean_bitmap, + mapping->num_4k_pages, + 0, nbits, + align); + if (unlikely(bit > mapping->num_4k_pages)) + return DMA_ERROR_CODE; + + } else { + return DMA_ERROR_CODE; + } + } } bitmap_set(mapping->bitmap, bit, nbits); - prev_search_start = mapping->next_start; + bitmap_set(mapping->clean_bitmap, bit, nbits); mapping->next_start = bit + nbits; if (unlikely(mapping->next_start >= mapping->num_4k_pages)) mapping->next_start = 0; - /* - * If we just re-allocated a VA whose TLB hasn't been invalidated - * since it was last used and unmapped, we need to invalidate it - * here. We actually invalidate the entire TLB so that we don't - * have to invalidate the TLB again until we wrap back around. - */ - if (mapping->have_stale_tlbs && - __bit_covered_stale(mapping->upcoming_stale_bit, - prev_search_start, - bit + nbits - 1)) { - bool skip_sync = (attrs & DMA_ATTR_SKIP_CPU_SYNC); - - iommu_tlbiall(mapping->domain); - mapping->have_stale_tlbs = false; - av8l_fast_clear_stale_ptes(mapping->pgtbl_ops, - mapping->domain->geometry.aperture_start, - mapping->base, - mapping->base + mapping->size - 1, - skip_sync); - } - return (bit << FAST_PAGE_SHIFT) + mapping->base; } -/* - * Checks whether the candidate bit will be allocated sooner than the - * current upcoming stale bit. We can say candidate will be upcoming - * sooner than the current upcoming stale bit if it lies between the - * starting bit of the next search range and the upcoming stale bit - * (allowing for wrap-around). - * - * Stated differently, we're checking the relative ordering of three - * unsigned numbers. So we need to check all 6 (i.e. 3!) permutations, - * namely: - * - * 0 |---A---B---C---| TOP (Case 1) - * 0 |---A---C---B---| TOP (Case 2) - * 0 |---B---A---C---| TOP (Case 3) - * 0 |---B---C---A---| TOP (Case 4) - * 0 |---C---A---B---| TOP (Case 5) - * 0 |---C---B---A---| TOP (Case 6) - * - * Note that since we're allowing numbers to wrap, the following three - * scenarios are all equivalent for Case 1: - * - * 0 |---A---B---C---| TOP - * 0 |---C---A---B---| TOP (C has wrapped. This is Case 5.) - * 0 |---B---C---A---| TOP (C and B have wrapped. This is Case 4.) - * - * In any of these cases, if we start searching from A, we will find B - * before we find C. - * - * We can also find two equivalent cases for Case 2: - * - * 0 |---A---C---B---| TOP - * 0 |---B---A---C---| TOP (B has wrapped. This is Case 3.) - * 0 |---C---B---A---| TOP (B and C have wrapped. This is Case 6.) - * - * In any of these cases, if we start searching from A, we will find C - * before we find B. - */ -static bool __bit_is_sooner(unsigned long candidate, - struct dma_fast_smmu_mapping *mapping) -{ - unsigned long A = mapping->next_start; - unsigned long B = candidate; - unsigned long C = mapping->upcoming_stale_bit; - - if ((A < B && B < C) || /* Case 1 */ - (C < A && A < B) || /* Case 5 */ - (B < C && C < A)) /* Case 4 */ - return true; - - if ((A < C && C < B) || /* Case 2 */ - (B < A && A < C) || /* Case 3 */ - (C < B && B < A)) /* Case 6 */ - return false; - - /* - * For simplicity, we've been ignoring the possibility of any of - * our three numbers being equal. Handle those cases here (they - * shouldn't happen very often, (I think?)). - */ - - /* - * If candidate is the next bit to be searched then it's definitely - * sooner. - */ - if (A == B) - return true; - - /* - * If candidate is the next upcoming stale bit we'll return false - * to avoid doing `upcoming = candidate' in the caller (which would - * be useless since they're already equal) - */ - if (B == C) - return false; - - /* - * If next start is the upcoming stale bit then candidate can't - * possibly be sooner. The "soonest" bit is already selected. - */ - if (A == C) - return false; - - /* We should have covered all logical combinations. */ - WARN(1, "Well, that's awkward. A=%ld, B=%ld, C=%ld\n", A, B, C); - return true; -} - #ifdef CONFIG_ARM64 static int __init atomic_pool_init(void) { @@ -381,12 +253,8 @@ static void __fast_smmu_free_iova(struct dma_fast_smmu_mapping *mapping, /* * We don't invalidate TLBs on unmap. We invalidate TLBs on map * when we're about to re-allocate a VA that was previously - * unmapped but hasn't yet been invalidated. So we need to keep - * track of which bit is the closest to being re-allocated here. + * unmapped but hasn't yet been invalidated. */ - if (__bit_is_sooner(start_bit, mapping)) - mapping->upcoming_stale_bit = start_bit; - bitmap_clear(mapping->bitmap, start_bit, nbits); mapping->have_stale_tlbs = true; } @@ -1107,6 +975,14 @@ static struct dma_fast_smmu_mapping *__fast_smmu_create_mapping_sized( if (!fast->bitmap) goto err2; + fast->clean_bitmap = kzalloc(fast->bitmap_size, GFP_KERNEL | + __GFP_NOWARN | __GFP_NORETRY); + if (!fast->clean_bitmap) + fast->clean_bitmap = vzalloc(fast->bitmap_size); + + if (!fast->clean_bitmap) + goto err3; + spin_lock_init(&fast->lock); fast->iovad = kzalloc(sizeof(*fast->iovad), GFP_KERNEL); @@ -1118,6 +994,8 @@ static struct dma_fast_smmu_mapping *__fast_smmu_create_mapping_sized( return fast; err_free_bitmap: + kvfree(fast->clean_bitmap); +err3: kvfree(fast->bitmap); err2: kfree(fast); @@ -1184,6 +1062,9 @@ void fast_smmu_put_dma_cookie(struct iommu_domain *domain) if (fast->bitmap) kvfree(fast->bitmap); + if (fast->clean_bitmap) + kvfree(fast->clean_bitmap); + kfree(fast); domain->iova_cookie = NULL; } diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c index 7f9824b0609e79aef47cc26a8df5f48416976043..72994d67bc5b96838e5598c644d1b14121d8e652 100644 --- a/drivers/iommu/dmar.c +++ b/drivers/iommu/dmar.c @@ -1345,7 +1345,6 @@ void qi_flush_dev_iotlb(struct intel_iommu *iommu, u16 sid, u16 pfsid, struct qi_desc desc; if (mask) { - WARN_ON_ONCE(addr & ((1ULL << (VTD_PAGE_SHIFT + mask)) - 1)); addr |= (1ULL << (VTD_PAGE_SHIFT + mask - 1)) - 1; desc.high = QI_DEV_IOTLB_ADDR(addr) | QI_DEV_IOTLB_SIZE; } else diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index b9af2419006f8341563551d2f32c1226c081b249..9df3b844122742af40e389ec9179ecf8a1eb6834 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -387,7 +387,6 @@ struct dmar_rmrr_unit { u64 end_address; /* reserved end address */ struct dmar_dev_scope *devices; /* target devices */ int devices_cnt; /* target device count */ - struct iommu_resv_region *resv; /* reserved region handle */ }; struct dmar_atsr_unit { @@ -3387,9 +3386,12 @@ static int __init init_dmars(void) iommu_identity_mapping |= IDENTMAP_ALL; #ifdef CONFIG_INTEL_IOMMU_BROKEN_GFX_WA - iommu_identity_mapping |= IDENTMAP_GFX; + dmar_map_gfx = 0; #endif + if (!dmar_map_gfx) + iommu_identity_mapping |= IDENTMAP_GFX; + check_tylersburg_isoch(); if (iommu_identity_mapping) { @@ -4182,7 +4184,6 @@ static inline void init_iommu_pm_ops(void) {} int __init dmar_parse_one_rmrr(struct acpi_dmar_header *header, void *arg) { struct acpi_dmar_reserved_memory *rmrr; - int prot = DMA_PTE_READ|DMA_PTE_WRITE; struct dmar_rmrr_unit *rmrru; size_t length; @@ -4196,22 +4197,16 @@ int __init dmar_parse_one_rmrr(struct acpi_dmar_header *header, void *arg) rmrru->end_address = rmrr->end_address; length = rmrr->end_address - rmrr->base_address + 1; - rmrru->resv = iommu_alloc_resv_region(rmrr->base_address, length, prot, - IOMMU_RESV_DIRECT); - if (!rmrru->resv) - goto free_rmrru; rmrru->devices = dmar_alloc_dev_scope((void *)(rmrr + 1), ((void *)rmrr) + rmrr->header.length, &rmrru->devices_cnt); if (rmrru->devices_cnt && rmrru->devices == NULL) - goto free_all; + goto free_rmrru; list_add(&rmrru->list, &dmar_rmrr_units); return 0; -free_all: - kfree(rmrru->resv); free_rmrru: kfree(rmrru); out: @@ -4429,7 +4424,6 @@ static void intel_iommu_free_dmars(void) list_for_each_entry_safe(rmrru, rmrr_n, &dmar_rmrr_units, list) { list_del(&rmrru->list); dmar_free_dev_scope(&rmrru->devices, &rmrru->devices_cnt); - kfree(rmrru->resv); kfree(rmrru); } @@ -5203,22 +5197,33 @@ static void intel_iommu_remove_device(struct device *dev) static void intel_iommu_get_resv_regions(struct device *device, struct list_head *head) { + int prot = DMA_PTE_READ | DMA_PTE_WRITE; struct iommu_resv_region *reg; struct dmar_rmrr_unit *rmrr; struct device *i_dev; int i; - rcu_read_lock(); + down_read(&dmar_global_lock); for_each_rmrr_units(rmrr) { for_each_active_dev_scope(rmrr->devices, rmrr->devices_cnt, i, i_dev) { + struct iommu_resv_region *resv; + size_t length; + if (i_dev != device) continue; - list_add_tail(&rmrr->resv->list, head); + length = rmrr->end_address - rmrr->base_address + 1; + resv = iommu_alloc_resv_region(rmrr->base_address, + length, prot, + IOMMU_RESV_DIRECT); + if (!resv) + break; + + list_add_tail(&resv->list, head); } } - rcu_read_unlock(); + up_read(&dmar_global_lock); reg = iommu_alloc_resv_region(IOAPIC_RANGE_START, IOAPIC_RANGE_END - IOAPIC_RANGE_START + 1, @@ -5233,10 +5238,8 @@ static void intel_iommu_put_resv_regions(struct device *dev, { struct iommu_resv_region *entry, *next; - list_for_each_entry_safe(entry, next, head, list) { - if (entry->type == IOMMU_RESV_MSI) - kfree(entry); - } + list_for_each_entry_safe(entry, next, head, list) + kfree(entry); } #ifdef CONFIG_INTEL_IOMMU_SVM diff --git a/drivers/iommu/intel-svm.c b/drivers/iommu/intel-svm.c index 188f4eaed6e5902d996bf616078770c4262c02aa..5944d3b4dca37a9c555cef2e01a5cdcbd23c90b0 100644 --- a/drivers/iommu/intel-svm.c +++ b/drivers/iommu/intel-svm.c @@ -293,7 +293,7 @@ int intel_svm_bind_mm(struct device *dev, int *pasid, int flags, struct svm_dev_ int pasid_max; int ret; - if (!iommu) + if (!iommu || dmar_disabled) return -EINVAL; if (dev_is_pci(dev)) { @@ -377,7 +377,7 @@ int intel_svm_bind_mm(struct device *dev, int *pasid, int flags, struct svm_dev_ /* Do not use PASID 0 in caching mode (virtualised IOMMU) */ ret = intel_pasid_alloc_id(svm, !!cap_caching_mode(iommu->cap), - pasid_max - 1, GFP_KERNEL); + pasid_max, GFP_KERNEL); if (ret < 0) { kfree(svm); kfree(sdev); diff --git a/drivers/iommu/iommu-debugfs.c b/drivers/iommu/iommu-debugfs.c index ced14f8b85d31b41e770e7b7340c6d94a64a594b..41ced5ab3bedf0d131e6db2b42847ebee65c1c0f 100644 --- a/drivers/iommu/iommu-debugfs.c +++ b/drivers/iommu/iommu-debugfs.c @@ -12,6 +12,7 @@ #include struct dentry *iommu_debugfs_dir; +EXPORT_SYMBOL_GPL(iommu_debugfs_dir); /** * iommu_debugfs_setup - create the top-level iommu directory in debugfs @@ -23,9 +24,9 @@ struct dentry *iommu_debugfs_dir; * Emit a strong warning at boot time to indicate that this feature is * enabled. * - * This function is called from iommu_init; drivers may then call - * iommu_debugfs_new_driver_dir() to instantiate a vendor-specific - * directory to be used to expose internal data. + * This function is called from iommu_init; drivers may then use + * iommu_debugfs_dir to instantiate a vendor-specific directory to be used + * to expose internal data. */ void iommu_debugfs_setup(void) { @@ -49,19 +50,3 @@ void iommu_debugfs_setup(void) pr_warn("*************************************************************\n"); } } - -/** - * iommu_debugfs_new_driver_dir - create a vendor directory under debugfs/iommu - * @vendor: name of the vendor-specific subdirectory to create - * - * This function is called by an IOMMU driver to create the top-level debugfs - * directory for that driver. - * - * Return: upon success, a pointer to the dentry for the new directory. - * NULL in case of failure. - */ -struct dentry *iommu_debugfs_new_driver_dir(const char *vendor) -{ - return debugfs_create_dir(vendor, iommu_debugfs_dir); -} -EXPORT_SYMBOL_GPL(iommu_debugfs_new_driver_dir); diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 4b4680939dec008647b1c475ff7a6dee38a9ad80..a04ccab77514d441d0d2254681f235d161002937 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -341,7 +341,7 @@ static ssize_t iommu_group_show_type(struct iommu_group *group, type = "unmanaged\n"; break; case IOMMU_DOMAIN_DMA: - type = "DMA"; + type = "DMA\n"; break; } } @@ -675,6 +675,7 @@ int iommu_group_add_device(struct iommu_group *group, struct device *dev) mutex_unlock(&group->mutex); dev->iommu_group = NULL; kobject_put(group->devices_kobj); + sysfs_remove_link(group->devices_kobj, device->name); err_free_name: kfree(device->name); err_remove_link: @@ -1978,9 +1979,9 @@ int iommu_request_dm_for_dev(struct device *dev) int ret; /* Device must already be in a group before calling this function */ - group = iommu_group_get_for_dev(dev); - if (IS_ERR(group)) - return PTR_ERR(group); + group = iommu_group_get(dev); + if (!group) + return -EINVAL; mutex_lock(&group->mutex); diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c index 19259ae3346c38c0b8d0a5ef96393a8cd8fb95a7..329205bde96d4890a31b9d777ed20cf76ebea0e3 100644 --- a/drivers/iommu/iova.c +++ b/drivers/iommu/iova.c @@ -212,8 +212,9 @@ static int __alloc_and_insert_iova_range(struct iova_domain *iovad, struct rb_node *curr, *prev; struct iova *curr_iova; unsigned long flags; - unsigned long new_pfn; + unsigned long new_pfn, low_pfn_new; unsigned long align_mask = ~0UL; + unsigned long high_pfn = limit_pfn, low_pfn = iovad->start_pfn; if (size_aligned) align_mask <<= limit_align(iovad, fls_long(size - 1)); @@ -222,15 +223,25 @@ static int __alloc_and_insert_iova_range(struct iova_domain *iovad, spin_lock_irqsave(&iovad->iova_rbtree_lock, flags); curr = __get_cached_rbnode(iovad, limit_pfn); curr_iova = rb_entry(curr, struct iova, node); + low_pfn_new = curr_iova->pfn_hi + 1; + +retry: do { - limit_pfn = min(limit_pfn, curr_iova->pfn_lo); - new_pfn = (limit_pfn - size) & align_mask; + high_pfn = min(high_pfn, curr_iova->pfn_lo); + new_pfn = (high_pfn - size) & align_mask; prev = curr; curr = rb_prev(curr); curr_iova = rb_entry(curr, struct iova, node); - } while (curr && new_pfn <= curr_iova->pfn_hi); - - if (limit_pfn < size || new_pfn < iovad->start_pfn) { + } while (curr && new_pfn <= curr_iova->pfn_hi && new_pfn >= low_pfn); + + if (high_pfn < size || new_pfn < low_pfn) { + if (low_pfn == iovad->start_pfn && low_pfn_new < limit_pfn) { + high_pfn = limit_pfn; + low_pfn = low_pfn_new; + curr = &iovad->anchor.node; + curr_iova = rb_entry(curr, struct iova, node); + goto retry; + } spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags); return -ENOMEM; } @@ -521,6 +532,7 @@ alloc_iova_fast(struct iova_domain *iovad, unsigned long size, flush_rcache = false; for_each_online_cpu(cpu) free_cpu_cached_iovas(cpu, iovad); + free_global_cached_iovas(iovad); goto retry; } @@ -1134,5 +1146,27 @@ void free_cpu_cached_iovas(unsigned int cpu, struct iova_domain *iovad) } } +/* + * free all the IOVA ranges of global cache + */ +void free_global_cached_iovas(struct iova_domain *iovad) +{ + struct iova_rcache *rcache; + unsigned long flags; + int i, j; + + for (i = 0; i < IOVA_RANGE_CACHE_MAX_SIZE; ++i) { + rcache = &iovad->rcaches[i]; + spin_lock_irqsave(&rcache->lock, flags); + for (j = 0; j < rcache->depot_size; ++j) { + iova_magazine_free_pfns(rcache->depot[j], iovad); + iova_magazine_free(rcache->depot[j]); + rcache->depot[j] = NULL; + } + rcache->depot_size = 0; + spin_unlock_irqrestore(&rcache->lock, flags); + } +} + MODULE_AUTHOR("Anil S Keshavamurthy "); MODULE_LICENSE("GPL"); diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c index f9f69f7111a91e99c1552e16533afac7d0d5f275..8e75f34ac8868d3ce5f28126665a3185a81aec1a 100644 --- a/drivers/iommu/mtk_iommu.c +++ b/drivers/iommu/mtk_iommu.c @@ -115,6 +115,30 @@ struct mtk_iommu_domain { static struct iommu_ops mtk_iommu_ops; +/* + * In M4U 4GB mode, the physical address is remapped as below: + * + * CPU Physical address: + * ==================== + * + * 0 1G 2G 3G 4G 5G + * |---A---|---B---|---C---|---D---|---E---| + * +--I/O--+------------Memory-------------+ + * + * IOMMU output physical address: + * ============================= + * + * 4G 5G 6G 7G 8G + * |---E---|---B---|---C---|---D---| + * +------------Memory-------------+ + * + * The Region 'A'(I/O) can NOT be mapped by M4U; For Region 'B'/'C'/'D', the + * bit32 of the CPU physical address always is needed to set, and for Region + * 'E', the CPU physical address keep as is. + * Additionally, The iommu consumers always use the CPU phyiscal address. + */ +#define MTK_IOMMU_4GB_MODE_REMAP_BASE 0x40000000 + static LIST_HEAD(m4ulist); /* List all the M4U HWs */ #define for_each_m4u(data) list_for_each_entry(data, &m4ulist, list) @@ -394,7 +418,7 @@ static size_t mtk_iommu_unmap(struct iommu_domain *domain, static void mtk_iommu_iotlb_sync(struct iommu_domain *domain) { - mtk_iommu_tlb_sync(mtk_iommu_get_m4u_data()); + mtk_iommu_tlb_flush_all(mtk_iommu_get_m4u_data()); } static phys_addr_t mtk_iommu_iova_to_phys(struct iommu_domain *domain, @@ -409,7 +433,7 @@ static phys_addr_t mtk_iommu_iova_to_phys(struct iommu_domain *domain, pa = dom->iop->iova_to_phys(dom->iop, iova); spin_unlock_irqrestore(&dom->pgtlock, flags); - if (data->enable_4GB) + if (data->enable_4GB && pa < MTK_IOMMU_4GB_MODE_REMAP_BASE) pa |= BIT_ULL(32); return pa; diff --git a/drivers/iommu/qcom_iommu.c b/drivers/iommu/qcom_iommu.c index 296dbcef856e69c1e2f1535b4309a474fc8cfa27..9f947ea3e48d2ede9be65688f4b9eb945db35f7f 100644 --- a/drivers/iommu/qcom_iommu.c +++ b/drivers/iommu/qcom_iommu.c @@ -333,21 +333,19 @@ static void qcom_iommu_domain_free(struct iommu_domain *domain) { struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain); - if (WARN_ON(qcom_domain->iommu)) /* forgot to detach? */ - return; - iommu_put_dma_cookie(domain); - /* NOTE: unmap can be called after client device is powered off, - * for example, with GPUs or anything involving dma-buf. So we - * cannot rely on the device_link. Make sure the IOMMU is on to - * avoid unclocked accesses in the TLB inv path: - */ - pm_runtime_get_sync(qcom_domain->iommu->dev); - - free_io_pgtable_ops(qcom_domain->pgtbl_ops); - - pm_runtime_put_sync(qcom_domain->iommu->dev); + if (qcom_domain->iommu) { + /* + * NOTE: unmap can be called after client device is powered + * off, for example, with GPUs or anything involving dma-buf. + * So we cannot rely on the device_link. Make sure the IOMMU + * is on to avoid unclocked accesses in the TLB inv path: + */ + pm_runtime_get_sync(qcom_domain->iommu->dev); + free_io_pgtable_ops(qcom_domain->pgtbl_ops); + pm_runtime_put_sync(qcom_domain->iommu->dev); + } kfree(qcom_domain); } @@ -392,7 +390,7 @@ static void qcom_iommu_detach_dev(struct iommu_domain *domain, struct device *de struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain); unsigned i; - if (!qcom_domain->iommu) + if (WARN_ON(!qcom_domain->iommu)) return; pm_runtime_get_sync(qcom_iommu->dev); @@ -405,8 +403,6 @@ static void qcom_iommu_detach_dev(struct iommu_domain *domain, struct device *de ctx->domain = NULL; } pm_runtime_put_sync(qcom_iommu->dev); - - qcom_domain->iommu = NULL; } static int qcom_iommu_map(struct iommu_domain *domain, unsigned long iova, diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 3fb07b152cc066dbfe123f48e9007d247158e364..6d73f6d6ebe0b621d4582b08a21dfd648bbf8d4c 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -402,8 +402,6 @@ config QCOM_MPM Say yes here to enable the MSM Power Manager interrupt controller to use as a wakeup interrupt controller. -endmenu - config SIFIVE_PLIC bool "SiFive Platform-Level Interrupt Controller" depends on RISCV @@ -415,3 +413,5 @@ config SIFIVE_PLIC interrupt sources are subordinate to the PLIC. If you don't know what to do here, say Y. + +endmenu diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index e3a994846819524ad753d167e577e8b9b2e1d5cd..3a01526f41ae0b544b3a32aedab8b8edfe95eaa8 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -88,5 +88,5 @@ obj-$(CONFIG_MESON_IRQ_GPIO) += irq-meson-gpio.o obj-$(CONFIG_GOLDFISH_PIC) += irq-goldfish-pic.o obj-$(CONFIG_NDS32) += irq-ativic32.o obj-$(CONFIG_QCOM_PDC) += qcom-pdc.o -obj-$(CONFIG_QCOM_MPM) += qcom-mpm.o qcom-mpm-bengal.o qcom-mpm-scuba.o +obj-$(CONFIG_QCOM_MPM) += qcom-mpm.o qcom-mpm-bengal.o qcom-mpm-scuba.o qcom-mpm-sdm660.o obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 050d6e040128d3bc3d8a4b9d9e6ef88950000ae6..f9b73336a39eee92fc8f600914809446234997bb 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -208,7 +208,7 @@ static struct its_collection *dev_event_to_col(struct its_device *its_dev, static struct its_collection *valid_col(struct its_collection *col) { - if (WARN_ON_ONCE(col->target_address & GENMASK_ULL(0, 15))) + if (WARN_ON_ONCE(col->target_address & GENMASK_ULL(15, 0))) return NULL; return col; @@ -578,7 +578,7 @@ static struct its_collection *its_build_invall_cmd(struct its_node *its, struct its_cmd_desc *desc) { its_encode_cmd(cmd, GITS_CMD_INVALL); - its_encode_collection(cmd, desc->its_mapc_cmd.col->col_id); + its_encode_collection(cmd, desc->its_invall_cmd.col->col_id); its_fixup_cmd(cmd); diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 1bbf9a058714a434448e9b1b7755843291533a7a..3d0ae38d550a98dbecf8582db0c12c486a198023 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -1417,6 +1417,7 @@ static struct struct redist_region *redist_regs; u32 nr_redist_regions; bool single_redist; + int enabled_rdists; u32 maint_irq; int maint_irq_mode; phys_addr_t vcpu_base; @@ -1511,8 +1512,10 @@ static int __init gic_acpi_match_gicc(struct acpi_subtable_header *header, * If GICC is enabled and has valid gicr base address, then it means * GICR base is presented via GICC */ - if ((gicc->flags & ACPI_MADT_ENABLED) && gicc->gicr_base_address) + if ((gicc->flags & ACPI_MADT_ENABLED) && gicc->gicr_base_address) { + acpi_data.enabled_rdists++; return 0; + } /* * It's perfectly valid firmware can pass disabled GICC entry, driver @@ -1542,8 +1545,10 @@ static int __init gic_acpi_count_gicr_regions(void) count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT, gic_acpi_match_gicc, 0); - if (count > 0) + if (count > 0) { acpi_data.single_redist = true; + count = acpi_data.enabled_rdists; + } return count; } diff --git a/drivers/irqchip/irq-mbigen.c b/drivers/irqchip/irq-mbigen.c index 98b6e1d4b1a68cf8a247f4c75a67d09fd11b6b08..f7fdbf5d183b9ced6518238dd021760dcb8410d8 100644 --- a/drivers/irqchip/irq-mbigen.c +++ b/drivers/irqchip/irq-mbigen.c @@ -381,6 +381,7 @@ static struct platform_driver mbigen_platform_driver = { .name = "Hisilicon MBIGEN-V2", .of_match_table = mbigen_of_match, .acpi_match_table = ACPI_PTR(mbigen_acpi_match), + .suppress_bind_attrs = true, }, .probe = mbigen_device_probe, }; diff --git a/drivers/irqchip/qcom-mpm-sdm660.c b/drivers/irqchip/qcom-mpm-sdm660.c new file mode 100644 index 0000000000000000000000000000000000000000..2e482e04d35657e68b0c0464437c13c651b15631 --- /dev/null +++ b/drivers/irqchip/qcom-mpm-sdm660.c @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +#include + +const struct mpm_pin mpm_sdm660_gic_chip_data[] = { + {2, 216}, /* tsens1_tsens_upper_lower_int */ + {52, 275}, /* qmp_usb3_lfps_rxterm_irq_cx */ + {61, 209}, /* lpi_dir_conn_irq_apps[1] */ + {79, 379}, /* qusb2phy_intr for Dm */ + {80, 380}, /* qusb2phy_intr for Dm for secondary PHY */ + {81, 379}, /* qusb2phy_intr for Dp */ + {82, 380}, /* qusb2phy_intr for Dp for secondary PHY */ + {87, 358}, /* ee0_apps_hlos_spmi_periph_irq */ + {91, 519}, /* lpass_pmu_tmr_timeout_irq_cx */ + {-1}, +}; diff --git a/drivers/irqchip/qcom-mpm.c b/drivers/irqchip/qcom-mpm.c index f7f4864e2b5743489752e6c75f1cec6adeb3190d..ab8a3b35f26be4c66c685efad831df099fd3e5aa 100644 --- a/drivers/irqchip/qcom-mpm.c +++ b/drivers/irqchip/qcom-mpm.c @@ -592,6 +592,10 @@ static const struct of_device_id mpm_gic_chip_data_table[] = { .compatible = "qcom,mpm-gic-scuba", .data = mpm_scuba_gic_chip_data, }, + { + .compatible = "qcom,mpm-gic-sdm660", + .data = mpm_sdm660_gic_chip_data, + }, {} }; MODULE_DEVICE_TABLE(of, mpm_gic_chip_data_table); diff --git a/drivers/irqchip/qcom-pdc.c b/drivers/irqchip/qcom-pdc.c index eb0e8a13d034cf39bf72d8d265e21aa3dd1b7be6..83b49b4c5367341b5d1723b997d146898384a2d3 100644 --- a/drivers/irqchip/qcom-pdc.c +++ b/drivers/irqchip/qcom-pdc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ #include @@ -39,6 +39,7 @@ struct pdc_pin_region { u32 cnt; }; +DECLARE_BITMAP(pdc_wake_irqs, PDC_MAX_IRQS); static DEFINE_RAW_SPINLOCK(pdc_lock); static void __iomem *pdc_base, *pdc_cfg_base; static struct pdc_pin_region *pdc_region; @@ -73,13 +74,20 @@ static void pdc_enable_intr(struct irq_data *d, bool on) raw_spin_unlock(&pdc_lock); } -static void qcom_pdc_gic_disable(struct irq_data *d) +static int qcom_pdc_gic_set_wake(struct irq_data *d, unsigned int on) { if (d->hwirq == GPIO_NO_WAKE_IRQ) - return; + return 0; - pdc_enable_intr(d, false); - irq_chip_disable_parent(d); + if (on) { + pdc_enable_intr(d, true); + set_bit(d->hwirq, pdc_wake_irqs); + irq_chip_enable_parent(d); + } else { + clear_bit(d->hwirq, pdc_wake_irqs); + } + + return irq_chip_set_wake_parent(d, on); } static int qcom_pdc_gic_get_irqchip_state(struct irq_data *d, @@ -116,6 +124,10 @@ static void qcom_pdc_gic_mask(struct irq_data *d) ipc_log_string(pdc_ipc_log, "PIN=%d mask", d->hwirq); irq_chip_mask_parent(d); + + /* Mask at PDC if not a wake irq */ + if (!test_bit(d->hwirq, pdc_wake_irqs)) + pdc_enable_intr(d, false); } static void qcom_pdc_gic_unmask(struct irq_data *d) @@ -241,15 +253,14 @@ static struct irq_chip qcom_pdc_gic_chip = { .irq_eoi = irq_chip_eoi_parent, .irq_mask = qcom_pdc_gic_mask, .irq_unmask = qcom_pdc_gic_unmask, - .irq_disable = qcom_pdc_gic_disable, .irq_enable = qcom_pdc_gic_enable, .irq_get_irqchip_state = qcom_pdc_gic_get_irqchip_state, .irq_set_irqchip_state = qcom_pdc_gic_set_irqchip_state, .irq_retrigger = irq_chip_retrigger_hierarchy, .irq_set_type = qcom_pdc_gic_set_type, + .irq_set_wake = qcom_pdc_gic_set_wake, .flags = IRQCHIP_MASK_ON_SUSPEND | - IRQCHIP_SET_TYPE_MASKED | - IRQCHIP_SKIP_SET_WAKE, + IRQCHIP_SET_TYPE_MASKED, .irq_set_vcpu_affinity = irq_chip_set_vcpu_affinity_parent, .irq_set_affinity = irq_chip_set_affinity_parent, }; diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index 1391b90440c36e6f38041ecb818fd4b890be89ed..b41c53418f9755f3cb3d35fa995dd3ad7b7fee5d 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -172,11 +172,11 @@ int led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig) trig->deactivate(led_cdev); err_activate: - led_cdev->trigger = NULL; - led_cdev->trigger_data = NULL; write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags); list_del(&led_cdev->trig_list); write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock, flags); + led_cdev->trigger = NULL; + led_cdev->trigger_data = NULL; led_set_brightness(led_cdev, LED_OFF); kfree(event); diff --git a/drivers/leds/leds-pca963x.c b/drivers/leds/leds-pca963x.c index 5c0908113e388ac9e67e751b496423feb9e73e9f..bbcde13b77f166e02e60e52f1574437b6f080754 100644 --- a/drivers/leds/leds-pca963x.c +++ b/drivers/leds/leds-pca963x.c @@ -43,6 +43,8 @@ #define PCA963X_LED_PWM 0x2 /* Controlled through PWM */ #define PCA963X_LED_GRP_PWM 0x3 /* Controlled through PWM/GRPPWM */ +#define PCA963X_MODE2_OUTDRV 0x04 /* Open-drain or totem pole */ +#define PCA963X_MODE2_INVRT 0x10 /* Normal or inverted direction */ #define PCA963X_MODE2_DMBLNK 0x20 /* Enable blinking */ #define PCA963X_MODE1 0x00 @@ -462,12 +464,12 @@ static int pca963x_probe(struct i2c_client *client, PCA963X_MODE2); /* Configure output: open-drain or totem pole (push-pull) */ if (pdata->outdrv == PCA963X_OPEN_DRAIN) - mode2 |= 0x01; + mode2 &= ~PCA963X_MODE2_OUTDRV; else - mode2 |= 0x05; + mode2 |= PCA963X_MODE2_OUTDRV; /* Configure direction: normal or inverted */ if (pdata->dir == PCA963X_INVERTED) - mode2 |= 0x10; + mode2 |= PCA963X_MODE2_INVRT; i2c_smbus_write_byte_data(pca963x->chip->client, PCA963X_MODE2, mode2); } diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c index 5d3faae51d59e6c3428ec17988a91a5458603ba0..5e2b643c2a403e56163188c964c07a7dba967ac6 100644 --- a/drivers/leds/leds-pwm.c +++ b/drivers/leds/leds-pwm.c @@ -22,12 +22,29 @@ #include #include +#define PWM_PERIOD_DEFAULT_NS 1000000 + +struct pwm_setting { + u64 period_ns; + u64 duty_ns; +}; + +struct led_setting { + u64 on_ms; + u64 off_ms; + enum led_brightness brightness; + bool blink; +}; + struct led_pwm_data { struct led_classdev cdev; struct pwm_device *pwm; + struct pwm_setting pwm_setting; + struct led_setting led_setting; unsigned int active_low; unsigned int period; int duty; + bool blinking; }; struct led_pwm_priv { @@ -35,35 +52,139 @@ struct led_pwm_priv { struct led_pwm_data leds[0]; }; -static void __led_pwm_set(struct led_pwm_data *led_dat) +static int __led_blink_config_pwm(struct led_pwm_data *led_data) +{ + struct pwm_state pstate; + int rc; + + pwm_get_state(led_data->pwm, &pstate); + pstate.enabled = !!(led_data->pwm_setting.duty_ns != 0); + pstate.period = led_data->pwm_setting.period_ns; + pstate.duty_cycle = led_data->pwm_setting.duty_ns; + /* Use default pattern in PWM device */ + pstate.output_pattern = NULL; + + rc = pwm_apply_state(led_data->pwm, &pstate); + if (rc < 0) + pr_err("Apply PWM state for %s led failed, rc=%d\n", + led_data->cdev.name, rc); + + return rc; +} + +static int led_pwm_set_blink(struct led_pwm_data *led_data) +{ + u64 on_ms, off_ms, period_ns, duty_ns; + enum led_brightness brightness = led_data->led_setting.brightness; + int rc = 0; + + if (led_data->led_setting.blink) { + on_ms = led_data->led_setting.on_ms; + off_ms = led_data->led_setting.off_ms; + + duty_ns = on_ms * NSEC_PER_MSEC; + period_ns = (on_ms + off_ms) * NSEC_PER_MSEC; + + if (period_ns < duty_ns && duty_ns != 0) + period_ns = duty_ns + 1; + + } else { + /* Use initial period if no blinking is required */ + period_ns = PWM_PERIOD_DEFAULT_NS; + + duty_ns = period_ns * brightness; + do_div(duty_ns, LED_FULL); + + if (period_ns < duty_ns && duty_ns != 0) + period_ns = duty_ns + 1; + } + + pr_debug("BLINK: PWM settings for %s led: period = %lluns, duty = %lluns brightness = %d\n", + led_data->cdev.name, period_ns, duty_ns, brightness); + + led_data->pwm_setting.duty_ns = duty_ns; + led_data->pwm_setting.period_ns = period_ns; + + rc = __led_blink_config_pwm(led_data); + if (rc < 0) { + pr_err("failed to config pwm for blink %s failed, rc=%d\n", + led_data->cdev.name, rc); + return rc; + } + + if (led_data->led_setting.blink) { + led_data->cdev.brightness = LED_FULL; + led_data->blinking = true; + } else { + led_data->cdev.brightness = led_data->led_setting.brightness; + led_data->blinking = false; + } + + return rc; +} + +static int led_pwm_blink_set(struct led_classdev *led_cdev, + unsigned long *on_ms, unsigned long *off_ms) +{ + struct led_pwm_data *led_data = + container_of(led_cdev, struct led_pwm_data, cdev); + int rc = 0; + + if (led_data->blinking && *on_ms == led_data->led_setting.on_ms && + *off_ms == led_data->led_setting.off_ms) { + pr_debug("Ignore, on/off setting is not changed: on %lums, off %lums\n", + *on_ms, *off_ms); + return 0; + } + + if (*on_ms == 0) { + led_data->led_setting.blink = false; + led_data->led_setting.brightness = LED_OFF; + } else if (*off_ms == 0) { + led_data->led_setting.blink = false; + led_data->led_setting.brightness = led_data->cdev.brightness; + } else { + led_data->led_setting.on_ms = *on_ms; + led_data->led_setting.off_ms = *off_ms; + led_data->led_setting.blink = true; + } + + rc = led_pwm_set_blink(led_data); + if (rc < 0) + pr_err("blink led failed for rc=%d\n", rc); + + return rc; +} + +static void __led_pwm_set(struct led_pwm_data *led_data) { - int new_duty = led_dat->duty; + int new_duty = led_data->duty; - pwm_config(led_dat->pwm, new_duty, led_dat->period); + pwm_config(led_data->pwm, new_duty, led_data->period); if (new_duty == 0) - pwm_disable(led_dat->pwm); + pwm_disable(led_data->pwm); else - pwm_enable(led_dat->pwm); + pwm_enable(led_data->pwm); } static int led_pwm_set(struct led_classdev *led_cdev, enum led_brightness brightness) { - struct led_pwm_data *led_dat = + struct led_pwm_data *led_data = container_of(led_cdev, struct led_pwm_data, cdev); - unsigned int max = led_dat->cdev.max_brightness; - unsigned long long duty = led_dat->period; + unsigned int max = led_data->cdev.max_brightness; + unsigned long long duty = led_data->period; duty *= brightness; do_div(duty, max); - if (led_dat->active_low) - duty = led_dat->period - duty; + if (led_data->active_low) + duty = led_data->period - duty; - led_dat->duty = duty; + led_data->duty = duty; - __led_pwm_set(led_dat); + __led_pwm_set(led_data); return 0; } @@ -92,7 +213,8 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv, led_data->cdev.default_trigger = led->default_trigger; led_data->cdev.brightness = LED_OFF; led_data->cdev.max_brightness = led->max_brightness; - led_data->cdev.flags = LED_CORE_SUSPENDRESUME; + /* Set a flag to keep the trigger always */ + led_data->cdev.flags |= LED_KEEP_TRIGGER; if (child) led_data->pwm = devm_of_pwm_get(dev, child, NULL); @@ -107,6 +229,7 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv, } led_data->cdev.brightness_set_blocking = led_pwm_set; + led_data->cdev.blink_set = led_pwm_blink_set; /* * FIXME: pwm_apply_args() should be removed when switching to the diff --git a/drivers/leds/leds-qti-flash.c b/drivers/leds/leds-qti-flash.c index 0e02e78facb825db90a2c7ec59a9ea32f7bce2dd..4457cf67a801f91522030d7d168ec778496a8b98 100644 --- a/drivers/leds/leds-qti-flash.c +++ b/drivers/leds/leds-qti-flash.c @@ -277,7 +277,7 @@ static int qti_flash_poll_vreg_ok(struct qti_flash_led *led) for (i = 0; i < 60; i++) { /* wait for the flash vreg_ok to be set */ - usleep_range(5000, 5500); + mdelay(5); rc = power_supply_get_property(led->main_psy, POWER_SUPPLY_PROP_FLASH_TRIGGER, &pval); @@ -859,6 +859,51 @@ static ssize_t qti_flash_led_max_current_show(struct device *dev, return scnprintf(buf, PAGE_SIZE, "%d\n", snode->led->max_current); } +static int qti_flash_led_regulator_control(struct led_classdev *led_cdev, + int options) +{ + struct flash_switch_data *snode; + union power_supply_propval ret = {0, }; + int rc = 0; + + snode = container_of(led_cdev, struct flash_switch_data, cdev); + + if (snode->led->data->pmic_type != PM2250) + return 0; + + rc = is_main_psy_available(snode->led); + if (rc < 0) + return rc; + + if (options & ENABLE_REGULATOR) { + ret.intval = 1; + rc = power_supply_set_property(snode->led->main_psy, + POWER_SUPPLY_PROP_FLASH_ACTIVE, + &ret); + if (rc < 0) { + pr_err("Failed to set FLASH_ACTIVE on charger rc=%d\n", + rc); + return rc; + } + + pr_debug("FLASH_ACTIVE = 1\n"); + } else if (options & DISABLE_REGULATOR) { + ret.intval = 0; + rc = power_supply_set_property(snode->led->main_psy, + POWER_SUPPLY_PROP_FLASH_ACTIVE, + &ret); + if (rc < 0) { + pr_err("Failed to set FLASH_ACTIVE on charger rc=%d\n", + rc); + return rc; + } + + pr_debug("FLASH_ACTIVE = 0\n"); + } + + return 0; +} + int qti_flash_led_prepare(struct led_trigger *trig, int options, int *max_current) { @@ -901,18 +946,42 @@ int qti_flash_led_prepare(struct led_trigger *trig, int options, return 0; } - if (options & ENABLE_REGULATOR) - return 0; - - if (options & DISABLE_REGULATOR) - return 0; + rc = qti_flash_led_regulator_control(led_cdev, options); + if (rc < 0) + pr_err("Failed to set flash control options\n"); - return -EINVAL; + return rc; } EXPORT_SYMBOL(qti_flash_led_prepare); +static ssize_t qti_flash_led_prepare_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int rc, options; + u32 val; + struct led_classdev *led_cdev = dev_get_drvdata(dev); + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + if (val != 0 && val != 1) + return count; + + options = val ? ENABLE_REGULATOR : DISABLE_REGULATOR; + + rc = qti_flash_led_regulator_control(led_cdev, options); + if (rc < 0) { + pr_err("failed to query led regulator\n"); + return rc; + } + + return count; +} + static struct device_attribute qti_flash_led_attrs[] = { __ATTR(max_current, 0664, qti_flash_led_max_current_show, NULL), + __ATTR(enable, 0664, NULL, qti_flash_led_prepare_store), }; static int qti_flash_brightness_set_blocking( diff --git a/drivers/lightnvm/pblk-rb.c b/drivers/lightnvm/pblk-rb.c index f6eec0212dfcf07fe5e2af1fa32530f593b1a14c..d22c13b556221ecf56ca83751c18f0b95ccca321 100644 --- a/drivers/lightnvm/pblk-rb.c +++ b/drivers/lightnvm/pblk-rb.c @@ -784,8 +784,8 @@ int pblk_rb_tear_down_check(struct pblk_rb *rb) } out: - spin_unlock(&rb->w_lock); spin_unlock_irq(&rb->s_lock); + spin_unlock(&rb->w_lock); return ret; } diff --git a/drivers/macintosh/therm_windtunnel.c b/drivers/macintosh/therm_windtunnel.c index 8c744578122a3e229079571b7bce093380c90662..a0d87ed9da69612224903eb192da987e3d5f5504 100644 --- a/drivers/macintosh/therm_windtunnel.c +++ b/drivers/macintosh/therm_windtunnel.c @@ -300,9 +300,11 @@ static int control_loop(void *dummy) /* i2c probing and setup */ /************************************************************************/ -static int -do_attach( struct i2c_adapter *adapter ) +static void do_attach(struct i2c_adapter *adapter) { + struct i2c_board_info info = { }; + struct device_node *np; + /* scan 0x48-0x4f (DS1775) and 0x2c-2x2f (ADM1030) */ static const unsigned short scan_ds1775[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, @@ -313,25 +315,24 @@ do_attach( struct i2c_adapter *adapter ) I2C_CLIENT_END }; - if( strncmp(adapter->name, "uni-n", 5) ) - return 0; - - if( !x.running ) { - struct i2c_board_info info; + if (x.running || strncmp(adapter->name, "uni-n", 5)) + return; - memset(&info, 0, sizeof(struct i2c_board_info)); - strlcpy(info.type, "therm_ds1775", I2C_NAME_SIZE); + np = of_find_compatible_node(adapter->dev.of_node, NULL, "MAC,ds1775"); + if (np) { + of_node_put(np); + } else { + strlcpy(info.type, "MAC,ds1775", I2C_NAME_SIZE); i2c_new_probed_device(adapter, &info, scan_ds1775, NULL); + } - strlcpy(info.type, "therm_adm1030", I2C_NAME_SIZE); + np = of_find_compatible_node(adapter->dev.of_node, NULL, "MAC,adm1030"); + if (np) { + of_node_put(np); + } else { + strlcpy(info.type, "MAC,adm1030", I2C_NAME_SIZE); i2c_new_probed_device(adapter, &info, scan_adm1030, NULL); - - if( x.thermostat && x.fan ) { - x.running = 1; - x.poll_task = kthread_run(control_loop, NULL, "g4fand"); - } } - return 0; } static int @@ -404,8 +405,8 @@ attach_thermostat( struct i2c_client *cl ) enum chip { ds1775, adm1030 }; static const struct i2c_device_id therm_windtunnel_id[] = { - { "therm_ds1775", ds1775 }, - { "therm_adm1030", adm1030 }, + { "MAC,ds1775", ds1775 }, + { "MAC,adm1030", adm1030 }, { } }; MODULE_DEVICE_TABLE(i2c, therm_windtunnel_id); @@ -414,6 +415,7 @@ static int do_probe(struct i2c_client *cl, const struct i2c_device_id *id) { struct i2c_adapter *adapter = cl->adapter; + int ret = 0; if( !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_WRITE_BYTE) ) @@ -421,11 +423,19 @@ do_probe(struct i2c_client *cl, const struct i2c_device_id *id) switch (id->driver_data) { case adm1030: - return attach_fan( cl ); + ret = attach_fan(cl); + break; case ds1775: - return attach_thermostat(cl); + ret = attach_thermostat(cl); + break; } - return 0; + + if (!x.running && x.thermostat && x.fan) { + x.running = 1; + x.poll_task = kthread_run(control_loop, NULL, "g4fand"); + } + + return ret; } static struct i2c_driver g4fan_driver = { diff --git a/drivers/mailbox/mtk-cmdq-mailbox.c b/drivers/mailbox/mtk-cmdq-mailbox.c index aec46d5d3506181a7dc34bee920d80645c2c95f0..f7cc29c00302a493ee4136fe41273775b706c35a 100644 --- a/drivers/mailbox/mtk-cmdq-mailbox.c +++ b/drivers/mailbox/mtk-cmdq-mailbox.c @@ -363,6 +363,9 @@ static int cmdq_mbox_send_data(struct mbox_chan *chan, void *data) WARN_ON(cmdq->suspended); task = kzalloc(sizeof(*task), GFP_ATOMIC); + if (!task) + return -ENOMEM; + task->cmdq = cmdq; INIT_LIST_HEAD(&task->list_entry); task->pa_base = pkt->pa_base; diff --git a/drivers/mailbox/qcom-apcs-ipc-mailbox.c b/drivers/mailbox/qcom-apcs-ipc-mailbox.c index ab4a6af7bef8ba815553ecfc474493abaf7a23c5..f3deddf176bc8267bda2b94e332d420ec09b6460 100644 --- a/drivers/mailbox/qcom-apcs-ipc-mailbox.c +++ b/drivers/mailbox/qcom-apcs-ipc-mailbox.c @@ -36,7 +36,7 @@ static const struct regmap_config apcs_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, - .max_register = 0x1000, + .max_register = 0xFFC, .fast_io = true, }; diff --git a/drivers/mailbox/ti-msgmgr.c b/drivers/mailbox/ti-msgmgr.c index 5bceafbf66993f266de4df2ddf574c9c19bfdca9..01e9e462512b78cb06f85a2fdb4816ffa558841b 100644 --- a/drivers/mailbox/ti-msgmgr.c +++ b/drivers/mailbox/ti-msgmgr.c @@ -547,7 +547,7 @@ static struct mbox_chan *ti_msgmgr_of_xlate(struct mbox_controller *mbox, } if (d->is_sproxy) { - if (req_pid > d->num_valid_queues) + if (req_pid >= d->num_valid_queues) goto err; qinst = &inst->qinsts[req_pid]; return qinst->chan; diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index 3b0bdde2bd67809366398ebde6515aa6467e688e..72c3d6658670737e35ade645879fd5b85f54112f 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -298,6 +298,9 @@ config DM_DEFAULT_KEY tristate "Default-key target support" depends on BLK_DEV_DM depends on BLK_INLINE_ENCRYPTION + # dm-default-key doesn't require -o inlinecrypt, but it does currently + # rely on the inline encryption hooks being built into the kernel. + depends on FS_ENCRYPTION_INLINE_CRYPT help This device-mapper target allows you to create a device that assigns a default encryption key to bios that aren't for the diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h index 4677b18ac2810ef79133d04a28f93cf1e806893a..2a2f189dd37cbd0ec5f27508d745b94716516056 100644 --- a/drivers/md/bcache/bcache.h +++ b/drivers/md/bcache/bcache.h @@ -329,6 +329,9 @@ struct cached_dev { */ atomic_t has_dirty; +#define BCH_CACHE_READA_ALL 0 +#define BCH_CACHE_READA_META_ONLY 1 + unsigned int cache_readahead_policy; struct bch_ratelimit writeback_rate; struct delayed_work writeback_rate_update; diff --git a/drivers/md/bcache/bset.h b/drivers/md/bcache/bset.h index c71365e7c1fac7fc87b051b10e8697edbe29f8c4..a50dcfda656f5f9426eb66ac6620348aa23e2a0d 100644 --- a/drivers/md/bcache/bset.h +++ b/drivers/md/bcache/bset.h @@ -397,7 +397,8 @@ void bch_btree_keys_stats(struct btree_keys *b, struct bset_stats *state); /* Bkey utility code */ -#define bset_bkey_last(i) bkey_idx((struct bkey *) (i)->d, (i)->keys) +#define bset_bkey_last(i) bkey_idx((struct bkey *) (i)->d, \ + (unsigned int)(i)->keys) static inline struct bkey *bset_bkey_idx(struct bset *i, unsigned int idx) { diff --git a/drivers/md/bcache/debug.c b/drivers/md/bcache/debug.c index 8c53d874ada4aa92526d328dd93203c400564254..f6b60d5908f704da0f35763f5161f32bf002bd55 100644 --- a/drivers/md/bcache/debug.c +++ b/drivers/md/bcache/debug.c @@ -178,10 +178,9 @@ static ssize_t bch_dump_read(struct file *file, char __user *buf, while (size) { struct keybuf_key *w; unsigned int bytes = min(i->bytes, size); - int err = copy_to_user(buf, i->buf, bytes); - if (err) - return err; + if (copy_to_user(buf, i->buf, bytes)) + return -EFAULT; ret += bytes; buf += bytes; diff --git a/drivers/md/bcache/request.c b/drivers/md/bcache/request.c index 4ca3e3d3f9c7568aff2c91f6cf5b112ca6c665b0..c1e487d1261cb7897d63098b84a6d29579fc4f84 100644 --- a/drivers/md/bcache/request.c +++ b/drivers/md/bcache/request.c @@ -391,13 +391,20 @@ static bool check_should_bypass(struct cached_dev *dc, struct bio *bio) goto skip; /* - * Flag for bypass if the IO is for read-ahead or background, - * unless the read-ahead request is for metadata + * If the bio is for read-ahead or background IO, bypass it or + * not depends on the following situations, + * - If the IO is for meta data, always cache it and no bypass + * - If the IO is not meta data, check dc->cache_reada_policy, + * BCH_CACHE_READA_ALL: cache it and not bypass + * BCH_CACHE_READA_META_ONLY: not cache it and bypass + * That is, read-ahead request for metadata always get cached * (eg, for gfs2 or xfs). */ - if (bio->bi_opf & (REQ_RAHEAD|REQ_BACKGROUND) && - !(bio->bi_opf & (REQ_META|REQ_PRIO))) - goto skip; + if ((bio->bi_opf & (REQ_RAHEAD|REQ_BACKGROUND))) { + if (!(bio->bi_opf & (REQ_META|REQ_PRIO)) && + (dc->cache_readahead_policy != BCH_CACHE_READA_ALL)) + goto skip; + } if (bio->bi_iter.bi_sector & (c->sb.block_size - 1) || bio_sectors(bio) & (c->sb.block_size - 1)) { diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c index c45d9ad010770f1d50f65e9909ac4d4433a5517f..5b5cbfadd003d9fec86ecb87c2550d91cf0101db 100644 --- a/drivers/md/bcache/super.c +++ b/drivers/md/bcache/super.c @@ -1226,6 +1226,9 @@ static void cached_dev_free(struct closure *cl) mutex_unlock(&bch_register_lock); + if (dc->sb_bio.bi_inline_vecs[0].bv_page) + put_page(bio_first_page_all(&dc->sb_bio)); + if (!IS_ERR_OR_NULL(dc->bdev)) blkdev_put(dc->bdev, FMODE_READ|FMODE_WRITE|FMODE_EXCL); diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c index 3e8d1f1b562f8be02f99ac43b382ec4e5333980d..591d9c8107dd9b790113b3eb91a482445c453fd1 100644 --- a/drivers/md/bcache/sysfs.c +++ b/drivers/md/bcache/sysfs.c @@ -25,6 +25,12 @@ static const char * const bch_cache_modes[] = { NULL }; +static const char * const bch_reada_cache_policies[] = { + "all", + "meta-only", + NULL +}; + /* Default is -1; we skip past it for stop_when_cache_set_failed */ static const char * const bch_stop_on_failure_modes[] = { "auto", @@ -94,6 +100,7 @@ rw_attribute(congested_write_threshold_us); rw_attribute(sequential_cutoff); rw_attribute(data_csum); rw_attribute(cache_mode); +rw_attribute(readahead_cache_policy); rw_attribute(stop_when_cache_set_failed); rw_attribute(writeback_metadata); rw_attribute(writeback_running); @@ -160,6 +167,11 @@ SHOW(__bch_cached_dev) bch_cache_modes, BDEV_CACHE_MODE(&dc->sb)); + if (attr == &sysfs_readahead_cache_policy) + return bch_snprint_string_list(buf, PAGE_SIZE, + bch_reada_cache_policies, + dc->cache_readahead_policy); + if (attr == &sysfs_stop_when_cache_set_failed) return bch_snprint_string_list(buf, PAGE_SIZE, bch_stop_on_failure_modes, @@ -324,6 +336,15 @@ STORE(__cached_dev) } } + if (attr == &sysfs_readahead_cache_policy) { + v = __sysfs_match_string(bch_reada_cache_policies, -1, buf); + if (v < 0) + return v; + + if ((unsigned int) v != dc->cache_readahead_policy) + dc->cache_readahead_policy = v; + } + if (attr == &sysfs_stop_when_cache_set_failed) { v = __sysfs_match_string(bch_stop_on_failure_modes, -1, buf); if (v < 0) @@ -417,6 +438,7 @@ static struct attribute *bch_cached_dev_files[] = { &sysfs_data_csum, #endif &sysfs_cache_mode, + &sysfs_readahead_cache_policy, &sysfs_stop_when_cache_set_failed, &sysfs_writeback_metadata, &sysfs_writeback_running, diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c index 84ff70027c2520d4e04da2b4b725d0abadc74b3b..2ddd575e97f735856735ffefa30cb586a133d0b9 100644 --- a/drivers/md/dm-cache-target.c +++ b/drivers/md/dm-cache-target.c @@ -2859,8 +2859,8 @@ static void cache_postsuspend(struct dm_target *ti) prevent_background_work(cache); BUG_ON(atomic_read(&cache->nr_io_migrations)); - cancel_delayed_work(&cache->waker); - flush_workqueue(cache->wq); + cancel_delayed_work_sync(&cache->waker); + drain_workqueue(cache->wq); WARN_ON(cache->tracker.in_flight); /* diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index 62f70042d09642d39cd114ba9c48627e62ace4e0..62289f24fa4a1d87e9f3b178d30e7ad1533df590 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -482,8 +482,14 @@ static int crypt_iv_essiv_gen(struct crypt_config *cc, u8 *iv, static int crypt_iv_benbi_ctr(struct crypt_config *cc, struct dm_target *ti, const char *opts) { - unsigned bs = crypto_skcipher_blocksize(any_tfm(cc)); - int log = ilog2(bs); + unsigned bs; + int log; + + if (test_bit(CRYPT_MODE_INTEGRITY_AEAD, &cc->cipher_flags)) + bs = crypto_aead_blocksize(any_tfm_aead(cc)); + else + bs = crypto_skcipher_blocksize(any_tfm(cc)); + log = ilog2(bs); /* we need to calculate how far we must shift the sector count * to get the cipher block count, we use this shift in _gen */ diff --git a/drivers/md/dm-integrity.c b/drivers/md/dm-integrity.c index e71aecc85bca5abe55136b1854d40c521c7cffed..06a3dc631e45efc86742e52303f99b72e07bdf2f 100644 --- a/drivers/md/dm-integrity.c +++ b/drivers/md/dm-integrity.c @@ -186,17 +186,19 @@ struct dm_integrity_c { __u8 sectors_per_block; unsigned char mode; - int suspending; int failed; struct crypto_shash *internal_hash; + struct dm_target *ti; + /* these variables are locked with endio_wait.lock */ struct rb_root in_progress; struct list_head wait_list; wait_queue_head_t endio_wait; struct workqueue_struct *wait_wq; + struct workqueue_struct *offload_wq; unsigned char commit_seq; commit_id_t commit_ids[N_COMMIT_IDS]; @@ -1241,7 +1243,7 @@ static void dec_in_flight(struct dm_integrity_io *dio) dio->range.logical_sector += dio->range.n_sectors; bio_advance(bio, dio->range.n_sectors << SECTOR_SHIFT); INIT_WORK(&dio->work, integrity_bio_wait); - queue_work(ic->wait_wq, &dio->work); + queue_work(ic->offload_wq, &dio->work); return; } do_endio_flush(ic, dio); @@ -1667,7 +1669,7 @@ static void dm_integrity_map_continue(struct dm_integrity_io *dio, bool from_map if (need_sync_io && from_map) { INIT_WORK(&dio->work, integrity_bio_wait); - queue_work(ic->metadata_wq, &dio->work); + queue_work(ic->offload_wq, &dio->work); return; } @@ -2091,7 +2093,7 @@ static void integrity_writer(struct work_struct *w) unsigned prev_free_sectors; /* the following test is not needed, but it tests the replay code */ - if (READ_ONCE(ic->suspending) && !ic->meta_dev) + if (unlikely(dm_suspended(ic->ti)) && !ic->meta_dev) return; spin_lock_irq(&ic->endio_wait.lock); @@ -2150,7 +2152,7 @@ static void integrity_recalc(struct work_struct *w) next_chunk: - if (unlikely(READ_ONCE(ic->suspending))) + if (unlikely(dm_suspended(ic->ti))) goto unlock_ret; range.logical_sector = le64_to_cpu(ic->sb->recalc_sector); @@ -2422,8 +2424,6 @@ static void dm_integrity_postsuspend(struct dm_target *ti) del_timer_sync(&ic->autocommit_timer); - WRITE_ONCE(ic->suspending, 1); - if (ic->recalc_wq) drain_workqueue(ic->recalc_wq); @@ -2437,8 +2437,6 @@ static void dm_integrity_postsuspend(struct dm_target *ti) dm_integrity_flush_buffers(ic); } - WRITE_ONCE(ic->suspending, 0); - BUG_ON(!RB_EMPTY_ROOT(&ic->in_progress)); ic->journal_uptodate = true; @@ -3127,6 +3125,7 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned argc, char **argv) } ti->private = ic; ti->per_io_data_size = sizeof(struct dm_integrity_io); + ic->ti = ti; ic->in_progress = RB_ROOT; INIT_LIST_HEAD(&ic->wait_list); @@ -3321,6 +3320,14 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned argc, char **argv) goto bad; } + ic->offload_wq = alloc_workqueue("dm-integrity-offload", WQ_MEM_RECLAIM, + METADATA_WORKQUEUE_MAX_ACTIVE); + if (!ic->offload_wq) { + ti->error = "Cannot allocate workqueue"; + r = -ENOMEM; + goto bad; + } + ic->commit_wq = alloc_workqueue("dm-integrity-commit", WQ_MEM_RECLAIM, 1); if (!ic->commit_wq) { ti->error = "Cannot allocate workqueue"; @@ -3557,6 +3564,8 @@ static void dm_integrity_dtr(struct dm_target *ti) destroy_workqueue(ic->metadata_wq); if (ic->wait_wq) destroy_workqueue(ic->wait_wq); + if (ic->offload_wq) + destroy_workqueue(ic->offload_wq); if (ic->commit_wq) destroy_workqueue(ic->commit_wq); if (ic->writer_wq) diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c index 3c50c4e4da8f39ee56703f9e0abc2461f8b96659..963d3774c93e287432966b658088914b67f10da3 100644 --- a/drivers/md/dm-snap-persistent.c +++ b/drivers/md/dm-snap-persistent.c @@ -17,7 +17,7 @@ #include #define DM_MSG_PREFIX "persistent snapshot" -#define DM_CHUNK_SIZE_DEFAULT_SECTORS 32 /* 16KB */ +#define DM_CHUNK_SIZE_DEFAULT_SECTORS 32U /* 16KB */ #define DM_PREFETCH_CHUNKS 12 diff --git a/drivers/md/dm-writecache.c b/drivers/md/dm-writecache.c index 503c4265ecbe106eaca30abbd7bdc4463952afc2..4e4a09054f858ee3824c9457a8c2d0e543d41fcc 100644 --- a/drivers/md/dm-writecache.c +++ b/drivers/md/dm-writecache.c @@ -447,7 +447,13 @@ static void writecache_notify_io(unsigned long error, void *context) complete(&endio->c); } -static void ssd_commit_flushed(struct dm_writecache *wc) +static void writecache_wait_for_ios(struct dm_writecache *wc, int direction) +{ + wait_event(wc->bio_in_progress_wait[direction], + !atomic_read(&wc->bio_in_progress[direction])); +} + +static void ssd_commit_flushed(struct dm_writecache *wc, bool wait_for_ios) { struct dm_io_region region; struct dm_io_request req; @@ -493,17 +499,20 @@ static void ssd_commit_flushed(struct dm_writecache *wc) writecache_notify_io(0, &endio); wait_for_completion_io(&endio.c); + if (wait_for_ios) + writecache_wait_for_ios(wc, WRITE); + writecache_disk_flush(wc, wc->ssd_dev); memset(wc->dirty_bitmap, 0, wc->dirty_bitmap_size); } -static void writecache_commit_flushed(struct dm_writecache *wc) +static void writecache_commit_flushed(struct dm_writecache *wc, bool wait_for_ios) { if (WC_MODE_PMEM(wc)) wmb(); else - ssd_commit_flushed(wc); + ssd_commit_flushed(wc, wait_for_ios); } static void writecache_disk_flush(struct dm_writecache *wc, struct dm_dev *dev) @@ -527,12 +536,6 @@ static void writecache_disk_flush(struct dm_writecache *wc, struct dm_dev *dev) writecache_error(wc, r, "error flushing metadata: %d", r); } -static void writecache_wait_for_ios(struct dm_writecache *wc, int direction) -{ - wait_event(wc->bio_in_progress_wait[direction], - !atomic_read(&wc->bio_in_progress[direction])); -} - #define WFE_RETURN_FOLLOWING 1 #define WFE_LOWEST_SEQ 2 @@ -628,6 +631,12 @@ static void writecache_add_to_freelist(struct dm_writecache *wc, struct wc_entry wc->freelist_size++; } +static inline void writecache_verify_watermark(struct dm_writecache *wc) +{ + if (unlikely(wc->freelist_size + wc->writeback_size <= wc->freelist_high_watermark)) + queue_work(wc->writeback_wq, &wc->writeback_work); +} + static struct wc_entry *writecache_pop_from_freelist(struct dm_writecache *wc) { struct wc_entry *e; @@ -649,8 +658,8 @@ static struct wc_entry *writecache_pop_from_freelist(struct dm_writecache *wc) list_del(&e->lru); } wc->freelist_size--; - if (unlikely(wc->freelist_size + wc->writeback_size <= wc->freelist_high_watermark)) - queue_work(wc->writeback_wq, &wc->writeback_work); + + writecache_verify_watermark(wc); return e; } @@ -730,14 +739,12 @@ static void writecache_flush(struct dm_writecache *wc) e = e2; cond_resched(); } - writecache_commit_flushed(wc); - - writecache_wait_for_ios(wc, WRITE); + writecache_commit_flushed(wc, true); wc->seq_count++; pmem_assign(sb(wc)->seq_count, cpu_to_le64(wc->seq_count)); writecache_flush_region(wc, &sb(wc)->seq_count, sizeof sb(wc)->seq_count); - writecache_commit_flushed(wc); + writecache_commit_flushed(wc, false); wc->overwrote_committed = false; @@ -761,7 +768,7 @@ static void writecache_flush(struct dm_writecache *wc) } if (need_flush_after_free) - writecache_commit_flushed(wc); + writecache_commit_flushed(wc, false); } static void writecache_flush_work(struct work_struct *work) @@ -814,7 +821,7 @@ static void writecache_discard(struct dm_writecache *wc, sector_t start, sector_ } if (discarded_something) - writecache_commit_flushed(wc); + writecache_commit_flushed(wc, false); } static bool writecache_wait_for_writeback(struct dm_writecache *wc) @@ -843,7 +850,7 @@ static void writecache_suspend(struct dm_target *ti) } wc_unlock(wc); - flush_workqueue(wc->writeback_wq); + drain_workqueue(wc->writeback_wq); wc_lock(wc); if (flush_on_suspend) @@ -963,9 +970,11 @@ static void writecache_resume(struct dm_target *ti) if (need_flush) { writecache_flush_all_metadata(wc); - writecache_commit_flushed(wc); + writecache_commit_flushed(wc, false); } + writecache_verify_watermark(wc); + wc_unlock(wc); } @@ -1347,7 +1356,7 @@ static void __writecache_endio_pmem(struct dm_writecache *wc, struct list_head * wc->writeback_size--; n_walked++; if (unlikely(n_walked >= ENDIO_LATENCY)) { - writecache_commit_flushed(wc); + writecache_commit_flushed(wc, false); wc_unlock(wc); wc_lock(wc); n_walked = 0; @@ -1428,7 +1437,7 @@ static int writecache_endio_thread(void *data) writecache_wait_for_ios(wc, READ); } - writecache_commit_flushed(wc); + writecache_commit_flushed(wc, false); wc_unlock(wc); } @@ -1759,10 +1768,10 @@ static int init_memory(struct dm_writecache *wc) write_original_sector_seq_count(wc, &wc->entries[b], -1, -1); writecache_flush_all_metadata(wc); - writecache_commit_flushed(wc); + writecache_commit_flushed(wc, false); pmem_assign(sb(wc)->magic, cpu_to_le32(MEMORY_SUPERBLOCK_MAGIC)); writecache_flush_region(wc, &sb(wc)->magic, sizeof sb(wc)->magic); - writecache_commit_flushed(wc); + writecache_commit_flushed(wc, false); return 0; } diff --git a/drivers/md/dm-zoned-metadata.c b/drivers/md/dm-zoned-metadata.c index c2c17149d9687b3a595ab64577b59c429cea81ea..086a870087cff81ff7fe57b8311a7e10000f6b9b 100644 --- a/drivers/md/dm-zoned-metadata.c +++ b/drivers/md/dm-zoned-metadata.c @@ -132,6 +132,7 @@ struct dmz_metadata { sector_t zone_bitmap_size; unsigned int zone_nr_bitmap_blocks; + unsigned int zone_bits_per_mblk; unsigned int nr_bitmap_blocks; unsigned int nr_map_blocks; @@ -1165,7 +1166,10 @@ static int dmz_init_zones(struct dmz_metadata *zmd) /* Init */ zmd->zone_bitmap_size = dev->zone_nr_blocks >> 3; - zmd->zone_nr_bitmap_blocks = zmd->zone_bitmap_size >> DMZ_BLOCK_SHIFT; + zmd->zone_nr_bitmap_blocks = + max_t(sector_t, 1, zmd->zone_bitmap_size >> DMZ_BLOCK_SHIFT); + zmd->zone_bits_per_mblk = min_t(sector_t, dev->zone_nr_blocks, + DMZ_BLOCK_SIZE_BITS); /* Allocate zone array */ zmd->zones = kcalloc(dev->nr_zones, sizeof(struct dm_zone), GFP_KERNEL); @@ -1982,7 +1986,7 @@ int dmz_copy_valid_blocks(struct dmz_metadata *zmd, struct dm_zone *from_zone, dmz_release_mblock(zmd, to_mblk); dmz_release_mblock(zmd, from_mblk); - chunk_block += DMZ_BLOCK_SIZE_BITS; + chunk_block += zmd->zone_bits_per_mblk; } to_zone->weight = from_zone->weight; @@ -2043,7 +2047,7 @@ int dmz_validate_blocks(struct dmz_metadata *zmd, struct dm_zone *zone, /* Set bits */ bit = chunk_block & DMZ_BLOCK_MASK_BITS; - nr_bits = min(nr_blocks, DMZ_BLOCK_SIZE_BITS - bit); + nr_bits = min(nr_blocks, zmd->zone_bits_per_mblk - bit); count = dmz_set_bits((unsigned long *)mblk->data, bit, nr_bits); if (count) { @@ -2122,7 +2126,7 @@ int dmz_invalidate_blocks(struct dmz_metadata *zmd, struct dm_zone *zone, /* Clear bits */ bit = chunk_block & DMZ_BLOCK_MASK_BITS; - nr_bits = min(nr_blocks, DMZ_BLOCK_SIZE_BITS - bit); + nr_bits = min(nr_blocks, zmd->zone_bits_per_mblk - bit); count = dmz_clear_bits((unsigned long *)mblk->data, bit, nr_bits); @@ -2182,6 +2186,7 @@ static int dmz_to_next_set_block(struct dmz_metadata *zmd, struct dm_zone *zone, { struct dmz_mblock *mblk; unsigned int bit, set_bit, nr_bits; + unsigned int zone_bits = zmd->zone_bits_per_mblk; unsigned long *bitmap; int n = 0; @@ -2196,15 +2201,15 @@ static int dmz_to_next_set_block(struct dmz_metadata *zmd, struct dm_zone *zone, /* Get offset */ bitmap = (unsigned long *) mblk->data; bit = chunk_block & DMZ_BLOCK_MASK_BITS; - nr_bits = min(nr_blocks, DMZ_BLOCK_SIZE_BITS - bit); + nr_bits = min(nr_blocks, zone_bits - bit); if (set) - set_bit = find_next_bit(bitmap, DMZ_BLOCK_SIZE_BITS, bit); + set_bit = find_next_bit(bitmap, zone_bits, bit); else - set_bit = find_next_zero_bit(bitmap, DMZ_BLOCK_SIZE_BITS, bit); + set_bit = find_next_zero_bit(bitmap, zone_bits, bit); dmz_release_mblock(zmd, mblk); n += set_bit - bit; - if (set_bit < DMZ_BLOCK_SIZE_BITS) + if (set_bit < zone_bits) break; nr_blocks -= nr_bits; @@ -2307,7 +2312,7 @@ static void dmz_get_zone_weight(struct dmz_metadata *zmd, struct dm_zone *zone) /* Count bits in this block */ bitmap = mblk->data; bit = chunk_block & DMZ_BLOCK_MASK_BITS; - nr_bits = min(nr_blocks, DMZ_BLOCK_SIZE_BITS - bit); + nr_bits = min(nr_blocks, zmd->zone_bits_per_mblk - bit); n += dmz_count_bits(bitmap, bit, nr_bits); dmz_release_mblock(zmd, mblk); diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 5df048032a8918414725de62d6f56616077cb779..4f8a043ab123cdc9b377f95a815edc84176d949f 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -1822,6 +1822,7 @@ static void dm_init_normal_md_queue(struct mapped_device *md) /* * Initialize aspects of queue that aren't relevant for blk-mq */ + md->queue->backing_dev_info->congested_data = md; md->queue->backing_dev_info->congested_fn = dm_any_congested; } @@ -1920,7 +1921,12 @@ static struct mapped_device *alloc_dev(int minor) if (!md->queue) goto bad; md->queue->queuedata = md; - md->queue->backing_dev_info->congested_data = md; + /* + * default to bio-based required ->make_request_fn until DM + * table is loaded and md->type established. If request-based + * table is loaded: blk-mq will override accordingly. + */ + blk_queue_make_request(md->queue, dm_make_request); md->disk = alloc_disk_node(1, md->numa_node_id); if (!md->disk) @@ -2352,7 +2358,8 @@ static int dm_init_inline_encryption(struct mapped_device *md) */ memset(mode_masks, 0xFF, sizeof(mode_masks)); - md->queue->ksm = keyslot_manager_create_passthrough(&dm_ksm_ll_ops, + md->queue->ksm = keyslot_manager_create_passthrough(NULL, + &dm_ksm_ll_ops, mode_masks, md); if (!md->queue->ksm) return -ENOMEM; @@ -2403,7 +2410,6 @@ int dm_setup_md_queue(struct mapped_device *md, struct dm_table *t) case DM_TYPE_BIO_BASED: case DM_TYPE_DAX_BIO_BASED: dm_init_normal_md_queue(md); - blk_queue_make_request(md->queue, dm_make_request); break; case DM_TYPE_NVME_BIO_BASED: dm_init_normal_md_queue(md); @@ -2516,6 +2522,7 @@ static void __dm_destroy(struct mapped_device *md, bool wait) map = dm_get_live_table(md, &srcu_idx); if (!dm_suspended_md(md)) { dm_table_presuspend_targets(map); + set_bit(DMF_SUSPENDED, &md->flags); dm_table_postsuspend_targets(map); } /* dm_put_live_table must be before msleep, otherwise deadlock is possible */ diff --git a/drivers/md/persistent-data/dm-space-map-common.c b/drivers/md/persistent-data/dm-space-map-common.c index 0a3b8ae4a29c6789b400e91a912f90b8ced1a806..17aef55ed708a2addd1adfc9d6ab0ddaf2c83927 100644 --- a/drivers/md/persistent-data/dm-space-map-common.c +++ b/drivers/md/persistent-data/dm-space-map-common.c @@ -382,6 +382,33 @@ int sm_ll_find_free_block(struct ll_disk *ll, dm_block_t begin, return -ENOSPC; } +int sm_ll_find_common_free_block(struct ll_disk *old_ll, struct ll_disk *new_ll, + dm_block_t begin, dm_block_t end, dm_block_t *b) +{ + int r; + uint32_t count; + + do { + r = sm_ll_find_free_block(new_ll, begin, new_ll->nr_blocks, b); + if (r) + break; + + /* double check this block wasn't used in the old transaction */ + if (*b >= old_ll->nr_blocks) + count = 0; + else { + r = sm_ll_lookup(old_ll, *b, &count); + if (r) + break; + + if (count) + begin = *b + 1; + } + } while (count); + + return r; +} + static int sm_ll_mutate(struct ll_disk *ll, dm_block_t b, int (*mutator)(void *context, uint32_t old, uint32_t *new), void *context, enum allocation_event *ev) diff --git a/drivers/md/persistent-data/dm-space-map-common.h b/drivers/md/persistent-data/dm-space-map-common.h index b3078d5eda0c335c2986b84128d3e5bde01e8b8e..8de63ce39bdd53d099cb91eb50647f0d703d727a 100644 --- a/drivers/md/persistent-data/dm-space-map-common.h +++ b/drivers/md/persistent-data/dm-space-map-common.h @@ -109,6 +109,8 @@ int sm_ll_lookup_bitmap(struct ll_disk *ll, dm_block_t b, uint32_t *result); int sm_ll_lookup(struct ll_disk *ll, dm_block_t b, uint32_t *result); int sm_ll_find_free_block(struct ll_disk *ll, dm_block_t begin, dm_block_t end, dm_block_t *result); +int sm_ll_find_common_free_block(struct ll_disk *old_ll, struct ll_disk *new_ll, + dm_block_t begin, dm_block_t end, dm_block_t *result); int sm_ll_insert(struct ll_disk *ll, dm_block_t b, uint32_t ref_count, enum allocation_event *ev); int sm_ll_inc(struct ll_disk *ll, dm_block_t b, enum allocation_event *ev); int sm_ll_dec(struct ll_disk *ll, dm_block_t b, enum allocation_event *ev); diff --git a/drivers/md/persistent-data/dm-space-map-disk.c b/drivers/md/persistent-data/dm-space-map-disk.c index 32adf6b4a9c7097e12796f8b396f80bfc8867c0a..bf4c5e2ccb6ffc8d8e0fa5ba049429bdb6dc18d0 100644 --- a/drivers/md/persistent-data/dm-space-map-disk.c +++ b/drivers/md/persistent-data/dm-space-map-disk.c @@ -167,8 +167,10 @@ static int sm_disk_new_block(struct dm_space_map *sm, dm_block_t *b) enum allocation_event ev; struct sm_disk *smd = container_of(sm, struct sm_disk, sm); - /* FIXME: we should loop round a couple of times */ - r = sm_ll_find_free_block(&smd->old_ll, smd->begin, smd->old_ll.nr_blocks, b); + /* + * Any block we allocate has to be free in both the old and current ll. + */ + r = sm_ll_find_common_free_block(&smd->old_ll, &smd->ll, smd->begin, smd->ll.nr_blocks, b); if (r) return r; diff --git a/drivers/md/persistent-data/dm-space-map-metadata.c b/drivers/md/persistent-data/dm-space-map-metadata.c index 25328582cc4820dc29d0e79a8012eeeb8a7fb2f4..9e3c64ec2026fa66b3b1a02adcd5c3b0085f1318 100644 --- a/drivers/md/persistent-data/dm-space-map-metadata.c +++ b/drivers/md/persistent-data/dm-space-map-metadata.c @@ -448,7 +448,10 @@ static int sm_metadata_new_block_(struct dm_space_map *sm, dm_block_t *b) enum allocation_event ev; struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm); - r = sm_ll_find_free_block(&smm->old_ll, smm->begin, smm->old_ll.nr_blocks, b); + /* + * Any block we allocate has to be free in both the old and current ll. + */ + r = sm_ll_find_common_free_block(&smm->old_ll, &smm->ll, smm->begin, smm->ll.nr_blocks, b); if (r) return r; diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index 2e7140507f93007d1a852b48c78dbe9ab9681ab2..efa9df2336dac43135c6cf5b4c79311d29249460 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -94,7 +94,7 @@ static int create_strip_zones(struct mddev *mddev, struct r0conf **private_conf) char b[BDEVNAME_SIZE]; char b2[BDEVNAME_SIZE]; struct r0conf *conf = kzalloc(sizeof(*conf), GFP_KERNEL); - unsigned short blksize = 512; + unsigned blksize = 512; *private_conf = ERR_PTR(-ENOMEM); if (!conf) diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h index 1cf46c401664d5dfade36847ebe5910589c9ad0e..9cc5672e4148af31952b81c01dff60dfbd7ffdfb 100644 --- a/drivers/media/i2c/adv748x/adv748x.h +++ b/drivers/media/i2c/adv748x/adv748x.h @@ -361,10 +361,10 @@ int adv748x_write_block(struct adv748x_state *state, int client_page, #define io_read(s, r) adv748x_read(s, ADV748X_PAGE_IO, r) #define io_write(s, r, v) adv748x_write(s, ADV748X_PAGE_IO, r, v) -#define io_clrset(s, r, m, v) io_write(s, r, (io_read(s, r) & ~m) | v) +#define io_clrset(s, r, m, v) io_write(s, r, (io_read(s, r) & ~(m)) | (v)) #define hdmi_read(s, r) adv748x_read(s, ADV748X_PAGE_HDMI, r) -#define hdmi_read16(s, r, m) (((hdmi_read(s, r) << 8) | hdmi_read(s, r+1)) & m) +#define hdmi_read16(s, r, m) (((hdmi_read(s, r) << 8) | hdmi_read(s, (r)+1)) & (m)) #define hdmi_write(s, r, v) adv748x_write(s, ADV748X_PAGE_HDMI, r, v) #define repeater_read(s, r) adv748x_read(s, ADV748X_PAGE_REPEATER, r) @@ -372,11 +372,11 @@ int adv748x_write_block(struct adv748x_state *state, int client_page, #define sdp_read(s, r) adv748x_read(s, ADV748X_PAGE_SDP, r) #define sdp_write(s, r, v) adv748x_write(s, ADV748X_PAGE_SDP, r, v) -#define sdp_clrset(s, r, m, v) sdp_write(s, r, (sdp_read(s, r) & ~m) | v) +#define sdp_clrset(s, r, m, v) sdp_write(s, r, (sdp_read(s, r) & ~(m)) | (v)) #define cp_read(s, r) adv748x_read(s, ADV748X_PAGE_CP, r) #define cp_write(s, r, v) adv748x_write(s, ADV748X_PAGE_CP, r, v) -#define cp_clrset(s, r, m, v) cp_write(s, r, (cp_read(s, r) & ~m) | v) +#define cp_clrset(s, r, m, v) cp_write(s, r, (cp_read(s, r) & ~(m)) | (v)) #define txa_read(s, r) adv748x_read(s, ADV748X_PAGE_TXA, r) #define txb_read(s, r) adv748x_read(s, ADV748X_PAGE_TXB, r) diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index f74730d24d8fe38bf049fb3020fa5493c2f64a49..04788692c9ff4efe6cf2f3edd66304d0568c5d76 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -431,10 +431,12 @@ static int mt9v032_enum_mbus_code(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { + struct mt9v032 *mt9v032 = to_mt9v032(subdev); + if (code->index > 0) return -EINVAL; - code->code = MEDIA_BUS_FMT_SGRBG10_1X10; + code->code = mt9v032->format.code; return 0; } @@ -442,7 +444,11 @@ static int mt9v032_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { - if (fse->index >= 3 || fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) + struct mt9v032 *mt9v032 = to_mt9v032(subdev); + + if (fse->index >= 3) + return -EINVAL; + if (mt9v032->format.code != fse->code) return -EINVAL; fse->min_width = MT9V032_WINDOW_WIDTH_DEF / (1 << fse->index); diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c index 4b6be3b0fd5283723cac3b2424fdfb5f92acb2b7..5cdda9d6ca31eee215c5b17dd5613f39c59e0a27 100644 --- a/drivers/media/i2c/ov2659.c +++ b/drivers/media/i2c/ov2659.c @@ -1136,7 +1136,7 @@ static int ov2659_set_fmt(struct v4l2_subdev *sd, mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); *mf = fmt->format; #else - return -ENOTTY; + ret = -ENOTTY; #endif } else { s64 val; diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c index c5aadd8dd23f104a6d66ae4a1e665838bae56d26..33a67a3685e6880966e5377be10394c64046c786 100644 --- a/drivers/media/i2c/ov6650.c +++ b/drivers/media/i2c/ov6650.c @@ -203,7 +203,6 @@ struct ov6650 { unsigned long pclk_max; /* from resolution and format */ struct v4l2_fract tpf; /* as requested with s_frame_interval */ u32 code; - enum v4l2_colorspace colorspace; }; @@ -216,6 +215,17 @@ static u32 ov6650_codes[] = { MEDIA_BUS_FMT_Y8_1X8, }; +static const struct v4l2_mbus_framefmt ov6650_def_fmt = { + .width = W_CIF, + .height = H_CIF, + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .field = V4L2_FIELD_NONE, + .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT, + .quantization = V4L2_QUANTIZATION_DEFAULT, + .xfer_func = V4L2_XFER_FUNC_DEFAULT, +}; + /* read a register */ static int ov6650_reg_read(struct i2c_client *client, u8 reg, u8 *val) { @@ -517,12 +527,20 @@ static int ov6650_get_fmt(struct v4l2_subdev *sd, if (format->pad) return -EINVAL; - mf->width = priv->rect.width >> priv->half_scale; - mf->height = priv->rect.height >> priv->half_scale; - mf->code = priv->code; - mf->colorspace = priv->colorspace; - mf->field = V4L2_FIELD_NONE; + /* initialize response with default media bus frame format */ + *mf = ov6650_def_fmt; + /* update media bus format code and frame size */ + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + mf->width = cfg->try_fmt.width; + mf->height = cfg->try_fmt.height; + mf->code = cfg->try_fmt.code; + + } else { + mf->width = priv->rect.width >> priv->half_scale; + mf->height = priv->rect.height >> priv->half_scale; + mf->code = priv->code; + } return 0; } @@ -627,11 +645,6 @@ static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) priv->pclk_max = 8000000; } - if (code == MEDIA_BUS_FMT_SBGGR8_1X8) - priv->colorspace = V4L2_COLORSPACE_SRGB; - else if (code != 0) - priv->colorspace = V4L2_COLORSPACE_JPEG; - if (half_scale) { dev_dbg(&client->dev, "max resolution: QCIF\n"); coma_set |= COMA_QCIF; @@ -665,11 +678,6 @@ static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) if (!ret) priv->code = code; - if (!ret) { - mf->colorspace = priv->colorspace; - mf->width = priv->rect.width >> half_scale; - mf->height = priv->rect.height >> half_scale; - } return ret; } @@ -688,8 +696,6 @@ static int ov6650_set_fmt(struct v4l2_subdev *sd, v4l_bound_align_image(&mf->width, 2, W_CIF, 1, &mf->height, 2, H_CIF, 1, 0); - mf->field = V4L2_FIELD_NONE; - switch (mf->code) { case MEDIA_BUS_FMT_Y10_1X10: mf->code = MEDIA_BUS_FMT_Y8_1X8; @@ -699,20 +705,39 @@ static int ov6650_set_fmt(struct v4l2_subdev *sd, case MEDIA_BUS_FMT_YUYV8_2X8: case MEDIA_BUS_FMT_VYUY8_2X8: case MEDIA_BUS_FMT_UYVY8_2X8: - mf->colorspace = V4L2_COLORSPACE_JPEG; break; default: mf->code = MEDIA_BUS_FMT_SBGGR8_1X8; /* fall through */ case MEDIA_BUS_FMT_SBGGR8_1X8: - mf->colorspace = V4L2_COLORSPACE_SRGB; break; } - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return ov6650_s_fmt(sd, mf); - cfg->try_fmt = *mf; + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + /* store media bus format code and frame size in pad config */ + cfg->try_fmt.width = mf->width; + cfg->try_fmt.height = mf->height; + cfg->try_fmt.code = mf->code; + /* return default mbus frame format updated with pad config */ + *mf = ov6650_def_fmt; + mf->width = cfg->try_fmt.width; + mf->height = cfg->try_fmt.height; + mf->code = cfg->try_fmt.code; + + } else { + /* apply new media bus format code and frame size */ + int ret = ov6650_s_fmt(sd, mf); + + if (ret) + return ret; + + /* return default format updated with active size and code */ + *mf = ov6650_def_fmt; + mf->width = priv->rect.width >> priv->half_scale; + mf->height = priv->rect.height >> priv->half_scale; + mf->code = priv->code; + } return 0; } @@ -1006,7 +1031,6 @@ static int ov6650_probe(struct i2c_client *client, priv->rect.height = H_CIF; priv->half_scale = false; priv->code = MEDIA_BUS_FMT_YUYV8_2X8; - priv->colorspace = V4L2_COLORSPACE_JPEG; ret = ov6650_video_probe(client); if (ret) diff --git a/drivers/media/i2c/tw9910.c b/drivers/media/i2c/tw9910.c index a54548cc42857f603b619a92be2b2af9973c762c..c7321a70e3ed0b28a3e7ce65defb3e575111c415 100644 --- a/drivers/media/i2c/tw9910.c +++ b/drivers/media/i2c/tw9910.c @@ -1000,7 +1000,7 @@ static int tw9910_remove(struct i2c_client *client) if (priv->pdn_gpio) gpiod_put(priv->pdn_gpio); clk_put(priv->clk); - v4l2_device_unregister_subdev(&priv->subdev); + v4l2_async_unregister_subdev(&priv->subdev); return 0; } diff --git a/drivers/media/pci/cx18/cx18-fileops.c b/drivers/media/pci/cx18/cx18-fileops.c index a3f44e30f821921b88acfbffce824c7d42b34744..88c2f3bea2b612df49c4fffc3f52f67eda84673c 100644 --- a/drivers/media/pci/cx18/cx18-fileops.c +++ b/drivers/media/pci/cx18/cx18-fileops.c @@ -484,7 +484,7 @@ static ssize_t cx18_read_pos(struct cx18_stream *s, char __user *ubuf, CX18_DEBUG_HI_FILE("read %zd from %s, got %zd\n", count, s->name, rc); if (rc > 0) - pos += rc; + *pos += rc; return rc; } diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c index ed3210dc50bc2cf43685cddd931d79cd6964a5f9..642aefdbb7bb6ff32f9712cb6cd26fa5e6b0623d 100644 --- a/drivers/media/pci/cx23885/cx23885-cards.c +++ b/drivers/media/pci/cx23885/cx23885-cards.c @@ -811,6 +811,25 @@ struct cx23885_board cx23885_boards[] = { .name = "Hauppauge WinTV-Starburst2", .portb = CX23885_MPEG_DVB, }, + [CX23885_BOARD_AVERMEDIA_CE310B] = { + .name = "AVerMedia CE310B", + .porta = CX23885_ANALOG_VIDEO, + .force_bff = 1, + .input = {{ + .type = CX23885_VMUX_COMPOSITE1, + .vmux = CX25840_VIN1_CH1 | + CX25840_NONE_CH2 | + CX25840_NONE0_CH3, + .amux = CX25840_AUDIO7, + }, { + .type = CX23885_VMUX_SVIDEO, + .vmux = CX25840_VIN8_CH1 | + CX25840_NONE_CH2 | + CX25840_VIN7_CH3 | + CX25840_SVIDEO_ON, + .amux = CX25840_AUDIO7, + } }, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -1134,6 +1153,10 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x0070, .subdevice = 0xf02a, .card = CX23885_BOARD_HAUPPAUGE_STARBURST2, + }, { + .subvendor = 0x1461, + .subdevice = 0x3100, + .card = CX23885_BOARD_AVERMEDIA_CE310B, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -2358,6 +2381,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_DVBSKY_T982: case CX23885_BOARD_VIEWCAST_260E: case CX23885_BOARD_VIEWCAST_460E: + case CX23885_BOARD_AVERMEDIA_CE310B: dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_bus[2].i2c_adap, "cx25840", 0x88 >> 1, NULL); diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index 7d52173073d6eedf23fcd19e8de01f848a81311a..bafcb39822747f17f5372f436dba0231ab4fc85d 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -1474,8 +1474,9 @@ static int dvb_register(struct cx23885_tsport *port) if (fe0->dvb.frontend != NULL) { struct i2c_adapter *tun_i2c; - fe0->dvb.frontend->sec_priv = kmalloc(sizeof(dib7000p_ops), GFP_KERNEL); - memcpy(fe0->dvb.frontend->sec_priv, &dib7000p_ops, sizeof(dib7000p_ops)); + fe0->dvb.frontend->sec_priv = kmemdup(&dib7000p_ops, sizeof(dib7000p_ops), GFP_KERNEL); + if (!fe0->dvb.frontend->sec_priv) + return -ENOMEM; tun_i2c = dib7000p_ops.get_i2c_master(fe0->dvb.frontend, DIBX000_I2C_INTERFACE_TUNER, 1); if (!dvb_attach(dib0070_attach, fe0->dvb.frontend, tun_i2c, &dib7070p_dib0070_config)) return -ENODEV; diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c index f8a3deadc77a19e2b1fdd0d404dfb3948f52a0f6..2a20c7165e1e8c71ebf3ef3814c9688e6a0a39fe 100644 --- a/drivers/media/pci/cx23885/cx23885-video.c +++ b/drivers/media/pci/cx23885/cx23885-video.c @@ -268,7 +268,8 @@ static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input) (dev->board == CX23885_BOARD_MYGICA_X8507) || (dev->board == CX23885_BOARD_AVERMEDIA_HC81R) || (dev->board == CX23885_BOARD_VIEWCAST_260E) || - (dev->board == CX23885_BOARD_VIEWCAST_460E)) { + (dev->board == CX23885_BOARD_VIEWCAST_460E) || + (dev->board == CX23885_BOARD_AVERMEDIA_CE310B)) { /* Configure audio routing */ v4l2_subdev_call(dev->sd_cx25840, audio, s_routing, INPUT(input)->amux, 0, 0); diff --git a/drivers/media/pci/cx23885/cx23885.h b/drivers/media/pci/cx23885/cx23885.h index cf965efabe66678f8c475f675ffbf818ec102ad1..7bbd62cc993efdec6cca3995f8d3305964ea1cc1 100644 --- a/drivers/media/pci/cx23885/cx23885.h +++ b/drivers/media/pci/cx23885/cx23885.h @@ -111,6 +111,7 @@ #define CX23885_BOARD_HAUPPAUGE_STARBURST2 59 #define CX23885_BOARD_HAUPPAUGE_QUADHD_DVB_885 60 #define CX23885_BOARD_HAUPPAUGE_QUADHD_ATSC_885 61 +#define CX23885_BOARD_AVERMEDIA_CE310B 62 #define GPIO_0 0x00000001 #define GPIO_1 0x00000002 diff --git a/drivers/media/pci/ivtv/ivtv-fileops.c b/drivers/media/pci/ivtv/ivtv-fileops.c index 6196daae4b3e0e05053bc16b6f6974ebb404e811..043ac0ae9ed07b4385ba84537119dceab239fe6f 100644 --- a/drivers/media/pci/ivtv/ivtv-fileops.c +++ b/drivers/media/pci/ivtv/ivtv-fileops.c @@ -420,7 +420,7 @@ static ssize_t ivtv_read_pos(struct ivtv_stream *s, char __user *ubuf, size_t co IVTV_DEBUG_HI_FILE("read %zd from %s, got %zd\n", count, s->name, rc); if (rc > 0) - pos += rc; + *pos += rc; return rc; } diff --git a/drivers/media/pci/pt1/pt1.c b/drivers/media/pci/pt1/pt1.c index 7f878fc41b7e68dad6be30cfa8a3565c4e6a6704..93fecffb36ee7550b45a7329e22e4d7499d46d68 100644 --- a/drivers/media/pci/pt1/pt1.c +++ b/drivers/media/pci/pt1/pt1.c @@ -200,16 +200,10 @@ static const u8 va1j5jf8007t_25mhz_configs[][2] = { static int config_demod(struct i2c_client *cl, enum pt1_fe_clk clk) { int ret; - u8 buf[2] = {0x01, 0x80}; bool is_sat; const u8 (*cfg_data)[2]; int i, len; - ret = i2c_master_send(cl, buf, 2); - if (ret < 0) - return ret; - usleep_range(30000, 50000); - is_sat = !strncmp(cl->name, TC90522_I2C_DEV_SAT, strlen(TC90522_I2C_DEV_SAT)); if (is_sat) { @@ -260,6 +254,46 @@ static int config_demod(struct i2c_client *cl, enum pt1_fe_clk clk) return 0; } +/* + * Init registers for (each pair of) terrestrial/satellite block in demod. + * Note that resetting terr. block also resets its peer sat. block as well. + * This function must be called before configuring any demod block + * (before pt1_wakeup(), fe->ops.init()). + */ +static int pt1_demod_block_init(struct pt1 *pt1) +{ + struct i2c_client *cl; + u8 buf[2] = {0x01, 0x80}; + int ret; + int i; + + /* reset all terr. & sat. pairs first */ + for (i = 0; i < PT1_NR_ADAPS; i++) { + cl = pt1->adaps[i]->demod_i2c_client; + if (strncmp(cl->name, TC90522_I2C_DEV_TER, + strlen(TC90522_I2C_DEV_TER))) + continue; + + ret = i2c_master_send(cl, buf, 2); + if (ret < 0) + return ret; + usleep_range(30000, 50000); + } + + for (i = 0; i < PT1_NR_ADAPS; i++) { + cl = pt1->adaps[i]->demod_i2c_client; + if (strncmp(cl->name, TC90522_I2C_DEV_SAT, + strlen(TC90522_I2C_DEV_SAT))) + continue; + + ret = i2c_master_send(cl, buf, 2); + if (ret < 0) + return ret; + usleep_range(30000, 50000); + } + return 0; +} + static void pt1_write_reg(struct pt1 *pt1, int reg, u32 data) { writel(data, pt1->regs + reg * 4); @@ -987,6 +1021,10 @@ static int pt1_init_frontends(struct pt1 *pt1) goto tuner_release; } + ret = pt1_demod_block_init(pt1); + if (ret < 0) + goto fe_unregister; + return 0; tuner_release: @@ -1245,6 +1283,10 @@ static int pt1_resume(struct device *dev) pt1_update_power(pt1); usleep_range(1000, 2000); + ret = pt1_demod_block_init(pt1); + if (ret < 0) + goto resume_err; + for (i = 0; i < PT1_NR_ADAPS; i++) dvb_frontend_reinitialise(pt1->adaps[i]->fe); diff --git a/drivers/media/pci/tw5864/tw5864-video.c b/drivers/media/pci/tw5864/tw5864-video.c index ff2b7da90c088694a83f7c908536aebfd5c8a8a1..6c40e60ac99391bcad47038701e13013baccc765 100644 --- a/drivers/media/pci/tw5864/tw5864-video.c +++ b/drivers/media/pci/tw5864/tw5864-video.c @@ -1395,13 +1395,13 @@ static void tw5864_handle_frame(struct tw5864_h264_frame *frame) input->vb = NULL; spin_unlock_irqrestore(&input->slock, flags); - v4l2_buf = to_vb2_v4l2_buffer(&vb->vb.vb2_buf); - if (!vb) { /* Gone because of disabling */ dev_dbg(&dev->pci->dev, "vb is empty, dropping frame\n"); return; } + v4l2_buf = to_vb2_v4l2_buffer(&vb->vb.vb2_buf); + /* * Check for space. * Mind the overhead of startcode emulation prevention. diff --git a/drivers/media/platform/atmel/atmel-isi.c b/drivers/media/platform/atmel/atmel-isi.c index e8db4df1e7c41c96e0b9607b8f1506d9d803357e..1a0e5233ae28e6717d4862364defa39b36d49bf9 100644 --- a/drivers/media/platform/atmel/atmel-isi.c +++ b/drivers/media/platform/atmel/atmel-isi.c @@ -496,7 +496,7 @@ static void stop_streaming(struct vb2_queue *vq) spin_unlock_irq(&isi->irqlock); if (!isi->enable_preview_path) { - timeout = jiffies + FRAME_INTERVAL_MILLI_SEC * HZ; + timeout = jiffies + (FRAME_INTERVAL_MILLI_SEC * HZ) / 1000; /* Wait until the end of the current frame. */ while ((isi_readl(isi, ISI_STATUS) & ISI_CTRL_CDC) && time_before(jiffies, timeout)) diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c index 43e43c7b3e982324445d0d25c19ccf5964c468d7..6f64703d2c7c74fd4f1a3dae4635faae9a724e60 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -129,7 +129,7 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) */ for (i = csi2rx->num_lanes; i < csi2rx->max_lanes; i++) { unsigned int idx = find_first_zero_bit(&lanes_used, - sizeof(lanes_used)); + csi2rx->max_lanes); set_bit(idx, &lanes_used); reg |= CSI2RX_STATIC_CFG_DLANE_MAP(i, i + 1); } diff --git a/drivers/media/platform/davinci/isif.c b/drivers/media/platform/davinci/isif.c index 340f8218f54d307cd582ecf645ca2228dc0cf067..80fa60a4c44898734bbb8191418fff3cc03ba3e2 100644 --- a/drivers/media/platform/davinci/isif.c +++ b/drivers/media/platform/davinci/isif.c @@ -884,9 +884,7 @@ static int isif_set_hw_if_params(struct vpfe_hw_if_param *params) static int isif_config_ycbcr(void) { struct isif_ycbcr_config *params = &isif_cfg.ycbcr; - struct vpss_pg_frame_size frame_size; u32 modeset = 0, ccdcfg = 0; - struct vpss_sync_pol sync; dev_dbg(isif_cfg.dev, "\nStarting isif_config_ycbcr..."); @@ -974,13 +972,6 @@ static int isif_config_ycbcr(void) /* two fields are interleaved in memory */ regw(0x00000249, SDOFST); - /* Setup test pattern if enabled */ - if (isif_cfg.bayer.config_params.test_pat_gen) { - sync.ccdpg_hdpol = params->hd_pol; - sync.ccdpg_vdpol = params->vd_pol; - dm365_vpss_set_sync_pol(sync); - dm365_vpss_set_pg_frame_size(frame_size); - } return 0; } diff --git a/drivers/media/platform/davinci/vpbe.c b/drivers/media/platform/davinci/vpbe.c index df1ae6b5c85458fd2153ce948e6ad0bd8173f434..e45e062f4442579e544b06734e6a97e77e2247ec 100644 --- a/drivers/media/platform/davinci/vpbe.c +++ b/drivers/media/platform/davinci/vpbe.c @@ -126,7 +126,7 @@ static int vpbe_enum_outputs(struct vpbe_device *vpbe_dev, struct v4l2_output *output) { struct vpbe_config *cfg = vpbe_dev->cfg; - int temp_index = output->index; + unsigned int temp_index = output->index; if (temp_index >= cfg->num_outputs) return -EINVAL; diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/exynos4-is/fimc-isp-video.c index a920164f53f1f0ab9de8a9947af72ac6cf653c95..39340abefd14de6540bff7013135809ee03731bf 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp-video.c +++ b/drivers/media/platform/exynos4-is/fimc-isp-video.c @@ -316,7 +316,7 @@ static int isp_video_release(struct file *file) ivc->streaming = 0; } - vb2_fop_release(file); + _vb2_fop_release(file, NULL); if (v4l2_fh_is_singular_file(file)) { fimc_pipeline_call(&ivc->ve, close); diff --git a/drivers/media/platform/msm/npu/npu_host_ipc.c b/drivers/media/platform/msm/npu/npu_host_ipc.c index 0d2816fa4036ea196f463764220dd219b36faf16..5ea693b1d3c560bee03ae8997c8948e712ce04db 100644 --- a/drivers/media/platform/msm/npu/npu_host_ipc.c +++ b/drivers/media/platform/msm/npu/npu_host_ipc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. */ /* ------------------------------------------------------------------------- @@ -367,8 +367,6 @@ static int ipc_queue_write(struct npu_device *npu_dev, /* Update qhdr_write_idx */ queue.qhdr_write_idx = new_write_idx; - *is_rx_req_set = (queue.qhdr_rx_req == 1) ? 1 : 0; - /* Update Write pointer -- queue.qhdr_write_idx */ exit: /* Update TX request -- queue.qhdr_tx_req */ @@ -379,6 +377,13 @@ static int ipc_queue_write(struct npu_device *npu_dev, (size_t)&(queue.qhdr_write_idx) - (size_t)&queue))), &queue.qhdr_write_idx, sizeof(queue.qhdr_write_idx)); + /* check if irq is required after write_idx is updated */ + MEMR(npu_dev, (void *)((size_t)(offset + (uint32_t)( + (size_t)&(queue.qhdr_rx_req) - (size_t)&queue))), + (uint8_t *)&queue.qhdr_rx_req, + sizeof(queue.qhdr_rx_req)); + *is_rx_req_set = (queue.qhdr_rx_req == 1) ? 1 : 0; + return status; } diff --git a/drivers/media/platform/msm/synx/synx.c b/drivers/media/platform/msm/synx/synx.c index eb467c025e2e3f91dd97b821f84fab39c043f47e..cd6795cb0bcb7ad9ede73cafc0ffa1da9a1dc2ca 100644 --- a/drivers/media/platform/msm/synx/synx.c +++ b/drivers/media/platform/msm/synx/synx.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "synx: " fmt @@ -1327,7 +1327,7 @@ static void synx_object_cleanup(struct synx_client *client) struct synx_table_row *row = synx_dev->synx_table + i; - mutex_lock(&synx_dev->row_locks[row->index]); + mutex_lock(&synx_dev->row_locks[i]); if (row->index) { list_for_each_entry_safe(payload_info, temp_payload_info, @@ -1339,7 +1339,7 @@ static void synx_object_cleanup(struct synx_client *client) } } } - mutex_unlock(&synx_dev->row_locks[row->index]); + mutex_unlock(&synx_dev->row_locks[i]); } } diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index 5700b7818621de4d91fa562e3515b2be09c8ba08..45511d24d5705941e4efe182b3d841cce98a80c7 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -1527,23 +1527,20 @@ static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) unsigned long size; struct videobuf_buffer *vb; - vb = q->bufs[b->index]; - if (!vout->streaming) return -EINVAL; - if (file->f_flags & O_NONBLOCK) - /* Call videobuf_dqbuf for non blocking mode */ - ret = videobuf_dqbuf(q, (struct v4l2_buffer *)b, 1); - else - /* Call videobuf_dqbuf for blocking mode */ - ret = videobuf_dqbuf(q, (struct v4l2_buffer *)b, 0); + ret = videobuf_dqbuf(q, b, !!(file->f_flags & O_NONBLOCK)); + if (ret) + return ret; + + vb = q->bufs[b->index]; addr = (unsigned long) vout->buf_phy_addr[vb->i]; size = (unsigned long) vb->size; dma_unmap_single(vout->vid_dev->v4l2_dev.dev, addr, size, DMA_TO_DEVICE); - return ret; + return 0; } static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i) diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index 485fa3fa8b49a7852bb5fd81631137e846eba56f..c389ba9ba74d2cacc7b9970ba9367d926c262440 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -631,7 +631,7 @@ static int rvin_parallel_init(struct rvin_dev *vin) ret = v4l2_async_notifier_register(&vin->v4l2_dev, &vin->notifier); if (ret < 0) { vin_err(vin, "Notifier registration failed\n"); - v4l2_async_notifier_cleanup(&vin->group->notifier); + v4l2_async_notifier_cleanup(&vin->notifier); return ret; } diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index 5a54779cfc27f65ddce716b2e784f3fcbd2ee431..1236e6e8228c6b72d3a7d1fdc84d4856f5024ae2 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -196,6 +196,7 @@ static int rvin_try_format(struct rvin_dev *vin, u32 which, ret = v4l2_subdev_call(sd, pad, set_fmt, pad_cfg, &format); if (ret < 0 && ret != -ENOIOCTLCMD) goto done; + ret = 0; v4l2_fill_pix_format(pix, &format.format); @@ -230,7 +231,7 @@ static int rvin_try_format(struct rvin_dev *vin, u32 which, done: v4l2_subdev_free_pad_config(pad_cfg); - return 0; + return ret; } static int rvin_querycap(struct file *file, void *priv, diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 350afaa29a620ff938f2bf6ee68f5e3f1e68c524..fa7c42cf4b4ef18e5c3541621c26af86c5cef888 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -2005,7 +2005,7 @@ static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx) v4l2_ctrl_new_std(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops, V4L2_CID_JPEG_RESTART_INTERVAL, - 0, 3, 0xffff, 0); + 0, 0xffff, 1, 0); if (ctx->jpeg->variant->version == SJPEG_S5P) mask = ~0x06; /* 422, 420 */ } diff --git a/drivers/media/platform/sti/bdisp/bdisp-hw.c b/drivers/media/platform/sti/bdisp/bdisp-hw.c index 26d9fa7aeb5f2d8b46167bca20ab06c6c0ad1914..d57f659d740a1e6906e0cc2118bebf32558824dc 100644 --- a/drivers/media/platform/sti/bdisp/bdisp-hw.c +++ b/drivers/media/platform/sti/bdisp/bdisp-hw.c @@ -14,8 +14,8 @@ #define MAX_SRC_WIDTH 2048 /* Reset & boot poll config */ -#define POLL_RST_MAX 50 -#define POLL_RST_DELAY_MS 20 +#define POLL_RST_MAX 500 +#define POLL_RST_DELAY_MS 2 enum bdisp_target_plan { BDISP_RGB, @@ -382,7 +382,7 @@ int bdisp_hw_reset(struct bdisp_dev *bdisp) for (i = 0; i < POLL_RST_MAX; i++) { if (readl(bdisp->regs + BLT_STA1) & BLT_STA1_IDLE) break; - msleep(POLL_RST_DELAY_MS); + udelay(POLL_RST_DELAY_MS * 1000); } if (i == POLL_RST_MAX) dev_err(bdisp->dev, "Reset timeout\n"); diff --git a/drivers/media/platform/vivid/vivid-osd.c b/drivers/media/platform/vivid/vivid-osd.c index bbbc1b6938a56ba8fa91cb093bb46b70ffc5394f..b24596697f5797beb09d3acf99490dfbd8f29e91 100644 --- a/drivers/media/platform/vivid/vivid-osd.c +++ b/drivers/media/platform/vivid/vivid-osd.c @@ -155,7 +155,7 @@ static int _vivid_fb_check_var(struct fb_var_screeninfo *var, struct vivid_dev * var->nonstd = 0; var->vmode &= ~FB_VMODE_MASK; - var->vmode = FB_VMODE_NONINTERLACED; + var->vmode |= FB_VMODE_NONINTERLACED; /* Dummy values */ var->hsync_len = 24; diff --git a/drivers/media/radio/rtc6226/radio-rtc6226-common.c b/drivers/media/radio/rtc6226/radio-rtc6226-common.c index 2e50c4fc0c5df6e8079c57f0d08e60a0154f2c1f..967e16f64cac3436b4ddc29b0e32b378eae280c5 100644 --- a/drivers/media/radio/rtc6226/radio-rtc6226-common.c +++ b/drivers/media/radio/rtc6226/radio-rtc6226-common.c @@ -1485,60 +1485,53 @@ int rtc6226_power_up(struct rtc6226_device *radio) **************************************************************************/ /* - * rtc6226_fops_read - read RDS data + * rtc6226_fops_read - read event data */ -static ssize_t rtc6226_fops_read(struct file *file, char __user *buf, +static ssize_t rtc6226_fops_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { - struct rtc6226_device *radio = video_drvdata(file); - int retval = 0; - unsigned int block_count = 0; - - /* switch on rds reception */ - mutex_lock(&radio->lock); - /* if RDS is not on, then turn on RDS */ - if ((radio->registers[SYSCFG] & SYSCFG_CSR0_RDS_EN) == 0) - rtc6226_rds_on(radio); + struct rtc6226_device *radio = video_get_drvdata(video_devdata(file)); + enum rtc6226_buf_t buf_type = -1; + u8 buf_fifo[STD_BUF_SIZE] = {0}; + struct kfifo *data_fifo = NULL; + int len = 0, retval = -1; + u32 bytesused = 0; - /* block if no new data available */ - while (radio->wr_index == radio->rd_index) { - if (file->f_flags & O_NONBLOCK) { - retval = -EWOULDBLOCK; - goto done; - } - if (wait_event_interruptible(radio->read_queue, - radio->wr_index != radio->rd_index) < 0) { - retval = -EINTR; - goto done; - } + if ((radio == NULL) || (buffer == NULL)) { + FMDERR("%s radio/buffer is NULL\n", __func__); + return -ENXIO; } - /* calculate block count from byte count */ - count /= 3; - FMDBG("%s : count = %zu\n", __func__, count); + buf_type = count; + len = STD_BUF_SIZE; + FMDBG("%s: requesting buffer %d\n", __func__, buf_type); - /* copy RDS block out of internal buffer and to user buffer */ - while (block_count < count) { - if (radio->rd_index == radio->wr_index) - break; - /* always transfer rds complete blocks */ - if (copy_to_user(buf, &radio->buffer[radio->rd_index], 3)) - /* retval = -EFAULT; */ - break; - /* increment and wrap read pointer */ - radio->rd_index += 3; - if (radio->rd_index >= radio->buf_size) - radio->rd_index = 0; - /* increment counters */ - block_count++; - buf += 3; - retval += 3; - FMDBG("%s : block_count = %d, count = %zu\n", __func__, - block_count, count); + if ((buf_type < RTC6226_FM_BUF_MAX) && (buf_type >= 0)) { + data_fifo = &radio->data_buf[buf_type]; + if (buf_type == RTC6226_FM_BUF_EVENTS) { + if (wait_event_interruptible(radio->event_queue, + kfifo_len(data_fifo)) < 0) { + return -EINTR; + } + } + } else { + FMDERR("%s invalid buffer type\n", __func__); + return -EINVAL; } - -done: - mutex_unlock(&radio->lock); + if (len <= STD_BUF_SIZE) { + bytesused = kfifo_out_locked(data_fifo, &buf_fifo[0], + len, &radio->buf_lock[buf_type]); + } else { + FMDERR("%s kfifo_out_locked can not use len more than 128\n", + __func__); + return -EINVAL; + } + retval = copy_to_user(buffer, &buf_fifo[0], bytesused); + if (retval > 0) { + FMDERR("%s Failed to copy %d bytes data\n", __func__, retval); + return -EAGAIN; + } + retval = bytesused; return retval; } diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c index ae7540b765e1dd81659bbaf99e4b5612b284d2aa..aa12fd26638954c405ea449fb29f08a6f46a4562 100644 --- a/drivers/media/radio/si470x/radio-si470x-i2c.c +++ b/drivers/media/radio/si470x/radio-si470x-i2c.c @@ -483,10 +483,10 @@ static int si470x_i2c_remove(struct i2c_client *client) free_irq(client->irq, radio); video_unregister_device(&radio->videodev); - kfree(radio); v4l2_ctrl_handler_free(&radio->hdl); v4l2_device_unregister(&radio->v4l2_dev); + kfree(radio); return 0; } diff --git a/drivers/media/radio/wl128x/fmdrv_common.c b/drivers/media/radio/wl128x/fmdrv_common.c index 1cf4019689a566fd39bd20505b9034dd8269311a..cccf1a743f4e61df6e62445bb3e1f8d1eeeb5415 100644 --- a/drivers/media/radio/wl128x/fmdrv_common.c +++ b/drivers/media/radio/wl128x/fmdrv_common.c @@ -1271,8 +1271,9 @@ static int fm_download_firmware(struct fmdev *fmdev, const u8 *fw_name) switch (action->type) { case ACTION_SEND_COMMAND: /* Send */ - if (fmc_send_cmd(fmdev, 0, 0, action->data, - action->size, NULL, NULL)) + ret = fmc_send_cmd(fmdev, 0, 0, action->data, + action->size, NULL, NULL); + if (ret) goto rel_fw; cmd_cnt++; diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c index 6f3030b2054d07ca0731df6552e108e1c1d6e54f..1df9522c30fa0a7ce1478e7865c81c394d0b54bd 100644 --- a/drivers/media/rc/iguanair.c +++ b/drivers/media/rc/iguanair.c @@ -424,7 +424,7 @@ static int iguanair_probe(struct usb_interface *intf, int ret, pipein, pipeout; struct usb_host_interface *idesc; - idesc = intf->altsetting; + idesc = intf->cur_altsetting; if (idesc->desc.bNumEndpoints < 2) return -ENODEV; diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 0f218afdadaa51dca3fa7a9b5edc99a54ae8d1f4..c30affbd43a98a0a244faaf468d49e3d0f7bcdbf 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -1874,23 +1874,28 @@ int rc_register_device(struct rc_dev *dev) dev->registered = true; - if (dev->driver_type != RC_DRIVER_IR_RAW_TX) { - rc = rc_setup_rx_device(dev); - if (rc) - goto out_dev; - } - - /* Ensure that the lirc kfifo is setup before we start the thread */ + /* + * once the the input device is registered in rc_setup_rx_device, + * userspace can open the input device and rc_open() will be called + * as a result. This results in driver code being allowed to submit + * keycodes with rc_keydown, so lirc must be registered first. + */ if (dev->allowed_protocols != RC_PROTO_BIT_CEC) { rc = ir_lirc_register(dev); if (rc < 0) - goto out_rx; + goto out_dev; + } + + if (dev->driver_type != RC_DRIVER_IR_RAW_TX) { + rc = rc_setup_rx_device(dev); + if (rc) + goto out_lirc; } if (dev->driver_type == RC_DRIVER_IR_RAW) { rc = ir_raw_event_register(dev); if (rc < 0) - goto out_lirc; + goto out_rx; } dev_dbg(&dev->dev, "Registered rc%u (driver: %s)\n", dev->minor, @@ -1898,11 +1903,11 @@ int rc_register_device(struct rc_dev *dev) return 0; +out_rx: + rc_free_rx_device(dev); out_lirc: if (dev->allowed_protocols != RC_PROTO_BIT_CEC) ir_lirc_unregister(dev); -out_rx: - rc_free_rx_device(dev); out_dev: device_del(&dev->dev); out_rx_free: diff --git a/drivers/media/usb/dvb-usb/af9005.c b/drivers/media/usb/dvb-usb/af9005.c index 25871979123f4ac101b6d4cc0d5bd784b4d43767..d2737460c9d3b5ad72328909d8d4790c37b5e44d 100644 --- a/drivers/media/usb/dvb-usb/af9005.c +++ b/drivers/media/usb/dvb-usb/af9005.c @@ -563,7 +563,7 @@ static int af9005_boot_packet(struct usb_device *udev, int type, u8 *reply, u8 *buf, int size) { u16 checksum; - int act_len, i, ret; + int act_len = 0, i, ret; memset(buf, 0, size); buf[0] = (u8) (FW_BULKOUT_SIZE & 0xff); diff --git a/drivers/media/usb/dvb-usb/digitv.c b/drivers/media/usb/dvb-usb/digitv.c index 49b9d63e5885ab5cc1f2100e96df607d9d428a26..e66df4fd1a296d22ed3a1a0307c8403c0cc001c3 100644 --- a/drivers/media/usb/dvb-usb/digitv.c +++ b/drivers/media/usb/dvb-usb/digitv.c @@ -233,18 +233,22 @@ static struct rc_map_table rc_map_digitv_table[] = { static int digitv_rc_query(struct dvb_usb_device *d, u32 *event, int *state) { - int i; + int ret, i; u8 key[5]; u8 b[4] = { 0 }; *event = 0; *state = REMOTE_NO_KEY_PRESSED; - digitv_ctrl_msg(d,USB_READ_REMOTE,0,NULL,0,&key[1],4); + ret = digitv_ctrl_msg(d, USB_READ_REMOTE, 0, NULL, 0, &key[1], 4); + if (ret) + return ret; /* Tell the device we've read the remote. Not sure how necessary this is, but the Nebula SDK does it. */ - digitv_ctrl_msg(d,USB_WRITE_REMOTE,0,b,4,NULL,0); + ret = digitv_ctrl_msg(d, USB_WRITE_REMOTE, 0, b, 4, NULL, 0); + if (ret) + return ret; /* if something is inside the buffer, simulate key press */ if (key[1] != 0) diff --git a/drivers/media/usb/dvb-usb/dvb-usb-urb.c b/drivers/media/usb/dvb-usb/dvb-usb-urb.c index c1b4e94a37f8d774291c546d62073f91cd21b2e8..2aabf90d869769144184465414d9da1cb3925352 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb-urb.c +++ b/drivers/media/usb/dvb-usb/dvb-usb-urb.c @@ -12,7 +12,7 @@ int dvb_usb_generic_rw(struct dvb_usb_device *d, u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen, int delay_ms) { - int actlen,ret = -ENOMEM; + int actlen = 0, ret = -ENOMEM; if (!d || wbuf == NULL || wlen == 0) return -EINVAL; diff --git a/drivers/media/usb/dvb-usb/vp7045.c b/drivers/media/usb/dvb-usb/vp7045.c index e2c8a853055443be76e3eb599abb2dd9be64663e..445d3e4c3de556b0f678fa2a5011a8426d93ef67 100644 --- a/drivers/media/usb/dvb-usb/vp7045.c +++ b/drivers/media/usb/dvb-usb/vp7045.c @@ -99,10 +99,14 @@ static int vp7045_power_ctrl(struct dvb_usb_device *d, int onoff) static int vp7045_rc_query(struct dvb_usb_device *d) { + int ret; u8 key; - vp7045_usb_op(d,RC_VAL_READ,NULL,0,&key,1,20); - deb_rc("remote query key: %x %d\n",key,key); + ret = vp7045_usb_op(d, RC_VAL_READ, NULL, 0, &key, 1, 20); + if (ret) + return ret; + + deb_rc("remote query key: %x\n", key); if (key != 0x44) { /* @@ -118,15 +122,18 @@ static int vp7045_rc_query(struct dvb_usb_device *d) static int vp7045_read_eeprom(struct dvb_usb_device *d,u8 *buf, int len, int offset) { - int i = 0; - u8 v,br[2]; + int i, ret; + u8 v, br[2]; for (i=0; i < len; i++) { v = offset + i; - vp7045_usb_op(d,GET_EE_VALUE,&v,1,br,2,5); + ret = vp7045_usb_op(d, GET_EE_VALUE, &v, 1, br, 2, 5); + if (ret) + return ret; + buf[i] = br[1]; } - deb_info("VP7045 EEPROM read (offs: %d, len: %d) : ",offset, i); - debug_dump(buf,i,deb_info); + deb_info("VP7045 EEPROM read (offs: %d, len: %d) : ", offset, i); + debug_dump(buf, i, deb_info); return 0; } diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 5657f8710ca6b60ee3afe681eff32fcb96a964d8..69445c8e38e2849cdaa6c6efa371a5e2c2cb685f 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -930,7 +930,7 @@ int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk, usb_bufs->buf = kcalloc(num_bufs, sizeof(void *), GFP_KERNEL); if (!usb_bufs->buf) { - kfree(usb_bufs->buf); + kfree(usb_bufs->urb); return -ENOMEM; } diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c index b12356c533a656f2d8318330004722a99fe07737..c9a2b29a60a57bd2e629436f41b3a78d42ddc829 100644 --- a/drivers/media/usb/gspca/gspca.c +++ b/drivers/media/usb/gspca/gspca.c @@ -1473,7 +1473,7 @@ int gspca_dev_probe2(struct usb_interface *intf, pr_err("couldn't kzalloc gspca struct\n"); return -ENOMEM; } - gspca_dev->usb_buf = kmalloc(USB_BUF_SZ, GFP_KERNEL); + gspca_dev->usb_buf = kzalloc(USB_BUF_SZ, GFP_KERNEL); if (!gspca_dev->usb_buf) { pr_err("out of memory\n"); ret = -ENOMEM; diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 063e229ead5ef7b8dfee8ceaae32018babfceac7..38c73cdbef70e85846a33dc0674b709c3bc1ed86 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -1482,6 +1482,11 @@ static int uvc_scan_chain_forward(struct uvc_video_chain *chain, break; if (forward == prev) continue; + if (forward->chain.next || forward->chain.prev) { + uvc_trace(UVC_TRACE_DESCR, "Found reference to " + "entity %d already in chain.\n", forward->id); + return -EINVAL; + } switch (UVC_ENTITY_TYPE(forward)) { case UVC_VC_EXTENSION_UNIT: @@ -1563,6 +1568,13 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain, return -1; } + if (term->chain.next || term->chain.prev) { + uvc_trace(UVC_TRACE_DESCR, "Found reference to " + "entity %d already in chain.\n", + term->id); + return -EINVAL; + } + if (uvc_trace_param & UVC_TRACE_PROBE) printk(KERN_CONT " %d", term->id); diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c index b8886102c5ed6325b6ef36eddc34bca80e1a5859..501030bb2e7d02d0db41191eadab758c6feb6fa5 100644 --- a/drivers/media/usb/zr364xx/zr364xx.c +++ b/drivers/media/usb/zr364xx/zr364xx.c @@ -703,7 +703,8 @@ static int zr364xx_vidioc_querycap(struct file *file, void *priv, struct zr364xx_camera *cam = video_drvdata(file); strlcpy(cap->driver, DRIVER_DESC, sizeof(cap->driver)); - strlcpy(cap->card, cam->udev->product, sizeof(cap->card)); + if (cam->udev->product) + strlcpy(cap->card, cam->udev->product, sizeof(cap->card)); strlcpy(cap->bus_info, dev_name(&cam->udev->dev), sizeof(cap->bus_info)); cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c index 988ec014cfec769c107b1a4a404c4d7d203ff807..8914ddb693c25a28d9f8e5db1248ddb40bbdf9c2 100644 --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -1177,36 +1177,38 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar u32 aux_space; int compatible_arg = 1; long err = 0; + unsigned int ncmd; /* * 1. When struct size is different, converts the command. */ switch (cmd) { - case VIDIOC_G_FMT32: cmd = VIDIOC_G_FMT; break; - case VIDIOC_S_FMT32: cmd = VIDIOC_S_FMT; break; - case VIDIOC_QUERYBUF32: cmd = VIDIOC_QUERYBUF; break; - case VIDIOC_G_FBUF32: cmd = VIDIOC_G_FBUF; break; - case VIDIOC_S_FBUF32: cmd = VIDIOC_S_FBUF; break; - case VIDIOC_QBUF32: cmd = VIDIOC_QBUF; break; - case VIDIOC_DQBUF32: cmd = VIDIOC_DQBUF; break; - case VIDIOC_ENUMSTD32: cmd = VIDIOC_ENUMSTD; break; - case VIDIOC_ENUMINPUT32: cmd = VIDIOC_ENUMINPUT; break; - case VIDIOC_TRY_FMT32: cmd = VIDIOC_TRY_FMT; break; - case VIDIOC_G_EXT_CTRLS32: cmd = VIDIOC_G_EXT_CTRLS; break; - case VIDIOC_S_EXT_CTRLS32: cmd = VIDIOC_S_EXT_CTRLS; break; - case VIDIOC_TRY_EXT_CTRLS32: cmd = VIDIOC_TRY_EXT_CTRLS; break; - case VIDIOC_DQEVENT32: cmd = VIDIOC_DQEVENT; break; - case VIDIOC_OVERLAY32: cmd = VIDIOC_OVERLAY; break; - case VIDIOC_STREAMON32: cmd = VIDIOC_STREAMON; break; - case VIDIOC_STREAMOFF32: cmd = VIDIOC_STREAMOFF; break; - case VIDIOC_G_INPUT32: cmd = VIDIOC_G_INPUT; break; - case VIDIOC_S_INPUT32: cmd = VIDIOC_S_INPUT; break; - case VIDIOC_G_OUTPUT32: cmd = VIDIOC_G_OUTPUT; break; - case VIDIOC_S_OUTPUT32: cmd = VIDIOC_S_OUTPUT; break; - case VIDIOC_CREATE_BUFS32: cmd = VIDIOC_CREATE_BUFS; break; - case VIDIOC_PREPARE_BUF32: cmd = VIDIOC_PREPARE_BUF; break; - case VIDIOC_G_EDID32: cmd = VIDIOC_G_EDID; break; - case VIDIOC_S_EDID32: cmd = VIDIOC_S_EDID; break; + case VIDIOC_G_FMT32: ncmd = VIDIOC_G_FMT; break; + case VIDIOC_S_FMT32: ncmd = VIDIOC_S_FMT; break; + case VIDIOC_QUERYBUF32: ncmd = VIDIOC_QUERYBUF; break; + case VIDIOC_G_FBUF32: ncmd = VIDIOC_G_FBUF; break; + case VIDIOC_S_FBUF32: ncmd = VIDIOC_S_FBUF; break; + case VIDIOC_QBUF32: ncmd = VIDIOC_QBUF; break; + case VIDIOC_DQBUF32: ncmd = VIDIOC_DQBUF; break; + case VIDIOC_ENUMSTD32: ncmd = VIDIOC_ENUMSTD; break; + case VIDIOC_ENUMINPUT32: ncmd = VIDIOC_ENUMINPUT; break; + case VIDIOC_TRY_FMT32: ncmd = VIDIOC_TRY_FMT; break; + case VIDIOC_G_EXT_CTRLS32: ncmd = VIDIOC_G_EXT_CTRLS; break; + case VIDIOC_S_EXT_CTRLS32: ncmd = VIDIOC_S_EXT_CTRLS; break; + case VIDIOC_TRY_EXT_CTRLS32: ncmd = VIDIOC_TRY_EXT_CTRLS; break; + case VIDIOC_DQEVENT32: ncmd = VIDIOC_DQEVENT; break; + case VIDIOC_OVERLAY32: ncmd = VIDIOC_OVERLAY; break; + case VIDIOC_STREAMON32: ncmd = VIDIOC_STREAMON; break; + case VIDIOC_STREAMOFF32: ncmd = VIDIOC_STREAMOFF; break; + case VIDIOC_G_INPUT32: ncmd = VIDIOC_G_INPUT; break; + case VIDIOC_S_INPUT32: ncmd = VIDIOC_S_INPUT; break; + case VIDIOC_G_OUTPUT32: ncmd = VIDIOC_G_OUTPUT; break; + case VIDIOC_S_OUTPUT32: ncmd = VIDIOC_S_OUTPUT; break; + case VIDIOC_CREATE_BUFS32: ncmd = VIDIOC_CREATE_BUFS; break; + case VIDIOC_PREPARE_BUF32: ncmd = VIDIOC_PREPARE_BUF; break; + case VIDIOC_G_EDID32: ncmd = VIDIOC_G_EDID; break; + case VIDIOC_S_EDID32: ncmd = VIDIOC_S_EDID; break; + default: ncmd = cmd; break; } /* @@ -1215,11 +1217,11 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar * argument into it. */ switch (cmd) { - case VIDIOC_OVERLAY: - case VIDIOC_STREAMON: - case VIDIOC_STREAMOFF: - case VIDIOC_S_INPUT: - case VIDIOC_S_OUTPUT: + case VIDIOC_OVERLAY32: + case VIDIOC_STREAMON32: + case VIDIOC_STREAMOFF32: + case VIDIOC_S_INPUT32: + case VIDIOC_S_OUTPUT32: err = alloc_userspace(sizeof(unsigned int), 0, &new_p64); if (!err && assign_in_user((unsigned int __user *)new_p64, (compat_uint_t __user *)p32)) @@ -1227,23 +1229,23 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar compatible_arg = 0; break; - case VIDIOC_G_INPUT: - case VIDIOC_G_OUTPUT: + case VIDIOC_G_INPUT32: + case VIDIOC_G_OUTPUT32: err = alloc_userspace(sizeof(unsigned int), 0, &new_p64); compatible_arg = 0; break; - case VIDIOC_G_EDID: - case VIDIOC_S_EDID: + case VIDIOC_G_EDID32: + case VIDIOC_S_EDID32: err = alloc_userspace(sizeof(struct v4l2_edid), 0, &new_p64); if (!err) err = get_v4l2_edid32(new_p64, p32); compatible_arg = 0; break; - case VIDIOC_G_FMT: - case VIDIOC_S_FMT: - case VIDIOC_TRY_FMT: + case VIDIOC_G_FMT32: + case VIDIOC_S_FMT32: + case VIDIOC_TRY_FMT32: err = bufsize_v4l2_format(p32, &aux_space); if (!err) err = alloc_userspace(sizeof(struct v4l2_format), @@ -1256,7 +1258,7 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar compatible_arg = 0; break; - case VIDIOC_CREATE_BUFS: + case VIDIOC_CREATE_BUFS32: err = bufsize_v4l2_create(p32, &aux_space); if (!err) err = alloc_userspace(sizeof(struct v4l2_create_buffers), @@ -1269,10 +1271,10 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar compatible_arg = 0; break; - case VIDIOC_PREPARE_BUF: - case VIDIOC_QUERYBUF: - case VIDIOC_QBUF: - case VIDIOC_DQBUF: + case VIDIOC_PREPARE_BUF32: + case VIDIOC_QUERYBUF32: + case VIDIOC_QBUF32: + case VIDIOC_DQBUF32: err = bufsize_v4l2_buffer(p32, &aux_space); if (!err) err = alloc_userspace(sizeof(struct v4l2_buffer), @@ -1285,7 +1287,7 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar compatible_arg = 0; break; - case VIDIOC_S_FBUF: + case VIDIOC_S_FBUF32: err = alloc_userspace(sizeof(struct v4l2_framebuffer), 0, &new_p64); if (!err) @@ -1293,13 +1295,13 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar compatible_arg = 0; break; - case VIDIOC_G_FBUF: + case VIDIOC_G_FBUF32: err = alloc_userspace(sizeof(struct v4l2_framebuffer), 0, &new_p64); compatible_arg = 0; break; - case VIDIOC_ENUMSTD: + case VIDIOC_ENUMSTD32: err = alloc_userspace(sizeof(struct v4l2_standard), 0, &new_p64); if (!err) @@ -1307,16 +1309,16 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar compatible_arg = 0; break; - case VIDIOC_ENUMINPUT: + case VIDIOC_ENUMINPUT32: err = alloc_userspace(sizeof(struct v4l2_input), 0, &new_p64); if (!err) err = get_v4l2_input32(new_p64, p32); compatible_arg = 0; break; - case VIDIOC_G_EXT_CTRLS: - case VIDIOC_S_EXT_CTRLS: - case VIDIOC_TRY_EXT_CTRLS: + case VIDIOC_G_EXT_CTRLS32: + case VIDIOC_S_EXT_CTRLS32: + case VIDIOC_TRY_EXT_CTRLS32: err = bufsize_v4l2_ext_controls(p32, &aux_space); if (!err) err = alloc_userspace(sizeof(struct v4l2_ext_controls), @@ -1328,7 +1330,7 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar } compatible_arg = 0; break; - case VIDIOC_DQEVENT: + case VIDIOC_DQEVENT32: err = alloc_userspace(sizeof(struct v4l2_event), 0, &new_p64); compatible_arg = 0; break; @@ -1346,9 +1348,9 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar * Otherwise, it will pass the newly allocated @new_p64 argument. */ if (compatible_arg) - err = native_ioctl(file, cmd, (unsigned long)p32); + err = native_ioctl(file, ncmd, (unsigned long)p32); else - err = native_ioctl(file, cmd, (unsigned long)new_p64); + err = native_ioctl(file, ncmd, (unsigned long)new_p64); if (err == -ENOTTY) return err; @@ -1364,13 +1366,13 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar * the blocks to maximum allowed value. */ switch (cmd) { - case VIDIOC_G_EXT_CTRLS: - case VIDIOC_S_EXT_CTRLS: - case VIDIOC_TRY_EXT_CTRLS: + case VIDIOC_G_EXT_CTRLS32: + case VIDIOC_S_EXT_CTRLS32: + case VIDIOC_TRY_EXT_CTRLS32: if (put_v4l2_ext_controls32(file, new_p64, p32)) err = -EFAULT; break; - case VIDIOC_S_EDID: + case VIDIOC_S_EDID32: if (put_v4l2_edid32(new_p64, p32)) err = -EFAULT; break; @@ -1383,49 +1385,49 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar * the original 32 bits structure. */ switch (cmd) { - case VIDIOC_S_INPUT: - case VIDIOC_S_OUTPUT: - case VIDIOC_G_INPUT: - case VIDIOC_G_OUTPUT: + case VIDIOC_S_INPUT32: + case VIDIOC_S_OUTPUT32: + case VIDIOC_G_INPUT32: + case VIDIOC_G_OUTPUT32: if (assign_in_user((compat_uint_t __user *)p32, ((unsigned int __user *)new_p64))) err = -EFAULT; break; - case VIDIOC_G_FBUF: + case VIDIOC_G_FBUF32: err = put_v4l2_framebuffer32(new_p64, p32); break; - case VIDIOC_DQEVENT: + case VIDIOC_DQEVENT32: err = put_v4l2_event32(new_p64, p32); break; - case VIDIOC_G_EDID: + case VIDIOC_G_EDID32: err = put_v4l2_edid32(new_p64, p32); break; - case VIDIOC_G_FMT: - case VIDIOC_S_FMT: - case VIDIOC_TRY_FMT: + case VIDIOC_G_FMT32: + case VIDIOC_S_FMT32: + case VIDIOC_TRY_FMT32: err = put_v4l2_format32(new_p64, p32); break; - case VIDIOC_CREATE_BUFS: + case VIDIOC_CREATE_BUFS32: err = put_v4l2_create32(new_p64, p32); break; - case VIDIOC_PREPARE_BUF: - case VIDIOC_QUERYBUF: - case VIDIOC_QBUF: - case VIDIOC_DQBUF: + case VIDIOC_PREPARE_BUF32: + case VIDIOC_QUERYBUF32: + case VIDIOC_QBUF32: + case VIDIOC_DQBUF32: err = put_v4l2_buffer32(new_p64, p32); break; - case VIDIOC_ENUMSTD: + case VIDIOC_ENUMSTD32: err = put_v4l2_standard32(new_p64, p32); break; - case VIDIOC_ENUMINPUT: + case VIDIOC_ENUMINPUT32: err = put_v4l2_input32(new_p64, p32); break; } diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index b7bf43bf8acae27a706fb582503dc398257db319..9a8743c7e6cb5936391489d3ffc857b2646b03ee 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1638,12 +1638,12 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, case V4L2_BUF_TYPE_VBI_CAPTURE: if (unlikely(!ops->vidioc_s_fmt_vbi_cap)) break; - CLEAR_AFTER_FIELD(p, fmt.vbi); + CLEAR_AFTER_FIELD(p, fmt.vbi.flags); return ops->vidioc_s_fmt_vbi_cap(file, fh, arg); case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_cap)) break; - CLEAR_AFTER_FIELD(p, fmt.sliced); + CLEAR_AFTER_FIELD(p, fmt.sliced.io_size); return ops->vidioc_s_fmt_sliced_vbi_cap(file, fh, arg); case V4L2_BUF_TYPE_VIDEO_OUTPUT: if (unlikely(!ops->vidioc_s_fmt_vid_out)) @@ -1666,22 +1666,22 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, case V4L2_BUF_TYPE_VBI_OUTPUT: if (unlikely(!ops->vidioc_s_fmt_vbi_out)) break; - CLEAR_AFTER_FIELD(p, fmt.vbi); + CLEAR_AFTER_FIELD(p, fmt.vbi.flags); return ops->vidioc_s_fmt_vbi_out(file, fh, arg); case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_out)) break; - CLEAR_AFTER_FIELD(p, fmt.sliced); + CLEAR_AFTER_FIELD(p, fmt.sliced.io_size); return ops->vidioc_s_fmt_sliced_vbi_out(file, fh, arg); case V4L2_BUF_TYPE_SDR_CAPTURE: if (unlikely(!ops->vidioc_s_fmt_sdr_cap)) break; - CLEAR_AFTER_FIELD(p, fmt.sdr); + CLEAR_AFTER_FIELD(p, fmt.sdr.buffersize); return ops->vidioc_s_fmt_sdr_cap(file, fh, arg); case V4L2_BUF_TYPE_SDR_OUTPUT: if (unlikely(!ops->vidioc_s_fmt_sdr_out)) break; - CLEAR_AFTER_FIELD(p, fmt.sdr); + CLEAR_AFTER_FIELD(p, fmt.sdr.buffersize); return ops->vidioc_s_fmt_sdr_out(file, fh, arg); case V4L2_BUF_TYPE_META_CAPTURE: if (unlikely(!ops->vidioc_s_fmt_meta_cap)) @@ -1725,12 +1725,12 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, case V4L2_BUF_TYPE_VBI_CAPTURE: if (unlikely(!ops->vidioc_try_fmt_vbi_cap)) break; - CLEAR_AFTER_FIELD(p, fmt.vbi); + CLEAR_AFTER_FIELD(p, fmt.vbi.flags); return ops->vidioc_try_fmt_vbi_cap(file, fh, arg); case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_cap)) break; - CLEAR_AFTER_FIELD(p, fmt.sliced); + CLEAR_AFTER_FIELD(p, fmt.sliced.io_size); return ops->vidioc_try_fmt_sliced_vbi_cap(file, fh, arg); case V4L2_BUF_TYPE_VIDEO_OUTPUT: if (unlikely(!ops->vidioc_try_fmt_vid_out)) @@ -1753,22 +1753,22 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, case V4L2_BUF_TYPE_VBI_OUTPUT: if (unlikely(!ops->vidioc_try_fmt_vbi_out)) break; - CLEAR_AFTER_FIELD(p, fmt.vbi); + CLEAR_AFTER_FIELD(p, fmt.vbi.flags); return ops->vidioc_try_fmt_vbi_out(file, fh, arg); case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_out)) break; - CLEAR_AFTER_FIELD(p, fmt.sliced); + CLEAR_AFTER_FIELD(p, fmt.sliced.io_size); return ops->vidioc_try_fmt_sliced_vbi_out(file, fh, arg); case V4L2_BUF_TYPE_SDR_CAPTURE: if (unlikely(!ops->vidioc_try_fmt_sdr_cap)) break; - CLEAR_AFTER_FIELD(p, fmt.sdr); + CLEAR_AFTER_FIELD(p, fmt.sdr.buffersize); return ops->vidioc_try_fmt_sdr_cap(file, fh, arg); case V4L2_BUF_TYPE_SDR_OUTPUT: if (unlikely(!ops->vidioc_try_fmt_sdr_out)) break; - CLEAR_AFTER_FIELD(p, fmt.sdr); + CLEAR_AFTER_FIELD(p, fmt.sdr.buffersize); return ops->vidioc_try_fmt_sdr_out(file, fh, arg); case V4L2_BUF_TYPE_META_CAPTURE: if (unlikely(!ops->vidioc_try_fmt_meta_cap)) diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index ce9bd1b912102aeb1b89840872f2ea5f1add6a1a..fc237b820c4fb99a25edf7680e38e512fcd11d14 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -787,12 +787,12 @@ int v4l2_m2m_register_media_controller(struct v4l2_m2m_dev *m2m_dev, goto err_rel_entity1; /* Connect the three entities */ - ret = media_create_pad_link(m2m_dev->source, 0, &m2m_dev->proc, 1, + ret = media_create_pad_link(m2m_dev->source, 0, &m2m_dev->proc, 0, MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); if (ret) goto err_rel_entity2; - ret = media_create_pad_link(&m2m_dev->proc, 0, &m2m_dev->sink, 0, + ret = media_create_pad_link(&m2m_dev->proc, 1, &m2m_dev->sink, 0, MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); if (ret) goto err_rm_links0; diff --git a/drivers/media/v4l2-core/videobuf-dma-sg.c b/drivers/media/v4l2-core/videobuf-dma-sg.c index 08929c087e270397f8d013102e6961f19e2f09b4..4c396e0defa47b69ea9649d7ce62fb91f1c22227 100644 --- a/drivers/media/v4l2-core/videobuf-dma-sg.c +++ b/drivers/media/v4l2-core/videobuf-dma-sg.c @@ -352,8 +352,11 @@ int videobuf_dma_free(struct videobuf_dmabuf *dma) BUG_ON(dma->sglen); if (dma->pages) { - for (i = 0; i < dma->nr_pages; i++) + for (i = 0; i < dma->nr_pages; i++) { + if (dma->direction == DMA_FROM_DEVICE) + set_page_dirty_lock(dma->pages[i]); put_page(dma->pages[i]); + } kfree(dma->pages); dma->pages = NULL; } diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index c8f16666256cc3c23f5d68cfe3d2370f045e8c39..346d8eadb44b1be02f0aca879dac82b16360feeb 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c @@ -664,12 +664,13 @@ static int tegra_mc_probe(struct platform_device *pdev) } isr = tegra_mc_irq; - } - err = tegra_mc_setup_timings(mc); - if (err < 0) { - dev_err(&pdev->dev, "failed to setup timings: %d\n", err); - return err; + err = tegra_mc_setup_timings(mc); + if (err < 0) { + dev_err(&pdev->dev, "failed to setup timings: %d\n", + err); + return err; + } } mc->irq = platform_get_irq(pdev, 0); diff --git a/drivers/message/fusion/mptctl.c b/drivers/message/fusion/mptctl.c index 8d22d6134a89d1abb7d55c59663d8de51ae8b550..a0a42bdc3028b891d1e24bb9038ccb48f25b8829 100644 --- a/drivers/message/fusion/mptctl.c +++ b/drivers/message/fusion/mptctl.c @@ -100,19 +100,19 @@ struct buflist { * Function prototypes. Called from OS entry point mptctl_ioctl. * arg contents specific to function. */ -static int mptctl_fw_download(unsigned long arg); -static int mptctl_getiocinfo(unsigned long arg, unsigned int cmd); -static int mptctl_gettargetinfo(unsigned long arg); -static int mptctl_readtest(unsigned long arg); -static int mptctl_mpt_command(unsigned long arg); -static int mptctl_eventquery(unsigned long arg); -static int mptctl_eventenable(unsigned long arg); -static int mptctl_eventreport(unsigned long arg); -static int mptctl_replace_fw(unsigned long arg); - -static int mptctl_do_reset(unsigned long arg); -static int mptctl_hp_hostinfo(unsigned long arg, unsigned int cmd); -static int mptctl_hp_targetinfo(unsigned long arg); +static int mptctl_fw_download(MPT_ADAPTER *iocp, unsigned long arg); +static int mptctl_getiocinfo(MPT_ADAPTER *iocp, unsigned long arg, unsigned int cmd); +static int mptctl_gettargetinfo(MPT_ADAPTER *iocp, unsigned long arg); +static int mptctl_readtest(MPT_ADAPTER *iocp, unsigned long arg); +static int mptctl_mpt_command(MPT_ADAPTER *iocp, unsigned long arg); +static int mptctl_eventquery(MPT_ADAPTER *iocp, unsigned long arg); +static int mptctl_eventenable(MPT_ADAPTER *iocp, unsigned long arg); +static int mptctl_eventreport(MPT_ADAPTER *iocp, unsigned long arg); +static int mptctl_replace_fw(MPT_ADAPTER *iocp, unsigned long arg); + +static int mptctl_do_reset(MPT_ADAPTER *iocp, unsigned long arg); +static int mptctl_hp_hostinfo(MPT_ADAPTER *iocp, unsigned long arg, unsigned int cmd); +static int mptctl_hp_targetinfo(MPT_ADAPTER *iocp, unsigned long arg); static int mptctl_probe(struct pci_dev *, const struct pci_device_id *); static void mptctl_remove(struct pci_dev *); @@ -123,8 +123,8 @@ static long compat_mpctl_ioctl(struct file *f, unsigned cmd, unsigned long arg); /* * Private function calls. */ -static int mptctl_do_mpt_command(struct mpt_ioctl_command karg, void __user *mfPtr); -static int mptctl_do_fw_download(int ioc, char __user *ufwbuf, size_t fwlen); +static int mptctl_do_mpt_command(MPT_ADAPTER *iocp, struct mpt_ioctl_command karg, void __user *mfPtr); +static int mptctl_do_fw_download(MPT_ADAPTER *iocp, char __user *ufwbuf, size_t fwlen); static MptSge_t *kbuf_alloc_2_sgl(int bytes, u32 dir, int sge_offset, int *frags, struct buflist **blp, dma_addr_t *sglbuf_dma, MPT_ADAPTER *ioc); static void kfree_sgl(MptSge_t *sgl, dma_addr_t sgl_dma, @@ -656,19 +656,19 @@ __mptctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) * by TM and FW reloads. */ if ((cmd & ~IOCSIZE_MASK) == (MPTIOCINFO & ~IOCSIZE_MASK)) { - return mptctl_getiocinfo(arg, _IOC_SIZE(cmd)); + return mptctl_getiocinfo(iocp, arg, _IOC_SIZE(cmd)); } else if (cmd == MPTTARGETINFO) { - return mptctl_gettargetinfo(arg); + return mptctl_gettargetinfo(iocp, arg); } else if (cmd == MPTTEST) { - return mptctl_readtest(arg); + return mptctl_readtest(iocp, arg); } else if (cmd == MPTEVENTQUERY) { - return mptctl_eventquery(arg); + return mptctl_eventquery(iocp, arg); } else if (cmd == MPTEVENTENABLE) { - return mptctl_eventenable(arg); + return mptctl_eventenable(iocp, arg); } else if (cmd == MPTEVENTREPORT) { - return mptctl_eventreport(arg); + return mptctl_eventreport(iocp, arg); } else if (cmd == MPTFWREPLACE) { - return mptctl_replace_fw(arg); + return mptctl_replace_fw(iocp, arg); } /* All of these commands require an interrupt or @@ -678,15 +678,15 @@ __mptctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return ret; if (cmd == MPTFWDOWNLOAD) - ret = mptctl_fw_download(arg); + ret = mptctl_fw_download(iocp, arg); else if (cmd == MPTCOMMAND) - ret = mptctl_mpt_command(arg); + ret = mptctl_mpt_command(iocp, arg); else if (cmd == MPTHARDRESET) - ret = mptctl_do_reset(arg); + ret = mptctl_do_reset(iocp, arg); else if ((cmd & ~IOCSIZE_MASK) == (HP_GETHOSTINFO & ~IOCSIZE_MASK)) - ret = mptctl_hp_hostinfo(arg, _IOC_SIZE(cmd)); + ret = mptctl_hp_hostinfo(iocp, arg, _IOC_SIZE(cmd)); else if (cmd == HP_GETTARGETINFO) - ret = mptctl_hp_targetinfo(arg); + ret = mptctl_hp_targetinfo(iocp, arg); else ret = -EINVAL; @@ -705,11 +705,10 @@ mptctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return ret; } -static int mptctl_do_reset(unsigned long arg) +static int mptctl_do_reset(MPT_ADAPTER *iocp, unsigned long arg) { struct mpt_ioctl_diag_reset __user *urinfo = (void __user *) arg; struct mpt_ioctl_diag_reset krinfo; - MPT_ADAPTER *iocp; if (copy_from_user(&krinfo, urinfo, sizeof(struct mpt_ioctl_diag_reset))) { printk(KERN_ERR MYNAM "%s@%d::mptctl_do_reset - " @@ -718,12 +717,6 @@ static int mptctl_do_reset(unsigned long arg) return -EFAULT; } - if (mpt_verify_adapter(krinfo.hdr.iocnum, &iocp) < 0) { - printk(KERN_DEBUG MYNAM "%s@%d::mptctl_do_reset - ioc%d not found!\n", - __FILE__, __LINE__, krinfo.hdr.iocnum); - return -ENODEV; /* (-6) No such device or address */ - } - dctlprintk(iocp, printk(MYIOC_s_DEBUG_FMT "mptctl_do_reset called.\n", iocp->name)); @@ -754,7 +747,7 @@ static int mptctl_do_reset(unsigned long arg) * -ENOMSG if FW upload returned bad status */ static int -mptctl_fw_download(unsigned long arg) +mptctl_fw_download(MPT_ADAPTER *iocp, unsigned long arg) { struct mpt_fw_xfer __user *ufwdl = (void __user *) arg; struct mpt_fw_xfer kfwdl; @@ -766,7 +759,7 @@ mptctl_fw_download(unsigned long arg) return -EFAULT; } - return mptctl_do_fw_download(kfwdl.iocnum, kfwdl.bufp, kfwdl.fwlen); + return mptctl_do_fw_download(iocp, kfwdl.bufp, kfwdl.fwlen); } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ @@ -784,11 +777,10 @@ mptctl_fw_download(unsigned long arg) * -ENOMSG if FW upload returned bad status */ static int -mptctl_do_fw_download(int ioc, char __user *ufwbuf, size_t fwlen) +mptctl_do_fw_download(MPT_ADAPTER *iocp, char __user *ufwbuf, size_t fwlen) { FWDownload_t *dlmsg; MPT_FRAME_HDR *mf; - MPT_ADAPTER *iocp; FWDownloadTCSGE_t *ptsge; MptSge_t *sgl, *sgIn; char *sgOut; @@ -808,17 +800,10 @@ mptctl_do_fw_download(int ioc, char __user *ufwbuf, size_t fwlen) pFWDownloadReply_t ReplyMsg = NULL; unsigned long timeleft; - if (mpt_verify_adapter(ioc, &iocp) < 0) { - printk(KERN_DEBUG MYNAM "ioctl_fwdl - ioc%d not found!\n", - ioc); - return -ENODEV; /* (-6) No such device or address */ - } else { - - /* Valid device. Get a message frame and construct the FW download message. - */ - if ((mf = mpt_get_msg_frame(mptctl_id, iocp)) == NULL) - return -EAGAIN; - } + /* Valid device. Get a message frame and construct the FW download message. + */ + if ((mf = mpt_get_msg_frame(mptctl_id, iocp)) == NULL) + return -EAGAIN; dctlprintk(iocp, printk(MYIOC_s_DEBUG_FMT "mptctl_do_fwdl called. mptctl_id = %xh.\n", iocp->name, mptctl_id)); @@ -826,8 +811,6 @@ mptctl_do_fw_download(int ioc, char __user *ufwbuf, size_t fwlen) iocp->name, ufwbuf)); dctlprintk(iocp, printk(MYIOC_s_DEBUG_FMT "DbG: kfwdl.fwlen = %d\n", iocp->name, (int)fwlen)); - dctlprintk(iocp, printk(MYIOC_s_DEBUG_FMT "DbG: kfwdl.ioc = %04xh\n", - iocp->name, ioc)); dlmsg = (FWDownload_t*) mf; ptsge = (FWDownloadTCSGE_t *) &dlmsg->SGL; @@ -1238,13 +1221,11 @@ kfree_sgl(MptSge_t *sgl, dma_addr_t sgl_dma, struct buflist *buflist, MPT_ADAPTE * -ENODEV if no such device/adapter */ static int -mptctl_getiocinfo (unsigned long arg, unsigned int data_size) +mptctl_getiocinfo (MPT_ADAPTER *ioc, unsigned long arg, unsigned int data_size) { struct mpt_ioctl_iocinfo __user *uarg = (void __user *) arg; struct mpt_ioctl_iocinfo *karg; - MPT_ADAPTER *ioc; struct pci_dev *pdev; - int iocnum; unsigned int port; int cim_rev; struct scsi_device *sdev; @@ -1272,14 +1253,6 @@ mptctl_getiocinfo (unsigned long arg, unsigned int data_size) return PTR_ERR(karg); } - if (((iocnum = mpt_verify_adapter(karg->hdr.iocnum, &ioc)) < 0) || - (ioc == NULL)) { - printk(KERN_DEBUG MYNAM "%s::mptctl_getiocinfo() @%d - ioc%d not found!\n", - __FILE__, __LINE__, iocnum); - kfree(karg); - return -ENODEV; - } - /* Verify the data transfer size is correct. */ if (karg->hdr.maxDataSize != data_size) { printk(MYIOC_s_ERR_FMT "%s@%d::mptctl_getiocinfo - " @@ -1385,15 +1358,13 @@ mptctl_getiocinfo (unsigned long arg, unsigned int data_size) * -ENODEV if no such device/adapter */ static int -mptctl_gettargetinfo (unsigned long arg) +mptctl_gettargetinfo (MPT_ADAPTER *ioc, unsigned long arg) { struct mpt_ioctl_targetinfo __user *uarg = (void __user *) arg; struct mpt_ioctl_targetinfo karg; - MPT_ADAPTER *ioc; VirtDevice *vdevice; char *pmem; int *pdata; - int iocnum; int numDevices = 0; int lun; int maxWordsLeft; @@ -1408,13 +1379,6 @@ mptctl_gettargetinfo (unsigned long arg) return -EFAULT; } - if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || - (ioc == NULL)) { - printk(KERN_DEBUG MYNAM "%s::mptctl_gettargetinfo() @%d - ioc%d not found!\n", - __FILE__, __LINE__, iocnum); - return -ENODEV; - } - dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mptctl_gettargetinfo called.\n", ioc->name)); /* Get the port number and set the maximum number of bytes @@ -1510,12 +1474,10 @@ mptctl_gettargetinfo (unsigned long arg) * -ENODEV if no such device/adapter */ static int -mptctl_readtest (unsigned long arg) +mptctl_readtest (MPT_ADAPTER *ioc, unsigned long arg) { struct mpt_ioctl_test __user *uarg = (void __user *) arg; struct mpt_ioctl_test karg; - MPT_ADAPTER *ioc; - int iocnum; if (copy_from_user(&karg, uarg, sizeof(struct mpt_ioctl_test))) { printk(KERN_ERR MYNAM "%s@%d::mptctl_readtest - " @@ -1524,13 +1486,6 @@ mptctl_readtest (unsigned long arg) return -EFAULT; } - if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || - (ioc == NULL)) { - printk(KERN_DEBUG MYNAM "%s::mptctl_readtest() @%d - ioc%d not found!\n", - __FILE__, __LINE__, iocnum); - return -ENODEV; - } - dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mptctl_readtest called.\n", ioc->name)); /* Fill in the data and return the structure to the calling @@ -1571,12 +1526,10 @@ mptctl_readtest (unsigned long arg) * -ENODEV if no such device/adapter */ static int -mptctl_eventquery (unsigned long arg) +mptctl_eventquery (MPT_ADAPTER *ioc, unsigned long arg) { struct mpt_ioctl_eventquery __user *uarg = (void __user *) arg; struct mpt_ioctl_eventquery karg; - MPT_ADAPTER *ioc; - int iocnum; if (copy_from_user(&karg, uarg, sizeof(struct mpt_ioctl_eventquery))) { printk(KERN_ERR MYNAM "%s@%d::mptctl_eventquery - " @@ -1585,13 +1538,6 @@ mptctl_eventquery (unsigned long arg) return -EFAULT; } - if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || - (ioc == NULL)) { - printk(KERN_DEBUG MYNAM "%s::mptctl_eventquery() @%d - ioc%d not found!\n", - __FILE__, __LINE__, iocnum); - return -ENODEV; - } - dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mptctl_eventquery called.\n", ioc->name)); karg.eventEntries = MPTCTL_EVENT_LOG_SIZE; @@ -1610,12 +1556,10 @@ mptctl_eventquery (unsigned long arg) /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ static int -mptctl_eventenable (unsigned long arg) +mptctl_eventenable (MPT_ADAPTER *ioc, unsigned long arg) { struct mpt_ioctl_eventenable __user *uarg = (void __user *) arg; struct mpt_ioctl_eventenable karg; - MPT_ADAPTER *ioc; - int iocnum; if (copy_from_user(&karg, uarg, sizeof(struct mpt_ioctl_eventenable))) { printk(KERN_ERR MYNAM "%s@%d::mptctl_eventenable - " @@ -1624,13 +1568,6 @@ mptctl_eventenable (unsigned long arg) return -EFAULT; } - if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || - (ioc == NULL)) { - printk(KERN_DEBUG MYNAM "%s::mptctl_eventenable() @%d - ioc%d not found!\n", - __FILE__, __LINE__, iocnum); - return -ENODEV; - } - dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mptctl_eventenable called.\n", ioc->name)); if (ioc->events == NULL) { @@ -1658,12 +1595,10 @@ mptctl_eventenable (unsigned long arg) /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ static int -mptctl_eventreport (unsigned long arg) +mptctl_eventreport (MPT_ADAPTER *ioc, unsigned long arg) { struct mpt_ioctl_eventreport __user *uarg = (void __user *) arg; struct mpt_ioctl_eventreport karg; - MPT_ADAPTER *ioc; - int iocnum; int numBytes, maxEvents, max; if (copy_from_user(&karg, uarg, sizeof(struct mpt_ioctl_eventreport))) { @@ -1673,12 +1608,6 @@ mptctl_eventreport (unsigned long arg) return -EFAULT; } - if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || - (ioc == NULL)) { - printk(KERN_DEBUG MYNAM "%s::mptctl_eventreport() @%d - ioc%d not found!\n", - __FILE__, __LINE__, iocnum); - return -ENODEV; - } dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mptctl_eventreport called.\n", ioc->name)); @@ -1712,12 +1641,10 @@ mptctl_eventreport (unsigned long arg) /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ static int -mptctl_replace_fw (unsigned long arg) +mptctl_replace_fw (MPT_ADAPTER *ioc, unsigned long arg) { struct mpt_ioctl_replace_fw __user *uarg = (void __user *) arg; struct mpt_ioctl_replace_fw karg; - MPT_ADAPTER *ioc; - int iocnum; int newFwSize; if (copy_from_user(&karg, uarg, sizeof(struct mpt_ioctl_replace_fw))) { @@ -1727,13 +1654,6 @@ mptctl_replace_fw (unsigned long arg) return -EFAULT; } - if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || - (ioc == NULL)) { - printk(KERN_DEBUG MYNAM "%s::mptctl_replace_fw() @%d - ioc%d not found!\n", - __FILE__, __LINE__, iocnum); - return -ENODEV; - } - dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mptctl_replace_fw called.\n", ioc->name)); /* If caching FW, Free the old FW image @@ -1780,12 +1700,10 @@ mptctl_replace_fw (unsigned long arg) * -ENOMEM if memory allocation error */ static int -mptctl_mpt_command (unsigned long arg) +mptctl_mpt_command (MPT_ADAPTER *ioc, unsigned long arg) { struct mpt_ioctl_command __user *uarg = (void __user *) arg; struct mpt_ioctl_command karg; - MPT_ADAPTER *ioc; - int iocnum; int rc; @@ -1796,14 +1714,7 @@ mptctl_mpt_command (unsigned long arg) return -EFAULT; } - if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || - (ioc == NULL)) { - printk(KERN_DEBUG MYNAM "%s::mptctl_mpt_command() @%d - ioc%d not found!\n", - __FILE__, __LINE__, iocnum); - return -ENODEV; - } - - rc = mptctl_do_mpt_command (karg, &uarg->MF); + rc = mptctl_do_mpt_command (ioc, karg, &uarg->MF); return rc; } @@ -1821,9 +1732,8 @@ mptctl_mpt_command (unsigned long arg) * -EPERM if SCSI I/O and target is untagged */ static int -mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr) +mptctl_do_mpt_command (MPT_ADAPTER *ioc, struct mpt_ioctl_command karg, void __user *mfPtr) { - MPT_ADAPTER *ioc; MPT_FRAME_HDR *mf = NULL; MPIHeader_t *hdr; char *psge; @@ -1832,7 +1742,7 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr) dma_addr_t dma_addr_in; dma_addr_t dma_addr_out; int sgSize = 0; /* Num SG elements */ - int iocnum, flagsLength; + int flagsLength; int sz, rc = 0; int msgContext; u16 req_idx; @@ -1847,13 +1757,6 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr) bufIn.kptr = bufOut.kptr = NULL; bufIn.len = bufOut.len = 0; - if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || - (ioc == NULL)) { - printk(KERN_DEBUG MYNAM "%s::mptctl_do_mpt_command() @%d - ioc%d not found!\n", - __FILE__, __LINE__, iocnum); - return -ENODEV; - } - spin_lock_irqsave(&ioc->taskmgmt_lock, flags); if (ioc->ioc_reset_in_progress) { spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); @@ -2418,17 +2321,15 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr) * -ENOMEM if memory allocation error */ static int -mptctl_hp_hostinfo(unsigned long arg, unsigned int data_size) +mptctl_hp_hostinfo(MPT_ADAPTER *ioc, unsigned long arg, unsigned int data_size) { hp_host_info_t __user *uarg = (void __user *) arg; - MPT_ADAPTER *ioc; struct pci_dev *pdev; char *pbuf=NULL; dma_addr_t buf_dma; hp_host_info_t karg; CONFIGPARMS cfg; ConfigPageHeader_t hdr; - int iocnum; int rc, cim_rev; ToolboxIstwiReadWriteRequest_t *IstwiRWRequest; MPT_FRAME_HDR *mf = NULL; @@ -2452,12 +2353,6 @@ mptctl_hp_hostinfo(unsigned long arg, unsigned int data_size) return -EFAULT; } - if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || - (ioc == NULL)) { - printk(KERN_DEBUG MYNAM "%s::mptctl_hp_hostinfo() @%d - ioc%d not found!\n", - __FILE__, __LINE__, iocnum); - return -ENODEV; - } dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT ": mptctl_hp_hostinfo called.\n", ioc->name)); @@ -2659,15 +2554,13 @@ mptctl_hp_hostinfo(unsigned long arg, unsigned int data_size) * -ENOMEM if memory allocation error */ static int -mptctl_hp_targetinfo(unsigned long arg) +mptctl_hp_targetinfo(MPT_ADAPTER *ioc, unsigned long arg) { hp_target_info_t __user *uarg = (void __user *) arg; SCSIDevicePage0_t *pg0_alloc; SCSIDevicePage3_t *pg3_alloc; - MPT_ADAPTER *ioc; MPT_SCSI_HOST *hd = NULL; hp_target_info_t karg; - int iocnum; int data_sz; dma_addr_t page_dma; CONFIGPARMS cfg; @@ -2681,12 +2574,6 @@ mptctl_hp_targetinfo(unsigned long arg) return -EFAULT; } - if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) || - (ioc == NULL)) { - printk(KERN_DEBUG MYNAM "%s::mptctl_hp_targetinfo() @%d - ioc%d not found!\n", - __FILE__, __LINE__, iocnum); - return -ENODEV; - } if (karg.hdr.id >= MPT_MAX_FC_DEVICES) return -EINVAL; dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "mptctl_hp_targetinfo called.\n", @@ -2854,7 +2741,7 @@ compat_mptfwxfer_ioctl(struct file *filp, unsigned int cmd, kfw.fwlen = kfw32.fwlen; kfw.bufp = compat_ptr(kfw32.bufp); - ret = mptctl_do_fw_download(kfw.iocnum, kfw.bufp, kfw.fwlen); + ret = mptctl_do_fw_download(iocp, kfw.bufp, kfw.fwlen); mutex_unlock(&iocp->ioctl_cmds.mutex); @@ -2908,7 +2795,7 @@ compat_mpt_command(struct file *filp, unsigned int cmd, /* Pass new structure to do_mpt_command */ - ret = mptctl_do_mpt_command (karg, &uarg->MF); + ret = mptctl_do_mpt_command (iocp, karg, &uarg->MF); mutex_unlock(&iocp->ioctl_cmds.mutex); diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index f8e0fa97bb31e9ad097ea66039aa6d7cd37bd5a6..aa65931142ba7d35b292e65a1bed854908941d90 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -128,7 +128,7 @@ static const struct regmap_range axp288_writeable_ranges[] = { static const struct regmap_range axp288_volatile_ranges[] = { regmap_reg_range(AXP20X_PWR_INPUT_STATUS, AXP288_POWER_REASON), regmap_reg_range(AXP288_BC_GLOBAL, AXP288_BC_GLOBAL), - regmap_reg_range(AXP288_BC_DET_STAT, AXP288_BC_DET_STAT), + regmap_reg_range(AXP288_BC_DET_STAT, AXP20X_VBUS_IPSOUT_MGMT), regmap_reg_range(AXP20X_CHRG_BAK_CTRL, AXP20X_CHRG_BAK_CTRL), regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IPSOUT_V_HIGH_L), regmap_reg_range(AXP20X_TIMER_CTRL, AXP20X_TIMER_CTRL), diff --git a/drivers/mfd/da9062-core.c b/drivers/mfd/da9062-core.c index 9f6105906c093505a718248cdfd12a35da140fcf..83c624f6033ca81ca16516310f7fc6db9ea5af6b 100644 --- a/drivers/mfd/da9062-core.c +++ b/drivers/mfd/da9062-core.c @@ -257,7 +257,7 @@ static const struct mfd_cell da9062_devs[] = { .name = "da9062-watchdog", .num_resources = ARRAY_SIZE(da9062_wdt_resources), .resources = da9062_wdt_resources, - .of_compatible = "dlg,da9062-wdt", + .of_compatible = "dlg,da9062-watchdog", }, { .name = "da9062-thermal", diff --git a/drivers/mfd/dln2.c b/drivers/mfd/dln2.c index 90e789943466a0e4002131f71f505382ebe895be..1476465ce803b1fbe19bf8fc218fb3d747188d03 100644 --- a/drivers/mfd/dln2.c +++ b/drivers/mfd/dln2.c @@ -725,6 +725,8 @@ static int dln2_probe(struct usb_interface *interface, const struct usb_device_id *usb_id) { struct usb_host_interface *hostif = interface->cur_altsetting; + struct usb_endpoint_descriptor *epin; + struct usb_endpoint_descriptor *epout; struct device *dev = &interface->dev; struct dln2_dev *dln2; int ret; @@ -734,12 +736,19 @@ static int dln2_probe(struct usb_interface *interface, hostif->desc.bNumEndpoints < 2) return -ENODEV; + epin = &hostif->endpoint[0].desc; + epout = &hostif->endpoint[1].desc; + if (!usb_endpoint_is_bulk_out(epout)) + return -ENODEV; + if (!usb_endpoint_is_bulk_in(epin)) + return -ENODEV; + dln2 = kzalloc(sizeof(*dln2), GFP_KERNEL); if (!dln2) return -ENOMEM; - dln2->ep_out = hostif->endpoint[0].desc.bEndpointAddress; - dln2->ep_in = hostif->endpoint[1].desc.bEndpointAddress; + dln2->ep_out = epout->bEndpointAddress; + dln2->ep_in = epin->bEndpointAddress; dln2->usb_dev = usb_get_dev(interface_to_usbdev(interface)); dln2->interface = interface; usb_set_intfdata(interface, dln2); diff --git a/drivers/mfd/intel-lpss-pci.c b/drivers/mfd/intel-lpss-pci.c index c37c8bb860685d5f6e75d70c126018b32d77da8a..742d6c1973f4f372e565a2bc66371d210278aac8 100644 --- a/drivers/mfd/intel-lpss-pci.c +++ b/drivers/mfd/intel-lpss-pci.c @@ -126,6 +126,18 @@ static const struct intel_lpss_platform_info apl_i2c_info = { .properties = apl_i2c_properties, }; +static struct property_entry glk_i2c_properties[] = { + PROPERTY_ENTRY_U32("i2c-sda-hold-time-ns", 313), + PROPERTY_ENTRY_U32("i2c-sda-falling-time-ns", 171), + PROPERTY_ENTRY_U32("i2c-scl-falling-time-ns", 290), + { }, +}; + +static const struct intel_lpss_platform_info glk_i2c_info = { + .clk_rate = 133000000, + .properties = glk_i2c_properties, +}; + static const struct intel_lpss_platform_info cnl_i2c_info = { .clk_rate = 216000000, .properties = spt_i2c_properties, @@ -165,14 +177,14 @@ static const struct pci_device_id intel_lpss_pci_ids[] = { { PCI_VDEVICE(INTEL, 0x1ac6), (kernel_ulong_t)&bxt_info }, { PCI_VDEVICE(INTEL, 0x1aee), (kernel_ulong_t)&bxt_uart_info }, /* GLK */ - { PCI_VDEVICE(INTEL, 0x31ac), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x31ae), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x31b0), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x31b2), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x31b4), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x31b6), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x31b8), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x31ba), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x31ac), (kernel_ulong_t)&glk_i2c_info }, + { PCI_VDEVICE(INTEL, 0x31ae), (kernel_ulong_t)&glk_i2c_info }, + { PCI_VDEVICE(INTEL, 0x31b0), (kernel_ulong_t)&glk_i2c_info }, + { PCI_VDEVICE(INTEL, 0x31b2), (kernel_ulong_t)&glk_i2c_info }, + { PCI_VDEVICE(INTEL, 0x31b4), (kernel_ulong_t)&glk_i2c_info }, + { PCI_VDEVICE(INTEL, 0x31b6), (kernel_ulong_t)&glk_i2c_info }, + { PCI_VDEVICE(INTEL, 0x31b8), (kernel_ulong_t)&glk_i2c_info }, + { PCI_VDEVICE(INTEL, 0x31ba), (kernel_ulong_t)&glk_i2c_info }, { PCI_VDEVICE(INTEL, 0x31bc), (kernel_ulong_t)&bxt_uart_info }, { PCI_VDEVICE(INTEL, 0x31be), (kernel_ulong_t)&bxt_uart_info }, { PCI_VDEVICE(INTEL, 0x31c0), (kernel_ulong_t)&bxt_uart_info }, diff --git a/drivers/mfd/intel-lpss.c b/drivers/mfd/intel-lpss.c index ff3fba16e7359c1fe17c95bf3b0d1994c90049d8..95e217e6b6d757e2e5b1621bac2be269387dec54 100644 --- a/drivers/mfd/intel-lpss.c +++ b/drivers/mfd/intel-lpss.c @@ -545,6 +545,7 @@ module_init(intel_lpss_init); static void __exit intel_lpss_exit(void) { + ida_destroy(&intel_lpss_devid_ida); debugfs_remove(intel_lpss_debugfs); } module_exit(intel_lpss_exit); diff --git a/drivers/mfd/rn5t618.c b/drivers/mfd/rn5t618.c index f4037d42a60fa7bcc63f034e01779d0557597110..dd4251f105e024b4d29c745f5a0f7518da7c2006 100644 --- a/drivers/mfd/rn5t618.c +++ b/drivers/mfd/rn5t618.c @@ -32,6 +32,7 @@ static bool rn5t618_volatile_reg(struct device *dev, unsigned int reg) case RN5T618_WATCHDOGCNT: case RN5T618_DCIRQ: case RN5T618_ILIMDATAH ... RN5T618_AIN0DATAL: + case RN5T618_ADCCNT3: case RN5T618_IR_ADC1 ... RN5T618_IR_ADC3: case RN5T618_IR_GPR: case RN5T618_IR_GPF: diff --git a/drivers/misc/aspeed-lpc-snoop.c b/drivers/misc/aspeed-lpc-snoop.c index 2feb4347d67f1d5cca628fbdbed024d787eb5759..c10be21a1663da839a1c55d25ba9a84ff7e83ce2 100644 --- a/drivers/misc/aspeed-lpc-snoop.c +++ b/drivers/misc/aspeed-lpc-snoop.c @@ -101,13 +101,13 @@ static ssize_t snoop_file_read(struct file *file, char __user *buffer, return ret ? ret : copied; } -static unsigned int snoop_file_poll(struct file *file, +static __poll_t snoop_file_poll(struct file *file, struct poll_table_struct *pt) { struct aspeed_lpc_snoop_channel *chan = snoop_file_to_chan(file); poll_wait(file, &chan->wq, pt); - return !kfifo_is_empty(&chan->fifo) ? POLLIN : 0; + return !kfifo_is_empty(&chan->fifo) ? EPOLLIN : 0; } static const struct file_operations snoop_fops = { diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c index 5a17bfeb80d398c023278ff9f0ed691d6785ef53..471263ffd9c92d1afaaad170f06e0e29f8d96f78 100644 --- a/drivers/misc/enclosure.c +++ b/drivers/misc/enclosure.c @@ -419,10 +419,9 @@ int enclosure_remove_device(struct enclosure_device *edev, struct device *dev) cdev = &edev->component[i]; if (cdev->dev == dev) { enclosure_remove_links(cdev); - device_del(&cdev->cdev); put_device(dev); cdev->dev = NULL; - return device_add(&cdev->cdev); + return 0; } } return -ENODEV; diff --git a/drivers/misc/fpr_FingerprintCard/fpc1020_platform_tee.c b/drivers/misc/fpr_FingerprintCard/fpc1020_platform_tee.c index d49d8577a8d8a04bab83c04fe1b63af613ed9d19..0eda552934a8d510a561846115eb256245fc633e 100644 --- a/drivers/misc/fpr_FingerprintCard/fpc1020_platform_tee.c +++ b/drivers/misc/fpr_FingerprintCard/fpc1020_platform_tee.c @@ -78,7 +78,7 @@ struct fpc1020_data { struct pinctrl *fingerprint_pinctrl; struct pinctrl_state *pinctrl_state[ARRAY_SIZE(pctl_names)]; struct regulator *vreg[ARRAY_SIZE(vreg_conf)]; - struct wakeup_source ttw_wl; + struct wakeup_source *ttw_wl; struct mutex lock; /* To set/get exported values in sysfs */ int irq_gpio; int rst_gpio; @@ -384,7 +384,7 @@ static ssize_t handle_wakelock_store(struct device *dev, min(count, strlen(RELEASE_WAKELOCK_W_V)))) { if (fpc1020->nbr_irqs_received_counter_start == fpc1020->nbr_irqs_received) { - __pm_relax(&fpc1020->ttw_wl); + __pm_relax(fpc1020->ttw_wl); } else { dev_dbg(dev, "Ignore releasing of wakelock %d != %d", fpc1020->nbr_irqs_received_counter_start, @@ -392,7 +392,7 @@ static ssize_t handle_wakelock_store(struct device *dev, } } else if (!memcmp(buf, RELEASE_WAKELOCK, min(count, strlen(RELEASE_WAKELOCK)))) { - __pm_relax(&fpc1020->ttw_wl); + __pm_relax(fpc1020->ttw_wl); } else if (!memcmp(buf, START_IRQS_RECEIVED_CNT, min(count, strlen(START_IRQS_RECEIVED_CNT)))) { fpc1020->nbr_irqs_received_counter_start = @@ -443,7 +443,7 @@ static irqreturn_t fpc1020_irq_handler(int irq, void *handle) mutex_lock(&fpc1020->lock); if (atomic_read(&fpc1020->wakeup_enabled)) { fpc1020->nbr_irqs_received++; - __pm_wakeup_event(&fpc1020->ttw_wl, + __pm_wakeup_event(fpc1020->ttw_wl, msecs_to_jiffies(FPC_TTW_HOLD_TIME)); } mutex_unlock(&fpc1020->lock); @@ -546,7 +546,9 @@ static int fpc1020_probe(struct platform_device *pdev) enable_irq_wake(gpio_to_irq(fpc1020->irq_gpio)); - wakeup_source_init(&fpc1020->ttw_wl, "fpc_ttw_wl"); + fpc1020->ttw_wl = wakeup_source_register(dev, "fpc_ttw_wl"); + if (!fpc1020->ttw_wl) + return -ENOMEM; rc = sysfs_create_group(&dev->kobj, &attribute_group); if (rc) { @@ -570,7 +572,7 @@ static int fpc1020_remove(struct platform_device *pdev) sysfs_remove_group(&pdev->dev.kobj, &attribute_group); mutex_destroy(&fpc1020->lock); - wakeup_source_trash(&fpc1020->ttw_wl); + wakeup_source_unregister(fpc1020->ttw_wl); vreg_setup(fpc1020, "vdd_ana", false); vreg_setup(fpc1020, "vdd_io", false); vreg_setup(fpc1020, "vcc_spi", false); diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h index 9c40424200224225592227b9d7fb1d4aae8a084c..d80372d21c14f09e6d4abaca70d71957e2ecbe74 100644 --- a/drivers/misc/mei/hw-me-regs.h +++ b/drivers/misc/mei/hw-me-regs.h @@ -141,8 +141,12 @@ #define MEI_DEV_ID_CMP_LP 0x02e0 /* Comet Point LP */ #define MEI_DEV_ID_CMP_LP_3 0x02e4 /* Comet Point LP 3 (iTouch) */ + #define MEI_DEV_ID_CMP_V 0xA3BA /* Comet Point Lake V */ +#define MEI_DEV_ID_CMP_H 0x06e0 /* Comet Lake H */ +#define MEI_DEV_ID_CMP_H_3 0x06e4 /* Comet Lake H 3 (iTouch) */ + #define MEI_DEV_ID_ICP_LP 0x34E0 /* Ice Lake Point LP */ #define MEI_DEV_ID_TGP_LP 0xA0E0 /* Tiger Lake Point LP */ diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 4d77a6ae183a95e313abb362c4c5e4a7935f9e6b..87281b3695e607c023ab1fd8a0bc6a5d29bf971b 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -599,10 +599,10 @@ static __poll_t mei_poll(struct file *file, poll_table *wait) mei_cl_read_start(cl, mei_cl_mtu(cl), file); } - if (req_events & (POLLOUT | POLLWRNORM)) { + if (req_events & (EPOLLOUT | EPOLLWRNORM)) { poll_wait(file, &cl->tx_wait, wait); if (cl->tx_cb_queued < dev->tx_queue_limit) - mask |= POLLOUT | POLLWRNORM; + mask |= EPOLLOUT | EPOLLWRNORM; } out: diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 41a10e392839ec713ce13f1bb9e93b973d05a81b..3498c10b8263ba43dffd398ece29dc5e7e4b8f70 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -108,6 +108,8 @@ static const struct pci_device_id mei_me_pci_tbl[] = { {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_LP, MEI_ME_PCH12_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_LP_3, MEI_ME_PCH8_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_V, MEI_ME_PCH12_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_H, MEI_ME_PCH12_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_H_3, MEI_ME_PCH8_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_ICP_LP, MEI_ME_PCH12_CFG)}, diff --git a/drivers/misc/mic/card/mic_x100.c b/drivers/misc/mic/card/mic_x100.c index b9f0710ffa6b0f80f256994c968d783242723243..4007adc666f377d0ef4ddbf4f8bbdcb773abfa39 100644 --- a/drivers/misc/mic/card/mic_x100.c +++ b/drivers/misc/mic/card/mic_x100.c @@ -249,6 +249,9 @@ static int __init mic_probe(struct platform_device *pdev) mdrv->dev = &pdev->dev; snprintf(mdrv->name, sizeof(mic_driver_name), mic_driver_name); + /* FIXME: use dma_set_mask_and_coherent() and check result */ + dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + mdev->mmio.pa = MIC_X100_MMIO_BASE; mdev->mmio.len = MIC_X100_MMIO_LEN; mdev->mmio.va = devm_ioremap(&pdev->dev, MIC_X100_MMIO_BASE, @@ -294,18 +297,6 @@ static void mic_platform_shutdown(struct platform_device *pdev) mic_remove(pdev); } -static u64 mic_dma_mask = DMA_BIT_MASK(64); - -static struct platform_device mic_platform_dev = { - .name = mic_driver_name, - .id = 0, - .num_resources = 0, - .dev = { - .dma_mask = &mic_dma_mask, - .coherent_dma_mask = DMA_BIT_MASK(64), - }, -}; - static struct platform_driver __refdata mic_platform_driver = { .probe = mic_probe, .remove = mic_remove, @@ -315,6 +306,8 @@ static struct platform_driver __refdata mic_platform_driver = { }, }; +static struct platform_device *mic_platform_dev; + static int __init mic_init(void) { int ret; @@ -328,9 +321,12 @@ static int __init mic_init(void) request_module("mic_x100_dma"); mic_init_card_debugfs(); - ret = platform_device_register(&mic_platform_dev); + + mic_platform_dev = platform_device_register_simple(mic_driver_name, + 0, NULL, 0); + ret = PTR_ERR_OR_ZERO(mic_platform_dev); if (ret) { - pr_err("platform_device_register ret %d\n", ret); + pr_err("platform_device_register_full ret %d\n", ret); goto cleanup_debugfs; } ret = platform_driver_register(&mic_platform_driver); @@ -341,7 +337,7 @@ static int __init mic_init(void) return ret; device_unregister: - platform_device_unregister(&mic_platform_dev); + platform_device_unregister(mic_platform_dev); cleanup_debugfs: mic_exit_card_debugfs(); done: @@ -351,7 +347,7 @@ static int __init mic_init(void) static void __exit mic_exit(void) { platform_driver_unregister(&mic_platform_driver); - platform_device_unregister(&mic_platform_dev); + platform_device_unregister(mic_platform_dev); mic_exit_card_debugfs(); } diff --git a/drivers/misc/sgi-xp/xpc_partition.c b/drivers/misc/sgi-xp/xpc_partition.c index 0c3ef6f1df54662f4ab3002dddb4ff20e1449c0d..519826ba13786370cf520041f2dbe9f46fdfcd01 100644 --- a/drivers/misc/sgi-xp/xpc_partition.c +++ b/drivers/misc/sgi-xp/xpc_partition.c @@ -70,7 +70,7 @@ xpc_get_rsvd_page_pa(int nasid) unsigned long rp_pa = nasid; /* seed with nasid */ size_t len = 0; size_t buf_len = 0; - void *buf = buf; + void *buf = NULL; void *buf_base = NULL; enum xp_retval (*get_partition_rsvd_page_pa) (void *, u64 *, unsigned long *, size_t *) = diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index c5ab22fc03761ae6b1d0c38774653b7b5e98ac88..2c47833979ec1f8bff339c0ad7e8b9a5fc5175fd 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -410,8 +410,6 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) if (mmc_gpio_alloc(host)) { put_device(&host->class_dev); - ida_simple_remove(&mmc_host_ida, host->index); - kfree(host); return NULL; } diff --git a/drivers/mmc/core/quirks.h b/drivers/mmc/core/quirks.h index 74b50b13a813c5f22b0e4a8c64636c44d2c7eeb1..b920009ba97454f3133f22afd078a30317de4a1c 100644 --- a/drivers/mmc/core/quirks.h +++ b/drivers/mmc/core/quirks.h @@ -145,7 +145,14 @@ static const struct mmc_fixup mmc_ext_csd_fixups[] = { END_FIXUP }; + static const struct mmc_fixup sdio_fixup_methods[] = { + SDIO_FIXUP(SDIO_VENDOR_ID_TI_WL1251, SDIO_DEVICE_ID_TI_WL1251, + add_quirk, MMC_QUIRK_NONSTD_FUNC_IF), + + SDIO_FIXUP(SDIO_VENDOR_ID_TI_WL1251, SDIO_DEVICE_ID_TI_WL1251, + add_quirk, MMC_QUIRK_DISABLE_CD), + SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271, add_quirk, MMC_QUIRK_NONSTD_FUNC_IF), diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index 72d286cab6338027b0caef02e948935c82e03852..274a7114ef980f1b79b63f1e5bf2ff2f53e4c86a 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -162,7 +162,8 @@ void mmc_gpiod_request_cd_irq(struct mmc_host *host) ctx->cd_gpio_isr = mmc_gpio_cd_irqt; ret = devm_request_threaded_irq(host->parent, irq, NULL, ctx->cd_gpio_isr, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | + IRQF_ONESHOT | IRQF_SHARED, ctx->cd_label, host); if (ret < 0) irq = ret; diff --git a/drivers/mmc/host/cqhci-crypto-qti.c b/drivers/mmc/host/cqhci-crypto-qti.c index 7be53353ed5b866a3f51ec8f718411bbc89c25cc..73825257025685e89bd8d7f6efbd89f41ae8776e 100644 --- a/drivers/mmc/host/cqhci-crypto-qti.c +++ b/drivers/mmc/host/cqhci-crypto-qti.c @@ -220,8 +220,10 @@ int cqhci_host_init_crypto_qti_spec(struct cqhci_host *host, host->crypto_cap_array[cap_idx].sdus_mask * 512; } - host->ksm = keyslot_manager_create(cqhci_num_keyslots(host), ksm_ops, - crypto_modes_supported, host); + host->ksm = keyslot_manager_create(host->mmc->parent, + cqhci_num_keyslots(host), + ksm_ops, crypto_modes_supported, + host); if (!host->ksm) { err = -ENOMEM; diff --git a/drivers/mmc/host/cqhci-crypto.c b/drivers/mmc/host/cqhci-crypto.c index 5b06a6b2cb56155bb2b07df06adb92d5f28e146b..e3c9152960018c0e2433bc98576b970109c21983 100644 --- a/drivers/mmc/host/cqhci-crypto.c +++ b/drivers/mmc/host/cqhci-crypto.c @@ -334,8 +334,10 @@ int cqhci_host_init_crypto_spec(struct cqhci_host *host, cqhci_crypto_clear_all_keyslots(host); - host->ksm = keyslot_manager_create(cqhci_num_keyslots(host), ksm_ops, - crypto_modes_supported, host); + host->ksm = keyslot_manager_create(host->mmc->parent, + cqhci_num_keyslots(host), + ksm_ops, crypto_modes_supported, + host); if (!host->ksm) { err = -ENOMEM; diff --git a/drivers/mmc/host/cqhci.c b/drivers/mmc/host/cqhci.c index 55bfcfd7bc73682fea18018e02ece117413f0ac3..6070393a8493898d77d83d8636d732e821334e04 100644 --- a/drivers/mmc/host/cqhci.c +++ b/drivers/mmc/host/cqhci.c @@ -753,7 +753,18 @@ static int cqhci_request(struct mmc_host *mmc, struct mmc_request *mrq) static void cqhci_crypto_update_queue(struct mmc_host *mmc, struct request_queue *queue) { - struct cqhci_host *cq_host = mmc->cqe_private; + struct cqhci_host *cq_host; + + if (!mmc) { + pr_err("%s mmc host is NULL\n", __func__); + return; + } + + cq_host = mmc->cqe_private; + if (!cq_host) { + pr_err("%s cq host is NULL\n", __func__); + return; + } if (cq_host->caps & CQHCI_CAP_CRYPTO_SUPPORT) { if (queue) diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index ea254d00541f12c0cc9b0b8bbf0db885febd3cb1..24795454d106617e6614656b4a9d4fa4b85b095f 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -1154,17 +1154,22 @@ static void mmc_spi_initsequence(struct mmc_spi_host *host) * SPI protocol. Another is that when chipselect is released while * the card returns BUSY status, the clock must issue several cycles * with chipselect high before the card will stop driving its output. + * + * SPI_CS_HIGH means "asserted" here. In some cases like when using + * GPIOs for chip select, SPI_CS_HIGH is set but this will be logically + * inverted by gpiolib, so if we want to ascertain to drive it high + * we should toggle the default with an XOR as we do here. */ - host->spi->mode |= SPI_CS_HIGH; + host->spi->mode ^= SPI_CS_HIGH; if (spi_setup(host->spi) != 0) { /* Just warn; most cards work without it. */ dev_warn(&host->spi->dev, "can't change chip-select polarity\n"); - host->spi->mode &= ~SPI_CS_HIGH; + host->spi->mode ^= SPI_CS_HIGH; } else { mmc_spi_readbytes(host, 18); - host->spi->mode &= ~SPI_CS_HIGH; + host->spi->mode ^= SPI_CS_HIGH; if (spi_setup(host->spi) != 0) { /* Wot, we can't get the same setup we had before? */ dev_err(&host->spi->dev, diff --git a/drivers/mmc/host/sdhci-brcmstb.c b/drivers/mmc/host/sdhci-brcmstb.c index 552bddc5096ce2e2d2638483f3baf7317115d5bf..1cd10356fc14fd1ddbe9d20306b913b684de0add 100644 --- a/drivers/mmc/host/sdhci-brcmstb.c +++ b/drivers/mmc/host/sdhci-brcmstb.c @@ -55,7 +55,9 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev) } sdhci_get_of_property(pdev); - mmc_of_parse(host->mmc); + res = mmc_of_parse(host->mmc); + if (res) + goto err; /* * Supply the existing CAPS, but clear the UHS modes. This diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 9d097b8a629db81ebf762373912e2987b3073655..c103a66d6e81226dab523adc06cdd9796d72e3c4 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -1283,6 +1283,20 @@ void sdhci_msm_mm_dbg_configure(struct sdhci_host *host, u32 mask, readl_relaxed(host->ioaddr + SDCC_DEBUG_FEATURE_CFG_REG); } +/* Dummy func for Mask and Match show */ +static ssize_t show_mask_and_match(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sdhci_host *host = dev_get_drvdata(dev); + + if (!host) + return -EINVAL; + + pr_info("%s: M&M show func\n", mmc_hostname(host->mmc)); + + return 0; +} + static ssize_t store_mask_and_match(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -1335,6 +1349,8 @@ void sdhci_msm_enter_dbg_mode(struct sdhci_host *host) SDCC_IP_CATALOG)); if (minor < 2 || msm_host->debug_mode_enabled) return; + if (!(host->quirks2 & SDHCI_QUIRK2_USE_DBG_FEATURE)) + return; /* Enable debug mode */ writel_relaxed(ENABLE_DBG, @@ -1378,6 +1394,8 @@ void sdhci_msm_exit_dbg_mode(struct sdhci_host *host) SDCC_IP_CATALOG)); if (minor < 2 || !msm_host->debug_mode_enabled) return; + if (!(host->quirks2 & SDHCI_QUIRK2_USE_DBG_FEATURE)) + return; /* Exit debug mode */ writel_relaxed(DISABLE_DBG, @@ -4978,8 +4996,6 @@ static void sdhci_msm_hw_reset(struct sdhci_host *host) return; } - if (!msm_host->debug_mode_enabled) - return; msm_host->reg_store = true; sdhci_msm_exit_dbg_mode(host); sdhci_msm_registers_save(host); @@ -5257,6 +5273,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) void __iomem *tlmm_mem; unsigned long flags; bool force_probe; + u32 minor; pr_debug("%s: Enter %s\n", dev_name(&pdev->dev), __func__); msm_host = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_msm_host), @@ -5556,6 +5573,9 @@ static int sdhci_msm_probe(struct platform_device *pdev) if (host->quirks2 & SDHCI_QUIRK2_ALWAYS_USE_BASE_CLOCK) host->quirks2 |= SDHCI_QUIRK2_DIVIDE_TOUT_BY_4; + minor = IPCAT_MINOR_MASK(readl_relaxed(host->ioaddr + + SDCC_IP_CATALOG)); + host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION)); dev_dbg(&pdev->dev, "Host Version: 0x%x Vendor Version 0x%x\n", host_version, ((host_version & SDHCI_VENDOR_VER_MASK) >> @@ -5758,8 +5778,8 @@ static int sdhci_msm_probe(struct platform_device *pdev) device_remove_file(&pdev->dev, &msm_host->auto_cmd21_attr); } - if (IPCAT_MINOR_MASK(readl_relaxed(host->ioaddr + - SDCC_IP_CATALOG)) >= 2) { + if (minor >= 2) { + msm_host->mask_and_match.show = show_mask_and_match; msm_host->mask_and_match.store = store_mask_and_match; sysfs_attr_init(&msm_host->mask_and_match.attr); msm_host->mask_and_match.attr.name = "mask_and_match"; diff --git a/drivers/mmc/host/sdhci-of-at91.c b/drivers/mmc/host/sdhci-of-at91.c index 1ebcf0eb781ea6cae8dc9cf4ae12b53cfdf03b96..04e88d4796fa98769606f9770b03b608c11e0dc4 100644 --- a/drivers/mmc/host/sdhci-of-at91.c +++ b/drivers/mmc/host/sdhci-of-at91.c @@ -332,19 +332,22 @@ static int sdhci_at91_probe(struct platform_device *pdev) priv->mainck = devm_clk_get(&pdev->dev, "baseclk"); if (IS_ERR(priv->mainck)) { dev_err(&pdev->dev, "failed to get baseclk\n"); - return PTR_ERR(priv->mainck); + ret = PTR_ERR(priv->mainck); + goto sdhci_pltfm_free; } priv->hclock = devm_clk_get(&pdev->dev, "hclock"); if (IS_ERR(priv->hclock)) { dev_err(&pdev->dev, "failed to get hclock\n"); - return PTR_ERR(priv->hclock); + ret = PTR_ERR(priv->hclock); + goto sdhci_pltfm_free; } priv->gck = devm_clk_get(&pdev->dev, "multclk"); if (IS_ERR(priv->gck)) { dev_err(&pdev->dev, "failed to get multclk\n"); - return PTR_ERR(priv->gck); + ret = PTR_ERR(priv->gck); + goto sdhci_pltfm_free; } ret = sdhci_at91_set_clks_presets(&pdev->dev); diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index 908b23e6a03cc48ccedd134c11cdcaa972c474a4..14d749a0de95453f1092cad92b179265f92ab37e 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -177,7 +177,7 @@ static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask) misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_DDR50; if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR104) misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR104; - if (soc_data->nvquirks & SDHCI_MISC_CTRL_ENABLE_SDR50) + if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50) clk_ctrl |= SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE; } diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 5e8263f1a8e6d38b6ec85867aa7fded986fa8125..ff7f9b2b7ed58a85a03a9fa8b4de51f98ce8426c 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -892,12 +892,6 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd, if (!data && !cmd->busy_timeout) return 0xE; - /* During initialization, don't use max timeout as the clock is slow */ - if ((host->quirks2 & SDHCI_QUIRK2_USE_RESERVED_MAX_TIMEOUT) && - (host->clock > 400000)) { - return 0xF; - } - /* timeout in us */ target_timeout = sdhci_target_timeout(host, cmd, data); @@ -927,9 +921,9 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd, break; } - if (count >= 0xF) { - if (!(host->quirks2 & SDHCI_QUIRK2_DISABLE_HW_TIMEOUT) || - !(host->quirks2 & SDHCI_QUIRK2_USE_RESERVED_MAX_TIMEOUT)) + if (count >= 0xF && + !(host->quirks2 & SDHCI_QUIRK2_USE_RESERVED_MAX_TIMEOUT)) { + if (!(host->quirks2 & SDHCI_QUIRK2_DISABLE_HW_TIMEOUT)) DBG("Too large timeout 0x%x requested for CMD%d!\n", count, cmd->opcode); count = 0xE; @@ -4348,11 +4342,13 @@ int sdhci_setup_host(struct sdhci_host *host) if (host->ops->get_min_clock) mmc->f_min = host->ops->get_min_clock(host); else if (host->version >= SDHCI_SPEC_300) { - if (host->clk_mul) { - mmc->f_min = (host->max_clk * host->clk_mul) / 1024; + if (host->clk_mul) max_clk = host->max_clk * host->clk_mul; - } else - mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_300; + /* + * Divided Clock Mode minimum clock rate is always less than + * Programmable Clock Mode minimum clock rate. + */ + mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_300; } else mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_200; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index ab25f13fff97f03eb7125c5d5ebb581878c109f8..0e179f59a9a7826a79b4637a838352b7c0ca70a2 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -548,6 +548,11 @@ struct sdhci_host { */ #define SDHCI_QUIRK2_USE_PIO_FOR_EMMC_TUNING (1 << 29) +/* + * Use QTI specific SDCC debug feature. + */ +#define SDHCI_QUIRK2_USE_DBG_FEATURE (1 << 30) + int irq; /* Device IRQ */ void __iomem *ioaddr; /* Mapped address */ char *bounce_buffer; /* For packing SDMA reads/writes */ diff --git a/drivers/mtd/devices/mchp23k256.c b/drivers/mtd/devices/mchp23k256.c index 75f71d166fd6b31d871a0116073223c7554ecdd7..c2f52aed80b9bba4bf4485078b01f4236d4448d8 100644 --- a/drivers/mtd/devices/mchp23k256.c +++ b/drivers/mtd/devices/mchp23k256.c @@ -68,15 +68,17 @@ static int mchp23k256_write(struct mtd_info *mtd, loff_t to, size_t len, struct spi_transfer transfer[2] = {}; struct spi_message message; unsigned char command[MAX_CMD_SIZE]; - int ret; + int ret, cmd_len; spi_message_init(&message); + cmd_len = mchp23k256_cmdsz(flash); + command[0] = MCHP23K256_CMD_WRITE; mchp23k256_addr2cmd(flash, to, command); transfer[0].tx_buf = command; - transfer[0].len = mchp23k256_cmdsz(flash); + transfer[0].len = cmd_len; spi_message_add_tail(&transfer[0], &message); transfer[1].tx_buf = buf; @@ -92,8 +94,8 @@ static int mchp23k256_write(struct mtd_info *mtd, loff_t to, size_t len, if (ret) return ret; - if (retlen && message.actual_length > sizeof(command)) - *retlen += message.actual_length - sizeof(command); + if (retlen && message.actual_length > cmd_len) + *retlen += message.actual_length - cmd_len; return 0; } @@ -105,16 +107,18 @@ static int mchp23k256_read(struct mtd_info *mtd, loff_t from, size_t len, struct spi_transfer transfer[2] = {}; struct spi_message message; unsigned char command[MAX_CMD_SIZE]; - int ret; + int ret, cmd_len; spi_message_init(&message); + cmd_len = mchp23k256_cmdsz(flash); + memset(&transfer, 0, sizeof(transfer)); command[0] = MCHP23K256_CMD_READ; mchp23k256_addr2cmd(flash, from, command); transfer[0].tx_buf = command; - transfer[0].len = mchp23k256_cmdsz(flash); + transfer[0].len = cmd_len; spi_message_add_tail(&transfer[0], &message); transfer[1].rx_buf = buf; @@ -130,8 +134,8 @@ static int mchp23k256_read(struct mtd_info *mtd, loff_t from, size_t len, if (ret) return ret; - if (retlen && message.actual_length > sizeof(command)) - *retlen += message.actual_length - sizeof(command); + if (retlen && message.actual_length > cmd_len) + *retlen += message.actual_length - cmd_len; return 0; } diff --git a/drivers/mtd/nand/onenand/omap2.c b/drivers/mtd/nand/onenand/omap2.c index 321137158ff3161a1c89aa7f1a6ad51fa91647c4..a1ae05ddfeabeadefcc13908a599afcd3680f16a 100644 --- a/drivers/mtd/nand/onenand/omap2.c +++ b/drivers/mtd/nand/onenand/omap2.c @@ -341,7 +341,8 @@ static inline int omap2_onenand_dma_transfer(struct omap2_onenand *c, struct dma_async_tx_descriptor *tx; dma_cookie_t cookie; - tx = dmaengine_prep_dma_memcpy(c->dma_chan, dst, src, count, 0); + tx = dmaengine_prep_dma_memcpy(c->dma_chan, dst, src, count, + DMA_CTRL_ACK | DMA_PREP_INTERRUPT); if (!tx) { dev_err(&c->pdev->dev, "Failed to prepare DMA memcpy\n"); return -EIO; diff --git a/drivers/mtd/nand/onenand/onenand_base.c b/drivers/mtd/nand/onenand/onenand_base.c index 4ca4b194e7d724411de9ad2fbc43fbb7a338cb39..db3587d30691da97f88e30dbe403ab38f0505d8b 100644 --- a/drivers/mtd/nand/onenand/onenand_base.c +++ b/drivers/mtd/nand/onenand/onenand_base.c @@ -1251,44 +1251,44 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, stats = mtd->ecc_stats; - /* Read-while-load method */ + /* Read-while-load method */ - /* Do first load to bufferRAM */ - if (read < len) { - if (!onenand_check_bufferram(mtd, from)) { + /* Do first load to bufferRAM */ + if (read < len) { + if (!onenand_check_bufferram(mtd, from)) { this->command(mtd, ONENAND_CMD_READ, from, writesize); - ret = this->wait(mtd, FL_READING); - onenand_update_bufferram(mtd, from, !ret); + ret = this->wait(mtd, FL_READING); + onenand_update_bufferram(mtd, from, !ret); if (mtd_is_eccerr(ret)) ret = 0; - } - } + } + } thislen = min_t(int, writesize, len - read); column = from & (writesize - 1); if (column + thislen > writesize) thislen = writesize - column; - while (!ret) { - /* If there is more to load then start next load */ - from += thislen; - if (read + thislen < len) { + while (!ret) { + /* If there is more to load then start next load */ + from += thislen; + if (read + thislen < len) { this->command(mtd, ONENAND_CMD_READ, from, writesize); - /* - * Chip boundary handling in DDP - * Now we issued chip 1 read and pointed chip 1 + /* + * Chip boundary handling in DDP + * Now we issued chip 1 read and pointed chip 1 * bufferram so we have to point chip 0 bufferram. - */ - if (ONENAND_IS_DDP(this) && - unlikely(from == (this->chipsize >> 1))) { - this->write_word(ONENAND_DDP_CHIP0, this->base + ONENAND_REG_START_ADDRESS2); - boundary = 1; - } else - boundary = 0; - ONENAND_SET_PREV_BUFFERRAM(this); - } - /* While load is going, read from last bufferRAM */ - this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen); + */ + if (ONENAND_IS_DDP(this) && + unlikely(from == (this->chipsize >> 1))) { + this->write_word(ONENAND_DDP_CHIP0, this->base + ONENAND_REG_START_ADDRESS2); + boundary = 1; + } else + boundary = 0; + ONENAND_SET_PREV_BUFFERRAM(this); + } + /* While load is going, read from last bufferRAM */ + this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen); /* Read oob area if needed */ if (oobbuf) { @@ -1304,24 +1304,24 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, oobcolumn = 0; } - /* See if we are done */ - read += thislen; - if (read == len) - break; - /* Set up for next read from bufferRAM */ - if (unlikely(boundary)) - this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2); - ONENAND_SET_NEXT_BUFFERRAM(this); - buf += thislen; + /* See if we are done */ + read += thislen; + if (read == len) + break; + /* Set up for next read from bufferRAM */ + if (unlikely(boundary)) + this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2); + ONENAND_SET_NEXT_BUFFERRAM(this); + buf += thislen; thislen = min_t(int, writesize, len - read); - column = 0; - cond_resched(); - /* Now wait for load */ - ret = this->wait(mtd, FL_READING); - onenand_update_bufferram(mtd, from, !ret); + column = 0; + cond_resched(); + /* Now wait for load */ + ret = this->wait(mtd, FL_READING); + onenand_update_bufferram(mtd, from, !ret); if (mtd_is_eccerr(ret)) ret = 0; - } + } /* * Return success, if no ECC failures, else -EBADMSG diff --git a/drivers/mtd/parsers/sharpslpart.c b/drivers/mtd/parsers/sharpslpart.c index e5ea6127ab5a58f6ebfea5ad2278a67bd068977c..671a61845bd5dc7ceeab51342a417a1f64d67a9c 100644 --- a/drivers/mtd/parsers/sharpslpart.c +++ b/drivers/mtd/parsers/sharpslpart.c @@ -165,10 +165,10 @@ static int sharpsl_nand_get_logical_num(u8 *oob) static int sharpsl_nand_init_ftl(struct mtd_info *mtd, struct sharpsl_ftl *ftl) { - unsigned int block_num, log_num, phymax; + unsigned int block_num, phymax; + int i, ret, log_num; loff_t block_adr; u8 *oob; - int i, ret; oob = kzalloc(mtd->oobsize, GFP_KERNEL); if (!oob) diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 2e183425facd855860e0f4d6d182954f6a9850e1..ff641c06003a8fb81f8ee6264a8c9c97a24c868d 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -1294,7 +1294,7 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { struct spi_nor *nor = mtd_to_spi_nor(mtd); - int ret; + ssize_t ret; dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len); @@ -1523,7 +1523,7 @@ static int macronix_quad_enable(struct spi_nor *nor) */ static int write_sr_cr(struct spi_nor *nor, u8 *sr_cr) { - int ret; + ssize_t ret; write_enable(nor); diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c index 462526a105371e26e2059f280f21801a57af22c3..8e292992f84c7ad365e09726ac605d9637115b0c 100644 --- a/drivers/mtd/ubi/fastmap.c +++ b/drivers/mtd/ubi/fastmap.c @@ -73,7 +73,7 @@ static int self_check_seen(struct ubi_device *ubi, unsigned long *seen) return 0; for (pnum = 0; pnum < ubi->peb_count; pnum++) { - if (test_bit(pnum, seen) && ubi->lookuptbl[pnum]) { + if (!test_bit(pnum, seen) && ubi->lookuptbl[pnum]) { ubi_err(ubi, "self-check failed for PEB %d, fastmap didn't see it", pnum); ret = -EINVAL; } @@ -1146,7 +1146,7 @@ static int ubi_write_fastmap(struct ubi_device *ubi, struct rb_node *tmp_rb; int ret, i, j, free_peb_count, used_peb_count, vol_count; int scrub_peb_count, erase_peb_count; - unsigned long *seen_pebs = NULL; + unsigned long *seen_pebs; fm_raw = ubi->fm_buf; memset(ubi->fm_buf, 0, ubi->fm_size); @@ -1160,7 +1160,7 @@ static int ubi_write_fastmap(struct ubi_device *ubi, dvbuf = new_fm_vbuf(ubi, UBI_FM_DATA_VOLUME_ID); if (!dvbuf) { ret = -ENOMEM; - goto out_kfree; + goto out_free_avbuf; } avhdr = ubi_get_vid_hdr(avbuf); @@ -1169,7 +1169,7 @@ static int ubi_write_fastmap(struct ubi_device *ubi, seen_pebs = init_seen(ubi); if (IS_ERR(seen_pebs)) { ret = PTR_ERR(seen_pebs); - goto out_kfree; + goto out_free_dvbuf; } spin_lock(&ubi->volumes_lock); @@ -1337,7 +1337,7 @@ static int ubi_write_fastmap(struct ubi_device *ubi, ret = ubi_io_write_vid_hdr(ubi, new_fm->e[0]->pnum, avbuf); if (ret) { ubi_err(ubi, "unable to write vid_hdr to fastmap SB!"); - goto out_kfree; + goto out_free_seen; } for (i = 0; i < new_fm->used_blocks; i++) { @@ -1359,7 +1359,7 @@ static int ubi_write_fastmap(struct ubi_device *ubi, if (ret) { ubi_err(ubi, "unable to write vid_hdr to PEB %i!", new_fm->e[i]->pnum); - goto out_kfree; + goto out_free_seen; } } @@ -1369,7 +1369,7 @@ static int ubi_write_fastmap(struct ubi_device *ubi, if (ret) { ubi_err(ubi, "unable to write fastmap to PEB %i!", new_fm->e[i]->pnum); - goto out_kfree; + goto out_free_seen; } } @@ -1379,10 +1379,13 @@ static int ubi_write_fastmap(struct ubi_device *ubi, ret = self_check_seen(ubi, seen_pebs); dbg_bld("fastmap written!"); -out_kfree: - ubi_free_vid_buf(avbuf); - ubi_free_vid_buf(dvbuf); +out_free_seen: free_seen(seen_pebs); +out_free_dvbuf: + ubi_free_vid_buf(dvbuf); +out_free_avbuf: + ubi_free_vid_buf(avbuf); + out: return ret; } diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index e82108c917a635f036a9e86eeeed5d09c9cbbf53..334e3f22d4f1c19ecd17464ca4614565e6137de1 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -1399,26 +1399,31 @@ netdev_tx_t bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev) bool do_tx_balance = true; u32 hash_index = 0; const u8 *hash_start = NULL; - struct ipv6hdr *ip6hdr; skb_reset_mac_header(skb); eth_data = eth_hdr(skb); switch (ntohs(skb->protocol)) { case ETH_P_IP: { - const struct iphdr *iph = ip_hdr(skb); + const struct iphdr *iph; if (is_broadcast_ether_addr(eth_data->h_dest) || - iph->daddr == ip_bcast || - iph->protocol == IPPROTO_IGMP) { + !pskb_network_may_pull(skb, sizeof(*iph))) { + do_tx_balance = false; + break; + } + iph = ip_hdr(skb); + if (iph->daddr == ip_bcast || iph->protocol == IPPROTO_IGMP) { do_tx_balance = false; break; } hash_start = (char *)&(iph->daddr); hash_size = sizeof(iph->daddr); - } break; - case ETH_P_IPV6: + } + case ETH_P_IPV6: { + const struct ipv6hdr *ip6hdr; + /* IPv6 doesn't really use broadcast mac address, but leave * that here just in case. */ @@ -1435,7 +1440,11 @@ netdev_tx_t bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev) break; } - /* Additianally, DAD probes should not be tx-balanced as that + if (!pskb_network_may_pull(skb, sizeof(*ip6hdr))) { + do_tx_balance = false; + break; + } + /* Additionally, DAD probes should not be tx-balanced as that * will lead to false positives for duplicate addresses and * prevent address configuration from working. */ @@ -1445,17 +1454,26 @@ netdev_tx_t bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev) break; } - hash_start = (char *)&(ipv6_hdr(skb)->daddr); - hash_size = sizeof(ipv6_hdr(skb)->daddr); + hash_start = (char *)&ip6hdr->daddr; + hash_size = sizeof(ip6hdr->daddr); break; - case ETH_P_IPX: - if (ipx_hdr(skb)->ipx_checksum != IPX_NO_CHECKSUM) { + } + case ETH_P_IPX: { + const struct ipxhdr *ipxhdr; + + if (pskb_network_may_pull(skb, sizeof(*ipxhdr))) { + do_tx_balance = false; + break; + } + ipxhdr = (struct ipxhdr *)skb_network_header(skb); + + if (ipxhdr->ipx_checksum != IPX_NO_CHECKSUM) { /* something is wrong with this packet */ do_tx_balance = false; break; } - if (ipx_hdr(skb)->ipx_type != IPX_TYPE_NCP) { + if (ipxhdr->ipx_type != IPX_TYPE_NCP) { /* The only protocol worth balancing in * this family since it has an "ARP" like * mechanism @@ -1464,9 +1482,11 @@ netdev_tx_t bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev) break; } + eth_data = eth_hdr(skb); hash_start = (char *)eth_data->h_dest; hash_size = ETH_ALEN; break; + } case ETH_P_ARP: do_tx_balance = false; if (bond_info->rlb_enabled) diff --git a/drivers/net/can/mscan/mscan.c b/drivers/net/can/mscan/mscan.c index acb708fc14636e76959c068ec249682ac64564de..0a7d818a06f3f11f6fc3139ce6fd4aad145dc068 100644 --- a/drivers/net/can/mscan/mscan.c +++ b/drivers/net/can/mscan/mscan.c @@ -392,13 +392,12 @@ static int mscan_rx_poll(struct napi_struct *napi, int quota) struct net_device *dev = napi->dev; struct mscan_regs __iomem *regs = priv->reg_base; struct net_device_stats *stats = &dev->stats; - int npackets = 0; - int ret = 1; + int work_done = 0; struct sk_buff *skb; struct can_frame *frame; u8 canrflg; - while (npackets < quota) { + while (work_done < quota) { canrflg = in_8(®s->canrflg); if (!(canrflg & (MSCAN_RXF | MSCAN_ERR_IF))) break; @@ -419,18 +418,18 @@ static int mscan_rx_poll(struct napi_struct *napi, int quota) stats->rx_packets++; stats->rx_bytes += frame->can_dlc; - npackets++; + work_done++; netif_receive_skb(skb); } - if (!(in_8(®s->canrflg) & (MSCAN_RXF | MSCAN_ERR_IF))) { - napi_complete(&priv->napi); - clear_bit(F_RX_PROGRESS, &priv->flags); - if (priv->can.state < CAN_STATE_BUS_OFF) - out_8(®s->canrier, priv->shadow_canrier); - ret = 0; + if (work_done < quota) { + if (likely(napi_complete_done(&priv->napi, work_done))) { + clear_bit(F_RX_PROGRESS, &priv->flags); + if (priv->can.state < CAN_STATE_BUS_OFF) + out_8(®s->canrier, priv->shadow_canrier); + } } - return ret; + return work_done; } static irqreturn_t mscan_isr(int irq, void *dev_id) diff --git a/drivers/net/can/slcan.c b/drivers/net/can/slcan.c index cf0769ad39cdba93c24316190e54db8071f2ef1d..b2e5bcae7fbe0e5d0b9a081e076f115936f8e616 100644 --- a/drivers/net/can/slcan.c +++ b/drivers/net/can/slcan.c @@ -343,9 +343,16 @@ static void slcan_transmit(struct work_struct *work) */ static void slcan_write_wakeup(struct tty_struct *tty) { - struct slcan *sl = tty->disc_data; + struct slcan *sl; + + rcu_read_lock(); + sl = rcu_dereference(tty->disc_data); + if (!sl) + goto out; schedule_work(&sl->tx_work); +out: + rcu_read_unlock(); } /* Send a can_frame to a TTY queue. */ @@ -640,10 +647,11 @@ static void slcan_close(struct tty_struct *tty) return; spin_lock_bh(&sl->lock); - tty->disc_data = NULL; + rcu_assign_pointer(tty->disc_data, NULL); sl->tty = NULL; spin_unlock_bh(&sl->lock); + synchronize_rcu(); flush_work(&sl->tx_work); /* Flush network side */ diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c index 3a39f51a9e2448790a6840b0dda76c339c98e85c..cc2e224661b305e56851f881320605d652de9d98 100644 --- a/drivers/net/can/usb/gs_usb.c +++ b/drivers/net/can/usb/gs_usb.c @@ -926,7 +926,7 @@ static int gs_usb_probe(struct usb_interface *intf, GS_USB_BREQ_HOST_FORMAT, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 1, - intf->altsetting[0].desc.bInterfaceNumber, + intf->cur_altsetting->desc.bInterfaceNumber, hconf, sizeof(*hconf), 1000); @@ -949,7 +949,7 @@ static int gs_usb_probe(struct usb_interface *intf, GS_USB_BREQ_DEVICE_CONFIG, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 1, - intf->altsetting[0].desc.bInterfaceNumber, + intf->cur_altsetting->desc.bInterfaceNumber, dconf, sizeof(*dconf), 1000); diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c index 5fc0be564274375f3d5c579521a2d3b89ecd4a88..7ab87a758754543a189672ad886ce8dbda2f19e3 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c @@ -1590,7 +1590,7 @@ static int kvaser_usb_hydra_setup_endpoints(struct kvaser_usb *dev) struct usb_endpoint_descriptor *ep; int i; - iface_desc = &dev->intf->altsetting[0]; + iface_desc = dev->intf->cur_altsetting; for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { ep = &iface_desc->endpoint[i].desc; diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c index ae4c37e1bb753ecd8e2cc01320c9f7797f7868c9..1b9957f12459a9780ac9e94a73fa773cca6483de 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c @@ -1310,7 +1310,7 @@ static int kvaser_usb_leaf_setup_endpoints(struct kvaser_usb *dev) struct usb_endpoint_descriptor *endpoint; int i; - iface_desc = &dev->intf->altsetting[0]; + iface_desc = dev->intf->cur_altsetting; for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 2d3a2cb026d264751a79f1de3960e8427edf2759..ac5d945b934a04f01ce8148193150a01c783155b 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -343,7 +343,8 @@ static void b53_set_forwarding(struct b53_device *dev, int enable) b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_CTRL, mgmt); } -static void b53_enable_vlan(struct b53_device *dev, bool enable) +static void b53_enable_vlan(struct b53_device *dev, bool enable, + bool enable_filtering) { u8 mgmt, vc0, vc1, vc4 = 0, vc5; @@ -368,8 +369,13 @@ static void b53_enable_vlan(struct b53_device *dev, bool enable) vc0 |= VC0_VLAN_EN | VC0_VID_CHK_EN | VC0_VID_HASH_VID; vc1 |= VC1_RX_MCST_UNTAG_EN | VC1_RX_MCST_FWD_EN; vc4 &= ~VC4_ING_VID_CHECK_MASK; - vc4 |= VC4_ING_VID_VIO_DROP << VC4_ING_VID_CHECK_S; - vc5 |= VC5_DROP_VTABLE_MISS; + if (enable_filtering) { + vc4 |= VC4_ING_VID_VIO_DROP << VC4_ING_VID_CHECK_S; + vc5 |= VC5_DROP_VTABLE_MISS; + } else { + vc4 |= VC4_ING_VID_VIO_FWD << VC4_ING_VID_CHECK_S; + vc5 &= ~VC5_DROP_VTABLE_MISS; + } if (is5325(dev)) vc0 &= ~VC0_RESERVED_1; @@ -419,6 +425,9 @@ static void b53_enable_vlan(struct b53_device *dev, bool enable) } b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt); + + dev->vlan_enabled = enable; + dev->vlan_filtering_enabled = enable_filtering; } static int b53_set_jumbo(struct b53_device *dev, bool enable, bool allow_10_100) @@ -622,25 +631,35 @@ static void b53_enable_mib(struct b53_device *dev) b53_write8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc); } +static u16 b53_default_pvid(struct b53_device *dev) +{ + if (is5325(dev) || is5365(dev)) + return 1; + else + return 0; +} + int b53_configure_vlan(struct dsa_switch *ds) { struct b53_device *dev = ds->priv; struct b53_vlan vl = { 0 }; - int i; + int i, def_vid; + + def_vid = b53_default_pvid(dev); /* clear all vlan entries */ if (is5325(dev) || is5365(dev)) { - for (i = 1; i < dev->num_vlans; i++) + for (i = def_vid; i < dev->num_vlans; i++) b53_set_vlan_entry(dev, i, &vl); } else { b53_do_vlan_op(dev, VTA_CMD_CLEAR); } - b53_enable_vlan(dev, false); + b53_enable_vlan(dev, dev->vlan_enabled, dev->vlan_filtering_enabled); b53_for_each_port(dev, i) b53_write16(dev, B53_VLAN_PAGE, - B53_VLAN_PORT_DEF_TAG(i), 1); + B53_VLAN_PORT_DEF_TAG(i), def_vid); if (!is5325(dev) && !is5365(dev)) b53_set_jumbo(dev, dev->enable_jumbo, false); @@ -1071,6 +1090,46 @@ static void b53_adjust_link(struct dsa_switch *ds, int port, int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering) { + struct b53_device *dev = ds->priv; + struct net_device *bridge_dev; + unsigned int i; + u16 pvid, new_pvid; + + /* Handle the case were multiple bridges span the same switch device + * and one of them has a different setting than what is being requested + * which would be breaking filtering semantics for any of the other + * bridge devices. + */ + b53_for_each_port(dev, i) { + bridge_dev = dsa_to_port(ds, i)->bridge_dev; + if (bridge_dev && + bridge_dev != dsa_to_port(ds, port)->bridge_dev && + br_vlan_enabled(bridge_dev) != vlan_filtering) { + netdev_err(bridge_dev, + "VLAN filtering is global to the switch!\n"); + return -EINVAL; + } + } + + b53_read16(dev, B53_VLAN_PAGE, B53_VLAN_PORT_DEF_TAG(port), &pvid); + new_pvid = pvid; + if (dev->vlan_filtering_enabled && !vlan_filtering) { + /* Filtering is currently enabled, use the default PVID since + * the bridge does not expect tagging anymore + */ + dev->ports[port].pvid = pvid; + new_pvid = b53_default_pvid(dev); + } else if (!dev->vlan_filtering_enabled && vlan_filtering) { + /* Filtering is currently disabled, restore the previous PVID */ + new_pvid = dev->ports[port].pvid; + } + + if (pvid != new_pvid) + b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_PORT_DEF_TAG(port), + new_pvid); + + b53_enable_vlan(dev, dev->vlan_enabled, vlan_filtering); + return 0; } EXPORT_SYMBOL(b53_vlan_filtering); @@ -1086,7 +1145,7 @@ int b53_vlan_prepare(struct dsa_switch *ds, int port, if (vlan->vid_end > dev->num_vlans) return -ERANGE; - b53_enable_vlan(dev, true); + b53_enable_vlan(dev, true, dev->vlan_filtering_enabled); return 0; } @@ -1106,6 +1165,9 @@ void b53_vlan_add(struct dsa_switch *ds, int port, b53_get_vlan_entry(dev, vid, vl); + if (vid == 0 && vid == b53_default_pvid(dev)) + untagged = true; + vl->members |= BIT(port); if (untagged && !dsa_is_cpu_port(ds, port)) vl->untag |= BIT(port); @@ -1116,7 +1178,7 @@ void b53_vlan_add(struct dsa_switch *ds, int port, b53_fast_age_vlan(dev, vid); } - if (pvid) { + if (pvid && !dsa_is_cpu_port(ds, port)) { b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_PORT_DEF_TAG(port), vlan->vid_end); b53_fast_age_vlan(dev, vid); @@ -1142,12 +1204,8 @@ int b53_vlan_del(struct dsa_switch *ds, int port, vl->members &= ~BIT(port); - if (pvid == vid) { - if (is5325(dev) || is5365(dev)) - pvid = 1; - else - pvid = 0; - } + if (pvid == vid) + pvid = b53_default_pvid(dev); if (untagged && !dsa_is_cpu_port(ds, port)) vl->untag &= ~(BIT(port)); @@ -1460,10 +1518,7 @@ void b53_br_leave(struct dsa_switch *ds, int port, struct net_device *br) b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port), pvlan); dev->ports[port].vlan_ctl_mask = pvlan; - if (is5325(dev) || is5365(dev)) - pvid = 1; - else - pvid = 0; + pvid = b53_default_pvid(dev); /* Make this port join all VLANs without VLAN entries */ if (is58xx(dev)) { diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h index df149756c2820fd7eda73a0cdb3e5de8f16ebaa6..e87af5db0d6d85d2f34f8e904e540867ef315ca7 100644 --- a/drivers/net/dsa/b53/b53_priv.h +++ b/drivers/net/dsa/b53/b53_priv.h @@ -73,6 +73,7 @@ enum { struct b53_port { u16 vlan_ctl_mask; struct ethtool_eee eee; + u16 pvid; }; struct b53_vlan { @@ -118,6 +119,8 @@ struct b53_device { unsigned int num_vlans; struct b53_vlan *vlans; + bool vlan_enabled; + bool vlan_filtering_enabled; unsigned int num_ports; struct b53_port *ports; }; diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 02a4187d81bd037e6bcb9abe3e430d406a2a9b62..8c69789fbe094ed0ae9d89415bbc00917057adba 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -73,6 +73,7 @@ static void bcm_sf2_imp_setup(struct dsa_switch *ds, int port) /* Force link status for IMP port */ reg = core_readl(priv, offset); reg |= (MII_SW_OR | LINK_STS); + reg &= ~GMII_SPEED_UP_2G; core_writel(priv, reg, offset); /* Enable Broadcast, Multicast, Unicast forwarding to IMP port */ diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index 33232cc9fb04d2a0dcf8713e9f161e971a7a0bd0..6c04f32e96418afe3919d45341f38c609752a0a3 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -451,6 +451,18 @@ qca8k_set_pad_ctrl(struct qca8k_priv *priv, int port, int mode) qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL, QCA8K_PORT_PAD_RGMII_RX_DELAY_EN); break; + case PHY_INTERFACE_MODE_RGMII_ID: + /* RGMII_ID needs internal delay. This is enabled through + * PORT5_PAD_CTRL for all ports, rather than individual port + * registers + */ + qca8k_write(priv, reg, + QCA8K_PORT_PAD_RGMII_EN | + QCA8K_PORT_PAD_RGMII_TX_DELAY(QCA8K_MAX_DELAY) | + QCA8K_PORT_PAD_RGMII_RX_DELAY(QCA8K_MAX_DELAY)); + qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL, + QCA8K_PORT_PAD_RGMII_RX_DELAY_EN); + break; case PHY_INTERFACE_MODE_SGMII: qca8k_write(priv, reg, QCA8K_PORT_PAD_SGMII_EN); break; diff --git a/drivers/net/dsa/qca8k.h b/drivers/net/dsa/qca8k.h index 613fe5c50236c50cfbc6659b5a7d895b21409ec9..d146e54c8a6c615045ff18b31b413fba08365221 100644 --- a/drivers/net/dsa/qca8k.h +++ b/drivers/net/dsa/qca8k.h @@ -40,6 +40,7 @@ ((0x8 + (x & 0x3)) << 22) #define QCA8K_PORT_PAD_RGMII_RX_DELAY(x) \ ((0x10 + (x & 0x3)) << 20) +#define QCA8K_MAX_DELAY 3 #define QCA8K_PORT_PAD_RGMII_RX_DELAY_EN BIT(24) #define QCA8K_PORT_PAD_SGMII_EN BIT(7) #define QCA8K_REG_MODULE_EN 0x030 diff --git a/drivers/net/ethernet/amazon/ena/ena_com.c b/drivers/net/ethernet/amazon/ena/ena_com.c index 7635c38e77dd0ce43a960ffef0fdbaabc4f1422d..3afc0e59a2bd3552a54111cb50b63c759fe3ef10 100644 --- a/drivers/net/ethernet/amazon/ena/ena_com.c +++ b/drivers/net/ethernet/amazon/ena/ena_com.c @@ -201,6 +201,11 @@ static inline void comp_ctxt_release(struct ena_com_admin_queue *queue, static struct ena_comp_ctx *get_comp_ctxt(struct ena_com_admin_queue *queue, u16 command_id, bool capture) { + if (unlikely(!queue->comp_ctx)) { + pr_err("Completion context is NULL\n"); + return NULL; + } + if (unlikely(command_id >= queue->q_depth)) { pr_err("command id is larger than the queue size. cmd_id: %u queue size %d\n", command_id, queue->q_depth); @@ -842,6 +847,24 @@ static int ena_com_get_feature(struct ena_com_dev *ena_dev, 0); } +static void ena_com_hash_key_fill_default_key(struct ena_com_dev *ena_dev) +{ + struct ena_admin_feature_rss_flow_hash_control *hash_key = + (ena_dev->rss).hash_key; + + netdev_rss_key_fill(&hash_key->key, sizeof(hash_key->key)); + /* The key is stored in the device in u32 array + * as well as the API requires the key to be passed in this + * format. Thus the size of our array should be divided by 4 + */ + hash_key->keys_num = sizeof(hash_key->key) / sizeof(u32); +} + +int ena_com_get_current_hash_function(struct ena_com_dev *ena_dev) +{ + return ena_dev->rss.hash_func; +} + static int ena_com_hash_key_allocate(struct ena_com_dev *ena_dev) { struct ena_rss *rss = &ena_dev->rss; @@ -2008,7 +2031,7 @@ int ena_com_set_hash_function(struct ena_com_dev *ena_dev) if (unlikely(ret)) return ret; - if (get_resp.u.flow_hash_func.supported_func & (1 << rss->hash_func)) { + if (!(get_resp.u.flow_hash_func.supported_func & BIT(rss->hash_func))) { pr_err("Func hash %d isn't supported by device, abort\n", rss->hash_func); return -EOPNOTSUPP; @@ -2075,15 +2098,16 @@ int ena_com_fill_hash_function(struct ena_com_dev *ena_dev, switch (func) { case ENA_ADMIN_TOEPLITZ: - if (key_len > sizeof(hash_key->key)) { - pr_err("key len (%hu) is bigger than the max supported (%zu)\n", - key_len, sizeof(hash_key->key)); - return -EINVAL; + if (key) { + if (key_len != sizeof(hash_key->key)) { + pr_err("key len (%hu) doesn't equal the supported size (%zu)\n", + key_len, sizeof(hash_key->key)); + return -EINVAL; + } + memcpy(hash_key->key, key, key_len); + rss->hash_init_val = init_val; + hash_key->keys_num = key_len >> 2; } - - memcpy(hash_key->key, key, key_len); - rss->hash_init_val = init_val; - hash_key->keys_num = key_len >> 2; break; case ENA_ADMIN_CRC32: rss->hash_init_val = init_val; @@ -2093,6 +2117,7 @@ int ena_com_fill_hash_function(struct ena_com_dev *ena_dev, return -EINVAL; } + rss->hash_func = func; rc = ena_com_set_hash_function(ena_dev); /* Restore the old function */ @@ -2119,7 +2144,11 @@ int ena_com_get_hash_function(struct ena_com_dev *ena_dev, if (unlikely(rc)) return rc; - rss->hash_func = get_resp.u.flow_hash_func.selected_func; + /* ffs() returns 1 in case the lsb is set */ + rss->hash_func = ffs(get_resp.u.flow_hash_func.selected_func); + if (rss->hash_func) + rss->hash_func--; + if (func) *func = rss->hash_func; @@ -2407,6 +2436,8 @@ int ena_com_rss_init(struct ena_com_dev *ena_dev, u16 indr_tbl_log_size) if (unlikely(rc)) goto err_hash_key; + ena_com_hash_key_fill_default_key(ena_dev); + rc = ena_com_hash_ctrl_init(ena_dev); if (unlikely(rc)) goto err_hash_ctrl; diff --git a/drivers/net/ethernet/amazon/ena/ena_com.h b/drivers/net/ethernet/amazon/ena/ena_com.h index 7b784f8a06a6608c37f2c458b8172961ceed08f3..7272fb0d858d0af11222d569890608d1356cb39d 100644 --- a/drivers/net/ethernet/amazon/ena/ena_com.h +++ b/drivers/net/ethernet/amazon/ena/ena_com.h @@ -42,6 +42,7 @@ #include #include #include +#include #include "ena_common_defs.h" #include "ena_admin_defs.h" @@ -631,6 +632,14 @@ int ena_com_rss_init(struct ena_com_dev *ena_dev, u16 log_size); */ void ena_com_rss_destroy(struct ena_com_dev *ena_dev); +/* ena_com_get_current_hash_function - Get RSS hash function + * @ena_dev: ENA communication layer struct + * + * Return the current hash function. + * @return: 0 or one of the ena_admin_hash_functions values. + */ +int ena_com_get_current_hash_function(struct ena_com_dev *ena_dev); + /* ena_com_fill_hash_function - Fill RSS hash function * @ena_dev: ENA communication layer struct * @func: The hash function (Toeplitz or crc) diff --git a/drivers/net/ethernet/amazon/ena/ena_ethtool.c b/drivers/net/ethernet/amazon/ena/ena_ethtool.c index 521607bc43937f0954fb20414c5b1002123c244d..66f992510e0e337f66e98a93dae4282d306690df 100644 --- a/drivers/net/ethernet/amazon/ena/ena_ethtool.c +++ b/drivers/net/ethernet/amazon/ena/ena_ethtool.c @@ -649,6 +649,28 @@ static u32 ena_get_rxfh_key_size(struct net_device *netdev) return ENA_HASH_KEY_SIZE; } +static int ena_indirection_table_get(struct ena_adapter *adapter, u32 *indir) +{ + struct ena_com_dev *ena_dev = adapter->ena_dev; + int i, rc; + + if (!indir) + return 0; + + rc = ena_com_indirect_table_get(ena_dev, indir); + if (rc) + return rc; + + /* Our internal representation of the indices is: even indices + * for Tx and uneven indices for Rx. We need to convert the Rx + * indices to be consecutive + */ + for (i = 0; i < ENA_RX_RSS_TABLE_SIZE; i++) + indir[i] = ENA_IO_RXQ_IDX_TO_COMBINED_IDX(indir[i]); + + return rc; +} + static int ena_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key, u8 *hfunc) { @@ -657,11 +679,25 @@ static int ena_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key, u8 func; int rc; - rc = ena_com_indirect_table_get(adapter->ena_dev, indir); + rc = ena_indirection_table_get(adapter, indir); if (rc) return rc; + /* We call this function in order to check if the device + * supports getting/setting the hash function. + */ rc = ena_com_get_hash_function(adapter->ena_dev, &ena_func, key); + + if (rc) { + if (rc == -EOPNOTSUPP) { + key = NULL; + hfunc = NULL; + rc = 0; + } + + return rc; + } + if (rc) return rc; @@ -670,7 +706,7 @@ static int ena_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key, func = ETH_RSS_HASH_TOP; break; case ENA_ADMIN_CRC32: - func = ETH_RSS_HASH_XOR; + func = ETH_RSS_HASH_CRC32; break; default: netif_err(adapter, drv, netdev, @@ -695,8 +731,8 @@ static int ena_set_rxfh(struct net_device *netdev, const u32 *indir, if (indir) { for (i = 0; i < ENA_RX_RSS_TABLE_SIZE; i++) { rc = ena_com_indirect_table_fill_entry(ena_dev, - ENA_IO_RXQ_IDX(indir[i]), - i); + i, + ENA_IO_RXQ_IDX(indir[i])); if (unlikely(rc)) { netif_err(adapter, drv, netdev, "Cannot fill indirect table (index is too large)\n"); @@ -713,10 +749,13 @@ static int ena_set_rxfh(struct net_device *netdev, const u32 *indir, } switch (hfunc) { + case ETH_RSS_HASH_NO_CHANGE: + func = ena_com_get_current_hash_function(ena_dev); + break; case ETH_RSS_HASH_TOP: func = ENA_ADMIN_TOEPLITZ; break; - case ETH_RSS_HASH_XOR: + case ETH_RSS_HASH_CRC32: func = ENA_ADMIN_CRC32; break; default: @@ -817,6 +856,7 @@ static const struct ethtool_ops ena_ethtool_ops = { .get_channels = ena_get_channels, .get_tunable = ena_get_tunable, .set_tunable = ena_set_tunable, + .get_ts_info = ethtool_op_get_ts_info, }; void ena_set_ethtool_ops(struct net_device *netdev) diff --git a/drivers/net/ethernet/amazon/ena/ena_netdev.c b/drivers/net/ethernet/amazon/ena/ena_netdev.c index e26c195fec83b4d68840f4244d92053e4c352feb..8736718b17359f226ab569f51b37a6536049212e 100644 --- a/drivers/net/ethernet/amazon/ena/ena_netdev.c +++ b/drivers/net/ethernet/amazon/ena/ena_netdev.c @@ -1800,6 +1800,7 @@ static int ena_up(struct ena_adapter *adapter) err_setup_tx: ena_free_io_irq(adapter); err_req_irq: + ena_del_napi(adapter); return rc; } @@ -2846,8 +2847,8 @@ static void check_for_missing_keep_alive(struct ena_adapter *adapter) if (adapter->keep_alive_timeout == ENA_HW_HINTS_NO_TIMEOUT) return; - keep_alive_expired = round_jiffies(adapter->last_keep_alive_jiffies + - adapter->keep_alive_timeout); + keep_alive_expired = adapter->last_keep_alive_jiffies + + adapter->keep_alive_timeout; if (unlikely(time_is_before_jiffies(keep_alive_expired))) { netif_err(adapter, drv, adapter->netdev, "Keep alive watchdog timeout.\n"); @@ -2949,7 +2950,7 @@ static void ena_timer_service(struct timer_list *t) } /* Reset the timer */ - mod_timer(&adapter->timer_service, jiffies + HZ); + mod_timer(&adapter->timer_service, round_jiffies(jiffies + HZ)); } static int ena_calc_io_queue_num(struct pci_dev *pdev, diff --git a/drivers/net/ethernet/amazon/ena/ena_netdev.h b/drivers/net/ethernet/amazon/ena/ena_netdev.h index 7c7ae56c52cfddb11a8e1b096738dfc499ea57dc..f4783effe5c04361a023e2742d1938ddeaa67b6e 100644 --- a/drivers/net/ethernet/amazon/ena/ena_netdev.h +++ b/drivers/net/ethernet/amazon/ena/ena_netdev.h @@ -113,6 +113,8 @@ #define ENA_IO_TXQ_IDX(q) (2 * (q)) #define ENA_IO_RXQ_IDX(q) (2 * (q) + 1) +#define ENA_IO_TXQ_IDX_TO_COMBINED_IDX(q) ((q) / 2) +#define ENA_IO_RXQ_IDX_TO_COMBINED_IDX(q) (((q) - 1) / 2) #define ENA_MGMNT_IRQ_IDX 0 #define ENA_IO_IRQ_FIRST_IDX 1 diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c index 50dd6bf176d034721590bf15c2abfab88dd5285a..3a489b2b99c99fe0fb644f74575c71be0c8584f0 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c @@ -2034,7 +2034,7 @@ static int xgene_enet_probe(struct platform_device *pdev) int ret; ndev = alloc_etherdev_mqs(sizeof(struct xgene_enet_pdata), - XGENE_NUM_RX_RING, XGENE_NUM_TX_RING); + XGENE_NUM_TX_RING, XGENE_NUM_RX_RING); if (!ndev) return -ENOMEM; diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c index 8cc34b0bedc3a2f388ac778fe50b573f24f4b5ac..15dcfb6704e5b8e70b7d6aebe4b42b2c710a6c1f 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c @@ -399,8 +399,10 @@ static unsigned int aq_nic_map_skb(struct aq_nic_s *self, dx_buff->len, DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(aq_nic_get_dev(self), dx_buff->pa))) + if (unlikely(dma_mapping_error(aq_nic_get_dev(self), dx_buff->pa))) { + ret = 0; goto exit; + } first = dx_buff; dx_buff->len_pkt = skb->len; @@ -530,10 +532,6 @@ int aq_nic_xmit(struct aq_nic_s *self, struct sk_buff *skb) if (likely(frags)) { err = self->aq_hw_ops->hw_ring_tx_xmit(self->aq_hw, ring, frags); - if (err >= 0) { - ++ring->stats.tx.packets; - ring->stats.tx.bytes += skb->len; - } } else { err = NETDEV_TX_BUSY; } diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c index b3c7994d73eb1599456fa5c584f4297d4ebc0a83..b03e5fd4327e37e8e8e09512915560991bbd7616 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c @@ -162,9 +162,12 @@ bool aq_ring_tx_clean(struct aq_ring_s *self) } } - if (unlikely(buff->is_eop)) - dev_kfree_skb_any(buff->skb); + if (unlikely(buff->is_eop)) { + ++self->stats.rx.packets; + self->stats.tx.bytes += buff->skb->len; + dev_kfree_skb_any(buff->skb); + } buff->pa = 0U; buff->eop_index = 0xffffU; self->sw_head = aq_ring_next_dx(self, self->sw_head); diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_vec.c b/drivers/net/ethernet/aquantia/atlantic/aq_vec.c index 82582fa54d5d25fdce4b7d9bac10a2a146d3ab6a..72aa2a4c4d666a5293d6bb23e1168b9d375150d0 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_vec.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_vec.c @@ -309,15 +309,13 @@ irqreturn_t aq_vec_isr_legacy(int irq, void *private) { struct aq_vec_s *self = private; u64 irq_mask = 0U; - irqreturn_t err = 0; + int err; - if (!self) { - err = -EINVAL; - goto err_exit; - } + if (!self) + return IRQ_NONE; err = self->aq_hw_ops->hw_irq_read(self->aq_hw, &irq_mask); if (err < 0) - goto err_exit; + return IRQ_NONE; if (irq_mask) { self->aq_hw_ops->hw_irq_disable(self->aq_hw, @@ -325,11 +323,10 @@ irqreturn_t aq_vec_isr_legacy(int irq, void *private) napi_schedule(&self->napi); } else { self->aq_hw_ops->hw_irq_enable(self->aq_hw, 1U); - err = IRQ_NONE; + return IRQ_NONE; } -err_exit: - return err >= 0 ? IRQ_HANDLED : IRQ_NONE; + return IRQ_HANDLED; } cpumask_t *aq_vec_get_affinity_mask(struct aq_vec_s *self) diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_a0.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_a0.c index 97addfa6f89569d7d23f8b60f42d67030c55ac0a..dab5891b9714564153b6f90ed2f1dde7ec7b56ed 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_a0.c +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_a0.c @@ -207,8 +207,8 @@ static int hw_atl_a0_hw_rss_set(struct aq_hw_s *self, u32 i = 0U; u32 num_rss_queues = max(1U, self->aq_nic_cfg->num_rss_queues); int err = 0; - u16 bitary[(HW_ATL_A0_RSS_REDIRECTION_MAX * - HW_ATL_A0_RSS_REDIRECTION_BITS / 16U)]; + u16 bitary[1 + (HW_ATL_A0_RSS_REDIRECTION_MAX * + HW_ATL_A0_RSS_REDIRECTION_BITS / 16U)]; memset(bitary, 0, sizeof(bitary)); diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c index 51cd1f98bcf07e1321a13301db3fffbefd395e65..c4f914a29c385bcbfe5a234e1b18ad0f08756975 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c @@ -192,8 +192,8 @@ static int hw_atl_b0_hw_rss_set(struct aq_hw_s *self, u32 i = 0U; u32 num_rss_queues = max(1U, self->aq_nic_cfg->num_rss_queues); int err = 0; - u16 bitary[(HW_ATL_B0_RSS_REDIRECTION_MAX * - HW_ATL_B0_RSS_REDIRECTION_BITS / 16U)]; + u16 bitary[1 + (HW_ATL_B0_RSS_REDIRECTION_MAX * + HW_ATL_B0_RSS_REDIRECTION_BITS / 16U)]; memset(bitary, 0, sizeof(bitary)); diff --git a/drivers/net/ethernet/broadcom/b44.c b/drivers/net/ethernet/broadcom/b44.c index e445ab724827f8d3c7c3770748902c08b76f8e13..88f8d31e4c833819a1ebac7f97d68f1463eebe78 100644 --- a/drivers/net/ethernet/broadcom/b44.c +++ b/drivers/net/ethernet/broadcom/b44.c @@ -1519,8 +1519,10 @@ static int b44_magic_pattern(u8 *macaddr, u8 *ppattern, u8 *pmask, int offset) int ethaddr_bytes = ETH_ALEN; memset(ppattern + offset, 0xff, magicsync); - for (j = 0; j < magicsync; j++) - set_bit(len++, (unsigned long *) pmask); + for (j = 0; j < magicsync; j++) { + pmask[len >> 3] |= BIT(len & 7); + len++; + } for (j = 0; j < B44_MAX_PATTERNS; j++) { if ((B44_PATTERN_SIZE - len) >= ETH_ALEN) @@ -1532,7 +1534,8 @@ static int b44_magic_pattern(u8 *macaddr, u8 *ppattern, u8 *pmask, int offset) for (k = 0; k< ethaddr_bytes; k++) { ppattern[offset + magicsync + (j * ETH_ALEN) + k] = macaddr[k]; - set_bit(len++, (unsigned long *) pmask); + pmask[len >> 3] |= BIT(len & 7); + len++; } } return len - 1; diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c index 0bdbc72605e1fd1ad3d048651894d546665b4c8c..6f8649376ff06b860a1f39070e8d02a2bf1df34a 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c @@ -2470,7 +2470,7 @@ static int bcm_sysport_probe(struct platform_device *pdev) priv->phy_interface = of_get_phy_mode(dn); /* Default to GMII interface mode */ - if (priv->phy_interface < 0) + if ((int)priv->phy_interface < 0) priv->phy_interface = PHY_INTERFACE_MODE_GMII; /* In the case of a fixed PHY, the DT node associated @@ -2716,6 +2716,9 @@ static int __maybe_unused bcm_sysport_resume(struct device *d) umac_reset(priv); + /* Disable the UniMAC RX/TX */ + umac_enable_set(priv, CMD_RX_EN | CMD_TX_EN, 0); + /* We may have been suspended and never received a WOL event that * would turn off MPD detection, take care of that now */ diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 2f61175f5655a2d19d4ed6daf9c1ff01f6e2f737..c19d0eabeb526714d6ac020035dacb25b21062bb 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -5861,7 +5861,7 @@ static void bnxt_setup_msix(struct bnxt *bp) int tcs, i; tcs = netdev_get_num_tc(dev); - if (tcs > 1) { + if (tcs) { int i, off, count; for (i = 0; i < tcs; i++) { @@ -8215,11 +8215,23 @@ static bool bnxt_fltr_match(struct bnxt_ntuple_filter *f1, struct flow_keys *keys1 = &f1->fkeys; struct flow_keys *keys2 = &f2->fkeys; - if (keys1->addrs.v4addrs.src == keys2->addrs.v4addrs.src && - keys1->addrs.v4addrs.dst == keys2->addrs.v4addrs.dst && - keys1->ports.ports == keys2->ports.ports && - keys1->basic.ip_proto == keys2->basic.ip_proto && - keys1->basic.n_proto == keys2->basic.n_proto && + if (keys1->basic.n_proto != keys2->basic.n_proto || + keys1->basic.ip_proto != keys2->basic.ip_proto) + return false; + + if (keys1->basic.n_proto == htons(ETH_P_IP)) { + if (keys1->addrs.v4addrs.src != keys2->addrs.v4addrs.src || + keys1->addrs.v4addrs.dst != keys2->addrs.v4addrs.dst) + return false; + } else { + if (memcmp(&keys1->addrs.v6addrs.src, &keys2->addrs.v6addrs.src, + sizeof(keys1->addrs.v6addrs.src)) || + memcmp(&keys1->addrs.v6addrs.dst, &keys2->addrs.v6addrs.dst, + sizeof(keys1->addrs.v6addrs.dst))) + return false; + } + + if (keys1->ports.ports == keys2->ports.ports && keys1->control.flags == keys2->control.flags && ether_addr_equal(f1->src_mac_addr, f2->src_mac_addr) && ether_addr_equal(f1->dst_mac_addr, f2->dst_mac_addr)) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index f9e253b705ece65d61ff4b37f8207c39f99d2cd4..585f5aef0a45bb0b748876d5db2fbdf7ed4663aa 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -527,6 +527,7 @@ struct rx_tpa_end_cmp_ext { #define DFLT_HWRM_CMD_TIMEOUT 500 #define HWRM_CMD_TIMEOUT (bp->hwrm_cmd_timeout) #define HWRM_RESET_TIMEOUT ((HWRM_CMD_TIMEOUT) * 4) +#define HWRM_COREDUMP_TIMEOUT ((HWRM_CMD_TIMEOUT) * 12) #define HWRM_RESP_ERR_CODE_MASK 0xffff #define HWRM_RESP_LEN_OFFSET 4 #define HWRM_RESP_LEN_MASK 0xffff0000 diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c index a85d2be986af48a7143566a5aa9739129275b126..0e4e0b47f5d85ab310f062cf076cd47faf04f9b4 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c @@ -396,7 +396,7 @@ static int bnxt_hwrm_queue_dscp_qcaps(struct bnxt *bp) bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_QUEUE_DSCP_QCAPS, -1, -1); mutex_lock(&bp->hwrm_cmd_lock); - rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); + rc = _hwrm_send_message_silent(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); if (!rc) { bp->max_dscp_value = (1 << resp->num_dscp_bits) - 1; if (bp->max_dscp_value < 0x3f) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 0a409ba4012a3041726c959437b2676a702d68e3..047024717d654a0e07f18955f8b9b28961964a50 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -1778,21 +1778,19 @@ static int bnxt_flash_package_from_file(struct net_device *dev, mutex_lock(&bp->hwrm_cmd_lock); hwrm_err = _hwrm_send_message(bp, &install, sizeof(install), INSTALL_PACKAGE_TIMEOUT); - if (hwrm_err) - goto flash_pkg_exit; - - if (resp->error_code) { + if (hwrm_err) { u8 error_code = ((struct hwrm_err_output *)resp)->cmd_err; - if (error_code == NVM_INSTALL_UPDATE_CMD_ERR_CODE_FRAG_ERR) { + if (resp->error_code && error_code == + NVM_INSTALL_UPDATE_CMD_ERR_CODE_FRAG_ERR) { install.flags |= cpu_to_le16( NVM_INSTALL_UPDATE_REQ_FLAGS_ALLOWED_TO_DEFRAG); hwrm_err = _hwrm_send_message(bp, &install, sizeof(install), INSTALL_PACKAGE_TIMEOUT); - if (hwrm_err) - goto flash_pkg_exit; } + if (hwrm_err) + goto flash_pkg_exit; } if (resp->result) { @@ -2600,7 +2598,7 @@ static void bnxt_self_test(struct net_device *dev, struct ethtool_test *etest, bool offline = false; u8 test_results = 0; u8 test_mask = 0; - int rc, i; + int rc = 0, i; if (!bp->num_tests || !BNXT_SINGLE_PF(bp)) return; @@ -2671,9 +2669,9 @@ static void bnxt_self_test(struct net_device *dev, struct ethtool_test *etest, } bnxt_hwrm_phy_loopback(bp, false, false); bnxt_half_close_nic(bp); - bnxt_open_nic(bp, false, true); + rc = bnxt_open_nic(bp, false, true); } - if (bnxt_test_irq(bp)) { + if (rc || bnxt_test_irq(bp)) { buf[BNXT_IRQ_TEST_IDX] = 1; etest->flags |= ETH_TEST_FL_FAILED; } @@ -2835,7 +2833,7 @@ static int bnxt_hwrm_dbg_coredump_initiate(struct bnxt *bp, u16 component_id, req.component_id = cpu_to_le16(component_id); req.segment_id = cpu_to_le16(segment_id); - return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); + return hwrm_send_message(bp, &req, sizeof(req), HWRM_COREDUMP_TIMEOUT); } static int bnxt_hwrm_dbg_coredump_retrieve(struct bnxt *bp, u16 component_id, diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index b7d75011cede58a13bf8e7188c7f8cd6b43e8735..736a6a5fbd98db31681bfab9d966cb6b908cf886 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -2166,8 +2166,8 @@ static void bcmgenet_init_tx_ring(struct bcmgenet_priv *priv, DMA_END_ADDR); /* Initialize Tx NAPI */ - netif_napi_add(priv->dev, &ring->napi, bcmgenet_tx_poll, - NAPI_POLL_WEIGHT); + netif_tx_napi_add(priv->dev, &ring->napi, bcmgenet_tx_poll, + NAPI_POLL_WEIGHT); } /* Initialize a RDMA ring */ diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index c2eb188547942fa58c3b41d9b416b7f3062cf796..d1ff317f3b182c1030c3d3f7a7f238f214af0057 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -66,7 +66,11 @@ /* Max length of transmit frame must be a multiple of 8 bytes */ #define MACB_TX_LEN_ALIGN 8 #define MACB_MAX_TX_LEN ((unsigned int)((1 << MACB_TX_FRMLEN_SIZE) - 1) & ~((unsigned int)(MACB_TX_LEN_ALIGN - 1))) -#define GEM_MAX_TX_LEN ((unsigned int)((1 << GEM_TX_FRMLEN_SIZE) - 1) & ~((unsigned int)(MACB_TX_LEN_ALIGN - 1))) +/* Limit maximum TX length as per Cadence TSO errata. This is to avoid a + * false amba_error in TX path from the DMA assuming there is not enough + * space in the SRAM (16KB) even when there is. + */ +#define GEM_MAX_TX_LEN (unsigned int)(0x3FC0) #define GEM_MTU_MIN_SIZE ETH_MIN_MTU #define MACB_NETIF_LSO NETIF_F_TSO @@ -1654,16 +1658,14 @@ static netdev_features_t macb_features_check(struct sk_buff *skb, /* Validate LSO compatibility */ - /* there is only one buffer */ - if (!skb_is_nonlinear(skb)) + /* there is only one buffer or protocol is not UDP */ + if (!skb_is_nonlinear(skb) || (ip_hdr(skb)->protocol != IPPROTO_UDP)) return features; /* length of header */ hdrlen = skb_transport_offset(skb); - if (ip_hdr(skb)->protocol == IPPROTO_TCP) - hdrlen += tcp_hdrlen(skb); - /* For LSO: + /* For UFO only: * When software supplies two or more payload buffers all payload buffers * apart from the last must be a multiple of 8 bytes in size. */ diff --git a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c index 8ae28f82aafdc2226d2b26f24ef48a0809b32f4c..e5fc89813852c800d73c5d6d8cc24f7602f2a2a2 100644 --- a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c +++ b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c @@ -413,10 +413,19 @@ void bgx_lmac_rx_tx_enable(int node, int bgx_idx, int lmacid, bool enable) lmac = &bgx->lmac[lmacid]; cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG); - if (enable) + if (enable) { cfg |= CMR_PKT_RX_EN | CMR_PKT_TX_EN; - else + + /* enable TX FIFO Underflow interrupt */ + bgx_reg_modify(bgx, lmacid, BGX_GMP_GMI_TXX_INT_ENA_W1S, + GMI_TXX_INT_UNDFLW); + } else { cfg &= ~(CMR_PKT_RX_EN | CMR_PKT_TX_EN); + + /* Disable TX FIFO Underflow interrupt */ + bgx_reg_modify(bgx, lmacid, BGX_GMP_GMI_TXX_INT_ENA_W1C, + GMI_TXX_INT_UNDFLW); + } bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg); if (bgx->is_rgx) @@ -1544,6 +1553,48 @@ static int bgx_init_phy(struct bgx *bgx) return bgx_init_of_phy(bgx); } +static irqreturn_t bgx_intr_handler(int irq, void *data) +{ + struct bgx *bgx = (struct bgx *)data; + u64 status, val; + int lmac; + + for (lmac = 0; lmac < bgx->lmac_count; lmac++) { + status = bgx_reg_read(bgx, lmac, BGX_GMP_GMI_TXX_INT); + if (status & GMI_TXX_INT_UNDFLW) { + pci_err(bgx->pdev, "BGX%d lmac%d UNDFLW\n", + bgx->bgx_id, lmac); + val = bgx_reg_read(bgx, lmac, BGX_CMRX_CFG); + val &= ~CMR_EN; + bgx_reg_write(bgx, lmac, BGX_CMRX_CFG, val); + val |= CMR_EN; + bgx_reg_write(bgx, lmac, BGX_CMRX_CFG, val); + } + /* clear interrupts */ + bgx_reg_write(bgx, lmac, BGX_GMP_GMI_TXX_INT, status); + } + + return IRQ_HANDLED; +} + +static void bgx_register_intr(struct pci_dev *pdev) +{ + struct bgx *bgx = pci_get_drvdata(pdev); + int ret; + + ret = pci_alloc_irq_vectors(pdev, BGX_LMAC_VEC_OFFSET, + BGX_LMAC_VEC_OFFSET, PCI_IRQ_ALL_TYPES); + if (ret < 0) { + pci_err(pdev, "Req for #%d msix vectors failed\n", + BGX_LMAC_VEC_OFFSET); + return; + } + ret = pci_request_irq(pdev, GMPX_GMI_TX_INT, bgx_intr_handler, NULL, + bgx, "BGX%d", bgx->bgx_id); + if (ret) + pci_free_irq(pdev, GMPX_GMI_TX_INT, bgx); +} + static int bgx_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { int err; @@ -1559,7 +1610,7 @@ static int bgx_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pci_set_drvdata(pdev, bgx); - err = pci_enable_device(pdev); + err = pcim_enable_device(pdev); if (err) { dev_err(dev, "Failed to enable PCI device\n"); pci_set_drvdata(pdev, NULL); @@ -1613,6 +1664,8 @@ static int bgx_probe(struct pci_dev *pdev, const struct pci_device_id *ent) bgx_init_hw(bgx); + bgx_register_intr(pdev); + /* Enable all LMACs */ for (lmac = 0; lmac < bgx->lmac_count; lmac++) { err = bgx_lmac_enable(bgx, lmac); @@ -1629,6 +1682,7 @@ static int bgx_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err_enable: bgx_vnic[bgx->bgx_id] = NULL; + pci_free_irq(pdev, GMPX_GMI_TX_INT, bgx); err_release_regions: pci_release_regions(pdev); err_disable_device: @@ -1646,6 +1700,8 @@ static void bgx_remove(struct pci_dev *pdev) for (lmac = 0; lmac < bgx->lmac_count; lmac++) bgx_lmac_disable(bgx, lmac); + pci_free_irq(pdev, GMPX_GMI_TX_INT, bgx); + bgx_vnic[bgx->bgx_id] = NULL; pci_release_regions(pdev); pci_disable_device(pdev); diff --git a/drivers/net/ethernet/cavium/thunder/thunder_bgx.h b/drivers/net/ethernet/cavium/thunder/thunder_bgx.h index cbdd20b9ee6f1d4ede1551c37d585f3ecef0ab1b..ac0c89cd5c3d27c4ad87326cd574e76416fc104b 100644 --- a/drivers/net/ethernet/cavium/thunder/thunder_bgx.h +++ b/drivers/net/ethernet/cavium/thunder/thunder_bgx.h @@ -183,6 +183,15 @@ #define BGX_GMP_GMI_TXX_BURST 0x38228 #define BGX_GMP_GMI_TXX_MIN_PKT 0x38240 #define BGX_GMP_GMI_TXX_SGMII_CTL 0x38300 +#define BGX_GMP_GMI_TXX_INT 0x38500 +#define BGX_GMP_GMI_TXX_INT_W1S 0x38508 +#define BGX_GMP_GMI_TXX_INT_ENA_W1C 0x38510 +#define BGX_GMP_GMI_TXX_INT_ENA_W1S 0x38518 +#define GMI_TXX_INT_PTP_LOST BIT_ULL(4) +#define GMI_TXX_INT_LATE_COL BIT_ULL(3) +#define GMI_TXX_INT_XSDEF BIT_ULL(2) +#define GMI_TXX_INT_XSCOL BIT_ULL(1) +#define GMI_TXX_INT_UNDFLW BIT_ULL(0) #define BGX_MSIX_VEC_0_29_ADDR 0x400000 /* +(0..29) << 4 */ #define BGX_MSIX_VEC_0_29_CTL 0x400008 diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c index 6be6de0774b61f7cd90e7e607326c1e5ac8052c5..c82469ab7aba1fbd52238bfd4d2b184ca654e7b0 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c +++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c @@ -2449,6 +2449,8 @@ static int cxgb_extension_ioctl(struct net_device *dev, void __user *useraddr) if (!is_offload(adapter)) return -EOPNOTSUPP; + if (!capable(CAP_NET_ADMIN)) + return -EPERM; if (!(adapter->flags & FULL_INIT_DONE)) return -EIO; /* need the memory controllers */ if (copy_from_user(&t, useraddr, sizeof(t))) diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c index d320e9afab880cac92bb7d7eec7ce5c393c0a802..4af6e6ffc5df24bba5490d17d63f0bfea325039b 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c @@ -70,8 +70,7 @@ static void *seq_tab_start(struct seq_file *seq, loff_t *pos) static void *seq_tab_next(struct seq_file *seq, void *v, loff_t *pos) { v = seq_tab_get_idx(seq->private, *pos + 1); - if (v) - ++*pos; + ++(*pos); return v; } diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index bb04c695ab9fdc29d272695e3687472619276c18..c81d6c330548dc9d56cedda539b6ccd35afd9a65 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -5452,7 +5452,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) whoami = t4_read_reg(adapter, PL_WHOAMI_A); pci_read_config_word(pdev, PCI_DEVICE_ID, &device_id); chip = t4_get_chip_type(adapter, CHELSIO_PCI_ID_VER(device_id)); - if (chip < 0) { + if ((int)chip < 0) { dev_err(&pdev->dev, "Device %d is not supported\n", device_id); err = chip; goto out_free_adapter; diff --git a/drivers/net/ethernet/chelsio/cxgb4/l2t.c b/drivers/net/ethernet/chelsio/cxgb4/l2t.c index 301c4df8a56646adb094e49b977e7bd5b0bcba56..986277744611c3d18ed9c71046caf1f2a16577cb 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/l2t.c +++ b/drivers/net/ethernet/chelsio/cxgb4/l2t.c @@ -683,8 +683,7 @@ static void *l2t_seq_start(struct seq_file *seq, loff_t *pos) static void *l2t_seq_next(struct seq_file *seq, void *v, loff_t *pos) { v = l2t_get_idx(seq, *pos); - if (v) - ++*pos; + ++(*pos); return v; } diff --git a/drivers/net/ethernet/chelsio/cxgb4/smt.c b/drivers/net/ethernet/chelsio/cxgb4/smt.c index 7b2207a2a130f69fd61fde4dc87af9e967718adf..9b3f4205cb4d4c794ee1145148e1df9e3d6226f1 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/smt.c +++ b/drivers/net/ethernet/chelsio/cxgb4/smt.c @@ -98,11 +98,9 @@ static struct smt_entry *find_or_alloc_smte(struct smt_data *s, u8 *smac) static void t4_smte_free(struct smt_entry *e) { - spin_lock_bh(&e->lock); if (atomic_read(&e->refcnt) == 0) { /* hasn't been recycled */ e->state = SMT_STATE_UNUSED; } - spin_unlock_bh(&e->lock); } /** @@ -112,8 +110,10 @@ static void t4_smte_free(struct smt_entry *e) */ void cxgb4_smt_release(struct smt_entry *e) { + spin_lock_bh(&e->lock); if (atomic_dec_and_test(&e->refcnt)) t4_smte_free(e); + spin_unlock_bh(&e->lock); } EXPORT_SYMBOL(cxgb4_smt_release); diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index 733d9172425bf46398714c8a003c99fdfbd91f67..026a3bd71204f26fda91baabe2bb1b83074268ec 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -2013,10 +2013,10 @@ static int enic_stop(struct net_device *netdev) napi_disable(&enic->napi[i]); netif_carrier_off(netdev); - netif_tx_disable(netdev); if (vnic_dev_get_intr_mode(enic->vdev) == VNIC_DEV_INTR_MODE_MSIX) for (i = 0; i < enic->wq_count; i++) napi_disable(&enic->napi[enic_cq_wq(enic, i)]); + netif_tx_disable(netdev); if (!enic_is_dynamic(enic) && !enic_is_sriov_vf(enic)) enic_dev_del_station_addr(enic); diff --git a/drivers/net/ethernet/dec/tulip/dmfe.c b/drivers/net/ethernet/dec/tulip/dmfe.c index 17ef7a28873d93a35ccf342aac8c4fa26f0f71e6..0defd5b1212a28de8aaa2100d31fa8e69522e416 100644 --- a/drivers/net/ethernet/dec/tulip/dmfe.c +++ b/drivers/net/ethernet/dec/tulip/dmfe.c @@ -2222,15 +2222,16 @@ static int __init dmfe_init_module(void) if (cr6set) dmfe_cr6_user_set = cr6set; - switch(mode) { - case DMFE_10MHF: + switch (mode) { + case DMFE_10MHF: case DMFE_100MHF: case DMFE_10MFD: case DMFE_100MFD: case DMFE_1M_HPNA: dmfe_media_mode = mode; break; - default:dmfe_media_mode = DMFE_AUTO; + default: + dmfe_media_mode = DMFE_AUTO; break; } diff --git a/drivers/net/ethernet/dec/tulip/uli526x.c b/drivers/net/ethernet/dec/tulip/uli526x.c index 488a744084c9493590fd4d56180ea641d18e4e1e..f4751a8de6294cf813124f9a4eea86de947bb85d 100644 --- a/drivers/net/ethernet/dec/tulip/uli526x.c +++ b/drivers/net/ethernet/dec/tulip/uli526x.c @@ -1817,8 +1817,8 @@ static int __init uli526x_init_module(void) if (cr6set) uli526x_cr6_user_set = cr6set; - switch (mode) { - case ULI526X_10MHF: + switch (mode) { + case ULI526X_10MHF: case ULI526X_100MHF: case ULI526X_10MFD: case ULI526X_100MFD: diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index 462bb8c4f80c93a21be4497cd978bf3304e98408..d7736c9c6339aa35f34a6bd56a669e9c537d4baf 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -1600,13 +1600,15 @@ static int dpaa_eth_refill_bpools(struct dpaa_priv *priv) * Skb freeing is not handled here. * * This function may be called on error paths in the Tx function, so guard - * against cases when not all fd relevant fields were filled in. + * against cases when not all fd relevant fields were filled in. To avoid + * reading the invalid transmission timestamp for the error paths set ts to + * false. * * Return the skb backpointer, since for S/G frames the buffer containing it * gets freed here. */ static struct sk_buff *dpaa_cleanup_tx_fd(const struct dpaa_priv *priv, - const struct qm_fd *fd) + const struct qm_fd *fd, bool ts) { const enum dma_data_direction dma_dir = DMA_TO_DEVICE; struct device *dev = priv->net_dev->dev.parent; @@ -1620,18 +1622,6 @@ static struct sk_buff *dpaa_cleanup_tx_fd(const struct dpaa_priv *priv, skbh = (struct sk_buff **)phys_to_virt(addr); skb = *skbh; - if (priv->tx_tstamp && skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) { - memset(&shhwtstamps, 0, sizeof(shhwtstamps)); - - if (!fman_port_get_tstamp(priv->mac_dev->port[TX], (void *)skbh, - &ns)) { - shhwtstamps.hwtstamp = ns_to_ktime(ns); - skb_tstamp_tx(skb, &shhwtstamps); - } else { - dev_warn(dev, "fman_port_get_tstamp failed!\n"); - } - } - if (unlikely(qm_fd_get_format(fd) == qm_fd_sg)) { nr_frags = skb_shinfo(skb)->nr_frags; dma_unmap_single(dev, addr, @@ -1654,14 +1644,29 @@ static struct sk_buff *dpaa_cleanup_tx_fd(const struct dpaa_priv *priv, dma_unmap_page(dev, qm_sg_addr(&sgt[i]), qm_sg_entry_get_len(&sgt[i]), dma_dir); } - - /* Free the page frag that we allocated on Tx */ - skb_free_frag(phys_to_virt(addr)); } else { dma_unmap_single(dev, addr, skb_tail_pointer(skb) - (u8 *)skbh, dma_dir); } + /* DMA unmapping is required before accessing the HW provided info */ + if (ts && priv->tx_tstamp && + skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) { + memset(&shhwtstamps, 0, sizeof(shhwtstamps)); + + if (!fman_port_get_tstamp(priv->mac_dev->port[TX], (void *)skbh, + &ns)) { + shhwtstamps.hwtstamp = ns_to_ktime(ns); + skb_tstamp_tx(skb, &shhwtstamps); + } else { + dev_warn(dev, "fman_port_get_tstamp failed!\n"); + } + } + + if (qm_fd_get_format(fd) == qm_fd_sg) + /* Free the page frag that we allocated on Tx */ + skb_free_frag(phys_to_virt(addr)); + return skb; } @@ -2116,7 +2121,7 @@ dpaa_start_xmit(struct sk_buff *skb, struct net_device *net_dev) if (likely(dpaa_xmit(priv, percpu_stats, queue_mapping, &fd) == 0)) return NETDEV_TX_OK; - dpaa_cleanup_tx_fd(priv, &fd); + dpaa_cleanup_tx_fd(priv, &fd, false); skb_to_fd_failed: enomem: percpu_stats->tx_errors++; @@ -2162,7 +2167,7 @@ static void dpaa_tx_error(struct net_device *net_dev, percpu_priv->stats.tx_errors++; - skb = dpaa_cleanup_tx_fd(priv, fd); + skb = dpaa_cleanup_tx_fd(priv, fd, false); dev_kfree_skb(skb); } @@ -2203,7 +2208,7 @@ static void dpaa_tx_conf(struct net_device *net_dev, percpu_priv->tx_confirm++; - skb = dpaa_cleanup_tx_fd(priv, fd); + skb = dpaa_cleanup_tx_fd(priv, fd, true); consume_skb(skb); } @@ -2433,7 +2438,7 @@ static void egress_ern(struct qman_portal *portal, percpu_priv->stats.tx_fifo_errors++; count_ern(percpu_priv, msg); - skb = dpaa_cleanup_tx_fd(priv, fd); + skb = dpaa_cleanup_tx_fd(priv, fd, false); dev_kfree_skb_any(skb); } diff --git a/drivers/net/ethernet/freescale/fman/fman_memac.c b/drivers/net/ethernet/freescale/fman/fman_memac.c index 41c6fa200e7467af31a05ef4c365705fe89f2329..e1901874c19f09417e47e78d5c1e366b51b21992 100644 --- a/drivers/net/ethernet/freescale/fman/fman_memac.c +++ b/drivers/net/ethernet/freescale/fman/fman_memac.c @@ -110,7 +110,7 @@ do { \ /* Interface Mode Register (IF_MODE) */ #define IF_MODE_MASK 0x00000003 /* 30-31 Mask on i/f mode bits */ -#define IF_MODE_XGMII 0x00000000 /* 30-31 XGMII (10G) interface */ +#define IF_MODE_10G 0x00000000 /* 30-31 10G interface */ #define IF_MODE_GMII 0x00000002 /* 30-31 GMII (1G) interface */ #define IF_MODE_RGMII 0x00000004 #define IF_MODE_RGMII_AUTO 0x00008000 @@ -440,7 +440,7 @@ static int init(struct memac_regs __iomem *regs, struct memac_cfg *cfg, tmp = 0; switch (phy_if) { case PHY_INTERFACE_MODE_XGMII: - tmp |= IF_MODE_XGMII; + tmp |= IF_MODE_10G; break; default: tmp |= IF_MODE_GMII; diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index c97c4edfa31bcf2a73ab7465c241cc4d768a368f..cf2d1e846a692d0d77829b4cc3dc2a9f253aa3f5 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -2685,13 +2685,17 @@ static void gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue) skb_dirtytx = tx_queue->skb_dirtytx; while ((skb = tx_queue->tx_skbuff[skb_dirtytx])) { + bool do_tstamp; + + do_tstamp = (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && + priv->hwts_tx_en; frags = skb_shinfo(skb)->nr_frags; /* When time stamping, one additional TxBD must be freed. * Also, we need to dma_unmap_single() the TxPAL. */ - if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) + if (unlikely(do_tstamp)) nr_txbds = frags + 2; else nr_txbds = frags + 1; @@ -2705,7 +2709,7 @@ static void gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue) (lstatus & BD_LENGTH_MASK)) break; - if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) { + if (unlikely(do_tstamp)) { next = next_txbd(bdp, base, tx_ring_size); buflen = be16_to_cpu(next->length) + GMAC_FCB_LEN + GMAC_TXPAL_LEN; @@ -2715,7 +2719,7 @@ static void gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue) dma_unmap_single(priv->dev, be32_to_cpu(bdp->bufPtr), buflen, DMA_TO_DEVICE); - if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) { + if (unlikely(do_tstamp)) { struct skb_shared_hwtstamps shhwtstamps; u64 *ns = (u64 *)(((uintptr_t)skb->data + 0x10) & ~0x7UL); diff --git a/drivers/net/ethernet/freescale/xgmac_mdio.c b/drivers/net/ethernet/freescale/xgmac_mdio.c index e03b30c60dcfda168291ce7f3a93306b3f3748de..c82c85ef5fb3407d1a0016573b325dd39e068ffa 100644 --- a/drivers/net/ethernet/freescale/xgmac_mdio.c +++ b/drivers/net/ethernet/freescale/xgmac_mdio.c @@ -49,6 +49,7 @@ struct tgec_mdio_controller { struct mdio_fsl_priv { struct tgec_mdio_controller __iomem *mdio_base; bool is_little_endian; + bool has_a011043; }; static u32 xgmac_read32(void __iomem *regs, @@ -226,7 +227,8 @@ static int xgmac_mdio_read(struct mii_bus *bus, int phy_id, int regnum) return ret; /* Return all Fs if nothing was there */ - if (xgmac_read32(®s->mdio_stat, endian) & MDIO_STAT_RD_ER) { + if ((xgmac_read32(®s->mdio_stat, endian) & MDIO_STAT_RD_ER) && + !priv->has_a011043) { dev_err(&bus->dev, "Error while reading PHY%d reg at %d.%hhu\n", phy_id, dev_addr, regnum); @@ -274,6 +276,9 @@ static int xgmac_mdio_probe(struct platform_device *pdev) priv->is_little_endian = of_property_read_bool(pdev->dev.of_node, "little-endian"); + priv->has_a011043 = of_property_read_bool(pdev->dev.of_node, + "fsl,erratum-a011043"); + ret = of_mdiobus_register(bus, np); if (ret) { dev_err(&pdev->dev, "cannot register MDIO bus\n"); diff --git a/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c b/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c index 471805ea363b6b5eda58800b7ef0e5417e1d5530..b63871ef8a403a503fb0b197d40c92ab2679cc39 100644 --- a/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c +++ b/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c @@ -1201,7 +1201,7 @@ static int hix5hd2_dev_probe(struct platform_device *pdev) goto err_free_mdio; priv->phy_mode = of_get_phy_mode(node); - if (priv->phy_mode < 0) { + if ((int)priv->phy_mode < 0) { netdev_err(ndev, "not find phy-mode\n"); ret = -EINVAL; goto err_mdiobus; diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c index 7f8cf809e02b5e4a36b43481677304b220dea120..024b08fafd3b281823f53aeb14bda428fbcbed10 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c @@ -569,7 +569,6 @@ static int hns_nic_poll_rx_skb(struct hns_nic_ring_data *ring_data, skb = *out_skb = napi_alloc_skb(&ring_data->napi, HNS_RX_HEAD_SIZE); if (unlikely(!skb)) { - netdev_err(ndev, "alloc rx skb fail\n"); ring->stats.sw_err_cnt++; return -ENOMEM; } @@ -1060,7 +1059,6 @@ static int hns_nic_common_poll(struct napi_struct *napi, int budget) container_of(napi, struct hns_nic_ring_data, napi); struct hnae_ring *ring = ring_data->ring; -try_again: clean_complete += ring_data->poll_one( ring_data, budget - clean_complete, ring_data->ex_process); @@ -1070,7 +1068,7 @@ static int hns_nic_common_poll(struct napi_struct *napi, int budget) napi_complete(napi); ring->q->handle->dev->ops->toggle_ring_irq(ring, 0); } else { - goto try_again; + return budget; } } diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c index 1aaf6e2a3b39d1800b5cd70e98a7806cbd8dc398..3eb8b85f6afb6e467912be09120d3a7eb45a0532 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c @@ -1464,7 +1464,7 @@ static bool hns3_get_tx_timeo_queue_info(struct net_device *ndev) int i; /* Find the stopped queue the same way the stack does */ - for (i = 0; i < ndev->real_num_tx_queues; i++) { + for (i = 0; i < ndev->num_tx_queues; i++) { struct netdev_queue *q; unsigned long trans_start; @@ -2605,9 +2605,10 @@ static int hns3_get_vector_ring_chain(struct hns3_enet_tqp_vector *tqp_vector, cur_chain = head->next; while (cur_chain) { chain = cur_chain->next; - devm_kfree(&pdev->dev, chain); + devm_kfree(&pdev->dev, cur_chain); cur_chain = chain; } + head->next = NULL; return -ENOMEM; } @@ -2642,7 +2643,7 @@ static int hns3_nic_init_vector_data(struct hns3_nic_priv *priv) struct hnae3_handle *h = priv->ae_handle; struct hns3_enet_tqp_vector *tqp_vector; int ret = 0; - u16 i; + int i; for (i = 0; i < priv->vector_num; i++) { tqp_vector = &priv->tqp_vector[i]; @@ -2679,7 +2680,7 @@ static int hns3_nic_init_vector_data(struct hns3_nic_priv *priv) ret = hns3_get_vector_ring_chain(tqp_vector, &vector_ring_chain); if (ret) - return ret; + goto map_ring_fail; ret = h->ae_algo->ops->map_ring_to_vector(h, tqp_vector->vector_irq, &vector_ring_chain); @@ -2687,13 +2688,19 @@ static int hns3_nic_init_vector_data(struct hns3_nic_priv *priv) hns3_free_vector_ring_chain(tqp_vector, &vector_ring_chain); if (ret) - return ret; + goto map_ring_fail; netif_napi_add(priv->netdev, &tqp_vector->napi, hns3_nic_common_poll, NAPI_POLL_WEIGHT); } return 0; + +map_ring_fail: + while (i--) + netif_napi_del(&priv->tqp_vector[i].napi); + + return ret; } static int hns3_nic_alloc_vector_data(struct hns3_nic_priv *priv) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index f8cc8d1f0b2097fe46de56db5ab9f7e9e1b1b7fa..d575dd9a329d9bfc81d235409fcf51eed76c9c2c 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -4833,6 +4833,7 @@ static int hclge_set_vlan_tx_offload_cfg(struct hclge_vport *vport) struct hclge_vport_vtag_tx_cfg_cmd *req; struct hclge_dev *hdev = vport->back; struct hclge_desc desc; + u16 bmap_index; int status; hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_VLAN_PORT_TX_CFG, false); @@ -4855,8 +4856,10 @@ static int hclge_set_vlan_tx_offload_cfg(struct hclge_vport *vport) hnae3_set_bit(req->vport_vlan_cfg, HCLGE_CFG_NIC_ROCE_SEL_B, 0); req->vf_offset = vport->vport_id / HCLGE_VF_NUM_PER_CMD; - req->vf_bitmap[req->vf_offset] = - 1 << (vport->vport_id % HCLGE_VF_NUM_PER_BYTE); + bmap_index = vport->vport_id % HCLGE_VF_NUM_PER_CMD / + HCLGE_VF_NUM_PER_BYTE; + req->vf_bitmap[bmap_index] = + 1U << (vport->vport_id % HCLGE_VF_NUM_PER_BYTE); status = hclge_cmd_send(&hdev->hw, &desc, 1); if (status) @@ -4873,6 +4876,7 @@ static int hclge_set_vlan_rx_offload_cfg(struct hclge_vport *vport) struct hclge_vport_vtag_rx_cfg_cmd *req; struct hclge_dev *hdev = vport->back; struct hclge_desc desc; + u16 bmap_index; int status; hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_VLAN_PORT_RX_CFG, false); @@ -4888,8 +4892,10 @@ static int hclge_set_vlan_rx_offload_cfg(struct hclge_vport *vport) vcfg->vlan2_vlan_prionly ? 1 : 0); req->vf_offset = vport->vport_id / HCLGE_VF_NUM_PER_CMD; - req->vf_bitmap[req->vf_offset] = - 1 << (vport->vport_id % HCLGE_VF_NUM_PER_BYTE); + bmap_index = vport->vport_id % HCLGE_VF_NUM_PER_CMD / + HCLGE_VF_NUM_PER_BYTE; + req->vf_bitmap[bmap_index] = + 1U << (vport->vport_id % HCLGE_VF_NUM_PER_BYTE); status = hclge_cmd_send(&hdev->hw, &desc, 1); if (status) @@ -5922,18 +5928,17 @@ static u32 hclge_get_max_channels(struct hnae3_handle *handle) struct hclge_vport *vport = hclge_get_vport(handle); struct hclge_dev *hdev = vport->back; - return min_t(u32, hdev->rss_size_max * kinfo->num_tc, hdev->num_tqps); + return min_t(u32, hdev->rss_size_max, + vport->alloc_tqps / kinfo->num_tc); } static void hclge_get_channels(struct hnae3_handle *handle, struct ethtool_channels *ch) { - struct hclge_vport *vport = hclge_get_vport(handle); - ch->max_combined = hclge_get_max_channels(handle); ch->other_count = 1; ch->max_other = 1; - ch->combined_count = vport->alloc_tqps; + ch->combined_count = handle->kinfo.rss_size; } static void hclge_get_tqps_and_rss_info(struct hnae3_handle *handle, diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h index 260b1e77969080678cb4afcfeccef8c885d0856f..d14b7018fdf34e76394bf6fbb98b4eb8a71293e6 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h @@ -600,7 +600,7 @@ struct hclge_vport { u16 alloc_rss_size; u16 qs_offset; - u16 bw_limit; /* VSI BW Limit (0 = disabled) */ + u32 bw_limit; /* VSI BW Limit (0 = disabled) */ u8 dwrr; struct hclge_tx_vtag_cfg txvlan_cfg; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c index e08e82020402a8168850927f1f492b141c0352df..997ca79ed892baeea95290fde7bfce5f3d95f923 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c @@ -181,12 +181,10 @@ static int hclge_map_unmap_ring_to_vf_vector(struct hclge_vport *vport, bool en, return ret; ret = hclge_bind_ring_with_vector(vport, vector_id, en, &ring_chain); - if (ret) - return ret; hclge_free_vector_ring_chain(&ring_chain); - return 0; + return ret; } static int hclge_set_vf_promisc_mode(struct hclge_vport *vport, diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c index 67db19709deaa89ac6bc71ba67ca9ff02753dcd9..fd5375b5991bb22dc262e55cea88a3076aecba53 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c @@ -1957,7 +1957,8 @@ static u32 hclgevf_get_max_channels(struct hclgevf_dev *hdev) struct hnae3_handle *nic = &hdev->nic; struct hnae3_knic_private_info *kinfo = &nic->kinfo; - return min_t(u32, hdev->rss_size_max * kinfo->num_tc, hdev->num_tqps); + return min_t(u32, hdev->rss_size_max, + hdev->num_tqps / kinfo->num_tc); } /** @@ -1978,7 +1979,7 @@ static void hclgevf_get_channels(struct hnae3_handle *handle, ch->max_combined = hclgevf_get_max_channels(hdev); ch->other_count = 0; ch->max_other = 0; - ch->combined_count = hdev->num_tqps; + ch->combined_count = handle->kinfo.rss_size; } static void hclgevf_get_tqps_and_rss_info(struct hnae3_handle *handle, diff --git a/drivers/net/ethernet/ibm/ehea/ehea_main.c b/drivers/net/ethernet/ibm/ehea/ehea_main.c index e8ee69d4e4d34898f4cb490c32b3d0f2370bd710..0f799e8e093cdbf1d185ca9cde318c62c2199dbe 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_main.c +++ b/drivers/net/ethernet/ibm/ehea/ehea_main.c @@ -1464,7 +1464,7 @@ static int ehea_init_port_res(struct ehea_port *port, struct ehea_port_res *pr, memset(pr, 0, sizeof(struct ehea_port_res)); - pr->tx_bytes = rx_bytes; + pr->tx_bytes = tx_bytes; pr->tx_packets = tx_packets; pr->rx_bytes = rx_bytes; pr->rx_packets = rx_packets; diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index 85f75b5978fca572f776f7eab2ea7f6365cf78d3..eb0ae6ab01e265403eef21c2eed6d79c4ea99253 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -1668,25 +1668,15 @@ enum i40e_status_code i40e_aq_set_phy_config(struct i40e_hw *hw, return status; } -/** - * i40e_set_fc - * @hw: pointer to the hw struct - * @aq_failures: buffer to return AdminQ failure information - * @atomic_restart: whether to enable atomic link restart - * - * Set the requested flow control mode using set_phy_config. - **/ -enum i40e_status_code i40e_set_fc(struct i40e_hw *hw, u8 *aq_failures, - bool atomic_restart) +static noinline_for_stack enum i40e_status_code +i40e_set_fc_status(struct i40e_hw *hw, + struct i40e_aq_get_phy_abilities_resp *abilities, + bool atomic_restart) { - enum i40e_fc_mode fc_mode = hw->fc.requested_mode; - struct i40e_aq_get_phy_abilities_resp abilities; struct i40e_aq_set_phy_config config; - enum i40e_status_code status; + enum i40e_fc_mode fc_mode = hw->fc.requested_mode; u8 pause_mask = 0x0; - *aq_failures = 0x0; - switch (fc_mode) { case I40E_FC_FULL: pause_mask |= I40E_AQ_PHY_FLAG_PAUSE_TX; @@ -1702,6 +1692,48 @@ enum i40e_status_code i40e_set_fc(struct i40e_hw *hw, u8 *aq_failures, break; } + memset(&config, 0, sizeof(struct i40e_aq_set_phy_config)); + /* clear the old pause settings */ + config.abilities = abilities->abilities & ~(I40E_AQ_PHY_FLAG_PAUSE_TX) & + ~(I40E_AQ_PHY_FLAG_PAUSE_RX); + /* set the new abilities */ + config.abilities |= pause_mask; + /* If the abilities have changed, then set the new config */ + if (config.abilities == abilities->abilities) + return 0; + + /* Auto restart link so settings take effect */ + if (atomic_restart) + config.abilities |= I40E_AQ_PHY_ENABLE_ATOMIC_LINK; + /* Copy over all the old settings */ + config.phy_type = abilities->phy_type; + config.phy_type_ext = abilities->phy_type_ext; + config.link_speed = abilities->link_speed; + config.eee_capability = abilities->eee_capability; + config.eeer = abilities->eeer_val; + config.low_power_ctrl = abilities->d3_lpan; + config.fec_config = abilities->fec_cfg_curr_mod_ext_info & + I40E_AQ_PHY_FEC_CONFIG_MASK; + + return i40e_aq_set_phy_config(hw, &config, NULL); +} + +/** + * i40e_set_fc + * @hw: pointer to the hw struct + * @aq_failures: buffer to return AdminQ failure information + * @atomic_restart: whether to enable atomic link restart + * + * Set the requested flow control mode using set_phy_config. + **/ +enum i40e_status_code i40e_set_fc(struct i40e_hw *hw, u8 *aq_failures, + bool atomic_restart) +{ + struct i40e_aq_get_phy_abilities_resp abilities; + enum i40e_status_code status; + + *aq_failures = 0x0; + /* Get the current phy config */ status = i40e_aq_get_phy_capabilities(hw, false, false, &abilities, NULL); @@ -1710,31 +1742,10 @@ enum i40e_status_code i40e_set_fc(struct i40e_hw *hw, u8 *aq_failures, return status; } - memset(&config, 0, sizeof(struct i40e_aq_set_phy_config)); - /* clear the old pause settings */ - config.abilities = abilities.abilities & ~(I40E_AQ_PHY_FLAG_PAUSE_TX) & - ~(I40E_AQ_PHY_FLAG_PAUSE_RX); - /* set the new abilities */ - config.abilities |= pause_mask; - /* If the abilities have changed, then set the new config */ - if (config.abilities != abilities.abilities) { - /* Auto restart link so settings take effect */ - if (atomic_restart) - config.abilities |= I40E_AQ_PHY_ENABLE_ATOMIC_LINK; - /* Copy over all the old settings */ - config.phy_type = abilities.phy_type; - config.phy_type_ext = abilities.phy_type_ext; - config.link_speed = abilities.link_speed; - config.eee_capability = abilities.eee_capability; - config.eeer = abilities.eeer_val; - config.low_power_ctrl = abilities.d3_lpan; - config.fec_config = abilities.fec_cfg_curr_mod_ext_info & - I40E_AQ_PHY_FEC_CONFIG_MASK; - status = i40e_aq_set_phy_config(hw, &config, NULL); + status = i40e_set_fc_status(hw, &abilities, atomic_restart); + if (status) + *aq_failures |= I40E_SET_FC_AQ_FAIL_SET; - if (status) - *aq_failures |= I40E_SET_FC_AQ_FAIL_SET; - } /* Update the link info */ status = i40e_update_link_info(hw); if (status) { @@ -2563,7 +2574,7 @@ i40e_status i40e_get_link_status(struct i40e_hw *hw, bool *link_up) * i40e_updatelink_status - update status of the HW network link * @hw: pointer to the hw struct **/ -i40e_status i40e_update_link_info(struct i40e_hw *hw) +noinline_for_stack i40e_status i40e_update_link_info(struct i40e_hw *hw) { struct i40e_aq_get_phy_abilities_resp abilities; i40e_status status = 0; diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool.c b/drivers/net/ethernet/intel/ice/ice_ethtool.c index 4c5c87b158f5593a7e4264443e5ac57828f2ee7b..627abef829c9a84a46000422456b4855d7098d2c 100644 --- a/drivers/net/ethernet/intel/ice/ice_ethtool.c +++ b/drivers/net/ethernet/intel/ice/ice_ethtool.c @@ -772,13 +772,6 @@ ice_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause) else return -EINVAL; - /* Tell the OS link is going down, the link will go back up when fw - * says it is ready asynchronously - */ - ice_print_link_msg(vsi, false); - netif_carrier_off(netdev); - netif_tx_stop_all_queues(netdev); - /* Set the FC mode and only restart AN if link is up */ status = ice_set_fc(pi, &aq_failures, link_up); diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c index bafdcf70a353d534b2c92fcc898b435f87ffa951..fdab974b245b76acff56b5b10afd27e86e4eba2b 100644 --- a/drivers/net/ethernet/intel/igb/e1000_82575.c +++ b/drivers/net/ethernet/intel/igb/e1000_82575.c @@ -530,7 +530,7 @@ static s32 igb_set_sfp_media_type_82575(struct e1000_hw *hw) dev_spec->module_plugged = true; if (eth_flags->e1000_base_lx || eth_flags->e1000_base_sx) { hw->phy.media_type = e1000_media_type_internal_serdes; - } else if (eth_flags->e100_base_fx) { + } else if (eth_flags->e100_base_fx || eth_flags->e100_base_lx) { dev_spec->sgmii_active = true; hw->phy.media_type = e1000_media_type_internal_serdes; } else if (eth_flags->e1000_base_t) { @@ -657,14 +657,10 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw) break; } - /* do not change link mode for 100BaseFX */ - if (dev_spec->eth_flags.e100_base_fx) - break; - /* change current link mode setting */ ctrl_ext &= ~E1000_CTRL_EXT_LINK_MODE_MASK; - if (hw->phy.media_type == e1000_media_type_copper) + if (dev_spec->sgmii_active) ctrl_ext |= E1000_CTRL_EXT_LINK_MODE_SGMII; else ctrl_ext |= E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES; diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c index 5acf3b743876a485f61002658dccba47a0ca3d59..50954e4449854aacbba793345c38bbcf8a24cffa 100644 --- a/drivers/net/ethernet/intel/igb/igb_ethtool.c +++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c @@ -181,7 +181,7 @@ static int igb_get_link_ksettings(struct net_device *netdev, advertising &= ~ADVERTISED_1000baseKX_Full; } } - if (eth_flags->e100_base_fx) { + if (eth_flags->e100_base_fx || eth_flags->e100_base_lx) { supported |= SUPPORTED_100baseT_Full; advertising |= ADVERTISED_100baseT_Full; } diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.c index b27f7a968820d6f62417fb32e74817faa7a6cb89..49e6d66ccf8027a179201fac32a62f690f9ea171 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.c @@ -114,7 +114,6 @@ static void ixgbe_ipsec_set_rx_ip(struct ixgbe_hw *hw, u16 idx, __be32 addr[]) **/ static void ixgbe_ipsec_clear_hw_tables(struct ixgbe_adapter *adapter) { - struct ixgbe_ipsec *ipsec = adapter->ipsec; struct ixgbe_hw *hw = &adapter->hw; u32 buf[4] = {0, 0, 0, 0}; u16 idx; @@ -133,9 +132,6 @@ static void ixgbe_ipsec_clear_hw_tables(struct ixgbe_adapter *adapter) ixgbe_ipsec_set_tx_sa(hw, idx, buf, 0); ixgbe_ipsec_set_rx_sa(hw, idx, 0, buf, 0, 0, 0); } - - ipsec->num_rx_sa = 0; - ipsec->num_tx_sa = 0; } /** diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index de65ca1e6558076175cbb291a3c9eed4bb6bda08..8177276500f5a67d46502d9935efed88086205cb 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -1822,13 +1822,7 @@ static void ixgbe_pull_tail(struct ixgbe_ring *rx_ring, static void ixgbe_dma_sync_frag(struct ixgbe_ring *rx_ring, struct sk_buff *skb) { - /* if the page was released unmap it, else just sync our portion */ - if (unlikely(IXGBE_CB(skb)->page_released)) { - dma_unmap_page_attrs(rx_ring->dev, IXGBE_CB(skb)->dma, - ixgbe_rx_pg_size(rx_ring), - DMA_FROM_DEVICE, - IXGBE_RX_DMA_ATTR); - } else if (ring_uses_build_skb(rx_ring)) { + if (ring_uses_build_skb(rx_ring)) { unsigned long offset = (unsigned long)(skb->data) & ~PAGE_MASK; dma_sync_single_range_for_cpu(rx_ring->dev, @@ -1845,6 +1839,14 @@ static void ixgbe_dma_sync_frag(struct ixgbe_ring *rx_ring, skb_frag_size(frag), DMA_FROM_DEVICE); } + + /* If the page was released, just unmap it. */ + if (unlikely(IXGBE_CB(skb)->page_released)) { + dma_unmap_page_attrs(rx_ring->dev, IXGBE_CB(skb)->dma, + ixgbe_rx_pg_size(rx_ring), + DMA_FROM_DEVICE, + IXGBE_RX_DMA_ATTR); + } } /** @@ -5187,7 +5189,7 @@ static void ixgbe_fdir_filter_restore(struct ixgbe_adapter *adapter) struct ixgbe_hw *hw = &adapter->hw; struct hlist_node *node2; struct ixgbe_fdir_filter *filter; - u64 action; + u8 queue; spin_lock(&adapter->fdir_perfect_lock); @@ -5196,17 +5198,34 @@ static void ixgbe_fdir_filter_restore(struct ixgbe_adapter *adapter) hlist_for_each_entry_safe(filter, node2, &adapter->fdir_filter_list, fdir_node) { - action = filter->action; - if (action != IXGBE_FDIR_DROP_QUEUE && action != 0) - action = - (action >> ETHTOOL_RX_FLOW_SPEC_RING_VF_OFF) - 1; + if (filter->action == IXGBE_FDIR_DROP_QUEUE) { + queue = IXGBE_FDIR_DROP_QUEUE; + } else { + u32 ring = ethtool_get_flow_spec_ring(filter->action); + u8 vf = ethtool_get_flow_spec_ring_vf(filter->action); + + if (!vf && (ring >= adapter->num_rx_queues)) { + e_err(drv, "FDIR restore failed without VF, ring: %u\n", + ring); + continue; + } else if (vf && + ((vf > adapter->num_vfs) || + ring >= adapter->num_rx_queues_per_pool)) { + e_err(drv, "FDIR restore failed with VF, vf: %hhu, ring: %u\n", + vf, ring); + continue; + } + + /* Map the ring onto the absolute queue index */ + if (!vf) + queue = adapter->rx_ring[ring]->reg_idx; + else + queue = ((vf - 1) * + adapter->num_rx_queues_per_pool) + ring; + } ixgbe_fdir_write_perfect_filter_82599(hw, - &filter->filter, - filter->sw_idx, - (action == IXGBE_FDIR_DROP_QUEUE) ? - IXGBE_FDIR_DROP_QUEUE : - adapter->rx_ring[action]->reg_idx); + &filter->filter, filter->sw_idx, queue); } spin_unlock(&adapter->fdir_perfect_lock); diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 4093a9c52c182ee2d7cb01bcedbde90f8dfeab44..a10756f0b0d8bd89586e04f5dc37751a8cc74fcb 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -2066,11 +2066,6 @@ static int ixgbevf_write_uc_addr_list(struct net_device *netdev) struct ixgbe_hw *hw = &adapter->hw; int count = 0; - if ((netdev_uc_count(netdev)) > 10) { - pr_err("Too many unicast filters - No Space\n"); - return -ENOSPC; - } - if (!netdev_uc_empty(netdev)) { struct netdev_hw_addr *ha; diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 4313bbb2396f4df801ffc099fa385f9b3e51e425..51885e6dec50bc9391038bf204db1583bfaf12eb 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -385,6 +385,8 @@ struct mvneta_pcpu_stats { struct u64_stats_sync syncp; u64 rx_packets; u64 rx_bytes; + u64 rx_dropped; + u64 rx_errors; u64 tx_packets; u64 tx_bytes; }; @@ -701,6 +703,8 @@ mvneta_get_stats64(struct net_device *dev, struct mvneta_pcpu_stats *cpu_stats; u64 rx_packets; u64 rx_bytes; + u64 rx_dropped; + u64 rx_errors; u64 tx_packets; u64 tx_bytes; @@ -709,19 +713,20 @@ mvneta_get_stats64(struct net_device *dev, start = u64_stats_fetch_begin_irq(&cpu_stats->syncp); rx_packets = cpu_stats->rx_packets; rx_bytes = cpu_stats->rx_bytes; + rx_dropped = cpu_stats->rx_dropped; + rx_errors = cpu_stats->rx_errors; tx_packets = cpu_stats->tx_packets; tx_bytes = cpu_stats->tx_bytes; } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start)); stats->rx_packets += rx_packets; stats->rx_bytes += rx_bytes; + stats->rx_dropped += rx_dropped; + stats->rx_errors += rx_errors; stats->tx_packets += tx_packets; stats->tx_bytes += tx_bytes; } - stats->rx_errors = dev->stats.rx_errors; - stats->rx_dropped = dev->stats.rx_dropped; - stats->tx_dropped = dev->stats.tx_dropped; } @@ -1698,8 +1703,14 @@ static u32 mvneta_txq_desc_csum(int l3_offs, int l3_proto, static void mvneta_rx_error(struct mvneta_port *pp, struct mvneta_rx_desc *rx_desc) { + struct mvneta_pcpu_stats *stats = this_cpu_ptr(pp->stats); u32 status = rx_desc->status; + /* update per-cpu counter */ + u64_stats_update_begin(&stats->syncp); + stats->rx_errors++; + u64_stats_update_end(&stats->syncp); + switch (status & MVNETA_RXD_ERR_CODE_MASK) { case MVNETA_RXD_ERR_CRC: netdev_err(pp->dev, "bad rx status %08x (crc error), size=%d\n", @@ -1960,7 +1971,6 @@ static int mvneta_rx_swbm(struct napi_struct *napi, /* Check errors only for FIRST descriptor */ if (rx_status & MVNETA_RXD_ERR_SUMMARY) { mvneta_rx_error(pp, rx_desc); - dev->stats.rx_errors++; /* leave the descriptor untouched */ continue; } @@ -1971,11 +1981,17 @@ static int mvneta_rx_swbm(struct napi_struct *napi, skb_size = max(rx_copybreak, rx_header_size); rxq->skb = netdev_alloc_skb_ip_align(dev, skb_size); if (unlikely(!rxq->skb)) { + struct mvneta_pcpu_stats *stats = this_cpu_ptr(pp->stats); + netdev_err(dev, "Can't allocate skb on queue %d\n", rxq->id); - dev->stats.rx_dropped++; + rxq->skb_alloc_err++; + + u64_stats_update_begin(&stats->syncp); + stats->rx_dropped++; + u64_stats_update_end(&stats->syncp); continue; } copy_size = min(skb_size, rx_bytes); @@ -2135,7 +2151,6 @@ static int mvneta_rx_hwbm(struct napi_struct *napi, mvneta_bm_pool_put_bp(pp->bm_priv, bm_pool, rx_desc->buf_phys_addr); err_drop_frame: - dev->stats.rx_errors++; mvneta_rx_error(pp, rx_desc); /* leave the descriptor untouched */ continue; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index 9cbc4173973e9327ee8f0135a7b216ac86edcd48..044687a1f27cc2cf260bbf3412206b24b2ba8b71 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -1364,8 +1364,14 @@ static inline void mlx5i_complete_rx_cqe(struct mlx5e_rq *rq, skb->protocol = *((__be16 *)(skb->data)); - skb->ip_summed = CHECKSUM_COMPLETE; - skb->csum = csum_unfold((__force __sum16)cqe->check_sum); + if (netdev->features & NETIF_F_RXCSUM) { + skb->ip_summed = CHECKSUM_COMPLETE; + skb->csum = csum_unfold((__force __sum16)cqe->check_sum); + stats->csum_complete++; + } else { + skb->ip_summed = CHECKSUM_NONE; + stats->csum_none++; + } if (unlikely(mlx5e_rx_hw_stamp(tstamp))) skb_hwtstamps(skb)->hwtstamp = @@ -1384,7 +1390,6 @@ static inline void mlx5i_complete_rx_cqe(struct mlx5e_rq *rq, skb->dev = netdev; - stats->csum_complete++; stats->packets++; stats->bytes += cqe_bcnt; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c index 8255d797ea9434829f82f93bbf44ce122e76c6e7..9a68dee588c1af41921ba8a17bf5547934d2fc05 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c @@ -211,6 +211,9 @@ void mlx5e_grp_sw_update_stats(struct mlx5e_priv *priv) s->tx_tls_resync_bytes += sq_stats->tls_resync_bytes; #endif s->tx_cqes += sq_stats->cqes; + + /* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92657 */ + barrier(); } } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fpga/core.c b/drivers/net/ethernet/mellanox/mlx5/core/fpga/core.c index 436a8136f26ff5f8b879eb02313beabb717f779e..310f9e7d8320045093b35ab42651d465d7cc3465 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fpga/core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fpga/core.c @@ -289,7 +289,6 @@ void mlx5_fpga_event(struct mlx5_core_dev *mdev, u8 event, void *data) const char *event_name; bool teardown = false; unsigned long flags; - u32 fpga_qpn; u8 syndrome; switch (event) { @@ -300,7 +299,6 @@ void mlx5_fpga_event(struct mlx5_core_dev *mdev, u8 event, void *data) case MLX5_EVENT_TYPE_FPGA_QP_ERROR: syndrome = MLX5_GET(fpga_qp_error_event, data, syndrome); event_name = mlx5_fpga_qp_syndrome_to_string(syndrome); - fpga_qpn = MLX5_GET(fpga_qp_error_event, data, fpga_qpn); break; default: mlx5_fpga_warn_ratelimited(fdev, "Unexpected event %u\n", diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.c index b5a8769a5bfda91a190fb1bbf4391e420638f1d5..715ccafc92cd3b3e0058512c8cd3a9b6d8dc4e6c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.c @@ -848,6 +848,7 @@ void mlx5_fpga_ipsec_delete_sa_ctx(void *context) mutex_lock(&fpga_xfrm->lock); if (!--fpga_xfrm->num_rules) { mlx5_fpga_ipsec_release_sa_ctx(fpga_xfrm->sa_ctx); + kfree(fpga_xfrm->sa_ctx); fpga_xfrm->sa_ctx = NULL; } mutex_unlock(&fpga_xfrm->lock); @@ -1472,7 +1473,7 @@ int mlx5_fpga_esp_modify_xfrm(struct mlx5_accel_esp_xfrm *xfrm, if (!memcmp(&xfrm->attrs, attrs, sizeof(xfrm->attrs))) return 0; - if (!mlx5_fpga_esp_validate_xfrm_attrs(mdev, attrs)) { + if (mlx5_fpga_esp_validate_xfrm_attrs(mdev, attrs)) { mlx5_core_warn(mdev, "Tried to create an esp with unsupported attrs\n"); return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index 82a53317285d0d181f1d4baffc85563920986be1..b16e0f45d28c5ac74f1922bf56c391568eeae41d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -469,6 +469,7 @@ static void del_hw_fte(struct fs_node *node) mlx5_core_warn(dev, "flow steering can't delete fte in index %d of flow group id %d\n", fte->index, fg->id); + node->active = 0; } } @@ -1597,6 +1598,11 @@ lookup_fte_locked(struct mlx5_flow_group *g, fte_tmp = NULL; goto out; } + if (!fte_tmp->node.active) { + tree_put_node(&fte_tmp->node); + fte_tmp = NULL; + goto out; + } nested_down_write_ref_node(&fte_tmp->node, FS_LOCK_CHILD); out: diff --git a/drivers/net/ethernet/mellanox/mlx5/core/qp.c b/drivers/net/ethernet/mellanox/mlx5/core/qp.c index f33707ce8b6b0f26d9ace1d1f20e915e29f49913..479ac21cdbc6940701f1029ba31c699449293235 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/qp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/qp.c @@ -44,14 +44,15 @@ static struct mlx5_core_rsc_common *mlx5_get_rsc(struct mlx5_core_dev *dev, { struct mlx5_qp_table *table = &dev->priv.qp_table; struct mlx5_core_rsc_common *common; + unsigned long flags; - spin_lock(&table->lock); + spin_lock_irqsave(&table->lock, flags); common = radix_tree_lookup(&table->tree, rsn); if (common) atomic_inc(&common->refcount); - spin_unlock(&table->lock); + spin_unlock_irqrestore(&table->lock, flags); if (!common) { mlx5_core_warn(dev, "Async event for bogus resource 0x%x\n", diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index aee58b3892f20c2279f74e91d82318060585fcee..c9895876a23177e13cd7dbf22be02b032eb9cef4 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -3215,7 +3215,7 @@ static inline void mlxsw_reg_qtct_pack(char *payload, u8 local_port, * Configures the ETS elements. */ #define MLXSW_REG_QEEC_ID 0x400D -#define MLXSW_REG_QEEC_LEN 0x1C +#define MLXSW_REG_QEEC_LEN 0x20 MLXSW_REG_DEFINE(qeec, MLXSW_REG_QEEC_ID, MLXSW_REG_QEEC_LEN); @@ -3257,6 +3257,15 @@ MLXSW_ITEM32(reg, qeec, element_index, 0x04, 0, 8); */ MLXSW_ITEM32(reg, qeec, next_element_index, 0x08, 0, 8); +/* reg_qeec_mise + * Min shaper configuration enable. Enables configuration of the min + * shaper on this ETS element + * 0 - Disable + * 1 - Enable + * Access: RW + */ +MLXSW_ITEM32(reg, qeec, mise, 0x0C, 31, 1); + enum { MLXSW_REG_QEEC_BYTES_MODE, MLXSW_REG_QEEC_PACKETS_MODE, @@ -3273,6 +3282,17 @@ enum { */ MLXSW_ITEM32(reg, qeec, pb, 0x0C, 28, 1); +/* The smallest permitted min shaper rate. */ +#define MLXSW_REG_QEEC_MIS_MIN 200000 /* Kbps */ + +/* reg_qeec_min_shaper_rate + * Min shaper information rate. + * For CPU port, can only be configured for port hierarchy. + * When in bytes mode, value is specified in units of 1000bps. + * Access: RW + */ +MLXSW_ITEM32(reg, qeec, min_shaper_rate, 0x0C, 0, 28); + /* reg_qeec_mase * Max shaper configuration enable. Enables configuration of the max * shaper on this ETS element. diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index e498ee95bacab35c2f3363f1152b6a419fe2a282..5df9b25cab27d6bfc2322032b39c26f6b75b893a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1061,6 +1061,9 @@ static void update_stats_cache(struct work_struct *work) periodic_hw_stats.update_dw.work); if (!netif_carrier_ok(mlxsw_sp_port->dev)) + /* Note: mlxsw_sp_port_down_wipe_counters() clears the cache as + * necessary when port goes down. + */ goto out; mlxsw_sp_port_get_hw_stats(mlxsw_sp_port->dev, @@ -2750,6 +2753,21 @@ int mlxsw_sp_port_ets_maxrate_set(struct mlxsw_sp_port *mlxsw_sp_port, return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qeec), qeec_pl); } +static int mlxsw_sp_port_min_bw_set(struct mlxsw_sp_port *mlxsw_sp_port, + enum mlxsw_reg_qeec_hr hr, u8 index, + u8 next_index, u32 minrate) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char qeec_pl[MLXSW_REG_QEEC_LEN]; + + mlxsw_reg_qeec_pack(qeec_pl, mlxsw_sp_port->local_port, hr, index, + next_index); + mlxsw_reg_qeec_mise_set(qeec_pl, true); + mlxsw_reg_qeec_min_shaper_rate_set(qeec_pl, minrate); + + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qeec), qeec_pl); +} + int mlxsw_sp_port_prio_tc_set(struct mlxsw_sp_port *mlxsw_sp_port, u8 switch_prio, u8 tclass) { @@ -2827,6 +2845,16 @@ static int mlxsw_sp_port_ets_init(struct mlxsw_sp_port *mlxsw_sp_port) return err; } + /* Configure the min shaper for multicast TCs. */ + for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { + err = mlxsw_sp_port_min_bw_set(mlxsw_sp_port, + MLXSW_REG_QEEC_HIERARCY_TC, + i + 8, i, + MLXSW_REG_QEEC_MIS_MIN); + if (err) + return err; + } + /* Map all priorities to traffic class 0. */ for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { err = mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i, 0); @@ -3309,6 +3337,15 @@ static int mlxsw_sp_port_unsplit(struct mlxsw_core *mlxsw_core, u8 local_port, return 0; } +static void +mlxsw_sp_port_down_wipe_counters(struct mlxsw_sp_port *mlxsw_sp_port) +{ + int i; + + for (i = 0; i < TC_MAX_QUEUE; i++) + mlxsw_sp_port->periodic_hw_stats.xstats.backlog[i] = 0; +} + static void mlxsw_sp_pude_event_func(const struct mlxsw_reg_info *reg, char *pude_pl, void *priv) { @@ -3329,6 +3366,7 @@ static void mlxsw_sp_pude_event_func(const struct mlxsw_reg_info *reg, } else { netdev_info(mlxsw_sp_port->dev, "link down\n"); netif_carrier_off(mlxsw_sp_port->dev); + mlxsw_sp_port_down_wipe_counters(mlxsw_sp_port); } } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c index 41e607a14846db0a791f721c15c2171caf736881..4fe193c4fa55da6e0f1d9c55a1f4aa1e966eaf68 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c @@ -215,7 +215,7 @@ mlxsw_sp_dpipe_table_erif_entries_dump(void *priv, bool counters_enabled, start_again: err = devlink_dpipe_entry_ctx_prepare(dump_ctx); if (err) - return err; + goto err_ctx_prepare; j = 0; for (; i < rif_count; i++) { struct mlxsw_sp_rif *rif = mlxsw_sp_rif_by_index(mlxsw_sp, i); @@ -247,6 +247,7 @@ mlxsw_sp_dpipe_table_erif_entries_dump(void *priv, bool counters_enabled, return 0; err_entry_append: err_entry_get: +err_ctx_prepare: rtnl_unlock(); devlink_dpipe_entry_clear(&entry); return err; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c index 720514b5c006eb29824254f4231b1ce74b12e632..dc63583c49483375ff8d62d69a2733cd3032c946 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c @@ -195,6 +195,20 @@ mlxsw_sp_qdisc_get_xstats(struct mlxsw_sp_port *mlxsw_sp_port, return -EOPNOTSUPP; } +static u64 +mlxsw_sp_xstats_backlog(struct mlxsw_sp_port_xstats *xstats, int tclass_num) +{ + return xstats->backlog[tclass_num] + + xstats->backlog[tclass_num + 8]; +} + +static u64 +mlxsw_sp_xstats_tail_drop(struct mlxsw_sp_port_xstats *xstats, int tclass_num) +{ + return xstats->tail_drop[tclass_num] + + xstats->tail_drop[tclass_num + 8]; +} + static void mlxsw_sp_qdisc_bstats_per_priority_get(struct mlxsw_sp_port_xstats *xstats, u8 prio_bitmap, u64 *tx_packets, @@ -269,7 +283,7 @@ mlxsw_sp_setup_tc_qdisc_red_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port, &stats_base->tx_bytes); red_base->prob_mark = xstats->ecn; red_base->prob_drop = xstats->wred_drop[tclass_num]; - red_base->pdrop = xstats->tail_drop[tclass_num]; + red_base->pdrop = mlxsw_sp_xstats_tail_drop(xstats, tclass_num); stats_base->overlimits = red_base->prob_drop + red_base->prob_mark; stats_base->drops = red_base->prob_drop + red_base->pdrop; @@ -369,7 +383,8 @@ mlxsw_sp_qdisc_get_red_xstats(struct mlxsw_sp_port *mlxsw_sp_port, early_drops = xstats->wred_drop[tclass_num] - xstats_base->prob_drop; marks = xstats->ecn - xstats_base->prob_mark; - pdrops = xstats->tail_drop[tclass_num] - xstats_base->pdrop; + pdrops = mlxsw_sp_xstats_tail_drop(xstats, tclass_num) - + xstats_base->pdrop; res->pdrop += pdrops; res->prob_drop += early_drops; @@ -402,9 +417,10 @@ mlxsw_sp_qdisc_get_red_stats(struct mlxsw_sp_port *mlxsw_sp_port, overlimits = xstats->wred_drop[tclass_num] + xstats->ecn - stats_base->overlimits; - drops = xstats->wred_drop[tclass_num] + xstats->tail_drop[tclass_num] - + drops = xstats->wred_drop[tclass_num] + + mlxsw_sp_xstats_tail_drop(xstats, tclass_num) - stats_base->drops; - backlog = xstats->backlog[tclass_num]; + backlog = mlxsw_sp_xstats_backlog(xstats, tclass_num); _bstats_update(stats_ptr->bstats, tx_bytes, tx_packets); stats_ptr->qstats->overlimits += overlimits; @@ -575,9 +591,9 @@ mlxsw_sp_qdisc_get_prio_stats(struct mlxsw_sp_port *mlxsw_sp_port, tx_packets = stats->tx_packets - stats_base->tx_packets; for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { - drops += xstats->tail_drop[i]; + drops += mlxsw_sp_xstats_tail_drop(xstats, i); drops += xstats->wred_drop[i]; - backlog += xstats->backlog[i]; + backlog += mlxsw_sp_xstats_backlog(xstats, i); } drops = drops - stats_base->drops; @@ -613,7 +629,7 @@ mlxsw_sp_setup_tc_qdisc_prio_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port, stats_base->drops = 0; for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { - stats_base->drops += xstats->tail_drop[i]; + stats_base->drops += mlxsw_sp_xstats_tail_drop(xstats, i); stats_base->drops += xstats->wred_drop[i]; } diff --git a/drivers/net/ethernet/micrel/ks8851_mll.c b/drivers/net/ethernet/micrel/ks8851_mll.c index 35f8c9ef204d91cd4c17591d84ebab597cff33b4..9de59facec21842af42cc9b9cf7f1d415fcde1b4 100644 --- a/drivers/net/ethernet/micrel/ks8851_mll.c +++ b/drivers/net/ethernet/micrel/ks8851_mll.c @@ -475,24 +475,6 @@ static int msg_enable; * chip is busy transferring packet data (RX/TX FIFO accesses). */ -/** - * ks_rdreg8 - read 8 bit register from device - * @ks : The chip information - * @offset: The register address - * - * Read a 8bit register from the chip, returning the result - */ -static u8 ks_rdreg8(struct ks_net *ks, int offset) -{ - u16 data; - u8 shift_bit = offset & 0x03; - u8 shift_data = (offset & 1) << 3; - ks->cmd_reg_cache = (u16) offset | (u16)(BE0 << shift_bit); - iowrite16(ks->cmd_reg_cache, ks->hw_addr_cmd); - data = ioread16(ks->hw_addr); - return (u8)(data >> shift_data); -} - /** * ks_rdreg16 - read 16 bit register from device * @ks : The chip information @@ -503,27 +485,11 @@ static u8 ks_rdreg8(struct ks_net *ks, int offset) static u16 ks_rdreg16(struct ks_net *ks, int offset) { - ks->cmd_reg_cache = (u16)offset | ((BE1 | BE0) << (offset & 0x02)); + ks->cmd_reg_cache = (u16)offset | ((BE3 | BE2) >> (offset & 0x02)); iowrite16(ks->cmd_reg_cache, ks->hw_addr_cmd); return ioread16(ks->hw_addr); } -/** - * ks_wrreg8 - write 8bit register value to chip - * @ks: The chip information - * @offset: The register address - * @value: The value to write - * - */ -static void ks_wrreg8(struct ks_net *ks, int offset, u8 value) -{ - u8 shift_bit = (offset & 0x03); - u16 value_write = (u16)(value << ((offset & 1) << 3)); - ks->cmd_reg_cache = (u16)offset | (BE0 << shift_bit); - iowrite16(ks->cmd_reg_cache, ks->hw_addr_cmd); - iowrite16(value_write, ks->hw_addr); -} - /** * ks_wrreg16 - write 16bit register value to chip * @ks: The chip information @@ -534,7 +500,7 @@ static void ks_wrreg8(struct ks_net *ks, int offset, u8 value) static void ks_wrreg16(struct ks_net *ks, int offset, u16 value) { - ks->cmd_reg_cache = (u16)offset | ((BE1 | BE0) << (offset & 0x02)); + ks->cmd_reg_cache = (u16)offset | ((BE3 | BE2) >> (offset & 0x02)); iowrite16(ks->cmd_reg_cache, ks->hw_addr_cmd); iowrite16(value, ks->hw_addr); } @@ -550,7 +516,7 @@ static inline void ks_inblk(struct ks_net *ks, u16 *wptr, u32 len) { len >>= 1; while (len--) - *wptr++ = (u16)ioread16(ks->hw_addr); + *wptr++ = be16_to_cpu(ioread16(ks->hw_addr)); } /** @@ -564,7 +530,7 @@ static inline void ks_outblk(struct ks_net *ks, u16 *wptr, u32 len) { len >>= 1; while (len--) - iowrite16(*wptr++, ks->hw_addr); + iowrite16(cpu_to_be16(*wptr++), ks->hw_addr); } static void ks_disable_int(struct ks_net *ks) @@ -643,8 +609,7 @@ static void ks_read_config(struct ks_net *ks) u16 reg_data = 0; /* Regardless of bus width, 8 bit read should always work.*/ - reg_data = ks_rdreg8(ks, KS_CCR) & 0x00FF; - reg_data |= ks_rdreg8(ks, KS_CCR+1) << 8; + reg_data = ks_rdreg16(ks, KS_CCR); /* addr/data bus are multiplexed */ ks->sharedbus = (reg_data & CCR_SHARED) == CCR_SHARED; @@ -748,7 +713,7 @@ static inline void ks_read_qmu(struct ks_net *ks, u16 *buf, u32 len) /* 1. set sudo DMA mode */ ks_wrreg16(ks, KS_RXFDPR, RXFDPR_RXFPAI); - ks_wrreg8(ks, KS_RXQCR, (ks->rc_rxqcr | RXQCR_SDA) & 0xff); + ks_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr | RXQCR_SDA); /* 2. read prepend data */ /** @@ -765,7 +730,7 @@ static inline void ks_read_qmu(struct ks_net *ks, u16 *buf, u32 len) ks_inblk(ks, buf, ALIGN(len, 4)); /* 4. reset sudo DMA Mode */ - ks_wrreg8(ks, KS_RXQCR, ks->rc_rxqcr); + ks_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr); } /** @@ -998,13 +963,13 @@ static void ks_write_qmu(struct ks_net *ks, u8 *pdata, u16 len) ks->txh.txw[1] = cpu_to_le16(len); /* 1. set sudo-DMA mode */ - ks_wrreg8(ks, KS_RXQCR, (ks->rc_rxqcr | RXQCR_SDA) & 0xff); + ks_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr | RXQCR_SDA); /* 2. write status/lenth info */ ks_outblk(ks, ks->txh.txw, 4); /* 3. write pkt data */ ks_outblk(ks, (u16 *)pdata, ALIGN(len, 4)); /* 4. reset sudo-DMA mode */ - ks_wrreg8(ks, KS_RXQCR, ks->rc_rxqcr); + ks_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr); /* 5. Enqueue Tx(move the pkt from TX buffer into TXQ) */ ks_wrreg16(ks, KS_TXQCR, TXQCR_METFE); /* 6. wait until TXQCR_METFE is auto-cleared */ diff --git a/drivers/net/ethernet/mscc/ocelot_board.c b/drivers/net/ethernet/mscc/ocelot_board.c index 3cdf63e35b53bc1ea3e2f445e6a3746a56655bd3..4054cf9db8181185eb430196763387d75f179fde 100644 --- a/drivers/net/ethernet/mscc/ocelot_board.c +++ b/drivers/net/ethernet/mscc/ocelot_board.c @@ -105,6 +105,14 @@ static irqreturn_t ocelot_xtr_irq_handler(int irq, void *arg) if (err != 4) break; + /* At this point the IFH was read correctly, so it is safe to + * presume that there is no error. The err needs to be reset + * otherwise a frame could come in CPU queue between the while + * condition and the check for error later on. And in that case + * the new frame is just removed and not processed. + */ + err = 0; + ocelot_parse_ifh(ifh, &info); dev = ocelot->ports[info.port]->dev; diff --git a/drivers/net/ethernet/natsemi/sonic.c b/drivers/net/ethernet/natsemi/sonic.c index c805dcbebd0278752b2ed8b34b7531ec19ea2357..69282f31d519e81ee02a86ad106c6040479c0912 100644 --- a/drivers/net/ethernet/natsemi/sonic.c +++ b/drivers/net/ethernet/natsemi/sonic.c @@ -63,6 +63,8 @@ static int sonic_open(struct net_device *dev) netif_dbg(lp, ifup, dev, "%s: initializing sonic driver\n", __func__); + spin_lock_init(&lp->lock); + for (i = 0; i < SONIC_NUM_RRS; i++) { struct sk_buff *skb = netdev_alloc_skb(dev, SONIC_RBSIZE + 2); if (skb == NULL) { @@ -113,6 +115,24 @@ static int sonic_open(struct net_device *dev) return 0; } +/* Wait for the SONIC to become idle. */ +static void sonic_quiesce(struct net_device *dev, u16 mask) +{ + struct sonic_local * __maybe_unused lp = netdev_priv(dev); + int i; + u16 bits; + + for (i = 0; i < 1000; ++i) { + bits = SONIC_READ(SONIC_CMD) & mask; + if (!bits) + return; + if (irqs_disabled() || in_interrupt()) + udelay(20); + else + usleep_range(100, 200); + } + WARN_ONCE(1, "command deadline expired! 0x%04x\n", bits); +} /* * Close the SONIC device @@ -129,6 +149,9 @@ static int sonic_close(struct net_device *dev) /* * stop the SONIC, disable interrupts */ + SONIC_WRITE(SONIC_CMD, SONIC_CR_RXDIS); + sonic_quiesce(dev, SONIC_CR_ALL); + SONIC_WRITE(SONIC_IMR, 0); SONIC_WRITE(SONIC_ISR, 0x7fff); SONIC_WRITE(SONIC_CMD, SONIC_CR_RST); @@ -168,6 +191,9 @@ static void sonic_tx_timeout(struct net_device *dev) * put the Sonic into software-reset mode and * disable all interrupts before releasing DMA buffers */ + SONIC_WRITE(SONIC_CMD, SONIC_CR_RXDIS); + sonic_quiesce(dev, SONIC_CR_ALL); + SONIC_WRITE(SONIC_IMR, 0); SONIC_WRITE(SONIC_ISR, 0x7fff); SONIC_WRITE(SONIC_CMD, SONIC_CR_RST); @@ -205,8 +231,6 @@ static void sonic_tx_timeout(struct net_device *dev) * wake the tx queue * Concurrently with all of this, the SONIC is potentially writing to * the status flags of the TDs. - * Until some mutual exclusion is added, this code will not work with SMP. However, - * MIPS Jazz machines and m68k Macs were all uni-processor machines. */ static int sonic_send_packet(struct sk_buff *skb, struct net_device *dev) @@ -214,7 +238,8 @@ static int sonic_send_packet(struct sk_buff *skb, struct net_device *dev) struct sonic_local *lp = netdev_priv(dev); dma_addr_t laddr; int length; - int entry = lp->next_tx; + int entry; + unsigned long flags; netif_dbg(lp, tx_queued, dev, "%s: skb=%p\n", __func__, skb); @@ -231,11 +256,15 @@ static int sonic_send_packet(struct sk_buff *skb, struct net_device *dev) laddr = dma_map_single(lp->device, skb->data, length, DMA_TO_DEVICE); if (!laddr) { - printk(KERN_ERR "%s: failed to map tx DMA buffer.\n", dev->name); - dev_kfree_skb(skb); - return NETDEV_TX_BUSY; + pr_err_ratelimited("%s: failed to map tx DMA buffer.\n", dev->name); + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; } + spin_lock_irqsave(&lp->lock, flags); + + entry = lp->next_tx; + sonic_tda_put(dev, entry, SONIC_TD_STATUS, 0); /* clear status */ sonic_tda_put(dev, entry, SONIC_TD_FRAG_COUNT, 1); /* single fragment */ sonic_tda_put(dev, entry, SONIC_TD_PKTSIZE, length); /* length of packet */ @@ -245,10 +274,6 @@ static int sonic_send_packet(struct sk_buff *skb, struct net_device *dev) sonic_tda_put(dev, entry, SONIC_TD_LINK, sonic_tda_get(dev, entry, SONIC_TD_LINK) | SONIC_EOL); - /* - * Must set tx_skb[entry] only after clearing status, and - * before clearing EOL and before stopping queue - */ wmb(); lp->tx_len[entry] = length; lp->tx_laddr[entry] = laddr; @@ -271,6 +296,8 @@ static int sonic_send_packet(struct sk_buff *skb, struct net_device *dev) SONIC_WRITE(SONIC_CMD, SONIC_CR_TXP); + spin_unlock_irqrestore(&lp->lock, flags); + return NETDEV_TX_OK; } @@ -283,15 +310,28 @@ static irqreturn_t sonic_interrupt(int irq, void *dev_id) struct net_device *dev = dev_id; struct sonic_local *lp = netdev_priv(dev); int status; + unsigned long flags; + + /* The lock has two purposes. Firstly, it synchronizes sonic_interrupt() + * with sonic_send_packet() so that the two functions can share state. + * Secondly, it makes sonic_interrupt() re-entrant, as that is required + * by macsonic which must use two IRQs with different priority levels. + */ + spin_lock_irqsave(&lp->lock, flags); + + status = SONIC_READ(SONIC_ISR) & SONIC_IMR_DEFAULT; + if (!status) { + spin_unlock_irqrestore(&lp->lock, flags); - if (!(status = SONIC_READ(SONIC_ISR) & SONIC_IMR_DEFAULT)) return IRQ_NONE; + } do { + SONIC_WRITE(SONIC_ISR, status); /* clear the interrupt(s) */ + if (status & SONIC_INT_PKTRX) { netif_dbg(lp, intr, dev, "%s: packet rx\n", __func__); sonic_rx(dev); /* got packet(s) */ - SONIC_WRITE(SONIC_ISR, SONIC_INT_PKTRX); /* clear the interrupt */ } if (status & SONIC_INT_TXDN) { @@ -299,11 +339,12 @@ static irqreturn_t sonic_interrupt(int irq, void *dev_id) int td_status; int freed_some = 0; - /* At this point, cur_tx is the index of a TD that is one of: - * unallocated/freed (status set & tx_skb[entry] clear) - * allocated and sent (status set & tx_skb[entry] set ) - * allocated and not yet sent (status clear & tx_skb[entry] set ) - * still being allocated by sonic_send_packet (status clear & tx_skb[entry] clear) + /* The state of a Transmit Descriptor may be inferred + * from { tx_skb[entry], td_status } as follows. + * { clear, clear } => the TD has never been used + * { set, clear } => the TD was handed to SONIC + * { set, set } => the TD was handed back + * { clear, set } => the TD is available for re-use */ netif_dbg(lp, intr, dev, "%s: tx done\n", __func__); @@ -312,18 +353,19 @@ static irqreturn_t sonic_interrupt(int irq, void *dev_id) if ((td_status = sonic_tda_get(dev, entry, SONIC_TD_STATUS)) == 0) break; - if (td_status & 0x0001) { + if (td_status & SONIC_TCR_PTX) { lp->stats.tx_packets++; lp->stats.tx_bytes += sonic_tda_get(dev, entry, SONIC_TD_PKTSIZE); } else { - lp->stats.tx_errors++; - if (td_status & 0x0642) + if (td_status & (SONIC_TCR_EXD | + SONIC_TCR_EXC | SONIC_TCR_BCM)) lp->stats.tx_aborted_errors++; - if (td_status & 0x0180) + if (td_status & + (SONIC_TCR_NCRS | SONIC_TCR_CRLS)) lp->stats.tx_carrier_errors++; - if (td_status & 0x0020) + if (td_status & SONIC_TCR_OWC) lp->stats.tx_window_errors++; - if (td_status & 0x0004) + if (td_status & SONIC_TCR_FU) lp->stats.tx_fifo_errors++; } @@ -345,7 +387,6 @@ static irqreturn_t sonic_interrupt(int irq, void *dev_id) if (freed_some || lp->tx_skb[entry] == NULL) netif_wake_queue(dev); /* The ring is no longer full */ lp->cur_tx = entry; - SONIC_WRITE(SONIC_ISR, SONIC_INT_TXDN); /* clear the interrupt */ } /* @@ -354,42 +395,37 @@ static irqreturn_t sonic_interrupt(int irq, void *dev_id) if (status & SONIC_INT_RFO) { netif_dbg(lp, rx_err, dev, "%s: rx fifo overrun\n", __func__); - lp->stats.rx_fifo_errors++; - SONIC_WRITE(SONIC_ISR, SONIC_INT_RFO); /* clear the interrupt */ } if (status & SONIC_INT_RDE) { netif_dbg(lp, rx_err, dev, "%s: rx descriptors exhausted\n", __func__); - lp->stats.rx_dropped++; - SONIC_WRITE(SONIC_ISR, SONIC_INT_RDE); /* clear the interrupt */ } if (status & SONIC_INT_RBAE) { netif_dbg(lp, rx_err, dev, "%s: rx buffer area exceeded\n", __func__); - lp->stats.rx_dropped++; - SONIC_WRITE(SONIC_ISR, SONIC_INT_RBAE); /* clear the interrupt */ } /* counter overruns; all counters are 16bit wide */ - if (status & SONIC_INT_FAE) { + if (status & SONIC_INT_FAE) lp->stats.rx_frame_errors += 65536; - SONIC_WRITE(SONIC_ISR, SONIC_INT_FAE); /* clear the interrupt */ - } - if (status & SONIC_INT_CRC) { + if (status & SONIC_INT_CRC) lp->stats.rx_crc_errors += 65536; - SONIC_WRITE(SONIC_ISR, SONIC_INT_CRC); /* clear the interrupt */ - } - if (status & SONIC_INT_MP) { + if (status & SONIC_INT_MP) lp->stats.rx_missed_errors += 65536; - SONIC_WRITE(SONIC_ISR, SONIC_INT_MP); /* clear the interrupt */ - } /* transmit error */ if (status & SONIC_INT_TXER) { - if (SONIC_READ(SONIC_TCR) & SONIC_TCR_FU) - netif_dbg(lp, tx_err, dev, "%s: tx fifo underrun\n", - __func__); - SONIC_WRITE(SONIC_ISR, SONIC_INT_TXER); /* clear the interrupt */ + u16 tcr = SONIC_READ(SONIC_TCR); + + netif_dbg(lp, tx_err, dev, "%s: TXER intr, TCR %04x\n", + __func__, tcr); + + if (tcr & (SONIC_TCR_EXD | SONIC_TCR_EXC | + SONIC_TCR_FU | SONIC_TCR_BCM)) { + /* Aborted transmission. Try again. */ + netif_stop_queue(dev); + SONIC_WRITE(SONIC_CMD, SONIC_CR_TXP); + } } /* bus retry */ @@ -399,107 +435,164 @@ static irqreturn_t sonic_interrupt(int irq, void *dev_id) /* ... to help debug DMA problems causing endless interrupts. */ /* Bounce the eth interface to turn on the interrupt again. */ SONIC_WRITE(SONIC_IMR, 0); - SONIC_WRITE(SONIC_ISR, SONIC_INT_BR); /* clear the interrupt */ } - /* load CAM done */ - if (status & SONIC_INT_LCD) - SONIC_WRITE(SONIC_ISR, SONIC_INT_LCD); /* clear the interrupt */ - } while((status = SONIC_READ(SONIC_ISR) & SONIC_IMR_DEFAULT)); + status = SONIC_READ(SONIC_ISR) & SONIC_IMR_DEFAULT; + } while (status); + + spin_unlock_irqrestore(&lp->lock, flags); + return IRQ_HANDLED; } +/* Return the array index corresponding to a given Receive Buffer pointer. */ +static int index_from_addr(struct sonic_local *lp, dma_addr_t addr, + unsigned int last) +{ + unsigned int i = last; + + do { + i = (i + 1) & SONIC_RRS_MASK; + if (addr == lp->rx_laddr[i]) + return i; + } while (i != last); + + return -ENOENT; +} + +/* Allocate and map a new skb to be used as a receive buffer. */ +static bool sonic_alloc_rb(struct net_device *dev, struct sonic_local *lp, + struct sk_buff **new_skb, dma_addr_t *new_addr) +{ + *new_skb = netdev_alloc_skb(dev, SONIC_RBSIZE + 2); + if (!*new_skb) + return false; + + if (SONIC_BUS_SCALE(lp->dma_bitmode) == 2) + skb_reserve(*new_skb, 2); + + *new_addr = dma_map_single(lp->device, skb_put(*new_skb, SONIC_RBSIZE), + SONIC_RBSIZE, DMA_FROM_DEVICE); + if (!*new_addr) { + dev_kfree_skb(*new_skb); + *new_skb = NULL; + return false; + } + + return true; +} + +/* Place a new receive resource in the Receive Resource Area and update RWP. */ +static void sonic_update_rra(struct net_device *dev, struct sonic_local *lp, + dma_addr_t old_addr, dma_addr_t new_addr) +{ + unsigned int entry = sonic_rr_entry(dev, SONIC_READ(SONIC_RWP)); + unsigned int end = sonic_rr_entry(dev, SONIC_READ(SONIC_RRP)); + u32 buf; + + /* The resources in the range [RRP, RWP) belong to the SONIC. This loop + * scans the other resources in the RRA, those in the range [RWP, RRP). + */ + do { + buf = (sonic_rra_get(dev, entry, SONIC_RR_BUFADR_H) << 16) | + sonic_rra_get(dev, entry, SONIC_RR_BUFADR_L); + + if (buf == old_addr) + break; + + entry = (entry + 1) & SONIC_RRS_MASK; + } while (entry != end); + + WARN_ONCE(buf != old_addr, "failed to find resource!\n"); + + sonic_rra_put(dev, entry, SONIC_RR_BUFADR_H, new_addr >> 16); + sonic_rra_put(dev, entry, SONIC_RR_BUFADR_L, new_addr & 0xffff); + + entry = (entry + 1) & SONIC_RRS_MASK; + + SONIC_WRITE(SONIC_RWP, sonic_rr_addr(dev, entry)); +} + /* * We have a good packet(s), pass it/them up the network stack. */ static void sonic_rx(struct net_device *dev) { struct sonic_local *lp = netdev_priv(dev); - int status; int entry = lp->cur_rx; + int prev_entry = lp->eol_rx; + bool rbe = false; while (sonic_rda_get(dev, entry, SONIC_RD_IN_USE) == 0) { - struct sk_buff *used_skb; - struct sk_buff *new_skb; - dma_addr_t new_laddr; - u16 bufadr_l; - u16 bufadr_h; - int pkt_len; - - status = sonic_rda_get(dev, entry, SONIC_RD_STATUS); - if (status & SONIC_RCR_PRX) { - /* Malloc up new buffer. */ - new_skb = netdev_alloc_skb(dev, SONIC_RBSIZE + 2); - if (new_skb == NULL) { - lp->stats.rx_dropped++; + u16 status = sonic_rda_get(dev, entry, SONIC_RD_STATUS); + + /* If the RD has LPKT set, the chip has finished with the RB */ + if ((status & SONIC_RCR_PRX) && (status & SONIC_RCR_LPKT)) { + struct sk_buff *new_skb; + dma_addr_t new_laddr; + u32 addr = (sonic_rda_get(dev, entry, + SONIC_RD_PKTPTR_H) << 16) | + sonic_rda_get(dev, entry, SONIC_RD_PKTPTR_L); + int i = index_from_addr(lp, addr, entry); + + if (i < 0) { + WARN_ONCE(1, "failed to find buffer!\n"); break; } - /* provide 16 byte IP header alignment unless DMA requires otherwise */ - if(SONIC_BUS_SCALE(lp->dma_bitmode) == 2) - skb_reserve(new_skb, 2); - - new_laddr = dma_map_single(lp->device, skb_put(new_skb, SONIC_RBSIZE), - SONIC_RBSIZE, DMA_FROM_DEVICE); - if (!new_laddr) { - dev_kfree_skb(new_skb); - printk(KERN_ERR "%s: Failed to map rx buffer, dropping packet.\n", dev->name); + + if (sonic_alloc_rb(dev, lp, &new_skb, &new_laddr)) { + struct sk_buff *used_skb = lp->rx_skb[i]; + int pkt_len; + + /* Pass the used buffer up the stack */ + dma_unmap_single(lp->device, addr, SONIC_RBSIZE, + DMA_FROM_DEVICE); + + pkt_len = sonic_rda_get(dev, entry, + SONIC_RD_PKTLEN); + skb_trim(used_skb, pkt_len); + used_skb->protocol = eth_type_trans(used_skb, + dev); + netif_rx(used_skb); + lp->stats.rx_packets++; + lp->stats.rx_bytes += pkt_len; + + lp->rx_skb[i] = new_skb; + lp->rx_laddr[i] = new_laddr; + } else { + /* Failed to obtain a new buffer so re-use it */ + new_laddr = addr; lp->stats.rx_dropped++; - break; } - - /* now we have a new skb to replace it, pass the used one up the stack */ - dma_unmap_single(lp->device, lp->rx_laddr[entry], SONIC_RBSIZE, DMA_FROM_DEVICE); - used_skb = lp->rx_skb[entry]; - pkt_len = sonic_rda_get(dev, entry, SONIC_RD_PKTLEN); - skb_trim(used_skb, pkt_len); - used_skb->protocol = eth_type_trans(used_skb, dev); - netif_rx(used_skb); - lp->stats.rx_packets++; - lp->stats.rx_bytes += pkt_len; - - /* and insert the new skb */ - lp->rx_laddr[entry] = new_laddr; - lp->rx_skb[entry] = new_skb; - - bufadr_l = (unsigned long)new_laddr & 0xffff; - bufadr_h = (unsigned long)new_laddr >> 16; - sonic_rra_put(dev, entry, SONIC_RR_BUFADR_L, bufadr_l); - sonic_rra_put(dev, entry, SONIC_RR_BUFADR_H, bufadr_h); - } else { - /* This should only happen, if we enable accepting broken packets. */ - lp->stats.rx_errors++; - if (status & SONIC_RCR_FAER) - lp->stats.rx_frame_errors++; - if (status & SONIC_RCR_CRCR) - lp->stats.rx_crc_errors++; - } - if (status & SONIC_RCR_LPKT) { - /* - * this was the last packet out of the current receive buffer - * give the buffer back to the SONIC + /* If RBE is already asserted when RWP advances then + * it's safe to clear RBE after processing this packet. */ - lp->cur_rwp += SIZEOF_SONIC_RR * SONIC_BUS_SCALE(lp->dma_bitmode); - if (lp->cur_rwp >= lp->rra_end) lp->cur_rwp = lp->rra_laddr & 0xffff; - SONIC_WRITE(SONIC_RWP, lp->cur_rwp); - if (SONIC_READ(SONIC_ISR) & SONIC_INT_RBE) { - netif_dbg(lp, rx_err, dev, "%s: rx buffer exhausted\n", - __func__); - SONIC_WRITE(SONIC_ISR, SONIC_INT_RBE); /* clear the flag */ - } - } else - printk(KERN_ERR "%s: rx desc without RCR_LPKT. Shouldn't happen !?\n", - dev->name); + rbe = rbe || SONIC_READ(SONIC_ISR) & SONIC_INT_RBE; + sonic_update_rra(dev, lp, addr, new_laddr); + } /* * give back the descriptor */ - sonic_rda_put(dev, entry, SONIC_RD_LINK, - sonic_rda_get(dev, entry, SONIC_RD_LINK) | SONIC_EOL); + sonic_rda_put(dev, entry, SONIC_RD_STATUS, 0); sonic_rda_put(dev, entry, SONIC_RD_IN_USE, 1); - sonic_rda_put(dev, lp->eol_rx, SONIC_RD_LINK, - sonic_rda_get(dev, lp->eol_rx, SONIC_RD_LINK) & ~SONIC_EOL); - lp->eol_rx = entry; - lp->cur_rx = entry = (entry + 1) & SONIC_RDS_MASK; + + prev_entry = entry; + entry = (entry + 1) & SONIC_RDS_MASK; + } + + lp->cur_rx = entry; + + if (prev_entry != lp->eol_rx) { + /* Advance the EOL flag to put descriptors back into service */ + sonic_rda_put(dev, prev_entry, SONIC_RD_LINK, SONIC_EOL | + sonic_rda_get(dev, prev_entry, SONIC_RD_LINK)); + sonic_rda_put(dev, lp->eol_rx, SONIC_RD_LINK, ~SONIC_EOL & + sonic_rda_get(dev, lp->eol_rx, SONIC_RD_LINK)); + lp->eol_rx = prev_entry; } + + if (rbe) + SONIC_WRITE(SONIC_ISR, SONIC_INT_RBE); /* * If any worth-while packets have been received, netif_rx() * has done a mark_bh(NET_BH) for us and will work on them @@ -549,6 +642,8 @@ static void sonic_multicast_list(struct net_device *dev) (netdev_mc_count(dev) > 15)) { rcr |= SONIC_RCR_AMC; } else { + unsigned long flags; + netif_dbg(lp, ifup, dev, "%s: mc_count %d\n", __func__, netdev_mc_count(dev)); sonic_set_cam_enable(dev, 1); /* always enable our own address */ @@ -562,9 +657,14 @@ static void sonic_multicast_list(struct net_device *dev) i++; } SONIC_WRITE(SONIC_CDC, 16); - /* issue Load CAM command */ SONIC_WRITE(SONIC_CDP, lp->cda_laddr & 0xffff); + + /* LCAM and TXP commands can't be used simultaneously */ + spin_lock_irqsave(&lp->lock, flags); + sonic_quiesce(dev, SONIC_CR_TXP); SONIC_WRITE(SONIC_CMD, SONIC_CR_LCAM); + sonic_quiesce(dev, SONIC_CR_LCAM); + spin_unlock_irqrestore(&lp->lock, flags); } } @@ -579,7 +679,6 @@ static void sonic_multicast_list(struct net_device *dev) */ static int sonic_init(struct net_device *dev) { - unsigned int cmd; struct sonic_local *lp = netdev_priv(dev); int i; @@ -591,12 +690,16 @@ static int sonic_init(struct net_device *dev) SONIC_WRITE(SONIC_ISR, 0x7fff); SONIC_WRITE(SONIC_CMD, SONIC_CR_RST); + /* While in reset mode, clear CAM Enable register */ + SONIC_WRITE(SONIC_CE, 0); + /* * clear software reset flag, disable receiver, clear and * enable interrupts, then completely initialize the SONIC */ SONIC_WRITE(SONIC_CMD, 0); - SONIC_WRITE(SONIC_CMD, SONIC_CR_RXDIS); + SONIC_WRITE(SONIC_CMD, SONIC_CR_RXDIS | SONIC_CR_STP); + sonic_quiesce(dev, SONIC_CR_ALL); /* * initialize the receive resource area @@ -614,15 +717,10 @@ static int sonic_init(struct net_device *dev) } /* initialize all RRA registers */ - lp->rra_end = (lp->rra_laddr + SONIC_NUM_RRS * SIZEOF_SONIC_RR * - SONIC_BUS_SCALE(lp->dma_bitmode)) & 0xffff; - lp->cur_rwp = (lp->rra_laddr + (SONIC_NUM_RRS - 1) * SIZEOF_SONIC_RR * - SONIC_BUS_SCALE(lp->dma_bitmode)) & 0xffff; - - SONIC_WRITE(SONIC_RSA, lp->rra_laddr & 0xffff); - SONIC_WRITE(SONIC_REA, lp->rra_end); - SONIC_WRITE(SONIC_RRP, lp->rra_laddr & 0xffff); - SONIC_WRITE(SONIC_RWP, lp->cur_rwp); + SONIC_WRITE(SONIC_RSA, sonic_rr_addr(dev, 0)); + SONIC_WRITE(SONIC_REA, sonic_rr_addr(dev, SONIC_NUM_RRS)); + SONIC_WRITE(SONIC_RRP, sonic_rr_addr(dev, 0)); + SONIC_WRITE(SONIC_RWP, sonic_rr_addr(dev, SONIC_NUM_RRS - 1)); SONIC_WRITE(SONIC_URRA, lp->rra_laddr >> 16); SONIC_WRITE(SONIC_EOBC, (SONIC_RBSIZE >> 1) - (lp->dma_bitmode ? 2 : 1)); @@ -630,14 +728,7 @@ static int sonic_init(struct net_device *dev) netif_dbg(lp, ifup, dev, "%s: issuing RRRA command\n", __func__); SONIC_WRITE(SONIC_CMD, SONIC_CR_RRRA); - i = 0; - while (i++ < 100) { - if (SONIC_READ(SONIC_CMD) & SONIC_CR_RRRA) - break; - } - - netif_dbg(lp, ifup, dev, "%s: status=%x, i=%d\n", __func__, - SONIC_READ(SONIC_CMD), i); + sonic_quiesce(dev, SONIC_CR_RRRA); /* * Initialize the receive descriptors so that they @@ -712,28 +803,17 @@ static int sonic_init(struct net_device *dev) * load the CAM */ SONIC_WRITE(SONIC_CMD, SONIC_CR_LCAM); - - i = 0; - while (i++ < 100) { - if (SONIC_READ(SONIC_ISR) & SONIC_INT_LCD) - break; - } - netif_dbg(lp, ifup, dev, "%s: CMD=%x, ISR=%x, i=%d\n", __func__, - SONIC_READ(SONIC_CMD), SONIC_READ(SONIC_ISR), i); + sonic_quiesce(dev, SONIC_CR_LCAM); /* * enable receiver, disable loopback * and enable all interrupts */ - SONIC_WRITE(SONIC_CMD, SONIC_CR_RXEN | SONIC_CR_STP); SONIC_WRITE(SONIC_RCR, SONIC_RCR_DEFAULT); SONIC_WRITE(SONIC_TCR, SONIC_TCR_DEFAULT); SONIC_WRITE(SONIC_ISR, 0x7fff); SONIC_WRITE(SONIC_IMR, SONIC_IMR_DEFAULT); - - cmd = SONIC_READ(SONIC_CMD); - if ((cmd & SONIC_CR_RXEN) == 0 || (cmd & SONIC_CR_STP) == 0) - printk(KERN_ERR "sonic_init: failed, status=%x\n", cmd); + SONIC_WRITE(SONIC_CMD, SONIC_CR_RXEN); netif_dbg(lp, ifup, dev, "%s: new status=%x\n", __func__, SONIC_READ(SONIC_CMD)); diff --git a/drivers/net/ethernet/natsemi/sonic.h b/drivers/net/ethernet/natsemi/sonic.h index 2b27f7049acb8a5a5d011c29bfe4c5ba8484f46b..1df6d2f06cc42ccd01a8c61c338863828f1fa3eb 100644 --- a/drivers/net/ethernet/natsemi/sonic.h +++ b/drivers/net/ethernet/natsemi/sonic.h @@ -110,6 +110,9 @@ #define SONIC_CR_TXP 0x0002 #define SONIC_CR_HTX 0x0001 +#define SONIC_CR_ALL (SONIC_CR_LCAM | SONIC_CR_RRRA | \ + SONIC_CR_RXEN | SONIC_CR_TXP) + /* * SONIC data configuration bits */ @@ -175,6 +178,7 @@ #define SONIC_TCR_NCRS 0x0100 #define SONIC_TCR_CRLS 0x0080 #define SONIC_TCR_EXC 0x0040 +#define SONIC_TCR_OWC 0x0020 #define SONIC_TCR_PMB 0x0008 #define SONIC_TCR_FU 0x0004 #define SONIC_TCR_BCM 0x0002 @@ -274,8 +278,9 @@ #define SONIC_NUM_RDS SONIC_NUM_RRS /* number of receive descriptors */ #define SONIC_NUM_TDS 16 /* number of transmit descriptors */ -#define SONIC_RDS_MASK (SONIC_NUM_RDS-1) -#define SONIC_TDS_MASK (SONIC_NUM_TDS-1) +#define SONIC_RRS_MASK (SONIC_NUM_RRS - 1) +#define SONIC_RDS_MASK (SONIC_NUM_RDS - 1) +#define SONIC_TDS_MASK (SONIC_NUM_TDS - 1) #define SONIC_RBSIZE 1520 /* size of one resource buffer */ @@ -312,8 +317,6 @@ struct sonic_local { u32 rda_laddr; /* logical DMA address of RDA */ dma_addr_t rx_laddr[SONIC_NUM_RRS]; /* logical DMA addresses of rx skbuffs */ dma_addr_t tx_laddr[SONIC_NUM_TDS]; /* logical DMA addresses of tx skbuffs */ - unsigned int rra_end; - unsigned int cur_rwp; unsigned int cur_rx; unsigned int cur_tx; /* first unacked transmit packet */ unsigned int eol_rx; @@ -322,6 +325,7 @@ struct sonic_local { int msg_enable; struct device *device; /* generic device */ struct net_device_stats stats; + spinlock_t lock; }; #define TX_TIMEOUT (3 * HZ) @@ -344,30 +348,30 @@ static void sonic_msg_init(struct net_device *dev); as far as we can tell. */ /* OpenBSD calls this "SWO". I'd like to think that sonic_buf_put() is a much better name. */ -static inline void sonic_buf_put(void* base, int bitmode, +static inline void sonic_buf_put(u16 *base, int bitmode, int offset, __u16 val) { if (bitmode) #ifdef __BIG_ENDIAN - ((__u16 *) base + (offset*2))[1] = val; + __raw_writew(val, base + (offset * 2) + 1); #else - ((__u16 *) base + (offset*2))[0] = val; + __raw_writew(val, base + (offset * 2) + 0); #endif else - ((__u16 *) base)[offset] = val; + __raw_writew(val, base + (offset * 1) + 0); } -static inline __u16 sonic_buf_get(void* base, int bitmode, +static inline __u16 sonic_buf_get(u16 *base, int bitmode, int offset) { if (bitmode) #ifdef __BIG_ENDIAN - return ((volatile __u16 *) base + (offset*2))[1]; + return __raw_readw(base + (offset * 2) + 1); #else - return ((volatile __u16 *) base + (offset*2))[0]; + return __raw_readw(base + (offset * 2) + 0); #endif else - return ((volatile __u16 *) base)[offset]; + return __raw_readw(base + (offset * 1) + 0); } /* Inlines that you should actually use for reading/writing DMA buffers */ @@ -447,6 +451,22 @@ static inline __u16 sonic_rra_get(struct net_device* dev, int entry, (entry * SIZEOF_SONIC_RR) + offset); } +static inline u16 sonic_rr_addr(struct net_device *dev, int entry) +{ + struct sonic_local *lp = netdev_priv(dev); + + return lp->rra_laddr + + entry * SIZEOF_SONIC_RR * SONIC_BUS_SCALE(lp->dma_bitmode); +} + +static inline u16 sonic_rr_entry(struct net_device *dev, u16 addr) +{ + struct sonic_local *lp = netdev_priv(dev); + + return (addr - (u16)lp->rra_laddr) / (SIZEOF_SONIC_RR * + SONIC_BUS_SCALE(lp->dma_bitmode)); +} + static const char version[] = "sonic.c:v0.92 20.9.98 tsbogend@alpha.franken.de\n"; diff --git a/drivers/net/ethernet/netronome/nfp/bpf/jit.c b/drivers/net/ethernet/netronome/nfp/bpf/jit.c index 4e18d95e548f1680f27bac98b8f1fa206607e546..c3ce0fb47a0f06d0f417ee833eb335c75946907b 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/jit.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/jit.c @@ -326,7 +326,18 @@ __emit_shf(struct nfp_prog *nfp_prog, u16 dst, enum alu_dst_ab dst_ab, return; } - if (sc == SHF_SC_L_SHF) + /* NFP shift instruction has something special. If shift direction is + * left then shift amount of 1 to 31 is specified as 32 minus the amount + * to shift. + * + * But no need to do this for indirect shift which has shift amount be + * 0. Even after we do this subtraction, shift amount 0 will be turned + * into 32 which will eventually be encoded the same as 0 because only + * low 5 bits are encoded, but shift amount be 32 will fail the + * FIELD_PREP check done later on shift mask (0x1f), due to 32 is out of + * mask range. + */ + if (sc == SHF_SC_L_SHF && shift) shift = 32 - shift; insn = OP_SHF_BASE | diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h index 44d3ea75d043a2867fa6ae717535fae80c6a4dd6..ab602a79b084d188baedf30ff3b79e9c0a81ee87 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h @@ -423,7 +423,7 @@ #define NFP_NET_CFG_MBOX_SIMPLE_CMD 0x0 #define NFP_NET_CFG_MBOX_SIMPLE_RET 0x4 #define NFP_NET_CFG_MBOX_SIMPLE_VAL 0x8 -#define NFP_NET_CFG_MBOX_SIMPLE_LEN 0x12 +#define NFP_NET_CFG_MBOX_SIMPLE_LEN 12 #define NFP_NET_CFG_MBOX_CMD_CTAG_FILTER_ADD 1 #define NFP_NET_CFG_MBOX_CMD_CTAG_FILTER_KILL 2 diff --git a/drivers/net/ethernet/ni/nixge.c b/drivers/net/ethernet/ni/nixge.c index 76efed058f334b75f356641f77bcb55bb107e422..a791d7932b0eff8f224482a949bf2458fca86bde 100644 --- a/drivers/net/ethernet/ni/nixge.c +++ b/drivers/net/ethernet/ni/nixge.c @@ -1233,7 +1233,7 @@ static int nixge_probe(struct platform_device *pdev) } priv->phy_mode = of_get_phy_mode(pdev->dev.of_node); - if (priv->phy_mode < 0) { + if ((int)priv->phy_mode < 0) { netdev_err(ndev, "not find \"phy-mode\" property\n"); err = -EINVAL; goto unregister_mdio; diff --git a/drivers/net/ethernet/pasemi/pasemi_mac.c b/drivers/net/ethernet/pasemi/pasemi_mac.c index 8a31a02c9f47f7ec9c328cbbacb8088b07c90ab8..65f69e562618a4bf7b619f7374b764ec614ac804 100644 --- a/drivers/net/ethernet/pasemi/pasemi_mac.c +++ b/drivers/net/ethernet/pasemi/pasemi_mac.c @@ -1053,7 +1053,6 @@ static int pasemi_mac_phy_init(struct net_device *dev) dn = pci_device_to_OF_node(mac->pdev); phy_dn = of_parse_phandle(dn, "phy-handle", 0); - of_node_put(phy_dn); mac->link = 0; mac->speed = 0; @@ -1062,6 +1061,7 @@ static int pasemi_mac_phy_init(struct net_device *dev) phydev = of_phy_connect(dev, phy_dn, &pasemi_adjust_link, 0, PHY_INTERFACE_MODE_SGMII); + of_node_put(phy_dn); if (!phydev) { printk(KERN_ERR "%s: Could not attach to phy\n", dev->name); return -ENODEV; diff --git a/drivers/net/ethernet/qlogic/qed/qed_iwarp.c b/drivers/net/ethernet/qlogic/qed/qed_iwarp.c index 7002a660b6b4c278d130388e4ac59fb7e7cddb93..39787bb885c86a390c00d46add23c5a5efdd97a0 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_iwarp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_iwarp.c @@ -532,7 +532,8 @@ int qed_iwarp_destroy_qp(struct qed_hwfn *p_hwfn, struct qed_rdma_qp *qp) /* Make sure ep is closed before returning and freeing memory. */ if (ep) { - while (ep->state != QED_IWARP_EP_CLOSED && wait_count++ < 200) + while (READ_ONCE(ep->state) != QED_IWARP_EP_CLOSED && + wait_count++ < 200) msleep(100); if (ep->state != QED_IWARP_EP_CLOSED) @@ -1023,8 +1024,6 @@ qed_iwarp_mpa_complete(struct qed_hwfn *p_hwfn, params.ep_context = ep; - ep->state = QED_IWARP_EP_CLOSED; - switch (fw_return_code) { case RDMA_RETURN_OK: ep->qp->max_rd_atomic_req = ep->cm_info.ord; @@ -1084,6 +1083,10 @@ qed_iwarp_mpa_complete(struct qed_hwfn *p_hwfn, break; } + if (fw_return_code != RDMA_RETURN_OK) + /* paired with READ_ONCE in destroy_qp */ + smp_store_release(&ep->state, QED_IWARP_EP_CLOSED); + ep->event_cb(ep->cb_context, ¶ms); /* on passive side, if there is no associated QP (REJECT) we need to @@ -2638,6 +2641,7 @@ qed_iwarp_ll2_start(struct qed_hwfn *p_hwfn, cbs.rx_release_cb = qed_iwarp_ll2_rel_rx_pkt; cbs.tx_comp_cb = qed_iwarp_ll2_comp_tx_pkt; cbs.tx_release_cb = qed_iwarp_ll2_rel_tx_pkt; + cbs.slowpath_cb = NULL; cbs.cookie = p_hwfn; memset(&data, 0, sizeof(data)); @@ -2828,7 +2832,9 @@ static void qed_iwarp_qp_in_error(struct qed_hwfn *p_hwfn, params.status = (fw_return_code == IWARP_QP_IN_ERROR_GOOD_CLOSE) ? 0 : -ECONNRESET; - ep->state = QED_IWARP_EP_CLOSED; + /* paired with READ_ONCE in destroy_qp */ + smp_store_release(&ep->state, QED_IWARP_EP_CLOSED); + spin_lock_bh(&p_hwfn->p_rdma_info->iwarp.iw_lock); list_del(&ep->list_entry); spin_unlock_bh(&p_hwfn->p_rdma_info->iwarp.iw_lock); @@ -2917,7 +2923,8 @@ qed_iwarp_tcp_connect_unsuccessful(struct qed_hwfn *p_hwfn, params.event = QED_IWARP_EVENT_ACTIVE_COMPLETE; params.ep_context = ep; params.cm_info = &ep->cm_info; - ep->state = QED_IWARP_EP_CLOSED; + /* paired with READ_ONCE in destroy_qp */ + smp_store_release(&ep->state, QED_IWARP_EP_CLOSED); switch (fw_return_code) { case IWARP_CONN_ERROR_TCP_CONNECT_INVALID_PACKET: diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.c b/drivers/net/ethernet/qlogic/qed/qed_l2.c index 64ac95ca4df21e605c65046c85c86cd13c88cea6..d921b991dbdb5fb3bc871c6401c620c05bbe9233 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_l2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_l2.c @@ -1631,10 +1631,9 @@ static void __qed_get_vport_pstats_addrlen(struct qed_hwfn *p_hwfn, } } -static void __qed_get_vport_pstats(struct qed_hwfn *p_hwfn, - struct qed_ptt *p_ptt, - struct qed_eth_stats *p_stats, - u16 statistics_bin) +static noinline_for_stack void +__qed_get_vport_pstats(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, + struct qed_eth_stats *p_stats, u16 statistics_bin) { struct eth_pstorm_per_queue_stat pstats; u32 pstats_addr = 0, pstats_len = 0; @@ -1661,10 +1660,9 @@ static void __qed_get_vport_pstats(struct qed_hwfn *p_hwfn, HILO_64_REGPAIR(pstats.error_drop_pkts); } -static void __qed_get_vport_tstats(struct qed_hwfn *p_hwfn, - struct qed_ptt *p_ptt, - struct qed_eth_stats *p_stats, - u16 statistics_bin) +static noinline_for_stack void +__qed_get_vport_tstats(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, + struct qed_eth_stats *p_stats, u16 statistics_bin) { struct tstorm_per_port_stat tstats; u32 tstats_addr, tstats_len; @@ -1709,10 +1707,9 @@ static void __qed_get_vport_ustats_addrlen(struct qed_hwfn *p_hwfn, } } -static void __qed_get_vport_ustats(struct qed_hwfn *p_hwfn, - struct qed_ptt *p_ptt, - struct qed_eth_stats *p_stats, - u16 statistics_bin) +static noinline_for_stack +void __qed_get_vport_ustats(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, + struct qed_eth_stats *p_stats, u16 statistics_bin) { struct eth_ustorm_per_queue_stat ustats; u32 ustats_addr = 0, ustats_len = 0; @@ -1751,10 +1748,9 @@ static void __qed_get_vport_mstats_addrlen(struct qed_hwfn *p_hwfn, } } -static void __qed_get_vport_mstats(struct qed_hwfn *p_hwfn, - struct qed_ptt *p_ptt, - struct qed_eth_stats *p_stats, - u16 statistics_bin) +static noinline_for_stack void +__qed_get_vport_mstats(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, + struct qed_eth_stats *p_stats, u16 statistics_bin) { struct eth_mstorm_per_queue_stat mstats; u32 mstats_addr = 0, mstats_len = 0; @@ -1780,9 +1776,9 @@ static void __qed_get_vport_mstats(struct qed_hwfn *p_hwfn, HILO_64_REGPAIR(mstats.tpa_coalesced_bytes); } -static void __qed_get_vport_port_stats(struct qed_hwfn *p_hwfn, - struct qed_ptt *p_ptt, - struct qed_eth_stats *p_stats) +static noinline_for_stack void +__qed_get_vport_port_stats(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, + struct qed_eth_stats *p_stats) { struct qed_eth_stats_common *p_common = &p_stats->common; struct port_stats port_stats; diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h index d242a5724069129fa0a87f8ce6f1c9efcf4c27d7..dc3be8a4acf4bf44b71fd0119b3009dae2cd9e5c 100644 --- a/drivers/net/ethernet/qlogic/qede/qede.h +++ b/drivers/net/ethernet/qlogic/qede/qede.h @@ -162,6 +162,8 @@ struct qede_rdma_dev { struct list_head entry; struct list_head rdma_event_list; struct workqueue_struct *rdma_wq; + struct kref refcnt; + struct completion event_comp; }; struct qede_ptp; diff --git a/drivers/net/ethernet/qlogic/qede/qede_rdma.c b/drivers/net/ethernet/qlogic/qede/qede_rdma.c index 1900bf7e67d1297dc9b24648e99d0ec50f779305..cd12fb919ad574ba5f46207d0315518f652631f7 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_rdma.c +++ b/drivers/net/ethernet/qlogic/qede/qede_rdma.c @@ -57,6 +57,9 @@ static void _qede_rdma_dev_add(struct qede_dev *edev) static int qede_rdma_create_wq(struct qede_dev *edev) { INIT_LIST_HEAD(&edev->rdma_info.rdma_event_list); + kref_init(&edev->rdma_info.refcnt); + init_completion(&edev->rdma_info.event_comp); + edev->rdma_info.rdma_wq = create_singlethread_workqueue("rdma_wq"); if (!edev->rdma_info.rdma_wq) { DP_NOTICE(edev, "qedr: Could not create workqueue\n"); @@ -81,8 +84,23 @@ static void qede_rdma_cleanup_event(struct qede_dev *edev) } } +static void qede_rdma_complete_event(struct kref *ref) +{ + struct qede_rdma_dev *rdma_dev = + container_of(ref, struct qede_rdma_dev, refcnt); + + /* no more events will be added after this */ + complete(&rdma_dev->event_comp); +} + static void qede_rdma_destroy_wq(struct qede_dev *edev) { + /* Avoid race with add_event flow, make sure it finishes before + * we start accessing the list and cleaning up the work + */ + kref_put(&edev->rdma_info.refcnt, qede_rdma_complete_event); + wait_for_completion(&edev->rdma_info.event_comp); + qede_rdma_cleanup_event(edev); destroy_workqueue(edev->rdma_info.rdma_wq); } @@ -287,15 +305,24 @@ static void qede_rdma_add_event(struct qede_dev *edev, if (!edev->rdma_info.qedr_dev) return; + /* We don't want the cleanup flow to start while we're allocating and + * scheduling the work + */ + if (!kref_get_unless_zero(&edev->rdma_info.refcnt)) + return; /* already being destroyed */ + event_node = qede_rdma_get_free_event_node(edev); if (!event_node) - return; + goto out; event_node->event = event; event_node->ptr = edev; INIT_WORK(&event_node->work, qede_rdma_handle_event); queue_work(edev->rdma_info.rdma_wq, &event_node->work); + +out: + kref_put(&edev->rdma_info.refcnt, qede_rdma_complete_event); } void qede_rdma_dev_event_open(struct qede_dev *edev) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c index a496390b8632fa3f9dfa9f5ca18bf64d0b9e7f31..07f9067affc65ea4d73543c18016174217c1971a 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c @@ -2043,6 +2043,7 @@ static void qlcnic_83xx_exec_template_cmd(struct qlcnic_adapter *p_dev, break; } entry += p_hdr->size; + cond_resched(); } p_dev->ahw->reset.seq_index = index; } diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c index afa10a163da1fc1a43ea5dde6f8c6da297aa0f48..f34ae8c75bc5e11f4e2a4ee02b20e87983c656f7 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c @@ -703,6 +703,7 @@ static u32 qlcnic_read_memory_test_agent(struct qlcnic_adapter *adapter, addr += 16; reg_read -= 16; ret += 16; + cond_resched(); } out: mutex_unlock(&adapter->ahw->mem_lock); @@ -1383,6 +1384,7 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter) buf_offset += entry->hdr.cap_size; entry_offset += entry->hdr.offset; buffer = fw_dump->data + buf_offset; + cond_resched(); } fw_dump->clr = 1; diff --git a/drivers/net/ethernet/qualcomm/qca_spi.c b/drivers/net/ethernet/qualcomm/qca_spi.c index 66b775d462fd8ed111dbb18e845ad4d9af1b6d2b..9d188931bc09e5196d5c7182250fc8c2b559e16c 100644 --- a/drivers/net/ethernet/qualcomm/qca_spi.c +++ b/drivers/net/ethernet/qualcomm/qca_spi.c @@ -475,7 +475,6 @@ qcaspi_qca7k_sync(struct qcaspi *qca, int event) u16 signature = 0; u16 spi_config; u16 wrbuf_space = 0; - static u16 reset_count; if (event == QCASPI_EVENT_CPUON) { /* Read signature twice, if not valid @@ -528,13 +527,13 @@ qcaspi_qca7k_sync(struct qcaspi *qca, int event) qca->sync = QCASPI_SYNC_RESET; qca->stats.trig_reset++; - reset_count = 0; + qca->reset_count = 0; break; case QCASPI_SYNC_RESET: - reset_count++; + qca->reset_count++; netdev_dbg(qca->net_dev, "sync: waiting for CPU on, count %u.\n", - reset_count); - if (reset_count >= QCASPI_RESET_TIMEOUT) { + qca->reset_count); + if (qca->reset_count >= QCASPI_RESET_TIMEOUT) { /* reset did not seem to take place, try again */ qca->sync = QCASPI_SYNC_UNKNOWN; qca->stats.reset_timeout++; diff --git a/drivers/net/ethernet/qualcomm/qca_spi.h b/drivers/net/ethernet/qualcomm/qca_spi.h index fc0e98726b3613ddd3774169fa13aa6099a5c6e5..719c41227f221b89c4c62f0e98be0d6e5a6faca3 100644 --- a/drivers/net/ethernet/qualcomm/qca_spi.h +++ b/drivers/net/ethernet/qualcomm/qca_spi.h @@ -92,6 +92,7 @@ struct qcaspi { unsigned int intr_req; unsigned int intr_svc; + u16 reset_count; #ifdef CONFIG_DEBUG_FS struct dentry *device_root; diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_genl.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_genl.c index df92f4b3f49be148a37810794c56bd2ab38ec42c..560e4ece3975698d3fd2d71c50ecb9a77b209a7b 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_genl.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_genl.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2019, The Linux Foundation. All rights reserved. +/* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. * * RMNET Data Generic Netlink * @@ -187,11 +187,8 @@ static void rmnet_create_pid_bps_resp(struct rmnet_core_pid_bps_resp tx_bytes_cur = node_p->tx_bytes; if (tx_bytes_cur <= node_p->tx_bytes_last_query) { /* Dont send inactive pids to userspace */ - /* TODO: can remove from hash table probably */ - node_p->tx_bps = 0; - node_p->timstamp_last_query = - pid_bps_resp_ptr->timestamp; - node_p->sched_boost_remaining_ms = 0; + hash_del(&node_p->list); + kfree(node_p); continue; } diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index 4ab87fe845427672a346f3d5aa38b447b3d9a968..6ea43e48d5f97845c4b638caad9696e4840cb5af 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -7433,6 +7433,15 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) int chipset, region, i; int jumbo_max, rc; + /* Some tools for creating an initramfs don't consider softdeps, then + * r8169.ko may be in initramfs, but realtek.ko not. Then the generic + * PHY driver is used that doesn't work with most chip versions. + */ + if (!driver_find("RTL8201CP Ethernet", &mdio_bus_type)) { + dev_err(&pdev->dev, "realtek.ko not loaded, maybe it needs to be added to initramfs?\n"); + return -ENOENT; + } + dev = devm_alloc_etherdev(&pdev->dev, sizeof (*tp)); if (!dev) return -ENOMEM; diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 5e3e6e262ba3749a2771946c5e15fc681b757c5e..441643670ac0ecfab111bd132caeb70b57212c0b 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -2184,24 +2184,28 @@ static size_t __sh_eth_get_regs(struct net_device *ndev, u32 *buf) if (cd->tsu) { add_tsu_reg(ARSTR); add_tsu_reg(TSU_CTRST); - add_tsu_reg(TSU_FWEN0); - add_tsu_reg(TSU_FWEN1); - add_tsu_reg(TSU_FCM); - add_tsu_reg(TSU_BSYSL0); - add_tsu_reg(TSU_BSYSL1); - add_tsu_reg(TSU_PRISL0); - add_tsu_reg(TSU_PRISL1); - add_tsu_reg(TSU_FWSL0); - add_tsu_reg(TSU_FWSL1); + if (cd->dual_port) { + add_tsu_reg(TSU_FWEN0); + add_tsu_reg(TSU_FWEN1); + add_tsu_reg(TSU_FCM); + add_tsu_reg(TSU_BSYSL0); + add_tsu_reg(TSU_BSYSL1); + add_tsu_reg(TSU_PRISL0); + add_tsu_reg(TSU_PRISL1); + add_tsu_reg(TSU_FWSL0); + add_tsu_reg(TSU_FWSL1); + } add_tsu_reg(TSU_FWSLC); - add_tsu_reg(TSU_QTAGM0); - add_tsu_reg(TSU_QTAGM1); - add_tsu_reg(TSU_FWSR); - add_tsu_reg(TSU_FWINMK); - add_tsu_reg(TSU_ADQT0); - add_tsu_reg(TSU_ADQT1); - add_tsu_reg(TSU_VTAG0); - add_tsu_reg(TSU_VTAG1); + if (cd->dual_port) { + add_tsu_reg(TSU_QTAGM0); + add_tsu_reg(TSU_QTAGM1); + add_tsu_reg(TSU_FWSR); + add_tsu_reg(TSU_FWINMK); + add_tsu_reg(TSU_ADQT0); + add_tsu_reg(TSU_ADQT1); + add_tsu_reg(TSU_VTAG0); + add_tsu_reg(TSU_VTAG1); + } add_tsu_reg(TSU_ADSBSY); add_tsu_reg(TSU_TEN); add_tsu_reg(TSU_POST1); @@ -3129,12 +3133,16 @@ static struct sh_eth_plat_data *sh_eth_parse_dt(struct device *dev) struct device_node *np = dev->of_node; struct sh_eth_plat_data *pdata; const char *mac_addr; + int ret; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return NULL; - pdata->phy_interface = of_get_phy_mode(np); + ret = of_get_phy_mode(np); + if (ret < 0) + return NULL; + pdata->phy_interface = ret; mac_addr = of_get_mac_address(np); if (mac_addr) diff --git a/drivers/net/ethernet/smsc/smc911x.c b/drivers/net/ethernet/smsc/smc911x.c index 8355dfbb8ec3c71d01d6da5d6baad00829edf13a..f97b35430c840654bc684021352123ccabcb28df 100644 --- a/drivers/net/ethernet/smsc/smc911x.c +++ b/drivers/net/ethernet/smsc/smc911x.c @@ -947,7 +947,7 @@ static void smc911x_phy_configure(struct work_struct *work) if (lp->ctl_rspeed != 100) my_ad_caps &= ~(ADVERTISE_100BASE4|ADVERTISE_100FULL|ADVERTISE_100HALF); - if (!lp->ctl_rfduplx) + if (!lp->ctl_rfduplx) my_ad_caps &= ~(ADVERTISE_100FULL|ADVERTISE_10FULL); /* Update our Auto-Neg Advertisement Register */ diff --git a/drivers/net/ethernet/socionext/netsec.c b/drivers/net/ethernet/socionext/netsec.c index 28d582c18afb9f5b9846428747fc5568c672e5e0..027367b9cc4802506e74398cdffc40333315d645 100644 --- a/drivers/net/ethernet/socionext/netsec.c +++ b/drivers/net/ethernet/socionext/netsec.c @@ -432,9 +432,12 @@ static int netsec_mac_update_to_phy_state(struct netsec_priv *priv) return 0; } +static int netsec_phy_read(struct mii_bus *bus, int phy_addr, int reg_addr); + static int netsec_phy_write(struct mii_bus *bus, int phy_addr, int reg, u16 val) { + int status; struct netsec_priv *priv = bus->priv; if (netsec_mac_write(priv, GMAC_REG_GDR, val)) @@ -447,8 +450,19 @@ static int netsec_phy_write(struct mii_bus *bus, GMAC_REG_SHIFT_CR_GAR))) return -ETIMEDOUT; - return netsec_mac_wait_while_busy(priv, GMAC_REG_GAR, - NETSEC_GMAC_GAR_REG_GB); + status = netsec_mac_wait_while_busy(priv, GMAC_REG_GAR, + NETSEC_GMAC_GAR_REG_GB); + + /* Developerbox implements RTL8211E PHY and there is + * a compatibility problem with F_GMAC4. + * RTL8211E expects MDC clock must be kept toggling for several + * clock cycle with MDIO high before entering the IDLE state. + * To meet this requirement, netsec driver needs to issue dummy + * read(e.g. read PHYID1(offset 0x2) register) right after write. + */ + netsec_phy_read(bus, phy_addr, MII_PHYSID1); + + return status; } static int netsec_phy_read(struct mii_bus *bus, int phy_addr, int reg_addr) @@ -1590,7 +1604,7 @@ static int netsec_probe(struct platform_device *pdev) NETIF_MSG_LINK | NETIF_MSG_PROBE; priv->phy_interface = device_get_phy_mode(&pdev->dev); - if (priv->phy_interface < 0) { + if ((int)priv->phy_interface < 0) { dev_err(&pdev->dev, "missing required property 'phy-mode'\n"); ret = -ENODEV; goto free_ndev; diff --git a/drivers/net/ethernet/socionext/sni_ave.c b/drivers/net/ethernet/socionext/sni_ave.c index 09d25b87cf7c0ee09be03e785c76bc4a09bb7170..c309accc6797e0c3a43627e96e3109f59c1eaa14 100644 --- a/drivers/net/ethernet/socionext/sni_ave.c +++ b/drivers/net/ethernet/socionext/sni_ave.c @@ -1575,7 +1575,7 @@ static int ave_probe(struct platform_device *pdev) np = dev->of_node; phy_mode = of_get_phy_mode(np); - if (phy_mode < 0) { + if ((int)phy_mode < 0) { dev_err(dev, "phy-mode not found\n"); return -EINVAL; } diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index b069b3a2453be2330b8790445e88ae32a6e26399..c3c0c8ca082cab4901058f467413d2315f26d828 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -363,9 +363,8 @@ struct dma_features { unsigned int frpes; }; -/* GMAC TX FIFO is 8K, Rx FIFO is 16K */ -#define BUF_SIZE_16KiB 16384 -/* RX Buffer size must be < 8191 and multiple of 4/8/16 bytes */ +/* RX Buffer size must be multiple of 4/8/16 bytes */ +#define BUF_SIZE_16KiB 16368 #define BUF_SIZE_8KiB 8188 #define BUF_SIZE_4KiB 4096 #define BUF_SIZE_2KiB 2048 diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c index 2c6d7c69c8f741c22c9c55306c04bc920355e0d8..0d21082ceb93d1d0b0e2fb1e7edbb6653ab76385 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c @@ -191,7 +191,7 @@ static int ipq806x_gmac_of_parse(struct ipq806x_gmac *gmac) struct device *dev = &gmac->pdev->dev; gmac->phy_mode = of_get_phy_mode(dev->of_node); - if (gmac->phy_mode < 0) { + if ((int)gmac->phy_mode < 0) { dev_err(dev, "missing phy mode property\n"); return -EINVAL; } diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c index 94b46258e8ff82b5efe5afa7cbe1c672a762a6a1..0a17535f13ae4313686fd478eb3c0144f4321ab3 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c @@ -355,7 +355,7 @@ static int meson8b_dwmac_probe(struct platform_device *pdev) dwmac->dev = &pdev->dev; dwmac->phy_mode = of_get_phy_mode(pdev->dev.of_node); - if (dwmac->phy_mode < 0) { + if ((int)dwmac->phy_mode < 0) { dev_err(&pdev->dev, "missing phy-mode property\n"); ret = -EINVAL; goto err_remove_config_dt; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c index 48cf5e2b24417f282fd6be1c7991c9a3a5dccacb..bc8871e7351f221b0e4470cb73b81b2773e8c832 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c @@ -443,7 +443,7 @@ static void dwmac4_set_filter(struct mac_device_info *hw, } /* Handle multiple unicast addresses */ - if (netdev_uc_count(dev) > GMAC_MAX_PERFECT_ADDRESSES) { + if (netdev_uc_count(dev) > hw->unicast_filter_entries) { /* Switch to promiscuous mode if more than 128 addrs * are required */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac5.c b/drivers/net/ethernet/stmicro/stmmac/dwmac5.c index 3f4f3132e16b3b019869de9c6a219eb677e628a4..e436fa160c7d6a473f46f68c0facc1a31b0dc69a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac5.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac5.c @@ -515,6 +515,7 @@ int dwmac5_flex_pps_config(void __iomem *ioaddr, int index, if (!enable) { val |= PPSCMDx(index, 0x5); + val |= PPSEN0; writel(val, ioaddr + MAC_PPS_CONTROL); return 0; } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 7ee0e46539c01e61f78a81c3e90ec479d1b59039..9c7b1d8e82204f49e0d11dd33212dfc1c37002b9 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1082,7 +1082,9 @@ static int stmmac_set_bfsize(int mtu, int bufsize) { int ret = bufsize; - if (mtu >= BUF_SIZE_4KiB) + if (mtu >= BUF_SIZE_8KiB) + ret = BUF_SIZE_16KiB; + else if (mtu >= BUF_SIZE_4KiB) ret = BUF_SIZE_8KiB; else if (mtu >= BUF_SIZE_2KiB) ret = BUF_SIZE_4KiB; @@ -4511,6 +4513,7 @@ int stmmac_suspend(struct device *dev) { struct net_device *ndev = dev_get_drvdata(dev); struct stmmac_priv *priv = netdev_priv(ndev); + u32 chan; if (!ndev || !netif_running(ndev)) return 0; @@ -4525,6 +4528,9 @@ int stmmac_suspend(struct device *dev) stmmac_disable_all_queues(priv); + for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) + del_timer_sync(&priv->tx_queue[chan].txtimer); + /* Stop TX/RX DMA */ stmmac_stop_all_dma(priv); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c index cc60b3fb0892792d61cad4e46620a20913a30b54..8f8b8f381ffd4246b2794a03830edf59f742370e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c @@ -174,7 +174,7 @@ static int stmmac_enable(struct ptp_clock_info *ptp, /* structure describing a PTP hardware clock */ static struct ptp_clock_info stmmac_ptp_clock_ops = { .owner = THIS_MODULE, - .name = "stmmac_ptp_clock", + .name = "stmmac ptp", .max_adj = 62500000, .n_alarm = 0, .n_ext_ts = 0, diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index 28764268a44f87d61bc9d6b1f8976fd6fb3fbecc..b093f14eeec3905c663713341f1308feb6d4fbaa 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -1573,7 +1573,7 @@ static int axienet_probe(struct platform_device *pdev) } } else { lp->phy_mode = of_get_phy_mode(pdev->dev.of_node); - if (lp->phy_mode < 0) { + if ((int)lp->phy_mode < 0) { ret = -EINVAL; goto free_netdev; } diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 6571cac6e7865d66631a8f008e25f690d85efc83..eab9984f73a880ac3c11c37d0f654b7d1e1dd850 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -772,12 +772,12 @@ static int gtp_hashtable_new(struct gtp_dev *gtp, int hsize) int i; gtp->addr_hash = kmalloc_array(hsize, sizeof(struct hlist_head), - GFP_KERNEL); + GFP_KERNEL | __GFP_NOWARN); if (gtp->addr_hash == NULL) return -ENOMEM; gtp->tid_hash = kmalloc_array(hsize, sizeof(struct hlist_head), - GFP_KERNEL); + GFP_KERNEL | __GFP_NOWARN); if (gtp->tid_hash == NULL) goto err1; @@ -809,19 +809,21 @@ static struct sock *gtp_encap_enable_socket(int fd, int type, return NULL; } - if (sock->sk->sk_protocol != IPPROTO_UDP) { + sk = sock->sk; + if (sk->sk_protocol != IPPROTO_UDP || + sk->sk_type != SOCK_DGRAM || + (sk->sk_family != AF_INET && sk->sk_family != AF_INET6)) { pr_debug("socket fd=%d not UDP\n", fd); sk = ERR_PTR(-EINVAL); goto out_sock; } - lock_sock(sock->sk); - if (sock->sk->sk_user_data) { + lock_sock(sk); + if (sk->sk_user_data) { sk = ERR_PTR(-EBUSY); goto out_rel_sock; } - sk = sock->sk; sock_hold(sk); tuncfg.sk_user_data = gtp; diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 50709c76b6725aa55aa1b35b31040e6831764f7b..dfa801315da6b941726956874253c5e1220e1585 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -616,7 +616,8 @@ struct nvsp_5_send_indirect_table { /* The number of entries in the send indirection table */ u32 count; - /* The offset of the send indirection table from top of this struct. + /* The offset of the send indirection table from the beginning of + * struct nvsp_message. * The send indirection table tells which channel to put the send * traffic on. Each entry is a channel number. */ diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 35413041dcf8174252f7db5956a923a245faa031..77a9a753d9794ca6ee8c79079f0db6b3d7f5737d 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -110,7 +110,7 @@ static struct netvsc_device *alloc_net_device(void) init_waitqueue_head(&net_device->wait_drain); net_device->destroy = false; - net_device->tx_disable = false; + net_device->tx_disable = true; net_device->max_pkt = RNDIS_MAX_PKT_DEFAULT; net_device->pkt_align = RNDIS_PKT_ALIGN_DEFAULT; @@ -1182,20 +1182,39 @@ static int netvsc_receive(struct net_device *ndev, } static void netvsc_send_table(struct net_device *ndev, - const struct nvsp_message *nvmsg) + struct netvsc_device *nvscdev, + const struct nvsp_message *nvmsg, + u32 msglen) { struct net_device_context *net_device_ctx = netdev_priv(ndev); - u32 count, *tab; + u32 count, offset, *tab; int i; count = nvmsg->msg.v5_msg.send_table.count; + offset = nvmsg->msg.v5_msg.send_table.offset; + if (count != VRSS_SEND_TAB_SIZE) { netdev_err(ndev, "Received wrong send-table size:%u\n", count); return; } - tab = (u32 *)((unsigned long)&nvmsg->msg.v5_msg.send_table + - nvmsg->msg.v5_msg.send_table.offset); + /* If negotiated version <= NVSP_PROTOCOL_VERSION_6, the offset may be + * wrong due to a host bug. So fix the offset here. + */ + if (nvscdev->nvsp_version <= NVSP_PROTOCOL_VERSION_6 && + msglen >= sizeof(struct nvsp_message_header) + + sizeof(union nvsp_6_message_uber) + count * sizeof(u32)) + offset = sizeof(struct nvsp_message_header) + + sizeof(union nvsp_6_message_uber); + + /* Boundary check for all versions */ + if (offset > msglen - count * sizeof(u32)) { + netdev_err(ndev, "Received send-table offset too big:%u\n", + offset); + return; + } + + tab = (void *)nvmsg + offset; for (i = 0; i < count; i++) net_device_ctx->tx_table[i] = tab[i]; @@ -1213,12 +1232,14 @@ static void netvsc_send_vf(struct net_device *ndev, net_device_ctx->vf_alloc ? "added" : "removed"); } -static void netvsc_receive_inband(struct net_device *ndev, - const struct nvsp_message *nvmsg) +static void netvsc_receive_inband(struct net_device *ndev, + struct netvsc_device *nvscdev, + const struct nvsp_message *nvmsg, + u32 msglen) { switch (nvmsg->hdr.msg_type) { case NVSP_MSG5_TYPE_SEND_INDIRECTION_TABLE: - netvsc_send_table(ndev, nvmsg); + netvsc_send_table(ndev, nvscdev, nvmsg, msglen); break; case NVSP_MSG4_TYPE_SEND_VF_ASSOCIATION: @@ -1235,6 +1256,7 @@ static int netvsc_process_raw_pkt(struct hv_device *device, int budget) { const struct nvsp_message *nvmsg = hv_pkt_data(desc); + u32 msglen = hv_pkt_datalen(desc); trace_nvsp_recv(ndev, channel, nvmsg); @@ -1250,7 +1272,7 @@ static int netvsc_process_raw_pkt(struct hv_device *device, break; case VM_PKT_DATA_INBAND: - netvsc_receive_inband(ndev, nvmsg); + netvsc_receive_inband(ndev, net_device, nvmsg, msglen); break; default: diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 1f9f7fcdb0eb363ab9bbb57c0a00a1f235d38807..bdb55db4523b15e7989314d105f33640dde37e2c 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -295,9 +295,9 @@ static inline u32 netvsc_get_hash( else if (flow.basic.n_proto == htons(ETH_P_IPV6)) hash = jhash2((u32 *)&flow.addrs.v6addrs, 8, hashrnd); else - hash = 0; + return 0; - skb_set_hash(skb, hash, PKT_HASH_TYPE_L3); + __skb_set_sw_hash(skb, hash, false); } return hash; @@ -804,8 +804,7 @@ static struct sk_buff *netvsc_alloc_recv_skb(struct net_device *net, skb->protocol == htons(ETH_P_IP)) netvsc_comp_ipcsum(skb); - /* Do L4 checksum offload if enabled and present. - */ + /* Do L4 checksum offload if enabled and present. */ if (csum_info && (net->features & NETIF_F_RXCSUM)) { if (csum_info->receive.tcp_checksum_succeeded || csum_info->receive.udp_checksum_succeeded) @@ -985,6 +984,7 @@ static int netvsc_attach(struct net_device *ndev, } /* In any case device is now ready */ + nvdev->tx_disable = false; netif_device_attach(ndev); /* Note: enable and attach happen when sub-channels setup */ @@ -2004,6 +2004,12 @@ static rx_handler_result_t netvsc_vf_handle_frame(struct sk_buff **pskb) struct netvsc_vf_pcpu_stats *pcpu_stats = this_cpu_ptr(ndev_ctx->vf_stats); + skb = skb_share_check(skb, GFP_ATOMIC); + if (unlikely(!skb)) + return RX_HANDLER_CONSUMED; + + *pskb = skb; + skb->dev = ndev; u64_stats_update_begin(&pcpu_stats->syncp); @@ -2331,6 +2337,8 @@ static int netvsc_probe(struct hv_device *dev, else net->max_mtu = ETH_DATA_LEN; + nvdev->tx_disable = false; + ret = register_netdevice(net); if (ret != 0) { pr_err("Unable to register netdev.\n"); diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index f47e36ac42a7f8aa6b0a2a01302c884b5116fe63..dd91834f841d5f399ce2f6cea896bd12333b4fc7 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -1375,8 +1375,6 @@ void rndis_filter_device_remove(struct hv_device *dev, /* Halt and release the rndis device */ rndis_filter_halt_device(net_dev, rndis_dev); - net_dev->extension = NULL; - netvsc_device_remove(dev); } diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 41c0a3b55bfb4dc890c88b93118275e4f867dc59..277bbff53cff8e0aa7fae236a4f820201c8c620f 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -263,7 +263,7 @@ static void macvlan_broadcast(struct sk_buff *skb, struct net_device *src, enum macvlan_mode mode) { - const struct ethhdr *eth = skb_eth_hdr(skb); + const struct ethhdr *eth = eth_hdr(skb); const struct macvlan_dev *vlan; struct sk_buff *nskb; unsigned int i; @@ -517,10 +517,11 @@ static int macvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev) const struct macvlan_dev *dest; if (vlan->mode == MACVLAN_MODE_BRIDGE) { - const struct ethhdr *eth = (void *)skb->data; + const struct ethhdr *eth = skb_eth_hdr(skb); /* send to other bridge ports directly */ if (is_multicast_ether_addr(eth->h_dest)) { + skb_reset_mac_header(skb); macvlan_broadcast(skb, port, dev, MACVLAN_MODE_BRIDGE); goto xmit_world; } diff --git a/drivers/net/phy/fixed_phy.c b/drivers/net/phy/fixed_phy.c index 67b260877f305a33c8a8b4aa37d5862478fe7835..59820164502eba50479301ae2813505956a28c76 100644 --- a/drivers/net/phy/fixed_phy.c +++ b/drivers/net/phy/fixed_phy.c @@ -67,11 +67,11 @@ static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num) do { s = read_seqcount_begin(&fp->seqcount); /* Issue callback if user registered it. */ - if (fp->link_update) { + if (fp->link_update) fp->link_update(fp->phydev->attached_dev, &fp->status); - fixed_phy_update(fp); - } + /* Check the GPIO for change in status */ + fixed_phy_update(fp); state = fp->status; } while (read_seqcount_retry(&fp->seqcount, s)); diff --git a/drivers/net/phy/mdio-bcm-iproc.c b/drivers/net/phy/mdio-bcm-iproc.c index 46fe1ae919a30a9a9b7644b5862f4a5bfa0b56ef..51ce3ea17fb37f0533bb1dae3ad76e5797d24976 100644 --- a/drivers/net/phy/mdio-bcm-iproc.c +++ b/drivers/net/phy/mdio-bcm-iproc.c @@ -188,6 +188,23 @@ static int iproc_mdio_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +int iproc_mdio_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct iproc_mdio_priv *priv = platform_get_drvdata(pdev); + + /* restore the mii clock configuration */ + iproc_mdio_config_clk(priv->base); + + return 0; +} + +static const struct dev_pm_ops iproc_mdio_pm_ops = { + .resume = iproc_mdio_resume +}; +#endif /* CONFIG_PM_SLEEP */ + static const struct of_device_id iproc_mdio_of_match[] = { { .compatible = "brcm,iproc-mdio", }, { /* sentinel */ }, @@ -198,6 +215,9 @@ static struct platform_driver iproc_mdio_driver = { .driver = { .name = "iproc-mdio", .of_match_table = iproc_mdio_of_match, +#ifdef CONFIG_PM_SLEEP + .pm = &iproc_mdio_pm_ops, +#endif }, .probe = iproc_mdio_probe, .remove = iproc_mdio_remove, diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index c5588d4508f97877de45b7e7fb78f9cd3116acf4..5c89a310359de7f53fc575929811463a90934e84 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -56,11 +56,12 @@ static int mdiobus_register_gpiod(struct mdio_device *mdiodev) gpiod = fwnode_get_named_gpiod(&mdiodev->dev.of_node->fwnode, "reset-gpios", 0, GPIOD_OUT_LOW, "PHY reset"); - if (PTR_ERR(gpiod) == -ENOENT || - PTR_ERR(gpiod) == -ENOSYS) - gpiod = NULL; - else if (IS_ERR(gpiod)) - return PTR_ERR(gpiod); + if (IS_ERR(gpiod)) { + if (PTR_ERR(gpiod) == -ENOENT || PTR_ERR(gpiod) == -ENOSYS) + gpiod = NULL; + else + return PTR_ERR(gpiod); + } mdiodev->reset = gpiod; diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 05a6ae32ff65221c4c92acbb5ffafd613ea5df9c..b4c67c3a928b5515f4bb22dda357c42feb4507cd 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -977,6 +977,7 @@ static struct phy_driver ksphy_driver[] = { .driver_data = &ksz9021_type, .probe = kszphy_probe, .config_init = ksz9031_config_init, + .soft_reset = genphy_soft_reset, .read_status = ksz9031_read_status, .ack_interrupt = kszphy_ack_interrupt, .config_intr = kszphy_config_intr, diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 43c4f358eeb8a83fd745423d510f782b672af979..ae40d8137fd20a1fde6a6533c4efadc28cfb55e1 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -76,7 +76,7 @@ static LIST_HEAD(phy_fixup_list); static DEFINE_MUTEX(phy_fixup_lock); #ifdef CONFIG_PM -static bool mdio_bus_phy_may_suspend(struct phy_device *phydev) +static bool mdio_bus_phy_may_suspend(struct phy_device *phydev, bool suspend) { struct device_driver *drv = phydev->mdio.dev.driver; struct phy_driver *phydrv = to_phy_driver(drv); @@ -88,10 +88,11 @@ static bool mdio_bus_phy_may_suspend(struct phy_device *phydev) /* PHY not attached? May suspend if the PHY has not already been * suspended as part of a prior call to phy_disconnect() -> * phy_detach() -> phy_suspend() because the parent netdev might be the - * MDIO bus driver and clock gated at this point. + * MDIO bus driver and clock gated at this point. Also may resume if + * PHY is not attached. */ if (!netdev) - return !phydev->suspended; + return suspend ? !phydev->suspended : phydev->suspended; if (netdev->wol_enabled) return false; @@ -126,7 +127,7 @@ static int mdio_bus_phy_suspend(struct device *dev) if (phydev->attached_dev && phydev->adjust_link) phy_stop_machine(phydev); - if (!mdio_bus_phy_may_suspend(phydev)) + if (!mdio_bus_phy_may_suspend(phydev, true)) return 0; return phy_suspend(phydev); @@ -137,7 +138,7 @@ static int mdio_bus_phy_resume(struct device *dev) struct phy_device *phydev = to_phy_device(dev); int ret; - if (!mdio_bus_phy_may_suspend(phydev)) + if (!mdio_bus_phy_may_suspend(phydev, false)) goto no_resume; ret = phy_resume(phydev); @@ -1656,7 +1657,7 @@ int genphy_soft_reset(struct phy_device *phydev) { int ret; - ret = phy_write(phydev, MII_BMCR, BMCR_RESET); + ret = phy_set_bits(phydev, MII_BMCR, BMCR_RESET); if (ret < 0) return ret; diff --git a/drivers/net/ppp/ppp_async.c b/drivers/net/ppp/ppp_async.c index 7eae08870ccfb4d57fc84461b97ac9de809d4cfd..6fd84855603169f0bea22b1c6cf32cbf9b6325cb 100644 --- a/drivers/net/ppp/ppp_async.c +++ b/drivers/net/ppp/ppp_async.c @@ -878,15 +878,15 @@ ppp_async_input(struct asyncppp *ap, const unsigned char *buf, skb = dev_alloc_skb(ap->mru + PPP_HDRLEN + 2); if (!skb) goto nomem; - ap->rpkt = skb; - } - if (skb->len == 0) { - /* Try to get the payload 4-byte aligned. - * This should match the - * PPP_ALLSTATIONS/PPP_UI/compressed tests in - * process_input_packet, but we do not have - * enough chars here to test buf[1] and buf[2]. - */ + ap->rpkt = skb; + } + if (skb->len == 0) { + /* Try to get the payload 4-byte aligned. + * This should match the + * PPP_ALLSTATIONS/PPP_UI/compressed tests in + * process_input_packet, but we do not have + * enough chars here to test buf[1] and buf[2]. + */ if (buf[0] != PPP_ALLSTATIONS) skb_reserve(skb, 2 + (buf[0] & 1)); } diff --git a/drivers/net/slip/slip.c b/drivers/net/slip/slip.c index 77207f936871e510afceaee29926eb610450a16a..93f303ec17e21f362a96f87d5e75a3e3b65c6621 100644 --- a/drivers/net/slip/slip.c +++ b/drivers/net/slip/slip.c @@ -452,9 +452,16 @@ static void slip_transmit(struct work_struct *work) */ static void slip_write_wakeup(struct tty_struct *tty) { - struct slip *sl = tty->disc_data; + struct slip *sl; + + rcu_read_lock(); + sl = rcu_dereference(tty->disc_data); + if (!sl) + goto out; schedule_work(&sl->tx_work); +out: + rcu_read_unlock(); } static void sl_tx_timeout(struct net_device *dev) @@ -882,10 +889,11 @@ static void slip_close(struct tty_struct *tty) return; spin_lock_bh(&sl->lock); - tty->disc_data = NULL; + rcu_assign_pointer(tty->disc_data, NULL); sl->tty = NULL; spin_unlock_bh(&sl->lock); + synchronize_rcu(); flush_work(&sl->tx_work); /* VSV = very important to remove timers */ diff --git a/drivers/net/tun.c b/drivers/net/tun.c index dda409c808c3f1dae125649b39fd1fdf8ed7b416..12a57e466c64e263929a99702b07c730c82349fa 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1904,6 +1904,10 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, if (ret != XDP_PASS) { rcu_read_unlock(); local_bh_enable(); + if (frags) { + tfile->napi.skb = NULL; + mutex_unlock(&tfile->napi_mutex); + } return total_len; } } diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index 57236d6f128b810528d3c655476139ac7909b2e4..299e1e494ac2e67ce4274c34785a06ef39a3a46f 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -3671,6 +3672,19 @@ static void lan78xx_tx_timeout(struct net_device *net) tasklet_schedule(&dev->bh); } +static netdev_features_t lan78xx_features_check(struct sk_buff *skb, + struct net_device *netdev, + netdev_features_t features) +{ + if (skb->len + TX_OVERHEAD > MAX_SINGLE_PACKET_SIZE) + features &= ~NETIF_F_GSO_MASK; + + features = vlan_features_check(skb, features); + features = vxlan_features_check(skb, features); + + return features; +} + static const struct net_device_ops lan78xx_netdev_ops = { .ndo_open = lan78xx_open, .ndo_stop = lan78xx_stop, @@ -3684,6 +3698,7 @@ static const struct net_device_ops lan78xx_netdev_ops = { .ndo_set_features = lan78xx_set_features, .ndo_vlan_rx_add_vid = lan78xx_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = lan78xx_vlan_rx_kill_vid, + .ndo_features_check = lan78xx_features_check, }; static void lan78xx_stat_monitor(struct timer_list *t) @@ -3753,6 +3768,7 @@ static int lan78xx_probe(struct usb_interface *intf, /* MTU range: 68 - 9000 */ netdev->max_mtu = MAX_SINGLE_PACKET_SIZE; + netif_set_gso_max_size(netdev, MAX_SINGLE_PACKET_SIZE - MAX_HEADER); dev->ep_blkin = (intf->cur_altsetting)->endpoint + 0; dev->ep_blkout = (intf->cur_altsetting)->endpoint + 1; diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index b55fd76348f9f8479158b808dc5a79708515c0e1..a04f8577d9f2168a1ad1cc92a0e39b692441a7a4 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -63,7 +63,6 @@ enum qmi_wwan_flags { enum qmi_wwan_quirks { QMI_WWAN_QUIRK_DTR = 1 << 0, /* needs "set DTR" request */ - QMI_WWAN_QUIRK_QUECTEL_DYNCFG = 1 << 1, /* check num. endpoints */ }; struct qmimux_hdr { @@ -853,16 +852,6 @@ static const struct driver_info qmi_wwan_info_quirk_dtr = { .data = QMI_WWAN_QUIRK_DTR, }; -static const struct driver_info qmi_wwan_info_quirk_quectel_dyncfg = { - .description = "WWAN/QMI device", - .flags = FLAG_WWAN | FLAG_SEND_ZLP, - .bind = qmi_wwan_bind, - .unbind = qmi_wwan_unbind, - .manage_power = qmi_wwan_manage_power, - .rx_fixup = qmi_wwan_rx_fixup, - .data = QMI_WWAN_QUIRK_DTR | QMI_WWAN_QUIRK_QUECTEL_DYNCFG, -}; - #define HUAWEI_VENDOR_ID 0x12D1 /* map QMI/wwan function by a fixed interface number */ @@ -883,14 +872,18 @@ static const struct driver_info qmi_wwan_info_quirk_quectel_dyncfg = { #define QMI_GOBI_DEVICE(vend, prod) \ QMI_FIXED_INTF(vend, prod, 0) -/* Quectel does not use fixed interface numbers on at least some of their - * devices. We need to check the number of endpoints to ensure that we bind to - * the correct interface. +/* Many devices have QMI and DIAG functions which are distinguishable + * from other vendor specific functions by class, subclass and + * protocol all being 0xff. The DIAG function has exactly 2 endpoints + * and is silently rejected when probed. + * + * This makes it possible to match dynamically numbered QMI functions + * as seen on e.g. many Quectel modems. */ -#define QMI_QUIRK_QUECTEL_DYNCFG(vend, prod) \ +#define QMI_MATCH_FF_FF_FF(vend, prod) \ USB_DEVICE_AND_INTERFACE_INFO(vend, prod, USB_CLASS_VENDOR_SPEC, \ USB_SUBCLASS_VENDOR_SPEC, 0xff), \ - .driver_info = (unsigned long)&qmi_wwan_info_quirk_quectel_dyncfg + .driver_info = (unsigned long)&qmi_wwan_info_quirk_dtr static const struct usb_device_id products[] = { /* 1. CDC ECM like devices match on the control interface */ @@ -996,9 +989,10 @@ static const struct usb_device_id products[] = { USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x581d, USB_CLASS_VENDOR_SPEC, 1, 7), .driver_info = (unsigned long)&qmi_wwan_info, }, - {QMI_QUIRK_QUECTEL_DYNCFG(0x2c7c, 0x0125)}, /* Quectel EC25, EC20 R2.0 Mini PCIe */ - {QMI_QUIRK_QUECTEL_DYNCFG(0x2c7c, 0x0306)}, /* Quectel EP06/EG06/EM06 */ - {QMI_QUIRK_QUECTEL_DYNCFG(0x2c7c, 0x0512)}, /* Quectel EG12/EM12 */ + {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0125)}, /* Quectel EC25, EC20 R2.0 Mini PCIe */ + {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0306)}, /* Quectel EP06/EG06/EM06 */ + {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0512)}, /* Quectel EG12/EM12 */ + {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0800)}, /* Quectel RM500Q-GL */ /* 3. Combined interface devices matching on interface number */ {QMI_FIXED_INTF(0x0408, 0xea42, 4)}, /* Yota / Megafon M100-1 */ @@ -1297,6 +1291,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x413c, 0x81b6, 8)}, /* Dell Wireless 5811e */ {QMI_FIXED_INTF(0x413c, 0x81b6, 10)}, /* Dell Wireless 5811e */ {QMI_FIXED_INTF(0x413c, 0x81d7, 0)}, /* Dell Wireless 5821e */ + {QMI_FIXED_INTF(0x413c, 0x81d7, 1)}, /* Dell Wireless 5821e preproduction config */ {QMI_FIXED_INTF(0x413c, 0x81e0, 0)}, /* Dell Wireless 5821e with eSIM support*/ {QMI_FIXED_INTF(0x03f0, 0x4e1d, 8)}, /* HP lt4111 LTE/EV-DO/HSPA+ Gobi 4G Module */ {QMI_FIXED_INTF(0x03f0, 0x9d1d, 1)}, /* HP lt4120 Snapdragon X5 LTE */ @@ -1388,7 +1383,6 @@ static int qmi_wwan_probe(struct usb_interface *intf, { struct usb_device_id *id = (struct usb_device_id *)prod; struct usb_interface_descriptor *desc = &intf->cur_altsetting->desc; - const struct driver_info *info; /* Workaround to enable dynamic IDs. This disables usbnet * blacklisting functionality. Which, if required, can be @@ -1424,12 +1418,8 @@ static int qmi_wwan_probe(struct usb_interface *intf, * different. Ignore the current interface if the number of endpoints * equals the number for the diag interface (two). */ - info = (void *)id->driver_info; - - if (info->data & QMI_WWAN_QUIRK_QUECTEL_DYNCFG) { - if (desc->bNumEndpoints == 2) - return -ENODEV; - } + if (desc->bNumEndpoints == 2) + return -ENODEV; return usbnet_probe(intf, id); } diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 91d47a714afdf63829904faf9a5f0ea955207d2a..c5c188dc662688aac8b2e507eddaf1f585933d46 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -5167,6 +5167,9 @@ static int rtl8152_probe(struct usb_interface *intf, return -ENODEV; } + if (intf->cur_altsetting->desc.bNumEndpoints < 3) + return -ENODEV; + usb_reset_device(udev); netdev = alloc_etherdev(sizeof(struct r8152)); if (!netdev) { @@ -5256,6 +5259,11 @@ static int rtl8152_probe(struct usb_interface *intf, intf->needs_remote_wakeup = 1; + if (!rtl_can_wakeup(tp)) + __rtl_set_wol(tp, 0); + else + tp->saved_wolopts = __rtl_get_wol(tp); + tp->rtl_ops.init(tp); queue_delayed_work(system_long_wq, &tp->hw_phy_work, 0); set_ethernet_addr(tp); @@ -5269,10 +5277,6 @@ static int rtl8152_probe(struct usb_interface *intf, goto out1; } - if (!rtl_can_wakeup(tp)) - __rtl_set_wol(tp, 0); - - tp->saved_wolopts = __rtl_get_wol(tp); if (tp->saved_wolopts) device_set_wakeup_enable(&udev->dev, true); else diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 613f3668185365c3f77acb03d15315544b995bf8..df88981e796a4c355725d3122f075a086e591328 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -3496,7 +3496,6 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[], struct vxlan_rdst *dst = &vxlan->default_dst; struct vxlan_rdst old_dst; struct vxlan_config conf; - struct vxlan_fdb *f = NULL; int err; err = vxlan_nl2conf(tb, data, @@ -3522,19 +3521,19 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[], old_dst.remote_ifindex, 0); if (!vxlan_addr_any(&dst->remote_ip)) { - err = vxlan_fdb_create(vxlan, all_zeros_mac, + err = vxlan_fdb_update(vxlan, all_zeros_mac, &dst->remote_ip, NUD_REACHABLE | NUD_PERMANENT, + NLM_F_APPEND | NLM_F_CREATE, vxlan->cfg.dst_port, dst->remote_vni, dst->remote_vni, dst->remote_ifindex, - NTF_SELF, &f); + NTF_SELF); if (err) { spin_unlock_bh(&vxlan->hash_lock); return err; } - vxlan_fdb_notify(vxlan, f, first_remote_rtnl(f), RTM_NEWNEIGH); } spin_unlock_bh(&vxlan->hash_lock); } diff --git a/drivers/net/wan/fsl_ucc_hdlc.c b/drivers/net/wan/fsl_ucc_hdlc.c index 0212f576a838ca2b4e798dc001345a449fa9ef80..9ab04ef532f342d69f50024a66f823ddefff94dc 100644 --- a/drivers/net/wan/fsl_ucc_hdlc.c +++ b/drivers/net/wan/fsl_ucc_hdlc.c @@ -76,7 +76,7 @@ static struct ucc_tdm_info utdm_primary_info = { }, }; -static struct ucc_tdm_info utdm_info[MAX_HDLC_NUM]; +static struct ucc_tdm_info utdm_info[UCC_MAX_NUM]; static int uhdlc_init(struct ucc_hdlc_private *priv) { @@ -242,6 +242,11 @@ static int uhdlc_init(struct ucc_hdlc_private *priv) ret = -ENOMEM; goto free_riptr; } + if (riptr != (u16)riptr || tiptr != (u16)tiptr) { + dev_err(priv->dev, "MURAM allocation out of addressable range\n"); + ret = -ENOMEM; + goto free_tiptr; + } /* Set RIPTR, TIPTR */ iowrite16be(riptr, &priv->ucc_pram->riptr); diff --git a/drivers/net/wan/ixp4xx_hss.c b/drivers/net/wan/ixp4xx_hss.c index 6a505c26a3e74416c0461b677871a81e2b543041..a269ed63d90f7fd069b6e055ec209c10e5b6dffa 100644 --- a/drivers/net/wan/ixp4xx_hss.c +++ b/drivers/net/wan/ixp4xx_hss.c @@ -261,7 +261,7 @@ struct port { struct hss_plat_info *plat; buffer_t *rx_buff_tab[RX_DESCS], *tx_buff_tab[TX_DESCS]; struct desc *desc_tab; /* coherent */ - u32 desc_tab_phys; + dma_addr_t desc_tab_phys; unsigned int id; unsigned int clock_type, clock_rate, loopback; unsigned int initialized, carrier; @@ -861,7 +861,7 @@ static int hss_hdlc_xmit(struct sk_buff *skb, struct net_device *dev) dev->stats.tx_dropped++; return NETDEV_TX_OK; } - memcpy_swab32(mem, (u32 *)((int)skb->data & ~3), bytes / 4); + memcpy_swab32(mem, (u32 *)((uintptr_t)skb->data & ~3), bytes / 4); dev_kfree_skb(skb); #endif diff --git a/drivers/net/wan/sdla.c b/drivers/net/wan/sdla.c index 57ed259c8208d32474912a144e735cae74992fe6..09fde60a5f9de7a2dd7c00b03f67109b1313d6aa 100644 --- a/drivers/net/wan/sdla.c +++ b/drivers/net/wan/sdla.c @@ -711,7 +711,7 @@ static netdev_tx_t sdla_transmit(struct sk_buff *skb, spin_lock_irqsave(&sdla_lock, flags); SDLA_WINDOW(dev, addr); - pbuf = (void *)(((int) dev->mem_start) + (addr & SDLA_ADDR_MASK)); + pbuf = (void *)(dev->mem_start + (addr & SDLA_ADDR_MASK)); __sdla_write(dev, pbuf->buf_addr, skb->data, skb->len); SDLA_WINDOW(dev, addr); pbuf->opp_flag = 1; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 448e3a8c33a6dfa30bfa01190bbfbc24efbd042b..a09d7a07e90a8448219fa721591528b156cb9c20 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1,7 +1,7 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. - * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -3853,7 +3853,7 @@ void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work) ath10k_warn(ar, "failed to transmit management frame by ref via WMI: %d\n", ret); dma_unmap_single(ar->dev, paddr, skb->len, - DMA_FROM_DEVICE); + DMA_TO_DEVICE); ieee80211_free_txskb(ar->hw, skb); } } else { diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 2a503aacf0c645b6477d10fc452c355b5f996637..caece8339a50aab0082ec149d5e1be871b7a5482 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -1613,11 +1613,22 @@ static int ath10k_pci_dump_memory_reg(struct ath10k *ar, { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); u32 i; + int ret; + + mutex_lock(&ar->conf_mutex); + if (ar->state != ATH10K_STATE_ON) { + ath10k_warn(ar, "Skipping pci_dump_memory_reg invalid state\n"); + ret = -EIO; + goto done; + } for (i = 0; i < region->len; i += 4) *(u32 *)(buf + i) = ioread32(ar_pci->mem + region->start + i); - return region->len; + ret = region->len; +done: + mutex_unlock(&ar->conf_mutex); + return ret; } /* if an error happened returns < 0, otherwise the length */ @@ -1713,7 +1724,11 @@ static void ath10k_pci_dump_memory(struct ath10k *ar, count = ath10k_pci_dump_memory_sram(ar, current_region, buf); break; case ATH10K_MEM_REGION_TYPE_IOREG: - count = ath10k_pci_dump_memory_reg(ar, current_region, buf); + ret = ath10k_pci_dump_memory_reg(ar, current_region, buf); + if (ret < 0) + break; + + count = ret; break; default: ret = ath10k_pci_dump_memory_generic(ar, current_region, buf); diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c index 686759b5613f2c7f05685a8911c1f3a882e55732..0ecaba824fb288d7eb36705d2ff70b838195a1e4 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.c +++ b/drivers/net/wireless/ath/ath10k/sdio.c @@ -392,16 +392,11 @@ static int ath10k_sdio_mbox_rx_process_packet(struct ath10k *ar, struct ath10k_htc_hdr *htc_hdr = (struct ath10k_htc_hdr *)skb->data; bool trailer_present = htc_hdr->flags & ATH10K_HTC_FLAG_TRAILER_PRESENT; enum ath10k_htc_ep_id eid; - u16 payload_len; u8 *trailer; int ret; - payload_len = le16_to_cpu(htc_hdr->len); - skb->len = payload_len + sizeof(struct ath10k_htc_hdr); - if (trailer_present) { - trailer = skb->data + sizeof(*htc_hdr) + - payload_len - htc_hdr->trailer_len; + trailer = skb->data + skb->len - htc_hdr->trailer_len; eid = pipe_id_to_eid(htc_hdr->eid); @@ -638,13 +633,31 @@ static int ath10k_sdio_mbox_rx_packet(struct ath10k *ar, { struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar); struct sk_buff *skb = pkt->skb; + struct ath10k_htc_hdr *htc_hdr; int ret; ret = ath10k_sdio_readsb(ar, ar_sdio->mbox_info.htc_addr, skb->data, pkt->alloc_len); + if (ret) + goto out; + + /* Update actual length. The original length may be incorrect, + * as the FW will bundle multiple packets as long as their sizes + * fit within the same aligned length (pkt->alloc_len). + */ + htc_hdr = (struct ath10k_htc_hdr *)skb->data; + pkt->act_len = le16_to_cpu(htc_hdr->len) + sizeof(*htc_hdr); + if (pkt->act_len > pkt->alloc_len) { + ath10k_warn(ar, "rx packet too large (%zu > %zu)\n", + pkt->act_len, pkt->alloc_len); + ret = -EMSGSIZE; + goto out; + } + + skb_put(skb, pkt->act_len); + +out: pkt->status = ret; - if (!ret) - skb_put(skb, pkt->act_len); return ret; } diff --git a/drivers/net/wireless/ath/ath10k/usb.c b/drivers/net/wireless/ath/ath10k/usb.c index f9c79e21ab22abfe044b9566f4425f05214a85f3..c64a03f164c0f4e0ee2373081400334b9ee91974 100644 --- a/drivers/net/wireless/ath/ath10k/usb.c +++ b/drivers/net/wireless/ath/ath10k/usb.c @@ -454,6 +454,7 @@ static int ath10k_usb_hif_tx_sg(struct ath10k *ar, u8 pipe_id, ath10k_dbg(ar, ATH10K_DBG_USB_BULK, "usb bulk transmit failed: %d\n", ret); usb_unanchor_urb(urb); + usb_free_urb(urb); ret = -EINVAL; goto err_free_urb_to_pipe; } diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index cdc1e64d52ad50e30dd5fe59a0e300635eda3f9d..248decb494c2815ad43aaf99eb2ed12444ba2232 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -2693,7 +2693,7 @@ ath10k_wmi_tlv_op_gen_mgmt_tx_send(struct ath10k *ar, struct sk_buff *msdu, ieee80211_is_deauth(hdr->frame_control) || ieee80211_is_disassoc(hdr->frame_control)) && ieee80211_has_protected(hdr->frame_control)) { - len += IEEE80211_CCMP_MIC_LEN; + skb_put(msdu, IEEE80211_CCMP_MIC_LEN); buf_len += IEEE80211_CCMP_MIC_LEN; } diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index aefc92d2c09b930babb9d82af8342fffc34d48fd..3372dfa0deccf076532db157d93f4476f0bdf1af 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -1,7 +1,7 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. - * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -2340,7 +2340,7 @@ static int wmi_process_mgmt_tx_comp(struct ath10k *ar, u32 desc_id, msdu = pkt_addr->vaddr; dma_unmap_single(ar->dev, pkt_addr->paddr, - msdu->len, DMA_FROM_DEVICE); + msdu->len, DMA_TO_DEVICE); info = IEEE80211_SKB_CB(msdu); if (status) @@ -9193,7 +9193,7 @@ static int ath10k_wmi_mgmt_tx_clean_up_pending(int msdu_id, void *ptr, msdu = pkt_addr->vaddr; dma_unmap_single(ar->dev, pkt_addr->paddr, - msdu->len, DMA_FROM_DEVICE); + msdu->len, DMA_TO_DEVICE); ieee80211_free_txskb(ar->hw, msdu); return 0; diff --git a/drivers/net/wireless/ath/ath9k/dynack.c b/drivers/net/wireless/ath/ath9k/dynack.c index 6e236a4854311cc972b97ccda8e782392bf9a2e3..71b4888b30e717fd590288f530e64837aec44afd 100644 --- a/drivers/net/wireless/ath/ath9k/dynack.c +++ b/drivers/net/wireless/ath/ath9k/dynack.c @@ -300,9 +300,9 @@ void ath_dynack_node_init(struct ath_hw *ah, struct ath_node *an) an->ackto = ackto; - spin_lock(&da->qlock); + spin_lock_bh(&da->qlock); list_add_tail(&an->list, &da->nodes); - spin_unlock(&da->qlock); + spin_unlock_bh(&da->qlock); } EXPORT_SYMBOL(ath_dynack_node_init); @@ -316,9 +316,9 @@ void ath_dynack_node_deinit(struct ath_hw *ah, struct ath_node *an) { struct ath_dynack *da = &ah->dynack; - spin_lock(&da->qlock); + spin_lock_bh(&da->qlock); list_del(&an->list); - spin_unlock(&da->qlock); + spin_unlock_bh(&da->qlock); } EXPORT_SYMBOL(ath_dynack_node_deinit); diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index fb649d85b8fc73fbaa44dcfacb231674593f776c..dd0c32379375af8d63a4ad95212266de2bf3766f 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -1216,7 +1216,7 @@ static void ath9k_hif_usb_firmware_cb(const struct firmware *fw, void *context) static int send_eject_command(struct usb_interface *interface) { struct usb_device *udev = interface_to_usbdev(interface); - struct usb_host_interface *iface_desc = &interface->altsetting[0]; + struct usb_host_interface *iface_desc = interface->cur_altsetting; struct usb_endpoint_descriptor *endpoint; unsigned char *cmd; u8 bulk_out_ep; diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c index 00098f24116dea042ad5e57526e4a4cf6b64183d..6cd113b3b3e49c82ec0296bae293e107e3fb7d8b 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.c +++ b/drivers/net/wireless/ath/wcn36xx/smd.c @@ -641,52 +641,58 @@ int wcn36xx_smd_start_hw_scan(struct wcn36xx *wcn, struct ieee80211_vif *vif, struct cfg80211_scan_request *req) { struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif); - struct wcn36xx_hal_start_scan_offload_req_msg msg_body; + struct wcn36xx_hal_start_scan_offload_req_msg *msg_body; int ret, i; if (req->ie_len > WCN36XX_MAX_SCAN_IE_LEN) return -EINVAL; mutex_lock(&wcn->hal_mutex); - INIT_HAL_MSG(msg_body, WCN36XX_HAL_START_SCAN_OFFLOAD_REQ); + msg_body = kzalloc(sizeof(*msg_body), GFP_KERNEL); + if (!msg_body) { + ret = -ENOMEM; + goto out; + } - msg_body.scan_type = WCN36XX_HAL_SCAN_TYPE_ACTIVE; - msg_body.min_ch_time = 30; - msg_body.max_ch_time = 100; - msg_body.scan_hidden = 1; - memcpy(msg_body.mac, vif->addr, ETH_ALEN); - msg_body.bss_type = vif_priv->bss_type; - msg_body.p2p_search = vif->p2p; + INIT_HAL_MSG((*msg_body), WCN36XX_HAL_START_SCAN_OFFLOAD_REQ); - msg_body.num_ssid = min_t(u8, req->n_ssids, ARRAY_SIZE(msg_body.ssids)); - for (i = 0; i < msg_body.num_ssid; i++) { - msg_body.ssids[i].length = min_t(u8, req->ssids[i].ssid_len, - sizeof(msg_body.ssids[i].ssid)); - memcpy(msg_body.ssids[i].ssid, req->ssids[i].ssid, - msg_body.ssids[i].length); + msg_body->scan_type = WCN36XX_HAL_SCAN_TYPE_ACTIVE; + msg_body->min_ch_time = 30; + msg_body->max_ch_time = 100; + msg_body->scan_hidden = 1; + memcpy(msg_body->mac, vif->addr, ETH_ALEN); + msg_body->bss_type = vif_priv->bss_type; + msg_body->p2p_search = vif->p2p; + + msg_body->num_ssid = min_t(u8, req->n_ssids, ARRAY_SIZE(msg_body->ssids)); + for (i = 0; i < msg_body->num_ssid; i++) { + msg_body->ssids[i].length = min_t(u8, req->ssids[i].ssid_len, + sizeof(msg_body->ssids[i].ssid)); + memcpy(msg_body->ssids[i].ssid, req->ssids[i].ssid, + msg_body->ssids[i].length); } - msg_body.num_channel = min_t(u8, req->n_channels, - sizeof(msg_body.channels)); - for (i = 0; i < msg_body.num_channel; i++) - msg_body.channels[i] = req->channels[i]->hw_value; + msg_body->num_channel = min_t(u8, req->n_channels, + sizeof(msg_body->channels)); + for (i = 0; i < msg_body->num_channel; i++) + msg_body->channels[i] = req->channels[i]->hw_value; - msg_body.header.len -= WCN36XX_MAX_SCAN_IE_LEN; + msg_body->header.len -= WCN36XX_MAX_SCAN_IE_LEN; if (req->ie_len > 0) { - msg_body.ie_len = req->ie_len; - msg_body.header.len += req->ie_len; - memcpy(msg_body.ie, req->ie, req->ie_len); + msg_body->ie_len = req->ie_len; + msg_body->header.len += req->ie_len; + memcpy(msg_body->ie, req->ie, req->ie_len); } - PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + PREPARE_HAL_BUF(wcn->hal_buf, (*msg_body)); wcn36xx_dbg(WCN36XX_DBG_HAL, "hal start hw-scan (channels: %u; ssids: %u; p2p: %s)\n", - msg_body.num_channel, msg_body.num_ssid, - msg_body.p2p_search ? "yes" : "no"); + msg_body->num_channel, msg_body->num_ssid, + msg_body->p2p_search ? "yes" : "no"); - ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + ret = wcn36xx_smd_send_and_wait(wcn, msg_body->header.len); if (ret) { wcn36xx_err("Sending hal_start_scan_offload failed\n"); goto out; @@ -698,6 +704,7 @@ int wcn36xx_smd_start_hw_scan(struct wcn36xx *wcn, struct ieee80211_vif *vif, goto out; } out: + kfree(msg_body); mutex_unlock(&wcn->hal_mutex); return ret; } @@ -1257,96 +1264,104 @@ int wcn36xx_smd_config_sta(struct wcn36xx *wcn, struct ieee80211_vif *vif, static int wcn36xx_smd_config_bss_v1(struct wcn36xx *wcn, const struct wcn36xx_hal_config_bss_req_msg *orig) { - struct wcn36xx_hal_config_bss_req_msg_v1 msg_body; - struct wcn36xx_hal_config_bss_params_v1 *bss = &msg_body.bss_params; - struct wcn36xx_hal_config_sta_params_v1 *sta = &bss->sta; + struct wcn36xx_hal_config_bss_req_msg_v1 *msg_body; + struct wcn36xx_hal_config_bss_params_v1 *bss; + struct wcn36xx_hal_config_sta_params_v1 *sta; + int ret; + + msg_body = kzalloc(sizeof(*msg_body), GFP_KERNEL); + if (!msg_body) + return -ENOMEM; + + INIT_HAL_MSG((*msg_body), WCN36XX_HAL_CONFIG_BSS_REQ); - INIT_HAL_MSG(msg_body, WCN36XX_HAL_CONFIG_BSS_REQ); + bss = &msg_body->bss_params; + sta = &bss->sta; /* convert orig to v1 */ - memcpy(&msg_body.bss_params.bssid, + memcpy(&msg_body->bss_params.bssid, &orig->bss_params.bssid, ETH_ALEN); - memcpy(&msg_body.bss_params.self_mac_addr, + memcpy(&msg_body->bss_params.self_mac_addr, &orig->bss_params.self_mac_addr, ETH_ALEN); - msg_body.bss_params.bss_type = orig->bss_params.bss_type; - msg_body.bss_params.oper_mode = orig->bss_params.oper_mode; - msg_body.bss_params.nw_type = orig->bss_params.nw_type; + msg_body->bss_params.bss_type = orig->bss_params.bss_type; + msg_body->bss_params.oper_mode = orig->bss_params.oper_mode; + msg_body->bss_params.nw_type = orig->bss_params.nw_type; - msg_body.bss_params.short_slot_time_supported = + msg_body->bss_params.short_slot_time_supported = orig->bss_params.short_slot_time_supported; - msg_body.bss_params.lla_coexist = orig->bss_params.lla_coexist; - msg_body.bss_params.llb_coexist = orig->bss_params.llb_coexist; - msg_body.bss_params.llg_coexist = orig->bss_params.llg_coexist; - msg_body.bss_params.ht20_coexist = orig->bss_params.ht20_coexist; - msg_body.bss_params.lln_non_gf_coexist = + msg_body->bss_params.lla_coexist = orig->bss_params.lla_coexist; + msg_body->bss_params.llb_coexist = orig->bss_params.llb_coexist; + msg_body->bss_params.llg_coexist = orig->bss_params.llg_coexist; + msg_body->bss_params.ht20_coexist = orig->bss_params.ht20_coexist; + msg_body->bss_params.lln_non_gf_coexist = orig->bss_params.lln_non_gf_coexist; - msg_body.bss_params.lsig_tx_op_protection_full_support = + msg_body->bss_params.lsig_tx_op_protection_full_support = orig->bss_params.lsig_tx_op_protection_full_support; - msg_body.bss_params.rifs_mode = orig->bss_params.rifs_mode; - msg_body.bss_params.beacon_interval = orig->bss_params.beacon_interval; - msg_body.bss_params.dtim_period = orig->bss_params.dtim_period; - msg_body.bss_params.tx_channel_width_set = + msg_body->bss_params.rifs_mode = orig->bss_params.rifs_mode; + msg_body->bss_params.beacon_interval = orig->bss_params.beacon_interval; + msg_body->bss_params.dtim_period = orig->bss_params.dtim_period; + msg_body->bss_params.tx_channel_width_set = orig->bss_params.tx_channel_width_set; - msg_body.bss_params.oper_channel = orig->bss_params.oper_channel; - msg_body.bss_params.ext_channel = orig->bss_params.ext_channel; + msg_body->bss_params.oper_channel = orig->bss_params.oper_channel; + msg_body->bss_params.ext_channel = orig->bss_params.ext_channel; - msg_body.bss_params.reserved = orig->bss_params.reserved; + msg_body->bss_params.reserved = orig->bss_params.reserved; - memcpy(&msg_body.bss_params.ssid, + memcpy(&msg_body->bss_params.ssid, &orig->bss_params.ssid, sizeof(orig->bss_params.ssid)); - msg_body.bss_params.action = orig->bss_params.action; - msg_body.bss_params.rateset = orig->bss_params.rateset; - msg_body.bss_params.ht = orig->bss_params.ht; - msg_body.bss_params.obss_prot_enabled = + msg_body->bss_params.action = orig->bss_params.action; + msg_body->bss_params.rateset = orig->bss_params.rateset; + msg_body->bss_params.ht = orig->bss_params.ht; + msg_body->bss_params.obss_prot_enabled = orig->bss_params.obss_prot_enabled; - msg_body.bss_params.rmf = orig->bss_params.rmf; - msg_body.bss_params.ht_oper_mode = orig->bss_params.ht_oper_mode; - msg_body.bss_params.dual_cts_protection = + msg_body->bss_params.rmf = orig->bss_params.rmf; + msg_body->bss_params.ht_oper_mode = orig->bss_params.ht_oper_mode; + msg_body->bss_params.dual_cts_protection = orig->bss_params.dual_cts_protection; - msg_body.bss_params.max_probe_resp_retry_limit = + msg_body->bss_params.max_probe_resp_retry_limit = orig->bss_params.max_probe_resp_retry_limit; - msg_body.bss_params.hidden_ssid = orig->bss_params.hidden_ssid; - msg_body.bss_params.proxy_probe_resp = + msg_body->bss_params.hidden_ssid = orig->bss_params.hidden_ssid; + msg_body->bss_params.proxy_probe_resp = orig->bss_params.proxy_probe_resp; - msg_body.bss_params.edca_params_valid = + msg_body->bss_params.edca_params_valid = orig->bss_params.edca_params_valid; - memcpy(&msg_body.bss_params.acbe, + memcpy(&msg_body->bss_params.acbe, &orig->bss_params.acbe, sizeof(orig->bss_params.acbe)); - memcpy(&msg_body.bss_params.acbk, + memcpy(&msg_body->bss_params.acbk, &orig->bss_params.acbk, sizeof(orig->bss_params.acbk)); - memcpy(&msg_body.bss_params.acvi, + memcpy(&msg_body->bss_params.acvi, &orig->bss_params.acvi, sizeof(orig->bss_params.acvi)); - memcpy(&msg_body.bss_params.acvo, + memcpy(&msg_body->bss_params.acvo, &orig->bss_params.acvo, sizeof(orig->bss_params.acvo)); - msg_body.bss_params.ext_set_sta_key_param_valid = + msg_body->bss_params.ext_set_sta_key_param_valid = orig->bss_params.ext_set_sta_key_param_valid; - memcpy(&msg_body.bss_params.ext_set_sta_key_param, + memcpy(&msg_body->bss_params.ext_set_sta_key_param, &orig->bss_params.ext_set_sta_key_param, sizeof(orig->bss_params.acvo)); - msg_body.bss_params.wcn36xx_hal_persona = + msg_body->bss_params.wcn36xx_hal_persona = orig->bss_params.wcn36xx_hal_persona; - msg_body.bss_params.spectrum_mgt_enable = + msg_body->bss_params.spectrum_mgt_enable = orig->bss_params.spectrum_mgt_enable; - msg_body.bss_params.tx_mgmt_power = orig->bss_params.tx_mgmt_power; - msg_body.bss_params.max_tx_power = orig->bss_params.max_tx_power; + msg_body->bss_params.tx_mgmt_power = orig->bss_params.tx_mgmt_power; + msg_body->bss_params.max_tx_power = orig->bss_params.max_tx_power; wcn36xx_smd_convert_sta_to_v1(wcn, &orig->bss_params.sta, - &msg_body.bss_params.sta); + &msg_body->bss_params.sta); - PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + PREPARE_HAL_BUF(wcn->hal_buf, (*msg_body)); wcn36xx_dbg(WCN36XX_DBG_HAL, "hal config bss v1 bssid %pM self_mac_addr %pM bss_type %d oper_mode %d nw_type %d\n", @@ -1358,7 +1373,10 @@ static int wcn36xx_smd_config_bss_v1(struct wcn36xx *wcn, sta->bssid, sta->action, sta->sta_index, sta->bssid_index, sta->aid, sta->type, sta->mac); - return wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + ret = wcn36xx_smd_send_and_wait(wcn, msg_body->header.len); + kfree(msg_body); + + return ret; } @@ -1410,16 +1428,21 @@ int wcn36xx_smd_config_bss(struct wcn36xx *wcn, struct ieee80211_vif *vif, struct ieee80211_sta *sta, const u8 *bssid, bool update) { - struct wcn36xx_hal_config_bss_req_msg msg; + struct wcn36xx_hal_config_bss_req_msg *msg; struct wcn36xx_hal_config_bss_params *bss; struct wcn36xx_hal_config_sta_params *sta_params; struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif); int ret; mutex_lock(&wcn->hal_mutex); - INIT_HAL_MSG(msg, WCN36XX_HAL_CONFIG_BSS_REQ); + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) { + ret = -ENOMEM; + goto out; + } + INIT_HAL_MSG((*msg), WCN36XX_HAL_CONFIG_BSS_REQ); - bss = &msg.bss_params; + bss = &msg->bss_params; sta_params = &bss->sta; WARN_ON(is_zero_ether_addr(bssid)); @@ -1514,11 +1537,11 @@ int wcn36xx_smd_config_bss(struct wcn36xx *wcn, struct ieee80211_vif *vif, sta_params->mac); if (!wcn36xx_is_fw_version(wcn, 1, 2, 2, 24)) { - ret = wcn36xx_smd_config_bss_v1(wcn, &msg); + ret = wcn36xx_smd_config_bss_v1(wcn, msg); } else { - PREPARE_HAL_BUF(wcn->hal_buf, msg); + PREPARE_HAL_BUF(wcn->hal_buf, (*msg)); - ret = wcn36xx_smd_send_and_wait(wcn, msg.header.len); + ret = wcn36xx_smd_send_and_wait(wcn, msg->header.len); } if (ret) { wcn36xx_err("Sending hal_config_bss failed\n"); @@ -1534,6 +1557,7 @@ int wcn36xx_smd_config_bss(struct wcn36xx *wcn, struct ieee80211_vif *vif, goto out; } out: + kfree(msg); mutex_unlock(&wcn->hal_mutex); return ret; } diff --git a/drivers/net/wireless/broadcom/b43legacy/main.c b/drivers/net/wireless/broadcom/b43legacy/main.c index 55f411925960e9cc954de38ae2a834a4ab140c60..770cc218ca4bdf34674e36317229cef46c6bb070 100644 --- a/drivers/net/wireless/broadcom/b43legacy/main.c +++ b/drivers/net/wireless/broadcom/b43legacy/main.c @@ -1304,8 +1304,9 @@ static void handle_irq_ucode_debug(struct b43legacy_wldev *dev) } /* Interrupt handler bottom-half */ -static void b43legacy_interrupt_tasklet(struct b43legacy_wldev *dev) +static void b43legacy_interrupt_tasklet(unsigned long data) { + struct b43legacy_wldev *dev = (struct b43legacy_wldev *)data; u32 reason; u32 dma_reason[ARRAY_SIZE(dev->dma_reason)]; u32 merged_dma_reason = 0; @@ -3775,7 +3776,7 @@ static int b43legacy_one_core_attach(struct ssb_device *dev, b43legacy_set_status(wldev, B43legacy_STAT_UNINIT); wldev->bad_frames_preempt = modparam_bad_frames_preempt; tasklet_init(&wldev->isr_tasklet, - (void (*)(unsigned long))b43legacy_interrupt_tasklet, + b43legacy_interrupt_tasklet, (unsigned long)wldev); if (modparam_pio) wldev->__using_pio = true; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c index d2f788d8866814ec1e8e02444c9ca1de26615a87..710dc59c5d34ddb479f923feb38a5d988d9e230a 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c @@ -617,15 +617,13 @@ int brcmf_sdiod_send_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes) err = brcmf_sdiod_set_backplane_window(sdiodev, addr); if (err) - return err; + goto out; addr &= SBSDIO_SB_OFT_ADDR_MASK; addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; - if (!err) - err = brcmf_sdiod_skbuff_write(sdiodev, sdiodev->func2, addr, - mypkt); - + err = brcmf_sdiod_skbuff_write(sdiodev, sdiodev->func2, addr, mypkt); +out: brcmu_pkt_buf_free_skb(mypkt); return err; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h index c4965184cdf377af15353b1a41f05902172357ac..3d441c5c745ccf30dddf7a38e36fe8a62617d958 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h @@ -90,6 +90,7 @@ struct brcmf_bus_ops { int (*get_memdump)(struct device *dev, void *data, size_t len); int (*get_fwname)(struct device *dev, const char *ext, unsigned char *fw_name); + void (*debugfs_create)(struct device *dev); }; @@ -235,6 +236,15 @@ int brcmf_bus_get_fwname(struct brcmf_bus *bus, const char *ext, return bus->ops->get_fwname(bus->dev, ext, fw_name); } +static inline +void brcmf_bus_debugfs_create(struct brcmf_bus *bus) +{ + if (!bus->ops->debugfs_create) + return; + + return bus->ops->debugfs_create(bus->dev); +} + /* * interface functions from common layer */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index 584e05fdca6adaecdd5120e24c98b73b204e500f..9d7b8834b85454fb0dabf23538598726ec3a7f88 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -1105,6 +1105,7 @@ static int brcmf_bus_started(struct brcmf_pub *drvr, struct cfg80211_ops *ops) brcmf_debugfs_add_entry(drvr, "revinfo", brcmf_revinfo_read); brcmf_feat_debugfs_create(drvr); brcmf_proto_debugfs_create(drvr); + brcmf_bus_debugfs_create(bus_if); return 0; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index abaed2fa2defd39841109135529d848968e136c0..e0211321fe9e88a9efb2c2a46acb19dfb2f9689c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -1934,6 +1934,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) BRCMF_SDIO_FT_NORMAL)) { rd->len = 0; brcmu_pkt_buf_free_skb(pkt); + continue; } bus->sdcnt.rx_readahead_cnt++; if (rd->len != roundup(rd_new.len, 16)) { @@ -3131,9 +3132,12 @@ static int brcmf_debugfs_sdio_count_read(struct seq_file *seq, void *data) return 0; } -static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) +static void brcmf_sdio_debugfs_create(struct device *dev) { - struct brcmf_pub *drvr = bus->sdiodev->bus_if->drvr; + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pub *drvr = bus_if->drvr; + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + struct brcmf_sdio *bus = sdiodev->bus; struct dentry *dentry = brcmf_debugfs_get_devdir(drvr); if (IS_ERR_OR_NULL(dentry)) @@ -3153,7 +3157,7 @@ static int brcmf_sdio_checkdied(struct brcmf_sdio *bus) return 0; } -static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) +static void brcmf_sdio_debugfs_create(struct device *dev) { } #endif /* DEBUG */ @@ -3438,8 +3442,6 @@ static int brcmf_sdio_bus_preinit(struct device *dev) if (bus->rxbuf) bus->rxblen = value; - brcmf_sdio_debugfs_create(bus); - /* the commands below use the terms tx and rx from * a device perspective, ie. bus:txglom affects the * bus transfers from device to host. @@ -4050,6 +4052,7 @@ static const struct brcmf_bus_ops brcmf_sdio_bus_ops = { .get_ramsize = brcmf_sdio_bus_get_ramsize, .get_memdump = brcmf_sdio_bus_get_memdump, .get_fwname = brcmf_sdio_get_fwname, + .debugfs_create = brcmf_sdio_debugfs_create }; #define BRCMF_SDIO_FW_CODE 0 diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c index 44ead0fea7c61d55bb49ecfcfb51b0d18892f0cf..41254f04ab152eb4e562d04867ccdd7cceee5168 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c @@ -441,6 +441,7 @@ brcmf_usbdev_qinit(struct list_head *q, int qsize) usb_free_urb(req->urb); list_del(q->next); } + kfree(reqs); return NULL; } @@ -1357,7 +1358,7 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) goto fail; } - desc = &intf->altsetting[0].desc; + desc = &intf->cur_altsetting->desc; if ((desc->bInterfaceClass != USB_CLASS_VENDOR_SPEC) || (desc->bInterfaceSubClass != 2) || (desc->bInterfaceProtocol != 0xff)) { @@ -1370,7 +1371,7 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) num_of_eps = desc->bNumEndpoints; for (ep = 0; ep < num_of_eps; ep++) { - endpoint = &intf->altsetting[0].endpoint[ep].desc; + endpoint = &intf->cur_altsetting->endpoint[ep].desc; endpoint_num = usb_endpoint_num(endpoint); if (!usb_endpoint_xfer_bulk(endpoint)) continue; diff --git a/drivers/net/wireless/cisco/airo.c b/drivers/net/wireless/cisco/airo.c index 5512c7f73fce89ec231f42357d7ee257c3a6f7d7..c3fe9bfff8122ca9e340382302ed2b1e63e5e0ef 100644 --- a/drivers/net/wireless/cisco/airo.c +++ b/drivers/net/wireless/cisco/airo.c @@ -7786,16 +7786,8 @@ static int readrids(struct net_device *dev, aironet_ioctl *comp) { case AIROGVLIST: ridcode = RID_APLIST; break; case AIROGDRVNAM: ridcode = RID_DRVNAME; break; case AIROGEHTENC: ridcode = RID_ETHERENCAP; break; - case AIROGWEPKTMP: ridcode = RID_WEP_TEMP; - /* Only super-user can read WEP keys */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - break; - case AIROGWEPKNV: ridcode = RID_WEP_PERM; - /* Only super-user can read WEP keys */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - break; + case AIROGWEPKTMP: ridcode = RID_WEP_TEMP; break; + case AIROGWEPKNV: ridcode = RID_WEP_PERM; break; case AIROGSTAT: ridcode = RID_STATUS; break; case AIROGSTATSD32: ridcode = RID_STATSDELTA; break; case AIROGSTATSC32: ridcode = RID_STATS; break; @@ -7809,7 +7801,13 @@ static int readrids(struct net_device *dev, aironet_ioctl *comp) { return -EINVAL; } - if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL) + if (ridcode == RID_WEP_TEMP || ridcode == RID_WEP_PERM) { + /* Only super-user can read WEP keys */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + } + + if ((iobuf = kzalloc(RIDSIZE, GFP_KERNEL)) == NULL) return -ENOMEM; PC4500_readrid(ai,ridcode,iobuf,RIDSIZE, 1); diff --git a/drivers/net/wireless/cnss2/debug.c b/drivers/net/wireless/cnss2/debug.c index 900286667bf704596b4353be46799f4f923cc6c6..489a2d01958aab246887b9c202dcfcad2fa0ca06 100644 --- a/drivers/net/wireless/cnss2/debug.c +++ b/drivers/net/wireless/cnss2/debug.c @@ -904,17 +904,21 @@ int cnss_debugfs_create(struct cnss_plat_data *plat_priv) out: return ret; } + +void cnss_debugfs_destroy(struct cnss_plat_data *plat_priv) +{ + debugfs_remove_recursive(plat_priv->root_dentry); +} #else int cnss_debugfs_create(struct cnss_plat_data *plat_priv) { return 0; } -#endif void cnss_debugfs_destroy(struct cnss_plat_data *plat_priv) { - debugfs_remove_recursive(plat_priv->root_dentry); } +#endif int cnss_debug_init(void) { diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c index 6757ea93fdf2921ea2f0b3ac1cdc6458209b0b0e..848f2ab4b35856645782209e2ee73fac74ff7774 100644 --- a/drivers/net/wireless/cnss2/main.c +++ b/drivers/net/wireless/cnss2/main.c @@ -178,6 +178,7 @@ int cnss_request_bus_bandwidth(struct device *dev, int bandwidth) case CNSS_BUS_WIDTH_MEDIUM: case CNSS_BUS_WIDTH_HIGH: case CNSS_BUS_WIDTH_VERY_HIGH: + case CNSS_BUS_WIDTH_LOW_LATENCY: ret = msm_bus_scale_client_update_request (bus_bw_info->bus_client, bandwidth); if (!ret) diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c index cc9489007a3b7407d5f19a816867e52d4952423f..3640d2067049db024a5fe8b3db5d925ced43b9a2 100644 --- a/drivers/net/wireless/cnss2/pci.c +++ b/drivers/net/wireless/cnss2/pci.c @@ -461,27 +461,22 @@ static int cnss_pci_reg_write(struct cnss_pci_data *pci_priv, u32 offset, static int cnss_pci_force_wake_get(struct cnss_pci_data *pci_priv) { struct device *dev = &pci_priv->pci_dev->dev; - u32 timeout = 0; int ret; - ret = cnss_pci_force_wake_request(dev); + ret = cnss_pci_force_wake_request_sync(dev, + FORCE_WAKE_DELAY_TIMEOUT_US); if (ret) { if (ret != -EAGAIN) cnss_pr_err("Failed to request force wake\n"); return ret; } - while (!cnss_pci_is_device_awake(dev) && - timeout <= FORCE_WAKE_DELAY_TIMEOUT_US) { - usleep_range(FORCE_WAKE_DELAY_MIN_US, FORCE_WAKE_DELAY_MAX_US); - timeout += FORCE_WAKE_DELAY_MAX_US; - } - - if (cnss_pci_is_device_awake(dev) != true) { - cnss_pr_err("Timed out to request force wake\n"); - cnss_pci_force_wake_release(dev); - return -ETIMEDOUT; - } + /* If device's M1 state-change event races here, it can be ignored, + * as the device is expected to immediately move from M2 to M0 + * without entering low power state. + */ + if (cnss_pci_is_device_awake(dev) != true) + cnss_pr_warn("MHI not in M0, while reg still accessible\n"); return 0; } @@ -1064,16 +1059,20 @@ static int cnss_pci_set_mhi_state(struct cnss_pci_data *pci_priv, ret = 0; break; case CNSS_MHI_SUSPEND: + mutex_lock(&pci_priv->mhi_ctrl->pm_mutex); if (pci_priv->drv_connected_last) ret = mhi_pm_fast_suspend(pci_priv->mhi_ctrl, true); else ret = mhi_pm_suspend(pci_priv->mhi_ctrl); + mutex_unlock(&pci_priv->mhi_ctrl->pm_mutex); break; case CNSS_MHI_RESUME: + mutex_lock(&pci_priv->mhi_ctrl->pm_mutex); if (pci_priv->drv_connected_last) ret = mhi_pm_fast_resume(pci_priv->mhi_ctrl, true); else ret = mhi_pm_resume(pci_priv->mhi_ctrl); + mutex_unlock(&pci_priv->mhi_ctrl->pm_mutex); break; case CNSS_MHI_TRIGGER_RDDM: ret = mhi_force_rddm_mode(pci_priv->mhi_ctrl); @@ -2879,6 +2878,35 @@ int cnss_auto_resume(struct device *dev) } EXPORT_SYMBOL(cnss_auto_resume); +int cnss_pci_force_wake_request_sync(struct device *dev, int timeout_us) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev); + struct cnss_plat_data *plat_priv; + struct mhi_controller *mhi_ctrl; + + if (!pci_priv) + return -ENODEV; + + if (pci_priv->device_id != QCA6390_DEVICE_ID && + pci_priv->device_id != QCA6490_DEVICE_ID) + return 0; + + mhi_ctrl = pci_priv->mhi_ctrl; + if (!mhi_ctrl) + return -EINVAL; + + plat_priv = pci_priv->plat_priv; + if (!plat_priv) + return -ENODEV; + + if (test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state)) + return -EAGAIN; + + return mhi_device_get_sync_atomic(mhi_ctrl->mhi_dev, timeout_us); +} +EXPORT_SYMBOL(cnss_pci_force_wake_request_sync); + int cnss_pci_force_wake_request(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); @@ -4261,6 +4289,29 @@ static int cnss_pci_get_mhi_msi(struct cnss_pci_data *pci_priv) return 0; } +static int cnss_mhi_bw_scale(struct mhi_controller *mhi_ctrl, + struct mhi_link_info *link_info) +{ + struct cnss_pci_data *pci_priv = mhi_ctrl->priv_data; + int ret = 0; + + ret = msm_pcie_set_link_bandwidth(pci_priv->pci_dev, + link_info->target_link_speed, + link_info->target_link_width); + + if (ret) + return ret; + + pci_priv->def_link_speed = link_info->target_link_speed; + pci_priv->def_link_width = link_info->target_link_width; + + cnss_pr_dbg("Setting link speed:0x%x, width:0x%x\n", + link_info->target_link_speed, + link_info->target_link_width); + + return 0; +} + static int cnss_pci_register_mhi(struct cnss_pci_data *pci_priv) { int ret = 0; @@ -4288,8 +4339,10 @@ static int cnss_pci_register_mhi(struct cnss_pci_data *pci_priv) mhi_ctrl->fw_image_fallback = plat_priv->fw_fallback_name; mhi_ctrl->regs = pci_priv->bar; - cnss_pr_dbg("BAR starts at %pa\n", - &pci_resource_start(pci_priv->pci_dev, PCI_BAR_NUM)); + mhi_ctrl->len = pci_resource_len(pci_priv->pci_dev, PCI_BAR_NUM); + cnss_pr_dbg("BAR starts at %pa, len-%x\n", + &pci_resource_start(pci_priv->pci_dev, PCI_BAR_NUM), + mhi_ctrl->len); ret = cnss_pci_get_mhi_msi(pci_priv); if (ret) { @@ -4310,6 +4363,7 @@ static int cnss_pci_register_mhi(struct cnss_pci_data *pci_priv) mhi_ctrl->status_cb = cnss_mhi_notify_status; mhi_ctrl->runtime_get = cnss_mhi_pm_runtime_get; mhi_ctrl->runtime_put = cnss_mhi_pm_runtime_put_noidle; + mhi_ctrl->bw_scale = cnss_mhi_bw_scale; mhi_ctrl->rddm_size = pci_priv->plat_priv->ramdump_info_v2.ramdump_size; mhi_ctrl->sbl_size = SZ_512K; diff --git a/drivers/net/wireless/cnss2/qmi.c b/drivers/net/wireless/cnss2/qmi.c index e7dc9ff19793e81d126d09491e49260e05d8ffe0..46cc594c03202c4b39e9b40560a23b4d9025546a 100644 --- a/drivers/net/wireless/cnss2/qmi.c +++ b/drivers/net/wireless/cnss2/qmi.c @@ -150,6 +150,18 @@ static int cnss_wlfw_ind_register_send_sync(struct cnss_plat_data *plat_priv) return ret; } +#ifdef CONFIG_CNSS2_DEBUG +static inline u32 cnss_get_host_build_type(void) +{ + return QMI_HOST_BUILD_TYPE_PRIMARY_V01; +} +#else +static inline u32 cnss_get_host_build_type(void) +{ + return QMI_HOST_BUILD_TYPE_SECONDARY_V01; +} +#endif + static int cnss_wlfw_host_cap_send_sync(struct cnss_plat_data *plat_priv) { struct wlfw_host_cap_req_msg_v01 *req; @@ -209,6 +221,9 @@ static int cnss_wlfw_host_cap_send_sync(struct cnss_plat_data *plat_priv) req->ddr_range[0].start, req->ddr_range[0].size); } + req->host_build_type_valid = 1; + req->host_build_type = cnss_get_host_build_type(); + ret = qmi_txn_init(&plat_priv->qmi_wlfw, &txn, wlfw_host_cap_resp_msg_v01_ei, resp); if (ret < 0) { diff --git a/drivers/net/wireless/cnss_utils/wlan_firmware_service_v01.c b/drivers/net/wireless/cnss_utils/wlan_firmware_service_v01.c index 1899d4a619056715f971a9eb2f90790d45eaa02a..09c7e8d0babcf43e65f3a217e4bca3dbaa678b42 100644 --- a/drivers/net/wireless/cnss_utils/wlan_firmware_service_v01.c +++ b/drivers/net/wireless/cnss_utils/wlan_firmware_service_v01.c @@ -464,8 +464,7 @@ struct qmi_elem_info wlfw_ind_register_req_msg_v01_ei[] = { .elem_size = sizeof(u8), .array_type = NO_ARRAY, .tlv_type = 0x10, - .offset = offsetof(struct - wlfw_ind_register_req_msg_v01, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, fw_ready_enable_valid), }, { @@ -765,6 +764,24 @@ struct qmi_elem_info wlfw_ind_register_req_msg_v01_ei[] = { .offset = offsetof(struct wlfw_ind_register_req_msg_v01, m3_dump_upload_req_enable), }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x21, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + wfc_call_twt_config_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x21, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + wfc_call_twt_config_enable), + }, { .data_type = QMI_EOTI, .array_type = NO_ARRAY, @@ -2712,6 +2729,24 @@ struct qmi_elem_info wlfw_host_cap_req_msg_v01_ei[] = { ddr_range), .ei_array = wlfw_host_ddr_range_s_v01_ei, }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x20, + .offset = offsetof(struct wlfw_host_cap_req_msg_v01, + host_build_type_valid), + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum wlfw_host_build_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x20, + .offset = offsetof(struct wlfw_host_cap_req_msg_v01, + host_build_type), + }, { .data_type = QMI_EOTI, .array_type = NO_ARRAY, @@ -3529,6 +3564,26 @@ struct qmi_elem_info wlfw_qdss_trace_mode_req_msg_v01_ei[] = { wlfw_qdss_trace_mode_req_msg_v01, option), }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct + wlfw_qdss_trace_mode_req_msg_v01, + hw_trc_disable_override_valid), + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum wlfw_qmi_param_value_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct + wlfw_qdss_trace_mode_req_msg_v01, + hw_trc_disable_override), + }, { .data_type = QMI_EOTI, .array_type = NO_ARRAY, @@ -3753,6 +3808,126 @@ struct qmi_elem_info wlfw_wfc_call_status_req_msg_v01_ei[] = { wlfw_wfc_call_status_req_msg_v01, wfc_call_status), }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct + wlfw_wfc_call_status_req_msg_v01, + wfc_call_active_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct + wlfw_wfc_call_status_req_msg_v01, + wfc_call_active), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct + wlfw_wfc_call_status_req_msg_v01, + all_wfc_calls_held_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct + wlfw_wfc_call_status_req_msg_v01, + all_wfc_calls_held), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct + wlfw_wfc_call_status_req_msg_v01, + is_wfc_emergency_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct + wlfw_wfc_call_status_req_msg_v01, + is_wfc_emergency), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct + wlfw_wfc_call_status_req_msg_v01, + twt_ims_start_valid), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct + wlfw_wfc_call_status_req_msg_v01, + twt_ims_start), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct + wlfw_wfc_call_status_req_msg_v01, + twt_ims_int_valid), + }, + { + .data_type = QMI_UNSIGNED_2_BYTE, + .elem_len = 1, + .elem_size = sizeof(u16), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct + wlfw_wfc_call_status_req_msg_v01, + twt_ims_int), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct + wlfw_wfc_call_status_req_msg_v01, + media_quality_valid), + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum wlfw_wfc_media_quality_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct + wlfw_wfc_call_status_req_msg_v01, + media_quality), + }, { .data_type = QMI_EOTI, .array_type = NO_ARRAY, @@ -4138,3 +4313,131 @@ struct qmi_elem_info wlfw_exit_power_save_resp_msg_v01_ei[] = { .tlv_type = QMI_COMMON_TLV_TYPE, }, }; + +struct qmi_elem_info wlfw_wfc_call_twt_config_ind_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = + offsetof(struct wlfw_wfc_call_twt_config_ind_msg_v01, + twt_sta_start_valid), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = + offsetof(struct wlfw_wfc_call_twt_config_ind_msg_v01, + twt_sta_start), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = + offsetof(struct wlfw_wfc_call_twt_config_ind_msg_v01, + twt_sta_int_valid), + }, + { + .data_type = QMI_UNSIGNED_2_BYTE, + .elem_len = 1, + .elem_size = sizeof(u16), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = + offsetof(struct wlfw_wfc_call_twt_config_ind_msg_v01, + twt_sta_int), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = + offsetof(struct wlfw_wfc_call_twt_config_ind_msg_v01, + twt_sta_upo_valid), + }, + { + .data_type = QMI_UNSIGNED_2_BYTE, + .elem_len = 1, + .elem_size = sizeof(u16), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = + offsetof(struct wlfw_wfc_call_twt_config_ind_msg_v01, + twt_sta_upo), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = + offsetof(struct wlfw_wfc_call_twt_config_ind_msg_v01, + twt_sta_sp_valid), + }, + { + .data_type = QMI_UNSIGNED_2_BYTE, + .elem_len = 1, + .elem_size = sizeof(u16), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = + offsetof(struct wlfw_wfc_call_twt_config_ind_msg_v01, + twt_sta_sp), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = + offsetof(struct wlfw_wfc_call_twt_config_ind_msg_v01, + twt_sta_dl_valid), + }, + { + .data_type = QMI_UNSIGNED_2_BYTE, + .elem_len = 1, + .elem_size = sizeof(u16), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = + offsetof(struct wlfw_wfc_call_twt_config_ind_msg_v01, + twt_sta_dl), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x15, + .offset = + offsetof(struct wlfw_wfc_call_twt_config_ind_msg_v01, + twt_sta_config_changed_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x15, + .offset = + offsetof(struct wlfw_wfc_call_twt_config_ind_msg_v01, + twt_sta_config_changed), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; diff --git a/drivers/net/wireless/cnss_utils/wlan_firmware_service_v01.h b/drivers/net/wireless/cnss_utils/wlan_firmware_service_v01.h index e105cd48a9c4f20de3b07ddb232066ee96163930..4c2e5d8530b3015caf454a1df6cec2d5ff2a28aa 100644 --- a/drivers/net/wireless/cnss_utils/wlan_firmware_service_v01.h +++ b/drivers/net/wireless/cnss_utils/wlan_firmware_service_v01.h @@ -84,6 +84,7 @@ #define QMI_WLFW_MAC_ADDR_REQ_V01 0x0033 #define QMI_WLFW_EXIT_POWER_SAVE_RESP_V01 0x0050 #define QMI_WLFW_RESPOND_MEM_RESP_V01 0x0036 +#define QMI_WLFW_WFC_CALL_TWT_CONFIG_IND_V01 0x0051 #define QMI_WLFW_VBATT_RESP_V01 0x0032 #define QMI_WLFW_MSA_INFO_REQ_V01 0x002D #define QMI_WLFW_QDSS_TRACE_FREE_IND_V01 0x0046 @@ -153,6 +154,7 @@ enum wlfw_mem_type_enum_v01 { QMI_WLFW_MEM_CAL_V01 = 4, QMI_WLFW_MEM_DPD_V01 = 5, QMI_WLFW_MEM_QDSS_V01 = 6, + QMI_WLFW_MEM_HANG_DATA_V01 = 7, WLFW_MEM_TYPE_ENUM_MAX_VAL_V01 = INT_MAX, }; @@ -163,6 +165,15 @@ enum wlfw_qdss_trace_mode_enum_v01 { WLFW_QDSS_TRACE_MODE_ENUM_MAX_VAL_V01 = INT_MAX, }; +enum wlfw_wfc_media_quality_v01 { + WLFW_WFC_MEDIA_QUALITY_MIN_VAL_V01 = INT_MIN, + QMI_WLFW_WFC_MEDIA_QUAL_NOT_AVAILABLE_V01 = 0, + QMI_WLFW_WFC_MEDIA_QUAL_BAD_V01 = 1, + QMI_WLFW_WFC_MEDIA_QUAL_GOOD_V01 = 2, + QMI_WLFW_WFC_MEDIA_QUAL_EXCELLENT_V01 = 3, + WLFW_WFC_MEDIA_QUALITY_MAX_VAL_V01 = INT_MAX, +}; + enum wlfw_soc_wake_enum_v01 { WLFW_SOC_WAKE_ENUM_MIN_VAL_V01 = INT_MIN, QMI_WLFW_WAKE_REQUEST_V01 = 0, @@ -170,6 +181,22 @@ enum wlfw_soc_wake_enum_v01 { WLFW_SOC_WAKE_ENUM_MAX_VAL_V01 = INT_MAX, }; +enum wlfw_host_build_type_v01 { + WLFW_HOST_BUILD_TYPE_MIN_VAL_V01 = INT_MIN, + QMI_HOST_BUILD_TYPE_UNSPECIFIED_V01 = 0, + QMI_HOST_BUILD_TYPE_PRIMARY_V01 = 1, + QMI_HOST_BUILD_TYPE_SECONDARY_V01 = 2, + WLFW_HOST_BUILD_TYPE_MAX_VAL_V01 = INT_MAX, +}; + +enum wlfw_qmi_param_value_v01 { + WLFW_QMI_PARAM_VALUE_MIN_VAL_V01 = INT_MIN, + QMI_PARAM_INVALID_V01 = 0, + QMI_PARAM_ENABLE_V01 = 1, + QMI_PARAM_DISABLE_V01 = 2, + WLFW_QMI_PARAM_VALUE_MAX_VAL_V01 = INT_MAX, +}; + #define QMI_WLFW_CE_ATTR_FLAGS_V01 ((u32)0x00) #define QMI_WLFW_CE_ATTR_NO_SNOOP_V01 ((u32)0x01) #define QMI_WLFW_CE_ATTR_BYTE_SWAP_DATA_V01 ((u32)0x02) @@ -302,9 +329,11 @@ struct wlfw_ind_register_req_msg_v01 { u8 respond_get_info_enable; u8 m3_dump_upload_req_enable_valid; u8 m3_dump_upload_req_enable; + u8 wfc_call_twt_config_enable_valid; + u8 wfc_call_twt_config_enable; }; -#define WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN 74 +#define WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN 78 extern struct qmi_elem_info wlfw_ind_register_req_msg_v01_ei[]; struct wlfw_ind_register_resp_msg_v01 { @@ -698,9 +727,11 @@ struct wlfw_host_cap_req_msg_v01 { u8 ddr_range_valid; struct wlfw_host_ddr_range_s_v01 ddr_range[QMI_WLFW_MAX_HOST_DDR_RANGE_SIZE_V01]; + u8 host_build_type_valid; + enum wlfw_host_build_type_v01 host_build_type; }; -#define WLFW_HOST_CAP_REQ_MSG_V01_MAX_MSG_LEN 312 +#define WLFW_HOST_CAP_REQ_MSG_V01_MAX_MSG_LEN 319 extern struct qmi_elem_info wlfw_host_cap_req_msg_v01_ei[]; struct wlfw_host_cap_resp_msg_v01 { @@ -909,9 +940,11 @@ struct wlfw_qdss_trace_mode_req_msg_v01 { enum wlfw_qdss_trace_mode_enum_v01 mode; u8 option_valid; u64 option; + u8 hw_trc_disable_override_valid; + enum wlfw_qmi_param_value_v01 hw_trc_disable_override; }; -#define WLFW_QDSS_TRACE_MODE_REQ_MSG_V01_MAX_MSG_LEN 18 +#define WLFW_QDSS_TRACE_MODE_REQ_MSG_V01_MAX_MSG_LEN 25 extern struct qmi_elem_info wlfw_qdss_trace_mode_req_msg_v01_ei[]; struct wlfw_qdss_trace_mode_resp_msg_v01 { @@ -979,9 +1012,21 @@ extern struct qmi_elem_info wlfw_antenna_grant_resp_msg_v01_ei[]; struct wlfw_wfc_call_status_req_msg_v01 { u32 wfc_call_status_len; u8 wfc_call_status[QMI_WLFW_MAX_WFC_CALL_STATUS_DATA_SIZE_V01]; -}; - -#define WLFW_WFC_CALL_STATUS_REQ_MSG_V01_MAX_MSG_LEN 261 + u8 wfc_call_active_valid; + u8 wfc_call_active; + u8 all_wfc_calls_held_valid; + u8 all_wfc_calls_held; + u8 is_wfc_emergency_valid; + u8 is_wfc_emergency; + u8 twt_ims_start_valid; + u64 twt_ims_start; + u8 twt_ims_int_valid; + u16 twt_ims_int; + u8 media_quality_valid; + enum wlfw_wfc_media_quality_v01 media_quality; +}; + +#define WLFW_WFC_CALL_STATUS_REQ_MSG_V01_MAX_MSG_LEN 296 extern struct qmi_elem_info wlfw_wfc_call_status_req_msg_v01_ei[]; struct wlfw_wfc_call_status_resp_msg_v01 { @@ -1092,4 +1137,22 @@ struct wlfw_exit_power_save_resp_msg_v01 { #define WLFW_EXIT_POWER_SAVE_RESP_MSG_V01_MAX_MSG_LEN 7 extern struct qmi_elem_info wlfw_exit_power_save_resp_msg_v01_ei[]; +struct wlfw_wfc_call_twt_config_ind_msg_v01 { + u8 twt_sta_start_valid; + u64 twt_sta_start; + u8 twt_sta_int_valid; + u16 twt_sta_int; + u8 twt_sta_upo_valid; + u16 twt_sta_upo; + u8 twt_sta_sp_valid; + u16 twt_sta_sp; + u8 twt_sta_dl_valid; + u16 twt_sta_dl; + u8 twt_sta_config_changed_valid; + u8 twt_sta_config_changed; +}; + +#define WLFW_WFC_CALL_TWT_CONFIG_IND_MSG_V01_MAX_MSG_LEN 35 +extern struct qmi_elem_info wlfw_wfc_call_twt_config_ind_msg_v01_ei[]; + #endif diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2100.c b/drivers/net/wireless/intel/ipw2x00/ipw2100.c index 910db46db6a12ea2c74cf3b9e42a7a42ea9a7351..a3a470976a5c7a05d41f9cd722665d1d8228fc42 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2100.c @@ -3220,8 +3220,9 @@ static void ipw2100_tx_send_data(struct ipw2100_priv *priv) } } -static void ipw2100_irq_tasklet(struct ipw2100_priv *priv) +static void ipw2100_irq_tasklet(unsigned long data) { + struct ipw2100_priv *priv = (struct ipw2100_priv *)data; struct net_device *dev = priv->net_dev; unsigned long flags; u32 inta, tmp; @@ -6025,7 +6026,7 @@ static void ipw2100_rf_kill(struct work_struct *work) spin_unlock_irqrestore(&priv->low_lock, flags); } -static void ipw2100_irq_tasklet(struct ipw2100_priv *priv); +static void ipw2100_irq_tasklet(unsigned long data); static const struct net_device_ops ipw2100_netdev_ops = { .ndo_open = ipw2100_open, @@ -6155,7 +6156,7 @@ static struct net_device *ipw2100_alloc_device(struct pci_dev *pci_dev, INIT_DELAYED_WORK(&priv->rf_kill, ipw2100_rf_kill); INIT_DELAYED_WORK(&priv->scan_event, ipw2100_scan_event); - tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long)) + tasklet_init(&priv->irq_tasklet, ipw2100_irq_tasklet, (unsigned long)priv); /* NOTE: We do not start the deferred work for status checks yet */ diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2200.c b/drivers/net/wireless/intel/ipw2x00/ipw2200.c index 9644e7b93645f510394b1deb3accf6976fc87a78..04aee2fdba3756f7ffa63b997f27cf44bb149c39 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.c @@ -1959,8 +1959,9 @@ static void notify_wx_assoc_event(struct ipw_priv *priv) wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL); } -static void ipw_irq_tasklet(struct ipw_priv *priv) +static void ipw_irq_tasklet(unsigned long data) { + struct ipw_priv *priv = (struct ipw_priv *)data; u32 inta, inta_mask, handled = 0; unsigned long flags; int rc = 0; @@ -10694,7 +10695,7 @@ static int ipw_setup_deferred_work(struct ipw_priv *priv) INIT_WORK(&priv->qos_activate, ipw_bg_qos_activate); #endif /* CONFIG_IPW2200_QOS */ - tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long)) + tasklet_init(&priv->irq_tasklet, ipw_irq_tasklet, (unsigned long)priv); return ret; diff --git a/drivers/net/wireless/intel/iwlegacy/3945-mac.c b/drivers/net/wireless/intel/iwlegacy/3945-mac.c index 57e3b6cca2341442a8ca23052defbe84f3e3899d..b536ec20eaccb189da2d7db667099ef87756e261 100644 --- a/drivers/net/wireless/intel/iwlegacy/3945-mac.c +++ b/drivers/net/wireless/intel/iwlegacy/3945-mac.c @@ -1392,8 +1392,9 @@ il3945_dump_nic_error_log(struct il_priv *il) } static void -il3945_irq_tasklet(struct il_priv *il) +il3945_irq_tasklet(unsigned long data) { + struct il_priv *il = (struct il_priv *)data; u32 inta, handled = 0; u32 inta_fh; unsigned long flags; @@ -3419,7 +3420,7 @@ il3945_setup_deferred_work(struct il_priv *il) timer_setup(&il->watchdog, il_bg_watchdog, 0); tasklet_init(&il->irq_tasklet, - (void (*)(unsigned long))il3945_irq_tasklet, + il3945_irq_tasklet, (unsigned long)il); } diff --git a/drivers/net/wireless/intel/iwlegacy/4965-mac.c b/drivers/net/wireless/intel/iwlegacy/4965-mac.c index 280cd8ae1696db3204fa54729b8870efe9115346..6fc51c74cdb86f9dad7d1575973fd2d462121212 100644 --- a/drivers/net/wireless/intel/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/intel/iwlegacy/4965-mac.c @@ -4360,8 +4360,9 @@ il4965_synchronize_irq(struct il_priv *il) } static void -il4965_irq_tasklet(struct il_priv *il) +il4965_irq_tasklet(unsigned long data) { + struct il_priv *il = (struct il_priv *)data; u32 inta, handled = 0; u32 inta_fh; unsigned long flags; @@ -6257,7 +6258,7 @@ il4965_setup_deferred_work(struct il_priv *il) timer_setup(&il->watchdog, il_bg_watchdog, 0); tasklet_init(&il->irq_tasklet, - (void (*)(unsigned long))il4965_irq_tasklet, + il4965_irq_tasklet, (unsigned long)il); } diff --git a/drivers/net/wireless/intel/iwlegacy/common.c b/drivers/net/wireless/intel/iwlegacy/common.c index 6514baf799fef58b98a56d468641e72f8cde0888..e16f2597c219914a8c1c5d38ce2e393fd75edf31 100644 --- a/drivers/net/wireless/intel/iwlegacy/common.c +++ b/drivers/net/wireless/intel/iwlegacy/common.c @@ -717,7 +717,7 @@ il_eeprom_init(struct il_priv *il) u32 gp = _il_rd(il, CSR_EEPROM_GP); int sz; int ret; - u16 addr; + int addr; /* allocate eeprom */ sz = il->cfg->eeprom_size; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h index 6c53383647942bc915bf09766a24276f4f22b5e2..d22c1eefba6a288ab38f37dd7b73dfccb56e32b6 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h @@ -165,7 +165,7 @@ struct iwl_nvm_access_resp { */ struct iwl_nvm_get_info { __le32 reserved; -} __packed; /* GRP_REGULATORY_NVM_GET_INFO_CMD_S_VER_1 */ +} __packed; /* REGULATORY_NVM_GET_INFO_CMD_API_S_VER_1 */ /** * enum iwl_nvm_info_general_flags - flags in NVM_GET_INFO resp @@ -180,14 +180,14 @@ enum iwl_nvm_info_general_flags { * @flags: bit 0: 1 - empty, 0 - non-empty * @nvm_version: nvm version * @board_type: board type - * @reserved: reserved + * @n_hw_addrs: number of reserved MAC addresses */ struct iwl_nvm_get_info_general { __le32 flags; __le16 nvm_version; u8 board_type; - u8 reserved; -} __packed; /* GRP_REGULATORY_NVM_GET_INFO_GENERAL_S_VER_1 */ + u8 n_hw_addrs; +} __packed; /* REGULATORY_NVM_GET_INFO_GENERAL_S_VER_2 */ /** * enum iwl_nvm_mac_sku_flags - flags in &iwl_nvm_get_info_sku @@ -231,7 +231,7 @@ struct iwl_nvm_get_info_sku { struct iwl_nvm_get_info_phy { __le32 tx_chains; __le32 rx_chains; -} __packed; /* GRP_REGULATORY_NVM_GET_INFO_PHY_SKU_SECTION_S_VER_1 */ +} __packed; /* REGULATORY_NVM_GET_INFO_PHY_SKU_SECTION_S_VER_1 */ #define IWL_NUM_CHANNELS (51) @@ -245,7 +245,7 @@ struct iwl_nvm_get_info_regulatory { __le32 lar_enabled; __le16 channel_profile[IWL_NUM_CHANNELS]; __le16 reserved; -} __packed; /* GRP_REGULATORY_NVM_GET_INFO_REGULATORY_S_VER_1 */ +} __packed; /* REGULATORY_NVM_GET_INFO_REGULATORY_S_VER_1 */ /** * struct iwl_nvm_get_info_rsp - response to get NVM data @@ -259,7 +259,7 @@ struct iwl_nvm_get_info_rsp { struct iwl_nvm_get_info_sku mac_sku; struct iwl_nvm_get_info_phy phy_sku; struct iwl_nvm_get_info_regulatory regulatory; -} __packed; /* GRP_REGULATORY_NVM_GET_INFO_CMD_RSP_S_VER_2 */ +} __packed; /* REGULATORY_NVM_GET_INFO_RSP_API_S_VER_3 */ /** * struct iwl_nvm_access_complete_cmd - NVM_ACCESS commands are completed diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index 3443cbdbab4ae2a49bf8d58cefde92a0a79fbfd8..d6fca00ecb885601d26faa35a945228399fc4fb6 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -547,6 +547,7 @@ static struct scatterlist *alloc_sgtable(int size) if (new_page) __free_page(new_page); } + kfree(table); return NULL; } alloc_size = min_t(int, size, PAGE_SIZE); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 73969dbeb5c5ff390709dea24cbd61da794d9a3a..a6e64787a3454a11d29bdf4e9d63474f0c720dab 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -217,6 +217,34 @@ enum iwl_nvm_channel_flags { NVM_CHANNEL_DC_HIGH = BIT(12), }; +/** + * enum iwl_reg_capa_flags - global flags applied for the whole regulatory + * domain. + * @REG_CAPA_BF_CCD_LOW_BAND: Beam-forming or Cyclic Delay Diversity in the + * 2.4Ghz band is allowed. + * @REG_CAPA_BF_CCD_HIGH_BAND: Beam-forming or Cyclic Delay Diversity in the + * 5Ghz band is allowed. + * @REG_CAPA_160MHZ_ALLOWED: 11ac channel with a width of 160Mhz is allowed + * for this regulatory domain (valid only in 5Ghz). + * @REG_CAPA_80MHZ_ALLOWED: 11ac channel with a width of 80Mhz is allowed + * for this regulatory domain (valid only in 5Ghz). + * @REG_CAPA_MCS_8_ALLOWED: 11ac with MCS 8 is allowed. + * @REG_CAPA_MCS_9_ALLOWED: 11ac with MCS 9 is allowed. + * @REG_CAPA_40MHZ_FORBIDDEN: 11n channel with a width of 40Mhz is forbidden + * for this regulatory domain (valid only in 5Ghz). + * @REG_CAPA_DC_HIGH_ENABLED: DC HIGH allowed. + */ +enum iwl_reg_capa_flags { + REG_CAPA_BF_CCD_LOW_BAND = BIT(0), + REG_CAPA_BF_CCD_HIGH_BAND = BIT(1), + REG_CAPA_160MHZ_ALLOWED = BIT(2), + REG_CAPA_80MHZ_ALLOWED = BIT(3), + REG_CAPA_MCS_8_ALLOWED = BIT(4), + REG_CAPA_MCS_9_ALLOWED = BIT(5), + REG_CAPA_40MHZ_FORBIDDEN = BIT(7), + REG_CAPA_DC_HIGH_ENABLED = BIT(9), +}; + static inline void iwl_nvm_print_channel_flags(struct device *dev, u32 level, int chan, u16 flags) { @@ -923,6 +951,7 @@ IWL_EXPORT_SYMBOL(iwl_parse_nvm_data); static u32 iwl_nvm_get_regdom_bw_flags(const u8 *nvm_chan, int ch_idx, u16 nvm_flags, + u16 cap_flags, const struct iwl_cfg *cfg) { u32 flags = NL80211_RRF_NO_HT40; @@ -966,6 +995,20 @@ static u32 iwl_nvm_get_regdom_bw_flags(const u8 *nvm_chan, (flags & NL80211_RRF_NO_IR)) flags |= NL80211_RRF_GO_CONCURRENT; + /* + * cap_flags is per regulatory domain so apply it for every channel + */ + if (ch_idx >= NUM_2GHZ_CHANNELS) { + if (cap_flags & REG_CAPA_40MHZ_FORBIDDEN) + flags |= NL80211_RRF_NO_HT40; + + if (!(cap_flags & REG_CAPA_80MHZ_ALLOWED)) + flags |= NL80211_RRF_NO_80MHZ; + + if (!(cap_flags & REG_CAPA_160MHZ_ALLOWED)) + flags |= NL80211_RRF_NO_160MHZ; + } + return flags; } @@ -977,7 +1020,7 @@ struct regdb_ptrs { struct ieee80211_regdomain * iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg, int num_of_ch, __le32 *channels, u16 fw_mcc, - u16 geo_info) + u16 geo_info, u16 cap) { int ch_idx; u16 ch_flags; @@ -1038,7 +1081,8 @@ iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg, } reg_rule_flags = iwl_nvm_get_regdom_bw_flags(nvm_chan, ch_idx, - ch_flags, cfg); + ch_flags, cap, + cfg); /* we can't continue the same rule */ if (ch_idx == 0 || prev_reg_rule_flags != reg_rule_flags || @@ -1315,6 +1359,7 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans, bool lar_fw_supported = !iwlwifi_mod_params.lar_disable && fw_has_capa(&fw->ucode_capa, IWL_UCODE_TLV_CAPA_LAR_SUPPORT); + bool empty_otp; u32 mac_flags; u32 sbands_flags = 0; @@ -1330,7 +1375,9 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans, } rsp = (void *)hcmd.resp_pkt->data; - if (le32_to_cpu(rsp->general.flags) & NVM_GENERAL_FLAGS_EMPTY_OTP) + empty_otp = !!(le32_to_cpu(rsp->general.flags) & + NVM_GENERAL_FLAGS_EMPTY_OTP); + if (empty_otp) IWL_INFO(trans, "OTP is empty\n"); nvm = kzalloc(sizeof(*nvm) + @@ -1354,6 +1401,11 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans, /* Initialize general data */ nvm->nvm_version = le16_to_cpu(rsp->general.nvm_version); + nvm->n_hw_addrs = rsp->general.n_hw_addrs; + if (nvm->n_hw_addrs == 0) + IWL_WARN(trans, + "Firmware declares no reserved mac addresses. OTP is empty: %d\n", + empty_otp); /* Initialize MAC sku data */ mac_flags = le32_to_cpu(rsp->mac_sku.mac_sku_flags); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h index 234d1009a9de40a81a660cfeb8bf77d5753ab0fd..a9bdd4aa01c7ea01a6de33162e74e0d5b11810b4 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h @@ -7,7 +7,7 @@ * * Copyright(c) 2008 - 2015 Intel Corporation. All rights reserved. * Copyright(c) 2016 - 2017 Intel Deutschland GmbH - * Copyright(c) 2018 Intel Corporation + * Copyright(c) 2018 - 2019 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -34,7 +34,7 @@ * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2016 - 2017 Intel Deutschland GmbH - * Copyright(c) 2018 Intel Corporation + * Copyright(c) 2018 - 2019 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -108,7 +108,7 @@ iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, struct ieee80211_regdomain * iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg, int num_of_ch, __le32 *channels, u16 fw_mcc, - u16 geo_info); + u16 geo_info, u16 cap); /** * struct iwl_nvm_section - describes an NVM section in memory. diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 9808d954dca2942d566ecde8a292cf4b6432ab3a..c7e2b88cd5abbf1e4cafecbaf604b7214ae9404b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -110,12 +110,12 @@ static int iwl_send_rss_cfg_cmd(struct iwl_mvm *mvm) int i; struct iwl_rss_config_cmd cmd = { .flags = cpu_to_le32(IWL_RSS_ENABLE), - .hash_mask = IWL_RSS_HASH_TYPE_IPV4_TCP | - IWL_RSS_HASH_TYPE_IPV4_UDP | - IWL_RSS_HASH_TYPE_IPV4_PAYLOAD | - IWL_RSS_HASH_TYPE_IPV6_TCP | - IWL_RSS_HASH_TYPE_IPV6_UDP | - IWL_RSS_HASH_TYPE_IPV6_PAYLOAD, + .hash_mask = BIT(IWL_RSS_HASH_TYPE_IPV4_TCP) | + BIT(IWL_RSS_HASH_TYPE_IPV4_UDP) | + BIT(IWL_RSS_HASH_TYPE_IPV4_PAYLOAD) | + BIT(IWL_RSS_HASH_TYPE_IPV6_TCP) | + BIT(IWL_RSS_HASH_TYPE_IPV6_UDP) | + BIT(IWL_RSS_HASH_TYPE_IPV6_PAYLOAD), }; if (mvm->trans->num_rx_queues == 1) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 476c44db0e64b1c5a0325a1e24bbd4aed6d42be0..58653598db1461e063227c2548bef44437d55245 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -317,7 +317,8 @@ struct ieee80211_regdomain *iwl_mvm_get_regdomain(struct wiphy *wiphy, __le32_to_cpu(resp->n_channels), resp->channels, __le16_to_cpu(resp->mcc), - __le16_to_cpu(resp->geo_info)); + __le16_to_cpu(resp->geo_info), + __le16_to_cpu(resp->cap)); /* Store the return source id */ src_id = resp->source_id; kfree(resp); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c index f2579c94ffdbcab604c29ee06398cc1871469b77..3270faafe0bc304d62b81845a497be0f15a736b4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c @@ -286,7 +286,7 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm) int regulatory_type; /* Checking for required sections */ - if (mvm->trans->cfg->nvm_type != IWL_NVM_EXT) { + if (mvm->trans->cfg->nvm_type == IWL_NVM) { if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data || !mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data) { IWL_ERR(mvm, "Can't parse empty OTP/NVM sections\n"); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 036d1d82d93e7eed2d6d08be91245f62d6d902f1..77e36945364215391c4116be4801213009d4c6f0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -1077,7 +1077,6 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, he_phy_data = le64_to_cpu(desc->v1.he_phy_data); rx_status->flag |= RX_FLAG_AMPDU_DETAILS; - rx_status->ampdu_reference = mvm->ampdu_ref; /* toggle is switched whenever new aggregation starts */ if (toggle_bit != mvm->ampdu_toggle) { mvm->ampdu_ref++; @@ -1092,6 +1091,7 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, RX_FLAG_AMPDU_EOF_BIT; } } + rx_status->ampdu_reference = mvm->ampdu_ref; } rcu_read_lock(); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index e850aa504b6084c94c55ad7d37ee79e6da309701..373ace38edab7bbbd467287b0d6556698e5fe434 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -2462,7 +2462,7 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_tid_data *tid_data; u16 normalized_ssn; - int txq_id; + u16 txq_id; int ret; if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT)) @@ -2506,17 +2506,24 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, */ txq_id = mvmsta->tid_data[tid].txq_id; if (txq_id == IWL_MVM_INVALID_QUEUE) { - txq_id = iwl_mvm_find_free_queue(mvm, mvmsta->sta_id, - IWL_MVM_DQA_MIN_DATA_QUEUE, - IWL_MVM_DQA_MAX_DATA_QUEUE); - if (txq_id < 0) { - ret = txq_id; + ret = iwl_mvm_find_free_queue(mvm, mvmsta->sta_id, + IWL_MVM_DQA_MIN_DATA_QUEUE, + IWL_MVM_DQA_MAX_DATA_QUEUE); + if (ret < 0) { IWL_ERR(mvm, "Failed to allocate agg queue\n"); goto release_locks; } + txq_id = ret; + /* TXQ hasn't yet been enabled, so mark it only as reserved */ mvm->queue_info[txq_id].status = IWL_MVM_QUEUE_RESERVED; + } else if (WARN_ON(txq_id >= IWL_MAX_HW_QUEUES)) { + ret = -ENXIO; + IWL_ERR(mvm, "tid_id %d out of range (0, %d)!\n", + tid, IWL_MAX_HW_QUEUES - 1); + goto out; + } else if (unlikely(mvm->queue_info[txq_id].status == IWL_MVM_QUEUE_SHARED)) { ret = -ENXIO; @@ -3038,6 +3045,10 @@ static int iwl_mvm_send_sta_igtk(struct iwl_mvm *mvm, igtk_cmd.sta_id = cpu_to_le32(sta_id); if (remove_key) { + /* This is a valid situation for IGTK */ + if (sta_id == IWL_MVM_INVALID_STA) + return 0; + igtk_cmd.ctrl_flags |= cpu_to_le32(STA_KEY_NOT_VALID); } else { struct ieee80211_key_seq seq; @@ -3345,9 +3356,9 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, IWL_DEBUG_WEP(mvm, "mvm remove dynamic key: idx=%d sta=%d\n", keyconf->keyidx, sta_id); - if (mvm_sta && (keyconf->cipher == WLAN_CIPHER_SUITE_AES_CMAC || - keyconf->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_128 || - keyconf->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256)) + if (keyconf->cipher == WLAN_CIPHER_SUITE_AES_CMAC || + keyconf->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_128 || + keyconf->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256) return iwl_mvm_send_sta_igtk(mvm, keyconf, sta_id, true); if (!__test_and_clear_bit(keyconf->hw_key_idx, mvm->fw_key_table)) { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c index 1232f63278eb64167263e55255cb95b54c27258d..319103f4b432e16cdefbca802a372fe7860dd28b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c @@ -739,7 +739,8 @@ static struct thermal_zone_device_ops tzone_ops = { static void iwl_mvm_thermal_zone_register(struct iwl_mvm *mvm) { int i; - char name[] = "iwlwifi"; + char name[16]; + static atomic_t counter = ATOMIC_INIT(0); if (!iwl_mvm_is_tt_in_fw(mvm)) { mvm->tz_device.tzone = NULL; @@ -749,6 +750,7 @@ static void iwl_mvm_thermal_zone_register(struct iwl_mvm *mvm) BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH); + sprintf(name, "iwlwifi_%u", atomic_inc_return(&counter) & 0xFF); mvm->tz_device.tzone = thermal_zone_device_register(name, IWL_MAX_DTS_TRIPS, IWL_WRITABLE_TRIPS_MSK, diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c index 64d976d872b84ab50de574703d60b081a58ad8ca..6783b20d9681bb54773f5bc87e0ddd8bfda202c5 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c @@ -102,13 +102,9 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans, /* allocate ucode sections in dram and set addresses */ ret = iwl_pcie_init_fw_sec(trans, fw, &prph_scratch->dram); - if (ret) { - dma_free_coherent(trans->dev, - sizeof(*prph_scratch), - prph_scratch, - trans_pcie->prph_scratch_dma_addr); - return ret; - } + if (ret) + goto err_free_prph_scratch; + /* Allocate prph information * currently we don't assign to the prph info anything, but it would get @@ -116,16 +112,20 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans, prph_info = dma_alloc_coherent(trans->dev, sizeof(*prph_info), &trans_pcie->prph_info_dma_addr, GFP_KERNEL); - if (!prph_info) - return -ENOMEM; + if (!prph_info) { + ret = -ENOMEM; + goto err_free_prph_scratch; + } /* Allocate context info */ ctxt_info_gen3 = dma_alloc_coherent(trans->dev, sizeof(*ctxt_info_gen3), &trans_pcie->ctxt_info_dma_addr, GFP_KERNEL); - if (!ctxt_info_gen3) - return -ENOMEM; + if (!ctxt_info_gen3) { + ret = -ENOMEM; + goto err_free_prph_info; + } ctxt_info_gen3->prph_info_base_addr = cpu_to_le64(trans_pcie->prph_info_dma_addr); @@ -176,6 +176,20 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans, iwl_set_bit(trans, CSR_GP_CNTRL, CSR_AUTO_FUNC_INIT); return 0; + +err_free_prph_info: + dma_free_coherent(trans->dev, + sizeof(*prph_info), + prph_info, + trans_pcie->prph_info_dma_addr); + +err_free_prph_scratch: + dma_free_coherent(trans->dev, + sizeof(*prph_scratch), + prph_scratch, + trans_pcie->prph_scratch_dma_addr); + return ret; + } void iwl_pcie_ctxt_info_gen3_free(struct iwl_trans *trans) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 4f5571123f70aff8c31790f597244dbea16a4e0e..24da4961513530bdeea965331ee1b33dc5cf9d46 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -3283,6 +3283,15 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, spin_lock_init(&trans_pcie->reg_lock); mutex_init(&trans_pcie->mutex); init_waitqueue_head(&trans_pcie->ucode_write_waitq); + + trans_pcie->rba.alloc_wq = alloc_workqueue("rb_allocator", + WQ_HIGHPRI | WQ_UNBOUND, 1); + if (!trans_pcie->rba.alloc_wq) { + ret = -ENOMEM; + goto out_free_trans; + } + INIT_WORK(&trans_pcie->rba.rx_alloc, iwl_pcie_rx_allocator_work); + trans_pcie->tso_hdr_page = alloc_percpu(struct iwl_tso_hdr_page); if (!trans_pcie->tso_hdr_page) { ret = -ENOMEM; @@ -3485,10 +3494,6 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, trans_pcie->inta_mask = CSR_INI_SET_MASK; } - trans_pcie->rba.alloc_wq = alloc_workqueue("rb_allocator", - WQ_HIGHPRI | WQ_UNBOUND, 1); - INIT_WORK(&trans_pcie->rba.rx_alloc, iwl_pcie_rx_allocator_work); - #ifdef CONFIG_IWLWIFI_PCIE_RTPM trans->runtime_pm_mode = IWL_PLAT_PM_MODE_D0I3; #else @@ -3501,6 +3506,8 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, iwl_pcie_free_ict(trans); out_no_pci: free_percpu(trans_pcie->tso_hdr_page); + destroy_workqueue(trans_pcie->rba.alloc_wq); +out_free_trans: iwl_trans_free(trans); return ERR_PTR(ret); } diff --git a/drivers/net/wireless/intersil/hostap/hostap_ap.c b/drivers/net/wireless/intersil/hostap/hostap_ap.c index 0094b1d2b5770568fa1993474fe96f701debcb04..3ec46f48cfde1c21b862245d4f50b32aa374ce98 100644 --- a/drivers/net/wireless/intersil/hostap/hostap_ap.c +++ b/drivers/net/wireless/intersil/hostap/hostap_ap.c @@ -2508,7 +2508,7 @@ static int prism2_hostapd_add_sta(struct ap_data *ap, sta->supported_rates[0] = 2; if (sta->tx_supp_rates & WLAN_RATE_2M) sta->supported_rates[1] = 4; - if (sta->tx_supp_rates & WLAN_RATE_5M5) + if (sta->tx_supp_rates & WLAN_RATE_5M5) sta->supported_rates[2] = 11; if (sta->tx_supp_rates & WLAN_RATE_11M) sta->supported_rates[3] = 22; diff --git a/drivers/net/wireless/intersil/orinoco/orinoco_usb.c b/drivers/net/wireless/intersil/orinoco/orinoco_usb.c index 94ad6fe29e69bdf199157e78023e15595e22daa9..b704e4bce171d4765fbab40e961d3e8134a88cb1 100644 --- a/drivers/net/wireless/intersil/orinoco/orinoco_usb.c +++ b/drivers/net/wireless/intersil/orinoco/orinoco_usb.c @@ -1364,7 +1364,8 @@ static int ezusb_init(struct hermes *hw) int retval; BUG_ON(in_interrupt()); - BUG_ON(!upriv); + if (!upriv) + return -EINVAL; upriv->reply_count = 0; /* Write the MAGIC number on the simulated registers to keep @@ -1611,9 +1612,9 @@ static int ezusb_probe(struct usb_interface *interface, /* set up the endpoint information */ /* check out the endpoints */ - iface_desc = &interface->altsetting[0].desc; + iface_desc = &interface->cur_altsetting->desc; for (i = 0; i < iface_desc->bNumEndpoints; ++i) { - ep = &interface->altsetting[0].endpoint[i].desc; + ep = &interface->cur_altsetting->endpoint[i].desc; if (usb_endpoint_is_bulk_in(ep)) { /* we found a bulk in endpoint */ diff --git a/drivers/net/wireless/marvell/libertas/cfg.c b/drivers/net/wireless/marvell/libertas/cfg.c index 57edfada0665fda7ec68b7f60128eef3af9a5219..4e3de684928bf90aaab8eefedd028bf9b209d32c 100644 --- a/drivers/net/wireless/marvell/libertas/cfg.c +++ b/drivers/net/wireless/marvell/libertas/cfg.c @@ -273,6 +273,10 @@ add_ie_rates(u8 *tlv, const u8 *ie, int *nrates) int hw, ap, ap_max = ie[1]; u8 hw_rate; + if (ap_max > MAX_RATES) { + lbs_deb_assoc("invalid rates\n"); + return tlv; + } /* Advance past IE header */ ie += 2; @@ -1717,6 +1721,9 @@ static int lbs_ibss_join_existing(struct lbs_private *priv, struct cmd_ds_802_11_ad_hoc_join cmd; u8 preamble = RADIO_PREAMBLE_SHORT; int ret = 0; + int hw, i; + u8 rates_max; + u8 *rates; /* TODO: set preamble based on scan result */ ret = lbs_set_radio(priv, preamble, 1); @@ -1775,9 +1782,14 @@ static int lbs_ibss_join_existing(struct lbs_private *priv, if (!rates_eid) { lbs_add_rates(cmd.bss.rates); } else { - int hw, i; - u8 rates_max = rates_eid[1]; - u8 *rates = cmd.bss.rates; + rates_max = rates_eid[1]; + if (rates_max > MAX_RATES) { + lbs_deb_join("invalid rates"); + rcu_read_unlock(); + ret = -EINVAL; + goto out; + } + rates = cmd.bss.rates; for (hw = 0; hw < ARRAY_SIZE(lbs_rates); hw++) { u8 hw_rate = lbs_rates[hw].bitrate / 5; for (i = 0; i < rates_max; i++) { diff --git a/drivers/net/wireless/marvell/libertas_tf/cmd.c b/drivers/net/wireless/marvell/libertas_tf/cmd.c index 909ac3685010f116d160063a5e95ebcb2f34f358..2b193f1257a5ac6271af561573dba23449d26863 100644 --- a/drivers/net/wireless/marvell/libertas_tf/cmd.c +++ b/drivers/net/wireless/marvell/libertas_tf/cmd.c @@ -69,7 +69,7 @@ static void lbtf_geo_init(struct lbtf_private *priv) break; } - for (ch = priv->range.start; ch < priv->range.end; ch++) + for (ch = range->start; ch < range->end; ch++) priv->channels[CHAN_TO_IDX(ch)].flags = 0; } diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h index e39bb5c42c9a540c07ea475d8557cbb7383cf7ff..7e526014b6385895a27d070ee531f82407343a37 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.h +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -1294,19 +1294,6 @@ mwifiex_copy_rates(u8 *dest, u32 pos, u8 *src, int len) return pos; } -/* This function return interface number with the same bss_type. - */ -static inline u8 -mwifiex_get_intf_num(struct mwifiex_adapter *adapter, u8 bss_type) -{ - u8 i, num = 0; - - for (i = 0; i < adapter->priv_num; i++) - if (adapter->priv[i] && adapter->priv[i]->bss_type == bss_type) - num++; - return num; -} - /* * This function returns the correct private structure pointer based * upon the BSS type and BSS number. diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index 918c699365402f94e8bad86be88a03f03dbf47cc..991b9cc18000636edd0c7d3c335011c8070f0767 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -1036,8 +1036,10 @@ static int mwifiex_pcie_alloc_cmdrsp_buf(struct mwifiex_adapter *adapter) } skb_put(skb, MWIFIEX_UPLD_SIZE); if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE, - PCI_DMA_FROMDEVICE)) + PCI_DMA_FROMDEVICE)) { + kfree_skb(skb); return -1; + } card->cmdrsp_buf = skb; diff --git a/drivers/net/wireless/marvell/mwifiex/scan.c b/drivers/net/wireless/marvell/mwifiex/scan.c index dd02bbd9544e7c75ae77c8b46acf7aac941887b1..85d6d5f3dce5b3fa0169900ec6e1f311dc1ff3e7 100644 --- a/drivers/net/wireless/marvell/mwifiex/scan.c +++ b/drivers/net/wireless/marvell/mwifiex/scan.c @@ -2894,6 +2894,13 @@ mwifiex_cmd_append_vsie_tlv(struct mwifiex_private *priv, vs_param_set->header.len = cpu_to_le16((((u16) priv->vs_ie[id].ie[1]) & 0x00FF) + 2); + if (le16_to_cpu(vs_param_set->header.len) > + MWIFIEX_MAX_VSIE_LEN) { + mwifiex_dbg(priv->adapter, ERROR, + "Invalid param length!\n"); + break; + } + memcpy(vs_param_set->ie, priv->vs_ie[id].ie, le16_to_cpu(vs_param_set->header.len)); *buffer += le16_to_cpu(vs_param_set->header.len) + diff --git a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c index 74e50566db1f2711d3c35a3bc31c0f9bdb767b2c..fbfa0b15d0c8dce8893528c0d289d96c71e975c6 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c @@ -229,6 +229,15 @@ static int mwifiex_process_country_ie(struct mwifiex_private *priv, "11D: skip setting domain info in FW\n"); return 0; } + + if (country_ie_len > + (IEEE80211_COUNTRY_STRING_LEN + MWIFIEX_MAX_TRIPLET_802_11D)) { + rcu_read_unlock(); + mwifiex_dbg(priv->adapter, ERROR, + "11D: country_ie_len overflow!, deauth AP\n"); + return -EINVAL; + } + memcpy(priv->adapter->country_code, &country_ie[2], 2); domain_info->country_code[0] = country_ie[2]; @@ -272,8 +281,9 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, priv->scan_block = false; if (bss) { - if (adapter->region_code == 0x00) - mwifiex_process_country_ie(priv, bss); + if (adapter->region_code == 0x00 && + mwifiex_process_country_ie(priv, bss)) + return -EINVAL; /* Allocate and fill new bss descriptor */ bss_desc = kzalloc(sizeof(struct mwifiex_bssdescriptor), diff --git a/drivers/net/wireless/marvell/mwifiex/tdls.c b/drivers/net/wireless/marvell/mwifiex/tdls.c index 6058c48d56dc6f901d346a4af35cac4f6f3f989e..b6b7bbe168ebcb15fb8fda8032e65474dd033818 100644 --- a/drivers/net/wireless/marvell/mwifiex/tdls.c +++ b/drivers/net/wireless/marvell/mwifiex/tdls.c @@ -897,7 +897,7 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, u8 *peer, *pos, *end; u8 i, action, basic; u16 cap = 0; - int ie_len = 0; + int ies_len = 0; if (len < (sizeof(struct ethhdr) + 3)) return; @@ -919,7 +919,7 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, pos = buf + sizeof(struct ethhdr) + 4; /* payload 1+ category 1 + action 1 + dialog 1 */ cap = get_unaligned_le16(pos); - ie_len = len - sizeof(struct ethhdr) - TDLS_REQ_FIX_LEN; + ies_len = len - sizeof(struct ethhdr) - TDLS_REQ_FIX_LEN; pos += 2; break; @@ -929,7 +929,7 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, /* payload 1+ category 1 + action 1 + dialog 1 + status code 2*/ pos = buf + sizeof(struct ethhdr) + 6; cap = get_unaligned_le16(pos); - ie_len = len - sizeof(struct ethhdr) - TDLS_RESP_FIX_LEN; + ies_len = len - sizeof(struct ethhdr) - TDLS_RESP_FIX_LEN; pos += 2; break; @@ -937,7 +937,7 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, if (len < (sizeof(struct ethhdr) + TDLS_CONFIRM_FIX_LEN)) return; pos = buf + sizeof(struct ethhdr) + TDLS_CONFIRM_FIX_LEN; - ie_len = len - sizeof(struct ethhdr) - TDLS_CONFIRM_FIX_LEN; + ies_len = len - sizeof(struct ethhdr) - TDLS_CONFIRM_FIX_LEN; break; default: mwifiex_dbg(priv->adapter, ERROR, "Unknown TDLS frame type.\n"); @@ -950,33 +950,33 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, sta_ptr->tdls_cap.capab = cpu_to_le16(cap); - for (end = pos + ie_len; pos + 1 < end; pos += 2 + pos[1]) { - if (pos + 2 + pos[1] > end) + for (end = pos + ies_len; pos + 1 < end; pos += 2 + pos[1]) { + u8 ie_len = pos[1]; + + if (pos + 2 + ie_len > end) break; switch (*pos) { case WLAN_EID_SUPP_RATES: - if (pos[1] > 32) + if (ie_len > sizeof(sta_ptr->tdls_cap.rates)) return; - sta_ptr->tdls_cap.rates_len = pos[1]; - for (i = 0; i < pos[1]; i++) + sta_ptr->tdls_cap.rates_len = ie_len; + for (i = 0; i < ie_len; i++) sta_ptr->tdls_cap.rates[i] = pos[i + 2]; break; case WLAN_EID_EXT_SUPP_RATES: - if (pos[1] > 32) + if (ie_len > sizeof(sta_ptr->tdls_cap.rates)) return; basic = sta_ptr->tdls_cap.rates_len; - if (pos[1] > 32 - basic) + if (ie_len > sizeof(sta_ptr->tdls_cap.rates) - basic) return; - for (i = 0; i < pos[1]; i++) + for (i = 0; i < ie_len; i++) sta_ptr->tdls_cap.rates[basic + i] = pos[i + 2]; - sta_ptr->tdls_cap.rates_len += pos[1]; + sta_ptr->tdls_cap.rates_len += ie_len; break; case WLAN_EID_HT_CAPABILITY: - if (pos > end - sizeof(struct ieee80211_ht_cap) - 2) - return; - if (pos[1] != sizeof(struct ieee80211_ht_cap)) + if (ie_len != sizeof(struct ieee80211_ht_cap)) return; /* copy the ie's value into ht_capb*/ memcpy((u8 *)&sta_ptr->tdls_cap.ht_capb, pos + 2, @@ -984,59 +984,45 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, sta_ptr->is_11n_enabled = 1; break; case WLAN_EID_HT_OPERATION: - if (pos > end - - sizeof(struct ieee80211_ht_operation) - 2) - return; - if (pos[1] != sizeof(struct ieee80211_ht_operation)) + if (ie_len != sizeof(struct ieee80211_ht_operation)) return; /* copy the ie's value into ht_oper*/ memcpy(&sta_ptr->tdls_cap.ht_oper, pos + 2, sizeof(struct ieee80211_ht_operation)); break; case WLAN_EID_BSS_COEX_2040: - if (pos > end - 3) - return; - if (pos[1] != 1) + if (ie_len != sizeof(pos[2])) return; sta_ptr->tdls_cap.coex_2040 = pos[2]; break; case WLAN_EID_EXT_CAPABILITY: - if (pos > end - sizeof(struct ieee_types_header)) - return; - if (pos[1] < sizeof(struct ieee_types_header)) + if (ie_len < sizeof(struct ieee_types_header)) return; - if (pos[1] > 8) + if (ie_len > 8) return; memcpy((u8 *)&sta_ptr->tdls_cap.extcap, pos, sizeof(struct ieee_types_header) + - min_t(u8, pos[1], 8)); + min_t(u8, ie_len, 8)); break; case WLAN_EID_RSN: - if (pos > end - sizeof(struct ieee_types_header)) + if (ie_len < sizeof(struct ieee_types_header)) return; - if (pos[1] < sizeof(struct ieee_types_header)) - return; - if (pos[1] > IEEE_MAX_IE_SIZE - + if (ie_len > IEEE_MAX_IE_SIZE - sizeof(struct ieee_types_header)) return; memcpy((u8 *)&sta_ptr->tdls_cap.rsn_ie, pos, sizeof(struct ieee_types_header) + - min_t(u8, pos[1], IEEE_MAX_IE_SIZE - + min_t(u8, ie_len, IEEE_MAX_IE_SIZE - sizeof(struct ieee_types_header))); break; case WLAN_EID_QOS_CAPA: - if (pos > end - 3) - return; - if (pos[1] != 1) + if (ie_len != sizeof(pos[2])) return; sta_ptr->tdls_cap.qos_info = pos[2]; break; case WLAN_EID_VHT_OPERATION: if (priv->adapter->is_hw_11ac_capable) { - if (pos > end - - sizeof(struct ieee80211_vht_operation) - 2) - return; - if (pos[1] != + if (ie_len != sizeof(struct ieee80211_vht_operation)) return; /* copy the ie's value into vhtoper*/ @@ -1046,10 +1032,7 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, break; case WLAN_EID_VHT_CAPABILITY: if (priv->adapter->is_hw_11ac_capable) { - if (pos > end - - sizeof(struct ieee80211_vht_cap) - 2) - return; - if (pos[1] != sizeof(struct ieee80211_vht_cap)) + if (ie_len != sizeof(struct ieee80211_vht_cap)) return; /* copy the ie's value into vhtcap*/ memcpy((u8 *)&sta_ptr->tdls_cap.vhtcap, pos + 2, @@ -1059,9 +1042,7 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, break; case WLAN_EID_AID: if (priv->adapter->is_hw_11ac_capable) { - if (pos > end - 4) - return; - if (pos[1] != 2) + if (ie_len != sizeof(u16)) return; sta_ptr->tdls_cap.aid = get_unaligned_le16((pos + 2)); diff --git a/drivers/net/wireless/marvell/mwifiex/wmm.c b/drivers/net/wireless/marvell/mwifiex/wmm.c index 64916ba15df5d3f3937fa2c1aea79aff14c5c91a..429ea2752e6aa6cda8f174615e357c3309a60ec8 100644 --- a/drivers/net/wireless/marvell/mwifiex/wmm.c +++ b/drivers/net/wireless/marvell/mwifiex/wmm.c @@ -977,6 +977,10 @@ int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv, "WMM Parameter Set Count: %d\n", wmm_param_ie->qos_info_bitmap & mask); + if (wmm_param_ie->vend_hdr.len + 2 > + sizeof(struct ieee_types_wmm_parameter)) + break; + memcpy((u8 *) &priv->curr_bss_params.bss_descriptor. wmm_ie, wmm_param_ie, wmm_param_ie->vend_hdr.len + 2); diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c index 8d40e92fb6f270abb64bd3571e05bf538c3b1183..dcf927de65f39430512a4c2ae64a307b8cb5edc6 100644 --- a/drivers/net/wireless/mediatek/mt76/usb.c +++ b/drivers/net/wireless/mediatek/mt76/usb.c @@ -273,10 +273,16 @@ EXPORT_SYMBOL_GPL(mt76u_buf_alloc); void mt76u_buf_free(struct mt76u_buf *buf) { struct urb *urb = buf->urb; + struct scatterlist *sg; int i; - for (i = 0; i < urb->num_sgs; i++) - skb_free_frag(sg_virt(&urb->sg[i])); + for (i = 0; i < urb->num_sgs; i++) { + sg = &urb->sg[i]; + if (!sg) + continue; + + skb_free_frag(sg_virt(sg)); + } usb_free_urb(buf->urb); } EXPORT_SYMBOL_GPL(mt76u_buf_free); @@ -478,7 +484,8 @@ static int mt76u_alloc_rx(struct mt76_dev *dev) nsgs = 1; } - for (i = 0; i < MT_NUM_RX_ENTRIES; i++) { + q->ndesc = MT_NUM_RX_ENTRIES; + for (i = 0; i < q->ndesc; i++) { err = mt76u_buf_alloc(dev, &q->entry[i].ubuf, nsgs, q->buf_size, SKB_WITH_OVERHEAD(q->buf_size), @@ -486,7 +493,6 @@ static int mt76u_alloc_rx(struct mt76_dev *dev) if (err < 0) return err; } - q->ndesc = MT_NUM_RX_ENTRIES; return mt76u_submit_rx_buffers(dev); } diff --git a/drivers/net/wireless/mediatek/mt7601u/phy.c b/drivers/net/wireless/mediatek/mt7601u/phy.c index b804abd464ae06365adbe108a5706412efe53f4b..539f78149e43b6072e90cf70da15d132d55e3cd4 100644 --- a/drivers/net/wireless/mediatek/mt7601u/phy.c +++ b/drivers/net/wireless/mediatek/mt7601u/phy.c @@ -221,7 +221,7 @@ int mt7601u_wait_bbp_ready(struct mt7601u_dev *dev) do { val = mt7601u_bbp_rr(dev, MT_BBP_REG_VERSION); - if (val && ~val) + if (val && val != 0xff) break; } while (--i); diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index 66c6ee70f00aa0c5ba261252258d6d8f039e9b53..070ea0f456abd61c5e2ae4fff5e692f4d3756902 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -5453,6 +5453,7 @@ static int rtl8xxxu_submit_int_urb(struct ieee80211_hw *hw) ret = usb_submit_urb(urb, GFP_KERNEL); if (ret) { usb_unanchor_urb(urb); + usb_free_urb(urb); goto error; } @@ -5920,7 +5921,7 @@ static int rtl8xxxu_parse_usb(struct rtl8xxxu_priv *priv, u8 dir, xtype, num; int ret = 0; - host_interface = &interface->altsetting[0]; + host_interface = interface->cur_altsetting; interface_desc = &host_interface->desc; endpoints = interface_desc->bNumEndpoints; diff --git a/drivers/net/wireless/realtek/rtlwifi/debug.c b/drivers/net/wireless/realtek/rtlwifi/debug.c index d70385be99762e97507b998a1711c150182490f5..498994041bbcbb51af22ee82c4b1071948381e30 100644 --- a/drivers/net/wireless/realtek/rtlwifi/debug.c +++ b/drivers/net/wireless/realtek/rtlwifi/debug.c @@ -109,7 +109,7 @@ static const struct file_operations file_ops_common = { .open = dl_debug_open_common, .read = seq_read, .llseek = seq_lseek, - .release = seq_release, + .release = single_release, }; static int rtl_debug_get_mac_page(struct seq_file *m, void *v) diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c index 5d1fda16fc8c4c966ff8e24ca8d49bd3b6ca38c6..83749578fa8b38266ec14057ee88260b929c8d30 100644 --- a/drivers/net/wireless/realtek/rtlwifi/pci.c +++ b/drivers/net/wireless/realtek/rtlwifi/pci.c @@ -1082,13 +1082,15 @@ static irqreturn_t _rtl_pci_interrupt(int irq, void *dev_id) return ret; } -static void _rtl_pci_irq_tasklet(struct ieee80211_hw *hw) +static void _rtl_pci_irq_tasklet(unsigned long data) { + struct ieee80211_hw *hw = (struct ieee80211_hw *)data; _rtl_pci_tx_chk_waitq(hw); } -static void _rtl_pci_prepare_bcn_tasklet(struct ieee80211_hw *hw) +static void _rtl_pci_prepare_bcn_tasklet(unsigned long data) { + struct ieee80211_hw *hw = (struct ieee80211_hw *)data; struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); @@ -1214,10 +1216,10 @@ static void _rtl_pci_init_struct(struct ieee80211_hw *hw, /*task */ tasklet_init(&rtlpriv->works.irq_tasklet, - (void (*)(unsigned long))_rtl_pci_irq_tasklet, + _rtl_pci_irq_tasklet, (unsigned long)hw); tasklet_init(&rtlpriv->works.irq_prepare_bcn_tasklet, - (void (*)(unsigned long))_rtl_pci_prepare_bcn_tasklet, + _rtl_pci_prepare_bcn_tasklet, (unsigned long)hw); INIT_WORK(&rtlpriv->works.lps_change_work, rtl_lps_change_work_callback); diff --git a/drivers/net/wireless/realtek/rtlwifi/regd.c b/drivers/net/wireless/realtek/rtlwifi/regd.c index 1bf3eb25c1da85a805451e885e51665b85478781..72ca370331fb46ab927467a3fcde6cf495d9b8f5 100644 --- a/drivers/net/wireless/realtek/rtlwifi/regd.c +++ b/drivers/net/wireless/realtek/rtlwifi/regd.c @@ -427,7 +427,7 @@ int rtl_regd_init(struct ieee80211_hw *hw, struct wiphy *wiphy = hw->wiphy; struct country_code_to_enum_rd *country = NULL; - if (wiphy == NULL || &rtlpriv->regd == NULL) + if (!wiphy) return -EINVAL; /* init country_code from efuse channel plan */ diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c index 317c1b3101dad1607bc670e8a3d5f9662754a05a..ba258318ee9f36814365bca8f5436f9eff167aba 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c @@ -3404,75 +3404,6 @@ static void rtl8821ae_update_hal_rate_table(struct ieee80211_hw *hw, "%x\n", rtl_read_dword(rtlpriv, REG_ARFR0)); } -static u8 _rtl8821ae_mrate_idx_to_arfr_id( - struct ieee80211_hw *hw, u8 rate_index, - enum wireless_mode wirelessmode) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_phy *rtlphy = &rtlpriv->phy; - u8 ret = 0; - switch (rate_index) { - case RATR_INX_WIRELESS_NGB: - if (rtlphy->rf_type == RF_1T1R) - ret = 1; - else - ret = 0; - ; break; - case RATR_INX_WIRELESS_N: - case RATR_INX_WIRELESS_NG: - if (rtlphy->rf_type == RF_1T1R) - ret = 5; - else - ret = 4; - ; break; - case RATR_INX_WIRELESS_NB: - if (rtlphy->rf_type == RF_1T1R) - ret = 3; - else - ret = 2; - ; break; - case RATR_INX_WIRELESS_GB: - ret = 6; - break; - case RATR_INX_WIRELESS_G: - ret = 7; - break; - case RATR_INX_WIRELESS_B: - ret = 8; - break; - case RATR_INX_WIRELESS_MC: - if ((wirelessmode == WIRELESS_MODE_B) - || (wirelessmode == WIRELESS_MODE_G) - || (wirelessmode == WIRELESS_MODE_N_24G) - || (wirelessmode == WIRELESS_MODE_AC_24G)) - ret = 6; - else - ret = 7; - case RATR_INX_WIRELESS_AC_5N: - if (rtlphy->rf_type == RF_1T1R) - ret = 10; - else - ret = 9; - break; - case RATR_INX_WIRELESS_AC_24N: - if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_80) { - if (rtlphy->rf_type == RF_1T1R) - ret = 10; - else - ret = 9; - } else { - if (rtlphy->rf_type == RF_1T1R) - ret = 11; - else - ret = 12; - } - break; - default: - ret = 0; break; - } - return ret; -} - static u32 _rtl8821ae_rate_to_bitmap_2ssvht(__le16 vht_rate) { u8 i, j, tmp_rate; @@ -3761,7 +3692,7 @@ static void rtl8821ae_update_hal_rate_mask(struct ieee80211_hw *hw, break; } - ratr_index = _rtl8821ae_mrate_idx_to_arfr_id(hw, ratr_index, wirelessmode); + ratr_index = rtl_mrate_idx_to_arfr_id(hw, ratr_index, wirelessmode); sta_entry->ratr_index = ratr_index; ratr_bitmap = _rtl8821ae_set_ra_vht_ratr_bitmap(hw, wirelessmode, ratr_bitmap); diff --git a/drivers/net/wireless/rsi/rsi_91x_hal.c b/drivers/net/wireless/rsi/rsi_91x_hal.c index 01edf960ff3cc72f5c4d383acd915525f1bbe333..a7b341e95e7640660d319c65d9bd8b8b1f7a0953 100644 --- a/drivers/net/wireless/rsi/rsi_91x_hal.c +++ b/drivers/net/wireless/rsi/rsi_91x_hal.c @@ -616,6 +616,7 @@ static int bl_cmd(struct rsi_hw *adapter, u8 cmd, u8 exp_resp, char *str) bl_start_cmd_timer(adapter, timeout); status = bl_write_cmd(adapter, cmd, exp_resp, ®out_val); if (status < 0) { + bl_stop_cmd_timer(adapter); rsi_dbg(ERR_ZONE, "%s: Command %s (%0x) writing failed..\n", __func__, str, cmd); @@ -731,10 +732,9 @@ static int ping_pong_write(struct rsi_hw *adapter, u8 cmd, u8 *addr, u32 size) } status = bl_cmd(adapter, cmd_req, cmd_resp, str); - if (status) { - bl_stop_cmd_timer(adapter); + if (status) return status; - } + return 0; } @@ -822,10 +822,9 @@ static int auto_fw_upgrade(struct rsi_hw *adapter, u8 *flash_content, status = bl_cmd(adapter, EOF_REACHED, FW_LOADING_SUCCESSFUL, "EOF_REACHED"); - if (status) { - bl_stop_cmd_timer(adapter); + if (status) return status; - } + rsi_dbg(INFO_ZONE, "FW loading is done and FW is running..\n"); return 0; } @@ -846,6 +845,7 @@ static int rsi_load_firmware(struct rsi_hw *adapter) status = hif_ops->master_reg_read(adapter, SWBL_REGOUT, ®out_val, 2); if (status < 0) { + bl_stop_cmd_timer(adapter); rsi_dbg(ERR_ZONE, "%s: REGOUT read failed\n", __func__); return status; diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio.c b/drivers/net/wireless/rsi/rsi_91x_sdio.c index 5733e440ecafff23599bc5e561ca200e9887ee0a..81cc1044532d1ae2407a05af4c7c66e94e2bd92d 100644 --- a/drivers/net/wireless/rsi/rsi_91x_sdio.c +++ b/drivers/net/wireless/rsi/rsi_91x_sdio.c @@ -1129,6 +1129,12 @@ static void rsi_disconnect(struct sdio_func *pfunction) rsi_mac80211_detach(adapter); mdelay(10); + if (IS_ENABLED(CONFIG_RSI_COEX) && adapter->priv->coex_mode > 1 && + adapter->priv->bt_adapter) { + rsi_bt_ops.detach(adapter->priv->bt_adapter); + adapter->priv->bt_adapter = NULL; + } + /* Reset Chip */ rsi_reset_chip(adapter); @@ -1305,6 +1311,12 @@ static int rsi_freeze(struct device *dev) rsi_dbg(ERR_ZONE, "##### Device can not wake up through WLAN\n"); + if (IS_ENABLED(CONFIG_RSI_COEX) && common->coex_mode > 1 && + common->bt_adapter) { + rsi_bt_ops.detach(common->bt_adapter); + common->bt_adapter = NULL; + } + ret = rsi_sdio_disable_interrupts(pfunction); if (sdev->write_fail) @@ -1352,6 +1364,12 @@ static void rsi_shutdown(struct device *dev) if (rsi_config_wowlan(adapter, wowlan)) rsi_dbg(ERR_ZONE, "Failed to configure WoWLAN\n"); + if (IS_ENABLED(CONFIG_RSI_COEX) && adapter->priv->coex_mode > 1 && + adapter->priv->bt_adapter) { + rsi_bt_ops.detach(adapter->priv->bt_adapter); + adapter->priv->bt_adapter = NULL; + } + rsi_sdio_disable_interrupts(sdev->pfunction); if (sdev->write_fail) diff --git a/drivers/net/wireless/rsi/rsi_91x_usb.c b/drivers/net/wireless/rsi/rsi_91x_usb.c index 14e56bee05484c33bc88db5f9ecfdf533684487a..54106646445a9be1653d86a3879608dea44641ce 100644 --- a/drivers/net/wireless/rsi/rsi_91x_usb.c +++ b/drivers/net/wireless/rsi/rsi_91x_usb.c @@ -16,6 +16,7 @@ */ #include +#include #include #include "rsi_usb.h" #include "rsi_hal.h" @@ -29,7 +30,7 @@ MODULE_PARM_DESC(dev_oper_mode, "9[Wi-Fi STA + BT LE], 13[Wi-Fi STA + BT classic + BT LE]\n" "6[AP + BT classic], 14[AP + BT classic + BT LE]"); -static int rsi_rx_urb_submit(struct rsi_hw *adapter, u8 ep_num); +static int rsi_rx_urb_submit(struct rsi_hw *adapter, u8 ep_num, gfp_t flags); /** * rsi_usb_card_write() - This function writes to the USB Card. @@ -117,7 +118,7 @@ static int rsi_find_bulk_in_and_out_endpoints(struct usb_interface *interface, __le16 buffer_size; int ii, bin_found = 0, bout_found = 0; - iface_desc = &(interface->altsetting[0]); + iface_desc = interface->cur_altsetting; for (ii = 0; ii < iface_desc->desc.bNumEndpoints; ++ii) { endpoint = &(iface_desc->endpoint[ii].desc); @@ -283,20 +284,29 @@ static void rsi_rx_done_handler(struct urb *urb) status = 0; out: - if (rsi_rx_urb_submit(dev->priv, rx_cb->ep_num)) + if (rsi_rx_urb_submit(dev->priv, rx_cb->ep_num, GFP_ATOMIC)) rsi_dbg(ERR_ZONE, "%s: Failed in urb submission", __func__); if (status) dev_kfree_skb(rx_cb->rx_skb); } +static void rsi_rx_urb_kill(struct rsi_hw *adapter, u8 ep_num) +{ + struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; + struct rx_usb_ctrl_block *rx_cb = &dev->rx_cb[ep_num - 1]; + struct urb *urb = rx_cb->rx_urb; + + usb_kill_urb(urb); +} + /** * rsi_rx_urb_submit() - This function submits the given URB to the USB stack. * @adapter: Pointer to the adapter structure. * * Return: 0 on success, a negative error code on failure. */ -static int rsi_rx_urb_submit(struct rsi_hw *adapter, u8 ep_num) +static int rsi_rx_urb_submit(struct rsi_hw *adapter, u8 ep_num, gfp_t mem_flags) { struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; struct rx_usb_ctrl_block *rx_cb = &dev->rx_cb[ep_num - 1]; @@ -326,9 +336,11 @@ static int rsi_rx_urb_submit(struct rsi_hw *adapter, u8 ep_num) rsi_rx_done_handler, rx_cb); - status = usb_submit_urb(urb, GFP_KERNEL); - if (status) + status = usb_submit_urb(urb, mem_flags); + if (status) { rsi_dbg(ERR_ZONE, "%s: Failed in urb submission\n", __func__); + dev_kfree_skb(skb); + } return status; } @@ -781,17 +793,20 @@ static int rsi_probe(struct usb_interface *pfunction, rsi_dbg(INIT_ZONE, "%s: Device Init Done\n", __func__); } - status = rsi_rx_urb_submit(adapter, WLAN_EP); + status = rsi_rx_urb_submit(adapter, WLAN_EP, GFP_KERNEL); if (status) goto err1; if (adapter->priv->coex_mode > 1) { - status = rsi_rx_urb_submit(adapter, BT_EP); + status = rsi_rx_urb_submit(adapter, BT_EP, GFP_KERNEL); if (status) - goto err1; + goto err_kill_wlan_urb; } return 0; + +err_kill_wlan_urb: + rsi_rx_urb_kill(adapter, WLAN_EP); err1: rsi_deinit_usb_interface(adapter); err: @@ -815,6 +830,17 @@ static void rsi_disconnect(struct usb_interface *pfunction) return; rsi_mac80211_detach(adapter); + + if (IS_ENABLED(CONFIG_RSI_COEX) && adapter->priv->coex_mode > 1 && + adapter->priv->bt_adapter) { + rsi_bt_ops.detach(adapter->priv->bt_adapter); + adapter->priv->bt_adapter = NULL; + } + + if (adapter->priv->coex_mode > 1) + rsi_rx_urb_kill(adapter, BT_EP); + rsi_rx_urb_kill(adapter, WLAN_EP); + rsi_reset_card(adapter); rsi_deinit_usb_interface(adapter); rsi_91x_deinit(adapter); diff --git a/drivers/net/wireless/st/cw1200/fwio.c b/drivers/net/wireless/st/cw1200/fwio.c index 30e7646d04affac71a3f3a1ad65702883fef5bef..16be7fa82a233bee067fcf8cbec42bd39156106c 100644 --- a/drivers/net/wireless/st/cw1200/fwio.c +++ b/drivers/net/wireless/st/cw1200/fwio.c @@ -323,12 +323,12 @@ int cw1200_load_firmware(struct cw1200_common *priv) goto out; } - priv->hw_type = cw1200_get_hw_type(val32, &major_revision); - if (priv->hw_type < 0) { + ret = cw1200_get_hw_type(val32, &major_revision); + if (ret < 0) { pr_err("Can't deduce hardware type.\n"); - ret = -ENOTSUPP; goto out; } + priv->hw_type = ret; /* Set DPLL Reg value, and read back to confirm writes work */ ret = cw1200_reg_write_32(priv, ST90TDS_TSET_GEN_R_W_REG_ID, diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_usb.c b/drivers/net/wireless/zydas/zd1211rw/zd_usb.c index c2cda3acd4af37a2ca4130283010e7faba8d3af1..0fddfb464e9c5404877c89903dda519339ad0763 100644 --- a/drivers/net/wireless/zydas/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zydas/zd1211rw/zd_usb.c @@ -1275,7 +1275,7 @@ static void print_id(struct usb_device *udev) static int eject_installer(struct usb_interface *intf) { struct usb_device *udev = interface_to_usbdev(intf); - struct usb_host_interface *iface_desc = &intf->altsetting[0]; + struct usb_host_interface *iface_desc = intf->cur_altsetting; struct usb_endpoint_descriptor *endpoint; unsigned char *cmd; u8 bulk_out_ep; diff --git a/drivers/nfc/pn533/usb.c b/drivers/nfc/pn533/usb.c index fcb57d64d97e60d37dbc30364a4fc5d222f4d1a4..a2c9b3f3bc2322a7504cdcdb96ba8bf144fcc4ec 100644 --- a/drivers/nfc/pn533/usb.c +++ b/drivers/nfc/pn533/usb.c @@ -403,7 +403,7 @@ static int pn533_acr122_poweron_rdr(struct pn533_usb_phy *phy) cmd, sizeof(cmd), false); rc = usb_bulk_msg(phy->udev, phy->out_urb->pipe, buffer, sizeof(cmd), - &transferred, 0); + &transferred, 5000); kfree(buffer); if (rc || (transferred != sizeof(cmd))) { nfc_err(&phy->udev->dev, diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c index d0207f8e68b7c8760d3f93c8d54a5295ae65f432..dcef73eb412068aae04fe817192be7a5a8c979b4 100644 --- a/drivers/nfc/pn544/i2c.c +++ b/drivers/nfc/pn544/i2c.c @@ -236,6 +236,7 @@ static void pn544_hci_i2c_platform_init(struct pn544_i2c_phy *phy) out: gpiod_set_value_cansleep(phy->gpiod_en, !phy->en_polarity); + usleep_range(10000, 15000); } static void pn544_hci_i2c_enable_mode(struct pn544_i2c_phy *phy, int run_mode) diff --git a/drivers/nfc/pn544/pn544.c b/drivers/nfc/pn544/pn544.c index 70e898e38b16189ea3ab0d6233e09209b29966f9..f30bdf95610f4b5d06fdff099c42b332cc1b6794 100644 --- a/drivers/nfc/pn544/pn544.c +++ b/drivers/nfc/pn544/pn544.c @@ -704,7 +704,7 @@ static int pn544_hci_check_presence(struct nfc_hci_dev *hdev, target->nfcid1_len != 10) return -EOPNOTSUPP; - return nfc_hci_send_cmd(hdev, NFC_HCI_RF_READER_A_GATE, + return nfc_hci_send_cmd(hdev, NFC_HCI_RF_READER_A_GATE, PN544_RF_READER_CMD_ACTIVATE_NEXT, target->nfcid1, target->nfcid1_len, NULL); } else if (target->supported_protocols & (NFC_PROTO_JEWEL_MASK | diff --git a/drivers/nfc/port100.c b/drivers/nfc/port100.c index 60ae382f50da9887ad900a14807835490eb6bee3..06bb226c62ef46e734d5d10887b0dbc38ce8ff7e 100644 --- a/drivers/nfc/port100.c +++ b/drivers/nfc/port100.c @@ -574,7 +574,7 @@ static void port100_tx_update_payload_len(void *_frame, int len) { struct port100_frame *frame = _frame; - frame->datalen = cpu_to_le16(le16_to_cpu(frame->datalen) + len); + le16_add_cpu(&frame->datalen, len); } static bool port100_rx_frame_is_valid(void *_frame) diff --git a/drivers/ntb/hw/idt/ntb_hw_idt.c b/drivers/ntb/hw/idt/ntb_hw_idt.c index dbe72f116017ab305a0a1f19276155700f69211e..a67ef23e81bca70127edb92afcb78662ad3dc0e3 100644 --- a/drivers/ntb/hw/idt/ntb_hw_idt.c +++ b/drivers/ntb/hw/idt/ntb_hw_idt.c @@ -1105,9 +1105,9 @@ static struct idt_mw_cfg *idt_scan_mws(struct idt_ntb_dev *ndev, int port, } /* Allocate memory for memory window descriptors */ - ret_mws = devm_kcalloc(&ndev->ntb.pdev->dev, *mw_cnt, - sizeof(*ret_mws), GFP_KERNEL); - if (IS_ERR_OR_NULL(ret_mws)) + ret_mws = devm_kcalloc(&ndev->ntb.pdev->dev, *mw_cnt, sizeof(*ret_mws), + GFP_KERNEL); + if (!ret_mws) return ERR_PTR(-ENOMEM); /* Copy the info of detected memory windows */ @@ -2390,7 +2390,7 @@ static struct idt_ntb_dev *idt_create_dev(struct pci_dev *pdev, /* Allocate memory for the IDT PCIe-device descriptor */ ndev = devm_kzalloc(&pdev->dev, sizeof(*ndev), GFP_KERNEL); - if (IS_ERR_OR_NULL(ndev)) { + if (!ndev) { dev_err(&pdev->dev, "Memory allocation failed for descriptor"); return ERR_PTR(-ENOMEM); } diff --git a/drivers/ntb/hw/mscc/ntb_hw_switchtec.c b/drivers/ntb/hw/mscc/ntb_hw_switchtec.c index 5ee5f40b4dfc3fba0ca2c3c8ddf824f4cd5b0f05..313f6258c4249ef94e57756e5562df090fe0e35a 100644 --- a/drivers/ntb/hw/mscc/ntb_hw_switchtec.c +++ b/drivers/ntb/hw/mscc/ntb_hw_switchtec.c @@ -899,7 +899,7 @@ static int switchtec_ntb_init_sndev(struct switchtec_ntb *sndev) } sndev->peer_partition = ffs(tpart_vec) - 1; - if (!(part_map & (1 << sndev->peer_partition))) { + if (!(part_map & (1ULL << sndev->peer_partition))) { dev_err(&sndev->stdev->dev, "ntb target partition is not NT partition\n"); return -ENODEV; @@ -1120,7 +1120,7 @@ static int crosslink_enum_partition(struct switchtec_ntb *sndev, dev_dbg(&sndev->stdev->dev, "Crosslink BAR%d addr: %llx\n", - i, bar_addr); + i*2, bar_addr); if (bar_addr != bar_space * i) continue; diff --git a/drivers/nvdimm/Makefile b/drivers/nvdimm/Makefile index e8847045dac006e7fe884f6306bbc40c16a5512b..5816072010d02d9be1ba1ac957d79a784edf3ace 100644 --- a/drivers/nvdimm/Makefile +++ b/drivers/nvdimm/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_ND_BTT) += nd_btt.o obj-$(CONFIG_ND_BLK) += nd_blk.o obj-$(CONFIG_X86_PMEM_LEGACY) += nd_e820.o obj-$(CONFIG_OF_PMEM) += of_pmem.o +obj-$(CONFIG_VIRTIO_PMEM) += virtio_pmem.o nd_virtio.o nd_pmem-y := pmem.o diff --git a/drivers/nvdimm/claim.c b/drivers/nvdimm/claim.c index fb667bf469c7e980411c2836d4a9b97d1f96a53d..13510bae1e6fb13de366a78fedea8855656e1902 100644 --- a/drivers/nvdimm/claim.c +++ b/drivers/nvdimm/claim.c @@ -263,7 +263,7 @@ static int nsio_rw_bytes(struct nd_namespace_common *ndns, struct nd_namespace_io *nsio = to_nd_namespace_io(&ndns->dev); unsigned int sz_align = ALIGN(size + (offset & (512 - 1)), 512); sector_t sector = offset >> 9; - int rc = 0; + int rc = 0, ret = 0; if (unlikely(!size)) return 0; @@ -301,7 +301,9 @@ static int nsio_rw_bytes(struct nd_namespace_common *ndns, } memcpy_flushcache(nsio->addr + offset, buf, size); - nvdimm_flush(to_nd_region(ndns->dev.parent)); + ret = nvdimm_flush(to_nd_region(ndns->dev.parent), NULL); + if (ret) + rc = ret; return rc; } diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h index 01e194a5824e43c06ba4cae77c2aff5ab29ca739..fbb01a7bac8b165661cc17592d55c10916ec3b05 100644 --- a/drivers/nvdimm/nd.h +++ b/drivers/nvdimm/nd.h @@ -163,6 +163,7 @@ struct nd_region { struct badblocks bb; struct nd_interleave_set *nd_set; struct nd_percpu_lane __percpu *lane; + int (*flush)(struct nd_region *nd_region, struct bio *bio); struct nd_mapping mapping[0]; }; diff --git a/drivers/nvdimm/nd_virtio.c b/drivers/nvdimm/nd_virtio.c new file mode 100644 index 0000000000000000000000000000000000000000..8645275c08c21cd2ad93888dd129fa1f2f8ae754 --- /dev/null +++ b/drivers/nvdimm/nd_virtio.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * virtio_pmem.c: Virtio pmem Driver + * + * Discovers persistent memory range information + * from host and provides a virtio based flushing + * interface. + */ +#include "virtio_pmem.h" +#include "nd.h" + + /* The interrupt handler */ +void virtio_pmem_host_ack(struct virtqueue *vq) +{ + struct virtio_pmem *vpmem = vq->vdev->priv; + struct virtio_pmem_request *req_data, *req_buf; + unsigned long flags; + unsigned int len; + + spin_lock_irqsave(&vpmem->pmem_lock, flags); + while ((req_data = virtqueue_get_buf(vq, &len)) != NULL) { + req_data->done = true; + wake_up(&req_data->host_acked); + + if (!list_empty(&vpmem->req_list)) { + req_buf = list_first_entry(&vpmem->req_list, + struct virtio_pmem_request, list); + req_buf->wq_buf_avail = true; + wake_up(&req_buf->wq_buf); + list_del(&req_buf->list); + } + } + spin_unlock_irqrestore(&vpmem->pmem_lock, flags); +} +EXPORT_SYMBOL_GPL(virtio_pmem_host_ack); + + /* The request submission function */ +static int virtio_pmem_flush(struct nd_region *nd_region) +{ + struct virtio_device *vdev = nd_region->provider_data; + struct virtio_pmem *vpmem = vdev->priv; + struct virtio_pmem_request *req_data; + struct scatterlist *sgs[2], sg, ret; + unsigned long flags; + int err, err1; + + might_sleep(); + req_data = kmalloc(sizeof(*req_data), GFP_KERNEL); + if (!req_data) + return -ENOMEM; + + req_data->done = false; + init_waitqueue_head(&req_data->host_acked); + init_waitqueue_head(&req_data->wq_buf); + INIT_LIST_HEAD(&req_data->list); + req_data->req.type = cpu_to_virtio32(vdev, VIRTIO_PMEM_REQ_TYPE_FLUSH); + sg_init_one(&sg, &req_data->req, sizeof(req_data->req)); + sgs[0] = &sg; + sg_init_one(&ret, &req_data->resp.ret, sizeof(req_data->resp)); + sgs[1] = &ret; + + spin_lock_irqsave(&vpmem->pmem_lock, flags); + /* + * If virtqueue_add_sgs returns -ENOSPC then req_vq virtual + * queue does not have free descriptor. We add the request + * to req_list and wait for host_ack to wake us up when free + * slots are available. + */ + while ((err = virtqueue_add_sgs(vpmem->req_vq, sgs, 1, 1, req_data, + GFP_ATOMIC)) == -ENOSPC) { + + dev_info(&vdev->dev, "failed to send command to virtio pmem device, no free slots in the virtqueue\n"); + req_data->wq_buf_avail = false; + list_add_tail(&req_data->list, &vpmem->req_list); + spin_unlock_irqrestore(&vpmem->pmem_lock, flags); + + /* A host response results in "host_ack" getting called */ + wait_event(req_data->wq_buf, req_data->wq_buf_avail); + spin_lock_irqsave(&vpmem->pmem_lock, flags); + } + err1 = virtqueue_kick(vpmem->req_vq); + spin_unlock_irqrestore(&vpmem->pmem_lock, flags); + /* + * virtqueue_add_sgs failed with error different than -ENOSPC, we can't + * do anything about that. + */ + if (err || !err1) { + dev_info(&vdev->dev, "failed to send command to virtio pmem device\n"); + err = -EIO; + } else { + /* A host repsonse results in "host_ack" getting called */ + wait_event(req_data->host_acked, req_data->done); + err = virtio32_to_cpu(vdev, req_data->resp.ret); + } + + kfree(req_data); + return err; +}; + +/* The asynchronous flush callback function */ +int async_pmem_flush(struct nd_region *nd_region, struct bio *bio) +{ + /* + * Create child bio for asynchronous flush and chain with + * parent bio. Otherwise directly call nd_region flush. + */ + if (bio && bio->bi_iter.bi_sector != -1) { + struct bio *child = bio_alloc(GFP_ATOMIC, 0); + + if (!child) + return -ENOMEM; + bio_copy_dev(child, bio); + child->bi_opf = REQ_PREFLUSH; + child->bi_iter.bi_sector = -1; + bio_chain(child, bio); + submit_bio(child); + return 0; + } + if (virtio_pmem_flush(nd_region)) + return -EIO; + + return 0; +}; +EXPORT_SYMBOL_GPL(async_pmem_flush); +MODULE_LICENSE("GPL"); diff --git a/drivers/nvdimm/of_pmem.c b/drivers/nvdimm/of_pmem.c index 0a701837dfc0b9bd6c011f5ee092feb101a15acb..6e5cbfd8cb13fb179a613d7afa0035a1587b1b57 100644 --- a/drivers/nvdimm/of_pmem.c +++ b/drivers/nvdimm/of_pmem.c @@ -42,7 +42,7 @@ static int of_pmem_region_probe(struct platform_device *pdev) return -ENOMEM; priv->bus_desc.attr_groups = bus_attr_groups; - priv->bus_desc.provider_name = "of_pmem"; + priv->bus_desc.provider_name = kstrdup(pdev->name, GFP_KERNEL); priv->bus_desc.module = THIS_MODULE; priv->bus_desc.of_node = np; @@ -108,7 +108,6 @@ static struct platform_driver of_pmem_region_driver = { .remove = of_pmem_region_remove, .driver = { .name = "of_pmem", - .owner = THIS_MODULE, .of_match_table = of_pmem_region_match, }, }; diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c index a7ce2f1761a2c5d5b965e2944aa8cd79a44412f0..18e10421e7d75b4fbfd3dd9f1c80821665e209be 100644 --- a/drivers/nvdimm/pmem.c +++ b/drivers/nvdimm/pmem.c @@ -192,6 +192,7 @@ static blk_status_t pmem_do_bvec(struct pmem_device *pmem, struct page *page, static blk_qc_t pmem_make_request(struct request_queue *q, struct bio *bio) { + int ret = 0; blk_status_t rc = 0; bool do_acct; unsigned long start; @@ -201,7 +202,7 @@ static blk_qc_t pmem_make_request(struct request_queue *q, struct bio *bio) struct nd_region *nd_region = to_region(pmem); if (bio->bi_opf & REQ_PREFLUSH) - nvdimm_flush(nd_region); + ret = nvdimm_flush(nd_region, bio); do_acct = nd_iostat_start(bio, &start); bio_for_each_segment(bvec, bio, iter) { @@ -216,7 +217,10 @@ static blk_qc_t pmem_make_request(struct request_queue *q, struct bio *bio) nd_iostat_end(bio, start); if (bio->bi_opf & REQ_FUA) - nvdimm_flush(nd_region); + ret = nvdimm_flush(nd_region, bio); + + if (ret) + bio->bi_status = errno_to_blk_status(ret); bio_endio(bio); return BLK_QC_T_NONE; @@ -475,7 +479,6 @@ static int pmem_attach_disk(struct device *dev, } dax_write_cache(dax_dev, nvdimm_has_cache(nd_region)); pmem->dax_dev = dax_dev; - gendev = disk_to_dev(disk); gendev->groups = pmem_attribute_groups; @@ -533,14 +536,14 @@ static int nd_pmem_remove(struct device *dev) sysfs_put(pmem->bb_state); pmem->bb_state = NULL; } - nvdimm_flush(to_nd_region(dev->parent)); + nvdimm_flush(to_nd_region(dev->parent), NULL); return 0; } static void nd_pmem_shutdown(struct device *dev) { - nvdimm_flush(to_nd_region(dev->parent)); + nvdimm_flush(to_nd_region(dev->parent), NULL); } static void nd_pmem_notify(struct device *dev, enum nvdimm_event event) diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c index 609fc450522a14979b5ab3b608ce8d3adb6647d8..71cfdc3d5524ac271eb10bb2c7b3ebe82fcbe2f6 100644 --- a/drivers/nvdimm/region_devs.c +++ b/drivers/nvdimm/region_devs.c @@ -290,7 +290,9 @@ static ssize_t deep_flush_store(struct device *dev, struct device_attribute *att return rc; if (!flush) return -EINVAL; - nvdimm_flush(nd_region); + rc = nvdimm_flush(nd_region, NULL); + if (rc) + return rc; return len; } @@ -1076,6 +1078,11 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus, dev->of_node = ndr_desc->of_node; nd_region->ndr_size = resource_size(ndr_desc->res); nd_region->ndr_start = ndr_desc->res->start; + if (ndr_desc->flush) + nd_region->flush = ndr_desc->flush; + else + nd_region->flush = NULL; + nd_device_register(dev); return nd_region; @@ -1116,11 +1123,24 @@ struct nd_region *nvdimm_volatile_region_create(struct nvdimm_bus *nvdimm_bus, } EXPORT_SYMBOL_GPL(nvdimm_volatile_region_create); +int nvdimm_flush(struct nd_region *nd_region, struct bio *bio) +{ + int rc = 0; + + if (!nd_region->flush) + rc = generic_nvdimm_flush(nd_region); + else { + if (nd_region->flush(nd_region, bio)) + rc = -EIO; + } + + return rc; +} /** * nvdimm_flush - flush any posted write queues between the cpu and pmem media * @nd_region: blk or interleaved pmem region */ -void nvdimm_flush(struct nd_region *nd_region) +int generic_nvdimm_flush(struct nd_region *nd_region) { struct nd_region_data *ndrd = dev_get_drvdata(&nd_region->dev); int i, idx; @@ -1144,6 +1164,8 @@ void nvdimm_flush(struct nd_region *nd_region) if (ndrd_get_flush_wpq(ndrd, i, 0)) writeq(1, ndrd_get_flush_wpq(ndrd, i, idx)); wmb(); + + return 0; } EXPORT_SYMBOL_GPL(nvdimm_flush); diff --git a/drivers/nvdimm/virtio_pmem.c b/drivers/nvdimm/virtio_pmem.c new file mode 100644 index 0000000000000000000000000000000000000000..5e3d07b47e0cad7881d9b1ac14447770e8b695c3 --- /dev/null +++ b/drivers/nvdimm/virtio_pmem.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * virtio_pmem.c: Virtio pmem Driver + * + * Discovers persistent memory range information + * from host and registers the virtual pmem device + * with libnvdimm core. + */ +#include "virtio_pmem.h" +#include "nd.h" + +static struct virtio_device_id id_table[] = { + { VIRTIO_ID_PMEM, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + + /* Initialize virt queue */ +static int init_vq(struct virtio_pmem *vpmem) +{ + /* single vq */ + vpmem->req_vq = virtio_find_single_vq(vpmem->vdev, + virtio_pmem_host_ack, "flush_queue"); + if (IS_ERR(vpmem->req_vq)) + return PTR_ERR(vpmem->req_vq); + + spin_lock_init(&vpmem->pmem_lock); + INIT_LIST_HEAD(&vpmem->req_list); + + return 0; +}; + +static int virtio_pmem_probe(struct virtio_device *vdev) +{ + struct nd_region_desc ndr_desc = {}; + int nid = dev_to_node(&vdev->dev); + struct nd_region *nd_region; + struct virtio_pmem *vpmem; + struct resource res; + int err = 0; + + if (!vdev->config->get) { + dev_err(&vdev->dev, "%s failure: config access disabled\n", + __func__); + return -EINVAL; + } + + vpmem = devm_kzalloc(&vdev->dev, sizeof(*vpmem), GFP_KERNEL); + if (!vpmem) { + err = -ENOMEM; + goto out_err; + } + + vpmem->vdev = vdev; + vdev->priv = vpmem; + err = init_vq(vpmem); + if (err) { + dev_err(&vdev->dev, "failed to initialize virtio pmem vq's\n"); + goto out_err; + } + + virtio_cread(vpmem->vdev, struct virtio_pmem_config, + start, &vpmem->start); + virtio_cread(vpmem->vdev, struct virtio_pmem_config, + size, &vpmem->size); + + res.start = vpmem->start; + res.end = vpmem->start + vpmem->size - 1; + vpmem->nd_desc.provider_name = "virtio-pmem"; + vpmem->nd_desc.module = THIS_MODULE; + + vpmem->nvdimm_bus = nvdimm_bus_register(&vdev->dev, + &vpmem->nd_desc); + if (!vpmem->nvdimm_bus) { + dev_err(&vdev->dev, "failed to register device with nvdimm_bus\n"); + err = -ENXIO; + goto out_vq; + } + + dev_set_drvdata(&vdev->dev, vpmem->nvdimm_bus); + + ndr_desc.res = &res; + ndr_desc.numa_node = nid; + ndr_desc.flush = async_pmem_flush; + set_bit(ND_REGION_PAGEMAP, &ndr_desc.flags); + set_bit(ND_REGION_ASYNC, &ndr_desc.flags); + nd_region = nvdimm_pmem_region_create(vpmem->nvdimm_bus, &ndr_desc); + if (!nd_region) { + dev_err(&vdev->dev, "failed to create nvdimm region\n"); + err = -ENXIO; + goto out_nd; + } + nd_region->provider_data = dev_to_virtio(nd_region->dev.parent->parent); + return 0; +out_nd: + nvdimm_bus_unregister(vpmem->nvdimm_bus); +out_vq: + vdev->config->del_vqs(vdev); +out_err: + return err; +} + +static void virtio_pmem_remove(struct virtio_device *vdev) +{ + struct nvdimm_bus *nvdimm_bus = dev_get_drvdata(&vdev->dev); + + nvdimm_bus_unregister(nvdimm_bus); + vdev->config->del_vqs(vdev); + vdev->config->reset(vdev); +} + +static struct virtio_driver virtio_pmem_driver = { + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .id_table = id_table, + .probe = virtio_pmem_probe, + .remove = virtio_pmem_remove, +}; + +module_virtio_driver(virtio_pmem_driver); +MODULE_DEVICE_TABLE(virtio, id_table); +MODULE_DESCRIPTION("Virtio pmem driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/nvdimm/virtio_pmem.h b/drivers/nvdimm/virtio_pmem.h new file mode 100644 index 0000000000000000000000000000000000000000..0dddefe594c46ad7e0c0557f73ffcdc20bbe9c1c --- /dev/null +++ b/drivers/nvdimm/virtio_pmem.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * virtio_pmem.h: virtio pmem Driver + * + * Discovers persistent memory range information + * from host and provides a virtio based flushing + * interface. + **/ + +#ifndef _LINUX_VIRTIO_PMEM_H +#define _LINUX_VIRTIO_PMEM_H + +#include +#include +#include +#include + +struct virtio_pmem_request { + struct virtio_pmem_req req; + struct virtio_pmem_resp resp; + + /* Wait queue to process deferred work after ack from host */ + wait_queue_head_t host_acked; + bool done; + + /* Wait queue to process deferred work after virt queue buffer avail */ + wait_queue_head_t wq_buf; + bool wq_buf_avail; + struct list_head list; +}; + +struct virtio_pmem { + struct virtio_device *vdev; + + /* Virtio pmem request queue */ + struct virtqueue *req_vq; + + /* nvdimm bus registers virtio pmem device */ + struct nvdimm_bus *nvdimm_bus; + struct nvdimm_bus_descriptor nd_desc; + + /* List to store deferred work if virtqueue is full */ + struct list_head req_list; + + /* Synchronize virtqueue data */ + spinlock_t pmem_lock; + + /* Memory region information */ + __u64 start; + __u64 size; +}; + +void virtio_pmem_host_ack(struct virtqueue *vq); +int async_pmem_flush(struct nd_region *nd_region, struct bio *bio); +#endif diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index b7bd89b3b2f9097ced2e9b7e5aad2752858a7dc3..a8132e8d72bb473352fbbdeec90094425be5edbd 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -1058,8 +1058,8 @@ static struct nvme_id_ns *nvme_identify_ns(struct nvme_ctrl *ctrl, static int nvme_set_features(struct nvme_ctrl *dev, unsigned fid, unsigned dword11, void *buffer, size_t buflen, u32 *result) { + union nvme_result res = { 0 }; struct nvme_command c; - union nvme_result res; int ret; memset(&c, 0, sizeof(c)); @@ -3449,7 +3449,7 @@ static void nvme_get_fw_slot_info(struct nvme_ctrl *ctrl) if (!log) return; - if (nvme_get_log(ctrl, NVME_NSID_ALL, 0, NVME_LOG_FW_SLOT, log, + if (nvme_get_log(ctrl, NVME_NSID_ALL, NVME_LOG_FW_SLOT, 0, log, sizeof(*log), 0)) dev_warn(ctrl->device, "Get FW SLOT INFO log error\n"); kfree(log); diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c index 838ee58d80cda8c5404e394a00ff7ea195b55a42..e8bc25aed44ca6653eb0ccc53345dd65d410bcba 100644 --- a/drivers/nvme/host/multipath.c +++ b/drivers/nvme/host/multipath.c @@ -569,6 +569,7 @@ int nvme_mpath_init(struct nvme_ctrl *ctrl, struct nvme_id_ctrl *id) } INIT_WORK(&ctrl->ana_work, nvme_ana_work); + kfree(ctrl->ana_log_buf); ctrl->ana_log_buf = kmalloc(ctrl->ana_log_size, GFP_KERNEL); if (!ctrl->ana_log_buf) { error = -ENOMEM; diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index 124f41157173ea0c4bc21f90702e877c0b4a6ae5..3c68a5b35ec1b3d4147177ecb165f0545ca0c364 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -2396,7 +2396,7 @@ static int nvme_pci_reg_write32(struct nvme_ctrl *ctrl, u32 off, u32 val) static int nvme_pci_reg_read64(struct nvme_ctrl *ctrl, u32 off, u64 *val) { - *val = readq(to_nvme_dev(ctrl)->bar + off); + *val = lo_hi_readq(to_nvme_dev(ctrl)->bar + off); return 0; } diff --git a/drivers/nvmem/imx-ocotp.c b/drivers/nvmem/imx-ocotp.c index 926d9cc080cf4db3112e6614fcc584a9f581adb5..09281aca86c29e181fa35d570869b11c9051b196 100644 --- a/drivers/nvmem/imx-ocotp.c +++ b/drivers/nvmem/imx-ocotp.c @@ -50,7 +50,9 @@ #define IMX_OCOTP_BM_CTRL_ERROR 0x00000200 #define IMX_OCOTP_BM_CTRL_REL_SHADOWS 0x00000400 -#define DEF_RELAX 20 /* > 16.5ns */ +#define TIMING_STROBE_PROG_US 10 /* Min time to blow a fuse */ +#define TIMING_STROBE_READ_NS 37 /* Min time before read */ +#define TIMING_RELAX_NS 17 #define DEF_FSOURCE 1001 /* > 1000 ns */ #define DEF_STROBE_PROG 10000 /* IPG clocks */ #define IMX_OCOTP_WR_UNLOCK 0x3E770000 @@ -182,14 +184,41 @@ static void imx_ocotp_set_imx6_timing(struct ocotp_priv *priv) * fields with timing values to match the current frequency of the * ipg_clk. OTP writes will work at maximum bus frequencies as long * as the HW_OCOTP_TIMING parameters are set correctly. + * + * Note: there are minimum timings required to ensure an OTP fuse burns + * correctly that are independent of the ipg_clk. Those values are not + * formally documented anywhere however, working from the minimum + * timings given in u-boot we can say: + * + * - Minimum STROBE_PROG time is 10 microseconds. Intuitively 10 + * microseconds feels about right as representative of a minimum time + * to physically burn out a fuse. + * + * - Minimum STROBE_READ i.e. the time to wait post OTP fuse burn before + * performing another read is 37 nanoseconds + * + * - Minimum RELAX timing is 17 nanoseconds. This final RELAX minimum + * timing is not entirely clear the documentation says "This + * count value specifies the time to add to all default timing + * parameters other than the Tpgm and Trd. It is given in number + * of ipg_clk periods." where Tpgm and Trd refer to STROBE_PROG + * and STROBE_READ respectively. What the other timing parameters + * are though, is not specified. Experience shows a zero RELAX + * value will mess up a re-load of the shadow registers post OTP + * burn. */ clk_rate = clk_get_rate(priv->clk); - relax = clk_rate / (1000000000 / DEF_RELAX) - 1; - strobe_prog = clk_rate / (1000000000 / 10000) + 2 * (DEF_RELAX + 1) - 1; - strobe_read = clk_rate / (1000000000 / 40) + 2 * (DEF_RELAX + 1) - 1; + relax = DIV_ROUND_UP(clk_rate * TIMING_RELAX_NS, 1000000000) - 1; + strobe_read = DIV_ROUND_UP(clk_rate * TIMING_STROBE_READ_NS, + 1000000000); + strobe_read += 2 * (relax + 1) - 1; + strobe_prog = DIV_ROUND_CLOSEST(clk_rate * TIMING_STROBE_PROG_US, + 1000000); + strobe_prog += 2 * (relax + 1) - 1; - timing = strobe_prog & 0x00000FFF; + timing = readl(priv->base + IMX_OCOTP_ADDR_TIMING) & 0x0FC00000; + timing |= strobe_prog & 0x00000FFF; timing |= (relax << 12) & 0x0000F000; timing |= (strobe_read << 16) & 0x003F0000; diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index bfe6c9f99cf98f6a131390524a361abbc4bf1738..57409d7627cacae8d29ffa6f1f69c332798f35f6 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -120,4 +120,8 @@ config OF_BATTERYDATA help OpenFirmware BatteryData accessors +config OF_DMA_DEFAULT_COHERENT + # arches should select this if DMA is coherent by default for OF devices + bool + endif # OF diff --git a/drivers/of/address.c b/drivers/of/address.c index 7ddbf0a1ab8675508b798b322ba5e8f6414d14c7..c42aebba35ab8248cf297bedd603de321929e988 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -970,12 +970,16 @@ EXPORT_SYMBOL_GPL(of_dma_get_range); * @np: device node * * It returns true if "dma-coherent" property was found - * for this device in DT. + * for this device in the DT, or if DMA is coherent by + * default for OF devices on the current platform. */ bool of_dma_is_coherent(struct device_node *np) { struct device_node *node = of_node_get(np); + if (IS_ENABLED(CONFIG_OF_DMA_DEFAULT_COHERENT)) + return true; + while (node) { if (of_property_read_bool(node, "dma-coherent")) { of_node_put(node); diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 9c73c848879a657eae1666da521579c5d169d2c1..688f1226eea2b6629887ae9edc2f812caa3c0c28 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -25,6 +25,7 @@ #include #include #include +#include #include /* for COMMAND_LINE_SIZE */ #include @@ -167,6 +168,7 @@ int of_fdt_get_ddrtype(void) return ret; } +EXPORT_SYMBOL_GPL(of_fdt_get_ddrtype); /** * of_fdt_is_compatible - Return true if given node from the given blob has @@ -1190,6 +1192,7 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname, int l = 0; const char *p = NULL; char *cmdline = data; + const void *rng_seed; pr_debug("search \"chosen\", depth: %d, uname: %s\n", depth, uname); @@ -1224,6 +1227,14 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname, pr_debug("Command line is: %s\n", (char*)data); + rng_seed = of_get_flat_dt_prop(node, "rng-seed", &l); + if (rng_seed && l > 0) { + add_bootloader_randomness(rng_seed, l); + + /* try to clear seed so it won't be found. */ + fdt_nop_property(initial_boot_params, node, "rng-seed"); + } + /* break now */ return 1; } @@ -1326,8 +1337,6 @@ bool __init early_init_dt_verify(void *params) /* Setup flat device-tree pointer */ initial_boot_params = params; - of_fdt_crc32 = crc32_be(~0, initial_boot_params, - fdt_totalsize(initial_boot_params)); return true; } @@ -1353,6 +1362,8 @@ bool __init early_init_dt_scan(void *params) return false; early_init_dt_scan_nodes(); + of_fdt_crc32 = crc32_be(~0, initial_boot_params, + fdt_totalsize(initial_boot_params)); return true; } diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c index 5ad1342f568252bb8fb6b845f1ae5e0b8e24fb50..7d2bc22680d908d697f61233a82f023dc182412d 100644 --- a/drivers/of/of_mdio.c +++ b/drivers/of/of_mdio.c @@ -370,7 +370,7 @@ struct phy_device *of_phy_get_and_connect(struct net_device *dev, int ret; iface = of_get_phy_mode(np); - if (iface < 0) + if ((int)iface < 0) return NULL; if (of_phy_is_fixed_link(np)) { ret = of_phy_register_fixed_link(np); diff --git a/drivers/of/property.c b/drivers/of/property.c index bcd1b992f70c1851f7b26d34cae76d59e8b8de4c..d54d5bdce2d7860e6226a9f201392b5a4dcc13e4 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -1162,6 +1162,35 @@ static struct device_node *parse_##fname(struct device_node *np, \ return parse_suffix_prop_cells(np, prop_name, index, suffix, cells); \ } +static struct device_node *parse_msm_bus_name(struct device_node *np, + const char *prop_name, int index) +{ + static struct device_node *bus_dev_np; + + if (index || strcmp(prop_name, "qcom,msm-bus,name")) + return NULL; + + if (!bus_dev_np) + bus_dev_np = of_find_compatible_node(NULL, NULL, + "qcom,msm-bus-device"); + + return bus_dev_np; +} + +/* Force ignore of any qcom properties. */ +static struct device_node *parse_qcom_any(struct device_node *np, + const char *prop_name, int index) +{ + if (index || strncmp(prop_name, "qcom,", strlen("qcom,"))) + return NULL; + + /* + * Returning np will cause this property to be matched and then + * ignored. + */ + return np; +} + /** * struct supplier_bindings - Property parsing functions for suppliers * @@ -1190,6 +1219,12 @@ DEFINE_SIMPLE_PROP(mboxes, "mboxes", "#mbox-cells") DEFINE_SIMPLE_PROP(io_channels, "io-channel", "#io-channel-cells") DEFINE_SIMPLE_PROP(interrupt_parent, "interrupt-parent", NULL) DEFINE_SIMPLE_PROP(dmas, "dmas", "#dma-cells") +DEFINE_SIMPLE_PROP(power_domains, "power-domains", "#power-domain-cells") +DEFINE_SIMPLE_PROP(hwlocks, "hwlocks", "#hwlock-cells") +DEFINE_SIMPLE_PROP(pinctrl0, "pinctrl-0", NULL) +DEFINE_SIMPLE_PROP(pinctrl1, "pinctrl-1", NULL) +DEFINE_SIMPLE_PROP(pinctrl2, "pinctrl-2", NULL) +DEFINE_SIMPLE_PROP(pinctrl3, "pinctrl-3", NULL) DEFINE_SUFFIX_PROP(regulators, "-supply", NULL) DEFINE_SUFFIX_PROP(gpio, "-gpio", "#gpio-cells") DEFINE_SUFFIX_PROP(gpios, "-gpios", "#gpio-cells") @@ -1204,6 +1239,8 @@ static struct device_node *parse_iommu_maps(struct device_node *np, } static const struct supplier_bindings of_supplier_bindings[] = { + { .parse_prop = parse_msm_bus_name, }, + { .parse_prop = parse_qcom_any, }, { .parse_prop = parse_clocks, }, { .parse_prop = parse_interconnects, }, { .parse_prop = parse_iommus, }, @@ -1212,6 +1249,12 @@ static const struct supplier_bindings of_supplier_bindings[] = { { .parse_prop = parse_io_channels, }, { .parse_prop = parse_interrupt_parent, }, { .parse_prop = parse_dmas, }, + { .parse_prop = parse_power_domains, }, + { .parse_prop = parse_hwlocks, }, + { .parse_prop = parse_pinctrl0, }, + { .parse_prop = parse_pinctrl1, }, + { .parse_prop = parse_pinctrl2, }, + { .parse_prop = parse_pinctrl3, }, { .parse_prop = parse_regulators, }, { .parse_prop = parse_gpio, }, { .parse_prop = parse_gpios, }, diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 31eb950ea3a7a031ffff52003217b6c0a5c5c06e..486b6c60d59f6729175edd22b06f92339dca276b 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -793,6 +793,9 @@ static struct opp_table *_allocate_opp_table(struct device *dev) INIT_LIST_HEAD(&opp_table->dev_list); + /* Mark regulator count uninitialized */ + opp_table->regulator_count = -1; + opp_dev = _add_opp_dev(dev, opp_table); if (!opp_dev) { kfree(opp_table); @@ -1008,7 +1011,7 @@ struct dev_pm_opp *_opp_allocate(struct opp_table *table) int count, supply_size; /* Allocate space for at least one supply */ - count = table->regulator_count ? table->regulator_count : 1; + count = table->regulator_count > 0 ? table->regulator_count : 1; supply_size = sizeof(*opp->supplies) * count; /* allocate new OPP node and supplies structures */ @@ -1416,7 +1419,7 @@ struct opp_table *dev_pm_opp_set_regulators(struct device *dev, kfree(opp_table->regulators); opp_table->regulators = NULL; - opp_table->regulator_count = 0; + opp_table->regulator_count = -1; err: dev_pm_opp_put_opp_table(opp_table); @@ -1445,7 +1448,7 @@ void dev_pm_opp_put_regulators(struct opp_table *opp_table) kfree(opp_table->regulators); opp_table->regulators = NULL; - opp_table->regulator_count = 0; + opp_table->regulator_count = -1; put_opp_table: dev_pm_opp_put_opp_table(opp_table); @@ -1598,6 +1601,9 @@ int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt) if (!opp_table) return -ENOMEM; + /* Fix regulator count for dynamic OPPs */ + opp_table->regulator_count = 1; + ret = _opp_add_v1(opp_table, dev, freq, u_volt, true); dev_pm_opp_put_opp_table(opp_table); diff --git a/drivers/opp/of.c b/drivers/opp/of.c index bce3b7664e35df50f1a5e9a677c3347feff5005f..292339107964394a8351f4452092493a1fbf5fc7 100644 --- a/drivers/opp/of.c +++ b/drivers/opp/of.c @@ -114,12 +114,10 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev, struct opp_table *opp_table) { u32 *microvolt, *microamp = NULL; - int supplies, vcount, icount, ret, i, j; + int supplies = opp_table->regulator_count, vcount, icount, ret, i, j; struct property *prop = NULL; char name[NAME_MAX]; - supplies = opp_table->regulator_count ? opp_table->regulator_count : 1; - /* Search for "opp-microvolt-" */ if (opp_table->prop_name) { snprintf(name, sizeof(name), "opp-microvolt-%s", @@ -134,7 +132,13 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev, /* Missing property isn't a problem, but an invalid entry is */ if (!prop) { - if (!opp_table->regulator_count) + if (unlikely(supplies == -1)) { + /* Initialize regulator_count */ + opp_table->regulator_count = 0; + return 0; + } + + if (!supplies) return 0; dev_err(dev, "%s: opp-microvolt missing although OPP managing regulators\n", @@ -143,6 +147,14 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev, } } + if (unlikely(supplies == -1)) { + /* Initialize regulator_count */ + supplies = opp_table->regulator_count = 1; + } else if (unlikely(!supplies)) { + dev_err(dev, "%s: opp-microvolt wasn't expected\n", __func__); + return -EINVAL; + } + vcount = of_property_count_u32_elems(opp->np, name); if (vcount < 0) { dev_err(dev, "%s: Invalid %s property (%d)\n", diff --git a/drivers/opp/opp.h b/drivers/opp/opp.h index 7c540fd063b2dcc61c8e370b28d9fb2c5adb79d7..c9e65964ed8481138a2944326b135c0993889d2a 100644 --- a/drivers/opp/opp.h +++ b/drivers/opp/opp.h @@ -136,7 +136,9 @@ enum opp_table_access { * @prop_name: A name to postfix to many DT properties, while parsing them. * @clk: Device's clock handle * @regulators: Supply regulators - * @regulator_count: Number of power supply regulators + * @regulator_count: Number of power supply regulators. Its value can be -1 + * (uninitialized), 0 (no opp-microvolt property) or > 0 (has opp-microvolt + * property). * @genpd_performance_state: Device's power domain support performance state. * @set_opp: Platform specific set_opp callback * @set_opp_data: Data to be passed to set_opp callback @@ -172,7 +174,7 @@ struct opp_table { const char *prop_name; struct clk *clk; struct regulator **regulators; - unsigned int regulator_count; + int regulator_count; bool genpd_performance_state; int (*set_opp)(struct dev_pm_set_opp_data *data); diff --git a/drivers/pci/controller/dwc/pci-keystone-dw.c b/drivers/pci/controller/dwc/pci-keystone-dw.c index 0682213328e93ba2a000b4c67bb92736b0518228..15c612e853afbfc881413425afb33579e918c630 100644 --- a/drivers/pci/controller/dwc/pci-keystone-dw.c +++ b/drivers/pci/controller/dwc/pci-keystone-dw.c @@ -425,7 +425,7 @@ void ks_dw_pcie_initiate_link_train(struct keystone_pcie *ks_pcie) /* Disable Link training */ val = ks_dw_app_readl(ks_pcie, CMD_STATUS); val &= ~LTSSM_EN_VAL; - ks_dw_app_writel(ks_pcie, CMD_STATUS, LTSSM_EN_VAL | val); + ks_dw_app_writel(ks_pcie, CMD_STATUS, val); /* Initiate Link Training */ val = ks_dw_app_readl(ks_pcie, CMD_STATUS); diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 739d97080d3bdb8c81e9f445c87409213b97a1f4..a3d07d9c598bff4f101340e04e8044575d25cd80 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -46,16 +46,19 @@ static u8 __dw_pcie_ep_find_next_cap(struct dw_pcie *pci, u8 cap_ptr, u8 cap_id, next_cap_ptr; u16 reg; + if (!cap_ptr) + return 0; + reg = dw_pcie_readw_dbi(pci, cap_ptr); - next_cap_ptr = (reg & 0xff00) >> 8; cap_id = (reg & 0x00ff); - if (!next_cap_ptr || cap_id > PCI_CAP_ID_MAX) + if (cap_id > PCI_CAP_ID_MAX) return 0; if (cap_id == cap) return cap_ptr; + next_cap_ptr = (reg & 0xff00) >> 8; return __dw_pcie_ep_find_next_cap(pci, next_cap_ptr, cap); } @@ -67,9 +70,6 @@ static u8 dw_pcie_ep_find_capability(struct dw_pcie *pci, u8 cap) reg = dw_pcie_readw_dbi(pci, PCI_CAPABILITY_LIST); next_cap_ptr = (reg & 0x00ff); - if (!next_cap_ptr) - return 0; - return __dw_pcie_ep_find_next_cap(pci, next_cap_ptr, cap); } diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index b57ee79f6d699f0db02b7e592dc9f91c5cdf929f..6d4ef0101ef68274c963b26580493751952b7e8b 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -78,7 +78,8 @@ static struct msi_domain_info dw_pcie_msi_domain_info = { irqreturn_t dw_handle_msi_irq(struct pcie_port *pp) { int i, pos, irq; - u32 val, num_ctrls; + unsigned long val; + u32 status, num_ctrls; irqreturn_t ret = IRQ_NONE; num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL; @@ -86,14 +87,14 @@ irqreturn_t dw_handle_msi_irq(struct pcie_port *pp) for (i = 0; i < num_ctrls; i++) { dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_STATUS + (i * MSI_REG_CTRL_BLOCK_SIZE), - 4, &val); - if (!val) + 4, &status); + if (!status) continue; ret = IRQ_HANDLED; + val = status; pos = 0; - while ((pos = find_next_bit((unsigned long *) &val, - MAX_MSI_IRQS_PER_CTRL, + while ((pos = find_next_bit(&val, MAX_MSI_IRQS_PER_CTRL, pos)) != MAX_MSI_IRQS_PER_CTRL) { irq = irq_find_mapping(pp->irq_domain, (i * MAX_MSI_IRQS_PER_CTRL) + diff --git a/drivers/pci/controller/pci-msm.c b/drivers/pci/controller/pci-msm.c index b30331fb33fd0a5f2709210837e1e142095c2815..c0e0722a9e39e8ee80c0c8c95ef201bc6e12631c 100644 --- a/drivers/pci/controller/pci-msm.c +++ b/drivers/pci/controller/pci-msm.c @@ -682,7 +682,7 @@ struct msm_pcie_dev_t { bool disable_pc; struct pci_saved_state *saved_state; - struct wakeup_source ws; + struct wakeup_source *ws; struct msm_bus_scale_pdata *bus_scale_table; uint32_t bus_client; @@ -4991,8 +4991,8 @@ static irqreturn_t handle_wake_irq(int irq, void *data) schedule_work(&dev->handle_wake_work); } else { PCIE_DBG2(dev, "Wake up RC%d\n", dev->rc_idx); - __pm_stay_awake(&dev->ws); - __pm_relax(&dev->ws); + __pm_stay_awake(dev->ws); + __pm_relax(dev->ws); if (dev->num_ep > 1) { for (i = 0; i < MAX_DEVICE_NUM; i++) { @@ -5129,10 +5129,13 @@ static int32_t msm_pcie_irq_init(struct msm_pcie_dev_t *dev) PCIE_DBG(dev, "RC%d\n", dev->rc_idx); - if (dev->rc_idx) - wakeup_source_init(&dev->ws, "RC1 pcie_wakeup_source"); - else - wakeup_source_init(&dev->ws, "RC0 pcie_wakeup_source"); + dev->ws = wakeup_source_register(pdev, dev_name(pdev)); + if (!dev->ws) { + PCIE_ERR(dev, + "PCIe: RC%d: failed to register wakeup source\n", + dev->rc_idx); + return -ENOMEM; + } if (dev->irq[MSM_PCIE_INT_GLOBAL_INT].num) { rc = devm_request_irq(pdev, @@ -5180,7 +5183,7 @@ static void msm_pcie_irq_deinit(struct msm_pcie_dev_t *dev) { PCIE_DBG(dev, "RC%d\n", dev->rc_idx); - wakeup_source_trash(&dev->ws); + wakeup_source_unregister(dev->ws); if (dev->wake_n) disable_irq(dev->wake_n); diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 58e48735285337292d9ae0c1b78f5654ed6ed6b5..6f86583605a46992ef3e73cf4e8e064cf67ad3da 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -2398,7 +2398,7 @@ static int tegra_pcie_probe(struct platform_device *pdev) pm_runtime_enable(pcie->dev); err = pm_runtime_get_sync(pcie->dev); - if (err) { + if (err < 0) { dev_err(dev, "fail to enable pcie controller: %d\n", err); goto teardown_msi; } diff --git a/drivers/pci/controller/pcie-iproc.c b/drivers/pci/controller/pcie-iproc.c index 3160e9342a2fb424ded9edea1c43bee4782eb54f..ec86414216f979f2925c679fb62570ca8b219447 100644 --- a/drivers/pci/controller/pcie-iproc.c +++ b/drivers/pci/controller/pcie-iproc.c @@ -630,14 +630,6 @@ static void __iomem *iproc_pcie_map_cfg_bus(struct iproc_pcie *pcie, return (pcie->base + offset); } - /* - * PAXC is connected to an internally emulated EP within the SoC. It - * allows only one device. - */ - if (pcie->ep_is_internal) - if (slot > 0) - return NULL; - return iproc_pcie_map_ep_cfg_reg(pcie, busno, slot, fn, where); } @@ -1355,7 +1347,6 @@ static int iproc_pcie_rev_init(struct iproc_pcie *pcie) break; case IPROC_PCIE_PAXB: regs = iproc_pcie_reg_paxb; - pcie->iproc_cfg_read = true; pcie->has_apb_err_disable = true; if (pcie->need_ob_cfg) { pcie->ob_map = paxb_ob_map; @@ -1364,6 +1355,7 @@ static int iproc_pcie_rev_init(struct iproc_pcie *pcie) break; case IPROC_PCIE_PAXB_V2: regs = iproc_pcie_reg_paxb_v2; + pcie->iproc_cfg_read = true; pcie->has_apb_err_disable = true; if (pcie->need_ob_cfg) { pcie->ob_map = paxb_v2_ob_map; @@ -1534,6 +1526,30 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd802, DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd804, quirk_paxc_disable_msi_parsing); +static void quirk_paxc_bridge(struct pci_dev *pdev) +{ + /* + * The PCI config space is shared with the PAXC root port and the first + * Ethernet device. So, we need to workaround this by telling the PCI + * code that the bridge is not an Ethernet device. + */ + if (pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE) + pdev->class = PCI_CLASS_BRIDGE_PCI << 8; + + /* + * MPSS is not being set properly (as it is currently 0). This is + * because that area of the PCI config space is hard coded to zero, and + * is not modifiable by firmware. Set this to 2 (e.g., 512 byte MPS) + * so that the MPS can be set to the real max value. + */ + pdev->pcie_mpss = 2; +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x16cd, quirk_paxc_bridge); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x16f0, quirk_paxc_bridge); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd750, quirk_paxc_bridge); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd802, quirk_paxc_bridge); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd804, quirk_paxc_bridge); + MODULE_AUTHOR("Ray Jui "); MODULE_DESCRIPTION("Broadcom iPROC PCIe common driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/controller/pcie-mobiveil.c b/drivers/pci/controller/pcie-mobiveil.c index a2d1e89d48674842e8e0afb0b711ec6370c602fb..14f816591e8449c81a121c62097e6324c7cd75f0 100644 --- a/drivers/pci/controller/pcie-mobiveil.c +++ b/drivers/pci/controller/pcie-mobiveil.c @@ -174,7 +174,7 @@ static bool mobiveil_pcie_valid_device(struct pci_bus *bus, unsigned int devfn) * Do not read more than one device on the bus directly * attached to RC */ - if ((bus->primary == pcie->root_bus_nr) && (devfn > 0)) + if ((bus->primary == pcie->root_bus_nr) && (PCI_SLOT(devfn) > 0)) return false; return true; @@ -395,7 +395,7 @@ static void program_ib_windows(struct mobiveil_pcie *pcie, int win_num, int amap_ctrl_dw; u64 size64 = ~(size - 1); - if ((pcie->ib_wins_configured + 1) > pcie->ppio_wins) { + if (win_num >= pcie->ppio_wins) { dev_err(&pcie->pdev->dev, "ERROR: max inbound windows reached !\n"); return; @@ -429,7 +429,7 @@ static void program_ob_windows(struct mobiveil_pcie *pcie, int win_num, u32 value, type; u64 size64 = ~(size - 1); - if ((pcie->ob_wins_configured + 1) > pcie->apio_wins) { + if (win_num >= pcie->apio_wins) { dev_err(&pcie->pdev->dev, "ERROR: max outbound windows reached !\n"); return; @@ -643,7 +643,7 @@ static struct irq_chip mobiveil_msi_irq_chip = { static struct msi_domain_info mobiveil_msi_domain_info = { .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | - MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX), + MSI_FLAG_PCI_MSIX), .chip = &mobiveil_msi_irq_chip, }; diff --git a/drivers/pci/controller/pcie-rockchip-ep.c b/drivers/pci/controller/pcie-rockchip-ep.c index b8163c56a142d91bb8641fa92e26ec8c1ee15aa6..caf34661d38d4dc656e3067dd7e20f32afbb64db 100644 --- a/drivers/pci/controller/pcie-rockchip-ep.c +++ b/drivers/pci/controller/pcie-rockchip-ep.c @@ -350,7 +350,7 @@ static void rockchip_pcie_ep_assert_intx(struct rockchip_pcie_ep *ep, u8 fn, struct rockchip_pcie *rockchip = &ep->rockchip; u32 r = ep->max_regions - 1; u32 offset; - u16 status; + u32 status; u8 msg_code; if (unlikely(ep->irq_pci_addr != ROCKCHIP_PCIE_EP_PCI_LEGACY_IRQ_ADDR || diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 3e86fa3c7da32eed5f432d57126b1748951aa2ff..4bbd26e8a9e2fcdfb334e271644a0dccbc023654 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -175,7 +175,7 @@ static int pci_epf_test_read(struct pci_epf_test *epf_test) goto err_map_addr; } - memcpy(buf, src_addr, reg->size); + memcpy_fromio(buf, src_addr, reg->size); crc32 = crc32_le(~0, buf, reg->size); if (crc32 != reg->checksum) @@ -230,7 +230,7 @@ static int pci_epf_test_write(struct pci_epf_test *epf_test) get_random_bytes(buf, reg->size); reg->checksum = crc32_le(~0, buf, reg->size); - memcpy(dst_addr, buf, reg->size); + memcpy_toio(dst_addr, buf, reg->size); /* * wait 1ms inorder for the write to complete. Without this delay L3 diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index c5f3cd4ed766f8d79798e8e22a8e12f56764333f..c3b0b10f95cb6bdf97f218af93d3162df51f52e2 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -188,10 +188,10 @@ int pci_iov_add_virtfn(struct pci_dev *dev, int id) sprintf(buf, "virtfn%u", id); rc = sysfs_create_link(&dev->dev.kobj, &virtfn->dev.kobj, buf); if (rc) - goto failed2; + goto failed1; rc = sysfs_create_link(&virtfn->dev.kobj, &dev->dev.kobj, "physfn"); if (rc) - goto failed3; + goto failed2; kobject_uevent(&virtfn->dev.kobj, KOBJ_CHANGE); @@ -199,11 +199,10 @@ int pci_iov_add_virtfn(struct pci_dev *dev, int id) return 0; -failed3: - sysfs_remove_link(&dev->dev.kobj, buf); failed2: - pci_stop_and_remove_bus_device(virtfn); + sysfs_remove_link(&dev->dev.kobj, buf); failed1: + pci_stop_and_remove_bus_device(virtfn); pci_dev_put(dev); failed0: virtfn_remove_bus(dev->bus, bus); diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index de65154196acf4930349b0b61e48288b44f91eb8..d11e1576fc3e3eacfaa623619bba19e1fcb55729 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -968,15 +968,15 @@ static int pci_pm_freeze(struct device *dev) } /* - * This used to be done in pci_pm_prepare() for all devices and some - * drivers may depend on it, so do it here. Ideally, runtime-suspended - * devices should not be touched during freeze/thaw transitions, - * however. + * Resume all runtime-suspended devices before creating a snapshot + * image of system memory, because the restore kernel generally cannot + * be expected to always handle them consistently and they need to be + * put into the runtime-active metastate during system resume anyway, + * so it is better to ensure that the state saved in the image will be + * always consistent with that. */ - if (!dev_pm_smart_suspend_and_suspended(dev)) { - pm_runtime_resume(dev); - pci_dev->state_saved = false; - } + pm_runtime_resume(dev); + pci_dev->state_saved = false; if (pm->freeze) { int error; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index c9f51fc24563c9594dc17ee38c93d946fd862682..57a87a001b4f4f608c79c4f7940eac95a20335f8 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -5039,39 +5039,42 @@ static int pci_slot_trylock(struct pci_slot *slot) return 0; } -/* Save and disable devices from the top of the tree down */ -static void pci_bus_save_and_disable(struct pci_bus *bus) +/* + * Save and disable devices from the top of the tree down while holding + * the @dev mutex lock for the entire tree. + */ +static void pci_bus_save_and_disable_locked(struct pci_bus *bus) { struct pci_dev *dev; list_for_each_entry(dev, &bus->devices, bus_list) { - pci_dev_lock(dev); pci_dev_save_and_disable(dev); - pci_dev_unlock(dev); if (dev->subordinate) - pci_bus_save_and_disable(dev->subordinate); + pci_bus_save_and_disable_locked(dev->subordinate); } } /* - * Restore devices from top of the tree down - parent bridges need to be - * restored before we can get to subordinate devices. + * Restore devices from top of the tree down while holding @dev mutex lock + * for the entire tree. Parent bridges need to be restored before we can + * get to subordinate devices. */ -static void pci_bus_restore(struct pci_bus *bus) +static void pci_bus_restore_locked(struct pci_bus *bus) { struct pci_dev *dev; list_for_each_entry(dev, &bus->devices, bus_list) { - pci_dev_lock(dev); pci_dev_restore(dev); - pci_dev_unlock(dev); if (dev->subordinate) - pci_bus_restore(dev->subordinate); + pci_bus_restore_locked(dev->subordinate); } } -/* Save and disable devices from the top of the tree down */ -static void pci_slot_save_and_disable(struct pci_slot *slot) +/* + * Save and disable devices from the top of the tree down while holding + * the @dev mutex lock for the entire tree. + */ +static void pci_slot_save_and_disable_locked(struct pci_slot *slot) { struct pci_dev *dev; @@ -5080,26 +5083,25 @@ static void pci_slot_save_and_disable(struct pci_slot *slot) continue; pci_dev_save_and_disable(dev); if (dev->subordinate) - pci_bus_save_and_disable(dev->subordinate); + pci_bus_save_and_disable_locked(dev->subordinate); } } /* - * Restore devices from top of the tree down - parent bridges need to be - * restored before we can get to subordinate devices. + * Restore devices from top of the tree down while holding @dev mutex lock + * for the entire tree. Parent bridges need to be restored before we can + * get to subordinate devices. */ -static void pci_slot_restore(struct pci_slot *slot) +static void pci_slot_restore_locked(struct pci_slot *slot) { struct pci_dev *dev; list_for_each_entry(dev, &slot->bus->devices, bus_list) { if (!dev->slot || dev->slot != slot) continue; - pci_dev_lock(dev); pci_dev_restore(dev); - pci_dev_unlock(dev); if (dev->subordinate) - pci_bus_restore(dev->subordinate); + pci_bus_restore_locked(dev->subordinate); } } @@ -5158,17 +5160,15 @@ static int __pci_reset_slot(struct pci_slot *slot) if (rc) return rc; - pci_slot_save_and_disable(slot); - if (pci_slot_trylock(slot)) { + pci_slot_save_and_disable_locked(slot); might_sleep(); rc = pci_reset_hotplug_slot(slot->hotplug, 0); + pci_slot_restore_locked(slot); pci_slot_unlock(slot); } else rc = -EAGAIN; - pci_slot_restore(slot); - return rc; } @@ -5254,17 +5254,15 @@ static int __pci_reset_bus(struct pci_bus *bus) if (rc) return rc; - pci_bus_save_and_disable(bus); - if (pci_bus_trylock(bus)) { + pci_bus_save_and_disable_locked(bus); might_sleep(); rc = pci_bridge_secondary_bus_reset(bus->self); + pci_bus_restore_locked(bus); pci_bus_unlock(bus); } else rc = -EAGAIN; - pci_bus_restore(bus); - return rc; } diff --git a/drivers/pci/pcie/ptm.c b/drivers/pci/pcie/ptm.c index 98cfa30f3fae5aa8241700736b540ed870963d33..9361f3aa26ab8e6a0bfa100b48152f14bff29bc3 100644 --- a/drivers/pci/pcie/ptm.c +++ b/drivers/pci/pcie/ptm.c @@ -21,7 +21,7 @@ static void pci_ptm_info(struct pci_dev *dev) snprintf(clock_desc, sizeof(clock_desc), ">254ns"); break; default: - snprintf(clock_desc, sizeof(clock_desc), "%udns", + snprintf(clock_desc, sizeof(clock_desc), "%uns", dev->ptm_granularity); break; } diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 20a57a48ae1e4f7058a43b8c2b084253803dafba..419dda6dbd1662ca1840123d6c40c2a991722fd2 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -1848,19 +1848,40 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x2609, quirk_intel_pcie_pm); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x260a, quirk_intel_pcie_pm); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x260b, quirk_intel_pcie_pm); +static void quirk_d3hot_delay(struct pci_dev *dev, unsigned int delay) +{ + if (dev->d3_delay >= delay) + return; + + dev->d3_delay = delay; + pci_info(dev, "extending delay after power-on from D3hot to %d msec\n", + dev->d3_delay); +} + static void quirk_radeon_pm(struct pci_dev *dev) { if (dev->subsystem_vendor == PCI_VENDOR_ID_APPLE && - dev->subsystem_device == 0x00e2) { - if (dev->d3_delay < 20) { - dev->d3_delay = 20; - pci_info(dev, "extending delay after power-on from D3 to %d msec\n", - dev->d3_delay); - } - } + dev->subsystem_device == 0x00e2) + quirk_d3hot_delay(dev, 20); } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x6741, quirk_radeon_pm); +/* + * Ryzen5/7 XHCI controllers fail upon resume from runtime suspend or s2idle. + * https://bugzilla.kernel.org/show_bug.cgi?id=205587 + * + * The kernel attempts to transition these devices to D3cold, but that seems + * to be ineffective on the platforms in question; the PCI device appears to + * remain on in D3hot state. The D3hot-to-D0 transition then requires an + * extended delay in order to succeed. + */ +static void quirk_ryzen_xhci_d3hot(struct pci_dev *dev) +{ + quirk_d3hot_delay(dev, 20); +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, 0x15e0, quirk_ryzen_xhci_d3hot); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, 0x15e1, quirk_ryzen_xhci_d3hot); + #ifdef CONFIG_X86_IO_APIC static int dmi_disable_ioapicreroute(const struct dmi_system_id *d) { @@ -2358,32 +2379,6 @@ DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5719, quirk_brcm_5719_limit_mrrs); -#ifdef CONFIG_PCIE_IPROC_PLATFORM -static void quirk_paxc_bridge(struct pci_dev *pdev) -{ - /* - * The PCI config space is shared with the PAXC root port and the first - * Ethernet device. So, we need to workaround this by telling the PCI - * code that the bridge is not an Ethernet device. - */ - if (pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE) - pdev->class = PCI_CLASS_BRIDGE_PCI << 8; - - /* - * MPSS is not being set properly (as it is currently 0). This is - * because that area of the PCI config space is hard coded to zero, and - * is not modifiable by firmware. Set this to 2 (e.g., 512 byte MPS) - * so that the MPS can be set to the real max value. - */ - pdev->pcie_mpss = 2; -} -DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x16cd, quirk_paxc_bridge); -DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x16f0, quirk_paxc_bridge); -DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd750, quirk_paxc_bridge); -DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd802, quirk_paxc_bridge); -DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd804, quirk_paxc_bridge); -#endif - /* * Originally in EDAC sources for i82875P: Intel tells BIOS developers to * hide device 6 which configures the overflow device access containing the @@ -3986,6 +3981,40 @@ static void quirk_mic_x200_dma_alias(struct pci_dev *pdev) DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2260, quirk_mic_x200_dma_alias); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2264, quirk_mic_x200_dma_alias); +/* + * Intel Visual Compute Accelerator (VCA) is a family of PCIe add-in devices + * exposing computational units via Non Transparent Bridges (NTB, PEX 87xx). + * + * Similarly to MIC x200, we need to add DMA aliases to allow buffer access + * when IOMMU is enabled. These aliases allow computational unit access to + * host memory. These aliases mark the whole VCA device as one IOMMU + * group. + * + * All possible slot numbers (0x20) are used, since we are unable to tell + * what slot is used on other side. This quirk is intended for both host + * and computational unit sides. The VCA devices have up to five functions + * (four for DMA channels and one additional). + */ +static void quirk_pex_vca_alias(struct pci_dev *pdev) +{ + const unsigned int num_pci_slots = 0x20; + unsigned int slot; + + for (slot = 0; slot < num_pci_slots; slot++) { + pci_add_dma_alias(pdev, PCI_DEVFN(slot, 0x0)); + pci_add_dma_alias(pdev, PCI_DEVFN(slot, 0x1)); + pci_add_dma_alias(pdev, PCI_DEVFN(slot, 0x2)); + pci_add_dma_alias(pdev, PCI_DEVFN(slot, 0x3)); + pci_add_dma_alias(pdev, PCI_DEVFN(slot, 0x4)); + } +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2954, quirk_pex_vca_alias); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2955, quirk_pex_vca_alias); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2956, quirk_pex_vca_alias); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2958, quirk_pex_vca_alias); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2959, quirk_pex_vca_alias); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x295A, quirk_pex_vca_alias); + /* * The IOMMU and interrupt controller on Broadcom Vulcan/Cavium ThunderX2 are * associated not at the root bus, but at a bridge below. This quirk avoids @@ -4891,18 +4920,25 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, 0x0422, quirk_no_ext_tags); #ifdef CONFIG_PCI_ATS /* - * Some devices have a broken ATS implementation causing IOMMU stalls. - * Don't use ATS for those devices. + * Some devices require additional driver setup to enable ATS. Don't use + * ATS for those devices as ATS will be enabled before the driver has had a + * chance to load and configure the device. */ -static void quirk_no_ats(struct pci_dev *pdev) +static void quirk_amd_harvest_no_ats(struct pci_dev *pdev) { - pci_info(pdev, "disabling ATS (broken on this device)\n"); + if (pdev->device == 0x7340 && pdev->revision != 0xc5) + return; + + pci_info(pdev, "disabling ATS\n"); pdev->ats_cap = 0; } /* AMD Stoney platform GPU */ -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x98e4, quirk_no_ats); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x6900, quirk_no_ats); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x98e4, quirk_amd_harvest_no_ats); +/* AMD Iceland dGPU */ +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x6900, quirk_amd_harvest_no_ats); +/* AMD Navi14 dGPU */ +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x7340, quirk_amd_harvest_no_ats); #endif /* CONFIG_PCI_ATS */ /* Freescale PCIe doesn't support MSI in RC mode */ diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 79b1824e83b47c058a262185668aff946293bf5e..8e5b00a420a55fd5b6c92d2dc8af121226503ce9 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1820,12 +1820,18 @@ void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus) /* restore size and flags */ list_for_each_entry(fail_res, &fail_head, list) { struct resource *res = fail_res->res; + int idx; res->start = fail_res->start; res->end = fail_res->end; res->flags = fail_res->flags; - if (fail_res->dev->subordinate) - res->flags = 0; + + if (pci_is_bridge(fail_res->dev)) { + idx = res - &fail_res->dev->resource[0]; + if (idx >= PCI_BRIDGE_RESOURCES && + idx <= PCI_BRIDGE_RESOURCE_END) + res->flags = 0; + } } free_list(&fail_head); @@ -2066,12 +2072,18 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge) /* restore size and flags */ list_for_each_entry(fail_res, &fail_head, list) { struct resource *res = fail_res->res; + int idx; res->start = fail_res->start; res->end = fail_res->end; res->flags = fail_res->flags; - if (fail_res->dev->subordinate) - res->flags = 0; + + if (pci_is_bridge(fail_res->dev)) { + idx = res - &fail_res->dev->resource[0]; + if (idx >= PCI_BRIDGE_RESOURCES && + idx <= PCI_BRIDGE_RESOURCE_END) + res->flags = 0; + } } free_list(&fail_head); diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index 5aaa4ce04ec3dfbdecb71286104cbba9d26ff881..43431816412c1116859ebd2f29574e1d191bf477 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -134,10 +134,6 @@ static void mrpc_cmd_submit(struct switchtec_dev *stdev) stuser->data, stuser->data_len); iowrite32(stuser->cmd, &stdev->mmio_mrpc->cmd); - stuser->status = ioread32(&stdev->mmio_mrpc->status); - if (stuser->status != SWITCHTEC_MRPC_STATUS_INPROGRESS) - mrpc_complete_cmd(stdev); - schedule_delayed_work(&stdev->mrpc_timeout, msecs_to_jiffies(500)); } @@ -1190,7 +1186,7 @@ static int switchtec_init_isr(struct switchtec_dev *stdev) if (nvecs < 0) return nvecs; - event_irq = ioread32(&stdev->mmio_part_cfg->vep_vector_number); + event_irq = ioread16(&stdev->mmio_part_cfg->vep_vector_number); if (event_irq < 0 || event_irq >= nvecs) return -EFAULT; diff --git a/drivers/phy/broadcom/phy-brcm-usb.c b/drivers/phy/broadcom/phy-brcm-usb.c index d1dab36fa5b7b92e12fd2851d9c5ad00a15214e0..e2455ffb859796ea8b9c1a9a586ffc48e01fc739 100644 --- a/drivers/phy/broadcom/phy-brcm-usb.c +++ b/drivers/phy/broadcom/phy-brcm-usb.c @@ -378,6 +378,13 @@ static int brcm_usb_phy_probe(struct platform_device *pdev) return 0; } +static int brcm_usb_phy_remove(struct platform_device *pdev) +{ + sysfs_remove_group(&pdev->dev.kobj, &brcm_usb_phy_group); + + return 0; +} + #ifdef CONFIG_PM_SLEEP static int brcm_usb_phy_suspend(struct device *dev) { @@ -443,6 +450,7 @@ MODULE_DEVICE_TABLE(of, brcm_usb_dt_ids); static struct platform_driver brcm_usb_driver = { .probe = brcm_usb_phy_probe, + .remove = brcm_usb_phy_remove, .driver = { .name = "brcmstb-usb-phy", .owner = THIS_MODULE, diff --git a/drivers/phy/motorola/phy-cpcap-usb.c b/drivers/phy/motorola/phy-cpcap-usb.c index 6601ad0dfb3ad23c77865138b3db7bd0c4f55ae8..593c77dbde2eb37ec072955ba1a877e877954bc6 100644 --- a/drivers/phy/motorola/phy-cpcap-usb.c +++ b/drivers/phy/motorola/phy-cpcap-usb.c @@ -115,7 +115,7 @@ struct cpcap_usb_ints_state { enum cpcap_gpio_mode { CPCAP_DM_DP, CPCAP_MDM_RX_TX, - CPCAP_UNKNOWN, + CPCAP_UNKNOWN_DISABLED, /* Seems to disable USB lines */ CPCAP_OTG_DM_DP, }; @@ -207,6 +207,19 @@ static int cpcap_phy_get_ints_state(struct cpcap_phy_ddata *ddata, static int cpcap_usb_set_uart_mode(struct cpcap_phy_ddata *ddata); static int cpcap_usb_set_usb_mode(struct cpcap_phy_ddata *ddata); +static void cpcap_usb_try_musb_mailbox(struct cpcap_phy_ddata *ddata, + enum musb_vbus_id_status status) +{ + int error; + + error = musb_mailbox(status); + if (!error) + return; + + dev_dbg(ddata->dev, "%s: musb_mailbox failed: %i\n", + __func__, error); +} + static void cpcap_usb_detect(struct work_struct *work) { struct cpcap_phy_ddata *ddata; @@ -226,9 +239,7 @@ static void cpcap_usb_detect(struct work_struct *work) if (error) goto out_err; - error = musb_mailbox(MUSB_ID_GROUND); - if (error) - goto out_err; + cpcap_usb_try_musb_mailbox(ddata, MUSB_ID_GROUND); error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC3, CPCAP_BIT_VBUSSTBY_EN, @@ -255,9 +266,7 @@ static void cpcap_usb_detect(struct work_struct *work) error = cpcap_usb_set_usb_mode(ddata); if (error) goto out_err; - error = musb_mailbox(MUSB_ID_GROUND); - if (error) - goto out_err; + cpcap_usb_try_musb_mailbox(ddata, MUSB_ID_GROUND); return; } @@ -267,22 +276,18 @@ static void cpcap_usb_detect(struct work_struct *work) error = cpcap_usb_set_usb_mode(ddata); if (error) goto out_err; - error = musb_mailbox(MUSB_VBUS_VALID); - if (error) - goto out_err; + cpcap_usb_try_musb_mailbox(ddata, MUSB_VBUS_VALID); return; } + cpcap_usb_try_musb_mailbox(ddata, MUSB_VBUS_OFF); + /* Default to debug UART mode */ error = cpcap_usb_set_uart_mode(ddata); if (error) goto out_err; - error = musb_mailbox(MUSB_VBUS_OFF); - if (error) - goto out_err; - dev_dbg(ddata->dev, "set UART mode\n"); return; @@ -374,7 +379,8 @@ static int cpcap_usb_set_uart_mode(struct cpcap_phy_ddata *ddata) { int error; - error = cpcap_usb_gpio_set_mode(ddata, CPCAP_DM_DP); + /* Disable lines to prevent glitches from waking up mdm6600 */ + error = cpcap_usb_gpio_set_mode(ddata, CPCAP_UNKNOWN_DISABLED); if (error) goto out_err; @@ -401,6 +407,11 @@ static int cpcap_usb_set_uart_mode(struct cpcap_phy_ddata *ddata) if (error) goto out_err; + /* Enable UART mode */ + error = cpcap_usb_gpio_set_mode(ddata, CPCAP_DM_DP); + if (error) + goto out_err; + return 0; out_err: @@ -413,7 +424,8 @@ static int cpcap_usb_set_usb_mode(struct cpcap_phy_ddata *ddata) { int error; - error = cpcap_usb_gpio_set_mode(ddata, CPCAP_OTG_DM_DP); + /* Disable lines to prevent glitches from waking up mdm6600 */ + error = cpcap_usb_gpio_set_mode(ddata, CPCAP_UNKNOWN_DISABLED); if (error) return error; @@ -453,6 +465,11 @@ static int cpcap_usb_set_usb_mode(struct cpcap_phy_ddata *ddata) if (error) goto out_err; + /* Enable USB mode */ + error = cpcap_usb_gpio_set_mode(ddata, CPCAP_OTG_DM_DP); + if (error) + goto out_err; + return 0; out_err: @@ -647,9 +664,7 @@ static int cpcap_usb_phy_remove(struct platform_device *pdev) if (error) dev_err(ddata->dev, "could not set UART mode\n"); - error = musb_mailbox(MUSB_VBUS_OFF); - if (error) - dev_err(ddata->dev, "could not set mailbox\n"); + cpcap_usb_try_musb_mailbox(ddata, MUSB_VBUS_OFF); usb_remove_phy(&ddata->phy); cancel_delayed_work_sync(&ddata->detect_work); diff --git a/drivers/phy/motorola/phy-mapphone-mdm6600.c b/drivers/phy/motorola/phy-mapphone-mdm6600.c index 0075fb0bef8c55eab8d66804f9c6310003e8ec17..77518010adc88353002561543c3ebf3b66de3ceb 100644 --- a/drivers/phy/motorola/phy-mapphone-mdm6600.c +++ b/drivers/phy/motorola/phy-mapphone-mdm6600.c @@ -19,6 +19,7 @@ #define PHY_MDM6600_PHY_DELAY_MS 4000 /* PHY enable 2.2s to 3.5s */ #define PHY_MDM6600_ENABLED_DELAY_MS 8000 /* 8s more total for MDM6600 */ +#define PHY_MDM6600_WAKE_KICK_MS 600 /* time on after GPIO toggle */ #define MDM6600_MODEM_IDLE_DELAY_MS 1000 /* modem after USB suspend */ #define MDM6600_MODEM_WAKE_DELAY_MS 200 /* modem response after idle */ @@ -224,10 +225,24 @@ static irqreturn_t phy_mdm6600_wakeirq_thread(int irq, void *data) { struct phy_mdm6600 *ddata = data; struct gpio_desc *mode_gpio1; + int error, wakeup; mode_gpio1 = ddata->mode_gpios->desc[PHY_MDM6600_MODE1]; - dev_dbg(ddata->dev, "OOB wake on mode_gpio1: %i\n", - gpiod_get_value(mode_gpio1)); + wakeup = gpiod_get_value(mode_gpio1); + if (!wakeup) + return IRQ_NONE; + + dev_dbg(ddata->dev, "OOB wake on mode_gpio1: %i\n", wakeup); + error = pm_runtime_get_sync(ddata->dev); + if (error < 0) { + pm_runtime_put_noidle(ddata->dev); + + return IRQ_NONE; + } + + /* Just wake-up and kick the autosuspend timer */ + pm_runtime_mark_last_busy(ddata->dev); + pm_runtime_put_autosuspend(ddata->dev); return IRQ_HANDLED; } @@ -477,8 +492,14 @@ static void phy_mdm6600_modem_wake(struct work_struct *work) ddata = container_of(work, struct phy_mdm6600, modem_wake_work.work); phy_mdm6600_wake_modem(ddata); + + /* + * The modem does not always stay awake 1.2 seconds after toggling + * the wake GPIO, and sometimes it idles after about some 600 ms + * making writes time out. + */ schedule_delayed_work(&ddata->modem_wake_work, - msecs_to_jiffies(MDM6600_MODEM_IDLE_DELAY_MS)); + msecs_to_jiffies(PHY_MDM6600_WAKE_KICK_MS)); } static int __maybe_unused phy_mdm6600_runtime_suspend(struct device *dev) diff --git a/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c b/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c index 69ce2afac0150fb580630921ed4f9892d58dab77..c6925e3e878bfc7770c2176639e1c939afeaef82 100644 --- a/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c +++ b/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c @@ -88,7 +88,7 @@ static int read_poll_timeout(void __iomem *addr, u32 mask) if (readl_relaxed(addr) & mask) return 0; - usleep_range(DELAY_INTERVAL_US, DELAY_INTERVAL_US + 50); + usleep_range(DELAY_INTERVAL_US, DELAY_INTERVAL_US + 50); } while (!time_after(jiffies, timeout)); return (readl_relaxed(addr) & mask) ? 0 : -ETIMEDOUT; diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index 4c470104a0d61fa22a18690475216b17f693d0dd..cf515928fed09653d3ab92c87253f84bd5cf23c5 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -66,7 +66,7 @@ /* QPHY_V3_PCS_MISC_CLAMP_ENABLE register bits */ #define CLAMP_EN BIT(0) /* enables i/o clamp_n */ -#define PHY_INIT_COMPLETE_TIMEOUT 1000 +#define PHY_INIT_COMPLETE_TIMEOUT 10000 #define POWER_DOWN_DELAY_US_MIN 10 #define POWER_DOWN_DELAY_US_MAX 11 diff --git a/drivers/phy/qualcomm/phy-qcom-qusb2.c b/drivers/phy/qualcomm/phy-qcom-qusb2.c index 69c92843eb3b2833affb52af03e8e88f56629fd9..9b7ae93e9df1e115238a403144d76544bfc808c8 100644 --- a/drivers/phy/qualcomm/phy-qcom-qusb2.c +++ b/drivers/phy/qualcomm/phy-qcom-qusb2.c @@ -526,7 +526,7 @@ static int __maybe_unused qusb2_phy_runtime_resume(struct device *dev) } if (!qphy->has_se_clk_scheme) { - clk_prepare_enable(qphy->ref_clk); + ret = clk_prepare_enable(qphy->ref_clk); if (ret) { dev_err(dev, "failed to enable ref clk, %d\n", ret); goto disable_ahb_clk; diff --git a/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c b/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c index b70058caee5049845633717ba6d54fdb892ef56b..20b9864adce067bd535a0371cbbe35c30084c21d 100644 --- a/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c +++ b/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c @@ -54,8 +54,12 @@ /* drive strength control for ASIU GPIO */ #define IPROC_GPIO_ASIU_DRV0_CTRL_OFFSET 0x58 -/* drive strength control for CCM/CRMU (AON) GPIO */ -#define IPROC_GPIO_DRV0_CTRL_OFFSET 0x00 +/* pinconf for CCM GPIO */ +#define IPROC_GPIO_PULL_DN_OFFSET 0x10 +#define IPROC_GPIO_PULL_UP_OFFSET 0x14 + +/* pinconf for CRMU(aon) GPIO and CCM GPIO*/ +#define IPROC_GPIO_DRV_CTRL_OFFSET 0x00 #define GPIO_BANK_SIZE 0x200 #define NGPIOS_PER_BANK 32 @@ -76,6 +80,12 @@ enum iproc_pinconf_param { IPROC_PINCON_MAX, }; +enum iproc_pinconf_ctrl_type { + IOCTRL_TYPE_AON = 1, + IOCTRL_TYPE_CDRU, + IOCTRL_TYPE_INVALID, +}; + /* * Iproc GPIO core * @@ -100,6 +110,7 @@ struct iproc_gpio { void __iomem *base; void __iomem *io_ctrl; + enum iproc_pinconf_ctrl_type io_ctrl_type; raw_spinlock_t lock; @@ -461,20 +472,44 @@ static const struct pinctrl_ops iproc_pctrl_ops = { static int iproc_gpio_set_pull(struct iproc_gpio *chip, unsigned gpio, bool disable, bool pull_up) { + void __iomem *base; unsigned long flags; + unsigned int shift; + u32 val_1, val_2; raw_spin_lock_irqsave(&chip->lock, flags); - - if (disable) { - iproc_set_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio, false); + if (chip->io_ctrl_type == IOCTRL_TYPE_CDRU) { + base = chip->io_ctrl; + shift = IPROC_GPIO_SHIFT(gpio); + + val_1 = readl(base + IPROC_GPIO_PULL_UP_OFFSET); + val_2 = readl(base + IPROC_GPIO_PULL_DN_OFFSET); + if (disable) { + /* no pull-up or pull-down */ + val_1 &= ~BIT(shift); + val_2 &= ~BIT(shift); + } else if (pull_up) { + val_1 |= BIT(shift); + val_2 &= ~BIT(shift); + } else { + val_1 &= ~BIT(shift); + val_2 |= BIT(shift); + } + writel(val_1, base + IPROC_GPIO_PULL_UP_OFFSET); + writel(val_2, base + IPROC_GPIO_PULL_DN_OFFSET); } else { - iproc_set_bit(chip, IPROC_GPIO_PAD_RES_OFFSET, gpio, - pull_up); - iproc_set_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio, true); + if (disable) { + iproc_set_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio, + false); + } else { + iproc_set_bit(chip, IPROC_GPIO_PAD_RES_OFFSET, gpio, + pull_up); + iproc_set_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio, + true); + } } raw_spin_unlock_irqrestore(&chip->lock, flags); - dev_dbg(chip->dev, "gpio:%u set pullup:%d\n", gpio, pull_up); return 0; @@ -483,14 +518,35 @@ static int iproc_gpio_set_pull(struct iproc_gpio *chip, unsigned gpio, static void iproc_gpio_get_pull(struct iproc_gpio *chip, unsigned gpio, bool *disable, bool *pull_up) { + void __iomem *base; unsigned long flags; + unsigned int shift; + u32 val_1, val_2; raw_spin_lock_irqsave(&chip->lock, flags); - *disable = !iproc_get_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio); - *pull_up = iproc_get_bit(chip, IPROC_GPIO_PAD_RES_OFFSET, gpio); + if (chip->io_ctrl_type == IOCTRL_TYPE_CDRU) { + base = chip->io_ctrl; + shift = IPROC_GPIO_SHIFT(gpio); + + val_1 = readl(base + IPROC_GPIO_PULL_UP_OFFSET) & BIT(shift); + val_2 = readl(base + IPROC_GPIO_PULL_DN_OFFSET) & BIT(shift); + + *pull_up = val_1 ? true : false; + *disable = (val_1 | val_2) ? false : true; + + } else { + *disable = !iproc_get_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio); + *pull_up = iproc_get_bit(chip, IPROC_GPIO_PAD_RES_OFFSET, gpio); + } raw_spin_unlock_irqrestore(&chip->lock, flags); } +#define DRV_STRENGTH_OFFSET(gpio, bit, type) ((type) == IOCTRL_TYPE_AON ? \ + ((2 - (bit)) * 4 + IPROC_GPIO_DRV_CTRL_OFFSET) : \ + ((type) == IOCTRL_TYPE_CDRU) ? \ + ((bit) * 4 + IPROC_GPIO_DRV_CTRL_OFFSET) : \ + ((bit) * 4 + IPROC_GPIO_REG(gpio, IPROC_GPIO_ASIU_DRV0_CTRL_OFFSET))) + static int iproc_gpio_set_strength(struct iproc_gpio *chip, unsigned gpio, unsigned strength) { @@ -505,11 +561,8 @@ static int iproc_gpio_set_strength(struct iproc_gpio *chip, unsigned gpio, if (chip->io_ctrl) { base = chip->io_ctrl; - offset = IPROC_GPIO_DRV0_CTRL_OFFSET; } else { base = chip->base; - offset = IPROC_GPIO_REG(gpio, - IPROC_GPIO_ASIU_DRV0_CTRL_OFFSET); } shift = IPROC_GPIO_SHIFT(gpio); @@ -520,11 +573,11 @@ static int iproc_gpio_set_strength(struct iproc_gpio *chip, unsigned gpio, raw_spin_lock_irqsave(&chip->lock, flags); strength = (strength / 2) - 1; for (i = 0; i < GPIO_DRV_STRENGTH_BITS; i++) { + offset = DRV_STRENGTH_OFFSET(gpio, i, chip->io_ctrl_type); val = readl(base + offset); val &= ~BIT(shift); val |= ((strength >> i) & 0x1) << shift; writel(val, base + offset); - offset += 4; } raw_spin_unlock_irqrestore(&chip->lock, flags); @@ -541,11 +594,8 @@ static int iproc_gpio_get_strength(struct iproc_gpio *chip, unsigned gpio, if (chip->io_ctrl) { base = chip->io_ctrl; - offset = IPROC_GPIO_DRV0_CTRL_OFFSET; } else { base = chip->base; - offset = IPROC_GPIO_REG(gpio, - IPROC_GPIO_ASIU_DRV0_CTRL_OFFSET); } shift = IPROC_GPIO_SHIFT(gpio); @@ -553,10 +603,10 @@ static int iproc_gpio_get_strength(struct iproc_gpio *chip, unsigned gpio, raw_spin_lock_irqsave(&chip->lock, flags); *strength = 0; for (i = 0; i < GPIO_DRV_STRENGTH_BITS; i++) { + offset = DRV_STRENGTH_OFFSET(gpio, i, chip->io_ctrl_type); val = readl(base + offset) & BIT(shift); val >>= shift; *strength += (val << i); - offset += 4; } /* convert to mA */ @@ -734,6 +784,7 @@ static int iproc_gpio_probe(struct platform_device *pdev) u32 ngpios, pinconf_disable_mask = 0; int irq, ret; bool no_pinconf = false; + enum iproc_pinconf_ctrl_type io_ctrl_type = IOCTRL_TYPE_INVALID; /* NSP does not support drive strength config */ if (of_device_is_compatible(dev->of_node, "brcm,iproc-nsp-gpio")) @@ -764,8 +815,15 @@ static int iproc_gpio_probe(struct platform_device *pdev) dev_err(dev, "unable to map I/O memory\n"); return PTR_ERR(chip->io_ctrl); } + if (of_device_is_compatible(dev->of_node, + "brcm,cygnus-ccm-gpio")) + io_ctrl_type = IOCTRL_TYPE_CDRU; + else + io_ctrl_type = IOCTRL_TYPE_AON; } + chip->io_ctrl_type = io_ctrl_type; + if (of_property_read_u32(dev->of_node, "ngpios", &ngpios)) { dev_err(&pdev->dev, "missing ngpios DT property\n"); return -ENODEV; diff --git a/drivers/pinctrl/intel/pinctrl-baytrail.c b/drivers/pinctrl/intel/pinctrl-baytrail.c index 021e28ff1194e6bec64604fc75d1033b7faeba1b..a760d8bda0af75b21ac0904caff6e8764bbbc91c 100644 --- a/drivers/pinctrl/intel/pinctrl-baytrail.c +++ b/drivers/pinctrl/intel/pinctrl-baytrail.c @@ -950,7 +950,13 @@ static void byt_gpio_clear_triggering(struct byt_gpio *vg, unsigned int offset) raw_spin_lock_irqsave(&byt_lock, flags); value = readl(reg); - value &= ~(BYT_TRIG_POS | BYT_TRIG_NEG | BYT_TRIG_LVL); + + /* Do not clear direct-irq enabled IRQs (from gpio_disable_free) */ + if (value & BYT_DIRECT_IRQ_EN) + /* nothing to do */ ; + else + value &= ~(BYT_TRIG_POS | BYT_TRIG_NEG | BYT_TRIG_LVL); + writel(value, reg); raw_spin_unlock_irqrestore(&byt_lock, flags); } diff --git a/drivers/pinctrl/intel/pinctrl-lewisburg.c b/drivers/pinctrl/intel/pinctrl-lewisburg.c index 99894647edddc5ea1a9481f3112e970f34dd5f89..dc32c22bf19fa53b816352cf57d89816bd38d2a3 100644 --- a/drivers/pinctrl/intel/pinctrl-lewisburg.c +++ b/drivers/pinctrl/intel/pinctrl-lewisburg.c @@ -31,6 +31,7 @@ .npins = ((e) - (s) + 1), \ } +/* Lewisburg */ static const struct pinctrl_pin_desc lbg_pins[] = { /* GPP_A */ PINCTRL_PIN(0, "RCINB"), @@ -70,7 +71,7 @@ static const struct pinctrl_pin_desc lbg_pins[] = { PINCTRL_PIN(33, "SRCCLKREQB_4"), PINCTRL_PIN(34, "SRCCLKREQB_5"), PINCTRL_PIN(35, "GPP_B_11"), - PINCTRL_PIN(36, "GLB_RST_WARN_N"), + PINCTRL_PIN(36, "SLP_S0B"), PINCTRL_PIN(37, "PLTRSTB"), PINCTRL_PIN(38, "SPKR"), PINCTRL_PIN(39, "GPP_B_15"), @@ -183,96 +184,96 @@ static const struct pinctrl_pin_desc lbg_pins[] = { PINCTRL_PIN(141, "GBE_PCI_DIS"), PINCTRL_PIN(142, "GBE_LAN_DIS"), PINCTRL_PIN(143, "GPP_I_10"), - PINCTRL_PIN(144, "GPIO_RCOMP_3P3"), /* GPP_J */ - PINCTRL_PIN(145, "GBE_LED_0_0"), - PINCTRL_PIN(146, "GBE_LED_0_1"), - PINCTRL_PIN(147, "GBE_LED_1_0"), - PINCTRL_PIN(148, "GBE_LED_1_1"), - PINCTRL_PIN(149, "GBE_LED_2_0"), - PINCTRL_PIN(150, "GBE_LED_2_1"), - PINCTRL_PIN(151, "GBE_LED_3_0"), - PINCTRL_PIN(152, "GBE_LED_3_1"), - PINCTRL_PIN(153, "GBE_SCL_0"), - PINCTRL_PIN(154, "GBE_SDA_0"), - PINCTRL_PIN(155, "GBE_SCL_1"), - PINCTRL_PIN(156, "GBE_SDA_1"), - PINCTRL_PIN(157, "GBE_SCL_2"), - PINCTRL_PIN(158, "GBE_SDA_2"), - PINCTRL_PIN(159, "GBE_SCL_3"), - PINCTRL_PIN(160, "GBE_SDA_3"), - PINCTRL_PIN(161, "GBE_SDP_0_0"), - PINCTRL_PIN(162, "GBE_SDP_0_1"), - PINCTRL_PIN(163, "GBE_SDP_1_0"), - PINCTRL_PIN(164, "GBE_SDP_1_1"), - PINCTRL_PIN(165, "GBE_SDP_2_0"), - PINCTRL_PIN(166, "GBE_SDP_2_1"), - PINCTRL_PIN(167, "GBE_SDP_3_0"), - PINCTRL_PIN(168, "GBE_SDP_3_1"), + PINCTRL_PIN(144, "GBE_LED_0_0"), + PINCTRL_PIN(145, "GBE_LED_0_1"), + PINCTRL_PIN(146, "GBE_LED_1_0"), + PINCTRL_PIN(147, "GBE_LED_1_1"), + PINCTRL_PIN(148, "GBE_LED_2_0"), + PINCTRL_PIN(149, "GBE_LED_2_1"), + PINCTRL_PIN(150, "GBE_LED_3_0"), + PINCTRL_PIN(151, "GBE_LED_3_1"), + PINCTRL_PIN(152, "GBE_SCL_0"), + PINCTRL_PIN(153, "GBE_SDA_0"), + PINCTRL_PIN(154, "GBE_SCL_1"), + PINCTRL_PIN(155, "GBE_SDA_1"), + PINCTRL_PIN(156, "GBE_SCL_2"), + PINCTRL_PIN(157, "GBE_SDA_2"), + PINCTRL_PIN(158, "GBE_SCL_3"), + PINCTRL_PIN(159, "GBE_SDA_3"), + PINCTRL_PIN(160, "GBE_SDP_0_0"), + PINCTRL_PIN(161, "GBE_SDP_0_1"), + PINCTRL_PIN(162, "GBE_SDP_1_0"), + PINCTRL_PIN(163, "GBE_SDP_1_1"), + PINCTRL_PIN(164, "GBE_SDP_2_0"), + PINCTRL_PIN(165, "GBE_SDP_2_1"), + PINCTRL_PIN(166, "GBE_SDP_3_0"), + PINCTRL_PIN(167, "GBE_SDP_3_1"), /* GPP_K */ - PINCTRL_PIN(169, "GBE_RMIICLK"), - PINCTRL_PIN(170, "GBE_RMII_TXD_0"), - PINCTRL_PIN(171, "GBE_RMII_TXD_1"), + PINCTRL_PIN(168, "GBE_RMIICLK"), + PINCTRL_PIN(169, "GBE_RMII_RXD_0"), + PINCTRL_PIN(170, "GBE_RMII_RXD_1"), + PINCTRL_PIN(171, "GBE_RMII_CRS_DV"), PINCTRL_PIN(172, "GBE_RMII_TX_EN"), - PINCTRL_PIN(173, "GBE_RMII_CRS_DV"), - PINCTRL_PIN(174, "GBE_RMII_RXD_0"), - PINCTRL_PIN(175, "GBE_RMII_RXD_1"), - PINCTRL_PIN(176, "GBE_RMII_RX_ER"), - PINCTRL_PIN(177, "GBE_RMII_ARBIN"), - PINCTRL_PIN(178, "GBE_RMII_ARB_OUT"), - PINCTRL_PIN(179, "PE_RST_N"), - PINCTRL_PIN(180, "GPIO_RCOMP_1P8_3P3"), + PINCTRL_PIN(173, "GBE_RMII_TXD_0"), + PINCTRL_PIN(174, "GBE_RMII_TXD_1"), + PINCTRL_PIN(175, "GBE_RMII_RX_ER"), + PINCTRL_PIN(176, "GBE_RMII_ARBIN"), + PINCTRL_PIN(177, "GBE_RMII_ARB_OUT"), + PINCTRL_PIN(178, "PE_RST_N"), /* GPP_G */ - PINCTRL_PIN(181, "FAN_TACH_0"), - PINCTRL_PIN(182, "FAN_TACH_1"), - PINCTRL_PIN(183, "FAN_TACH_2"), - PINCTRL_PIN(184, "FAN_TACH_3"), - PINCTRL_PIN(185, "FAN_TACH_4"), - PINCTRL_PIN(186, "FAN_TACH_5"), - PINCTRL_PIN(187, "FAN_TACH_6"), - PINCTRL_PIN(188, "FAN_TACH_7"), - PINCTRL_PIN(189, "FAN_PWM_0"), - PINCTRL_PIN(190, "FAN_PWM_1"), - PINCTRL_PIN(191, "FAN_PWM_2"), - PINCTRL_PIN(192, "FAN_PWM_3"), - PINCTRL_PIN(193, "GSXDOUT"), - PINCTRL_PIN(194, "GSXSLOAD"), - PINCTRL_PIN(195, "GSXDIN"), - PINCTRL_PIN(196, "GSXSRESETB"), - PINCTRL_PIN(197, "GSXCLK"), - PINCTRL_PIN(198, "ADR_COMPLETE"), - PINCTRL_PIN(199, "NMIB"), - PINCTRL_PIN(200, "SMIB"), - PINCTRL_PIN(201, "SSATA_DEVSLP_0"), - PINCTRL_PIN(202, "SSATA_DEVSLP_1"), - PINCTRL_PIN(203, "SSATA_DEVSLP_2"), - PINCTRL_PIN(204, "SSATAXPCIE0_SSATAGP0"), + PINCTRL_PIN(179, "FAN_TACH_0"), + PINCTRL_PIN(180, "FAN_TACH_1"), + PINCTRL_PIN(181, "FAN_TACH_2"), + PINCTRL_PIN(182, "FAN_TACH_3"), + PINCTRL_PIN(183, "FAN_TACH_4"), + PINCTRL_PIN(184, "FAN_TACH_5"), + PINCTRL_PIN(185, "FAN_TACH_6"), + PINCTRL_PIN(186, "FAN_TACH_7"), + PINCTRL_PIN(187, "FAN_PWM_0"), + PINCTRL_PIN(188, "FAN_PWM_1"), + PINCTRL_PIN(189, "FAN_PWM_2"), + PINCTRL_PIN(190, "FAN_PWM_3"), + PINCTRL_PIN(191, "GSXDOUT"), + PINCTRL_PIN(192, "GSXSLOAD"), + PINCTRL_PIN(193, "GSXDIN"), + PINCTRL_PIN(194, "GSXSRESETB"), + PINCTRL_PIN(195, "GSXCLK"), + PINCTRL_PIN(196, "ADR_COMPLETE"), + PINCTRL_PIN(197, "NMIB"), + PINCTRL_PIN(198, "SMIB"), + PINCTRL_PIN(199, "SSATA_DEVSLP_0"), + PINCTRL_PIN(200, "SSATA_DEVSLP_1"), + PINCTRL_PIN(201, "SSATA_DEVSLP_2"), + PINCTRL_PIN(202, "SSATAXPCIE0_SSATAGP0"), /* GPP_H */ - PINCTRL_PIN(205, "SRCCLKREQB_6"), - PINCTRL_PIN(206, "SRCCLKREQB_7"), - PINCTRL_PIN(207, "SRCCLKREQB_8"), - PINCTRL_PIN(208, "SRCCLKREQB_9"), - PINCTRL_PIN(209, "SRCCLKREQB_10"), - PINCTRL_PIN(210, "SRCCLKREQB_11"), - PINCTRL_PIN(211, "SRCCLKREQB_12"), - PINCTRL_PIN(212, "SRCCLKREQB_13"), - PINCTRL_PIN(213, "SRCCLKREQB_14"), - PINCTRL_PIN(214, "SRCCLKREQB_15"), - PINCTRL_PIN(215, "SML2CLK"), - PINCTRL_PIN(216, "SML2DATA"), - PINCTRL_PIN(217, "SML2ALERTB"), - PINCTRL_PIN(218, "SML3CLK"), - PINCTRL_PIN(219, "SML3DATA"), - PINCTRL_PIN(220, "SML3ALERTB"), - PINCTRL_PIN(221, "SML4CLK"), - PINCTRL_PIN(222, "SML4DATA"), - PINCTRL_PIN(223, "SML4ALERTB"), - PINCTRL_PIN(224, "SSATAXPCIE1_SSATAGP1"), - PINCTRL_PIN(225, "SSATAXPCIE2_SSATAGP2"), - PINCTRL_PIN(226, "SSATAXPCIE3_SSATAGP3"), - PINCTRL_PIN(227, "SSATAXPCIE4_SSATAGP4"), - PINCTRL_PIN(228, "SSATAXPCIE5_SSATAGP5"), + PINCTRL_PIN(203, "SRCCLKREQB_6"), + PINCTRL_PIN(204, "SRCCLKREQB_7"), + PINCTRL_PIN(205, "SRCCLKREQB_8"), + PINCTRL_PIN(206, "SRCCLKREQB_9"), + PINCTRL_PIN(207, "SRCCLKREQB_10"), + PINCTRL_PIN(208, "SRCCLKREQB_11"), + PINCTRL_PIN(209, "SRCCLKREQB_12"), + PINCTRL_PIN(210, "SRCCLKREQB_13"), + PINCTRL_PIN(211, "SRCCLKREQB_14"), + PINCTRL_PIN(212, "SRCCLKREQB_15"), + PINCTRL_PIN(213, "SML2CLK"), + PINCTRL_PIN(214, "SML2DATA"), + PINCTRL_PIN(215, "SML2ALERTB"), + PINCTRL_PIN(216, "SML3CLK"), + PINCTRL_PIN(217, "SML3DATA"), + PINCTRL_PIN(218, "SML3ALERTB"), + PINCTRL_PIN(219, "SML4CLK"), + PINCTRL_PIN(220, "SML4DATA"), + PINCTRL_PIN(221, "SML4ALERTB"), + PINCTRL_PIN(222, "SSATAXPCIE1_SSATAGP1"), + PINCTRL_PIN(223, "SSATAXPCIE2_SSATAGP2"), + PINCTRL_PIN(224, "SSATAXPCIE3_SSATAGP3"), + PINCTRL_PIN(225, "SSATAXPCIE4_SSATAGP4"), + PINCTRL_PIN(226, "SSATAXPCIE5_SSATAGP5"), /* GPP_L */ + PINCTRL_PIN(227, "GPP_L_0"), + PINCTRL_PIN(228, "EC_CSME_INTR_OUT"), PINCTRL_PIN(229, "VISA2CH0_D0"), PINCTRL_PIN(230, "VISA2CH0_D1"), PINCTRL_PIN(231, "VISA2CH0_D2"), diff --git a/drivers/pinctrl/meson/pinctrl-meson-gxl.c b/drivers/pinctrl/meson/pinctrl-meson-gxl.c index 158f618f169570d07dcbd2b9850c72f7334ca495..0c0a5018102b06af979aebea9166683cb28a7e7b 100644 --- a/drivers/pinctrl/meson/pinctrl-meson-gxl.c +++ b/drivers/pinctrl/meson/pinctrl-meson-gxl.c @@ -239,13 +239,9 @@ static const unsigned int eth_link_led_pins[] = { GPIOZ_14 }; static const unsigned int eth_act_led_pins[] = { GPIOZ_15 }; static const unsigned int tsin_a_d0_pins[] = { GPIODV_0 }; -static const unsigned int tsin_a_d0_x_pins[] = { GPIOX_10 }; static const unsigned int tsin_a_clk_pins[] = { GPIODV_8 }; -static const unsigned int tsin_a_clk_x_pins[] = { GPIOX_11 }; static const unsigned int tsin_a_sop_pins[] = { GPIODV_9 }; -static const unsigned int tsin_a_sop_x_pins[] = { GPIOX_8 }; static const unsigned int tsin_a_d_valid_pins[] = { GPIODV_10 }; -static const unsigned int tsin_a_d_valid_x_pins[] = { GPIOX_9 }; static const unsigned int tsin_a_fail_pins[] = { GPIODV_11 }; static const unsigned int tsin_a_dp_pins[] = { GPIODV_1, GPIODV_2, GPIODV_3, GPIODV_4, GPIODV_5, GPIODV_6, GPIODV_7, @@ -432,10 +428,6 @@ static struct meson_pmx_group meson_gxl_periphs_groups[] = { GROUP(spi_miso, 5, 2), GROUP(spi_ss0, 5, 1), GROUP(spi_sclk, 5, 0), - GROUP(tsin_a_sop_x, 6, 3), - GROUP(tsin_a_d_valid_x, 6, 2), - GROUP(tsin_a_d0_x, 6, 1), - GROUP(tsin_a_clk_x, 6, 0), /* Bank Z */ GROUP(eth_mdio, 4, 23), @@ -698,8 +690,8 @@ static const char * const eth_led_groups[] = { }; static const char * const tsin_a_groups[] = { - "tsin_a_clk", "tsin_a_clk_x", "tsin_a_sop", "tsin_a_sop_x", - "tsin_a_d_valid", "tsin_a_d_valid_x", "tsin_a_d0", "tsin_a_d0_x", + "tsin_a_clk", "tsin_a_sop", + "tsin_a_d_valid", "tsin_a_d0", "tsin_a_dp", "tsin_a_fail", }; diff --git a/drivers/pinctrl/qcom/pinctrl-lagoon.c b/drivers/pinctrl/qcom/pinctrl-lagoon.c index d133a1a5f5dbd1a61825e869debaba308256f434..a86f9ced651f067b8cc137831e3b192d2c8dc735 100644 --- a/drivers/pinctrl/qcom/pinctrl-lagoon.c +++ b/drivers/pinctrl/qcom/pinctrl-lagoon.c @@ -42,6 +42,7 @@ .intr_cfg_reg = REG_BASE + 0x8 + REG_SIZE * id, \ .intr_status_reg = REG_BASE + 0xc + REG_SIZE * id, \ .intr_target_reg = REG_BASE + 0x8 + REG_SIZE * id, \ + .dir_conn_reg = REG_BASE + 0xBF000,\ .mux_bit = 2, \ .pull_bit = 0, \ .drv_bit = 6, \ @@ -58,6 +59,7 @@ .intr_polarity_bit = 1, \ .intr_detection_bit = 2, \ .intr_detection_width = 2, \ + .dir_conn_en_bit = 8, \ .wake_reg = REG_BASE + wake_off, \ .wake_bit = bit, \ } @@ -1614,6 +1616,15 @@ static const struct msm_pingroup lagoon_groups[] = { [163] = UFS_RESET(ufs_reset, 0x1ae000), }; +static const int lagoon_reserved_gpios[] = { + 13, 14, 15, 16, 45, 46, 56, 57, -1 +}; + +static struct msm_dir_conn lagoon_dir_conn[] = { + {-1, 0}, {-1, 0}, {-1, 0}, {-1, 0}, {-1, 0}, + {-1, 0}, {-1, 0}, {-1, 0}, {-1, 0} +}; + static const struct msm_pinctrl_soc_data lagoon_pinctrl = { .pins = lagoon_pins, .npins = ARRAY_SIZE(lagoon_pins), @@ -1621,7 +1632,9 @@ static const struct msm_pinctrl_soc_data lagoon_pinctrl = { .nfunctions = ARRAY_SIZE(lagoon_functions), .groups = lagoon_groups, .ngroups = ARRAY_SIZE(lagoon_groups), + .reserved_gpios = lagoon_reserved_gpios, .ngpios = 156, + .dir_conn = lagoon_dir_conn, }; static int lagoon_pinctrl_probe(struct platform_device *pdev) diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index 0a8ca53c8363385aa9de67c9139281f08d6011e3..4d88da68ede77d4f446ac7ab74bf637695019d5d 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2013, Sony Mobile Communications AB. - * Copyright (c) 2013-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -68,6 +68,7 @@ struct msm_pinctrl { struct irq_chip irq_chip; int irq; + int n_dir_conns; raw_spinlock_t lock; @@ -633,6 +634,25 @@ static void msm_gpio_update_dual_edge_pos(struct msm_pinctrl *pctrl, val, val2); } +static bool is_gpio_dual_edge(struct irq_data *d, irq_hw_number_t *irq) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct msm_pinctrl *pctrl = gpiochip_get_data(gc); + struct msm_dir_conn *dc; + int i; + + for (i = pctrl->n_dir_conns; i > 0; i--) { + dc = &pctrl->soc->dir_conn[i]; + + if (dc->gpio == d->hwirq) { + *irq = dc->irq; + return true; + } + } + + return false; +} + static void _msm_gpio_irq_mask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); @@ -714,9 +734,19 @@ static void msm_gpio_irq_mask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct msm_pinctrl *pctrl = gpiochip_get_data(gc); + struct irq_data *dir_conn_data; + irq_hw_number_t dir_conn_irq = 0; - if (d->parent_data) + if (d->parent_data) { + if (is_gpio_dual_edge(d, &dir_conn_irq)) { + dir_conn_data = irq_get_irq_data(dir_conn_irq); + if (!dir_conn_data) + return; + + dir_conn_data->chip->irq_mask(dir_conn_data); + } irq_chip_mask_parent(d); + } if (test_bit(d->hwirq, pctrl->wakeup_masked_irqs)) return; @@ -728,9 +758,19 @@ static void msm_gpio_irq_unmask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct msm_pinctrl *pctrl = gpiochip_get_data(gc); + struct irq_data *dir_conn_data; + irq_hw_number_t dir_conn_irq = 0; - if (d->parent_data) + if (d->parent_data) { + if (is_gpio_dual_edge(d, &dir_conn_irq)) { + dir_conn_data = irq_get_irq_data(dir_conn_irq); + if (!dir_conn_data) + return; + + dir_conn_data->chip->irq_unmask(dir_conn_data); + } irq_chip_unmask_parent(d); + } if (test_bit(d->hwirq, pctrl->wakeup_masked_irqs)) return; @@ -738,24 +778,12 @@ static void msm_gpio_irq_unmask(struct irq_data *d) _msm_gpio_irq_unmask(d, false); } -static void msm_gpio_irq_disable(struct irq_data *d) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct msm_pinctrl *pctrl = gpiochip_get_data(gc); - - if (d->parent_data) - irq_chip_disable_parent(d); - - if (test_bit(d->hwirq, pctrl->wakeup_masked_irqs)) - return; - - _msm_gpio_irq_mask(d); -} - static void msm_gpio_irq_enable(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct msm_pinctrl *pctrl = gpiochip_get_data(gc); + struct irq_data *dir_conn_data; + irq_hw_number_t dir_conn_irq = 0; if (d->parent_data) { /* @@ -767,6 +795,18 @@ static void msm_gpio_irq_enable(struct irq_data *d) * the interrupt from being latched at the GIC. The state at * GIC needs to be cleared before enabling. */ + if (is_gpio_dual_edge(d, &dir_conn_irq)) { + dir_conn_data = irq_get_irq_data(dir_conn_irq); + if (!dir_conn_data) + return; + + irq_set_irqchip_state(dir_conn_irq, + IRQCHIP_STATE_PENDING, 0); + if (dir_conn_data->chip->irq_enable) + dir_conn_data->chip->irq_enable(dir_conn_data); + else + dir_conn_data->chip->irq_unmask(dir_conn_data); + } irq_chip_set_parent_state(d, IRQCHIP_STATE_PENDING, 0); irq_chip_enable_parent(d); } @@ -806,18 +846,148 @@ static void msm_gpio_irq_ack(struct irq_data *d) raw_spin_unlock_irqrestore(&pctrl->lock, flags); } +static void msm_dirconn_cfg_reg(struct irq_data *d, u32 offset) +{ + u32 val; + const struct msm_pingroup *g; + unsigned long flags; + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct msm_pinctrl *pctrl = gpiochip_get_data(gc); + + raw_spin_lock_irqsave(&pctrl->lock, flags); + g = &pctrl->soc->groups[d->hwirq]; + + val = (d->hwirq) & 0xFF; + + writel_relaxed(val, pctrl->regs + g->dir_conn_reg + (offset * 4)); + + val = readl_relaxed(pctrl->regs + g->intr_cfg_reg); + val |= BIT(g->dir_conn_en_bit); + + writel_relaxed(val, pctrl->regs + g->intr_cfg_reg); + raw_spin_unlock_irqrestore(&pctrl->lock, flags); +} + +static void msm_dirconn_uncfg_reg(struct irq_data *d, u32 offset) +{ + u32 val = 0; + const struct msm_pingroup *g; + unsigned long flags; + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct msm_pinctrl *pctrl = gpiochip_get_data(gc); + + raw_spin_lock_irqsave(&pctrl->lock, flags); + g = &pctrl->soc->groups[d->hwirq]; + + writel_relaxed(val, pctrl->regs + g->dir_conn_reg + (offset * 4)); + val = readl_relaxed(pctrl->regs + g->intr_cfg_reg); + val &= ~BIT(g->dir_conn_en_bit); + writel_relaxed(val, pctrl->regs + g->intr_cfg_reg); + raw_spin_unlock_irqrestore(&pctrl->lock, flags); +} + +static int select_dir_conn_mux(struct irq_data *d, irq_hw_number_t *irq, + bool add) +{ + struct msm_dir_conn *dc = NULL; + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct msm_pinctrl *pctrl = gpiochip_get_data(gc); + int i, n_dir_conns = pctrl->n_dir_conns; + + for (i = n_dir_conns; i > 0; i--) { + dc = &pctrl->soc->dir_conn[i]; + if (dc->gpio == d->hwirq && !add) { + *irq = dc->irq; + dc->gpio = -1; + return n_dir_conns - i; + } + + if (dc->gpio == -1 && add) { + dc->gpio = (int)d->hwirq; + *irq = dc->irq; + return n_dir_conns - i; + } + } + + pr_err("%s: No direct connects selected for interrupt %lu\n", + __func__, d->hwirq); + return -EBUSY; +} + +static void msm_gpio_dirconn_handler(struct irq_desc *desc) +{ + struct irq_data *irqd = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + + if (!irqd) + return; + + chained_irq_enter(chip, desc); + generic_handle_irq(irqd->irq); + chained_irq_exit(chip, desc); + irq_set_irqchip_state(irq_desc_get_irq_data(desc)->irq, + IRQCHIP_STATE_ACTIVE, 0); +} + +static void add_dirconn_tlmm(struct irq_data *d, struct msm_pinctrl *pctrl) +{ + struct irq_data *dir_conn_data = NULL; + int offset = 0; + irq_hw_number_t irq = 0; + + offset = select_dir_conn_mux(d, &irq, true); + if (offset < 0) + return; + + msm_dirconn_cfg_reg(d, offset); + irq_set_handler_data(irq, d); + dir_conn_data = irq_get_irq_data(irq); + + if (!dir_conn_data) + return; + + dir_conn_data->chip->irq_unmask(dir_conn_data); +} + +static void remove_dirconn_tlmm(struct irq_data *d, irq_hw_number_t irq) +{ + struct irq_data *dir_conn_data = NULL; + int offset = 0; + + offset = select_dir_conn_mux(d, &irq, false); + if (offset < 0) + return; + + msm_dirconn_uncfg_reg(d, offset); + irq_set_handler_data(irq, NULL); + dir_conn_data = irq_get_irq_data(irq); + + if (!dir_conn_data) + return; + + dir_conn_data->chip->irq_mask(dir_conn_data); +} + static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int type) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct msm_pinctrl *pctrl = gpiochip_get_data(gc); const struct msm_pingroup *g; + irq_hw_number_t irq = 0; unsigned long flags; u32 val; g = &pctrl->soc->groups[d->hwirq]; - if (d->parent_data) + if (d->parent_data) { + if (pctrl->n_dir_conns > 0) { + if (type == IRQ_TYPE_EDGE_BOTH) + add_dirconn_tlmm(d, pctrl); + else if (is_gpio_dual_edge(d, &irq)) + remove_dirconn_tlmm(d, irq); + } irq_chip_set_type_parent(d, type); + } /* Monitored by parent wakeup controller? Keep masked */ if (test_bit(d->hwirq, pctrl->wakeup_masked_irqs)) @@ -988,8 +1158,23 @@ static int msm_gpio_init_valid_mask(struct gpio_chip *chip, int ret; unsigned int len, i; unsigned int max_gpios = pctrl->soc->ngpios; + const int *reserved = pctrl->soc->reserved_gpios; u16 *tmp; + /* Driver provided reserved list overrides */ + if (reserved) { + bitmap_fill(chip->valid_mask, max_gpios); + for (i = 0; reserved[i] >= 0; i++) { + if (i >= max_gpios || reserved[i] >= max_gpios) { + dev_err(pctrl->dev, "invalid list of reserved" + "GPIOs\n"); + return -EINVAL; + } + clear_bit(reserved[i], chip->valid_mask); + } + return 0; + } + /* The number of GPIOs in the ACPI tables */ len = ret = device_property_read_u16_array(pctrl->dev, "gpios", NULL, 0); if (ret < 0) @@ -1019,6 +1204,9 @@ static int msm_gpio_init_valid_mask(struct gpio_chip *chip, static bool msm_gpio_needs_valid_mask(struct msm_pinctrl *pctrl) { + if (pctrl->soc->reserved_gpios) + return true; + return device_property_read_u16_array(pctrl->dev, "gpios", NULL, 0) > 0; } @@ -1127,7 +1315,6 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl) pctrl->irq_chip.name = "msmgpio"; pctrl->irq_chip.irq_eoi = irq_chip_eoi_parent; pctrl->irq_chip.irq_enable = msm_gpio_irq_enable; - pctrl->irq_chip.irq_disable = msm_gpio_irq_disable; pctrl->irq_chip.irq_mask = msm_gpio_irq_mask; pctrl->irq_chip.irq_unmask = msm_gpio_irq_unmask; pctrl->irq_chip.irq_ack = msm_gpio_irq_ack; @@ -1135,7 +1322,8 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl) pctrl->irq_chip.irq_set_wake = msm_gpio_irq_set_wake; pctrl->irq_chip.irq_set_affinity = msm_gpio_irq_set_affinity; pctrl->irq_chip.irq_set_vcpu_affinity = msm_gpio_irq_set_vcpu_affinity; - + pctrl->irq_chip.flags = IRQCHIP_MASK_ON_SUSPEND + | IRQCHIP_SET_TYPE_MASKED; chip->irq.chip = &pctrl->irq_chip; chip->irq.handler = handle_edge_irq; chip->irq.default_type = IRQ_TYPE_NONE; @@ -1343,7 +1531,7 @@ int msm_pinctrl_probe(struct platform_device *pdev, { struct msm_pinctrl *pctrl; struct resource *res; - int ret; + int ret, i, num_irq, irq; msm_pinctrl_data = pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL); @@ -1388,6 +1576,18 @@ int msm_pinctrl_probe(struct platform_device *pdev, if (ret) return ret; + num_irq = platform_irq_count(pdev); + + for (i = 1; i < num_irq; i++) { + struct msm_dir_conn *dc = &soc_data->dir_conn[i]; + + irq = platform_get_irq(pdev, i); + dc->irq = irq; + __irq_set_handler(irq, msm_gpio_dirconn_handler, false, NULL); + irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING); + pctrl->n_dir_conns++; + } + platform_set_drvdata(pdev, pctrl); register_syscore_ops(&msm_pinctrl_pm_ops); diff --git a/drivers/pinctrl/qcom/pinctrl-msm.h b/drivers/pinctrl/qcom/pinctrl-msm.h index 55e73e7bc1c32570f71a8cbc6289a64f3b58de31..835b9f20228a637b33648995859f89da2f381b02 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.h +++ b/drivers/pinctrl/qcom/pinctrl-msm.h @@ -45,6 +45,7 @@ struct msm_function { * @intr_status_reg: Offset of the register holding the status bits for this group. * @intr_target_reg: Offset of the register specifying routing of the interrupts * from this group. + * @dir_conn_reg: Offset of the register hmss setup in tile. * @mux_bit: Offset in @ctl_reg for the pinmux function selection. * @pull_bit: Offset in @ctl_reg for the bias configuration. * @drv_bit: Offset in @ctl_reg for the drive strength configuration. @@ -63,6 +64,7 @@ struct msm_function { * @intr_detection_width: Number of bits used for specifying interrupt type, * Should be 2 for SoCs that can detect both edges in hardware, * otherwise 1. + * @dir_conn_en_bit: Offset in @intr_cfg_reg for direct connect enable bit * @wake_reg: Offset of the WAKEUP_INT_EN register from base tile * @wake_bit: Bit number for the corresponding gpio */ @@ -79,6 +81,7 @@ struct msm_pingroup { u32 intr_cfg_reg; u32 intr_status_reg; u32 intr_target_reg; + u32 dir_conn_reg; unsigned mux_bit:5; @@ -101,11 +104,22 @@ struct msm_pingroup { unsigned intr_polarity_bit:5; unsigned intr_detection_bit:5; unsigned intr_detection_width:5; + unsigned dir_conn_en_bit:8; u32 wake_reg; unsigned int wake_bit; }; +/** + * struct msm_dir_conn - TLMM Direct GPIO connect configuration + * @gpio: GPIO pin number + * @irq: The GIC interrupt that the pin is connected to + */ +struct msm_dir_conn { + int gpio; + int irq; +}; + /* * struct pinctrl_qup - Qup mode configuration * @mode: Qup i3c mode @@ -126,6 +140,9 @@ struct pinctrl_qup { * @ngroups: The numbmer of entries in @groups. * @ngpio: The number of pingroups the driver should expose as GPIOs. * @pull_no_keeper: The SoC does not support keeper bias. + * @wakeirq_map: The map of wakeup capable GPIOs and the pin at PDC/MPM + * @nwakeirq_map: The number of entries in @hierarchy_map + * @dir_conn: An array describing all the pins directly connected to GIC. */ struct msm_pinctrl_soc_data { const struct pinctrl_pin_desc *pins; @@ -138,6 +155,8 @@ struct msm_pinctrl_soc_data { bool pull_no_keeper; struct pinctrl_qup *qup_regs; unsigned int nqup_regs; + const int *reserved_gpios; + struct msm_dir_conn *dir_conn; }; int msm_pinctrl_probe(struct platform_device *pdev, diff --git a/drivers/pinctrl/sh-pfc/pfc-emev2.c b/drivers/pinctrl/sh-pfc/pfc-emev2.c index 1cbbe04d7df657e8fa11bfddc93433739a9a73ec..eafd8edbcbe95b7ec9ac64ab723dc9443af8016d 100644 --- a/drivers/pinctrl/sh-pfc/pfc-emev2.c +++ b/drivers/pinctrl/sh-pfc/pfc-emev2.c @@ -1263,6 +1263,14 @@ static const char * const dtv_groups[] = { "dtv_b", }; +static const char * const err_rst_reqb_groups[] = { + "err_rst_reqb", +}; + +static const char * const ext_clki_groups[] = { + "ext_clki", +}; + static const char * const iic0_groups[] = { "iic0", }; @@ -1285,6 +1293,10 @@ static const char * const lcd_groups[] = { "yuv3", }; +static const char * const lowpwr_groups[] = { + "lowpwr", +}; + static const char * const ntsc_groups[] = { "ntsc_clk", "ntsc_data", @@ -1298,6 +1310,10 @@ static const char * const pwm1_groups[] = { "pwm1", }; +static const char * const ref_clko_groups[] = { + "ref_clko", +}; + static const char * const sd_groups[] = { "sd_cki", }; @@ -1391,13 +1407,17 @@ static const struct sh_pfc_function pinmux_functions[] = { SH_PFC_FUNCTION(cam), SH_PFC_FUNCTION(cf), SH_PFC_FUNCTION(dtv), + SH_PFC_FUNCTION(err_rst_reqb), + SH_PFC_FUNCTION(ext_clki), SH_PFC_FUNCTION(iic0), SH_PFC_FUNCTION(iic1), SH_PFC_FUNCTION(jtag), SH_PFC_FUNCTION(lcd), + SH_PFC_FUNCTION(lowpwr), SH_PFC_FUNCTION(ntsc), SH_PFC_FUNCTION(pwm0), SH_PFC_FUNCTION(pwm1), + SH_PFC_FUNCTION(ref_clko), SH_PFC_FUNCTION(sd), SH_PFC_FUNCTION(sdi0), SH_PFC_FUNCTION(sdi1), diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7740.c b/drivers/pinctrl/sh-pfc/pfc-r8a7740.c index 35f436bcb849185a559f4ad4095b6914d5163f4c..e9739dbcb356e90b9e19538886f6acf732eb1e06 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a7740.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a7740.c @@ -1982,7 +1982,7 @@ static const unsigned int gether_gmii_pins[] = { */ 185, 186, 187, 188, 189, 190, 191, 192, 174, 161, 204, 171, 170, 169, 168, 167, 166, 173, 172, 176, 184, 183, 203, - 205, 163, 206, 207, + 205, 163, 206, 207, 158, }; static const unsigned int gether_gmii_mux[] = { ET_ERXD0_MARK, ET_ERXD1_MARK, ET_ERXD2_MARK, ET_ERXD3_MARK, @@ -2154,6 +2154,7 @@ static const unsigned int lcd0_data24_1_mux[] = { LCD0_D0_MARK, LCD0_D1_MARK, LCD0_D2_MARK, LCD0_D3_MARK, LCD0_D4_MARK, LCD0_D5_MARK, LCD0_D6_MARK, LCD0_D7_MARK, LCD0_D8_MARK, LCD0_D9_MARK, LCD0_D10_MARK, LCD0_D11_MARK, + LCD0_D12_MARK, LCD0_D13_MARK, LCD0_D14_MARK, LCD0_D15_MARK, LCD0_D16_MARK, LCD0_D17_MARK, LCD0_D18_PORT163_MARK, LCD0_D19_PORT162_MARK, LCD0_D20_PORT161_MARK, LCD0_D21_PORT158_MARK, LCD0_D22_PORT160_MARK, LCD0_D23_PORT159_MARK, diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7778.c b/drivers/pinctrl/sh-pfc/pfc-r8a7778.c index 00d61d175249681e3e741e0692e57245e8c25d26..c60fce1225b022872909fe6ee805f8d3034b230a 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a7778.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a7778.c @@ -2325,7 +2325,7 @@ static const struct pinmux_cfg_reg pinmux_config_regs[] = { FN_ATAG0_A, 0, FN_REMOCON_B, 0, /* IP0_11_8 [4] */ FN_SD1_DAT2_A, FN_MMC_D2, 0, FN_BS, - FN_ATADIR0_A, 0, FN_SDSELF_B, 0, + FN_ATADIR0_A, 0, FN_SDSELF_A, 0, FN_PWM4_B, 0, 0, 0, 0, 0, 0, 0, /* IP0_7_5 [3] */ @@ -2367,7 +2367,7 @@ static const struct pinmux_cfg_reg pinmux_config_regs[] = { FN_TS_SDAT0_A, 0, 0, 0, 0, 0, 0, 0, /* IP1_10_8 [3] */ - FN_SD1_CLK_B, FN_MMC_D6, 0, FN_A24, + FN_SD1_CD_A, FN_MMC_D6, 0, FN_A24, FN_DREQ1_A, 0, FN_HRX0_B, FN_TS_SPSYNC0_A, /* IP1_7_5 [3] */ FN_A23, FN_HTX0_B, FN_TX2_B, FN_DACK2_A, diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7791.c b/drivers/pinctrl/sh-pfc/pfc-r8a7791.c index 5811784d88cb966590d39d1c0bb4929ee60d3ab6..9eb860c8f91749fc3b21aabc729f7f5be2cd8be7 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a7791.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a7791.c @@ -3220,8 +3220,7 @@ static const unsigned int qspi_data4_b_pins[] = { RCAR_GP_PIN(6, 4), }; static const unsigned int qspi_data4_b_mux[] = { - SPCLK_B_MARK, MOSI_IO0_B_MARK, MISO_IO1_B_MARK, - IO2_B_MARK, IO3_B_MARK, SSL_B_MARK, + MOSI_IO0_B_MARK, MISO_IO1_B_MARK, IO2_B_MARK, IO3_B_MARK, }; /* - SCIF0 ------------------------------------------------------------------ */ static const unsigned int scif0_data_pins[] = { @@ -4375,17 +4374,14 @@ static const unsigned int vin1_b_data18_pins[] = { }; static const unsigned int vin1_b_data18_mux[] = { /* B */ - VI1_DATA0_B_MARK, VI1_DATA1_B_MARK, VI1_DATA2_B_MARK, VI1_DATA3_B_MARK, VI1_DATA4_B_MARK, VI1_DATA5_B_MARK, VI1_DATA6_B_MARK, VI1_DATA7_B_MARK, /* G */ - VI1_G0_B_MARK, VI1_G1_B_MARK, VI1_G2_B_MARK, VI1_G3_B_MARK, VI1_G4_B_MARK, VI1_G5_B_MARK, VI1_G6_B_MARK, VI1_G7_B_MARK, /* R */ - VI1_R0_B_MARK, VI1_R1_B_MARK, VI1_R2_B_MARK, VI1_R3_B_MARK, VI1_R4_B_MARK, VI1_R5_B_MARK, VI1_R6_B_MARK, VI1_R7_B_MARK, @@ -5243,7 +5239,7 @@ static const char * const scifb2_groups[] = { "scifb2_data_b", "scifb2_clk_b", "scifb2_ctrl_b", - "scifb0_data_c", + "scifb2_data_c", "scifb2_clk_c", "scifb2_data_d", }; diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7792.c b/drivers/pinctrl/sh-pfc/pfc-r8a7792.c index cc3597f66605a3e2d1960534b755f3e70062353c..46c41ca6ea38b6f958e0fcc42551a890b412c478 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a7792.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a7792.c @@ -1916,6 +1916,7 @@ static const char * const vin1_groups[] = { "vin1_data8", "vin1_data24_b", "vin1_data20_b", + "vin1_data18_b", "vin1_data16_b", "vin1_sync", "vin1_field", diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7794.c b/drivers/pinctrl/sh-pfc/pfc-r8a7794.c index 16400243759462a6e3af3e721b0a0bb62220a547..24b9bb1ee1fe5a98d5f2f28c08d274e765b9db40 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a7794.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a7794.c @@ -5215,7 +5215,7 @@ static const struct pinmux_cfg_reg pinmux_config_regs[] = { FN_AVB_MDC, FN_SSI_SDATA6_B, 0, 0, } }, { PINMUX_CFG_REG_VAR("IPSR9", 0xE6060044, 32, - 1, 3, 3, 3, 3, 2, 2, 3, 3, 3, 3, 3, 3) { + 1, 3, 3, 3, 3, 2, 2, 3, 3, 3, 3, 3) { /* IP9_31 [1] */ 0, 0, /* IP9_30_28 [3] */ diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a77970.c b/drivers/pinctrl/sh-pfc/pfc-r8a77970.c index eeb58b3bbc9a0cef4b47f65c4b8855f683e9f7e0..53fae9fd682b8792b84c05000798576ed7f1550a 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a77970.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a77970.c @@ -2354,7 +2354,7 @@ static const struct pinmux_cfg_reg pinmux_config_regs[] = { #define F_(x, y) x, #define FM(x) FN_##x, { PINMUX_CFG_REG_VAR("MOD_SEL0", 0xe6060500, 32, - 4, 4, 4, 4, + 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) { /* RESERVED 31, 30, 29, 28 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a77980.c b/drivers/pinctrl/sh-pfc/pfc-r8a77980.c index 3f6967331f64695f526ebdf6ec476143db2f1b09..81a710bb8555ee396be1028e04545af208b72e9a 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a77980.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a77980.c @@ -2751,7 +2751,7 @@ static const struct pinmux_cfg_reg pinmux_config_regs[] = { #define F_(x, y) x, #define FM(x) FN_##x, { PINMUX_CFG_REG_VAR("MOD_SEL0", 0xe6060500, 32, - 4, 4, 4, 4, + 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) { /* RESERVED 31, 30, 29, 28 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a77995.c b/drivers/pinctrl/sh-pfc/pfc-r8a77995.c index adade5b7ffbc2e985bcfd829d558584c440e073f..337c80bde8f93ff17858f6a2a9a1d45c54d7701e 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a77995.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a77995.c @@ -391,10 +391,10 @@ FM(IP12_31_28) IP12_31_28 \ #define MOD_SEL0_27 FM(SEL_MSIOF3_0) FM(SEL_MSIOF3_1) #define MOD_SEL0_26 FM(SEL_HSCIF3_0) FM(SEL_HSCIF3_1) #define MOD_SEL0_25 FM(SEL_SCIF4_0) FM(SEL_SCIF4_1) -#define MOD_SEL0_24_23 FM(SEL_PWM0_0) FM(SEL_PWM0_1) FM(SEL_PWM0_2) FM(SEL_PWM0_3) -#define MOD_SEL0_22_21 FM(SEL_PWM1_0) FM(SEL_PWM1_1) FM(SEL_PWM1_2) FM(SEL_PWM1_3) -#define MOD_SEL0_20_19 FM(SEL_PWM2_0) FM(SEL_PWM2_1) FM(SEL_PWM2_2) FM(SEL_PWM2_3) -#define MOD_SEL0_18_17 FM(SEL_PWM3_0) FM(SEL_PWM3_1) FM(SEL_PWM3_2) FM(SEL_PWM3_3) +#define MOD_SEL0_24_23 FM(SEL_PWM0_0) FM(SEL_PWM0_1) FM(SEL_PWM0_2) F_(0, 0) +#define MOD_SEL0_22_21 FM(SEL_PWM1_0) FM(SEL_PWM1_1) FM(SEL_PWM1_2) F_(0, 0) +#define MOD_SEL0_20_19 FM(SEL_PWM2_0) FM(SEL_PWM2_1) FM(SEL_PWM2_2) F_(0, 0) +#define MOD_SEL0_18_17 FM(SEL_PWM3_0) FM(SEL_PWM3_1) FM(SEL_PWM3_2) F_(0, 0) #define MOD_SEL0_15 FM(SEL_IRQ_0_0) FM(SEL_IRQ_0_1) #define MOD_SEL0_14 FM(SEL_IRQ_1_0) FM(SEL_IRQ_1_1) #define MOD_SEL0_13 FM(SEL_IRQ_2_0) FM(SEL_IRQ_2_1) diff --git a/drivers/pinctrl/sh-pfc/pfc-sh7264.c b/drivers/pinctrl/sh-pfc/pfc-sh7264.c index e1c34e19222ee7bcf12d3a98f348b9d749b312fe..3ddb9565ed8044486308ef1abe631568ffec55bd 100644 --- a/drivers/pinctrl/sh-pfc/pfc-sh7264.c +++ b/drivers/pinctrl/sh-pfc/pfc-sh7264.c @@ -500,17 +500,15 @@ enum { SD_WP_MARK, SD_CLK_MARK, SD_CMD_MARK, CRX0_MARK, CRX1_MARK, CTX0_MARK, CTX1_MARK, + CRX0_CRX1_MARK, CTX0_CTX1_MARK, PWM1A_MARK, PWM1B_MARK, PWM1C_MARK, PWM1D_MARK, PWM1E_MARK, PWM1F_MARK, PWM1G_MARK, PWM1H_MARK, PWM2A_MARK, PWM2B_MARK, PWM2C_MARK, PWM2D_MARK, PWM2E_MARK, PWM2F_MARK, PWM2G_MARK, PWM2H_MARK, IERXD_MARK, IETXD_MARK, - CRX0_CRX1_MARK, WDTOVF_MARK, - CRX0X1_MARK, - /* DMAC */ TEND0_MARK, DACK0_MARK, DREQ0_MARK, TEND1_MARK, DACK1_MARK, DREQ1_MARK, @@ -998,12 +996,12 @@ static const u16 pinmux_data[] = { PINMUX_DATA(PJ3_DATA, PJ3MD_00), PINMUX_DATA(CRX1_MARK, PJ3MD_01), - PINMUX_DATA(CRX0X1_MARK, PJ3MD_10), + PINMUX_DATA(CRX0_CRX1_MARK, PJ3MD_10), PINMUX_DATA(IRQ1_PJ_MARK, PJ3MD_11), PINMUX_DATA(PJ2_DATA, PJ2MD_000), PINMUX_DATA(CTX1_MARK, PJ2MD_001), - PINMUX_DATA(CRX0_CRX1_MARK, PJ2MD_010), + PINMUX_DATA(CTX0_CTX1_MARK, PJ2MD_010), PINMUX_DATA(CS2_MARK, PJ2MD_011), PINMUX_DATA(SCK0_MARK, PJ2MD_100), PINMUX_DATA(LCD_M_DISP_MARK, PJ2MD_101), @@ -1248,6 +1246,7 @@ static const struct pinmux_func pinmux_func_gpios[] = { GPIO_FN(CTX1), GPIO_FN(CRX1), GPIO_FN(CTX0), + GPIO_FN(CTX0_CTX1), GPIO_FN(CRX0), GPIO_FN(CRX0_CRX1), diff --git a/drivers/pinctrl/sh-pfc/pfc-sh7269.c b/drivers/pinctrl/sh-pfc/pfc-sh7269.c index a50d22bef1f444517523c6bce95dc091a7d42b9a..3df0c0d139d08f6bd889084373ecd81610f5b618 100644 --- a/drivers/pinctrl/sh-pfc/pfc-sh7269.c +++ b/drivers/pinctrl/sh-pfc/pfc-sh7269.c @@ -740,13 +740,12 @@ enum { CRX0_MARK, CTX0_MARK, CRX1_MARK, CTX1_MARK, CRX2_MARK, CTX2_MARK, - CRX0_CRX1_MARK, - CRX0_CRX1_CRX2_MARK, - CTX0CTX1CTX2_MARK, + CRX0_CRX1_MARK, CTX0_CTX1_MARK, + CRX0_CRX1_CRX2_MARK, CTX0_CTX1_CTX2_MARK, CRX1_PJ22_MARK, CTX1_PJ23_MARK, CRX2_PJ20_MARK, CTX2_PJ21_MARK, - CRX0CRX1_PJ22_MARK, - CRX0CRX1CRX2_PJ20_MARK, + CRX0_CRX1_PJ22_MARK, CTX0_CTX1_PJ23_MARK, + CRX0_CRX1_CRX2_PJ20_MARK, CTX0_CTX1_CTX2_PJ21_MARK, /* VDC */ DV_CLK_MARK, @@ -824,6 +823,7 @@ static const u16 pinmux_data[] = { PINMUX_DATA(CS3_MARK, PC8MD_001), PINMUX_DATA(TXD7_MARK, PC8MD_010), PINMUX_DATA(CTX1_MARK, PC8MD_011), + PINMUX_DATA(CTX0_CTX1_MARK, PC8MD_100), PINMUX_DATA(PC7_DATA, PC7MD_000), PINMUX_DATA(CKE_MARK, PC7MD_001), @@ -836,11 +836,12 @@ static const u16 pinmux_data[] = { PINMUX_DATA(CAS_MARK, PC6MD_001), PINMUX_DATA(SCK7_MARK, PC6MD_010), PINMUX_DATA(CTX0_MARK, PC6MD_011), + PINMUX_DATA(CTX0_CTX1_CTX2_MARK, PC6MD_100), PINMUX_DATA(PC5_DATA, PC5MD_000), PINMUX_DATA(RAS_MARK, PC5MD_001), PINMUX_DATA(CRX0_MARK, PC5MD_011), - PINMUX_DATA(CTX0CTX1CTX2_MARK, PC5MD_100), + PINMUX_DATA(CTX0_CTX1_CTX2_MARK, PC5MD_100), PINMUX_DATA(IRQ0_PC_MARK, PC5MD_101), PINMUX_DATA(PC4_DATA, PC4MD_00), @@ -1292,30 +1293,32 @@ static const u16 pinmux_data[] = { PINMUX_DATA(LCD_DATA23_PJ23_MARK, PJ23MD_010), PINMUX_DATA(LCD_TCON6_MARK, PJ23MD_011), PINMUX_DATA(IRQ3_PJ_MARK, PJ23MD_100), - PINMUX_DATA(CTX1_MARK, PJ23MD_101), + PINMUX_DATA(CTX1_PJ23_MARK, PJ23MD_101), + PINMUX_DATA(CTX0_CTX1_PJ23_MARK, PJ23MD_110), PINMUX_DATA(PJ22_DATA, PJ22MD_000), PINMUX_DATA(DV_DATA22_MARK, PJ22MD_001), PINMUX_DATA(LCD_DATA22_PJ22_MARK, PJ22MD_010), PINMUX_DATA(LCD_TCON5_MARK, PJ22MD_011), PINMUX_DATA(IRQ2_PJ_MARK, PJ22MD_100), - PINMUX_DATA(CRX1_MARK, PJ22MD_101), - PINMUX_DATA(CRX0_CRX1_MARK, PJ22MD_110), + PINMUX_DATA(CRX1_PJ22_MARK, PJ22MD_101), + PINMUX_DATA(CRX0_CRX1_PJ22_MARK, PJ22MD_110), PINMUX_DATA(PJ21_DATA, PJ21MD_000), PINMUX_DATA(DV_DATA21_MARK, PJ21MD_001), PINMUX_DATA(LCD_DATA21_PJ21_MARK, PJ21MD_010), PINMUX_DATA(LCD_TCON4_MARK, PJ21MD_011), PINMUX_DATA(IRQ1_PJ_MARK, PJ21MD_100), - PINMUX_DATA(CTX2_MARK, PJ21MD_101), + PINMUX_DATA(CTX2_PJ21_MARK, PJ21MD_101), + PINMUX_DATA(CTX0_CTX1_CTX2_PJ21_MARK, PJ21MD_110), PINMUX_DATA(PJ20_DATA, PJ20MD_000), PINMUX_DATA(DV_DATA20_MARK, PJ20MD_001), PINMUX_DATA(LCD_DATA20_PJ20_MARK, PJ20MD_010), PINMUX_DATA(LCD_TCON3_MARK, PJ20MD_011), PINMUX_DATA(IRQ0_PJ_MARK, PJ20MD_100), - PINMUX_DATA(CRX2_MARK, PJ20MD_101), - PINMUX_DATA(CRX0CRX1CRX2_PJ20_MARK, PJ20MD_110), + PINMUX_DATA(CRX2_PJ20_MARK, PJ20MD_101), + PINMUX_DATA(CRX0_CRX1_CRX2_PJ20_MARK, PJ20MD_110), PINMUX_DATA(PJ19_DATA, PJ19MD_000), PINMUX_DATA(DV_DATA19_MARK, PJ19MD_001), @@ -1666,12 +1669,24 @@ static const struct pinmux_func pinmux_func_gpios[] = { GPIO_FN(WDTOVF), /* CAN */ + GPIO_FN(CTX2), + GPIO_FN(CRX2), GPIO_FN(CTX1), GPIO_FN(CRX1), GPIO_FN(CTX0), GPIO_FN(CRX0), + GPIO_FN(CTX0_CTX1), GPIO_FN(CRX0_CRX1), + GPIO_FN(CTX0_CTX1_CTX2), GPIO_FN(CRX0_CRX1_CRX2), + GPIO_FN(CTX2_PJ21), + GPIO_FN(CRX2_PJ20), + GPIO_FN(CTX1_PJ23), + GPIO_FN(CRX1_PJ22), + GPIO_FN(CTX0_CTX1_PJ23), + GPIO_FN(CRX0_CRX1_PJ22), + GPIO_FN(CTX0_CTX1_CTX2_PJ21), + GPIO_FN(CRX0_CRX1_CRX2_PJ20), /* DMAC */ GPIO_FN(TEND0), @@ -2119,7 +2134,7 @@ static const struct pinmux_cfg_reg pinmux_config_regs[] = { }, { PINMUX_CFG_REG("PCIOR0", 0xfffe3852, 16, 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, 0, 0, PC8_IN, PC8_OUT, PC7_IN, PC7_OUT, PC6_IN, PC6_OUT, diff --git a/drivers/pinctrl/sh-pfc/pfc-sh73a0.c b/drivers/pinctrl/sh-pfc/pfc-sh73a0.c index d25e6f674d0aba84cdb3ca7ba9b555eba0729f13..6dca760f9f280dd5997cfb266a535aeeac814e03 100644 --- a/drivers/pinctrl/sh-pfc/pfc-sh73a0.c +++ b/drivers/pinctrl/sh-pfc/pfc-sh73a0.c @@ -3086,6 +3086,7 @@ static const unsigned int tpu4_to2_mux[] = { }; static const unsigned int tpu4_to3_pins[] = { /* TO */ + PIN_NUMBER(6, 26), }; static const unsigned int tpu4_to3_mux[] = { TPU4TO3_MARK, @@ -3366,7 +3367,8 @@ static const char * const fsic_groups[] = { "fsic_sclk_out", "fsic_data_in", "fsic_data_out", - "fsic_spdif", + "fsic_spdif_0", + "fsic_spdif_1", }; static const char * const fsid_groups[] = { diff --git a/drivers/pinctrl/sh-pfc/pfc-sh7734.c b/drivers/pinctrl/sh-pfc/pfc-sh7734.c index 3eccc9b3ca84aabe8ce6081355a0a3f7f7dee752..c691e5e9d9dea56d2a16dcc416cd53c0d6d4ec4a 100644 --- a/drivers/pinctrl/sh-pfc/pfc-sh7734.c +++ b/drivers/pinctrl/sh-pfc/pfc-sh7734.c @@ -2231,13 +2231,13 @@ static const struct pinmux_cfg_reg pinmux_config_regs[] = { FN_LCD_CL1_B, 0, 0, /* IP10_5_3 [3] */ FN_SSI_WS23, FN_VI1_5_B, FN_TX1_D, FN_HSCK0_C, FN_FALE_B, - FN_LCD_DON_B, 0, 0, 0, + FN_LCD_DON_B, 0, 0, /* IP10_2_0 [3] */ FN_SSI_SCK23, FN_VI1_4_B, FN_RX1_D, FN_FCLE_B, FN_LCD_DATA15_B, 0, 0, 0 } }, { PINMUX_CFG_REG_VAR("IPSR11", 0xFFFC0048, 32, - 3, 1, 2, 2, 2, 3, 3, 1, 2, 3, 3, 1, 1, 1, 1) { + 3, 1, 2, 3, 2, 2, 3, 3, 1, 2, 3, 3, 1, 1, 1, 1) { /* IP11_31_29 [3] */ 0, 0, 0, 0, 0, 0, 0, 0, /* IP11_28 [1] */ diff --git a/drivers/pinctrl/ti/pinctrl-ti-iodelay.c b/drivers/pinctrl/ti/pinctrl-ti-iodelay.c index 8782c348ebe9443d8e0ec2df3298d8100a38cb00..4eda888b4d048eabcf85e7362a0454a03247433f 100644 --- a/drivers/pinctrl/ti/pinctrl-ti-iodelay.c +++ b/drivers/pinctrl/ti/pinctrl-ti-iodelay.c @@ -496,7 +496,7 @@ static int ti_iodelay_dt_node_to_map(struct pinctrl_dev *pctldev, return -EINVAL; rows = pinctrl_count_index_with_args(np, name); - if (rows == -EINVAL) + if (rows < 0) return rows; *map = devm_kzalloc(iod->dev, sizeof(**map), GFP_KERNEL); diff --git a/drivers/platform/mips/cpu_hwmon.c b/drivers/platform/mips/cpu_hwmon.c index f66521c7f8462b3ec438f1b07beae4f0efd487cc..98128374d710ac16a6d8e271499c8111382f9ce9 100644 --- a/drivers/platform/mips/cpu_hwmon.c +++ b/drivers/platform/mips/cpu_hwmon.c @@ -159,7 +159,7 @@ static int __init loongson_hwmon_init(void) cpu_hwmon_dev = hwmon_device_register(NULL); if (IS_ERR(cpu_hwmon_dev)) { - ret = -ENOMEM; + ret = PTR_ERR(cpu_hwmon_dev); pr_err("hwmon_device_register fail!\n"); goto fail_hwmon_device_register; } diff --git a/drivers/platform/msm/gsi/gsi_emulation.h b/drivers/platform/msm/gsi/gsi_emulation.h index eead5ef55100f2f5c7fd78fa6b9d75ddf70c2049..0d10a65bb028b65634ac0b24499cfc8ee35894ef 100644 --- a/drivers/platform/msm/gsi/gsi_emulation.h +++ b/drivers/platform/msm/gsi/gsi_emulation.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. */ #if !defined(_GSI_EMULATION_H_) @@ -10,7 +10,10 @@ # include "gsi.h" # include "gsi_reg.h" + +#if defined(CONFIG_IPA_EMULATION) # include "gsi_emulation_stubs.h" +#endif # define gsi_emu_readl(c) (readl(c)) # define gsi_emu_writel(v, c) ({ __iowmb(); writel_relaxed((v), (c)); }) diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c b/drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c index 45194732e3e8ebd6dc1b0889e968fbfeca07d7b6..ad15e0d3fe71f6a8fd587115e03ed0b16358902e 100644 --- a/drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c +++ b/drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c @@ -483,6 +483,28 @@ int ipa_uc_offload_conn_pipes(struct ipa_uc_offload_conn_in_params *inp, } EXPORT_SYMBOL(ipa_uc_offload_conn_pipes); +int ipa_set_perf_profile(struct ipa_perf_profile *profile) +{ + if (!profile) { + IPA_UC_OFFLOAD_ERR("Invalid input\n"); + return -EINVAL; + } + + if (profile->client != IPA_CLIENT_ETHERNET_PROD && + profile->client != IPA_CLIENT_ETHERNET_CONS) { + IPA_UC_OFFLOAD_ERR("not supported\n"); + return -EINVAL; + } + + IPA_UC_OFFLOAD_DBG("setting throughput to %d\n", + profile->max_supported_bw_mbps); + + return ipa_pm_set_throughput( + ipa_uc_offload_ctx[IPA_UC_NTN]->pm_hdl, + profile->max_supported_bw_mbps); +} +EXPORT_SYMBOL(ipa_set_perf_profile); + static int ipa_uc_ntn_disconn_pipes(struct ipa_uc_offload_ctx *ntn_ctx) { int ipa_ep_idx_ul, ipa_ep_idx_dl; diff --git a/drivers/platform/msm/ipa/ipa_rm_inactivity_timer.c b/drivers/platform/msm/ipa/ipa_rm_inactivity_timer.c index 692587a9226658a3885805a50b7e384e85b27105..d6b8b946a80d1009c9abca5e6c3a5c1b7441e97b 100644 --- a/drivers/platform/msm/ipa/ipa_rm_inactivity_timer.c +++ b/drivers/platform/msm/ipa/ipa_rm_inactivity_timer.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2018, 2020, The Linux Foundation. All rights reserved. */ #include @@ -40,7 +40,7 @@ struct ipa_rm_it_private { bool reschedule_work; bool work_in_progress; unsigned long jiffies; - struct wakeup_source w_lock; + struct wakeup_source *w_lock; char w_lock_name[MAX_WS_NAME]; }; @@ -83,7 +83,7 @@ static void ipa_rm_inactivity_timer_func(struct work_struct *work) } else { IPA_RM_DBG_LOW("calling release_resource on resource %d\n", me->resource_name); - __pm_relax(&ipa_rm_it_handles[me->resource_name].w_lock); + __pm_relax(ipa_rm_it_handles[me->resource_name].w_lock); ipa_rm_release_resource(me->resource_name); ipa_rm_it_handles[me->resource_name].work_in_progress = false; } @@ -107,7 +107,6 @@ static void ipa_rm_inactivity_timer_func(struct work_struct *work) int ipa_rm_inactivity_timer_init(enum ipa_rm_resource_name resource_name, unsigned long msecs) { - struct wakeup_source *pwlock; char *name; IPA_RM_DBG_LOW("resource %d\n", resource_name); @@ -129,10 +128,16 @@ int ipa_rm_inactivity_timer_init(enum ipa_rm_resource_name resource_name, ipa_rm_it_handles[resource_name].resource_requested = false; ipa_rm_it_handles[resource_name].reschedule_work = false; ipa_rm_it_handles[resource_name].work_in_progress = false; - pwlock = &(ipa_rm_it_handles[resource_name].w_lock); name = ipa_rm_it_handles[resource_name].w_lock_name; snprintf(name, MAX_WS_NAME, "IPA_RM%d\n", resource_name); - wakeup_source_init(pwlock, name); + ipa_rm_it_handles[resource_name].w_lock = + wakeup_source_register(NULL, name); + if (!ipa_rm_it_handles[resource_name].w_lock) { + IPA_RM_ERR("IPA wakeup source register failed %s\n", + name); + return -ENOMEM; + } + INIT_DELAYED_WORK(&ipa_rm_it_handles[resource_name].work, ipa_rm_inactivity_timer_func); ipa_rm_it_handles[resource_name].initied = true; @@ -151,8 +156,6 @@ EXPORT_SYMBOL(ipa_rm_inactivity_timer_init); */ int ipa_rm_inactivity_timer_destroy(enum ipa_rm_resource_name resource_name) { - struct wakeup_source *pwlock; - IPA_RM_DBG_LOW("resource %d\n", resource_name); if (resource_name < 0 || @@ -168,8 +171,7 @@ int ipa_rm_inactivity_timer_destroy(enum ipa_rm_resource_name resource_name) } cancel_delayed_work_sync(&ipa_rm_it_handles[resource_name].work); - pwlock = &(ipa_rm_it_handles[resource_name].w_lock); - wakeup_source_trash(pwlock); + wakeup_source_unregister(ipa_rm_it_handles[resource_name].w_lock); memset(&ipa_rm_it_handles[resource_name], 0, sizeof(struct ipa_rm_it_private)); @@ -264,7 +266,7 @@ int ipa_rm_inactivity_timer_release_resource( } ipa_rm_it_handles[resource_name].work_in_progress = true; ipa_rm_it_handles[resource_name].reschedule_work = false; - __pm_stay_awake(&ipa_rm_it_handles[resource_name].w_lock); + __pm_stay_awake(ipa_rm_it_handles[resource_name].w_lock); IPA_RM_DBG_LOW("setting delayed work\n"); queue_delayed_work(system_unbound_wq, &ipa_rm_it_handles[resource_name].work, diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c index 7044c1024ad041d4d80f6a368451cbcb2e22e2f3..071920a6d2afdb4a3e70a6460e1663c3e52e7652 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c @@ -58,8 +58,8 @@ #define IPA_SUSPEND_BUSY_TIMEOUT (msecs_to_jiffies(10)) -#define DEFAULT_MPM_RING_SIZE_UL 6 -#define DEFAULT_MPM_RING_SIZE_DL 16 +#define DEFAULT_MPM_RING_SIZE_UL 64 +#define DEFAULT_MPM_RING_SIZE_DL 64 #define DEFAULT_MPM_TETH_AGGR_SIZE 24 #define DEFAULT_MPM_UC_THRESH_SIZE 4 @@ -5209,7 +5209,7 @@ void ipa3_inc_acquire_wakelock(void) spin_lock_irqsave(&ipa3_ctx->wakelock_ref_cnt.spinlock, flags); ipa3_ctx->wakelock_ref_cnt.cnt++; if (ipa3_ctx->wakelock_ref_cnt.cnt == 1) - __pm_stay_awake(&ipa3_ctx->w_lock); + __pm_stay_awake(ipa3_ctx->w_lock); IPADBG_LOW("active wakelock ref cnt = %d\n", ipa3_ctx->wakelock_ref_cnt.cnt); spin_unlock_irqrestore(&ipa3_ctx->wakelock_ref_cnt.spinlock, flags); @@ -5232,7 +5232,7 @@ void ipa3_dec_release_wakelock(void) IPADBG_LOW("active wakelock ref cnt = %d\n", ipa3_ctx->wakelock_ref_cnt.cnt); if (ipa3_ctx->wakelock_ref_cnt.cnt == 0) - __pm_relax(&ipa3_ctx->w_lock); + __pm_relax(ipa3_ctx->w_lock); spin_unlock_irqrestore(&ipa3_ctx->wakelock_ref_cnt.spinlock, flags); } @@ -6943,8 +6943,14 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p, goto fail_device_create; } - /* Create a wakeup source. */ - wakeup_source_init(&ipa3_ctx->w_lock, "IPA_WS"); + /* Register a wakeup source. */ + ipa3_ctx->w_lock = + wakeup_source_register(&ipa_pdev->dev, "IPA_WS"); + if (!ipa3_ctx->w_lock) { + IPAERR("IPA wakeup source register failed\n"); + result = -ENOMEM; + goto fail_w_source_register; + } spin_lock_init(&ipa3_ctx->wakelock_ref_cnt.spinlock); /* Initialize Power Management framework */ @@ -7037,6 +7043,9 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p, fail_ipa_dma_setup: ipa_pm_destroy(); fail_ipa_pm_init: + wakeup_source_unregister(ipa3_ctx->w_lock); + ipa3_ctx->w_lock = NULL; +fail_w_source_register: device_destroy(ipa3_ctx->cdev.class, ipa3_ctx->cdev.dev_num); fail_device_create: unregister_chrdev_region(ipa3_ctx->cdev.dev_num, 1); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c index 871b3d957de2a8455c45834f52e6e93db284ac22..06103be1ec56bc1eb156ec7ab8e94aa394ee625a 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c @@ -1466,6 +1466,9 @@ int ipa3_release_gsi_channel(u32 clnt_hdl) if (!ep->keep_ipa_awake) IPA_ACTIVE_CLIENTS_INC_EP(ipa3_get_client_mapping(clnt_hdl)); + /* Set the disconnect in progress flag to avoid calling cb.*/ + atomic_set(&ep->disconnect_in_progress, 1); + gsi_res = gsi_dealloc_channel(ep->gsi_chan_hdl); if (gsi_res != GSI_STATUS_SUCCESS) { IPAERR("Error deallocating channel: %d\n", gsi_res); @@ -1792,9 +1795,7 @@ int ipa3_clear_endpoint_delay(u32 clnt_hdl) /* Set disconnect in progress flag so further flow control events are * not honored. */ - spin_lock(&ipa3_ctx->disconnect_lock); - ep->disconnect_in_progress = true; - spin_unlock(&ipa3_ctx->disconnect_lock); + atomic_set(&ep->disconnect_in_progress, 1); /* If flow is disabled at this point, restore the ep state.*/ ep_ctrl.ipa_ep_delay = false; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c index bcf58a7c0a2d4ad06408e753cf1c746d17860ed8..d5017fbef271b6ad7171e9fc994cc6c7c161a7d7 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c @@ -1502,9 +1502,9 @@ static ssize_t ipa3_read_ntn(struct file *file, char __user *ubuf, size_t count, loff_t *ppos) { #define TX_STATS(y) \ - ipa3_ctx->uc_ntn_ctx.ntn_uc_stats_mmio->tx_ch_stats[0].y + stats.tx_ch_stats[0].y #define RX_STATS(y) \ - ipa3_ctx->uc_ntn_ctx.ntn_uc_stats_mmio->rx_ch_stats[0].y + stats.rx_ch_stats[0].y struct Ipa3HwStatsNTNInfoData_t stats; int nbytes; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c index 25cf98844a91fc5b6fe4dd906c122d7578fe9296..c56718d49b45d768e019fce2fca1f5c0059cee60 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c @@ -15,7 +15,7 @@ #include "ipahal/ipahal.h" #include "ipahal/ipahal_fltrt.h" -#define IPA_WAN_AGGR_PKT_CNT 5 +#define IPA_WAN_AGGR_PKT_CNT 1 #define IPA_WAN_NAPI_MAX_FRAMES (NAPI_WEIGHT / IPA_WAN_AGGR_PKT_CNT) #define IPA_WAN_PAGE_ORDER 3 #define IPA_LAN_AGGR_PKT_CNT 5 @@ -3265,11 +3265,8 @@ void ipa3_lan_rx_cb(void *priv, enum ipa_dp_evt_type evt, unsigned long data) metadata = status.metadata; ucp = status.ucp; ep = &ipa3_ctx->ep[src_pipe]; - if (unlikely(src_pipe >= ipa3_ctx->ipa_num_pipes || - !ep->valid || - !ep->client_notify)) { - IPAERR_RL("drop pipe=%d ep_valid=%d client_notify=%pK\n", - src_pipe, ep->valid, ep->client_notify); + if (unlikely(src_pipe >= ipa3_ctx->ipa_num_pipes)) { + IPAERR_RL("drop pipe=%d\n", src_pipe); dev_kfree_skb_any(rx_skb); return; } @@ -3291,7 +3288,12 @@ void ipa3_lan_rx_cb(void *priv, enum ipa_dp_evt_type evt, unsigned long data) metadata, *(u32 *)rx_skb->cb); IPADBG_LOW("ucp: %d\n", *(u8 *)(rx_skb->cb + 4)); - ep->client_notify(ep->priv, IPA_RECEIVE, (unsigned long)(rx_skb)); + if (likely((!atomic_read(&ep->disconnect_in_progress)) && + ep->valid && ep->client_notify)) + ep->client_notify(ep->priv, IPA_RECEIVE, + (unsigned long)(rx_skb)); + else + dev_kfree_skb_any(rx_skb); } static void ipa3_recycle_rx_wrapper(struct ipa3_rx_pkt_wrapper *rx_pkt) diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_hw_stats.c b/drivers/platform/msm/ipa/ipa_v3/ipa_hw_stats.c index 43fb44687ff156fba6ebca77197c4703bfe158c4..1b900a27173be5d6b1bde964cfcc48c033ebe0ee 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_hw_stats.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_hw_stats.c @@ -2235,6 +2235,81 @@ static ssize_t ipa_debugfs_print_drop_stats(struct file *file, return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes); } +static ssize_t ipa_debugfs_enable_disable_drop_stats(struct file *file, + const char __user *ubuf, size_t count, loff_t *ppos) +{ + unsigned long missing; + unsigned int pipe_num = 0; + bool enable_pipe = true; + u32 pipe_bitmask = ipa3_ctx->hw_stats.drop.init.enabled_bitmask; + char seprator = ','; + int i, j; + bool is_pipe = false; + ssize_t ret; + + mutex_lock(&ipa3_ctx->lock); + if (sizeof(dbg_buff) < count + 1) { + ret = -EFAULT; + goto bail; + } + + missing = copy_from_user(dbg_buff, ubuf, count); + if (missing) { + ret = -EFAULT; + goto bail; + } + dbg_buff[count] = '\0'; + IPADBG("data is %s", dbg_buff); + + i = 0; + while (dbg_buff[i] != ' ' && i < count) + i++; + j = i; + i++; + if (i < count) { + if (dbg_buff[i] == '0') { + enable_pipe = false; + IPADBG("Drop stats will be disabled for pipes:"); + } + } + + for (i = 0; i < j; i++) { + if (dbg_buff[i] >= '0' && dbg_buff[i] <= '9') { + pipe_num = (pipe_num * 10) + (dbg_buff[i] - '0'); + is_pipe = true; + } + if (dbg_buff[i] == seprator) { + if (pipe_num >= 0 && pipe_num < ipa3_ctx->ipa_num_pipes + && ipa3_get_client_by_pipe(pipe_num) < + IPA_CLIENT_MAX) { + IPADBG("pipe number %u\n", pipe_num); + if (enable_pipe) + pipe_bitmask = pipe_bitmask | + (1 << pipe_num); + else + pipe_bitmask = pipe_bitmask & + (~(1 << pipe_num)); + } + pipe_num = 0; + is_pipe = false; + } + } + if (is_pipe && pipe_num >= 0 && pipe_num < ipa3_ctx->ipa_num_pipes && + ipa3_get_client_by_pipe(pipe_num) < IPA_CLIENT_MAX) { + IPADBG("pipe number %u\n", pipe_num); + if (enable_pipe) + pipe_bitmask = pipe_bitmask | (1 << pipe_num); + else + pipe_bitmask = pipe_bitmask & (~(1 << pipe_num)); + } + + ipa_init_drop_stats(pipe_bitmask); + ret = count; +bail: + mutex_unlock(&ipa3_ctx->lock); + return ret; +} + static const struct file_operations ipa3_quota_ops = { .read = ipa_debugfs_print_quota_stats, .write = ipa_debugfs_reset_quota_stats, @@ -2255,10 +2330,14 @@ static const struct file_operations ipa3_drop_ops = { .write = ipa_debugfs_reset_drop_stats, }; +static const struct file_operations ipa3_enable_drop_ops = { + .write = ipa_debugfs_enable_disable_drop_stats, +}; int ipa_debugfs_init_stats(struct dentry *parent) { const mode_t read_write_mode = 0664; + const mode_t write_mode = 0220; struct dentry *file; struct dentry *dent; @@ -2285,6 +2364,13 @@ int ipa_debugfs_init_stats(struct dentry *parent) goto fail; } + file = debugfs_create_file("enable_drop_stats", write_mode, dent, NULL, + &ipa3_enable_drop_ops); + if (IS_ERR_OR_NULL(file)) { + IPAERR("fail to create file %s\n", "enable_drop_stats"); + goto fail; + } + file = debugfs_create_file("tethering", read_write_mode, dent, NULL, &ipa3_tethering_ops); if (IS_ERR_OR_NULL(file)) { diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h index a270f44b3a2aabac558d8fae34957a63f03479ea..57ddab22e49d646cb3d61cde202b888808182f7b 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h @@ -83,7 +83,7 @@ #define IPA_MAX_NUM_REQ_CACHE 10 -#define NAPI_WEIGHT 60 +#define NAPI_WEIGHT 64 #define IPADBG(fmt, args...) \ do { \ @@ -935,7 +935,7 @@ struct ipa3_ep_context { struct ipa3_wlan_stats wstats; u32 uc_offload_state; u32 gsi_offload_state; - bool disconnect_in_progress; + atomic_t disconnect_in_progress; u32 qmi_request_sent; u32 eot_in_poll_err; bool ep_delay_set; @@ -1986,7 +1986,7 @@ struct ipa3_context { bool gsi_ch20_wa; bool s1_bypass_arr[IPA_SMMU_CB_MAX]; u32 wdi_map_cnt; - struct wakeup_source w_lock; + struct wakeup_source *w_lock; struct ipa3_wakelock_ref_cnt wakelock_ref_cnt; /* RMNET_IOCTL_INGRESS_FORMAT_AGG_DATA */ bool ipa_client_apps_wan_cons_agg_gro; @@ -3230,5 +3230,5 @@ int ipa3_uc_send_enable_flow_control(uint16_t gsi_chid, int ipa3_uc_send_disable_flow_control(void); int ipa3_uc_send_update_flow_control(uint32_t bitmask, uint8_t add_delete); -int ipa3_qmi_reg_dereg_for_bw(bool bw_reg); +int ipa3_qmi_reg_dereg_for_bw(bool bw_reg, int bw_reg_dereg_type); #endif /* _IPA3_I_H_ */ diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_mpm.c b/drivers/platform/msm/ipa/ipa_v3/ipa_mpm.c index f2cdd9b5877200390ed7d165c782adf726085c6d..c20fb9e892e916073cb7bb480e8e736231e8b7ca 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_mpm.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_mpm.c @@ -72,6 +72,15 @@ #define IPA_MPM_FLOW_CTRL_DELETE 0 #define IPA_MPM_NUM_OF_INIT_CMD_DESC 2 #define IPA_UC_FC_DB_ADDR 0x1EC2088 +#define IPA_MAX_BW_REG_DEREG_CACHE 20 + +enum bw_reg_dereg_type { + BW_VOTE_WAN_NOTIFY = 0, + BW_UNVOTE_WAN_NOTIFY = 1, + BW_VOTE_PROBE_CB = 2, + BW_VOTE_XDCI_ENABLE = 3, + BW_UNVOTE_XDCI_DISABLE = 4, +}; enum mhip_re_type { MHIP_RE_XFER = 0x2, @@ -387,6 +396,11 @@ struct ipa_mpm_mhi_driver { enum ipa_mpm_remote_state remote_state; }; +struct bw_cache { + int bw_reg_dereg_type; + int ref_count; +}; + struct ipa_mpm_context { struct ipa_mpm_dev_info dev_info; struct ipa_mpm_mhi_driver md[IPA_MPM_MAX_MHIP_CHAN]; @@ -399,6 +413,8 @@ struct ipa_mpm_context { atomic_t adpl_over_odl_available; atomic_t active_teth_count; atomic_t voted_before; + struct bw_cache bw_reg_dereg_cache[IPA_MAX_BW_REG_DEREG_CACHE]; + int cache_index; struct device *parent_pdev; struct ipa_smmu_cb_ctx carved_smmu_cb; struct device *mhi_parent_dev; @@ -716,7 +732,6 @@ static void ipa_mpm_smmu_unmap(dma_addr_t carved_iova, int sz, int dir, if (carved_iova <= 0) { IPA_MPM_ERR("carved_iova is zero/negative\n"); - WARN_ON(1); return; } @@ -1554,9 +1569,8 @@ static int ipa_mpm_vote_unvote_pcie_clk(enum ipa_mpm_clk_vote_type vote, if ((atomic_read( &ipa_mpm_ctx->md[probe_id].clk_cnt.pcie_clk_cnt) == 0)) { - IPA_MPM_DBG("probe_id %d PCIE clock already devoted\n", + IPA_MPM_ERR("probe_id %d PCIE clock already devoted\n", probe_id); - WARN_ON(1); *is_acted = true; return 0; } @@ -1592,9 +1606,8 @@ static void ipa_mpm_vote_unvote_ipa_clk(enum ipa_mpm_clk_vote_type vote, if ((atomic_read (&ipa_mpm_ctx->md[probe_id].clk_cnt.ipa_clk_cnt) == 0)) { - IPA_MPM_DBG("probe_id %d IPA clock count < 0\n", + IPA_MPM_ERR("probe_id %d IPA clock count < 0\n", probe_id); - WARN_ON(1); return; } IPA_ACTIVE_CLIENTS_DEC_SPECIAL(ipa_mpm_mhip_chan_str[probe_id]); @@ -1940,7 +1953,8 @@ int ipa_mpm_notify_wan_state(struct wan_ioctl_notify_wan_state *state) ipa_mpm_ctx->md[probe_id].teth_state = IPA_MPM_TETH_CONNECTED; /* Register for BW indication from Q6 */ - if (!ipa3_qmi_reg_dereg_for_bw(true)) + if (!ipa3_qmi_reg_dereg_for_bw(true, + BW_VOTE_WAN_NOTIFY)) IPA_MPM_ERR( "Failed rgstring for QMIBW Ind, might be SSR"); break; @@ -1994,7 +2008,8 @@ int ipa_mpm_notify_wan_state(struct wan_ioctl_notify_wan_state *state) /* De-register for BW indication from Q6*/ if (atomic_read(&ipa_mpm_ctx->active_teth_count) >= 1) { - if (!ipa3_qmi_reg_dereg_for_bw(false)) + if (!ipa3_qmi_reg_dereg_for_bw(false, + BW_UNVOTE_WAN_NOTIFY)) IPA_MPM_DBG( "Failed De-rgstrng QMI BW Indctn,might be SSR"); } else { @@ -2347,7 +2362,6 @@ static int ipa_mpm_mhi_probe_cb(struct mhi_device *mhi_dev, ret = mhi_prepare_for_transfer(ipa_mpm_ctx->md[probe_id].mhi_dev); if (ret) { IPA_MPM_ERR("mhi_prepare_for_transfer failed %d\n", ret); - WARN_ON(1); /* * WA to handle prepare_for_tx failures. * Though prepare for transfer fails, indicate success @@ -2550,9 +2564,12 @@ static int ipa_mpm_mhi_probe_cb(struct mhi_device *mhi_dev, pipe_idx = ipa3_get_ep_mapping(IPA_CLIENT_USB_PROD); ipa3_xdci_ep_delay_rm(pipe_idx); /* Register for BW indication from Q6*/ - if (!ipa3_qmi_reg_dereg_for_bw(true)) - IPA_MPM_DBG( - "QMI BW reg Req failed,might be SSR"); + if (ipa_mpm_ctx->md[probe_id].teth_state == + IPA_MPM_TETH_CONNECTED) + if (!ipa3_qmi_reg_dereg_for_bw(true, + BW_VOTE_PROBE_CB)) + IPA_MPM_DBG( + "QMI BW reg Req failed,might be SSR"); } break; default: @@ -2910,7 +2927,7 @@ int ipa_mpm_mhip_xdci_pipe_enable(enum ipa_usb_teth_prot xdci_teth_prot) case MHIP_STATUS_NO_OP: ipa_mpm_change_teth_state(probe_id, IPA_MPM_TETH_CONNECTED); /* Register for BW indication from Q6*/ - if (!ipa3_qmi_reg_dereg_for_bw(true)) + if (!ipa3_qmi_reg_dereg_for_bw(true, BW_VOTE_XDCI_ENABLE)) IPA_MPM_DBG("Fail regst QMI BW Indctn,might be SSR"); pipe_idx = ipa3_get_ep_mapping(IPA_CLIENT_USB_PROD); @@ -3055,7 +3072,8 @@ int ipa_mpm_mhip_xdci_pipe_disable(enum ipa_usb_teth_prot xdci_teth_prot) ipa_mpm_change_teth_state(probe_id, IPA_MPM_TETH_INIT); /* De-register for BW indication from Q6*/ if (atomic_read(&ipa_mpm_ctx->active_teth_count) >= 1) { - if (!ipa3_qmi_reg_dereg_for_bw(false)) + if (!ipa3_qmi_reg_dereg_for_bw(false, + BW_UNVOTE_XDCI_DISABLE)) IPA_MPM_DBG( "Failed De-rgstrng QMI BW Indctn,might be SSR"); } else { @@ -3186,6 +3204,8 @@ static int ipa_mpm_probe(struct platform_device *pdev) mutex_init(&ipa_mpm_ctx->md[i].mutex); mutex_init(&ipa_mpm_ctx->md[i].mhi_mutex); } + mutex_init(&ipa_mpm_ctx->mutex); + ipa_mpm_ctx->cache_index = 0; ipa_mpm_ctx->dev_info.pdev = pdev; ipa_mpm_ctx->dev_info.dev = &pdev->dev; @@ -3484,10 +3504,21 @@ int ipa3_mpm_enable_adpl_over_odl(bool enable) return ret; } -int ipa3_qmi_reg_dereg_for_bw(bool bw_reg) +int ipa3_qmi_reg_dereg_for_bw(bool bw_reg, int bw_reg_dereg_type) { int rt; + mutex_lock(&ipa_mpm_ctx->mutex); + ipa_mpm_ctx->bw_reg_dereg_cache[ + ipa_mpm_ctx->cache_index].bw_reg_dereg_type = + bw_reg_dereg_type; + ipa_mpm_ctx->bw_reg_dereg_cache[ + ipa_mpm_ctx->cache_index].ref_count = + atomic_read(&ipa_mpm_ctx->active_teth_count); + ipa_mpm_ctx->cache_index = + (ipa_mpm_ctx->cache_index + 1) % IPA_MAX_BW_REG_DEREG_CACHE; + mutex_unlock(&ipa_mpm_ctx->mutex); + if (bw_reg) { atomic_inc(&ipa_mpm_ctx->active_teth_count); if (atomic_read(&ipa_mpm_ctx->active_teth_count) == 1) { @@ -3502,7 +3533,10 @@ int ipa3_qmi_reg_dereg_for_bw(bool bw_reg) atomic_set(&ipa_mpm_ctx->voted_before, 0); return false; } - IPA_MPM_DBG("QMI BW regst success"); + IPA_MPM_DBG("QMI BW regst success from %d", + ipa_mpm_ctx->bw_reg_dereg_cache[ + ipa_mpm_ctx->cache_index - + 1].bw_reg_dereg_type); } else { IPA_MPM_DBG("bw_change to %d no-op, teth_count = %d", bw_reg, @@ -3521,7 +3555,10 @@ int ipa3_qmi_reg_dereg_for_bw(bool bw_reg) IPA_MPM_ERR("QMI BW de-regst fail, rt= %d", rt); return false; } - IPA_MPM_DBG("QMI BW De-regst success"); + IPA_MPM_DBG("QMI BW De-regst success %d", + ipa_mpm_ctx->bw_reg_dereg_cache[ + ipa_mpm_ctx->cache_index - + 1].bw_reg_dereg_type); } else { IPA_MPM_DBG("bw_change to %d no-op, teth_count = %d", bw_reg, diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_pm.c b/drivers/platform/msm/ipa/ipa_v3/ipa_pm.c index f35abd038ef820e1df035591c1a272eda9d59e14..c6bfd81bf89d2479f8df03e2340b721a4355b70f 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_pm.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_pm.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ #include @@ -149,7 +149,7 @@ struct ipa_pm_client { struct work_struct activate_work; struct delayed_work deactivate_work; struct completion complete; - struct wakeup_source wlock; + struct wakeup_source *wlock; }; /* @@ -399,7 +399,7 @@ static void activate_work_func(struct work_struct *work) if (!client->skip_clk_vote) { IPA_ACTIVE_CLIENTS_INC_SPECIAL(client->name); if (client->group == IPA_PM_GROUP_APPS) - __pm_stay_awake(&client->wlock); + __pm_stay_awake(client->wlock); } spin_lock_irqsave(&client->state_lock, flags); @@ -421,7 +421,7 @@ static void activate_work_func(struct work_struct *work) if (!client->skip_clk_vote) { IPA_ACTIVE_CLIENTS_DEC_SPECIAL(client->name); if (client->group == IPA_PM_GROUP_APPS) - __pm_relax(&client->wlock); + __pm_relax(client->wlock); } IPA_PM_DBG_STATE(client->hdl, client->name, client->state); @@ -480,7 +480,7 @@ static void delayed_deferred_deactivate_work_func(struct work_struct *work) if (!client->skip_clk_vote) { IPA_ACTIVE_CLIENTS_DEC_SPECIAL(client->name); if (client->group == IPA_PM_GROUP_APPS) - __pm_relax(&client->wlock); + __pm_relax(client->wlock); } deactivate_client(client->hdl); @@ -704,7 +704,6 @@ int ipa_pm_destroy(void) int ipa_pm_register(struct ipa_pm_register_params *params, u32 *hdl) { struct ipa_pm_client *client; - struct wakeup_source *wlock; int elem; if (ipa_pm_ctx == NULL) { @@ -755,9 +754,13 @@ int ipa_pm_register(struct ipa_pm_register_params *params, u32 *hdl) client->group = params->group; client->hdl = *hdl; client->skip_clk_vote = params->skip_clk_vote; - wlock = &client->wlock; - wakeup_source_init(wlock, client->name); - + client->wlock = wakeup_source_register(NULL, client->name); + if (!client->wlock) { + ipa_pm_deregister(*hdl); + IPA_PM_ERR("IPA wakeup source register failed %s\n", + client->name); + return -ENOMEM; + } init_completion(&client->complete); /* add client to exception list */ @@ -822,7 +825,7 @@ int ipa_pm_deregister(u32 hdl) if (ipa_pm_ctx->clients_by_pipe[i] == ipa_pm_ctx->clients[hdl]) ipa_pm_ctx->clients_by_pipe[i] = NULL; } - wakeup_source_trash(&client->wlock); + wakeup_source_unregister(client->wlock); kfree(client); ipa_pm_ctx->clients[hdl] = NULL; @@ -941,7 +944,7 @@ static int ipa_pm_activate_helper(struct ipa_pm_client *client, bool sync) if (result == 0) { client->state = IPA_PM_ACTIVATED; if (client->group == IPA_PM_GROUP_APPS) - __pm_stay_awake(&client->wlock); + __pm_stay_awake(client->wlock); spin_unlock_irqrestore(&client->state_lock, flags); activate_client(client->hdl); if (sync) @@ -1121,7 +1124,7 @@ int ipa_pm_deactivate_all_deferred(void) if (!client->skip_clk_vote) { IPA_ACTIVE_CLIENTS_DEC_SPECIAL(client->name); if (client->group == IPA_PM_GROUP_APPS) - __pm_relax(&client->wlock); + __pm_relax(client->wlock); } deactivate_client(client->hdl); } else /* if activated or deactivated, we do nothing */ @@ -1176,7 +1179,7 @@ int ipa_pm_deactivate_sync(u32 hdl) if (!client->skip_clk_vote) { IPA_ACTIVE_CLIENTS_DEC_SPECIAL(client->name); if (client->group == IPA_PM_GROUP_APPS) - __pm_relax(&client->wlock); + __pm_relax(client->wlock); } spin_lock_irqsave(&client->state_lock, flags); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c index eb453fded02bc953897be42c2d0db6bf54c44d5d..c1e3735827fc40ab7ec4c70ad727938e92a87d5b 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c @@ -775,6 +775,44 @@ int ipa3_qmi_filter_request_send(struct ipa_install_fltr_rule_req_msg_v01 *req) resp.resp.error, "ipa_install_filter"); } +static int ipa3_qmi_filter_request_ex_calc_length( + struct ipa_install_fltr_rule_req_ex_msg_v01 *req) +{ + int len = 0; + + /* caller should validate and send the req */ + /* instead of sending max length,the approximate length is calculated */ + len += ((sizeof(struct ipa_install_fltr_rule_req_ex_msg_v01)) - + (QMI_IPA_MAX_FILTERS_EX_V01 * + sizeof(struct ipa_filter_spec_ex_type_v01) - + QMI_IPA_MAX_FILTERS_EX_V01 * sizeof(uint32_t)) - + (QMI_IPA_MAX_FILTERS_V01 * + sizeof(struct ipa_filter_spec_ex2_type_v01))); + + if (req->filter_spec_ex_list_valid && + req->filter_spec_ex_list_len > 0) { + len += sizeof(struct ipa_filter_spec_ex_type_v01)* + req->filter_spec_ex_list_len; + } + if (req->xlat_filter_indices_list_valid && + req->xlat_filter_indices_list_len > 0) { + len += sizeof(uint32_t)*req->xlat_filter_indices_list_len; + } + + if (req->filter_spec_ex2_list_valid && + req->filter_spec_ex2_list_len > 0) { + len += sizeof(struct ipa_filter_spec_ex2_type_v01)* + req->filter_spec_ex2_list_len; + } + + if (req->ul_firewall_indices_list_valid && + req->ul_firewall_indices_list_len > 0) { + len += sizeof(uint32_t)*req->ul_firewall_indices_list_len; + } + + return len; +} + /* sending filter-install-request to modem*/ int ipa3_qmi_filter_request_ex_send( struct ipa_install_fltr_rule_req_ex_msg_v01 *req) @@ -840,8 +878,9 @@ int ipa3_qmi_filter_request_ex_send( } mutex_unlock(&ipa3_qmi_lock); - req_desc.max_msg_len = - QMI_IPA_INSTALL_FILTER_RULE_EX_REQ_MAX_MSG_LEN_V01; + req_desc.max_msg_len = ipa3_qmi_filter_request_ex_calc_length(req); + IPAWANDBG("QMI send request length = %d\n", req_desc.max_msg_len); + req_desc.msg_id = QMI_IPA_INSTALL_FILTER_RULE_EX_REQ_V01; req_desc.ei_array = ipa3_install_fltr_rule_req_ex_msg_data_v01_ei; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_ntn.c b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_ntn.c index b8616b8d683b3083e78f23f8e86af88e6ec2dbd1..461f7922468a898e08a9352e8dbb26beae3680b9 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_ntn.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_ntn.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. */ #include "ipa_i.h" @@ -550,6 +550,9 @@ int ipa3_tear_down_uc_offload_pipes(int ipa_ep_idx_ul, return -EFAULT; } + atomic_set(&ep_ul->disconnect_in_progress, 1); + atomic_set(&ep_dl->disconnect_in_progress, 1); + if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) cmd.size = sizeof(*cmd_data_v4_0); else diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c index 2854c1ee0f8b944385d0b4423338ed45723bb39c..69bd9dfc2ffc2e89176ba7bd66f7ee94f7b4edf8 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c @@ -2195,6 +2195,7 @@ int ipa3_enable_gsi_wdi_pipe(u32 clnt_hdl) struct ipa3_ep_context *ep; struct ipa_ep_cfg_ctrl ep_cfg_ctrl; int ipa_ep_idx; + struct ipa_ep_cfg_holb holb_cfg; IPADBG("ep=%d\n", clnt_hdl); @@ -2215,6 +2216,18 @@ int ipa3_enable_gsi_wdi_pipe(u32 clnt_hdl) memset(&ep_cfg_ctrl, 0, sizeof(struct ipa_ep_cfg_ctrl)); ipa3_cfg_ep_ctrl(ipa_ep_idx, &ep_cfg_ctrl); + if (IPA_CLIENT_IS_CONS(ep->client)) { + memset(&holb_cfg, 0, sizeof(holb_cfg)); + holb_cfg.en = IPA_HOLB_TMR_EN; + if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_5) + holb_cfg.tmr_val = IPA_HOLB_TMR_VAL; + else + holb_cfg.tmr_val = IPA_HOLB_TMR_VAL_4_5; + + result = ipa3_cfg_ep_holb(clnt_hdl, &holb_cfg); + } + + IPA_ACTIVE_CLIENTS_DEC_EP(ipa3_get_client_mapping(clnt_hdl)); ep->gsi_offload_state |= IPA_WDI_ENABLED; IPADBG("client (ep: %d) enabled\n", clnt_hdl); diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa_fd_ioctl.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa_fd_ioctl.c index bad741fa926fa7c21c6451e7353b4bba3d633453..4089a21e0e3c42a3a85bafa9443f6f40fc6891ad 100644 --- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa_fd_ioctl.c +++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa_fd_ioctl.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2013-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2020, The Linux Foundation. All rights reserved. */ #include @@ -96,7 +96,7 @@ static long ipa3_wan_ioctl(struct file *filp, IPAWANDBG("device %s got WAN_IOC_ADD_FLT_RULE :>>>\n", DRIVER_NAME); pyld_sz = sizeof(struct ipa_install_fltr_rule_req_msg_v01); - param = memdup_user((const void __user *)arg, pyld_sz); + param = vmemdup_user((const void __user *)arg, pyld_sz); if (IS_ERR(param)) { retval = PTR_ERR(param); break; @@ -117,7 +117,7 @@ static long ipa3_wan_ioctl(struct file *filp, IPAWANDBG("device %s got WAN_IOC_ADD_FLT_RULE_EX :>>>\n", DRIVER_NAME); pyld_sz = sizeof(struct ipa_install_fltr_rule_req_ex_msg_v01); - param = memdup_user((const void __user *)arg, pyld_sz); + param = vmemdup_user((const void __user *)arg, pyld_sz); if (IS_ERR(param)) { retval = PTR_ERR(param); break; @@ -138,7 +138,7 @@ static long ipa3_wan_ioctl(struct file *filp, IPAWANDBG("device %s got WAN_IOC_ADD_OFFLOAD_CONNECTION :>>>\n", DRIVER_NAME); pyld_sz = sizeof(struct ipa_add_offload_connection_req_msg_v01); - param = memdup_user((const void __user *)arg, pyld_sz); + param = vmemdup_user((const void __user *)arg, pyld_sz); if (IS_ERR(param)) { retval = PTR_ERR(param); break; @@ -160,7 +160,7 @@ static long ipa3_wan_ioctl(struct file *filp, IPAWANDBG("device %s got WAN_IOC_RMV_OFFLOAD_CONNECTION :>>>\n", DRIVER_NAME); pyld_sz = rmv_offload_req__msg_size; - param = memdup_user((const void __user *)arg, pyld_sz); + param = vmemdup_user((const void __user *)arg, pyld_sz); if (IS_ERR(param)) { retval = PTR_ERR(param); break; @@ -183,7 +183,7 @@ static long ipa3_wan_ioctl(struct file *filp, DRIVER_NAME); pyld_sz = sizeof(struct ipa_configure_ul_firewall_rules_req_msg_v01); - param = memdup_user((const void __user *)arg, pyld_sz); + param = vmemdup_user((const void __user *)arg, pyld_sz); if (IS_ERR(param)) { retval = PTR_ERR(param); break; @@ -205,7 +205,7 @@ static long ipa3_wan_ioctl(struct file *filp, IPAWANDBG("device %s got WAN_IOC_ADD_FLT_RULE_INDEX :>>>\n", DRIVER_NAME); pyld_sz = sizeof(struct ipa_fltr_installed_notif_req_msg_v01); - param = memdup_user((const void __user *)arg, pyld_sz); + param = vmemdup_user((const void __user *)arg, pyld_sz); if (IS_ERR(param)) { retval = PTR_ERR(param); break; @@ -226,7 +226,7 @@ static long ipa3_wan_ioctl(struct file *filp, IPAWANDBG("device %s got WAN_IOC_VOTE_FOR_BW_MBPS :>>>\n", DRIVER_NAME); pyld_sz = sizeof(uint32_t); - param = memdup_user((const void __user *)arg, pyld_sz); + param = vmemdup_user((const void __user *)arg, pyld_sz); if (IS_ERR(param)) { retval = PTR_ERR(param); break; @@ -245,7 +245,7 @@ static long ipa3_wan_ioctl(struct file *filp, case WAN_IOC_POLL_TETHERING_STATS: IPAWANDBG_LOW("got WAN_IOCTL_POLL_TETHERING_STATS :>>>\n"); pyld_sz = sizeof(struct wan_ioctl_poll_tethering_stats); - param = memdup_user((const void __user *)arg, pyld_sz); + param = vmemdup_user((const void __user *)arg, pyld_sz); if (IS_ERR(param)) { retval = PTR_ERR(param); break; @@ -266,7 +266,7 @@ static long ipa3_wan_ioctl(struct file *filp, IPAWANDBG_LOW("device %s got WAN_IOCTL_SET_DATA_QUOTA :>>>\n", DRIVER_NAME); pyld_sz = sizeof(struct wan_ioctl_set_data_quota); - param = memdup_user((const void __user *)arg, pyld_sz); + param = vmemdup_user((const void __user *)arg, pyld_sz); if (IS_ERR(param)) { retval = PTR_ERR(param); break; @@ -290,7 +290,7 @@ static long ipa3_wan_ioctl(struct file *filp, case WAN_IOC_SET_TETHER_CLIENT_PIPE: IPAWANDBG_LOW("got WAN_IOC_SET_TETHER_CLIENT_PIPE :>>>\n"); pyld_sz = sizeof(struct wan_ioctl_set_tether_client_pipe); - param = memdup_user((const void __user *)arg, pyld_sz); + param = vmemdup_user((const void __user *)arg, pyld_sz); if (IS_ERR(param)) { retval = PTR_ERR(param); break; @@ -306,7 +306,7 @@ static long ipa3_wan_ioctl(struct file *filp, case WAN_IOC_QUERY_TETHER_STATS: IPAWANDBG_LOW("got WAN_IOC_QUERY_TETHER_STATS :>>>\n"); pyld_sz = sizeof(struct wan_ioctl_query_tether_stats); - param = memdup_user((const void __user *)arg, pyld_sz); + param = vmemdup_user((const void __user *)arg, pyld_sz); if (IS_ERR(param)) { retval = PTR_ERR(param); break; @@ -328,7 +328,7 @@ static long ipa3_wan_ioctl(struct file *filp, case WAN_IOC_QUERY_TETHER_STATS_ALL: IPAWANDBG_LOW("got WAN_IOC_QUERY_TETHER_STATS_ALL :>>>\n"); pyld_sz = sizeof(struct wan_ioctl_query_tether_stats_all); - param = memdup_user((const void __user *)arg, pyld_sz); + param = vmemdup_user((const void __user *)arg, pyld_sz); if (IS_ERR(param)) { retval = PTR_ERR(param); break; @@ -351,7 +351,7 @@ static long ipa3_wan_ioctl(struct file *filp, IPAWANDBG_LOW("device %s got WAN_IOC_RESET_TETHER_STATS :>>>\n", DRIVER_NAME); pyld_sz = sizeof(struct wan_ioctl_reset_tether_stats); - param = memdup_user((const void __user *)arg, pyld_sz); + param = vmemdup_user((const void __user *)arg, pyld_sz); if (IS_ERR(param)) { retval = PTR_ERR(param); break; @@ -369,7 +369,7 @@ static long ipa3_wan_ioctl(struct file *filp, IPAWANDBG_LOW("device %s got WAN_IOC_NOTIFY_WAN_STATE :>>>\n", DRIVER_NAME); pyld_sz = sizeof(struct wan_ioctl_notify_wan_state); - param = memdup_user((const void __user *)arg, pyld_sz); + param = vmemdup_user((const void __user *)arg, pyld_sz); if (IS_ERR(param)) { retval = PTR_ERR(param); break; @@ -391,7 +391,7 @@ static long ipa3_wan_ioctl(struct file *filp, case WAN_IOC_ENABLE_PER_CLIENT_STATS: IPAWANDBG_LOW("got WAN_IOC_ENABLE_PER_CLIENT_STATS :>>>\n"); pyld_sz = sizeof(bool); - param = memdup_user((const void __user *)arg, pyld_sz); + param = vmemdup_user((const void __user *)arg, pyld_sz); if (IS_ERR(param)) { retval = PTR_ERR(param); break; @@ -406,7 +406,7 @@ static long ipa3_wan_ioctl(struct file *filp, case WAN_IOC_QUERY_PER_CLIENT_STATS: IPAWANDBG_LOW("got WAN_IOC_QUERY_PER_CLIENT_STATS :>>>\n"); pyld_sz = sizeof(struct wan_ioctl_query_per_client_stats); - param = memdup_user((const void __user *)arg, pyld_sz); + param = vmemdup_user((const void __user *)arg, pyld_sz); if (IS_ERR(param)) { retval = PTR_ERR(param); break; @@ -428,7 +428,7 @@ static long ipa3_wan_ioctl(struct file *filp, case WAN_IOC_SET_LAN_CLIENT_INFO: IPAWANDBG_LOW("got WAN_IOC_SET_LAN_CLIENT_INFO :>>>\n"); pyld_sz = sizeof(struct wan_ioctl_lan_client_info); - param = memdup_user((const void __user *)arg, pyld_sz); + param = vmemdup_user((const void __user *)arg, pyld_sz); if (IS_ERR(param)) { retval = PTR_ERR(param); break; @@ -444,7 +444,7 @@ static long ipa3_wan_ioctl(struct file *filp, case WAN_IOC_CLEAR_LAN_CLIENT_INFO: IPAWANDBG_LOW("got WAN_IOC_CLEAR_LAN_CLIENT_INFO :>>>\n"); pyld_sz = sizeof(struct wan_ioctl_lan_client_info); - param = memdup_user((const void __user *)arg, pyld_sz); + param = vmemdup_user((const void __user *)arg, pyld_sz); if (IS_ERR(param)) { retval = PTR_ERR(param); break; @@ -461,7 +461,7 @@ static long ipa3_wan_ioctl(struct file *filp, case WAN_IOC_SEND_LAN_CLIENT_MSG: IPAWANDBG_LOW("got WAN_IOC_SEND_LAN_CLIENT_MSG :>>>\n"); pyld_sz = sizeof(struct wan_ioctl_send_lan_client_msg); - param = memdup_user((const void __user *)arg, pyld_sz); + param = vmemdup_user((const void __user *)arg, pyld_sz); if (IS_ERR(param)) { retval = PTR_ERR(param); break; @@ -479,7 +479,7 @@ static long ipa3_wan_ioctl(struct file *filp, retval = -ENOTTY; } if (!IS_ERR(param)) - kfree(param); + kvfree(param); return retval; } diff --git a/drivers/platform/x86/alienware-wmi.c b/drivers/platform/x86/alienware-wmi.c index f10af5c383c551d7632d0e66b751792ae9dc7abd..83fd7677af24c1f7f82aa0295eca0afcc0101d0b 100644 --- a/drivers/platform/x86/alienware-wmi.c +++ b/drivers/platform/x86/alienware-wmi.c @@ -522,23 +522,22 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args, input.length = (acpi_size) sizeof(*in_args); input.pointer = in_args; - if (out_data != NULL) { + if (out_data) { output.length = ACPI_ALLOCATE_BUFFER; output.pointer = NULL; status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0, command, &input, &output); - } else + if (ACPI_SUCCESS(status)) { + obj = (union acpi_object *)output.pointer; + if (obj && obj->type == ACPI_TYPE_INTEGER) + *out_data = (u32)obj->integer.value; + } + kfree(output.pointer); + } else { status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0, command, &input, NULL); - - if (ACPI_SUCCESS(status) && out_data != NULL) { - obj = (union acpi_object *)output.pointer; - if (obj && obj->type == ACPI_TYPE_INTEGER) - *out_data = (u32) obj->integer.value; } - kfree(output.pointer); return status; - } /* @@ -588,7 +587,7 @@ static ssize_t show_hdmi_source(struct device *dev, return scnprintf(buf, PAGE_SIZE, "input [gpu] unknown\n"); } - pr_err("alienware-wmi: unknown HDMI source status: %d\n", out_data); + pr_err("alienware-wmi: unknown HDMI source status: %u\n", status); return scnprintf(buf, PAGE_SIZE, "input gpu [unknown]\n"); } diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 22eac449d3a39019ac522bb03cf8bb01ba0535c3..339b753ba447fdd8b2d4bac7ba3cd9e9f39cf138 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -463,13 +463,7 @@ static void kbd_led_update(struct work_struct *work) asus = container_of(work, struct asus_wmi, kbd_led_work); - /* - * bits 0-2: level - * bit 7: light on/off - */ - if (asus->kbd_led_wk > 0) - ctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F); - + ctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F); asus_wmi_set_devstate(ASUS_WMI_DEVID_KBD_BACKLIGHT, ctrl_param, NULL); led_classdev_notify_brightness_hw_changed(&asus->kbd_led, asus->kbd_led_wk); } diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 3433986d52200379ff368dbefc6d9e2e63d9b43f..949dbc8aab41361cb8545f1779b1a1248a001aab 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -37,6 +37,7 @@ struct quirk_entry { bool touchpad_led; + bool kbd_led_not_present; bool kbd_led_levels_off_1; bool kbd_missing_ac_tag; @@ -77,6 +78,10 @@ static struct quirk_entry quirk_dell_latitude_e6410 = { .kbd_led_levels_off_1 = true, }; +static struct quirk_entry quirk_dell_inspiron_1012 = { + .kbd_led_not_present = true, +}; + static struct platform_driver platform_driver = { .driver = { .name = "dell-laptop", @@ -314,6 +319,24 @@ static const struct dmi_system_id dell_quirks[] __initconst = { }, .driver_data = &quirk_dell_latitude_e6410, }, + { + .callback = dmi_matched, + .ident = "Dell Inspiron 1012", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1012"), + }, + .driver_data = &quirk_dell_inspiron_1012, + }, + { + .callback = dmi_matched, + .ident = "Dell Inspiron 1018", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1018"), + }, + .driver_data = &quirk_dell_inspiron_1012, + }, { } }; @@ -1497,6 +1520,9 @@ static void kbd_init(void) { int ret; + if (quirks && quirks->kbd_led_not_present) + return; + ret = kbd_init_info(); kbd_init_tokens(); diff --git a/drivers/platform/x86/gpd-pocket-fan.c b/drivers/platform/x86/gpd-pocket-fan.c index be85ed966bf33bd7b809440f53dd0dac1b405273..b471b86c28fe8d2e15aca3766de871e2dfececad 100644 --- a/drivers/platform/x86/gpd-pocket-fan.c +++ b/drivers/platform/x86/gpd-pocket-fan.c @@ -16,17 +16,27 @@ #define MAX_SPEED 3 -static int temp_limits[3] = { 55000, 60000, 65000 }; +#define TEMP_LIMIT0_DEFAULT 55000 +#define TEMP_LIMIT1_DEFAULT 60000 +#define TEMP_LIMIT2_DEFAULT 65000 + +#define HYSTERESIS_DEFAULT 3000 + +#define SPEED_ON_AC_DEFAULT 2 + +static int temp_limits[3] = { + TEMP_LIMIT0_DEFAULT, TEMP_LIMIT1_DEFAULT, TEMP_LIMIT2_DEFAULT, +}; module_param_array(temp_limits, int, NULL, 0444); MODULE_PARM_DESC(temp_limits, "Millicelsius values above which the fan speed increases"); -static int hysteresis = 3000; +static int hysteresis = HYSTERESIS_DEFAULT; module_param(hysteresis, int, 0444); MODULE_PARM_DESC(hysteresis, "Hysteresis in millicelsius before lowering the fan speed"); -static int speed_on_ac = 2; +static int speed_on_ac = SPEED_ON_AC_DEFAULT; module_param(speed_on_ac, int, 0444); MODULE_PARM_DESC(speed_on_ac, "minimum fan speed to allow when system is powered by AC"); @@ -117,21 +127,24 @@ static int gpd_pocket_fan_probe(struct platform_device *pdev) int i; for (i = 0; i < ARRAY_SIZE(temp_limits); i++) { - if (temp_limits[i] < 40000 || temp_limits[i] > 70000) { + if (temp_limits[i] < 20000 || temp_limits[i] > 90000) { dev_err(&pdev->dev, "Invalid temp-limit %d (must be between 40000 and 70000)\n", temp_limits[i]); - return -EINVAL; + temp_limits[0] = TEMP_LIMIT0_DEFAULT; + temp_limits[1] = TEMP_LIMIT1_DEFAULT; + temp_limits[2] = TEMP_LIMIT2_DEFAULT; + break; } } if (hysteresis < 1000 || hysteresis > 10000) { dev_err(&pdev->dev, "Invalid hysteresis %d (must be between 1000 and 10000)\n", hysteresis); - return -EINVAL; + hysteresis = HYSTERESIS_DEFAULT; } if (speed_on_ac < 0 || speed_on_ac > MAX_SPEED) { dev_err(&pdev->dev, "Invalid speed_on_ac %d (must be between 0 and 3)\n", speed_on_ac); - return -EINVAL; + speed_on_ac = SPEED_ON_AC_DEFAULT; } fan = devm_kzalloc(&pdev->dev, sizeof(*fan), GFP_KERNEL); diff --git a/drivers/platform/x86/intel_cht_int33fe.c b/drivers/platform/x86/intel_cht_int33fe.c index f40b1c1921064b734614705c72dc212700f979fc..13eeb1018017aee806b18727c894b61141e5aaed 100644 --- a/drivers/platform/x86/intel_cht_int33fe.c +++ b/drivers/platform/x86/intel_cht_int33fe.c @@ -35,7 +35,7 @@ struct cht_int33fe_data { struct i2c_client *fusb302; struct i2c_client *pi3usb30532; /* Contain a list-head must be per device */ - struct device_connection connections[5]; + struct device_connection connections[4]; }; /* @@ -176,17 +176,14 @@ static int cht_int33fe_probe(struct platform_device *pdev) } data->connections[0].endpoint[0] = "port0"; - data->connections[0].endpoint[1] = "i2c-pi3usb30532"; - data->connections[0].id = "typec-switch"; + data->connections[0].endpoint[1] = "i2c-pi3usb30532-switch"; + data->connections[0].id = "orientation-switch"; data->connections[1].endpoint[0] = "port0"; - data->connections[1].endpoint[1] = "i2c-pi3usb30532"; - data->connections[1].id = "typec-mux"; - data->connections[2].endpoint[0] = "port0"; - data->connections[2].endpoint[1] = "i2c-pi3usb30532"; - data->connections[2].id = "idff01m01"; - data->connections[3].endpoint[0] = "i2c-fusb302"; - data->connections[3].endpoint[1] = "intel_xhci_usb_sw-role-switch"; - data->connections[3].id = "usb-role-switch"; + data->connections[1].endpoint[1] = "i2c-pi3usb30532-mux"; + data->connections[1].id = "mode-switch"; + data->connections[2].endpoint[0] = "i2c-fusb302"; + data->connections[2].endpoint[1] = "intel_xhci_usb_sw-role-switch"; + data->connections[2].id = "usb-role-switch"; device_connections_add(data->connections); diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c index 5ad44204a9c3c997bf237aacfb1bbb99ab292ef7..10dbd6cac48aedea426d0cf8b525c7717ffd23b4 100644 --- a/drivers/platform/x86/intel_mid_powerbtn.c +++ b/drivers/platform/x86/intel_mid_powerbtn.c @@ -158,9 +158,10 @@ static int mid_pb_probe(struct platform_device *pdev) input_set_capability(input, EV_KEY, KEY_POWER); - ddata = (struct mid_pb_ddata *)id->driver_data; + ddata = devm_kmemdup(&pdev->dev, (void *)id->driver_data, + sizeof(*ddata), GFP_KERNEL); if (!ddata) - return -ENODATA; + return -ENOMEM; ddata->dev = &pdev->dev; ddata->irq = irq; diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index 75c8fef7a482c41717c69aa8ef98b21ef4dd1043..54f131bec192c647adface1586f2e0755d8d8498 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c @@ -69,26 +69,22 @@ struct intel_scu_ipc_pdata_t { u32 i2c_base; u32 i2c_len; - u8 irq_mode; }; static const struct intel_scu_ipc_pdata_t intel_scu_ipc_lincroft_pdata = { .i2c_base = 0xff12b000, .i2c_len = 0x10, - .irq_mode = 0, }; /* Penwell and Cloverview */ static const struct intel_scu_ipc_pdata_t intel_scu_ipc_penwell_pdata = { .i2c_base = 0xff12b000, .i2c_len = 0x10, - .irq_mode = 1, }; static const struct intel_scu_ipc_pdata_t intel_scu_ipc_tangier_pdata = { .i2c_base = 0xff00d000, .i2c_len = 0x10, - .irq_mode = 0, }; struct intel_scu_ipc_dev { @@ -101,6 +97,9 @@ struct intel_scu_ipc_dev { static struct intel_scu_ipc_dev ipcdev; /* Only one for now */ +#define IPC_STATUS 0x04 +#define IPC_STATUS_IRQ BIT(2) + /* * IPC Read Buffer (Read Only): * 16 byte buffer for receiving data from SCU, if IPC command @@ -122,11 +121,8 @@ static DEFINE_MUTEX(ipclock); /* lock used to prevent multiple call to SCU */ */ static inline void ipc_command(struct intel_scu_ipc_dev *scu, u32 cmd) { - if (scu->irq_mode) { - reinit_completion(&scu->cmd_complete); - writel(cmd | IPC_IOC, scu->ipc_base); - } - writel(cmd, scu->ipc_base); + reinit_completion(&scu->cmd_complete); + writel(cmd | IPC_IOC, scu->ipc_base); } /* @@ -612,9 +608,10 @@ EXPORT_SYMBOL(intel_scu_ipc_i2c_cntrl); static irqreturn_t ioc(int irq, void *dev_id) { struct intel_scu_ipc_dev *scu = dev_id; + int status = ipc_read_status(scu); - if (scu->irq_mode) - complete(&scu->cmd_complete); + writel(status | IPC_STATUS_IRQ, scu->ipc_base + IPC_STATUS); + complete(&scu->cmd_complete); return IRQ_HANDLED; } @@ -640,8 +637,6 @@ static int ipc_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (!pdata) return -ENODEV; - scu->irq_mode = pdata->irq_mode; - err = pcim_enable_device(pdev); if (err) return err; diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 04791ea5d97b69f9ed9fc30fb7de46cf2370acbe..35cdc3998eb5914d6841abda625e0371d9f79a15 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -768,6 +768,9 @@ static int wmi_dev_match(struct device *dev, struct device_driver *driver) struct wmi_block *wblock = dev_to_wblock(dev); const struct wmi_device_id *id = wmi_driver->id_table; + if (id == NULL) + return 0; + while (id->guid_string) { uuid_le driver_guid; diff --git a/drivers/power/supply/ltc2941-battery-gauge.c b/drivers/power/supply/ltc2941-battery-gauge.c index 4f129bb4c9729e9ab085cf465f636ab6395daf1f..ff5febea1a211ed95c627dfa00c9416d00ea0197 100644 --- a/drivers/power/supply/ltc2941-battery-gauge.c +++ b/drivers/power/supply/ltc2941-battery-gauge.c @@ -448,7 +448,7 @@ static int ltc294x_i2c_remove(struct i2c_client *client) { struct ltc294x_info *info = i2c_get_clientdata(client); - cancel_delayed_work(&info->work); + cancel_delayed_work_sync(&info->work); power_supply_unregister(info->supply); return 0; } diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c index 8a119caac50da28331200fc73438db3fba977c5f..0714dad3469d752d72c59a203ee6522174b5d8f6 100644 --- a/drivers/power/supply/qcom/battery.c +++ b/drivers/power/supply/qcom/battery.c @@ -1998,7 +1998,7 @@ int qcom_batt_init(struct charger_param *chg_param) pl_config_init(chip, chg_param->smb_version); chip->restricted_current = DEFAULT_RESTRICTED_CURRENT_UA; - chip->pl_ws = wakeup_source_register("qcom-battery"); + chip->pl_ws = wakeup_source_register(NULL, "qcom-battery"); if (!chip->pl_ws) goto cleanup; diff --git a/drivers/power/supply/qcom/qg-core.h b/drivers/power/supply/qcom/qg-core.h index 8704699203b3b0621251729999f5917c69ff567d..b5716c3fb73833e07db5f3eb8fabe7a8a962a50b 100644 --- a/drivers/power/supply/qcom/qg-core.h +++ b/drivers/power/supply/qcom/qg-core.h @@ -66,6 +66,7 @@ struct qg_dt { bool esr_disable; bool esr_discharge_enable; bool qg_ext_sense; + bool use_cp_iin_sns; bool use_s7_ocv; bool qg_sleep_config; bool qg_fast_chg_cfg; @@ -122,6 +123,7 @@ struct qpnp_qg { struct power_supply *usb_psy; struct power_supply *dc_psy; struct power_supply *parallel_psy; + struct power_supply *cp_psy; struct qg_esr_data esr_data[QG_MAX_ESR_COUNT]; /* status variable */ diff --git a/drivers/power/supply/qcom/qg-util.c b/drivers/power/supply/qcom/qg-util.c index 170ca876d9b728f4130dfe40ff7b36da7242128d..36b5fefa42aa069df36b9186d508c650cbb778d3 100644 --- a/drivers/power/supply/qcom/qg-util.c +++ b/drivers/power/supply/qcom/qg-util.c @@ -307,7 +307,7 @@ bool is_input_present(struct qpnp_qg *chip) return is_usb_present(chip) || is_dc_present(chip); } -static bool is_parallel_available(struct qpnp_qg *chip) +bool is_parallel_available(struct qpnp_qg *chip) { if (chip->parallel_psy) return true; @@ -319,6 +319,18 @@ static bool is_parallel_available(struct qpnp_qg *chip) return true; } +bool is_cp_available(struct qpnp_qg *chip) +{ + if (chip->cp_psy) + return true; + + chip->cp_psy = power_supply_get_by_name("charge_pump_master"); + if (!chip->cp_psy) + return false; + + return true; +} + bool is_parallel_enabled(struct qpnp_qg *chip) { union power_supply_propval pval = {0, }; @@ -326,6 +338,9 @@ bool is_parallel_enabled(struct qpnp_qg *chip) if (is_parallel_available(chip)) { power_supply_get_property(chip->parallel_psy, POWER_SUPPLY_PROP_CHARGING_ENABLED, &pval); + } else if (is_cp_available(chip)) { + power_supply_get_property(chip->cp_psy, + POWER_SUPPLY_PROP_CP_ENABLE, &pval); } return pval.intval ? true : false; diff --git a/drivers/power/supply/qcom/qg-util.h b/drivers/power/supply/qcom/qg-util.h index 7a1892b0e627ccea5c1050258028e8c30b48185d..ae9ef81f9ed9cf0255deb009d665d2fb7acbc892 100644 --- a/drivers/power/supply/qcom/qg-util.h +++ b/drivers/power/supply/qcom/qg-util.h @@ -19,6 +19,8 @@ bool is_usb_present(struct qpnp_qg *chip); bool is_dc_present(struct qpnp_qg *chip); bool is_input_present(struct qpnp_qg *chip); bool is_parallel_enabled(struct qpnp_qg *chip); +bool is_cp_available(struct qpnp_qg *chip); +bool is_parallel_available(struct qpnp_qg *chip); int qg_write_monotonic_soc(struct qpnp_qg *chip, int msoc); int qg_get_battery_temp(struct qpnp_qg *chip, int *batt_temp); int qg_get_battery_current(struct qpnp_qg *chip, int *ibat_ua); diff --git a/drivers/power/supply/qcom/qpnp-qg.c b/drivers/power/supply/qcom/qpnp-qg.c index 0acae678b550a4935dd6eb63f9ee9cd50c17f719..956f75f5b3516e705eb50acaafa2b611fb88e1e8 100644 --- a/drivers/power/supply/qcom/qpnp-qg.c +++ b/drivers/power/supply/qcom/qpnp-qg.c @@ -2442,13 +2442,15 @@ static int qg_parallel_status_update(struct qpnp_qg *chip) "Parallel status changed Enabled=%d\n", parallel_enabled); mutex_lock(&chip->data_lock); - /* - * Parallel charger uses the same external sense, hence do not - * enable SMB sensing if PMI632 is configured for external sense. + * dt.qg_ext_sense = Uses external rsense, if defined do not + * enable SMB sensing (for non-CP parallel charger). + * dt.cp_iin_sns = Uses CP IIN_SNS, enable SMB sensing (for CP charger). */ - if (!chip->dt.qg_ext_sense) - update_smb = true; + if (is_cp_available(chip)) + update_smb = chip->dt.use_cp_iin_sns ? true : false; + else if (is_parallel_available(chip)) + update_smb = chip->dt.qg_ext_sense ? false : true; rc = process_rt_fifo_data(chip, update_smb); if (rc < 0) @@ -2699,7 +2701,8 @@ static int qg_notifier_cb(struct notifier_block *nb, if ((strcmp(psy->desc->name, "battery") == 0) || (strcmp(psy->desc->name, "parallel") == 0) || (strcmp(psy->desc->name, "usb") == 0) - || (strcmp(psy->desc->name, "dc") == 0)) { + || (strcmp(psy->desc->name, "dc") == 0) + || (strcmp(psy->desc->name, "charge_pump_master") == 0)) { /* * We cannot vote for awake votable here as that takes * a mutex lock and this is executed in an atomic context. @@ -4340,6 +4343,9 @@ static int qg_parse_dt(struct qpnp_qg *chip) chip->dt.qg_ext_sense = of_property_read_bool(node, "qcom,qg-ext-sns"); + chip->dt.use_cp_iin_sns = of_property_read_bool(node, + "qcom,use-cp-iin-sns"); + chip->dt.use_s7_ocv = of_property_read_bool(node, "qcom,qg-use-s7-ocv"); rc = of_property_read_u32(node, "qcom,min-sleep-time-secs", &temp); @@ -4427,6 +4433,7 @@ static int process_suspend(struct qpnp_qg *chip) return rc; } sleep_fifo_length &= SLEEP_IBAT_QUALIFIED_LENGTH_MASK; + sleep_fifo_length++; if (chip->dt.qg_sleep_config) { qg_dbg(chip, QG_DEBUG_STATUS, "Suspend: Forcing S2_SLEEP\n"); diff --git a/drivers/power/supply/qcom/qpnp-smblite.c b/drivers/power/supply/qcom/qpnp-smblite.c index 5f8c7146e5c953e5bcda659e56e2f80e8ed95552..0d81fa10007eec000070b1a70564e830d0acdd83 100644 --- a/drivers/power/supply/qcom/qpnp-smblite.c +++ b/drivers/power/supply/qcom/qpnp-smblite.c @@ -433,9 +433,6 @@ static int smblite_usb_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_SCOPE: rc = smblite_lib_get_prop_scope(chg, val); break; - case POWER_SUPPLY_PROP_FLASH_TRIGGER: - rc = schgm_flashlite_get_vreg_ok(chg, &val->intval); - break; default: pr_err("get prop %d is not supported in usb\n", psp); rc = -EINVAL; @@ -530,6 +527,7 @@ static enum power_supply_property smblite_usb_main_props[] = { POWER_SUPPLY_PROP_FCC_DELTA, POWER_SUPPLY_PROP_CURRENT_MAX, POWER_SUPPLY_PROP_FLASH_TRIGGER, + POWER_SUPPLY_PROP_FLASH_ACTIVE, }; static int smblite_usb_main_get_prop(struct power_supply *psy, @@ -564,6 +562,12 @@ static int smblite_usb_main_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_CURRENT_MAX: rc = smblite_lib_get_icl_current(chg, &val->intval); break; + case POWER_SUPPLY_PROP_FLASH_TRIGGER: + rc = schgm_flashlite_get_vreg_ok(chg, &val->intval); + break; + case POWER_SUPPLY_PROP_FLASH_ACTIVE: + val->intval = chg->flash_active; + break; default: pr_debug("get prop %d is not supported in usb-main\n", psp); rc = -EINVAL; @@ -582,6 +586,7 @@ static int smblite_usb_main_set_prop(struct power_supply *psy, struct smblite *chip = power_supply_get_drvdata(psy); struct smb_charger *chg = &chip->chg; int rc = 0; + union power_supply_propval pval = {0, }; switch (psp) { case POWER_SUPPLY_PROP_VOLTAGE_MAX: @@ -595,6 +600,21 @@ static int smblite_usb_main_set_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_CURRENT_MAX: rc = smblite_lib_set_icl_current(chg, val->intval); break; + case POWER_SUPPLY_PROP_FLASH_ACTIVE: + if (chg->flash_active != val->intval) { + chg->flash_active = val->intval; + + rc = smblite_lib_get_prop_usb_present(chg, &pval); + if (rc < 0) + pr_err("Failed to get USB present status rc=%d\n", + rc); + if (!pval.intval) { + /* vote 100ma when usb is not present*/ + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, + true, USBIN_100UA); + } + } + break; default: pr_err("set prop %d is not supported\n", psp); rc = -EINVAL; @@ -972,7 +992,8 @@ static int smblite_configure_typec(struct smb_charger *chg) } rc = smblite_lib_masked_write(chg, TYPE_C_MODE_CFG_REG, - EN_SNK_ONLY_BIT, 0); + EN_TRY_SNK_BIT | EN_SNK_ONLY_BIT, + EN_TRY_SNK_BIT); if (rc < 0) { dev_err(chg->dev, "Couldn't configure TYPE_C_MODE_CFG_REG rc=%d\n", diff --git a/drivers/power/supply/qcom/smb1390-charger-psy.c b/drivers/power/supply/qcom/smb1390-charger-psy.c index 833c8b64c84d1c3d877c08d9af1a3f815ecaa813..797f0c5c5405f75fc0cd0ca44ab91dc1f37f5d14 100644 --- a/drivers/power/supply/qcom/smb1390-charger-psy.c +++ b/drivers/power/supply/qcom/smb1390-charger-psy.c @@ -1885,7 +1885,7 @@ static int smb1390_master_probe(struct smb1390 *chip) return rc; } - chip->cp_ws = wakeup_source_register("qcom-chargepump"); + chip->cp_ws = wakeup_source_register(NULL, "qcom-chargepump"); if (!chip->cp_ws) return -ENOMEM; diff --git a/drivers/power/supply/qcom/smb1398-charger.c b/drivers/power/supply/qcom/smb1398-charger.c index 2c2e339ee7ba0e6fd82e3d9ecb91c393c80be81e..8b2ce9c2f0ab7098061c2c122e9859ec3887c665 100644 --- a/drivers/power/supply/qcom/smb1398-charger.c +++ b/drivers/power/supply/qcom/smb1398-charger.c @@ -19,6 +19,8 @@ #include /* Status register definition */ +#define PERPH0_REVISION4 0x2603 + #define INPUT_STATUS_REG 0x2609 #define INPUT_USB_IN BIT(1) #define INPUT_WLS_IN BIT(0) @@ -173,6 +175,10 @@ #define PERPH0_CFG_SDCDC_REG 0x267A #define EN_WIN_UV_BIT BIT(7) +#define PERPH0_SSUPPLY_CFG0_REG 0x2682 +#define EN_HV_OV_OPTION2_BIT BIT(7) +#define EN_MV_OV_OPTION2_BIT BIT(5) + #define SSUPLY_TEMP_CTRL_REG 0x2683 #define SEL_OUT_TEMP_MAX_MASK GENMASK(7, 5) #define SEL_OUT_TEMP_MAX_SHFT 5 @@ -187,6 +193,10 @@ #define DIV2_ILIM_STS BIT(5) #define DIV2_CFLY_SS_DONE_STS BIT(1) +#define PERPH1_LOCK_SPARE_REG 0x27C3 +#define CFG_LOCK_SPARE1_MASK GENMASK(7, 6) +#define CFG_LOCK_SPARE1_SHIFT 6 + /* available voters */ #define ILIM_VOTER "ILIM_VOTER" #define TAPER_VOTER "TAPER_VOTER" @@ -228,6 +238,13 @@ enum isns_mode { ISNS_MODE_STANDBY, }; +enum ovp { + OVP_17P7V = 0, + OVP_14V, + OVP_22P2V, + OVP_7P3, +}; + enum { /* Perph0 IRQs */ CFLY_HARD_FAULT_LATCH_IRQ, @@ -917,6 +934,24 @@ static int div2_cp_master_get_prop_suspended(struct smb1398_chip *chip, return 0; } +#define DEFAULT_HVDCP3_MIN_ICL_UA 1000000 +static int smb1398_div2_cp_get_min_icl(struct smb1398_chip *chip) +{ + union power_supply_propval pval; + int rc; + + /* Use max(dt_min_icl, 1A) for HVDCP3 */ + if (chip->usb_psy) { + rc = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_REAL_TYPE, &pval); + if (rc >= 0 && (pval.intval == POWER_SUPPLY_TYPE_USB_HVDCP_3)) + return max(chip->div2_cp_min_ilim_ua, + DEFAULT_HVDCP3_MIN_ICL_UA); + } + + return chip->div2_cp_min_ilim_ua; +} + static int div2_cp_master_get_prop(struct power_supply *psy, enum power_supply_property prop, union power_supply_propval *val) @@ -1017,7 +1052,7 @@ static int div2_cp_master_get_prop(struct power_supply *psy, val->intval = chip->pl_output_mode; break; case POWER_SUPPLY_PROP_MIN_ICL: - val->intval = chip->div2_cp_min_ilim_ua; + val->intval = smb1398_div2_cp_get_min_icl(chip); break; default: rc = -EINVAL; @@ -1316,7 +1351,7 @@ static int smb1398_div2_cp_ilim_vote_cb(struct votable *votable, { struct smb1398_chip *chip = (struct smb1398_chip *)data; union power_supply_propval pval = {0}; - int rc = 0, max_ilim_ua; + int rc = 0, max_ilim_ua, min_ilim_ua; bool slave_dis, split_ilim = false; if (!is_psy_voter_available(chip) || chip->in_suspend) @@ -1325,19 +1360,21 @@ static int smb1398_div2_cp_ilim_vote_cb(struct votable *votable, if (!client) return -EINVAL; + min_ilim_ua = smb1398_div2_cp_get_min_icl(chip); + ilim_ua = (ilim_ua * DIV2_ILIM_CFG_PCT) / 100; max_ilim_ua = is_cps_available(chip) ? DIV2_MAX_ILIM_DUAL_CP_UA : DIV2_MAX_ILIM_UA; ilim_ua = min(ilim_ua, max_ilim_ua); - if (ilim_ua < chip->div2_cp_min_ilim_ua) { + if (ilim_ua < min_ilim_ua) { dev_dbg(chip->dev, "ilim %duA is too low to config CP charging\n", ilim_ua); vote(chip->div2_cp_disable_votable, ILIM_VOTER, true, 0); } else { if (is_cps_available(chip)) { split_ilim = true; - slave_dis = ilim_ua < (2 * chip->div2_cp_min_ilim_ua); + slave_dis = ilim_ua < (2 * min_ilim_ua); vote(chip->div2_cp_slave_disable_votable, ILIM_VOTER, slave_dis, 0); slave_dis = !!get_effective_result( @@ -1508,7 +1545,7 @@ static int smb1398_get_irq_index_byname(const char *irq_name) { int i; - for (i = 0; i < NUM_IRQS; i++) { + for (i = 0; i < ARRAY_SIZE(smb_irqs); i++) { if (smb_irqs[i].name != NULL) if (strcmp(smb_irqs[i].name, irq_name) == 0) return i; @@ -1764,7 +1801,7 @@ static void smb1398_taper_work(struct work_struct *work) struct smb1398_chip *chip = container_of(work, struct smb1398_chip, taper_work); union power_supply_propval pval = {0}; - int rc, fcc_ua, fv_uv, stepper_ua, main_fcc_ua; + int rc, fcc_ua, fv_uv, stepper_ua, main_fcc_ua = 0, min_ilim_ua; bool slave_en; if (!is_psy_voter_available(chip)) @@ -1776,6 +1813,8 @@ static void smb1398_taper_work(struct work_struct *work) if (chip->fcc_main_votable) main_fcc_ua = get_effective_result(chip->fcc_main_votable); + min_ilim_ua = smb1398_div2_cp_get_min_icl(chip); + chip->taper_entry_fv = get_effective_result(chip->fv_votable); while (true) { rc = power_supply_get_property(chip->batt_psy, @@ -1811,7 +1850,7 @@ static void smb1398_taper_work(struct work_struct *work) * If total FCC is less than the minimum ILIM to * keep CP master and slave online, disable CP. */ - if (fcc_ua < (chip->div2_cp_min_ilim_ua * 2)) { + if (fcc_ua < (min_ilim_ua * 2)) { vote(chip->div2_cp_disable_votable, TAPER_VOTER, true, 0); /* @@ -1860,10 +1899,53 @@ static void smb1398_taper_work(struct work_struct *work) chip->taper_work_running = false; } +static int smb1398_update_ovp(struct smb1398_chip *chip) +{ + int rc = 0; + u8 reg = 0; + + rc = smb1398_read(chip, PERPH0_REVISION4, ®); + if (rc < 0) { + dev_err(chip->dev, + "Couldn't read PERPH0_REVISION4 rc=%d\n", rc); + return rc; + } + + /* Ignore for REV2 and below */ + if (reg <= 2) + return 0; + + rc = smb1398_masked_write(chip, PERPH0_SSUPPLY_CFG0_REG, + EN_HV_OV_OPTION2_BIT | EN_MV_OV_OPTION2_BIT, + EN_HV_OV_OPTION2_BIT); + if (rc < 0) { + dev_err(chip->dev, + "Couldn't set PERPH0_SSUPPLY_CFG0_REG rc=%d\n", rc); + return rc; + } + + rc = smb1398_masked_write(chip, PERPH1_LOCK_SPARE_REG, + CFG_LOCK_SPARE1_MASK, + OVP_14V << CFG_LOCK_SPARE1_SHIFT); + if (rc < 0) { + dev_err(chip->dev, + "Couldn't set PERPH1_LOCK_SPARE_REG rc=%d\n", rc); + return rc; + } + + return 0; +} + static int smb1398_div2_cp_hw_init(struct smb1398_chip *chip) { int rc = 0; + rc = smb1398_update_ovp(chip); + if (rc < 0) { + dev_err(chip->dev, "Couldn't update OVP threshold rc=%d\n", rc); + return rc; + } + /* Configure window (Vin/2 - Vout) OV level to 500mV */ rc = smb1398_masked_write(chip, DIV2_PROTECTION_REG, DIV2_WIN_OV_SEL_MASK, WIN_OV_500_MV); @@ -2184,6 +2266,12 @@ static int smb1398_div2_cp_slave_probe(struct smb1398_chip *chip) int rc; u8 status; + rc = smb1398_update_ovp(chip); + if (rc < 0) { + dev_err(chip->dev, "Couldn't update OVP threshold rc=%d\n", rc); + return rc; + } + rc = smb1398_read(chip, MODE_STATUS_REG, &status); if (rc < 0) { dev_err(chip->dev, "Couldn't read slave MODE_STATUS_REG, rc=%d\n", diff --git a/drivers/power/supply/qcom/smblite-lib.c b/drivers/power/supply/qcom/smblite-lib.c index 5c127f0069a151ad5349b2281d0d79c9d2fb4407..06f5f0cbeb1422c29b533a9fcdd70c9546e5af7f 100644 --- a/drivers/power/supply/qcom/smblite-lib.c +++ b/drivers/power/supply/qcom/smblite-lib.c @@ -2072,14 +2072,116 @@ irqreturn_t smblite_icl_change_irq_handler(int irq, void *data) return IRQ_HANDLED; } +static int smblite_lib_role_switch_failure(struct smb_charger *chg) +{ + int rc = 0; + union power_supply_propval pval = {0, }; + + rc = smblite_lib_get_prop_usb_present(chg, &pval); + if (rc < 0) { + smblite_lib_err(chg, "Couldn't get usb presence status rc=%d\n", + rc); + return rc; + } + + /* + * When role switch fails notify the + * current charger state to usb driver. + */ + if (pval.intval) { + smblite_lib_dbg(chg, PR_MISC, "Role reversal failed, notifying device mode to usb driver.\n"); + smblite_lib_notify_device_mode(chg, true); + } + + return rc; +} + +static int typec_partner_register(struct smb_charger *chg) +{ + int typec_mode, rc = 0; + + mutex_lock(&chg->typec_lock); + + if (!chg->typec_port || chg->pr_swap_in_progress) + goto unlock; + + if (!chg->typec_partner) { + if (chg->sink_src_mode == AUDIO_ACCESS_MODE) + chg->typec_partner_desc.accessory = + TYPEC_ACCESSORY_AUDIO; + else + chg->typec_partner_desc.accessory = + TYPEC_ACCESSORY_NONE; + + chg->typec_partner = typec_register_partner(chg->typec_port, + &chg->typec_partner_desc); + if (IS_ERR(chg->typec_partner)) { + rc = PTR_ERR(chg->typec_partner); + pr_err("Couldn't to register typec_partner rc=%d\n", + rc); + goto unlock; + } + } + + if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) + goto unlock; + + typec_mode = smblite_lib_get_prop_typec_mode(chg); + + if (typec_mode >= POWER_SUPPLY_TYPEC_SOURCE_DEFAULT + || typec_mode == POWER_SUPPLY_TYPEC_NONE) { + if (chg->typec_role_swap_failed) { + rc = smblite_lib_role_switch_failure(chg); + if (rc < 0) + smblite_lib_err(chg, "Failed to role switch rc=%d\n", + rc); + chg->typec_role_swap_failed = false; + } + + typec_set_data_role(chg->typec_port, TYPEC_DEVICE); + typec_set_pwr_role(chg->typec_port, TYPEC_SINK); + } else { + typec_set_data_role(chg->typec_port, TYPEC_HOST); + typec_set_pwr_role(chg->typec_port, TYPEC_SOURCE); + } + +unlock: + mutex_unlock(&chg->typec_lock); + return rc; +} + +static void typec_partner_unregister(struct smb_charger *chg) +{ + mutex_lock(&chg->typec_lock); + + if (!chg->typec_port) + goto unlock; + + if (chg->typec_partner && !chg->pr_swap_in_progress) { + smblite_lib_dbg(chg, PR_MISC, "Un-registering typeC partner\n"); + typec_unregister_partner(chg->typec_partner); + chg->typec_partner = NULL; + } + +unlock: + mutex_unlock(&chg->typec_lock); +} + static void smblite_lib_micro_usb_plugin(struct smb_charger *chg, bool vbus_rising) { + int rc = 0; + if (vbus_rising) { smblite_lib_notify_device_mode(chg, true); + rc = typec_partner_register(chg); + if (rc < 0) + smblite_lib_err(chg, "Couldn't register partner rc =%d\n", + rc); } else { smblite_lib_notify_device_mode(chg, false); smblite_lib_uusb_removal(chg); + typec_partner_unregister(chg); } } @@ -2263,52 +2365,6 @@ static void typec_ra_ra_insertion(struct smb_charger *chg) vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0); } -static int typec_partner_register(struct smb_charger *chg) -{ - int typec_mode, rc = 0; - - if (!chg->typec_port) - return 0; - - if (chg->typec_partner && chg->pr_swap_in_progress) - return 0; - - if (chg->sink_src_mode == AUDIO_ACCESS_MODE) - chg->typec_partner_desc.accessory = TYPEC_ACCESSORY_AUDIO; - else - chg->typec_partner_desc.accessory = TYPEC_ACCESSORY_NONE; - - chg->typec_partner = typec_register_partner(chg->typec_port, - &chg->typec_partner_desc); - if (IS_ERR(chg->typec_partner)) { - rc = PTR_ERR(chg->typec_partner); - pr_err("Couldn't to register typec_partner rc=%d\n", rc); - return rc; - } - - typec_mode = smblite_lib_get_prop_typec_mode(chg); - - if (typec_mode >= POWER_SUPPLY_TYPEC_SOURCE_DEFAULT - || typec_mode == POWER_SUPPLY_TYPEC_NONE) { - typec_set_data_role(chg->typec_port, TYPEC_DEVICE); - typec_set_pwr_role(chg->typec_port, TYPEC_SINK); - } else { - typec_set_data_role(chg->typec_port, TYPEC_HOST); - typec_set_pwr_role(chg->typec_port, TYPEC_SOURCE); - } - - return rc; -} - -static void typec_partner_unregister(struct smb_charger *chg) -{ - if (chg->typec_partner && !chg->pr_swap_in_progress) { - smblite_lib_dbg(chg, PR_MISC, "Un-registering typeC partner\n"); - typec_unregister_partner(chg->typec_partner); - chg->typec_partner = NULL; - } -} - static const char * const dr_mode_text[] = { "ufp", "dfp", "none" }; @@ -2401,30 +2457,6 @@ int smblite_lib_typec_port_type_set(const struct typec_capability *cap, return rc; } -static int smblite_lib_role_switch_failure(struct smb_charger *chg, int mode) -{ - int rc = 0; - union power_supply_propval pval = {0, }; - - rc = smblite_lib_get_prop_usb_present(chg, &pval); - if (rc < 0) { - smblite_lib_err(chg, "Couldn't get usb presence status rc=%d\n", - rc); - return rc; - } - - /* - * When role switch fails notify the - * current charger state to usb driver. - */ - if (pval.intval && mode == TYPEC_PORT_SRC) { - smblite_lib_dbg(chg, PR_MISC, "Role reversal failed, notifying device mode to usb driver.\n"); - smblite_lib_notify_device_mode(chg, true); - } - - return rc; -} - static void smblite_lib_typec_role_check_work(struct work_struct *work) { struct smb_charger *chg = container_of(work, struct smb_charger, @@ -2460,16 +2492,13 @@ static void smblite_lib_typec_role_check_work(struct work_struct *work) || chg->typec_mode == POWER_SUPPLY_TYPEC_NONE) { smblite_lib_dbg(chg, PR_MISC, "Role reversal not latched to DFP in %d msecs. Resetting to DRP mode\n", ROLE_REVERSAL_DELAY_MS); + chg->pr_swap_in_progress = false; + chg->typec_role_swap_failed = true; rc = smblite_lib_force_dr_mode(chg, TYPEC_PORT_DRP); if (rc < 0) smblite_lib_err(chg, "Couldn't to set DRP mode, rc=%d\n", rc); - rc = smblite_lib_role_switch_failure(chg, - TYPEC_PORT_SRC); - if (rc < 0) - smblite_lib_err(chg, "Couldn't to role switch rc=%d\n", - rc); } else { chg->power_role = POWER_SUPPLY_TYPEC_PR_SOURCE; typec_set_pwr_role(chg->typec_port, TYPEC_SOURCE); @@ -2491,8 +2520,6 @@ static void typec_sink_removal(struct smb_charger *chg) { if (chg->otg_present) smblite_lib_notify_usb_host(chg, false); - - typec_partner_unregister(chg); } static void typec_src_removal(struct smb_charger *chg) @@ -2530,7 +2557,6 @@ static void typec_src_removal(struct smb_charger *chg) smblite_lib_notify_device_mode(chg, false); - typec_partner_unregister(chg); chg->typec_legacy = false; } @@ -2596,6 +2622,7 @@ irqreturn_t smblite_typec_state_change_irq_handler(int irq, void *data) return IRQ_HANDLED; } +#define TYPEC_DETACH_DETECT_DELAY_MS 2000 irqreturn_t smblite_typec_attach_detach_irq_handler(int irq, void *data) { struct smb_irq_data *irq_data = data; @@ -2663,8 +2690,25 @@ irqreturn_t smblite_typec_attach_detach_irq_handler(int irq, void *data) * mode configuration is reset properly. */ - if (chg->typec_port && !chg->pr_swap_in_progress) + if (chg->typec_port && !chg->pr_swap_in_progress) { + /* + * Schedule the work to differentiate actual removal + * of cable and detach interrupt during role swap, + * unregister the partner only during actual cable + * removal. + */ + cancel_delayed_work(&chg->pr_swap_detach_work); + vote(chg->awake_votable, DETACH_DETECT_VOTER, true, 0); + schedule_delayed_work(&chg->pr_swap_detach_work, + msecs_to_jiffies(TYPEC_DETACH_DETECT_DELAY_MS)); smblite_lib_force_dr_mode(chg, TYPEC_PORT_DRP); + + /* + * To handle cable removal during role + * swap failure. + */ + chg->typec_role_swap_failed = false; + } } rc = smblite_lib_masked_write(chg, USB_CMD_PULLDOWN_REG, @@ -2814,6 +2858,28 @@ irqreturn_t smblite_usb_id_irq_handler(int irq, void *data) /*************** * Work Queues * ***************/ +static void smblite_lib_pr_swap_detach_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + pr_swap_detach_work.work); + int rc; + u8 stat; + + rc = smblite_lib_read(chg, TYPE_C_STATE_MACHINE_STATUS_REG, &stat); + if (rc < 0) { + smblite_lib_err(chg, "Couldn't read STATE_MACHINE_STS rc=%d\n", + rc); + goto out; + } + smblite_lib_dbg(chg, PR_REGISTER, "STATE_MACHINE_STS %#x\n", stat); + + if (!(stat & TYPEC_ATTACH_DETACH_STATE_BIT)) + typec_partner_unregister(chg); + +out: + vote(chg->awake_votable, DETACH_DETECT_VOTER, false, 0); +} + static void bms_update_work(struct work_struct *work) { struct smb_charger *chg = container_of(work, struct smb_charger, @@ -3212,6 +3278,8 @@ int smblite_lib_init(struct smb_charger *chg) smblite_lib_thermal_regulation_work); INIT_DELAYED_WORK(&chg->role_reversal_check, smblite_lib_typec_role_check_work); + INIT_DELAYED_WORK(&chg->pr_swap_detach_work, + smblite_lib_pr_swap_detach_work); chg->fake_capacity = -EINVAL; chg->fake_batt_status = -EINVAL; chg->sink_src_mode = UNATTACHED_MODE; @@ -3270,6 +3338,7 @@ int smblite_lib_deinit(struct smb_charger *chg) cancel_delayed_work_sync(&chg->bb_removal_work); cancel_delayed_work_sync(&chg->thermal_regulation_work); cancel_delayed_work_sync(&chg->role_reversal_check); + cancel_delayed_work_sync(&chg->pr_swap_detach_work); power_supply_unreg_notifier(&chg->nb); smblite_lib_destroy_votables(chg); qcom_step_chg_deinit(); diff --git a/drivers/power/supply/qcom/smblite-lib.h b/drivers/power/supply/qcom/smblite-lib.h index 0d6e8c3cd41cef744a8400aa37545d06b54bb64d..a5984646a92c0ca2bd0d2a0d7917995fdc95ba01 100644 --- a/drivers/power/supply/qcom/smblite-lib.h +++ b/drivers/power/supply/qcom/smblite-lib.h @@ -275,6 +275,7 @@ struct smb_charger { struct delayed_work bb_removal_work; struct delayed_work thermal_regulation_work; struct delayed_work role_reversal_check; + struct delayed_work pr_swap_detach_work; struct charger_param chg_param; @@ -313,6 +314,7 @@ struct smb_charger { bool ldo_mode; int usb_id_gpio; int usb_id_irq; + bool typec_role_swap_failed; /* workaround flag */ u32 wa_flags; diff --git a/drivers/power/supply/qcom/step-chg-jeita.c b/drivers/power/supply/qcom/step-chg-jeita.c index 5a81e53ed4bdddfc658fb26144a0153d0b22d037..a2e9bf95918a2e9fec60850cb08b55e2081dfa1a 100644 --- a/drivers/power/supply/qcom/step-chg-jeita.c +++ b/drivers/power/supply/qcom/step-chg-jeita.c @@ -843,7 +843,7 @@ int qcom_step_chg_init(struct device *dev, if (!chip) return -ENOMEM; - chip->step_chg_ws = wakeup_source_register("qcom-step-chg"); + chip->step_chg_ws = wakeup_source_register(dev, "qcom-step-chg"); if (!chip->step_chg_ws) return -EINVAL; diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c index b818f65480c152bda2209fb4a76be083da719ccc..e232233beb8f26f2b3634c684d3096f035d08527 100644 --- a/drivers/ptp/ptp_clock.c +++ b/drivers/ptp/ptp_clock.c @@ -179,6 +179,7 @@ static void ptp_clock_release(struct device *dev) { struct ptp_clock *ptp = container_of(dev, struct ptp_clock, dev); + ptp_cleanup_pin_groups(ptp); mutex_destroy(&ptp->tsevq_mux); mutex_destroy(&ptp->pincfg_mux); ida_simple_remove(&ptp_clocks_map, ptp->index); @@ -315,9 +316,8 @@ int ptp_clock_unregister(struct ptp_clock *ptp) if (ptp->pps_source) pps_unregister_source(ptp->pps_source); - ptp_cleanup_pin_groups(ptp); - posix_clock_unregister(&ptp->clock); + return 0; } EXPORT_SYMBOL(ptp_clock_unregister); diff --git a/drivers/pwm/pwm-lpss.c b/drivers/pwm/pwm-lpss.c index 1e69c1c9ec09635c9dffbb6b4d9cfd157f0c9108..7a4a6406cf69adc945d8290df016954ca1c4a323 100644 --- a/drivers/pwm/pwm-lpss.c +++ b/drivers/pwm/pwm-lpss.c @@ -216,6 +216,12 @@ EXPORT_SYMBOL_GPL(pwm_lpss_probe); int pwm_lpss_remove(struct pwm_lpss_chip *lpwm) { + int i; + + for (i = 0; i < lpwm->info->npwm; i++) { + if (pwm_is_enabled(&lpwm->chip.pwms[i])) + pm_runtime_put(lpwm->chip.dev); + } return pwmchip_remove(&lpwm->chip); } EXPORT_SYMBOL_GPL(pwm_lpss_remove); diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c index f6e738ad7bd92942ee9ea0ee028ae11ae7befb0b..e247ab632530bac5d07c22f816b68681ac866578 100644 --- a/drivers/pwm/pwm-meson.c +++ b/drivers/pwm/pwm-meson.c @@ -188,7 +188,7 @@ static int meson_pwm_calc(struct meson_pwm *meson, do_div(fin_ps, fin_freq); /* Calc pre_div with the period */ - for (pre_div = 0; pre_div < MISC_CLK_DIV_MASK; pre_div++) { + for (pre_div = 0; pre_div <= MISC_CLK_DIV_MASK; pre_div++) { cnt = DIV_ROUND_CLOSEST_ULL((u64)period * 1000, fin_ps * (pre_div + 1)); dev_dbg(meson->chip.dev, "fin_ps=%llu pre_div=%u cnt=%u\n", @@ -197,7 +197,7 @@ static int meson_pwm_calc(struct meson_pwm *meson, break; } - if (pre_div == MISC_CLK_DIV_MASK) { + if (pre_div > MISC_CLK_DIV_MASK) { dev_err(meson->chip.dev, "unable to get period pre_div\n"); return -EINVAL; } @@ -325,11 +325,6 @@ static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, if (state->period != channel->state.period || state->duty_cycle != channel->state.duty_cycle || state->polarity != channel->state.polarity) { - if (channel->state.enabled) { - meson_pwm_disable(meson, pwm->hwpwm); - channel->state.enabled = false; - } - if (state->polarity != channel->state.polarity) { if (state->polarity == PWM_POLARITY_NORMAL) meson->inverter_mask |= BIT(pwm->hwpwm); diff --git a/drivers/pwm/pwm-omap-dmtimer.c b/drivers/pwm/pwm-omap-dmtimer.c index f45798679e3c046add4f646ecd5c86e02a7b9b9c..527b87959742340e0b8d8fb5323e7e814927c8a2 100644 --- a/drivers/pwm/pwm-omap-dmtimer.c +++ b/drivers/pwm/pwm-omap-dmtimer.c @@ -259,7 +259,7 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev) if (!timer_pdev) { dev_err(&pdev->dev, "Unable to find Timer pdev\n"); ret = -ENODEV; - goto put; + goto err_find_timer_pdev; } timer_pdata = dev_get_platdata(&timer_pdev->dev); @@ -267,7 +267,7 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "dmtimer pdata structure NULL, deferring probe\n"); ret = -EPROBE_DEFER; - goto put; + goto err_platdata; } pdata = timer_pdata->timer_ops; @@ -286,30 +286,25 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev) !pdata->write_counter) { dev_err(&pdev->dev, "Incomplete dmtimer pdata structure\n"); ret = -EINVAL; - goto put; + goto err_platdata; } if (!of_get_property(timer, "ti,timer-pwm", NULL)) { dev_err(&pdev->dev, "Missing ti,timer-pwm capability\n"); ret = -ENODEV; - goto put; + goto err_timer_property; } dm_timer = pdata->request_by_node(timer); if (!dm_timer) { ret = -EPROBE_DEFER; - goto put; + goto err_request_timer; } -put: - of_node_put(timer); - if (ret < 0) - return ret; - omap = devm_kzalloc(&pdev->dev, sizeof(*omap), GFP_KERNEL); if (!omap) { - pdata->free(dm_timer); - return -ENOMEM; + ret = -ENOMEM; + goto err_alloc_omap; } omap->pdata = pdata; @@ -342,27 +337,56 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev) ret = pwmchip_add(&omap->chip); if (ret < 0) { dev_err(&pdev->dev, "failed to register PWM\n"); - omap->pdata->free(omap->dm_timer); - return ret; + goto err_pwmchip_add; } + of_node_put(timer); + platform_set_drvdata(pdev, omap); return 0; + +err_pwmchip_add: + + /* + * *omap is allocated using devm_kzalloc, + * so no free necessary here + */ +err_alloc_omap: + + pdata->free(dm_timer); +err_request_timer: + +err_timer_property: +err_platdata: + + put_device(&timer_pdev->dev); +err_find_timer_pdev: + + of_node_put(timer); + + return ret; } static int pwm_omap_dmtimer_remove(struct platform_device *pdev) { struct pwm_omap_dmtimer_chip *omap = platform_get_drvdata(pdev); + int ret; + + ret = pwmchip_remove(&omap->chip); + if (ret) + return ret; if (pm_runtime_active(&omap->dm_timer_pdev->dev)) omap->pdata->stop(omap->dm_timer); omap->pdata->free(omap->dm_timer); + put_device(&omap->dm_timer_pdev->dev); + mutex_destroy(&omap->mutex); - return pwmchip_remove(&omap->chip); + return 0; } static const struct of_device_id pwm_omap_dmtimer_of_match[] = { diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c index 567f5e2771c47288488063f49363fd3e0b4eaab7..e1e5dfcb16f367d7fbc0a72cd22113bf9a731f21 100644 --- a/drivers/pwm/pwm-pca9685.c +++ b/drivers/pwm/pwm-pca9685.c @@ -170,13 +170,9 @@ static void pca9685_pwm_gpio_set(struct gpio_chip *gpio, unsigned int offset, static void pca9685_pwm_gpio_free(struct gpio_chip *gpio, unsigned int offset) { struct pca9685 *pca = gpiochip_get_data(gpio); - struct pwm_device *pwm; pca9685_pwm_gpio_set(gpio, offset, 0); pm_runtime_put(pca->chip.dev); - mutex_lock(&pca->lock); - pwm = &pca->chip.pwms[offset]; - mutex_unlock(&pca->lock); } static int pca9685_pwm_gpio_get_direction(struct gpio_chip *chip, diff --git a/drivers/rapidio/rio_cm.c b/drivers/rapidio/rio_cm.c index ef989a15aefc41b47aebfced74795d88a7cb88ce..b29fc258eeba4becb609d6e2063af1ebf5022e91 100644 --- a/drivers/rapidio/rio_cm.c +++ b/drivers/rapidio/rio_cm.c @@ -1215,7 +1215,9 @@ static int riocm_ch_listen(u16 ch_id) riocm_debug(CHOP, "(ch_%d)", ch_id); ch = riocm_get_channel(ch_id); - if (!ch || !riocm_cmp_exch(ch, RIO_CM_CHAN_BOUND, RIO_CM_LISTEN)) + if (!ch) + return -EINVAL; + if (!riocm_cmp_exch(ch, RIO_CM_CHAN_BOUND, RIO_CM_LISTEN)) ret = -EINVAL; riocm_put_channel(ch); return ret; diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 4e4bdcf0443bc300571d0204bf1e511fb1fad734..7e7dd029c7b5dd1dff8428b3054cbc0d1a851408 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1094,6 +1094,16 @@ config REGULATOR_RPM_SMD be used on systems which contain an RPM which communicates with the application processor over SMD. +config REGULATOR_SPM + bool "SPM regulator driver" + depends on SPMI + help + Enable support for the SPM regulator driver which is used for + setting voltages of processor supply regulators via the SPM module + found inside chips of Qualcomm Technologies Inc. The SPM regulator + driver can be used on QTI SoCs where the APSS processor cores are + supplied by their own PMIC regulator. + config REGULATOR_STUB tristate "Stub Regulator" help diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 9aff7d8f89d35c5e7cf8ddb365284e213bb0478c..2f8b0ba9c91e911edc7b0bd7c2c7b76faea891d2 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -137,6 +137,7 @@ obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o obj-$(CONFIG_REGULATOR_WM8994) += wm8994-regulator.o obj-$(CONFIG_REGULATOR_MEM_ACC) += mem-acc-regulator.o obj-$(CONFIG_REGULATOR_REFGEN) += refgen.o +obj-$(CONFIG_REGULATOR_SPM) += spm-regulator.o obj-$(CONFIG_REGULATOR_RPMH) += rpmh-regulator.o obj-$(CONFIG_REGULATOR_STUB) += stub-regulator.o diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index f2360fa6e6a44d877b33d03d80f3594dca6d0645..b9cae335a04cbf9746fdaeb4c24b1791aeea6158 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -230,6 +230,37 @@ static void regulator_unlock_supply(struct regulator_dev *rdev) } } +/** + * of_get_child_regulator - get a child regulator device node + * based on supply name + * @parent: Parent device node + * @prop_name: Combination regulator supply name and "-supply" + * + * Traverse all child nodes. + * Extract the child regulator device node corresponding to the supply name. + * returns the device node corresponding to the regulator if found, else + * returns NULL. + */ +static struct device_node *of_get_child_regulator(struct device_node *parent, + const char *prop_name) +{ + struct device_node *regnode = NULL; + struct device_node *child = NULL; + + for_each_child_of_node(parent, child) { + regnode = of_parse_phandle(child, prop_name, 0); + + if (!regnode) { + regnode = of_get_child_regulator(child, prop_name); + if (regnode) + return regnode; + } else { + return regnode; + } + } + return NULL; +} + /** * of_get_regulator - get a regulator device node based on supply name * @dev: Device pointer for the consumer (of regulator) device @@ -250,6 +281,10 @@ static struct device_node *of_get_regulator(struct device *dev, const char *supp regnode = of_parse_phandle(dev->of_node, prop_name, 0); if (!regnode) { + regnode = of_get_child_regulator(dev->of_node, prop_name); + if (regnode) + return regnode; + dev_dbg(dev, "Looking up %s property in node %pOF failed\n", prop_name, dev->of_node); return NULL; @@ -2733,6 +2768,40 @@ int regulator_list_hardware_vsel(struct regulator *regulator, } EXPORT_SYMBOL_GPL(regulator_list_hardware_vsel); +/** + * regulator_list_corner_voltage - return the maximum voltage in microvolts that + * can be physically configured for the regulator when operating at the + * specified voltage corner + * @regulator: regulator source + * @corner: voltage corner value + * Context: can sleep + * + * This function can be used for regulators which allow scaling between + * different voltage corners as opposed to be different absolute voltages. The + * absolute voltage for a given corner may vary part-to-part or for a given part + * at runtime based upon various factors. + * + * Returns a voltage corresponding to the specified voltage corner or a negative + * errno if the corner value can't be used on this system. + */ +int regulator_list_corner_voltage(struct regulator *regulator, int corner) +{ + struct regulator_dev *rdev = regulator->rdev; + int ret; + + if (corner < rdev->constraints->min_uV || + corner > rdev->constraints->max_uV || + !rdev->desc->ops->list_corner_voltage) + return -EINVAL; + + mutex_lock(&rdev->mutex); + ret = rdev->desc->ops->list_corner_voltage(rdev, corner); + mutex_unlock(&rdev->mutex); + + return ret; +} +EXPORT_SYMBOL(regulator_list_corner_voltage); + /** * regulator_get_linear_step - return the voltage step size between VSEL values * @regulator: regulator source diff --git a/drivers/regulator/lp87565-regulator.c b/drivers/regulator/lp87565-regulator.c index c192357d1dea0ecb0891e19781d7442938f7a072..7cd6862406b70ce75791b4843bbbab73182f3f27 100644 --- a/drivers/regulator/lp87565-regulator.c +++ b/drivers/regulator/lp87565-regulator.c @@ -193,7 +193,7 @@ static int lp87565_regulator_probe(struct platform_device *pdev) struct lp87565 *lp87565 = dev_get_drvdata(pdev->dev.parent); struct regulator_config config = { }; struct regulator_dev *rdev; - int i, min_idx = LP87565_BUCK_1, max_idx = LP87565_BUCK_3; + int i, min_idx = LP87565_BUCK_0, max_idx = LP87565_BUCK_3; platform_set_drvdata(pdev, lp87565); diff --git a/drivers/regulator/pv88060-regulator.c b/drivers/regulator/pv88060-regulator.c index a9446056435f9b71772d4cecb33017c42abeebcf..1f2d8180506bc248676ce61429b1b93442f8d718 100644 --- a/drivers/regulator/pv88060-regulator.c +++ b/drivers/regulator/pv88060-regulator.c @@ -135,7 +135,7 @@ static int pv88060_set_current_limit(struct regulator_dev *rdev, int min, int i; /* search for closest to maximum */ - for (i = info->n_current_limits; i >= 0; i--) { + for (i = info->n_current_limits - 1; i >= 0; i--) { if (min <= info->current_limits[i] && max >= info->current_limits[i]) { return regmap_update_bits(rdev->regmap, diff --git a/drivers/regulator/pv88080-regulator.c b/drivers/regulator/pv88080-regulator.c index 9a08cb2de501e95175fe4f56168ec0bfe123b08a..6770e4de2097804d4c9701ca6598247c1d7ee196 100644 --- a/drivers/regulator/pv88080-regulator.c +++ b/drivers/regulator/pv88080-regulator.c @@ -279,7 +279,7 @@ static int pv88080_set_current_limit(struct regulator_dev *rdev, int min, int i; /* search for closest to maximum */ - for (i = info->n_current_limits; i >= 0; i--) { + for (i = info->n_current_limits - 1; i >= 0; i--) { if (min <= info->current_limits[i] && max >= info->current_limits[i]) { return regmap_update_bits(rdev->regmap, diff --git a/drivers/regulator/pv88090-regulator.c b/drivers/regulator/pv88090-regulator.c index 7a0c15957bd0b710ec25b65454fe0474ca5f87d7..2302b0df7630f08a090679183bc87a3549645772 100644 --- a/drivers/regulator/pv88090-regulator.c +++ b/drivers/regulator/pv88090-regulator.c @@ -157,7 +157,7 @@ static int pv88090_set_current_limit(struct regulator_dev *rdev, int min, int i; /* search for closest to maximum */ - for (i = info->n_current_limits; i >= 0; i--) { + for (i = info->n_current_limits - 1; i >= 0; i--) { if (min <= info->current_limits[i] && max >= info->current_limits[i]) { return regmap_update_bits(rdev->regmap, diff --git a/drivers/regulator/rk808-regulator.c b/drivers/regulator/rk808-regulator.c index 213b68743cc89bd00571a12e8870a36434dd43cc..92498ac503035c54c3742929fd2978833226fb5d 100644 --- a/drivers/regulator/rk808-regulator.c +++ b/drivers/regulator/rk808-regulator.c @@ -714,7 +714,7 @@ static int rk808_regulator_dt_parse_pdata(struct device *dev, } if (!pdata->dvs_gpio[i]) { - dev_warn(dev, "there is no dvs%d gpio\n", i); + dev_info(dev, "there is no dvs%d gpio\n", i); continue; } diff --git a/drivers/regulator/spm-regulator.c b/drivers/regulator/spm-regulator.c new file mode 100644 index 0000000000000000000000000000000000000000..313dceed68addaa0ce077629778fb5a6e17adac6 --- /dev/null +++ b/drivers/regulator/spm-regulator.c @@ -0,0 +1,1351 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2013-2017, 2020, The Linux Foundation. All rights reserved. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_ARM64) || (defined(CONFIG_ARM) && defined(CONFIG_ARM_PSCI)) +#else + #define __invoke_psci_fn_smc(a, b, c, d) 0 +#endif + +#define SPM_REGULATOR_DRIVER_NAME "qcom,spm-regulator" + +struct voltage_range { + int min_uV; + int set_point_min_uV; + int max_uV; + int step_uV; +}; + +enum qpnp_regulator_uniq_type { + QPNP_TYPE_HF, + QPNP_TYPE_FTS2, + QPNP_TYPE_FTS2p5, + QPNP_TYPE_FTS426, + QPNP_TYPE_ULT_HF, + QPNP_TYPE_HFS430, +}; + +enum qpnp_regulator_type { + QPNP_HF_TYPE = 0x03, + QPNP_FTS2_TYPE = 0x1C, + QPNP_FTS2p5_TYPE = 0x1C, + QPNP_FTS426_TYPE = 0x1C, + QPNP_ULT_HF_TYPE = 0x22, +}; + +enum qpnp_regulator_subtype { + QPNP_FTS2_SUBTYPE = 0x08, + QPNP_HF_SUBTYPE = 0x08, + QPNP_FTS2p5_SUBTYPE = 0x09, + QPNP_FTS426_SUBTYPE = 0x0A, + QPNP_ULT_HF_SUBTYPE = 0x0D, + QPNP_HFS430_SUBTYPE = 0x0A, +}; + +enum qpnp_logical_mode { + QPNP_LOGICAL_MODE_AUTO, + QPNP_LOGICAL_MODE_PWM, +}; + +static const struct voltage_range fts2_range0 = {0, 350000, 1275000, 5000}; +static const struct voltage_range fts2_range1 = {0, 700000, 2040000, 10000}; +static const struct voltage_range fts2p5_range0 + = { 80000, 350000, 1355000, 5000}; +static const struct voltage_range fts2p5_range1 + = {160000, 700000, 2200000, 10000}; +static const struct voltage_range fts426_range = {0, 320000, 1352000, 4000}; +static const struct voltage_range hfs430_range = {0, 320000, 2040000, 8000}; +static const struct voltage_range ult_hf_range0 = {375000, 375000, 1562500, + 12500}; +static const struct voltage_range ult_hf_range1 = {750000, 750000, 1525000, + 25000}; +static const struct voltage_range hf_range0 = {375000, 375000, 1562500, 12500}; +static const struct voltage_range hf_range1 = {1550000, 1550000, 3125000, + 25000}; + +#define QPNP_SMPS_REG_TYPE 0x04 +#define QPNP_SMPS_REG_SUBTYPE 0x05 +#define QPNP_SMPS_REG_VOLTAGE_RANGE 0x40 +#define QPNP_SMPS_REG_VOLTAGE_SETPOINT 0x41 +#define QPNP_SMPS_REG_MODE 0x45 +#define QPNP_SMPS_REG_STEP_CTRL 0x61 +#define QPNP_SMPS_REG_UL_LL_CTRL 0x68 + +/* FTS426/HFS430 voltage control registers */ +#define QPNP_FTS426_HFS430_REG_VOLTAGE_LB 0x40 +#define QPNP_FTS426_HFS430_REG_VOLTAGE_UB 0x41 +#define QPNP_FTS426_HFS430_REG_VOLTAGE_VALID_LB 0x42 +#define QPNP_FTS426_HFS430_REG_VOLTAGE_VALID_UB 0x43 + +/* HF voltage limit registers */ +#define QPNP_HF_REG_VOLTAGE_ULS 0x69 +#define QPNP_HF_REG_VOLTAGE_LLS 0x6B + +/* FTS voltage limit registers */ +#define QPNP_FTS_REG_VOLTAGE_ULS_VALID 0x6A +#define QPNP_FTS_REG_VOLTAGE_LLS_VALID 0x6C + +/* FTS426/HFS430 voltage limit registers */ +#define QPNP_FTS426_HFS430_REG_VOLTAGE_ULS_LB 0x68 +#define QPNP_FTS426_HFS430_REG_VOLTAGE_ULS_UB 0x69 + +/* Common regulator UL & LL limits control register layout */ +#define QPNP_COMMON_UL_EN_MASK 0x80 +#define QPNP_COMMON_LL_EN_MASK 0x40 + +#define QPNP_SMPS_MODE_PWM 0x80 +#define QPNP_SMPS_MODE_AUTO 0x40 +#define QPNP_FTS426_HFS430_MODE_PWM 0x07 +#define QPNP_FTS426_HFS430_MODE_AUTO 0x06 + +#define QPNP_SMPS_STEP_CTRL_STEP_MASK 0x18 +#define QPNP_SMPS_STEP_CTRL_STEP_SHIFT 3 +#define QPNP_SMPS_STEP_CTRL_DELAY_MASK 0x07 +#define QPNP_SMPS_STEP_CTRL_DELAY_SHIFT 0 +#define QPNP_FTS426_HFS430_STEP_CTRL_DELAY_MASK 0x03 +#define QPNP_FTS426_HFS430_STEP_CTRL_DELAY_SHIFT 0 + +/* Clock rate in kHz of the FTS2 regulator reference clock. */ +#define QPNP_SMPS_CLOCK_RATE 19200 +#define QPNP_FTS426_CLOCK_RATE 4800 +#define QPNP_HFS430_CLOCK_RATE 1600 + +/* Time to delay in us to ensure that a mode change has completed. */ +#define QPNP_FTS2_MODE_CHANGE_DELAY 50 + +/* Minimum time in us that it takes to complete a single SPMI write. */ +#define QPNP_SPMI_WRITE_MIN_DELAY 8 + +/* Minimum voltage stepper delay for each step. */ +#define QPNP_FTS2_STEP_DELAY 8 +#define QPNP_HF_STEP_DELAY 20 +#define QPNP_FTS426_HFS430_STEP_DELAY 2 + +/* Arbitrarily large max step size used to avoid possible numerical overflow */ +#define SPM_REGULATOR_MAX_STEP_UV 10000000 + +/* + * The ratio QPNP_FTS2_STEP_MARGIN_NUM/QPNP_FTS2_STEP_MARGIN_DEN is use to + * adjust the step rate in order to account for oscillator variance. + */ +#define QPNP_FTS2_STEP_MARGIN_NUM 4 +#define QPNP_FTS2_STEP_MARGIN_DEN 5 +#define QPNP_FTS426_HFS430_STEP_MARGIN_NUM 10 +#define QPNP_FTS426_HFS430_STEP_MARGIN_DEN 11 + +/* + * Settling delay for FTS2.5 + * Warm-up=20uS, 0-10% & 90-100% non-linear V-ramp delay = 50uS + */ +#define FTS2P5_SETTLING_DELAY_US 70 + +/* VSET value to decide the range of ULT SMPS */ +#define ULT_SMPS_RANGE_SPLIT 0x60 + +struct spm_vreg { + struct regulator_desc rdesc; + struct regulator_dev *rdev; + struct platform_device *pdev; + struct regmap *regmap; + const struct voltage_range *range; + int uV; + int last_set_uV; + unsigned int vlevel; + unsigned int last_set_vlevel; + u32 max_step_uV; + bool online; + u16 spmi_base_addr; + enum qpnp_logical_mode init_mode; + enum qpnp_logical_mode mode; + int step_rate; + enum qpnp_regulator_uniq_type regulator_type; + u32 cpu_num; + bool bypass_spm; + struct regulator_desc avs_rdesc; + struct regulator_dev *avs_rdev; + int avs_min_uV; + int avs_max_uV; + bool avs_enabled; + u32 recal_cluster_mask; +}; + +static inline bool spm_regulator_using_avs(struct spm_vreg *vreg) +{ + return vreg->avs_rdev && !vreg->bypass_spm; +} + +static int spm_regulator_uv_to_vlevel(struct spm_vreg *vreg, int uV) +{ + int vlevel; + + if (vreg->regulator_type == QPNP_TYPE_FTS426 + || vreg->regulator_type == QPNP_TYPE_HFS430) + return roundup(uV, vreg->range->step_uV) / 1000; + + vlevel = DIV_ROUND_UP(uV - vreg->range->min_uV, vreg->range->step_uV); + + /* Fix VSET for ULT HF Buck */ + if (vreg->regulator_type == QPNP_TYPE_ULT_HF + && vreg->range == &ult_hf_range1) { + vlevel &= 0x1F; + vlevel |= ULT_SMPS_RANGE_SPLIT; + } + + return vlevel; +} + +static int spm_regulator_vlevel_to_uv(struct spm_vreg *vreg, int vlevel) +{ + if (vreg->regulator_type == QPNP_TYPE_FTS426 + || vreg->regulator_type == QPNP_TYPE_HFS430) + return vlevel * 1000; + /* + * Calculate ULT HF buck VSET based on range: + * In case of range 0: VSET is a 7 bit value. + * In case of range 1: VSET is a 5 bit value. + */ + if (vreg->regulator_type == QPNP_TYPE_ULT_HF + && vreg->range == &ult_hf_range1) + vlevel &= ~ULT_SMPS_RANGE_SPLIT; + + return vlevel * vreg->range->step_uV + vreg->range->min_uV; +} + +static unsigned int spm_regulator_vlevel_to_selector(struct spm_vreg *vreg, + unsigned int vlevel) +{ + /* Fix VSET for ULT HF Buck */ + if (vreg->regulator_type == QPNP_TYPE_ULT_HF + && vreg->range == &ult_hf_range1) + vlevel &= ~ULT_SMPS_RANGE_SPLIT; + + if (vreg->regulator_type == QPNP_TYPE_HFS430) + vlevel = spm_regulator_vlevel_to_uv(vreg, vlevel) + / vreg->range->step_uV; + + return vlevel - (vreg->range->set_point_min_uV - vreg->range->min_uV) + / vreg->range->step_uV; +} + +static int qpnp_smps_read_voltage(struct spm_vreg *vreg) +{ + int rc; + u8 val[2] = {0}; + + if (vreg->regulator_type == QPNP_TYPE_FTS426 + || vreg->regulator_type == QPNP_TYPE_HFS430) { + rc = regmap_bulk_read(vreg->regmap, + vreg->spmi_base_addr + + QPNP_FTS426_HFS430_REG_VOLTAGE_VALID_LB, + val, 2); + if (rc) { + dev_err(&vreg->pdev->dev, "%s: could not read voltage setpoint registers, rc=%d\n", + __func__, rc); + return rc; + } + + vreg->last_set_vlevel = ((unsigned int)val[1] << 8) | val[0]; + } else { + rc = regmap_bulk_read(vreg->regmap, + vreg->spmi_base_addr + QPNP_SMPS_REG_VOLTAGE_SETPOINT, + val, 1); + if (rc) { + dev_err(&vreg->pdev->dev, "%s: could not read voltage setpoint register, rc=%d\n", + __func__, rc); + return rc; + } + vreg->last_set_vlevel = val[0]; + } + + vreg->last_set_uV = spm_regulator_vlevel_to_uv(vreg, + vreg->last_set_vlevel); + return rc; +} + +static int qpnp_smps_write_voltage(struct spm_vreg *vreg, unsigned int vlevel) +{ + int rc = 0; + u8 reg[2]; + + /* Set voltage control registers via SPMI. */ + reg[0] = vlevel & 0xFF; + reg[1] = (vlevel >> 8) & 0xFF; + + if (vreg->regulator_type == QPNP_TYPE_FTS426 + || vreg->regulator_type == QPNP_TYPE_HFS430) { + rc = regmap_bulk_write(vreg->regmap, + vreg->spmi_base_addr + + QPNP_FTS426_HFS430_REG_VOLTAGE_LB, + reg, 2); + } else { + rc = regmap_write(vreg->regmap, + vreg->spmi_base_addr + QPNP_SMPS_REG_VOLTAGE_SETPOINT, + reg[0]); + } + + if (rc) + pr_err("%s: regmap_write failed, rc=%d\n", + vreg->rdesc.name, rc); + + return rc; +} + +static inline enum qpnp_logical_mode qpnp_regval_to_mode(struct spm_vreg *vreg, + u8 regval) +{ + if (vreg->regulator_type == QPNP_TYPE_FTS426 + || vreg->regulator_type == QPNP_TYPE_HFS430) + return (regval == QPNP_FTS426_HFS430_MODE_PWM) + ? QPNP_LOGICAL_MODE_PWM : QPNP_LOGICAL_MODE_AUTO; + else + return (regval & QPNP_SMPS_MODE_PWM) + ? QPNP_LOGICAL_MODE_PWM : QPNP_LOGICAL_MODE_AUTO; +} + +static inline u8 qpnp_mode_to_regval(struct spm_vreg *vreg, + enum qpnp_logical_mode mode) +{ + if (vreg->regulator_type == QPNP_TYPE_FTS426 + || vreg->regulator_type == QPNP_TYPE_HFS430) + return (mode == QPNP_LOGICAL_MODE_PWM) + ? QPNP_FTS426_HFS430_MODE_PWM + : QPNP_FTS426_HFS430_MODE_AUTO; + else + return (mode == QPNP_LOGICAL_MODE_PWM) + ? QPNP_SMPS_MODE_PWM : QPNP_SMPS_MODE_AUTO; +} + +static int qpnp_smps_set_mode(struct spm_vreg *vreg, u8 mode) +{ + int rc; + + rc = regmap_write(vreg->regmap, + vreg->spmi_base_addr + QPNP_SMPS_REG_MODE, + qpnp_mode_to_regval(vreg, mode)); + if (rc) + dev_err(&vreg->pdev->dev, + "%s: could not write to mode register, rc=%d\n", + __func__, rc); + + return rc; +} + +static int spm_regulator_get_voltage(struct regulator_dev *rdev) +{ + struct spm_vreg *vreg = rdev_get_drvdata(rdev); + int vlevel, rc; + + if (spm_regulator_using_avs(vreg)) { + vlevel = msm_spm_get_vdd(vreg->cpu_num); + + if (vlevel < 0) { + pr_debug("%s: msm_spm_get_vdd failed, rc=%d; falling back on SPMI read\n", + vreg->rdesc.name, vlevel); + + rc = qpnp_smps_read_voltage(vreg); + if (rc) { + pr_err("%s: voltage read failed, rc=%d\n", + vreg->rdesc.name, rc); + return rc; + } + + return vreg->last_set_uV; + } + + vreg->last_set_vlevel = vlevel; + vreg->last_set_uV = spm_regulator_vlevel_to_uv(vreg, vlevel); + + return vreg->last_set_uV; + } else { + return vreg->uV; + } +}; + +static int spm_regulator_write_voltage(struct spm_vreg *vreg, int uV) +{ + unsigned int vlevel = spm_regulator_uv_to_vlevel(vreg, uV); + bool spm_failed = false; + int rc = 0; + u32 slew_delay; + + if (likely(!vreg->bypass_spm)) { + /* Set voltage control register via SPM. */ + rc = msm_spm_set_vdd(vreg->cpu_num, vlevel); + if (rc) { + pr_debug("%s: msm_spm_set_vdd failed, rc=%d; falling back on SPMI write\n", + vreg->rdesc.name, rc); + spm_failed = true; + } + } + + if (unlikely(vreg->bypass_spm || spm_failed)) { + rc = qpnp_smps_write_voltage(vreg, vlevel); + if (rc) { + pr_err("%s: voltage write failed, rc=%d\n", + vreg->rdesc.name, rc); + return rc; + } + } + + if (uV > vreg->last_set_uV) { + /* Wait for voltage stepping to complete. */ + slew_delay = DIV_ROUND_UP(uV - vreg->last_set_uV, + vreg->step_rate); + if (vreg->regulator_type == QPNP_TYPE_FTS2p5) + slew_delay += FTS2P5_SETTLING_DELAY_US; + udelay(slew_delay); + } else if (vreg->regulator_type == QPNP_TYPE_FTS2p5) { + /* add the ramp-down delay */ + slew_delay = DIV_ROUND_UP(vreg->last_set_uV - uV, + vreg->step_rate) + FTS2P5_SETTLING_DELAY_US; + udelay(slew_delay); + } + + vreg->last_set_uV = uV; + vreg->last_set_vlevel = vlevel; + + return rc; +} + +static int spm_regulator_recalibrate(struct spm_vreg *vreg) +{ + struct arm_smccc_res res; + + if (!vreg->recal_cluster_mask) + return 0; + + arm_smccc_smc(0xC4000020, vreg->recal_cluster_mask, + 2, 0, 0, 0, 0, 0, &res); + if (res.a0) + pr_err("%s: recalibration failed, rc=%ld\n", vreg->rdesc.name, + res.a0); + + return res.a0; +} + +static int _spm_regulator_set_voltage(struct regulator_dev *rdev) +{ + struct spm_vreg *vreg = rdev_get_drvdata(rdev); + bool pwm_required; + int rc = 0; + int uV; + + rc = spm_regulator_get_voltage(rdev); + if (rc < 0) + return rc; + + if (vreg->vlevel == vreg->last_set_vlevel) + return 0; + + pwm_required = (vreg->regulator_type == QPNP_TYPE_FTS2) + && (vreg->init_mode != QPNP_LOGICAL_MODE_PWM) + && vreg->uV > vreg->last_set_uV; + + if (pwm_required) { + /* Switch to PWM mode so that voltage ramping is fast. */ + rc = qpnp_smps_set_mode(vreg, QPNP_LOGICAL_MODE_PWM); + if (rc) + return rc; + } + + do { + uV = vreg->uV > vreg->last_set_uV + ? min(vreg->uV, vreg->last_set_uV + (int)vreg->max_step_uV) + : max(vreg->uV, vreg->last_set_uV - (int)vreg->max_step_uV); + + rc = spm_regulator_write_voltage(vreg, uV); + if (rc) + return rc; + } while (vreg->last_set_uV != vreg->uV); + + if (pwm_required) { + /* Wait for mode transition to complete. */ + udelay(QPNP_FTS2_MODE_CHANGE_DELAY - QPNP_SPMI_WRITE_MIN_DELAY); + /* Switch to AUTO mode so that power consumption is lowered. */ + rc = qpnp_smps_set_mode(vreg, QPNP_LOGICAL_MODE_AUTO); + if (rc) + return rc; + } + + rc = spm_regulator_recalibrate(vreg); + + return rc; +} + +static int spm_regulator_set_voltage(struct regulator_dev *rdev, int min_uV, + int max_uV, unsigned int *selector) +{ + struct spm_vreg *vreg = rdev_get_drvdata(rdev); + const struct voltage_range *range = vreg->range; + int uV = min_uV; + unsigned int vlevel; + + if (uV < range->set_point_min_uV && max_uV >= range->set_point_min_uV) + uV = range->set_point_min_uV; + + if (uV < range->set_point_min_uV || uV > range->max_uV) { + pr_err("%s: request v=[%d, %d] is outside possible v=[%d, %d]\n", + vreg->rdesc.name, min_uV, max_uV, + range->set_point_min_uV, range->max_uV); + return -EINVAL; + } + + vlevel = spm_regulator_uv_to_vlevel(vreg, uV); + uV = spm_regulator_vlevel_to_uv(vreg, vlevel); + + if (uV > max_uV) { + pr_err("%s: request v=[%d, %d] cannot be met by any set point\n", + vreg->rdesc.name, min_uV, max_uV); + return -EINVAL; + } + + *selector = spm_regulator_vlevel_to_selector(vreg, vlevel); + vreg->vlevel = vlevel; + vreg->uV = uV; + + if (!vreg->online) + return 0; + + return _spm_regulator_set_voltage(rdev); +} + +static int spm_regulator_list_voltage(struct regulator_dev *rdev, + unsigned int selector) +{ + struct spm_vreg *vreg = rdev_get_drvdata(rdev); + + if (selector >= vreg->rdesc.n_voltages) + return 0; + + return selector * vreg->range->step_uV + vreg->range->set_point_min_uV; +} + +static int spm_regulator_enable(struct regulator_dev *rdev) +{ + struct spm_vreg *vreg = rdev_get_drvdata(rdev); + int rc; + + rc = _spm_regulator_set_voltage(rdev); + + if (!rc) + vreg->online = true; + + return rc; +} + +static int spm_regulator_disable(struct regulator_dev *rdev) +{ + struct spm_vreg *vreg = rdev_get_drvdata(rdev); + + vreg->online = false; + + return 0; +} + +static int spm_regulator_is_enabled(struct regulator_dev *rdev) +{ + struct spm_vreg *vreg = rdev_get_drvdata(rdev); + + return vreg->online; +} + +static unsigned int spm_regulator_get_mode(struct regulator_dev *rdev) +{ + struct spm_vreg *vreg = rdev_get_drvdata(rdev); + + return vreg->mode == QPNP_LOGICAL_MODE_PWM + ? REGULATOR_MODE_NORMAL : REGULATOR_MODE_IDLE; +} + +static int spm_regulator_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct spm_vreg *vreg = rdev_get_drvdata(rdev); + + /* + * Map REGULATOR_MODE_NORMAL to PWM mode and REGULATOR_MODE_IDLE to + * init_mode. This ensures that the regulator always stays in PWM mode + * in the case that qcom,mode has been specified as "pwm" in device + * tree. + */ + vreg->mode = (mode == REGULATOR_MODE_NORMAL) ? QPNP_LOGICAL_MODE_PWM + : vreg->init_mode; + + return qpnp_smps_set_mode(vreg, vreg->mode); +} + +static struct regulator_ops spm_regulator_ops = { + .get_voltage = spm_regulator_get_voltage, + .set_voltage = spm_regulator_set_voltage, + .list_voltage = spm_regulator_list_voltage, + .get_mode = spm_regulator_get_mode, + .set_mode = spm_regulator_set_mode, + .enable = spm_regulator_enable, + .disable = spm_regulator_disable, + .is_enabled = spm_regulator_is_enabled, +}; + +static int spm_regulator_avs_set_voltage(struct regulator_dev *rdev, int min_uV, + int max_uV, unsigned int *selector) +{ + struct spm_vreg *vreg = rdev_get_drvdata(rdev); + const struct voltage_range *range = vreg->range; + unsigned int vlevel_min, vlevel_max; + int uV, avs_min_uV, avs_max_uV, rc; + + uV = min_uV; + + if (uV < range->set_point_min_uV && max_uV >= range->set_point_min_uV) + uV = range->set_point_min_uV; + + if (uV < range->set_point_min_uV || uV > range->max_uV) { + pr_err("%s: request v=[%d, %d] is outside possible v=[%d, %d]\n", + vreg->avs_rdesc.name, min_uV, max_uV, + range->set_point_min_uV, range->max_uV); + return -EINVAL; + } + + vlevel_min = spm_regulator_uv_to_vlevel(vreg, uV); + avs_min_uV = spm_regulator_vlevel_to_uv(vreg, vlevel_min); + + if (avs_min_uV > max_uV) { + pr_err("%s: request v=[%d, %d] cannot be met by any set point\n", + vreg->avs_rdesc.name, min_uV, max_uV); + return -EINVAL; + } + + uV = max_uV; + + if (uV > range->max_uV && min_uV <= range->max_uV) + uV = range->max_uV; + + if (uV < range->set_point_min_uV || uV > range->max_uV) { + pr_err("%s: request v=[%d, %d] is outside possible v=[%d, %d]\n", + vreg->avs_rdesc.name, min_uV, max_uV, + range->set_point_min_uV, range->max_uV); + return -EINVAL; + } + + vlevel_max = spm_regulator_uv_to_vlevel(vreg, uV); + avs_max_uV = spm_regulator_vlevel_to_uv(vreg, vlevel_max); + + if (avs_max_uV < min_uV) { + pr_err("%s: request v=[%d, %d] cannot be met by any set point\n", + vreg->avs_rdesc.name, min_uV, max_uV); + return -EINVAL; + } + + if (likely(!vreg->bypass_spm)) { + rc = msm_spm_avs_set_limit(vreg->cpu_num, vlevel_min, + vlevel_max); + if (rc) { + pr_err("%s: AVS limit setting failed, rc=%d\n", + vreg->avs_rdesc.name, rc); + return rc; + } + } + + *selector = spm_regulator_vlevel_to_selector(vreg, vlevel_min); + vreg->avs_min_uV = avs_min_uV; + vreg->avs_max_uV = avs_max_uV; + + return 0; +} + +static int spm_regulator_avs_get_voltage(struct regulator_dev *rdev) +{ + struct spm_vreg *vreg = rdev_get_drvdata(rdev); + + return vreg->avs_min_uV; +} + +static int spm_regulator_avs_enable(struct regulator_dev *rdev) +{ + struct spm_vreg *vreg = rdev_get_drvdata(rdev); + int rc; + + if (likely(!vreg->bypass_spm)) { + rc = msm_spm_avs_enable(vreg->cpu_num); + if (rc) { + pr_err("%s: AVS enable failed, rc=%d\n", + vreg->avs_rdesc.name, rc); + return rc; + } + } + + vreg->avs_enabled = true; + + return 0; +} + +static int spm_regulator_avs_disable(struct regulator_dev *rdev) +{ + struct spm_vreg *vreg = rdev_get_drvdata(rdev); + int rc; + + if (likely(!vreg->bypass_spm)) { + rc = msm_spm_avs_disable(vreg->cpu_num); + if (rc) { + pr_err("%s: AVS disable failed, rc=%d\n", + vreg->avs_rdesc.name, rc); + return rc; + } + } + + vreg->avs_enabled = false; + + return 0; +} + +static int spm_regulator_avs_is_enabled(struct regulator_dev *rdev) +{ + struct spm_vreg *vreg = rdev_get_drvdata(rdev); + + return vreg->avs_enabled; +} + +static struct regulator_ops spm_regulator_avs_ops = { + .get_voltage = spm_regulator_avs_get_voltage, + .set_voltage = spm_regulator_avs_set_voltage, + .list_voltage = spm_regulator_list_voltage, + .enable = spm_regulator_avs_enable, + .disable = spm_regulator_avs_disable, + .is_enabled = spm_regulator_avs_is_enabled, +}; + +static int qpnp_smps_check_type(struct spm_vreg *vreg) +{ + int rc; + u8 type[2]; + + rc = regmap_bulk_read(vreg->regmap, + vreg->spmi_base_addr + QPNP_SMPS_REG_TYPE, + type, + 2); + if (rc) { + dev_err(&vreg->pdev->dev, + "%s: could not read type register, rc=%d\n", + __func__, rc); + return rc; + } + + if (type[0] == QPNP_FTS2_TYPE && type[1] == QPNP_FTS2_SUBTYPE) { + vreg->regulator_type = QPNP_TYPE_FTS2; + } else if (type[0] == QPNP_FTS2p5_TYPE + && type[1] == QPNP_FTS2p5_SUBTYPE) { + vreg->regulator_type = QPNP_TYPE_FTS2p5; + } else if (type[0] == QPNP_FTS426_TYPE + && type[1] == QPNP_FTS426_SUBTYPE) { + vreg->regulator_type = QPNP_TYPE_FTS426; + } else if (type[0] == QPNP_HF_TYPE + && type[1] == QPNP_HFS430_SUBTYPE) { + vreg->regulator_type = QPNP_TYPE_HFS430; + } else if (type[0] == QPNP_ULT_HF_TYPE + && type[1] == QPNP_ULT_HF_SUBTYPE) { + vreg->regulator_type = QPNP_TYPE_ULT_HF; + } else if (type[0] == QPNP_HF_TYPE + && type[1] == QPNP_HF_SUBTYPE) { + vreg->regulator_type = QPNP_TYPE_HF; + } else { + dev_err(&vreg->pdev->dev, + "%s: invalid type=0x%02X, subtype=0x%02X register pair\n", + __func__, type[0], type[1]); + return -ENODEV; + } + + return rc; +} + +static int qpnp_smps_init_range(struct spm_vreg *vreg, + const struct voltage_range *range0, const struct voltage_range *range1) +{ + int rc; + u8 reg = 0; + uint val; + + rc = regmap_read(vreg->regmap, + vreg->spmi_base_addr + QPNP_SMPS_REG_VOLTAGE_RANGE, + &val); + if (rc) { + dev_err(&vreg->pdev->dev, + "%s: could not read voltage range register, rc=%d\n", + __func__, rc); + return rc; + } + reg = (u8)val; + + if (reg == 0x00) { + vreg->range = range0; + } else if (reg == 0x01) { + vreg->range = range1; + } else { + dev_err(&vreg->pdev->dev, "%s: voltage range=%d is invalid\n", + __func__, reg); + rc = -EINVAL; + } + + return rc; +} + +static int qpnp_ult_hf_init_range(struct spm_vreg *vreg) +{ + int rc; + u8 reg = 0; + uint val; + + rc = regmap_read(vreg->regmap, + vreg->spmi_base_addr + QPNP_SMPS_REG_VOLTAGE_SETPOINT, + &val); + if (rc) { + dev_err(&vreg->pdev->dev, + "%s: could not read voltage range register, rc=%d\n", + __func__, rc); + return rc; + } + reg = (u8)val; + + vreg->range = (reg < ULT_SMPS_RANGE_SPLIT) ? &ult_hf_range0 : + &ult_hf_range1; + return rc; +} + +static int qpnp_smps_init_voltage(struct spm_vreg *vreg) +{ + int rc; + + rc = qpnp_smps_read_voltage(vreg); + if (rc) { + pr_err("%s: voltage read failed, rc=%d\n", vreg->rdesc.name, + rc); + return rc; + } + + vreg->vlevel = vreg->last_set_vlevel; + vreg->uV = vreg->last_set_uV; + + /* Initialize SAW voltage control register */ + if (!vreg->bypass_spm) { + rc = msm_spm_set_vdd(vreg->cpu_num, vreg->vlevel); + if (rc) + pr_err("%s: msm_spm_set_vdd failed, rc=%d\n", + vreg->rdesc.name, rc); + } + + return 0; +} + +static int qpnp_smps_init_mode(struct spm_vreg *vreg) +{ + const char *mode_name; + int rc; + uint val; + + rc = of_property_read_string(vreg->pdev->dev.of_node, "qcom,mode", + &mode_name); + if (!rc) { + if (strcmp("pwm", mode_name) == 0) { + vreg->init_mode = QPNP_LOGICAL_MODE_PWM; + } else if ((strcmp("auto", mode_name) == 0) && + (vreg->regulator_type != QPNP_TYPE_ULT_HF)) { + vreg->init_mode = QPNP_LOGICAL_MODE_AUTO; + } else { + dev_err(&vreg->pdev->dev, + "%s: unknown regulator mode: %s\n", + __func__, mode_name); + return -EINVAL; + } + + rc = qpnp_smps_set_mode(vreg, vreg->init_mode); + if (rc) + return rc; + } else { + rc = regmap_read(vreg->regmap, + vreg->spmi_base_addr + QPNP_SMPS_REG_MODE, + &val); + if (rc) + dev_err(&vreg->pdev->dev, + "%s: could not read mode register, rc=%d\n", + __func__, rc); + vreg->init_mode = qpnp_regval_to_mode(vreg, val); + } + + vreg->mode = vreg->init_mode; + + return rc; +} + +static int qpnp_smps_init_step_rate(struct spm_vreg *vreg) +{ + int rc; + u8 reg = 0; + int step = 0, delay; + uint val; + + rc = regmap_read(vreg->regmap, + vreg->spmi_base_addr + QPNP_SMPS_REG_STEP_CTRL, &val); + if (rc) { + dev_err(&vreg->pdev->dev, + "%s: could not read stepping control register, rc=%d\n", + __func__, rc); + return rc; + } + reg = (u8)val; + + /* ULT and FTS426 bucks do not support steps */ + if (vreg->regulator_type != QPNP_TYPE_ULT_HF && vreg->regulator_type != + QPNP_TYPE_FTS426 && vreg->regulator_type != QPNP_TYPE_HFS430) + step = (reg & QPNP_SMPS_STEP_CTRL_STEP_MASK) + >> QPNP_SMPS_STEP_CTRL_STEP_SHIFT; + + if (vreg->regulator_type == QPNP_TYPE_FTS426 + || vreg->regulator_type == QPNP_TYPE_HFS430) { + delay = (reg & QPNP_FTS426_HFS430_STEP_CTRL_DELAY_MASK) + >> QPNP_FTS426_HFS430_STEP_CTRL_DELAY_SHIFT; + + /* step_rate has units of uV/us. */ + vreg->step_rate = ((vreg->regulator_type == QPNP_TYPE_FTS426) + ? QPNP_FTS426_CLOCK_RATE + : QPNP_HFS430_CLOCK_RATE) + * vreg->range->step_uV; + } else { + delay = (reg & QPNP_SMPS_STEP_CTRL_DELAY_MASK) + >> QPNP_SMPS_STEP_CTRL_DELAY_SHIFT; + + /* step_rate has units of uV/us. */ + vreg->step_rate = QPNP_SMPS_CLOCK_RATE * vreg->range->step_uV + * (1 << step); + } + + if ((vreg->regulator_type == QPNP_TYPE_ULT_HF) + || (vreg->regulator_type == QPNP_TYPE_HF)) + vreg->step_rate /= 1000 * (QPNP_HF_STEP_DELAY << delay); + else if (vreg->regulator_type == QPNP_TYPE_FTS426 + || vreg->regulator_type == QPNP_TYPE_HFS430) + vreg->step_rate /= 1000 * (QPNP_FTS426_HFS430_STEP_DELAY + << delay); + else + vreg->step_rate /= 1000 * (QPNP_FTS2_STEP_DELAY << delay); + + if (vreg->regulator_type == QPNP_TYPE_FTS426 + || vreg->regulator_type == QPNP_TYPE_HFS430) + vreg->step_rate = vreg->step_rate + * QPNP_FTS426_HFS430_STEP_MARGIN_NUM + / QPNP_FTS426_HFS430_STEP_MARGIN_DEN; + else + vreg->step_rate = vreg->step_rate * QPNP_FTS2_STEP_MARGIN_NUM + / QPNP_FTS2_STEP_MARGIN_DEN; + + /* Ensure that the stepping rate is greater than 0. */ + vreg->step_rate = max(vreg->step_rate, 1); + + return rc; +} + +static int qpnp_smps_check_constraints(struct spm_vreg *vreg, + struct regulator_init_data *init_data) +{ + int rc = 0, limit_min_uV, limit_max_uV; + u16 ul_reg, ll_reg; + u8 reg[2]; + + limit_min_uV = 0; + limit_max_uV = INT_MAX; + + ul_reg = QPNP_FTS_REG_VOLTAGE_ULS_VALID; + ll_reg = QPNP_FTS_REG_VOLTAGE_LLS_VALID; + + switch (vreg->regulator_type) { + case QPNP_TYPE_HF: + ul_reg = QPNP_HF_REG_VOLTAGE_ULS; + ll_reg = QPNP_HF_REG_VOLTAGE_LLS; + case QPNP_TYPE_FTS2: + case QPNP_TYPE_FTS2p5: + rc = regmap_bulk_read(vreg->regmap, vreg->spmi_base_addr + + QPNP_SMPS_REG_UL_LL_CTRL, reg, 1); + if (rc) { + dev_err(&vreg->pdev->dev, "%s: UL_LL register read failed, rc=%d\n", + __func__, rc); + return rc; + } + + if (reg[0] & QPNP_COMMON_UL_EN_MASK) { + rc = regmap_bulk_read(vreg->regmap, vreg->spmi_base_addr + + ul_reg, ®[1], 1); + if (rc) { + dev_err(&vreg->pdev->dev, "%s: ULS register read failed, rc=%d\n", + __func__, rc); + return rc; + } + + limit_max_uV = spm_regulator_vlevel_to_uv(vreg, reg[1]); + } + + if (reg[0] & QPNP_COMMON_LL_EN_MASK) { + rc = regmap_bulk_read(vreg->regmap, vreg->spmi_base_addr + + ll_reg, ®[1], 1); + if (rc) { + dev_err(&vreg->pdev->dev, "%s: LLS register read failed, rc=%d\n", + __func__, rc); + return rc; + } + + limit_min_uV = spm_regulator_vlevel_to_uv(vreg, reg[1]); + } + + break; + case QPNP_TYPE_FTS426: + case QPNP_TYPE_HFS430: + rc = regmap_bulk_read(vreg->regmap, vreg->spmi_base_addr + + QPNP_FTS426_HFS430_REG_VOLTAGE_ULS_LB, + reg, 2); + if (rc) { + dev_err(&vreg->pdev->dev, "%s: could not read voltage limit registers, rc=%d\n", + __func__, rc); + return rc; + } + + limit_max_uV = spm_regulator_vlevel_to_uv(vreg, + ((unsigned int)reg[1] << 8) | reg[0]); + break; + case QPNP_TYPE_ULT_HF: + /* no HW voltage limit configuration */ + break; + } + + if (init_data->constraints.min_uV < limit_min_uV + || init_data->constraints.max_uV > limit_max_uV) { + dev_err(&vreg->pdev->dev, "regulator min/max(%d/%d) constraints do not fit within HW configured min/max(%d/%d) constraints\n", + init_data->constraints.min_uV, + init_data->constraints.max_uV, limit_min_uV, + limit_max_uV); + return -EINVAL; + } + + return rc; +} + +static bool spm_regulator_using_range0(struct spm_vreg *vreg) +{ + return vreg->range == &fts2_range0 || vreg->range == &fts2p5_range0 + || vreg->range == &ult_hf_range0 || vreg->range == &hf_range0 + || vreg->range == &fts426_range; +} + +/* Register a regulator to enable/disable AVS and set AVS min/max limits. */ +static int spm_regulator_avs_register(struct spm_vreg *vreg, + struct device *dev, struct device_node *node) +{ + struct regulator_config reg_config = {}; + struct device_node *avs_node = NULL; + struct device_node *child_node; + struct regulator_init_data *init_data; + int rc; + + /* + * Find the first available child node (if any). It corresponds to an + * AVS limits regulator. + */ + for_each_available_child_of_node(node, child_node) { + avs_node = child_node; + break; + } + + if (!avs_node) + return 0; + + init_data = of_get_regulator_init_data(dev, avs_node, &vreg->avs_rdesc); + if (!init_data) { + dev_err(dev, "%s: unable to allocate memory\n", __func__); + return -ENOMEM; + } + init_data->constraints.input_uV = init_data->constraints.max_uV; + init_data->constraints.valid_ops_mask |= REGULATOR_CHANGE_STATUS + | REGULATOR_CHANGE_VOLTAGE; + + if (!init_data->constraints.name) { + dev_err(dev, "%s: AVS node is missing regulator name\n", + __func__); + return -EINVAL; + } + + vreg->avs_rdesc.name = init_data->constraints.name; + vreg->avs_rdesc.type = REGULATOR_VOLTAGE; + vreg->avs_rdesc.owner = THIS_MODULE; + vreg->avs_rdesc.ops = &spm_regulator_avs_ops; + vreg->avs_rdesc.n_voltages + = (vreg->range->max_uV - vreg->range->set_point_min_uV) + / vreg->range->step_uV + 1; + + reg_config.dev = dev; + reg_config.init_data = init_data; + reg_config.driver_data = vreg; + reg_config.of_node = avs_node; + + vreg->avs_rdev = regulator_register(&vreg->avs_rdesc, ®_config); + if (IS_ERR(vreg->avs_rdev)) { + rc = PTR_ERR(vreg->avs_rdev); + dev_err(dev, "%s: AVS regulator_register failed, rc=%d\n", + __func__, rc); + return rc; + } + + if (vreg->bypass_spm) + pr_debug("%s: SPM bypassed so AVS regulator calls are no-ops\n", + vreg->avs_rdesc.name); + + return 0; +} + +static int spm_regulator_probe(struct platform_device *pdev) +{ + struct regulator_config reg_config = {}; + struct device_node *node = pdev->dev.of_node; + struct regulator_init_data *init_data; + struct spm_vreg *vreg; + unsigned int base; + bool bypass_spm; + int rc; + + if (!node) { + dev_err(&pdev->dev, "%s: device node missing\n", __func__); + return -ENODEV; + } + + bypass_spm = of_property_read_bool(node, "qcom,bypass-spm"); + if (!bypass_spm) { + rc = msm_spm_probe_done(); + if (rc) { + if (rc != -EPROBE_DEFER) + dev_err(&pdev->dev, + "%s: spm unavailable, rc=%d\n", + __func__, rc); + return rc; + } + } + + vreg = devm_kzalloc(&pdev->dev, sizeof(*vreg), GFP_KERNEL); + if (!vreg) + return -ENOMEM; + + vreg->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!vreg->regmap) { + dev_err(&pdev->dev, "Couldn't get parent's regmap\n"); + return -EINVAL; + } + vreg->pdev = pdev; + vreg->bypass_spm = bypass_spm; + + rc = of_property_read_u32(pdev->dev.of_node, "reg", &base); + if (rc < 0) { + dev_err(&pdev->dev, + "Couldn't find reg in node = %s rc = %d\n", + pdev->dev.of_node->full_name, rc); + return rc; + } + vreg->spmi_base_addr = base; + + rc = qpnp_smps_check_type(vreg); + if (rc) + return rc; + + /* Specify CPU 0 as default in order to handle shared regulator case. */ + vreg->cpu_num = 0; + of_property_read_u32(vreg->pdev->dev.of_node, "qcom,cpu-num", + &vreg->cpu_num); + + of_property_read_u32(vreg->pdev->dev.of_node, "qcom,recal-mask", + &vreg->recal_cluster_mask); + + /* + * The regulator must be initialized to range 0 or range 1 during + * PMIC power on sequence. Once it is set, it cannot be changed + * dynamically. + */ + if (vreg->regulator_type == QPNP_TYPE_FTS2) + rc = qpnp_smps_init_range(vreg, &fts2_range0, &fts2_range1); + else if (vreg->regulator_type == QPNP_TYPE_FTS2p5) + rc = qpnp_smps_init_range(vreg, &fts2p5_range0, &fts2p5_range1); + else if (vreg->regulator_type == QPNP_TYPE_FTS426) + vreg->range = &fts426_range; + else if (vreg->regulator_type == QPNP_TYPE_HFS430) + vreg->range = &hfs430_range; + else if (vreg->regulator_type == QPNP_TYPE_HF) + rc = qpnp_smps_init_range(vreg, &hf_range0, &hf_range1); + else if (vreg->regulator_type == QPNP_TYPE_ULT_HF) + rc = qpnp_ult_hf_init_range(vreg); + if (rc) + return rc; + + rc = qpnp_smps_init_voltage(vreg); + if (rc) + return rc; + + rc = qpnp_smps_init_mode(vreg); + if (rc) + return rc; + + rc = qpnp_smps_init_step_rate(vreg); + if (rc) + return rc; + + init_data = of_get_regulator_init_data(&pdev->dev, node, &vreg->rdesc); + if (!init_data) { + dev_err(&pdev->dev, "%s: unable to allocate memory\n", + __func__); + return -ENOMEM; + } + init_data->constraints.input_uV = init_data->constraints.max_uV; + init_data->constraints.valid_ops_mask |= REGULATOR_CHANGE_STATUS + | REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE; + init_data->constraints.valid_modes_mask + = REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE; + + if (!init_data->constraints.name) { + dev_err(&pdev->dev, "%s: node is missing regulator name\n", + __func__); + return -EINVAL; + } + + rc = qpnp_smps_check_constraints(vreg, init_data); + if (rc) { + dev_err(&pdev->dev, "%s: regulator constraints check failed, rc=%d\n", + __func__, rc); + return rc; + } + + vreg->rdesc.name = init_data->constraints.name; + vreg->rdesc.type = REGULATOR_VOLTAGE; + vreg->rdesc.owner = THIS_MODULE; + vreg->rdesc.ops = &spm_regulator_ops; + vreg->rdesc.n_voltages + = (vreg->range->max_uV - vreg->range->set_point_min_uV) + / vreg->range->step_uV + 1; + + vreg->max_step_uV = SPM_REGULATOR_MAX_STEP_UV; + of_property_read_u32(vreg->pdev->dev.of_node, + "qcom,max-voltage-step", &vreg->max_step_uV); + + if (vreg->max_step_uV > SPM_REGULATOR_MAX_STEP_UV) + vreg->max_step_uV = SPM_REGULATOR_MAX_STEP_UV; + + vreg->max_step_uV = rounddown(vreg->max_step_uV, vreg->range->step_uV); + pr_debug("%s: max single voltage step size=%u uV\n", + vreg->rdesc.name, vreg->max_step_uV); + + reg_config.dev = &pdev->dev; + reg_config.init_data = init_data; + reg_config.driver_data = vreg; + reg_config.of_node = node; + vreg->rdev = regulator_register(&vreg->rdesc, ®_config); + + if (IS_ERR(vreg->rdev)) { + rc = PTR_ERR(vreg->rdev); + dev_err(&pdev->dev, "%s: regulator_register failed, rc=%d\n", + __func__, rc); + return rc; + } + + rc = spm_regulator_avs_register(vreg, &pdev->dev, node); + if (rc) { + regulator_unregister(vreg->rdev); + return rc; + } + + dev_set_drvdata(&pdev->dev, vreg); + + pr_info("name=%s, range=%s, voltage=%d uV, mode=%s, step rate=%d uV/us\n", + vreg->rdesc.name, + spm_regulator_using_range0(vreg) ? "LV" : "MV", + vreg->uV, + vreg->init_mode == QPNP_LOGICAL_MODE_PWM ? "PWM" : + (vreg->init_mode == QPNP_LOGICAL_MODE_AUTO ? "AUTO" : "PFM"), + vreg->step_rate); + + return rc; +} + +static int spm_regulator_remove(struct platform_device *pdev) +{ + struct spm_vreg *vreg = dev_get_drvdata(&pdev->dev); + + if (vreg->avs_rdev) + regulator_unregister(vreg->avs_rdev); + regulator_unregister(vreg->rdev); + + return 0; +} + +static const struct of_device_id spm_regulator_match_table[] = { + { .compatible = SPM_REGULATOR_DRIVER_NAME, }, + {} +}; + +static const struct platform_device_id spm_regulator_id[] = { + { SPM_REGULATOR_DRIVER_NAME, 0 }, + {} +}; +MODULE_DEVICE_TABLE(spmi, spm_regulator_id); + +static struct platform_driver spm_regulator_driver = { + .driver = { + .name = SPM_REGULATOR_DRIVER_NAME, + .of_match_table = spm_regulator_match_table, + }, + .probe = spm_regulator_probe, + .remove = spm_regulator_remove, + .id_table = spm_regulator_id, +}; + +/** + * spm_regulator_init() - register spmi driver for spm-regulator + * + * This initialization function should be called in systems in which driver + * registration ordering must be controlled precisely. + * + * Returns 0 on success or errno on failure. + */ +int __init spm_regulator_init(void) +{ + static bool has_registered; + + if (has_registered) + return 0; + + has_registered = true; + + return platform_driver_register(&spm_regulator_driver); +} +EXPORT_SYMBOL(spm_regulator_init); + +static void __exit spm_regulator_exit(void) +{ + platform_driver_unregister(&spm_regulator_driver); +} + +arch_initcall(spm_regulator_init); +module_exit(spm_regulator_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("SPM regulator driver"); +MODULE_ALIAS("platform:spm-regulator"); diff --git a/drivers/regulator/tps65086-regulator.c b/drivers/regulator/tps65086-regulator.c index 45e96e15469005f376478d49dbc09045aae4bc3c..5a5e9b5bf4bea7c6935cca2423dce30475c4177c 100644 --- a/drivers/regulator/tps65086-regulator.c +++ b/drivers/regulator/tps65086-regulator.c @@ -90,8 +90,8 @@ static const struct regulator_linear_range tps65086_buck345_25mv_ranges[] = { static const struct regulator_linear_range tps65086_ldoa1_ranges[] = { REGULATOR_LINEAR_RANGE(1350000, 0x0, 0x0, 0), REGULATOR_LINEAR_RANGE(1500000, 0x1, 0x7, 100000), - REGULATOR_LINEAR_RANGE(2300000, 0x8, 0xA, 100000), - REGULATOR_LINEAR_RANGE(2700000, 0xB, 0xD, 150000), + REGULATOR_LINEAR_RANGE(2300000, 0x8, 0xB, 100000), + REGULATOR_LINEAR_RANGE(2850000, 0xC, 0xD, 150000), REGULATOR_LINEAR_RANGE(3300000, 0xE, 0xE, 0), }; diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c index 5a5bc4bb08d26ee78926aacaa906010cc1f426c2..df591435d12a376f051e79e670ade55ab6ef1b7a 100644 --- a/drivers/regulator/wm831x-dcdc.c +++ b/drivers/regulator/wm831x-dcdc.c @@ -327,8 +327,8 @@ static int wm831x_buckv_get_voltage_sel(struct regulator_dev *rdev) } /* Current limit options */ -static u16 wm831x_dcdc_ilim[] = { - 125, 250, 375, 500, 625, 750, 875, 1000 +static const unsigned int wm831x_dcdc_ilim[] = { + 125000, 250000, 375000, 500000, 625000, 750000, 875000, 1000000 }; static int wm831x_buckv_set_current_limit(struct regulator_dev *rdev, diff --git a/drivers/remoteproc/qcom_q6v5_pil.c b/drivers/remoteproc/qcom_q6v5_pil.c index 6a84b6372897dfce0b6cb640786e15f60574e8a7..cc475dcbf27f3ba2bf1b961637d9d28289915bf9 100644 --- a/drivers/remoteproc/qcom_q6v5_pil.c +++ b/drivers/remoteproc/qcom_q6v5_pil.c @@ -1268,16 +1268,26 @@ static const struct rproc_hexagon_res sdm845_mss = { static const struct rproc_hexagon_res msm8996_mss = { .hexagon_mba_image = "mba.mbn", + .proxy_supply = (struct qcom_mss_reg_res[]) { + { + .supply = "pll", + .uA = 100000, + }, + {} + }, .proxy_clk_names = (char*[]){ "xo", "pnoc", + "qdss", NULL }, .active_clk_names = (char*[]){ "iface", "bus", "mem", - "gpll0_mss_clk", + "gpll0_mss", + "snoc_axi", + "mnoc_axi", NULL }, .need_mem_protection = true, diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index aa6206706fe335008060a9e3da7a8a97d2f91e50..abbef17c97ee2a9ec44038ea94d3cf80e0febf7b 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -1786,7 +1786,7 @@ static int __init remoteproc_init(void) return 0; } -module_init(remoteproc_init); +subsys_initcall(remoteproc_init); static void __exit remoteproc_exit(void) { diff --git a/drivers/reset/reset-uniphier.c b/drivers/reset/reset-uniphier.c index 5605745663ae0d76122ed1e52e417e741b5ea8b2..adbecb2c7cd3adbebd2f2962e9a27c029636c182 100644 --- a/drivers/reset/reset-uniphier.c +++ b/drivers/reset/reset-uniphier.c @@ -202,8 +202,8 @@ static const struct uniphier_reset_data uniphier_pro5_sd_reset_data[] = { #define UNIPHIER_PERI_RESET_FI2C(id, ch) \ UNIPHIER_RESETX((id), 0x114, 24 + (ch)) -#define UNIPHIER_PERI_RESET_SCSSI(id) \ - UNIPHIER_RESETX((id), 0x110, 17) +#define UNIPHIER_PERI_RESET_SCSSI(id, ch) \ + UNIPHIER_RESETX((id), 0x110, 17 + (ch)) #define UNIPHIER_PERI_RESET_MCSSI(id) \ UNIPHIER_RESETX((id), 0x114, 14) @@ -218,7 +218,7 @@ static const struct uniphier_reset_data uniphier_ld4_peri_reset_data[] = { UNIPHIER_PERI_RESET_I2C(6, 2), UNIPHIER_PERI_RESET_I2C(7, 3), UNIPHIER_PERI_RESET_I2C(8, 4), - UNIPHIER_PERI_RESET_SCSSI(11), + UNIPHIER_PERI_RESET_SCSSI(11, 0), UNIPHIER_RESET_END, }; @@ -234,8 +234,11 @@ static const struct uniphier_reset_data uniphier_pro4_peri_reset_data[] = { UNIPHIER_PERI_RESET_FI2C(8, 4), UNIPHIER_PERI_RESET_FI2C(9, 5), UNIPHIER_PERI_RESET_FI2C(10, 6), - UNIPHIER_PERI_RESET_SCSSI(11), - UNIPHIER_PERI_RESET_MCSSI(12), + UNIPHIER_PERI_RESET_SCSSI(11, 0), + UNIPHIER_PERI_RESET_SCSSI(12, 1), + UNIPHIER_PERI_RESET_SCSSI(13, 2), + UNIPHIER_PERI_RESET_SCSSI(14, 3), + UNIPHIER_PERI_RESET_MCSSI(15), UNIPHIER_RESET_END, }; diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 56b7e0e89b19af78182a7f69f77a5c13c992fa3a..a3353005ad371682ac21fdfa50fca70571bad6b6 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -974,6 +974,14 @@ static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail) /* Drop the message */ goto advance_rx; } + + if (!channel->ept.cb) { + dev_err(glink->dev, + "Callback not available on channel %s\n", + channel->name); + return -EAGAIN; + } + CH_INFO(channel, "chunk_size:%d left_size:%d\n", chunk_size, left_size); if (glink->intentless) { diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 0fca4d74c76b34a645d3b65382a46f16c9bf9501..8c0e76ec868f085488358a039f3e8e6487c31f12 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -341,6 +341,11 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev, dev_info(dev, "rtc core: registered %s as %s\n", name, dev_name(&rtc->dev)); +#ifdef CONFIG_RTC_HCTOSYS_DEVICE + if (!strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE)) + rtc_hctosys(); +#endif + return rtc; exit_ida: @@ -527,6 +532,11 @@ int __rtc_register_device(struct module *owner, struct rtc_device *rtc) dev_info(rtc->dev.parent, "registered as %s\n", dev_name(&rtc->dev)); +#ifdef CONFIG_RTC_HCTOSYS_DEVICE + if (!strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE)) + rtc_hctosys(); +#endif + return 0; } EXPORT_SYMBOL_GPL(__rtc_register_device); diff --git a/drivers/rtc/hctosys.c b/drivers/rtc/hctosys.c index b9ec4a16db1f6b6fd113c5661a28aa0e9153eeaa..02b71afa330c4b0708b2bc81152f3139cb53e731 100644 --- a/drivers/rtc/hctosys.c +++ b/drivers/rtc/hctosys.c @@ -24,7 +24,7 @@ * the best guess is to add 0.5s. */ -static int __init rtc_hctosys(void) +int rtc_hctosys(void) { int err = -ENODEV; struct rtc_time tm; @@ -73,5 +73,3 @@ static int __init rtc_hctosys(void) return err; } - -late_initcall(rtc_hctosys); diff --git a/drivers/rtc/rtc-88pm80x.c b/drivers/rtc/rtc-88pm80x.c index cab293cb2bf0b335bdd07940f713965720f00830..1fc48ebd3cd05ebe6a6d00b983a7444eeb773921 100644 --- a/drivers/rtc/rtc-88pm80x.c +++ b/drivers/rtc/rtc-88pm80x.c @@ -114,12 +114,14 @@ static int pm80x_rtc_read_time(struct device *dev, struct rtc_time *tm) unsigned char buf[4]; unsigned long ticks, base, data; regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4); - base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + base = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]); /* load 32-bit read-only counter */ regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4); - data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; ticks = base + data; dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", base, data, ticks); @@ -137,7 +139,8 @@ static int pm80x_rtc_set_time(struct device *dev, struct rtc_time *tm) /* load 32-bit read-only counter */ regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4); - data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; base = ticks - data; dev_dbg(info->dev, "set base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", base, data, ticks); @@ -158,11 +161,13 @@ static int pm80x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) int ret; regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4); - base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + base = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]); regmap_raw_read(info->map, PM800_RTC_EXPIRE1_1, buf, 4); - data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; ticks = base + data; dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", base, data, ticks); @@ -185,12 +190,14 @@ static int pm80x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) regmap_update_bits(info->map, PM800_RTC_CONTROL, PM800_ALARM1_EN, 0); regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4); - base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + base = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]); /* load 32-bit read-only counter */ regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4); - data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; ticks = base + data; dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", base, data, ticks); diff --git a/drivers/rtc/rtc-88pm860x.c b/drivers/rtc/rtc-88pm860x.c index fbcf13bbbd8d11b4bbe876c2f5e40f698250f8e8..73697e4b18a9d4d9d85546a1d807a861966df8d8 100644 --- a/drivers/rtc/rtc-88pm860x.c +++ b/drivers/rtc/rtc-88pm860x.c @@ -115,11 +115,13 @@ static int pm860x_rtc_read_time(struct device *dev, struct rtc_time *tm) pm860x_page_bulk_read(info->i2c, REG0_ADDR, 8, buf); dev_dbg(info->dev, "%x-%x-%x-%x-%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); - base = (buf[1] << 24) | (buf[3] << 16) | (buf[5] << 8) | buf[7]; + base = ((unsigned long)buf[1] << 24) | (buf[3] << 16) | + (buf[5] << 8) | buf[7]; /* load 32-bit read-only counter */ pm860x_bulk_read(info->i2c, PM8607_RTC_COUNTER1, 4, buf); - data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; ticks = base + data; dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", base, data, ticks); @@ -145,7 +147,8 @@ static int pm860x_rtc_set_time(struct device *dev, struct rtc_time *tm) /* load 32-bit read-only counter */ pm860x_bulk_read(info->i2c, PM8607_RTC_COUNTER1, 4, buf); - data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; base = ticks - data; dev_dbg(info->dev, "set base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", base, data, ticks); @@ -170,10 +173,12 @@ static int pm860x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) pm860x_page_bulk_read(info->i2c, REG0_ADDR, 8, buf); dev_dbg(info->dev, "%x-%x-%x-%x-%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); - base = (buf[1] << 24) | (buf[3] << 16) | (buf[5] << 8) | buf[7]; + base = ((unsigned long)buf[1] << 24) | (buf[3] << 16) | + (buf[5] << 8) | buf[7]; pm860x_bulk_read(info->i2c, PM8607_RTC_EXPIRE1, 4, buf); - data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; ticks = base + data; dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", base, data, ticks); @@ -198,11 +203,13 @@ static int pm860x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) pm860x_page_bulk_read(info->i2c, REG0_ADDR, 8, buf); dev_dbg(info->dev, "%x-%x-%x-%x-%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); - base = (buf[1] << 24) | (buf[3] << 16) | (buf[5] << 8) | buf[7]; + base = ((unsigned long)buf[1] << 24) | (buf[3] << 16) | + (buf[5] << 8) | buf[7]; /* load 32-bit read-only counter */ pm860x_bulk_read(info->i2c, PM8607_RTC_COUNTER1, 4, buf); - data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; ticks = base + data; dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", base, data, ticks); diff --git a/drivers/rtc/rtc-brcmstb-waketimer.c b/drivers/rtc/rtc-brcmstb-waketimer.c index f4010a75f2bef728b084ecb18692e38755ce62a9..1abc8397850a60997fa84f324cff519fe9763cc8 100644 --- a/drivers/rtc/rtc-brcmstb-waketimer.c +++ b/drivers/rtc/rtc-brcmstb-waketimer.c @@ -287,6 +287,7 @@ static int brcmstb_waketmr_remove(struct platform_device *pdev) struct brcmstb_waketmr *timer = dev_get_drvdata(&pdev->dev); unregister_reboot_notifier(&timer->reboot_notifier); + clk_disable_unprepare(timer->clk); return 0; } diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index a5a19ff10535463d91d39d69ced1f13110ff139d..0fa94d9e8d44dc22ac1ec8a55c56665c5c6e218e 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -854,7 +854,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) rtc_cmos_int_handler = cmos_interrupt; retval = request_irq(rtc_irq, rtc_cmos_int_handler, - IRQF_SHARED, dev_name(&cmos_rtc.rtc->dev), + 0, dev_name(&cmos_rtc.rtc->dev), cmos_rtc.rtc); if (retval < 0) { dev_dbg(dev, "IRQ %d is already in use\n", rtc_irq); diff --git a/drivers/rtc/rtc-core.h b/drivers/rtc/rtc-core.h index ccc17a2e293d178985a496fa87e696cac5491fe9..57a8a18e6eb40afd888b54f02fe8c5dfc4ce0afb 100644 --- a/drivers/rtc/rtc-core.h +++ b/drivers/rtc/rtc-core.h @@ -60,3 +60,7 @@ int rtc_add_groups(struct rtc_device *rtc, const struct attribute_group **grps) return 0; } #endif + +#ifdef CONFIG_RTC_HCTOSYS +extern int rtc_hctosys(void); +#endif diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 71396b62dc52b230fe33e87ae4ac235cb8e16bde..ebd59e86a567b5b41d989b168e753defba492820 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -749,8 +749,8 @@ static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t) if (ret < 0) return ret; - ctl[0] &= ~RX8130_REG_EXTENSION_WADA; - ctl[1] |= RX8130_REG_FLAG_AF; + ctl[0] &= RX8130_REG_EXTENSION_WADA; + ctl[1] &= ~RX8130_REG_FLAG_AF; ctl[2] &= ~RX8130_REG_CONTROL0_AIE; ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, @@ -773,8 +773,7 @@ static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t) ctl[2] |= RX8130_REG_CONTROL0_AIE; - return regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, - sizeof(ctl)); + return regmap_write(ds1307->regmap, RX8130_REG_CONTROL0, ctl[2]); } static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled) diff --git a/drivers/rtc/rtc-ds1672.c b/drivers/rtc/rtc-ds1672.c index 9caaccccaa575f41fb3280f3753d8dea8ec3c9db..b1ebca099b0dffb064d59c8dfa8821f55374f48c 100644 --- a/drivers/rtc/rtc-ds1672.c +++ b/drivers/rtc/rtc-ds1672.c @@ -58,7 +58,8 @@ static int ds1672_get_datetime(struct i2c_client *client, struct rtc_time *tm) "%s: raw read data - counters=%02x,%02x,%02x,%02x\n", __func__, buf[0], buf[1], buf[2], buf[3]); - time = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + time = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; rtc_time_to_tm(time, tm); diff --git a/drivers/rtc/rtc-hym8563.c b/drivers/rtc/rtc-hym8563.c index e5ad527cb75e369945a7a533105d6d0d8573ac10..a8c2d38b24112f53b9e7561c9843ab3deca86db7 100644 --- a/drivers/rtc/rtc-hym8563.c +++ b/drivers/rtc/rtc-hym8563.c @@ -105,7 +105,7 @@ static int hym8563_rtc_read_time(struct device *dev, struct rtc_time *tm) if (!hym8563->valid) { dev_warn(&client->dev, "no valid clock/calendar values available\n"); - return -EPERM; + return -EINVAL; } ret = i2c_smbus_read_i2c_block_data(client, HYM8563_SEC, 7, buf); diff --git a/drivers/rtc/rtc-mc146818-lib.c b/drivers/rtc/rtc-mc146818-lib.c index 2f1772a358ca50342d4375de3e69f2fba451ceaf..18a6f15e313d8b33ae6ae2905ea6b364e9ede701 100644 --- a/drivers/rtc/rtc-mc146818-lib.c +++ b/drivers/rtc/rtc-mc146818-lib.c @@ -82,7 +82,7 @@ unsigned int mc146818_get_time(struct rtc_time *time) time->tm_year += real_year - 72; #endif - if (century) + if (century > 20) time->tm_year += (century - 19) * 100; /* diff --git a/drivers/rtc/rtc-msm6242.c b/drivers/rtc/rtc-msm6242.c index 0c72a2e8ec67d27f5bfd296a7772210371c3d88f..6aace9319fe94aa2eacdac82bbe77b193dd8475a 100644 --- a/drivers/rtc/rtc-msm6242.c +++ b/drivers/rtc/rtc-msm6242.c @@ -132,7 +132,8 @@ static int msm6242_read_time(struct device *dev, struct rtc_time *tm) msm6242_read(priv, MSM6242_SECOND1); tm->tm_min = msm6242_read(priv, MSM6242_MINUTE10) * 10 + msm6242_read(priv, MSM6242_MINUTE1); - tm->tm_hour = (msm6242_read(priv, MSM6242_HOUR10 & 3)) * 10 + + tm->tm_hour = (msm6242_read(priv, MSM6242_HOUR10) & + MSM6242_HOUR10_HR_MASK) * 10 + msm6242_read(priv, MSM6242_HOUR1); tm->tm_mday = msm6242_read(priv, MSM6242_DAY10) * 10 + msm6242_read(priv, MSM6242_DAY1); diff --git a/drivers/rtc/rtc-mt6397.c b/drivers/rtc/rtc-mt6397.c index e9a25ec4d434f9c30c9b6a46c272547374d8a026..964ed91416e1b174da0d0bb00bf2d67d3f95d092 100644 --- a/drivers/rtc/rtc-mt6397.c +++ b/drivers/rtc/rtc-mt6397.c @@ -55,6 +55,14 @@ #define RTC_AL_SEC 0x0018 +#define RTC_AL_SEC_MASK 0x003f +#define RTC_AL_MIN_MASK 0x003f +#define RTC_AL_HOU_MASK 0x001f +#define RTC_AL_DOM_MASK 0x001f +#define RTC_AL_DOW_MASK 0x0007 +#define RTC_AL_MTH_MASK 0x000f +#define RTC_AL_YEA_MASK 0x007f + #define RTC_PDN2 0x002e #define RTC_PDN2_PWRON_ALARM BIT(4) @@ -111,7 +119,7 @@ static irqreturn_t mtk_rtc_irq_handler_thread(int irq, void *data) irqen = irqsta & ~RTC_IRQ_EN_AL; mutex_lock(&rtc->lock); if (regmap_write(rtc->regmap, rtc->addr_base + RTC_IRQ_EN, - irqen) < 0) + irqen) == 0) mtk_rtc_write_trigger(rtc); mutex_unlock(&rtc->lock); @@ -233,12 +241,12 @@ static int mtk_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) alm->pending = !!(pdn2 & RTC_PDN2_PWRON_ALARM); mutex_unlock(&rtc->lock); - tm->tm_sec = data[RTC_OFFSET_SEC]; - tm->tm_min = data[RTC_OFFSET_MIN]; - tm->tm_hour = data[RTC_OFFSET_HOUR]; - tm->tm_mday = data[RTC_OFFSET_DOM]; - tm->tm_mon = data[RTC_OFFSET_MTH]; - tm->tm_year = data[RTC_OFFSET_YEAR]; + tm->tm_sec = data[RTC_OFFSET_SEC] & RTC_AL_SEC_MASK; + tm->tm_min = data[RTC_OFFSET_MIN] & RTC_AL_MIN_MASK; + tm->tm_hour = data[RTC_OFFSET_HOUR] & RTC_AL_HOU_MASK; + tm->tm_mday = data[RTC_OFFSET_DOM] & RTC_AL_DOM_MASK; + tm->tm_mon = data[RTC_OFFSET_MTH] & RTC_AL_MTH_MASK; + tm->tm_year = data[RTC_OFFSET_YEAR] & RTC_AL_YEA_MASK; tm->tm_year += RTC_MIN_YEAR_OFFSET; tm->tm_mon--; @@ -259,14 +267,25 @@ static int mtk_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) tm->tm_year -= RTC_MIN_YEAR_OFFSET; tm->tm_mon++; - data[RTC_OFFSET_SEC] = tm->tm_sec; - data[RTC_OFFSET_MIN] = tm->tm_min; - data[RTC_OFFSET_HOUR] = tm->tm_hour; - data[RTC_OFFSET_DOM] = tm->tm_mday; - data[RTC_OFFSET_MTH] = tm->tm_mon; - data[RTC_OFFSET_YEAR] = tm->tm_year; - mutex_lock(&rtc->lock); + ret = regmap_bulk_read(rtc->regmap, rtc->addr_base + RTC_AL_SEC, + data, RTC_OFFSET_COUNT); + if (ret < 0) + goto exit; + + data[RTC_OFFSET_SEC] = ((data[RTC_OFFSET_SEC] & ~(RTC_AL_SEC_MASK)) | + (tm->tm_sec & RTC_AL_SEC_MASK)); + data[RTC_OFFSET_MIN] = ((data[RTC_OFFSET_MIN] & ~(RTC_AL_MIN_MASK)) | + (tm->tm_min & RTC_AL_MIN_MASK)); + data[RTC_OFFSET_HOUR] = ((data[RTC_OFFSET_HOUR] & ~(RTC_AL_HOU_MASK)) | + (tm->tm_hour & RTC_AL_HOU_MASK)); + data[RTC_OFFSET_DOM] = ((data[RTC_OFFSET_DOM] & ~(RTC_AL_DOM_MASK)) | + (tm->tm_mday & RTC_AL_DOM_MASK)); + data[RTC_OFFSET_MTH] = ((data[RTC_OFFSET_MTH] & ~(RTC_AL_MTH_MASK)) | + (tm->tm_mon & RTC_AL_MTH_MASK)); + data[RTC_OFFSET_YEAR] = ((data[RTC_OFFSET_YEAR] & ~(RTC_AL_YEA_MASK)) | + (tm->tm_year & RTC_AL_YEA_MASK)); + if (alm->enabled) { ret = regmap_bulk_write(rtc->regmap, rtc->addr_base + RTC_AL_SEC, @@ -343,7 +362,7 @@ static int mtk_rtc_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n", rtc->irq, ret); - goto out_dispose_irq; + return ret; } device_init_wakeup(&pdev->dev, 1); @@ -359,9 +378,7 @@ static int mtk_rtc_probe(struct platform_device *pdev) return 0; out_free_irq: - free_irq(rtc->irq, rtc->rtc_dev); -out_dispose_irq: - irq_dispose_mapping(rtc->irq); + free_irq(rtc->irq, rtc); return ret; } @@ -369,8 +386,7 @@ static int mtk_rtc_remove(struct platform_device *pdev) { struct mt6397_rtc *rtc = platform_get_drvdata(pdev); - free_irq(rtc->irq, rtc->rtc_dev); - irq_dispose_mapping(rtc->irq); + free_irq(rtc->irq, rtc); return 0; } diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c index 7cb786d76e3c1da81bf5055bf01375546a74e9ff..8c62406f92dd1633b1020ce75110d456115ae74b 100644 --- a/drivers/rtc/rtc-pcf2127.c +++ b/drivers/rtc/rtc-pcf2127.c @@ -57,20 +57,14 @@ static int pcf2127_rtc_read_time(struct device *dev, struct rtc_time *tm) struct pcf2127 *pcf2127 = dev_get_drvdata(dev); unsigned char buf[10]; int ret; - int i; - for (i = 0; i <= PCF2127_REG_CTRL3; i++) { - ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL1 + i, - (unsigned int *)(buf + i)); - if (ret) { - dev_err(dev, "%s: read error\n", __func__); - return ret; - } - } - - ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_SC, - (buf + PCF2127_REG_SC), - ARRAY_SIZE(buf) - PCF2127_REG_SC); + /* + * 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); if (ret) { dev_err(dev, "%s: read error\n", __func__); return ret; @@ -91,14 +85,12 @@ static int pcf2127_rtc_read_time(struct device *dev, struct rtc_time *tm) } dev_dbg(dev, - "%s: raw data is cr1=%02x, cr2=%02x, cr3=%02x, " - "sec=%02x, min=%02x, hr=%02x, " + "%s: raw data is cr3=%02x, sec=%02x, min=%02x, hr=%02x, " "mday=%02x, wday=%02x, mon=%02x, year=%02x\n", - __func__, - buf[0], buf[1], buf[2], - buf[3], buf[4], buf[5], - buf[6], buf[7], buf[8], buf[9]); - + __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); diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index 3efc86c25d27a5754031f95136522766e67cd563..d8adf69b66978412343f965b34fe78cc2f87c1d8 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c @@ -563,7 +563,6 @@ static int pcf8563_probe(struct i2c_client *client, struct pcf8563 *pcf8563; int err; unsigned char buf; - unsigned char alm_pending; dev_dbg(&client->dev, "%s\n", __func__); @@ -587,13 +586,13 @@ static int pcf8563_probe(struct i2c_client *client, return err; } - err = pcf8563_get_alarm_mode(client, NULL, &alm_pending); - if (err) { - dev_err(&client->dev, "%s: read error\n", __func__); + /* Clear flags and disable interrupts */ + buf = 0; + err = pcf8563_write_block_data(client, PCF8563_REG_ST2, 1, &buf); + if (err < 0) { + dev_err(&client->dev, "%s: write error\n", __func__); return err; } - if (alm_pending) - pcf8563_set_alarm_mode(client, 0); pcf8563->rtc = devm_rtc_device_register(&client->dev, pcf8563_driver.driver.name, @@ -605,7 +604,7 @@ static int pcf8563_probe(struct i2c_client *client, if (client->irq > 0) { err = devm_request_threaded_irq(&client->dev, client->irq, NULL, pcf8563_irq, - IRQF_SHARED|IRQF_ONESHOT|IRQF_TRIGGER_FALLING, + IRQF_SHARED | IRQF_ONESHOT | IRQF_TRIGGER_LOW, pcf8563_driver.driver.name, client); if (err) { dev_err(&client->dev, "unable to request IRQ %d\n", diff --git a/drivers/rtc/rtc-pm8xxx.c b/drivers/rtc/rtc-pm8xxx.c index d38f38ddb2a3056ed05c32dbd9031a18bee0c6be..40fefd9c38b186e301ccbd75764d142e5a6695d3 100644 --- a/drivers/rtc/rtc-pm8xxx.c +++ b/drivers/rtc/rtc-pm8xxx.c @@ -208,7 +208,8 @@ static int pm8xxx_rtc_read_time(struct device *dev, struct rtc_time *tm) } } - secs = value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24); + secs = value[0] | (value[1] << 8) | (value[2] << 16) | + ((unsigned long)value[3] << 24); rtc_time_to_tm(secs, tm); @@ -283,7 +284,8 @@ static int pm8xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) return rc; } - secs = value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24); + secs = value[0] | (value[1] << 8) | (value[2] << 16) | + ((unsigned long)value[3] << 24); rtc_time_to_tm(secs, &alarm->time); diff --git a/drivers/rtc/rtc-rv3029c2.c b/drivers/rtc/rtc-rv3029c2.c index 3d6174eb32f6a3b09920c7a9eb370fcd1fc95c89..cfe3aece51d17759fcb5fa25471aa09af04d515a 100644 --- a/drivers/rtc/rtc-rv3029c2.c +++ b/drivers/rtc/rtc-rv3029c2.c @@ -282,13 +282,13 @@ static int rv3029_eeprom_read(struct device *dev, u8 reg, static int rv3029_eeprom_write(struct device *dev, u8 reg, u8 const buf[], size_t len) { - int ret; + int ret, err; size_t i; u8 tmp; - ret = rv3029_eeprom_enter(dev); - if (ret < 0) - return ret; + err = rv3029_eeprom_enter(dev); + if (err < 0) + return err; for (i = 0; i < len; i++, reg++) { ret = rv3029_read_regs(dev, reg, &tmp, 1); @@ -304,11 +304,11 @@ static int rv3029_eeprom_write(struct device *dev, u8 reg, break; } - ret = rv3029_eeprom_exit(dev); - if (ret < 0) - return ret; + err = rv3029_eeprom_exit(dev); + if (err < 0) + return err; - return 0; + return ret; } static int rv3029_eeprom_update_bits(struct device *dev, diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c index 2a3f874a21d548ef6a04bca6e3a8955f3668d291..9cebff8e8d74001f38f0148cf9118a163f2a1c37 100644 --- a/drivers/s390/cio/blacklist.c +++ b/drivers/s390/cio/blacklist.c @@ -303,8 +303,10 @@ static void * cio_ignore_proc_seq_next(struct seq_file *s, void *it, loff_t *offset) { struct ccwdev_iter *iter; + loff_t p = *offset; - if (*offset >= (__MAX_SUBCHANNEL + 1) * (__MAX_SSID + 1)) + (*offset)++; + if (p >= (__MAX_SUBCHANNEL + 1) * (__MAX_SSID + 1)) return NULL; iter = it; if (iter->devno == __MAX_SUBCHANNEL) { @@ -314,7 +316,6 @@ cio_ignore_proc_seq_next(struct seq_file *s, void *it, loff_t *offset) return NULL; } else iter->devno++; - (*offset)++; return iter; } diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c index 034528a5453ec4b058b86c65f513037371a4e37d..d040c4920ee784e4d09b0b63dcff0601a4f5454e 100644 --- a/drivers/s390/cio/qdio_setup.c +++ b/drivers/s390/cio/qdio_setup.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "cio.h" @@ -208,7 +209,7 @@ static void setup_storage_lists(struct qdio_q *q, struct qdio_irq *irq_ptr, /* fill in sl */ for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; j++) - q->sl->element[j].sbal = (unsigned long)q->sbal[j]; + q->sl->element[j].sbal = virt_to_phys(q->sbal[j]); } static void setup_queues(struct qdio_irq *irq_ptr, diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h index 7e85d238767ba1d42938a24b67381221f7cc0593..1c799ddd970923609c6821a6d3597f059675859d 100644 --- a/drivers/s390/crypto/ap_bus.h +++ b/drivers/s390/crypto/ap_bus.h @@ -158,7 +158,7 @@ struct ap_card { unsigned int functions; /* AP device function bitfield. */ int queue_depth; /* AP queue depth.*/ int id; /* AP card number. */ - atomic_t total_request_count; /* # requests ever for this AP device.*/ + atomic64_t total_request_count; /* # requests ever for this AP device.*/ }; #define to_ap_card(x) container_of((x), struct ap_card, ap_dev.device) @@ -175,7 +175,7 @@ struct ap_queue { enum ap_state state; /* State of the AP device. */ int pendingq_count; /* # requests on pendingq list. */ int requestq_count; /* # requests on requestq list. */ - int total_request_count; /* # requests ever for this AP device.*/ + u64 total_request_count; /* # requests ever for this AP device.*/ int request_timeout; /* Request timeout in jiffies. */ struct timer_list timeout; /* Timer for request timeouts. */ struct list_head pendingq; /* List of message sent to AP queue. */ diff --git a/drivers/s390/crypto/ap_card.c b/drivers/s390/crypto/ap_card.c index 63b4cc6cd7e596f7b51681391eb17ddffb6d0512..e85bfca1ed163575989cae3e8120b235369ba0b4 100644 --- a/drivers/s390/crypto/ap_card.c +++ b/drivers/s390/crypto/ap_card.c @@ -63,13 +63,13 @@ static ssize_t request_count_show(struct device *dev, char *buf) { struct ap_card *ac = to_ap_card(dev); - unsigned int req_cnt; + u64 req_cnt; req_cnt = 0; spin_lock_bh(&ap_list_lock); - req_cnt = atomic_read(&ac->total_request_count); + req_cnt = atomic64_read(&ac->total_request_count); spin_unlock_bh(&ap_list_lock); - return snprintf(buf, PAGE_SIZE, "%d\n", req_cnt); + return snprintf(buf, PAGE_SIZE, "%llu\n", req_cnt); } static ssize_t request_count_store(struct device *dev, @@ -83,7 +83,7 @@ static ssize_t request_count_store(struct device *dev, for_each_ap_queue(aq, ac) aq->total_request_count = 0; spin_unlock_bh(&ap_list_lock); - atomic_set(&ac->total_request_count, 0); + atomic64_set(&ac->total_request_count, 0); return count; } diff --git a/drivers/s390/crypto/ap_queue.c b/drivers/s390/crypto/ap_queue.c index 576ac08777c509ee038a50ec95a892fc79776355..e1647da122f7f75cd4a9c4755fd44b05f4605e47 100644 --- a/drivers/s390/crypto/ap_queue.c +++ b/drivers/s390/crypto/ap_queue.c @@ -470,12 +470,12 @@ static ssize_t request_count_show(struct device *dev, char *buf) { struct ap_queue *aq = to_ap_queue(dev); - unsigned int req_cnt; + u64 req_cnt; spin_lock_bh(&aq->lock); req_cnt = aq->total_request_count; spin_unlock_bh(&aq->lock); - return snprintf(buf, PAGE_SIZE, "%d\n", req_cnt); + return snprintf(buf, PAGE_SIZE, "%llu\n", req_cnt); } static ssize_t request_count_store(struct device *dev, @@ -667,7 +667,7 @@ void ap_queue_message(struct ap_queue *aq, struct ap_message *ap_msg) list_add_tail(&ap_msg->list, &aq->requestq); aq->requestq_count++; aq->total_request_count++; - atomic_inc(&aq->card->total_request_count); + atomic64_inc(&aq->card->total_request_count); /* Send/receive as many request from the queue as possible. */ ap_wait(ap_sm_event_loop(aq, AP_EVENT_POLL)); spin_unlock_bh(&aq->lock); diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index b2737bfeb8bb68e06e64d75923fdf36ba6687574..23c24a699cefe50f9b2d1970c0d2bcb312bcfbba 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -190,8 +190,8 @@ static inline bool zcrypt_card_compare(struct zcrypt_card *zc, weight += atomic_read(&zc->load); pref_weight += atomic_read(&pref_zc->load); if (weight == pref_weight) - return atomic_read(&zc->card->total_request_count) > - atomic_read(&pref_zc->card->total_request_count); + return atomic64_read(&zc->card->total_request_count) > + atomic64_read(&pref_zc->card->total_request_count); return weight > pref_weight; } @@ -719,11 +719,12 @@ static void zcrypt_qdepth_mask(char qdepth[], size_t max_adapters) spin_unlock(&zcrypt_list_lock); } -static void zcrypt_perdev_reqcnt(int reqcnt[], size_t max_adapters) +static void zcrypt_perdev_reqcnt(u32 reqcnt[], size_t max_adapters) { struct zcrypt_card *zc; struct zcrypt_queue *zq; int card; + u64 cnt; memset(reqcnt, 0, sizeof(int) * max_adapters); spin_lock(&zcrypt_list_lock); @@ -735,8 +736,9 @@ static void zcrypt_perdev_reqcnt(int reqcnt[], size_t max_adapters) || card >= max_adapters) continue; spin_lock(&zq->queue->lock); - reqcnt[card] = zq->queue->total_request_count; + cnt = zq->queue->total_request_count; spin_unlock(&zq->queue->lock); + reqcnt[card] = (cnt < UINT_MAX) ? (u32) cnt : UINT_MAX; } } local_bh_enable(); @@ -907,9 +909,9 @@ static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd, return 0; } case ZCRYPT_PERDEV_REQCNT: { - int *reqcnt; + u32 *reqcnt; - reqcnt = kcalloc(AP_DEVICES, sizeof(int), GFP_KERNEL); + reqcnt = kcalloc(AP_DEVICES, sizeof(u32), GFP_KERNEL); if (!reqcnt) return -ENOMEM; zcrypt_perdev_reqcnt(reqcnt, AP_DEVICES); @@ -966,7 +968,7 @@ static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd, } case Z90STAT_PERDEV_REQCNT: { /* the old ioctl supports only 64 adapters */ - int reqcnt[MAX_ZDEV_CARDIDS]; + u32 reqcnt[MAX_ZDEV_CARDIDS]; zcrypt_perdev_reqcnt(reqcnt, MAX_ZDEV_CARDIDS); if (copy_to_user((int __user *) arg, reqcnt, sizeof(reqcnt))) diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 81e2c591acb0ba3f416e5d624fae707897d3d344..d99bfbfcafb7646309ef08fd853bff938773705b 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -4929,10 +4929,10 @@ static void qeth_qdio_establish_cq(struct qeth_card *card, if (card->options.cq == QETH_CQ_ENABLED) { int offset = QDIO_MAX_BUFFERS_PER_Q * (card->qdio.no_in_queues - 1); - for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; ++i) { - in_sbal_ptrs[offset + i] = (struct qdio_buffer *) - virt_to_phys(card->qdio.c_q->bufs[i].buffer); - } + + for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; i++) + in_sbal_ptrs[offset + i] = + card->qdio.c_q->bufs[i].buffer; queue_start_poll[card->qdio.no_in_queues - 1] = NULL; } @@ -4967,10 +4967,9 @@ static int qeth_qdio_establish(struct qeth_card *card) rc = -ENOMEM; goto out_free_qib_param; } - for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; ++i) { - in_sbal_ptrs[i] = (struct qdio_buffer *) - virt_to_phys(card->qdio.in_q->bufs[i].buffer); - } + + for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; i++) + in_sbal_ptrs[i] = card->qdio.in_q->bufs[i].buffer; queue_start_poll = kcalloc(card->qdio.no_in_queues, sizeof(void *), GFP_KERNEL); @@ -4991,11 +4990,11 @@ static int qeth_qdio_establish(struct qeth_card *card) rc = -ENOMEM; goto out_free_queue_start_poll; } + for (i = 0, k = 0; i < card->qdio.no_out_queues; ++i) - for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j, ++k) { - out_sbal_ptrs[k] = (struct qdio_buffer *)virt_to_phys( - card->qdio.out_qs[i]->bufs[j]->buffer); - } + for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; j++, k++) + out_sbal_ptrs[k] = + card->qdio.out_qs[i]->bufs[j]->buffer; memset(&init_data, 0, sizeof(struct qdio_initialize)); init_data.cdev = CARD_DDEV(card); diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 95669d47c389eb67c39db0c450a564ece2bc2d83..eb917e93fa72ff742df95aad911fd8e2e41200de 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -2148,15 +2148,14 @@ int qeth_l2_vnicc_set_state(struct qeth_card *card, u32 vnicc, bool state) QETH_CARD_TEXT(card, 2, "vniccsch"); - /* do not change anything if BridgePort is enabled */ - if (qeth_bridgeport_is_in_use(card)) - return -EBUSY; - /* check if characteristic and enable/disable are supported */ if (!(card->options.vnicc.sup_chars & vnicc) || !(card->options.vnicc.set_char_sup & vnicc)) return -EOPNOTSUPP; + if (qeth_bridgeport_is_in_use(card)) + return -EBUSY; + /* set enable/disable command and store wanted characteristic */ if (state) { cmd = IPA_VNICC_ENABLE; @@ -2202,14 +2201,13 @@ int qeth_l2_vnicc_get_state(struct qeth_card *card, u32 vnicc, bool *state) QETH_CARD_TEXT(card, 2, "vniccgch"); - /* do not get anything if BridgePort is enabled */ - if (qeth_bridgeport_is_in_use(card)) - return -EBUSY; - /* check if characteristic is supported */ if (!(card->options.vnicc.sup_chars & vnicc)) return -EOPNOTSUPP; + if (qeth_bridgeport_is_in_use(card)) + return -EBUSY; + /* if card is ready, query current VNICC state */ if (qeth_card_hw_is_reachable(card)) rc = qeth_l2_vnicc_query_chars(card); @@ -2227,15 +2225,14 @@ int qeth_l2_vnicc_set_timeout(struct qeth_card *card, u32 timeout) QETH_CARD_TEXT(card, 2, "vniccsto"); - /* do not change anything if BridgePort is enabled */ - if (qeth_bridgeport_is_in_use(card)) - return -EBUSY; - /* check if characteristic and set_timeout are supported */ if (!(card->options.vnicc.sup_chars & QETH_VNICC_LEARNING) || !(card->options.vnicc.getset_timeout_sup & QETH_VNICC_LEARNING)) return -EOPNOTSUPP; + if (qeth_bridgeport_is_in_use(card)) + return -EBUSY; + /* do we need to do anything? */ if (card->options.vnicc.learning_timeout == timeout) return rc; @@ -2264,14 +2261,14 @@ int qeth_l2_vnicc_get_timeout(struct qeth_card *card, u32 *timeout) QETH_CARD_TEXT(card, 2, "vniccgto"); - /* do not get anything if BridgePort is enabled */ - if (qeth_bridgeport_is_in_use(card)) - return -EBUSY; - /* check if characteristic and get_timeout are supported */ if (!(card->options.vnicc.sup_chars & QETH_VNICC_LEARNING) || !(card->options.vnicc.getset_timeout_sup & QETH_VNICC_LEARNING)) return -EOPNOTSUPP; + + if (qeth_bridgeport_is_in_use(card)) + return -EBUSY; + /* if card is ready, get timeout. Otherwise, just return stored value */ *timeout = card->options.vnicc.learning_timeout; if (qeth_card_hw_is_reachable(card)) @@ -2285,8 +2282,7 @@ int qeth_l2_vnicc_get_timeout(struct qeth_card *card, u32 *timeout) /* check if VNICC is currently enabled */ bool qeth_l2_vnicc_is_in_use(struct qeth_card *card) { - /* if everything is turned off, VNICC is not active */ - if (!card->options.vnicc.cur_chars) + if (!card->options.vnicc.sup_chars) return false; /* default values are only OK if rx_bcast was not enabled by user * or the card is offline. @@ -2331,10 +2327,10 @@ static bool qeth_l2_vnicc_recover_char(struct qeth_card *card, u32 vnicc, static void qeth_l2_vnicc_init(struct qeth_card *card) { u32 *timeout = &card->options.vnicc.learning_timeout; + bool enable, error = false; unsigned int chars_len, i; unsigned long chars_tmp; u32 sup_cmds, vnicc; - bool enable, error; QETH_CARD_TEXT(card, 2, "vniccini"); /* reset rx_bcast */ @@ -2355,19 +2351,25 @@ static void qeth_l2_vnicc_init(struct qeth_card *card) chars_len = sizeof(card->options.vnicc.sup_chars) * BITS_PER_BYTE; for_each_set_bit(i, &chars_tmp, chars_len) { vnicc = BIT(i); - qeth_l2_vnicc_query_cmds(card, vnicc, &sup_cmds); - if (!(sup_cmds & IPA_VNICC_SET_TIMEOUT) || - !(sup_cmds & IPA_VNICC_GET_TIMEOUT)) + if (qeth_l2_vnicc_query_cmds(card, vnicc, &sup_cmds)) { + sup_cmds = 0; + error = true; + } + if ((sup_cmds & IPA_VNICC_SET_TIMEOUT) && + (sup_cmds & IPA_VNICC_GET_TIMEOUT)) + card->options.vnicc.getset_timeout_sup |= vnicc; + else card->options.vnicc.getset_timeout_sup &= ~vnicc; - if (!(sup_cmds & IPA_VNICC_ENABLE) || - !(sup_cmds & IPA_VNICC_DISABLE)) + if ((sup_cmds & IPA_VNICC_ENABLE) && + (sup_cmds & IPA_VNICC_DISABLE)) + card->options.vnicc.set_char_sup |= vnicc; + else card->options.vnicc.set_char_sup &= ~vnicc; } /* enforce assumed default values and recover settings, if changed */ - error = qeth_l2_vnicc_recover_timeout(card, QETH_VNICC_LEARNING, - timeout); + error |= qeth_l2_vnicc_recover_timeout(card, QETH_VNICC_LEARNING, + timeout); chars_tmp = card->options.vnicc.wanted_chars ^ QETH_VNICC_DEFAULT; - chars_tmp |= QETH_VNICC_BRIDGE_INVISIBLE; chars_len = sizeof(card->options.vnicc.wanted_chars) * BITS_PER_BYTE; for_each_set_bit(i, &chars_tmp, chars_len) { vnicc = BIT(i); diff --git a/drivers/scsi/aic7xxx/aic7xxx_core.c b/drivers/scsi/aic7xxx/aic7xxx_core.c index 915a34f141e4f66503133af8453521de0ffb3783..49e02e87455337a3810b7870cceb11e05f387cee 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_core.c +++ b/drivers/scsi/aic7xxx/aic7xxx_core.c @@ -2321,7 +2321,7 @@ ahc_find_syncrate(struct ahc_softc *ahc, u_int *period, * At some speeds, we only support * ST transfers. */ - if ((syncrate->sxfr_u2 & ST_SXFR) != 0) + if ((syncrate->sxfr_u2 & ST_SXFR) != 0) *ppr_options &= ~MSG_EXT_PPR_DT_REQ; break; } diff --git a/drivers/scsi/bfa/bfad_attr.c b/drivers/scsi/bfa/bfad_attr.c index 26b0fa4e90b58b5340bf18bb20e1a22fd8d5c677..3b84290cf0a7e8f1bd9d0e0c2b3297a4ab682466 100644 --- a/drivers/scsi/bfa/bfad_attr.c +++ b/drivers/scsi/bfa/bfad_attr.c @@ -283,8 +283,10 @@ bfad_im_get_stats(struct Scsi_Host *shost) rc = bfa_port_get_stats(BFA_FCPORT(&bfad->bfa), fcstats, bfad_hcb_comp, &fcomp); spin_unlock_irqrestore(&bfad->bfad_lock, flags); - if (rc != BFA_STATUS_OK) + if (rc != BFA_STATUS_OK) { + kfree(fcstats); return NULL; + } wait_for_completion(&fcomp.comp); diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c index de0a507577ef56bc90330c8c0b532086823f1e85..718a0335cfbb3227d101769034d862e214ce3dff 100644 --- a/drivers/scsi/bnx2i/bnx2i_iscsi.c +++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c @@ -915,12 +915,12 @@ void bnx2i_free_hba(struct bnx2i_hba *hba) INIT_LIST_HEAD(&hba->ep_ofld_list); INIT_LIST_HEAD(&hba->ep_active_list); INIT_LIST_HEAD(&hba->ep_destroy_list); - pci_dev_put(hba->pcidev); if (hba->regview) { pci_iounmap(hba->pcidev, hba->regview); hba->regview = NULL; } + pci_dev_put(hba->pcidev); bnx2i_free_mp_bdt(hba); bnx2i_release_free_cid_que(hba); iscsi_host_free(shost); diff --git a/drivers/scsi/csiostor/csio_scsi.c b/drivers/scsi/csiostor/csio_scsi.c index e09c7f360dbde90d0b8eb54c8509956c8d93012a..0cb585759de6ae3da041da0b9fdb7c729508881a 100644 --- a/drivers/scsi/csiostor/csio_scsi.c +++ b/drivers/scsi/csiostor/csio_scsi.c @@ -1383,7 +1383,7 @@ csio_device_reset(struct device *dev, return -EINVAL; /* Delete NPIV lnodes */ - csio_lnodes_exit(hw, 1); + csio_lnodes_exit(hw, 1); /* Block upper IOs */ csio_lnodes_block_request(hw); diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c index cd2c247d6d0ce1731b053b93a690fa9ca8f02056..559d8bda4cca7efe791983f8e357659aa5c81d22 100644 --- a/drivers/scsi/cxgbi/libcxgbi.c +++ b/drivers/scsi/cxgbi/libcxgbi.c @@ -121,7 +121,8 @@ static inline void cxgbi_device_destroy(struct cxgbi_device *cdev) "cdev 0x%p, p# %u.\n", cdev, cdev->nports); cxgbi_hbas_remove(cdev); cxgbi_device_portmap_cleanup(cdev); - cxgbi_ppm_release(cdev->cdev2ppm(cdev)); + if (cdev->cdev2ppm) + cxgbi_ppm_release(cdev->cdev2ppm(cdev)); if (cdev->pmap.max_connect) cxgbi_free_big_mem(cdev->pmap.port_csk); kfree(cdev); diff --git a/drivers/scsi/esas2r/esas2r_flash.c b/drivers/scsi/esas2r/esas2r_flash.c index 7bd376d95ed5225e0490ce4939775aa609eea74f..b02ac389e6c6050fc92696d480131db15678ebb4 100644 --- a/drivers/scsi/esas2r/esas2r_flash.c +++ b/drivers/scsi/esas2r/esas2r_flash.c @@ -1197,6 +1197,7 @@ bool esas2r_nvram_read_direct(struct esas2r_adapter *a) if (!esas2r_read_flash_block(a, a->nvram, FLS_OFFSET_NVR, sizeof(struct esas2r_sas_nvram))) { esas2r_hdebug("NVRAM read failed, using defaults"); + up(&a->nvram_semaphore); return false; } diff --git a/drivers/scsi/fnic/fnic_isr.c b/drivers/scsi/fnic/fnic_isr.c index 4e3a50202e8c5028cc35943004e16af833d0e7eb..d28088218c3644b7733c0e65176d89bc80593242 100644 --- a/drivers/scsi/fnic/fnic_isr.c +++ b/drivers/scsi/fnic/fnic_isr.c @@ -254,7 +254,7 @@ int fnic_set_intr_mode(struct fnic *fnic) int vecs = n + m + o + 1; if (pci_alloc_irq_vectors(fnic->pdev, vecs, vecs, - PCI_IRQ_MSIX) < 0) { + PCI_IRQ_MSIX) == vecs) { fnic->rq_count = n; fnic->raw_wq_count = m; fnic->wq_copy_count = o; @@ -280,7 +280,7 @@ int fnic_set_intr_mode(struct fnic *fnic) fnic->wq_copy_count >= 1 && fnic->cq_count >= 3 && fnic->intr_count >= 1 && - pci_alloc_irq_vectors(fnic->pdev, 1, 1, PCI_IRQ_MSI) < 0) { + pci_alloc_irq_vectors(fnic->pdev, 1, 1, PCI_IRQ_MSI) == 1) { fnic->rq_count = 1; fnic->raw_wq_count = 1; fnic->wq_copy_count = 1; diff --git a/drivers/scsi/fnic/fnic_scsi.c b/drivers/scsi/fnic/fnic_scsi.c index 8cbd3c9f0b4cb9ca199736d838a435fc8490b1cf..73ffc16ec0225385f6bc6105e50594b4b1b18efe 100644 --- a/drivers/scsi/fnic/fnic_scsi.c +++ b/drivers/scsi/fnic/fnic_scsi.c @@ -446,6 +446,9 @@ static int fnic_queuecommand_lck(struct scsi_cmnd *sc, void (*done)(struct scsi_ if (unlikely(fnic_chk_state_flags_locked(fnic, FNIC_FLAGS_IO_BLOCKED))) return SCSI_MLQUEUE_HOST_BUSY; + if (unlikely(fnic_chk_state_flags_locked(fnic, FNIC_FLAGS_FWRESET))) + return SCSI_MLQUEUE_HOST_BUSY; + rport = starget_to_rport(scsi_target(sc->device)); if (!rport) { FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host, diff --git a/drivers/scsi/fnic/vnic_dev.c b/drivers/scsi/fnic/vnic_dev.c index ba69d6112fa13d34bc6acda465521797a04ee94c..c5b89a003d2ab0c80082b55d9665a5647d662eff 100644 --- a/drivers/scsi/fnic/vnic_dev.c +++ b/drivers/scsi/fnic/vnic_dev.c @@ -445,26 +445,26 @@ int vnic_dev_soft_reset_done(struct vnic_dev *vdev, int *done) int vnic_dev_hang_notify(struct vnic_dev *vdev) { - u64 a0, a1; + u64 a0 = 0, a1 = 0; int wait = 1000; return vnic_dev_cmd(vdev, CMD_HANG_NOTIFY, &a0, &a1, wait); } int vnic_dev_mac_addr(struct vnic_dev *vdev, u8 *mac_addr) { - u64 a0, a1; + u64 a[2] = {}; int wait = 1000; int err, i; for (i = 0; i < ETH_ALEN; i++) mac_addr[i] = 0; - err = vnic_dev_cmd(vdev, CMD_MAC_ADDR, &a0, &a1, wait); + err = vnic_dev_cmd(vdev, CMD_MAC_ADDR, &a[0], &a[1], wait); if (err) return err; for (i = 0; i < ETH_ALEN; i++) - mac_addr[i] = ((u8 *)&a0)[i]; + mac_addr[i] = ((u8 *)&a)[i]; return 0; } @@ -489,30 +489,30 @@ void vnic_dev_packet_filter(struct vnic_dev *vdev, int directed, int multicast, void vnic_dev_add_addr(struct vnic_dev *vdev, u8 *addr) { - u64 a0 = 0, a1 = 0; + u64 a[2] = {}; int wait = 1000; int err; int i; for (i = 0; i < ETH_ALEN; i++) - ((u8 *)&a0)[i] = addr[i]; + ((u8 *)&a)[i] = addr[i]; - err = vnic_dev_cmd(vdev, CMD_ADDR_ADD, &a0, &a1, wait); + err = vnic_dev_cmd(vdev, CMD_ADDR_ADD, &a[0], &a[1], wait); if (err) pr_err("Can't add addr [%pM], %d\n", addr, err); } void vnic_dev_del_addr(struct vnic_dev *vdev, u8 *addr) { - u64 a0 = 0, a1 = 0; + u64 a[2] = {}; int wait = 1000; int err; int i; for (i = 0; i < ETH_ALEN; i++) - ((u8 *)&a0)[i] = addr[i]; + ((u8 *)&a)[i] = addr[i]; - err = vnic_dev_cmd(vdev, CMD_ADDR_DEL, &a0, &a1, wait); + err = vnic_dev_cmd(vdev, CMD_ADDR_DEL, &a[0], &a[1], wait); if (err) pr_err("Can't del addr [%pM], %d\n", addr, err); } diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index 55181d28291e7daab0e642a12a88d7f5cac01e40..7212e3a13fe6bfc307d2e2abb753b370ae6e37f2 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -892,6 +892,10 @@ iscsi_sw_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max, static void iscsi_sw_tcp_session_destroy(struct iscsi_cls_session *cls_session) { struct Scsi_Host *shost = iscsi_session_to_shost(cls_session); + struct iscsi_session *session = cls_session->dd_data; + + if (WARN_ON_ONCE(session->leadconn)) + return; iscsi_tcp_r2tpool_free(cls_session->dd_data); iscsi_session_teardown(cls_session); diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c index 42bcf7f3a0f90bf5a8778e21cfe0b5c20f6e6a87..6ba257cbc6d946c677f984d08bf13312dd71543b 100644 --- a/drivers/scsi/libfc/fc_exch.c +++ b/drivers/scsi/libfc/fc_exch.c @@ -2603,7 +2603,7 @@ void fc_exch_recv(struct fc_lport *lport, struct fc_frame *fp) /* lport lock ? */ if (!lport || lport->state == LPORT_ST_DISABLED) { - FC_LPORT_DBG(lport, "Receiving frames for an lport that " + FC_LIBFC_DBG("Receiving frames for an lport that " "has not been initialized correctly\n"); fc_frame_free(fp); return; diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index 2f31d266339f85317ad8426f9686cbff1c3eba98..21f971447dd8c0abe3b2969e49df4d31c6f7ff40 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -3894,12 +3894,12 @@ megasas_transition_to_ready(struct megasas_instance *instance, int ocr) /* * The cur_state should not last for more than max_wait secs */ - for (i = 0; i < max_wait; i++) { + for (i = 0; i < max_wait * 50; i++) { curr_abs_state = instance->instancet-> read_fw_status_reg(instance->reg_set); if (abs_state == curr_abs_state) { - msleep(1000); + msleep(20); } else break; } @@ -4177,7 +4177,8 @@ dcmd_timeout_ocr_possible(struct megasas_instance *instance) { if (instance->adapter_type == MFI_SERIES) return KILL_ADAPTER; else if (instance->unload || - test_bit(MEGASAS_FUSION_IN_RESET, &instance->reset_flags)) + test_bit(MEGASAS_FUSION_OCR_NOT_POSSIBLE, + &instance->reset_flags)) return IGNORE_TIMEOUT; else return INITIATE_OCR; diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c index f45c54f02bfa5c6fef7a29aeeb45c4913f2d8d98..81bd824bb9d99861a38cf38841d7dce5c8190501 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -530,7 +530,8 @@ megasas_alloc_request_fusion(struct megasas_instance *instance) fusion->io_request_frames = dma_pool_alloc(fusion->io_request_frames_pool, - GFP_KERNEL, &fusion->io_request_frames_phys); + GFP_KERNEL | __GFP_NOWARN, + &fusion->io_request_frames_phys); if (!fusion->io_request_frames) { if (instance->max_fw_cmds >= (MEGASAS_REDUCE_QD_COUNT * 2)) { instance->max_fw_cmds -= MEGASAS_REDUCE_QD_COUNT; @@ -568,7 +569,7 @@ megasas_alloc_request_fusion(struct megasas_instance *instance) fusion->io_request_frames = dma_pool_alloc(fusion->io_request_frames_pool, - GFP_KERNEL, + GFP_KERNEL | __GFP_NOWARN, &fusion->io_request_frames_phys); if (!fusion->io_request_frames) { @@ -4558,6 +4559,7 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int reason) if (instance->requestorId && !instance->skip_heartbeat_timer_del) del_timer_sync(&instance->sriov_heartbeat_timer); set_bit(MEGASAS_FUSION_IN_RESET, &instance->reset_flags); + set_bit(MEGASAS_FUSION_OCR_NOT_POSSIBLE, &instance->reset_flags); atomic_set(&instance->adprecovery, MEGASAS_ADPRESET_SM_POLLING); instance->instancet->disable_intr(instance); megasas_sync_irqs((unsigned long)instance); @@ -4747,7 +4749,7 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int reason) atomic_set(&instance->adprecovery, MEGASAS_HBA_OPERATIONAL); } out: - clear_bit(MEGASAS_FUSION_IN_RESET, &instance->reset_flags); + clear_bit(MEGASAS_FUSION_OCR_NOT_POSSIBLE, &instance->reset_flags); mutex_unlock(&instance->reset_mutex); return retval; } diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.h b/drivers/scsi/megaraid/megaraid_sas_fusion.h index 8e5ebee6517f0994faf7a7a7cfb55e1a98e00307..df7bbd0354e9a026d384ccd40c3fcd91c56404c2 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.h +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.h @@ -102,6 +102,7 @@ enum MR_RAID_FLAGS_IO_SUB_TYPE { #define MEGASAS_FP_CMD_LEN 16 #define MEGASAS_FUSION_IN_RESET 0 +#define MEGASAS_FUSION_OCR_NOT_POSSIBLE 1 #define THRESHOLD_REPLY_COUNT 50 #define RAID_1_PEER_CMDS 2 #define JBOD_MAPS_COUNT 2 diff --git a/drivers/scsi/pm8001/pm8001_sas.c b/drivers/scsi/pm8001/pm8001_sas.c index 59feda261e08888c33a4893b944cfdaf52a3e23f..5be4212312cb02401961e964c4b0e95634320c3b 100644 --- a/drivers/scsi/pm8001/pm8001_sas.c +++ b/drivers/scsi/pm8001/pm8001_sas.c @@ -866,6 +866,8 @@ static void pm8001_dev_gone_notify(struct domain_device *dev) spin_unlock_irqrestore(&pm8001_ha->lock, flags); pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev , dev, 1, 0); + while (pm8001_dev->running_req) + msleep(20); spin_lock_irqsave(&pm8001_ha->lock, flags); } PM8001_CHIP_DISP->dereg_dev_req(pm8001_ha, device_id); @@ -1238,8 +1240,10 @@ int pm8001_abort_task(struct sas_task *task) PM8001_MSG_DBG(pm8001_ha, pm8001_printk("Waiting for Port reset\n")); wait_for_completion(&completion_reset); - if (phy->port_reset_status) + if (phy->port_reset_status) { + pm8001_dev_gone_notify(dev); goto out; + } /* * 4. SATA Abort ALL diff --git a/drivers/scsi/pm8001/pm80xx_hwi.c b/drivers/scsi/pm8001/pm80xx_hwi.c index 8627feb80261a01be749fba1a5e51886c5f13236..c63b5db435c59339a05eca56dd54fe443c0a7021 100644 --- a/drivers/scsi/pm8001/pm80xx_hwi.c +++ b/drivers/scsi/pm8001/pm80xx_hwi.c @@ -604,7 +604,7 @@ static void update_main_config_table(struct pm8001_hba_info *pm8001_ha) pm8001_ha->main_cfg_tbl.pm80xx_tbl.port_recovery_timer &= 0x0000ffff; pm8001_ha->main_cfg_tbl.pm80xx_tbl.port_recovery_timer |= - 0x140000; + CHIP_8006_PORT_RECOVERY_TIMEOUT; } pm8001_mw32(address, MAIN_PORT_RECOVERY_TIMER, pm8001_ha->main_cfg_tbl.pm80xx_tbl.port_recovery_timer); diff --git a/drivers/scsi/pm8001/pm80xx_hwi.h b/drivers/scsi/pm8001/pm80xx_hwi.h index 7dd2699d0efb5da1b12483ec7af41788dd4ba78f..bbe1747234ff0a85bcd6d85b4d3efbf99afa2ce8 100644 --- a/drivers/scsi/pm8001/pm80xx_hwi.h +++ b/drivers/scsi/pm8001/pm80xx_hwi.h @@ -228,6 +228,8 @@ #define SAS_MAX_AIP 0x200000 #define IT_NEXUS_TIMEOUT 0x7D0 #define PORT_RECOVERY_TIMEOUT ((IT_NEXUS_TIMEOUT/100) + 30) +/* Port recovery timeout, 10000 ms for PM8006 controller */ +#define CHIP_8006_PORT_RECOVERY_TIMEOUT 0x640000 #ifdef __LITTLE_ENDIAN_BITFIELD struct sas_identify_frame_local { diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c index c7533fa7f46ebb93a8795238a6790b961a48113d..36871760a5d372da9b63109a2be5fa8d4edb22ec 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.c +++ b/drivers/scsi/qla2xxx/qla_dbg.c @@ -2520,12 +2520,6 @@ qla83xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) /* Driver Debug Functions. */ /****************************************************************************/ -static inline int -ql_mask_match(uint32_t level) -{ - return (level & ql2xextended_error_logging) == level; -} - /* * This function is for formatting and logging debug information. * It is to be used when vha is available. It formats the message diff --git a/drivers/scsi/qla2xxx/qla_dbg.h b/drivers/scsi/qla2xxx/qla_dbg.h index 8877aa97d829f00ba72a9ae29e27c98ac6b7afdd..ceca6dd34db12f8e5ae1c4ae32258b706afdcdbb 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.h +++ b/drivers/scsi/qla2xxx/qla_dbg.h @@ -374,3 +374,9 @@ extern int qla24xx_dump_ram(struct qla_hw_data *, uint32_t, uint32_t *, extern void qla24xx_pause_risc(struct device_reg_24xx __iomem *, struct qla_hw_data *); extern int qla24xx_soft_reset(struct qla_hw_data *); + +static inline int +ql_mask_match(uint level) +{ + return (level & ql2xextended_error_logging) == level; +} diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 851f75b12216cf906ef9c240628bdf7739175237..f45759b353bea7b11e324b47ca88df900831e7c8 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -5710,8 +5710,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha) if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) break; - if ((fcport->flags & FCF_FABRIC_DEVICE) == 0 || - (fcport->flags & FCF_LOGIN_NEEDED) == 0) + if ((fcport->flags & FCF_FABRIC_DEVICE) == 0) continue; if (fcport->scan_state == QLA_FCPORT_SCAN) { @@ -5734,7 +5733,8 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha) } } - if (fcport->scan_state == QLA_FCPORT_FOUND) + if (fcport->scan_state == QLA_FCPORT_FOUND && + (fcport->flags & FCF_LOGIN_NEEDED) != 0) qla24xx_fcport_handle_login(vha, fcport); } return (rval); diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index e6d162945f5db4785178230863e8d6f3822a9509..f9b3151f4b10dfb1811a621109d6033e63db377f 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -1876,6 +1876,18 @@ static void qla24xx_nvme_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, inbuf = (uint32_t *)&sts->nvme_ersp_data; outbuf = (uint32_t *)fd->rspaddr; iocb->u.nvme.rsp_pyld_len = le16_to_cpu(sts->nvme_rsp_pyld_len); + if (unlikely(iocb->u.nvme.rsp_pyld_len > + sizeof(struct nvme_fc_ersp_iu))) { + if (ql_mask_match(ql_dbg_io)) { + WARN_ONCE(1, "Unexpected response payload length %u.\n", + iocb->u.nvme.rsp_pyld_len); + ql_log(ql_log_warn, fcport->vha, 0x5100, + "Unexpected response payload length %u.\n", + iocb->u.nvme.rsp_pyld_len); + } + iocb->u.nvme.rsp_pyld_len = + sizeof(struct nvme_fc_ersp_iu); + } iter = iocb->u.nvme.rsp_pyld_len >> 2; for (; iter; iter--) *outbuf++ = swab32(*inbuf++); @@ -3573,7 +3585,7 @@ qla2x00_request_irqs(struct qla_hw_data *ha, struct rsp_que *rsp) skip_msix: ql_log(ql_log_info, vha, 0x0037, - "Falling back-to MSI mode -%d.\n", ret); + "Falling back-to MSI mode -- ret=%d.\n", ret); if (!IS_QLA24XX(ha) && !IS_QLA2532(ha) && !IS_QLA8432(ha) && !IS_QLA8001(ha) && !IS_P3P_TYPE(ha) && !IS_QLAFX00(ha) && @@ -3581,13 +3593,13 @@ qla2x00_request_irqs(struct qla_hw_data *ha, struct rsp_que *rsp) goto skip_msi; ret = pci_alloc_irq_vectors(ha->pdev, 1, 1, PCI_IRQ_MSI); - if (!ret) { + if (ret > 0) { ql_dbg(ql_dbg_init, vha, 0x0038, "MSI: Enabled.\n"); ha->flags.msi_enabled = 1; } else ql_log(ql_log_warn, vha, 0x0039, - "Falling back-to INTa mode -- %d.\n", ret); + "Falling back-to INTa mode -- ret=%d.\n", ret); skip_msi: /* Skip INTx on ISP82xx. */ diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index abef3b29fa101c407629f99039954846c1dc55df..bef9faea5eeeb579194dd67678cdcd4af9b8eee5 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -5994,9 +5994,8 @@ qla2x00_dump_mctp_data(scsi_qla_host_t *vha, dma_addr_t req_dma, uint32_t addr, mcp->mb[7] = LSW(MSD(req_dma)); mcp->mb[8] = MSW(addr); /* Setting RAM ID to valid */ - mcp->mb[10] |= BIT_7; /* For MCTP RAM ID is 0x40 */ - mcp->mb[10] |= 0x40; + mcp->mb[10] = BIT_7 | 0x40; mcp->out_mb |= MBX_10|MBX_8|MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1| MBX_0; diff --git a/drivers/scsi/qla2xxx/qla_nx.c b/drivers/scsi/qla2xxx/qla_nx.c index de2bc78449e786500b0680411d6245895ac43790..3007eecfa509b0380c0025cc603be6ce037826c2 100644 --- a/drivers/scsi/qla2xxx/qla_nx.c +++ b/drivers/scsi/qla2xxx/qla_nx.c @@ -1605,8 +1605,7 @@ qla82xx_get_bootld_offset(struct qla_hw_data *ha) return (u8 *)&ha->hablob->fw->data[offset]; } -static __le32 -qla82xx_get_fw_size(struct qla_hw_data *ha) +static u32 qla82xx_get_fw_size(struct qla_hw_data *ha) { struct qla82xx_uri_data_desc *uri_desc = NULL; @@ -1617,7 +1616,7 @@ qla82xx_get_fw_size(struct qla_hw_data *ha) return cpu_to_le32(uri_desc->size); } - return cpu_to_le32(*(u32 *)&ha->hablob->fw->data[FW_SIZE_OFFSET]); + return get_unaligned_le32(&ha->hablob->fw->data[FW_SIZE_OFFSET]); } static u8 * @@ -1808,7 +1807,7 @@ qla82xx_fw_load_from_blob(struct qla_hw_data *ha) } flashaddr = FLASH_ADDR_START; - size = (__force u32)qla82xx_get_fw_size(ha) / 8; + size = qla82xx_get_fw_size(ha) / 8; ptr64 = (u64 *)qla82xx_get_fw_offs(ha); for (i = 0; i < size; i++) { diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index bb20a4a228cfebef595509be1140fcd6105a67a6..fff20a3707677b1c1cd2d3deefaddec971dad0c1 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -6967,8 +6967,7 @@ qla2x00_module_init(void) /* Initialize target kmem_cache and mem_pools */ ret = qlt_init(); if (ret < 0) { - kmem_cache_destroy(srb_cachep); - return ret; + goto destroy_cache; } else if (ret > 0) { /* * If initiator mode is explictly disabled by qlt_init(), @@ -6989,11 +6988,10 @@ qla2x00_module_init(void) qla2xxx_transport_template = fc_attach_transport(&qla2xxx_transport_functions); if (!qla2xxx_transport_template) { - kmem_cache_destroy(srb_cachep); ql_log(ql_log_fatal, NULL, 0x0002, "fc_attach_transport failed...Failing load!.\n"); - qlt_exit(); - return -ENODEV; + ret = -ENODEV; + goto qlt_exit; } apidev_major = register_chrdev(0, QLA2XXX_APIDEV, &apidev_fops); @@ -7005,27 +7003,37 @@ qla2x00_module_init(void) qla2xxx_transport_vport_template = fc_attach_transport(&qla2xxx_transport_vport_functions); if (!qla2xxx_transport_vport_template) { - kmem_cache_destroy(srb_cachep); - qlt_exit(); - fc_release_transport(qla2xxx_transport_template); ql_log(ql_log_fatal, NULL, 0x0004, "fc_attach_transport vport failed...Failing load!.\n"); - return -ENODEV; + ret = -ENODEV; + goto unreg_chrdev; } ql_log(ql_log_info, NULL, 0x0005, "QLogic Fibre Channel HBA Driver: %s.\n", qla2x00_version_str); ret = pci_register_driver(&qla2xxx_pci_driver); if (ret) { - kmem_cache_destroy(srb_cachep); - qlt_exit(); - fc_release_transport(qla2xxx_transport_template); - fc_release_transport(qla2xxx_transport_vport_template); ql_log(ql_log_fatal, NULL, 0x0006, "pci_register_driver failed...ret=%d Failing load!.\n", ret); + goto release_vport_transport; } return ret; + +release_vport_transport: + fc_release_transport(qla2xxx_transport_vport_template); + +unreg_chrdev: + if (apidev_major >= 0) + unregister_chrdev(apidev_major, QLA2XXX_APIDEV); + fc_release_transport(qla2xxx_transport_template); + +qlt_exit: + qlt_exit(); + +destroy_cache: + kmem_cache_destroy(srb_cachep); + return ret; } /** diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index e9545411ec5a91aa4b81ef958e3c4465d6e20594..95206e227730c1d36d433382f56cded6e920dc00 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -2233,14 +2233,14 @@ void qlt_send_resp_ctio(struct qla_qpair *qpair, struct qla_tgt_cmd *cmd, ctio->u.status1.scsi_status |= cpu_to_le16(SS_RESIDUAL_UNDER); - /* Response code and sense key */ - put_unaligned_le32(((0x70 << 24) | (sense_key << 8)), - (&ctio->u.status1.sense_data)[0]); + /* Fixed format sense data. */ + ctio->u.status1.sense_data[0] = 0x70; + ctio->u.status1.sense_data[2] = sense_key; /* Additional sense length */ - put_unaligned_le32(0x0a, (&ctio->u.status1.sense_data)[1]); + ctio->u.status1.sense_data[7] = 0xa; /* ASC and ASCQ */ - put_unaligned_le32(((asc << 24) | (ascq << 16)), - (&ctio->u.status1.sense_data)[3]); + ctio->u.status1.sense_data[12] = asc; + ctio->u.status1.sense_data[13] = ascq; /* Memory Barrier */ wmb(); @@ -2290,7 +2290,7 @@ void qlt_xmit_tm_rsp(struct qla_tgt_mgmt_cmd *mcmd) mcmd->orig_iocb.imm_ntfy.u.isp24.status_subcode == ELS_TPRLO) { ql_dbg(ql_dbg_disc, vha, 0x2106, - "TM response logo %phC status %#x state %#x", + "TM response logo %8phC status %#x state %#x", mcmd->sess->port_name, mcmd->fc_tm_rsp, mcmd->flags); qlt_schedule_sess_for_deletion(mcmd->sess); @@ -5334,11 +5334,7 @@ qlt_alloc_qfull_cmd(struct scsi_qla_host *vha, se_sess = sess->se_sess; tag = sbitmap_queue_get(&se_sess->sess_tag_pool, &cpu); - if (tag < 0) - return; - - cmd = &((struct qla_tgt_cmd *)se_sess->sess_cmd_map)[tag]; - if (!cmd) { + if (tag < 0) { ql_dbg(ql_dbg_io, vha, 0x3009, "qla_target(%d): %s: Allocation of cmd failed\n", vha->vp_idx, __func__); @@ -5353,6 +5349,7 @@ qlt_alloc_qfull_cmd(struct scsi_qla_host *vha, return; } + cmd = &((struct qla_tgt_cmd *)se_sess->sess_cmd_map)[tag]; memset(cmd, 0, sizeof(struct qla_tgt_cmd)); qlt_incr_num_pend_cmds(vha); diff --git a/drivers/scsi/qla4xxx/ql4_mbx.c b/drivers/scsi/qla4xxx/ql4_mbx.c index 5d56904687b95b4ae52f916d9277ee224798128c..c81de77323597ffc276d0ab542afbe755702feaa 100644 --- a/drivers/scsi/qla4xxx/ql4_mbx.c +++ b/drivers/scsi/qla4xxx/ql4_mbx.c @@ -640,9 +640,6 @@ int qla4xxx_initialize_fw_cb(struct scsi_qla_host * ha) if (qla4xxx_get_ifcb(ha, &mbox_cmd[0], &mbox_sts[0], init_fw_cb_dma) != QLA_SUCCESS) { - dma_free_coherent(&ha->pdev->dev, - sizeof(struct addr_ctrl_blk), - init_fw_cb, init_fw_cb_dma); goto exit_init_fw_cb; } diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index f8acf101af3d9a89493b33ac05316f1bc2838d98..f59b8982b28834ca0da4f471cff55e270d7327aa 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -4146,7 +4146,7 @@ static void qla4xxx_mem_free(struct scsi_qla_host *ha) dma_free_coherent(&ha->pdev->dev, ha->queues_len, ha->queues, ha->queues_dma); - if (ha->fw_dump) + if (ha->fw_dump) vfree(ha->fw_dump); ha->queues_len = 0; diff --git a/drivers/scsi/scsi_trace.c b/drivers/scsi/scsi_trace.c index 617a607375908dfd031a0e30e1d126f5d135521c..22472d140ef7c6bce8955edbc37d1c9270eee10a 100644 --- a/drivers/scsi/scsi_trace.c +++ b/drivers/scsi/scsi_trace.c @@ -21,7 +21,7 @@ #include #define SERVICE_ACTION16(cdb) (cdb[1] & 0x1f) -#define SERVICE_ACTION32(cdb) ((cdb[8] << 8) | cdb[9]) +#define SERVICE_ACTION32(cdb) (get_unaligned_be16(&cdb[8])) static const char * scsi_trace_misc(struct trace_seq *, unsigned char *, int); @@ -51,17 +51,12 @@ static const char * scsi_trace_rw10(struct trace_seq *p, unsigned char *cdb, int len) { const char *ret = trace_seq_buffer_ptr(p); - sector_t lba = 0, txlen = 0; + u32 lba, txlen; - lba |= (cdb[2] << 24); - lba |= (cdb[3] << 16); - lba |= (cdb[4] << 8); - lba |= cdb[5]; - txlen |= (cdb[7] << 8); - txlen |= cdb[8]; + lba = get_unaligned_be32(&cdb[2]); + txlen = get_unaligned_be16(&cdb[7]); - trace_seq_printf(p, "lba=%llu txlen=%llu protect=%u", - (unsigned long long)lba, (unsigned long long)txlen, + trace_seq_printf(p, "lba=%u txlen=%u protect=%u", lba, txlen, cdb[1] >> 5); if (cdb[0] == WRITE_SAME) @@ -76,19 +71,12 @@ static const char * scsi_trace_rw12(struct trace_seq *p, unsigned char *cdb, int len) { const char *ret = trace_seq_buffer_ptr(p); - sector_t lba = 0, txlen = 0; - - lba |= (cdb[2] << 24); - lba |= (cdb[3] << 16); - lba |= (cdb[4] << 8); - lba |= cdb[5]; - txlen |= (cdb[6] << 24); - txlen |= (cdb[7] << 16); - txlen |= (cdb[8] << 8); - txlen |= cdb[9]; - - trace_seq_printf(p, "lba=%llu txlen=%llu protect=%u", - (unsigned long long)lba, (unsigned long long)txlen, + u32 lba, txlen; + + lba = get_unaligned_be32(&cdb[2]); + txlen = get_unaligned_be32(&cdb[6]); + + trace_seq_printf(p, "lba=%u txlen=%u protect=%u", lba, txlen, cdb[1] >> 5); trace_seq_putc(p, 0); @@ -99,23 +87,13 @@ static const char * scsi_trace_rw16(struct trace_seq *p, unsigned char *cdb, int len) { const char *ret = trace_seq_buffer_ptr(p); - sector_t lba = 0, txlen = 0; - - lba |= ((u64)cdb[2] << 56); - lba |= ((u64)cdb[3] << 48); - lba |= ((u64)cdb[4] << 40); - lba |= ((u64)cdb[5] << 32); - lba |= (cdb[6] << 24); - lba |= (cdb[7] << 16); - lba |= (cdb[8] << 8); - lba |= cdb[9]; - txlen |= (cdb[10] << 24); - txlen |= (cdb[11] << 16); - txlen |= (cdb[12] << 8); - txlen |= cdb[13]; - - trace_seq_printf(p, "lba=%llu txlen=%llu protect=%u", - (unsigned long long)lba, (unsigned long long)txlen, + u64 lba; + u32 txlen; + + lba = get_unaligned_be64(&cdb[2]); + txlen = get_unaligned_be32(&cdb[10]); + + trace_seq_printf(p, "lba=%llu txlen=%u protect=%u", lba, txlen, cdb[1] >> 5); if (cdb[0] == WRITE_SAME_16) @@ -130,8 +108,8 @@ static const char * scsi_trace_rw32(struct trace_seq *p, unsigned char *cdb, int len) { const char *ret = trace_seq_buffer_ptr(p), *cmd; - sector_t lba = 0, txlen = 0; - u32 ei_lbrt = 0; + u64 lba; + u32 ei_lbrt, txlen; switch (SERVICE_ACTION32(cdb)) { case READ_32: @@ -151,26 +129,12 @@ scsi_trace_rw32(struct trace_seq *p, unsigned char *cdb, int len) goto out; } - lba |= ((u64)cdb[12] << 56); - lba |= ((u64)cdb[13] << 48); - lba |= ((u64)cdb[14] << 40); - lba |= ((u64)cdb[15] << 32); - lba |= (cdb[16] << 24); - lba |= (cdb[17] << 16); - lba |= (cdb[18] << 8); - lba |= cdb[19]; - ei_lbrt |= (cdb[20] << 24); - ei_lbrt |= (cdb[21] << 16); - ei_lbrt |= (cdb[22] << 8); - ei_lbrt |= cdb[23]; - txlen |= (cdb[28] << 24); - txlen |= (cdb[29] << 16); - txlen |= (cdb[30] << 8); - txlen |= cdb[31]; - - trace_seq_printf(p, "%s_32 lba=%llu txlen=%llu protect=%u ei_lbrt=%u", - cmd, (unsigned long long)lba, - (unsigned long long)txlen, cdb[10] >> 5, ei_lbrt); + lba = get_unaligned_be64(&cdb[12]); + ei_lbrt = get_unaligned_be32(&cdb[20]); + txlen = get_unaligned_be32(&cdb[28]); + + trace_seq_printf(p, "%s_32 lba=%llu txlen=%u protect=%u ei_lbrt=%u", + cmd, lba, txlen, cdb[10] >> 5, ei_lbrt); if (SERVICE_ACTION32(cdb) == WRITE_SAME_32) trace_seq_printf(p, " unmap=%u", cdb[10] >> 3 & 1); @@ -185,7 +149,7 @@ static const char * scsi_trace_unmap(struct trace_seq *p, unsigned char *cdb, int len) { const char *ret = trace_seq_buffer_ptr(p); - unsigned int regions = cdb[7] << 8 | cdb[8]; + unsigned int regions = get_unaligned_be16(&cdb[7]); trace_seq_printf(p, "regions=%u", (regions - 8) / 16); trace_seq_putc(p, 0); @@ -197,8 +161,8 @@ static const char * scsi_trace_service_action_in(struct trace_seq *p, unsigned char *cdb, int len) { const char *ret = trace_seq_buffer_ptr(p), *cmd; - sector_t lba = 0; - u32 alloc_len = 0; + u64 lba; + u32 alloc_len; switch (SERVICE_ACTION16(cdb)) { case SAI_READ_CAPACITY_16: @@ -212,21 +176,10 @@ scsi_trace_service_action_in(struct trace_seq *p, unsigned char *cdb, int len) goto out; } - lba |= ((u64)cdb[2] << 56); - lba |= ((u64)cdb[3] << 48); - lba |= ((u64)cdb[4] << 40); - lba |= ((u64)cdb[5] << 32); - lba |= (cdb[6] << 24); - lba |= (cdb[7] << 16); - lba |= (cdb[8] << 8); - lba |= cdb[9]; - alloc_len |= (cdb[10] << 24); - alloc_len |= (cdb[11] << 16); - alloc_len |= (cdb[12] << 8); - alloc_len |= cdb[13]; - - trace_seq_printf(p, "%s lba=%llu alloc_len=%u", cmd, - (unsigned long long)lba, alloc_len); + lba = get_unaligned_be64(&cdb[2]); + alloc_len = get_unaligned_be32(&cdb[10]); + + trace_seq_printf(p, "%s lba=%llu alloc_len=%u", cmd, lba, alloc_len); out: trace_seq_putc(p, 0); diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index 4d0fc6b01fa0dc9066272dc4d084dc35906a7fba..c0fb9e7890807f34a958308bff2d7ef2a37151bf 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -37,6 +37,8 @@ #define ISCSI_TRANSPORT_VERSION "2.0-870" +#define ISCSI_SEND_MAX_ALLOWED 10 + static int dbg_session; module_param_named(debug_session, dbg_session, int, S_IRUGO | S_IWUSR); @@ -2943,6 +2945,24 @@ iscsi_set_path(struct iscsi_transport *transport, struct iscsi_uevent *ev) return err; } +static int iscsi_session_has_conns(int sid) +{ + struct iscsi_cls_conn *conn; + unsigned long flags; + int found = 0; + + spin_lock_irqsave(&connlock, flags); + list_for_each_entry(conn, &connlist, conn_list) { + if (iscsi_conn_get_sid(conn) == sid) { + found = 1; + break; + } + } + spin_unlock_irqrestore(&connlock, flags); + + return found; +} + static int iscsi_set_iface_params(struct iscsi_transport *transport, struct iscsi_uevent *ev, uint32_t len) @@ -3520,10 +3540,12 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group) break; case ISCSI_UEVENT_DESTROY_SESSION: session = iscsi_session_lookup(ev->u.d_session.sid); - if (session) - transport->destroy_session(session); - else + if (!session) err = -EINVAL; + else if (iscsi_session_has_conns(ev->u.d_session.sid)) + err = -EBUSY; + else + transport->destroy_session(session); break; case ISCSI_UEVENT_UNBIND_SESSION: session = iscsi_session_lookup(ev->u.d_session.sid); @@ -3680,6 +3702,7 @@ iscsi_if_rx(struct sk_buff *skb) struct nlmsghdr *nlh; struct iscsi_uevent *ev; uint32_t group; + int retries = ISCSI_SEND_MAX_ALLOWED; nlh = nlmsg_hdr(skb); if (nlh->nlmsg_len < sizeof(*nlh) + sizeof(*ev) || @@ -3710,6 +3733,10 @@ iscsi_if_rx(struct sk_buff *skb) break; err = iscsi_if_send_reply(portid, nlh->nlmsg_type, ev, sizeof(*ev)); + if (err == -EAGAIN && --retries < 0) { + printk(KERN_WARNING "Send reply failed, error %d\n", err); + break; + } } while (err < 0 && err != -ECONNREFUSED && err != -ESRCH); skb_pull(skb, rlen); } diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 745ebb08af09945a5b786e806c73580829f24be7..5e1d13c573539dd5ee5aa51525ebc4cb3d3de38b 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1611,20 +1611,30 @@ static void sd_rescan(struct device *dev) static int sd_compat_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) { - struct scsi_device *sdev = scsi_disk(bdev->bd_disk)->device; + struct gendisk *disk = bdev->bd_disk; + struct scsi_disk *sdkp = scsi_disk(disk); + struct scsi_device *sdev = sdkp->device; + void __user *p = compat_ptr(arg); int error; + error = scsi_verify_blk_ioctl(bdev, cmd); + if (error < 0) + return error; + error = scsi_ioctl_block_when_processing_errors(sdev, cmd, (mode & FMODE_NDELAY) != 0); if (error) return error; + + if (is_sed_ioctl(cmd)) + return sed_ioctl(sdkp->opal_dev, cmd, p); /* * Let the static ioctl translation table take care of it. */ if (!sdev->host->hostt->compat_ioctl) return -ENOIOCTLCMD; - return sdev->host->hostt->compat_ioctl(sdev, cmd, (void __user *)arg); + return sdev->host->hostt->compat_ioctl(sdev, cmd, p); } #endif @@ -1884,9 +1894,13 @@ static int sd_done(struct scsi_cmnd *SCpnt) } break; case REQ_OP_ZONE_REPORT: + /* To avoid that the block layer performs an incorrect + * bio_advance() call and restart of the remainder of + * incomplete report zone BIOs, always indicate a full + * completion of REQ_OP_ZONE_REPORT. + */ if (!result) { - good_bytes = scsi_bufflen(SCpnt) - - scsi_get_resid(SCpnt); + good_bytes = scsi_bufflen(SCpnt); scsi_set_resid(SCpnt, 0); } else { good_bytes = 0; @@ -2120,8 +2134,10 @@ static int sd_read_protection_type(struct scsi_disk *sdkp, unsigned char *buffer u8 type; int ret = 0; - if (scsi_device_protection(sdp) == 0 || (buffer[12] & 1) == 0) + if (scsi_device_protection(sdp) == 0 || (buffer[12] & 1) == 0) { + sdkp->protection_type = 0; return ret; + } type = ((buffer[12] >> 1) & 7) + 1; /* P_TYPE 0 = Type 1 */ diff --git a/drivers/scsi/ufs/ufs_quirks.h b/drivers/scsi/ufs/ufs_quirks.h index 60169a35caf10438cdf9f2fb9695f8eb2c90fc42..162a77a356d2da48aeef1e6130e51f09979f8abd 100644 --- a/drivers/scsi/ufs/ufs_quirks.h +++ b/drivers/scsi/ufs/ufs_quirks.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -25,6 +25,7 @@ #define UFS_VENDOR_SAMSUNG 0x1CE #define UFS_VENDOR_SKHYNIX 0x1AD #define UFS_VENDOR_WDC 0x145 +#define UFS_VENDOR_MICRON 0x12C /** * ufs_dev_fix - ufs device quirk info diff --git a/drivers/scsi/ufs/ufshcd-crypto-qti.c b/drivers/scsi/ufs/ufshcd-crypto-qti.c index f3351d02d608fd2b038dd865a40c29aaf49cc674..9e64ec5c3942f8047ce150c22e44a60b9845e268 100644 --- a/drivers/scsi/ufs/ufshcd-crypto-qti.c +++ b/drivers/scsi/ufs/ufshcd-crypto-qti.c @@ -236,8 +236,8 @@ static int ufshcd_hba_init_crypto_qti_spec(struct ufs_hba *hba, hba->crypto_cap_array[cap_idx].sdus_mask * 512; } - hba->ksm = keyslot_manager_create(ufshcd_num_keyslots(hba), ksm_ops, - crypto_modes_supported, hba); + hba->ksm = keyslot_manager_create(hba->dev, ufshcd_num_keyslots(hba), + ksm_ops, crypto_modes_supported, hba); if (!hba->ksm) { err = -ENOMEM; diff --git a/drivers/scsi/ufs/ufshcd-crypto.c b/drivers/scsi/ufs/ufshcd-crypto.c index a72b1ca8c3391d065de9dc26f8ae595ec1037a2c..3c7bee842e1fccbc3ccaac99087d0ec9e37459aa 100644 --- a/drivers/scsi/ufs/ufshcd-crypto.c +++ b/drivers/scsi/ufs/ufshcd-crypto.c @@ -125,7 +125,6 @@ static int ufshcd_program_key(struct ufs_hba *hba, u32 slot_offset = hba->crypto_cfg_register + slot * sizeof(*cfg); int err; - pm_runtime_get_sync(hba->dev); ufshcd_hold(hba, false); if (hba->var->vops->program_key) { @@ -155,7 +154,6 @@ static int ufshcd_program_key(struct ufs_hba *hba, err = 0; out: ufshcd_release(hba, false); - pm_runtime_put_sync(hba->dev); return err; } @@ -337,8 +335,8 @@ int ufshcd_hba_init_crypto_spec(struct ufs_hba *hba, ufshcd_clear_all_keyslots(hba); - hba->ksm = keyslot_manager_create(ufshcd_num_keyslots(hba), ksm_ops, - crypto_modes_supported, hba); + hba->ksm = keyslot_manager_create(hba->dev, ufshcd_num_keyslots(hba), + ksm_ops, crypto_modes_supported, hba); if (!hba->ksm) { err = -ENOMEM; diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index e5e7004f284e80d492d439d1a1d9b033bc250ef4..6b9d697306eea225b14abf2860d09f0438db5c11 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -438,6 +438,8 @@ static struct ufs_dev_fix ufs_fixups[] = { UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM), UFS_FIX(UFS_VENDOR_TOSHIBA, UFS_ANY_MODEL, UFS_DEVICE_QUIRK_NO_LINK_OFF), + UFS_FIX(UFS_VENDOR_MICRON, UFS_ANY_MODEL, + UFS_DEVICE_QUIRK_NO_LINK_OFF), UFS_FIX(UFS_VENDOR_TOSHIBA, "THGLF2G9C8KBADG", UFS_DEVICE_QUIRK_PA_TACTIVATE), UFS_FIX(UFS_VENDOR_TOSHIBA, "THGLF2G9D8KBADG", @@ -942,16 +944,19 @@ void ufshcd_print_trs(struct ufs_hba *hba, unsigned long bitmap, bool pr_prdt) ufshcd_hex_dump(hba, "UPIU RSP", lrbp->ucd_rsp_ptr, sizeof(struct utp_upiu_rsp)); - prdt_length = le16_to_cpu( - lrbp->utr_descriptor_ptr->prd_table_length); + prdt_length = + le16_to_cpu(lrbp->utr_descriptor_ptr->prd_table_length); + if (hba->quirks & UFSHCD_QUIRK_PRDT_BYTE_GRAN) + prdt_length /= hba->sg_entry_size; + dev_err(hba->dev, "UPIU[%d] - PRDT - %d entries phys@0x%llx\n", tag, prdt_length, (u64)lrbp->ucd_prdt_dma_addr); if (pr_prdt) - ufshcd_hex_dump(hba, "UPIU PRDT", lrbp->ucd_prdt_ptr, - sizeof(struct ufshcd_sg_entry) * prdt_length); + ufshcd_hex_dump(hba, "UPIU PRDT: ", lrbp->ucd_prdt_ptr, + hba->sg_entry_size * prdt_length); } } @@ -3292,7 +3297,7 @@ ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) */ static int ufshcd_map_sg(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) { - struct ufshcd_sg_entry *prd_table; + struct ufshcd_sg_entry *prd; struct scatterlist *sg; struct scsi_cmnd *cmd; int sg_segments; @@ -3307,21 +3312,22 @@ static int ufshcd_map_sg(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) if (hba->quirks & UFSHCD_QUIRK_PRDT_BYTE_GRAN) lrbp->utr_descriptor_ptr->prd_table_length = cpu_to_le16((u16)(sg_segments * - sizeof(struct ufshcd_sg_entry))); + hba->sg_entry_size)); else lrbp->utr_descriptor_ptr->prd_table_length = cpu_to_le16((u16) (sg_segments)); - prd_table = (struct ufshcd_sg_entry *)lrbp->ucd_prdt_ptr; + prd = (struct ufshcd_sg_entry *)lrbp->ucd_prdt_ptr; scsi_for_each_sg(cmd, sg, sg_segments, i) { - prd_table[i].size = + prd->size = cpu_to_le32(((u32) sg_dma_len(sg))-1); - prd_table[i].base_addr = + prd->base_addr = cpu_to_le32(lower_32_bits(sg->dma_address)); - prd_table[i].upper_addr = + prd->upper_addr = cpu_to_le32(upper_32_bits(sg->dma_address)); - prd_table[i].reserved = 0; + prd->reserved = 0; + prd = (void *)prd + hba->sg_entry_size; } } else { lrbp->utr_descriptor_ptr->prd_table_length = 0; @@ -4720,7 +4726,7 @@ static int ufshcd_memory_alloc(struct ufs_hba *hba) size_t utmrdl_size, utrdl_size, ucdl_size; /* Allocate memory for UTP command descriptors */ - ucdl_size = (sizeof(struct utp_transfer_cmd_desc) * hba->nutrs); + ucdl_size = (sizeof_utp_transfer_cmd_desc(hba) * hba->nutrs); hba->ucdl_base_addr = dmam_alloc_coherent(hba->dev, ucdl_size, &hba->ucdl_dma_addr, @@ -4816,7 +4822,7 @@ static void ufshcd_host_memory_configure(struct ufs_hba *hba) prdt_offset = offsetof(struct utp_transfer_cmd_desc, prd_table); - cmd_desc_size = sizeof(struct utp_transfer_cmd_desc); + cmd_desc_size = sizeof_utp_transfer_cmd_desc(hba); cmd_desc_dma_addr = hba->ucdl_dma_addr; for (i = 0; i < hba->nutrs; i++) { @@ -4848,17 +4854,17 @@ static void ufshcd_host_memory_configure(struct ufs_hba *hba) hba->lrb[i].utr_descriptor_ptr = (utrdlp + i); hba->lrb[i].utrd_dma_addr = hba->utrdl_dma_addr + (i * sizeof(struct utp_transfer_req_desc)); - hba->lrb[i].ucd_req_ptr = - (struct utp_upiu_req *)(cmd_descp + i); + hba->lrb[i].ucd_req_ptr = (struct utp_upiu_req *)cmd_descp; hba->lrb[i].ucd_req_dma_addr = cmd_desc_element_addr; hba->lrb[i].ucd_rsp_ptr = - (struct utp_upiu_rsp *)cmd_descp[i].response_upiu; + (struct utp_upiu_rsp *)cmd_descp->response_upiu; hba->lrb[i].ucd_rsp_dma_addr = cmd_desc_element_addr + response_offset; hba->lrb[i].ucd_prdt_ptr = - (struct ufshcd_sg_entry *)cmd_descp[i].prd_table; + (struct ufshcd_sg_entry *)cmd_descp->prd_table; hba->lrb[i].ucd_prdt_dma_addr = cmd_desc_element_addr + prdt_offset; + cmd_descp = (void *)cmd_descp + cmd_desc_size; } } @@ -6786,6 +6792,7 @@ static int ufshcd_disable_auto_bkops(struct ufs_hba *hba) hba->auto_bkops_enabled = false; trace_ufshcd_auto_bkops_state(dev_name(hba->dev), 0); + hba->is_urgent_bkops_lvl_checked = false; out: return err; } @@ -6810,6 +6817,7 @@ static void ufshcd_force_reset_auto_bkops(struct ufs_hba *hba) hba->ee_ctrl_mask &= ~MASK_EE_URGENT_BKOPS; ufshcd_disable_auto_bkops(hba); } + hba->is_urgent_bkops_lvl_checked = false; } static inline int ufshcd_get_bkops_status(struct ufs_hba *hba, u32 *status) @@ -6856,6 +6864,7 @@ static int ufshcd_bkops_ctrl(struct ufs_hba *hba, err = ufshcd_enable_auto_bkops(hba); else err = ufshcd_disable_auto_bkops(hba); + hba->urgent_bkops_lvl = curr_status; out: return err; } @@ -8179,7 +8188,6 @@ static int ufshcd_detect_device(struct ufs_hba *hba) static int ufshcd_reset_and_restore(struct ufs_hba *hba) { int err = 0; - unsigned long flags; int retries = MAX_HOST_RESET_RETRIES; ufshcd_enable_irq(hba); @@ -8194,15 +8202,6 @@ static int ufshcd_reset_and_restore(struct ufs_hba *hba) */ BUG_ON(err && ufshcd_is_embedded_dev(hba)); - /* - * After reset the door-bell might be cleared, complete - * outstanding requests in s/w here. - */ - spin_lock_irqsave(hba->host->host_lock, flags); - ufshcd_transfer_req_compl(hba); - ufshcd_tmc_handler(hba); - spin_unlock_irqrestore(hba->host->host_lock, flags); - return err; } @@ -8528,8 +8527,9 @@ static int ufs_get_device_desc(struct ufs_hba *hba, model_index = desc_buf[DEVICE_DESC_PARAM_PRDCT_NAME]; - /* Enable WB only for UFS-3.1 OR if desc len >= 0x59 */ + /* Enable WB only for UFS-3.1 or UFS-2.2 OR if desc len >= 0x59 */ if ((dev_desc->wspecversion >= 0x310) || + (dev_desc->wspecversion == 0x220) || (dev_desc->wmanufacturerid == UFS_VENDOR_TOSHIBA && dev_desc->wspecversion >= 0x300 && hba->desc_size.dev_desc >= 0x59)) { @@ -9082,21 +9082,27 @@ static int ufshcd_probe_hba(struct ufs_hba *hba) goto out; } - /** - * UFS3.0 and newer devices use Vcc and Vccq(1.2V) - * while UFS2.1 devices use Vcc and Vccq2(1.8V) power - * supplies. If the system allows turning off the regulators - * during power collapse event, turn off the regulators - * during system suspend events. This will cause the UFS - * device to re-initialize upon system resume events. + /* + * On 4.19 kernel, the controlling of Vccq requlator got changed. + * Its relying on sys_suspend_pwr_off regulator dt flag instead of spm + * level. + * Updated logic is not honoring spm level specified and the device + * specific quirks for the desired link state. + * Below change is to fix above listed issue without distrubing the + * present logic. */ - if ((hba->dev_info.w_spec_version >= 0x300 && hba->vreg_info.vccq && - hba->vreg_info.vccq->sys_suspend_pwr_off) || - (hba->dev_info.w_spec_version < 0x300 && - hba->vreg_info.vccq2->sys_suspend_pwr_off)) - hba->spm_lvl = ufs_get_desired_pm_lvl_for_dev_link_state( + if (hba->spm_lvl == ufs_get_desired_pm_lvl_for_dev_link_state( UFS_POWERDOWN_PWR_MODE, - UIC_LINK_OFF_STATE); + UIC_LINK_OFF_STATE)) { + if ((hba->dev_info.w_spec_version >= 0x300 && + hba->vreg_info.vccq && + !hba->vreg_info.vccq->sys_suspend_pwr_off)) + hba->vreg_info.vccq->sys_suspend_pwr_off = true; + + if ((hba->dev_info.w_spec_version < 0x300 && + !hba->vreg_info.vccq2->sys_suspend_pwr_off)) + hba->vreg_info.vccq2->sys_suspend_pwr_off = true; + } /* UFS device is also active now */ ufshcd_set_ufs_dev_active(hba); @@ -9153,7 +9159,8 @@ static int ufshcd_probe_hba(struct ufs_hba *hba) hba->dev_info.f_power_on_wp_en = flag; /* Add required well known logical units to scsi mid layer */ - if (ufshcd_scsi_add_wlus(hba)) + ret = ufshcd_scsi_add_wlus(hba); + if (ret) goto out; /* lower VCC voltage level */ @@ -10299,10 +10306,18 @@ static void ufshcd_hba_vreg_set_lpm(struct ufs_hba *hba) static void ufshcd_hba_vreg_set_hpm(struct ufs_hba *hba) { + int ret; + struct ufs_vreg_info *info = &hba->vreg_info; + if (ufshcd_is_link_off(hba) || (ufshcd_is_link_hibern8(hba) && ufshcd_is_power_collapse_during_hibern8_allowed(hba))) - ufshcd_setup_hba_vreg(hba, true); + ret = ufshcd_setup_hba_vreg(hba, true); + + if (ret && (info->vdd_hba->enabled == false)) { + dev_err(hba->dev, "vdd_hba is not enabled\n"); + BUG_ON(1); + } } /** @@ -10948,6 +10963,7 @@ int ufshcd_alloc_host(struct device *dev, struct ufs_hba **hba_handle) hba->host = host; hba->dev = dev; *hba_handle = hba; + hba->sg_entry_size = sizeof(struct ufshcd_sg_entry); INIT_LIST_HEAD(&hba->clk_list_head); diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 8d88011a06423f6e699e13e833945eec0d3fca7b..4d6e66ab0d19e084e94015a8548539e3622fdc70 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -756,6 +756,7 @@ struct ufshcd_cmd_log { * @ufs_version: UFS Version to which controller complies * @var: pointer to variant specific data * @priv: pointer to variant specific private data + * @sg_entry_size: size of struct ufshcd_sg_entry (may include variant fields) * @irq: Irq number of the controller * @active_uic_cmd: handle of active UIC command * @uic_cmd_mutex: mutex for uic command @@ -776,6 +777,7 @@ struct ufshcd_cmd_log { * @uic_error: UFS interconnect layer error status * @saved_err: sticky error mask * @saved_uic_err: sticky UIC error mask + * @silence_err_logs: flag to silence error logs * @dev_cmd: ufs device management command information * @last_dme_cmd_tstamp: time stamp of the last completed DME command * @auto_bkops_enabled: to track whether bkops is enabled in device @@ -845,6 +847,7 @@ struct ufs_hba { struct ufs_hba_variant *var; void *priv; const struct ufs_hba_crypto_variant_ops *crypto_vops; + size_t sg_entry_size; unsigned int irq; bool is_irq_enabled; bool crash_on_err; @@ -988,10 +991,10 @@ struct ufs_hba { u32 saved_err; u32 saved_uic_err; u32 saved_ce_err; - bool silence_err_logs; bool force_host_reset; bool auto_h8_err; struct ufs_stats ufs_stats; + bool silence_err_logs; /* Device management request data */ struct ufs_dev_cmd dev_cmd; diff --git a/drivers/scsi/ufs/ufshci.h b/drivers/scsi/ufs/ufshci.h index 3bf11f78c8e512a7cdd6a3008f3c20ecceab4e69..96408c3bb420e69b8bcbce7ac4ef2c418d0912cf 100644 --- a/drivers/scsi/ufs/ufshci.h +++ b/drivers/scsi/ufs/ufshci.h @@ -483,21 +483,27 @@ struct ufshcd_sg_entry { __le32 upper_addr; __le32 reserved; __le32 size; + /* + * followed by variant-specific fields if + * hba->sg_entry_size != sizeof(struct ufshcd_sg_entry) + */ }; /** * struct utp_transfer_cmd_desc - UFS Command Descriptor structure * @command_upiu: Command UPIU Frame address * @response_upiu: Response UPIU Frame address - * @prd_table: Physical Region Descriptor + * @prd_table: Physical Region Descriptor: an array of SG_ALL struct + * ufshcd_sg_entry's. Variant-specific fields may be present after each. */ struct utp_transfer_cmd_desc { u8 command_upiu[ALIGNED_UPIU_SIZE]; u8 response_upiu[ALIGNED_UPIU_SIZE]; - struct ufshcd_sg_entry prd_table[SG_ALL]; + u8 prd_table[]; }; -#define UTRD_CRYPTO_ENABLE UFS_BIT(23) +#define sizeof_utp_transfer_cmd_desc(hba) \ + (sizeof(struct utp_transfer_cmd_desc) + SG_ALL * (hba)->sg_entry_size) /** * struct request_desc_header - Descriptor Header common to both UTRD and UTMRD diff --git a/drivers/soc/amlogic/meson-gx-pwrc-vpu.c b/drivers/soc/amlogic/meson-gx-pwrc-vpu.c index 6289965c42e98cbe6c13611bf8dd7c29ddb17c80..05421d029dff9bd3fc3c4e64927ef1aca8e84a52 100644 --- a/drivers/soc/amlogic/meson-gx-pwrc-vpu.c +++ b/drivers/soc/amlogic/meson-gx-pwrc-vpu.c @@ -54,12 +54,12 @@ static int meson_gx_pwrc_vpu_power_off(struct generic_pm_domain *genpd) /* Power Down Memories */ for (i = 0; i < 32; i += 2) { regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0, - 0x2 << i, 0x3 << i); + 0x3 << i, 0x3 << i); udelay(5); } for (i = 0; i < 32; i += 2) { regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1, - 0x2 << i, 0x3 << i); + 0x3 << i, 0x3 << i); udelay(5); } for (i = 8; i < 16; i++) { @@ -108,13 +108,13 @@ static int meson_gx_pwrc_vpu_power_on(struct generic_pm_domain *genpd) /* Power Up Memories */ for (i = 0; i < 32; i += 2) { regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0, - 0x2 << i, 0); + 0x3 << i, 0); udelay(5); } for (i = 0; i < 32; i += 2) { regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1, - 0x2 << i, 0); + 0x3 << i, 0); udelay(5); } diff --git a/drivers/soc/amlogic/meson-gx-socinfo.c b/drivers/soc/amlogic/meson-gx-socinfo.c index 37ea0a1c24c821f7457b23e2a5bf91a1e6da57ce..1ae339f5eadbd17d716f814d65d824852c954848 100644 --- a/drivers/soc/amlogic/meson-gx-socinfo.c +++ b/drivers/soc/amlogic/meson-gx-socinfo.c @@ -43,20 +43,21 @@ static const struct meson_gx_package_id { const char *name; unsigned int major_id; unsigned int pack_id; + unsigned int pack_mask; } soc_packages[] = { - { "S905", 0x1f, 0 }, - { "S905H", 0x1f, 0x13 }, - { "S905M", 0x1f, 0x20 }, - { "S905D", 0x21, 0 }, - { "S905X", 0x21, 0x80 }, - { "S905W", 0x21, 0xa0 }, - { "S905L", 0x21, 0xc0 }, - { "S905M2", 0x21, 0xe0 }, - { "S912", 0x22, 0 }, - { "962X", 0x24, 0x10 }, - { "962E", 0x24, 0x20 }, - { "A113X", 0x25, 0x37 }, - { "A113D", 0x25, 0x22 }, + { "S905", 0x1f, 0, 0x20 }, /* pack_id != 0x20 */ + { "S905H", 0x1f, 0x3, 0xf }, /* pack_id & 0xf == 0x3 */ + { "S905M", 0x1f, 0x20, 0xf0 }, /* pack_id == 0x20 */ + { "S905D", 0x21, 0, 0xf0 }, + { "S905X", 0x21, 0x80, 0xf0 }, + { "S905W", 0x21, 0xa0, 0xf0 }, + { "S905L", 0x21, 0xc0, 0xf0 }, + { "S905M2", 0x21, 0xe0, 0xf0 }, + { "S912", 0x22, 0, 0x0 }, /* Only S912 is known for GXM */ + { "962X", 0x24, 0x10, 0xf0 }, + { "962E", 0x24, 0x20, 0xf0 }, + { "A113X", 0x25, 0x37, 0xff }, + { "A113D", 0x25, 0x22, 0xff }, }; static inline unsigned int socinfo_to_major(u32 socinfo) @@ -81,13 +82,14 @@ static inline unsigned int socinfo_to_misc(u32 socinfo) static const char *socinfo_to_package_id(u32 socinfo) { - unsigned int pack = socinfo_to_pack(socinfo) & 0xf0; + unsigned int pack = socinfo_to_pack(socinfo); unsigned int major = socinfo_to_major(socinfo); int i; for (i = 0 ; i < ARRAY_SIZE(soc_packages) ; ++i) { if (soc_packages[i].major_id == major && - soc_packages[i].pack_id == pack) + soc_packages[i].pack_id == + (pack & soc_packages[i].pack_mask)) return soc_packages[i].name; } diff --git a/drivers/soc/fsl/qe/gpio.c b/drivers/soc/fsl/qe/gpio.c index 819bed0f566799db5e34988282ae85032a8742ad..51b3a47b5a559b4d3b6663a87f417a793181033b 100644 --- a/drivers/soc/fsl/qe/gpio.c +++ b/drivers/soc/fsl/qe/gpio.c @@ -179,8 +179,10 @@ struct qe_pin *qe_pin_request(struct device_node *np, int index) if (err < 0) goto err0; gc = gpio_to_chip(err); - if (WARN_ON(!gc)) + if (WARN_ON(!gc)) { + err = -ENODEV; goto err0; + } if (!of_device_is_compatible(gc->of_node, "fsl,mpc8323-qe-pario-bank")) { pr_debug("%s: tried to get a non-qe pin\n", __func__); diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 0d74602e9ae1995619ae8437d85da0f725087b06..e0be5b36c558052ac759969872f66c4747d84996 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -4,7 +4,7 @@ # menu "Qualcomm SoC drivers" config QCOM_COMMAND_DB - bool "Qualcomm Command DB" + tristate "Qualcomm Command DB" depends on ARCH_QCOM || COMPILE_TEST depends on OF_RESERVED_MEM help @@ -241,7 +241,7 @@ config QCOM_RMTFS_MEM Say y here if you intend to boot the modem remoteproc. config QCOM_RPMH - bool "Qualcomm RPM-Hardened (RPMH) Communication" + tristate "Qualcomm RPM-Hardened (RPMH) Communication" depends on ARCH_QCOM && ARM64 && OF || COMPILE_TEST help Support for communication with the hardened-RPM blocks in @@ -343,6 +343,15 @@ config QCOM_WCNSS_CTRL Client driver for the WCNSS_CTRL SMD channel, used to download nv firmware to a newly booted WCNSS chip. +config MSM_PIL_MSS_QDSP6V5 + tristate "MSS QDSP6v5 (Hexagon) Boot Support" + depends on MSM_PIL && MSM_SUBSYSTEM_RESTART + help + Support for booting and shutting down QDSP6v5 (Hexagon) processors + in modem subsystems. If you would like to make or receive phone + calls then say Y here. + If unsure, say N. + config SETUP_SSR_NOTIF_TIMEOUTS bool "Set timeouts on SSR sysmon notifications and notifier callbacks" help @@ -580,6 +589,14 @@ config QCOM_WATCHDOG_V2 deadlocks. It does not run during the bootup process, so it will not catch any early lockups. +config QCOM_INITIAL_LOGBUF + bool "QCOM save initial log_buf" + depends on QCOM_WATCHDOG_V2 + help + This enables to keep copy of initial log_buf at 100 second from + bootup.It can help in debugging issues which are manifestation of + failure during initial bootup. + config QCOM_FORCE_WDOG_BITE_ON_PANIC bool "QCOM force watchdog bite" depends on QCOM_WATCHDOG_V2 diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index f5b2b90e2c1695bf2a279852d3e244b81fd34528..62a34a5e20b8fea6f87e410fd7394b7a7f48c30e 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_MSM_SERVICE_NOTIFIER) += service-notifier.o obj-$(CONFIG_MSM_SERVICE_LOCATOR) += service-locator.o obj-$(CONFIG_MSM_SYSMON_GLINK_COMM) += sysmon-glink.o obj-$(CONFIG_MSM_SYSMON_QMI_COMM) += sysmon-qmi.o +obj-$(CONFIG_MSM_PIL_MSS_QDSP6V5) += pil-q6v5.o pil-msa.o pil-q6v5-mss.o obj-$(CONFIG_MSM_PIL_SSR_GENERIC) += subsys-pil-tz.o obj-$(CONFIG_MEM_SHARE_QMI_SERVICE) += memshare/ obj-$(CONFIG_MSM_PIL) += peripheral-loader.o diff --git a/drivers/soc/qcom/cmd-db.c b/drivers/soc/qcom/cmd-db.c index a6532a74e1f670a0973141f3543bf1397226c99b..1515e83188cb1768571231d732bea542c03ffafe 100644 --- a/drivers/soc/qcom/cmd-db.c +++ b/drivers/soc/qcom/cmd-db.c @@ -2,6 +2,7 @@ /* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. */ #include +#include #include #include #include @@ -399,8 +400,8 @@ static int cmd_db_dev_probe(struct platform_device *pdev) } cmd_db_header = memremap(rmem->base, rmem->size, MEMREMAP_WB); - if (IS_ERR_OR_NULL(cmd_db_header)) { - ret = PTR_ERR(cmd_db_header); + if (!cmd_db_header) { + ret = -ENOMEM; cmd_db_header = NULL; return ret; } @@ -437,3 +438,5 @@ static int __init cmd_db_device_init(void) return platform_driver_register(&cmd_db_dev_driver); } arch_initcall(cmd_db_device_init); +MODULE_DESCRIPTION("Qualcomm Command DB"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/cx_ipeak.c b/drivers/soc/qcom/cx_ipeak.c index 543885b4229dc395b34c776dae9ab4b2d559d8fd..9952316cd7176728932e235cf24f52f533cae897 100644 --- a/drivers/soc/qcom/cx_ipeak.c +++ b/drivers/soc/qcom/cx_ipeak.c @@ -1,17 +1,17 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. */ #include #include #include #include -#include #include #include #include #include +#include #include @@ -29,9 +29,13 @@ #define CXIP_CLIENT_OFFSET 0x1000 #define CXIP_CLIENT10_OFFSET 0x3000 +#define CXIP_VICTIM_OFFSET 0xB000 #define CXIP_POLL_TIMEOUT_US (50 * 1000) +#define CXIP_VICTIMS 3 +#define VICTIM_ENTRIES 3 + struct cx_ipeak_client; struct cx_ipeak_core_ops { @@ -39,18 +43,35 @@ struct cx_ipeak_core_ops { struct cx_ipeak_client* (*register_client)(int client_id); }; +static struct cx_ipeak_victims { + u32 client_id; + u32 victim_id; + u32 freq_limit; + void *data; + cx_ipeak_victim_fn victim_cb; + struct cx_ipeak_client *client; +} victim_list[CXIP_VICTIMS]; + static struct cx_ipeak_device { - spinlock_t vote_lock; + struct platform_device *pdev; + struct mutex vote_lock; + struct mutex throttle_lock; void __iomem *tcsr_vptr; struct cx_ipeak_core_ops *core_ops; + u32 victims_count; + int danger_intr_num; + int safe_intr_num; } device_ipeak; struct cx_ipeak_client { - int vote_count; + u32 vote_count; unsigned int offset; + u32 client_id; + bool danger_assert; struct cx_ipeak_device *dev; }; + /** * cx_ipeak_register() - allocate client structure and fill device private and * offset details. @@ -84,6 +105,9 @@ struct cx_ipeak_client *cx_ipeak_register(struct device_node *dev_node, if (device_ipeak.core_ops) client = device_ipeak.core_ops->register_client (cx_spec.args[0]); + + client->client_id = cx_spec.args[0]; + return client; } EXPORT_SYMBOL(cx_ipeak_register); @@ -145,7 +169,53 @@ static struct cx_ipeak_client *cx_ipeak_register_v2(int client_id) return client; } -/* +/** + * cx_ipeak_victim_register - victim registration API to handle + * the cx ipeak hw interrupts (danger/safe) to throttle freq. + * @client: cx ipeak client + * @victim_cb: callback function of victim + * @data: data to be passed to victim while handling irq + */ +int cx_ipeak_victim_register(struct cx_ipeak_client *client, + cx_ipeak_victim_fn victim_cb, void *data) +{ + int i = 0; + + if (!victim_cb) + return -EINVAL; + + for (i = 0; i < device_ipeak.victims_count; i++) + if (client->client_id == victim_list[i].client_id) { + victim_list[i].victim_cb = victim_cb; + victim_list[i].data = data; + victim_list[i].client = client; + return 0; + } + + return -ENOENT; +} +EXPORT_SYMBOL(cx_ipeak_victim_register); + +/** + * cx_ipeak_victim_unregister - unregister victim client from + * cx_ipeak driver. + * @client: cx ipeak client + */ + +void cx_ipeak_victim_unregister(struct cx_ipeak_client *client) +{ + int i = 0; + + for (i = 0; i < device_ipeak.victims_count; i++) + if (client->client_id == victim_list[i].client_id) { + victim_list[i].victim_cb = NULL; + victim_list[i].data = NULL; + victim_list[i].client = NULL; + } +} +EXPORT_SYMBOL(cx_ipeak_victim_unregister); + +/** * cx_ipeak_update() - Set/Clear client vote for Cx iPeak limit * manager to throttle cDSP. * @client: client handle. @@ -172,7 +242,7 @@ static int cx_ipeak_update_v1(struct cx_ipeak_client *client, bool vote) unsigned int reg_val; int ret = 0; - spin_lock(&client->dev->vote_lock); + mutex_lock(&client->dev->vote_lock); if (vote) { if (client->vote_count == 0) { @@ -211,27 +281,36 @@ static int cx_ipeak_update_v1(struct cx_ipeak_client *client, bool vote) } done: - spin_unlock(&client->dev->vote_lock); + mutex_unlock(&client->dev->vote_lock); return ret; } static int cx_ipeak_update_v2(struct cx_ipeak_client *client, bool vote) { - unsigned int reg_val; + u32 reg_val; int ret = 0; - spin_lock(&client->dev->vote_lock); + mutex_lock(&client->dev->vote_lock); if (vote) { if (client->vote_count == 0) { writel_relaxed(BIT(0), - client->dev->tcsr_vptr + - client->offset); + client->dev->tcsr_vptr + + client->offset); + + ret = readl_poll_timeout( + client->dev->tcsr_vptr + + TCSR_CXIP_LM_DANGER_OFFSET, + reg_val, !reg_val || + client->danger_assert, + 0, CXIP_POLL_TIMEOUT_US); + /* + * If poll exits due to danger assert condition return + * error to client to avoid voting. + */ + if (client->danger_assert) + ret = -ETIMEDOUT; - ret = readl_poll_timeout(client->dev->tcsr_vptr + - TCSR_CXIP_LM_DANGER_OFFSET, - reg_val, !reg_val, 0, - CXIP_POLL_TIMEOUT_US); if (ret) { writel_relaxed(0, client->dev->tcsr_vptr + @@ -254,10 +333,94 @@ static int cx_ipeak_update_v2(struct cx_ipeak_client *client, bool vote) } done: - spin_unlock(&client->dev->vote_lock); + mutex_unlock(&client->dev->vote_lock); return ret; } +static irqreturn_t cx_ipeak_irq_soft_handler(int irq, void *data) +{ + int i; + irqreturn_t ret = IRQ_NONE; + + mutex_lock(&device_ipeak.throttle_lock); + + for (i = 0; i < device_ipeak.victims_count; i++) { + cx_ipeak_victim_fn victim_cb = victim_list[i].victim_cb; + struct cx_ipeak_client *victim_client = victim_list[i].client; + + if (!victim_cb || !victim_client) + continue; + + if (irq == device_ipeak.danger_intr_num) { + + victim_client->danger_assert = true; + + /* + * To set frequency limit at victim client + * side in danger interrupt case + */ + + ret = victim_cb(victim_list[i].data, + victim_list[i].freq_limit); + + if (ret) { + dev_err(&device_ipeak.pdev->dev, + "Unable to throttle client:%d freq:%d\n", + victim_list[i].client_id, + victim_list[i].freq_limit); + victim_client->danger_assert = false; + ret = IRQ_HANDLED; + goto done; + } + + writel_relaxed(1, (device_ipeak.tcsr_vptr + + CXIP_VICTIM_OFFSET + + ((victim_list[i].victim_id)* + CXIP_CLIENT_OFFSET))); + + ret = IRQ_HANDLED; + } else if (irq == device_ipeak.safe_intr_num) { + victim_client->danger_assert = false; + /* + * To remove frequency limit at victim client + * side in safe interrupt case + */ + ret = victim_cb(victim_list[i].data, 0); + + if (ret) + dev_err(&device_ipeak.pdev->dev, "Unable to remove freq limit client:%d\n", + victim_list[i].client_id); + + writel_relaxed(0, (device_ipeak.tcsr_vptr + + CXIP_VICTIM_OFFSET + + ((victim_list[i].victim_id)* + CXIP_CLIENT_OFFSET))); + ret = IRQ_HANDLED; + } + } +done: + mutex_unlock(&device_ipeak.throttle_lock); + return ret; +} + +int cx_ipeak_request_irq(struct platform_device *pdev, const char *name, + irq_handler_t handler, irq_handler_t thread_fn, void *data) +{ + int ret, num = platform_get_irq_byname(pdev, name); + + if (num < 0) + return num; + + ret = devm_request_threaded_irq(&pdev->dev, num, handler, thread_fn, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, name, data); + + if (ret) + dev_err(&pdev->dev, "Unable to get interrupt %s: %d\n", + name, ret); + + return ret ? ret : num; +} + /** * cx_ipeak_unregister() - unregister client * @client: client address to free @@ -283,6 +446,8 @@ struct cx_ipeak_core_ops core_ops_v2 = { static int cx_ipeak_probe(struct platform_device *pdev) { struct resource *res; + int i, ret, count; + u32 victim_en; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); device_ipeak.tcsr_vptr = devm_ioremap_resource(&pdev->dev, res); @@ -297,7 +462,61 @@ static int cx_ipeak_probe(struct platform_device *pdev) else device_ipeak.core_ops = NULL; - spin_lock_init(&device_ipeak.vote_lock); + victim_en = of_property_read_bool(pdev->dev.of_node, + "victims_table"); + + if (victim_en) { + count = of_property_count_u32_elems(pdev->dev.of_node, + "victims_table"); + + if (((count%VICTIM_ENTRIES) != 0) || + ((count/VICTIM_ENTRIES) > CXIP_VICTIMS)) + return -EINVAL; + + for (i = 0; i < (count/VICTIM_ENTRIES); i++) { + ret = of_property_read_u32_index(pdev->dev.of_node, + "victims_table", i*VICTIM_ENTRIES, + &victim_list[i].client_id); + + if (ret) + return ret; + + ret = of_property_read_u32_index(pdev->dev.of_node, + "victims_table", (i*VICTIM_ENTRIES) + 1, + &victim_list[i].victim_id); + + if (ret) + return ret; + + ret = of_property_read_u32_index(pdev->dev.of_node, + "victims_table", (i*VICTIM_ENTRIES) + 2, + &victim_list[i].freq_limit); + + if (ret) + return ret; + + device_ipeak.victims_count++; + } + + device_ipeak.danger_intr_num = cx_ipeak_request_irq(pdev, + "cx_ipeak_danger", NULL, + cx_ipeak_irq_soft_handler, NULL); + + if (device_ipeak.danger_intr_num < 0) + return device_ipeak.danger_intr_num; + + device_ipeak.safe_intr_num = cx_ipeak_request_irq(pdev, + "cx_ipeak_safe", NULL, + cx_ipeak_irq_soft_handler, NULL); + + if (device_ipeak.safe_intr_num < 0) + return device_ipeak.safe_intr_num; + + } + + device_ipeak.pdev = pdev; + mutex_init(&device_ipeak.vote_lock); + mutex_init(&device_ipeak.throttle_lock); return 0; } diff --git a/drivers/soc/qcom/dcc_v2.c b/drivers/soc/qcom/dcc_v2.c index ada4be81392413d45a0d64fa83473a2a2feb92d4..d8e2fd36800624c9d6f074148b50915c5304d29a 100644 --- a/drivers/soc/qcom/dcc_v2.c +++ b/drivers/soc/qcom/dcc_v2.c @@ -1580,7 +1580,8 @@ static ssize_t dcc_sram_read(struct file *file, char __user *data, if (drvdata->ram_size <= *ppos) return 0; - if ((*ppos + len) > drvdata->ram_size) + if ((*ppos + len) < len + || (*ppos + len) > drvdata->ram_size) len = (drvdata->ram_size - *ppos); buf = kzalloc(len, GFP_KERNEL); diff --git a/drivers/soc/qcom/eud.c b/drivers/soc/qcom/eud.c index 864bd65d0e2e164ca84cac222d1d0e184e652578..0ee43a8c495ec4655c7de9525b32aea6a7c7b85c 100644 --- a/drivers/soc/qcom/eud.c +++ b/drivers/soc/qcom/eud.c @@ -92,14 +92,6 @@ static int enable; static bool eud_ready; static struct platform_device *eud_private; -static int check_eud_mode_mgr2(struct eud_chip *chip) -{ - u32 val; - - val = scm_io_read(chip->eud_mode_mgr2_phys_base); - return val & BIT(0); -} - static void enable_eud(struct platform_device *pdev) { struct eud_chip *priv = platform_get_drvdata(pdev); @@ -113,7 +105,7 @@ static void enable_eud(struct platform_device *pdev) priv->eud_reg_base + EUD_REG_INT1_EN_MASK); /* Enable secure eud if supported */ - if (priv->secure_eud_en && !check_eud_mode_mgr2(priv)) { + if (priv->secure_eud_en) { ret = scm_io_write(priv->eud_mode_mgr2_phys_base + EUD_REG_EUD_EN2, EUD_ENABLE_CMD); if (ret) @@ -572,9 +564,6 @@ static int msm_eud_probe(struct platform_device *pdev) } chip->eud_mode_mgr2_phys_base = res->start; - - if (check_eud_mode_mgr2(chip)) - enable = 1; } chip->need_phy_clk_vote = of_property_read_bool(pdev->dev.of_node, diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index addf18ee51eb6bd383e55923a42b084a555f91dd..4dcfc8312a6e81ba080de268a5720badf495c968 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -844,6 +844,8 @@ static int icnss_driver_event_server_arrive(void *data) set_bit(ICNSS_WLFW_EXISTS, &penv->state); clear_bit(ICNSS_FW_DOWN, &penv->state); + clear_bit(ICNSS_FW_READY, &penv->state); + icnss_ignore_fw_timeout(false); if (test_bit(ICNSS_WLFW_CONNECTED, &penv->state)) { @@ -1460,12 +1462,11 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb, notif->crashed == CRASH_STATUS_ERR_FATAL) { icnss_pr_info("Collecting msa0 segment dump\n"); icnss_msa0_ramdump(priv); - - return NOTIFY_OK; + goto out; } if (code != SUBSYS_BEFORE_SHUTDOWN) - return NOTIFY_OK; + goto out; priv->is_ssr = true; @@ -1481,7 +1482,7 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb, ICNSS_UEVENT_FW_DOWN, &fw_down_data); } - return NOTIFY_OK; + goto out; } icnss_pr_info("Modem went down, state: 0x%lx, crashed: %d\n", @@ -1498,8 +1499,10 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb, event_data = kzalloc(sizeof(*event_data), GFP_KERNEL); - if (event_data == NULL) + if (event_data == NULL) { + icnss_pr_vdbg("Exit %s, event_data is NULL\n", __func__); return notifier_from_errno(-ENOMEM); + } event_data->crashed = notif->crashed; @@ -1511,7 +1514,8 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb, } icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN, ICNSS_EVENT_SYNC, event_data); - +out: + icnss_pr_vdbg("Exit %s,state: 0x%lx\n", __func__, priv->state); return NOTIFY_OK; } @@ -2799,6 +2803,9 @@ static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv) continue; case ICNSS_PDR: seq_puts(s, "PDR TRIGGERED"); + continue; + case ICNSS_DEL_SERVER: + seq_puts(s, "DEL SERVER"); } seq_printf(s, "UNKNOWN-%d", i); diff --git a/drivers/soc/qcom/icnss2/debug.c b/drivers/soc/qcom/icnss2/debug.c index 8731a7b4df436ebb62bf1aa8e307bb3c72aa05c5..3771a6b39babadabcd4d067e6adef4dc51c33a5c 100644 --- a/drivers/soc/qcom/icnss2/debug.c +++ b/drivers/soc/qcom/icnss2/debug.c @@ -404,6 +404,9 @@ static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv) continue; case ICNSS_PDR: seq_puts(s, "PDR TRIGGERED"); + continue; + case ICNSS_DEL_SERVER: + seq_puts(s, "DEL SERVER"); } seq_printf(s, "UNKNOWN-%d", i); diff --git a/drivers/soc/qcom/icnss2/main.c b/drivers/soc/qcom/icnss2/main.c index b869497d911ce6640049af1ab5baddb4a6c47eaf..13f0bc081951f23bda663e4969317f175e0bfc4b 100644 --- a/drivers/soc/qcom/icnss2/main.c +++ b/drivers/soc/qcom/icnss2/main.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -177,6 +178,20 @@ char *icnss_driver_event_to_str(enum icnss_driver_event_type type) return "UNKNOWN"; }; +char *icnss_soc_wake_event_to_str(enum icnss_soc_wake_event_type type) +{ + switch (type) { + case ICNSS_SOC_WAKE_REQUEST_EVENT: + return "SOC_WAKE_REQUEST"; + case ICNSS_SOC_WAKE_RELEASE_EVENT: + return "SOC_WAKE_RELEASE"; + case ICNSS_SOC_WAKE_EVENT_MAX: + return "SOC_EVENT_MAX"; + } + + return "UNKNOWN"; +}; + int icnss_driver_event_post(struct icnss_priv *priv, enum icnss_driver_event_type type, u32 flags, void *data) @@ -249,6 +264,78 @@ int icnss_driver_event_post(struct icnss_priv *priv, return ret; } +int icnss_soc_wake_event_post(struct icnss_priv *priv, + enum icnss_soc_wake_event_type type, + u32 flags, void *data) +{ + struct icnss_soc_wake_event *event; + unsigned long irq_flags; + int gfp = GFP_KERNEL; + int ret = 0; + + if (!priv) + return -ENODEV; + + icnss_pr_dbg("Posting event: %s(%d), %s, flags: 0x%x, state: 0x%lx\n", + icnss_soc_wake_event_to_str(type), type, current->comm, + flags, priv->state); + + if (type >= ICNSS_SOC_WAKE_EVENT_MAX) { + icnss_pr_err("Invalid Event type: %d, can't post", type); + return -EINVAL; + } + + if (in_interrupt() || irqs_disabled()) + gfp = GFP_ATOMIC; + + event = kzalloc(sizeof(*event), gfp); + if (!event) + return -ENOMEM; + + icnss_pm_stay_awake(priv); + + event->type = type; + event->data = data; + init_completion(&event->complete); + event->ret = ICNSS_EVENT_PENDING; + event->sync = !!(flags & ICNSS_EVENT_SYNC); + + spin_lock_irqsave(&priv->soc_wake_msg_lock, irq_flags); + list_add_tail(&event->list, &priv->soc_wake_msg_list); + spin_unlock_irqrestore(&priv->soc_wake_msg_lock, irq_flags); + + priv->stats.soc_wake_events[type].posted++; + queue_work(priv->soc_wake_wq, &priv->soc_wake_msg_work); + + if (!(flags & ICNSS_EVENT_SYNC)) + goto out; + + if (flags & ICNSS_EVENT_UNINTERRUPTIBLE) + wait_for_completion(&event->complete); + else + ret = wait_for_completion_interruptible(&event->complete); + + icnss_pr_dbg("Completed event: %s(%d), state: 0x%lx, ret: %d/%d\n", + icnss_soc_wake_event_to_str(type), type, priv->state, ret, + event->ret); + + spin_lock_irqsave(&priv->soc_wake_msg_lock, irq_flags); + if (ret == -ERESTARTSYS && event->ret == ICNSS_EVENT_PENDING) { + event->sync = false; + spin_unlock_irqrestore(&priv->soc_wake_msg_lock, irq_flags); + ret = -EINTR; + goto out; + } + spin_unlock_irqrestore(&priv->soc_wake_msg_lock, irq_flags); + + ret = event->ret; + kfree(event); + +out: + icnss_pm_relax(priv); + return ret; +} + bool icnss_is_fw_ready(void) { if (!penv) @@ -448,6 +535,8 @@ static int icnss_driver_event_server_arrive(struct icnss_priv *priv, set_bit(ICNSS_WLFW_EXISTS, &priv->state); clear_bit(ICNSS_FW_DOWN, &priv->state); + clear_bit(ICNSS_FW_READY, &priv->state); + icnss_ignore_fw_timeout(false); if (test_bit(ICNSS_WLFW_CONNECTED, &penv->state)) { @@ -522,7 +611,7 @@ static int icnss_driver_event_server_arrive(struct icnss_priv *priv, goto err_power_on; } - icnss_pr_dbg("MEM_BASE pa: %pa, va: 0x%pK\n", + icnss_pr_dbg("Non-Secured Bar Address pa: %pa, va: 0x%pK\n", &priv->mem_base_pa, priv->mem_base_va); @@ -888,6 +977,41 @@ static int icnss_qdss_trace_save_hdlr(struct icnss_priv *priv, return ret; } +static int icnss_event_soc_wake_request(struct icnss_priv *priv, void *data) +{ + int ret = 0; + + if (!priv) + return -ENODEV; + + ret = wlfw_send_soc_wake_msg(priv, QMI_WLFW_WAKE_REQUEST_V01); + if (!ret) + atomic_inc(&priv->soc_wake_ref_count); + + return ret; +} + +static int icnss_event_soc_wake_release(struct icnss_priv *priv, void *data) +{ + int ret = 0; + int count = 0; + + if (!priv) + return -ENODEV; + + count = atomic_dec_return(&priv->soc_wake_ref_count); + + if (count) { + icnss_pr_dbg("Wake release not called. Ref count: %d", + count); + return 0; + } + + ret = wlfw_send_soc_wake_msg(priv, QMI_WLFW_WAKE_RELEASE_V01); + + return ret; +} + static int icnss_driver_event_register_driver(struct icnss_priv *priv, void *data) { @@ -1225,6 +1349,68 @@ static void icnss_driver_event_work(struct work_struct *work) icnss_pm_relax(priv); } +static void icnss_soc_wake_msg_work(struct work_struct *work) +{ + struct icnss_priv *priv = + container_of(work, struct icnss_priv, soc_wake_msg_work); + struct icnss_soc_wake_event *event; + unsigned long flags; + int ret; + + icnss_pm_stay_awake(priv); + + spin_lock_irqsave(&priv->soc_wake_msg_lock, flags); + + while (!list_empty(&priv->soc_wake_msg_list)) { + event = list_first_entry(&priv->soc_wake_msg_list, + struct icnss_soc_wake_event, list); + list_del(&event->list); + spin_unlock_irqrestore(&priv->soc_wake_msg_lock, flags); + + icnss_pr_dbg("Processing event: %s%s(%d), state: 0x%lx\n", + icnss_soc_wake_event_to_str(event->type), + event->sync ? "-sync" : "", event->type, + priv->state); + + switch (event->type) { + case ICNSS_SOC_WAKE_REQUEST_EVENT: + ret = icnss_event_soc_wake_request(priv, + event->data); + break; + case ICNSS_SOC_WAKE_RELEASE_EVENT: + ret = icnss_event_soc_wake_release(priv, + event->data); + break; + default: + icnss_pr_err("Invalid Event type: %d", event->type); + kfree(event); + continue; + } + + priv->stats.soc_wake_events[event->type].processed++; + + icnss_pr_dbg("Event Processed: %s%s(%d), ret: %d, state: 0x%lx\n", + icnss_soc_wake_event_to_str(event->type), + event->sync ? "-sync" : "", event->type, ret, + priv->state); + + spin_lock_irqsave(&priv->soc_wake_msg_lock, flags); + if (event->sync) { + event->ret = ret; + complete(&event->complete); + continue; + } + spin_unlock_irqrestore(&priv->soc_wake_msg_lock, flags); + + kfree(event); + + spin_lock_irqsave(&priv->soc_wake_msg_lock, flags); + } + spin_unlock_irqrestore(&priv->soc_wake_msg_lock, flags); + + icnss_pm_relax(priv); +} + static int icnss_msa0_ramdump(struct icnss_priv *priv) { struct ramdump_segment segment; @@ -1613,6 +1799,113 @@ static int icnss_enable_recovery(struct icnss_priv *priv) return 0; } +static int icnss_tcdev_get_max_state(struct thermal_cooling_device *tcdev, + unsigned long *thermal_state) +{ + struct icnss_priv *priv = tcdev->devdata; + + *thermal_state = priv->max_thermal_state; + + return 0; +} + +static int icnss_tcdev_get_cur_state(struct thermal_cooling_device *tcdev, + unsigned long *thermal_state) +{ + struct icnss_priv *priv = tcdev->devdata; + + *thermal_state = priv->curr_thermal_state; + + return 0; +} + +static int icnss_tcdev_set_cur_state(struct thermal_cooling_device *tcdev, + unsigned long thermal_state) +{ + struct icnss_priv *priv = tcdev->devdata; + struct device *dev = &priv->pdev->dev; + int ret = 0; + + priv->curr_thermal_state = thermal_state; + + if (!priv->ops || !priv->ops->set_therm_state) + return 0; + + icnss_pr_vdbg("Cooling device set current state: %ld", + thermal_state); + + ret = priv->ops->set_therm_state(dev, thermal_state); + + if (ret) + icnss_pr_err("Setting Current Thermal State Failed: %d\n", ret); + + return 0; +} + +static struct thermal_cooling_device_ops icnss_cooling_ops = { + .get_max_state = icnss_tcdev_get_max_state, + .get_cur_state = icnss_tcdev_get_cur_state, + .set_cur_state = icnss_tcdev_set_cur_state, +}; + +int icnss_thermal_register(struct device *dev, unsigned long max_state) +{ + struct icnss_priv *priv = dev_get_drvdata(dev); + int ret = 0; + + priv->max_thermal_state = max_state; + + if (of_find_property(dev->of_node, "#cooling-cells", NULL)) { + priv->tcdev = thermal_of_cooling_device_register(dev->of_node, + "icnss", priv, + &icnss_cooling_ops); + if (IS_ERR_OR_NULL(priv->tcdev)) { + ret = PTR_ERR(priv->tcdev); + icnss_pr_err("Cooling device register failed: %d\n", + ret); + } else { + icnss_pr_vdbg("Cooling device registered"); + } + } else { + icnss_pr_dbg("Cooling device registration not supported"); + ret = -EOPNOTSUPP; + } + + return ret; +} +EXPORT_SYMBOL(icnss_thermal_register); + +void icnss_thermal_unregister(struct device *dev) +{ + struct icnss_priv *priv = dev_get_drvdata(dev); + + if (!IS_ERR_OR_NULL(priv->tcdev)) + thermal_cooling_device_unregister(priv->tcdev); + + priv->tcdev = NULL; +} +EXPORT_SYMBOL(icnss_thermal_unregister); + +int icnss_get_curr_therm_state(struct device *dev, + unsigned long *thermal_state) +{ + struct icnss_priv *priv = dev_get_drvdata(dev); + int ret = 0; + + if (IS_ERR_OR_NULL(priv->tcdev)) { + ret = PTR_ERR(priv->tcdev); + icnss_pr_err("Get current thermal state failed: %d\n", ret); + return ret; + } + + icnss_pr_vdbg("Cooling device current state: %ld", + priv->curr_thermal_state); + + *thermal_state = priv->curr_thermal_state; + return ret; +} +EXPORT_SYMBOL(icnss_get_curr_therm_state); + int icnss_qmi_send(struct device *dev, int type, void *cmd, int cmd_len, void *cb_ctx, int (*cb)(void *ctx, void *event, int event_len)) @@ -1963,6 +2256,71 @@ int icnss_set_fw_log_mode(struct device *dev, uint8_t fw_log_mode) } EXPORT_SYMBOL(icnss_set_fw_log_mode); +int icnss_force_wake_request(struct device *dev) +{ + struct icnss_priv *priv = dev_get_drvdata(dev); + int count = 0; + + if (!dev) + return -ENODEV; + + if (!priv) { + icnss_pr_err("Platform driver not initialized\n"); + return -EINVAL; + } + + icnss_pr_dbg("Calling SOC Wake request"); + + if (atomic_read(&priv->soc_wake_ref_count)) { + count = atomic_inc_return(&priv->soc_wake_ref_count); + icnss_pr_dbg("SOC already awake, Ref count: %d", count); + return 0; + } + + icnss_soc_wake_event_post(priv, ICNSS_SOC_WAKE_REQUEST_EVENT, + 0, NULL); + + return 0; +} +EXPORT_SYMBOL(icnss_force_wake_request); + +int icnss_force_wake_release(struct device *dev) +{ + struct icnss_priv *priv = dev_get_drvdata(dev); + + if (!dev) + return -ENODEV; + + if (!priv) { + icnss_pr_err("Platform driver not initialized\n"); + return -EINVAL; + } + + icnss_pr_dbg("Calling SOC Wake response"); + + icnss_soc_wake_event_post(priv, ICNSS_SOC_WAKE_RELEASE_EVENT, + 0, NULL); + + return 0; +} +EXPORT_SYMBOL(icnss_force_wake_release); + +int icnss_is_device_awake(struct device *dev) +{ + struct icnss_priv *priv = dev_get_drvdata(dev); + + if (!dev) + return -ENODEV; + + if (!priv) { + icnss_pr_err("Platform driver not initialized\n"); + return -EINVAL; + } + + return atomic_read(&priv->soc_wake_ref_count); +} +EXPORT_SYMBOL(icnss_is_device_awake); + int icnss_athdiag_read(struct device *dev, uint32_t offset, uint32_t mem_type, uint32_t data_len, uint8_t *output) @@ -2620,7 +2978,7 @@ static int icnss_probe(struct platform_device *pdev) if (!of_id || !of_id->data) { icnss_pr_err("Failed to find of match device!\n"); ret = -ENODEV; - goto out; + goto out_reset_drvdata; } device_id = of_id->data; @@ -2644,18 +3002,19 @@ static int icnss_probe(struct platform_device *pdev) ret = icnss_resource_parse(priv); if (ret) - goto out; + goto out_reset_drvdata; ret = icnss_msa_dt_parse(priv); if (ret) - goto out; + goto out_free_resources; ret = icnss_smmu_dt_parse(priv); if (ret) - goto out; + goto out_free_resources; spin_lock_init(&priv->event_lock); spin_lock_init(&priv->on_off_lock); + spin_lock_init(&priv->soc_wake_msg_lock); mutex_init(&priv->dev_lock); priv->event_wq = alloc_workqueue("icnss_driver_event", WQ_UNBOUND, 1); @@ -2668,10 +3027,21 @@ static int icnss_probe(struct platform_device *pdev) INIT_WORK(&priv->event_work, icnss_driver_event_work); INIT_LIST_HEAD(&priv->event_list); + priv->soc_wake_wq = alloc_workqueue("icnss_soc_wake_event", + WQ_UNBOUND, 1); + if (!priv->soc_wake_wq) { + icnss_pr_err("Soc wake Workqueue creation failed\n"); + ret = -EFAULT; + goto out_destroy_wq; + } + + INIT_WORK(&priv->soc_wake_msg_work, icnss_soc_wake_msg_work); + INIT_LIST_HEAD(&priv->soc_wake_msg_list); + ret = icnss_register_fw_service(priv); if (ret < 0) { icnss_pr_err("fw service registration failed: %d\n", ret); - goto out_destroy_wq; + goto out_destroy_soc_wq; } icnss_enable_recovery(priv); @@ -2689,21 +3059,26 @@ static int icnss_probe(struct platform_device *pdev) init_completion(&priv->unblock_shutdown); - ret = icnss_genl_init(); - if (ret < 0) - icnss_pr_err("ICNSS genl init failed %d\n", ret); + if (priv->device_id == WCN6750_DEVICE_ID) { + ret = icnss_genl_init(); + if (ret < 0) + icnss_pr_err("ICNSS genl init failed %d\n", ret); + } icnss_pr_info("Platform driver probed successfully\n"); return 0; +out_destroy_soc_wq: + destroy_workqueue(priv->soc_wake_wq); out_destroy_wq: destroy_workqueue(priv->event_wq); smmu_cleanup: priv->iommu_domain = NULL; -out: +out_free_resources: + icnss_put_resources(priv); +out_reset_drvdata: dev_set_drvdata(dev, NULL); - return ret; } @@ -2713,7 +3088,8 @@ static int icnss_remove(struct platform_device *pdev) icnss_pr_info("Removing driver: state: 0x%lx\n", priv->state); - icnss_genl_exit(); + if (priv->device_id == WCN6750_DEVICE_ID) + icnss_genl_exit(); device_init_wakeup(&priv->pdev->dev, false); @@ -2733,10 +3109,15 @@ static int icnss_remove(struct platform_device *pdev) if (priv->event_wq) destroy_workqueue(priv->event_wq); + if (priv->soc_wake_wq) + destroy_workqueue(priv->soc_wake_wq); + priv->iommu_domain = NULL; icnss_hw_power_off(priv); + icnss_put_resources(priv); + dev_set_drvdata(&pdev->dev, NULL); return 0; diff --git a/drivers/soc/qcom/icnss2/main.h b/drivers/soc/qcom/icnss2/main.h index cd5d6dd244f3aac78ca1bef7f5a996e937b1b2b7..19eae7c5e05d0e396e668fd10a2004548079465b 100644 --- a/drivers/soc/qcom/icnss2/main.h +++ b/drivers/soc/qcom/icnss2/main.h @@ -55,6 +55,12 @@ enum icnss_driver_event_type { ICNSS_DRIVER_EVENT_MAX, }; +enum icnss_soc_wake_event_type { + ICNSS_SOC_WAKE_REQUEST_EVENT, + ICNSS_SOC_WAKE_RELEASE_EVENT, + ICNSS_SOC_WAKE_EVENT_MAX, +}; + struct icnss_event_server_arrive_data { unsigned int node; unsigned int port; @@ -74,6 +80,15 @@ struct icnss_driver_event { void *data; }; +struct icnss_soc_wake_event { + struct list_head list; + enum icnss_soc_wake_event_type type; + bool sync; + struct completion complete; + int ret; + void *data; +}; + enum icnss_driver_state { ICNSS_WLFW_CONNECTED, ICNSS_POWER_ON, @@ -94,6 +109,7 @@ enum icnss_driver_state { ICNSS_MODE_ON, ICNSS_BLOCK_SHUTDOWN, ICNSS_PDR, + ICNSS_DEL_SERVER, }; struct ce_irq_list { @@ -149,6 +165,11 @@ struct icnss_stats { uint32_t processed; } events[ICNSS_DRIVER_EVENT_MAX]; + struct { + u32 posted; + u32 processed; + } soc_wake_events[ICNSS_SOC_WAKE_EVENT_MAX]; + struct { uint32_t request; uint32_t free; @@ -210,6 +231,9 @@ struct icnss_stats { u32 exit_power_save_req; u32 exit_power_save_resp; u32 exit_power_save_err; + u32 soc_wake_req; + u32 soc_wake_resp; + u32 soc_wake_err; }; #define WLFW_MAX_TIMESTAMP_LEN 32 @@ -282,10 +306,14 @@ struct icnss_priv { size_t smmu_iova_ipa_len; struct qmi_handle qmi; struct list_head event_list; + struct list_head soc_wake_msg_list; spinlock_t event_lock; + spinlock_t soc_wake_msg_lock; struct work_struct event_work; struct work_struct fw_recv_msg_work; + struct work_struct soc_wake_msg_work; struct workqueue_struct *event_wq; + struct workqueue_struct *soc_wake_wq; phys_addr_t msa_pa; phys_addr_t msi_addr_pa; dma_addr_t msi_addr_iova; @@ -342,6 +370,10 @@ struct icnss_priv { struct icnss_fw_mem qdss_mem[QMI_WLFW_MAX_NUM_MEM_SEG]; void *get_info_cb_ctx; int (*get_info_cb)(void *ctx, void *event, int event_len); + atomic_t soc_wake_ref_count; + struct thermal_cooling_device *tcdev; + unsigned long curr_thermal_state; + unsigned long max_thermal_state; }; struct icnss_reg_info { @@ -358,5 +390,9 @@ int icnss_driver_event_post(struct icnss_priv *priv, u32 flags, void *data); void icnss_allow_recursive_recovery(struct device *dev); void icnss_disallow_recursive_recovery(struct device *dev); +char *icnss_soc_wake_event_to_str(enum icnss_soc_wake_event_type type); +int icnss_soc_wake_event_post(struct icnss_priv *priv, + enum icnss_soc_wake_event_type type, + u32 flags, void *data); #endif diff --git a/drivers/soc/qcom/icnss2/power.c b/drivers/soc/qcom/icnss2/power.c index 8ed9f9851237007b260f02cb93da90c7d2f97e30..97a71d122f33674098a5f834b163e88feff2f9ca 100644 --- a/drivers/soc/qcom/icnss2/power.c +++ b/drivers/soc/qcom/icnss2/power.c @@ -12,22 +12,10 @@ #include "qmi.h" #include "debug.h" -static struct icnss_vreg_cfg icnss_vreg_list[] = { - {"vdd-wlan-core", 1300000, 1300000, 0, 0, 0, false}, - {"vdd-wlan-io", 1800000, 1800000, 0, 0, 0, false}, - {"vdd-wlan-xtal-aon", 0, 0, 0, 0, 0, false}, - {"vdd-wlan-xtal", 1800000, 1800000, 0, 2, 0, false}, - {"vdd-wlan", 0, 0, 0, 0, 0, false}, - {"vdd-wlan-ctrl1", 0, 0, 0, 0, 0, false}, - {"vdd-wlan-ctrl2", 0, 0, 0, 0, 0, false}, - {"vdd-wlan-sp2t", 2700000, 2700000, 0, 0, 0, false}, - {"wlan-ant-switch", 1800000, 1800000, 0, 0, 0, false}, - {"wlan-soc-swreg", 1200000, 1200000, 0, 0, 0, false}, - {"vdd-wlan-aon", 950000, 950000, 0, 0, 0, false}, - {"vdd-wlan-dig", 950000, 952000, 0, 0, 0, false}, - {"vdd-wlan-rfa1", 1900000, 1900000, 0, 0, 0, false}, - {"vdd-wlan-rfa2", 1350000, 1350000, 0, 0, 0, false}, - {"vdd-wlan-en", 0, 0, 0, 10, 0, false}, +static struct icnss_vreg_cfg icnss_wcn6750_vreg_list[] = { + {"vdd-cx-mx", 824000, 952000, 0, 0, 0, false}, + {"vdd-1.8-xo", 1872000, 1872000, 0, 0, 0, false}, + {"vdd-1.3-rfa", 1256000, 1352000, 0, 0, 0, false}, }; static struct icnss_vreg_cfg icnss_adrestea_vreg_list[] = { @@ -46,7 +34,7 @@ static struct icnss_clk_cfg icnss_adrestea_clk_list[] = { {"cxo_ref_clk_pin", 0, 0}, }; -#define ICNSS_VREG_LIST_SIZE ARRAY_SIZE(icnss_vreg_list) +#define ICNSS_VREG_LIST_SIZE ARRAY_SIZE(icnss_wcn6750_vreg_list) #define ICNSS_VREG_ADRESTEA_LIST_SIZE ARRAY_SIZE(icnss_adrestea_vreg_list) #define ICNSS_CLK_LIST_SIZE ARRAY_SIZE(icnss_clk_list) #define ICNSS_CLK_ADRESTEA_LIST_SIZE ARRAY_SIZE(icnss_adrestea_clk_list) @@ -150,16 +138,6 @@ static int icnss_get_vreg_single(struct icnss_priv *priv, return ret; } -static void icnss_put_vreg_single(struct icnss_priv *priv, - struct icnss_vreg_info *vreg) -{ - struct device *dev = &priv->pdev->dev; - - icnss_pr_dbg("Put regulator: %s\n", vreg->cfg.name); - devm_regulator_put(vreg->reg); - devm_kfree(dev, vreg); -} - static int icnss_vreg_on_single(struct icnss_vreg_info *vreg) { int ret = 0; @@ -286,7 +264,7 @@ static struct icnss_vreg_cfg *get_vreg_list(u32 *vreg_list_size, switch (device_id) { case WCN6750_DEVICE_ID: *vreg_list_size = ICNSS_VREG_LIST_SIZE; - return icnss_vreg_list; + return icnss_wcn6750_vreg_list; case ADRASTEA_DEVICE_ID: *vreg_list_size = ICNSS_VREG_ADRESTEA_LIST_SIZE; @@ -321,13 +299,10 @@ int icnss_get_vreg(struct icnss_priv *priv) memcpy(&vreg->cfg, &vreg_cfg[i], sizeof(vreg->cfg)); ret = icnss_get_vreg_single(priv, vreg); if (ret != 0) { - if (ret == -ENODEV) { - devm_kfree(dev, vreg); + if (ret == -ENODEV) continue; - } else { - devm_kfree(dev, vreg); + else return ret; - } } list_add_tail(&vreg->list, vreg_list); } @@ -344,9 +319,6 @@ void icnss_put_vreg(struct icnss_priv *priv) vreg = list_first_entry(vreg_list, struct icnss_vreg_info, list); list_del(&vreg->list); - if (IS_ERR_OR_NULL(vreg->reg)) - continue; - icnss_put_vreg_single(priv, vreg); } } @@ -434,15 +406,6 @@ int icnss_get_clk_single(struct icnss_priv *priv, return 0; } -static void icnss_put_clk_single(struct icnss_priv *priv, - struct icnss_clk_info *clk_info) -{ - struct device *dev = &priv->pdev->dev; - - icnss_pr_dbg("Put clock: %s\n", clk_info->cfg.name); - devm_clk_put(dev, clk_info->clk); -} - static int icnss_clk_on_single(struct icnss_clk_info *clk_info) { int ret; @@ -532,13 +495,10 @@ int icnss_get_clk(struct icnss_priv *priv) sizeof(clk_info->cfg)); ret = icnss_get_clk_single(priv, clk_info); if (ret != 0) { - if (clk_info->cfg.required) { - devm_kfree(dev, clk_info); + if (clk_info->cfg.required) goto cleanup; - } else { - devm_kfree(dev, clk_info); + else continue; - } } list_add_tail(&clk_info->list, clk_list); } @@ -550,10 +510,6 @@ int icnss_get_clk(struct icnss_priv *priv) clk_info = list_first_entry(clk_list, struct icnss_clk_info, list); list_del(&clk_info->list); - if (IS_ERR_OR_NULL(clk_info->clk)) - continue; - icnss_put_clk_single(priv, clk_info); - devm_kfree(dev, clk_info); } return ret; @@ -575,10 +531,6 @@ void icnss_put_clk(struct icnss_priv *priv) clk_info = list_first_entry(clk_list, struct icnss_clk_info, list); list_del(&clk_info->list); - if (IS_ERR_OR_NULL(clk_info->clk)) - continue; - icnss_put_clk_single(priv, clk_info); - devm_kfree(dev, clk_info); } } diff --git a/drivers/soc/qcom/icnss2/qmi.c b/drivers/soc/qcom/icnss2/qmi.c index 3a961316448c80c3860c876484ecc54cba264736..6223d73f12c026df6f201bd0623dde405a0ad1f9 100644 --- a/drivers/soc/qcom/icnss2/qmi.c +++ b/drivers/soc/qcom/icnss2/qmi.c @@ -413,6 +413,82 @@ int wlfw_exit_power_save_send_msg(struct icnss_priv *priv) return ret; } +int wlfw_send_soc_wake_msg(struct icnss_priv *priv, + enum wlfw_soc_wake_enum_v01 type) +{ + int ret; + struct wlfw_soc_wake_req_msg_v01 *req; + struct wlfw_soc_wake_resp_msg_v01 *resp; + struct qmi_txn txn; + + if (!priv) + return -ENODEV; + + if (test_bit(ICNSS_FW_DOWN, &priv->state)) + return -EINVAL; + + icnss_pr_dbg("Sending soc wake msg, type: 0x%x\n", + type); + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + resp = kzalloc(sizeof(*resp), GFP_KERNEL); + if (!resp) { + kfree(req); + return -ENOMEM; + } + req->wake_valid = 1; + req->wake = type; + + priv->stats.soc_wake_req++; + + ret = qmi_txn_init(&priv->qmi, &txn, + wlfw_soc_wake_resp_msg_v01_ei, resp); + + if (ret < 0) { + icnss_pr_err("Fail to init txn for wake msg resp %d\n", + ret); + goto out; + } + + ret = qmi_send_request(&priv->qmi, NULL, &txn, + QMI_WLFW_SOC_WAKE_REQ_V01, + WLFW_SOC_WAKE_REQ_MSG_V01_MAX_MSG_LEN, + wlfw_soc_wake_req_msg_v01_ei, req); + if (ret < 0) { + qmi_txn_cancel(&txn); + icnss_pr_err("Fail to send soc wake msg %d\n", ret); + goto out; + } + + ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout); + if (ret < 0) { + icnss_qmi_fatal_err("SOC wake timed out with ret %d\n", + ret); + goto out; + } else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) { + icnss_qmi_fatal_err( + "SOC wake request rejected,result:%d error:%d\n", + resp->resp.result, resp->resp.error); + ret = -resp->resp.result; + goto out; + } + + priv->stats.soc_wake_resp++; + + kfree(resp); + kfree(req); + return 0; + +out: + kfree(req); + kfree(resp); + priv->stats.soc_wake_err++; + return ret; +} + int wlfw_ind_register_send_sync_msg(struct icnss_priv *priv) { int ret; @@ -533,7 +609,8 @@ int wlfw_cap_send_sync_msg(struct icnss_priv *priv) if (!priv) return -ENODEV; - icnss_pr_dbg("Sending capability message, state: 0x%lx\n", priv->state); + icnss_pr_dbg("Sending target capability message, state: 0x%lx\n", + priv->state); req = kzalloc(sizeof(*req), GFP_KERNEL); if (!req) @@ -1220,6 +1297,8 @@ int wlfw_athdiag_write_send_sync_msg(struct icnss_priv *priv, resp->resp.result, resp->resp.error); ret = -resp->resp.result; goto out; + } else { + ret = 0; } out: @@ -1857,12 +1936,24 @@ int icnss_connect_to_fw_server(struct icnss_priv *priv, void *data) int icnss_clear_server(struct icnss_priv *priv) { + int ret; + if (!priv) return -ENODEV; icnss_pr_info("QMI Service Disconnected: 0x%lx\n", priv->state); clear_bit(ICNSS_WLFW_CONNECTED, &priv->state); + icnss_unregister_fw_service(priv); + + clear_bit(ICNSS_DEL_SERVER, &priv->state); + + ret = icnss_register_fw_service(priv); + if (ret < 0) { + icnss_pr_err("WLFW server registration failed\n"); + ICNSS_ASSERT(0); + } + return 0; } @@ -1873,6 +1964,12 @@ static int wlfw_new_server(struct qmi_handle *qmi, container_of(qmi, struct icnss_priv, qmi); struct icnss_event_server_arrive_data *event_data; + if (priv && test_bit(ICNSS_DEL_SERVER, &priv->state)) { + icnss_pr_info("WLFW server delete in progress, Ignore server arrive: 0x%lx\n", + priv->state); + return 0; + } + icnss_pr_dbg("WLFW server arrive: node %u port %u\n", service->node, service->port); @@ -1894,9 +1991,16 @@ static void wlfw_del_server(struct qmi_handle *qmi, { struct icnss_priv *priv = container_of(qmi, struct icnss_priv, qmi); + if (priv && test_bit(ICNSS_DEL_SERVER, &priv->state)) { + icnss_pr_info("WLFW server delete in progress, Ignore server delete: 0x%lx\n", + priv->state); + return; + } + icnss_pr_dbg("WLFW server delete\n"); if (priv) { + set_bit(ICNSS_DEL_SERVER, &priv->state); set_bit(ICNSS_FW_DOWN, &priv->state); icnss_ignore_fw_timeout(true); } @@ -2103,6 +2207,18 @@ int icnss_send_vbatt_update(struct icnss_priv *priv, uint64_t voltage_uv) return ret; } +#ifdef CONFIG_ICNSS2_DEBUG +static inline u32 icnss_get_host_build_type(void) +{ + return QMI_HOST_BUILD_TYPE_PRIMARY_V01; +} +#else +static inline u32 icnss_get_host_build_type(void) +{ + return QMI_HOST_BUILD_TYPE_SECONDARY_V01; +} +#endif + int wlfw_host_cap_send_sync(struct icnss_priv *priv) { struct wlfw_host_cap_req_msg_v01 *req; @@ -2138,6 +2254,9 @@ int wlfw_host_cap_send_sync(struct icnss_priv *priv) req->cal_done = priv->cal_done; icnss_pr_dbg("Calibration done is %d\n", priv->cal_done); + req->host_build_type_valid = 1; + req->host_build_type = icnss_get_host_build_type(); + ret = qmi_txn_init(&priv->qmi, &txn, wlfw_host_cap_resp_msg_v01_ei, resp); if (ret < 0) { @@ -2196,7 +2315,7 @@ int icnss_wlfw_get_info_send_sync(struct icnss_priv *plat_priv, int type, if (cmd_len > QMI_WLFW_MAX_DATA_SIZE_V01) return -EINVAL; - if (test_bit(ICNSS_FW_DOWN, &priv->state)) + if (test_bit(ICNSS_FW_DOWN, &plat_priv->state)) return -EINVAL; req = kzalloc(sizeof(*req), GFP_KERNEL); diff --git a/drivers/soc/qcom/icnss2/qmi.h b/drivers/soc/qcom/icnss2/qmi.h index c4c42cec5d4c7a2ae07c87a9bc0bfae700d7a4c5..f4c1d2b263f1e03ab199c2716be7301dd7c7634e 100644 --- a/drivers/soc/qcom/icnss2/qmi.h +++ b/drivers/soc/qcom/icnss2/qmi.h @@ -139,6 +139,12 @@ int icnss_wlfw_get_info_send_sync(struct icnss_priv *priv, int type, { return 0; } + +int wlfw_send_soc_wake_msg(struct icnss_priv *priv, + enum wlfw_soc_wake_enum_v01 type) +{ + return 0; +} #else int wlfw_ind_register_send_sync_msg(struct icnss_priv *priv); int icnss_connect_to_fw_server(struct icnss_priv *priv, void *data); @@ -177,6 +183,8 @@ int wlfw_qdss_trace_mem_info_send_sync(struct icnss_priv *priv); int wlfw_exit_power_save_send_msg(struct icnss_priv *priv); int icnss_wlfw_get_info_send_sync(struct icnss_priv *priv, int type, void *cmd, int cmd_len); +int wlfw_send_soc_wake_msg(struct icnss_priv *priv, + enum wlfw_soc_wake_enum_v01 type); #endif #endif /* __ICNSS_QMI_H__*/ diff --git a/drivers/soc/qcom/icnss_private.h b/drivers/soc/qcom/icnss_private.h index e864a0b428603aeff89ecf3f388d18ea1fec849b..45ad71a3c6362890786bf1d56ac8ed2eae02f7d7 100644 --- a/drivers/soc/qcom/icnss_private.h +++ b/drivers/soc/qcom/icnss_private.h @@ -157,6 +157,7 @@ enum icnss_driver_state { ICNSS_MODE_ON, ICNSS_BLOCK_SHUTDOWN, ICNSS_PDR, + ICNSS_DEL_SERVER, }; struct ce_irq_list { diff --git a/drivers/soc/qcom/icnss_qmi.c b/drivers/soc/qcom/icnss_qmi.c index 3141600156eadadbd6ea8bb00fda34394882fb4f..1f73ae4420c1eee516206a6bc5d25e28c696951e 100644 --- a/drivers/soc/qcom/icnss_qmi.c +++ b/drivers/soc/qcom/icnss_qmi.c @@ -1265,6 +1265,8 @@ int icnss_clear_server(struct icnss_priv *priv) icnss_unregister_fw_service(priv); + clear_bit(ICNSS_DEL_SERVER, &priv->state); + ret = icnss_register_fw_service(priv); if (ret < 0) { icnss_pr_err("WLFW server registration failed\n"); @@ -1277,8 +1279,15 @@ int icnss_clear_server(struct icnss_priv *priv) static int wlfw_new_server(struct qmi_handle *qmi, struct qmi_service *service) { + struct icnss_priv *priv = container_of(qmi, struct icnss_priv, qmi); struct icnss_event_server_arrive_data *event_data; + if (priv && test_bit(ICNSS_DEL_SERVER, &priv->state)) { + icnss_pr_info("WLFW server delete in progress, Ignore server arrive: 0x%lx\n", + priv->state); + return 0; + } + icnss_pr_dbg("WLFW server arrive: node %u port %u\n", service->node, service->port); @@ -1300,9 +1309,16 @@ static void wlfw_del_server(struct qmi_handle *qmi, { struct icnss_priv *priv = container_of(qmi, struct icnss_priv, qmi); + if (priv && test_bit(ICNSS_DEL_SERVER, &priv->state)) { + icnss_pr_info("WLFW server delete in progress, Ignore server delete: 0x%lx\n", + priv->state); + return; + } + icnss_pr_dbg("WLFW server delete\n"); if (priv) { + set_bit(ICNSS_DEL_SERVER, &priv->state); set_bit(ICNSS_FW_DOWN, &priv->state); icnss_ignore_fw_timeout(true); } diff --git a/drivers/soc/qcom/minidump_log.c b/drivers/soc/qcom/minidump_log.c index 87b72ed3856895f5a38be62a071b4fd94cffce44..c0ef1be1e1fd0277a8c37df3610a227ca53cc02f 100644 --- a/drivers/soc/qcom/minidump_log.c +++ b/drivers/soc/qcom/minidump_log.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -161,10 +162,47 @@ void dump_stack_minidump(u64 sp) pr_err("Failed to add current task %d in Minidump\n", cpu); } +#ifdef CONFIG_ARM64 +static void register_irq_stack(void) +{ + int cpu; + unsigned int i; + int irq_stack_pages_count; + u64 irq_stack_base; + struct md_region irq_sp_entry; + u64 sp; + + for_each_possible_cpu(cpu) { + irq_stack_base = (u64)per_cpu(irq_stack_ptr, cpu); + if (IS_ENABLED(CONFIG_VMAP_STACK)) { + irq_stack_pages_count = IRQ_STACK_SIZE / PAGE_SIZE; + sp = irq_stack_base & ~(PAGE_SIZE - 1); + for (i = 0; i < irq_stack_pages_count; i++) { + scnprintf(irq_sp_entry.name, + sizeof(irq_sp_entry.name), + "KISTACK%d_%d", cpu, i); + register_stack_entry(&irq_sp_entry, sp, + PAGE_SIZE, cpu); + sp += PAGE_SIZE; + } + } else { + sp = irq_stack_base; + scnprintf(irq_sp_entry.name, sizeof(irq_sp_entry.name), + "KISTACK%d", cpu); + register_stack_entry(&irq_sp_entry, sp, IRQ_STACK_SIZE, + cpu); + } + } +} +#else +static inline void register_irq_stack(void) {} +#endif + static int __init msm_minidump_log_init(void) { register_kernel_sections(); + register_irq_stack(); register_log_buf(); return 0; } -late_initcall(msm_minidump_log_init); +subsys_initcall(msm_minidump_log_init); diff --git a/drivers/soc/qcom/peripheral-loader.c b/drivers/soc/qcom/peripheral-loader.c index 6fa278f9997d6cb3f3818371ca2e058e27d77459..5c55fb3189046d2d7572ab8cadd8846149650619 100644 --- a/drivers/soc/qcom/peripheral-loader.c +++ b/drivers/soc/qcom/peripheral-loader.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2010-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2010-2020, The Linux Foundation. All rights reserved. */ #include @@ -134,7 +134,7 @@ struct pil_seg { */ struct pil_priv { struct delayed_work proxy; - struct wakeup_source ws; + struct wakeup_source *ws; char wname[32]; struct pil_desc *desc; int num_segs; @@ -413,6 +413,33 @@ static int pil_do_minidump(struct pil_desc *desc, void *ramdump_dev) return ret; } +/** + * print_aux_minidump_tocs() - Print the ToC for an auxiliary minidump entry + * @desc: PIL descriptor for the subsystem for which minidump is collected + * + * Prints out the table of contents(ToC) for all of the auxiliary + * minidump entries for a subsystem. + */ +static void print_aux_minidump_tocs(struct pil_desc *desc) +{ + int i; + struct md_ss_toc *toc; + + for (i = 0; i < desc->num_aux_minidump_ids; i++) { + toc = desc->aux_minidump[i]; + pr_debug("Minidump : md_aux_toc->toc_init 0x%x\n", + (unsigned int)toc->md_ss_toc_init); + pr_debug("Minidump : md_aux_toc->enable_status 0x%x\n", + (unsigned int)toc->md_ss_enable_status); + pr_debug("Minidump : md_aux_toc->encryption_status 0x%x\n", + (unsigned int)toc->encryption_status); + pr_debug("Minidump : md_aux_toc->ss_region_count 0x%x\n", + (unsigned int)toc->ss_region_count); + pr_debug("Minidump : md_aux_toc->smem_regions_baseptr 0x%x\n", + (unsigned int)toc->md_ss_smem_regions_baseptr); + } +} + /** * pil_do_ramdump() - Ramdump an image * @desc: descriptor from pil_desc_init() @@ -441,6 +468,9 @@ int pil_do_ramdump(struct pil_desc *desc, pr_debug("Minidump : md_ss_toc->md_ss_smem_regions_baseptr is 0x%x\n", (unsigned int) desc->minidump_ss->md_ss_smem_regions_baseptr); + + print_aux_minidump_tocs(desc); + /** * Collect minidump if SS ToC is valid and segment table * is initialized in memory and encryption status is set. @@ -581,7 +611,7 @@ static void __pil_proxy_unvote(struct pil_priv *priv) desc->ops->proxy_unvote(desc); notify_proxy_unvote(desc->dev); - __pm_relax(&priv->ws); + __pm_relax(priv->ws); module_put(desc->owner); } @@ -600,10 +630,10 @@ static int pil_proxy_vote(struct pil_desc *desc) struct pil_priv *priv = desc->priv; if (desc->ops->proxy_vote) { - __pm_stay_awake(&priv->ws); + __pm_stay_awake(priv->ws); ret = desc->ops->proxy_vote(desc); if (ret) - __pm_relax(&priv->ws); + __pm_relax(priv->ws); } if (desc->proxy_unvote_irq) @@ -1305,7 +1335,7 @@ int pil_boot(struct pil_desc *desc) * Fallback to serial loading of blobs if the * workqueue creatation failed during module init. */ - if (pil_wq) { + if (pil_wq && !(desc->sequential_loading)) { ret = pil_load_segs(desc); if (ret) goto err_deinit_image; @@ -1561,7 +1591,13 @@ int pil_desc_init(struct pil_desc *desc) } snprintf(priv->wname, sizeof(priv->wname), "pil-%s", desc->name); - wakeup_source_init(&priv->ws, priv->wname); + + priv->ws = wakeup_source_register(desc->dev, priv->wname); + if (!priv->ws) { + ret = -ENOMEM; + goto err; + } + INIT_DELAYED_WORK(&priv->proxy, pil_proxy_unvote_work); INIT_LIST_HEAD(&priv->segs); @@ -1597,7 +1633,7 @@ void pil_desc_release(struct pil_desc *desc) if (priv) { ida_simple_remove(&pil_ida, priv->id); flush_delayed_work(&priv->proxy); - wakeup_source_trash(&priv->ws); + wakeup_source_unregister(priv->ws); } desc->priv = NULL; kfree(priv); diff --git a/drivers/soc/qcom/peripheral-loader.h b/drivers/soc/qcom/peripheral-loader.h index c83b038f9b8cb9d9b9e7ff5f6c09a994ce29cf02..29fa4b612667178778ca2ea624a533d26c184155 100644 --- a/drivers/soc/qcom/peripheral-loader.h +++ b/drivers/soc/qcom/peripheral-loader.h @@ -63,6 +63,7 @@ struct pil_desc { bool shutdown_fail; bool modem_ssr; bool clear_fw_region; + bool sequential_loading; u32 subsys_vmid; bool signal_aop; struct mbox_client cl; diff --git a/drivers/soc/qcom/pil-msa.c b/drivers/soc/qcom/pil-msa.c new file mode 100644 index 0000000000000000000000000000000000000000..9cff905d3e43b5bcf940df81338e29d085b7f887 --- /dev/null +++ b/drivers/soc/qcom/pil-msa.c @@ -0,0 +1,1051 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "peripheral-loader.h" +#include "pil-q6v5.h" +#include "pil-msa.h" + +/* Q6 Register Offsets */ +#define QDSP6SS_RST_EVB 0x010 +#define QDSP6SS_DBG_CFG 0x018 +#define QDSP6SS_NMI_CFG 0x40 + +/* AXI Halting Registers */ +#define MSS_Q6_HALT_BASE 0x180 +#define MSS_MODEM_HALT_BASE 0x200 +#define MSS_NC_HALT_BASE 0x280 + +/* RMB Status Register Values */ +#define STATUS_PBL_SUCCESS 0x1 +#define STATUS_XPU_UNLOCKED 0x1 +#define STATUS_XPU_UNLOCKED_SCRIBBLED 0x2 + +/* PBL/MBA interface registers */ +#define RMB_MBA_IMAGE 0x00 +#define RMB_PBL_STATUS 0x04 +#define RMB_MBA_COMMAND 0x08 +#define RMB_MBA_STATUS 0x0C +#define RMB_PMI_META_DATA 0x10 +#define RMB_PMI_CODE_START 0x14 +#define RMB_PMI_CODE_LENGTH 0x18 +#define RMB_PROTOCOL_VERSION 0x1C +#define RMB_MBA_DEBUG_INFORMATION 0x20 + +#define POLL_INTERVAL_US 50 + +#define CMD_META_DATA_READY 0x1 +#define CMD_LOAD_READY 0x2 +#define CMD_PILFAIL_NFY_MBA 0xffffdead + +#define STATUS_META_DATA_AUTH_SUCCESS 0x3 +#define STATUS_AUTH_COMPLETE 0x4 +#define STATUS_MBA_UNLOCKED 0x6 + +/* External BHS */ +#define EXTERNAL_BHS_ON BIT(0) +#define EXTERNAL_BHS_STATUS BIT(4) +#define BHS_TIMEOUT_US 50 + +#define MSS_RESTART_PARAM_ID 0x2 +#define MSS_RESTART_ID 0xA + +#define MSS_MAGIC 0XAABADEAD + +/* Timeout value for MBA boot when minidump is enabled */ +#define MBA_ENCRYPTION_TIMEOUT 3000 +enum scm_cmd { + PAS_MEM_SETUP_CMD = 2, +}; + +static int pbl_mba_boot_timeout_ms = 1000; +module_param(pbl_mba_boot_timeout_ms, int, 0644); + +static int modem_auth_timeout_ms = 10000; +module_param(modem_auth_timeout_ms, int, 0644); + +/* If set to 0xAABADEAD, MBA failures trigger a kernel panic */ +static uint modem_trigger_panic; +module_param(modem_trigger_panic, uint, 0644); + +/* To set the modem debug cookie in DBG_CFG register for debugging */ +static uint modem_dbg_cfg; +module_param(modem_dbg_cfg, uint, 0644); + +static void modem_log_rmb_regs(void __iomem *base) +{ + pr_err("RMB_MBA_IMAGE: %08x\n", readl_relaxed(base + RMB_MBA_IMAGE)); + pr_err("RMB_PBL_STATUS: %08x\n", readl_relaxed(base + RMB_PBL_STATUS)); + pr_err("RMB_MBA_COMMAND: %08x\n", + readl_relaxed(base + RMB_MBA_COMMAND)); + pr_err("RMB_MBA_STATUS: %08x\n", readl_relaxed(base + RMB_MBA_STATUS)); + pr_err("RMB_PMI_META_DATA: %08x\n", + readl_relaxed(base + RMB_PMI_META_DATA)); + pr_err("RMB_PMI_CODE_START: %08x\n", + readl_relaxed(base + RMB_PMI_CODE_START)); + pr_err("RMB_PMI_CODE_LENGTH: %08x\n", + readl_relaxed(base + RMB_PMI_CODE_LENGTH)); + pr_err("RMB_PROTOCOL_VERSION: %08x\n", + readl_relaxed(base + RMB_PROTOCOL_VERSION)); + pr_err("RMB_MBA_DEBUG_INFORMATION: %08x\n", + readl_relaxed(base + RMB_MBA_DEBUG_INFORMATION)); + + if (modem_trigger_panic == MSS_MAGIC) + panic("%s: System ramdump is needed!!!\n", __func__); +} + +static int pil_mss_power_up(struct q6v5_data *drv) +{ + int ret = 0; + u32 regval; + + if (drv->cxrail_bhs) { + regval = readl_relaxed(drv->cxrail_bhs); + regval |= EXTERNAL_BHS_ON; + writel_relaxed(regval, drv->cxrail_bhs); + + ret = readl_poll_timeout(drv->cxrail_bhs, regval, + regval & EXTERNAL_BHS_STATUS, 1, BHS_TIMEOUT_US); + } + + return ret; +} + +static int pil_mss_power_down(struct q6v5_data *drv) +{ + u32 regval; + + if (drv->cxrail_bhs) { + regval = readl_relaxed(drv->cxrail_bhs); + regval &= ~EXTERNAL_BHS_ON; + writel_relaxed(regval, drv->cxrail_bhs); + } + + return 0; +} + +static int pil_mss_enable_clks(struct q6v5_data *drv) +{ + int ret; + + ret = clk_prepare_enable(drv->ahb_clk); + if (ret) + goto err_ahb_clk; + ret = clk_prepare_enable(drv->axi_clk); + if (ret) + goto err_axi_clk; + ret = clk_prepare_enable(drv->rom_clk); + if (ret) + goto err_rom_clk; + ret = clk_prepare_enable(drv->gpll0_mss_clk); + if (ret) + goto err_gpll0_mss_clk; + ret = clk_prepare_enable(drv->snoc_axi_clk); + if (ret) + goto err_snoc_axi_clk; + ret = clk_prepare_enable(drv->mnoc_axi_clk); + if (ret) + goto err_mnoc_axi_clk; + return 0; +err_mnoc_axi_clk: + clk_disable_unprepare(drv->mnoc_axi_clk); +err_snoc_axi_clk: + clk_disable_unprepare(drv->snoc_axi_clk); +err_gpll0_mss_clk: + clk_disable_unprepare(drv->gpll0_mss_clk); +err_rom_clk: + clk_disable_unprepare(drv->rom_clk); +err_axi_clk: + clk_disable_unprepare(drv->axi_clk); +err_ahb_clk: + clk_disable_unprepare(drv->ahb_clk); + return ret; +} + +static void pil_mss_disable_clks(struct q6v5_data *drv) +{ + clk_disable_unprepare(drv->mnoc_axi_clk); + clk_disable_unprepare(drv->snoc_axi_clk); + clk_disable_unprepare(drv->gpll0_mss_clk); + clk_disable_unprepare(drv->rom_clk); + clk_disable_unprepare(drv->axi_clk); + if (!drv->ahb_clk_vote) + clk_disable_unprepare(drv->ahb_clk); +} + +static void pil_mss_pdc_sync(struct q6v5_data *drv, bool pdc_sync) +{ + u32 val = 0; + u32 mss_pdc_mask = BIT(drv->mss_pdc_offset); + + if (drv->pdc_sync) { + val = readl_relaxed(drv->pdc_sync); + if (pdc_sync) + val |= mss_pdc_mask; + else + val &= ~mss_pdc_mask; + writel_relaxed(val, drv->pdc_sync); + /* Ensure PDC is written before next write */ + wmb(); + udelay(2); + } +} + +static void pil_mss_alt_reset(struct q6v5_data *drv, u32 val) +{ + if (drv->alt_reset) { + writel_relaxed(val, drv->alt_reset); + /* Ensure alt reset is written before restart reg */ + wmb(); + udelay(2); + } +} + +static int pil_mss_restart_reg(struct q6v5_data *drv, u32 mss_restart) +{ + int ret = 0; + int scm_ret = 0; + struct scm_desc desc = {0}; + + desc.args[0] = mss_restart; + desc.args[1] = 0; + desc.arginfo = SCM_ARGS(2); + + if (drv->restart_reg && !drv->restart_reg_sec) { + writel_relaxed(mss_restart, drv->restart_reg); + /* Ensure physical address access is done before returning.*/ + mb(); + udelay(2); + } else if (drv->restart_reg_sec) { + ret = scm_call2(SCM_SIP_FNID(SCM_SVC_PIL, + MSS_RESTART_ID), &desc); + scm_ret = desc.ret[0]; + if (ret || scm_ret) + pr_err("Secure MSS restart failed\n"); + } + + return ret; +} + +int pil_mss_assert_resets(struct q6v5_data *drv) +{ + int ret = 0; + + pil_mss_pdc_sync(drv, 1); + pil_mss_alt_reset(drv, 1); + if (drv->reset_clk) { + pil_mss_disable_clks(drv); + if (drv->ahb_clk_vote) + clk_disable_unprepare(drv->ahb_clk); + } + + ret = pil_mss_restart_reg(drv, true); + + return ret; +} + +int pil_mss_deassert_resets(struct q6v5_data *drv) +{ + int ret = 0; + + ret = pil_mss_restart_reg(drv, 0); + if (ret) + return ret; + /* Wait 6 32kHz sleep cycles for reset */ + udelay(200); + + if (drv->reset_clk) + pil_mss_enable_clks(drv); + pil_mss_alt_reset(drv, 0); + pil_mss_pdc_sync(drv, false); + + return ret; +} + +static int pil_msa_wait_for_mba_ready(struct q6v5_data *drv) +{ + struct device *dev = drv->desc.dev; + int ret; + u32 status; + u64 val; + + if (of_property_read_bool(dev->of_node, "qcom,minidump-id")) + pbl_mba_boot_timeout_ms = MBA_ENCRYPTION_TIMEOUT; + + val = is_timeout_disabled() ? 0 : pbl_mba_boot_timeout_ms * 1000; + + /* Wait for PBL completion. */ + ret = readl_poll_timeout(drv->rmb_base + RMB_PBL_STATUS, status, + status != 0, POLL_INTERVAL_US, val); + if (ret) { + dev_err(dev, "PBL boot timed out (rc:%d)\n", ret); + return ret; + } + if (status != STATUS_PBL_SUCCESS) { + dev_err(dev, "PBL returned unexpected status %d\n", status); + return -EINVAL; + } + + /* Wait for MBA completion. */ + ret = readl_poll_timeout(drv->rmb_base + RMB_MBA_STATUS, status, + status != 0, POLL_INTERVAL_US, val); + if (ret) { + dev_err(dev, "MBA boot timed out (rc:%d)\n", ret); + return ret; + } + if (status != STATUS_XPU_UNLOCKED && + status != STATUS_XPU_UNLOCKED_SCRIBBLED) { + dev_err(dev, "MBA returned unexpected status %d\n", status); + return -EINVAL; + } + + return 0; +} + +int pil_mss_shutdown(struct pil_desc *pil) +{ + struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc); + int ret = 0; + + if (drv->axi_halt_base) { + pil_q6v5_halt_axi_port(pil, + drv->axi_halt_base + MSS_Q6_HALT_BASE); + pil_q6v5_halt_axi_port(pil, + drv->axi_halt_base + MSS_MODEM_HALT_BASE); + pil_q6v5_halt_axi_port(pil, + drv->axi_halt_base + MSS_NC_HALT_BASE); + } + + if (drv->axi_halt_q6) + pil_q6v5_halt_axi_port(pil, drv->axi_halt_q6); + if (drv->axi_halt_mss) + pil_q6v5_halt_axi_port(pil, drv->axi_halt_mss); + if (drv->axi_halt_nc) + pil_q6v5_halt_axi_port(pil, drv->axi_halt_nc); + + /* + * Software workaround to avoid high MX current during LPASS/MSS + * restart. + */ + if (drv->mx_spike_wa && drv->ahb_clk_vote) { + ret = clk_prepare_enable(drv->ahb_clk); + if (!ret) + assert_clamps(pil); + else + dev_err(pil->dev, "error turning ON AHB clock(rc:%d)\n", + ret); + } + + pil_mss_pdc_sync(drv, true); + /* Wait 6 32kHz sleep cycles for PDC SYNC true */ + udelay(200); + pil_mss_restart_reg(drv, 1); + /* Wait 6 32kHz sleep cycles for reset */ + udelay(200); + ret = pil_mss_restart_reg(drv, 0); + /* Wait 6 32kHz sleep cycles for reset false */ + udelay(200); + pil_mss_pdc_sync(drv, false); + + if (drv->is_booted) { + pil_mss_disable_clks(drv); + pil_mss_power_down(drv); + drv->is_booted = false; + } + + return ret; +} + +int __pil_mss_deinit_image(struct pil_desc *pil, bool err_path) +{ + struct modem_data *drv = dev_get_drvdata(pil->dev); + struct q6v5_data *q6_drv = container_of(pil, struct q6v5_data, desc); + int ret = 0; + struct device *dma_dev = drv->mba_mem_dev_fixed ?: &drv->mba_mem_dev; + s32 status; + u64 val = is_timeout_disabled() ? 0 : pbl_mba_boot_timeout_ms * 1000; + + if (err_path) { + writel_relaxed(CMD_PILFAIL_NFY_MBA, + drv->rmb_base + RMB_MBA_COMMAND); + ret = readl_poll_timeout(drv->rmb_base + RMB_MBA_STATUS, status, + status == STATUS_MBA_UNLOCKED || status < 0, + POLL_INTERVAL_US, val); + if (ret) + dev_err(pil->dev, "MBA region unlock timed out(rc:%d)\n", + ret); + else if (status < 0) + dev_err(pil->dev, "MBA unlock returned err status: %d\n", + status); + } + + ret = pil_mss_shutdown(pil); + + if (q6_drv->ahb_clk_vote) + clk_disable_unprepare(q6_drv->ahb_clk); + + /* In case of any failure where reclaiming MBA and DP memory + * could not happen, free the memory here + */ + if (drv->q6->mba_dp_virt && !drv->mba_mem_dev_fixed) { + if (pil->subsys_vmid > 0) + pil_assign_mem_to_linux(pil, drv->q6->mba_dp_phys, + drv->q6->mba_dp_size); + dma_free_attrs(dma_dev, drv->q6->mba_dp_size, + drv->q6->mba_dp_virt, drv->q6->mba_dp_phys, + drv->attrs_dma); + drv->q6->mba_dp_virt = NULL; + } + + return ret; +} + +int pil_mss_deinit_image(struct pil_desc *pil) +{ + return __pil_mss_deinit_image(pil, true); +} + +int pil_mss_make_proxy_votes(struct pil_desc *pil) +{ + int ret; + struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc); + int uv = 0; + + ret = of_property_read_u32(pil->dev->of_node, "vdd_mx-uV", &uv); + if (ret) { + dev_err(pil->dev, "missing vdd_mx-uV property(rc:%d)\n", ret); + return ret; + } + + ret = regulator_set_voltage(drv->vreg_mx, uv, INT_MAX); + if (ret) { + dev_err(pil->dev, "Failed to request vreg_mx voltage(rc:%d)\n", + ret); + return ret; + } + + ret = regulator_enable(drv->vreg_mx); + if (ret) { + dev_err(pil->dev, "Failed to enable vreg_mx(rc:%d)\n", ret); + regulator_set_voltage(drv->vreg_mx, 0, INT_MAX); + return ret; + } + + if (drv->vreg) { + ret = of_property_read_u32(pil->dev->of_node, "vdd_mss-uV", + &uv); + if (ret) { + dev_err(pil->dev, + "missing vdd_mss-uV property(rc:%d)\n", ret); + goto out; + } + + ret = regulator_set_voltage(drv->vreg, uv, + INT_MAX); + if (ret) { + dev_err(pil->dev, "Failed to set vreg voltage(rc:%d)\n", + ret); + goto out; + } + + ret = regulator_set_load(drv->vreg, 100000); + if (ret < 0) { + dev_err(pil->dev, "Failed to set vreg mode(rc:%d)\n", + ret); + goto out; + } + ret = regulator_enable(drv->vreg); + if (ret) { + dev_err(pil->dev, "Failed to enable vreg(rc:%d)\n", + ret); + regulator_set_voltage(drv->vreg, 0, INT_MAX); + goto out; + } + } + + ret = pil_q6v5_make_proxy_votes(pil); + if (ret && drv->vreg) { + regulator_disable(drv->vreg); + regulator_set_voltage(drv->vreg, 0, INT_MAX); + } +out: + if (ret) { + regulator_disable(drv->vreg_mx); + regulator_set_voltage(drv->vreg_mx, 0, INT_MAX); + } + + return ret; +} + +void pil_mss_remove_proxy_votes(struct pil_desc *pil) +{ + struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc); + + pil_q6v5_remove_proxy_votes(pil); + regulator_disable(drv->vreg_mx); + regulator_set_voltage(drv->vreg_mx, 0, INT_MAX); + if (drv->vreg) { + regulator_disable(drv->vreg); + regulator_set_voltage(drv->vreg, 0, INT_MAX); + } +} + +static int pil_mss_mem_setup(struct pil_desc *pil, + phys_addr_t addr, size_t size) +{ + struct modem_data *md = dev_get_drvdata(pil->dev); + + struct pas_init_image_req { + u32 proc; + u32 start_addr; + u32 len; + } request; + u32 scm_ret = 0; + int ret; + struct scm_desc desc = {0}; + + if (!md->subsys_desc.pil_mss_memsetup) + return 0; + + request.proc = md->pas_id; + request.start_addr = addr; + request.len = size; + + desc.args[0] = md->pas_id; + desc.args[1] = addr; + desc.args[2] = size; + desc.arginfo = SCM_ARGS(3); + ret = scm_call2(SCM_SIP_FNID(SCM_SVC_PIL, PAS_MEM_SETUP_CMD), + &desc); + scm_ret = desc.ret[0]; + + if (ret) + return ret; + return scm_ret; +} + +static int pil_mss_reset(struct pil_desc *pil) +{ + struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc); + phys_addr_t start_addr = pil_get_entry_addr(pil); + u32 debug_val = 0; + int ret; + + trace_pil_func(__func__); + if (drv->mba_dp_phys) + start_addr = drv->mba_dp_phys; + + /* + * Bring subsystem out of reset and enable required + * regulators and clocks. + */ + ret = pil_mss_power_up(drv); + if (ret) + goto err_power; + + ret = pil_mss_enable_clks(drv); + if (ret) + goto err_clks; + + if (!pil->minidump_ss || !pil->modem_ssr) { + /* Save state of modem debug register before full reset */ + debug_val = readl_relaxed(drv->reg_base + QDSP6SS_DBG_CFG); + } + + /* Assert reset to subsystem */ + pil_mss_assert_resets(drv); + /* Wait 6 32kHz sleep cycles for reset */ + udelay(200); + ret = pil_mss_deassert_resets(drv); + if (ret) + goto err_restart; + + if (!pil->minidump_ss || !pil->modem_ssr) { + writel_relaxed(debug_val, drv->reg_base + QDSP6SS_DBG_CFG); + if (modem_dbg_cfg) + writel_relaxed(modem_dbg_cfg, + drv->reg_base + QDSP6SS_DBG_CFG); + } + + /* Program Image Address */ + if (drv->self_auth) { + writel_relaxed(start_addr, drv->rmb_base + RMB_MBA_IMAGE); + /* + * Ensure write to RMB base occurs before reset + * is released. + */ + mb(); + } else { + writel_relaxed((start_addr >> 4) & 0x0FFFFFF0, + drv->reg_base + QDSP6SS_RST_EVB); + } + + /* Program DP Address */ + if (drv->dp_size) { + writel_relaxed(start_addr + SZ_1M, drv->rmb_base + + RMB_PMI_CODE_START); + writel_relaxed(drv->dp_size, drv->rmb_base + + RMB_PMI_CODE_LENGTH); + } else { + writel_relaxed(0, drv->rmb_base + RMB_PMI_CODE_START); + writel_relaxed(0, drv->rmb_base + RMB_PMI_CODE_LENGTH); + } + /* Make sure RMB regs are written before bringing modem out of reset */ + mb(); + + ret = pil_q6v5_reset(pil); + if (ret) + goto err_q6v5_reset; + + /* Wait for MBA to start. Check for PBL and MBA errors while waiting. */ + if (drv->self_auth) { + ret = pil_msa_wait_for_mba_ready(drv); + if (ret) + goto err_q6v5_reset; + } + + dev_info(pil->dev, "MBA boot done\n"); + drv->is_booted = true; + + return 0; + +err_q6v5_reset: + modem_log_rmb_regs(drv->rmb_base); +err_restart: + pil_mss_disable_clks(drv); + if (drv->ahb_clk_vote) + clk_disable_unprepare(drv->ahb_clk); +err_clks: + pil_mss_power_down(drv); +err_power: + return ret; +} + +int pil_mss_reset_load_mba(struct pil_desc *pil) +{ + struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc); + struct modem_data *md = dev_get_drvdata(pil->dev); + const struct firmware *fw = NULL, *dp_fw = NULL; + char fw_name_legacy[10] = "mba.b00"; + char fw_name[10] = "mba.mbn"; + char *dp_name = "msadp"; + char *fw_name_p; + void *mba_dp_virt; + dma_addr_t mba_dp_phys, mba_dp_phys_end; + int ret; + const u8 *data; + struct device *dma_dev = md->mba_mem_dev_fixed ?: &md->mba_mem_dev; + + trace_pil_func(__func__); + if (drv->mba_dp_virt && md->mba_mem_dev_fixed) + goto mss_reset; + fw_name_p = drv->non_elf_image ? fw_name_legacy : fw_name; + ret = request_firmware(&fw, fw_name_p, pil->dev); + if (ret) { + dev_err(pil->dev, "Failed to locate %s (rc:%d)\n", + fw_name_p, ret); + return ret; + } + + data = fw ? fw->data : NULL; + if (!data) { + dev_err(pil->dev, "MBA data is NULL\n"); + ret = -ENOMEM; + goto err_invalid_fw; + } + + drv->mba_dp_size = SZ_1M; + + arch_setup_dma_ops(dma_dev, 0, 0, NULL, 0); + + dma_dev->coherent_dma_mask = DMA_BIT_MASK(sizeof(dma_addr_t) * 8); + + md->attrs_dma = 0; + md->attrs_dma |= DMA_ATTR_SKIP_ZEROING; + md->attrs_dma |= DMA_ATTR_STRONGLY_ORDERED; + + ret = request_firmware(&dp_fw, dp_name, pil->dev); + if (ret) { + dev_warn(pil->dev, "Debug policy not present - %s. Continue.\n", + dp_name); + } else { + if (!dp_fw || !dp_fw->data) { + dev_err(pil->dev, "Invalid DP firmware\n"); + ret = -ENOMEM; + goto err_invalid_fw; + } + drv->dp_size = dp_fw->size; + drv->mba_dp_size += drv->dp_size; + drv->mba_dp_size = ALIGN(drv->mba_dp_size, SZ_4K); + } + + mba_dp_virt = dma_alloc_attrs(dma_dev, drv->mba_dp_size, &mba_dp_phys, + GFP_KERNEL, md->attrs_dma); + if (!mba_dp_virt) { + dev_err(pil->dev, "%s MBA/DP buffer allocation %zx bytes failed\n", + __func__, drv->mba_dp_size); + ret = -ENOMEM; + goto err_invalid_fw; + } + + /* Make sure there are no mappings in PKMAP and fixmap */ + kmap_flush_unused(); + kmap_atomic_flush_unused(); + + drv->mba_dp_phys = mba_dp_phys; + drv->mba_dp_virt = mba_dp_virt; + mba_dp_phys_end = mba_dp_phys + drv->mba_dp_size; + + dev_info(pil->dev, "Loading MBA and DP (if present) from %pa to %pa\n", + &mba_dp_phys, &mba_dp_phys_end); + + /* Load the MBA image into memory */ + if (fw->size <= SZ_1M) { + /* Ensures memcpy is done for max 1MB fw size */ + memcpy(mba_dp_virt, data, fw->size); + } else { + dev_err(pil->dev, "%s fw image loading into memory is failed due to fw size overflow\n", + __func__); + ret = -EINVAL; + goto err_mba_data; + } + /* Ensure memcpy of the MBA memory is done before loading the DP */ + wmb(); + + /* Load the DP image into memory */ + if (drv->mba_dp_size > SZ_1M) { + memcpy(mba_dp_virt + SZ_1M, dp_fw->data, dp_fw->size); + /* Ensure memcpy is done before powering up modem */ + wmb(); + } + + if (pil->subsys_vmid > 0) { + ret = pil_assign_mem_to_subsys(pil, drv->mba_dp_phys, + drv->mba_dp_size); + if (ret) { + pr_err("scm_call to unprotect MBA and DP mem failed(rc:%d)\n", + ret); + goto err_mba_data; + } + } + if (dp_fw) + release_firmware(dp_fw); + release_firmware(fw); + dp_fw = NULL; + fw = NULL; + +mss_reset: + ret = pil_mss_reset(pil); + if (ret) { + dev_err(pil->dev, "MBA boot failed(rc:%d)\n", ret); + goto err_mss_reset; + } + + return 0; + +err_mss_reset: + if (pil->subsys_vmid > 0) + pil_assign_mem_to_linux(pil, drv->mba_dp_phys, + drv->mba_dp_size); +err_mba_data: + dma_free_attrs(dma_dev, drv->mba_dp_size, drv->mba_dp_virt, + drv->mba_dp_phys, md->attrs_dma); +err_invalid_fw: + if (dp_fw) + release_firmware(dp_fw); + if (fw) + release_firmware(fw); + drv->mba_dp_virt = NULL; + return ret; +} + +int pil_mss_debug_reset(struct pil_desc *pil) +{ + struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc); + u32 encryption_status; + int ret; + + + if (!pil->minidump_ss) + return 0; + + encryption_status = pil->minidump_ss->encryption_status; + + if ((pil->minidump_ss->md_ss_enable_status != MD_SS_ENABLED) || + encryption_status == MD_SS_ENCR_NOTREQ) + return 0; + + /* + * Bring subsystem out of reset and enable required + * regulators and clocks. + */ + ret = pil_mss_enable_clks(drv); + if (ret) + return ret; + + if (pil->minidump_ss) { + writel_relaxed(0x1, drv->reg_base + QDSP6SS_NMI_CFG); + /* Let write complete before proceeding */ + mb(); + udelay(2); + } + /* Assert reset to subsystem */ + pil_mss_restart_reg(drv, true); + /* Wait 6 32kHz sleep cycles for reset */ + udelay(200); + ret = pil_mss_restart_reg(drv, false); + if (ret) + goto err_restart; + /* Let write complete before proceeding */ + mb(); + udelay(200); + ret = pil_q6v5_reset(pil); + /* + * Need to Wait for timeout for debug reset sequence to + * complete before returning + */ + pr_info("Minidump: waiting encryption to complete\n"); + msleep(13000); + if (pil->minidump_ss) { + writel_relaxed(0x2, drv->reg_base + QDSP6SS_NMI_CFG); + /* Let write complete before proceeding */ + mb(); + udelay(200); + } + if (ret) + goto err_restart; + return 0; +err_restart: + pil_mss_disable_clks(drv); + if (drv->ahb_clk_vote) + clk_disable_unprepare(drv->ahb_clk); + return ret; +} + +static int pil_msa_auth_modem_mdt(struct pil_desc *pil, const u8 *metadata, + size_t size) +{ + struct modem_data *drv = dev_get_drvdata(pil->dev); + void *mdata_virt; + dma_addr_t mdata_phys; + s32 status; + int ret; + u64 val = is_timeout_disabled() ? 0 : modem_auth_timeout_ms * 1000; + struct device *dma_dev = drv->mba_mem_dev_fixed ?: &drv->mba_mem_dev; + unsigned long attrs = 0; + + trace_pil_func(__func__); + dma_dev->coherent_dma_mask = DMA_BIT_MASK(sizeof(dma_addr_t) * 8); + attrs |= DMA_ATTR_SKIP_ZEROING; + attrs |= DMA_ATTR_STRONGLY_ORDERED; + /* Make metadata physically contiguous and 4K aligned. */ + mdata_virt = dma_alloc_attrs(dma_dev, size, &mdata_phys, + GFP_KERNEL, attrs); + if (!mdata_virt) { + dev_err(pil->dev, "MBA metadata buffer allocation failed\n"); + ret = -ENOMEM; + goto fail; + } + memcpy(mdata_virt, metadata, size); + /* wmb() ensures copy completes prior to starting authentication. */ + wmb(); + + if (pil->subsys_vmid > 0) { + ret = pil_assign_mem_to_subsys(pil, mdata_phys, + ALIGN(size, SZ_4K)); + if (ret) { + pr_err("scm_call to unprotect modem metadata mem failed(rc:%d)\n", + ret); + dma_free_attrs(dma_dev, size, mdata_virt, mdata_phys, + attrs); + goto fail; + } + } + + /* Initialize length counter to 0 */ + writel_relaxed(0, drv->rmb_base + RMB_PMI_CODE_LENGTH); + + /* Pass address of meta-data to the MBA and perform authentication */ + writel_relaxed(mdata_phys, drv->rmb_base + RMB_PMI_META_DATA); + writel_relaxed(CMD_META_DATA_READY, drv->rmb_base + RMB_MBA_COMMAND); + ret = readl_poll_timeout(drv->rmb_base + RMB_MBA_STATUS, status, + status == STATUS_META_DATA_AUTH_SUCCESS || status < 0, + POLL_INTERVAL_US, val); + if (ret) { + dev_err(pil->dev, "MBA authentication of headers timed out(rc:%d)\n", + ret); + } else if (status < 0) { + dev_err(pil->dev, "MBA returned error %d for headers\n", + status); + ret = -EINVAL; + } + + if (pil->subsys_vmid > 0) + pil_assign_mem_to_linux(pil, mdata_phys, ALIGN(size, SZ_4K)); + + dma_free_attrs(dma_dev, size, mdata_virt, mdata_phys, attrs); + + if (!ret) + return ret; + +fail: + modem_log_rmb_regs(drv->rmb_base); + if (drv->q6) { + pil_mss_shutdown(pil); + if (pil->subsys_vmid > 0) + pil_assign_mem_to_linux(pil, drv->q6->mba_dp_phys, + drv->q6->mba_dp_size); + if (drv->q6->mba_dp_virt && !drv->mba_mem_dev_fixed) { + dma_free_attrs(dma_dev, drv->q6->mba_dp_size, + drv->q6->mba_dp_virt, drv->q6->mba_dp_phys, + drv->attrs_dma); + drv->q6->mba_dp_virt = NULL; + } + + } + return ret; +} + +static int pil_msa_mss_reset_mba_load_auth_mdt(struct pil_desc *pil, + const u8 *metadata, size_t size) +{ + int ret; + + ret = pil_mss_reset_load_mba(pil); + if (ret) + return ret; + + return pil_msa_auth_modem_mdt(pil, metadata, size); +} + +static int pil_msa_mba_verify_blob(struct pil_desc *pil, phys_addr_t phy_addr, + size_t size) +{ + struct modem_data *drv = dev_get_drvdata(pil->dev); + s32 status; + u32 img_length = readl_relaxed(drv->rmb_base + RMB_PMI_CODE_LENGTH); + + /* Begin image authentication */ + if (img_length == 0) { + writel_relaxed(phy_addr, drv->rmb_base + RMB_PMI_CODE_START); + writel_relaxed(CMD_LOAD_READY, drv->rmb_base + RMB_MBA_COMMAND); + } + /* Increment length counter */ + img_length += size; + writel_relaxed(img_length, drv->rmb_base + RMB_PMI_CODE_LENGTH); + + status = readl_relaxed(drv->rmb_base + RMB_MBA_STATUS); + if (status < 0) { + dev_err(pil->dev, "MBA returned error %d\n", status); + modem_log_rmb_regs(drv->rmb_base); + return -EINVAL; + } + + return 0; +} + +static int pil_msa_mba_auth(struct pil_desc *pil) +{ + struct modem_data *drv = dev_get_drvdata(pil->dev); + struct q6v5_data *q6_drv = container_of(pil, struct q6v5_data, desc); + int ret; + struct device *dma_dev = drv->mba_mem_dev_fixed ?: &drv->mba_mem_dev; + s32 status; + u64 val = is_timeout_disabled() ? 0 : modem_auth_timeout_ms * 1000; + + /* Wait for all segments to be authenticated or an error to occur */ + ret = readl_poll_timeout(drv->rmb_base + RMB_MBA_STATUS, status, + status == STATUS_AUTH_COMPLETE || status < 0, 50, val); + if (ret) { + dev_err(pil->dev, "MBA authentication of image timed out(rc:%d)\n", + ret); + } else if (status < 0) { + dev_err(pil->dev, "MBA returned error %d for image\n", status); + ret = -EINVAL; + } + + if (drv->q6) { + if (drv->q6->mba_dp_virt && !drv->mba_mem_dev_fixed) { + /* Reclaim MBA and DP (if allocated) memory. */ + if (pil->subsys_vmid > 0) + pil_assign_mem_to_linux(pil, + drv->q6->mba_dp_phys, + drv->q6->mba_dp_size); + dma_free_attrs(dma_dev, drv->q6->mba_dp_size, + drv->q6->mba_dp_virt, drv->q6->mba_dp_phys, + drv->attrs_dma); + + drv->q6->mba_dp_virt = NULL; + } + } + if (ret) + modem_log_rmb_regs(drv->rmb_base); + if (q6_drv->ahb_clk_vote) + clk_disable_unprepare(q6_drv->ahb_clk); + + return ret; +} + +/* + * To be used only if self-auth is disabled, or if the + * MBA image is loaded as segments and not in init_image. + */ +struct pil_reset_ops pil_msa_mss_ops = { + .proxy_vote = pil_mss_make_proxy_votes, + .proxy_unvote = pil_mss_remove_proxy_votes, + .auth_and_reset = pil_mss_reset, + .shutdown = pil_mss_shutdown, +}; + +/* + * To be used if self-auth is enabled and the MBA is to be loaded + * in init_image and the modem headers are also to be authenticated + * in init_image. Modem segments authenticated in auth_and_reset. + */ +struct pil_reset_ops pil_msa_mss_ops_selfauth = { + .init_image = pil_msa_mss_reset_mba_load_auth_mdt, + .proxy_vote = pil_mss_make_proxy_votes, + .proxy_unvote = pil_mss_remove_proxy_votes, + .mem_setup = pil_mss_mem_setup, + .verify_blob = pil_msa_mba_verify_blob, + .auth_and_reset = pil_msa_mba_auth, + .deinit_image = pil_mss_deinit_image, + .shutdown = pil_mss_shutdown, +}; + +/* + * To be used if the modem headers are to be authenticated + * in init_image, and the modem segments in auth_and_reset. + */ +struct pil_reset_ops pil_msa_femto_mba_ops = { + .init_image = pil_msa_auth_modem_mdt, + .verify_blob = pil_msa_mba_verify_blob, + .auth_and_reset = pil_msa_mba_auth, +}; diff --git a/drivers/soc/qcom/pil-msa.h b/drivers/soc/qcom/pil-msa.h new file mode 100644 index 0000000000000000000000000000000000000000..0310234f7203e5517f3e6fcb1360cdd4ccfa58a9 --- /dev/null +++ b/drivers/soc/qcom/pil-msa.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + */ + +#ifndef __MSM_PIL_MSA_H +#define __MSM_PIL_MSA_H + +#include + +#include "peripheral-loader.h" + +struct modem_data { + struct q6v5_data *q6; + struct subsys_device *subsys; + struct subsys_desc subsys_desc; + void *ramdump_dev; + void *minidump_dev; + bool crash_shutdown; + u32 pas_id; + bool ignore_errors; + struct completion stop_ack; + void __iomem *rmb_base; + struct clk *xo; + struct pil_desc desc; + struct device mba_mem_dev; + struct device *mba_mem_dev_fixed; + unsigned long attrs_dma; +}; + +extern struct pil_reset_ops pil_msa_mss_ops; +extern struct pil_reset_ops pil_msa_mss_ops_selfauth; +extern struct pil_reset_ops pil_msa_femto_mba_ops; + +int pil_mss_reset_load_mba(struct pil_desc *pil); +int pil_mss_make_proxy_votes(struct pil_desc *pil); +void pil_mss_remove_proxy_votes(struct pil_desc *pil); +int pil_mss_shutdown(struct pil_desc *pil); +int pil_mss_deinit_image(struct pil_desc *pil); +int __pil_mss_deinit_image(struct pil_desc *pil, bool err_path); +int pil_mss_assert_resets(struct q6v5_data *drv); +int pil_mss_deassert_resets(struct q6v5_data *drv); +int pil_mss_debug_reset(struct pil_desc *pil); +#endif diff --git a/drivers/soc/qcom/pil-q6v5-mss.c b/drivers/soc/qcom/pil-q6v5-mss.c new file mode 100644 index 0000000000000000000000000000000000000000..db48b1a2117162dbd0c893c9c9a49c74838805c8 --- /dev/null +++ b/drivers/soc/qcom/pil-q6v5-mss.c @@ -0,0 +1,554 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "peripheral-loader.h" +#include "pil-q6v5.h" +#include "pil-msa.h" + +#define PROXY_TIMEOUT_MS 10000 +#define MAX_SSR_REASON_LEN 256U +#define STOP_ACK_TIMEOUT_MS 1000 + +#define subsys_to_drv(d) container_of(d, struct modem_data, subsys_desc) + +static void log_modem_sfr(struct modem_data *drv) +{ + size_t size; + char *smem_reason, reason[MAX_SSR_REASON_LEN]; + + if (drv->q6->smem_id == -1) + return; + + smem_reason = qcom_smem_get(QCOM_SMEM_HOST_ANY, drv->q6->smem_id, + &size); + if (IS_ERR(smem_reason) || !size) { + pr_err("modem SFR: (unknown, qcom_smem_get failed).\n"); + return; + } + if (!smem_reason[0]) { + pr_err("modem SFR: (unknown, empty string found).\n"); + return; + } + + strlcpy(reason, smem_reason, min(size, (size_t)MAX_SSR_REASON_LEN)); + pr_err("modem subsystem failure reason: %s.\n", reason); +} + +static void restart_modem(struct modem_data *drv) +{ + log_modem_sfr(drv); + drv->ignore_errors = true; + subsystem_restart_dev(drv->subsys); +} + +static irqreturn_t modem_err_fatal_intr_handler(int irq, void *dev_id) +{ + struct modem_data *drv = subsys_to_drv(dev_id); + + /* Ignore if we're the one that set the force stop BIT */ + if (drv->crash_shutdown) + return IRQ_HANDLED; + + pr_err("Fatal error on the modem.\n"); + subsys_set_crash_status(drv->subsys, CRASH_STATUS_ERR_FATAL); + restart_modem(drv); + return IRQ_HANDLED; +} + +static irqreturn_t modem_stop_ack_intr_handler(int irq, void *dev_id) +{ + struct modem_data *drv = subsys_to_drv(dev_id); + + pr_info("Received stop ack interrupt from modem\n"); + complete(&drv->stop_ack); + return IRQ_HANDLED; +} + +static irqreturn_t modem_shutdown_ack_intr_handler(int irq, void *dev_id) +{ + struct modem_data *drv = subsys_to_drv(dev_id); + + pr_info("Received stop shutdown interrupt from modem\n"); + complete_shutdown_ack(drv->subsys); + return IRQ_HANDLED; +} + +static irqreturn_t modem_ramdump_disable_intr_handler(int irq, void *dev_id) +{ + struct modem_data *drv = subsys_to_drv(dev_id); + + pr_info("Received ramdump disable interrupt from modem\n"); + drv->subsys_desc.ramdump_disable = 1; + return IRQ_HANDLED; +} + +static int modem_shutdown(const struct subsys_desc *subsys, bool force_stop) +{ + struct modem_data *drv = subsys_to_drv(subsys); + unsigned long ret; + + if (subsys->is_not_loadable) + return 0; + + if (!subsys_get_crash_status(drv->subsys) && force_stop && + subsys->force_stop_bit) { + qcom_smem_state_update_bits(subsys->state, + BIT(subsys->force_stop_bit), 1); + ret = wait_for_completion_timeout(&drv->stop_ack, + msecs_to_jiffies(STOP_ACK_TIMEOUT_MS)); + if (!ret) + pr_warn("Timed out on stop ack from modem.\n"); + qcom_smem_state_update_bits(subsys->state, + BIT(subsys->force_stop_bit), 0); + } + + if (drv->subsys_desc.ramdump_disable_irq) { + pr_warn("Ramdump disable value is %d\n", + drv->subsys_desc.ramdump_disable); + } + + pil_shutdown(&drv->q6->desc); + + return 0; +} + +static int modem_powerup(const struct subsys_desc *subsys) +{ + struct modem_data *drv = subsys_to_drv(subsys); + + if (subsys->is_not_loadable) + return 0; + /* + * At this time, the modem is shutdown. Therefore this function cannot + * run concurrently with the watchdog bite error handler, making it safe + * to unset the flag below. + */ + reinit_completion(&drv->stop_ack); + drv->subsys_desc.ramdump_disable = 0; + drv->ignore_errors = false; + drv->q6->desc.fw_name = subsys->fw_name; + return pil_boot(&drv->q6->desc); +} + +static void modem_crash_shutdown(const struct subsys_desc *subsys) +{ + struct modem_data *drv = subsys_to_drv(subsys); + + drv->crash_shutdown = true; + if (!subsys_get_crash_status(drv->subsys) && + subsys->force_stop_bit) { + qcom_smem_state_update_bits(subsys->state, + BIT(subsys->force_stop_bit), 1); + msleep(STOP_ACK_TIMEOUT_MS); + } +} + +static int modem_ramdump(int enable, const struct subsys_desc *subsys) +{ + struct modem_data *drv = subsys_to_drv(subsys); + int ret; + + if (!enable) + return 0; + + ret = pil_mss_make_proxy_votes(&drv->q6->desc); + if (ret) + return ret; + + ret = pil_mss_debug_reset(&drv->q6->desc); + if (ret) + return ret; + + pil_mss_remove_proxy_votes(&drv->q6->desc); + ret = pil_mss_make_proxy_votes(&drv->q6->desc); + if (ret) + return ret; + + ret = pil_mss_reset_load_mba(&drv->q6->desc); + if (ret) + return ret; + + ret = pil_do_ramdump(&drv->q6->desc, + drv->ramdump_dev, drv->minidump_dev); + if (ret < 0) + pr_err("Unable to dump modem fw memory (rc = %d).\n", ret); + + ret = __pil_mss_deinit_image(&drv->q6->desc, false); + if (ret < 0) + pr_err("Unable to free up resources (rc = %d).\n", ret); + + pil_mss_remove_proxy_votes(&drv->q6->desc); + return ret; +} + +static irqreturn_t modem_wdog_bite_intr_handler(int irq, void *dev_id) +{ + struct modem_data *drv = subsys_to_drv(dev_id); + + if (drv->ignore_errors) + return IRQ_HANDLED; + + pr_err("Watchdog bite received from modem software!\n"); + if (drv->subsys_desc.system_debug) + panic("%s: System ramdump requested. Triggering device restart!\n", + __func__); + subsys_set_crash_status(drv->subsys, CRASH_STATUS_WDOG_BITE); + restart_modem(drv); + return IRQ_HANDLED; +} + +static int pil_subsys_init(struct modem_data *drv, + struct platform_device *pdev) +{ + int ret = -EINVAL; + + drv->subsys_desc.name = "modem"; + drv->subsys_desc.dev = &pdev->dev; + drv->subsys_desc.owner = THIS_MODULE; + drv->subsys_desc.shutdown = modem_shutdown; + drv->subsys_desc.powerup = modem_powerup; + drv->subsys_desc.ramdump = modem_ramdump; + drv->subsys_desc.crash_shutdown = modem_crash_shutdown; + drv->subsys_desc.err_fatal_handler = modem_err_fatal_intr_handler; + drv->subsys_desc.stop_ack_handler = modem_stop_ack_intr_handler; + drv->subsys_desc.wdog_bite_handler = modem_wdog_bite_intr_handler; + drv->subsys_desc.ramdump_disable_handler = + modem_ramdump_disable_intr_handler; + drv->subsys_desc.shutdown_ack_handler = modem_shutdown_ack_intr_handler; + + if (IS_ERR_OR_NULL(drv->q6)) { + ret = PTR_ERR(drv->q6); + dev_err(&pdev->dev, "Pil q6 data is err %pK %d!!!\n", + drv->q6, ret); + goto err_subsys; + } + + drv->q6->desc.modem_ssr = false; + drv->q6->desc.signal_aop = of_property_read_bool(pdev->dev.of_node, + "qcom,signal-aop"); + if (drv->q6->desc.signal_aop) { + drv->q6->desc.cl.dev = &pdev->dev; + drv->q6->desc.cl.tx_block = true; + drv->q6->desc.cl.tx_tout = 1000; + drv->q6->desc.cl.knows_txdone = false; + drv->q6->desc.mbox = mbox_request_channel(&drv->q6->desc.cl, 0); + if (IS_ERR(drv->q6->desc.mbox)) { + ret = PTR_ERR(drv->q6->desc.mbox); + dev_err(&pdev->dev, "Failed to get mailbox channel %pK %d\n", + drv->q6->desc.mbox, ret); + goto err_subsys; + } + } + + drv->subsys = subsys_register(&drv->subsys_desc); + if (IS_ERR(drv->subsys)) { + ret = PTR_ERR(drv->subsys); + goto err_subsys; + } + + drv->ramdump_dev = create_ramdump_device("modem", &pdev->dev); + if (!drv->ramdump_dev) { + pr_err("%s: Unable to create a modem ramdump device.\n", + __func__); + ret = -ENOMEM; + goto err_ramdump; + } + drv->minidump_dev = create_ramdump_device("md_modem", &pdev->dev); + if (!drv->minidump_dev) { + pr_err("%s: Unable to create a modem minidump device.\n", + __func__); + ret = -ENOMEM; + goto err_minidump; + } + + return 0; + +err_minidump: + destroy_ramdump_device(drv->ramdump_dev); +err_ramdump: + subsys_unregister(drv->subsys); +err_subsys: + return ret; +} + +static int pil_mss_loadable_init(struct modem_data *drv, + struct platform_device *pdev) +{ + struct q6v5_data *q6; + struct pil_desc *q6_desc; + struct resource *res; + struct property *prop; + int ret; + + q6 = pil_q6v5_init(pdev); + if (IS_ERR_OR_NULL(q6)) + return PTR_ERR(q6); + drv->q6 = q6; + drv->xo = q6->xo; + + q6_desc = &q6->desc; + q6_desc->owner = THIS_MODULE; + q6_desc->proxy_timeout = PROXY_TIMEOUT_MS; + + q6_desc->ops = &pil_msa_mss_ops; + + q6_desc->sequential_loading = of_property_read_bool(pdev->dev.of_node, + "qcom,sequential-fw-load"); + q6->reset_clk = of_property_read_bool(pdev->dev.of_node, + "qcom,reset-clk"); + q6->self_auth = of_property_read_bool(pdev->dev.of_node, + "qcom,pil-self-auth"); + if (q6->self_auth) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "rmb_base"); + q6->rmb_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(q6->rmb_base)) + return PTR_ERR(q6->rmb_base); + drv->rmb_base = q6->rmb_base; + q6_desc->ops = &pil_msa_mss_ops_selfauth; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "restart_reg"); + if (!res) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "restart_reg_sec"); + if (!res) { + dev_err(&pdev->dev, "No restart register defined\n"); + return -ENOMEM; + } + q6->restart_reg_sec = true; + } + + q6->restart_reg = devm_ioremap(&pdev->dev, + res->start, resource_size(res)); + if (!q6->restart_reg) + return -ENOMEM; + + q6->pdc_sync = NULL; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pdc_sync"); + if (res) { + q6->pdc_sync = devm_ioremap(&pdev->dev, + res->start, resource_size(res)); + if (of_property_read_u32(pdev->dev.of_node, + "qcom,mss_pdc_offset", &q6->mss_pdc_offset)) { + dev_err(&pdev->dev, + "Offset for MSS PDC not specified\n"); + return -EINVAL; + } + + } + + q6->alt_reset = NULL; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "alt_reset"); + if (res) { + q6->alt_reset = devm_ioremap(&pdev->dev, + res->start, resource_size(res)); + } + + q6->vreg = NULL; + + prop = of_find_property(pdev->dev.of_node, "vdd_mss-supply", NULL); + if (prop) { + q6->vreg = devm_regulator_get(&pdev->dev, "vdd_mss"); + if (IS_ERR(q6->vreg)) + return PTR_ERR(q6->vreg); + } + + q6->vreg_mx = devm_regulator_get(&pdev->dev, "vdd_mx"); + if (IS_ERR(q6->vreg_mx)) + return PTR_ERR(q6->vreg_mx); + prop = of_find_property(pdev->dev.of_node, "vdd_mx-uV", NULL); + if (!prop) { + dev_err(&pdev->dev, "Missing vdd_mx-uV property\n"); + return -EINVAL; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "cxrail_bhs_reg"); + if (res) + q6->cxrail_bhs = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + + q6->ahb_clk = devm_clk_get(&pdev->dev, "iface_clk"); + if (IS_ERR(q6->ahb_clk)) + return PTR_ERR(q6->ahb_clk); + + q6->axi_clk = devm_clk_get(&pdev->dev, "bus_clk"); + if (IS_ERR(q6->axi_clk)) + return PTR_ERR(q6->axi_clk); + + q6->rom_clk = devm_clk_get(&pdev->dev, "mem_clk"); + if (IS_ERR(q6->rom_clk)) + return PTR_ERR(q6->rom_clk); + + ret = of_property_read_u32(pdev->dev.of_node, + "qcom,pas-id", &drv->pas_id); + if (ret) + dev_info(&pdev->dev, "No pas_id found.\n"); + + drv->subsys_desc.pil_mss_memsetup = + of_property_read_bool(pdev->dev.of_node, "qcom,pil-mss-memsetup"); + + /* Optional. */ + if (of_property_match_string(pdev->dev.of_node, + "qcom,active-clock-names", "gpll0_mss_clk") >= 0) + q6->gpll0_mss_clk = devm_clk_get(&pdev->dev, "gpll0_mss_clk"); + + if (of_property_match_string(pdev->dev.of_node, + "qcom,active-clock-names", "snoc_axi_clk") >= 0) + q6->snoc_axi_clk = devm_clk_get(&pdev->dev, "snoc_axi_clk"); + + if (of_property_match_string(pdev->dev.of_node, + "qcom,active-clock-names", "mnoc_axi_clk") >= 0) + q6->mnoc_axi_clk = devm_clk_get(&pdev->dev, "mnoc_axi_clk"); + + /* Defaulting smem_id to be not present */ + q6->smem_id = -1; + + if (of_find_property(pdev->dev.of_node, "qcom,smem-id", NULL)) { + ret = of_property_read_u32(pdev->dev.of_node, "qcom,smem-id", + &q6->smem_id); + if (ret) { + dev_err(&pdev->dev, "Failed to get the smem_id(ret:%d)\n", + ret); + return ret; + } + } + + ret = pil_desc_init(q6_desc); + + return ret; +} + +static int pil_mss_driver_probe(struct platform_device *pdev) +{ + struct modem_data *drv; + int ret, is_not_loadable; + + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + platform_set_drvdata(pdev, drv); + + is_not_loadable = of_property_read_bool(pdev->dev.of_node, + "qcom,is-not-loadable"); + if (is_not_loadable) { + drv->subsys_desc.is_not_loadable = 1; + } else { + ret = pil_mss_loadable_init(drv, pdev); + if (ret) + return ret; + } + init_completion(&drv->stop_ack); + + /* Probe the MBA mem device if present */ + ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (ret) + return ret; + + return pil_subsys_init(drv, pdev); +} + +static int pil_mss_driver_exit(struct platform_device *pdev) +{ + struct modem_data *drv = platform_get_drvdata(pdev); + + subsys_unregister(drv->subsys); + destroy_ramdump_device(drv->ramdump_dev); + destroy_ramdump_device(drv->minidump_dev); + pil_desc_release(&drv->q6->desc); + return 0; +} + +static int pil_mba_mem_driver_probe(struct platform_device *pdev) +{ + struct modem_data *drv; + + if (!pdev->dev.parent) { + pr_err("No parent found.\n"); + return -EINVAL; + } + drv = dev_get_drvdata(pdev->dev.parent); + drv->mba_mem_dev_fixed = &pdev->dev; + return 0; +} + +static const struct of_device_id mba_mem_match_table[] = { + { .compatible = "qcom,pil-mba-mem" }, + {} +}; + +static struct platform_driver pil_mba_mem_driver = { + .probe = pil_mba_mem_driver_probe, + .driver = { + .name = "pil-mba-mem", + .of_match_table = mba_mem_match_table, + }, +}; + +static const struct of_device_id mss_match_table[] = { + { .compatible = "qcom,pil-q6v5-mss" }, + { .compatible = "qcom,pil-q6v55-mss" }, + { .compatible = "qcom,pil-q6v56-mss" }, + {} +}; + +static struct platform_driver pil_mss_driver = { + .probe = pil_mss_driver_probe, + .remove = pil_mss_driver_exit, + .driver = { + .name = "pil-q6v5-mss", + .of_match_table = mss_match_table, + }, +}; + +static int __init pil_mss_init(void) +{ + int ret; + + ret = platform_driver_register(&pil_mba_mem_driver); + if (!ret) + ret = platform_driver_register(&pil_mss_driver); + return ret; +} +module_init(pil_mss_init); + +static void __exit pil_mss_exit(void) +{ + platform_driver_unregister(&pil_mss_driver); +} +module_exit(pil_mss_exit); + +MODULE_DESCRIPTION("Support for booting modem subsystems with QDSP6v5 Hexagon processors"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/pil-q6v5.c b/drivers/soc/qcom/pil-q6v5.c new file mode 100644 index 0000000000000000000000000000000000000000..208e3270d0ef94cd81a8154182bb7108cacbc508 --- /dev/null +++ b/drivers/soc/qcom/pil-q6v5.c @@ -0,0 +1,848 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "peripheral-loader.h" +#include "pil-msa.h" +#include "pil-q6v5.h" + +/* QDSP6SS Register Offsets */ +#define QDSP6SS_RESET 0x014 +#define QDSP6SS_GFMUX_CTL 0x020 +#define QDSP6SS_PWR_CTL 0x030 +#define QDSP6V6SS_MEM_PWR_CTL 0x034 +#define QDSP6SS_BHS_STATUS 0x078 +#define QDSP6SS_MEM_PWR_CTL 0x0B0 +#define QDSP6SS_STRAP_ACC 0x110 +#define QDSP6V62SS_BHS_STATUS 0x0C4 + +/* AXI Halt Register Offsets */ +#define AXI_HALTREQ 0x0 +#define AXI_HALTACK 0x4 +#define AXI_IDLE 0x8 + +#define HALT_ACK_TIMEOUT_US 100000 + +/* QDSP6SS_RESET */ +#define Q6SS_STOP_CORE BIT(0) +#define Q6SS_CORE_ARES BIT(1) +#define Q6SS_BUS_ARES_ENA BIT(2) + +/* QDSP6SS_GFMUX_CTL */ +#define Q6SS_CLK_ENA BIT(1) +#define Q6SS_CLK_SRC_SEL_C BIT(3) +#define Q6SS_CLK_SRC_SEL_FIELD 0xC +#define Q6SS_CLK_SRC_SWITCH_CLK_OVR BIT(8) + +/* QDSP6SS_PWR_CTL */ +#define Q6SS_L2DATA_SLP_NRET_N_0 BIT(0) +#define Q6SS_L2DATA_SLP_NRET_N_1 BIT(1) +#define Q6SS_L2DATA_SLP_NRET_N_2 BIT(2) +#define Q6SS_L2TAG_SLP_NRET_N BIT(16) +#define Q6SS_ETB_SLP_NRET_N BIT(17) +#define Q6SS_L2DATA_STBY_N BIT(18) +#define Q6SS_SLP_RET_N BIT(19) +#define Q6SS_CLAMP_IO BIT(20) +#define QDSS_BHS_ON BIT(21) +#define QDSS_LDO_BYP BIT(22) + +/* QDSP6v55 parameters */ +#define QDSP6v55_LDO_ON BIT(26) +#define QDSP6v55_LDO_BYP BIT(25) +#define QDSP6v55_BHS_ON BIT(24) +#define QDSP6v55_CLAMP_WL BIT(21) +#define QDSP6v55_CLAMP_QMC_MEM BIT(22) +#define L1IU_SLP_NRET_N BIT(15) +#define L1DU_SLP_NRET_N BIT(14) +#define L2PLRU_SLP_NRET_N BIT(13) +#define QDSP6v55_BHS_EN_REST_ACK BIT(0) + +#define HALT_CHECK_MAX_LOOPS (200) +#define BHS_CHECK_MAX_LOOPS (200) +#define QDSP6SS_XO_CBCR (0x0038) + +/* QDSP6v65 parameters */ +#define QDSP6SS_BOOT_CORE_START (0x400) +#define QDSP6SS_BOOT_CMD (0x404) +#define MSS_STATUS (0x40) +#define QDSP6SS_SLEEP (0x3C) +#define SLEEP_CHECK_MAX_LOOPS (200) +#define BOOT_FSM_TIMEOUT (10000) + +#define QDSP6SS_ACC_OVERRIDE_VAL 0x20 + +int pil_q6v5_make_proxy_votes(struct pil_desc *pil) +{ + int ret; + struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc); + int uv; + + ret = of_property_read_u32(pil->dev->of_node, "vdd_cx-voltage", &uv); + if (ret) { + dev_err(pil->dev, "missing vdd_cx-voltage property(rc:%d)\n", + ret); + return ret; + } + + ret = clk_prepare_enable(drv->xo); + if (ret) { + dev_err(pil->dev, "Failed to vote for XO(rc:%d)\n", ret); + goto out; + } + + ret = clk_prepare_enable(drv->pnoc_clk); + if (ret) { + dev_err(pil->dev, "Failed to vote for pnoc(rc:%d)\n", ret); + goto err_pnoc_vote; + } + + ret = clk_prepare_enable(drv->qdss_clk); + if (ret) { + dev_err(pil->dev, "Failed to vote for qdss(rc:%d)\n", ret); + goto err_qdss_vote; + } + + ret = clk_prepare_enable(drv->prng_clk); + if (ret) { + dev_err(pil->dev, "Failed to vote for prng(rc:%d)\n", ret); + goto err_prng_vote; + } + + ret = clk_prepare_enable(drv->axis2_clk); + if (ret) { + dev_err(pil->dev, "Failed to vote for axis2(rc:%d)\n", ret); + goto err_axis2_vote; + } + + ret = regulator_set_voltage(drv->vreg_cx, uv, INT_MAX); + if (ret) { + dev_err(pil->dev, "Failed to request vdd_cx voltage(rc:%d)\n", + ret); + goto err_cx_voltage; + } + + ret = regulator_set_load(drv->vreg_cx, 100000); + if (ret < 0) { + dev_err(pil->dev, "Failed to set vdd_cx mode(rc:%d)\n", ret); + goto err_cx_mode; + } + + ret = regulator_enable(drv->vreg_cx); + if (ret) { + dev_err(pil->dev, "Failed to vote for vdd_cx(rc:%d)\n", ret); + goto err_cx_enable; + } + + if (drv->vreg_pll) { + ret = regulator_enable(drv->vreg_pll); + if (ret) { + dev_err(pil->dev, "Failed to vote for vdd_pll(rc:%d)\n", + ret); + goto err_vreg_pll; + } + } + + return 0; + +err_vreg_pll: + regulator_disable(drv->vreg_cx); +err_cx_enable: + regulator_set_load(drv->vreg_cx, 0); +err_cx_mode: + regulator_set_voltage(drv->vreg_cx, 0, INT_MAX); +err_cx_voltage: + clk_disable_unprepare(drv->axis2_clk); +err_axis2_vote: + clk_disable_unprepare(drv->prng_clk); +err_prng_vote: + clk_disable_unprepare(drv->qdss_clk); +err_qdss_vote: + clk_disable_unprepare(drv->pnoc_clk); +err_pnoc_vote: + clk_disable_unprepare(drv->xo); +out: + return ret; +} +EXPORT_SYMBOL(pil_q6v5_make_proxy_votes); + +void pil_q6v5_remove_proxy_votes(struct pil_desc *pil) +{ + struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc); + int uv, ret = 0; + + ret = of_property_read_u32(pil->dev->of_node, "vdd_cx-voltage", &uv); + if (ret) { + dev_err(pil->dev, "missing vdd_cx-voltage property(rc:%d)\n", + ret); + return; + } + + if (drv->vreg_pll) { + regulator_disable(drv->vreg_pll); + regulator_set_load(drv->vreg_pll, 0); + } + regulator_disable(drv->vreg_cx); + regulator_set_load(drv->vreg_cx, 0); + regulator_set_voltage(drv->vreg_cx, 0, INT_MAX); + clk_disable_unprepare(drv->xo); + clk_disable_unprepare(drv->pnoc_clk); + clk_disable_unprepare(drv->qdss_clk); + clk_disable_unprepare(drv->prng_clk); + clk_disable_unprepare(drv->axis2_clk); +} +EXPORT_SYMBOL(pil_q6v5_remove_proxy_votes); + +void pil_q6v5_halt_axi_port(struct pil_desc *pil, void __iomem *halt_base) +{ + int ret; + u32 status; + + /* Assert halt request */ + writel_relaxed(1, halt_base + AXI_HALTREQ); + + /* Wait for halt */ + ret = readl_poll_timeout(halt_base + AXI_HALTACK, + status, status != 0, 50, HALT_ACK_TIMEOUT_US); + if (ret) + dev_warn(pil->dev, "Port %pK halt timeout\n", halt_base); + else if (!readl_relaxed(halt_base + AXI_IDLE)) + dev_warn(pil->dev, "Port %pK halt failed\n", halt_base); + + /* Clear halt request (port will remain halted until reset) */ + writel_relaxed(0, halt_base + AXI_HALTREQ); +} +EXPORT_SYMBOL(pil_q6v5_halt_axi_port); + +void assert_clamps(struct pil_desc *pil) +{ + u32 val; + struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc); + + /* + * Assert QDSP6 I/O clamp, memory wordline clamp, and compiler memory + * clamp as a software workaround to avoid high MX current during + * LPASS/MSS restart. + */ + val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL); + val |= (Q6SS_CLAMP_IO | QDSP6v55_CLAMP_WL | + QDSP6v55_CLAMP_QMC_MEM); + writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL); + /* To make sure asserting clamps is done before MSS restart*/ + mb(); +} + +static void __pil_q6v5_shutdown(struct pil_desc *pil) +{ + u32 val; + struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc); + + /* Turn off core clock */ + val = readl_relaxed(drv->reg_base + QDSP6SS_GFMUX_CTL); + val &= ~Q6SS_CLK_ENA; + writel_relaxed(val, drv->reg_base + QDSP6SS_GFMUX_CTL); + + /* Clamp IO */ + val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL); + val |= Q6SS_CLAMP_IO; + writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL); + + /* Turn off Q6 memories */ + val &= ~(Q6SS_L2DATA_SLP_NRET_N_0 | Q6SS_L2DATA_SLP_NRET_N_1 | + Q6SS_L2DATA_SLP_NRET_N_2 | Q6SS_SLP_RET_N | + Q6SS_L2TAG_SLP_NRET_N | Q6SS_ETB_SLP_NRET_N | + Q6SS_L2DATA_STBY_N); + writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL); + + /* Assert Q6 resets */ + val = readl_relaxed(drv->reg_base + QDSP6SS_RESET); + val |= (Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENA); + writel_relaxed(val, drv->reg_base + QDSP6SS_RESET); + + /* Kill power at block headswitch */ + val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL); + val &= ~QDSS_BHS_ON; + writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL); +} + +void pil_q6v5_shutdown(struct pil_desc *pil) +{ + struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc); + + if (drv->qdsp6v55) { + /* Subsystem driver expected to halt bus and assert reset */ + return; + } + __pil_q6v5_shutdown(pil); +} +EXPORT_SYMBOL(pil_q6v5_shutdown); + +static int __pil_q6v5_reset(struct pil_desc *pil) +{ + struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc); + u32 val; + + /* Assert resets, stop core */ + val = readl_relaxed(drv->reg_base + QDSP6SS_RESET); + val |= (Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENA | Q6SS_STOP_CORE); + writel_relaxed(val, drv->reg_base + QDSP6SS_RESET); + + /* Enable power block headswitch, and wait for it to stabilize */ + val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL); + val |= QDSS_BHS_ON | QDSS_LDO_BYP; + writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL); + + /* Ensure physical memory access is done*/ + mb(); + udelay(1); + + /* + * Turn on memories. L2 banks should be done individually + * to minimize inrush current. + */ + val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL); + val |= Q6SS_SLP_RET_N | Q6SS_L2TAG_SLP_NRET_N | + Q6SS_ETB_SLP_NRET_N | Q6SS_L2DATA_STBY_N; + writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL); + val |= Q6SS_L2DATA_SLP_NRET_N_2; + writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL); + val |= Q6SS_L2DATA_SLP_NRET_N_1; + writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL); + val |= Q6SS_L2DATA_SLP_NRET_N_0; + writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL); + + /* Remove IO clamp */ + val &= ~Q6SS_CLAMP_IO; + writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL); + + /* Bring core out of reset */ + val = readl_relaxed(drv->reg_base + QDSP6SS_RESET); + val &= ~Q6SS_CORE_ARES; + writel_relaxed(val, drv->reg_base + QDSP6SS_RESET); + + /* Turn on core clock */ + val = readl_relaxed(drv->reg_base + QDSP6SS_GFMUX_CTL); + val |= Q6SS_CLK_ENA; + + /* Need a different clock source for v5.2.0 */ + if (drv->qdsp6v5_2_0) { + val &= ~Q6SS_CLK_SRC_SEL_FIELD; + val |= Q6SS_CLK_SRC_SEL_C; + } + + /* force clock on during source switch */ + if (drv->qdsp6v56) + val |= Q6SS_CLK_SRC_SWITCH_CLK_OVR; + + writel_relaxed(val, drv->reg_base + QDSP6SS_GFMUX_CTL); + + /* Start core execution */ + val = readl_relaxed(drv->reg_base + QDSP6SS_RESET); + val &= ~Q6SS_STOP_CORE; + writel_relaxed(val, drv->reg_base + QDSP6SS_RESET); + + return 0; +} + +static int q6v55_branch_clk_enable(struct q6v5_data *drv) +{ + u32 val, count; + void __iomem *cbcr_reg = drv->reg_base + QDSP6SS_XO_CBCR; + + val = readl_relaxed(cbcr_reg); + val |= 0x1; + writel_relaxed(val, cbcr_reg); + + for (count = HALT_CHECK_MAX_LOOPS; count > 0; count--) { + val = readl_relaxed(cbcr_reg); + if (!(val & BIT(31))) + return 0; + udelay(1); + } + + dev_err(drv->desc.dev, "Failed to enable xo branch clock.\n"); + return -EINVAL; +} + +static int __pil_q6v65_reset(struct pil_desc *pil) +{ + struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc); + u32 val, count; + int ret; + + val = readl_relaxed(drv->reg_base + QDSP6SS_SLEEP); + val |= 0x1; + writel_relaxed(val, drv->reg_base + QDSP6SS_SLEEP); + for (count = SLEEP_CHECK_MAX_LOOPS; count > 0; count--) { + val = readl_relaxed(drv->reg_base + QDSP6SS_SLEEP); + if (!(val & BIT(31))) + break; + udelay(1); + } + + if (!count) { + dev_err(drv->desc.dev, "Sleep clock did not come on in time\n"); + return -ETIMEDOUT; + } + + /* De-assert QDSP6 stop core */ + writel_relaxed(1, drv->reg_base + QDSP6SS_BOOT_CORE_START); + /* De-assert stop core before starting boot FSM */ + mb(); + /* Trigger boot FSM */ + writel_relaxed(1, drv->reg_base + QDSP6SS_BOOT_CMD); + + /* Wait for boot FSM to complete */ + ret = readl_poll_timeout(drv->rmb_base + MSS_STATUS, val, + (val & BIT(0)) != 0, 10, BOOT_FSM_TIMEOUT); + + if (ret) { + dev_err(drv->desc.dev, "Boot FSM failed to complete.\n"); + /* Reset the modem so that boot FSM is in reset state */ + pil_mss_assert_resets(drv); + /* Wait 6 32kHz sleep cycles for reset */ + udelay(200); + pil_mss_deassert_resets(drv); + } + + return ret; +} + +static int __pil_q6v55_reset(struct pil_desc *pil) +{ + struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc); + u32 val; + int i; + + trace_pil_func(__func__); + /* Override the ACC value if required */ + if (drv->override_acc) + writel_relaxed(QDSP6SS_ACC_OVERRIDE_VAL, + drv->reg_base + QDSP6SS_STRAP_ACC); + + /* Override the ACC value with input value */ + if (!of_property_read_u32(pil->dev->of_node, "qcom,override-acc-1", + &drv->override_acc_1)) + writel_relaxed(drv->override_acc_1, + drv->reg_base + QDSP6SS_STRAP_ACC); + + /* Assert resets, stop core */ + val = readl_relaxed(drv->reg_base + QDSP6SS_RESET); + val |= (Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENA | Q6SS_STOP_CORE); + writel_relaxed(val, drv->reg_base + QDSP6SS_RESET); + + /* BHS require xo cbcr to be enabled */ + i = q6v55_branch_clk_enable(drv); + if (i) + return i; + + /* Enable power block headswitch, and wait for it to stabilize */ + val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL); + val |= QDSP6v55_BHS_ON; + writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL); + + /* Ensure physical memory access is done*/ + mb(); + udelay(1); + + if (drv->qdsp6v62_1_2 || drv->qdsp6v62_1_5 || drv->qdsp6v62_1_4) { + for (i = BHS_CHECK_MAX_LOOPS; i > 0; i--) { + if (readl_relaxed(drv->reg_base + QDSP6V62SS_BHS_STATUS) + & QDSP6v55_BHS_EN_REST_ACK) + break; + udelay(1); + } + if (!i) { + pr_err("%s: BHS_EN_REST_ACK not set!\n", __func__); + return -ETIMEDOUT; + } + } + + if (drv->qdsp6v61_1_1) { + for (i = BHS_CHECK_MAX_LOOPS; i > 0; i--) { + if (readl_relaxed(drv->reg_base + QDSP6SS_BHS_STATUS) + & QDSP6v55_BHS_EN_REST_ACK) + break; + udelay(1); + } + if (!i) { + pr_err("%s: BHS_EN_REST_ACK not set!\n", __func__); + return -ETIMEDOUT; + } + } + + /* Put LDO in bypass mode */ + val |= QDSP6v55_LDO_BYP; + writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL); + + if (drv->qdsp6v56_1_3) { + /* Deassert memory peripheral sleep and L2 memory standby */ + val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL); + val |= (Q6SS_L2DATA_STBY_N | Q6SS_SLP_RET_N); + writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL); + + /* Turn on L1, L2 and ETB memories 1 at a time */ + for (i = 17; i >= 0; i--) { + val |= BIT(i); + writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL); + udelay(1); + } + } else if (drv->qdsp6v56_1_5 || drv->qdsp6v56_1_8 + || drv->qdsp6v56_1_10) { + /* Deassert QDSP6 compiler memory clamp */ + val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL); + val &= ~QDSP6v55_CLAMP_QMC_MEM; + writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL); + + /* Deassert memory peripheral sleep and L2 memory standby */ + val |= (Q6SS_L2DATA_STBY_N | Q6SS_SLP_RET_N); + writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL); + + /* Turn on L1, L2, ETB and JU memories 1 at a time */ + val = readl_relaxed(drv->reg_base + QDSP6SS_MEM_PWR_CTL); + for (i = 19; i >= 0; i--) { + val |= BIT(i); + writel_relaxed(val, drv->reg_base + + QDSP6SS_MEM_PWR_CTL); + val |= readl_relaxed(drv->reg_base + + QDSP6SS_MEM_PWR_CTL); + /* + * Wait for 1us for both memory peripheral and + * data array to turn on. + */ + udelay(1); + } + } else if (drv->qdsp6v56_1_8_inrush_current) { + /* Deassert QDSP6 compiler memory clamp */ + val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL); + val &= ~QDSP6v55_CLAMP_QMC_MEM; + writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL); + + /* Deassert memory peripheral sleep and L2 memory standby */ + val |= (Q6SS_L2DATA_STBY_N | Q6SS_SLP_RET_N); + writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL); + + /* Turn on L1, L2, ETB and JU memories 1 at a time */ + val = readl_relaxed(drv->reg_base + QDSP6SS_MEM_PWR_CTL); + for (i = 19; i >= 6; i--) { + val |= BIT(i); + writel_relaxed(val, drv->reg_base + + QDSP6SS_MEM_PWR_CTL); + /* + * Wait for 1us for both memory peripheral and + * data array to turn on. + */ + udelay(1); + } + + for (i = 0 ; i <= 5 ; i++) { + val |= BIT(i); + writel_relaxed(val, drv->reg_base + + QDSP6SS_MEM_PWR_CTL); + /* + * Wait for 1us for both memory peripheral and + * data array to turn on. + */ + udelay(1); + } + } else if (drv->qdsp6v61_1_1 || drv->qdsp6v62_1_2 || + drv->qdsp6v62_1_4 || drv->qdsp6v62_1_5) { + /* Deassert QDSP6 compiler memory clamp */ + val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL); + val &= ~QDSP6v55_CLAMP_QMC_MEM; + writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL); + + /* Deassert memory peripheral sleep and L2 memory standby */ + val |= (Q6SS_L2DATA_STBY_N | Q6SS_SLP_RET_N); + writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL); + + /* Turn on L1, L2, ETB and JU memories 1 at a time */ + val = readl_relaxed(drv->reg_base + + QDSP6V6SS_MEM_PWR_CTL); + + if (drv->qdsp6v62_1_4 || drv->qdsp6v62_1_5) + i = 29; + else + i = 28; + + for ( ; i >= 0; i--) { + val |= BIT(i); + writel_relaxed(val, drv->reg_base + + QDSP6V6SS_MEM_PWR_CTL); + /* + * Wait for 1us for both memory peripheral and + * data array to turn on. + */ + udelay(1); + } + } else { + /* Turn on memories. */ + val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL); + val |= 0xFFF00; + writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL); + + /* Turn on L2 banks 1 at a time */ + for (i = 0; i <= 7; i++) { + val |= BIT(i); + writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL); + } + } + + /* Remove word line clamp */ + val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL); + val &= ~QDSP6v55_CLAMP_WL; + writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL); + + /* Remove IO clamp */ + val &= ~Q6SS_CLAMP_IO; + writel_relaxed(val, drv->reg_base + QDSP6SS_PWR_CTL); + + /* Bring core out of reset */ + val = readl_relaxed(drv->reg_base + QDSP6SS_RESET); + val &= ~(Q6SS_CORE_ARES | Q6SS_STOP_CORE); + writel_relaxed(val, drv->reg_base + QDSP6SS_RESET); + + /* Turn on core clock */ + val = readl_relaxed(drv->reg_base + QDSP6SS_GFMUX_CTL); + val |= Q6SS_CLK_ENA; + writel_relaxed(val, drv->reg_base + QDSP6SS_GFMUX_CTL); + + return 0; +} + +int pil_q6v5_reset(struct pil_desc *pil) +{ + struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc); + + + if (drv->qdsp6v65_1_0) + return __pil_q6v65_reset(pil); + else if (drv->qdsp6v55) + return __pil_q6v55_reset(pil); + else + return __pil_q6v5_reset(pil); +} +EXPORT_SYMBOL(pil_q6v5_reset); + +struct q6v5_data *pil_q6v5_init(struct platform_device *pdev) +{ + struct q6v5_data *drv; + struct resource *res; + struct pil_desc *desc; + struct property *prop; + int ret, vdd_pll; + + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return ERR_PTR(-ENOMEM); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qdsp6_base"); + drv->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(drv->reg_base)) + return drv->reg_base; + + desc = &drv->desc; + ret = of_property_read_string(pdev->dev.of_node, "qcom,firmware-name", + &desc->name); + if (ret) + return ERR_PTR(ret); + + desc->clear_fw_region = false; + desc->dev = &pdev->dev; + + drv->qdsp6v5_2_0 = of_device_is_compatible(pdev->dev.of_node, + "qcom,pil-femto-modem"); + + if (drv->qdsp6v5_2_0) + return drv; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "halt_base"); + if (res) { + drv->axi_halt_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!drv->axi_halt_base) { + dev_err(&pdev->dev, "Failed to map axi_halt_base.\n"); + return ERR_PTR(-ENOMEM); + } + } + + if (!drv->axi_halt_base) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "halt_q6"); + if (res) { + drv->axi_halt_q6 = devm_ioremap(&pdev->dev, + res->start, resource_size(res)); + if (!drv->axi_halt_q6) { + dev_err(&pdev->dev, "Failed to map axi_halt_q6.\n"); + return ERR_PTR(-ENOMEM); + } + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "halt_modem"); + if (res) { + drv->axi_halt_mss = devm_ioremap(&pdev->dev, + res->start, resource_size(res)); + if (!drv->axi_halt_mss) { + dev_err(&pdev->dev, "Failed to map axi_halt_mss.\n"); + return ERR_PTR(-ENOMEM); + } + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "halt_nc"); + if (res) { + drv->axi_halt_nc = devm_ioremap(&pdev->dev, + res->start, resource_size(res)); + if (!drv->axi_halt_nc) { + dev_err(&pdev->dev, "Failed to map axi_halt_nc.\n"); + return ERR_PTR(-ENOMEM); + } + } + } + + if (!(drv->axi_halt_base || (drv->axi_halt_q6 && drv->axi_halt_mss + && drv->axi_halt_nc))) { + dev_err(&pdev->dev, "halt bases for Q6 are not defined.\n"); + return ERR_PTR(-EINVAL); + } + + drv->qdsp6v55 = of_device_is_compatible(pdev->dev.of_node, + "qcom,pil-q6v55-mss"); + drv->qdsp6v56 = of_device_is_compatible(pdev->dev.of_node, + "qcom,pil-q6v56-mss"); + + drv->qdsp6v56_1_3 = of_property_read_bool(pdev->dev.of_node, + "qcom,qdsp6v56-1-3"); + drv->qdsp6v56_1_5 = of_property_read_bool(pdev->dev.of_node, + "qcom,qdsp6v56-1-5"); + + drv->qdsp6v56_1_8 = of_property_read_bool(pdev->dev.of_node, + "qcom,qdsp6v56-1-8"); + drv->qdsp6v56_1_10 = of_property_read_bool(pdev->dev.of_node, + "qcom,qdsp6v56-1-10"); + + drv->qdsp6v56_1_8_inrush_current = of_property_read_bool( + pdev->dev.of_node, + "qcom,qdsp6v56-1-8-inrush-current"); + + drv->qdsp6v61_1_1 = of_property_read_bool(pdev->dev.of_node, + "qcom,qdsp6v61-1-1"); + + drv->qdsp6v62_1_2 = of_property_read_bool(pdev->dev.of_node, + "qcom,qdsp6v62-1-2"); + + drv->qdsp6v62_1_4 = of_property_read_bool(pdev->dev.of_node, + "qcom,qdsp6v62-1-4"); + + drv->qdsp6v62_1_5 = of_property_read_bool(pdev->dev.of_node, + "qcom,qdsp6v62-1-5"); + + drv->qdsp6v65_1_0 = of_property_read_bool(pdev->dev.of_node, + "qcom,qdsp6v65-1-0"); + + drv->non_elf_image = of_property_read_bool(pdev->dev.of_node, + "qcom,mba-image-is-not-elf"); + + drv->override_acc = of_property_read_bool(pdev->dev.of_node, + "qcom,override-acc"); + + drv->ahb_clk_vote = of_property_read_bool(pdev->dev.of_node, + "qcom,ahb-clk-vote"); + drv->mx_spike_wa = of_property_read_bool(pdev->dev.of_node, + "qcom,mx-spike-wa"); + + drv->xo = devm_clk_get(&pdev->dev, "xo"); + if (IS_ERR(drv->xo)) + return ERR_CAST(drv->xo); + + if (of_property_read_bool(pdev->dev.of_node, "qcom,pnoc-clk-vote")) { + drv->pnoc_clk = devm_clk_get(&pdev->dev, "pnoc_clk"); + if (IS_ERR(drv->pnoc_clk)) + return ERR_CAST(drv->pnoc_clk); + } else { + drv->pnoc_clk = NULL; + } + + if (of_property_match_string(pdev->dev.of_node, + "qcom,proxy-clock-names", "qdss_clk") >= 0) { + drv->qdss_clk = devm_clk_get(&pdev->dev, "qdss_clk"); + if (IS_ERR(drv->qdss_clk)) + return ERR_CAST(drv->qdss_clk); + } else { + drv->qdss_clk = NULL; + } + + if (of_property_match_string(pdev->dev.of_node, + "qcom,proxy-clock-names", "prng_clk") >= 0) { + drv->prng_clk = devm_clk_get(&pdev->dev, "prng_clk"); + if (IS_ERR(drv->prng_clk)) + return ERR_CAST(drv->prng_clk); + } else { + drv->prng_clk = NULL; + } + + if (of_property_match_string(pdev->dev.of_node, + "qcom,proxy-clock-names", "axis2_clk") >= 0) { + drv->axis2_clk = devm_clk_get(&pdev->dev, "axis2_clk"); + if (IS_ERR(drv->axis2_clk)) + return ERR_CAST(drv->axis2_clk); + } else { + drv->axis2_clk = NULL; + } + + drv->vreg_cx = devm_regulator_get(&pdev->dev, "vdd_cx"); + if (IS_ERR(drv->vreg_cx)) + return ERR_CAST(drv->vreg_cx); + prop = of_find_property(pdev->dev.of_node, "vdd_cx-voltage", NULL); + if (!prop) { + dev_err(&pdev->dev, "Missing vdd_cx-voltage property\n"); + return ERR_CAST(prop); + } + + ret = of_property_read_u32(pdev->dev.of_node, "qcom,vdd_pll", + &vdd_pll); + if (!ret) { + drv->vreg_pll = devm_regulator_get(&pdev->dev, "vdd_pll"); + if (!IS_ERR_OR_NULL(drv->vreg_pll)) { + ret = regulator_set_voltage(drv->vreg_pll, vdd_pll, + vdd_pll); + if (ret) { + dev_err(&pdev->dev, "Failed to set vdd_pll voltage(rc:%d)\n", + ret); + return ERR_PTR(ret); + } + + ret = regulator_set_load(drv->vreg_pll, 10000); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to set vdd_pll mode(rc:%d)\n", + ret); + return ERR_PTR(ret); + } + } else + drv->vreg_pll = NULL; + } + + return drv; +} +EXPORT_SYMBOL(pil_q6v5_init); diff --git a/drivers/soc/qcom/pil-q6v5.h b/drivers/soc/qcom/pil-q6v5.h new file mode 100644 index 0000000000000000000000000000000000000000..01b1cefb4a38af1ca2b739286e8bcc25c89d89f8 --- /dev/null +++ b/drivers/soc/qcom/pil-q6v5.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + */ +#ifndef __MSM_PIL_Q6V5_H +#define __MSM_PIL_Q6V5_H + +#include "peripheral-loader.h" + +struct regulator; +struct clk; +struct pil_device; +struct platform_device; + +struct q6v5_data { + void __iomem *reg_base; + void __iomem *rmb_base; + void __iomem *cxrail_bhs; /* External BHS register */ + struct clk *xo; /* XO clock source */ + struct clk *pnoc_clk; /* PNOC bus clock source */ + struct clk *ahb_clk; /* PIL access to registers */ + struct clk *axi_clk; /* CPU access to memory */ + struct clk *core_clk; /* CPU core */ + struct clk *reg_clk; /* CPU access registers */ + struct clk *gpll0_mss_clk; /* GPLL0 to MSS connection */ + struct clk *rom_clk; /* Boot ROM */ + struct clk *snoc_axi_clk; + struct clk *mnoc_axi_clk; + struct clk *qdss_clk; + struct clk *prng_clk; + struct clk *axis2_clk; + void __iomem *axi_halt_base; /* Halt base of q6, mss, + * nc are in same 4K page + */ + void __iomem *axi_halt_q6; + void __iomem *axi_halt_mss; + void __iomem *axi_halt_nc; + void __iomem *restart_reg; + void __iomem *pdc_sync; + void __iomem *alt_reset; + struct regulator *vreg; + struct regulator *vreg_cx; + struct regulator *vreg_mx; + struct regulator *vreg_pll; + bool is_booted; + struct pil_desc desc; + bool self_auth; + phys_addr_t mba_dp_phys; + void *mba_dp_virt; + size_t mba_dp_size; + size_t dp_size; + bool qdsp6v55; + bool qdsp6v5_2_0; + bool qdsp6v56; + bool qdsp6v56_1_3; + bool qdsp6v56_1_5; + bool qdsp6v56_1_8; + bool qdsp6v56_1_8_inrush_current; + bool qdsp6v56_1_10; + bool qdsp6v61_1_1; + bool qdsp6v62_1_2; + bool qdsp6v62_1_4; + bool qdsp6v62_1_5; + bool qdsp6v65_1_0; + bool non_elf_image; + bool restart_reg_sec; + bool override_acc; + int override_acc_1; + int mss_pdc_offset; + int smem_id; + bool ahb_clk_vote; + bool mx_spike_wa; + bool reset_clk; +}; + +int pil_q6v5_make_proxy_votes(struct pil_desc *pil); +void pil_q6v5_remove_proxy_votes(struct pil_desc *pil); +void pil_q6v5_halt_axi_port(struct pil_desc *pil, void __iomem *halt_base); +void pil_q6v5_shutdown(struct pil_desc *pil); +int pil_q6v5_reset(struct pil_desc *pil); +void assert_clamps(struct pil_desc *pil); +struct q6v5_data *pil_q6v5_init(struct platform_device *pdev); + +#endif diff --git a/drivers/soc/qcom/qdss_bridge.c b/drivers/soc/qcom/qdss_bridge.c index 92af4dcb600755cc559ecb341ba8ca5e126f493f..2edd9af7fd24b67b005bdc53d08debba1a69ca2d 100644 --- a/drivers/soc/qcom/qdss_bridge.c +++ b/drivers/soc/qcom/qdss_bridge.c @@ -450,7 +450,7 @@ static void usb_notifier(void *priv, unsigned int event, struct qdss_bridge_drvdata *drvdata = priv; if (!drvdata || drvdata->mode != MHI_TRANSFER_TYPE_USB - || drvdata->opened == DISABLE) { + || drvdata->opened != ENABLE) { pr_err_ratelimited("%s can't be called in invalid status.\n", __func__); return; @@ -530,6 +530,7 @@ static void qdss_bridge_open_work_fn(struct work_struct *work) return; err: + drvdata->opened = DISABLE; mhi_unprepare_from_transfer(drvdata->mhi_dev); mhi_ch_close(drvdata); err_open: diff --git a/drivers/soc/qcom/qmi_rmnet.c b/drivers/soc/qcom/qmi_rmnet.c index 4e20ba85856c186c3473b6f1f17d0e6ec598ae99..739a0ef329a0265e34e0f4f114742b7e21e8fa3f 100644 --- a/drivers/soc/qcom/qmi_rmnet.c +++ b/drivers/soc/qcom/qmi_rmnet.c @@ -1139,7 +1139,7 @@ void qmi_rmnet_work_init(void *port) return; rmnet_ps_wq = alloc_workqueue("rmnet_powersave_work", - WQ_MEM_RECLAIM | WQ_CPU_INTENSIVE, 1); + WQ_CPU_INTENSIVE, 1); if (!rmnet_ps_wq) return; diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index ceedbffeb6bedc1f576d3c45768df7e9be605da8..e7e882e809486d37b2689dae96d5555edf6c053b 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -920,3 +920,6 @@ static int __init rpmh_driver_init(void) return platform_driver_register(&rpmh_driver); } arch_initcall(rpmh_driver_init); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Qualcomm RPM-Hardened (RPMH) Communication driver"); diff --git a/drivers/soc/qcom/rpmh_master_stat.c b/drivers/soc/qcom/rpmh_master_stat.c index 5403a0c1e90a3eed49a95565abefbda149538d30..d4418baaa1aba66e60d30b4e8a329e97934e1452 100644 --- a/drivers/soc/qcom/rpmh_master_stat.c +++ b/drivers/soc/qcom/rpmh_master_stat.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "%s: " fmt, KBUILD_MODNAME @@ -126,7 +126,6 @@ static ssize_t msm_rpmh_master_stats_show(struct kobject *kobj, { ssize_t length; int i = 0; - size_t size = 0; struct msm_rpmh_master_stats *record = NULL; mutex_lock(&rpmh_stats_mutex); @@ -141,7 +140,7 @@ static ssize_t msm_rpmh_master_stats_show(struct kobject *kobj, for (i = 0; i < ARRAY_SIZE(rpmh_masters); i++) { record = (struct msm_rpmh_master_stats *) qcom_smem_get( rpmh_masters[i].pid, - rpmh_masters[i].smem_id, &size); + rpmh_masters[i].smem_id, NULL); if (!IS_ERR_OR_NULL(record) && (PAGE_SIZE - length > 0)) length += msm_rpmh_master_stats_print_data( buf + length, PAGE_SIZE - length, diff --git a/drivers/soc/qcom/smp2p.c b/drivers/soc/qcom/smp2p.c index b8585d13838ae27813e10b5be5ea92c665ee031c..09e549d100bb70ee0825d26f29d5be182ed88de1 100644 --- a/drivers/soc/qcom/smp2p.c +++ b/drivers/soc/qcom/smp2p.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2015, Sony Mobile Communications AB. - * Copyright (c) 2012-2013, 2018-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2013, 2018-2020 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -161,7 +161,7 @@ struct qcom_smp2p { struct regmap *ipc_regmap; int ipc_offset; int ipc_bit; - struct wakeup_source ws; + struct wakeup_source *ws; struct mbox_client mbox_client; struct mbox_chan *mbox_chan; @@ -303,7 +303,7 @@ static irqreturn_t qcom_smp2p_isr(int irq, void *data) { struct qcom_smp2p *smp2p = data; - __pm_stay_awake(&smp2p->ws); + __pm_stay_awake(smp2p->ws); return IRQ_WAKE_THREAD; } @@ -351,7 +351,7 @@ static irqreturn_t qcom_smp2p_intr(int irq, void *data) } out: - __pm_relax(&smp2p->ws); + __pm_relax(smp2p->ws); return IRQ_HANDLED; } @@ -648,7 +648,12 @@ static int qcom_smp2p_probe(struct platform_device *pdev) list_add(&entry->node, &smp2p->outbound); } } - wakeup_source_init(&smp2p->ws, "smp2p"); + + smp2p->ws = wakeup_source_register(&pdev->dev, "smp2p"); + if (!smp2p->ws) { + ret = -ENOMEM; + goto unwind_interfaces; + } /* Kick the outgoing edge after allocating entries */ qcom_smp2p_kick(smp2p); @@ -659,12 +664,15 @@ static int qcom_smp2p_probe(struct platform_device *pdev) "smp2p", (void *)smp2p); if (ret) { dev_err(&pdev->dev, "failed to request interrupt\n"); - goto unwind_interfaces; + goto unreg_ws; } enable_irq_wake(smp2p->irq); return 0; +unreg_ws: + wakeup_source_unregister(smp2p->ws); + unwind_interfaces: list_for_each_entry(entry, &smp2p->inbound, node) irq_domain_remove(entry->domain); @@ -689,6 +697,8 @@ static int qcom_smp2p_remove(struct platform_device *pdev) struct qcom_smp2p *smp2p = platform_get_drvdata(pdev); struct smp2p_entry *entry; + wakeup_source_unregister(smp2p->ws); + list_for_each_entry(entry, &smp2p->inbound, node) irq_domain_remove(entry->domain); diff --git a/drivers/soc/qcom/smp2p_sleepstate.c b/drivers/soc/qcom/smp2p_sleepstate.c index f090415a9a8a00d4241ac3932871e91db43de78f..eeae932ab91ebad708d019e3bee63d765cfd6c4b 100644 --- a/drivers/soc/qcom/smp2p_sleepstate.c +++ b/drivers/soc/qcom/smp2p_sleepstate.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2014-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2020, The Linux Foundation. All rights reserved. */ #include #include @@ -15,7 +15,7 @@ #define PROC_AWAKE_ID 12 /* 12th bit */ #define AWAKE_BIT BIT(PROC_AWAKE_ID) static struct qcom_smem_state *state; -static struct wakeup_source notify_ws; +static struct wakeup_source *notify_ws; /** * sleepstate_pm_notifier() - PM notifier callback function. @@ -49,7 +49,7 @@ static struct notifier_block sleepstate_pm_nb = { static irqreturn_t smp2p_sleepstate_handler(int irq, void *ctxt) { - __pm_wakeup_event(¬ify_ws, 200); + __pm_wakeup_event(notify_ws, 200); return IRQ_HANDLED; } @@ -70,7 +70,12 @@ static int smp2p_sleepstate_probe(struct platform_device *pdev) dev_err(dev, "%s: power state notif error %d\n", __func__, ret); return ret; } - wakeup_source_init(¬ify_ws, "smp2p-sleepstate"); + + notify_ws = wakeup_source_register(&pdev->dev, "smp2p-sleepstate"); + if (!notify_ws) { + return -ENOMEM; + goto err_ws; + } irq = of_irq_get_byname(node, "smp2p-sleepstate-in"); if (irq <= 0) { @@ -89,7 +94,8 @@ static int smp2p_sleepstate_probe(struct platform_device *pdev) } return 0; err: - wakeup_source_trash(¬ify_ws); + wakeup_source_unregister(notify_ws); +err_ws: unregister_pm_notifier(&sleepstate_pm_nb); return ret; } diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index 9d24d3cdd5e7e4ef1c0ed57c6caa5984fe8051b4..217b577e60bc9358158cc184ba2af03856a95778 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -339,6 +339,7 @@ static struct msm_soc_info cpu_of_id[] = { /* Scuba ID */ [441] = {MSM_CPU_SCUBA, "SCUBA"}, + [471] = {MSM_CPU_SCUBA, "SCUBA"}, /* Uninitialized IDs are not known to run Linux. * MSM_CPU_UNKNOWN is set to 0 to ensure these IDs are diff --git a/drivers/soc/qcom/subsystem_restart.c b/drivers/soc/qcom/subsystem_restart.c index f8fb03488aeaefe42bd9eb5d44e482cde1f09831..69f7aa599ee0be1c19a7788022c57d447c3da9e7 100644 --- a/drivers/soc/qcom/subsystem_restart.c +++ b/drivers/soc/qcom/subsystem_restart.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2011-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2011-2020, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "subsys-restart: %s(): " fmt, __func__ @@ -181,7 +181,7 @@ struct restart_log { struct subsys_device { struct subsys_desc *desc; struct work_struct work; - struct wakeup_source ssr_wlock; + struct wakeup_source *ssr_wlock; char wlname[64]; struct work_struct device_restart_work; struct subsys_tracking track; @@ -1159,7 +1159,7 @@ static void subsystem_restart_wq_func(struct work_struct *work) spin_lock_irqsave(&track->s_lock, flags); track->p_state = SUBSYS_NORMAL; - __pm_relax(&dev->ssr_wlock); + __pm_relax(dev->ssr_wlock); spin_unlock_irqrestore(&track->s_lock, flags); } @@ -1183,7 +1183,7 @@ static void __subsystem_restart_dev(struct subsys_device *dev) dev->track.state == SUBSYS_ONLINE) { if (track->p_state != SUBSYS_RESTARTING) { track->p_state = SUBSYS_CRASHED; - __pm_stay_awake(&dev->ssr_wlock); + __pm_stay_awake(dev->ssr_wlock); queue_work(ssr_wq, &dev->work); } else { panic("Subsystem %s crashed during SSR!", name); @@ -1251,7 +1251,7 @@ int subsystem_restart_dev(struct subsys_device *dev) __subsystem_restart_dev(dev); break; case RESET_SOC: - __pm_stay_awake(&dev->ssr_wlock); + __pm_stay_awake(dev->ssr_wlock); schedule_work(&dev->device_restart_work); return 0; default: @@ -1415,7 +1415,7 @@ static void subsys_device_release(struct device *dev) { struct subsys_device *subsys = to_subsys(dev); - wakeup_source_trash(&subsys->ssr_wlock); + wakeup_source_unregister(subsys->ssr_wlock); mutex_destroy(&subsys->track.lock); ida_simple_remove(&subsys_ida, subsys->id); kfree(subsys); @@ -1830,7 +1830,14 @@ struct subsys_device *subsys_register(struct subsys_desc *desc) subsys->early_notify = subsys_get_early_notif_info(desc->name); snprintf(subsys->wlname, sizeof(subsys->wlname), "ssr(%s)", desc->name); - wakeup_source_init(&subsys->ssr_wlock, subsys->wlname); + + subsys->ssr_wlock = + wakeup_source_register(&subsys->dev, subsys->wlname); + if (!subsys->ssr_wlock) { + kfree(subsys); + return ERR_PTR(-ENOMEM); + } + INIT_WORK(&subsys->work, subsystem_restart_wq_func); INIT_WORK(&subsys->device_restart_work, device_restart_work_hdlr); spin_lock_init(&subsys->track.s_lock); @@ -1838,7 +1845,7 @@ struct subsys_device *subsys_register(struct subsys_desc *desc) subsys->id = ida_simple_get(&subsys_ida, 0, 0, GFP_KERNEL); if (subsys->id < 0) { - wakeup_source_trash(&subsys->ssr_wlock); + wakeup_source_unregister(subsys->ssr_wlock); ret = subsys->id; kfree(subsys); return ERR_PTR(ret); diff --git a/drivers/soc/qcom/watchdog_v2.c b/drivers/soc/qcom/watchdog_v2.c index 7b8485ed2d5e77a76f49f1aeceb394b8b8a28d55..301d06f0f8cfe19edaf8a68314f1e769dd27474d 100644 --- a/drivers/soc/qcom/watchdog_v2.c +++ b/drivers/soc/qcom/watchdog_v2.c @@ -7,6 +7,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -28,6 +31,10 @@ #include #include #include +#ifdef CONFIG_QCOM_INITIAL_LOGBUF +#include +#include +#endif #define MODULE_NAME "msm_watchdog" #define WDT0_ACCSCSSNBARK_INT 0 @@ -47,11 +54,30 @@ #define SCM_SET_REGSAVE_CMD 0x2 #define SCM_SVC_SEC_WDOG_DIS 0x7 #define MAX_CPU_CTX_SIZE 2048 +#define NR_TOP_HITTERS 10 +#define COMPARE_RET -1 + +typedef int (*compare_t) (const void *lhs, const void *rhs); + +#ifdef CONFIG_QCOM_INITIAL_LOGBUF +#define LOGBUF_TIMEOUT 100000U + +static struct delayed_work log_buf_work; +static char *init_log_buf; +static unsigned int *log_buf_size; +static dma_addr_t log_buf_paddr; +#endif static struct msm_watchdog_data *wdog_data; static int cpu_idle_pc_state[NR_CPUS]; +struct irq_info { + unsigned int irq; + unsigned int total_count; + unsigned int irq_counter[NR_CPUS]; +}; + /* * user_pet_enable: * Require userspace to write to a sysfs file every pet_time milliseconds. @@ -92,6 +118,11 @@ struct msm_watchdog_data { unsigned long long thread_start; unsigned long long ping_start[NR_CPUS]; unsigned long long ping_end[NR_CPUS]; + struct work_struct irq_counts_work; + struct irq_info irq_counts[NR_TOP_HITTERS]; + struct irq_info ipi_counts[NR_IPI]; + unsigned int tot_irq_count[NR_CPUS]; + atomic_t irq_counts_running; }; /* @@ -404,6 +435,183 @@ static void pet_task_wakeup(struct timer_list *t) wake_up(&wdog_dd->pet_complete); } +static int cmp_irq_info_fn(const void *a, const void *b) +{ + struct irq_info *lhs = (struct irq_info *)a; + struct irq_info *rhs = (struct irq_info *)b; + + if (lhs->total_count < rhs->total_count) + return 1; + + if (lhs->total_count > rhs->total_count) + return COMPARE_RET; + + return 0; +} + +static void swap_irq_info_fn(void *a, void *b, int size) +{ + struct irq_info temp; + struct irq_info *lhs = (struct irq_info *)a; + struct irq_info *rhs = (struct irq_info *)b; + + temp = *lhs; + *lhs = *rhs; + *rhs = temp; +} + +static struct irq_info *search(struct irq_info *key, struct irq_info *base, + size_t num, compare_t cmp) +{ + struct irq_info *pivot; + int result; + + while (num > 0) { + pivot = base + (num >> 1); + result = cmp(key, pivot); + + if (result == 0) + goto out; + + if (result > 0) { + base = pivot + 1; + num--; + } + + if (num) + num >>= 1; + } + +out: + return pivot; +} + +static void print_irq_stat(struct msm_watchdog_data *wdog_dd) +{ + int index; + int cpu; + struct irq_info *info; + + + pr_info("(virq:irq_count)- "); + for (index = 0; index < NR_TOP_HITTERS; index++) { + info = &wdog_dd->irq_counts[index]; + pr_cont("%u:%u ", info->irq, info->total_count); + } + pr_cont("\n"); + + pr_info("(cpu:irq_count)- "); + for_each_possible_cpu(cpu) + pr_cont("%u:%u ", cpu, wdog_dd->tot_irq_count[cpu]); + pr_cont("\n"); + + pr_info("(ipi:irq_count)- "); + for (index = 0; index < NR_IPI; index++) { + info = &wdog_dd->ipi_counts[index]; + pr_cont("%u:%u ", info->irq, info->total_count); + } + pr_cont("\n"); +} + +static void compute_irq_stat(struct work_struct *work) +{ + unsigned int count; + int index = 0, cpu, irq; + struct irq_desc *desc; + struct irq_info *pos; + struct irq_info *start; + struct irq_info key = {0}; + unsigned int running; + struct msm_watchdog_data *wdog_dd = container_of(work, + struct msm_watchdog_data, + irq_counts_work); + + size_t arr_size = ARRAY_SIZE(wdog_dd->irq_counts); + + /* avoid parallel execution from bark handler and queued + * irq_counts_work. + */ + running = atomic_xchg(&wdog_dd->irq_counts_running, 1); + if (running) + return; + + /* per irq counts */ + rcu_read_lock(); + for_each_irq_nr(irq) { + desc = irq_to_desc(irq); + if (!desc) + continue; + + count = kstat_irqs_usr(irq); + if (!count) + continue; + + if (index < arr_size) { + wdog_dd->irq_counts[index].irq = irq; + wdog_dd->irq_counts[index].total_count = count; + for_each_possible_cpu(cpu) + wdog_dd->irq_counts[index].irq_counter[cpu] = + *per_cpu_ptr(desc->kstat_irqs, cpu); + + index++; + if (index == arr_size) + sort(wdog_dd->irq_counts, arr_size, + sizeof(*pos), cmp_irq_info_fn, + swap_irq_info_fn); + + continue; + } + + key.total_count = count; + start = wdog_dd->irq_counts + (arr_size - 1); + pos = search(&key, wdog_dd->irq_counts, + arr_size, cmp_irq_info_fn); + pr_debug("*pos:%u key:%u\n", + pos->total_count, key.total_count); + if (pos->total_count >= key.total_count) { + if (pos < start) + pos++; + else + pos = NULL; + } + + pr_debug("count :%u irq:%u\n", count, irq); + if (pos && pos < start) { + start--; + for (; start >= pos ; start--) + *(start + 1) = *start; + } + + if (pos) { + pos->irq = irq; + pos->total_count = count; + for_each_possible_cpu(cpu) + pos->irq_counter[cpu] = + *per_cpu_ptr(desc->kstat_irqs, cpu); + } + } + rcu_read_unlock(); + + /* per cpu total irq counts */ + for_each_possible_cpu(cpu) + wdog_dd->tot_irq_count[cpu] = kstat_cpu_irqs_sum(cpu); + + /* per IPI counts */ + for (index = 0; index < NR_IPI; index++) { + wdog_dd->ipi_counts[index].total_count = 0; + wdog_dd->ipi_counts[index].irq = index; + for_each_possible_cpu(cpu) { + wdog_dd->ipi_counts[index].irq_counter[cpu] = + __IRQ_STAT(cpu, ipi_irqs[index]); + wdog_dd->ipi_counts[index].total_count += + wdog_dd->ipi_counts[index].irq_counter[cpu]; + } + } + + print_irq_stat(wdog_dd); + atomic_xchg(&wdog_dd->irq_counts_running, 0); +} + static __ref int watchdog_kthread(void *arg) { struct msm_watchdog_data *wdog_dd = @@ -442,6 +650,7 @@ static __ref int watchdog_kthread(void *arg) * Could have been changed on other cpu */ mod_timer(&wdog_dd->pet_timer, jiffies + delay_time); + queue_work(system_unbound_wq, &wdog_dd->irq_counts_work); } return 0; } @@ -470,6 +679,17 @@ static struct notifier_block wdog_cpu_pm_nb = { .notifier_call = wdog_cpu_pm_notify, }; +#ifdef CONFIG_QCOM_INITIAL_LOGBUF +static void log_buf_remove(void) +{ + flush_delayed_work(&log_buf_work); + dma_free_coherent(wdog_data->dev, *log_buf_size, + init_log_buf, log_buf_paddr); +} +#else +static void log_buf_remove(void) { return; } +#endif + static int msm_watchdog_remove(struct platform_device *pdev) { struct msm_watchdog_data *wdog_dd = @@ -489,6 +709,8 @@ static int msm_watchdog_remove(struct platform_device *pdev) dev_info(wdog_dd->dev, "MSM Watchdog Exit - Deactivated\n"); del_timer_sync(&wdog_dd->pet_timer); kthread_stop(wdog_dd->watchdog_task); + flush_work(&wdog_dd->irq_counts_work); + log_buf_remove(); kfree(wdog_dd); return 0; } @@ -497,6 +719,8 @@ void msm_trigger_wdog_bite(void) { if (!wdog_data) return; + + compute_irq_stat(&wdog_data->irq_counts_work); pr_info("Causing a watchdog bite!"); __raw_writel(1, wdog_data->base + WDT0_BITE_TIME); /* Mke sure bite time is written before we reset */ @@ -568,6 +792,65 @@ static int init_watchdog_sysfs(struct msm_watchdog_data *wdog_dd) return error; } +#ifdef CONFIG_QCOM_INITIAL_LOGBUF +static void minidump_reg_init_log_buf(void) +{ + struct md_region md_entry; + + /* Register init_log_buf info to minidump table */ + strlcpy(md_entry.name, "KBOOT_LOG", sizeof(md_entry.name)); + md_entry.virt_addr = (uintptr_t)init_log_buf; + md_entry.phys_addr = log_buf_paddr; + md_entry.size = *log_buf_size; + md_entry.id = MINIDUMP_DEFAULT_ID; + if (msm_minidump_add_region(&md_entry)) + pr_err("Failed to add init_log_buf in Minidump\n"); +} + +static void log_buf_work_fn(struct work_struct *work) +{ + char **addr = NULL; + + addr = (char **)kallsyms_lookup_name("log_buf"); + if (!addr) { + dev_err(wdog_data->dev, "log_buf symbol not found\n"); + goto out; + } + + log_buf_size = (unsigned int *)kallsyms_lookup_name("log_buf_len"); + if (!log_buf_size) { + dev_err(wdog_data->dev, "log_buf_len symbol not found\n"); + goto out; + } + + init_log_buf = dma_alloc_coherent(wdog_data->dev, *log_buf_size, + &log_buf_paddr, GFP_KERNEL); + if (!init_log_buf) { + dev_err(wdog_data->dev, "log_buf dma_alloc_coherent failed\n"); + goto out; + } + + minidump_reg_init_log_buf(); + memcpy(init_log_buf, *addr, (size_t)(*log_buf_size)); + pr_info("boot log copy done\n"); +out: + return; +} + +static void log_buf_init(void) +{ + /* keep granularity of milli seconds */ + unsigned int curr_time_msec = div_u64(sched_clock(), NSEC_PER_MSEC); + unsigned int timeout_msec = LOGBUF_TIMEOUT - curr_time_msec; + + INIT_DELAYED_WORK(&log_buf_work, log_buf_work_fn); + queue_delayed_work(system_unbound_wq, &log_buf_work, + msecs_to_jiffies(timeout_msec)); +} +#else +static void log_buf_init(void) { return; } +#endif + static void init_watchdog_data(struct msm_watchdog_data *wdog_dd) { unsigned long delay_time; @@ -606,6 +889,9 @@ static void init_watchdog_data(struct msm_watchdog_data *wdog_dd) return; } } + + INIT_WORK(&wdog_dd->irq_counts_work, compute_irq_stat); + atomic_set(&wdog_dd->irq_counts_running, 0); delay_time = msecs_to_jiffies(wdog_dd->pet_time); wdog_dd->min_slack_ticks = UINT_MAX; wdog_dd->min_slack_ns = ULLONG_MAX; @@ -755,6 +1041,8 @@ static int msm_watchdog_probe(struct platform_device *pdev) } init_watchdog_data(wdog_dd); + log_buf_init(); + /* Add wdog info to minidump table */ strlcpy(md_entry.name, "KWDOGDATA", sizeof(md_entry.name)); md_entry.virt_addr = (uintptr_t)wdog_dd; diff --git a/drivers/soc/tegra/fuse/fuse-tegra30.c b/drivers/soc/tegra/fuse/fuse-tegra30.c index 257e254c6137f806e6cf77112184ce4796bf0257..0ec6385eb15e6842deb0f53344d034aa1555252d 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra30.c +++ b/drivers/soc/tegra/fuse/fuse-tegra30.c @@ -47,7 +47,8 @@ defined(CONFIG_ARCH_TEGRA_124_SOC) || \ defined(CONFIG_ARCH_TEGRA_132_SOC) || \ defined(CONFIG_ARCH_TEGRA_210_SOC) || \ - defined(CONFIG_ARCH_TEGRA_186_SOC) + defined(CONFIG_ARCH_TEGRA_186_SOC) || \ + defined(CONFIG_ARCH_TEGRA_194_SOC) static u32 tegra30_fuse_read_early(struct tegra_fuse *fuse, unsigned int offset) { if (WARN_ON(!fuse->base)) diff --git a/drivers/soc/tegra/fuse/tegra-apbmisc.c b/drivers/soc/tegra/fuse/tegra-apbmisc.c index e5a4d8f98b10e5df1dc1ed292d7bef5d866ec1eb..d1cbb0fe1691ae50e5d1dbf7f76f52c5fcb301ce 100644 --- a/drivers/soc/tegra/fuse/tegra-apbmisc.c +++ b/drivers/soc/tegra/fuse/tegra-apbmisc.c @@ -135,7 +135,7 @@ void __init tegra_init_apbmisc(void) apbmisc.flags = IORESOURCE_MEM; /* strapping options */ - if (tegra_get_chip_id() == TEGRA124) { + if (of_machine_is_compatible("nvidia,tegra124")) { straps.start = 0x7000e864; straps.end = 0x7000e867; } else { diff --git a/drivers/soc/ti/wkup_m3_ipc.c b/drivers/soc/ti/wkup_m3_ipc.c index f5cb8c0af09f3ab8ade4443ada270bf1f1eefa92..c1fda6acb670ab51c4ba1d55f40f02057c9623a4 100644 --- a/drivers/soc/ti/wkup_m3_ipc.c +++ b/drivers/soc/ti/wkup_m3_ipc.c @@ -426,6 +426,8 @@ static void wkup_m3_rproc_boot_thread(struct wkup_m3_ipc *m3_ipc) ret = rproc_boot(m3_ipc->rproc); if (ret) dev_err(dev, "rproc_boot failed\n"); + else + m3_ipc_state = m3_ipc; do_exit(0); } @@ -512,8 +514,6 @@ static int wkup_m3_ipc_probe(struct platform_device *pdev) goto err_put_rproc; } - m3_ipc_state = m3_ipc; - return 0; err_put_rproc: diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 2fb43c582559de0b9e1b8571fcf015246ca0b374..5a9d7e252a77c406132faf1c82607f8291d7900b 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -305,7 +305,6 @@ struct atmel_spi { bool use_cs_gpios; bool keep_cs; - bool cs_active; u32 fifo_size; }; @@ -1381,11 +1380,9 @@ static int atmel_spi_one_transfer(struct spi_master *master, &msg->transfers)) { as->keep_cs = true; } else { - as->cs_active = !as->cs_active; - if (as->cs_active) - cs_activate(as, msg->spi); - else - cs_deactivate(as, msg->spi); + cs_deactivate(as, msg->spi); + udelay(10); + cs_activate(as, msg->spi); } } @@ -1408,7 +1405,6 @@ static int atmel_spi_transfer_one_message(struct spi_master *master, atmel_spi_lock(as); cs_activate(as, spi); - as->cs_active = true; as->keep_cs = false; msg->status = 0; diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c index 584bcb018a62db6743eb2028dd29e15b178c734f..285a6f463013b0b2a919e92ac063b23359dc8bf3 100644 --- a/drivers/spi/spi-bcm-qspi.c +++ b/drivers/spi/spi-bcm-qspi.c @@ -354,7 +354,7 @@ static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi, { int bpc = 0, bpp = 0; u8 command = op->cmd.opcode; - int width = op->cmd.buswidth ? op->cmd.buswidth : SPI_NBITS_SINGLE; + int width = op->data.buswidth ? op->data.buswidth : SPI_NBITS_SINGLE; int addrlen = op->addr.nbytes; int flex_mode = 1; @@ -992,7 +992,7 @@ static int bcm_qspi_exec_mem_op(struct spi_mem *mem, if (mspi_read) return bcm_qspi_mspi_exec_mem_op(spi, op); - ret = bcm_qspi_bspi_set_mode(qspi, op, -1); + ret = bcm_qspi_bspi_set_mode(qspi, op, 0); if (!ret) ret = bcm_qspi_bspi_exec_mem_op(spi, op); diff --git a/drivers/spi/spi-bcm2835aux.c b/drivers/spi/spi-bcm2835aux.c index 12c1fa5b06c5b6e3da0028e6ab4f72d5b2f5447c..c63ed402cf860ad00a65bad29975a614137ca1b1 100644 --- a/drivers/spi/spi-bcm2835aux.c +++ b/drivers/spi/spi-bcm2835aux.c @@ -416,7 +416,18 @@ static int bcm2835aux_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, master); master->mode_bits = (SPI_CPOL | SPI_CS_HIGH | SPI_NO_CS); master->bits_per_word_mask = SPI_BPW_MASK(8); - master->num_chipselect = -1; + /* even though the driver never officially supported native CS + * allow a single native CS for legacy DT support purposes when + * no cs-gpio is configured. + * Known limitations for native cs are: + * * multiple chip-selects: cs0-cs2 are all simultaniously asserted + * whenever there is a transfer - this even includes SPI_NO_CS + * * SPI_CS_HIGH: is ignores - cs are always asserted low + * * cs_change: cs is deasserted after each spi_transfer + * * cs_delay_usec: cs is always deasserted one SCK cycle after + * a spi_transfer + */ + master->num_chipselect = 1; master->transfer_one = bcm2835aux_spi_transfer_one; master->handle_err = bcm2835aux_spi_handle_err; master->prepare_message = bcm2835aux_spi_prepare_message; diff --git a/drivers/spi/spi-bcm63xx-hsspi.c b/drivers/spi/spi-bcm63xx-hsspi.c index 9a06ffdb73b88641e68da218e63f59ba15704ebf..1669c554ea340f47d0a40f218d5ef9ce4e89ddd8 100644 --- a/drivers/spi/spi-bcm63xx-hsspi.c +++ b/drivers/spi/spi-bcm63xx-hsspi.c @@ -371,7 +371,6 @@ static int bcm63xx_hsspi_probe(struct platform_device *pdev) goto out_disable_clk; rate = clk_get_rate(pll_clk); - clk_disable_unprepare(pll_clk); if (!rate) { ret = -EINVAL; goto out_disable_pll_clk; diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c index 7c88f74f7f4701a68091fef91c4ad785cd55179a..94cc0a152449f72c9061a1f74696fd1f39ad279c 100644 --- a/drivers/spi/spi-cadence.c +++ b/drivers/spi/spi-cadence.c @@ -584,11 +584,6 @@ static int cdns_spi_probe(struct platform_device *pdev) goto clk_dis_apb; } - pm_runtime_use_autosuspend(&pdev->dev); - pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT); - pm_runtime_set_active(&pdev->dev); - pm_runtime_enable(&pdev->dev); - ret = of_property_read_u32(pdev->dev.of_node, "num-cs", &num_cs); if (ret < 0) master->num_chipselect = CDNS_SPI_DEFAULT_NUM_CS; @@ -603,8 +598,10 @@ static int cdns_spi_probe(struct platform_device *pdev) /* SPI controller initializations */ cdns_spi_init_hw(xspi); - pm_runtime_mark_last_busy(&pdev->dev); - pm_runtime_put_autosuspend(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT); irq = platform_get_irq(pdev, 0); if (irq <= 0) { diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index ac2eb89ef7a5a0616fe915ec96453ac441f589be..5a47e28e38c16c206d94af2cb0cb8079c44ee7d0 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -179,9 +179,11 @@ static inline u32 rx_max(struct dw_spi *dws) static void dw_writer(struct dw_spi *dws) { - u32 max = tx_max(dws); + u32 max; u16 txw = 0; + spin_lock(&dws->buf_lock); + max = tx_max(dws); while (max--) { /* Set the tx word if the transfer's original "tx" is not null */ if (dws->tx_end - dws->len) { @@ -193,13 +195,16 @@ static void dw_writer(struct dw_spi *dws) dw_write_io_reg(dws, DW_SPI_DR, txw); dws->tx += dws->n_bytes; } + spin_unlock(&dws->buf_lock); } static void dw_reader(struct dw_spi *dws) { - u32 max = rx_max(dws); + u32 max; u16 rxw; + spin_lock(&dws->buf_lock); + max = rx_max(dws); while (max--) { rxw = dw_read_io_reg(dws, DW_SPI_DR); /* Care rx only if the transfer's original "rx" is not null */ @@ -211,6 +216,7 @@ static void dw_reader(struct dw_spi *dws) } dws->rx += dws->n_bytes; } + spin_unlock(&dws->buf_lock); } static void int_error_stop(struct dw_spi *dws, const char *msg) @@ -283,18 +289,20 @@ static int dw_spi_transfer_one(struct spi_controller *master, { struct dw_spi *dws = spi_controller_get_devdata(master); struct chip_data *chip = spi_get_ctldata(spi); + unsigned long flags; u8 imask = 0; u16 txlevel = 0; u32 cr0; int ret; dws->dma_mapped = 0; - + spin_lock_irqsave(&dws->buf_lock, flags); dws->tx = (void *)transfer->tx_buf; dws->tx_end = dws->tx + transfer->len; dws->rx = transfer->rx_buf; dws->rx_end = dws->rx + transfer->len; dws->len = transfer->len; + spin_unlock_irqrestore(&dws->buf_lock, flags); spi_enable_chip(dws, 0); @@ -485,6 +493,7 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) dws->type = SSI_MOTO_SPI; dws->dma_inited = 0; dws->dma_addr = (dma_addr_t)(dws->paddr + DW_SPI_DR); + spin_lock_init(&dws->buf_lock); spi_controller_set_devdata(master, dws); diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 0168b08364d5d1a83f598c870e297b452f15b952..20a09fe79ae7d029dcdf4161b766f66683f8db75 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -118,6 +118,7 @@ struct dw_spi { size_t len; void *tx; void *tx_end; + spinlock_t buf_lock; void *rx; void *rx_end; int dma_mapped; diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index 8b79e36fab21c478d673d6f654e4df590483da66..cd784552de7f15048537ddcd78cf43f8822bb12d 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -407,7 +407,6 @@ static int fsl_spi_do_one_msg(struct spi_master *master, } m->status = status; - spi_finalize_current_message(master); if (status || !cs_change) { ndelay(nsecs); @@ -415,6 +414,7 @@ static int fsl_spi_do_one_msg(struct spi_master *master, } fsl_spi_setup_transfer(spi, NULL); + spi_finalize_current_message(master); return 0; } diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index 00bfed4ddfbddf027bda2b383afdc362fd25e7db..89f27f4f39c742ef6dd00e1239c25b09bd2ec105 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -94,6 +94,16 @@ #define MAX_TX_SG (3) #define NUM_SPI_XFER (8) +/* SPI sampling registers */ +#define SE_GENI_CGC_CTRL (0x28) +#define SE_GENI_CFG_SEQ_START (0x84) +#define SE_GENI_CFG_REG108 (0x2B0) +#define SE_GENI_CFG_REG109 (0x2B4) +#define CPOL_CTRL_SHFT 1 +#define RX_IO_POS_FF_EN_SEL_SHFT 4 +#define RX_IO_EN2CORE_EN_DELAY_SHFT 8 +#define RX_SI_EN2IO_DELAY_SHFT 12 + struct gsi_desc_cb { struct spi_master *spi; struct spi_transfer *xfer; @@ -152,6 +162,8 @@ struct spi_geni_master { bool shared_ee; /* Dual EE use case */ bool dis_autosuspend; bool cmd_done; + bool set_miso_sampling; + u32 miso_sampling_ctrl_val; }; static struct spi_master *get_spi_master(struct device *dev) @@ -799,6 +811,7 @@ static int spi_geni_prepare_transfer_hardware(struct spi_master *spi) int ret = 0, count = 0; u32 max_speed = spi->cur_msg->spi->max_speed_hz; struct se_geni_rsc *rsc = &mas->spi_rsc; + u32 cpol, cpha, cfg_reg108, cfg_reg109, cfg_seq_start; /* Adjust the IB based on the max speed of the slave.*/ rsc->ib = max_speed * DEFAULT_BUS_WIDTH; @@ -861,6 +874,7 @@ static int spi_geni_prepare_transfer_hardware(struct spi_master *spi) dev_info(mas->dev, "Failed to get rx DMA ch %ld\n", PTR_ERR(mas->rx)); dma_release_channel(mas->tx); + goto setup_ipc; } mas->gsi = devm_kzalloc(mas->dev, (sizeof(struct spi_geni_gsi) * NUM_SPI_XFER), @@ -918,6 +932,54 @@ static int spi_geni_prepare_transfer_hardware(struct spi_master *spi) "%s:Major:%d Minor:%d step:%dos%d\n", __func__, major, minor, step, mas->oversampling); } + + if (!mas->set_miso_sampling) + goto shared_se; + + cpol = geni_read_reg(mas->base, SE_SPI_CPOL); + cpha = geni_read_reg(mas->base, SE_SPI_CPHA); + cfg_reg108 = geni_read_reg(mas->base, SE_GENI_CFG_REG108); + cfg_reg109 = geni_read_reg(mas->base, SE_GENI_CFG_REG109); + /* clear CPOL bit */ + cfg_reg108 &= ~(1 << CPOL_CTRL_SHFT); + + if (major == 1 && minor == 0) { + /* Write 1 to RX_SI_EN2IO_DELAY reg */ + cfg_reg108 &= ~(0x7 << RX_SI_EN2IO_DELAY_SHFT); + cfg_reg108 |= (1 << RX_SI_EN2IO_DELAY_SHFT); + /* Write 0 to RX_IO_POS_FF_EN_SEL reg */ + cfg_reg108 &= ~(1 << RX_IO_POS_FF_EN_SEL_SHFT); + } else if ((major < 2) || (major == 2 && minor < 5)) { + /* Write 0 to RX_IO_EN2CORE_EN_DELAY reg */ + cfg_reg108 &= ~(0x7 << RX_IO_EN2CORE_EN_DELAY_SHFT); + } else { + /* + * Write miso_sampling_ctrl_set to + * RX_IO_EN2CORE_EN_DELAY reg + */ + cfg_reg108 &= ~(0x7 << RX_IO_EN2CORE_EN_DELAY_SHFT); + cfg_reg108 |= (mas->miso_sampling_ctrl_val << + RX_IO_EN2CORE_EN_DELAY_SHFT); + } + + geni_write_reg(cfg_reg108, mas->base, SE_GENI_CFG_REG108); + + if (cpol == 0 && cpha == 0) + cfg_reg109 = 1; + else if (cpol == 1 && cpha == 0) + cfg_reg109 = 0; + geni_write_reg(cfg_reg109, mas->base, + SE_GENI_CFG_REG109); + if (!(major == 1 && minor == 0)) + geni_write_reg(1, mas->base, SE_GENI_CFG_SEQ_START); + cfg_reg108 = geni_read_reg(mas->base, SE_GENI_CFG_REG108); + cfg_reg109 = geni_read_reg(mas->base, SE_GENI_CFG_REG109); + cfg_seq_start = geni_read_reg(mas->base, SE_GENI_CFG_SEQ_START); + + GENI_SE_DBG(mas->ipc, false, mas->dev, + "%s cfg108: 0x%x cfg109: 0x%x cfg_seq_start: 0x%x\n", + __func__, cfg_reg108, cfg_reg109, cfg_seq_start); +shared_se: mas->shared_se = (geni_read_reg(mas->base, GENI_IF_FIFO_DISABLE_RO) & FIFO_IF_DISABLE); @@ -1108,12 +1170,30 @@ static void handle_fifo_timeout(struct spi_geni_master *mas, "Failed to cancel/abort m_cmd\n"); } if (mas->cur_xfer_mode == SE_DMA) { - if (xfer->tx_buf) + if (xfer->tx_buf) { + reinit_completion(&mas->xfer_done); + writel_relaxed(1, mas->base + + SE_DMA_TX_FSM_RST); + timeout = + wait_for_completion_timeout(&mas->xfer_done, HZ); + if (!timeout) + dev_err(mas->dev, + "DMA TX RESET failed\n"); geni_se_tx_dma_unprep(mas->wrapper_dev, - xfer->tx_dma, xfer->len); - if (xfer->rx_buf) + xfer->tx_dma, xfer->len); + } + if (xfer->rx_buf) { + reinit_completion(&mas->xfer_done); + writel_relaxed(1, mas->base + + SE_DMA_RX_FSM_RST); + timeout = + wait_for_completion_timeout(&mas->xfer_done, HZ); + if (!timeout) + dev_err(mas->dev, + "DMA RX RESET failed\n"); geni_se_rx_dma_unprep(mas->wrapper_dev, - xfer->rx_dma, xfer->len); + xfer->rx_dma, xfer->len); + } } } @@ -1131,6 +1211,12 @@ static int spi_geni_transfer_one(struct spi_master *spi, return -EINVAL; } + /* Check for zero length transfer */ + if (xfer->len < 1) { + dev_err(mas->dev, "Zero length transfer\n"); + return -EINVAL; + } + if (mas->cur_xfer_mode != GSI_DMA) { reinit_completion(&mas->xfer_done); setup_fifo_xfer(xfer, mas, slv->mode, spi); @@ -1520,6 +1606,15 @@ static int spi_geni_probe(struct platform_device *pdev) of_property_read_bool(pdev->dev.of_node, "qcom,shared_ee"); + geni_mas->set_miso_sampling = of_property_read_bool(pdev->dev.of_node, + "qcom,set-miso-sampling"); + if (geni_mas->set_miso_sampling) { + if (!of_property_read_u32(pdev->dev.of_node, + "qcom,miso-sampling-ctrl-val", + &geni_mas->miso_sampling_ctrl_val)) + dev_info(&pdev->dev, "MISO_SAMPLING_SET: %d\n", + geni_mas->miso_sampling_ctrl_val); + } geni_mas->phys_addr = res->start; geni_mas->size = resource_size(res); geni_mas->base = devm_ioremap(&pdev->dev, res->start, diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c index eb72dba71d832a47165b824c2f859fed00a33a4e..62a7b80801d22098f24f8bc0711b7d5f63c557d6 100644 --- a/drivers/spi/spi-mem.c +++ b/drivers/spi/spi-mem.c @@ -12,6 +12,8 @@ #include "internals.h" +#define SPI_MEM_MAX_BUSWIDTH 4 + /** * spi_controller_dma_map_mem_op_data() - DMA-map the buffer attached to a * memory operation @@ -149,6 +151,44 @@ static bool spi_mem_default_supports_op(struct spi_mem *mem, } EXPORT_SYMBOL_GPL(spi_mem_default_supports_op); +static bool spi_mem_buswidth_is_valid(u8 buswidth) +{ + if (hweight8(buswidth) > 1 || buswidth > SPI_MEM_MAX_BUSWIDTH) + return false; + + return true; +} + +static int spi_mem_check_op(const struct spi_mem_op *op) +{ + if (!op->cmd.buswidth) + return -EINVAL; + + if ((op->addr.nbytes && !op->addr.buswidth) || + (op->dummy.nbytes && !op->dummy.buswidth) || + (op->data.nbytes && !op->data.buswidth)) + return -EINVAL; + + if (!spi_mem_buswidth_is_valid(op->cmd.buswidth) || + !spi_mem_buswidth_is_valid(op->addr.buswidth) || + !spi_mem_buswidth_is_valid(op->dummy.buswidth) || + !spi_mem_buswidth_is_valid(op->data.buswidth)) + return -EINVAL; + + return 0; +} + +static bool spi_mem_internal_supports_op(struct spi_mem *mem, + const struct spi_mem_op *op) +{ + struct spi_controller *ctlr = mem->spi->controller; + + if (ctlr->mem_ops && ctlr->mem_ops->supports_op) + return ctlr->mem_ops->supports_op(mem, op); + + return spi_mem_default_supports_op(mem, op); +} + /** * spi_mem_supports_op() - Check if a memory device and the controller it is * connected to support a specific memory operation @@ -166,12 +206,10 @@ EXPORT_SYMBOL_GPL(spi_mem_default_supports_op); */ bool spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op) { - struct spi_controller *ctlr = mem->spi->controller; - - if (ctlr->mem_ops && ctlr->mem_ops->supports_op) - return ctlr->mem_ops->supports_op(mem, op); + if (spi_mem_check_op(op)) + return false; - return spi_mem_default_supports_op(mem, op); + return spi_mem_internal_supports_op(mem, op); } EXPORT_SYMBOL_GPL(spi_mem_supports_op); @@ -196,7 +234,11 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) u8 *tmpbuf; int ret; - if (!spi_mem_supports_op(mem, op)) + ret = spi_mem_check_op(op); + if (ret) + return ret; + + if (!spi_mem_internal_supports_op(mem, op)) return -ENOTSUPP; if (ctlr->mem_ops) { diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index a1888dc6a938af435cc78a0635358b3b32ac1db9..09cfae3abce2d8f2f111d1a340efad8f2440c3e7 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -307,10 +307,16 @@ static unsigned tegra_spi_fill_tx_fifo_from_client_txbuf( x |= (u32)(*tx_buf++) << (i * 8); tegra_spi_writel(tspi, x, SPI_TX_FIFO); } + + tspi->cur_tx_pos += written_words * tspi->bytes_per_word; } else { + unsigned int write_bytes; max_n_32bit = min(tspi->curr_dma_words, tx_empty_count); written_words = max_n_32bit; nbytes = written_words * tspi->bytes_per_word; + if (nbytes > t->len - tspi->cur_pos) + nbytes = t->len - tspi->cur_pos; + write_bytes = nbytes; for (count = 0; count < max_n_32bit; count++) { u32 x = 0; @@ -319,8 +325,10 @@ static unsigned tegra_spi_fill_tx_fifo_from_client_txbuf( x |= (u32)(*tx_buf++) << (i * 8); tegra_spi_writel(tspi, x, SPI_TX_FIFO); } + + tspi->cur_tx_pos += write_bytes; } - tspi->cur_tx_pos += written_words * tspi->bytes_per_word; + return written_words; } @@ -344,20 +352,27 @@ static unsigned int tegra_spi_read_rx_fifo_to_client_rxbuf( for (i = 0; len && (i < 4); i++, len--) *rx_buf++ = (x >> i*8) & 0xFF; } - tspi->cur_rx_pos += tspi->curr_dma_words * tspi->bytes_per_word; read_words += tspi->curr_dma_words; + tspi->cur_rx_pos += tspi->curr_dma_words * tspi->bytes_per_word; } else { u32 rx_mask = ((u32)1 << t->bits_per_word) - 1; + u8 bytes_per_word = tspi->bytes_per_word; + unsigned int read_bytes; + len = rx_full_count * bytes_per_word; + if (len > t->len - tspi->cur_pos) + len = t->len - tspi->cur_pos; + read_bytes = len; for (count = 0; count < rx_full_count; count++) { u32 x = tegra_spi_readl(tspi, SPI_RX_FIFO) & rx_mask; - for (i = 0; (i < tspi->bytes_per_word); i++) + for (i = 0; len && (i < bytes_per_word); i++, len--) *rx_buf++ = (x >> (i*8)) & 0xFF; } - tspi->cur_rx_pos += rx_full_count * tspi->bytes_per_word; read_words += rx_full_count; + tspi->cur_rx_pos += read_bytes; } + return read_words; } @@ -372,12 +387,17 @@ static void tegra_spi_copy_client_txbuf_to_spi_txbuf( unsigned len = tspi->curr_dma_words * tspi->bytes_per_word; memcpy(tspi->tx_dma_buf, t->tx_buf + tspi->cur_pos, len); + tspi->cur_tx_pos += tspi->curr_dma_words * tspi->bytes_per_word; } else { unsigned int i; unsigned int count; u8 *tx_buf = (u8 *)t->tx_buf + tspi->cur_tx_pos; unsigned consume = tspi->curr_dma_words * tspi->bytes_per_word; + unsigned int write_bytes; + if (consume > t->len - tspi->cur_pos) + consume = t->len - tspi->cur_pos; + write_bytes = consume; for (count = 0; count < tspi->curr_dma_words; count++) { u32 x = 0; @@ -386,8 +406,9 @@ static void tegra_spi_copy_client_txbuf_to_spi_txbuf( x |= (u32)(*tx_buf++) << (i * 8); tspi->tx_dma_buf[count] = x; } + + tspi->cur_tx_pos += write_bytes; } - tspi->cur_tx_pos += tspi->curr_dma_words * tspi->bytes_per_word; /* Make the dma buffer to read by dma */ dma_sync_single_for_device(tspi->dev, tspi->tx_dma_phys, @@ -405,20 +426,28 @@ static void tegra_spi_copy_spi_rxbuf_to_client_rxbuf( unsigned len = tspi->curr_dma_words * tspi->bytes_per_word; memcpy(t->rx_buf + tspi->cur_rx_pos, tspi->rx_dma_buf, len); + tspi->cur_rx_pos += tspi->curr_dma_words * tspi->bytes_per_word; } else { unsigned int i; unsigned int count; unsigned char *rx_buf = t->rx_buf + tspi->cur_rx_pos; u32 rx_mask = ((u32)1 << t->bits_per_word) - 1; + unsigned consume = tspi->curr_dma_words * tspi->bytes_per_word; + unsigned int read_bytes; + if (consume > t->len - tspi->cur_pos) + consume = t->len - tspi->cur_pos; + read_bytes = consume; for (count = 0; count < tspi->curr_dma_words; count++) { u32 x = tspi->rx_dma_buf[count] & rx_mask; - for (i = 0; (i < tspi->bytes_per_word); i++) + for (i = 0; consume && (i < tspi->bytes_per_word); + i++, consume--) *rx_buf++ = (x >> (i*8)) & 0xFF; } + + tspi->cur_rx_pos += read_bytes; } - tspi->cur_rx_pos += tspi->curr_dma_words * tspi->bytes_per_word; /* Make the dma buffer to read by dma */ dma_sync_single_for_device(tspi->dev, tspi->rx_dma_phys, @@ -470,22 +499,39 @@ static int tegra_spi_start_rx_dma(struct tegra_spi_data *tspi, int len) return 0; } -static int tegra_spi_start_dma_based_transfer( - struct tegra_spi_data *tspi, struct spi_transfer *t) +static int tegra_spi_flush_fifos(struct tegra_spi_data *tspi) { - u32 val; - unsigned int len; - int ret = 0; + unsigned long timeout = jiffies + HZ; u32 status; - /* Make sure that Rx and Tx fifo are empty */ status = tegra_spi_readl(tspi, SPI_FIFO_STATUS); if ((status & SPI_FIFO_EMPTY) != SPI_FIFO_EMPTY) { - dev_err(tspi->dev, "Rx/Tx fifo are not empty status 0x%08x\n", - (unsigned)status); - return -EIO; + status |= SPI_RX_FIFO_FLUSH | SPI_TX_FIFO_FLUSH; + tegra_spi_writel(tspi, status, SPI_FIFO_STATUS); + while ((status & SPI_FIFO_EMPTY) != SPI_FIFO_EMPTY) { + status = tegra_spi_readl(tspi, SPI_FIFO_STATUS); + if (time_after(jiffies, timeout)) { + dev_err(tspi->dev, + "timeout waiting for fifo flush\n"); + return -EIO; + } + + udelay(1); + } } + return 0; +} + +static int tegra_spi_start_dma_based_transfer( + struct tegra_spi_data *tspi, struct spi_transfer *t) +{ + u32 val; + unsigned int len; + int ret = 0; + u8 dma_burst; + struct dma_slave_config dma_sconfig = {0}; + val = SPI_DMA_BLK_SET(tspi->curr_dma_words - 1); tegra_spi_writel(tspi, val, SPI_DMA_BLK); @@ -496,12 +542,16 @@ static int tegra_spi_start_dma_based_transfer( len = tspi->curr_dma_words * 4; /* Set attention level based on length of transfer */ - if (len & 0xF) + if (len & 0xF) { val |= SPI_TX_TRIG_1 | SPI_RX_TRIG_1; - else if (((len) >> 4) & 0x1) + dma_burst = 1; + } else if (((len) >> 4) & 0x1) { val |= SPI_TX_TRIG_4 | SPI_RX_TRIG_4; - else + dma_burst = 4; + } else { val |= SPI_TX_TRIG_8 | SPI_RX_TRIG_8; + dma_burst = 8; + } if (tspi->cur_direction & DATA_DIR_TX) val |= SPI_IE_TX; @@ -512,7 +562,18 @@ static int tegra_spi_start_dma_based_transfer( tegra_spi_writel(tspi, val, SPI_DMA_CTL); tspi->dma_control_reg = val; + dma_sconfig.device_fc = true; if (tspi->cur_direction & DATA_DIR_TX) { + dma_sconfig.dst_addr = tspi->phys + SPI_TX_FIFO; + dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + dma_sconfig.dst_maxburst = dma_burst; + ret = dmaengine_slave_config(tspi->tx_dma_chan, &dma_sconfig); + if (ret < 0) { + dev_err(tspi->dev, + "DMA slave config failed: %d\n", ret); + return ret; + } + tegra_spi_copy_client_txbuf_to_spi_txbuf(tspi, t); ret = tegra_spi_start_tx_dma(tspi, len); if (ret < 0) { @@ -523,6 +584,16 @@ static int tegra_spi_start_dma_based_transfer( } if (tspi->cur_direction & DATA_DIR_RX) { + dma_sconfig.src_addr = tspi->phys + SPI_RX_FIFO; + dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + dma_sconfig.src_maxburst = dma_burst; + ret = dmaengine_slave_config(tspi->rx_dma_chan, &dma_sconfig); + if (ret < 0) { + dev_err(tspi->dev, + "DMA slave config failed: %d\n", ret); + return ret; + } + /* Make the dma buffer to read by dma */ dma_sync_single_for_device(tspi->dev, tspi->rx_dma_phys, tspi->dma_buf_size, DMA_FROM_DEVICE); @@ -582,7 +653,6 @@ static int tegra_spi_init_dma_param(struct tegra_spi_data *tspi, u32 *dma_buf; dma_addr_t dma_phys; int ret; - struct dma_slave_config dma_sconfig; dma_chan = dma_request_slave_channel_reason(tspi->dev, dma_to_memory ? "rx" : "tx"); @@ -602,19 +672,6 @@ static int tegra_spi_init_dma_param(struct tegra_spi_data *tspi, return -ENOMEM; } - if (dma_to_memory) { - dma_sconfig.src_addr = tspi->phys + SPI_RX_FIFO; - dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - dma_sconfig.src_maxburst = 0; - } else { - dma_sconfig.dst_addr = tspi->phys + SPI_TX_FIFO; - dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - dma_sconfig.dst_maxburst = 0; - } - - ret = dmaengine_slave_config(dma_chan, &dma_sconfig); - if (ret) - goto scrub; if (dma_to_memory) { tspi->rx_dma_chan = dma_chan; tspi->rx_dma_buf = dma_buf; @@ -625,11 +682,6 @@ static int tegra_spi_init_dma_param(struct tegra_spi_data *tspi, tspi->tx_dma_phys = dma_phys; } return 0; - -scrub: - dma_free_coherent(tspi->dev, tspi->dma_buf_size, dma_buf, dma_phys); - dma_release_channel(dma_chan); - return ret; } static void tegra_spi_deinit_dma_param(struct tegra_spi_data *tspi, @@ -730,6 +782,8 @@ static int tegra_spi_start_transfer_one(struct spi_device *spi, if (tspi->is_packed) command1 |= SPI_PACKED; + else + command1 &= ~SPI_PACKED; command1 &= ~(SPI_CS_SEL_MASK | SPI_TX_EN | SPI_RX_EN); tspi->cur_direction = 0; @@ -748,6 +802,9 @@ static int tegra_spi_start_transfer_one(struct spi_device *spi, dev_dbg(tspi->dev, "The def 0x%x and written 0x%x\n", tspi->def_command1_reg, (unsigned)command1); + ret = tegra_spi_flush_fifos(tspi); + if (ret < 0) + return ret; if (total_fifo_words > SPI_FIFO_DEPTH) ret = tegra_spi_start_dma_based_transfer(tspi, t); else @@ -838,7 +895,17 @@ static int tegra_spi_transfer_one_message(struct spi_master *master, if (WARN_ON(ret == 0)) { dev_err(tspi->dev, "spi transfer timeout, err %d\n", ret); + if (tspi->is_curr_dma_xfer && + (tspi->cur_direction & DATA_DIR_TX)) + dmaengine_terminate_all(tspi->tx_dma_chan); + if (tspi->is_curr_dma_xfer && + (tspi->cur_direction & DATA_DIR_RX)) + dmaengine_terminate_all(tspi->rx_dma_chan); ret = -EIO; + tegra_spi_flush_fifos(tspi); + reset_control_assert(tspi->rst); + udelay(2); + reset_control_deassert(tspi->rst); goto complete_xfer; } @@ -889,6 +956,7 @@ static irqreturn_t handle_cpu_based_xfer(struct tegra_spi_data *tspi) tspi->status_reg); dev_err(tspi->dev, "CpuXfer 0x%08x:0x%08x\n", tspi->command1_reg, tspi->dma_control_reg); + tegra_spi_flush_fifos(tspi); reset_control_assert(tspi->rst); udelay(2); reset_control_deassert(tspi->rst); @@ -961,6 +1029,7 @@ static irqreturn_t handle_dma_based_xfer(struct tegra_spi_data *tspi) tspi->status_reg); dev_err(tspi->dev, "DmaXfer 0x%08x:0x%08x\n", tspi->command1_reg, tspi->dma_control_reg); + tegra_spi_flush_fifos(tspi); reset_control_assert(tspi->rst); udelay(2); reset_control_deassert(tspi->rst); diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c index 4389ab80c23e6cdbaeba15d8ccc5cfba736d7fa2..fa730a871d25276cc9293d8e57e01b6fd95294ec 100644 --- a/drivers/spi/spi-topcliff-pch.c +++ b/drivers/spi/spi-topcliff-pch.c @@ -1008,6 +1008,9 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw) /* RX */ dma->sg_rx_p = kcalloc(num, sizeof(*dma->sg_rx_p), GFP_ATOMIC); + if (!dma->sg_rx_p) + return; + sg_init_table(dma->sg_rx_p, num); /* Initialize SG table */ /* offset, length setting */ sg = dma->sg_rx_p; @@ -1068,6 +1071,9 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw) } dma->sg_tx_p = kcalloc(num, sizeof(*dma->sg_tx_p), GFP_ATOMIC); + if (!dma->sg_tx_p) + return; + sg_init_table(dma->sg_tx_p, num); /* Initialize SG table */ /* offset, length setting */ sg = dma->sg_tx_p; diff --git a/drivers/staging/android/ion/ion_system_heap.c b/drivers/staging/android/ion/ion_system_heap.c index 4e525203be58d3661586896c2a75b305cad9bdd5..6754d13673594aacf1dbb1c32a8461fb121304b7 100644 --- a/drivers/staging/android/ion/ion_system_heap.c +++ b/drivers/staging/android/ion/ion_system_heap.c @@ -695,8 +695,8 @@ static struct task_struct *ion_create_kworker(struct ion_page_pool **pools, attr.sched_nice = ION_KTHREAD_NICE_VAL; buf = cached ? "cached" : "uncached"; - thread = kthread_create(ion_sys_heap_worker, pools, - "ion-pool-%s-worker", buf); + thread = kthread_run(ion_sys_heap_worker, pools, + "ion-pool-%s-worker", buf); if (IS_ERR(thread)) { pr_err("%s: failed to create %s worker thread: %ld\n", __func__, buf, PTR_ERR(thread)); diff --git a/drivers/staging/android/vsoc.c b/drivers/staging/android/vsoc.c index 22571abcaa4e88c0a141adf42f2a899c5669303e..034d86869772ef5bad01829e2764f9bce61820c1 100644 --- a/drivers/staging/android/vsoc.c +++ b/drivers/staging/android/vsoc.c @@ -260,7 +260,8 @@ do_create_fd_scoped_permission(struct vsoc_device_region *region_p, atomic_t *owner_ptr = NULL; struct vsoc_device_region *managed_region_p; - if (copy_from_user(&np->permission, &arg->perm, sizeof(*np)) || + if (copy_from_user(&np->permission, + &arg->perm, sizeof(np->permission)) || copy_from_user(&managed_fd, &arg->managed_region_fd, sizeof(managed_fd))) { return -EFAULT; diff --git a/drivers/staging/comedi/drivers/adv_pci1710.c b/drivers/staging/comedi/drivers/adv_pci1710.c index 6a93b04f1fdfd7dd53d47f70d675cb1e3158a88e..c1c3b18793d500bc67ffe0061282fff8a9005f5d 100644 --- a/drivers/staging/comedi/drivers/adv_pci1710.c +++ b/drivers/staging/comedi/drivers/adv_pci1710.c @@ -46,8 +46,8 @@ #define PCI171X_RANGE_UNI BIT(4) #define PCI171X_RANGE_GAIN(x) (((x) & 0x7) << 0) #define PCI171X_MUX_REG 0x04 /* W: A/D multiplexor control */ -#define PCI171X_MUX_CHANH(x) (((x) & 0xf) << 8) -#define PCI171X_MUX_CHANL(x) (((x) & 0xf) << 0) +#define PCI171X_MUX_CHANH(x) (((x) & 0xff) << 8) +#define PCI171X_MUX_CHANL(x) (((x) & 0xff) << 0) #define PCI171X_MUX_CHAN(x) (PCI171X_MUX_CHANH(x) | PCI171X_MUX_CHANL(x)) #define PCI171X_STATUS_REG 0x06 /* R: status register */ #define PCI171X_STATUS_IRQ BIT(11) /* 1=IRQ occurred */ diff --git a/drivers/staging/comedi/drivers/ni_mio_common.c b/drivers/staging/comedi/drivers/ni_mio_common.c index d799b1b55de39a6ae443a6ac28018d79cf48407f..747518c2954274cd66a030439884a597cb380594 100644 --- a/drivers/staging/comedi/drivers/ni_mio_common.c +++ b/drivers/staging/comedi/drivers/ni_mio_common.c @@ -4984,7 +4984,10 @@ static int ni_valid_rtsi_output_source(struct comedi_device *dev, case NI_RTSI_OUTPUT_G_SRC0: case NI_RTSI_OUTPUT_G_GATE0: case NI_RTSI_OUTPUT_RGOUT0: - case NI_RTSI_OUTPUT_RTSI_BRD_0: + case NI_RTSI_OUTPUT_RTSI_BRD(0): + case NI_RTSI_OUTPUT_RTSI_BRD(1): + case NI_RTSI_OUTPUT_RTSI_BRD(2): + case NI_RTSI_OUTPUT_RTSI_BRD(3): return 1; case NI_RTSI_OUTPUT_RTSI_OSC: return (devpriv->is_m_series) ? 1 : 0; @@ -5005,11 +5008,18 @@ static int ni_set_rtsi_routing(struct comedi_device *dev, devpriv->rtsi_trig_a_output_reg |= NISTC_RTSI_TRIG(chan, src); ni_stc_writew(dev, devpriv->rtsi_trig_a_output_reg, NISTC_RTSI_TRIGA_OUT_REG); - } else if (chan < 8) { + } else if (chan < NISTC_RTSI_TRIG_NUM_CHAN(devpriv->is_m_series)) { devpriv->rtsi_trig_b_output_reg &= ~NISTC_RTSI_TRIG_MASK(chan); devpriv->rtsi_trig_b_output_reg |= NISTC_RTSI_TRIG(chan, src); ni_stc_writew(dev, devpriv->rtsi_trig_b_output_reg, NISTC_RTSI_TRIGB_OUT_REG); + } else if (chan != NISTC_RTSI_TRIG_OLD_CLK_CHAN) { + /* probably should never reach this, since the + * ni_valid_rtsi_output_source above errors out if chan is too + * high + */ + dev_err(dev->class_dev, "%s: unknown rtsi channel\n", __func__); + return -EINVAL; } return 2; } @@ -5025,12 +5035,12 @@ static unsigned int ni_get_rtsi_routing(struct comedi_device *dev, } else if (chan < NISTC_RTSI_TRIG_NUM_CHAN(devpriv->is_m_series)) { return NISTC_RTSI_TRIG_TO_SRC(chan, devpriv->rtsi_trig_b_output_reg); - } else { - if (chan == NISTC_RTSI_TRIG_OLD_CLK_CHAN) - return NI_RTSI_OUTPUT_RTSI_OSC; - dev_err(dev->class_dev, "bug! should never get here?\n"); - return 0; + } else if (chan == NISTC_RTSI_TRIG_OLD_CLK_CHAN) { + return NI_RTSI_OUTPUT_RTSI_OSC; } + + dev_err(dev->class_dev, "%s: unknown rtsi channel\n", __func__); + return -EINVAL; } static int ni_rtsi_insn_config(struct comedi_device *dev, diff --git a/drivers/staging/greybus/audio_manager.c b/drivers/staging/greybus/audio_manager.c index d44b070d886262998b6a70f71ae2a4b6df666fe4..0f5c68edf2c1550f55d86a41514c6a29ba859d17 100644 --- a/drivers/staging/greybus/audio_manager.c +++ b/drivers/staging/greybus/audio_manager.c @@ -89,8 +89,8 @@ void gb_audio_manager_remove_all(void) list_for_each_entry_safe(module, next, &modules_list, list) { list_del(&module->list); - kobject_put(&module->kobj); ida_simple_remove(&module_id, module->id); + kobject_put(&module->kobj); } is_empty = list_empty(&modules_list); diff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index 010ae1e9c7fbfabee6b26ceb4fe412e9b20b436f..40680eaf3974f4058a9e475aa458fc4e0f4058fc 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -1098,21 +1098,21 @@ static void gb_lights_channel_release(struct gb_channel *channel) static void gb_lights_light_release(struct gb_light *light) { int i; - int count; light->ready = false; - count = light->channels_count; - if (light->has_flash) gb_lights_light_v4l2_unregister(light); + light->has_flash = false; - for (i = 0; i < count; i++) { + for (i = 0; i < light->channels_count; i++) gb_lights_channel_release(&light->channels[i]); - light->channels_count--; - } + light->channels_count = 0; + kfree(light->channels); + light->channels = NULL; kfree(light->name); + light->name = NULL; } static void gb_lights_release(struct gb_lights *glights) diff --git a/drivers/staging/most/cdev/cdev.c b/drivers/staging/most/cdev/cdev.c index 0b48677fa958a3f8e2e60ea4e26182761ff792ad..27d58b55b8108744d5b03d58931068805df8b155 100644 --- a/drivers/staging/most/cdev/cdev.c +++ b/drivers/staging/most/cdev/cdev.c @@ -453,7 +453,9 @@ static int comp_probe(struct most_interface *iface, int channel_id, c->devno = MKDEV(comp.major, current_minor); cdev_init(&c->cdev, &channel_fops); c->cdev.owner = THIS_MODULE; - cdev_add(&c->cdev, c->devno, 1); + retval = cdev_add(&c->cdev, c->devno, 1); + if (retval < 0) + goto err_free_c; c->iface = iface; c->cfg = cfg; c->channel_id = channel_id; @@ -485,6 +487,7 @@ static int comp_probe(struct most_interface *iface, int channel_id, list_del(&c->list); error_alloc_kfifo: cdev_del(&c->cdev); +err_free_c: kfree(c); error_alloc_channel: ida_simple_remove(&comp.minor_id, current_minor); diff --git a/drivers/staging/most/net/net.c b/drivers/staging/most/net/net.c index 30d816b7e16567f843d67d4ed0c41e1becbd39af..ff80834ef04aab316006cfcc8ffedf7c9e4323ce 100644 --- a/drivers/staging/most/net/net.c +++ b/drivers/staging/most/net/net.c @@ -81,6 +81,11 @@ static int skb_to_mamac(const struct sk_buff *skb, struct mbo *mbo) unsigned int payload_len = skb->len - ETH_HLEN; unsigned int mdp_len = payload_len + MDP_HDR_LEN; + if (mdp_len < skb->len) { + pr_err("drop: too large packet! (%u)\n", skb->len); + return -EINVAL; + } + if (mbo->buffer_length < mdp_len) { pr_err("drop: too small buffer! (%d for %d)\n", mbo->buffer_length, mdp_len); @@ -128,6 +133,11 @@ static int skb_to_mep(const struct sk_buff *skb, struct mbo *mbo) u8 *buff = mbo->virt_address; unsigned int mep_len = skb->len + MEP_HDR_LEN; + if (mep_len < skb->len) { + pr_err("drop: too large packet! (%u)\n", skb->len); + return -EINVAL; + } + if (mbo->buffer_length < mep_len) { pr_err("drop: too small buffer! (%d for %d)\n", mbo->buffer_length, mep_len); diff --git a/drivers/staging/rtl8188eu/os_dep/ioctl_linux.c b/drivers/staging/rtl8188eu/os_dep/ioctl_linux.c index bee3c3a7a7a999e00d2acb67c6340793ee003f08..0003f0c38038b630f93490a70baeafa43bfca0bc 100644 --- a/drivers/staging/rtl8188eu/os_dep/ioctl_linux.c +++ b/drivers/staging/rtl8188eu/os_dep/ioctl_linux.c @@ -229,18 +229,21 @@ static char *translate_scan(struct adapter *padapter, /* parsing WPA/WPA2 IE */ { - u8 buf[MAX_WPA_IE_LEN]; + u8 *buf; u8 wpa_ie[255], rsn_ie[255]; u16 wpa_len = 0, rsn_len = 0; u8 *p; + buf = kzalloc(MAX_WPA_IE_LEN, GFP_ATOMIC); + if (!buf) + return start; + rtw_get_sec_ie(pnetwork->network.ies, pnetwork->network.ie_length, rsn_ie, &rsn_len, wpa_ie, &wpa_len); RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_wx_get_scan: ssid =%s\n", pnetwork->network.Ssid.Ssid)); RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_wx_get_scan: wpa_len =%d rsn_len =%d\n", wpa_len, rsn_len)); if (wpa_len > 0) { p = buf; - memset(buf, 0, MAX_WPA_IE_LEN); p += sprintf(p, "wpa_ie="); for (i = 0; i < wpa_len; i++) p += sprintf(p, "%02x", wpa_ie[i]); @@ -257,7 +260,6 @@ static char *translate_scan(struct adapter *padapter, } if (rsn_len > 0) { p = buf; - memset(buf, 0, MAX_WPA_IE_LEN); p += sprintf(p, "rsn_ie="); for (i = 0; i < rsn_len; i++) p += sprintf(p, "%02x", rsn_ie[i]); @@ -271,6 +273,7 @@ static char *translate_scan(struct adapter *padapter, iwe.u.data.length = rsn_len; start = iwe_stream_add_point(info, start, stop, &iwe, rsn_ie); } + kfree(buf); } {/* parsing WPS IE */ @@ -2023,7 +2026,7 @@ static int wpa_supplicant_ioctl(struct net_device *dev, struct iw_point *p) struct ieee_param *param; uint ret = 0; - if (p->length < sizeof(struct ieee_param) || !p->pointer) { + if (!p->pointer || p->length != sizeof(struct ieee_param)) { ret = -EINVAL; goto out; } @@ -2816,7 +2819,7 @@ static int rtw_hostapd_ioctl(struct net_device *dev, struct iw_point *p) goto out; } - if (!p->pointer) { + if (!p->pointer || p->length != sizeof(struct ieee_param)) { ret = -EINVAL; goto out; } diff --git a/drivers/staging/rtl8188eu/os_dep/usb_intf.c b/drivers/staging/rtl8188eu/os_dep/usb_intf.c index 55952dd883598798e5317a26fe61df33823725fb..4b363112fa97a3889349cabc0ca7d11b1bdd3e82 100644 --- a/drivers/staging/rtl8188eu/os_dep/usb_intf.c +++ b/drivers/staging/rtl8188eu/os_dep/usb_intf.c @@ -37,6 +37,7 @@ static const struct usb_device_id rtw_usb_id_tbl[] = { {USB_DEVICE(0x2001, 0x3311)}, /* DLink GO-USB-N150 REV B1 */ {USB_DEVICE(0x2001, 0x331B)}, /* D-Link DWA-121 rev B1 */ {USB_DEVICE(0x2357, 0x010c)}, /* TP-Link TL-WN722N v2 */ + {USB_DEVICE(0x2357, 0x0111)}, /* TP-Link TL-WN727N v5.21 */ {USB_DEVICE(0x0df6, 0x0076)}, /* Sitecom N150 v2 */ {USB_DEVICE(USB_VENDER_ID_REALTEK, 0xffef)}, /* Rosewill RNX-N150NUB */ {} /* Terminating entry */ diff --git a/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c b/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c index 10b3f9733bad44799503021a906aa6634dfefad7..4a27c3927da9a4abbe171d4fb26bcb4ba469cfde 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c @@ -478,14 +478,13 @@ int rtl8723bs_xmit_thread(void *context) s32 ret; struct adapter *padapter; struct xmit_priv *pxmitpriv; - u8 thread_name[20] = "RTWHALXT"; - + u8 thread_name[20]; ret = _SUCCESS; padapter = context; pxmitpriv = &padapter->xmitpriv; - rtw_sprintf(thread_name, 20, "%s-"ADPT_FMT, thread_name, ADPT_ARG(padapter)); + rtw_sprintf(thread_name, 20, "RTWHALXT-" ADPT_FMT, ADPT_ARG(padapter)); thread_enter(thread_name); DBG_871X("start "FUNC_ADPT_FMT"\n", FUNC_ADPT_ARG(padapter)); diff --git a/drivers/staging/rtl8723bs/os_dep/ioctl_linux.c b/drivers/staging/rtl8723bs/os_dep/ioctl_linux.c index 4f120e72c7d2a5587c20162628ee1d491f9fa4ba..466d25ccc4bbc857c41faf95daa11bc96de94f79 100644 --- a/drivers/staging/rtl8723bs/os_dep/ioctl_linux.c +++ b/drivers/staging/rtl8723bs/os_dep/ioctl_linux.c @@ -3400,7 +3400,7 @@ static int wpa_supplicant_ioctl(struct net_device *dev, struct iw_point *p) /* down(&ieee->wx_sem); */ - if (p->length < sizeof(struct ieee_param) || !p->pointer) { + if (!p->pointer || p->length != sizeof(struct ieee_param)) { ret = -EINVAL; goto out; } @@ -4236,7 +4236,7 @@ static int rtw_hostapd_ioctl(struct net_device *dev, struct iw_point *p) /* if (p->length < sizeof(struct ieee_param) || !p->pointer) { */ - if (!p->pointer) { + if (!p->pointer || p->length != sizeof(*param)) { ret = -EINVAL; goto out; } diff --git a/drivers/staging/rtlwifi/halmac/halmac_88xx/halmac_func_88xx.c b/drivers/staging/rtlwifi/halmac/halmac_88xx/halmac_func_88xx.c index 53f55f129a760267e2cb508dd163fa11c9653ca2..ddbeff8224ab68fedb1c7e838375cb753b66339b 100644 --- a/drivers/staging/rtlwifi/halmac/halmac_88xx/halmac_func_88xx.c +++ b/drivers/staging/rtlwifi/halmac/halmac_88xx/halmac_func_88xx.c @@ -2466,8 +2466,11 @@ halmac_parse_psd_data_88xx(struct halmac_adapter *halmac_adapter, u8 *c2h_buf, segment_size = (u8)PSD_DATA_GET_SEGMENT_SIZE(c2h_buf); psd_set->data_size = total_size; - if (!psd_set->data) + if (!psd_set->data) { psd_set->data = kzalloc(psd_set->data_size, GFP_KERNEL); + if (!psd_set->data) + return HALMAC_RET_MALLOC_FAIL; + } if (segment_id == 0) psd_set->segment_size = segment_size; diff --git a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c index 455082867246d84caeec1b5ee103f9b73574be8e..edf25922b12d623b100ee798662d39e86c504aea 100644 --- a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c +++ b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c @@ -47,6 +47,7 @@ MODULE_DESCRIPTION("Broadcom 2835 MMAL video capture"); MODULE_AUTHOR("Vincent Sanders"); MODULE_LICENSE("GPL"); MODULE_VERSION(BM2835_MMAL_VERSION); +MODULE_ALIAS("platform:bcm2835-camera"); int bcm2835_v4l2_debug; module_param_named(debug, bcm2835_v4l2_debug, int, 0644); @@ -1854,6 +1855,12 @@ static int bcm2835_mmal_probe(struct platform_device *pdev) num_cameras = get_num_cameras(instance, resolutions, MAX_BCM2835_CAMERAS); + + if (num_cameras < 1) { + ret = -ENODEV; + goto cleanup_mmal; + } + if (num_cameras > MAX_BCM2835_CAMERAS) num_cameras = MAX_BCM2835_CAMERAS; @@ -1953,6 +1960,9 @@ static int bcm2835_mmal_probe(struct platform_device *pdev) pr_info("%s: error %d while loading driver\n", BM2835_MMAL_MODULE_NAME, ret); +cleanup_mmal: + vchiq_mmal_finalise(instance); + return ret; } diff --git a/drivers/staging/vt6656/device.h b/drivers/staging/vt6656/device.h index a2feeb916836ccbc8b7240235b924a3e491d4c94..77e59a92e80b018200d54e65725ba02226c4d3dc 100644 --- a/drivers/staging/vt6656/device.h +++ b/drivers/staging/vt6656/device.h @@ -52,6 +52,8 @@ #define RATE_AUTO 12 #define MAX_RATE 12 +#define VNT_B_RATES (BIT(RATE_1M) | BIT(RATE_2M) |\ + BIT(RATE_5M) | BIT(RATE_11M)) /* * device specific @@ -259,6 +261,7 @@ struct vnt_private { u8 mac_hw; /* netdev */ struct usb_device *usb; + struct usb_interface *intf; u64 tsf_time; u8 rx_rate; diff --git a/drivers/staging/vt6656/dpc.c b/drivers/staging/vt6656/dpc.c index 3b94e80f1d5e191ed13541cba17b8753297bc6b5..879ceef517fbbc274b75ccaa83bfd177ade4e05f 100644 --- a/drivers/staging/vt6656/dpc.c +++ b/drivers/staging/vt6656/dpc.c @@ -130,7 +130,7 @@ int vnt_rx_data(struct vnt_private *priv, struct vnt_rcb *ptr_rcb, vnt_rf_rssi_to_dbm(priv, *rssi, &rx_dbm); - priv->bb_pre_ed_rssi = (u8)rx_dbm + 1; + priv->bb_pre_ed_rssi = (u8)-rx_dbm + 1; priv->current_rssi = priv->bb_pre_ed_rssi; skb_pull(skb, 8); diff --git a/drivers/staging/vt6656/int.c b/drivers/staging/vt6656/int.c index 504424b19fcf9981ceb1a1ff28348ba8e2624c22..af0060c74530eece3e57e9bf097a85818275dc68 100644 --- a/drivers/staging/vt6656/int.c +++ b/drivers/staging/vt6656/int.c @@ -97,9 +97,11 @@ static int vnt_int_report_rate(struct vnt_private *priv, u8 pkt_no, u8 tsr) info->status.rates[0].count = tx_retry; - if (!(tsr & (TSR_TMO | TSR_RETRYTMO))) { + if (!(tsr & TSR_TMO)) { info->status.rates[0].idx = idx; - info->flags |= IEEE80211_TX_STAT_ACK; + + if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) + info->flags |= IEEE80211_TX_STAT_ACK; } ieee80211_tx_status_irqsafe(priv->hw, context->skb); diff --git a/drivers/staging/vt6656/main_usb.c b/drivers/staging/vt6656/main_usb.c index 70433f756d8e1ff10b8f0e113d167f058019ccbf..36562ac94c1f892096440ae7d122c0a62a3fcd72 100644 --- a/drivers/staging/vt6656/main_usb.c +++ b/drivers/staging/vt6656/main_usb.c @@ -955,6 +955,7 @@ vt6656_probe(struct usb_interface *intf, const struct usb_device_id *id) priv = hw->priv; priv->hw = hw; priv->usb = udev; + priv->intf = intf; vnt_set_options(priv); @@ -977,6 +978,7 @@ vt6656_probe(struct usb_interface *intf, const struct usb_device_id *id) ieee80211_hw_set(priv->hw, RX_INCLUDES_FCS); ieee80211_hw_set(priv->hw, REPORTS_TX_ACK_STATUS); ieee80211_hw_set(priv->hw, SUPPORTS_PS); + ieee80211_hw_set(priv->hw, PS_NULLFUNC_STACK); priv->hw->max_signal = 100; diff --git a/drivers/staging/vt6656/rxtx.c b/drivers/staging/vt6656/rxtx.c index 9def0748ffee7e2b5e1d7636ba3e8cb0e6ab9c46..4b4f07fbb9bf28e643c772bc06e193609c97a2c9 100644 --- a/drivers/staging/vt6656/rxtx.c +++ b/drivers/staging/vt6656/rxtx.c @@ -278,11 +278,9 @@ static u16 vnt_rxtx_datahead_g(struct vnt_usb_send_context *tx_context, PK_TYPE_11B, &buf->b); /* Get Duration and TimeStamp */ - if (ieee80211_is_pspoll(hdr->frame_control)) { - __le16 dur = cpu_to_le16(priv->current_aid | BIT(14) | BIT(15)); - - buf->duration_a = dur; - buf->duration_b = dur; + if (ieee80211_is_nullfunc(hdr->frame_control)) { + buf->duration_a = hdr->duration_id; + buf->duration_b = hdr->duration_id; } else { buf->duration_a = vnt_get_duration_le(priv, tx_context->pkt_type, need_ack); @@ -371,10 +369,8 @@ static u16 vnt_rxtx_datahead_ab(struct vnt_usb_send_context *tx_context, tx_context->pkt_type, &buf->ab); /* Get Duration and TimeStampOff */ - if (ieee80211_is_pspoll(hdr->frame_control)) { - __le16 dur = cpu_to_le16(priv->current_aid | BIT(14) | BIT(15)); - - buf->duration = dur; + if (ieee80211_is_nullfunc(hdr->frame_control)) { + buf->duration = hdr->duration_id; } else { buf->duration = vnt_get_duration_le(priv, tx_context->pkt_type, need_ack); @@ -815,10 +811,14 @@ int vnt_tx_packet(struct vnt_private *priv, struct sk_buff *skb) if (info->band == NL80211_BAND_5GHZ) { pkt_type = PK_TYPE_11A; } else { - if (tx_rate->flags & IEEE80211_TX_RC_USE_CTS_PROTECT) - pkt_type = PK_TYPE_11GB; - else - pkt_type = PK_TYPE_11GA; + if (tx_rate->flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { + if (priv->basic_rates & VNT_B_RATES) + pkt_type = PK_TYPE_11GB; + else + pkt_type = PK_TYPE_11GA; + } else { + pkt_type = PK_TYPE_11A; + } } } else { pkt_type = PK_TYPE_11B; diff --git a/drivers/staging/vt6656/wcmd.c b/drivers/staging/vt6656/wcmd.c index 3eb2f11a5de13676247c8daf4d2ac7407ecfe555..2c5250ca2801d455aa65408f3f07571a9c280e86 100644 --- a/drivers/staging/vt6656/wcmd.c +++ b/drivers/staging/vt6656/wcmd.c @@ -99,6 +99,7 @@ void vnt_run_command(struct work_struct *work) if (vnt_init(priv)) { /* If fail all ends TODO retry */ dev_err(&priv->usb->dev, "failed to start\n"); + usb_set_intfdata(priv->intf, NULL); ieee80211_free_hw(priv->hw); return; } diff --git a/drivers/staging/wlan-ng/prism2mgmt.c b/drivers/staging/wlan-ng/prism2mgmt.c index 7350fe5d96a341cf8e30fee3de1c7bfb50e74611..a8860d2aee6836a55f28889ea327a0c35bfc78c4 100644 --- a/drivers/staging/wlan-ng/prism2mgmt.c +++ b/drivers/staging/wlan-ng/prism2mgmt.c @@ -959,7 +959,7 @@ int prism2mgmt_flashdl_state(struct wlandevice *wlandev, void *msgp) } } - return 0; + return result; } /*---------------------------------------------------------------- diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 317d0f3f7a14cb7bb1bc66a7c25883af94f98c92..03e9cb156df94990d12ed2d0b3d4044bf0ede86a 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -1157,9 +1157,7 @@ int iscsit_setup_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, hdr->cmdsn, be32_to_cpu(hdr->data_length), payload_length, conn->cid); - if (target_get_sess_cmd(&cmd->se_cmd, true) < 0) - return iscsit_add_reject_cmd(cmd, - ISCSI_REASON_WAITING_FOR_LOGOUT, buf); + target_get_sess_cmd(&cmd->se_cmd, true); cmd->sense_reason = transport_lookup_cmd_lun(&cmd->se_cmd, scsilun_to_int(&hdr->lun)); @@ -2000,9 +1998,7 @@ iscsit_handle_task_mgt_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, conn->sess->se_sess, 0, DMA_NONE, TCM_SIMPLE_TAG, cmd->sense_buffer + 2); - if (target_get_sess_cmd(&cmd->se_cmd, true) < 0) - return iscsit_add_reject_cmd(cmd, - ISCSI_REASON_WAITING_FOR_LOGOUT, buf); + target_get_sess_cmd(&cmd->se_cmd, true); /* * TASK_REASSIGN for ERL=2 / connection stays inside of @@ -4208,8 +4204,6 @@ int iscsit_close_connection( * must wait until they have completed. */ iscsit_check_conn_usage_count(conn); - target_sess_cmd_list_set_waiting(sess->se_sess); - target_wait_for_sess_cmds(sess->se_sess); ahash_request_free(conn->conn_tx_hash); if (conn->conn_rx_hash) { diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index e9ff2a7c0c0e67af564b951d1a4a4fb3f468b530..22e97a93728db14c5e6d72fae451638745d19c73 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -85,7 +85,7 @@ transport_lookup_cmd_lun(struct se_cmd *se_cmd, u64 unpacked_lun) goto out_unlock; } - se_cmd->se_lun = rcu_dereference(deve->se_lun); + se_cmd->se_lun = se_lun; se_cmd->pr_res_key = deve->pr_res_key; se_cmd->orig_fe_lun = unpacked_lun; se_cmd->se_cmd_flags |= SCF_SE_LUN_CMD; @@ -176,7 +176,7 @@ int transport_lookup_tmr_lun(struct se_cmd *se_cmd, u64 unpacked_lun) goto out_unlock; } - se_cmd->se_lun = rcu_dereference(deve->se_lun); + se_cmd->se_lun = se_lun; se_cmd->pr_res_key = deve->pr_res_key; se_cmd->orig_fe_lun = unpacked_lun; se_cmd->se_cmd_flags |= SCF_SE_LUN_CMD; diff --git a/drivers/target/target_core_fabric_lib.c b/drivers/target/target_core_fabric_lib.c index 71a80257a05260fd061b680cf13fcc5a2759431b..10fae26b44addf7a12dafa3a47a03ad035669941 100644 --- a/drivers/target/target_core_fabric_lib.c +++ b/drivers/target/target_core_fabric_lib.c @@ -131,7 +131,7 @@ static int srp_get_pr_transport_id( memset(buf + 8, 0, leading_zero_bytes); rc = hex2bin(buf + 8 + leading_zero_bytes, p, count); if (rc < 0) { - pr_debug("hex2bin failed for %s: %d\n", __func__, rc); + pr_debug("hex2bin failed for %s: %d\n", p, rc); return rc; } diff --git a/drivers/tee/optee/Kconfig b/drivers/tee/optee/Kconfig index 3c59e19029befd7ad0070db571de0b4b8271dfdb..3c1ec4e9ed29aeeb692bb3eb184a2cdf75dd5742 100644 --- a/drivers/tee/optee/Kconfig +++ b/drivers/tee/optee/Kconfig @@ -2,6 +2,7 @@ config OPTEE tristate "OP-TEE" depends on HAVE_ARM_SMCCC + depends on MMU help This implements the OP-TEE Trusted Execution Environment (TEE) driver. diff --git a/drivers/thermal/broadcom/brcmstb_thermal.c b/drivers/thermal/broadcom/brcmstb_thermal.c index 1919f91fa756597ff63f6e1518d92363f402e769..8d16a41eacae499fbd9094bb760e8810b6e3c127 100644 --- a/drivers/thermal/broadcom/brcmstb_thermal.c +++ b/drivers/thermal/broadcom/brcmstb_thermal.c @@ -58,7 +58,7 @@ #define AVS_TMON_TP_TEST_ENABLE 0x20 /* Default coefficients */ -#define AVS_TMON_TEMP_SLOPE -487 +#define AVS_TMON_TEMP_SLOPE 487 #define AVS_TMON_TEMP_OFFSET 410040 /* HW related temperature constants */ @@ -117,23 +117,12 @@ struct brcmstb_thermal_priv { struct thermal_zone_device *thermal; }; -static void avs_tmon_get_coeffs(struct thermal_zone_device *tz, int *slope, - int *offset) -{ - *slope = thermal_zone_get_slope(tz); - *offset = thermal_zone_get_offset(tz); -} - /* Convert a HW code to a temperature reading (millidegree celsius) */ static inline int avs_tmon_code_to_temp(struct thermal_zone_device *tz, u32 code) { - const int val = code & AVS_TMON_TEMP_MASK; - int slope, offset; - - avs_tmon_get_coeffs(tz, &slope, &offset); - - return slope * val + offset; + return (AVS_TMON_TEMP_OFFSET - + (int)((code & AVS_TMON_TEMP_MAX) * AVS_TMON_TEMP_SLOPE)); } /* @@ -145,20 +134,18 @@ static inline int avs_tmon_code_to_temp(struct thermal_zone_device *tz, static inline u32 avs_tmon_temp_to_code(struct thermal_zone_device *tz, int temp, bool low) { - int slope, offset; - if (temp < AVS_TMON_TEMP_MIN) - return AVS_TMON_TEMP_MAX; /* Maximum code value */ - - avs_tmon_get_coeffs(tz, &slope, &offset); + return AVS_TMON_TEMP_MAX; /* Maximum code value */ - if (temp >= offset) + if (temp >= AVS_TMON_TEMP_OFFSET) return 0; /* Minimum code value */ if (low) - return (u32)(DIV_ROUND_UP(offset - temp, abs(slope))); + return (u32)(DIV_ROUND_UP(AVS_TMON_TEMP_OFFSET - temp, + AVS_TMON_TEMP_SLOPE)); else - return (u32)((offset - temp) / abs(slope)); + return (u32)((AVS_TMON_TEMP_OFFSET - temp) / + AVS_TMON_TEMP_SLOPE); } static int brcmstb_get_temp(void *data, int *temp) diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 02f623b38f76ef9636204be5afc00ffeade47159..17177f9a033f13e4e89de6c5ee60d988d7a163f1 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -481,7 +481,7 @@ static int cpufreq_get_requested_power(struct thermal_cooling_device *cdev, load = 0; total_load += load; - if (trace_thermal_power_cpu_limit_enabled() && load_cpu) + if (load_cpu) load_cpu[i] = load; i++; diff --git a/drivers/thermal/msm-tsens.c b/drivers/thermal/msm-tsens.c index c4099614f8595aa296b61415dd04a535283383e2..d79e32fd291f5f4d3c54db78e520e1c6ed3076eb 100644 --- a/drivers/thermal/msm-tsens.c +++ b/drivers/thermal/msm-tsens.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ #include @@ -273,7 +273,7 @@ static void tsens_therm_fwk_notify(struct work_struct *work) } TSENS_DBG(tmdev, "Calling trip_temp for sensor %d\n", i); - of_thermal_handle_trip_temp(tmdev->sensor[i].tzd, temp); + of_thermal_handle_trip(tmdev->sensor[i].tzd); } } if (tmdev->min_temp_sensor_id != MIN_TEMP_DEF_OFFSET) { @@ -284,7 +284,7 @@ static void tsens_therm_fwk_notify(struct work_struct *work) return; } TSENS_DBG(tmdev, "Calling trip_temp for sensor %d\n", i); - of_thermal_handle_trip_temp(tmdev->min_temp.tzd, temp); + of_thermal_handle_trip(tmdev->min_temp.tzd); } } diff --git a/drivers/thermal/mtk_thermal.c b/drivers/thermal/mtk_thermal.c index 0691f260f6eabec0536e4975f82be279aeb78630..f64643629d8b54981d8de9957320905012b7b8a7 100644 --- a/drivers/thermal/mtk_thermal.c +++ b/drivers/thermal/mtk_thermal.c @@ -431,7 +431,8 @@ static int mtk_thermal_bank_temperature(struct mtk_thermal_bank *bank) u32 raw; for (i = 0; i < conf->bank_data[bank->id].num_sensors; i++) { - raw = readl(mt->thermal_base + conf->msr[i]); + raw = readl(mt->thermal_base + + conf->msr[conf->bank_data[bank->id].sensors[i]]); temp = raw_to_mcelsius(mt, conf->bank_data[bank->id].sensors[i], @@ -568,7 +569,8 @@ static void mtk_thermal_init_bank(struct mtk_thermal *mt, int num, for (i = 0; i < conf->bank_data[num].num_sensors; i++) writel(conf->sensor_mux_values[conf->bank_data[num].sensors[i]], - mt->thermal_base + conf->adcpnp[i]); + mt->thermal_base + + conf->adcpnp[conf->bank_data[num].sensors[i]]); writel((1 << conf->bank_data[num].num_sensors) - 1, mt->thermal_base + TEMP_MONCTL0); diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c index 8556510c2e4281e92d820ac6380a7d60800e97b6..2f74de504146bd0c380beb2b8b9235320924e68b 100644 --- a/drivers/thermal/of-thermal.c +++ b/drivers/thermal/of-thermal.c @@ -200,13 +200,15 @@ static int of_thermal_set_trips(struct thermal_zone_device *tz, return -EINVAL; mutex_lock(&data->senps->lock); + if (data->mode == THERMAL_DEVICE_DISABLED) + goto set_trips_exit; of_thermal_aggregate_trip_types(tz, GENMASK(THERMAL_TRIP_CRITICAL, 0), &low, &high); data->senps->trip_low = low; data->senps->trip_high = high; ret = data->senps->ops->set_trips(data->senps->sensor_data, low, high); - +set_trips_exit: mutex_unlock(&data->senps->lock); return ret; } diff --git a/drivers/thermal/qcom/Kconfig b/drivers/thermal/qcom/Kconfig index 247162792d1baa12192316cfed90a720977d3dab..cc78d3711bfea802c2fcffbbddfcacf519c09594 100644 --- a/drivers/thermal/qcom/Kconfig +++ b/drivers/thermal/qcom/Kconfig @@ -102,6 +102,17 @@ config REGULATOR_COOLING_DEVICE If you want this support, you should say Y here. +config MSM_BCL_PERIPHERAL_CTL + bool "BCL driver to control the PMIC BCL peripheral" + depends on SPMI && THERMAL_OF + help + Say Y here to enable this BCL PMIC peripheral driver. This driver + provides routines to configure and monitor the BCL + PMIC peripheral. This driver registers the battery current and + voltage sensors with the thermal core framework and can take + threshold input and notify the thermal core when the threshold is + reached. + config QTI_CPU_ISOLATE_COOLING_DEVICE bool "QTI CPU Isolate cooling devices" depends on THERMAL_OF diff --git a/drivers/thermal/qcom/Makefile b/drivers/thermal/qcom/Makefile index 71785f2fad39faeb54765b290ab0d9e93463ec81..8a0b6190ec9f466709bb94df2175229ba151fdf0 100644 --- a/drivers/thermal/qcom/Makefile +++ b/drivers/thermal/qcom/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_QTI_QMI_COOLING_DEVICE) += thermal_mitigation_device_service_v01.o obj-$(CONFIG_QTI_THERMAL_LIMITS_DCVS) += msm_lmh_dcvs.o lmh_dbg.o obj-$(CONFIG_QTI_AOP_REG_COOLING_DEVICE) += regulator_aop_cdev.o obj-$(CONFIG_REGULATOR_COOLING_DEVICE) += regulator_cdev.o +obj-$(CONFIG_MSM_BCL_PERIPHERAL_CTL) += bcl_peripheral.o obj-$(CONFIG_QTI_CPU_ISOLATE_COOLING_DEVICE) += cpu_isolate.o obj-$(CONFIG_QTI_LMH_CPU_VDD_COOLING_DEVICE) += lmh_cpu_vdd_cdev.o obj-$(CONFIG_QTI_LIMITS_ISENSE_CDSP) += msm_isense_cdsp.o diff --git a/drivers/thermal/qcom/adc-tm.c b/drivers/thermal/qcom/adc-tm.c index 195d2e9328ddc5a92d6357e2b5c05d3ed8520396..b6359dd946da132ce0f3947b4b7c5dabe88095bf 100644 --- a/drivers/thermal/qcom/adc-tm.c +++ b/drivers/thermal/qcom/adc-tm.c @@ -195,6 +195,10 @@ static const struct of_device_id adc_tm_match_table[] = { .compatible = "qcom,adc-tm5", .data = &data_adc_tm5, }, + { + .compatible = "qcom,adc-tm-rev2", + .data = &data_adc_tm_rev2, + }, { .compatible = "qcom,adc-tm5-iio", .data = &data_adc_tm5, diff --git a/drivers/thermal/qcom/adc-tm.h b/drivers/thermal/qcom/adc-tm.h index b616aff934fc93f2ce64ba36213ad19cfa20bc34..b26af664b69d0c42e4324ec68d1dbe4f7f19af0a 100644 --- a/drivers/thermal/qcom/adc-tm.h +++ b/drivers/thermal/qcom/adc-tm.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. */ #ifndef __QCOM_ADC_TM_H__ @@ -164,6 +164,7 @@ struct adc_tm_data { extern const struct adc_tm_data data_adc_tm5; extern const struct adc_tm_data data_adc_tm7; +extern const struct adc_tm_data data_adc_tm_rev2; /** * Channel index for the corresponding index to adc_tm_channel_select diff --git a/drivers/thermal/qcom/adc-tm5.c b/drivers/thermal/qcom/adc-tm5.c index 3371802c41cbdb8a6dcba3a4f9150103e2de28f5..c66f6c087db7686c394a27e87b9be95f6b1db174 100644 --- a/drivers/thermal/qcom/adc-tm5.c +++ b/drivers/thermal/qcom/adc-tm5.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. */ #include @@ -28,6 +28,9 @@ #define ADC_TM_MEAS_INTERVAL_CTL 0x44 #define ADC_TM_MEAS_INTERVAL_CTL2 0x45 +#define ADC_TM_MEAS_INTERVAL_CTL_660 0x50 +#define ADC_TM_MEAS_INTERVAL_CTL2_660 0x51 + #define ADC_TM_MEAS_INTERVAL_CTL2_SHIFT 0x4 #define ADC_TM_MEAS_INTERVAL_CTL2_MASK 0xf0 #define ADC_TM_MEAS_INTERVAL_CTL3_MASK 0xf @@ -1035,22 +1038,31 @@ static int adc_tm5_init(struct adc_tm_chip *chip, uint32_t dt_chans) { u8 buf[4], channels_available, meas_int_timer_2_3 = 0; int ret; + int dig_param_len = 4; + bool pmic_subtype_660 = false; unsigned int offset_btm_idx = 0, i; - ret = adc_tm5_read_reg(chip, ADC_TM_NUM_BTM, &channels_available, 1); - if (ret < 0) { - pr_err("read failed for BTM channels\n"); - return ret; - } + if ((chip->pmic_rev_id) && + (chip->pmic_rev_id->pmic_subtype == PM660_SUBTYPE)) { + dig_param_len = 2; + pmic_subtype_660 = true; + } else { + ret = adc_tm5_read_reg(chip, ADC_TM_NUM_BTM, + &channels_available, 1); + if (ret < 0) { + pr_err("read failed for BTM channels\n"); + return ret; + } - if (dt_chans > channels_available) { - pr_err("Number of nodes greater than channels supported:%d\n", - channels_available); - return -EINVAL; + if (dt_chans > channels_available) { + pr_err("More nodes than channels supported:%d\n", + channels_available); + return -EINVAL; + } } ret = adc_tm5_read_reg(chip, - ADC_TM_ADC_DIG_PARAM, buf, 4); + ADC_TM_ADC_DIG_PARAM, buf, dig_param_len); if (ret < 0) { pr_err("adc-tm block read failed with %d\n", ret); return ret; @@ -1072,10 +1084,18 @@ static int adc_tm5_init(struct adc_tm_chip *chip, uint32_t dt_chans) buf[3] = meas_int_timer_2_3; ret = adc_tm5_write_reg(chip, - ADC_TM_ADC_DIG_PARAM, buf, 4); + ADC_TM_ADC_DIG_PARAM, buf, dig_param_len); if (ret < 0) pr_err("adc-tm block write failed with %d\n", ret); + if (pmic_subtype_660) { + ret = adc_tm5_write_reg(chip, + ADC_TM_MEAS_INTERVAL_CTL_660, &buf[2], 2); + + if (ret < 0) + pr_err("adc-tm block write failed with %d\n", ret); + } + spin_lock_init(&chip->adc_tm_lock); mutex_init(&chip->adc_mutex_lock); @@ -1120,3 +1140,11 @@ const struct adc_tm_data data_adc_tm5 = { .hw_settle = (unsigned int []) {15, 100, 200, 300, 400, 500, 600, 700, 1, 2, 4, 8, 16, 32, 64, 128}, }; + +const struct adc_tm_data data_adc_tm_rev2 = { + .ops = &ops_adc_tm5, + .full_scale_code_volt = 0x4000, + .decimation = (unsigned int []) {256, 512, 1024}, + .hw_settle = (unsigned int []) {0, 100, 200, 300, 400, 500, 600, 700, + 800, 900, 1, 2, 4, 6, 8, 10}, +}; diff --git a/drivers/thermal/qcom/bcl_peripheral.c b/drivers/thermal/qcom/bcl_peripheral.c new file mode 100644 index 0000000000000000000000000000000000000000..a9cf4f5e1155d3d53b690f4fc7b92bea64599925 --- /dev/null +++ b/drivers/thermal/qcom/bcl_peripheral.c @@ -0,0 +1,779 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014-2020, The Linux Foundation. All rights reserved. + */ + +#define pr_fmt(fmt) "%s:%s " fmt, KBUILD_MODNAME, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../thermal_core.h" + +#define BCL_DRIVER_NAME "bcl_peripheral" +#define BCL_VBAT_INT "bcl-low-vbat" +#define BCL_VLOW_VBAT_INT "bcl-very-low-vbat" +#define BCL_CLOW_VBAT_INT "bcl-crit-low-vbat" +#define BCL_IBAT_INT "bcl-high-ibat" +#define BCL_VHIGH_IBAT_INT "bcl-very-high-ibat" +#define BCL_MONITOR_EN 0x46 +#define BCL_VBAT_MIN 0x5C +#define BCL_IBAT_MAX 0x5D +#define BCL_MAX_MIN_CLR 0x48 +#define BCL_IBAT_MAX_CLR 3 +#define BCL_VBAT_MIN_CLR 2 +#define BCL_VBAT_ADC_LOW 0x72 +#define BCL_VBAT_COMP_LOW 0x75 +#define BCL_VBAT_COMP_TLOW 0x76 +#define BCL_IBAT_HIGH 0x78 +#define BCL_IBAT_TOO_HIGH 0x79 +#define BCL_LMH_CFG 0xA3 +#define BCL_CFG 0x6A +#define LMH_INT_POL_HIGH 0x12 +#define LMH_INT_EN 0x15 +#define BCL_VBAT_SCALING 39000 +#define BCL_IBAT_SCALING 80 +#define BCL_LMH_CFG_VAL 0x3 +#define BCL_CFG_VAL 0x81 +#define LMH_INT_VAL 0x7 +#define BCL_READ_RETRY_LIMIT 3 +#define VAL_CP_REG_BUF_LEN 3 +#define VAL_REG_BUF_OFFSET 0 +#define VAL_CP_REG_BUF_OFFSET 2 +#define BCL_STD_VBAT_NR 9 +#define BCL_VBAT_NO_READING 127 + +enum bcl_dev_type { + BCL_HIGH_IBAT, + BCL_VHIGH_IBAT, + BCL_LOW_VBAT, + BCL_VLOW_VBAT, + BCL_CLOW_VBAT, + BCL_SOC_MONITOR, + BCL_TYPE_MAX, +}; + +struct bcl_peripheral_data { + int irq_num; + long trip_temp; + int trip_val; + int last_val; + struct mutex state_trans_lock; + bool irq_enabled; + struct thermal_zone_of_device_ops ops; + struct thermal_zone_device *tz_dev; +}; + +struct bcl_device { + struct regmap *regmap; + uint16_t fg_bcl_addr; + uint16_t fg_lmh_addr; + struct notifier_block psy_nb; + struct work_struct soc_eval_work; + struct bcl_peripheral_data param[BCL_TYPE_MAX]; +}; + +static struct bcl_device *bcl_perph; +static int vbat_low[BCL_STD_VBAT_NR] = { + 2400, 2500, 2600, 2700, 2800, 2900, + 3000, 3100, 3200}; + +static int bcl_read_multi_register(int16_t reg_offset, uint8_t *data, int len) +{ + int ret = 0; + + if (!bcl_perph) { + pr_err("BCL device not initialized\n"); + return -EINVAL; + } + ret = regmap_bulk_read(bcl_perph->regmap, + (bcl_perph->fg_bcl_addr + reg_offset), + data, len); + if (ret < 0) { + pr_err("Error reading register %d. err:%d\n", reg_offset, ret); + return ret; + } + + return ret; +} + +static int bcl_write_general_register(int16_t reg_offset, + uint16_t base, uint8_t data) +{ + int ret = 0; + uint8_t *write_buf = &data; + + if (!bcl_perph) { + pr_err("BCL device not initialized\n"); + return -EINVAL; + } + ret = regmap_write(bcl_perph->regmap, (base + reg_offset), *write_buf); + if (ret < 0) { + pr_err("Error reading register %d. err:%d\n", reg_offset, ret); + return ret; + } + pr_debug("wrote 0x%02x to 0x%04x\n", data, base + reg_offset); + + return ret; +} + +static int bcl_write_register(int16_t reg_offset, uint8_t data) +{ + return bcl_write_general_register(reg_offset, + bcl_perph->fg_bcl_addr, data); +} + +static void convert_vbat_to_adc_val(int *val) +{ + *val = (*val * 1000) / BCL_VBAT_SCALING; +} + +static void convert_adc_to_vbat_val(int *val) +{ + *val = *val * BCL_VBAT_SCALING / 1000; +} + +static void convert_ibat_to_adc_val(int *val) +{ + *val = *val / BCL_IBAT_SCALING; +} + +static void convert_adc_to_ibat_val(int *val) +{ + *val = *val * BCL_IBAT_SCALING; +} + +static int bcl_set_ibat(void *data, int low, int high) +{ + int ret = 0, ibat_ua, thresh_value; + int8_t val = 0; + int16_t addr; + struct bcl_peripheral_data *bat_data = + (struct bcl_peripheral_data *)data; + + thresh_value = high; + if (bat_data->trip_temp == thresh_value) + return 0; + + mutex_lock(&bat_data->state_trans_lock); + if (bat_data->irq_num && bat_data->irq_enabled) { + disable_irq_nosync(bat_data->irq_num); + bat_data->irq_enabled = false; + } + if (thresh_value == INT_MAX) { + bat_data->trip_temp = thresh_value; + goto set_trip_exit; + } + + ibat_ua = thresh_value; + convert_ibat_to_adc_val(&thresh_value); + val = (int8_t)thresh_value; + if (&bcl_perph->param[BCL_HIGH_IBAT] == bat_data) { + addr = BCL_IBAT_HIGH; + pr_debug("ibat high threshold:%d mA ADC:0x%02x\n", + ibat_ua, val); + } else if (&bcl_perph->param[BCL_VHIGH_IBAT] == bat_data) { + addr = BCL_IBAT_TOO_HIGH; + pr_debug("ibat too high threshold:%d mA ADC:0x%02x\n", + ibat_ua, val); + } else { + goto set_trip_exit; + } + ret = bcl_write_register(addr, val); + if (ret) { + pr_err("Error accessing BCL peripheral. err:%d\n", ret); + goto set_trip_exit; + } + bat_data->trip_temp = ibat_ua; + + if (bat_data->irq_num && !bat_data->irq_enabled) { + enable_irq(bat_data->irq_num); + bat_data->irq_enabled = true; + } + +set_trip_exit: + mutex_unlock(&bat_data->state_trans_lock); + + return ret; +} + +static int bcl_set_vbat(void *data, int low, int high) +{ + int ret = 0, vbat_uv, vbat_idx, thresh_value; + int8_t val = 0; + struct bcl_peripheral_data *bat_data = + (struct bcl_peripheral_data *)data; + uint16_t addr; + + thresh_value = low; + if (bat_data->trip_temp == thresh_value) + return 0; + + mutex_lock(&bat_data->state_trans_lock); + + if (bat_data->irq_num && bat_data->irq_enabled) { + disable_irq_nosync(bat_data->irq_num); + bat_data->irq_enabled = false; + } + if (thresh_value == INT_MIN) { + bat_data->trip_temp = thresh_value; + goto set_trip_exit; + } + vbat_uv = thresh_value; + convert_vbat_to_adc_val(&thresh_value); + val = (int8_t)thresh_value; + /* + * very low and critical low trip can support only standard + * trip thresholds + */ + if (&bcl_perph->param[BCL_LOW_VBAT] == bat_data) { + addr = BCL_VBAT_ADC_LOW; + pr_debug("vbat low threshold:%d mv ADC:0x%02x\n", + vbat_uv, val); + } else if (&bcl_perph->param[BCL_VLOW_VBAT] == bat_data) { + /* + * Scan the standard voltage table, sorted in ascending order + * and find the closest threshold that is lower or equal to + * the requested value. Passive trip supports thresholds + * indexed from 1...BCL_STD_VBAT_NR in the voltage table. + */ + for (vbat_idx = 2; vbat_idx < BCL_STD_VBAT_NR; + vbat_idx++) { + if (vbat_uv >= vbat_low[vbat_idx]) + continue; + break; + } + addr = BCL_VBAT_COMP_LOW; + val = vbat_idx - 2; + vbat_uv = vbat_low[vbat_idx - 1]; + pr_debug("vbat too low threshold:%d mv ADC:0x%02x\n", + vbat_uv, val); + } else if (&bcl_perph->param[BCL_CLOW_VBAT] == bat_data) { + /* Hot trip supports thresholds indexed from + * 0...BCL_STD_VBAT_NR-1 in the voltage table. + */ + for (vbat_idx = 1; vbat_idx < (BCL_STD_VBAT_NR - 1); + vbat_idx++) { + if (vbat_uv >= vbat_low[vbat_idx]) + continue; + break; + } + addr = BCL_VBAT_COMP_TLOW; + val = vbat_idx - 1; + vbat_uv = vbat_low[vbat_idx - 1]; + pr_debug("vbat critic low threshold:%d mv ADC:0x%02x\n", + vbat_uv, val); + } else { + goto set_trip_exit; + } + + ret = bcl_write_register(addr, val); + if (ret) { + pr_err("Error accessing BCL peripheral. err:%d\n", ret); + goto set_trip_exit; + } + bat_data->trip_temp = vbat_uv; + if (bat_data->irq_num && !bat_data->irq_enabled) { + enable_irq(bat_data->irq_num); + bat_data->irq_enabled = true; + } + +set_trip_exit: + mutex_unlock(&bat_data->state_trans_lock); + return ret; +} + +static int bcl_clear_vbat_min(void) +{ + int ret = 0; + + ret = bcl_write_register(BCL_MAX_MIN_CLR, + BIT(BCL_VBAT_MIN_CLR)); + if (ret) + pr_err("Error in clearing vbat min reg. err:%d\n", ret); + + return ret; +} + +static int bcl_clear_ibat_max(void) +{ + int ret = 0; + + ret = bcl_write_register(BCL_MAX_MIN_CLR, + BIT(BCL_IBAT_MAX_CLR)); + if (ret) + pr_err("Error in clearing ibat max reg. err:%d\n", ret); + + return ret; +} + +static int bcl_read_ibat(void *data, int *adc_value) +{ + int ret = 0, timeout = 0; + int8_t val[VAL_CP_REG_BUF_LEN] = {0}; + struct bcl_peripheral_data *bat_data = + (struct bcl_peripheral_data *)data; + + *adc_value = (int)val[VAL_REG_BUF_OFFSET]; + do { + ret = bcl_read_multi_register(BCL_IBAT_MAX, val, + VAL_CP_REG_BUF_LEN); + if (ret) { + pr_err("BCL register read error. err:%d\n", ret); + goto bcl_read_exit; + } + } while (val[VAL_REG_BUF_OFFSET] != val[VAL_CP_REG_BUF_OFFSET] + && timeout++ < BCL_READ_RETRY_LIMIT); + if (val[VAL_REG_BUF_OFFSET] != val[VAL_CP_REG_BUF_OFFSET]) { + ret = -ENODEV; + *adc_value = bat_data->last_val; + goto bcl_read_exit; + } + *adc_value = (int)val[VAL_REG_BUF_OFFSET]; + if (*adc_value == 0) { + /* + * The sensor sometime can read a value 0 if there is + * consequtive reads + */ + *adc_value = bat_data->last_val; + } else { + convert_adc_to_ibat_val(adc_value); + bat_data->last_val = *adc_value; + } + pr_debug("ibat:%d mA\n", bat_data->last_val); + +bcl_read_exit: + return ret; +} + +static int bcl_read_ibat_and_clear(void *data, int *adc_value) +{ + int ret = 0; + + ret = bcl_read_ibat(data, adc_value); + if (ret) + return ret; + return bcl_clear_ibat_max(); +} + +static int bcl_read_vbat(void *data, int *adc_value) +{ + int ret = 0, timeout = 0; + int8_t val[VAL_CP_REG_BUF_LEN] = {0}; + struct bcl_peripheral_data *bat_data = + (struct bcl_peripheral_data *)data; + + *adc_value = (int)val[VAL_REG_BUF_OFFSET]; + do { + ret = bcl_read_multi_register(BCL_VBAT_MIN, val, + VAL_CP_REG_BUF_LEN); + if (ret) { + pr_err("BCL register read error. err:%d\n", ret); + goto bcl_read_exit; + } + } while (val[VAL_REG_BUF_OFFSET] != val[VAL_CP_REG_BUF_OFFSET] + && timeout++ < BCL_READ_RETRY_LIMIT); + if (val[VAL_REG_BUF_OFFSET] != val[VAL_CP_REG_BUF_OFFSET]) { + ret = -ENODEV; + goto bcl_read_exit; + } + *adc_value = (int)val[VAL_REG_BUF_OFFSET]; + if (*adc_value == BCL_VBAT_NO_READING) { + *adc_value = bat_data->last_val; + } else { + convert_adc_to_vbat_val(adc_value); + bat_data->last_val = *adc_value; + } + pr_debug("vbat:%d mv\n", bat_data->last_val); + +bcl_read_exit: + return ret; +} + +static int bcl_read_vbat_and_clear(void *data, int *adc_value) +{ + int ret; + + ret = bcl_read_vbat(data, adc_value); + if (ret) + return ret; + return bcl_clear_vbat_min(); +} + +static irqreturn_t bcl_handle_ibat(int irq, void *data) +{ + struct bcl_peripheral_data *perph_data = + (struct bcl_peripheral_data *)data; + + mutex_lock(&perph_data->state_trans_lock); + if (!perph_data->irq_enabled) { + WARN_ON(1); + disable_irq_nosync(irq); + perph_data->irq_enabled = false; + goto exit_intr; + } + mutex_unlock(&perph_data->state_trans_lock); + of_thermal_handle_trip(perph_data->tz_dev); + + return IRQ_HANDLED; + +exit_intr: + mutex_unlock(&perph_data->state_trans_lock); + return IRQ_HANDLED; +} + +static irqreturn_t bcl_handle_vbat(int irq, void *data) +{ + struct bcl_peripheral_data *perph_data = + (struct bcl_peripheral_data *)data; + + mutex_lock(&perph_data->state_trans_lock); + if (!perph_data->irq_enabled) { + WARN_ON(1); + disable_irq_nosync(irq); + perph_data->irq_enabled = false; + goto exit_intr; + } + mutex_unlock(&perph_data->state_trans_lock); + of_thermal_handle_trip(perph_data->tz_dev); + + return IRQ_HANDLED; + +exit_intr: + mutex_unlock(&perph_data->state_trans_lock); + return IRQ_HANDLED; +} + +static int bcl_get_devicetree_data(struct platform_device *pdev) +{ + int ret = 0; + const __be32 *prop = NULL; + struct device_node *dev_node = pdev->dev.of_node; + + prop = of_get_address(dev_node, 0, NULL, NULL); + if (prop) { + bcl_perph->fg_bcl_addr = be32_to_cpu(*prop); + pr_debug("fg_user_adc@%04x\n", bcl_perph->fg_bcl_addr); + } else { + dev_err(&pdev->dev, "No fg_user_adc registers found\n"); + return -ENODEV; + } + + prop = of_get_address(dev_node, 1, NULL, NULL); + if (prop) { + bcl_perph->fg_lmh_addr = be32_to_cpu(*prop); + pr_debug("fg_lmh@%04x\n", bcl_perph->fg_lmh_addr); + } else { + dev_err(&pdev->dev, "No fg_lmh registers found\n"); + return -ENODEV; + } + + return ret; +} + +static int bcl_set_soc(void *data, int low, int high) +{ + struct bcl_peripheral_data *bat_data = + (struct bcl_peripheral_data *)data; + + if (low == bat_data->trip_temp) + return 0; + + mutex_lock(&bat_data->state_trans_lock); + pr_debug("low soc threshold:%d\n", low); + bat_data->trip_temp = low; + if (low == INT_MIN) { + bat_data->irq_enabled = false; + goto unlock_and_exit; + } + bat_data->irq_enabled = true; + schedule_work(&bcl_perph->soc_eval_work); + +unlock_and_exit: + mutex_unlock(&bat_data->state_trans_lock); + return 0; +} + +static int bcl_read_soc(void *data, int *val) +{ + static struct power_supply *batt_psy; + union power_supply_propval ret = {0,}; + int err = 0; + + *val = 100; + if (!batt_psy) + batt_psy = power_supply_get_by_name("battery"); + if (batt_psy) { + err = power_supply_get_property(batt_psy, + POWER_SUPPLY_PROP_CAPACITY, &ret); + if (err) { + pr_err("battery percentage read error:%d\n", + err); + return err; + } + *val = ret.intval; + } + pr_debug("soc:%d\n", *val); + + return err; +} + +static void bcl_evaluate_soc(struct work_struct *work) +{ + int battery_percentage; + struct bcl_peripheral_data *perph_data = + &bcl_perph->param[BCL_SOC_MONITOR]; + + if (bcl_read_soc((void *)perph_data, &battery_percentage)) + return; + + mutex_lock(&perph_data->state_trans_lock); + if (!perph_data->irq_enabled) + goto eval_exit; + if (battery_percentage > perph_data->trip_temp) + goto eval_exit; + + perph_data->trip_val = battery_percentage; + mutex_unlock(&perph_data->state_trans_lock); + of_thermal_handle_trip(perph_data->tz_dev); + + return; +eval_exit: + mutex_unlock(&perph_data->state_trans_lock); +} + +static int battery_supply_callback(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct power_supply *psy = data; + + if (strcmp(psy->desc->name, "battery")) + return NOTIFY_OK; + schedule_work(&bcl_perph->soc_eval_work); + + return NOTIFY_OK; +} + +static void bcl_fetch_trip(struct platform_device *pdev, const char *int_name, + struct bcl_peripheral_data *data, + irqreturn_t (*handle)(int, void *)) +{ + int ret = 0, irq_num = 0; + + /* + * Allow flexibility for the HLOS to set the trip temperature for + * all the thresholds but handle the interrupt for only one vbat + * and ibat interrupt. The LMH-DCVSh will handle and mitigate for the + * rest of the ibat/vbat interrupts. + */ + if (!handle) { + mutex_lock(&data->state_trans_lock); + data->irq_num = 0; + data->irq_enabled = false; + mutex_unlock(&data->state_trans_lock); + return; + } + + irq_num = platform_get_irq_byname(pdev, int_name); + if (irq_num) { + mutex_lock(&data->state_trans_lock); + ret = devm_request_threaded_irq(&pdev->dev, + irq_num, NULL, handle, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + int_name, data); + if (ret) { + dev_err(&pdev->dev, + "Error requesting trip irq. err:%d\n", + ret); + mutex_unlock(&data->state_trans_lock); + return; + } + disable_irq_nosync(irq_num); + data->irq_num = irq_num; + data->irq_enabled = false; + mutex_unlock(&data->state_trans_lock); + } +} + +static void bcl_probe_soc(struct platform_device *pdev) +{ + int ret = 0; + struct bcl_peripheral_data *soc_data; + + soc_data = &bcl_perph->param[BCL_SOC_MONITOR]; + mutex_init(&soc_data->state_trans_lock); + soc_data->ops.get_temp = bcl_read_soc; + soc_data->ops.set_trips = bcl_set_soc; + INIT_WORK(&bcl_perph->soc_eval_work, bcl_evaluate_soc); + bcl_perph->psy_nb.notifier_call = battery_supply_callback; + ret = power_supply_reg_notifier(&bcl_perph->psy_nb); + if (ret < 0) { + pr_err("Unable to register soc notifier. err:%d\n", ret); + return; + } + soc_data->tz_dev = thermal_zone_of_sensor_register(&pdev->dev, + BCL_SOC_MONITOR, soc_data, &soc_data->ops); + if (IS_ERR(soc_data->tz_dev)) { + pr_err("vbat register failed. err:%ld\n", + PTR_ERR(soc_data->tz_dev)); + return; + } + thermal_zone_device_update(soc_data->tz_dev, THERMAL_DEVICE_UP); + schedule_work(&bcl_perph->soc_eval_work); +} + +static void bcl_vbat_init(struct platform_device *pdev, + struct bcl_peripheral_data *vbat, enum bcl_dev_type type) +{ + mutex_init(&vbat->state_trans_lock); + switch (type) { + case BCL_LOW_VBAT: + bcl_fetch_trip(pdev, BCL_VBAT_INT, vbat, bcl_handle_vbat); + break; + case BCL_VLOW_VBAT: + bcl_fetch_trip(pdev, BCL_VLOW_VBAT_INT, vbat, NULL); + break; + case BCL_CLOW_VBAT: + bcl_fetch_trip(pdev, BCL_CLOW_VBAT_INT, vbat, NULL); + break; + default: + return; + } + vbat->ops.get_temp = bcl_read_vbat_and_clear; + vbat->ops.set_trips = bcl_set_vbat; + vbat->tz_dev = thermal_zone_of_sensor_register(&pdev->dev, + type, vbat, &vbat->ops); + if (IS_ERR(vbat->tz_dev)) { + pr_err("vbat register failed. err:%ld\n", + PTR_ERR(vbat->tz_dev)); + return; + } + thermal_zone_device_update(vbat->tz_dev, THERMAL_DEVICE_UP); +} + +static void bcl_probe_vbat(struct platform_device *pdev) +{ + bcl_vbat_init(pdev, &bcl_perph->param[BCL_LOW_VBAT], BCL_LOW_VBAT); + bcl_vbat_init(pdev, &bcl_perph->param[BCL_VLOW_VBAT], BCL_VLOW_VBAT); + bcl_vbat_init(pdev, &bcl_perph->param[BCL_CLOW_VBAT], BCL_CLOW_VBAT); +} + +static void bcl_ibat_init(struct platform_device *pdev, + struct bcl_peripheral_data *ibat, enum bcl_dev_type type) +{ + mutex_init(&ibat->state_trans_lock); + if (type == BCL_HIGH_IBAT) + bcl_fetch_trip(pdev, BCL_IBAT_INT, ibat, bcl_handle_ibat); + else + bcl_fetch_trip(pdev, BCL_VHIGH_IBAT_INT, ibat, NULL); + ibat->ops.get_temp = bcl_read_ibat_and_clear; + ibat->ops.set_trips = bcl_set_ibat; + ibat->tz_dev = thermal_zone_of_sensor_register(&pdev->dev, + type, ibat, &ibat->ops); + if (IS_ERR(ibat->tz_dev)) { + pr_err("ibat register failed. err:%ld\n", + PTR_ERR(ibat->tz_dev)); + return; + } + thermal_zone_device_update(ibat->tz_dev, THERMAL_DEVICE_UP); +} + +static void bcl_probe_ibat(struct platform_device *pdev) +{ + bcl_ibat_init(pdev, &bcl_perph->param[BCL_HIGH_IBAT], BCL_HIGH_IBAT); + bcl_ibat_init(pdev, &bcl_perph->param[BCL_VHIGH_IBAT], BCL_VHIGH_IBAT); +} + +static void bcl_configure_lmh_peripheral(void) +{ + bcl_write_register(BCL_LMH_CFG, BCL_LMH_CFG_VAL); + bcl_write_register(BCL_CFG, BCL_CFG_VAL); + bcl_write_general_register(LMH_INT_POL_HIGH, + bcl_perph->fg_lmh_addr, LMH_INT_VAL); + bcl_write_general_register(LMH_INT_EN, + bcl_perph->fg_lmh_addr, LMH_INT_VAL); +} + +static int bcl_remove(struct platform_device *pdev) +{ + int i = 0; + + for (; i < BCL_TYPE_MAX; i++) { + if (!bcl_perph->param[i].tz_dev) + continue; + if (i == BCL_SOC_MONITOR) { + power_supply_unreg_notifier(&bcl_perph->psy_nb); + flush_work(&bcl_perph->soc_eval_work); + } + thermal_zone_of_sensor_unregister(&pdev->dev, + bcl_perph->param[i].tz_dev); + } + bcl_perph = NULL; + + return 0; +} + +static int bcl_probe(struct platform_device *pdev) +{ + int ret = 0; + + bcl_perph = devm_kzalloc(&pdev->dev, sizeof(*bcl_perph), GFP_KERNEL); + if (!bcl_perph) + return -ENOMEM; + + bcl_perph->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!bcl_perph->regmap) { + dev_err(&pdev->dev, "Couldn't get parent's regmap\n"); + return -EINVAL; + } + + bcl_get_devicetree_data(pdev); + bcl_probe_ibat(pdev); + bcl_probe_vbat(pdev); + bcl_probe_soc(pdev); + bcl_configure_lmh_peripheral(); + + dev_set_drvdata(&pdev->dev, bcl_perph); + ret = bcl_write_register(BCL_MONITOR_EN, BIT(7)); + if (ret) { + pr_err("Error accessing BCL peripheral. err:%d\n", ret); + goto bcl_probe_exit; + } + + return 0; + +bcl_probe_exit: + bcl_remove(pdev); + return ret; +} + +static const struct of_device_id bcl_match[] = { + { + .compatible = "qcom,msm-bcl-lmh", + }, + {}, +}; + +static struct platform_driver bcl_driver = { + .probe = bcl_probe, + .remove = bcl_remove, + .driver = { + .name = BCL_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = bcl_match, + }, +}; + +builtin_platform_driver(bcl_driver); diff --git a/drivers/thermal/qcom/msm_lmh_dcvs.c b/drivers/thermal/qcom/msm_lmh_dcvs.c index 95155ac6c6a33625c8db5c07f4eaa54cdd46c3fb..7c9e53c1c6660ecbb9fb76e6c51d03f734f52cd2 100644 --- a/drivers/thermal/qcom/msm_lmh_dcvs.c +++ b/drivers/thermal/qcom/msm_lmh_dcvs.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "%s:%s " fmt, KBUILD_MODNAME, __func__ @@ -35,9 +35,16 @@ #include #define LIMITS_DCVSH 0x10 +#define LIMITS_PROFILE_CHANGE 0x01 #define LIMITS_NODE_DCVS 0x44435653 #define LIMITS_SUB_FN_THERMAL 0x54484D4C +#define LIMITS_SUB_FN_CRNT 0x43524E54 +#define LIMITS_SUB_FN_REL 0x52454C00 +#define LIMITS_SUB_FN_BCL 0x42434C00 + +#define LIMITS_ALGO_MODE_ENABLE 0x454E424C + #define LIMITS_HI_THRESHOLD 0x48494748 #define LIMITS_LOW_THRESHOLD 0x4C4F5700 #define LIMITS_ARM_THRESHOLD 0x41524D00 @@ -337,6 +344,23 @@ static struct limits_dcvs_hw *get_dcvsh_hw_from_cpu(int cpu) return NULL; } +static int enable_lmh(void) +{ + int ret = 0; + struct scm_desc desc_arg; + + desc_arg.args[0] = 1; + desc_arg.arginfo = SCM_ARGS(1, SCM_VAL); + ret = scm_call2(SCM_SIP_FNID(SCM_SVC_LMH, LIMITS_PROFILE_CHANGE), + &desc_arg); + if (ret) { + pr_err("Error switching profile:[1]. err:%d\n", ret); + return ret; + } + + return ret; +} + static int lmh_set_max_limit(int cpu, u32 freq) { struct limits_dcvs_hw *hw = get_dcvsh_hw_from_cpu(cpu); @@ -591,6 +615,45 @@ static int limits_dcvs_probe(struct platform_device *pdev) return -EINVAL; } + /* Check legcay LMH HW enablement is needed or not */ + if (of_property_read_bool(dn, "qcom,legacy-lmh-enable")) { + /* Enable the thermal algorithm early */ + ret = limits_dcvs_write(hw->affinity, LIMITS_SUB_FN_THERMAL, + LIMITS_ALGO_MODE_ENABLE, 1, 0, 0); + if (ret) { + pr_err("Unable to enable THERM algo for cluster%d\n", + affinity); + return ret; + } + /* Enable the LMH outer loop algorithm */ + ret = limits_dcvs_write(hw->affinity, LIMITS_SUB_FN_CRNT, + LIMITS_ALGO_MODE_ENABLE, 1, 0, 0); + if (ret) { + pr_err("Unable to enable CRNT algo for cluster%d\n", + affinity); + return ret; + } + /* Enable the Reliability algorithm */ + ret = limits_dcvs_write(hw->affinity, LIMITS_SUB_FN_REL, + LIMITS_ALGO_MODE_ENABLE, 1, 0, 0); + if (ret) { + pr_err("Unable to enable REL algo for cluster%d\n", + affinity); + return ret; + } + /* Enable the BCL algorithm */ + ret = limits_dcvs_write(hw->affinity, LIMITS_SUB_FN_BCL, + LIMITS_ALGO_MODE_ENABLE, 1, 0, 0); + if (ret) { + pr_err("Unable to enable BCL algo for cluster%d\n", + affinity); + return ret; + } + ret = enable_lmh(); + if (ret) + return ret; + } + no_cdev_register = of_property_read_bool(dn, "qcom,no-cooling-device-register"); @@ -631,11 +694,13 @@ static int limits_dcvs_probe(struct platform_device *pdev) return PTR_ERR(tzdev); } - hw->min_freq_reg = devm_ioremap(&pdev->dev, min_reg, 0x4); - if (!hw->min_freq_reg) { - pr_err("min frequency enable register remap failed\n"); - ret = -ENOMEM; - goto unregister_sensor; + if (!no_cdev_register) { + hw->min_freq_reg = devm_ioremap(&pdev->dev, min_reg, 0x4); + if (!hw->min_freq_reg) { + pr_err("min frequency enable register remap failed\n"); + ret = -ENOMEM; + goto unregister_sensor; + } } mutex_init(&hw->access_lock); diff --git a/drivers/thermal/qcom/qmi_cooling.c b/drivers/thermal/qcom/qmi_cooling.c index 5cd8aef55a16b7292c3b8c2a3df732f424d62aee..2498ec448e7d6f94abe5d60d82e4744394d7b0c1 100644 --- a/drivers/thermal/qcom/qmi_cooling.c +++ b/drivers/thermal/qcom/qmi_cooling.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "%s:%s " fmt, KBUILD_MODNAME, __func__ @@ -291,7 +291,7 @@ static int qmi_set_min_state(struct thermal_cooling_device *cdev, return 0; if (state > qmi_cdev->max_level) - return -EINVAL; + state = qmi_cdev->max_level; /* Convert state into QMI client expects for min state */ state = qmi_cdev->max_level - state; diff --git a/drivers/thermal/qcom/regulator_aop_cdev.c b/drivers/thermal/qcom/regulator_aop_cdev.c index 032cc93783bed032d71d8a906078a040d5d9e876..1c304db4c8a7bbddab6247196b63af4f91a1d8c7 100644 --- a/drivers/thermal/qcom/regulator_aop_cdev.c +++ b/drivers/thermal/qcom/regulator_aop_cdev.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ #include @@ -92,7 +92,7 @@ static int reg_send_min_state(struct thermal_cooling_device *cdev, int ret = 0; if (state > REG_CDEV_MAX_STATE) - return -EINVAL; + state = REG_CDEV_MAX_STATE; if (reg_dev->min_state == state) return ret; diff --git a/drivers/thermal/qcom/regulator_cdev.c b/drivers/thermal/qcom/regulator_cdev.c index b0421818ac22469bb4e0fb3e54c4afc1fb322c48..224478da295edf0d219101d107a54ee4def9045e 100644 --- a/drivers/thermal/qcom/regulator_cdev.c +++ b/drivers/thermal/qcom/regulator_cdev.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ #include @@ -47,7 +47,7 @@ static int reg_set_min_state(struct thermal_cooling_device *cdev, int ret = 0; if (state > reg_dev->lvl_ct) - return -EINVAL; + state = reg_dev->lvl_ct; if (reg_dev->min_reg_state == state) return ret; diff --git a/drivers/thermal/rcar_gen3_thermal.c b/drivers/thermal/rcar_gen3_thermal.c index 704c8ad045bb557977917ae513cd96286864512a..8f553453dd7fcb552698c5e802ccd88e7e04fae4 100644 --- a/drivers/thermal/rcar_gen3_thermal.c +++ b/drivers/thermal/rcar_gen3_thermal.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include @@ -81,7 +80,6 @@ struct rcar_gen3_thermal_tsc { struct rcar_gen3_thermal_priv { struct rcar_gen3_thermal_tsc *tscs[TSC_MAX_NUM]; unsigned int num_tscs; - spinlock_t lock; /* Protect interrupts on and off */ void (*thermal_init)(struct rcar_gen3_thermal_tsc *tsc); }; @@ -231,38 +229,16 @@ static irqreturn_t rcar_gen3_thermal_irq(int irq, void *data) { struct rcar_gen3_thermal_priv *priv = data; u32 status; - int i, ret = IRQ_HANDLED; + int i; - spin_lock(&priv->lock); for (i = 0; i < priv->num_tscs; i++) { status = rcar_gen3_thermal_read(priv->tscs[i], REG_GEN3_IRQSTR); rcar_gen3_thermal_write(priv->tscs[i], REG_GEN3_IRQSTR, 0); if (status) - ret = IRQ_WAKE_THREAD; + thermal_zone_device_update(priv->tscs[i]->zone, + THERMAL_EVENT_UNSPECIFIED); } - if (ret == IRQ_WAKE_THREAD) - rcar_thermal_irq_set(priv, false); - - spin_unlock(&priv->lock); - - return ret; -} - -static irqreturn_t rcar_gen3_thermal_irq_thread(int irq, void *data) -{ - struct rcar_gen3_thermal_priv *priv = data; - unsigned long flags; - int i; - - for (i = 0; i < priv->num_tscs; i++) - thermal_zone_device_update(priv->tscs[i]->zone, - THERMAL_EVENT_UNSPECIFIED); - - spin_lock_irqsave(&priv->lock, flags); - rcar_thermal_irq_set(priv, true); - spin_unlock_irqrestore(&priv->lock, flags); - return IRQ_HANDLED; } @@ -364,8 +340,6 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) if (soc_device_match(r8a7795es1)) priv->thermal_init = rcar_gen3_thermal_init_r8a7795es1; - spin_lock_init(&priv->lock); - platform_set_drvdata(pdev, priv); /* @@ -383,9 +357,9 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) if (!irqname) return -ENOMEM; - ret = devm_request_threaded_irq(dev, irq, rcar_gen3_thermal_irq, - rcar_gen3_thermal_irq_thread, - IRQF_SHARED, irqname, priv); + ret = devm_request_threaded_irq(dev, irq, NULL, + rcar_gen3_thermal_irq, + IRQF_ONESHOT, irqname, priv); if (ret) return ret; } diff --git a/drivers/thermal/tsens2xxx.c b/drivers/thermal/tsens2xxx.c index 941f7f437843df4677d4b929a35891dd1231a4ed..d04ea031aa43c03ad41c6b33a9ff13f63e096446 100644 --- a/drivers/thermal/tsens2xxx.c +++ b/drivers/thermal/tsens2xxx.c @@ -83,12 +83,70 @@ static void msm_tsens_convert_temp(int last_temp, int *temp) *temp = last_temp * TSENS_TM_SCALE_DECI_MILLIDEG; } +static int __tsens2xxx_hw_init(struct tsens_device *tmdev) +{ + void __iomem *srot_addr; + void __iomem *sensor_int_mask_addr; + unsigned int srot_val, crit_mask, crit_val; + void __iomem *int_mask_addr; + + srot_addr = TSENS_CTRL_ADDR(tmdev->tsens_srot_addr + 0x4); + srot_val = readl_relaxed(srot_addr); + if (!(srot_val & TSENS_EN)) { + pr_err("TSENS device is not enabled\n"); + return -ENODEV; + } + + if (tmdev->ctrl_data->cycle_monitor) { + sensor_int_mask_addr = + TSENS_TM_CRITICAL_INT_MASK(tmdev->tsens_tm_addr); + crit_mask = readl_relaxed(sensor_int_mask_addr); + crit_val = TSENS_TM_CRITICAL_CYCLE_MONITOR; + if (tmdev->ctrl_data->cycle_compltn_monitor_mask) + writel_relaxed((crit_mask | crit_val), + (TSENS_TM_CRITICAL_INT_MASK + (tmdev->tsens_tm_addr))); + else + writel_relaxed((crit_mask & ~crit_val), + (TSENS_TM_CRITICAL_INT_MASK + (tmdev->tsens_tm_addr))); + /*Update critical cycle monitoring*/ + mb(); + } + + if (tmdev->ctrl_data->wd_bark) { + sensor_int_mask_addr = + TSENS_TM_CRITICAL_INT_MASK(tmdev->tsens_tm_addr); + crit_mask = readl_relaxed(sensor_int_mask_addr); + crit_val = TSENS_TM_CRITICAL_WD_BARK; + if (tmdev->ctrl_data->wd_bark_mask) + writel_relaxed((crit_mask | crit_val), + (TSENS_TM_CRITICAL_INT_MASK + (tmdev->tsens_tm_addr))); + else + writel_relaxed((crit_mask & ~crit_val), + (TSENS_TM_CRITICAL_INT_MASK + (tmdev->tsens_tm_addr))); + /*Update watchdog monitoring*/ + mb(); + } + + int_mask_addr = TSENS_TM_UPPER_LOWER_INT_MASK(tmdev->tsens_tm_addr); + writel_relaxed(TSENS_TM_UPPER_LOWER_INT_DISABLE, int_mask_addr); + + writel_relaxed(TSENS_TM_CRITICAL_INT_EN | + TSENS_TM_UPPER_INT_EN | TSENS_TM_LOWER_INT_EN, + TSENS_TM_INT_EN(tmdev->tsens_tm_addr)); + + return 0; +} + static int tsens2xxx_get_temp(struct tsens_sensor *sensor, int *temp) { struct tsens_device *tmdev = NULL, *tmdev_itr; unsigned int code, ret, tsens_ret; void __iomem *sensor_addr, *trdy; - int last_temp = 0, last_temp2 = 0, last_temp3 = 0, count = 0; + int rc = 0, last_temp = 0, last_temp2 = 0, last_temp3 = 0, count = 0; static atomic_t in_tsens_reinit; if (!sensor) @@ -172,6 +230,13 @@ static int tsens2xxx_get_temp(struct tsens_sensor *sensor, int *temp) /* Notify thermal fwk */ list_for_each_entry(tmdev_itr, &tsens_device_list, list) { + rc = __tsens2xxx_hw_init(tmdev_itr); + if (rc) { + pr_err( + "%s: Failed to re-initialize TSENS controller\n", + __func__); + BUG(); + } queue_work(tmdev_itr->tsens_reinit_work, &tmdev_itr->therm_fwk_notify); } @@ -713,58 +778,11 @@ static int tsens2xxx_hw_sensor_en(struct tsens_device *tmdev, static int tsens2xxx_hw_init(struct tsens_device *tmdev) { - void __iomem *srot_addr; - void __iomem *sensor_int_mask_addr; - unsigned int srot_val, crit_mask, crit_val; - void __iomem *int_mask_addr; - - srot_addr = TSENS_CTRL_ADDR(tmdev->tsens_srot_addr + 0x4); - srot_val = readl_relaxed(srot_addr); - if (!(srot_val & TSENS_EN)) { - pr_err("TSENS device is not enabled\n"); - return -ENODEV; - } - - if (tmdev->ctrl_data->cycle_monitor) { - sensor_int_mask_addr = - TSENS_TM_CRITICAL_INT_MASK(tmdev->tsens_tm_addr); - crit_mask = readl_relaxed(sensor_int_mask_addr); - crit_val = TSENS_TM_CRITICAL_CYCLE_MONITOR; - if (tmdev->ctrl_data->cycle_compltn_monitor_mask) - writel_relaxed((crit_mask | crit_val), - (TSENS_TM_CRITICAL_INT_MASK - (tmdev->tsens_tm_addr))); - else - writel_relaxed((crit_mask & ~crit_val), - (TSENS_TM_CRITICAL_INT_MASK - (tmdev->tsens_tm_addr))); - /*Update critical cycle monitoring*/ - mb(); - } - - if (tmdev->ctrl_data->wd_bark) { - sensor_int_mask_addr = - TSENS_TM_CRITICAL_INT_MASK(tmdev->tsens_tm_addr); - crit_mask = readl_relaxed(sensor_int_mask_addr); - crit_val = TSENS_TM_CRITICAL_WD_BARK; - if (tmdev->ctrl_data->wd_bark_mask) - writel_relaxed((crit_mask | crit_val), - (TSENS_TM_CRITICAL_INT_MASK - (tmdev->tsens_tm_addr))); - else - writel_relaxed((crit_mask & ~crit_val), - (TSENS_TM_CRITICAL_INT_MASK - (tmdev->tsens_tm_addr))); - /*Update watchdog monitoring*/ - mb(); - } - - int_mask_addr = TSENS_TM_UPPER_LOWER_INT_MASK(tmdev->tsens_tm_addr); - writel_relaxed(TSENS_TM_UPPER_LOWER_INT_DISABLE, int_mask_addr); + int rc = 0; - writel_relaxed(TSENS_TM_CRITICAL_INT_EN | - TSENS_TM_UPPER_INT_EN | TSENS_TM_LOWER_INT_EN, - TSENS_TM_INT_EN(tmdev->tsens_tm_addr)); + rc = __tsens2xxx_hw_init(tmdev); + if (rc) + return rc; spin_lock_init(&tmdev->tsens_crit_lock); spin_lock_init(&tmdev->tsens_upp_low_lock); diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 678bf336594707439914939ec6f7f8b868228236..42d90ceec2794496e71de589a5cb0475b43e7d30 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -264,6 +264,12 @@ static int tb_switch_nvm_read(void *priv, unsigned int offset, void *val, return ret; } +static int tb_switch_nvm_no_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + return -EPERM; +} + static int tb_switch_nvm_write(void *priv, unsigned int offset, void *val, size_t bytes) { @@ -309,6 +315,7 @@ static struct nvmem_device *register_nvmem(struct tb_switch *sw, int id, config.read_only = true; } else { config.name = "nvm_non_active"; + config.reg_read = tb_switch_nvm_no_read; config.reg_write = tb_switch_nvm_write; config.root_only = true; } diff --git a/drivers/tty/ipwireless/hardware.c b/drivers/tty/ipwireless/hardware.c index b0baa4ce10f9897d5f2284c7aa6e50c6dd5bb7de..6bbf35682d5310cd1226410b4fdf91dfff1ae67a 100644 --- a/drivers/tty/ipwireless/hardware.c +++ b/drivers/tty/ipwireless/hardware.c @@ -1516,6 +1516,8 @@ static void ipw_send_setup_packet(struct ipw_hardware *hw) sizeof(struct ipw_setup_get_version_query_packet), ADDR_SETUP_PROT, TL_PROTOCOLID_SETUP, TL_SETUP_SIGNO_GET_VERSION_QRY); + if (!ver_packet) + return; ver_packet->header.length = sizeof(struct tl_setup_get_version_qry); /* diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c index 2a59f3306f8369655a92c4d482818833b7378412..191f91c440eb376e70e50c1e7e5529ae92234a3f 100644 --- a/drivers/tty/serdev/core.c +++ b/drivers/tty/serdev/core.c @@ -548,6 +548,12 @@ static acpi_status acpi_serdev_register_device(struct serdev_controller *ctrl, return AE_OK; } +static const struct acpi_device_id serdev_acpi_devices_blacklist[] = { + { "INT3511", 0 }, + { "INT3512", 0 }, + { }, +}; + static acpi_status acpi_serdev_add_device(acpi_handle handle, u32 level, void *data, void **return_value) { @@ -557,6 +563,10 @@ static acpi_status acpi_serdev_add_device(acpi_handle handle, u32 level, if (acpi_bus_get_device(handle, &adev)) return AE_OK; + /* Skip if black listed */ + if (!acpi_match_device_ids(adev, serdev_acpi_devices_blacklist)) + return AE_OK; + return acpi_serdev_register_device(ctrl, adev); } @@ -613,16 +623,18 @@ static int platform_serdev_register_devices(struct serdev_controller *ctrl) return err; } + /** - * serdev_controller_add() - Add an serdev controller + * serdev_controller_add_platform() - Add an serdev controller * @ctrl: controller to be registered. + * @platform: whether to permit fallthrough to platform device probe * * Register a controller previously allocated via serdev_controller_alloc() with - * the serdev core. + * the serdev core. Optionally permit probing via a platform device fallback. */ -int serdev_controller_add(struct serdev_controller *ctrl) +int serdev_controller_add_platform(struct serdev_controller *ctrl, bool platform) { - int ret_of, ret_acpi, ret_platform, ret; + int ret, ret_of, ret_acpi, ret_platform = -ENODEV; /* Can't register until after driver model init */ if (WARN_ON(!is_registered)) @@ -636,7 +648,8 @@ int serdev_controller_add(struct serdev_controller *ctrl) ret_of = of_serdev_register_devices(ctrl); ret_acpi = acpi_serdev_register_devices(ctrl); - ret_platform = platform_serdev_register_devices(ctrl); + if (platform) + ret_platform = platform_serdev_register_devices(ctrl); if (ret_of && ret_acpi && ret_platform) { dev_dbg(&ctrl->dev, "no devices registered: of:%d acpi:%d " "platform:%d\n", @@ -654,7 +667,7 @@ int serdev_controller_add(struct serdev_controller *ctrl) device_del(&ctrl->dev); return ret; }; -EXPORT_SYMBOL_GPL(serdev_controller_add); +EXPORT_SYMBOL_GPL(serdev_controller_add_platform); /* Remove a device associated with a controller */ static int serdev_remove_device(struct device *dev, void *data) diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/serdev-ttyport.c index 1b04b4b3090128089f1afcfdd170ceb4f5446597..b6b7ea6b5b82eeb7338b9cbb763dcf9dcca51648 100644 --- a/drivers/tty/serdev/serdev-ttyport.c +++ b/drivers/tty/serdev/serdev-ttyport.c @@ -7,9 +7,15 @@ #include #include #include +#include +#include #define SERPORT_ACTIVE 1 +static char *pdev_tty_port; +module_param(pdev_tty_port, charp, 0644); +MODULE_PARM_DESC(pdev_tty_port, "platform device tty port to claim"); + struct serport { struct tty_port *port; struct tty_struct *tty; @@ -265,19 +271,14 @@ struct device *serdev_tty_port_register(struct tty_port *port, struct device *parent, struct tty_driver *drv, int idx) { - const struct tty_port_client_operations *old_ops; struct serdev_controller *ctrl; struct serport *serport; + bool platform = false; int ret; if (!port || !drv || !parent) return ERR_PTR(-ENODEV); - if (port->console) { - /* can't convert tty's that are already in use */ - return ERR_PTR(-ENODEV); - } - ctrl = serdev_controller_alloc(parent, sizeof(struct serport)); if (!ctrl) return ERR_PTR(-ENOMEM); @@ -289,11 +290,27 @@ struct device *serdev_tty_port_register(struct tty_port *port, ctrl->ops = &ctrl_ops; - old_ops = port->client_ops; port->client_ops = &client_ops; port->client_data = ctrl; - ret = serdev_controller_add(ctrl); + /* There is not always a way to bind specific platform devices because + * they may be defined on platforms without DT or ACPI. When dealing + * with a platform devices, do not allow direct binding unless it is + * whitelisted by module parameter. If a platform device is otherwise + * described by DT or ACPI it will still be bound and this check will + * be ignored. + */ + if (parent->bus == &platform_bus_type) { + char tty_port_name[7]; + + sprintf(tty_port_name, "%s%d", drv->name, idx); + if (pdev_tty_port && + !strcmp(pdev_tty_port, tty_port_name)) { + platform = true; + } + } + + ret = serdev_controller_add_platform(ctrl, platform); if (ret) goto err_reset_data; @@ -302,7 +319,7 @@ struct device *serdev_tty_port_register(struct tty_port *port, err_reset_data: port->client_data = NULL; - port->client_ops = old_ops; + port->client_ops = &tty_port_default_client_ops; serdev_controller_put(ctrl); return ERR_PTR(ret); @@ -317,8 +334,8 @@ int serdev_tty_port_unregister(struct tty_port *port) return -ENODEV; serdev_controller_remove(ctrl); - port->client_ops = NULL; port->client_data = NULL; + port->client_ops = &tty_port_default_client_ops; serdev_controller_put(ctrl); return 0; diff --git a/drivers/tty/serial/8250/8250_aspeed_vuart.c b/drivers/tty/serial/8250/8250_aspeed_vuart.c index 435bec40dee6e4eebb87fc65f8b12489f3f99e77..2d5c3643e6a5ac35d2765906b68c3fb7ffb6befe 100644 --- a/drivers/tty/serial/8250/8250_aspeed_vuart.c +++ b/drivers/tty/serial/8250/8250_aspeed_vuart.c @@ -375,7 +375,6 @@ static int aspeed_vuart_probe(struct platform_device *pdev) port.port.line = rc; port.port.irq = irq_of_parse_and_map(np, 0); - port.port.irqflags = IRQF_SHARED; port.port.handle_irq = aspeed_vuart_handle_irq; port.port.iotype = UPIO_MEM; port.port.type = PORT_16550A; diff --git a/drivers/tty/serial/8250/8250_bcm2835aux.c b/drivers/tty/serial/8250/8250_bcm2835aux.c index bd53661103eb2b5b6331d147b25e19eb69e1b227..3173d98cb42d8ff822f37642c98999bb4ba1169d 100644 --- a/drivers/tty/serial/8250/8250_bcm2835aux.c +++ b/drivers/tty/serial/8250/8250_bcm2835aux.c @@ -115,7 +115,7 @@ static int bcm2835aux_serial_remove(struct platform_device *pdev) { struct bcm2835aux_data *data = platform_get_drvdata(pdev); - serial8250_unregister_port(data->uart.port.line); + serial8250_unregister_port(data->line); clk_disable_unprepare(data->clk); return 0; diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 69aaee5d7fe141984eeff400a2f0bd53366232a2..b9567ef843fc789b91984cf102293bd1e3568968 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -177,7 +177,7 @@ static int serial_link_irq_chain(struct uart_8250_port *up) struct hlist_head *h; struct hlist_node *n; struct irq_info *i; - int ret, irq_flags = up->port.flags & UPF_SHARE_IRQ ? IRQF_SHARED : 0; + int ret; mutex_lock(&hash_mutex); @@ -212,9 +212,8 @@ static int serial_link_irq_chain(struct uart_8250_port *up) INIT_LIST_HEAD(&up->list); i->head = &up->list; spin_unlock_irq(&i->lock); - irq_flags |= up->port.irqflags; ret = request_irq(up->port.irq, serial8250_interrupt, - irq_flags, up->port.name, i); + up->port.irqflags, up->port.name, i); if (ret < 0) serial_do_unlink(i, up); } diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 0089aa305ef9f931dcba31f4828f299241dea4dc..87073577643753945cadfd67ee7c6798de729a4d 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -24,6 +24,14 @@ #include "8250.h" +#define PCI_DEVICE_ID_ACCES_COM_2S 0x1052 +#define PCI_DEVICE_ID_ACCES_COM_4S 0x105d +#define PCI_DEVICE_ID_ACCES_COM_8S 0x106c +#define PCI_DEVICE_ID_ACCES_COM232_8 0x10a8 +#define PCI_DEVICE_ID_ACCES_COM_2SM 0x10d2 +#define PCI_DEVICE_ID_ACCES_COM_4SM 0x10db +#define PCI_DEVICE_ID_ACCES_COM_8SM 0x10ea + #define PCI_DEVICE_ID_COMMTECH_4224PCI335 0x0002 #define PCI_DEVICE_ID_COMMTECH_4222PCI335 0x0004 #define PCI_DEVICE_ID_COMMTECH_2324PCI335 0x000a @@ -571,6 +579,22 @@ static int __maybe_unused exar_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(exar_pci_pm, exar_suspend, exar_resume); +static const struct exar8250_board acces_com_2x = { + .num_ports = 2, + .setup = pci_xr17c154_setup, +}; + +static const struct exar8250_board acces_com_4x = { + .num_ports = 4, + .setup = pci_xr17c154_setup, +}; + +static const struct exar8250_board acces_com_8x = { + .num_ports = 8, + .setup = pci_xr17c154_setup, +}; + + static const struct exar8250_board pbn_fastcom335_2 = { .num_ports = 2, .setup = pci_fastcom335_setup, @@ -639,6 +663,15 @@ static const struct exar8250_board pbn_exar_XR17V8358 = { } static const struct pci_device_id exar_pci_tbl[] = { + EXAR_DEVICE(ACCESSIO, ACCES_COM_2S, acces_com_2x), + EXAR_DEVICE(ACCESSIO, ACCES_COM_4S, acces_com_4x), + EXAR_DEVICE(ACCESSIO, ACCES_COM_8S, acces_com_8x), + EXAR_DEVICE(ACCESSIO, ACCES_COM232_8, acces_com_8x), + EXAR_DEVICE(ACCESSIO, ACCES_COM_2SM, acces_com_2x), + EXAR_DEVICE(ACCESSIO, ACCES_COM_4SM, acces_com_4x), + EXAR_DEVICE(ACCESSIO, ACCES_COM_8SM, acces_com_8x), + + CONNECT_DEVICE(XR17C152, UART_2_232, pbn_connect), CONNECT_DEVICE(XR17C154, UART_4_232, pbn_connect), CONNECT_DEVICE(XR17C158, UART_8_232, pbn_connect), diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index 2488de1c4bc4b9ddeaff6f4f572806537b9be795..8fedc075fb1ebb7a5f39caea3dbf76ffb22bb3df 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -171,7 +171,6 @@ static int of_platform_serial_setup(struct platform_device *ofdev, port->type = type; port->uartclk = clk; - port->irqflags |= IRQF_SHARED; if (of_property_read_bool(np, "no-loopback-test")) port->flags |= UPF_SKIP_TEST; diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index aa4de6907f771a14d067358a1578523f29bd7dbc..5a04d4ddca736b7dc707d0df0de3d04797d536e9 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -2253,6 +2253,10 @@ int serial8250_do_startup(struct uart_port *port) } } + /* Check if we need to have shared IRQs */ + if (port->irq && (up->port.flags & UPF_SHARE_IRQ)) + up->port.irqflags |= IRQF_SHARED; + if (port->irq && !(up->port.flags & UPF_NO_THRE_TEST)) { unsigned char iir1; /* diff --git a/drivers/tty/serial/ar933x_uart.c b/drivers/tty/serial/ar933x_uart.c index 3bdd56a1021b26d6e74ff98ad6f0e25f25f5de08..ea12f10610b64dd46062735fc4e682213c407167 100644 --- a/drivers/tty/serial/ar933x_uart.c +++ b/drivers/tty/serial/ar933x_uart.c @@ -286,6 +286,10 @@ static void ar933x_uart_set_termios(struct uart_port *port, ar933x_uart_rmw_set(up, AR933X_UART_CS_REG, AR933X_UART_CS_HOST_INT_EN); + /* enable RX and TX ready overide */ + ar933x_uart_rmw_set(up, AR933X_UART_CS_REG, + AR933X_UART_CS_TX_READY_ORIDE | AR933X_UART_CS_RX_READY_ORIDE); + /* reenable the UART */ ar933x_uart_rmw(up, AR933X_UART_CS_REG, AR933X_UART_CS_IF_MODE_M << AR933X_UART_CS_IF_MODE_S, @@ -418,6 +422,10 @@ static int ar933x_uart_startup(struct uart_port *port) ar933x_uart_rmw_set(up, AR933X_UART_CS_REG, AR933X_UART_CS_HOST_INT_EN); + /* enable RX and TX ready overide */ + ar933x_uart_rmw_set(up, AR933X_UART_CS_REG, + AR933X_UART_CS_TX_READY_ORIDE | AR933X_UART_CS_RX_READY_ORIDE); + /* Enable RX interrupts */ up->ier = AR933X_UART_INT_RX_VALID; ar933x_uart_write(up, AR933X_UART_INT_EN_REG, up->ier); diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index f34520e9ad6e595fc1f6e17d27ea7acaef4f99ad..936d401f20b9583e1e8ed95c34a835e4f9dc5eab 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -490,7 +490,8 @@ static void atmel_stop_tx(struct uart_port *port) atmel_uart_writel(port, ATMEL_US_IDR, atmel_port->tx_done_mask); if (atmel_uart_is_half_duplex(port)) - atmel_start_rx(port); + if (!atomic_read(&atmel_port->tasklet_shutdown)) + atmel_start_rx(port); } diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index ee8a5cb61a5f10a3956c6dddead1b33183d04886..2daccb10ae2f3542e7938b2612756f63d06dfbbc 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -528,26 +528,26 @@ static int lpuart32_poll_init(struct uart_port *port) spin_lock_irqsave(&sport->port.lock, flags); /* Disable Rx & Tx */ - writel(0, sport->port.membase + UARTCTRL); + lpuart32_write(&sport->port, UARTCTRL, 0); - temp = readl(sport->port.membase + UARTFIFO); + temp = lpuart32_read(&sport->port, UARTFIFO); /* Enable Rx and Tx FIFO */ - writel(temp | UARTFIFO_RXFE | UARTFIFO_TXFE, - sport->port.membase + UARTFIFO); + lpuart32_write(&sport->port, UARTFIFO, + temp | UARTFIFO_RXFE | UARTFIFO_TXFE); /* flush Tx and Rx FIFO */ - writel(UARTFIFO_TXFLUSH | UARTFIFO_RXFLUSH, - sport->port.membase + UARTFIFO); + lpuart32_write(&sport->port, UARTFIFO, + UARTFIFO_TXFLUSH | UARTFIFO_RXFLUSH); /* explicitly clear RDRF */ - if (readl(sport->port.membase + UARTSTAT) & UARTSTAT_RDRF) { - readl(sport->port.membase + UARTDATA); - writel(UARTFIFO_RXUF, sport->port.membase + UARTFIFO); + if (lpuart32_read(&sport->port, UARTSTAT) & UARTSTAT_RDRF) { + lpuart32_read(&sport->port, UARTDATA); + lpuart32_write(&sport->port, UARTFIFO, UARTFIFO_RXUF); } /* Enable Rx and Tx */ - writel(UARTCTRL_RE | UARTCTRL_TE, sport->port.membase + UARTCTRL); + lpuart32_write(&sport->port, UARTCTRL, UARTCTRL_RE | UARTCTRL_TE); spin_unlock_irqrestore(&sport->port.lock, flags); return 0; @@ -555,18 +555,18 @@ static int lpuart32_poll_init(struct uart_port *port) static void lpuart32_poll_put_char(struct uart_port *port, unsigned char c) { - while (!(readl(port->membase + UARTSTAT) & UARTSTAT_TDRE)) + while (!(lpuart32_read(port, UARTSTAT) & UARTSTAT_TDRE)) barrier(); - writel(c, port->membase + UARTDATA); + lpuart32_write(port, UARTDATA, c); } static int lpuart32_poll_get_char(struct uart_port *port) { - if (!(readl(port->membase + UARTSTAT) & UARTSTAT_RDRF)) + if (!(lpuart32_read(port, UARTSTAT) & UARTSTAT_RDRF)) return NO_POLL_CHAR; - return readl(port->membase + UARTDATA); + return lpuart32_read(port, UARTDATA); } #endif diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 989ca7d662f3fa18fea74d3aa41ffca96362124f..4066cb2b79cb0c756e5be4232deaa61fd6a38ba9 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -608,7 +608,7 @@ static void imx_uart_dma_tx(struct imx_port *sport) sport->tx_bytes = uart_circ_chars_pending(xmit); - if (xmit->tail < xmit->head) { + if (xmit->tail < xmit->head || xmit->head == 0) { sport->dma_tx_nents = 1; sg_init_one(sgl, xmit->buf + xmit->tail, sport->tx_bytes); } else { @@ -624,7 +624,7 @@ static void imx_uart_dma_tx(struct imx_port *sport) dev_err(dev, "DMA mapping error for TX.\n"); return; } - desc = dmaengine_prep_slave_sg(chan, sgl, sport->dma_tx_nents, + desc = dmaengine_prep_slave_sg(chan, sgl, ret, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT); if (!desc) { dma_unmap_sg(dev, sgl, sport->dma_tx_nents, diff --git a/drivers/tty/serial/msm_geni_serial.c b/drivers/tty/serial/msm_geni_serial.c index b6bd906bcee704bc7b54e33a7496e6ba87798bd0..d0d509523951c7f118854c7c42eb96a6deeb0007 100644 --- a/drivers/tty/serial/msm_geni_serial.c +++ b/drivers/tty/serial/msm_geni_serial.c @@ -172,8 +172,7 @@ struct msm_geni_serial_port { unsigned int rx_fifo_wc, unsigned int rx_last_byte_valid, unsigned int rx_last, - bool drop_rx, - unsigned long *flags); + bool drop_rx); struct device *wrapper_dev; struct se_geni_rsc serial_rsc; dma_addr_t tx_dma; @@ -183,7 +182,7 @@ struct msm_geni_serial_port { int loopback; int wakeup_irq; unsigned char wakeup_byte; - struct wakeup_source geni_wake; + struct wakeup_source *geni_wake; void *ipc_log_tx; void *ipc_log_rx; void *ipc_log_pwr; @@ -213,12 +212,12 @@ static int handle_rx_console(struct uart_port *uport, unsigned int rx_fifo_wc, unsigned int rx_last_byte_valid, unsigned int rx_last, - bool drop_rx, unsigned long *flags); + bool drop_rx); static int handle_rx_hs(struct uart_port *uport, unsigned int rx_fifo_wc, unsigned int rx_last_byte_valid, unsigned int rx_last, - bool drop_rx, unsigned long *flags); + bool drop_rx); static unsigned int msm_geni_serial_tx_empty(struct uart_port *port); static int msm_geni_serial_power_on(struct uart_port *uport); static void msm_geni_serial_power_off(struct uart_port *uport); @@ -238,7 +237,7 @@ static int uart_line_id; static struct msm_geni_serial_port msm_geni_console_port; static struct msm_geni_serial_port msm_geni_serial_ports[GENI_UART_NR_PORTS]; static void msm_geni_serial_handle_isr(struct uart_port *uport, - unsigned long *flags); + unsigned long *flags, bool is_irq_masked); /* * The below API is required to check if uport->lock (spinlock) @@ -351,7 +350,6 @@ bool geni_wait_for_cmd_done(struct uart_port *uport, bool is_irq_masked) { struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport); unsigned long timeout = POLL_ITERATIONS; - unsigned long ret; unsigned long flags = 0; /* @@ -366,13 +364,13 @@ bool geni_wait_for_cmd_done(struct uart_port *uport, bool is_irq_masked) */ if (msm_port->m_cmd) { while (!msm_port->m_cmd_done && timeout > 0) { - msm_geni_serial_handle_isr(uport, &flags); + msm_geni_serial_handle_isr(uport, &flags, true); timeout--; udelay(100); } } else if (msm_port->s_cmd) { while (!msm_port->s_cmd_done && timeout > 0) { - msm_geni_serial_handle_isr(uport, &flags); + msm_geni_serial_handle_isr(uport, &flags, true); timeout--; udelay(100); } @@ -380,16 +378,16 @@ bool geni_wait_for_cmd_done(struct uart_port *uport, bool is_irq_masked) } else { /* Waiting for 10 milli second for interrupt to be fired */ if (msm_port->m_cmd) - ret = wait_for_completion_timeout + timeout = wait_for_completion_timeout (&msm_port->m_cmd_timeout, msecs_to_jiffies(POLL_WAIT_TIMEOUT_MSEC)); else if (msm_port->s_cmd) - ret = wait_for_completion_timeout + timeout = wait_for_completion_timeout (&msm_port->s_cmd_timeout, msecs_to_jiffies(POLL_WAIT_TIMEOUT_MSEC)); } - return ret ? 0 : 1; + return timeout ? 0 : 1; } static void msm_geni_serial_config_port(struct uart_port *uport, int cfg_flags) @@ -627,7 +625,8 @@ static unsigned int msm_geni_serial_get_mctrl(struct uart_port *uport) geni_ios = geni_read_reg_nolog(uport->membase, SE_GENI_IOS); if (!(geni_ios & IO2_DATA_IN)) mctrl |= TIOCM_CTS; - + IPC_LOG_MSG(port->ipc_log_misc, "%s: geni_ios:0x%x, mctrl:0x%x\n", + __func__, geni_ios, mctrl); return mctrl; } @@ -1033,13 +1032,12 @@ static int handle_rx_console(struct uart_port *uport, unsigned int rx_fifo_wc, unsigned int rx_last_byte_valid, unsigned int rx_last, - bool drop_rx, unsigned long *flags) + bool drop_rx) { int i, c; unsigned char *rx_char; struct tty_port *tport; struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport); - bool locked; tport = &uport->state->port; for (i = 0; i < rx_fifo_wc; i++) { @@ -1065,23 +1063,6 @@ static int handle_rx_console(struct uart_port *uport, tty_insert_flip_char(tport, rx_char[c], flag); } } - if (!drop_rx) { - /* - * Driver acquiring port->lock in isr function and calling - * tty_flip_buffer_push() which in turn will wait for - * another lock from framework __queue_work function. - * release the port lock before calling tty_flip_buffer_push() - * to avoid deadlock scenarios. - */ - locked = msm_geni_serial_spinlocked(uport); - if (locked) { - spin_unlock_irqrestore(&uport->lock, *flags); - tty_flip_buffer_push(tport); - spin_lock_irqsave(&uport->lock, *flags); - } else { - tty_flip_buffer_push(tport); - } - } return 0; } #else @@ -1089,7 +1070,7 @@ static int handle_rx_console(struct uart_port *uport, unsigned int rx_fifo_wc, unsigned int rx_last_byte_valid, unsigned int rx_last, - bool drop_rx, unsigned long *flags) + bool drop_rx) { return -EPERM; } @@ -1520,7 +1501,7 @@ static void stop_rx_sequencer(struct uart_port *uport) "%s cancel failed is_rx_active:%d 0x%x\n", __func__, is_rx_active, geni_status); if (uart_console(uport) && !is_rx_active) { - msm_geni_serial_handle_isr(uport, &flags); + msm_geni_serial_handle_isr(uport, &flags, true); goto exit_rx_seq; } port->s_cmd_done = false; @@ -1568,7 +1549,7 @@ static int handle_rx_hs(struct uart_port *uport, unsigned int rx_fifo_wc, unsigned int rx_last_byte_valid, unsigned int rx_last, - bool drop_rx, unsigned long *flags) + bool drop_rx) { unsigned char *rx_char; struct tty_port *tport; @@ -1600,8 +1581,8 @@ static int handle_rx_hs(struct uart_port *uport, return ret; } -static int msm_geni_serial_handle_rx(struct uart_port *uport, bool drop_rx, - unsigned long *flags) +static int msm_geni_serial_handle_rx(struct uart_port *uport, bool drop_rx) + { int ret = 0; unsigned int rx_fifo_status; @@ -1620,7 +1601,7 @@ static int msm_geni_serial_handle_rx(struct uart_port *uport, bool drop_rx, rx_last = rx_fifo_status & RX_LAST; if (rx_fifo_wc) ret = port->handle_rx(uport, rx_fifo_wc, rx_last_byte_valid, - rx_last, drop_rx, flags); + rx_last, drop_rx); return ret; } @@ -1803,7 +1784,8 @@ static int msm_geni_serial_handle_dma_tx(struct uart_port *uport) } static void msm_geni_serial_handle_isr(struct uart_port *uport, - unsigned long *flags) + unsigned long *flags, + bool is_irq_masked) { unsigned int m_irq_status; unsigned int s_irq_status; @@ -1889,8 +1871,16 @@ static void msm_geni_serial_handle_isr(struct uart_port *uport, } if (s_irq_status & (S_RX_FIFO_WATERMARK_EN | - S_RX_FIFO_LAST_EN)) - msm_geni_serial_handle_rx(uport, drop_rx, flags); + S_RX_FIFO_LAST_EN)) { + msm_geni_serial_handle_rx(uport, drop_rx); + if (!drop_rx && !is_irq_masked) { + spin_unlock_irqrestore(&uport->lock, *flags); + tty_flip_buffer_push(tport); + spin_lock_irqsave(&uport->lock, *flags); + } else if (!drop_rx) { + tty_flip_buffer_push(tport); + } + } } else { dma_tx_status = geni_read_reg_nolog(uport->membase, SE_DMA_TX_IRQ_STAT); @@ -1902,15 +1892,15 @@ static void msm_geni_serial_handle_isr(struct uart_port *uport, geni_write_reg_nolog(dma_tx_status, uport->membase, SE_DMA_TX_IRQ_CLR); - if (dma_tx_status & TX_DMA_DONE) - msm_geni_serial_handle_dma_tx(uport); - if (dma_tx_status & (TX_RESET_DONE | TX_GENI_CANCEL_IRQ)) m_cmd_done = true; if (m_irq_status & (M_CMD_CANCEL_EN | M_CMD_ABORT_EN)) m_cmd_done = true; + + if ((dma_tx_status & TX_DMA_DONE) && !m_cmd_done) + msm_geni_serial_handle_dma_tx(uport); } if (dma_rx_status) { @@ -1984,7 +1974,7 @@ static irqreturn_t msm_geni_serial_isr(int isr, void *dev) unsigned long flags; spin_lock_irqsave(&uport->lock, flags); - msm_geni_serial_handle_isr(uport, &flags); + msm_geni_serial_handle_isr(uport, &flags, false); spin_unlock_irqrestore(&uport->lock, flags); return IRQ_HANDLED; } @@ -2006,7 +1996,7 @@ static irqreturn_t msm_geni_wakeup_isr(int isr, void *dev) __func__, port->wakeup_byte); port->edge_count = 0; tty_flip_buffer_push(tty->port); - __pm_wakeup_event(&port->geni_wake, WAKEBYTE_TIMEOUT_MSEC); + __pm_wakeup_event(port->geni_wake, WAKEBYTE_TIMEOUT_MSEC); } else if (port->edge_count < 2) { port->edge_count++; } @@ -2128,10 +2118,6 @@ static int msm_geni_serial_port_setup(struct uart_port *uport) SE_GENI_RX_PACKING_CFG0); geni_write_reg_nolog(cfg1, uport->membase, SE_GENI_RX_PACKING_CFG1); - msm_port->handle_rx = handle_rx_hs; - msm_port->rx_fifo = devm_kzalloc(uport->dev, - sizeof(msm_port->rx_fifo_depth * sizeof(u32)), - GFP_KERNEL); if (!msm_port->rx_fifo) { ret = -ENOMEM; goto exit_portsetup; @@ -2659,6 +2645,12 @@ static void msm_geni_serial_cancel_rx(struct uart_port *uport) unsigned int irq_status; u32 rx_fifo_status; u32 rx_fifo_wc; + u32 geni_status; + + geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS); + /* Possible thats stop rx is already done from UEFI end */ + if (!(geni_status & S_GENI_CMD_ACTIVE)) + return; geni_cancel_s_cmd(uport->membase); /* Ensure this goes through before polling. */ @@ -3196,7 +3188,17 @@ static int msm_geni_serial_probe(struct platform_device *pdev) } } - wakeup_source_init(&dev_port->geni_wake, dev_name(&pdev->dev)); + if (!is_console) { + dev_port->geni_wake = wakeup_source_register(uport->dev, + dev_name(&pdev->dev)); + if (!dev_port->geni_wake) { + dev_err(&pdev->dev, + "Failed to register wakeup_source\n"); + ret = -ENOMEM; + goto exit_geni_serial_probe; + } + } + dev_port->tx_fifo_depth = DEF_FIFO_DEPTH_WORDS; dev_port->rx_fifo_depth = DEF_FIFO_DEPTH_WORDS; dev_port->tx_fifo_width = DEF_FIFO_WIDTH_BITS; @@ -3210,7 +3212,7 @@ static int msm_geni_serial_probe(struct platform_device *pdev) if (uport->irq < 0) { ret = uport->irq; dev_err(&pdev->dev, "Failed to get IRQ %d\n", ret); - goto exit_geni_serial_probe; + goto exit_wakeup_unregister; } dev_port->name = devm_kasprintf(uport->dev, GFP_KERNEL, @@ -3221,7 +3223,7 @@ static int msm_geni_serial_probe(struct platform_device *pdev) if (ret) { dev_err(uport->dev, "%s: Failed to get IRQ ret %d\n", __func__, ret); - goto exit_geni_serial_probe; + goto exit_wakeup_unregister; } uport->private_data = (void *)drv; @@ -3231,6 +3233,11 @@ static int msm_geni_serial_probe(struct platform_device *pdev) dev_port->rx_fifo = devm_kzalloc(uport->dev, sizeof(u32), GFP_KERNEL); } else { + dev_port->handle_rx = handle_rx_hs; + dev_port->rx_fifo = devm_kzalloc(uport->dev, + sizeof(dev_port->rx_fifo_depth * sizeof(u32)), + GFP_KERNEL); + pm_runtime_set_suspended(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, 150); pm_runtime_use_autosuspend(&pdev->dev); @@ -3254,10 +3261,20 @@ static int msm_geni_serial_probe(struct platform_device *pdev) dev_port->port_setup = false; ret = msm_geni_serial_get_ver_info(uport); if (ret) - goto exit_geni_serial_probe; + goto exit_wakeup_unregister; ret = uart_add_one_port(drv, uport); + if (ret) + goto exit_wakeup_unregister; + IPC_LOG_MSG(dev_port->ipc_log_misc, "%s: port:%s irq:%d\n", __func__, + uport->name, uport->irq); + + return 0; + +exit_wakeup_unregister: + if (!is_console) + wakeup_source_unregister(dev_port->geni_wake); exit_geni_serial_probe: IPC_LOG_MSG(dev_port->ipc_log_misc, "%s: ret:%d\n", __func__, ret); return ret; @@ -3269,7 +3286,8 @@ static int msm_geni_serial_remove(struct platform_device *pdev) struct uart_driver *drv = (struct uart_driver *)port->uport.private_data; - wakeup_source_trash(&port->geni_wake); + if (!uart_console(&port->uport)) + wakeup_source_unregister(port->geni_wake); uart_remove_one_port(drv, &port->uport); if (port->rx_dma) { geni_se_iommu_free_buf(port->wrapper_dev, &port->rx_dma, @@ -3314,7 +3332,7 @@ static int msm_geni_serial_runtime_suspend(struct device *dev) enable_irq(port->wakeup_irq); } IPC_LOG_MSG(port->ipc_log_pwr, "%s:\n", __func__); - __pm_relax(&port->geni_wake); + __pm_relax(port->geni_wake); exit_runtime_suspend: return ret; } @@ -3329,8 +3347,8 @@ static int msm_geni_serial_runtime_resume(struct device *dev) * Do an unconditional relax followed by a stay awake in case the * wake source is activated by the wakeup isr. */ - __pm_relax(&port->geni_wake); - __pm_stay_awake(&port->geni_wake); + __pm_relax(port->geni_wake); + __pm_stay_awake(port->geni_wake); if (port->wakeup_irq > 0) disable_irq(port->wakeup_irq); /* @@ -3342,7 +3360,7 @@ static int msm_geni_serial_runtime_resume(struct device *dev) ret = se_geni_resources_on(&port->serial_rsc); if (ret) { dev_err(dev, "%s: Error ret %d\n", __func__, ret); - __pm_relax(&port->geni_wake); + __pm_relax(port->geni_wake); goto exit_runtime_resume; } start_rx_sequencer(&port->uport); diff --git a/drivers/tty/serial/mvebu-uart.c b/drivers/tty/serial/mvebu-uart.c index 7d26c9b57d8edf1baf53920dc370bea5f8d86b13..fb9d369e0f50c7b95dd80e96f145c9489bc3094a 100644 --- a/drivers/tty/serial/mvebu-uart.c +++ b/drivers/tty/serial/mvebu-uart.c @@ -840,7 +840,7 @@ static int mvebu_uart_probe(struct platform_device *pdev) port->membase = devm_ioremap_resource(&pdev->dev, reg); if (IS_ERR(port->membase)) - return -PTR_ERR(port->membase); + return PTR_ERR(port->membase); mvuart = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_uart), GFP_KERNEL); diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index cb85002a10d8d16c9c0b4ec5bc70c1994822e25a..3245cdbf91161165f8c3405aff81e54400bfc2d7 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -235,6 +235,7 @@ struct eg20t_port { struct dma_chan *chan_rx; struct scatterlist *sg_tx_p; int nent; + int orig_nent; struct scatterlist sg_rx; int tx_dma_use; void *rx_buf_virt; @@ -789,9 +790,10 @@ static void pch_dma_tx_complete(void *arg) } xmit->tail &= UART_XMIT_SIZE - 1; async_tx_ack(priv->desc_tx); - dma_unmap_sg(port->dev, sg, priv->nent, DMA_TO_DEVICE); + dma_unmap_sg(port->dev, sg, priv->orig_nent, DMA_TO_DEVICE); priv->tx_dma_use = 0; priv->nent = 0; + priv->orig_nent = 0; kfree(priv->sg_tx_p); pch_uart_hal_enable_interrupt(priv, PCH_UART_HAL_TX_INT); } @@ -1015,6 +1017,7 @@ static unsigned int dma_handle_tx(struct eg20t_port *priv) dev_err(priv->port.dev, "%s:dma_map_sg Failed\n", __func__); return 0; } + priv->orig_nent = num; priv->nent = nent; for (i = 0; i < nent; i++, sg++) { diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index b3f7d1a1e97f824e4b4793ec30c677432261954a..4458419f053b63ee0652148faf0d598b1ed6b958 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -85,7 +85,7 @@ #define DEF_FIFO_DEPTH_WORDS 16 #define DEF_TX_WM 2 #define DEF_FIFO_WIDTH_BITS 32 -#define UART_CONSOLE_RX_WM 2 +#define UART_RX_WM 2 #define MAX_LOOPBACK_CFG 3 #ifdef CONFIG_CONSOLE_POLL @@ -101,10 +101,6 @@ struct qcom_geni_serial_port { u32 tx_fifo_depth; u32 tx_fifo_width; u32 rx_fifo_depth; - u32 tx_wm; - u32 rx_wm; - u32 rx_rfr; - enum geni_se_xfer_mode xfer_mode; bool setup; int (*handle_rx)(struct uart_port *uport, u32 bytes, bool drop); unsigned int baud; @@ -125,6 +121,7 @@ static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop); static int handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop); static unsigned int qcom_geni_serial_tx_empty(struct uart_port *port); static void qcom_geni_serial_stop_rx(struct uart_port *uport); +static void qcom_geni_serial_handle_rx(struct uart_port *uport, bool drop); static const unsigned long root_freq[] = {7372800, 14745600, 19200000, 29491200, 32000000, 48000000, 64000000, 80000000, @@ -226,7 +223,7 @@ static unsigned int qcom_geni_serial_get_mctrl(struct uart_port *uport) if (uart_console(uport)) { mctrl |= TIOCM_CTS; } else { - geni_ios = readl_relaxed(uport->membase + SE_GENI_IOS); + geni_ios = readl(uport->membase + SE_GENI_IOS); if (!(geni_ios & IO2_DATA_IN)) mctrl |= TIOCM_CTS; } @@ -244,7 +241,7 @@ static void qcom_geni_serial_set_mctrl(struct uart_port *uport, if (!(mctrl & TIOCM_RTS)) uart_manual_rfr = UART_MANUAL_RFR_EN | UART_RFR_NOT_READY; - writel_relaxed(uart_manual_rfr, uport->membase + SE_UART_MANUAL_RFR); + writel(uart_manual_rfr, uport->membase + SE_UART_MANUAL_RFR); } static const char *qcom_geni_serial_get_type(struct uart_port *uport) @@ -273,9 +270,6 @@ static bool qcom_geni_serial_poll_bit(struct uart_port *uport, unsigned int fifo_bits; unsigned long timeout_us = 20000; - /* Ensure polling is not re-ordered before the prior writes/reads */ - mb(); - if (uport->private_data) { port = to_dev_port(uport, uport); baud = port->baud; @@ -295,7 +289,7 @@ static bool qcom_geni_serial_poll_bit(struct uart_port *uport, */ timeout_us = DIV_ROUND_UP(timeout_us, 10) * 10; while (timeout_us) { - reg = readl_relaxed(uport->membase + offset); + reg = readl(uport->membase + offset); if ((bool)(reg & field) == set) return true; udelay(10); @@ -308,7 +302,7 @@ static void qcom_geni_serial_setup_tx(struct uart_port *uport, u32 xmit_size) { u32 m_cmd; - writel_relaxed(xmit_size, uport->membase + SE_UART_TX_TRANS_LEN); + writel(xmit_size, uport->membase + SE_UART_TX_TRANS_LEN); m_cmd = UART_START_TX << M_OPCODE_SHFT; writel(m_cmd, uport->membase + SE_GENI_M_CMD0); } @@ -321,13 +315,13 @@ static void qcom_geni_serial_poll_tx_done(struct uart_port *uport) done = qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, M_CMD_DONE_EN, true); if (!done) { - writel_relaxed(M_GENI_CMD_ABORT, uport->membase + + writel(M_GENI_CMD_ABORT, uport->membase + SE_GENI_M_CMD_CTRL_REG); irq_clear |= M_CMD_ABORT_EN; qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, M_CMD_ABORT_EN, true); } - writel_relaxed(irq_clear, uport->membase + SE_GENI_M_IRQ_CLEAR); + writel(irq_clear, uport->membase + SE_GENI_M_IRQ_CLEAR); } static void qcom_geni_serial_abort_rx(struct uart_port *uport) @@ -337,8 +331,8 @@ static void qcom_geni_serial_abort_rx(struct uart_port *uport) writel(S_GENI_CMD_ABORT, uport->membase + SE_GENI_S_CMD_CTRL_REG); qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG, S_GENI_CMD_ABORT, false); - writel_relaxed(irq_clear, uport->membase + SE_GENI_S_IRQ_CLEAR); - writel_relaxed(FORCE_DEFAULT, uport->membase + GENI_FORCE_DEFAULT_REG); + writel(irq_clear, uport->membase + SE_GENI_S_IRQ_CLEAR); + writel(FORCE_DEFAULT, uport->membase + GENI_FORCE_DEFAULT_REG); } #ifdef CONFIG_CONSOLE_POLL @@ -347,19 +341,13 @@ static int qcom_geni_serial_get_char(struct uart_port *uport) u32 rx_fifo; u32 status; - status = readl_relaxed(uport->membase + SE_GENI_M_IRQ_STATUS); - writel_relaxed(status, uport->membase + SE_GENI_M_IRQ_CLEAR); - - status = readl_relaxed(uport->membase + SE_GENI_S_IRQ_STATUS); - writel_relaxed(status, uport->membase + SE_GENI_S_IRQ_CLEAR); + status = readl(uport->membase + SE_GENI_M_IRQ_STATUS); + writel(status, uport->membase + SE_GENI_M_IRQ_CLEAR); - /* - * Ensure the writes to clear interrupts is not re-ordered after - * reading the data. - */ - mb(); + status = readl(uport->membase + SE_GENI_S_IRQ_STATUS); + writel(status, uport->membase + SE_GENI_S_IRQ_CLEAR); - status = readl_relaxed(uport->membase + SE_GENI_RX_FIFO_STATUS); + status = readl(uport->membase + SE_GENI_RX_FIFO_STATUS); if (!(status & RX_FIFO_WC_MSK)) return NO_POLL_CHAR; @@ -370,15 +358,12 @@ static int qcom_geni_serial_get_char(struct uart_port *uport) static void qcom_geni_serial_poll_put_char(struct uart_port *uport, unsigned char c) { - struct qcom_geni_serial_port *port = to_dev_port(uport, uport); - - writel_relaxed(port->tx_wm, uport->membase + SE_GENI_TX_WATERMARK_REG); + writel(DEF_TX_WM, uport->membase + SE_GENI_TX_WATERMARK_REG); qcom_geni_serial_setup_tx(uport, 1); WARN_ON(!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, M_TX_FIFO_WATERMARK_EN, true)); - writel_relaxed(c, uport->membase + SE_GENI_TX_FIFOn); - writel_relaxed(M_TX_FIFO_WATERMARK_EN, uport->membase + - SE_GENI_M_IRQ_CLEAR); + writel(c, uport->membase + SE_GENI_TX_FIFOn); + writel(M_TX_FIFO_WATERMARK_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); qcom_geni_serial_poll_tx_done(uport); } #endif @@ -386,7 +371,7 @@ static void qcom_geni_serial_poll_put_char(struct uart_port *uport, #ifdef CONFIG_SERIAL_QCOM_GENI_CONSOLE static void qcom_geni_serial_wr_char(struct uart_port *uport, int ch) { - writel_relaxed(ch, uport->membase + SE_GENI_TX_FIFOn); + writel(ch, uport->membase + SE_GENI_TX_FIFOn); } static void @@ -405,7 +390,7 @@ __qcom_geni_serial_console_write(struct uart_port *uport, const char *s, bytes_to_send++; } - writel_relaxed(DEF_TX_WM, uport->membase + SE_GENI_TX_WATERMARK_REG); + writel(DEF_TX_WM, uport->membase + SE_GENI_TX_WATERMARK_REG); qcom_geni_serial_setup_tx(uport, bytes_to_send); for (i = 0; i < count; ) { size_t chars_to_write = 0; @@ -423,7 +408,7 @@ __qcom_geni_serial_console_write(struct uart_port *uport, const char *s, chars_to_write = min_t(size_t, count - i, avail / 2); uart_console_write(uport, s + i, chars_to_write, qcom_geni_serial_wr_char); - writel_relaxed(M_TX_FIFO_WATERMARK_EN, uport->membase + + writel(M_TX_FIFO_WATERMARK_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); i += chars_to_write; } @@ -438,6 +423,7 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s, bool locked = true; unsigned long flags; u32 geni_status; + u32 irq_en; WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS); @@ -451,7 +437,7 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s, else spin_lock_irqsave(&uport->lock, flags); - geni_status = readl_relaxed(uport->membase + SE_GENI_STATUS); + geni_status = readl(uport->membase + SE_GENI_STATUS); /* Cancel the current write to log the fault */ if (!locked) { @@ -461,17 +447,22 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s, geni_se_abort_m_cmd(&port->se); qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, M_CMD_ABORT_EN, true); - writel_relaxed(M_CMD_ABORT_EN, uport->membase + + writel(M_CMD_ABORT_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); } - writel_relaxed(M_CMD_CANCEL_EN, uport->membase + - SE_GENI_M_IRQ_CLEAR); + writel(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); } else if ((geni_status & M_GENI_CMD_ACTIVE) && !port->tx_remaining) { /* * It seems we can't interrupt existing transfers if all data * has been sent, in which case we need to look for done first. */ qcom_geni_serial_poll_tx_done(uport); + + if (uart_circ_chars_pending(&uport->state->xmit)) { + irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); + writel(irq_en | M_TX_FIFO_WATERMARK_EN, + uport->membase + SE_GENI_M_IRQ_EN); + } } __qcom_geni_serial_console_write(uport, s, count); @@ -556,29 +547,20 @@ static int handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop) static void qcom_geni_serial_start_tx(struct uart_port *uport) { u32 irq_en; - struct qcom_geni_serial_port *port = to_dev_port(uport, uport); u32 status; - if (port->xfer_mode == GENI_SE_FIFO) { - /* - * readl ensures reading & writing of IRQ_EN register - * is not re-ordered before checking the status of the - * Serial Engine. - */ - status = readl(uport->membase + SE_GENI_STATUS); - if (status & M_GENI_CMD_ACTIVE) - return; + status = readl(uport->membase + SE_GENI_STATUS); + if (status & M_GENI_CMD_ACTIVE) + return; - if (!qcom_geni_serial_tx_empty(uport)) - return; + if (!qcom_geni_serial_tx_empty(uport)) + return; - irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN); - irq_en |= M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN; + irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); + irq_en |= M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN; - writel_relaxed(port->tx_wm, uport->membase + - SE_GENI_TX_WATERMARK_REG); - writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN); - } + writel(DEF_TX_WM, uport->membase + SE_GENI_TX_WATERMARK_REG); + writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN); } static void qcom_geni_serial_stop_tx(struct uart_port *uport) @@ -587,35 +569,24 @@ static void qcom_geni_serial_stop_tx(struct uart_port *uport) u32 status; struct qcom_geni_serial_port *port = to_dev_port(uport, uport); - irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN); - irq_en &= ~M_CMD_DONE_EN; - if (port->xfer_mode == GENI_SE_FIFO) { - irq_en &= ~M_TX_FIFO_WATERMARK_EN; - writel_relaxed(0, uport->membase + - SE_GENI_TX_WATERMARK_REG); - } - writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN); - status = readl_relaxed(uport->membase + SE_GENI_STATUS); + irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); + irq_en &= ~(M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN); + writel(0, uport->membase + SE_GENI_TX_WATERMARK_REG); + writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN); + status = readl(uport->membase + SE_GENI_STATUS); /* Possible stop tx is called multiple times. */ if (!(status & M_GENI_CMD_ACTIVE)) return; - /* - * Ensure cancel command write is not re-ordered before checking - * the status of the Primary Sequencer. - */ - mb(); - geni_se_cancel_m_cmd(&port->se); if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, M_CMD_CANCEL_EN, true)) { geni_se_abort_m_cmd(&port->se); qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, M_CMD_ABORT_EN, true); - writel_relaxed(M_CMD_ABORT_EN, uport->membase + - SE_GENI_M_IRQ_CLEAR); + writel(M_CMD_ABORT_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); } - writel_relaxed(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); + writel(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); } static void qcom_geni_serial_start_rx(struct uart_port *uport) @@ -624,27 +595,19 @@ static void qcom_geni_serial_start_rx(struct uart_port *uport) u32 status; struct qcom_geni_serial_port *port = to_dev_port(uport, uport); - status = readl_relaxed(uport->membase + SE_GENI_STATUS); + status = readl(uport->membase + SE_GENI_STATUS); if (status & S_GENI_CMD_ACTIVE) qcom_geni_serial_stop_rx(uport); - /* - * Ensure setup command write is not re-ordered before checking - * the status of the Secondary Sequencer. - */ - mb(); - geni_se_setup_s_cmd(&port->se, UART_START_READ, 0); - if (port->xfer_mode == GENI_SE_FIFO) { - irq_en = readl_relaxed(uport->membase + SE_GENI_S_IRQ_EN); - irq_en |= S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN; - writel_relaxed(irq_en, uport->membase + SE_GENI_S_IRQ_EN); + irq_en = readl(uport->membase + SE_GENI_S_IRQ_EN); + irq_en |= S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN; + writel(irq_en, uport->membase + SE_GENI_S_IRQ_EN); - irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN); - irq_en |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN; - writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN); - } + irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); + irq_en |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN; + writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN); } static void qcom_geni_serial_stop_rx(struct uart_port *uport) @@ -652,34 +615,35 @@ static void qcom_geni_serial_stop_rx(struct uart_port *uport) u32 irq_en; u32 status; struct qcom_geni_serial_port *port = to_dev_port(uport, uport); - u32 irq_clear = S_CMD_DONE_EN; + u32 s_irq_status; - if (port->xfer_mode == GENI_SE_FIFO) { - irq_en = readl_relaxed(uport->membase + SE_GENI_S_IRQ_EN); - irq_en &= ~(S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN); - writel_relaxed(irq_en, uport->membase + SE_GENI_S_IRQ_EN); + irq_en = readl(uport->membase + SE_GENI_S_IRQ_EN); + irq_en &= ~(S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN); + writel(irq_en, uport->membase + SE_GENI_S_IRQ_EN); - irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN); - irq_en &= ~(M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN); - writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN); - } + irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); + irq_en &= ~(M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN); + writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN); - status = readl_relaxed(uport->membase + SE_GENI_STATUS); + status = readl(uport->membase + SE_GENI_STATUS); /* Possible stop rx is called multiple times. */ if (!(status & S_GENI_CMD_ACTIVE)) return; + geni_se_cancel_s_cmd(&port->se); + qcom_geni_serial_poll_bit(uport, SE_GENI_S_IRQ_STATUS, + S_CMD_CANCEL_EN, true); /* - * Ensure cancel command write is not re-ordered before checking - * the status of the Secondary Sequencer. + * If timeout occurs secondary engine remains active + * and Abort sequence is executed. */ - mb(); + s_irq_status = readl(uport->membase + SE_GENI_S_IRQ_STATUS); + /* Flush the Rx buffer */ + if (s_irq_status & S_RX_FIFO_LAST_EN) + qcom_geni_serial_handle_rx(uport, true); + writel(s_irq_status, uport->membase + SE_GENI_S_IRQ_CLEAR); - geni_se_cancel_s_cmd(&port->se); - qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG, - S_GENI_CMD_CANCEL, false); - status = readl_relaxed(uport->membase + SE_GENI_STATUS); - writel_relaxed(irq_clear, uport->membase + SE_GENI_S_IRQ_CLEAR); + status = readl(uport->membase + SE_GENI_STATUS); if (status & S_GENI_CMD_ACTIVE) qcom_geni_serial_abort_rx(uport); } @@ -693,7 +657,7 @@ static void qcom_geni_serial_handle_rx(struct uart_port *uport, bool drop) u32 total_bytes; struct qcom_geni_serial_port *port = to_dev_port(uport, uport); - status = readl_relaxed(uport->membase + SE_GENI_RX_FIFO_STATUS); + status = readl(uport->membase + SE_GENI_RX_FIFO_STATUS); word_cnt = status & RX_FIFO_WC_MSK; last_word_partial = status & RX_LAST; last_word_byte_cnt = (status & RX_LAST_BYTE_VALID_MSK) >> @@ -719,10 +683,11 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport, bool done, size_t pending; int i; u32 status; + u32 irq_en; unsigned int chunk; int tail; - status = readl_relaxed(uport->membase + SE_GENI_TX_FIFO_STATUS); + status = readl(uport->membase + SE_GENI_TX_FIFO_STATUS); /* Complete the current tx command before taking newly added data */ if (active) @@ -747,6 +712,11 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport, bool done, if (!port->tx_remaining) { qcom_geni_serial_setup_tx(uport, pending); port->tx_remaining = pending; + + irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); + if (!(irq_en & M_TX_FIFO_WATERMARK_EN)) + writel(irq_en | M_TX_FIFO_WATERMARK_EN, + uport->membase + SE_GENI_M_IRQ_EN); } remaining = chunk; @@ -770,7 +740,23 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport, bool done, } xmit->tail = tail & (UART_XMIT_SIZE - 1); + + /* + * The tx fifo watermark is level triggered and latched. Though we had + * cleared it in qcom_geni_serial_isr it will have already reasserted + * so we must clear it again here after our writes. + */ + writel(M_TX_FIFO_WATERMARK_EN, + uport->membase + SE_GENI_M_IRQ_CLEAR); + out_write_wakeup: + if (!port->tx_remaining) { + irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); + if (irq_en & M_TX_FIFO_WATERMARK_EN) + writel(irq_en & ~M_TX_FIFO_WATERMARK_EN, + uport->membase + SE_GENI_M_IRQ_EN); + } + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(uport); } @@ -791,12 +777,12 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) return IRQ_NONE; spin_lock_irqsave(&uport->lock, flags); - m_irq_status = readl_relaxed(uport->membase + SE_GENI_M_IRQ_STATUS); - s_irq_status = readl_relaxed(uport->membase + SE_GENI_S_IRQ_STATUS); - geni_status = readl_relaxed(uport->membase + SE_GENI_STATUS); - m_irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN); - writel_relaxed(m_irq_status, uport->membase + SE_GENI_M_IRQ_CLEAR); - writel_relaxed(s_irq_status, uport->membase + SE_GENI_S_IRQ_CLEAR); + m_irq_status = readl(uport->membase + SE_GENI_M_IRQ_STATUS); + s_irq_status = readl(uport->membase + SE_GENI_S_IRQ_STATUS); + geni_status = readl(uport->membase + SE_GENI_STATUS); + m_irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); + writel(m_irq_status, uport->membase + SE_GENI_M_IRQ_CLEAR); + writel(s_irq_status, uport->membase + SE_GENI_S_IRQ_CLEAR); if (WARN_ON(m_irq_status & M_ILLEGAL_CMD_EN)) goto out_unlock; @@ -806,8 +792,7 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) tty_insert_flip_char(tport, 0, TTY_OVERRUN); } - if (m_irq_status & (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN) && - m_irq_en & (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN)) + if (m_irq_status & m_irq_en & (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN)) qcom_geni_serial_handle_tx(uport, m_irq_status & M_CMD_DONE_EN, geni_status & M_GENI_CMD_ACTIVE); @@ -842,17 +827,6 @@ static void get_tx_fifo_size(struct qcom_geni_serial_port *port) (port->tx_fifo_depth * port->tx_fifo_width) / BITS_PER_BYTE; } -static void set_rfr_wm(struct qcom_geni_serial_port *port) -{ - /* - * Set RFR (Flow off) to FIFO_DEPTH - 2. - * RX WM level at 10% RX_FIFO_DEPTH. - * TX WM level at 10% TX_FIFO_DEPTH. - */ - port->rx_rfr = port->rx_fifo_depth - 2; - port->rx_wm = UART_CONSOLE_RX_WM; - port->tx_wm = DEF_TX_WM; -} static void qcom_geni_serial_shutdown(struct uart_port *uport) { @@ -891,21 +865,19 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport) get_tx_fifo_size(port); - set_rfr_wm(port); - writel_relaxed(rxstale, uport->membase + SE_UART_RX_STALE_CNT); + writel(rxstale, uport->membase + SE_UART_RX_STALE_CNT); /* * Make an unconditional cancel on the main sequencer to reset * it else we could end up in data loss scenarios. */ - port->xfer_mode = GENI_SE_FIFO; if (uart_console(uport)) qcom_geni_serial_poll_tx_done(uport); geni_se_config_packing(&port->se, BITS_PER_BYTE, port->tx_bytes_pw, false, true, false); geni_se_config_packing(&port->se, BITS_PER_BYTE, port->rx_bytes_pw, false, false, true); - geni_se_init(&port->se, port->rx_wm, port->rx_rfr); - geni_se_select_mode(&port->se, port->xfer_mode); + geni_se_init(&port->se, UART_RX_WM, port->rx_fifo_depth - 2); + geni_se_select_mode(&port->se, GENI_SE_FIFO); if (!uart_console(uport)) { port->rx_fifo = devm_kcalloc(uport->dev, port->rx_fifo_depth, sizeof(u32), GFP_KERNEL); @@ -996,10 +968,10 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport, ser_clk_cfg |= clk_div << CLK_DIV_SHFT; /* parity */ - tx_trans_cfg = readl_relaxed(uport->membase + SE_UART_TX_TRANS_CFG); - tx_parity_cfg = readl_relaxed(uport->membase + SE_UART_TX_PARITY_CFG); - rx_trans_cfg = readl_relaxed(uport->membase + SE_UART_RX_TRANS_CFG); - rx_parity_cfg = readl_relaxed(uport->membase + SE_UART_RX_PARITY_CFG); + tx_trans_cfg = readl(uport->membase + SE_UART_TX_TRANS_CFG); + tx_parity_cfg = readl(uport->membase + SE_UART_TX_PARITY_CFG); + rx_trans_cfg = readl(uport->membase + SE_UART_RX_TRANS_CFG); + rx_parity_cfg = readl(uport->membase + SE_UART_RX_PARITY_CFG); if (termios->c_cflag & PARENB) { tx_trans_cfg |= UART_TX_PAR_EN; rx_trans_cfg |= UART_RX_PAR_EN; @@ -1055,17 +1027,17 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport, uart_update_timeout(uport, termios->c_cflag, baud); if (!uart_console(uport)) - writel_relaxed(port->loopback, + writel(port->loopback, uport->membase + SE_UART_LOOPBACK_CFG); - writel_relaxed(tx_trans_cfg, uport->membase + SE_UART_TX_TRANS_CFG); - writel_relaxed(tx_parity_cfg, uport->membase + SE_UART_TX_PARITY_CFG); - writel_relaxed(rx_trans_cfg, uport->membase + SE_UART_RX_TRANS_CFG); - writel_relaxed(rx_parity_cfg, uport->membase + SE_UART_RX_PARITY_CFG); - writel_relaxed(bits_per_char, uport->membase + SE_UART_TX_WORD_LEN); - writel_relaxed(bits_per_char, uport->membase + SE_UART_RX_WORD_LEN); - writel_relaxed(stop_bit_len, uport->membase + SE_UART_TX_STOP_BIT_LEN); - writel_relaxed(ser_clk_cfg, uport->membase + GENI_SER_M_CLK_CFG); - writel_relaxed(ser_clk_cfg, uport->membase + GENI_SER_S_CLK_CFG); + writel(tx_trans_cfg, uport->membase + SE_UART_TX_TRANS_CFG); + writel(tx_parity_cfg, uport->membase + SE_UART_TX_PARITY_CFG); + writel(rx_trans_cfg, uport->membase + SE_UART_RX_TRANS_CFG); + writel(rx_parity_cfg, uport->membase + SE_UART_RX_PARITY_CFG); + writel(bits_per_char, uport->membase + SE_UART_TX_WORD_LEN); + writel(bits_per_char, uport->membase + SE_UART_RX_WORD_LEN); + writel(stop_bit_len, uport->membase + SE_UART_TX_STOP_BIT_LEN); + writel(ser_clk_cfg, uport->membase + GENI_SER_M_CLK_CFG); + writel(ser_clk_cfg, uport->membase + GENI_SER_S_CLK_CFG); out_restart_rx: qcom_geni_serial_start_rx(uport); } @@ -1156,13 +1128,13 @@ static int __init qcom_geni_serial_earlycon_setup(struct earlycon_device *dev, geni_se_init(&se, DEF_FIFO_DEPTH_WORDS / 2, DEF_FIFO_DEPTH_WORDS - 2); geni_se_select_mode(&se, GENI_SE_FIFO); - writel_relaxed(tx_trans_cfg, uport->membase + SE_UART_TX_TRANS_CFG); - writel_relaxed(tx_parity_cfg, uport->membase + SE_UART_TX_PARITY_CFG); - writel_relaxed(rx_trans_cfg, uport->membase + SE_UART_RX_TRANS_CFG); - writel_relaxed(rx_parity_cfg, uport->membase + SE_UART_RX_PARITY_CFG); - writel_relaxed(bits_per_char, uport->membase + SE_UART_TX_WORD_LEN); - writel_relaxed(bits_per_char, uport->membase + SE_UART_RX_WORD_LEN); - writel_relaxed(stop_bit_len, uport->membase + SE_UART_TX_STOP_BIT_LEN); + writel(tx_trans_cfg, uport->membase + SE_UART_TX_TRANS_CFG); + writel(tx_parity_cfg, uport->membase + SE_UART_TX_PARITY_CFG); + writel(rx_trans_cfg, uport->membase + SE_UART_RX_TRANS_CFG); + writel(rx_parity_cfg, uport->membase + SE_UART_RX_PARITY_CFG); + writel(bits_per_char, uport->membase + SE_UART_TX_WORD_LEN); + writel(bits_per_char, uport->membase + SE_UART_RX_WORD_LEN); + writel(stop_bit_len, uport->membase + SE_UART_TX_STOP_BIT_LEN); dev->con->write = qcom_geni_serial_earlycon_write; dev->con->setup = NULL; diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index dc56cddca6dc8db7638252c59dcb33ee2442d8ac..2a5bf4c14fb89c648f2e30334a9e583508f9eb2c 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2786,6 +2786,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) if (uport->cons && uport->dev) of_console_check(uport->dev->of_node, uport->cons->name, uport->line); + tty_port_link_device(port, drv->tty_driver, uport->line); uart_configure_port(drv, state, uport); port->console = uart_console(uport); diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index e8d7a7bb4339e4ef40fc22cb433588c54dbd43c0..bce4ac1787add94c6e0ff7c1605f04c6f0ec9b8f 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -105,9 +105,7 @@ static int stm32_config_rs485(struct uart_port *port, struct stm32_usart_config *cfg = &stm32_port->info->cfg; u32 usartdiv, baud, cr1, cr3; bool over8; - unsigned long flags; - spin_lock_irqsave(&port->lock, flags); stm32_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); port->rs485 = *rs485conf; @@ -147,7 +145,6 @@ static int stm32_config_rs485(struct uart_port *port, } stm32_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); - spin_unlock_irqrestore(&port->lock, flags); return 0; } @@ -194,8 +191,8 @@ static int stm32_pending_rx(struct uart_port *port, u32 *sr, int *last_res, return 0; } -static unsigned long -stm32_get_char(struct uart_port *port, u32 *sr, int *last_res) +static unsigned long stm32_get_char(struct uart_port *port, u32 *sr, + int *last_res) { struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; @@ -205,10 +202,13 @@ stm32_get_char(struct uart_port *port, u32 *sr, int *last_res) c = stm32_port->rx_buf[RX_BUF_L - (*last_res)--]; if ((*last_res) == 0) *last_res = RX_BUF_L; - return c; } else { - return readl_relaxed(port->membase + ofs->rdr); + c = readl_relaxed(port->membase + ofs->rdr); + /* apply RDR data mask */ + c &= stm32_port->rdr_mask; } + + return c; } static void stm32_receive_chars(struct uart_port *port, bool threaded) @@ -225,35 +225,51 @@ static void stm32_receive_chars(struct uart_port *port, bool threaded) while (stm32_pending_rx(port, &sr, &stm32_port->last_res, threaded)) { sr |= USART_SR_DUMMY_RX; - c = stm32_get_char(port, &sr, &stm32_port->last_res); flag = TTY_NORMAL; - port->icount.rx++; + /* + * Status bits has to be cleared before reading the RDR: + * In FIFO mode, reading the RDR will pop the next data + * (if any) along with its status bits into the SR. + * Not doing so leads to misalignement between RDR and SR, + * and clear status bits of the next rx data. + * + * Clear errors flags for stm32f7 and stm32h7 compatible + * devices. On stm32f4 compatible devices, the error bit is + * cleared by the sequence [read SR - read DR]. + */ + if ((sr & USART_SR_ERR_MASK) && ofs->icr != UNDEF_REG) + writel_relaxed(sr & USART_SR_ERR_MASK, + port->membase + ofs->icr); + + c = stm32_get_char(port, &sr, &stm32_port->last_res); + port->icount.rx++; if (sr & USART_SR_ERR_MASK) { - if (sr & USART_SR_LBD) { - port->icount.brk++; - if (uart_handle_break(port)) - continue; - } else if (sr & USART_SR_ORE) { - if (ofs->icr != UNDEF_REG) - writel_relaxed(USART_ICR_ORECF, - port->membase + - ofs->icr); + if (sr & USART_SR_ORE) { port->icount.overrun++; } else if (sr & USART_SR_PE) { port->icount.parity++; } else if (sr & USART_SR_FE) { - port->icount.frame++; + /* Break detection if character is null */ + if (!c) { + port->icount.brk++; + if (uart_handle_break(port)) + continue; + } else { + port->icount.frame++; + } } sr &= port->read_status_mask; - if (sr & USART_SR_LBD) - flag = TTY_BREAK; - else if (sr & USART_SR_PE) + if (sr & USART_SR_PE) { flag = TTY_PARITY; - else if (sr & USART_SR_FE) - flag = TTY_FRAME; + } else if (sr & USART_SR_FE) { + if (!c) + flag = TTY_BREAK; + else + flag = TTY_FRAME; + } } if (uart_handle_sysrq_char(port, c)) @@ -271,21 +287,6 @@ static void stm32_tx_dma_complete(void *arg) struct uart_port *port = arg; struct stm32_port *stm32port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32port->info->ofs; - unsigned int isr; - int ret; - - ret = readl_relaxed_poll_timeout_atomic(port->membase + ofs->isr, - isr, - (isr & USART_SR_TC), - 10, 100000); - - if (ret) - dev_err(port->dev, "terminal count not set\n"); - - if (ofs->icr == UNDEF_REG) - stm32_clr_bits(port, ofs->isr, USART_SR_TC); - else - stm32_set_bits(port, ofs->icr, USART_CR_TC); stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAT); stm32port->tx_dma_busy = false; @@ -377,7 +378,6 @@ static void stm32_transmit_chars_dma(struct uart_port *port) /* Issue pending DMA TX requests */ dma_async_issue_pending(stm32port->tx_ch); - stm32_clr_bits(port, ofs->isr, USART_SR_TC); stm32_set_bits(port, ofs->cr3, USART_CR3_DMAT); xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1); @@ -401,15 +401,15 @@ static void stm32_transmit_chars(struct uart_port *port) return; } - if (uart_tx_stopped(port)) { - stm32_stop_tx(port); + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + stm32_clr_bits(port, ofs->cr1, USART_CR1_TXEIE); return; } - if (uart_circ_empty(xmit)) { - stm32_stop_tx(port); - return; - } + if (ofs->icr == UNDEF_REG) + stm32_clr_bits(port, ofs->isr, USART_SR_TC); + else + writel_relaxed(USART_ICR_TCCF, port->membase + ofs->icr); if (stm32_port->tx_ch) stm32_transmit_chars_dma(port); @@ -420,7 +420,7 @@ static void stm32_transmit_chars(struct uart_port *port) uart_write_wakeup(port); if (uart_circ_empty(xmit)) - stm32_stop_tx(port); + stm32_clr_bits(port, ofs->cr1, USART_CR1_TXEIE); } static irqreturn_t stm32_interrupt(int irq, void *ptr) @@ -554,7 +554,6 @@ static int stm32_startup(struct uart_port *port) { struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; - struct stm32_usart_config *cfg = &stm32_port->info->cfg; const char *name = to_platform_device(port->dev)->name; u32 val; int ret; @@ -565,15 +564,6 @@ static int stm32_startup(struct uart_port *port) if (ret) return ret; - if (cfg->has_wakeup && stm32_port->wakeirq >= 0) { - ret = dev_pm_set_dedicated_wake_irq(port->dev, - stm32_port->wakeirq); - if (ret) { - free_irq(port->irq, port); - return ret; - } - } - val = USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE; if (stm32_port->fifoen) val |= USART_CR1_FIFOEN; @@ -587,18 +577,56 @@ static void stm32_shutdown(struct uart_port *port) struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; struct stm32_usart_config *cfg = &stm32_port->info->cfg; - u32 val; + u32 val, isr; + int ret; val = USART_CR1_TXEIE | USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE; val |= BIT(cfg->uart_enable_bit); if (stm32_port->fifoen) val |= USART_CR1_FIFOEN; + + ret = readl_relaxed_poll_timeout(port->membase + ofs->isr, + isr, (isr & USART_SR_TC), + 10, 100000); + + if (ret) + dev_err(port->dev, "transmission complete not set\n"); + stm32_clr_bits(port, ofs->cr1, val); - dev_pm_clear_wake_irq(port->dev); free_irq(port->irq, port); } +unsigned int stm32_get_databits(struct ktermios *termios) +{ + unsigned int bits; + + tcflag_t cflag = termios->c_cflag; + + switch (cflag & CSIZE) { + /* + * CSIZE settings are not necessarily supported in hardware. + * CSIZE unsupported configurations are handled here to set word length + * to 8 bits word as default configuration and to print debug message. + */ + case CS5: + bits = 5; + break; + case CS6: + bits = 6; + break; + case CS7: + bits = 7; + break; + /* default including CS8 */ + default: + bits = 8; + break; + } + + return bits; +} + static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { @@ -606,7 +634,7 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; struct stm32_usart_config *cfg = &stm32_port->info->cfg; struct serial_rs485 *rs485conf = &port->rs485; - unsigned int baud; + unsigned int baud, bits; u32 usartdiv, mantissa, fraction, oversampling; tcflag_t cflag = termios->c_cflag; u32 cr1, cr2, cr3; @@ -632,16 +660,29 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, if (cflag & CSTOPB) cr2 |= USART_CR2_STOP_2B; + bits = stm32_get_databits(termios); + stm32_port->rdr_mask = (BIT(bits) - 1); + if (cflag & PARENB) { + bits++; cr1 |= USART_CR1_PCE; - if ((cflag & CSIZE) == CS8) { - if (cfg->has_7bits_data) - cr1 |= USART_CR1_M0; - else - cr1 |= USART_CR1_M; - } } + /* + * Word length configuration: + * CS8 + parity, 9 bits word aka [M1:M0] = 0b01 + * CS7 or (CS6 + parity), 7 bits word aka [M1:M0] = 0b10 + * CS8 or (CS7 + parity), 8 bits word aka [M1:M0] = 0b00 + * M0 and M1 already cleared by cr1 initialization. + */ + if (bits == 9) + cr1 |= USART_CR1_M0; + else if ((bits == 7) && cfg->has_7bits_data) + cr1 |= USART_CR1_M1; + else if (bits != 8) + dev_dbg(port->dev, "Unsupported data bits config: %u bits\n" + , bits); + if (cflag & PARODD) cr1 |= USART_CR1_PS; @@ -679,14 +720,14 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, if (termios->c_iflag & INPCK) port->read_status_mask |= USART_SR_PE | USART_SR_FE; if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) - port->read_status_mask |= USART_SR_LBD; + port->read_status_mask |= USART_SR_FE; /* Characters to ignore */ port->ignore_status_mask = 0; if (termios->c_iflag & IGNPAR) port->ignore_status_mask = USART_SR_PE | USART_SR_FE; if (termios->c_iflag & IGNBRK) { - port->ignore_status_mask |= USART_SR_LBD; + port->ignore_status_mask |= USART_SR_FE; /* * If we're ignoring parity and break indicators, * ignore overruns too (for real raw support). @@ -1024,11 +1065,18 @@ static int stm32_serial_probe(struct platform_device *pdev) ret = device_init_wakeup(&pdev->dev, true); if (ret) goto err_uninit; + + ret = dev_pm_set_dedicated_wake_irq(&pdev->dev, + stm32port->wakeirq); + if (ret) + goto err_nowup; + + device_set_wakeup_enable(&pdev->dev, false); } ret = uart_add_one_port(&stm32_usart_driver, &stm32port->port); if (ret) - goto err_nowup; + goto err_wirq; ret = stm32_of_dma_rx_probe(stm32port, pdev); if (ret) @@ -1042,6 +1090,10 @@ static int stm32_serial_probe(struct platform_device *pdev) return 0; +err_wirq: + if (stm32port->info->cfg.has_wakeup && stm32port->wakeirq >= 0) + dev_pm_clear_wake_irq(&pdev->dev); + err_nowup: if (stm32port->info->cfg.has_wakeup && stm32port->wakeirq >= 0) device_init_wakeup(&pdev->dev, false); @@ -1079,8 +1131,10 @@ static int stm32_serial_remove(struct platform_device *pdev) TX_BUF_L, stm32_port->tx_buf, stm32_port->tx_dma_buf); - if (cfg->has_wakeup && stm32_port->wakeirq >= 0) + if (cfg->has_wakeup && stm32_port->wakeirq >= 0) { + dev_pm_clear_wake_irq(&pdev->dev); device_init_wakeup(&pdev->dev, false); + } clk_disable_unprepare(stm32_port->clk); diff --git a/drivers/tty/serial/stm32-usart.h b/drivers/tty/serial/stm32-usart.h index 6f294e280ea301a72be694b53f3dfa1374b397c3..30d2433e27c3c0a75c7f8ee144b2afd5a2e251e7 100644 --- a/drivers/tty/serial/stm32-usart.h +++ b/drivers/tty/serial/stm32-usart.h @@ -108,7 +108,6 @@ struct stm32_usart_info stm32h7_info = { #define USART_SR_RXNE BIT(5) #define USART_SR_TC BIT(6) #define USART_SR_TXE BIT(7) -#define USART_SR_LBD BIT(8) #define USART_SR_CTSIF BIT(9) #define USART_SR_CTS BIT(10) /* F7 */ #define USART_SR_RTOF BIT(11) /* F7 */ @@ -120,8 +119,7 @@ struct stm32_usart_info stm32h7_info = { #define USART_SR_SBKF BIT(18) /* F7 */ #define USART_SR_WUF BIT(20) /* H7 */ #define USART_SR_TEACK BIT(21) /* F7 */ -#define USART_SR_ERR_MASK (USART_SR_LBD | USART_SR_ORE | \ - USART_SR_FE | USART_SR_PE) +#define USART_SR_ERR_MASK (USART_SR_ORE | USART_SR_FE | USART_SR_PE) /* Dummy bits */ #define USART_SR_DUMMY_RX BIT(16) @@ -151,8 +149,7 @@ struct stm32_usart_info stm32h7_info = { #define USART_CR1_PS BIT(9) #define USART_CR1_PCE BIT(10) #define USART_CR1_WAKE BIT(11) -#define USART_CR1_M BIT(12) -#define USART_CR1_M0 BIT(12) /* F7 */ +#define USART_CR1_M0 BIT(12) /* F7 (CR1_M for F4) */ #define USART_CR1_MME BIT(13) /* F7 */ #define USART_CR1_CMIE BIT(14) /* F7 */ #define USART_CR1_OVER8 BIT(15) @@ -169,8 +166,6 @@ struct stm32_usart_info stm32h7_info = { /* USART_CR2 */ #define USART_CR2_ADD_MASK GENMASK(3, 0) /* F4 */ #define USART_CR2_ADDM7 BIT(4) /* F7 */ -#define USART_CR2_LBDL BIT(5) -#define USART_CR2_LBDIE BIT(6) #define USART_CR2_LBCL BIT(8) #define USART_CR2_CPHA BIT(9) #define USART_CR2_CPOL BIT(10) @@ -227,12 +222,10 @@ struct stm32_usart_info stm32h7_info = { /* USART_ICR */ #define USART_ICR_PECF BIT(0) /* F7 */ -#define USART_ICR_FFECF BIT(1) /* F7 */ -#define USART_ICR_NCF BIT(2) /* F7 */ +#define USART_ICR_FECF BIT(1) /* F7 */ #define USART_ICR_ORECF BIT(3) /* F7 */ #define USART_ICR_IDLECF BIT(4) /* F7 */ #define USART_ICR_TCCF BIT(6) /* F7 */ -#define USART_ICR_LBDCF BIT(8) /* F7 */ #define USART_ICR_CTSCF BIT(9) /* F7 */ #define USART_ICR_RTOCF BIT(11) /* F7 */ #define USART_ICR_EOBCF BIT(12) /* F7 */ @@ -261,6 +254,7 @@ struct stm32_port { bool hw_flow_control; bool fifoen; int wakeirq; + int rdr_mask; /* receive data register mask */ }; static struct stm32_port stm32_ports[STM32_MAX_PORTS]; diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 66d49d511885354cdb927501fd275054c4080e22..31950a38f0fb7bb5c4055b2f5fee9c9097854989 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -26,6 +26,7 @@ #include #include #include +#include #define CDNS_UART_TTY_NAME "ttyPS" #define CDNS_UART_NAME "xuartps" @@ -34,6 +35,7 @@ #define CDNS_UART_NR_PORTS 2 #define CDNS_UART_FIFO_SIZE 64 /* FIFO size */ #define CDNS_UART_REGISTER_SPACE 0x1000 +#define TX_TIMEOUT 500000 /* Rx Trigger level */ static int rx_trigger_level = 56; @@ -681,18 +683,21 @@ static void cdns_uart_set_termios(struct uart_port *port, unsigned int cval = 0; unsigned int baud, minbaud, maxbaud; unsigned long flags; - unsigned int ctrl_reg, mode_reg; - - spin_lock_irqsave(&port->lock, flags); + unsigned int ctrl_reg, mode_reg, val; + int err; /* Wait for the transmit FIFO to empty before making changes */ if (!(readl(port->membase + CDNS_UART_CR) & CDNS_UART_CR_TX_DIS)) { - while (!(readl(port->membase + CDNS_UART_SR) & - CDNS_UART_SR_TXEMPTY)) { - cpu_relax(); + err = readl_poll_timeout(port->membase + CDNS_UART_SR, + val, (val & CDNS_UART_SR_TXEMPTY), + 1000, TX_TIMEOUT); + if (err) { + dev_err(port->dev, "timed out waiting for tx empty"); + return; } } + spin_lock_irqsave(&port->lock, flags); /* Disable the TX and RX to set baud rate */ ctrl_reg = readl(port->membase + CDNS_UART_CR); diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c index b88ecf102764e43e532a461f148ad97c282bea78..e9779b03ee565f29472dbac7711e348d568f494f 100644 --- a/drivers/tty/synclink_gt.c +++ b/drivers/tty/synclink_gt.c @@ -1335,10 +1335,10 @@ static void throttle(struct tty_struct * tty) DBGINFO(("%s throttle\n", info->device_name)); if (I_IXOFF(tty)) send_xchar(tty, STOP_CHAR(tty)); - if (C_CRTSCTS(tty)) { + if (C_CRTSCTS(tty)) { spin_lock_irqsave(&info->lock,flags); info->signals &= ~SerialSignal_RTS; - set_signals(info); + set_signals(info); spin_unlock_irqrestore(&info->lock,flags); } } @@ -1360,10 +1360,10 @@ static void unthrottle(struct tty_struct * tty) else send_xchar(tty, START_CHAR(tty)); } - if (C_CRTSCTS(tty)) { + if (C_CRTSCTS(tty)) { spin_lock_irqsave(&info->lock,flags); info->signals |= SerialSignal_RTS; - set_signals(info); + set_signals(info); spin_unlock_irqrestore(&info->lock,flags); } } @@ -2561,8 +2561,8 @@ static void change_params(struct slgt_info *info) info->read_status_mask = IRQ_RXOVER; if (I_INPCK(info->port.tty)) info->read_status_mask |= MASK_PARITY | MASK_FRAMING; - if (I_BRKINT(info->port.tty) || I_PARMRK(info->port.tty)) - info->read_status_mask |= MASK_BREAK; + if (I_BRKINT(info->port.tty) || I_PARMRK(info->port.tty)) + info->read_status_mask |= MASK_BREAK; if (I_IGNPAR(info->port.tty)) info->ignore_status_mask |= MASK_PARITY | MASK_FRAMING; if (I_IGNBRK(info->port.tty)) { @@ -3193,7 +3193,7 @@ static int tiocmset(struct tty_struct *tty, info->signals &= ~SerialSignal_DTR; spin_lock_irqsave(&info->lock,flags); - set_signals(info); + set_signals(info); spin_unlock_irqrestore(&info->lock,flags); return 0; } @@ -3204,7 +3204,7 @@ static int carrier_raised(struct tty_port *port) struct slgt_info *info = container_of(port, struct slgt_info, port); spin_lock_irqsave(&info->lock,flags); - get_signals(info); + get_signals(info); spin_unlock_irqrestore(&info->lock,flags); return (info->signals & SerialSignal_DCD) ? 1 : 0; } @@ -3219,7 +3219,7 @@ static void dtr_rts(struct tty_port *port, int on) info->signals |= SerialSignal_RTS | SerialSignal_DTR; else info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR); - set_signals(info); + set_signals(info); spin_unlock_irqrestore(&info->lock,flags); } diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c index 1e4d5b9c981a45668f25e31c910d5b43952af7ec..57c2c647af61e4cdd6be513bbfa17ca6a9763b80 100644 --- a/drivers/tty/synclinkmp.c +++ b/drivers/tty/synclinkmp.c @@ -1454,10 +1454,10 @@ static void throttle(struct tty_struct * tty) if (I_IXOFF(tty)) send_xchar(tty, STOP_CHAR(tty)); - if (C_CRTSCTS(tty)) { + if (C_CRTSCTS(tty)) { spin_lock_irqsave(&info->lock,flags); info->serial_signals &= ~SerialSignal_RTS; - set_signals(info); + set_signals(info); spin_unlock_irqrestore(&info->lock,flags); } } @@ -1483,10 +1483,10 @@ static void unthrottle(struct tty_struct * tty) send_xchar(tty, START_CHAR(tty)); } - if (C_CRTSCTS(tty)) { + if (C_CRTSCTS(tty)) { spin_lock_irqsave(&info->lock,flags); info->serial_signals |= SerialSignal_RTS; - set_signals(info); + set_signals(info); spin_unlock_irqrestore(&info->lock,flags); } } @@ -2471,7 +2471,7 @@ static void isr_io_pin( SLMP_INFO *info, u16 status ) if (status & SerialSignal_CTS) { if ( debug_level >= DEBUG_LEVEL_ISR ) printk("CTS tx start..."); - info->port.tty->hw_stopped = 0; + info->port.tty->hw_stopped = 0; tx_start(info); info->pending_bh |= BH_TRANSMIT; return; @@ -2480,7 +2480,7 @@ static void isr_io_pin( SLMP_INFO *info, u16 status ) if (!(status & SerialSignal_CTS)) { if ( debug_level >= DEBUG_LEVEL_ISR ) printk("CTS tx stop..."); - info->port.tty->hw_stopped = 1; + info->port.tty->hw_stopped = 1; tx_stop(info); } } @@ -2807,8 +2807,8 @@ static void change_params(SLMP_INFO *info) info->read_status_mask2 = OVRN; if (I_INPCK(info->port.tty)) info->read_status_mask2 |= PE | FRME; - if (I_BRKINT(info->port.tty) || I_PARMRK(info->port.tty)) - info->read_status_mask1 |= BRKD; + if (I_BRKINT(info->port.tty) || I_PARMRK(info->port.tty)) + info->read_status_mask1 |= BRKD; if (I_IGNPAR(info->port.tty)) info->ignore_status_mask2 |= PE | FRME; if (I_IGNBRK(info->port.tty)) { @@ -3178,7 +3178,7 @@ static int tiocmget(struct tty_struct *tty) unsigned long flags; spin_lock_irqsave(&info->lock,flags); - get_signals(info); + get_signals(info); spin_unlock_irqrestore(&info->lock,flags); result = ((info->serial_signals & SerialSignal_RTS) ? TIOCM_RTS : 0) | @@ -3216,7 +3216,7 @@ static int tiocmset(struct tty_struct *tty, info->serial_signals &= ~SerialSignal_DTR; spin_lock_irqsave(&info->lock,flags); - set_signals(info); + set_signals(info); spin_unlock_irqrestore(&info->lock,flags); return 0; @@ -3228,7 +3228,7 @@ static int carrier_raised(struct tty_port *port) unsigned long flags; spin_lock_irqsave(&info->lock,flags); - get_signals(info); + get_signals(info); spin_unlock_irqrestore(&info->lock,flags); return (info->serial_signals & SerialSignal_DCD) ? 1 : 0; @@ -3244,7 +3244,7 @@ static void dtr_rts(struct tty_port *port, int on) info->serial_signals |= SerialSignal_RTS | SerialSignal_DTR; else info->serial_signals &= ~(SerialSignal_RTS | SerialSignal_DTR); - set_signals(info); + set_signals(info); spin_unlock_irqrestore(&info->lock,flags); } diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index 06ed20dd01ba6f9cc443c690f04c131f09790cba..cee0274806c551f9971abc250c226606e8df59c2 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -546,7 +546,6 @@ void __handle_sysrq(int key, bool check_mask) */ orig_log_level = console_loglevel; console_loglevel = CONSOLE_LOGLEVEL_DEFAULT; - pr_info("SysRq : "); op_p = __sysrq_get_key_op(key); if (op_p) { @@ -555,14 +554,15 @@ void __handle_sysrq(int key, bool check_mask) * should not) and is the invoked operation enabled? */ if (!check_mask || sysrq_on_mask(op_p->enable_mask)) { - pr_cont("%s\n", op_p->action_msg); + pr_info("%s\n", op_p->action_msg); console_loglevel = orig_log_level; op_p->handler(key); } else { - pr_cont("This sysrq operation is disabled.\n"); + pr_info("This sysrq operation is disabled.\n"); + console_loglevel = orig_log_level; } } else { - pr_cont("HELP : "); + pr_info("HELP : "); /* Only print the help msg once per handler */ for (i = 0; i < ARRAY_SIZE(sysrq_key_table); i++) { if (sysrq_key_table[i]) { diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index c699d41a2a48f5ce8acffab4251e35bbd54c62c1..fbacb00c2601b7df256d3ddc92d51e367e524a4b 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -52,10 +52,11 @@ static void tty_port_default_wakeup(struct tty_port *port) } } -static const struct tty_port_client_operations default_client_ops = { +const struct tty_port_client_operations tty_port_default_client_ops = { .receive_buf = tty_port_default_receive_buf, .write_wakeup = tty_port_default_wakeup, }; +EXPORT_SYMBOL_GPL(tty_port_default_client_ops); void tty_port_init(struct tty_port *port) { @@ -68,7 +69,7 @@ void tty_port_init(struct tty_port *port) spin_lock_init(&port->lock); port->close_delay = (50 * HZ) / 100; port->closing_wait = (3000 * HZ) / 100; - port->client_ops = &default_client_ops; + port->client_ops = &tty_port_default_client_ops; kref_init(&port->kref); } EXPORT_SYMBOL(tty_port_init); diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c index 07496c711d7dcc1dde58fb1b3db7a29384ef336d..8a16717248356d95a601e198a5b6733a4aba9876 100644 --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -27,6 +28,8 @@ #include #include +#include + /* Don't take this from : 011-015 on the screen aren't spaces */ #define isspace(c) ((c) == ' ') @@ -41,6 +44,7 @@ static volatile int sel_start = -1; /* cleared by clear_selection */ static int sel_end; static int sel_buffer_lth; static char *sel_buffer; +static DEFINE_MUTEX(sel_lock); /* clear_selection, highlight and highlight_pointer can be called from interrupt (via scrollback/front) */ @@ -163,7 +167,7 @@ static int store_utf8(u32 c, char *p) * The entire selection process is managed under the console_lock. It's * a lot under the lock but its hardly a performance path */ -int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *tty) +static int __set_selection(const struct tiocl_selection __user *sel, struct tty_struct *tty) { struct vc_data *vc = vc_cons[fg_console].d; int new_sel_start, new_sel_end, spc; @@ -171,7 +175,7 @@ int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *t char *bp, *obp; int i, ps, pe, multiplier; u32 c; - int mode; + int mode, ret = 0; poke_blanked_console(); if (copy_from_user(&v, sel, sizeof(*sel))) @@ -320,7 +324,21 @@ int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *t } } sel_buffer_lth = bp - sel_buffer; - return 0; + + return ret; +} + +int set_selection(const struct tiocl_selection __user *v, struct tty_struct *tty) +{ + int ret; + + mutex_lock(&sel_lock); + console_lock(); + ret = __set_selection(v, tty); + console_unlock(); + mutex_unlock(&sel_lock); + + return ret; } /* Insert the contents of the selection buffer into the @@ -337,6 +355,7 @@ int paste_selection(struct tty_struct *tty) unsigned int count; struct tty_ldisc *ld; DECLARE_WAITQUEUE(wait, current); + int ret = 0; console_lock(); poke_blanked_console(); @@ -348,10 +367,17 @@ int paste_selection(struct tty_struct *tty) tty_buffer_lock_exclusive(&vc->port); add_wait_queue(&vc->paste_wait, &wait); + mutex_lock(&sel_lock); while (sel_buffer && sel_buffer_lth > pasted) { set_current_state(TASK_INTERRUPTIBLE); + if (signal_pending(current)) { + ret = -EINTR; + break; + } if (tty_throttled(tty)) { + mutex_unlock(&sel_lock); schedule(); + mutex_lock(&sel_lock); continue; } __set_current_state(TASK_RUNNING); @@ -360,10 +386,11 @@ int paste_selection(struct tty_struct *tty) count); pasted += count; } + mutex_unlock(&sel_lock); remove_wait_queue(&vc->paste_wait, &wait); __set_current_state(TASK_RUNNING); tty_buffer_unlock_exclusive(&vc->port); tty_ldisc_deref(ld); - return 0; + return ret; } diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index d673e3592662c9225bec6d847a0204cdd58b28d6..5cecf529562a3eacc6f30e5df886b20773233ba0 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -936,10 +936,21 @@ static void flush_scrollback(struct vc_data *vc) WARN_CONSOLE_UNLOCKED(); set_origin(vc); - if (vc->vc_sw->con_flush_scrollback) + if (vc->vc_sw->con_flush_scrollback) { vc->vc_sw->con_flush_scrollback(vc); - else + } else if (con_is_visible(vc)) { + /* + * When no con_flush_scrollback method is provided then the + * legacy way for flushing the scrollback buffer is to use + * a side effect of the con_switch method. We do it only on + * the foreground console as background consoles have no + * scrollback buffers in that case and we obviously don't + * want to switch to them. + */ + hide_cursor(vc); vc->vc_sw->con_switch(vc); + set_cursor(vc); + } } /* @@ -3011,9 +3022,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg) switch (type) { case TIOCL_SETSEL: - console_lock(); ret = set_selection((struct tiocl_selection __user *)(p+1), tty); - console_unlock(); break; case TIOCL_PASTESEL: ret = paste_selection(tty); diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index 73cdc0d633dd62c12e6ed1ca6545846b8dabaa09..2bb6de89b0298ff0d9d9258038aaeddc97b0bb17 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -876,15 +876,20 @@ int vt_ioctl(struct tty_struct *tty, return -EINVAL; for (i = 0; i < MAX_NR_CONSOLES; i++) { + struct vc_data *vcp; + if (!vc_cons[i].d) continue; console_lock(); - if (v.v_vlin) - vc_cons[i].d->vc_scan_lines = v.v_vlin; - if (v.v_clin) - vc_cons[i].d->vc_font.height = v.v_clin; - vc_cons[i].d->vc_resize_user = 1; - vc_resize(vc_cons[i].d, v.v_cols, v.v_rows); + vcp = vc_cons[i].d; + if (vcp) { + if (v.v_vlin) + vcp->vc_scan_lines = v.v_vlin; + if (v.v_clin) + vcp->vc_font.height = v.v_clin; + vcp->vc_resize_user = 1; + vc_resize(vcp, v.v_cols, v.v_rows); + } console_unlock(); } break; diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index 2762148c169df206fae40da2230435f515b7d282..9c788748bdc65543ea449d1b6a18fb7f05a33994 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -938,9 +938,12 @@ int __uio_register_device(struct module *owner, atomic_set(&idev->event, 0); ret = uio_get_minor(idev); - if (ret) + if (ret) { + kfree(idev); return ret; + } + device_initialize(&idev->dev); idev->dev.devt = MKDEV(uio_major, idev->minor); idev->dev.class = &uio_class; idev->dev.parent = parent; @@ -951,7 +954,7 @@ int __uio_register_device(struct module *owner, if (ret) goto err_device_create; - ret = device_register(&idev->dev); + ret = device_add(&idev->dev); if (ret) goto err_device_create; @@ -983,9 +986,10 @@ int __uio_register_device(struct module *owner, err_request_irq: uio_dev_del_attributes(idev); err_uio_dev_add_attributes: - device_unregister(&idev->dev); + device_del(&idev->dev); err_device_create: uio_free_minor(idev); + put_device(&idev->dev); return ret; } EXPORT_SYMBOL_GPL(__uio_register_device); diff --git a/drivers/uio/uio_dmem_genirq.c b/drivers/uio/uio_dmem_genirq.c index e1134a4d97f3fd0005e30dc29e9acf9da77e0e46..a00b4aee6c799cd12b8273db0101990c6437033a 100644 --- a/drivers/uio/uio_dmem_genirq.c +++ b/drivers/uio/uio_dmem_genirq.c @@ -135,11 +135,13 @@ static int uio_dmem_genirq_irqcontrol(struct uio_info *dev_info, s32 irq_on) if (irq_on) { if (test_and_clear_bit(0, &priv->flags)) enable_irq(dev_info->irq); + spin_unlock_irqrestore(&priv->lock, flags); } else { - if (!test_and_set_bit(0, &priv->flags)) + if (!test_and_set_bit(0, &priv->flags)) { + spin_unlock_irqrestore(&priv->lock, flags); disable_irq(dev_info->irq); + } } - spin_unlock_irqrestore(&priv->lock, flags); return 0; } diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index 4638d9b066bea7ad2b35f6c966ee7acec428facf..71b26d0d19fd27f4e57ed7e4af8c1ec708b8d77c 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -25,6 +25,7 @@ static int (*orig_bus_suspend)(struct usb_hcd *hcd); struct ehci_ci_priv { struct regulator *reg_vbus; + bool enabled; }; static int ehci_ci_portpower(struct usb_hcd *hcd, int portnum, bool enable) @@ -36,7 +37,7 @@ static int ehci_ci_portpower(struct usb_hcd *hcd, int portnum, bool enable) int ret = 0; int port = HCS_N_PORTS(ehci->hcs_params); - if (priv->reg_vbus) { + if (priv->reg_vbus && enable != priv->enabled) { if (port > 1) { dev_warn(dev, "Not support multi-port regulator control\n"); @@ -52,6 +53,7 @@ static int ehci_ci_portpower(struct usb_hcd *hcd, int portnum, bool enable) enable ? "enable" : "disable", ret); return ret; } + priv->enabled = enable; } if (enable && (ci->platdata->phy_mode == USBPHY_INTERFACE_MODE_HSIC)) { diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index b8a1fdefb515039f2f9dd2f63616900eff5d34c6..4929c588306887d3cbb0b07e93e405c522172544 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -1107,7 +1107,7 @@ static int wdm_post_reset(struct usb_interface *intf) rv = recover_from_urb_loss(desc); mutex_unlock(&desc->wlock); mutex_unlock(&desc->rlock); - return 0; + return rv; } static struct usb_driver wdm_driver = { diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 3d2eb459d515a10e2168dd5209a72bcdc928edc9..4ed767e8eaf7065aa6dd130db853c8a2eea5c8fd 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -256,6 +256,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, struct usb_host_interface *ifp, int num_ep, unsigned char *buffer, int size) { + struct usb_device *udev = to_usb_device(ddev); unsigned char *buffer0 = buffer; struct usb_endpoint_descriptor *d; struct usb_host_endpoint *endpoint; @@ -297,6 +298,16 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, goto skip_to_next_endpoint_or_interface_descriptor; } + /* Ignore blacklisted endpoints */ + if (udev->quirks & USB_QUIRK_ENDPOINT_BLACKLIST) { + if (usb_endpoint_is_blacklisted(udev, ifp, d)) { + dev_warn(ddev, "config %d interface %d altsetting %d has a blacklisted endpoint with address 0x%X, skipping\n", + cfgno, inum, asnum, + d->bEndpointAddress); + goto skip_to_next_endpoint_or_interface_descriptor; + } + } + endpoint = &ifp->endpoint[ifp->desc.bNumEndpoints]; ++ifp->desc.bNumEndpoints; @@ -392,12 +403,16 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, endpoint->desc.wMaxPacketSize = cpu_to_le16(8); } - /* Validate the wMaxPacketSize field */ + /* + * Validate the wMaxPacketSize field. + * Some devices have isochronous endpoints in altsetting 0; + * the USB-2 spec requires such endpoints to have wMaxPacketSize = 0 + * (see the end of section 5.6.3), so don't warn about them. + */ maxp = usb_endpoint_maxp(&endpoint->desc); - if (maxp == 0) { - dev_warn(ddev, "config %d interface %d altsetting %d endpoint 0x%X has wMaxPacketSize 0, skipping\n", + if (maxp == 0 && !(usb_endpoint_xfer_isoc(d) && asnum == 0)) { + dev_warn(ddev, "config %d interface %d altsetting %d endpoint 0x%X has invalid wMaxPacketSize 0\n", cfgno, inum, asnum, d->bEndpointAddress); - goto skip_to_next_endpoint_or_interface_descriptor; } /* Find the highest legal maxpacket size for this endpoint */ diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 5c6f1c49a26838baf2ea40cae0ba10a71146bfe4..ed408f40017feba694d9e911c17689a5f92977ab 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -36,7 +37,9 @@ #include "otg_whitelist.h" #define USB_VENDOR_GENESYS_LOGIC 0x05e3 +#define USB_VENDOR_SMSC 0x0424 #define HUB_QUIRK_CHECK_PORT_AUTOSUSPEND 0x01 +#define HUB_QUIRK_DISABLE_AUTOSUSPEND 0x02 #define USB_TP_TRANSMISSION_DELAY 40 /* ns */ #define USB_TP_TRANSMISSION_DELAY_MAX 65535 /* ns */ @@ -970,13 +973,17 @@ int usb_remove_device(struct usb_device *udev) { struct usb_hub *hub; struct usb_interface *intf; + int ret; if (!udev->parent) /* Can't remove a root hub */ return -EINVAL; hub = usb_hub_to_struct_hub(udev->parent); intf = to_usb_interface(hub->intfdev); - usb_autopm_get_interface(intf); + ret = usb_autopm_get_interface(intf); + if (ret < 0) + return ret; + set_bit(udev->portnum, hub->removed_bits); hub_port_logical_disconnect(hub, udev->portnum); usb_autopm_put_interface(intf); @@ -1176,6 +1183,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) * PORT_OVER_CURRENT is not. So check for any of them. */ if (udev || (portstatus & USB_PORT_STAT_CONNECTION) || + (portchange & USB_PORT_STAT_C_CONNECTION) || (portstatus & USB_PORT_STAT_OVERCURRENT) || (portchange & USB_PORT_STAT_C_OVERCURRENT)) set_bit(port1, hub->change_bits); @@ -1200,11 +1208,6 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) #ifdef CONFIG_PM udev->reset_resume = 1; #endif - /* Don't set the change_bits when the device - * was powered off. - */ - if (test_bit(port1, hub->power_bits)) - set_bit(port1, hub->change_bits); } else { /* The power session is gone; tell hub_wq */ @@ -1710,6 +1713,10 @@ static void hub_disconnect(struct usb_interface *intf) kfree(hub->buffer); pm_suspend_ignore_children(&intf->dev, false); + + if (hub->quirk_disable_autosuspend) + usb_autopm_put_interface(intf); + kref_put(&hub->kref, hub_release); } @@ -1840,6 +1847,11 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id) if (id->driver_info & HUB_QUIRK_CHECK_PORT_AUTOSUSPEND) hub->quirk_check_port_auto_suspend = 1; + if (id->driver_info & HUB_QUIRK_DISABLE_AUTOSUSPEND) { + hub->quirk_disable_autosuspend = 1; + usb_autopm_get_interface_no_resume(intf); + } + if (hub_configure(hub, &desc->endpoint[0].desc) >= 0) return 0; @@ -5314,6 +5326,8 @@ static void hub_event(struct work_struct *work) hub_dev = hub->intfdev; intf = to_usb_interface(hub_dev); + kcov_remote_start_usb((u64)hdev->bus->busnum); + dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n", hdev->state, hdev->maxchild, /* NOTE: expects max 15 ports... */ @@ -5420,9 +5434,15 @@ static void hub_event(struct work_struct *work) /* Balance the stuff in kick_hub_wq() and allow autosuspend */ usb_autopm_put_interface(intf); kref_put(&hub->kref, hub_release); + + kcov_remote_stop(); } static const struct usb_device_id hub_id_table[] = { + { .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_CLASS, + .idVendor = USB_VENDOR_SMSC, + .bInterfaceClass = USB_CLASS_HUB, + .driver_info = HUB_QUIRK_DISABLE_AUTOSUSPEND}, { .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_CLASS, .idVendor = USB_VENDOR_GENESYS_LOGIC, diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index 4accfb63f7dcbddf77c407bb46d84be2d33a3118..d0bbbd76ba8e9ac0043f609dccb942910b89b788 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -61,6 +61,7 @@ struct usb_hub { unsigned quiescing:1; unsigned disconnected:1; unsigned in_reset:1; + unsigned quirk_disable_autosuspend:1; unsigned quirk_check_port_auto_suspend:1; diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index 4a21431953953d5b52b467a107a04c5f8198cf19..1fe83b522d5fa85effc16445a256e29a3c0b2418 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -203,7 +203,10 @@ static int usb_port_runtime_resume(struct device *dev) if (!port_dev->is_superspeed && peer) pm_runtime_get_sync(&peer->dev); - usb_autopm_get_interface(intf); + retval = usb_autopm_get_interface(intf); + if (retval < 0) + return retval; + retval = usb_hub_set_port_power(hdev, hub, port1, true); msleep(hub_power_on_good_delay(hub)); if (udev && !retval) { @@ -256,7 +259,10 @@ static int usb_port_runtime_suspend(struct device *dev) if (usb_port_block_power_off) return -EBUSY; - usb_autopm_get_interface(intf); + retval = usb_autopm_get_interface(intf); + if (retval < 0) + return retval; + retval = usb_hub_set_port_power(hdev, hub, port1, false); usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION); if (!port_dev->is_superspeed) diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 6b6413073584339ba9e271e17ffa63fc4f883aa4..2dac3e7cdd97205d47883606a35a74d57711e26a 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -231,6 +231,9 @@ static const struct usb_device_id usb_quirk_list[] = { /* Logitech PTZ Pro Camera */ { USB_DEVICE(0x046d, 0x0853), .driver_info = USB_QUIRK_DELAY_INIT }, + /* Logitech Screen Share */ + { USB_DEVICE(0x046d, 0x086c), .driver_info = USB_QUIRK_NO_LPM }, + /* Logitech Quickcam Fusion */ { USB_DEVICE(0x046d, 0x08c1), .driver_info = USB_QUIRK_RESET_RESUME }, @@ -354,6 +357,10 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x0904, 0x6103), .driver_info = USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL }, + /* Sound Devices USBPre2 */ + { USB_DEVICE(0x0926, 0x0202), .driver_info = + USB_QUIRK_ENDPOINT_BLACKLIST }, + /* Keytouch QWERTY Panel keyboard */ { USB_DEVICE(0x0926, 0x3333), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS }, @@ -445,6 +452,9 @@ static const struct usb_device_id usb_quirk_list[] = { /* INTEL VALUE SSD */ { USB_DEVICE(0x8086, 0xf1a5), .driver_info = USB_QUIRK_RESET_RESUME }, + /* novation SoundControl XL */ + { USB_DEVICE(0x1235, 0x0061), .driver_info = USB_QUIRK_RESET_RESUME }, + { } /* terminating entry must be last */ }; @@ -472,6 +482,39 @@ static const struct usb_device_id usb_amd_resume_quirk_list[] = { { } /* terminating entry must be last */ }; +/* + * Entries for blacklisted endpoints that should be ignored when parsing + * configuration descriptors. + * + * Matched for devices with USB_QUIRK_ENDPOINT_BLACKLIST. + */ +static const struct usb_device_id usb_endpoint_blacklist[] = { + { USB_DEVICE_INTERFACE_NUMBER(0x0926, 0x0202, 1), .driver_info = 0x85 }, + { } +}; + +bool usb_endpoint_is_blacklisted(struct usb_device *udev, + struct usb_host_interface *intf, + struct usb_endpoint_descriptor *epd) +{ + const struct usb_device_id *id; + unsigned int address; + + for (id = usb_endpoint_blacklist; id->match_flags; ++id) { + if (!usb_match_device(udev, id)) + continue; + + if (!usb_match_one_id_intf(udev, intf, id)) + continue; + + address = id->driver_info; + if (address == epd->bEndpointAddress) + return true; + } + + return false; +} + static bool usb_match_any_interface(struct usb_device *udev, const struct usb_device_id *id) { diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 8788c425368cc39da17419ae9365a5452b060ea6..dff5b36f320ded14f4f688e0e03d639ac9a4a20b 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -37,6 +37,9 @@ extern void usb_authorize_interface(struct usb_interface *); extern void usb_detect_quirks(struct usb_device *udev); extern void usb_detect_interface_quirks(struct usb_device *udev); extern void usb_release_quirk_list(void); +extern bool usb_endpoint_is_blacklisted(struct usb_device *udev, + struct usb_host_interface *intf, + struct usb_endpoint_descriptor *epd); extern int usb_remove_device(struct usb_device *udev); extern int usb_get_device_descriptor(struct usb_device *dev, diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 3f68edde0f03a613fee93cbbcba9c15c3ab7261b..d8424834902db52c4cee4cac1053f03ffa99fdb3 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -1004,11 +1004,6 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg, else packets = 1; /* send one packet if length is zero. */ - if (hs_ep->isochronous && length > (hs_ep->mc * hs_ep->ep.maxpacket)) { - dev_err(hsotg->dev, "req length > maxpacket*mc\n"); - return; - } - if (dir_in && index != 0) if (hs_ep->isochronous) epsize = DXEPTSIZ_MC(packets); @@ -1312,6 +1307,13 @@ static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req, req->actual = 0; req->status = -EINPROGRESS; + /* Don't queue ISOC request if length greater than mps*mc */ + if (hs_ep->isochronous && + req->length > (hs_ep->mc * hs_ep->ep.maxpacket)) { + dev_err(hs->dev, "req length > maxpacket*mc\n"); + return -EINVAL; + } + /* In DDMA mode for ISOC's don't queue request if length greater * than descriptor limits. */ @@ -1542,6 +1544,7 @@ static int dwc2_hsotg_process_req_status(struct dwc2_hsotg *hsotg, struct dwc2_hsotg_ep *ep0 = hsotg->eps_out[0]; struct dwc2_hsotg_ep *ep; __le16 reply; + u16 status; int ret; dev_dbg(hsotg->dev, "%s: USB_REQ_GET_STATUS\n", __func__); @@ -1553,11 +1556,10 @@ static int dwc2_hsotg_process_req_status(struct dwc2_hsotg *hsotg, switch (ctrl->bRequestType & USB_RECIP_MASK) { case USB_RECIP_DEVICE: - /* - * bit 0 => self powered - * bit 1 => remote wakeup - */ - reply = cpu_to_le16(0); + status = 1 << USB_DEVICE_SELF_POWERED; + status |= hsotg->remote_wakeup_allowed << + USB_DEVICE_REMOTE_WAKEUP; + reply = cpu_to_le16(status); break; case USB_RECIP_INTERFACE: @@ -1668,7 +1670,10 @@ static int dwc2_hsotg_process_req_feature(struct dwc2_hsotg *hsotg, case USB_RECIP_DEVICE: switch (wValue) { case USB_DEVICE_REMOTE_WAKEUP: - hsotg->remote_wakeup_allowed = 1; + if (set) + hsotg->remote_wakeup_allowed = 1; + else + hsotg->remote_wakeup_allowed = 0; break; case USB_DEVICE_TEST_MODE: @@ -1678,16 +1683,17 @@ static int dwc2_hsotg_process_req_feature(struct dwc2_hsotg *hsotg, return -EINVAL; hsotg->test_mode = wIndex >> 8; - ret = dwc2_hsotg_send_reply(hsotg, ep0, NULL, 0); - if (ret) { - dev_err(hsotg->dev, - "%s: failed to send reply\n", __func__); - return ret; - } break; default: return -ENOENT; } + + ret = dwc2_hsotg_send_reply(hsotg, ep0, NULL, 0); + if (ret) { + dev_err(hsotg->dev, + "%s: failed to send reply\n", __func__); + return ret; + } break; case USB_RECIP_ENDPOINT: @@ -2230,6 +2236,7 @@ static unsigned int dwc2_gadget_get_xfersize_ddma(struct dwc2_hsotg_ep *hs_ep) if (status & DEV_DMA_STS_MASK) dev_err(hsotg->dev, "descriptor %d closed with %x\n", i, status & DEV_DMA_STS_MASK); + desc++; } return bytes_rem; @@ -3917,11 +3924,12 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep, * a unique tx-fifo even if it is non-periodic. */ if (dir_in && hsotg->dedicated_fifos) { + unsigned fifo_count = dwc2_hsotg_tx_fifo_count(hsotg); u32 fifo_index = 0; u32 fifo_size = UINT_MAX; size = hs_ep->ep.maxpacket * hs_ep->mc; - for (i = 1; i < hsotg->num_of_eps; ++i) { + for (i = 1; i <= fifo_count; ++i) { if (hsotg->fifo_map & (1 << i)) continue; val = dwc2_readl(hsotg, DPTXFSIZN(i)); diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 8097f29c83458687a8d983797715a714424276d5..4ddec102aaad01e85684a3a8241e90b639d14158 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -52,7 +52,8 @@ comment "Platform Glue Driver Support" config USB_DWC3_OMAP tristate "Texas Instruments OMAP5 and similar Platforms" - depends on EXTCON && (ARCH_OMAP2PLUS || COMPILE_TEST) + depends on ARCH_OMAP2PLUS || COMPILE_TEST + depends on EXTCON || !EXTCON depends on OF default USB_DWC3 help @@ -114,6 +115,7 @@ config USB_DWC3_ST config USB_DWC3_QCOM tristate "Qualcomm Platform" depends on ARCH_QCOM || COMPILE_TEST + depends on EXTCON || !EXTCON depends on OF default USB_DWC3 help diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 818a358b4dcff3bf0de2bc72c3b04e70b5bd8f47..d36e44a467eb72d3a64434301e7dd8c9188b5bef 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1197,6 +1197,9 @@ static void __maybe_unused dwc3_core_exit_mode(struct dwc3 *dwc) /* do nothing */ break; } + + /* de-assert DRVVBUS for HOST and OTG mode */ + dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE); } static void (*notify_event)(struct dwc3 *, unsigned int, unsigned int); diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index 3a5c4175062cac40d455608d2c869b42d52fe4be..ea90c043153d85cf3cb42050dd0f9d3d0580d374 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -1569,6 +1569,11 @@ static int dwc3_msm_gsi_ep_op(struct usb_ep *ep, ret = gsi_get_xfer_index(ep); break; case GSI_EP_OP_STORE_DBL_INFO: + if (!dwc->pullups_connected) { + dbg_log_string("No Pullup\n"); + return -ESHUTDOWN; + } + request = (struct usb_gsi_request *)op_data; gsi_store_ringbase_dbl_info(ep, request); break; @@ -3316,8 +3321,12 @@ static int dwc3_msm_vbus_notifier(struct notifier_block *nb, mdwc->vbus_active = event; } + /* + * Drive a pulse on DP to ensure proper CDP detection + * and only when the vbus connect event is a valid one. + */ if (get_psy_type(mdwc) == POWER_SUPPLY_TYPE_USB_CDP && - mdwc->vbus_active) { + mdwc->vbus_active && !mdwc->check_eud_state) { dev_dbg(mdwc->dev, "Connected to CDP, pull DP up\n"); usb_phy_drive_dp_pulse(mdwc->hs_phy, DP_PULSE_WIDTH_MSEC); } diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 8fa39e66494057cd478815b58c4d7381198f09f6..edf7984707b7da23703f56ebe91c5eb57ab6fc1a 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -34,6 +34,7 @@ #define PCI_DEVICE_ID_INTEL_GLK 0x31aa #define PCI_DEVICE_ID_INTEL_CNPLP 0x9dee #define PCI_DEVICE_ID_INTEL_CNPH 0xa36e +#define PCI_DEVICE_ID_INTEL_CNPV 0xa3b0 #define PCI_DEVICE_ID_INTEL_ICLLP 0x34ee #define PCI_INTEL_BXT_DSM_GUID "732b85d5-b7a7-4a1b-9ba0-4bbd00ffd511" @@ -340,6 +341,9 @@ static const struct pci_device_id dwc3_pci_id_table[] = { { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_CNPH), (kernel_ulong_t) &dwc3_pci_intel_properties, }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_CNPV), + (kernel_ulong_t) &dwc3_pci_intel_properties, }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICLLP), (kernel_ulong_t) &dwc3_pci_intel_properties, }, diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 51625d695c35a3e6209e7b175a1ade20ad4218e0..1d82889cdf3679ddccaffee57bae967984daeda8 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -753,12 +753,13 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action) reg |= DWC3_DALEPENA_EP(dep->number); dwc3_writel(dwc->regs, DWC3_DALEPENA, reg); + dep->trb_dequeue = 0; + dep->trb_enqueue = 0; + if (usb_endpoint_xfer_control(desc)) goto out; /* Initialize the TRB ring */ - dep->trb_dequeue = 0; - dep->trb_enqueue = 0; memset(dep->trb_pool, 0, sizeof(struct dwc3_trb) * DWC3_TRB_NUM); @@ -1289,13 +1290,13 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, unsigned chain = true; /* - * IOMMU driver is clubbing the list of sgs which shares a page - * boundary into one and giving it to USB driver. With this the - * number of sgs mapped it not equal to the the number of sgs - * passed. Mark the chain bit to false if it is the last mapped - * sg. + * IOMMU driver is coalescing the list of sgs which shares a + * page boundary into one and giving it to USB driver. With + * this the number of sgs mapped is not equal to the number of + * sgs passed. So mark the chain bit to false if it isthe last + * mapped sg. */ - if (sg_is_last(s) || (i == remaining - 1)) + if (i == remaining - 1) chain = false; if (rem && usb_endpoint_dir_out(dep->endpoint.desc) && !chain) { @@ -1558,6 +1559,7 @@ static void __dwc3_gadget_start_isoc(struct dwc3_ep *dep) static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) { struct dwc3 *dwc = dep->dwc; + int ret = 0; if (!dep->endpoint.desc || !dwc->pullups_connected) { dev_err_ratelimited(dwc->dev, "%s: can't queue to disabled endpoint\n", @@ -1604,7 +1606,11 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) } } - return __dwc3_gadget_kick_transfer(dep); + ret = __dwc3_gadget_kick_transfer(dep); + if (ret < 0) + list_del_init(&req->list); + + return ret; } static int dwc3_gadget_wakeup(struct usb_gadget *g) @@ -2331,8 +2337,14 @@ static void dwc3_gadget_enable_irq(struct dwc3 *dwc) DWC3_DEVTEN_USBRSTEN | DWC3_DEVTEN_DISCONNEVTEN); + /* + * Enable SUSPENDEVENT(BIT:6) for version 230A and above + * else enable USB Link change event (BIT:3) for older version + */ if (dwc->revision < DWC3_REVISION_230A) reg |= DWC3_DEVTEN_ULSTCNGEN; + else + reg |= DWC3_DEVTEN_EOPFEN; dwc3_writel(dwc->regs, DWC3_DEVTEN, reg); } @@ -2510,6 +2522,7 @@ static int __dwc3_gadget_start(struct dwc3 *dwc) /* begin to receive SETUP packets */ dwc->ep0state = EP0_SETUP_PHASE; + dwc->ep0_bounced = false; dwc->link_state = DWC3_LINK_STATE_SS_DIS; dwc3_ep0_out_start(dwc); @@ -2866,7 +2879,8 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep, if (event->status & DEPEVT_STATUS_SHORT && !chain) return 1; - if (event->status & DEPEVT_STATUS_IOC) + if ((trb->ctrl & DWC3_TRB_CTRL_IOC) || + (trb->ctrl & DWC3_TRB_CTRL_LST)) return 1; return 0; @@ -3370,6 +3384,10 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) dwc3_ep0_end_control_data(dwc, dwc->eps[dir]); else dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]); + + dwc->eps[0]->trb_enqueue = 0; + dwc->eps[1]->trb_enqueue = 0; + dwc3_ep0_stall_and_restart(dwc); } @@ -3399,13 +3417,6 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) speed = reg & DWC3_DSTS_CONNECTSPD; dwc->speed = speed; - /* Enable SUSPENDEVENT(BIT:6) for version 230A and above */ - if (dwc->revision >= DWC3_REVISION_230A) { - reg = dwc3_readl(dwc->regs, DWC3_DEVTEN); - reg |= DWC3_DEVTEN_EOPFEN; - dwc3_writel(dwc->regs, DWC3_DEVTEN, reg); - } - /* Reset the retry on erratic error event count */ dwc->retries_on_error = 0; diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 9086b6486830d1c2e4c4747d6bfdfe8b719ee825..a316b9a59eb6449d6758cc72d820b5bce001743c 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -548,14 +548,14 @@ static u8 encode_bMaxPower(enum usb_device_speed speed, val = CONFIG_USB_GADGET_VBUS_DRAW; if (!val) return 0; - switch (speed) { - case USB_SPEED_SUPER: - case USB_SPEED_SUPER_PLUS: - return (u8)(val / 8); - default: - /* only SuperSpeed and faster support > 500mA */ - return DIV_ROUND_UP(min(val, 500U), 2); - } + if (speed < USB_SPEED_SUPER) + return min(val, 500U) / 2; + else + /* + * USB 3.x supports up to 900mA, but since 900 isn't divisible + * by 8 the integral division will effectively cap to 896mA. + */ + return min(val, 900U) / 8; } static int config_buf(struct usb_configuration *config, @@ -964,7 +964,8 @@ static int set_config(struct usb_composite_dev *cdev, power = c->MaxPower ? c->MaxPower : CONFIG_USB_GADGET_VBUS_DRAW; if (gadget->speed < USB_SPEED_SUPER) power = min(power, 500U); - + else + power = min(power, 900U); done: usb_gadget_vbus_draw(gadget, power); if (result >= 0 && cdev->delayed_status) @@ -2457,10 +2458,13 @@ void composite_resume(struct usb_gadget *gadget) f->resume(f); } - maxpower = cdev->config->MaxPower; - maxpower = maxpower ? maxpower : CONFIG_USB_GADGET_VBUS_DRAW; + maxpower = cdev->config->MaxPower ? + cdev->config->MaxPower : CONFIG_USB_GADGET_VBUS_DRAW; if (gadget->speed < USB_SPEED_SUPER) maxpower = min(maxpower, 500U); + else + maxpower = min(maxpower, 900U); + usb_gadget_vbus_draw(gadget, maxpower); } diff --git a/drivers/usb/gadget/function/f_cdev.c b/drivers/usb/gadget/function/f_cdev.c index 616558e4d6c58bcca12e1c8fa01e0e4bc6e66eab..1faf4ed7b44d05f9cf0b5ba5fb3bff072e8cbbea 100644 --- a/drivers/usb/gadget/function/f_cdev.c +++ b/drivers/usb/gadget/function/f_cdev.c @@ -139,6 +139,7 @@ struct f_cdev_opts { struct f_cdev *port; char *func_name; u8 port_num; + u8 proto; }; static int major, minors; @@ -161,8 +162,8 @@ static struct usb_interface_descriptor cser_interface_desc = { /* .bInterfaceNumber = DYNAMIC */ .bNumEndpoints = 3, .bInterfaceClass = USB_CLASS_VENDOR_SPEC, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 0, + .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC, + /* .bInterfaceProtocol = DYNAMIC */ /* .iInterface = DYNAMIC */ }; @@ -778,6 +779,8 @@ static int usb_cser_bind(struct usb_configuration *c, struct usb_function *f) struct f_cdev *port = func_to_port(f); int status; struct usb_ep *ep; + struct f_cdev_opts *opts = + container_of(f->fi, struct f_cdev_opts, func_inst); if (cser_string_defs[0].id == 0) { status = usb_string_id(c->cdev); @@ -791,6 +794,7 @@ static int usb_cser_bind(struct usb_configuration *c, struct usb_function *f) goto fail; port->port_usb.data_id = status; cser_interface_desc.bInterfaceNumber = status; + cser_interface_desc.bInterfaceProtocol = opts->proto; status = -ENODEV; ep = usb_ep_autoconfig(cdev->gadget, &cser_fs_in_desc); @@ -949,7 +953,10 @@ static void usb_cser_start_rx(struct f_cdev *port) if (ret) { pr_err("port(%d):%pK usb ep(%s) queue failed\n", port->port_num, port, ep->name); - list_add(&req->list, pool); + if (port->is_connected) + list_add(&req->list, &port->read_pool); + else + usb_cser_free_req(port->port_usb.out, req); break; } } @@ -2005,6 +2012,9 @@ static int cser_set_inst_name(struct usb_function_instance *f, const char *name) port->port_usb.send_modem_ctrl_bits = dun_cser_send_ctrl_bits; port->port_usb.disconnect = dun_cser_disconnect; port->port_usb.send_break = dun_cser_send_break; + opts->proto = 0x40; + } else { + opts->proto = 0x60; } return 0; diff --git a/drivers/usb/gadget/function/f_diag.c b/drivers/usb/gadget/function/f_diag.c index 10f165a7400fc4843b1b99c0ad6ddcb44bedd0af..ffd427fa0b8f8fb5c7bba9b80427492d21504eeb 100644 --- a/drivers/usb/gadget/function/f_diag.c +++ b/drivers/usb/gadget/function/f_diag.c @@ -3,7 +3,7 @@ * Diag Function Device - Route ARM9 and ARM11 DIAG messages * between HOST and DEVICE. * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2008-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2008-2020, The Linux Foundation. All rights reserved. * Author: Brian Swetland */ #include @@ -60,8 +60,8 @@ static struct usb_interface_descriptor intf_desc = { .bLength = sizeof(intf_desc), .bDescriptorType = USB_DT_INTERFACE, .bNumEndpoints = 2, - .bInterfaceClass = 0xFF, - .bInterfaceSubClass = 0xFF, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC, .bInterfaceProtocol = 0x30, }; diff --git a/drivers/usb/gadget/function/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c index 460d5d7c984f50c66ff6bc821cea526986008d2a..7f5cf488b2b1eda3ae69de6e5b85dd32e7992c8a 100644 --- a/drivers/usb/gadget/function/f_ecm.c +++ b/drivers/usb/gadget/function/f_ecm.c @@ -52,6 +52,7 @@ struct f_ecm { struct usb_ep *notify; struct usb_request *notify_req; u8 notify_state; + atomic_t notify_count; bool is_open; /* FIXME is_open needs some irq-ish locking @@ -380,7 +381,7 @@ static void ecm_do_notify(struct f_ecm *ecm) int status; /* notification already in flight? */ - if (!req) + if (atomic_read(&ecm->notify_count)) return; event = req->buf; @@ -420,10 +421,10 @@ static void ecm_do_notify(struct f_ecm *ecm) event->bmRequestType = 0xA1; event->wIndex = cpu_to_le16(ecm->ctrl_id); - ecm->notify_req = NULL; + atomic_inc(&ecm->notify_count); status = usb_ep_queue(ecm->notify, req, GFP_ATOMIC); if (status < 0) { - ecm->notify_req = req; + atomic_dec(&ecm->notify_count); DBG(cdev, "notify --> %d\n", status); } } @@ -448,17 +449,19 @@ static void ecm_notify_complete(struct usb_ep *ep, struct usb_request *req) switch (req->status) { case 0: /* no fault */ + atomic_dec(&ecm->notify_count); break; case -ECONNRESET: case -ESHUTDOWN: + atomic_set(&ecm->notify_count, 0); ecm->notify_state = ECM_NOTIFY_NONE; break; default: DBG(cdev, "event %02x --> %d\n", event->bNotificationType, req->status); + atomic_dec(&ecm->notify_count); break; } - ecm->notify_req = req; ecm_do_notify(ecm); } @@ -907,6 +910,11 @@ static void ecm_unbind(struct usb_configuration *c, struct usb_function *f) usb_free_all_descriptors(f); + if (atomic_read(&ecm->notify_count)) { + usb_ep_dequeue(ecm->notify, ecm->notify_req); + atomic_set(&ecm->notify_count, 0); + } + kfree(ecm->notify_req->buf); usb_ep_free_request(ecm->notify, ecm->notify_req); } diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 4024d3dcad1fd4b982b3eb55247f4f44cb96fb8e..511d46db60497bfc7561eda80b274cdc64dfe11e 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -139,6 +139,7 @@ struct ffs_epfile { struct ffs_data *ffs; struct ffs_ep *ep; /* P: ffs->eps_lock */ + atomic_t opened; struct dentry *dentry; @@ -205,7 +206,7 @@ struct ffs_epfile { unsigned char in; /* P: ffs->eps_lock */ unsigned char isoc; /* P: ffs->eps_lock */ - unsigned char _pad; + bool invalid; }; struct ffs_buffer { @@ -956,6 +957,16 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) if (file->f_flags & O_NONBLOCK) return -EAGAIN; + /* + * epfile->invalid is set when EPs are disabled. Userspace + * might have stale threads continuing to do I/O and may be + * unaware of that especially if we block here. Instead return + * an error immediately here and don't allow any more I/O + * until the epfile is reopened. + */ + if (epfile->invalid) + return -ENODEV; + ret = wait_event_interruptible( epfile->ffs->wait, (ep = epfile->ep)); if (ret) @@ -1152,15 +1163,16 @@ ffs_epfile_open(struct inode *inode, struct file *file) ENTER(); - ffs_log("%s: state %d setup_state %d flag %lu", epfile->name, - epfile->ffs->state, epfile->ffs->setup_state, - epfile->ffs->flags); + ffs_log("%s: state %d setup_state %d flag %lu opened %u", + epfile->name, epfile->ffs->state, epfile->ffs->setup_state, + epfile->ffs->flags, atomic_read(&epfile->opened)); if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) return -ENODEV; file->private_data = epfile; ffs_data_opened(epfile->ffs); + atomic_inc(&epfile->opened); return 0; } @@ -1170,6 +1182,7 @@ static int ffs_aio_cancel(struct kiocb *kiocb) struct ffs_io_data *io_data = kiocb->private; struct ffs_epfile *epfile = kiocb->ki_filp->private_data; struct ffs_data *ffs = epfile->ffs; + unsigned long flags; int value; ENTER(); @@ -1177,14 +1190,14 @@ static int ffs_aio_cancel(struct kiocb *kiocb) ffs_log("enter:state %d setup_state %d flag %lu", epfile->ffs->state, epfile->ffs->setup_state, epfile->ffs->flags); - spin_lock_irq(&epfile->ffs->eps_lock); + spin_lock_irqsave(&epfile->ffs->eps_lock, flags); if (likely(io_data && io_data->ep && io_data->req)) value = usb_ep_dequeue(io_data->ep, io_data->req); else value = -EINVAL; - spin_unlock_irq(&epfile->ffs->eps_lock); + spin_unlock_irqrestore(&epfile->ffs->eps_lock, flags); ffs_log("exit: value %d", value); @@ -1300,9 +1313,12 @@ ffs_epfile_release(struct inode *inode, struct file *file) ENTER(); __ffs_epfile_read_buffer_free(epfile); - ffs_log("%s: state %d setup_state %d flag %lu", epfile->name, - epfile->ffs->state, epfile->ffs->setup_state, - epfile->ffs->flags); + ffs_log("%s: state %d setup_state %d flag %lu opened %u", + epfile->name, epfile->ffs->state, epfile->ffs->setup_state, + epfile->ffs->flags, atomic_read(&epfile->opened)); + + if (atomic_dec_and_test(&epfile->opened)) + epfile->invalid = false; ffs_data_closed(epfile->ffs); @@ -1332,6 +1348,10 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, if (file->f_flags & O_NONBLOCK) return -EAGAIN; + /* don't allow any I/O until file is reopened */ + if (epfile->invalid) + return -ENODEV; + ret = wait_event_interruptible( epfile->ffs->wait, (ep = epfile->ep)); if (ret) @@ -1997,6 +2017,8 @@ static int ffs_epfiles_create(struct ffs_data *ffs) ffs_epfiles_destroy(epfiles, i - 1); return -ENOMEM; } + + atomic_set(&epfile->opened, 0); } ffs->epfiles = epfiles; @@ -2044,6 +2066,7 @@ static void ffs_func_eps_disable(struct ffs_function *func) ++ep; if (epfile) { + epfile->invalid = true; /* until file is reopened */ epfile->ep = NULL; __ffs_epfile_read_buffer_free(epfile); ++epfile; @@ -3426,8 +3449,10 @@ static int ffs_func_set_alt(struct usb_function *f, return intf; } - if (ffs->func) + if (ffs->func) { ffs_func_eps_disable(ffs->func); + ffs->func = NULL; + } if (ffs->state == FFS_DEACTIVATED) { ffs->state = FFS_CLOSING; diff --git a/drivers/usb/gadget/function/f_gsi.h b/drivers/usb/gadget/function/f_gsi.h index ae698bfb779ce8580494bff0c95fe0cfb08c6c6c..4aaee1d6efa0cc4bc55c77f1986715d3275905d6 100644 --- a/drivers/usb/gadget/function/f_gsi.h +++ b/drivers/usb/gadget/function/f_gsi.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. */ #ifndef _F_GSI_H @@ -344,8 +344,8 @@ static struct usb_interface_descriptor rmnet_gsi_interface_desc = { .bDescriptorType = USB_DT_INTERFACE, .bNumEndpoints = 3, .bInterfaceClass = USB_CLASS_VENDOR_SPEC, - .bInterfaceSubClass = USB_CLASS_VENDOR_SPEC, - .bInterfaceProtocol = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC, + .bInterfaceProtocol = 0x50, /* .iInterface = DYNAMIC */ }; @@ -1355,9 +1355,9 @@ static struct usb_interface_descriptor qdss_gsi_data_intf_desc = { .bDescriptorType = USB_DT_INTERFACE, .bAlternateSetting = 0, .bNumEndpoints = 1, - .bInterfaceClass = 0xff, - .bInterfaceSubClass = 0xff, - .bInterfaceProtocol = 0xff, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC, + .bInterfaceProtocol = 0x80, }; static struct usb_endpoint_descriptor qdss_gsi_fs_data_desc = { diff --git a/drivers/usb/gadget/function/f_mtp.c b/drivers/usb/gadget/function/f_mtp.c index 19a6511b706cdd6730019cd1b7bb96e1fe75e8c6..80ff811b6b1a1262693fe1a1081c97a105fd64b9 100644 --- a/drivers/usb/gadget/function/f_mtp.c +++ b/drivers/usb/gadget/function/f_mtp.c @@ -1128,18 +1128,15 @@ static long mtp_send_receive_ioctl(struct file *fp, unsigned int code, * in kernel context, which is necessary for vfs_read and * vfs_write to use our buffers in the kernel address space. */ - dev->xfer_result = 0; - if (dev->xfer_file_length) { - queue_work(dev->wq, work); - /* wait for operation to complete */ - flush_workqueue(dev->wq); - - /* read the result */ - smp_rmb(); - } - ret = dev->xfer_result; + queue_work(dev->wq, work); + /* wait for operation to complete */ + flush_workqueue(dev->wq); fput(filp); + /* read the result */ + smp_rmb(); + ret = dev->xfer_result; + fail: spin_lock_irq(&dev->lock); if (dev->state == STATE_CANCELED) diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index 97cce3bee17d745ad159f4366003a907ba9ab6cf..2200c8f9f33371031c53029e53d10d663ff5111a 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -54,6 +54,7 @@ struct f_ncm { struct usb_ep *notify; struct usb_request *notify_req; u8 notify_state; + atomic_t notify_count; bool is_open; const struct ndp_parser_opts *parser_opts; @@ -547,7 +548,7 @@ static void ncm_do_notify(struct f_ncm *ncm) int status; /* notification already in flight? */ - if (!req) + if (atomic_read(&ncm->notify_count)) return; event = req->buf; @@ -587,7 +588,8 @@ static void ncm_do_notify(struct f_ncm *ncm) event->bmRequestType = 0xA1; event->wIndex = cpu_to_le16(ncm->ctrl_id); - ncm->notify_req = NULL; + atomic_inc(&ncm->notify_count); + /* * In double buffering if there is a space in FIFO, * completion callback can be called right after the call, @@ -597,7 +599,7 @@ static void ncm_do_notify(struct f_ncm *ncm) status = usb_ep_queue(ncm->notify, req, GFP_ATOMIC); spin_lock(&ncm->lock); if (status < 0) { - ncm->notify_req = req; + atomic_dec(&ncm->notify_count); DBG(cdev, "notify --> %d\n", status); } } @@ -632,17 +634,19 @@ static void ncm_notify_complete(struct usb_ep *ep, struct usb_request *req) case 0: VDBG(cdev, "Notification %02x sent\n", event->bNotificationType); + atomic_dec(&ncm->notify_count); break; case -ECONNRESET: case -ESHUTDOWN: + atomic_set(&ncm->notify_count, 0); ncm->notify_state = NCM_NOTIFY_NONE; break; default: DBG(cdev, "event %02x --> %d\n", event->bNotificationType, req->status); + atomic_dec(&ncm->notify_count); break; } - ncm->notify_req = req; ncm_do_notify(ncm); spin_unlock(&ncm->lock); } @@ -1689,6 +1693,11 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f) ncm_string_defs[0].id = 0; usb_free_all_descriptors(f); + if (atomic_read(&ncm->notify_count)) { + usb_ep_dequeue(ncm->notify, ncm->notify_req); + atomic_set(&ncm->notify_count, 0); + } + kfree(ncm->notify_req->buf); usb_ep_free_request(ncm->notify, ncm->notify_req); diff --git a/drivers/usb/gadget/function/f_qdss.c b/drivers/usb/gadget/function/f_qdss.c index e5c179b876b4995b7b76f0c3f23b81374d62bf09..3eedde60a6fe37d9eaeeb732c7725991f8fb3403 100644 --- a/drivers/usb/gadget/function/f_qdss.c +++ b/drivers/usb/gadget/function/f_qdss.c @@ -22,9 +22,9 @@ static struct usb_interface_descriptor qdss_data_intf_desc = { .bDescriptorType = USB_DT_INTERFACE, .bAlternateSetting = 0, .bNumEndpoints = 1, - .bInterfaceClass = 0xff, - .bInterfaceSubClass = 0xff, - .bInterfaceProtocol = 0xff, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC, + .bInterfaceProtocol = 0x70, }; static struct usb_endpoint_descriptor qdss_hs_data_desc = { @@ -56,9 +56,9 @@ static struct usb_interface_descriptor qdss_ctrl_intf_desc = { .bDescriptorType = USB_DT_INTERFACE, .bAlternateSetting = 0, .bNumEndpoints = 2, - .bInterfaceClass = 0xff, - .bInterfaceSubClass = 0xff, - .bInterfaceProtocol = 0xff, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC, + .bInterfaceProtocol = 0x70, }; static struct usb_endpoint_descriptor qdss_hs_ctrl_in_desc = { @@ -854,8 +854,22 @@ int usb_qdss_write(struct usb_qdss_ch *ch, struct qdss_request *d_req) return -EIO; } + /* If data_write_pool is empty & debug inface is set + * then return -EINVAL instead of -EAGAIN. + * qdss_write is expecting data_write_pool to + * be populated. But data_write_pool is populated only + * when debug_inface_enbled is not set. + */ + if (list_empty(&qdss->data_write_pool) && + qdss->debug_inface_enabled) { + pr_err("%s:error: Invalid operation.\n", __func__); + spin_unlock_irqrestore(&qdss->lock, flags); + return -EINVAL; + } + if (list_empty(&qdss->data_write_pool)) { - pr_err("error: usb_qdss_data_write list is empty\n"); + pr_err_ratelimited( + "error: usb_qdss_data_write list is empty\n"); spin_unlock_irqrestore(&qdss->lock, flags); return -EAGAIN; } diff --git a/drivers/usb/gadget/function/f_serial.c b/drivers/usb/gadget/function/f_serial.c index c860f30a0ea2bb9d0266d4134b39c890c3bb135d..0f63755c62fd2b1a9ebec8ea2d968a51fdd0d72f 100644 --- a/drivers/usb/gadget/function/f_serial.c +++ b/drivers/usb/gadget/function/f_serial.c @@ -46,7 +46,7 @@ static struct usb_interface_descriptor gser_interface_desc = { .bNumEndpoints = 2, .bInterfaceClass = USB_CLASS_VENDOR_SPEC, .bInterfaceSubClass = 0, - .bInterfaceProtocol = 0, + .bInterfaceProtocol = 0x40, /* .iInterface = DYNAMIC */ }; diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index d4d317db89df5ded4af8ee8979a1214bb68eb2ab..38afe96c5cd2658875a99715d6db52a81cd861f2 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -567,8 +567,10 @@ static int gs_start_io(struct gs_port *port) port->n_read = 0; started = gs_start_rx(port); - /* unblock any pending writes into our circular buffer */ if (started) { + gs_start_tx(port); + /* Unblock any pending writes into our circular buffer, in case + * we didn't in gs_start_tx() */ tty_wakeup(port->port.tty); } else { gs_free_requests(ep, head, &port->read_allocated); diff --git a/drivers/usb/gadget/legacy/cdc2.c b/drivers/usb/gadget/legacy/cdc2.c index da1c37933ca137c39e39e953ed4dbdbcebe9f58d..8d7a556ece30843108c1a797ccf494da4e9e8a57 100644 --- a/drivers/usb/gadget/legacy/cdc2.c +++ b/drivers/usb/gadget/legacy/cdc2.c @@ -225,7 +225,7 @@ static struct usb_composite_driver cdc_driver = { .name = "g_cdc", .dev = &device_desc, .strings = dev_strings, - .max_speed = USB_SPEED_HIGH, + .max_speed = USB_SPEED_SUPER, .bind = cdc_bind, .unbind = cdc_unbind, }; diff --git a/drivers/usb/gadget/legacy/g_ffs.c b/drivers/usb/gadget/legacy/g_ffs.c index b640ed3fcf706720b14a2c39c8fce843cf1afd04..ae6d8f7092b8e97a0153bfc7758cca99348397a6 100644 --- a/drivers/usb/gadget/legacy/g_ffs.c +++ b/drivers/usb/gadget/legacy/g_ffs.c @@ -149,7 +149,7 @@ static struct usb_composite_driver gfs_driver = { .name = DRIVER_NAME, .dev = &gfs_dev_desc, .strings = gfs_dev_strings, - .max_speed = USB_SPEED_HIGH, + .max_speed = USB_SPEED_SUPER, .bind = gfs_bind, .unbind = gfs_unbind, }; diff --git a/drivers/usb/gadget/legacy/multi.c b/drivers/usb/gadget/legacy/multi.c index 50515f9e1022dae5f3dbf7baabe30f24671baf44..ec974984566095a54cbae210b384ff600c2dfdd4 100644 --- a/drivers/usb/gadget/legacy/multi.c +++ b/drivers/usb/gadget/legacy/multi.c @@ -482,7 +482,7 @@ static struct usb_composite_driver multi_driver = { .name = "g_multi", .dev = &device_desc, .strings = dev_strings, - .max_speed = USB_SPEED_HIGH, + .max_speed = USB_SPEED_SUPER, .bind = multi_bind, .unbind = multi_unbind, .needs_serial = 1, diff --git a/drivers/usb/gadget/legacy/ncm.c b/drivers/usb/gadget/legacy/ncm.c index 8465f081e9218209c3319a39307a784eb42ac883..c61e71ba7045a3e8a996e95be49775a02b6fa46c 100644 --- a/drivers/usb/gadget/legacy/ncm.c +++ b/drivers/usb/gadget/legacy/ncm.c @@ -197,7 +197,7 @@ static struct usb_composite_driver ncm_driver = { .name = "g_ncm", .dev = &device_desc, .strings = dev_strings, - .max_speed = USB_SPEED_HIGH, + .max_speed = USB_SPEED_SUPER, .bind = gncm_bind, .unbind = gncm_unbind, }; diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index d44b26d5b2a2c5b4b26c54018a692dccee36bb17..367697144cda2964ae248079d8d9fa5b7f028605 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -2247,8 +2247,10 @@ static int struct_udc_setup(struct fsl_udc *udc, udc->phy_mode = pdata->phy_mode; udc->eps = kcalloc(udc->max_ep, sizeof(struct fsl_ep), GFP_KERNEL); - if (!udc->eps) - return -1; + if (!udc->eps) { + ERR("kmalloc udc endpoint status failed\n"); + goto eps_alloc_failed; + } /* initialized QHs, take care of alignment */ size = udc->max_ep * sizeof(struct ep_queue_head); @@ -2262,8 +2264,7 @@ static int struct_udc_setup(struct fsl_udc *udc, &udc->ep_qh_dma, GFP_KERNEL); if (!udc->ep_qh) { ERR("malloc QHs for udc failed\n"); - kfree(udc->eps); - return -1; + goto ep_queue_alloc_failed; } udc->ep_qh_size = size; @@ -2272,8 +2273,17 @@ static int struct_udc_setup(struct fsl_udc *udc, /* FIXME: fsl_alloc_request() ignores ep argument */ udc->status_req = container_of(fsl_alloc_request(NULL, GFP_KERNEL), struct fsl_req, req); + if (!udc->status_req) { + ERR("kzalloc for udc status request failed\n"); + goto udc_status_alloc_failed; + } + /* allocate a small amount of memory to get valid address */ udc->status_req->req.buf = kmalloc(8, GFP_KERNEL); + if (!udc->status_req->req.buf) { + ERR("kzalloc for udc request buffer failed\n"); + goto udc_req_buf_alloc_failed; + } udc->resume_state = USB_STATE_NOTATTACHED; udc->usb_state = USB_STATE_POWERED; @@ -2281,6 +2291,18 @@ static int struct_udc_setup(struct fsl_udc *udc, udc->remote_wakeup = 0; /* default to 0 on reset */ return 0; + +udc_req_buf_alloc_failed: + kfree(udc->status_req); +udc_status_alloc_failed: + kfree(udc->ep_qh); + udc->ep_qh_size = 0; +ep_queue_alloc_failed: + kfree(udc->eps); +eps_alloc_failed: + udc->phy_mode = 0; + return -1; + } /*---------------------------------------------------------------- diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c index 729e60e495641c8a523265f6812c5b34c1f23213..e50108f9a374e6d99f0f50ec91e6220aab24032a 100644 --- a/drivers/usb/gadget/udc/gr_udc.c +++ b/drivers/usb/gadget/udc/gr_udc.c @@ -2180,8 +2180,6 @@ static int gr_probe(struct platform_device *pdev) return -ENOMEM; } - spin_lock(&dev->lock); - /* Inside lock so that no gadget can use this udc until probe is done */ retval = usb_add_gadget_udc(dev->dev, &dev->gadget); if (retval) { @@ -2190,15 +2188,21 @@ static int gr_probe(struct platform_device *pdev) } dev->added = 1; + spin_lock(&dev->lock); + retval = gr_udc_init(dev); - if (retval) + if (retval) { + spin_unlock(&dev->lock); goto out; - - gr_dfs_create(dev); + } /* Clear all interrupt enables that might be left on since last boot */ gr_disable_interrupts_and_pullup(dev); + spin_unlock(&dev->lock); + + gr_dfs_create(dev); + retval = gr_request_irq(dev, dev->irq); if (retval) { dev_err(dev->dev, "Failed to request irq %d\n", dev->irq); @@ -2227,8 +2231,6 @@ static int gr_probe(struct platform_device *pdev) dev_info(dev->dev, "regs: %p, irq %d\n", dev->regs, dev->irq); out: - spin_unlock(&dev->lock); - if (retval) gr_remove(pdev); diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index e4c377218ba8038c8d16a4187f69b67d0a9f4948..d32d7da231da620ad0427edc518b86a30f1b7e79 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -55,6 +55,7 @@ static u8 usb_bos_descriptor [] = { static int xhci_create_usb3_bos_desc(struct xhci_hcd *xhci, char *buf, u16 wLength) { + struct xhci_port_cap *port_cap = NULL; int i, ssa_count; u32 temp; u16 desc_size, ssp_cap_size, ssa_size = 0; @@ -64,16 +65,24 @@ static int xhci_create_usb3_bos_desc(struct xhci_hcd *xhci, char *buf, ssp_cap_size = sizeof(usb_bos_descriptor) - desc_size; /* does xhci support USB 3.1 Enhanced SuperSpeed */ - if (xhci->usb3_rhub.min_rev >= 0x01) { + for (i = 0; i < xhci->num_port_caps; i++) { + if (xhci->port_caps[i].maj_rev == 0x03 && + xhci->port_caps[i].min_rev >= 0x01) { + usb3_1 = true; + port_cap = &xhci->port_caps[i]; + break; + } + } + + if (usb3_1) { /* does xhci provide a PSI table for SSA speed attributes? */ - if (xhci->usb3_rhub.psi_count) { + if (port_cap->psi_count) { /* two SSA entries for each unique PSI ID, RX and TX */ - ssa_count = xhci->usb3_rhub.psi_uid_count * 2; + ssa_count = port_cap->psi_uid_count * 2; ssa_size = ssa_count * sizeof(u32); ssp_cap_size -= 16; /* skip copying the default SSA */ } desc_size += ssp_cap_size; - usb3_1 = true; } memcpy(buf, &usb_bos_descriptor, min(desc_size, wLength)); @@ -99,7 +108,7 @@ static int xhci_create_usb3_bos_desc(struct xhci_hcd *xhci, char *buf, } /* If PSI table exists, add the custom speed attributes from it */ - if (usb3_1 && xhci->usb3_rhub.psi_count) { + if (usb3_1 && port_cap->psi_count) { u32 ssp_cap_base, bm_attrib, psi, psi_mant, psi_exp; int offset; @@ -111,7 +120,7 @@ static int xhci_create_usb3_bos_desc(struct xhci_hcd *xhci, char *buf, /* attribute count SSAC bits 4:0 and ID count SSIC bits 8:5 */ bm_attrib = (ssa_count - 1) & 0x1f; - bm_attrib |= (xhci->usb3_rhub.psi_uid_count - 1) << 5; + bm_attrib |= (port_cap->psi_uid_count - 1) << 5; put_unaligned_le32(bm_attrib, &buf[ssp_cap_base + 4]); if (wLength < desc_size + ssa_size) @@ -124,8 +133,8 @@ static int xhci_create_usb3_bos_desc(struct xhci_hcd *xhci, char *buf, * USB 3.1 requires two SSA entries (RX and TX) for every link */ offset = desc_size; - for (i = 0; i < xhci->usb3_rhub.psi_count; i++) { - psi = xhci->usb3_rhub.psi[i]; + for (i = 0; i < port_cap->psi_count; i++) { + psi = port_cap->psi[i]; psi &= ~USB_SSP_SUBLINK_SPEED_RSVD; psi_exp = XHCI_EXT_PORT_PSIE(psi); psi_mant = XHCI_EXT_PORT_PSIM(psi); @@ -1249,7 +1258,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, } port_li = readl(ports[wIndex]->addr + PORTLI); status = xhci_get_ext_port_status(temp, port_li); - put_unaligned_le32(cpu_to_le32(status), &buf[4]); + put_unaligned_le32(status, &buf[4]); } break; case SetPortFeature: diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index ecf73f6c08d6bc57ff94e5f24acc5b094d6cf93c..5a27091f252433307fd95b8a214db0f939e66b32 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1475,9 +1475,15 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, /* Allow 3 retries for everything but isoc, set CErr = 3 */ if (!usb_endpoint_xfer_isoc(&ep->desc)) err_count = 3; - /* Some devices get this wrong */ - if (usb_endpoint_xfer_bulk(&ep->desc) && udev->speed == USB_SPEED_HIGH) - max_packet = 512; + /* HS bulk max packet should be 512, FS bulk supports 8, 16, 32 or 64 */ + if (usb_endpoint_xfer_bulk(&ep->desc)) { + if (udev->speed == USB_SPEED_HIGH) + max_packet = 512; + if (udev->speed == USB_SPEED_FULL) { + max_packet = rounddown_pow_of_two(max_packet); + max_packet = clamp_val(max_packet, 8, 64); + } + } /* xHCI 1.0 and 1.1 indicates that ctrl ep avg TRB Length should be 8 */ if (usb_endpoint_xfer_control(&ep->desc) && xhci->hci_version >= 0x100) avg_trb_len = 8; @@ -2035,17 +2041,17 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) xhci->usb3_rhub.num_ports = 0; xhci->num_active_eps = 0; kfree(xhci->usb2_rhub.ports); - kfree(xhci->usb2_rhub.psi); kfree(xhci->usb3_rhub.ports); - kfree(xhci->usb3_rhub.psi); kfree(xhci->hw_ports); kfree(xhci->rh_bw); kfree(xhci->ext_caps); + for (i = 0; i < xhci->num_port_caps; i++) + kfree(xhci->port_caps[i].psi); + kfree(xhci->port_caps); + xhci->num_port_caps = 0; xhci->usb2_rhub.ports = NULL; - xhci->usb2_rhub.psi = NULL; xhci->usb3_rhub.ports = NULL; - xhci->usb3_rhub.psi = NULL; xhci->hw_ports = NULL; xhci->rh_bw = NULL; xhci->ext_caps = NULL; @@ -2222,6 +2228,7 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, u8 major_revision, minor_revision; struct xhci_hub *rhub; struct device *dev = xhci_to_hcd(xhci)->self.sysdev; + struct xhci_port_cap *port_cap; temp = readl(addr); major_revision = XHCI_EXT_PORT_MAJOR(temp); @@ -2256,31 +2263,39 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, /* WTF? "Valid values are ‘1’ to MaxPorts" */ return; - rhub->psi_count = XHCI_EXT_PORT_PSIC(temp); - if (rhub->psi_count) { - rhub->psi = kcalloc_node(rhub->psi_count, sizeof(*rhub->psi), - GFP_KERNEL, dev_to_node(dev)); - if (!rhub->psi) - rhub->psi_count = 0; + port_cap = &xhci->port_caps[xhci->num_port_caps++]; + if (xhci->num_port_caps > max_caps) + return; + + port_cap->maj_rev = major_revision; + port_cap->min_rev = minor_revision; + port_cap->psi_count = XHCI_EXT_PORT_PSIC(temp); - rhub->psi_uid_count++; - for (i = 0; i < rhub->psi_count; i++) { - rhub->psi[i] = readl(addr + 4 + i); + if (port_cap->psi_count) { + port_cap->psi = kcalloc_node(port_cap->psi_count, + sizeof(*port_cap->psi), + GFP_KERNEL, dev_to_node(dev)); + if (!port_cap->psi) + port_cap->psi_count = 0; + + port_cap->psi_uid_count++; + for (i = 0; i < port_cap->psi_count; i++) { + port_cap->psi[i] = readl(addr + 4 + i); /* count unique ID values, two consecutive entries can * have the same ID if link is assymetric */ - if (i && (XHCI_EXT_PORT_PSIV(rhub->psi[i]) != - XHCI_EXT_PORT_PSIV(rhub->psi[i - 1]))) - rhub->psi_uid_count++; + if (i && (XHCI_EXT_PORT_PSIV(port_cap->psi[i]) != + XHCI_EXT_PORT_PSIV(port_cap->psi[i - 1]))) + port_cap->psi_uid_count++; xhci_dbg(xhci, "PSIV:%d PSIE:%d PLT:%d PFD:%d LP:%d PSIM:%d\n", - XHCI_EXT_PORT_PSIV(rhub->psi[i]), - XHCI_EXT_PORT_PSIE(rhub->psi[i]), - XHCI_EXT_PORT_PLT(rhub->psi[i]), - XHCI_EXT_PORT_PFD(rhub->psi[i]), - XHCI_EXT_PORT_LP(rhub->psi[i]), - XHCI_EXT_PORT_PSIM(rhub->psi[i])); + XHCI_EXT_PORT_PSIV(port_cap->psi[i]), + XHCI_EXT_PORT_PSIE(port_cap->psi[i]), + XHCI_EXT_PORT_PLT(port_cap->psi[i]), + XHCI_EXT_PORT_PFD(port_cap->psi[i]), + XHCI_EXT_PORT_LP(port_cap->psi[i]), + XHCI_EXT_PORT_PSIM(port_cap->psi[i])); } } /* cache usb2 port capabilities */ @@ -2327,6 +2342,7 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, continue; } hw_port->rhub = rhub; + hw_port->port_cap = port_cap; rhub->num_ports++; } /* FIXME: Should we disable ports not in the Extended Capabilities? */ @@ -2417,6 +2433,11 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) if (!xhci->ext_caps) return -ENOMEM; + xhci->port_caps = kcalloc_node(cap_count, sizeof(*xhci->port_caps), + flags, dev_to_node(dev)); + if (!xhci->port_caps) + return -ENOMEM; + offset = cap_start; while (offset) { diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 075c49cfe60f9bde8c05f273244e1890059a9774..58cf551a1246773d4ad21b904c4a19578b476cdd 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -41,6 +41,7 @@ #define PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI 0x1aa8 #define PCI_DEVICE_ID_INTEL_APL_XHCI 0x5aa8 #define PCI_DEVICE_ID_INTEL_DNV_XHCI 0x19d0 +#define PCI_DEVICE_ID_INTEL_CML_XHCI 0xa3af #define PCI_DEVICE_ID_AMD_PROMONTORYA_4 0x43b9 #define PCI_DEVICE_ID_AMD_PROMONTORYA_3 0x43ba @@ -179,7 +180,8 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI || pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI || pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI || - pdev->device == PCI_DEVICE_ID_INTEL_DNV_XHCI)) { + pdev->device == PCI_DEVICE_ID_INTEL_DNV_XHCI || + pdev->device == PCI_DEVICE_ID_INTEL_CML_XHCI)) { xhci->quirks |= XHCI_PME_STUCK_QUIRK; } if (pdev->vendor == PCI_VENDOR_ID_INTEL && @@ -283,6 +285,9 @@ static int xhci_pci_setup(struct usb_hcd *hcd) if (!usb_hcd_is_primary_hcd(hcd)) return 0; + if (xhci->quirks & XHCI_PME_STUCK_QUIRK) + xhci_pme_acpi_rtd3_enable(pdev); + xhci_dbg(xhci, "Got SBRN %u\n", (unsigned int) xhci->sbrn); /* Find any debug ports */ @@ -340,9 +345,6 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) HCC_MAX_PSA(xhci->hcc_params) >= 4) xhci->shared_hcd->can_do_streams = 1; - if (xhci->quirks & XHCI_PME_STUCK_QUIRK) - xhci_pme_acpi_rtd3_enable(dev); - /* USB-2 and USB-3 roothubs initialized, allow runtime pm suspend */ pm_runtime_put_noidle(&dev->dev); diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 056886104a7adfffbc8e9d66e2127ab4b4c6ef4d..20ea7f5cb044d490962dce518c17119b07edcdaa 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2692,6 +2692,42 @@ static int xhci_handle_event(struct xhci_hcd *xhci) return 1; } +/* + * Update Event Ring Dequeue Pointer: + * - When all events have finished + * - To avoid "Event Ring Full Error" condition + */ +static void xhci_update_erst_dequeue(struct xhci_hcd *xhci, + union xhci_trb *event_ring_deq) +{ + u64 temp_64; + dma_addr_t deq; + + temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); + /* If necessary, update the HW's version of the event ring deq ptr. */ + if (event_ring_deq != xhci->event_ring->dequeue) { + deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, + xhci->event_ring->dequeue); + if (deq == 0) + xhci_warn(xhci, "WARN something wrong with SW event ring dequeue ptr\n"); + /* + * Per 4.9.4, Software writes to the ERDP register shall + * always advance the Event Ring Dequeue Pointer value. + */ + if ((temp_64 & (u64) ~ERST_PTR_MASK) == + ((u64) deq & (u64) ~ERST_PTR_MASK)) + return; + + /* Update HC event ring dequeue pointer */ + temp_64 &= ERST_PTR_MASK; + temp_64 |= ((u64) deq & (u64) ~ERST_PTR_MASK); + } + + /* Clear the event handler busy flag (RW1C) */ + temp_64 |= ERST_EHB; + xhci_write_64(xhci, temp_64, &xhci->ir_set->erst_dequeue); +} + /* * xHCI spec says we can get an interrupt, and if the HC has an error condition, * we might get bad data out of the event ring. Section 4.10.2.7 has a list of @@ -2703,9 +2739,9 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) union xhci_trb *event_ring_deq; irqreturn_t ret = IRQ_NONE; unsigned long flags; - dma_addr_t deq; u64 temp_64; u32 status; + int event_loop = 0; spin_lock_irqsave(&xhci->lock, flags); /* Check if the xHC generated the interrupt, or the irq is shared */ @@ -2759,24 +2795,14 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) /* FIXME this should be a delayed service routine * that clears the EHB. */ - while (xhci_handle_event(xhci) > 0) {} - - temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); - /* If necessary, update the HW's version of the event ring deq ptr. */ - if (event_ring_deq != xhci->event_ring->dequeue) { - deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, - xhci->event_ring->dequeue); - if (deq == 0) - xhci_warn(xhci, "WARN something wrong with SW event " - "ring dequeue ptr.\n"); - /* Update HC event ring dequeue pointer */ - temp_64 &= ERST_PTR_MASK; - temp_64 |= ((u64) deq & (u64) ~ERST_PTR_MASK); + while (xhci_handle_event(xhci) > 0) { + if (event_loop++ < TRBS_PER_SEGMENT / 2) + continue; + xhci_update_erst_dequeue(xhci, event_ring_deq); + event_loop = 0; } - /* Clear the event handler busy flag (RW1C); event ring is empty. */ - temp_64 |= ERST_EHB; - xhci_write_64(xhci, temp_64, &xhci->ir_set->erst_dequeue); + xhci_update_erst_dequeue(xhci, event_ring_deq); ret = IRQ_HANDLED; out: diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 076fdd5408a2db66386fdf422d68ac6f9c3df263..4aca59a64be07952bd0d51d33f93cc9b06dca524 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1704,11 +1704,21 @@ static inline unsigned int hcd_index(struct usb_hcd *hcd) else return 1; } + +struct xhci_port_cap { + u32 *psi; /* array of protocol speed ID entries */ + u8 psi_count; + u8 psi_uid_count; + u8 maj_rev; + u8 min_rev; +}; + struct xhci_port { __le32 __iomem *addr; int hw_portnum; int hcd_portnum; struct xhci_hub *rhub; + struct xhci_port_cap *port_cap; }; struct xhci_hub { @@ -1718,9 +1728,6 @@ struct xhci_hub { /* supported prococol extended capabiliy values */ u8 maj_rev; u8 min_rev; - u32 *psi; /* array of protocol speed ID entries */ - u8 psi_count; - u8 psi_uid_count; }; /* There is one xhci_hcd structure per controller */ @@ -1891,6 +1898,9 @@ struct xhci_hcd { /* cached usb2 extened protocol capabilites */ u32 *ext_caps; unsigned int num_ext_caps; + /* cached extended protocol port capabilities */ + struct xhci_port_cap *port_caps; + unsigned int num_port_caps; /* Compliance Mode Recovery Data */ struct timer_list comp_mode_recovery_timer; u32 port_status_u0; diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index 2d9d9490cdd4759cbf190c5a2be8653add77b3e0..92875a264b14a4885897d9f585708b0c9cd99650 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -33,6 +33,14 @@ #define USB_DEVICE_ID_CODEMERCS_IOWPV2 0x1512 /* full speed iowarrior */ #define USB_DEVICE_ID_CODEMERCS_IOW56 0x1503 +/* fuller speed iowarrior */ +#define USB_DEVICE_ID_CODEMERCS_IOW28 0x1504 +#define USB_DEVICE_ID_CODEMERCS_IOW28L 0x1505 +#define USB_DEVICE_ID_CODEMERCS_IOW100 0x1506 + +/* OEMed devices */ +#define USB_DEVICE_ID_CODEMERCS_IOW24SAG 0x158a +#define USB_DEVICE_ID_CODEMERCS_IOW56AM 0x158b /* Get a minor range for your devices from the usb maintainer */ #ifdef CONFIG_USB_DYNAMIC_MINORS @@ -137,6 +145,11 @@ static const struct usb_device_id iowarrior_ids[] = { {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOWPV1)}, {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOWPV2)}, {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW56)}, + {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW24SAG)}, + {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW56AM)}, + {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW28)}, + {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW28L)}, + {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW100)}, {} /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, iowarrior_ids); @@ -361,6 +374,7 @@ static ssize_t iowarrior_write(struct file *file, } switch (dev->product_id) { case USB_DEVICE_ID_CODEMERCS_IOW24: + case USB_DEVICE_ID_CODEMERCS_IOW24SAG: case USB_DEVICE_ID_CODEMERCS_IOWPV1: case USB_DEVICE_ID_CODEMERCS_IOWPV2: case USB_DEVICE_ID_CODEMERCS_IOW40: @@ -375,6 +389,10 @@ static ssize_t iowarrior_write(struct file *file, goto exit; break; case USB_DEVICE_ID_CODEMERCS_IOW56: + case USB_DEVICE_ID_CODEMERCS_IOW56AM: + case USB_DEVICE_ID_CODEMERCS_IOW28: + case USB_DEVICE_ID_CODEMERCS_IOW28L: + case USB_DEVICE_ID_CODEMERCS_IOW100: /* The IOW56 uses asynchronous IO and more urbs */ if (atomic_read(&dev->write_busy) == MAX_WRITES_IN_FLIGHT) { /* Wait until we are below the limit for submitted urbs */ @@ -499,6 +517,7 @@ static long iowarrior_ioctl(struct file *file, unsigned int cmd, switch (cmd) { case IOW_WRITE: if (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW24 || + dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW24SAG || dev->product_id == USB_DEVICE_ID_CODEMERCS_IOWPV1 || dev->product_id == USB_DEVICE_ID_CODEMERCS_IOWPV2 || dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW40) { @@ -782,7 +801,11 @@ static int iowarrior_probe(struct usb_interface *interface, goto error; } - if (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW56) { + if ((dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW56) || + (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW56AM) || + (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW28) || + (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW28L) || + (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW100)) { res = usb_find_last_int_out_endpoint(iface_desc, &dev->int_out_endpoint); if (res) { @@ -795,7 +818,11 @@ static int iowarrior_probe(struct usb_interface *interface, /* we have to check the report_size often, so remember it in the endianness suitable for our machine */ dev->report_size = usb_endpoint_maxp(dev->int_in_endpoint); if ((dev->interface->cur_altsetting->desc.bInterfaceNumber == 0) && - (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW56)) + ((dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW56) || + (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW56AM) || + (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW28) || + (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW28L) || + (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW100))) /* IOWarrior56 has wMaxPacketSize different from report size */ dev->report_size = 7; diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index b7d56272f9d175065e32fbb784d425714d9eb8fa..b6b4f99a399c9a4e425116cab9e773c42ae1c10c 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1842,6 +1842,9 @@ static const struct attribute_group musb_attr_group = { #define MUSB_QUIRK_B_INVALID_VBUS_91 (MUSB_DEVCTL_BDEVICE | \ (2 << MUSB_DEVCTL_VBUS_SHIFT) | \ MUSB_DEVCTL_SESSION) +#define MUSB_QUIRK_B_DISCONNECT_99 (MUSB_DEVCTL_BDEVICE | \ + (3 << MUSB_DEVCTL_VBUS_SHIFT) | \ + MUSB_DEVCTL_SESSION) #define MUSB_QUIRK_A_DISCONNECT_19 ((3 << MUSB_DEVCTL_VBUS_SHIFT) | \ MUSB_DEVCTL_SESSION) @@ -1864,6 +1867,11 @@ static void musb_pm_runtime_check_session(struct musb *musb) s = MUSB_DEVCTL_FSDEV | MUSB_DEVCTL_LSDEV | MUSB_DEVCTL_HR; switch (devctl & ~s) { + case MUSB_QUIRK_B_DISCONNECT_99: + musb_dbg(musb, "Poll devctl in case of suspend after disconnect\n"); + schedule_delayed_work(&musb->irq_work, + msecs_to_jiffies(1000)); + break; case MUSB_QUIRK_B_INVALID_VBUS_91: if (musb->quirk_retries && !musb->flush_irq_work) { musb_dbg(musb, @@ -2316,6 +2324,9 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) musb_disable_interrupts(musb); musb_writeb(musb->mregs, MUSB_DEVCTL, 0); + /* MUSB_POWER_SOFTCONN might be already set, JZ4740 does this. */ + musb_writeb(musb->mregs, MUSB_POWER, 0); + /* Init IRQ workqueue before request_irq */ INIT_DELAYED_WORK(&musb->irq_work, musb_irq_work); INIT_DELAYED_WORK(&musb->deassert_reset_work, musb_deassert_reset); diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c index 5fc6825745f21bd6246d71b99fd98d7313396f01..2d3751d885b429cb008e46dbfab5a295b815b794 100644 --- a/drivers/usb/musb/musbhsdma.c +++ b/drivers/usb/musb/musbhsdma.c @@ -425,7 +425,7 @@ struct dma_controller *musbhs_dma_controller_create(struct musb *musb, controller->controller.channel_abort = dma_channel_abort; if (request_irq(irq, dma_controller_irq, 0, - dev_name(musb->controller), &controller->controller)) { + dev_name(musb->controller), controller)) { dev_err(dev, "request_irq %d failed!\n", irq); musb_dma_controller_destroy(&controller->controller); diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index b1dd81fb5f55e3e9fbe249bb7cc058cc5e5a2e85..24e622c056383dd58b6ce2e9da89d5e16fc47b0b 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -361,8 +361,6 @@ static const struct musb_platform_ops omap2430_ops = { .init = omap2430_musb_init, .exit = omap2430_musb_exit, - .set_vbus = omap2430_musb_set_vbus, - .enable = omap2430_musb_enable, .disable = omap2430_musb_disable, diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index a86bac4489f45b96e2785ee5b70c337732b3cc6e..6692263ae9d4730be6fdb0e24f39090023c6d2fb 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -248,7 +248,7 @@ static void *usbpd_ipc_log; #define PD_MAX_DATA_OBJ 7 #define PD_SRC_CAP_EXT_DB_LEN 24 -#define PD_STATUS_DB_LEN 5 +#define PD_STATUS_DB_LEN 6 #define PD_BATTERY_CAP_DB_LEN 9 #define PD_MAX_EXT_MSG_LEN 260 @@ -1377,10 +1377,12 @@ int usbpd_send_svdm(struct usbpd *pd, u16 svid, u8 cmd, enum usbpd_svdm_cmd_type cmd_type, int obj_pos, const u32 *vdos, int num_vdos) { - u32 svdm_hdr = SVDM_HDR(svid, 0, obj_pos, cmd_type, cmd); + u32 svdm_hdr = SVDM_HDR(svid, pd->spec_rev == USBPD_REV_30 ? 1 : 0, + obj_pos, cmd_type, cmd); - usbpd_dbg(&pd->dev, "VDM tx: svid:%x cmd:%x cmd_type:%x svdm_hdr:%x\n", - svid, cmd, cmd_type, svdm_hdr); + usbpd_dbg(&pd->dev, "VDM tx: svid:%04x ver:%d obj_pos:%d cmd:%x cmd_type:%x svdm_hdr:%x\n", + svid, pd->spec_rev == USBPD_REV_30 ? 1 : 0, obj_pos, + cmd, cmd_type, svdm_hdr); return usbpd_send_vdm(pd, svdm_hdr, vdos, num_vdos); } @@ -1547,7 +1549,7 @@ static void handle_vdm_rx(struct usbpd *pd, struct rx_msg *rx_msg) ktime_t recvd_time = ktime_get(); usbpd_dbg(&pd->dev, - "VDM rx: svid:%x cmd:%x cmd_type:%x vdm_hdr:%x has_dp: %s\n", + "VDM rx: svid:%04x cmd:%x cmd_type:%x vdm_hdr:%x has_dp: %s\n", svid, cmd, cmd_type, vdm_hdr, pd->has_dp ? "true" : "false"); @@ -1574,11 +1576,9 @@ static void handle_vdm_rx(struct usbpd *pd, struct rx_msg *rx_msg) return; } - if (SVDM_HDR_VER(vdm_hdr) > 1) { - usbpd_dbg(&pd->dev, "Discarding SVDM with incorrect version:%d\n", + if (SVDM_HDR_VER(vdm_hdr) > 1) + usbpd_dbg(&pd->dev, "Received SVDM with unsupported version:%d\n", SVDM_HDR_VER(vdm_hdr)); - return; - } if (cmd_type != SVDM_CMD_TYPE_INITIATOR && pd->current_state != PE_SRC_STARTUP_WAIT_FOR_VDM_RESP) @@ -2984,13 +2984,15 @@ static bool handle_ext_snk_ready(struct usbpd *pd, struct rx_msg *rx_msg) complete(&pd->is_ready); break; case MSG_STATUS: - if (rx_msg->data_len != PD_STATUS_DB_LEN) { - usbpd_err(&pd->dev, "Invalid status db\n"); - break; - } + if (rx_msg->data_len > PD_STATUS_DB_LEN) + usbpd_err(&pd->dev, "Invalid status db length:%d\n", + rx_msg->data_len); + + memset(&pd->status_db, 0, sizeof(pd->status_db)); memcpy(&pd->status_db, rx_msg->payload, - sizeof(pd->status_db)); + min((size_t)rx_msg->data_len, sizeof(pd->status_db))); kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE); + complete(&pd->is_ready); break; case MSG_BATTERY_CAPABILITIES: if (rx_msg->data_len != PD_BATTERY_CAP_DB_LEN) { @@ -3985,9 +3987,9 @@ static int usbpd_uevent(struct device *dev, struct kobj_uevent_env *env) "explicit" : "implicit"); add_uevent_var(env, "ALT_MODE=%d", pd->vdm_state == MODE_ENTERED); - add_uevent_var(env, "SDB=%02x %02x %02x %02x %02x", pd->status_db[0], - pd->status_db[1], pd->status_db[2], pd->status_db[3], - pd->status_db[4]); + add_uevent_var(env, "SDB=%02x %02x %02x %02x %02x %02x", + pd->status_db[0], pd->status_db[1], pd->status_db[2], + pd->status_db[3], pd->status_db[4], pd->status_db[5]); return 0; } diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index f87c991b179c0d3508fddc63114ab0d047bbf2c8..13d33001038e1c15339f97c1bb1d5995d797c985 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -20,7 +20,7 @@ config AB8500_USB in host mode, low speed. config FSL_USB2_OTG - bool "Freescale USB OTG Transceiver Driver" + tristate "Freescale USB OTG Transceiver Driver" depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_OTG_FSM=y && PM depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y' select USB_PHY diff --git a/drivers/usb/phy/phy-msm-qusb.c b/drivers/usb/phy/phy-msm-qusb.c index 8e73f4e196fbbd49575fd269b43b6194d37e65fa..8f1038b3c66be094722aac54c392f12e14da3f68 100644 --- a/drivers/usb/phy/phy-msm-qusb.c +++ b/drivers/usb/phy/phy-msm-qusb.c @@ -14,6 +14,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -181,6 +184,11 @@ struct qusb_phy { enum port_state port_state; unsigned int dcd_timeout; + int notifier_gpio; + bool is_gpio_active_low; + int notifier_irq; + bool is_port_valid; + /* debugfs entries */ struct dentry *root; u8 tune1; @@ -1014,6 +1022,61 @@ static void qusb_phy_create_debugfs(struct qusb_phy *qphy) debugfs_create_x8("tune5", 0644, qphy->root, &qphy->tune5); } +int qphy_get_notifier_gpio_state(struct qusb_phy *qphy) +{ + int value; + + if (gpio_is_valid(qphy->notifier_gpio)) { + value = gpio_get_value(qphy->notifier_gpio); + return qphy->is_gpio_active_low ? !value : value; + } + + return 1; +} + +static irqreturn_t qphy_notifier_irq_handler(int irq, void *_qphy) +{ + struct qusb_phy *qphy = (struct qusb_phy *)_qphy; + + if (gpio_is_valid(qphy->notifier_gpio)) { + qphy->is_port_valid = qphy_get_notifier_gpio_state(qphy); + + dev_dbg(qphy->phy.dev, "Got notification: %d\n", + qphy->is_port_valid); + queue_delayed_work(system_freezable_wq, + &qphy->port_det_w, 0); + } + + return IRQ_HANDLED; +} + +static int qphy_register_notifier_interrupt(struct qusb_phy *qphy, + struct platform_device *pdev) +{ + int ret = 0; + + qphy->notifier_irq = platform_get_irq_byname(pdev, "notifier_irq"); + if (qphy->notifier_irq < 0) { + dev_err(qphy->phy.dev, "failed to get notifier irq\n"); + return qphy->notifier_irq; + } + + ret = devm_request_threaded_irq(qphy->phy.dev, qphy->notifier_irq, NULL, + qphy_notifier_irq_handler, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | IRQF_ONESHOT | + IRQF_SHARED, + pdev->name, qphy); + if (ret < 0) { + dev_err(qphy->phy.dev, "failed to request handler for irq\n"); + return ret; + } + + qphy->is_port_valid = qphy_get_notifier_gpio_state(qphy); + + return ret; +} + static int qusb_phy_vbus_notifier(struct notifier_block *nb, unsigned long event, void *data) { @@ -1258,18 +1321,34 @@ static void qusb_phy_port_state_work(struct work_struct *w) port_det_w.work); unsigned long delay = 0; int status, ret; + bool vbus_active, id_state; dev_dbg(qphy->phy.dev, "state: %d\n", qphy->port_state); + /* + * The events for cable connect should be ignored if + * the is_port_valid set to false to prevent the USB + * stack being brought up and voting for the regulators. + */ + qphy->is_port_valid = qphy_get_notifier_gpio_state(qphy); + if (!qphy->is_port_valid) { + dev_err(qphy->phy.dev, "Port not valid, notify disconnect\n"); + vbus_active = false; + id_state = true; + } else { + vbus_active = qphy->vbus_active; + id_state = qphy->id_state; + } + switch (qphy->port_state) { case PORT_UNKNOWN: - if (!qphy->id_state) { + if (!id_state) { qphy->port_state = PORT_HOST_MODE; qusb_phy_notify_extcon(qphy, EXTCON_USB_HOST, 1); return; } - if (qphy->vbus_active) { + if (vbus_active) { /* Enable DCD sequence */ ret = qusb_phy_prepare_chg_det(qphy); if (ret) @@ -1287,7 +1366,7 @@ static void qusb_phy_port_state_work(struct work_struct *w) qphy->port_state = PORT_UNKNOWN; break; case PORT_DCD_IN_PROGRESS: - if (!qphy->vbus_active) { + if (!vbus_active) { /* Disable PHY sequence */ qphy->port_state = PORT_DISCONNECTED; break; @@ -1309,7 +1388,7 @@ static void qusb_phy_port_state_work(struct work_struct *w) } break; case PORT_PRIMARY_IN_PROGRESS: - if (!qphy->vbus_active) { + if (!vbus_active) { qphy->port_state = PORT_DISCONNECTED; break; } @@ -1328,7 +1407,7 @@ static void qusb_phy_port_state_work(struct work_struct *w) } break; case PORT_SECONDARY_IN_PROGRESS: - if (!qphy->vbus_active) { + if (!vbus_active) { qphy->port_state = PORT_DISCONNECTED; break; } @@ -1350,14 +1429,14 @@ static void qusb_phy_port_state_work(struct work_struct *w) * during detection. */ case PORT_CHG_DET_DONE: - if (!qphy->vbus_active) { + if (!vbus_active) { qphy->port_state = PORT_UNKNOWN; qusb_phy_notify_extcon(qphy, EXTCON_USB, 0); } return; case PORT_HOST_MODE: - if (qphy->id_state) { + if (id_state) { qphy->port_state = PORT_UNKNOWN; qusb_phy_notify_extcon(qphy, EXTCON_USB_HOST, 0); } @@ -1417,6 +1496,7 @@ static int qusb_phy_probe(struct platform_device *pdev) const char *phy_type; bool hold_phy_reset; u32 temp; + enum of_gpio_flags flags; qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL); if (!qphy) @@ -1681,6 +1761,18 @@ static int qusb_phy_probe(struct platform_device *pdev) } + qphy->notifier_gpio = of_get_named_gpio_flags(dev->of_node, + "notifier", 0, &flags); + if (gpio_is_valid(qphy->notifier_gpio)) { + qphy->is_gpio_active_low = flags & OF_GPIO_ACTIVE_LOW; + ret = qphy_register_notifier_interrupt(qphy, pdev); + if (ret) + return ret; + } else { + qphy->notifier_gpio = 0; + qphy->is_port_valid = true; + } + ret = usb_add_phy_dev(&qphy->phy); if (ret) return ret; @@ -1744,6 +1836,26 @@ static int qusb_phy_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int qusb_phy_pm_resume(struct device *dev) +{ + struct qusb_phy *qphy = dev_get_drvdata(dev); + + /* + * The notifier is not wakeup capable, so get the state of the GPIO + * once the system wakes up. + */ + if (gpio_is_valid(qphy->notifier_gpio)) + queue_delayed_work(system_freezable_wq, &qphy->port_det_w, 0); + + return 0; +} +#endif + +static const struct dev_pm_ops qusb_phy_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(NULL, qusb_phy_pm_resume) +}; + static const struct of_device_id qusb_phy_id_table[] = { { .compatible = "qcom,qusb2phy", }, { }, @@ -1755,6 +1867,7 @@ static struct platform_driver qusb_phy_driver = { .remove = qusb_phy_remove, .driver = { .name = "msm-qusb-phy", + .pm = &qusb_phy_dev_pm_ops, .of_match_table = of_match_ptr(qusb_phy_id_table), }, }; diff --git a/drivers/usb/phy/phy-msm-snps-hs.c b/drivers/usb/phy/phy-msm-snps-hs.c index 818c5a0e2bcfa1a4ddd5dafc367e64b4dd3b8c9c..ea0a0614a754fee7ca03504e3f63a5b8f6760098 100644 --- a/drivers/usb/phy/phy-msm-snps-hs.c +++ b/drivers/usb/phy/phy-msm-snps-hs.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ #include @@ -280,6 +280,12 @@ static int msm_hsphy_enable_power(struct msm_hsphy *phy, bool on) if (ret) dev_err(phy->phy.dev, "Unable unconfig VDD:%d\n", ret); + /* Return from here based on power_enabled. If it is not set + * then return -EINVAL since either set_voltage or + * regulator_enable failed + */ + if (!phy->power_enabled) + return -EINVAL; err_vdd: phy->power_enabled = false; dev_dbg(phy->phy.dev, "HSUSB PHY's regulators are turned OFF.\n"); diff --git a/drivers/usb/phy/phy-twl6030-usb.c b/drivers/usb/phy/phy-twl6030-usb.c index 183550b63faadc8aaac5a68b1b3eb8706b511265..dade34d704198f9320da4f6894c219b9e17a9cec 100644 --- a/drivers/usb/phy/phy-twl6030-usb.c +++ b/drivers/usb/phy/phy-twl6030-usb.c @@ -400,7 +400,7 @@ static int twl6030_usb_remove(struct platform_device *pdev) { struct twl6030_usb *twl = platform_get_drvdata(pdev); - cancel_delayed_work(&twl->get_status_work); + cancel_delayed_work_sync(&twl->get_status_work); twl6030_interrupt_mask(TWL6030_USBOTG_INT_MASK, REG_INT_MSK_LINE_C); twl6030_interrupt_mask(TWL6030_USBOTG_INT_MASK, diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index 3bb1fff02bedd0e076581baabeff440e4552e3ce..955ab97b9b22ad915324132e227dad26c2732b66 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -589,9 +589,13 @@ static int ch341_tiocmget(struct tty_struct *tty) static int ch341_reset_resume(struct usb_serial *serial) { struct usb_serial_port *port = serial->port[0]; - struct ch341_private *priv = usb_get_serial_port_data(port); + struct ch341_private *priv; int ret; + priv = usb_get_serial_port_data(port); + if (!priv) + return 0; + /* reconfigure ch341 serial port after bus-reset */ ch341_configure(serial->dev, priv); diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 97eb738b058fa85463d2c46dc23f45625a67d685..5d101b022e713950576d815bbef0add84966ae9e 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -716,7 +716,7 @@ static void edge_interrupt_callback(struct urb *urb) if (txCredits) { port = edge_serial->serial->port[portNumber]; edge_port = usb_get_serial_port_data(port); - if (edge_port->open) { + if (edge_port && edge_port->open) { spin_lock_irqsave(&edge_port->ep_lock, flags); edge_port->txCredits += txCredits; @@ -1734,7 +1734,8 @@ static void edge_break(struct tty_struct *tty, int break_state) static void process_rcvd_data(struct edgeport_serial *edge_serial, unsigned char *buffer, __u16 bufferLength) { - struct device *dev = &edge_serial->serial->dev->dev; + struct usb_serial *serial = edge_serial->serial; + struct device *dev = &serial->dev->dev; struct usb_serial_port *port; struct edgeport_port *edge_port; __u16 lastBufferLength; @@ -1839,11 +1840,10 @@ static void process_rcvd_data(struct edgeport_serial *edge_serial, /* spit this data back into the tty driver if this port is open */ - if (rxLen) { - port = edge_serial->serial->port[ - edge_serial->rxPort]; + if (rxLen && edge_serial->rxPort < serial->num_ports) { + port = serial->port[edge_serial->rxPort]; edge_port = usb_get_serial_port_data(port); - if (edge_port->open) { + if (edge_port && edge_port->open) { dev_dbg(dev, "%s - Sending %d bytes to TTY for port %d\n", __func__, rxLen, edge_serial->rxPort); @@ -1851,8 +1851,8 @@ static void process_rcvd_data(struct edgeport_serial *edge_serial, rxLen); edge_port->port->icount.rx += rxLen; } - buffer += rxLen; } + buffer += rxLen; break; case EXPECT_HDR3: /* Expect 3rd byte of status header */ @@ -1887,6 +1887,8 @@ static void process_rcvd_status(struct edgeport_serial *edge_serial, __u8 code = edge_serial->rxStatusCode; /* switch the port pointer to the one being currently talked about */ + if (edge_serial->rxPort >= edge_serial->serial->num_ports) + return; port = edge_serial->serial->port[edge_serial->rxPort]; edge_port = usb_get_serial_port_data(port); if (edge_port == NULL) { diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index 7643716b52997e233990fd0217003ddb42a31e8a..badd38586b54856b17eeb77eea4bb770cc22a433 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -45,9 +45,10 @@ static int buffer_size; static int xbof = -1; static int ir_startup (struct usb_serial *serial); -static int ir_open(struct tty_struct *tty, struct usb_serial_port *port); -static int ir_prepare_write_buffer(struct usb_serial_port *port, - void *dest, size_t size); +static int ir_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count); +static int ir_write_room(struct tty_struct *tty); +static void ir_write_bulk_callback(struct urb *urb); static void ir_process_read_urb(struct urb *urb); static void ir_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios); @@ -77,8 +78,9 @@ static struct usb_serial_driver ir_device = { .num_ports = 1, .set_termios = ir_set_termios, .attach = ir_startup, - .open = ir_open, - .prepare_write_buffer = ir_prepare_write_buffer, + .write = ir_write, + .write_room = ir_write_room, + .write_bulk_callback = ir_write_bulk_callback, .process_read_urb = ir_process_read_urb, }; @@ -195,6 +197,9 @@ static int ir_startup(struct usb_serial *serial) struct usb_irda_cs_descriptor *irda_desc; int rates; + if (serial->num_bulk_in < 1 || serial->num_bulk_out < 1) + return -ENODEV; + irda_desc = irda_usb_find_class_desc(serial, 0); if (!irda_desc) { dev_err(&serial->dev->dev, @@ -251,35 +256,102 @@ static int ir_startup(struct usb_serial *serial) return 0; } -static int ir_open(struct tty_struct *tty, struct usb_serial_port *port) +static int ir_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { - int i; + struct urb *urb = NULL; + unsigned long flags; + int ret; - for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) - port->write_urbs[i]->transfer_flags = URB_ZERO_PACKET; + if (port->bulk_out_size == 0) + return -EINVAL; - /* Start reading from the device */ - return usb_serial_generic_open(tty, port); -} + if (count == 0) + return 0; -static int ir_prepare_write_buffer(struct usb_serial_port *port, - void *dest, size_t size) -{ - unsigned char *buf = dest; - int count; + count = min(count, port->bulk_out_size - 1); + + spin_lock_irqsave(&port->lock, flags); + if (__test_and_clear_bit(0, &port->write_urbs_free)) { + urb = port->write_urbs[0]; + port->tx_bytes += count; + } + spin_unlock_irqrestore(&port->lock, flags); + + if (!urb) + return 0; /* * The first byte of the packet we send to the device contains an - * inbound header which indicates an additional number of BOFs and + * outbound header which indicates an additional number of BOFs and * a baud rate change. * * See section 5.4.2.2 of the USB IrDA spec. */ - *buf = ir_xbof | ir_baud; + *(u8 *)urb->transfer_buffer = ir_xbof | ir_baud; + + memcpy(urb->transfer_buffer + 1, buf, count); + + urb->transfer_buffer_length = count + 1; + urb->transfer_flags = URB_ZERO_PACKET; + + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret) { + dev_err(&port->dev, "failed to submit write urb: %d\n", ret); + + spin_lock_irqsave(&port->lock, flags); + __set_bit(0, &port->write_urbs_free); + port->tx_bytes -= count; + spin_unlock_irqrestore(&port->lock, flags); + + return ret; + } + + return count; +} + +static void ir_write_bulk_callback(struct urb *urb) +{ + struct usb_serial_port *port = urb->context; + int status = urb->status; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + __set_bit(0, &port->write_urbs_free); + port->tx_bytes -= urb->transfer_buffer_length - 1; + spin_unlock_irqrestore(&port->lock, flags); + + switch (status) { + case 0: + break; + case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + dev_dbg(&port->dev, "write urb stopped: %d\n", status); + return; + case -EPIPE: + dev_err(&port->dev, "write urb stopped: %d\n", status); + return; + default: + dev_err(&port->dev, "nonzero write-urb status: %d\n", status); + break; + } + + usb_serial_port_softint(port); +} + +static int ir_write_room(struct tty_struct *tty) +{ + struct usb_serial_port *port = tty->driver_data; + int count = 0; + + if (port->bulk_out_size == 0) + return 0; + + if (test_bit(0, &port->write_urbs_free)) + count = port->bulk_out_size - 1; - count = kfifo_out_locked(&port->write_fifo, buf + 1, size - 1, - &port->lock); - return count + 1; + return count; } static void ir_process_read_urb(struct urb *urb) @@ -332,34 +404,34 @@ static void ir_set_termios(struct tty_struct *tty, switch (baud) { case 2400: - ir_baud = USB_IRDA_BR_2400; + ir_baud = USB_IRDA_LS_2400; break; case 9600: - ir_baud = USB_IRDA_BR_9600; + ir_baud = USB_IRDA_LS_9600; break; case 19200: - ir_baud = USB_IRDA_BR_19200; + ir_baud = USB_IRDA_LS_19200; break; case 38400: - ir_baud = USB_IRDA_BR_38400; + ir_baud = USB_IRDA_LS_38400; break; case 57600: - ir_baud = USB_IRDA_BR_57600; + ir_baud = USB_IRDA_LS_57600; break; case 115200: - ir_baud = USB_IRDA_BR_115200; + ir_baud = USB_IRDA_LS_115200; break; case 576000: - ir_baud = USB_IRDA_BR_576000; + ir_baud = USB_IRDA_LS_576000; break; case 1152000: - ir_baud = USB_IRDA_BR_1152000; + ir_baud = USB_IRDA_LS_1152000; break; case 4000000: - ir_baud = USB_IRDA_BR_4000000; + ir_baud = USB_IRDA_LS_4000000; break; default: - ir_baud = USB_IRDA_BR_9600; + ir_baud = USB_IRDA_LS_9600; baud = 9600; } diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index e66a59ef43a1cb59cfa0d41b8af8c7316ededa69..aa3dbce22cfbe857af446804f28440c691729cb7 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -1058,6 +1058,8 @@ static void usa49_glocont_callback(struct urb *urb) for (i = 0; i < serial->num_ports; ++i) { port = serial->port[i]; p_priv = usb_get_serial_port_data(port); + if (!p_priv) + continue; if (p_priv->resend_cont) { dev_dbg(&port->dev, "%s - sending setup\n", __func__); @@ -1459,6 +1461,8 @@ static void usa67_glocont_callback(struct urb *urb) for (i = 0; i < serial->num_ports; ++i) { port = serial->port[i]; p_priv = usb_get_serial_port_data(port); + if (!p_priv) + continue; if (p_priv->resend_cont) { dev_dbg(&port->dev, "%s - sending setup\n", __func__); diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index caa0746326fd52cd30734078938a9b7031b96746..5abe46d9f5782a80ac6f1395c67a8ef4c1cc9391 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -113,7 +113,7 @@ static int send_control_msg(struct usb_serial_port *port, u8 requesttype, retval = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), requesttype, USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, - 0, 0, buffer, 1, 0); + 0, 0, buffer, 1, USB_CTRL_SET_TIMEOUT); kfree(buffer); if (retval < 0) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 553adab174bf345d04180d7f25666906593a0a48..5b2e246f99dafc18607acf863891f45ef8472d3e 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -248,6 +248,7 @@ static void option_instat_callback(struct urb *urb); #define QUECTEL_PRODUCT_BG96 0x0296 #define QUECTEL_PRODUCT_EP06 0x0306 #define QUECTEL_PRODUCT_EM12 0x0512 +#define QUECTEL_PRODUCT_RM500Q 0x0800 #define CMOTECH_VENDOR_ID 0x16d8 #define CMOTECH_PRODUCT_6001 0x6001 @@ -567,6 +568,9 @@ static void option_instat_callback(struct urb *urb); /* Interface must have two endpoints */ #define NUMEP2 BIT(16) +/* Device needs ZLP */ +#define ZLP BIT(17) + static const struct usb_device_id option_ids[] = { { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) }, @@ -1101,6 +1105,11 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM12, 0xff, 0xff, 0xff), .driver_info = RSVD(1) | RSVD(2) | RSVD(3) | RSVD(4) | NUMEP2 }, { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM12, 0xff, 0, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM500Q, 0xff, 0xff, 0x30) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM500Q, 0xff, 0, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM500Q, 0xff, 0xff, 0x10), + .driver_info = ZLP }, + { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6001) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CMU_300) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6003), @@ -1198,6 +1207,8 @@ static const struct usb_device_id option_ids[] = { .driver_info = NCTRL(0) | RSVD(1) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1901, 0xff), /* Telit LN940 (MBIM) */ .driver_info = NCTRL(0) }, + { USB_DEVICE(TELIT_VENDOR_ID, 0x9010), /* Telit SBL FN980 flashing device */ + .driver_info = NCTRL(0) | ZLP }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF622, 0xff, 0xff, 0xff) }, /* ZTE WCDMA products */ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0002, 0xff, 0xff, 0xff), .driver_info = RSVD(1) }, @@ -2098,6 +2109,9 @@ static int option_attach(struct usb_serial *serial) if (!(device_flags & NCTRL(iface_desc->bInterfaceNumber))) data->use_send_setup = 1; + if (device_flags & ZLP) + data->use_zlp = 1; + spin_lock_init(&data->susp_lock); usb_set_serial_data(serial, data); diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index b61c2a9b6b1112baf2bb4a118028cf9d50a4fc60..9ae57a961027a3c3caef2f78e9cb840119c94b92 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -864,7 +864,10 @@ static void qt2_update_msr(struct usb_serial_port *port, unsigned char *ch) u8 newMSR = (u8) *ch; unsigned long flags; + /* May be called from qt2_process_read_urb() for an unbound port. */ port_priv = usb_get_serial_port_data(port); + if (!port_priv) + return; spin_lock_irqsave(&port_priv->lock, flags); port_priv->shadowMSR = newMSR; @@ -892,7 +895,10 @@ static void qt2_update_lsr(struct usb_serial_port *port, unsigned char *ch) unsigned long flags; u8 newLSR = (u8) *ch; + /* May be called from qt2_process_read_urb() for an unbound port. */ port_priv = usb_get_serial_port_data(port); + if (!port_priv) + return; if (newLSR & UART_LSR_BI) newLSR &= (u8) (UART_LSR_OE | UART_LSR_BI); diff --git a/drivers/usb/serial/usb-serial-simple.c b/drivers/usb/serial/usb-serial-simple.c index edbbb13d6de6ee39285fef25268be3d08f4e3b0e..bd23a7cb1be2bceaa8422e88bc3cdb5255dd566c 100644 --- a/drivers/usb/serial/usb-serial-simple.c +++ b/drivers/usb/serial/usb-serial-simple.c @@ -86,6 +86,8 @@ DEVICE(moto_modem, MOTO_IDS); #define MOTOROLA_TETRA_IDS() \ { USB_DEVICE(0x0cad, 0x9011) }, /* Motorola Solutions TETRA PEI */ \ { USB_DEVICE(0x0cad, 0x9012) }, /* MTP6550 */ \ + { USB_DEVICE(0x0cad, 0x9013) }, /* MTP3xxx */ \ + { USB_DEVICE(0x0cad, 0x9015) }, /* MTP85xx */ \ { USB_DEVICE(0x0cad, 0x9016) } /* TPG2200 */ DEVICE(motorola_tetra, MOTOROLA_TETRA_IDS); diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 434153790982191b693716c527177b0fadaa7f52..b1f0aa12ba39cd8c0e0da6cdb18eddf948829ea8 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -1294,6 +1294,9 @@ static int usb_serial_register(struct usb_serial_driver *driver) return -EINVAL; } + /* Prevent individual ports from being unbound. */ + driver->driver.suppress_bind_attrs = true; + usb_serial_operations_init(driver); /* Add this device to our list of devices */ diff --git a/drivers/usb/serial/usb-wwan.h b/drivers/usb/serial/usb-wwan.h index d28dab4b9effcd5d766bb76fa2e2d92de7dcd4f1..9879773fb39ea9afa54ecf66e9dde36b9ada513b 100644 --- a/drivers/usb/serial/usb-wwan.h +++ b/drivers/usb/serial/usb-wwan.h @@ -36,6 +36,7 @@ struct usb_wwan_intf_private { spinlock_t susp_lock; unsigned int suspended:1; unsigned int use_send_setup:1; + unsigned int use_zlp:1; int in_flight; unsigned int open_ports; void *private; diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c index 912472f26e4f6c5a9ad30357f7a47d9a3085a1d9..35406cb4a726e9814f717d4a1746a7d6d8e7cf6e 100644 --- a/drivers/usb/serial/usb_wwan.c +++ b/drivers/usb/serial/usb_wwan.c @@ -490,6 +490,7 @@ static struct urb *usb_wwan_setup_urb(struct usb_serial_port *port, void (*callback) (struct urb *)) { struct usb_serial *serial = port->serial; + struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial); struct urb *urb; urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */ @@ -500,6 +501,9 @@ static struct urb *usb_wwan_setup_urb(struct usb_serial_port *port, usb_sndbulkpipe(serial->dev, endpoint) | dir, buf, len, callback, ctx); + if (intfdata->use_zlp && dir == USB_DIR_OUT) + urb->transfer_flags |= URB_ZERO_PACKET; + return urb; } diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 1c6eb3a8741ee52bae713a65495a3c23f64d2a79..62ca8e29da48fa0a51abda388211afb156616a24 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -45,6 +45,7 @@ struct uas_dev_info { struct scsi_cmnd *cmnd[MAX_CMNDS]; spinlock_t lock; struct work_struct work; + struct work_struct scan_work; /* for async scanning */ }; enum { @@ -114,6 +115,17 @@ static void uas_do_work(struct work_struct *work) spin_unlock_irqrestore(&devinfo->lock, flags); } +static void uas_scan_work(struct work_struct *work) +{ + struct uas_dev_info *devinfo = + container_of(work, struct uas_dev_info, scan_work); + struct Scsi_Host *shost = usb_get_intfdata(devinfo->intf); + + dev_dbg(&devinfo->intf->dev, "starting scan\n"); + scsi_scan_host(shost); + dev_dbg(&devinfo->intf->dev, "scan complete\n"); +} + static void uas_add_work(struct uas_cmd_info *cmdinfo) { struct scsi_pointer *scp = (void *)cmdinfo; @@ -989,6 +1001,7 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) init_usb_anchor(&devinfo->data_urbs); spin_lock_init(&devinfo->lock); INIT_WORK(&devinfo->work, uas_do_work); + INIT_WORK(&devinfo->scan_work, uas_scan_work); result = uas_configure_endpoints(devinfo); if (result) @@ -1005,7 +1018,9 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) if (result) goto free_streams; - scsi_scan_host(shost); + /* Submit the delayed_work for SCSI-device scanning */ + schedule_work(&devinfo->scan_work); + return result; free_streams: @@ -1173,6 +1188,12 @@ static void uas_disconnect(struct usb_interface *intf) usb_kill_anchored_urbs(&devinfo->data_urbs); uas_zap_pending(devinfo, DID_NO_CONNECT); + /* + * Prevent SCSI scanning (if it hasn't started yet) + * or wait for the SCSI-scanning routine to stop. + */ + cancel_work_sync(&devinfo->scan_work); + scsi_remove_host(shost); uas_free_streams(devinfo); scsi_host_put(shost); diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 1cd9b6305b06042fecf75942c4f79af41747feea..1880f3e13f57633d7c401998d33f859e0423cc31 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -1258,6 +1258,12 @@ UNUSUAL_DEV( 0x090a, 0x1200, 0x0000, 0x9999, USB_SC_RBC, USB_PR_BULK, NULL, 0 ), +UNUSUAL_DEV(0x090c, 0x1000, 0x1100, 0x1100, + "Samsung", + "Flash Drive FIT", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_MAX_SECTORS_64), + /* aeb */ UNUSUAL_DEV( 0x090c, 0x1132, 0x0000, 0xffff, "Feiya", diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h index d0bdebd87ce3a90a0ab82f1a46b6ffa47ba102db..1b23741036ee8e913f6a8e508e754a94d78fb19e 100644 --- a/drivers/usb/storage/unusual_uas.h +++ b/drivers/usb/storage/unusual_uas.h @@ -87,12 +87,15 @@ UNUSUAL_DEV(0x2537, 0x1068, 0x0000, 0x9999, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_IGNORE_UAS), -/* Reported-by: Takeo Nakayama */ +/* + * Initially Reported-by: Takeo Nakayama + * UAS Ignore Reported by Steven Ellis + */ UNUSUAL_DEV(0x357d, 0x7788, 0x0000, 0x9999, "JMicron", "JMS566", USB_SC_DEVICE, USB_PR_DEVICE, NULL, - US_FL_NO_REPORT_OPCODES), + US_FL_NO_REPORT_OPCODES | US_FL_IGNORE_UAS), /* Reported-by: Hans de Goede */ UNUSUAL_DEV(0x4971, 0x1012, 0x0000, 0x9999, diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig index 00878c386dd09d26a80f7aa30186a61d42700616..8445890accdfe1137e76a5c9e36842867f5754e7 100644 --- a/drivers/usb/typec/Kconfig +++ b/drivers/usb/typec/Kconfig @@ -95,6 +95,7 @@ source "drivers/usb/typec/ucsi/Kconfig" config TYPEC_TPS6598X tristate "TI TPS6598x USB Power Delivery controller driver" depends on I2C + select REGMAP_I2C help Say Y or M here if your system has TI TPS65982 or TPS65983 USB Power Delivery controller. diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c index 76299b6ff06dae4eb29e49960d7403b66c39e3d8..74cb3c2ecb3476a7c2ea9f66574c782d6018b712 100644 --- a/drivers/usb/typec/bus.c +++ b/drivers/usb/typec/bus.c @@ -192,7 +192,7 @@ EXPORT_SYMBOL_GPL(typec_altmode_vdm); const struct typec_altmode * typec_altmode_get_partner(struct typec_altmode *adev) { - return &to_altmode(adev)->partner->adev; + return adev ? &to_altmode(adev)->partner->adev : NULL; } EXPORT_SYMBOL_GPL(typec_altmode_get_partner); diff --git a/drivers/usb/typec/bus.h b/drivers/usb/typec/bus.h index db40e61d8b72872ffd2673ad2aba78479207e136..0c9661c964732c65297e8cf74ed9631fe5e7a16c 100644 --- a/drivers/usb/typec/bus.h +++ b/drivers/usb/typec/bus.h @@ -35,4 +35,19 @@ extern const struct device_type typec_port_dev_type; #define is_typec_altmode(_dev_) (_dev_->type == &typec_altmode_dev_type) #define is_typec_port(_dev_) (_dev_->type == &typec_port_dev_type) +extern struct class typec_mux_class; + +struct typec_switch { + struct device dev; + typec_switch_set_fn_t set; +}; + +struct typec_mux { + struct device dev; + typec_mux_set_fn_t set; +}; + +#define to_typec_switch(_dev_) container_of(_dev_, struct typec_switch, dev) +#define to_typec_mux(_dev_) container_of(_dev_, struct typec_mux, dev) + #endif /* __USB_TYPEC_ALTMODE_H__ */ diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 1916ee1600b47c201203b35d7eb7664b636619dd..6fc8d44a3c2368f3d591d94d1b2cea4abe5cecca 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "bus.h" @@ -204,15 +205,32 @@ static void typec_altmode_put_partner(struct altmode *altmode) put_device(&adev->dev); } -static int __typec_port_match(struct device *dev, const void *name) +static int typec_port_fwnode_match(struct device *dev, const void *fwnode) +{ + return dev_fwnode(dev) == fwnode; +} + +static int typec_port_name_match(struct device *dev, const void *name) { return !strcmp((const char *)name, dev_name(dev)); } static void *typec_port_match(struct device_connection *con, int ep, void *data) { - return class_find_device(typec_class, NULL, con->endpoint[ep], - __typec_port_match); + struct device *dev; + + /* + * FIXME: Check does the fwnode supports the requested SVID. If it does + * we need to return ERR_PTR(-PROBE_DEFER) when there is no device. + */ + if (con->fwnode) + return class_find_device(typec_class, NULL, con->fwnode, + typec_port_fwnode_match); + + dev = class_find_device(typec_class, NULL, con->endpoint[ep], + typec_port_name_match); + + return dev ? dev : ERR_PTR(-EPROBE_DEFER); } struct typec_altmode * @@ -277,7 +295,7 @@ void typec_altmode_update_active(struct typec_altmode *adev, bool active) if (adev->active == active) return; - if (!is_typec_port(adev->dev.parent)) { + if (!is_typec_port(adev->dev.parent) && adev->dev.driver) { if (!active) module_put(adev->dev.driver->owner); else @@ -1496,11 +1514,8 @@ typec_port_register_altmode(struct typec_port *port, { struct typec_altmode *adev; struct typec_mux *mux; - char id[10]; - - sprintf(id, "id%04xm%02x", desc->svid, desc->mode); - mux = typec_mux_get(&port->dev, id); + mux = typec_mux_get(&port->dev, desc); if (IS_ERR(mux)) return ERR_CAST(mux); @@ -1594,7 +1609,7 @@ struct typec_port *typec_register_port(struct device *parent, return ERR_PTR(ret); } - port->mux = typec_mux_get(&port->dev, "typec-mux"); + port->mux = typec_mux_get(&port->dev, NULL); if (IS_ERR(port->mux)) { ret = PTR_ERR(port->mux); put_device(&port->dev); @@ -1633,13 +1648,25 @@ static int __init typec_init(void) if (ret) return ret; + ret = class_register(&typec_mux_class); + if (ret) + goto err_unregister_bus; + typec_class = class_create(THIS_MODULE, "typec"); if (IS_ERR(typec_class)) { - bus_unregister(&typec_bus); - return PTR_ERR(typec_class); + ret = PTR_ERR(typec_class); + goto err_unregister_mux_class; } return 0; + +err_unregister_mux_class: + class_unregister(&typec_mux_class); + +err_unregister_bus: + bus_unregister(&typec_bus); + + return ret; } subsys_initcall(typec_init); @@ -1648,6 +1675,7 @@ static void __exit typec_exit(void) class_destroy(typec_class); ida_destroy(&typec_index_ida); bus_unregister(&typec_bus); + class_unregister(&typec_mux_class); } module_exit(typec_exit); diff --git a/drivers/usb/typec/fusb302/fusb302.c b/drivers/usb/typec/fusb302/fusb302.c index 62a0060d39d8dc6e8b82807df2d8c698ed3b36de..c749ce486ec85f36e9b4bcb46bad3cc7df21c1cc 100644 --- a/drivers/usb/typec/fusb302/fusb302.c +++ b/drivers/usb/typec/fusb302/fusb302.c @@ -990,13 +990,17 @@ static int tcpm_set_roles(struct tcpc_dev *dev, bool attached, return ret; } -static int tcpm_start_drp_toggling(struct tcpc_dev *dev, - enum typec_cc_status cc) +static int tcpm_start_toggling(struct tcpc_dev *dev, + enum typec_port_type port_type, + enum typec_cc_status cc) { struct fusb302_chip *chip = container_of(dev, struct fusb302_chip, tcpc_dev); int ret = 0; + if (port_type != TYPEC_PORT_DRP) + return -EOPNOTSUPP; + mutex_lock(&chip->lock); ret = fusb302_set_src_current(chip, cc_src_current[cc]); if (ret < 0) { @@ -1206,7 +1210,7 @@ static void init_tcpc_dev(struct tcpc_dev *fusb302_tcpc_dev) fusb302_tcpc_dev->set_vbus = tcpm_set_vbus; fusb302_tcpc_dev->set_pd_rx = tcpm_set_pd_rx; fusb302_tcpc_dev->set_roles = tcpm_set_roles; - fusb302_tcpc_dev->start_drp_toggling = tcpm_start_drp_toggling; + fusb302_tcpc_dev->start_toggling = tcpm_start_toggling; fusb302_tcpc_dev->pd_transmit = tcpm_pd_transmit; } diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c index d990aa510fabf008a332706c34fe3f61b2929e82..57907f26f68197fb0a4582936fe2c57df2174a27 100644 --- a/drivers/usb/typec/mux.c +++ b/drivers/usb/typec/mux.c @@ -11,27 +11,51 @@ #include #include #include +#include +#include #include -static DEFINE_MUTEX(switch_lock); -static DEFINE_MUTEX(mux_lock); -static LIST_HEAD(switch_list); -static LIST_HEAD(mux_list); +#include "bus.h" + +static int name_match(struct device *dev, const void *name) +{ + return !strcmp((const char *)name, dev_name(dev)); +} + +static bool dev_name_ends_with(struct device *dev, const char *suffix) +{ + const char *name = dev_name(dev); + const int name_len = strlen(name); + const int suffix_len = strlen(suffix); + + if (suffix_len > name_len) + return false; + + return strcmp(name + (name_len - suffix_len), suffix) == 0; +} + +static int switch_fwnode_match(struct device *dev, const void *fwnode) +{ + return dev_fwnode(dev) == fwnode && dev_name_ends_with(dev, "-switch"); +} static void *typec_switch_match(struct device_connection *con, int ep, void *data) { - struct typec_switch *sw; + struct device *dev; - list_for_each_entry(sw, &switch_list, entry) - if (!strcmp(con->endpoint[ep], dev_name(sw->dev))) - return sw; + if (con->fwnode) { + if (con->id && !fwnode_property_present(con->fwnode, con->id)) + return NULL; - /* - * We only get called if a connection was found, tell the caller to - * wait for the switch to show up. - */ - return ERR_PTR(-EPROBE_DEFER); + dev = class_find_device(&typec_mux_class, NULL, con->fwnode, + switch_fwnode_match); + } else { + dev = class_find_device(&typec_mux_class, NULL, + con->endpoint[ep], name_match); + } + + return dev ? to_typec_switch(dev) : ERR_PTR(-EPROBE_DEFER); } /** @@ -47,14 +71,10 @@ struct typec_switch *typec_switch_get(struct device *dev) { struct typec_switch *sw; - mutex_lock(&switch_lock); - sw = device_connection_find_match(dev, "typec-switch", NULL, + sw = device_connection_find_match(dev, "orientation-switch", NULL, typec_switch_match); - if (!IS_ERR_OR_NULL(sw)) { - WARN_ON(!try_module_get(sw->dev->driver->owner)); - get_device(sw->dev); - } - mutex_unlock(&switch_lock); + if (!IS_ERR_OR_NULL(sw)) + WARN_ON(!try_module_get(sw->dev.parent->driver->owner)); return sw; } @@ -69,28 +89,64 @@ EXPORT_SYMBOL_GPL(typec_switch_get); void typec_switch_put(struct typec_switch *sw) { if (!IS_ERR_OR_NULL(sw)) { - module_put(sw->dev->driver->owner); - put_device(sw->dev); + module_put(sw->dev.parent->driver->owner); + put_device(&sw->dev); } } EXPORT_SYMBOL_GPL(typec_switch_put); +static void typec_switch_release(struct device *dev) +{ + kfree(to_typec_switch(dev)); +} + +static const struct device_type typec_switch_dev_type = { + .name = "orientation_switch", + .release = typec_switch_release, +}; + /** * typec_switch_register - Register USB Type-C orientation switch - * @sw: USB Type-C orientation switch + * @parent: Parent device + * @desc: Orientation switch description * * This function registers a switch that can be used for routing the correct * data pairs depending on the cable plug orientation from the USB Type-C * connector to the USB controllers. USB Type-C plugs can be inserted * right-side-up or upside-down. */ -int typec_switch_register(struct typec_switch *sw) +struct typec_switch * +typec_switch_register(struct device *parent, + const struct typec_switch_desc *desc) { - mutex_lock(&switch_lock); - list_add_tail(&sw->entry, &switch_list); - mutex_unlock(&switch_lock); + struct typec_switch *sw; + int ret; - return 0; + if (!desc || !desc->set) + return ERR_PTR(-EINVAL); + + sw = kzalloc(sizeof(*sw), GFP_KERNEL); + if (!sw) + return ERR_PTR(-ENOMEM); + + sw->set = desc->set; + + device_initialize(&sw->dev); + sw->dev.parent = parent; + sw->dev.fwnode = desc->fwnode; + sw->dev.class = &typec_mux_class; + sw->dev.type = &typec_switch_dev_type; + sw->dev.driver_data = desc->drvdata; + dev_set_name(&sw->dev, "%s-switch", dev_name(parent)); + + ret = device_add(&sw->dev); + if (ret) { + dev_err(parent, "failed to register switch (%d)\n", ret); + put_device(&sw->dev); + return ERR_PTR(ret); + } + + return sw; } EXPORT_SYMBOL_GPL(typec_switch_register); @@ -102,50 +158,113 @@ EXPORT_SYMBOL_GPL(typec_switch_register); */ void typec_switch_unregister(struct typec_switch *sw) { - mutex_lock(&switch_lock); - list_del(&sw->entry); - mutex_unlock(&switch_lock); + if (!IS_ERR_OR_NULL(sw)) + device_unregister(&sw->dev); } EXPORT_SYMBOL_GPL(typec_switch_unregister); +void typec_switch_set_drvdata(struct typec_switch *sw, void *data) +{ + dev_set_drvdata(&sw->dev, data); +} +EXPORT_SYMBOL_GPL(typec_switch_set_drvdata); + +void *typec_switch_get_drvdata(struct typec_switch *sw) +{ + return dev_get_drvdata(&sw->dev); +} +EXPORT_SYMBOL_GPL(typec_switch_get_drvdata); + /* ------------------------------------------------------------------------- */ +static int mux_fwnode_match(struct device *dev, const void *fwnode) +{ + return dev_fwnode(dev) == fwnode && dev_name_ends_with(dev, "-mux"); +} + static void *typec_mux_match(struct device_connection *con, int ep, void *data) { - struct typec_mux *mux; + const struct typec_altmode_desc *desc = data; + struct device *dev; + bool match; + int nval; + u16 *val; + int i; - list_for_each_entry(mux, &mux_list, entry) - if (!strcmp(con->endpoint[ep], dev_name(mux->dev))) - return mux; + if (!con->fwnode) { + dev = class_find_device(&typec_mux_class, NULL, + con->endpoint[ep], name_match); + + return dev ? to_typec_switch(dev) : ERR_PTR(-EPROBE_DEFER); + } /* - * We only get called if a connection was found, tell the caller to - * wait for the switch to show up. + * Check has the identifier already been "consumed". If it + * has, no need to do any extra connection identification. */ - return ERR_PTR(-EPROBE_DEFER); + match = !con->id; + if (match) + goto find_mux; + + /* Accessory Mode muxes */ + if (!desc) { + match = fwnode_property_present(con->fwnode, "accessory"); + if (match) + goto find_mux; + return NULL; + } + + /* Alternate Mode muxes */ + nval = fwnode_property_count_u16(con->fwnode, "svid"); + if (nval <= 0) + return NULL; + + val = kcalloc(nval, sizeof(*val), GFP_KERNEL); + if (!val) + return ERR_PTR(-ENOMEM); + + nval = fwnode_property_read_u16_array(con->fwnode, "svid", val, nval); + if (nval < 0) { + kfree(val); + return ERR_PTR(nval); + } + + for (i = 0; i < nval; i++) { + match = val[i] == desc->svid; + if (match) { + kfree(val); + goto find_mux; + } + } + kfree(val); + return NULL; + +find_mux: + dev = class_find_device(&typec_mux_class, NULL, con->fwnode, + mux_fwnode_match); + + return dev ? to_typec_switch(dev) : ERR_PTR(-EPROBE_DEFER); } /** * typec_mux_get - Find USB Type-C Multiplexer * @dev: The caller device - * @name: Mux identifier + * @desc: Alt Mode description * * Finds a mux linked to the caller. This function is primarily meant for the * Type-C drivers. Returns a reference to the mux on success, NULL if no * matching connection was found, or ERR_PTR(-EPROBE_DEFER) when a connection * was found but the mux has not been enumerated yet. */ -struct typec_mux *typec_mux_get(struct device *dev, const char *name) +struct typec_mux *typec_mux_get(struct device *dev, + const struct typec_altmode_desc *desc) { struct typec_mux *mux; - mutex_lock(&mux_lock); - mux = device_connection_find_match(dev, name, NULL, typec_mux_match); - if (!IS_ERR_OR_NULL(mux)) { - WARN_ON(!try_module_get(mux->dev->driver->owner)); - get_device(mux->dev); - } - mutex_unlock(&mux_lock); + mux = device_connection_find_match(dev, "mode-switch", (void *)desc, + typec_mux_match); + if (!IS_ERR_OR_NULL(mux)) + WARN_ON(!try_module_get(mux->dev.parent->driver->owner)); return mux; } @@ -160,28 +279,63 @@ EXPORT_SYMBOL_GPL(typec_mux_get); void typec_mux_put(struct typec_mux *mux) { if (!IS_ERR_OR_NULL(mux)) { - module_put(mux->dev->driver->owner); - put_device(mux->dev); + module_put(mux->dev.parent->driver->owner); + put_device(&mux->dev); } } EXPORT_SYMBOL_GPL(typec_mux_put); +static void typec_mux_release(struct device *dev) +{ + kfree(to_typec_mux(dev)); +} + +static const struct device_type typec_mux_dev_type = { + .name = "mode_switch", + .release = typec_mux_release, +}; + /** * typec_mux_register - Register Multiplexer routing USB Type-C pins - * @mux: USB Type-C Connector Multiplexer/DeMultiplexer + * @parent: Parent device + * @desc: Multiplexer description * * USB Type-C connectors can be used for alternate modes of operation besides * USB when Accessory/Alternate Modes are supported. With some of those modes, * the pins on the connector need to be reconfigured. This function registers * multiplexer switches routing the pins on the connector. */ -int typec_mux_register(struct typec_mux *mux) +struct typec_mux * +typec_mux_register(struct device *parent, const struct typec_mux_desc *desc) { - mutex_lock(&mux_lock); - list_add_tail(&mux->entry, &mux_list); - mutex_unlock(&mux_lock); + struct typec_mux *mux; + int ret; + + if (!desc || !desc->set) + return ERR_PTR(-EINVAL); + + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + return ERR_PTR(-ENOMEM); + + mux->set = desc->set; + + device_initialize(&mux->dev); + mux->dev.parent = parent; + mux->dev.fwnode = desc->fwnode; + mux->dev.class = &typec_mux_class; + mux->dev.type = &typec_mux_dev_type; + mux->dev.driver_data = desc->drvdata; + dev_set_name(&mux->dev, "%s-mux", dev_name(parent)); + + ret = device_add(&mux->dev); + if (ret) { + dev_err(parent, "failed to register mux (%d)\n", ret); + put_device(&mux->dev); + return ERR_PTR(ret); + } - return 0; + return mux; } EXPORT_SYMBOL_GPL(typec_mux_register); @@ -193,8 +347,24 @@ EXPORT_SYMBOL_GPL(typec_mux_register); */ void typec_mux_unregister(struct typec_mux *mux) { - mutex_lock(&mux_lock); - list_del(&mux->entry); - mutex_unlock(&mux_lock); + if (!IS_ERR_OR_NULL(mux)) + device_unregister(&mux->dev); } EXPORT_SYMBOL_GPL(typec_mux_unregister); + +void typec_mux_set_drvdata(struct typec_mux *mux, void *data) +{ + dev_set_drvdata(&mux->dev, data); +} +EXPORT_SYMBOL_GPL(typec_mux_set_drvdata); + +void *typec_mux_get_drvdata(struct typec_mux *mux) +{ + return dev_get_drvdata(&mux->dev); +} +EXPORT_SYMBOL_GPL(typec_mux_get_drvdata); + +struct class typec_mux_class = { + .name = "typec_mux", + .owner = THIS_MODULE, +}; diff --git a/drivers/usb/typec/mux/pi3usb30532.c b/drivers/usb/typec/mux/pi3usb30532.c index 64eb5983e17a4918d5c2b40b760d66820816b48b..531961154eb91a97b04ba3abb41124845e03a6ed 100644 --- a/drivers/usb/typec/mux/pi3usb30532.c +++ b/drivers/usb/typec/mux/pi3usb30532.c @@ -23,8 +23,8 @@ struct pi3usb30532 { struct i2c_client *client; struct mutex lock; /* protects the cached conf register */ - struct typec_switch sw; - struct typec_mux mux; + struct typec_switch *sw; + struct typec_mux *mux; u8 conf; }; @@ -48,7 +48,7 @@ static int pi3usb30532_set_conf(struct pi3usb30532 *pi, u8 new_conf) static int pi3usb30532_sw_set(struct typec_switch *sw, enum typec_orientation orientation) { - struct pi3usb30532 *pi = container_of(sw, struct pi3usb30532, sw); + struct pi3usb30532 *pi = typec_switch_get_drvdata(sw); u8 new_conf; int ret; @@ -75,7 +75,7 @@ static int pi3usb30532_sw_set(struct typec_switch *sw, static int pi3usb30532_mux_set(struct typec_mux *mux, int state) { - struct pi3usb30532 *pi = container_of(mux, struct pi3usb30532, mux); + struct pi3usb30532 *pi = typec_mux_get_drvdata(mux); u8 new_conf; int ret; @@ -112,6 +112,8 @@ static int pi3usb30532_mux_set(struct typec_mux *mux, int state) static int pi3usb30532_probe(struct i2c_client *client) { struct device *dev = &client->dev; + struct typec_switch_desc sw_desc; + struct typec_mux_desc mux_desc; struct pi3usb30532 *pi; int ret; @@ -120,10 +122,6 @@ static int pi3usb30532_probe(struct i2c_client *client) return -ENOMEM; pi->client = client; - pi->sw.dev = dev; - pi->sw.set = pi3usb30532_sw_set; - pi->mux.dev = dev; - pi->mux.set = pi3usb30532_mux_set; mutex_init(&pi->lock); ret = i2c_smbus_read_byte_data(client, PI3USB30532_CONF); @@ -133,17 +131,27 @@ static int pi3usb30532_probe(struct i2c_client *client) } pi->conf = ret; - ret = typec_switch_register(&pi->sw); - if (ret) { - dev_err(dev, "Error registering typec switch: %d\n", ret); - return ret; + sw_desc.drvdata = pi; + sw_desc.fwnode = dev->fwnode; + sw_desc.set = pi3usb30532_sw_set; + + pi->sw = typec_switch_register(dev, &sw_desc); + if (IS_ERR(pi->sw)) { + dev_err(dev, "Error registering typec switch: %ld\n", + PTR_ERR(pi->sw)); + return PTR_ERR(pi->sw); } - ret = typec_mux_register(&pi->mux); - if (ret) { - typec_switch_unregister(&pi->sw); - dev_err(dev, "Error registering typec mux: %d\n", ret); - return ret; + mux_desc.drvdata = pi; + mux_desc.fwnode = dev->fwnode; + mux_desc.set = pi3usb30532_mux_set; + + pi->mux = typec_mux_register(dev, &mux_desc); + if (IS_ERR(pi->mux)) { + typec_switch_unregister(pi->sw); + dev_err(dev, "Error registering typec mux: %ld\n", + PTR_ERR(pi->mux)); + return PTR_ERR(pi->mux); } i2c_set_clientdata(client, pi); @@ -154,8 +162,8 @@ static int pi3usb30532_remove(struct i2c_client *client) { struct pi3usb30532 *pi = i2c_get_clientdata(client); - typec_mux_unregister(&pi->mux); - typec_switch_unregister(&pi->sw); + typec_mux_unregister(pi->mux); + typec_switch_unregister(pi->sw); return 0; } diff --git a/drivers/usb/typec/tcpci.c b/drivers/usb/typec/tcpci.c index ac6b418b15f1f1d1baeb3448ee0d02a0a851e073..dfae41fe133109b31edd744362a4fb3636019ca3 100644 --- a/drivers/usb/typec/tcpci.c +++ b/drivers/usb/typec/tcpci.c @@ -100,13 +100,17 @@ static int tcpci_set_cc(struct tcpc_dev *tcpc, enum typec_cc_status cc) return 0; } -static int tcpci_start_drp_toggling(struct tcpc_dev *tcpc, - enum typec_cc_status cc) +static int tcpci_start_toggling(struct tcpc_dev *tcpc, + enum typec_port_type port_type, + enum typec_cc_status cc) { int ret; struct tcpci *tcpci = tcpc_to_tcpci(tcpc); unsigned int reg = TCPC_ROLE_CTRL_DRP; + if (port_type != TYPEC_PORT_DRP) + return -EOPNOTSUPP; + /* Handle vendor drp toggling */ if (tcpci->data->start_drp_toggling) { ret = tcpci->data->start_drp_toggling(tcpci, tcpci->data, cc); @@ -511,7 +515,7 @@ struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data) tcpci->tcpc.get_cc = tcpci_get_cc; tcpci->tcpc.set_polarity = tcpci_set_polarity; tcpci->tcpc.set_vconn = tcpci_set_vconn; - tcpci->tcpc.start_drp_toggling = tcpci_start_drp_toggling; + tcpci->tcpc.start_toggling = tcpci_start_toggling; tcpci->tcpc.set_pd_rx = tcpci_set_pd_rx; tcpci->tcpc.set_roles = tcpci_set_roles; @@ -577,6 +581,12 @@ static int tcpci_probe(struct i2c_client *client, static int tcpci_remove(struct i2c_client *client) { struct tcpci_chip *chip = i2c_get_clientdata(client); + int err; + + /* Disable chip interrupts before unregistering port */ + err = tcpci_write16(chip->tcpci, TCPC_ALERT_MASK, 0); + if (err < 0) + return err; tcpci_unregister_port(chip->tcpci); diff --git a/drivers/usb/typec/tcpm.c b/drivers/usb/typec/tcpm.c index 39cf19001239313f5adb8ada3c8d0362eb342454..29d72e9b0f017bb51a8a03184170cdc6ed7ed0c0 100644 --- a/drivers/usb/typec/tcpm.c +++ b/drivers/usb/typec/tcpm.c @@ -31,7 +31,7 @@ #define FOREACH_STATE(S) \ S(INVALID_STATE), \ - S(DRP_TOGGLING), \ + S(TOGGLING), \ S(SRC_UNATTACHED), \ S(SRC_ATTACH_WAIT), \ S(SRC_ATTACHED), \ @@ -473,7 +473,7 @@ static void tcpm_log(struct tcpm_port *port, const char *fmt, ...) /* Do not log while disconnected and unattached */ if (tcpm_port_is_disconnected(port) && (port->state == SRC_UNATTACHED || port->state == SNK_UNATTACHED || - port->state == DRP_TOGGLING)) + port->state == TOGGLING)) return; va_start(args, fmt); @@ -2561,20 +2561,16 @@ static int tcpm_set_charge(struct tcpm_port *port, bool charge) return 0; } -static bool tcpm_start_drp_toggling(struct tcpm_port *port, - enum typec_cc_status cc) +static bool tcpm_start_toggling(struct tcpm_port *port, enum typec_cc_status cc) { int ret; - if (port->tcpc->start_drp_toggling && - port->port_type == TYPEC_PORT_DRP) { - tcpm_log_force(port, "Start DRP toggling"); - ret = port->tcpc->start_drp_toggling(port->tcpc, cc); - if (!ret) - return true; - } + if (!port->tcpc->start_toggling) + return false; - return false; + tcpm_log_force(port, "Start toggling"); + ret = port->tcpc->start_toggling(port->tcpc, port->port_type, cc); + return ret == 0; } static void tcpm_set_cc(struct tcpm_port *port, enum typec_cc_status cc) @@ -2868,15 +2864,15 @@ static void run_state_machine(struct tcpm_port *port) port->enter_state = port->state; switch (port->state) { - case DRP_TOGGLING: + case TOGGLING: break; /* SRC states */ case SRC_UNATTACHED: if (!port->non_pd_role_swap) tcpm_swap_complete(port, -ENOTCONN); tcpm_src_detach(port); - if (tcpm_start_drp_toggling(port, tcpm_rp_cc(port))) { - tcpm_set_state(port, DRP_TOGGLING, 0); + if (tcpm_start_toggling(port, tcpm_rp_cc(port))) { + tcpm_set_state(port, TOGGLING, 0); break; } tcpm_set_cc(port, tcpm_rp_cc(port)); @@ -3074,8 +3070,8 @@ static void run_state_machine(struct tcpm_port *port) tcpm_swap_complete(port, -ENOTCONN); tcpm_pps_complete(port, -ENOTCONN); tcpm_snk_detach(port); - if (tcpm_start_drp_toggling(port, TYPEC_CC_RD)) { - tcpm_set_state(port, DRP_TOGGLING, 0); + if (tcpm_start_toggling(port, TYPEC_CC_RD)) { + tcpm_set_state(port, TOGGLING, 0); break; } tcpm_set_cc(port, TYPEC_CC_RD); @@ -3642,7 +3638,7 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1, : "connected"); switch (port->state) { - case DRP_TOGGLING: + case TOGGLING: if (tcpm_port_is_debug(port) || tcpm_port_is_audio(port) || tcpm_port_is_source(port)) tcpm_set_state(port, SRC_ATTACH_WAIT, 0); diff --git a/drivers/usb/typec/typec_wcove.c b/drivers/usb/typec/typec_wcove.c index 6770afd4076548eeb0021eef062160013b97b1ea..f1f8f45e2f3dc8e0325fc16f4ff926c3ae6676d5 100644 --- a/drivers/usb/typec/typec_wcove.c +++ b/drivers/usb/typec/typec_wcove.c @@ -416,12 +416,16 @@ static int wcove_pd_transmit(struct tcpc_dev *tcpc, return regmap_write(wcove->regmap, USBC_TXCMD, cmd | USBC_TXCMD_START); } -static int wcove_start_drp_toggling(struct tcpc_dev *tcpc, - enum typec_cc_status cc) +static int wcove_start_toggling(struct tcpc_dev *tcpc, + enum typec_port_type port_type, + enum typec_cc_status cc) { struct wcove_typec *wcove = tcpc_to_wcove(tcpc); unsigned int usbc_ctrl; + if (port_type != TYPEC_PORT_DRP) + return -EOPNOTSUPP; + usbc_ctrl = USBC_CONTROL1_MODE_DRP | USBC_CONTROL1_DRPTOGGLE_RANDOM; switch (cc) { @@ -642,7 +646,7 @@ static int wcove_typec_probe(struct platform_device *pdev) wcove->tcpc.set_polarity = wcove_set_polarity; wcove->tcpc.set_vconn = wcove_set_vconn; wcove->tcpc.set_current_limit = wcove_set_current_limit; - wcove->tcpc.start_drp_toggling = wcove_start_drp_toggling; + wcove->tcpc.start_toggling = wcove_start_toggling; wcove->tcpc.set_pd_rx = wcove_set_pd_rx; wcove->tcpc.set_roles = wcove_set_roles; diff --git a/drivers/vfio/mdev/mdev_core.c b/drivers/vfio/mdev/mdev_core.c index 0212f0ee8aea7577246c01c99821e0ba12cf9373..e052f62fdea7e8151cf4fd2b3d650805161da341 100644 --- a/drivers/vfio/mdev/mdev_core.c +++ b/drivers/vfio/mdev/mdev_core.c @@ -150,10 +150,10 @@ static int mdev_device_remove_ops(struct mdev_device *mdev, bool force_remove) static int mdev_device_remove_cb(struct device *dev, void *data) { - if (!dev_is_mdev(dev)) - return 0; + if (dev_is_mdev(dev)) + mdev_device_remove(dev, true); - return mdev_device_remove(dev, data ? *(bool *)data : true); + return 0; } /* @@ -182,6 +182,7 @@ int mdev_register_device(struct device *dev, const struct mdev_parent_ops *ops) /* Check for duplicate */ parent = __find_parent_device(dev); if (parent) { + parent = NULL; ret = -EEXIST; goto add_dev_err; } @@ -240,7 +241,6 @@ EXPORT_SYMBOL(mdev_register_device); void mdev_unregister_device(struct device *dev) { struct mdev_parent *parent; - bool force_remove = true; mutex_lock(&parent_list_lock); parent = __find_parent_device(dev); @@ -254,8 +254,7 @@ void mdev_unregister_device(struct device *dev) list_del(&parent->next); class_compat_remove_link(mdev_bus_compat_class, dev, NULL); - device_for_each_child(dev, (void *)&force_remove, - mdev_device_remove_cb); + device_for_each_child(dev, NULL, mdev_device_remove_cb); parent_remove_sysfs_files(parent); diff --git a/drivers/vfio/mdev/mdev_sysfs.c b/drivers/vfio/mdev/mdev_sysfs.c index 249472f055097eebdebc087dc8d3a6caf56d541b..e7770b511d0331ee2719fe09ac2d96012326d12e 100644 --- a/drivers/vfio/mdev/mdev_sysfs.c +++ b/drivers/vfio/mdev/mdev_sysfs.c @@ -280,7 +280,7 @@ int mdev_create_sysfs_files(struct device *dev, struct mdev_type *type) void mdev_remove_sysfs_files(struct device *dev, struct mdev_type *type) { + sysfs_remove_files(&dev->kobj, mdev_device_attrs); sysfs_remove_link(&dev->kobj, "mdev_type"); sysfs_remove_link(type->devices_kobj, dev_name(dev)); - sysfs_remove_files(&dev->kobj, mdev_device_attrs); } diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c index 0a6eb53e79fbfb23bfd81d7c83ea634ac8255eb5..66783a37f450cc5df7e7935e983c0f2365b73115 100644 --- a/drivers/vfio/pci/vfio_pci.c +++ b/drivers/vfio/pci/vfio_pci.c @@ -696,6 +696,7 @@ static long vfio_pci_ioctl(void *device_data, { void __iomem *io; size_t size; + u16 orig_cmd; info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index); info.flags = 0; @@ -711,15 +712,23 @@ static long vfio_pci_ioctl(void *device_data, break; } - /* Is it really there? */ + /* + * Is it really there? Enable memory decode for + * implicit access in pci_map_rom(). + */ + pci_read_config_word(pdev, PCI_COMMAND, &orig_cmd); + pci_write_config_word(pdev, PCI_COMMAND, + orig_cmd | PCI_COMMAND_MEMORY); + io = pci_map_rom(pdev, &size); - if (!io || !size) { + if (io) { + info.flags = VFIO_REGION_INFO_FLAG_READ; + pci_unmap_rom(pdev, io); + } else { info.size = 0; - break; } - pci_unmap_rom(pdev, io); - info.flags = VFIO_REGION_INFO_FLAG_READ; + pci_write_config_word(pdev, PCI_COMMAND, orig_cmd); break; } case VFIO_PCI_VGA_REGION_INDEX: diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index 124356dc39e14589e6cd94e7e7a078073836d667..88c8c158ec25c972ca9c7da77eda26d633457a14 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -1187,10 +1187,6 @@ static int vhost_net_release(struct inode *inode, struct file *f) static struct socket *get_raw_socket(int fd) { - struct { - struct sockaddr_ll sa; - char buf[MAX_ADDR_LEN]; - } uaddr; int r; struct socket *sock = sockfd_lookup(fd, &r); @@ -1203,11 +1199,7 @@ static struct socket *get_raw_socket(int fd) goto err; } - r = sock->ops->getname(sock, (struct sockaddr *)&uaddr.sa, 0); - if (r < 0) - goto err; - - if (uaddr.sa.sll_family != AF_PACKET) { + if (sock->sk->sk_family != AF_PACKET) { r = -EPFNOSUPPORT; goto err; } diff --git a/drivers/vhost/test.c b/drivers/vhost/test.c index a9be2d8e98df74c021c90bd358fa0a08fc2371c9..55090d9f9de0d8e5d1de5d97c043095b6bc73f08 100644 --- a/drivers/vhost/test.c +++ b/drivers/vhost/test.c @@ -162,6 +162,7 @@ static int vhost_test_release(struct inode *inode, struct file *f) vhost_test_stop(n, &private); vhost_test_flush(n); + vhost_dev_stop(&n->dev); vhost_dev_cleanup(&n->dev); /* We do an extra flush before freeing memory, * since jobs can re-queue themselves. */ @@ -238,6 +239,7 @@ static long vhost_test_reset_owner(struct vhost_test *n) } vhost_test_stop(n, &priv); vhost_test_flush(n); + vhost_dev_stop(&n->dev); vhost_dev_reset_owner(&n->dev, umem); done: mutex_unlock(&n->dev.mutex); diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c index 98b6eb902df9ecdacb6dfc8ecfc19571818b9003..5cf3d1996f47cb955468805abd83da0de8ffb607 100644 --- a/drivers/vhost/vhost.c +++ b/drivers/vhost/vhost.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "vhost.h" @@ -359,7 +360,9 @@ static int vhost_worker(void *data) llist_for_each_entry_safe(work, work_next, node, node) { clear_bit(VHOST_WORK_QUEUED, &work->flags); __set_current_state(TASK_RUNNING); + kcov_remote_start_common(dev->kcov_handle); work->fn(work); + kcov_remote_stop(); if (need_resched()) schedule(); } @@ -522,6 +525,7 @@ long vhost_dev_set_owner(struct vhost_dev *dev) /* No owner, become one */ dev->mm = get_task_mm(current); + dev->kcov_handle = kcov_common_handle(); worker = kthread_create(vhost_worker, dev, "vhost-%d", current->pid); if (IS_ERR(worker)) { err = PTR_ERR(worker); @@ -547,6 +551,7 @@ long vhost_dev_set_owner(struct vhost_dev *dev) if (dev->mm) mmput(dev->mm); dev->mm = NULL; + dev->kcov_handle = 0; err_mm: return err; } @@ -658,6 +663,7 @@ void vhost_dev_cleanup(struct vhost_dev *dev) if (dev->worker) { kthread_stop(dev->worker); dev->worker = NULL; + dev->kcov_handle = 0; } if (dev->mm) mmput(dev->mm); diff --git a/drivers/vhost/vhost.h b/drivers/vhost/vhost.h index 27a78a9b8cc7dc6e21626f1134d34055ca71dfe2..7c04ca37bd46baa2c608001f2b8e7c39454c5cdb 100644 --- a/drivers/vhost/vhost.h +++ b/drivers/vhost/vhost.h @@ -173,6 +173,7 @@ struct vhost_dev { int iov_limit; int weight; int byte_weight; + u64 kcov_handle; }; bool vhost_exceeds_weight(struct vhost_virtqueue *vq, int pkts, int total_len); diff --git a/drivers/video/backlight/lm3630a_bl.c b/drivers/video/backlight/lm3630a_bl.c index 2030a6b77a09725e97bb1618b2695aea5c55f2a0..ef2553f452ca998b6df35175d4d4a84b3f13a782 100644 --- a/drivers/video/backlight/lm3630a_bl.c +++ b/drivers/video/backlight/lm3630a_bl.c @@ -201,7 +201,7 @@ static int lm3630a_bank_a_update_status(struct backlight_device *bl) LM3630A_LEDA_ENABLE, LM3630A_LEDA_ENABLE); if (ret < 0) goto out_i2c_err; - return bl->props.brightness; + return 0; out_i2c_err: dev_err(pchip->dev, "i2c failed to access\n"); @@ -278,7 +278,7 @@ static int lm3630a_bank_b_update_status(struct backlight_device *bl) LM3630A_LEDB_ENABLE, LM3630A_LEDB_ENABLE); if (ret < 0) goto out_i2c_err; - return bl->props.brightness; + return 0; out_i2c_err: dev_err(pchip->dev, "i2c failed to access REG_CTRL\n"); diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index 7ddc0930e98c696b98f42b2b703a8594f3d1af6e..3a3098d4873be7b37fb68879261dd06ced5a4579 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -199,29 +199,17 @@ int pwm_backlight_brightness_default(struct device *dev, struct platform_pwm_backlight_data *data, unsigned int period) { - unsigned int counter = 0; - unsigned int i, n; + unsigned int i; u64 retval; /* - * Count the number of bits needed to represent the period number. The - * number of bits is used to calculate the number of levels used for the - * brightness-levels table, the purpose of this calculation is have a - * pre-computed table with enough levels to get linear brightness - * perception. The period is divided by the number of bits so for a - * 8-bit PWM we have 255 / 8 = 32 brightness levels or for a 16-bit PWM - * we have 65535 / 16 = 4096 brightness levels. - * - * Note that this method is based on empirical testing on different - * devices with PWM of 8 and 16 bits of resolution. + * Once we have 4096 levels there's little point going much higher... + * neither interactive sliders nor animation benefits from having + * more values in the table. */ - n = period; - while (n) { - counter += n % 2; - n >>= 1; - } + data->max_brightness = + min((int)DIV_ROUND_UP(period, fls(period)), 4096); - data->max_brightness = DIV_ROUND_UP(period, counter); data->levels = devm_kcalloc(dev, data->max_brightness, sizeof(*data->levels), GFP_KERNEL); if (!data->levels) diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index c6b3bdbbdbc9e283d661eb960696681611575489..bfaa9ec4bc1fd12aa228d2d4c270428bfcc71d95 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -1316,6 +1316,9 @@ static int vgacon_font_get(struct vc_data *c, struct console_font *font) static int vgacon_resize(struct vc_data *c, unsigned int width, unsigned int height, unsigned int user) { + if ((width << 1) * height > vga_vram_size) + return -EINVAL; + if (width % 2 || width > screen_info.orig_video_cols || height > (screen_info.orig_video_lines * vga_default_font_height)/ c->vc_font.height) diff --git a/drivers/video/fbdev/chipsfb.c b/drivers/video/fbdev/chipsfb.c index f103665cad431c449422b87bea145934444a46e4..f9b366d1758758417d8d069518488a93e878724b 100644 --- a/drivers/video/fbdev/chipsfb.c +++ b/drivers/video/fbdev/chipsfb.c @@ -350,7 +350,7 @@ static void init_chips(struct fb_info *p, unsigned long addr) static int chipsfb_pci_init(struct pci_dev *dp, const struct pci_device_id *ent) { struct fb_info *p; - unsigned long addr, size; + unsigned long addr; unsigned short cmd; int rc = -ENODEV; @@ -362,7 +362,6 @@ static int chipsfb_pci_init(struct pci_dev *dp, const struct pci_device_id *ent) if ((dp->resource[0].flags & IORESOURCE_MEM) == 0) goto err_disable; addr = pci_resource_start(dp, 0); - size = pci_resource_len(dp, 0); if (addr == 0) goto err_disable; diff --git a/drivers/video/fbdev/pxa168fb.c b/drivers/video/fbdev/pxa168fb.c index d059d04c63acd7bc118bbc0ec02fc3cb1dfa41f8..20195d3dbf088c87195c2976f09acefb1f5895da 100644 --- a/drivers/video/fbdev/pxa168fb.c +++ b/drivers/video/fbdev/pxa168fb.c @@ -769,8 +769,8 @@ static int pxa168fb_probe(struct platform_device *pdev) failed_free_clk: clk_disable_unprepare(fbi->clk); failed_free_fbmem: - dma_free_coherent(fbi->dev, info->fix.smem_len, - info->screen_base, fbi->fb_start_dma); + dma_free_wc(fbi->dev, info->fix.smem_len, + info->screen_base, fbi->fb_start_dma); failed_free_info: kfree(info); @@ -804,7 +804,7 @@ static int pxa168fb_remove(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); - dma_free_wc(fbi->dev, PAGE_ALIGN(info->fix.smem_len), + dma_free_wc(fbi->dev, info->fix.smem_len, info->screen_base, info->fix.smem_start); clk_disable_unprepare(fbi->clk); diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig index 35897649c24f64ac65af894ed2e48734a431d625..009278b23241e00e3e15fcdab930c547260c8432 100644 --- a/drivers/virtio/Kconfig +++ b/drivers/virtio/Kconfig @@ -42,6 +42,17 @@ config VIRTIO_PCI_LEGACY If unsure, say Y. +config VIRTIO_PMEM + tristate "Support for virtio pmem driver" + depends on VIRTIO + depends on LIBNVDIMM + help + This driver provides access to virtio-pmem devices, storage devices + that are mapped into the physical address space - similar to NVDIMMs + - with a virtio-based flushing interface. + + If unsure, say Y. + config VIRTIO_BALLOON tristate "Virtio balloon driver" depends on VIRTIO diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 14ac36ca8fbd3c05408b8405aca3609ac13f3523..1afcbef397ab7d3ee0e56ffb67f51e75cdeeb08e 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -126,6 +126,8 @@ static void set_page_pfns(struct virtio_balloon *vb, { unsigned int i; + BUILD_BUG_ON(VIRTIO_BALLOON_PAGES_PER_PAGE > VIRTIO_BALLOON_ARRAY_PFNS_MAX); + /* * Set balloon pfns pointing at this page. * Note that the first pfn points at start of the page. diff --git a/drivers/virtio/virtio_input.c b/drivers/virtio/virtio_input.c index 89e14b01959bf82a6bda12fe52cc73b889b8dc84..0073d078e50ebdb804df58d85dcc71b8625051d2 100644 --- a/drivers/virtio/virtio_input.c +++ b/drivers/virtio/virtio_input.c @@ -164,12 +164,15 @@ static void virtinput_cfg_abs(struct virtio_input *vi, int abs) virtio_cread(vi->vdev, struct virtio_input_config, u.abs.flat, &fl); input_set_abs_params(vi->idev, abs, mi, ma, fu, fl); input_abs_set_res(vi->idev, abs, re); - if (abs == ABS_MT_TRACKING_ID) + if (abs == ABS_MT_TRACKING_ID) { + unsigned int slot_flags = + test_bit(INPUT_PROP_DIRECT, vi->idev->propbit) ? + INPUT_MT_DIRECT : 0; + input_mt_init_slots(vi->idev, ma, /* input max finger */ - INPUT_MT_DIRECT - | INPUT_MT_DROP_UNUSED - | INPUT_MT_TRACK); + slot_flags); + } } static int virtinput_init_vqs(struct virtio_input *vi) diff --git a/drivers/visorbus/visorchipset.c b/drivers/visorbus/visorchipset.c index ca752b8f495fa509fd8bd55bfbe9eecf68054f57..cb1eb7e05f871229dc1861f1a9236a52d26f1078 100644 --- a/drivers/visorbus/visorchipset.c +++ b/drivers/visorbus/visorchipset.c @@ -1210,14 +1210,17 @@ static void setup_crash_devices_work_queue(struct work_struct *work) { struct controlvm_message local_crash_bus_msg; struct controlvm_message local_crash_dev_msg; - struct controlvm_message msg; + struct controlvm_message msg = { + .hdr.id = CONTROLVM_CHIPSET_INIT, + .cmd.init_chipset = { + .bus_count = 23, + .switch_count = 0, + }, + }; u32 local_crash_msg_offset; u16 local_crash_msg_count; /* send init chipset msg */ - msg.hdr.id = CONTROLVM_CHIPSET_INIT; - msg.cmd.init_chipset.bus_count = 23; - msg.cmd.init_chipset.switch_count = 0; chipset_init(&msg); /* get saved message count */ if (visorchannel_read(chipset_dev->controlvm_channel, diff --git a/drivers/vme/bridges/vme_fake.c b/drivers/vme/bridges/vme_fake.c index 7d83691047f47441a8d13b14bef4e8e8aaf89c7c..685a43bdc2a1dc667543877bdcf418406bc08ea0 100644 --- a/drivers/vme/bridges/vme_fake.c +++ b/drivers/vme/bridges/vme_fake.c @@ -418,8 +418,9 @@ static void fake_lm_check(struct fake_driver *bridge, unsigned long long addr, } } -static u8 fake_vmeread8(struct fake_driver *bridge, unsigned long long addr, - u32 aspace, u32 cycle) +static noinline_for_stack u8 fake_vmeread8(struct fake_driver *bridge, + unsigned long long addr, + u32 aspace, u32 cycle) { u8 retval = 0xff; int i; @@ -450,8 +451,9 @@ static u8 fake_vmeread8(struct fake_driver *bridge, unsigned long long addr, return retval; } -static u16 fake_vmeread16(struct fake_driver *bridge, unsigned long long addr, - u32 aspace, u32 cycle) +static noinline_for_stack u16 fake_vmeread16(struct fake_driver *bridge, + unsigned long long addr, + u32 aspace, u32 cycle) { u16 retval = 0xffff; int i; @@ -482,8 +484,9 @@ static u16 fake_vmeread16(struct fake_driver *bridge, unsigned long long addr, return retval; } -static u32 fake_vmeread32(struct fake_driver *bridge, unsigned long long addr, - u32 aspace, u32 cycle) +static noinline_for_stack u32 fake_vmeread32(struct fake_driver *bridge, + unsigned long long addr, + u32 aspace, u32 cycle) { u32 retval = 0xffffffff; int i; @@ -613,8 +616,9 @@ static ssize_t fake_master_read(struct vme_master_resource *image, void *buf, return retval; } -static void fake_vmewrite8(struct fake_driver *bridge, u8 *buf, - unsigned long long addr, u32 aspace, u32 cycle) +static noinline_for_stack void fake_vmewrite8(struct fake_driver *bridge, + u8 *buf, unsigned long long addr, + u32 aspace, u32 cycle) { int i; unsigned long long start, end, offset; @@ -643,8 +647,9 @@ static void fake_vmewrite8(struct fake_driver *bridge, u8 *buf, } -static void fake_vmewrite16(struct fake_driver *bridge, u16 *buf, - unsigned long long addr, u32 aspace, u32 cycle) +static noinline_for_stack void fake_vmewrite16(struct fake_driver *bridge, + u16 *buf, unsigned long long addr, + u32 aspace, u32 cycle) { int i; unsigned long long start, end, offset; @@ -673,8 +678,9 @@ static void fake_vmewrite16(struct fake_driver *bridge, u16 *buf, } -static void fake_vmewrite32(struct fake_driver *bridge, u32 *buf, - unsigned long long addr, u32 aspace, u32 cycle) +static noinline_for_stack void fake_vmewrite32(struct fake_driver *bridge, + u32 *buf, unsigned long long addr, + u32 aspace, u32 cycle) { int i; unsigned long long start, end, offset; diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index b165c46aca741b6d10c036f0f761e0faad2fbbd6..709d4de11f40fd33209cdc65a835d5f9f1f1fed9 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -594,6 +594,7 @@ config MAX63XX_WATCHDOG config MAX77620_WATCHDOG tristate "Maxim Max77620 Watchdog Timer" depends on MFD_MAX77620 || COMPILE_TEST + select WATCHDOG_CORE help This is the driver for the Max77620 watchdog timer. Say 'Y' here to enable the watchdog timer support for diff --git a/drivers/watchdog/da9062_wdt.c b/drivers/watchdog/da9062_wdt.c index fe169d8e1fb2f2c75b4dca8d8af6e83dd462123b..7f0a8e635286060535ca3ef46c5a7f095eb74f0f 100644 --- a/drivers/watchdog/da9062_wdt.c +++ b/drivers/watchdog/da9062_wdt.c @@ -99,13 +99,6 @@ static int da9062_wdt_stop(struct watchdog_device *wdd) struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); int ret; - ret = da9062_reset_watchdog_timer(wdt); - if (ret) { - dev_err(wdt->hw->dev, "Failed to ping the watchdog (err = %d)\n", - ret); - return ret; - } - ret = regmap_update_bits(wdt->hw->regmap, DA9062AA_CONTROL_D, DA9062AA_TWDSCALE_MASK, diff --git a/drivers/watchdog/rn5t618_wdt.c b/drivers/watchdog/rn5t618_wdt.c index e60f55702ab79d111078dae1b253799dfdc4684d..d2e79cf70e774c4676e258d1f95ed4041bfa16c9 100644 --- a/drivers/watchdog/rn5t618_wdt.c +++ b/drivers/watchdog/rn5t618_wdt.c @@ -193,6 +193,7 @@ static struct platform_driver rn5t618_wdt_driver = { module_platform_driver(rn5t618_wdt_driver); +MODULE_ALIAS("platform:rn5t618-wdt"); MODULE_AUTHOR("Beniamino Galvani "); MODULE_DESCRIPTION("RN5T618 watchdog driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/watchdog/rtd119x_wdt.c b/drivers/watchdog/rtd119x_wdt.c index d001c17ddfdeeea843e208682dbe8ca7fc57c441..99caec6882d2f1cef3131df8e3b38dfa8d6c006b 100644 --- a/drivers/watchdog/rtd119x_wdt.c +++ b/drivers/watchdog/rtd119x_wdt.c @@ -135,7 +135,7 @@ static int rtd119x_wdt_probe(struct platform_device *pdev) rtd119x_wdt_set_timeout(&data->wdt_dev, data->wdt_dev.timeout); rtd119x_wdt_stop(&data->wdt_dev); - ret = devm_watchdog_register_device(&pdev->dev, &data->wdt_dev); + ret = watchdog_register_device(&data->wdt_dev); if (ret) { clk_disable_unprepare(data->clk); clk_put(data->clk); diff --git a/drivers/watchdog/sprd_wdt.c b/drivers/watchdog/sprd_wdt.c index ff9397d9638a7b4a7745084ddd2654f98c8e8c06..b6c65afd3677876eeaeb85e5ea13f7611949e021 100644 --- a/drivers/watchdog/sprd_wdt.c +++ b/drivers/watchdog/sprd_wdt.c @@ -342,10 +342,9 @@ static int sprd_wdt_probe(struct platform_device *pdev) static int __maybe_unused sprd_wdt_pm_suspend(struct device *dev) { - struct watchdog_device *wdd = dev_get_drvdata(dev); struct sprd_wdt *wdt = dev_get_drvdata(dev); - if (watchdog_active(wdd)) + if (watchdog_active(&wdt->wdd)) sprd_wdt_stop(&wdt->wdd); sprd_wdt_disable(wdt); @@ -354,7 +353,6 @@ static int __maybe_unused sprd_wdt_pm_suspend(struct device *dev) static int __maybe_unused sprd_wdt_pm_resume(struct device *dev) { - struct watchdog_device *wdd = dev_get_drvdata(dev); struct sprd_wdt *wdt = dev_get_drvdata(dev); int ret; @@ -362,7 +360,7 @@ static int __maybe_unused sprd_wdt_pm_resume(struct device *dev) if (ret) return ret; - if (watchdog_active(wdd)) { + if (watchdog_active(&wdt->wdd)) { ret = sprd_wdt_start(&wdt->wdd); if (ret) { sprd_wdt_disable(wdt); diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index eb8fa25f8eb2957951195ec2ad0e94afb22f2319..8b1f37ffb65acd7f55a11ea66190c5f3f2c283ea 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c @@ -138,6 +138,25 @@ int watchdog_init_timeout(struct watchdog_device *wdd, } EXPORT_SYMBOL_GPL(watchdog_init_timeout); +static int watchdog_reboot_notifier(struct notifier_block *nb, + unsigned long code, void *data) +{ + struct watchdog_device *wdd; + + wdd = container_of(nb, struct watchdog_device, reboot_nb); + if (code == SYS_DOWN || code == SYS_HALT) { + if (watchdog_active(wdd)) { + int ret; + + ret = wdd->ops->stop(wdd); + if (ret) + return NOTIFY_BAD; + } + } + + return NOTIFY_DONE; +} + static int watchdog_restart_notifier(struct notifier_block *nb, unsigned long action, void *data) { @@ -226,6 +245,19 @@ static int __watchdog_register_device(struct watchdog_device *wdd) } } + if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) { + wdd->reboot_nb.notifier_call = watchdog_reboot_notifier; + + ret = register_reboot_notifier(&wdd->reboot_nb); + if (ret) { + pr_err("watchdog%d: Cannot register reboot notifier (%d)\n", + wdd->id, ret); + watchdog_dev_unregister(wdd); + ida_simple_remove(&watchdog_ida, id); + return ret; + } + } + if (wdd->ops->restart) { wdd->restart_nb.notifier_call = watchdog_restart_notifier; @@ -271,6 +303,9 @@ static void __watchdog_unregister_device(struct watchdog_device *wdd) if (wdd->ops->restart) unregister_restart_handler(&wdd->restart_nb); + if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) + unregister_reboot_notifier(&wdd->reboot_nb); + watchdog_dev_unregister(wdd); ida_simple_remove(&watchdog_ida, wdd->id); } diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 4b89333e8eb4b60d38e75aa59d7a669ac99de2fc..e64aa88e99dabaf5fff7ebf0d1a07e0b1e897584 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -42,7 +42,6 @@ #include /* For handling misc devices */ #include /* For module stuff/... */ #include /* For mutexes */ -#include /* For reboot notifier */ #include /* For memory functions */ #include /* For standard types (like size_t) */ #include /* For watchdog specific items */ @@ -1048,25 +1047,6 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd) put_device(&wd_data->dev); } -static int watchdog_reboot_notifier(struct notifier_block *nb, - unsigned long code, void *data) -{ - struct watchdog_device *wdd; - - wdd = container_of(nb, struct watchdog_device, reboot_nb); - if (code == SYS_DOWN || code == SYS_HALT) { - if (watchdog_active(wdd)) { - int ret; - - ret = wdd->ops->stop(wdd); - if (ret) - return NOTIFY_BAD; - } - } - - return NOTIFY_DONE; -} - /* * watchdog_dev_register: register a watchdog device * @wdd: watchdog device @@ -1085,22 +1065,8 @@ int watchdog_dev_register(struct watchdog_device *wdd) return ret; ret = watchdog_register_pretimeout(wdd); - if (ret) { + if (ret) watchdog_cdev_unregister(wdd); - return ret; - } - - if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) { - wdd->reboot_nb.notifier_call = watchdog_reboot_notifier; - - ret = devm_register_reboot_notifier(&wdd->wd_data->dev, - &wdd->reboot_nb); - if (ret) { - pr_err("watchdog%d: Cannot register reboot notifier (%d)\n", - wdd->id, ret); - watchdog_dev_unregister(wdd); - } - } return ret; } diff --git a/drivers/watchdog/wdat_wdt.c b/drivers/watchdog/wdat_wdt.c index 56ad19608a9bed0ae43ffaa87e59c71ee85d104e..9d91ed59615dc627e81eb09e9ef2a6d1bfcee45a 100644 --- a/drivers/watchdog/wdat_wdt.c +++ b/drivers/watchdog/wdat_wdt.c @@ -392,7 +392,7 @@ static int wdat_wdt_probe(struct platform_device *pdev) memset(&r, 0, sizeof(r)); r.start = gas->address; - r.end = r.start + gas->access_width - 1; + r.end = r.start + ACPI_ACCESS_BYTE_WIDTH(gas->access_width) - 1; if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { r.flags = IORESOURCE_MEM; } else if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO) { diff --git a/drivers/xen/cpu_hotplug.c b/drivers/xen/cpu_hotplug.c index b1357aa4bc552eb3a5989dab5eeacf9295d2a2d8..f192b6f42da9f7eb856c34f2baac0a6a7a4c4c45 100644 --- a/drivers/xen/cpu_hotplug.c +++ b/drivers/xen/cpu_hotplug.c @@ -54,7 +54,7 @@ static int vcpu_online(unsigned int cpu) } static void vcpu_hotplug(unsigned int cpu) { - if (!cpu_possible(cpu)) + if (cpu >= nr_cpu_ids || !cpu_possible(cpu)) return; switch (vcpu_online(cpu)) { diff --git a/drivers/xen/preempt.c b/drivers/xen/preempt.c index 08cb419eb4e63c90f30ea7cdb541d31c14da2391..5f6b77ea34fb5b5ddef4d4fb5e3ba722194ded6f 100644 --- a/drivers/xen/preempt.c +++ b/drivers/xen/preempt.c @@ -37,7 +37,9 @@ asmlinkage __visible void xen_maybe_preempt_hcall(void) * cpu. */ __this_cpu_write(xen_in_preemptible_hcall, false); - _cond_resched(); + local_irq_enable(); + cond_resched(); + local_irq_disable(); __this_cpu_write(xen_in_preemptible_hcall, true); } } diff --git a/drivers/xen/pvcalls-back.c b/drivers/xen/pvcalls-back.c index d4ea33581ac2603cde8e98c894abfa6166db6de2..b3fbfed28682f91e388a18d03efc650f6540e53f 100644 --- a/drivers/xen/pvcalls-back.c +++ b/drivers/xen/pvcalls-back.c @@ -784,7 +784,7 @@ static int pvcalls_back_poll(struct xenbus_device *dev, mappass->reqcopy = *req; icsk = inet_csk(mappass->sock->sk); queue = &icsk->icsk_accept_queue; - data = queue->rskq_accept_head != NULL; + data = READ_ONCE(queue->rskq_accept_head) != NULL; if (data) { mappass->reqcopy.cmd = 0; ret = 0; diff --git a/drivers/xen/xen-balloon.c b/drivers/xen/xen-balloon.c index 2acbfe104e464ab0d8e59bb5ff1862ce08411ed0..3aab77916915e8b978b797da83b757b64a9b6aeb 100644 --- a/drivers/xen/xen-balloon.c +++ b/drivers/xen/xen-balloon.c @@ -83,7 +83,7 @@ static void watch_target(struct xenbus_watch *watch, "%llu", &static_max) == 1)) static_max >>= PAGE_SHIFT - 10; else - static_max = new_target; + static_max = balloon_stats.current_pages; target_diff = (xen_pv_domain() || xen_initial_domain()) ? 0 : static_max - balloon_stats.target_pages; diff --git a/firmware/Makefile b/firmware/Makefile index 29641383e136024ec9f2993b1a29a7fc9c2fa94b..d3d7578e2c451b99845be50c1b8877f66000e251 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -19,7 +19,7 @@ quiet_cmd_fwbin = MK_FW $@ PROGBITS=$(if $(CONFIG_ARM),%,@)progbits; \ echo "/* Generated by firmware/Makefile */" > $@;\ echo " .section .rodata" >>$@;\ - echo " .p2align $${ASM_ALIGN}" >>$@;\ + echo " .p2align 4" >>$@;\ echo "_fw_$${FWSTR}_bin:" >>$@;\ echo " .incbin \"$(2)\"" >>$@;\ echo "_fw_end:" >>$@;\ diff --git a/fs/Kconfig b/fs/Kconfig index f0dde0db5dda371d12d8ad2f7edca05c5f894c03..b8d003f02b765d48392a232b4afd895d55af9eb7 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -114,6 +114,7 @@ source "fs/quota/Kconfig" source "fs/autofs/Kconfig" source "fs/fuse/Kconfig" source "fs/overlayfs/Kconfig" +source "fs/incfs/Kconfig" menu "Caches" diff --git a/fs/Makefile b/fs/Makefile index af76f2296eebf9cf0679b7897556435b04569690..9d1caea038f6c496342fa8a946726710cb454a7f 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -110,6 +110,7 @@ obj-$(CONFIG_ADFS_FS) += adfs/ obj-$(CONFIG_FUSE_FS) += fuse/ obj-$(CONFIG_OVERLAY_FS) += overlayfs/ obj-$(CONFIG_ORANGEFS_FS) += orangefs/ +obj-$(CONFIG_INCREMENTAL_FS) += incfs/ obj-$(CONFIG_UDF_FS) += udf/ obj-$(CONFIG_SUN_OPENPROMFS) += openpromfs/ obj-$(CONFIG_OMFS_FS) += omfs/ diff --git a/fs/affs/super.c b/fs/affs/super.c index d1ad11a8a4a59c2d7de3e854cfeb8b28716be59d..b6ce0c36029bd91e3a98837600b0243d9b53cd97 100644 --- a/fs/affs/super.c +++ b/fs/affs/super.c @@ -561,14 +561,9 @@ affs_remount(struct super_block *sb, int *flags, char *data) int root_block; unsigned long mount_flags; int res = 0; - char *new_opts; char volume[32]; char *prefix = NULL; - new_opts = kstrdup(data, GFP_KERNEL); - if (data && !new_opts) - return -ENOMEM; - pr_debug("%s(flags=0x%x,opts=\"%s\")\n", __func__, *flags, data); sync_filesystem(sb); @@ -579,7 +574,6 @@ affs_remount(struct super_block *sb, int *flags, char *data) &blocksize, &prefix, volume, &mount_flags)) { kfree(prefix); - kfree(new_opts); return -EINVAL; } diff --git a/fs/afs/callback.c b/fs/afs/callback.c index 4ad70125029988ae29ad81dc7bf0022f57a8c2b8..97283b04fa6fd5574244130932c015f20fc16bab 100644 --- a/fs/afs/callback.c +++ b/fs/afs/callback.c @@ -221,14 +221,8 @@ void afs_break_callback(struct afs_vnode *vnode) vnode->cb_break++; afs_clear_permits(vnode); - spin_lock(&vnode->lock); - - _debug("break callback"); - - if (list_empty(&vnode->granted_locks) && - !list_empty(&vnode->pending_locks)) + if (vnode->lock_state == AFS_VNODE_LOCK_WAITING_FOR_CB) afs_lock_may_be_available(vnode); - spin_unlock(&vnode->lock); } write_sequnlock(&vnode->cb_lock); diff --git a/fs/afs/cell.c b/fs/afs/cell.c index ee07162d35c7a2b260dacbf6e6897e5230335dd9..cce0e23b2454266f476f32366dbf19f73a6086c5 100644 --- a/fs/afs/cell.c +++ b/fs/afs/cell.c @@ -135,8 +135,17 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net, _leave(" = -ENAMETOOLONG"); return ERR_PTR(-ENAMETOOLONG); } - if (namelen == 5 && memcmp(name, "@cell", 5) == 0) + + /* Prohibit cell names that contain unprintable chars, '/' and '@' or + * that begin with a dot. This also precludes "@cell". + */ + if (name[0] == '.') return ERR_PTR(-EINVAL); + for (i = 0; i < namelen; i++) { + char ch = name[i]; + if (!isprint(ch) || ch == '/' || ch == '@') + return ERR_PTR(-EINVAL); + } _enter("%*.*s,%s", namelen, namelen, name, vllist); diff --git a/fs/afs/dir_edit.c b/fs/afs/dir_edit.c index 8b400f5aead58233862f880c54f85240ae6d9947..0e7162527db8c3469192f6c4e231f2da3d30a288 100644 --- a/fs/afs/dir_edit.c +++ b/fs/afs/dir_edit.c @@ -72,13 +72,11 @@ static int afs_find_contig_bits(union afs_xdr_dir_block *block, unsigned int nr_ static void afs_set_contig_bits(union afs_xdr_dir_block *block, int bit, unsigned int nr_slots) { - u64 mask, before, after; + u64 mask; mask = (1 << nr_slots) - 1; mask <<= bit; - before = *(u64 *)block->hdr.bitmap; - block->hdr.bitmap[0] |= (u8)(mask >> 0 * 8); block->hdr.bitmap[1] |= (u8)(mask >> 1 * 8); block->hdr.bitmap[2] |= (u8)(mask >> 2 * 8); @@ -87,8 +85,6 @@ static void afs_set_contig_bits(union afs_xdr_dir_block *block, block->hdr.bitmap[5] |= (u8)(mask >> 5 * 8); block->hdr.bitmap[6] |= (u8)(mask >> 6 * 8); block->hdr.bitmap[7] |= (u8)(mask >> 7 * 8); - - after = *(u64 *)block->hdr.bitmap; } /* @@ -97,13 +93,11 @@ static void afs_set_contig_bits(union afs_xdr_dir_block *block, static void afs_clear_contig_bits(union afs_xdr_dir_block *block, int bit, unsigned int nr_slots) { - u64 mask, before, after; + u64 mask; mask = (1 << nr_slots) - 1; mask <<= bit; - before = *(u64 *)block->hdr.bitmap; - block->hdr.bitmap[0] &= ~(u8)(mask >> 0 * 8); block->hdr.bitmap[1] &= ~(u8)(mask >> 1 * 8); block->hdr.bitmap[2] &= ~(u8)(mask >> 2 * 8); @@ -112,8 +106,6 @@ static void afs_clear_contig_bits(union afs_xdr_dir_block *block, block->hdr.bitmap[5] &= ~(u8)(mask >> 5 * 8); block->hdr.bitmap[6] &= ~(u8)(mask >> 6 * 8); block->hdr.bitmap[7] &= ~(u8)(mask >> 7 * 8); - - after = *(u64 *)block->hdr.bitmap; } /* diff --git a/fs/afs/file.c b/fs/afs/file.c index 3b628b0caa09296f581e44c86fcd1def45aa3910..e9e779c48d18c0e63ca49c7ac85e3a03860ae8b9 100644 --- a/fs/afs/file.c +++ b/fs/afs/file.c @@ -169,11 +169,12 @@ int afs_release(struct inode *inode, struct file *file) { struct afs_vnode *vnode = AFS_FS_I(inode); struct afs_file *af = file->private_data; + int ret = 0; _enter("{%x:%u},", vnode->fid.vid, vnode->fid.vnode); if ((file->f_mode & FMODE_WRITE)) - return vfs_fsync(file, 0); + ret = vfs_fsync(file, 0); file->private_data = NULL; if (af->wb) @@ -181,8 +182,8 @@ int afs_release(struct inode *inode, struct file *file) key_put(af->key); kfree(af); afs_prune_wb_keys(vnode); - _leave(" = 0"); - return 0; + _leave(" = %d", ret); + return ret; } /* diff --git a/fs/afs/flock.c b/fs/afs/flock.c index 1bb300ef362b008339cb6eb53ff230a05503aa99..fbf4986b12245c012f00682961cded78ab34a7f0 100644 --- a/fs/afs/flock.c +++ b/fs/afs/flock.c @@ -13,9 +13,11 @@ #define AFS_LOCK_GRANTED 0 #define AFS_LOCK_PENDING 1 +#define AFS_LOCK_YOUR_TRY 2 struct workqueue_struct *afs_lock_manager; +static void afs_next_locker(struct afs_vnode *vnode, int error); static void afs_fl_copy_lock(struct file_lock *new, struct file_lock *fl); static void afs_fl_release_private(struct file_lock *fl); @@ -24,6 +26,12 @@ static const struct file_lock_operations afs_lock_ops = { .fl_release_private = afs_fl_release_private, }; +static inline void afs_set_lock_state(struct afs_vnode *vnode, enum afs_lock_state state) +{ + _debug("STATE %u -> %u", vnode->lock_state, state); + vnode->lock_state = state; +} + /* * if the callback is broken on this vnode, then the lock may now be available */ @@ -31,14 +39,17 @@ void afs_lock_may_be_available(struct afs_vnode *vnode) { _enter("{%x:%u}", vnode->fid.vid, vnode->fid.vnode); - queue_delayed_work(afs_lock_manager, &vnode->lock_work, 0); + spin_lock(&vnode->lock); + if (vnode->lock_state == AFS_VNODE_LOCK_WAITING_FOR_CB) + afs_next_locker(vnode, 0); + spin_unlock(&vnode->lock); } /* * the lock will time out in 5 minutes unless we extend it, so schedule * extension in a bit less than that time */ -static void afs_schedule_lock_extension(struct afs_vnode *vnode) +static void __maybe_unused afs_schedule_lock_extension(struct afs_vnode *vnode) { queue_delayed_work(afs_lock_manager, &vnode->lock_work, AFS_LOCKWAIT * HZ / 2); @@ -49,22 +60,65 @@ static void afs_schedule_lock_extension(struct afs_vnode *vnode) * first lock in the queue is itself a readlock) * - the caller must hold the vnode lock */ -static void afs_grant_locks(struct afs_vnode *vnode, struct file_lock *fl) +static void afs_grant_locks(struct afs_vnode *vnode) { struct file_lock *p, *_p; + bool exclusive = (vnode->lock_type == AFS_LOCK_WRITE); - list_move_tail(&fl->fl_u.afs.link, &vnode->granted_locks); - if (fl->fl_type == F_RDLCK) { - list_for_each_entry_safe(p, _p, &vnode->pending_locks, - fl_u.afs.link) { - if (p->fl_type == F_RDLCK) { - p->fl_u.afs.state = AFS_LOCK_GRANTED; - list_move_tail(&p->fl_u.afs.link, - &vnode->granted_locks); - wake_up(&p->fl_wait); - } + list_for_each_entry_safe(p, _p, &vnode->pending_locks, fl_u.afs.link) { + if (!exclusive && p->fl_type == F_WRLCK) + continue; + + list_move_tail(&p->fl_u.afs.link, &vnode->granted_locks); + p->fl_u.afs.state = AFS_LOCK_GRANTED; + wake_up(&p->fl_wait); + } +} + +/* + * If an error is specified, reject every pending lock that matches the + * authentication and type of the lock we failed to get. If there are any + * remaining lockers, try to wake up one of them to have a go. + */ +static void afs_next_locker(struct afs_vnode *vnode, int error) +{ + struct file_lock *p, *_p, *next = NULL; + struct key *key = vnode->lock_key; + unsigned int fl_type = F_RDLCK; + + _enter(""); + + if (vnode->lock_type == AFS_LOCK_WRITE) + fl_type = F_WRLCK; + + list_for_each_entry_safe(p, _p, &vnode->pending_locks, fl_u.afs.link) { + if (error && + p->fl_type == fl_type && + afs_file_key(p->fl_file) == key) { + list_del_init(&p->fl_u.afs.link); + p->fl_u.afs.state = error; + wake_up(&p->fl_wait); } + + /* Select the next locker to hand off to. */ + if (next && + (next->fl_type == F_WRLCK || p->fl_type == F_RDLCK)) + continue; + next = p; + } + + vnode->lock_key = NULL; + key_put(key); + + if (next) { + afs_set_lock_state(vnode, AFS_VNODE_LOCK_SETTING); + next->fl_u.afs.state = AFS_LOCK_YOUR_TRY; + wake_up(&next->fl_wait); + } else { + afs_set_lock_state(vnode, AFS_VNODE_LOCK_NONE); } + + _leave(""); } /* @@ -170,8 +224,6 @@ void afs_lock_work(struct work_struct *work) { struct afs_vnode *vnode = container_of(work, struct afs_vnode, lock_work.work); - struct file_lock *fl, *next; - afs_lock_type_t type; struct key *key; int ret; @@ -184,7 +236,7 @@ void afs_lock_work(struct work_struct *work) switch (vnode->lock_state) { case AFS_VNODE_LOCK_NEED_UNLOCK: _debug("unlock"); - vnode->lock_state = AFS_VNODE_LOCK_UNLOCKING; + afs_set_lock_state(vnode, AFS_VNODE_LOCK_UNLOCKING); spin_unlock(&vnode->lock); /* attempt to release the server lock; if it fails, we just @@ -196,22 +248,9 @@ void afs_lock_work(struct work_struct *work) vnode->fid.vid, vnode->fid.vnode, ret); spin_lock(&vnode->lock); - key_put(vnode->lock_key); - vnode->lock_key = NULL; - vnode->lock_state = AFS_VNODE_LOCK_NONE; - - if (list_empty(&vnode->pending_locks)) { - spin_unlock(&vnode->lock); - return; - } - - /* The new front of the queue now owns the state variables. */ - next = list_entry(vnode->pending_locks.next, - struct file_lock, fl_u.afs.link); - vnode->lock_key = key_get(afs_file_key(next->fl_file)); - vnode->lock_type = (next->fl_type == F_RDLCK) ? AFS_LOCK_READ : AFS_LOCK_WRITE; - vnode->lock_state = AFS_VNODE_LOCK_WAITING_FOR_CB; - goto again; + afs_next_locker(vnode, 0); + spin_unlock(&vnode->lock); + return; /* If we've already got a lock, then it must be time to extend that * lock as AFS locks time out after 5 minutes. @@ -222,7 +261,7 @@ void afs_lock_work(struct work_struct *work) ASSERT(!list_empty(&vnode->granted_locks)); key = key_get(vnode->lock_key); - vnode->lock_state = AFS_VNODE_LOCK_EXTENDING; + afs_set_lock_state(vnode, AFS_VNODE_LOCK_EXTENDING); spin_unlock(&vnode->lock); ret = afs_extend_lock(vnode, key); /* RPC */ @@ -236,72 +275,26 @@ void afs_lock_work(struct work_struct *work) if (vnode->lock_state != AFS_VNODE_LOCK_EXTENDING) goto again; - vnode->lock_state = AFS_VNODE_LOCK_GRANTED; + afs_set_lock_state(vnode, AFS_VNODE_LOCK_GRANTED); - if (ret == 0) - afs_schedule_lock_extension(vnode); - else + if (ret != 0) queue_delayed_work(afs_lock_manager, &vnode->lock_work, HZ * 10); spin_unlock(&vnode->lock); _leave(" [ext]"); return; - /* If we don't have a granted lock, then we must've been called - * back by the server, and so if might be possible to get a - * lock we're currently waiting for. - */ + /* If we're waiting for a callback to indicate lock release, we can't + * actually rely on this, so need to recheck at regular intervals. The + * problem is that the server might not notify us if the lock just + * expires (say because a client died) rather than being explicitly + * released. + */ case AFS_VNODE_LOCK_WAITING_FOR_CB: - _debug("get"); - - key = key_get(vnode->lock_key); - type = vnode->lock_type; - vnode->lock_state = AFS_VNODE_LOCK_SETTING; + _debug("retry"); + afs_next_locker(vnode, 0); spin_unlock(&vnode->lock); - - ret = afs_set_lock(vnode, key, type); /* RPC */ - key_put(key); - - spin_lock(&vnode->lock); - switch (ret) { - case -EWOULDBLOCK: - _debug("blocked"); - break; - case 0: - _debug("acquired"); - vnode->lock_state = AFS_VNODE_LOCK_GRANTED; - /* Fall through */ - default: - /* Pass the lock or the error onto the first locker in - * the list - if they're looking for this type of lock. - * If they're not, we assume that whoever asked for it - * took a signal. - */ - if (list_empty(&vnode->pending_locks)) { - _debug("withdrawn"); - vnode->lock_state = AFS_VNODE_LOCK_NEED_UNLOCK; - goto again; - } - - fl = list_entry(vnode->pending_locks.next, - struct file_lock, fl_u.afs.link); - type = (fl->fl_type == F_RDLCK) ? AFS_LOCK_READ : AFS_LOCK_WRITE; - if (vnode->lock_type != type) { - _debug("changed"); - vnode->lock_state = AFS_VNODE_LOCK_NEED_UNLOCK; - goto again; - } - - fl->fl_u.afs.state = ret; - if (ret == 0) - afs_grant_locks(vnode, fl); - else - list_del_init(&fl->fl_u.afs.link); - wake_up(&fl->fl_wait); - spin_unlock(&vnode->lock); - _leave(" [granted]"); - return; - } + return; default: /* Looks like a lock request was withdrawn. */ @@ -319,14 +312,15 @@ void afs_lock_work(struct work_struct *work) */ static void afs_defer_unlock(struct afs_vnode *vnode) { - _enter(""); + _enter("%u", vnode->lock_state); - if (vnode->lock_state == AFS_VNODE_LOCK_GRANTED || - vnode->lock_state == AFS_VNODE_LOCK_EXTENDING) { + if (list_empty(&vnode->granted_locks) && + (vnode->lock_state == AFS_VNODE_LOCK_GRANTED || + vnode->lock_state == AFS_VNODE_LOCK_EXTENDING)) { cancel_delayed_work(&vnode->lock_work); - vnode->lock_state = AFS_VNODE_LOCK_NEED_UNLOCK; - afs_lock_may_be_available(vnode); + afs_set_lock_state(vnode, AFS_VNODE_LOCK_NEED_UNLOCK); + queue_delayed_work(afs_lock_manager, &vnode->lock_work, 0); } } @@ -375,50 +369,6 @@ static int afs_do_setlk_check(struct afs_vnode *vnode, struct key *key, return 0; } -/* - * Remove the front runner from the pending queue. - * - The caller must hold vnode->lock. - */ -static void afs_dequeue_lock(struct afs_vnode *vnode, struct file_lock *fl) -{ - struct file_lock *next; - - _enter(""); - - /* ->lock_type, ->lock_key and ->lock_state only belong to this - * file_lock if we're at the front of the pending queue or if we have - * the lock granted or if the lock_state is NEED_UNLOCK or UNLOCKING. - */ - if (vnode->granted_locks.next == &fl->fl_u.afs.link && - vnode->granted_locks.prev == &fl->fl_u.afs.link) { - list_del_init(&fl->fl_u.afs.link); - afs_defer_unlock(vnode); - return; - } - - if (!list_empty(&vnode->granted_locks) || - vnode->pending_locks.next != &fl->fl_u.afs.link) { - list_del_init(&fl->fl_u.afs.link); - return; - } - - list_del_init(&fl->fl_u.afs.link); - key_put(vnode->lock_key); - vnode->lock_key = NULL; - vnode->lock_state = AFS_VNODE_LOCK_NONE; - - if (list_empty(&vnode->pending_locks)) - return; - - /* The new front of the queue now owns the state variables. */ - next = list_entry(vnode->pending_locks.next, - struct file_lock, fl_u.afs.link); - vnode->lock_key = key_get(afs_file_key(next->fl_file)); - vnode->lock_type = (next->fl_type == F_RDLCK) ? AFS_LOCK_READ : AFS_LOCK_WRITE; - vnode->lock_state = AFS_VNODE_LOCK_WAITING_FOR_CB; - afs_lock_may_be_available(vnode); -} - /* * request a lock on a file on the server */ @@ -432,10 +382,6 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl) _enter("{%x:%u},%u", vnode->fid.vid, vnode->fid.vnode, fl->fl_type); - /* only whole-file locks are supported */ - if (fl->fl_start != 0 || fl->fl_end != OFFSET_MAX) - return -EINVAL; - fl->fl_ops = &afs_lock_ops; INIT_LIST_HEAD(&fl->fl_u.afs.link); fl->fl_u.afs.state = AFS_LOCK_PENDING; @@ -447,44 +393,66 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl) return ret; spin_lock(&vnode->lock); + list_add_tail(&fl->fl_u.afs.link, &vnode->pending_locks); - /* If we've already got a readlock on the server then we instantly - * grant another readlock, irrespective of whether there are any - * pending writelocks. + /* If we've already got a lock on the server then try to move to having + * the VFS grant the requested lock. Note that this means that other + * clients may get starved out. */ - if (type == AFS_LOCK_READ && - vnode->lock_state == AFS_VNODE_LOCK_GRANTED && - vnode->lock_type == AFS_LOCK_READ) { - _debug("instant readlock"); - ASSERT(!list_empty(&vnode->granted_locks)); - goto share_existing_lock; - } + _debug("try %u", vnode->lock_state); + if (vnode->lock_state == AFS_VNODE_LOCK_GRANTED) { + if (type == AFS_LOCK_READ) { + _debug("instant readlock"); + list_move_tail(&fl->fl_u.afs.link, &vnode->granted_locks); + fl->fl_u.afs.state = AFS_LOCK_GRANTED; + goto vnode_is_locked_u; + } - list_add_tail(&fl->fl_u.afs.link, &vnode->pending_locks); + if (vnode->lock_type == AFS_LOCK_WRITE) { + _debug("instant writelock"); + list_move_tail(&fl->fl_u.afs.link, &vnode->granted_locks); + fl->fl_u.afs.state = AFS_LOCK_GRANTED; + goto vnode_is_locked_u; + } + } if (vnode->lock_state != AFS_VNODE_LOCK_NONE) goto need_to_wait; +try_to_lock: /* We don't have a lock on this vnode and we aren't currently waiting * for one either, so ask the server for a lock. * * Note that we need to be careful if we get interrupted by a signal * after dispatching the request as we may still get the lock, even * though we don't wait for the reply (it's not too bad a problem - the - * lock will expire in 10 mins anyway). + * lock will expire in 5 mins anyway). */ _debug("not locked"); vnode->lock_key = key_get(key); vnode->lock_type = type; - vnode->lock_state = AFS_VNODE_LOCK_SETTING; + afs_set_lock_state(vnode, AFS_VNODE_LOCK_SETTING); spin_unlock(&vnode->lock); ret = afs_set_lock(vnode, key, type); /* RPC */ spin_lock(&vnode->lock); switch (ret) { + case -EKEYREJECTED: + case -EKEYEXPIRED: + case -EKEYREVOKED: + case -EPERM: + case -EACCES: + fl->fl_u.afs.state = ret; + list_del_init(&fl->fl_u.afs.link); + afs_next_locker(vnode, ret); + goto error_unlock; + default: - goto abort_attempt; + fl->fl_u.afs.state = ret; + list_del_init(&fl->fl_u.afs.link); + afs_next_locker(vnode, 0); + goto error_unlock; case -EWOULDBLOCK: /* The server doesn't have a lock-waiting queue, so the client @@ -494,29 +462,23 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl) _debug("would block"); ASSERT(list_empty(&vnode->granted_locks)); ASSERTCMP(vnode->pending_locks.next, ==, &fl->fl_u.afs.link); - vnode->lock_state = AFS_VNODE_LOCK_WAITING_FOR_CB; - goto need_to_wait; + goto lock_is_contended; case 0: _debug("acquired"); - break; + afs_set_lock_state(vnode, AFS_VNODE_LOCK_GRANTED); + afs_grant_locks(vnode); + goto vnode_is_locked_u; } - /* we've acquired a server lock, but it needs to be renewed after 5 - * mins */ - vnode->lock_state = AFS_VNODE_LOCK_GRANTED; - afs_schedule_lock_extension(vnode); - -share_existing_lock: - /* the lock has been granted as far as we're concerned... */ - fl->fl_u.afs.state = AFS_LOCK_GRANTED; - list_move_tail(&fl->fl_u.afs.link, &vnode->granted_locks); - -given_lock: - /* ... but we do still need to get the VFS's blessing */ +vnode_is_locked_u: spin_unlock(&vnode->lock); +vnode_is_locked: + /* the lock has been granted by the server... */ + ASSERTCMP(fl->fl_u.afs.state, ==, AFS_LOCK_GRANTED); - ret = posix_lock_file(file, fl, NULL); + /* ... but the VFS still needs to distribute access on this client. */ + ret = locks_lock_file_wait(file, fl); if (ret < 0) goto vfs_rejected_lock; @@ -528,38 +490,61 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl) _leave(" = 0"); return 0; +lock_is_contended: + if (!(fl->fl_flags & FL_SLEEP)) { + list_del_init(&fl->fl_u.afs.link); + afs_next_locker(vnode, 0); + ret = -EAGAIN; + goto error_unlock; + } + + afs_set_lock_state(vnode, AFS_VNODE_LOCK_WAITING_FOR_CB); + queue_delayed_work(afs_lock_manager, &vnode->lock_work, HZ * 5); + need_to_wait: /* We're going to have to wait. Either this client doesn't have a lock * on the server yet and we need to wait for a callback to occur, or - * the client does have a lock on the server, but it belongs to some - * other process(es) and is incompatible with the lock we want. + * the client does have a lock on the server, but it's shared and we + * need an exclusive lock. */ - ret = -EAGAIN; - if (fl->fl_flags & FL_SLEEP) { - spin_unlock(&vnode->lock); + spin_unlock(&vnode->lock); - _debug("sleep"); - ret = wait_event_interruptible(fl->fl_wait, - fl->fl_u.afs.state != AFS_LOCK_PENDING); + _debug("sleep"); + ret = wait_event_interruptible(fl->fl_wait, + fl->fl_u.afs.state != AFS_LOCK_PENDING); + _debug("wait = %d", ret); + if (fl->fl_u.afs.state >= 0 && fl->fl_u.afs.state != AFS_LOCK_GRANTED) { spin_lock(&vnode->lock); - } - if (fl->fl_u.afs.state == AFS_LOCK_GRANTED) - goto given_lock; - if (fl->fl_u.afs.state < 0) - ret = fl->fl_u.afs.state; + switch (fl->fl_u.afs.state) { + case AFS_LOCK_YOUR_TRY: + fl->fl_u.afs.state = AFS_LOCK_PENDING; + goto try_to_lock; + case AFS_LOCK_PENDING: + if (ret > 0) { + /* We need to retry the lock. We may not be + * notified by the server if it just expired + * rather than being released. + */ + ASSERTCMP(vnode->lock_state, ==, AFS_VNODE_LOCK_WAITING_FOR_CB); + afs_set_lock_state(vnode, AFS_VNODE_LOCK_SETTING); + fl->fl_u.afs.state = AFS_LOCK_PENDING; + goto try_to_lock; + } + goto error_unlock; + case AFS_LOCK_GRANTED: + default: + break; + } -abort_attempt: - /* we aren't going to get the lock, either because we're unwilling to - * wait, or because some signal happened */ - _debug("abort"); - afs_dequeue_lock(vnode, fl); + spin_unlock(&vnode->lock); + } -error_unlock: - spin_unlock(&vnode->lock); - _leave(" = %d", ret); - return ret; + if (fl->fl_u.afs.state == AFS_LOCK_GRANTED) + goto vnode_is_locked; + ret = fl->fl_u.afs.state; + goto error; vfs_rejected_lock: /* The VFS rejected the lock we just obtained, so we have to discard @@ -569,9 +554,13 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl) _debug("vfs refused %d", ret); spin_lock(&vnode->lock); list_del_init(&fl->fl_u.afs.link); - if (list_empty(&vnode->granted_locks)) - afs_defer_unlock(vnode); - goto error_unlock; + afs_defer_unlock(vnode); + +error_unlock: + spin_unlock(&vnode->lock); +error: + _leave(" = %d", ret); + return ret; } /* @@ -587,11 +576,7 @@ static int afs_do_unlk(struct file *file, struct file_lock *fl) /* Flush all pending writes before doing anything with locks. */ vfs_fsync(file, 0); - /* only whole-file unlocks are supported */ - if (fl->fl_start != 0 || fl->fl_end != OFFSET_MAX) - return -EINVAL; - - ret = posix_lock_file(file, fl, NULL); + ret = locks_lock_file_wait(file, fl); _leave(" = %d [%u]", ret, vnode->lock_state); return ret; } @@ -618,12 +603,15 @@ static int afs_do_getlk(struct file *file, struct file_lock *fl) goto error; lock_count = READ_ONCE(vnode->status.lock_count); - if (lock_count > 0) - fl->fl_type = F_RDLCK; - else - fl->fl_type = F_WRLCK; - fl->fl_start = 0; - fl->fl_end = OFFSET_MAX; + if (lock_count != 0) { + if (lock_count > 0) + fl->fl_type = F_RDLCK; + else + fl->fl_type = F_WRLCK; + fl->fl_start = 0; + fl->fl_end = OFFSET_MAX; + fl->fl_pid = 0; + } } ret = 0; @@ -710,7 +698,11 @@ static void afs_fl_release_private(struct file_lock *fl) _enter(""); spin_lock(&vnode->lock); - afs_dequeue_lock(vnode, fl); + + list_del_init(&fl->fl_u.afs.link); + if (list_empty(&vnode->granted_locks)) + afs_defer_unlock(vnode); + _debug("state %u for %p", vnode->lock_state, vnode); spin_unlock(&vnode->lock); } diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 0726e40db0f8bcd17b27211dae53998f6e0e40c2..e6f11da5461be0f98121e3e7494ec05379765986 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -398,12 +398,9 @@ int afs_validate(struct afs_vnode *vnode, struct key *key) vnode->cb_s_break = vnode->cb_interest->server->cb_s_break; vnode->cb_v_break = vnode->volume->cb_v_break; valid = false; - } else if (vnode->status.type == AFS_FTYPE_DIR && - (!test_bit(AFS_VNODE_DIR_VALID, &vnode->flags) || - vnode->cb_expires_at - 10 <= now)) { + } else if (test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) { valid = false; - } else if (test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags) || - vnode->cb_expires_at - 10 <= now) { + } else if (vnode->cb_expires_at - 10 <= now) { valid = false; } else { valid = true; @@ -541,6 +538,7 @@ void afs_evict_inode(struct inode *inode) } #endif + afs_prune_wb_keys(vnode); afs_put_permits(rcu_access_pointer(vnode->permit_cache)); key_put(vnode->lock_key); vnode->lock_key = NULL; diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index 2543f24d23f8d1290fd159d421cfcef37eb66c72..560dd5ff5a151ea769bfbc511dba6e51f4ad5bc0 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c @@ -573,6 +573,7 @@ static long afs_wait_for_call_to_complete(struct afs_call *call, call->need_attention = false; __set_current_state(TASK_RUNNING); afs_deliver_to_call(call); + timeout = rtt2; continue; } diff --git a/fs/afs/security.c b/fs/afs/security.c index 81dfedb7879ff9bf56ab4fcca26ef6c90d835de2..66042b432baa8a88012af6298d834b286ba3839b 100644 --- a/fs/afs/security.c +++ b/fs/afs/security.c @@ -87,11 +87,9 @@ void afs_clear_permits(struct afs_vnode *vnode) permits = rcu_dereference_protected(vnode->permit_cache, lockdep_is_held(&vnode->lock)); RCU_INIT_POINTER(vnode->permit_cache, NULL); - vnode->cb_break++; spin_unlock(&vnode->lock); - if (permits) - afs_put_permits(permits); + afs_put_permits(permits); } /* diff --git a/fs/afs/super.c b/fs/afs/super.c index bd2608297473272a9d1e684bb1d92bab906aef60..4961d32ccd1e0abeb22e48b0e87c19f87c1620ff 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -356,6 +356,7 @@ static int afs_test_super(struct super_block *sb, void *data) return (as->net_ns == as1->net_ns && as->volume && as->volume->vid == as1->volume->vid && + as->cell == as1->cell && !as->dyn_root); } @@ -392,6 +393,7 @@ static int afs_fill_super(struct super_block *sb, /* fill in the superblock */ sb->s_blocksize = PAGE_SIZE; sb->s_blocksize_bits = PAGE_SHIFT; + sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_magic = AFS_FS_MAGIC; sb->s_op = &afs_super_ops; if (!as->dyn_root) diff --git a/fs/afs/xattr.c b/fs/afs/xattr.c index cfcc674e64a55bc52be024d873a5b70fa85fafc1..411f67c79f09064e987d6ed4d8551847cd5af622 100644 --- a/fs/afs/xattr.c +++ b/fs/afs/xattr.c @@ -50,7 +50,7 @@ static int afs_xattr_get_cell(const struct xattr_handler *handler, return namelen; if (namelen > size) return -ERANGE; - memcpy(buffer, cell->name, size); + memcpy(buffer, cell->name, namelen); return namelen; } @@ -104,7 +104,7 @@ static int afs_xattr_get_volume(const struct xattr_handler *handler, return namelen; if (namelen > size) return -ERANGE; - memcpy(buffer, volname, size); + memcpy(buffer, volname, namelen); return namelen; } diff --git a/fs/aio.c b/fs/aio.c index 911e23087dfb633fc546085bfb97d2c812539c96..b5fbf2061868ce9ed84b2415df8c37d988907629 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -1600,6 +1600,14 @@ static int aio_fsync(struct fsync_iocb *req, const struct iocb *iocb, return 0; } +static void aio_poll_put_work(struct work_struct *work) +{ + struct poll_iocb *req = container_of(work, struct poll_iocb, work); + struct aio_kiocb *iocb = container_of(req, struct aio_kiocb, poll); + + iocb_put(iocb); +} + static void aio_poll_complete_work(struct work_struct *work) { struct poll_iocb *req = container_of(work, struct poll_iocb, work); @@ -1664,6 +1672,8 @@ static int aio_poll_wake(struct wait_queue_entry *wait, unsigned mode, int sync, list_del_init(&req->wait.entry); if (mask && spin_trylock_irqsave(&iocb->ki_ctx->ctx_lock, flags)) { + struct kioctx *ctx = iocb->ki_ctx; + /* * Try to complete the iocb inline if we can. Use * irqsave/irqrestore because not all filesystems (e.g. fuse) @@ -1673,8 +1683,14 @@ static int aio_poll_wake(struct wait_queue_entry *wait, unsigned mode, int sync, list_del(&iocb->ki_list); iocb->ki_res.res = mangle_poll(mask); req->done = true; - spin_unlock_irqrestore(&iocb->ki_ctx->ctx_lock, flags); - iocb_put(iocb); + if (iocb->ki_eventfd && eventfd_signal_count()) { + iocb = NULL; + INIT_WORK(&req->work, aio_poll_put_work); + schedule_work(&req->work); + } + spin_unlock_irqrestore(&ctx->ctx_lock, flags); + if (iocb) + iocb_put(iocb); } else { schedule_work(&req->work); } diff --git a/fs/btrfs/check-integrity.c b/fs/btrfs/check-integrity.c index 833cf3c35b4df8d0040bac23809dc7fcc20cef3d..3b77c8ab5357e38fc5171f72f8e25995b2c30940 100644 --- a/fs/btrfs/check-integrity.c +++ b/fs/btrfs/check-integrity.c @@ -629,7 +629,6 @@ static struct btrfsic_dev_state *btrfsic_dev_state_hashtable_lookup(dev_t dev, static int btrfsic_process_superblock(struct btrfsic_state *state, struct btrfs_fs_devices *fs_devices) { - struct btrfs_fs_info *fs_info = state->fs_info; struct btrfs_super_block *selected_super; struct list_head *dev_head = &fs_devices->devices; struct btrfs_device *device; @@ -700,7 +699,7 @@ static int btrfsic_process_superblock(struct btrfsic_state *state, break; } - num_copies = btrfs_num_copies(fs_info, next_bytenr, + num_copies = btrfs_num_copies(state->fs_info, next_bytenr, state->metablock_size); if (state->print_mask & BTRFSIC_PRINT_MASK_NUM_COPIES) pr_info("num_copies(log_bytenr=%llu) = %d\n", diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 84ff398ae70b6d125e3dd6c2b623dd5fa59e2937..c9943d70e2cb2df002971482da998a88bd5ff554 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -337,12 +337,10 @@ u64 btrfs_get_tree_mod_seq(struct btrfs_fs_info *fs_info, struct seq_list *elem) { write_lock(&fs_info->tree_mod_log_lock); - spin_lock(&fs_info->tree_mod_seq_lock); if (!elem->seq) { elem->seq = btrfs_inc_tree_mod_seq(fs_info); list_add_tail(&elem->list, &fs_info->tree_mod_seq_list); } - spin_unlock(&fs_info->tree_mod_seq_lock); write_unlock(&fs_info->tree_mod_log_lock); return elem->seq; @@ -362,7 +360,7 @@ void btrfs_put_tree_mod_seq(struct btrfs_fs_info *fs_info, if (!seq_putting) return; - spin_lock(&fs_info->tree_mod_seq_lock); + write_lock(&fs_info->tree_mod_log_lock); list_del(&elem->list); elem->seq = 0; @@ -373,19 +371,17 @@ void btrfs_put_tree_mod_seq(struct btrfs_fs_info *fs_info, * blocker with lower sequence number exists, we * cannot remove anything from the log */ - spin_unlock(&fs_info->tree_mod_seq_lock); + write_unlock(&fs_info->tree_mod_log_lock); return; } min_seq = cur_elem->seq; } } - spin_unlock(&fs_info->tree_mod_seq_lock); /* * anything that's lower than the lowest existing (read: blocked) * sequence number can be removed from the tree. */ - write_lock(&fs_info->tree_mod_log_lock); tm_root = &fs_info->tree_mod_log; for (node = rb_first(tm_root); node; node = next) { next = rb_next(node); diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index d24ecbf938b6c2257861490cb708a3cf965c0a61..15cb96ad15d8c1bc41deda728120f9cb51413bec 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -893,14 +893,12 @@ struct btrfs_fs_info { struct list_head delayed_iputs; struct mutex cleaner_delayed_iput_mutex; - /* this protects tree_mod_seq_list */ - spinlock_t tree_mod_seq_lock; atomic64_t tree_mod_seq; - struct list_head tree_mod_seq_list; - /* this protects tree_mod_log */ + /* this protects tree_mod_log and tree_mod_seq_list */ rwlock_t tree_mod_log_lock; struct rb_root tree_mod_log; + struct list_head tree_mod_seq_list; atomic_t async_delalloc_pages; diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c index 7e5c81e80e15d7d9c31c5eebad1c2bde699212e7..09a12115b64086be5f14b3621ad0617017f6f053 100644 --- a/fs/btrfs/delayed-ref.c +++ b/fs/btrfs/delayed-ref.c @@ -301,7 +301,7 @@ void btrfs_merge_delayed_refs(struct btrfs_trans_handle *trans, if (head->is_data) return; - spin_lock(&fs_info->tree_mod_seq_lock); + read_lock(&fs_info->tree_mod_log_lock); if (!list_empty(&fs_info->tree_mod_seq_list)) { struct seq_list *elem; @@ -309,7 +309,7 @@ void btrfs_merge_delayed_refs(struct btrfs_trans_handle *trans, struct seq_list, list); seq = elem->seq; } - spin_unlock(&fs_info->tree_mod_seq_lock); + read_unlock(&fs_info->tree_mod_log_lock); again: for (node = rb_first(&head->ref_tree); node; node = rb_next(node)) { @@ -326,7 +326,7 @@ int btrfs_check_delayed_seq(struct btrfs_fs_info *fs_info, u64 seq) struct seq_list *elem; int ret = 0; - spin_lock(&fs_info->tree_mod_seq_lock); + read_lock(&fs_info->tree_mod_log_lock); if (!list_empty(&fs_info->tree_mod_seq_list)) { elem = list_first_entry(&fs_info->tree_mod_seq_list, struct seq_list, list); @@ -339,7 +339,7 @@ int btrfs_check_delayed_seq(struct btrfs_fs_info *fs_info, u64 seq) } } - spin_unlock(&fs_info->tree_mod_seq_lock); + read_unlock(&fs_info->tree_mod_log_lock); return ret; } diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index e12c37f457e056aa15b2a5c61d83b1d0f263d4a9..b5039b16de93a36ac59c1b6eddc072cb3bb21388 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -2031,7 +2031,7 @@ static void free_root_extent_buffers(struct btrfs_root *root) } /* helper to cleanup tree roots */ -static void free_root_pointers(struct btrfs_fs_info *info, int chunk_root) +static void free_root_pointers(struct btrfs_fs_info *info, bool free_chunk_root) { free_root_extent_buffers(info->tree_root); @@ -2040,7 +2040,7 @@ static void free_root_pointers(struct btrfs_fs_info *info, int chunk_root) free_root_extent_buffers(info->csum_root); free_root_extent_buffers(info->quota_root); free_root_extent_buffers(info->uuid_root); - if (chunk_root) + if (free_chunk_root) free_root_extent_buffers(info->chunk_root); free_root_extent_buffers(info->free_space_root); } @@ -2645,7 +2645,6 @@ int open_ctree(struct super_block *sb, spin_lock_init(&fs_info->fs_roots_radix_lock); spin_lock_init(&fs_info->delayed_iput_lock); spin_lock_init(&fs_info->defrag_inodes_lock); - spin_lock_init(&fs_info->tree_mod_seq_lock); spin_lock_init(&fs_info->super_lock); spin_lock_init(&fs_info->qgroup_op_lock); spin_lock_init(&fs_info->buffer_lock); @@ -3118,6 +3117,7 @@ int open_ctree(struct super_block *sb, /* do not make disk changes in broken FS or nologreplay is given */ if (btrfs_super_log_root(disk_super) != 0 && !btrfs_test_opt(fs_info, NOLOGREPLAY)) { + btrfs_info(fs_info, "start tree-log replay"); ret = btrfs_replay_log(fs_info, fs_devices); if (ret) { err = ret; @@ -3153,6 +3153,7 @@ int open_ctree(struct super_block *sb, if (IS_ERR(fs_info->fs_root)) { err = PTR_ERR(fs_info->fs_root); btrfs_warn(fs_info, "failed to read fs tree: %d", err); + fs_info->fs_root = NULL; goto fail_qgroup; } @@ -3274,7 +3275,7 @@ int open_ctree(struct super_block *sb, btrfs_put_block_group_cache(fs_info); fail_tree_roots: - free_root_pointers(fs_info, 1); + free_root_pointers(fs_info, true); invalidate_inode_pages2(fs_info->btree_inode->i_mapping); fail_sb_buffer: @@ -3302,7 +3303,7 @@ int open_ctree(struct super_block *sb, if (!btrfs_test_opt(fs_info, USEBACKUPROOT)) goto fail_tree_roots; - free_root_pointers(fs_info, 0); + free_root_pointers(fs_info, false); /* don't use the log in recovery mode, it won't be valid */ btrfs_set_super_log_root(disk_super, 0); @@ -3984,10 +3985,17 @@ void close_ctree(struct btrfs_fs_info *fs_info) invalidate_inode_pages2(fs_info->btree_inode->i_mapping); btrfs_stop_all_workers(fs_info); - btrfs_free_block_groups(fs_info); - clear_bit(BTRFS_FS_OPEN, &fs_info->flags); - free_root_pointers(fs_info, 1); + free_root_pointers(fs_info, true); + + /* + * We must free the block groups after dropping the fs_roots as we could + * have had an IO error and have left over tree log blocks that aren't + * cleaned up until the fs roots are freed. This makes the block group + * accounting appear to be wrong because there's pending reserved bytes, + * so make sure we do the block group cleanup afterwards. + */ + btrfs_free_block_groups(fs_info); iput(fs_info->btree_inode); @@ -4461,7 +4469,6 @@ void btrfs_cleanup_one_transaction(struct btrfs_transaction *cur_trans, wake_up(&fs_info->transaction_wait); btrfs_destroy_delayed_inodes(fs_info); - btrfs_assert_delayed_root_empty(fs_info); btrfs_destroy_marked_extents(fs_info, &cur_trans->dirty_pages, EXTENT_DIRTY); diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index fed44390c04923a84d57ba289199f953380bd1fe..11efb4f5041c7934d26355801a48298af84221a2 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -4014,6 +4014,14 @@ static int extent_write_cache_pages(struct address_space *mapping, */ scanned = 1; index = 0; + + /* + * If we're looping we could run into a page that is locked by a + * writer and that writer could be waiting on writeback for a + * page in our current bio, and thus deadlock, so flush the + * write bio here. + */ + flush_write_bio(epd); goto retry; } diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c index 6648d55e5339622c1a835873fe58d01d7aa24f30..813425df16f77af34ca2d2ba7408e3f92a45249b 100644 --- a/fs/btrfs/extent_map.c +++ b/fs/btrfs/extent_map.c @@ -228,6 +228,17 @@ static void try_merge_map(struct extent_map_tree *tree, struct extent_map *em) struct extent_map *merge = NULL; struct rb_node *rb; + /* + * We can't modify an extent map that is in the tree and that is being + * used by another task, as it can cause that other task to see it in + * inconsistent state during the merging. We always have 1 reference for + * the tree and 1 for this task (which is unpinning the extent map or + * clearing the logging flag), so anything > 2 means it's being used by + * other tasks too. + */ + if (refcount_read(&em->refs) > 2) + return; + if (em->start != 0) { rb = rb_prev(&em->rb_node); if (rb) diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c index 4cf2817ab120287765ac31dcbc498a8eb1a41a38..f9e280d0b44f35f8df2257065242741bea9b6256 100644 --- a/fs/btrfs/file-item.c +++ b/fs/btrfs/file-item.c @@ -275,7 +275,8 @@ static blk_status_t __btrfs_lookup_bio_sums(struct inode *inode, struct bio *bio csum += count * csum_size; nblocks -= count; next: - while (count--) { + while (count > 0) { + count--; disk_bytenr += fs_info->sectorsize; offset += fs_info->sectorsize; page_bytes_left -= fs_info->sectorsize; diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index d9d90f0b66d29bdd3cd5f0ca2366961bea1acc9b..c2c93fe9d7fd5805c0b4f142f7774e84589e6a66 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1895,7 +1895,7 @@ static ssize_t btrfs_file_write_iter(struct kiocb *iocb, bool sync = (file->f_flags & O_DSYNC) || IS_SYNC(file->f_mapping->host); ssize_t err; loff_t pos; - size_t count = iov_iter_count(from); + size_t count; loff_t oldsize; int clean_page = 0; @@ -1903,9 +1903,10 @@ static ssize_t btrfs_file_write_iter(struct kiocb *iocb, (iocb->ki_flags & IOCB_NOWAIT)) return -EOPNOTSUPP; - if (!inode_trylock(inode)) { - if (iocb->ki_flags & IOCB_NOWAIT) + if (iocb->ki_flags & IOCB_NOWAIT) { + if (!inode_trylock(inode)) return -EAGAIN; + } else { inode_lock(inode); } @@ -1916,6 +1917,7 @@ static ssize_t btrfs_file_write_iter(struct kiocb *iocb, } pos = iocb->ki_pos; + count = iov_iter_count(from); if (iocb->ki_flags & IOCB_NOWAIT) { /* * We will allocate space in case nodatacow is not set, diff --git a/fs/btrfs/inode-map.c b/fs/btrfs/inode-map.c index e1b50c62ba6501ab4ecfe53757cf179180cf09e7..c7dcb7c5210866a38e861b023629c32523d5eec9 100644 --- a/fs/btrfs/inode-map.c +++ b/fs/btrfs/inode-map.c @@ -12,6 +12,19 @@ #include "inode-map.h" #include "transaction.h" +static void fail_caching_thread(struct btrfs_root *root) +{ + struct btrfs_fs_info *fs_info = root->fs_info; + + btrfs_warn(fs_info, "failed to start inode caching task"); + btrfs_clear_pending_and_info(fs_info, INODE_MAP_CACHE, + "disabling inode map caching"); + spin_lock(&root->ino_cache_lock); + root->ino_cache_state = BTRFS_CACHE_ERROR; + spin_unlock(&root->ino_cache_lock); + wake_up(&root->ino_cache_wait); +} + static int caching_kthread(void *data) { struct btrfs_root *root = data; @@ -28,8 +41,10 @@ static int caching_kthread(void *data) return 0; path = btrfs_alloc_path(); - if (!path) + if (!path) { + fail_caching_thread(root); return -ENOMEM; + } /* Since the commit root is read-only, we can safely skip locking. */ path->skip_locking = 1; @@ -145,6 +160,7 @@ static void start_caching(struct btrfs_root *root) spin_lock(&root->ino_cache_lock); root->ino_cache_state = BTRFS_CACHE_FINISHED; spin_unlock(&root->ino_cache_lock); + wake_up(&root->ino_cache_wait); return; } @@ -163,11 +179,8 @@ static void start_caching(struct btrfs_root *root) tsk = kthread_run(caching_kthread, root, "btrfs-ino-cache-%llu", root->root_key.objectid); - if (IS_ERR(tsk)) { - btrfs_warn(fs_info, "failed to start inode caching task"); - btrfs_clear_pending_and_info(fs_info, INODE_MAP_CACHE, - "disabling inode map caching"); - } + if (IS_ERR(tsk)) + fail_caching_thread(root); } int btrfs_find_free_ino(struct btrfs_root *root, u64 *objectid) @@ -185,11 +198,14 @@ int btrfs_find_free_ino(struct btrfs_root *root, u64 *objectid) wait_event(root->ino_cache_wait, root->ino_cache_state == BTRFS_CACHE_FINISHED || + root->ino_cache_state == BTRFS_CACHE_ERROR || root->free_ino_ctl->free_space > 0); if (root->ino_cache_state == BTRFS_CACHE_FINISHED && root->free_ino_ctl->free_space == 0) return -ENOSPC; + else if (root->ino_cache_state == BTRFS_CACHE_ERROR) + return btrfs_find_free_objectid(root, objectid); else goto again; } diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 70085e98d372ed70144bf309e46362c019cb3147..dec508a28ffa9679fa75e274a45d33fecc1fea02 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4120,18 +4120,30 @@ static int btrfs_unlink(struct inode *dir, struct dentry *dentry) } static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans, - struct inode *dir, u64 objectid, - const char *name, int name_len) + struct inode *dir, struct dentry *dentry) { struct btrfs_root *root = BTRFS_I(dir)->root; + struct btrfs_inode *inode = BTRFS_I(d_inode(dentry)); struct btrfs_path *path; struct extent_buffer *leaf; struct btrfs_dir_item *di; struct btrfs_key key; + const char *name = dentry->d_name.name; + int name_len = dentry->d_name.len; u64 index; int ret; + u64 objectid; u64 dir_ino = btrfs_ino(BTRFS_I(dir)); + if (btrfs_ino(inode) == BTRFS_FIRST_FREE_OBJECTID) { + objectid = inode->root->root_key.objectid; + } else if (btrfs_ino(inode) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID) { + objectid = inode->location.objectid; + } else { + WARN_ON(1); + return -EINVAL; + } + path = btrfs_alloc_path(); if (!path) return -ENOMEM; @@ -4156,13 +4168,16 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans, } btrfs_release_path(path); - ret = btrfs_del_root_ref(trans, objectid, root->root_key.objectid, - dir_ino, &index, name, name_len); - if (ret < 0) { - if (ret != -ENOENT) { - btrfs_abort_transaction(trans, ret); - goto out; - } + /* + * This is a placeholder inode for a subvolume we didn't have a + * reference to at the time of the snapshot creation. In the meantime + * we could have renamed the real subvol link into our snapshot, so + * depending on btrfs_del_root_ref to return -ENOENT here is incorret. + * Instead simply lookup the dir_index_item for this entry so we can + * remove it. Otherwise we know we have a ref to the root and we can + * call btrfs_del_root_ref, and it _shouldn't_ fail. + */ + if (btrfs_ino(inode) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID) { di = btrfs_search_dir_index_item(root, path, dir_ino, name, name_len); if (IS_ERR_OR_NULL(di)) { @@ -4177,8 +4192,16 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans, leaf = path->nodes[0]; btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); index = key.offset; + btrfs_release_path(path); + } else { + ret = btrfs_del_root_ref(trans, objectid, + root->root_key.objectid, dir_ino, + &index, name, name_len); + if (ret) { + btrfs_abort_transaction(trans, ret); + goto out; + } } - btrfs_release_path(path); ret = btrfs_delete_delayed_dir_index(trans, BTRFS_I(dir), index); if (ret) { @@ -4373,8 +4396,7 @@ int btrfs_delete_subvolume(struct inode *dir, struct dentry *dentry) btrfs_record_snapshot_destroy(trans, BTRFS_I(dir)); - ret = btrfs_unlink_subvol(trans, dir, dest->root_key.objectid, - dentry->d_name.name, dentry->d_name.len); + ret = btrfs_unlink_subvol(trans, dir, dentry); if (ret) { err = ret; btrfs_abort_transaction(trans, ret); @@ -4469,10 +4491,7 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry) return PTR_ERR(trans); if (unlikely(btrfs_ino(BTRFS_I(inode)) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID)) { - err = btrfs_unlink_subvol(trans, dir, - BTRFS_I(inode)->location.objectid, - dentry->d_name.name, - dentry->d_name.len); + err = btrfs_unlink_subvol(trans, dir, dentry); goto out; } @@ -9471,7 +9490,6 @@ static int btrfs_rename_exchange(struct inode *old_dir, u64 new_ino = btrfs_ino(BTRFS_I(new_inode)); u64 old_idx = 0; u64 new_idx = 0; - u64 root_objectid; int ret; bool root_log_pinned = false; bool dest_log_pinned = false; @@ -9577,10 +9595,7 @@ static int btrfs_rename_exchange(struct inode *old_dir, /* src is a subvolume */ if (old_ino == BTRFS_FIRST_FREE_OBJECTID) { - root_objectid = BTRFS_I(old_inode)->root->root_key.objectid; - ret = btrfs_unlink_subvol(trans, old_dir, root_objectid, - old_dentry->d_name.name, - old_dentry->d_name.len); + ret = btrfs_unlink_subvol(trans, old_dir, old_dentry); } else { /* src is an inode */ ret = __btrfs_unlink_inode(trans, root, BTRFS_I(old_dir), BTRFS_I(old_dentry->d_inode), @@ -9596,10 +9611,7 @@ static int btrfs_rename_exchange(struct inode *old_dir, /* dest is a subvolume */ if (new_ino == BTRFS_FIRST_FREE_OBJECTID) { - root_objectid = BTRFS_I(new_inode)->root->root_key.objectid; - ret = btrfs_unlink_subvol(trans, new_dir, root_objectid, - new_dentry->d_name.name, - new_dentry->d_name.len); + ret = btrfs_unlink_subvol(trans, new_dir, new_dentry); } else { /* dest is an inode */ ret = __btrfs_unlink_inode(trans, dest, BTRFS_I(new_dir), BTRFS_I(new_dentry->d_inode), @@ -9797,7 +9809,6 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_inode = d_inode(new_dentry); struct inode *old_inode = d_inode(old_dentry); u64 index = 0; - u64 root_objectid; int ret; u64 old_ino = btrfs_ino(BTRFS_I(old_inode)); bool log_pinned = false; @@ -9905,10 +9916,7 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, BTRFS_I(old_inode), 1); if (unlikely(old_ino == BTRFS_FIRST_FREE_OBJECTID)) { - root_objectid = BTRFS_I(old_inode)->root->root_key.objectid; - ret = btrfs_unlink_subvol(trans, old_dir, root_objectid, - old_dentry->d_name.name, - old_dentry->d_name.len); + ret = btrfs_unlink_subvol(trans, old_dir, old_dentry); } else { ret = __btrfs_unlink_inode(trans, root, BTRFS_I(old_dir), BTRFS_I(d_inode(old_dentry)), @@ -9927,10 +9935,7 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, new_inode->i_ctime = current_time(new_inode); if (unlikely(btrfs_ino(BTRFS_I(new_inode)) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID)) { - root_objectid = BTRFS_I(new_inode)->location.objectid; - ret = btrfs_unlink_subvol(trans, new_dir, root_objectid, - new_dentry->d_name.name, - new_dentry->d_name.len); + ret = btrfs_unlink_subvol(trans, new_dir, new_dentry); BUG_ON(new_inode->i_nlink == 0); } else { ret = btrfs_unlink_inode(trans, dest, BTRFS_I(new_dir), @@ -10343,6 +10348,7 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode, struct btrfs_root *root = BTRFS_I(inode)->root; struct btrfs_key ins; u64 cur_offset = start; + u64 clear_offset = start; u64 i_size; u64 cur_bytes; u64 last_alloc = (u64)-1; @@ -10377,6 +10383,15 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode, btrfs_end_transaction(trans); break; } + + /* + * We've reserved this space, and thus converted it from + * ->bytes_may_use to ->bytes_reserved. Any error that happens + * from here on out we will only need to clear our reservation + * for the remaining unreserved area, so advance our + * clear_offset by our extent size. + */ + clear_offset += ins.offset; btrfs_dec_block_group_reservations(fs_info, ins.objectid); last_alloc = ins.offset; @@ -10457,9 +10472,9 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode, if (own_trans) btrfs_end_transaction(trans); } - if (cur_offset < end) - btrfs_free_reserved_data_space(inode, NULL, cur_offset, - end - cur_offset + 1); + if (clear_offset < end) + btrfs_free_reserved_data_space(inode, NULL, clear_offset, + end - clear_offset + 1); return ret; } diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c index 0c4ef208b8b91cc54c934d25960b41522c438e54..0f6d53ec78ed717ec832f2b449eaeb202e882d1d 100644 --- a/fs/btrfs/ordered-data.c +++ b/fs/btrfs/ordered-data.c @@ -712,10 +712,15 @@ int btrfs_wait_ordered_range(struct inode *inode, u64 start, u64 len) } btrfs_start_ordered_extent(inode, ordered, 1); end = ordered->file_offset; + /* + * If the ordered extent had an error save the error but don't + * exit without waiting first for all other ordered extents in + * the range to complete. + */ if (test_bit(BTRFS_ORDERED_IOERR, &ordered->flags)) ret = -EIO; btrfs_put_ordered_extent(ordered); - if (ret || end == 0 || end == start) + if (end == 0 || end == start) break; end--; } diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index 7916f711daf531628c2fac98b0e83e3642e4cf90..0cd043f03081e1ad3df197b6bdbd1273d53bf9ad 100644 --- a/fs/btrfs/qgroup.c +++ b/fs/btrfs/qgroup.c @@ -2055,8 +2055,12 @@ int btrfs_qgroup_account_extent(struct btrfs_trans_handle *trans, u64 bytenr, u64 nr_old_roots = 0; int ret = 0; + /* + * If quotas get disabled meanwhile, the resouces need to be freed and + * we can't just exit here. + */ if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags)) - return 0; + goto out_free; if (new_roots) { if (!maybe_fs_roots(new_roots)) diff --git a/fs/btrfs/ref-verify.c b/fs/btrfs/ref-verify.c index cd2a5864e103cabed50efdc03ba50ebe100c7a5b..dbc685ca017f2fe899da26ceaf637ad47eab7940 100644 --- a/fs/btrfs/ref-verify.c +++ b/fs/btrfs/ref-verify.c @@ -747,6 +747,7 @@ int btrfs_ref_tree_mod(struct btrfs_root *root, u64 bytenr, u64 num_bytes, */ be = add_block_entry(root->fs_info, bytenr, num_bytes, ref_root); if (IS_ERR(be)) { + kfree(ref); kfree(ra); ret = PTR_ERR(be); goto out; @@ -760,6 +761,8 @@ int btrfs_ref_tree_mod(struct btrfs_root *root, u64 bytenr, u64 num_bytes, "re-allocated a block that still has references to it!"); dump_block_entry(fs_info, be); dump_ref_action(fs_info, ra); + kfree(ref); + kfree(ra); goto out_unlock; } @@ -822,6 +825,7 @@ int btrfs_ref_tree_mod(struct btrfs_root *root, u64 bytenr, u64 num_bytes, "dropping a ref for a existing root that doesn't have a ref on the block"); dump_block_entry(fs_info, be); dump_ref_action(fs_info, ra); + kfree(ref); kfree(ra); goto out_unlock; } @@ -837,6 +841,7 @@ int btrfs_ref_tree_mod(struct btrfs_root *root, u64 bytenr, u64 num_bytes, "attempting to add another ref for an existing ref on a tree block"); dump_block_entry(fs_info, be); dump_ref_action(fs_info, ra); + kfree(ref); kfree(ra); goto out_unlock; } diff --git a/fs/btrfs/root-tree.c b/fs/btrfs/root-tree.c index 3228d3b3084a4f154e065da29661f70b378c0b0a..b309f298d17f4bb944f15ad95cd2e61c938ef0c4 100644 --- a/fs/btrfs/root-tree.c +++ b/fs/btrfs/root-tree.c @@ -370,11 +370,13 @@ int btrfs_del_root_ref(struct btrfs_trans_handle *trans, u64 root_id, leaf = path->nodes[0]; ref = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_root_ref); - - WARN_ON(btrfs_root_ref_dirid(leaf, ref) != dirid); - WARN_ON(btrfs_root_ref_name_len(leaf, ref) != name_len); ptr = (unsigned long)(ref + 1); - WARN_ON(memcmp_extent_buffer(leaf, name, ptr, name_len)); + if ((btrfs_root_ref_dirid(leaf, ref) != dirid) || + (btrfs_root_ref_name_len(leaf, ref) != name_len) || + memcmp_extent_buffer(leaf, name, ptr, name_len)) { + err = -ENOENT; + goto out; + } *sequence = btrfs_root_ref_sequence(leaf, ref); ret = btrfs_del_item(trans, tree_root, path); diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index ddbad8d5094906af82109ad196dcc43203dd3422..6a5b16a119eda27c80a746b0c50948ba21c889fc 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -1857,6 +1857,8 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data) } if (btrfs_super_log_root(fs_info->super_copy) != 0) { + btrfs_warn(fs_info, + "mount required to replay tree-log, cannot remount read-write"); ret = -EINVAL; goto restore; } @@ -2167,7 +2169,15 @@ static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf) */ thresh = SZ_4M; - if (!mixed && total_free_meta - thresh < block_rsv->size) + /* + * We only want to claim there's no available space if we can no longer + * allocate chunks for our metadata profile and our global reserve will + * not fit in the free metadata space. If we aren't ->full then we + * still can allocate chunks and thus are fine using the currently + * calculated f_bavail. + */ + if (!mixed && block_rsv->space_info->full && + total_free_meta - thresh < block_rsv->size) buf->f_bavail = 0; buf->f_type = BTRFS_SUPER_MAGIC; diff --git a/fs/btrfs/tests/btrfs-tests.c b/fs/btrfs/tests/btrfs-tests.c index db72b3b6209e000e8d4a1a94507ed9185715dd28..2eec1dd3803af1503e99d6c6c99c575e66a9bc33 100644 --- a/fs/btrfs/tests/btrfs-tests.c +++ b/fs/btrfs/tests/btrfs-tests.c @@ -102,7 +102,6 @@ struct btrfs_fs_info *btrfs_alloc_dummy_fs_info(u32 nodesize, u32 sectorsize) spin_lock_init(&fs_info->qgroup_op_lock); spin_lock_init(&fs_info->super_lock); spin_lock_init(&fs_info->fs_roots_radix_lock); - spin_lock_init(&fs_info->tree_mod_seq_lock); mutex_init(&fs_info->qgroup_ioctl_lock); mutex_init(&fs_info->qgroup_rescan_lock); rwlock_init(&fs_info->tree_mod_log_lock); diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 26317bca56499a406492dd84e4a602e2ee00f1eb..4b1491e1b80320c0d196e8e1cdf13f629a70c220 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -1936,6 +1936,14 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) struct btrfs_transaction *prev_trans = NULL; int ret; + /* + * Some places just start a transaction to commit it. We need to make + * sure that if this commit fails that the abort code actually marks the + * transaction as failed, so set trans->dirty to make the abort code do + * the right thing. + */ + trans->dirty = true; + /* Stop the commit early if ->aborted is set */ if (unlikely(READ_ONCE(cur_trans->aborted))) { ret = cur_trans->aborted; diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index fe7165c9d87559623fba2dbcf77f27fd6b619a02..d4c86c6cbe39537d2d59446d6292d006d97ae26a 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -3892,7 +3892,7 @@ static int log_csums(struct btrfs_trans_handle *trans, static noinline int copy_items(struct btrfs_trans_handle *trans, struct btrfs_inode *inode, struct btrfs_path *dst_path, - struct btrfs_path *src_path, u64 *last_extent, + struct btrfs_path *src_path, int start_slot, int nr, int inode_only, u64 logged_isize) { @@ -3903,7 +3903,6 @@ static noinline int copy_items(struct btrfs_trans_handle *trans, struct btrfs_file_extent_item *extent; struct btrfs_inode_item *inode_item; struct extent_buffer *src = src_path->nodes[0]; - struct btrfs_key first_key, last_key, key; int ret; struct btrfs_key *ins_keys; u32 *ins_sizes; @@ -3911,9 +3910,6 @@ static noinline int copy_items(struct btrfs_trans_handle *trans, int i; struct list_head ordered_sums; int skip_csum = inode->flags & BTRFS_INODE_NODATASUM; - bool has_extents = false; - bool need_find_last_extent = true; - bool done = false; INIT_LIST_HEAD(&ordered_sums); @@ -3922,8 +3918,6 @@ static noinline int copy_items(struct btrfs_trans_handle *trans, if (!ins_data) return -ENOMEM; - first_key.objectid = (u64)-1; - ins_sizes = (u32 *)ins_data; ins_keys = (struct btrfs_key *)(ins_data + nr * sizeof(u32)); @@ -3944,9 +3938,6 @@ static noinline int copy_items(struct btrfs_trans_handle *trans, src_offset = btrfs_item_ptr_offset(src, start_slot + i); - if (i == nr - 1) - last_key = ins_keys[i]; - if (ins_keys[i].type == BTRFS_INODE_ITEM_KEY) { inode_item = btrfs_item_ptr(dst_path->nodes[0], dst_path->slots[0], @@ -3960,20 +3951,6 @@ static noinline int copy_items(struct btrfs_trans_handle *trans, src_offset, ins_sizes[i]); } - /* - * We set need_find_last_extent here in case we know we were - * processing other items and then walk into the first extent in - * the inode. If we don't hit an extent then nothing changes, - * we'll do the last search the next time around. - */ - if (ins_keys[i].type == BTRFS_EXTENT_DATA_KEY) { - has_extents = true; - if (first_key.objectid == (u64)-1) - first_key = ins_keys[i]; - } else { - need_find_last_extent = false; - } - /* take a reference on file data extents so that truncates * or deletes of this inode don't have to relog the inode * again @@ -4039,167 +4016,6 @@ static noinline int copy_items(struct btrfs_trans_handle *trans, kfree(sums); } - if (!has_extents) - return ret; - - if (need_find_last_extent && *last_extent == first_key.offset) { - /* - * We don't have any leafs between our current one and the one - * we processed before that can have file extent items for our - * inode (and have a generation number smaller than our current - * transaction id). - */ - need_find_last_extent = false; - } - - /* - * Because we use btrfs_search_forward we could skip leaves that were - * not modified and then assume *last_extent is valid when it really - * isn't. So back up to the previous leaf and read the end of the last - * extent before we go and fill in holes. - */ - if (need_find_last_extent) { - u64 len; - - ret = btrfs_prev_leaf(inode->root, src_path); - if (ret < 0) - return ret; - if (ret) - goto fill_holes; - if (src_path->slots[0]) - src_path->slots[0]--; - src = src_path->nodes[0]; - btrfs_item_key_to_cpu(src, &key, src_path->slots[0]); - if (key.objectid != btrfs_ino(inode) || - key.type != BTRFS_EXTENT_DATA_KEY) - goto fill_holes; - extent = btrfs_item_ptr(src, src_path->slots[0], - struct btrfs_file_extent_item); - if (btrfs_file_extent_type(src, extent) == - BTRFS_FILE_EXTENT_INLINE) { - len = btrfs_file_extent_ram_bytes(src, extent); - *last_extent = ALIGN(key.offset + len, - fs_info->sectorsize); - } else { - len = btrfs_file_extent_num_bytes(src, extent); - *last_extent = key.offset + len; - } - } -fill_holes: - /* So we did prev_leaf, now we need to move to the next leaf, but a few - * things could have happened - * - * 1) A merge could have happened, so we could currently be on a leaf - * that holds what we were copying in the first place. - * 2) A split could have happened, and now not all of the items we want - * are on the same leaf. - * - * So we need to adjust how we search for holes, we need to drop the - * path and re-search for the first extent key we found, and then walk - * forward until we hit the last one we copied. - */ - if (need_find_last_extent) { - /* btrfs_prev_leaf could return 1 without releasing the path */ - btrfs_release_path(src_path); - ret = btrfs_search_slot(NULL, inode->root, &first_key, - src_path, 0, 0); - if (ret < 0) - return ret; - ASSERT(ret == 0); - src = src_path->nodes[0]; - i = src_path->slots[0]; - } else { - i = start_slot; - } - - /* - * Ok so here we need to go through and fill in any holes we may have - * to make sure that holes are punched for those areas in case they had - * extents previously. - */ - while (!done) { - u64 offset, len; - u64 extent_end; - - if (i >= btrfs_header_nritems(src_path->nodes[0])) { - ret = btrfs_next_leaf(inode->root, src_path); - if (ret < 0) - return ret; - ASSERT(ret == 0); - src = src_path->nodes[0]; - i = 0; - need_find_last_extent = true; - } - - btrfs_item_key_to_cpu(src, &key, i); - if (!btrfs_comp_cpu_keys(&key, &last_key)) - done = true; - if (key.objectid != btrfs_ino(inode) || - key.type != BTRFS_EXTENT_DATA_KEY) { - i++; - continue; - } - extent = btrfs_item_ptr(src, i, struct btrfs_file_extent_item); - if (btrfs_file_extent_type(src, extent) == - BTRFS_FILE_EXTENT_INLINE) { - len = btrfs_file_extent_ram_bytes(src, extent); - extent_end = ALIGN(key.offset + len, - fs_info->sectorsize); - } else { - len = btrfs_file_extent_num_bytes(src, extent); - extent_end = key.offset + len; - } - i++; - - if (*last_extent == key.offset) { - *last_extent = extent_end; - continue; - } - offset = *last_extent; - len = key.offset - *last_extent; - ret = btrfs_insert_file_extent(trans, log, btrfs_ino(inode), - offset, 0, 0, len, 0, len, 0, 0, 0); - if (ret) - break; - *last_extent = extent_end; - } - - /* - * Check if there is a hole between the last extent found in our leaf - * and the first extent in the next leaf. If there is one, we need to - * log an explicit hole so that at replay time we can punch the hole. - */ - if (ret == 0 && - key.objectid == btrfs_ino(inode) && - key.type == BTRFS_EXTENT_DATA_KEY && - i == btrfs_header_nritems(src_path->nodes[0])) { - ret = btrfs_next_leaf(inode->root, src_path); - need_find_last_extent = true; - if (ret > 0) { - ret = 0; - } else if (ret == 0) { - btrfs_item_key_to_cpu(src_path->nodes[0], &key, - src_path->slots[0]); - if (key.objectid == btrfs_ino(inode) && - key.type == BTRFS_EXTENT_DATA_KEY && - *last_extent < key.offset) { - const u64 len = key.offset - *last_extent; - - ret = btrfs_insert_file_extent(trans, log, - btrfs_ino(inode), - *last_extent, 0, - 0, len, 0, len, - 0, 0, 0); - *last_extent += len; - } - } - } - /* - * Need to let the callers know we dropped the path so they should - * re-search. - */ - if (!ret && need_find_last_extent) - ret = 1; return ret; } @@ -4365,7 +4181,7 @@ static int btrfs_log_prealloc_extents(struct btrfs_trans_handle *trans, const u64 i_size = i_size_read(&inode->vfs_inode); const u64 ino = btrfs_ino(inode); struct btrfs_path *dst_path = NULL; - u64 last_extent = (u64)-1; + bool dropped_extents = false; int ins_nr = 0; int start_slot; int ret; @@ -4387,8 +4203,7 @@ static int btrfs_log_prealloc_extents(struct btrfs_trans_handle *trans, if (slot >= btrfs_header_nritems(leaf)) { if (ins_nr > 0) { ret = copy_items(trans, inode, dst_path, path, - &last_extent, start_slot, - ins_nr, 1, 0); + start_slot, ins_nr, 1, 0); if (ret < 0) goto out; ins_nr = 0; @@ -4412,8 +4227,7 @@ static int btrfs_log_prealloc_extents(struct btrfs_trans_handle *trans, path->slots[0]++; continue; } - if (last_extent == (u64)-1) { - last_extent = key.offset; + if (!dropped_extents) { /* * Avoid logging extent items logged in past fsync calls * and leading to duplicate keys in the log tree. @@ -4427,6 +4241,7 @@ static int btrfs_log_prealloc_extents(struct btrfs_trans_handle *trans, } while (ret == -EAGAIN); if (ret) goto out; + dropped_extents = true; } if (ins_nr == 0) start_slot = slot; @@ -4441,7 +4256,7 @@ static int btrfs_log_prealloc_extents(struct btrfs_trans_handle *trans, } } if (ins_nr > 0) { - ret = copy_items(trans, inode, dst_path, path, &last_extent, + ret = copy_items(trans, inode, dst_path, path, start_slot, ins_nr, 1, 0); if (ret > 0) ret = 0; @@ -4636,13 +4451,8 @@ static int btrfs_log_all_xattrs(struct btrfs_trans_handle *trans, if (slot >= nritems) { if (ins_nr > 0) { - u64 last_extent = 0; - ret = copy_items(trans, inode, dst_path, path, - &last_extent, start_slot, - ins_nr, 1, 0); - /* can't be 1, extent items aren't processed */ - ASSERT(ret <= 0); + start_slot, ins_nr, 1, 0); if (ret < 0) return ret; ins_nr = 0; @@ -4666,13 +4476,8 @@ static int btrfs_log_all_xattrs(struct btrfs_trans_handle *trans, cond_resched(); } if (ins_nr > 0) { - u64 last_extent = 0; - ret = copy_items(trans, inode, dst_path, path, - &last_extent, start_slot, - ins_nr, 1, 0); - /* can't be 1, extent items aren't processed */ - ASSERT(ret <= 0); + start_slot, ins_nr, 1, 0); if (ret < 0) return ret; } @@ -4681,100 +4486,119 @@ static int btrfs_log_all_xattrs(struct btrfs_trans_handle *trans, } /* - * If the no holes feature is enabled we need to make sure any hole between the - * last extent and the i_size of our inode is explicitly marked in the log. This - * is to make sure that doing something like: - * - * 1) create file with 128Kb of data - * 2) truncate file to 64Kb - * 3) truncate file to 256Kb - * 4) fsync file - * 5) - * 6) mount fs and trigger log replay - * - * Will give us a file with a size of 256Kb, the first 64Kb of data match what - * the file had in its first 64Kb of data at step 1 and the last 192Kb of the - * file correspond to a hole. The presence of explicit holes in a log tree is - * what guarantees that log replay will remove/adjust file extent items in the - * fs/subvol tree. - * - * Here we do not need to care about holes between extents, that is already done - * by copy_items(). We also only need to do this in the full sync path, where we - * lookup for extents from the fs/subvol tree only. In the fast path case, we - * lookup the list of modified extent maps and if any represents a hole, we - * insert a corresponding extent representing a hole in the log tree. + * When using the NO_HOLES feature if we punched a hole that causes the + * deletion of entire leafs or all the extent items of the first leaf (the one + * that contains the inode item and references) we may end up not processing + * any extents, because there are no leafs with a generation matching the + * current transaction that have extent items for our inode. So we need to find + * if any holes exist and then log them. We also need to log holes after any + * truncate operation that changes the inode's size. */ -static int btrfs_log_trailing_hole(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct btrfs_inode *inode, - struct btrfs_path *path) +static int btrfs_log_holes(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_inode *inode, + struct btrfs_path *path) { struct btrfs_fs_info *fs_info = root->fs_info; - int ret; struct btrfs_key key; - u64 hole_start; - u64 hole_size; - struct extent_buffer *leaf; - struct btrfs_root *log = root->log_root; const u64 ino = btrfs_ino(inode); const u64 i_size = i_size_read(&inode->vfs_inode); + u64 prev_extent_end = 0; + int ret; - if (!btrfs_fs_incompat(fs_info, NO_HOLES)) + if (!btrfs_fs_incompat(fs_info, NO_HOLES) || i_size == 0) return 0; key.objectid = ino; key.type = BTRFS_EXTENT_DATA_KEY; - key.offset = (u64)-1; + key.offset = 0; ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); - ASSERT(ret != 0); if (ret < 0) return ret; - ASSERT(path->slots[0] > 0); - path->slots[0]--; - leaf = path->nodes[0]; - btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); - - if (key.objectid != ino || key.type != BTRFS_EXTENT_DATA_KEY) { - /* inode does not have any extents */ - hole_start = 0; - hole_size = i_size; - } else { + while (true) { struct btrfs_file_extent_item *extent; + struct extent_buffer *leaf = path->nodes[0]; u64 len; - /* - * If there's an extent beyond i_size, an explicit hole was - * already inserted by copy_items(). - */ - if (key.offset >= i_size) - return 0; + if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) { + ret = btrfs_next_leaf(root, path); + if (ret < 0) + return ret; + if (ret > 0) { + ret = 0; + break; + } + leaf = path->nodes[0]; + } + + btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); + if (key.objectid != ino || key.type != BTRFS_EXTENT_DATA_KEY) + break; + + /* We have a hole, log it. */ + if (prev_extent_end < key.offset) { + const u64 hole_len = key.offset - prev_extent_end; + + /* + * Release the path to avoid deadlocks with other code + * paths that search the root while holding locks on + * leafs from the log root. + */ + btrfs_release_path(path); + ret = btrfs_insert_file_extent(trans, root->log_root, + ino, prev_extent_end, 0, + 0, hole_len, 0, hole_len, + 0, 0, 0); + if (ret < 0) + return ret; + + /* + * Search for the same key again in the root. Since it's + * an extent item and we are holding the inode lock, the + * key must still exist. If it doesn't just emit warning + * and return an error to fall back to a transaction + * commit. + */ + ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + if (ret < 0) + return ret; + if (WARN_ON(ret > 0)) + return -ENOENT; + leaf = path->nodes[0]; + } extent = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_file_extent_item); - if (btrfs_file_extent_type(leaf, extent) == - BTRFS_FILE_EXTENT_INLINE) - return 0; + BTRFS_FILE_EXTENT_INLINE) { + len = btrfs_file_extent_ram_bytes(leaf, extent); + prev_extent_end = ALIGN(key.offset + len, + fs_info->sectorsize); + } else { + len = btrfs_file_extent_num_bytes(leaf, extent); + prev_extent_end = key.offset + len; + } - len = btrfs_file_extent_num_bytes(leaf, extent); - /* Last extent goes beyond i_size, no need to log a hole. */ - if (key.offset + len > i_size) - return 0; - hole_start = key.offset + len; - hole_size = i_size - hole_start; + path->slots[0]++; + cond_resched(); } - btrfs_release_path(path); - /* Last extent ends at i_size. */ - if (hole_size == 0) - return 0; + if (prev_extent_end < i_size) { + u64 hole_len; - hole_size = ALIGN(hole_size, fs_info->sectorsize); - ret = btrfs_insert_file_extent(trans, log, ino, hole_start, 0, 0, - hole_size, 0, hole_size, 0, 0, 0); - return ret; + btrfs_release_path(path); + hole_len = ALIGN(i_size - prev_extent_end, fs_info->sectorsize); + ret = btrfs_insert_file_extent(trans, root->log_root, + ino, prev_extent_end, 0, 0, + hole_len, 0, hole_len, + 0, 0, 0); + if (ret < 0) + return ret; + } + + return 0; } /* @@ -4934,7 +4758,6 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans, struct btrfs_key min_key; struct btrfs_key max_key; struct btrfs_root *log = root->log_root; - u64 last_extent = 0; int err = 0; int ret; int nritems; @@ -5108,7 +4931,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans, ins_start_slot = path->slots[0]; } ret = copy_items(trans, inode, dst_path, path, - &last_extent, ins_start_slot, + ins_start_slot, ins_nr, inode_only, logged_isize); if (ret < 0) { @@ -5161,17 +4984,13 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans, if (ins_nr == 0) goto next_slot; ret = copy_items(trans, inode, dst_path, path, - &last_extent, ins_start_slot, + ins_start_slot, ins_nr, inode_only, logged_isize); if (ret < 0) { err = ret; goto out_unlock; } ins_nr = 0; - if (ret) { - btrfs_release_path(path); - continue; - } goto next_slot; } @@ -5184,18 +5003,13 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans, goto next_slot; } - ret = copy_items(trans, inode, dst_path, path, &last_extent, + ret = copy_items(trans, inode, dst_path, path, ins_start_slot, ins_nr, inode_only, logged_isize); if (ret < 0) { err = ret; goto out_unlock; } - if (ret) { - ins_nr = 0; - btrfs_release_path(path); - continue; - } ins_nr = 1; ins_start_slot = path->slots[0]; next_slot: @@ -5209,13 +5023,12 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans, } if (ins_nr) { ret = copy_items(trans, inode, dst_path, path, - &last_extent, ins_start_slot, + ins_start_slot, ins_nr, inode_only, logged_isize); if (ret < 0) { err = ret; goto out_unlock; } - ret = 0; ins_nr = 0; } btrfs_release_path(path); @@ -5230,14 +5043,13 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans, } } if (ins_nr) { - ret = copy_items(trans, inode, dst_path, path, &last_extent, + ret = copy_items(trans, inode, dst_path, path, ins_start_slot, ins_nr, inode_only, logged_isize); if (ret < 0) { err = ret; goto out_unlock; } - ret = 0; ins_nr = 0; } @@ -5250,7 +5062,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans, if (max_key.type >= BTRFS_EXTENT_DATA_KEY && !fast_search) { btrfs_release_path(path); btrfs_release_path(dst_path); - err = btrfs_log_trailing_hole(trans, root, inode, path); + err = btrfs_log_holes(trans, root, inode, path); if (err) goto out_unlock; } diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 5bbcdcff68a9eb26c9e016bb3de2f75856d1d858..9c3b394b99fa2c497e8b0f56adc968ea0563421d 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -7260,6 +7260,8 @@ int btrfs_get_dev_stats(struct btrfs_fs_info *fs_info, else btrfs_dev_stat_reset(dev, i); } + btrfs_info(fs_info, "device stats zeroed by %s (%d)", + current->comm, task_pid_nr(current)); } else { for (i = 0; i < BTRFS_DEV_STAT_VALUES_MAX; i++) if (stats->nr_items > i) diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 09db6d08614d235797745951980c685e282cdf75..a2e903203bf9fbc6904fa93dd85c126cfae29d36 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -2343,8 +2343,7 @@ static void __do_request(struct ceph_mds_client *mdsc, if (!(mdsc->fsc->mount_options->flags & CEPH_MOUNT_OPT_MOUNTWAIT) && !ceph_mdsmap_is_cluster_available(mdsc->mdsmap)) { - err = -ENOENT; - pr_info("probably no mds server is up\n"); + err = -EHOSTUNREACH; goto finish; } } diff --git a/fs/ceph/super.c b/fs/ceph/super.c index 2bd0b1ed9708e862e0cab012043c0a33051d41a7..c4314f4492401453debfa4df6bf520dad0ffec10 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -1106,6 +1106,11 @@ static struct dentry *ceph_mount(struct file_system_type *fs_type, return res; out_splat: + if (!ceph_mdsmap_is_cluster_available(fsc->mdsc->mdsmap)) { + pr_info("No mds server is up or the cluster is laggy\n"); + err = -EHOSTUNREACH; + } + ceph_mdsc_close_sessions(fsc->mdsc); deactivate_locked_super(sb); goto out_final; diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index 5e4f3f833e85e3ab8002dcd79fe4c92de4292d4a..a09ce27ab2204ae963c154373cef9d8958060192 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c @@ -221,7 +221,7 @@ static size_t ceph_vxattrcb_dir_rbytes(struct ceph_inode_info *ci, char *val, static size_t ceph_vxattrcb_dir_rctime(struct ceph_inode_info *ci, char *val, size_t size) { - return snprintf(val, size, "%lld.09%ld", ci->i_rctime.tv_sec, + return snprintf(val, size, "%lld.%09ld", ci->i_rctime.tv_sec, ci->i_rctime.tv_nsec); } diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index 1d377b7f286055fa0b8c2117c89f843704fb9c3c..130bdca9e568062a0111d051577b16f26e250b59 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -603,7 +603,7 @@ static void access_flags_to_mode(__le32 ace_flags, int type, umode_t *pmode, ((flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS)) *pmode |= (S_IXUGO & (*pbits_to_set)); - cifs_dbg(NOISY, "access flags 0x%x mode now 0x%x\n", flags, *pmode); + cifs_dbg(NOISY, "access flags 0x%x mode now %04o\n", flags, *pmode); return; } @@ -632,7 +632,7 @@ static void mode_to_access_flags(umode_t mode, umode_t bits_to_use, if (mode & S_IXUGO) *pace_flags |= SET_FILE_EXEC_RIGHTS; - cifs_dbg(NOISY, "mode: 0x%x, access flags now 0x%x\n", + cifs_dbg(NOISY, "mode: %04o, access flags now 0x%x\n", mode, *pace_flags); return; } diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 7e85070d010f4ee006a4c64178e9f17bd274a76c..975f800b9dd4d169dece68e33a1c3bb0a05859e8 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -970,6 +970,7 @@ cifs_demultiplex_thread(void *p) mempool_resize(cifs_req_poolp, length + cifs_min_rcv); set_freezable(); + allow_kernel_signal(SIGKILL); while (server->tcpStatus != CifsExiting) { if (try_to_freeze()) continue; @@ -2454,7 +2455,7 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect) task = xchg(&server->tsk, NULL); if (task) - force_sig(SIGKILL, task); + send_sig(SIGKILL, task, 1); } static struct TCP_Server_Info * @@ -3341,8 +3342,10 @@ match_prepath(struct super_block *sb, struct cifs_mnt_data *mnt_data) { struct cifs_sb_info *old = CIFS_SB(sb); struct cifs_sb_info *new = mnt_data->cifs_sb; - bool old_set = old->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH; - bool new_set = new->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH; + bool old_set = (old->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) && + old->prepath; + bool new_set = (new->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) && + new->prepath; if (old_set && new_set && !strcmp(new->prepath, old->prepath)) return 1; @@ -3791,7 +3794,7 @@ int cifs_setup_cifs_sb(struct smb_vol *pvolume_info, cifs_sb->mnt_gid = pvolume_info->linux_gid; cifs_sb->mnt_file_mode = pvolume_info->file_mode; cifs_sb->mnt_dir_mode = pvolume_info->dir_mode; - cifs_dbg(FYI, "file mode: 0x%hx dir mode: 0x%hx\n", + cifs_dbg(FYI, "file mode: %04ho dir mode: %04ho\n", cifs_sb->mnt_file_mode, cifs_sb->mnt_dir_mode); cifs_sb->actimeo = pvolume_info->actimeo; diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 26154db6c87f1625366e39f09afa9a7bdc5fe1dc..51d410c6f6a4735bb7225fcb6804f9f9e83b5006 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -1579,7 +1579,7 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, umode_t mode) struct TCP_Server_Info *server; char *full_path; - cifs_dbg(FYI, "In cifs_mkdir, mode = 0x%hx inode = 0x%p\n", + cifs_dbg(FYI, "In cifs_mkdir, mode = %04ho inode = 0x%p\n", mode, inode); cifs_sb = CIFS_SB(inode->i_sb); @@ -2003,6 +2003,7 @@ int cifs_revalidate_dentry_attr(struct dentry *dentry) struct inode *inode = d_inode(dentry); struct super_block *sb = dentry->d_sb; char *full_path = NULL; + int count = 0; if (inode == NULL) return -ENOENT; @@ -2024,15 +2025,18 @@ int cifs_revalidate_dentry_attr(struct dentry *dentry) full_path, inode, inode->i_count.counter, dentry, cifs_get_time(dentry), jiffies); +again: if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext) rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid); else rc = cifs_get_inode_info(&inode, full_path, NULL, sb, xid, NULL); - + if (rc == -EAGAIN && count++ < 10) + goto again; out: kfree(full_path); free_xid(xid); + return rc; } diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c index 9168b2266e4fa31a00a5f8df139bdcdf837efa03..9e2e120e163e4138abcfcfead7ac15def760a1a6 100644 --- a/fs/cifs/smb2file.c +++ b/fs/cifs/smb2file.c @@ -67,7 +67,7 @@ smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, goto out; - if (oparms->tcon->use_resilient) { + if (oparms->tcon->use_resilient) { nr_ioctl_req.Timeout = 0; /* use server default (120 seconds) */ nr_ioctl_req.Reserved = 0; rc = SMB2_ioctl(xid, oparms->tcon, fid->persistent_fid, diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c index 766974fe637ab2cdb2a33877d6883ac598871506..14265b4bbcc00a22a640eb5912e60a105a17639d 100644 --- a/fs/cifs/smb2misc.c +++ b/fs/cifs/smb2misc.c @@ -750,7 +750,7 @@ __smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid, { struct close_cancelled_open *cancelled; - cancelled = kzalloc(sizeof(*cancelled), GFP_KERNEL); + cancelled = kzalloc(sizeof(*cancelled), GFP_ATOMIC); if (!cancelled) return -ENOMEM; diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 43f29621e51ff901ec2a755698863e7ddc7177db..e2d2b749c8f383e598fa0c2de0f371603873c376 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -259,9 +259,14 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon) } rc = cifs_negotiate_protocol(0, tcon->ses); - if (!rc && tcon->ses->need_reconnect) + if (!rc && tcon->ses->need_reconnect) { rc = cifs_setup_session(0, tcon->ses, nls_codepage); - + if ((rc == -EACCES) && !tcon->retry) { + rc = -EHOSTDOWN; + mutex_unlock(&tcon->ses->session_mutex); + goto failed; + } + } if (rc || !tcon->need_reconnect) { mutex_unlock(&tcon->ses->session_mutex); goto out; @@ -306,6 +311,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon) case SMB2_SET_INFO: rc = -EAGAIN; } +failed: unload_nls(nls_codepage); return rc; } @@ -3130,8 +3136,8 @@ smb2_readv_callback(struct mid_q_entry *mid) struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)rdata->iov[0].iov_base; unsigned int credits_received = 0; - struct smb_rqst rqst = { .rq_iov = rdata->iov, - .rq_nvec = 2, + struct smb_rqst rqst = { .rq_iov = &rdata->iov[1], + .rq_nvec = 1, .rq_pages = rdata->pages, .rq_offset = rdata->page_offset, .rq_npages = rdata->nr_pages, @@ -3419,6 +3425,9 @@ smb2_writev_callback(struct mid_q_entry *mid) wdata->cfile->fid.persistent_fid, tcon->tid, tcon->ses->Suid, wdata->offset, wdata->bytes, wdata->result); + if (wdata->result == -ENOSPC) + printk_once(KERN_WARNING "Out of space writing to %s\n", + tcon->treeName); } else trace_smb3_write_done(0 /* no xid */, wdata->cfile->fid.persistent_fid, diff --git a/fs/crypto/Kconfig b/fs/crypto/Kconfig index 0701bb90f99c3422547fcb7baf79bf5fd4edca10..97c0a113f4cc636783d0dd8367327123e8fc0d23 100644 --- a/fs/crypto/Kconfig +++ b/fs/crypto/Kconfig @@ -1,13 +1,8 @@ config FS_ENCRYPTION bool "FS Encryption (Per-file encryption)" select CRYPTO - select CRYPTO_AES - select CRYPTO_CBC - select CRYPTO_ECB - select CRYPTO_XTS - select CRYPTO_CTS - select CRYPTO_SHA512 - select CRYPTO_HMAC + select CRYPTO_HASH + select CRYPTO_BLKCIPHER select KEYS help Enable encryption of files and directories. This @@ -16,6 +11,19 @@ config FS_ENCRYPTION decrypted pages in the page cache. Currently Ext4, F2FS and UBIFS make use of this feature. +# Filesystems supporting encryption must select this if FS_ENCRYPTION. This +# allows the algorithms to be built as modules when all the filesystems are. +config FS_ENCRYPTION_ALGS + tristate + select CRYPTO_AES + select CRYPTO_CBC + select CRYPTO_CTS + select CRYPTO_ECB + select CRYPTO_HMAC + select CRYPTO_SHA256 + select CRYPTO_SHA512 + select CRYPTO_XTS + config FS_ENCRYPTION_INLINE_CRYPT bool "Enable fscrypt to use inline crypto" depends on FS_ENCRYPTION && BLK_INLINE_ENCRYPTION diff --git a/fs/crypto/bio.c b/fs/crypto/bio.c index 69275782f3dba41471a3ad7c771e970ab96dab65..649658e8efd44bca2946e532776ee63aeac9a5e3 100644 --- a/fs/crypto/bio.c +++ b/fs/crypto/bio.c @@ -42,63 +42,154 @@ void fscrypt_decrypt_bio(struct bio *bio) } EXPORT_SYMBOL(fscrypt_decrypt_bio); +static int fscrypt_zeroout_range_inlinecrypt(const struct inode *inode, + pgoff_t lblk, + sector_t pblk, unsigned int len) +{ + const unsigned int blockbits = inode->i_blkbits; + const unsigned int blocks_per_page_bits = PAGE_SHIFT - blockbits; + const unsigned int blocks_per_page = 1 << blocks_per_page_bits; + unsigned int i; + struct bio *bio; + int ret, err; + + /* This always succeeds since __GFP_DIRECT_RECLAIM is set. */ + bio = bio_alloc(GFP_NOFS, BIO_MAX_PAGES); + + do { + bio_set_dev(bio, inode->i_sb->s_bdev); + bio->bi_iter.bi_sector = pblk << (blockbits - 9); + bio_set_op_attrs(bio, REQ_OP_WRITE, 0); + fscrypt_set_bio_crypt_ctx(bio, inode, lblk, GFP_NOFS); + + i = 0; + do { + unsigned int blocks_this_page = + min(len, blocks_per_page); + unsigned int bytes_this_page = + blocks_this_page << blockbits; + + ret = bio_add_page(bio, ZERO_PAGE(0), + bytes_this_page, 0); + if (WARN_ON(ret != bytes_this_page)) { + err = -EIO; + goto out; + } + lblk += blocks_this_page; + pblk += blocks_this_page; + len -= blocks_this_page; + } while (++i != BIO_MAX_PAGES && len != 0); + + err = submit_bio_wait(bio); + if (err) + goto out; + bio_reset(bio); + } while (len != 0); + err = 0; +out: + bio_put(bio); + return err; +} + +/** + * fscrypt_zeroout_range() - zero out a range of blocks in an encrypted file + * @inode: the file's inode + * @lblk: the first file logical block to zero out + * @pblk: the first filesystem physical block to zero out + * @len: number of blocks to zero out + * + * Zero out filesystem blocks in an encrypted regular file on-disk, i.e. write + * ciphertext blocks which decrypt to the all-zeroes block. The blocks must be + * both logically and physically contiguous. It's also assumed that the + * filesystem only uses a single block device, ->s_bdev. + * + * Note that since each block uses a different IV, this involves writing a + * different ciphertext to each block; we can't simply reuse the same one. + * + * Return: 0 on success; -errno on failure. + */ int fscrypt_zeroout_range(const struct inode *inode, pgoff_t lblk, - sector_t pblk, unsigned int len) + sector_t pblk, unsigned int len) { const unsigned int blockbits = inode->i_blkbits; const unsigned int blocksize = 1 << blockbits; - const bool inlinecrypt = fscrypt_inode_uses_inline_crypto(inode); - struct page *ciphertext_page; + const unsigned int blocks_per_page_bits = PAGE_SHIFT - blockbits; + const unsigned int blocks_per_page = 1 << blocks_per_page_bits; + struct page *pages[16]; /* write up to 16 pages at a time */ + unsigned int nr_pages; + unsigned int i; + unsigned int offset; struct bio *bio; - int ret, err = 0; - - if (inlinecrypt) { - ciphertext_page = ZERO_PAGE(0); - } else { - ciphertext_page = fscrypt_alloc_bounce_page(GFP_NOWAIT); - if (!ciphertext_page) - return -ENOMEM; - } + int ret, err; - while (len--) { - if (!inlinecrypt) { - err = fscrypt_crypt_block(inode, FS_ENCRYPT, lblk, - ZERO_PAGE(0), ciphertext_page, - blocksize, 0, GFP_NOFS); - if (err) - goto errout; - } + if (len == 0) + return 0; + + if (fscrypt_inode_uses_inline_crypto(inode)) + return fscrypt_zeroout_range_inlinecrypt(inode, lblk, pblk, + len); - bio = bio_alloc(GFP_NOWAIT, 1); - if (!bio) { - err = -ENOMEM; - goto errout; - } - fscrypt_set_bio_crypt_ctx(bio, inode, lblk, GFP_NOIO); + BUILD_BUG_ON(ARRAY_SIZE(pages) > BIO_MAX_PAGES); + nr_pages = min_t(unsigned int, ARRAY_SIZE(pages), + (len + blocks_per_page - 1) >> blocks_per_page_bits); + /* + * We need at least one page for ciphertext. Allocate the first one + * from a mempool, with __GFP_DIRECT_RECLAIM set so that it can't fail. + * + * Any additional page allocations are allowed to fail, as they only + * help performance, and waiting on the mempool for them could deadlock. + */ + for (i = 0; i < nr_pages; i++) { + pages[i] = fscrypt_alloc_bounce_page(i == 0 ? GFP_NOFS : + GFP_NOWAIT | __GFP_NOWARN); + if (!pages[i]) + break; + } + nr_pages = i; + if (WARN_ON(nr_pages <= 0)) + return -EINVAL; + + /* This always succeeds since __GFP_DIRECT_RECLAIM is set. */ + bio = bio_alloc(GFP_NOFS, nr_pages); + + do { bio_set_dev(bio, inode->i_sb->s_bdev); bio->bi_iter.bi_sector = pblk << (blockbits - 9); bio_set_op_attrs(bio, REQ_OP_WRITE, 0); - ret = bio_add_page(bio, ciphertext_page, blocksize, 0); - if (WARN_ON(ret != blocksize)) { - /* should never happen! */ - bio_put(bio); - err = -EIO; - goto errout; - } + + i = 0; + offset = 0; + do { + err = fscrypt_crypt_block(inode, FS_ENCRYPT, lblk, + ZERO_PAGE(0), pages[i], + blocksize, offset, GFP_NOFS); + if (err) + goto out; + lblk++; + pblk++; + len--; + offset += blocksize; + if (offset == PAGE_SIZE || len == 0) { + ret = bio_add_page(bio, pages[i++], offset, 0); + if (WARN_ON(ret != offset)) { + err = -EIO; + goto out; + } + offset = 0; + } + } while (i != nr_pages && len != 0); + err = submit_bio_wait(bio); - if (err == 0 && bio->bi_status) - err = -EIO; - bio_put(bio); if (err) - goto errout; - lblk++; - pblk++; - } + goto out; + bio_reset(bio); + } while (len != 0); err = 0; -errout: - if (!inlinecrypt) - fscrypt_free_bounce_page(ciphertext_page); +out: + bio_put(bio); + for (i = 0; i < nr_pages; i++) + fscrypt_free_bounce_page(pages[i]); return err; } EXPORT_SYMBOL(fscrypt_zeroout_range); diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 05ba4ffc91c61d787cc8d9535eff7ee4aad61637..cc8e334165f5c369445d13a0ed8793639417679e 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -24,8 +24,6 @@ #include #include #include -#include -#include #include #include "fscrypt_private.h" @@ -146,7 +144,7 @@ int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw, * multiple of the filesystem's block size. * @offs: Byte offset within @page of the first block to encrypt. Must be * a multiple of the filesystem's block size. - * @gfp_flags: Memory allocation flags + * @gfp_flags: Memory allocation flags. See details below. * * A new bounce page is allocated, and the specified block(s) are encrypted into * it. In the bounce page, the ciphertext block(s) will be located at the same @@ -156,6 +154,11 @@ int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw, * * This is for use by the filesystem's ->writepages() method. * + * The bounce page allocation is mempool-backed, so it will always succeed when + * @gfp_flags includes __GFP_DIRECT_RECLAIM, e.g. when it's GFP_NOFS. However, + * only the first page of each bio can be allocated this way. To prevent + * deadlocks, for any additional pages a mask like GFP_NOWAIT must be used. + * * Return: the new encrypted bounce page on success; an ERR_PTR() on failure */ struct page *fscrypt_encrypt_pagecache_blocks(struct page *page, @@ -292,54 +295,6 @@ int fscrypt_decrypt_block_inplace(const struct inode *inode, struct page *page, } EXPORT_SYMBOL(fscrypt_decrypt_block_inplace); -/* - * Validate dentries in encrypted directories to make sure we aren't potentially - * caching stale dentries after a key has been added. - */ -static int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags) -{ - struct dentry *dir; - int err; - int valid; - - /* - * Plaintext names are always valid, since fscrypt doesn't support - * reverting to ciphertext names without evicting the directory's inode - * -- which implies eviction of the dentries in the directory. - */ - if (!(dentry->d_flags & DCACHE_ENCRYPTED_NAME)) - return 1; - - /* - * Ciphertext name; valid if the directory's key is still unavailable. - * - * Although fscrypt forbids rename() on ciphertext names, we still must - * use dget_parent() here rather than use ->d_parent directly. That's - * because a corrupted fs image may contain directory hard links, which - * the VFS handles by moving the directory's dentry tree in the dcache - * each time ->lookup() finds the directory and it already has a dentry - * elsewhere. Thus ->d_parent can be changing, and we must safely grab - * a reference to some ->d_parent to prevent it from being freed. - */ - - if (flags & LOOKUP_RCU) - return -ECHILD; - - dir = dget_parent(dentry); - err = fscrypt_get_encryption_info(d_inode(dir)); - valid = !fscrypt_has_encryption_key(d_inode(dir)); - dput(dir); - - if (err < 0) - return err; - - return valid; -} - -const struct dentry_operations fscrypt_d_ops = { - .d_revalidate = fscrypt_d_revalidate, -}; - /** * fscrypt_initialize() - allocate major buffers for fs encryption. * @cop_flags: fscrypt operations flags diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index 3aafddaab703a0ec4a5a7c36fdc0ca023f8c8563..63bfe5e8accd2814f49159d541727a77f8fbb2a4 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -11,10 +11,88 @@ * This has not yet undergone a rigorous security audit. */ +#include #include +#include +#include #include #include "fscrypt_private.h" +/** + * struct fscrypt_nokey_name - identifier for directory entry when key is absent + * + * When userspace lists an encrypted directory without access to the key, the + * filesystem must present a unique "no-key name" for each filename that allows + * it to find the directory entry again if requested. Naively, that would just + * mean using the ciphertext filenames. However, since the ciphertext filenames + * can contain illegal characters ('\0' and '/'), they must be encoded in some + * way. We use base64. But that can cause names to exceed NAME_MAX (255 + * bytes), so we also need to use a strong hash to abbreviate long names. + * + * The filesystem may also need another kind of hash, the "dirhash", to quickly + * find the directory entry. Since filesystems normally compute the dirhash + * over the on-disk filename (i.e. the ciphertext), it's not computable from + * no-key names that abbreviate the ciphertext using the strong hash to fit in + * NAME_MAX. It's also not computable if it's a keyed hash taken over the + * plaintext (but it may still be available in the on-disk directory entry); + * casefolded directories use this type of dirhash. At least in these cases, + * each no-key name must include the name's dirhash too. + * + * To meet all these requirements, we base64-encode the following + * variable-length structure. It contains the dirhash, or 0's if the filesystem + * didn't provide one; up to 149 bytes of the ciphertext name; and for + * ciphertexts longer than 149 bytes, also the SHA-256 of the remaining bytes. + * + * This ensures that each no-key name contains everything needed to find the + * directory entry again, contains only legal characters, doesn't exceed + * NAME_MAX, is unambiguous unless there's a SHA-256 collision, and that we only + * take the performance hit of SHA-256 on very long filenames (which are rare). + */ +struct fscrypt_nokey_name { + u32 dirhash[2]; + u8 bytes[149]; + u8 sha256[SHA256_DIGEST_SIZE]; +}; /* 189 bytes => 252 bytes base64-encoded, which is <= NAME_MAX (255) */ + +/* + * Decoded size of max-size nokey name, i.e. a name that was abbreviated using + * the strong hash and thus includes the 'sha256' field. This isn't simply + * sizeof(struct fscrypt_nokey_name), as the padding at the end isn't included. + */ +#define FSCRYPT_NOKEY_NAME_MAX offsetofend(struct fscrypt_nokey_name, sha256) + +static struct crypto_shash *sha256_hash_tfm; + +static int fscrypt_do_sha256(const u8 *data, unsigned int data_len, u8 *result) +{ + struct crypto_shash *tfm = READ_ONCE(sha256_hash_tfm); + + if (unlikely(!tfm)) { + struct crypto_shash *prev_tfm; + + tfm = crypto_alloc_shash("sha256", 0, 0); + if (IS_ERR(tfm)) { + fscrypt_err(NULL, + "Error allocating SHA-256 transform: %ld", + PTR_ERR(tfm)); + return PTR_ERR(tfm); + } + prev_tfm = cmpxchg(&sha256_hash_tfm, NULL, tfm); + if (prev_tfm) { + crypto_free_shash(tfm); + tfm = prev_tfm; + } + } + { + SHASH_DESC_ON_STACK(desc, tfm); + + desc->tfm = tfm; + desc->flags = 0; + + return crypto_shash_digest(desc, data, data_len, result); + } +} + static inline bool fscrypt_is_dot_dotdot(const struct qstr *str) { if (str->len == 1 && str->name[0] == '.') @@ -27,19 +105,19 @@ static inline bool fscrypt_is_dot_dotdot(const struct qstr *str) } /** - * fname_encrypt() - encrypt a filename + * fscrypt_fname_encrypt() - encrypt a filename * * The output buffer must be at least as large as the input buffer. * Any extra space is filled with NUL padding before encryption. * * Return: 0 on success, -errno on failure */ -int fname_encrypt(struct inode *inode, const struct qstr *iname, - u8 *out, unsigned int olen) +int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname, + u8 *out, unsigned int olen) { struct skcipher_request *req = NULL; DECLARE_CRYPTO_WAIT(wait); - struct fscrypt_info *ci = inode->i_crypt_info; + const struct fscrypt_info *ci = inode->i_crypt_info; struct crypto_skcipher *tfm = ci->ci_key.tfm; union fscrypt_iv iv; struct scatterlist sg; @@ -85,14 +163,14 @@ int fname_encrypt(struct inode *inode, const struct qstr *iname, * * Return: 0 on success, -errno on failure */ -static int fname_decrypt(struct inode *inode, - const struct fscrypt_str *iname, - struct fscrypt_str *oname) +static int fname_decrypt(const struct inode *inode, + const struct fscrypt_str *iname, + struct fscrypt_str *oname) { struct skcipher_request *req = NULL; DECLARE_CRYPTO_WAIT(wait); struct scatterlist src_sg, dst_sg; - struct fscrypt_info *ci = inode->i_crypt_info; + const struct fscrypt_info *ci = inode->i_crypt_info; struct crypto_skcipher *tfm = ci->ci_key.tfm; union fscrypt_iv iv; int res; @@ -206,9 +284,7 @@ int fscrypt_fname_alloc_buffer(const struct inode *inode, u32 max_encrypted_len, struct fscrypt_str *crypto_str) { - const u32 max_encoded_len = - max_t(u32, BASE64_CHARS(FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE), - 1 + BASE64_CHARS(sizeof(struct fscrypt_digested_name))); + const u32 max_encoded_len = BASE64_CHARS(FSCRYPT_NOKEY_NAME_MAX); u32 max_presented_len; max_presented_len = max(max_encoded_len, max_encrypted_len); @@ -241,19 +317,21 @@ EXPORT_SYMBOL(fscrypt_fname_free_buffer); * * The caller must have allocated sufficient memory for the @oname string. * - * If the key is available, we'll decrypt the disk name; otherwise, we'll encode - * it for presentation. Short names are directly base64-encoded, while long - * names are encoded in fscrypt_digested_name format. + * If the key is available, we'll decrypt the disk name. Otherwise, we'll + * encode it for presentation in fscrypt_nokey_name format. + * See struct fscrypt_nokey_name for details. * * Return: 0 on success, -errno on failure */ -int fscrypt_fname_disk_to_usr(struct inode *inode, - u32 hash, u32 minor_hash, - const struct fscrypt_str *iname, - struct fscrypt_str *oname) +int fscrypt_fname_disk_to_usr(const struct inode *inode, + u32 hash, u32 minor_hash, + const struct fscrypt_str *iname, + struct fscrypt_str *oname) { const struct qstr qname = FSTR_TO_QSTR(iname); - struct fscrypt_digested_name digested_name; + struct fscrypt_nokey_name nokey_name; + u32 size; /* size of the unencoded no-key name */ + int err; if (fscrypt_is_dot_dotdot(&qname)) { oname->name[0] = '.'; @@ -268,24 +346,37 @@ int fscrypt_fname_disk_to_usr(struct inode *inode, if (fscrypt_has_encryption_key(inode)) return fname_decrypt(inode, iname, oname); - if (iname->len <= FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE) { - oname->len = base64_encode(iname->name, iname->len, - oname->name); - return 0; - } + /* + * Sanity check that struct fscrypt_nokey_name doesn't have padding + * between fields and that its encoded size never exceeds NAME_MAX. + */ + BUILD_BUG_ON(offsetofend(struct fscrypt_nokey_name, dirhash) != + offsetof(struct fscrypt_nokey_name, bytes)); + BUILD_BUG_ON(offsetofend(struct fscrypt_nokey_name, bytes) != + offsetof(struct fscrypt_nokey_name, sha256)); + BUILD_BUG_ON(BASE64_CHARS(FSCRYPT_NOKEY_NAME_MAX) > NAME_MAX); + if (hash) { - digested_name.hash = hash; - digested_name.minor_hash = minor_hash; + nokey_name.dirhash[0] = hash; + nokey_name.dirhash[1] = minor_hash; + } else { + nokey_name.dirhash[0] = 0; + nokey_name.dirhash[1] = 0; + } + if (iname->len <= sizeof(nokey_name.bytes)) { + memcpy(nokey_name.bytes, iname->name, iname->len); + size = offsetof(struct fscrypt_nokey_name, bytes[iname->len]); } else { - digested_name.hash = 0; - digested_name.minor_hash = 0; + memcpy(nokey_name.bytes, iname->name, sizeof(nokey_name.bytes)); + /* Compute strong hash of remaining part of name. */ + err = fscrypt_do_sha256(&iname->name[sizeof(nokey_name.bytes)], + iname->len - sizeof(nokey_name.bytes), + nokey_name.sha256); + if (err) + return err; + size = FSCRYPT_NOKEY_NAME_MAX; } - memcpy(digested_name.digest, - FSCRYPT_FNAME_DIGEST(iname->name, iname->len), - FSCRYPT_FNAME_DIGEST_SIZE); - oname->name[0] = '_'; - oname->len = 1 + base64_encode((const u8 *)&digested_name, - sizeof(digested_name), oname->name + 1); + oname->len = base64_encode((const u8 *)&nokey_name, size, oname->name); return 0; } EXPORT_SYMBOL(fscrypt_fname_disk_to_usr); @@ -306,8 +397,7 @@ EXPORT_SYMBOL(fscrypt_fname_disk_to_usr); * get the disk_name. * * Else, for keyless @lookup operations, @iname is the presented ciphertext, so - * we decode it to get either the ciphertext disk_name (for short names) or the - * fscrypt_digested_name (for long names). Non-@lookup operations will be + * we decode it to get the fscrypt_nokey_name. Non-@lookup operations will be * impossible in this case, so we fail them with ENOKEY. * * If successful, fscrypt_free_filename() must be called later to clean up. @@ -317,8 +407,8 @@ EXPORT_SYMBOL(fscrypt_fname_disk_to_usr); int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, int lookup, struct fscrypt_name *fname) { + struct fscrypt_nokey_name *nokey_name; int ret; - int digested; memset(fname, 0, sizeof(struct fscrypt_name)); fname->usr_fname = iname; @@ -342,8 +432,8 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, if (!fname->crypto_buf.name) return -ENOMEM; - ret = fname_encrypt(dir, iname, fname->crypto_buf.name, - fname->crypto_buf.len); + ret = fscrypt_fname_encrypt(dir, iname, fname->crypto_buf.name, + fname->crypto_buf.len); if (ret) goto errout; fname->disk_name.name = fname->crypto_buf.name; @@ -358,40 +448,31 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, * We don't have the key and we are doing a lookup; decode the * user-supplied name */ - if (iname->name[0] == '_') { - if (iname->len != - 1 + BASE64_CHARS(sizeof(struct fscrypt_digested_name))) - return -ENOENT; - digested = 1; - } else { - if (iname->len > - BASE64_CHARS(FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE)) - return -ENOENT; - digested = 0; - } - fname->crypto_buf.name = - kmalloc(max_t(size_t, FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE, - sizeof(struct fscrypt_digested_name)), - GFP_KERNEL); + if (iname->len > BASE64_CHARS(FSCRYPT_NOKEY_NAME_MAX)) + return -ENOENT; + + fname->crypto_buf.name = kmalloc(FSCRYPT_NOKEY_NAME_MAX, GFP_KERNEL); if (fname->crypto_buf.name == NULL) return -ENOMEM; - ret = base64_decode(iname->name + digested, iname->len - digested, - fname->crypto_buf.name); - if (ret < 0) { + ret = base64_decode(iname->name, iname->len, fname->crypto_buf.name); + if (ret < (int)offsetof(struct fscrypt_nokey_name, bytes[1]) || + (ret > offsetof(struct fscrypt_nokey_name, sha256) && + ret != FSCRYPT_NOKEY_NAME_MAX)) { ret = -ENOENT; goto errout; } fname->crypto_buf.len = ret; - if (digested) { - const struct fscrypt_digested_name *n = - (const void *)fname->crypto_buf.name; - fname->hash = n->hash; - fname->minor_hash = n->minor_hash; - } else { - fname->disk_name.name = fname->crypto_buf.name; - fname->disk_name.len = fname->crypto_buf.len; + + nokey_name = (void *)fname->crypto_buf.name; + fname->hash = nokey_name->dirhash[0]; + fname->minor_hash = nokey_name->dirhash[1]; + if (ret != FSCRYPT_NOKEY_NAME_MAX) { + /* The full ciphertext filename is available. */ + fname->disk_name.name = nokey_name->bytes; + fname->disk_name.len = + ret - offsetof(struct fscrypt_nokey_name, bytes); } return 0; @@ -400,3 +481,106 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, return ret; } EXPORT_SYMBOL(fscrypt_setup_filename); + +/** + * fscrypt_match_name() - test whether the given name matches a directory entry + * @fname: the name being searched for + * @de_name: the name from the directory entry + * @de_name_len: the length of @de_name in bytes + * + * Normally @fname->disk_name will be set, and in that case we simply compare + * that to the name stored in the directory entry. The only exception is that + * if we don't have the key for an encrypted directory and the name we're + * looking for is very long, then we won't have the full disk_name and instead + * we'll need to match against a fscrypt_nokey_name that includes a strong hash. + * + * Return: %true if the name matches, otherwise %false. + */ +bool fscrypt_match_name(const struct fscrypt_name *fname, + const u8 *de_name, u32 de_name_len) +{ + const struct fscrypt_nokey_name *nokey_name = + (const void *)fname->crypto_buf.name; + u8 sha256[SHA256_DIGEST_SIZE]; + + if (likely(fname->disk_name.name)) { + if (de_name_len != fname->disk_name.len) + return false; + return !memcmp(de_name, fname->disk_name.name, de_name_len); + } + if (de_name_len <= sizeof(nokey_name->bytes)) + return false; + if (memcmp(de_name, nokey_name->bytes, sizeof(nokey_name->bytes))) + return false; + if (fscrypt_do_sha256(&de_name[sizeof(nokey_name->bytes)], + de_name_len - sizeof(nokey_name->bytes), sha256)) + return false; + return !memcmp(sha256, nokey_name->sha256, sizeof(sha256)); +} +EXPORT_SYMBOL_GPL(fscrypt_match_name); + +/** + * fscrypt_fname_siphash() - calculate the SipHash of a filename + * @dir: the parent directory + * @name: the filename to calculate the SipHash of + * + * Given a plaintext filename @name and a directory @dir which uses SipHash as + * its dirhash method and has had its fscrypt key set up, this function + * calculates the SipHash of that name using the directory's secret dirhash key. + * + * Return: the SipHash of @name using the hash key of @dir + */ +u64 fscrypt_fname_siphash(const struct inode *dir, const struct qstr *name) +{ + const struct fscrypt_info *ci = dir->i_crypt_info; + + WARN_ON(!ci->ci_dirhash_key_initialized); + + return siphash(name->name, name->len, &ci->ci_dirhash_key); +} +EXPORT_SYMBOL_GPL(fscrypt_fname_siphash); + +/* + * Validate dentries in encrypted directories to make sure we aren't potentially + * caching stale dentries after a key has been added. + */ +int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags) +{ + struct dentry *dir; + int err; + int valid; + + /* + * Plaintext names are always valid, since fscrypt doesn't support + * reverting to ciphertext names without evicting the directory's inode + * -- which implies eviction of the dentries in the directory. + */ + if (!(dentry->d_flags & DCACHE_ENCRYPTED_NAME)) + return 1; + + /* + * Ciphertext name; valid if the directory's key is still unavailable. + * + * Although fscrypt forbids rename() on ciphertext names, we still must + * use dget_parent() here rather than use ->d_parent directly. That's + * because a corrupted fs image may contain directory hard links, which + * the VFS handles by moving the directory's dentry tree in the dcache + * each time ->lookup() finds the directory and it already has a dentry + * elsewhere. Thus ->d_parent can be changing, and we must safely grab + * a reference to some ->d_parent to prevent it from being freed. + */ + + if (flags & LOOKUP_RCU) + return -ECHILD; + + dir = dget_parent(dentry); + err = fscrypt_get_encryption_info(d_inode(dir)); + valid = !fscrypt_has_encryption_key(d_inode(dir)); + dput(dir); + + if (err < 0) + return err; + + return valid; +} +EXPORT_SYMBOL(fscrypt_d_revalidate); diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index af6300c790deac7f6e26aef6d78ae944740bf2e2..f78cc4dfb452da9073129381d62502a8e2c5d993 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -12,6 +12,7 @@ #define _FSCRYPT_PRIVATE_H #include +#include #include #include @@ -138,12 +139,6 @@ fscrypt_policy_flags(const union fscrypt_policy *policy) BUG(); } -static inline bool -fscrypt_is_direct_key_policy(const union fscrypt_policy *policy) -{ - return fscrypt_policy_flags(policy) & FSCRYPT_POLICY_FLAG_DIRECT_KEY; -} - /** * For encrypted symlinks, the ciphertext length is stored at the beginning * of the string in little-endian format. @@ -218,6 +213,14 @@ struct fscrypt_info { */ struct fscrypt_direct_key *ci_direct_key; + /* + * This inode's hash key for filenames. This is a 128-bit SipHash-2-4 + * key. This is only set for directories that use a keyed dirhash over + * the plaintext filenames -- currently just casefolded directories. + */ + siphash_key_t ci_dirhash_key; + bool ci_dirhash_key_initialized; + /* The encryption policy used by this inode */ union fscrypt_policy ci_policy; @@ -230,28 +233,6 @@ typedef enum { FS_ENCRYPT, } fscrypt_direction_t; -static inline bool fscrypt_valid_enc_modes(u32 contents_mode, - u32 filenames_mode) -{ - if (contents_mode == FSCRYPT_MODE_AES_128_CBC && - filenames_mode == FSCRYPT_MODE_AES_128_CTS) - return true; - - if (contents_mode == FSCRYPT_MODE_AES_256_XTS && - filenames_mode == FSCRYPT_MODE_AES_256_CTS) - return true; - - if (contents_mode == FSCRYPT_MODE_ADIANTUM && - filenames_mode == FSCRYPT_MODE_ADIANTUM) - return true; - - if (contents_mode == FSCRYPT_MODE_PRIVATE && - filenames_mode == FSCRYPT_MODE_AES_256_CTS) - return true; - - return false; -} - /* crypto.c */ extern struct kmem_cache *fscrypt_info_cachep; extern int fscrypt_initialize(unsigned int cop_flags); @@ -261,7 +242,6 @@ extern int fscrypt_crypt_block(const struct inode *inode, unsigned int len, unsigned int offs, gfp_t gfp_flags); extern struct page *fscrypt_alloc_bounce_page(gfp_t gfp_flags); -extern const struct dentry_operations fscrypt_d_ops; extern void __printf(3, 4) __cold fscrypt_msg(const struct inode *inode, const char *level, const char *fmt, ...); @@ -289,8 +269,9 @@ void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num, const struct fscrypt_info *ci); /* fname.c */ -extern int fname_encrypt(struct inode *inode, const struct qstr *iname, - u8 *out, unsigned int olen); +extern int fscrypt_fname_encrypt(const struct inode *inode, + const struct qstr *iname, + u8 *out, unsigned int olen); extern bool fscrypt_fname_encrypted_size(const struct inode *inode, u32 orig_len, u32 max_len, u32 *encrypted_len_ret); @@ -312,11 +293,12 @@ extern int fscrypt_init_hkdf(struct fscrypt_hkdf *hkdf, const u8 *master_key, * output doesn't reveal another. */ #define HKDF_CONTEXT_KEY_IDENTIFIER 1 -#define HKDF_CONTEXT_PER_FILE_KEY 2 +#define HKDF_CONTEXT_PER_FILE_ENC_KEY 2 #define HKDF_CONTEXT_DIRECT_KEY 3 #define HKDF_CONTEXT_IV_INO_LBLK_64_KEY 4 +#define HKDF_CONTEXT_DIRHASH_KEY 5 -extern int fscrypt_hkdf_expand(struct fscrypt_hkdf *hkdf, u8 context, +extern int fscrypt_hkdf_expand(const struct fscrypt_hkdf *hkdf, u8 context, const u8 *info, unsigned int infolen, u8 *okm, unsigned int okmlen); @@ -568,18 +550,12 @@ struct fscrypt_mode { const char *cipher_str; int keysize; int ivsize; - bool logged_impl_name; + int logged_impl_name; enum blk_crypto_mode_num blk_crypto_mode; }; extern struct fscrypt_mode fscrypt_modes[]; -static inline bool -fscrypt_mode_supports_direct_key(const struct fscrypt_mode *mode) -{ - return mode->ivsize >= offsetofend(union fscrypt_iv, nonce); -} - extern int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key, const u8 *raw_key, unsigned int raw_key_size, bool is_hw_wrapped, @@ -587,8 +563,11 @@ extern int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key, extern void fscrypt_destroy_prepared_key(struct fscrypt_prepared_key *prep_key); -extern int fscrypt_set_derived_key(struct fscrypt_info *ci, - const u8 *derived_key); +extern int fscrypt_set_per_file_enc_key(struct fscrypt_info *ci, + const u8 *raw_key); + +extern int fscrypt_derive_dirhash_key(struct fscrypt_info *ci, + const struct fscrypt_master_key *mk); /* keysetup_v1.c */ diff --git a/fs/crypto/hkdf.c b/fs/crypto/hkdf.c index 2c026009c6e70b5b3aac1f3921a8e7cc1ac94e73..fd7f67628561254e5dc62a7e5b2506646a6bcf0f 100644 --- a/fs/crypto/hkdf.c +++ b/fs/crypto/hkdf.c @@ -113,7 +113,7 @@ int fscrypt_init_hkdf(struct fscrypt_hkdf *hkdf, const u8 *master_key, * adds to its application-specific info strings to guarantee that it doesn't * accidentally repeat an info string when using HKDF for different purposes.) */ -int fscrypt_hkdf_expand(struct fscrypt_hkdf *hkdf, u8 context, +int fscrypt_hkdf_expand(const struct fscrypt_hkdf *hkdf, u8 context, const u8 *info, unsigned int infolen, u8 *okm, unsigned int okmlen) { diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c index 30b1ca66124920a23a2e226e853406045f3bfba3..a6396bf721acfd86a98c8810b2e2bf85d4d71f8d 100644 --- a/fs/crypto/hooks.c +++ b/fs/crypto/hooks.c @@ -4,6 +4,8 @@ * Encryption hooks for higher-level filesystem operations. */ +#include + #include "fscrypt_private.h" /** @@ -115,12 +117,53 @@ int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry, spin_lock(&dentry->d_lock); dentry->d_flags |= DCACHE_ENCRYPTED_NAME; spin_unlock(&dentry->d_lock); - d_set_d_op(dentry, &fscrypt_d_ops); } return err; } EXPORT_SYMBOL_GPL(__fscrypt_prepare_lookup); +/** + * fscrypt_prepare_setflags() - prepare to change flags with FS_IOC_SETFLAGS + * @inode: the inode on which flags are being changed + * @oldflags: the old flags + * @flags: the new flags + * + * The caller should be holding i_rwsem for write. + * + * Return: 0 on success; -errno if the flags change isn't allowed or if + * another error occurs. + */ +int fscrypt_prepare_setflags(struct inode *inode, + unsigned int oldflags, unsigned int flags) +{ + struct fscrypt_info *ci; + struct fscrypt_master_key *mk; + int err; + + /* + * When the CASEFOLD flag is set on an encrypted directory, we must + * derive the secret key needed for the dirhash. This is only possible + * if the directory uses a v2 encryption policy. + */ + if (IS_ENCRYPTED(inode) && (flags & ~oldflags & FS_CASEFOLD_FL)) { + err = fscrypt_require_key(inode); + if (err) + return err; + ci = inode->i_crypt_info; + if (ci->ci_policy.version != FSCRYPT_POLICY_V2) + return -EINVAL; + mk = ci->ci_master_key->payload.data[0]; + down_read(&mk->mk_secret_sem); + if (is_master_key_secret_present(&mk->mk_secret)) + err = fscrypt_derive_dirhash_key(ci, mk); + else + err = -ENOKEY; + up_read(&mk->mk_secret_sem); + return err; + } + return 0; +} + int __fscrypt_prepare_symlink(struct inode *dir, unsigned int len, unsigned int max_len, struct fscrypt_str *disk_link) @@ -187,7 +230,8 @@ int __fscrypt_encrypt_symlink(struct inode *inode, const char *target, ciphertext_len = disk_link->len - sizeof(*sd); sd->len = cpu_to_le16(ciphertext_len); - err = fname_encrypt(inode, &iname, sd->encrypted_path, ciphertext_len); + err = fscrypt_fname_encrypt(inode, &iname, sd->encrypted_path, + ciphertext_len); if (err) goto err_free_sd; diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c index d524b436848b76bb57e0dd48d136b3427ac0f93a..9257ea1102b1654ab6f325dd3e67f069ebcfc442 100644 --- a/fs/crypto/keyring.c +++ b/fs/crypto/keyring.c @@ -632,7 +632,7 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg) if (arg.raw_size < FSCRYPT_MIN_KEY_SIZE || arg.raw_size > ((arg.__flags & __FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED) ? - FSCRYPT_MAX_HW_WRAPPED_KEY_SIZE : FSCRYPT_MAX_KEY_SIZE)) + FSCRYPT_MAX_HW_WRAPPED_KEY_SIZE : FSCRYPT_MAX_KEY_SIZE)) return -EINVAL; secret.size = arg.raw_size; err = -EFAULT; @@ -809,9 +809,6 @@ static int check_for_busy_inodes(struct super_block *sb, struct list_head *pos; size_t busy_count = 0; unsigned long ino; - struct dentry *dentry; - char _path[256]; - char *path = NULL; spin_lock(&mk->mk_decrypted_inodes_lock); @@ -830,22 +827,14 @@ static int check_for_busy_inodes(struct super_block *sb, struct fscrypt_info, ci_master_key_link)->ci_inode; ino = inode->i_ino; - dentry = d_find_alias(inode); } spin_unlock(&mk->mk_decrypted_inodes_lock); - if (dentry) { - path = dentry_path(dentry, _path, sizeof(_path)); - dput(dentry); - } - if (IS_ERR_OR_NULL(path)) - path = "(unknown)"; - fscrypt_warn(NULL, - "%s: %zu inode(s) still busy after removing key with %s %*phN, including ino %lu (%s)", + "%s: %zu inode(s) still busy after removing key with %s %*phN, including ino %lu", sb->s_id, busy_count, master_key_spec_type(&mk->mk_spec), master_key_spec_len(&mk->mk_spec), (u8 *)&mk->mk_spec.u, - ino, path); + ino); return -EBUSY; } diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c index 5414e275029621967ae0314019cf1a0ebf715d00..e6fd5fc1e226d3f89de73ef8a21f03a9a0be0a85 100644 --- a/fs/crypto/keysetup.c +++ b/fs/crypto/keysetup.c @@ -91,18 +91,19 @@ fscrypt_allocate_skcipher(struct fscrypt_mode *mode, const u8 *raw_key, mode->cipher_str, PTR_ERR(tfm)); return tfm; } - if (unlikely(!mode->logged_impl_name)) { + if (!xchg(&mode->logged_impl_name, 1)) { /* * fscrypt performance can vary greatly depending on which * crypto algorithm implementation is used. Help people debug * performance problems by logging the ->cra_driver_name the - * first time a mode is used. Note that multiple threads can - * race here, but it doesn't really matter. + * first time a mode is used. */ - mode->logged_impl_name = true; pr_info("fscrypt: %s using implementation \"%s\"\n", - mode->friendly_name, - crypto_skcipher_alg(tfm)->base.cra_driver_name); + mode->friendly_name, crypto_skcipher_driver_name(tfm)); + } + if (WARN_ON(crypto_skcipher_ivsize(tfm) != mode->ivsize)) { + err = -EINVAL; + goto err_free_tfm; } crypto_skcipher_set_flags(tfm, CRYPTO_TFM_REQ_WEAK_KEY); err = crypto_skcipher_setkey(tfm, raw_key, mode->keysize); @@ -152,18 +153,18 @@ void fscrypt_destroy_prepared_key(struct fscrypt_prepared_key *prep_key) fscrypt_destroy_inline_crypt_key(prep_key); } -/* Given the per-file key, set up the file's crypto transform object */ -int fscrypt_set_derived_key(struct fscrypt_info *ci, const u8 *derived_key) +/* Given a per-file encryption key, set up the file's crypto transform object */ +int fscrypt_set_per_file_enc_key(struct fscrypt_info *ci, const u8 *raw_key) { ci->ci_owns_key = true; - return fscrypt_prepare_key(&ci->ci_key, derived_key, - ci->ci_mode->keysize, false, ci); + return fscrypt_prepare_key(&ci->ci_key, raw_key, ci->ci_mode->keysize, + false /*is_hw_wrapped*/, ci); } -static int setup_per_mode_key(struct fscrypt_info *ci, - struct fscrypt_master_key *mk, - struct fscrypt_prepared_key *keys, - u8 hkdf_context, bool include_fs_uuid) +static int setup_per_mode_enc_key(struct fscrypt_info *ci, + struct fscrypt_master_key *mk, + struct fscrypt_prepared_key *keys, + u8 hkdf_context, bool include_fs_uuid) { static DEFINE_MUTEX(mode_key_setup_mutex); const struct inode *inode = ci->ci_inode; @@ -240,10 +241,24 @@ static int setup_per_mode_key(struct fscrypt_info *ci, return err; } +int fscrypt_derive_dirhash_key(struct fscrypt_info *ci, + const struct fscrypt_master_key *mk) +{ + int err; + + err = fscrypt_hkdf_expand(&mk->mk_secret.hkdf, HKDF_CONTEXT_DIRHASH_KEY, + ci->ci_nonce, FS_KEY_DERIVATION_NONCE_SIZE, + (u8 *)&ci->ci_dirhash_key, + sizeof(ci->ci_dirhash_key)); + if (err) + return err; + ci->ci_dirhash_key_initialized = true; + return 0; +} + static int fscrypt_setup_v2_file_key(struct fscrypt_info *ci, struct fscrypt_master_key *mk) { - u8 derived_key[FSCRYPT_MAX_KEY_SIZE]; int err; if (mk->mk_secret.is_hw_wrapped && @@ -255,21 +270,15 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_info *ci, if (ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) { /* - * DIRECT_KEY: instead of deriving per-file keys, the per-file - * nonce will be included in all the IVs. But unlike v1 - * policies, for v2 policies in this case we don't encrypt with - * the master key directly but rather derive a per-mode key. - * This ensures that the master key is consistently used only - * for HKDF, avoiding key reuse issues. + * DIRECT_KEY: instead of deriving per-file encryption keys, the + * per-file nonce will be included in all the IVs. But unlike + * v1 policies, for v2 policies in this case we don't encrypt + * with the master key directly but rather derive a per-mode + * encryption key. This ensures that the master key is + * consistently used only for HKDF, avoiding key reuse issues. */ - if (!fscrypt_mode_supports_direct_key(ci->ci_mode)) { - fscrypt_warn(ci->ci_inode, - "Direct key flag not allowed with %s", - ci->ci_mode->friendly_name); - return -EINVAL; - } - return setup_per_mode_key(ci, mk, mk->mk_direct_keys, - HKDF_CONTEXT_DIRECT_KEY, false); + err = setup_per_mode_enc_key(ci, mk, mk->mk_direct_keys, + HKDF_CONTEXT_DIRECT_KEY, false); } else if (ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) { /* @@ -278,21 +287,34 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_info *ci, * the IVs. This format is optimized for use with inline * encryption hardware compliant with the UFS or eMMC standards. */ - return setup_per_mode_key(ci, mk, mk->mk_iv_ino_lblk_64_keys, - HKDF_CONTEXT_IV_INO_LBLK_64_KEY, - true); - } + err = setup_per_mode_enc_key(ci, mk, mk->mk_iv_ino_lblk_64_keys, + HKDF_CONTEXT_IV_INO_LBLK_64_KEY, + true); + } else { + u8 derived_key[FSCRYPT_MAX_KEY_SIZE]; - err = fscrypt_hkdf_expand(&mk->mk_secret.hkdf, - HKDF_CONTEXT_PER_FILE_KEY, - ci->ci_nonce, FS_KEY_DERIVATION_NONCE_SIZE, - derived_key, ci->ci_mode->keysize); + err = fscrypt_hkdf_expand(&mk->mk_secret.hkdf, + HKDF_CONTEXT_PER_FILE_ENC_KEY, + ci->ci_nonce, + FS_KEY_DERIVATION_NONCE_SIZE, + derived_key, ci->ci_mode->keysize); + if (err) + return err; + + err = fscrypt_set_per_file_enc_key(ci, derived_key); + memzero_explicit(derived_key, ci->ci_mode->keysize); + } if (err) return err; - err = fscrypt_set_derived_key(ci, derived_key); - memzero_explicit(derived_key, ci->ci_mode->keysize); - return err; + /* Derive a secret dirhash key for directories that need it. */ + if (S_ISDIR(ci->ci_inode->i_mode) && IS_CASEFOLDED(ci->ci_inode)) { + err = fscrypt_derive_dirhash_key(ci, mk); + if (err) + return err; + } + + return 0; } /* @@ -592,6 +614,15 @@ int fscrypt_drop_inode(struct inode *inode) return 0; mk = ci->ci_master_key->payload.data[0]; + /* + * With proper, non-racy use of FS_IOC_REMOVE_ENCRYPTION_KEY, all inodes + * protected by the key were cleaned by sync_filesystem(). But if + * userspace is still using the files, inodes can be dirtied between + * then and now. We mustn't lose any writes, so skip dirty inodes here. + */ + if (inode->i_state & I_DIRTY_ALL) + return 0; + /* * Note: since we aren't holding ->mk_secret_sem, the result here can * immediately become outdated. But there's no correctness problem with diff --git a/fs/crypto/keysetup_v1.c b/fs/crypto/keysetup_v1.c index 1f0c19dba639afc7fc56d613258cbe552f8b3b5a..38e54313653d24bc1a289b414c3dbf050725d916 100644 --- a/fs/crypto/keysetup_v1.c +++ b/fs/crypto/keysetup_v1.c @@ -9,7 +9,7 @@ * This file implements compatibility functions for the original encryption * policy version ("v1"), including: * - * - Deriving per-file keys using the AES-128-ECB based KDF + * - Deriving per-file encryption keys using the AES-128-ECB based KDF * (rather than the new method of using HKDF-SHA512) * * - Retrieving fscrypt master keys from process-subscribed keyrings @@ -253,23 +253,8 @@ fscrypt_get_direct_key(const struct fscrypt_info *ci, const u8 *raw_key) static int setup_v1_file_key_direct(struct fscrypt_info *ci, const u8 *raw_master_key) { - const struct fscrypt_mode *mode = ci->ci_mode; struct fscrypt_direct_key *dk; - if (!fscrypt_mode_supports_direct_key(mode)) { - fscrypt_warn(ci->ci_inode, - "Direct key mode not allowed with %s", - mode->friendly_name); - return -EINVAL; - } - - if (ci->ci_policy.v1.contents_encryption_mode != - ci->ci_policy.v1.filenames_encryption_mode) { - fscrypt_warn(ci->ci_inode, - "Direct key mode not allowed with different contents and filenames modes"); - return -EINVAL; - } - dk = fscrypt_get_direct_key(ci, raw_master_key); if (IS_ERR(dk)) return PTR_ERR(dk); @@ -319,7 +304,7 @@ static int setup_v1_file_key_derived(struct fscrypt_info *ci, if (err) goto out; - err = fscrypt_set_derived_key(ci, derived_key); + err = fscrypt_set_per_file_enc_key(ci, derived_key); out: kzfree(derived_key); return err; diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index 96f528071bed3f8ea401e38b90ae622d7a261745..4bed63a18d4151c82f8367ebe21b13961086c6a3 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -29,6 +29,47 @@ bool fscrypt_policies_equal(const union fscrypt_policy *policy1, return !memcmp(policy1, policy2, fscrypt_policy_size(policy1)); } +static bool fscrypt_valid_enc_modes(u32 contents_mode, u32 filenames_mode) +{ + if (contents_mode == FSCRYPT_MODE_AES_256_XTS && + filenames_mode == FSCRYPT_MODE_AES_256_CTS) + return true; + + if (contents_mode == FSCRYPT_MODE_AES_128_CBC && + filenames_mode == FSCRYPT_MODE_AES_128_CTS) + return true; + + if (contents_mode == FSCRYPT_MODE_ADIANTUM && + filenames_mode == FSCRYPT_MODE_ADIANTUM) + return true; + + if (contents_mode == FSCRYPT_MODE_PRIVATE && + filenames_mode == FSCRYPT_MODE_AES_256_CTS) + return true; + + return false; +} + +static bool supported_direct_key_modes(const struct inode *inode, + u32 contents_mode, u32 filenames_mode) +{ + const struct fscrypt_mode *mode; + + if (contents_mode != filenames_mode) { + fscrypt_warn(inode, + "Direct key flag not allowed with different contents and filenames modes"); + return false; + } + mode = &fscrypt_modes[contents_mode]; + + if (mode->ivsize < offsetofend(union fscrypt_iv, nonce)) { + fscrypt_warn(inode, "Direct key flag not allowed with %s", + mode->friendly_name); + return false; + } + return true; +} + static bool supported_iv_ino_lblk_64_policy( const struct fscrypt_policy_v2 *policy, const struct inode *inode) @@ -63,13 +104,82 @@ static bool supported_iv_ino_lblk_64_policy( return true; } +static bool fscrypt_supported_v1_policy(const struct fscrypt_policy_v1 *policy, + const struct inode *inode) +{ + if (!fscrypt_valid_enc_modes(policy->contents_encryption_mode, + policy->filenames_encryption_mode)) { + fscrypt_warn(inode, + "Unsupported encryption modes (contents %d, filenames %d)", + policy->contents_encryption_mode, + policy->filenames_encryption_mode); + return false; + } + + if (policy->flags & ~(FSCRYPT_POLICY_FLAGS_PAD_MASK | + FSCRYPT_POLICY_FLAG_DIRECT_KEY)) { + fscrypt_warn(inode, "Unsupported encryption flags (0x%02x)", + policy->flags); + return false; + } + + if ((policy->flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) && + !supported_direct_key_modes(inode, policy->contents_encryption_mode, + policy->filenames_encryption_mode)) + return false; + + if (IS_CASEFOLDED(inode)) { + /* With v1, there's no way to derive dirhash keys. */ + fscrypt_warn(inode, + "v1 policies can't be used on casefolded directories"); + return false; + } + + return true; +} + +static bool fscrypt_supported_v2_policy(const struct fscrypt_policy_v2 *policy, + const struct inode *inode) +{ + if (!fscrypt_valid_enc_modes(policy->contents_encryption_mode, + policy->filenames_encryption_mode)) { + fscrypt_warn(inode, + "Unsupported encryption modes (contents %d, filenames %d)", + policy->contents_encryption_mode, + policy->filenames_encryption_mode); + return false; + } + + if (policy->flags & ~FSCRYPT_POLICY_FLAGS_VALID) { + fscrypt_warn(inode, "Unsupported encryption flags (0x%02x)", + policy->flags); + return false; + } + + if ((policy->flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) && + !supported_direct_key_modes(inode, policy->contents_encryption_mode, + policy->filenames_encryption_mode)) + return false; + + if ((policy->flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) && + !supported_iv_ino_lblk_64_policy(policy, inode)) + return false; + + if (memchr_inv(policy->__reserved, 0, sizeof(policy->__reserved))) { + fscrypt_warn(inode, "Reserved bits set in encryption policy"); + return false; + } + + return true; +} + /** * fscrypt_supported_policy - check whether an encryption policy is supported * * Given an encryption policy, check whether all its encryption modes and other - * settings are supported by this kernel. (But we don't currently don't check - * for crypto API support here, so attempting to use an algorithm not configured - * into the crypto API will still fail later.) + * settings are supported by this kernel on the given inode. (But we don't + * currently don't check for crypto API support here, so attempting to use an + * algorithm not configured into the crypto API will still fail later.) * * Return: %true if supported, else %false */ @@ -77,60 +187,10 @@ bool fscrypt_supported_policy(const union fscrypt_policy *policy_u, const struct inode *inode) { switch (policy_u->version) { - case FSCRYPT_POLICY_V1: { - const struct fscrypt_policy_v1 *policy = &policy_u->v1; - - if (!fscrypt_valid_enc_modes(policy->contents_encryption_mode, - policy->filenames_encryption_mode)) { - fscrypt_warn(inode, - "Unsupported encryption modes (contents %d, filenames %d)", - policy->contents_encryption_mode, - policy->filenames_encryption_mode); - return false; - } - - if (policy->flags & ~(FSCRYPT_POLICY_FLAGS_PAD_MASK | - FSCRYPT_POLICY_FLAG_DIRECT_KEY)) { - fscrypt_warn(inode, - "Unsupported encryption flags (0x%02x)", - policy->flags); - return false; - } - - return true; - } - case FSCRYPT_POLICY_V2: { - const struct fscrypt_policy_v2 *policy = &policy_u->v2; - - if (!fscrypt_valid_enc_modes(policy->contents_encryption_mode, - policy->filenames_encryption_mode)) { - fscrypt_warn(inode, - "Unsupported encryption modes (contents %d, filenames %d)", - policy->contents_encryption_mode, - policy->filenames_encryption_mode); - return false; - } - - if (policy->flags & ~FSCRYPT_POLICY_FLAGS_VALID) { - fscrypt_warn(inode, - "Unsupported encryption flags (0x%02x)", - policy->flags); - return false; - } - - if ((policy->flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) && - !supported_iv_ino_lblk_64_policy(policy, inode)) - return false; - - if (memchr_inv(policy->__reserved, 0, - sizeof(policy->__reserved))) { - fscrypt_warn(inode, - "Reserved bits set in encryption policy"); - return false; - } - - return true; - } + case FSCRYPT_POLICY_V1: + return fscrypt_supported_v1_policy(&policy_u->v1, inode); + case FSCRYPT_POLICY_V2: + return fscrypt_supported_v2_policy(&policy_u->v2, inode); } return false; } diff --git a/fs/dax.c b/fs/dax.c index f0d932fa39c20db0367e7db9129d520719fd2660..d09701aa6f2f451b623c5a7e9f74b94ad3397c1b 100644 --- a/fs/dax.c +++ b/fs/dax.c @@ -1301,6 +1301,9 @@ dax_iomap_rw(struct kiocb *iocb, struct iov_iter *iter, lockdep_assert_held(&inode->i_rwsem); } + if (iocb->ki_flags & IOCB_NOWAIT) + flags |= IOMAP_NOWAIT; + while (iov_iter_count(iter)) { ret = iomap_apply(inode, pos, iov_iter_count(iter), flags, ops, iter, dax_iomap_actor); diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index 708f931c36f14adace91af82229a6043386c9baf..8e5353bd72cfd07f2c5dd469dcd760760546fd13 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -325,8 +325,10 @@ static int crypt_scatterlist(struct ecryptfs_crypt_stat *crypt_stat, struct extent_crypt_result ecr; int rc = 0; - BUG_ON(!crypt_stat || !crypt_stat->tfm - || !(crypt_stat->flags & ECRYPTFS_STRUCT_INITIALIZED)); + if (!crypt_stat || !crypt_stat->tfm + || !(crypt_stat->flags & ECRYPTFS_STRUCT_INITIALIZED)) + return -EINVAL; + if (unlikely(ecryptfs_verbosity > 0)) { ecryptfs_printk(KERN_DEBUG, "Key size [%zd]; key:\n", crypt_stat->key_size); diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c index e74fe84d0886879c04cc26297f0e8b27db8281a6..250cb23ae69f222a093720894b69d25cc36e325b 100644 --- a/fs/ecryptfs/keystore.c +++ b/fs/ecryptfs/keystore.c @@ -1318,7 +1318,7 @@ parse_tag_1_packet(struct ecryptfs_crypt_stat *crypt_stat, printk(KERN_WARNING "Tag 1 packet contains key larger " "than ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES\n"); rc = -EINVAL; - goto out; + goto out_free; } memcpy((*new_auth_tok)->session_key.encrypted_key, &data[(*packet_size)], (body_size - (ECRYPTFS_SIG_SIZE + 2))); diff --git a/fs/ecryptfs/messaging.c b/fs/ecryptfs/messaging.c index 9fdd5bcf4564a6ffbea991a56b2ae2ec01998a49..aa3ddb48ebacb0ba5c1329ba5770be17e3df9f7d 100644 --- a/fs/ecryptfs/messaging.c +++ b/fs/ecryptfs/messaging.c @@ -392,6 +392,7 @@ int __init ecryptfs_init_messaging(void) * ecryptfs_message_buf_len), GFP_KERNEL); if (!ecryptfs_msg_ctx_arr) { + kfree(ecryptfs_daemon_hash); rc = -ENOMEM; goto out; } diff --git a/fs/eventfd.c b/fs/eventfd.c index 08d3bd602f73d8f219ee1f259c0cbaa839245c56..ce1d1711fbbaf1ff7fe9de6e419d6cd891890827 100644 --- a/fs/eventfd.c +++ b/fs/eventfd.c @@ -22,6 +22,8 @@ #include #include +DEFINE_PER_CPU(int, eventfd_wake_count); + struct eventfd_ctx { struct kref kref; wait_queue_head_t wqh; @@ -55,12 +57,25 @@ __u64 eventfd_signal(struct eventfd_ctx *ctx, __u64 n) { unsigned long flags; + /* + * Deadlock or stack overflow issues can happen if we recurse here + * through waitqueue wakeup handlers. If the caller users potentially + * nested waitqueues with custom wakeup handlers, then it should + * check eventfd_signal_count() before calling this function. If + * it returns true, the eventfd_signal() call should be deferred to a + * safe context. + */ + if (WARN_ON_ONCE(this_cpu_read(eventfd_wake_count))) + return 0; + spin_lock_irqsave(&ctx->wqh.lock, flags); + this_cpu_inc(eventfd_wake_count); if (ULLONG_MAX - ctx->count < n) n = ULLONG_MAX - ctx->count; ctx->count += n; if (waitqueue_active(&ctx->wqh)) wake_up_locked_poll(&ctx->wqh, EPOLLIN); + this_cpu_dec(eventfd_wake_count); spin_unlock_irqrestore(&ctx->wqh.lock, flags); return n; diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 8b4ded92d1e98f8f833d310cb4dfbd4af06e06dd..4760c2b7ea84eb1c15cac6c7849ac844d7ff2478 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -1383,13 +1383,13 @@ static int ep_create_wakeup_source(struct epitem *epi) struct wakeup_source *ws; if (!epi->ep->ws) { - epi->ep->ws = wakeup_source_register("eventpoll"); + epi->ep->ws = wakeup_source_register(NULL, "eventpoll"); if (!epi->ep->ws) return -ENOMEM; } name = epi->ffd.file->f_path.dentry->d_name.name; - ws = wakeup_source_register(name); + ws = wakeup_source_register(NULL, name); if (!ws) return -ENOMEM; diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c index 808cae6d5f50f2f159eddc7c6e90fe43799abc01..ae3248326c449c19cce6da5c93bac2577f0e150d 100644 --- a/fs/exportfs/expfs.c +++ b/fs/exportfs/expfs.c @@ -147,6 +147,7 @@ static struct dentry *reconnect_one(struct vfsmount *mnt, tmp = lookup_one_len_unlocked(nbuf, parent, strlen(nbuf)); if (IS_ERR(tmp)) { dprintk("%s: lookup failed: %d\n", __func__, PTR_ERR(tmp)); + err = PTR_ERR(tmp); goto out_err; } if (tmp != dentry) { diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 364e647d87c0a9955211b567c4ae8078684fefda..80a3038e0e464c0e6b72673d3b2166a04cd5ae67 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -1093,9 +1093,9 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) if (EXT2_BLOCKS_PER_GROUP(sb) == 0) goto cantfind_ext2; - sbi->s_groups_count = ((le32_to_cpu(es->s_blocks_count) - - le32_to_cpu(es->s_first_data_block) - 1) - / EXT2_BLOCKS_PER_GROUP(sb)) + 1; + sbi->s_groups_count = ((le32_to_cpu(es->s_blocks_count) - + le32_to_cpu(es->s_first_data_block) - 1) + / EXT2_BLOCKS_PER_GROUP(sb)) + 1; db_count = (sbi->s_groups_count + EXT2_DESC_PER_BLOCK(sb) - 1) / EXT2_DESC_PER_BLOCK(sb); sbi->s_group_desc = kmalloc_array (db_count, diff --git a/fs/ext4/Kconfig b/fs/ext4/Kconfig index 037358be4c6292beb8c154351448a48e14dbed6c..8d8c03176a919c2289bf9607710a6b388f334939 100644 --- a/fs/ext4/Kconfig +++ b/fs/ext4/Kconfig @@ -38,6 +38,7 @@ config EXT4_FS select CRYPTO select CRYPTO_CRC32C select FS_IOMAP + select FS_ENCRYPTION_ALGS if FS_ENCRYPTION help This is the next generation of the ext3 filesystem. diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c index e5d6ee61ff48fe97cd67a5952c747bdab84fd820..f9645de9d04cc55410f779df7cd0eea36670c9e8 100644 --- a/fs/ext4/balloc.c +++ b/fs/ext4/balloc.c @@ -270,6 +270,7 @@ struct ext4_group_desc * ext4_get_group_desc(struct super_block *sb, ext4_group_t ngroups = ext4_get_groups_count(sb); struct ext4_group_desc *desc; struct ext4_sb_info *sbi = EXT4_SB(sb); + struct buffer_head *bh_p; if (block_group >= ngroups) { ext4_error(sb, "block_group >= groups_count - block_group = %u," @@ -280,7 +281,14 @@ struct ext4_group_desc * ext4_get_group_desc(struct super_block *sb, group_desc = block_group >> EXT4_DESC_PER_BLOCK_BITS(sb); offset = block_group & (EXT4_DESC_PER_BLOCK(sb) - 1); - if (!sbi->s_group_desc[group_desc]) { + bh_p = sbi_array_rcu_deref(sbi, s_group_desc, group_desc); + /* + * sbi_array_rcu_deref returns with rcu unlocked, this is ok since + * the pointer being dereferenced won't be dereferenced again. By + * looking at the usage in add_new_gdb() the value isn't modified, + * just the pointer, and so it remains valid. + */ + if (!bh_p) { ext4_error(sb, "Group descriptor not loaded - " "block_group = %u, group_desc = %u, desc = %u", block_group, group_desc, offset); @@ -288,10 +296,10 @@ struct ext4_group_desc * ext4_get_group_desc(struct super_block *sb, } desc = (struct ext4_group_desc *)( - (__u8 *)sbi->s_group_desc[group_desc]->b_data + + (__u8 *)bh_p->b_data + offset * EXT4_DESC_SIZE(sb)); if (bh) - *bh = sbi->s_group_desc[group_desc]; + *bh = bh_p; return desc; } diff --git a/fs/ext4/block_validity.c b/fs/ext4/block_validity.c index 7edc8172c53ad0cb8cb0badaa782b327c6d062de..d203cc935ff833ef023cfc32035de7fe7ae7cf43 100644 --- a/fs/ext4/block_validity.c +++ b/fs/ext4/block_validity.c @@ -203,6 +203,7 @@ static int ext4_protect_reserved_inode(struct super_block *sb, return PTR_ERR(inode); num = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits; while (i < num) { + cond_resched(); map.m_lblk = i; map.m_len = num - i; n = ext4_map_blocks(NULL, inode, &map, 0); diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c index 2770af6d06b13ef1c17ced21fbb56bc696d19509..1c904c48faa604f6995048538044ff04c91b69ee 100644 --- a/fs/ext4/dir.c +++ b/fs/ext4/dir.c @@ -118,7 +118,7 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx) if (IS_ENCRYPTED(inode)) { err = fscrypt_get_encryption_info(inode); - if (err && err != -ENOKEY) + if (err) return err; } @@ -127,12 +127,14 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx) if (err != ERR_BAD_DX_DIR) { return err; } - /* - * We don't set the inode dirty flag since it's not - * critical that it get flushed back to the disk. - */ - ext4_clear_inode_flag(file_inode(file), - EXT4_INODE_INDEX); + /* Can we just clear INDEX flag to ignore htree information? */ + if (!ext4_has_metadata_csum(sb)) { + /* + * We don't set the inode dirty flag since it's not + * critical that it gets flushed back to the disk. + */ + ext4_clear_inode_flag(inode, EXT4_INODE_INDEX); + } } if (ext4_has_inline_data(inode)) { @@ -665,51 +667,3 @@ const struct file_operations ext4_dir_operations = { .open = ext4_dir_open, .release = ext4_release_dir, }; - -#ifdef CONFIG_UNICODE -static int ext4_d_compare(const struct dentry *dentry, unsigned int len, - const char *str, const struct qstr *name) -{ - struct qstr qstr = {.name = str, .len = len }; - struct inode *inode = dentry->d_parent->d_inode; - - if (!IS_CASEFOLDED(inode) || !EXT4_SB(inode->i_sb)->s_encoding) { - if (len != name->len) - return -1; - return memcmp(str, name->name, len); - } - - return ext4_ci_compare(inode, name, &qstr, false); -} - -static int ext4_d_hash(const struct dentry *dentry, struct qstr *str) -{ - const struct ext4_sb_info *sbi = EXT4_SB(dentry->d_sb); - const struct unicode_map *um = sbi->s_encoding; - unsigned char *norm; - int len, ret = 0; - - if (!IS_CASEFOLDED(dentry->d_inode) || !um) - return 0; - - norm = kmalloc(PATH_MAX, GFP_ATOMIC); - if (!norm) - return -ENOMEM; - - len = utf8_casefold(um, str, norm, PATH_MAX); - if (len < 0) { - if (ext4_has_strict_mode(sbi)) - ret = -EINVAL; - goto out; - } - str->hash = full_name_hash(dentry, norm, len); -out: - kfree(norm); - return ret; -} - -const struct dentry_operations ext4_dentry_ops = { - .d_hash = ext4_d_hash, - .d_compare = ext4_d_compare, -}; -#endif diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 56f9de2997afa5d96886c21e59c217316defb3dc..66fa445ce804b509a6c3c26315ff532563c47327 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1361,14 +1361,6 @@ struct ext4_super_block { #define EXT4_ENC_UTF8_12_1 1 -/* - * Flags for ext4_sb_info.s_encoding_flags. - */ -#define EXT4_ENC_STRICT_MODE_FL (1 << 0) - -#define ext4_has_strict_mode(sbi) \ - (sbi->s_encoding_flags & EXT4_ENC_STRICT_MODE_FL) - /* * fourth extended-fs super-block data in memory */ @@ -1389,7 +1381,7 @@ struct ext4_sb_info { loff_t s_bitmap_maxbytes; /* max bytes for bitmap files */ struct buffer_head * s_sbh; /* Buffer containing the super block */ struct ext4_super_block *s_es; /* Pointer to the super block in the buffer */ - struct buffer_head **s_group_desc; + struct buffer_head * __rcu *s_group_desc; unsigned int s_mount_opt; unsigned int s_mount_opt2; unsigned int s_mount_flags; @@ -1418,10 +1410,6 @@ struct ext4_sb_info { struct kobject s_kobj; struct completion s_kobj_unregister; struct super_block *s_sb; -#ifdef CONFIG_UNICODE - struct unicode_map *s_encoding; - __u16 s_encoding_flags; -#endif /* Journaling */ struct journal_s *s_journal; @@ -1451,7 +1439,7 @@ struct ext4_sb_info { #endif /* for buddy allocator */ - struct ext4_group_info ***s_group_info; + struct ext4_group_info ** __rcu *s_group_info; struct inode *s_buddy_cache; spinlock_t s_md_lock; unsigned short *s_mb_offsets; @@ -1501,7 +1489,7 @@ struct ext4_sb_info { unsigned int s_extent_max_zeroout_kb; unsigned int s_log_groups_per_flex; - struct flex_groups *s_flex_groups; + struct flex_groups * __rcu *s_flex_groups; ext4_group_t s_flex_groups_allocated; /* workqueue for reserved extent conversions (buffered io) */ @@ -1541,8 +1529,11 @@ struct ext4_sb_info { struct ratelimit_state s_warning_ratelimit_state; struct ratelimit_state s_msg_ratelimit_state; - /* Barrier between changing inodes' journal flags and writepages ops. */ - struct percpu_rw_semaphore s_journal_flag_rwsem; + /* + * Barrier between writepages ops and changing any inode's JOURNAL_DATA + * or EXTENTS flag. + */ + struct percpu_rw_semaphore s_writepages_rwsem; struct dax_device *s_daxdev; }; @@ -1562,6 +1553,23 @@ static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino) ino <= le32_to_cpu(EXT4_SB(sb)->s_es->s_inodes_count)); } +/* + * Returns: sbi->field[index] + * Used to access an array element from the following sbi fields which require + * rcu protection to avoid dereferencing an invalid pointer due to reassignment + * - s_group_desc + * - s_group_info + * - s_flex_group + */ +#define sbi_array_rcu_deref(sbi, field, index) \ +({ \ + typeof(*((sbi)->field)) _v; \ + rcu_read_lock(); \ + _v = ((typeof(_v)*)rcu_dereference((sbi)->field))[index]; \ + rcu_read_unlock(); \ + _v; \ +}) + /* * Inode dynamic state flags */ @@ -2468,8 +2476,11 @@ void ext4_insert_dentry(struct inode *inode, struct ext4_filename *fname); static inline void ext4_update_dx_flag(struct inode *inode) { - if (!ext4_has_feature_dir_index(inode->i_sb)) + if (!ext4_has_feature_dir_index(inode->i_sb)) { + /* ext4_iget() should have caught this... */ + WARN_ON_ONCE(ext4_has_feature_metadata_csum(inode->i_sb)); ext4_clear_inode_flag(inode, EXT4_INODE_INDEX); + } } static const unsigned char ext4_filetype_table[] = { DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK @@ -2654,6 +2665,7 @@ extern int ext4_generic_delete_entry(handle_t *handle, extern bool ext4_empty_dir(struct inode *inode); /* resize.c */ +extern void ext4_kvfree_array_rcu(void *to_free); extern int ext4_group_add(struct super_block *sb, struct ext4_new_group_data *input); extern int ext4_group_extend(struct super_block *sb, @@ -2901,13 +2913,13 @@ static inline struct ext4_group_info *ext4_get_group_info(struct super_block *sb, ext4_group_t group) { - struct ext4_group_info ***grp_info; + struct ext4_group_info **grp_info; long indexv, indexh; BUG_ON(group >= EXT4_SB(sb)->s_groups_count); - grp_info = EXT4_SB(sb)->s_group_info; indexv = group >> (EXT4_DESC_PER_BLOCK_BITS(sb)); indexh = group & ((EXT4_DESC_PER_BLOCK(sb)) - 1); - return grp_info[indexv][indexh]; + grp_info = sbi_array_rcu_deref(EXT4_SB(sb), s_group_info, indexv); + return grp_info[indexh]; } /* @@ -2957,7 +2969,7 @@ static inline void ext4_update_i_disksize(struct inode *inode, loff_t newsize) !inode_is_locked(inode)); down_write(&EXT4_I(inode)->i_data_sem); if (newsize > EXT4_I(inode)->i_disksize) - EXT4_I(inode)->i_disksize = newsize; + WRITE_ONCE(EXT4_I(inode)->i_disksize, newsize); up_write(&EXT4_I(inode)->i_data_sem); } diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 7ad36a9fd63276ede652750c8c7d7a2c1cc59cbe..26d5627019737bb5456ba2552b2bb6df17f3e99d 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -40,9 +40,10 @@ static ssize_t ext4_dax_read_iter(struct kiocb *iocb, struct iov_iter *to) struct inode *inode = file_inode(iocb->ki_filp); ssize_t ret; - if (!inode_trylock_shared(inode)) { - if (iocb->ki_flags & IOCB_NOWAIT) + if (iocb->ki_flags & IOCB_NOWAIT) { + if (!inode_trylock_shared(inode)) return -EAGAIN; + } else { inode_lock_shared(inode); } /* @@ -190,9 +191,10 @@ ext4_dax_write_iter(struct kiocb *iocb, struct iov_iter *from) struct inode *inode = file_inode(iocb->ki_filp); ssize_t ret; - if (!inode_trylock(inode)) { - if (iocb->ki_flags & IOCB_NOWAIT) + if (iocb->ki_flags & IOCB_NOWAIT) { + if (!inode_trylock(inode)) return -EAGAIN; + } else { inode_lock(inode); } ret = ext4_write_checks(iocb, from); diff --git a/fs/ext4/hash.c b/fs/ext4/hash.c index b934206040b7f94f2ac875b2f3a636efeb7b1ef4..3674b807479f7c00e8d806b85049580f2488c14b 100644 --- a/fs/ext4/hash.c +++ b/fs/ext4/hash.c @@ -273,7 +273,7 @@ int ext4fs_dirhash(const struct inode *dir, const char *name, int len, struct dx_hash_info *hinfo) { #ifdef CONFIG_UNICODE - const struct unicode_map *um = EXT4_SB(dir->i_sb)->s_encoding; + const struct unicode_map *um = dir->i_sb->s_encoding; int r, dlen; unsigned char *buff; struct qstr qstr = {.name = name, .len = len }; diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index 47cea9c9bc72094522a9bc3439065ee0d966f534..cbb65e62e4012dbeba2d510eb7c569272e982308 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -330,11 +330,13 @@ void ext4_free_inode(handle_t *handle, struct inode *inode) percpu_counter_inc(&sbi->s_freeinodes_counter); if (sbi->s_log_groups_per_flex) { - ext4_group_t f = ext4_flex_group(sbi, block_group); + struct flex_groups *fg; - atomic_inc(&sbi->s_flex_groups[f].free_inodes); + fg = sbi_array_rcu_deref(sbi, s_flex_groups, + ext4_flex_group(sbi, block_group)); + atomic_inc(&fg->free_inodes); if (is_directory) - atomic_dec(&sbi->s_flex_groups[f].used_dirs); + atomic_dec(&fg->used_dirs); } BUFFER_TRACE(bh2, "call ext4_handle_dirty_metadata"); fatal = ext4_handle_dirty_metadata(handle, NULL, bh2); @@ -370,12 +372,13 @@ static void get_orlov_stats(struct super_block *sb, ext4_group_t g, int flex_size, struct orlov_stats *stats) { struct ext4_group_desc *desc; - struct flex_groups *flex_group = EXT4_SB(sb)->s_flex_groups; if (flex_size > 1) { - stats->free_inodes = atomic_read(&flex_group[g].free_inodes); - stats->free_clusters = atomic64_read(&flex_group[g].free_clusters); - stats->used_dirs = atomic_read(&flex_group[g].used_dirs); + struct flex_groups *fg = sbi_array_rcu_deref(EXT4_SB(sb), + s_flex_groups, g); + stats->free_inodes = atomic_read(&fg->free_inodes); + stats->free_clusters = atomic64_read(&fg->free_clusters); + stats->used_dirs = atomic_read(&fg->used_dirs); return; } @@ -1056,7 +1059,8 @@ struct inode *__ext4_new_inode(handle_t *handle, struct inode *dir, if (sbi->s_log_groups_per_flex) { ext4_group_t f = ext4_flex_group(sbi, group); - atomic_inc(&sbi->s_flex_groups[f].used_dirs); + atomic_inc(&sbi_array_rcu_deref(sbi, s_flex_groups, + f)->used_dirs); } } if (ext4_has_group_desc_csum(sb)) { @@ -1079,7 +1083,8 @@ struct inode *__ext4_new_inode(handle_t *handle, struct inode *dir, if (sbi->s_log_groups_per_flex) { flex_group = ext4_flex_group(sbi, group); - atomic_dec(&sbi->s_flex_groups[flex_group].free_inodes); + atomic_dec(&sbi_array_rcu_deref(sbi, s_flex_groups, + flex_group)->free_inodes); } inode->i_ino = ino + group * EXT4_INODES_PER_GROUP(sb); diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c index d06563453f350bb71c55b7695c5a189bc87fcce3..c6f7d7f32c7df6038ab051c1d3d40f6cf1650d85 100644 --- a/fs/ext4/inline.c +++ b/fs/ext4/inline.c @@ -1433,7 +1433,7 @@ int htree_inlinedir_to_tree(struct file *dir_file, err = ext4_htree_store_dirent(dir_file, hinfo->hash, hinfo->minor_hash, de, &tmp_str); if (err) { - count = err; + ret = err; goto out; } count++; diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index e8d1c112655a99a790c7dfb0a46cfa9fb9b4cdbc..75442deb98776142ff9c73d1975ed919a24c8a4a 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -2595,7 +2595,7 @@ static int mpage_map_and_submit_extent(handle_t *handle, * truncate are avoided by checking i_size under i_data_sem. */ disksize = ((loff_t)mpd->first_page) << PAGE_SHIFT; - if (disksize > EXT4_I(inode)->i_disksize) { + if (disksize > READ_ONCE(EXT4_I(inode)->i_disksize)) { int err2; loff_t i_size; @@ -2756,7 +2756,7 @@ static int ext4_writepages(struct address_space *mapping, if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb)))) return -EIO; - percpu_down_read(&sbi->s_journal_flag_rwsem); + percpu_down_read(&sbi->s_writepages_rwsem); trace_ext4_writepages(inode, wbc); /* @@ -2976,7 +2976,7 @@ static int ext4_writepages(struct address_space *mapping, out_writepages: trace_ext4_writepages_result(inode, wbc, ret, nr_to_write - wbc->nr_to_write); - percpu_up_read(&sbi->s_journal_flag_rwsem); + percpu_up_read(&sbi->s_writepages_rwsem); return ret; } @@ -2991,13 +2991,13 @@ static int ext4_dax_writepages(struct address_space *mapping, if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb)))) return -EIO; - percpu_down_read(&sbi->s_journal_flag_rwsem); + percpu_down_read(&sbi->s_writepages_rwsem); trace_ext4_writepages(inode, wbc); ret = dax_writeback_mapping_range(mapping, inode->i_sb->s_bdev, wbc); trace_ext4_writepages_result(inode, wbc, ret, nr_to_write - wbc->nr_to_write); - percpu_up_read(&sbi->s_journal_flag_rwsem); + percpu_up_read(&sbi->s_writepages_rwsem); return ret; } @@ -5060,6 +5060,18 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino, ret = -EFSCORRUPTED; goto bad_inode; } + /* + * If dir_index is not enabled but there's dir with INDEX flag set, + * we'd normally treat htree data as empty space. But with metadata + * checksumming that corrupts checksums so forbid that. + */ + if (!ext4_has_feature_dir_index(sb) && ext4_has_metadata_csum(sb) && + ext4_test_inode_flag(inode, EXT4_INODE_INDEX)) { + ext4_error_inode(inode, function, line, 0, + "iget: Dir with htree data on filesystem without dir_index feature."); + ret = -EFSCORRUPTED; + goto bad_inode; + } ei->i_disksize = inode->i_size; #ifdef CONFIG_QUOTA ei->i_reserved_quota = 0; @@ -5826,12 +5838,15 @@ int ext4_getattr(const struct path *path, struct kstat *stat, stat->attributes |= STATX_ATTR_IMMUTABLE; if (flags & EXT4_NODUMP_FL) stat->attributes |= STATX_ATTR_NODUMP; + if (flags & EXT4_VERITY_FL) + stat->attributes |= STATX_ATTR_VERITY; stat->attributes_mask |= (STATX_ATTR_APPEND | STATX_ATTR_COMPRESSED | STATX_ATTR_ENCRYPTED | STATX_ATTR_IMMUTABLE | - STATX_ATTR_NODUMP); + STATX_ATTR_NODUMP | + STATX_ATTR_VERITY); generic_fillattr(inode, stat); return 0; @@ -6287,7 +6302,7 @@ int ext4_change_inode_journal_flag(struct inode *inode, int val) } } - percpu_down_write(&sbi->s_journal_flag_rwsem); + percpu_down_write(&sbi->s_writepages_rwsem); jbd2_journal_lock_updates(journal); /* @@ -6304,7 +6319,7 @@ int ext4_change_inode_journal_flag(struct inode *inode, int val) err = jbd2_journal_flush(journal); if (err < 0) { jbd2_journal_unlock_updates(journal); - percpu_up_write(&sbi->s_journal_flag_rwsem); + percpu_up_write(&sbi->s_writepages_rwsem); return err; } ext4_clear_inode_flag(inode, EXT4_INODE_JOURNAL_DATA); @@ -6312,7 +6327,7 @@ int ext4_change_inode_journal_flag(struct inode *inode, int val) ext4_set_aops(inode); jbd2_journal_unlock_updates(journal); - percpu_up_write(&sbi->s_journal_flag_rwsem); + percpu_up_write(&sbi->s_writepages_rwsem); if (val) up_write(&EXT4_I(inode)->i_mmap_sem); diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index 96f83294f76861f6c0bb699cdd5611a795c30ce8..1b39fbd8a4d07fd57a9628b30022090700c4cad4 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -1301,6 +1301,8 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case FS_IOC_GETFSMAP: case FS_IOC_ENABLE_VERITY: case FS_IOC_MEASURE_VERITY: + case EXT4_IOC_FSGETXATTR: + case EXT4_IOC_FSSETXATTR: break; default: return -ENOIOCTLCMD; diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index cc229f3357f7e6b793a010d77e7c78c63144ceec..71121fcf9e8ccdaa232744f12535940158cc2d1c 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -2356,7 +2356,7 @@ int ext4_mb_alloc_groupinfo(struct super_block *sb, ext4_group_t ngroups) { struct ext4_sb_info *sbi = EXT4_SB(sb); unsigned size; - struct ext4_group_info ***new_groupinfo; + struct ext4_group_info ***old_groupinfo, ***new_groupinfo; size = (ngroups + EXT4_DESC_PER_BLOCK(sb) - 1) >> EXT4_DESC_PER_BLOCK_BITS(sb); @@ -2369,13 +2369,16 @@ int ext4_mb_alloc_groupinfo(struct super_block *sb, ext4_group_t ngroups) ext4_msg(sb, KERN_ERR, "can't allocate buddy meta group"); return -ENOMEM; } - if (sbi->s_group_info) { - memcpy(new_groupinfo, sbi->s_group_info, + rcu_read_lock(); + old_groupinfo = rcu_dereference(sbi->s_group_info); + if (old_groupinfo) + memcpy(new_groupinfo, old_groupinfo, sbi->s_group_info_size * sizeof(*sbi->s_group_info)); - kvfree(sbi->s_group_info); - } - sbi->s_group_info = new_groupinfo; + rcu_read_unlock(); + rcu_assign_pointer(sbi->s_group_info, new_groupinfo); sbi->s_group_info_size = size / sizeof(*sbi->s_group_info); + if (old_groupinfo) + ext4_kvfree_array_rcu(old_groupinfo); ext4_debug("allocated s_groupinfo array for %d meta_bg's\n", sbi->s_group_info_size); return 0; @@ -2387,6 +2390,7 @@ int ext4_mb_add_groupinfo(struct super_block *sb, ext4_group_t group, { int i; int metalen = 0; + int idx = group >> EXT4_DESC_PER_BLOCK_BITS(sb); struct ext4_sb_info *sbi = EXT4_SB(sb); struct ext4_group_info **meta_group_info; struct kmem_cache *cachep = get_groupinfo_cache(sb->s_blocksize_bits); @@ -2405,12 +2409,12 @@ int ext4_mb_add_groupinfo(struct super_block *sb, ext4_group_t group, "for a buddy group"); goto exit_meta_group_info; } - sbi->s_group_info[group >> EXT4_DESC_PER_BLOCK_BITS(sb)] = - meta_group_info; + rcu_read_lock(); + rcu_dereference(sbi->s_group_info)[idx] = meta_group_info; + rcu_read_unlock(); } - meta_group_info = - sbi->s_group_info[group >> EXT4_DESC_PER_BLOCK_BITS(sb)]; + meta_group_info = sbi_array_rcu_deref(sbi, s_group_info, idx); i = group & (EXT4_DESC_PER_BLOCK(sb) - 1); meta_group_info[i] = kmem_cache_zalloc(cachep, GFP_NOFS); @@ -2458,8 +2462,13 @@ int ext4_mb_add_groupinfo(struct super_block *sb, ext4_group_t group, exit_group_info: /* If a meta_group_info table has been allocated, release it now */ if (group % EXT4_DESC_PER_BLOCK(sb) == 0) { - kfree(sbi->s_group_info[group >> EXT4_DESC_PER_BLOCK_BITS(sb)]); - sbi->s_group_info[group >> EXT4_DESC_PER_BLOCK_BITS(sb)] = NULL; + struct ext4_group_info ***group_info; + + rcu_read_lock(); + group_info = rcu_dereference(sbi->s_group_info); + kfree(group_info[idx]); + group_info[idx] = NULL; + rcu_read_unlock(); } exit_meta_group_info: return -ENOMEM; @@ -2472,6 +2481,7 @@ static int ext4_mb_init_backend(struct super_block *sb) struct ext4_sb_info *sbi = EXT4_SB(sb); int err; struct ext4_group_desc *desc; + struct ext4_group_info ***group_info; struct kmem_cache *cachep; err = ext4_mb_alloc_groupinfo(sb, ngroups); @@ -2506,11 +2516,16 @@ static int ext4_mb_init_backend(struct super_block *sb) while (i-- > 0) kmem_cache_free(cachep, ext4_get_group_info(sb, i)); i = sbi->s_group_info_size; + rcu_read_lock(); + group_info = rcu_dereference(sbi->s_group_info); while (i-- > 0) - kfree(sbi->s_group_info[i]); + kfree(group_info[i]); + rcu_read_unlock(); iput(sbi->s_buddy_cache); err_freesgi: - kvfree(sbi->s_group_info); + rcu_read_lock(); + kvfree(rcu_dereference(sbi->s_group_info)); + rcu_read_unlock(); return -ENOMEM; } @@ -2699,7 +2714,7 @@ int ext4_mb_release(struct super_block *sb) ext4_group_t ngroups = ext4_get_groups_count(sb); ext4_group_t i; int num_meta_group_infos; - struct ext4_group_info *grinfo; + struct ext4_group_info *grinfo, ***group_info; struct ext4_sb_info *sbi = EXT4_SB(sb); struct kmem_cache *cachep = get_groupinfo_cache(sb->s_blocksize_bits); @@ -2717,9 +2732,12 @@ int ext4_mb_release(struct super_block *sb) num_meta_group_infos = (ngroups + EXT4_DESC_PER_BLOCK(sb) - 1) >> EXT4_DESC_PER_BLOCK_BITS(sb); + rcu_read_lock(); + group_info = rcu_dereference(sbi->s_group_info); for (i = 0; i < num_meta_group_infos; i++) - kfree(sbi->s_group_info[i]); - kvfree(sbi->s_group_info); + kfree(group_info[i]); + kvfree(group_info); + rcu_read_unlock(); } kfree(sbi->s_mb_offsets); kfree(sbi->s_mb_maxs); @@ -3018,7 +3036,8 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac, ext4_group_t flex_group = ext4_flex_group(sbi, ac->ac_b_ex.fe_group); atomic64_sub(ac->ac_b_ex.fe_len, - &sbi->s_flex_groups[flex_group].free_clusters); + &sbi_array_rcu_deref(sbi, s_flex_groups, + flex_group)->free_clusters); } err = ext4_handle_dirty_metadata(handle, NULL, bitmap_bh); @@ -4912,7 +4931,8 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode, if (sbi->s_log_groups_per_flex) { ext4_group_t flex_group = ext4_flex_group(sbi, block_group); atomic64_add(count_clusters, - &sbi->s_flex_groups[flex_group].free_clusters); + &sbi_array_rcu_deref(sbi, s_flex_groups, + flex_group)->free_clusters); } if (!(flags & EXT4_FREE_BLOCKS_NO_QUOT_UPDATE)) @@ -5061,7 +5081,8 @@ int ext4_group_add_blocks(handle_t *handle, struct super_block *sb, if (sbi->s_log_groups_per_flex) { ext4_group_t flex_group = ext4_flex_group(sbi, block_group); atomic64_add(clusters_freed, - &sbi->s_flex_groups[flex_group].free_clusters); + &sbi_array_rcu_deref(sbi, s_flex_groups, + flex_group)->free_clusters); } ext4_mb_unload_buddy(&e4b); diff --git a/fs/ext4/migrate.c b/fs/ext4/migrate.c index a98bfca9c463cfeb4d4bc95fa2d4b5fcd767bcca..bec4ad787c7d8cff5b821fc8bc0c61dd455181ab 100644 --- a/fs/ext4/migrate.c +++ b/fs/ext4/migrate.c @@ -427,6 +427,7 @@ static int free_ext_block(handle_t *handle, struct inode *inode) int ext4_ext_migrate(struct inode *inode) { + struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); handle_t *handle; int retval = 0, i; __le32 *i_data; @@ -451,6 +452,8 @@ int ext4_ext_migrate(struct inode *inode) */ return retval; + percpu_down_write(&sbi->s_writepages_rwsem); + /* * Worst case we can touch the allocation bitmaps, a bgd * block, and a block to link in the orphan list. We do need @@ -461,7 +464,7 @@ int ext4_ext_migrate(struct inode *inode) if (IS_ERR(handle)) { retval = PTR_ERR(handle); - return retval; + goto out_unlock; } goal = (((inode->i_ino - 1) / EXT4_INODES_PER_GROUP(inode->i_sb)) * EXT4_INODES_PER_GROUP(inode->i_sb)) + 1; @@ -472,7 +475,7 @@ int ext4_ext_migrate(struct inode *inode) if (IS_ERR(tmp_inode)) { retval = PTR_ERR(tmp_inode); ext4_journal_stop(handle); - return retval; + goto out_unlock; } i_size_write(tmp_inode, i_size_read(inode)); /* @@ -514,7 +517,7 @@ int ext4_ext_migrate(struct inode *inode) */ ext4_orphan_del(NULL, tmp_inode); retval = PTR_ERR(handle); - goto out; + goto out_tmp_inode; } ei = EXT4_I(inode); @@ -595,10 +598,11 @@ int ext4_ext_migrate(struct inode *inode) /* Reset the extent details */ ext4_ext_tree_init(handle, tmp_inode); ext4_journal_stop(handle); -out: +out_tmp_inode: unlock_new_inode(tmp_inode); iput(tmp_inode); - +out_unlock: + percpu_up_write(&sbi->s_writepages_rwsem); return retval; } @@ -608,7 +612,8 @@ int ext4_ext_migrate(struct inode *inode) int ext4_ind_migrate(struct inode *inode) { struct ext4_extent_header *eh; - struct ext4_super_block *es = EXT4_SB(inode->i_sb)->s_es; + struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); + struct ext4_super_block *es = sbi->s_es; struct ext4_inode_info *ei = EXT4_I(inode); struct ext4_extent *ex; unsigned int i, len; @@ -632,9 +637,13 @@ int ext4_ind_migrate(struct inode *inode) if (test_opt(inode->i_sb, DELALLOC)) ext4_alloc_da_blocks(inode); + percpu_down_write(&sbi->s_writepages_rwsem); + handle = ext4_journal_start(inode, EXT4_HT_MIGRATE, 1); - if (IS_ERR(handle)) - return PTR_ERR(handle); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + goto out_unlock; + } down_write(&EXT4_I(inode)->i_data_sem); ret = ext4_ext_check_inode(inode); @@ -669,5 +678,7 @@ int ext4_ind_migrate(struct inode *inode) errout: ext4_journal_stop(handle); up_write(&EXT4_I(inode)->i_data_sem); +out_unlock: + percpu_up_write(&sbi->s_writepages_rwsem); return ret; } diff --git a/fs/ext4/mmp.c b/fs/ext4/mmp.c index 2305b4374fd3e020eea80de3293040fd59d5de84..9d00e0dd2ba99f7064c8e21dbb4c986685a097b3 100644 --- a/fs/ext4/mmp.c +++ b/fs/ext4/mmp.c @@ -120,10 +120,10 @@ void __dump_mmp_msg(struct super_block *sb, struct mmp_struct *mmp, { __ext4_warning(sb, function, line, "%s", msg); __ext4_warning(sb, function, line, - "MMP failure info: last update time: %llu, last update " - "node: %s, last update device: %s", - (long long unsigned int) le64_to_cpu(mmp->mmp_time), - mmp->mmp_nodename, mmp->mmp_bdevname); + "MMP failure info: last update time: %llu, last update node: %.*s, last update device: %.*s", + (unsigned long long)le64_to_cpu(mmp->mmp_time), + (int)sizeof(mmp->mmp_nodename), mmp->mmp_nodename, + (int)sizeof(mmp->mmp_bdevname), mmp->mmp_bdevname); } /* @@ -154,6 +154,7 @@ static int kmmpd(void *data) mmp_check_interval = max(EXT4_MMP_CHECK_MULT * mmp_update_interval, EXT4_MMP_MIN_CHECK_INTERVAL); mmp->mmp_check_interval = cpu_to_le16(mmp_check_interval); + BUILD_BUG_ON(sizeof(mmp->mmp_bdevname) < BDEVNAME_SIZE); bdevname(bh->b_bdev, mmp->mmp_bdevname); memcpy(mmp->mmp_nodename, init_utsname()->nodename, @@ -375,7 +376,8 @@ int ext4_multi_mount_protect(struct super_block *sb, /* * Start a kernel thread to update the MMP block periodically. */ - EXT4_SB(sb)->s_mmp_tsk = kthread_run(kmmpd, mmpd_data, "kmmpd-%s", + EXT4_SB(sb)->s_mmp_tsk = kthread_run(kmmpd, mmpd_data, "kmmpd-%.*s", + (int)sizeof(mmp->mmp_bdevname), bdevname(bh->b_bdev, mmp->mmp_bdevname)); if (IS_ERR(EXT4_SB(sb)->s_mmp_tsk)) { diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 37cc47a65cae256623a62ab75b28e9ec8bcab4a2..d61018ca4cfb93fa1330726abada0c0fa2324f94 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -1281,8 +1281,8 @@ static void dx_insert_block(struct dx_frame *frame, u32 hash, ext4_lblk_t block) int ext4_ci_compare(const struct inode *parent, const struct qstr *name, const struct qstr *entry, bool quick) { - const struct ext4_sb_info *sbi = EXT4_SB(parent->i_sb); - const struct unicode_map *um = sbi->s_encoding; + const struct super_block *sb = parent->i_sb; + const struct unicode_map *um = sb->s_encoding; int ret; if (quick) @@ -1294,7 +1294,7 @@ int ext4_ci_compare(const struct inode *parent, const struct qstr *name, /* Handle invalid character sequence as either an error * or as an opaque byte sequence. */ - if (ext4_has_strict_mode(sbi)) + if (sb_has_enc_strict_mode(sb)) return -EINVAL; if (name->len != entry->len) @@ -1311,7 +1311,7 @@ void ext4_fname_setup_ci_filename(struct inode *dir, const struct qstr *iname, { int len; - if (!IS_CASEFOLDED(dir) || !EXT4_SB(dir->i_sb)->s_encoding) { + if (!needs_casefold(dir)) { cf_name->name = NULL; return; } @@ -1320,7 +1320,7 @@ void ext4_fname_setup_ci_filename(struct inode *dir, const struct qstr *iname, if (!cf_name->name) return; - len = utf8_casefold(EXT4_SB(dir->i_sb)->s_encoding, + len = utf8_casefold(dir->i_sb->s_encoding, iname, cf_name->name, EXT4_NAME_LEN); if (len <= 0) { @@ -1357,7 +1357,7 @@ static inline bool ext4_match(const struct inode *parent, #endif #ifdef CONFIG_UNICODE - if (EXT4_SB(parent->i_sb)->s_encoding && IS_CASEFOLDED(parent)) { + if (needs_casefold(parent)) { if (fname->cf_name.name) { struct qstr cf = {.name = fname->cf_name.name, .len = fname->cf_name.len}; @@ -1506,6 +1506,7 @@ static struct buffer_head *__ext4_find_entry(struct inode *dir, /* * We deal with the read-ahead logic here. */ + cond_resched(); if (ra_ptr >= ra_max) { /* Refill the readahead buffer */ ra_ptr = 0; @@ -1608,6 +1609,7 @@ static struct buffer_head *ext4_lookup_entry(struct inode *dir, struct buffer_head *bh; err = ext4_fname_prepare_lookup(dir, dentry, &fname); + generic_set_encrypted_ci_d_ops(dir, dentry); if (err == -ENOENT) return NULL; if (err) @@ -2173,7 +2175,6 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, struct ext4_dir_entry_2 *de; struct ext4_dir_entry_tail *t; struct super_block *sb; - struct ext4_sb_info *sbi; struct ext4_filename fname; int retval; int dx_fallback=0; @@ -2185,14 +2186,13 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, csum_size = sizeof(struct ext4_dir_entry_tail); sb = dir->i_sb; - sbi = EXT4_SB(sb); blocksize = sb->s_blocksize; if (!dentry->d_name.len) return -EINVAL; #ifdef CONFIG_UNICODE - if (ext4_has_strict_mode(sbi) && IS_CASEFOLDED(dir) && - sbi->s_encoding && utf8_validate(sbi->s_encoding, &dentry->d_name)) + if (sb_has_enc_strict_mode(sb) && IS_CASEFOLDED(dir) && + sb->s_encoding && utf8_validate(sb->s_encoding, &dentry->d_name)) return -EINVAL; #endif @@ -2214,6 +2214,13 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, retval = ext4_dx_add_entry(handle, &fname, dir, inode); if (!retval || (retval != ERR_BAD_DX_DIR)) goto out; + /* Can we just ignore htree data? */ + if (ext4_has_metadata_csum(sb)) { + EXT4_ERROR_INODE(dir, + "Directory has corrupted htree index."); + retval = -EFSCORRUPTED; + goto out; + } ext4_clear_inode_flag(dir, EXT4_INODE_INDEX); dx_fallback++; ext4_mark_inode_dirty(handle, dir); diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c index 85180868a87d01578eb7609251c9fb724f6b05b6..e470740ef37f6de849effe8dabc955771045b20c 100644 --- a/fs/ext4/page-io.c +++ b/fs/ext4/page-io.c @@ -474,17 +474,26 @@ int ext4_bio_write_page(struct ext4_io_submit *io, if (fscrypt_inode_uses_fs_layer_crypto(inode) && nr_to_submit) { gfp_t gfp_flags = GFP_NOFS; + /* + * Since bounce page allocation uses a mempool, we can only use + * a waiting mask (i.e. request guaranteed allocation) on the + * first page of the bio. Otherwise it can deadlock. + */ + if (io->io_bio) + gfp_flags = GFP_NOWAIT | __GFP_NOWARN; retry_encrypt: bounce_page = fscrypt_encrypt_pagecache_blocks(page, PAGE_SIZE,0, gfp_flags); if (IS_ERR(bounce_page)) { ret = PTR_ERR(bounce_page); - if (ret == -ENOMEM && wbc->sync_mode == WB_SYNC_ALL) { - if (io->io_bio) { + if (ret == -ENOMEM && + (io->io_bio || wbc->sync_mode == WB_SYNC_ALL)) { + gfp_flags = GFP_NOFS; + if (io->io_bio) ext4_io_submit(io); - congestion_wait(BLK_RW_ASYNC, HZ/50); - } - gfp_flags |= __GFP_NOFAIL; + else + gfp_flags |= __GFP_NOFAIL; + congestion_wait(BLK_RW_ASYNC, HZ/50); goto retry_encrypt; } bounce_page = NULL; diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c index 4d5c0fc9d23a78533ed6fcca349f03550e1c2521..ef552d93708e2a0369588ad09229e605963aad7e 100644 --- a/fs/ext4/resize.c +++ b/fs/ext4/resize.c @@ -17,6 +17,33 @@ #include "ext4_jbd2.h" +struct ext4_rcu_ptr { + struct rcu_head rcu; + void *ptr; +}; + +static void ext4_rcu_ptr_callback(struct rcu_head *head) +{ + struct ext4_rcu_ptr *ptr; + + ptr = container_of(head, struct ext4_rcu_ptr, rcu); + kvfree(ptr->ptr); + kfree(ptr); +} + +void ext4_kvfree_array_rcu(void *to_free) +{ + struct ext4_rcu_ptr *ptr = kzalloc(sizeof(*ptr), GFP_KERNEL); + + if (ptr) { + ptr->ptr = to_free; + call_rcu(&ptr->rcu, ext4_rcu_ptr_callback); + return; + } + synchronize_rcu(); + kvfree(to_free); +} + int ext4_resize_begin(struct super_block *sb) { struct ext4_sb_info *sbi = EXT4_SB(sb); @@ -560,8 +587,8 @@ static int setup_new_flex_group_blocks(struct super_block *sb, brelse(gdb); goto out; } - memcpy(gdb->b_data, sbi->s_group_desc[j]->b_data, - gdb->b_size); + memcpy(gdb->b_data, sbi_array_rcu_deref(sbi, + s_group_desc, j)->b_data, gdb->b_size); set_buffer_uptodate(gdb); err = ext4_handle_dirty_metadata(handle, NULL, gdb); @@ -879,13 +906,15 @@ static int add_new_gdb(handle_t *handle, struct inode *inode, } brelse(dind); - o_group_desc = EXT4_SB(sb)->s_group_desc; + rcu_read_lock(); + o_group_desc = rcu_dereference(EXT4_SB(sb)->s_group_desc); memcpy(n_group_desc, o_group_desc, EXT4_SB(sb)->s_gdb_count * sizeof(struct buffer_head *)); + rcu_read_unlock(); n_group_desc[gdb_num] = gdb_bh; - EXT4_SB(sb)->s_group_desc = n_group_desc; + rcu_assign_pointer(EXT4_SB(sb)->s_group_desc, n_group_desc); EXT4_SB(sb)->s_gdb_count++; - kvfree(o_group_desc); + ext4_kvfree_array_rcu(o_group_desc); le16_add_cpu(&es->s_reserved_gdt_blocks, -1); err = ext4_handle_dirty_super(handle, sb); @@ -929,9 +958,11 @@ static int add_new_gdb_meta_bg(struct super_block *sb, return err; } - o_group_desc = EXT4_SB(sb)->s_group_desc; + rcu_read_lock(); + o_group_desc = rcu_dereference(EXT4_SB(sb)->s_group_desc); memcpy(n_group_desc, o_group_desc, EXT4_SB(sb)->s_gdb_count * sizeof(struct buffer_head *)); + rcu_read_unlock(); n_group_desc[gdb_num] = gdb_bh; BUFFER_TRACE(gdb_bh, "get_write_access"); @@ -942,9 +973,9 @@ static int add_new_gdb_meta_bg(struct super_block *sb, return err; } - EXT4_SB(sb)->s_group_desc = n_group_desc; + rcu_assign_pointer(EXT4_SB(sb)->s_group_desc, n_group_desc); EXT4_SB(sb)->s_gdb_count++; - kvfree(o_group_desc); + ext4_kvfree_array_rcu(o_group_desc); return err; } @@ -1210,7 +1241,8 @@ static int ext4_add_new_descs(handle_t *handle, struct super_block *sb, * use non-sparse filesystems anymore. This is already checked above. */ if (gdb_off) { - gdb_bh = sbi->s_group_desc[gdb_num]; + gdb_bh = sbi_array_rcu_deref(sbi, s_group_desc, + gdb_num); BUFFER_TRACE(gdb_bh, "get_write_access"); err = ext4_journal_get_write_access(handle, gdb_bh); @@ -1292,7 +1324,7 @@ static int ext4_setup_new_descs(handle_t *handle, struct super_block *sb, /* * get_write_access() has been called on gdb_bh by ext4_add_new_desc(). */ - gdb_bh = sbi->s_group_desc[gdb_num]; + gdb_bh = sbi_array_rcu_deref(sbi, s_group_desc, gdb_num); /* Update group descriptor block for new group */ gdp = (struct ext4_group_desc *)(gdb_bh->b_data + gdb_off * EXT4_DESC_SIZE(sb)); @@ -1420,11 +1452,14 @@ static void ext4_update_super(struct super_block *sb, percpu_counter_read(&sbi->s_freeclusters_counter)); if (ext4_has_feature_flex_bg(sb) && sbi->s_log_groups_per_flex) { ext4_group_t flex_group; + struct flex_groups *fg; + flex_group = ext4_flex_group(sbi, group_data[0].group); + fg = sbi_array_rcu_deref(sbi, s_flex_groups, flex_group); atomic64_add(EXT4_NUM_B2C(sbi, free_blocks), - &sbi->s_flex_groups[flex_group].free_clusters); + &fg->free_clusters); atomic_add(EXT4_INODES_PER_GROUP(sb) * flex_gd->count, - &sbi->s_flex_groups[flex_group].free_inodes); + &fg->free_inodes); } /* @@ -1519,7 +1554,8 @@ static int ext4_flex_group_add(struct super_block *sb, for (; gdb_num <= gdb_num_end; gdb_num++) { struct buffer_head *gdb_bh; - gdb_bh = sbi->s_group_desc[gdb_num]; + gdb_bh = sbi_array_rcu_deref(sbi, s_group_desc, + gdb_num); if (old_gdb == gdb_bh->b_blocknr) continue; update_backups(sb, gdb_bh->b_blocknr, gdb_bh->b_data, diff --git a/fs/ext4/super.c b/fs/ext4/super.c index efcb09186bf692c5bb4feea7371937cbff1184e0..21e3f28b02bb47edc162ed2d666054f402d73020 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -970,6 +970,8 @@ static void ext4_put_super(struct super_block *sb) { struct ext4_sb_info *sbi = EXT4_SB(sb); struct ext4_super_block *es = sbi->s_es; + struct buffer_head **group_desc; + struct flex_groups **flex_groups; int aborted = 0; int i, err; @@ -1000,15 +1002,23 @@ static void ext4_put_super(struct super_block *sb) if (!sb_rdonly(sb)) ext4_commit_super(sb, 1); + rcu_read_lock(); + group_desc = rcu_dereference(sbi->s_group_desc); for (i = 0; i < sbi->s_gdb_count; i++) - brelse(sbi->s_group_desc[i]); - kvfree(sbi->s_group_desc); - kvfree(sbi->s_flex_groups); + brelse(group_desc[i]); + kvfree(group_desc); + flex_groups = rcu_dereference(sbi->s_flex_groups); + if (flex_groups) { + for (i = 0; i < sbi->s_flex_groups_allocated; i++) + kvfree(flex_groups[i]); + kvfree(flex_groups); + } + rcu_read_unlock(); percpu_counter_destroy(&sbi->s_freeclusters_counter); percpu_counter_destroy(&sbi->s_freeinodes_counter); percpu_counter_destroy(&sbi->s_dirs_counter); percpu_counter_destroy(&sbi->s_dirtyclusters_counter); - percpu_free_rwsem(&sbi->s_journal_flag_rwsem); + percpu_free_rwsem(&sbi->s_writepages_rwsem); #ifdef CONFIG_QUOTA for (i = 0; i < EXT4_MAXQUOTAS; i++) kfree(get_qf_name(sb, sbi, i)); @@ -1057,7 +1067,7 @@ static void ext4_put_super(struct super_block *sb) kfree(sbi->s_blockgroup_lock); fs_put_dax(sbi->s_daxdev); #ifdef CONFIG_UNICODE - utf8_unload(sbi->s_encoding); + utf8_unload(sb->s_encoding); #endif kfree(sbi); } @@ -1910,6 +1920,13 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token, arg = JBD2_DEFAULT_MAX_COMMIT_AGE; sbi->s_commit_interval = HZ * arg; } else if (token == Opt_debug_want_extra_isize) { + if ((arg & 1) || + (arg < 4) || + (arg > (sbi->s_inode_size - EXT4_GOOD_OLD_INODE_SIZE))) { + ext4_msg(sb, KERN_ERR, + "Invalid want_extra_isize %d", arg); + return -1; + } sbi->s_want_extra_isize = arg; } else if (token == Opt_max_batch_time) { sbi->s_max_batch_time = arg; @@ -2348,8 +2365,8 @@ static int ext4_setup_super(struct super_block *sb, struct ext4_super_block *es, int ext4_alloc_flex_bg_array(struct super_block *sb, ext4_group_t ngroup) { struct ext4_sb_info *sbi = EXT4_SB(sb); - struct flex_groups *new_groups; - int size; + struct flex_groups **old_groups, **new_groups; + int size, i, j; if (!sbi->s_log_groups_per_flex) return 0; @@ -2358,22 +2375,37 @@ int ext4_alloc_flex_bg_array(struct super_block *sb, ext4_group_t ngroup) if (size <= sbi->s_flex_groups_allocated) return 0; - size = roundup_pow_of_two(size * sizeof(struct flex_groups)); - new_groups = kvzalloc(size, GFP_KERNEL); + new_groups = kvzalloc(roundup_pow_of_two(size * + sizeof(*sbi->s_flex_groups)), GFP_KERNEL); if (!new_groups) { - ext4_msg(sb, KERN_ERR, "not enough memory for %d flex groups", - size / (int) sizeof(struct flex_groups)); + ext4_msg(sb, KERN_ERR, + "not enough memory for %d flex group pointers", size); return -ENOMEM; } - - if (sbi->s_flex_groups) { - memcpy(new_groups, sbi->s_flex_groups, - (sbi->s_flex_groups_allocated * - sizeof(struct flex_groups))); - kvfree(sbi->s_flex_groups); + for (i = sbi->s_flex_groups_allocated; i < size; i++) { + new_groups[i] = kvzalloc(roundup_pow_of_two( + sizeof(struct flex_groups)), + GFP_KERNEL); + if (!new_groups[i]) { + for (j = sbi->s_flex_groups_allocated; j < i; j++) + kvfree(new_groups[j]); + kvfree(new_groups); + ext4_msg(sb, KERN_ERR, + "not enough memory for %d flex groups", size); + return -ENOMEM; + } } - sbi->s_flex_groups = new_groups; - sbi->s_flex_groups_allocated = size / sizeof(struct flex_groups); + rcu_read_lock(); + old_groups = rcu_dereference(sbi->s_flex_groups); + if (old_groups) + memcpy(new_groups, old_groups, + (sbi->s_flex_groups_allocated * + sizeof(struct flex_groups *))); + rcu_read_unlock(); + rcu_assign_pointer(sbi->s_flex_groups, new_groups); + sbi->s_flex_groups_allocated = size; + if (old_groups) + ext4_kvfree_array_rcu(old_groups); return 0; } @@ -2381,6 +2413,7 @@ static int ext4_fill_flex_info(struct super_block *sb) { struct ext4_sb_info *sbi = EXT4_SB(sb); struct ext4_group_desc *gdp = NULL; + struct flex_groups *fg; ext4_group_t flex_group; int i, err; @@ -2398,12 +2431,11 @@ static int ext4_fill_flex_info(struct super_block *sb) gdp = ext4_get_group_desc(sb, i, NULL); flex_group = ext4_flex_group(sbi, i); - atomic_add(ext4_free_inodes_count(sb, gdp), - &sbi->s_flex_groups[flex_group].free_inodes); + fg = sbi_array_rcu_deref(sbi, s_flex_groups, flex_group); + atomic_add(ext4_free_inodes_count(sb, gdp), &fg->free_inodes); atomic64_add(ext4_free_group_clusters(sb, gdp), - &sbi->s_flex_groups[flex_group].free_clusters); - atomic_add(ext4_used_dirs_count(sb, gdp), - &sbi->s_flex_groups[flex_group].used_dirs); + &fg->free_clusters); + atomic_add(ext4_used_dirs_count(sb, gdp), &fg->used_dirs); } return 1; @@ -2993,17 +3025,11 @@ static int ext4_feature_set_ok(struct super_block *sb, int readonly) return 0; } -#ifndef CONFIG_QUOTA - if (ext4_has_feature_quota(sb) && !readonly) { +#if !IS_ENABLED(CONFIG_QUOTA) || !IS_ENABLED(CONFIG_QFMT_V2) + if (!readonly && (ext4_has_feature_quota(sb) || + ext4_has_feature_project(sb))) { ext4_msg(sb, KERN_ERR, - "Filesystem with quota feature cannot be mounted RDWR " - "without CONFIG_QUOTA"); - return 0; - } - if (ext4_has_feature_project(sb) && !readonly) { - ext4_msg(sb, KERN_ERR, - "Filesystem with project quota feature cannot be mounted RDWR " - "without CONFIG_QUOTA"); + "The kernel was not built with CONFIG_QUOTA and CONFIG_QFMT_V2"); return 0; } #endif /* CONFIG_QUOTA */ @@ -3590,40 +3616,6 @@ int ext4_calculate_overhead(struct super_block *sb) return 0; } -static void ext4_clamp_want_extra_isize(struct super_block *sb) -{ - struct ext4_sb_info *sbi = EXT4_SB(sb); - struct ext4_super_block *es = sbi->s_es; - unsigned def_extra_isize = sizeof(struct ext4_inode) - - EXT4_GOOD_OLD_INODE_SIZE; - - if (sbi->s_inode_size == EXT4_GOOD_OLD_INODE_SIZE) { - sbi->s_want_extra_isize = 0; - return; - } - if (sbi->s_want_extra_isize < 4) { - sbi->s_want_extra_isize = def_extra_isize; - if (ext4_has_feature_extra_isize(sb)) { - if (sbi->s_want_extra_isize < - le16_to_cpu(es->s_want_extra_isize)) - sbi->s_want_extra_isize = - le16_to_cpu(es->s_want_extra_isize); - if (sbi->s_want_extra_isize < - le16_to_cpu(es->s_min_extra_isize)) - sbi->s_want_extra_isize = - le16_to_cpu(es->s_min_extra_isize); - } - } - /* Check if enough inode space is available */ - if ((sbi->s_want_extra_isize > sbi->s_inode_size) || - (EXT4_GOOD_OLD_INODE_SIZE + sbi->s_want_extra_isize > - sbi->s_inode_size)) { - sbi->s_want_extra_isize = def_extra_isize; - ext4_msg(sb, KERN_INFO, - "required extra inode space not available"); - } -} - static void ext4_set_resv_clusters(struct super_block *sb) { ext4_fsblk_t resv_clusters; @@ -3658,9 +3650,10 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) { struct dax_device *dax_dev = fs_dax_get_by_bdev(sb->s_bdev); char *orig_data = kstrdup(data, GFP_KERNEL); - struct buffer_head *bh; + struct buffer_head *bh, **group_desc; struct ext4_super_block *es = NULL; struct ext4_sb_info *sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); + struct flex_groups **flex_groups; ext4_fsblk_t block; ext4_fsblk_t sb_block = get_sb_block(&data); ext4_fsblk_t logical_sb_block; @@ -3831,6 +3824,75 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) */ sbi->s_li_wait_mult = EXT4_DEF_LI_WAIT_MULT; + blocksize = BLOCK_SIZE << le32_to_cpu(es->s_log_block_size); + if (blocksize < EXT4_MIN_BLOCK_SIZE || + blocksize > EXT4_MAX_BLOCK_SIZE) { + ext4_msg(sb, KERN_ERR, + "Unsupported filesystem blocksize %d (%d log_block_size)", + blocksize, le32_to_cpu(es->s_log_block_size)); + goto failed_mount; + } + + if (le32_to_cpu(es->s_rev_level) == EXT4_GOOD_OLD_REV) { + sbi->s_inode_size = EXT4_GOOD_OLD_INODE_SIZE; + sbi->s_first_ino = EXT4_GOOD_OLD_FIRST_INO; + } else { + sbi->s_inode_size = le16_to_cpu(es->s_inode_size); + sbi->s_first_ino = le32_to_cpu(es->s_first_ino); + if (sbi->s_first_ino < EXT4_GOOD_OLD_FIRST_INO) { + ext4_msg(sb, KERN_ERR, "invalid first ino: %u", + sbi->s_first_ino); + goto failed_mount; + } + if ((sbi->s_inode_size < EXT4_GOOD_OLD_INODE_SIZE) || + (!is_power_of_2(sbi->s_inode_size)) || + (sbi->s_inode_size > blocksize)) { + ext4_msg(sb, KERN_ERR, + "unsupported inode size: %d", + sbi->s_inode_size); + ext4_msg(sb, KERN_ERR, "blocksize: %d", blocksize); + goto failed_mount; + } + /* + * i_atime_extra is the last extra field available for + * [acm]times in struct ext4_inode. Checking for that + * field should suffice to ensure we have extra space + * for all three. + */ + if (sbi->s_inode_size >= offsetof(struct ext4_inode, i_atime_extra) + + sizeof(((struct ext4_inode *)0)->i_atime_extra)) { + sb->s_time_gran = 1; + } else { + sb->s_time_gran = NSEC_PER_SEC; + } + } + if (sbi->s_inode_size > EXT4_GOOD_OLD_INODE_SIZE) { + sbi->s_want_extra_isize = sizeof(struct ext4_inode) - + EXT4_GOOD_OLD_INODE_SIZE; + if (ext4_has_feature_extra_isize(sb)) { + unsigned v, max = (sbi->s_inode_size - + EXT4_GOOD_OLD_INODE_SIZE); + + v = le16_to_cpu(es->s_want_extra_isize); + if (v > max) { + ext4_msg(sb, KERN_ERR, + "bad s_want_extra_isize: %d", v); + goto failed_mount; + } + if (sbi->s_want_extra_isize < v) + sbi->s_want_extra_isize = v; + + v = le16_to_cpu(es->s_min_extra_isize); + if (v > max) { + ext4_msg(sb, KERN_ERR, + "bad s_min_extra_isize: %d", v); + goto failed_mount; + } + if (sbi->s_want_extra_isize < v) + sbi->s_want_extra_isize = v; + } + } + if (sbi->s_es->s_mount_opts[0]) { char *s_mount_opts = kstrndup(sbi->s_es->s_mount_opts, sizeof(sbi->s_es->s_mount_opts), @@ -3851,7 +3913,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) goto failed_mount; #ifdef CONFIG_UNICODE - if (ext4_has_feature_casefold(sb) && !sbi->s_encoding) { + if (ext4_has_feature_casefold(sb) && !sb->s_encoding) { const struct ext4_sb_encodings *encoding_info; struct unicode_map *encoding; __u16 encoding_flags; @@ -3882,8 +3944,8 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) "%s-%s with flags 0x%hx", encoding_info->name, encoding_info->version?:"\b", encoding_flags); - sbi->s_encoding = encoding; - sbi->s_encoding_flags = encoding_flags; + sb->s_encoding = encoding; + sb->s_encoding_flags = encoding_flags; } #endif @@ -3989,14 +4051,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) if (!ext4_feature_set_ok(sb, (sb_rdonly(sb)))) goto failed_mount; - blocksize = BLOCK_SIZE << le32_to_cpu(es->s_log_block_size); - if (blocksize < EXT4_MIN_BLOCK_SIZE || - blocksize > EXT4_MAX_BLOCK_SIZE) { - ext4_msg(sb, KERN_ERR, - "Unsupported filesystem blocksize %d (%d log_block_size)", - blocksize, le32_to_cpu(es->s_log_block_size)); - goto failed_mount; - } if (le32_to_cpu(es->s_log_block_size) > (EXT4_MAX_BLOCK_LOG_SIZE - EXT4_MIN_BLOCK_LOG_SIZE)) { ext4_msg(sb, KERN_ERR, @@ -4069,29 +4123,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) has_huge_files); sb->s_maxbytes = ext4_max_size(sb->s_blocksize_bits, has_huge_files); - if (le32_to_cpu(es->s_rev_level) == EXT4_GOOD_OLD_REV) { - sbi->s_inode_size = EXT4_GOOD_OLD_INODE_SIZE; - sbi->s_first_ino = EXT4_GOOD_OLD_FIRST_INO; - } else { - sbi->s_inode_size = le16_to_cpu(es->s_inode_size); - sbi->s_first_ino = le32_to_cpu(es->s_first_ino); - if (sbi->s_first_ino < EXT4_GOOD_OLD_FIRST_INO) { - ext4_msg(sb, KERN_ERR, "invalid first ino: %u", - sbi->s_first_ino); - goto failed_mount; - } - if ((sbi->s_inode_size < EXT4_GOOD_OLD_INODE_SIZE) || - (!is_power_of_2(sbi->s_inode_size)) || - (sbi->s_inode_size > blocksize)) { - ext4_msg(sb, KERN_ERR, - "unsupported inode size: %d", - sbi->s_inode_size); - goto failed_mount; - } - if (sbi->s_inode_size > EXT4_GOOD_OLD_INODE_SIZE) - sb->s_time_gran = 1 << (EXT4_EPOCH_BITS - 2); - } - sbi->s_desc_size = le16_to_cpu(es->s_desc_size); if (ext4_has_feature_64bit(sb)) { if (sbi->s_desc_size < EXT4_MIN_DESC_SIZE_64BIT || @@ -4275,9 +4306,10 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) goto failed_mount; } } - sbi->s_group_desc = kvmalloc_array(db_count, - sizeof(struct buffer_head *), - GFP_KERNEL); + rcu_assign_pointer(sbi->s_group_desc, + kvmalloc_array(db_count, + sizeof(struct buffer_head *), + GFP_KERNEL)); if (sbi->s_group_desc == NULL) { ext4_msg(sb, KERN_ERR, "not enough memory"); ret = -ENOMEM; @@ -4293,14 +4325,19 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) } for (i = 0; i < db_count; i++) { + struct buffer_head *bh; + block = descriptor_loc(sb, logical_sb_block, i); - sbi->s_group_desc[i] = sb_bread_unmovable(sb, block); - if (!sbi->s_group_desc[i]) { + bh = sb_bread_unmovable(sb, block); + if (!bh) { ext4_msg(sb, KERN_ERR, "can't read group descriptor %d", i); db_count = i; goto failed_mount2; } + rcu_read_lock(); + rcu_dereference(sbi->s_group_desc)[i] = bh; + rcu_read_unlock(); } sbi->s_gdb_count = db_count; if (!ext4_check_descriptors(sb, logical_sb_block, &first_not_zeroed)) { @@ -4530,11 +4567,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) goto failed_mount4; } -#ifdef CONFIG_UNICODE - if (sbi->s_encoding) - sb->s_d_op = &ext4_dentry_ops; -#endif - sb->s_root = d_make_root(root); if (!sb->s_root) { ext4_msg(sb, KERN_ERR, "get root dentry failed"); @@ -4549,8 +4581,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) } else if (ret) goto failed_mount4a; - ext4_clamp_want_extra_isize(sb); - ext4_set_resv_clusters(sb); err = ext4_setup_system_zone(sb); @@ -4588,7 +4618,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) err = percpu_counter_init(&sbi->s_dirtyclusters_counter, 0, GFP_KERNEL); if (!err) - err = percpu_init_rwsem(&sbi->s_journal_flag_rwsem); + err = percpu_init_rwsem(&sbi->s_writepages_rwsem); if (err) { ext4_msg(sb, KERN_ERR, "insufficient memory"); @@ -4676,13 +4706,19 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) ext4_unregister_li_request(sb); failed_mount6: ext4_mb_release(sb); - if (sbi->s_flex_groups) - kvfree(sbi->s_flex_groups); + rcu_read_lock(); + flex_groups = rcu_dereference(sbi->s_flex_groups); + if (flex_groups) { + for (i = 0; i < sbi->s_flex_groups_allocated; i++) + kvfree(flex_groups[i]); + kvfree(flex_groups); + } + rcu_read_unlock(); percpu_counter_destroy(&sbi->s_freeclusters_counter); percpu_counter_destroy(&sbi->s_freeinodes_counter); percpu_counter_destroy(&sbi->s_dirs_counter); percpu_counter_destroy(&sbi->s_dirtyclusters_counter); - percpu_free_rwsem(&sbi->s_journal_flag_rwsem); + percpu_free_rwsem(&sbi->s_writepages_rwsem); failed_mount5: ext4_ext_release(sb); ext4_release_system_zone(sb); @@ -4713,15 +4749,18 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) if (sbi->s_mmp_tsk) kthread_stop(sbi->s_mmp_tsk); failed_mount2: + rcu_read_lock(); + group_desc = rcu_dereference(sbi->s_group_desc); for (i = 0; i < db_count; i++) - brelse(sbi->s_group_desc[i]); - kvfree(sbi->s_group_desc); + brelse(group_desc[i]); + kvfree(group_desc); + rcu_read_unlock(); failed_mount: if (sbi->s_chksum_driver) crypto_free_shash(sbi->s_chksum_driver); #ifdef CONFIG_UNICODE - utf8_unload(sbi->s_encoding); + utf8_unload(sb->s_encoding); #endif #ifdef CONFIG_QUOTA @@ -5340,8 +5379,6 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data) goto restore_opts; } - ext4_clamp_want_extra_isize(sb); - if ((old_opts.s_mount_opt & EXT4_MOUNT_JOURNAL_CHECKSUM) ^ test_opt(sb, JOURNAL_CHECKSUM)) { ext4_msg(sb, KERN_ERR, "changing journal_checksum " diff --git a/fs/ext4/verity.c b/fs/ext4/verity.c index d0d8a9795dd627c517acca997d083b002ff3a1e0..06500560dcddbdbd75579dd93c8c3db1e3253735 100644 --- a/fs/ext4/verity.c +++ b/fs/ext4/verity.c @@ -342,12 +342,57 @@ static int ext4_get_verity_descriptor(struct inode *inode, void *buf, return desc_size; } +/* + * Prefetch some pages from the file's Merkle tree. + * + * This is basically a stripped-down version of __do_page_cache_readahead() + * which works on pages past i_size. + */ +static void ext4_merkle_tree_readahead(struct address_space *mapping, + pgoff_t start_index, unsigned long count) +{ + LIST_HEAD(pages); + unsigned int nr_pages = 0; + struct page *page; + pgoff_t index; + struct blk_plug plug; + + for (index = start_index; index < start_index + count; index++) { + rcu_read_lock(); + page = radix_tree_lookup(&mapping->i_pages, index); + rcu_read_unlock(); + if (!page || radix_tree_exceptional_entry(page)) { + page = __page_cache_alloc(readahead_gfp_mask(mapping)); + if (!page) + break; + page->index = index; + list_add(&page->lru, &pages); + nr_pages++; + } + } + blk_start_plug(&plug); + ext4_mpage_readpages(mapping, &pages, NULL, nr_pages, true); + blk_finish_plug(&plug); +} + static struct page *ext4_read_merkle_tree_page(struct inode *inode, - pgoff_t index) + pgoff_t index, + unsigned long num_ra_pages) { + struct page *page; + index += ext4_verity_metadata_pos(inode) >> PAGE_SHIFT; - return read_mapping_page(inode->i_mapping, index, NULL); + page = find_get_page_flags(inode->i_mapping, index, FGP_ACCESSED); + if (!page || !PageUptodate(page)) { + if (page) + put_page(page); + else if (num_ra_pages > 1) + ext4_merkle_tree_readahead(inode->i_mapping, index, + num_ra_pages); + page = read_mapping_page(inode->i_mapping, index, NULL); + } + return page; } static int ext4_write_merkle_tree_block(struct inode *inode, const void *buf, diff --git a/fs/f2fs/Kconfig b/fs/f2fs/Kconfig index f3414f2ccecd880b40196f803f5455aa4d126467..2dd1bbca241c78196e629a40ccff20f2ff41cf98 100644 --- a/fs/f2fs/Kconfig +++ b/fs/f2fs/Kconfig @@ -5,6 +5,7 @@ config F2FS_FS select CRYPTO select CRYPTO_CRC32 select F2FS_FS_XATTR if FS_ENCRYPTION + select FS_ENCRYPTION_ALGS if FS_ENCRYPTION help F2FS is based on Log-structured File System (LFS), which supports versatile "flash-friendly" features. The design has been focused on @@ -20,7 +21,7 @@ config F2FS_FS config F2FS_STAT_FS bool "F2FS Status Information" - depends on F2FS_FS && DEBUG_FS + depends on F2FS_FS default y help /sys/kernel/debug/f2fs/ contains information about all the partitions @@ -100,3 +101,28 @@ config F2FS_FAULT_INJECTION Test F2FS to inject faults such as ENOMEM, ENOSPC, and so on. If unsure, say N. + +config F2FS_FS_COMPRESSION + bool "F2FS compression feature" + depends on F2FS_FS + help + Enable filesystem-level compression on f2fs regular files, + multiple back-end compression algorithms are supported. + +config F2FS_FS_LZO + bool "LZO compression support" + depends on F2FS_FS_COMPRESSION + select LZO_COMPRESS + select LZO_DECOMPRESS + default y + help + Support LZO compress algorithm, if unsure, say Y. + +config F2FS_FS_LZ4 + bool "LZ4 compression support" + depends on F2FS_FS_COMPRESSION + select LZ4_COMPRESS + select LZ4_DECOMPRESS + default y + help + Support LZ4 compress algorithm, if unsure, say Y. diff --git a/fs/f2fs/Makefile b/fs/f2fs/Makefile index 2aaecc63834fc857891d60f6d3ee94ece1da4983..ee7316b42f69d9cefa0b9141a7a6496c6f839c83 100644 --- a/fs/f2fs/Makefile +++ b/fs/f2fs/Makefile @@ -9,3 +9,4 @@ f2fs-$(CONFIG_F2FS_FS_XATTR) += xattr.o f2fs-$(CONFIG_F2FS_FS_POSIX_ACL) += acl.o f2fs-$(CONFIG_F2FS_IO_TRACE) += trace.o f2fs-$(CONFIG_FS_VERITY) += verity.o +f2fs-$(CONFIG_F2FS_FS_COMPRESSION) += compress.o diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 3a02d79af6d28bdb4fefb5187ef10664ec37e1d1..49c18affde5decc31fd10ff2b1852c77d5ea8009 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1168,6 +1168,11 @@ static int block_operations(struct f2fs_sb_info *sbi) blk_start_plug(&plug); + /* + * Let's flush inline_data in dirty node pages. + */ + f2fs_flush_inline_data(sbi); + retry_flush_quotas: f2fs_lock_all(sbi); if (__need_flush_quota(sbi)) { @@ -1216,19 +1221,21 @@ static int block_operations(struct f2fs_sb_info *sbi) goto retry_flush_quotas; } +retry_flush_nodes: down_write(&sbi->node_write); if (get_pages(sbi, F2FS_DIRTY_NODES)) { up_write(&sbi->node_write); - up_write(&sbi->node_change); - f2fs_unlock_all(sbi); atomic_inc(&sbi->wb_sync_req[NODE]); err = f2fs_sync_node_pages(sbi, &wbc, false, FS_CP_NODE_IO); atomic_dec(&sbi->wb_sync_req[NODE]); - if (err) + if (err) { + up_write(&sbi->node_change); + f2fs_unlock_all(sbi); goto out; + } cond_resched(); - goto retry_flush_quotas; + goto retry_flush_nodes; } /* @@ -1511,10 +1518,10 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) f2fs_wait_on_all_pages_writeback(sbi); /* - * invalidate intermediate page cache borrowed from meta inode - * which are used for migration of encrypted inode's blocks. + * invalidate intermediate page cache borrowed from meta inode which are + * used for migration of encrypted or verity inode's blocks. */ - if (f2fs_sb_has_encrypt(sbi)) + if (f2fs_sb_has_encrypt(sbi) || f2fs_sb_has_verity(sbi)) invalidate_mapping_pages(META_MAPPING(sbi), MAIN_BLKADDR(sbi), MAX_BLKADDR(sbi) - 1); diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c new file mode 100644 index 0000000000000000000000000000000000000000..3fe617d3e4d1cfce402ae278d65f8f8f423ae69b --- /dev/null +++ b/fs/f2fs/compress.c @@ -0,0 +1,1177 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * f2fs compress support + * + * Copyright (c) 2019 Chao Yu + */ + +#include +#include +#include +#include +#include +#include + +#include "f2fs.h" +#include "node.h" +#include + +struct f2fs_compress_ops { + int (*init_compress_ctx)(struct compress_ctx *cc); + void (*destroy_compress_ctx)(struct compress_ctx *cc); + int (*compress_pages)(struct compress_ctx *cc); + int (*decompress_pages)(struct decompress_io_ctx *dic); +}; + +static unsigned int offset_in_cluster(struct compress_ctx *cc, pgoff_t index) +{ + return index & (cc->cluster_size - 1); +} + +static pgoff_t cluster_idx(struct compress_ctx *cc, pgoff_t index) +{ + return index >> cc->log_cluster_size; +} + +static pgoff_t start_idx_of_cluster(struct compress_ctx *cc) +{ + return cc->cluster_idx << cc->log_cluster_size; +} + +bool f2fs_is_compressed_page(struct page *page) +{ + if (!PagePrivate(page)) + return false; + if (!page_private(page)) + return false; + if (IS_ATOMIC_WRITTEN_PAGE(page) || IS_DUMMY_WRITTEN_PAGE(page)) + return false; + f2fs_bug_on(F2FS_M_SB(page->mapping), + *((u32 *)page_private(page)) != F2FS_COMPRESSED_PAGE_MAGIC); + return true; +} + +static void f2fs_set_compressed_page(struct page *page, + struct inode *inode, pgoff_t index, void *data, refcount_t *r) +{ + SetPagePrivate(page); + set_page_private(page, (unsigned long)data); + + /* i_crypto_info and iv index */ + page->index = index; + page->mapping = inode->i_mapping; + if (r) + refcount_inc(r); +} + +static void f2fs_put_compressed_page(struct page *page) +{ + set_page_private(page, (unsigned long)NULL); + ClearPagePrivate(page); + page->mapping = NULL; + unlock_page(page); + put_page(page); +} + +static void f2fs_drop_rpages(struct compress_ctx *cc, int len, bool unlock) +{ + int i; + + for (i = 0; i < len; i++) { + if (!cc->rpages[i]) + continue; + if (unlock) + unlock_page(cc->rpages[i]); + else + put_page(cc->rpages[i]); + } +} + +static void f2fs_put_rpages(struct compress_ctx *cc) +{ + f2fs_drop_rpages(cc, cc->cluster_size, false); +} + +static void f2fs_unlock_rpages(struct compress_ctx *cc, int len) +{ + f2fs_drop_rpages(cc, len, true); +} + +static void f2fs_put_rpages_mapping(struct compress_ctx *cc, + struct address_space *mapping, + pgoff_t start, int len) +{ + int i; + + for (i = 0; i < len; i++) { + struct page *page = find_get_page(mapping, start + i); + + put_page(page); + put_page(page); + } +} + +static void f2fs_put_rpages_wbc(struct compress_ctx *cc, + struct writeback_control *wbc, bool redirty, int unlock) +{ + unsigned int i; + + for (i = 0; i < cc->cluster_size; i++) { + if (!cc->rpages[i]) + continue; + if (redirty) + redirty_page_for_writepage(wbc, cc->rpages[i]); + f2fs_put_page(cc->rpages[i], unlock); + } +} + +struct page *f2fs_compress_control_page(struct page *page) +{ + return ((struct compress_io_ctx *)page_private(page))->rpages[0]; +} + +int f2fs_init_compress_ctx(struct compress_ctx *cc) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(cc->inode); + + if (cc->nr_rpages) + return 0; + + cc->rpages = f2fs_kzalloc(sbi, sizeof(struct page *) << + cc->log_cluster_size, GFP_NOFS); + return cc->rpages ? 0 : -ENOMEM; +} + +void f2fs_destroy_compress_ctx(struct compress_ctx *cc) +{ + kfree(cc->rpages); + cc->rpages = NULL; + cc->nr_rpages = 0; + cc->nr_cpages = 0; + cc->cluster_idx = NULL_CLUSTER; +} + +void f2fs_compress_ctx_add_page(struct compress_ctx *cc, struct page *page) +{ + unsigned int cluster_ofs; + + if (!f2fs_cluster_can_merge_page(cc, page->index)) + f2fs_bug_on(F2FS_I_SB(cc->inode), 1); + + cluster_ofs = offset_in_cluster(cc, page->index); + cc->rpages[cluster_ofs] = page; + cc->nr_rpages++; + cc->cluster_idx = cluster_idx(cc, page->index); +} + +#ifdef CONFIG_F2FS_FS_LZO +static int lzo_init_compress_ctx(struct compress_ctx *cc) +{ + cc->private = f2fs_kvmalloc(F2FS_I_SB(cc->inode), + LZO1X_MEM_COMPRESS, GFP_NOFS); + if (!cc->private) + return -ENOMEM; + + cc->clen = lzo1x_worst_compress(PAGE_SIZE << cc->log_cluster_size); + return 0; +} + +static void lzo_destroy_compress_ctx(struct compress_ctx *cc) +{ + kvfree(cc->private); + cc->private = NULL; +} + +static int lzo_compress_pages(struct compress_ctx *cc) +{ + int ret; + + ret = lzo1x_1_compress(cc->rbuf, cc->rlen, cc->cbuf->cdata, + &cc->clen, cc->private); + if (ret != LZO_E_OK) { + printk_ratelimited("%sF2FS-fs (%s): lzo compress failed, ret:%d\n", + KERN_ERR, F2FS_I_SB(cc->inode)->sb->s_id, ret); + return -EIO; + } + return 0; +} + +static int lzo_decompress_pages(struct decompress_io_ctx *dic) +{ + int ret; + + ret = lzo1x_decompress_safe(dic->cbuf->cdata, dic->clen, + dic->rbuf, &dic->rlen); + if (ret != LZO_E_OK) { + printk_ratelimited("%sF2FS-fs (%s): lzo decompress failed, ret:%d\n", + KERN_ERR, F2FS_I_SB(dic->inode)->sb->s_id, ret); + return -EIO; + } + + if (dic->rlen != PAGE_SIZE << dic->log_cluster_size) { + printk_ratelimited("%sF2FS-fs (%s): lzo invalid rlen:%zu, " + "expected:%lu\n", KERN_ERR, + F2FS_I_SB(dic->inode)->sb->s_id, + dic->rlen, + PAGE_SIZE << dic->log_cluster_size); + return -EIO; + } + return 0; +} + +static const struct f2fs_compress_ops f2fs_lzo_ops = { + .init_compress_ctx = lzo_init_compress_ctx, + .destroy_compress_ctx = lzo_destroy_compress_ctx, + .compress_pages = lzo_compress_pages, + .decompress_pages = lzo_decompress_pages, +}; +#endif + +#ifdef CONFIG_F2FS_FS_LZ4 +static int lz4_init_compress_ctx(struct compress_ctx *cc) +{ + cc->private = f2fs_kvmalloc(F2FS_I_SB(cc->inode), + LZ4_MEM_COMPRESS, GFP_NOFS); + if (!cc->private) + return -ENOMEM; + + cc->clen = LZ4_compressBound(PAGE_SIZE << cc->log_cluster_size); + return 0; +} + +static void lz4_destroy_compress_ctx(struct compress_ctx *cc) +{ + kvfree(cc->private); + cc->private = NULL; +} + +static int lz4_compress_pages(struct compress_ctx *cc) +{ + int len; + + len = LZ4_compress_default(cc->rbuf, cc->cbuf->cdata, cc->rlen, + cc->clen, cc->private); + if (!len) { + printk_ratelimited("%sF2FS-fs (%s): lz4 compress failed\n", + KERN_ERR, F2FS_I_SB(cc->inode)->sb->s_id); + return -EIO; + } + cc->clen = len; + return 0; +} + +static int lz4_decompress_pages(struct decompress_io_ctx *dic) +{ + int ret; + + ret = LZ4_decompress_safe(dic->cbuf->cdata, dic->rbuf, + dic->clen, dic->rlen); + if (ret < 0) { + printk_ratelimited("%sF2FS-fs (%s): lz4 decompress failed, ret:%d\n", + KERN_ERR, F2FS_I_SB(dic->inode)->sb->s_id, ret); + return -EIO; + } + + if (ret != PAGE_SIZE << dic->log_cluster_size) { + printk_ratelimited("%sF2FS-fs (%s): lz4 invalid rlen:%zu, " + "expected:%lu\n", KERN_ERR, + F2FS_I_SB(dic->inode)->sb->s_id, + dic->rlen, + PAGE_SIZE << dic->log_cluster_size); + return -EIO; + } + return 0; +} + +static const struct f2fs_compress_ops f2fs_lz4_ops = { + .init_compress_ctx = lz4_init_compress_ctx, + .destroy_compress_ctx = lz4_destroy_compress_ctx, + .compress_pages = lz4_compress_pages, + .decompress_pages = lz4_decompress_pages, +}; +#endif + +static const struct f2fs_compress_ops *f2fs_cops[COMPRESS_MAX] = { +#ifdef CONFIG_F2FS_FS_LZO + &f2fs_lzo_ops, +#else + NULL, +#endif +#ifdef CONFIG_F2FS_FS_LZ4 + &f2fs_lz4_ops, +#else + NULL, +#endif +}; + +bool f2fs_is_compress_backend_ready(struct inode *inode) +{ + if (!f2fs_compressed_file(inode)) + return true; + return f2fs_cops[F2FS_I(inode)->i_compress_algorithm]; +} + +static struct page *f2fs_grab_page(void) +{ + struct page *page; + + page = alloc_page(GFP_NOFS); + if (!page) + return NULL; + lock_page(page); + return page; +} + +static int f2fs_compress_pages(struct compress_ctx *cc) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(cc->inode); + struct f2fs_inode_info *fi = F2FS_I(cc->inode); + const struct f2fs_compress_ops *cops = + f2fs_cops[fi->i_compress_algorithm]; + unsigned int max_len, nr_cpages; + int i, ret; + + trace_f2fs_compress_pages_start(cc->inode, cc->cluster_idx, + cc->cluster_size, fi->i_compress_algorithm); + + ret = cops->init_compress_ctx(cc); + if (ret) + goto out; + + max_len = COMPRESS_HEADER_SIZE + cc->clen; + cc->nr_cpages = DIV_ROUND_UP(max_len, PAGE_SIZE); + + cc->cpages = f2fs_kzalloc(sbi, sizeof(struct page *) * + cc->nr_cpages, GFP_NOFS); + if (!cc->cpages) { + ret = -ENOMEM; + goto destroy_compress_ctx; + } + + for (i = 0; i < cc->nr_cpages; i++) { + cc->cpages[i] = f2fs_grab_page(); + if (!cc->cpages[i]) { + ret = -ENOMEM; + goto out_free_cpages; + } + } + + cc->rbuf = vmap(cc->rpages, cc->cluster_size, VM_MAP, PAGE_KERNEL_RO); + if (!cc->rbuf) { + ret = -ENOMEM; + goto out_free_cpages; + } + + cc->cbuf = vmap(cc->cpages, cc->nr_cpages, VM_MAP, PAGE_KERNEL); + if (!cc->cbuf) { + ret = -ENOMEM; + goto out_vunmap_rbuf; + } + + ret = cops->compress_pages(cc); + if (ret) + goto out_vunmap_cbuf; + + max_len = PAGE_SIZE * (cc->cluster_size - 1) - COMPRESS_HEADER_SIZE; + + if (cc->clen > max_len) { + ret = -EAGAIN; + goto out_vunmap_cbuf; + } + + cc->cbuf->clen = cpu_to_le32(cc->clen); + cc->cbuf->chksum = cpu_to_le32(0); + + for (i = 0; i < COMPRESS_DATA_RESERVED_SIZE; i++) + cc->cbuf->reserved[i] = cpu_to_le32(0); + + vunmap(cc->cbuf); + vunmap(cc->rbuf); + + nr_cpages = DIV_ROUND_UP(cc->clen + COMPRESS_HEADER_SIZE, PAGE_SIZE); + + for (i = nr_cpages; i < cc->nr_cpages; i++) { + f2fs_put_compressed_page(cc->cpages[i]); + cc->cpages[i] = NULL; + } + + cc->nr_cpages = nr_cpages; + + trace_f2fs_compress_pages_end(cc->inode, cc->cluster_idx, + cc->clen, ret); + return 0; + +out_vunmap_cbuf: + vunmap(cc->cbuf); +out_vunmap_rbuf: + vunmap(cc->rbuf); +out_free_cpages: + for (i = 0; i < cc->nr_cpages; i++) { + if (cc->cpages[i]) + f2fs_put_compressed_page(cc->cpages[i]); + } + kfree(cc->cpages); + cc->cpages = NULL; +destroy_compress_ctx: + cops->destroy_compress_ctx(cc); +out: + trace_f2fs_compress_pages_end(cc->inode, cc->cluster_idx, + cc->clen, ret); + return ret; +} + +void f2fs_decompress_pages(struct bio *bio, struct page *page, bool verity) +{ + struct decompress_io_ctx *dic = + (struct decompress_io_ctx *)page_private(page); + struct f2fs_sb_info *sbi = F2FS_I_SB(dic->inode); + struct f2fs_inode_info *fi= F2FS_I(dic->inode); + const struct f2fs_compress_ops *cops = + f2fs_cops[fi->i_compress_algorithm]; + int ret; + + dec_page_count(sbi, F2FS_RD_DATA); + + if (bio->bi_status || PageError(page)) + dic->failed = true; + + if (refcount_dec_not_one(&dic->ref)) + return; + + trace_f2fs_decompress_pages_start(dic->inode, dic->cluster_idx, + dic->cluster_size, fi->i_compress_algorithm); + + /* submit partial compressed pages */ + if (dic->failed) { + ret = -EIO; + goto out_free_dic; + } + + dic->rbuf = vmap(dic->tpages, dic->cluster_size, VM_MAP, PAGE_KERNEL); + if (!dic->rbuf) { + ret = -ENOMEM; + goto out_free_dic; + } + + dic->cbuf = vmap(dic->cpages, dic->nr_cpages, VM_MAP, PAGE_KERNEL_RO); + if (!dic->cbuf) { + ret = -ENOMEM; + goto out_vunmap_rbuf; + } + + dic->clen = le32_to_cpu(dic->cbuf->clen); + dic->rlen = PAGE_SIZE << dic->log_cluster_size; + + if (dic->clen > PAGE_SIZE * dic->nr_cpages - COMPRESS_HEADER_SIZE) { + ret = -EFSCORRUPTED; + goto out_vunmap_cbuf; + } + + ret = cops->decompress_pages(dic); + +out_vunmap_cbuf: + vunmap(dic->cbuf); +out_vunmap_rbuf: + vunmap(dic->rbuf); +out_free_dic: + if (!verity) + f2fs_decompress_end_io(dic->rpages, dic->cluster_size, + ret, false); + + trace_f2fs_decompress_pages_end(dic->inode, dic->cluster_idx, + dic->clen, ret); + if (!verity) + f2fs_free_dic(dic); +} + +static bool is_page_in_cluster(struct compress_ctx *cc, pgoff_t index) +{ + if (cc->cluster_idx == NULL_CLUSTER) + return true; + return cc->cluster_idx == cluster_idx(cc, index); +} + +bool f2fs_cluster_is_empty(struct compress_ctx *cc) +{ + return cc->nr_rpages == 0; +} + +static bool f2fs_cluster_is_full(struct compress_ctx *cc) +{ + return cc->cluster_size == cc->nr_rpages; +} + +bool f2fs_cluster_can_merge_page(struct compress_ctx *cc, pgoff_t index) +{ + if (f2fs_cluster_is_empty(cc)) + return true; + return is_page_in_cluster(cc, index); +} + +static bool __cluster_may_compress(struct compress_ctx *cc) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(cc->inode); + loff_t i_size = i_size_read(cc->inode); + unsigned nr_pages = DIV_ROUND_UP(i_size, PAGE_SIZE); + int i; + + for (i = 0; i < cc->cluster_size; i++) { + struct page *page = cc->rpages[i]; + + f2fs_bug_on(sbi, !page); + + if (unlikely(f2fs_cp_error(sbi))) + return false; + if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) + return false; + + /* beyond EOF */ + if (page->index >= nr_pages) + return false; + } + return true; +} + +/* return # of compressed block addresses */ +static int f2fs_compressed_blocks(struct compress_ctx *cc) +{ + struct dnode_of_data dn; + int ret; + + set_new_dnode(&dn, cc->inode, NULL, NULL, 0); + ret = f2fs_get_dnode_of_data(&dn, start_idx_of_cluster(cc), + LOOKUP_NODE); + if (ret) { + if (ret == -ENOENT) + ret = 0; + goto fail; + } + + if (dn.data_blkaddr == COMPRESS_ADDR) { + int i; + + ret = 1; + for (i = 1; i < cc->cluster_size; i++) { + block_t blkaddr; + + blkaddr = datablock_addr(dn.inode, + dn.node_page, dn.ofs_in_node + i); + if (blkaddr != NULL_ADDR) + ret++; + } + } +fail: + f2fs_put_dnode(&dn); + return ret; +} + +int f2fs_is_compressed_cluster(struct inode *inode, pgoff_t index) +{ + struct compress_ctx cc = { + .inode = inode, + .log_cluster_size = F2FS_I(inode)->i_log_cluster_size, + .cluster_size = F2FS_I(inode)->i_cluster_size, + .cluster_idx = index >> F2FS_I(inode)->i_log_cluster_size, + }; + + return f2fs_compressed_blocks(&cc); +} + +static bool cluster_may_compress(struct compress_ctx *cc) +{ + if (!f2fs_compressed_file(cc->inode)) + return false; + if (f2fs_is_atomic_file(cc->inode)) + return false; + if (f2fs_is_mmap_file(cc->inode)) + return false; + if (!f2fs_cluster_is_full(cc)) + return false; + return __cluster_may_compress(cc); +} + +static void set_cluster_writeback(struct compress_ctx *cc) +{ + int i; + + for (i = 0; i < cc->cluster_size; i++) { + if (cc->rpages[i]) + set_page_writeback(cc->rpages[i]); + } +} + +static void set_cluster_dirty(struct compress_ctx *cc) +{ + int i; + + for (i = 0; i < cc->cluster_size; i++) + if (cc->rpages[i]) + set_page_dirty(cc->rpages[i]); +} + +static int prepare_compress_overwrite(struct compress_ctx *cc, + struct page **pagep, pgoff_t index, void **fsdata) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(cc->inode); + struct address_space *mapping = cc->inode->i_mapping; + struct page *page; + struct dnode_of_data dn; + sector_t last_block_in_bio; + unsigned fgp_flag = FGP_LOCK | FGP_WRITE | FGP_CREAT; + pgoff_t start_idx = start_idx_of_cluster(cc); + int i, ret; + bool prealloc; + +retry: + ret = f2fs_compressed_blocks(cc); + if (ret <= 0) + return ret; + + /* compressed case */ + prealloc = (ret < cc->cluster_size); + + ret = f2fs_init_compress_ctx(cc); + if (ret) + return ret; + + /* keep page reference to avoid page reclaim */ + for (i = 0; i < cc->cluster_size; i++) { + page = f2fs_pagecache_get_page(mapping, start_idx + i, + fgp_flag, GFP_NOFS); + if (!page) { + ret = -ENOMEM; + goto unlock_pages; + } + + if (PageUptodate(page)) + unlock_page(page); + else + f2fs_compress_ctx_add_page(cc, page); + } + + if (!f2fs_cluster_is_empty(cc)) { + struct bio *bio = NULL; + + ret = f2fs_read_multi_pages(cc, &bio, cc->cluster_size, + &last_block_in_bio, false); + f2fs_destroy_compress_ctx(cc); + if (ret) + goto release_pages; + if (bio) + f2fs_submit_bio(sbi, bio, DATA); + + ret = f2fs_init_compress_ctx(cc); + if (ret) + goto release_pages; + } + + for (i = 0; i < cc->cluster_size; i++) { + f2fs_bug_on(sbi, cc->rpages[i]); + + page = find_lock_page(mapping, start_idx + i); + f2fs_bug_on(sbi, !page); + + f2fs_wait_on_page_writeback(page, DATA, true, true); + + f2fs_compress_ctx_add_page(cc, page); + f2fs_put_page(page, 0); + + if (!PageUptodate(page)) { + f2fs_unlock_rpages(cc, i + 1); + f2fs_put_rpages_mapping(cc, mapping, start_idx, + cc->cluster_size); + f2fs_destroy_compress_ctx(cc); + goto retry; + } + } + + if (prealloc) { + __do_map_lock(sbi, F2FS_GET_BLOCK_PRE_AIO, true); + + set_new_dnode(&dn, cc->inode, NULL, NULL, 0); + + for (i = cc->cluster_size - 1; i > 0; i--) { + ret = f2fs_get_block(&dn, start_idx + i); + if (ret) { + i = cc->cluster_size; + break; + } + + if (dn.data_blkaddr != NEW_ADDR) + break; + } + + __do_map_lock(sbi, F2FS_GET_BLOCK_PRE_AIO, false); + } + + if (likely(!ret)) { + *fsdata = cc->rpages; + *pagep = cc->rpages[offset_in_cluster(cc, index)]; + return cc->cluster_size; + } + +unlock_pages: + f2fs_unlock_rpages(cc, i); +release_pages: + f2fs_put_rpages_mapping(cc, mapping, start_idx, i); + f2fs_destroy_compress_ctx(cc); + return ret; +} + +int f2fs_prepare_compress_overwrite(struct inode *inode, + struct page **pagep, pgoff_t index, void **fsdata) +{ + struct compress_ctx cc = { + .inode = inode, + .log_cluster_size = F2FS_I(inode)->i_log_cluster_size, + .cluster_size = F2FS_I(inode)->i_cluster_size, + .cluster_idx = index >> F2FS_I(inode)->i_log_cluster_size, + .rpages = NULL, + .nr_rpages = 0, + }; + + return prepare_compress_overwrite(&cc, pagep, index, fsdata); +} + +bool f2fs_compress_write_end(struct inode *inode, void *fsdata, + pgoff_t index, unsigned copied) + +{ + struct compress_ctx cc = { + .log_cluster_size = F2FS_I(inode)->i_log_cluster_size, + .cluster_size = F2FS_I(inode)->i_cluster_size, + .rpages = fsdata, + }; + bool first_index = (index == cc.rpages[0]->index); + + if (copied) + set_cluster_dirty(&cc); + + f2fs_put_rpages_wbc(&cc, NULL, false, 1); + f2fs_destroy_compress_ctx(&cc); + + return first_index; +} + +static int f2fs_write_compressed_pages(struct compress_ctx *cc, + int *submitted, + struct writeback_control *wbc, + enum iostat_type io_type) +{ + struct inode *inode = cc->inode; + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct f2fs_inode_info *fi = F2FS_I(inode); + struct f2fs_io_info fio = { + .sbi = sbi, + .ino = cc->inode->i_ino, + .type = DATA, + .op = REQ_OP_WRITE, + .op_flags = wbc_to_write_flags(wbc), + .old_blkaddr = NEW_ADDR, + .page = NULL, + .encrypted_page = NULL, + .compressed_page = NULL, + .submitted = false, + .need_lock = LOCK_RETRY, + .io_type = io_type, + .io_wbc = wbc, + .encrypted = f2fs_encrypted_file(cc->inode), + }; + struct dnode_of_data dn; + struct node_info ni; + struct compress_io_ctx *cic; + pgoff_t start_idx = start_idx_of_cluster(cc); + unsigned int last_index = cc->cluster_size - 1; + loff_t psize; + int i, err; + + set_new_dnode(&dn, cc->inode, NULL, NULL, 0); + + f2fs_lock_op(sbi); + + err = f2fs_get_dnode_of_data(&dn, start_idx, LOOKUP_NODE); + if (err) + goto out_unlock_op; + + for (i = 0; i < cc->cluster_size; i++) { + if (datablock_addr(dn.inode, dn.node_page, + dn.ofs_in_node + i) == NULL_ADDR) + goto out_put_dnode; + } + + psize = (loff_t)(cc->rpages[last_index]->index + 1) << PAGE_SHIFT; + + err = f2fs_get_node_info(fio.sbi, dn.nid, &ni); + if (err) + goto out_put_dnode; + + fio.version = ni.version; + + cic = f2fs_kzalloc(sbi, sizeof(struct compress_io_ctx), GFP_NOFS); + if (!cic) + goto out_put_dnode; + + cic->magic = F2FS_COMPRESSED_PAGE_MAGIC; + cic->inode = inode; + refcount_set(&cic->ref, 1); + cic->rpages = f2fs_kzalloc(sbi, sizeof(struct page *) << + cc->log_cluster_size, GFP_NOFS); + if (!cic->rpages) + goto out_put_cic; + + cic->nr_rpages = cc->cluster_size; + + for (i = 0; i < cc->nr_cpages; i++) { + f2fs_set_compressed_page(cc->cpages[i], inode, + cc->rpages[i + 1]->index, + cic, i ? &cic->ref : NULL); + fio.compressed_page = cc->cpages[i]; + if (fio.encrypted) { + fio.page = cc->rpages[i + 1]; + err = f2fs_encrypt_one_page(&fio); + if (err) + goto out_destroy_crypt; + if (fscrypt_inode_uses_fs_layer_crypto(inode)) + cc->cpages[i] = fio.encrypted_page; + } + } + + set_cluster_writeback(cc); + + for (i = 0; i < cc->cluster_size; i++) + cic->rpages[i] = cc->rpages[i]; + + for (i = 0; i < cc->cluster_size; i++, dn.ofs_in_node++) { + block_t blkaddr; + + blkaddr = datablock_addr(dn.inode, dn.node_page, + dn.ofs_in_node); + fio.page = cic->rpages[i]; + fio.old_blkaddr = blkaddr; + + /* cluster header */ + if (i == 0) { + if (blkaddr == COMPRESS_ADDR) + fio.compr_blocks++; + if (__is_valid_data_blkaddr(blkaddr)) + f2fs_invalidate_blocks(sbi, blkaddr); + f2fs_update_data_blkaddr(&dn, COMPRESS_ADDR); + goto unlock_continue; + } + + if (fio.compr_blocks && __is_valid_data_blkaddr(blkaddr)) + fio.compr_blocks++; + + if (i > cc->nr_cpages) { + if (__is_valid_data_blkaddr(blkaddr)) { + f2fs_invalidate_blocks(sbi, blkaddr); + f2fs_update_data_blkaddr(&dn, NEW_ADDR); + } + goto unlock_continue; + } + + f2fs_bug_on(fio.sbi, blkaddr == NULL_ADDR); + + if (fio.encrypted && fscrypt_inode_uses_fs_layer_crypto(inode)) + fio.encrypted_page = cc->cpages[i - 1]; + else + fio.compressed_page = cc->cpages[i - 1]; + + cc->cpages[i - 1] = NULL; + f2fs_outplace_write_data(&dn, &fio); + (*submitted)++; +unlock_continue: + inode_dec_dirty_pages(cc->inode); + unlock_page(fio.page); + } + + if (fio.compr_blocks) + f2fs_i_compr_blocks_update(inode, fio.compr_blocks - 1, false); + f2fs_i_compr_blocks_update(inode, cc->nr_cpages, true); + + set_inode_flag(cc->inode, FI_APPEND_WRITE); + if (cc->cluster_idx == 0) + set_inode_flag(inode, FI_FIRST_BLOCK_WRITTEN); + + f2fs_put_dnode(&dn); + f2fs_unlock_op(sbi); + + down_write(&fi->i_sem); + if (fi->last_disk_size < psize) + fi->last_disk_size = psize; + up_write(&fi->i_sem); + + f2fs_put_rpages(cc); + f2fs_destroy_compress_ctx(cc); + return 0; + +out_destroy_crypt: + kfree(cic->rpages); + + for (--i; i >= 0; i--) + fscrypt_finalize_bounce_page(&cc->cpages[i]); + for (i = 0; i < cc->nr_cpages; i++) { + if (!cc->cpages[i]) + continue; + f2fs_put_page(cc->cpages[i], 1); + } +out_put_cic: + kfree(cic); +out_put_dnode: + f2fs_put_dnode(&dn); +out_unlock_op: + f2fs_unlock_op(sbi); + return -EAGAIN; +} + +void f2fs_compress_write_end_io(struct bio *bio, struct page *page) +{ + struct f2fs_sb_info *sbi = bio->bi_private; + struct compress_io_ctx *cic = + (struct compress_io_ctx *)page_private(page); + int i; + + if (unlikely(bio->bi_status)) + mapping_set_error(cic->inode->i_mapping, -EIO); + + f2fs_put_compressed_page(page); + + dec_page_count(sbi, F2FS_WB_DATA); + + if (refcount_dec_not_one(&cic->ref)) + return; + + for (i = 0; i < cic->nr_rpages; i++) { + WARN_ON(!cic->rpages[i]); + clear_cold_data(cic->rpages[i]); + end_page_writeback(cic->rpages[i]); + } + + kfree(cic->rpages); + kfree(cic); +} + +static int f2fs_write_raw_pages(struct compress_ctx *cc, + int *submitted, + struct writeback_control *wbc, + enum iostat_type io_type) +{ + struct address_space *mapping = cc->inode->i_mapping; + int _submitted, compr_blocks, ret; + int i = -1, err = 0; + + compr_blocks = f2fs_compressed_blocks(cc); + if (compr_blocks < 0) { + err = compr_blocks; + goto out_err; + } + + for (i = 0; i < cc->cluster_size; i++) { + if (!cc->rpages[i]) + continue; +retry_write: + if (cc->rpages[i]->mapping != mapping) { + unlock_page(cc->rpages[i]); + continue; + } + + BUG_ON(!PageLocked(cc->rpages[i])); + + ret = f2fs_write_single_data_page(cc->rpages[i], &_submitted, + NULL, NULL, wbc, io_type, + compr_blocks); + if (ret) { + if (ret == AOP_WRITEPAGE_ACTIVATE) { + unlock_page(cc->rpages[i]); + ret = 0; + } else if (ret == -EAGAIN) { + ret = 0; + cond_resched(); + congestion_wait(BLK_RW_ASYNC, HZ/50); + lock_page(cc->rpages[i]); + clear_page_dirty_for_io(cc->rpages[i]); + goto retry_write; + } + err = ret; + goto out_fail; + } + + *submitted += _submitted; + } + return 0; + +out_fail: + /* TODO: revoke partially updated block addresses */ + BUG_ON(compr_blocks); +out_err: + for (++i; i < cc->cluster_size; i++) { + if (!cc->rpages[i]) + continue; + redirty_page_for_writepage(wbc, cc->rpages[i]); + unlock_page(cc->rpages[i]); + } + return err; +} + +int f2fs_write_multi_pages(struct compress_ctx *cc, + int *submitted, + struct writeback_control *wbc, + enum iostat_type io_type) +{ + struct f2fs_inode_info *fi = F2FS_I(cc->inode); + const struct f2fs_compress_ops *cops = + f2fs_cops[fi->i_compress_algorithm]; + int err; + + *submitted = 0; + if (cluster_may_compress(cc)) { + err = f2fs_compress_pages(cc); + if (err == -EAGAIN) { + goto write; + } else if (err) { + f2fs_put_rpages_wbc(cc, wbc, true, 1); + goto destroy_out; + } + + err = f2fs_write_compressed_pages(cc, submitted, + wbc, io_type); + cops->destroy_compress_ctx(cc); + if (!err) + return 0; + f2fs_bug_on(F2FS_I_SB(cc->inode), err != -EAGAIN); + } +write: + f2fs_bug_on(F2FS_I_SB(cc->inode), *submitted); + + err = f2fs_write_raw_pages(cc, submitted, wbc, io_type); + f2fs_put_rpages_wbc(cc, wbc, false, 0); +destroy_out: + f2fs_destroy_compress_ctx(cc); + return err; +} + +struct decompress_io_ctx *f2fs_alloc_dic(struct compress_ctx *cc) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(cc->inode); + struct decompress_io_ctx *dic; + pgoff_t start_idx = start_idx_of_cluster(cc); + int i; + + dic = f2fs_kzalloc(sbi, sizeof(struct decompress_io_ctx), GFP_NOFS); + if (!dic) + return ERR_PTR(-ENOMEM); + + dic->rpages = f2fs_kzalloc(sbi, sizeof(struct page *) << + cc->log_cluster_size, GFP_NOFS); + if (!dic->rpages) { + kfree(dic); + return ERR_PTR(-ENOMEM); + } + + dic->magic = F2FS_COMPRESSED_PAGE_MAGIC; + dic->inode = cc->inode; + refcount_set(&dic->ref, 1); + dic->cluster_idx = cc->cluster_idx; + dic->cluster_size = cc->cluster_size; + dic->log_cluster_size = cc->log_cluster_size; + dic->nr_cpages = cc->nr_cpages; + dic->failed = false; + + for (i = 0; i < dic->cluster_size; i++) + dic->rpages[i] = cc->rpages[i]; + dic->nr_rpages = cc->cluster_size; + + dic->cpages = f2fs_kzalloc(sbi, sizeof(struct page *) * + dic->nr_cpages, GFP_NOFS); + if (!dic->cpages) + goto out_free; + + for (i = 0; i < dic->nr_cpages; i++) { + struct page *page; + + page = f2fs_grab_page(); + if (!page) + goto out_free; + + f2fs_set_compressed_page(page, cc->inode, + start_idx + i + 1, + dic, i ? &dic->ref : NULL); + dic->cpages[i] = page; + } + + dic->tpages = f2fs_kzalloc(sbi, sizeof(struct page *) * + dic->cluster_size, GFP_NOFS); + if (!dic->tpages) + goto out_free; + + for (i = 0; i < dic->cluster_size; i++) { + if (cc->rpages[i]) + continue; + + dic->tpages[i] = f2fs_grab_page(); + if (!dic->tpages[i]) + goto out_free; + } + + for (i = 0; i < dic->cluster_size; i++) { + if (dic->tpages[i]) + continue; + dic->tpages[i] = cc->rpages[i]; + } + + return dic; + +out_free: + f2fs_free_dic(dic); + return ERR_PTR(-ENOMEM); +} + +void f2fs_free_dic(struct decompress_io_ctx *dic) +{ + int i; + + if (dic->tpages) { + for (i = 0; i < dic->cluster_size; i++) { + if (dic->rpages[i]) + continue; + f2fs_put_page(dic->tpages[i], 1); + } + kfree(dic->tpages); + } + + if (dic->cpages) { + for (i = 0; i < dic->nr_cpages; i++) { + if (!dic->cpages[i]) + continue; + f2fs_put_compressed_page(dic->cpages[i]); + } + kfree(dic->cpages); + } + + kfree(dic->rpages); + kfree(dic); +} + +void f2fs_decompress_end_io(struct page **rpages, + unsigned int cluster_size, bool err, bool verity) +{ + int i; + + for (i = 0; i < cluster_size; i++) { + struct page *rpage = rpages[i]; + + if (!rpage) + continue; + + if (err || PageError(rpage)) { + ClearPageUptodate(rpage); + ClearPageError(rpage); + } else { + if (!verity || fsverity_verify_page(rpage)) + SetPageUptodate(rpage); + else + SetPageError(rpage); + } + unlock_page(rpage); + } +} diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 8ebefd7488ac3fbd0df2428b80f3697b14d8e046..e14ff5e2ea0c57a303d26f165a1594549c8ef011 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -32,6 +32,47 @@ static struct kmem_cache *bio_post_read_ctx_cache; static struct kmem_cache *bio_entry_slab; static mempool_t *bio_post_read_ctx_pool; +static struct bio_set f2fs_bioset; + +#define F2FS_BIO_POOL_SIZE NR_CURSEG_TYPE + +int __init f2fs_init_bioset(void) +{ + if (bioset_init(&f2fs_bioset, F2FS_BIO_POOL_SIZE, + 0, BIOSET_NEED_BVECS)) + return -ENOMEM; + return 0; +} + +void f2fs_destroy_bioset(void) +{ + bioset_exit(&f2fs_bioset); +} + +static inline struct bio *__f2fs_bio_alloc(gfp_t gfp_mask, + unsigned int nr_iovecs) +{ + return bio_alloc_bioset(gfp_mask, nr_iovecs, &f2fs_bioset); +} + +struct bio *f2fs_bio_alloc(struct f2fs_sb_info *sbi, int npages, bool no_fail) +{ + struct bio *bio; + + if (no_fail) { + /* No failure on bio allocation */ + bio = __f2fs_bio_alloc(GFP_NOIO, npages); + if (!bio) + bio = __f2fs_bio_alloc(GFP_NOIO | __GFP_NOFAIL, npages); + return bio; + } + if (time_to_inject(sbi, FAULT_ALLOC_BIO)) { + f2fs_show_injection_info(sbi, FAULT_ALLOC_BIO); + return NULL; + } + + return __f2fs_bio_alloc(GFP_KERNEL, npages); +} static bool __is_cp_guaranteed(struct page *page) { @@ -42,6 +83,9 @@ static bool __is_cp_guaranteed(struct page *page) if (!mapping) return false; + if (f2fs_is_compressed_page(page)) + return false; + inode = mapping->host; sbi = F2FS_I_SB(inode); @@ -74,19 +118,19 @@ static enum count_type __read_io_type(struct page *page) /* postprocessing steps for read bios */ enum bio_post_read_step { - STEP_INITIAL = 0, STEP_DECRYPT, + STEP_DECOMPRESS, STEP_VERITY, }; struct bio_post_read_ctx { struct bio *bio; + struct f2fs_sb_info *sbi; struct work_struct work; - unsigned int cur_step; unsigned int enabled_steps; }; -static void __read_end_io(struct bio *bio) +static void __read_end_io(struct bio *bio, bool compr, bool verity) { struct page *page; struct bio_vec *bv; @@ -95,6 +139,13 @@ static void __read_end_io(struct bio *bio) bio_for_each_segment_all(bv, bio, i) { page = bv->bv_page; +#ifdef CONFIG_F2FS_FS_COMPRESSION + if (compr && f2fs_is_compressed_page(page)) { + f2fs_decompress_pages(bio, page, verity); + continue; + } +#endif + /* PG_error was set if any post_read step failed */ if (bio->bi_status || PageError(page)) { ClearPageUptodate(page); @@ -106,31 +157,107 @@ static void __read_end_io(struct bio *bio) dec_page_count(F2FS_P_SB(page), __read_io_type(page)); unlock_page(page); } - if (bio->bi_private) - mempool_free(bio->bi_private, bio_post_read_ctx_pool); - bio_put(bio); +} + +static void f2fs_release_read_bio(struct bio *bio); +static void __f2fs_read_end_io(struct bio *bio, bool compr, bool verity) +{ + if (!compr) + __read_end_io(bio, false, verity); + f2fs_release_read_bio(bio); +} + +static void f2fs_decompress_bio(struct bio *bio, bool verity) +{ + __read_end_io(bio, true, verity); } static void bio_post_read_processing(struct bio_post_read_ctx *ctx); -static void decrypt_work(struct work_struct *work) +static void f2fs_decrypt_work(struct bio_post_read_ctx *ctx) +{ + fscrypt_decrypt_bio(ctx->bio); +} + +static void f2fs_decompress_work(struct bio_post_read_ctx *ctx) +{ + f2fs_decompress_bio(ctx->bio, ctx->enabled_steps & (1 << STEP_VERITY)); +} + +#ifdef CONFIG_F2FS_FS_COMPRESSION +static void f2fs_verify_pages(struct page **rpages, unsigned int cluster_size) +{ + f2fs_decompress_end_io(rpages, cluster_size, false, true); +} + +static void f2fs_verify_bio(struct bio *bio) +{ + struct page *page = bio_first_page_all(bio); + struct decompress_io_ctx *dic = + (struct decompress_io_ctx *)page_private(page); + + f2fs_verify_pages(dic->rpages, dic->cluster_size); + f2fs_free_dic(dic); +} +#endif + +static void f2fs_verity_work(struct work_struct *work) { struct bio_post_read_ctx *ctx = container_of(work, struct bio_post_read_ctx, work); + struct bio *bio = ctx->bio; +#ifdef CONFIG_F2FS_FS_COMPRESSION + unsigned int enabled_steps = ctx->enabled_steps; +#endif - fscrypt_decrypt_bio(ctx->bio); + /* + * fsverity_verify_bio() may call readpages() again, and while verity + * will be disabled for this, decryption may still be needed, resulting + * in another bio_post_read_ctx being allocated. So to prevent + * deadlocks we need to release the current ctx to the mempool first. + * This assumes that verity is the last post-read step. + */ + mempool_free(ctx, bio_post_read_ctx_pool); + bio->bi_private = NULL; + +#ifdef CONFIG_F2FS_FS_COMPRESSION + /* previous step is decompression */ + if (enabled_steps & (1 << STEP_DECOMPRESS)) { + f2fs_verify_bio(bio); + f2fs_release_read_bio(bio); + return; + } +#endif - bio_post_read_processing(ctx); + fsverity_verify_bio(bio); + __f2fs_read_end_io(bio, false, false); } -static void verity_work(struct work_struct *work) +static void f2fs_post_read_work(struct work_struct *work) { struct bio_post_read_ctx *ctx = container_of(work, struct bio_post_read_ctx, work); - fsverity_verify_bio(ctx->bio); + if (ctx->enabled_steps & (1 << STEP_DECRYPT)) + f2fs_decrypt_work(ctx); + + if (ctx->enabled_steps & (1 << STEP_DECOMPRESS)) + f2fs_decompress_work(ctx); - bio_post_read_processing(ctx); + if (ctx->enabled_steps & (1 << STEP_VERITY)) { + INIT_WORK(&ctx->work, f2fs_verity_work); + fsverity_enqueue_verify_work(&ctx->work); + return; + } + + __f2fs_read_end_io(ctx->bio, + ctx->enabled_steps & (1 << STEP_DECOMPRESS), false); +} + +static void f2fs_enqueue_post_read_work(struct f2fs_sb_info *sbi, + struct work_struct *work) +{ + queue_work(sbi->post_read_wq, work); } static void bio_post_read_processing(struct bio_post_read_ctx *ctx) @@ -140,31 +267,26 @@ static void bio_post_read_processing(struct bio_post_read_ctx *ctx) * verity may require reading metadata pages that need decryption, and * we shouldn't recurse to the same workqueue. */ - switch (++ctx->cur_step) { - case STEP_DECRYPT: - if (ctx->enabled_steps & (1 << STEP_DECRYPT)) { - INIT_WORK(&ctx->work, decrypt_work); - fscrypt_enqueue_decrypt_work(&ctx->work); - return; - } - ctx->cur_step++; - /* fall-through */ - case STEP_VERITY: - if (ctx->enabled_steps & (1 << STEP_VERITY)) { - INIT_WORK(&ctx->work, verity_work); - fsverity_enqueue_verify_work(&ctx->work); - return; - } - ctx->cur_step++; - /* fall-through */ - default: - __read_end_io(ctx->bio); + + if (ctx->enabled_steps & (1 << STEP_DECRYPT) || + ctx->enabled_steps & (1 << STEP_DECOMPRESS)) { + INIT_WORK(&ctx->work, f2fs_post_read_work); + f2fs_enqueue_post_read_work(ctx->sbi, &ctx->work); + return; } + + if (ctx->enabled_steps & (1 << STEP_VERITY)) { + INIT_WORK(&ctx->work, f2fs_verity_work); + fsverity_enqueue_verify_work(&ctx->work); + return; + } + + __f2fs_read_end_io(ctx->bio, false, false); } static bool f2fs_bio_post_read_required(struct bio *bio) { - return bio->bi_private && !bio->bi_status; + return bio->bi_private; } static void f2fs_read_end_io(struct bio *bio) @@ -180,7 +302,6 @@ static void f2fs_read_end_io(struct bio *bio) if (f2fs_bio_post_read_required(bio)) { struct bio_post_read_ctx *ctx = bio->bi_private; - ctx->cur_step = STEP_INITIAL; bio_post_read_processing(ctx); return; } @@ -192,7 +313,7 @@ static void f2fs_read_end_io(struct bio *bio) bio->bi_iter.bi_size); } - __read_end_io(bio); + __f2fs_read_end_io(bio, false, false); } static void f2fs_write_end_io(struct bio *bio) @@ -223,6 +344,13 @@ static void f2fs_write_end_io(struct bio *bio) fscrypt_finalize_bounce_page(&page); +#ifdef CONFIG_F2FS_FS_COMPRESSION + if (f2fs_is_compressed_page(page)) { + f2fs_compress_write_end_io(bio, page); + continue; + } +#endif + if (unlikely(bio->bi_status)) { mapping_set_error(page->mapping, -EIO); if (type == F2FS_WB_CP_DATA) @@ -424,6 +552,12 @@ static void __f2fs_submit_read_bio(struct f2fs_sb_info *sbi, __submit_bio(sbi, bio, type); } +void f2fs_submit_bio(struct f2fs_sb_info *sbi, + struct bio *bio, enum page_type type) +{ + __submit_bio(sbi, bio, type); +} + static void __submit_merged_bio(struct f2fs_bio_info *io) { struct f2fs_io_info *fio = &io->fio; @@ -446,7 +580,6 @@ static bool __has_merged_page(struct bio *bio, struct inode *inode, struct page *page, nid_t ino) { struct bio_vec *bvec; - struct page *target; int i; if (!bio) @@ -456,10 +589,18 @@ static bool __has_merged_page(struct bio *bio, struct inode *inode, return true; bio_for_each_segment_all(bvec, bio, i) { + struct page *target = bvec->bv_page; - target = bvec->bv_page; - if (fscrypt_is_bounce_page(target)) + if (fscrypt_is_bounce_page(target)) { target = fscrypt_pagecache_page(target); + if (IS_ERR(target)) + continue; + } + if (f2fs_is_compressed_page(target)) { + target = f2fs_compress_control_page(target); + if (IS_ERR(target)) + continue; + } if (inode && inode == target->mapping->host) return true; @@ -660,7 +801,8 @@ static int add_ipu_page(struct f2fs_sb_info *sbi, struct bio **bio, found = true; - if (bio_add_page(*bio, page, PAGE_SIZE, 0) == PAGE_SIZE) { + if (bio_add_page(*bio, page, PAGE_SIZE, 0) == + PAGE_SIZE) { ret = 0; break; } @@ -806,7 +948,12 @@ void f2fs_submit_page_write(struct f2fs_io_info *fio) verify_fio_blkaddr(fio); - bio_page = fio->encrypted_page ? fio->encrypted_page : fio->page; + if (fio->encrypted_page) + bio_page = fio->encrypted_page; + else if (fio->compressed_page) + bio_page = fio->compressed_page; + else + bio_page = fio->page; /* set submitted = true as a return value */ fio->submitted = true; @@ -884,17 +1031,16 @@ static struct bio *f2fs_grab_read_bio(struct inode *inode, block_t blkaddr, if (fscrypt_inode_uses_fs_layer_crypto(inode)) post_read_steps |= 1 << STEP_DECRYPT; - + if (f2fs_compressed_file(inode)) + post_read_steps |= 1 << STEP_DECOMPRESS; if (f2fs_need_verity(inode, first_idx)) post_read_steps |= 1 << STEP_VERITY; if (post_read_steps) { + /* Due to the mempool, this never fails. */ ctx = mempool_alloc(bio_post_read_ctx_pool, GFP_NOFS); - if (!ctx) { - bio_put(bio); - return ERR_PTR(-ENOMEM); - } ctx->bio = bio; + ctx->sbi = sbi; ctx->enabled_steps = post_read_steps; bio->bi_private = ctx; } @@ -902,6 +1048,13 @@ static struct bio *f2fs_grab_read_bio(struct inode *inode, block_t blkaddr, return bio; } +static void f2fs_release_read_bio(struct bio *bio) +{ + if (bio->bi_private) + mempool_free(bio->bi_private, bio_post_read_ctx_pool); + bio_put(bio); +} + /* This can handle encryption stuffs */ static int f2fs_submit_page_read(struct inode *inode, struct page *page, block_t blkaddr) @@ -1267,19 +1420,6 @@ int f2fs_preallocate_blocks(struct kiocb *iocb, struct iov_iter *from) int err = 0; bool direct_io = iocb->ki_flags & IOCB_DIRECT; - /* convert inline data for Direct I/O*/ - if (direct_io) { - err = f2fs_convert_inline_inode(inode); - if (err) - return err; - } - - if (direct_io && allow_outplace_dio(inode, iocb, from)) - return 0; - - if (is_inode_flag_set(inode, FI_NO_PREALLOC)) - return 0; - map.m_lblk = F2FS_BLK_ALIGN(iocb->ki_pos); map.m_len = F2FS_BYTES_TO_BLK(iocb->ki_pos + iov_iter_count(from)); if (map.m_len > map.m_lblk) @@ -1959,6 +2099,145 @@ static int f2fs_read_single_page(struct inode *inode, struct page *page, return ret; } +#ifdef CONFIG_F2FS_FS_COMPRESSION +int f2fs_read_multi_pages(struct compress_ctx *cc, struct bio **bio_ret, + unsigned nr_pages, sector_t *last_block_in_bio, + bool is_readahead) +{ + struct dnode_of_data dn; + struct inode *inode = cc->inode; + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct bio *bio = *bio_ret; + unsigned int start_idx = cc->cluster_idx << cc->log_cluster_size; + sector_t last_block_in_file; + const unsigned blkbits = inode->i_blkbits; + const unsigned blocksize = 1 << blkbits; + struct decompress_io_ctx *dic = NULL; + int i; + int ret = 0; + + f2fs_bug_on(sbi, f2fs_cluster_is_empty(cc)); + + last_block_in_file = (i_size_read(inode) + blocksize - 1) >> blkbits; + + /* get rid of pages beyond EOF */ + for (i = 0; i < cc->cluster_size; i++) { + struct page *page = cc->rpages[i]; + + if (!page) + continue; + if ((sector_t)page->index >= last_block_in_file) { + zero_user_segment(page, 0, PAGE_SIZE); + if (!PageUptodate(page)) + SetPageUptodate(page); + } else if (!PageUptodate(page)) { + continue; + } + unlock_page(page); + cc->rpages[i] = NULL; + cc->nr_rpages--; + } + + /* we are done since all pages are beyond EOF */ + if (f2fs_cluster_is_empty(cc)) + goto out; + + set_new_dnode(&dn, inode, NULL, NULL, 0); + ret = f2fs_get_dnode_of_data(&dn, start_idx, LOOKUP_NODE); + if (ret) + goto out; + + /* cluster was overwritten as normal cluster */ + if (dn.data_blkaddr != COMPRESS_ADDR) + goto out; + + for (i = 1; i < cc->cluster_size; i++) { + block_t blkaddr; + + blkaddr = datablock_addr(dn.inode, dn.node_page, + dn.ofs_in_node + i); + + if (!__is_valid_data_blkaddr(blkaddr)) + break; + + if (!f2fs_is_valid_blkaddr(sbi, blkaddr, DATA_GENERIC)) { + ret = -EFAULT; + goto out_put_dnode; + } + cc->nr_cpages++; + } + + /* nothing to decompress */ + if (cc->nr_cpages == 0) { + ret = 0; + goto out_put_dnode; + } + + dic = f2fs_alloc_dic(cc); + if (IS_ERR(dic)) { + ret = PTR_ERR(dic); + goto out_put_dnode; + } + + for (i = 0; i < dic->nr_cpages; i++) { + struct page *page = dic->cpages[i]; + block_t blkaddr; + + blkaddr = datablock_addr(dn.inode, dn.node_page, + dn.ofs_in_node + i + 1); + + if (bio && (!page_is_mergeable(sbi, bio, + *last_block_in_bio, blkaddr) || + !f2fs_crypt_mergeable_bio(bio, inode, page->index, NULL))) { +submit_and_realloc: + __submit_bio(sbi, bio, DATA); + bio = NULL; + } + + if (!bio) { + bio = f2fs_grab_read_bio(inode, blkaddr, nr_pages, + is_readahead ? REQ_RAHEAD : 0, + page->index); + if (IS_ERR(bio)) { + ret = PTR_ERR(bio); + bio = NULL; + dic->failed = true; + if (refcount_sub_and_test(dic->nr_cpages - i, + &dic->ref)) + f2fs_decompress_end_io(dic->rpages, + cc->cluster_size, true, + false); + f2fs_free_dic(dic); + f2fs_put_dnode(&dn); + *bio_ret = bio; + return ret; + } + } + + f2fs_wait_on_block_writeback(inode, blkaddr); + + if (bio_add_page(bio, page, blocksize, 0) < blocksize) + goto submit_and_realloc; + + inc_page_count(sbi, F2FS_RD_DATA); + ClearPageError(page); + *last_block_in_bio = blkaddr; + } + + f2fs_put_dnode(&dn); + + *bio_ret = bio; + return 0; + +out_put_dnode: + f2fs_put_dnode(&dn); +out: + f2fs_decompress_end_io(cc->rpages, cc->cluster_size, true, false); + *bio_ret = bio; + return ret; +} +#endif + /* * This function was originally taken from fs/mpage.c, and customized for f2fs. * Major change was from block_size == page_size in f2fs by default. @@ -1968,7 +2247,7 @@ static int f2fs_read_single_page(struct inode *inode, struct page *page, * use ->readpage() or do the necessary surgery to decouple ->readpages() * from read-ahead. */ -static int f2fs_mpage_readpages(struct address_space *mapping, +int f2fs_mpage_readpages(struct address_space *mapping, struct list_head *pages, struct page *page, unsigned nr_pages, bool is_readahead) { @@ -1976,6 +2255,19 @@ static int f2fs_mpage_readpages(struct address_space *mapping, sector_t last_block_in_bio = 0; struct inode *inode = mapping->host; struct f2fs_map_blocks map; +#ifdef CONFIG_F2FS_FS_COMPRESSION + struct compress_ctx cc = { + .inode = inode, + .log_cluster_size = F2FS_I(inode)->i_log_cluster_size, + .cluster_size = F2FS_I(inode)->i_cluster_size, + .cluster_idx = NULL_CLUSTER, + .rpages = NULL, + .cpages = NULL, + .nr_rpages = 0, + .nr_cpages = 0, + }; +#endif + unsigned max_nr_pages = nr_pages; int ret = 0; map.m_pblk = 0; @@ -1999,9 +2291,41 @@ static int f2fs_mpage_readpages(struct address_space *mapping, goto next_page; } - ret = f2fs_read_single_page(inode, page, nr_pages, &map, &bio, - &last_block_in_bio, is_readahead); +#ifdef CONFIG_F2FS_FS_COMPRESSION + if (f2fs_compressed_file(inode)) { + /* there are remained comressed pages, submit them */ + if (!f2fs_cluster_can_merge_page(&cc, page->index)) { + ret = f2fs_read_multi_pages(&cc, &bio, + max_nr_pages, + &last_block_in_bio, + is_readahead); + f2fs_destroy_compress_ctx(&cc); + if (ret) + goto set_error_page; + } + ret = f2fs_is_compressed_cluster(inode, page->index); + if (ret < 0) + goto set_error_page; + else if (!ret) + goto read_single_page; + + ret = f2fs_init_compress_ctx(&cc); + if (ret) + goto set_error_page; + + f2fs_compress_ctx_add_page(&cc, page); + + goto next_page; + } +read_single_page: +#endif + + ret = f2fs_read_single_page(inode, page, max_nr_pages, &map, + &bio, &last_block_in_bio, is_readahead); if (ret) { +#ifdef CONFIG_F2FS_FS_COMPRESSION +set_error_page: +#endif SetPageError(page); zero_user_segment(page, 0, PAGE_SIZE); unlock_page(page); @@ -2010,6 +2334,19 @@ static int f2fs_mpage_readpages(struct address_space *mapping, next_page: if (pages) put_page(page); + +#ifdef CONFIG_F2FS_FS_COMPRESSION + if (f2fs_compressed_file(inode)) { + /* last page */ + if (nr_pages == 1 && !f2fs_cluster_is_empty(&cc)) { + ret = f2fs_read_multi_pages(&cc, &bio, + max_nr_pages, + &last_block_in_bio, + is_readahead); + f2fs_destroy_compress_ctx(&cc); + } + } +#endif } BUG_ON(pages && !list_empty(pages)); if (bio) @@ -2024,6 +2361,11 @@ static int f2fs_read_data_page(struct file *file, struct page *page) trace_f2fs_readpage(page, DATA); + if (!f2fs_is_compress_backend_ready(inode)) { + unlock_page(page); + return -EOPNOTSUPP; + } + /* If the file has inline data, try to read it directly */ if (f2fs_has_inline_data(inode)) ret = f2fs_read_inline_data(inode, page); @@ -2042,6 +2384,9 @@ static int f2fs_read_data_pages(struct file *file, trace_f2fs_readpages(inode, page, nr_pages); + if (!f2fs_is_compress_backend_ready(inode)) + return 0; + /* If the file has inline data, skip readpages */ if (f2fs_has_inline_data(inode)) return 0; @@ -2049,15 +2394,17 @@ static int f2fs_read_data_pages(struct file *file, return f2fs_mpage_readpages(mapping, pages, NULL, nr_pages, true); } -static int encrypt_one_page(struct f2fs_io_info *fio) +int f2fs_encrypt_one_page(struct f2fs_io_info *fio) { struct inode *inode = fio->page->mapping->host; - struct page *mpage; + struct page *mpage, *page; gfp_t gfp_flags = GFP_NOFS; if (!f2fs_encrypted_file(inode)) return 0; + page = fio->compressed_page ? fio->compressed_page : fio->page; + /* wait for GCed page writeback via META_MAPPING */ f2fs_wait_on_block_writeback(inode, fio->old_blkaddr); @@ -2065,10 +2412,8 @@ static int encrypt_one_page(struct f2fs_io_info *fio) return 0; retry_encrypt: - - fio->encrypted_page = fscrypt_encrypt_pagecache_blocks(fio->page, - PAGE_SIZE, 0, - gfp_flags); + fio->encrypted_page = fscrypt_encrypt_pagecache_blocks(page, + PAGE_SIZE, 0, gfp_flags); if (IS_ERR(fio->encrypted_page)) { /* flush pending IOs and wait for a while in the ENOMEM case */ if (PTR_ERR(fio->encrypted_page) == -ENOMEM) { @@ -2228,7 +2573,7 @@ int f2fs_do_write_data_page(struct f2fs_io_info *fio) if (ipu_force || (__is_valid_data_blkaddr(fio->old_blkaddr) && need_inplace_update(fio))) { - err = encrypt_one_page(fio); + err = f2fs_encrypt_one_page(fio); if (err) goto out_writepage; @@ -2264,13 +2609,16 @@ int f2fs_do_write_data_page(struct f2fs_io_info *fio) fio->version = ni.version; - err = encrypt_one_page(fio); + err = f2fs_encrypt_one_page(fio); if (err) goto out_writepage; set_page_writeback(page); ClearPageError(page); + if (fio->compr_blocks && fio->old_blkaddr == COMPRESS_ADDR) + f2fs_i_compr_blocks_update(inode, fio->compr_blocks - 1, false); + /* LFS mode write path */ f2fs_outplace_write_data(&dn, fio); trace_f2fs_do_write_data_page(page, OPU); @@ -2285,16 +2633,17 @@ int f2fs_do_write_data_page(struct f2fs_io_info *fio) return err; } -static int __write_data_page(struct page *page, bool *submitted, +int f2fs_write_single_data_page(struct page *page, int *submitted, struct bio **bio, sector_t *last_block, struct writeback_control *wbc, - enum iostat_type io_type) + enum iostat_type io_type, + int compr_blocks) { struct inode *inode = page->mapping->host; struct f2fs_sb_info *sbi = F2FS_I_SB(inode); loff_t i_size = i_size_read(inode); - const pgoff_t end_index = ((unsigned long long) i_size) + const pgoff_t end_index = ((unsigned long long)i_size) >> PAGE_SHIFT; loff_t psize = (loff_t)(page->index + 1) << PAGE_SHIFT; unsigned offset = 0; @@ -2310,6 +2659,7 @@ static int __write_data_page(struct page *page, bool *submitted, .page = page, .encrypted_page = NULL, .submitted = false, + .compr_blocks = compr_blocks, .need_lock = LOCK_RETRY, .io_type = io_type, .io_wbc = wbc, @@ -2334,7 +2684,9 @@ static int __write_data_page(struct page *page, bool *submitted, if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) goto redirty_out; - if (page->index < end_index || f2fs_verity_in_progress(inode)) + if (page->index < end_index || + f2fs_verity_in_progress(inode) || + compr_blocks) goto write; /* @@ -2410,7 +2762,6 @@ static int __write_data_page(struct page *page, bool *submitted, f2fs_remove_dirty_inode(inode); submitted = NULL; } - unlock_page(page); if (!S_ISDIR(inode->i_mode) && !IS_NOQUOTA(inode) && !F2FS_I(inode)->cp_task) @@ -2423,7 +2774,7 @@ static int __write_data_page(struct page *page, bool *submitted, } if (submitted) - *submitted = fio.submitted; + *submitted = fio.submitted ? 1 : 0; return 0; @@ -2444,7 +2795,23 @@ static int __write_data_page(struct page *page, bool *submitted, static int f2fs_write_data_page(struct page *page, struct writeback_control *wbc) { - return __write_data_page(page, NULL, NULL, NULL, wbc, FS_DATA_IO); +#ifdef CONFIG_F2FS_FS_COMPRESSION + struct inode *inode = page->mapping->host; + + if (unlikely(f2fs_cp_error(F2FS_I_SB(inode)))) + goto out; + + if (f2fs_compressed_file(inode)) { + if (f2fs_is_compressed_cluster(inode, page->index)) { + redirty_page_for_writepage(wbc, page); + return AOP_WRITEPAGE_ACTIVATE; + } + } +out: +#endif + + return f2fs_write_single_data_page(page, NULL, NULL, NULL, + wbc, FS_DATA_IO, 0); } /* @@ -2457,11 +2824,27 @@ static int f2fs_write_cache_pages(struct address_space *mapping, enum iostat_type io_type) { int ret = 0; - int done = 0; + int done = 0, retry = 0; struct pagevec pvec; struct f2fs_sb_info *sbi = F2FS_M_SB(mapping); struct bio *bio = NULL; sector_t last_block; +#ifdef CONFIG_F2FS_FS_COMPRESSION + struct inode *inode = mapping->host; + struct compress_ctx cc = { + .inode = inode, + .log_cluster_size = F2FS_I(inode)->i_log_cluster_size, + .cluster_size = F2FS_I(inode)->i_cluster_size, + .cluster_idx = NULL_CLUSTER, + .rpages = NULL, + .nr_rpages = 0, + .cpages = NULL, + .rbuf = NULL, + .cbuf = NULL, + .rlen = PAGE_SIZE * F2FS_I(inode)->i_cluster_size, + .private = NULL, + }; +#endif int nr_pages; pgoff_t uninitialized_var(writeback_index); pgoff_t index; @@ -2471,6 +2854,8 @@ static int f2fs_write_cache_pages(struct address_space *mapping, int range_whole = 0; int tag; int nwritten = 0; + int submitted = 0; + int i; pagevec_init(&pvec); @@ -2500,12 +2885,11 @@ static int f2fs_write_cache_pages(struct address_space *mapping, else tag = PAGECACHE_TAG_DIRTY; retry: + retry = 0; if (wbc->sync_mode == WB_SYNC_ALL || wbc->tagged_writepages) tag_pages_for_writeback(mapping, index, end); done_index = index; - while (!done && (index <= end)) { - int i; - + while (!done && !retry && (index <= end)) { nr_pages = pagevec_lookup_range_tag(&pvec, mapping, &index, end, tag); if (nr_pages == 0) @@ -2513,15 +2897,62 @@ static int f2fs_write_cache_pages(struct address_space *mapping, for (i = 0; i < nr_pages; i++) { struct page *page = pvec.pages[i]; - bool submitted = false; + bool need_readd; +readd: + need_readd = false; +#ifdef CONFIG_F2FS_FS_COMPRESSION + if (f2fs_compressed_file(inode)) { + ret = f2fs_init_compress_ctx(&cc); + if (ret) { + done = 1; + break; + } + + if (!f2fs_cluster_can_merge_page(&cc, + page->index)) { + ret = f2fs_write_multi_pages(&cc, + &submitted, wbc, io_type); + if (!ret) + need_readd = true; + goto result; + } + if (unlikely(f2fs_cp_error(sbi))) + goto lock_page; + + if (f2fs_cluster_is_empty(&cc)) { + void *fsdata = NULL; + struct page *pagep; + int ret2; + + ret2 = f2fs_prepare_compress_overwrite( + inode, &pagep, + page->index, &fsdata); + if (ret2 < 0) { + ret = ret2; + done = 1; + break; + } else if (ret2 && + !f2fs_compress_write_end(inode, + fsdata, page->index, + 1)) { + retry = 1; + break; + } + } else { + goto lock_page; + } + } +#endif /* give a priority to WB_SYNC threads */ if (atomic_read(&sbi->wb_sync_req[DATA]) && wbc->sync_mode == WB_SYNC_NONE) { done = 1; break; } - +#ifdef CONFIG_F2FS_FS_COMPRESSION +lock_page: +#endif done_index = page->index; retry_write: lock_page(page); @@ -2548,45 +2979,71 @@ static int f2fs_write_cache_pages(struct address_space *mapping, if (!clear_page_dirty_for_io(page)) goto continue_unlock; - ret = __write_data_page(page, &submitted, &bio, - &last_block, wbc, io_type); +#ifdef CONFIG_F2FS_FS_COMPRESSION + if (f2fs_compressed_file(inode)) { + get_page(page); + f2fs_compress_ctx_add_page(&cc, page); + continue; + } +#endif + ret = f2fs_write_single_data_page(page, &submitted, + &bio, &last_block, wbc, io_type, 0); + if (ret == AOP_WRITEPAGE_ACTIVATE) + unlock_page(page); +#ifdef CONFIG_F2FS_FS_COMPRESSION +result: +#endif + nwritten += submitted; + wbc->nr_to_write -= submitted; + if (unlikely(ret)) { /* * keep nr_to_write, since vfs uses this to * get # of written pages. */ if (ret == AOP_WRITEPAGE_ACTIVATE) { - unlock_page(page); ret = 0; - continue; + goto next; } else if (ret == -EAGAIN) { ret = 0; if (wbc->sync_mode == WB_SYNC_ALL) { cond_resched(); congestion_wait(BLK_RW_ASYNC, - HZ/50); + HZ/50); goto retry_write; } - continue; + goto next; } done_index = page->index + 1; done = 1; break; - } else if (submitted) { - nwritten++; } - if (--wbc->nr_to_write <= 0 && + if (wbc->nr_to_write <= 0 && wbc->sync_mode == WB_SYNC_NONE) { done = 1; break; } +next: + if (need_readd) + goto readd; } pagevec_release(&pvec); cond_resched(); } - - if (!cycled && !done) { +#ifdef CONFIG_F2FS_FS_COMPRESSION + /* flush remained pages in compress cluster */ + if (f2fs_compressed_file(inode) && !f2fs_cluster_is_empty(&cc)) { + ret = f2fs_write_multi_pages(&cc, &submitted, wbc, io_type); + nwritten += submitted; + wbc->nr_to_write -= submitted; + if (ret) { + done = 1; + retry = 0; + } + } +#endif + if ((!cycled && !done) || retry) { cycled = 1; index = 0; end = writeback_index - 1; @@ -2610,6 +3067,8 @@ static inline bool __should_serialize_io(struct inode *inode, { if (!S_ISREG(inode->i_mode)) return false; + if (f2fs_compressed_file(inode)) + return true; if (IS_NOQUOTA(inode)) return false; /* to avoid deadlock in path of data flush */ @@ -2705,14 +3164,16 @@ static void f2fs_write_failed(struct address_space *mapping, loff_t to) struct inode *inode = mapping->host; loff_t i_size = i_size_read(inode); + if (IS_NOQUOTA(inode)) + return; + /* In the fs-verity case, f2fs_end_enable_verity() does the truncate */ if (to > i_size && !f2fs_verity_in_progress(inode)) { down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); down_write(&F2FS_I(inode)->i_mmap_sem); truncate_pagecache(inode, i_size); - if (!IS_NOQUOTA(inode)) - f2fs_truncate_blocks(inode, i_size, true); + f2fs_truncate_blocks(inode, i_size, true); up_write(&F2FS_I(inode)->i_mmap_sem); up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); @@ -2752,6 +3213,7 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi, __do_map_lock(sbi, flag, true); locked = true; } + restart: /* check inline_data */ ipage = f2fs_get_node_page(sbi, inode->i_ino); @@ -2852,6 +3314,24 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping, if (err) goto fail; } + +#ifdef CONFIG_F2FS_FS_COMPRESSION + if (f2fs_compressed_file(inode)) { + int ret; + + *fsdata = NULL; + + ret = f2fs_prepare_compress_overwrite(inode, pagep, + index, fsdata); + if (ret < 0) { + err = ret; + goto fail; + } else if (ret) { + return 0; + } + } +#endif + repeat: /* * Do not use grab_cache_page_write_begin() to avoid deadlock due to @@ -2864,6 +3344,8 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping, goto fail; } + /* TODO: cluster can be compressed due to race with .writepage */ + *pagep = page; err = prepare_write_begin(sbi, page, pos, len, @@ -2948,6 +3430,16 @@ static int f2fs_write_end(struct file *file, else SetPageUptodate(page); } + +#ifdef CONFIG_F2FS_FS_COMPRESSION + /* overwrite compressed file */ + if (f2fs_compressed_file(inode) && fsdata) { + f2fs_compress_write_end(inode, fsdata, page->index, copied); + f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); + return copied; + } +#endif + if (!copied) goto unlock_out; @@ -3278,7 +3770,8 @@ int f2fs_migrate_page(struct address_space *mapping, #ifdef CONFIG_SWAP /* Copied from generic_swapfile_activate() to check any holes */ -static int check_swap_activate(struct file *swap_file, unsigned int max) +static int check_swap_activate(struct swap_info_struct *sis, + struct file *swap_file, sector_t *span) { struct address_space *mapping = swap_file->f_mapping; struct inode *inode = mapping->host; @@ -3289,6 +3782,8 @@ static int check_swap_activate(struct file *swap_file, unsigned int max) sector_t last_block; sector_t lowest_block = -1; sector_t highest_block = 0; + int nr_extents = 0; + int ret; blkbits = inode->i_blkbits; blocks_per_page = PAGE_SIZE >> blkbits; @@ -3300,7 +3795,8 @@ static int check_swap_activate(struct file *swap_file, unsigned int max) probe_block = 0; page_no = 0; last_block = i_size_read(inode) >> blkbits; - while ((probe_block + blocks_per_page) <= last_block && page_no < max) { + while ((probe_block + blocks_per_page) <= last_block && + page_no < sis->max) { unsigned block_in_page; sector_t first_block; @@ -3340,13 +3836,27 @@ static int check_swap_activate(struct file *swap_file, unsigned int max) highest_block = first_block; } + /* + * We found a PAGE_SIZE-length, PAGE_SIZE-aligned run of blocks + */ + ret = add_swap_extent(sis, page_no, 1, first_block); + if (ret < 0) + goto out; + nr_extents += ret; page_no++; probe_block += blocks_per_page; reprobe: continue; } - return 0; - + ret = nr_extents; + *span = 1 + highest_block - lowest_block; + if (page_no == 0) + page_no = 1; /* force Empty message */ + sis->max = page_no; + sis->pages = page_no - 1; + sis->highest_bit = page_no - 1; +out: + return ret; bad_bmap: pr_err("swapon: swapfile has holes\n"); return -EINVAL; @@ -3368,14 +3878,17 @@ static int f2fs_swap_activate(struct swap_info_struct *sis, struct file *file, if (ret) return ret; - ret = check_swap_activate(file, sis->max); - if (ret) + if (f2fs_disable_compressed_file(inode)) + return -EINVAL; + + ret = check_swap_activate(sis, file, span); + if (ret < 0) return ret; set_inode_flag(inode, FI_PIN_FILE); f2fs_precache_extents(inode); f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); - return 0; + return ret; } static void f2fs_swap_deactivate(struct file *file) @@ -3452,6 +3965,27 @@ void f2fs_destroy_post_read_processing(void) kmem_cache_destroy(bio_post_read_ctx_cache); } +int f2fs_init_post_read_wq(struct f2fs_sb_info *sbi) +{ + if (!f2fs_sb_has_encrypt(sbi) && + !f2fs_sb_has_verity(sbi) && + !f2fs_sb_has_compression(sbi)) + return 0; + + sbi->post_read_wq = alloc_workqueue("f2fs_post_read_wq", + WQ_UNBOUND | WQ_HIGHPRI, + num_online_cpus()); + if (!sbi->post_read_wq) + return -ENOMEM; + return 0; +} + +void f2fs_destroy_post_read_wq(struct f2fs_sb_info *sbi) +{ + if (sbi->post_read_wq) + destroy_workqueue(sbi->post_read_wq); +} + int __init f2fs_init_bio_entry_cache(void) { bio_entry_slab = f2fs_kmem_cache_create("bio_entry_slab", @@ -3461,7 +3995,7 @@ int __init f2fs_init_bio_entry_cache(void) return 0; } -void __exit f2fs_destroy_bio_entry_cache(void) +void f2fs_destroy_bio_entry_cache(void) { kmem_cache_destroy(bio_entry_slab); } diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 9b0bedd82581b263568695422d7410e679534ace..6b89eae5e4ca21a794fde1f59d436c3797d65519 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -21,9 +21,45 @@ #include "gc.h" static LIST_HEAD(f2fs_stat_list); -static struct dentry *f2fs_debugfs_root; static DEFINE_MUTEX(f2fs_stat_mutex); +#ifdef CONFIG_DEBUG_FS +static struct dentry *f2fs_debugfs_root; +#endif + +/* + * This function calculates BDF of every segments + */ +void f2fs_update_sit_info(struct f2fs_sb_info *sbi) +{ + struct f2fs_stat_info *si = F2FS_STAT(sbi); + unsigned long long blks_per_sec, hblks_per_sec, total_vblocks; + unsigned long long bimodal, dist; + unsigned int segno, vblocks; + int ndirty = 0; + + bimodal = 0; + total_vblocks = 0; + blks_per_sec = BLKS_PER_SEC(sbi); + hblks_per_sec = blks_per_sec / 2; + for (segno = 0; segno < MAIN_SEGS(sbi); segno += sbi->segs_per_sec) { + vblocks = get_valid_blocks(sbi, segno, true); + dist = abs(vblocks - hblks_per_sec); + bimodal += dist * dist; + + if (vblocks > 0 && vblocks < blks_per_sec) { + total_vblocks += vblocks; + ndirty++; + } + } + dist = div_u64(MAIN_SECS(sbi) * hblks_per_sec * hblks_per_sec, 100); + si->bimodal = div64_u64(bimodal, dist); + if (si->dirty_count) + si->avg_vblocks = div_u64(total_vblocks, ndirty); + else + si->avg_vblocks = 0; +} +#ifdef CONFIG_DEBUG_FS static void update_general_status(struct f2fs_sb_info *sbi) { struct f2fs_stat_info *si = F2FS_STAT(sbi); @@ -56,7 +92,7 @@ static void update_general_status(struct f2fs_sb_info *sbi) si->nquota_files = sbi->nquota_files; si->ndirty_all = sbi->ndirty_inode[DIRTY_META]; si->inmem_pages = get_pages(sbi, F2FS_INMEM_PAGES); - si->aw_cnt = atomic_read(&sbi->aw_cnt); + si->aw_cnt = sbi->atomic_files; si->vw_cnt = atomic_read(&sbi->vw_cnt); si->max_aw_cnt = atomic_read(&sbi->max_aw_cnt); si->max_vw_cnt = atomic_read(&sbi->max_vw_cnt); @@ -94,6 +130,8 @@ static void update_general_status(struct f2fs_sb_info *sbi) si->inline_xattr = atomic_read(&sbi->inline_xattr); si->inline_inode = atomic_read(&sbi->inline_inode); si->inline_dir = atomic_read(&sbi->inline_dir); + si->compr_inode = atomic_read(&sbi->compr_inode); + si->compr_blocks = atomic_read(&sbi->compr_blocks); si->append = sbi->im[APPEND_INO].ino_num; si->update = sbi->im[UPDATE_INO].ino_num; si->orphans = sbi->im[ORPHAN_INO].ino_num; @@ -114,7 +152,6 @@ static void update_general_status(struct f2fs_sb_info *sbi) si->free_nids = NM_I(sbi)->nid_cnt[FREE_NID]; si->avail_nids = NM_I(sbi)->available_nids; si->alloc_nids = NM_I(sbi)->nid_cnt[PREALLOC_NID]; - si->bg_gc = sbi->bg_gc; si->io_skip_bggc = sbi->io_skip_bggc; si->other_skip_bggc = sbi->other_skip_bggc; si->skipped_atomic_files[BG_GC] = sbi->skipped_atomic_files[BG_GC]; @@ -145,39 +182,6 @@ static void update_general_status(struct f2fs_sb_info *sbi) si->inplace_count = atomic_read(&sbi->inplace_count); } -/* - * This function calculates BDF of every segments - */ -static void update_sit_info(struct f2fs_sb_info *sbi) -{ - struct f2fs_stat_info *si = F2FS_STAT(sbi); - unsigned long long blks_per_sec, hblks_per_sec, total_vblocks; - unsigned long long bimodal, dist; - unsigned int segno, vblocks; - int ndirty = 0; - - bimodal = 0; - total_vblocks = 0; - blks_per_sec = BLKS_PER_SEC(sbi); - hblks_per_sec = blks_per_sec / 2; - for (segno = 0; segno < MAIN_SEGS(sbi); segno += sbi->segs_per_sec) { - vblocks = get_valid_blocks(sbi, segno, true); - dist = abs(vblocks - hblks_per_sec); - bimodal += dist * dist; - - if (vblocks > 0 && vblocks < blks_per_sec) { - total_vblocks += vblocks; - ndirty++; - } - } - dist = div_u64(MAIN_SECS(sbi) * hblks_per_sec * hblks_per_sec, 100); - si->bimodal = div64_u64(bimodal, dist); - if (si->dirty_count) - si->avg_vblocks = div_u64(total_vblocks, ndirty); - else - si->avg_vblocks = 0; -} - /* * This function calculates memory footprint. */ @@ -315,6 +319,8 @@ static int stat_show(struct seq_file *s, void *v) si->inline_inode); seq_printf(s, " - Inline_dentry Inode: %u\n", si->inline_dir); + seq_printf(s, " - Compressed Inode: %u, Blocks: %u\n", + si->compr_inode, si->compr_blocks); seq_printf(s, " - Orphan/Append/Update Inode: %u, %u, %u\n", si->orphans, si->append, si->update); seq_printf(s, "\nMain area: %d segs, %d secs %d zones\n", @@ -441,7 +447,7 @@ static int stat_show(struct seq_file *s, void *v) si->block_count[LFS], si->segment_count[LFS]); /* segment usage info */ - update_sit_info(si->sbi); + f2fs_update_sit_info(si->sbi); seq_printf(s, "\nBDF: %u, avg. vblocks: %u\n", si->bimodal, si->avg_vblocks); @@ -461,6 +467,7 @@ static int stat_show(struct seq_file *s, void *v) } DEFINE_SHOW_ATTRIBUTE(stat); +#endif int f2fs_build_stats(struct f2fs_sb_info *sbi) { @@ -491,11 +498,12 @@ int f2fs_build_stats(struct f2fs_sb_info *sbi) atomic_set(&sbi->inline_xattr, 0); atomic_set(&sbi->inline_inode, 0); atomic_set(&sbi->inline_dir, 0); + atomic_set(&sbi->compr_inode, 0); + atomic_set(&sbi->compr_blocks, 0); atomic_set(&sbi->inplace_count, 0); for (i = META_CP; i < META_MAX; i++) atomic_set(&sbi->meta_count[i], 0); - atomic_set(&sbi->aw_cnt, 0); atomic_set(&sbi->vw_cnt, 0); atomic_set(&sbi->max_aw_cnt, 0); atomic_set(&sbi->max_vw_cnt, 0); @@ -520,14 +528,18 @@ void f2fs_destroy_stats(struct f2fs_sb_info *sbi) void __init f2fs_create_root_stats(void) { +#ifdef CONFIG_DEBUG_FS f2fs_debugfs_root = debugfs_create_dir("f2fs", NULL); debugfs_create_file("status", S_IRUGO, f2fs_debugfs_root, NULL, &stat_fops); +#endif } void f2fs_destroy_root_stats(void) { +#ifdef CONFIG_DEBUG_FS debugfs_remove_recursive(f2fs_debugfs_root); f2fs_debugfs_root = NULL; +#endif } diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index dbb9f5a4d7a6e98d1efcefc8f67dde64842dd8eb..8f83bfa8ffc6d1c471c2fa2100b5b5fa536dbb97 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -108,34 +108,52 @@ static struct f2fs_dir_entry *find_in_block(struct inode *dir, * Test whether a case-insensitive directory entry matches the filename * being searched for. * + * Only called for encrypted names if the key is available. + * * Returns: 0 if the directory entry matches, more than 0 if it * doesn't match or less than zero on error. */ -int f2fs_ci_compare(const struct inode *parent, const struct qstr *name, - const struct qstr *entry, bool quick) +static int f2fs_ci_compare(const struct inode *parent, const struct qstr *name, + u8 *de_name, size_t de_name_len, bool quick) { - const struct f2fs_sb_info *sbi = F2FS_SB(parent->i_sb); - const struct unicode_map *um = sbi->s_encoding; + const struct super_block *sb = parent->i_sb; + const struct unicode_map *um = sb->s_encoding; + struct fscrypt_str decrypted_name = FSTR_INIT(NULL, de_name_len); + struct qstr entry = QSTR_INIT(de_name, de_name_len); int ret; + if (IS_ENCRYPTED(parent)) { + const struct fscrypt_str encrypted_name = + FSTR_INIT(de_name, de_name_len); + + decrypted_name.name = kmalloc(de_name_len, GFP_KERNEL); + if (!decrypted_name.name) + return -ENOMEM; + ret = fscrypt_fname_disk_to_usr(parent, 0, 0, &encrypted_name, + &decrypted_name); + if (ret < 0) + goto out; + entry.name = decrypted_name.name; + entry.len = decrypted_name.len; + } + if (quick) - ret = utf8_strncasecmp_folded(um, name, entry); + ret = utf8_strncasecmp_folded(um, name, &entry); else - ret = utf8_strncasecmp(um, name, entry); - + ret = utf8_strncasecmp(um, name, &entry); if (ret < 0) { /* Handle invalid character sequence as either an error * or as an opaque byte sequence. */ - if (f2fs_has_strict_mode(sbi)) - return -EINVAL; - - if (name->len != entry->len) - return 1; - - return !!memcmp(name->name, entry->name, name->len); + if (sb_has_enc_strict_mode(sb)) + ret = -EINVAL; + else if (name->len != entry.len) + ret = 1; + else + ret = !!memcmp(name->name, entry.name, entry.len); } - +out: + kfree(decrypted_name.name); return ret; } @@ -154,7 +172,7 @@ static void f2fs_fname_setup_ci_filename(struct inode *dir, if (!cf_name->name) return; - cf_name->len = utf8_casefold(sbi->s_encoding, + cf_name->len = utf8_casefold(dir->i_sb->s_encoding, iname, cf_name->name, F2FS_NAME_LEN); if ((int)cf_name->len <= 0) { @@ -173,24 +191,24 @@ static inline bool f2fs_match_name(struct f2fs_dentry_ptr *d, { #ifdef CONFIG_UNICODE struct inode *parent = d->inode; - struct f2fs_sb_info *sbi = F2FS_I_SB(parent); - struct qstr entry; + u8 *name; + int len; #endif if (de->hash_code != namehash) return false; #ifdef CONFIG_UNICODE - entry.name = d->filename[bit_pos]; - entry.len = de->name_len; + name = d->filename[bit_pos]; + len = le16_to_cpu(de->name_len); - if (sbi->s_encoding && IS_CASEFOLDED(parent)) { + if (needs_casefold(parent)) { if (cf_str->name) { struct qstr cf = {.name = cf_str->name, .len = cf_str->len}; - return !f2fs_ci_compare(parent, &cf, &entry, true); + return !f2fs_ci_compare(parent, &cf, name, len, true); } - return !f2fs_ci_compare(parent, fname->usr_fname, &entry, + return !f2fs_ci_compare(parent, fname->usr_fname, name, len, false); } #endif @@ -357,8 +375,8 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir, int err; #ifdef CONFIG_UNICODE - if (f2fs_has_strict_mode(F2FS_I_SB(dir)) && IS_CASEFOLDED(dir) && - utf8_validate(F2FS_I_SB(dir)->s_encoding, child)) { + if (sb_has_enc_strict_mode(dir->i_sb) && IS_CASEFOLDED(dir) && + utf8_validate(dir->i_sb->s_encoding, child)) { *res_page = ERR_PTR(-EINVAL); return NULL; } @@ -578,6 +596,20 @@ int f2fs_room_for_filename(const void *bitmap, int slots, int max_slots) goto next; } +bool f2fs_has_enough_room(struct inode *dir, struct page *ipage, + struct fscrypt_name *fname) +{ + struct f2fs_dentry_ptr d; + unsigned int bit_pos; + int slots = GET_DENTRY_SLOTS(fname_len(fname)); + + make_dentry_ptr_inline(dir, &d, inline_data_addr(dir, ipage)); + + bit_pos = f2fs_room_for_filename(d.bitmap, slots, d.max); + + return bit_pos < d.max; +} + void f2fs_update_dentry(nid_t ino, umode_t mode, struct f2fs_dentry_ptr *d, const struct qstr *name, f2fs_hash_t name_hash, unsigned int bit_pos) @@ -602,13 +634,13 @@ void f2fs_update_dentry(nid_t ino, umode_t mode, struct f2fs_dentry_ptr *d, int f2fs_add_regular_entry(struct inode *dir, const struct qstr *new_name, const struct qstr *orig_name, + f2fs_hash_t dentry_hash, struct inode *inode, nid_t ino, umode_t mode) { unsigned int bit_pos; unsigned int level; unsigned int current_depth; unsigned long bidx, block; - f2fs_hash_t dentry_hash; unsigned int nbucket, nblock; struct page *dentry_page = NULL; struct f2fs_dentry_block *dentry_blk = NULL; @@ -618,7 +650,6 @@ int f2fs_add_regular_entry(struct inode *dir, const struct qstr *new_name, level = 0; slots = GET_DENTRY_SLOTS(new_name->len); - dentry_hash = f2fs_dentry_hash(dir, new_name, NULL); current_depth = F2FS_I(dir)->i_current_depth; if (F2FS_I(dir)->chash == dentry_hash) { @@ -704,17 +735,19 @@ int f2fs_add_dentry(struct inode *dir, struct fscrypt_name *fname, struct inode *inode, nid_t ino, umode_t mode) { struct qstr new_name; + f2fs_hash_t dentry_hash; int err = -EAGAIN; new_name.name = fname_name(fname); new_name.len = fname_len(fname); if (f2fs_has_inline_dentry(dir)) - err = f2fs_add_inline_entry(dir, &new_name, fname->usr_fname, + err = f2fs_add_inline_entry(dir, &new_name, fname, inode, ino, mode); + dentry_hash = f2fs_dentry_hash(dir, &new_name, fname); if (err == -EAGAIN) err = f2fs_add_regular_entry(dir, &new_name, fname->usr_fname, - inode, ino, mode); + dentry_hash, inode, ino, mode); f2fs_update_time(F2FS_I_SB(dir), REQ_TIME); return err; @@ -987,7 +1020,7 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) if (IS_ENCRYPTED(inode)) { err = fscrypt_get_encryption_info(inode); - if (err && err != -ENOKEY) + if (err) goto out; err = fscrypt_fname_alloc_buffer(inode, F2FS_NAME_LEN, &fstr); @@ -1063,50 +1096,3 @@ const struct file_operations f2fs_dir_operations = { .compat_ioctl = f2fs_compat_ioctl, #endif }; - -#ifdef CONFIG_UNICODE -static int f2fs_d_compare(const struct dentry *dentry, unsigned int len, - const char *str, const struct qstr *name) -{ - struct qstr qstr = {.name = str, .len = len }; - - if (!IS_CASEFOLDED(dentry->d_parent->d_inode)) { - if (len != name->len) - return -1; - return memcmp(str, name, len); - } - - return f2fs_ci_compare(dentry->d_parent->d_inode, name, &qstr, false); -} - -static int f2fs_d_hash(const struct dentry *dentry, struct qstr *str) -{ - struct f2fs_sb_info *sbi = F2FS_SB(dentry->d_sb); - const struct unicode_map *um = sbi->s_encoding; - unsigned char *norm; - int len, ret = 0; - - if (!IS_CASEFOLDED(dentry->d_inode)) - return 0; - - norm = f2fs_kmalloc(sbi, PATH_MAX, GFP_ATOMIC); - if (!norm) - return -ENOMEM; - - len = utf8_casefold(um, str, norm, PATH_MAX); - if (len < 0) { - if (f2fs_has_strict_mode(sbi)) - ret = -EINVAL; - goto out; - } - str->hash = full_name_hash(dentry, norm, len); -out: - kvfree(norm); - return ret; -} - -const struct dentry_operations f2fs_dentry_ops = { - .d_hash = f2fs_d_hash, - .d_compare = f2fs_d_compare, -}; -#endif diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 229726794b76e25e70a23dd8e0b7982b57993900..28ab0cd1e26983abfab01b53fb4a1bfa181da13b 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -116,6 +116,8 @@ typedef u32 block_t; /* */ typedef u32 nid_t; +#define COMPRESS_EXT_NUM 16 + struct f2fs_mount_info { unsigned int opt; int write_io_size_bits; /* Write IO size bits */ @@ -143,6 +145,12 @@ struct f2fs_mount_info { block_t unusable_cap; /* Amount of space allowed to be * unusable when disabling checkpoint */ + + /* For compression */ + unsigned char compress_algorithm; /* algorithm type */ + unsigned compress_log_size; /* cluster log size */ + unsigned char compress_ext_cnt; /* extension count */ + unsigned char extensions[COMPRESS_EXT_NUM][F2FS_EXTENSION_LEN]; /* extensions */ }; #define F2FS_FEATURE_ENCRYPT 0x0001 @@ -158,6 +166,7 @@ struct f2fs_mount_info { #define F2FS_FEATURE_VERITY 0x0400 #define F2FS_FEATURE_SB_CHKSUM 0x0800 #define F2FS_FEATURE_CASEFOLD 0x1000 +#define F2FS_FEATURE_COMPRESSION 0x2000 #define __F2FS_HAS_FEATURE(raw_super, mask) \ ((raw_super->feature & cpu_to_le32(mask)) != 0) @@ -715,6 +724,12 @@ struct f2fs_inode_info { int i_inline_xattr_size; /* inline xattr size */ struct timespec64 i_crtime; /* inode creation time */ struct timespec64 i_disk_time[4];/* inode disk times */ + + /* for file compress */ + u64 i_compr_blocks; /* # of compressed blocks */ + unsigned char i_compress_algorithm; /* algorithm type */ + unsigned char i_log_cluster_size; /* log of cluster size */ + unsigned int i_cluster_size; /* cluster size */ }; static inline void get_extent_info(struct extent_info *ext, @@ -1021,6 +1036,7 @@ enum need_lock_type { enum cp_reason_type { CP_NO_NEEDED, CP_NON_REGULAR, + CP_COMPRESSED, CP_HARDLINK, CP_SB_NEED_CP, CP_WRONG_PINO, @@ -1059,12 +1075,15 @@ struct f2fs_io_info { block_t old_blkaddr; /* old block address before Cow */ struct page *page; /* page to be written */ struct page *encrypted_page; /* encrypted page */ + struct page *compressed_page; /* compressed page */ struct list_head list; /* serialize IOs */ bool submitted; /* indicate IO submission */ int need_lock; /* indicate we need to lock cp_rwsem */ bool in_list; /* indicate fio is in io_list */ bool is_por; /* indicate IO is from recovery or not */ bool retry; /* need to reallocate block address */ + int compr_blocks; /* # of compressed block addresses */ + bool encrypted; /* indicate file is encrypted */ enum iostat_type io_type; /* io type */ struct writeback_control *io_wbc; /* writeback control */ struct bio **bio; /* bio for ipu */ @@ -1172,6 +1191,18 @@ enum fsync_mode { FSYNC_MODE_NOBARRIER, /* fsync behaves nobarrier based on posix */ }; +/* + * this value is set in page as a private data which indicate that + * the page is atomically written, and it is in inmem_pages list. + */ +#define ATOMIC_WRITTEN_PAGE ((unsigned long)-1) +#define DUMMY_WRITTEN_PAGE ((unsigned long)-2) + +#define IS_ATOMIC_WRITTEN_PAGE(page) \ + (page_private(page) == (unsigned long)ATOMIC_WRITTEN_PAGE) +#define IS_DUMMY_WRITTEN_PAGE(page) \ + (page_private(page) == (unsigned long)DUMMY_WRITTEN_PAGE) + #ifdef CONFIG_FS_ENCRYPTION #define DUMMY_ENCRYPTION_ENABLED(sbi) \ (unlikely(F2FS_OPTION(sbi).test_dummy_encryption)) @@ -1179,6 +1210,75 @@ enum fsync_mode { #define DUMMY_ENCRYPTION_ENABLED(sbi) (0) #endif +/* For compression */ +enum compress_algorithm_type { + COMPRESS_LZO, + COMPRESS_LZ4, + COMPRESS_MAX, +}; + +#define COMPRESS_DATA_RESERVED_SIZE 4 +struct compress_data { + __le32 clen; /* compressed data size */ + __le32 chksum; /* checksum of compressed data */ + __le32 reserved[COMPRESS_DATA_RESERVED_SIZE]; /* reserved */ + u8 cdata[]; /* compressed data */ +}; + +#define COMPRESS_HEADER_SIZE (sizeof(struct compress_data)) + +#define F2FS_COMPRESSED_PAGE_MAGIC 0xF5F2C000 + +/* compress context */ +struct compress_ctx { + struct inode *inode; /* inode the context belong to */ + pgoff_t cluster_idx; /* cluster index number */ + unsigned int cluster_size; /* page count in cluster */ + unsigned int log_cluster_size; /* log of cluster size */ + struct page **rpages; /* pages store raw data in cluster */ + unsigned int nr_rpages; /* total page number in rpages */ + struct page **cpages; /* pages store compressed data in cluster */ + unsigned int nr_cpages; /* total page number in cpages */ + void *rbuf; /* virtual mapped address on rpages */ + struct compress_data *cbuf; /* virtual mapped address on cpages */ + size_t rlen; /* valid data length in rbuf */ + size_t clen; /* valid data length in cbuf */ + void *private; /* payload buffer for specified compression algorithm */ +}; + +/* compress context for write IO path */ +struct compress_io_ctx { + u32 magic; /* magic number to indicate page is compressed */ + struct inode *inode; /* inode the context belong to */ + struct page **rpages; /* pages store raw data in cluster */ + unsigned int nr_rpages; /* total page number in rpages */ + refcount_t ref; /* referrence count of raw page */ +}; + +/* decompress io context for read IO path */ +struct decompress_io_ctx { + u32 magic; /* magic number to indicate page is compressed */ + struct inode *inode; /* inode the context belong to */ + pgoff_t cluster_idx; /* cluster index number */ + unsigned int cluster_size; /* page count in cluster */ + unsigned int log_cluster_size; /* log of cluster size */ + struct page **rpages; /* pages store raw data in cluster */ + unsigned int nr_rpages; /* total page number in rpages */ + struct page **cpages; /* pages store compressed data in cluster */ + unsigned int nr_cpages; /* total page number in cpages */ + struct page **tpages; /* temp pages to pad holes in cluster */ + void *rbuf; /* virtual mapped address on rpages */ + struct compress_data *cbuf; /* virtual mapped address on cpages */ + size_t rlen; /* valid data length in rbuf */ + size_t clen; /* valid data length in cbuf */ + refcount_t ref; /* referrence count of compressed page */ + bool failed; /* indicate IO error during decompression */ +}; + +#define NULL_CLUSTER ((unsigned int)(~0)) +#define MIN_COMPRESS_LOG_SIZE 2 +#define MAX_COMPRESS_LOG_SIZE 8 + struct f2fs_sb_info { struct super_block *sb; /* pointer to VFS super block */ struct proc_dir_entry *s_proc; /* proc entry */ @@ -1187,10 +1287,6 @@ struct f2fs_sb_info { int valid_super_block; /* valid super block no */ unsigned long s_flag; /* flags for sbi */ struct mutex writepages; /* mutex for writepages() */ -#ifdef CONFIG_UNICODE - struct unicode_map *s_encoding; - __u16 s_encoding_flags; -#endif #ifdef CONFIG_BLK_DEV_ZONED unsigned int blocks_per_blkz; /* F2FS blocks per zone */ @@ -1294,7 +1390,10 @@ struct f2fs_sb_info { struct f2fs_mount_info mount_opt; /* mount options */ /* for cleaning operations */ - struct mutex gc_mutex; /* mutex for GC */ + struct rw_semaphore gc_lock; /* + * semaphore for GC, avoid + * race between GC and GC or CP + */ struct f2fs_gc_kthread *gc_thread; /* GC thread */ unsigned int cur_victim_sec; /* current victim section num */ unsigned int gc_mode; /* current GC state */ @@ -1330,11 +1429,11 @@ struct f2fs_sb_info { atomic_t inline_xattr; /* # of inline_xattr inodes */ atomic_t inline_inode; /* # of inline_data inodes */ atomic_t inline_dir; /* # of inline_dentry inodes */ - atomic_t aw_cnt; /* # of atomic writes */ + atomic_t compr_inode; /* # of compressed inodes */ + atomic_t compr_blocks; /* # of compressed blocks */ atomic_t vw_cnt; /* # of volatile writes */ atomic_t max_aw_cnt; /* max # of atomic writes */ atomic_t max_vw_cnt; /* max # of volatile writes */ - int bg_gc; /* background gc calls */ 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 */ @@ -1368,6 +1467,8 @@ struct f2fs_sb_info { /* Precomputed FS UUID checksum for seeding other checksums */ __u32 s_chksum_seed; + + struct workqueue_struct *post_read_wq; /* post read workqueue */ }; struct f2fs_private_dio { @@ -2226,26 +2327,6 @@ static inline void *f2fs_kmem_cache_alloc(struct kmem_cache *cachep, return entry; } -static inline struct bio *f2fs_bio_alloc(struct f2fs_sb_info *sbi, - int npages, bool no_fail) -{ - struct bio *bio; - - if (no_fail) { - /* No failure on bio allocation */ - bio = bio_alloc(GFP_NOIO, npages); - if (!bio) - bio = bio_alloc(GFP_NOIO | __GFP_NOFAIL, npages); - return bio; - } - if (time_to_inject(sbi, FAULT_ALLOC_BIO)) { - f2fs_show_injection_info(sbi, FAULT_ALLOC_BIO); - return NULL; - } - - return bio_alloc(GFP_KERNEL, npages); -} - static inline bool is_idle(struct f2fs_sb_info *sbi, int type) { if (sbi->gc_mode == GC_URGENT) @@ -2382,11 +2463,13 @@ static inline void f2fs_change_bit(unsigned int nr, char *addr) /* * On-disk inode flags (f2fs_inode::i_flags) */ +#define F2FS_COMPR_FL 0x00000004 /* Compress file */ #define F2FS_SYNC_FL 0x00000008 /* Synchronous updates */ #define F2FS_IMMUTABLE_FL 0x00000010 /* Immutable file */ #define F2FS_APPEND_FL 0x00000020 /* writes to file may only append */ #define F2FS_NODUMP_FL 0x00000040 /* do not dump file */ #define F2FS_NOATIME_FL 0x00000080 /* do not update atime */ +#define F2FS_NOCOMP_FL 0x00000400 /* Don't compress */ #define F2FS_INDEX_FL 0x00001000 /* hash-indexed directory */ #define F2FS_DIRSYNC_FL 0x00010000 /* dirsync behaviour (directories only) */ #define F2FS_PROJINHERIT_FL 0x20000000 /* Create with parents projid */ @@ -2395,7 +2478,7 @@ static inline void f2fs_change_bit(unsigned int nr, char *addr) /* Flags that should be inherited by new inodes from their parent. */ #define F2FS_FL_INHERITED (F2FS_SYNC_FL | F2FS_NODUMP_FL | F2FS_NOATIME_FL | \ F2FS_DIRSYNC_FL | F2FS_PROJINHERIT_FL | \ - F2FS_CASEFOLD_FL) + F2FS_CASEFOLD_FL | F2FS_COMPR_FL | F2FS_NOCOMP_FL) /* Flags that are appropriate for regular files (all but dir-specific ones). */ #define F2FS_REG_FLMASK (~(F2FS_DIRSYNC_FL | F2FS_PROJINHERIT_FL | \ @@ -2447,6 +2530,8 @@ enum { FI_PIN_FILE, /* indicate file should not be gced */ FI_ATOMIC_REVOKE_REQUEST, /* request to drop atomic data */ FI_VERITY_IN_PROGRESS, /* building fs-verity Merkle tree */ + FI_COMPRESSED_FILE, /* indicate file's data can be compressed */ + FI_MMAP_FILE, /* indicate file was mmapped */ }; static inline void __mark_inode_dirty_flag(struct inode *inode, @@ -2463,6 +2548,7 @@ static inline void __mark_inode_dirty_flag(struct inode *inode, case FI_DATA_EXIST: case FI_INLINE_DOTS: case FI_PIN_FILE: + case FI_COMPRESSED_FILE: f2fs_mark_inode_dirty_sync(inode, true); } } @@ -2618,16 +2704,27 @@ static inline int f2fs_has_inline_xattr(struct inode *inode) return is_inode_flag_set(inode, FI_INLINE_XATTR); } +static inline int f2fs_compressed_file(struct inode *inode) +{ + return S_ISREG(inode->i_mode) && + is_inode_flag_set(inode, FI_COMPRESSED_FILE); +} + static inline unsigned int addrs_per_inode(struct inode *inode) { unsigned int addrs = CUR_ADDRS_PER_INODE(inode) - get_inline_xattr_addrs(inode); - return ALIGN_DOWN(addrs, 1); + + if (!f2fs_compressed_file(inode)) + return addrs; + return ALIGN_DOWN(addrs, F2FS_I(inode)->i_cluster_size); } static inline unsigned int addrs_per_block(struct inode *inode) { - return ALIGN_DOWN(DEF_ADDRS_PER_BLOCK, 1); + if (!f2fs_compressed_file(inode)) + return DEF_ADDRS_PER_BLOCK; + return ALIGN_DOWN(DEF_ADDRS_PER_BLOCK, F2FS_I(inode)->i_cluster_size); } static inline void *inline_xattr_addr(struct inode *inode, struct page *page) @@ -2660,6 +2757,11 @@ static inline int f2fs_has_inline_dots(struct inode *inode) return is_inode_flag_set(inode, FI_INLINE_DOTS); } +static inline int f2fs_is_mmap_file(struct inode *inode) +{ + return is_inode_flag_set(inode, FI_MMAP_FILE); +} + static inline bool f2fs_is_pinned_file(struct inode *inode) { return is_inode_flag_set(inode, FI_PIN_FILE); @@ -2787,7 +2889,8 @@ static inline bool f2fs_may_extent_tree(struct inode *inode) struct f2fs_sb_info *sbi = F2FS_I_SB(inode); if (!test_opt(sbi, EXTENT_CACHE) || - is_inode_flag_set(inode, FI_NO_EXTENT)) + is_inode_flag_set(inode, FI_NO_EXTENT) || + is_inode_flag_set(inode, FI_COMPRESSED_FILE)) return false; /* @@ -2907,7 +3010,8 @@ static inline void verify_blkaddr(struct f2fs_sb_info *sbi, static inline bool __is_valid_data_blkaddr(block_t blkaddr) { - if (blkaddr == NEW_ADDR || blkaddr == NULL_ADDR) + if (blkaddr == NEW_ADDR || blkaddr == NULL_ADDR || + blkaddr == COMPRESS_ADDR) return false; return true; } @@ -2973,11 +3077,6 @@ int f2fs_update_extension_list(struct f2fs_sb_info *sbi, const char *name, bool hot, bool set); struct dentry *f2fs_get_parent(struct dentry *child); -extern int f2fs_ci_compare(const struct inode *parent, - const struct qstr *name, - const struct qstr *entry, - bool quick); - /* * dir.c */ @@ -3005,11 +3104,13 @@ ino_t f2fs_inode_by_name(struct inode *dir, const struct qstr *qstr, struct page **page); void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de, struct page *page, struct inode *inode); +bool f2fs_has_enough_room(struct inode *dir, struct page *ipage, + struct fscrypt_name *fname); void f2fs_update_dentry(nid_t ino, umode_t mode, struct f2fs_dentry_ptr *d, const struct qstr *name, f2fs_hash_t name_hash, unsigned int bit_pos); int f2fs_add_regular_entry(struct inode *dir, const struct qstr *new_name, - const struct qstr *orig_name, + const struct qstr *orig_name, f2fs_hash_t dentry_hash, struct inode *inode, nid_t ino, umode_t mode); int f2fs_add_dentry(struct inode *dir, struct fscrypt_name *fname, struct inode *inode, nid_t ino, umode_t mode); @@ -3042,7 +3143,7 @@ int f2fs_sanity_check_ckpt(struct f2fs_sb_info *sbi); * hash.c */ f2fs_hash_t f2fs_dentry_hash(const struct inode *dir, - const struct qstr *name_info, struct fscrypt_name *fname); + const struct qstr *name_info, const struct fscrypt_name *fname); /* * node.c @@ -3074,6 +3175,7 @@ void f2fs_ra_node_page(struct f2fs_sb_info *sbi, nid_t nid); struct page *f2fs_get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid); struct page *f2fs_get_node_page_ra(struct page *parent, int start); int f2fs_move_node_page(struct page *node_page, int gc_type); +int f2fs_flush_inline_data(struct f2fs_sb_info *sbi); int f2fs_fsync_node_pages(struct f2fs_sb_info *sbi, struct inode *inode, struct writeback_control *wbc, bool atomic, unsigned int *seq_id); @@ -3209,10 +3311,13 @@ void f2fs_destroy_checkpoint_caches(void); /* * data.c */ -int f2fs_init_post_read_processing(void); -void f2fs_destroy_post_read_processing(void); +int __init f2fs_init_bioset(void); +void f2fs_destroy_bioset(void); +struct bio *f2fs_bio_alloc(struct f2fs_sb_info *sbi, int npages, bool no_fail); int f2fs_init_bio_entry_cache(void); void f2fs_destroy_bio_entry_cache(void); +void f2fs_submit_bio(struct f2fs_sb_info *sbi, + struct bio *bio, enum page_type type); void f2fs_submit_merged_write(struct f2fs_sb_info *sbi, enum page_type type); void f2fs_submit_merged_write_cond(struct f2fs_sb_info *sbi, struct inode *inode, struct page *page, @@ -3233,6 +3338,9 @@ int f2fs_reserve_new_block(struct dnode_of_data *dn); int f2fs_get_block(struct dnode_of_data *dn, pgoff_t index); int f2fs_preallocate_blocks(struct kiocb *iocb, struct iov_iter *from); int f2fs_reserve_block(struct dnode_of_data *dn, pgoff_t index); +int f2fs_mpage_readpages(struct address_space *mapping, + struct list_head *pages, struct page *page, + unsigned nr_pages, bool is_readahead); struct page *f2fs_get_read_data_page(struct inode *inode, pgoff_t index, int op_flags, bool for_write); struct page *f2fs_find_data_page(struct inode *inode, pgoff_t index); @@ -3246,8 +3354,14 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, int create, int flag); int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, u64 start, u64 len); +int f2fs_encrypt_one_page(struct f2fs_io_info *fio); bool f2fs_should_update_inplace(struct inode *inode, struct f2fs_io_info *fio); bool f2fs_should_update_outplace(struct inode *inode, struct f2fs_io_info *fio); +int f2fs_write_single_data_page(struct page *page, int *submitted, + struct bio **bio, sector_t *last_block, + struct writeback_control *wbc, + enum iostat_type io_type, + int compr_blocks); void f2fs_invalidate_page(struct page *page, unsigned int offset, unsigned int length); int f2fs_release_page(struct page *page, gfp_t wait); @@ -3257,6 +3371,10 @@ int f2fs_migrate_page(struct address_space *mapping, struct page *newpage, #endif bool f2fs_overwrite_io(struct inode *inode, loff_t pos, size_t len); void f2fs_clear_radix_tree_dirty_tag(struct page *page); +int f2fs_init_post_read_processing(void); +void f2fs_destroy_post_read_processing(void); +int f2fs_init_post_read_wq(struct f2fs_sb_info *sbi); +void f2fs_destroy_post_read_wq(struct f2fs_sb_info *sbi); /* * gc.c @@ -3303,6 +3421,7 @@ struct f2fs_stat_info { int nr_discard_cmd; unsigned int undiscard_blks; int inline_xattr, inline_inode, inline_dir, append, update, orphans; + int compr_inode, compr_blocks; int aw_cnt, max_aw_cnt, vw_cnt, max_vw_cnt; unsigned int valid_count, valid_node_count, valid_inode_count, discard_blks; unsigned int bimodal, avg_vblocks; @@ -3334,7 +3453,7 @@ static inline struct f2fs_stat_info *F2FS_STAT(struct f2fs_sb_info *sbi) #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(sbi) ((sbi)->bg_gc++) +#define stat_inc_bggc_count(si) ((si)->bg_gc++) #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]++) @@ -3373,6 +3492,20 @@ static inline struct f2fs_stat_info *F2FS_STAT(struct f2fs_sb_info *sbi) if (f2fs_has_inline_dentry(inode)) \ (atomic_dec(&F2FS_I_SB(inode)->inline_dir)); \ } while (0) +#define stat_inc_compr_inode(inode) \ + do { \ + if (f2fs_compressed_file(inode)) \ + (atomic_inc(&F2FS_I_SB(inode)->compr_inode)); \ + } while (0) +#define stat_dec_compr_inode(inode) \ + do { \ + if (f2fs_compressed_file(inode)) \ + (atomic_dec(&F2FS_I_SB(inode)->compr_inode)); \ + } while (0) +#define stat_add_compr_blocks(inode, blocks) \ + (atomic_add(blocks, &F2FS_I_SB(inode)->compr_blocks)) +#define stat_sub_compr_blocks(inode, blocks) \ + (atomic_sub(blocks, &F2FS_I_SB(inode)->compr_blocks)) #define stat_inc_meta_count(sbi, blkaddr) \ do { \ if (blkaddr < SIT_I(sbi)->sit_base_addr) \ @@ -3390,13 +3523,9 @@ static inline struct f2fs_stat_info *F2FS_STAT(struct f2fs_sb_info *sbi) ((sbi)->block_count[(curseg)->alloc_type]++) #define stat_inc_inplace_blocks(sbi) \ (atomic_inc(&(sbi)->inplace_count)) -#define stat_inc_atomic_write(inode) \ - (atomic_inc(&F2FS_I_SB(inode)->aw_cnt)) -#define stat_dec_atomic_write(inode) \ - (atomic_dec(&F2FS_I_SB(inode)->aw_cnt)) #define stat_update_max_atomic_write(inode) \ do { \ - int cur = atomic_read(&F2FS_I_SB(inode)->aw_cnt); \ + int cur = F2FS_I_SB(inode)->atomic_files; \ int max = atomic_read(&F2FS_I_SB(inode)->max_aw_cnt); \ if (cur > max) \ atomic_set(&F2FS_I_SB(inode)->max_aw_cnt, cur); \ @@ -3448,6 +3577,7 @@ int f2fs_build_stats(struct f2fs_sb_info *sbi); void f2fs_destroy_stats(struct f2fs_sb_info *sbi); 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) @@ -3457,8 +3587,8 @@ void f2fs_destroy_root_stats(void); #define stat_other_skip_bggc_count(sbi) do { } while (0) #define stat_inc_dirty_inode(sbi, type) do { } while (0) #define stat_dec_dirty_inode(sbi, type) do { } while (0) -#define stat_inc_total_hit(sb) do { } while (0) -#define stat_inc_rbtree_node_hit(sb) do { } while (0) +#define stat_inc_total_hit(sbi) do { } while (0) +#define stat_inc_rbtree_node_hit(sbi) do { } while (0) #define stat_inc_largest_node_hit(sbi) do { } while (0) #define stat_inc_cached_node_hit(sbi) do { } while (0) #define stat_inc_inline_xattr(inode) do { } while (0) @@ -3467,6 +3597,10 @@ void f2fs_destroy_root_stats(void); #define stat_dec_inline_inode(inode) do { } while (0) #define stat_inc_inline_dir(inode) do { } while (0) #define stat_dec_inline_dir(inode) do { } while (0) +#define stat_inc_compr_inode(inode) do { } while (0) +#define stat_dec_compr_inode(inode) do { } while (0) +#define stat_add_compr_blocks(inode, blocks) do { } while (0) +#define stat_sub_compr_blocks(inode, blocks) do { } while (0) #define stat_inc_atomic_write(inode) do { } while (0) #define stat_dec_atomic_write(inode) do { } while (0) #define stat_update_max_atomic_write(inode) do { } while (0) @@ -3486,12 +3620,10 @@ static inline int f2fs_build_stats(struct f2fs_sb_info *sbi) { return 0; } static inline void f2fs_destroy_stats(struct f2fs_sb_info *sbi) { } static inline void __init f2fs_create_root_stats(void) { } static inline void f2fs_destroy_root_stats(void) { } +static inline void update_sit_info(struct f2fs_sb_info *sbi) {} #endif extern const struct file_operations f2fs_dir_operations; -#ifdef CONFIG_UNICODE -extern const struct dentry_operations f2fs_dentry_ops; -#endif extern const struct file_operations f2fs_file_operations; extern const struct inode_operations f2fs_file_inode_operations; extern const struct address_space_operations f2fs_dblock_aops; @@ -3514,6 +3646,7 @@ void f2fs_truncate_inline_inode(struct inode *inode, int f2fs_read_inline_data(struct inode *inode, struct page *page); int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page); int f2fs_convert_inline_inode(struct inode *inode); +int f2fs_try_convert_inline_dir(struct inode *dir, struct dentry *dentry); int f2fs_write_inline_data(struct inode *inode, struct page *page); bool f2fs_recover_inline_data(struct inode *inode, struct page *npage); struct f2fs_dir_entry *f2fs_find_in_inline_dir(struct inode *dir, @@ -3521,7 +3654,7 @@ struct f2fs_dir_entry *f2fs_find_in_inline_dir(struct inode *dir, int f2fs_make_empty_inline_dir(struct inode *inode, struct inode *parent, struct page *ipage); int f2fs_add_inline_entry(struct inode *dir, const struct qstr *new_name, - const struct qstr *orig_name, + const struct fscrypt_name *fname, struct inode *inode, nid_t ino, umode_t mode); void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct page *page, struct inode *dir, @@ -3606,7 +3739,85 @@ static inline void f2fs_set_encrypted_inode(struct inode *inode) */ static inline bool f2fs_post_read_required(struct inode *inode) { - return f2fs_encrypted_file(inode) || fsverity_active(inode); + return f2fs_encrypted_file(inode) || fsverity_active(inode) || + f2fs_compressed_file(inode); +} + +/* + * compress.c + */ +#ifdef CONFIG_F2FS_FS_COMPRESSION +bool f2fs_is_compressed_page(struct page *page); +struct page *f2fs_compress_control_page(struct page *page); +int f2fs_prepare_compress_overwrite(struct inode *inode, + struct page **pagep, pgoff_t index, void **fsdata); +bool f2fs_compress_write_end(struct inode *inode, void *fsdata, + pgoff_t index, unsigned copied); +void f2fs_compress_write_end_io(struct bio *bio, struct page *page); +bool f2fs_is_compress_backend_ready(struct inode *inode); +void f2fs_decompress_pages(struct bio *bio, struct page *page, bool verity); +bool f2fs_cluster_is_empty(struct compress_ctx *cc); +bool f2fs_cluster_can_merge_page(struct compress_ctx *cc, pgoff_t index); +void f2fs_compress_ctx_add_page(struct compress_ctx *cc, struct page *page); +int f2fs_write_multi_pages(struct compress_ctx *cc, + int *submitted, + struct writeback_control *wbc, + enum iostat_type io_type); +int f2fs_is_compressed_cluster(struct inode *inode, pgoff_t index); +int f2fs_read_multi_pages(struct compress_ctx *cc, struct bio **bio_ret, + unsigned nr_pages, sector_t *last_block_in_bio, + bool is_readahead); +struct decompress_io_ctx *f2fs_alloc_dic(struct compress_ctx *cc); +void f2fs_free_dic(struct decompress_io_ctx *dic); +void f2fs_decompress_end_io(struct page **rpages, + unsigned int cluster_size, bool err, bool verity); +int f2fs_init_compress_ctx(struct compress_ctx *cc); +void f2fs_destroy_compress_ctx(struct compress_ctx *cc); +void f2fs_init_compress_info(struct f2fs_sb_info *sbi); +#else +static inline bool f2fs_is_compressed_page(struct page *page) { return false; } +static inline bool f2fs_is_compress_backend_ready(struct inode *inode) +{ + if (!f2fs_compressed_file(inode)) + return true; + /* not support compression */ + return false; +} +static inline struct page *f2fs_compress_control_page(struct page *page) +{ + WARN_ON_ONCE(1); + return ERR_PTR(-EINVAL); +} +#endif + +static inline void set_compress_context(struct inode *inode) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + + F2FS_I(inode)->i_compress_algorithm = + F2FS_OPTION(sbi).compress_algorithm; + F2FS_I(inode)->i_log_cluster_size = + F2FS_OPTION(sbi).compress_log_size; + F2FS_I(inode)->i_cluster_size = + 1 << F2FS_I(inode)->i_log_cluster_size; + F2FS_I(inode)->i_flags |= F2FS_COMPR_FL; + set_inode_flag(inode, FI_COMPRESSED_FILE); + stat_inc_compr_inode(inode); +} + +static inline u64 f2fs_disable_compressed_file(struct inode *inode) +{ + struct f2fs_inode_info *fi = F2FS_I(inode); + + if (!f2fs_compressed_file(inode)) + return 0; + if (fi->i_compr_blocks) + return fi->i_compr_blocks; + + fi->i_flags &= ~F2FS_COMPR_FL; + clear_inode_flag(inode, FI_COMPRESSED_FILE); + stat_dec_compr_inode(inode); + return 0; } #define F2FS_FEATURE_FUNCS(name, flagname) \ @@ -3627,6 +3838,7 @@ F2FS_FEATURE_FUNCS(lost_found, LOST_FOUND); F2FS_FEATURE_FUNCS(verity, VERITY); F2FS_FEATURE_FUNCS(sb_chksum, SB_CHKSUM); F2FS_FEATURE_FUNCS(casefold, CASEFOLD); +F2FS_FEATURE_FUNCS(compression, COMPRESSION); #ifdef CONFIG_BLK_DEV_ZONED static inline bool f2fs_blkz_is_seq(struct f2fs_sb_info *sbi, int devi, @@ -3708,6 +3920,30 @@ static inline bool f2fs_may_encrypt(struct inode *inode) #endif } +static inline bool f2fs_may_compress(struct inode *inode) +{ + if (IS_SWAPFILE(inode) || f2fs_is_pinned_file(inode) || + f2fs_is_atomic_file(inode) || + f2fs_is_volatile_file(inode)) + return false; + return S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode); +} + +static inline void f2fs_i_compr_blocks_update(struct inode *inode, + u64 blocks, bool add) +{ + int diff = F2FS_I(inode)->i_cluster_size - blocks; + + if (add) { + F2FS_I(inode)->i_compr_blocks += diff; + stat_add_compr_blocks(inode, diff); + } else { + F2FS_I(inode)->i_compr_blocks -= diff; + stat_sub_compr_blocks(inode, diff); + } + f2fs_mark_inode_dirty_sync(inode, true); +} + static inline int block_unaligned_IO(struct inode *inode, struct kiocb *iocb, struct iov_iter *iter) { @@ -3745,6 +3981,8 @@ static inline bool f2fs_force_buffered_io(struct inode *inode, return true; if (f2fs_is_multi_device(sbi)) return true; + if (f2fs_compressed_file(inode)) + return true; /* * for blkzoned device, fallback direct IO to buffered IO, so * all IOs can be serialized by log-structured write. diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index ec2227927aaf7832a49d4ac9b2aeb6f5dd504cc1..996e79529b015ca0a5bf43742ad768c651d29459 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -52,7 +52,8 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf) struct inode *inode = file_inode(vmf->vma->vm_file); struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct dnode_of_data dn; - int err; + bool need_alloc = true; + int err = 0; if (unlikely(f2fs_cp_error(sbi))) { err = -EIO; @@ -67,6 +68,26 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf) goto err; } +#ifdef CONFIG_F2FS_FS_COMPRESSION + if (f2fs_compressed_file(inode)) { + int ret = f2fs_is_compressed_cluster(inode, page->index); + + if (ret < 0) { + err = ret; + goto err; + } else if (ret) { + if (ret < F2FS_I(inode)->i_cluster_size) { + err = -EAGAIN; + goto err; + } + need_alloc = false; + } + } +#endif + /* should do out of any locked page */ + if (need_alloc) + f2fs_balance_fs(sbi, true); + sb_start_pagefault(inode->i_sb); f2fs_bug_on(sbi, f2fs_has_inline_data(inode)); @@ -82,15 +103,17 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf) goto out_sem; } - /* block allocation */ - __do_map_lock(sbi, F2FS_GET_BLOCK_PRE_AIO, true); - set_new_dnode(&dn, inode, NULL, NULL, 0); - err = f2fs_get_block(&dn, page->index); - f2fs_put_dnode(&dn); - __do_map_lock(sbi, F2FS_GET_BLOCK_PRE_AIO, false); - if (err) { - unlock_page(page); - goto out_sem; + if (need_alloc) { + /* block allocation */ + __do_map_lock(sbi, F2FS_GET_BLOCK_PRE_AIO, true); + set_new_dnode(&dn, inode, NULL, NULL, 0); + err = f2fs_get_block(&dn, page->index); + f2fs_put_dnode(&dn); + __do_map_lock(sbi, F2FS_GET_BLOCK_PRE_AIO, false); + if (err) { + unlock_page(page); + goto out_sem; + } } /* fill the page */ @@ -157,6 +180,8 @@ static inline enum cp_reason_type need_do_checkpoint(struct inode *inode) if (!S_ISREG(inode->i_mode)) cp_reason = CP_NON_REGULAR; + else if (f2fs_compressed_file(inode)) + cp_reason = CP_COMPRESSED; else if (inode->i_nlink != 1) cp_reason = CP_HARDLINK; else if (is_sbi_flag_set(sbi, SBI_NEED_CP)) @@ -498,6 +523,9 @@ static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma) if (unlikely(f2fs_cp_error(F2FS_I_SB(inode)))) return -EIO; + if (!f2fs_is_compress_backend_ready(inode)) + return -EOPNOTSUPP; + /* we don't need to use inline_data strictly */ err = f2fs_convert_inline_inode(inode); if (err) @@ -505,6 +533,7 @@ static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma) file_accessed(file); vma->vm_ops = &f2fs_file_vm_ops; + set_inode_flag(inode, FI_MMAP_FILE); return 0; } @@ -515,6 +544,9 @@ static int f2fs_file_open(struct inode *inode, struct file *filp) if (err) return err; + if (!f2fs_is_compress_backend_ready(inode)) + return -EOPNOTSUPP; + err = fsverity_file_open(inode, filp); if (err) return err; @@ -531,6 +563,9 @@ void f2fs_truncate_data_blocks_range(struct dnode_of_data *dn, int count) int nr_free = 0, ofs = dn->ofs_in_node, len = count; __le32 *addr; int base = 0; + bool compressed_cluster = false; + int cluster_index = 0, valid_blocks = 0; + int cluster_size = F2FS_I(dn->inode)->i_cluster_size; if (IS_INODE(dn->node_page) && f2fs_has_extra_attr(dn->inode)) base = get_extra_isize(dn->inode); @@ -538,26 +573,43 @@ void f2fs_truncate_data_blocks_range(struct dnode_of_data *dn, int count) raw_node = F2FS_NODE(dn->node_page); addr = blkaddr_in_node(raw_node) + base + ofs; - for (; count > 0; count--, addr++, dn->ofs_in_node++) { + /* Assumption: truncateion starts with cluster */ + for (; count > 0; count--, addr++, dn->ofs_in_node++, cluster_index++) { block_t blkaddr = le32_to_cpu(*addr); + if (f2fs_compressed_file(dn->inode) && + !(cluster_index & (cluster_size - 1))) { + if (compressed_cluster) + f2fs_i_compr_blocks_update(dn->inode, + valid_blocks, false); + compressed_cluster = (blkaddr == COMPRESS_ADDR); + valid_blocks = 0; + } + if (blkaddr == NULL_ADDR) continue; dn->data_blkaddr = NULL_ADDR; f2fs_set_data_blkaddr(dn); - if (__is_valid_data_blkaddr(blkaddr) && - !f2fs_is_valid_blkaddr(sbi, blkaddr, + if (__is_valid_data_blkaddr(blkaddr)) { + if (!f2fs_is_valid_blkaddr(sbi, blkaddr, DATA_GENERIC_ENHANCE)) - continue; + continue; + if (compressed_cluster) + valid_blocks++; + } - f2fs_invalidate_blocks(sbi, blkaddr); if (dn->ofs_in_node == 0 && IS_INODE(dn->node_page)) clear_inode_flag(dn->inode, FI_FIRST_BLOCK_WRITTEN); + + f2fs_invalidate_blocks(sbi, blkaddr); nr_free++; } + if (compressed_cluster) + f2fs_i_compr_blocks_update(dn->inode, valid_blocks, false); + if (nr_free) { pgoff_t fofs; /* @@ -600,6 +652,9 @@ static int truncate_partial_data_page(struct inode *inode, u64 from, return 0; } + if (f2fs_compressed_file(inode)) + return 0; + page = f2fs_get_lock_data_page(inode, index, true); if (IS_ERR(page)) return PTR_ERR(page) == -ENOENT ? 0 : PTR_ERR(page); @@ -615,7 +670,7 @@ static int truncate_partial_data_page(struct inode *inode, u64 from, return 0; } -int f2fs_truncate_blocks(struct inode *inode, u64 from, bool lock) +static int do_truncate_blocks(struct inode *inode, u64 from, bool lock) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct dnode_of_data dn; @@ -680,6 +735,28 @@ int f2fs_truncate_blocks(struct inode *inode, u64 from, bool lock) return err; } +int f2fs_truncate_blocks(struct inode *inode, u64 from, bool lock) +{ + u64 free_from = from; + + /* + * for compressed file, only support cluster size + * aligned truncation. + */ + if (f2fs_compressed_file(inode)) { + size_t cluster_shift = PAGE_SHIFT + + F2FS_I(inode)->i_log_cluster_size; + size_t cluster_mask = (1 << cluster_shift) - 1; + + free_from = from >> cluster_shift; + if (from & cluster_mask) + free_from++; + free_from <<= cluster_shift; + } + + return do_truncate_blocks(inode, free_from, lock); +} + int f2fs_truncate(struct inode *inode) { int err; @@ -739,11 +816,14 @@ int f2fs_getattr(const struct path *path, struct kstat *stat, stat->attributes |= STATX_ATTR_IMMUTABLE; if (flags & F2FS_NODUMP_FL) stat->attributes |= STATX_ATTR_NODUMP; + if (IS_VERITY(inode)) + stat->attributes |= STATX_ATTR_VERITY; stat->attributes_mask |= (STATX_ATTR_APPEND | STATX_ATTR_ENCRYPTED | STATX_ATTR_IMMUTABLE | - STATX_ATTR_NODUMP); + STATX_ATTR_NODUMP | + STATX_ATTR_VERITY); generic_fillattr(inode, stat); @@ -793,6 +873,10 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) if (unlikely(f2fs_cp_error(F2FS_I_SB(inode)))) return -EIO; + if ((attr->ia_valid & ATTR_SIZE) && + !f2fs_is_compress_backend_ready(inode)) + return -EOPNOTSUPP; + err = setattr_prepare(dentry, attr); if (err) return err; @@ -1033,8 +1117,8 @@ static int __read_out_blkaddrs(struct inode *inode, block_t *blkaddr, } else if (ret == -ENOENT) { if (dn.max_level == 0) return -ENOENT; - done = min((pgoff_t)ADDRS_PER_BLOCK(inode) - dn.ofs_in_node, - len); + done = min((pgoff_t)ADDRS_PER_BLOCK(inode) - + dn.ofs_in_node, len); blkaddr += done; do_replace += done; goto next; @@ -1197,13 +1281,13 @@ static int __exchange_data_block(struct inode *src_inode, src_blkaddr = f2fs_kvzalloc(F2FS_I_SB(src_inode), array_size(olen, sizeof(block_t)), - GFP_KERNEL); + GFP_NOFS); if (!src_blkaddr) return -ENOMEM; do_replace = f2fs_kvzalloc(F2FS_I_SB(src_inode), array_size(olen, sizeof(int)), - GFP_KERNEL); + GFP_NOFS); if (!do_replace) { kvfree(src_blkaddr); return -ENOMEM; @@ -1570,7 +1654,7 @@ static int expand_inode_data(struct inode *inode, loff_t offset, next_alloc: if (has_not_enough_free_secs(sbi, 0, GET_SEC_FROM_SEG(sbi, overprovision_segments(sbi)))) { - mutex_lock(&sbi->gc_mutex); + down_write(&sbi->gc_lock); err = f2fs_gc(sbi, true, false, NULL_SEGNO); if (err && err != -ENODATA && err != -EAGAIN) goto out_err; @@ -1628,6 +1712,8 @@ static long f2fs_fallocate(struct file *file, int mode, return -EIO; if (!f2fs_is_checkpoint_ready(F2FS_I_SB(inode))) return -ENOSPC; + if (!f2fs_is_compress_backend_ready(inode)) + return -EOPNOTSUPP; /* f2fs only support ->fallocate for regular file */ if (!S_ISREG(inode->i_mode)) @@ -1637,6 +1723,11 @@ static long f2fs_fallocate(struct file *file, int mode, (mode & (FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_INSERT_RANGE))) return -EOPNOTSUPP; + if (f2fs_compressed_file(inode) && + (mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_COLLAPSE_RANGE | + FALLOC_FL_ZERO_RANGE | FALLOC_FL_INSERT_RANGE))) + return -EOPNOTSUPP; + if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE | FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE | FALLOC_FL_INSERT_RANGE)) @@ -1714,19 +1805,55 @@ static int f2fs_file_flush(struct file *file, fl_owner_t id) static int f2fs_setflags_common(struct inode *inode, u32 iflags, u32 mask) { struct f2fs_inode_info *fi = F2FS_I(inode); + u32 masked_flags = fi->i_flags & mask; + + f2fs_bug_on(F2FS_I_SB(inode), (iflags & ~mask)); /* Is it quota file? Do not allow user to mess with it */ if (IS_NOQUOTA(inode)) return -EPERM; - if ((iflags ^ fi->i_flags) & F2FS_CASEFOLD_FL) { + if ((iflags ^ masked_flags) & F2FS_CASEFOLD_FL) { if (!f2fs_sb_has_casefold(F2FS_I_SB(inode))) return -EOPNOTSUPP; if (!f2fs_empty_dir(inode)) return -ENOTEMPTY; } + if (iflags & (F2FS_COMPR_FL | F2FS_NOCOMP_FL)) { + if (!f2fs_sb_has_compression(F2FS_I_SB(inode))) + return -EOPNOTSUPP; + if ((iflags & F2FS_COMPR_FL) && (iflags & F2FS_NOCOMP_FL)) + return -EINVAL; + } + + if ((iflags ^ masked_flags) & F2FS_COMPR_FL) { + if (S_ISREG(inode->i_mode) && + (masked_flags & F2FS_COMPR_FL || i_size_read(inode) || + F2FS_HAS_BLOCKS(inode))) + return -EINVAL; + if (iflags & F2FS_NOCOMP_FL) + return -EINVAL; + if (iflags & F2FS_COMPR_FL) { + int err = f2fs_convert_inline_inode(inode); + + if (err) + return err; + + if (!f2fs_may_compress(inode)) + return -EINVAL; + + set_compress_context(inode); + } + } + if ((iflags ^ masked_flags) & F2FS_NOCOMP_FL) { + if (masked_flags & F2FS_COMPR_FL) + return -EINVAL; + } + fi->i_flags = iflags | (fi->i_flags & ~mask); + f2fs_bug_on(F2FS_I_SB(inode), (fi->i_flags & F2FS_COMPR_FL) && + (fi->i_flags & F2FS_NOCOMP_FL)); if (fi->i_flags & F2FS_PROJINHERIT_FL) set_inode_flag(inode, FI_PROJ_INHERIT); @@ -1752,11 +1879,13 @@ static const struct { u32 iflag; u32 fsflag; } f2fs_fsflags_map[] = { + { F2FS_COMPR_FL, FS_COMPR_FL }, { F2FS_SYNC_FL, FS_SYNC_FL }, { F2FS_IMMUTABLE_FL, FS_IMMUTABLE_FL }, { F2FS_APPEND_FL, FS_APPEND_FL }, { F2FS_NODUMP_FL, FS_NODUMP_FL }, { F2FS_NOATIME_FL, FS_NOATIME_FL }, + { F2FS_NOCOMP_FL, FS_NOCOMP_FL }, { F2FS_INDEX_FL, FS_INDEX_FL }, { F2FS_DIRSYNC_FL, FS_DIRSYNC_FL }, { F2FS_PROJINHERIT_FL, FS_PROJINHERIT_FL }, @@ -1764,11 +1893,13 @@ static const struct { }; #define F2FS_GETTABLE_FS_FL ( \ + FS_COMPR_FL | \ FS_SYNC_FL | \ FS_IMMUTABLE_FL | \ FS_APPEND_FL | \ FS_NODUMP_FL | \ FS_NOATIME_FL | \ + FS_NOCOMP_FL | \ FS_INDEX_FL | \ FS_DIRSYNC_FL | \ FS_PROJINHERIT_FL | \ @@ -1779,11 +1910,13 @@ static const struct { FS_CASEFOLD_FL) #define F2FS_SETTABLE_FS_FL ( \ + FS_COMPR_FL | \ FS_SYNC_FL | \ FS_IMMUTABLE_FL | \ FS_APPEND_FL | \ FS_NODUMP_FL | \ FS_NOATIME_FL | \ + FS_NOCOMP_FL | \ FS_DIRSYNC_FL | \ FS_PROJINHERIT_FL | \ FS_CASEFOLD_FL) @@ -1904,6 +2037,8 @@ static int f2fs_ioc_start_atomic_write(struct file *filp) inode_lock(inode); + f2fs_disable_compressed_file(inode); + if (f2fs_is_atomic_file(inode)) { if (is_inode_flag_set(inode, FI_ATOMIC_REVOKE_REQUEST)) ret = -EINVAL; @@ -1942,7 +2077,6 @@ static int f2fs_ioc_start_atomic_write(struct file *filp) f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); F2FS_I(inode)->inmem_task = current; - stat_inc_atomic_write(inode); stat_update_max_atomic_write(inode); out: inode_unlock(inode); @@ -2331,12 +2465,12 @@ static int f2fs_ioc_gc(struct file *filp, unsigned long arg) return ret; if (!sync) { - if (!mutex_trylock(&sbi->gc_mutex)) { + if (!down_write_trylock(&sbi->gc_lock)) { ret = -EBUSY; goto out; } } else { - mutex_lock(&sbi->gc_mutex); + down_write(&sbi->gc_lock); } ret = f2fs_gc(sbi, sync, true, NULL_SEGNO); @@ -2374,12 +2508,12 @@ static int f2fs_ioc_gc_range(struct file *filp, unsigned long arg) do_more: if (!range.sync) { - if (!mutex_trylock(&sbi->gc_mutex)) { + if (!down_write_trylock(&sbi->gc_lock)) { ret = -EBUSY; goto out; } } else { - mutex_lock(&sbi->gc_mutex); + down_write(&sbi->gc_lock); } ret = f2fs_gc(sbi, range.sync, true, GET_SEGNO(sbi, range.start)); @@ -2810,7 +2944,7 @@ static int f2fs_ioc_flush_device(struct file *filp, unsigned long arg) end_segno = min(start_segno + range.segments, dev_end_segno); while (start_segno < end_segno) { - if (!mutex_trylock(&sbi->gc_mutex)) { + if (!down_write_trylock(&sbi->gc_lock)) { ret = -EBUSY; goto out; } @@ -3105,10 +3239,16 @@ static int f2fs_ioc_set_pin_file(struct file *filp, unsigned long arg) ret = -EAGAIN; goto out; } + ret = f2fs_convert_inline_inode(inode); if (ret) goto out; + if (f2fs_disable_compressed_file(inode)) { + ret = -EOPNOTSUPP; + goto out; + } + set_inode_flag(inode, FI_PIN_FILE); ret = F2FS_I(inode)->i_gc_failures[GC_FAILURE_PIN]; done: @@ -3357,6 +3497,17 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) } } +static ssize_t f2fs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter) +{ + struct file *file = iocb->ki_filp; + struct inode *inode = file_inode(file); + + if (!f2fs_is_compress_backend_ready(inode)) + return -EOPNOTSUPP; + + return generic_file_read_iter(iocb, iter); +} + static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) { struct file *file = iocb->ki_filp; @@ -3368,6 +3519,9 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) goto out; } + if (!f2fs_is_compress_backend_ready(inode)) + return -EOPNOTSUPP; + if (iocb->ki_flags & IOCB_NOWAIT) { if (!inode_trylock(inode)) { ret = -EAGAIN; @@ -3396,18 +3550,41 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) ret = -EAGAIN; goto out; } - } else { - preallocated = true; - target_size = iocb->ki_pos + iov_iter_count(from); + goto write; + } - err = f2fs_preallocate_blocks(iocb, from); - if (err) { - clear_inode_flag(inode, FI_NO_PREALLOC); - inode_unlock(inode); - ret = err; - goto out; - } + if (is_inode_flag_set(inode, FI_NO_PREALLOC)) + goto write; + + if (iocb->ki_flags & IOCB_DIRECT) { + /* + * Convert inline data for Direct I/O before entering + * f2fs_direct_IO(). + */ + err = f2fs_convert_inline_inode(inode); + if (err) + goto out_err; + /* + * If force_buffere_io() is true, we have to allocate + * blocks all the time, since f2fs_direct_IO will fall + * back to buffered IO. + */ + if (!f2fs_force_buffered_io(inode, iocb, from) && + allow_outplace_dio(inode, iocb, from)) + goto write; + } + preallocated = true; + target_size = iocb->ki_pos + iov_iter_count(from); + + err = f2fs_preallocate_blocks(iocb, from); + if (err) { +out_err: + clear_inode_flag(inode, FI_NO_PREALLOC); + inode_unlock(inode); + ret = err; + goto out; } +write: ret = __generic_file_write_iter(iocb, from); clear_inode_flag(inode, FI_NO_PREALLOC); @@ -3481,7 +3658,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) const struct file_operations f2fs_file_operations = { .llseek = f2fs_llseek, - .read_iter = generic_file_read_iter, + .read_iter = f2fs_file_read_iter, .write_iter = f2fs_file_write_iter, .open = f2fs_file_open, .release = f2fs_release_file, diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index b3d3996232901090d7d50eb908c940575a5c693b..db8725d473b52c285e1522d6379e5c271ea23359 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -78,18 +78,18 @@ static int gc_thread_func(void *data) */ if (sbi->gc_mode == GC_URGENT) { wait_ms = gc_th->urgent_sleep_time; - mutex_lock(&sbi->gc_mutex); + down_write(&sbi->gc_lock); goto do_gc; } - if (!mutex_trylock(&sbi->gc_mutex)) { + if (!down_write_trylock(&sbi->gc_lock)) { stat_other_skip_bggc_count(sbi); goto next; } if (!is_idle(sbi, GC_TIME)) { increase_sleep_time(gc_th, &wait_ms); - mutex_unlock(&sbi->gc_mutex); + up_write(&sbi->gc_lock); stat_io_skip_bggc_count(sbi); goto next; } @@ -99,7 +99,7 @@ static int gc_thread_func(void *data) else increase_sleep_time(gc_th, &wait_ms); do_gc: - stat_inc_bggc_count(sbi); + stat_inc_bggc_count(sbi->stat_info); /* if return value is not zero, no victim was selected */ if (f2fs_gc(sbi, test_opt(sbi, FORCE_FG_GC), true, NULL_SEGNO)) @@ -1049,8 +1049,10 @@ static int gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, if (phase == 3) { inode = f2fs_iget(sb, dni.ino); - if (IS_ERR(inode) || is_bad_inode(inode)) + if (IS_ERR(inode) || is_bad_inode(inode)) { + set_sbi_flag(sbi, SBI_NEED_FSCK); continue; + } if (!down_write_trylock( &F2FS_I(inode)->i_gc_rwsem[WRITE])) { @@ -1368,7 +1370,7 @@ int f2fs_gc(struct f2fs_sb_info *sbi, bool sync, reserved_segments(sbi), prefree_segments(sbi)); - mutex_unlock(&sbi->gc_mutex); + up_write(&sbi->gc_lock); put_gc_inode(&gc_list); @@ -1407,9 +1409,9 @@ static int free_segment_range(struct f2fs_sb_info *sbi, unsigned int start, .iroot = RADIX_TREE_INIT(gc_list.iroot, GFP_NOFS), }; - mutex_lock(&sbi->gc_mutex); + down_write(&sbi->gc_lock); do_garbage_collect(sbi, segno, &gc_list, FG_GC); - mutex_unlock(&sbi->gc_mutex); + up_write(&sbi->gc_lock); put_gc_inode(&gc_list); if (get_valid_blocks(sbi, segno, true)) diff --git a/fs/f2fs/hash.c b/fs/f2fs/hash.c index 5bc4dcd8fc03fbbb97ece6233c925634dc972fa8..8f7ee4362312b3398d79f1a36ad459f8134ee1aa 100644 --- a/fs/f2fs/hash.c +++ b/fs/f2fs/hash.c @@ -68,8 +68,9 @@ static void str2hashbuf(const unsigned char *msg, size_t len, *buf++ = pad; } -static f2fs_hash_t __f2fs_dentry_hash(const struct qstr *name_info, - struct fscrypt_name *fname) +static f2fs_hash_t __f2fs_dentry_hash(const struct inode *dir, + const struct qstr *name_info, + const struct fscrypt_name *fname) { __u32 hash; f2fs_hash_t f2fs_hash; @@ -79,12 +80,17 @@ static f2fs_hash_t __f2fs_dentry_hash(const struct qstr *name_info, size_t len = name_info->len; /* encrypted bigname case */ - if (fname && !fname->disk_name.name) + if (fname && fname->is_ciphertext_name) return cpu_to_le32(fname->hash); if (is_dot_dotdot(name_info)) return 0; + if (IS_CASEFOLDED(dir) && IS_ENCRYPTED(dir)) { + f2fs_hash = cpu_to_le32(fscrypt_fname_siphash(dir, name_info)); + return f2fs_hash; + } + /* Initialize the default seed for the hash checksum functions */ buf[0] = 0x67452301; buf[1] = 0xefcdab89; @@ -106,35 +112,38 @@ static f2fs_hash_t __f2fs_dentry_hash(const struct qstr *name_info, } f2fs_hash_t f2fs_dentry_hash(const struct inode *dir, - const struct qstr *name_info, struct fscrypt_name *fname) + const struct qstr *name_info, const struct fscrypt_name *fname) { #ifdef CONFIG_UNICODE struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb); - const struct unicode_map *um = sbi->s_encoding; + const struct unicode_map *um = dir->i_sb->s_encoding; int r, dlen; unsigned char *buff; struct qstr folded; + const struct qstr *name = fname ? fname->usr_fname : name_info; if (!name_info->len || !IS_CASEFOLDED(dir)) goto opaque_seq; + if (IS_ENCRYPTED(dir) && !fscrypt_has_encryption_key(dir)) + goto opaque_seq; + buff = f2fs_kzalloc(sbi, sizeof(char) * PATH_MAX, GFP_KERNEL); if (!buff) return -ENOMEM; - - dlen = utf8_casefold(um, name_info, buff, PATH_MAX); + dlen = utf8_casefold(um, name, buff, PATH_MAX); if (dlen < 0) { kvfree(buff); goto opaque_seq; } folded.name = buff; folded.len = dlen; - r = __f2fs_dentry_hash(&folded, fname); + r = __f2fs_dentry_hash(dir, &folded, fname); kvfree(buff); return r; opaque_seq: #endif - return __f2fs_dentry_hash(name_info, fname); + return __f2fs_dentry_hash(dir, name_info, fname); } diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index e98d53d5e4aae95d013687643fcbe5302cdd4b35..c68a32369f446e022c54df279aca21f0fee68458 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -386,7 +386,7 @@ static int f2fs_move_inline_dirents(struct inode *dir, struct page *ipage, struct f2fs_dentry_ptr src, dst; int err; - page = f2fs_grab_cache_page(dir->i_mapping, 0, false); + page = f2fs_grab_cache_page(dir->i_mapping, 0, true); if (!page) { f2fs_put_page(ipage, 1); return -ENOMEM; @@ -483,8 +483,8 @@ static int f2fs_add_inline_entries(struct inode *dir, void *inline_dentry) ino = le32_to_cpu(de->ino); fake_mode = f2fs_get_de_type(de) << S_SHIFT; - err = f2fs_add_regular_entry(dir, &new_name, NULL, NULL, - ino, fake_mode); + err = f2fs_add_regular_entry(dir, &new_name, NULL, + de->hash_code, NULL, ino, fake_mode); if (err) goto punch_dentry_pages; @@ -548,7 +548,7 @@ static int f2fs_move_rehashed_dirents(struct inode *dir, struct page *ipage, return err; } -static int f2fs_convert_inline_dir(struct inode *dir, struct page *ipage, +static int do_convert_inline_dir(struct inode *dir, struct page *ipage, void *inline_dentry) { if (!F2FS_I(dir)->i_dir_level) @@ -557,8 +557,46 @@ static int f2fs_convert_inline_dir(struct inode *dir, struct page *ipage, return f2fs_move_rehashed_dirents(dir, ipage, inline_dentry); } +int f2fs_try_convert_inline_dir(struct inode *dir, struct dentry *dentry) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(dir); + struct page *ipage; + struct fscrypt_name fname; + void *inline_dentry = NULL; + int err = 0; + + if (!f2fs_has_inline_dentry(dir)) + return 0; + + f2fs_lock_op(sbi); + + err = fscrypt_setup_filename(dir, &dentry->d_name, 0, &fname); + if (err) + goto out; + + ipage = f2fs_get_node_page(sbi, dir->i_ino); + if (IS_ERR(ipage)) { + err = PTR_ERR(ipage); + goto out; + } + + if (f2fs_has_enough_room(dir, ipage, &fname)) { + f2fs_put_page(ipage, 1); + goto out; + } + + inline_dentry = inline_data_addr(dir, ipage); + + err = do_convert_inline_dir(dir, ipage, inline_dentry); + if (!err) + f2fs_put_page(ipage, 1); +out: + f2fs_unlock_op(sbi); + return err; +} + int f2fs_add_inline_entry(struct inode *dir, const struct qstr *new_name, - const struct qstr *orig_name, + const struct fscrypt_name *fname, struct inode *inode, nid_t ino, umode_t mode) { struct f2fs_sb_info *sbi = F2FS_I_SB(dir); @@ -569,6 +607,7 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *new_name, struct f2fs_dentry_ptr d; int slots = GET_DENTRY_SLOTS(new_name->len); struct page *page = NULL; + const struct qstr *orig_name = fname->usr_fname; int err = 0; ipage = f2fs_get_node_page(sbi, dir->i_ino); @@ -580,7 +619,7 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *new_name, bit_pos = f2fs_room_for_filename(d.bitmap, slots, d.max); if (bit_pos >= d.max) { - err = f2fs_convert_inline_dir(dir, ipage, inline_dentry); + err = do_convert_inline_dir(dir, ipage, inline_dentry); if (err) return err; err = -EAGAIN; @@ -599,7 +638,7 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *new_name, f2fs_wait_on_page_writeback(ipage, NODE, true, true); - name_hash = f2fs_dentry_hash(dir, new_name, NULL); + name_hash = f2fs_dentry_hash(dir, new_name, fname); f2fs_update_dentry(ino, mode, &d, new_name, name_hash, bit_pos); set_page_dirty(ipage); diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 502bd491336a8200861232e9b3ba60ccc94965c3..78c3f1d70f1dca4430813f2108b8f8186eeb7b95 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -200,6 +200,7 @@ static bool sanity_check_inode(struct inode *inode, struct page *node_page) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_inode_info *fi = F2FS_I(inode); + struct f2fs_inode *ri = F2FS_INODE(node_page); unsigned long long iblocks; iblocks = le64_to_cpu(F2FS_INODE(node_page)->i_blocks); @@ -286,6 +287,19 @@ static bool sanity_check_inode(struct inode *inode, struct page *node_page) return false; } + if (f2fs_has_extra_attr(inode) && f2fs_sb_has_compression(sbi) && + fi->i_flags & F2FS_COMPR_FL && + F2FS_FITS_IN_INODE(ri, fi->i_extra_isize, + i_log_cluster_size)) { + if (ri->i_compress_algorithm >= COMPRESS_MAX) + return false; + if (le64_to_cpu(ri->i_compr_blocks) > inode->i_blocks) + return false; + if (ri->i_log_cluster_size < MIN_COMPRESS_LOG_SIZE || + ri->i_log_cluster_size > MAX_COMPRESS_LOG_SIZE) + return false; + } + return true; } @@ -407,6 +421,18 @@ static int do_read_inode(struct inode *inode) fi->i_crtime.tv_nsec = le32_to_cpu(ri->i_crtime_nsec); } + if (f2fs_has_extra_attr(inode) && f2fs_sb_has_compression(sbi) && + (fi->i_flags & F2FS_COMPR_FL)) { + if (F2FS_FITS_IN_INODE(ri, fi->i_extra_isize, + i_log_cluster_size)) { + fi->i_compr_blocks = le64_to_cpu(ri->i_compr_blocks); + fi->i_compress_algorithm = ri->i_compress_algorithm; + fi->i_log_cluster_size = ri->i_log_cluster_size; + fi->i_cluster_size = 1 << fi->i_log_cluster_size; + set_inode_flag(inode, FI_COMPRESSED_FILE); + } + } + F2FS_I(inode)->i_disk_time[0] = inode->i_atime; F2FS_I(inode)->i_disk_time[1] = inode->i_ctime; F2FS_I(inode)->i_disk_time[2] = inode->i_mtime; @@ -416,6 +442,8 @@ static int do_read_inode(struct inode *inode) stat_inc_inline_xattr(inode); stat_inc_inline_inode(inode); stat_inc_inline_dir(inode); + stat_inc_compr_inode(inode); + stat_add_compr_blocks(inode, F2FS_I(inode)->i_compr_blocks); return 0; } @@ -569,6 +597,17 @@ void f2fs_update_inode(struct inode *inode, struct page *node_page) ri->i_crtime_nsec = cpu_to_le32(F2FS_I(inode)->i_crtime.tv_nsec); } + + if (f2fs_sb_has_compression(F2FS_I_SB(inode)) && + F2FS_FITS_IN_INODE(ri, F2FS_I(inode)->i_extra_isize, + i_log_cluster_size)) { + ri->i_compr_blocks = + cpu_to_le64(F2FS_I(inode)->i_compr_blocks); + ri->i_compress_algorithm = + F2FS_I(inode)->i_compress_algorithm; + ri->i_log_cluster_size = + F2FS_I(inode)->i_log_cluster_size; + } } __set_inode_rdev(inode, ri); @@ -711,6 +750,8 @@ void f2fs_evict_inode(struct inode *inode) stat_dec_inline_xattr(inode); stat_dec_inline_dir(inode); stat_dec_inline_inode(inode); + stat_dec_compr_inode(inode); + stat_sub_compr_blocks(inode, F2FS_I(inode)->i_compr_blocks); if (likely(!f2fs_cp_error(sbi) && !is_sbi_flag_set(sbi, SBI_CP_DISABLED))) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index a1c507b0b4ac4b293b4e086675b2a3669f76d177..24d68eafffa404b8299bacf6c822462fe3e8652d 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -119,6 +119,13 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) if (F2FS_I(inode)->i_flags & F2FS_PROJINHERIT_FL) set_inode_flag(inode, FI_PROJ_INHERIT); + if (f2fs_sb_has_compression(sbi)) { + /* Inherit the compression flag in directory */ + if ((F2FS_I(dir)->i_flags & F2FS_COMPR_FL) && + f2fs_may_compress(inode)) + set_compress_context(inode); + } + f2fs_set_inode_flags(inode); trace_f2fs_new_inode(inode, 0); @@ -149,6 +156,9 @@ static inline int is_extension_exist(const unsigned char *s, const char *sub) size_t sublen = strlen(sub); int i; + if (sublen == 1 && *sub == '*') + return 1; + /* * filename format of multimedia file should be defined as: * "filename + '.' + extension + (optional: '.' + temp extension)". @@ -262,6 +272,45 @@ int f2fs_update_extension_list(struct f2fs_sb_info *sbi, const char *name, return 0; } +static void set_compress_inode(struct f2fs_sb_info *sbi, struct inode *inode, + const unsigned char *name) +{ + __u8 (*extlist)[F2FS_EXTENSION_LEN] = sbi->raw_super->extension_list; + unsigned char (*ext)[F2FS_EXTENSION_LEN]; + unsigned int ext_cnt = F2FS_OPTION(sbi).compress_ext_cnt; + int i, cold_count, hot_count; + + if (!f2fs_sb_has_compression(sbi) || + is_inode_flag_set(inode, FI_COMPRESSED_FILE) || + F2FS_I(inode)->i_flags & F2FS_NOCOMP_FL || + !f2fs_may_compress(inode)) + return; + + down_read(&sbi->sb_lock); + + cold_count = le32_to_cpu(sbi->raw_super->extension_count); + hot_count = sbi->raw_super->hot_ext_count; + + for (i = cold_count; i < cold_count + hot_count; i++) { + if (is_extension_exist(name, extlist[i])) { + up_read(&sbi->sb_lock); + return; + } + } + + up_read(&sbi->sb_lock); + + ext = F2FS_OPTION(sbi).extensions; + + for (i = 0; i < ext_cnt; i++) { + if (!is_extension_exist(name, ext[i])) + continue; + + set_compress_context(inode); + return; + } +} + static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool excl) { @@ -286,6 +335,8 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode, if (!test_opt(sbi, DISABLE_EXT_IDENTIFY)) set_file_temperature(sbi, inode, dentry->d_name.name); + set_compress_inode(sbi, inode, dentry->d_name.name); + inode->i_op = &f2fs_file_inode_operations; inode->i_fop = &f2fs_file_operations; inode->i_mapping->a_ops = &f2fs_dblock_aops; @@ -443,6 +494,7 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry, } err = fscrypt_prepare_lookup(dir, dentry, &fname); + generic_set_encrypted_ci_d_ops(dir, dentry); if (err == -ENOENT) goto out_splice; if (err) @@ -797,6 +849,7 @@ static int __f2fs_tmpfile(struct inode *dir, struct dentry *dentry, if (whiteout) { f2fs_i_links_write(inode, false); + inode->i_state |= I_LINKABLE; *whiteout = inode; } else { d_tmpfile(dentry, inode); @@ -849,12 +902,11 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *old_inode = d_inode(old_dentry); struct inode *new_inode = d_inode(new_dentry); struct inode *whiteout = NULL; - struct page *old_dir_page; + struct page *old_dir_page = NULL; struct page *old_page, *new_page = NULL; struct f2fs_dir_entry *old_dir_entry = NULL; struct f2fs_dir_entry *old_entry; struct f2fs_dir_entry *new_entry; - bool is_old_inline = f2fs_has_inline_dentry(old_dir); int err; if (unlikely(f2fs_cp_error(sbi))) @@ -867,6 +919,26 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, F2FS_I(old_dentry->d_inode)->i_projid))) return -EXDEV; + /* + * If new_inode is null, the below renaming flow will + * add a link in old_dir which can conver inline_dir. + * After then, if we failed to get the entry due to other + * reasons like ENOMEM, we had to remove the new entry. + * Instead of adding such the error handling routine, let's + * simply convert first here. + */ + if (old_dir == new_dir && !new_inode) { + err = f2fs_try_convert_inline_dir(old_dir, new_dentry); + if (err) + return err; + } + + if (flags & RENAME_WHITEOUT) { + err = f2fs_create_whiteout(old_dir, &whiteout); + if (err) + return err; + } + err = dquot_initialize(old_dir); if (err) goto out; @@ -898,17 +970,11 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, } } - if (flags & RENAME_WHITEOUT) { - err = f2fs_create_whiteout(old_dir, &whiteout); - if (err) - goto out_dir; - } - if (new_inode) { err = -ENOTEMPTY; if (old_dir_entry && !f2fs_empty_dir(new_inode)) - goto out_whiteout; + goto out_dir; err = -ENOENT; new_entry = f2fs_find_entry(new_dir, &new_dentry->d_name, @@ -916,7 +982,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, if (!new_entry) { if (IS_ERR(new_page)) err = PTR_ERR(new_page); - goto out_whiteout; + goto out_dir; } f2fs_balance_fs(sbi, true); @@ -928,6 +994,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, goto put_out_dir; f2fs_set_link(new_dir, new_entry, new_page, old_inode); + new_page = NULL; new_inode->i_ctime = current_time(new_inode); down_write(&F2FS_I(new_inode)->i_sem); @@ -948,33 +1015,11 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, err = f2fs_add_link(new_dentry, old_inode); if (err) { f2fs_unlock_op(sbi); - goto out_whiteout; + goto out_dir; } if (old_dir_entry) f2fs_i_links_write(new_dir, true); - - /* - * old entry and new entry can locate in the same inline - * dentry in inode, when attaching new entry in inline dentry, - * it could force inline dentry conversion, after that, - * old_entry and old_page will point to wrong address, in - * order to avoid this, let's do the check and update here. - */ - if (is_old_inline && !f2fs_has_inline_dentry(old_dir)) { - f2fs_put_page(old_page, 0); - old_page = NULL; - - old_entry = f2fs_find_entry(old_dir, - &old_dentry->d_name, &old_page); - if (!old_entry) { - err = -ENOENT; - if (IS_ERR(old_page)) - err = PTR_ERR(old_page); - f2fs_unlock_op(sbi); - goto out_whiteout; - } - } } down_write(&F2FS_I(old_inode)->i_sem); @@ -989,9 +1034,9 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, f2fs_mark_inode_dirty_sync(old_inode, false); f2fs_delete_entry(old_entry, old_page, old_dir, NULL); + old_page = NULL; if (whiteout) { - whiteout->i_state |= I_LINKABLE; set_inode_flag(whiteout, FI_INC_LINK); err = f2fs_add_link(old_dentry, whiteout); if (err) @@ -1025,17 +1070,15 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, put_out_dir: f2fs_unlock_op(sbi); - if (new_page) - f2fs_put_page(new_page, 0); -out_whiteout: - if (whiteout) - iput(whiteout); + f2fs_put_page(new_page, 0); out_dir: if (old_dir_entry) f2fs_put_page(old_dir_page, 0); out_old: f2fs_put_page(old_page, 0); out: + if (whiteout) + iput(whiteout); return err; } diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 9b4d197fae8e5962f6e983d15fa08d07732d7f43..1fb2ef09ff7040b6d0f077c7889e65b80025fe29 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1804,6 +1804,53 @@ static bool flush_dirty_inode(struct page *page) return true; } +int f2fs_flush_inline_data(struct f2fs_sb_info *sbi) +{ + pgoff_t index = 0; + struct pagevec pvec; + int nr_pages; + int ret = 0; + + pagevec_init(&pvec); + + while ((nr_pages = pagevec_lookup_tag(&pvec, + NODE_MAPPING(sbi), &index, PAGECACHE_TAG_DIRTY))) { + int i; + + for (i = 0; i < nr_pages; i++) { + struct page *page = pvec.pages[i]; + + if (!IS_DNODE(page)) + continue; + + lock_page(page); + + if (unlikely(page->mapping != NODE_MAPPING(sbi))) { +continue_unlock: + unlock_page(page); + continue; + } + + if (!PageDirty(page)) { + /* someone wrote it for us */ + goto continue_unlock; + } + + /* flush inline_data, if it's async context. */ + if (is_inline_node(page)) { + clear_inline_node(page); + unlock_page(page); + flush_inline_data(sbi, ino_of_node(page)); + continue; + } + unlock_page(page); + } + pagevec_release(&pvec); + cond_resched(); + } + return ret; +} + int f2fs_sync_node_pages(struct f2fs_sb_info *sbi, struct writeback_control *wbc, bool do_balance, enum iostat_type io_type) @@ -1867,8 +1914,8 @@ int f2fs_sync_node_pages(struct f2fs_sb_info *sbi, goto continue_unlock; } - /* flush inline_data */ - if (is_inline_node(page)) { + /* flush inline_data, if it's async context. */ + if (do_balance && is_inline_node(page)) { clear_inline_node(page); unlock_page(page); flush_inline_data(sbi, ino_of_node(page)); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 110f38005ed0be6e90733f549dc0357353e3d9da..469990ab1e629aff0aa5c751b08054736fd4bd28 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -334,7 +334,6 @@ void f2fs_drop_inmem_pages(struct inode *inode) } fi->i_gc_failures[GC_FAILURE_ATOMIC] = 0; - stat_dec_atomic_write(inode); spin_lock(&sbi->inode_lock[ATOMIC_FILE]); if (!list_empty(&fi->inmem_ilist)) @@ -505,7 +504,7 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need) * dir/node pages without enough free segments. */ if (has_not_enough_free_secs(sbi, 0, 0)) { - mutex_lock(&sbi->gc_mutex); + down_write(&sbi->gc_lock); f2fs_gc(sbi, false, false, NULL_SEGNO); } } @@ -2232,7 +2231,7 @@ void f2fs_invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr) struct sit_info *sit_i = SIT_I(sbi); f2fs_bug_on(sbi, addr == NULL_ADDR); - if (addr == NEW_ADDR) + if (addr == NEW_ADDR || addr == COMPRESS_ADDR) return; invalidate_mapping_pages(META_MAPPING(sbi), addr, addr); @@ -2868,9 +2867,9 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) if (sbi->discard_blks == 0) goto out; - mutex_lock(&sbi->gc_mutex); + down_write(&sbi->gc_lock); err = f2fs_write_checkpoint(sbi, &cpc); - mutex_unlock(&sbi->gc_mutex); + up_write(&sbi->gc_lock); if (err) goto out; @@ -3043,7 +3042,8 @@ static int __get_segment_type_6(struct f2fs_io_info *fio) if (fio->type == DATA) { struct inode *inode = fio->page->mapping->host; - if (is_cold_data(fio->page) || file_is_cold(inode)) + if (is_cold_data(fio->page) || file_is_cold(inode) || + f2fs_compressed_file(inode)) return CURSEG_COLD_DATA; if (file_is_hot(inode) || is_inode_flag_set(inode, FI_HOT_DATA) || @@ -3296,7 +3296,7 @@ int f2fs_inplace_write_data(struct f2fs_io_info *fio) stat_inc_inplace_blocks(fio->sbi); - if (fio->bio) + if (fio->bio && !(SM_I(sbi)->ipu_policy & (1 << F2FS_IPU_NOCACHE))) err = f2fs_merge_page_bio(fio); else err = f2fs_submit_page_bio(fio); diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index a95467b202ea2040800f4d87209f317757f62f6a..459dc3901a574c8a1cc66ef265e5c13dea58bb8e 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -200,18 +200,6 @@ struct segment_allocation { void (*allocate_segment)(struct f2fs_sb_info *, int, bool); }; -/* - * this value is set in page as a private data which indicate that - * the page is atomically written, and it is in inmem_pages list. - */ -#define ATOMIC_WRITTEN_PAGE ((unsigned long)-1) -#define DUMMY_WRITTEN_PAGE ((unsigned long)-2) - -#define IS_ATOMIC_WRITTEN_PAGE(page) \ - (page_private(page) == (unsigned long)ATOMIC_WRITTEN_PAGE) -#define IS_DUMMY_WRITTEN_PAGE(page) \ - (page_private(page) == (unsigned long)DUMMY_WRITTEN_PAGE) - #define MAX_SKIP_GC_COUNT 16 struct inmem_pages { @@ -619,8 +607,10 @@ static inline int utilization(struct f2fs_sb_info *sbi) * threashold, * F2FS_IPU_FSYNC - activated in fsync path only for high performance flash * storages. IPU will be triggered only if the # of dirty - * pages over min_fsync_blocks. - * F2FS_IPUT_DISABLE - disable IPU. (=default option) + * pages over min_fsync_blocks. (=default option) + * F2FS_IPU_ASYNC - do IPU given by asynchronous write requests. + * F2FS_IPU_NOCACHE - disable IPU bio cache. + * F2FS_IPUT_DISABLE - disable IPU. (=default option in LFS mode) */ #define DEF_MIN_IPU_UTIL 70 #define DEF_MIN_FSYNC_BLOCKS 8 @@ -635,6 +625,7 @@ enum { F2FS_IPU_SSR_UTIL, F2FS_IPU_FSYNC, F2FS_IPU_ASYNC, + F2FS_IPU_NOCACHE, }; static inline unsigned int curseg_segno(struct f2fs_sb_info *sbi, diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 9be6d2c9243aa04afd0ff8bef491006184c38d41..ff8f15623a5fa474b795ca0ecf0ccbb4bdc5e02a 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -142,6 +142,9 @@ enum { Opt_checkpoint_disable_cap, Opt_checkpoint_disable_cap_perc, Opt_checkpoint_enable, + Opt_compress_algorithm, + Opt_compress_log_size, + Opt_compress_extension, Opt_err, }; @@ -205,6 +208,9 @@ static match_table_t f2fs_tokens = { {Opt_checkpoint_disable_cap, "checkpoint=disable:%u"}, {Opt_checkpoint_disable_cap_perc, "checkpoint=disable:%u%%"}, {Opt_checkpoint_enable, "checkpoint=enable"}, + {Opt_compress_algorithm, "compress_algorithm=%s"}, + {Opt_compress_log_size, "compress_log_size=%u"}, + {Opt_compress_extension, "compress_extension=%s"}, {Opt_err, NULL}, }; @@ -393,8 +399,9 @@ static int parse_options(struct super_block *sb, char *options) { struct f2fs_sb_info *sbi = F2FS_SB(sb); substring_t args[MAX_OPT_ARGS]; + unsigned char (*ext)[F2FS_EXTENSION_LEN]; char *p, *name; - int arg = 0; + int arg = 0, ext_cnt; kuid_t uid; kgid_t gid; #ifdef CONFIG_QUOTA @@ -819,6 +826,66 @@ static int parse_options(struct super_block *sb, char *options) case Opt_checkpoint_enable: clear_opt(sbi, DISABLE_CHECKPOINT); break; + case Opt_compress_algorithm: + if (!f2fs_sb_has_compression(sbi)) { + f2fs_err(sbi, "Compression feature if off"); + return -EINVAL; + } + name = match_strdup(&args[0]); + if (!name) + return -ENOMEM; + if (strlen(name) == 3 && !strcmp(name, "lzo")) { + F2FS_OPTION(sbi).compress_algorithm = + COMPRESS_LZO; + } else if (strlen(name) == 3 && + !strcmp(name, "lz4")) { + F2FS_OPTION(sbi).compress_algorithm = + COMPRESS_LZ4; + } else { + kfree(name); + return -EINVAL; + } + kfree(name); + break; + case Opt_compress_log_size: + if (!f2fs_sb_has_compression(sbi)) { + f2fs_err(sbi, "Compression feature is off"); + return -EINVAL; + } + if (args->from && match_int(args, &arg)) + return -EINVAL; + if (arg < MIN_COMPRESS_LOG_SIZE || + arg > MAX_COMPRESS_LOG_SIZE) { + f2fs_err(sbi, + "Compress cluster log size is out of range"); + return -EINVAL; + } + F2FS_OPTION(sbi).compress_log_size = arg; + break; + case Opt_compress_extension: + if (!f2fs_sb_has_compression(sbi)) { + f2fs_err(sbi, "Compression feature is off"); + return -EINVAL; + } + name = match_strdup(&args[0]); + if (!name) + return -ENOMEM; + + ext = F2FS_OPTION(sbi).extensions; + ext_cnt = F2FS_OPTION(sbi).compress_ext_cnt; + + if (strlen(name) >= F2FS_EXTENSION_LEN || + ext_cnt >= COMPRESS_EXT_NUM) { + f2fs_err(sbi, + "invalid extension length/number"); + kfree(name); + return -EINVAL; + } + + strcpy(ext[ext_cnt], name); + F2FS_OPTION(sbi).compress_ext_cnt++; + kfree(name); + break; default: f2fs_err(sbi, "Unrecognized mount option \"%s\" or missing value", p); @@ -1142,6 +1209,8 @@ static void f2fs_put_super(struct super_block *sb) f2fs_destroy_node_manager(sbi); f2fs_destroy_segment_manager(sbi); + f2fs_destroy_post_read_wq(sbi); + kvfree(sbi->ckpt); f2fs_unregister_sysfs(sbi); @@ -1161,7 +1230,7 @@ static void f2fs_put_super(struct super_block *sb) for (i = 0; i < NR_PAGE_TYPE; i++) kvfree(sbi->write_io[i]); #ifdef CONFIG_UNICODE - utf8_unload(sbi->s_encoding); + utf8_unload(sb->s_encoding); #endif kvfree(sbi); } @@ -1186,9 +1255,9 @@ int f2fs_sync_fs(struct super_block *sb, int sync) cpc.reason = __get_cp_reason(sbi); - mutex_lock(&sbi->gc_mutex); + down_write(&sbi->gc_lock); err = f2fs_write_checkpoint(sbi, &cpc); - mutex_unlock(&sbi->gc_mutex); + up_write(&sbi->gc_lock); } f2fs_trace_ios(NULL, 1); @@ -1230,12 +1299,10 @@ static int f2fs_statfs_project(struct super_block *sb, return PTR_ERR(dquot); spin_lock(&dquot->dq_dqb_lock); - limit = 0; - if (dquot->dq_dqb.dqb_bsoftlimit) - limit = dquot->dq_dqb.dqb_bsoftlimit; - if (dquot->dq_dqb.dqb_bhardlimit && - (!limit || dquot->dq_dqb.dqb_bhardlimit < limit)) - limit = dquot->dq_dqb.dqb_bhardlimit; + limit = min_not_zero(dquot->dq_dqb.dqb_bsoftlimit, + dquot->dq_dqb.dqb_bhardlimit); + if (limit) + limit >>= sb->s_blocksize_bits; if (limit && buf->f_blocks > limit) { curblock = dquot->dq_dqb.dqb_curspace >> sb->s_blocksize_bits; @@ -1245,12 +1312,8 @@ static int f2fs_statfs_project(struct super_block *sb, (buf->f_blocks - curblock) : 0; } - limit = 0; - if (dquot->dq_dqb.dqb_isoftlimit) - limit = dquot->dq_dqb.dqb_isoftlimit; - if (dquot->dq_dqb.dqb_ihardlimit && - (!limit || dquot->dq_dqb.dqb_ihardlimit < limit)) - limit = dquot->dq_dqb.dqb_ihardlimit; + limit = min_not_zero(dquot->dq_dqb.dqb_isoftlimit, + dquot->dq_dqb.dqb_ihardlimit); if (limit && buf->f_files > limit) { buf->f_files = limit; @@ -1357,6 +1420,35 @@ static inline void f2fs_show_quota_options(struct seq_file *seq, #endif } +static inline void f2fs_show_compress_options(struct seq_file *seq, + struct super_block *sb) +{ + struct f2fs_sb_info *sbi = F2FS_SB(sb); + char *algtype = ""; + int i; + + if (!f2fs_sb_has_compression(sbi)) + return; + + switch (F2FS_OPTION(sbi).compress_algorithm) { + case COMPRESS_LZO: + algtype = "lzo"; + break; + case COMPRESS_LZ4: + algtype = "lz4"; + break; + } + seq_printf(seq, ",compress_algorithm=%s", algtype); + + seq_printf(seq, ",compress_log_size=%u", + F2FS_OPTION(sbi).compress_log_size); + + for (i = 0; i < F2FS_OPTION(sbi).compress_ext_cnt; i++) { + seq_printf(seq, ",compress_extension=%s", + F2FS_OPTION(sbi).extensions[i]); + } +} + static int f2fs_show_options(struct seq_file *seq, struct dentry *root) { struct f2fs_sb_info *sbi = F2FS_SB(root->d_sb); @@ -1481,6 +1573,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) seq_printf(seq, ",fsync_mode=%s", "strict"); else if (F2FS_OPTION(sbi).fsync_mode == FSYNC_MODE_NOBARRIER) seq_printf(seq, ",fsync_mode=%s", "nobarrier"); + + f2fs_show_compress_options(seq, sbi->sb); return 0; } @@ -1498,6 +1592,9 @@ static void default_options(struct f2fs_sb_info *sbi) #endif F2FS_OPTION(sbi).s_resuid = make_kuid(&init_user_ns, F2FS_DEF_RESUID); F2FS_OPTION(sbi).s_resgid = make_kgid(&init_user_ns, F2FS_DEF_RESGID); + F2FS_OPTION(sbi).compress_algorithm = COMPRESS_LZO; + F2FS_OPTION(sbi).compress_log_size = MIN_COMPRESS_LOG_SIZE; + F2FS_OPTION(sbi).compress_ext_cnt = 0; set_opt(sbi, BG_GC); set_opt(sbi, INLINE_XATTR); @@ -1546,7 +1643,7 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi) f2fs_update_time(sbi, DISABLE_TIME); while (!f2fs_time_over(sbi, DISABLE_TIME)) { - mutex_lock(&sbi->gc_mutex); + down_write(&sbi->gc_lock); err = f2fs_gc(sbi, true, false, NULL_SEGNO); if (err == -ENODATA) { err = 0; @@ -1568,7 +1665,7 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi) goto restore_flag; } - mutex_lock(&sbi->gc_mutex); + down_write(&sbi->gc_lock); cpc.reason = CP_PAUSE; set_sbi_flag(sbi, SBI_CP_DISABLED); err = f2fs_write_checkpoint(sbi, &cpc); @@ -1580,7 +1677,7 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi) spin_unlock(&sbi->stat_lock); out_unlock: - mutex_unlock(&sbi->gc_mutex); + up_write(&sbi->gc_lock); restore_flag: sbi->sb->s_flags = s_flags; /* Restore MS_RDONLY status */ return err; @@ -1588,12 +1685,12 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi) static void f2fs_enable_checkpoint(struct f2fs_sb_info *sbi) { - mutex_lock(&sbi->gc_mutex); + down_write(&sbi->gc_lock); f2fs_dirty_to_prefree(sbi); clear_sbi_flag(sbi, SBI_CP_DISABLED); set_sbi_flag(sbi, SBI_IS_DIRTY); - mutex_unlock(&sbi->gc_mutex); + up_write(&sbi->gc_lock); f2fs_sync_fs(sbi->sb, 1); } @@ -2180,7 +2277,7 @@ static int f2fs_dquot_commit(struct dquot *dquot) struct f2fs_sb_info *sbi = F2FS_SB(dquot->dq_sb); int ret; - down_read(&sbi->quota_sem); + down_read_nested(&sbi->quota_sem, SINGLE_DEPTH_NESTING); ret = dquot_commit(dquot); if (ret < 0) set_sbi_flag(sbi, SBI_QUOTA_NEED_REPAIR); @@ -2204,13 +2301,10 @@ static int f2fs_dquot_acquire(struct dquot *dquot) static int f2fs_dquot_release(struct dquot *dquot) { struct f2fs_sb_info *sbi = F2FS_SB(dquot->dq_sb); - int ret; + int ret = dquot_release(dquot); - down_read(&sbi->quota_sem); - ret = dquot_release(dquot); if (ret < 0) set_sbi_flag(sbi, SBI_QUOTA_NEED_REPAIR); - up_read(&sbi->quota_sem); return ret; } @@ -2218,29 +2312,22 @@ static int f2fs_dquot_mark_dquot_dirty(struct dquot *dquot) { struct super_block *sb = dquot->dq_sb; struct f2fs_sb_info *sbi = F2FS_SB(sb); - int ret; - - down_read(&sbi->quota_sem); - ret = dquot_mark_dquot_dirty(dquot); + int ret = dquot_mark_dquot_dirty(dquot); /* if we are using journalled quota */ if (is_journalled_quota(sbi)) set_sbi_flag(sbi, SBI_QUOTA_NEED_FLUSH); - up_read(&sbi->quota_sem); return ret; } static int f2fs_dquot_commit_info(struct super_block *sb, int type) { struct f2fs_sb_info *sbi = F2FS_SB(sb); - int ret; + int ret = dquot_commit_info(sb, type); - down_read(&sbi->quota_sem); - ret = dquot_commit_info(sb, type); if (ret < 0) set_sbi_flag(sbi, SBI_QUOTA_NEED_REPAIR); - up_read(&sbi->quota_sem); return ret; } @@ -3212,17 +3299,11 @@ static int f2fs_scan_devices(struct f2fs_sb_info *sbi) static int f2fs_setup_casefold(struct f2fs_sb_info *sbi) { #ifdef CONFIG_UNICODE - if (f2fs_sb_has_casefold(sbi) && !sbi->s_encoding) { + if (f2fs_sb_has_casefold(sbi) && !sbi->sb->s_encoding) { const struct f2fs_sb_encodings *encoding_info; struct unicode_map *encoding; __u16 encoding_flags; - if (f2fs_sb_has_encrypt(sbi)) { - f2fs_err(sbi, - "Can't mount with encoding and encryption"); - return -EINVAL; - } - if (f2fs_sb_read_encoding(sbi->raw_super, &encoding_info, &encoding_flags)) { f2fs_err(sbi, @@ -3243,9 +3324,8 @@ static int f2fs_setup_casefold(struct f2fs_sb_info *sbi) "%s-%s with flags 0x%hx", encoding_info->name, encoding_info->version?:"\b", encoding_flags); - sbi->s_encoding = encoding; - sbi->s_encoding_flags = encoding_flags; - sbi->sb->s_d_op = &f2fs_dentry_ops; + sbi->sb->s_encoding = encoding; + sbi->sb->s_encoding_flags = encoding_flags; } #else if (f2fs_sb_has_casefold(sbi)) { @@ -3387,7 +3467,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) /* init f2fs-specific super block info */ sbi->valid_super_block = valid_super_block; - mutex_init(&sbi->gc_mutex); + init_rwsem(&sbi->gc_lock); mutex_init(&sbi->writepages); mutex_init(&sbi->cp_mutex); mutex_init(&sbi->resize_mutex); @@ -3476,6 +3556,12 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) goto free_devices; } + err = f2fs_init_post_read_wq(sbi); + if (err) { + f2fs_err(sbi, "Failed to initialize post read workqueue"); + goto free_devices; + } + sbi->total_valid_node_count = le32_to_cpu(sbi->ckpt->valid_node_count); percpu_counter_set(&sbi->total_valid_inode_count, @@ -3697,6 +3783,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) f2fs_destroy_node_manager(sbi); free_sm: f2fs_destroy_segment_manager(sbi); + f2fs_destroy_post_read_wq(sbi); free_devices: destroy_device_list(sbi); kvfree(sbi->ckpt); @@ -3713,7 +3800,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) kvfree(sbi->write_io[i]); #ifdef CONFIG_UNICODE - utf8_unload(sbi->s_encoding); + utf8_unload(sb->s_encoding); #endif free_options: #ifdef CONFIG_QUOTA @@ -3838,8 +3925,12 @@ static int __init init_f2fs_fs(void) err = f2fs_init_bio_entry_cache(); if (err) goto free_post_read; + err = f2fs_init_bioset(); + if (err) + goto free_bio_enrty_cache; return 0; - +free_bio_enrty_cache: + f2fs_destroy_bio_entry_cache(); free_post_read: f2fs_destroy_post_read_processing(); free_root_stats: @@ -3865,6 +3956,7 @@ static int __init init_f2fs_fs(void) static void __exit exit_f2fs_fs(void) { + f2fs_destroy_bioset(); f2fs_destroy_bio_entry_cache(); f2fs_destroy_post_read_processing(); f2fs_destroy_root_stats(); diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 62fab5f45ac3344f33a4262e4a0d9bcff788db29..a97b37c16af4af29a247aa413c1c88a2b27d6473 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -25,6 +25,9 @@ enum { DCC_INFO, /* struct discard_cmd_control */ NM_INFO, /* struct f2fs_nm_info */ F2FS_SBI, /* struct f2fs_sb_info */ +#ifdef CONFIG_F2FS_STAT_FS + STAT_INFO, /* struct f2fs_stat_info */ +#endif #ifdef CONFIG_F2FS_FAULT_INJECTION FAULT_INFO_RATE, /* struct f2fs_fault_info */ FAULT_INFO_TYPE, /* struct f2fs_fault_info */ @@ -42,6 +45,9 @@ struct f2fs_attr { int id; }; +static ssize_t f2fs_sbi_show(struct f2fs_attr *a, + struct f2fs_sb_info *sbi, char *buf); + static unsigned char *__struct_ptr(struct f2fs_sb_info *sbi, int struct_type) { if (struct_type == GC_THREAD) @@ -58,6 +64,10 @@ static unsigned char *__struct_ptr(struct f2fs_sb_info *sbi, int struct_type) else if (struct_type == FAULT_INFO_RATE || struct_type == FAULT_INFO_TYPE) return (unsigned char *)&F2FS_OPTION(sbi).fault_info; +#endif +#ifdef CONFIG_F2FS_STAT_FS + else if (struct_type == STAT_INFO) + return (unsigned char *)F2FS_STAT(sbi); #endif return NULL; } @@ -65,35 +75,15 @@ static unsigned char *__struct_ptr(struct f2fs_sb_info *sbi, int struct_type) static ssize_t dirty_segments_show(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf) { - return snprintf(buf, PAGE_SIZE, "%llu\n", - (unsigned long long)(dirty_segments(sbi))); + return sprintf(buf, "%llu\n", + (unsigned long long)(dirty_segments(sbi))); } -static ssize_t unusable_show(struct f2fs_attr *a, +static ssize_t free_segments_show(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf) { - block_t unusable; - - if (test_opt(sbi, DISABLE_CHECKPOINT)) - unusable = sbi->unusable_block_count; - else - unusable = f2fs_get_unusable_blocks(sbi); - return snprintf(buf, PAGE_SIZE, "%llu\n", - (unsigned long long)unusable); -} - -static ssize_t encoding_show(struct f2fs_attr *a, - struct f2fs_sb_info *sbi, char *buf) -{ -#ifdef CONFIG_UNICODE - if (f2fs_sb_has_casefold(sbi)) - return snprintf(buf, PAGE_SIZE, "%s (%d.%d.%d)\n", - sbi->s_encoding->charset, - (sbi->s_encoding->version >> 16) & 0xff, - (sbi->s_encoding->version >> 8) & 0xff, - sbi->s_encoding->version & 0xff); -#endif - return snprintf(buf, PAGE_SIZE, "(none)"); + return sprintf(buf, "%llu\n", + (unsigned long long)(free_segments(sbi))); } static ssize_t lifetime_write_kbytes_show(struct f2fs_attr *a, @@ -102,10 +92,10 @@ static ssize_t lifetime_write_kbytes_show(struct f2fs_attr *a, struct super_block *sb = sbi->sb; if (!sb->s_bdev->bd_part) - return snprintf(buf, PAGE_SIZE, "0\n"); + return sprintf(buf, "0\n"); - return snprintf(buf, PAGE_SIZE, "%llu\n", - (unsigned long long)(sbi->kbytes_written + + return sprintf(buf, "%llu\n", + (unsigned long long)(sbi->kbytes_written + BD_PART_WRITTEN(sbi))); } @@ -116,7 +106,7 @@ static ssize_t features_show(struct f2fs_attr *a, int len = 0; if (!sb->s_bdev->bd_part) - return snprintf(buf, PAGE_SIZE, "0\n"); + return sprintf(buf, "0\n"); if (f2fs_sb_has_encrypt(sbi)) len += snprintf(buf, PAGE_SIZE - len, "%s", @@ -154,6 +144,9 @@ static ssize_t features_show(struct f2fs_attr *a, if (f2fs_sb_has_casefold(sbi)) len += snprintf(buf + len, PAGE_SIZE - len, "%s%s", len ? ", " : "", "casefold"); + if (f2fs_sb_has_compression(sbi)) + len += snprintf(buf + len, PAGE_SIZE - len, "%s%s", + len ? ", " : "", "compression"); len += snprintf(buf + len, PAGE_SIZE - len, "%s%s", len ? ", " : "", "pin_file"); len += snprintf(buf + len, PAGE_SIZE - len, "\n"); @@ -163,8 +156,67 @@ static ssize_t features_show(struct f2fs_attr *a, static ssize_t current_reserved_blocks_show(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf) { - return snprintf(buf, PAGE_SIZE, "%u\n", sbi->current_reserved_blocks); + return sprintf(buf, "%u\n", sbi->current_reserved_blocks); +} + +static ssize_t unusable_show(struct f2fs_attr *a, + struct f2fs_sb_info *sbi, char *buf) +{ + block_t unusable; + + if (test_opt(sbi, DISABLE_CHECKPOINT)) + unusable = sbi->unusable_block_count; + else + unusable = f2fs_get_unusable_blocks(sbi); + return sprintf(buf, "%llu\n", (unsigned long long)unusable); +} + +static ssize_t encoding_show(struct f2fs_attr *a, + struct f2fs_sb_info *sbi, char *buf) +{ +#ifdef CONFIG_UNICODE + struct super_block *sb = sbi->sb; + + if (f2fs_sb_has_casefold(sbi)) + return snprintf(buf, PAGE_SIZE, "%s (%d.%d.%d)\n", + sb->s_encoding->charset, + (sb->s_encoding->version >> 16) & 0xff, + (sb->s_encoding->version >> 8) & 0xff, + sb->s_encoding->version & 0xff); +#endif + return sprintf(buf, "(none)"); +} + +#ifdef CONFIG_F2FS_STAT_FS +static ssize_t moved_blocks_foreground_show(struct f2fs_attr *a, + struct f2fs_sb_info *sbi, char *buf) +{ + struct f2fs_stat_info *si = F2FS_STAT(sbi); + + return sprintf(buf, "%llu\n", + (unsigned long long)(si->tot_blks - + (si->bg_data_blks + si->bg_node_blks))); +} + +static ssize_t moved_blocks_background_show(struct f2fs_attr *a, + struct f2fs_sb_info *sbi, char *buf) +{ + struct f2fs_stat_info *si = F2FS_STAT(sbi); + + return sprintf(buf, "%llu\n", + (unsigned long long)(si->bg_data_blks + si->bg_node_blks)); +} + +static ssize_t avg_vblocks_show(struct f2fs_attr *a, + struct f2fs_sb_info *sbi, char *buf) +{ + struct f2fs_stat_info *si = F2FS_STAT(sbi); + + si->dirty_count = dirty_segments(sbi); + f2fs_update_sit_info(sbi); + return sprintf(buf, "%llu\n", (unsigned long long)(si->avg_vblocks)); } +#endif static ssize_t f2fs_sbi_show(struct f2fs_attr *a, struct f2fs_sb_info *sbi, char *buf) @@ -199,7 +251,7 @@ static ssize_t f2fs_sbi_show(struct f2fs_attr *a, ui = (unsigned int *)(ptr + a->offset); - return snprintf(buf, PAGE_SIZE, "%u\n", *ui); + return sprintf(buf, "%u\n", *ui); } static ssize_t __sbi_store(struct f2fs_attr *a, @@ -389,6 +441,7 @@ enum feat_id { FEAT_VERITY, FEAT_SB_CHECKSUM, FEAT_CASEFOLD, + FEAT_COMPRESSION, }; static ssize_t f2fs_feature_show(struct f2fs_attr *a, @@ -408,7 +461,8 @@ static ssize_t f2fs_feature_show(struct f2fs_attr *a, case FEAT_VERITY: case FEAT_SB_CHECKSUM: case FEAT_CASEFOLD: - return snprintf(buf, PAGE_SIZE, "supported\n"); + case FEAT_COMPRESSION: + return sprintf(buf, "supported\n"); } return 0; } @@ -437,6 +491,14 @@ static struct f2fs_attr f2fs_attr_##_name = { \ .id = _id, \ } +#define F2FS_STAT_ATTR(_struct_type, _struct_name, _name, _elname) \ +static struct f2fs_attr f2fs_attr_##_name = { \ + .attr = {.name = __stringify(_name), .mode = 0444 }, \ + .show = f2fs_sbi_show, \ + .struct_type = _struct_type, \ + .offset = offsetof(struct _struct_name, _elname), \ +} + F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_urgent_sleep_time, urgent_sleep_time); F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_min_sleep_time, min_sleep_time); @@ -478,11 +540,21 @@ F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate); F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type); #endif F2FS_GENERAL_RO_ATTR(dirty_segments); +F2FS_GENERAL_RO_ATTR(free_segments); F2FS_GENERAL_RO_ATTR(lifetime_write_kbytes); F2FS_GENERAL_RO_ATTR(features); F2FS_GENERAL_RO_ATTR(current_reserved_blocks); F2FS_GENERAL_RO_ATTR(unusable); F2FS_GENERAL_RO_ATTR(encoding); +#ifdef CONFIG_F2FS_STAT_FS +F2FS_STAT_ATTR(STAT_INFO, f2fs_stat_info, cp_foreground_calls, cp_count); +F2FS_STAT_ATTR(STAT_INFO, f2fs_stat_info, cp_background_calls, bg_cp_count); +F2FS_STAT_ATTR(STAT_INFO, f2fs_stat_info, gc_foreground_calls, call_count); +F2FS_STAT_ATTR(STAT_INFO, f2fs_stat_info, gc_background_calls, bg_gc); +F2FS_GENERAL_RO_ATTR(moved_blocks_background); +F2FS_GENERAL_RO_ATTR(moved_blocks_foreground); +F2FS_GENERAL_RO_ATTR(avg_vblocks); +#endif #ifdef CONFIG_FS_ENCRYPTION F2FS_FEATURE_RO_ATTR(encryption, FEAT_CRYPTO); @@ -503,6 +575,7 @@ F2FS_FEATURE_RO_ATTR(verity, FEAT_VERITY); #endif F2FS_FEATURE_RO_ATTR(sb_checksum, FEAT_SB_CHECKSUM); F2FS_FEATURE_RO_ATTR(casefold, FEAT_CASEFOLD); +F2FS_FEATURE_RO_ATTR(compression, FEAT_COMPRESSION); #define ATTR_LIST(name) (&f2fs_attr_##name.attr) static struct attribute *f2fs_attrs[] = { @@ -543,12 +616,22 @@ static struct attribute *f2fs_attrs[] = { ATTR_LIST(inject_type), #endif ATTR_LIST(dirty_segments), + ATTR_LIST(free_segments), ATTR_LIST(unusable), ATTR_LIST(lifetime_write_kbytes), ATTR_LIST(features), ATTR_LIST(reserved_blocks), ATTR_LIST(current_reserved_blocks), ATTR_LIST(encoding), +#ifdef CONFIG_F2FS_STAT_FS + ATTR_LIST(cp_foreground_calls), + ATTR_LIST(cp_background_calls), + ATTR_LIST(gc_foreground_calls), + ATTR_LIST(gc_background_calls), + ATTR_LIST(moved_blocks_foreground), + ATTR_LIST(moved_blocks_background), + ATTR_LIST(avg_vblocks), +#endif NULL, }; @@ -572,6 +655,7 @@ static struct attribute *f2fs_feat_attrs[] = { #endif ATTR_LIST(sb_checksum), ATTR_LIST(casefold), + ATTR_LIST(compression), NULL, }; @@ -731,10 +815,12 @@ int __init f2fs_init_sysfs(void) ret = kobject_init_and_add(&f2fs_feat, &f2fs_feat_ktype, NULL, "features"); - if (ret) + if (ret) { + kobject_put(&f2fs_feat); kset_unregister(&f2fs_kset); - else + } else { f2fs_proc_root = proc_mkdir("fs/f2fs", NULL); + } return ret; } @@ -755,8 +841,11 @@ int f2fs_register_sysfs(struct f2fs_sb_info *sbi) init_completion(&sbi->s_kobj_unregister); err = kobject_init_and_add(&sbi->s_kobj, &f2fs_sb_ktype, NULL, "%s", sb->s_id); - if (err) + if (err) { + kobject_put(&sbi->s_kobj); + wait_for_completion(&sbi->s_kobj_unregister); return err; + } if (f2fs_proc_root) sbi->s_proc = proc_mkdir(sb->s_id, f2fs_proc_root); @@ -784,4 +873,5 @@ void f2fs_unregister_sysfs(struct f2fs_sb_info *sbi) remove_proc_entry(sbi->sb->s_id, f2fs_proc_root); } kobject_del(&sbi->s_kobj); + kobject_put(&sbi->s_kobj); } diff --git a/fs/f2fs/verity.c b/fs/f2fs/verity.c index a401ef72bc82136b31dfdc8eca4cdcf5bc445dd1..077ac835ec53bedf23c50bb78ebbbe20b4b6778d 100644 --- a/fs/f2fs/verity.c +++ b/fs/f2fs/verity.c @@ -222,12 +222,57 @@ static int f2fs_get_verity_descriptor(struct inode *inode, void *buf, return size; } +/* + * Prefetch some pages from the file's Merkle tree. + * + * This is basically a stripped-down version of __do_page_cache_readahead() + * which works on pages past i_size. + */ +static void f2fs_merkle_tree_readahead(struct address_space *mapping, + pgoff_t start_index, unsigned long count) +{ + LIST_HEAD(pages); + unsigned int nr_pages = 0; + struct page *page; + pgoff_t index; + struct blk_plug plug; + + for (index = start_index; index < start_index + count; index++) { + rcu_read_lock(); + page = radix_tree_lookup(&mapping->i_pages, index); + rcu_read_unlock(); + if (!page || radix_tree_exceptional_entry(page)) { + page = __page_cache_alloc(readahead_gfp_mask(mapping)); + if (!page) + break; + page->index = index; + list_add(&page->lru, &pages); + nr_pages++; + } + } + blk_start_plug(&plug); + f2fs_mpage_readpages(mapping, &pages, NULL, nr_pages, true); + blk_finish_plug(&plug); +} + static struct page *f2fs_read_merkle_tree_page(struct inode *inode, - pgoff_t index) + pgoff_t index, + unsigned long num_ra_pages) { + struct page *page; + index += f2fs_verity_metadata_pos(inode) >> PAGE_SHIFT; - return read_mapping_page(inode->i_mapping, index, NULL); + page = find_get_page_flags(inode->i_mapping, index, FGP_ACCESSED); + if (!page || !PageUptodate(page)) { + if (page) + put_page(page); + else if (num_ra_pages > 1) + f2fs_merkle_tree_readahead(inode->i_mapping, index, + num_ra_pages); + page = read_mapping_page(inode->i_mapping, index, NULL); + } + return page; } static int f2fs_write_merkle_tree_block(struct inode *inode, const void *buf, diff --git a/fs/fat/inode.c b/fs/fat/inode.c index d6b81e31f9f5d827be92ffad4cf240fa32e5f767..70d37a5fd72c6a1ad3ad0e0bc31d743a4d949d0a 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -743,6 +743,13 @@ static struct inode *fat_alloc_inode(struct super_block *sb) return NULL; init_rwsem(&ei->truncate_lock); + /* Zeroing to allow iput() even if partial initialized inode. */ + ei->mmu_private = 0; + ei->i_start = 0; + ei->i_logstart = 0; + ei->i_attrs = 0; + ei->i_pos = 0; + return &ei->vfs_inode; } @@ -1373,16 +1380,6 @@ static int parse_options(struct super_block *sb, char *options, int is_vfat, return 0; } -static void fat_dummy_inode_init(struct inode *inode) -{ - /* Initialize this dummy inode to work as no-op. */ - MSDOS_I(inode)->mmu_private = 0; - MSDOS_I(inode)->i_start = 0; - MSDOS_I(inode)->i_logstart = 0; - MSDOS_I(inode)->i_attrs = 0; - MSDOS_I(inode)->i_pos = 0; -} - static int fat_read_root(struct inode *inode) { struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); @@ -1827,13 +1824,11 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat, fat_inode = new_inode(sb); if (!fat_inode) goto out_fail; - fat_dummy_inode_init(fat_inode); sbi->fat_inode = fat_inode; fsinfo_inode = new_inode(sb); if (!fsinfo_inode) goto out_fail; - fat_dummy_inode_init(fsinfo_inode); fsinfo_inode->i_ino = MSDOS_FSINFO_INO; sbi->fsinfo_inode = fsinfo_inode; insert_inode_hash(fsinfo_inode); diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index 08369c6cd127a4fee6a301babe1fea80d475d2a3..143e7d518c5d5839e94318ff6ecb46c761e3f929 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -780,7 +780,7 @@ static ssize_t gfs2_file_write_iter(struct kiocb *iocb, struct iov_iter *from) struct file *file = iocb->ki_filp; struct inode *inode = file_inode(file); struct gfs2_inode *ip = GFS2_I(inode); - ssize_t written = 0, ret; + ssize_t ret; ret = gfs2_rsqa_alloc(ip); if (ret) @@ -800,68 +800,58 @@ static ssize_t gfs2_file_write_iter(struct kiocb *iocb, struct iov_iter *from) inode_lock(inode); ret = generic_write_checks(iocb, from); if (ret <= 0) - goto out; - - /* We can write back this queue in page reclaim */ - current->backing_dev_info = inode_to_bdi(inode); + goto out_unlock; ret = file_remove_privs(file); if (ret) - goto out2; + goto out_unlock; ret = file_update_time(file); if (ret) - goto out2; + goto out_unlock; if (iocb->ki_flags & IOCB_DIRECT) { struct address_space *mapping = file->f_mapping; - loff_t pos, endbyte; - ssize_t buffered; + ssize_t buffered, ret2; - written = gfs2_file_direct_write(iocb, from); - if (written < 0 || !iov_iter_count(from)) - goto out2; + ret = gfs2_file_direct_write(iocb, from); + if (ret < 0 || !iov_iter_count(from)) + goto out_unlock; - ret = iomap_file_buffered_write(iocb, from, &gfs2_iomap_ops); - if (unlikely(ret < 0)) - goto out2; - buffered = ret; + iocb->ki_flags |= IOCB_DSYNC; + current->backing_dev_info = inode_to_bdi(inode); + buffered = iomap_file_buffered_write(iocb, from, &gfs2_iomap_ops); + current->backing_dev_info = NULL; + if (unlikely(buffered <= 0)) + goto out_unlock; /* * We need to ensure that the page cache pages are written to * disk and invalidated to preserve the expected O_DIRECT - * semantics. + * semantics. If the writeback or invalidate fails, only report + * the direct I/O range as we don't know if the buffered pages + * made it to disk. */ - pos = iocb->ki_pos; - endbyte = pos + buffered - 1; - ret = filemap_write_and_wait_range(mapping, pos, endbyte); - if (!ret) { - iocb->ki_pos += buffered; - written += buffered; - invalidate_mapping_pages(mapping, - pos >> PAGE_SHIFT, - endbyte >> PAGE_SHIFT); - } else { - /* - * We don't know how much we wrote, so just return - * the number of bytes which were direct-written - */ - } + iocb->ki_pos += buffered; + ret2 = generic_write_sync(iocb, buffered); + invalidate_mapping_pages(mapping, + (iocb->ki_pos - buffered) >> PAGE_SHIFT, + (iocb->ki_pos - 1) >> PAGE_SHIFT); + if (!ret || ret2 > 0) + ret += ret2; } else { + current->backing_dev_info = inode_to_bdi(inode); ret = iomap_file_buffered_write(iocb, from, &gfs2_iomap_ops); - if (likely(ret > 0)) + current->backing_dev_info = NULL; + if (likely(ret > 0)) { iocb->ki_pos += ret; + ret = generic_write_sync(iocb, ret); + } } -out2: - current->backing_dev_info = NULL; -out: +out_unlock: inode_unlock(inode); - if (likely(ret > 0)) { - /* Handle various SYNC-type writes */ - ret = generic_write_sync(iocb, ret); - } - return written ? written : ret; + return ret; } static int fallocate_chunk(struct inode *inode, loff_t offset, loff_t len, diff --git a/fs/incfs/Kconfig b/fs/incfs/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..a655d599ea46cd1b71f3398a41eaaacdc6fa9cb0 --- /dev/null +++ b/fs/incfs/Kconfig @@ -0,0 +1,19 @@ +config INCREMENTAL_FS + tristate "Incremental file system support" + depends on BLOCK + select DECOMPRESS_LZ4 + select CRC32 + select CRYPTO + select CRYPTO_RSA + select CRYPTO_SHA256 + select X509_CERTIFICATE_PARSER + select ASYMMETRIC_KEY_TYPE + select ASYMMETRIC_PUBLIC_KEY_SUBTYPE + select PKCS7_MESSAGE_PARSER + help + Incremental FS is a read-only virtual file system that facilitates execution + of programs while their binaries are still being lazily downloaded over the + network, USB or pigeon post. + + To compile this file system support as a module, choose M here: the + module will be called incrementalfs. diff --git a/fs/incfs/Makefile b/fs/incfs/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..8d734bf91ecdc67a726a288eca1dacf815790d67 --- /dev/null +++ b/fs/incfs/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_INCREMENTAL_FS) += incrementalfs.o + +incrementalfs-y := \ + data_mgmt.o \ + format.o \ + integrity.o \ + main.o \ + vfs.o diff --git a/fs/incfs/data_mgmt.c b/fs/incfs/data_mgmt.c new file mode 100644 index 0000000000000000000000000000000000000000..91a81a571d510b20971d0632be14b93ae0e9058c --- /dev/null +++ b/fs/incfs/data_mgmt.c @@ -0,0 +1,1138 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 Google LLC + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "data_mgmt.h" +#include "format.h" +#include "integrity.h" + +struct mount_info *incfs_alloc_mount_info(struct super_block *sb, + struct mount_options *options, + struct path *backing_dir_path) +{ + struct mount_info *mi = NULL; + int error = 0; + + mi = kzalloc(sizeof(*mi), GFP_NOFS); + if (!mi) + return ERR_PTR(-ENOMEM); + + mi->mi_sb = sb; + mi->mi_options = *options; + mi->mi_backing_dir_path = *backing_dir_path; + mi->mi_owner = get_current_cred(); + path_get(&mi->mi_backing_dir_path); + mutex_init(&mi->mi_dir_struct_mutex); + mutex_init(&mi->mi_pending_reads_mutex); + init_waitqueue_head(&mi->mi_pending_reads_notif_wq); + INIT_LIST_HEAD(&mi->mi_reads_list_head); + + if (options->read_log_pages != 0) { + size_t buf_size = PAGE_SIZE * options->read_log_pages; + + spin_lock_init(&mi->mi_log.rl_writer_lock); + init_waitqueue_head(&mi->mi_log.ml_notif_wq); + + mi->mi_log.rl_size = buf_size / sizeof(*mi->mi_log.rl_ring_buf); + mi->mi_log.rl_ring_buf = kzalloc(buf_size, GFP_NOFS); + if (!mi->mi_log.rl_ring_buf) { + error = -ENOMEM; + goto err; + } + } + + return mi; + +err: + incfs_free_mount_info(mi); + return ERR_PTR(error); +} + +void incfs_free_mount_info(struct mount_info *mi) +{ + if (!mi) + return; + + dput(mi->mi_index_dir); + path_put(&mi->mi_backing_dir_path); + mutex_destroy(&mi->mi_dir_struct_mutex); + mutex_destroy(&mi->mi_pending_reads_mutex); + put_cred(mi->mi_owner); + kfree(mi->mi_log.rl_ring_buf); + kfree(mi->log_xattr); + kfree(mi->pending_read_xattr); + kfree(mi); +} + +static void data_file_segment_init(struct data_file_segment *segment) +{ + init_waitqueue_head(&segment->new_data_arrival_wq); + mutex_init(&segment->blockmap_mutex); + INIT_LIST_HEAD(&segment->reads_list_head); +} + +static void data_file_segment_destroy(struct data_file_segment *segment) +{ + mutex_destroy(&segment->blockmap_mutex); +} + +struct data_file *incfs_open_data_file(struct mount_info *mi, struct file *bf) +{ + struct data_file *df = NULL; + struct backing_file_context *bfc = NULL; + int md_records; + u64 size; + int error = 0; + int i; + + if (!bf || !mi) + return ERR_PTR(-EFAULT); + + if (!S_ISREG(bf->f_inode->i_mode)) + return ERR_PTR(-EBADF); + + bfc = incfs_alloc_bfc(bf); + if (IS_ERR(bfc)) + return ERR_CAST(bfc); + + df = kzalloc(sizeof(*df), GFP_NOFS); + if (!df) { + error = -ENOMEM; + goto out; + } + + df->df_backing_file_context = bfc; + df->df_mount_info = mi; + for (i = 0; i < ARRAY_SIZE(df->df_segments); i++) + data_file_segment_init(&df->df_segments[i]); + + error = mutex_lock_interruptible(&bfc->bc_mutex); + if (error) + goto out; + error = incfs_read_file_header(bfc, &df->df_metadata_off, + &df->df_id, &size); + mutex_unlock(&bfc->bc_mutex); + + if (error) + goto out; + + df->df_size = size; + if (size > 0) + df->df_block_count = get_blocks_count_for_size(size); + + md_records = incfs_scan_metadata_chain(df); + if (md_records < 0) + error = md_records; + +out: + if (error) { + incfs_free_bfc(bfc); + df->df_backing_file_context = NULL; + incfs_free_data_file(df); + return ERR_PTR(error); + } + return df; +} + +void incfs_free_data_file(struct data_file *df) +{ + int i; + + if (!df) + return; + + incfs_free_mtree(df->df_hash_tree); + for (i = 0; i < ARRAY_SIZE(df->df_segments); i++) + data_file_segment_destroy(&df->df_segments[i]); + incfs_free_bfc(df->df_backing_file_context); + kfree(df); +} + +int make_inode_ready_for_data_ops(struct mount_info *mi, + struct inode *inode, + struct file *backing_file) +{ + struct inode_info *node = get_incfs_node(inode); + struct data_file *df = NULL; + int err = 0; + + inode_lock(inode); + if (S_ISREG(inode->i_mode)) { + if (!node->n_file) { + df = incfs_open_data_file(mi, backing_file); + + if (IS_ERR(df)) + err = PTR_ERR(df); + else + node->n_file = df; + } + } else + err = -EBADF; + inode_unlock(inode); + return err; +} + +struct dir_file *incfs_open_dir_file(struct mount_info *mi, struct file *bf) +{ + struct dir_file *dir = NULL; + + if (!S_ISDIR(bf->f_inode->i_mode)) + return ERR_PTR(-EBADF); + + dir = kzalloc(sizeof(*dir), GFP_NOFS); + if (!dir) + return ERR_PTR(-ENOMEM); + + dir->backing_dir = get_file(bf); + dir->mount_info = mi; + return dir; +} + +void incfs_free_dir_file(struct dir_file *dir) +{ + if (!dir) + return; + if (dir->backing_dir) + fput(dir->backing_dir); + kfree(dir); +} + +static ssize_t decompress(struct mem_range src, struct mem_range dst) +{ + int result = LZ4_decompress_safe(src.data, dst.data, src.len, dst.len); + + if (result < 0) + return -EBADMSG; + + return result; +} + +static void log_block_read(struct mount_info *mi, incfs_uuid_t *id, + int block_index, bool timed_out) +{ + struct read_log *log = &mi->mi_log; + struct read_log_state state; + s64 now_us = ktime_to_us(ktime_get()); + struct read_log_record record = { + .file_id = *id, + .block_index = block_index, + .timed_out = timed_out, + .timestamp_us = now_us + }; + + if (log->rl_size == 0) + return; + + spin_lock(&log->rl_writer_lock); + state = READ_ONCE(log->rl_state); + log->rl_ring_buf[state.next_index] = record; + if (++state.next_index == log->rl_size) { + state.next_index = 0; + ++state.current_pass_no; + } + WRITE_ONCE(log->rl_state, state); + spin_unlock(&log->rl_writer_lock); + + wake_up_all(&log->ml_notif_wq); +} + +static int validate_hash_tree(struct file *bf, struct data_file *df, + int block_index, struct mem_range data, u8 *buf) +{ + u8 digest[INCFS_MAX_HASH_SIZE] = {}; + struct mtree *tree = NULL; + struct ondisk_signature *sig = NULL; + struct mem_range calc_digest_rng; + struct mem_range saved_digest_rng; + struct mem_range root_hash_rng; + int digest_size; + int hash_block_index = block_index; + int hash_per_block; + int lvl = 0; + int res; + + tree = df->df_hash_tree; + sig = df->df_signature; + if (!tree || !sig) + return 0; + + digest_size = tree->alg->digest_size; + hash_per_block = INCFS_DATA_FILE_BLOCK_SIZE / digest_size; + calc_digest_rng = range(digest, digest_size); + res = incfs_calc_digest(tree->alg, data, calc_digest_rng); + if (res) + return res; + + for (lvl = 0; lvl < tree->depth; lvl++) { + loff_t lvl_off = tree->hash_level_suboffset[lvl] + + sig->mtree_offset; + loff_t hash_block_off = lvl_off + + round_down(hash_block_index * digest_size, + INCFS_DATA_FILE_BLOCK_SIZE); + size_t hash_off_in_block = hash_block_index * digest_size + % INCFS_DATA_FILE_BLOCK_SIZE; + struct mem_range buf_range = range(buf, + INCFS_DATA_FILE_BLOCK_SIZE); + ssize_t read_res = incfs_kread(bf, buf, + INCFS_DATA_FILE_BLOCK_SIZE, hash_block_off); + + if (read_res < 0) + return read_res; + if (read_res != INCFS_DATA_FILE_BLOCK_SIZE) + return -EIO; + + saved_digest_rng = range(buf + hash_off_in_block, digest_size); + if (!incfs_equal_ranges(calc_digest_rng, saved_digest_rng)) { + int i; + bool zero = true; + + pr_debug("incfs: Hash mismatch lvl:%d blk:%d\n", + lvl, block_index); + for (i = 0; i < saved_digest_rng.len; ++i) + if (saved_digest_rng.data[i]) { + zero = false; + break; + } + + if (zero) + pr_debug("incfs: Note saved_digest all zero - did you forget to load the hashes?\n"); + return -EBADMSG; + } + + res = incfs_calc_digest(tree->alg, buf_range, calc_digest_rng); + if (res) + return res; + hash_block_index /= hash_per_block; + } + + root_hash_rng = range(tree->root_hash, digest_size); + if (!incfs_equal_ranges(calc_digest_rng, root_hash_rng)) { + pr_debug("incfs: Root hash mismatch blk:%d\n", block_index); + return -EBADMSG; + } + return 0; +} + +static int revalidate_signature(struct file *bf, struct data_file *df) +{ + struct ondisk_signature *sig = df->df_signature; + struct mem_range root_hash = {}; + int result = 0; + u8 *sig_buf = NULL; + u8 *add_data_buf = NULL; + ssize_t read_res; + + /* File has no signature. */ + if (!sig || !df->df_hash_tree || sig->sig_size == 0) + return 0; + + /* Signature has already been validated. */ + if (df->df_signature_validated) + return 0; + + add_data_buf = kzalloc(sig->add_data_size, GFP_NOFS); + if (!add_data_buf) { + result = -ENOMEM; + goto out; + } + + read_res = incfs_kread(bf, add_data_buf, sig->add_data_size, + sig->add_data_offset); + if (read_res < 0) { + result = read_res; + goto out; + } + if (read_res != sig->add_data_size) { + result = -EIO; + goto out; + } + + sig_buf = kzalloc(sig->sig_size, GFP_NOFS); + if (!sig_buf) { + result = -ENOMEM; + goto out; + } + + read_res = incfs_kread(bf, sig_buf, sig->sig_size, sig->sig_offset); + if (read_res < 0) { + result = read_res; + goto out; + } + if (read_res != sig->sig_size) { + result = -EIO; + goto out; + } + + root_hash = range(df->df_hash_tree->root_hash, + df->df_hash_tree->alg->digest_size); + + result = incfs_validate_pkcs7_signature( + range(sig_buf, sig->sig_size), + root_hash, + range(add_data_buf, sig->add_data_size)); + + if (result == 0) + df->df_signature_validated = true; +out: + kfree(sig_buf); + kfree(add_data_buf); + return result; +} + +static struct data_file_segment *get_file_segment(struct data_file *df, + int block_index) +{ + int seg_idx = block_index % ARRAY_SIZE(df->df_segments); + + return &df->df_segments[seg_idx]; +} + +static bool is_data_block_present(struct data_file_block *block) +{ + return (block->db_backing_file_data_offset != 0) && + (block->db_stored_size != 0); +} + +static int get_data_file_block(struct data_file *df, int index, + struct data_file_block *res_block) +{ + struct incfs_blockmap_entry bme = {}; + struct backing_file_context *bfc = NULL; + loff_t blockmap_off = 0; + u16 flags = 0; + int error = 0; + + if (!df || !res_block) + return -EFAULT; + + blockmap_off = df->df_blockmap_off; + bfc = df->df_backing_file_context; + + if (index < 0 || index >= df->df_block_count || blockmap_off == 0) + return -EINVAL; + + error = incfs_read_blockmap_entry(bfc, index, blockmap_off, &bme); + if (error) + return error; + + flags = le16_to_cpu(bme.me_flags); + res_block->db_backing_file_data_offset = + le16_to_cpu(bme.me_data_offset_hi); + res_block->db_backing_file_data_offset <<= 32; + res_block->db_backing_file_data_offset |= + le32_to_cpu(bme.me_data_offset_lo); + res_block->db_stored_size = le16_to_cpu(bme.me_data_size); + res_block->db_comp_alg = (flags & INCFS_BLOCK_COMPRESSED_LZ4) ? + COMPRESSION_LZ4 : + COMPRESSION_NONE; + return 0; +} + +static bool is_read_done(struct pending_read *read) +{ + return atomic_read_acquire(&read->done) != 0; +} + +static void set_read_done(struct pending_read *read) +{ + atomic_set_release(&read->done, 1); +} + +/* + * Notifies a given data file about pending read from a given block. + * Returns a new pending read entry. + */ +static struct pending_read *add_pending_read(struct data_file *df, + int block_index) +{ + struct pending_read *result = NULL; + struct data_file_segment *segment = NULL; + struct mount_info *mi = NULL; + + segment = get_file_segment(df, block_index); + mi = df->df_mount_info; + + result = kzalloc(sizeof(*result), GFP_NOFS); + if (!result) + return NULL; + + result->file_id = df->df_id; + result->block_index = block_index; + result->timestamp_us = ktime_to_us(ktime_get()); + + mutex_lock(&mi->mi_pending_reads_mutex); + + result->serial_number = ++mi->mi_last_pending_read_number; + mi->mi_pending_reads_count++; + + list_add(&result->mi_reads_list, &mi->mi_reads_list_head); + list_add(&result->segment_reads_list, &segment->reads_list_head); + mutex_unlock(&mi->mi_pending_reads_mutex); + + wake_up_all(&mi->mi_pending_reads_notif_wq); + return result; +} + +/* Notifies a given data file that pending read is completed. */ +static void remove_pending_read(struct data_file *df, struct pending_read *read) +{ + struct mount_info *mi = NULL; + + if (!df || !read) { + WARN_ON(!df); + WARN_ON(!read); + return; + } + + mi = df->df_mount_info; + + mutex_lock(&mi->mi_pending_reads_mutex); + list_del(&read->mi_reads_list); + list_del(&read->segment_reads_list); + + mi->mi_pending_reads_count--; + mutex_unlock(&mi->mi_pending_reads_mutex); + + kfree(read); +} + +static void notify_pending_reads(struct mount_info *mi, + struct data_file_segment *segment, + int index) +{ + struct pending_read *entry = NULL; + + /* Notify pending reads waiting for this block. */ + mutex_lock(&mi->mi_pending_reads_mutex); + list_for_each_entry(entry, &segment->reads_list_head, + segment_reads_list) { + if (entry->block_index == index) + set_read_done(entry); + } + mutex_unlock(&mi->mi_pending_reads_mutex); + wake_up_all(&segment->new_data_arrival_wq); +} + +static int wait_for_data_block(struct data_file *df, int block_index, + int timeout_ms, + struct data_file_block *res_block) +{ + struct data_file_block block = {}; + struct data_file_segment *segment = NULL; + struct pending_read *read = NULL; + struct mount_info *mi = NULL; + int error = 0; + int wait_res = 0; + + if (!df || !res_block) + return -EFAULT; + + if (block_index < 0 || block_index >= df->df_block_count) + return -EINVAL; + + if (df->df_blockmap_off <= 0) + return -ENODATA; + + segment = get_file_segment(df, block_index); + error = mutex_lock_interruptible(&segment->blockmap_mutex); + if (error) + return error; + + /* Look up the given block */ + error = get_data_file_block(df, block_index, &block); + + /* If it's not found, create a pending read */ + if (!error && !is_data_block_present(&block) && timeout_ms != 0) + read = add_pending_read(df, block_index); + + mutex_unlock(&segment->blockmap_mutex); + if (error) + return error; + + /* If the block was found, just return it. No need to wait. */ + if (is_data_block_present(&block)) { + *res_block = block; + return 0; + } + + mi = df->df_mount_info; + + if (timeout_ms == 0) { + log_block_read(mi, &df->df_id, block_index, + true /*timed out*/); + return -ETIME; + } + + if (!read) + return -ENOMEM; + + /* Wait for notifications about block's arrival */ + wait_res = + wait_event_interruptible_timeout(segment->new_data_arrival_wq, + (is_read_done(read)), + msecs_to_jiffies(timeout_ms)); + + /* Woke up, the pending read is no longer needed. */ + remove_pending_read(df, read); + read = NULL; + + if (wait_res == 0) { + /* Wait has timed out */ + log_block_read(mi, &df->df_id, block_index, + true /*timed out*/); + return -ETIME; + } + if (wait_res < 0) { + /* + * Only ERESTARTSYS is really expected here when a signal + * comes while we wait. + */ + return wait_res; + } + + error = mutex_lock_interruptible(&segment->blockmap_mutex); + if (error) + return error; + + /* + * Re-read block's info now, it has just arrived and + * should be available. + */ + error = get_data_file_block(df, block_index, &block); + if (!error) { + if (is_data_block_present(&block)) + *res_block = block; + else { + /* + * Somehow wait finished successfully bug block still + * can't be found. It's not normal. + */ + pr_warn("incfs:Wait succeeded, but block not found.\n"); + error = -ENODATA; + } + } + + mutex_unlock(&segment->blockmap_mutex); + return error; +} + +ssize_t incfs_read_data_file_block(struct mem_range dst, struct data_file *df, + int index, int timeout_ms, + struct mem_range tmp) +{ + loff_t pos; + ssize_t result; + size_t bytes_to_read; + struct mount_info *mi = NULL; + struct file *bf = NULL; + struct data_file_block block = {}; + + if (!dst.data || !df) + return -EFAULT; + + if (tmp.len < 2 * INCFS_DATA_FILE_BLOCK_SIZE) + return -ERANGE; + + mi = df->df_mount_info; + bf = df->df_backing_file_context->bc_file; + + result = wait_for_data_block(df, index, timeout_ms, &block); + if (result < 0) + goto out; + + pos = block.db_backing_file_data_offset; + if (block.db_comp_alg == COMPRESSION_NONE) { + bytes_to_read = min(dst.len, block.db_stored_size); + result = incfs_kread(bf, dst.data, bytes_to_read, pos); + + /* Some data was read, but not enough */ + if (result >= 0 && result != bytes_to_read) + result = -EIO; + } else { + bytes_to_read = min(tmp.len, block.db_stored_size); + result = incfs_kread(bf, tmp.data, bytes_to_read, pos); + if (result == bytes_to_read) { + result = + decompress(range(tmp.data, bytes_to_read), dst); + if (result < 0) { + const char *name = + bf->f_path.dentry->d_name.name; + + pr_warn_once("incfs: Decompression error. %s", + name); + } + } else if (result >= 0) { + /* Some data was read, but not enough */ + result = -EIO; + } + } + + if (result > 0) { + int err = validate_hash_tree(bf, df, index, dst, tmp.data); + + if (err < 0) + result = err; + } + + if (result > 0) { + int err = revalidate_signature(bf, df); + + if (err < 0) + result = err; + } + + if (result >= 0) + log_block_read(mi, &df->df_id, index, false /*timed out*/); + +out: + return result; +} + +int incfs_process_new_data_block(struct data_file *df, + struct incfs_new_data_block *block, u8 *data) +{ + struct mount_info *mi = NULL; + struct backing_file_context *bfc = NULL; + struct data_file_segment *segment = NULL; + struct data_file_block existing_block = {}; + u16 flags = 0; + int error = 0; + + if (!df || !block) + return -EFAULT; + + bfc = df->df_backing_file_context; + mi = df->df_mount_info; + + if (block->block_index >= df->df_block_count) + return -ERANGE; + + segment = get_file_segment(df, block->block_index); + if (!segment) + return -EFAULT; + if (block->compression == COMPRESSION_LZ4) + flags |= INCFS_BLOCK_COMPRESSED_LZ4; + + error = mutex_lock_interruptible(&segment->blockmap_mutex); + if (error) + return error; + + error = get_data_file_block(df, block->block_index, &existing_block); + if (error) + goto unlock; + if (is_data_block_present(&existing_block)) { + /* Block is already present, nothing to do here */ + goto unlock; + } + + error = mutex_lock_interruptible(&bfc->bc_mutex); + if (!error) { + error = incfs_write_data_block_to_backing_file( + bfc, range(data, block->data_len), block->block_index, + df->df_blockmap_off, flags); + mutex_unlock(&bfc->bc_mutex); + } + if (!error) + notify_pending_reads(mi, segment, block->block_index); + +unlock: + mutex_unlock(&segment->blockmap_mutex); + if (error) + pr_debug("incfs: %s %d error: %d\n", __func__, + block->block_index, error); + return error; +} + +int incfs_read_file_signature(struct data_file *df, struct mem_range dst) +{ + struct file *bf = df->df_backing_file_context->bc_file; + struct ondisk_signature *sig; + int read_res = 0; + + if (!dst.data) + return -EFAULT; + + sig = df->df_signature; + if (!sig) + return 0; + + if (dst.len < sig->sig_size) + return -E2BIG; + + read_res = incfs_kread(bf, dst.data, sig->sig_size, sig->sig_offset); + + if (read_res < 0) + return read_res; + + if (read_res != sig->sig_size) + return -EIO; + + return read_res; +} + +int incfs_process_new_hash_block(struct data_file *df, + struct incfs_new_data_block *block, u8 *data) +{ + struct backing_file_context *bfc = NULL; + struct mount_info *mi = NULL; + struct mtree *hash_tree = NULL; + struct ondisk_signature *sig = NULL; + loff_t hash_area_base = 0; + loff_t hash_area_size = 0; + int error = 0; + + if (!df || !block) + return -EFAULT; + + if (!(block->flags & INCFS_BLOCK_FLAGS_HASH)) + return -EINVAL; + + bfc = df->df_backing_file_context; + mi = df->df_mount_info; + + if (!df) + return -ENOENT; + + hash_tree = df->df_hash_tree; + sig = df->df_signature; + if (!hash_tree || !sig || sig->mtree_offset == 0) + return -ENOTSUPP; + + hash_area_base = sig->mtree_offset; + hash_area_size = sig->mtree_size; + if (hash_area_size < block->block_index * INCFS_DATA_FILE_BLOCK_SIZE + + block->data_len) { + /* Hash block goes beyond dedicated hash area of this file. */ + return -ERANGE; + } + + error = mutex_lock_interruptible(&bfc->bc_mutex); + if (!error) + error = incfs_write_hash_block_to_backing_file( + bfc, range(data, block->data_len), block->block_index, + hash_area_base); + mutex_unlock(&bfc->bc_mutex); + return error; +} + +static int process_blockmap_md(struct incfs_blockmap *bm, + struct metadata_handler *handler) +{ + struct data_file *df = handler->context; + int error = 0; + loff_t base_off = le64_to_cpu(bm->m_base_offset); + u32 block_count = le32_to_cpu(bm->m_block_count); + + if (!df) + return -EFAULT; + + if (df->df_block_count != block_count) + return -EBADMSG; + + df->df_blockmap_off = base_off; + return error; +} + +static int process_file_attr_md(struct incfs_file_attr *fa, + struct metadata_handler *handler) +{ + struct data_file *df = handler->context; + u16 attr_size = le16_to_cpu(fa->fa_size); + + if (!df) + return -EFAULT; + + if (attr_size > INCFS_MAX_FILE_ATTR_SIZE) + return -E2BIG; + + df->n_attr.fa_value_offset = le64_to_cpu(fa->fa_offset); + df->n_attr.fa_value_size = attr_size; + df->n_attr.fa_crc = le32_to_cpu(fa->fa_crc); + + return 0; +} + +static int process_file_signature_md(struct incfs_file_signature *sg, + struct metadata_handler *handler) +{ + struct data_file *df = handler->context; + struct mtree *hash_tree = NULL; + struct ondisk_signature *signature = NULL; + int error = 0; + loff_t base_tree_off = le64_to_cpu(sg->sg_hash_tree_offset); + u32 tree_size = le32_to_cpu(sg->sg_hash_tree_size); + loff_t sig_off = le64_to_cpu(sg->sg_sig_offset); + u32 sig_size = le32_to_cpu(sg->sg_sig_size); + loff_t add_data_off = le64_to_cpu(sg->sg_add_data_offset); + u32 add_data_size = le32_to_cpu(sg->sg_add_data_size); + + if (!df) + return -ENOENT; + + signature = kzalloc(sizeof(*signature), GFP_NOFS); + if (!signature) { + error = -ENOMEM; + goto out; + } + + signature->add_data_offset = add_data_off; + signature->add_data_size = add_data_size; + signature->sig_offset = sig_off; + signature->sig_size = sig_size; + signature->mtree_offset = base_tree_off; + signature->mtree_size = tree_size; + + hash_tree = incfs_alloc_mtree(sg->sg_hash_alg, df->df_block_count, + range(sg->sg_root_hash, sizeof(sg->sg_root_hash))); + if (IS_ERR(hash_tree)) { + error = PTR_ERR(hash_tree); + hash_tree = NULL; + goto out; + } + if (hash_tree->hash_tree_area_size != tree_size) { + error = -EINVAL; + goto out; + } + if (tree_size > 0 && handler->md_record_offset <= base_tree_off) { + error = -EINVAL; + goto out; + } + if (handler->md_record_offset <= signature->add_data_offset || + handler->md_record_offset <= signature->sig_offset) { + error = -EINVAL; + goto out; + } + df->df_hash_tree = hash_tree; + df->df_signature = signature; +out: + if (error) { + incfs_free_mtree(hash_tree); + kfree(signature); + } + + return error; +} + +int incfs_scan_metadata_chain(struct data_file *df) +{ + struct metadata_handler *handler = NULL; + int result = 0; + int records_count = 0; + int error = 0; + struct backing_file_context *bfc = NULL; + + if (!df || !df->df_backing_file_context) + return -EFAULT; + + bfc = df->df_backing_file_context; + + handler = kzalloc(sizeof(*handler), GFP_NOFS); + if (!handler) + return -ENOMEM; + + /* No writing to the backing file while it's being scanned. */ + error = mutex_lock_interruptible(&bfc->bc_mutex); + if (error) + goto out; + + /* Reading superblock */ + handler->md_record_offset = df->df_metadata_off; + handler->context = df; + handler->handle_blockmap = process_blockmap_md; + handler->handle_file_attr = process_file_attr_md; + handler->handle_signature = process_file_signature_md; + + pr_debug("incfs: Starting reading incfs-metadata records at offset %lld\n", + handler->md_record_offset); + while (handler->md_record_offset > 0) { + error = incfs_read_next_metadata_record(bfc, handler); + if (error) { + pr_warn("incfs: Error during reading incfs-metadata record. Offset: %lld Record #%d Error code: %d\n", + handler->md_record_offset, records_count + 1, + -error); + break; + } + records_count++; + } + if (error) { + pr_debug("incfs: Error %d after reading %d incfs-metadata records.\n", + -error, records_count); + result = error; + } else { + pr_debug("incfs: Finished reading %d incfs-metadata records.\n", + records_count); + result = records_count; + } + mutex_unlock(&bfc->bc_mutex); +out: + kfree(handler); + return result; +} + +/* + * Quickly checks if there are pending reads with a serial number larger + * than a given one. + */ +bool incfs_fresh_pending_reads_exist(struct mount_info *mi, int last_number) +{ + bool result = false; + + mutex_lock(&mi->mi_pending_reads_mutex); + result = (mi->mi_last_pending_read_number > last_number) && + (mi->mi_pending_reads_count > 0); + mutex_unlock(&mi->mi_pending_reads_mutex); + return result; +} + +int incfs_collect_pending_reads(struct mount_info *mi, int sn_lowerbound, + struct incfs_pending_read_info *reads, + int reads_size) +{ + int reported_reads = 0; + struct pending_read *entry = NULL; + + if (!mi) + return -EFAULT; + + if (reads_size <= 0) + return 0; + + mutex_lock(&mi->mi_pending_reads_mutex); + + if (mi->mi_last_pending_read_number <= sn_lowerbound + || mi->mi_pending_reads_count == 0) + goto unlock; + + list_for_each_entry(entry, &mi->mi_reads_list_head, mi_reads_list) { + if (entry->serial_number <= sn_lowerbound) + continue; + + reads[reported_reads].file_id = entry->file_id; + reads[reported_reads].block_index = entry->block_index; + reads[reported_reads].serial_number = entry->serial_number; + reads[reported_reads].timestamp_us = entry->timestamp_us; + /* reads[reported_reads].kind = INCFS_READ_KIND_PENDING; */ + + reported_reads++; + if (reported_reads >= reads_size) + break; + } + +unlock: + mutex_unlock(&mi->mi_pending_reads_mutex); + + return reported_reads; +} + +struct read_log_state incfs_get_log_state(struct mount_info *mi) +{ + struct read_log *log = &mi->mi_log; + struct read_log_state result; + + spin_lock(&log->rl_writer_lock); + result = READ_ONCE(log->rl_state); + spin_unlock(&log->rl_writer_lock); + return result; +} + +static u64 calc_record_count(const struct read_log_state *state, int rl_size) +{ + return state->current_pass_no * (u64)rl_size + state->next_index; +} + +int incfs_get_uncollected_logs_count(struct mount_info *mi, + struct read_log_state state) +{ + struct read_log *log = &mi->mi_log; + + u64 count = calc_record_count(&log->rl_state, log->rl_size) - + calc_record_count(&state, log->rl_size); + return min_t(int, count, log->rl_size); +} + +static void fill_pending_read_from_log_record( + struct incfs_pending_read_info *dest, const struct read_log_record *src, + struct read_log_state *state, u64 log_size) +{ + dest->file_id = src->file_id; + dest->block_index = src->block_index; + dest->serial_number = + state->current_pass_no * log_size + state->next_index; + dest->timestamp_us = src->timestamp_us; +} + +int incfs_collect_logged_reads(struct mount_info *mi, + struct read_log_state *reader_state, + struct incfs_pending_read_info *reads, + int reads_size) +{ + struct read_log *log = &mi->mi_log; + struct read_log_state live_state = incfs_get_log_state(mi); + u64 read_count = calc_record_count(reader_state, log->rl_size); + u64 written_count = calc_record_count(&live_state, log->rl_size); + int dst_idx; + + if (reader_state->next_index >= log->rl_size || + read_count > written_count) + return -ERANGE; + + if (read_count == written_count) + return 0; + + if (read_count > written_count) { + /* This reader is somehow ahead of the writer. */ + pr_debug("incfs: Log reader is ahead of writer\n"); + *reader_state = live_state; + } + + if (written_count - read_count > log->rl_size) { + /* + * Reading pointer is too far behind, + * start from the record following the write pointer. + */ + pr_debug("incfs: read pointer is behind, moving: %u/%u -> %u/%u / %u\n", + (u32)reader_state->next_index, + (u32)reader_state->current_pass_no, + (u32)live_state.next_index, + (u32)live_state.current_pass_no - 1, (u32)log->rl_size); + + *reader_state = (struct read_log_state){ + .next_index = live_state.next_index, + .current_pass_no = live_state.current_pass_no - 1, + }; + } + + for (dst_idx = 0; dst_idx < reads_size; dst_idx++) { + if (reader_state->next_index == live_state.next_index && + reader_state->current_pass_no == live_state.current_pass_no) + break; + + fill_pending_read_from_log_record( + &reads[dst_idx], + &log->rl_ring_buf[reader_state->next_index], + reader_state, log->rl_size); + + reader_state->next_index++; + if (reader_state->next_index == log->rl_size) { + reader_state->next_index = 0; + reader_state->current_pass_no++; + } + } + return dst_idx; +} + +bool incfs_equal_ranges(struct mem_range lhs, struct mem_range rhs) +{ + if (lhs.len != rhs.len) + return false; + return memcmp(lhs.data, rhs.data, lhs.len) == 0; +} diff --git a/fs/incfs/data_mgmt.h b/fs/incfs/data_mgmt.h new file mode 100644 index 0000000000000000000000000000000000000000..e86951c7845ae88d186432f17ba79688f54470f9 --- /dev/null +++ b/fs/incfs/data_mgmt.h @@ -0,0 +1,345 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2019 Google LLC + */ +#ifndef _INCFS_DATA_MGMT_H +#define _INCFS_DATA_MGMT_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "internal.h" + +#define SEGMENTS_PER_FILE 3 + +struct read_log_record { + u32 block_index : 31; + + u32 timed_out : 1; + + u64 timestamp_us; + + incfs_uuid_t file_id; +} __packed; + +struct read_log_state { + /* Next slot in rl_ring_buf to write to. */ + u32 next_index; + + /* Current number of writer pass over rl_ring_buf */ + u32 current_pass_no; +}; + +/* A ring buffer to save records about data blocks which were recently read. */ +struct read_log { + struct read_log_record *rl_ring_buf; + + struct read_log_state rl_state; + + spinlock_t rl_writer_lock; + + int rl_size; + + /* + * A queue of waiters who want to be notified about reads. + */ + wait_queue_head_t ml_notif_wq; +}; + +struct mount_options { + unsigned int read_timeout_ms; + unsigned int readahead_pages; + unsigned int read_log_pages; + unsigned int read_log_wakeup_count; + bool no_backing_file_cache; + bool no_backing_file_readahead; +}; + +struct mount_info { + struct super_block *mi_sb; + + struct path mi_backing_dir_path; + + struct dentry *mi_index_dir; + + const struct cred *mi_owner; + + struct mount_options mi_options; + + /* This mutex is to be taken before create, rename, delete */ + struct mutex mi_dir_struct_mutex; + + /* + * A queue of waiters who want to be notified about new pending reads. + */ + wait_queue_head_t mi_pending_reads_notif_wq; + + /* + * Protects: + * - reads_list_head + * - mi_pending_reads_count + * - mi_last_pending_read_number + * - data_file_segment.reads_list_head + */ + struct mutex mi_pending_reads_mutex; + + /* List of active pending_read objects */ + struct list_head mi_reads_list_head; + + /* Total number of items in reads_list_head */ + int mi_pending_reads_count; + + /* + * Last serial number that was assigned to a pending read. + * 0 means no pending reads have been seen yet. + */ + int mi_last_pending_read_number; + + /* Temporary buffer for read logger. */ + struct read_log mi_log; + + void *log_xattr; + size_t log_xattr_size; + + void *pending_read_xattr; + size_t pending_read_xattr_size; +}; + +struct data_file_block { + loff_t db_backing_file_data_offset; + + size_t db_stored_size; + + enum incfs_compression_alg db_comp_alg; +}; + +struct pending_read { + incfs_uuid_t file_id; + + s64 timestamp_us; + + atomic_t done; + + int block_index; + + int serial_number; + + struct list_head mi_reads_list; + + struct list_head segment_reads_list; +}; + +struct data_file_segment { + wait_queue_head_t new_data_arrival_wq; + + /* Protects reads and writes from the blockmap */ + /* Good candidate for read/write mutex */ + struct mutex blockmap_mutex; + + /* List of active pending_read objects belonging to this segment */ + /* Protected by mount_info.pending_reads_mutex */ + struct list_head reads_list_head; +}; + +/* + * Extra info associated with a file. Just a few bytes set by a user. + */ +struct file_attr { + loff_t fa_value_offset; + + size_t fa_value_size; + + u32 fa_crc; +}; + + +struct data_file { + struct backing_file_context *df_backing_file_context; + + struct mount_info *df_mount_info; + + incfs_uuid_t df_id; + + /* + * Array of segments used to reduce lock contention for the file. + * Segment is chosen for a block depends on the block's index. + */ + struct data_file_segment df_segments[SEGMENTS_PER_FILE]; + + /* Base offset of the first metadata record. */ + loff_t df_metadata_off; + + /* Base offset of the block map. */ + loff_t df_blockmap_off; + + /* File size in bytes */ + loff_t df_size; + + int df_block_count; /* File size in DATA_FILE_BLOCK_SIZE blocks */ + + struct file_attr n_attr; + + struct mtree *df_hash_tree; + + struct ondisk_signature *df_signature; + + /* True, if file signature has already been validated. */ + bool df_signature_validated; +}; + +struct dir_file { + struct mount_info *mount_info; + + struct file *backing_dir; +}; + +struct inode_info { + struct mount_info *n_mount_info; /* A mount, this file belongs to */ + + struct inode *n_backing_inode; + + struct data_file *n_file; + + struct inode n_vfs_inode; +}; + +struct dentry_info { + struct path backing_path; +}; + +struct mount_info *incfs_alloc_mount_info(struct super_block *sb, + struct mount_options *options, + struct path *backing_dir_path); + +void incfs_free_mount_info(struct mount_info *mi); + +struct data_file *incfs_open_data_file(struct mount_info *mi, struct file *bf); +void incfs_free_data_file(struct data_file *df); + +int incfs_scan_metadata_chain(struct data_file *df); + +struct dir_file *incfs_open_dir_file(struct mount_info *mi, struct file *bf); +void incfs_free_dir_file(struct dir_file *dir); + +ssize_t incfs_read_data_file_block(struct mem_range dst, struct data_file *df, + int index, int timeout_ms, + struct mem_range tmp); + +int incfs_read_file_signature(struct data_file *df, struct mem_range dst); + +int incfs_process_new_data_block(struct data_file *df, + struct incfs_new_data_block *block, u8 *data); + +int incfs_process_new_hash_block(struct data_file *df, + struct incfs_new_data_block *block, u8 *data); + + +bool incfs_fresh_pending_reads_exist(struct mount_info *mi, int last_number); + +/* + * Collects pending reads and saves them into the array (reads/reads_size). + * Only reads with serial_number > sn_lowerbound are reported. + * Returns how many reads were saved into the array. + */ +int incfs_collect_pending_reads(struct mount_info *mi, int sn_lowerbound, + struct incfs_pending_read_info *reads, + int reads_size); + +int incfs_collect_logged_reads(struct mount_info *mi, + struct read_log_state *start_state, + struct incfs_pending_read_info *reads, + int reads_size); +struct read_log_state incfs_get_log_state(struct mount_info *mi); +int incfs_get_uncollected_logs_count(struct mount_info *mi, + struct read_log_state state); + +static inline struct inode_info *get_incfs_node(struct inode *inode) +{ + if (!inode) + return NULL; + + if (inode->i_sb->s_magic != INCFS_MAGIC_NUMBER) { + /* This inode doesn't belong to us. */ + pr_warn_once("incfs: %s on an alien inode.", __func__); + return NULL; + } + + return container_of(inode, struct inode_info, n_vfs_inode); +} + +static inline struct data_file *get_incfs_data_file(struct file *f) +{ + struct inode_info *node = NULL; + + if (!f) + return NULL; + + if (!S_ISREG(f->f_inode->i_mode)) + return NULL; + + node = get_incfs_node(f->f_inode); + if (!node) + return NULL; + + return node->n_file; +} + +static inline struct dir_file *get_incfs_dir_file(struct file *f) +{ + if (!f) + return NULL; + + if (!S_ISDIR(f->f_inode->i_mode)) + return NULL; + + return (struct dir_file *)f->private_data; +} + +/* + * Make sure that inode_info.n_file is initialized and inode can be used + * for reading and writing data from/to the backing file. + */ +int make_inode_ready_for_data_ops(struct mount_info *mi, + struct inode *inode, + struct file *backing_file); + +static inline struct dentry_info *get_incfs_dentry(const struct dentry *d) +{ + if (!d) + return NULL; + + return (struct dentry_info *)d->d_fsdata; +} + +static inline void get_incfs_backing_path(const struct dentry *d, + struct path *path) +{ + struct dentry_info *di = get_incfs_dentry(d); + + if (!di) { + *path = (struct path) {}; + return; + } + + *path = di->backing_path; + path_get(path); +} + +static inline int get_blocks_count_for_size(u64 size) +{ + if (size == 0) + return 0; + return 1 + (size - 1) / INCFS_DATA_FILE_BLOCK_SIZE; +} + +bool incfs_equal_ranges(struct mem_range lhs, struct mem_range rhs); + +#endif /* _INCFS_DATA_MGMT_H */ diff --git a/fs/incfs/format.c b/fs/incfs/format.c new file mode 100644 index 0000000000000000000000000000000000000000..db71f527cf3637b357ef527e692a772b78fa4837 --- /dev/null +++ b/fs/incfs/format.c @@ -0,0 +1,687 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2018 Google LLC + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "format.h" + +struct backing_file_context *incfs_alloc_bfc(struct file *backing_file) +{ + struct backing_file_context *result = NULL; + + result = kzalloc(sizeof(*result), GFP_NOFS); + if (!result) + return ERR_PTR(-ENOMEM); + + result->bc_file = get_file(backing_file); + mutex_init(&result->bc_mutex); + return result; +} + +void incfs_free_bfc(struct backing_file_context *bfc) +{ + if (!bfc) + return; + + if (bfc->bc_file) + fput(bfc->bc_file); + + mutex_destroy(&bfc->bc_mutex); + kfree(bfc); +} + +loff_t incfs_get_end_offset(struct file *f) +{ + /* + * This function assumes that file size and the end-offset + * are the same. This is not always true. + */ + return i_size_read(file_inode(f)); +} + +/* + * Truncate the tail of the file to the given length. + * Used to rollback partially successful multistep writes. + */ +static int truncate_backing_file(struct backing_file_context *bfc, + loff_t new_end) +{ + struct inode *inode = NULL; + struct dentry *dentry = NULL; + loff_t old_end = 0; + struct iattr attr; + int result = 0; + + if (!bfc) + return -EFAULT; + + LOCK_REQUIRED(bfc->bc_mutex); + + if (!bfc->bc_file) + return -EFAULT; + + old_end = incfs_get_end_offset(bfc->bc_file); + if (old_end == new_end) + return 0; + if (old_end < new_end) + return -EINVAL; + + inode = bfc->bc_file->f_inode; + dentry = bfc->bc_file->f_path.dentry; + + attr.ia_size = new_end; + attr.ia_valid = ATTR_SIZE; + + inode_lock(inode); + result = notify_change(dentry, &attr, NULL); + inode_unlock(inode); + + return result; +} + +/* Append a given number of zero bytes to the end of the backing file. */ +static int append_zeros(struct backing_file_context *bfc, size_t len) +{ + loff_t file_size = 0; + loff_t new_last_byte_offset = 0; + int res = 0; + + if (!bfc) + return -EFAULT; + + if (len == 0) + return 0; + + LOCK_REQUIRED(bfc->bc_mutex); + + /* + * Allocate only one byte at the new desired end of the file. + * It will increase file size and create a zeroed area of + * a given size. + */ + file_size = incfs_get_end_offset(bfc->bc_file); + new_last_byte_offset = file_size + len - 1; + res = vfs_fallocate(bfc->bc_file, 0, new_last_byte_offset, 1); + if (res) + return res; + + res = vfs_fsync_range(bfc->bc_file, file_size, file_size + len, 1); + return res; +} + +static int write_to_bf(struct backing_file_context *bfc, const void *buf, + size_t count, loff_t pos, bool sync) +{ + ssize_t res = 0; + + res = incfs_kwrite(bfc->bc_file, buf, count, pos); + if (res < 0) + return res; + if (res != count) + return -EIO; + + if (sync) + return vfs_fsync_range(bfc->bc_file, pos, pos + count, 1); + + return 0; +} + +static u32 calc_md_crc(struct incfs_md_header *record) +{ + u32 result = 0; + __le32 saved_crc = record->h_record_crc; + __le64 saved_md_offset = record->h_next_md_offset; + size_t record_size = min_t(size_t, le16_to_cpu(record->h_record_size), + INCFS_MAX_METADATA_RECORD_SIZE); + + /* Zero fields which needs to be excluded from CRC calculation. */ + record->h_record_crc = 0; + record->h_next_md_offset = 0; + result = crc32(0, record, record_size); + + /* Restore excluded fields. */ + record->h_record_crc = saved_crc; + record->h_next_md_offset = saved_md_offset; + + return result; +} + +/* + * Append a given metadata record to the backing file and update a previous + * record to add the new record the the metadata list. + */ +static int append_md_to_backing_file(struct backing_file_context *bfc, + struct incfs_md_header *record) +{ + int result = 0; + loff_t record_offset; + loff_t file_pos; + __le64 new_md_offset; + size_t record_size; + + if (!bfc || !record) + return -EFAULT; + + if (bfc->bc_last_md_record_offset < 0) + return -EINVAL; + + LOCK_REQUIRED(bfc->bc_mutex); + + record_size = le16_to_cpu(record->h_record_size); + file_pos = incfs_get_end_offset(bfc->bc_file); + record->h_prev_md_offset = cpu_to_le64(bfc->bc_last_md_record_offset); + record->h_next_md_offset = 0; + record->h_record_crc = cpu_to_le32(calc_md_crc(record)); + + /* Write the metadata record to the end of the backing file */ + record_offset = file_pos; + new_md_offset = cpu_to_le64(record_offset); + result = write_to_bf(bfc, record, record_size, file_pos, true); + if (result) + return result; + + /* Update next metadata offset in a previous record or a superblock. */ + if (bfc->bc_last_md_record_offset) { + /* + * Find a place in the previous md record where new record's + * offset needs to be saved. + */ + file_pos = bfc->bc_last_md_record_offset + + offsetof(struct incfs_md_header, h_next_md_offset); + } else { + /* + * No metadata yet, file a place to update in the + * file_header. + */ + file_pos = offsetof(struct incfs_file_header, + fh_first_md_offset); + } + result = write_to_bf(bfc, &new_md_offset, sizeof(new_md_offset), + file_pos, true); + if (result) + return result; + + bfc->bc_last_md_record_offset = record_offset; + return result; +} + +/* + * Reserve 0-filled space for the blockmap body, and append + * incfs_blockmap metadata record pointing to it. + */ +int incfs_write_blockmap_to_backing_file(struct backing_file_context *bfc, + u32 block_count, loff_t *map_base_off) +{ + struct incfs_blockmap blockmap = {}; + int result = 0; + loff_t file_end = 0; + size_t map_size = block_count * sizeof(struct incfs_blockmap_entry); + + if (!bfc) + return -EFAULT; + + blockmap.m_header.h_md_entry_type = INCFS_MD_BLOCK_MAP; + blockmap.m_header.h_record_size = cpu_to_le16(sizeof(blockmap)); + blockmap.m_header.h_next_md_offset = cpu_to_le64(0); + blockmap.m_block_count = cpu_to_le32(block_count); + + LOCK_REQUIRED(bfc->bc_mutex); + + /* Reserve 0-filled space for the blockmap body in the backing file. */ + file_end = incfs_get_end_offset(bfc->bc_file); + result = append_zeros(bfc, map_size); + if (result) + return result; + + /* Write blockmap metadata record pointing to the body written above. */ + blockmap.m_base_offset = cpu_to_le64(file_end); + result = append_md_to_backing_file(bfc, &blockmap.m_header); + if (result) { + /* Error, rollback file changes */ + truncate_backing_file(bfc, file_end); + } else if (map_base_off) { + *map_base_off = file_end; + } + + return result; +} + +/* + * Write file attribute data and metadata record to the backing file. + */ +int incfs_write_file_attr_to_backing_file(struct backing_file_context *bfc, + struct mem_range value, struct incfs_file_attr *attr) +{ + struct incfs_file_attr file_attr = {}; + int result = 0; + u32 crc = 0; + loff_t value_offset = 0; + + if (!bfc) + return -EFAULT; + + if (value.len > INCFS_MAX_FILE_ATTR_SIZE) + return -ENOSPC; + + LOCK_REQUIRED(bfc->bc_mutex); + + crc = crc32(0, value.data, value.len); + value_offset = incfs_get_end_offset(bfc->bc_file); + file_attr.fa_header.h_md_entry_type = INCFS_MD_FILE_ATTR; + file_attr.fa_header.h_record_size = cpu_to_le16(sizeof(file_attr)); + file_attr.fa_header.h_next_md_offset = cpu_to_le64(0); + file_attr.fa_size = cpu_to_le16((u16)value.len); + file_attr.fa_offset = cpu_to_le64(value_offset); + file_attr.fa_crc = cpu_to_le32(crc); + + result = write_to_bf(bfc, value.data, value.len, value_offset, true); + if (result) + return result; + + result = append_md_to_backing_file(bfc, &file_attr.fa_header); + if (result) { + /* Error, rollback file changes */ + truncate_backing_file(bfc, value_offset); + } else if (attr) { + *attr = file_attr; + } + + return result; +} + +int incfs_write_signature_to_backing_file(struct backing_file_context *bfc, + u8 hash_alg, u32 tree_size, + struct mem_range root_hash, struct mem_range add_data, + struct mem_range sig) +{ + struct incfs_file_signature sg = {}; + int result = 0; + loff_t rollback_pos = 0; + loff_t tree_area_pos = 0; + size_t alignment = 0; + + if (!bfc) + return -EFAULT; + if (root_hash.len > sizeof(sg.sg_root_hash)) + return -E2BIG; + + LOCK_REQUIRED(bfc->bc_mutex); + + rollback_pos = incfs_get_end_offset(bfc->bc_file); + + sg.sg_header.h_md_entry_type = INCFS_MD_SIGNATURE; + sg.sg_header.h_record_size = cpu_to_le16(sizeof(sg)); + sg.sg_header.h_next_md_offset = cpu_to_le64(0); + sg.sg_hash_alg = hash_alg; + if (sig.data != NULL && sig.len > 0) { + loff_t pos = incfs_get_end_offset(bfc->bc_file); + + sg.sg_sig_size = cpu_to_le32(sig.len); + sg.sg_sig_offset = cpu_to_le64(pos); + + result = write_to_bf(bfc, sig.data, sig.len, pos, false); + if (result) + goto err; + } + + if (add_data.len > 0) { + loff_t pos = incfs_get_end_offset(bfc->bc_file); + + sg.sg_add_data_size = cpu_to_le32(add_data.len); + sg.sg_add_data_offset = cpu_to_le64(pos); + + result = write_to_bf(bfc, add_data.data, + add_data.len, pos, false); + if (result) + goto err; + } + + tree_area_pos = incfs_get_end_offset(bfc->bc_file); + if (hash_alg && tree_size > 0) { + if (tree_size > 5 * INCFS_DATA_FILE_BLOCK_SIZE) { + /* + * If hash tree is big enough, it makes sense to + * align in the backing file for faster access. + */ + loff_t offset = round_up(tree_area_pos, PAGE_SIZE); + + alignment = offset - tree_area_pos; + tree_area_pos = offset; + } + + /* + * If root hash is not the only hash in the tree. + * reserve 0-filled space for the tree. + */ + result = append_zeros(bfc, tree_size + alignment); + if (result) + goto err; + + sg.sg_hash_tree_size = cpu_to_le32(tree_size); + sg.sg_hash_tree_offset = cpu_to_le64(tree_area_pos); + } + memcpy(sg.sg_root_hash, root_hash.data, root_hash.len); + + /* Write a hash tree metadata record pointing to the hash tree above. */ + result = append_md_to_backing_file(bfc, &sg.sg_header); +err: + if (result) { + /* Error, rollback file changes */ + truncate_backing_file(bfc, rollback_pos); + } + return result; +} + +/* + * Write a backing file header + * It should always be called only on empty file. + * incfs_super_block.s_first_md_offset is 0 for now, but will be updated + * once first metadata record is added. + */ +int incfs_write_fh_to_backing_file(struct backing_file_context *bfc, + incfs_uuid_t *uuid, u64 file_size) +{ + struct incfs_file_header fh = {}; + loff_t file_pos = 0; + + if (!bfc) + return -EFAULT; + + fh.fh_magic = cpu_to_le64(INCFS_MAGIC_NUMBER); + fh.fh_version = cpu_to_le64(INCFS_FORMAT_CURRENT_VER); + fh.fh_header_size = cpu_to_le16(sizeof(fh)); + fh.fh_first_md_offset = cpu_to_le64(0); + fh.fh_data_block_size = cpu_to_le16(INCFS_DATA_FILE_BLOCK_SIZE); + + fh.fh_file_size = cpu_to_le64(file_size); + fh.fh_uuid = *uuid; + + LOCK_REQUIRED(bfc->bc_mutex); + + file_pos = incfs_get_end_offset(bfc->bc_file); + if (file_pos != 0) + return -EEXIST; + + return write_to_bf(bfc, &fh, sizeof(fh), file_pos, true); +} + +/* Write a given data block and update file's blockmap to point it. */ +int incfs_write_data_block_to_backing_file(struct backing_file_context *bfc, + struct mem_range block, int block_index, + loff_t bm_base_off, u16 flags) +{ + struct incfs_blockmap_entry bm_entry = {}; + int result = 0; + loff_t data_offset = 0; + loff_t bm_entry_off = + bm_base_off + sizeof(struct incfs_blockmap_entry) * block_index; + + if (!bfc) + return -EFAULT; + + if (block.len >= (1 << 16) || block_index < 0) + return -EINVAL; + + LOCK_REQUIRED(bfc->bc_mutex); + + data_offset = incfs_get_end_offset(bfc->bc_file); + if (data_offset <= bm_entry_off) { + /* Blockmap entry is beyond the file's end. It is not normal. */ + return -EINVAL; + } + + /* Write the block data at the end of the backing file. */ + result = write_to_bf(bfc, block.data, block.len, data_offset, false); + if (result) + return result; + + /* Update the blockmap to point to the newly written data. */ + bm_entry.me_data_offset_lo = cpu_to_le32((u32)data_offset); + bm_entry.me_data_offset_hi = cpu_to_le16((u16)(data_offset >> 32)); + bm_entry.me_data_size = cpu_to_le16((u16)block.len); + bm_entry.me_flags = cpu_to_le16(flags); + + result = write_to_bf(bfc, &bm_entry, sizeof(bm_entry), + bm_entry_off, false); + return result; +} + +int incfs_write_hash_block_to_backing_file(struct backing_file_context *bfc, + struct mem_range block, + int block_index, loff_t hash_area_off) +{ + loff_t data_offset = 0; + loff_t file_end = 0; + + + if (!bfc) + return -EFAULT; + + LOCK_REQUIRED(bfc->bc_mutex); + + data_offset = hash_area_off + block_index * INCFS_DATA_FILE_BLOCK_SIZE; + file_end = incfs_get_end_offset(bfc->bc_file); + if (data_offset + block.len > file_end) { + /* Block is located beyond the file's end. It is not normal. */ + return -EINVAL; + } + + return write_to_bf(bfc, block.data, block.len, data_offset, false); +} + +/* Initialize a new image in a given backing file. */ +int incfs_make_empty_backing_file(struct backing_file_context *bfc, + incfs_uuid_t *uuid, u64 file_size) +{ + int result = 0; + + if (!bfc || !bfc->bc_file) + return -EFAULT; + + result = mutex_lock_interruptible(&bfc->bc_mutex); + if (result) + goto out; + + result = truncate_backing_file(bfc, 0); + if (result) + goto out; + + result = incfs_write_fh_to_backing_file(bfc, uuid, file_size); +out: + mutex_unlock(&bfc->bc_mutex); + return result; +} + +int incfs_read_blockmap_entry(struct backing_file_context *bfc, int block_index, + loff_t bm_base_off, + struct incfs_blockmap_entry *bm_entry) +{ + return incfs_read_blockmap_entries(bfc, bm_entry, block_index, 1, + bm_base_off); +} + +int incfs_read_blockmap_entries(struct backing_file_context *bfc, + struct incfs_blockmap_entry *entries, + int start_index, int blocks_number, + loff_t bm_base_off) +{ + loff_t bm_entry_off = + bm_base_off + sizeof(struct incfs_blockmap_entry) * start_index; + const size_t bytes_to_read = sizeof(struct incfs_blockmap_entry) + * blocks_number; + int result = 0; + + if (!bfc || !entries) + return -EFAULT; + + if (start_index < 0 || bm_base_off <= 0) + return -ENODATA; + + result = incfs_kread(bfc->bc_file, entries, bytes_to_read, + bm_entry_off); + if (result < 0) + return result; + if (result < bytes_to_read) + return -EIO; + return 0; +} + + +int incfs_read_file_header(struct backing_file_context *bfc, + loff_t *first_md_off, incfs_uuid_t *uuid, + u64 *file_size) +{ + ssize_t bytes_read = 0; + struct incfs_file_header fh = {}; + + if (!bfc || !first_md_off) + return -EFAULT; + + LOCK_REQUIRED(bfc->bc_mutex); + bytes_read = incfs_kread(bfc->bc_file, &fh, sizeof(fh), 0); + if (bytes_read < 0) + return bytes_read; + + if (bytes_read < sizeof(fh)) + return -EBADMSG; + + if (le64_to_cpu(fh.fh_magic) != INCFS_MAGIC_NUMBER) + return -EILSEQ; + + if (le64_to_cpu(fh.fh_version) > INCFS_FORMAT_CURRENT_VER) + return -EILSEQ; + + if (le16_to_cpu(fh.fh_data_block_size) != INCFS_DATA_FILE_BLOCK_SIZE) + return -EILSEQ; + + if (le16_to_cpu(fh.fh_header_size) != sizeof(fh)) + return -EILSEQ; + + if (first_md_off) + *first_md_off = le64_to_cpu(fh.fh_first_md_offset); + if (uuid) + *uuid = fh.fh_uuid; + if (file_size) + *file_size = le64_to_cpu(fh.fh_file_size); + return 0; +} + +/* + * Read through metadata records from the backing file one by one + * and call provided metadata handlers. + */ +int incfs_read_next_metadata_record(struct backing_file_context *bfc, + struct metadata_handler *handler) +{ + const ssize_t max_md_size = INCFS_MAX_METADATA_RECORD_SIZE; + ssize_t bytes_read = 0; + size_t md_record_size = 0; + loff_t next_record = 0; + loff_t prev_record = 0; + int res = 0; + struct incfs_md_header *md_hdr = NULL; + + if (!bfc || !handler) + return -EFAULT; + + LOCK_REQUIRED(bfc->bc_mutex); + + if (handler->md_record_offset == 0) + return -EPERM; + + memset(&handler->md_buffer, 0, max_md_size); + bytes_read = incfs_kread(bfc->bc_file, &handler->md_buffer, + max_md_size, handler->md_record_offset); + if (bytes_read < 0) + return bytes_read; + if (bytes_read < sizeof(*md_hdr)) + return -EBADMSG; + + md_hdr = &handler->md_buffer.md_header; + next_record = le64_to_cpu(md_hdr->h_next_md_offset); + prev_record = le64_to_cpu(md_hdr->h_prev_md_offset); + md_record_size = le16_to_cpu(md_hdr->h_record_size); + + if (md_record_size > max_md_size) { + pr_warn("incfs: The record is too large. Size: %ld", + md_record_size); + return -EBADMSG; + } + + if (bytes_read < md_record_size) { + pr_warn("incfs: The record hasn't been fully read."); + return -EBADMSG; + } + + if (next_record <= handler->md_record_offset && next_record != 0) { + pr_warn("incfs: Next record (%lld) points back in file.", + next_record); + return -EBADMSG; + } + + if (prev_record != handler->md_prev_record_offset) { + pr_warn("incfs: Metadata chain has been corrupted."); + return -EBADMSG; + } + + if (le32_to_cpu(md_hdr->h_record_crc) != calc_md_crc(md_hdr)) { + pr_warn("incfs: Metadata CRC mismatch."); + return -EBADMSG; + } + + switch (md_hdr->h_md_entry_type) { + case INCFS_MD_NONE: + break; + case INCFS_MD_BLOCK_MAP: + if (handler->handle_blockmap) + res = handler->handle_blockmap( + &handler->md_buffer.blockmap, handler); + break; + case INCFS_MD_FILE_ATTR: + if (handler->handle_file_attr) + res = handler->handle_file_attr( + &handler->md_buffer.file_attr, handler); + break; + case INCFS_MD_SIGNATURE: + if (handler->handle_signature) + res = handler->handle_signature( + &handler->md_buffer.signature, handler); + break; + default: + res = -ENOTSUPP; + break; + } + + if (!res) { + if (next_record == 0) { + /* + * Zero offset for the next record means that the last + * metadata record has just been processed. + */ + bfc->bc_last_md_record_offset = + handler->md_record_offset; + } + handler->md_prev_record_offset = handler->md_record_offset; + handler->md_record_offset = next_record; + } + return res; +} + +ssize_t incfs_kread(struct file *f, void *buf, size_t size, loff_t pos) +{ + return kernel_read(f, buf, size, &pos); +} + +ssize_t incfs_kwrite(struct file *f, const void *buf, size_t size, loff_t pos) +{ + return kernel_write(f, buf, size, &pos); +} diff --git a/fs/incfs/format.h b/fs/incfs/format.h new file mode 100644 index 0000000000000000000000000000000000000000..a86881482e19110106b02033b744eb3d6877b10d --- /dev/null +++ b/fs/incfs/format.h @@ -0,0 +1,349 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2018 Google LLC + */ + +/* + * Overview + * -------- + * The backbone of the incremental-fs ondisk format is an append only linked + * list of metadata blocks. Each metadata block contains an offset of the next + * one. These blocks describe files and directories on the + * file system. They also represent actions of adding and removing file names + * (hard links). + * + * Every time incremental-fs instance is mounted, it reads through this list + * to recreate filesystem's state in memory. An offset of the first record in + * the metadata list is stored in the superblock at the beginning of the backing + * file. + * + * Most of the backing file is taken by data areas and blockmaps. + * Since data blocks can be compressed and have different sizes, + * single per-file data area can't be pre-allocated. That's why blockmaps are + * needed in order to find a location and size of each data block in + * the backing file. Each time a file is created, a corresponding block map is + * allocated to store future offsets of data blocks. + * + * Whenever a data block is given by data loader to incremental-fs: + * - A data area with the given block is appended to the end of + * the backing file. + * - A record in the blockmap for the given block index is updated to reflect + * its location, size, and compression algorithm. + + * Metadata records + * ---------------- + * incfs_blockmap - metadata record that specifies size and location + * of a blockmap area for a given file. This area + * contains an array of incfs_blockmap_entry-s. + * incfs_file_signature - metadata record that specifies where file signature + * and its hash tree can be found in the backing file. + * + * incfs_file_attr - metadata record that specifies where additional file + * attributes blob can be found. + * + * Metadata header + * --------------- + * incfs_md_header - header of a metadata record. It's always a part + * of other structures and served purpose of metadata + * bookkeeping. + * + * +-----------------------------------------------+ ^ + * | incfs_md_header | | + * | 1. type of body(BLOCKMAP, FILE_ATTR..) | | + * | 2. size of the whole record header + body | | + * | 3. CRC the whole record header + body | | + * | 4. offset of the previous md record |]------+ + * | 5. offset of the next md record (md link) |]---+ + * +-----------------------------------------------+ | + * | Metadata record body with useful data | | + * +-----------------------------------------------+ | + * +---> + * + * Other ondisk structures + * ----------------------- + * incfs_super_block - backing file header + * incfs_blockmap_entry - a record in a blockmap area that describes size + * and location of a data block. + * Data blocks dont have any particular structure, they are written to the + * backing file in a raw form as they come from a data loader. + * + * Backing file layout + * ------------------- + * + * + * +-------------------------------------------+ + * | incfs_super_block |]---+ + * +-------------------------------------------+ | + * | metadata |<---+ + * | incfs_file_signature |]---+ + * +-------------------------------------------+ | + * ......................... | + * +-------------------------------------------+ | metadata + * +------->| blockmap area | | list links + * | | [incfs_blockmap_entry] | | + * | | [incfs_blockmap_entry] | | + * | | [incfs_blockmap_entry] | | + * | +--[| [incfs_blockmap_entry] | | + * | | | [incfs_blockmap_entry] | | + * | | | [incfs_blockmap_entry] | | + * | | +-------------------------------------------+ | + * | | ......................... | + * | | +-------------------------------------------+ | + * | | | metadata |<---+ + * +----|--[| incfs_blockmap |]---+ + * | +-------------------------------------------+ | + * | ......................... | + * | +-------------------------------------------+ | + * +-->| data block | | + * +-------------------------------------------+ | + * ......................... | + * +-------------------------------------------+ | + * | metadata |<---+ + * | incfs_file_attr | + * +-------------------------------------------+ + */ +#ifndef _INCFS_FORMAT_H +#define _INCFS_FORMAT_H +#include +#include +#include + +#include "internal.h" + +#define INCFS_MAX_NAME_LEN 255 +#define INCFS_FORMAT_V1 1 +#define INCFS_FORMAT_CURRENT_VER INCFS_FORMAT_V1 + +enum incfs_metadata_type { + INCFS_MD_NONE = 0, + INCFS_MD_BLOCK_MAP = 1, + INCFS_MD_FILE_ATTR = 2, + INCFS_MD_SIGNATURE = 3 +}; + +/* Header included at the beginning of all metadata records on the disk. */ +struct incfs_md_header { + __u8 h_md_entry_type; + + /* + * Size of the metadata record. + * (e.g. inode, dir entry etc) not just this struct. + */ + __le16 h_record_size; + + /* + * CRC32 of the metadata record. + * (e.g. inode, dir entry etc) not just this struct. + */ + __le32 h_record_crc; + + /* Offset of the next metadata entry if any */ + __le64 h_next_md_offset; + + /* Offset of the previous metadata entry if any */ + __le64 h_prev_md_offset; + +} __packed; + +/* Backing file header */ +struct incfs_file_header { + /* Magic number: INCFS_MAGIC_NUMBER */ + __le64 fh_magic; + + /* Format version: INCFS_FORMAT_CURRENT_VER */ + __le64 fh_version; + + /* sizeof(incfs_file_header) */ + __le16 fh_header_size; + + /* INCFS_DATA_FILE_BLOCK_SIZE */ + __le16 fh_data_block_size; + + /* Padding, also reserved for future use. */ + __le32 fh_dummy; + + /* Offset of the first metadata record */ + __le64 fh_first_md_offset; + + /* + * Put file specific information after this point + */ + + /* Full size of the file's content */ + __le64 fh_file_size; + + /* File uuid */ + incfs_uuid_t fh_uuid; +} __packed; + +enum incfs_block_map_entry_flags { + INCFS_BLOCK_COMPRESSED_LZ4 = (1 << 0), +}; + +/* Block map entry pointing to an actual location of the data block. */ +struct incfs_blockmap_entry { + /* Offset of the actual data block. Lower 32 bits */ + __le32 me_data_offset_lo; + + /* Offset of the actual data block. Higher 16 bits */ + __le16 me_data_offset_hi; + + /* How many bytes the data actually occupies in the backing file */ + __le16 me_data_size; + + /* Block flags from incfs_block_map_entry_flags */ + __le16 me_flags; +} __packed; + +/* Metadata record for locations of file blocks. Type = INCFS_MD_BLOCK_MAP */ +struct incfs_blockmap { + struct incfs_md_header m_header; + + /* Base offset of the array of incfs_blockmap_entry */ + __le64 m_base_offset; + + /* Size of the map entry array in blocks */ + __le32 m_block_count; +} __packed; + +/* Metadata record for file attribute. Type = INCFS_MD_FILE_ATTR */ +struct incfs_file_attr { + struct incfs_md_header fa_header; + + __le64 fa_offset; + + __le16 fa_size; + + __le32 fa_crc; +} __packed; + +/* Metadata record for file attribute. Type = INCFS_MD_SIGNATURE */ +struct incfs_file_signature { + struct incfs_md_header sg_header; + + __u8 sg_hash_alg; /* Value from incfs_hash_tree_algorithm */ + + __le32 sg_hash_tree_size; /* The size of the hash tree. */ + + __le64 sg_hash_tree_offset; /* Hash tree offset in the backing file */ + + __u8 sg_root_hash[INCFS_MAX_HASH_SIZE]; + + __le32 sg_sig_size; /* The size of the pkcs7 signature. */ + + __le64 sg_sig_offset; /* pkcs7 signature's offset in the backing file */ + + __le32 sg_add_data_size; /* The size of the additional data. */ + + __le64 sg_add_data_offset; /* Additional data's offset */ +} __packed; + +/* State of the backing file. */ +struct backing_file_context { + /* Protects writes to bc_file */ + struct mutex bc_mutex; + + /* File object to read data from */ + struct file *bc_file; + + /* + * Offset of the last known metadata record in the backing file. + * 0 means there are no metadata records. + */ + loff_t bc_last_md_record_offset; +}; + + +/* Backing file locations of things required for signature validation. */ +struct ondisk_signature { + + loff_t add_data_offset; /* Additional data's offset */ + + loff_t sig_offset; /* pkcs7 signature's offset in the backing file */ + + loff_t mtree_offset; /* Backing file offset of the hash tree. */ + + u32 add_data_size; /* The size of the additional data. */ + + u32 sig_size; /* The size of the pkcs7 signature. */ + + u32 mtree_size; /* The size of the hash tree. */ +}; + +struct metadata_handler { + loff_t md_record_offset; + loff_t md_prev_record_offset; + void *context; + + union { + struct incfs_md_header md_header; + struct incfs_blockmap blockmap; + struct incfs_file_attr file_attr; + struct incfs_file_signature signature; + } md_buffer; + + int (*handle_blockmap)(struct incfs_blockmap *bm, + struct metadata_handler *handler); + int (*handle_file_attr)(struct incfs_file_attr *fa, + struct metadata_handler *handler); + int (*handle_signature)(struct incfs_file_signature *sig, + struct metadata_handler *handler); +}; +#define INCFS_MAX_METADATA_RECORD_SIZE \ + FIELD_SIZEOF(struct metadata_handler, md_buffer) + +loff_t incfs_get_end_offset(struct file *f); + +/* Backing file context management */ +struct backing_file_context *incfs_alloc_bfc(struct file *backing_file); + +void incfs_free_bfc(struct backing_file_context *bfc); + +/* Writing stuff */ +int incfs_write_blockmap_to_backing_file(struct backing_file_context *bfc, + u32 block_count, loff_t *map_base_off); + +int incfs_write_fh_to_backing_file(struct backing_file_context *bfc, + incfs_uuid_t *uuid, u64 file_size); + +int incfs_write_data_block_to_backing_file(struct backing_file_context *bfc, + struct mem_range block, + int block_index, loff_t bm_base_off, + u16 flags); + +int incfs_write_hash_block_to_backing_file(struct backing_file_context *bfc, + struct mem_range block, + int block_index, loff_t hash_area_off); + +int incfs_write_file_attr_to_backing_file(struct backing_file_context *bfc, + struct mem_range value, struct incfs_file_attr *attr); + +int incfs_write_signature_to_backing_file(struct backing_file_context *bfc, + u8 hash_alg, u32 tree_size, + struct mem_range root_hash, struct mem_range add_data, + struct mem_range sig); + +int incfs_make_empty_backing_file(struct backing_file_context *bfc, + incfs_uuid_t *uuid, u64 file_size); + +/* Reading stuff */ +int incfs_read_file_header(struct backing_file_context *bfc, + loff_t *first_md_off, incfs_uuid_t *uuid, + u64 *file_size); + +int incfs_read_blockmap_entry(struct backing_file_context *bfc, int block_index, + loff_t bm_base_off, + struct incfs_blockmap_entry *bm_entry); + +int incfs_read_blockmap_entries(struct backing_file_context *bfc, + struct incfs_blockmap_entry *entries, + int start_index, int blocks_number, + loff_t bm_base_off); + +int incfs_read_next_metadata_record(struct backing_file_context *bfc, + struct metadata_handler *handler); + +ssize_t incfs_kread(struct file *f, void *buf, size_t size, loff_t pos); +ssize_t incfs_kwrite(struct file *f, const void *buf, size_t size, loff_t pos); + +#endif /* _INCFS_FORMAT_H */ diff --git a/fs/incfs/integrity.c b/fs/incfs/integrity.c new file mode 100644 index 0000000000000000000000000000000000000000..feb212c38945721140b3147726080c14de1c6f4f --- /dev/null +++ b/fs/incfs/integrity.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 Google LLC + */ +#include +#include +#include +#include +#include + +#include "integrity.h" + +int incfs_validate_pkcs7_signature(struct mem_range pkcs7_blob, + struct mem_range root_hash, struct mem_range add_data) +{ + struct pkcs7_message *pkcs7 = NULL; + const void *data = NULL; + size_t data_len = 0; + const char *p; + int err; + + pkcs7 = pkcs7_parse_message(pkcs7_blob.data, pkcs7_blob.len); + if (IS_ERR(pkcs7)) { + pr_debug("PKCS#7 parsing error. ptr=%p size=%ld err=%ld\n", + pkcs7_blob.data, pkcs7_blob.len, -PTR_ERR(pkcs7)); + return PTR_ERR(pkcs7); + } + + err = pkcs7_get_content_data(pkcs7, &data, &data_len, NULL); + if (err || data_len == 0 || data == NULL) { + pr_debug("PKCS#7 message does not contain data\n"); + err = -EBADMSG; + goto out; + } + + if (root_hash.len == 0) { + pr_debug("Root hash is empty.\n"); + err = -EBADMSG; + goto out; + } + + if (data_len != root_hash.len + add_data.len) { + pr_debug("PKCS#7 data size doesn't match arguments.\n"); + err = -EKEYREJECTED; + goto out; + } + + p = data; + if (memcmp(p, root_hash.data, root_hash.len) != 0) { + pr_debug("Root hash mismatch.\n"); + err = -EKEYREJECTED; + goto out; + } + p += root_hash.len; + if (memcmp(p, add_data.data, add_data.len) != 0) { + pr_debug("Additional data mismatch.\n"); + err = -EKEYREJECTED; + goto out; + } + + err = pkcs7_verify(pkcs7, VERIFYING_UNSPECIFIED_SIGNATURE); + if (err) + pr_debug("PKCS#7 signature verification error: %d\n", -err); + + /* + * RSA signature verification sometimes returns unexpected error codes + * when signature doesn't match. + */ + if (err == -ERANGE || err == -EINVAL) + err = -EBADMSG; + +out: + pkcs7_free_message(pkcs7); + return err; +} + +struct incfs_hash_alg *incfs_get_hash_alg(enum incfs_hash_tree_algorithm id) +{ + static struct incfs_hash_alg sha256 = { + .name = "sha256", + .digest_size = SHA256_DIGEST_SIZE, + .id = INCFS_HASH_TREE_SHA256 + }; + struct incfs_hash_alg *result = NULL; + struct crypto_shash *shash; + + if (id == INCFS_HASH_TREE_SHA256) { + BUILD_BUG_ON(INCFS_MAX_HASH_SIZE < SHA256_DIGEST_SIZE); + result = &sha256; + } + + if (result == NULL) + return ERR_PTR(-ENOENT); + + /* pairs with cmpxchg_release() below */ + shash = smp_load_acquire(&result->shash); + if (shash) + return result; + + shash = crypto_alloc_shash(result->name, 0, 0); + if (IS_ERR(shash)) { + int err = PTR_ERR(shash); + + pr_err("Can't allocate hash alg %s, error code:%d", + result->name, err); + return ERR_PTR(err); + } + + /* pairs with smp_load_acquire() above */ + if (cmpxchg_release(&result->shash, NULL, shash) != NULL) + crypto_free_shash(shash); + + return result; +} + + +struct mtree *incfs_alloc_mtree(enum incfs_hash_tree_algorithm id, + int data_block_count, + struct mem_range root_hash) +{ + struct mtree *result = NULL; + struct incfs_hash_alg *hash_alg = NULL; + int hash_per_block; + int lvl; + int total_blocks = 0; + int blocks_in_level[INCFS_MAX_MTREE_LEVELS]; + int blocks = data_block_count; + + if (data_block_count <= 0) + return ERR_PTR(-EINVAL); + + hash_alg = incfs_get_hash_alg(id); + if (IS_ERR(hash_alg)) + return ERR_PTR(PTR_ERR(hash_alg)); + + if (root_hash.len < hash_alg->digest_size) + return ERR_PTR(-EINVAL); + + result = kzalloc(sizeof(*result), GFP_NOFS); + if (!result) + return ERR_PTR(-ENOMEM); + + result->alg = hash_alg; + hash_per_block = INCFS_DATA_FILE_BLOCK_SIZE / result->alg->digest_size; + + /* Calculating tree geometry. */ + /* First pass: calculate how many blocks in each tree level. */ + for (lvl = 0; blocks > 1; lvl++) { + if (lvl >= INCFS_MAX_MTREE_LEVELS) { + pr_err("incfs: too much data in mtree"); + goto err; + } + + blocks = (blocks + hash_per_block - 1) / hash_per_block; + blocks_in_level[lvl] = blocks; + total_blocks += blocks; + } + result->depth = lvl; + result->hash_tree_area_size = total_blocks * INCFS_DATA_FILE_BLOCK_SIZE; + if (result->hash_tree_area_size > INCFS_MAX_HASH_AREA_SIZE) + goto err; + + blocks = 0; + /* Second pass: calculate offset of each level. 0th level goes last. */ + for (lvl = 0; lvl < result->depth; lvl++) { + u32 suboffset; + + blocks += blocks_in_level[lvl]; + suboffset = (total_blocks - blocks) + * INCFS_DATA_FILE_BLOCK_SIZE; + + result->hash_level_suboffset[lvl] = suboffset; + } + + /* Root hash is stored separately from the rest of the tree. */ + memcpy(result->root_hash, root_hash.data, hash_alg->digest_size); + return result; + +err: + kfree(result); + return ERR_PTR(-E2BIG); +} + +void incfs_free_mtree(struct mtree *tree) +{ + kfree(tree); +} + +int incfs_calc_digest(struct incfs_hash_alg *alg, struct mem_range data, + struct mem_range digest) +{ + SHASH_DESC_ON_STACK(desc, alg->shash); + + if (!alg || !alg->shash || !data.data || !digest.data) + return -EFAULT; + + if (alg->digest_size > digest.len) + return -EINVAL; + + desc->tfm = alg->shash; + return crypto_shash_digest(desc, data.data, data.len, digest.data); +} + +void incfs_free_signature_info(struct signature_info *si) +{ + if (!si) + return; + kfree(si->root_hash.data); + kfree(si->additional_data.data); + kfree(si->signature.data); + kfree(si); +} + diff --git a/fs/incfs/integrity.h b/fs/incfs/integrity.h new file mode 100644 index 0000000000000000000000000000000000000000..da1c38486b2fee5a170094c52ddd7c0af4f89987 --- /dev/null +++ b/fs/incfs/integrity.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2019 Google LLC + */ +#ifndef _INCFS_INTEGRITY_H +#define _INCFS_INTEGRITY_H +#include +#include +#include + +#include + +#include "internal.h" + +#define INCFS_MAX_MTREE_LEVELS 8 +#define INCFS_MAX_HASH_AREA_SIZE (1280 * 1024 * 1024) + +struct incfs_hash_alg { + const char *name; + int digest_size; + enum incfs_hash_tree_algorithm id; + + struct crypto_shash *shash; +}; + +/* Merkle tree structure. */ +struct mtree { + struct incfs_hash_alg *alg; + + u8 root_hash[INCFS_MAX_HASH_SIZE]; + + /* Offset of each hash level in the hash area. */ + u32 hash_level_suboffset[INCFS_MAX_MTREE_LEVELS]; + + u32 hash_tree_area_size; + + /* Number of levels in hash_level_suboffset */ + int depth; +}; + +struct signature_info { + struct mem_range root_hash; + + struct mem_range additional_data; + + struct mem_range signature; + + enum incfs_hash_tree_algorithm hash_alg; +}; + +struct incfs_hash_alg *incfs_get_hash_alg(enum incfs_hash_tree_algorithm id); + +struct mtree *incfs_alloc_mtree(enum incfs_hash_tree_algorithm id, + int data_block_count, + struct mem_range root_hash); + +void incfs_free_mtree(struct mtree *tree); + +size_t incfs_get_mtree_depth(enum incfs_hash_tree_algorithm alg, loff_t size); + +size_t incfs_get_mtree_hash_count(enum incfs_hash_tree_algorithm alg, + loff_t size); + +int incfs_calc_digest(struct incfs_hash_alg *alg, struct mem_range data, + struct mem_range digest); + +int incfs_validate_pkcs7_signature(struct mem_range pkcs7_blob, + struct mem_range root_hash, struct mem_range add_data); + +void incfs_free_signature_info(struct signature_info *si); + +#endif /* _INCFS_INTEGRITY_H */ diff --git a/fs/incfs/internal.h b/fs/incfs/internal.h new file mode 100644 index 0000000000000000000000000000000000000000..0a85eaed41d3a8273925acd6fe7eb91c8f1a281f --- /dev/null +++ b/fs/incfs/internal.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2018 Google LLC + */ +#ifndef _INCFS_INTERNAL_H +#define _INCFS_INTERNAL_H +#include + +struct mem_range { + u8 *data; + size_t len; +}; + +static inline struct mem_range range(u8 *data, size_t len) +{ + return (struct mem_range){ .data = data, .len = len }; +} + +#define LOCK_REQUIRED(lock) WARN_ON_ONCE(!mutex_is_locked(&lock)) + +#endif /* _INCFS_INTERNAL_H */ diff --git a/fs/incfs/main.c b/fs/incfs/main.c new file mode 100644 index 0000000000000000000000000000000000000000..e65d0d895128e1b5a2be0698de3fa5fee6a3b21c --- /dev/null +++ b/fs/incfs/main.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2018 Google LLC + */ +#include +#include +#include + +#include + +#include "vfs.h" + +#define INCFS_NODE_FEATURES "features" + +static struct file_system_type incfs_fs_type = { + .owner = THIS_MODULE, + .name = INCFS_NAME, + .mount = incfs_mount_fs, + .kill_sb = incfs_kill_sb, + .fs_flags = 0 +}; + +static struct kobject *sysfs_root, *featurefs_root; + +static ssize_t corefs_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buff) +{ + return snprintf(buff, PAGE_SIZE, "supported\n"); +} + +static struct kobj_attribute corefs_attr = __ATTR_RO(corefs); + +static struct attribute *attributes[] = { + &corefs_attr.attr, + NULL, +}; + +static const struct attribute_group attr_group = { + .attrs = attributes, +}; + +static int __init init_sysfs(void) +{ + int res = 0; + + sysfs_root = kobject_create_and_add(INCFS_NAME, fs_kobj); + if (!sysfs_root) + return -ENOMEM; + + featurefs_root = kobject_create_and_add(INCFS_NODE_FEATURES, + sysfs_root); + if (!featurefs_root) + return -ENOMEM; + + res = sysfs_create_group(featurefs_root, &attr_group); + if (res) { + kobject_put(sysfs_root); + sysfs_root = NULL; + } + return res; +} + +static void cleanup_sysfs(void) +{ + if (featurefs_root) { + sysfs_remove_group(featurefs_root, &attr_group); + kobject_put(featurefs_root); + featurefs_root = NULL; + } + + if (sysfs_root) { + kobject_put(sysfs_root); + sysfs_root = NULL; + } +} + +static int __init init_incfs_module(void) +{ + int err = 0; + + err = init_sysfs(); + if (err) + return err; + + err = register_filesystem(&incfs_fs_type); + if (err) + cleanup_sysfs(); + + return err; +} + +static void __exit cleanup_incfs_module(void) +{ + cleanup_sysfs(); + unregister_filesystem(&incfs_fs_type); +} + +module_init(init_incfs_module); +module_exit(cleanup_incfs_module); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Eugene Zemtsov "); +MODULE_DESCRIPTION("Incremental File System"); diff --git a/fs/incfs/vfs.c b/fs/incfs/vfs.c new file mode 100644 index 0000000000000000000000000000000000000000..29bfda457e61caa95ddda4998bb1220fbba12f8c --- /dev/null +++ b/fs/incfs/vfs.c @@ -0,0 +1,2281 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2018 Google LLC + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "vfs.h" +#include "data_mgmt.h" +#include "format.h" +#include "integrity.h" +#include "internal.h" + +#define INCFS_PENDING_READS_INODE 2 +#define INCFS_LOG_INODE 3 +#define INCFS_START_INO_RANGE 10 +#define READ_FILE_MODE 0444 +#define READ_EXEC_FILE_MODE 0555 +#define READ_WRITE_FILE_MODE 0666 + +static int incfs_remount_fs(struct super_block *sb, int *flags, char *data); + +static int dentry_revalidate(struct dentry *dentry, unsigned int flags); +static void dentry_release(struct dentry *d); + +static int iterate_incfs_dir(struct file *file, struct dir_context *ctx); +static struct dentry *dir_lookup(struct inode *dir_inode, + struct dentry *dentry, unsigned int flags); +static int dir_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode); +static int dir_unlink(struct inode *dir, struct dentry *dentry); +static int dir_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *new_dentry); +static int dir_rmdir(struct inode *dir, struct dentry *dentry); +static int dir_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry); + +static int file_open(struct inode *inode, struct file *file); +static int file_release(struct inode *inode, struct file *file); +static ssize_t file_write(struct file *f, const char __user *buf, + size_t size, loff_t *offset); +static int read_single_page(struct file *f, struct page *page); +static long dispatch_ioctl(struct file *f, unsigned int req, unsigned long arg); + +static ssize_t pending_reads_read(struct file *f, char __user *buf, size_t len, + loff_t *ppos); +static __poll_t pending_reads_poll(struct file *file, poll_table *wait); +static int pending_reads_open(struct inode *inode, struct file *file); +static int pending_reads_release(struct inode *, struct file *); + +static ssize_t log_read(struct file *f, char __user *buf, size_t len, + loff_t *ppos); +static __poll_t log_poll(struct file *file, poll_table *wait); +static int log_open(struct inode *inode, struct file *file); +static int log_release(struct inode *, struct file *); + +static struct inode *alloc_inode(struct super_block *sb); +static void free_inode(struct inode *inode); +static void evict_inode(struct inode *inode); + +static ssize_t incfs_getxattr(struct dentry *d, const char *name, + void *value, size_t size); +static ssize_t incfs_setxattr(struct dentry *d, const char *name, + const void *value, size_t size, int flags); +static ssize_t incfs_listxattr(struct dentry *d, char *list, size_t size); + +static int show_options(struct seq_file *, struct dentry *); + +static const struct super_operations incfs_super_ops = { + .statfs = simple_statfs, + .remount_fs = incfs_remount_fs, + .alloc_inode = alloc_inode, + .destroy_inode = free_inode, + .evict_inode = evict_inode, + .show_options = show_options +}; + +static int dir_rename_wrap(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry, + unsigned int flags) +{ + return dir_rename(old_dir, old_dentry, new_dir, new_dentry); +} + +static const struct inode_operations incfs_dir_inode_ops = { + .lookup = dir_lookup, + .mkdir = dir_mkdir, + .rename = dir_rename_wrap, + .unlink = dir_unlink, + .link = dir_link, + .rmdir = dir_rmdir +}; + +static const struct file_operations incfs_dir_fops = { + .llseek = generic_file_llseek, + .read = generic_read_dir, + .iterate = iterate_incfs_dir, + .open = file_open, + .release = file_release, + .unlocked_ioctl = dispatch_ioctl, + .compat_ioctl = dispatch_ioctl +}; + +static const struct dentry_operations incfs_dentry_ops = { + .d_revalidate = dentry_revalidate, + .d_release = dentry_release +}; + +static const struct address_space_operations incfs_address_space_ops = { + .readpage = read_single_page, + /* .readpages = readpages */ +}; + +static const struct file_operations incfs_file_ops = { + .open = file_open, + .release = file_release, + .write = file_write, + .read_iter = generic_file_read_iter, + .mmap = generic_file_mmap, + .splice_read = generic_file_splice_read, + .llseek = generic_file_llseek, + .unlocked_ioctl = dispatch_ioctl, + .compat_ioctl = dispatch_ioctl +}; + +static const struct file_operations incfs_pending_read_file_ops = { + .read = pending_reads_read, + .poll = pending_reads_poll, + .open = pending_reads_open, + .release = pending_reads_release, + .llseek = noop_llseek, + .unlocked_ioctl = dispatch_ioctl, + .compat_ioctl = dispatch_ioctl +}; + +static const struct file_operations incfs_log_file_ops = { + .read = log_read, + .poll = log_poll, + .open = log_open, + .release = log_release, + .llseek = noop_llseek, + .unlocked_ioctl = dispatch_ioctl, + .compat_ioctl = dispatch_ioctl +}; + +static const struct inode_operations incfs_file_inode_ops = { + .setattr = simple_setattr, + .getattr = simple_getattr, + .listxattr = incfs_listxattr +}; + +static int incfs_handler_getxattr(const struct xattr_handler *xh, + struct dentry *d, struct inode *inode, + const char *name, void *buffer, size_t size) +{ + return incfs_getxattr(d, name, buffer, size); +} + +static int incfs_handler_setxattr(const struct xattr_handler *xh, + struct dentry *d, struct inode *inode, + const char *name, const void *buffer, + size_t size, int flags) +{ + return incfs_setxattr(d, name, buffer, size, flags); +} + +static const struct xattr_handler incfs_xattr_handler = { + .prefix = "", /* AKA all attributes */ + .get = incfs_handler_getxattr, + .set = incfs_handler_setxattr, +}; + +static const struct xattr_handler *incfs_xattr_ops[] = { + &incfs_xattr_handler, + NULL, +}; + +/* State of an open .pending_reads file, unique for each file descriptor. */ +struct pending_reads_state { + /* A serial number of the last pending read obtained from this file. */ + int last_pending_read_sn; +}; + +/* State of an open .log file, unique for each file descriptor. */ +struct log_file_state { + struct read_log_state state; +}; + +struct inode_search { + unsigned long ino; + + struct dentry *backing_dentry; +}; + +enum parse_parameter { + Opt_read_timeout, + Opt_readahead_pages, + Opt_no_backing_file_cache, + Opt_no_backing_file_readahead, + Opt_rlog_pages, + Opt_rlog_wakeup_cnt, + Opt_err +}; + +static const char pending_reads_file_name[] = INCFS_PENDING_READS_FILENAME; +static struct mem_range pending_reads_file_name_range = { + .data = (u8 *)pending_reads_file_name, + .len = ARRAY_SIZE(pending_reads_file_name) - 1 +}; + +static const char log_file_name[] = INCFS_LOG_FILENAME; +static struct mem_range log_file_name_range = { + .data = (u8 *)log_file_name, + .len = ARRAY_SIZE(log_file_name) - 1 +}; + +static const match_table_t option_tokens = { + { Opt_read_timeout, "read_timeout_ms=%u" }, + { Opt_readahead_pages, "readahead=%u" }, + { Opt_no_backing_file_cache, "no_bf_cache=%u" }, + { Opt_no_backing_file_readahead, "no_bf_readahead=%u" }, + { Opt_rlog_pages, "rlog_pages=%u" }, + { Opt_rlog_wakeup_cnt, "rlog_wakeup_cnt=%u" }, + { Opt_err, NULL } +}; + +static int parse_options(struct mount_options *opts, char *str) +{ + substring_t args[MAX_OPT_ARGS]; + int value; + char *position; + + if (opts == NULL) + return -EFAULT; + + opts->read_timeout_ms = 1000; /* Default: 1s */ + opts->readahead_pages = 10; + opts->read_log_pages = 2; + opts->read_log_wakeup_count = 10; + opts->no_backing_file_cache = false; + opts->no_backing_file_readahead = false; + if (str == NULL || *str == 0) + return 0; + + while ((position = strsep(&str, ",")) != NULL) { + int token; + + if (!*position) + continue; + + token = match_token(position, option_tokens, args); + + switch (token) { + case Opt_read_timeout: + if (match_int(&args[0], &value)) + return -EINVAL; + opts->read_timeout_ms = value; + break; + case Opt_readahead_pages: + if (match_int(&args[0], &value)) + return -EINVAL; + opts->readahead_pages = value; + break; + case Opt_no_backing_file_cache: + if (match_int(&args[0], &value)) + return -EINVAL; + opts->no_backing_file_cache = (value != 0); + break; + case Opt_no_backing_file_readahead: + if (match_int(&args[0], &value)) + return -EINVAL; + opts->no_backing_file_readahead = (value != 0); + break; + case Opt_rlog_pages: + if (match_int(&args[0], &value)) + return -EINVAL; + opts->read_log_pages = value; + break; + case Opt_rlog_wakeup_cnt: + if (match_int(&args[0], &value)) + return -EINVAL; + opts->read_log_wakeup_count = value; + break; + default: + return -EINVAL; + } + } + + return 0; +} + +static struct super_block *file_superblock(struct file *f) +{ + struct inode *inode = file_inode(f); + + return inode->i_sb; +} + +static struct mount_info *get_mount_info(struct super_block *sb) +{ + struct mount_info *result = sb->s_fs_info; + + WARN_ON(!result); + return result; +} + +/* Read file size from the attribute. Quicker than reading the header */ +static u64 read_size_attr(struct dentry *backing_dentry) +{ + __le64 attr_value; + ssize_t bytes_read; + + bytes_read = vfs_getxattr(backing_dentry, INCFS_XATTR_SIZE_NAME, + (char *)&attr_value, sizeof(attr_value)); + + if (bytes_read != sizeof(attr_value)) + return 0; + + return le64_to_cpu(attr_value); +} + +static int inode_test(struct inode *inode, void *opaque) +{ + struct inode_search *search = opaque; + struct inode_info *node = get_incfs_node(inode); + + if (!node) + return 0; + + if (search->backing_dentry) { + struct inode *backing_inode = d_inode(search->backing_dentry); + + return (node->n_backing_inode == backing_inode) && + inode->i_ino == search->ino; + } + return 1; +} + +static int inode_set(struct inode *inode, void *opaque) +{ + struct inode_search *search = opaque; + struct inode_info *node = get_incfs_node(inode); + + if (search->backing_dentry) { + /* It's a regular inode that has corresponding backing inode */ + struct dentry *backing_dentry = search->backing_dentry; + struct inode *backing_inode = d_inode(backing_dentry); + + fsstack_copy_attr_all(inode, backing_inode); + if (S_ISREG(inode->i_mode)) { + u64 size = read_size_attr(backing_dentry); + + inode->i_size = size; + inode->i_blocks = get_blocks_count_for_size(size); + inode->i_mapping->a_ops = &incfs_address_space_ops; + inode->i_op = &incfs_file_inode_ops; + inode->i_fop = &incfs_file_ops; + } else if (S_ISDIR(inode->i_mode)) { + inode->i_size = 0; + inode->i_blocks = 1; + inode->i_mapping->a_ops = &incfs_address_space_ops; + inode->i_op = &incfs_dir_inode_ops; + inode->i_fop = &incfs_dir_fops; + } else { + pr_warn_once("incfs: Unexpected inode type\n"); + return -EBADF; + } + + ihold(backing_inode); + node->n_backing_inode = backing_inode; + node->n_mount_info = get_mount_info(inode->i_sb); + inode->i_ctime = backing_inode->i_ctime; + inode->i_mtime = backing_inode->i_mtime; + inode->i_atime = backing_inode->i_atime; + inode->i_ino = backing_inode->i_ino; + if (backing_inode->i_ino < INCFS_START_INO_RANGE) { + pr_warn("incfs: ino conflict with backing FS %ld\n", + backing_inode->i_ino); + } + + return 0; + } else if (search->ino == INCFS_PENDING_READS_INODE) { + /* It's an inode for .pending_reads pseudo file. */ + + inode->i_ctime = (struct timespec64){}; + inode->i_mtime = inode->i_ctime; + inode->i_atime = inode->i_ctime; + inode->i_size = 0; + inode->i_ino = INCFS_PENDING_READS_INODE; + inode->i_private = NULL; + + inode_init_owner(inode, NULL, S_IFREG | READ_WRITE_FILE_MODE); + + inode->i_op = &incfs_file_inode_ops; + inode->i_fop = &incfs_pending_read_file_ops; + + } else if (search->ino == INCFS_LOG_INODE) { + /* It's an inode for .log pseudo file. */ + + inode->i_ctime = (struct timespec64){}; + inode->i_mtime = inode->i_ctime; + inode->i_atime = inode->i_ctime; + inode->i_size = 0; + inode->i_ino = INCFS_LOG_INODE; + inode->i_private = NULL; + + inode_init_owner(inode, NULL, S_IFREG | READ_WRITE_FILE_MODE); + + inode->i_op = &incfs_file_inode_ops; + inode->i_fop = &incfs_log_file_ops; + + } else { + /* Unknown inode requested. */ + return -EINVAL; + } + + return 0; +} + +static struct inode *fetch_regular_inode(struct super_block *sb, + struct dentry *backing_dentry) +{ + struct inode *backing_inode = d_inode(backing_dentry); + struct inode_search search = { + .ino = backing_inode->i_ino, + .backing_dentry = backing_dentry + }; + struct inode *inode = iget5_locked(sb, search.ino, inode_test, + inode_set, &search); + + if (!inode) + return ERR_PTR(-ENOMEM); + + if (inode->i_state & I_NEW) + unlock_new_inode(inode); + + return inode; +} + +static ssize_t pending_reads_read(struct file *f, char __user *buf, size_t len, + loff_t *ppos) +{ + struct pending_reads_state *pr_state = f->private_data; + struct mount_info *mi = get_mount_info(file_superblock(f)); + struct incfs_pending_read_info *reads_buf = NULL; + size_t reads_to_collect = len / sizeof(*reads_buf); + int last_known_read_sn = READ_ONCE(pr_state->last_pending_read_sn); + int new_max_sn = last_known_read_sn; + int reads_collected = 0; + ssize_t result = 0; + int i = 0; + + if (!access_ok(VERIFY_WRITE, buf, len)) + return -EFAULT; + + if (!incfs_fresh_pending_reads_exist(mi, last_known_read_sn)) + return 0; + + reads_buf = (struct incfs_pending_read_info *)get_zeroed_page(GFP_NOFS); + if (!reads_buf) + return -ENOMEM; + + reads_to_collect = + min_t(size_t, PAGE_SIZE / sizeof(*reads_buf), reads_to_collect); + + reads_collected = incfs_collect_pending_reads( + mi, last_known_read_sn, reads_buf, reads_to_collect); + if (reads_collected < 0) { + result = reads_collected; + goto out; + } + + for (i = 0; i < reads_collected; i++) + if (reads_buf[i].serial_number > new_max_sn) + new_max_sn = reads_buf[i].serial_number; + + /* + * Just to make sure that we don't accidentally copy more data + * to reads buffer than userspace can handle. + */ + reads_collected = min_t(size_t, reads_collected, reads_to_collect); + result = reads_collected * sizeof(*reads_buf); + + /* Copy reads info to the userspace buffer */ + if (copy_to_user(buf, reads_buf, result)) { + result = -EFAULT; + goto out; + } + + WRITE_ONCE(pr_state->last_pending_read_sn, new_max_sn); + *ppos = 0; +out: + if (reads_buf) + free_page((unsigned long)reads_buf); + return result; +} + + +static __poll_t pending_reads_poll(struct file *file, poll_table *wait) +{ + struct pending_reads_state *state = file->private_data; + struct mount_info *mi = get_mount_info(file_superblock(file)); + __poll_t ret = 0; + + poll_wait(file, &mi->mi_pending_reads_notif_wq, wait); + if (incfs_fresh_pending_reads_exist(mi, + state->last_pending_read_sn)) + ret = EPOLLIN | EPOLLRDNORM; + + return ret; +} + +static int pending_reads_open(struct inode *inode, struct file *file) +{ + struct pending_reads_state *state = NULL; + + state = kzalloc(sizeof(*state), GFP_NOFS); + if (!state) + return -ENOMEM; + + file->private_data = state; + return 0; +} + +static int pending_reads_release(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + return 0; +} + +static struct inode *fetch_pending_reads_inode(struct super_block *sb) +{ + struct inode_search search = { + .ino = INCFS_PENDING_READS_INODE + }; + struct inode *inode = iget5_locked(sb, search.ino, inode_test, + inode_set, &search); + + if (!inode) + return ERR_PTR(-ENOMEM); + + if (inode->i_state & I_NEW) + unlock_new_inode(inode); + + return inode; +} + +static int log_open(struct inode *inode, struct file *file) +{ + struct log_file_state *log_state = NULL; + struct mount_info *mi = get_mount_info(file_superblock(file)); + + log_state = kzalloc(sizeof(*log_state), GFP_NOFS); + if (!log_state) + return -ENOMEM; + + log_state->state = incfs_get_log_state(mi); + file->private_data = log_state; + return 0; +} + +static int log_release(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + return 0; +} + +static ssize_t log_read(struct file *f, char __user *buf, size_t len, + loff_t *ppos) +{ + struct log_file_state *log_state = f->private_data; + struct mount_info *mi = get_mount_info(file_superblock(f)); + struct incfs_pending_read_info *reads_buf = + (struct incfs_pending_read_info *)__get_free_page(GFP_NOFS); + size_t reads_to_collect = len / sizeof(*reads_buf); + size_t reads_per_page = PAGE_SIZE / sizeof(*reads_buf); + int total_reads_collected = 0; + ssize_t result = 0; + + if (!reads_buf) + return -ENOMEM; + + reads_to_collect = min_t(size_t, mi->mi_log.rl_size, reads_to_collect); + while (reads_to_collect > 0) { + struct read_log_state next_state = READ_ONCE(log_state->state); + int reads_collected = incfs_collect_logged_reads( + mi, &next_state, reads_buf, + min_t(size_t, reads_to_collect, reads_per_page)); + if (reads_collected <= 0) { + result = total_reads_collected ? + total_reads_collected * + sizeof(*reads_buf) : + reads_collected; + goto out; + } + if (copy_to_user(buf, reads_buf, + reads_collected * sizeof(*reads_buf))) { + result = total_reads_collected ? + total_reads_collected * + sizeof(*reads_buf) : + -EFAULT; + goto out; + } + + WRITE_ONCE(log_state->state, next_state); + total_reads_collected += reads_collected; + buf += reads_collected * sizeof(*reads_buf); + reads_to_collect -= reads_collected; + } + + result = total_reads_collected * sizeof(*reads_buf); + *ppos = 0; +out: + if (reads_buf) + free_page((unsigned long)reads_buf); + return result; +} + +static __poll_t log_poll(struct file *file, poll_table *wait) +{ + struct log_file_state *log_state = file->private_data; + struct mount_info *mi = get_mount_info(file_superblock(file)); + int count; + __poll_t ret = 0; + + poll_wait(file, &mi->mi_log.ml_notif_wq, wait); + count = incfs_get_uncollected_logs_count(mi, log_state->state); + if (count >= mi->mi_options.read_log_wakeup_count) + ret = EPOLLIN | EPOLLRDNORM; + + return ret; +} + +static struct inode *fetch_log_inode(struct super_block *sb) +{ + struct inode_search search = { + .ino = INCFS_LOG_INODE + }; + struct inode *inode = iget5_locked(sb, search.ino, inode_test, + inode_set, &search); + + if (!inode) + return ERR_PTR(-ENOMEM); + + if (inode->i_state & I_NEW) + unlock_new_inode(inode); + + return inode; +} + +static int iterate_incfs_dir(struct file *file, struct dir_context *ctx) +{ + struct dir_file *dir = get_incfs_dir_file(file); + int error = 0; + struct mount_info *mi = get_mount_info(file_superblock(file)); + bool root; + + if (!dir) { + error = -EBADF; + goto out; + } + + root = dir->backing_dir->f_inode + == d_inode(mi->mi_backing_dir_path.dentry); + + if (root && ctx->pos == 0) { + if (!dir_emit(ctx, pending_reads_file_name, + ARRAY_SIZE(pending_reads_file_name) - 1, + INCFS_PENDING_READS_INODE, DT_REG)) { + error = -EINVAL; + goto out; + } + ctx->pos++; + } + + if (root && ctx->pos == 1) { + if (!dir_emit(ctx, log_file_name, + ARRAY_SIZE(log_file_name) - 1, + INCFS_LOG_INODE, DT_REG)) { + error = -EINVAL; + goto out; + } + ctx->pos++; + } + + ctx->pos -= 2; + error = iterate_dir(dir->backing_dir, ctx); + ctx->pos += 2; + file->f_pos = dir->backing_dir->f_pos; +out: + if (error) + pr_warn("incfs: %s %s %d\n", __func__, + file->f_path.dentry->d_name.name, error); + return error; +} + +static int incfs_init_dentry(struct dentry *dentry, struct path *path) +{ + struct dentry_info *d_info = NULL; + + if (!dentry || !path) + return -EFAULT; + + d_info = kzalloc(sizeof(*d_info), GFP_NOFS); + if (!d_info) + return -ENOMEM; + + d_info->backing_path = *path; + path_get(path); + + dentry->d_fsdata = d_info; + return 0; +} + +static struct dentry *incfs_lookup_dentry(struct dentry *parent, + const char *name) +{ + struct inode *inode; + struct dentry *result = NULL; + + if (!parent) + return ERR_PTR(-EFAULT); + + inode = d_inode(parent); + inode_lock_nested(inode, I_MUTEX_PARENT); + result = lookup_one_len(name, parent, strlen(name)); + inode_unlock(inode); + + if (IS_ERR(result)) + pr_warn("%s err:%ld\n", __func__, PTR_ERR(result)); + + return result; +} + +static struct dentry *open_or_create_index_dir(struct dentry *backing_dir) +{ + static const char name[] = ".index"; + struct dentry *index_dentry; + struct inode *backing_inode = d_inode(backing_dir); + int err = 0; + + index_dentry = incfs_lookup_dentry(backing_dir, name); + if (!index_dentry) { + return ERR_PTR(-EINVAL); + } else if (IS_ERR(index_dentry)) { + return index_dentry; + } else if (d_really_is_positive(index_dentry)) { + /* Index already exists. */ + return index_dentry; + } + + /* Index needs to be created. */ + inode_lock_nested(backing_inode, I_MUTEX_PARENT); + err = vfs_mkdir(backing_inode, index_dentry, 0777); + inode_unlock(backing_inode); + + if (err) + return ERR_PTR(err); + + if (!d_really_is_positive(index_dentry)) { + dput(index_dentry); + return ERR_PTR(-EINVAL); + } + + return index_dentry; +} + +static int read_single_page(struct file *f, struct page *page) +{ + loff_t offset = 0; + loff_t size = 0; + ssize_t bytes_to_read = 0; + ssize_t read_result = 0; + struct data_file *df = get_incfs_data_file(f); + int result = 0; + void *page_start = kmap(page); + int block_index; + int timeout_ms; + + if (!df) + return -EBADF; + + offset = page_offset(page); + block_index = offset / INCFS_DATA_FILE_BLOCK_SIZE; + size = df->df_size; + timeout_ms = df->df_mount_info->mi_options.read_timeout_ms; + + pr_debug("incfs: %s %s %lld\n", __func__, + f->f_path.dentry->d_name.name, offset); + + if (offset < size) { + struct mem_range tmp = { + .len = 2 * INCFS_DATA_FILE_BLOCK_SIZE + }; + + tmp.data = (u8 *)__get_free_pages(GFP_NOFS, get_order(tmp.len)); + bytes_to_read = min_t(loff_t, size - offset, PAGE_SIZE); + read_result = incfs_read_data_file_block( + range(page_start, bytes_to_read), df, block_index, + timeout_ms, tmp); + + free_pages((unsigned long)tmp.data, get_order(tmp.len)); + } else { + bytes_to_read = 0; + read_result = 0; + } + + if (read_result < 0) + result = read_result; + else if (read_result < PAGE_SIZE) + zero_user(page, read_result, PAGE_SIZE - read_result); + + if (result == 0) + SetPageUptodate(page); + else + SetPageError(page); + + flush_dcache_page(page); + kunmap(page); + unlock_page(page); + return result; +} + +static char *file_id_to_str(incfs_uuid_t id) +{ + char *result = kmalloc(1 + sizeof(id.bytes) * 2, GFP_NOFS); + char *end; + + if (!result) + return NULL; + + end = bin2hex(result, id.bytes, sizeof(id.bytes)); + *end = 0; + return result; +} + +static struct signature_info *incfs_copy_signature_info_from_user( + struct incfs_file_signature_info __user *original) +{ + struct incfs_file_signature_info usr_si; + struct signature_info *result; + int error; + + if (!original) + return NULL; + + if (!access_ok(VERIFY_READ, original, sizeof(usr_si))) + return ERR_PTR(-EFAULT); + + if (copy_from_user(&usr_si, original, sizeof(usr_si)) > 0) + return ERR_PTR(-EFAULT); + + result = kzalloc(sizeof(*result), GFP_NOFS); + if (!result) + return ERR_PTR(-ENOMEM); + + result->hash_alg = usr_si.hash_tree_alg; + + if (result->hash_alg) { + void *p = kzalloc(INCFS_MAX_HASH_SIZE, GFP_NOFS); + + if (!p) { + error = -ENOMEM; + goto err; + } + + /* TODO this sets the root_hash length to MAX_HASH_SIZE not + * the actual size. Fix, then set INCFS_MAX_HASH_SIZE back + * to 64 + */ + result->root_hash = range(p, INCFS_MAX_HASH_SIZE); + if (copy_from_user(p, u64_to_user_ptr(usr_si.root_hash), + result->root_hash.len) > 0) { + error = -EFAULT; + goto err; + } + } + + if (usr_si.additional_data_size > INCFS_MAX_FILE_ATTR_SIZE) { + error = -E2BIG; + goto err; + } + + if (usr_si.additional_data && usr_si.additional_data_size) { + void *p = kzalloc(usr_si.additional_data_size, GFP_NOFS); + + if (!p) { + error = -ENOMEM; + goto err; + } + result->additional_data = range(p, + usr_si.additional_data_size); + if (copy_from_user(p, u64_to_user_ptr(usr_si.additional_data), + result->additional_data.len) > 0) { + error = -EFAULT; + goto err; + } + } + + if (usr_si.signature_size > INCFS_MAX_SIGNATURE_SIZE) { + error = -E2BIG; + goto err; + } + + if (usr_si.signature && usr_si.signature_size) { + void *p = kzalloc(usr_si.signature_size, GFP_NOFS); + + if (!p) { + error = -ENOMEM; + goto err; + } + result->signature = range(p, usr_si.signature_size); + if (copy_from_user(p, u64_to_user_ptr(usr_si.signature), + result->signature.len) > 0) { + error = -EFAULT; + goto err; + } + } + + return result; + +err: + incfs_free_signature_info(result); + return ERR_PTR(-error); +} + +static int init_new_file(struct mount_info *mi, struct dentry *dentry, + incfs_uuid_t *uuid, u64 size, struct mem_range attr, + struct incfs_file_signature_info __user *fsi) +{ + struct path path = {}; + struct file *new_file; + int error = 0; + struct backing_file_context *bfc = NULL; + u32 block_count; + struct mem_range mem_range = {NULL}; + struct signature_info *si = NULL; + struct mtree *hash_tree = NULL; + + if (!mi || !dentry || !uuid) + return -EFAULT; + + /* Resize newly created file to its true size. */ + path = (struct path) { + .mnt = mi->mi_backing_dir_path.mnt, + .dentry = dentry + }; + new_file = dentry_open(&path, O_RDWR | O_NOATIME, mi->mi_owner); + + if (IS_ERR(new_file)) { + error = PTR_ERR(new_file); + goto out; + } + + bfc = incfs_alloc_bfc(new_file); + if (IS_ERR(bfc)) { + error = PTR_ERR(bfc); + bfc = NULL; + goto out; + } + + mutex_lock(&bfc->bc_mutex); + error = incfs_write_fh_to_backing_file(bfc, uuid, size); + if (error) + goto out; + + block_count = (u32)get_blocks_count_for_size(size); + error = incfs_write_blockmap_to_backing_file(bfc, block_count, NULL); + if (error) + goto out; + + /* This fill has data, reserve space for the block map. */ + if (block_count > 0) { + error = incfs_write_blockmap_to_backing_file( + bfc, block_count, NULL); + if (error) + goto out; + } + + if (attr.data && attr.len) { + error = incfs_write_file_attr_to_backing_file(bfc, + attr, NULL); + if (error) + goto out; + } + + if (fsi) { + si = incfs_copy_signature_info_from_user(fsi); + + if (IS_ERR(si)) { + error = PTR_ERR(si); + si = NULL; + goto out; + } + + if (si->hash_alg) { + hash_tree = incfs_alloc_mtree(si->hash_alg, block_count, + si->root_hash); + if (IS_ERR(hash_tree)) { + error = PTR_ERR(hash_tree); + hash_tree = NULL; + goto out; + } + + /* TODO This code seems wrong when len is zero - we + * should error out?? + */ + if (si->signature.len > 0) + error = incfs_validate_pkcs7_signature( + si->signature, + si->root_hash, + si->additional_data); + if (error) + goto out; + + error = incfs_write_signature_to_backing_file(bfc, + si->hash_alg, + hash_tree->hash_tree_area_size, + si->root_hash, si->additional_data, + si->signature); + + if (error) + goto out; + } + } + +out: + if (bfc) { + mutex_unlock(&bfc->bc_mutex); + incfs_free_bfc(bfc); + } + incfs_free_mtree(hash_tree); + incfs_free_signature_info(si); + kfree(mem_range.data); + + if (error) + pr_debug("incfs: %s error: %d\n", __func__, error); + return error; +} + +static int incfs_link(struct dentry *what, struct dentry *where) +{ + struct dentry *parent_dentry = dget_parent(where); + struct inode *pinode = d_inode(parent_dentry); + int error = 0; + + inode_lock_nested(pinode, I_MUTEX_PARENT); + error = vfs_link(what, pinode, where, NULL); + inode_unlock(pinode); + + dput(parent_dentry); + return error; +} + +static int incfs_unlink(struct dentry *dentry) +{ + struct dentry *parent_dentry = dget_parent(dentry); + struct inode *pinode = d_inode(parent_dentry); + int error = 0; + + inode_lock_nested(pinode, I_MUTEX_PARENT); + error = vfs_unlink(pinode, dentry, NULL); + inode_unlock(pinode); + + dput(parent_dentry); + return error; +} + +static int incfs_rmdir(struct dentry *dentry) +{ + struct dentry *parent_dentry = dget_parent(dentry); + struct inode *pinode = d_inode(parent_dentry); + int error = 0; + + inode_lock_nested(pinode, I_MUTEX_PARENT); + error = vfs_rmdir(pinode, dentry); + inode_unlock(pinode); + + dput(parent_dentry); + return error; +} + +static int dir_relative_path_resolve( + struct mount_info *mi, + const char __user *relative_path, + struct path *result_path) +{ + struct path *base_path = &mi->mi_backing_dir_path; + int dir_fd = get_unused_fd_flags(0); + struct file *dir_f = NULL; + int error = 0; + + if (dir_fd < 0) + return dir_fd; + + dir_f = dentry_open(base_path, O_RDONLY | O_NOATIME, mi->mi_owner); + + if (IS_ERR(dir_f)) { + error = PTR_ERR(dir_f); + goto out; + } + fd_install(dir_fd, dir_f); + + if (!relative_path) { + /* No relative path given, just return the base dir. */ + *result_path = *base_path; + path_get(result_path); + goto out; + } + + error = user_path_at_empty(dir_fd, relative_path, + LOOKUP_FOLLOW | LOOKUP_DIRECTORY, result_path, NULL); + +out: + ksys_close(dir_fd); + if (error) + pr_debug("incfs: %s %d\n", __func__, error); + return error; +} + +static int validate_name(char *file_name) +{ + struct mem_range name = range(file_name, strlen(file_name)); + int i = 0; + + if (name.len > INCFS_MAX_NAME_LEN) + return -ENAMETOOLONG; + + if (incfs_equal_ranges(pending_reads_file_name_range, name)) + return -EINVAL; + + for (i = 0; i < name.len; i++) + if (name.data[i] == '/') + return -EINVAL; + + return 0; +} + +static int chmod(struct dentry *dentry, umode_t mode) +{ + struct inode *inode = dentry->d_inode; + struct inode *delegated_inode = NULL; + struct iattr newattrs; + int error; + +retry_deleg: + inode_lock(inode); + newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); + newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; + error = notify_change(dentry, &newattrs, &delegated_inode); + inode_unlock(inode); + if (delegated_inode) { + error = break_deleg_wait(&delegated_inode); + if (!error) + goto retry_deleg; + } + return error; +} + +static long ioctl_create_file(struct mount_info *mi, + struct incfs_new_file_args __user *usr_args) +{ + struct incfs_new_file_args args; + char *file_id_str = NULL; + struct dentry *index_file_dentry = NULL; + struct dentry *named_file_dentry = NULL; + struct path parent_dir_path = {}; + struct inode *index_dir_inode = NULL; + __le64 size_attr_value = 0; + char *file_name = NULL; + char *attr_value = NULL; + int error = 0; + bool locked = false; + + if (!mi || !mi->mi_index_dir) { + error = -EFAULT; + goto out; + } + if (!access_ok(VERIFY_READ, usr_args, sizeof(args))) { + error = -EFAULT; + goto out; + } + if (copy_from_user(&args, usr_args, sizeof(args)) > 0) { + error = -EFAULT; + goto out; + } + + file_name = strndup_user(u64_to_user_ptr(args.file_name), PATH_MAX); + if (IS_ERR(file_name)) { + error = PTR_ERR(file_name); + file_name = NULL; + goto out; + } + + error = validate_name(file_name); + if (error) + goto out; + + file_id_str = file_id_to_str(args.file_id); + if (!file_id_str) { + error = -ENOMEM; + goto out; + } + + error = mutex_lock_interruptible(&mi->mi_dir_struct_mutex); + if (error) + goto out; + locked = true; + + /* Find a directory to put the file into. */ + error = dir_relative_path_resolve(mi, + u64_to_user_ptr(args.directory_path), + &parent_dir_path); + if (error) + goto out; + + if (parent_dir_path.dentry == mi->mi_index_dir) { + /* Can't create a file directly inside .index */ + error = -EBUSY; + goto out; + } + + /* Look up a dentry in the parent dir. It should be negative. */ + named_file_dentry = incfs_lookup_dentry(parent_dir_path.dentry, + file_name); + if (!named_file_dentry) { + error = -EFAULT; + goto out; + } + if (IS_ERR(named_file_dentry)) { + error = PTR_ERR(named_file_dentry); + named_file_dentry = NULL; + goto out; + } + if (d_really_is_positive(named_file_dentry)) { + /* File with this path already exists. */ + error = -EEXIST; + goto out; + } + /* Look up a dentry in the .index dir. It should be negative. */ + index_file_dentry = incfs_lookup_dentry(mi->mi_index_dir, file_id_str); + if (!index_file_dentry) { + error = -EFAULT; + goto out; + } + if (IS_ERR(index_file_dentry)) { + error = PTR_ERR(index_file_dentry); + index_file_dentry = NULL; + goto out; + } + if (d_really_is_positive(index_file_dentry)) { + /* File with this ID already exists in index. */ + error = -EEXIST; + goto out; + } + + /* Creating a file in the .index dir. */ + index_dir_inode = d_inode(mi->mi_index_dir); + inode_lock_nested(index_dir_inode, I_MUTEX_PARENT); + error = vfs_create(index_dir_inode, index_file_dentry, args.mode | 0222, + true); + inode_unlock(index_dir_inode); + + if (error) + goto out; + if (!d_really_is_positive(index_file_dentry)) { + error = -EINVAL; + goto out; + } + + error = chmod(index_file_dentry, args.mode | 0222); + if (error) { + pr_debug("incfs: chmod err: %d\n", error); + goto delete_index_file; + } + + /* Save the file's ID as an xattr for easy fetching in future. */ + error = vfs_setxattr(index_file_dentry, INCFS_XATTR_ID_NAME, + file_id_str, strlen(file_id_str), XATTR_CREATE); + if (error) { + pr_debug("incfs: vfs_setxattr err:%d\n", error); + goto delete_index_file; + } + + /* Save the file's size as an xattr for easy fetching in future. */ + size_attr_value = cpu_to_le64(args.size); + error = vfs_setxattr(index_file_dentry, INCFS_XATTR_SIZE_NAME, + (char *)&size_attr_value, sizeof(size_attr_value), + XATTR_CREATE); + if (error) { + pr_debug("incfs: vfs_setxattr err:%d\n", error); + goto delete_index_file; + } + + /* Save the file's attrubute as an xattr */ + if (args.file_attr_len && args.file_attr) { + if (args.file_attr_len > INCFS_MAX_FILE_ATTR_SIZE) { + error = -E2BIG; + goto delete_index_file; + } + + attr_value = kmalloc(args.file_attr_len, GFP_NOFS); + if (!attr_value) { + error = -ENOMEM; + goto delete_index_file; + } + + if (!access_ok(VERIFY_READ, u64_to_user_ptr(args.file_attr), + args.file_attr_len)) { + error = -EFAULT; + goto delete_index_file; + } + + if (copy_from_user(attr_value, + u64_to_user_ptr(args.file_attr), + args.file_attr_len) > 0) { + error = -EFAULT; + goto delete_index_file; + } + + error = vfs_setxattr(index_file_dentry, + INCFS_XATTR_METADATA_NAME, + attr_value, args.file_attr_len, + XATTR_CREATE); + + if (error) + goto delete_index_file; + } + + /* Initializing a newly created file. */ + error = init_new_file(mi, index_file_dentry, &args.file_id, args.size, + range(attr_value, args.file_attr_len), + (struct incfs_file_signature_info __user *) + args.signature_info); + if (error) + goto delete_index_file; + + /* Linking a file with it's real name from the requested dir. */ + error = incfs_link(index_file_dentry, named_file_dentry); + + if (!error) + goto out; + +delete_index_file: + incfs_unlink(index_file_dentry); + +out: + if (error) + pr_debug("incfs: %s err:%d\n", __func__, error); + + kfree(file_id_str); + kfree(file_name); + kfree(attr_value); + dput(named_file_dentry); + dput(index_file_dentry); + path_put(&parent_dir_path); + if (locked) + mutex_unlock(&mi->mi_dir_struct_mutex); + return error; +} + +static long ioctl_read_file_signature(struct file *f, void __user *arg) +{ + struct incfs_get_file_sig_args __user *args_usr_ptr = arg; + struct incfs_get_file_sig_args args = {}; + u8 *sig_buffer = NULL; + size_t sig_buf_size = 0; + int error = 0; + int read_result = 0; + struct data_file *df = get_incfs_data_file(f); + + if (!df) + return -EINVAL; + + if (!access_ok(VERIFY_READ, args_usr_ptr, sizeof(args))) + return -EFAULT; + if (copy_from_user(&args, args_usr_ptr, sizeof(args)) > 0) + return -EINVAL; + + if (!access_ok(VERIFY_WRITE, u64_to_user_ptr(args.file_signature), + args.file_signature_buf_size)) + return -EFAULT; + + sig_buf_size = args.file_signature_buf_size; + if (sig_buf_size > INCFS_MAX_SIGNATURE_SIZE) + return -E2BIG; + + sig_buffer = kzalloc(sig_buf_size, GFP_NOFS); + if (!sig_buffer) + return -ENOMEM; + + read_result = incfs_read_file_signature(df, + range(sig_buffer, sig_buf_size)); + + if (read_result < 0) { + error = read_result; + goto out; + } + + if (copy_to_user(u64_to_user_ptr(args.file_signature), sig_buffer, + read_result)) { + error = -EFAULT; + goto out; + } + + args.file_signature_len_out = read_result; + if (copy_to_user(args_usr_ptr, &args, sizeof(args))) + error = -EFAULT; + +out: + kfree(sig_buffer); + + return error; +} + +static long dispatch_ioctl(struct file *f, unsigned int req, unsigned long arg) +{ + struct mount_info *mi = get_mount_info(file_superblock(f)); + + switch (req) { + case INCFS_IOC_CREATE_FILE: + return ioctl_create_file(mi, (void __user *)arg); + case INCFS_IOC_READ_FILE_SIGNATURE: + return ioctl_read_file_signature(f, (void __user *)arg); + default: + return -EINVAL; + } +} + +static struct dentry *dir_lookup(struct inode *dir_inode, struct dentry *dentry, + unsigned int flags) +{ + struct mount_info *mi = get_mount_info(dir_inode->i_sb); + struct dentry *dir_dentry = NULL; + struct dentry *backing_dentry = NULL; + struct path dir_backing_path = {}; + struct inode_info *dir_info = get_incfs_node(dir_inode); + struct mem_range name_range = + range((u8 *)dentry->d_name.name, dentry->d_name.len); + int err = 0; + + if (d_inode(mi->mi_backing_dir_path.dentry) == + dir_info->n_backing_inode) { + /* We do lookup in the FS root. Show pseudo files. */ + + if (incfs_equal_ranges(pending_reads_file_name_range, + name_range)) { + struct inode *inode = fetch_pending_reads_inode( + dir_inode->i_sb); + + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto out; + } + + d_add(dentry, inode); + goto out; + } + + if (incfs_equal_ranges(log_file_name_range, name_range)) { + struct inode *inode = fetch_log_inode( + dir_inode->i_sb); + + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto out; + } + + d_add(dentry, inode); + goto out; + } + } + + dir_dentry = dget_parent(dentry); + get_incfs_backing_path(dir_dentry, &dir_backing_path); + backing_dentry = incfs_lookup_dentry(dir_backing_path.dentry, + dentry->d_name.name); + + if (!backing_dentry || IS_ERR(backing_dentry)) { + err = IS_ERR(backing_dentry) + ? PTR_ERR(backing_dentry) + : -EFAULT; + backing_dentry = NULL; + goto out; + } else { + struct inode *inode = NULL; + struct path backing_path = { + .mnt = dir_backing_path.mnt, + .dentry = backing_dentry + }; + + err = incfs_init_dentry(dentry, &backing_path); + if (err) + goto out; + + if (!d_really_is_positive(backing_dentry)) { + /* + * No such entry found in the backing dir. + * Create a negative entry. + */ + d_add(dentry, NULL); + err = 0; + goto out; + } + + if (d_inode(backing_dentry)->i_sb != + dir_info->n_backing_inode->i_sb) { + /* + * Somehow after the path lookup we ended up in a + * different fs mount. If we keep going it's going + * to end badly. + */ + err = -EXDEV; + goto out; + } + + inode = fetch_regular_inode(dir_inode->i_sb, backing_dentry); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto out; + } + + d_add(dentry, inode); + } + +out: + dput(dir_dentry); + dput(backing_dentry); + path_put(&dir_backing_path); + if (err) + pr_debug("incfs: %s %s %d\n", __func__, + dentry->d_name.name, err); + return ERR_PTR(err); +} + +static int dir_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) +{ + struct mount_info *mi = get_mount_info(dir->i_sb); + struct inode_info *dir_node = get_incfs_node(dir); + struct dentry *backing_dentry = NULL; + struct path backing_path = {}; + int err = 0; + + + if (!mi || !dir_node || !dir_node->n_backing_inode) + return -EBADF; + + err = mutex_lock_interruptible(&mi->mi_dir_struct_mutex); + if (err) + return err; + + get_incfs_backing_path(dentry, &backing_path); + backing_dentry = backing_path.dentry; + + if (!backing_dentry) { + err = -EBADF; + goto out; + } + + if (backing_dentry->d_parent == mi->mi_index_dir) { + /* Can't create a subdir inside .index */ + err = -EBUSY; + goto out; + } + + inode_lock_nested(dir_node->n_backing_inode, I_MUTEX_PARENT); + err = vfs_mkdir(dir_node->n_backing_inode, backing_dentry, mode | 0222); + inode_unlock(dir_node->n_backing_inode); + if (!err) { + struct inode *inode = NULL; + + if (d_really_is_negative(backing_dentry)) { + err = -EINVAL; + goto out; + } + + inode = fetch_regular_inode(dir->i_sb, backing_dentry); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto out; + } + d_instantiate(dentry, inode); + } + +out: + if (d_really_is_negative(dentry)) + d_drop(dentry); + path_put(&backing_path); + mutex_unlock(&mi->mi_dir_struct_mutex); + if (err) + pr_debug("incfs: %s err:%d\n", __func__, err); + return err; +} + +/* Delete file referenced by backing_dentry and also its hardlink from .index */ +static int final_file_delete(struct mount_info *mi, + struct dentry *backing_dentry) +{ + struct dentry *index_file_dentry = NULL; + /* 2 chars per byte of file ID + 1 char for \0 */ + char file_id_str[2 * sizeof(incfs_uuid_t) + 1] = {0}; + ssize_t uuid_size = 0; + int error = 0; + + WARN_ON(!mutex_is_locked(&mi->mi_dir_struct_mutex)); + uuid_size = vfs_getxattr(backing_dentry, INCFS_XATTR_ID_NAME, + file_id_str, 2 * sizeof(incfs_uuid_t)); + if (uuid_size < 0) { + error = uuid_size; + goto out; + } + + if (uuid_size != 2 * sizeof(incfs_uuid_t)) { + error = -EBADMSG; + goto out; + } + + index_file_dentry = incfs_lookup_dentry(mi->mi_index_dir, file_id_str); + if (IS_ERR(index_file_dentry)) { + error = PTR_ERR(index_file_dentry); + goto out; + } + + error = incfs_unlink(backing_dentry); + if (error) + goto out; + + if (d_really_is_positive(index_file_dentry)) + error = incfs_unlink(index_file_dentry); +out: + if (error) + pr_debug("incfs: delete_file_from_index err:%d\n", error); + return error; +} + +static int dir_unlink(struct inode *dir, struct dentry *dentry) +{ + struct mount_info *mi = get_mount_info(dir->i_sb); + struct path backing_path = {}; + struct kstat stat; + int err = 0; + + err = mutex_lock_interruptible(&mi->mi_dir_struct_mutex); + if (err) + return err; + + get_incfs_backing_path(dentry, &backing_path); + if (!backing_path.dentry) { + err = -EBADF; + goto out; + } + + if (backing_path.dentry->d_parent == mi->mi_index_dir) { + /* Direct unlink from .index are not allowed. */ + err = -EBUSY; + goto out; + } + + err = vfs_getattr(&backing_path, &stat, STATX_NLINK, + AT_STATX_SYNC_AS_STAT); + if (err) + goto out; + + if (stat.nlink == 2) { + /* + * This is the last named link to this file. The only one left + * is in .index. Remove them both now. + */ + err = final_file_delete(mi, backing_path.dentry); + } else { + /* There are other links to this file. Remove just this one. */ + err = incfs_unlink(backing_path.dentry); + } + + d_drop(dentry); +out: + path_put(&backing_path); + if (err) + pr_debug("incfs: %s err:%d\n", __func__, err); + mutex_unlock(&mi->mi_dir_struct_mutex); + return err; +} + +static int dir_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *new_dentry) +{ + struct mount_info *mi = get_mount_info(dir->i_sb); + struct path backing_old_path = {}; + struct path backing_new_path = {}; + int error = 0; + + error = mutex_lock_interruptible(&mi->mi_dir_struct_mutex); + if (error) + return error; + + get_incfs_backing_path(old_dentry, &backing_old_path); + get_incfs_backing_path(new_dentry, &backing_new_path); + + if (backing_new_path.dentry->d_parent == mi->mi_index_dir) { + /* Can't link to .index */ + error = -EBUSY; + goto out; + } + + error = incfs_link(backing_old_path.dentry, backing_new_path.dentry); + if (!error) { + struct inode *inode = NULL; + struct dentry *bdentry = backing_new_path.dentry; + + if (d_really_is_negative(bdentry)) { + error = -EINVAL; + goto out; + } + + inode = fetch_regular_inode(dir->i_sb, bdentry); + if (IS_ERR(inode)) { + error = PTR_ERR(inode); + goto out; + } + d_instantiate(new_dentry, inode); + } + +out: + path_put(&backing_old_path); + path_put(&backing_new_path); + if (error) + pr_debug("incfs: %s err:%d\n", __func__, error); + mutex_unlock(&mi->mi_dir_struct_mutex); + return error; +} + +static int dir_rmdir(struct inode *dir, struct dentry *dentry) +{ + struct mount_info *mi = get_mount_info(dir->i_sb); + struct path backing_path = {}; + int err = 0; + + err = mutex_lock_interruptible(&mi->mi_dir_struct_mutex); + if (err) + return err; + + get_incfs_backing_path(dentry, &backing_path); + if (!backing_path.dentry) { + err = -EBADF; + goto out; + } + + if (backing_path.dentry == mi->mi_index_dir) { + /* Can't delete .index */ + err = -EBUSY; + goto out; + } + + err = incfs_rmdir(backing_path.dentry); + if (!err) + d_drop(dentry); +out: + path_put(&backing_path); + if (err) + pr_debug("incfs: %s err:%d\n", __func__, err); + mutex_unlock(&mi->mi_dir_struct_mutex); + return err; +} + +static int dir_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + struct mount_info *mi = get_mount_info(old_dir->i_sb); + struct dentry *backing_old_dentry; + struct dentry *backing_new_dentry; + struct dentry *backing_old_dir_dentry; + struct dentry *backing_new_dir_dentry; + struct inode *target_inode; + struct dentry *trap; + int error = 0; + + error = mutex_lock_interruptible(&mi->mi_dir_struct_mutex); + if (error) + return error; + + backing_old_dentry = get_incfs_dentry(old_dentry)->backing_path.dentry; + backing_new_dentry = get_incfs_dentry(new_dentry)->backing_path.dentry; + dget(backing_old_dentry); + dget(backing_new_dentry); + + backing_old_dir_dentry = dget_parent(backing_old_dentry); + backing_new_dir_dentry = dget_parent(backing_new_dentry); + target_inode = d_inode(new_dentry); + + if (backing_old_dir_dentry == mi->mi_index_dir) { + /* Direct moves from .index are not allowed. */ + error = -EBUSY; + goto out; + } + + trap = lock_rename(backing_old_dir_dentry, backing_new_dir_dentry); + + if (trap == backing_old_dentry) { + error = -EINVAL; + goto unlock_out; + } + if (trap == backing_new_dentry) { + error = -ENOTEMPTY; + goto unlock_out; + } + + error = vfs_rename(d_inode(backing_old_dir_dentry), backing_old_dentry, + d_inode(backing_new_dir_dentry), backing_new_dentry, + NULL, 0); + if (error) + goto unlock_out; + if (target_inode) + fsstack_copy_attr_all(target_inode, + get_incfs_node(target_inode)->n_backing_inode); + fsstack_copy_attr_all(new_dir, d_inode(backing_new_dir_dentry)); + if (new_dir != old_dir) + fsstack_copy_attr_all(old_dir, d_inode(backing_old_dir_dentry)); + +unlock_out: + unlock_rename(backing_old_dir_dentry, backing_new_dir_dentry); + +out: + dput(backing_new_dir_dentry); + dput(backing_old_dir_dentry); + dput(backing_new_dentry); + dput(backing_old_dentry); + + mutex_unlock(&mi->mi_dir_struct_mutex); + if (error) + pr_debug("incfs: %s err:%d\n", __func__, error); + return error; +} + + +static int file_open(struct inode *inode, struct file *file) +{ + struct mount_info *mi = get_mount_info(inode->i_sb); + struct file *backing_file = NULL; + struct path backing_path = {}; + int err = 0; + + get_incfs_backing_path(file->f_path.dentry, &backing_path); + backing_file = dentry_open(&backing_path, O_RDWR | O_NOATIME, + mi->mi_owner); + path_put(&backing_path); + + if (IS_ERR(backing_file)) { + err = PTR_ERR(backing_file); + backing_file = NULL; + goto out; + } + + if (S_ISREG(inode->i_mode)) + err = make_inode_ready_for_data_ops(mi, inode, backing_file); + else if (S_ISDIR(inode->i_mode)) { + struct dir_file *dir = NULL; + + dir = incfs_open_dir_file(mi, backing_file); + if (IS_ERR(dir)) + err = PTR_ERR(dir); + else + file->private_data = dir; + } else + err = -EBADF; + +out: + if (err) + pr_debug("incfs: %s name:%s err: %d\n", __func__, + file->f_path.dentry->d_name.name, err); + if (backing_file) + fput(backing_file); + return err; +} + +static int file_release(struct inode *inode, struct file *file) +{ + if (S_ISREG(inode->i_mode)) { + /* Do nothing. + * data_file is released only by inode eviction. + */ + } else if (S_ISDIR(inode->i_mode)) { + struct dir_file *dir = get_incfs_dir_file(file); + + incfs_free_dir_file(dir); + } + + return 0; +} + +static ssize_t file_write(struct file *f, const char __user *buf, + size_t size, loff_t *offset) +{ + struct data_file *df = get_incfs_data_file(f); + const ssize_t data_buf_size = 2 * INCFS_DATA_FILE_BLOCK_SIZE; + size_t block_count = size / sizeof(struct incfs_new_data_block); + struct incfs_new_data_block __user *usr_blocks = + (struct incfs_new_data_block __user *)buf; + u8 *data_buf = NULL; + ssize_t error = 0; + int i = 0; + + if (!df) + return -EBADF; + + if (!access_ok(VERIFY_READ, usr_blocks, size)) + return -EFAULT; + + data_buf = (u8 *)__get_free_pages(GFP_NOFS, get_order(data_buf_size)); + if (!data_buf) + return -ENOMEM; + + for (i = 0; i < block_count; i++) { + struct incfs_new_data_block block = {}; + + if (copy_from_user(&block, &usr_blocks[i], sizeof(block)) > 0) { + error = -EFAULT; + break; + } + + if (block.data_len > data_buf_size) { + error = -E2BIG; + break; + } + if (!access_ok(VERIFY_READ, u64_to_user_ptr(block.data), + block.data_len)) { + error = -EFAULT; + break; + } + if (copy_from_user(data_buf, u64_to_user_ptr(block.data), + block.data_len) > 0) { + error = -EFAULT; + break; + } + block.data = 0; /* To make sure nobody uses it. */ + if (block.flags & INCFS_BLOCK_FLAGS_HASH) { + error = incfs_process_new_hash_block(df, &block, + data_buf); + } else { + error = incfs_process_new_data_block(df, &block, + data_buf); + } + if (error) + break; + } + + if (data_buf) + free_pages((unsigned long)data_buf, get_order(data_buf_size)); + *offset = 0; + + /* + * Only report the error if no records were processed, otherwise + * just return how many were processed successfully. + */ + if (i == 0) + return error; + + return i * sizeof(struct incfs_new_data_block); +} + + +static int dentry_revalidate(struct dentry *d, unsigned int flags) +{ + struct path backing_path = {}; + struct inode_info *info = get_incfs_node(d_inode(d)); + struct inode *binode = (info == NULL) ? NULL : info->n_backing_inode; + struct dentry *backing_dentry = NULL; + int result = 0; + + if (flags & LOOKUP_RCU) + return -ECHILD; + + get_incfs_backing_path(d, &backing_path); + backing_dentry = backing_path.dentry; + if (!backing_dentry) + goto out; + + if (d_inode(backing_dentry) != binode) { + /* + * Backing inodes obtained via dentry and inode don't match. + * It indicates that most likely backing dir has changed + * directly bypassing Incremental FS interface. + */ + goto out; + } + + if (backing_dentry->d_flags & DCACHE_OP_REVALIDATE) { + result = backing_dentry->d_op->d_revalidate(backing_dentry, + flags); + } else + result = 1; + +out: + path_put(&backing_path); + return result; +} + +static void dentry_release(struct dentry *d) +{ + struct dentry_info *di = get_incfs_dentry(d); + + if (di) + path_put(&di->backing_path); + d->d_fsdata = NULL; +} + +static struct inode *alloc_inode(struct super_block *sb) +{ + struct inode_info *node = kzalloc(sizeof(*node), GFP_NOFS); + + /* TODO: add a slab-based cache here. */ + if (!node) + return NULL; + inode_init_once(&node->n_vfs_inode); + return &node->n_vfs_inode; +} + +static void free_inode(struct inode *inode) +{ + struct inode_info *node = get_incfs_node(inode); + + kfree(node); +} + +static void evict_inode(struct inode *inode) +{ + struct inode_info *node = get_incfs_node(inode); + + if (node) { + if (node->n_backing_inode) { + iput(node->n_backing_inode); + node->n_backing_inode = NULL; + } + if (node->n_file) { + incfs_free_data_file(node->n_file); + node->n_file = NULL; + } + } + + truncate_inode_pages(&inode->i_data, 0); + clear_inode(inode); +} + +static ssize_t incfs_getxattr(struct dentry *d, const char *name, + void *value, size_t size) +{ + struct dentry_info *di = get_incfs_dentry(d); + struct mount_info *mi = get_mount_info(d->d_sb); + char *stored_value; + size_t stored_size; + + if (di && di->backing_path.dentry) + return vfs_getxattr(di->backing_path.dentry, name, value, size); + + if (strcmp(name, "security.selinux")) + return -ENODATA; + + if (!strcmp(d->d_iname, INCFS_PENDING_READS_FILENAME)) { + stored_value = mi->pending_read_xattr; + stored_size = mi->pending_read_xattr_size; + } else if (!strcmp(d->d_iname, INCFS_LOG_FILENAME)) { + stored_value = mi->log_xattr; + stored_size = mi->log_xattr_size; + } else { + return -ENODATA; + } + + if (!stored_value) + return -ENODATA; + + if (stored_size > size) + return -E2BIG; + + memcpy(value, stored_value, stored_size); + return stored_size; + +} + + +static ssize_t incfs_setxattr(struct dentry *d, const char *name, + const void *value, size_t size, int flags) +{ + struct dentry_info *di = get_incfs_dentry(d); + struct mount_info *mi = get_mount_info(d->d_sb); + void **stored_value; + size_t *stored_size; + + if (di && di->backing_path.dentry) + return vfs_setxattr(di->backing_path.dentry, name, value, size, + flags); + + if (strcmp(name, "security.selinux")) + return -ENODATA; + + if (size > INCFS_MAX_FILE_ATTR_SIZE) + return -E2BIG; + + if (!strcmp(d->d_iname, INCFS_PENDING_READS_FILENAME)) { + stored_value = &mi->pending_read_xattr; + stored_size = &mi->pending_read_xattr_size; + } else if (!strcmp(d->d_iname, INCFS_LOG_FILENAME)) { + stored_value = &mi->log_xattr; + stored_size = &mi->log_xattr_size; + } else { + return -ENODATA; + } + + kfree (*stored_value); + *stored_value = kzalloc(size, GFP_NOFS); + if (!*stored_value) + return -ENOMEM; + + memcpy(*stored_value, value, size); + *stored_size = size; + return 0; +} + +static ssize_t incfs_listxattr(struct dentry *d, char *list, size_t size) +{ + struct dentry_info *di = get_incfs_dentry(d); + + if (!di || !di->backing_path.dentry) + return -ENODATA; + + return vfs_listxattr(di->backing_path.dentry, list, size); +} + +struct dentry *incfs_mount_fs(struct file_system_type *type, int flags, + const char *dev_name, void *data) +{ + struct mount_options options = {}; + struct mount_info *mi = NULL; + struct path backing_dir_path = {}; + struct dentry *index_dir; + struct super_block *src_fs_sb = NULL; + struct inode *root_inode = NULL; + struct super_block *sb = sget(type, NULL, set_anon_super, flags, NULL); + int error = 0; + + if (IS_ERR(sb)) + return ERR_CAST(sb); + + sb->s_op = &incfs_super_ops; + sb->s_d_op = &incfs_dentry_ops; + sb->s_flags |= S_NOATIME; + sb->s_magic = INCFS_MAGIC_NUMBER; + sb->s_time_gran = 1; + sb->s_blocksize = INCFS_DATA_FILE_BLOCK_SIZE; + sb->s_blocksize_bits = blksize_bits(sb->s_blocksize); + sb->s_xattr = incfs_xattr_ops; + + BUILD_BUG_ON(PAGE_SIZE != INCFS_DATA_FILE_BLOCK_SIZE); + + error = parse_options(&options, (char *)data); + if (error != 0) { + pr_err("incfs: Options parsing error. %d\n", error); + goto err; + } + + sb->s_bdi->ra_pages = options.readahead_pages; + if (!dev_name) { + pr_err("incfs: Backing dir is not set, filesystem can't be mounted.\n"); + error = -ENOENT; + goto err; + } + + error = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, + &backing_dir_path); + if (error || backing_dir_path.dentry == NULL || + !d_really_is_positive(backing_dir_path.dentry)) { + pr_err("incfs: Error accessing: %s.\n", + dev_name); + goto err; + } + src_fs_sb = backing_dir_path.dentry->d_sb; + sb->s_maxbytes = src_fs_sb->s_maxbytes; + + mi = incfs_alloc_mount_info(sb, &options, &backing_dir_path); + + if (IS_ERR_OR_NULL(mi)) { + error = PTR_ERR(mi); + pr_err("incfs: Error allocating mount info. %d\n", error); + mi = NULL; + goto err; + } + + index_dir = open_or_create_index_dir(backing_dir_path.dentry); + if (IS_ERR_OR_NULL(index_dir)) { + error = PTR_ERR(index_dir); + pr_err("incfs: Can't find or create .index dir in %s\n", + dev_name); + goto err; + } + mi->mi_index_dir = index_dir; + + sb->s_fs_info = mi; + root_inode = fetch_regular_inode(sb, backing_dir_path.dentry); + if (IS_ERR(root_inode)) { + error = PTR_ERR(root_inode); + goto err; + } + + sb->s_root = d_make_root(root_inode); + if (!sb->s_root) { + error = -ENOMEM; + goto err; + } + error = incfs_init_dentry(sb->s_root, &backing_dir_path); + if (error) + goto err; + + path_put(&backing_dir_path); + sb->s_flags |= SB_ACTIVE; + + pr_debug("infs: mount\n"); + return dget(sb->s_root); +err: + sb->s_fs_info = NULL; + path_put(&backing_dir_path); + incfs_free_mount_info(mi); + deactivate_locked_super(sb); + return ERR_PTR(error); +} + +static int incfs_remount_fs(struct super_block *sb, int *flags, char *data) +{ + struct mount_options options; + struct mount_info *mi = get_mount_info(sb); + int err = 0; + + sync_filesystem(sb); + err = parse_options(&options, (char *)data); + if (err) + return err; + + if (mi->mi_options.read_timeout_ms != options.read_timeout_ms) { + mi->mi_options.read_timeout_ms = options.read_timeout_ms; + pr_debug("incfs: new timeout_ms=%d", options.read_timeout_ms); + } + + pr_debug("infs: remount\n"); + return 0; +} + +void incfs_kill_sb(struct super_block *sb) +{ + struct mount_info *mi = sb->s_fs_info; + + pr_debug("infs: unmount\n"); + incfs_free_mount_info(mi); + generic_shutdown_super(sb); +} + +static int show_options(struct seq_file *m, struct dentry *root) +{ + struct mount_info *mi = get_mount_info(root->d_sb); + + seq_printf(m, ",read_timeout_ms=%u", mi->mi_options.read_timeout_ms); + seq_printf(m, ",readahead=%u", mi->mi_options.readahead_pages); + if (mi->mi_options.read_log_pages != 0) { + seq_printf(m, ",rlog_pages=%u", mi->mi_options.read_log_pages); + seq_printf(m, ",rlog_wakeup_cnt=%u", + mi->mi_options.read_log_wakeup_count); + } + if (mi->mi_options.no_backing_file_cache) + seq_puts(m, ",no_bf_cache"); + if (mi->mi_options.no_backing_file_readahead) + seq_puts(m, ",no_bf_readahead"); + return 0; +} diff --git a/fs/incfs/vfs.h b/fs/incfs/vfs.h new file mode 100644 index 0000000000000000000000000000000000000000..eaa490e1907211951da09624a1f88a0bd222a6ff --- /dev/null +++ b/fs/incfs/vfs.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2018 Google LLC + */ + +#ifndef _INCFS_VFS_H +#define _INCFS_VFS_H + +void incfs_kill_sb(struct super_block *sb); +struct dentry *incfs_mount_fs(struct file_system_type *type, int flags, + const char *dev_name, void *data); + +#endif diff --git a/fs/inode.c b/fs/inode.c index befe45f21123e0d8308e12df8f7eecce9378e4ff..453b0542567122d7f55c65700e46eb5715963483 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -2189,7 +2190,7 @@ int vfs_ioc_setflags_prepare(struct inode *inode, unsigned int oldflags, !capable(CAP_LINUX_IMMUTABLE)) return -EPERM; - return 0; + return fscrypt_prepare_setflags(inode, oldflags, flags); } EXPORT_SYMBOL(vfs_ioc_setflags_prepare); diff --git a/fs/jbd2/checkpoint.c b/fs/jbd2/checkpoint.c index 26f8d7e46462e1ba402ff7fc3050650aac1f096a..66409cbd3ed544242428d327a1189b6d324b3258 100644 --- a/fs/jbd2/checkpoint.c +++ b/fs/jbd2/checkpoint.c @@ -165,7 +165,7 @@ void __jbd2_log_wait_for_space(journal_t *journal) "journal space in %s\n", __func__, journal->j_devname); WARN_ON(1); - jbd2_journal_abort(journal, 0); + jbd2_journal_abort(journal, -EIO); } write_lock(&journal->j_state_lock); } else { diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index 020bd7a0d8e03f3d3334d3f949de53503219b49f..4200a6fe9599c72695df7d0ff4c0e17ce79bc696 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -781,7 +781,7 @@ void jbd2_journal_commit_transaction(journal_t *journal) err = journal_submit_commit_record(journal, commit_transaction, &cbh, crc32_sum); if (err) - __jbd2_journal_abort_hard(journal); + jbd2_journal_abort(journal, err); } blk_finish_plug(&plug); @@ -874,7 +874,7 @@ void jbd2_journal_commit_transaction(journal_t *journal) err = journal_submit_commit_record(journal, commit_transaction, &cbh, crc32_sum); if (err) - __jbd2_journal_abort_hard(journal); + jbd2_journal_abort(journal, err); } if (cbh) err = journal_wait_on_commit_record(journal, cbh); @@ -971,29 +971,33 @@ void jbd2_journal_commit_transaction(journal_t *journal) * it. */ /* - * A buffer which has been freed while still being journaled by - * a previous transaction. - */ - if (buffer_freed(bh)) { + * A buffer which has been freed while still being journaled + * by a previous transaction, refile the buffer to BJ_Forget of + * the running transaction. If the just committed transaction + * contains "add to orphan" operation, we can completely + * invalidate the buffer now. We are rather through in that + * since the buffer may be still accessible when blocksize < + * pagesize and it is attached to the last partial page. + */ + if (buffer_freed(bh) && !jh->b_next_transaction) { + struct address_space *mapping; + + clear_buffer_freed(bh); + clear_buffer_jbddirty(bh); + /* - * If the running transaction is the one containing - * "add to orphan" operation (b_next_transaction != - * NULL), we have to wait for that transaction to - * commit before we can really get rid of the buffer. - * So just clear b_modified to not confuse transaction - * credit accounting and refile the buffer to - * BJ_Forget of the running transaction. If the just - * committed transaction contains "add to orphan" - * operation, we can completely invalidate the buffer - * now. We are rather through in that since the - * buffer may be still accessible when blocksize < - * pagesize and it is attached to the last partial - * page. + * Block device buffers need to stay mapped all the + * time, so it is enough to clear buffer_jbddirty and + * buffer_freed bits. For the file mapping buffers (i.e. + * journalled data) we need to unmap buffer and clear + * more bits. We also need to be careful about the check + * because the data page mapping can get cleared under + * out hands, which alse need not to clear more bits + * because the page and buffers will be freed and can + * never be reused once we are done with them. */ - jh->b_modified = 0; - if (!jh->b_next_transaction) { - clear_buffer_freed(bh); - clear_buffer_jbddirty(bh); + mapping = READ_ONCE(bh->b_page->mapping); + if (mapping && !sb_is_blkdev_sb(mapping->host->i_sb)) { clear_buffer_mapped(bh); clear_buffer_new(bh); clear_buffer_req(bh); diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index df390a69c49a8cdd94401b6fd1e448d71c881ec2..a15a22d209090995a6ddc6440279665fff8a6f57 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -1002,6 +1002,7 @@ static void *jbd2_seq_info_start(struct seq_file *seq, loff_t *pos) static void *jbd2_seq_info_next(struct seq_file *seq, void *v, loff_t *pos) { + (*pos)++; return NULL; } @@ -1700,6 +1701,11 @@ int jbd2_journal_load(journal_t *journal) journal->j_devname); return -EFSCORRUPTED; } + /* + * clear JBD2_ABORT flag initialized in journal_init_common + * here to update log tail information with the newest seq. + */ + journal->j_flags &= ~JBD2_ABORT; /* OK, we've finished with the dynamic journal bits: * reinitialise the dynamic contents of the superblock in memory @@ -1707,7 +1713,6 @@ int jbd2_journal_load(journal_t *journal) if (journal_reset(journal)) goto recovery_error; - journal->j_flags &= ~JBD2_ABORT; journal->j_flags |= JBD2_LOADED; return 0; @@ -2128,8 +2133,7 @@ static void __journal_abort_soft (journal_t *journal, int errno) if (journal->j_flags & JBD2_ABORT) { write_unlock(&journal->j_state_lock); - if (!old_errno && old_errno != -ESHUTDOWN && - errno == -ESHUTDOWN) + if (old_errno != -ESHUTDOWN && errno == -ESHUTDOWN) jbd2_journal_update_sb_errno(journal); return; } @@ -2137,12 +2141,10 @@ static void __journal_abort_soft (journal_t *journal, int errno) __jbd2_journal_abort_hard(journal); - if (errno) { - jbd2_journal_update_sb_errno(journal); - write_lock(&journal->j_state_lock); - journal->j_flags |= JBD2_REC_ERR; - write_unlock(&journal->j_state_lock); - } + jbd2_journal_update_sb_errno(journal); + write_lock(&journal->j_state_lock); + journal->j_flags |= JBD2_REC_ERR; + write_unlock(&journal->j_state_lock); } /** @@ -2184,11 +2186,6 @@ static void __journal_abort_soft (journal_t *journal, int errno) * failure to disk. ext3_error, for example, now uses this * functionality. * - * Errors which originate from within the journaling layer will NOT - * supply an errno; a null errno implies that absolutely no further - * writes are done to the journal (unless there are any already in - * progress). - * */ void jbd2_journal_abort(journal_t *journal, int errno) diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index 911ff18249b75615e2151a50c4f6d3fa9436ccdc..04ffef9cea8ccfd16db5bc6965153a8c25477e00 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -831,8 +831,6 @@ do_get_write_access(handle_t *handle, struct journal_head *jh, char *frozen_buffer = NULL; unsigned long start_lock, time_lock; - if (is_handle_aborted(handle)) - return -EROFS; journal = transaction->t_journal; jbd_debug(5, "journal_head %p, force_copy %d\n", jh, force_copy); @@ -1084,6 +1082,9 @@ int jbd2_journal_get_write_access(handle_t *handle, struct buffer_head *bh) struct journal_head *jh; int rc; + if (is_handle_aborted(handle)) + return -EROFS; + if (jbd2_write_access_granted(handle, bh, false)) return 0; @@ -1221,6 +1222,9 @@ int jbd2_journal_get_undo_access(handle_t *handle, struct buffer_head *bh) struct journal_head *jh; char *committed_data = NULL; + if (is_handle_aborted(handle)) + return -EROFS; + if (jbd2_write_access_granted(handle, bh, true)) return 0; @@ -2228,14 +2232,16 @@ static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh, return -EBUSY; } /* - * OK, buffer won't be reachable after truncate. We just set - * j_next_transaction to the running transaction (if there is - * one) and mark buffer as freed so that commit code knows it - * should clear dirty bits when it is done with the buffer. + * OK, buffer won't be reachable after truncate. We just clear + * b_modified to not confuse transaction credit accounting, and + * set j_next_transaction to the running transaction (if there + * is one) and mark buffer as freed so that commit code knows + * it should clear dirty bits when it is done with the buffer. */ set_buffer_freed(bh); if (journal->j_running_transaction && buffer_jbddirty(bh)) jh->b_next_transaction = journal->j_running_transaction; + jh->b_modified = 0; jbd2_journal_put_journal_head(jh); spin_unlock(&journal->j_list_lock); jbd_unlock_bh_state(bh); diff --git a/fs/jfs/jfs_txnmgr.c b/fs/jfs/jfs_txnmgr.c index a5663cb621d8d16cb55968e2c4d8d0b86a7fbe3a..78789c5ed36b02f7106c40630e484656612c891f 100644 --- a/fs/jfs/jfs_txnmgr.c +++ b/fs/jfs/jfs_txnmgr.c @@ -1928,8 +1928,7 @@ static void xtLog(struct jfs_log * log, struct tblock * tblk, struct lrd * lrd, * header ? */ if (tlck->type & tlckTRUNCATE) { - /* This odd declaration suppresses a bogus gcc warning */ - pxd_t pxd = pxd; /* truncated extent of xad */ + pxd_t pxd; /* truncated extent of xad */ int twm; /* diff --git a/fs/libfs.c b/fs/libfs.c index bd2d193d0a2a11d516dd78d5d6910603526fe7d7..e8835ebb1092aeadaece02106d27dcec69bd0c57 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -16,6 +16,8 @@ #include #include #include /* sync_mapping_buffers */ +#include +#include #include @@ -1258,3 +1260,112 @@ bool is_empty_dir_inode(struct inode *inode) return (inode->i_fop == &empty_dir_operations) && (inode->i_op == &empty_dir_inode_operations); } + +#ifdef CONFIG_UNICODE +bool needs_casefold(const struct inode *dir) +{ + return IS_CASEFOLDED(dir) && dir->i_sb->s_encoding && + (!IS_ENCRYPTED(dir) || fscrypt_has_encryption_key(dir)); +} +EXPORT_SYMBOL(needs_casefold); + +int generic_ci_d_compare(const struct dentry *dentry, unsigned int len, + const char *str, const struct qstr *name) +{ + const struct dentry *parent = READ_ONCE(dentry->d_parent); + const struct inode *inode = READ_ONCE(parent->d_inode); + const struct super_block *sb = dentry->d_sb; + const struct unicode_map *um = sb->s_encoding; + struct qstr entry = QSTR_INIT(str, len); + int ret; + + if (!inode || !needs_casefold(inode)) + goto fallback; + + ret = utf8_strncasecmp(um, name, &entry); + if (ret >= 0) + return ret; + + if (sb_has_enc_strict_mode(sb)) + return -EINVAL; +fallback: + if (len != name->len) + return 1; + return !!memcmp(str, name->name, len); +} +EXPORT_SYMBOL(generic_ci_d_compare); + +int generic_ci_d_hash(const struct dentry *dentry, struct qstr *str) +{ + const struct inode *inode = READ_ONCE(dentry->d_inode); + struct super_block *sb = dentry->d_sb; + const struct unicode_map *um = sb->s_encoding; + int ret = 0; + + if (!inode || !needs_casefold(inode)) + return 0; + + ret = utf8_casefold_hash(um, dentry, str); + if (ret < 0) + goto err; + + return 0; +err: + if (sb_has_enc_strict_mode(sb)) + ret = -EINVAL; + else + ret = 0; + return ret; +} +EXPORT_SYMBOL(generic_ci_d_hash); + +static const struct dentry_operations generic_ci_dentry_ops = { + .d_hash = generic_ci_d_hash, + .d_compare = generic_ci_d_compare, +}; +#endif + +#ifdef CONFIG_FS_ENCRYPTION +static const struct dentry_operations generic_encrypted_dentry_ops = { + .d_revalidate = fscrypt_d_revalidate, +}; +#endif + +#if IS_ENABLED(CONFIG_UNICODE) && IS_ENABLED(CONFIG_FS_ENCRYPTION) +static const struct dentry_operations generic_encrypted_ci_dentry_ops = { + .d_hash = generic_ci_d_hash, + .d_compare = generic_ci_d_compare, + .d_revalidate = fscrypt_d_revalidate, +}; +#endif + +/** + * generic_set_encrypted_ci_d_ops - helper for setting d_ops for given dentry + * @dir: parent of dentry whose ops to set + * @dentry: detnry to set ops on + * + * This function sets the dentry ops for the given dentry to handle both + * casefolding and encryption of the dentry name. + */ +void generic_set_encrypted_ci_d_ops(struct inode *dir, struct dentry *dentry) +{ +#ifdef CONFIG_FS_ENCRYPTION + if (dentry->d_flags & DCACHE_ENCRYPTED_NAME) { +#ifdef CONFIG_UNICODE + if (dir->i_sb->s_encoding) { + d_set_d_op(dentry, &generic_encrypted_ci_dentry_ops); + return; + } +#endif + d_set_d_op(dentry, &generic_encrypted_dentry_ops); + return; + } +#endif +#ifdef CONFIG_UNICODE + if (dir->i_sb->s_encoding) { + d_set_d_op(dentry, &generic_ci_dentry_ops); + return; + } +#endif +} +EXPORT_SYMBOL(generic_set_encrypted_ci_d_ops); diff --git a/fs/namei.c b/fs/namei.c index c99cb21b377fbe7d4c05175f585eca61b9dafdaa..8492dc67fc3cddd33a1f5a2deaa686ede21d3c72 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1103,7 +1103,8 @@ static int may_linkat(struct path *link) * may_create_in_sticky - Check whether an O_CREAT open in a sticky directory * should be allowed, or not, on files that already * exist. - * @dir: the sticky parent directory + * @dir_mode: mode bits of directory + * @dir_uid: owner of directory * @inode: the inode of the file to open * * Block an O_CREAT open of a FIFO (or a regular file) when: @@ -1119,18 +1120,18 @@ static int may_linkat(struct path *link) * * Returns 0 if the open is allowed, -ve on error. */ -static int may_create_in_sticky(struct dentry * const dir, +static int may_create_in_sticky(umode_t dir_mode, kuid_t dir_uid, struct inode * const inode) { if ((!sysctl_protected_fifos && S_ISFIFO(inode->i_mode)) || (!sysctl_protected_regular && S_ISREG(inode->i_mode)) || - likely(!(dir->d_inode->i_mode & S_ISVTX)) || - uid_eq(inode->i_uid, dir->d_inode->i_uid) || + likely(!(dir_mode & S_ISVTX)) || + uid_eq(inode->i_uid, dir_uid) || uid_eq(current_fsuid(), inode->i_uid)) return 0; - if (likely(dir->d_inode->i_mode & 0002) || - (dir->d_inode->i_mode & 0020 && + if (likely(dir_mode & 0002) || + (dir_mode & 0020 && ((sysctl_protected_fifos >= 2 && S_ISFIFO(inode->i_mode)) || (sysctl_protected_regular >= 2 && S_ISREG(inode->i_mode))))) { return -EACCES; @@ -1461,7 +1462,7 @@ static int follow_dotdot_rcu(struct nameidata *nd) nd->path.dentry = parent; nd->seq = seq; if (unlikely(!path_connected(&nd->path))) - return -ENOENT; + return -ECHILD; break; } else { struct mount *mnt = real_mount(nd->path.mnt); @@ -3375,6 +3376,8 @@ static int do_last(struct nameidata *nd, struct file *file, const struct open_flags *op) { struct dentry *dir = nd->path.dentry; + kuid_t dir_uid = nd->inode->i_uid; + umode_t dir_mode = nd->inode->i_mode; int open_flag = op->open_flag; bool will_truncate = (open_flag & O_TRUNC) != 0; bool got_write = false; @@ -3510,7 +3513,7 @@ static int do_last(struct nameidata *nd, error = -EISDIR; if (d_is_dir(nd->path.dentry)) goto out; - error = may_create_in_sticky(dir, + error = may_create_in_sticky(dir_mode, dir_uid, d_backing_inode(nd->path.dentry)); if (unlikely(error)) goto out; diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig index 5f93cfacb3d14b9befc6ba33a91d79ccde044d89..ac3e06367cb689bc8c522fcab67bbf5c423b3f31 100644 --- a/fs/nfs/Kconfig +++ b/fs/nfs/Kconfig @@ -89,7 +89,7 @@ config NFS_V4 config NFS_SWAP bool "Provide swap over NFS support" default n - depends on NFS_FS + depends on NFS_FS && SWAP select SUNRPC_SWAP help This option enables swapon to work on files located on NFS mounts. diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 74ff459b75efa2095d0a03c38fdf02e2b7d34d3c..b0c0c2fc2fbac8e353432225e032ff9fa4b65681 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -240,6 +240,8 @@ static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation spin_lock(&delegation->lock); if (delegation->inode != NULL) inode = igrab(delegation->inode); + if (!inode) + set_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags); spin_unlock(&delegation->lock); return inode; } @@ -955,10 +957,11 @@ void nfs_delegation_reap_unclaimed(struct nfs_client *clp) list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { list_for_each_entry_rcu(delegation, &server->delegations, super_list) { - if (test_bit(NFS_DELEGATION_RETURNING, - &delegation->flags)) - continue; - if (test_bit(NFS_DELEGATION_NEED_RECLAIM, + if (test_bit(NFS_DELEGATION_INODE_FREEING, + &delegation->flags) || + test_bit(NFS_DELEGATION_RETURNING, + &delegation->flags) || + test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags) == 0) continue; if (!nfs_sb_active(server->super)) @@ -1064,10 +1067,11 @@ void nfs_reap_expired_delegations(struct nfs_client *clp) list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { list_for_each_entry_rcu(delegation, &server->delegations, super_list) { - if (test_bit(NFS_DELEGATION_RETURNING, - &delegation->flags)) - continue; - if (test_bit(NFS_DELEGATION_TEST_EXPIRED, + if (test_bit(NFS_DELEGATION_INODE_FREEING, + &delegation->flags) || + test_bit(NFS_DELEGATION_RETURNING, + &delegation->flags) || + test_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags) == 0) continue; if (!nfs_sb_active(server->super)) diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h index dd0f3eed3890de367afa06b47b1a2cee1ed17c18..f09b153ac82ff65dcb5b3a7a9671942f07d9926f 100644 --- a/fs/nfs/delegation.h +++ b/fs/nfs/delegation.h @@ -34,6 +34,7 @@ enum { NFS_DELEGATION_RETURNING, NFS_DELEGATION_REVOKED, NFS_DELEGATION_TEST_EXPIRED, + NFS_DELEGATION_INODE_FREEING, }; int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index afdf55ad85cb872c728ea693ae5985690e013b2a..4a5d9ab02cb9053a17566bab36d728910a66dfb8 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -162,6 +162,17 @@ typedef struct { bool eof; } nfs_readdir_descriptor_t; +static +void nfs_readdir_init_array(struct page *page) +{ + struct nfs_cache_array *array; + + array = kmap_atomic(page); + memset(array, 0, sizeof(struct nfs_cache_array)); + array->eof_index = -1; + kunmap_atomic(array); +} + /* * we are freeing strings created by nfs_add_to_readdir_array() */ @@ -174,6 +185,7 @@ void nfs_readdir_clear_array(struct page *page) array = kmap_atomic(page); for (i = 0; i < array->size; i++) kfree(array->array[i].string.name); + array->size = 0; kunmap_atomic(array); } @@ -610,6 +622,8 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page, int status = -ENOMEM; unsigned int array_size = ARRAY_SIZE(pages); + nfs_readdir_init_array(page); + entry.prev_cookie = 0; entry.cookie = desc->last_cookie; entry.eof = 0; @@ -626,8 +640,6 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page, } array = kmap(page); - memset(array, 0, sizeof(struct nfs_cache_array)); - array->eof_index = -1; status = nfs_readdir_alloc_pages(pages, array_size); if (status < 0) @@ -682,6 +694,7 @@ int nfs_readdir_filler(void *data, struct page* page) unlock_page(page); return 0; error: + nfs_readdir_clear_array(page); unlock_page(page); return ret; } @@ -689,8 +702,6 @@ int nfs_readdir_filler(void *data, struct page* page) static void cache_page_release(nfs_readdir_descriptor_t *desc) { - if (!desc->page->mapping) - nfs_readdir_clear_array(desc->page); put_page(desc->page); desc->page = NULL; } @@ -704,19 +715,28 @@ struct page *get_cache_page(nfs_readdir_descriptor_t *desc) /* * Returns 0 if desc->dir_cookie was found on page desc->page_index + * and locks the page to prevent removal from the page cache. */ static -int find_cache_page(nfs_readdir_descriptor_t *desc) +int find_and_lock_cache_page(nfs_readdir_descriptor_t *desc) { int res; desc->page = get_cache_page(desc); if (IS_ERR(desc->page)) return PTR_ERR(desc->page); - - res = nfs_readdir_search_array(desc); + res = lock_page_killable(desc->page); if (res != 0) - cache_page_release(desc); + goto error; + res = -EAGAIN; + if (desc->page->mapping != NULL) { + res = nfs_readdir_search_array(desc); + if (res == 0) + return 0; + } + unlock_page(desc->page); +error: + cache_page_release(desc); return res; } @@ -731,7 +751,7 @@ int readdir_search_pagecache(nfs_readdir_descriptor_t *desc) desc->last_cookie = 0; } do { - res = find_cache_page(desc); + res = find_and_lock_cache_page(desc); } while (res == -EAGAIN); return res; } @@ -770,7 +790,6 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc) desc->eof = true; kunmap(desc->page); - cache_page_release(desc); dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n", (unsigned long long)*desc->dir_cookie, res); return res; @@ -816,13 +835,13 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc) status = nfs_do_filldir(desc); + out_release: + nfs_readdir_clear_array(desc->page); + cache_page_release(desc); out: dfprintk(DIRCACHE, "NFS: %s: returns %d\n", __func__, status); return status; - out_release: - cache_page_release(desc); - goto out; } /* The file offset position represents the dirent entry number. A @@ -887,6 +906,8 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx) break; res = nfs_do_filldir(desc); + unlock_page(desc->page); + cache_page_release(desc); if (res < 0) break; } while (!desc->eof); diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 29b70337dcd9fd14a9217195a6787b3bb377aba4..c61bd3fc723ee2ce295d7eea6aeb00e148986736 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -261,10 +261,10 @@ static int nfs_direct_cmp_commit_data_verf(struct nfs_direct_req *dreq, data->ds_commit_index); /* verifier not set so always fail */ - if (verfp->committed < 0) + if (verfp->committed < 0 || data->res.verf->committed <= NFS_UNSTABLE) return 1; - return nfs_direct_cmp_verf(verfp, &data->verf); + return nfs_direct_cmp_verf(verfp, data->res.verf); } /** diff --git a/fs/nfs/flexfilelayout/flexfilelayout.h b/fs/nfs/flexfilelayout/flexfilelayout.h index de50a342d5a50503198bc0f8980e36599fec48f3..2ac99124474cb21036990ff2a4d3e3c53548f9d9 100644 --- a/fs/nfs/flexfilelayout/flexfilelayout.h +++ b/fs/nfs/flexfilelayout/flexfilelayout.h @@ -132,16 +132,6 @@ FF_LAYOUT_LSEG(struct pnfs_layout_segment *lseg) generic_hdr); } -static inline struct nfs4_deviceid_node * -FF_LAYOUT_DEVID_NODE(struct pnfs_layout_segment *lseg, u32 idx) -{ - if (idx >= FF_LAYOUT_LSEG(lseg)->mirror_array_cnt || - FF_LAYOUT_LSEG(lseg)->mirror_array[idx] == NULL || - FF_LAYOUT_LSEG(lseg)->mirror_array[idx]->mirror_ds == NULL) - return NULL; - return &FF_LAYOUT_LSEG(lseg)->mirror_array[idx]->mirror_ds->id_node; -} - static inline struct nfs4_ff_layout_ds * FF_LAYOUT_MIRROR_DS(struct nfs4_deviceid_node *node) { @@ -151,9 +141,25 @@ FF_LAYOUT_MIRROR_DS(struct nfs4_deviceid_node *node) static inline struct nfs4_ff_layout_mirror * FF_LAYOUT_COMP(struct pnfs_layout_segment *lseg, u32 idx) { - if (idx >= FF_LAYOUT_LSEG(lseg)->mirror_array_cnt) - return NULL; - return FF_LAYOUT_LSEG(lseg)->mirror_array[idx]; + struct nfs4_ff_layout_segment *fls = FF_LAYOUT_LSEG(lseg); + + if (idx < fls->mirror_array_cnt) + return fls->mirror_array[idx]; + return NULL; +} + +static inline struct nfs4_deviceid_node * +FF_LAYOUT_DEVID_NODE(struct pnfs_layout_segment *lseg, u32 idx) +{ + struct nfs4_ff_layout_mirror *mirror = FF_LAYOUT_COMP(lseg, idx); + + if (mirror != NULL) { + struct nfs4_ff_layout_ds *mirror_ds = mirror->mirror_ds; + + if (!IS_ERR_OR_NULL(mirror_ds)) + return &mirror_ds->id_node; + } + return NULL; } static inline u32 diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index 350675e3ed479e11af839fcbbc9b99e1d972b068..040a05f0e61ef0ce98c8b2645e17c402f814a940 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c @@ -385,7 +385,7 @@ static void encode_sattr(struct xdr_stream *xdr, const struct iattr *attr) } else p = xdr_time_not_set(p); if (attr->ia_valid & ATTR_MTIME_SET) { - ts = timespec64_to_timespec(attr->ia_atime); + ts = timespec64_to_timespec(attr->ia_mtime); xdr_encode_time(p, &ts); } else if (attr->ia_valid & ATTR_MTIME) { ts = timespec64_to_timespec(attr->ia_mtime); diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c index 64e4fa33d89f0e347155db74507862ec6f09fcc8..9956453aa6ffc8cd498cd6fbe8b4139959207eae 100644 --- a/fs/nfs/nfs3xdr.c +++ b/fs/nfs/nfs3xdr.c @@ -2380,6 +2380,7 @@ static int nfs3_xdr_dec_commit3res(struct rpc_rqst *req, void *data) { struct nfs_commitres *result = data; + struct nfs_writeverf *verf = result->verf; enum nfs_stat status; int error; @@ -2392,7 +2393,9 @@ static int nfs3_xdr_dec_commit3res(struct rpc_rqst *req, result->op_status = status; if (status != NFS3_OK) goto out_status; - error = decode_writeverf3(xdr, &result->verf->verifier); + error = decode_writeverf3(xdr, &verf->verifier); + if (!error) + verf->committed = NFS_FILE_SYNC; out: return error; out_status: diff --git a/fs/nfs/nfs42proc.c b/fs/nfs/nfs42proc.c index 94f98e190e632bbc32183d4d0bc05c7e22ca8fea..526441de89c1de12b7984a3cb26f641bd6d88166 100644 --- a/fs/nfs/nfs42proc.c +++ b/fs/nfs/nfs42proc.c @@ -283,14 +283,14 @@ static ssize_t _nfs42_proc_copy(struct file *src, status = handle_async_copy(res, server, src, dst, &args->src_stateid); if (status) - return status; + goto out; } if ((!res->synchronous || !args->sync) && res->write_res.verifier.committed != NFS_FILE_SYNC) { status = process_copy_commit(dst, pos_dst, res); if (status) - return status; + goto out; } truncate_pagecache_range(dst_inode, pos_dst, diff --git a/fs/nfs/nfs42xdr.c b/fs/nfs/nfs42xdr.c index 69f72ed2bf879310f0ece97fab1e4cccea75a402..ec9803088f6b8ce15f5e5ab644e8eefcbdbcbcd1 100644 --- a/fs/nfs/nfs42xdr.c +++ b/fs/nfs/nfs42xdr.c @@ -59,43 +59,53 @@ #define decode_clone_maxsz (op_decode_hdr_maxsz) #define NFS4_enc_allocate_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_allocate_maxsz + \ encode_getattr_maxsz) #define NFS4_dec_allocate_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_allocate_maxsz + \ decode_getattr_maxsz) #define NFS4_enc_copy_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_savefh_maxsz + \ encode_putfh_maxsz + \ encode_copy_maxsz + \ encode_commit_maxsz) #define NFS4_dec_copy_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_savefh_maxsz + \ decode_putfh_maxsz + \ decode_copy_maxsz + \ decode_commit_maxsz) #define NFS4_enc_offload_cancel_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_offload_cancel_maxsz) #define NFS4_dec_offload_cancel_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_offload_cancel_maxsz) #define NFS4_enc_deallocate_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_deallocate_maxsz + \ encode_getattr_maxsz) #define NFS4_dec_deallocate_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_deallocate_maxsz + \ decode_getattr_maxsz) #define NFS4_enc_seek_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_seek_maxsz) #define NFS4_dec_seek_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_seek_maxsz) #define NFS4_enc_layoutstats_sz (compound_encode_hdr_maxsz + \ diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 792f8821b5d6b4ad7fc192599e0577bb54536c63..668b648064b724d0f1a8494e0a8af6e79b01cb62 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -3089,6 +3089,11 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir, exception.retry = 1; continue; } + if (status == -NFS4ERR_EXPIRED) { + nfs4_schedule_lease_recovery(server->nfs_client); + exception.retry = 1; + continue; + } if (status == -EAGAIN) { /* We must have found a delegation */ exception.retry = 1; @@ -5112,7 +5117,7 @@ static void nfs4_proc_write_setup(struct nfs_pgio_header *hdr, hdr->timestamp = jiffies; msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_WRITE]; - nfs4_init_sequence(&hdr->args.seq_args, &hdr->res.seq_res, 1, 0); + nfs4_init_sequence(&hdr->args.seq_args, &hdr->res.seq_res, 0, 0); nfs4_state_protect_write(server->nfs_client, clnt, msg, hdr); } @@ -6103,8 +6108,10 @@ static void nfs4_delegreturn_prepare(struct rpc_task *task, void *data) d_data = (struct nfs4_delegreturndata *)data; - if (!d_data->lr.roc && nfs4_wait_on_layoutreturn(d_data->inode, task)) + if (!d_data->lr.roc && nfs4_wait_on_layoutreturn(d_data->inode, task)) { + nfs4_sequence_done(task, &d_data->res.seq_res); return; + } lo = d_data->args.lr_args ? d_data->args.lr_args->layout : NULL; if (lo && !pnfs_layout_is_valid(lo)) { diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 1c0227c78a7bc4a9b0f57549c94e297deb1b9875..c4cf0192d7bb8c0896fd0aed70b5a03c041ed074 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -4439,11 +4439,14 @@ static int decode_write_verifier(struct xdr_stream *xdr, struct nfs_write_verifi static int decode_commit(struct xdr_stream *xdr, struct nfs_commitres *res) { + struct nfs_writeverf *verf = res->verf; int status; status = decode_op_hdr(xdr, OP_COMMIT); if (!status) - status = decode_write_verifier(xdr, &res->verf->verifier); + status = decode_write_verifier(xdr, &verf->verifier); + if (!status) + verf->committed = NFS_FILE_SYNC; return status; } diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index c818f9886f61888a07d15d630a479725b0014e50..66f699e18755c9d44c275188757665c469f60f15 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -758,22 +758,35 @@ static int pnfs_layout_bulk_destroy_byserver_locked(struct nfs_client *clp, struct nfs_server *server, struct list_head *layout_list) + __must_hold(&clp->cl_lock) + __must_hold(RCU) { struct pnfs_layout_hdr *lo, *next; struct inode *inode; list_for_each_entry_safe(lo, next, &server->layouts, plh_layouts) { - if (test_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags)) + if (test_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags) || + test_bit(NFS_LAYOUT_INODE_FREEING, &lo->plh_flags) || + !list_empty(&lo->plh_bulk_destroy)) continue; + /* If the sb is being destroyed, just bail */ + if (!nfs_sb_active(server->super)) + break; inode = igrab(lo->plh_inode); - if (inode == NULL) - continue; - list_del_init(&lo->plh_layouts); - if (pnfs_layout_add_bulk_destroy_list(inode, layout_list)) - continue; - rcu_read_unlock(); - spin_unlock(&clp->cl_lock); - iput(inode); + if (inode != NULL) { + list_del_init(&lo->plh_layouts); + if (pnfs_layout_add_bulk_destroy_list(inode, + layout_list)) + continue; + rcu_read_unlock(); + spin_unlock(&clp->cl_lock); + iput(inode); + } else { + rcu_read_unlock(); + spin_unlock(&clp->cl_lock); + set_bit(NFS_LAYOUT_INODE_FREEING, &lo->plh_flags); + } + nfs_sb_deactive(server->super); spin_lock(&clp->cl_lock); rcu_read_lock(); return -EAGAIN; @@ -811,7 +824,7 @@ pnfs_layout_free_bulk_destroy_list(struct list_head *layout_list, /* Free all lsegs that are attached to commit buckets */ nfs_commit_inode(inode, 0); pnfs_put_layout_hdr(lo); - iput(inode); + nfs_iput_and_deactive(inode); } return ret; } diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h index ece367ebde6928204418e51321401e06bc4fe9aa..3ba44819a88ae744561d414e4d32902e23e17551 100644 --- a/fs/nfs/pnfs.h +++ b/fs/nfs/pnfs.h @@ -104,6 +104,7 @@ enum { NFS_LAYOUT_RETURN_REQUESTED, /* Return this layout ASAP */ NFS_LAYOUT_INVALID_STID, /* layout stateid id is invalid */ NFS_LAYOUT_FIRST_LAYOUTGET, /* Serialize first layoutget */ + NFS_LAYOUT_INODE_FREEING, /* The inode is being freed */ }; enum layoutdriver_policy_flags { diff --git a/fs/nfs/pnfs_nfs.c b/fs/nfs/pnfs_nfs.c index d5e4d3cd8c7f15762ef11de727a2fbcdb10c3f7a..acfb52bc0007dcf401be1f82ad6d1f9acc6c7f23 100644 --- a/fs/nfs/pnfs_nfs.c +++ b/fs/nfs/pnfs_nfs.c @@ -30,12 +30,11 @@ EXPORT_SYMBOL_GPL(pnfs_generic_rw_release); /* Fake up some data that will cause nfs_commit_release to retry the writes. */ void pnfs_generic_prepare_to_resend_writes(struct nfs_commit_data *data) { - struct nfs_page *first = nfs_list_entry(data->pages.next); + struct nfs_writeverf *verf = data->res.verf; data->task.tk_status = 0; - memcpy(&data->verf.verifier, &first->wb_verf, - sizeof(data->verf.verifier)); - data->verf.verifier.data[0]++; /* ensure verifier mismatch */ + memset(&verf->verifier, 0, sizeof(verf->verifier)); + verf->committed = NFS_UNSTABLE; } EXPORT_SYMBOL_GPL(pnfs_generic_prepare_to_resend_writes); diff --git a/fs/nfs/super.c b/fs/nfs/super.c index d90efdea9fbd6fba6058d38c1e6221368eac6007..5db7aceb41907293f428456b0626d3fa9df04c00 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -1930,7 +1930,7 @@ static int nfs_parse_devname(const char *dev_name, /* kill possible hostname list: not supported */ comma = strchr(dev_name, ','); if (comma != NULL && comma < end) - *comma = 0; + len = comma - dev_name; } if (len > maxnamlen) diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 117ffd90419e28348dca0c5a7f02808ab11ec678..ce1da8cbac0038ba41b014f2f2bd0c126b15a6bc 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -240,7 +240,15 @@ static void nfs_grow_file(struct page *page, unsigned int offset, unsigned int c /* A writeback failed: mark the page as bad, and invalidate the page cache */ static void nfs_set_pageerror(struct address_space *mapping) { + struct inode *inode = mapping->host; + nfs_zap_mapping(mapping->host, mapping); + /* Force file size revalidation */ + spin_lock(&inode->i_lock); + NFS_I(inode)->cache_validity |= NFS_INO_REVAL_FORCED | + NFS_INO_REVAL_PAGECACHE | + NFS_INO_INVALID_SIZE; + spin_unlock(&inode->i_lock); } /* @@ -646,7 +654,7 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio, return ret; out_launder: nfs_write_error_remove_page(req); - return ret; + return 0; } static int nfs_do_writepage(struct page *page, struct writeback_control *wbc, @@ -1806,6 +1814,7 @@ static void nfs_commit_done(struct rpc_task *task, void *calldata) static void nfs_commit_release_pages(struct nfs_commit_data *data) { + const struct nfs_writeverf *verf = data->res.verf; struct nfs_page *req; int status = data->task.tk_status; struct nfs_commit_info cinfo; @@ -1832,7 +1841,8 @@ static void nfs_commit_release_pages(struct nfs_commit_data *data) /* Okay, COMMIT succeeded, apparently. Check the verifier * returned by the server against all stored verfs. */ - if (!nfs_write_verifier_cmp(&req->wb_verf, &data->verf.verifier)) { + if (verf->committed > NFS_UNSTABLE && + !nfs_write_verifier_cmp(&req->wb_verf, &verf->verifier)) { /* We have a match */ if (req->wb_page) nfs_inode_remove_request(req); diff --git a/fs/nfsd/nfs4layouts.c b/fs/nfsd/nfs4layouts.c index 2b36aa037ce03a70e598aea2e0f387168b3806e1..f4cf1c0793c6a0be6f558b4419daba1cd7d72509 100644 --- a/fs/nfsd/nfs4layouts.c +++ b/fs/nfsd/nfs4layouts.c @@ -676,7 +676,7 @@ nfsd4_cb_layout_done(struct nfsd4_callback *cb, struct rpc_task *task) /* Client gets 2 lease periods to return it */ cutoff = ktime_add_ns(task->tk_start, - nn->nfsd4_lease * NSEC_PER_SEC * 2); + (u64)nn->nfsd4_lease * NSEC_PER_SEC * 2); if (ktime_before(now, cutoff)) { rpc_delay(task, HZ/100); /* 10 mili-seconds */ diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index c8ce128e00540e607dbba8df5f11031fabaece68..ed73e86194fac4ff1de08a62ecf18d6d5ea125d6 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -6075,7 +6075,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, } if (fl_flags & FL_SLEEP) { - nbl->nbl_time = jiffies; + nbl->nbl_time = get_seconds(); spin_lock(&nn->blocked_locks_lock); list_add_tail(&nbl->nbl_list, &lock_sop->lo_blocked); list_add_tail(&nbl->nbl_lru, &nn->blocked_locks_lru); diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 0f07ad6dc1efe10af97f1e28c67ae29dbb530e33..594fc1a8b67279a10181d0fd3f2380237d41be1c 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -592,7 +592,7 @@ static inline bool nfsd4_stateid_generation_after(stateid_t *a, stateid_t *b) struct nfsd4_blocked_lock { struct list_head nbl_list; struct list_head nbl_lru; - unsigned long nbl_time; + time_t nbl_time; struct file_lock nbl_lock; struct knfsd_fh nbl_fh; struct nfsd4_callback nbl_cb; diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 4fe8db3149506c59e098812de14ccebab7acdf61..80cededcd10d68899e654bc4d2af5f770d32a628 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -1016,6 +1016,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, host_err = vfs_iter_write(file, &iter, &pos, flags); if (host_err < 0) goto out_nfserr; + *cnt = host_err; nfsdstats.io_write += *cnt; fsnotify_modify(file); diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index a3e077fcfeb9bb985ee6253e689d70ec04a13ea0..fbd70111a2f12046026d9cbb3f36582e15925227 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -2109,17 +2109,15 @@ static int ocfs2_is_io_unaligned(struct inode *inode, size_t count, loff_t pos) static int ocfs2_inode_lock_for_extent_tree(struct inode *inode, struct buffer_head **di_bh, int meta_level, - int overwrite_io, int write_sem, int wait) { int ret = 0; if (wait) - ret = ocfs2_inode_lock(inode, NULL, meta_level); + ret = ocfs2_inode_lock(inode, di_bh, meta_level); else - ret = ocfs2_try_inode_lock(inode, - overwrite_io ? NULL : di_bh, meta_level); + ret = ocfs2_try_inode_lock(inode, di_bh, meta_level); if (ret < 0) goto out; @@ -2144,6 +2142,7 @@ static int ocfs2_inode_lock_for_extent_tree(struct inode *inode, out_unlock: brelse(*di_bh); + *di_bh = NULL; ocfs2_inode_unlock(inode, meta_level); out: return ret; @@ -2186,7 +2185,6 @@ static int ocfs2_prepare_inode_for_write(struct file *file, ret = ocfs2_inode_lock_for_extent_tree(inode, &di_bh, meta_level, - overwrite_io, write_sem, wait); if (ret < 0) { @@ -2244,13 +2242,13 @@ static int ocfs2_prepare_inode_for_write(struct file *file, &di_bh, meta_level, write_sem); + meta_level = 1; + write_sem = 1; ret = ocfs2_inode_lock_for_extent_tree(inode, &di_bh, meta_level, - overwrite_io, - 1, + write_sem, wait); - write_sem = 1; if (ret < 0) { if (ret != -EAGAIN) mlog_errno(ret); diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c index fc1f209e5db06fd16590ea9eef828f226332275c..c27d8ef47392b6de5b658cfca8e7098485229009 100644 --- a/fs/ocfs2/journal.c +++ b/fs/ocfs2/journal.c @@ -1080,6 +1080,14 @@ int ocfs2_journal_load(struct ocfs2_journal *journal, int local, int replayed) ocfs2_clear_journal_error(osb->sb, journal->j_journal, osb->slot_num); + if (replayed) { + jbd2_journal_lock_updates(journal->j_journal); + status = jbd2_journal_flush(journal->j_journal); + jbd2_journal_unlock_updates(journal->j_journal); + if (status < 0) + mlog_errno(status); + } + status = ocfs2_journal_toggle_dirty(osb, 1, replayed); if (status < 0) { mlog_errno(status); diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h index 497a4171ef61f6209a32303530b79c72439962cf..bfb50fc51528ffbf9d7c20c7b8b9872475991029 100644 --- a/fs/ocfs2/journal.h +++ b/fs/ocfs2/journal.h @@ -637,9 +637,11 @@ static inline void ocfs2_update_inode_fsync_trans(handle_t *handle, { struct ocfs2_inode_info *oi = OCFS2_I(inode); - oi->i_sync_tid = handle->h_transaction->t_tid; - if (datasync) - oi->i_datasync_tid = handle->h_transaction->t_tid; + if (!is_handle_aborted(handle)) { + oi->i_sync_tid = handle->h_transaction->t_tid; + if (datasync) + oi->i_datasync_tid = handle->h_transaction->t_tid; + } } #endif /* OCFS2_JOURNAL_H */ diff --git a/fs/orangefs/orangefs-debugfs.c b/fs/orangefs/orangefs-debugfs.c index 0732cb08173e9ba73f744f646b58f48dbc9634e1..e24738c691f66c38df5341b3931e715d94b72689 100644 --- a/fs/orangefs/orangefs-debugfs.c +++ b/fs/orangefs/orangefs-debugfs.c @@ -305,6 +305,7 @@ static void *help_start(struct seq_file *m, loff_t *pos) static void *help_next(struct seq_file *m, void *v, loff_t *pos) { + (*pos)++; gossip_debug(GOSSIP_DEBUGFS_DEBUG, "help_next: start\n"); return NULL; diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c index 8cc24138b85a15d835ea8f746d2976b159a2938a..1b5ad5843ac662da64fadba7ef7e97564413e00a 100644 --- a/fs/overlayfs/file.c +++ b/fs/overlayfs/file.c @@ -149,7 +149,7 @@ static loff_t ovl_llseek(struct file *file, loff_t offset, int whence) struct inode *inode = file_inode(file); struct fd real; const struct cred *old_cred; - ssize_t ret; + loff_t ret; /* * The two special cases below do not need to involve real fs, diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index ec591b49e9024fcc692bb97f0f8c7dbfed4d6e2f..47710c1a8d4f9fc1d553d73e145bb6beed4c0e64 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c @@ -507,7 +507,13 @@ static int ovl_cache_update_ino(struct path *path, struct ovl_cache_entry *p) if (err) goto fail; - WARN_ON_ONCE(dir->d_sb->s_dev != stat.dev); + /* + * Directory inode is always on overlay st_dev. + * Non-dir with ovl_same_dev() could be on pseudo st_dev in case + * of xino bits overflow. + */ + WARN_ON_ONCE(S_ISDIR(stat.mode) && + dir->d_sb->s_dev != stat.dev); ino = stat.ino; } else if (xinobits && !OVL_TYPE_UPPER(type)) { ino = ovl_remap_lower_ino(ino, xinobits, diff --git a/fs/reiserfs/stree.c b/fs/reiserfs/stree.c index 0037aea97d39a6c73eff9755d805bc2312b552a3..2946713cb00d6cfe327dd6e86ac3b452d6cc34fb 100644 --- a/fs/reiserfs/stree.c +++ b/fs/reiserfs/stree.c @@ -2250,7 +2250,8 @@ int reiserfs_insert_item(struct reiserfs_transaction_handle *th, /* also releases the path */ unfix_nodes(&s_ins_balance); #ifdef REISERQUOTA_DEBUG - reiserfs_debug(th->t_super, REISERFS_DEBUG_CODE, + if (inode) + reiserfs_debug(th->t_super, REISERFS_DEBUG_CODE, "reiserquota insert_item(): freeing %u id=%u type=%c", quota_bytes, inode->i_uid, head2type(ih)); #endif diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c index a3507490be6e8d913c5eebaba4caad6df6fb9779..de5eda33c92a0fd520f4d64265f61e22d51c695c 100644 --- a/fs/reiserfs/super.c +++ b/fs/reiserfs/super.c @@ -629,6 +629,7 @@ static void reiserfs_put_super(struct super_block *s) reiserfs_write_unlock(s); mutex_destroy(&REISERFS_SB(s)->lock); destroy_workqueue(REISERFS_SB(s)->commit_wq); + kfree(REISERFS_SB(s)->s_jdev); kfree(s->s_fs_info); s->s_fs_info = NULL; } @@ -1953,7 +1954,7 @@ static int reiserfs_fill_super(struct super_block *s, void *data, int silent) if (!sbi->s_jdev) { SWARN(silent, s, "", "Cannot allocate memory for " "journal device name"); - goto error; + goto error_unlocked; } } #ifdef CONFIG_QUOTA @@ -2243,6 +2244,7 @@ static int reiserfs_fill_super(struct super_block *s, void *data, int silent) kfree(qf_names[j]); } #endif + kfree(sbi->s_jdev); kfree(sbi); s->s_fs_info = NULL; diff --git a/fs/reiserfs/xattr.c b/fs/reiserfs/xattr.c index 2c5c45927b2f79735438deed1b7d901d2ad0f56b..ee216925a7090903f77282add2e42e504a40061a 100644 --- a/fs/reiserfs/xattr.c +++ b/fs/reiserfs/xattr.c @@ -319,8 +319,12 @@ static int reiserfs_for_each_xattr(struct inode *inode, out_dir: dput(dir); out: - /* -ENODATA isn't an error */ - if (err == -ENODATA) + /* + * -ENODATA: this object doesn't have any xattrs + * -EOPNOTSUPP: this file system doesn't have xattrs enabled on disk. + * Neither are errors + */ + if (err == -ENODATA || err == -EOPNOTSUPP) err = 0; return err; } diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 4dd681e0d59d948374883a7e34ed8318d4537e23..edeca118cce5008ccec9d35ccae19513c62bcc36 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -87,6 +87,9 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, lower_dentry_mnt = lower_path.mnt; lower_parent_dentry = lock_parent(lower_dentry); + if (d_is_positive(lower_dentry)) + return -EEXIST; + /* set last 16bytes of mode field to 0664 */ mode = (mode & S_IFMT) | 00664; diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index a5c9686090e0ace071eff737d86a485da9a7e197..d2dfdf1028c6c05e3dc34b8d60e8cd145f953221 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -257,7 +257,6 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, struct dentry *lower_dentry; const struct qstr *name; struct path lower_path; - struct qstr dname; struct dentry *ret_dentry = NULL; struct sdcardfs_sb_info *sbi; @@ -316,6 +315,7 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, /* no error: handle positive dentries */ if (!err) { +found: /* check if the dentry is an obb dentry * if true, the lower_inode must be replaced with * the inode of the graft path @@ -362,28 +362,26 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, if (err && err != -ENOENT) goto out; - /* instatiate a new negative dentry */ - dname.name = name->name; - dname.len = name->len; - - /* See if the low-level filesystem might want - * to use its own hash - */ - lower_dentry = d_hash_and_lookup(lower_dir_dentry, &dname); - if (IS_ERR(lower_dentry)) - return lower_dentry; - - if (!lower_dentry) { - /* We called vfs_path_lookup earlier, and did not get a negative - * dentry then. Don't confuse the lower filesystem by forcing - * one on it now... - */ - err = -ENOENT; + /* get a (very likely) new negative dentry */ + lower_dentry = lookup_one_len_unlocked(name->name, + lower_dir_dentry, name->len); + if (IS_ERR(lower_dentry)) { + err = PTR_ERR(lower_dentry); goto out; } lower_path.dentry = lower_dentry; lower_path.mnt = mntget(lower_dir_mnt); + + /* + * Check if someone sneakily filled in the dentry when + * we weren't looking. We'll check again in create. + */ + if (unlikely(d_inode_rcu(lower_dentry))) { + err = 0; + goto found; + } + sdcardfs_set_lower_path(dentry, &lower_path); /* diff --git a/fs/ubifs/Kconfig b/fs/ubifs/Kconfig index b9bc6e451f2c7828d9eeaeec773edc466a00a6ea..1b68e40a69f941f4f5379469de4fd7bd6e6256fb 100644 --- a/fs/ubifs/Kconfig +++ b/fs/ubifs/Kconfig @@ -8,6 +8,7 @@ config UBIFS_FS select CRYPTO_LZO if UBIFS_FS_LZO select CRYPTO_DEFLATE if UBIFS_FS_ZLIB select UBIFS_FS_XATTR if FS_ENCRYPTION + select FS_ENCRYPTION_ALGS if FS_ENCRYPTION depends on MTD_UBI help UBIFS is a file system for flash devices which works on top of UBI. diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c index b73de6d04fa3e29adc96aba300fa56eb301e0a3d..1a7c5c0754d46bb7f4f9c3c3172e56fd61db8494 100644 --- a/fs/ubifs/dir.c +++ b/fs/ubifs/dir.c @@ -208,6 +208,7 @@ static int dbg_check_name(const struct ubifs_info *c, return 0; } +static void ubifs_set_d_ops(struct inode *dir, struct dentry *dentry); static struct dentry *ubifs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { @@ -221,6 +222,7 @@ static struct dentry *ubifs_lookup(struct inode *dir, struct dentry *dentry, dbg_gen("'%pd' in dir ino %lu", dentry, dir->i_ino); err = fscrypt_prepare_lookup(dir, dentry, &nm); + ubifs_set_d_ops(dir, dentry); if (err == -ENOENT) return d_splice_alias(NULL, dentry); if (err) @@ -237,9 +239,9 @@ static struct dentry *ubifs_lookup(struct inode *dir, struct dentry *dentry, goto done; } - if (nm.hash) { - ubifs_assert(c, fname_len(&nm) == 0); - ubifs_assert(c, fname_name(&nm) == NULL); + if (fname_name(&nm) == NULL) { + if (nm.hash & ~UBIFS_S_KEY_HASH_MASK) + goto done; /* ENOENT */ dent_key_init_hash(c, &key, dir->i_ino, nm.hash); err = ubifs_tnc_lookup_dh(c, &key, dent, nm.minor_hash); } else { @@ -524,7 +526,7 @@ static int ubifs_readdir(struct file *file, struct dir_context *ctx) if (encrypted) { err = fscrypt_get_encryption_info(dir); - if (err && err != -ENOKEY) + if (err) return err; err = fscrypt_fname_alloc_buffer(dir, UBIFS_MAX_NLEN, &fstr); @@ -1668,3 +1670,19 @@ const struct file_operations ubifs_dir_operations = { .compat_ioctl = ubifs_compat_ioctl, #endif }; + +#ifdef CONFIG_FS_ENCRYPTION +static const struct dentry_operations ubifs_encrypted_dentry_ops = { + .d_revalidate = fscrypt_d_revalidate, +}; +#endif + +static void ubifs_set_d_ops(struct inode *dir, struct dentry *dentry) +{ +#ifdef CONFIG_FS_ENCRYPTION + if (dentry->d_flags & DCACHE_ENCRYPTED_NAME) { + d_set_d_op(dentry, &ubifs_encrypted_dentry_ops); + return; + } +#endif +} diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c index 1b78f2e09218bd3fcf44f2772c47af54347a1bc1..65b4f63349c767743723f21b8210af0aea3c757a 100644 --- a/fs/ubifs/file.c +++ b/fs/ubifs/file.c @@ -798,7 +798,9 @@ static int ubifs_do_bulk_read(struct ubifs_info *c, struct bu_info *bu, if (page_offset > end_index) break; - page = find_or_create_page(mapping, page_offset, ra_gfp_mask); + page = pagecache_get_page(mapping, page_offset, + FGP_LOCK|FGP_ACCESSED|FGP_CREAT|FGP_NOWAIT, + ra_gfp_mask); if (!page) break; if (!PageUptodate(page)) diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c index 71c34400157492f64405464ba083165a095759a0..203067d3985518c1d785b42d67c14087c621d9b3 100644 --- a/fs/ubifs/ioctl.c +++ b/fs/ubifs/ioctl.c @@ -28,6 +28,11 @@ #include #include "ubifs.h" +/* Need to be kept consistent with checked flags in ioctl2ubifs() */ +#define UBIFS_SUPPORTED_IOCTL_FLAGS \ + (FS_COMPR_FL | FS_SYNC_FL | FS_APPEND_FL | \ + FS_IMMUTABLE_FL | FS_DIRSYNC_FL) + /** * ubifs_set_inode_flags - set VFS inode flags. * @inode: VFS inode to set flags for @@ -127,7 +132,8 @@ static int setflags(struct inode *inode, int flags) } } - ui->flags = ioctl2ubifs(flags); + ui->flags &= ~ioctl2ubifs(UBIFS_SUPPORTED_IOCTL_FLAGS); + ui->flags |= ioctl2ubifs(flags); ubifs_set_inode_flags(inode); inode->i_ctime = current_time(inode); release = ui->dirty; @@ -169,6 +175,9 @@ long ubifs_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (get_user(flags, (int __user *) arg)) return -EFAULT; + if (flags & ~UBIFS_SUPPORTED_IOCTL_FLAGS) + return -EOPNOTSUPP; + if (!S_ISDIR(inode->i_mode)) flags &= ~FS_DIRSYNC_FL; diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c index 802565a17733ce4b0e304df7daffda9517252bca..81e539780e7796c86bc6a1b5973eb42cf3970fce 100644 --- a/fs/ubifs/journal.c +++ b/fs/ubifs/journal.c @@ -583,7 +583,7 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir, if (!xent) { dent->ch.node_type = UBIFS_DENT_NODE; - if (nm->hash) + if (fname_name(nm) == NULL) dent_key_init_hash(c, &dent_key, dir->i_ino, nm->hash); else dent_key_init(c, &dent_key, dir->i_ino, nm); @@ -630,7 +630,7 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir, kfree(dent); if (deletion) { - if (nm->hash) + if (fname_name(nm) == NULL) err = ubifs_tnc_remove_dh(c, &dent_key, nm->minor_hash); else err = ubifs_tnc_remove_nm(c, &dent_key, nm); diff --git a/fs/ubifs/key.h b/fs/ubifs/key.h index 2feff6cbbb773a4581a1994be725da15485cc6d0..fa6f2900bc1b6889b224c14bb7a03b846d0979f8 100644 --- a/fs/ubifs/key.h +++ b/fs/ubifs/key.h @@ -162,7 +162,6 @@ static inline void dent_key_init(const struct ubifs_info *c, uint32_t hash = c->key_hash(fname_name(nm), fname_len(nm)); ubifs_assert(c, !(hash & ~UBIFS_S_KEY_HASH_MASK)); - ubifs_assert(c, !nm->hash && !nm->minor_hash); key->u32[0] = inum; key->u32[1] = hash | (UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS); } diff --git a/fs/udf/super.c b/fs/udf/super.c index 7af011dc9ae8a70ad6f1dd63c39d8b47a7d5dba4..1676a175cd7a81c1af4dbfbddaf371d4556887e6 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -999,7 +999,6 @@ static int check_partition_desc(struct super_block *sb, switch (le32_to_cpu(p->accessType)) { case PD_ACCESS_TYPE_READ_ONLY: case PD_ACCESS_TYPE_WRITE_ONCE: - case PD_ACCESS_TYPE_REWRITABLE: case PD_ACCESS_TYPE_NONE: goto force_ro; } @@ -2470,17 +2469,29 @@ static unsigned int udf_count_free_table(struct super_block *sb, static unsigned int udf_count_free(struct super_block *sb) { unsigned int accum = 0; - struct udf_sb_info *sbi; + struct udf_sb_info *sbi = UDF_SB(sb); struct udf_part_map *map; + unsigned int part = sbi->s_partition; + int ptype = sbi->s_partmaps[part].s_partition_type; + + if (ptype == UDF_METADATA_MAP25) { + part = sbi->s_partmaps[part].s_type_specific.s_metadata. + s_phys_partition_ref; + } else if (ptype == UDF_VIRTUAL_MAP15 || ptype == UDF_VIRTUAL_MAP20) { + /* + * Filesystems with VAT are append-only and we cannot write to + * them. Let's just report 0 here. + */ + return 0; + } - sbi = UDF_SB(sb); if (sbi->s_lvid_bh) { struct logicalVolIntegrityDesc *lvid = (struct logicalVolIntegrityDesc *) sbi->s_lvid_bh->b_data; - if (le32_to_cpu(lvid->numOfPartitions) > sbi->s_partition) { + if (le32_to_cpu(lvid->numOfPartitions) > part) { accum = le32_to_cpu( - lvid->freeSpaceTable[sbi->s_partition]); + lvid->freeSpaceTable[part]); if (accum == 0xFFFFFFFF) accum = 0; } @@ -2489,7 +2500,7 @@ static unsigned int udf_count_free(struct super_block *sb) if (accum) return accum; - map = &sbi->s_partmaps[sbi->s_partition]; + map = &sbi->s_partmaps[part]; if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_BITMAP) { accum += udf_count_free_bitmap(sb, map->s_uspace.s_bitmap); diff --git a/fs/unicode/utf8-core.c b/fs/unicode/utf8-core.c index 71ca4d047d6543701dbbdcc2056348534a25e96f..d18789f276503136ea33ba25fedceeea796e9463 100644 --- a/fs/unicode/utf8-core.c +++ b/fs/unicode/utf8-core.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "utf8n.h" @@ -122,9 +123,29 @@ int utf8_casefold(const struct unicode_map *um, const struct qstr *str, } return -EINVAL; } - EXPORT_SYMBOL(utf8_casefold); +int utf8_casefold_hash(const struct unicode_map *um, const void *salt, + struct qstr *str) +{ + const struct utf8data *data = utf8nfdicf(um->version); + struct utf8cursor cur; + int c; + unsigned long hash = init_name_hash(salt); + + if (utf8ncursor(&cur, data, str->name, str->len) < 0) + return -EINVAL; + + while ((c = utf8byte(&cur))) { + if (c < 0) + return c; + hash = partial_name_hash((unsigned char)c, hash); + } + str->hash = end_name_hash(hash); + return 0; +} +EXPORT_SYMBOL(utf8_casefold_hash); + int utf8_normalize(const struct unicode_map *um, const struct qstr *str, unsigned char *dest, size_t dlen) { diff --git a/fs/verity/enable.c b/fs/verity/enable.c index eabc6ac1990641fc6b4c54108d32328a9bee95c6..15e7d14ec2ffc7e09b77dff8b235b351baa2f3f5 100644 --- a/fs/verity/enable.c +++ b/fs/verity/enable.c @@ -8,18 +8,48 @@ #include "fsverity_private.h" #include +#include #include #include #include #include -static int build_merkle_tree_level(struct inode *inode, unsigned int level, +/* + * Read a file data page for Merkle tree construction. Do aggressive readahead, + * since we're sequentially reading the entire file. + */ +static struct page *read_file_data_page(struct file *filp, pgoff_t index, + struct file_ra_state *ra, + unsigned long remaining_pages) +{ + struct page *page; + + page = find_get_page_flags(filp->f_mapping, index, FGP_ACCESSED); + if (!page || !PageUptodate(page)) { + if (page) + put_page(page); + else + page_cache_sync_readahead(filp->f_mapping, ra, filp, + index, remaining_pages); + page = read_mapping_page(filp->f_mapping, index, NULL); + if (IS_ERR(page)) + return page; + } + if (PageReadahead(page)) + page_cache_async_readahead(filp->f_mapping, ra, filp, page, + index, remaining_pages); + return page; +} + +static int build_merkle_tree_level(struct file *filp, unsigned int level, u64 num_blocks_to_hash, const struct merkle_tree_params *params, u8 *pending_hashes, struct ahash_request *req) { + struct inode *inode = file_inode(filp); const struct fsverity_operations *vops = inode->i_sb->s_vop; + struct file_ra_state ra = { 0 }; unsigned int pending_size = 0; u64 dst_block_num; u64 i; @@ -36,6 +66,8 @@ static int build_merkle_tree_level(struct inode *inode, unsigned int level, dst_block_num = 0; /* unused */ } + file_ra_state_init(&ra, filp->f_mapping); + for (i = 0; i < num_blocks_to_hash; i++) { struct page *src_page; @@ -45,7 +77,8 @@ static int build_merkle_tree_level(struct inode *inode, unsigned int level, if (level == 0) { /* Leaf: hashing a data block */ - src_page = read_mapping_page(inode->i_mapping, i, NULL); + src_page = read_file_data_page(filp, i, &ra, + num_blocks_to_hash - i); if (IS_ERR(src_page)) { err = PTR_ERR(src_page); fsverity_err(inode, @@ -54,9 +87,14 @@ static int build_merkle_tree_level(struct inode *inode, unsigned int level, return err; } } else { + unsigned long num_ra_pages = + min_t(unsigned long, num_blocks_to_hash - i, + inode->i_sb->s_bdi->io_pages); + /* Non-leaf: hashing hash block from level below */ src_page = vops->read_merkle_tree_page(inode, - params->level_start[level - 1] + i); + params->level_start[level - 1] + i, + num_ra_pages); if (IS_ERR(src_page)) { err = PTR_ERR(src_page); fsverity_err(inode, @@ -103,17 +141,18 @@ static int build_merkle_tree_level(struct inode *inode, unsigned int level, } /* - * Build the Merkle tree for the given inode using the given parameters, and + * Build the Merkle tree for the given file using the given parameters, and * return the root hash in @root_hash. * * The tree is written to a filesystem-specific location as determined by the * ->write_merkle_tree_block() method. However, the blocks that comprise the * tree are the same for all filesystems. */ -static int build_merkle_tree(struct inode *inode, +static int build_merkle_tree(struct file *filp, const struct merkle_tree_params *params, u8 *root_hash) { + struct inode *inode = file_inode(filp); u8 *pending_hashes; struct ahash_request *req; u64 blocks; @@ -126,9 +165,11 @@ static int build_merkle_tree(struct inode *inode, return 0; } + /* This allocation never fails, since it's mempool-backed. */ + req = fsverity_alloc_hash_request(params->hash_alg, GFP_KERNEL); + pending_hashes = kmalloc(params->block_size, GFP_KERNEL); - req = ahash_request_alloc(params->hash_alg->tfm, GFP_KERNEL); - if (!pending_hashes || !req) + if (!pending_hashes) goto out; /* @@ -139,7 +180,7 @@ static int build_merkle_tree(struct inode *inode, blocks = (inode->i_size + params->block_size - 1) >> params->log_blocksize; for (level = 0; level <= params->num_levels; level++) { - err = build_merkle_tree_level(inode, level, blocks, params, + err = build_merkle_tree_level(filp, level, blocks, params, pending_hashes, req); if (err) goto out; @@ -150,7 +191,7 @@ static int build_merkle_tree(struct inode *inode, err = 0; out: kfree(pending_hashes); - ahash_request_free(req); + fsverity_free_hash_request(params->hash_alg, req); return err; } @@ -175,8 +216,7 @@ static int enable_verity(struct file *filp, /* Get the salt if the user provided one */ if (arg->salt_size && - copy_from_user(desc->salt, - (const u8 __user *)(uintptr_t)arg->salt_ptr, + copy_from_user(desc->salt, u64_to_user_ptr(arg->salt_ptr), arg->salt_size)) { err = -EFAULT; goto out; @@ -185,8 +225,7 @@ static int enable_verity(struct file *filp, /* Get the signature if the user provided one */ if (arg->sig_size && - copy_from_user(desc->signature, - (const u8 __user *)(uintptr_t)arg->sig_ptr, + copy_from_user(desc->signature, u64_to_user_ptr(arg->sig_ptr), arg->sig_size)) { err = -EFAULT; goto out; @@ -227,7 +266,7 @@ static int enable_verity(struct file *filp, */ pr_debug("Building Merkle tree...\n"); BUILD_BUG_ON(sizeof(desc->root_hash) < FS_VERITY_MAX_DIGEST_SIZE); - err = build_merkle_tree(inode, ¶ms, desc->root_hash); + err = build_merkle_tree(filp, ¶ms, desc->root_hash); if (err) { fsverity_err(inode, "Error %d building Merkle tree", err); goto rollback; diff --git a/fs/verity/fsverity_private.h b/fs/verity/fsverity_private.h index e74c79b64d8898e4778cfe704ae536c77e5daa6b..74768cf539daf5f1d2c4ccf9157fa931cfea4b22 100644 --- a/fs/verity/fsverity_private.h +++ b/fs/verity/fsverity_private.h @@ -16,6 +16,7 @@ #include #include +#include struct ahash_request; @@ -37,11 +38,12 @@ struct fsverity_hash_alg { const char *name; /* crypto API name, e.g. sha256 */ unsigned int digest_size; /* digest size in bytes, e.g. 32 for SHA-256 */ unsigned int block_size; /* block size in bytes, e.g. 64 for SHA-256 */ + mempool_t req_pool; /* mempool with a preallocated hash request */ }; /* Merkle tree parameters: hash algorithm, initial hash state, and topology */ struct merkle_tree_params { - const struct fsverity_hash_alg *hash_alg; /* the hash algorithm */ + struct fsverity_hash_alg *hash_alg; /* the hash algorithm */ const u8 *hashstate; /* initial hash state or NULL */ unsigned int digest_size; /* same as hash_alg->digest_size */ unsigned int block_size; /* size of data and tree blocks */ @@ -50,6 +52,7 @@ struct merkle_tree_params { unsigned int log_arity; /* log2(hashes_per_block) */ unsigned int num_levels; /* number of levels in Merkle tree */ u64 tree_size; /* Merkle tree size in bytes */ + unsigned long level0_blocks; /* number of blocks in tree level 0 */ /* * Starting block index for each tree level, ordered from leaf level (0) @@ -114,14 +117,18 @@ struct fsverity_signed_digest { extern struct fsverity_hash_alg fsverity_hash_algs[]; -const struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode, - unsigned int num); -const u8 *fsverity_prepare_hash_state(const struct fsverity_hash_alg *alg, +struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode, + unsigned int num); +struct ahash_request *fsverity_alloc_hash_request(struct fsverity_hash_alg *alg, + gfp_t gfp_flags); +void fsverity_free_hash_request(struct fsverity_hash_alg *alg, + struct ahash_request *req); +const u8 *fsverity_prepare_hash_state(struct fsverity_hash_alg *alg, const u8 *salt, size_t salt_size); int fsverity_hash_page(const struct merkle_tree_params *params, const struct inode *inode, struct ahash_request *req, struct page *page, u8 *out); -int fsverity_hash_buffer(const struct fsverity_hash_alg *alg, +int fsverity_hash_buffer(struct fsverity_hash_alg *alg, const void *data, size_t size, u8 *out); void __init fsverity_check_hash_algs(void); diff --git a/fs/verity/hash_algs.c b/fs/verity/hash_algs.c index 31e6d7d2389ab00a1a7d8a4b6bc4c3af03e21a85..c37e186ebeb6c455db68c6580a889d4378161fd7 100644 --- a/fs/verity/hash_algs.c +++ b/fs/verity/hash_algs.c @@ -24,6 +24,8 @@ struct fsverity_hash_alg fsverity_hash_algs[] = { }, }; +static DEFINE_MUTEX(fsverity_hash_alg_init_mutex); + /** * fsverity_get_hash_alg() - validate and prepare a hash algorithm * @inode: optional inode for logging purposes @@ -36,8 +38,8 @@ struct fsverity_hash_alg fsverity_hash_algs[] = { * * Return: pointer to the hash alg on success, else an ERR_PTR() */ -const struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode, - unsigned int num) +struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode, + unsigned int num) { struct fsverity_hash_alg *alg; struct crypto_ahash *tfm; @@ -50,10 +52,15 @@ const struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode, } alg = &fsverity_hash_algs[num]; - /* pairs with cmpxchg() below */ - tfm = READ_ONCE(alg->tfm); - if (likely(tfm != NULL)) + /* pairs with smp_store_release() below */ + if (likely(smp_load_acquire(&alg->tfm) != NULL)) return alg; + + mutex_lock(&fsverity_hash_alg_init_mutex); + + if (alg->tfm != NULL) + goto out_unlock; + /* * Using the shash API would make things a bit simpler, but the ahash * API is preferable as it allows the use of crypto accelerators. @@ -64,12 +71,14 @@ const struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode, fsverity_warn(inode, "Missing crypto API support for hash algorithm \"%s\"", alg->name); - return ERR_PTR(-ENOPKG); + alg = ERR_PTR(-ENOPKG); + goto out_unlock; } fsverity_err(inode, "Error allocating hash algorithm \"%s\": %ld", alg->name, PTR_ERR(tfm)); - return ERR_CAST(tfm); + alg = ERR_CAST(tfm); + goto out_unlock; } err = -EINVAL; @@ -78,18 +87,61 @@ const struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode, if (WARN_ON(alg->block_size != crypto_ahash_blocksize(tfm))) goto err_free_tfm; + err = mempool_init_kmalloc_pool(&alg->req_pool, 1, + sizeof(struct ahash_request) + + crypto_ahash_reqsize(tfm)); + if (err) + goto err_free_tfm; + pr_info("%s using implementation \"%s\"\n", alg->name, crypto_ahash_driver_name(tfm)); - /* pairs with READ_ONCE() above */ - if (cmpxchg(&alg->tfm, NULL, tfm) != NULL) - crypto_free_ahash(tfm); - - return alg; + /* pairs with smp_load_acquire() above */ + smp_store_release(&alg->tfm, tfm); + goto out_unlock; err_free_tfm: crypto_free_ahash(tfm); - return ERR_PTR(err); + alg = ERR_PTR(err); +out_unlock: + mutex_unlock(&fsverity_hash_alg_init_mutex); + return alg; +} + +/** + * fsverity_alloc_hash_request() - allocate a hash request object + * @alg: the hash algorithm for which to allocate the request + * @gfp_flags: memory allocation flags + * + * This is mempool-backed, so this never fails if __GFP_DIRECT_RECLAIM is set in + * @gfp_flags. However, in that case this might need to wait for all + * previously-allocated requests to be freed. So to avoid deadlocks, callers + * must never need multiple requests at a time to make forward progress. + * + * Return: the request object on success; NULL on failure (but see above) + */ +struct ahash_request *fsverity_alloc_hash_request(struct fsverity_hash_alg *alg, + gfp_t gfp_flags) +{ + struct ahash_request *req = mempool_alloc(&alg->req_pool, gfp_flags); + + if (req) + ahash_request_set_tfm(req, alg->tfm); + return req; +} + +/** + * fsverity_free_hash_request() - free a hash request object + * @alg: the hash algorithm + * @req: the hash request object to free + */ +void fsverity_free_hash_request(struct fsverity_hash_alg *alg, + struct ahash_request *req) +{ + if (req) { + ahash_request_zero(req); + mempool_free(req, &alg->req_pool); + } } /** @@ -101,7 +153,7 @@ const struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode, * Return: NULL if the salt is empty, otherwise the kmalloc()'ed precomputed * initial hash state on success or an ERR_PTR() on failure. */ -const u8 *fsverity_prepare_hash_state(const struct fsverity_hash_alg *alg, +const u8 *fsverity_prepare_hash_state(struct fsverity_hash_alg *alg, const u8 *salt, size_t salt_size) { u8 *hashstate = NULL; @@ -119,11 +171,8 @@ const u8 *fsverity_prepare_hash_state(const struct fsverity_hash_alg *alg, if (!hashstate) return ERR_PTR(-ENOMEM); - req = ahash_request_alloc(alg->tfm, GFP_KERNEL); - if (!req) { - err = -ENOMEM; - goto err_free; - } + /* This allocation never fails, since it's mempool-backed. */ + req = fsverity_alloc_hash_request(alg, GFP_KERNEL); /* * Zero-pad the salt to the next multiple of the input size of the hash @@ -158,7 +207,7 @@ const u8 *fsverity_prepare_hash_state(const struct fsverity_hash_alg *alg, if (err) goto err_free; out: - ahash_request_free(req); + fsverity_free_hash_request(alg, req); kfree(padded_salt); return hashstate; @@ -229,7 +278,7 @@ int fsverity_hash_page(const struct merkle_tree_params *params, * * Return: 0 on success, -errno on failure */ -int fsverity_hash_buffer(const struct fsverity_hash_alg *alg, +int fsverity_hash_buffer(struct fsverity_hash_alg *alg, const void *data, size_t size, u8 *out) { struct ahash_request *req; @@ -237,9 +286,8 @@ int fsverity_hash_buffer(const struct fsverity_hash_alg *alg, DECLARE_CRYPTO_WAIT(wait); int err; - req = ahash_request_alloc(alg->tfm, GFP_KERNEL); - if (!req) - return -ENOMEM; + /* This allocation never fails, since it's mempool-backed. */ + req = fsverity_alloc_hash_request(alg, GFP_KERNEL); sg_init_one(&sg, data, size); ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP | @@ -249,7 +297,7 @@ int fsverity_hash_buffer(const struct fsverity_hash_alg *alg, err = crypto_wait_req(crypto_ahash_digest(req), &wait); - ahash_request_free(req); + fsverity_free_hash_request(alg, req); return err; } diff --git a/fs/verity/open.c b/fs/verity/open.c index 63d1004b688cb905dc35b3de5288b7ef96c261be..c5fe6948e26290befc4932dd98d97cb01f61a760 100644 --- a/fs/verity/open.c +++ b/fs/verity/open.c @@ -31,7 +31,7 @@ int fsverity_init_merkle_tree_params(struct merkle_tree_params *params, unsigned int log_blocksize, const u8 *salt, size_t salt_size) { - const struct fsverity_hash_alg *hash_alg; + struct fsverity_hash_alg *hash_alg; int err; u64 blocks; u64 offset; @@ -102,6 +102,7 @@ int fsverity_init_merkle_tree_params(struct merkle_tree_params *params, /* temporarily using level_start[] to store blocks in level */ params->level_start[params->num_levels++] = blocks; } + params->level0_blocks = params->level_start[0]; /* Compute the starting block of each level */ offset = 0; @@ -126,7 +127,7 @@ int fsverity_init_merkle_tree_params(struct merkle_tree_params *params, * Compute the file measurement by hashing the fsverity_descriptor excluding the * signature and with the sig_size field set to 0. */ -static int compute_file_measurement(const struct fsverity_hash_alg *hash_alg, +static int compute_file_measurement(struct fsverity_hash_alg *hash_alg, struct fsverity_descriptor *desc, u8 *measurement) { diff --git a/fs/verity/verify.c b/fs/verity/verify.c index 18180501867d5f5f890f5690e33a62a256b86644..8a833316ce36cad3f05de503c358c962f92133b5 100644 --- a/fs/verity/verify.c +++ b/fs/verity/verify.c @@ -84,7 +84,8 @@ static inline int cmp_hashes(const struct fsverity_info *vi, * Return: true if the page is valid, else false. */ static bool verify_page(struct inode *inode, const struct fsverity_info *vi, - struct ahash_request *req, struct page *data_page) + struct ahash_request *req, struct page *data_page, + unsigned long level0_ra_pages) { const struct merkle_tree_params *params = &vi->tree_params; const unsigned int hsize = params->digest_size; @@ -117,8 +118,8 @@ static bool verify_page(struct inode *inode, const struct fsverity_info *vi, pr_debug_ratelimited("Level %d: hindex=%lu, hoffset=%u\n", level, hindex, hoffset); - hpage = inode->i_sb->s_vop->read_merkle_tree_page(inode, - hindex); + hpage = inode->i_sb->s_vop->read_merkle_tree_page(inode, hindex, + level == 0 ? level0_ra_pages : 0); if (IS_ERR(hpage)) { err = PTR_ERR(hpage); fsverity_err(inode, @@ -191,13 +192,12 @@ bool fsverity_verify_page(struct page *page) struct ahash_request *req; bool valid; - req = ahash_request_alloc(vi->tree_params.hash_alg->tfm, GFP_NOFS); - if (unlikely(!req)) - return false; + /* This allocation never fails, since it's mempool-backed. */ + req = fsverity_alloc_hash_request(vi->tree_params.hash_alg, GFP_NOFS); - valid = verify_page(inode, vi, req, page); + valid = verify_page(inode, vi, req, page, 0); - ahash_request_free(req); + fsverity_free_hash_request(vi->tree_params.hash_alg, req); return valid; } @@ -222,25 +222,42 @@ void fsverity_verify_bio(struct bio *bio) { struct inode *inode = bio_first_page_all(bio)->mapping->host; const struct fsverity_info *vi = inode->i_verity_info; + const struct merkle_tree_params *params = &vi->tree_params; struct ahash_request *req; struct bio_vec *bv; int i; - - req = ahash_request_alloc(vi->tree_params.hash_alg->tfm, GFP_NOFS); - if (unlikely(!req)) { + unsigned long max_ra_pages = 0; + + /* This allocation never fails, since it's mempool-backed. */ + req = fsverity_alloc_hash_request(params->hash_alg, GFP_NOFS); + + if (bio->bi_opf & REQ_RAHEAD) { + /* + * If this bio is for data readahead, then we also do readahead + * of the first (largest) level of the Merkle tree. Namely, + * when a Merkle tree page is read, we also try to piggy-back on + * some additional pages -- up to 1/4 the number of data pages. + * + * This improves sequential read performance, as it greatly + * reduces the number of I/O requests made to the Merkle tree. + */ bio_for_each_segment_all(bv, bio, i) - SetPageError(bv->bv_page); - return; + max_ra_pages++; + max_ra_pages /= 4; } bio_for_each_segment_all(bv, bio, i) { struct page *page = bv->bv_page; + unsigned long level0_index = page->index >> params->log_arity; + unsigned long level0_ra_pages = + min(max_ra_pages, params->level0_blocks - level0_index); - if (!PageError(page) && !verify_page(inode, vi, req, page)) + if (!PageError(page) && + !verify_page(inode, vi, req, page, level0_ra_pages)) SetPageError(page); } - ahash_request_free(req); + fsverity_free_hash_request(params->hash_alg, req); } EXPORT_SYMBOL_GPL(fsverity_verify_bio); #endif /* CONFIG_BLOCK */ diff --git a/fs/xfs/xfs_quotaops.c b/fs/xfs/xfs_quotaops.c index a7c0c657dfaf943037bcb6fa753996c32956413b..13ca7c16bfc74ed168cfc8ca26a64edfbd9b13a5 100644 --- a/fs/xfs/xfs_quotaops.c +++ b/fs/xfs/xfs_quotaops.c @@ -202,6 +202,9 @@ xfs_fs_rm_xquota( if (XFS_IS_QUOTA_ON(mp)) return -EINVAL; + if (uflags & ~(FS_USER_QUOTA | FS_GROUP_QUOTA | FS_PROJ_QUOTA)) + return -EINVAL; + if (uflags & FS_USER_QUOTA) flags |= XFS_DQ_USER; if (uflags & FS_GROUP_QUOTA) diff --git a/gen_headers_arm.bp b/gen_headers_arm.bp index 65319b0f62ca3ced47413a7f3a7a7ad86b44b4ed..31da7a32a01ef4a44d32d840a47a29785cc765f7 100644 --- a/gen_headers_arm.bp +++ b/gen_headers_arm.bp @@ -301,6 +301,7 @@ gen_headers_out_arm = [ "linux/in.h", "linux/in6.h", "linux/in_route.h", + "linux/incrementalfs.h", "linux/inet_diag.h", "linux/inotify.h", "linux/input-event-codes.h", @@ -614,6 +615,7 @@ gen_headers_out_arm = [ "linux/virtio_mmio.h", "linux/virtio_net.h", "linux/virtio_pci.h", + "linux/virtio_pmem.h", "linux/virtio_ring.h", "linux/virtio_rng.h", "linux/virtio_scsi.h", @@ -965,15 +967,15 @@ gen_headers_out_arm = [ "media/cam_icp.h", "media/cam_isp.h", "media/cam_isp_ife.h", + "media/cam_isp_tfe.h", "media/cam_isp_vfe.h", "media/cam_jpeg.h", "media/cam_lrme.h", + "media/cam_ope.h", "media/cam_req_mgr.h", "media/cam_sensor.h", "media/cam_sync.h", "media/cam_tfe.h", - "media/cam_ope.h", - "media/cam_isp_tfe.h", ] genrule { diff --git a/gen_headers_arm64.bp b/gen_headers_arm64.bp index 3e1627bfc563d96dce3c699b39900be260b5c9b2..b6bcb982845ff885b7c8ff2270745da2945dfafc 100644 --- a/gen_headers_arm64.bp +++ b/gen_headers_arm64.bp @@ -296,6 +296,7 @@ gen_headers_out_arm64 = [ "linux/in.h", "linux/in6.h", "linux/in_route.h", + "linux/incrementalfs.h", "linux/inet_diag.h", "linux/inotify.h", "linux/input-event-codes.h", @@ -608,6 +609,7 @@ gen_headers_out_arm64 = [ "linux/virtio_mmio.h", "linux/virtio_net.h", "linux/virtio_pci.h", + "linux/virtio_pmem.h", "linux/virtio_ring.h", "linux/virtio_rng.h", "linux/virtio_scsi.h", @@ -959,15 +961,15 @@ gen_headers_out_arm64 = [ "media/cam_icp.h", "media/cam_isp.h", "media/cam_isp_ife.h", + "media/cam_isp_tfe.h", "media/cam_isp_vfe.h", "media/cam_jpeg.h", "media/cam_lrme.h", + "media/cam_ope.h", "media/cam_req_mgr.h", "media/cam_sensor.h", "media/cam_sync.h", "media/cam_tfe.h", - "media/cam_ope.h", - "media/cam_isp_tfe.h", ] genrule { diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index 66ceb12ebc63032a9f4af8a9da7db9ac04c83178..2939a6cd7fecbfc9ba2db8ef314593e8535b870a 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -528,11 +528,12 @@ typedef u64 acpi_integer; #define ACPI_MAKE_RSDP_SIG(dest) (memcpy (ACPI_CAST_PTR (char, (dest)), ACPI_SIG_RSDP, 8)) /* - * Algorithm to obtain access bit width. + * Algorithm to obtain access bit or byte width. * Can be used with access_width of struct acpi_generic_address and access_size of * struct acpi_resource_generic_register. */ #define ACPI_ACCESS_BIT_WIDTH(size) (1 << ((size) + 2)) +#define ACPI_ACCESS_BYTE_WIDTH(size) (1 << ((size) - 1)) /******************************************************************************* * diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h index d980d32667b02a6b12abf63b7338eedf473b6e06..efe246ce52bf847916777f6f4c7030e4b93baf45 100644 --- a/include/drm/drm_panel.h +++ b/include/drm/drm_panel.h @@ -24,6 +24,7 @@ #ifndef __DRM_PANEL_H__ #define __DRM_PANEL_H__ +#include #include #include #include diff --git a/include/dt-bindings/clock/mdss-7nm-pll-clk.h b/include/dt-bindings/clock/mdss-7nm-pll-clk.h index bb146d7534ca4c8e2219f814271a0b8af6143968..d414a8a6a894021a124f63350e1d240518cc3c7a 100644 --- a/include/dt-bindings/clock/mdss-7nm-pll-clk.h +++ b/include/dt-bindings/clock/mdss-7nm-pll-clk.h @@ -30,30 +30,38 @@ #define POST_VCO_DIV3_5_0_CLK 19 #define CPHY_PCLK_SRC_MUX_0_CLK 20 #define CPHY_PCLK_SRC_0_CLK 21 +#define SHADOW_CPHY_BYTECLK_SRC_0_CLK 22 +#define SHADOW_POST_VCO_DIV3_5_0_CLK 23 +#define SHADOW_CPHY_PCLK_SRC_MUX_0_CLK 24 +#define SHADOW_CPHY_PCLK_SRC_0_CLK 25 -#define VCO_CLK_1 22 -#define PLL_OUT_DIV_1_CLK 23 -#define BITCLK_SRC_1_CLK 24 -#define BYTECLK_SRC_1_CLK 25 -#define POST_BIT_DIV_1_CLK 26 -#define POST_VCO_DIV_1_CLK 27 -#define BYTECLK_MUX_1_CLK 28 -#define PCLK_SRC_MUX_1_CLK 29 -#define PCLK_SRC_1_CLK 30 -#define PCLK_MUX_1_CLK 31 -#define SHADOW_VCO_CLK_1 32 -#define SHADOW_PLL_OUT_DIV_1_CLK 33 -#define SHADOW_BITCLK_SRC_1_CLK 34 -#define SHADOW_BYTECLK_SRC_1_CLK 35 -#define SHADOW_POST_BIT_DIV_1_CLK 36 -#define SHADOW_POST_VCO_DIV_1_CLK 37 -#define SHADOW_PCLK_SRC_MUX_1_CLK 38 -#define SHADOW_PCLK_SRC_1_CLK 39 +#define VCO_CLK_1 26 +#define PLL_OUT_DIV_1_CLK 27 +#define BITCLK_SRC_1_CLK 28 +#define BYTECLK_SRC_1_CLK 29 +#define POST_BIT_DIV_1_CLK 30 +#define POST_VCO_DIV_1_CLK 31 +#define BYTECLK_MUX_1_CLK 32 +#define PCLK_SRC_MUX_1_CLK 33 +#define PCLK_SRC_1_CLK 34 +#define PCLK_MUX_1_CLK 35 +#define SHADOW_VCO_CLK_1 36 +#define SHADOW_PLL_OUT_DIV_1_CLK 37 +#define SHADOW_BITCLK_SRC_1_CLK 38 +#define SHADOW_BYTECLK_SRC_1_CLK 39 +#define SHADOW_POST_BIT_DIV_1_CLK 40 +#define SHADOW_POST_VCO_DIV_1_CLK 41 +#define SHADOW_PCLK_SRC_MUX_1_CLK 42 +#define SHADOW_PCLK_SRC_1_CLK 43 /* CPHY clocks for DSI-1 PLL */ -#define CPHY_BYTECLK_SRC_1_CLK 40 -#define POST_VCO_DIV3_5_1_CLK 41 -#define CPHY_PCLK_SRC_MUX_1_CLK 42 -#define CPHY_PCLK_SRC_1_CLK 43 +#define CPHY_BYTECLK_SRC_1_CLK 44 +#define POST_VCO_DIV3_5_1_CLK 45 +#define CPHY_PCLK_SRC_MUX_1_CLK 46 +#define CPHY_PCLK_SRC_1_CLK 47 +#define SHADOW_CPHY_BYTECLK_SRC_1_CLK 48 +#define SHADOW_POST_VCO_DIV3_5_1_CLK 49 +#define SHADOW_CPHY_PCLK_SRC_MUX_1_CLK 50 +#define SHADOW_CPHY_PCLK_SRC_1_CLK 51 /* DP PLL clocks */ diff --git a/include/dt-bindings/iio/qcom,spmi-vadc.h b/include/dt-bindings/iio/qcom,spmi-vadc.h index 0bb1f1646f4391c8e3d93db9316df794f38444c1..53a0da286d821980d5061775bbd14d448e77dc91 100644 --- a/include/dt-bindings/iio/qcom,spmi-vadc.h +++ b/include/dt-bindings/iio/qcom,spmi-vadc.h @@ -139,6 +139,7 @@ #define ADC_GPIO7 0x18 #define ADC_SBUx 0x99 #define ADC_MID_CHG_DIV6 0x1e +#define ANA_IN 0x1d #define ADC_OFF 0xff /* 30k pull-up1 */ @@ -158,6 +159,7 @@ #define ADC_GPIO6_PU1 0x37 #define ADC_GPIO7_PU1 0x38 #define ADC_SBUx_PU1 0x39 +#define ANA_IN 0x1d /* 100k pull-up2 */ #define ADC_BAT_THERM_PU2 0x4a diff --git a/include/dt-bindings/msm/msm-bus-ids.h b/include/dt-bindings/msm/msm-bus-ids.h index 4aff0c9f782851d630e712150ef63455f33a6495..046cc112b6299cd935286b04539b884dc445b5a1 100644 --- a/include/dt-bindings/msm/msm-bus-ids.h +++ b/include/dt-bindings/msm/msm-bus-ids.h @@ -609,6 +609,14 @@ #define MSM_BUS_SLAVE_TLMM_NORTH 731 #define MSM_BUS_SLAVE_TLMM_WEST 732 #define MSM_BUS_SLAVE_SKL 733 +#define MSM_BUS_SLAVE_LPASS_TCM 734 +#define MSM_BUS_SLAVE_TLMM_CENTER 736 +#define MSM_BUS_MSS_NAV_CE_MPU_CFG 737 +#define MSM_BUS_SLAVE_A2NOC_THROTTLE_CFG 738 +#define MSM_BUS_SLAVE_CDSP 739 +#define MSM_BUS_SLAVE_CDSP_SMMU_CFG 740 +#define MSM_BUS_SLAVE_LPASS_MPU_CFG 741 +#define MSM_BUS_SLAVE_CSI_PHY_CFG 742 #define MSM_BUS_SLAVE_SERVICE_A1NOC 744 #define MSM_BUS_SLAVE_ANOC_PCIE_SNOC 745 #define MSM_BUS_SLAVE_SERVICE_A2NOC 746 @@ -701,6 +709,7 @@ #define MSM_BUS_SLAVE_AHB2PHY_2 836 #define MSM_BUS_SLAVE_HWKM 837 #define MSM_BUS_SLAVE_PKA_WRAPPER 838 +#define MSM_BUS_SLAVE_LAST 839 #define MSM_BUS_SLAVE_EBI_CH0_DISPLAY 20512 #define MSM_BUS_SLAVE_LLCC_DISPLAY 20513 diff --git a/include/dt-bindings/reset/amlogic,meson8b-reset.h b/include/dt-bindings/reset/amlogic,meson8b-reset.h index 614aff2c7affe914bf12cb841656f1eb1342aa6e..a03e86fe2c5709b1c1e70253b115a5ee2f2bfb26 100644 --- a/include/dt-bindings/reset/amlogic,meson8b-reset.h +++ b/include/dt-bindings/reset/amlogic,meson8b-reset.h @@ -95,9 +95,9 @@ #define RESET_VD_RMEM 64 #define RESET_AUDIN 65 #define RESET_DBLK 66 -#define RESET_PIC_DC 66 -#define RESET_PSC 66 -#define RESET_NAND 66 +#define RESET_PIC_DC 67 +#define RESET_PSC 68 +#define RESET_NAND 69 #define RESET_GE2D 70 #define RESET_PARSER_REG 71 #define RESET_PARSER_FETCH 72 diff --git a/include/linux/acpi.h b/include/linux/acpi.h index df1252e22dcfd8194290c3fa975998ceb001a366..cd412817654fa15080592f0d7334052717606ddf 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -917,26 +917,18 @@ int acpi_subsys_prepare(struct device *dev); void acpi_subsys_complete(struct device *dev); int acpi_subsys_suspend_late(struct device *dev); int acpi_subsys_suspend_noirq(struct device *dev); -int acpi_subsys_resume_noirq(struct device *dev); -int acpi_subsys_resume_early(struct device *dev); int acpi_subsys_suspend(struct device *dev); int acpi_subsys_freeze(struct device *dev); -int acpi_subsys_freeze_late(struct device *dev); -int acpi_subsys_freeze_noirq(struct device *dev); -int acpi_subsys_thaw_noirq(struct device *dev); +int acpi_subsys_poweroff(struct device *dev); #else static inline int acpi_dev_resume_early(struct device *dev) { return 0; } static inline int acpi_subsys_prepare(struct device *dev) { return 0; } static inline void acpi_subsys_complete(struct device *dev) {} static inline int acpi_subsys_suspend_late(struct device *dev) { return 0; } static inline int acpi_subsys_suspend_noirq(struct device *dev) { return 0; } -static inline int acpi_subsys_resume_noirq(struct device *dev) { return 0; } -static inline int acpi_subsys_resume_early(struct device *dev) { return 0; } static inline int acpi_subsys_suspend(struct device *dev) { return 0; } static inline int acpi_subsys_freeze(struct device *dev) { return 0; } -static inline int acpi_subsys_freeze_late(struct device *dev) { return 0; } -static inline int acpi_subsys_freeze_noirq(struct device *dev) { return 0; } -static inline int acpi_subsys_thaw_noirq(struct device *dev) { return 0; } +static inline int acpi_subsys_poweroff(struct device *dev) { return 0; } #endif #ifdef CONFIG_ACPI diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index d01246c81aab79ba8a0c27a21369a25b07f916e4..46e3503d63a60a232221186b0b830c49f06ebe47 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -373,6 +373,7 @@ struct queue_limits { unsigned int max_sectors; unsigned int max_segment_size; unsigned int physical_block_size; + unsigned int logical_block_size; unsigned int alignment_offset; unsigned int io_min; unsigned int io_opt; @@ -383,7 +384,6 @@ struct queue_limits { unsigned int discard_granularity; unsigned int discard_alignment; - unsigned short logical_block_size; unsigned short max_segments; unsigned short max_integrity_segments; unsigned short max_discard_segments; @@ -1217,7 +1217,7 @@ extern void blk_queue_max_write_same_sectors(struct request_queue *q, unsigned int max_write_same_sectors); extern void blk_queue_max_write_zeroes_sectors(struct request_queue *q, unsigned int max_write_same_sectors); -extern void blk_queue_logical_block_size(struct request_queue *, unsigned short); +extern void blk_queue_logical_block_size(struct request_queue *, unsigned int); extern void blk_queue_physical_block_size(struct request_queue *, unsigned int); extern void blk_queue_alignment_offset(struct request_queue *q, unsigned int alignment); @@ -1478,7 +1478,7 @@ static inline unsigned int queue_max_segment_size(struct request_queue *q) return q->limits.max_segment_size; } -static inline unsigned short queue_logical_block_size(struct request_queue *q) +static inline unsigned queue_logical_block_size(struct request_queue *q) { int retval = 512; @@ -1488,7 +1488,7 @@ static inline unsigned short queue_logical_block_size(struct request_queue *q) return retval; } -static inline unsigned short bdev_logical_block_size(struct block_device *bdev) +static inline unsigned int bdev_logical_block_size(struct block_device *bdev) { return queue_logical_block_size(bdev_get_queue(bdev)); } diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h index f01623aef2f77945514d4b2ef0045f0735e11851..73199facd5a4f6127ee3bf77e4c9ff0b880df96a 100644 --- a/include/linux/can/dev.h +++ b/include/linux/can/dev.h @@ -18,6 +18,7 @@ #include #include #include +#include #include /* @@ -91,6 +92,36 @@ struct can_priv { #define get_can_dlc(i) (min_t(__u8, (i), CAN_MAX_DLC)) #define get_canfd_dlc(i) (min_t(__u8, (i), CANFD_MAX_DLC)) +/* Check for outgoing skbs that have not been created by the CAN subsystem */ +static inline bool can_skb_headroom_valid(struct net_device *dev, + struct sk_buff *skb) +{ + /* af_packet creates a headroom of HH_DATA_MOD bytes which is fine */ + if (WARN_ON_ONCE(skb_headroom(skb) < sizeof(struct can_skb_priv))) + return false; + + /* af_packet does not apply CAN skb specific settings */ + if (skb->ip_summed == CHECKSUM_NONE) { + /* init headroom */ + can_skb_prv(skb)->ifindex = dev->ifindex; + can_skb_prv(skb)->skbcnt = 0; + + skb->ip_summed = CHECKSUM_UNNECESSARY; + + /* preform proper loopback on capable devices */ + if (dev->flags & IFF_ECHO) + skb->pkt_type = PACKET_LOOPBACK; + else + skb->pkt_type = PACKET_HOST; + + skb_reset_mac_header(skb); + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + } + + return true; +} + /* Drop a given socketbuffer if it does not contain a valid CAN frame. */ static inline bool can_dropped_invalid_skb(struct net_device *dev, struct sk_buff *skb) @@ -108,6 +139,9 @@ static inline bool can_dropped_invalid_skb(struct net_device *dev, } else goto inval_skb; + if (!can_skb_headroom_valid(dev, skb)) + goto inval_skb; + return false; inval_skb: diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 42f0b78f53184d792ebed16d6b8d7942061c025a..b76413263e44f86e0b03a989fd22121efab5dc1d 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -130,6 +130,8 @@ void cgroup_free(struct task_struct *p); int cgroup_init_early(void); int cgroup_init(void); +int cgroup_parse_float(const char *input, unsigned dec_shift, s64 *v); + /* * Iteration helpers and macros. */ diff --git a/include/linux/device.h b/include/linux/device.h index 2c64731232baeff87335279cbc92942216686f08..5a02401ddb2ce0b84c8cd4da5cc1063857e9479c 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -719,7 +719,8 @@ extern unsigned long devm_get_free_pages(struct device *dev, gfp_t gfp_mask, unsigned int order); extern void devm_free_pages(struct device *dev, unsigned long addr); -void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res); +void __iomem *devm_ioremap_resource(struct device *dev, + const struct resource *res); void __iomem *devm_of_iomap(struct device *dev, struct device_node *node, int index, @@ -771,11 +772,17 @@ struct device_dma_parameters { /** * struct device_connection - Device Connection Descriptor + * @fwnode: The device node of the connected device * @endpoint: The names of the two devices connected together * @id: Unique identifier for the connection * @list: List head, private, for internal use only + * + * NOTE: @fwnode is not used together with @endpoint. @fwnode is used when + * platform firmware defines the connection. When the connection is registered + * with device_connection_add() @endpoint is used instead. */ struct device_connection { + struct fwnode_handle *fwnode; const char *endpoint[2]; const char *id; struct list_head list; @@ -869,11 +876,12 @@ struct device_link { struct list_head c_node; enum device_link_state status; u32 flags; - bool rpm_active; + refcount_t rpm_active; struct kref kref; #ifdef CONFIG_SRCU struct rcu_head rcu_head; #endif + bool supplier_preactivated; /* Owned by consumer probe. */ }; /** diff --git a/include/linux/diagchar.h b/include/linux/diagchar.h index dcabab1fbd8b532d90267380565c9eca3eb6c9fd..87f14022988b9d71b8156f9a69ca3fbf27366663 100644 --- a/include/linux/diagchar.h +++ b/include/linux/diagchar.h @@ -62,6 +62,7 @@ #define DIAG_IOCTL_QUERY_MD_PID 41 #define DIAG_IOCTL_QUERY_PD_FEATUREMASK 42 #define DIAG_IOCTL_PASSTHRU_CONTROL 43 +#define DIAG_IOCTL_MDM_HDLC_TOGGLE 44 /* PC Tools IDs */ #define APQ8060_TOOLS_ID 4062 diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h index 851a46c024f58d272d812ba359dfcbfddcfc1249..f14e248d4493068f1b8b15c1b3accf8bda1ed328 100644 --- a/include/linux/dma-buf.h +++ b/include/linux/dma-buf.h @@ -427,6 +427,7 @@ struct dma_buf { char *buf_name; ktime_t ktime; const char *name; + spinlock_t name_lock; struct module *owner; struct list_head list_node; void *priv; diff --git a/include/linux/dma-mapping-fast.h b/include/linux/dma-mapping-fast.h index aca7592bfb6e7709fa8d0f324a049903bc9bfda1..6d24110eaef672246b3e34e2b428196f7e778b6e 100644 --- a/include/linux/dma-mapping-fast.h +++ b/include/linux/dma-mapping-fast.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. */ #ifndef __LINUX_DMA_MAPPING_FAST_H @@ -23,9 +23,11 @@ struct dma_fast_smmu_mapping { size_t num_4k_pages; unsigned int bitmap_size; + /* bitmap has 1s marked only valid mappings */ unsigned long *bitmap; + /* clean_bitmap has 1s marked for both valid and stale tlb mappings */ + unsigned long *clean_bitmap; unsigned long next_start; - unsigned long upcoming_stale_bit; bool have_stale_tlbs; dma_addr_t pgtbl_dma_handle; diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 0647f436f88c23987cc0d5675ce4debac1362aa7..50128c36f0b48d47ec6cd5bdb2b960974b3e9762 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -686,6 +686,7 @@ struct dma_filter { * @fill_align: alignment shift for memset operations * @dev_id: unique device ID * @dev: struct device reference for dma mapping api + * @owner: owner module (automatically set based on the provided dev) * @src_addr_widths: bit mask of src addr widths the device supports * Width is specified in bytes, e.g. for a device supporting * a width of 4 the mask should have BIT(4) set. @@ -749,6 +750,7 @@ struct dma_device { int dev_id; struct device *dev; + struct module *owner; u32 src_addr_widths; u32 dst_addr_widths; diff --git a/include/linux/eventfd.h b/include/linux/eventfd.h index ffcc7724ca210097f70cca236d8e249d09541e64..dc4fd8a6644dd62da4837ebc7e9d20a24716e59a 100644 --- a/include/linux/eventfd.h +++ b/include/linux/eventfd.h @@ -12,6 +12,8 @@ #include #include #include +#include +#include /* * CAREFUL: Check include/uapi/asm-generic/fcntl.h when defining @@ -40,6 +42,13 @@ __u64 eventfd_signal(struct eventfd_ctx *ctx, __u64 n); int eventfd_ctx_remove_wait_queue(struct eventfd_ctx *ctx, wait_queue_entry_t *wait, __u64 *cnt); +DECLARE_PER_CPU(int, eventfd_wake_count); + +static inline bool eventfd_signal_count(void) +{ + return this_cpu_read(eventfd_wake_count); +} + #else /* CONFIG_EVENTFD */ /* @@ -68,6 +77,11 @@ static inline int eventfd_ctx_remove_wait_queue(struct eventfd_ctx *ctx, return -ENOSYS; } +static inline bool eventfd_signal_count(void) +{ + return false; +} + #endif #endif /* _LINUX_EVENTFD_H */ diff --git a/include/linux/extcon.h b/include/linux/extcon.h index c65aa57e9be590f02c2b27d6c87914a2ec59b898..3aef2d1f5f7fc350b4399d033aca0279e8e91df0 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -200,6 +200,14 @@ extern int extcon_get_property(struct extcon_dev *edev, unsigned int id, extern int extcon_get_property_capability(struct extcon_dev *edev, unsigned int id, unsigned int prop); +/* + * Following APIs set array of mutually exclusive. + * The 'exclusive' argument indicates the array of mutually exclusive set + * of cables that cannot be attached simultaneously. + */ +extern int extcon_set_mutually_exclusive(struct extcon_dev *edev, + const u32 *exclusive); + /* * Following APIs register the notifier block in order to detect * the change of both state and property value for each external connector. diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 28473899602817afd35ebe544c432a8d6044987b..e8763a955f90dff125b2522543ed5e251f00f84f 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -23,6 +23,7 @@ #define NULL_ADDR ((block_t)0) /* used as block_t addresses */ #define NEW_ADDR ((block_t)-1) /* used as block_t addresses */ +#define COMPRESS_ADDR ((block_t)-2) /* used as compressed data flag */ #define F2FS_BYTES_TO_BLK(bytes) ((bytes) >> F2FS_BLKSIZE_BITS) #define F2FS_BLK_TO_BYTES(blk) ((blk) << F2FS_BLKSIZE_BITS) @@ -37,9 +38,6 @@ #define F2FS_MAX_QUOTAS 3 #define F2FS_ENC_UTF8_12_1 1 -#define F2FS_ENC_STRICT_MODE_FL (1 << 0) -#define f2fs_has_strict_mode(sbi) \ - (sbi->s_encoding_flags & F2FS_ENC_STRICT_MODE_FL) #define F2FS_IO_SIZE(sbi) (1 << F2FS_OPTION(sbi).write_io_size_bits) /* Blocks */ #define F2FS_IO_SIZE_KB(sbi) (1 << (F2FS_OPTION(sbi).write_io_size_bits + 2)) /* KB */ @@ -271,6 +269,10 @@ struct f2fs_inode { __le32 i_inode_checksum;/* inode meta checksum */ __le64 i_crtime; /* creation time */ __le32 i_crtime_nsec; /* creation time in nano scale */ + __le64 i_compr_blocks; /* # of compressed blocks */ + __u8 i_compress_algorithm; /* compress algorithm */ + __u8 i_log_cluster_size; /* log of cluster size */ + __le16 i_padding; /* padding */ __le32 i_extra_end[0]; /* for attribute size calculation */ } __packed; __le32 i_addr[DEF_ADDRS_PER_INODE]; /* Pointers to data blocks */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 66963a1c9b4c2b1521beacf5fff8d7bf463c2658..3a2945c9b8730c43b300bdcea7454a2f5ab07a6b 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1333,6 +1333,12 @@ extern int send_sigurg(struct fown_struct *fown); #define SB_ACTIVE (1<<30) #define SB_NOUSER (1<<31) +/* These flags relate to encoding and casefolding */ +#define SB_ENC_STRICT_MODE_FL (1 << 0) + +#define sb_has_enc_strict_mode(sb) \ + (sb->s_encoding_flags & SB_ENC_STRICT_MODE_FL) + /* * Umount options */ @@ -1400,6 +1406,10 @@ struct super_block { #endif #ifdef CONFIG_FS_VERITY const struct fsverity_operations *s_vop; +#endif +#ifdef CONFIG_UNICODE + struct unicode_map *s_encoding; + __u16 s_encoding_flags; #endif struct hlist_bl_head s_roots; /* alternate root dentries for NFS */ struct list_head s_mounts; /* list of mounts; _not_ for fs use */ @@ -3261,6 +3271,20 @@ extern int generic_file_fsync(struct file *, loff_t, loff_t, int); extern int generic_check_addressable(unsigned, u64); +#ifdef CONFIG_UNICODE +extern int generic_ci_d_hash(const struct dentry *dentry, struct qstr *str); +extern int generic_ci_d_compare(const struct dentry *dentry, unsigned int len, + const char *str, const struct qstr *name); +extern bool needs_casefold(const struct inode *dir); +#else +static inline bool needs_casefold(const struct inode *dir) +{ + return 0; +} +#endif +extern void generic_set_encrypted_ci_d_ops(struct inode *dir, + struct dentry *dentry); + #ifdef CONFIG_MIGRATION extern int buffer_migrate_page(struct address_space *, struct page *, struct page *, diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index a298256102c5b5abbc6c40f3d6340379de6775ae..989b76e982a7afedea8d69948b328cd37aaa2c26 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -76,6 +76,21 @@ static inline bool fscrypt_has_encryption_key(const struct inode *inode) return READ_ONCE(inode->i_crypt_info) != NULL; } +/** + * fscrypt_needs_contents_encryption() - check whether an inode needs + * contents encryption + * + * Return: %true iff the inode is an encrypted regular file and the kernel was + * built with fscrypt support. + * + * If you need to know whether the encrypt bit is set even when the kernel was + * built without fscrypt support, you must use IS_ENCRYPTED() directly instead. + */ +static inline bool fscrypt_needs_contents_encryption(const struct inode *inode) +{ + return IS_ENCRYPTED(inode) && S_ISREG(inode->i_mode); +} + static inline bool fscrypt_dummy_context_enabled(struct inode *inode) { return inode->i_sb->s_cop->dummy_context && @@ -123,6 +138,7 @@ static inline struct page *fscrypt_pagecache_page(struct page *bounce_page) } extern void fscrypt_free_bounce_page(struct page *bounce_page); +extern int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags); /* policy.c */ extern int fscrypt_ioctl_set_policy(struct file *, const void __user *); @@ -159,82 +175,14 @@ static inline void fscrypt_free_filename(struct fscrypt_name *fname) extern int fscrypt_fname_alloc_buffer(const struct inode *, u32, struct fscrypt_str *); extern void fscrypt_fname_free_buffer(struct fscrypt_str *); -extern int fscrypt_fname_disk_to_usr(struct inode *, u32, u32, - const struct fscrypt_str *, struct fscrypt_str *); - -#define FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE 32 - -/* Extracts the second-to-last ciphertext block; see explanation below */ -#define FSCRYPT_FNAME_DIGEST(name, len) \ - ((name) + round_down((len) - FS_CRYPTO_BLOCK_SIZE - 1, \ - FS_CRYPTO_BLOCK_SIZE)) - -#define FSCRYPT_FNAME_DIGEST_SIZE FS_CRYPTO_BLOCK_SIZE - -/** - * fscrypt_digested_name - alternate identifier for an on-disk filename - * - * When userspace lists an encrypted directory without access to the key, - * filenames whose ciphertext is longer than FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE - * bytes are shown in this abbreviated form (base64-encoded) rather than as the - * full ciphertext (base64-encoded). This is necessary to allow supporting - * filenames up to NAME_MAX bytes, since base64 encoding expands the length. - * - * To make it possible for filesystems to still find the correct directory entry - * despite not knowing the full on-disk name, we encode any filesystem-specific - * 'hash' and/or 'minor_hash' which the filesystem may need for its lookups, - * followed by the second-to-last ciphertext block of the filename. Due to the - * use of the CBC-CTS encryption mode, the second-to-last ciphertext block - * depends on the full plaintext. (Note that ciphertext stealing causes the - * last two blocks to appear "flipped".) This makes accidental collisions very - * unlikely: just a 1 in 2^128 chance for two filenames to collide even if they - * share the same filesystem-specific hashes. - * - * However, this scheme isn't immune to intentional collisions, which can be - * created by anyone able to create arbitrary plaintext filenames and view them - * without the key. Making the "digest" be a real cryptographic hash like - * SHA-256 over the full ciphertext would prevent this, although it would be - * less efficient and harder to implement, especially since the filesystem would - * need to calculate it for each directory entry examined during a search. - */ -struct fscrypt_digested_name { - u32 hash; - u32 minor_hash; - u8 digest[FSCRYPT_FNAME_DIGEST_SIZE]; -}; - -/** - * fscrypt_match_name() - test whether the given name matches a directory entry - * @fname: the name being searched for - * @de_name: the name from the directory entry - * @de_name_len: the length of @de_name in bytes - * - * Normally @fname->disk_name will be set, and in that case we simply compare - * that to the name stored in the directory entry. The only exception is that - * if we don't have the key for an encrypted directory and a filename in it is - * very long, then we won't have the full disk_name and we'll instead need to - * match against the fscrypt_digested_name. - * - * Return: %true if the name matches, otherwise %false. - */ -static inline bool fscrypt_match_name(const struct fscrypt_name *fname, - const u8 *de_name, u32 de_name_len) -{ - if (unlikely(!fname->disk_name.name)) { - const struct fscrypt_digested_name *n = - (const void *)fname->crypto_buf.name; - if (WARN_ON_ONCE(fname->usr_fname->name[0] != '_')) - return false; - if (de_name_len <= FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE) - return false; - return !memcmp(FSCRYPT_FNAME_DIGEST(de_name, de_name_len), - n->digest, FSCRYPT_FNAME_DIGEST_SIZE); - } - - if (de_name_len != fname->disk_name.len) - return false; - return !memcmp(de_name, fname->disk_name.name, fname->disk_name.len); -} +extern int fscrypt_fname_disk_to_usr(const struct inode *inode, + u32 hash, u32 minor_hash, + const struct fscrypt_str *iname, + struct fscrypt_str *oname); +extern bool fscrypt_match_name(const struct fscrypt_name *fname, + const u8 *de_name, u32 de_name_len); +extern u64 fscrypt_fname_siphash(const struct inode *dir, + const struct qstr *name); /* bio.c */ extern void fscrypt_decrypt_bio(struct bio *); @@ -252,6 +200,8 @@ extern int __fscrypt_prepare_rename(struct inode *old_dir, unsigned int flags); extern int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry, struct fscrypt_name *fname); +extern int fscrypt_prepare_setflags(struct inode *inode, + unsigned int oldflags, unsigned int flags); extern int __fscrypt_prepare_symlink(struct inode *dir, unsigned int len, unsigned int max_len, struct fscrypt_str *disk_link); @@ -268,6 +218,11 @@ static inline bool fscrypt_has_encryption_key(const struct inode *inode) return false; } +static inline bool fscrypt_needs_contents_encryption(const struct inode *inode) +{ + return false; +} + static inline bool fscrypt_dummy_context_enabled(struct inode *inode) { return false; @@ -451,7 +406,7 @@ static inline void fscrypt_fname_free_buffer(struct fscrypt_str *crypto_str) return; } -static inline int fscrypt_fname_disk_to_usr(struct inode *inode, +static inline int fscrypt_fname_disk_to_usr(const struct inode *inode, u32 hash, u32 minor_hash, const struct fscrypt_str *iname, struct fscrypt_str *oname) @@ -468,6 +423,13 @@ static inline bool fscrypt_match_name(const struct fscrypt_name *fname, return !memcmp(de_name, fname->disk_name.name, fname->disk_name.len); } +static inline u64 fscrypt_fname_siphash(const struct inode *dir, + const struct qstr *name) +{ + WARN_ON_ONCE(1); + return 0; +} + /* bio.c */ static inline void fscrypt_decrypt_bio(struct bio *bio) { @@ -510,6 +472,13 @@ static inline int __fscrypt_prepare_lookup(struct inode *dir, return -EOPNOTSUPP; } +static inline int fscrypt_prepare_setflags(struct inode *inode, + unsigned int oldflags, + unsigned int flags) +{ + return 0; +} + static inline int __fscrypt_prepare_symlink(struct inode *dir, unsigned int len, unsigned int max_len, @@ -702,8 +671,9 @@ static inline int fscrypt_prepare_rename(struct inode *old_dir, * filenames are presented in encrypted form. Therefore, we'll try to set up * the directory's encryption key, but even without it the lookup can continue. * - * This also installs a custom ->d_revalidate() method which will invalidate the - * dentry if it was created without the key and the key is later added. + * After calling this function, a filesystem should ensure that it's dentry + * operations contain fscrypt_d_revalidate if DCACHE_ENCRYPTED_NAME was set, + * so that the dentry can be invalidated if the key is later added. * * Return: 0 on success; -ENOENT if key is unavailable but the filename isn't a * correctly formed encoded ciphertext name, so a negative dentry should be diff --git a/include/linux/fsverity.h b/include/linux/fsverity.h index 3b6b8ccebe7d2e33997338ea382f601946a26ab9..ecc604e61d61b9ffcc96946904f5608e837168b5 100644 --- a/include/linux/fsverity.h +++ b/include/linux/fsverity.h @@ -77,6 +77,10 @@ struct fsverity_operations { * * @inode: the inode * @index: 0-based index of the page within the Merkle tree + * @num_ra_pages: The number of Merkle tree pages that should be + * prefetched starting at @index if the page at @index + * isn't already cached. Implementations may ignore this + * argument; it's only a performance optimization. * * This can be called at any time on an open verity file, as well as * between ->begin_enable_verity() and ->end_enable_verity(). It may be @@ -87,7 +91,8 @@ struct fsverity_operations { * Return: the page on success, ERR_PTR() on failure */ struct page *(*read_merkle_tree_page)(struct inode *inode, - pgoff_t index); + pgoff_t index, + unsigned long num_ra_pages); /** * Write a Merkle tree block to the given inode. diff --git a/include/linux/gfp.h b/include/linux/gfp.h index 730b56e7eea43f6d8370ed2a1c662264752e5705..a7ca2fa72cfa4bc9c38114ec329d2cb3c28cf4bd 100644 --- a/include/linux/gfp.h +++ b/include/linux/gfp.h @@ -219,8 +219,13 @@ struct vm_area_struct; #define __GFP_NOLOCKDEP ((__force gfp_t)___GFP_NOLOCKDEP) /* Room for N __GFP_FOO bits */ -#define __GFP_BITS_SHIFT (24 + IS_ENABLED(CONFIG_LOCKDEP)) +#define __GFP_BITS_SHIFT (25) +#ifdef CONFIG_LOCKDEP #define __GFP_BITS_MASK ((__force gfp_t)((1 << __GFP_BITS_SHIFT) - 1)) +#else +#define __GFP_BITS_MASK (((__force gfp_t)((1 << __GFP_BITS_SHIFT) - 1)) & \ + ~0x800000u) +#endif /** * DOC: Useful GFP flag combinations diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 01106d1f7200f83e4b66eea073b6fbe9cee53756..6e0774d18d41155b7bd6fbaffa1c78890afcf9a6 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -47,11 +47,13 @@ struct gpio_irq_chip { */ const struct irq_domain_ops *domain_ops; +#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY /** * @parent_domain: * */ struct irq_domain *parent_domain; +#endif /** * @handler: diff --git a/include/linux/hid.h b/include/linux/hid.h index 8b3e5e8a72fbc5391a00b6ca417737fe76de593c..8506637f070d17f730d847c7f492a0365703c325 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -495,7 +495,7 @@ struct hid_report_enum { }; #define HID_MIN_BUFFER_SIZE 64 /* make sure there is at least a packet size of space */ -#define HID_MAX_BUFFER_SIZE 4096 /* 4kb */ +#define HID_MAX_BUFFER_SIZE 8192 /* 8kb */ #define HID_CONTROL_FIFO_SIZE 256 /* to init devices with >100 reports */ #define HID_OUTPUT_FIFO_SIZE 64 diff --git a/include/linux/i2c-msm-v2.h b/include/linux/i2c-msm-v2.h new file mode 100644 index 0000000000000000000000000000000000000000..6aac342c8c4feea0cf7b852a9cd4152c18c918bd --- /dev/null +++ b/include/linux/i2c-msm-v2.h @@ -0,0 +1,664 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014-2015,2017-2020, The Linux Foundation. All rights reserved. + */ + +/* + * I2C controller driver for Qualcomm Technologies Inc platforms + */ + +#ifndef _I2C_MSM_V2_H +#define _I2C_MSM_V2_H + +#include +#include + +enum msm_i2_debug_level { + MSM_ERR, /* Error messages only. Always on */ + MSM_PROF, /* High level events. Use for profiling */ + MSM_DBG, /* Low level details. Use for debugging */ +}; + +#define i2c_msm_dbg(ctrl, dbg_level, fmt, ...) do {\ + if (ctrl->dbgfs.dbg_lvl >= dbg_level)\ + dev_info(ctrl->dev, fmt, ##__VA_ARGS__);\ + } while (0) + +#define BIT_IS_SET(val, idx) ((val >> idx) & 0x1) +#define BITS_AT(val, idx, n_bits)(((val) & (((1 << n_bits) - 1) << idx)) >> idx) +#define MASK_IS_SET(val, mask) ((val & mask) == mask) +#define MASK_IS_SET_BOOL(val, mask) (MASK_IS_SET(val, mask) ? 1 : 0) +#define KHz(freq) (1000 * freq) +#define I2C_MSM_CLK_FAST_PLUS_FREQ (1000000) + +/* QUP Registers */ +enum { + QUP_CONFIG = 0x0, + QUP_STATE = 0x4, + QUP_IO_MODES = 0x8, + QUP_SW_RESET = 0xC, + QUP_OPERATIONAL = 0x18, + QUP_ERROR_FLAGS = 0x1C, + QUP_ERROR_FLAGS_EN = 0x20, + QUP_TEST_CTRL = 0x24, + QUP_OPERATIONAL_MASK = 0x28, + QUP_HW_VERSION = 0x30, + QUP_MX_READ_COUNT = 0x208, + QUP_MX_WRITE_COUNT = 0x150, + QUP_MX_OUTPUT_COUNT = 0x100, + QUP_MX_INPUT_COUNT = 0x200, + QUP_MX_WR_CNT = 0x100, + QUP_OUT_DEBUG = 0x108, + QUP_OUT_FIFO_CNT = 0x10C, + QUP_OUT_FIFO_BASE = 0x110, + QUP_IN_READ_CUR = 0x20C, + QUP_IN_DEBUG = 0x210, + QUP_IN_FIFO_CNT = 0x214, + QUP_IN_FIFO_BASE = 0x218, + QUP_I2C_MASTER_CLK_CTL = 0x400, + QUP_I2C_STATUS = 0x404, + QUP_I2C_MASTER_CONFIG = 0x408, + QUP_I2C_MASTER_BUS_CLR = 0x40C, +}; + +/* Register:QUP_STATE state field values */ +enum i2c_msm_qup_state { + QUP_STATE_RESET = 0, + QUP_STATE_RUN = 1U, + QUP_STATE_PAUSE = 3U, +}; + +/* Register:QUP_STATE fields */ +enum { + QUP_STATE_MASK = 3U, + QUP_STATE_VALID = BIT(2), + QUP_I2C_MAST_GEN = BIT(4), + QUP_I2C_FLUSH = BIT(6), + QUP_I2C_STATUS_RESET = 0x42, +}; + + +/* Register:QUP_CONFIG fields */ +enum { + QUP_MINI_CORE_MASK = 0xF00, + QUP_MINI_CORE_I2C_VAL = 0x200, + QUP_N_MASK = 0x1F, + QUP_N_VAL = 0x7, /* 0xF for A family */ + QUP_NO_OUTPUT = BIT(6), + QUP_NO_INPUT = BIT(7), + QUP_APP_CLK_ON_EN = BIT(12), + QUP_CORE_CLK_ON_EN = BIT(13), + QUP_FIFO_CLK_GATE_EN = BIT(14), +}; + +/* Register:QUP_OPERATIONAL fields */ +enum { + QUP_INPUT_FIFO_NOT_EMPTY = BIT(5), + QUP_OUTPUT_SERVICE_FLAG = BIT(8), + QUP_INPUT_SERVICE_FLAG = BIT(9), + QUP_MAX_OUTPUT_DONE_FLAG = BIT(10), + QUP_MAX_INPUT_DONE_FLAG = BIT(11), + QUP_OUT_BLOCK_WRITE_REQ = BIT(12), + QUP_IN_BLOCK_READ_REQ = BIT(13), +}; + +/* Register:QUP_OPERATIONAL_MASK fields */ +enum { + QUP_INPUT_SERVICE_MASK = BIT(9), + QUP_OUTPUT_SERVICE_MASK = BIT(8), +}; + +/* Register:QUP_IO_MODES fields */ +enum { + QUP_OUTPUT_MODE = BIT(10) | BIT(11), + QUP_INPUT_MODE = BIT(12) | BIT(13), + QUP_UNPACK_EN = BIT(14), + QUP_PACK_EN = BIT(15), + QUP_OUTPUT_BIT_SHIFT_EN = BIT(16), +}; + +/* Register:QUP_I2C_STATUS (a.k.a I2C_MASTER_STATUS) fields */ +enum { + QUP_BUS_ERROR = BIT(2), + QUP_PACKET_NACKED = BIT(3), + QUP_ARB_LOST = BIT(4), + QUP_INVALID_WRITE = BIT(5), + QUP_FAILED = BIT(6), + QUP_BUS_ACTIVE = BIT(8), + QUP_BUS_MASTER = BIT(9), + QUP_INVALID_TAG = BIT(23), + QUP_INVALID_READ_ADDR = BIT(24), + QUP_INVALID_READ_SEQ = BIT(25), + QUP_I2C_SDA = BIT(26), + QUP_I2C_SCL = BIT(27), + QUP_MSTR_STTS_ERR_MASK = 0x380003C, +}; + +/* Register:QUP_I2C_MASTER_CONFIG fields */ +enum { + QUP_EN_VERSION_TWO_TAG = 1U, +}; + +/* Register:QUP_I2C_MASTER_CLK_CTL field setters */ +#define I2C_MSM_SCL_NOISE_REJECTION(reg_val, noise_rej_val) \ + (((reg_val) & ~(0x3 << 24)) | (((noise_rej_val) & 0x3) << 24)) +#define I2C_MSM_SDA_NOISE_REJECTION(reg_val, noise_rej_val) \ + (((reg_val) & ~(0x3 << 26)) | (((noise_rej_val) & 0x3) << 26)) + +/* Register:QUP_ERROR_FLAGS_EN flags */ +enum { + QUP_OUTPUT_OVER_RUN_ERR_EN = BIT(5), + QUP_INPUT_UNDER_RUN_ERR_EN = BIT(4), + QUP_OUTPUT_UNDER_RUN_ERR_EN = BIT(3), + QUP_INPUT_OVER_RUN_ERR_EN = BIT(2), +}; + +/* Status, Error flags */ +enum { + I2C_STATUS_WR_BUFFER_FULL = BIT(0), + I2C_STATUS_BUS_ACTIVE = BIT(8), + I2C_STATUS_BUS_MASTER = BIT(9), + I2C_STATUS_ERROR_MASK = 0x38000FC, + QUP_I2C_NACK_FLAG = BIT(3), + QUP_IN_NOT_EMPTY = BIT(5), + QUP_ERR_FLGS_MASK = 0x3C, +}; + +/* Master status clock states */ +enum { + I2C_CLK_RESET_BUSIDLE_STATE = 0, + I2C_CLK_FORCED_LOW_STATE = 5, +}; + +/* Controller's power state */ +enum i2c_msm_power_state { + I2C_MSM_PM_RT_ACTIVE, + I2C_MSM_PM_RT_SUSPENDED, + I2C_MSM_PM_SYS_SUSPENDED +}; + +/* + * The max buffer size required for tags is for holding the following sequence: + * [start] + [start | slv-addr] + [ rd/wr | len] + * which sum up to 6 bytes. However, we use u64 to hold the value, thus we say + * that max length is 8 bytes. + */ +#define I2C_MSM_TAG2_MAX_LEN (4) +#define I2C_MSM_DMA_TX_SZ (64) /* tx chan n entries */ +#define I2C_MSM_DMA_RX_SZ (32) /* rx chan n entries */ +#define I2C_MSM_DMA_DESC_ARR_SIZ (I2C_MSM_DMA_TX_SZ + I2C_MSM_DMA_RX_SZ) +#define I2C_MSM_REG_2_STR_BUF_SZ (128) +/* Optimal value to hold the error strings */ +#define I2C_MSM_MAX_ERR_BUF_SZ (256) +#define I2C_MSM_BUF_DUMP_MAX_BC (20) +#define I2C_MSM_MAX_POLL_MSEC (100) +#define I2C_MSM_TIMEOUT_SAFETY_COEF (10) +#define I2C_MSM_TIMEOUT_MIN_USEC (500000) + +/* QUP v2 tags */ +#define QUP_TAG2_DATA_WRITE (0x82ULL) +#define QUP_TAG2_DATA_WRITE_N_STOP (0x83ULL) +#define QUP_TAG2_DATA_READ (0x85ULL) +#define QUP_TAG2_DATA_READ_N_STOP (0x87ULL) +#define QUP_TAG2_START (0x81ULL) +#define QUP_TAG2_DATA_READ_N_NACK (0x86ULL) +#define QUP_TAG2_START_STOP (0x8AULL) +#define QUP_TAG2_INPUT_EOT (0x93ULL) +#define QUP_TAG2_FLUSH_STOP (0x96ULL) +#define QUP_BUF_OVERHD_BC (2) +#define QUP_MAX_BUF_SZ (256) + +enum i2c_msm_clk_path_vec_idx { + I2C_MSM_CLK_PATH_SUSPEND_VEC, + I2C_MSM_CLK_PATH_RESUME_VEC, +}; +#define I2C_MSM_CLK_PATH_AVRG_BW(ctrl) (0) +/* + * Reducing the frequency by 1 to make sure it is less than 19.2MHz + * so that we don't need RPM ack to unvote which will work only if vote + * is less than or equal to 19.2MHz. To be on the safe side we are decreasing + * frequency by 1. + */ +#define I2C_MSM_CLK_PATH_BRST_BW(ctrl) ((ctrl->rsrcs.clk_freq_in - 1) * 4) + +enum i2c_msm_gpio_name_idx { + I2C_MSM_GPIO_SCL, + I2C_MSM_GPIO_SDA, +}; + +extern const char * const i2c_msm_mode_str_tbl[]; + +struct i2c_msm_ctrl; + +/* + * i2c_msm_dma_mem: utility struct which holds both physical and virtual addr + */ +struct i2c_msm_dma_mem { + dma_addr_t phy_addr; + void *vrtl_addr; +}; + +/* + * i2c_msm_tag: tag's data and its length. + * + * @len tag len can be two, four or six bytes. + */ +struct i2c_msm_tag { + u64 val; + int len; +}; + +/* + * i2c_msm_dma_tag: similar to struct i2c_msm_tag but holds physical address. + * + * @buf physical address of entry in the tag_arr of + * struct i2c_msm_xfer_mode_dma + * @len tag len. + * + * Hold the information from i2c_msm_dma_xfer_prepare() which is used by + * i2c_msm_dma_xfer_process() and freed by i2c_msm_dma_xfer_unprepare() + */ +struct i2c_msm_dma_tag { + dma_addr_t buf; + size_t len; +}; + +/* + * i2c_msm_dma_buf: dma mapped pointer to i2c_msg data buffer and related tag + * @vir_addr ptr to i2c_msg buf beginning or with offset (when buf len > 256) + */ +struct i2c_msm_dma_buf { + struct i2c_msm_dma_mem ptr; + enum dma_data_direction dma_dir; + size_t len; + bool is_rx; + bool is_last; + struct i2c_msm_dma_tag tag; + /* DMA API */ + struct scatterlist sg_list[2]; +}; + +/* + * i2c_msm_dma_chan: per channel info + * + * @is_init true when the channel is initialized and requires eventual teardown. + * @name channel name (tx/rx) for debugging. + * @desc_cnt_cur number of occupied descriptors + */ +struct i2c_msm_dma_chan { + bool is_init; + const char *name; + size_t desc_cnt_cur; + struct dma_chan *dma_chan; + enum dma_transfer_direction dir; +}; + +enum i2c_msm_dma_chan_dir { + I2C_MSM_DMA_TX, + I2C_MSM_DMA_RX, + I2C_MSM_DMA_CNT, +}; + +enum i2c_msm_dma_state { + I2C_MSM_DMA_INIT_NONE, /* Uninitialized DMA */ + I2C_MSM_DMA_INIT_CORE, /* Core init not channels, memory Allocated */ + I2C_MSM_DMA_INIT_CHAN, /* Both Core and channels are init */ +}; + +/* + * struct i2c_msm_xfer_mode_dma: DMA mode configuration and work space + * + * @state specifies the DMA core and channel initialization states. + * @buf_arr_cnt current number of valid buffers in buf_arr. The valid buffers + * are at index 0..buf_arr_cnt excluding buf_arr_cnt. + * @buf_arr array of descriptors which point to the user's buffer + * virtual and physical address, and hold meta data about the buffer + * and respective tag. + * @tag_arr array of tags in DMAable memory. Holds a tag per buffer of the same + * index, that is tag_arr[i] is related to buf_arr[i]. Also, tag_arr[i] + * is queued in the tx channel just before buf_arr[i] is queued in + * the tx (output buf) or rx channel (input buffer). + * @eot_n_flush_stop_tags EOT and flush-stop tags to be queued to the tx + * DMA channel after the last transfer when it is a read. + * @input_tag hw is placing input tags in the rx channel on read operations. + * The value of these tags is "don't care" from DMA transfer + * perspective. Thus, this single buffer is used for all the input + * tags. The field is used as write only. + */ +struct i2c_msm_xfer_mode_dma { + enum i2c_msm_dma_state state; + size_t buf_arr_cnt; + struct i2c_msm_dma_buf buf_arr[I2C_MSM_DMA_DESC_ARR_SIZ]; + struct i2c_msm_dma_mem tag_arr; + struct i2c_msm_dma_mem eot_n_flush_stop_tags; + struct i2c_msm_dma_mem input_tag; + struct i2c_msm_dma_chan chan[I2C_MSM_DMA_CNT]; +}; + +/* + * I2C_MSM_DMA_TAG_MEM_SZ includes the following fields of + * struct i2c_msm_xfer_mode_dma (in order): + * + * Buffer of DMA memory: + * +-----------+---------+-----------+-----------+----+-----------+ + * | input_tag | eot_... | tag_arr 0 | tag_arr 1 | .. | tag_arr n | + * +-----------+---------+-----------+-----------+----+-----------+ + * + * Why +2? + * One tag buffer for the input tags. This is a write only buffer for DMA, it is + * used to read the tags of the input fifo. We let them overwrite each other, + * since it is a throw-away from the driver's perspective. + * Second tag buffer for the EOT and flush-stop tags. This is a read only + * buffer (from DMA perspective). It is used to put EOT and flush-stop at the + * end of every transaction. + */ +#define I2C_MSM_DMA_TAG_MEM_SZ \ + ((I2C_MSM_DMA_DESC_ARR_SIZ + 2) * I2C_MSM_TAG2_MAX_LEN) + +/* + * i2c_msm_xfer_mode_fifo: operations and state of FIFO mode + * + * @ops "base class" of i2c_msm_xfer_mode_dma. Contains the operations while + * the rest of the fields contain the data. + * @input_fifo_sz input fifo size in bytes + * @output_fifo_sz output fifo size in bytes + * @in_rem remaining u32 entries in input FIFO before empty + * @out_rem remaining u32 entries in output FIFO before full + * @out_buf buffer for collecting bytes to four bytes groups (u32) before + * writing them to the output fifo. + * @out_buf_idx next free index in out_buf. 0..3 + */ +struct i2c_msm_xfer_mode_fifo { + size_t input_fifo_sz; + size_t output_fifo_sz; + size_t in_rem; + size_t out_rem; + u8 out_buf[4]; + int out_buf_idx; +}; + +/* i2c_msm_xfer_mode_blk: operations and state of Block mode + * + * @is_init when true, struct is initialized and requires mem free on exit + * @in_blk_sz size of input/rx block + * @out_blk_sz size of output/tx block + * @tx_cache internal buffer to store tx data + * @rx_cache internal buffer to store rx data + * @rx_cache_idx points to the next unread index in rx cache + * @tx_cache_idx points to the next unwritten index in tx cache + * @wait_rx_blk completion object to wait on for end of blk rx transfer. + * @wait_tx_blk completion object to wait on for end of blk tx transfer. + * @complete_mask applied to QUP_OPERATIONAL to determine when blk + * xfer is complete. + */ +struct i2c_msm_xfer_mode_blk { + bool is_init; + size_t in_blk_sz; + size_t out_blk_sz; + u8 *tx_cache; + u8 *rx_cache; + int rx_cache_idx; + int tx_cache_idx; + struct completion wait_rx_blk; + struct completion wait_tx_blk; + u32 complete_mask; +}; + +/* INPUT_MODE and OUTPUT_MODE filds of QUP_IO_MODES register */ +enum i2c_msm_xfer_mode_id { + I2C_MSM_XFER_MODE_FIFO, + I2C_MSM_XFER_MODE_BLOCK, + I2C_MSM_XFER_MODE_DMA, + I2C_MSM_XFER_MODE_NONE, /* keep last as a counter */ +}; + + +struct i2c_msm_dbgfs { + struct dentry *root; + enum msm_i2_debug_level dbg_lvl; + enum i2c_msm_xfer_mode_id force_xfer_mode; +}; + +/* + * qup_i2c_clk_path_vote: data to use bus scaling driver for clock path vote + * + * @mstr_id master id number of the i2c core or its wrapper (BLSP/GSBI). + * When zero, clock path voting is disabled. + * @client_hdl when zero, client is not registered with the bus scaling driver, + * and bus scaling functionality should not be used. When non zero, it + * is a bus scaling client id and may be used to vote for clock path. + * @reg_err when true, registration error was detected and an error message was + * logged. i2c will attempt to re-register but will log error only once. + * once registration succeed, the flag is set to false. + * @actv_only when set, votes when system active and removes the vote when + * system goes idle (optimises for performance). When unset, voting using + * runtime pm (optimizes for power). + */ +struct qup_i2c_clk_path_vote { + u32 mstr_id; + u32 client_hdl; + struct msm_bus_scale_pdata *pdata; + bool reg_err; + bool actv_only; +}; + +/* + * i2c_msm_resources: OS resources + * + * @mem I2C controller memory resource from platform data. + * @base I2C controller virtual base address + * @clk_freq_in core clock frequency in Hz + * @clk_freq_out bus clock frequency in Hz + */ +struct i2c_msm_resources { + struct resource *mem; + void __iomem *base; /* virtual */ + struct clk *core_clk; + struct clk *iface_clk; + int clk_freq_in; + int clk_freq_out; + struct qup_i2c_clk_path_vote clk_path_vote; + int irq; + bool disable_dma; + struct pinctrl *pinctrl; + struct pinctrl_state *gpio_state_active; + struct pinctrl_state *gpio_state_suspend; +}; + +#define I2C_MSM_PINCTRL_ACTIVE "i2c_active" +#define I2C_MSM_PINCTRL_SUSPEND "i2c_sleep" + +/* + * i2c_msm_xfer_buf: current xfer position and preprocessed tags + * + * @is_init the buf is marked initialized by the first call to + * i2c_msm_xfer_next_buf() + * @msg_idx index of the message that the buffer is pointing to + * @byte_idx index of first byte in the current buffer + * @end_idx count of bytes processed from the current message. This value + * is compared against len to find out if buffer is done processing. + * @len number of bytes in current buffer. + * @is_rx when true, current buffer is pointing to a i2c read operation. + * @slv_addr 8 bit address. This is the i2c_msg->addr + rd/wr bit. + * + * Keep track of current position in the client's transfer request and + * pre-process a transfer's buffer and tags. + */ +struct i2c_msm_xfer_buf { + bool is_init; + int msg_idx; + int byte_idx; + int end_idx; + int len; + bool is_rx; + bool is_last; + u16 slv_addr; + struct i2c_msm_tag in_tag; + struct i2c_msm_tag out_tag; +}; + +#ifdef DEBUG +#define I2C_MSM_PROF_MAX_EVNTS (64) +#else +#define I2C_MSM_PROF_MAX_EVNTS (16) +#endif + +/* + * i2c_msm_prof_event: profiling event + * + * @data Additional data about the event. The interpretation of the data is + * dependent on the type field. + * @type event type (see enum i2c_msm_prof_event_type) + */ +struct i2c_msm_prof_event { + struct timespec time; + u64 data0; + u32 data1; + u32 data2; + u8 type; + u8 dump_func_id; +}; + +enum i2c_msm_err { + I2C_MSM_NO_ERR = 0, + I2C_MSM_ERR_NACK, + I2C_MSM_ERR_ARB_LOST, + I2C_MSM_ERR_BUS_ERR, + I2C_MSM_ERR_TIMEOUT, + I2C_MSM_ERR_CORE_CLK, + I2C_MSM_ERR_OVR_UNDR_RUN, +}; + +/* + * i2c_msm_xfer: A client transfer request. A list of one or more i2c messages + * + * @msgs NULL when no active xfer. Points to array of i2c_msgs + * given by the client. + * @msg_cnt number of messages in msgs array. + * @complete completion object to wait on for end of transfer. + * @rx_cnt number of input bytes in the client's request. + * @tx_cnt number of output bytes in the client's request. + * @rx_ovrhd_cnt number of input bytes due to tags. + * @tx_ovrhd_cnt number of output bytes due to tags. + * @event profiling data. An array of timestamps of transfer events + * @event_cnt number of items in event array. + * @is_active true during xfer process and false after xfer end + * @mtx mutex to solve multithreaded problem in xfer + */ +struct i2c_msm_xfer { + struct i2c_msg *msgs; + int msg_cnt; + enum i2c_msm_xfer_mode_id mode_id; + struct completion complete; + struct completion rx_complete; + size_t rx_cnt; + size_t tx_cnt; + size_t rx_ovrhd_cnt; + size_t tx_ovrhd_cnt; + struct i2c_msm_xfer_buf cur_buf; + u32 timeout; + bool last_is_rx; + enum i2c_msm_err err; + struct i2c_msm_prof_event event[I2C_MSM_PROF_MAX_EVNTS]; + atomic_t event_cnt; + atomic_t is_active; + struct mutex mtx; + struct i2c_msm_xfer_mode_fifo fifo; + struct i2c_msm_xfer_mode_blk blk; + struct i2c_msm_xfer_mode_dma dma; +}; + +/* + * i2c_msm_ctrl: the driver's main struct + * + * @is_init true when + * @ver info that is different between i2c controller versions + * @ver_num ha + * @xfer state of the currently processed transfer. + * @dbgfs debug-fs root and values that may be set via debug-fs. + * @rsrcs resources from platform data including clocks, gpios, irqs, and + * memory regions. + * @mstr_clk_ctl cached value for programming to mstr_clk_ctl register + * @i2c_sts_reg status of QUP_I2C_MASTER_STATUS register. + * @qup_op_reg status of QUP_OPERATIONAL register. + */ +struct i2c_msm_ctrl { + struct device *dev; + struct i2c_adapter adapter; + struct i2c_msm_xfer xfer; + struct i2c_msm_dbgfs dbgfs; + struct i2c_msm_resources rsrcs; + u32 mstr_clk_ctl; + u32 i2c_sts_reg; + u32 qup_op_reg; + enum i2c_msm_power_state pwr_state; +}; + +/* Enum for the profiling event types */ +enum i2c_msm_prof_evnt_type { + I2C_MSM_VALID_END, + I2C_MSM_PIP_DSCN, + I2C_MSM_PIP_CNCT, + I2C_MSM_ACTV_END, + I2C_MSM_IRQ_BGN, + I2C_MSM_IRQ_END, + I2C_MSM_XFER_BEG, + I2C_MSM_XFER_END, + I2C_MSM_SCAN_SUM, + I2C_MSM_NEXT_BUF, + I2C_MSM_COMPLT_OK, + I2C_MSM_COMPLT_FL, + I2C_MSM_PROF_RESET, +}; + +#ifdef CONFIG_I2C_MSM_PROF_DBG +void i2c_msm_dbgfs_init(struct i2c_msm_ctrl *ctrl); + +void i2c_msm_dbgfs_teardown(struct i2c_msm_ctrl *ctrl); + +/* diagonisis the i2c registers and dump the errors accordingly */ +const char *i2c_msm_dbg_tag_to_str(const struct i2c_msm_tag *tag, + char *buf, size_t buf_len); + +void i2c_msm_prof_evnt_dump(struct i2c_msm_ctrl *ctrl); + +/* function definitions to be used from the i2c-msm-v2-debug file */ +void i2c_msm_prof_evnt_add(struct i2c_msm_ctrl *ctrl, + enum msm_i2_debug_level dbg_level, + enum i2c_msm_prof_evnt_type event, + u64 data0, u32 data1, u32 data2); + +int i2c_msm_dbg_qup_reg_dump(struct i2c_msm_ctrl *ctrl); + +const char * +i2c_msm_dbg_dma_tag_to_str(const struct i2c_msm_dma_tag *dma_tag, char *buf, + size_t buf_len); +#else +/* use dummy functions */ +static inline void i2c_msm_dbgfs_init(struct i2c_msm_ctrl *ctrl) {} +static inline void i2c_msm_dbgfs_teardown(struct i2c_msm_ctrl *ctrl) {} + +static inline const char *i2c_msm_dbg_tag_to_str(const struct i2c_msm_tag *tag, + char *buf, size_t buf_len) +{ + return NULL; +} +static inline void i2c_msm_prof_evnt_dump(struct i2c_msm_ctrl *ctrl) {} + +/* function definitions to be used from the i2c-msm-v2-debug file */ +static inline void i2c_msm_prof_evnt_add(struct i2c_msm_ctrl *ctrl, + enum msm_i2_debug_level dbg_level, + enum i2c_msm_prof_evnt_type event, + u64 data0, u32 data1, u32 data2) {} + +static inline int i2c_msm_dbg_qup_reg_dump(struct i2c_msm_ctrl *ctrl) +{ + return true; +} +static inline const char *i2c_msm_dbg_dma_tag_to_str(const struct + i2c_msm_dma_tag * dma_tag, char *buf, size_t buf_len) +{ + return NULL; +} +#endif /* I2C_MSM_V2_PROF_DBG */ +#endif /* _I2C_MSM_V2_H */ diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h index 9887f4f8e2a898bce65cc76477eab86aadafa296..11e95d934403f1b1780a9bbe24b5076f6a841d90 100644 --- a/include/linux/iio/consumer.h +++ b/include/linux/iio/consumer.h @@ -289,6 +289,15 @@ int iio_read_max_channel_raw(struct iio_channel *chan, int *val); */ int iio_read_avail_channel_raw(struct iio_channel *chan, const int **vals, int *length); +/** + * iio_write_channel_processed() - write to a given channel + * @chan: The channel being queried. + * @val: Value being written. + * + * Note processed writes to iio channels are converted to raw + * values before being written. + */ +int iio_write_channel_processed(struct iio_channel *chan, int val); /** * iio_get_channel_type() - get the type of a channel diff --git a/include/linux/input.h b/include/linux/input.h index 7c7516eb7d76c64071e51c199fdb0fd0f4e3c934..dcf02073e968f8ddc95eab99c48c46f074d64a7e 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -36,6 +36,13 @@ struct input_value { __s32 value; }; +enum input_clock_type { + INPUT_CLK_REAL = 0, + INPUT_CLK_MONO, + INPUT_CLK_BOOT, + INPUT_CLK_MAX +}; + /** * struct input_dev - represents an input device * @name: name of the device @@ -117,6 +124,8 @@ struct input_value { * @vals: array of values queued in the current frame * @devres_managed: indicates that devices is managed with devres framework * and needs not be explicitly unregistered or freed. + * @timestamp: storage for a timestamp set by input_set_timestamp called + * by a driver */ struct input_dev { const char *name; @@ -187,6 +196,8 @@ struct input_dev { struct input_value *vals; bool devres_managed; + + ktime_t timestamp[INPUT_CLK_MAX]; }; #define to_input_dev(d) container_of(d, struct input_dev, dev) @@ -385,6 +396,9 @@ void input_close_device(struct input_handle *); int input_flush_device(struct input_handle *handle, struct file *file); +void input_set_timestamp(struct input_dev *dev, ktime_t timestamp); +ktime_t *input_get_timestamp(struct input_dev *dev); + void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value); void input_inject_event(struct input_handle *handle, unsigned int type, unsigned int code, int value); diff --git a/include/linux/intel-svm.h b/include/linux/intel-svm.h index 99bc5b3ae26e1622f07558c52510c5e351e3cad6..733eaf95e207cd7c91223b01aaf9652b8cac0422 100644 --- a/include/linux/intel-svm.h +++ b/include/linux/intel-svm.h @@ -130,7 +130,7 @@ static inline int intel_svm_unbind_mm(struct device *dev, int pasid) BUG(); } -static int intel_svm_is_pasid_valid(struct device *dev, int pasid) +static inline int intel_svm_is_pasid_valid(struct device *dev, int pasid) { return -EINVAL; } diff --git a/include/linux/iova.h b/include/linux/iova.h index 8c48bfaad8a7739ed6c961ac17a1f000d26e22dc..5c105c9f416e1ddd0bba6311d8230eaf0b0a87b6 100644 --- a/include/linux/iova.h +++ b/include/linux/iova.h @@ -166,6 +166,7 @@ void put_iova_domain(struct iova_domain *iovad); struct iova *split_and_remove_iova(struct iova_domain *iovad, struct iova *iova, unsigned long pfn_lo, unsigned long pfn_hi); void free_cpu_cached_iovas(unsigned int cpu, struct iova_domain *iovad); +void free_global_cached_iovas(struct iova_domain *iovad); #else static inline int iova_cache_get(void) { @@ -273,6 +274,11 @@ static inline void free_cpu_cached_iovas(unsigned int cpu, struct iova_domain *iovad) { } + +static inline void free_global_cached_iovas(struct iova_domain *iovad) +{ +} + #endif #endif diff --git a/include/linux/irq.h b/include/linux/irq.h index 60b5bc9810a54babf8c10201ef5ee0da1679dd66..2e9da0eda9d1110675fc283357a3796a349de754 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -208,6 +208,8 @@ struct irq_data { * IRQD_SINGLE_TARGET - IRQ allows only a single affinity target * IRQD_DEFAULT_TRIGGER_SET - Expected trigger already been set * IRQD_CAN_RESERVE - Can use reservation mode + * IRQD_MSI_NOMASK_QUIRK - Non-maskable MSI quirk for affinity change + * required */ enum { IRQD_TRIGGER_MASK = 0xf, @@ -230,6 +232,7 @@ enum { IRQD_SINGLE_TARGET = (1 << 24), IRQD_DEFAULT_TRIGGER_SET = (1 << 25), IRQD_CAN_RESERVE = (1 << 26), + IRQD_MSI_NOMASK_QUIRK = (1 << 27), }; #define __irqd_to_state(d) ACCESS_PRIVATE((d)->common, state_use_accessors) @@ -389,6 +392,21 @@ static inline bool irqd_can_reserve(struct irq_data *d) return __irqd_to_state(d) & IRQD_CAN_RESERVE; } +static inline void irqd_set_msi_nomask_quirk(struct irq_data *d) +{ + __irqd_to_state(d) |= IRQD_MSI_NOMASK_QUIRK; +} + +static inline void irqd_clr_msi_nomask_quirk(struct irq_data *d) +{ + __irqd_to_state(d) &= ~IRQD_MSI_NOMASK_QUIRK; +} + +static inline bool irqd_msi_nomask_quirk(struct irq_data *d) +{ + return __irqd_to_state(d) & IRQD_MSI_NOMASK_QUIRK; +} + #undef __irqd_to_state static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d) diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h index 3188c0bef3e7967ea5b1316f37cbe3cc36109a98..1d21e98d68549440aaaaff08a0e5919eee9232c1 100644 --- a/include/linux/irqchip/arm-gic-v3.h +++ b/include/linux/irqchip/arm-gic-v3.h @@ -165,7 +165,7 @@ #define GICR_PROPBASER_nCnB GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, nCnB) #define GICR_PROPBASER_nC GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, nC) #define GICR_PROPBASER_RaWt GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, RaWt) -#define GICR_PROPBASER_RaWb GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, RaWt) +#define GICR_PROPBASER_RaWb GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, RaWb) #define GICR_PROPBASER_WaWt GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, WaWt) #define GICR_PROPBASER_WaWb GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, WaWb) #define GICR_PROPBASER_RaWaWt GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, RaWaWt) @@ -192,7 +192,7 @@ #define GICR_PENDBASER_nCnB GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, nCnB) #define GICR_PENDBASER_nC GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, nC) #define GICR_PENDBASER_RaWt GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, RaWt) -#define GICR_PENDBASER_RaWb GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, RaWt) +#define GICR_PENDBASER_RaWb GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, RaWb) #define GICR_PENDBASER_WaWt GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, WaWt) #define GICR_PENDBASER_WaWb GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, WaWb) #define GICR_PENDBASER_RaWaWt GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, RaWaWt) @@ -251,7 +251,7 @@ #define GICR_VPROPBASER_nCnB GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, nCnB) #define GICR_VPROPBASER_nC GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, nC) #define GICR_VPROPBASER_RaWt GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, RaWt) -#define GICR_VPROPBASER_RaWb GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, RaWt) +#define GICR_VPROPBASER_RaWb GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, RaWb) #define GICR_VPROPBASER_WaWt GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, WaWt) #define GICR_VPROPBASER_WaWb GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, WaWb) #define GICR_VPROPBASER_RaWaWt GIC_BASER_CACHEABILITY(GICR_VPROPBASER, INNER, RaWaWt) @@ -277,7 +277,7 @@ #define GICR_VPENDBASER_nCnB GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, nCnB) #define GICR_VPENDBASER_nC GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, nC) #define GICR_VPENDBASER_RaWt GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, RaWt) -#define GICR_VPENDBASER_RaWb GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, RaWt) +#define GICR_VPENDBASER_RaWb GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, RaWb) #define GICR_VPENDBASER_WaWt GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, WaWt) #define GICR_VPENDBASER_WaWb GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, WaWb) #define GICR_VPENDBASER_RaWaWt GIC_BASER_CACHEABILITY(GICR_VPENDBASER, INNER, RaWaWt) @@ -351,7 +351,7 @@ #define GITS_CBASER_nCnB GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, nCnB) #define GITS_CBASER_nC GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, nC) #define GITS_CBASER_RaWt GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, RaWt) -#define GITS_CBASER_RaWb GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, RaWt) +#define GITS_CBASER_RaWb GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, RaWb) #define GITS_CBASER_WaWt GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, WaWt) #define GITS_CBASER_WaWb GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, WaWb) #define GITS_CBASER_RaWaWt GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, RaWaWt) @@ -375,7 +375,7 @@ #define GITS_BASER_nCnB GIC_BASER_CACHEABILITY(GITS_BASER, INNER, nCnB) #define GITS_BASER_nC GIC_BASER_CACHEABILITY(GITS_BASER, INNER, nC) #define GITS_BASER_RaWt GIC_BASER_CACHEABILITY(GITS_BASER, INNER, RaWt) -#define GITS_BASER_RaWb GIC_BASER_CACHEABILITY(GITS_BASER, INNER, RaWt) +#define GITS_BASER_RaWb GIC_BASER_CACHEABILITY(GITS_BASER, INNER, RaWb) #define GITS_BASER_WaWt GIC_BASER_CACHEABILITY(GITS_BASER, INNER, WaWt) #define GITS_BASER_WaWb GIC_BASER_CACHEABILITY(GITS_BASER, INNER, WaWb) #define GITS_BASER_RaWaWt GIC_BASER_CACHEABILITY(GITS_BASER, INNER, RaWaWt) diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index b845420b7e875e16a1feb80cfcd6457664b7aeb2..a78afdfb6b77c8e4aaf292275eaf00863875572d 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -189,7 +189,7 @@ enum { IRQ_DOMAIN_FLAG_HIERARCHY = (1 << 0), /* Irq domain name was allocated in __irq_domain_add() */ - IRQ_DOMAIN_NAME_ALLOCATED = (1 << 6), + IRQ_DOMAIN_NAME_ALLOCATED = (1 << 1), /* Irq domain is an IPI domain with virq per cpu */ IRQ_DOMAIN_FLAG_IPI_PER_CPU = (1 << 2), @@ -203,6 +203,13 @@ enum { /* Irq domain implements MSI remapping */ IRQ_DOMAIN_FLAG_MSI_REMAP = (1 << 5), + /* + * Quirk to handle MSI implementations which do not provide + * masking. Currently known to affect x86, but partially + * handled in core code. + */ + IRQ_DOMAIN_MSI_NOMASK_QUIRK = (1 << 6), + /* * Flags starting from IRQ_DOMAIN_FLAG_NONCORE are reserved * for implementation specific purposes and ignored by the diff --git a/include/linux/kcov.h b/include/linux/kcov.h index b76a1807028ddcdd41df38064a174d36d8748646..a10e84707d820b54739f7dde0064c9334ac6c6e5 100644 --- a/include/linux/kcov.h +++ b/include/linux/kcov.h @@ -37,12 +37,35 @@ do { \ (t)->kcov_mode &= ~KCOV_IN_CTXSW; \ } while (0) +/* See Documentation/dev-tools/kcov.rst for usage details. */ +void kcov_remote_start(u64 handle); +void kcov_remote_stop(void); +u64 kcov_common_handle(void); + +static inline void kcov_remote_start_common(u64 id) +{ + kcov_remote_start(kcov_remote_handle(KCOV_SUBSYSTEM_COMMON, id)); +} + +static inline void kcov_remote_start_usb(u64 id) +{ + kcov_remote_start(kcov_remote_handle(KCOV_SUBSYSTEM_USB, id)); +} + #else static inline void kcov_task_init(struct task_struct *t) {} static inline void kcov_task_exit(struct task_struct *t) {} static inline void kcov_prepare_switch(struct task_struct *t) {} static inline void kcov_finish_switch(struct task_struct *t) {} +static inline void kcov_remote_start(u64 handle) {} +static inline void kcov_remote_stop(void) {} +static inline u64 kcov_common_handle(void) +{ + return 0; +} +static inline void kcov_remote_start_common(u64 id) {} +static inline void kcov_remote_start_usb(u64 id) {} #endif /* CONFIG_KCOV */ #endif /* _LINUX_KCOV_H */ diff --git a/include/linux/key.h b/include/linux/key.h index 86cbff8b2bb2e6b2c347cc02dbc0b64d7d7923fa..804e3afbcc3c8df860314be2480e549381af48f2 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -349,6 +349,9 @@ static inline key_serial_t key_serial(const struct key *key) extern void key_set_timeout(struct key *, unsigned); +extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags, + key_perm_t perm); + /* * The permissions required on a key that we're looking up. */ diff --git a/include/linux/keyslot-manager.h b/include/linux/keyslot-manager.h index 6d32a031218e085e7ca810deec3a1b6fd19b64a0..2f4aac2851bfed7ca976344bc83d86e134ad404e 100644 --- a/include/linux/keyslot-manager.h +++ b/include/linux/keyslot-manager.h @@ -41,7 +41,9 @@ struct keyslot_mgmt_ll_ops { u8 *secret, unsigned int secret_size); }; -struct keyslot_manager *keyslot_manager_create(unsigned int num_slots, +struct keyslot_manager *keyslot_manager_create( + struct device *dev, + unsigned int num_slots, const struct keyslot_mgmt_ll_ops *ksm_ops, const unsigned int crypto_mode_supported[BLK_ENCRYPTION_MODE_MAX], void *ll_priv_data); @@ -67,6 +69,7 @@ void *keyslot_manager_private(struct keyslot_manager *ksm); void keyslot_manager_destroy(struct keyslot_manager *ksm); struct keyslot_manager *keyslot_manager_create_passthrough( + struct device *dev, const struct keyslot_mgmt_ll_ops *ksm_ops, const unsigned int crypto_mode_supported[BLK_ENCRYPTION_MODE_MAX], void *ll_priv_data); diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 748016ae01e3acdbe44e09fa771d1a533a4c4c3b..0f99ecc01bc7d5128e5c88b97bb4131a0bf0cda7 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -186,7 +186,7 @@ struct kvm_async_pf { struct list_head queue; struct kvm_vcpu *vcpu; struct mm_struct *mm; - gva_t gva; + gpa_t cr2_or_gpa; unsigned long addr; struct kvm_arch_async_pf arch; bool wakeup_all; @@ -194,8 +194,8 @@ struct kvm_async_pf { void kvm_clear_async_pf_completion_queue(struct kvm_vcpu *vcpu); void kvm_check_async_pf_completion(struct kvm_vcpu *vcpu); -int kvm_setup_async_pf(struct kvm_vcpu *vcpu, gva_t gva, unsigned long hva, - struct kvm_arch_async_pf *arch); +int kvm_setup_async_pf(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, + unsigned long hva, struct kvm_arch_async_pf *arch); int kvm_async_pf_wakeup_all(struct kvm_vcpu *vcpu); #endif @@ -704,7 +704,7 @@ int kvm_clear_guest_page(struct kvm *kvm, gfn_t gfn, int offset, int len); int kvm_clear_guest(struct kvm *kvm, gpa_t gpa, unsigned long len); struct kvm_memory_slot *gfn_to_memslot(struct kvm *kvm, gfn_t gfn); bool kvm_is_visible_gfn(struct kvm *kvm, gfn_t gfn); -unsigned long kvm_host_page_size(struct kvm *kvm, gfn_t gfn); +unsigned long kvm_host_page_size(struct kvm_vcpu *vcpu, gfn_t gfn); void mark_page_dirty(struct kvm *kvm, gfn_t gfn); struct kvm_memslots *kvm_vcpu_memslots(struct kvm_vcpu *vcpu); diff --git a/include/linux/libata.h b/include/linux/libata.h index aff09d0b3545f03dfd22946af7ffda81508fa897..75a916d7ab2a4e0ecd24f737df89a2045fbe7170 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -1236,6 +1236,7 @@ struct pci_bits { }; extern int pci_test_config_bits(struct pci_dev *pdev, const struct pci_bits *bits); +extern void ata_pci_shutdown_one(struct pci_dev *pdev); extern void ata_pci_remove_one(struct pci_dev *pdev); #ifdef CONFIG_PM diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h index 097072c5a852f22e7aab76750b4bb4de709a7bc3..b0d16e3f15ac7a308f3ef6487f7d9e3fc36d053a 100644 --- a/include/linux/libnvdimm.h +++ b/include/linux/libnvdimm.h @@ -19,6 +19,7 @@ #include #include #include +#include struct badrange_entry { u64 start; @@ -59,6 +60,9 @@ enum { */ ND_REGION_PERSIST_MEMCTRL = 2, + /* Platform provides asynchronous flush mechanism */ + ND_REGION_ASYNC = 3, + /* mark newly adjusted resources as requiring a label update */ DPA_RESOURCE_ADJUSTED = 1 << 0, }; @@ -115,6 +119,7 @@ struct nd_mapping_desc { int position; }; +struct nd_region; struct nd_region_desc { struct resource *res; struct nd_mapping_desc *mapping; @@ -126,6 +131,7 @@ struct nd_region_desc { int numa_node; unsigned long flags; struct device_node *of_node; + int (*flush)(struct nd_region *nd_region, struct bio *bio); }; struct device; @@ -201,7 +207,8 @@ unsigned long nd_blk_memremap_flags(struct nd_blk_region *ndbr); unsigned int nd_region_acquire_lane(struct nd_region *nd_region); void nd_region_release_lane(struct nd_region *nd_region, unsigned int lane); u64 nd_fletcher64(void *addr, size_t len, bool le); -void nvdimm_flush(struct nd_region *nd_region); +int nvdimm_flush(struct nd_region *nd_region, struct bio *bio); +int generic_nvdimm_flush(struct nd_region *nd_region); int nvdimm_has_flush(struct nd_region *nd_region); int nvdimm_has_cache(struct nd_region *nd_region); diff --git a/include/linux/list_nulls.h b/include/linux/list_nulls.h index 3ef96743db8da3868744efc92d812c9b746bf9c7..1ecd35664e0d31e716e79a676ea856698dad6380 100644 --- a/include/linux/list_nulls.h +++ b/include/linux/list_nulls.h @@ -72,10 +72,10 @@ static inline void hlist_nulls_add_head(struct hlist_nulls_node *n, struct hlist_nulls_node *first = h->first; n->next = first; - n->pprev = &h->first; + WRITE_ONCE(n->pprev, &h->first); h->first = n; if (!is_a_nulls(first)) - first->pprev = &n->next; + WRITE_ONCE(first->pprev, &n->next); } static inline void __hlist_nulls_del(struct hlist_nulls_node *n) @@ -85,13 +85,13 @@ static inline void __hlist_nulls_del(struct hlist_nulls_node *n) WRITE_ONCE(*pprev, next); if (!is_a_nulls(next)) - next->pprev = pprev; + WRITE_ONCE(next->pprev, pprev); } static inline void hlist_nulls_del(struct hlist_nulls_node *n) { __hlist_nulls_del(n); - n->pprev = LIST_POISON2; + WRITE_ONCE(n->pprev, LIST_POISON2); } /** diff --git a/include/linux/log2.h b/include/linux/log2.h index 2af7f77866d035fd125f363eb432cddfdacf83bf..01d6ba6d8647f456a420bc7b6b897baf61c6c3ca 100644 --- a/include/linux/log2.h +++ b/include/linux/log2.h @@ -224,4 +224,38 @@ int __order_base_2(unsigned long n) ilog2((n) - 1) + 1) : \ __order_base_2(n) \ ) + +static inline __attribute__((const)) +int __bits_per(unsigned long n) +{ + if (n < 2) + return 1; + if (is_power_of_2(n)) + return order_base_2(n) + 1; + return order_base_2(n); +} + +/** + * bits_per - calculate the number of bits required for the argument + * @n: parameter + * + * This is constant-capable and can be used for compile time + * initializations, e.g bitfields. + * + * The first few values calculated by this routine: + * bf(0) = 1 + * bf(1) = 1 + * bf(2) = 2 + * bf(3) = 2 + * bf(4) = 3 + * ... and so on. + */ +#define bits_per(n) \ +( \ + __builtin_constant_p(n) ? ( \ + ((n) == 0 || (n) == 1) \ + ? 1 : ilog2(n) + 1 \ + ) : \ + __bits_per(n) \ +) #endif /* _LINUX_LOG2_H */ diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 0605f866044ac6dcc192c164a9f4d656991a43ad..1a922bd5b739de8be196818b22fe7ec81c582afe 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -1270,7 +1270,7 @@ * @cred contains the credentials to use. * @ns contains the user namespace we want the capability in * @cap contains the capability . - * @audit contains whether to write an audit message or not + * @opts contains options for the capable check * Return 0 if the capability is granted for @tsk. * @syslog: * Check permission before accessing the kernel message ring or changing @@ -1446,8 +1446,10 @@ union security_list_options { const kernel_cap_t *effective, const kernel_cap_t *inheritable, const kernel_cap_t *permitted); - int (*capable)(const struct cred *cred, struct user_namespace *ns, - int cap, int audit); + int (*capable)(const struct cred *cred, + struct user_namespace *ns, + int cap, + unsigned int opts); int (*quotactl)(int cmds, int type, int id, struct super_block *sb); int (*quota_on)(struct dentry *dentry); int (*syslog)(int type); diff --git a/include/linux/memblock.h b/include/linux/memblock.h index 0fa57e9c412a60a73cd5eaaf55759d1c3f1b75ec..7f7f79c207a76322361bf9713e16f7ecd4e1a04a 100644 --- a/include/linux/memblock.h +++ b/include/linux/memblock.h @@ -265,21 +265,6 @@ void __next_mem_pfn_range(int *idx, int nid, unsigned long *out_start_pfn, for_each_mem_range_rev(i, &memblock.memory, &memblock.reserved, \ nid, flags, p_start, p_end, p_nid) -/** - * for_each_resv_unavail_range - iterate through reserved and unavailable memory - * @i: u64 used as loop variable - * @p_start: ptr to phys_addr_t for start address of the range, can be %NULL - * @p_end: ptr to phys_addr_t for end address of the range, can be %NULL - * - * Walks over unavailable but reserved (reserved && !memory) areas of memblock. - * Available as soon as memblock is initialized. - * Note: because this memory does not belong to any physical node, flags and - * nid arguments do not make sense and thus not exported as arguments. - */ -#define for_each_resv_unavail_range(i, p_start, p_end) \ - for_each_mem_range(i, &memblock.reserved, &memblock.memory, \ - NUMA_NO_NODE, MEMBLOCK_NONE, p_start, p_end, NULL) - static inline void memblock_set_region_flags(struct memblock_region *r, enum memblock_flags flags) { diff --git a/include/linux/memory.h b/include/linux/memory.h index a6ddefc60517899167b55b53b0007ba3e3b9ed80..5c411365cdbea08615dfd52da9c8e223bd79b526 100644 --- a/include/linux/memory.h +++ b/include/linux/memory.h @@ -111,16 +111,16 @@ extern int register_memory_notifier(struct notifier_block *nb); extern void unregister_memory_notifier(struct notifier_block *nb); extern int register_memory_isolate_notifier(struct notifier_block *nb); extern void unregister_memory_isolate_notifier(struct notifier_block *nb); -int hotplug_memory_register(int nid, struct mem_section *section); -#ifdef CONFIG_MEMORY_HOTREMOVE -extern int unregister_memory_section(struct mem_section *); -#endif +int create_memory_block_devices(unsigned long start, unsigned long size); +void remove_memory_block_devices(unsigned long start, unsigned long size); extern int memory_dev_init(void); extern int memory_notify(unsigned long val, void *v); extern int memory_isolate_notify(unsigned long val, void *v); extern struct memory_block *find_memory_block_hinted(struct mem_section *, struct memory_block *); extern struct memory_block *find_memory_block(struct mem_section *); +typedef int (*walk_memory_blocks_func_t)(struct memory_block *, void *); +extern int for_each_memory_block(void *arg, walk_memory_blocks_func_t func); #define CONFIG_MEM_BLOCK_SIZE (PAGES_PER_SECTION< M2 -> M0 is possible later) as + * mentioned above. + * -ETIMEDOUT is device faled to move to M0 before @timeout_us elapsed + * -EIO if the MHI state is one of the ERROR states. + */ +int mhi_device_get_sync_atomic(struct mhi_device *mhi_dev, int timeout_us); + /** * mhi_device_put - re-enable low power modes * @mhi_dev: Device associated with the channels diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index 177f11c96187b7ff8f06af56589bfe59060e0eae..76b76b6aa83d0634af2ba7999e1d925683456ab9 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -9053,8 +9053,6 @@ struct mlx5_ifc_query_lag_out_bits { u8 syndrome[0x20]; - u8 reserved_at_40[0x40]; - struct mlx5_ifc_lagc_bits ctx; }; diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h index 4224902a8e22a3d86e5b97577e83d997cb9154f2..358d6be357edaeb223602908e557efb71cbe0cc9 100644 --- a/include/linux/mmc/sdio_ids.h +++ b/include/linux/mmc/sdio_ids.h @@ -68,6 +68,8 @@ #define SDIO_VENDOR_ID_TI 0x0097 #define SDIO_DEVICE_ID_TI_WL1271 0x4076 +#define SDIO_VENDOR_ID_TI_WL1251 0x104c +#define SDIO_DEVICE_ID_TI_WL1251 0x9066 #define SDIO_VENDOR_ID_STE 0x0020 #define SDIO_DEVICE_ID_STE_CW1200 0x2280 diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 664e49e57559613169805bf68a28236fcb95aea8..ad855211d81e08a40c584a153dfd0df1bec0ac77 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -660,8 +660,7 @@ typedef struct pglist_data { #if defined(CONFIG_MEMORY_HOTPLUG) || defined(CONFIG_DEFERRED_STRUCT_PAGE_INIT) /* * Must be held any time you expect node_start_pfn, node_present_pages - * or node_spanned_pages stay constant. Holding this will also - * guarantee that any pfn_valid() stays that way. + * or node_spanned_pages stay constant. * * pgdat_resize_lock() and pgdat_resize_unlock() are provided to * manipulate node_size_lock without checking for CONFIG_MEMORY_HOTPLUG diff --git a/include/linux/ndctl.h b/include/linux/ndctl.h new file mode 100644 index 0000000000000000000000000000000000000000..cd5a293ce3aef7c71145b3d8cb6dc1fca7f9c2a5 --- /dev/null +++ b/include/linux/ndctl.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2014-2016, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for + * more details. + */ +#ifndef _LINUX_NDCTL_H +#define _LINUX_NDCTL_H + +#include + +enum { + ND_MIN_NAMESPACE_SIZE = PAGE_SIZE, +}; + +#endif /* _LINUX_NDCTL_H */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 58b64a19ce6ef99065402c7a3417f6230d3816a4..922fa89f97e4a88b945da8e477eb8d88236e4de2 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -3580,6 +3580,8 @@ int dev_set_alias(struct net_device *, const char *, size_t); int dev_get_alias(const struct net_device *, char *, size_t); int dev_change_net_namespace(struct net_device *, struct net *, const char *); int __dev_set_mtu(struct net_device *, int); +int dev_validate_mtu(struct net_device *dev, int mtu, + struct netlink_ext_ack *extack); int dev_set_mtu_ext(struct net_device *dev, int mtu, struct netlink_ext_ack *extack); int dev_set_mtu(struct net_device *, int); diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index 1d100efe74ec76861084a4272327b662cf1de478..7e39049d2ce6161e28c6e7841739361c604a43a5 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -451,13 +451,6 @@ ip6addrptr(const struct sk_buff *skb, bool src, struct in6_addr *addr) sizeof(*addr)); } -/* Calculate the bytes required to store the inclusive range of a-b */ -static inline int -bitmap_bytes(u32 a, u32 b) -{ - return 4 * ((((b - a + 8) / 8) + 3) / 4); -} - #include #include #include diff --git a/include/linux/node.h b/include/linux/node.h index 257bb3d6d0143740188f93c1cd0f9c58eb4697e6..708939bae9aa89d665a236e6aeb1974ec3c104cb 100644 --- a/include/linux/node.h +++ b/include/linux/node.h @@ -72,8 +72,7 @@ extern int register_cpu_under_node(unsigned int cpu, unsigned int nid); extern int unregister_cpu_under_node(unsigned int cpu, unsigned int nid); extern int register_mem_sect_under_node(struct memory_block *mem_blk, void *arg); -extern int unregister_mem_sect_under_nodes(struct memory_block *mem_blk, - unsigned long phys_index); +extern void unregister_memory_block_under_nodes(struct memory_block *mem_blk); #ifdef CONFIG_HUGETLBFS extern void register_hugetlbfs_with_node(node_registration_func_t doregister, @@ -105,10 +104,8 @@ static inline int register_mem_sect_under_node(struct memory_block *mem_blk, { return 0; } -static inline int unregister_mem_sect_under_nodes(struct memory_block *mem_blk, - unsigned long phys_index) +static inline void unregister_memory_block_under_nodes(struct memory_block *mem_blk) { - return 0; } static inline void register_hugetlbfs_with_node(node_registration_func_t reg, diff --git a/include/linux/of.h b/include/linux/of.h index d5a863c1ee390f6482669a1adc7bb4627bfe934d..d4f14b0302b634b7ba4df2a501a7d55a66e85429 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -1001,7 +1001,7 @@ static inline struct device_node *of_find_matching_node( static inline const char *of_node_get_device_type(const struct device_node *np) { - return of_get_property(np, "type", NULL); + return of_get_property(np, "device_type", NULL); } static inline bool of_node_is_type(const struct device_node *np, const char *type) @@ -1425,7 +1425,8 @@ int of_overlay_notifier_unregister(struct notifier_block *nb); #else -static inline int of_overlay_fdt_apply(void *overlay_fdt, int *ovcs_id) +static inline int of_overlay_fdt_apply(void *overlay_fdt, u32 overlay_fdt_size, + int *ovcs_id) { return -ENOTSUPP; } diff --git a/include/linux/percpu-defs.h b/include/linux/percpu-defs.h index 2d2096ba1cfeca2672b8c368cb49975985d67363..90b8ce813fa6cb495193544ec9017b98de6ffe52 100644 --- a/include/linux/percpu-defs.h +++ b/include/linux/percpu-defs.h @@ -176,8 +176,7 @@ * Declaration/definition used for per-CPU variables that should be accessed * as decrypted when memory encryption is enabled in the guest. */ -#if defined(CONFIG_VIRTUALIZATION) && defined(CONFIG_AMD_MEM_ENCRYPT) - +#ifdef CONFIG_AMD_MEM_ENCRYPT #define DECLARE_PER_CPU_DECRYPTED(type, name) \ DECLARE_PER_CPU_SECTION(type, name, "..decrypted") diff --git a/include/linux/platform_data/dma-imx-sdma.h b/include/linux/platform_data/dma-imx-sdma.h index 6eaa53cef0bd228b8a0d7bb2b3938ff04c548f95..30e676b36b247a595c39e98d7c59bdaa7e6eb233 100644 --- a/include/linux/platform_data/dma-imx-sdma.h +++ b/include/linux/platform_data/dma-imx-sdma.h @@ -51,7 +51,10 @@ struct sdma_script_start_addrs { /* End of v2 array */ s32 zcanfd_2_mcu_addr; s32 zqspi_2_mcu_addr; + s32 mcu_2_ecspi_addr; /* End of v3 array */ + s32 mcu_2_zqspi_addr; + /* End of v4 array */ }; /** diff --git a/include/linux/pm_wakeup.h b/include/linux/pm_wakeup.h index 4238dde0aaf033d62fe7958d6e1387045f721dfd..6e6a25220c565076b152c4664e25a3ae79cffa7c 100644 --- a/include/linux/pm_wakeup.h +++ b/include/linux/pm_wakeup.h @@ -34,6 +34,7 @@ struct wake_irq; * struct wakeup_source - Representation of wakeup sources * * @name: Name of the wakeup source + * @id: Wakeup source id * @entry: Wakeup source list entry * @lock: Wakeup source lock * @wakeirq: Optional device specific wakeirq @@ -48,11 +49,13 @@ struct wake_irq; * @relax_count: Number of times the wakeup source was deactivated. * @expire_count: Number of times the wakeup source's timeout has expired. * @wakeup_count: Number of times the wakeup source might abort suspend. + * @dev: Struct device for sysfs statistics about the wakeup source. * @active: Status of the wakeup source. * @has_timeout: The wakeup source has been activated with a timeout. */ struct wakeup_source { const char *name; + int id; struct list_head entry; spinlock_t lock; struct wake_irq *wakeirq; @@ -68,10 +71,13 @@ struct wakeup_source { unsigned long relax_count; unsigned long expire_count; unsigned long wakeup_count; + struct device *dev; bool active:1; bool autosleep_enabled:1; }; +#define WAKEUP_SOURCE_DEV + #ifdef CONFIG_PM_SLEEP /* @@ -94,13 +100,12 @@ static inline void device_set_wakeup_path(struct device *dev) } /* drivers/base/power/wakeup.c */ -extern void wakeup_source_prepare(struct wakeup_source *ws, const char *name); extern struct wakeup_source *wakeup_source_create(const char *name); -extern void wakeup_source_drop(struct wakeup_source *ws); extern void wakeup_source_destroy(struct wakeup_source *ws); extern void wakeup_source_add(struct wakeup_source *ws); extern void wakeup_source_remove(struct wakeup_source *ws); -extern struct wakeup_source *wakeup_source_register(const char *name); +extern struct wakeup_source *wakeup_source_register(struct device *dev, + const char *name); extern void wakeup_source_unregister(struct wakeup_source *ws); extern int device_wakeup_enable(struct device *dev); extern int device_wakeup_disable(struct device *dev); @@ -126,23 +131,19 @@ static inline bool device_can_wakeup(struct device *dev) return dev->power.can_wakeup; } -static inline void wakeup_source_prepare(struct wakeup_source *ws, - const char *name) {} - static inline struct wakeup_source *wakeup_source_create(const char *name) { return NULL; } -static inline void wakeup_source_drop(struct wakeup_source *ws) {} - static inline void wakeup_source_destroy(struct wakeup_source *ws) {} static inline void wakeup_source_add(struct wakeup_source *ws) {} static inline void wakeup_source_remove(struct wakeup_source *ws) {} -static inline struct wakeup_source *wakeup_source_register(const char *name) +static inline struct wakeup_source *wakeup_source_register(struct device *dev, + const char *name) { return NULL; } @@ -197,19 +198,6 @@ static inline void pm_wakeup_dev_event(struct device *dev, unsigned int msec, #endif /* !CONFIG_PM_SLEEP */ -static inline void wakeup_source_init(struct wakeup_source *ws, - const char *name) -{ - wakeup_source_prepare(ws, name); - wakeup_source_add(ws); -} - -static inline void wakeup_source_trash(struct wakeup_source *ws) -{ - wakeup_source_remove(ws); - wakeup_source_drop(ws); -} - static inline void __pm_wakeup_event(struct wakeup_source *ws, unsigned int msec) { return pm_wakeup_ws_event(ws, msec, false); diff --git a/include/linux/poll.h b/include/linux/poll.h index 7e0fdcf905d2e77b355c94a7381446927452723c..1cdc32b1f1b0836e6dccf2ecf446b9aeab0a950b 100644 --- a/include/linux/poll.h +++ b/include/linux/poll.h @@ -16,7 +16,11 @@ extern struct ctl_table epoll_table[]; /* for sysctl */ /* ~832 bytes of stack space used max in sys_select/sys_poll before allocating additional memory. */ +#ifdef __clang__ +#define MAX_STACK_ALLOC 768 +#else #define MAX_STACK_ALLOC 832 +#endif #define FRONTEND_STACK_ALLOC 256 #define SELECT_STACK_ALLOC FRONTEND_STACK_ALLOC #define POLL_STACK_ALLOC FRONTEND_STACK_ALLOC diff --git a/include/linux/power/smartreflex.h b/include/linux/power/smartreflex.h index 7b81dad712de86512b0ab95f7a69b90ffaa12390..37d9b70ed8f0a20c85645fb371dfce092cdc343b 100644 --- a/include/linux/power/smartreflex.h +++ b/include/linux/power/smartreflex.h @@ -296,6 +296,9 @@ struct omap_sr_data { struct voltagedomain *voltdm; }; + +extern struct omap_sr_data omap_sr_pdata[OMAP_SR_NR]; + #ifdef CONFIG_POWER_AVS_OMAP /* Smartreflex module enable/disable interface */ diff --git a/include/linux/property.h b/include/linux/property.h index 1a12364050d8bf0c3fafa195af4e6c904ce471e3..df388bec57ed36053c58610c99a81566df7f556e 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -143,6 +143,26 @@ static inline int device_property_read_u64(struct device *dev, return device_property_read_u64_array(dev, propname, val, 1); } +static inline int device_property_count_u8(struct device *dev, const char *propname) +{ + return device_property_read_u8_array(dev, propname, NULL, 0); +} + +static inline int device_property_count_u16(struct device *dev, const char *propname) +{ + return device_property_read_u16_array(dev, propname, NULL, 0); +} + +static inline int device_property_count_u32(struct device *dev, const char *propname) +{ + return device_property_read_u32_array(dev, propname, NULL, 0); +} + +static inline int device_property_count_u64(struct device *dev, const char *propname) +{ + return device_property_read_u64_array(dev, propname, NULL, 0); +} + static inline bool fwnode_property_read_bool(const struct fwnode_handle *fwnode, const char *propname) { @@ -173,6 +193,30 @@ static inline int fwnode_property_read_u64(const struct fwnode_handle *fwnode, return fwnode_property_read_u64_array(fwnode, propname, val, 1); } +static inline int fwnode_property_count_u8(const struct fwnode_handle *fwnode, + const char *propname) +{ + return fwnode_property_read_u8_array(fwnode, propname, NULL, 0); +} + +static inline int fwnode_property_count_u16(const struct fwnode_handle *fwnode, + const char *propname) +{ + return fwnode_property_read_u16_array(fwnode, propname, NULL, 0); +} + +static inline int fwnode_property_count_u32(const struct fwnode_handle *fwnode, + const char *propname) +{ + return fwnode_property_read_u32_array(fwnode, propname, NULL, 0); +} + +static inline int fwnode_property_count_u64(const struct fwnode_handle *fwnode, + const char *propname) +{ + return fwnode_property_read_u64_array(fwnode, propname, NULL, 0); +} + /** * struct property_entry - "Built-in" device property representation. * @name: Name of the property. diff --git a/include/linux/random.h b/include/linux/random.h index 445a0ea4ff49a1de4edaf8e60ab82e2e36aa5080..012cfeeaa1527d28b8c0fa1ca02453c258dfec5f 100644 --- a/include/linux/random.h +++ b/include/linux/random.h @@ -19,6 +19,7 @@ struct random_ready_callback { }; extern void add_device_randomness(const void *, unsigned int); +extern void add_bootloader_randomness(const void *, unsigned int); #if defined(CONFIG_GCC_PLUGIN_LATENT_ENTROPY) && !defined(__CHECKER__) static inline void add_latent_entropy(void) @@ -36,6 +37,7 @@ extern void add_interrupt_randomness(int irq, int irq_flags) __latent_entropy; extern void get_random_bytes(void *buf, int nbytes); extern int wait_for_random_bytes(void); +extern int __init rand_initialize(void); extern bool rng_is_initialized(void); extern int add_random_ready_callback(struct random_ready_callback *rdy); extern void del_random_ready_callback(struct random_ready_callback *rdy); @@ -165,29 +167,21 @@ static inline void prandom_seed_state(struct rnd_state *state, u64 seed) #ifdef CONFIG_ARCH_RANDOM # include #else -static inline bool arch_get_random_long(unsigned long *v) +static inline bool __must_check arch_get_random_long(unsigned long *v) { - return 0; + return false; } -static inline bool arch_get_random_int(unsigned int *v) +static inline bool __must_check arch_get_random_int(unsigned int *v) { - return 0; + return false; } -static inline bool arch_has_random(void) +static inline bool __must_check arch_get_random_seed_long(unsigned long *v) { - return 0; + return false; } -static inline bool arch_get_random_seed_long(unsigned long *v) +static inline bool __must_check arch_get_random_seed_int(unsigned int *v) { - return 0; -} -static inline bool arch_get_random_seed_int(unsigned int *v) -{ - return 0; -} -static inline bool arch_has_random_seed(void) -{ - return 0; + return false; } #endif diff --git a/include/linux/rculist_nulls.h b/include/linux/rculist_nulls.h index 61974c4c566be44af05531ce5b53040d586153a0..90f2e2232c6d7cd256acc4daa3ec251bae8499d3 100644 --- a/include/linux/rculist_nulls.h +++ b/include/linux/rculist_nulls.h @@ -34,7 +34,7 @@ static inline void hlist_nulls_del_init_rcu(struct hlist_nulls_node *n) { if (!hlist_nulls_unhashed(n)) { __hlist_nulls_del(n); - n->pprev = NULL; + WRITE_ONCE(n->pprev, NULL); } } @@ -66,7 +66,7 @@ static inline void hlist_nulls_del_init_rcu(struct hlist_nulls_node *n) static inline void hlist_nulls_del_rcu(struct hlist_nulls_node *n) { __hlist_nulls_del(n); - n->pprev = LIST_POISON2; + WRITE_ONCE(n->pprev, LIST_POISON2); } /** @@ -94,10 +94,10 @@ static inline void hlist_nulls_add_head_rcu(struct hlist_nulls_node *n, struct hlist_nulls_node *first = h->first; n->next = first; - n->pprev = &h->first; + WRITE_ONCE(n->pprev, &h->first); rcu_assign_pointer(hlist_nulls_first_rcu(h), n); if (!is_a_nulls(first)) - first->pprev = &n->next; + WRITE_ONCE(first->pprev, &n->next); } /** diff --git a/include/linux/regulator/ab8500.h b/include/linux/regulator/ab8500.h index 06978ce01302793945e8e21a3676fcbc71b8f653..d48ab3e66d0bf031238fdd1837d04a9aac48ea17 100644 --- a/include/linux/regulator/ab8500.h +++ b/include/linux/regulator/ab8500.h @@ -43,8 +43,6 @@ enum ab8505_regulator_id { AB8505_LDO_ANAMIC2, AB8505_LDO_AUX8, AB8505_LDO_ANA, - AB8505_SYSCLKREQ_2, - AB8505_SYSCLKREQ_4, AB8505_NUM_REGULATORS, }; diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index f3f76051e8b008346a0e7acd4b6512d7ecef85f3..775e63ec1bc7650d47378a42fcc00fdd185895e0 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -244,6 +244,7 @@ void regulator_bulk_free(int num_consumers, int regulator_count_voltages(struct regulator *regulator); int regulator_list_voltage(struct regulator *regulator, unsigned selector); +int regulator_list_corner_voltage(struct regulator *regulator, int corner); int regulator_is_supported_voltage(struct regulator *regulator, int min_uV, int max_uV); unsigned int regulator_get_linear_step(struct regulator *regulator); @@ -579,6 +580,11 @@ static inline int regulator_list_voltage(struct regulator *regulator, unsigned s return -EINVAL; } +static inline int regulator_list_corner_voltage(struct regulator *regulator, + int corner) +{ + return -EINVAL; +} #endif static inline int regulator_set_voltage_triplet(struct regulator *regulator, diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 71756e6c6ae2a8bf6da59111be02cb011ef05fb2..7ae7dc370ca960bab96e36f56609ccd9ef6926fb 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -94,6 +94,10 @@ struct regulator_linear_range { * if the selector indicates a voltage that is unusable on this system; * or negative errno. Selectors range from zero to one less than * regulator_desc.n_voltages. Voltages may be reported in any order. + * @list_corner_voltage: Return the maximum voltage in microvolts that + * that can be physically configured for the regulator when operating at + * the specified voltage corner or a negative errno if the corner value + * can't be used on this system. * * @set_current_limit: Configure a limit for a current-limited regulator. * The driver should select the current closest to max_uA. @@ -150,6 +154,7 @@ struct regulator_ops { /* enumerate supported voltages */ int (*list_voltage) (struct regulator_dev *, unsigned selector); + int (*list_corner_voltage)(struct regulator_dev *list_reg, int corner); /* get/set regulator voltage */ int (*set_voltage) (struct regulator_dev *, int min_uV, int max_uV, diff --git a/include/linux/regulator/spm-regulator.h b/include/linux/regulator/spm-regulator.h new file mode 100644 index 0000000000000000000000000000000000000000..c1eaee6d90d4d056d55f672e4747d5ba2e228ff3 --- /dev/null +++ b/include/linux/regulator/spm-regulator.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2013-2014, 2020, The Linux Foundation. All rights reserved. + */ + +#ifndef _LINUX_REGULATOR_SPM_H +#define _LINUX_REGULATOR_SPM_H + +#include +#include + +#ifdef CONFIG_REGULATOR_SPM +int __init spm_regulator_init(void); +#else +static inline int __init spm_regulator_init(void) { return -ENODEV; } +#endif + +#endif diff --git a/include/linux/rtc.h b/include/linux/rtc.h index 6aedc30003e731576ae87857666b2abe5b510187..5a34f59941fb798b4cd096c79d47439ab6dea4cf 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -163,7 +163,7 @@ struct rtc_device { #define to_rtc_device(d) container_of(d, struct rtc_device, dev) /* useful timestamps */ -#define RTC_TIMESTAMP_BEGIN_1900 -2208989361LL /* 1900-01-01 00:00:00 */ +#define RTC_TIMESTAMP_BEGIN_1900 -2208988800LL /* 1900-01-01 00:00:00 */ #define RTC_TIMESTAMP_BEGIN_2000 946684800LL /* 2000-01-01 00:00:00 */ #define RTC_TIMESTAMP_END_2099 4102444799LL /* 2099-12-31 23:59:59 */ diff --git a/include/linux/sched.h b/include/linux/sched.h index b73670709c8d63cb6e47966f01d467cf41c5902e..8de5bf6702945164e0efbab5a878e2def162cd9a 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -351,6 +351,18 @@ struct vtime { u64 gtime; }; +/* + * Utilization clamp constraints. + * @UCLAMP_MIN: Minimum utilization + * @UCLAMP_MAX: Maximum utilization + * @UCLAMP_CNT: Utilization clamp constraints count + */ +enum uclamp_id { + UCLAMP_MIN = 0, + UCLAMP_MAX, + UCLAMP_CNT +}; + struct sched_info { #ifdef CONFIG_SCHED_INFO /* Cumulative counters: */ @@ -382,6 +394,10 @@ struct sched_info { # define SCHED_FIXEDPOINT_SHIFT 10 # define SCHED_FIXEDPOINT_SCALE (1L << SCHED_FIXEDPOINT_SHIFT) +/* Increase resolution of cpu_capacity calculations */ +# define SCHED_CAPACITY_SHIFT SCHED_FIXEDPOINT_SHIFT +# define SCHED_CAPACITY_SCALE (1L << SCHED_CAPACITY_SHIFT) + struct load_weight { unsigned long weight; u32 inv_weight; @@ -730,6 +746,41 @@ struct sched_dl_entity { struct hrtimer inactive_timer; }; +#ifdef CONFIG_UCLAMP_TASK +/* Number of utilization clamp buckets (shorter alias) */ +#define UCLAMP_BUCKETS CONFIG_UCLAMP_BUCKETS_COUNT + +/* + * Utilization clamp for a scheduling entity + * @value: clamp value "assigned" to a se + * @bucket_id: bucket index corresponding to the "assigned" value + * @active: the se is currently refcounted in a rq's bucket + * @user_defined: the requested clamp value comes from user-space + * + * The bucket_id is the index of the clamp bucket matching the clamp value + * which is pre-computed and stored to avoid expensive integer divisions from + * the fast path. + * + * The active bit is set whenever a task has got an "effective" value assigned, + * which can be different from the clamp value "requested" from user-space. + * This allows to know a task is refcounted in the rq's bucket corresponding + * to the "effective" bucket_id. + * + * The user_defined bit is set whenever a task has got a task-specific clamp + * value requested from userspace, i.e. the system defaults apply to this task + * just as a restriction. This allows to relax default clamps when a less + * restrictive task-specific value has been requested, thus allowing to + * implement a "nice" semantic. For example, a task running with a 20% + * default boost can still drop its own boosting to 0%. + */ +struct uclamp_se { + unsigned int value : bits_per(SCHED_CAPACITY_SCALE); + unsigned int bucket_id : bits_per(UCLAMP_BUCKETS); + unsigned int active : 1; + unsigned int user_defined : 1; +}; +#endif /* CONFIG_UCLAMP_TASK */ + union rcu_special { struct { u8 blocked; @@ -833,6 +884,13 @@ struct task_struct { #endif struct sched_dl_entity dl; +#ifdef CONFIG_UCLAMP_TASK + /* Clamp values requested for a scheduling entity */ + struct uclamp_se uclamp_req[UCLAMP_CNT]; + /* Effective clamp values used for a scheduling entity */ + struct uclamp_se uclamp[UCLAMP_CNT]; +#endif + #ifdef CONFIG_PREEMPT_NOTIFIERS /* List of struct preempt_notifier: */ struct hlist_head preempt_notifiers; @@ -1345,6 +1403,8 @@ struct task_struct { #endif /* CONFIG_TRACING */ #ifdef CONFIG_KCOV + /* See kernel/kcov.c for more details. */ + /* Coverage collection mode enabled for this task (0 if disabled): */ unsigned int kcov_mode; @@ -1356,6 +1416,12 @@ struct task_struct { /* KCOV descriptor wired with this task or NULL: */ struct kcov *kcov; + + /* KCOV common handle for remote coverage collection: */ + u64 kcov_handle; + + /* KCOV sequence number: */ + int kcov_sequence; #endif #ifdef CONFIG_MEMCG @@ -2052,11 +2118,11 @@ static inline void rseq_migrate(struct task_struct *t) /* * If parent process has a registered restartable sequences area, the - * child inherits. Only applies when forking a process, not a thread. + * child inherits. Unregister rseq for a clone with CLONE_VM set. */ static inline void rseq_fork(struct task_struct *t, unsigned long clone_flags) { - if (clone_flags & CLONE_THREAD) { + if (clone_flags & CLONE_VM) { t->rseq = NULL; t->rseq_len = 0; t->rseq_sig = 0; diff --git a/include/linux/sched/sysctl.h b/include/linux/sched/sysctl.h index 1c4c5b6c1a5f52331205ed2b97e666f7c078bd88..f77efc13cf26ac18cd1b28348be2ca3cbfbb162b 100644 --- a/include/linux/sched/sysctl.h +++ b/include/linux/sched/sysctl.h @@ -117,6 +117,11 @@ extern int sched_boost_handler(struct ctl_table *table, int write, extern unsigned int sysctl_sched_rt_period; extern int sysctl_sched_rt_runtime; +#ifdef CONFIG_UCLAMP_TASK +extern unsigned int sysctl_sched_uclamp_util_min; +extern unsigned int sysctl_sched_uclamp_util_max; +#endif + #ifdef CONFIG_CFS_BANDWIDTH extern unsigned int sysctl_sched_cfs_bandwidth_slice; #endif @@ -136,6 +141,12 @@ extern int sched_rt_handler(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); +#ifdef CONFIG_UCLAMP_TASK +extern int sysctl_sched_uclamp_handler(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, + loff_t *ppos); +#endif + extern int sysctl_numa_balancing(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); @@ -148,4 +159,11 @@ extern char sched_lib_name[LIB_PATH_LENGTH]; extern unsigned int sched_lib_mask_force; extern bool is_sched_lib_based_app(pid_t pid); +#if defined(CONFIG_ENERGY_MODEL) && defined(CONFIG_CPU_FREQ_GOV_SCHEDUTIL) +extern unsigned int sysctl_sched_energy_aware; +extern int sched_energy_aware_handler(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, + loff_t *ppos); +#endif + #endif /* _LINUX_SCHED_SYSCTL_H */ diff --git a/include/linux/sched/topology.h b/include/linux/sched/topology.h index 6119139515ac97047814db5b025fb501dcb41ce2..ca3bec74a3c59a3fd4f2667230a803311a259b5b 100644 --- a/include/linux/sched/topology.h +++ b/include/linux/sched/topology.h @@ -6,12 +6,6 @@ #include -/* - * Increase resolution of cpu_capacity calculations - */ -#define SCHED_CAPACITY_SHIFT SCHED_FIXEDPOINT_SHIFT -#define SCHED_CAPACITY_SCALE (1L << SCHED_CAPACITY_SHIFT) - /* * sched-domains (multiprocessor balancing) declarations: */ diff --git a/include/linux/security.h b/include/linux/security.h index 9fa7661125ab54495ca2ce4cb58f9f5a06b59cc1..71ad1841c59d53f4589adc04c5b2c7dd7eccd8c1 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -54,9 +54,12 @@ struct xattr; struct xfrm_sec_ctx; struct mm_struct; +/* Default (no) options for the capable function */ +#define CAP_OPT_NONE 0x0 /* If capable should audit the security request */ -#define SECURITY_CAP_NOAUDIT 0 -#define SECURITY_CAP_AUDIT 1 +#define CAP_OPT_NOAUDIT BIT(1) +/* If capable is being called by a setid function */ +#define CAP_OPT_INSETID BIT(2) /* LSM Agnostic defines for sb_set_mnt_opts */ #define SECURITY_LSM_NATIVE_LABELS 1 @@ -72,7 +75,7 @@ enum lsm_event { /* These functions are in security/commoncap.c */ extern int cap_capable(const struct cred *cred, struct user_namespace *ns, - int cap, int audit); + int cap, unsigned int opts); extern int cap_settime(const struct timespec64 *ts, const struct timezone *tz); extern int cap_ptrace_access_check(struct task_struct *child, unsigned int mode); extern int cap_ptrace_traceme(struct task_struct *parent); @@ -233,10 +236,10 @@ int security_capset(struct cred *new, const struct cred *old, const kernel_cap_t *effective, const kernel_cap_t *inheritable, const kernel_cap_t *permitted); -int security_capable(const struct cred *cred, struct user_namespace *ns, - int cap); -int security_capable_noaudit(const struct cred *cred, struct user_namespace *ns, - int cap); +int security_capable(const struct cred *cred, + struct user_namespace *ns, + int cap, + unsigned int opts); int security_quotactl(int cmds, int type, int id, struct super_block *sb); int security_quota_on(struct dentry *dentry); int security_syslog(int type); @@ -492,14 +495,11 @@ static inline int security_capset(struct cred *new, } static inline int security_capable(const struct cred *cred, - struct user_namespace *ns, int cap) + struct user_namespace *ns, + int cap, + unsigned int opts) { - return cap_capable(cred, ns, cap, SECURITY_CAP_AUDIT); -} - -static inline int security_capable_noaudit(const struct cred *cred, - struct user_namespace *ns, int cap) { - return cap_capable(cred, ns, cap, SECURITY_CAP_NOAUDIT); + return cap_capable(cred, ns, cap, opts); } static inline int security_quotactl(int cmds, int type, int id, diff --git a/include/linux/serdev.h b/include/linux/serdev.h index f153b2c7f0cd65610906f15c083643fba54e527f..df6a853650584b00d3ce18a5b074e893e6b0c232 100644 --- a/include/linux/serdev.h +++ b/include/linux/serdev.h @@ -173,9 +173,21 @@ int serdev_device_add(struct serdev_device *); void serdev_device_remove(struct serdev_device *); struct serdev_controller *serdev_controller_alloc(struct device *, size_t); -int serdev_controller_add(struct serdev_controller *); +int serdev_controller_add_platform(struct serdev_controller *, bool); void serdev_controller_remove(struct serdev_controller *); +/** + * serdev_controller_add() - Add an serdev controller + * @ctrl: controller to be registered. + * + * Register a controller previously allocated via serdev_controller_alloc() with + * the serdev core. + */ +static inline int serdev_controller_add(struct serdev_controller *ctrl) +{ + return serdev_controller_add_platform(ctrl, false); +} + static inline void serdev_controller_write_wakeup(struct serdev_controller *ctrl) { struct serdev_device *serdev = ctrl->serdev; diff --git a/include/linux/signal.h b/include/linux/signal.h index e4d01469ed60c736926674d944e8570f81409867..0be5ce2375cb913c91cccbdd6111f5cd54619d6f 100644 --- a/include/linux/signal.h +++ b/include/linux/signal.h @@ -272,6 +272,9 @@ extern void signal_setup_done(int failed, struct ksignal *ksig, int stepping); extern void exit_signals(struct task_struct *tsk); extern void kernel_sigaction(int, __sighandler_t); +#define SIG_KTHREAD ((__force __sighandler_t)2) +#define SIG_KTHREAD_KERNEL ((__force __sighandler_t)3) + static inline void allow_signal(int sig) { /* @@ -279,7 +282,17 @@ static inline void allow_signal(int sig) * know it'll be handled, so that they don't get converted to * SIGKILL or just silently dropped. */ - kernel_sigaction(sig, (__force __sighandler_t)2); + kernel_sigaction(sig, SIG_KTHREAD); +} + +static inline void allow_kernel_signal(int sig) +{ + /* + * Kernel threads handle their own signals. Let the signal code + * know signals sent by the kernel will be handled, so that they + * don't get silently dropped. + */ + kernel_sigaction(sig, SIG_KTHREAD_KERNEL); } static inline void disallow_signal(int sig) diff --git a/include/linux/stat.h b/include/linux/stat.h index 765573dc17d659d36bf8418d1ba19e66c0c843a4..528c4baad09146aa815bdfe529dfd41978e3fe44 100644 --- a/include/linux/stat.h +++ b/include/linux/stat.h @@ -33,7 +33,8 @@ struct kstat { STATX_ATTR_IMMUTABLE | \ STATX_ATTR_APPEND | \ STATX_ATTR_NODUMP | \ - STATX_ATTR_ENCRYPTED \ + STATX_ATTR_ENCRYPTED | \ + STATX_ATTR_VERITY \ )/* Attrs corresponding to FS_*_FL flags */ u64 ino; dev_t dev; diff --git a/include/linux/switchtec.h b/include/linux/switchtec.h index ab400af6f0ce369ce073b7c75d05fbd64e355cef..623719c9170613c96c7930f69f6b74e7879f9abf 100644 --- a/include/linux/switchtec.h +++ b/include/linux/switchtec.h @@ -244,8 +244,8 @@ struct ntb_ctrl_regs { u64 xlate_addr; } bar_entry[6]; u32 reserved2[216]; - u32 req_id_table[256]; - u32 reserved3[512]; + u32 req_id_table[512]; + u32 reserved3[256]; u64 lut_entry[512]; } __packed; diff --git a/include/linux/tnum.h b/include/linux/tnum.h index c7dc2b5902c057ee0475f786f576fd921b3a8014..06b9c20cc77ee03fe32dbc2dfc460d1100a9acd3 100644 --- a/include/linux/tnum.h +++ b/include/linux/tnum.h @@ -26,7 +26,7 @@ struct tnum tnum_lshift(struct tnum a, u8 shift); /* Shift (rsh) a tnum right (by a fixed shift) */ struct tnum tnum_rshift(struct tnum a, u8 shift); /* Shift (arsh) a tnum right (by a fixed min_shift) */ -struct tnum tnum_arshift(struct tnum a, u8 min_shift); +struct tnum tnum_arshift(struct tnum a, u8 min_shift, u8 insn_bitness); /* Add two tnums, return @a + @b */ struct tnum tnum_add(struct tnum a, struct tnum b); /* Subtract two tnums, return @a - @b */ diff --git a/include/linux/tty.h b/include/linux/tty.h index 761982ea1d9c255003221690b0d450827b4b6f36..4f6a57b78a0a4da9431e7c46b8ca63f6c98b1ac8 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -225,6 +225,8 @@ struct tty_port_client_operations { void (*write_wakeup)(struct tty_port *port); }; +extern const struct tty_port_client_operations tty_port_default_client_ops; + struct tty_port { struct tty_bufhead buf; /* Locked internally */ struct tty_struct *tty; /* Back pointer */ diff --git a/include/linux/unicode.h b/include/linux/unicode.h index 990aa97d804962e65ed00e354ba6dd88e14f2bcd..74484d44c7554bdfea1f86a5cebe89809d1bd8e1 100644 --- a/include/linux/unicode.h +++ b/include/linux/unicode.h @@ -27,6 +27,9 @@ int utf8_normalize(const struct unicode_map *um, const struct qstr *str, int utf8_casefold(const struct unicode_map *um, const struct qstr *str, unsigned char *dest, size_t dlen); +int utf8_casefold_hash(const struct unicode_map *um, const void *salt, + struct qstr *str); + struct unicode_map *utf8_load(const char *version); void utf8_unload(struct unicode_map *um); diff --git a/include/linux/usb/irda.h b/include/linux/usb/irda.h index 396d2b043e6476fe98c578eff331a36aa4d8ba61..556a801efce30163fca9588a6f6a2d4d9d17d8fe 100644 --- a/include/linux/usb/irda.h +++ b/include/linux/usb/irda.h @@ -119,11 +119,22 @@ struct usb_irda_cs_descriptor { * 6 - 115200 bps * 7 - 576000 bps * 8 - 1.152 Mbps - * 9 - 5 mbps + * 9 - 4 Mbps * 10..15 - Reserved */ #define USB_IRDA_STATUS_LINK_SPEED 0x0f +#define USB_IRDA_LS_NO_CHANGE 0 +#define USB_IRDA_LS_2400 1 +#define USB_IRDA_LS_9600 2 +#define USB_IRDA_LS_19200 3 +#define USB_IRDA_LS_38400 4 +#define USB_IRDA_LS_57600 5 +#define USB_IRDA_LS_115200 6 +#define USB_IRDA_LS_576000 7 +#define USB_IRDA_LS_1152000 8 +#define USB_IRDA_LS_4000000 9 + /* The following is a 4-bit value used only for * outbound header: * diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h index a1be64c9940fb4ad4e2365f226419f050d6addba..22c1f579afe3020b22ed36a40523ab34c08a3292 100644 --- a/include/linux/usb/quirks.h +++ b/include/linux/usb/quirks.h @@ -69,4 +69,7 @@ /* Hub needs extra delay after resetting its port. */ #define USB_QUIRK_HUB_SLOW_RESET BIT(14) +/* device has blacklisted endpoints */ +#define USB_QUIRK_ENDPOINT_BLACKLIST BIT(15) + #endif /* __LINUX_USB_QUIRKS_H */ diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h index 50c74a77db55c435af06e83833ca1271c263d6fd..8cb93aff69f1e9b0bca8dadeede932af6ebb38bc 100644 --- a/include/linux/usb/tcpm.h +++ b/include/linux/usb/tcpm.h @@ -121,10 +121,10 @@ struct tcpc_config { * with partner. * @set_pd_rx: Called to enable or disable reception of PD messages * @set_roles: Called to set power and data roles - * @start_drp_toggling: - * Optional; if supported by hardware, called to start DRP - * toggling. DRP toggling is stopped automatically if - * a connection is established. + * @start_toggling: + * Optional; if supported by hardware, called to start dual-role + * toggling or single-role connection detection. Toggling stops + * automatically if a connection is established. * @try_role: Optional; called to set a preferred role * @pd_transmit:Called to transmit PD message * @mux: Pointer to multiplexer data @@ -147,8 +147,9 @@ struct tcpc_dev { int (*set_pd_rx)(struct tcpc_dev *dev, bool on); int (*set_roles)(struct tcpc_dev *dev, bool attached, enum typec_role role, enum typec_data_role data); - int (*start_drp_toggling)(struct tcpc_dev *dev, - enum typec_cc_status cc); + int (*start_toggling)(struct tcpc_dev *dev, + enum typec_port_type port_type, + enum typec_cc_status cc); int (*try_role)(struct tcpc_dev *dev, int role); int (*pd_transmit)(struct tcpc_dev *dev, enum tcpm_transmit_type type, const struct pd_message *msg); diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index 7df4ecabc78a2fa52d9f0b051dbd3c463991b304..27a6fc7c35ea7bbcd55b78ee2fe4c6c525175b02 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -74,6 +74,7 @@ enum typec_orientation { * @id_header: ID Header VDO * @cert_stat: Cert Stat VDO * @product: Product VDO + * @product_type: Product type VDO * * USB power delivery Discover Identity command response data. * @@ -84,6 +85,7 @@ struct usb_pd_identity { u32 id_header; u32 cert_stat; u32 product; + u32 product_type; }; int typec_partner_set_identity(struct typec_partner *partner); diff --git a/include/linux/usb/typec_mux.h b/include/linux/usb/typec_mux.h index 79293f630ee12b52665ae0513dc5f9985f3584c3..873ace5b0cf861d8520d6bf991feecba6f8d9832 100644 --- a/include/linux/usb/typec_mux.h +++ b/include/linux/usb/typec_mux.h @@ -3,53 +3,48 @@ #ifndef __USB_TYPEC_MUX #define __USB_TYPEC_MUX -#include #include struct device; +struct typec_mux; +struct typec_switch; +struct fwnode_handle; -/** - * struct typec_switch - USB Type-C cable orientation switch - * @dev: Switch device - * @entry: List entry - * @set: Callback to the driver for setting the orientation - * - * USB Type-C pin flipper switch routing the correct data pairs from the - * connector to the USB controller depending on the orientation of the cable - * plug. - */ -struct typec_switch { - struct device *dev; - struct list_head entry; - - int (*set)(struct typec_switch *sw, enum typec_orientation orientation); -}; +typedef int (*typec_switch_set_fn_t)(struct typec_switch *sw, + enum typec_orientation orientation); -/** - * struct typec_switch - USB Type-C connector pin mux - * @dev: Mux device - * @entry: List entry - * @set: Callback to the driver for setting the state of the mux - * - * Pin Multiplexer/DeMultiplexer switch routing the USB Type-C connector pins to - * different components depending on the requested mode of operation. Used with - * Accessory/Alternate modes. - */ -struct typec_mux { - struct device *dev; - struct list_head entry; - - int (*set)(struct typec_mux *mux, int state); +struct typec_switch_desc { + struct fwnode_handle *fwnode; + typec_switch_set_fn_t set; + void *drvdata; }; struct typec_switch *typec_switch_get(struct device *dev); void typec_switch_put(struct typec_switch *sw); -int typec_switch_register(struct typec_switch *sw); +struct typec_switch * +typec_switch_register(struct device *parent, + const struct typec_switch_desc *desc); void typec_switch_unregister(struct typec_switch *sw); -struct typec_mux *typec_mux_get(struct device *dev, const char *name); +void typec_switch_set_drvdata(struct typec_switch *sw, void *data); +void *typec_switch_get_drvdata(struct typec_switch *sw); + +typedef int (*typec_mux_set_fn_t)(struct typec_mux *mux, int state); + +struct typec_mux_desc { + struct fwnode_handle *fwnode; + typec_mux_set_fn_t set; + void *drvdata; +}; + +struct typec_mux * +typec_mux_get(struct device *dev, const struct typec_altmode_desc *desc); void typec_mux_put(struct typec_mux *mux); -int typec_mux_register(struct typec_mux *mux); +struct typec_mux * +typec_mux_register(struct device *parent, const struct typec_mux_desc *desc); void typec_mux_unregister(struct typec_mux *mux); +void typec_mux_set_drvdata(struct typec_mux *mux, void *data); +void *typec_mux_get_drvdata(struct typec_mux *mux); + #endif /* __USB_TYPEC_MUX */ diff --git a/include/media/davinci/vpbe.h b/include/media/davinci/vpbe.h index 79a566d7defd00353651c8a06fc0d3a3d62a8615..180a05e91497936611c2b340a755fc6e9491207d 100644 --- a/include/media/davinci/vpbe.h +++ b/include/media/davinci/vpbe.h @@ -92,7 +92,7 @@ struct vpbe_config { struct encoder_config_info *ext_encoders; /* amplifier information goes here */ struct amp_config_info *amp; - int num_outputs; + unsigned int num_outputs; /* Order is venc outputs followed by LCD and then external encoders */ struct vpbe_output *outputs; }; diff --git a/include/media/v4l2-device.h b/include/media/v4l2-device.h index b330e4a08a6b805e6613dc626ae4e569d3b89b1f..40840fec337ca97b52d0bf9c1cae050edfed7c8e 100644 --- a/include/media/v4l2-device.h +++ b/include/media/v4l2-device.h @@ -372,7 +372,7 @@ static inline void v4l2_subdev_notify(struct v4l2_subdev *sd, struct v4l2_subdev *__sd; \ \ __v4l2_device_call_subdevs_p(v4l2_dev, __sd, \ - !(grpid) || __sd->grp_id == (grpid), o, f , \ + (grpid) == 0 || __sd->grp_id == (grpid), o, f , \ ##args); \ } while (0) @@ -404,7 +404,7 @@ static inline void v4l2_subdev_notify(struct v4l2_subdev *sd, ({ \ struct v4l2_subdev *__sd; \ __v4l2_device_call_subdevs_until_err_p(v4l2_dev, __sd, \ - !(grpid) || __sd->grp_id == (grpid), o, f , \ + (grpid) == 0 || __sd->grp_id == (grpid), o, f , \ ##args); \ }) @@ -432,8 +432,8 @@ static inline void v4l2_subdev_notify(struct v4l2_subdev *sd, struct v4l2_subdev *__sd; \ \ __v4l2_device_call_subdevs_p(v4l2_dev, __sd, \ - !(grpmsk) || (__sd->grp_id & (grpmsk)), o, f , \ - ##args); \ + (grpmsk) == 0 || (__sd->grp_id & (grpmsk)), o, \ + f , ##args); \ } while (0) /** @@ -463,8 +463,8 @@ static inline void v4l2_subdev_notify(struct v4l2_subdev *sd, ({ \ struct v4l2_subdev *__sd; \ __v4l2_device_call_subdevs_until_err_p(v4l2_dev, __sd, \ - !(grpmsk) || (__sd->grp_id & (grpmsk)), o, f , \ - ##args); \ + (grpmsk) == 0 || (__sd->grp_id & (grpmsk)), o, \ + f , ##args); \ }) diff --git a/include/media/v4l2-rect.h b/include/media/v4l2-rect.h index 595c3ba05f2396f8c05d2f9d92655afb0a0b1cb3..59745e5feb4da976c1c0cb5b564c784b7d920b70 100644 --- a/include/media/v4l2-rect.h +++ b/include/media/v4l2-rect.h @@ -63,10 +63,10 @@ static inline void v4l2_rect_map_inside(struct v4l2_rect *r, r->left = boundary->left; if (r->top < boundary->top) r->top = boundary->top; - if (r->left + r->width > boundary->width) - r->left = boundary->width - r->width; - if (r->top + r->height > boundary->height) - r->top = boundary->height - r->height; + if (r->left + r->width > boundary->left + boundary->width) + r->left = boundary->left + boundary->width - r->width; + if (r->top + r->height > boundary->top + boundary->height) + r->top = boundary->top + boundary->height - r->height; } /** diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 32b561290d0190ccde7d3ed94650e8c6be8a18d9..97e3af501c67fec311ee25cfe44d1ceadcf4ab63 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3232,6 +3232,9 @@ struct cfg80211_update_owe_info { * * @start_radar_detection: Start radar detection in the driver. * + * @end_cac: End running CAC, probably because a related CAC + * was finished on another phy. + * * @update_ft_ies: Provide updated Fast BSS Transition information to the * driver. If the SME is in the driver/firmware, this information can be * used in building Authentication and Reassociation Request frames. @@ -3553,6 +3556,8 @@ struct cfg80211_ops { struct net_device *dev, struct cfg80211_chan_def *chandef, u32 cac_time_ms); + void (*end_cac)(struct wiphy *wiphy, + struct net_device *dev); int (*update_ft_ies)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_update_ft_ies_params *ftie); int (*crit_proto_start)(struct wiphy *wiphy, @@ -5064,6 +5069,17 @@ cfg80211_find_vendor_ie(unsigned int oui, int oui_type, return (void *)cfg80211_find_vendor_elem(oui, oui_type, ies, len); } +/** + * cfg80211_send_layer2_update - send layer 2 update frame + * + * @dev: network device + * @addr: STA MAC address + * + * Wireless drivers can use this function to update forwarding tables in bridge + * devices upon STA association. + */ +void cfg80211_send_layer2_update(struct net_device *dev, const u8 *addr); + /** * DOC: Regulatory enforcement infrastructure * diff --git a/include/net/cnss2.h b/include/net/cnss2.h index eb37edc647f592df68cbae9b17804cbed8fd30a3..ecec0832af7795dfe1904735f4b501cb3932d22f 100644 --- a/include/net/cnss2.h +++ b/include/net/cnss2.h @@ -21,7 +21,8 @@ enum cnss_bus_width_type { CNSS_BUS_WIDTH_LOW, CNSS_BUS_WIDTH_MEDIUM, CNSS_BUS_WIDTH_HIGH, - CNSS_BUS_WIDTH_VERY_HIGH + CNSS_BUS_WIDTH_VERY_HIGH, + CNSS_BUS_WIDTH_LOW_LATENCY }; enum cnss_platform_cap_flag { @@ -212,6 +213,7 @@ extern int cnss_wlan_pm_control(struct device *dev, bool vote); extern int cnss_auto_suspend(struct device *dev); extern int cnss_auto_resume(struct device *dev); extern int cnss_pci_is_drv_connected(struct device *dev); +extern int cnss_pci_force_wake_request_sync(struct device *dev, int timeout); extern int cnss_pci_force_wake_request(struct device *dev); extern int cnss_pci_is_device_awake(struct device *dev); extern int cnss_pci_force_wake_release(struct device *dev); diff --git a/include/net/flow_dissector.h b/include/net/flow_dissector.h index 4618cbbe3632477870b9d2b85ed7a5a9b2714737..99f8580344d03d831604a9b8d262fee90dfa4ce9 100644 --- a/include/net/flow_dissector.h +++ b/include/net/flow_dissector.h @@ -5,6 +5,7 @@ #include #include #include +#include #include /** @@ -306,4 +307,12 @@ static inline void *skb_flow_dissector_target(struct flow_dissector *flow_dissec return ((char *)target_container) + flow_dissector->offset[key_id]; } +static inline void +flow_dissector_init_keys(struct flow_dissector_key_control *key_control, + struct flow_dissector_key_basic *key_basic) +{ + memset(key_control, 0, sizeof(*key_control)); + memset(key_basic, 0, sizeof(*key_basic)); +} + #endif diff --git a/include/net/ipx.h b/include/net/ipx.h index baf0903909984de0a2ee823f72e3cd673da42961..9d1342807b597058c268cf84ac57eac77798bf37 100644 --- a/include/net/ipx.h +++ b/include/net/ipx.h @@ -47,11 +47,6 @@ struct ipxhdr { /* From af_ipx.c */ extern int sysctl_ipx_pprop_broadcasting; -static __inline__ struct ipxhdr *ipx_hdr(struct sk_buff *skb) -{ - return (struct ipxhdr *)skb_transport_header(skb); -} - struct ipx_interface { /* IPX address */ __be32 if_netnum; diff --git a/include/net/ndisc.h b/include/net/ndisc.h index d2a016ef5c417377583952ca142e98fe4e1f5b26..35d7c887a5b9abe746932fe36ca15c7d270cdd51 100644 --- a/include/net/ndisc.h +++ b/include/net/ndisc.h @@ -39,6 +39,7 @@ enum { ND_OPT_DNSSL = 31, /* RFC6106 */ ND_OPT_6CO = 34, /* RFC6775 */ ND_OPT_CAPTIVE_PORTAL = 37, /* RFC7710 */ + ND_OPT_PREF64 = 38, /* RFC-ietf-6man-ra-pref64-09 */ __ND_OPT_MAX }; diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 75a3f3fdb3591720d266fd33d98c608fc673b6ca..c1162f2fde783df6937dadaf1205c357c56e3433 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -206,31 +206,38 @@ __cls_set_class(unsigned long *clp, unsigned long cl) return xchg(clp, cl); } -static inline unsigned long -cls_set_class(struct Qdisc *q, unsigned long *clp, unsigned long cl) +static inline void +__tcf_bind_filter(struct Qdisc *q, struct tcf_result *r, unsigned long base) { - unsigned long old_cl; + unsigned long cl; - sch_tree_lock(q); - old_cl = __cls_set_class(clp, cl); - sch_tree_unlock(q); - return old_cl; + cl = q->ops->cl_ops->bind_tcf(q, base, r->classid); + cl = __cls_set_class(&r->class, cl); + if (cl) + q->ops->cl_ops->unbind_tcf(q, cl); } static inline void tcf_bind_filter(struct tcf_proto *tp, struct tcf_result *r, unsigned long base) { struct Qdisc *q = tp->chain->block->q; - unsigned long cl; /* Check q as it is not set for shared blocks. In that case, * setting class is not supported. */ if (!q) return; - cl = q->ops->cl_ops->bind_tcf(q, base, r->classid); - cl = cls_set_class(q, &r->class, cl); - if (cl) + sch_tree_lock(q); + __tcf_bind_filter(q, r, base); + sch_tree_unlock(q); +} + +static inline void +__tcf_unbind_filter(struct Qdisc *q, struct tcf_result *r) +{ + unsigned long cl; + + if ((cl = __cls_set_class(&r->class, 0)) != 0) q->ops->cl_ops->unbind_tcf(q, cl); } @@ -238,12 +245,10 @@ static inline void tcf_unbind_filter(struct tcf_proto *tp, struct tcf_result *r) { struct Qdisc *q = tp->chain->block->q; - unsigned long cl; if (!q) return; - if ((cl = __cls_set_class(&r->class, 0)) != 0) - q->ops->cl_ops->unbind_tcf(q, cl); + __tcf_unbind_filter(q, r); } struct tcf_exts { diff --git a/include/net/request_sock.h b/include/net/request_sock.h index 347015515a7de68a09d935e2b4a1baad3479cc03..1653435f18f5c7d1a27228dcaa6535995f47a6f0 100644 --- a/include/net/request_sock.h +++ b/include/net/request_sock.h @@ -183,7 +183,7 @@ void reqsk_fastopen_remove(struct sock *sk, struct request_sock *req, static inline bool reqsk_queue_empty(const struct request_sock_queue *queue) { - return queue->rskq_accept_head == NULL; + return READ_ONCE(queue->rskq_accept_head) == NULL; } static inline struct request_sock *reqsk_queue_remove(struct request_sock_queue *queue, @@ -195,7 +195,7 @@ static inline struct request_sock *reqsk_queue_remove(struct request_sock_queue req = queue->rskq_accept_head; if (req) { sk_acceptq_removed(parent); - queue->rskq_accept_head = req->dl_next; + WRITE_ONCE(queue->rskq_accept_head, req->dl_next); if (queue->rskq_accept_head == NULL) queue->rskq_accept_tail = NULL; } diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index c9cd5086bd544e1be75b594fa34d8c5113f6526e..d737a6a2600be802672bdcd119a782aad1a5ec9d 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -273,7 +273,8 @@ struct tcf_proto_ops { int (*reoffload)(struct tcf_proto *tp, bool add, tc_setup_cb_t *cb, void *cb_priv, struct netlink_ext_ack *extack); - void (*bind_class)(void *, u32, unsigned long); + void (*bind_class)(void *, u32, unsigned long, + void *, unsigned long); void * (*tmplt_create)(struct net *net, struct tcf_chain *chain, struct nlattr **tca, diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 2abbc15824af953589d8fb4eb5c15e6d2e4a4c3d..2c6570e6fcfec1839c150ab7743be11d129aa422 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -625,4 +625,9 @@ static inline __u32 sctp_min_frag_point(struct sctp_sock *sp, __u16 datasize) return sctp_mtu_payload(sp, SCTP_DEFAULT_MINSEGMENT, datasize); } +static inline bool sctp_newsk_ready(const struct sock *sk) +{ + return sock_flag(sk, SOCK_DEAD) || sk->sk_socket; +} + #endif /* __net_sctp_h__ */ diff --git a/include/net/tcp.h b/include/net/tcp.h index 743412ecd89145b39589f4413261d5eb98079004..b6531f0c5f3081a9d91155f4515d36c83f72f8cd 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -269,7 +269,7 @@ static inline bool tcp_under_memory_pressure(const struct sock *sk) mem_cgroup_under_socket_pressure(sk->sk_memcg)) return true; - return tcp_memory_pressure; + return READ_ONCE(tcp_memory_pressure); } /* * The next routines deal with comparing 32 bit unsigned ints diff --git a/include/net/xfrm.h b/include/net/xfrm.h index fb9b19a3b749645584e571fb8fb684462574b8bb..48dc1ce2170d817c52d4b141fc974a7886a2189f 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1054,7 +1054,6 @@ static inline void xfrm_dst_destroy(struct xfrm_dst *xdst) void xfrm_dst_ifdown(struct dst_entry *dst, struct net_device *dev); struct xfrm_if_parms { - char name[IFNAMSIZ]; /* name of XFRM device */ int link; /* ifindex of underlying L2 interface */ u32 if_id; /* interface identifyer */ }; diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 54e4d1fd21f8f88aec48dbe580f6a0f51e777c9c..874cd6e94093b0a5b4eb4987581a0e9c061f480e 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -3864,6 +3864,9 @@ static inline int ib_check_mr_access(int flags) !(flags & IB_ACCESS_LOCAL_WRITE)) return -EINVAL; + if (flags & ~IB_ACCESS_SUPPORTED) + return -EINVAL; + return 0; } diff --git a/include/scsi/iscsi_proto.h b/include/scsi/iscsi_proto.h index f0a01a54bd153cbca39abc9cb8670c2597cdb231..df156f1d50b2d7e74297e2606ec5e4792f86d24e 100644 --- a/include/scsi/iscsi_proto.h +++ b/include/scsi/iscsi_proto.h @@ -638,7 +638,6 @@ struct iscsi_reject { #define ISCSI_REASON_BOOKMARK_INVALID 9 #define ISCSI_REASON_BOOKMARK_NO_RESOURCES 10 #define ISCSI_REASON_NEGOTIATION_RESET 11 -#define ISCSI_REASON_WAITING_FOR_LOGOUT 12 /* Max. number of Key=Value pairs in a text message */ #define MAX_KEY_VALUE_PAIRS 8192 diff --git a/include/soc/qcom/cx_ipeak.h b/include/soc/qcom/cx_ipeak.h index 53d7e9c7b9e85166afbaaac1080daf20902911bf..f52d953c2edd6e3773263249b6973094ff379267 100644 --- a/include/soc/qcom/cx_ipeak.h +++ b/include/soc/qcom/cx_ipeak.h @@ -1,11 +1,13 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2018, 2020 The Linux Foundation. All rights reserved. */ #ifndef __SOC_COM_CX_IPEAK_H #define __SOC_COM_CX_IPEAK_H +typedef int (*cx_ipeak_victim_fn)(void *data, u32 freq_limit); + struct device_node; struct cx_ipeak_client; @@ -27,12 +29,25 @@ static inline int cx_ipeak_update(struct cx_ipeak_client *ipeak_client, { return 0; } + +static inline int cx_ipeak_victim_register(struct cx_ipeak_client *client, + cx_ipeak_victim_fn victim_cb, void *data) +{ + return 0; +} + +static inline void cx_ipeak_victim_unregister(struct cx_ipeak_client *client) +{ +} #else struct cx_ipeak_client *cx_ipeak_register(struct device_node *dev_node, const char *client_name); void cx_ipeak_unregister(struct cx_ipeak_client *client); int cx_ipeak_update(struct cx_ipeak_client *ipeak_client, bool vote); +int cx_ipeak_victim_register(struct cx_ipeak_client *client, + cx_ipeak_victim_fn victim_cb, void *data); +void cx_ipeak_victim_unregister(struct cx_ipeak_client *client); #endif diff --git a/include/soc/qcom/icnss2.h b/include/soc/qcom/icnss2.h index 64128de929860a65fee443b2f64badaaf53008e8..d40b70bd4a91e030df8f40992262f9468273b39d 100644 --- a/include/soc/qcom/icnss2.h +++ b/include/soc/qcom/icnss2.h @@ -46,6 +46,7 @@ struct icnss_driver_ops { int (*uevent)(struct device *dev, struct icnss_uevent_data *uevent); int (*idle_shutdown)(struct device *dev); int (*idle_restart)(struct device *dev); + int (*set_therm_state)(struct device *dev, unsigned long thermal_state); }; @@ -167,4 +168,11 @@ extern void icnss_get_msi_address(struct device *dev, u32 *msi_addr_low, extern int icnss_qmi_send(struct device *dev, int type, void *cmd, int cmd_len, void *cb_ctx, int (*cb)(void *ctx, void *event, int event_len)); +extern int icnss_force_wake_request(struct device *dev); +extern int icnss_force_wake_release(struct device *dev); +extern int icnss_is_device_awake(struct device *dev); +extern int icnss_thermal_register(struct device *dev, unsigned long max_state); +extern void icnss_thermal_unregister(struct device *dev); +extern int icnss_get_curr_therm_state(struct device *dev, + unsigned long *thermal_state); #endif /* _ICNSS_WLAN_H_ */ diff --git a/include/soc/qcom/mpm.h b/include/soc/qcom/mpm.h index 43bed05ad2170901c60d30a39de9ad57cfb8b262..236033511bb853bde5e86620ca796998532afe3a 100644 --- a/include/soc/qcom/mpm.h +++ b/include/soc/qcom/mpm.h @@ -16,4 +16,5 @@ struct mpm_pin { extern const struct mpm_pin mpm_bengal_gic_chip_data[]; extern const struct mpm_pin mpm_scuba_gic_chip_data[]; +extern const struct mpm_pin mpm_sdm660_gic_chip_data[]; #endif /* __QCOM_MPM_H__ */ diff --git a/include/sound/rawmidi.h b/include/sound/rawmidi.h index 6665cb29e1a238af24b7dcb4df543c147cbad742..c2a71fd8dfafd172fdf7dc798705c81d7615b8bd 100644 --- a/include/sound/rawmidi.h +++ b/include/sound/rawmidi.h @@ -92,9 +92,9 @@ struct snd_rawmidi_substream { struct list_head list; /* list of all substream for given stream */ int stream; /* direction */ int number; /* substream number */ - unsigned int opened: 1, /* open flag */ - append: 1, /* append flag (merge more streams) */ - active_sensing: 1; /* send active sensing when close */ + bool opened; /* open flag */ + bool append; /* append flag (merge more streams) */ + bool active_sensing; /* send active sensing when close */ int use_count; /* use counter (for output) */ size_t bytes; struct snd_rawmidi *rmidi; diff --git a/include/sound/soc.h b/include/sound/soc.h index c2c05c9676bad170825c6b7b60de7962881e9209..9c3e6f44b94a253bf6f848d450ea8ee8594ea9dc 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -563,12 +563,12 @@ static inline void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count, void snd_soc_card_change_online_state(struct snd_soc_card *soc_card, int online); -#ifdef CONFIG_SND_SOC_AC97_BUS struct snd_ac97 *snd_soc_alloc_ac97_component(struct snd_soc_component *component); struct snd_ac97 *snd_soc_new_ac97_component(struct snd_soc_component *component, unsigned int id, unsigned int id_mask); void snd_soc_free_ac97_component(struct snd_ac97 *ac97); +#ifdef CONFIG_SND_SOC_AC97_BUS int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops); int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops, struct platform_device *pdev); @@ -1368,6 +1368,8 @@ int snd_soc_component_test_bits(struct snd_soc_component *component, unsigned int reg, unsigned int mask, unsigned int value); struct snd_soc_component *soc_find_component( const struct device_node *of_node, const char *name); +struct snd_soc_component *soc_find_component_locked( + const struct device_node *of_node, const char *name); /* component wide operations */ int snd_soc_component_set_sysclk(struct snd_soc_component *component, diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index bcd512b305d3267dc38cf21575d300813379fec3..ad989c7cba90c580c65c6b74f2ae3684e828deca 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -49,6 +49,7 @@ TRACE_DEFINE_ENUM(CP_SYNC); TRACE_DEFINE_ENUM(CP_RECOVERY); TRACE_DEFINE_ENUM(CP_DISCARD); TRACE_DEFINE_ENUM(CP_TRIMMED); +TRACE_DEFINE_ENUM(CP_PAUSE); #define show_block_type(type) \ __print_symbolic(type, \ @@ -134,13 +135,14 @@ TRACE_DEFINE_ENUM(CP_TRIMMED); { CP_SYNC, "Sync" }, \ { CP_RECOVERY, "Recovery" }, \ { CP_DISCARD, "Discard" }, \ - { CP_UMOUNT, "Umount" }, \ + { CP_PAUSE, "Pause" }, \ { CP_TRIMMED, "Trimmed" }) #define show_fsync_cpreason(type) \ __print_symbolic(type, \ { CP_NO_NEEDED, "no needed" }, \ { CP_NON_REGULAR, "non regular" }, \ + { CP_COMPRESSED, "compressed" }, \ { CP_HARDLINK, "hardlink" }, \ { CP_SB_NEED_CP, "sb needs cp" }, \ { CP_WRONG_PINO, "wrong pino" }, \ @@ -158,6 +160,11 @@ TRACE_DEFINE_ENUM(CP_TRIMMED); { F2FS_GOING_DOWN_METAFLUSH, "meta flush" }, \ { F2FS_GOING_DOWN_NEED_FSCK, "need fsck" }) +#define show_compress_algorithm(type) \ + __print_symbolic(type, \ + { COMPRESS_LZO, "LZO" }, \ + { COMPRESS_LZ4, "LZ4" }) + struct f2fs_sb_info; struct f2fs_io_info; struct extent_info; @@ -1720,6 +1727,100 @@ TRACE_EVENT(f2fs_shutdown, __entry->ret) ); +DECLARE_EVENT_CLASS(f2fs_zip_start, + + TP_PROTO(struct inode *inode, pgoff_t cluster_idx, + unsigned int cluster_size, unsigned char algtype), + + TP_ARGS(inode, cluster_idx, cluster_size, algtype), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(pgoff_t, idx) + __field(unsigned int, size) + __field(unsigned int, algtype) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->idx = cluster_idx; + __entry->size = cluster_size; + __entry->algtype = algtype; + ), + + TP_printk("dev = (%d,%d), ino = %lu, cluster_idx:%lu, " + "cluster_size = %u, algorithm = %s", + show_dev_ino(__entry), + __entry->idx, + __entry->size, + show_compress_algorithm(__entry->algtype)) +); + +DECLARE_EVENT_CLASS(f2fs_zip_end, + + TP_PROTO(struct inode *inode, pgoff_t cluster_idx, + unsigned int compressed_size, int ret), + + TP_ARGS(inode, cluster_idx, compressed_size, ret), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(pgoff_t, idx) + __field(unsigned int, size) + __field(unsigned int, ret) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->idx = cluster_idx; + __entry->size = compressed_size; + __entry->ret = ret; + ), + + TP_printk("dev = (%d,%d), ino = %lu, cluster_idx:%lu, " + "compressed_size = %u, ret = %d", + show_dev_ino(__entry), + __entry->idx, + __entry->size, + __entry->ret) +); + +DEFINE_EVENT(f2fs_zip_start, f2fs_compress_pages_start, + + TP_PROTO(struct inode *inode, pgoff_t cluster_idx, + unsigned int cluster_size, unsigned char algtype), + + TP_ARGS(inode, cluster_idx, cluster_size, algtype) +); + +DEFINE_EVENT(f2fs_zip_start, f2fs_decompress_pages_start, + + TP_PROTO(struct inode *inode, pgoff_t cluster_idx, + unsigned int cluster_size, unsigned char algtype), + + TP_ARGS(inode, cluster_idx, cluster_size, algtype) +); + +DEFINE_EVENT(f2fs_zip_end, f2fs_compress_pages_end, + + TP_PROTO(struct inode *inode, pgoff_t cluster_idx, + unsigned int compressed_size, int ret), + + TP_ARGS(inode, cluster_idx, compressed_size, ret) +); + +DEFINE_EVENT(f2fs_zip_end, f2fs_decompress_pages_end, + + TP_PROTO(struct inode *inode, pgoff_t cluster_idx, + unsigned int compressed_size, int ret), + + TP_ARGS(inode, cluster_idx, compressed_size, ret) +); + #endif /* _TRACE_F2FS_H */ /* This part must be outside protection */ diff --git a/include/trace/events/power.h b/include/trace/events/power.h index b666893c62197fd45a06b49b715332cbba7b21fe..53a3fcd4ea0803cf9183e4d13227a87fc8d9c0b4 100644 --- a/include/trace/events/power.h +++ b/include/trace/events/power.h @@ -730,12 +730,11 @@ TRACE_EVENT(cache_hwmon_update, ); TRACE_EVENT(memlat_dev_meas, - TP_PROTO(const char *name, unsigned int dev_id, unsigned long inst, unsigned long mem, unsigned long freq, unsigned int stall, - unsigned int ratio), + unsigned int wb, unsigned int ratio), - TP_ARGS(name, dev_id, inst, mem, freq, stall, ratio), + TP_ARGS(name, dev_id, inst, mem, freq, stall, wb, ratio), TP_STRUCT__entry( __string(name, name) @@ -744,6 +743,7 @@ TRACE_EVENT(memlat_dev_meas, __field(unsigned long, mem) __field(unsigned long, freq) __field(unsigned int, stall) + __field(unsigned int, wb) __field(unsigned int, ratio) ), @@ -754,16 +754,18 @@ TRACE_EVENT(memlat_dev_meas, __entry->mem = mem; __entry->freq = freq; __entry->stall = stall; + __entry->wb = wb; __entry->ratio = ratio; ), - TP_printk("dev: %s, id=%u, inst=%lu, mem=%lu, freq=%lu, stall=%u, ratio=%u", + TP_printk("dev: %s, id=%u, inst=%lu, mem=%lu, freq=%lu, stall=%u, wb=%u, ratio=%u", __get_str(name), __entry->dev_id, __entry->inst, __entry->mem, __entry->freq, __entry->stall, + __entry->wb, __entry->ratio) ); diff --git a/include/trace/events/preemptirq.h b/include/trace/events/preemptirq.h index 33696556f994c9fae67b5f082af04d27ec01a886..752da323a87a635363199134af6ecb166e2a436b 100644 --- a/include/trace/events/preemptirq.h +++ b/include/trace/events/preemptirq.h @@ -18,13 +18,13 @@ DECLARE_EVENT_CLASS(preemptirq_template, TP_ARGS(ip, parent_ip), TP_STRUCT__entry( - __field(u32, caller_offs) - __field(u32, parent_offs) + __field(s32, caller_offs) + __field(s32, parent_offs) ), TP_fast_assign( - __entry->caller_offs = (u32)(ip - (unsigned long)_stext); - __entry->parent_offs = (u32)(parent_ip - (unsigned long)_stext); + __entry->caller_offs = (s32)(ip - (unsigned long)_stext); + __entry->parent_offs = (s32)(parent_ip - (unsigned long)_stext); ), TP_printk("caller=%pF parent=%pF", diff --git a/include/trace/events/random.h b/include/trace/events/random.h index 0560dfc33f1c34d5f789e09ae54d33ad13d5e0f5..32c10a515e2d5438e8d620a0c2313aab5f849b2b 100644 --- a/include/trace/events/random.h +++ b/include/trace/events/random.h @@ -62,15 +62,14 @@ DEFINE_EVENT(random__mix_pool_bytes, mix_pool_bytes_nolock, TRACE_EVENT(credit_entropy_bits, TP_PROTO(const char *pool_name, int bits, int entropy_count, - int entropy_total, unsigned long IP), + unsigned long IP), - TP_ARGS(pool_name, bits, entropy_count, entropy_total, IP), + TP_ARGS(pool_name, bits, entropy_count, IP), TP_STRUCT__entry( __field( const char *, pool_name ) __field( int, bits ) __field( int, entropy_count ) - __field( int, entropy_total ) __field(unsigned long, IP ) ), @@ -78,14 +77,12 @@ TRACE_EVENT(credit_entropy_bits, __entry->pool_name = pool_name; __entry->bits = bits; __entry->entropy_count = entropy_count; - __entry->entropy_total = entropy_total; __entry->IP = IP; ), - TP_printk("%s pool: bits %d entropy_count %d entropy_total %d " - "caller %pS", __entry->pool_name, __entry->bits, - __entry->entropy_count, __entry->entropy_total, - (void *)__entry->IP) + TP_printk("%s pool: bits %d entropy_count %d caller %pS", + __entry->pool_name, __entry->bits, + __entry->entropy_count, (void *)__entry->IP) ); TRACE_EVENT(push_to_pool, diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index a08916eb76152ce4ab6069761621ca59f5784dba..0924119bcfa40f34efa148fa872698f5fd65e66a 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -554,10 +554,10 @@ TRACE_EVENT(rxrpc_peer, ); TRACE_EVENT(rxrpc_conn, - TP_PROTO(struct rxrpc_connection *conn, enum rxrpc_conn_trace op, + TP_PROTO(unsigned int conn_debug_id, enum rxrpc_conn_trace op, int usage, const void *where), - TP_ARGS(conn, op, usage, where), + TP_ARGS(conn_debug_id, op, usage, where), TP_STRUCT__entry( __field(unsigned int, conn ) @@ -567,7 +567,7 @@ TRACE_EVENT(rxrpc_conn, ), TP_fast_assign( - __entry->conn = conn->debug_id; + __entry->conn = conn_debug_id; __entry->op = op; __entry->usage = usage; __entry->where = where; diff --git a/include/trace/events/udp.h b/include/trace/events/udp.h index 336fe272889f59c5fe59cda70f0b5a1bff526c97..f20cb146ed85442c1087490da4a3892a483acd51 100644 --- a/include/trace/events/udp.h +++ b/include/trace/events/udp.h @@ -27,6 +27,54 @@ TRACE_EVENT(udp_fail_queue_rcv_skb, TP_printk("rc=%d port=%hu", __entry->rc, __entry->lport) ); +TRACE_EVENT(udpv4_fail_rcv_buf_errors, + + TP_PROTO(struct sk_buff *skb), + + TP_ARGS(skb), + + TP_STRUCT__entry( + __field(void *, saddr) + __field(void *, daddr) + __field(__be16, sport) + __field(__be16, dport) + ), + + TP_fast_assign( + __entry->saddr = &ip_hdr(skb)->saddr; + __entry->daddr = &ip_hdr(skb)->daddr; + __entry->sport = ntohs(udp_hdr(skb)->source); + __entry->dport = ntohs(udp_hdr(skb)->dest); + ), + + TP_printk("src %pI4:%u dst %pI4:%u", __entry->saddr, + __entry->sport, __entry->daddr, __entry->dport) +); + +TRACE_EVENT(udpv6_fail_rcv_buf_errors, + + TP_PROTO(struct sk_buff *skb), + + TP_ARGS(skb), + + TP_STRUCT__entry( + __field(void *, saddr) + __field(void *, daddr) + __field(__be16, sport) + __field(__be16, dport) + ), + + TP_fast_assign( + __entry->saddr = &ipv6_hdr(skb)->saddr; + __entry->daddr = &ipv6_hdr(skb)->daddr; + __entry->sport = ntohs(udp_hdr(skb)->source); + __entry->dport = ntohs(udp_hdr(skb)->dest); + ), + + TP_printk("src %pI6:%u dst %pI6:%u", __entry->saddr, + __entry->sport, __entry->daddr, __entry->dport) +); + #endif /* _TRACE_UDP_H */ /* This part must be outside protection */ diff --git a/include/trace/events/xen.h b/include/trace/events/xen.h index fdcf88bcf0ea3dec3df105aa7e7d40fe2aaaaf0c..bb76c03898ccd5692108411db85904461f3fdd65 100644 --- a/include/trace/events/xen.h +++ b/include/trace/events/xen.h @@ -66,7 +66,11 @@ TRACE_EVENT(xen_mc_callback, TP_PROTO(xen_mc_callback_fn_t fn, void *data), TP_ARGS(fn, data), TP_STRUCT__entry( - __field(xen_mc_callback_fn_t, fn) + /* + * Use field_struct to avoid is_signed_type() + * comparison of a function pointer. + */ + __field_struct(xen_mc_callback_fn_t, fn) __field(void *, data) ), TP_fast_assign( diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h index 8a9adc6dfe2a7276384612f3bb6a3d5fa8d97fde..214bc1bc81fa268c33de5cc8c92f52c59cab0944 100644 --- a/include/uapi/drm/msm_drm.h +++ b/include/uapi/drm/msm_drm.h @@ -215,7 +215,11 @@ struct drm_msm_gem_cpu_fini { */ struct drm_msm_gem_submit_reloc { __u32 submit_offset; /* in, offset from submit_bo */ +#ifdef __cplusplus + __u32 or_val; +#else __u32 or; /* in, value OR'd with result */ +#endif __s32 shift; /* in, amount of left shift (can be negative) */ __u32 reloc_idx; /* in, index of reloc_bo buffer */ __u64 reloc_offset; /* in, offset from start of reloc_bo */ diff --git a/include/uapi/linux/btf.h b/include/uapi/linux/btf.h index 972265f328717b8286edc2fc93c9d2a54ed394f8..1e2662ff052910d58f7c66c7dd296f565b383af2 100644 --- a/include/uapi/linux/btf.h +++ b/include/uapi/linux/btf.h @@ -22,9 +22,9 @@ struct btf_header { }; /* Max # of type identifier */ -#define BTF_MAX_TYPE 0x0000ffff +#define BTF_MAX_TYPE 0x000fffff /* Max offset into the string section */ -#define BTF_MAX_NAME_OFFSET 0x0000ffff +#define BTF_MAX_NAME_OFFSET 0x00ffffff /* Max # of struct/union/enum members or func args */ #define BTF_MAX_VLEN 0xffff diff --git a/include/uapi/linux/fscrypt.h b/include/uapi/linux/fscrypt.h index 12ac8cc6d54a79c5d0e1e95c02a072b1a47b36f8..8b3e3da9cccea8bcafaf848466df85bd94caae50 100644 --- a/include/uapi/linux/fscrypt.h +++ b/include/uapi/linux/fscrypt.h @@ -8,6 +8,7 @@ #ifndef _UAPI_LINUX_FSCRYPT_H #define _UAPI_LINUX_FSCRYPT_H +#include #include /* Encryption policy flags */ diff --git a/include/uapi/linux/incrementalfs.h b/include/uapi/linux/incrementalfs.h new file mode 100644 index 0000000000000000000000000000000000000000..b257b9f0ec3fc046158c761837ae73569739d779 --- /dev/null +++ b/include/uapi/linux/incrementalfs.h @@ -0,0 +1,245 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Userspace interface for Incremental FS. + * + * Incremental FS is special-purpose Linux virtual file system that allows + * execution of a program while its binary and resource files are still being + * lazily downloaded over the network, USB etc. + * + * Copyright 2019 Google LLC + */ +#ifndef _UAPI_LINUX_INCREMENTALFS_H +#define _UAPI_LINUX_INCREMENTALFS_H + +#include +#include +#include +#include + +/* ===== constants ===== */ +#define INCFS_NAME "incremental-fs" +#define INCFS_MAGIC_NUMBER (0x5346434e49ul) +#define INCFS_DATA_FILE_BLOCK_SIZE 4096 +#define INCFS_HEADER_VER 1 + +/* TODO: This value is assumed in incfs_copy_signature_info_from_user to be the + * actual signature length. Set back to 64 when fixed. + */ +#define INCFS_MAX_HASH_SIZE 32 +#define INCFS_MAX_FILE_ATTR_SIZE 512 + +#define INCFS_PENDING_READS_FILENAME ".pending_reads" +#define INCFS_LOG_FILENAME ".log" +#define INCFS_XATTR_ID_NAME (XATTR_USER_PREFIX "incfs.id") +#define INCFS_XATTR_SIZE_NAME (XATTR_USER_PREFIX "incfs.size") +#define INCFS_XATTR_METADATA_NAME (XATTR_USER_PREFIX "incfs.metadata") + +#define INCFS_MAX_SIGNATURE_SIZE 8096 + +#define INCFS_IOCTL_BASE_CODE 'g' + +/* ===== ioctl requests on the command dir ===== */ + +/* Create a new file */ +#define INCFS_IOC_CREATE_FILE \ + _IOWR(INCFS_IOCTL_BASE_CODE, 30, struct incfs_new_file_args) + +/* Read file signature */ +#define INCFS_IOC_READ_FILE_SIGNATURE \ + _IOWR(INCFS_IOCTL_BASE_CODE, 31, struct incfs_get_file_sig_args) + +enum incfs_compression_alg { + COMPRESSION_NONE = 0, + COMPRESSION_LZ4 = 1 +}; + +enum incfs_block_flags { + INCFS_BLOCK_FLAGS_NONE = 0, + INCFS_BLOCK_FLAGS_HASH = 1, +}; + +typedef struct { + __u8 bytes[16]; +} incfs_uuid_t __attribute__((aligned (8))); + +/* + * Description of a pending read. A pending read - a read call by + * a userspace program for which the filesystem currently doesn't have data. + */ +struct incfs_pending_read_info { + /* Id of a file that is being read from. */ + incfs_uuid_t file_id; + + /* A number of microseconds since system boot to the read. */ + __aligned_u64 timestamp_us; + + /* Index of a file block that is being read. */ + __u32 block_index; + + /* A serial number of this pending read. */ + __u32 serial_number; +}; + +/* + * A struct to be written into a control file to load a data or hash + * block to a data file. + */ +struct incfs_new_data_block { + /* Index of a data block. */ + __u32 block_index; + + /* Length of data */ + __u32 data_len; + + /* + * A pointer to an actual data for the block. + * + * Equivalent to: __u8 *data; + */ + __aligned_u64 data; + + /* + * Compression algorithm used to compress the data block. + * Values from enum incfs_compression_alg. + */ + __u8 compression; + + /* Values from enum incfs_block_flags */ + __u8 flags; + + __u16 reserved1; + + __u32 reserved2; + + __aligned_u64 reserved3; +}; + +enum incfs_hash_tree_algorithm { + INCFS_HASH_TREE_NONE = 0, + INCFS_HASH_TREE_SHA256 = 1 +}; + +struct incfs_file_signature_info { + /* + * A pointer to file's root hash (if determined != 0) + * Actual hash size determined by hash_tree_alg. + * Size of the buffer should be at least INCFS_MAX_HASH_SIZE + * + * Equivalent to: u8 *root_hash; + */ + __aligned_u64 root_hash; + + /* + * A pointer to additional data that was attached to the root hash + * before signing. + * + * Equivalent to: u8 *additional_data; + */ + __aligned_u64 additional_data; + + /* Size of additional data. */ + __u32 additional_data_size; + + __u32 reserved1; + + /* + * A pointer to pkcs7 signature DER blob. + * + * Equivalent to: u8 *signature; + */ + __aligned_u64 signature; + + + /* Size of pkcs7 signature DER blob */ + __u32 signature_size; + + __u32 reserved2; + + /* Value from incfs_hash_tree_algorithm */ + __u8 hash_tree_alg; +}; + +/* + * Create a new file or directory. + */ +struct incfs_new_file_args { + /* Id of a file to create. */ + incfs_uuid_t file_id; + + /* + * Total size of the new file. Ignored if S_ISDIR(mode). + */ + __aligned_u64 size; + + /* + * File mode. Permissions and dir flag. + */ + __u16 mode; + + __u16 reserved1; + + __u32 reserved2; + + /* + * A pointer to a null-terminated relative path to the file's parent + * dir. + * Max length: PATH_MAX + * + * Equivalent to: char *directory_path; + */ + __aligned_u64 directory_path; + + /* + * A pointer to a null-terminated file's name. + * Max length: PATH_MAX + * + * Equivalent to: char *file_name; + */ + __aligned_u64 file_name; + + /* + * A pointer to a file attribute to be set on creation. + * + * Equivalent to: u8 *file_attr; + */ + __aligned_u64 file_attr; + + /* + * Length of the data buffer specfied by file_attr. + * Max value: INCFS_MAX_FILE_ATTR_SIZE + */ + __u32 file_attr_len; + + __u32 reserved4; + + /* struct incfs_file_signature_info *signature_info; */ + __aligned_u64 signature_info; + + __aligned_u64 reserved5; + + __aligned_u64 reserved6; +}; + +/* + * Request a digital signature blob for a given file. + * Argument for INCFS_IOC_READ_FILE_SIGNATURE ioctl + */ +struct incfs_get_file_sig_args { + /* + * A pointer to the data buffer to save an signature blob to. + * + * Equivalent to: u8 *file_signature; + */ + __aligned_u64 file_signature; + + /* Size of the buffer at file_signature. */ + __u32 file_signature_buf_size; + + /* + * Number of bytes save file_signature buffer. + * It is set after ioctl done. + */ + __u32 file_signature_len_out; +}; + +#endif /* _UAPI_LINUX_INCREMENTALFS_H */ diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h index f056b2a00d5c7695a69826786b5e2336c79c3c31..9a61c28ed3ae48cf2107ffc8b9ea0647e9ccb4f8 100644 --- a/include/uapi/linux/input.h +++ b/include/uapi/linux/input.h @@ -34,6 +34,7 @@ struct input_event { __kernel_ulong_t __sec; #if defined(__sparc__) && defined(__arch64__) unsigned int __usec; + unsigned int __pad; #else __kernel_ulong_t __usec; #endif diff --git a/include/uapi/linux/kcov.h b/include/uapi/linux/kcov.h index 9529867717a866cc7d02a4906165d0bd59faced0..1d0350e44ae34c57ae029de544a51c47d9fb0b95 100644 --- a/include/uapi/linux/kcov.h +++ b/include/uapi/linux/kcov.h @@ -4,9 +4,24 @@ #include +/* + * Argument for KCOV_REMOTE_ENABLE ioctl, see Documentation/dev-tools/kcov.rst + * and the comment before kcov_remote_start() for usage details. + */ +struct kcov_remote_arg { + __u32 trace_mode; /* KCOV_TRACE_PC or KCOV_TRACE_CMP */ + __u32 area_size; /* Length of coverage buffer in words */ + __u32 num_handles; /* Size of handles array */ + __aligned_u64 common_handle; + __aligned_u64 handles[0]; +}; + +#define KCOV_REMOTE_MAX_HANDLES 0x100 + #define KCOV_INIT_TRACE _IOR('c', 1, unsigned long) #define KCOV_ENABLE _IO('c', 100) #define KCOV_DISABLE _IO('c', 101) +#define KCOV_REMOTE_ENABLE _IOW('c', 102, struct kcov_remote_arg) enum { /* @@ -32,4 +47,17 @@ enum { #define KCOV_CMP_SIZE(n) ((n) << 1) #define KCOV_CMP_MASK KCOV_CMP_SIZE(3) +#define KCOV_SUBSYSTEM_COMMON (0x00ull << 56) +#define KCOV_SUBSYSTEM_USB (0x01ull << 56) + +#define KCOV_SUBSYSTEM_MASK (0xffull << 56) +#define KCOV_INSTANCE_MASK (0xffffffffull) + +static inline __u64 kcov_remote_handle(__u64 subsys, __u64 inst) +{ + if (subsys & ~KCOV_SUBSYSTEM_MASK || inst & ~KCOV_INSTANCE_MASK) + return 0; + return subsys | inst; +} + #endif /* _LINUX_KCOV_IOCTLS_H */ diff --git a/include/uapi/linux/ndctl.h b/include/uapi/linux/ndctl.h index 2f2c43d633c59c25ca1dd43feb69e7b648639ece..f57c9e434d2d3bc5ca350ae81b73e1c96c8bc0a8 100644 --- a/include/uapi/linux/ndctl.h +++ b/include/uapi/linux/ndctl.h @@ -202,10 +202,6 @@ enum nd_driver_flags { ND_DRIVER_DAX_PMEM = 1 << ND_DEVICE_DAX_PMEM, }; -enum { - ND_MIN_NAMESPACE_SIZE = PAGE_SIZE, -}; - enum ars_masks { ARS_STATUS_MASK = 0x0000FFFF, ARS_EXT_STATUS_SHIFT = 16, diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 325ec6ef0a7647571cee94bf2f58f46ed322c6d9..5eac62e1b68d57570e2b920d61c7c260a774367c 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -1128,7 +1128,7 @@ enum nft_log_level { NFT_LOGLEVEL_AUDIT, __NFT_LOGLEVEL_MAX }; -#define NFT_LOGLEVEL_MAX (__NFT_LOGLEVEL_MAX + 1) +#define NFT_LOGLEVEL_MAX (__NFT_LOGLEVEL_MAX - 1) /** * enum nft_queue_attributes - nf_tables queue expression netlink attributes diff --git a/include/uapi/linux/random.h b/include/uapi/linux/random.h index 26ee91300e3ecbb2d5f8c18db6231343c09944bd..dcc1b3e6106fe20de80d06d0c842fdb12fb561e2 100644 --- a/include/uapi/linux/random.h +++ b/include/uapi/linux/random.h @@ -48,9 +48,11 @@ struct rand_pool_info { * Flags for getrandom(2) * * GRND_NONBLOCK Don't block and return EAGAIN instead - * GRND_RANDOM Use the /dev/random pool instead of /dev/urandom + * GRND_RANDOM No effect + * GRND_INSECURE Return non-cryptographic random bytes */ #define GRND_NONBLOCK 0x0001 #define GRND_RANDOM 0x0002 +#define GRND_INSECURE 0x0004 #endif /* _UAPI_LINUX_RANDOM_H */ diff --git a/include/uapi/linux/sched.h b/include/uapi/linux/sched.h index ed4ee170bee2a5e7dc0c3cde35e1c5549799c427..617bb59aa8bac70724b57ec373b74bef19f8e3c5 100644 --- a/include/uapi/linux/sched.h +++ b/include/uapi/linux/sched.h @@ -51,9 +51,21 @@ #define SCHED_FLAG_RESET_ON_FORK 0x01 #define SCHED_FLAG_RECLAIM 0x02 #define SCHED_FLAG_DL_OVERRUN 0x04 +#define SCHED_FLAG_KEEP_POLICY 0x08 +#define SCHED_FLAG_KEEP_PARAMS 0x10 +#define SCHED_FLAG_UTIL_CLAMP_MIN 0x20 +#define SCHED_FLAG_UTIL_CLAMP_MAX 0x40 + +#define SCHED_FLAG_KEEP_ALL (SCHED_FLAG_KEEP_POLICY | \ + SCHED_FLAG_KEEP_PARAMS) + +#define SCHED_FLAG_UTIL_CLAMP (SCHED_FLAG_UTIL_CLAMP_MIN | \ + SCHED_FLAG_UTIL_CLAMP_MAX) #define SCHED_FLAG_ALL (SCHED_FLAG_RESET_ON_FORK | \ SCHED_FLAG_RECLAIM | \ - SCHED_FLAG_DL_OVERRUN) + SCHED_FLAG_DL_OVERRUN | \ + SCHED_FLAG_KEEP_ALL | \ + SCHED_FLAG_UTIL_CLAMP) #endif /* _UAPI_LINUX_SCHED_H */ diff --git a/include/uapi/linux/sched/types.h b/include/uapi/linux/sched/types.h index 10fbb8031930045d4dc04879c4fccf4bf372ac86..c852153ddb0d3890111d399ae87a9fb34585c8ca 100644 --- a/include/uapi/linux/sched/types.h +++ b/include/uapi/linux/sched/types.h @@ -9,6 +9,7 @@ struct sched_param { }; #define SCHED_ATTR_SIZE_VER0 48 /* sizeof first published struct */ +#define SCHED_ATTR_SIZE_VER1 56 /* add: util_{min,max} */ /* * Extended scheduling parameters data structure. @@ -21,8 +22,33 @@ struct sched_param { * the tasks may be useful for a wide variety of application fields, e.g., * multimedia, streaming, automation and control, and many others. * - * This variant (sched_attr) is meant at describing a so-called - * sporadic time-constrained task. In such model a task is specified by: + * This variant (sched_attr) allows to define additional attributes to + * improve the scheduler knowledge about task requirements. + * + * Scheduling Class Attributes + * =========================== + * + * A subset of sched_attr attributes specifies the + * scheduling policy and relative POSIX attributes: + * + * @size size of the structure, for fwd/bwd compat. + * + * @sched_policy task's scheduling policy + * @sched_nice task's nice value (SCHED_NORMAL/BATCH) + * @sched_priority task's static priority (SCHED_FIFO/RR) + * + * Certain more advanced scheduling features can be controlled by a + * predefined set of flags via the attribute: + * + * @sched_flags for customizing the scheduler behaviour + * + * Sporadic Time-Constrained Task Attributes + * ========================================= + * + * A subset of sched_attr attributes allows to describe a so-called + * sporadic time-constrained task. + * + * In such a model a task is specified by: * - the activation period or minimum instance inter-arrival time; * - the maximum (or average, depending on the actual scheduling * discipline) computation time of all instances, a.k.a. runtime; @@ -34,14 +60,8 @@ struct sched_param { * than the runtime and must be completed by time instant t equal to * the instance activation time + the deadline. * - * This is reflected by the actual fields of the sched_attr structure: + * This is reflected by the following fields of the sched_attr structure: * - * @size size of the structure, for fwd/bwd compat. - * - * @sched_policy task's scheduling policy - * @sched_flags for customizing the scheduler behaviour - * @sched_nice task's nice value (SCHED_NORMAL/BATCH) - * @sched_priority task's static priority (SCHED_FIFO/RR) * @sched_deadline representative of the task's deadline * @sched_runtime representative of the task's runtime * @sched_period representative of the task's period @@ -53,6 +73,29 @@ struct sched_param { * As of now, the SCHED_DEADLINE policy (sched_dl scheduling class) is the * only user of this new interface. More information about the algorithm * available in the scheduling class file or in Documentation/. + * + * Task Utilization Attributes + * =========================== + * + * A subset of sched_attr attributes allows to specify the utilization + * expected for a task. These attributes allow to inform the scheduler about + * the utilization boundaries within which it should schedule the task. These + * boundaries are valuable hints to support scheduler decisions on both task + * placement and frequency selection. + * + * @sched_util_min represents the minimum utilization + * @sched_util_max represents the maximum utilization + * + * Utilization is a value in the range [0..SCHED_CAPACITY_SCALE]. It + * represents the percentage of CPU time used by a task when running at the + * maximum frequency on the highest capacity CPU of the system. For example, a + * 20% utilization task is a task running for 2ms every 10ms at maximum + * frequency. + * + * A task with a min utilization value bigger than 0 is more likely scheduled + * on a CPU with a capacity big enough to fit the specified value. + * A task with a max utilization value smaller than 1024 is more likely + * scheduled on a CPU with no more capacity than the specified value. */ struct sched_attr { __u32 size; @@ -70,6 +113,11 @@ struct sched_attr { __u64 sched_runtime; __u64 sched_deadline; __u64 sched_period; + + /* Utilization hints */ + __u32 sched_util_min; + __u32 sched_util_max; + }; #endif /* _UAPI_LINUX_SCHED_TYPES_H */ diff --git a/include/uapi/linux/stat.h b/include/uapi/linux/stat.h index 7b35e98d3c58b1df9f4315e3f76a2564c25a106b..ad80a5c885d598231ccf207ac48d2d908eaf1b85 100644 --- a/include/uapi/linux/stat.h +++ b/include/uapi/linux/stat.h @@ -167,8 +167,8 @@ struct statx { #define STATX_ATTR_APPEND 0x00000020 /* [I] File is append-only */ #define STATX_ATTR_NODUMP 0x00000040 /* [I] File is not to be dumped */ #define STATX_ATTR_ENCRYPTED 0x00000800 /* [I] File requires key to decrypt in fs */ - #define STATX_ATTR_AUTOMOUNT 0x00001000 /* Dir: Automount trigger */ +#define STATX_ATTR_VERITY 0x00100000 /* [I] Verity protected file */ #endif /* _UAPI_LINUX_STAT_H */ diff --git a/include/uapi/linux/usb/charger.h b/include/uapi/linux/usb/charger.h index 5f72af35b3ed768fd3bc1e2d8ca3f4f6ecaea47f..ad22079125bff2ba34f03b74e6cfd5e2eb460479 100644 --- a/include/uapi/linux/usb/charger.h +++ b/include/uapi/linux/usb/charger.h @@ -14,18 +14,18 @@ * ACA (Accessory Charger Adapters) */ enum usb_charger_type { - UNKNOWN_TYPE, - SDP_TYPE, - DCP_TYPE, - CDP_TYPE, - ACA_TYPE, + UNKNOWN_TYPE = 0, + SDP_TYPE = 1, + DCP_TYPE = 2, + CDP_TYPE = 3, + ACA_TYPE = 4, }; /* USB charger state */ enum usb_charger_state { - USB_CHARGER_DEFAULT, - USB_CHARGER_PRESENT, - USB_CHARGER_ABSENT, + USB_CHARGER_DEFAULT = 0, + USB_CHARGER_PRESENT = 1, + USB_CHARGER_ABSENT = 2, }; #endif /* _UAPI__LINUX_USB_CHARGER_H */ diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h index 6d5c3b2d4f4d55368564e6e2ce003a1cb0b6d33b..32b2f94d1f58c2fadf2e34ebb259c374f02339b9 100644 --- a/include/uapi/linux/virtio_ids.h +++ b/include/uapi/linux/virtio_ids.h @@ -43,5 +43,6 @@ #define VIRTIO_ID_INPUT 18 /* virtio input */ #define VIRTIO_ID_VSOCK 19 /* virtio vsock transport */ #define VIRTIO_ID_CRYPTO 20 /* virtio crypto */ +#define VIRTIO_ID_PMEM 27 /* virtio pmem */ #endif /* _LINUX_VIRTIO_IDS_H */ diff --git a/include/uapi/linux/virtio_pmem.h b/include/uapi/linux/virtio_pmem.h new file mode 100644 index 0000000000000000000000000000000000000000..efcd72f2d20d5f41aadd7c8a58ebbe55f454e2f5 --- /dev/null +++ b/include/uapi/linux/virtio_pmem.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Definitions for virtio-pmem devices. + * + * Copyright (C) 2019 Red Hat, Inc. + * + * Author(s): Pankaj Gupta + */ + +#ifndef _UAPI_LINUX_VIRTIO_PMEM_H +#define _UAPI_LINUX_VIRTIO_PMEM_H + +#include +#include +#include + +struct virtio_pmem_config { + __u64 start; + __u64 size; +}; + +#define VIRTIO_PMEM_REQ_TYPE_FLUSH 0 + +struct virtio_pmem_resp { + /* Host return status corresponding to flush request */ + __u32 ret; +}; + +struct virtio_pmem_req { + /* command type */ + __u32 type; +}; + +#endif diff --git a/init/Kconfig b/init/Kconfig index 5fe5082c4ff271de683c42fd783bea1cbb98fd46..ff9c4c6381c3644e3078147f7b18a953476a231d 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -709,6 +709,59 @@ config HAVE_UNSTABLE_SCHED_CLOCK config GENERIC_SCHED_CLOCK bool +menu "Scheduler features" + +config UCLAMP_TASK + bool "Enable utilization clamping for RT/FAIR tasks" + depends on CPU_FREQ_GOV_SCHEDUTIL + help + This feature enables the scheduler to track the clamped utilization + of each CPU based on RUNNABLE tasks scheduled on that CPU. + + With this option, the user can specify the min and max CPU + utilization allowed for RUNNABLE tasks. The max utilization defines + the maximum frequency a task should use while the min utilization + defines the minimum frequency it should use. + + Both min and max utilization clamp values are hints to the scheduler, + aiming at improving its frequency selection policy, but they do not + enforce or grant any specific bandwidth for tasks. + + If in doubt, say N. + +config UCLAMP_BUCKETS_COUNT + int "Number of supported utilization clamp buckets" + range 5 20 + default 5 + depends on UCLAMP_TASK + help + Defines the number of clamp buckets to use. The range of each bucket + will be SCHED_CAPACITY_SCALE/UCLAMP_BUCKETS_COUNT. The higher the + number of clamp buckets the finer their granularity and the higher + the precision of clamping aggregation and tracking at run-time. + + For example, with the minimum configuration value we will have 5 + clamp buckets tracking 20% utilization each. A 25% boosted tasks will + be refcounted in the [20..39]% bucket and will set the bucket clamp + effective value to 25%. + If a second 30% boosted task should be co-scheduled on the same CPU, + that task will be refcounted in the same bucket of the first task and + it will boost the bucket clamp effective value to 30%. + The clamp effective value of a bucket is reset to its nominal value + (20% in the example above) when there are no more tasks refcounted in + that bucket. + + An additional boost/capping margin can be added to some tasks. In the + example above the 25% task will be boosted to 30% until it exits the + CPU. If that should be considered not acceptable on certain systems, + it's always possible to reduce the margin by increasing the number of + clamp buckets to trade off used memory for run-time tracking + precision. + + If in doubt, use the default value. + +endmenu + # # For architectures that want to enable the support for NUMA-affine scheduler # balancing logic: @@ -882,6 +935,28 @@ config RT_GROUP_SCHED endif #CGROUP_SCHED +config UCLAMP_TASK_GROUP + bool "Utilization clamping per group of tasks" + depends on CGROUP_SCHED + depends on UCLAMP_TASK + default n + help + This feature enables the scheduler to track the clamped utilization + of each CPU based on RUNNABLE tasks currently scheduled on that CPU. + + When this option is enabled, the user can specify a min and max + CPU bandwidth which is allowed for each single task in a group. + The max bandwidth allows to clamp the maximum frequency a task + can use, while the min bandwidth allows to define a minimum + frequency a task will always use. + + When task group based utilization clamping is enabled, an eventually + specified task-specific clamp value is constrained by the cgroup + specified clamp value. Both minimum and maximum task clamping cannot + be bigger than the corresponding clamping defined at task group level. + + If in doubt, say N. + config CGROUP_PIDS bool "PIDs controller" help @@ -1099,6 +1174,7 @@ config SCHED_AUTOGROUP config SCHED_TUNE bool "Boosting for CFS tasks (EXPERIMENTAL)" + depends on !UCLAMP_TASK depends on SMP help This option enables support for task classification using a new @@ -2098,6 +2174,19 @@ config TRIM_UNUSED_KSYMS If unsure, or if you need to build out-of-tree modules, say N. +config UNUSED_KSYMS_WHITELIST + string "Whitelist of symbols to keep in ksymtab" + depends on TRIM_UNUSED_KSYMS + help + By default, all unused exported symbols will be un-exported from the + build when TRIM_UNUSED_KSYMS is selected. + + UNUSED_KSYMS_WHITELIST allows to whitelist symbols that must be kept + exported at all times, even in absence of in-tree users. The value to + set here is the path to a text file containing the list of symbols, + one per line. The path can be absolute, or relative to the kernel + source tree. + endif # MODULES config MODULES_TREE_LOOKUP diff --git a/init/Kconfig.gki b/init/Kconfig.gki index 7bd91be205da4556f97d12045b3637a474e2b4c8..e4e6de184d745e5790b19f5720a80066d50eac80 100644 --- a/init/Kconfig.gki +++ b/init/Kconfig.gki @@ -7,7 +7,7 @@ config GKI_HIDDEN_DRM_CONFIGS select VIDEOMODE_HELPERS help Dummy config option used to enable hidden DRM configs. - These are normally selected implicitely when including a + These are normally selected implicitly when including a DRM module, but for GKI, the modules are built out-of-tree. config GKI_HIDDEN_REGMAP_CONFIGS @@ -15,7 +15,26 @@ config GKI_HIDDEN_REGMAP_CONFIGS select REGMAP_IRQ help Dummy config option used to enable hidden regmap configs. - These are normally selected implicitely when a module + These are normally selected implicitly when a module + that relies on it is configured. + +config GKI_HIDDEN_CRYPTO_CONFIGS + bool "Hidden CRYPTO configs needed for GKI" + select CRYPTO_ENGINE + help + Dummy config option used to enable hidden CRYPTO configs. + These are normally selected implicitly when a module + that relies on it is configured. + +config GKI_HIDDEN_SND_CONFIGS + bool "Hidden SND configs needed for GKI" + select SND_VMASTER + select SND_PCM_ELD + select SND_JACK + select SND_JACK_INPUT_DEV + help + Dummy config option used to enable hidden SND configs. + These are normally selected implicitly when a module that relies on it is configured. config GKI_HIDDEN_SND_SOC_CONFIGS @@ -23,7 +42,7 @@ config GKI_HIDDEN_SND_SOC_CONFIGS select SND_SOC_GENERIC_DMAENGINE_PCM if (SND_SOC && SND) help Dummy config option used to enable hidden SND_SOC configs. - These are normally selected implicitely when a module + These are normally selected implicitly when a module that relies on it is configured. config GKI_HIDDEN_GPIO_CONFIGS @@ -32,7 +51,15 @@ config GKI_HIDDEN_GPIO_CONFIGS select GPIO_PL061 if (HAS_IOMEM && ARM_AMBA && GPIOLIB) help Dummy config option used to enable hidden GPIO configs. - These are normally selected implicitely when a module + These are normally selected implicitly when a module + that relies on it is configured. + +config GKI_HIDDEN_VIRTUAL_CONFIGS + bool "Hidden Virtual configs needed for GKI" + select HVC_DRIVER + help + Dummy config option used to enable hidden virtual device configs. + These are normally selected implicitly when a module that relies on it is configured. # LEGACY_WEXT_ALLCONFIG Discussed upstream, soundly rejected as a unique @@ -54,6 +81,13 @@ config GKI_LEGACY_WEXT_ALLCONFIG If you are not building a kernel to be used for a variety of out-of-kernel built wireless modules, say N here. +config GKI_HIDDEN_SOC_PM_CONFIGS + bool "Hidden SOC Power Management configs for GKI" + select PM_GENERIC_DOMAINS if PM + help + Dummy config option used to enable all the hidden SOC specific power + management add-in modules. + # Atrocities needed for # a) building GKI modules in separate tree, or # b) building drivers that are not modularizable @@ -63,11 +97,15 @@ config GKI_LEGACY_WEXT_ALLCONFIG # config GKI_HACKS_TO_FIX bool "GKI Dummy config options" + select GKI_HIDDEN_CRYPTO_CONFIGS select GKI_HIDDEN_DRM_CONFIGS select GKI_HIDDEN_REGMAP_CONFIGS + select GKI_HIDDEN_SND_CONFIGS select GKI_HIDDEN_SND_SOC_CONFIGS select GKI_HIDDEN_GPIO_CONFIGS select GKI_LEGACY_WEXT_ALLCONFIG + select GKI_HIDDEN_VIRTUAL_CONFIGS + select GKI_HIDDEN_SOC_PM_CONFIGS help Dummy config option used to enable core functionality used by modules that may not be selectable in this config. diff --git a/init/main.c b/init/main.c index bc440bb86fa9fe92d8a8bc354b57f030dcb25961..7942ad3cdb428f0527a18ae37f4aaa1cf982afc9 100644 --- a/init/main.c +++ b/init/main.c @@ -573,13 +573,6 @@ asmlinkage __visible void __init start_kernel(void) page_address_init(); pr_notice("%s", linux_banner); setup_arch(&command_line); - /* - * Set up the the initial canary and entropy after arch - * and after adding latent and command line entropy. - */ - add_latent_entropy(); - add_device_randomness(command_line, strlen(command_line)); - boot_init_stack_canary(); mm_init_cpumask(&init_mm); setup_command_line(command_line); setup_nr_cpu_ids(); @@ -664,6 +657,20 @@ asmlinkage __visible void __init start_kernel(void) hrtimers_init(); softirq_init(); timekeeping_init(); + + /* + * For best initial stack canary entropy, prepare it after: + * - setup_arch() for any UEFI RNG entropy and boot cmdline access + * - timekeeping_init() for ktime entropy used in rand_initialize() + * - rand_initialize() to get any arch-specific entropy like RDRAND + * - add_latent_entropy() to get any latent entropy + * - adding command line entropy + */ + rand_initialize(); + add_latent_entropy(); + add_device_randomness(command_line, strlen(command_line)); + boot_init_stack_canary(); + time_init(); printk_safe_init(); perf_event_init(); diff --git a/ipc/msg.c b/ipc/msg.c index 883642cf2b2767ae7c05b9eb19841d5f16c230ca..ac4de3f672615f928f3095178b670464f63f4428 100644 --- a/ipc/msg.c +++ b/ipc/msg.c @@ -377,7 +377,7 @@ copy_msqid_from_user(struct msqid64_ds *out, void __user *buf, int version) * NOTE: no locks must be held, the rwsem is taken inside this function. */ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd, - struct msqid64_ds *msqid64) + struct ipc64_perm *perm, int msg_qbytes) { struct kern_ipc_perm *ipcp; struct msg_queue *msq; @@ -387,7 +387,7 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd, rcu_read_lock(); ipcp = ipcctl_obtain_check(ns, &msg_ids(ns), msqid, cmd, - &msqid64->msg_perm, msqid64->msg_qbytes); + perm, msg_qbytes); if (IS_ERR(ipcp)) { err = PTR_ERR(ipcp); goto out_unlock1; @@ -409,18 +409,18 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd, { DEFINE_WAKE_Q(wake_q); - if (msqid64->msg_qbytes > ns->msg_ctlmnb && + if (msg_qbytes > ns->msg_ctlmnb && !capable(CAP_SYS_RESOURCE)) { err = -EPERM; goto out_unlock1; } ipc_lock_object(&msq->q_perm); - err = ipc_update_perm(&msqid64->msg_perm, ipcp); + err = ipc_update_perm(perm, ipcp); if (err) goto out_unlock0; - msq->q_qbytes = msqid64->msg_qbytes; + msq->q_qbytes = msg_qbytes; msq->q_ctime = ktime_get_real_seconds(); /* @@ -603,9 +603,10 @@ long ksys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf) case IPC_SET: if (copy_msqid_from_user(&msqid64, buf, version)) return -EFAULT; - /* fallthru */ + return msgctl_down(ns, msqid, cmd, &msqid64.msg_perm, + msqid64.msg_qbytes); case IPC_RMID: - return msgctl_down(ns, msqid, cmd, &msqid64); + return msgctl_down(ns, msqid, cmd, NULL, 0); default: return -EINVAL; } @@ -724,9 +725,9 @@ long compat_ksys_msgctl(int msqid, int cmd, void __user *uptr) case IPC_SET: if (copy_compat_msqid_from_user(&msqid64, uptr, version)) return -EFAULT; - /* fallthru */ + return msgctl_down(ns, msqid, cmd, &msqid64.msg_perm, msqid64.msg_qbytes); case IPC_RMID: - return msgctl_down(ns, msqid, cmd, &msqid64); + return msgctl_down(ns, msqid, cmd, NULL, 0); default: return -EINVAL; } diff --git a/ipc/sem.c b/ipc/sem.c index 26f8e37fcdcbcf428e8f03396625272fae763023..2bf535dd0b93481a631165d1894d3bb25170c30f 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -2345,11 +2345,9 @@ void exit_sem(struct task_struct *tsk) ipc_assert_locked_object(&sma->sem_perm); list_del(&un->list_id); - /* we are the last process using this ulp, acquiring ulp->lock - * isn't required. Besides that, we are also protected against - * IPC_RMID as we hold sma->sem_perm lock now - */ + spin_lock(&ulp->lock); list_del_rcu(&un->list_proc); + spin_unlock(&ulp->lock); /* perform adjustments registered in un */ for (i = 0; i < sma->sem_nsems; i++) { diff --git a/kernel/audit.c b/kernel/audit.c index 2a8058764aa62406097022ec240b5e0e1cf57dfa..1f08c38e604a92d3aa7a65b4960d9350309c88e6 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -1106,13 +1106,11 @@ static void audit_log_feature_change(int which, u32 old_feature, u32 new_feature audit_log_end(ab); } -static int audit_set_feature(struct sk_buff *skb) +static int audit_set_feature(struct audit_features *uaf) { - struct audit_features *uaf; int i; BUILD_BUG_ON(AUDIT_LAST_FEATURE + 1 > ARRAY_SIZE(audit_feature_names)); - uaf = nlmsg_data(nlmsg_hdr(skb)); /* if there is ever a version 2 we should handle that here */ @@ -1180,6 +1178,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) { u32 seq; void *data; + int data_len; int err; struct audit_buffer *ab; u16 msg_type = nlh->nlmsg_type; @@ -1193,6 +1192,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) seq = nlh->nlmsg_seq; data = nlmsg_data(nlh); + data_len = nlmsg_len(nlh); switch (msg_type) { case AUDIT_GET: { @@ -1216,7 +1216,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) struct audit_status s; memset(&s, 0, sizeof(s)); /* guard against past and future API changes */ - memcpy(&s, data, min_t(size_t, sizeof(s), nlmsg_len(nlh))); + memcpy(&s, data, min_t(size_t, sizeof(s), data_len)); if (s.mask & AUDIT_STATUS_ENABLED) { err = audit_set_enabled(s.enabled); if (err < 0) @@ -1320,7 +1320,9 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) return err; break; case AUDIT_SET_FEATURE: - err = audit_set_feature(skb); + if (data_len < sizeof(struct audit_features)) + return -EINVAL; + err = audit_set_feature(data); if (err) return err; break; @@ -1332,6 +1334,8 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) err = audit_filter(msg_type, AUDIT_FILTER_USER); if (err == 1) { /* match or error */ + char *str = data; + err = 0; if (msg_type == AUDIT_USER_TTY) { err = tty_audit_push(); @@ -1339,26 +1343,24 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) break; } audit_log_common_recv_msg(&ab, msg_type); - if (msg_type != AUDIT_USER_TTY) + if (msg_type != AUDIT_USER_TTY) { + /* ensure NULL termination */ + str[data_len - 1] = '\0'; audit_log_format(ab, " msg='%.*s'", AUDIT_MESSAGE_TEXT_MAX, - (char *)data); - else { - int size; - + str); + } else { audit_log_format(ab, " data="); - size = nlmsg_len(nlh); - if (size > 0 && - ((unsigned char *)data)[size - 1] == '\0') - size--; - audit_log_n_untrustedstring(ab, data, size); + if (data_len > 0 && str[data_len - 1] == '\0') + data_len--; + audit_log_n_untrustedstring(ab, str, data_len); } audit_log_end(ab); } break; case AUDIT_ADD_RULE: case AUDIT_DEL_RULE: - if (nlmsg_len(nlh) < sizeof(struct audit_rule_data)) + if (data_len < sizeof(struct audit_rule_data)) return -EINVAL; if (audit_enabled == AUDIT_LOCKED) { audit_log_common_recv_msg(&ab, AUDIT_CONFIG_CHANGE); @@ -1366,7 +1368,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) audit_log_end(ab); return -EPERM; } - err = audit_rule_change(msg_type, seq, data, nlmsg_len(nlh)); + err = audit_rule_change(msg_type, seq, data, data_len); break; case AUDIT_LIST_RULES: err = audit_list_rules_send(skb, seq); @@ -1380,7 +1382,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) case AUDIT_MAKE_EQUIV: { void *bufp = data; u32 sizes[2]; - size_t msglen = nlmsg_len(nlh); + size_t msglen = data_len; char *old, *new; err = -EINVAL; @@ -1456,7 +1458,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) memset(&s, 0, sizeof(s)); /* guard against past and future API changes */ - memcpy(&s, data, min_t(size_t, sizeof(s), nlmsg_len(nlh))); + memcpy(&s, data, min_t(size_t, sizeof(s), data_len)); /* check if new data is valid */ if ((s.enabled != 0 && s.enabled != 1) || (s.log_passwd != 0 && s.log_passwd != 1)) diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index 425c67e4f5681a7fc0947ff916138a905a19939c..1c8a48abda80d948d1c86112a93011b68552eaad 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -452,6 +452,7 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, bufp = data->buf; for (i = 0; i < data->field_count; i++) { struct audit_field *f = &entry->rule.fields[i]; + u32 f_val; err = -EINVAL; @@ -460,12 +461,12 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, goto exit_free; f->type = data->fields[i]; - f->val = data->values[i]; + f_val = data->values[i]; /* Support legacy tests for a valid loginuid */ - if ((f->type == AUDIT_LOGINUID) && (f->val == AUDIT_UID_UNSET)) { + if ((f->type == AUDIT_LOGINUID) && (f_val == AUDIT_UID_UNSET)) { f->type = AUDIT_LOGINUID_SET; - f->val = 0; + f_val = 0; entry->rule.pflags |= AUDIT_LOGINUID_LEGACY; } @@ -481,7 +482,7 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, case AUDIT_SUID: case AUDIT_FSUID: case AUDIT_OBJ_UID: - f->uid = make_kuid(current_user_ns(), f->val); + f->uid = make_kuid(current_user_ns(), f_val); if (!uid_valid(f->uid)) goto exit_free; break; @@ -490,11 +491,12 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, case AUDIT_SGID: case AUDIT_FSGID: case AUDIT_OBJ_GID: - f->gid = make_kgid(current_user_ns(), f->val); + f->gid = make_kgid(current_user_ns(), f_val); if (!gid_valid(f->gid)) goto exit_free; break; case AUDIT_ARCH: + f->val = f_val; entry->rule.arch_f = f; break; case AUDIT_SUBJ_USER: @@ -507,11 +509,13 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, case AUDIT_OBJ_TYPE: case AUDIT_OBJ_LEV_LOW: case AUDIT_OBJ_LEV_HIGH: - str = audit_unpack_string(&bufp, &remain, f->val); - if (IS_ERR(str)) + str = audit_unpack_string(&bufp, &remain, f_val); + if (IS_ERR(str)) { + err = PTR_ERR(str); goto exit_free; - entry->rule.buflen += f->val; - + } + entry->rule.buflen += f_val; + f->lsm_str = str; err = security_audit_rule_init(f->type, f->op, str, (void **)&f->lsm_rule); /* Keep currently invalid fields around in case they @@ -520,68 +524,71 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, pr_warn("audit rule for LSM \'%s\' is invalid\n", str); err = 0; - } - if (err) { - kfree(str); + } else if (err) goto exit_free; - } else - f->lsm_str = str; break; case AUDIT_WATCH: - str = audit_unpack_string(&bufp, &remain, f->val); - if (IS_ERR(str)) + str = audit_unpack_string(&bufp, &remain, f_val); + if (IS_ERR(str)) { + err = PTR_ERR(str); goto exit_free; - entry->rule.buflen += f->val; - - err = audit_to_watch(&entry->rule, str, f->val, f->op); + } + err = audit_to_watch(&entry->rule, str, f_val, f->op); if (err) { kfree(str); goto exit_free; } + entry->rule.buflen += f_val; break; case AUDIT_DIR: - str = audit_unpack_string(&bufp, &remain, f->val); - if (IS_ERR(str)) + str = audit_unpack_string(&bufp, &remain, f_val); + if (IS_ERR(str)) { + err = PTR_ERR(str); goto exit_free; - entry->rule.buflen += f->val; - + } err = audit_make_tree(&entry->rule, str, f->op); kfree(str); if (err) goto exit_free; + entry->rule.buflen += f_val; break; case AUDIT_INODE: + f->val = f_val; err = audit_to_inode(&entry->rule, f); if (err) goto exit_free; break; case AUDIT_FILTERKEY: - if (entry->rule.filterkey || f->val > AUDIT_MAX_KEY_LEN) + if (entry->rule.filterkey || f_val > AUDIT_MAX_KEY_LEN) goto exit_free; - str = audit_unpack_string(&bufp, &remain, f->val); - if (IS_ERR(str)) + str = audit_unpack_string(&bufp, &remain, f_val); + if (IS_ERR(str)) { + err = PTR_ERR(str); goto exit_free; - entry->rule.buflen += f->val; + } + entry->rule.buflen += f_val; entry->rule.filterkey = str; break; case AUDIT_EXE: - if (entry->rule.exe || f->val > PATH_MAX) + if (entry->rule.exe || f_val > PATH_MAX) goto exit_free; - str = audit_unpack_string(&bufp, &remain, f->val); + str = audit_unpack_string(&bufp, &remain, f_val); if (IS_ERR(str)) { err = PTR_ERR(str); goto exit_free; } - entry->rule.buflen += f->val; - - audit_mark = audit_alloc_mark(&entry->rule, str, f->val); + audit_mark = audit_alloc_mark(&entry->rule, str, f_val); if (IS_ERR(audit_mark)) { kfree(str); err = PTR_ERR(audit_mark); goto exit_free; } + entry->rule.buflen += f_val; entry->rule.exe = audit_mark; break; + default: + f->val = f_val; + break; } } diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c index dc9d7ac8228db065617004e49cbd621de6ee5fb1..c04815bb15cc12230f0124de428fb88e5d6f6b4e 100644 --- a/kernel/bpf/inode.c +++ b/kernel/bpf/inode.c @@ -198,6 +198,7 @@ static void *map_seq_next(struct seq_file *m, void *v, loff_t *pos) void *key = map_iter(m)->key; void *prev_key; + (*pos)++; if (map_iter(m)->done) return NULL; @@ -210,8 +211,6 @@ static void *map_seq_next(struct seq_file *m, void *v, loff_t *pos) map_iter(m)->done = true; return NULL; } - - ++(*pos); return key; } diff --git a/kernel/bpf/offload.c b/kernel/bpf/offload.c index 177a524363942e0c66079cf51b0f760c98bf8031..66e13aace2411c0cb1de10d66957e123c672de8f 100644 --- a/kernel/bpf/offload.c +++ b/kernel/bpf/offload.c @@ -289,7 +289,7 @@ int bpf_prog_offload_info_fill(struct bpf_prog_info *info, ulen = info->jited_prog_len; info->jited_prog_len = aux->offload->jited_len; - if (info->jited_prog_len & ulen) { + if (info->jited_prog_len && ulen) { uinsns = u64_to_user_ptr(info->jited_prog_insns); ulen = min_t(u32, info->jited_prog_len, ulen); if (copy_to_user(uinsns, aux->offload->jited_image, ulen)) { @@ -645,8 +645,10 @@ struct bpf_offload_dev *bpf_offload_dev_create(void) down_write(&bpf_devs_lock); if (!offdevs_inited) { err = rhashtable_init(&offdevs, &offdevs_params); - if (err) + if (err) { + up_write(&bpf_devs_lock); return ERR_PTR(err); + } offdevs_inited = true; } up_write(&bpf_devs_lock); diff --git a/kernel/bpf/tnum.c b/kernel/bpf/tnum.c index 938d41211be70cde6e18446ec956907f07309d39..84984c0fc3d3574de69bcba01ac32d6468f3d94f 100644 --- a/kernel/bpf/tnum.c +++ b/kernel/bpf/tnum.c @@ -43,14 +43,19 @@ struct tnum tnum_rshift(struct tnum a, u8 shift) return TNUM(a.value >> shift, a.mask >> shift); } -struct tnum tnum_arshift(struct tnum a, u8 min_shift) +struct tnum tnum_arshift(struct tnum a, u8 min_shift, u8 insn_bitness) { /* if a.value is negative, arithmetic shifting by minimum shift * will have larger negative offset compared to more shifting. * If a.value is nonnegative, arithmetic shifting by minimum shift * will have larger positive offset compare to more shifting. */ - return TNUM((s64)a.value >> min_shift, (s64)a.mask >> min_shift); + if (insn_bitness == 32) + return TNUM((u32)(((s32)a.value) >> min_shift), + (u32)(((s32)a.mask) >> min_shift)); + else + return TNUM((s64)a.value >> min_shift, + (s64)a.mask >> min_shift); } struct tnum tnum_add(struct tnum a, struct tnum b) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 9e72b2f8c3dd3bffa5f55f690afab09ad4add79f..e85636fb81b9c2db7d61b0a5755984a71d96fe8b 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -1253,7 +1253,7 @@ static int check_stack_access(struct bpf_verifier_env *env, char tn_buf[48]; tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off); - verbose(env, "variable stack access var_off=%s off=%d size=%d", + verbose(env, "variable stack access var_off=%s off=%d size=%d\n", tn_buf, off, size); return -EACCES; } @@ -3309,9 +3309,16 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env, /* Upon reaching here, src_known is true and * umax_val is equal to umin_val. */ - dst_reg->smin_value >>= umin_val; - dst_reg->smax_value >>= umin_val; - dst_reg->var_off = tnum_arshift(dst_reg->var_off, umin_val); + if (insn_bitness == 32) { + dst_reg->smin_value = (u32)(((s32)dst_reg->smin_value) >> umin_val); + dst_reg->smax_value = (u32)(((s32)dst_reg->smax_value) >> umin_val); + } else { + dst_reg->smin_value >>= umin_val; + dst_reg->smax_value >>= umin_val; + } + + dst_reg->var_off = tnum_arshift(dst_reg->var_off, umin_val, + insn_bitness); /* blow away the dst_reg umin_value/umax_value and rely on * dst_reg var_off to refine the result. diff --git a/kernel/capability.c b/kernel/capability.c index 1e1c0236f55b5b05b54caa49dca62c1b7e2d5819..7718d7dcadc7abf9d2bd62cf5e7b84aeba351ea3 100644 --- a/kernel/capability.c +++ b/kernel/capability.c @@ -299,7 +299,7 @@ bool has_ns_capability(struct task_struct *t, int ret; rcu_read_lock(); - ret = security_capable(__task_cred(t), ns, cap); + ret = security_capable(__task_cred(t), ns, cap, CAP_OPT_NONE); rcu_read_unlock(); return (ret == 0); @@ -340,7 +340,7 @@ bool has_ns_capability_noaudit(struct task_struct *t, int ret; rcu_read_lock(); - ret = security_capable_noaudit(__task_cred(t), ns, cap); + ret = security_capable(__task_cred(t), ns, cap, CAP_OPT_NOAUDIT); rcu_read_unlock(); return (ret == 0); @@ -363,7 +363,9 @@ bool has_capability_noaudit(struct task_struct *t, int cap) return has_ns_capability_noaudit(t, &init_user_ns, cap); } -static bool ns_capable_common(struct user_namespace *ns, int cap, bool audit) +static bool ns_capable_common(struct user_namespace *ns, + int cap, + unsigned int opts) { int capable; @@ -372,8 +374,7 @@ static bool ns_capable_common(struct user_namespace *ns, int cap, bool audit) BUG(); } - capable = audit ? security_capable(current_cred(), ns, cap) : - security_capable_noaudit(current_cred(), ns, cap); + capable = security_capable(current_cred(), ns, cap, opts); if (capable == 0) { current->flags |= PF_SUPERPRIV; return true; @@ -394,7 +395,7 @@ static bool ns_capable_common(struct user_namespace *ns, int cap, bool audit) */ bool ns_capable(struct user_namespace *ns, int cap) { - return ns_capable_common(ns, cap, true); + return ns_capable_common(ns, cap, CAP_OPT_NONE); } EXPORT_SYMBOL(ns_capable); @@ -412,7 +413,7 @@ EXPORT_SYMBOL(ns_capable); */ bool ns_capable_noaudit(struct user_namespace *ns, int cap) { - return ns_capable_common(ns, cap, false); + return ns_capable_common(ns, cap, CAP_OPT_NOAUDIT); } EXPORT_SYMBOL(ns_capable_noaudit); @@ -448,10 +449,11 @@ EXPORT_SYMBOL(capable); bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap) { + if (WARN_ON_ONCE(!cap_valid(cap))) return false; - if (security_capable(file->f_cred, ns, cap) == 0) + if (security_capable(file->f_cred, ns, cap, CAP_OPT_NONE) == 0) return true; return false; @@ -500,10 +502,12 @@ bool ptracer_capable(struct task_struct *tsk, struct user_namespace *ns) { int ret = 0; /* An absent tracer adds no restrictions */ const struct cred *cred; + rcu_read_lock(); cred = rcu_dereference(tsk->ptracer_cred); if (cred) - ret = security_capable_noaudit(cred, ns, CAP_SYS_PTRACE); + ret = security_capable(cred, ns, CAP_SYS_PTRACE, + CAP_OPT_NOAUDIT); rcu_read_unlock(); return (ret == 0); } diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c index eb8fdf9b7bc4ef542b5356fdab557cf3897e53cd..0045bc16a0302009406ccc8ec333a6c2db8cc4d8 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c @@ -2941,8 +2941,6 @@ static int cgroup_apply_control_enable(struct cgroup *cgrp) for_each_subsys(ss, ssid) { struct cgroup_subsys_state *css = cgroup_css(dsct, ss); - WARN_ON_ONCE(css && percpu_ref_is_dying(&css->refcnt)); - if (!(cgroup_ss_mask(dsct) & (1 << ss->id))) continue; @@ -2952,6 +2950,8 @@ static int cgroup_apply_control_enable(struct cgroup *cgrp) return PTR_ERR(css); } + WARN_ON_ONCE(percpu_ref_is_dying(&css->refcnt)); + if (css_visible(css)) { ret = css_populate_dir(css); if (ret) @@ -2987,11 +2987,11 @@ static void cgroup_apply_control_disable(struct cgroup *cgrp) for_each_subsys(ss, ssid) { struct cgroup_subsys_state *css = cgroup_css(dsct, ss); - WARN_ON_ONCE(css && percpu_ref_is_dying(&css->refcnt)); - if (!css) continue; + WARN_ON_ONCE(percpu_ref_is_dying(&css->refcnt)); + if (css->parent && !(cgroup_ss_mask(dsct) & (1 << ss->id))) { kill_css(css); @@ -3278,7 +3278,8 @@ static ssize_t cgroup_type_write(struct kernfs_open_file *of, char *buf, if (strcmp(strstrip(buf), "threaded")) return -EINVAL; - cgrp = cgroup_kn_lock_live(of->kn, false); + /* drain dying csses before we re-apply (threaded) subtree control */ + cgrp = cgroup_kn_lock_live(of->kn, true); if (!cgrp) return -ENOENT; @@ -6180,4 +6181,47 @@ static int __init cgroup_sysfs_init(void) return sysfs_create_group(kernel_kobj, &cgroup_sysfs_attr_group); } subsys_initcall(cgroup_sysfs_init); + +static u64 power_of_ten(int power) +{ + u64 v = 1; + while (power--) + v *= 10; + return v; +} + +/** + * cgroup_parse_float - parse a floating number + * @input: input string + * @dec_shift: number of decimal digits to shift + * @v: output + * + * Parse a decimal floating point number in @input and store the result in + * @v with decimal point right shifted @dec_shift times. For example, if + * @input is "12.3456" and @dec_shift is 3, *@v will be set to 12345. + * Returns 0 on success, -errno otherwise. + * + * There's nothing cgroup specific about this function except that it's + * currently the only user. + */ +int cgroup_parse_float(const char *input, unsigned dec_shift, s64 *v) +{ + s64 whole, frac = 0; + int fstart = 0, fend = 0, flen; + + if (!sscanf(input, "%lld.%n%lld%n", &whole, &fstart, &frac, &fend)) + return -EINVAL; + if (frac < 0) + return -EINVAL; + + flen = fend > fstart ? fend - fstart : 0; + if (flen < dec_shift) + frac *= power_of_ten(dec_shift - flen); + else + frac = DIV_ROUND_CLOSEST_ULL(frac, power_of_ten(flen - dec_shift)); + + *v = whole * power_of_ten(dec_shift) + frac; + return 0; +} + #endif /* CONFIG_SYSFS */ diff --git a/kernel/cpu.c b/kernel/cpu.c index 430757469dee0945b30aab49853e3a9f91c4c610..75324bc6b6a7e66f3b5a02fde6e05af59d6b4834 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -496,8 +496,7 @@ static int bringup_wait_for_ap(unsigned int cpu) if (WARN_ON_ONCE((!cpu_online(cpu)))) return -ECANCELED; - /* Unpark the stopper thread and the hotplug thread of the target cpu */ - stop_machine_unpark(cpu); + /* Unpark the hotplug thread of the target cpu */ kthread_unpark(st->thread); /* @@ -1071,8 +1070,8 @@ void notify_cpu_starting(unsigned int cpu) /* * Called from the idle task. Wake up the controlling task which brings the - * stopper and the hotplug thread of the upcoming CPU up and then delegates - * the rest of the online bringup to the hotplug thread. + * hotplug thread of the upcoming CPU up and then delegates the rest of the + * online bringup to the hotplug thread. */ void cpuhp_online_idle(enum cpuhp_state state) { @@ -1082,6 +1081,12 @@ void cpuhp_online_idle(enum cpuhp_state state) if (state != CPUHP_AP_ONLINE_IDLE) return; + /* + * Unpart the stopper thread before we start the idle loop (and start + * scheduling); this ensures the stopper task is always available. + */ + stop_machine_unpark(smp_processor_id()); + st->state = CPUHP_AP_ONLINE_IDLE; complete_ap_thread(st, true); } diff --git a/kernel/debug/kdb/kdb_main.c b/kernel/debug/kdb/kdb_main.c index 959242084b404dcb0a2d394eaaf4559152a14514..cdde39438b11ef3175ca5890a9593348cd8ef618 100644 --- a/kernel/debug/kdb/kdb_main.c +++ b/kernel/debug/kdb/kdb_main.c @@ -2599,7 +2599,7 @@ static int kdb_per_cpu(int argc, const char **argv) diag = kdbgetularg(argv[3], &whichcpu); if (diag) return diag; - if (!cpu_online(whichcpu)) { + if (whichcpu >= nr_cpu_ids || !cpu_online(whichcpu)) { kdb_printf("cpu %ld is not online\n", whichcpu); return KDB_BADCPUNUM; } diff --git a/kernel/events/core.c b/kernel/events/core.c index 6b0989936a04221be8f94ec00dadeb3458ffc0e8..fb93388c9f0c891d2937d4cf2df60ea2053f11a6 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -5191,6 +5191,9 @@ static int perf_event_period(struct perf_event *event, u64 __user *arg) if (perf_event_check_period(event, value)) return -EINVAL; + if (!event->attr.freq && (value & (1ULL << 63))) + return -EINVAL; + event_function_call(event, __perf_event_period, &value); return 0; @@ -5894,7 +5897,15 @@ static int perf_mmap(struct file *file, struct vm_area_struct *vma) */ user_lock_limit *= num_online_cpus(); - user_locked = atomic_long_read(&user->locked_vm) + user_extra; + user_locked = atomic_long_read(&user->locked_vm); + + /* + * sysctl_perf_event_mlock may have changed, so that + * user->locked_vm > user_lock_limit + */ + if (user_locked > user_lock_limit) + user_locked = user_lock_limit; + user_locked += user_extra; if (user_locked > user_lock_limit) extra = user_locked - user_lock_limit; diff --git a/kernel/fork.c b/kernel/fork.c index a3a4e771a40a338d98c7d4148b0d791391bfea32..68b80fcf94bd8edf17332a4e0d05e3e73fa51c1b 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -220,6 +220,7 @@ static unsigned long *alloc_thread_stack_node(struct task_struct *tsk, int node) memset(s->addr, 0, THREAD_SIZE); tsk->stack_vm_area = s; + tsk->stack = s->addr; return s->addr; } @@ -234,14 +235,20 @@ static unsigned long *alloc_thread_stack_node(struct task_struct *tsk, int node) * free_thread_stack() can be called in interrupt context, * so cache the vm_struct. */ - if (stack) + if (stack) { tsk->stack_vm_area = find_vm_area(stack); + tsk->stack = stack; + } return stack; #else struct page *page = alloc_pages_node(node, THREADINFO_GFP, THREAD_SIZE_ORDER); - return page ? page_address(page) : NULL; + if (likely(page)) { + tsk->stack = page_address(page); + return tsk->stack; + } + return NULL; #endif } @@ -272,7 +279,10 @@ static struct kmem_cache *thread_stack_cache; static unsigned long *alloc_thread_stack_node(struct task_struct *tsk, int node) { - return kmem_cache_alloc_node(thread_stack_cache, THREADINFO_GFP, node); + unsigned long *stack; + stack = kmem_cache_alloc_node(thread_stack_cache, THREADINFO_GFP, node); + tsk->stack = stack; + return stack; } static void free_thread_stack(struct task_struct *tsk) diff --git a/kernel/gen_kheaders.sh b/kernel/gen_kheaders.sh index 9a34e1d9bd7f97eb82e6ceb0042c7f5502742041..9ff449888d9cda491a39b0ffeba27c31d47a72b3 100755 --- a/kernel/gen_kheaders.sh +++ b/kernel/gen_kheaders.sh @@ -4,24 +4,12 @@ # This script generates an archive consisting of kernel headers # for CONFIG_IKHEADERS. set -e -spath="$(dirname "$(readlink -f "$0")")" -kroot="$spath/.." +sfile="$(readlink -f "$0")" outdir="$(pwd)" tarfile=$1 cpio_dir=$outdir/$tarfile.tmp -# Script filename relative to the kernel source root -# We add it to the archive because it is small and any changes -# to this script will also cause a rebuild of the archive. -sfile="$(realpath --relative-to $kroot "$(readlink -f "$0")")" - -src_file_list=" -include/ -arch/$SRCARCH/include/ -$sfile -" - -obj_file_list=" +dir_list=" include/ arch/$SRCARCH/include/ " @@ -33,33 +21,29 @@ arch/$SRCARCH/include/ # Uncomment it for debugging. # if [ ! -f /tmp/iter ]; then iter=1; echo 1 > /tmp/iter; # else iter=$(($(cat /tmp/iter) + 1)); echo $iter > /tmp/iter; fi -# find $src_file_list -type f | xargs ls -lR > /tmp/src-ls-$iter -# find $obj_file_list -type f | xargs ls -lR > /tmp/obj-ls-$iter +# find $src_file_list -name "*.h" | xargs ls -l > /tmp/src-ls-$iter +# find $obj_file_list -name "*.h" | xargs ls -l > /tmp/obj-ls-$iter # include/generated/compile.h is ignored because it is touched even when none # of the source files changed. This causes pointless regeneration, so let us # ignore them for md5 calculation. -pushd $kroot > /dev/null -src_files_md5="$(find $src_file_list -type f | +pushd $srctree > /dev/null +src_files_md5="$(find $dir_list -name "*.h" | grep -v "include/generated/compile.h" | grep -v "include/generated/autoconf.h" | - grep -v "include/config/auto.conf" | - grep -v "include/config/auto.conf.cmd" | - grep -v "include/config/tristate.conf" | - xargs ls -lR | md5sum | cut -d ' ' -f1)" + xargs ls -l | md5sum | cut -d ' ' -f1)" popd > /dev/null -obj_files_md5="$(find $obj_file_list -type f | +obj_files_md5="$(find $dir_list -name "*.h" | grep -v "include/generated/compile.h" | grep -v "include/generated/autoconf.h" | - grep -v "include/config/auto.conf" | - grep -v "include/config/auto.conf.cmd" | - grep -v "include/config/tristate.conf" | - xargs ls -lR | md5sum | cut -d ' ' -f1)" - + xargs ls -l | md5sum | cut -d ' ' -f1)" +# Any changes to this script will also cause a rebuild of the archive. +this_file_md5="$(ls -l $sfile | md5sum | cut -d ' ' -f1)" if [ -f $tarfile ]; then tarfile_md5="$(md5sum $tarfile | cut -d ' ' -f1)"; fi if [ -f kernel/kheaders.md5 ] && [ "$(cat kernel/kheaders.md5|head -1)" == "$src_files_md5" ] && [ "$(cat kernel/kheaders.md5|head -2|tail -1)" == "$obj_files_md5" ] && + [ "$(cat kernel/kheaders.md5|head -3|tail -1)" == "$this_file_md5" ] && [ "$(cat kernel/kheaders.md5|tail -1)" == "$tarfile_md5" ]; then exit fi @@ -71,16 +55,16 @@ fi rm -rf $cpio_dir mkdir $cpio_dir -pushd $kroot > /dev/null -for f in $src_file_list; - do find "$f" ! -name "*.cmd" ! -name ".*"; +pushd $srctree > /dev/null +for f in $dir_list; + do find "$f" -name "*.h"; done | cpio --quiet -pd $cpio_dir popd > /dev/null # The second CPIO can complain if files already exist which can # happen with out of tree builds. Just silence CPIO for now. -for f in $obj_file_list; - do find "$f" ! -name "*.cmd" ! -name ".*"; +for f in $dir_list; + do find "$f" -name "*.h"; done | cpio --quiet -pd $cpio_dir >/dev/null 2>&1 # Remove comments except SDPX lines @@ -91,6 +75,7 @@ tar -Jcf $tarfile -C $cpio_dir/ . > /dev/null echo "$src_files_md5" > kernel/kheaders.md5 echo "$obj_files_md5" >> kernel/kheaders.md5 +echo "$this_file_md5" >> kernel/kheaders.md5 echo "$(md5sum $tarfile | cut -d ' ' -f1)" >> kernel/kheaders.md5 rm -rf $cpio_dir diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 068fc01885bbc87ec28f1c128310f8a72a505b14..600c9be64959740a15805b988c6dd87e40944336 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -1409,6 +1409,7 @@ int irq_chip_retrigger_hierarchy(struct irq_data *data) return 0; } +EXPORT_SYMBOL_GPL(irq_chip_retrigger_hierarchy); /** * irq_chip_set_vcpu_affinity_parent - Set vcpu affinity on the parent interrupt @@ -1423,6 +1424,7 @@ int irq_chip_set_vcpu_affinity_parent(struct irq_data *data, void *vcpu_info) return -ENOSYS; } +EXPORT_SYMBOL_GPL(irq_chip_set_vcpu_affinity_parent); /** * irq_chip_set_wake_parent - Set/reset wake-up on the parent interrupt @@ -1443,6 +1445,7 @@ int irq_chip_set_wake_parent(struct irq_data *data, unsigned int on) return -ENOSYS; } +EXPORT_SYMBOL_GPL(irq_chip_set_wake_parent); #endif /** diff --git a/kernel/irq/debugfs.c b/kernel/irq/debugfs.c index 6f636136cccc05993e20034e92effc0c0fc3e7e2..b3f55dd581b0a56f86f6056c6d8df7e6b3fe7dcb 100644 --- a/kernel/irq/debugfs.c +++ b/kernel/irq/debugfs.c @@ -113,6 +113,7 @@ static const struct irq_bit_descr irqdata_states[] = { BIT_MASK_DESCR(IRQD_AFFINITY_MANAGED), BIT_MASK_DESCR(IRQD_MANAGED_SHUTDOWN), BIT_MASK_DESCR(IRQD_CAN_RESERVE), + BIT_MASK_DESCR(IRQD_MSI_NOMASK_QUIRK), BIT_MASK_DESCR(IRQD_FORWARDED_TO_VCPU), diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index ea57f3d397fe35fcaacb227b0be9387f062c8c1f..3f4618510d051949c583c4c66d908ac3626a884b 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -126,8 +126,6 @@ static inline void unregister_handler_proc(unsigned int irq, extern bool irq_can_set_affinity_usr(unsigned int irq); -extern int irq_select_affinity_usr(unsigned int irq); - extern void irq_set_thread_affinity(struct irq_desc *desc); extern int irq_do_set_affinity(struct irq_data *data, diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 5d9fc01b60a610da27f0e133c04d21bfa9ff00a6..065b1ec313d963ecd10e0aa870be6380f46f8f7c 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -148,6 +148,7 @@ struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, int size, switch (fwid->type) { case IRQCHIP_FWNODE_NAMED: case IRQCHIP_FWNODE_NAMED_ID: + domain->fwnode = fwnode; domain->name = kstrdup(fwid->name, GFP_KERNEL); if (!domain->name) { kfree(domain); @@ -183,7 +184,7 @@ struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, int size, * unhappy about. Replace them with ':', which does * the trick and is not as offensive as '\'... */ - name = kstrdup(of_node_full_name(of_node), GFP_KERNEL); + name = kasprintf(GFP_KERNEL, "%pOF", of_node); if (!name) { kfree(domain); return NULL; @@ -294,6 +295,7 @@ void irq_domain_update_bus_token(struct irq_domain *domain, mutex_unlock(&irq_domain_mutex); } +EXPORT_SYMBOL_GPL(irq_domain_update_bus_token); /** * irq_domain_add_simple() - Register an irq_domain and optionally map a range of irqs @@ -1241,6 +1243,7 @@ void irq_domain_free_irqs_top(struct irq_domain *domain, unsigned int virq, } irq_domain_free_irqs_common(domain, virq, nr_irqs); } +EXPORT_SYMBOL_GPL(irq_domain_free_irqs_top); static void irq_domain_free_irqs_hierarchy(struct irq_domain *domain, unsigned int irq_base, @@ -1420,6 +1423,7 @@ int irq_domain_push_irq(struct irq_domain *domain, int virq, void *arg) if (rv) { /* Restore the original irq_data. */ *root_irq_data = *child_irq_data; + kfree(child_irq_data); goto error; } diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 23bcfa71077fe30f9733a2a3134295cd20d61fec..eb69b805f908b2ba33ca702b7940bd147a862060 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -441,23 +441,9 @@ int irq_setup_affinity(struct irq_desc *desc) { return irq_select_affinity(irq_desc_get_irq(desc)); } -#endif +#endif /* CONFIG_AUTO_IRQ_AFFINITY */ +#endif /* CONFIG_SMP */ -/* - * Called when a bogus affinity is set via /proc/irq - */ -int irq_select_affinity_usr(unsigned int irq) -{ - struct irq_desc *desc = irq_to_desc(irq); - unsigned long flags; - int ret; - - raw_spin_lock_irqsave(&desc->lock, flags); - ret = irq_setup_affinity(desc); - raw_spin_unlock_irqrestore(&desc->lock, flags); - return ret; -} -#endif /** * irq_set_vcpu_affinity - Set vcpu affinity for the interrupt diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 4ca2fd46645dae7bf488a072d5b07c93641bd038..dc1186ce3ecdb5ff61b7fc0f0cc0d07dca739405 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -453,8 +453,11 @@ int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, continue; irq_data = irq_domain_get_irq_data(domain, desc->irq); - if (!can_reserve) + if (!can_reserve) { irqd_clr_can_reserve(irq_data); + if (domain->flags & IRQ_DOMAIN_MSI_NOMASK_QUIRK) + irqd_set_msi_nomask_quirk(irq_data); + } ret = irq_domain_activate_irq(irq_data, can_reserve); if (ret) goto cleanup; diff --git a/kernel/irq/proc.c b/kernel/irq/proc.c index 2f30037d79810da025d61c36961eef0b77e20e4b..49dc6f3620f0b9e522a8ecce5c90ae0180b886e6 100644 --- a/kernel/irq/proc.c +++ b/kernel/irq/proc.c @@ -115,6 +115,28 @@ static int irq_affinity_list_proc_show(struct seq_file *m, void *v) return show_irq_affinity(AFFINITY_LIST, m); } +#ifndef CONFIG_AUTO_IRQ_AFFINITY +static inline int irq_select_affinity_usr(unsigned int irq) +{ + /* + * If the interrupt is started up already then this fails. The + * interrupt is assigned to an online CPU already. There is no + * point to move it around randomly. Tell user space that the + * selected mask is bogus. + * + * If not then any change to the affinity is pointless because the + * startup code invokes irq_setup_affinity() which will select + * a online CPU anyway. + */ + return -EINVAL; +} +#else +/* ALPHA magic affinity auto selector. Keep it for historical reasons. */ +static inline int irq_select_affinity_usr(unsigned int irq) +{ + return irq_select_affinity(irq); +} +#endif static ssize_t write_irq_affinity(int type, struct file *file, const char __user *buffer, size_t count, loff_t *pos) diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c index 181db01aed7e05bc41d20d7ae7af5d3abc7e3678..9fa5626ce53313ff3174d4ed3d7d817c2909aa4f 100644 --- a/kernel/kallsyms.c +++ b/kernel/kallsyms.c @@ -173,7 +173,6 @@ unsigned long kallsyms_lookup_name(const char *name) } return module_kallsyms_lookup_name(name); } -EXPORT_SYMBOL_GPL(kallsyms_lookup_name); int kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *, unsigned long), @@ -192,7 +191,6 @@ int kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *, } return module_kallsyms_on_each_symbol(fn, data); } -EXPORT_SYMBOL_GPL(kallsyms_on_each_symbol); static unsigned long get_symbol_pos(unsigned long addr, unsigned long *symbolsize, @@ -280,6 +278,12 @@ static inline void cleanup_symbol_name(char *s) { char *res; +#ifdef CONFIG_THINLTO + /* Filter out hashes from static functions */ + res = strrchr(s, '$'); + if (res) + *res = '\0'; +#endif res = strrchr(s, '.'); if (res && !strcmp(res, ".cfi")) *res = '\0'; diff --git a/kernel/kcov.c b/kernel/kcov.c index c2277dbdbfb148842b855a942678e1218d891e07..f50354202dbe52ca0652f7c20151b45894fd8840 100644 --- a/kernel/kcov.c +++ b/kernel/kcov.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -20,8 +21,12 @@ #include #include #include +#include +#include #include +#define kcov_debug(fmt, ...) pr_debug("%s: " fmt, __func__, ##__VA_ARGS__) + /* Number of 64-bit words written per one comparison: */ #define KCOV_WORDS_PER_CMP 4 @@ -43,19 +48,100 @@ struct kcov { * Reference counter. We keep one for: * - opened file descriptor * - task with enabled coverage (we can't unwire it from another task) + * - each code section for remote coverage collection */ - atomic_t refcount; + refcount_t refcount; /* The lock protects mode, size, area and t. */ spinlock_t lock; enum kcov_mode mode; - /* Size of arena (in long's for KCOV_MODE_TRACE). */ - unsigned size; + /* Size of arena (in long's). */ + unsigned int size; /* Coverage buffer shared with user space. */ void *area; /* Task for which we collect coverage, or NULL. */ struct task_struct *t; + /* Collecting coverage from remote (background) threads. */ + bool remote; + /* Size of remote area (in long's). */ + unsigned int remote_size; + /* + * Sequence is incremented each time kcov is reenabled, used by + * kcov_remote_stop(), see the comment there. + */ + int sequence; }; +struct kcov_remote_area { + struct list_head list; + unsigned int size; +}; + +struct kcov_remote { + u64 handle; + struct kcov *kcov; + struct hlist_node hnode; +}; + +static DEFINE_SPINLOCK(kcov_remote_lock); +static DEFINE_HASHTABLE(kcov_remote_map, 4); +static struct list_head kcov_remote_areas = LIST_HEAD_INIT(kcov_remote_areas); + +/* Must be called with kcov_remote_lock locked. */ +static struct kcov_remote *kcov_remote_find(u64 handle) +{ + struct kcov_remote *remote; + + hash_for_each_possible(kcov_remote_map, remote, hnode, handle) { + if (remote->handle == handle) + return remote; + } + return NULL; +} + +static struct kcov_remote *kcov_remote_add(struct kcov *kcov, u64 handle) +{ + struct kcov_remote *remote; + + if (kcov_remote_find(handle)) + return ERR_PTR(-EEXIST); + remote = kmalloc(sizeof(*remote), GFP_ATOMIC); + if (!remote) + return ERR_PTR(-ENOMEM); + remote->handle = handle; + remote->kcov = kcov; + hash_add(kcov_remote_map, &remote->hnode, handle); + return remote; +} + +/* Must be called with kcov_remote_lock locked. */ +static struct kcov_remote_area *kcov_remote_area_get(unsigned int size) +{ + struct kcov_remote_area *area; + struct list_head *pos; + + kcov_debug("size = %u\n", size); + list_for_each(pos, &kcov_remote_areas) { + area = list_entry(pos, struct kcov_remote_area, list); + if (area->size == size) { + list_del(&area->list); + kcov_debug("rv = %px\n", area); + return area; + } + } + kcov_debug("rv = NULL\n"); + return NULL; +} + +/* Must be called with kcov_remote_lock locked. */ +static void kcov_remote_area_put(struct kcov_remote_area *area, + unsigned int size) +{ + kcov_debug("area = %px, size = %u\n", area, size); + INIT_LIST_HEAD(&area->list); + area->size = size; + list_add(&area->list, &kcov_remote_areas); +} + static notrace bool check_kcov_mode(enum kcov_mode needed_mode, struct task_struct *t) { unsigned int mode; @@ -72,7 +158,7 @@ static notrace bool check_kcov_mode(enum kcov_mode needed_mode, struct task_stru * in_interrupt() returns false (e.g. preempt_schedule_irq()). * READ_ONCE()/barrier() effectively provides load-acquire wrt * interrupts, there are paired barrier()/WRITE_ONCE() in - * kcov_ioctl_locked(). + * kcov_start(). */ barrier(); return mode == needed_mode; @@ -226,26 +312,90 @@ void notrace __sanitizer_cov_trace_switch(u64 val, u64 *cases) EXPORT_SYMBOL(__sanitizer_cov_trace_switch); #endif /* ifdef CONFIG_KCOV_ENABLE_COMPARISONS */ -static void kcov_get(struct kcov *kcov) +static void kcov_start(struct task_struct *t, unsigned int size, + void *area, enum kcov_mode mode, int sequence) { - atomic_inc(&kcov->refcount); -} - -static void kcov_put(struct kcov *kcov) -{ - if (atomic_dec_and_test(&kcov->refcount)) { - vfree(kcov->area); - kfree(kcov); - } + kcov_debug("t = %px, size = %u, area = %px\n", t, size, area); + /* Cache in task struct for performance. */ + t->kcov_size = size; + t->kcov_area = area; + /* See comment in check_kcov_mode(). */ + barrier(); + WRITE_ONCE(t->kcov_mode, mode); + t->kcov_sequence = sequence; } -void kcov_task_init(struct task_struct *t) +static void kcov_stop(struct task_struct *t) { WRITE_ONCE(t->kcov_mode, KCOV_MODE_DISABLED); barrier(); t->kcov_size = 0; t->kcov_area = NULL; +} + +static void kcov_task_reset(struct task_struct *t) +{ + kcov_stop(t); t->kcov = NULL; + t->kcov_sequence = 0; + t->kcov_handle = 0; +} + +void kcov_task_init(struct task_struct *t) +{ + kcov_task_reset(t); + t->kcov_handle = current->kcov_handle; +} + +static void kcov_reset(struct kcov *kcov) +{ + kcov->t = NULL; + kcov->mode = KCOV_MODE_INIT; + kcov->remote = false; + kcov->remote_size = 0; + kcov->sequence++; +} + +static void kcov_remote_reset(struct kcov *kcov) +{ + int bkt; + struct kcov_remote *remote; + struct hlist_node *tmp; + + spin_lock(&kcov_remote_lock); + hash_for_each_safe(kcov_remote_map, bkt, tmp, remote, hnode) { + if (remote->kcov != kcov) + continue; + kcov_debug("removing handle %llx\n", remote->handle); + hash_del(&remote->hnode); + kfree(remote); + } + /* Do reset before unlock to prevent races with kcov_remote_start(). */ + kcov_reset(kcov); + spin_unlock(&kcov_remote_lock); +} + +static void kcov_disable(struct task_struct *t, struct kcov *kcov) +{ + kcov_task_reset(t); + if (kcov->remote) + kcov_remote_reset(kcov); + else + kcov_reset(kcov); +} + +static void kcov_get(struct kcov *kcov) +{ + refcount_inc(&kcov->refcount); +} + +static void kcov_put(struct kcov *kcov) +{ + if (refcount_dec_and_test(&kcov->refcount)) { + kcov_remote_reset(kcov); + vfree(kcov->area); + kfree(kcov); + } } void kcov_task_exit(struct task_struct *t) @@ -255,15 +405,36 @@ void kcov_task_exit(struct task_struct *t) kcov = t->kcov; if (kcov == NULL) return; + spin_lock(&kcov->lock); + kcov_debug("t = %px, kcov->t = %px\n", t, kcov->t); + /* + * For KCOV_ENABLE devices we want to make sure that t->kcov->t == t, + * which comes down to: + * WARN_ON(!kcov->remote && kcov->t != t); + * + * For KCOV_REMOTE_ENABLE devices, the exiting task is either: + * 2. A remote task between kcov_remote_start() and kcov_remote_stop(). + * In this case we should print a warning right away, since a task + * shouldn't be exiting when it's in a kcov coverage collection + * section. Here t points to the task that is collecting remote + * coverage, and t->kcov->t points to the thread that created the + * kcov device. Which means that to detect this case we need to + * check that t != t->kcov->t, and this gives us the following: + * WARN_ON(kcov->remote && kcov->t != t); + * + * 2. The task that created kcov exiting without calling KCOV_DISABLE, + * and then again we can make sure that t->kcov->t == t: + * WARN_ON(kcov->remote && kcov->t != t); + * + * By combining all three checks into one we get: + */ if (WARN_ON(kcov->t != t)) { spin_unlock(&kcov->lock); return; } /* Just to not leave dangling references behind. */ - kcov_task_init(t); - kcov->t = NULL; - kcov->mode = KCOV_MODE_INIT; + kcov_disable(t, kcov); spin_unlock(&kcov->lock); kcov_put(kcov); } @@ -312,7 +483,8 @@ static int kcov_open(struct inode *inode, struct file *filep) if (!kcov) return -ENOMEM; kcov->mode = KCOV_MODE_DISABLED; - atomic_set(&kcov->refcount, 1); + kcov->sequence = 1; + refcount_set(&kcov->refcount, 1); spin_lock_init(&kcov->lock); filep->private_data = kcov; return nonseekable_open(inode, filep); @@ -324,6 +496,20 @@ static int kcov_close(struct inode *inode, struct file *filep) return 0; } +static int kcov_get_mode(unsigned long arg) +{ + if (arg == KCOV_TRACE_PC) + return KCOV_MODE_TRACE_PC; + else if (arg == KCOV_TRACE_CMP) +#ifdef CONFIG_KCOV_ENABLE_COMPARISONS + return KCOV_MODE_TRACE_CMP; +#else + return -ENOTSUPP; +#endif + else + return -EINVAL; +} + /* * Fault in a lazily-faulted vmalloc area before it can be used by * __santizer_cov_trace_pc(), to avoid recursion issues if any code on the @@ -339,14 +525,35 @@ static void kcov_fault_in_area(struct kcov *kcov) READ_ONCE(area[offset]); } +static inline bool kcov_check_handle(u64 handle, bool common_valid, + bool uncommon_valid, bool zero_valid) +{ + if (handle & ~(KCOV_SUBSYSTEM_MASK | KCOV_INSTANCE_MASK)) + return false; + switch (handle & KCOV_SUBSYSTEM_MASK) { + case KCOV_SUBSYSTEM_COMMON: + return (handle & KCOV_INSTANCE_MASK) ? + common_valid : zero_valid; + case KCOV_SUBSYSTEM_USB: + return uncommon_valid; + default: + return false; + } + return false; +} + static int kcov_ioctl_locked(struct kcov *kcov, unsigned int cmd, unsigned long arg) { struct task_struct *t; unsigned long size, unused; + int mode, i; + struct kcov_remote_arg *remote_arg; + struct kcov_remote *remote; switch (cmd) { case KCOV_INIT_TRACE: + kcov_debug("KCOV_INIT_TRACE\n"); /* * Enable kcov in trace mode and setup buffer size. * Must happen before anything else. @@ -365,6 +572,7 @@ static int kcov_ioctl_locked(struct kcov *kcov, unsigned int cmd, kcov->mode = KCOV_MODE_INIT; return 0; case KCOV_ENABLE: + kcov_debug("KCOV_ENABLE\n"); /* * Enable coverage for the current task. * At this point user must have been enabled trace mode, @@ -377,29 +585,20 @@ static int kcov_ioctl_locked(struct kcov *kcov, unsigned int cmd, t = current; if (kcov->t != NULL || t->kcov != NULL) return -EBUSY; - if (arg == KCOV_TRACE_PC) - kcov->mode = KCOV_MODE_TRACE_PC; - else if (arg == KCOV_TRACE_CMP) -#ifdef CONFIG_KCOV_ENABLE_COMPARISONS - kcov->mode = KCOV_MODE_TRACE_CMP; -#else - return -ENOTSUPP; -#endif - else - return -EINVAL; + mode = kcov_get_mode(arg); + if (mode < 0) + return mode; kcov_fault_in_area(kcov); - /* Cache in task struct for performance. */ - t->kcov_size = kcov->size; - t->kcov_area = kcov->area; - /* See comment in check_kcov_mode(). */ - barrier(); - WRITE_ONCE(t->kcov_mode, kcov->mode); + kcov->mode = mode; + kcov_start(t, kcov->size, kcov->area, kcov->mode, + kcov->sequence); t->kcov = kcov; kcov->t = t; - /* This is put either in kcov_task_exit() or in KCOV_DISABLE. */ + /* Put either in kcov_task_exit() or in KCOV_DISABLE. */ kcov_get(kcov); return 0; case KCOV_DISABLE: + kcov_debug("KCOV_DISABLE\n"); /* Disable coverage for the current task. */ unused = arg; if (unused != 0 || current->kcov != kcov) @@ -407,11 +606,65 @@ static int kcov_ioctl_locked(struct kcov *kcov, unsigned int cmd, t = current; if (WARN_ON(kcov->t != t)) return -EINVAL; - kcov_task_init(t); - kcov->t = NULL; - kcov->mode = KCOV_MODE_INIT; + kcov_disable(t, kcov); kcov_put(kcov); return 0; + case KCOV_REMOTE_ENABLE: + kcov_debug("KCOV_REMOTE_ENABLE\n"); + if (kcov->mode != KCOV_MODE_INIT || !kcov->area) + return -EINVAL; + t = current; + if (kcov->t != NULL || t->kcov != NULL) + return -EBUSY; + remote_arg = (struct kcov_remote_arg *)arg; + mode = kcov_get_mode(remote_arg->trace_mode); + if (mode < 0) + return mode; + if (remote_arg->area_size > LONG_MAX / sizeof(unsigned long)) + return -EINVAL; + kcov->mode = mode; + t->kcov = kcov; + kcov->t = t; + kcov->remote = true; + kcov->remote_size = remote_arg->area_size; + spin_lock(&kcov_remote_lock); + for (i = 0; i < remote_arg->num_handles; i++) { + kcov_debug("handle %llx\n", remote_arg->handles[i]); + if (!kcov_check_handle(remote_arg->handles[i], + false, true, false)) { + spin_unlock(&kcov_remote_lock); + kcov_disable(t, kcov); + return -EINVAL; + } + remote = kcov_remote_add(kcov, remote_arg->handles[i]); + if (IS_ERR(remote)) { + spin_unlock(&kcov_remote_lock); + kcov_disable(t, kcov); + return PTR_ERR(remote); + } + } + if (remote_arg->common_handle) { + kcov_debug("common handle %llx\n", + remote_arg->common_handle); + if (!kcov_check_handle(remote_arg->common_handle, + true, false, false)) { + spin_unlock(&kcov_remote_lock); + kcov_disable(t, kcov); + return -EINVAL; + } + remote = kcov_remote_add(kcov, + remote_arg->common_handle); + if (IS_ERR(remote)) { + spin_unlock(&kcov_remote_lock); + kcov_disable(t, kcov); + return PTR_ERR(remote); + } + t->kcov_handle = remote_arg->common_handle; + } + spin_unlock(&kcov_remote_lock); + /* Put either in kcov_task_exit() or in KCOV_DISABLE. */ + kcov_get(kcov); + return 0; default: return -ENOTTY; } @@ -421,11 +674,35 @@ static long kcov_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { struct kcov *kcov; int res; + struct kcov_remote_arg *remote_arg = NULL; + unsigned int remote_num_handles; + unsigned long remote_arg_size; + + if (cmd == KCOV_REMOTE_ENABLE) { + if (get_user(remote_num_handles, (unsigned __user *)(arg + + offsetof(struct kcov_remote_arg, num_handles)))) + return -EFAULT; + if (remote_num_handles > KCOV_REMOTE_MAX_HANDLES) + return -EINVAL; + remote_arg_size = struct_size(remote_arg, handles, + remote_num_handles); + remote_arg = memdup_user((void __user *)arg, remote_arg_size); + if (IS_ERR(remote_arg)) + return PTR_ERR(remote_arg); + if (remote_arg->num_handles != remote_num_handles) { + kfree(remote_arg); + return -EINVAL; + } + arg = (unsigned long)remote_arg; + } kcov = filep->private_data; spin_lock(&kcov->lock); res = kcov_ioctl_locked(kcov, cmd, arg); spin_unlock(&kcov->lock); + + kfree(remote_arg); + return res; } @@ -437,6 +714,207 @@ static const struct file_operations kcov_fops = { .release = kcov_close, }; +/* + * kcov_remote_start() and kcov_remote_stop() can be used to annotate a section + * of code in a kernel background thread to allow kcov to be used to collect + * coverage from that part of code. + * + * The handle argument of kcov_remote_start() identifies a code section that is + * used for coverage collection. A userspace process passes this handle to + * KCOV_REMOTE_ENABLE ioctl to make the used kcov device start collecting + * coverage for the code section identified by this handle. + * + * The usage of these annotations in the kernel code is different depending on + * the type of the kernel thread whose code is being annotated. + * + * For global kernel threads that are spawned in a limited number of instances + * (e.g. one USB hub_event() worker thread is spawned per USB HCD), each + * instance must be assigned a unique 4-byte instance id. The instance id is + * then combined with a 1-byte subsystem id to get a handle via + * kcov_remote_handle(subsystem_id, instance_id). + * + * For local kernel threads that are spawned from system calls handler when a + * user interacts with some kernel interface (e.g. vhost workers), a handle is + * passed from a userspace process as the common_handle field of the + * kcov_remote_arg struct (note, that the user must generate a handle by using + * kcov_remote_handle() with KCOV_SUBSYSTEM_COMMON as the subsystem id and an + * arbitrary 4-byte non-zero number as the instance id). This common handle + * then gets saved into the task_struct of the process that issued the + * KCOV_REMOTE_ENABLE ioctl. When this proccess issues system calls that spawn + * kernel threads, the common handle must be retrived via kcov_common_handle() + * and passed to the spawned threads via custom annotations. Those kernel + * threads must in turn be annotated with kcov_remote_start(common_handle) and + * kcov_remote_stop(). All of the threads that are spawned by the same process + * obtain the same handle, hence the name "common". + * + * See Documentation/dev-tools/kcov.rst for more details. + * + * Internally, this function looks up the kcov device associated with the + * provided handle, allocates an area for coverage collection, and saves the + * pointers to kcov and area into the current task_struct to allow coverage to + * be collected via __sanitizer_cov_trace_pc() + * In turns kcov_remote_stop() clears those pointers from task_struct to stop + * collecting coverage and copies all collected coverage into the kcov area. + */ +void kcov_remote_start(u64 handle) +{ + struct kcov_remote *remote; + void *area; + struct task_struct *t; + unsigned int size; + enum kcov_mode mode; + int sequence; + + if (WARN_ON(!kcov_check_handle(handle, true, true, true))) + return; + if (WARN_ON(!in_task())) + return; + t = current; + /* + * Check that kcov_remote_start is not called twice + * nor called by user tasks (with enabled kcov). + */ + if (WARN_ON(t->kcov)) + return; + + kcov_debug("handle = %llx\n", handle); + + spin_lock(&kcov_remote_lock); + remote = kcov_remote_find(handle); + if (!remote) { + kcov_debug("no remote found"); + spin_unlock(&kcov_remote_lock); + return; + } + /* Put in kcov_remote_stop(). */ + kcov_get(remote->kcov); + t->kcov = remote->kcov; + /* + * Read kcov fields before unlock to prevent races with + * KCOV_DISABLE / kcov_remote_reset(). + */ + size = remote->kcov->remote_size; + mode = remote->kcov->mode; + sequence = remote->kcov->sequence; + area = kcov_remote_area_get(size); + spin_unlock(&kcov_remote_lock); + + if (!area) { + area = vmalloc(size * sizeof(unsigned long)); + if (!area) { + t->kcov = NULL; + kcov_put(remote->kcov); + return; + } + } + /* Reset coverage size. */ + *(u64 *)area = 0; + + kcov_debug("area = %px, size = %u", area, size); + + kcov_start(t, size, area, mode, sequence); + +} +EXPORT_SYMBOL(kcov_remote_start); + +static void kcov_move_area(enum kcov_mode mode, void *dst_area, + unsigned int dst_area_size, void *src_area) +{ + u64 word_size = sizeof(unsigned long); + u64 count_size, entry_size_log; + u64 dst_len, src_len; + void *dst_entries, *src_entries; + u64 dst_occupied, dst_free, bytes_to_move, entries_moved; + + kcov_debug("%px %u <= %px %lu\n", + dst_area, dst_area_size, src_area, *(unsigned long *)src_area); + + switch (mode) { + case KCOV_MODE_TRACE_PC: + dst_len = READ_ONCE(*(unsigned long *)dst_area); + src_len = *(unsigned long *)src_area; + count_size = sizeof(unsigned long); + entry_size_log = __ilog2_u64(sizeof(unsigned long)); + break; + case KCOV_MODE_TRACE_CMP: + dst_len = READ_ONCE(*(u64 *)dst_area); + src_len = *(u64 *)src_area; + count_size = sizeof(u64); + BUILD_BUG_ON(!is_power_of_2(KCOV_WORDS_PER_CMP)); + entry_size_log = __ilog2_u64(sizeof(u64) * KCOV_WORDS_PER_CMP); + break; + default: + WARN_ON(1); + return; + } + + /* As arm can't divide u64 integers use log of entry size. */ + if (dst_len > ((dst_area_size * word_size - count_size) >> + entry_size_log)) + return; + dst_occupied = count_size + (dst_len << entry_size_log); + dst_free = dst_area_size * word_size - dst_occupied; + bytes_to_move = min(dst_free, src_len << entry_size_log); + dst_entries = dst_area + dst_occupied; + src_entries = src_area + count_size; + memcpy(dst_entries, src_entries, bytes_to_move); + entries_moved = bytes_to_move >> entry_size_log; + + switch (mode) { + case KCOV_MODE_TRACE_PC: + WRITE_ONCE(*(unsigned long *)dst_area, dst_len + entries_moved); + break; + case KCOV_MODE_TRACE_CMP: + WRITE_ONCE(*(u64 *)dst_area, dst_len + entries_moved); + break; + default: + break; + } +} + +/* See the comment before kcov_remote_start() for usage details. */ +void kcov_remote_stop(void) +{ + struct task_struct *t = current; + struct kcov *kcov = t->kcov; + void *area = t->kcov_area; + unsigned int size = t->kcov_size; + int sequence = t->kcov_sequence; + + if (!kcov) { + kcov_debug("no kcov found\n"); + return; + } + + kcov_stop(t); + t->kcov = NULL; + + spin_lock(&kcov->lock); + /* + * KCOV_DISABLE could have been called between kcov_remote_start() + * and kcov_remote_stop(), hence the check. + */ + kcov_debug("move if: %d == %d && %d\n", + sequence, kcov->sequence, (int)kcov->remote); + if (sequence == kcov->sequence && kcov->remote) + kcov_move_area(kcov->mode, kcov->area, kcov->size, area); + spin_unlock(&kcov->lock); + + spin_lock(&kcov_remote_lock); + kcov_remote_area_put(area, size); + spin_unlock(&kcov_remote_lock); + + kcov_put(kcov); +} +EXPORT_SYMBOL(kcov_remote_stop); + +/* See the comment before kcov_remote_start() for usage details. */ +u64 kcov_common_handle(void) +{ + return current->kcov_handle; +} +EXPORT_SYMBOL(kcov_common_handle); + static int __init kcov_init(void) { /* @@ -444,10 +922,8 @@ static int __init kcov_init(void) * there is no need to protect it against removal races. The * use of debugfs_create_file_unsafe() is actually safe here. */ - if (!debugfs_create_file_unsafe("kcov", 0600, NULL, NULL, &kcov_fops)) { - pr_err("failed to create kcov in debugfs\n"); - return -ENOMEM; - } + debugfs_create_file_unsafe("kcov", 0600, NULL, NULL, &kcov_fops); + return 0; } diff --git a/kernel/kprobes.c b/kernel/kprobes.c index f4e4095ec7eae392d12e4d5b2a9630df7b91301b..92aad49b82f9c99b33499fe0b565b6365176b701 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -523,6 +523,8 @@ static void do_unoptimize_kprobes(void) arch_unoptimize_kprobes(&unoptimizing_list, &freeing_list); /* Loop free_list for disarming */ list_for_each_entry_safe(op, tmp, &freeing_list, list) { + /* Switching from detour code to origin */ + op->kp.flags &= ~KPROBE_FLAG_OPTIMIZED; /* Disarm probes if marked disabled */ if (kprobe_disabled(&op->kp)) arch_disarm_kprobe(&op->kp); @@ -623,6 +625,18 @@ void wait_for_kprobe_optimizer(void) mutex_unlock(&kprobe_mutex); } +static bool optprobe_queued_unopt(struct optimized_kprobe *op) +{ + struct optimized_kprobe *_op; + + list_for_each_entry(_op, &unoptimizing_list, list) { + if (op == _op) + return true; + } + + return false; +} + /* Optimize kprobe if p is ready to be optimized */ static void optimize_kprobe(struct kprobe *p) { @@ -644,17 +658,21 @@ static void optimize_kprobe(struct kprobe *p) return; /* Check if it is already optimized. */ - if (op->kp.flags & KPROBE_FLAG_OPTIMIZED) + if (op->kp.flags & KPROBE_FLAG_OPTIMIZED) { + if (optprobe_queued_unopt(op)) { + /* This is under unoptimizing. Just dequeue the probe */ + list_del_init(&op->list); + } return; + } op->kp.flags |= KPROBE_FLAG_OPTIMIZED; - if (!list_empty(&op->list)) - /* This is under unoptimizing. Just dequeue the probe */ - list_del_init(&op->list); - else { - list_add(&op->list, &optimizing_list); - kick_kprobe_optimizer(); - } + /* On unoptimizing/optimizing_list, op must have OPTIMIZED flag */ + if (WARN_ON_ONCE(!list_empty(&op->list))) + return; + + list_add(&op->list, &optimizing_list); + kick_kprobe_optimizer(); } /* Short cut to direct unoptimizing */ @@ -662,6 +680,7 @@ static void force_unoptimize_kprobe(struct optimized_kprobe *op) { lockdep_assert_cpus_held(); arch_unoptimize_kprobe(op); + op->kp.flags &= ~KPROBE_FLAG_OPTIMIZED; if (kprobe_disabled(&op->kp)) arch_disarm_kprobe(&op->kp); } @@ -675,31 +694,33 @@ static void unoptimize_kprobe(struct kprobe *p, bool force) return; /* This is not an optprobe nor optimized */ op = container_of(p, struct optimized_kprobe, kp); - if (!kprobe_optimized(p)) { - /* Unoptimized or unoptimizing case */ - if (force && !list_empty(&op->list)) { - /* - * Only if this is unoptimizing kprobe and forced, - * forcibly unoptimize it. (No need to unoptimize - * unoptimized kprobe again :) - */ - list_del_init(&op->list); - force_unoptimize_kprobe(op); - } + if (!kprobe_optimized(p)) return; - } - op->kp.flags &= ~KPROBE_FLAG_OPTIMIZED; if (!list_empty(&op->list)) { - /* Dequeue from the optimization queue */ - list_del_init(&op->list); + if (optprobe_queued_unopt(op)) { + /* Queued in unoptimizing queue */ + if (force) { + /* + * Forcibly unoptimize the kprobe here, and queue it + * in the freeing list for release afterwards. + */ + force_unoptimize_kprobe(op); + list_move(&op->list, &freeing_list); + } + } else { + /* Dequeue from the optimizing queue */ + list_del_init(&op->list); + op->kp.flags &= ~KPROBE_FLAG_OPTIMIZED; + } return; } + /* Optimized kprobe case */ - if (force) + if (force) { /* Forcibly update the code: this is a special case */ force_unoptimize_kprobe(op); - else { + } else { list_add(&op->list, &unoptimizing_list); kick_kprobe_optimizer(); } diff --git a/kernel/memremap.c b/kernel/memremap.c index 7c5fb8a208acc354e44b680585a63b6928c6b7e2..331baad8efec3a543e3efd5f866ce2e17f7469e5 100644 --- a/kernel/memremap.c +++ b/kernel/memremap.c @@ -120,7 +120,9 @@ static void devm_memremap_pages_release(void *data) struct device *dev = pgmap->dev; struct resource *res = &pgmap->res; resource_size_t align_start, align_size; + struct page *first_page; unsigned long pfn; + int nid; pgmap->kill(pgmap->ref); for_each_device_pfn(pfn, pgmap) @@ -131,13 +133,17 @@ static void devm_memremap_pages_release(void *data) align_size = ALIGN(res->start + resource_size(res), SECTION_SIZE) - align_start; + /* make sure to access a memmap that was actually initialized */ + first_page = pfn_to_page(pfn_first(pgmap)); + + nid = page_to_nid(first_page); + mem_hotplug_begin(); if (pgmap->type == MEMORY_DEVICE_PRIVATE) { pfn = align_start >> PAGE_SHIFT; - __remove_pages(page_zone(pfn_to_page(pfn)), pfn, - align_size >> PAGE_SHIFT, NULL); + __remove_pages(pfn, align_size >> PAGE_SHIFT, NULL); } else { - arch_remove_memory(align_start, align_size, + arch_remove_memory(nid, align_start, align_size, pgmap->altmap_valid ? &pgmap->altmap : NULL); kasan_remove_zero_shadow(__va(align_start), align_size); } diff --git a/kernel/module.c b/kernel/module.c index fbc27293b7e4d3ec45ccff940a1359c07a50775e..c0a952ef955cc85e81d6040a19b570fc3fb6cc48 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -1729,6 +1729,8 @@ static int module_add_modinfo_attrs(struct module *mod) error_out: if (i > 0) module_remove_modinfo_attrs(mod, --i); + else + kfree(mod->modinfo_attrs); return error; } @@ -2988,9 +2990,7 @@ static int setup_load_info(struct load_info *info, int flags) /* Try to find a name early so we can log errors with a module name */ info->index.info = find_sec(info, ".modinfo"); - if (!info->index.info) - info->name = "(missing .modinfo section)"; - else + if (info->index.info) info->name = get_modinfo(info, "name"); /* Find internal symbols and strings. */ @@ -3005,14 +3005,15 @@ static int setup_load_info(struct load_info *info, int flags) } if (info->index.sym == 0) { - pr_warn("%s: module has no symbols (stripped?)\n", info->name); + pr_warn("%s: module has no symbols (stripped?)\n", + info->name ?: "(missing .modinfo section or name field)"); return -ENOEXEC; } info->index.mod = find_sec(info, ".gnu.linkonce.this_module"); if (!info->index.mod) { pr_warn("%s: No module found in object\n", - info->name ?: "(missing .modinfo name field)"); + info->name ?: "(missing .modinfo section or name field)"); return -ENOEXEC; } /* This is temporary: point mod into copy of data. */ diff --git a/kernel/padata.c b/kernel/padata.c index 6c06b3039faed01d992965f73c95bc218880e286..cfab62923c452f750e68a21b286592093f5591ef 100644 --- a/kernel/padata.c +++ b/kernel/padata.c @@ -35,6 +35,8 @@ #define MAX_OBJ_NUM 1000 +static void padata_free_pd(struct parallel_data *pd); + static int padata_index_to_cpu(struct parallel_data *pd, int cpu_index) { int cpu, target_cpu; @@ -334,6 +336,7 @@ static void padata_serial_worker(struct work_struct *serial_work) struct padata_serial_queue *squeue; struct parallel_data *pd; LIST_HEAD(local_list); + int cnt; local_bh_disable(); squeue = container_of(serial_work, struct padata_serial_queue, work); @@ -343,6 +346,8 @@ static void padata_serial_worker(struct work_struct *serial_work) list_replace_init(&squeue->serial.list, &local_list); spin_unlock(&squeue->serial.lock); + cnt = 0; + while (!list_empty(&local_list)) { struct padata_priv *padata; @@ -352,9 +357,12 @@ static void padata_serial_worker(struct work_struct *serial_work) list_del_init(&padata->list); padata->serial(padata); - atomic_dec(&pd->refcnt); + cnt++; } local_bh_enable(); + + if (atomic_sub_and_test(cnt, &pd->refcnt)) + padata_free_pd(pd); } /** @@ -501,7 +509,7 @@ static struct parallel_data *padata_alloc_pd(struct padata_instance *pinst, timer_setup(&pd->timer, padata_reorder_timer, 0); atomic_set(&pd->seq_nr, -1); atomic_set(&pd->reorder_objects, 0); - atomic_set(&pd->refcnt, 0); + atomic_set(&pd->refcnt, 1); pd->pinst = pinst; spin_lock_init(&pd->lock); @@ -526,31 +534,6 @@ static void padata_free_pd(struct parallel_data *pd) kfree(pd); } -/* Flush all objects out of the padata queues. */ -static void padata_flush_queues(struct parallel_data *pd) -{ - int cpu; - struct padata_parallel_queue *pqueue; - struct padata_serial_queue *squeue; - - for_each_cpu(cpu, pd->cpumask.pcpu) { - pqueue = per_cpu_ptr(pd->pqueue, cpu); - flush_work(&pqueue->work); - } - - del_timer_sync(&pd->timer); - - if (atomic_read(&pd->reorder_objects)) - padata_reorder(pd); - - for_each_cpu(cpu, pd->cpumask.cbcpu) { - squeue = per_cpu_ptr(pd->squeue, cpu); - flush_work(&squeue->work); - } - - BUG_ON(atomic_read(&pd->refcnt) != 0); -} - static void __padata_start(struct padata_instance *pinst) { pinst->flags |= PADATA_INIT; @@ -564,10 +547,6 @@ static void __padata_stop(struct padata_instance *pinst) pinst->flags &= ~PADATA_INIT; synchronize_rcu(); - - get_online_cpus(); - padata_flush_queues(pinst->pd); - put_online_cpus(); } /* Replace the internal control structure with a new one. */ @@ -588,8 +567,8 @@ static void padata_replace(struct padata_instance *pinst, if (!cpumask_equal(pd_old->cpumask.cbcpu, pd_new->cpumask.cbcpu)) notification_mask |= PADATA_CPU_SERIAL; - padata_flush_queues(pd_old); - padata_free_pd(pd_old); + if (atomic_dec_and_test(&pd_old->refcnt)) + padata_free_pd(pd_old); if (notification_mask) blocking_notifier_call_chain(&pinst->cpumask_change_notifier, diff --git a/kernel/power/autosleep.c b/kernel/power/autosleep.c index 41e83a779e19992f0097c92622332a05b1c4043e..9af5a50d348994c001505074c3fa9f5639b9b308 100644 --- a/kernel/power/autosleep.c +++ b/kernel/power/autosleep.c @@ -116,7 +116,7 @@ int pm_autosleep_set_state(suspend_state_t state) int __init pm_autosleep_init(void) { - autosleep_ws = wakeup_source_register("autosleep"); + autosleep_ws = wakeup_source_register(NULL, "autosleep"); if (!autosleep_ws) return -ENOMEM; diff --git a/kernel/power/wakelock.c b/kernel/power/wakelock.c index 4210152e56f0f403c69f1539cc011ae807e5eee0..105df4dfc783971acdd1d17ee2971cb892c97d02 100644 --- a/kernel/power/wakelock.c +++ b/kernel/power/wakelock.c @@ -27,7 +27,7 @@ static DEFINE_MUTEX(wakelocks_lock); struct wakelock { char *name; struct rb_node node; - struct wakeup_source ws; + struct wakeup_source *ws; #ifdef CONFIG_PM_WAKELOCKS_GC struct list_head lru; #endif @@ -46,7 +46,7 @@ ssize_t pm_show_wakelocks(char *buf, bool show_active) for (node = rb_first(&wakelocks_tree); node; node = rb_next(node)) { wl = rb_entry(node, struct wakelock, node); - if (wl->ws.active == show_active) + if (wl->ws->active == show_active) str += scnprintf(str, end - str, "%s ", wl->name); } if (str > buf) @@ -112,16 +112,16 @@ static void __wakelocks_gc(struct work_struct *work) u64 idle_time_ns; bool active; - spin_lock_irq(&wl->ws.lock); - idle_time_ns = ktime_to_ns(ktime_sub(now, wl->ws.last_time)); - active = wl->ws.active; - spin_unlock_irq(&wl->ws.lock); + spin_lock_irq(&wl->ws->lock); + idle_time_ns = ktime_to_ns(ktime_sub(now, wl->ws->last_time)); + active = wl->ws->active; + spin_unlock_irq(&wl->ws->lock); if (idle_time_ns < ((u64)WL_GC_TIME_SEC * NSEC_PER_SEC)) break; if (!active) { - wakeup_source_remove(&wl->ws); + wakeup_source_unregister(wl->ws); rb_erase(&wl->node, &wakelocks_tree); list_del(&wl->lru); kfree(wl->name); @@ -187,9 +187,15 @@ static struct wakelock *wakelock_lookup_add(const char *name, size_t len, kfree(wl); return ERR_PTR(-ENOMEM); } - wl->ws.name = wl->name; - wl->ws.last_time = ktime_get(); - wakeup_source_add(&wl->ws); + + wl->ws = wakeup_source_register(NULL, wl->name); + if (!wl->ws) { + kfree(wl->name); + kfree(wl); + return ERR_PTR(-ENOMEM); + } + wl->ws->last_time = ktime_get(); + rb_link_node(&wl->node, parent, node); rb_insert_color(&wl->node, &wakelocks_tree); wakelocks_lru_add(wl); @@ -233,9 +239,9 @@ int pm_wake_lock(const char *buf) u64 timeout_ms = timeout_ns + NSEC_PER_MSEC - 1; do_div(timeout_ms, NSEC_PER_MSEC); - __pm_wakeup_event(&wl->ws, timeout_ms); + __pm_wakeup_event(wl->ws, timeout_ms); } else { - __pm_stay_awake(&wl->ws); + __pm_stay_awake(wl->ws); } wakelocks_lru_most_recent(wl); @@ -271,7 +277,7 @@ int pm_wake_unlock(const char *buf) ret = PTR_ERR(wl); goto out; } - __pm_relax(&wl->ws); + __pm_relax(wl->ws); wakelocks_lru_most_recent(wl); wakelocks_gc(); diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 8487e2758ad8b03504f685082614924e057105cb..a9e567ac6ce4f8407c620e07cc212ae55d014b9d 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -2721,8 +2721,6 @@ void register_console(struct console *newcon) * for us. */ logbuf_lock_irqsave(flags); - console_seq = syslog_seq; - console_idx = syslog_idx; /* * We're about to replay the log buffer. Only do this to the * just-registered console to avoid excessive message spam to @@ -2734,6 +2732,8 @@ void register_console(struct console *newcon) */ exclusive_console = newcon; exclusive_console_stop_seq = console_seq; + console_seq = syslog_seq; + console_idx = syslog_idx; logbuf_unlock_irqrestore(flags); } console_unlock(); diff --git a/kernel/ptrace.c b/kernel/ptrace.c index fed682a01a75f5a110453c3991abb955acfbba6d..b93eb4eaf7acfebff467f4bf94856c9987a5d218 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -258,12 +258,17 @@ static int ptrace_check_attach(struct task_struct *child, bool ignore_state) return ret; } -static int ptrace_has_cap(struct user_namespace *ns, unsigned int mode) +static bool ptrace_has_cap(const struct cred *cred, struct user_namespace *ns, + unsigned int mode) { + int ret; + if (mode & PTRACE_MODE_NOAUDIT) - return has_ns_capability_noaudit(current, ns, CAP_SYS_PTRACE); + ret = security_capable(cred, ns, CAP_SYS_PTRACE, CAP_OPT_NOAUDIT); else - return has_ns_capability(current, ns, CAP_SYS_PTRACE); + ret = security_capable(cred, ns, CAP_SYS_PTRACE, CAP_OPT_NONE); + + return ret == 0; } /* Returns 0 on success, -errno on denial. */ @@ -315,7 +320,7 @@ static int __ptrace_may_access(struct task_struct *task, unsigned int mode) gid_eq(caller_gid, tcred->sgid) && gid_eq(caller_gid, tcred->gid)) goto ok; - if (ptrace_has_cap(tcred->user_ns, mode)) + if (ptrace_has_cap(cred, tcred->user_ns, mode)) goto ok; rcu_read_unlock(); return -EPERM; @@ -334,7 +339,7 @@ static int __ptrace_may_access(struct task_struct *task, unsigned int mode) mm = task->mm; if (mm && ((get_dumpable(mm) != SUID_DUMP_USER) && - !ptrace_has_cap(mm->user_ns, mode))) + !ptrace_has_cap(cred, mm->user_ns, mode))) return -EPERM; return security_ptrace_access_check(task, mode); diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index a97c20ea9bce65c7a6126effc271ce979f6e9f33..5f6de49dc78e555c45f9dd71ce890bf433554200 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -267,7 +267,7 @@ static void rcu_preempt_ctxt_queue(struct rcu_node *rnp, struct rcu_data *rdp) * blocked tasks. */ if (!rnp->gp_tasks && (blkd_state & RCU_GP_BLKD)) { - rnp->gp_tasks = &t->rcu_node_entry; + WRITE_ONCE(rnp->gp_tasks, &t->rcu_node_entry); WARN_ON_ONCE(rnp->completedqs == rnp->gp_seq); } if (!rnp->exp_tasks && (blkd_state & RCU_EXP_BLKD)) @@ -392,7 +392,7 @@ static void rcu_preempt_note_context_switch(bool preempt) */ static int rcu_preempt_blocked_readers_cgp(struct rcu_node *rnp) { - return rnp->gp_tasks != NULL; + return READ_ONCE(rnp->gp_tasks) != NULL; } /* @@ -557,7 +557,7 @@ static void rcu_read_unlock_special(struct task_struct *t) trace_rcu_unlock_preempted_task(TPS("rcu_preempt"), rnp->gp_seq, t->pid); if (&t->rcu_node_entry == rnp->gp_tasks) - rnp->gp_tasks = np; + WRITE_ONCE(rnp->gp_tasks, np); if (&t->rcu_node_entry == rnp->exp_tasks) rnp->exp_tasks = np; if (IS_ENABLED(CONFIG_RCU_BOOST)) { @@ -716,7 +716,7 @@ rcu_preempt_check_blocked_tasks(struct rcu_state *rsp, struct rcu_node *rnp) dump_blkd_tasks(rsp, rnp, 10); if (rcu_preempt_has_tasks(rnp) && (rnp->qsmaskinit || rnp->wait_blkd_tasks)) { - rnp->gp_tasks = rnp->blkd_tasks.next; + WRITE_ONCE(rnp->gp_tasks, rnp->blkd_tasks.next); t = container_of(rnp->gp_tasks, struct task_struct, rcu_node_entry); trace_rcu_unlock_preempted_task(TPS("rcu_preempt-GPS"), @@ -883,7 +883,8 @@ dump_blkd_tasks(struct rcu_state *rsp, struct rcu_node *rnp, int ncheck) pr_info("%s: %d:%d ->qsmask %#lx ->qsmaskinit %#lx ->qsmaskinitnext %#lx\n", __func__, rnp1->grplo, rnp1->grphi, rnp1->qsmask, rnp1->qsmaskinit, rnp1->qsmaskinitnext); pr_info("%s: ->gp_tasks %p ->boost_tasks %p ->exp_tasks %p\n", - __func__, rnp->gp_tasks, rnp->boost_tasks, rnp->exp_tasks); + __func__, READ_ONCE(rnp->gp_tasks), rnp->boost_tasks, + rnp->exp_tasks); pr_info("%s: ->blkd_tasks", __func__); i = 0; list_for_each(lhp, &rnp->blkd_tasks) { diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 08c4eb0cf8580cd3e0418ebdac416e82158c1bf9..00448773290b0a16578e002d8c03050cf6dc2431 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -730,6 +730,598 @@ static void set_load_weight(struct task_struct *p, bool update_load) } } +#ifdef CONFIG_UCLAMP_TASK +/* + * Serializes updates of utilization clamp values + * + * The (slow-path) user-space triggers utilization clamp value updates which + * can require updates on (fast-path) scheduler's data structures used to + * support enqueue/dequeue operations. + * While the per-CPU rq lock protects fast-path update operations, user-space + * requests are serialized using a mutex to reduce the risk of conflicting + * updates or API abuses. + */ +static DEFINE_MUTEX(uclamp_mutex); + +/* Max allowed minimum utilization */ +unsigned int sysctl_sched_uclamp_util_min = SCHED_CAPACITY_SCALE; + +/* Max allowed maximum utilization */ +unsigned int sysctl_sched_uclamp_util_max = SCHED_CAPACITY_SCALE; + +/* All clamps are required to be less or equal than these values */ +static struct uclamp_se uclamp_default[UCLAMP_CNT]; + +/* Integer rounded range for each bucket */ +#define UCLAMP_BUCKET_DELTA DIV_ROUND_CLOSEST(SCHED_CAPACITY_SCALE, UCLAMP_BUCKETS) + +#define for_each_clamp_id(clamp_id) \ + for ((clamp_id) = 0; (clamp_id) < UCLAMP_CNT; (clamp_id)++) + +static inline unsigned int uclamp_bucket_id(unsigned int clamp_value) +{ + return clamp_value / UCLAMP_BUCKET_DELTA; +} + +static inline unsigned int uclamp_bucket_base_value(unsigned int clamp_value) +{ + return UCLAMP_BUCKET_DELTA * uclamp_bucket_id(clamp_value); +} + +static inline unsigned int uclamp_none(enum uclamp_id clamp_id) +{ + if (clamp_id == UCLAMP_MIN) + return 0; + return SCHED_CAPACITY_SCALE; +} + +static inline void uclamp_se_set(struct uclamp_se *uc_se, + unsigned int value, bool user_defined) +{ + uc_se->value = value; + uc_se->bucket_id = uclamp_bucket_id(value); + uc_se->user_defined = user_defined; +} + +static inline unsigned int +uclamp_idle_value(struct rq *rq, enum uclamp_id clamp_id, + unsigned int clamp_value) +{ + /* + * Avoid blocked utilization pushing up the frequency when we go + * idle (which drops the max-clamp) by retaining the last known + * max-clamp. + */ + if (clamp_id == UCLAMP_MAX) { + rq->uclamp_flags |= UCLAMP_FLAG_IDLE; + return clamp_value; + } + + return uclamp_none(UCLAMP_MIN); +} + +static inline void uclamp_idle_reset(struct rq *rq, enum uclamp_id clamp_id, + unsigned int clamp_value) +{ + /* Reset max-clamp retention only on idle exit */ + if (!(rq->uclamp_flags & UCLAMP_FLAG_IDLE)) + return; + + WRITE_ONCE(rq->uclamp[clamp_id].value, clamp_value); +} + +static inline +unsigned int uclamp_rq_max_value(struct rq *rq, enum uclamp_id clamp_id, + unsigned int clamp_value) +{ + struct uclamp_bucket *bucket = rq->uclamp[clamp_id].bucket; + int bucket_id = UCLAMP_BUCKETS - 1; + + /* + * Since both min and max clamps are max aggregated, find the + * top most bucket with tasks in. + */ + for ( ; bucket_id >= 0; bucket_id--) { + if (!bucket[bucket_id].tasks) + continue; + return bucket[bucket_id].value; + } + + /* No tasks -- default clamp values */ + return uclamp_idle_value(rq, clamp_id, clamp_value); +} + +static inline struct uclamp_se +uclamp_tg_restrict(struct task_struct *p, enum uclamp_id clamp_id) +{ + struct uclamp_se uc_req = p->uclamp_req[clamp_id]; +#ifdef CONFIG_UCLAMP_TASK_GROUP + struct uclamp_se uc_max; + + /* + * Tasks in autogroups or root task group will be + * restricted by system defaults. + */ + if (task_group_is_autogroup(task_group(p))) + return uc_req; + if (task_group(p) == &root_task_group) + return uc_req; + + uc_max = task_group(p)->uclamp[clamp_id]; + if (uc_req.value > uc_max.value || !uc_req.user_defined) + return uc_max; +#endif + + return uc_req; +} + +/* + * The effective clamp bucket index of a task depends on, by increasing + * priority: + * - the task specific clamp value, when explicitly requested from userspace + * - the task group effective clamp value, for tasks not either in the root + * group or in an autogroup + * - the system default clamp value, defined by the sysadmin + */ +static inline struct uclamp_se +uclamp_eff_get(struct task_struct *p, enum uclamp_id clamp_id) +{ + struct uclamp_se uc_req = uclamp_tg_restrict(p, clamp_id); + struct uclamp_se uc_max = uclamp_default[clamp_id]; + + /* System default restrictions always apply */ + if (unlikely(uc_req.value > uc_max.value)) + return uc_max; + + return uc_req; +} + +unsigned long uclamp_eff_value(struct task_struct *p, enum uclamp_id clamp_id) +{ + struct uclamp_se uc_eff; + + /* Task currently refcounted: use back-annotated (effective) value */ + if (p->uclamp[clamp_id].active) + return (unsigned long)p->uclamp[clamp_id].value; + + uc_eff = uclamp_eff_get(p, clamp_id); + + return (unsigned long)uc_eff.value; +} + +/* + * When a task is enqueued on a rq, the clamp bucket currently defined by the + * task's uclamp::bucket_id is refcounted on that rq. This also immediately + * updates the rq's clamp value if required. + * + * Tasks can have a task-specific value requested from user-space, track + * within each bucket the maximum value for tasks refcounted in it. + * This "local max aggregation" allows to track the exact "requested" value + * for each bucket when all its RUNNABLE tasks require the same clamp. + */ +static inline void uclamp_rq_inc_id(struct rq *rq, struct task_struct *p, + enum uclamp_id clamp_id) +{ + struct uclamp_rq *uc_rq = &rq->uclamp[clamp_id]; + struct uclamp_se *uc_se = &p->uclamp[clamp_id]; + struct uclamp_bucket *bucket; + + lockdep_assert_held(&rq->lock); + + /* Update task effective clamp */ + p->uclamp[clamp_id] = uclamp_eff_get(p, clamp_id); + + bucket = &uc_rq->bucket[uc_se->bucket_id]; + bucket->tasks++; + uc_se->active = true; + + uclamp_idle_reset(rq, clamp_id, uc_se->value); + + /* + * Local max aggregation: rq buckets always track the max + * "requested" clamp value of its RUNNABLE tasks. + */ + if (bucket->tasks == 1 || uc_se->value > bucket->value) + bucket->value = uc_se->value; + + if (uc_se->value > READ_ONCE(uc_rq->value)) + WRITE_ONCE(uc_rq->value, uc_se->value); +} + +/* + * When a task is dequeued from a rq, the clamp bucket refcounted by the task + * is released. If this is the last task reference counting the rq's max + * active clamp value, then the rq's clamp value is updated. + * + * Both refcounted tasks and rq's cached clamp values are expected to be + * always valid. If it's detected they are not, as defensive programming, + * enforce the expected state and warn. + */ +static inline void uclamp_rq_dec_id(struct rq *rq, struct task_struct *p, + enum uclamp_id clamp_id) +{ + struct uclamp_rq *uc_rq = &rq->uclamp[clamp_id]; + struct uclamp_se *uc_se = &p->uclamp[clamp_id]; + struct uclamp_bucket *bucket; + unsigned int bkt_clamp; + unsigned int rq_clamp; + + lockdep_assert_held(&rq->lock); + + bucket = &uc_rq->bucket[uc_se->bucket_id]; + SCHED_WARN_ON(!bucket->tasks); + if (likely(bucket->tasks)) + bucket->tasks--; + uc_se->active = false; + + /* + * Keep "local max aggregation" simple and accept to (possibly) + * overboost some RUNNABLE tasks in the same bucket. + * The rq clamp bucket value is reset to its base value whenever + * there are no more RUNNABLE tasks refcounting it. + */ + if (likely(bucket->tasks)) + return; + + rq_clamp = READ_ONCE(uc_rq->value); + /* + * Defensive programming: this should never happen. If it happens, + * e.g. due to future modification, warn and fixup the expected value. + */ + SCHED_WARN_ON(bucket->value > rq_clamp); + if (bucket->value >= rq_clamp) { + bkt_clamp = uclamp_rq_max_value(rq, clamp_id, uc_se->value); + WRITE_ONCE(uc_rq->value, bkt_clamp); + } +} + +static inline void uclamp_rq_inc(struct rq *rq, struct task_struct *p) +{ + enum uclamp_id clamp_id; + + if (unlikely(!p->sched_class->uclamp_enabled)) + return; + + for_each_clamp_id(clamp_id) + uclamp_rq_inc_id(rq, p, clamp_id); + + /* Reset clamp idle holding when there is one RUNNABLE task */ + if (rq->uclamp_flags & UCLAMP_FLAG_IDLE) + rq->uclamp_flags &= ~UCLAMP_FLAG_IDLE; +} + +static inline void uclamp_rq_dec(struct rq *rq, struct task_struct *p) +{ + enum uclamp_id clamp_id; + + if (unlikely(!p->sched_class->uclamp_enabled)) + return; + + for_each_clamp_id(clamp_id) + uclamp_rq_dec_id(rq, p, clamp_id); +} + +static inline void +uclamp_update_active(struct task_struct *p, enum uclamp_id clamp_id) +{ + struct rq_flags rf; + struct rq *rq; + + /* + * Lock the task and the rq where the task is (or was) queued. + * + * We might lock the (previous) rq of a !RUNNABLE task, but that's the + * price to pay to safely serialize util_{min,max} updates with + * enqueues, dequeues and migration operations. + * This is the same locking schema used by __set_cpus_allowed_ptr(). + */ + rq = task_rq_lock(p, &rf); + + /* + * Setting the clamp bucket is serialized by task_rq_lock(). + * If the task is not yet RUNNABLE and its task_struct is not + * affecting a valid clamp bucket, the next time it's enqueued, + * it will already see the updated clamp bucket value. + */ + if (p->uclamp[clamp_id].active) { + uclamp_rq_dec_id(rq, p, clamp_id); + uclamp_rq_inc_id(rq, p, clamp_id); + } + + task_rq_unlock(rq, p, &rf); +} + +#ifdef CONFIG_UCLAMP_TASK_GROUP +static inline void +uclamp_update_active_tasks(struct cgroup_subsys_state *css, + unsigned int clamps) +{ + enum uclamp_id clamp_id; + struct css_task_iter it; + struct task_struct *p; + + css_task_iter_start(css, 0, &it); + while ((p = css_task_iter_next(&it))) { + for_each_clamp_id(clamp_id) { + if ((0x1 << clamp_id) & clamps) + uclamp_update_active(p, clamp_id); + } + } + css_task_iter_end(&it); +} + +static void cpu_util_update_eff(struct cgroup_subsys_state *css); +static void uclamp_update_root_tg(void) +{ + struct task_group *tg = &root_task_group; + + uclamp_se_set(&tg->uclamp_req[UCLAMP_MIN], + sysctl_sched_uclamp_util_min, false); + uclamp_se_set(&tg->uclamp_req[UCLAMP_MAX], + sysctl_sched_uclamp_util_max, false); + + rcu_read_lock(); + cpu_util_update_eff(&root_task_group.css); + rcu_read_unlock(); +} +#else +static void uclamp_update_root_tg(void) { } +#endif + +int sysctl_sched_uclamp_handler(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, + loff_t *ppos) +{ + bool update_root_tg = false; + int old_min, old_max; + int result; + + mutex_lock(&uclamp_mutex); + old_min = sysctl_sched_uclamp_util_min; + old_max = sysctl_sched_uclamp_util_max; + + result = proc_dointvec(table, write, buffer, lenp, ppos); + if (result) + goto undo; + if (!write) + goto done; + + if (sysctl_sched_uclamp_util_min > sysctl_sched_uclamp_util_max || + sysctl_sched_uclamp_util_max > SCHED_CAPACITY_SCALE) { + result = -EINVAL; + goto undo; + } + + if (old_min != sysctl_sched_uclamp_util_min) { + uclamp_se_set(&uclamp_default[UCLAMP_MIN], + sysctl_sched_uclamp_util_min, false); + update_root_tg = true; + } + if (old_max != sysctl_sched_uclamp_util_max) { + uclamp_se_set(&uclamp_default[UCLAMP_MAX], + sysctl_sched_uclamp_util_max, false); + update_root_tg = true; + } + + if (update_root_tg) + uclamp_update_root_tg(); + + /* + * We update all RUNNABLE tasks only when task groups are in use. + * Otherwise, keep it simple and do just a lazy update at each next + * task enqueue time. + */ + + goto done; + +undo: + sysctl_sched_uclamp_util_min = old_min; + sysctl_sched_uclamp_util_max = old_max; +done: + mutex_unlock(&uclamp_mutex); + + return result; +} + +static int uclamp_validate(struct task_struct *p, + const struct sched_attr *attr) +{ + unsigned int lower_bound = p->uclamp_req[UCLAMP_MIN].value; + unsigned int upper_bound = p->uclamp_req[UCLAMP_MAX].value; + + if (attr->sched_flags & SCHED_FLAG_UTIL_CLAMP_MIN) + lower_bound = attr->sched_util_min; + if (attr->sched_flags & SCHED_FLAG_UTIL_CLAMP_MAX) + upper_bound = attr->sched_util_max; + + if (lower_bound > upper_bound) + return -EINVAL; + if (upper_bound > SCHED_CAPACITY_SCALE) + return -EINVAL; + + return 0; +} + +static void __setscheduler_uclamp(struct task_struct *p, + const struct sched_attr *attr) +{ + enum uclamp_id clamp_id; + + /* + * On scheduling class change, reset to default clamps for tasks + * without a task-specific value. + */ + for_each_clamp_id(clamp_id) { + struct uclamp_se *uc_se = &p->uclamp_req[clamp_id]; + unsigned int clamp_value = uclamp_none(clamp_id); + + /* Keep using defined clamps across class changes */ + if (uc_se->user_defined) + continue; + + /* By default, RT tasks always get 100% boost */ + if (sched_feat(SUGOV_RT_MAX_FREQ) && + unlikely(rt_task(p) && + clamp_id == UCLAMP_MIN)) { + + clamp_value = uclamp_none(UCLAMP_MAX); + } + + uclamp_se_set(uc_se, clamp_value, false); + } + + if (likely(!(attr->sched_flags & SCHED_FLAG_UTIL_CLAMP))) + return; + + if (attr->sched_flags & SCHED_FLAG_UTIL_CLAMP_MIN) { + uclamp_se_set(&p->uclamp_req[UCLAMP_MIN], + attr->sched_util_min, true); + } + + if (attr->sched_flags & SCHED_FLAG_UTIL_CLAMP_MAX) { + uclamp_se_set(&p->uclamp_req[UCLAMP_MAX], + attr->sched_util_max, true); + } +} + +static void uclamp_fork(struct task_struct *p) +{ + enum uclamp_id clamp_id; + + for_each_clamp_id(clamp_id) + p->uclamp[clamp_id].active = false; + + if (likely(!p->sched_reset_on_fork)) + return; + + for_each_clamp_id(clamp_id) { + unsigned int clamp_value = uclamp_none(clamp_id); + + /* By default, RT tasks always get 100% boost */ + if (sched_feat(SUGOV_RT_MAX_FREQ) && + unlikely(rt_task(p) && + clamp_id == UCLAMP_MIN)) { + + clamp_value = uclamp_none(UCLAMP_MAX); + } + + uclamp_se_set(&p->uclamp_req[clamp_id], clamp_value, false); + } +} + +#ifdef CONFIG_SMP +unsigned int uclamp_task(struct task_struct *p) +{ + unsigned long util; + + util = task_util_est(p); + util = max(util, uclamp_eff_value(p, UCLAMP_MIN)); + util = min(util, uclamp_eff_value(p, UCLAMP_MAX)); + + return util; +} + +bool uclamp_boosted(struct task_struct *p) +{ + return uclamp_eff_value(p, UCLAMP_MIN) > 0; +} + +bool uclamp_latency_sensitive(struct task_struct *p) +{ +#ifdef CONFIG_UCLAMP_TASK_GROUP + struct cgroup_subsys_state *css = task_css(p, cpu_cgrp_id); + struct task_group *tg; + + if (!css) + return false; + tg = container_of(css, struct task_group, css); + + return tg->latency_sensitive; +#else + return false; +#endif +} +#endif /* CONFIG_SMP */ + +static void __init init_uclamp(void) +{ + struct uclamp_se uc_max = {}; + enum uclamp_id clamp_id; + int cpu; + + mutex_init(&uclamp_mutex); + + for_each_possible_cpu(cpu) { + memset(&cpu_rq(cpu)->uclamp, 0, + sizeof(struct uclamp_rq)*UCLAMP_CNT); + cpu_rq(cpu)->uclamp_flags = 0; + } + + for_each_clamp_id(clamp_id) { + uclamp_se_set(&init_task.uclamp_req[clamp_id], + uclamp_none(clamp_id), false); + } + + /* System defaults allow max clamp values for both indexes */ + uclamp_se_set(&uc_max, uclamp_none(UCLAMP_MAX), false); + for_each_clamp_id(clamp_id) { + uclamp_default[clamp_id] = uc_max; +#ifdef CONFIG_UCLAMP_TASK_GROUP + root_task_group.uclamp_req[clamp_id] = uc_max; + root_task_group.uclamp[clamp_id] = uc_max; +#endif + } +} + +#else /* CONFIG_UCLAMP_TASK */ +static inline void uclamp_rq_inc(struct rq *rq, struct task_struct *p) { } +static inline void uclamp_rq_dec(struct rq *rq, struct task_struct *p) { } +static inline int uclamp_validate(struct task_struct *p, + const struct sched_attr *attr) +{ + return -EOPNOTSUPP; +} +static void __setscheduler_uclamp(struct task_struct *p, + const struct sched_attr *attr) { } +static inline void uclamp_fork(struct task_struct *p) { } + +long schedtune_task_margin(struct task_struct *task); + +#ifdef CONFIG_SMP +unsigned int uclamp_task(struct task_struct *p) +{ + unsigned long util = task_util_est(p); +#ifdef CONFIG_SCHED_TUNE + long margin = schedtune_task_margin(p); + + trace_sched_boost_task(p, util, margin); + + util += margin; +#endif + + return util; +} + +bool uclamp_boosted(struct task_struct *p) +{ +#ifdef CONFIG_SCHED_TUNE + return schedtune_task_boost(p) > 0; +#endif + return false; +} + +bool uclamp_latency_sensitive(struct task_struct *p) +{ +#ifdef CONFIG_SCHED_TUNE + return schedtune_prefer_idle(p) != 0; +#endif + return false; +} +#endif /* CONFIG_SMP */ + +static inline void init_uclamp(void) { } +#endif /* CONFIG_UCLAMP_TASK */ + static inline void enqueue_task(struct rq *rq, struct task_struct *p, int flags) { if (!(flags & ENQUEUE_NOCLOCK)) @@ -740,6 +1332,7 @@ static inline void enqueue_task(struct rq *rq, struct task_struct *p, int flags) psi_enqueue(p, flags & ENQUEUE_WAKEUP); } + uclamp_rq_inc(rq, p); p->sched_class->enqueue_task(rq, p, flags); walt_update_last_enqueue(p); trace_sched_enq_deq_task(p, 1, cpumask_bits(&p->cpus_allowed)[0]); @@ -755,6 +1348,7 @@ static inline void dequeue_task(struct rq *rq, struct task_struct *p, int flags) psi_dequeue(p, flags & DEQUEUE_SLEEP); } + uclamp_rq_dec(rq, p); p->sched_class->dequeue_task(rq, p, flags); #ifdef CONFIG_SCHED_WALT if (p == rq->ed_task) @@ -1892,7 +2486,8 @@ static void ttwu_queue(struct task_struct *p, int cpu, int wake_flags) struct rq_flags rf; #if defined(CONFIG_SMP) - if (sched_feat(TTWU_QUEUE) && !cpus_share_cache(smp_processor_id(), cpu)) { + if ((sched_feat(TTWU_QUEUE) && !cpus_share_cache(smp_processor_id(), cpu)) || + walt_want_remote_wakeup()) { sched_clock_cpu(cpu); /* Sync clocks across CPUs */ ttwu_queue_remote(p, cpu, wake_flags); return; @@ -2427,6 +3022,8 @@ int sched_fork(unsigned long clone_flags, struct task_struct *p) */ p->prio = current->normal_prio; + uclamp_fork(p); + /* * Revert to default priority/policy on fork if requested. */ @@ -4282,6 +4879,13 @@ static void __setscheduler_params(struct task_struct *p, static void __setscheduler(struct rq *rq, struct task_struct *p, const struct sched_attr *attr, bool keep_boost) { + /* + * If params can't change scheduling class changes aren't allowed + * either. + */ + if (attr->sched_flags & SCHED_FLAG_KEEP_PARAMS) + return; + __setscheduler_params(p, attr); /* @@ -4419,6 +5023,13 @@ static int __sched_setscheduler(struct task_struct *p, return retval; } + /* Update task specific "requested" clamps */ + if (attr->sched_flags & SCHED_FLAG_UTIL_CLAMP) { + retval = uclamp_validate(p, attr); + if (retval) + return retval; + } + /* * Make sure no PI-waiters arrive (or leave) while we are * changing the priority of the task: @@ -4448,6 +5059,8 @@ static int __sched_setscheduler(struct task_struct *p, goto change; if (dl_policy(policy) && dl_param_changed(p, attr)) goto change; + if (attr->sched_flags & SCHED_FLAG_UTIL_CLAMP) + goto change; p->sched_reset_on_fork = reset_on_fork; task_rq_unlock(rq, p, &rf); @@ -4528,7 +5141,9 @@ static int __sched_setscheduler(struct task_struct *p, put_prev_task(rq, p); prev_class = p->sched_class; + __setscheduler(rq, p, attr, pi); + __setscheduler_uclamp(p, attr); if (queued) { /* @@ -4704,6 +5319,10 @@ static int sched_copy_attr(struct sched_attr __user *uattr, struct sched_attr *a if (ret) return -EFAULT; + if ((attr->sched_flags & SCHED_FLAG_UTIL_CLAMP) && + size < SCHED_ATTR_SIZE_VER1) + return -EINVAL; + /* * XXX: Do we want to be lenient like existing syscalls; or do we want * to be strict and return an error on out-of-bounds values? @@ -4767,14 +5386,21 @@ SYSCALL_DEFINE3(sched_setattr, pid_t, pid, struct sched_attr __user *, uattr, if ((int)attr.sched_policy < 0) return -EINVAL; + if (attr.sched_flags & SCHED_FLAG_KEEP_POLICY) + attr.sched_policy = SETPARAM_POLICY; rcu_read_lock(); retval = -ESRCH; p = find_process_by_pid(pid); - if (p != NULL) - retval = sched_setattr(p, &attr); + if (likely(p)) + get_task_struct(p); rcu_read_unlock(); + if (likely(p)) { + retval = sched_setattr(p, &attr); + put_task_struct(p); + } + return retval; } @@ -4849,37 +5475,40 @@ SYSCALL_DEFINE2(sched_getparam, pid_t, pid, struct sched_param __user *, param) return retval; } -static int sched_read_attr(struct sched_attr __user *uattr, - struct sched_attr *attr, - unsigned int usize) +/* + * Copy the kernel size attribute structure (which might be larger + * than what user-space knows about) to user-space. + * + * Note that all cases are valid: user-space buffer can be larger or + * smaller than the kernel-space buffer. The usual case is that both + * have the same size. + */ +static int +sched_attr_copy_to_user(struct sched_attr __user *uattr, + struct sched_attr *kattr, + unsigned int usize) { - int ret; + unsigned int ksize = sizeof(*kattr); if (!access_ok(VERIFY_WRITE, uattr, usize)) return -EFAULT; /* - * If we're handed a smaller struct than we know of, - * ensure all the unknown bits are 0 - i.e. old - * user-space does not get uncomplete information. + * sched_getattr() ABI forwards and backwards compatibility: + * + * If usize == ksize then we just copy everything to user-space and all is good. + * + * If usize < ksize then we only copy as much as user-space has space for, + * this keeps ABI compatibility as well. We skip the rest. + * + * If usize > ksize then user-space is using a newer version of the ABI, + * which part the kernel doesn't know about. Just ignore it - tooling can + * detect the kernel's knowledge of attributes from the attr->size value + * which is set to ksize in this case. */ - if (usize < sizeof(*attr)) { - unsigned char *addr; - unsigned char *end; - - addr = (void *)attr + usize; - end = (void *)attr + sizeof(*attr); - - for (; addr < end; addr++) { - if (*addr) - return -EFBIG; - } - - attr->size = usize; - } + kattr->size = min(usize, ksize); - ret = copy_to_user(uattr, attr, attr->size); - if (ret) + if (copy_to_user(uattr, kattr, kattr->size)) return -EFAULT; return 0; @@ -4889,20 +5518,18 @@ static int sched_read_attr(struct sched_attr __user *uattr, * sys_sched_getattr - similar to sched_getparam, but with sched_attr * @pid: the pid in question. * @uattr: structure containing the extended parameters. - * @size: sizeof(attr) for fwd/bwd comp. + * @usize: sizeof(attr) that user-space knows about, for forwards and backwards compatibility. * @flags: for future extension. */ SYSCALL_DEFINE4(sched_getattr, pid_t, pid, struct sched_attr __user *, uattr, - unsigned int, size, unsigned int, flags) + unsigned int, usize, unsigned int, flags) { - struct sched_attr attr = { - .size = sizeof(struct sched_attr), - }; + struct sched_attr kattr = { }; struct task_struct *p; int retval; - if (!uattr || pid < 0 || size > PAGE_SIZE || - size < SCHED_ATTR_SIZE_VER0 || flags) + if (!uattr || pid < 0 || usize > PAGE_SIZE || + usize < SCHED_ATTR_SIZE_VER0 || flags) return -EINVAL; rcu_read_lock(); @@ -4915,20 +5542,24 @@ SYSCALL_DEFINE4(sched_getattr, pid_t, pid, struct sched_attr __user *, uattr, if (retval) goto out_unlock; - attr.sched_policy = p->policy; + kattr.sched_policy = p->policy; if (p->sched_reset_on_fork) - attr.sched_flags |= SCHED_FLAG_RESET_ON_FORK; + kattr.sched_flags |= SCHED_FLAG_RESET_ON_FORK; if (task_has_dl_policy(p)) - __getparam_dl(p, &attr); + __getparam_dl(p, &kattr); else if (task_has_rt_policy(p)) - attr.sched_priority = p->rt_priority; + kattr.sched_priority = p->rt_priority; else - attr.sched_nice = task_nice(p); + kattr.sched_nice = task_nice(p); + +#ifdef CONFIG_UCLAMP_TASK + kattr.sched_util_min = p->uclamp_req[UCLAMP_MIN].value; + kattr.sched_util_max = p->uclamp_req[UCLAMP_MAX].value; +#endif rcu_read_unlock(); - retval = sched_read_attr(uattr, &attr, size); - return retval; + return sched_attr_copy_to_user(uattr, &kattr, usize); out_unlock: rcu_read_unlock(); @@ -6673,6 +7304,8 @@ void __init sched_init(void) psi_init(); + init_uclamp(); + scheduler_running = 1; } @@ -6844,6 +7477,20 @@ void ia64_set_curr_task(int cpu, struct task_struct *p) /* task_group_lock serializes the addition/removal of task groups */ static DEFINE_SPINLOCK(task_group_lock); +static inline void alloc_uclamp_sched_group(struct task_group *tg, + struct task_group *parent) +{ +#ifdef CONFIG_UCLAMP_TASK_GROUP + enum uclamp_id clamp_id; + + for_each_clamp_id(clamp_id) { + uclamp_se_set(&tg->uclamp_req[clamp_id], + uclamp_none(clamp_id), false); + tg->uclamp[clamp_id] = parent->uclamp[clamp_id]; + } +#endif +} + static void sched_free_group(struct task_group *tg) { free_fair_sched_group(tg); @@ -6867,6 +7514,8 @@ struct task_group *sched_create_group(struct task_group *parent) if (!alloc_rt_sched_group(tg, parent)) goto err; + alloc_uclamp_sched_group(tg, parent); + return tg; err: @@ -7006,6 +7655,12 @@ static int cpu_cgroup_css_online(struct cgroup_subsys_state *css) if (parent) sched_online_group(tg, parent); + +#ifdef CONFIG_UCLAMP_TASK_GROUP + /* Propagate the effective uclamp value for the new group */ + cpu_util_update_eff(css); +#endif + return 0; } @@ -7083,6 +7738,199 @@ static void cpu_cgroup_attach(struct cgroup_taskset *tset) sched_move_task(task); } +#ifdef CONFIG_UCLAMP_TASK_GROUP +static void cpu_util_update_eff(struct cgroup_subsys_state *css) +{ + struct cgroup_subsys_state *top_css = css; + struct uclamp_se *uc_parent = NULL; + struct uclamp_se *uc_se = NULL; + unsigned int eff[UCLAMP_CNT]; + enum uclamp_id clamp_id; + unsigned int clamps; + + css_for_each_descendant_pre(css, top_css) { + uc_parent = css_tg(css)->parent + ? css_tg(css)->parent->uclamp : NULL; + + for_each_clamp_id(clamp_id) { + /* Assume effective clamps matches requested clamps */ + eff[clamp_id] = css_tg(css)->uclamp_req[clamp_id].value; + /* Cap effective clamps with parent's effective clamps */ + if (uc_parent && + eff[clamp_id] > uc_parent[clamp_id].value) { + eff[clamp_id] = uc_parent[clamp_id].value; + } + } + /* Ensure protection is always capped by limit */ + eff[UCLAMP_MIN] = min(eff[UCLAMP_MIN], eff[UCLAMP_MAX]); + + /* Propagate most restrictive effective clamps */ + clamps = 0x0; + uc_se = css_tg(css)->uclamp; + for_each_clamp_id(clamp_id) { + if (eff[clamp_id] == uc_se[clamp_id].value) + continue; + uc_se[clamp_id].value = eff[clamp_id]; + uc_se[clamp_id].bucket_id = uclamp_bucket_id(eff[clamp_id]); + clamps |= (0x1 << clamp_id); + } + if (!clamps) { + css = css_rightmost_descendant(css); + continue; + } + + /* Immediately update descendants RUNNABLE tasks */ + uclamp_update_active_tasks(css, clamps); + } +} + +/* + * Integer 10^N with a given N exponent by casting to integer the literal "1eN" + * C expression. Since there is no way to convert a macro argument (N) into a + * character constant, use two levels of macros. + */ +#define _POW10(exp) ((unsigned int)1e##exp) +#define POW10(exp) _POW10(exp) + +struct uclamp_request { +#define UCLAMP_PERCENT_SHIFT 2 +#define UCLAMP_PERCENT_SCALE (100 * POW10(UCLAMP_PERCENT_SHIFT)) + s64 percent; + u64 util; + int ret; +}; + +static inline struct uclamp_request +capacity_from_percent(char *buf) +{ + struct uclamp_request req = { + .percent = UCLAMP_PERCENT_SCALE, + .util = SCHED_CAPACITY_SCALE, + .ret = 0, + }; + + buf = strim(buf); + if (strcmp(buf, "max")) { + req.ret = cgroup_parse_float(buf, UCLAMP_PERCENT_SHIFT, + &req.percent); + if (req.ret) + return req; + if ((u64)req.percent > UCLAMP_PERCENT_SCALE) { + req.ret = -ERANGE; + return req; + } + + req.util = req.percent << SCHED_CAPACITY_SHIFT; + req.util = DIV_ROUND_CLOSEST_ULL(req.util, UCLAMP_PERCENT_SCALE); + } + + return req; +} + +static ssize_t cpu_uclamp_write(struct kernfs_open_file *of, char *buf, + size_t nbytes, loff_t off, + enum uclamp_id clamp_id) +{ + struct uclamp_request req; + struct task_group *tg; + + req = capacity_from_percent(buf); + if (req.ret) + return req.ret; + + mutex_lock(&uclamp_mutex); + rcu_read_lock(); + + tg = css_tg(of_css(of)); + if (tg->uclamp_req[clamp_id].value != req.util) + uclamp_se_set(&tg->uclamp_req[clamp_id], req.util, false); + + /* + * Because of not recoverable conversion rounding we keep track of the + * exact requested value + */ + tg->uclamp_pct[clamp_id] = req.percent; + + /* Update effective clamps to track the most restrictive value */ + cpu_util_update_eff(of_css(of)); + + rcu_read_unlock(); + mutex_unlock(&uclamp_mutex); + + return nbytes; +} + +static ssize_t cpu_uclamp_min_write(struct kernfs_open_file *of, + char *buf, size_t nbytes, + loff_t off) +{ + return cpu_uclamp_write(of, buf, nbytes, off, UCLAMP_MIN); +} + +static ssize_t cpu_uclamp_max_write(struct kernfs_open_file *of, + char *buf, size_t nbytes, + loff_t off) +{ + return cpu_uclamp_write(of, buf, nbytes, off, UCLAMP_MAX); +} + +static inline void cpu_uclamp_print(struct seq_file *sf, + enum uclamp_id clamp_id) +{ + struct task_group *tg; + u64 util_clamp; + u64 percent; + u32 rem; + + rcu_read_lock(); + tg = css_tg(seq_css(sf)); + util_clamp = tg->uclamp_req[clamp_id].value; + rcu_read_unlock(); + + if (util_clamp == SCHED_CAPACITY_SCALE) { + seq_puts(sf, "max\n"); + return; + } + + percent = tg->uclamp_pct[clamp_id]; + percent = div_u64_rem(percent, POW10(UCLAMP_PERCENT_SHIFT), &rem); + seq_printf(sf, "%llu.%0*u\n", percent, UCLAMP_PERCENT_SHIFT, rem); +} + +static int cpu_uclamp_min_show(struct seq_file *sf, void *v) +{ + cpu_uclamp_print(sf, UCLAMP_MIN); + return 0; +} + +static int cpu_uclamp_max_show(struct seq_file *sf, void *v) +{ + cpu_uclamp_print(sf, UCLAMP_MAX); + return 0; +} + +static int cpu_uclamp_ls_write_u64(struct cgroup_subsys_state *css, + struct cftype *cftype, u64 ls) +{ + struct task_group *tg; + + if (ls > 1) + return -EINVAL; + tg = css_tg(css); + tg->latency_sensitive = (unsigned int) ls; + + return 0; +} + +static u64 cpu_uclamp_ls_read_u64(struct cgroup_subsys_state *css, + struct cftype *cft) +{ + struct task_group *tg = css_tg(css); + + return (u64) tg->latency_sensitive; +} +#endif /* CONFIG_UCLAMP_TASK_GROUP */ + #ifdef CONFIG_FAIR_GROUP_SCHED static int cpu_shares_write_u64(struct cgroup_subsys_state *css, struct cftype *cftype, u64 shareval) @@ -7427,6 +8275,26 @@ static struct cftype cpu_legacy_files[] = { .read_u64 = cpu_rt_period_read_uint, .write_u64 = cpu_rt_period_write_uint, }, +#endif +#ifdef CONFIG_UCLAMP_TASK_GROUP + { + .name = "uclamp.min", + .flags = CFTYPE_NOT_ON_ROOT, + .seq_show = cpu_uclamp_min_show, + .write = cpu_uclamp_min_write, + }, + { + .name = "uclamp.max", + .flags = CFTYPE_NOT_ON_ROOT, + .seq_show = cpu_uclamp_max_show, + .write = cpu_uclamp_max_write, + }, + { + .name = "uclamp.latency_sensitive", + .flags = CFTYPE_NOT_ON_ROOT, + .read_u64 = cpu_uclamp_ls_read_u64, + .write_u64 = cpu_uclamp_ls_write_u64, + }, #endif { } /* Terminate */ }; @@ -7594,6 +8462,26 @@ static struct cftype cpu_files[] = { .seq_show = cpu_max_show, .write = cpu_max_write, }, +#endif +#ifdef CONFIG_UCLAMP_TASK_GROUP + { + .name = "uclamp.min", + .flags = CFTYPE_NOT_ON_ROOT, + .seq_show = cpu_uclamp_min_show, + .write = cpu_uclamp_min_write, + }, + { + .name = "uclamp.max", + .flags = CFTYPE_NOT_ON_ROOT, + .seq_show = cpu_uclamp_max_show, + .write = cpu_uclamp_max_write, + }, + { + .name = "uclamp.latency_sensitive", + .flags = CFTYPE_NOT_ON_ROOT, + .read_u64 = cpu_uclamp_ls_read_u64, + .write_u64 = cpu_uclamp_ls_write_u64, + }, #endif { } /* terminate */ }; diff --git a/kernel/sched/core_ctl.c b/kernel/sched/core_ctl.c index dc4d2eb06635f232c8211aeab49b85c9aa249678..f31d727d74f4f86873352dbe64b2167238845785 100644 --- a/kernel/sched/core_ctl.c +++ b/kernel/sched/core_ctl.c @@ -65,8 +65,8 @@ static struct cluster_data cluster_state[MAX_CLUSTERS]; static unsigned int num_clusters; #define for_each_cluster(cluster, idx) \ - for ((cluster) = &cluster_state[idx]; (idx) < num_clusters;\ - (idx)++, (cluster) = &cluster_state[idx]) + for (; (idx) < num_clusters && ((cluster) = &cluster_state[idx]);\ + (idx)++) static DEFINE_SPINLOCK(state_lock); static void apply_need(struct cluster_data *state); diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c index 6d8430116f6c049f420d5a42078deb79ebc16196..af3f3c04c66e6d43f1a7bf8a2d931905992b2652 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -332,15 +332,17 @@ static unsigned int get_next_freq(struct sugov_policy *sg_policy, * based on the task model parameters and gives the minimal utilization * required to meet deadlines. */ -unsigned long schedutil_freq_util(int cpu, unsigned long util, - unsigned long max, enum schedutil_type type) +unsigned long schedutil_cpu_util(int cpu, unsigned long util_cfs, + unsigned long max, enum schedutil_type type, + struct task_struct *p) { - unsigned long dl_util, irq; + unsigned long dl_util, util, irq; struct rq *rq = cpu_rq(cpu); - if (sched_feat(SUGOV_RT_MAX_FREQ) && type == FREQUENCY_UTIL && - rt_rq_is_runnable(&rq->rt)) + if (sched_feat(SUGOV_RT_MAX_FREQ) && !IS_BUILTIN(CONFIG_UCLAMP_TASK) && + type == FREQUENCY_UTIL && rt_rq_is_runnable(&rq->rt)) { return max; + } /* * Early check to see if IRQ/steal time saturates the CPU, can be @@ -352,11 +354,21 @@ unsigned long schedutil_freq_util(int cpu, unsigned long util, return max; /* - * The function is called with @util defined as the aggregation (the - * sum) of RT and CFS signals, hence leaving the special case of DL - * to be delt with. The exact way of doing things depend on the calling - * context. + * Because the time spend on RT/DL tasks is visible as 'lost' time to + * CFS tasks and we use the same metric to track the effective + * utilization (PELT windows are synchronized) we can directly add them + * to obtain the CPU's actual utilization. + * + * CFS and RT utilization can be boosted or capped, depending on + * utilization clamp constraints requested by currently RUNNABLE + * tasks. + * When there are no CFS RUNNABLE tasks, clamps are released and + * frequency will be gracefully reduced with the utilization decay. */ + util = util_cfs + cpu_util_rt(rq); + if (type == FREQUENCY_UTIL) + util = uclamp_rq_util_with(rq, util, p); + dl_util = cpu_util_dl(rq); /* @@ -415,20 +427,26 @@ static unsigned long sugov_get_util(struct sugov_cpu *sg_cpu) sg_cpu->max = max; sg_cpu->bw_dl = cpu_bw_dl(rq); - return boosted_cpu_util(sg_cpu->cpu, 0, &sg_cpu->walt_load); + return stune_util(sg_cpu->cpu, 0, &sg_cpu->walt_load); } #else static unsigned long sugov_get_util(struct sugov_cpu *sg_cpu) { struct rq *rq = cpu_rq(sg_cpu->cpu); - unsigned long util = boosted_cpu_util(sg_cpu->cpu, cpu_util_rt(rq), - NULL); + +#ifdef CONFIG_SCHED_TUNE + unsigned long util = stune_util(sg_cpu->cpu, cpu_util_rt(rq), NULL); +#else + unsigned long util = cpu_util_freq(sg_cpu->cpu, NULL); +#endif + unsigned long util_cfs = util - cpu_util_rt(rq); unsigned long max = arch_scale_cpu_capacity(NULL, sg_cpu->cpu); sg_cpu->max = max; sg_cpu->bw_dl = cpu_bw_dl(rq); - return schedutil_freq_util(sg_cpu->cpu, util, max, FREQUENCY_UTIL); + return schedutil_cpu_util(sg_cpu->cpu, util_cfs, max, + FREQUENCY_UTIL, NULL); } #endif diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index f832fab9f386db5177c27e8aeb873ac2821a1b5b..40f2d69fe029f4d55f3c4e94282a92976247eab2 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -336,79 +336,103 @@ static inline struct cfs_rq *group_cfs_rq(struct sched_entity *grp) return grp->my_q; } -static inline void list_add_leaf_cfs_rq(struct cfs_rq *cfs_rq) +static inline bool list_add_leaf_cfs_rq(struct cfs_rq *cfs_rq) { - if (!cfs_rq->on_list) { - struct rq *rq = rq_of(cfs_rq); - int cpu = cpu_of(rq); + struct rq *rq = rq_of(cfs_rq); + int cpu = cpu_of(rq); + + if (cfs_rq->on_list) + return rq->tmp_alone_branch == &rq->leaf_cfs_rq_list; + + cfs_rq->on_list = 1; + + /* + * Ensure we either appear before our parent (if already + * enqueued) or force our parent to appear after us when it is + * enqueued. The fact that we always enqueue bottom-up + * reduces this to two cases and a special case for the root + * cfs_rq. Furthermore, it also means that we will always reset + * tmp_alone_branch either when the branch is connected + * to a tree or when we reach the top of the tree + */ + if (cfs_rq->tg->parent && + cfs_rq->tg->parent->cfs_rq[cpu]->on_list) { /* - * Ensure we either appear before our parent (if already - * enqueued) or force our parent to appear after us when it is - * enqueued. The fact that we always enqueue bottom-up - * reduces this to two cases and a special case for the root - * cfs_rq. Furthermore, it also means that we will always reset - * tmp_alone_branch either when the branch is connected - * to a tree or when we reach the beg of the tree + * If parent is already on the list, we add the child + * just before. Thanks to circular linked property of + * the list, this means to put the child at the tail + * of the list that starts by parent. */ - if (cfs_rq->tg->parent && - cfs_rq->tg->parent->cfs_rq[cpu]->on_list) { - /* - * If parent is already on the list, we add the child - * just before. Thanks to circular linked property of - * the list, this means to put the child at the tail - * of the list that starts by parent. - */ - list_add_tail_rcu(&cfs_rq->leaf_cfs_rq_list, - &(cfs_rq->tg->parent->cfs_rq[cpu]->leaf_cfs_rq_list)); - /* - * The branch is now connected to its tree so we can - * reset tmp_alone_branch to the beginning of the - * list. - */ - rq->tmp_alone_branch = &rq->leaf_cfs_rq_list; - } else if (!cfs_rq->tg->parent) { - /* - * cfs rq without parent should be put - * at the tail of the list. - */ - list_add_tail_rcu(&cfs_rq->leaf_cfs_rq_list, - &rq->leaf_cfs_rq_list); - /* - * We have reach the beg of a tree so we can reset - * tmp_alone_branch to the beginning of the list. - */ - rq->tmp_alone_branch = &rq->leaf_cfs_rq_list; - } else { - /* - * The parent has not already been added so we want to - * make sure that it will be put after us. - * tmp_alone_branch points to the beg of the branch - * where we will add parent. - */ - list_add_rcu(&cfs_rq->leaf_cfs_rq_list, - rq->tmp_alone_branch); - /* - * update tmp_alone_branch to points to the new beg - * of the branch - */ - rq->tmp_alone_branch = &cfs_rq->leaf_cfs_rq_list; - } + list_add_tail_rcu(&cfs_rq->leaf_cfs_rq_list, + &(cfs_rq->tg->parent->cfs_rq[cpu]->leaf_cfs_rq_list)); + /* + * The branch is now connected to its tree so we can + * reset tmp_alone_branch to the beginning of the + * list. + */ + rq->tmp_alone_branch = &rq->leaf_cfs_rq_list; + return true; + } - cfs_rq->on_list = 1; + if (!cfs_rq->tg->parent) { + /* + * cfs rq without parent should be put + * at the tail of the list. + */ + list_add_tail_rcu(&cfs_rq->leaf_cfs_rq_list, + &rq->leaf_cfs_rq_list); + /* + * We have reach the top of a tree so we can reset + * tmp_alone_branch to the beginning of the list. + */ + rq->tmp_alone_branch = &rq->leaf_cfs_rq_list; + return true; } + + /* + * The parent has not already been added so we want to + * make sure that it will be put after us. + * tmp_alone_branch points to the begin of the branch + * where we will add parent. + */ + list_add_rcu(&cfs_rq->leaf_cfs_rq_list, rq->tmp_alone_branch); + /* + * update tmp_alone_branch to points to the new begin + * of the branch + */ + rq->tmp_alone_branch = &cfs_rq->leaf_cfs_rq_list; + return false; } static inline void list_del_leaf_cfs_rq(struct cfs_rq *cfs_rq) { if (cfs_rq->on_list) { + struct rq *rq = rq_of(cfs_rq); + + /* + * With cfs_rq being unthrottled/throttled during an enqueue, + * it can happen the tmp_alone_branch points the a leaf that + * we finally want to del. In this case, tmp_alone_branch moves + * to the prev element but it will point to rq->leaf_cfs_rq_list + * at the end of the enqueue. + */ + if (rq->tmp_alone_branch == &cfs_rq->leaf_cfs_rq_list) + rq->tmp_alone_branch = cfs_rq->leaf_cfs_rq_list.prev; + list_del_rcu(&cfs_rq->leaf_cfs_rq_list); cfs_rq->on_list = 0; } } -/* Iterate through all leaf cfs_rq's on a runqueue: */ -#define for_each_leaf_cfs_rq(rq, cfs_rq) \ - list_for_each_entry_rcu(cfs_rq, &rq->leaf_cfs_rq_list, leaf_cfs_rq_list) +static inline void assert_list_leaf_cfs_rq(struct rq *rq) +{ + SCHED_WARN_ON(rq->tmp_alone_branch != &rq->leaf_cfs_rq_list); +} + +/* Iterate thr' all leaf cfs_rq's on a runqueue */ +#define for_each_leaf_cfs_rq_safe(rq, cfs_rq, pos) \ + list_for_each_entry_safe(cfs_rq, pos, &rq->leaf_cfs_rq_list, \ + leaf_cfs_rq_list) /* Do the two (enqueued) entities belong to the same group ? */ static inline struct cfs_rq * @@ -486,16 +510,21 @@ static inline struct cfs_rq *group_cfs_rq(struct sched_entity *grp) return NULL; } -static inline void list_add_leaf_cfs_rq(struct cfs_rq *cfs_rq) +static inline bool list_add_leaf_cfs_rq(struct cfs_rq *cfs_rq) { + return true; } static inline void list_del_leaf_cfs_rq(struct cfs_rq *cfs_rq) { } -#define for_each_leaf_cfs_rq(rq, cfs_rq) \ - for (cfs_rq = &rq->cfs; cfs_rq; cfs_rq = NULL) +static inline void assert_list_leaf_cfs_rq(struct rq *rq) +{ +} + +#define for_each_leaf_cfs_rq_safe(rq, cfs_rq, pos) \ + for (cfs_rq = &rq->cfs, pos = NULL; cfs_rq; cfs_rq = pos) static inline struct sched_entity *parent_entity(struct sched_entity *se) { @@ -3748,7 +3777,7 @@ static inline unsigned long _task_util_est(struct task_struct *p) return max(ue.ewma, ue.enqueued); } -static inline unsigned long task_util_est(struct task_struct *p) +unsigned long task_util_est(struct task_struct *p) { #ifdef CONFIG_SCHED_WALT return p->ravg.demand_scaled; @@ -3756,6 +3785,20 @@ static inline unsigned long task_util_est(struct task_struct *p) return max(task_util(p), _task_util_est(p)); } +#ifdef CONFIG_UCLAMP_TASK +static inline unsigned long uclamp_task_util(struct task_struct *p) +{ + return clamp(task_util_est(p), + uclamp_eff_value(p, UCLAMP_MIN), + uclamp_eff_value(p, UCLAMP_MAX)); +} +#else +static inline unsigned long uclamp_task_util(struct task_struct *p) +{ + return task_util_est(p); +} +#endif + static inline void util_est_enqueue(struct cfs_rq *cfs_rq, struct task_struct *p) { @@ -3821,11 +3864,22 @@ util_est_dequeue(struct cfs_rq *cfs_rq, struct task_struct *p, bool task_sleep) if (ue.enqueued & UTIL_AVG_UNCHANGED) return; + /* + * Reset EWMA on utilization increases, the moving average is used only + * to smooth utilization decreases. + */ + ue.enqueued = (task_util(p) | UTIL_AVG_UNCHANGED); + if (sched_feat(UTIL_EST_FASTUP)) { + if (ue.ewma < ue.enqueued) { + ue.ewma = ue.enqueued; + goto done; + } + } + /* * Skip update of task's estimated utilization when its EWMA is * already ~1% close to its last activation value. */ - ue.enqueued = (task_util(p) | UTIL_AVG_UNCHANGED); last_ewma_diff = ue.enqueued - ue.ewma; if (within_margin(last_ewma_diff, (SCHED_CAPACITY_SCALE / 100))) return; @@ -3858,6 +3912,7 @@ util_est_dequeue(struct cfs_rq *cfs_rq, struct task_struct *p, bool task_sleep) ue.ewma <<= UTIL_EST_WEIGHT_SHIFT; ue.ewma += last_ewma_diff; ue.ewma >>= UTIL_EST_WEIGHT_SHIFT; +done: WRITE_ONCE(p->se.avg.util_est, ue); /* Update plots for Task's estimated utilization */ @@ -3890,7 +3945,7 @@ static inline bool task_fits_capacity(struct task_struct *p, else margin = sched_capacity_margin_up[task_cpu(p)]; - return capacity * 1024 > task_util_est(p) * margin; + return capacity * 1024 > uclamp_task_util(p) * margin; } static inline bool task_fits_max(struct task_struct *p, int cpu) @@ -4647,6 +4702,10 @@ static int tg_unthrottle_up(struct task_group *tg, void *data) /* adjust cfs_rq_clock_task() */ cfs_rq->throttled_clock_task_time += rq_clock_task(rq) - cfs_rq->throttled_clock_task; + + /* Add cfs_rq with already running entity in the list */ + if (cfs_rq->nr_running >= 1) + list_add_leaf_cfs_rq(cfs_rq); } return 0; @@ -4658,8 +4717,10 @@ static int tg_throttle_down(struct task_group *tg, void *data) struct cfs_rq *cfs_rq = tg->cfs_rq[cpu_of(rq)]; /* group is entering throttled state, stop time */ - if (!cfs_rq->throttle_count) + if (!cfs_rq->throttle_count) { cfs_rq->throttled_clock_task = rq_clock_task(rq); + list_del_leaf_cfs_rq(cfs_rq); + } cfs_rq->throttle_count++; return 0; @@ -4767,6 +4828,8 @@ void unthrottle_cfs_rq(struct cfs_rq *cfs_rq) break; } + assert_list_leaf_cfs_rq(rq); + if (!se) { add_nr_running(rq, task_delta); walt_inc_throttled_cfs_rq_stats(&rq->walt_stats, tcfs_rq); @@ -5235,6 +5298,12 @@ static void __maybe_unused unthrottle_offline_cfs_rqs(struct rq *rq) } #else /* CONFIG_CFS_BANDWIDTH */ + +static inline bool cfs_bandwidth_used(void) +{ + return false; +} + static inline u64 cfs_rq_clock_task(struct cfs_rq *cfs_rq) { return rq_clock_task(rq_of(cfs_rq)); @@ -5477,6 +5546,23 @@ enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags) update_overutilized_status(rq); } + if (cfs_bandwidth_used()) { + /* + * When bandwidth control is enabled; the cfs_rq_throttled() + * breaks in the above iteration can result in incomplete + * leaf list maintenance, resulting in triggering the assertion + * below. + */ + for_each_sched_entity(se) { + cfs_rq = cfs_rq_of(se); + + if (list_add_leaf_cfs_rq(cfs_rq)) + break; + } + } + + assert_list_leaf_cfs_rq(rq); + hrtick_update(rq); } @@ -6064,8 +6150,7 @@ schedtune_cpu_margin(unsigned long util, int cpu) return schedtune_margin(util, boost); } -static inline long -schedtune_task_margin(struct task_struct *task) +long schedtune_task_margin(struct task_struct *task) { int boost = schedtune_task_boost(task); unsigned long util; @@ -6081,7 +6166,7 @@ schedtune_task_margin(struct task_struct *task) } unsigned long -boosted_cpu_util(int cpu, unsigned long other_util, +stune_util(int cpu, unsigned long other_util, struct sched_walt_cpu_load *walt_load) { unsigned long util = min_t(unsigned long, SCHED_CAPACITY_SCALE, @@ -6101,25 +6186,8 @@ schedtune_cpu_margin(unsigned long util, int cpu) return 0; } -static inline int -schedtune_task_margin(struct task_struct *task) -{ - return 0; -} - #endif /* CONFIG_SCHED_TUNE */ -static inline unsigned long -boosted_task_util(struct task_struct *task) -{ - unsigned long util = task_util_est(task); - long margin = schedtune_task_margin(task); - - trace_sched_boost_task(task, util, margin); - - return util + margin; -} - static unsigned long cpu_util_without(int cpu, struct task_struct *p); static unsigned long capacity_spare_without(int cpu, struct task_struct *p) @@ -6822,14 +6890,13 @@ enum fastpaths { NONE = 0, SYNC_WAKEUP, PREV_CPU_FASTPATH, - MANY_WAKEUP, }; static void find_best_target(struct sched_domain *sd, cpumask_t *cpus, struct task_struct *p, struct find_best_target_env *fbt_env) { - unsigned long min_util = boosted_task_util(p); + unsigned long min_util = uclamp_task(p); unsigned long target_capacity = ULONG_MAX; unsigned long min_wake_util = ULONG_MAX; unsigned long target_max_spare_cap = 0; @@ -6862,6 +6929,8 @@ static void find_best_target(struct sched_domain *sd, cpumask_t *cpus, * performance CPU, thus requiring to maximise target_capacity. In this * case we initialise target_capacity to 0. */ + prefer_idle = uclamp_latency_sensitive(p); + boosted = uclamp_boosted(p); if (prefer_idle && boosted) target_capacity = 0; @@ -7384,11 +7453,20 @@ cpu_util_next_walt(int cpu, struct task_struct *p, int dst_cpu) static long compute_energy(struct task_struct *p, int dst_cpu, struct perf_domain *pd) { - long util, max_util, sum_util, energy = 0; + unsigned int max_util, cpu_util, cpu_cap; + unsigned long sum_util, energy = 0; int cpu; for (; pd; pd = pd->next) { + struct cpumask *pd_mask = perf_domain_span(pd); + + /* + * The energy model mandates all the CPUs of a performance + * domain have the same capacity. + */ + cpu_cap = arch_scale_cpu_capacity(NULL, cpumask_first(pd_mask)); max_util = sum_util = 0; + /* * The capacity state of CPUs of the current rd can be driven by * CPUs of another rd if they belong to the same performance @@ -7399,16 +7477,37 @@ compute_energy(struct task_struct *p, int dst_cpu, struct perf_domain *pd) * it will not appear in its pd list and will not be accounted * by compute_energy(). */ - for_each_cpu_and(cpu, perf_domain_span(pd), cpu_online_mask) { + for_each_cpu_and(cpu, pd_mask, cpu_online_mask) { #ifdef CONFIG_SCHED_WALT - util = cpu_util_next_walt(cpu, p, dst_cpu); + cpu_util = cpu_util_next_walt(cpu, p, dst_cpu); + sum_util += cpu_util; #else - util = cpu_util_next(cpu, p, dst_cpu); - util += cpu_util_rt(cpu_rq(cpu)); - util = schedutil_energy_util(cpu, util); + unsigned int util_cfs; + struct task_struct *tsk; + + util_cfs = cpu_util_next(cpu, p, dst_cpu); + + /* + * Busy time computation: utilization clamping is not + * required since the ratio (sum_util / cpu_capacity) + * is already enough to scale the EM reported power + * consumption at the (eventually clamped) cpu_capacity. + */ + sum_util += schedutil_cpu_util(cpu, util_cfs, cpu_cap, + ENERGY_UTIL, NULL); + + /* + * Performance domain frequency: utilization clamping + * must be considered since it affects the selection + * of the performance domain frequency. + * NOTE: in case RT tasks are running, by default the + * FREQUENCY_UTIL's utilization can be max OPP. + */ + tsk = cpu == dst_cpu ? p : NULL; + cpu_util = schedutil_cpu_util(cpu, util_cfs, cpu_cap, + FREQUENCY_UTIL, tsk); #endif - max_util = max(util, max_util); - sum_util += util; + max_util = max(max_util, cpu_util); } energy += em_pd_energy(pd->em_pd, max_util, sum_util); @@ -7422,8 +7521,8 @@ static void select_cpu_candidates(struct sched_domain *sd, cpumask_t *cpus, { int highest_spare_cap_cpu = prev_cpu, best_idle_cpu = -1; unsigned long spare_cap, max_spare_cap, util, cpu_cap; - bool prefer_idle = schedtune_prefer_idle(p); - bool boosted = schedtune_task_boost(p) > 0; + bool prefer_idle = uclamp_latency_sensitive(p); + bool boosted = uclamp_boosted(p); unsigned long target_cap = boosted ? 0 : ULONG_MAX; unsigned long highest_spare_cap = 0; unsigned int min_exit_lat = UINT_MAX; @@ -7438,9 +7537,19 @@ static void select_cpu_candidates(struct sched_domain *sd, cpumask_t *cpus, if (!cpumask_test_cpu(cpu, &p->cpus_allowed)) continue; - /* Skip CPUs that will be overutilized. */ util = cpu_util_next(cpu, p, cpu); cpu_cap = capacity_of(cpu); + spare_cap = cpu_cap - util; + + /* + * Skip CPUs that cannot satisfy the capacity request. + * IOW, placing the task there would make the CPU + * overutilized. Take uclamp into account to see how + * much capacity we can get out of the CPU; this is + * aligned with schedutil_cpu_util(). + */ + util = uclamp_rq_util_with(cpu_rq(cpu), util, p); + if (cpu_cap * 1024 < util * sched_capacity_margin_up[cpu]) continue; @@ -7449,7 +7558,6 @@ static void select_cpu_candidates(struct sched_domain *sd, cpumask_t *cpus, * Find the CPU with the maximum spare capacity in * the performance domain */ - spare_cap = cpu_cap - util; if (spare_cap > max_spare_cap) { max_spare_cap = spare_cap; max_spare_cap_cpu = cpu; @@ -7577,8 +7685,13 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu, int delta = 0; int task_boost = per_task_boost(p); int boosted = (schedtune_task_boost(p) > 0) || (task_boost > 0); - int start_cpu = get_start_cpu(p); + int start_cpu; + + if (is_many_wakeup(sibling_count_hint) && prev_cpu != cpu && + cpumask_test_cpu(prev_cpu, &p->cpus_allowed)) + return prev_cpu; + start_cpu = get_start_cpu(p); if (start_cpu < 0) goto eas_not_ready; @@ -7605,13 +7718,6 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu, goto done; } - if (is_many_wakeup(sibling_count_hint) && prev_cpu != cpu && - bias_to_this_cpu(p, prev_cpu, start_cpu)) { - best_energy_cpu = prev_cpu; - fbt_env.fastpath = MANY_WAKEUP; - goto done; - } - rcu_read_lock(); pd = rcu_dereference(rd->pd); if (!pd) @@ -7653,7 +7759,7 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu, /* If there is only one sensible candidate, select it now. */ cpu = cpumask_first(candidates); - if (weight == 1 && ((schedtune_prefer_idle(p) && idle_cpu(cpu)) || + if (weight == 1 && ((uclamp_latency_sensitive(p) && idle_cpu(cpu)) || (cpu == prev_cpu))) { best_energy_cpu = cpu; goto unlock; @@ -7755,7 +7861,7 @@ select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_f record_wakee(p); if (static_branch_unlikely(&sched_energy_present)) { - if (schedtune_prefer_idle(p) && !sched_feat(EAS_PREFER_IDLE) && !sync) + if (uclamp_latency_sensitive(p) && !sched_feat(EAS_PREFER_IDLE) && !sync) goto sd_loop; new_cpu = find_energy_efficient_cpu(p, prev_cpu, sync, @@ -8910,10 +9016,27 @@ static inline bool others_have_blocked(struct rq *rq) #ifdef CONFIG_FAIR_GROUP_SCHED +static inline bool cfs_rq_is_decayed(struct cfs_rq *cfs_rq) +{ + if (cfs_rq->load.weight) + return false; + + if (cfs_rq->avg.load_sum) + return false; + + if (cfs_rq->avg.util_sum) + return false; + + if (cfs_rq->avg.runnable_load_sum) + return false; + + return true; +} + static void update_blocked_averages(int cpu) { struct rq *rq = cpu_rq(cpu); - struct cfs_rq *cfs_rq; + struct cfs_rq *cfs_rq, *pos; const struct sched_class *curr_class; struct rq_flags rf; bool done = true; @@ -8925,13 +9048,9 @@ static void update_blocked_averages(int cpu) * Iterates the task_group tree in a bottom up fashion, see * list_add_leaf_cfs_rq() for details. */ - for_each_leaf_cfs_rq(rq, cfs_rq) { + for_each_leaf_cfs_rq_safe(rq, cfs_rq, pos) { struct sched_entity *se; - /* throttled entities do not contribute to load */ - if (throttled_hierarchy(cfs_rq)) - continue; - if (update_cfs_rq_load_avg(cfs_rq_clock_pelt(cfs_rq), cfs_rq)) update_tg_load_avg(cfs_rq, 0); @@ -8940,6 +9059,13 @@ static void update_blocked_averages(int cpu) if (se && !skip_blocked_update(se)) update_load_avg(cfs_rq_of(se), se, 0); + /* + * There can be a lot of idle CPU cgroups. Don't let fully + * decayed cfs_rqs linger on the list. + */ + if (cfs_rq_is_decayed(cfs_rq)) + list_del_leaf_cfs_rq(cfs_rq); + /* Don't need periodic decay once load/util_avg are null */ if (cfs_rq_has_blocked(cfs_rq)) done = false; @@ -12350,15 +12476,19 @@ const struct sched_class fair_sched_class = { #ifdef CONFIG_SCHED_WALT .fixup_walt_sched_stats = walt_fixup_sched_stats_fair, #endif + +#ifdef CONFIG_UCLAMP_TASK + .uclamp_enabled = 1, +#endif }; #ifdef CONFIG_SCHED_DEBUG void print_cfs_stats(struct seq_file *m, int cpu) { - struct cfs_rq *cfs_rq; + struct cfs_rq *cfs_rq, *pos; rcu_read_lock(); - for_each_leaf_cfs_rq(cpu_rq(cpu), cfs_rq) + for_each_leaf_cfs_rq_safe(cpu_rq(cpu), cfs_rq, pos) print_cfs_rq(m, cpu, cfs_rq); rcu_read_unlock(); } diff --git a/kernel/sched/features.h b/kernel/sched/features.h index e91c0c86934570c3a9250c64a91dc6c70d4a53b8..842c262757ac3126ae87e962114d63172851efb7 100644 --- a/kernel/sched/features.h +++ b/kernel/sched/features.h @@ -90,6 +90,7 @@ SCHED_FEAT(WA_BIAS, true) * UtilEstimation. Use estimated CPU utilization. */ SCHED_FEAT(UTIL_EST, true) +SCHED_FEAT(UTIL_EST_FASTUP, true) /* * Fast pre-selection of CPU candidates for EAS. diff --git a/kernel/sched/psi.c b/kernel/sched/psi.c index 101bb088769bca8e52d0115dd8b559828eea8978..7370d315345015301c24ed71e438a7940cec37bf 100644 --- a/kernel/sched/psi.c +++ b/kernel/sched/psi.c @@ -189,7 +189,8 @@ static void group_init(struct psi_group *group) for_each_possible_cpu(cpu) seqcount_init(&per_cpu_ptr(group->pcpu, cpu)->seq); - group->avg_next_update = sched_clock() + psi_period; + group->avg_last_update = sched_clock(); + group->avg_next_update = group->avg_last_update + psi_period; INIT_DEFERRABLE_WORK(&group->avgs_work, psi_avgs_work); mutex_init(&group->avgs_lock); /* Init trigger-related members */ @@ -520,7 +521,7 @@ static u64 window_update(struct psi_window *win, u64 now, u64 value) u32 remaining; remaining = win->size - elapsed; - growth += div_u64(win->prev_growth * remaining, win->size); + growth += div64_u64(win->prev_growth * remaining, win->size); } return growth; @@ -1318,7 +1319,10 @@ static ssize_t psi_write(struct file *file, const char __user *user_buf, if (static_branch_likely(&psi_disabled)) return -EOPNOTSUPP; - buf_size = min(nbytes, (sizeof(buf) - 1)); + if (!nbytes) + return -EINVAL; + + buf_size = min(nbytes, sizeof(buf)); if (copy_from_user(buf, user_buf, buf_size)) return -EFAULT; diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c index 60786da74f370cea994e3675c53f49eaacac83fd..0b2bc6c5a29171405b7d312288c2e7e16e26f97f 100644 --- a/kernel/sched/rt.c +++ b/kernel/sched/rt.c @@ -2635,6 +2635,10 @@ const struct sched_class rt_sched_class = { #ifdef CONFIG_SCHED_WALT .fixup_walt_sched_stats = fixup_walt_sched_stats_common, #endif + +#ifdef CONFIG_UCLAMP_TASK + .uclamp_enabled = 1, +#endif }; #ifdef CONFIG_RT_GROUP_SCHED diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index f800274b6132e10a09ef5246da089394ca2f51dd..701b7b7dac1b2094636e394d5f1bcf3ffe0616fb 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -456,6 +456,18 @@ struct task_group { #endif struct cfs_bandwidth cfs_bandwidth; + +#ifdef CONFIG_UCLAMP_TASK_GROUP + /* The two decimal precision [%] value requested from user-space */ + unsigned int uclamp_pct[UCLAMP_CNT]; + /* Clamp values requested for a task group */ + struct uclamp_se uclamp_req[UCLAMP_CNT]; + /* Effective clamp values used for a task group */ + struct uclamp_se uclamp[UCLAMP_CNT]; + /* Latency-sensitive flag used for a task group */ + unsigned int latency_sensitive; +#endif + }; #ifdef CONFIG_FAIR_GROUP_SCHED @@ -866,6 +878,48 @@ extern void rto_push_irq_work_func(struct irq_work *work); #endif #endif /* CONFIG_SMP */ +#ifdef CONFIG_UCLAMP_TASK +/* + * struct uclamp_bucket - Utilization clamp bucket + * @value: utilization clamp value for tasks on this clamp bucket + * @tasks: number of RUNNABLE tasks on this clamp bucket + * + * Keep track of how many tasks are RUNNABLE for a given utilization + * clamp value. + */ +struct uclamp_bucket { + unsigned long value : bits_per(SCHED_CAPACITY_SCALE); + unsigned long tasks : BITS_PER_LONG - bits_per(SCHED_CAPACITY_SCALE); +}; + +/* + * struct uclamp_rq - rq's utilization clamp + * @value: currently active clamp values for a rq + * @bucket: utilization clamp buckets affecting a rq + * + * Keep track of RUNNABLE tasks on a rq to aggregate their clamp values. + * A clamp value is affecting a rq when there is at least one task RUNNABLE + * (or actually running) with that value. + * + * There are up to UCLAMP_CNT possible different clamp values, currently there + * are only two: minimum utilization and maximum utilization. + * + * All utilization clamping values are MAX aggregated, since: + * - for util_min: we want to run the CPU at least at the max of the minimum + * utilization required by its currently RUNNABLE tasks. + * - for util_max: we want to allow the CPU to run up to the max of the + * maximum utilization allowed by its currently RUNNABLE tasks. + * + * Since on each system we expect only a limited number of different + * utilization clamp values (UCLAMP_BUCKETS), use a simple array to track + * the metrics required to compute all the per-rq utilization clamp values. + */ +struct uclamp_rq { + unsigned int value; + struct uclamp_bucket bucket[UCLAMP_BUCKETS]; +}; +#endif /* CONFIG_UCLAMP_TASK */ + /* * This is the main, per-CPU runqueue data structure. * @@ -904,6 +958,13 @@ struct rq { unsigned long nr_load_updates; u64 nr_switches; +#ifdef CONFIG_UCLAMP_TASK + /* Utilization clamp values based on CPU's RUNNABLE tasks */ + struct uclamp_rq uclamp[UCLAMP_CNT] ____cacheline_aligned; + unsigned int uclamp_flags; +#define UCLAMP_FLAG_IDLE 0x01 +#endif + struct cfs_rq cfs; struct rt_rq rt; struct dl_rq dl; @@ -1755,6 +1816,10 @@ extern const u32 sched_prio_to_wmult[40]; struct sched_class { const struct sched_class *next; +#ifdef CONFIG_UCLAMP_TASK + int uclamp_enabled; +#endif + void (*enqueue_task) (struct rq *rq, struct task_struct *p, int flags); void (*dequeue_task) (struct rq *rq, struct task_struct *p, int flags); void (*yield_task) (struct rq *rq); @@ -2157,8 +2222,8 @@ static inline unsigned long cpu_util_cum(int cpu, int delta) } #ifdef CONFIG_SCHED_TUNE -extern unsigned long boosted_cpu_util(int cpu, unsigned long other_util, - struct sched_walt_cpu_load *walt_load); +extern unsigned long stune_util(int cpu, unsigned long other_util, + struct sched_walt_cpu_load *walt_load); #endif static inline unsigned long @@ -2523,6 +2588,45 @@ static inline void cpufreq_update_util(struct rq *rq, unsigned int flags) static inline void cpufreq_update_util(struct rq *rq, unsigned int flags) {} #endif /* CONFIG_CPU_FREQ */ +#ifdef CONFIG_UCLAMP_TASK +unsigned long uclamp_eff_value(struct task_struct *p, enum uclamp_id clamp_id); + +static __always_inline +unsigned long uclamp_rq_util_with(struct rq *rq, unsigned long util, + struct task_struct *p) +{ + unsigned long min_util = READ_ONCE(rq->uclamp[UCLAMP_MIN].value); + unsigned long max_util = READ_ONCE(rq->uclamp[UCLAMP_MAX].value); + + if (p) { + min_util = max(min_util, uclamp_eff_value(p, UCLAMP_MIN)); + max_util = max(max_util, uclamp_eff_value(p, UCLAMP_MAX)); + } + + /* + * Since CPU's {min,max}_util clamps are MAX aggregated considering + * RUNNABLE tasks with _different_ clamps, we can end up with an + * inversion. Fix it now when the clamps are applied. + */ + if (unlikely(min_util >= max_util)) + return min_util; + + return clamp(util, min_util, max_util); +} +#else /* CONFIG_UCLAMP_TASK */ +static inline +unsigned long uclamp_rq_util_with(struct rq *rq, unsigned long util, + struct task_struct *p) +{ + return util; +} +#endif /* CONFIG_UCLAMP_TASK */ + +unsigned long task_util_est(struct task_struct *p); +unsigned int uclamp_task(struct task_struct *p); +bool uclamp_latency_sensitive(struct task_struct *p); +bool uclamp_boosted(struct task_struct *p); + #ifdef arch_scale_freq_capacity # ifndef arch_scale_freq_invariant # define arch_scale_freq_invariant() true @@ -2531,7 +2635,6 @@ static inline void cpufreq_update_util(struct rq *rq, unsigned int flags) {} # define arch_scale_freq_invariant() false #endif -#ifdef CONFIG_CPU_FREQ_GOV_SCHEDUTIL /** * enum schedutil_type - CPU utilization type * @FREQUENCY_UTIL: Utilization used to select frequency @@ -2547,23 +2650,26 @@ enum schedutil_type { ENERGY_UTIL, }; -unsigned long schedutil_freq_util(int cpu, unsigned long util, - unsigned long max, enum schedutil_type type); - -static inline unsigned long schedutil_energy_util(int cpu, unsigned long util) +#ifdef CONFIG_SMP +static inline unsigned long cpu_util_cfs(struct rq *rq) { - unsigned long max = arch_scale_cpu_capacity(NULL, cpu); + unsigned long util = READ_ONCE(rq->cfs.avg.util_avg); + + if (sched_feat(UTIL_EST)) { + util = max_t(unsigned long, util, + READ_ONCE(rq->cfs.avg.util_est.enqueued)); + } - return schedutil_freq_util(cpu, util, max, ENERGY_UTIL); -} -#else /* CONFIG_CPU_FREQ_GOV_SCHEDUTIL */ -static inline unsigned long schedutil_energy_util(int cpu, unsigned long util) -{ return util; } #endif -#ifdef CONFIG_SMP +#ifdef CONFIG_CPU_FREQ_GOV_SCHEDUTIL + +unsigned long schedutil_cpu_util(int cpu, unsigned long util_cfs, + unsigned long max, enum schedutil_type type, + struct task_struct *p); + static inline unsigned long cpu_bw_dl(struct rq *rq) { return (rq->dl.running_bw * SCHED_CAPACITY_SCALE) >> BW_SHIFT; @@ -2574,23 +2680,19 @@ static inline unsigned long cpu_util_dl(struct rq *rq) return READ_ONCE(rq->avg_dl.util_avg); } -static inline unsigned long cpu_util_cfs(struct rq *rq) +static inline unsigned long cpu_util_rt(struct rq *rq) { - unsigned long util = READ_ONCE(rq->cfs.avg.util_avg); - - if (sched_feat(UTIL_EST)) { - util = max_t(unsigned long, util, - READ_ONCE(rq->cfs.avg.util_est.enqueued)); - } - - return util; + return READ_ONCE(rq->avg_rt.util_avg); } -static inline unsigned long cpu_util_rt(struct rq *rq) +#else /* CONFIG_CPU_FREQ_GOV_SCHEDUTIL */ +static inline unsigned long schedutil_cpu_util(int cpu, unsigned long util_cfs, + unsigned long max, enum schedutil_type type, + struct task_struct *p) { - return READ_ONCE(rq->avg_rt.util_avg); + return 0; } -#endif +#endif /* CONFIG_CPU_FREQ_GOV_SCHEDUTIL */ #ifdef CONFIG_HAVE_SCHED_AVG_IRQ static inline unsigned long cpu_util_irq(struct rq *rq) @@ -2638,6 +2740,12 @@ enum sched_boost_policy { #ifdef CONFIG_SCHED_WALT +#define WALT_MANY_WAKEUP_DEFAULT 1000 +static inline bool walt_want_remote_wakeup(void) +{ + return sysctl_sched_many_wakeup_threshold < WALT_MANY_WAKEUP_DEFAULT; +} + static inline int cluster_first_cpu(struct sched_cluster *cluster) { return cpumask_first(&cluster->cpus); @@ -3086,6 +3194,10 @@ static inline unsigned int power_cost(int cpu, u64 demand) #endif static inline void note_task_waking(struct task_struct *p, u64 wallclock) { } +static inline bool walt_want_remote_wakeup(void) +{ + return false; +} #endif /* CONFIG_SCHED_WALT */ struct sched_avg_stats { diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c index d956d867334a21504e25ad58c9a783eca2d60867..d756a103bffaf4af69bc3150089261df3e34ecdc 100644 --- a/kernel/sched/topology.c +++ b/kernel/sched/topology.c @@ -203,6 +203,34 @@ sd_parent_degenerate(struct sched_domain *sd, struct sched_domain *parent) DEFINE_STATIC_KEY_FALSE(sched_energy_present); #ifdef CONFIG_ENERGY_MODEL +unsigned int sysctl_sched_energy_aware = 1; +DEFINE_MUTEX(sched_energy_mutex); +bool sched_energy_update; + +#ifdef CONFIG_PROC_SYSCTL +int sched_energy_aware_handler(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + int ret, state; + + if (write && !capable(CAP_SYS_ADMIN)) + return -EPERM; + + ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); + if (!ret && write) { + state = static_branch_unlikely(&sched_energy_present); + if (state != sysctl_sched_energy_aware) { + mutex_lock(&sched_energy_mutex); + sched_energy_update = 1; + rebuild_sched_domains(); + sched_energy_update = 0; + mutex_unlock(&sched_energy_mutex); + } + } + + return ret; +} +#endif static void free_pd(struct perf_domain *pd) { @@ -316,6 +344,9 @@ static bool build_perf_domains(const struct cpumask *cpu_map) int cpu = cpumask_first(cpu_map); struct root_domain *rd = cpu_rq(cpu)->rd; + if (!sysctl_sched_energy_aware) + goto free; + /* EAS is enabled for asymmetric CPU capacity topologies. */ if (!per_cpu(sd_asym_cpucapacity, cpu)) { if (sched_debug()) { diff --git a/kernel/sched/tune.h b/kernel/sched/tune.h index ab44614a96413cd8730cf99b0f23963bc0da3964..5ba191b94e153c30e38f10bf46d0a13002093b48 100644 --- a/kernel/sched/tune.h +++ b/kernel/sched/tune.h @@ -30,6 +30,5 @@ void schedtune_dequeue_task(struct task_struct *p, int cpu); #define schedtune_enqueue_task(task, cpu) do { } while (0) #define schedtune_dequeue_task(task, cpu) do { } while (0) -#define boosted_cpu_util(cpu, other_util, walt_load) cpu_util_cfs(cpu_rq(cpu)) - +#define stune_util(cpu, other_util, walt_load) cpu_util_cfs(cpu_rq(cpu)) #endif /* CONFIG_SCHED_TUNE */ diff --git a/kernel/sched/walt.c b/kernel/sched/walt.c index 3ef27fc6ec9afbfaa9d35af0b88086323936accb..11f0cfb5548cab9c48476417b597743325a5324d 100644 --- a/kernel/sched/walt.c +++ b/kernel/sched/walt.c @@ -1026,7 +1026,7 @@ unsigned int max_possible_efficiency = 1; unsigned int min_possible_efficiency = UINT_MAX; unsigned int sysctl_sched_conservative_pl; -unsigned int sysctl_sched_many_wakeup_threshold = 1000; +unsigned int sysctl_sched_many_wakeup_threshold = WALT_MANY_WAKEUP_DEFAULT; #define INC_STEP 8 #define DEC_STEP 2 diff --git a/kernel/seccomp.c b/kernel/seccomp.c index fd023ac24e10ea16dde538a4a3274387e9b7df8d..56e69203b65888be1e55db277fc81979269999a8 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -383,8 +383,8 @@ static struct seccomp_filter *seccomp_prepare_filter(struct sock_fprog *fprog) * behavior of privileged children. */ if (!task_no_new_privs(current) && - security_capable_noaudit(current_cred(), current_user_ns(), - CAP_SYS_ADMIN) != 0) + security_capable(current_cred(), current_user_ns(), + CAP_SYS_ADMIN, CAP_OPT_NOAUDIT) != 0) return ERR_PTR(-EACCES); /* Allocate a new seccomp_filter */ diff --git a/kernel/signal.c b/kernel/signal.c index 12057b25b3aec58ade6929be01010a232f3775ed..efeedcff7585aec9b14fe52b93327fcec2c22f68 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -90,6 +90,11 @@ static bool sig_task_ignored(struct task_struct *t, int sig, bool force) handler == SIG_DFL && !(force && sig_kernel_only(sig))) return true; + /* Only allow kernel generated signals to this kthread */ + if (unlikely((t->flags & PF_KTHREAD) && + (handler == SIG_KTHREAD_KERNEL) && !force)) + return true; + return sig_handler_ignored(handler, sig); } diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 8f828def515fa98e1f4ccbb2e4ced740f3bd26fd..bfa5b0810218d3fda152cbde63bee154e7156866 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -702,6 +702,22 @@ static struct ctl_table kern_table[] = { .mode = 0644, .proc_handler = sched_rr_handler, }, +#ifdef CONFIG_UCLAMP_TASK + { + .procname = "sched_util_clamp_min", + .data = &sysctl_sched_uclamp_util_min, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = sysctl_sched_uclamp_handler, + }, + { + .procname = "sched_util_clamp_max", + .data = &sysctl_sched_uclamp_util_max, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = sysctl_sched_uclamp_handler, + }, +#endif #ifdef CONFIG_SCHED_AUTOGROUP { .procname = "sched_autogroup_enabled", @@ -739,6 +755,17 @@ static struct ctl_table kern_table[] = { .extra1 = &zero, .extra2 = &two_hundred_fifty_five, }, +#if defined(CONFIG_ENERGY_MODEL) && defined(CONFIG_CPU_FREQ_GOV_SCHEDUTIL) + { + .procname = "sched_energy_aware", + .data = &sysctl_sched_energy_aware, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = sched_energy_aware_handler, + .extra1 = &zero, + .extra2 = &one, + }, +#endif #ifdef CONFIG_PROVE_LOCKING { .procname = "prove_locking", diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c index f4255a65c44b2347bd4559137e53c2bf7636cd0c..4bb158755ef734734eee2d5410e9beb94a7ad662 100644 --- a/kernel/time/alarmtimer.c +++ b/kernel/time/alarmtimer.c @@ -91,6 +91,7 @@ static int alarmtimer_rtc_add_device(struct device *dev, unsigned long flags; struct rtc_device *rtc = to_rtc_device(dev); struct wakeup_source *__ws; + int ret = 0; if (rtcdev) return -EBUSY; @@ -100,13 +101,13 @@ static int alarmtimer_rtc_add_device(struct device *dev, if (!device_may_wakeup(rtc->dev.parent)) return -1; - __ws = wakeup_source_register("alarmtimer"); + __ws = wakeup_source_register(dev, "alarmtimer"); spin_lock_irqsave(&rtcdev_lock, flags); if (!rtcdev) { if (!try_module_get(rtc->owner)) { - spin_unlock_irqrestore(&rtcdev_lock, flags); - return -1; + ret = -1; + goto unlock; } rtcdev = rtc; @@ -115,11 +116,12 @@ static int alarmtimer_rtc_add_device(struct device *dev, ws = __ws; __ws = NULL; } +unlock: spin_unlock_irqrestore(&rtcdev_lock, flags); wakeup_source_unregister(__ws); - return 0; + return ret; } static inline void alarmtimer_rtc_timer_init(void) diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index e20674326dcf16fd6f4bcf0939443777e71714fb..1f4bffb6459432c8e9d3eeeecf9e4aa29817dd52 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c @@ -311,8 +311,15 @@ static void clocksource_watchdog(struct timer_list *unused) next_cpu = cpumask_next(raw_smp_processor_id(), cpu_online_mask); if (next_cpu >= nr_cpu_ids) next_cpu = cpumask_first(cpu_online_mask); - watchdog_timer.expires += WATCHDOG_INTERVAL; - add_timer_on(&watchdog_timer, next_cpu); + + /* + * Arm timer if not already pending: could race with concurrent + * pair clocksource_stop_watchdog() clocksource_start_watchdog(). + */ + if (!timer_pending(&watchdog_timer)) { + watchdog_timer.expires += WATCHDOG_INTERVAL; + add_timer_on(&watchdog_timer, next_cpu); + } out: spin_unlock(&watchdog_lock); } diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 778ce93f3108d9ec3b5cd9d0ccbb2efd750ceced..0008021161ee067b0e6fde195512a56fd8186f1f 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -67,8 +67,9 @@ static void tick_do_update_jiffies64(ktime_t now) /* * Do a quick check without holding jiffies_lock: + * The READ_ONCE() pairs with two updates done later in this function. */ - delta = ktime_sub(now, last_jiffies_update); + delta = ktime_sub(now, READ_ONCE(last_jiffies_update)); if (delta < tick_period) return; @@ -79,8 +80,9 @@ static void tick_do_update_jiffies64(ktime_t now) if (delta >= tick_period) { delta = ktime_sub(delta, tick_period); - last_jiffies_update = ktime_add(last_jiffies_update, - tick_period); + /* Pairs with the lockless read in this function. */ + WRITE_ONCE(last_jiffies_update, + ktime_add(last_jiffies_update, tick_period)); /* Slow path for long timeouts */ if (unlikely(delta >= tick_period)) { @@ -88,8 +90,10 @@ static void tick_do_update_jiffies64(ktime_t now) ticks = ktime_divns(delta, incr); - last_jiffies_update = ktime_add_ns(last_jiffies_update, - incr * ticks); + /* Pairs with the lockless read in this function. */ + WRITE_ONCE(last_jiffies_update, + ktime_add_ns(last_jiffies_update, + incr * ticks)); } do_timer(++ticks); diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 00001660f8f80a9247151190f1482c1c93450834..0441265d518c78f196bafbad14dd55f06fadd3b0 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -5073,8 +5073,8 @@ static const struct file_operations ftrace_notrace_fops = { static DEFINE_MUTEX(graph_lock); -struct ftrace_hash *ftrace_graph_hash = EMPTY_HASH; -struct ftrace_hash *ftrace_graph_notrace_hash = EMPTY_HASH; +struct ftrace_hash __rcu *ftrace_graph_hash = EMPTY_HASH; +struct ftrace_hash __rcu *ftrace_graph_notrace_hash = EMPTY_HASH; enum graph_filter_type { GRAPH_FILTER_NOTRACE = 0, @@ -5345,8 +5345,15 @@ ftrace_graph_release(struct inode *inode, struct file *file) mutex_unlock(&graph_lock); - /* Wait till all users are no longer using the old hash */ - synchronize_sched(); + /* + * We need to do a hard force of sched synchronization. + * This is because we use preempt_disable() to do RCU, but + * the function tracers can be called where RCU is not watching + * (like before user_exit()). We can not rely on the RCU + * infrastructure to do the synchronization, thus we must do it + * ourselves. + */ + schedule_on_each_cpu(ftrace_sync); free_ftrace_hash(old_hash); } @@ -6520,9 +6527,10 @@ static void *fpid_next(struct seq_file *m, void *v, loff_t *pos) struct trace_array *tr = m->private; struct trace_pid_list *pid_list = rcu_dereference_sched(tr->function_pids); - if (v == FTRACE_NO_PIDS) + if (v == FTRACE_NO_PIDS) { + (*pos)++; return NULL; - + } return trace_pid_next(pid_list, v, pos); } diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 6bd66b648b58979bb2b6020d0a2881a8800147ec..d2b64671513040b4f13b483b8c2b88c302223f4f 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -1550,6 +1550,7 @@ static __init int init_trace_selftests(void) pr_info("Running postponed tracer tests:\n"); + tracing_selftest_running = true; list_for_each_entry_safe(p, n, &postponed_selftests, list) { ret = run_tracer_selftest(p->type); /* If the test fails, then warn and remove from available_tracers */ @@ -1568,6 +1569,7 @@ static __init int init_trace_selftests(void) list_del(&p->list); kfree(p); } + tracing_selftest_running = false; out: mutex_unlock(&trace_types_lock); diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 9c96cfb51a39f807e6f72d61ba81221a7cdd74c1..8d3378fd387b55b5403cad474ce63efb3c1cd8de 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -872,22 +872,31 @@ extern void __trace_graph_return(struct trace_array *tr, unsigned long flags, int pc); #ifdef CONFIG_DYNAMIC_FTRACE -extern struct ftrace_hash *ftrace_graph_hash; -extern struct ftrace_hash *ftrace_graph_notrace_hash; +extern struct ftrace_hash __rcu *ftrace_graph_hash; +extern struct ftrace_hash __rcu *ftrace_graph_notrace_hash; static inline int ftrace_graph_addr(struct ftrace_graph_ent *trace) { unsigned long addr = trace->func; int ret = 0; + struct ftrace_hash *hash; preempt_disable_notrace(); - if (ftrace_hash_empty(ftrace_graph_hash)) { + /* + * Have to open code "rcu_dereference_sched()" because the + * function graph tracer can be called when RCU is not + * "watching". + * Protected with schedule_on_each_cpu(ftrace_sync) + */ + hash = rcu_dereference_protected(ftrace_graph_hash, !preemptible()); + + if (ftrace_hash_empty(hash)) { ret = 1; goto out; } - if (ftrace_lookup_ip(ftrace_graph_hash, addr)) { + if (ftrace_lookup_ip(hash, addr)) { /* * This needs to be cleared on the return functions @@ -923,10 +932,20 @@ static inline void ftrace_graph_addr_finish(struct ftrace_graph_ret *trace) static inline int ftrace_graph_notrace_addr(unsigned long addr) { int ret = 0; + struct ftrace_hash *notrace_hash; preempt_disable_notrace(); - if (ftrace_lookup_ip(ftrace_graph_notrace_hash, addr)) + /* + * Have to open code "rcu_dereference_sched()" because the + * function graph tracer can be called when RCU is not + * "watching". + * Protected with schedule_on_each_cpu(ftrace_sync) + */ + notrace_hash = rcu_dereference_protected(ftrace_graph_notrace_hash, + !preemptible()); + + if (ftrace_lookup_ip(notrace_hash, addr)) ret = 1; preempt_enable_notrace(); diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c index 45c9ed10d4d05713f265ecc79ab2e50c7c5cb1ac..0d98c7738066d86654c05f51e413880094b125d9 100644 --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c @@ -49,6 +49,7 @@ struct hist_field { struct ftrace_event_field *field; unsigned long flags; hist_field_fn_t fn; + unsigned int ref; unsigned int size; unsigned int offset; unsigned int is_signed; @@ -1274,6 +1275,17 @@ static u64 hist_field_cpu(struct hist_field *hist_field, return cpu; } +/** + * check_field_for_var_ref - Check if a VAR_REF field references a variable + * @hist_field: The VAR_REF field to check + * @var_data: The hist trigger that owns the variable + * @var_idx: The trigger variable identifier + * + * Check the given VAR_REF field to see whether or not it references + * the given variable associated with the given trigger. + * + * Return: The VAR_REF field if it does reference the variable, NULL if not + */ static struct hist_field * check_field_for_var_ref(struct hist_field *hist_field, struct hist_trigger_data *var_data, @@ -1324,6 +1336,18 @@ check_field_for_var_refs(struct hist_trigger_data *hist_data, return found; } +/** + * find_var_ref - Check if a trigger has a reference to a trigger variable + * @hist_data: The hist trigger that might have a reference to the variable + * @var_data: The hist trigger that owns the variable + * @var_idx: The trigger variable identifier + * + * Check the list of var_refs[] on the first hist trigger to see + * whether any of them are references to the variable on the second + * trigger. + * + * Return: The VAR_REF field referencing the variable if so, NULL if not + */ static struct hist_field *find_var_ref(struct hist_trigger_data *hist_data, struct hist_trigger_data *var_data, unsigned int var_idx) @@ -1350,6 +1374,20 @@ static struct hist_field *find_var_ref(struct hist_trigger_data *hist_data, return found; } +/** + * find_any_var_ref - Check if there is a reference to a given trigger variable + * @hist_data: The hist trigger + * @var_idx: The trigger variable identifier + * + * Check to see whether the given variable is currently referenced by + * any other trigger. + * + * The trigger the variable is defined on is explicitly excluded - the + * assumption being that a self-reference doesn't prevent a trigger + * from being removed. + * + * Return: The VAR_REF field referencing the variable if so, NULL if not + */ static struct hist_field *find_any_var_ref(struct hist_trigger_data *hist_data, unsigned int var_idx) { @@ -1368,6 +1406,19 @@ static struct hist_field *find_any_var_ref(struct hist_trigger_data *hist_data, return found; } +/** + * check_var_refs - Check if there is a reference to any of trigger's variables + * @hist_data: The hist trigger + * + * A trigger can define one or more variables. If any one of them is + * currently referenced by any other trigger, this function will + * determine that. + + * Typically used to determine whether or not a trigger can be removed + * - if there are any references to a trigger's variables, it cannot. + * + * Return: True if there is a reference to any of trigger's variables + */ static bool check_var_refs(struct hist_trigger_data *hist_data) { struct hist_field *field; @@ -1511,11 +1562,13 @@ static struct hist_field *find_var(struct hist_trigger_data *hist_data, struct event_trigger_data *test; struct hist_field *hist_field; + lockdep_assert_held(&event_mutex); + hist_field = find_var_field(hist_data, var_name); if (hist_field) return hist_field; - list_for_each_entry_rcu(test, &file->triggers, list) { + list_for_each_entry(test, &file->triggers, list) { if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { test_data = test->private_data; hist_field = find_var_field(test_data, var_name); @@ -1565,7 +1618,9 @@ static struct hist_field *find_file_var(struct trace_event_file *file, struct event_trigger_data *test; struct hist_field *hist_field; - list_for_each_entry_rcu(test, &file->triggers, list) { + lockdep_assert_held(&event_mutex); + + list_for_each_entry(test, &file->triggers, list) { if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { test_data = test->private_data; hist_field = find_var_field(test_data, var_name); @@ -2171,6 +2226,23 @@ static int contains_operator(char *str) return field_op; } +static void get_hist_field(struct hist_field *hist_field) +{ + hist_field->ref++; +} + +static void __destroy_hist_field(struct hist_field *hist_field) +{ + if (--hist_field->ref > 1) + return; + + kfree(hist_field->var.name); + kfree(hist_field->name); + kfree(hist_field->type); + + kfree(hist_field); +} + static void destroy_hist_field(struct hist_field *hist_field, unsigned int level) { @@ -2182,14 +2254,13 @@ static void destroy_hist_field(struct hist_field *hist_field, if (!hist_field) return; + if (hist_field->flags & HIST_FIELD_FL_VAR_REF) + return; /* var refs will be destroyed separately */ + for (i = 0; i < HIST_FIELD_OPERANDS_MAX; i++) destroy_hist_field(hist_field->operands[i], level + 1); - kfree(hist_field->var.name); - kfree(hist_field->name); - kfree(hist_field->type); - - kfree(hist_field); + __destroy_hist_field(hist_field); } static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data, @@ -2206,6 +2277,8 @@ static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data, if (!hist_field) return NULL; + hist_field->ref = 1; + hist_field->hist_data = hist_data; if (flags & HIST_FIELD_FL_EXPR || flags & HIST_FIELD_FL_ALIAS) @@ -2316,6 +2389,12 @@ static void destroy_hist_fields(struct hist_trigger_data *hist_data) hist_data->fields[i] = NULL; } } + + for (i = 0; i < hist_data->n_var_refs; i++) { + WARN_ON(!(hist_data->var_refs[i]->flags & HIST_FIELD_FL_VAR_REF)); + __destroy_hist_field(hist_data->var_refs[i]); + hist_data->var_refs[i] = NULL; + } } static int init_var_ref(struct hist_field *ref_field, @@ -2374,11 +2453,38 @@ static int init_var_ref(struct hist_field *ref_field, goto out; } -static struct hist_field *create_var_ref(struct hist_field *var_field, +/** + * create_var_ref - Create a variable reference and attach it to trigger + * @hist_data: The trigger that will be referencing the variable + * @var_field: The VAR field to create a reference to + * @system: The optional system string + * @event_name: The optional event_name string + * + * Given a variable hist_field, create a VAR_REF hist_field that + * represents a reference to it. + * + * This function also adds the reference to the trigger that + * now references the variable. + * + * Return: The VAR_REF field if successful, NULL if not + */ +static struct hist_field *create_var_ref(struct hist_trigger_data *hist_data, + struct hist_field *var_field, char *system, char *event_name) { unsigned long flags = HIST_FIELD_FL_VAR_REF; struct hist_field *ref_field; + int i; + + /* Check if the variable already exists */ + for (i = 0; i < hist_data->n_var_refs; i++) { + ref_field = hist_data->var_refs[i]; + if (ref_field->var.idx == var_field->var.idx && + ref_field->var.hist_data == var_field->hist_data) { + get_hist_field(ref_field); + return ref_field; + } + } ref_field = create_hist_field(var_field->hist_data, NULL, flags, NULL); if (ref_field) { @@ -2386,6 +2492,9 @@ static struct hist_field *create_var_ref(struct hist_field *var_field, destroy_hist_field(ref_field, 0); return NULL; } + + hist_data->var_refs[hist_data->n_var_refs] = ref_field; + ref_field->var_ref_idx = hist_data->n_var_refs++; } return ref_field; @@ -2459,7 +2568,8 @@ static struct hist_field *parse_var_ref(struct hist_trigger_data *hist_data, var_field = find_event_var(hist_data, system, event_name, var_name); if (var_field) - ref_field = create_var_ref(var_field, system, event_name); + ref_field = create_var_ref(hist_data, var_field, + system, event_name); if (!ref_field) hist_err_event("Couldn't find variable: $", @@ -2579,8 +2689,6 @@ static struct hist_field *parse_atom(struct hist_trigger_data *hist_data, if (!s) { hist_field = parse_var_ref(hist_data, ref_system, ref_event, ref_var); if (hist_field) { - hist_data->var_refs[hist_data->n_var_refs] = hist_field; - hist_field->var_ref_idx = hist_data->n_var_refs++; if (var_name) { hist_field = create_alias(hist_data, hist_field, var_name); if (!hist_field) { @@ -2828,7 +2936,9 @@ static char *find_trigger_filter(struct hist_trigger_data *hist_data, { struct event_trigger_data *test; - list_for_each_entry_rcu(test, &file->triggers, list) { + lockdep_assert_held(&event_mutex); + + list_for_each_entry(test, &file->triggers, list) { if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { if (test->private_data == hist_data) return test->filter_str; @@ -2879,9 +2989,11 @@ find_compatible_hist(struct hist_trigger_data *target_hist_data, struct event_trigger_data *test; unsigned int n_keys; + lockdep_assert_held(&event_mutex); + n_keys = target_hist_data->n_fields - target_hist_data->n_vals; - list_for_each_entry_rcu(test, &file->triggers, list) { + list_for_each_entry(test, &file->triggers, list) { if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { hist_data = test->private_data; @@ -3354,7 +3466,6 @@ static int onmax_create(struct hist_trigger_data *hist_data, unsigned int var_ref_idx = hist_data->n_var_refs; struct field_var *field_var; char *onmax_var_str, *param; - unsigned long flags; unsigned int i; int ret = 0; @@ -3371,18 +3482,10 @@ static int onmax_create(struct hist_trigger_data *hist_data, return -EINVAL; } - flags = HIST_FIELD_FL_VAR_REF; - ref_field = create_hist_field(hist_data, NULL, flags, NULL); + ref_field = create_var_ref(hist_data, var_field, NULL, NULL); if (!ref_field) return -ENOMEM; - if (init_var_ref(ref_field, var_field, NULL, NULL)) { - destroy_hist_field(ref_field, 0); - ret = -ENOMEM; - goto out; - } - hist_data->var_refs[hist_data->n_var_refs] = ref_field; - ref_field->var_ref_idx = hist_data->n_var_refs++; data->onmax.var = ref_field; data->fn = onmax_save; @@ -3573,9 +3676,6 @@ static void save_synth_var_ref(struct hist_trigger_data *hist_data, struct hist_field *var_ref) { hist_data->synth_var_refs[hist_data->n_synth_var_refs++] = var_ref; - - hist_data->var_refs[hist_data->n_var_refs] = var_ref; - var_ref->var_ref_idx = hist_data->n_var_refs++; } static int check_synth_field(struct synth_event *event, @@ -3730,7 +3830,8 @@ static int onmatch_create(struct hist_trigger_data *hist_data, } if (check_synth_field(event, hist_field, field_pos) == 0) { - var_ref = create_var_ref(hist_field, system, event_name); + var_ref = create_var_ref(hist_data, hist_field, + system, event_name); if (!var_ref) { kfree(p); ret = -ENOMEM; @@ -4905,7 +5006,7 @@ static int hist_show(struct seq_file *m, void *v) goto out_unlock; } - list_for_each_entry_rcu(data, &event_file->triggers, list) { + list_for_each_entry(data, &event_file->triggers, list) { if (data->cmd_ops->trigger_type == ETT_EVENT_HIST) hist_trigger_show(m, data, n++); } @@ -5296,7 +5397,9 @@ static int hist_register_trigger(char *glob, struct event_trigger_ops *ops, if (hist_data->attrs->name && !named_data) goto new; - list_for_each_entry_rcu(test, &file->triggers, list) { + lockdep_assert_held(&event_mutex); + + list_for_each_entry(test, &file->triggers, list) { if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { if (!hist_trigger_match(data, test, named_data, false)) continue; @@ -5380,10 +5483,12 @@ static bool have_hist_trigger_match(struct event_trigger_data *data, struct event_trigger_data *test, *named_data = NULL; bool match = false; + lockdep_assert_held(&event_mutex); + if (hist_data->attrs->name) named_data = find_named_trigger(hist_data->attrs->name); - list_for_each_entry_rcu(test, &file->triggers, list) { + list_for_each_entry(test, &file->triggers, list) { if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { if (hist_trigger_match(data, test, named_data, false)) { match = true; @@ -5401,10 +5506,12 @@ static bool hist_trigger_check_refs(struct event_trigger_data *data, struct hist_trigger_data *hist_data = data->private_data; struct event_trigger_data *test, *named_data = NULL; + lockdep_assert_held(&event_mutex); + if (hist_data->attrs->name) named_data = find_named_trigger(hist_data->attrs->name); - list_for_each_entry_rcu(test, &file->triggers, list) { + list_for_each_entry(test, &file->triggers, list) { if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { if (!hist_trigger_match(data, test, named_data, false)) continue; @@ -5426,10 +5533,12 @@ static void hist_unregister_trigger(char *glob, struct event_trigger_ops *ops, struct event_trigger_data *test, *named_data = NULL; bool unregistered = false; + lockdep_assert_held(&event_mutex); + if (hist_data->attrs->name) named_data = find_named_trigger(hist_data->attrs->name); - list_for_each_entry_rcu(test, &file->triggers, list) { + list_for_each_entry(test, &file->triggers, list) { if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { if (!hist_trigger_match(data, test, named_data, false)) continue; @@ -5455,7 +5564,9 @@ static bool hist_file_check_refs(struct trace_event_file *file) struct hist_trigger_data *hist_data; struct event_trigger_data *test; - list_for_each_entry_rcu(test, &file->triggers, list) { + lockdep_assert_held(&event_mutex); + + list_for_each_entry(test, &file->triggers, list) { if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { hist_data = test->private_data; if (check_var_refs(hist_data)) diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c index cd12ecb66eb9236e3ca741517ea55105d744ab6e..9300e8bbf08ac80a70ec4103101d9c837c19ba69 100644 --- a/kernel/trace/trace_events_trigger.c +++ b/kernel/trace/trace_events_trigger.c @@ -115,9 +115,10 @@ static void *trigger_next(struct seq_file *m, void *t, loff_t *pos) { struct trace_event_file *event_file = event_file_data(m->private); - if (t == SHOW_AVAILABLE_TRIGGERS) + if (t == SHOW_AVAILABLE_TRIGGERS) { + (*pos)++; return NULL; - + } return seq_list_next(t, &event_file->triggers, pos); } @@ -495,7 +496,9 @@ void update_cond_flag(struct trace_event_file *file) struct event_trigger_data *data; bool set_cond = false; - list_for_each_entry_rcu(data, &file->triggers, list) { + lockdep_assert_held(&event_mutex); + + list_for_each_entry(data, &file->triggers, list) { if (data->filter || event_command_post_trigger(data->cmd_ops) || event_command_needs_rec(data->cmd_ops)) { set_cond = true; @@ -530,7 +533,9 @@ static int register_trigger(char *glob, struct event_trigger_ops *ops, struct event_trigger_data *test; int ret = 0; - list_for_each_entry_rcu(test, &file->triggers, list) { + lockdep_assert_held(&event_mutex); + + list_for_each_entry(test, &file->triggers, list) { if (test->cmd_ops->trigger_type == data->cmd_ops->trigger_type) { ret = -EEXIST; goto out; @@ -575,7 +580,9 @@ static void unregister_trigger(char *glob, struct event_trigger_ops *ops, struct event_trigger_data *data; bool unregistered = false; - list_for_each_entry_rcu(data, &file->triggers, list) { + lockdep_assert_held(&event_mutex); + + list_for_each_entry(data, &file->triggers, list) { if (data->cmd_ops->trigger_type == test->cmd_ops->trigger_type) { unregistered = true; list_del_rcu(&data->list); @@ -1490,7 +1497,9 @@ int event_enable_register_trigger(char *glob, struct event_trigger_data *test; int ret = 0; - list_for_each_entry_rcu(test, &file->triggers, list) { + lockdep_assert_held(&event_mutex); + + list_for_each_entry(test, &file->triggers, list) { test_enable_data = test->private_data; if (test_enable_data && (test->cmd_ops->trigger_type == @@ -1530,7 +1539,9 @@ void event_enable_unregister_trigger(char *glob, struct event_trigger_data *data; bool unregistered = false; - list_for_each_entry_rcu(data, &file->triggers, list) { + lockdep_assert_held(&event_mutex); + + list_for_each_entry(data, &file->triggers, list) { enable_data = data->private_data; if (enable_data && (data->cmd_ops->trigger_type == diff --git a/kernel/trace/trace_sched_switch.c b/kernel/trace/trace_sched_switch.c index e288168661e118c07ca5638bb2ddb3dd33fb1acc..e304196d7c2853ae0056c06c78d74819468265ca 100644 --- a/kernel/trace/trace_sched_switch.c +++ b/kernel/trace/trace_sched_switch.c @@ -89,8 +89,10 @@ static void tracing_sched_unregister(void) static void tracing_start_sched_switch(int ops) { - bool sched_register = (!sched_cmdline_ref && !sched_tgid_ref); + bool sched_register; + mutex_lock(&sched_register_mutex); + sched_register = (!sched_cmdline_ref && !sched_tgid_ref); switch (ops) { case RECORD_CMDLINE: diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c index 7d04b989075516f327c22a2b3dae77f227b75f1a..11f4dbd9526b69c0600c292e374db725795dc1e6 100644 --- a/kernel/trace/trace_sched_wakeup.c +++ b/kernel/trace/trace_sched_wakeup.c @@ -640,7 +640,7 @@ static void start_wakeup_tracer(struct trace_array *tr) if (ret) { pr_info("wakeup trace: Couldn't activate tracepoint" " probe to kernel_sched_migrate_task\n"); - return; + goto fail_deprobe_sched_switch; } wakeup_reset(tr); @@ -658,6 +658,8 @@ static void start_wakeup_tracer(struct trace_array *tr) printk(KERN_ERR "failed to start wakeup tracer\n"); return; +fail_deprobe_sched_switch: + unregister_trace_sched_switch(probe_wakeup_sched_switch, NULL); fail_deprobe_wake_new: unregister_trace_sched_wakeup_new(probe_wakeup, NULL); fail_deprobe: diff --git a/kernel/trace/trace_stack.c b/kernel/trace/trace_stack.c index 4237eba4ef2020c6debb9df2851702b859ae515f..40337094085c00a80b3d5c6b2b425075b3f65005 100644 --- a/kernel/trace/trace_stack.c +++ b/kernel/trace/trace_stack.c @@ -196,6 +196,11 @@ check_stack(unsigned long ip, unsigned long *stack) local_irq_restore(flags); } +/* Some archs may not define MCOUNT_INSN_SIZE */ +#ifndef MCOUNT_INSN_SIZE +# define MCOUNT_INSN_SIZE 0 +#endif + static void stack_trace_call(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *op, struct pt_regs *pt_regs) diff --git a/kernel/trace/trace_stat.c b/kernel/trace/trace_stat.c index 75bf1bcb4a8a56164641549f51bbec41584afe24..92b76f9e25eddd71ef4760ca2cce00504236b18d 100644 --- a/kernel/trace/trace_stat.c +++ b/kernel/trace/trace_stat.c @@ -278,18 +278,22 @@ static int tracing_stat_init(void) d_tracing = tracing_init_dentry(); if (IS_ERR(d_tracing)) - return 0; + return -ENODEV; stat_dir = tracefs_create_dir("trace_stat", d_tracing); - if (!stat_dir) + if (!stat_dir) { pr_warn("Could not create tracefs 'trace_stat' entry\n"); + return -ENOMEM; + } return 0; } static int init_stat_file(struct stat_session *session) { - if (!stat_dir && tracing_stat_init()) - return -ENODEV; + int ret; + + if (!stat_dir && (ret = tracing_stat_init())) + return ret; session->file = tracefs_create_file(session->ts->name, 0644, stat_dir, @@ -302,7 +306,7 @@ static int init_stat_file(struct stat_session *session) int register_stat_tracer(struct tracer_stat *trace) { struct stat_session *session, *node; - int ret; + int ret = -EINVAL; if (!trace) return -EINVAL; @@ -313,17 +317,15 @@ int register_stat_tracer(struct tracer_stat *trace) /* Already registered? */ mutex_lock(&all_stat_sessions_mutex); list_for_each_entry(node, &all_stat_sessions, session_list) { - if (node->ts == trace) { - mutex_unlock(&all_stat_sessions_mutex); - return -EINVAL; - } + if (node->ts == trace) + goto out; } - mutex_unlock(&all_stat_sessions_mutex); + ret = -ENOMEM; /* Init the session */ session = kzalloc(sizeof(*session), GFP_KERNEL); if (!session) - return -ENOMEM; + goto out; session->ts = trace; INIT_LIST_HEAD(&session->session_list); @@ -332,15 +334,16 @@ int register_stat_tracer(struct tracer_stat *trace) ret = init_stat_file(session); if (ret) { destroy_session(session); - return ret; + goto out; } + ret = 0; /* Register */ - mutex_lock(&all_stat_sessions_mutex); list_add_tail(&session->session_list, &all_stat_sessions); + out: mutex_unlock(&all_stat_sessions_mutex); - return 0; + return ret; } void unregister_stat_tracer(struct tracer_stat *trace) diff --git a/kernel/watchdog.c b/kernel/watchdog.c index b9214abf53947945bc05f934e7333831307384dd..cb8ccbdee4c9ddb6205d39479f8ad8f8cff607c7 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c @@ -162,6 +162,8 @@ static void lockup_detector_update_enable(void) #ifdef CONFIG_SOFTLOCKUP_DETECTOR +#define SOFTLOCKUP_RESET ULONG_MAX + /* Global variables, exported for sysctl */ unsigned int __read_mostly softlockup_panic = CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE; @@ -269,7 +271,7 @@ notrace void touch_softlockup_watchdog_sched(void) * Preemption can be enabled. It doesn't matter which CPU's timestamp * gets zeroed here, so use the raw_ operation. */ - raw_cpu_write(watchdog_touch_ts, 0); + raw_cpu_write(watchdog_touch_ts, SOFTLOCKUP_RESET); } notrace void touch_softlockup_watchdog(void) @@ -293,14 +295,14 @@ void touch_all_softlockup_watchdogs(void) * the softlockup check. */ for_each_cpu(cpu, &watchdog_allowed_mask) - per_cpu(watchdog_touch_ts, cpu) = 0; + per_cpu(watchdog_touch_ts, cpu) = SOFTLOCKUP_RESET; wq_watchdog_touch(-1); } void touch_softlockup_watchdog_sync(void) { __this_cpu_write(softlockup_touch_sync, true); - __this_cpu_write(watchdog_touch_ts, 0); + __this_cpu_write(watchdog_touch_ts, SOFTLOCKUP_RESET); } static int is_softlockup(unsigned long touch_ts) @@ -378,7 +380,7 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer) /* .. and repeat */ hrtimer_forward_now(hrtimer, ns_to_ktime(sample_period)); - if (touch_ts == 0) { + if (touch_ts == SOFTLOCKUP_RESET) { if (unlikely(__this_cpu_read(softlockup_touch_sync))) { /* * If the time stamp was touched atomically diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 9d22430d2ab6262c746ed0032f7d1c66cc3ff29e..11d5eeefebb814e55545da8bcbad8a21e508381f 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -1546,7 +1546,9 @@ static void __queue_delayed_work(int cpu, struct workqueue_struct *wq, struct work_struct *work = &dwork->work; WARN_ON_ONCE(!wq); +#ifndef CONFIG_CFI WARN_ON_ONCE(timer->function != delayed_work_timer_fn); +#endif WARN_ON_ONCE(timer_pending(timer)); WARN_ON_ONCE(!list_empty(&work->entry)); diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index adf7fb3ee69314aca33c4b75f31e2fe4018162c7..c46ecc5508bca244fa2256c2819470af4e6c8a3c 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -80,7 +80,7 @@ config DYNAMIC_DEBUG bool "Enable dynamic printk() support" default n depends on PRINTK - depends on DEBUG_FS + depends on (DEBUG_FS || PROC_FS) help Compiles debug level messages into the kernel, which would not @@ -98,8 +98,9 @@ config DYNAMIC_DEBUG Usage: Dynamic debugging is controlled via the 'dynamic_debug/control' file, - which is contained in the 'debugfs' filesystem. Thus, the debugfs - filesystem must first be mounted before making use of this feature. + which is contained in the 'debugfs' filesystem or procfs. + Thus, the debugfs or procfs filesystem must first be mounted before + making use of this feature. We refer the control file as: /dynamic_debug/control. This file contains a list of the debug statements that can be enabled. The format for each line of the file is: @@ -768,9 +769,9 @@ endmenu # "Memory Debugging" config ARCH_HAS_KCOV bool help - KCOV does not have any arch-specific code, but currently it is enabled - only for x86_64. KCOV requires testing on other archs, and most likely - disabling of instrumentation for some early boot code. + An architecture should select this when it can successfully + build and run with CONFIG_KCOV. This typically requires + disabling instrumentation for some early boot code. config CC_HAS_SANCOV_TRACE_PC def_bool $(cc-option,-fsanitize-coverage=trace-pc) @@ -2070,6 +2071,16 @@ config TEST_MEMINIT If unsure, say N. +config TEST_STACKINIT + tristate "Test level of stack variable initialization" + help + Test if the kernel is zero-initializing stack variables and + padding. Coverage is controlled by compiler flags, + CONFIG_GCC_PLUGIN_STRUCTLEAK, CONFIG_GCC_PLUGIN_STRUCTLEAK_BYREF, + or CONFIG_GCC_PLUGIN_STRUCTLEAK_BYREF_ALL. + + If unsure, say N. + endif # RUNTIME_TESTING_MENU config MEMTEST diff --git a/lib/Makefile b/lib/Makefile index 27a495ac42e08a293416376e72528b8f4458c375..2aeab86aa5a8b19257fc72f9459f927984444d41 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -83,6 +83,10 @@ obj-$(CONFIG_TEST_UUID) += test_uuid.o obj-$(CONFIG_TEST_PARMAN) += test_parman.o obj-$(CONFIG_TEST_KMOD) += test_kmod.o obj-$(CONFIG_TEST_DEBUG_VIRTUAL) += test_debug_virtual.o +obj-$(CONFIG_TEST_MEMCAT_P) += test_memcat_p.o +obj-$(CONFIG_TEST_OBJAGG) += test_objagg.o +CFLAGS_test_stackinit.o += $(call cc-disable-warning, switch-unreachable) +obj-$(CONFIG_TEST_STACKINIT) += test_stackinit.o obj-$(CONFIG_TEST_MEMINIT) += test_meminit.o ifeq ($(CONFIG_DEBUG_KOBJECT),y) diff --git a/lib/devres.c b/lib/devres.c index faccf1a037d05cdd7cc179de960457016d6504cc..aa0f5308ac6be8f9bcc3826132c856cd8fa8db06 100644 --- a/lib/devres.c +++ b/lib/devres.c @@ -131,7 +131,8 @@ EXPORT_SYMBOL(devm_iounmap); * if (IS_ERR(base)) * return PTR_ERR(base); */ -void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res) +void __iomem *devm_ioremap_resource(struct device *dev, + const struct resource *res) { resource_size_t size; const char *name; diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index dbf2b457e47e6a688f15d01d16d956fce7520b5b..d27e1d51112c262926d8f63dbc1bbd50cfcf7cad 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -954,22 +954,26 @@ static void ddebug_remove_all_tables(void) static __initdata int ddebug_init_success; -static int __init dynamic_debug_init_debugfs(void) +static int __init dynamic_debug_init_control(void) { - struct dentry *dir, *file; + struct proc_dir_entry *procfs_dir; + struct dentry *debugfs_dir; if (!ddebug_init_success) return -ENODEV; - dir = debugfs_create_dir("dynamic_debug", NULL); - if (!dir) - return -ENOMEM; - file = debugfs_create_file("control", 0644, dir, NULL, - &ddebug_proc_fops); - if (!file) { - debugfs_remove(dir); - return -ENOMEM; + /* Create the control file in debugfs if it is enabled */ + if (debugfs_initialized()) { + debugfs_dir = debugfs_create_dir("dynamic_debug", NULL); + debugfs_create_file("control", 0644, debugfs_dir, NULL, + &ddebug_proc_fops); } + + /* Also create the control file in procfs */ + procfs_dir = proc_mkdir("dynamic_debug", NULL); + if (procfs_dir) + proc_create("control", 0644, procfs_dir, &ddebug_proc_fops); + return 0; } @@ -1046,4 +1050,4 @@ static int __init dynamic_debug_init(void) early_initcall(dynamic_debug_init); /* Debugfs setup must be done later */ -fs_initcall(dynamic_debug_init_debugfs); +fs_initcall(dynamic_debug_init_control); diff --git a/lib/kfifo.c b/lib/kfifo.c index 015656aa8182d081522615c3eb71d406cc7f809f..6320ab91e3431bf53f3dad0ca82b72560c9144cb 100644 --- a/lib/kfifo.c +++ b/lib/kfifo.c @@ -82,7 +82,8 @@ int __kfifo_init(struct __kfifo *fifo, void *buffer, { size /= esize; - size = roundup_pow_of_two(size); + if (!is_power_of_2(size)) + size = rounddown_pow_of_two(size); fifo->in = 0; fifo->out = 0; diff --git a/lib/plist.c b/lib/plist.c index 199408f91057d527dcaafc2f054be51892819da5..30162b4be1c10f67a2271849001e6110944afa6b 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -103,6 +103,7 @@ void plist_add(struct plist_node *node, struct plist_head *head) plist_check_head(head); } +EXPORT_SYMBOL_GPL(plist_add); /** * plist_del - Remove a @node from plist. diff --git a/lib/scatterlist.c b/lib/scatterlist.c index 8c3036c37ba0eacdc5989cc49e55bac4ef921f9b..60e7eca2f4bed4c7e6cff00fbc4076d7fd403c4d 100644 --- a/lib/scatterlist.c +++ b/lib/scatterlist.c @@ -305,7 +305,7 @@ int __sg_alloc_table(struct sg_table *table, unsigned int nents, if (prv) table->nents = ++table->orig_nents; - return -ENOMEM; + return -ENOMEM; } sg_init_table(sg, alloc_size); diff --git a/lib/stackdepot.c b/lib/stackdepot.c index 241f9630617c8da681bab83423a44b49691a6057..c3ca0c8a693a8fbec35995b9af0cfd1d18d11fa1 100644 --- a/lib/stackdepot.c +++ b/lib/stackdepot.c @@ -92,15 +92,19 @@ static bool init_stack_slab(void **prealloc) return true; if (stack_slabs[depot_index] == NULL) { stack_slabs[depot_index] = *prealloc; + *prealloc = NULL; } else { - stack_slabs[depot_index + 1] = *prealloc; + /* If this is the last depot slab, do not touch the next one. */ + if (depot_index + 1 < STACK_ALLOC_MAX_SLABS) { + stack_slabs[depot_index + 1] = *prealloc; + *prealloc = NULL; + } /* * This smp_store_release pairs with smp_load_acquire() from * |next_slab_inited| above and in depot_save_stack(). */ smp_store_release(&next_slab_inited, 1); } - *prealloc = NULL; return true; } diff --git a/lib/test_kasan.c b/lib/test_kasan.c index 7de2702621dc10ce0fe77617e5e4cd4573c7814f..d86d3b152da6f6f29152ac09ae202224fe6df64e 100644 --- a/lib/test_kasan.c +++ b/lib/test_kasan.c @@ -157,6 +157,7 @@ static noinline void __init kmalloc_oob_krealloc_more(void) if (!ptr1 || !ptr2) { pr_err("Allocation failed\n"); kfree(ptr1); + kfree(ptr2); return; } diff --git a/lib/test_meminit.c b/lib/test_meminit.c index 9729f271d15041ac1930eb85922b81ba094a574f..e4f706a404b3a1f45b3443b4f8de0fbdd15107f8 100644 --- a/lib/test_meminit.c +++ b/lib/test_meminit.c @@ -183,6 +183,9 @@ static bool __init check_buf(void *buf, int size, bool want_ctor, return fail; } +#define BULK_SIZE 100 +static void *bulk_array[BULK_SIZE]; + /* * Test kmem_cache with given parameters: * want_ctor - use a constructor; @@ -203,9 +206,24 @@ static int __init do_kmem_cache_size(size_t size, bool want_ctor, want_rcu ? SLAB_TYPESAFE_BY_RCU : 0, want_ctor ? test_ctor : NULL); for (iter = 0; iter < 10; iter++) { + /* Do a test of bulk allocations */ + if (!want_rcu && !want_ctor) { + int ret; + + ret = kmem_cache_alloc_bulk(c, alloc_mask, BULK_SIZE, bulk_array); + if (!ret) { + fail = true; + } else { + int i; + for (i = 0; i < ret; i++) + fail |= check_buf(bulk_array[i], size, want_ctor, want_rcu, want_zero); + kmem_cache_free_bulk(c, ret, bulk_array); + } + } + buf = kmem_cache_alloc(c, alloc_mask); /* Check that buf is zeroed, if it must be. */ - fail = check_buf(buf, size, want_ctor, want_rcu, want_zero); + fail |= check_buf(buf, size, want_ctor, want_rcu, want_zero); fill_with_garbage_skip(buf, size, want_ctor ? CTOR_BYTES : 0); if (!want_rcu) { @@ -297,6 +315,32 @@ static int __init do_kmem_cache_rcu_persistent(int size, int *total_failures) return 1; } +static int __init do_kmem_cache_size_bulk(int size, int *total_failures) +{ + struct kmem_cache *c; + int i, iter, maxiter = 1024; + int num, bytes; + bool fail = false; + void *objects[10]; + + c = kmem_cache_create("test_cache", size, size, 0, NULL); + for (iter = 0; (iter < maxiter) && !fail; iter++) { + num = kmem_cache_alloc_bulk(c, GFP_KERNEL, ARRAY_SIZE(objects), + objects); + for (i = 0; i < num; i++) { + bytes = count_nonzero_bytes(objects[i], size); + if (bytes) + fail = true; + fill_with_garbage(objects[i], size); + } + + if (num) + kmem_cache_free_bulk(c, num, objects); + } + *total_failures += fail; + return 1; +} + /* * Test kmem_cache allocation by creating caches of different sizes, with and * without constructors, with and without SLAB_TYPESAFE_BY_RCU. @@ -318,6 +362,7 @@ static int __init test_kmemcache(int *total_failures) num_tests += do_kmem_cache_size(size, ctor, rcu, zero, &failures); } + num_tests += do_kmem_cache_size_bulk(size, &failures); } REPORT_FAILURES_IN_FN(); *total_failures += failures; diff --git a/lib/test_stackinit.c b/lib/test_stackinit.c new file mode 100644 index 0000000000000000000000000000000000000000..c355575f8d61e7e5904f8c6eb4c928355e9721d5 --- /dev/null +++ b/lib/test_stackinit.c @@ -0,0 +1,395 @@ +// SPDX-Licenses: GPLv2 +/* + * Test cases for compiler-based stack variable zeroing via future + * compiler flags or CONFIG_GCC_PLUGIN_STRUCTLEAK*. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include + +/* Exfiltration buffer. */ +#define MAX_VAR_SIZE 128 +static u8 check_buf[MAX_VAR_SIZE]; + +/* Character array to trigger stack protector in all functions. */ +#define VAR_BUFFER 32 + +/* Volatile mask to convince compiler to copy memory with 0xff. */ +static volatile u8 forced_mask = 0xff; + +/* Location and size tracking to validate fill and test are colocated. */ +static void *fill_start, *target_start; +static size_t fill_size, target_size; + +static bool range_contains(char *haystack_start, size_t haystack_size, + char *needle_start, size_t needle_size) +{ + if (needle_start >= haystack_start && + needle_start + needle_size <= haystack_start + haystack_size) + return true; + return false; +} + +#define DO_NOTHING_TYPE_SCALAR(var_type) var_type +#define DO_NOTHING_TYPE_STRING(var_type) void +#define DO_NOTHING_TYPE_STRUCT(var_type) void + +#define DO_NOTHING_RETURN_SCALAR(ptr) *(ptr) +#define DO_NOTHING_RETURN_STRING(ptr) /**/ +#define DO_NOTHING_RETURN_STRUCT(ptr) /**/ + +#define DO_NOTHING_CALL_SCALAR(var, name) \ + (var) = do_nothing_ ## name(&(var)) +#define DO_NOTHING_CALL_STRING(var, name) \ + do_nothing_ ## name(var) +#define DO_NOTHING_CALL_STRUCT(var, name) \ + do_nothing_ ## name(&(var)) + +#define FETCH_ARG_SCALAR(var) &var +#define FETCH_ARG_STRING(var) var +#define FETCH_ARG_STRUCT(var) &var + +#define FILL_SIZE_STRING 16 + +#define INIT_CLONE_SCALAR /**/ +#define INIT_CLONE_STRING [FILL_SIZE_STRING] +#define INIT_CLONE_STRUCT /**/ + +#define INIT_SCALAR_none /**/ +#define INIT_SCALAR_zero = 0 + +#define INIT_STRING_none [FILL_SIZE_STRING] /**/ +#define INIT_STRING_zero [FILL_SIZE_STRING] = { } + +#define INIT_STRUCT_none /**/ +#define INIT_STRUCT_zero = { } +#define INIT_STRUCT_static_partial = { .two = 0, } +#define INIT_STRUCT_static_all = { .one = arg->one, \ + .two = arg->two, \ + .three = arg->three, \ + .four = arg->four, \ + } +#define INIT_STRUCT_dynamic_partial = { .two = arg->two, } +#define INIT_STRUCT_dynamic_all = { .one = arg->one, \ + .two = arg->two, \ + .three = arg->three, \ + .four = arg->four, \ + } +#define INIT_STRUCT_runtime_partial ; \ + var.two = 0 +#define INIT_STRUCT_runtime_all ; \ + var.one = 0; \ + var.two = 0; \ + var.three = 0; \ + memset(&var.four, 0, \ + sizeof(var.four)) + +/* + * @name: unique string name for the test + * @var_type: type to be tested for zeroing initialization + * @which: is this a SCALAR, STRING, or STRUCT type? + * @init_level: what kind of initialization is performed + * @xfail: is this test expected to fail? + */ +#define DEFINE_TEST_DRIVER(name, var_type, which, xfail) \ +/* Returns 0 on success, 1 on failure. */ \ +static noinline __init int test_ ## name (void) \ +{ \ + var_type zero INIT_CLONE_ ## which; \ + int ignored; \ + u8 sum = 0, i; \ + \ + /* Notice when a new test is larger than expected. */ \ + BUILD_BUG_ON(sizeof(zero) > MAX_VAR_SIZE); \ + \ + /* Fill clone type with zero for per-field init. */ \ + memset(&zero, 0x00, sizeof(zero)); \ + /* Clear entire check buffer for 0xFF overlap test. */ \ + memset(check_buf, 0x00, sizeof(check_buf)); \ + /* Fill stack with 0xFF. */ \ + ignored = leaf_ ##name((unsigned long)&ignored, 1, \ + FETCH_ARG_ ## which(zero)); \ + /* Verify all bytes overwritten with 0xFF. */ \ + for (sum = 0, i = 0; i < target_size; i++) \ + sum += (check_buf[i] != 0xFF); \ + if (sum) { \ + pr_err(#name ": leaf fill was not 0xFF!?\n"); \ + return 1; \ + } \ + /* Clear entire check buffer for later bit tests. */ \ + memset(check_buf, 0x00, sizeof(check_buf)); \ + /* Extract stack-defined variable contents. */ \ + ignored = leaf_ ##name((unsigned long)&ignored, 0, \ + FETCH_ARG_ ## which(zero)); \ + \ + /* Validate that compiler lined up fill and target. */ \ + if (!range_contains(fill_start, fill_size, \ + target_start, target_size)) { \ + pr_err(#name ": stack fill missed target!?\n"); \ + pr_err(#name ": fill %zu wide\n", fill_size); \ + pr_err(#name ": target offset by %d\n", \ + (int)((ssize_t)(uintptr_t)fill_start - \ + (ssize_t)(uintptr_t)target_start)); \ + return 1; \ + } \ + \ + /* Look for any bytes still 0xFF in check region. */ \ + for (sum = 0, i = 0; i < target_size; i++) \ + sum += (check_buf[i] == 0xFF); \ + \ + if (sum == 0) { \ + pr_info(#name " ok\n"); \ + return 0; \ + } else { \ + pr_warn(#name " %sFAIL (uninit bytes: %d)\n", \ + (xfail) ? "X" : "", sum); \ + return (xfail) ? 0 : 1; \ + } \ +} +#define DEFINE_TEST(name, var_type, which, init_level) \ +/* no-op to force compiler into ignoring "uninitialized" vars */\ +static noinline __init DO_NOTHING_TYPE_ ## which(var_type) \ +do_nothing_ ## name(var_type *ptr) \ +{ \ + /* Will always be true, but compiler doesn't know. */ \ + if ((unsigned long)ptr > 0x2) \ + return DO_NOTHING_RETURN_ ## which(ptr); \ + else \ + return DO_NOTHING_RETURN_ ## which(ptr + 1); \ +} \ +static noinline __init int leaf_ ## name(unsigned long sp, \ + bool fill, \ + var_type *arg) \ +{ \ + char buf[VAR_BUFFER]; \ + var_type var INIT_ ## which ## _ ## init_level; \ + \ + target_start = &var; \ + target_size = sizeof(var); \ + /* \ + * Keep this buffer around to make sure we've got a \ + * stack frame of SOME kind... \ + */ \ + memset(buf, (char)(sp & 0xff), sizeof(buf)); \ + /* Fill variable with 0xFF. */ \ + if (fill) { \ + fill_start = &var; \ + fill_size = sizeof(var); \ + memset(fill_start, \ + (char)((sp & 0xff) | forced_mask), \ + fill_size); \ + } \ + \ + /* Silence "never initialized" warnings. */ \ + DO_NOTHING_CALL_ ## which(var, name); \ + \ + /* Exfiltrate "var". */ \ + memcpy(check_buf, target_start, target_size); \ + \ + return (int)buf[0] | (int)buf[sizeof(buf) - 1]; \ +} \ +DEFINE_TEST_DRIVER(name, var_type, which, 0) + +/* Structure with no padding. */ +struct test_packed { + unsigned long one; + unsigned long two; + unsigned long three; + unsigned long four; +}; + +/* Simple structure with padding likely to be covered by compiler. */ +struct test_small_hole { + size_t one; + char two; + /* 3 byte padding hole here. */ + int three; + unsigned long four; +}; + +/* Try to trigger unhandled padding in a structure. */ +struct test_aligned { + u32 internal1; + u64 internal2; +} __aligned(64); + +struct test_big_hole { + u8 one; + u8 two; + u8 three; + /* 61 byte padding hole here. */ + struct test_aligned four; +} __aligned(64); + +struct test_trailing_hole { + char *one; + char *two; + char *three; + char four; + /* "sizeof(unsigned long) - 1" byte padding hole here. */ +}; + +/* Test if STRUCTLEAK is clearing structs with __user fields. */ +struct test_user { + u8 one; + unsigned long two; + char __user *three; + unsigned long four; +}; + +#define DEFINE_SCALAR_TEST(name, init) \ + DEFINE_TEST(name ## _ ## init, name, SCALAR, init) + +#define DEFINE_SCALAR_TESTS(init) \ + DEFINE_SCALAR_TEST(u8, init); \ + DEFINE_SCALAR_TEST(u16, init); \ + DEFINE_SCALAR_TEST(u32, init); \ + DEFINE_SCALAR_TEST(u64, init); \ + DEFINE_TEST(char_array_ ## init, unsigned char, STRING, init) + +#define DEFINE_STRUCT_TEST(name, init) \ + DEFINE_TEST(name ## _ ## init, \ + struct test_ ## name, STRUCT, init) + +#define DEFINE_STRUCT_TESTS(init) \ + DEFINE_STRUCT_TEST(small_hole, init); \ + DEFINE_STRUCT_TEST(big_hole, init); \ + DEFINE_STRUCT_TEST(trailing_hole, init); \ + DEFINE_STRUCT_TEST(packed, init) + +/* These should be fully initialized all the time! */ +DEFINE_SCALAR_TESTS(zero); +DEFINE_STRUCT_TESTS(zero); +/* Static initialization: padding may be left uninitialized. */ +DEFINE_STRUCT_TESTS(static_partial); +DEFINE_STRUCT_TESTS(static_all); +/* Dynamic initialization: padding may be left uninitialized. */ +DEFINE_STRUCT_TESTS(dynamic_partial); +DEFINE_STRUCT_TESTS(dynamic_all); +/* Runtime initialization: padding may be left uninitialized. */ +DEFINE_STRUCT_TESTS(runtime_partial); +DEFINE_STRUCT_TESTS(runtime_all); +/* No initialization without compiler instrumentation. */ +DEFINE_SCALAR_TESTS(none); +DEFINE_STRUCT_TESTS(none); +DEFINE_TEST(user, struct test_user, STRUCT, none); + +/* + * Check two uses through a variable declaration outside either path, + * which was noticed as a special case in porting earlier stack init + * compiler logic. + */ +static int noinline __leaf_switch_none(int path, bool fill) +{ + switch (path) { + uint64_t var; + + case 1: + target_start = &var; + target_size = sizeof(var); + if (fill) { + fill_start = &var; + fill_size = sizeof(var); + + memset(fill_start, forced_mask | 0x55, fill_size); + } + memcpy(check_buf, target_start, target_size); + break; + case 2: + target_start = &var; + target_size = sizeof(var); + if (fill) { + fill_start = &var; + fill_size = sizeof(var); + + memset(fill_start, forced_mask | 0xaa, fill_size); + } + memcpy(check_buf, target_start, target_size); + break; + default: + var = 5; + return var & forced_mask; + } + return 0; +} + +static noinline __init int leaf_switch_1_none(unsigned long sp, bool fill, + uint64_t *arg) +{ + return __leaf_switch_none(1, fill); +} + +static noinline __init int leaf_switch_2_none(unsigned long sp, bool fill, + uint64_t *arg) +{ + return __leaf_switch_none(2, fill); +} + +/* + * These are expected to fail for most configurations because neither + * GCC nor Clang have a way to perform initialization of variables in + * non-code areas (i.e. in a switch statement before the first "case"). + * https://bugs.llvm.org/show_bug.cgi?id=44916 + */ +DEFINE_TEST_DRIVER(switch_1_none, uint64_t, SCALAR, 1); +DEFINE_TEST_DRIVER(switch_2_none, uint64_t, SCALAR, 1); + +static int __init test_stackinit_init(void) +{ + unsigned int failures = 0; + +#define test_scalars(init) do { \ + failures += test_u8_ ## init (); \ + failures += test_u16_ ## init (); \ + failures += test_u32_ ## init (); \ + failures += test_u64_ ## init (); \ + failures += test_char_array_ ## init (); \ + } while (0) + +#define test_structs(init) do { \ + failures += test_small_hole_ ## init (); \ + failures += test_big_hole_ ## init (); \ + failures += test_trailing_hole_ ## init (); \ + failures += test_packed_ ## init (); \ + } while (0) + + /* These are explicitly initialized and should always pass. */ + test_scalars(zero); + test_structs(zero); + /* Padding here appears to be accidentally always initialized? */ + test_structs(dynamic_partial); + /* Padding initialization depends on compiler behaviors. */ + test_structs(static_partial); + test_structs(static_all); + test_structs(dynamic_all); + test_structs(runtime_partial); + test_structs(runtime_all); + + /* STRUCTLEAK_BYREF_ALL should cover everything from here down. */ + test_scalars(none); + failures += test_switch_1_none(); + failures += test_switch_2_none(); + + /* STRUCTLEAK_BYREF should cover from here down. */ + test_structs(none); + + /* STRUCTLEAK will only cover this. */ + failures += test_user(); + + if (failures == 0) + pr_info("all tests passed!\n"); + else + pr_err("failures: %u\n", failures); + + return failures ? -EINVAL : 0; +} +module_init(test_stackinit_init); + +static void __exit test_stackinit_exit(void) +{ } +module_exit(test_stackinit_exit); + +MODULE_LICENSE("GPL"); diff --git a/mm/cma.c b/mm/cma.c index 7af197cdabc0bfecf31395f365167d322f688e22..09e8f78736a3d5624c39c0fb490e8cb2120df066 100644 --- a/mm/cma.c +++ b/mm/cma.c @@ -55,6 +55,7 @@ const char *cma_get_name(const struct cma *cma) { return cma->name ? cma->name : "(undefined)"; } +EXPORT_SYMBOL_GPL(cma_get_name); static unsigned long cma_bitmap_aligned_mask(const struct cma *cma, unsigned int align_order) @@ -565,6 +566,7 @@ struct page *cma_alloc(struct cma *cma, size_t count, unsigned int align, pr_debug("%s(): returned %p\n", __func__, page); return page; } +EXPORT_SYMBOL_GPL(cma_alloc); /** * cma_release() - release allocated pages @@ -598,6 +600,7 @@ bool cma_release(struct cma *cma, const struct page *pages, unsigned int count) return true; } +EXPORT_SYMBOL_GPL(cma_release); int cma_for_each_area(int (*it)(struct cma *cma, void *data), void *data) { @@ -612,3 +615,4 @@ int cma_for_each_area(int (*it)(struct cma *cma, void *data), void *data) return 0; } +EXPORT_SYMBOL_GPL(cma_for_each_area); diff --git a/mm/hmm.c b/mm/hmm.c index 57f0d2a4ff34c6c5200d104855042b10c8309d91..c482c07bbab7aef29126807355163d6e27082d64 100644 --- a/mm/hmm.c +++ b/mm/hmm.c @@ -997,21 +997,21 @@ static void hmm_devmem_release(void *data) struct hmm_devmem *devmem = data; struct resource *resource = devmem->resource; unsigned long start_pfn, npages; - struct zone *zone; struct page *page; + int nid; /* pages are dead and unused, undo the arch mapping */ start_pfn = (resource->start & ~(PA_SECTION_SIZE - 1)) >> PAGE_SHIFT; npages = ALIGN(resource_size(resource), PA_SECTION_SIZE) >> PAGE_SHIFT; page = pfn_to_page(start_pfn); - zone = page_zone(page); + nid = page_to_nid(page); mem_hotplug_begin(); if (resource->desc == IORES_DESC_DEVICE_PRIVATE_MEMORY) - __remove_pages(zone, start_pfn, npages, NULL); + __remove_pages(start_pfn, npages, NULL); else - arch_remove_memory(start_pfn << PAGE_SHIFT, + arch_remove_memory(nid, start_pfn << PAGE_SHIFT, npages << PAGE_SHIFT, NULL); mem_hotplug_done(); diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 479a07062d6e20aa9bed9a482ff3a48679e3b02d..58a28dc29200f7d8730758e675e7eeb3a9b8b522 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -173,16 +173,13 @@ static ssize_t enabled_store(struct kobject *kobj, { ssize_t ret = count; - if (!memcmp("always", buf, - min(sizeof("always")-1, count))) { + if (sysfs_streq(buf, "always")) { clear_bit(TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG, &transparent_hugepage_flags); set_bit(TRANSPARENT_HUGEPAGE_FLAG, &transparent_hugepage_flags); - } else if (!memcmp("madvise", buf, - min(sizeof("madvise")-1, count))) { + } else if (sysfs_streq(buf, "madvise")) { clear_bit(TRANSPARENT_HUGEPAGE_FLAG, &transparent_hugepage_flags); set_bit(TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG, &transparent_hugepage_flags); - } else if (!memcmp("never", buf, - min(sizeof("never")-1, count))) { + } else if (sysfs_streq(buf, "never")) { clear_bit(TRANSPARENT_HUGEPAGE_FLAG, &transparent_hugepage_flags); clear_bit(TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG, &transparent_hugepage_flags); } else @@ -246,32 +243,27 @@ static ssize_t defrag_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { - if (!memcmp("always", buf, - min(sizeof("always")-1, count))) { + if (sysfs_streq(buf, "always")) { clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_FLAG, &transparent_hugepage_flags); clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_OR_MADV_FLAG, &transparent_hugepage_flags); clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG, &transparent_hugepage_flags); set_bit(TRANSPARENT_HUGEPAGE_DEFRAG_DIRECT_FLAG, &transparent_hugepage_flags); - } else if (!memcmp("defer+madvise", buf, - min(sizeof("defer+madvise")-1, count))) { + } else if (sysfs_streq(buf, "defer+madvise")) { clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_DIRECT_FLAG, &transparent_hugepage_flags); clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_FLAG, &transparent_hugepage_flags); clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG, &transparent_hugepage_flags); set_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_OR_MADV_FLAG, &transparent_hugepage_flags); - } else if (!memcmp("defer", buf, - min(sizeof("defer")-1, count))) { + } else if (sysfs_streq(buf, "defer")) { clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_DIRECT_FLAG, &transparent_hugepage_flags); clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_OR_MADV_FLAG, &transparent_hugepage_flags); clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG, &transparent_hugepage_flags); set_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_FLAG, &transparent_hugepage_flags); - } else if (!memcmp("madvise", buf, - min(sizeof("madvise")-1, count))) { + } else if (sysfs_streq(buf, "madvise")) { clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_DIRECT_FLAG, &transparent_hugepage_flags); clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_FLAG, &transparent_hugepage_flags); clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_OR_MADV_FLAG, &transparent_hugepage_flags); set_bit(TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG, &transparent_hugepage_flags); - } else if (!memcmp("never", buf, - min(sizeof("never")-1, count))) { + } else if (sysfs_streq(buf, "never")) { clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_DIRECT_FLAG, &transparent_hugepage_flags); clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_FLAG, &transparent_hugepage_flags); clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_OR_MADV_FLAG, &transparent_hugepage_flags); @@ -509,13 +501,13 @@ void prep_transhuge_page(struct page *page) set_compound_page_dtor(page, TRANSHUGE_PAGE_DTOR); } -unsigned long __thp_get_unmapped_area(struct file *filp, unsigned long len, +static unsigned long __thp_get_unmapped_area(struct file *filp, + unsigned long addr, unsigned long len, loff_t off, unsigned long flags, unsigned long size) { - unsigned long addr; loff_t off_end = off + len; loff_t off_align = round_up(off, size); - unsigned long len_pad; + unsigned long len_pad, ret; if (off_end <= off_align || (off_end - off_align) < size) return 0; @@ -524,30 +516,40 @@ unsigned long __thp_get_unmapped_area(struct file *filp, unsigned long len, if (len_pad < len || (off + len_pad) < off) return 0; - addr = current->mm->get_unmapped_area(filp, 0, len_pad, + ret = current->mm->get_unmapped_area(filp, addr, len_pad, off >> PAGE_SHIFT, flags); - if (IS_ERR_VALUE(addr)) + + /* + * The failure might be due to length padding. The caller will retry + * without the padding. + */ + if (IS_ERR_VALUE(ret)) return 0; - addr += (off - addr) & (size - 1); - return addr; + /* + * Do not try to align to THP boundary if allocation at the address + * hint succeeds. + */ + if (ret == addr) + return addr; + + ret += (off - ret) & (size - 1); + return ret; } unsigned long thp_get_unmapped_area(struct file *filp, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags) { + unsigned long ret; loff_t off = (loff_t)pgoff << PAGE_SHIFT; - if (addr) - goto out; if (!IS_DAX(filp->f_mapping->host) || !IS_ENABLED(CONFIG_FS_DAX_PMD)) goto out; - addr = __thp_get_unmapped_area(filp, len, off, flags, PMD_SIZE); - if (addr) - return addr; - - out: + ret = __thp_get_unmapped_area(filp, addr, len, off, flags, PMD_SIZE); + if (ret) + return ret; +out: return current->mm->get_unmapped_area(filp, addr, len, pgoff, flags); } EXPORT_SYMBOL_GPL(thp_get_unmapped_area); @@ -2650,7 +2652,7 @@ int split_huge_page_to_list(struct page *page, struct list_head *list) unsigned long flags; pgoff_t end; - VM_BUG_ON_PAGE(is_huge_zero_page(page), page); + VM_BUG_ON_PAGE(is_huge_zero_page(head), head); VM_BUG_ON_PAGE(!PageLocked(page), page); VM_BUG_ON_PAGE(!PageCompound(page), page); @@ -2946,8 +2948,7 @@ void set_pmd_migration_entry(struct page_vma_mapped_walk *pvmw, return; flush_cache_range(vma, address, address + HPAGE_PMD_SIZE); - pmdval = *pvmw->pmd; - pmdp_invalidate(vma, address, pvmw->pmd); + pmdval = pmdp_invalidate(vma, address, pvmw->pmd); if (pmd_dirty(pmdval)) set_page_dirty(page); entry = make_migration_entry(page, pmd_write(pmdval)); diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 3a3d109dce215a0fa42f22d55c7802cd762d8789..0f7ff204083ebfdbe1dc5ba05490f9c1a87b118c 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -419,8 +419,10 @@ int memcg_expand_shrinker_maps(int new_id) if (mem_cgroup_is_root(memcg)) continue; ret = memcg_expand_one_shrinker_map(memcg, size, old_size); - if (ret) + if (ret) { + mem_cgroup_iter_break(NULL, memcg); goto unlock; + } } unlock: if (!ret) diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index eb04479a5334335ac419f683bad8f57f3402a6a8..11b18bd308586c942eed91f2dbebd77f0ff3cc1a 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -260,14 +260,8 @@ static int __meminit __add_section(int nid, unsigned long phys_start_pfn, if (pfn_valid(phys_start_pfn)) return -EEXIST; - ret = sparse_add_one_section(NODE_DATA(nid), phys_start_pfn, altmap); - if (ret < 0) - return ret; - - if (!want_memblock) - return 0; - - return hotplug_memory_register(nid, __pfn_to_section(phys_start_pfn)); + ret = sparse_add_one_section(nid, phys_start_pfn, altmap); + return ret < 0 ? ret : 0; } /* @@ -320,7 +314,6 @@ int __ref __add_pages(int nid, unsigned long phys_start_pfn, return err; } -#ifdef CONFIG_MEMORY_HOTREMOVE /* find the smallest valid pfn in the range [start_pfn, end_pfn) */ static unsigned long find_smallest_section_pfn(int nid, struct zone *zone, unsigned long start_pfn, @@ -461,10 +454,11 @@ static void update_pgdat_span(struct pglist_data *pgdat) pgdat->node_spanned_pages = node_end_pfn - node_start_pfn; } -static void __remove_zone(struct zone *zone, unsigned long start_pfn) +void __ref remove_pfn_range_from_zone(struct zone *zone, + unsigned long start_pfn, + unsigned long nr_pages) { struct pglist_data *pgdat = zone->zone_pgdat; - int nr_pages = PAGES_PER_SECTION; unsigned long flags; #ifdef CONFIG_ZONE_DEVICE @@ -477,37 +471,33 @@ static void __remove_zone(struct zone *zone, unsigned long start_pfn) return; #endif + clear_zone_contiguous(zone); + pgdat_resize_lock(zone->zone_pgdat, &flags); shrink_zone_span(zone, start_pfn, start_pfn + nr_pages); update_pgdat_span(pgdat); pgdat_resize_unlock(zone->zone_pgdat, &flags); + + set_zone_contiguous(zone); } -static int __remove_section(struct zone *zone, struct mem_section *ms, - unsigned long map_offset, struct vmem_altmap *altmap) +static void __remove_section(struct mem_section *ms, unsigned long map_offset, + struct vmem_altmap *altmap) { unsigned long start_pfn; int scn_nr; - int ret = -EINVAL; - if (!valid_section(ms)) - return ret; - - ret = unregister_memory_section(ms); - if (ret) - return ret; + if (WARN_ON_ONCE(!valid_section(ms))) + return; scn_nr = __section_nr(ms); start_pfn = section_nr_to_pfn((unsigned long)scn_nr); - __remove_zone(zone, start_pfn); - sparse_remove_one_section(zone, ms, map_offset, altmap); - return 0; + sparse_remove_one_section(ms, map_offset, altmap); } /** - * __remove_pages() - remove sections of pages from a zone - * @zone: zone from which pages need to be removed + * __remove_pages() - remove sections of pages * @phys_start_pfn: starting pageframe (must be aligned to start of a section) * @nr_pages: number of pages to remove (must be multiple of section size) * @altmap: alternative device page map or %NULL if default memmap is used @@ -517,34 +507,15 @@ static int __remove_section(struct zone *zone, struct mem_section *ms, * sure that pages are marked reserved and zones are adjust properly by * calling offline_pages(). */ -int __remove_pages(struct zone *zone, unsigned long phys_start_pfn, - unsigned long nr_pages, struct vmem_altmap *altmap) +void __remove_pages(unsigned long phys_start_pfn, unsigned long nr_pages, + struct vmem_altmap *altmap) { unsigned long i; unsigned long map_offset = 0; - int sections_to_remove, ret = 0; - - /* In the ZONE_DEVICE case device driver owns the memory region */ - if (is_dev_zone(zone)) { - if (altmap) - map_offset = vmem_altmap_offset(altmap); - } else { - resource_size_t start, size; - - start = phys_start_pfn << PAGE_SHIFT; - size = nr_pages * PAGE_SIZE; - - ret = release_mem_region_adjustable(&iomem_resource, start, - size); - if (ret) { - resource_size_t endres = start + size - 1; + int sections_to_remove; - pr_warn("Unable to release resource <%pa-%pa> (%d)\n", - &start, &endres, ret); - } - } - - clear_zone_contiguous(zone); + if (altmap) + map_offset = vmem_altmap_offset(altmap); /* * We can only remove entire sections @@ -557,18 +528,10 @@ int __remove_pages(struct zone *zone, unsigned long phys_start_pfn, unsigned long pfn = phys_start_pfn + i*PAGES_PER_SECTION; cond_resched(); - ret = __remove_section(zone, __pfn_to_section(pfn), map_offset, - altmap); + __remove_section(__pfn_to_section(pfn), map_offset, altmap); map_offset = 0; - if (ret) - break; } - - set_zone_contiguous(zone); - - return ret; } -#endif /* CONFIG_MEMORY_HOTREMOVE */ int set_online_page_callback(online_page_callback_t callback) { @@ -976,6 +939,7 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages, int online_typ (unsigned long long) pfn << PAGE_SHIFT, (((unsigned long long) pfn + nr_pages) << PAGE_SHIFT) - 1); memory_notify(MEM_CANCEL_ONLINE, &arg); + remove_pfn_range_from_zone(zone, pfn, nr_pages); mem_hotplug_done(); return ret; } @@ -1192,6 +1156,13 @@ int __ref add_memory_resource(int nid, struct resource *res, bool online) if (ret < 0) goto error; + /* create memory block devices after memory was added */ + ret = create_memory_block_devices(start, size); + if (ret) { + arch_remove_memory(nid, start, size, NULL); + goto error; + } + if (new_node) { /* If sysfs file of new node can't be created, cpu on the node * can't be hot-added. There is no rollback way now. @@ -1784,6 +1755,7 @@ static int __ref __offline_pages(unsigned long start_pfn, writeback_set_ratelimit(); memory_notify(MEM_OFFLINE, &arg); + remove_pfn_range_from_zone(zone, start_pfn, nr_pages); mem_hotplug_done(); return 0; @@ -1914,6 +1886,18 @@ static int check_and_unmap_cpu_on_node(pg_data_t *pgdat) return 0; } +static int check_no_memblock_for_node_cb(struct memory_block *mem, void *arg) +{ + int nid = *(int *)arg; + + /* + * If a memory block belongs to multiple nodes, the stored nid is not + * reliable. However, such blocks are always online (e.g., cannot get + * offlined) and, therefore, are still spanned by the node. + */ + return mem->nid == nid ? -EEXIST : 0; +} + /** * try_offline_node * @nid: the node ID @@ -1926,25 +1910,24 @@ static int check_and_unmap_cpu_on_node(pg_data_t *pgdat) void try_offline_node(int nid) { pg_data_t *pgdat = NODE_DATA(nid); - unsigned long start_pfn = pgdat->node_start_pfn; - unsigned long end_pfn = start_pfn + pgdat->node_spanned_pages; - unsigned long pfn; - - for (pfn = start_pfn; pfn < end_pfn; pfn += PAGES_PER_SECTION) { - unsigned long section_nr = pfn_to_section_nr(pfn); - - if (!present_section_nr(section_nr)) - continue; + int rc; - if (pfn_to_nid(pfn) != nid) - continue; + /* + * If the node still spans pages (especially ZONE_DEVICE), don't + * offline it. A node spans memory after move_pfn_range_to_zone(), + * e.g., after the memory block was onlined. + */ + if (pgdat->node_spanned_pages) + return; - /* - * some memory sections of this node are not removed, and we - * can't offline node now. - */ + /* + * Especially offline memory blocks might not be spanned by the + * node. They will get spanned by the node once they get onlined. + * However, they link to the node in sysfs and can get onlined later. + */ + rc = for_each_memory_block(&nid, check_no_memblock_for_node_cb); + if (rc) return; - } if (check_and_unmap_cpu_on_node(pgdat)) return; @@ -1958,6 +1941,26 @@ void try_offline_node(int nid) } EXPORT_SYMBOL(try_offline_node); +static void __release_memory_resource(resource_size_t start, + resource_size_t size) +{ + int ret; + + /* + * When removing memory in the same granularity as it was added, + * this function never fails. It might only fail if resources + * have to be adjusted or split. We'll ignore the error, as + * removing of memory cannot fail. + */ + ret = release_mem_region_adjustable(&iomem_resource, start, size); + if (ret) { + resource_size_t endres = start + size - 1; + + pr_warn("Unable to release resource <%pa-%pa> (%d)\n", + &start, &endres, ret); + } +} + /** * remove_memory * @nid: the node ID @@ -1968,14 +1971,12 @@ EXPORT_SYMBOL(try_offline_node); * and online/offline operations before this call, as required by * try_offline_node(). */ -void __ref remove_memory(int nid, u64 start, u64 size) +void __ref __remove_memory(int nid, u64 start, u64 size) { int ret; BUG_ON(check_hotplug_memory_range(start, size)); - mem_hotplug_begin(); - /* * All memory blocks must be offlined before removing memory. Check * whether all memory blocks in question are offline and trigger a BUG() @@ -1991,11 +1992,27 @@ void __ref remove_memory(int nid, u64 start, u64 size) memblock_free(start, size); memblock_remove(start, size); - arch_remove_memory(start, size, NULL); + /* + * Memory block device removal under the device_hotplug_lock is + * a barrier against racing online attempts. + */ + remove_memory_block_devices(start, size); + + mem_hotplug_begin(); + + arch_remove_memory(nid, start, size, NULL); + __release_memory_resource(start, size); try_offline_node(nid); mem_hotplug_done(); } + +void remove_memory(int nid, u64 start, u64 size) +{ + lock_device_hotplug(); + __remove_memory(nid, start, size); + unlock_device_hotplug(); +} EXPORT_SYMBOL_GPL(remove_memory); #endif /* CONFIG_MEMORY_HOTREMOVE */ diff --git a/mm/mempolicy.c b/mm/mempolicy.c index 4fe31d898460c01ecd16c933b239567268699627..6af761550e678aa00d14c3b841daa49451e0e958 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -2829,6 +2829,9 @@ int mpol_parse_str(char *str, struct mempolicy **mpol) char *flags = strchr(str, '='); int err = 1; + if (flags) + *flags++ = '\0'; /* terminate mode string */ + if (nodelist) { /* NUL-terminate mode or flags string */ *nodelist++ = '\0'; @@ -2839,9 +2842,6 @@ int mpol_parse_str(char *str, struct mempolicy **mpol) } else nodes_clear(nodes); - if (flags) - *flags++ = '\0'; /* terminate mode string */ - for (mode = 0; mode < MPOL_MAX; mode++) { if (!strcmp(str, policy_modes[mode])) { break; diff --git a/mm/migrate.c b/mm/migrate.c index 7c148b722a0928d881af1378def633af6e15524b..6f9e92db99e6b1bebdf3b8d6383503a4d6c8d4a2 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -1623,8 +1623,19 @@ static int do_pages_move(struct mm_struct *mm, nodemask_t task_nodes, start = i; } else if (node != current_node) { err = do_move_pages_to_node(mm, &pagelist, current_node); - if (err) + if (err) { + /* + * Positive err means the number of failed + * pages to migrate. Since we are going to + * abort and return the number of non-migrated + * pages, so need to incude the rest of the + * nr_pages that have not been attempted as + * well. + */ + if (err > 0) + err += nr_pages - i - 1; goto out; + } err = store_status(status, start, current_node, i - start); if (err) goto out; @@ -1655,8 +1666,11 @@ static int do_pages_move(struct mm_struct *mm, nodemask_t task_nodes, goto out_flush; err = do_move_pages_to_node(mm, &pagelist, current_node); - if (err) + if (err) { + if (err > 0) + err += nr_pages - i - 1; goto out; + } if (i > start) { err = store_status(status, start, current_node, i - start); if (err) @@ -1670,9 +1684,16 @@ static int do_pages_move(struct mm_struct *mm, nodemask_t task_nodes, /* Make sure we do not overwrite the existing error */ err1 = do_move_pages_to_node(mm, &pagelist, current_node); + /* + * Don't have to report non-attempted pages here since: + * - If the above loop is done gracefully all pages have been + * attempted. + * - If the above loop is aborted it means a fatal error + * happened, should return ret. + */ if (!err1) err1 = store_status(status, start, current_node, i - start); - if (!err) + if (err >= 0) err = err1; out: return err; diff --git a/mm/mmap.c b/mm/mmap.c index c111771774b5fffd80690ce35c267b4ac7c3661f..85f87e882d4c273f6820194da4d40c58450e78ca 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -211,8 +211,6 @@ SYSCALL_DEFINE1(brk, unsigned long, brk) bool populate; LIST_HEAD(uf); - brk = untagged_addr(brk); - if (down_write_killable(&mm->mmap_sem)) return -EINTR; @@ -1633,8 +1631,6 @@ unsigned long ksys_mmap_pgoff(unsigned long addr, unsigned long len, struct file *file = NULL; unsigned long retval; - addr = untagged_addr(addr); - if (!(flags & MAP_ANONYMOUS)) { audit_mmap_fd(fd, flags); file = fget(fd); diff --git a/mm/mprotect.c b/mm/mprotect.c index d65edeb005ebb3735b8925e14e0c8263d2215a2b..836a7586d4bf1f23d38813e5a1142298b17e39d5 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -162,6 +162,31 @@ static unsigned long change_pte_range(struct vm_area_struct *vma, pmd_t *pmd, return pages; } +/* + * Used when setting automatic NUMA hinting protection where it is + * critical that a numa hinting PMD is not confused with a bad PMD. + */ +static inline int pmd_none_or_clear_bad_unless_trans_huge(pmd_t *pmd) +{ + pmd_t pmdval = pmd_read_atomic(pmd); + + /* See pmd_none_or_trans_huge_or_clear_bad for info on barrier */ +#ifdef CONFIG_TRANSPARENT_HUGEPAGE + barrier(); +#endif + + if (pmd_none(pmdval)) + return 1; + if (pmd_trans_huge(pmdval)) + return 0; + if (unlikely(pmd_bad(pmdval))) { + pmd_clear_bad(pmd); + return 1; + } + + return 0; +} + static inline unsigned long change_pmd_range(struct vm_area_struct *vma, pud_t *pud, unsigned long addr, unsigned long end, pgprot_t newprot, int dirty_accountable, int prot_numa) @@ -178,8 +203,17 @@ static inline unsigned long change_pmd_range(struct vm_area_struct *vma, unsigned long this_pages; next = pmd_addr_end(addr, end); - if (!is_swap_pmd(*pmd) && !pmd_trans_huge(*pmd) && !pmd_devmap(*pmd) - && pmd_none_or_clear_bad(pmd)) + + /* + * Automatic NUMA balancing walks the tables with mmap_sem + * held for read. It's possible a parallel update to occur + * between pmd_trans_huge() and a pmd_none_or_clear_bad() + * check leading to a false positive and clearing. + * Hence, it's necessary to atomically read the PMD value + * for all the checks. + */ + if (!is_swap_pmd(*pmd) && !pmd_devmap(*pmd) && + pmd_none_or_clear_bad_unless_trans_huge(pmd)) goto next; /* invoke the mmu notifier if the pmd is populated */ diff --git a/mm/mremap.c b/mm/mremap.c index 65ddb8c9e827c37322c801cdc8e3183f5a855dd0..d29b0c3c1571d4c66a1a9b7753464de561b7d1d2 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -539,7 +539,6 @@ SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len, LIST_HEAD(uf_unmap); addr = untagged_addr(addr); - new_addr = untagged_addr(new_addr); if (flags & ~(MREMAP_FIXED | MREMAP_MAYMOVE)) return ret; diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 43df0c52e1ccb65918abd867a7fe1c589f2482ff..078f1461e074633e9066149f945c9cbd5feda0ba 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -200,11 +200,11 @@ static void wb_min_max_ratio(struct bdi_writeback *wb, if (this_bw < tot_bw) { if (min) { min *= this_bw; - do_div(min, tot_bw); + min = div64_ul(min, tot_bw); } if (max < 100) { max *= this_bw; - do_div(max, tot_bw); + max = div64_ul(max, tot_bw); } } diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 7de87d20330afbdbe401f591a1c1cefb57d7fecf..c9665e0a6865a6af24fc299c37d19a713aef1512 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2691,7 +2691,7 @@ __rmqueue(struct zone *zone, unsigned int order, int migratetype, page = __rmqueue_smallest(zone, order, migratetype); if (unlikely(!page) && __rmqueue_fallback(zone, order, migratetype, - alloc_flags)) + alloc_flags)) goto retry; trace_mm_page_alloc_zone_locked(page, order, migratetype); @@ -6912,45 +6912,75 @@ void __init free_area_init_node(int nid, unsigned long *zones_size, } #if defined(CONFIG_HAVE_MEMBLOCK) && !defined(CONFIG_FLAT_NODE_MEM_MAP) + +/* + * Zero all valid struct pages in range [spfn, epfn), return number of struct + * pages zeroed + */ +static u64 zero_pfn_range(unsigned long spfn, unsigned long epfn) +{ + unsigned long pfn; + u64 pgcnt = 0; + + for (pfn = spfn; pfn < epfn; pfn++) { + if (!pfn_valid(ALIGN_DOWN(pfn, pageblock_nr_pages))) { + pfn = ALIGN_DOWN(pfn, pageblock_nr_pages) + + pageblock_nr_pages - 1; + continue; + } + mm_zero_struct_page(pfn_to_page(pfn)); + pgcnt++; + } + + return pgcnt; +} + /* * Only struct pages that are backed by physical memory are zeroed and * initialized by going through __init_single_page(). But, there are some * struct pages which are reserved in memblock allocator and their fields * may be accessed (for example page_to_pfn() on some configuration accesses * flags). We must explicitly zero those struct pages. + * + * This function also addresses a similar issue where struct pages are left + * uninitialized because the physical address range is not covered by + * memblock.memory or memblock.reserved. That could happen when memblock + * layout is manually configured via memmap=, or when the highest physical + * address (max_pfn) does not end on a section boundary. */ void __init zero_resv_unavail(void) { phys_addr_t start, end; - unsigned long pfn; u64 i, pgcnt; + phys_addr_t next = 0; /* - * Loop through ranges that are reserved, but do not have reported - * physical memory backing. + * Loop through unavailable ranges not covered by memblock.memory. */ pgcnt = 0; - for_each_resv_unavail_range(i, &start, &end) { - for (pfn = PFN_DOWN(start); pfn < PFN_UP(end); pfn++) { - if (!pfn_valid(ALIGN_DOWN(pfn, pageblock_nr_pages))) { - pfn = ALIGN_DOWN(pfn, pageblock_nr_pages) - + pageblock_nr_pages - 1; - continue; - } - mm_zero_struct_page(pfn_to_page(pfn)); - pgcnt++; - } + for_each_mem_range(i, &memblock.memory, NULL, + NUMA_NO_NODE, MEMBLOCK_NONE, &start, &end, NULL) { + if (next < start) + pgcnt += zero_pfn_range(PFN_DOWN(next), PFN_UP(start)); + next = end; } + /* + * Early sections always have a fully populated memmap for the whole + * section - see pfn_valid(). If the last section has holes at the + * end and that section is marked "online", the memmap will be + * considered initialized. Make sure that memmap has a well defined + * state. + */ + pgcnt += zero_pfn_range(PFN_DOWN(next), + round_up(max_pfn, PAGES_PER_SECTION)); + /* * Struct pages that do not have backing memory. This could be because * firmware is using some of this memory, or for some other reasons. - * Once memblock is changed so such behaviour is not allowed: i.e. - * list of "reserved" memory must be a subset of list of "memory", then - * this code can be removed. */ if (pgcnt) - pr_info("Reserved but unavailable: %lld pages", pgcnt); + pr_info("Zeroed struct page in unavailable ranges: %lld pages", pgcnt); } #endif /* CONFIG_HAVE_MEMBLOCK && !CONFIG_FLAT_NODE_MEM_MAP */ @@ -8412,7 +8442,9 @@ int alloc_contig_range(unsigned long start, unsigned long end, if (ret) return ret; +#ifdef CONFIG_CMA cc.zone->cma_alloc = 1; +#endif /* * In case of -EBUSY, we'd like to know which page causes problem. * So, just fall through. test_pages_isolated() has a tracepoint @@ -8495,7 +8527,9 @@ int alloc_contig_range(unsigned long start, unsigned long end, done: undo_isolate_page_range(pfn_max_align_down(start), pfn_max_align_up(end), migratetype); +#ifdef CONFIG_CMA cc.zone->cma_alloc = 0; +#endif return ret; } diff --git a/mm/page_io.c b/mm/page_io.c index e763047f0366139f8c87564a8afaecac89e49b70..abf04404c49bb7e095f4cb9899ec60bfbd33066e 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -357,10 +358,19 @@ int swap_readpage(struct page *page, bool synchronous) struct swap_info_struct *sis = page_swap_info(page); blk_qc_t qc; struct gendisk *disk; + unsigned long pflags; VM_BUG_ON_PAGE(!PageSwapCache(page) && !synchronous, page); VM_BUG_ON_PAGE(!PageLocked(page), page); VM_BUG_ON_PAGE(PageUptodate(page), page); + + /* + * Count submission time as memory stall. When the device is congested, + * or the submitting cgroup IO-throttled, submission can be a + * significant part of overall IO time. + */ + psi_memstall_enter(&pflags); + if (frontswap_load(page) == 0) { SetPageUptodate(page); unlock_page(page); @@ -374,7 +384,7 @@ int swap_readpage(struct page *page, bool synchronous) ret = mapping->a_ops->readpage(swap_file, page); if (!ret) count_vm_event(PSWPIN); - return ret; + goto out; } ret = bdev_read_page(sis->bdev, swap_page_sector(page), page); @@ -385,7 +395,7 @@ int swap_readpage(struct page *page, bool synchronous) } count_vm_event(PSWPIN); - return 0; + goto out; } ret = 0; @@ -418,6 +428,7 @@ int swap_readpage(struct page *page, bool synchronous) bio_put(bio); out: + psi_memstall_leave(&pflags); return ret; } diff --git a/mm/shmem.c b/mm/shmem.c index 98c2bf6b60a90990e37f24d219747e8c0b427162..44ae07981ead472641d6ebc82bd97cb1d62c469b 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2072,9 +2072,10 @@ unsigned long shmem_get_unmapped_area(struct file *file, /* * Our priority is to support MAP_SHARED mapped hugely; * and support MAP_PRIVATE mapped hugely too, until it is COWed. - * But if caller specified an address hint, respect that as before. + * But if caller specified an address hint and we allocated area there + * successfully, respect that as before. */ - if (uaddr) + if (uaddr == addr) return addr; if (shmem_huge != SHMEM_HUGE_FORCE) { @@ -2108,7 +2109,7 @@ unsigned long shmem_get_unmapped_area(struct file *file, if (inflated_len < len) return addr; - inflated_addr = get_area(NULL, 0, inflated_len, 0, flags); + inflated_addr = get_area(NULL, uaddr, inflated_len, 0, flags); if (IS_ERR_VALUE(inflated_addr)) return addr; if (inflated_addr & ~PAGE_MASK) diff --git a/mm/slab_common.c b/mm/slab_common.c index c6dbda56765afe2415ae346d7a4b988cfe0bc74f..e9d8b8e2216943cdc323992e2b9a8d1701cae6a2 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -850,7 +850,8 @@ static void flush_memcg_workqueue(struct kmem_cache *s) * deactivates the memcg kmem_caches through workqueue. Make sure all * previous workitems on workqueue are processed. */ - flush_workqueue(memcg_kmem_cache_wq); + if (likely(memcg_kmem_cache_wq)) + flush_workqueue(memcg_kmem_cache_wq); } #else static inline int shutdown_memcg_caches(struct kmem_cache *s) diff --git a/mm/slub.c b/mm/slub.c index 3285989314075571a98b0c6da35ca5b4ea204fc0..512b5ec17cb2d70a6ae05dce206ef7c24cef886b 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -2676,6 +2676,17 @@ static void *__slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node, return p; } +/* + * If the object has been wiped upon free, make sure it's fully initialized by + * zeroing out freelist pointer. + */ +static __always_inline void maybe_wipe_obj_freeptr(struct kmem_cache *s, + void *obj) +{ + if (unlikely(slab_want_init_on_free(s)) && obj) + memset((void *)((char *)obj + s->offset), 0, sizeof(void *)); +} + /* * Inlined fastpath so that allocation functions (kmalloc, kmem_cache_alloc) * have the fastpath folded into their functions. So no function call @@ -2764,12 +2775,8 @@ static __always_inline void *slab_alloc_node(struct kmem_cache *s, prefetch_freepointer(s, next_object); stat(s, ALLOC_FASTPATH); } - /* - * If the object has been wiped upon free, make sure it's fully - * initialized by zeroing out freelist pointer. - */ - if (unlikely(slab_want_init_on_free(s)) && object) - memset(object + s->offset, 0, sizeof(void *)); + + maybe_wipe_obj_freeptr(s, object); if (unlikely(slab_want_init_on_alloc(gfpflags, s)) && object) memset(object, 0, s->object_size); @@ -3184,10 +3191,13 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size, goto error; c = this_cpu_ptr(s->cpu_slab); + maybe_wipe_obj_freeptr(s, p[i]); + continue; /* goto for-loop */ } c->freelist = get_freepointer(s, object); p[i] = object; + maybe_wipe_obj_freeptr(s, p[i]); } c->tid = next_tid(c->tid); local_irq_enable(); diff --git a/mm/sparse.c b/mm/sparse.c index 45950a074bdb44fc8f45cbf3682057bbc074a41a..3b24ba903d9ee57403b63479f50f49c19e2a5c33 100644 --- a/mm/sparse.c +++ b/mm/sparse.c @@ -576,7 +576,6 @@ static void __kfree_section_memmap(struct page *memmap, vmemmap_free(start, end, altmap); } -#ifdef CONFIG_MEMORY_HOTREMOVE static void free_map_bootmem(struct page *memmap) { unsigned long start = (unsigned long)memmap; @@ -584,7 +583,6 @@ static void free_map_bootmem(struct page *memmap) vmemmap_free(start, end, NULL); } -#endif /* CONFIG_MEMORY_HOTREMOVE */ #else static struct page *__kmalloc_section_memmap(void) { @@ -623,7 +621,6 @@ static void __kfree_section_memmap(struct page *memmap, get_order(sizeof(struct page) * PAGES_PER_SECTION)); } -#ifdef CONFIG_MEMORY_HOTREMOVE static void free_map_bootmem(struct page *memmap) { unsigned long maps_section_nr, removing_section_nr, i; @@ -653,7 +650,6 @@ static void free_map_bootmem(struct page *memmap) put_page_bootmem(page); } } -#endif /* CONFIG_MEMORY_HOTREMOVE */ #endif /* CONFIG_SPARSEMEM_VMEMMAP */ /* @@ -661,25 +657,24 @@ static void free_map_bootmem(struct page *memmap) * set. If this is <=0, then that means that the passed-in * map was not consumed and must be freed. */ -int __meminit sparse_add_one_section(struct pglist_data *pgdat, - unsigned long start_pfn, struct vmem_altmap *altmap) +int __meminit sparse_add_one_section(int nid, unsigned long start_pfn, + struct vmem_altmap *altmap) { unsigned long section_nr = pfn_to_section_nr(start_pfn); struct mem_section *ms; struct page *memmap; unsigned long *usemap; - unsigned long flags; int ret; /* * no locking for this, because it does its own * plus, it does a kmalloc */ - ret = sparse_index_init(section_nr, pgdat->node_id); + ret = sparse_index_init(section_nr, nid); if (ret < 0 && ret != -EEXIST) return ret; ret = 0; - memmap = kmalloc_section_memmap(section_nr, pgdat->node_id, altmap); + memmap = kmalloc_section_memmap(section_nr, nid, altmap); if (!memmap) return -ENOMEM; usemap = __kmalloc_section_usemap(); @@ -688,8 +683,6 @@ int __meminit sparse_add_one_section(struct pglist_data *pgdat, return -ENOMEM; } - pgdat_resize_lock(pgdat, &flags); - ms = __pfn_to_section(start_pfn); if (ms->section_mem_map & SECTION_MARKED_PRESENT) { ret = -EEXIST; @@ -708,7 +701,6 @@ int __meminit sparse_add_one_section(struct pglist_data *pgdat, sparse_init_one_section(ms, section_nr, memmap, usemap); out: - pgdat_resize_unlock(pgdat, &flags); if (ret < 0) { kfree(usemap); __kfree_section_memmap(memmap, altmap); @@ -716,7 +708,6 @@ int __meminit sparse_add_one_section(struct pglist_data *pgdat, return ret; } -#ifdef CONFIG_MEMORY_HOTREMOVE #ifdef CONFIG_MEMORY_FAILURE static void clear_hwpoisoned_pages(struct page *memmap, int nr_pages) { @@ -766,14 +757,12 @@ static void free_section_usemap(struct page *memmap, unsigned long *usemap, free_map_bootmem(memmap); } -void sparse_remove_one_section(struct zone *zone, struct mem_section *ms, - unsigned long map_offset, struct vmem_altmap *altmap) +void sparse_remove_one_section(struct mem_section *ms, unsigned long map_offset, + struct vmem_altmap *altmap) { struct page *memmap = NULL; - unsigned long *usemap = NULL, flags; - struct pglist_data *pgdat = zone->zone_pgdat; + unsigned long *usemap = NULL; - pgdat_resize_lock(pgdat, &flags); if (ms->section_mem_map) { usemap = ms->pageblock_flags; memmap = sparse_decode_mem_map(ms->section_mem_map, @@ -781,11 +770,9 @@ void sparse_remove_one_section(struct zone *zone, struct mem_section *ms, ms->section_mem_map = 0; ms->pageblock_flags = NULL; } - pgdat_resize_unlock(pgdat, &flags); clear_hwpoisoned_pages(memmap + map_offset, PAGES_PER_SECTION - map_offset); free_section_usemap(memmap, usemap, altmap); } -#endif /* CONFIG_MEMORY_HOTREMOVE */ #endif /* CONFIG_MEMORY_HOTPLUG */ diff --git a/mm/swapfile.c b/mm/swapfile.c index 03ea5001fbbe63a2d5f35b627b02cd9dc586134c..c69b4faf61eb82c9f23cfe5c8d232565c81b486f 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -2397,6 +2397,7 @@ add_swap_extent(struct swap_info_struct *sis, unsigned long start_page, list_add_tail(&new_se->list, &sis->first_swap_extent.list); return 1; } +EXPORT_SYMBOL_GPL(add_swap_extent); /* * A `swap extent' is a simple thing which maps a contiguous range of pages diff --git a/mm/vmscan.c b/mm/vmscan.c index 98d6a33e1cf9899512ab5832b7d500b67dc78254..cf077f0a6c55abceb938ef5834968b02ea16692f 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -87,6 +87,9 @@ struct scan_control { /* Can pages be swapped as part of reclaim? */ unsigned int may_swap:1; + /* e.g. boosted watermark reclaim leaves slabs alone */ + unsigned int may_shrinkslab:1; + /* * Cgroups are not reclaimed below their configured memory.low, * unless we threaten to OOM. If any cgroups are skipped due to @@ -2517,10 +2520,13 @@ static void get_scan_count(struct lruvec *lruvec, struct mem_cgroup *memcg, /* * Scan types proportional to swappiness and * their relative recent reclaim efficiency. - * Make sure we don't miss the last page - * because of a round-off error. + * Make sure we don't miss the last page on + * the offlined memory cgroups because of a + * round-off error. */ - scan = DIV64_U64_ROUND_UP(scan * fraction[file], + scan = mem_cgroup_online(memcg) ? + div64_u64(scan * fraction[file], denominator) : + DIV64_U64_ROUND_UP(scan * fraction[file], denominator); break; case SCAN_FILE: @@ -3291,6 +3297,7 @@ unsigned long try_to_free_pages(struct zonelist *zonelist, int order, .may_writepage = !laptop_mode, .may_unmap = 1, .may_swap = 1, + .may_shrinkslab = 1, }; /* @@ -3335,6 +3342,7 @@ unsigned long mem_cgroup_shrink_node(struct mem_cgroup *memcg, .may_unmap = 1, .reclaim_idx = MAX_NR_ZONES - 1, .may_swap = !noswap, + .may_shrinkslab = 1, }; unsigned long lru_pages; @@ -3381,6 +3389,7 @@ unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg, .may_writepage = !laptop_mode, .may_unmap = 1, .may_swap = may_swap, + .may_shrinkslab = 1, }; /* diff --git a/net/6lowpan/nhc.c b/net/6lowpan/nhc.c index 4fa2fdda174d0139e669bd282ab4362aea41f512..9e56fb98f33cf0682991774293f77749a272478c 100644 --- a/net/6lowpan/nhc.c +++ b/net/6lowpan/nhc.c @@ -18,7 +18,7 @@ #include "nhc.h" static struct rb_root rb_root = RB_ROOT; -static struct lowpan_nhc *lowpan_nexthdr_nhcs[NEXTHDR_MAX]; +static struct lowpan_nhc *lowpan_nexthdr_nhcs[NEXTHDR_MAX + 1]; static DEFINE_SPINLOCK(lowpan_nhc_lock); static int lowpan_nhc_insert(struct lowpan_nhc *nhc) diff --git a/net/Kconfig b/net/Kconfig index f1704f58b81e965ba91ed7bcf11b0bdbe5f34464..6870f6c838e567af6c142beae0e9ee325b4416d4 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -190,7 +190,6 @@ config BRIDGE_NETFILTER depends on NETFILTER && INET depends on NETFILTER_ADVANCED select NETFILTER_FAMILY_BRIDGE - default m ---help--- Enabling this option will let arptables resp. iptables see bridged ARP resp. IP traffic. If you want a bridging firewall, you probably diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index 2895e3b26e930491dd7609504a225f40ac2e4a52..f2dc7499d266333d0ecb68c498e7c805de860794 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -251,6 +251,7 @@ static u32 batadv_hash_dat(const void *data, u32 size) u32 hash = 0; const struct batadv_dat_entry *dat = data; const unsigned char *key; + __be16 vid; u32 i; key = (const unsigned char *)&dat->ip; @@ -260,7 +261,8 @@ static u32 batadv_hash_dat(const void *data, u32 size) hash ^= (hash >> 6); } - key = (const unsigned char *)&dat->vid; + vid = htons(dat->vid); + key = (__force const unsigned char *)&vid; for (i = 0; i < sizeof(dat->vid); i++) { hash += key[i]; hash += (hash << 10); diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index d4e2a166ae17685f7445729adf4423c0fbd669b6..e506c51ff7653d5cb4a8ddf580924f967d795210 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -831,6 +831,8 @@ static int hci_sock_release(struct socket *sock) if (!sk) return 0; + lock_sock(sk); + switch (hci_pi(sk)->channel) { case HCI_CHANNEL_MONITOR: atomic_dec(&monitor_promisc); @@ -878,6 +880,7 @@ static int hci_sock_release(struct socket *sock) skb_queue_purge(&sk->sk_receive_queue); skb_queue_purge(&sk->sk_write_queue); + release_sock(sk); sock_put(sk); return 0; } diff --git a/net/bpfilter/bpfilter_kern.c b/net/bpfilter/bpfilter_kern.c index 94e88f510c5b8410dac3942677eadcd2ea3b4d83..450b257afa84d22fc52333f1e6aee56455d80f64 100644 --- a/net/bpfilter/bpfilter_kern.c +++ b/net/bpfilter/bpfilter_kern.c @@ -25,7 +25,7 @@ static void shutdown_umh(struct umh_info *info) return; tsk = get_pid_task(find_vpid(info->pid), PIDTYPE_PID); if (tsk) { - force_sig(SIGKILL, tsk); + send_sig(SIGKILL, tsk, 1); put_task_struct(tsk); } fput(info->pipe_to_umh); diff --git a/net/bridge/Kconfig b/net/bridge/Kconfig index aa0d3b2f1bb7f1aa2ea1dfc5b0b230c9b962f258..fbfc9962eeda6c06bcfe5e9de4297278246ccd4f 100644 --- a/net/bridge/Kconfig +++ b/net/bridge/Kconfig @@ -37,7 +37,6 @@ config BRIDGE_IGMP_SNOOPING bool "IGMP/MLD snooping" depends on BRIDGE depends on INET - default y ---help--- If you say Y here, then the Ethernet bridge will be able selectively forward multicast traffic based on IGMP/MLD traffic received from diff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c index 2cf7716254be6e0083f21fcbe8f90d14c9994f3e..d42e3904b498768d903726d41455e94411f3418b 100644 --- a/net/bridge/br_arp_nd_proxy.c +++ b/net/bridge/br_arp_nd_proxy.c @@ -311,7 +311,7 @@ static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p, /* Neighbor Advertisement */ memset(na, 0, sizeof(*na) + na_olen); na->icmph.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT; - na->icmph.icmp6_router = 0; /* XXX: should be 1 ? */ + na->icmph.icmp6_router = (n->flags & NTF_ROUTER) ? 1 : 0; na->icmph.icmp6_override = 1; na->icmph.icmp6_solicited = 1; na->target = ns->target; diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 785e19afd6aaa5b007628da93c2f15862ea92adb..f59230e4fc2956ea178c1c78890f4963a5fecc87 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -2165,7 +2165,9 @@ static int compat_copy_entries(unsigned char *data, unsigned int size_user, if (ret < 0) return ret; - WARN_ON(size_remaining); + if (size_remaining) + return -EINVAL; + return state->buf_kern_offset; } diff --git a/net/core/dev.c b/net/core/dev.c index 100784c519fec67e99cf8bada43ce319255ec170..4d573115aae34fcb8f89433bffb63a78a04f7c34 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4312,14 +4312,14 @@ static u32 netif_receive_generic_xdp(struct sk_buff *skb, /* Reinjected packets coming from act_mirred or similar should * not get XDP generic processing. */ - if (skb_cloned(skb) || skb_is_tc_redirected(skb)) + if (skb_is_tc_redirected(skb)) return XDP_PASS; /* XDP packets must be linear and must have sufficient headroom * of XDP_PACKET_HEADROOM bytes. This is the guarantee that also * native XDP provides, thus we need to do it here as well. */ - if (skb_is_nonlinear(skb) || + if (skb_cloned(skb) || skb_is_nonlinear(skb) || skb_headroom(skb) < XDP_PACKET_HEADROOM) { int hroom = XDP_PACKET_HEADROOM - skb_headroom(skb); int troom = skb->tail + skb->data_len - skb->end; @@ -4355,12 +4355,17 @@ static u32 netif_receive_generic_xdp(struct sk_buff *skb, act = bpf_prog_run_xdp(xdp_prog, xdp); + /* check if bpf_xdp_adjust_head was used */ off = xdp->data - orig_data; - if (off > 0) - __skb_pull(skb, off); - else if (off < 0) - __skb_push(skb, -off); - skb->mac_header += off; + if (off) { + if (off > 0) + __skb_pull(skb, off); + else if (off < 0) + __skb_push(skb, -off); + + skb->mac_header += off; + skb_reset_network_header(skb); + } /* check if bpf_xdp_adjust_tail was used. it can only "shrink" * pckt. @@ -4471,23 +4476,6 @@ static int netif_rx_internal(struct sk_buff *skb) trace_netif_rx(skb); - if (static_branch_unlikely(&generic_xdp_needed_key)) { - int ret; - - preempt_disable(); - rcu_read_lock(); - ret = do_xdp_generic(rcu_dereference(skb->dev->xdp_prog), skb); - rcu_read_unlock(); - preempt_enable(); - - /* Consider XDP consuming the packet a success from - * the netdev point of view we do not want to count - * this as an error. - */ - if (ret != XDP_PASS) - return NET_RX_SUCCESS; - } - #ifdef CONFIG_RPS if (static_key_false(&rps_needed)) { struct rps_dev_flow voidflow, *rflow = &voidflow; @@ -4821,6 +4809,18 @@ static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc, __this_cpu_inc(softnet_data.processed); + if (static_branch_unlikely(&generic_xdp_needed_key)) { + int ret2; + + preempt_disable(); + ret2 = do_xdp_generic(rcu_dereference(skb->dev->xdp_prog), skb); + preempt_enable(); + + if (ret2 != XDP_PASS) + return NET_RX_DROP; + skb_reset_mac_len(skb); + } + if (skb->protocol == cpu_to_be16(ETH_P_8021Q) || skb->protocol == cpu_to_be16(ETH_P_8021AD)) { skb = skb_vlan_untag(skb); @@ -5139,19 +5139,6 @@ static int netif_receive_skb_internal(struct sk_buff *skb) if (skb_defer_rx_timestamp(skb)) return NET_RX_SUCCESS; - if (static_branch_unlikely(&generic_xdp_needed_key)) { - int ret; - - preempt_disable(); - rcu_read_lock(); - ret = do_xdp_generic(rcu_dereference(skb->dev->xdp_prog), skb); - rcu_read_unlock(); - preempt_enable(); - - if (ret != XDP_PASS) - return NET_RX_DROP; - } - rcu_read_lock(); #ifdef CONFIG_RPS if (static_key_false(&rps_needed)) { @@ -5172,7 +5159,6 @@ static int netif_receive_skb_internal(struct sk_buff *skb) static void netif_receive_skb_list_internal(struct list_head *head) { - struct bpf_prog *xdp_prog = NULL; struct sk_buff *skb, *next; struct list_head sublist; @@ -5185,21 +5171,6 @@ static void netif_receive_skb_list_internal(struct list_head *head) } list_splice_init(&sublist, head); - if (static_branch_unlikely(&generic_xdp_needed_key)) { - preempt_disable(); - rcu_read_lock(); - list_for_each_entry_safe(skb, next, head, list) { - xdp_prog = rcu_dereference(skb->dev->xdp_prog); - skb_list_del_init(skb); - if (do_xdp_generic(xdp_prog, skb) == XDP_PASS) - list_add_tail(&skb->list, &sublist); - } - rcu_read_unlock(); - preempt_enable(); - /* Put passed packets back on main list */ - list_splice_init(&sublist, head); - } - rcu_read_lock(); #ifdef CONFIG_RPS if (static_key_false(&rps_needed)) { @@ -7811,6 +7782,22 @@ int __dev_set_mtu(struct net_device *dev, int new_mtu) } EXPORT_SYMBOL(__dev_set_mtu); +int dev_validate_mtu(struct net_device *dev, int new_mtu, + struct netlink_ext_ack *extack) +{ + /* MTU must be positive, and in range */ + if (new_mtu < 0 || new_mtu < dev->min_mtu) { + NL_SET_ERR_MSG(extack, "mtu less than device minimum"); + return -EINVAL; + } + + if (dev->max_mtu > 0 && new_mtu > dev->max_mtu) { + NL_SET_ERR_MSG(extack, "mtu greater than device maximum"); + return -EINVAL; + } + return 0; +} + /** * dev_set_mtu_ext - Change maximum transfer unit * @dev: device @@ -7827,16 +7814,9 @@ int dev_set_mtu_ext(struct net_device *dev, int new_mtu, if (new_mtu == dev->mtu) return 0; - /* MTU must be positive, and in range */ - if (new_mtu < 0 || new_mtu < dev->min_mtu) { - NL_SET_ERR_MSG(extack, "mtu less than device minimum"); - return -EINVAL; - } - - if (dev->max_mtu > 0 && new_mtu > dev->max_mtu) { - NL_SET_ERR_MSG(extack, "mtu greater than device maximum"); - return -EINVAL; - } + err = dev_validate_mtu(dev, new_mtu, extack); + if (err) + return err; if (!netif_device_present(dev)) return -ENODEV; @@ -8755,8 +8735,10 @@ int register_netdevice(struct net_device *dev) goto err_uninit; ret = netdev_register_kobject(dev); - if (ret) + if (ret) { + dev->reg_state = NETREG_UNREGISTERED; goto err_uninit; + } dev->reg_state = NETREG_REGISTERED; __netdev_update_features(dev); diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 09d828a6a173765e49f517aa65efbf3f1152246d..1011625a0ca4bae5053c1b71a7754c6890b85bce 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -2413,9 +2413,10 @@ static int ethtool_set_tunable(struct net_device *dev, void __user *useraddr) return ret; } -static int ethtool_get_per_queue_coalesce(struct net_device *dev, - void __user *useraddr, - struct ethtool_per_queue_op *per_queue_opt) +static noinline_for_stack int +ethtool_get_per_queue_coalesce(struct net_device *dev, + void __user *useraddr, + struct ethtool_per_queue_op *per_queue_opt) { u32 bit; int ret; @@ -2443,9 +2444,10 @@ static int ethtool_get_per_queue_coalesce(struct net_device *dev, return 0; } -static int ethtool_set_per_queue_coalesce(struct net_device *dev, - void __user *useraddr, - struct ethtool_per_queue_op *per_queue_opt) +static noinline_for_stack int +ethtool_set_per_queue_coalesce(struct net_device *dev, + void __user *useraddr, + struct ethtool_per_queue_op *per_queue_opt) { u32 bit; int i, ret = 0; @@ -2499,7 +2501,7 @@ static int ethtool_set_per_queue_coalesce(struct net_device *dev, return ret; } -static int ethtool_set_per_queue(struct net_device *dev, +static int noinline_for_stack ethtool_set_per_queue(struct net_device *dev, void __user *useraddr, u32 sub_cmd) { struct ethtool_per_queue_op per_queue_opt; diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index 0ff3953f64aa7830a07dad9c0873813eb944fdd1..8916c5d9b3b3a5c772a57bae2ac081e93373d174 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -968,7 +968,7 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule, frh = nlmsg_data(nlh); frh->family = ops->family; - frh->table = rule->table; + frh->table = rule->table < 256 ? rule->table : RT_TABLE_COMPAT; if (nla_put_u32(skb, FRA_TABLE, rule->table)) goto nla_put_failure; if (nla_put_u32(skb, FRA_SUPPRESS_PREFIXLEN, rule->suppress_prefixlen)) diff --git a/net/core/filter.c b/net/core/filter.c index 91b950261975770e25305bec83779e8676d2a1c1..23f2207814ac74c82c9dad5bb223e59f83805cc6 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2836,8 +2836,9 @@ static int bpf_skb_net_shrink(struct sk_buff *skb, u32 len_diff) static u32 __bpf_skb_max_len(const struct sk_buff *skb) { - return skb->dev ? skb->dev->mtu + skb->dev->hard_header_len : - SKB_MAX_ALLOC; + if (skb_at_tc_ingress(skb) || !skb->dev) + return SKB_MAX_ALLOC; + return skb->dev->mtu + skb->dev->hard_header_len; } static int bpf_skb_adjust_net(struct sk_buff *skb, s32 len_diff) @@ -3207,7 +3208,7 @@ static int __bpf_tx_xdp_map(struct net_device *dev_rx, void *fwd, return err; } default: - break; + return -EBADRQC; } return 0; } @@ -4367,7 +4368,7 @@ static int bpf_ipv6_fib_lookup(struct net *net, struct bpf_fib_lookup *params, return -ENODEV; idev = __in6_dev_get_safely(dev); - if (unlikely(!idev || !net->ipv6.devconf_all->forwarding)) + if (unlikely(!idev || !idev->cnf.forwarding)) return BPF_FIB_LKUP_RET_FWD_DISABLED; if (flags & BPF_FIB_LOOKUP_OUTPUT) { @@ -4956,6 +4957,8 @@ tc_cls_act_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) return &bpf_skb_adjust_room_proto; case BPF_FUNC_skb_change_tail: return &bpf_skb_change_tail_proto; + case BPF_FUNC_skb_change_head: + return &bpf_skb_change_head_proto; case BPF_FUNC_skb_get_tunnel_key: return &bpf_skb_get_tunnel_key_proto; case BPF_FUNC_skb_set_tunnel_key: diff --git a/net/core/neighbour.c b/net/core/neighbour.c index e6260c81d1af0e58d67a05df32e33363eba7bc0a..9a21bc3efb888174f1e3dc577671bcce45db768a 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -1885,8 +1885,8 @@ static int neightbl_fill_info(struct sk_buff *skb, struct neigh_table *tbl, goto nla_put_failure; { unsigned long now = jiffies; - unsigned int flush_delta = now - tbl->last_flush; - unsigned int rand_delta = now - tbl->last_rand; + long flush_delta = now - tbl->last_flush; + long rand_delta = now - tbl->last_rand; struct neigh_hash_table *nht; struct ndt_config ndc = { .ndtc_key_len = tbl->key_len, diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index bf9a3b6ac885a485d48114de1dc2cac3e790aead..7614a4f42bfc0ffb8e7a8d2f8ba8d18ca3514d43 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -928,25 +928,30 @@ static int rx_queue_add_kobject(struct net_device *dev, int index) struct kobject *kobj = &queue->kobj; int error = 0; + /* Kobject_put later will trigger rx_queue_release call which + * decreases dev refcount: Take that reference here + */ + dev_hold(queue->dev); + kobj->kset = dev->queues_kset; error = kobject_init_and_add(kobj, &rx_queue_ktype, NULL, "rx-%u", index); if (error) - return error; - - dev_hold(queue->dev); + goto err; if (dev->sysfs_rx_queue_group) { error = sysfs_create_group(kobj, dev->sysfs_rx_queue_group); - if (error) { - kobject_put(kobj); - return error; - } + if (error) + goto err; } kobject_uevent(kobj, KOBJ_ADD); return error; + +err: + kobject_put(kobj); + return error; } #endif /* CONFIG_SYSFS */ @@ -1467,25 +1472,29 @@ static int netdev_queue_add_kobject(struct net_device *dev, int index) struct kobject *kobj = &queue->kobj; int error = 0; + /* Kobject_put later will trigger netdev_queue_release call + * which decreases dev refcount: Take that reference here + */ + dev_hold(queue->dev); + kobj->kset = dev->queues_kset; error = kobject_init_and_add(kobj, &netdev_queue_ktype, NULL, "tx-%u", index); if (error) - return error; - - dev_hold(queue->dev); + goto err; #ifdef CONFIG_BQL error = sysfs_create_group(kobj, &dql_group); - if (error) { - kobject_put(kobj); - return error; - } + if (error) + goto err; #endif kobject_uevent(kobj, KOBJ_ADD); - return 0; + +err: + kobject_put(kobj); + return error; } #endif /* CONFIG_SYSFS */ diff --git a/net/core/net-traces.c b/net/core/net-traces.c index 419af6dfe29f4c9db71107a294ac9f508de0ef9b..0925c7b5ebc21a62db36bcc74509fe4562d0b38f 100644 --- a/net/core/net-traces.c +++ b/net/core/net-traces.c @@ -35,13 +35,11 @@ #include #include #include -#if IS_ENABLED(CONFIG_BRIDGE) #include EXPORT_TRACEPOINT_SYMBOL_GPL(br_fdb_add); EXPORT_TRACEPOINT_SYMBOL_GPL(br_fdb_external_learn_add); EXPORT_TRACEPOINT_SYMBOL_GPL(fdb_delete); EXPORT_TRACEPOINT_SYMBOL_GPL(br_fdb_update); -#endif EXPORT_TRACEPOINT_SYMBOL_GPL(kfree_skb); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index dbb3c0c7c13272cf845b3e0944c2e5c0855b9667..f51973f458e44bd72ba64c581f68ee480d476f85 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -2875,8 +2875,17 @@ struct net_device *rtnl_create_link(struct net *net, dev->rtnl_link_ops = ops; dev->rtnl_link_state = RTNL_LINK_INITIALIZING; - if (tb[IFLA_MTU]) - dev->mtu = nla_get_u32(tb[IFLA_MTU]); + if (tb[IFLA_MTU]) { + u32 mtu = nla_get_u32(tb[IFLA_MTU]); + int err; + + err = dev_validate_mtu(dev, mtu, NULL); + if (err) { + free_netdev(dev); + return ERR_PTR(err); + } + dev->mtu = mtu; + } if (tb[IFLA_ADDRESS]) { memcpy(dev->dev_addr, nla_data(tb[IFLA_ADDRESS]), nla_len(tb[IFLA_ADDRESS])); diff --git a/net/core/sock.c b/net/core/sock.c index 13ce20f04900ebafcdda13883d44c8d0204a8ac2..7744036c09b10443498e29bb407155052357bdc3 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2179,8 +2179,8 @@ static void sk_leave_memory_pressure(struct sock *sk) } else { unsigned long *memory_pressure = sk->sk_prot->memory_pressure; - if (memory_pressure && *memory_pressure) - *memory_pressure = 0; + if (memory_pressure && READ_ONCE(*memory_pressure)) + WRITE_ONCE(*memory_pressure, 0); } } diff --git a/net/core/utils.c b/net/core/utils.c index 2a597ac7808e2c9b3e68b9ca03ffe0dd4a0d9853..60045e9fea05b7026332694449a5aaf83aa5f97e 100644 --- a/net/core/utils.c +++ b/net/core/utils.c @@ -442,6 +442,23 @@ void inet_proto_csum_replace4(__sum16 *sum, struct sk_buff *skb, } EXPORT_SYMBOL(inet_proto_csum_replace4); +/** + * inet_proto_csum_replace16 - update layer 4 header checksum field + * @sum: Layer 4 header checksum field + * @skb: sk_buff for the packet + * @from: old IPv6 address + * @to: new IPv6 address + * @pseudohdr: True if layer 4 header checksum includes pseudoheader + * + * Update layer 4 header as per the update in IPv6 src/dst address. + * + * There is no need to update skb->csum in this function, because update in two + * fields a.) IPv6 src/dst address and b.) L4 header checksum cancels each other + * for skb->csum calculation. Whereas inet_proto_csum_replace4 function needs to + * update skb->csum, because update in 3 fields a.) IPv4 src/dst address, + * b.) IPv4 Header checksum and c.) L4 header checksum results in same diff as + * L4 Header checksum for skb->csum calculation. + */ void inet_proto_csum_replace16(__sum16 *sum, struct sk_buff *skb, const __be32 *from, const __be32 *to, bool pseudohdr) @@ -453,9 +470,6 @@ void inet_proto_csum_replace16(__sum16 *sum, struct sk_buff *skb, if (skb->ip_summed != CHECKSUM_PARTIAL) { *sum = csum_fold(csum_partial(diff, sizeof(diff), ~csum_unfold(*sum))); - if (skb->ip_summed == CHECKSUM_COMPLETE && pseudohdr) - skb->csum = ~csum_partial(diff, sizeof(diff), - ~skb->csum); } else if (pseudohdr) *sum = ~csum_fold(csum_partial(diff, sizeof(diff), csum_unfold(*sum))); diff --git a/net/dccp/feat.c b/net/dccp/feat.c index f227f002c73d382fecd98c8857ce4c9139cb7a8a..db87d9f5801983913e66549e5d5911ead10f3ac1 100644 --- a/net/dccp/feat.c +++ b/net/dccp/feat.c @@ -738,7 +738,12 @@ static int __feat_register_sp(struct list_head *fn, u8 feat, u8 is_local, if (dccp_feat_clone_sp_val(&fval, sp_val, sp_len)) return -ENOMEM; - return dccp_feat_push_change(fn, feat, is_local, mandatory, &fval); + if (dccp_feat_push_change(fn, feat, is_local, mandatory, &fval)) { + kfree(fval.sp.vec); + return -ENOMEM; + } + + return 0; } /** diff --git a/net/dsa/port.c b/net/dsa/port.c index ed0595459df13e61c10bd18fe0025d61f5837d97..ea7efc86b9d7c4ab28afdee5d1c4dac6524f4558 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -69,7 +69,6 @@ static void dsa_port_set_state_now(struct dsa_port *dp, u8 state) int dsa_port_enable(struct dsa_port *dp, struct phy_device *phy) { - u8 stp_state = dp->bridge_dev ? BR_STATE_BLOCKING : BR_STATE_FORWARDING; struct dsa_switch *ds = dp->ds; int port = dp->index; int err; @@ -80,7 +79,8 @@ int dsa_port_enable(struct dsa_port *dp, struct phy_device *phy) return err; } - dsa_port_set_state_now(dp, stp_state); + if (!dp->bridge_dev) + dsa_port_set_state_now(dp, BR_STATE_FORWARDING); return 0; } @@ -90,7 +90,8 @@ void dsa_port_disable(struct dsa_port *dp, struct phy_device *phy) struct dsa_switch *ds = dp->ds; int port = dp->index; - dsa_port_set_state_now(dp, BR_STATE_DISABLED); + if (!dp->bridge_dev) + dsa_port_set_state_now(dp, BR_STATE_DISABLED); if (ds->ops->port_disable) ds->ops->port_disable(ds, port, phy); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index b39720d0995d3a741a57cef46cc2a28c25abfe46..8ee28b6016d8267412c69353ee1fd6ecd9b44674 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -1219,9 +1219,9 @@ static int dsa_slave_phy_setup(struct net_device *slave_dev) phy_flags = ds->ops->get_phy_flags(ds, dp->index); ret = phylink_of_phy_connect(dp->pl, port_dn, phy_flags); - if (ret == -ENODEV) { - /* We could not connect to a designated PHY or SFP, so use the - * switch internal MDIO bus instead + if (ret == -ENODEV && ds->slave_mii_bus) { + /* We could not connect to a designated PHY or SFP, so try to + * use the switch internal MDIO bus instead */ ret = dsa_slave_phy_connect(slave_dev, dp->index); if (ret) { @@ -1233,7 +1233,7 @@ static int dsa_slave_phy_setup(struct net_device *slave_dev) } } - return 0; + return ret; } static struct lock_class_key dsa_slave_netdev_xmit_lock_key; diff --git a/net/dsa/tag_qca.c b/net/dsa/tag_qca.c index 613f4ee97771629c5bd46951e9fe2ab6b1297fe5..9c0dd31d44456d3e7cbab1d3fc55b8f0aaf71bc7 100644 --- a/net/dsa/tag_qca.c +++ b/net/dsa/tag_qca.c @@ -41,10 +41,7 @@ static struct sk_buff *qca_tag_xmit(struct sk_buff *skb, struct net_device *dev) struct dsa_port *dp = dsa_slave_to_port(dev); u16 *phdr, hdr; - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - - if (skb_cow_head(skb, 0) < 0) + if (skb_cow_head(skb, QCA_HDR_LEN) < 0) return NULL; skb_push(skb, QCA_HDR_LEN); diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c index a97bf326b231fe05c7562ca49279fc3fda9d53c0..c27577af32e3a1d0ce9990da79fd159dc48cb982 100644 --- a/net/hsr/hsr_device.c +++ b/net/hsr/hsr_device.c @@ -281,6 +281,8 @@ static void send_hsr_supervision_frame(struct hsr_port *master, skb->dev->dev_addr, skb->len) <= 0) goto out; skb_reset_mac_header(skb); + skb_reset_network_header(skb); + skb_reset_transport_header(skb); if (hsrVer > 0) { hsr_tag = skb_put(skb, sizeof(struct hsr_tag)); diff --git a/net/hsr/hsr_slave.c b/net/hsr/hsr_slave.c index 56080da4aa77ef581f4a3c23b4179d72e511980b..5fee6ec7c93d44cbc35bbb74989f73bbc7f7e774 100644 --- a/net/hsr/hsr_slave.c +++ b/net/hsr/hsr_slave.c @@ -32,6 +32,8 @@ static rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb) rcu_read_lock(); /* hsr->node_db, hsr->ports */ port = hsr_port_get_rcu(skb->dev); + if (!port) + goto finish_pass; if (hsr_addr_is_self(port->hsr, eth_hdr(skb)->h_source)) { /* Directly kill frames sent by ourselves */ diff --git a/net/ieee802154/6lowpan/reassembly.c b/net/ieee802154/6lowpan/reassembly.c index e7857a8ac86db517cccd0e0a14b95d9c975c6b09..f3074249c6fc9f3368f68a41049211757836fe5a 100644 --- a/net/ieee802154/6lowpan/reassembly.c +++ b/net/ieee802154/6lowpan/reassembly.c @@ -629,7 +629,7 @@ int __init lowpan_net_frag_init(void) void lowpan_net_frag_exit(void) { - inet_frags_fini(&lowpan_frags); lowpan_frags_sysctl_unregister(); unregister_pernet_subsys(&lowpan_frags_ops); + inet_frags_fini(&lowpan_frags); } diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index f6f6dfa2803af2444bdcfe76ac832d4935139601..5e818f48a28cea5c5fc0674a4ea4cab93c1072dd 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -446,8 +446,8 @@ int inet_release(struct socket *sock) if (sock_flag(sk, SOCK_LINGER) && !(current->flags & PF_EXITING)) timeout = sk->sk_lingertime; - sock->sk = NULL; sk->sk_prot->close(sk, timeout); + sock->sk = NULL; } return 0; } diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 9ef63de460d706956493d289d4357fdf63c449da..e10d04ba64a74e546c39321656fdf0076d4c615d 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -944,7 +944,7 @@ struct sock *inet_csk_reqsk_queue_add(struct sock *sk, req->sk = child; req->dl_next = NULL; if (queue->rskq_accept_head == NULL) - queue->rskq_accept_head = req; + WRITE_ONCE(queue->rskq_accept_head, req); else queue->rskq_accept_tail->dl_next = req; queue->rskq_accept_tail = req; diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index d63091812342fd4f78b0a92d962f382a28401a93..fbf30122e8bf26b997dc50e14c47f7c5b0828b54 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -940,7 +940,7 @@ static int __ip_append_data(struct sock *sk, unsigned int fraglen; unsigned int fraggap; unsigned int alloclen; - unsigned int pagedlen = 0; + unsigned int pagedlen; struct sk_buff *skb_prev; alloc_new_skb: skb_prev = skb; @@ -957,6 +957,7 @@ static int __ip_append_data(struct sock *sk, if (datalen > mtu - fragheaderlen) datalen = maxfraglen - fragheaderlen; fraglen = datalen + fragheaderlen; + pagedlen = 0; if ((flags & MSG_MORE) && !(rt->dst.dev->features&NETIF_F_SG)) diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index 420e891ac59da653a584a465bb60d31f1ed51239..14fd8a37a72950dd8d25928d20f277fe9ff0df40 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -574,8 +574,9 @@ void ip_md_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, u8 proto) else if (skb->protocol == htons(ETH_P_IPV6)) tos = ipv6_get_dsfield((const struct ipv6hdr *)inner_iph); } - ip_tunnel_init_flow(&fl4, proto, key->u.ipv4.dst, key->u.ipv4.src, 0, - RT_TOS(tos), tunnel->parms.link, tunnel->fwmark); + ip_tunnel_init_flow(&fl4, proto, key->u.ipv4.dst, key->u.ipv4.src, + tunnel_id_to_key32(key->tun_id), RT_TOS(tos), + 0, skb->mark); if (tunnel->encap.type != TUNNEL_ENCAP_NONE) goto tx_error; rt = ip_route_output_key(tunnel->net, &fl4); @@ -1202,10 +1203,8 @@ int ip_tunnel_init(struct net_device *dev) iph->version = 4; iph->ihl = 5; - if (tunnel->collect_md) { - dev->features |= NETIF_F_NETNS_LOCAL; + if (tunnel->collect_md) netif_keep_dst(dev); - } return 0; } EXPORT_SYMBOL_GPL(ip_tunnel_init); diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c index 960f4faaf2942891b3ba1f71b3a181584bb714c3..f5e5fcd9085927e1b4d641337d601f703b56504b 100644 --- a/net/ipv4/ip_vti.c +++ b/net/ipv4/ip_vti.c @@ -208,8 +208,17 @@ static netdev_tx_t vti_xmit(struct sk_buff *skb, struct net_device *dev, int mtu; if (!dst) { - dev->stats.tx_carrier_errors++; - goto tx_error_icmp; + struct rtable *rt; + + fl->u.ip4.flowi4_oif = dev->ifindex; + fl->u.ip4.flowi4_flags |= FLOWI_FLAG_ANYSRC; + rt = __ip_route_output_key(dev_net(dev), &fl->u.ip4); + if (IS_ERR(rt)) { + dev->stats.tx_carrier_errors++; + goto tx_error_icmp; + } + dst = &rt->dst; + skb_dst_set(skb, dst); } dst_hold(dst); diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index 0199ee87650927e25f30b429bc9e05efcc2f1559..51d2f7567ee9bde17e7c0529c33823b537cd4f87 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -383,10 +383,11 @@ next: ; return 1; } -static inline int check_target(struct arpt_entry *e, const char *name) +static int check_target(struct arpt_entry *e, struct net *net, const char *name) { struct xt_entry_target *t = arpt_get_target(e); struct xt_tgchk_param par = { + .net = net, .table = name, .entryinfo = e, .target = t->u.kernel.target, @@ -398,8 +399,9 @@ static inline int check_target(struct arpt_entry *e, const char *name) return xt_check_target(&par, t->u.target_size - sizeof(*t), 0, false); } -static inline int -find_check_entry(struct arpt_entry *e, const char *name, unsigned int size, +static int +find_check_entry(struct arpt_entry *e, struct net *net, const char *name, + unsigned int size, struct xt_percpu_counter_alloc_state *alloc_state) { struct xt_entry_target *t; @@ -418,7 +420,7 @@ find_check_entry(struct arpt_entry *e, const char *name, unsigned int size, } t->u.kernel.target = target; - ret = check_target(e, name); + ret = check_target(e, net, name); if (ret) goto err; return 0; @@ -493,12 +495,13 @@ static inline int check_entry_size_and_hooks(struct arpt_entry *e, return 0; } -static inline void cleanup_entry(struct arpt_entry *e) +static void cleanup_entry(struct arpt_entry *e, struct net *net) { struct xt_tgdtor_param par; struct xt_entry_target *t; t = arpt_get_target(e); + par.net = net; par.target = t->u.kernel.target; par.targinfo = t->data; par.family = NFPROTO_ARP; @@ -511,7 +514,9 @@ static inline void cleanup_entry(struct arpt_entry *e) /* Checks and translates the user-supplied table segment (held in * newinfo). */ -static int translate_table(struct xt_table_info *newinfo, void *entry0, +static int translate_table(struct net *net, + struct xt_table_info *newinfo, + void *entry0, const struct arpt_replace *repl) { struct xt_percpu_counter_alloc_state alloc_state = { 0 }; @@ -568,7 +573,7 @@ static int translate_table(struct xt_table_info *newinfo, void *entry0, /* Finally, each sanity check must pass */ i = 0; xt_entry_foreach(iter, entry0, newinfo->size) { - ret = find_check_entry(iter, repl->name, repl->size, + ret = find_check_entry(iter, net, repl->name, repl->size, &alloc_state); if (ret != 0) break; @@ -579,7 +584,7 @@ static int translate_table(struct xt_table_info *newinfo, void *entry0, xt_entry_foreach(iter, entry0, newinfo->size) { if (i-- == 0) break; - cleanup_entry(iter); + cleanup_entry(iter, net); } return ret; } @@ -920,7 +925,7 @@ static int __do_replace(struct net *net, const char *name, /* Decrease module usage counts and free resource */ loc_cpu_old_entry = oldinfo->entries; xt_entry_foreach(iter, loc_cpu_old_entry, oldinfo->size) - cleanup_entry(iter); + cleanup_entry(iter, net); xt_free_table_info(oldinfo); if (copy_to_user(counters_ptr, counters, @@ -972,7 +977,7 @@ static int do_replace(struct net *net, const void __user *user, goto free_newinfo; } - ret = translate_table(newinfo, loc_cpu_entry, &tmp); + ret = translate_table(net, newinfo, loc_cpu_entry, &tmp); if (ret != 0) goto free_newinfo; @@ -984,7 +989,7 @@ static int do_replace(struct net *net, const void __user *user, free_newinfo_untrans: xt_entry_foreach(iter, loc_cpu_entry, newinfo->size) - cleanup_entry(iter); + cleanup_entry(iter, net); free_newinfo: xt_free_table_info(newinfo); return ret; @@ -1147,7 +1152,8 @@ compat_copy_entry_from_user(struct compat_arpt_entry *e, void **dstptr, } } -static int translate_compat_table(struct xt_table_info **pinfo, +static int translate_compat_table(struct net *net, + struct xt_table_info **pinfo, void **pentry0, const struct compat_arpt_replace *compatr) { @@ -1215,7 +1221,7 @@ static int translate_compat_table(struct xt_table_info **pinfo, repl.num_counters = 0; repl.counters = NULL; repl.size = newinfo->size; - ret = translate_table(newinfo, entry1, &repl); + ret = translate_table(net, newinfo, entry1, &repl); if (ret) goto free_newinfo; @@ -1268,7 +1274,7 @@ static int compat_do_replace(struct net *net, void __user *user, goto free_newinfo; } - ret = translate_compat_table(&newinfo, &loc_cpu_entry, &tmp); + ret = translate_compat_table(net, &newinfo, &loc_cpu_entry, &tmp); if (ret != 0) goto free_newinfo; @@ -1280,7 +1286,7 @@ static int compat_do_replace(struct net *net, void __user *user, free_newinfo_untrans: xt_entry_foreach(iter, loc_cpu_entry, newinfo->size) - cleanup_entry(iter); + cleanup_entry(iter, net); free_newinfo: xt_free_table_info(newinfo); return ret; @@ -1507,7 +1513,7 @@ static int do_arpt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len return ret; } -static void __arpt_unregister_table(struct xt_table *table) +static void __arpt_unregister_table(struct net *net, struct xt_table *table) { struct xt_table_info *private; void *loc_cpu_entry; @@ -1519,7 +1525,7 @@ static void __arpt_unregister_table(struct xt_table *table) /* Decrease module usage counts and free resources */ loc_cpu_entry = private->entries; xt_entry_foreach(iter, loc_cpu_entry, private->size) - cleanup_entry(iter); + cleanup_entry(iter, net); if (private->number > private->initial_entries) module_put(table_owner); xt_free_table_info(private); @@ -1544,7 +1550,7 @@ int arpt_register_table(struct net *net, loc_cpu_entry = newinfo->entries; memcpy(loc_cpu_entry, repl->entries, repl->size); - ret = translate_table(newinfo, loc_cpu_entry, repl); + ret = translate_table(net, newinfo, loc_cpu_entry, repl); if (ret != 0) goto out_free; @@ -1559,7 +1565,7 @@ int arpt_register_table(struct net *net, ret = nf_register_net_hooks(net, ops, hweight32(table->valid_hooks)); if (ret != 0) { - __arpt_unregister_table(new_table); + __arpt_unregister_table(net, new_table); *res = NULL; } @@ -1574,7 +1580,7 @@ void arpt_unregister_table(struct net *net, struct xt_table *table, const struct nf_hook_ops *ops) { nf_unregister_net_hooks(net, ops, hweight32(table->valid_hooks)); - __arpt_unregister_table(table); + __arpt_unregister_table(net, table); } /* The built-in targets: standard (NULL) and error. */ diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index ddbfa7919e2b3fb67bc77033863aa66650efee9e..5bc66da4d4d5ec6b2df42938f7b6bf1a8612d095 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -330,7 +330,7 @@ void tcp_enter_memory_pressure(struct sock *sk) { unsigned long val; - if (tcp_memory_pressure) + if (READ_ONCE(tcp_memory_pressure)) return; val = jiffies; @@ -345,7 +345,7 @@ void tcp_leave_memory_pressure(struct sock *sk) { unsigned long val; - if (!tcp_memory_pressure) + if (!READ_ONCE(tcp_memory_pressure)) return; val = xchg(&tcp_memory_pressure, 0); if (val) @@ -1082,8 +1082,7 @@ ssize_t do_tcp_sendpages(struct sock *sk, struct page *page, int offset, goto out; out_err: /* make sure we wake any epoll edge trigger waiter */ - if (unlikely(skb_queue_len(&sk->sk_write_queue) == 0 && - err == -EAGAIN)) { + if (unlikely(tcp_rtx_and_write_queues_empty(sk) && err == -EAGAIN)) { sk->sk_write_space(sk); tcp_chrono_stop(sk, TCP_CHRONO_SNDBUF_LIMITED); } @@ -1442,8 +1441,7 @@ int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size) sock_zerocopy_put_abort(uarg); err = sk_stream_error(sk, flags, err); /* make sure we wake any epoll edge trigger waiter */ - if (unlikely(skb_queue_len(&sk->sk_write_queue) == 0 && - err == -EAGAIN)) { + if (unlikely(tcp_rtx_and_write_queues_empty(sk) && err == -EAGAIN)) { sk->sk_write_space(sk); tcp_chrono_stop(sk, TCP_CHRONO_SNDBUF_LIMITED); } @@ -2517,6 +2515,7 @@ static void tcp_rtx_queue_purge(struct sock *sk) { struct rb_node *p = rb_first(&sk->tcp_rtx_queue); + tcp_sk(sk)->highest_sack = NULL; while (p) { struct sk_buff *skb = rb_to_skb(p); @@ -2599,10 +2598,12 @@ int tcp_disconnect(struct sock *sk, int flags) tp->snd_ssthresh = TCP_INFINITE_SSTHRESH; tp->snd_cwnd_cnt = 0; tp->window_clamp = 0; + tp->delivered = 0; tp->delivered_ce = 0; tcp_set_ca_state(sk, TCP_CA_Open); tp->is_sack_reneg = 0; tcp_clear_retrans(tp); + tp->total_retrans = 0; inet_csk_delack_init(sk); /* Initialize rcv_mss to TCP_MIN_MSS to avoid division by 0 * issue in __tcp_select_window() @@ -2614,10 +2615,14 @@ int tcp_disconnect(struct sock *sk, int flags) sk->sk_rx_dst = NULL; tcp_saved_syn_free(tp); tp->compressed_ack = 0; + tp->segs_in = 0; + tp->segs_out = 0; tp->bytes_sent = 0; tp->bytes_acked = 0; tp->bytes_received = 0; tp->bytes_retrans = 0; + tp->data_segs_in = 0; + tp->data_segs_out = 0; tp->dsack_dups = 0; tp->reord_seen = 0; diff --git a/net/ipv4/tcp_bbr.c b/net/ipv4/tcp_bbr.c index 02ff2dde96094cf33b662a20994424a7adea509e..b371e66502c3665e9f9720fe6062d44e1f2bc175 100644 --- a/net/ipv4/tcp_bbr.c +++ b/net/ipv4/tcp_bbr.c @@ -680,8 +680,7 @@ static void bbr_update_bw(struct sock *sk, const struct rate_sample *rs) * bandwidth sample. Delivered is in packets and interval_us in uS and * ratio will be <<1 for most connections. So delivered is first scaled. */ - bw = (u64)rs->delivered * BW_UNIT; - do_div(bw, rs->interval_us); + bw = div64_long((u64)rs->delivered * BW_UNIT, rs->interval_us); /* If this sample is application-limited, it is likely to have a very * low delivered count that represents application behavior rather than diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index d4cecd4c6dc255c83adf3f377ef8c6edfe4c6d0e..496a5ac0e71f19f1eb880b1c747b51942dc9a7b3 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -901,9 +901,10 @@ static void tcp_check_sack_reordering(struct sock *sk, const u32 low_seq, /* This must be called before lost_out is incremented */ static void tcp_verify_retransmit_hint(struct tcp_sock *tp, struct sk_buff *skb) { - if (!tp->retransmit_skb_hint || - before(TCP_SKB_CB(skb)->seq, - TCP_SKB_CB(tp->retransmit_skb_hint)->seq)) + if ((!tp->retransmit_skb_hint && tp->retrans_out >= tp->lost_out) || + (tp->retransmit_skb_hint && + before(TCP_SKB_CB(skb)->seq, + TCP_SKB_CB(tp->retransmit_skb_hint)->seq))) tp->retransmit_skb_hint = skb; } @@ -3148,6 +3149,7 @@ static int tcp_clean_rtx_queue(struct sock *sk, u32 prior_fack, tp->retransmit_skb_hint = NULL; if (unlikely(skb == tp->lost_skb_hint)) tp->lost_skb_hint = NULL; + tcp_highest_sack_replace(sk, skb, next); tcp_rtx_queue_unlink_and_free(skb, sk); } diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index b0e89704ea27cad90c093e8bd8a061406faafd22..13213c250528d2e6497b88898853ebe117eef8a0 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -3168,6 +3168,7 @@ int tcp_send_synack(struct sock *sk) if (!nskb) return -ENOMEM; INIT_LIST_HEAD(&nskb->tcp_tsorted_anchor); + tcp_highest_sack_replace(sk, skb, nskb); tcp_rtx_queue_unlink_and_free(skb, sk); __skb_header_release(nskb); tcp_rbtree_insert(&sk->tcp_rtx_queue, nskb); diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 6d22933e795e8e9e83b929f2a992ce09ecce46e8..d3b8b3cfcb3e5a3aa1044da03038d57150166417 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1311,7 +1311,8 @@ static void udp_rmem_release(struct sock *sk, int size, int partial, if (likely(partial)) { up->forward_deficit += size; size = up->forward_deficit; - if (size < (sk->sk_rcvbuf >> 2)) + if (size < (sk->sk_rcvbuf >> 2) && + !skb_queue_empty(&up->reader_queue)) return; } else { size += up->forward_deficit; @@ -1918,9 +1919,11 @@ static int __udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) int is_udplite = IS_UDPLITE(sk); /* Note that an ENOMEM error is charged twice */ - if (rc == -ENOMEM) + if (rc == -ENOMEM) { UDP_INC_STATS(sock_net(sk), UDP_MIB_RCVBUFERRORS, is_udplite); + trace_udpv4_fail_rcv_buf_errors(skb); + } UDP_INC_STATS(sock_net(sk), UDP_MIB_INERRORS, is_udplite); kfree_skb(skb); trace_udp_fail_queue_rcv_skb(rc, sk); diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c index d7e17e81203b0554cef9c3d933f28a6c0e528479..7e848ddb1b3143bbde66855be308924cf030b6b7 100644 --- a/net/ipv4/udp_offload.c +++ b/net/ipv4/udp_offload.c @@ -227,6 +227,11 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb, seg = segs; uh = udp_hdr(seg); + /* preserve TX timestamp flags and TS key for first segment */ + skb_shinfo(seg)->tskey = skb_shinfo(gso_skb)->tskey; + skb_shinfo(seg)->tx_flags |= + (skb_shinfo(gso_skb)->tx_flags & SKBTX_ANY_TSTAMP); + /* compute checksum adjustment based on old length versus new */ newlen = htons(sizeof(*uh) + mss); check = csum16_add(csum16_sub(uh->check, uh->len), newlen); diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index bbb5ffb3397d8b98227c81023cac218603822619..5e8979c1f76d86a2c570d6e0581a899ce2658aa1 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -981,8 +981,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct fib6_info *rt, found++; break; } - if (rt_can_ecmp) - fallback_ins = fallback_ins ?: ins; + fallback_ins = fallback_ins ?: ins; goto next_iter; } @@ -1025,7 +1024,9 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct fib6_info *rt, } if (fallback_ins && !found) { - /* No ECMP-able route found, replace first non-ECMP one */ + /* No matching route with same ecmp-able-ness found, replace + * first matching route + */ ins = fallback_ins; iter = rcu_dereference_protected(*ins, lockdep_is_held(&rt->fib6_table->tb6_lock)); @@ -1529,7 +1530,8 @@ static struct fib6_node *fib6_locate_1(struct fib6_node *root, if (plen == fn->fn_bit) return fn; - prev = fn; + if (fn->fn_flags & RTN_RTINFO) + prev = fn; next: /* diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index b3515a4f13039fffde865a3a47862e1112fd508a..90621d498fd144aef81068519806e208069a8327 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -1486,7 +1486,6 @@ static int ip6gre_tunnel_init_common(struct net_device *dev) dev->mtu -= 8; if (tunnel->parms.collect_md) { - dev->features |= NETIF_F_NETNS_LOCAL; netif_keep_dst(dev); } ip6gre_tnl_init_features(dev); @@ -1914,7 +1913,6 @@ static void ip6gre_tap_setup(struct net_device *dev) dev->needs_free_netdev = true; dev->priv_destructor = ip6gre_dev_free; - dev->features |= NETIF_F_NETNS_LOCAL; dev->priv_flags &= ~IFF_TX_SKB_SHARING; dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; netif_keep_dst(dev); @@ -2218,11 +2216,11 @@ static void ip6erspan_tap_setup(struct net_device *dev) { ether_setup(dev); + dev->max_mtu = 0; dev->netdev_ops = &ip6erspan_netdev_ops; dev->needs_free_netdev = true; dev->priv_destructor = ip6gre_dev_free; - dev->features |= NETIF_F_NETNS_LOCAL; dev->priv_flags &= ~IFF_TX_SKB_SHARING; dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; netif_keep_dst(dev); diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index eed9231c90ad52041fad10d57a00d6280471a205..9886a84c25117fb9ec8ad04f3764ebb4424ef3f3 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1357,7 +1357,7 @@ static int __ip6_append_data(struct sock *sk, unsigned int fraglen; unsigned int fraggap; unsigned int alloclen; - unsigned int pagedlen = 0; + unsigned int pagedlen; alloc_new_skb: /* There's no room in the current skb */ if (skb) @@ -1381,6 +1381,7 @@ static int __ip6_append_data(struct sock *sk, if (datalen > (cork->length <= mtu && !(cork->flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - fragheaderlen) datalen = maxfraglen - fragheaderlen - rt->dst.trailer_len; fraglen = datalen + fragheaderlen; + pagedlen = 0; if ((flags & MSG_MORE) && !(rt->dst.dev->features&NETIF_F_SG)) diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index e3b4237b2832a71b0e93f8480253d5bbe09195da..8e70a015c7928d4c38020445ab291440a4dbea97 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -1882,10 +1882,8 @@ static int ip6_tnl_dev_init(struct net_device *dev) if (err) return err; ip6_tnl_link_config(t); - if (t->parms.collect_md) { - dev->features |= NETIF_F_NETNS_LOCAL; + if (t->parms.collect_md) netif_keep_dst(dev); - } return 0; } diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index bfd39db3398a52e1f4e8173338718fd8c0f69a74..67ff206b6d619172642720db0b86bb8ebaea7e50 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -453,8 +453,17 @@ vti6_xmit(struct sk_buff *skb, struct net_device *dev, struct flowi *fl) int err = -1; int mtu; - if (!dst) - goto tx_err_link_failure; + if (!dst) { + fl->u.ip6.flowi6_oif = dev->ifindex; + fl->u.ip6.flowi6_flags |= FLOWI_FLAG_ANYSRC; + dst = ip6_route_output(dev_net(dev), NULL, &fl->u.ip6); + if (dst->error) { + dst_release(dst); + dst = NULL; + goto tx_err_link_failure; + } + skb_dst_set(skb, dst); + } dst_hold(dst); dst = xfrm_lookup(t->net, dst, fl, NULL, 0); diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 45d8d08d199464ea267696b7b6bbaf74c657a7b8..171c5273acdcc124111b414c42969f47c8af92c1 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -198,6 +198,7 @@ static inline int ndisc_is_useropt(const struct net_device *dev, return opt->nd_opt_type == ND_OPT_RDNSS || opt->nd_opt_type == ND_OPT_DNSSL || opt->nd_opt_type == ND_OPT_CAPTIVE_PORTAL || + opt->nd_opt_type == ND_OPT_PREF64 || ndisc_ops_is_useropt(dev, opt->nd_opt_type); } diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 4856d9320b28e2e9ef6cc03e80f348ebdc82c575..a41156a00dd441be2f9b8ac2faa18ca631e71b64 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -660,6 +660,8 @@ static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length, skb->ip_summed = CHECKSUM_NONE; + sock_tx_timestamp(sk, sockc->tsflags, &skb_shinfo(skb)->tx_flags); + if (flags & MSG_CONFIRM) skb_set_dst_pending_confirm(skb, 1); diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index 095825f964e28455f20bbf808baacebf4e3aa266..c6132e39ab16b7d3298895440fc5a496ce1c80ad 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -593,8 +593,8 @@ int __init ipv6_frag_init(void) void ipv6_frag_exit(void) { - inet_frags_fini(&ip6_frags); ip6_frags_sysctl_unregister(); unregister_pernet_subsys(&ip6_frags_ops); inet6_del_protocol(&frag_protocol, IPPROTO_FRAGMENT); + inet_frags_fini(&ip6_frags); } diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 6370f07d9fe6ae8eaf6e21b8f0fec7944ed010e3..7a204db83013db2802fe6b070cf8c2dcc663db6c 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -4490,6 +4490,7 @@ static int ip6_route_multipath_add(struct fib6_config *cfg, */ cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL | NLM_F_REPLACE); + cfg->fc_nlinfo.nlh->nlmsg_flags |= NLM_F_CREATE; nhn++; } diff --git a/net/ipv6/seg6_local.c b/net/ipv6/seg6_local.c index 60325dbfe88b0ca8ffb08cc89666642c52739480..607709a8847cb112aac5410570202164cb4e6a53 100644 --- a/net/ipv6/seg6_local.c +++ b/net/ipv6/seg6_local.c @@ -28,6 +28,7 @@ #include #include #include +#include #ifdef CONFIG_IPV6_SEG6_HMAC #include #endif @@ -135,7 +136,8 @@ static bool decap_and_validate(struct sk_buff *skb, int proto) skb_reset_network_header(skb); skb_reset_transport_header(skb); - skb->encapsulation = 0; + if (iptunnel_pull_offloads(skb)) + return false; return true; } diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 5c5e752162469a87697904c35a3103900fdeefa3..c3060a30cf0398187144842025b8e89e08554a00 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -54,6 +54,7 @@ #include #include #include +#include #include "udp_impl.h" static bool udp6_lib_exact_dif_match(struct net *net, struct sk_buff *skb) @@ -538,9 +539,11 @@ static int __udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) int is_udplite = IS_UDPLITE(sk); /* Note that an ENOMEM error is charged twice */ - if (rc == -ENOMEM) + if (rc == -ENOMEM) { UDP6_INC_STATS(sock_net(sk), UDP_MIB_RCVBUFERRORS, is_udplite); + trace_udpv6_fail_rcv_buf_errors(skb); + } UDP6_INC_STATS(sock_net(sk), UDP_MIB_INERRORS, is_udplite); kfree_skb(skb); return -1; diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index f024914da1b2dca0815ebca9fbb09571902c6917..23a1002ed86dd90d9b8b5b389c0af606188ad9f4 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -13,6 +13,7 @@ #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt #include +#include #include #include #include @@ -355,6 +356,9 @@ static int afiucv_hs_send(struct iucv_message *imsg, struct sock *sock, err = -ENODEV; goto err_free; } + + dev_hard_header(skb, skb->dev, ETH_P_AF_IUCV, NULL, NULL, skb->len); + if (!(skb->dev->flags & IFF_UP) || !netif_carrier_ok(skb->dev)) { err = -ENETDOWN; goto err_free; @@ -367,6 +371,8 @@ static int afiucv_hs_send(struct iucv_message *imsg, struct sock *sock, skb_trim(skb, skb->dev->mtu); } skb->protocol = cpu_to_be16(ETH_P_AF_IUCV); + + __skb_header_release(skb); nskb = skb_clone(skb, GFP_ATOMIC); if (!nskb) { err = -ENOMEM; @@ -466,12 +472,14 @@ static void iucv_sever_path(struct sock *sk, int with_user_data) /* Send controlling flags through an IUCV socket for HIPER transport */ static int iucv_send_ctrl(struct sock *sk, u8 flags) { + struct iucv_sock *iucv = iucv_sk(sk); int err = 0; int blen; struct sk_buff *skb; u8 shutdown = 0; - blen = sizeof(struct af_iucv_trans_hdr) + ETH_HLEN; + blen = sizeof(struct af_iucv_trans_hdr) + + LL_RESERVED_SPACE(iucv->hs_dev); if (sk->sk_shutdown & SEND_SHUTDOWN) { /* controlling flags should be sent anyway */ shutdown = sk->sk_shutdown; @@ -1131,7 +1139,8 @@ static int iucv_sock_sendmsg(struct socket *sock, struct msghdr *msg, * segmented records using the MSG_EOR flag), but * for SOCK_STREAM we might want to improve it in future */ if (iucv->transport == AF_IUCV_TRANS_HIPER) { - headroom = sizeof(struct af_iucv_trans_hdr) + ETH_HLEN; + headroom = sizeof(struct af_iucv_trans_hdr) + + LL_RESERVED_SPACE(iucv->hs_dev); linear = len; } else { if (len < PAGE_SIZE) { @@ -2456,6 +2465,13 @@ static int afiucv_iucv_init(void) return err; } +static void afiucv_iucv_exit(void) +{ + device_unregister(af_iucv_dev); + driver_unregister(&af_iucv_driver); + pr_iucv->iucv_unregister(&af_iucv_handler, 0); +} + static int __init afiucv_init(void) { int err; @@ -2489,11 +2505,18 @@ static int __init afiucv_init(void) err = afiucv_iucv_init(); if (err) goto out_sock; - } else - register_netdevice_notifier(&afiucv_netdev_notifier); + } + + err = register_netdevice_notifier(&afiucv_netdev_notifier); + if (err) + goto out_notifier; + dev_add_pack(&iucv_packet_type); return 0; +out_notifier: + if (pr_iucv) + afiucv_iucv_exit(); out_sock: sock_unregister(PF_IUCV); out_proto: @@ -2507,12 +2530,11 @@ static int __init afiucv_init(void) static void __exit afiucv_exit(void) { if (pr_iucv) { - device_unregister(af_iucv_dev); - driver_unregister(&af_iucv_driver); - pr_iucv->iucv_unregister(&af_iucv_handler, 0); + afiucv_iucv_exit(); symbol_put(iucv_if); - } else - unregister_netdevice_notifier(&afiucv_netdev_notifier); + } + + unregister_netdevice_notifier(&afiucv_netdev_notifier); dev_remove_pack(&iucv_packet_type); sock_unregister(PF_IUCV); proto_unregister(&iucv_proto); diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index 52b5a2797c0c6e85e0cd2f8203616b536b86d178..d0a295cd71ef184ed638d8a7591b1dc0dc461853 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -325,8 +325,13 @@ int l2tp_session_register(struct l2tp_session *session, spin_lock_bh(&pn->l2tp_session_hlist_lock); + /* IP encap expects session IDs to be globally unique, while + * UDP encap doesn't. + */ hlist_for_each_entry(session_walk, g_head, global_hlist) - if (session_walk->session_id == session->session_id) { + if (session_walk->session_id == session->session_id && + (session_walk->tunnel->encap == L2TP_ENCAPTYPE_IP || + tunnel->encap == L2TP_ENCAPTYPE_IP)) { err = -EEXIST; goto err_tlock_pnlock; } @@ -1735,7 +1740,8 @@ static __net_exit void l2tp_exit_net(struct net *net) } rcu_read_unlock_bh(); - flush_workqueue(l2tp_wq); + if (l2tp_wq) + flush_workqueue(l2tp_wq); rcu_barrier(); for (hash = 0; hash < L2TP_HASH_SIZE_2; hash++) diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c index b99e73a7e7e0f2b4959b279e3aecbadf29667d55..ce841d59bc72a9bc90fb4143029b27178f4063c5 100644 --- a/net/llc/af_llc.c +++ b/net/llc/af_llc.c @@ -113,22 +113,26 @@ static inline u8 llc_ui_header_len(struct sock *sk, struct sockaddr_llc *addr) * * Send data via reliable llc2 connection. * Returns 0 upon success, non-zero if action did not succeed. + * + * This function always consumes a reference to the skb. */ static int llc_ui_send_data(struct sock* sk, struct sk_buff *skb, int noblock) { struct llc_sock* llc = llc_sk(sk); - int rc = 0; if (unlikely(llc_data_accept_state(llc->state) || llc->remote_busy_flag || llc->p_flag)) { long timeout = sock_sndtimeo(sk, noblock); + int rc; rc = llc_ui_wait_for_busy_core(sk, timeout); + if (rc) { + kfree_skb(skb); + return rc; + } } - if (unlikely(!rc)) - rc = llc_build_and_send_pkt(sk, skb); - return rc; + return llc_build_and_send_pkt(sk, skb); } static void llc_ui_sk_init(struct socket *sock, struct sock *sk) @@ -900,7 +904,7 @@ static int llc_ui_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) DECLARE_SOCKADDR(struct sockaddr_llc *, addr, msg->msg_name); int flags = msg->msg_flags; int noblock = flags & MSG_DONTWAIT; - struct sk_buff *skb; + struct sk_buff *skb = NULL; size_t size = 0; int rc = -EINVAL, copied = 0, hdrlen; @@ -909,10 +913,10 @@ static int llc_ui_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) lock_sock(sk); if (addr) { if (msg->msg_namelen < sizeof(*addr)) - goto release; + goto out; } else { if (llc_ui_addr_null(&llc->addr)) - goto release; + goto out; addr = &llc->addr; } /* must bind connection to sap if user hasn't done it. */ @@ -920,7 +924,7 @@ static int llc_ui_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) /* bind to sap with null dev, exclusive. */ rc = llc_ui_autobind(sock, addr); if (rc) - goto release; + goto out; } hdrlen = llc->dev->hard_header_len + llc_ui_header_len(sk, addr); size = hdrlen + len; @@ -929,12 +933,12 @@ static int llc_ui_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) copied = size - hdrlen; rc = -EINVAL; if (copied < 0) - goto release; + goto out; release_sock(sk); skb = sock_alloc_send_skb(sk, size, noblock, &rc); lock_sock(sk); if (!skb) - goto release; + goto out; skb->dev = llc->dev; skb->protocol = llc_proto_type(addr->sllc_arphrd); skb_reserve(skb, hdrlen); @@ -944,29 +948,31 @@ static int llc_ui_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) if (sk->sk_type == SOCK_DGRAM || addr->sllc_ua) { llc_build_and_send_ui_pkt(llc->sap, skb, addr->sllc_mac, addr->sllc_sap); + skb = NULL; goto out; } if (addr->sllc_test) { llc_build_and_send_test_pkt(llc->sap, skb, addr->sllc_mac, addr->sllc_sap); + skb = NULL; goto out; } if (addr->sllc_xid) { llc_build_and_send_xid_pkt(llc->sap, skb, addr->sllc_mac, addr->sllc_sap); + skb = NULL; goto out; } rc = -ENOPROTOOPT; if (!(sk->sk_type == SOCK_STREAM && !addr->sllc_ua)) goto out; rc = llc_ui_send_data(sk, skb, noblock); + skb = NULL; out: - if (rc) { - kfree_skb(skb); -release: + kfree_skb(skb); + if (rc) dprintk("%s: failed sending from %02X to %02X: %d\n", __func__, llc->laddr.lsap, llc->daddr.lsap, rc); - } release_sock(sk); return rc ? : copied; } diff --git a/net/llc/llc_conn.c b/net/llc/llc_conn.c index ed2aca12460ca870d8b7b38b8a9aff0e923cf7c5..a79b739eb223668aa837ac08bbcebc0ae5750a0a 100644 --- a/net/llc/llc_conn.c +++ b/net/llc/llc_conn.c @@ -55,6 +55,8 @@ int sysctl_llc2_busy_timeout = LLC2_BUSY_TIME * HZ; * (executing it's actions and changing state), upper layer will be * indicated or confirmed, if needed. Returns 0 for success, 1 for * failure. The socket lock has to be held before calling this function. + * + * This function always consumes a reference to the skb. */ int llc_conn_state_process(struct sock *sk, struct sk_buff *skb) { @@ -62,12 +64,6 @@ int llc_conn_state_process(struct sock *sk, struct sk_buff *skb) struct llc_sock *llc = llc_sk(skb->sk); struct llc_conn_state_ev *ev = llc_conn_ev(skb); - /* - * We have to hold the skb, because llc_conn_service will kfree it in - * the sending path and we need to look at the skb->cb, where we encode - * llc_conn_state_ev. - */ - skb_get(skb); ev->ind_prim = ev->cfm_prim = 0; /* * Send event to state machine @@ -75,21 +71,12 @@ int llc_conn_state_process(struct sock *sk, struct sk_buff *skb) rc = llc_conn_service(skb->sk, skb); if (unlikely(rc != 0)) { printk(KERN_ERR "%s: llc_conn_service failed\n", __func__); - goto out_kfree_skb; - } - - if (unlikely(!ev->ind_prim && !ev->cfm_prim)) { - /* indicate or confirm not required */ - if (!skb->next) - goto out_kfree_skb; goto out_skb_put; } - if (unlikely(ev->ind_prim && ev->cfm_prim)) /* Paranoia */ - skb_get(skb); - switch (ev->ind_prim) { case LLC_DATA_PRIM: + skb_get(skb); llc_save_primitive(sk, skb, LLC_DATA_PRIM); if (unlikely(sock_queue_rcv_skb(sk, skb))) { /* @@ -106,6 +93,7 @@ int llc_conn_state_process(struct sock *sk, struct sk_buff *skb) * skb->sk pointing to the newly created struct sock in * llc_conn_handler. -acme */ + skb_get(skb); skb_queue_tail(&sk->sk_receive_queue, skb); sk->sk_state_change(sk); break; @@ -121,7 +109,6 @@ int llc_conn_state_process(struct sock *sk, struct sk_buff *skb) sk->sk_state_change(sk); } } - kfree_skb(skb); sock_put(sk); break; case LLC_RESET_PRIM: @@ -130,14 +117,11 @@ int llc_conn_state_process(struct sock *sk, struct sk_buff *skb) * RESET is not being notified to upper layers for now */ printk(KERN_INFO "%s: received a reset ind!\n", __func__); - kfree_skb(skb); break; default: - if (ev->ind_prim) { + if (ev->ind_prim) printk(KERN_INFO "%s: received unknown %d prim!\n", __func__, ev->ind_prim); - kfree_skb(skb); - } /* No indication */ break; } @@ -179,15 +163,12 @@ int llc_conn_state_process(struct sock *sk, struct sk_buff *skb) printk(KERN_INFO "%s: received a reset conf!\n", __func__); break; default: - if (ev->cfm_prim) { + if (ev->cfm_prim) printk(KERN_INFO "%s: received unknown %d prim!\n", __func__, ev->cfm_prim); - break; - } - goto out_skb_put; /* No confirmation */ + /* No confirmation */ + break; } -out_kfree_skb: - kfree_skb(skb); out_skb_put: kfree_skb(skb); return rc; diff --git a/net/llc/llc_if.c b/net/llc/llc_if.c index 8db03c2d5440b12536b3b7016bc5c01b56666d8a..ad6547736c219dafe42c2014257c0c87f372934f 100644 --- a/net/llc/llc_if.c +++ b/net/llc/llc_if.c @@ -38,6 +38,8 @@ * closed and -EBUSY when sending data is not permitted in this state or * LLC has send an I pdu with p bit set to 1 and is waiting for it's * response. + * + * This function always consumes a reference to the skb. */ int llc_build_and_send_pkt(struct sock *sk, struct sk_buff *skb) { @@ -46,20 +48,22 @@ int llc_build_and_send_pkt(struct sock *sk, struct sk_buff *skb) struct llc_sock *llc = llc_sk(sk); if (unlikely(llc->state == LLC_CONN_STATE_ADM)) - goto out; + goto out_free; rc = -EBUSY; if (unlikely(llc_data_accept_state(llc->state) || /* data_conn_refuse */ llc->p_flag)) { llc->failed_data_req = 1; - goto out; + goto out_free; } ev = llc_conn_ev(skb); ev->type = LLC_CONN_EV_TYPE_PRIM; ev->prim = LLC_DATA_PRIM; ev->prim_type = LLC_PRIM_TYPE_REQ; skb->dev = llc->dev; - rc = llc_conn_state_process(sk, skb); -out: + return llc_conn_state_process(sk, skb); + +out_free: + kfree_skb(skb); return rc; } diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index a48e83b19cfa7b0250bfd38ca6ed0fce4351d0bd..cb7076d9a76986456b805519216c3d112227c285 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1096,50 +1096,6 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) return 0; } -/* Layer 2 Update frame (802.2 Type 1 LLC XID Update response) */ -struct iapp_layer2_update { - u8 da[ETH_ALEN]; /* broadcast */ - u8 sa[ETH_ALEN]; /* STA addr */ - __be16 len; /* 6 */ - u8 dsap; /* 0 */ - u8 ssap; /* 0 */ - u8 control; - u8 xid_info[3]; -} __packed; - -static void ieee80211_send_layer2_update(struct sta_info *sta) -{ - struct iapp_layer2_update *msg; - struct sk_buff *skb; - - /* Send Level 2 Update Frame to update forwarding tables in layer 2 - * bridge devices */ - - skb = dev_alloc_skb(sizeof(*msg)); - if (!skb) - return; - msg = skb_put(skb, sizeof(*msg)); - - /* 802.2 Type 1 Logical Link Control (LLC) Exchange Identifier (XID) - * Update response frame; IEEE Std 802.2-1998, 5.4.1.2.1 */ - - eth_broadcast_addr(msg->da); - memcpy(msg->sa, sta->sta.addr, ETH_ALEN); - msg->len = htons(6); - msg->dsap = 0; - msg->ssap = 0x01; /* NULL LSAP, CR Bit: Response */ - msg->control = 0xaf; /* XID response lsb.1111F101. - * F=0 (no poll command; unsolicited frame) */ - msg->xid_info[0] = 0x81; /* XID format identifier */ - msg->xid_info[1] = 1; /* LLC types/classes: Type 1 LLC */ - msg->xid_info[2] = 0; /* XID sender's receive window size (RW) */ - - skb->dev = sta->sdata->dev; - skb->protocol = eth_type_trans(skb, sta->sdata->dev); - memset(skb->cb, 0, sizeof(skb->cb)); - netif_rx_ni(skb); -} - static int sta_apply_auth_flags(struct ieee80211_local *local, struct sta_info *sta, u32 mask, u32 set) @@ -1454,7 +1410,6 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, struct sta_info *sta; struct ieee80211_sub_if_data *sdata; int err; - int layer2_update; if (params->vlan) { sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); @@ -1498,18 +1453,12 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, test_sta_flag(sta, WLAN_STA_ASSOC)) rate_control_rate_init(sta); - layer2_update = sdata->vif.type == NL80211_IFTYPE_AP_VLAN || - sdata->vif.type == NL80211_IFTYPE_AP; - err = sta_info_insert_rcu(sta); if (err) { rcu_read_unlock(); return err; } - if (layer2_update) - ieee80211_send_layer2_update(sta); - rcu_read_unlock(); return 0; @@ -1607,10 +1556,11 @@ static int ieee80211_change_station(struct wiphy *wiphy, sta->sdata = vlansdata; ieee80211_check_fast_xmit(sta); - if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) + if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) { ieee80211_vif_inc_num_mcast(sta->sdata); - - ieee80211_send_layer2_update(sta); + cfg80211_send_layer2_update(sta->sdata->dev, + sta->sta.addr); + } } err = sta_apply_parameters(local, sta, params); @@ -2875,6 +2825,28 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy, return err; } +static void ieee80211_end_cac(struct wiphy *wiphy, + struct net_device *dev) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = sdata->local; + + mutex_lock(&local->mtx); + list_for_each_entry(sdata, &local->interfaces, list) { + /* it might be waiting for the local->mtx, but then + * by the time it gets it, sdata->wdev.cac_started + * will no longer be true + */ + cancel_delayed_work(&sdata->dfs_cac_timer_work); + + if (sdata->wdev.cac_started) { + ieee80211_vif_release_channel(sdata); + sdata->wdev.cac_started = false; + } + } + mutex_unlock(&local->mtx); +} + static struct cfg80211_beacon_data * cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon) { @@ -3898,6 +3870,7 @@ const struct cfg80211_ops mac80211_config_ops = { #endif .get_channel = ieee80211_cfg_get_channel, .start_radar_detection = ieee80211_start_radar_detection, + .end_cac = ieee80211_end_cac, .channel_switch = ieee80211_channel_switch, .set_qos_map = ieee80211_set_qos_map, .set_ap_chanwidth = ieee80211_set_ap_chanwidth, diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 6950cd0bf5940a0bc76ea0f3bc283c4a1cac7963..740dc9fa127cdb93ba4c60fb465eeb71c51eabc4 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -326,6 +326,9 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local, unsigned long fail_avg = ewma_mesh_fail_avg_read(&sta->mesh->fail_avg); + if (sta->mesh->plink_state != NL80211_PLINK_ESTAB) + return MAX_METRIC; + /* Try to get rate based on HW/SW RC algorithm. * Rate is returned in units of Kbps, correct this * to comply with airtime calculation units diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index 3d5520776655d1798e20976caaaf752b0b5080d9..0b60e330c115b64a646bb4786280e1677b619efc 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -529,7 +529,7 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) /* (re)Initialize group rate indexes */ for(j = 0; j < MAX_THR_RATES; j++) - tmp_group_tp_rate[j] = group; + tmp_group_tp_rate[j] = MCS_GROUP_RATES * group; for (i = 0; i < MCS_GROUP_RATES; i++) { if (!(mi->supported[group] & BIT(i))) diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index b12f23c996f4e5e0584b8046d14a460a0335bdfa..02d0b22d01141f69a611be9bdfc5bdc8fc3d8d0a 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -3391,9 +3391,18 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx) case cpu_to_le16(IEEE80211_STYPE_PROBE_RESP): /* process for all: mesh, mlme, ibss */ break; + case cpu_to_le16(IEEE80211_STYPE_DEAUTH): + if (is_multicast_ether_addr(mgmt->da) && + !is_broadcast_ether_addr(mgmt->da)) + return RX_DROP_MONITOR; + + /* process only for station/IBSS */ + if (sdata->vif.type != NL80211_IFTYPE_STATION && + sdata->vif.type != NL80211_IFTYPE_ADHOC) + return RX_DROP_MONITOR; + break; case cpu_to_le16(IEEE80211_STYPE_ASSOC_RESP): case cpu_to_le16(IEEE80211_STYPE_REASSOC_RESP): - case cpu_to_le16(IEEE80211_STYPE_DEAUTH): case cpu_to_le16(IEEE80211_STYPE_DISASSOC): if (is_multicast_ether_addr(mgmt->da) && !is_broadcast_ether_addr(mgmt->da)) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 507409e3fd39ce2a722877cd7d720608c6445c55..0c80a59ef914e703b8f133a1bc789be73fee7011 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1906,6 +1906,10 @@ int sta_info_move_state(struct sta_info *sta, ieee80211_check_fast_xmit(sta); ieee80211_check_fast_rx(sta); } + if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN || + sta->sdata->vif.type == NL80211_IFTYPE_AP) + cfg80211_send_layer2_update(sta->sdata->dev, + sta->sta.addr); break; default: break; diff --git a/net/mac80211/tkip.c b/net/mac80211/tkip.c index b3622823bad23b9a34a58b09fea1dd3e067bbcf2..ebd66e8f46b3f81b017719a99066c14e3f1fdebe 100644 --- a/net/mac80211/tkip.c +++ b/net/mac80211/tkip.c @@ -266,9 +266,21 @@ int ieee80211_tkip_decrypt_data(struct crypto_cipher *tfm, if ((keyid >> 6) != key->conf.keyidx) return TKIP_DECRYPT_INVALID_KEYIDX; - if (rx_ctx->ctx.state != TKIP_STATE_NOT_INIT && - (iv32 < rx_ctx->iv32 || - (iv32 == rx_ctx->iv32 && iv16 <= rx_ctx->iv16))) + /* Reject replays if the received TSC is smaller than or equal to the + * last received value in a valid message, but with an exception for + * the case where a new key has been set and no valid frame using that + * key has yet received and the local RSC was initialized to 0. This + * exception allows the very first frame sent by the transmitter to be + * accepted even if that transmitter were to use TSC 0 (IEEE 802.11 + * described TSC to be initialized to 1 whenever a new key is taken into + * use). + */ + if (iv32 < rx_ctx->iv32 || + (iv32 == rx_ctx->iv32 && + (iv16 < rx_ctx->iv16 || + (iv16 == rx_ctx->iv16 && + (rx_ctx->iv32 || rx_ctx->iv16 || + rx_ctx->ctx.state != TKIP_STATE_NOT_INIT))))) return TKIP_DECRYPT_REPLAY; if (only_iv) { diff --git a/net/mac80211/util.c b/net/mac80211/util.c index f101a6460b44b07e09a2328423aed5535d978913..7fa9871b1db9fe325dea47bc81a15418ea1cbb81 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -945,16 +945,22 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, elem_parse_failed = true; break; case WLAN_EID_VHT_OPERATION: - if (elen >= sizeof(struct ieee80211_vht_operation)) + if (elen >= sizeof(struct ieee80211_vht_operation)) { elems->vht_operation = (void *)pos; - else - elem_parse_failed = true; + if (calc_crc) + crc = crc32_be(crc, pos - 2, elen + 2); + break; + } + elem_parse_failed = true; break; case WLAN_EID_OPMODE_NOTIF: - if (elen > 0) + if (elen > 0) { elems->opmode_notif = pos; - else - elem_parse_failed = true; + if (calc_crc) + crc = crc32_be(crc, pos - 2, elen + 2); + break; + } + elem_parse_failed = true; break; case WLAN_EID_MESH_ID: elems->mesh_id = pos; diff --git a/net/mpls/mpls_iptunnel.c b/net/mpls/mpls_iptunnel.c index 94f53a9b7d1ae67edaf2ec0bfba87e4ab1392ee5..faf6ef1b6a45407dbdac07c460fa527d6a8b859c 100644 --- a/net/mpls/mpls_iptunnel.c +++ b/net/mpls/mpls_iptunnel.c @@ -28,7 +28,7 @@ #include "internal.h" static const struct nla_policy mpls_iptunnel_policy[MPLS_IPTUNNEL_MAX + 1] = { - [MPLS_IPTUNNEL_DST] = { .type = NLA_U32 }, + [MPLS_IPTUNNEL_DST] = { .len = sizeof(u32) }, [MPLS_IPTUNNEL_TTL] = { .type = NLA_U8 }, }; diff --git a/net/netfilter/ipset/ip_set_bitmap_gen.h b/net/netfilter/ipset/ip_set_bitmap_gen.h index 257ca393e6f222f5bb881b547a4a53281d26379f..37f68062be41067920c32386b5679b1c38b951ad 100644 --- a/net/netfilter/ipset/ip_set_bitmap_gen.h +++ b/net/netfilter/ipset/ip_set_bitmap_gen.h @@ -64,9 +64,9 @@ mtype_destroy(struct ip_set *set) if (SET_WITH_TIMEOUT(set)) del_timer_sync(&map->gc); - ip_set_free(map->members); if (set->dsize && set->extensions & IPSET_EXT_DESTROY) mtype_ext_cleanup(set); + ip_set_free(map->members); ip_set_free(map); set->data = NULL; @@ -79,7 +79,7 @@ mtype_flush(struct ip_set *set) if (set->extensions & IPSET_EXT_DESTROY) mtype_ext_cleanup(set); - memset(map->members, 0, map->memsize); + bitmap_zero(map->members, map->elements); set->elements = 0; set->ext_size = 0; } diff --git a/net/netfilter/ipset/ip_set_bitmap_ip.c b/net/netfilter/ipset/ip_set_bitmap_ip.c index 488d6d05c65ce93a1360d5c623eb48d32a8cfbad..e3257077158f8449d97c99fb79830326f691bd58 100644 --- a/net/netfilter/ipset/ip_set_bitmap_ip.c +++ b/net/netfilter/ipset/ip_set_bitmap_ip.c @@ -40,7 +40,7 @@ MODULE_ALIAS("ip_set_bitmap:ip"); /* Type structure */ struct bitmap_ip { - void *members; /* the set members */ + unsigned long *members; /* the set members */ u32 first_ip; /* host byte order, included in range */ u32 last_ip; /* host byte order, included in range */ u32 elements; /* number of max elements in the set */ @@ -223,7 +223,7 @@ init_map_ip(struct ip_set *set, struct bitmap_ip *map, u32 first_ip, u32 last_ip, u32 elements, u32 hosts, u8 netmask) { - map->members = ip_set_alloc(map->memsize); + map->members = bitmap_zalloc(elements, GFP_KERNEL | __GFP_NOWARN); if (!map->members) return false; map->first_ip = first_ip; @@ -313,7 +313,7 @@ bitmap_ip_create(struct net *net, struct ip_set *set, struct nlattr *tb[], if (!map) return -ENOMEM; - map->memsize = bitmap_bytes(0, elements - 1); + map->memsize = BITS_TO_LONGS(elements) * sizeof(unsigned long); set->variant = &bitmap_ip; if (!init_map_ip(set, map, first_ip, last_ip, elements, hosts, netmask)) { diff --git a/net/netfilter/ipset/ip_set_bitmap_ipmac.c b/net/netfilter/ipset/ip_set_bitmap_ipmac.c index 794e0335a8648f3a856b700f126739932e84610e..9669cace4522a098fe8138321b5b348195acc509 100644 --- a/net/netfilter/ipset/ip_set_bitmap_ipmac.c +++ b/net/netfilter/ipset/ip_set_bitmap_ipmac.c @@ -46,7 +46,7 @@ enum { /* Type structure */ struct bitmap_ipmac { - void *members; /* the set members */ + unsigned long *members; /* the set members */ u32 first_ip; /* host byte order, included in range */ u32 last_ip; /* host byte order, included in range */ u32 elements; /* number of max elements in the set */ @@ -303,7 +303,7 @@ static bool init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map, u32 first_ip, u32 last_ip, u32 elements) { - map->members = ip_set_alloc(map->memsize); + map->members = bitmap_zalloc(elements, GFP_KERNEL | __GFP_NOWARN); if (!map->members) return false; map->first_ip = first_ip; @@ -364,7 +364,7 @@ bitmap_ipmac_create(struct net *net, struct ip_set *set, struct nlattr *tb[], if (!map) return -ENOMEM; - map->memsize = bitmap_bytes(0, elements - 1); + map->memsize = BITS_TO_LONGS(elements) * sizeof(unsigned long); set->variant = &bitmap_ipmac; if (!init_map_ipmac(set, map, first_ip, last_ip, elements)) { kfree(map); diff --git a/net/netfilter/ipset/ip_set_bitmap_port.c b/net/netfilter/ipset/ip_set_bitmap_port.c index b561ca8b3659102810d5979beafab316dc15e657..ae09f2af6a198e1a66f99235ee606d797abf82d3 100644 --- a/net/netfilter/ipset/ip_set_bitmap_port.c +++ b/net/netfilter/ipset/ip_set_bitmap_port.c @@ -34,7 +34,7 @@ MODULE_ALIAS("ip_set_bitmap:port"); /* Type structure */ struct bitmap_port { - void *members; /* the set members */ + unsigned long *members; /* the set members */ u16 first_port; /* host byte order, included in range */ u16 last_port; /* host byte order, included in range */ u32 elements; /* number of max elements in the set */ @@ -208,7 +208,7 @@ static bool init_map_port(struct ip_set *set, struct bitmap_port *map, u16 first_port, u16 last_port) { - map->members = ip_set_alloc(map->memsize); + map->members = bitmap_zalloc(map->elements, GFP_KERNEL | __GFP_NOWARN); if (!map->members) return false; map->first_port = first_port; @@ -248,7 +248,7 @@ bitmap_port_create(struct net *net, struct ip_set *set, struct nlattr *tb[], return -ENOMEM; map->elements = elements; - map->memsize = bitmap_bytes(0, map->elements); + map->memsize = BITS_TO_LONGS(elements) * sizeof(unsigned long); set->variant = &bitmap_port; if (!init_map_port(set, map, first_port, last_port)) { kfree(map); diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 1566261b6b5a58bf6a1a74bbb3b5bd4a0c3f1f97..21eb53f6d4fe32224c045a3814a1072247d5b72a 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -1666,6 +1666,7 @@ static int ip_set_utest(struct net *net, struct sock *ctnl, struct sk_buff *skb, struct ip_set *set; struct nlattr *tb[IPSET_ATTR_ADT_MAX + 1] = {}; int ret = 0; + u32 lineno; if (unlikely(protocol_failed(attr) || !attr[IPSET_ATTR_SETNAME] || @@ -1682,7 +1683,7 @@ static int ip_set_utest(struct net *net, struct sock *ctnl, struct sk_buff *skb, return -IPSET_ERR_PROTOCOL; rcu_read_lock_bh(); - ret = set->variant->uadt(set, tb, IPSET_TEST, NULL, 0, 0); + ret = set->variant->uadt(set, tb, IPSET_TEST, &lineno, 0, 0); rcu_read_unlock_bh(); /* Userspace can't trigger element to be re-added */ if (ret == -EAGAIN) diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 7ba9ea55816a6e9449d08e69148d93211a158bd4..31fa94064a620976c30f80517239d3e14ecde21e 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -555,10 +555,8 @@ ctnetlink_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, goto nla_put_failure; if (ctnetlink_dump_status(skb, ct) < 0 || - ctnetlink_dump_timeout(skb, ct) < 0 || ctnetlink_dump_acct(skb, ct, type) < 0 || ctnetlink_dump_timestamp(skb, ct) < 0 || - ctnetlink_dump_protoinfo(skb, ct) < 0 || ctnetlink_dump_helpinfo(skb, ct) < 0 || ctnetlink_dump_mark(skb, ct) < 0 || ctnetlink_dump_secctx(skb, ct) < 0 || @@ -570,6 +568,11 @@ ctnetlink_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, ctnetlink_dump_ct_synproxy(skb, ct) < 0) goto nla_put_failure; + if (!test_bit(IPS_OFFLOAD_BIT, &ct->status) && + (ctnetlink_dump_timeout(skb, ct) < 0 || + ctnetlink_dump_protoinfo(skb, ct) < 0)) + goto nla_put_failure; + nlmsg_end(skb, nlh); return skb->len; diff --git a/net/netfilter/nf_conntrack_proto_dccp.c b/net/netfilter/nf_conntrack_proto_dccp.c index f3f91ed2c21adce5dcc5e9ba06d6a843a6af39bb..e24c436d913e8a9c16a13397ae74d16cd0c68bb5 100644 --- a/net/netfilter/nf_conntrack_proto_dccp.c +++ b/net/netfilter/nf_conntrack_proto_dccp.c @@ -687,6 +687,9 @@ static int dccp_timeout_nlattr_to_obj(struct nlattr *tb[], unsigned int *timeouts = data; int i; + if (!timeouts) + timeouts = dn->dccp_timeout; + /* set default DCCP timeouts. */ for (i=0; idccp_timeout[i]; diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c index e4d738d34cd030fe5b2cd1629d436e3fa0fa4557..7d7e30ea0ecf9d5ace089cc6d16737406554ffa0 100644 --- a/net/netfilter/nf_conntrack_proto_sctp.c +++ b/net/netfilter/nf_conntrack_proto_sctp.c @@ -603,6 +603,9 @@ static int sctp_timeout_nlattr_to_obj(struct nlattr *tb[], struct nf_sctp_net *sn = sctp_pernet(net); int i; + if (!timeouts) + timeouts = sn->timeouts; + /* set default SCTP timeouts. */ for (i=0; itimeouts[i]; diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c index 70bd730ca059788b8c9e30aeb9b1778dff56d2a4..890799c16aa414cb7d27bb168b21e718a1ad8fd6 100644 --- a/net/netfilter/nf_flow_table_core.c +++ b/net/netfilter/nf_flow_table_core.c @@ -491,14 +491,17 @@ EXPORT_SYMBOL_GPL(nf_flow_table_init); static void nf_flow_table_do_cleanup(struct flow_offload *flow, void *data) { struct net_device *dev = data; + struct flow_offload_entry *e; + + e = container_of(flow, struct flow_offload_entry, flow); if (!dev) { flow_offload_teardown(flow); return; } - - if (flow->tuplehash[0].tuple.iifidx == dev->ifindex || - flow->tuplehash[1].tuple.iifidx == dev->ifindex) + if (net_eq(nf_ct_net(e->ct), dev_net(dev)) && + (flow->tuplehash[0].tuple.iifidx == dev->ifindex || + flow->tuplehash[1].tuple.iifidx == dev->ifindex)) flow_offload_dead(flow); } diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 4711a8b56f32f8a309357c337c27fdce12f9e44f..5881f66688171dee4a5bb4a039a1a28f9cea59f6 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -24,6 +24,8 @@ #include #include +#define NFT_MODULE_AUTOLOAD_LIMIT (MODULE_NAME_LEN - sizeof("nft-expr-255-")) + static LIST_HEAD(nf_tables_expressions); static LIST_HEAD(nf_tables_objects); static LIST_HEAD(nf_tables_flowtables); @@ -469,47 +471,61 @@ static inline u64 nf_tables_alloc_handle(struct nft_table *table) static const struct nft_chain_type *chain_type[NFPROTO_NUMPROTO][NFT_CHAIN_T_MAX]; +static const struct nft_chain_type * +__nft_chain_type_get(u8 family, enum nft_chain_types type) +{ + if (family >= NFPROTO_NUMPROTO || + type >= NFT_CHAIN_T_MAX) + return NULL; + + return chain_type[family][type]; +} + static const struct nft_chain_type * __nf_tables_chain_type_lookup(const struct nlattr *nla, u8 family) { + const struct nft_chain_type *type; int i; for (i = 0; i < NFT_CHAIN_T_MAX; i++) { - if (chain_type[family][i] != NULL && - !nla_strcmp(nla, chain_type[family][i]->name)) - return chain_type[family][i]; + type = __nft_chain_type_get(family, i); + if (!type) + continue; + if (!nla_strcmp(nla, type->name)) + return type; } return NULL; } /* - * Loading a module requires dropping mutex that guards the - * transaction. - * We first need to abort any pending transactions as once - * mutex is unlocked a different client could start a new - * transaction. It must not see any 'future generation' - * changes * as these changes will never happen. + * Loading a module requires dropping mutex that guards the transaction. + * A different client might race to start a new transaction meanwhile. Zap the + * list of pending transaction and then restore it once the mutex is grabbed + * again. Users of this function return EAGAIN which implicitly triggers the + * transaction abort path to clean up the list of pending transactions. */ #ifdef CONFIG_MODULES -static int __nf_tables_abort(struct net *net); - static void nft_request_module(struct net *net, const char *fmt, ...) { char module_name[MODULE_NAME_LEN]; + LIST_HEAD(commit_list); va_list args; int ret; - __nf_tables_abort(net); + list_splice_init(&net->nft.commit_list, &commit_list); va_start(args, fmt); ret = vsnprintf(module_name, MODULE_NAME_LEN, fmt, args); va_end(args); - if (WARN(ret >= MODULE_NAME_LEN, "truncated: '%s' (len %d)", module_name, ret)) + if (ret >= MODULE_NAME_LEN) return; mutex_unlock(&net->nft.commit_mutex); request_module("%s", module_name); mutex_lock(&net->nft.commit_mutex); + + WARN_ON_ONCE(!list_empty(&net->nft.commit_list)); + list_splice(&commit_list, &net->nft.commit_list); } #endif @@ -933,12 +949,18 @@ static int nft_flush_table(struct nft_ctx *ctx) } list_for_each_entry_safe(flowtable, nft, &ctx->table->flowtables, list) { + if (!nft_is_active_next(ctx->net, flowtable)) + continue; + err = nft_delflowtable(ctx, flowtable); if (err < 0) goto out; } list_for_each_entry_safe(obj, ne, &ctx->table->objects, list) { + if (!nft_is_active_next(ctx->net, obj)) + continue; + err = nft_delobj(ctx, obj); if (err < 0) goto out; @@ -1041,11 +1063,8 @@ static void nf_tables_table_destroy(struct nft_ctx *ctx) void nft_register_chain_type(const struct nft_chain_type *ctype) { - if (WARN_ON(ctype->family >= NFPROTO_NUMPROTO)) - return; - nfnl_lock(NFNL_SUBSYS_NFTABLES); - if (WARN_ON(chain_type[ctype->family][ctype->type] != NULL)) { + if (WARN_ON(__nft_chain_type_get(ctype->family, ctype->type))) { nfnl_unlock(NFNL_SUBSYS_NFTABLES); return; } @@ -1129,7 +1148,8 @@ static const struct nla_policy nft_chain_policy[NFTA_CHAIN_MAX + 1] = { .len = NFT_CHAIN_MAXNAMELEN - 1 }, [NFTA_CHAIN_HOOK] = { .type = NLA_NESTED }, [NFTA_CHAIN_POLICY] = { .type = NLA_U32 }, - [NFTA_CHAIN_TYPE] = { .type = NLA_STRING }, + [NFTA_CHAIN_TYPE] = { .type = NLA_STRING, + .len = NFT_MODULE_AUTOLOAD_LIMIT }, [NFTA_CHAIN_COUNTERS] = { .type = NLA_NESTED }, }; @@ -1501,7 +1521,10 @@ static int nft_chain_parse_hook(struct net *net, hook->num = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM])); hook->priority = ntohl(nla_get_be32(ha[NFTA_HOOK_PRIORITY])); - type = chain_type[family][NFT_CHAIN_T_DEFAULT]; + type = __nft_chain_type_get(family, NFT_CHAIN_T_DEFAULT); + if (!type) + return -EOPNOTSUPP; + if (nla[NFTA_CHAIN_TYPE]) { type = nf_tables_chain_type_lookup(net, nla[NFTA_CHAIN_TYPE], family, autoload); @@ -2012,7 +2035,8 @@ static const struct nft_expr_type *nft_expr_type_get(struct net *net, } static const struct nla_policy nft_expr_policy[NFTA_EXPR_MAX + 1] = { - [NFTA_EXPR_NAME] = { .type = NLA_STRING }, + [NFTA_EXPR_NAME] = { .type = NLA_STRING, + .len = NFT_MODULE_AUTOLOAD_LIMIT }, [NFTA_EXPR_DATA] = { .type = NLA_NESTED }, }; @@ -3796,7 +3820,8 @@ static const struct nla_policy nft_set_elem_policy[NFTA_SET_ELEM_MAX + 1] = { [NFTA_SET_ELEM_USERDATA] = { .type = NLA_BINARY, .len = NFT_USERDATA_MAXLEN }, [NFTA_SET_ELEM_EXPR] = { .type = NLA_NESTED }, - [NFTA_SET_ELEM_OBJREF] = { .type = NLA_STRING }, + [NFTA_SET_ELEM_OBJREF] = { .type = NLA_STRING, + .len = NFT_OBJ_MAXNAMELEN - 1 }, }; static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX + 1] = { diff --git a/net/netfilter/nft_flow_offload.c b/net/netfilter/nft_flow_offload.c index 1ef8cb789c41a668bf02bfa0b6b44d758e360624..166edea0e45273b91d8a6cd6298c685de285bf86 100644 --- a/net/netfilter/nft_flow_offload.c +++ b/net/netfilter/nft_flow_offload.c @@ -103,8 +103,7 @@ static void nft_flow_offload_eval(const struct nft_expr *expr, ct->status & IPS_SEQ_ADJUST) goto out; - if (ctinfo == IP_CT_NEW || - ctinfo == IP_CT_RELATED) + if (!nf_ct_is_confirmed(ct)) goto out; if (test_and_set_bit(IPS_OFFLOAD_BIT, &ct->status)) diff --git a/net/netfilter/nft_osf.c b/net/netfilter/nft_osf.c index a35fb59ace7326324811a21704eb2932e2ca5a4c..a003533ff4d93a5cc9eb7c8eef006a5dfcb3e3e5 100644 --- a/net/netfilter/nft_osf.c +++ b/net/netfilter/nft_osf.c @@ -47,6 +47,9 @@ static int nft_osf_init(const struct nft_ctx *ctx, struct nft_osf *priv = nft_expr_priv(expr); int err; + if (!tb[NFTA_OSF_DREG]) + return -EINVAL; + priv->dreg = nft_parse_register(tb[NFTA_OSF_DREG]); err = nft_validate_register_store(ctx, priv->dreg, NULL, NFT_DATA_VALUE, NFT_OSF_MAXGENRELEN); @@ -69,6 +72,15 @@ static int nft_osf_dump(struct sk_buff *skb, const struct nft_expr *expr) return -1; } +static int nft_osf_validate(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nft_data **data) +{ + return nft_chain_validate_hooks(ctx->chain, (1 << NF_INET_LOCAL_IN) | + (1 << NF_INET_PRE_ROUTING) | + (1 << NF_INET_FORWARD)); +} + static struct nft_expr_type nft_osf_type; static const struct nft_expr_ops nft_osf_op = { .eval = nft_osf_eval, @@ -76,6 +88,7 @@ static const struct nft_expr_ops nft_osf_op = { .init = nft_osf_init, .dump = nft_osf_dump, .type = &nft_osf_type, + .validate = nft_osf_validate, }; static struct nft_expr_type nft_osf_type __read_mostly = { diff --git a/net/netfilter/nft_set_hash.c b/net/netfilter/nft_set_hash.c index 015124e649cbdf0fb658515cf7c7e2dbf550a972..05118e03c3e48c38b90814a833113ba2b6cf40c9 100644 --- a/net/netfilter/nft_set_hash.c +++ b/net/netfilter/nft_set_hash.c @@ -488,6 +488,23 @@ static bool nft_hash_lookup_fast(const struct net *net, return false; } +static u32 nft_jhash(const struct nft_set *set, const struct nft_hash *priv, + const struct nft_set_ext *ext) +{ + const struct nft_data *key = nft_set_ext_key(ext); + u32 hash, k1; + + if (set->klen == 4) { + k1 = *(u32 *)key; + hash = jhash_1word(k1, priv->seed); + } else { + hash = jhash(key, set->klen, priv->seed); + } + hash = reciprocal_scale(hash, priv->buckets); + + return hash; +} + static int nft_hash_insert(const struct net *net, const struct nft_set *set, const struct nft_set_elem *elem, struct nft_set_ext **ext) @@ -497,8 +514,7 @@ static int nft_hash_insert(const struct net *net, const struct nft_set *set, u8 genmask = nft_genmask_next(net); u32 hash; - hash = jhash(nft_set_ext_key(&this->ext), set->klen, priv->seed); - hash = reciprocal_scale(hash, priv->buckets); + hash = nft_jhash(set, priv, &this->ext); hlist_for_each_entry(he, &priv->table[hash], node) { if (!memcmp(nft_set_ext_key(&this->ext), nft_set_ext_key(&he->ext), set->klen) && @@ -537,10 +553,9 @@ static void *nft_hash_deactivate(const struct net *net, u8 genmask = nft_genmask_next(net); u32 hash; - hash = jhash(nft_set_ext_key(&this->ext), set->klen, priv->seed); - hash = reciprocal_scale(hash, priv->buckets); + hash = nft_jhash(set, priv, &this->ext); hlist_for_each_entry(he, &priv->table[hash], node) { - if (!memcmp(nft_set_ext_key(&this->ext), &elem->key.val, + if (!memcmp(nft_set_ext_key(&he->ext), &elem->key.val, set->klen) && nft_set_elem_active(&he->ext, genmask)) { nft_set_elem_change_active(net, set, &he->ext); diff --git a/net/netfilter/nft_tunnel.c b/net/netfilter/nft_tunnel.c index 3a15f219e4e7fa68ee434a9c9c2c4587755d25a3..1c6d15ea76d4a1d8321e5c9f2eec3f172ca9f247 100644 --- a/net/netfilter/nft_tunnel.c +++ b/net/netfilter/nft_tunnel.c @@ -56,7 +56,7 @@ static int nft_tunnel_get_init(const struct nft_ctx *ctx, struct nft_tunnel *priv = nft_expr_priv(expr); u32 len; - if (!tb[NFTA_TUNNEL_KEY] && + if (!tb[NFTA_TUNNEL_KEY] || !tb[NFTA_TUNNEL_DREG]) return -EINVAL; @@ -218,8 +218,9 @@ static int nft_tunnel_obj_vxlan_init(const struct nlattr *attr, } static const struct nla_policy nft_tunnel_opts_erspan_policy[NFTA_TUNNEL_KEY_ERSPAN_MAX + 1] = { + [NFTA_TUNNEL_KEY_ERSPAN_VERSION] = { .type = NLA_U32 }, [NFTA_TUNNEL_KEY_ERSPAN_V1_INDEX] = { .type = NLA_U32 }, - [NFTA_TUNNEL_KEY_ERSPAN_V2_DIR] = { .type = NLA_U8 }, + [NFTA_TUNNEL_KEY_ERSPAN_V2_DIR] = { .type = NLA_U8 }, [NFTA_TUNNEL_KEY_ERSPAN_V2_HWID] = { .type = NLA_U8 }, }; @@ -235,6 +236,9 @@ static int nft_tunnel_obj_erspan_init(const struct nlattr *attr, if (err < 0) return err; + if (!tb[NFTA_TUNNEL_KEY_ERSPAN_VERSION]) + return -EINVAL; + version = ntohl(nla_get_be32(tb[NFTA_TUNNEL_KEY_ERSPAN_VERSION])); switch (version) { case ERSPAN_VERSION: @@ -463,8 +467,8 @@ static int nft_tunnel_opts_dump(struct sk_buff *skb, static int nft_tunnel_ports_dump(struct sk_buff *skb, struct ip_tunnel_info *info) { - if (nla_put_be16(skb, NFTA_TUNNEL_KEY_SPORT, htons(info->key.tp_src)) < 0 || - nla_put_be16(skb, NFTA_TUNNEL_KEY_DPORT, htons(info->key.tp_dst)) < 0) + if (nla_put_be16(skb, NFTA_TUNNEL_KEY_SPORT, info->key.tp_src) < 0 || + nla_put_be16(skb, NFTA_TUNNEL_KEY_DPORT, info->key.tp_dst) < 0) return -1; return 0; diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c index 1ad4017f9b7349849d845c9e5bb9a5592533a291..0c2dc6def86daad6ea1d8b5999e9fcda48490aed 100644 --- a/net/netfilter/xt_hashlimit.c +++ b/net/netfilter/xt_hashlimit.c @@ -845,6 +845,8 @@ hashlimit_mt(const struct sk_buff *skb, struct xt_action_param *par) return hashlimit_mt_common(skb, par, hinfo, &info->cfg, 3); } +#define HASHLIMIT_MAX_SIZE 1048576 + static int hashlimit_mt_check_common(const struct xt_mtchk_param *par, struct xt_hashlimit_htable **hinfo, struct hashlimit_cfg3 *cfg, @@ -855,6 +857,14 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par, if (cfg->gc_interval == 0 || cfg->expire == 0) return -EINVAL; + if (cfg->size > HASHLIMIT_MAX_SIZE) { + cfg->size = HASHLIMIT_MAX_SIZE; + pr_info_ratelimited("size too large, truncated to %u\n", cfg->size); + } + if (cfg->max > HASHLIMIT_MAX_SIZE) { + cfg->max = HASHLIMIT_MAX_SIZE; + pr_info_ratelimited("max too large, truncated to %u\n", cfg->max); + } if (par->family == NFPROTO_IPV4) { if (cfg->srcmask > 32 || cfg->dstmask > 32) return -EINVAL; diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 930d17fa906c9ebf1cf7b6031ce0a22f9f66c0e4..4a1b1bb39b4b7e60ff9d8997074072b4b6a6e719 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1029,7 +1029,8 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr, if (nlk->netlink_bind && groups) { int group; - for (group = 0; group < nlk->ngroups; group++) { + /* nl_groups is a u32, so cap the maximum groups we can bind */ + for (group = 0; group < BITS_PER_TYPE(u32); group++) { if (!test_bit(group, &groups)) continue; err = nlk->netlink_bind(net, group + 1); @@ -1048,7 +1049,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr, netlink_insert(sk, nladdr->nl_pid) : netlink_autobind(sock); if (err) { - netlink_undo_bind(nlk->ngroups, groups, sk); + netlink_undo_bind(BITS_PER_TYPE(u32), groups, sk); goto unlock; } } diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index fa6cc7140a2497b80c9505d450aba4272987595f..6fd71a249b6e2cb0f988b70fb0e3c465fe833c61 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1297,15 +1297,21 @@ static void packet_sock_destruct(struct sock *sk) static bool fanout_flow_is_huge(struct packet_sock *po, struct sk_buff *skb) { - u32 rxhash; + u32 *history = po->rollover->history; + u32 victim, rxhash; int i, count = 0; rxhash = skb_get_hash(skb); for (i = 0; i < ROLLOVER_HLEN; i++) - if (po->rollover->history[i] == rxhash) + if (READ_ONCE(history[i]) == rxhash) count++; - po->rollover->history[prandom_u32() % ROLLOVER_HLEN] = rxhash; + victim = prandom_u32() % ROLLOVER_HLEN; + + /* Avoid dirtying the cache line if possible */ + if (READ_ONCE(history[victim]) != rxhash) + WRITE_ONCE(history[victim], rxhash); + return count > (ROLLOVER_HLEN >> 1); } @@ -3371,20 +3377,29 @@ static int packet_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, sock_recv_ts_and_drops(msg, sk, skb); if (msg->msg_name) { + int copy_len; + /* If the address length field is there to be filled * in, we fill it in now. */ if (sock->type == SOCK_PACKET) { __sockaddr_check_size(sizeof(struct sockaddr_pkt)); msg->msg_namelen = sizeof(struct sockaddr_pkt); + copy_len = msg->msg_namelen; } else { struct sockaddr_ll *sll = &PACKET_SKB_CB(skb)->sa.ll; msg->msg_namelen = sll->sll_halen + offsetof(struct sockaddr_ll, sll_addr); + copy_len = msg->msg_namelen; + if (msg->msg_namelen < sizeof(struct sockaddr_ll)) { + memset(msg->msg_name + + offsetof(struct sockaddr_ll, sll_addr), + 0, sizeof(sll->sll_addr)); + msg->msg_namelen = sizeof(struct sockaddr_ll); + } } - memcpy(msg->msg_name, &PACKET_SKB_CB(skb)->sa, - msg->msg_namelen); + memcpy(msg->msg_name, &PACKET_SKB_CB(skb)->sa, copy_len); } if (pkt_sk(sk)->auxdata) { diff --git a/net/qrtr/mhi.c b/net/qrtr/mhi.c index ab21357fe1b023e13821bd0110255f26006a73f4..e532b7adba958be1123a095a829ebb2e70d95a3f 100644 --- a/net/qrtr/mhi.c +++ b/net/qrtr/mhi.c @@ -171,12 +171,11 @@ static int qcom_mhi_qrtr_probe(struct mhi_device *mhi_dev, INIT_LIST_HEAD(&qdev->ul_pkts); spin_lock_init(&qdev->ul_lock); + dev_set_drvdata(&mhi_dev->dev, qdev); rc = qrtr_endpoint_register(&qdev->ep, net_id, rt); if (rc) return rc; - dev_set_drvdata(&mhi_dev->dev, qdev); - dev_dbg(qdev->dev, "QTI MHI QRTR driver probed\n"); return 0; diff --git a/net/qrtr/qrtr.c b/net/qrtr/qrtr.c index 6f1f1d5a2440f054190a75e2c056f64a6d40eabb..59dd4ae583ac4a10e21626ef08d4d48d5fa3d78d 100644 --- a/net/qrtr/qrtr.c +++ b/net/qrtr/qrtr.c @@ -160,6 +160,7 @@ static struct work_struct qrtr_backup_work; * @kworker: worker thread for recv work * @task: task to run the worker thread * @read_data: scheduled work for recv work + * @say_hello: scheduled work for initiating hello * @ws: wakeupsource avoid system suspend * @ilc: ipc logging context reference */ @@ -170,6 +171,7 @@ struct qrtr_node { unsigned int nid; unsigned int net_id; atomic_t hello_sent; + atomic_t hello_rcvd; struct radix_tree_root qrtr_tx_flow; struct wait_queue_head resume_tx; @@ -181,6 +183,7 @@ struct qrtr_node { struct kthread_worker kworker; struct task_struct *task; struct kthread_work read_data; + struct kthread_work say_hello; struct wakeup_source *ws; @@ -529,6 +532,10 @@ static int qrtr_node_enqueue(struct qrtr_node *node, struct sk_buff *skb, kfree_skb(skb); return rc; } + if (atomic_read(&node->hello_sent) && type == QRTR_TYPE_HELLO) { + kfree_skb(skb); + return 0; + } /* If sk is null, this is a forwarded packet and should not wait */ if (!skb->sk) { @@ -649,7 +656,7 @@ static void qrtr_node_assign(struct qrtr_node *node, unsigned int nid) * cause APPS suspend problems and power drain issue. */ if (!node->ws && (nid == 0 || nid == 3 || nid == 7)) - node->ws = wakeup_source_register(name); + node->ws = wakeup_source_register(NULL, name); } /** @@ -966,6 +973,30 @@ static void qrtr_fwd_pkt(struct sk_buff *skb, struct qrtr_cb *cb) qrtr_node_enqueue(node, skb, cb->type, &from, &to, 0); qrtr_node_release(node); } + +static void qrtr_sock_queue_skb(struct qrtr_node *node, struct sk_buff *skb, + struct qrtr_sock *ipc) +{ + struct qrtr_cb *cb = (struct qrtr_cb *)skb->cb; + int rc; + + /* Don't queue HELLO if control port already received */ + if (cb->type == QRTR_TYPE_HELLO) { + if (atomic_read(&node->hello_rcvd)) { + kfree_skb(skb); + return; + } + atomic_inc(&node->hello_rcvd); + } + + rc = sock_queue_rcv_skb(&ipc->sk, skb); + if (rc) { + pr_err("%s: qrtr pkt dropped flow[%d] rc[%d]\n", + __func__, cb->confirm_rx, rc); + kfree_skb(skb); + } +} + /* Handle and route a received packet. * * This will auto-reply with resume-tx packet as necessary. @@ -1008,18 +1039,40 @@ static void qrtr_node_rx_work(struct kthread_work *work) if (!ipc) { kfree_skb(skb); } else { - if (sock_queue_rcv_skb(&ipc->sk, skb)) { - pr_err("%s qrtr pkt dropped flow[%d]\n", - __func__, cb->confirm_rx); - kfree_skb(skb); - } - + qrtr_sock_queue_skb(node, skb, ipc); qrtr_port_put(ipc); } } } } +static void qrtr_hello_work(struct kthread_work *work) +{ + struct sockaddr_qrtr from = {AF_QIPCRTR, 0, QRTR_PORT_CTRL}; + struct sockaddr_qrtr to = {AF_QIPCRTR, 0, QRTR_PORT_CTRL}; + struct qrtr_ctrl_pkt *pkt; + struct qrtr_node *node; + struct qrtr_sock *ctrl; + struct sk_buff *skb; + + ctrl = qrtr_port_lookup(QRTR_PORT_CTRL); + if (!ctrl) + return; + + skb = qrtr_alloc_ctrl_packet(&pkt); + if (!skb) { + qrtr_port_put(ctrl); + return; + } + + node = container_of(work, struct qrtr_node, say_hello); + pkt->cmd = cpu_to_le32(QRTR_TYPE_HELLO); + from.sq_node = qrtr_local_nid; + to.sq_node = node->nid; + qrtr_node_enqueue(node, skb, QRTR_TYPE_HELLO, &from, &to, 0); + qrtr_port_put(ctrl); +} + /** * qrtr_endpoint_register() - register a new endpoint * @ep: endpoint to register @@ -1048,8 +1101,10 @@ int qrtr_endpoint_register(struct qrtr_endpoint *ep, unsigned int net_id, node->nid = QRTR_EP_NID_AUTO; node->ep = ep; atomic_set(&node->hello_sent, 0); + atomic_set(&node->hello_rcvd, 0); kthread_init_work(&node->read_data, qrtr_node_rx_work); + kthread_init_work(&node->say_hello, qrtr_hello_work); kthread_init_worker(&node->kworker); node->task = kthread_run(kthread_worker_fn, &node->kworker, "qrtr_rx"); if (IS_ERR(node->task)) { @@ -1071,6 +1126,7 @@ int qrtr_endpoint_register(struct qrtr_endpoint *ep, unsigned int net_id, up_write(&qrtr_node_lock); ep->node = node; + kthread_queue_work(&node->kworker, &node->say_hello); return 0; } EXPORT_SYMBOL_GPL(qrtr_endpoint_register); @@ -1352,6 +1408,17 @@ static int __qrtr_bind(struct socket *sock, qrtr_reset_ports(); mutex_unlock(&qrtr_port_lock); + if (port == QRTR_PORT_CTRL) { + struct qrtr_node *node; + + down_write(&qrtr_node_lock); + list_for_each_entry(node, &qrtr_all_epts, item) { + atomic_set(&node->hello_sent, 0); + atomic_set(&node->hello_rcvd, 0); + } + up_write(&qrtr_node_lock); + } + /* unbind previous, if any */ if (!zapped) qrtr_port_remove(ipc); @@ -1451,7 +1518,7 @@ static int qrtr_bcast_enqueue(struct qrtr_node *node, struct sk_buff *skb, down_read(&qrtr_node_lock); list_for_each_entry(node, &qrtr_all_epts, item) { - if (node->nid == QRTR_EP_NID_AUTO) + if (node->nid == QRTR_EP_NID_AUTO && type != QRTR_TYPE_HELLO) continue; skbn = skb_clone(skb, GFP_KERNEL); if (!skbn) diff --git a/net/rds/ib_stats.c b/net/rds/ib_stats.c index 9252ad126335971fa202e5aee6e2babb55d969e0..ac46d8961b61a199032fbed88337558a713a1845 100644 --- a/net/rds/ib_stats.c +++ b/net/rds/ib_stats.c @@ -42,7 +42,7 @@ DEFINE_PER_CPU_SHARED_ALIGNED(struct rds_ib_statistics, rds_ib_stats); static const char *const rds_ib_stat_names[] = { "ib_connect_raced", "ib_listen_closed_stale", - "s_ib_evt_handler_call", + "ib_evt_handler_call", "ib_tasklet_call", "ib_tx_cq_event", "ib_tx_ring_full", diff --git a/net/rds/stats.c b/net/rds/stats.c index 73be187d389ed044c886d22e4960905283d9de87..6bbab4d74c4fecfe3a3b94851c87406d6d887b17 100644 --- a/net/rds/stats.c +++ b/net/rds/stats.c @@ -76,6 +76,8 @@ static const char *const rds_stat_names[] = { "cong_update_received", "cong_send_error", "cong_send_blocked", + "recv_bytes_added_to_sock", + "recv_bytes_freed_fromsock", }; void rds_stats_info_copy(struct rds_info_iterator *iter, diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c index 7319d3ca30e9494e31ff2a1202ac22e93ad6f56b..57f835d2442ec9b2b7a9cc9e8223f3330e517e49 100644 --- a/net/rxrpc/af_rxrpc.c +++ b/net/rxrpc/af_rxrpc.c @@ -196,6 +196,7 @@ static int rxrpc_bind(struct socket *sock, struct sockaddr *saddr, int len) service_in_use: write_unlock(&local->services_lock); rxrpc_unuse_local(local); + rxrpc_put_local(local); ret = -EADDRINUSE; error_unlock: release_sock(&rx->sk); @@ -869,7 +870,6 @@ static void rxrpc_sock_destructor(struct sock *sk) static int rxrpc_release_sock(struct sock *sk) { struct rxrpc_sock *rx = rxrpc_sk(sk); - struct rxrpc_net *rxnet = rxrpc_net(sock_net(&rx->sk)); _enter("%p{%d,%d}", sk, sk->sk_state, refcount_read(&sk->sk_refcnt)); @@ -905,10 +905,9 @@ static int rxrpc_release_sock(struct sock *sk) rxrpc_release_calls_on_socket(rx); flush_workqueue(rxrpc_workqueue); rxrpc_purge_queue(&sk->sk_receive_queue); - rxrpc_queue_work(&rxnet->service_conn_reaper); - rxrpc_queue_work(&rxnet->client_conn_reaper); rxrpc_unuse_local(rx->local); + rxrpc_put_local(rx->local); rx->local = NULL; key_put(rx->key); rx->key = NULL; diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 8d72e948e9c636bff1859916e063ca0a8f7bb8fa..8ef9fb5765742e109dfbaf7ca0d6d6868072cc4b 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -484,6 +484,7 @@ enum rxrpc_call_flag { RXRPC_CALL_BEGAN_RX_TIMER, /* We began the expect_rx_by timer */ RXRPC_CALL_RX_HEARD, /* The peer responded at least once to this call */ RXRPC_CALL_RX_UNDERRUN, /* Got data underrun */ + RXRPC_CALL_DISCONNECTED, /* The call has been disconnected */ }; /* @@ -644,6 +645,7 @@ struct rxrpc_call { u8 ackr_reason; /* reason to ACK */ u16 ackr_skew; /* skew on packet being ACK'd */ rxrpc_serial_t ackr_serial; /* serial of packet being ACK'd */ + rxrpc_serial_t ackr_first_seq; /* first sequence number received */ rxrpc_seq_t ackr_prev_seq; /* previous sequence number received */ rxrpc_seq_t ackr_consumed; /* Highest packet shown consumed */ rxrpc_seq_t ackr_seen; /* Highest packet shown seen */ @@ -902,6 +904,7 @@ void rxrpc_disconnect_client_call(struct rxrpc_call *); void rxrpc_put_client_conn(struct rxrpc_connection *); void rxrpc_discard_expired_client_conns(struct work_struct *); void rxrpc_destroy_all_client_connections(struct rxrpc_net *); +void rxrpc_clean_up_local_conns(struct rxrpc_local *); /* * conn_event.c @@ -1004,6 +1007,16 @@ void rxrpc_unuse_local(struct rxrpc_local *); void rxrpc_queue_local(struct rxrpc_local *); void rxrpc_destroy_all_locals(struct rxrpc_net *); +static inline bool __rxrpc_unuse_local(struct rxrpc_local *local) +{ + return atomic_dec_return(&local->active_users) == 0; +} + +static inline bool __rxrpc_use_local(struct rxrpc_local *local) +{ + return atomic_fetch_add_unless(&local->active_users, 1, 0) != 0; +} + /* * misc.c */ diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c index 8079aacaecace1498c71d0d0ac13937992cc4612..c5566bc4aaca35cfb66c2493503d57b72aed933d 100644 --- a/net/rxrpc/call_accept.c +++ b/net/rxrpc/call_accept.c @@ -88,7 +88,7 @@ static int rxrpc_service_prealloc_one(struct rxrpc_sock *rx, smp_store_release(&b->conn_backlog_head, (head + 1) & (size - 1)); - trace_rxrpc_conn(conn, rxrpc_conn_new_service, + trace_rxrpc_conn(conn->debug_id, rxrpc_conn_new_service, atomic_read(&conn->usage), here); } diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index 215f4d98baa0f2ed828bfe5c506374693747ea54..964c4e45de11f5a3fa68d5b832e856043415f08c 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -520,7 +520,7 @@ void rxrpc_release_call(struct rxrpc_sock *rx, struct rxrpc_call *call) _debug("RELEASE CALL %p (%d CONN %p)", call, call->debug_id, conn); - if (conn) + if (conn && !test_bit(RXRPC_CALL_DISCONNECTED, &call->flags)) rxrpc_disconnect_call(call); for (i = 0; i < RXRPC_RXTX_BUFF_SIZE; i++) { @@ -647,13 +647,14 @@ void rxrpc_put_call(struct rxrpc_call *call, enum rxrpc_call_trace op) } /* - * Final call destruction under RCU. + * Final call destruction - but must be done in process context. */ -static void rxrpc_rcu_destroy_call(struct rcu_head *rcu) +static void rxrpc_destroy_call(struct work_struct *work) { - struct rxrpc_call *call = container_of(rcu, struct rxrpc_call, rcu); + struct rxrpc_call *call = container_of(work, struct rxrpc_call, processor); struct rxrpc_net *rxnet = call->rxnet; + rxrpc_put_connection(call->conn); rxrpc_put_peer(call->peer); kfree(call->rxtx_buffer); kfree(call->rxtx_annotations); @@ -662,6 +663,22 @@ static void rxrpc_rcu_destroy_call(struct rcu_head *rcu) wake_up_var(&rxnet->nr_calls); } +/* + * Final call destruction under RCU. + */ +static void rxrpc_rcu_destroy_call(struct rcu_head *rcu) +{ + struct rxrpc_call *call = container_of(rcu, struct rxrpc_call, rcu); + + if (in_softirq()) { + INIT_WORK(&call->processor, rxrpc_destroy_call); + if (!rxrpc_queue_work(&call->processor)) + BUG(); + } else { + rxrpc_destroy_call(&call->processor); + } +} + /* * clean up a call */ @@ -677,7 +694,6 @@ void rxrpc_cleanup_call(struct rxrpc_call *call) ASSERTCMP(call->state, ==, RXRPC_CALL_COMPLETE); ASSERT(test_bit(RXRPC_CALL_RELEASED, &call->flags)); - ASSERTCMP(call->conn, ==, NULL); /* Clean up the Rx/Tx buffer */ for (i = 0; i < RXRPC_RXTX_BUFF_SIZE; i++) diff --git a/net/rxrpc/conn_client.c b/net/rxrpc/conn_client.c index c979a56faaef0278db0940c6f82db2661e6b21d0..4ffc7b87fec0b278da6083882c6282a027880ee0 100644 --- a/net/rxrpc/conn_client.c +++ b/net/rxrpc/conn_client.c @@ -217,7 +217,8 @@ rxrpc_alloc_client_connection(struct rxrpc_conn_parameters *cp, gfp_t gfp) rxrpc_get_local(conn->params.local); key_get(conn->params.key); - trace_rxrpc_conn(conn, rxrpc_conn_new_client, atomic_read(&conn->usage), + trace_rxrpc_conn(conn->debug_id, rxrpc_conn_new_client, + atomic_read(&conn->usage), __builtin_return_address(0)); trace_rxrpc_client(conn, -1, rxrpc_client_alloc); _leave(" = %p", conn); @@ -785,6 +786,7 @@ void rxrpc_disconnect_client_call(struct rxrpc_call *call) u32 cid; spin_lock(&conn->channel_lock); + set_bit(RXRPC_CALL_DISCONNECTED, &call->flags); cid = call->cid; if (cid) { @@ -792,7 +794,6 @@ void rxrpc_disconnect_client_call(struct rxrpc_call *call) chan = &conn->channels[channel]; } trace_rxrpc_client(conn, channel, rxrpc_client_chan_disconnect); - call->conn = NULL; /* Calls that have never actually been assigned a channel can simply be * discarded. If the conn didn't get used either, it will follow @@ -908,7 +909,6 @@ void rxrpc_disconnect_client_call(struct rxrpc_call *call) spin_unlock(&rxnet->client_conn_cache_lock); out_2: spin_unlock(&conn->channel_lock); - rxrpc_put_connection(conn); _leave(""); return; @@ -989,11 +989,12 @@ rxrpc_put_one_client_conn(struct rxrpc_connection *conn) void rxrpc_put_client_conn(struct rxrpc_connection *conn) { const void *here = __builtin_return_address(0); + unsigned int debug_id = conn->debug_id; int n; do { n = atomic_dec_return(&conn->usage); - trace_rxrpc_conn(conn, rxrpc_conn_put_client, n, here); + trace_rxrpc_conn(debug_id, rxrpc_conn_put_client, n, here); if (n > 0) return; ASSERTCMP(n, >=, 0); @@ -1166,3 +1167,47 @@ void rxrpc_destroy_all_client_connections(struct rxrpc_net *rxnet) _leave(""); } + +/* + * Clean up the client connections on a local endpoint. + */ +void rxrpc_clean_up_local_conns(struct rxrpc_local *local) +{ + struct rxrpc_connection *conn, *tmp; + struct rxrpc_net *rxnet = local->rxnet; + unsigned int nr_active; + LIST_HEAD(graveyard); + + _enter(""); + + spin_lock(&rxnet->client_conn_cache_lock); + nr_active = rxnet->nr_active_client_conns; + + list_for_each_entry_safe(conn, tmp, &rxnet->idle_client_conns, + cache_link) { + if (conn->params.local == local) { + ASSERTCMP(conn->cache_state, ==, RXRPC_CONN_CLIENT_IDLE); + + trace_rxrpc_client(conn, -1, rxrpc_client_discard); + if (!test_and_clear_bit(RXRPC_CONN_EXPOSED, &conn->flags)) + BUG(); + conn->cache_state = RXRPC_CONN_CLIENT_INACTIVE; + list_move(&conn->cache_link, &graveyard); + nr_active--; + } + } + + rxnet->nr_active_client_conns = nr_active; + spin_unlock(&rxnet->client_conn_cache_lock); + ASSERTCMP(nr_active, >=, 0); + + while (!list_empty(&graveyard)) { + conn = list_entry(graveyard.next, + struct rxrpc_connection, cache_link); + list_del_init(&conn->cache_link); + + rxrpc_put_connection(conn); + } + + _leave(" [culled]"); +} diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c index b6fca8ebb1173f4de1047e96315c26072666c2e9..126154a97a5921697d1c385dec78a3291d19179b 100644 --- a/net/rxrpc/conn_event.c +++ b/net/rxrpc/conn_event.c @@ -453,16 +453,12 @@ static void rxrpc_process_delayed_final_acks(struct rxrpc_connection *conn) /* * connection-level event processor */ -void rxrpc_process_connection(struct work_struct *work) +static void rxrpc_do_process_connection(struct rxrpc_connection *conn) { - struct rxrpc_connection *conn = - container_of(work, struct rxrpc_connection, processor); struct sk_buff *skb; u32 abort_code = RX_PROTOCOL_ERROR; int ret; - rxrpc_see_connection(conn); - if (test_and_clear_bit(RXRPC_CONN_EV_CHALLENGE, &conn->events)) rxrpc_secure_connection(conn); @@ -490,18 +486,33 @@ void rxrpc_process_connection(struct work_struct *work) } } -out: - rxrpc_put_connection(conn); - _leave(""); return; requeue_and_leave: skb_queue_head(&conn->rx_queue, skb); - goto out; + return; protocol_error: if (rxrpc_abort_connection(conn, ret, abort_code) < 0) goto requeue_and_leave; rxrpc_free_skb(skb, rxrpc_skb_rx_freed); - goto out; + return; +} + +void rxrpc_process_connection(struct work_struct *work) +{ + struct rxrpc_connection *conn = + container_of(work, struct rxrpc_connection, processor); + + rxrpc_see_connection(conn); + + if (__rxrpc_use_local(conn->params.local)) { + rxrpc_do_process_connection(conn); + rxrpc_unuse_local(conn->params.local); + } + + rxrpc_put_connection(conn); + _leave(""); + return; } + diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c index 885dae829f4a1a1690334f0a6a3375d34d79bffb..c4c4450891e0ff3d49e160ad2259e003708c3a6c 100644 --- a/net/rxrpc/conn_object.c +++ b/net/rxrpc/conn_object.c @@ -226,9 +226,8 @@ void rxrpc_disconnect_call(struct rxrpc_call *call) __rxrpc_disconnect_call(conn, call); spin_unlock(&conn->channel_lock); - call->conn = NULL; + set_bit(RXRPC_CALL_DISCONNECTED, &call->flags); conn->idle_timestamp = jiffies; - rxrpc_put_connection(conn); } /* @@ -272,7 +271,7 @@ bool rxrpc_queue_conn(struct rxrpc_connection *conn) if (n == 0) return false; if (rxrpc_queue_work(&conn->processor)) - trace_rxrpc_conn(conn, rxrpc_conn_queued, n + 1, here); + trace_rxrpc_conn(conn->debug_id, rxrpc_conn_queued, n + 1, here); else rxrpc_put_connection(conn); return true; @@ -287,7 +286,7 @@ void rxrpc_see_connection(struct rxrpc_connection *conn) if (conn) { int n = atomic_read(&conn->usage); - trace_rxrpc_conn(conn, rxrpc_conn_seen, n, here); + trace_rxrpc_conn(conn->debug_id, rxrpc_conn_seen, n, here); } } @@ -299,7 +298,7 @@ void rxrpc_get_connection(struct rxrpc_connection *conn) const void *here = __builtin_return_address(0); int n = atomic_inc_return(&conn->usage); - trace_rxrpc_conn(conn, rxrpc_conn_got, n, here); + trace_rxrpc_conn(conn->debug_id, rxrpc_conn_got, n, here); } /* @@ -313,7 +312,7 @@ rxrpc_get_connection_maybe(struct rxrpc_connection *conn) if (conn) { int n = atomic_fetch_add_unless(&conn->usage, 1, 0); if (n > 0) - trace_rxrpc_conn(conn, rxrpc_conn_got, n + 1, here); + trace_rxrpc_conn(conn->debug_id, rxrpc_conn_got, n + 1, here); else conn = NULL; } @@ -336,10 +335,11 @@ static void rxrpc_set_service_reap_timer(struct rxrpc_net *rxnet, void rxrpc_put_service_conn(struct rxrpc_connection *conn) { const void *here = __builtin_return_address(0); + unsigned int debug_id = conn->debug_id; int n; n = atomic_dec_return(&conn->usage); - trace_rxrpc_conn(conn, rxrpc_conn_put_service, n, here); + trace_rxrpc_conn(debug_id, rxrpc_conn_put_service, n, here); ASSERTCMP(n, >=, 0); if (n == 1) rxrpc_set_service_reap_timer(conn->params.local->rxnet, @@ -401,7 +401,7 @@ void rxrpc_service_connection_reaper(struct work_struct *work) if (conn->state == RXRPC_CONN_SERVICE_PREALLOC) continue; - if (rxnet->live) { + if (rxnet->live && !conn->params.local->dead) { idle_timestamp = READ_ONCE(conn->idle_timestamp); expire_at = idle_timestamp + rxrpc_connection_expiry * HZ; if (conn->params.local->service_closed) @@ -423,7 +423,7 @@ void rxrpc_service_connection_reaper(struct work_struct *work) */ if (atomic_cmpxchg(&conn->usage, 1, 0) != 1) continue; - trace_rxrpc_conn(conn, rxrpc_conn_reap_service, 0, NULL); + trace_rxrpc_conn(conn->debug_id, rxrpc_conn_reap_service, 0, NULL); if (rxrpc_conn_is_client(conn)) BUG(); diff --git a/net/rxrpc/conn_service.c b/net/rxrpc/conn_service.c index 80773a50c755168773c758cb5c2b5b86c117cf02..6da7c4bf15e88cdb10abce24302d06bbce944149 100644 --- a/net/rxrpc/conn_service.c +++ b/net/rxrpc/conn_service.c @@ -138,7 +138,7 @@ struct rxrpc_connection *rxrpc_prealloc_service_connection(struct rxrpc_net *rxn list_add_tail(&conn->proc_link, &rxnet->conn_proc_list); write_unlock(&rxnet->conn_lock); - trace_rxrpc_conn(conn, rxrpc_conn_new_service, + trace_rxrpc_conn(conn->debug_id, rxrpc_conn_new_service, atomic_read(&conn->usage), __builtin_return_address(0)); } diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 7965600ee5dec068a8830366e510a7acd9644eb6..d9beb28fc32f2eacf5bcfa8f2d5392ef450e1087 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -612,10 +612,8 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb, false, true, rxrpc_propose_ack_input_data); - if (sp->hdr.seq == READ_ONCE(call->rx_hard_ack) + 1) { - trace_rxrpc_notify_socket(call->debug_id, serial); - rxrpc_notify_socket(call); - } + trace_rxrpc_notify_socket(call->debug_id, serial); + rxrpc_notify_socket(call); unlock: spin_unlock(&call->input_lock); @@ -837,7 +835,7 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb, u8 acks[RXRPC_MAXACKS]; } buf; rxrpc_serial_t acked_serial; - rxrpc_seq_t first_soft_ack, hard_ack; + rxrpc_seq_t first_soft_ack, hard_ack, prev_pkt; int nr_acks, offset, ioffset; _enter(""); @@ -851,13 +849,14 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb, acked_serial = ntohl(buf.ack.serial); first_soft_ack = ntohl(buf.ack.firstPacket); + prev_pkt = ntohl(buf.ack.previousPacket); hard_ack = first_soft_ack - 1; nr_acks = buf.ack.nAcks; summary.ack_reason = (buf.ack.reason < RXRPC_ACK__INVALID ? buf.ack.reason : RXRPC_ACK__INVALID); trace_rxrpc_rx_ack(call, sp->hdr.serial, acked_serial, - first_soft_ack, ntohl(buf.ack.previousPacket), + first_soft_ack, prev_pkt, summary.ack_reason, nr_acks); if (buf.ack.reason == RXRPC_ACK_PING_RESPONSE) @@ -878,8 +877,9 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb, rxrpc_propose_ack_respond_to_ack); } - /* Discard any out-of-order or duplicate ACKs. */ - if (before_eq(sp->hdr.serial, call->acks_latest)) + /* Discard any out-of-order or duplicate ACKs (outside lock). */ + if (before(first_soft_ack, call->ackr_first_seq) || + before(prev_pkt, call->ackr_prev_seq)) return; buf.info.rxMTU = 0; @@ -890,12 +890,16 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb, spin_lock(&call->input_lock); - /* Discard any out-of-order or duplicate ACKs. */ - if (before_eq(sp->hdr.serial, call->acks_latest)) + /* Discard any out-of-order or duplicate ACKs (inside lock). */ + if (before(first_soft_ack, call->ackr_first_seq) || + before(prev_pkt, call->ackr_prev_seq)) goto out; call->acks_latest_ts = skb->tstamp; call->acks_latest = sp->hdr.serial; + call->ackr_first_seq = first_soft_ack; + call->ackr_prev_seq = prev_pkt; + /* Parse rwind and mtu sizes if provided. */ if (buf.info.rxMTU) rxrpc_input_ackinfo(call, skb, &buf.info); diff --git a/net/rxrpc/local_object.c b/net/rxrpc/local_object.c index c752ad4870678dcdd7c04e42692ef7c586fc014b..4c0087a48e8726919c04b8fa92d3d591905d1101 100644 --- a/net/rxrpc/local_object.c +++ b/net/rxrpc/local_object.c @@ -368,11 +368,14 @@ void rxrpc_queue_local(struct rxrpc_local *local) void rxrpc_put_local(struct rxrpc_local *local) { const void *here = __builtin_return_address(0); + unsigned int debug_id; int n; if (local) { + debug_id = local->debug_id; + n = atomic_dec_return(&local->usage); - trace_rxrpc_local(local->debug_id, rxrpc_local_put, n, here); + trace_rxrpc_local(debug_id, rxrpc_local_put, n, here); if (n == 0) call_rcu(&local->rcu, rxrpc_local_rcu); @@ -384,14 +387,11 @@ void rxrpc_put_local(struct rxrpc_local *local) */ struct rxrpc_local *rxrpc_use_local(struct rxrpc_local *local) { - unsigned int au; - local = rxrpc_get_local_maybe(local); if (!local) return NULL; - au = atomic_fetch_add_unless(&local->active_users, 1, 0); - if (au == 0) { + if (!__rxrpc_use_local(local)) { rxrpc_put_local(local); return NULL; } @@ -405,14 +405,11 @@ struct rxrpc_local *rxrpc_use_local(struct rxrpc_local *local) */ void rxrpc_unuse_local(struct rxrpc_local *local) { - unsigned int au; - if (local) { - au = atomic_dec_return(&local->active_users); - if (au == 0) + if (__rxrpc_unuse_local(local)) { + rxrpc_get_local(local); rxrpc_queue_local(local); - else - rxrpc_put_local(local); + } } } @@ -430,11 +427,14 @@ static void rxrpc_local_destroyer(struct rxrpc_local *local) _enter("%d", local->debug_id); + local->dead = true; + mutex_lock(&rxnet->local_mutex); list_del_init(&local->link); mutex_unlock(&rxnet->local_mutex); - ASSERT(RB_EMPTY_ROOT(&local->client_conns)); + rxrpc_clean_up_local_conns(local); + rxrpc_service_connection_reaper(&rxnet->service_conn_reaper); ASSERT(!local->service); if (socket) { @@ -466,7 +466,7 @@ static void rxrpc_local_processor(struct work_struct *work) do { again = false; - if (atomic_read(&local->active_users) == 0) { + if (!__rxrpc_use_local(local)) { rxrpc_local_destroyer(local); break; } @@ -480,6 +480,8 @@ static void rxrpc_local_processor(struct work_struct *work) rxrpc_process_local_events(local); again = true; } + + __rxrpc_unuse_local(local); } while (again); rxrpc_put_local(local); diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c index 345dc1c5fe72fba7f502eb9fe16a98b780993c17..b0aa08e3796de1909563f7448de224cded2a7f73 100644 --- a/net/rxrpc/output.c +++ b/net/rxrpc/output.c @@ -133,7 +133,7 @@ static size_t rxrpc_fill_out_ack(struct rxrpc_connection *conn, int rxrpc_send_ack_packet(struct rxrpc_call *call, bool ping, rxrpc_serial_t *_serial) { - struct rxrpc_connection *conn = NULL; + struct rxrpc_connection *conn; struct rxrpc_ack_buffer *pkt; struct msghdr msg; struct kvec iov[2]; @@ -143,18 +143,14 @@ int rxrpc_send_ack_packet(struct rxrpc_call *call, bool ping, int ret; u8 reason; - spin_lock_bh(&call->lock); - if (call->conn) - conn = rxrpc_get_connection_maybe(call->conn); - spin_unlock_bh(&call->lock); - if (!conn) + if (test_bit(RXRPC_CALL_DISCONNECTED, &call->flags)) return -ECONNRESET; pkt = kzalloc(sizeof(*pkt), GFP_KERNEL); - if (!pkt) { - rxrpc_put_connection(conn); + if (!pkt) return -ENOMEM; - } + + conn = call->conn; msg.msg_name = &call->peer->srx.transport; msg.msg_namelen = call->peer->srx.transport_len; @@ -249,7 +245,6 @@ int rxrpc_send_ack_packet(struct rxrpc_call *call, bool ping, } out: - rxrpc_put_connection(conn); kfree(pkt); return ret; } @@ -259,7 +254,7 @@ int rxrpc_send_ack_packet(struct rxrpc_call *call, bool ping, */ int rxrpc_send_abort_packet(struct rxrpc_call *call) { - struct rxrpc_connection *conn = NULL; + struct rxrpc_connection *conn; struct rxrpc_abort_buffer pkt; struct msghdr msg; struct kvec iov[1]; @@ -276,13 +271,11 @@ int rxrpc_send_abort_packet(struct rxrpc_call *call) test_bit(RXRPC_CALL_TX_LAST, &call->flags)) return 0; - spin_lock_bh(&call->lock); - if (call->conn) - conn = rxrpc_get_connection_maybe(call->conn); - spin_unlock_bh(&call->lock); - if (!conn) + if (test_bit(RXRPC_CALL_DISCONNECTED, &call->flags)) return -ECONNRESET; + conn = call->conn; + msg.msg_name = &call->peer->srx.transport; msg.msg_namelen = call->peer->srx.transport_len; msg.msg_control = NULL; @@ -317,8 +310,6 @@ int rxrpc_send_abort_packet(struct rxrpc_call *call) trace_rxrpc_tx_packet(call->debug_id, &pkt.whdr, rxrpc_tx_point_call_abort); rxrpc_tx_backoff(call, ret); - - rxrpc_put_connection(conn); return ret; } @@ -524,6 +515,9 @@ int rxrpc_send_data_packet(struct rxrpc_call *call, struct sk_buff *skb, } break; #endif + + default: + BUG(); } if (ret < 0) diff --git a/net/rxrpc/peer_event.c b/net/rxrpc/peer_event.c index 42582a9ff81d3aee99cfdf60b36ea69bd45e5a38..85bdc31d3dbf9e11157a805ec2b404e817198156 100644 --- a/net/rxrpc/peer_event.c +++ b/net/rxrpc/peer_event.c @@ -357,27 +357,31 @@ static void rxrpc_peer_keepalive_dispatch(struct rxrpc_net *rxnet, if (!rxrpc_get_peer_maybe(peer)) continue; - spin_unlock_bh(&rxnet->peer_hash_lock); - - keepalive_at = peer->last_tx_at + RXRPC_KEEPALIVE_TIME; - slot = keepalive_at - base; - _debug("%02x peer %u t=%d {%pISp}", - cursor, peer->debug_id, slot, &peer->srx.transport); + if (__rxrpc_use_local(peer->local)) { + spin_unlock_bh(&rxnet->peer_hash_lock); + + keepalive_at = peer->last_tx_at + RXRPC_KEEPALIVE_TIME; + slot = keepalive_at - base; + _debug("%02x peer %u t=%d {%pISp}", + cursor, peer->debug_id, slot, &peer->srx.transport); + + if (keepalive_at <= base || + keepalive_at > base + RXRPC_KEEPALIVE_TIME) { + rxrpc_send_keepalive(peer); + slot = RXRPC_KEEPALIVE_TIME; + } - if (keepalive_at <= base || - keepalive_at > base + RXRPC_KEEPALIVE_TIME) { - rxrpc_send_keepalive(peer); - slot = RXRPC_KEEPALIVE_TIME; + /* A transmission to this peer occurred since last we + * examined it so put it into the appropriate future + * bucket. + */ + slot += cursor; + slot &= mask; + spin_lock_bh(&rxnet->peer_hash_lock); + list_add_tail(&peer->keepalive_link, + &rxnet->peer_keepalive[slot & mask]); + rxrpc_unuse_local(peer->local); } - - /* A transmission to this peer occurred since last we examined - * it so put it into the appropriate future bucket. - */ - slot += cursor; - slot &= mask; - spin_lock_bh(&rxnet->peer_hash_lock); - list_add_tail(&peer->keepalive_link, - &rxnet->peer_keepalive[slot & mask]); rxrpc_put_peer_locked(peer); } diff --git a/net/sched/act_csum.c b/net/sched/act_csum.c index 1e269441065a6dd72966c48a2b5d84fb17832c8f..9ecbf8edcf390bff4e0b8aa9ae11ed3ed2577326 100644 --- a/net/sched/act_csum.c +++ b/net/sched/act_csum.c @@ -560,8 +560,11 @@ static int tcf_csum_act(struct sk_buff *skb, const struct tc_action *a, struct tcf_result *res) { struct tcf_csum *p = to_tcf_csum(a); + bool orig_vlan_tag_present = false; + unsigned int vlan_hdr_count = 0; struct tcf_csum_params *params; u32 update_flags; + __be16 protocol; int action; params = rcu_dereference_bh(p->params); @@ -574,7 +577,9 @@ static int tcf_csum_act(struct sk_buff *skb, const struct tc_action *a, goto drop; update_flags = params->update_flags; - switch (tc_skb_protocol(skb)) { + protocol = tc_skb_protocol(skb); +again: + switch (protocol) { case cpu_to_be16(ETH_P_IP): if (!tcf_csum_ipv4(skb, update_flags)) goto drop; @@ -583,13 +588,35 @@ static int tcf_csum_act(struct sk_buff *skb, const struct tc_action *a, if (!tcf_csum_ipv6(skb, update_flags)) goto drop; break; + case cpu_to_be16(ETH_P_8021AD): /* fall through */ + case cpu_to_be16(ETH_P_8021Q): + if (skb_vlan_tag_present(skb) && !orig_vlan_tag_present) { + protocol = skb->protocol; + orig_vlan_tag_present = true; + } else { + struct vlan_hdr *vlan = (struct vlan_hdr *)skb->data; + + protocol = vlan->h_vlan_encapsulated_proto; + skb_pull(skb, VLAN_HLEN); + skb_reset_network_header(skb); + vlan_hdr_count++; + } + goto again; + } + +out: + /* Restore the skb for the pulled VLAN tags */ + while (vlan_hdr_count--) { + skb_push(skb, VLAN_HLEN); + skb_reset_network_header(skb); } return action; drop: qstats_drop_inc(this_cpu_ptr(p->common.cpu_qstats)); - return TC_ACT_SHOT; + action = TC_ACT_SHOT; + goto out; } static int tcf_csum_dump(struct sk_buff *skb, struct tc_action *a, int bind, diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index 399e3beae6cf45a96973b417b6b06a03091b6e8b..a30c17a282819f4ba29783d085eca2d33948b200 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -445,7 +445,11 @@ static int __init mirred_init_module(void) return err; pr_info("Mirror/redirect action on\n"); - return tcf_register_action(&act_mirred_ops, &mirred_net_ops); + err = tcf_register_action(&act_mirred_ops, &mirred_net_ops); + if (err) + unregister_netdevice_notifier(&mirred_device_notifier); + + return err; } static void __exit mirred_cleanup_module(void) diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c index 6a5dce8baf190109f2e9e6902f872236211fc3ff..14098da696f2a61d52d4fb7a75b6d8e7e9384be9 100644 --- a/net/sched/cls_basic.c +++ b/net/sched/cls_basic.c @@ -254,12 +254,17 @@ static void basic_walk(struct tcf_proto *tp, struct tcf_walker *arg) } } -static void basic_bind_class(void *fh, u32 classid, unsigned long cl) +static void basic_bind_class(void *fh, u32 classid, unsigned long cl, void *q, + unsigned long base) { struct basic_filter *f = fh; - if (f && f->res.classid == classid) - f->res.class = cl; + if (f && f->res.classid == classid) { + if (cl) + __tcf_bind_filter(q, &f->res, base); + else + __tcf_unbind_filter(q, &f->res); + } } static int basic_dump(struct net *net, struct tcf_proto *tp, void *fh, diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index fa6fe2fe0f32b521ccb7a77f4e19693e08baf859..5d100126cbf3ea830b0fba200bcf1c86297018e8 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -627,12 +627,17 @@ static int cls_bpf_dump(struct net *net, struct tcf_proto *tp, void *fh, return -1; } -static void cls_bpf_bind_class(void *fh, u32 classid, unsigned long cl) +static void cls_bpf_bind_class(void *fh, u32 classid, unsigned long cl, + void *q, unsigned long base) { struct cls_bpf_prog *prog = fh; - if (prog && prog->res.classid == classid) - prog->res.class = cl; + if (prog && prog->res.classid == classid) { + if (cl) + __tcf_bind_filter(q, &prog->res, base); + else + __tcf_unbind_filter(q, &prog->res); + } } static void cls_bpf_walk(struct tcf_proto *tp, struct tcf_walker *arg) diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 09b359784629e46b1ce5f60c42e193196f312549..44ca31f8538d1c6140560ef45995080bee1b7a3e 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -196,6 +196,7 @@ static int fl_classify(struct sk_buff *skb, const struct tcf_proto *tp, struct fl_flow_key skb_mkey; list_for_each_entry_rcu(mask, &head->masks, list) { + flow_dissector_init_keys(&skb_key.control, &skb_key.basic); fl_clear_masked_range(&skb_key, mask); skb_key.indev_ifindex = skb->skb_iif; @@ -486,6 +487,7 @@ static const struct nla_policy fl_policy[TCA_FLOWER_MAX + 1] = { [TCA_FLOWER_KEY_ENC_IP_TTL_MASK] = { .type = NLA_U8 }, [TCA_FLOWER_KEY_ENC_OPTS] = { .type = NLA_NESTED }, [TCA_FLOWER_KEY_ENC_OPTS_MASK] = { .type = NLA_NESTED }, + [TCA_FLOWER_FLAGS] = { .type = NLA_U32 }, }; static const struct nla_policy @@ -1942,12 +1944,17 @@ static int fl_tmplt_dump(struct sk_buff *skb, struct net *net, void *tmplt_priv) return -EMSGSIZE; } -static void fl_bind_class(void *fh, u32 classid, unsigned long cl) +static void fl_bind_class(void *fh, u32 classid, unsigned long cl, void *q, + unsigned long base) { struct cls_fl_filter *f = fh; - if (f && f->res.classid == classid) - f->res.class = cl; + if (f && f->res.classid == classid) { + if (cl) + __tcf_bind_filter(q, &f->res, base); + else + __tcf_unbind_filter(q, &f->res); + } } static struct tcf_proto_ops cls_fl_ops __read_mostly = { diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c index 29eeeaf3ea4401ea43496f4a9da8916fa744a2ef..cb2c62605fc764dc3cae9eb4c87ed6b56b03e9db 100644 --- a/net/sched/cls_fw.c +++ b/net/sched/cls_fw.c @@ -432,12 +432,17 @@ static int fw_dump(struct net *net, struct tcf_proto *tp, void *fh, return -1; } -static void fw_bind_class(void *fh, u32 classid, unsigned long cl) +static void fw_bind_class(void *fh, u32 classid, unsigned long cl, void *q, + unsigned long base) { struct fw_filter *f = fh; - if (f && f->res.classid == classid) - f->res.class = cl; + if (f && f->res.classid == classid) { + if (cl) + __tcf_bind_filter(q, &f->res, base); + else + __tcf_unbind_filter(q, &f->res); + } } static struct tcf_proto_ops cls_fw_ops __read_mostly = { diff --git a/net/sched/cls_matchall.c b/net/sched/cls_matchall.c index 621bc1d5b0579043f9f632a90e6b0ae9d3c3dedb..74863b0ff69427629227ac0277f5d0043121c719 100644 --- a/net/sched/cls_matchall.c +++ b/net/sched/cls_matchall.c @@ -137,6 +137,7 @@ static void *mall_get(struct tcf_proto *tp, u32 handle) static const struct nla_policy mall_policy[TCA_MATCHALL_MAX + 1] = { [TCA_MATCHALL_UNSPEC] = { .type = NLA_UNSPEC }, [TCA_MATCHALL_CLASSID] = { .type = NLA_U32 }, + [TCA_MATCHALL_FLAGS] = { .type = NLA_U32 }, }; static int mall_set_parms(struct net *net, struct tcf_proto *tp, @@ -310,12 +311,17 @@ static int mall_dump(struct net *net, struct tcf_proto *tp, void *fh, return -1; } -static void mall_bind_class(void *fh, u32 classid, unsigned long cl) +static void mall_bind_class(void *fh, u32 classid, unsigned long cl, void *q, + unsigned long base) { struct cls_mall_head *head = fh; - if (head && head->res.classid == classid) - head->res.class = cl; + if (head && head->res.classid == classid) { + if (cl) + __tcf_bind_filter(q, &head->res, base); + else + __tcf_unbind_filter(q, &head->res); + } } static struct tcf_proto_ops cls_mall_ops __read_mostly = { diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index 0404aa5fa7cbb91a008b75a5ce3ff2f1b563d00e..37ae23db4a440844c24489acaf7fda64ca65b5c0 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c @@ -645,12 +645,17 @@ static int route4_dump(struct net *net, struct tcf_proto *tp, void *fh, return -1; } -static void route4_bind_class(void *fh, u32 classid, unsigned long cl) +static void route4_bind_class(void *fh, u32 classid, unsigned long cl, void *q, + unsigned long base) { struct route4_filter *f = fh; - if (f && f->res.classid == classid) - f->res.class = cl; + if (f && f->res.classid == classid) { + if (cl) + __tcf_bind_filter(q, &f->res, base); + else + __tcf_unbind_filter(q, &f->res); + } } static struct tcf_proto_ops cls_route4_ops __read_mostly = { diff --git a/net/sched/cls_rsvp.h b/net/sched/cls_rsvp.h index e9ccf7daea7d84eeefe5e872b3497a6f0edc7ea9..eb1dd2afc5a129473d05b571ff3d181b13cc4bd5 100644 --- a/net/sched/cls_rsvp.h +++ b/net/sched/cls_rsvp.h @@ -466,10 +466,8 @@ static u32 gen_tunnel(struct rsvp_head *data) static const struct nla_policy rsvp_policy[TCA_RSVP_MAX + 1] = { [TCA_RSVP_CLASSID] = { .type = NLA_U32 }, - [TCA_RSVP_DST] = { .type = NLA_BINARY, - .len = RSVP_DST_LEN * sizeof(u32) }, - [TCA_RSVP_SRC] = { .type = NLA_BINARY, - .len = RSVP_DST_LEN * sizeof(u32) }, + [TCA_RSVP_DST] = { .len = RSVP_DST_LEN * sizeof(u32) }, + [TCA_RSVP_SRC] = { .len = RSVP_DST_LEN * sizeof(u32) }, [TCA_RSVP_PINFO] = { .len = sizeof(struct tc_rsvp_pinfo) }, }; @@ -736,12 +734,17 @@ static int rsvp_dump(struct net *net, struct tcf_proto *tp, void *fh, return -1; } -static void rsvp_bind_class(void *fh, u32 classid, unsigned long cl) +static void rsvp_bind_class(void *fh, u32 classid, unsigned long cl, void *q, + unsigned long base) { struct rsvp_filter *f = fh; - if (f && f->res.classid == classid) - f->res.class = cl; + if (f && f->res.classid == classid) { + if (cl) + __tcf_bind_filter(q, &f->res, base); + else + __tcf_unbind_filter(q, &f->res); + } } static struct tcf_proto_ops RSVP_OPS __read_mostly = { diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c index 38bb882bb9587e57195f37ac81b3da8bdec81cdd..db4b5d9ffaf7db99b7f37ebf559a9d8296e9bbce 100644 --- a/net/sched/cls_tcindex.c +++ b/net/sched/cls_tcindex.c @@ -333,12 +333,31 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base, cp->fall_through = p->fall_through; cp->tp = tp; + if (tb[TCA_TCINDEX_HASH]) + cp->hash = nla_get_u32(tb[TCA_TCINDEX_HASH]); + + if (tb[TCA_TCINDEX_MASK]) + cp->mask = nla_get_u16(tb[TCA_TCINDEX_MASK]); + + if (tb[TCA_TCINDEX_SHIFT]) + cp->shift = nla_get_u32(tb[TCA_TCINDEX_SHIFT]); + + if (!cp->hash) { + /* Hash not specified, use perfect hash if the upper limit + * of the hashing index is below the threshold. + */ + if ((cp->mask >> cp->shift) < PERFECT_HASH_THRESHOLD) + cp->hash = (cp->mask >> cp->shift) + 1; + else + cp->hash = DEFAULT_HASH_SIZE; + } + if (p->perfect) { int i; if (tcindex_alloc_perfect_hash(net, cp) < 0) goto errout; - for (i = 0; i < cp->hash; i++) + for (i = 0; i < min(cp->hash, p->hash); i++) cp->perfect[i].res = p->perfect[i].res; balloc = 1; } @@ -346,19 +365,10 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base, err = tcindex_filter_result_init(&new_filter_result); if (err < 0) - goto errout1; + goto errout_alloc; if (old_r) cr = r->res; - if (tb[TCA_TCINDEX_HASH]) - cp->hash = nla_get_u32(tb[TCA_TCINDEX_HASH]); - - if (tb[TCA_TCINDEX_MASK]) - cp->mask = nla_get_u16(tb[TCA_TCINDEX_MASK]); - - if (tb[TCA_TCINDEX_SHIFT]) - cp->shift = nla_get_u32(tb[TCA_TCINDEX_SHIFT]); - err = -EBUSY; /* Hash already allocated, make sure that we still meet the @@ -376,16 +386,6 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base, if (tb[TCA_TCINDEX_FALL_THROUGH]) cp->fall_through = nla_get_u32(tb[TCA_TCINDEX_FALL_THROUGH]); - if (!cp->hash) { - /* Hash not specified, use perfect hash if the upper limit - * of the hashing index is below the threshold. - */ - if ((cp->mask >> cp->shift) < PERFECT_HASH_THRESHOLD) - cp->hash = (cp->mask >> cp->shift) + 1; - else - cp->hash = DEFAULT_HASH_SIZE; - } - if (!cp->perfect && !cp->h) cp->alloc_hash = cp->hash; @@ -484,7 +484,6 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base, tcindex_free_perfect_hash(cp); else if (balloc == 2) kfree(cp->h); -errout1: tcf_exts_destroy(&new_filter_result.exts); errout: kfree(cp); @@ -652,12 +651,17 @@ static int tcindex_dump(struct net *net, struct tcf_proto *tp, void *fh, return -1; } -static void tcindex_bind_class(void *fh, u32 classid, unsigned long cl) +static void tcindex_bind_class(void *fh, u32 classid, unsigned long cl, + void *q, unsigned long base) { struct tcindex_filter_result *r = fh; - if (r && r->res.classid == classid) - r->res.class = cl; + if (r && r->res.classid == classid) { + if (cl) + __tcf_bind_filter(q, &r->res, base); + else + __tcf_unbind_filter(q, &r->res); + } } static struct tcf_proto_ops cls_tcindex_ops __read_mostly = { diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index b2c3406a2cf292d93aa09280e19a5e149fc0673f..fe246e03fcd9c4134f642cda6a559289028022e5 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -1315,12 +1315,17 @@ static int u32_reoffload(struct tcf_proto *tp, bool add, tc_setup_cb_t *cb, return 0; } -static void u32_bind_class(void *fh, u32 classid, unsigned long cl) +static void u32_bind_class(void *fh, u32 classid, unsigned long cl, void *q, + unsigned long base) { struct tc_u_knode *n = fh; - if (n && n->res.classid == classid) - n->res.class = cl; + if (n && n->res.classid == classid) { + if (cl) + __tcf_bind_filter(q, &n->res, base); + else + __tcf_unbind_filter(q, &n->res); + } } static int u32_dump(struct net *net, struct tcf_proto *tp, void *fh, diff --git a/net/sched/ematch.c b/net/sched/ematch.c index 1331a4c2d8ff55fdc802b85b819939836feda4e1..113a133ee54403264db6bcd3b0e390ef42168ca2 100644 --- a/net/sched/ematch.c +++ b/net/sched/ematch.c @@ -242,6 +242,9 @@ static int tcf_em_validate(struct tcf_proto *tp, goto errout; if (em->ops->change) { + err = -EINVAL; + if (em_hdr->flags & TCF_EM_SIMPLE) + goto errout; err = em->ops->change(net, data, data_len, em); if (err < 0) goto errout; @@ -267,12 +270,12 @@ static int tcf_em_validate(struct tcf_proto *tp, } em->data = (unsigned long) v; } + em->datalen = data_len; } } em->matchid = em_hdr->matchid; em->flags = em_hdr->flags; - em->datalen = data_len; em->net = net; err = 0; diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 84fdc4857771bf62fc66f4c858f75b58db95de7a..39e319d04bb87b5ee98d5ec0935c13f76ace3f02 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -1803,8 +1803,9 @@ static int tclass_del_notify(struct net *net, struct tcf_bind_args { struct tcf_walker w; - u32 classid; + unsigned long base; unsigned long cl; + u32 classid; }; static int tcf_node_bind(struct tcf_proto *tp, void *n, struct tcf_walker *arg) @@ -1815,7 +1816,7 @@ static int tcf_node_bind(struct tcf_proto *tp, void *n, struct tcf_walker *arg) struct Qdisc *q = tcf_block_q(tp->chain->block); sch_tree_lock(q); - tp->ops->bind_class(n, a->classid, a->cl); + tp->ops->bind_class(n, a->classid, a->cl, q, a->base); sch_tree_unlock(q); } return 0; @@ -1846,6 +1847,7 @@ static void tc_bind_tclass(struct Qdisc *q, u32 portid, u32 clid, arg.w.fn = tcf_node_bind; arg.classid = clid; + arg.base = cl; arg.cl = new_cl; tp->ops->walk(tp, &arg.w); } diff --git a/net/sched/sch_cbs.c b/net/sched/sch_cbs.c index e26a24017faa6b15d1f6100350d9c39b539ce50b..940e72d6db1857b5401857cbba38983fed46a196 100644 --- a/net/sched/sch_cbs.c +++ b/net/sched/sch_cbs.c @@ -61,16 +61,20 @@ #include #include #include +#include #include #include #include +static LIST_HEAD(cbs_list); +static DEFINE_SPINLOCK(cbs_list_lock); + #define BYTES_PER_KBIT (1000LL / 8) struct cbs_sched_data { bool offload; int queue; - s64 port_rate; /* in bytes/s */ + atomic64_t port_rate; /* in bytes/s */ s64 last; /* timestamp in ns */ s64 credits; /* in bytes */ s32 locredit; /* in bytes */ @@ -82,6 +86,7 @@ struct cbs_sched_data { struct sk_buff **to_free); struct sk_buff *(*dequeue)(struct Qdisc *sch); struct Qdisc *qdisc; + struct list_head cbs_list; }; static int cbs_child_enqueue(struct sk_buff *skb, struct Qdisc *sch, @@ -206,7 +211,8 @@ static struct sk_buff *cbs_dequeue_soft(struct Qdisc *sch) /* As sendslope is a negative number, this will decrease the * amount of q->credits. */ - credits = credits_from_len(len, q->sendslope, q->port_rate); + credits = credits_from_len(len, q->sendslope, + atomic64_read(&q->port_rate)); credits += q->credits; q->credits = max_t(s64, credits, q->locredit); @@ -293,6 +299,58 @@ static int cbs_enable_offload(struct net_device *dev, struct cbs_sched_data *q, return 0; } +static void cbs_set_port_rate(struct net_device *dev, struct cbs_sched_data *q) +{ + struct ethtool_link_ksettings ecmd; + int speed = SPEED_10; + int port_rate = -1; + int err; + + err = __ethtool_get_link_ksettings(dev, &ecmd); + if (err < 0) + goto skip; + + if (ecmd.base.speed && ecmd.base.speed != SPEED_UNKNOWN) + speed = ecmd.base.speed; + +skip: + port_rate = speed * 1000 * BYTES_PER_KBIT; + + atomic64_set(&q->port_rate, port_rate); + netdev_dbg(dev, "cbs: set %s's port_rate to: %lld, linkspeed: %d\n", + dev->name, (long long)atomic64_read(&q->port_rate), + ecmd.base.speed); +} + +static int cbs_dev_notifier(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + struct cbs_sched_data *q; + struct net_device *qdev; + bool found = false; + + ASSERT_RTNL(); + + if (event != NETDEV_UP && event != NETDEV_CHANGE) + return NOTIFY_DONE; + + spin_lock(&cbs_list_lock); + list_for_each_entry(q, &cbs_list, cbs_list) { + qdev = qdisc_dev(q->qdisc); + if (qdev == dev) { + found = true; + break; + } + } + spin_unlock(&cbs_list_lock); + + if (found) + cbs_set_port_rate(dev, q); + + return NOTIFY_DONE; +} + static int cbs_change(struct Qdisc *sch, struct nlattr *opt, struct netlink_ext_ack *extack) { @@ -314,16 +372,7 @@ static int cbs_change(struct Qdisc *sch, struct nlattr *opt, qopt = nla_data(tb[TCA_CBS_PARMS]); if (!qopt->offload) { - struct ethtool_link_ksettings ecmd; - s64 link_speed; - - if (!__ethtool_get_link_ksettings(dev, &ecmd)) - link_speed = ecmd.base.speed; - else - link_speed = SPEED_1000; - - q->port_rate = link_speed * 1000 * BYTES_PER_KBIT; - + cbs_set_port_rate(dev, q); cbs_disable_offload(dev, q); } else { err = cbs_enable_offload(dev, q, qopt, extack); @@ -346,6 +395,7 @@ static int cbs_init(struct Qdisc *sch, struct nlattr *opt, { struct cbs_sched_data *q = qdisc_priv(sch); struct net_device *dev = qdisc_dev(sch); + int err; if (!opt) { NL_SET_ERR_MSG(extack, "Missing CBS qdisc options which are mandatory"); @@ -366,7 +416,17 @@ static int cbs_init(struct Qdisc *sch, struct nlattr *opt, qdisc_watchdog_init(&q->watchdog, sch); - return cbs_change(sch, opt, extack); + err = cbs_change(sch, opt, extack); + if (err) + return err; + + if (!q->offload) { + spin_lock(&cbs_list_lock); + list_add(&q->cbs_list, &cbs_list); + spin_unlock(&cbs_list_lock); + } + + return 0; } static void cbs_destroy(struct Qdisc *sch) @@ -374,8 +434,11 @@ static void cbs_destroy(struct Qdisc *sch) struct cbs_sched_data *q = qdisc_priv(sch); struct net_device *dev = qdisc_dev(sch); - qdisc_watchdog_cancel(&q->watchdog); + spin_lock(&cbs_list_lock); + list_del(&q->cbs_list); + spin_unlock(&cbs_list_lock); + qdisc_watchdog_cancel(&q->watchdog); cbs_disable_offload(dev, q); if (q->qdisc) @@ -486,14 +549,29 @@ static struct Qdisc_ops cbs_qdisc_ops __read_mostly = { .owner = THIS_MODULE, }; +static struct notifier_block cbs_device_notifier = { + .notifier_call = cbs_dev_notifier, +}; + static int __init cbs_module_init(void) { - return register_qdisc(&cbs_qdisc_ops); + int err; + + err = register_netdevice_notifier(&cbs_device_notifier); + if (err) + return err; + + err = register_qdisc(&cbs_qdisc_ops); + if (err) + unregister_netdevice_notifier(&cbs_device_notifier); + + return err; } static void __exit cbs_module_exit(void) { unregister_qdisc(&cbs_qdisc_ops); + unregister_netdevice_notifier(&cbs_device_notifier); } module_init(cbs_module_init) module_exit(cbs_module_exit) diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 15f8f24c190d4600a61f07a6acda86e29c433275..014a28d8dd4fafd54b07887a95058809c4ce0117 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -436,8 +436,7 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, struct netem_skb_cb *cb; struct sk_buff *skb2; struct sk_buff *segs = NULL; - unsigned int len = 0, last_len, prev_len = qdisc_pkt_len(skb); - int nb = 0; + unsigned int prev_len = qdisc_pkt_len(skb); int count = 1; int rc = NET_XMIT_SUCCESS; int rc_drop = NET_XMIT_DROP; @@ -494,6 +493,7 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, segs = netem_segment(skb, sch, to_free); if (!segs) return rc_drop; + qdisc_skb_cb(segs)->pkt_len = segs->len; } else { segs = skb; } @@ -509,6 +509,7 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) { qdisc_drop(skb, sch, to_free); + skb = NULL; goto finish_segs; } @@ -583,6 +584,12 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, finish_segs: if (segs) { + unsigned int len, last_len; + int nb; + + len = skb ? skb->len : 0; + nb = skb ? 1 : 0; + while (segs) { skb2 = segs->next; segs->next = NULL; @@ -598,9 +605,10 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, } segs = skb2; } - sch->q.qlen += nb; - if (nb > 1) - qdisc_tree_reduce_backlog(sch, 1 - nb, prev_len - len); + /* Parent qdiscs accounted for 1 skb of size @prev_len */ + qdisc_tree_reduce_backlog(sch, -(nb - 1), -(len - prev_len)); + } else if (!skb) { + return NET_XMIT_DROP; } return NET_XMIT_SUCCESS; } diff --git a/net/sctp/input.c b/net/sctp/input.c index bfe29158afccb9d2c9998981c617f0ec3b28af73..f64d882c86985e46cc8207702f8972c0368fb96b 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -255,7 +255,7 @@ int sctp_rcv(struct sk_buff *skb) bh_lock_sock(sk); } - if (sock_owned_by_user(sk)) { + if (sock_owned_by_user(sk) || !sctp_newsk_ready(sk)) { if (sctp_add_backlog(sk, skb)) { bh_unlock_sock(sk); sctp_chunk_free(chunk); @@ -333,7 +333,7 @@ int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb) local_bh_disable(); bh_lock_sock(sk); - if (sock_owned_by_user(sk)) { + if (sock_owned_by_user(sk) || !sctp_newsk_ready(sk)) { if (sk_add_backlog(sk, skb, sk->sk_rcvbuf)) sctp_chunk_free(chunk); else @@ -348,7 +348,13 @@ int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb) if (backloged) return 0; } else { - sctp_inq_push(inqueue, chunk); + if (!sctp_newsk_ready(sk)) { + if (!sk_add_backlog(sk, skb, sk->sk_rcvbuf)) + return 0; + sctp_chunk_free(chunk); + } else { + sctp_inq_push(inqueue, chunk); + } } done: diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 559f09ac0b2201f4f0dd467c5d823c3c56462472..9f4d325f3a79dea54da35c12cc2a53ac42710959 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -185,6 +185,16 @@ static inline bool sctp_chunk_length_valid(struct sctp_chunk *chunk, return true; } +/* Check for format error in an ABORT chunk */ +static inline bool sctp_err_chunk_valid(struct sctp_chunk *chunk) +{ + struct sctp_errhdr *err; + + sctp_walk_errors(err, chunk->chunk_hdr); + + return (void *)err == (void *)chunk->chunk_end; +} + /********************************************************** * These are the state functions for handling chunk events. **********************************************************/ @@ -2270,6 +2280,9 @@ enum sctp_disposition sctp_sf_shutdown_pending_abort( sctp_bind_addr_state(&asoc->base.bind_addr, &chunk->dest)) return sctp_sf_discard_chunk(net, ep, asoc, type, arg, commands); + if (!sctp_err_chunk_valid(chunk)) + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); + return __sctp_sf_do_9_1_abort(net, ep, asoc, type, arg, commands); } @@ -2313,6 +2326,9 @@ enum sctp_disposition sctp_sf_shutdown_sent_abort( sctp_bind_addr_state(&asoc->base.bind_addr, &chunk->dest)) return sctp_sf_discard_chunk(net, ep, asoc, type, arg, commands); + if (!sctp_err_chunk_valid(chunk)) + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); + /* Stop the T2-shutdown timer. */ sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, SCTP_TO(SCTP_EVENT_TIMEOUT_T2_SHUTDOWN)); @@ -2580,6 +2596,9 @@ enum sctp_disposition sctp_sf_do_9_1_abort( sctp_bind_addr_state(&asoc->base.bind_addr, &chunk->dest)) return sctp_sf_discard_chunk(net, ep, asoc, type, arg, commands); + if (!sctp_err_chunk_valid(chunk)) + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); + return __sctp_sf_do_9_1_abort(net, ep, asoc, type, arg, commands); } @@ -2597,16 +2616,8 @@ static enum sctp_disposition __sctp_sf_do_9_1_abort( /* See if we have an error cause code in the chunk. */ len = ntohs(chunk->chunk_hdr->length); - if (len >= sizeof(struct sctp_chunkhdr) + sizeof(struct sctp_errhdr)) { - struct sctp_errhdr *err; - - sctp_walk_errors(err, chunk->chunk_hdr); - if ((void *)err != (void *)chunk->chunk_end) - return sctp_sf_pdiscard(net, ep, asoc, type, arg, - commands); - + if (len >= sizeof(struct sctp_chunkhdr) + sizeof(struct sctp_errhdr)) error = ((struct sctp_errhdr *)chunk->skb->data)->cause; - } sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, SCTP_ERROR(ECONNRESET)); /* ASSOC_FAILED will DELETE_TCB. */ diff --git a/net/smc/smc_clc.c b/net/smc/smc_clc.c index 52241d679cc91cd3b6ec59a4388d33b68c76f1f1..aa9a17ac1f7b35749c5158a317b56a4998447e43 100644 --- a/net/smc/smc_clc.c +++ b/net/smc/smc_clc.c @@ -364,7 +364,9 @@ int smc_clc_send_decline(struct smc_sock *smc, u32 peer_diag_info) dclc.hdr.length = htons(sizeof(struct smc_clc_msg_decline)); dclc.hdr.version = SMC_CLC_V1; dclc.hdr.flag = (peer_diag_info == SMC_CLC_DECL_SYNCERR) ? 1 : 0; - memcpy(dclc.id_for_peer, local_systemid, sizeof(local_systemid)); + if (smc->conn.lgr && !smc->conn.lgr->is_smcd) + memcpy(dclc.id_for_peer, local_systemid, + sizeof(local_systemid)); dclc.peer_diagnosis = htonl(peer_diag_info); memcpy(dclc.trl.eyecatcher, SMC_EYECATCHER, sizeof(SMC_EYECATCHER)); diff --git a/net/smc/smc_diag.c b/net/smc/smc_diag.c index dbf64a93d68add6f834cb20533ffc31b82fa4f9e..2379a02c319d704b89a684c5160ad26ac8ebdb7b 100644 --- a/net/smc/smc_diag.c +++ b/net/smc/smc_diag.c @@ -38,21 +38,19 @@ static void smc_diag_msg_common_fill(struct smc_diag_msg *r, struct sock *sk) { struct smc_sock *smc = smc_sk(sk); + memset(r, 0, sizeof(*r)); + r->diag_family = sk->sk_family; + sock_diag_save_cookie(sk, r->id.idiag_cookie); if (!smc->clcsock) return; r->id.idiag_sport = htons(smc->clcsock->sk->sk_num); r->id.idiag_dport = smc->clcsock->sk->sk_dport; r->id.idiag_if = smc->clcsock->sk->sk_bound_dev_if; - sock_diag_save_cookie(sk, r->id.idiag_cookie); if (sk->sk_protocol == SMCPROTO_SMC) { - r->diag_family = PF_INET; - memset(&r->id.idiag_src, 0, sizeof(r->id.idiag_src)); - memset(&r->id.idiag_dst, 0, sizeof(r->id.idiag_dst)); r->id.idiag_src[0] = smc->clcsock->sk->sk_rcv_saddr; r->id.idiag_dst[0] = smc->clcsock->sk->sk_daddr; #if IS_ENABLED(CONFIG_IPV6) } else if (sk->sk_protocol == SMCPROTO_SMC6) { - r->diag_family = PF_INET6; memcpy(&r->id.idiag_src, &smc->clcsock->sk->sk_v6_rcv_saddr, sizeof(smc->clcsock->sk->sk_v6_rcv_saddr)); memcpy(&r->id.idiag_dst, &smc->clcsock->sk->sk_v6_daddr, diff --git a/net/smc/smc_rx.c b/net/smc/smc_rx.c index bbcf0fe4ae10f631a84a2379d1c075f087db7037..36340912df48afc1e8faeb803251f4819364db3c 100644 --- a/net/smc/smc_rx.c +++ b/net/smc/smc_rx.c @@ -212,8 +212,7 @@ int smc_rx_wait(struct smc_sock *smc, long *timeo, rc = sk_wait_event(sk, timeo, sk->sk_err || sk->sk_shutdown & RCV_SHUTDOWN || - fcrit(conn) || - smc_cdc_rxed_any_close_or_senddone(conn), + fcrit(conn), &wait); remove_wait_queue(sk_sleep(sk), &wait); sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk); @@ -263,6 +262,18 @@ static int smc_rx_recv_urg(struct smc_sock *smc, struct msghdr *msg, int len, return -EAGAIN; } +static bool smc_rx_recvmsg_data_available(struct smc_sock *smc) +{ + struct smc_connection *conn = &smc->conn; + + if (smc_rx_data_available(conn)) + return true; + else if (conn->urg_state == SMC_URG_VALID) + /* we received a single urgent Byte - skip */ + smc_rx_update_cons(smc, 0); + return false; +} + /* smc_rx_recvmsg - receive data from RMBE * @msg: copy data to receive buffer * @pipe: copy data to pipe if set - indicates splice() call @@ -304,16 +315,18 @@ int smc_rx_recvmsg(struct smc_sock *smc, struct msghdr *msg, if (read_done >= target || (pipe && read_done)) break; - if (atomic_read(&conn->bytes_to_rcv)) + if (smc_rx_recvmsg_data_available(smc)) goto copy; - else if (conn->urg_state == SMC_URG_VALID) - /* we received a single urgent Byte - skip */ - smc_rx_update_cons(smc, 0); if (sk->sk_shutdown & RCV_SHUTDOWN || - smc_cdc_rxed_any_close_or_senddone(conn) || - conn->local_tx_ctrl.conn_state_flags.peer_conn_abort) + conn->local_tx_ctrl.conn_state_flags.peer_conn_abort) { + /* smc_cdc_msg_recv_action() could have run after + * above smc_rx_recvmsg_data_available() + */ + if (smc_rx_recvmsg_data_available(smc)) + goto copy; break; + } if (read_done) { if (sk->sk_err || diff --git a/net/socket.c b/net/socket.c index 4a4c11d5d6219be71680737cf9e8e5b5e9887ee6..224aa325a53ad935d3a83997af1add3b7ee7c042 100644 --- a/net/socket.c +++ b/net/socket.c @@ -3261,6 +3261,7 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock, case SIOCSARP: case SIOCGARP: case SIOCDARP: + case SIOCOUTQNSD: case SIOCATMARK: return sock_do_ioctl(net, sock, cmd, arg); } diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index 1a65f88d021a57beea3f3b3fe869aff53ceafa33..68830e88b6e91749cea5b001f4bc36d63b7292eb 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -1054,24 +1054,32 @@ gss_read_verf(struct rpc_gss_wire_cred *gc, return 0; } -/* Ok this is really heavily depending on a set of semantics in - * how rqstp is set up by svc_recv and pages laid down by the - * server when reading a request. We are basically guaranteed that - * the token lays all down linearly across a set of pages, starting - * at iov_base in rq_arg.head[0] which happens to be the first of a - * set of pages stored in rq_pages[]. - * rq_arg.head[0].iov_base will provide us the page_base to pass - * to the upcall. - */ -static inline int -gss_read_proxy_verf(struct svc_rqst *rqstp, - struct rpc_gss_wire_cred *gc, __be32 *authp, - struct xdr_netobj *in_handle, - struct gssp_in_token *in_token) +static void gss_free_in_token_pages(struct gssp_in_token *in_token) { - struct kvec *argv = &rqstp->rq_arg.head[0]; u32 inlen; - int res; + int i; + + i = 0; + inlen = in_token->page_len; + while (inlen) { + if (in_token->pages[i]) + put_page(in_token->pages[i]); + inlen -= inlen > PAGE_SIZE ? PAGE_SIZE : inlen; + } + + kfree(in_token->pages); + in_token->pages = NULL; +} + +static int gss_read_proxy_verf(struct svc_rqst *rqstp, + struct rpc_gss_wire_cred *gc, __be32 *authp, + struct xdr_netobj *in_handle, + struct gssp_in_token *in_token) +{ + struct kvec *argv = &rqstp->rq_arg.head[0]; + unsigned int page_base, length; + int pages, i, res; + size_t inlen; res = gss_read_common_verf(gc, argv, authp, in_handle); if (res) @@ -1081,10 +1089,36 @@ gss_read_proxy_verf(struct svc_rqst *rqstp, if (inlen > (argv->iov_len + rqstp->rq_arg.page_len)) return SVC_DENIED; - in_token->pages = rqstp->rq_pages; - in_token->page_base = (ulong)argv->iov_base & ~PAGE_MASK; + pages = DIV_ROUND_UP(inlen, PAGE_SIZE); + in_token->pages = kcalloc(pages, sizeof(struct page *), GFP_KERNEL); + if (!in_token->pages) + return SVC_DENIED; + in_token->page_base = 0; in_token->page_len = inlen; + for (i = 0; i < pages; i++) { + in_token->pages[i] = alloc_page(GFP_KERNEL); + if (!in_token->pages[i]) { + gss_free_in_token_pages(in_token); + return SVC_DENIED; + } + } + length = min_t(unsigned int, inlen, argv->iov_len); + memcpy(page_address(in_token->pages[0]), argv->iov_base, length); + inlen -= length; + + i = 1; + page_base = rqstp->rq_arg.page_base; + while (inlen) { + length = min_t(unsigned int, inlen, PAGE_SIZE); + memcpy(page_address(in_token->pages[i]), + page_address(rqstp->rq_arg.pages[i]) + page_base, + length); + + inlen -= length; + page_base = 0; + i++; + } return 0; } @@ -1190,6 +1224,7 @@ static int gss_proxy_save_rsc(struct cache_detail *cd, dprintk("RPC: No creds found!\n"); goto out; } else { + struct timespec64 boot; /* steal creds */ rsci.cred = ud->creds; @@ -1210,6 +1245,9 @@ static int gss_proxy_save_rsc(struct cache_detail *cd, &expiry, GFP_KERNEL); if (status) goto out; + + getboottime64(&boot); + expiry -= boot.tv_sec; } rsci.h.expiry_time = expiry; @@ -1259,8 +1297,11 @@ static int svcauth_gss_proxy_init(struct svc_rqst *rqstp, break; case GSS_S_COMPLETE: status = gss_proxy_save_rsc(sn->rsc_cache, &ud, &handle); - if (status) + if (status) { + pr_info("%s: gss_proxy_save_rsc failed (%d)\n", + __func__, status); goto out; + } cli_handle.data = (u8 *)&handle; cli_handle.len = sizeof(handle); break; @@ -1271,15 +1312,20 @@ static int svcauth_gss_proxy_init(struct svc_rqst *rqstp, /* Got an answer to the upcall; use it: */ if (gss_write_init_verf(sn->rsc_cache, rqstp, - &cli_handle, &ud.major_status)) + &cli_handle, &ud.major_status)) { + pr_info("%s: gss_write_init_verf failed\n", __func__); goto out; + } if (gss_write_resv(resv, PAGE_SIZE, &cli_handle, &ud.out_token, - ud.major_status, ud.minor_status)) + ud.major_status, ud.minor_status)) { + pr_info("%s: gss_write_resv failed\n", __func__); goto out; + } ret = SVC_COMPLETE; out: + gss_free_in_token_pages(&ud.in_token); gssp_free_upcall_data(&ud); return ret; } diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index 5ddbf227e7c65cf31380aacf4da3e60aa30c4c06..ef1f3d076af96bd69832bf3c88f7334213394e41 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -248,6 +248,7 @@ rpcrdma_conn_upcall(struct rdma_cm_id *id, struct rdma_cm_event *event) ia->ri_device->name, rpcrdma_addrstr(xprt), rpcrdma_portstr(xprt)); #endif + init_completion(&ia->ri_remove_done); set_bit(RPCRDMA_IAF_REMOVING, &ia->ri_flags); ep->rep_connected = -ENODEV; xprt_force_disconnect(&xprt->rx_xprt); @@ -306,7 +307,6 @@ rpcrdma_create_id(struct rpcrdma_xprt *xprt, struct rpcrdma_ia *ia) trace_xprtrdma_conn_start(xprt); init_completion(&ia->ri_done); - init_completion(&ia->ri_remove_done); id = rdma_create_id(xprt->rx_xprt.xprt_net, rpcrdma_conn_upcall, xprt, RDMA_PS_TCP, IB_QPT_RC); @@ -1558,10 +1558,11 @@ rpcrdma_post_recvs(struct rpcrdma_xprt *r_xprt, bool temp) rc = ib_post_recv(r_xprt->rx_ia.ri_id->qp, wr, (const struct ib_recv_wr **)&bad_wr); if (rc) { - for (wr = bad_wr; wr; wr = wr->next) { + for (wr = bad_wr; wr;) { struct rpcrdma_rep *rep; rep = container_of(wr, struct rpcrdma_rep, rr_recv_wr); + wr = wr->next; rpcrdma_recv_buffer_put(rep); --count; } diff --git a/net/tipc/link.c b/net/tipc/link.c index 0fbf8ea18ce04f7d49bd420a49ae036ec3e51725..cc9a0485536b3f682ec64fbef503722aa453f035 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -830,18 +830,31 @@ static int link_schedule_user(struct tipc_link *l, struct tipc_msg *hdr) */ static void link_prepare_wakeup(struct tipc_link *l) { + struct sk_buff_head *wakeupq = &l->wakeupq; + struct sk_buff_head *inputq = l->inputq; struct sk_buff *skb, *tmp; - int imp, i = 0; + struct sk_buff_head tmpq; + int avail[5] = {0,}; + int imp = 0; + + __skb_queue_head_init(&tmpq); - skb_queue_walk_safe(&l->wakeupq, skb, tmp) { + for (; imp <= TIPC_SYSTEM_IMPORTANCE; imp++) + avail[imp] = l->backlog[imp].limit - l->backlog[imp].len; + + skb_queue_walk_safe(wakeupq, skb, tmp) { imp = TIPC_SKB_CB(skb)->chain_imp; - if (l->backlog[imp].len < l->backlog[imp].limit) { - skb_unlink(skb, &l->wakeupq); - skb_queue_tail(l->inputq, skb); - } else if (i++ > 10) { - break; - } + if (avail[imp] <= 0) + continue; + avail[imp]--; + __skb_unlink(skb, wakeupq); + __skb_queue_tail(&tmpq, skb); } + + spin_lock_bh(&inputq->lock); + skb_queue_splice_tail(&tmpq, inputq); + spin_unlock_bh(&inputq->lock); + } void tipc_link_reset(struct tipc_link *l) diff --git a/net/tipc/monitor.c b/net/tipc/monitor.c index 67f69389ec179bff3518b35c9d546685d9d2bc41..23706ee166074ffdd838c7f7a483d4e2fd186018 100644 --- a/net/tipc/monitor.c +++ b/net/tipc/monitor.c @@ -665,6 +665,21 @@ void tipc_mon_delete(struct net *net, int bearer_id) kfree(mon); } +void tipc_mon_reinit_self(struct net *net) +{ + struct tipc_monitor *mon; + int bearer_id; + + for (bearer_id = 0; bearer_id < MAX_BEARERS; bearer_id++) { + mon = tipc_monitor(net, bearer_id); + if (!mon) + continue; + write_lock_bh(&mon->lock); + mon->self->addr = tipc_own_addr(net); + write_unlock_bh(&mon->lock); + } +} + int tipc_nl_monitor_set_threshold(struct net *net, u32 cluster_size) { struct tipc_net *tn = tipc_net(net); diff --git a/net/tipc/monitor.h b/net/tipc/monitor.h index 2a21b93e0d04a204d4f6375647e2dfc9ea94a4f2..ed63d2e650b08309da785bd5d26ac6d4a0f8157d 100644 --- a/net/tipc/monitor.h +++ b/net/tipc/monitor.h @@ -77,6 +77,7 @@ int __tipc_nl_add_monitor(struct net *net, struct tipc_nl_msg *msg, u32 bearer_id); int tipc_nl_add_monitor_peer(struct net *net, struct tipc_nl_msg *msg, u32 bearer_id, u32 *prev_node); +void tipc_mon_reinit_self(struct net *net); extern const int tipc_max_domain_size; #endif diff --git a/net/tipc/name_distr.c b/net/tipc/name_distr.c index e0a3dd424d8c2fc1b93ace436ed6c4d7c58c0dd1..836e629e8f4ab8e1932c04c2f5056595a507313a 100644 --- a/net/tipc/name_distr.c +++ b/net/tipc/name_distr.c @@ -94,8 +94,9 @@ struct sk_buff *tipc_named_publish(struct net *net, struct publication *publ) list_add_tail_rcu(&publ->binding_node, &nt->node_scope); return NULL; } - list_add_tail_rcu(&publ->binding_node, &nt->cluster_scope); - + write_lock_bh(&nt->cluster_scope_lock); + list_add_tail(&publ->binding_node, &nt->cluster_scope); + write_unlock_bh(&nt->cluster_scope_lock); skb = named_prepare_buf(net, PUBLICATION, ITEM_SIZE, 0); if (!skb) { pr_warn("Publication distribution failure\n"); @@ -112,11 +113,13 @@ struct sk_buff *tipc_named_publish(struct net *net, struct publication *publ) */ struct sk_buff *tipc_named_withdraw(struct net *net, struct publication *publ) { + struct name_table *nt = tipc_name_table(net); struct sk_buff *buf; struct distr_item *item; - list_del_rcu(&publ->binding_node); - + write_lock_bh(&nt->cluster_scope_lock); + list_del(&publ->binding_node); + write_unlock_bh(&nt->cluster_scope_lock); if (publ->scope == TIPC_NODE_SCOPE) return NULL; @@ -147,7 +150,7 @@ static void named_distribute(struct net *net, struct sk_buff_head *list, ITEM_SIZE) * ITEM_SIZE; u32 msg_rem = msg_dsz; - list_for_each_entry_rcu(publ, pls, binding_node) { + list_for_each_entry(publ, pls, binding_node) { /* Prepare next buffer: */ if (!skb) { skb = named_prepare_buf(net, PUBLICATION, msg_rem, @@ -189,11 +192,10 @@ void tipc_named_node_up(struct net *net, u32 dnode) __skb_queue_head_init(&head); - rcu_read_lock(); + read_lock_bh(&nt->cluster_scope_lock); named_distribute(net, &head, dnode, &nt->cluster_scope); - rcu_read_unlock(); - tipc_node_xmit(net, &head, dnode, 0); + read_unlock_bh(&nt->cluster_scope_lock); } /** diff --git a/net/tipc/name_table.c b/net/tipc/name_table.c index d72985ca1d55584d906f0f500a8179f3e648b18f..89993afe0fbd38713dd3d0499cc79e6c3e159b4d 100644 --- a/net/tipc/name_table.c +++ b/net/tipc/name_table.c @@ -744,6 +744,7 @@ int tipc_nametbl_init(struct net *net) INIT_LIST_HEAD(&nt->node_scope); INIT_LIST_HEAD(&nt->cluster_scope); + rwlock_init(&nt->cluster_scope_lock); tn->nametbl = nt; spin_lock_init(&tn->nametbl_lock); return 0; diff --git a/net/tipc/name_table.h b/net/tipc/name_table.h index 892bd750b85fa48639b15af9bbeb6d437a7660a7..f79066334cc8f63a4bf71a61955b155c66b019be 100644 --- a/net/tipc/name_table.h +++ b/net/tipc/name_table.h @@ -100,6 +100,7 @@ struct name_table { struct hlist_head services[TIPC_NAMETBL_SIZE]; struct list_head node_scope; struct list_head cluster_scope; + rwlock_t cluster_scope_lock; u32 local_publ_count; }; diff --git a/net/tipc/net.c b/net/tipc/net.c index 7ce1e86b024f09cb7345840d4a4d8e3949c5107d..2e2e938fe4b7b1f43747f7599d8dc582eadfce99 100644 --- a/net/tipc/net.c +++ b/net/tipc/net.c @@ -42,6 +42,7 @@ #include "node.h" #include "bcast.h" #include "netlink.h" +#include "monitor.h" /* * The TIPC locking policy is designed to ensure a very fine locking @@ -136,6 +137,7 @@ static void tipc_net_finalize(struct net *net, u32 addr) tipc_set_node_addr(net, addr); tipc_named_reinit(net); tipc_sk_reinit(net); + tipc_mon_reinit_self(net); tipc_nametbl_publish(net, TIPC_CFG_SRV, addr, addr, TIPC_CLUSTER_SCOPE, 0, addr); } diff --git a/net/tipc/node.c b/net/tipc/node.c index 32556f480a6060e87b41e2a9e9ebe5d118c8429a..e67ffd1949272c76caf4d39a92594e2111065acd 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -810,10 +810,10 @@ static void __tipc_node_link_down(struct tipc_node *n, int *bearer_id, static void tipc_node_link_down(struct tipc_node *n, int bearer_id, bool delete) { struct tipc_link_entry *le = &n->links[bearer_id]; + struct tipc_media_addr *maddr = NULL; struct tipc_link *l = le->link; - struct tipc_media_addr *maddr; - struct sk_buff_head xmitq; int old_bearer_id = bearer_id; + struct sk_buff_head xmitq; if (!l) return; @@ -835,7 +835,8 @@ static void tipc_node_link_down(struct tipc_node *n, int bearer_id, bool delete) tipc_node_write_unlock(n); if (delete) tipc_mon_remove_peer(n->net, n->addr, old_bearer_id); - tipc_bearer_xmit(n->net, bearer_id, &xmitq, maddr); + if (!skb_queue_empty(&xmitq)) + tipc_bearer_xmit(n->net, bearer_id, &xmitq, maddr); tipc_sk_rcv(n->net, &le->inputq); } diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 5841d62ff58070bc940b54cf97b67a717082c2cc..40947ad902225c761e4c085cb2b48dcf89e20bc8 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -501,7 +501,7 @@ static void __tipc_shutdown(struct socket *sock, int error) struct sock *sk = sock->sk; struct tipc_sock *tsk = tipc_sk(sk); struct net *net = sock_net(sk); - long timeout = CONN_TIMEOUT_DEFAULT; + long timeout = msecs_to_jiffies(CONN_TIMEOUT_DEFAULT); u32 dnode = tsk_peer_node(tsk); struct sk_buff *skb; diff --git a/net/tipc/sysctl.c b/net/tipc/sysctl.c index 1a779b1e85100b501c1f0cde29cbfaa0d98fb4eb..40f6d82083d7bbcc3968b0091811519103fba497 100644 --- a/net/tipc/sysctl.c +++ b/net/tipc/sysctl.c @@ -37,6 +37,8 @@ #include +static int zero; +static int one = 1; static struct ctl_table_header *tipc_ctl_hdr; static struct ctl_table tipc_table[] = { @@ -45,14 +47,16 @@ static struct ctl_table tipc_table[] = { .data = &sysctl_tipc_rmem, .maxlen = sizeof(sysctl_tipc_rmem), .mode = 0644, - .proc_handler = proc_dointvec, + .proc_handler = proc_dointvec_minmax, + .extra1 = &one, }, { .procname = "named_timeout", .data = &sysctl_tipc_named_timeout, .maxlen = sizeof(sysctl_tipc_named_timeout), .mode = 0644, - .proc_handler = proc_dointvec, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, }, {} }; diff --git a/net/tls/tls_device.c b/net/tls/tls_device.c index 0a613e0ef3bf9908c38575cb1137c700dd61aefd..8f40bbfd60ea98ad7484ab87e0b998bf8165436f 100644 --- a/net/tls/tls_device.c +++ b/net/tls/tls_device.c @@ -506,7 +506,7 @@ struct tls_record_info *tls_get_record(struct tls_offload_context_tx *context, u32 seq, u64 *p_record_sn) { u64 record_sn = context->hint_record_sn; - struct tls_record_info *info; + struct tls_record_info *info, *last; info = context->retransmit_hint; if (!info || @@ -516,6 +516,25 @@ struct tls_record_info *tls_get_record(struct tls_offload_context_tx *context, */ info = list_first_entry(&context->records_list, struct tls_record_info, list); + + /* send the start_marker record if seq number is before the + * tls offload start marker sequence number. This record is + * required to handle TCP packets which are before TLS offload + * started. + * And if it's not start marker, look if this seq number + * belongs to the list. + */ + if (likely(!tls_record_is_start_marker(info))) { + /* we have the first record, get the last record to see + * if this seq number belongs to the list. + */ + last = list_last_entry(&context->records_list, + struct tls_record_info, list); + + if (!between(seq, tls_record_start_seq(info), + last->end_seq)) + return NULL; + } record_sn = context->unacked_record_sn; } diff --git a/net/tls/tls_device_fallback.c b/net/tls/tls_device_fallback.c index 426dd97725e4a1235eca9cf65cec9eadecf10359..6cf832891b53edb829fc5b63a8ee049d272aef4d 100644 --- a/net/tls/tls_device_fallback.c +++ b/net/tls/tls_device_fallback.c @@ -208,6 +208,10 @@ static void complete_skb(struct sk_buff *nskb, struct sk_buff *skb, int headln) update_chksum(nskb, headln); + /* sock_efree means skb must gone through skb_orphan_partial() */ + if (nskb->destructor == sock_efree) + return; + delta = nskb->truesize - skb->truesize; if (likely(delta < 0)) WARN_ON_ONCE(refcount_sub_and_test(-delta, &sk->sk_wmem_alloc)); diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index d2d6ff0c6265d71c6deec0c1ccf42d242a24946c..2318e2e2748f45f2e6f2b8aecef235098beb37b8 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -649,6 +649,9 @@ static __poll_t unix_poll(struct file *, struct socket *, poll_table *); static __poll_t unix_dgram_poll(struct file *, struct socket *, poll_table *); static int unix_ioctl(struct socket *, unsigned int, unsigned long); +#ifdef CONFIG_COMPAT +static int unix_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg); +#endif static int unix_shutdown(struct socket *, int); static int unix_stream_sendmsg(struct socket *, struct msghdr *, size_t); static int unix_stream_recvmsg(struct socket *, struct msghdr *, size_t, int); @@ -690,6 +693,9 @@ static const struct proto_ops unix_stream_ops = { .getname = unix_getname, .poll = unix_poll, .ioctl = unix_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = unix_compat_ioctl, +#endif .listen = unix_listen, .shutdown = unix_shutdown, .setsockopt = sock_no_setsockopt, @@ -713,6 +719,9 @@ static const struct proto_ops unix_dgram_ops = { .getname = unix_getname, .poll = unix_dgram_poll, .ioctl = unix_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = unix_compat_ioctl, +#endif .listen = sock_no_listen, .shutdown = unix_shutdown, .setsockopt = sock_no_setsockopt, @@ -735,6 +744,9 @@ static const struct proto_ops unix_seqpacket_ops = { .getname = unix_getname, .poll = unix_dgram_poll, .ioctl = unix_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = unix_compat_ioctl, +#endif .listen = unix_listen, .shutdown = unix_shutdown, .setsockopt = sock_no_setsockopt, @@ -2646,6 +2658,13 @@ static int unix_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) return err; } +#ifdef CONFIG_COMPAT +static int unix_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + return unix_ioctl(sock, cmd, (unsigned long)compat_ptr(arg)); +} +#endif + static __poll_t unix_poll(struct file *file, struct socket *sock, poll_table *wait) { struct sock *sk = sock->sk; diff --git a/net/vmw_vsock/hyperv_transport.c b/net/vmw_vsock/hyperv_transport.c index 70350dc673669291e8ce8938a4d077573d4a941d..db6ca51228d2f0473ac4cb7071384b5eb74c9b60 100644 --- a/net/vmw_vsock/hyperv_transport.c +++ b/net/vmw_vsock/hyperv_transport.c @@ -144,28 +144,15 @@ struct hvsock { **************************************************************************** * The only valid Service GUIDs, from the perspectives of both the host and * * Linux VM, that can be connected by the other end, must conform to this * - * format: -facb-11e6-bd58-64006a7986d3, and the "port" must be in * - * this range [0, 0x7FFFFFFF]. * + * format: -facb-11e6-bd58-64006a7986d3. * **************************************************************************** * * When we write apps on the host to connect(), the GUID ServiceID is used. * When we write apps in Linux VM to connect(), we only need to specify the * port and the driver will form the GUID and use that to request the host. * - * From the perspective of Linux VM: - * 1. the local ephemeral port (i.e. the local auto-bound port when we call - * connect() without explicit bind()) is generated by __vsock_bind_stream(), - * and the range is [1024, 0xFFFFFFFF). - * 2. the remote ephemeral port (i.e. the auto-generated remote port for - * a connect request initiated by the host's connect()) is generated by - * hvs_remote_addr_init() and the range is [0x80000000, 0xFFFFFFFF). */ -#define MAX_LISTEN_PORT ((u32)0x7FFFFFFF) -#define MAX_VM_LISTEN_PORT MAX_LISTEN_PORT -#define MAX_HOST_LISTEN_PORT MAX_LISTEN_PORT -#define MIN_HOST_EPHEMERAL_PORT (MAX_HOST_LISTEN_PORT + 1) - /* 00000000-facb-11e6-bd58-64006a7986d3 */ static const uuid_le srv_id_template = UUID_LE(0x00000000, 0xfacb, 0x11e6, 0xbd, 0x58, @@ -188,33 +175,6 @@ static void hvs_addr_init(struct sockaddr_vm *addr, const uuid_le *svr_id) vsock_addr_init(addr, VMADDR_CID_ANY, port); } -static void hvs_remote_addr_init(struct sockaddr_vm *remote, - struct sockaddr_vm *local) -{ - static u32 host_ephemeral_port = MIN_HOST_EPHEMERAL_PORT; - struct sock *sk; - - vsock_addr_init(remote, VMADDR_CID_ANY, VMADDR_PORT_ANY); - - while (1) { - /* Wrap around ? */ - if (host_ephemeral_port < MIN_HOST_EPHEMERAL_PORT || - host_ephemeral_port == VMADDR_PORT_ANY) - host_ephemeral_port = MIN_HOST_EPHEMERAL_PORT; - - remote->svm_port = host_ephemeral_port++; - - sk = vsock_find_connected_socket(remote, local); - if (!sk) { - /* Found an available ephemeral port */ - return; - } - - /* Release refcnt got in vsock_find_connected_socket */ - sock_put(sk); - } -} - static void hvs_set_channel_pending_send_size(struct vmbus_channel *chan) { set_channel_pending_send_size(chan, @@ -342,12 +302,7 @@ static void hvs_open_connection(struct vmbus_channel *chan) if_type = &chan->offermsg.offer.if_type; if_instance = &chan->offermsg.offer.if_instance; conn_from_host = chan->offermsg.offer.u.pipe.user_def[0]; - - /* The host or the VM should only listen on a port in - * [0, MAX_LISTEN_PORT] - */ - if (!is_valid_srv_id(if_type) || - get_port_by_srv_id(if_type) > MAX_LISTEN_PORT) + if (!is_valid_srv_id(if_type)) return; hvs_addr_init(&addr, conn_from_host ? if_type : if_instance); @@ -371,6 +326,13 @@ static void hvs_open_connection(struct vmbus_channel *chan) new->sk_state = TCP_SYN_SENT; vnew = vsock_sk(new); + + hvs_addr_init(&vnew->local_addr, if_type); + + /* Remote peer is always the host */ + vsock_addr_init(&vnew->remote_addr, + VMADDR_CID_HOST, VMADDR_PORT_ANY); + vnew->remote_addr.svm_port = get_port_by_srv_id(if_instance); hvs_new = vnew->trans; hvs_new->chan = chan; } else { @@ -410,8 +372,6 @@ static void hvs_open_connection(struct vmbus_channel *chan) sk->sk_ack_backlog++; hvs_addr_init(&vnew->local_addr, if_type); - hvs_remote_addr_init(&vnew->remote_addr, &vnew->local_addr); - hvs_new->vm_srv_id = *if_type; hvs_new->host_srv_id = *if_instance; @@ -716,16 +676,6 @@ static bool hvs_stream_is_active(struct vsock_sock *vsk) static bool hvs_stream_allow(u32 cid, u32 port) { - /* The host's port range [MIN_HOST_EPHEMERAL_PORT, 0xFFFFFFFF) is - * reserved as ephemeral ports, which are used as the host's ports - * when the host initiates connections. - * - * Perform this check in the guest so an immediate error is produced - * instead of a timeout. - */ - if (port > MAX_HOST_LISTEN_PORT) - return false; - if (cid == VMADDR_CID_HOST) return true; diff --git a/net/wireless/ethtool.c b/net/wireless/ethtool.c index a9c0f368db5d27ed72159a2395a4a4ec60234ed0..24e18405cdb48fff6090831f656b4f883c926109 100644 --- a/net/wireless/ethtool.c +++ b/net/wireless/ethtool.c @@ -7,9 +7,13 @@ void cfg80211_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct wireless_dev *wdev = dev->ieee80211_ptr; + struct device *pdev = wiphy_dev(wdev->wiphy); - strlcpy(info->driver, wiphy_dev(wdev->wiphy)->driver->name, - sizeof(info->driver)); + if (pdev->driver) + strlcpy(info->driver, pdev->driver->name, + sizeof(info->driver)); + else + strlcpy(info->driver, "N/A", sizeof(info->driver)); strlcpy(info->version, init_utsname()->release, sizeof(info->version)); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b1af67cd21b009c39c294d932541d9c94e65558d..f0f8efe4812bf16c52c699ed10053a310fa381ef 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -325,6 +325,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT] = { .type = NLA_FLAG }, [NL80211_ATTR_CONTROL_PORT_OVER_NL80211] = { .type = NLA_FLAG }, [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG }, + [NL80211_ATTR_STATUS_CODE] = { .type = NLA_U16 }, [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 }, [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 }, [NL80211_ATTR_PID] = { .type = NLA_U32 }, @@ -10428,6 +10429,7 @@ static int cfg80211_cqm_rssi_update(struct cfg80211_registered_device *rdev, if (err) return err; + cfg80211_sinfo_release_content(&sinfo); if (sinfo.filled & BIT_ULL(NL80211_STA_INFO_BEACON_SIGNAL_AVG)) wdev->cqm_config->last_rssi_event_value = (s8) sinfo.rx_beacon_signal_avg; diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 6aad76334db8a25af8848d20f1d5047f065bc209..7cfaab135647d15e98014b3034b85f30f04b4268 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -550,6 +550,10 @@ static inline int rdev_set_wiphy_params(struct cfg80211_registered_device *rdev, u32 changed) { int ret; + + if (!rdev->ops->set_wiphy_params) + return -EOPNOTSUPP; + trace_rdev_set_wiphy_params(&rdev->wiphy, changed); ret = rdev->ops->set_wiphy_params(&rdev->wiphy, changed); trace_rdev_return_int(&rdev->wiphy, ret); @@ -1179,6 +1183,16 @@ rdev_start_radar_detection(struct cfg80211_registered_device *rdev, return ret; } +static inline void +rdev_end_cac(struct cfg80211_registered_device *rdev, + struct net_device *dev) +{ + trace_rdev_end_cac(&rdev->wiphy, dev); + if (rdev->ops->end_cac) + rdev->ops->end_cac(&rdev->wiphy, dev); + trace_rdev_return_void(&rdev->wiphy); +} + static inline int rdev_set_mcast_rate(struct cfg80211_registered_device *rdev, struct net_device *dev, diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 3f2cde2d17231a7f15f40f1f9c6c820ff3f94fa2..925060d980992d7d528fd7aa5a4574d3d8a696b6 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2296,14 +2296,15 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) static void handle_channel_custom(struct wiphy *wiphy, struct ieee80211_channel *chan, - const struct ieee80211_regdomain *regd) + const struct ieee80211_regdomain *regd, + u32 min_bw) { u32 bw_flags = 0; const struct ieee80211_reg_rule *reg_rule = NULL; const struct ieee80211_power_rule *power_rule = NULL; u32 bw; - for (bw = MHZ_TO_KHZ(20); bw >= MHZ_TO_KHZ(5); bw = bw / 2) { + for (bw = MHZ_TO_KHZ(20); bw >= min_bw; bw = bw / 2) { reg_rule = freq_reg_info_regd(MHZ_TO_KHZ(chan->center_freq), regd, bw); if (!IS_ERR(reg_rule)) @@ -2359,8 +2360,14 @@ static void handle_band_custom(struct wiphy *wiphy, if (!sband) return; + /* + * We currently assume that you always want at least 20 MHz, + * otherwise channel 12 might get enabled if this rule is + * compatible to US, which permits 2402 - 2472 MHz. + */ for (i = 0; i < sband->n_channels; i++) - handle_channel_custom(wiphy, &sband->channels[i], regd); + handle_channel_custom(wiphy, &sband->channels[i], regd, + MHZ_TO_KHZ(20)); } /* Used by drivers prior to wiphy registration */ @@ -3880,6 +3887,25 @@ bool regulatory_pre_cac_allowed(struct wiphy *wiphy) return pre_cac_allowed; } +static void cfg80211_check_and_end_cac(struct cfg80211_registered_device *rdev) +{ + struct wireless_dev *wdev; + /* If we finished CAC or received radar, we should end any + * CAC running on the same channels. + * the check !cfg80211_chandef_dfs_usable contain 2 options: + * either all channels are available - those the CAC_FINISHED + * event has effected another wdev state, or there is a channel + * in unavailable state in wdev chandef - those the RADAR_DETECTED + * event has effected another wdev state. + * In both cases we should end the CAC on the wdev. + */ + list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { + if (wdev->cac_started && + !cfg80211_chandef_dfs_usable(&rdev->wiphy, &wdev->chandef)) + rdev_end_cac(rdev, wdev->netdev); + } +} + void regulatory_propagate_dfs_state(struct wiphy *wiphy, struct cfg80211_chan_def *chandef, enum nl80211_dfs_state dfs_state, @@ -3906,8 +3932,10 @@ void regulatory_propagate_dfs_state(struct wiphy *wiphy, cfg80211_set_dfs_state(&rdev->wiphy, chandef, dfs_state); if (event == NL80211_RADAR_DETECTED || - event == NL80211_RADAR_CAC_FINISHED) + event == NL80211_RADAR_CAC_FINISHED) { cfg80211_sched_dfs_chan_update(rdev); + cfg80211_check_and_end_cac(rdev); + } nl80211_radar_notify(rdev, chandef, event, NULL, GFP_KERNEL); } @@ -3917,6 +3945,15 @@ static int __init regulatory_init_db(void) { int err; + /* + * It's possible that - due to other bugs/issues - cfg80211 + * never called regulatory_init() below, or that it failed; + * in that case, don't try to do any further work here as + * it's doomed to lead to crashes. + */ + if (IS_ERR_OR_NULL(reg_pdev)) + return -EINVAL; + err = load_builtin_regdb_keys(); if (err) return err; diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 3c1b37e46fb0f24e47ec58053351b5b2e89d4c99..ae52143d6dca4270888f473bf74ab8116f4009a5 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -1288,14 +1288,14 @@ void cfg80211_autodisconnect_wk(struct work_struct *work) if (wdev->conn_owner_nlportid) { switch (wdev->iftype) { case NL80211_IFTYPE_ADHOC: - cfg80211_leave_ibss(rdev, wdev->netdev, false); + __cfg80211_leave_ibss(rdev, wdev->netdev, false); break; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: - cfg80211_stop_ap(rdev, wdev->netdev, false); + __cfg80211_stop_ap(rdev, wdev->netdev, false); break; case NL80211_IFTYPE_MESH_POINT: - cfg80211_leave_mesh(rdev, wdev->netdev); + __cfg80211_leave_mesh(rdev, wdev->netdev); break; case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: diff --git a/net/wireless/trace.h b/net/wireless/trace.h index f059ef57085630340f4dc05d6bb0ee01ce0bbab4..713c15d6c5e614234c838f82a0c5ea115b3b0c01 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -624,6 +624,11 @@ DEFINE_EVENT(wiphy_netdev_evt, rdev_flush_pmksa, TP_ARGS(wiphy, netdev) ); +DEFINE_EVENT(wiphy_netdev_evt, rdev_end_cac, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev), + TP_ARGS(wiphy, netdev) +); + DECLARE_EVENT_CLASS(station_add_change, TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *mac, struct station_parameters *params), diff --git a/net/wireless/util.c b/net/wireless/util.c index c22373e5d3e9a4ec0dec4cc55956109dc18a8dba..ddc6d95e65c3f0f63eb7952e0f32262895123931 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -542,7 +542,7 @@ __frame_add_frag(struct sk_buff *skb, struct page *page, struct skb_shared_info *sh = skb_shinfo(skb); int page_offset; - page_ref_inc(page); + get_page(page); page_offset = ptr - page_address(page); skb_add_rx_frag(skb, sh->nr_frags, page, page_offset, len, size); } @@ -1960,3 +1960,48 @@ bool cfg80211_iftype_allowed(struct wiphy *wiphy, enum nl80211_iftype iftype, return false; } EXPORT_SYMBOL(cfg80211_iftype_allowed); + +/* Layer 2 Update frame (802.2 Type 1 LLC XID Update response) */ +struct iapp_layer2_update { + u8 da[ETH_ALEN]; /* broadcast */ + u8 sa[ETH_ALEN]; /* STA addr */ + __be16 len; /* 6 */ + u8 dsap; /* 0 */ + u8 ssap; /* 0 */ + u8 control; + u8 xid_info[3]; +} __packed; + +void cfg80211_send_layer2_update(struct net_device *dev, const u8 *addr) +{ + struct iapp_layer2_update *msg; + struct sk_buff *skb; + + /* Send Level 2 Update Frame to update forwarding tables in layer 2 + * bridge devices */ + + skb = dev_alloc_skb(sizeof(*msg)); + if (!skb) + return; + msg = skb_put(skb, sizeof(*msg)); + + /* 802.2 Type 1 Logical Link Control (LLC) Exchange Identifier (XID) + * Update response frame; IEEE Std 802.2-1998, 5.4.1.2.1 */ + + eth_broadcast_addr(msg->da); + ether_addr_copy(msg->sa, addr); + msg->len = htons(6); + msg->dsap = 0; + msg->ssap = 0x01; /* NULL LSAP, CR Bit: Response */ + msg->control = 0xaf; /* XID response lsb.1111F101. + * F=0 (no poll command; unsolicited frame) */ + msg->xid_info[0] = 0x81; /* XID format identifier */ + msg->xid_info[1] = 1; /* LLC types/classes: Type 1 LLC */ + msg->xid_info[2] = 0; /* XID sender's receive window size (RW) */ + + skb->dev = dev; + skb->protocol = eth_type_trans(skb, dev); + memset(skb->cb, 0, sizeof(skb->cb)); + netif_rx_ni(skb); +} +EXPORT_SYMBOL(cfg80211_send_layer2_update); diff --git a/net/wireless/wext-core.c b/net/wireless/wext-core.c index 5e677dac2a0ceaa94b6f2d1548133b0fc33da8dc..69102fda9ebd46343a0444d7206799223e24a0fd 100644 --- a/net/wireless/wext-core.c +++ b/net/wireless/wext-core.c @@ -657,7 +657,8 @@ struct iw_statistics *get_wireless_stats(struct net_device *dev) return NULL; } -static int iw_handler_get_iwstats(struct net_device * dev, +/* noinline to avoid a bogus warning with -O3 */ +static noinline int iw_handler_get_iwstats(struct net_device * dev, struct iw_request_info * info, union iwreq_data * wrqu, char * extra) diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index 20a511398389d686383e9d8c74434b3f2ad40f3f..bd1cbbfe5924bf7fccda5d844b908fb5f76985c5 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -765,6 +765,10 @@ static int x25_connect(struct socket *sock, struct sockaddr *uaddr, if (sk->sk_state == TCP_ESTABLISHED) goto out; + rc = -EALREADY; /* Do nothing if call is already in progress */ + if (sk->sk_state == TCP_SYN_SENT) + goto out; + sk->sk_state = TCP_CLOSE; sock->state = SS_UNCONNECTED; @@ -811,7 +815,7 @@ static int x25_connect(struct socket *sock, struct sockaddr *uaddr, /* Now the loop */ rc = -EINPROGRESS; if (sk->sk_state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) - goto out_put_neigh; + goto out; rc = x25_wait_for_connection_establishment(sk); if (rc) diff --git a/net/xdp/xdp_umem.c b/net/xdp/xdp_umem.c index d9117ab035f7c5dea2455d88f3820be84de2fb99..556a649512b603e3222e2515c97739f6c8e2268a 100644 --- a/net/xdp/xdp_umem.c +++ b/net/xdp/xdp_umem.c @@ -23,6 +23,9 @@ void xdp_add_sk_umem(struct xdp_umem *umem, struct xdp_sock *xs) { unsigned long flags; + if (!xs->tx) + return; + spin_lock_irqsave(&umem->xsk_list_lock, flags); list_add_rcu(&xs->list, &umem->xsk_list); spin_unlock_irqrestore(&umem->xsk_list_lock, flags); @@ -32,6 +35,9 @@ void xdp_del_sk_umem(struct xdp_umem *umem, struct xdp_sock *xs) { unsigned long flags; + if (!xs->tx) + return; + spin_lock_irqsave(&umem->xsk_list_lock, flags); list_del_rcu(&xs->list); spin_unlock_irqrestore(&umem->xsk_list_lock, flags); diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index ff15207036dc5ca27430db98e8f704fab422d56c..72caa4fb13f47374761f471c0361f20c7e985503 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -218,6 +218,9 @@ static int xsk_generic_xmit(struct sock *sk, struct msghdr *m, mutex_lock(&xs->mutex); + if (xs->queue_id >= xs->dev->real_num_tx_queues) + goto out; + while (xskq_peek_desc(xs->tx, &desc)) { char *buffer; u64 addr; @@ -228,12 +231,6 @@ static int xsk_generic_xmit(struct sock *sk, struct msghdr *m, goto out; } - if (xskq_reserve_addr(xs->umem->cq)) - goto out; - - if (xs->queue_id >= xs->dev->real_num_tx_queues) - goto out; - len = desc.len; skb = sock_alloc_send_skb(sk, len, 1, &err); if (unlikely(!skb)) { @@ -245,7 +242,7 @@ static int xsk_generic_xmit(struct sock *sk, struct msghdr *m, addr = desc.addr; buffer = xdp_umem_get_data(xs->umem, addr); err = skb_store_bits(skb, 0, buffer, len); - if (unlikely(err)) { + if (unlikely(err) || xskq_reserve_addr(xs->umem->cq)) { kfree_skb(skb); goto out; } @@ -323,7 +320,7 @@ static int xsk_init_queue(u32 entries, struct xsk_queue **queue, /* Make sure queue is ready before it can be seen by others */ smp_wmb(); - *queue = q; + WRITE_ONCE(*queue, q); return 0; } @@ -457,7 +454,7 @@ static int xsk_bind(struct socket *sock, struct sockaddr *addr, int addr_len) } xdp_get_umem(umem_xs->umem); - xs->umem = umem_xs->umem; + WRITE_ONCE(xs->umem, umem_xs->umem); sockfd_put(sock); } else if (!xs->umem || !xdp_umem_validate_queues(xs->umem)) { err = -EINVAL; @@ -537,7 +534,7 @@ static int xsk_setsockopt(struct socket *sock, int level, int optname, /* Make sure umem is ready before it can be seen by others */ smp_wmb(); - xs->umem = umem; + WRITE_ONCE(xs->umem, umem); mutex_unlock(&xs->mutex); return 0; } @@ -661,6 +658,8 @@ static int xsk_mmap(struct file *file, struct socket *sock, if (!umem) return -EINVAL; + /* Matches the smp_wmb() in XDP_UMEM_REG */ + smp_rmb(); if (offset == XDP_UMEM_PGOFF_FILL_RING) q = READ_ONCE(umem->fq); else if (offset == XDP_UMEM_PGOFF_COMPLETION_RING) @@ -670,6 +669,8 @@ static int xsk_mmap(struct file *file, struct socket *sock, if (!q) return -EINVAL; + /* Matches the smp_wmb() in xsk_init_queue */ + smp_rmb(); qpg = virt_to_head_page(q->ring); if (size > (PAGE_SIZE << compound_order(qpg))) return -EINVAL; diff --git a/net/xfrm/xfrm_interface.c b/net/xfrm/xfrm_interface.c index d6a3cdf7885c3e9d8ce3296fc55db80c8ec19abb..317fe9c92932b92d6c472c951157d26f154c0236 100644 --- a/net/xfrm/xfrm_interface.c +++ b/net/xfrm/xfrm_interface.c @@ -145,8 +145,6 @@ static int xfrmi_create(struct net_device *dev) if (err < 0) goto out; - strcpy(xi->p.name, dev->name); - dev_hold(dev); xfrmi_link(xfrmn, xi); @@ -270,9 +268,6 @@ xfrmi_xmit2(struct sk_buff *skb, struct net_device *dev, struct flowi *fl) int err = -1; int mtu; - if (!dst) - goto tx_err_link_failure; - dst_hold(dst); dst = xfrm_lookup_with_ifid(xi->net, dst, fl, NULL, 0, xi->p.if_id); if (IS_ERR(dst)) { @@ -293,13 +288,13 @@ xfrmi_xmit2(struct sk_buff *skb, struct net_device *dev, struct flowi *fl) if (tdev == dev) { stats->collisions++; net_warn_ratelimited("%s: Local routing loop detected!\n", - xi->p.name); + dev->name); goto tx_err_dst_release; } mtu = dst_mtu(dst); if (!skb->ignore_df && skb->len > mtu) { - skb_dst_update_pmtu(skb, mtu); + skb_dst_update_pmtu_no_confirm(skb, mtu); if (skb->protocol == htons(ETH_P_IPV6)) { if (mtu < IPV6_MIN_MTU) @@ -345,6 +340,7 @@ static netdev_tx_t xfrmi_xmit(struct sk_buff *skb, struct net_device *dev) { struct xfrm_if *xi = netdev_priv(dev); struct net_device_stats *stats = &xi->dev->stats; + struct dst_entry *dst = skb_dst(skb); struct flowi fl; int ret; @@ -354,10 +350,33 @@ static netdev_tx_t xfrmi_xmit(struct sk_buff *skb, struct net_device *dev) case htons(ETH_P_IPV6): xfrm_decode_session(skb, &fl, AF_INET6); memset(IP6CB(skb), 0, sizeof(*IP6CB(skb))); + if (!dst) { + fl.u.ip6.flowi6_oif = dev->ifindex; + fl.u.ip6.flowi6_flags |= FLOWI_FLAG_ANYSRC; + dst = ip6_route_output(dev_net(dev), NULL, &fl.u.ip6); + if (dst->error) { + dst_release(dst); + stats->tx_carrier_errors++; + goto tx_err; + } + skb_dst_set(skb, dst); + } break; case htons(ETH_P_IP): xfrm_decode_session(skb, &fl, AF_INET); memset(IPCB(skb), 0, sizeof(*IPCB(skb))); + if (!dst) { + struct rtable *rt; + + fl.u.ip4.flowi4_oif = dev->ifindex; + fl.u.ip4.flowi4_flags |= FLOWI_FLAG_ANYSRC; + rt = __ip_route_output_key(dev_net(dev), &fl.u.ip4); + if (IS_ERR(rt)) { + stats->tx_carrier_errors++; + goto tx_err; + } + skb_dst_set(skb, &rt->dst); + } break; default: goto tx_err; @@ -568,12 +587,9 @@ static void xfrmi_dev_setup(struct net_device *dev) { dev->netdev_ops = &xfrmi_netdev_ops; dev->type = ARPHRD_NONE; - dev->hard_header_len = ETH_HLEN; - dev->min_header_len = ETH_HLEN; dev->mtu = ETH_DATA_LEN; dev->min_mtu = ETH_MIN_MTU; - dev->max_mtu = ETH_DATA_LEN; - dev->addr_len = ETH_ALEN; + dev->max_mtu = IP_MAX_MTU; dev->flags = IFF_NOARP; dev->needs_free_netdev = true; dev->priv_destructor = xfrmi_dev_free; @@ -648,12 +664,6 @@ static int xfrmi_newlink(struct net *src_net, struct net_device *dev, int err; xfrmi_netlink_parms(data, &p); - - if (!tb[IFLA_IFNAME]) - return -EINVAL; - - nla_strlcpy(p.name, tb[IFLA_IFNAME], IFNAMSIZ); - xi = xfrmi_locate(net, &p); if (xi) return -EEXIST; diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index 75d4b48601aaab076c50a67a6f6d236e17cdae73..85a6e8f5a75db943cd018e7f17d1a627c842bd04 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -221,7 +221,7 @@ all: clean: $(MAKE) -C ../../ M=$(CURDIR) clean - @rm -f *~ + @find $(CURDIR) -type f -name '*~' -delete $(LIBBPF): FORCE # Fix up variables inherited from Kbuild that tools/ build system won't like diff --git a/samples/bpf/xdp_rxq_info_user.c b/samples/bpf/xdp_rxq_info_user.c index ef26f882f92f437c741ad2abea2b0547a0336c75..a55c81301c1a922676405c8cc4fa574cdbeba617 100644 --- a/samples/bpf/xdp_rxq_info_user.c +++ b/samples/bpf/xdp_rxq_info_user.c @@ -472,9 +472,9 @@ int main(int argc, char **argv) if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd)) return EXIT_FAIL; - map = bpf_map__next(NULL, obj); - stats_global_map = bpf_map__next(map, obj); - rx_queue_index_map = bpf_map__next(stats_global_map, obj); + map = bpf_object__find_map_by_name(obj, "config_map"); + stats_global_map = bpf_object__find_map_by_name(obj, "stats_global_map"); + rx_queue_index_map = bpf_object__find_map_by_name(obj, "rx_queue_index_map"); if (!map || !stats_global_map || !rx_queue_index_map) { printf("finding a map in obj file failed\n"); return EXIT_FAIL; diff --git a/samples/hw_breakpoint/data_breakpoint.c b/samples/hw_breakpoint/data_breakpoint.c index ef7f32291852fec2b4e93f6d75053a1907b71b0f..3739481c6426ea1d53f909c820c1b7d4bf3c8d9f 100644 --- a/samples/hw_breakpoint/data_breakpoint.c +++ b/samples/hw_breakpoint/data_breakpoint.c @@ -36,7 +36,7 @@ struct perf_event * __percpu *sample_hbp; -static char ksym_name[KSYM_NAME_LEN] = "pid_max"; +static char ksym_name[KSYM_NAME_LEN] = "jiffies"; module_param_string(ksym, ksym_name, KSYM_NAME_LEN, S_IRUGO); MODULE_PARM_DESC(ksym, "Kernel symbol to monitor; this module will report any" " write operations on the kernel symbol"); @@ -54,11 +54,15 @@ static int __init hw_break_module_init(void) { int ret; struct perf_event_attr attr; + void *addr = __symbol_get(ksym_name); + + if (!addr) + return -ENXIO; hw_breakpoint_init(&attr); - attr.bp_addr = kallsyms_lookup_name(ksym_name); + attr.bp_addr = (unsigned long)addr; attr.bp_len = HW_BREAKPOINT_LEN_4; - attr.bp_type = HW_BREAKPOINT_W | HW_BREAKPOINT_R; + attr.bp_type = HW_BREAKPOINT_W; sample_hbp = register_wide_hw_breakpoint(&attr, sample_hbp_handler, NULL); if (IS_ERR((void __force *)sample_hbp)) { @@ -79,6 +83,7 @@ static int __init hw_break_module_init(void) static void __exit hw_break_module_exit(void) { unregister_wide_hw_breakpoint(sample_hbp); + symbol_put(ksym_name); printk(KERN_INFO "HW Breakpoint for %s write uninstalled\n", ksym_name); } diff --git a/scripts/Kconfig.include b/scripts/Kconfig.include index 3b2861f47709b46aa6f50218a8cf644e9423bf82..79455ad6b3863c3b4d5a495f8644a2150b3eef0b 100644 --- a/scripts/Kconfig.include +++ b/scripts/Kconfig.include @@ -20,7 +20,7 @@ success = $(if-success,$(1),y,n) # $(cc-option,) # Return y if the compiler supports , n otherwise -cc-option = $(success,$(CC) -Werror $(CLANG_FLAGS) $(1) -E -x c /dev/null -o /dev/null) +cc-option = $(success,$(CC) -Werror $(CLANG_FLAGS) $(1) -S -x c /dev/null -o /dev/null) # $(ld-option,) # Return y if the linker supports , n otherwise diff --git a/scripts/adjust_autoksyms.sh b/scripts/adjust_autoksyms.sh index 6e6d63957da348f6172ca7a7c5545e0058d8824d..4d1adf1c9827c3d66a42ea3db4419f7836e5cfd7 100755 --- a/scripts/adjust_autoksyms.sh +++ b/scripts/adjust_autoksyms.sh @@ -1,6 +1,6 @@ #!/bin/sh -# Script to create/update include/generated/autoksyms.h and dependency files +# Script to update include/generated/autoksyms.h and dependency files # # Copyright: (C) 2016 Linaro Limited # Created by: Nicolas Pitre, January 2016 @@ -9,9 +9,7 @@ # it under the terms of the GNU General Public License version 2 as # published by the Free Software Foundation. -# Create/update the include/generated/autoksyms.h file from the list -# of all module's needed symbols as recorded on the third line of -# .tmp_versions/*.mod files. +# Update the include/generated/autoksyms.h file. # # For each symbol being added or removed, the corresponding dependency # file's timestamp is updated to force a rebuild of the affected source @@ -39,35 +37,10 @@ case "$KBUILD_VERBOSE" in esac # We need access to CONFIG_ symbols -case "${KCONFIG_CONFIG}" in -*/*) - . "${KCONFIG_CONFIG}" - ;; -*) - # Force using a file from the current directory - . "./${KCONFIG_CONFIG}" -esac - -# Generate a new ksym list file with symbols needed by the current -# set of modules. -cat > "$new_ksyms_file" << EOT -/* - * Automatically generated file; DO NOT EDIT. - */ +. include/config/auto.conf -EOT -[ "$(ls -A "$MODVERDIR")" ] && -for mod in "$MODVERDIR"/*.mod; do - sed -n -e '3{s/ /\n/g;/^$/!p;}' "$mod" -done | sort -u | -while read sym; do - echo "#define __KSYM_${sym} 1" -done >> "$new_ksyms_file" - -# Special case for modversions (see modpost.c) -if [ -n "$CONFIG_MODVERSIONS" ]; then - echo "#define __KSYM_module_layout 1" >> "$new_ksyms_file" -fi +# Generate a new symbol list file +$CONFIG_SHELL $srctree/scripts/gen_autoksyms.sh "$new_ksyms_file" # Extract changes between old and new list and touch corresponding # dependency files. diff --git a/scripts/find-unused-docs.sh b/scripts/find-unused-docs.sh index 3f46f8977dc4dc8882cc9c89e8d80e4effd0bb4f..ee6a50e33aba805dfd1ea056adf24d80ebe8b283 100755 --- a/scripts/find-unused-docs.sh +++ b/scripts/find-unused-docs.sh @@ -54,7 +54,7 @@ for file in `find $1 -name '*.c'`; do if [[ ${FILES_INCLUDED[$file]+_} ]]; then continue; fi - str=$(scripts/kernel-doc -text -export "$file" 2>/dev/null) + str=$(scripts/kernel-doc -export "$file" 2>/dev/null) if [[ -n "$str" ]]; then echo "$file" fi diff --git a/scripts/gen_autoksyms.sh b/scripts/gen_autoksyms.sh new file mode 100755 index 0000000000000000000000000000000000000000..846dde7accca8e6092f08dc22b21ac44c55767e6 --- /dev/null +++ b/scripts/gen_autoksyms.sh @@ -0,0 +1,52 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-only + +# Create an autoksyms.h header file from the list of all module's needed symbols +# as recorded on the third line of *.mod files and the user-provided symbol +# whitelist. + +set -e + +output_file="$1" + +# Use "make V=1" to debug this script. +case "$KBUILD_VERBOSE" in +*1*) + set -x + ;; +esac + +# We need access to CONFIG_ symbols +. include/config/auto.conf + +ksym_wl=/dev/null +if [ -n "$CONFIG_UNUSED_KSYMS_WHITELIST" ]; then + # Use 'eval' to expand the whitelist path and check if it is relative + eval ksym_wl="$CONFIG_UNUSED_KSYMS_WHITELIST" + [ "${ksym_wl}" != "${ksym_wl#/}" ] || ksym_wl="$abs_srctree/$ksym_wl" + if [ ! -f "$ksym_wl" ] || [ ! -r "$ksym_wl" ]; then + echo "ERROR: '$ksym_wl' whitelist file not found" >&2 + exit 1 + fi +fi + +# Generate a new ksym list file with symbols needed by the current +# set of modules. +cat > "$output_file" << EOT +/* + * Automatically generated file; DO NOT EDIT. + */ + +EOT + +for mod in "$MODVERDIR"/*.mod; do + [ -f "$mod" ] && sed -n -e '3{s/ /\n/g;/^$/!p;}' "$mod" +done | cat - "$ksym_wl" | sort -u | +while read sym; do + echo "#define __KSYM_${sym} 1" +done >> "$output_file" + +# Special case for modversions (see modpost.c) +if [ -n "$CONFIG_MODVERSIONS" ]; then + echo "#define __KSYM_module_layout 1" >> "$output_file" +fi diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c index 0dde19cf748655b174b89321423eab09463e5e7f..2caf5fac102a286164ec623ff59a67777230d443 100644 --- a/scripts/kconfig/confdata.c +++ b/scripts/kconfig/confdata.c @@ -1314,7 +1314,7 @@ bool conf_set_all_new_symbols(enum conf_def_mode mode) sym_calc_value(csym); if (mode == def_random) - has_changed = randomize_choice_values(csym); + has_changed |= randomize_choice_values(csym); else { set_all_choice_values(csym); has_changed = true; diff --git a/scripts/kconfig/merge_config.sh b/scripts/kconfig/merge_config.sh index 67d1314476314590f285a63700c13e047dc17c80..b5115c6b22f602d35b618b19a51c06041c436232 100755 --- a/scripts/kconfig/merge_config.sh +++ b/scripts/kconfig/merge_config.sh @@ -151,7 +151,7 @@ fi # Use the merged file as the starting point for: # alldefconfig: Fills in any missing symbols with Kconfig default # allnoconfig: Fills in any missing symbols with # CONFIG_* is not set -make KCONFIG_ALLCONFIG=$TMP_FILE $OUTPUT_ARG $ALLTARGET +make $MAKE_ARGS KCONFIG_ALLCONFIG=$TMP_FILE $OUTPUT_ARG $ALLTARGET # Check all specified config values took (might have missed-dependency issues) diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh index 517c732bbe52b510dd155dbd6029f67f736678b7..559a531e09f26c0a53baa7a1feb9fee705b06a76 100755 --- a/scripts/link-vmlinux.sh +++ b/scripts/link-vmlinux.sh @@ -281,14 +281,7 @@ if [ "$1" = "clean" ]; then fi # We need access to CONFIG_ symbols -case "${KCONFIG_CONFIG}" in -*/*) - . "${KCONFIG_CONFIG}" - ;; -*) - # Force using a file from the current directory - . "./${KCONFIG_CONFIG}" -esac +. include/config/auto.conf # Update version info GEN .version diff --git a/scripts/recordmcount.c b/scripts/recordmcount.c index 7a9ad239da57cf7e73ea5c3cd061784ebfd9f0b8..92919606523219b22e786b8a13110450c74b1649 100644 --- a/scripts/recordmcount.c +++ b/scripts/recordmcount.c @@ -39,6 +39,10 @@ #define R_AARCH64_ABS64 257 #endif +#define R_ARM_PC24 1 +#define R_ARM_THM_CALL 10 +#define R_ARM_CALL 28 + static int fd_map; /* File descriptor for file being modified. */ static int mmap_failed; /* Boolean flag. */ static char gpfx; /* prefix for global symbol name (sometimes '_') */ @@ -415,6 +419,18 @@ is_mcounted_section_name(char const *const txtname) #define RECORD_MCOUNT_64 #include "recordmcount.h" +static int arm_is_fake_mcount(Elf32_Rel const *rp) +{ + switch (ELF32_R_TYPE(w(rp->r_info))) { + case R_ARM_THM_CALL: + case R_ARM_CALL: + case R_ARM_PC24: + return 0; + } + + return 1; +} + /* 64-bit EM_MIPS has weird ELF64_Rela.r_info. * http://techpubs.sgi.com/library/manuals/4000/007-4658-001/pdf/007-4658-001.pdf * We interpret Table 29 Relocation Operation (Elf64_Rel, Elf64_Rela) [p.40] @@ -516,6 +532,7 @@ do_file(char const *const fname) altmcount = "__gnu_mcount_nc"; make_nop = make_nop_arm; rel_type_nop = R_ARM_NONE; + is_fake_mcount32 = arm_is_fake_mcount; break; case EM_AARCH64: reltype = R_AARCH64_ABS64; diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c index 253ef6e9d445355c0f1d3379d319a8e052541032..752f73980e3085e81daceccc269f45e09b812b3c 100644 --- a/security/apparmor/capability.c +++ b/security/apparmor/capability.c @@ -110,13 +110,13 @@ static int audit_caps(struct common_audit_data *sa, struct aa_profile *profile, * profile_capable - test if profile allows use of capability @cap * @profile: profile being enforced (NOT NULL, NOT unconfined) * @cap: capability to test if allowed - * @audit: whether an audit record should be generated + * @opts: CAP_OPT_NOAUDIT bit determines whether audit record is generated * @sa: audit data (MAY BE NULL indicating no auditing) * * Returns: 0 if allowed else -EPERM */ -static int profile_capable(struct aa_profile *profile, int cap, int audit, - struct common_audit_data *sa) +static int profile_capable(struct aa_profile *profile, int cap, + unsigned int opts, struct common_audit_data *sa) { int error; @@ -126,7 +126,7 @@ static int profile_capable(struct aa_profile *profile, int cap, int audit, else error = -EPERM; - if (audit == SECURITY_CAP_NOAUDIT) { + if (opts & CAP_OPT_NOAUDIT) { if (!COMPLAIN_MODE(profile)) return error; /* audit the cap request in complain mode but note that it @@ -142,13 +142,13 @@ static int profile_capable(struct aa_profile *profile, int cap, int audit, * aa_capable - test permission to use capability * @label: label being tested for capability (NOT NULL) * @cap: capability to be tested - * @audit: whether an audit record should be generated + * @opts: CAP_OPT_NOAUDIT bit determines whether audit record is generated * * Look up capability in profile capability set. * * Returns: 0 on success, or else an error code. */ -int aa_capable(struct aa_label *label, int cap, int audit) +int aa_capable(struct aa_label *label, int cap, unsigned int opts) { struct aa_profile *profile; int error = 0; @@ -156,7 +156,7 @@ int aa_capable(struct aa_label *label, int cap, int audit) sa.u.cap = cap; error = fn_for_each_confined(label, profile, - profile_capable(profile, cap, audit, &sa)); + profile_capable(profile, cap, opts, &sa)); return error; } diff --git a/security/apparmor/include/capability.h b/security/apparmor/include/capability.h index e0304e2aeb7ffda8fe6862c235526c22dd10db20..1b3663b6ab129d649cd7d9604ae4e4cb2a51ef7b 100644 --- a/security/apparmor/include/capability.h +++ b/security/apparmor/include/capability.h @@ -40,7 +40,7 @@ struct aa_caps { extern struct aa_sfs_entry aa_sfs_entry_caps[]; -int aa_capable(struct aa_label *label, int cap, int audit); +int aa_capable(struct aa_label *label, int cap, unsigned int opts); static inline void aa_free_cap_rules(struct aa_caps *caps) { diff --git a/security/apparmor/include/cred.h b/security/apparmor/include/cred.h index e287b7d0d4bebcf2cc83d9add032fdb1feed54c0..265ae6641a0644e84e40c7f774f4f447a1df3fc2 100644 --- a/security/apparmor/include/cred.h +++ b/security/apparmor/include/cred.h @@ -151,6 +151,8 @@ static inline struct aa_label *begin_current_label_crit_section(void) { struct aa_label *label = aa_current_raw_label(); + might_sleep(); + if (label_is_stale(label)) { label = aa_get_newest_label(label); if (aa_replace_current_label(label) == 0) diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c index 527ea1557120ece44aa0c04d901bfa3e8d9b9422..aacd1e95cb596888fec63a00de1124092c11a188 100644 --- a/security/apparmor/ipc.c +++ b/security/apparmor/ipc.c @@ -107,7 +107,8 @@ static int profile_tracer_perm(struct aa_profile *tracer, aad(sa)->label = &tracer->label; aad(sa)->peer = tracee; aad(sa)->request = 0; - aad(sa)->error = aa_capable(&tracer->label, CAP_SYS_PTRACE, 1); + aad(sa)->error = aa_capable(&tracer->label, CAP_SYS_PTRACE, + CAP_OPT_NONE); return aa_audit(AUDIT_APPARMOR_AUTO, tracer, sa, audit_ptrace_cb); } diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 8b8b70620bbe753e35db3fe5e679334dab8ebb83..c8166bdbfcfad7bcd7c289c840518501990a9215 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -114,13 +114,13 @@ static int apparmor_ptrace_access_check(struct task_struct *child, struct aa_label *tracer, *tracee; int error; - tracer = begin_current_label_crit_section(); + tracer = __begin_current_label_crit_section(); tracee = aa_get_task_label(child); error = aa_may_ptrace(tracer, tracee, (mode & PTRACE_MODE_READ) ? AA_PTRACE_READ : AA_PTRACE_TRACE); aa_put_label(tracee); - end_current_label_crit_section(tracer); + __end_current_label_crit_section(tracer); return error; } @@ -130,11 +130,11 @@ static int apparmor_ptrace_traceme(struct task_struct *parent) struct aa_label *tracer, *tracee; int error; - tracee = begin_current_label_crit_section(); + tracee = __begin_current_label_crit_section(); tracer = aa_get_task_label(parent); error = aa_may_ptrace(tracer, tracee, AA_PTRACE_TRACE); aa_put_label(tracer); - end_current_label_crit_section(tracee); + __end_current_label_crit_section(tracee); return error; } @@ -174,14 +174,14 @@ static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective, } static int apparmor_capable(const struct cred *cred, struct user_namespace *ns, - int cap, int audit) + int cap, unsigned int opts) { struct aa_label *label; int error = 0; label = aa_get_newest_cred_label(cred); if (!unconfined(label)) - error = aa_capable(label, cap, audit); + error = aa_capable(label, cap, opts); aa_put_label(label); return error; diff --git a/security/apparmor/net.c b/security/apparmor/net.c index bb24cfa0a164cbbc0fbebe0b5e85a176bf840069..d5d72dd1ca1f9a2d331ec4ad60fdf0853763a8ae 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -146,17 +146,20 @@ int aa_af_perm(struct aa_label *label, const char *op, u32 request, u16 family, static int aa_label_sk_perm(struct aa_label *label, const char *op, u32 request, struct sock *sk) { - struct aa_profile *profile; - DEFINE_AUDIT_SK(sa, op, sk); + int error = 0; AA_BUG(!label); AA_BUG(!sk); - if (unconfined(label)) - return 0; + if (!unconfined(label)) { + struct aa_profile *profile; + DEFINE_AUDIT_SK(sa, op, sk); - return fn_for_each_confined(label, profile, - aa_profile_af_sk_perm(profile, &sa, request, sk)); + error = fn_for_each_confined(label, profile, + aa_profile_af_sk_perm(profile, &sa, request, sk)); + } + + return error; } int aa_sk_perm(const char *op, u32 request, struct sock *sk) diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c index 95fd26d09757f2c7fb7b39d541e1aa6fdc923a5d..552ed09cb47e75829a60e202b3a9465cecefe246 100644 --- a/security/apparmor/resource.c +++ b/security/apparmor/resource.c @@ -124,7 +124,7 @@ int aa_task_setrlimit(struct aa_label *label, struct task_struct *task, */ if (label != peer && - aa_capable(label, CAP_SYS_RESOURCE, SECURITY_CAP_NOAUDIT) != 0) + aa_capable(label, CAP_SYS_RESOURCE, CAP_OPT_NOAUDIT) != 0) error = fn_for_each(label, profile, audit_resource(profile, resource, new_rlim->rlim_max, peer, diff --git a/security/commoncap.c b/security/commoncap.c index ccc9927940041173d8766331f1a4416f50e50ab1..d618dfba5ce9a04d499fc71bdb8cb97ccb76f449 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -58,7 +58,7 @@ static void warn_setuid_and_fcaps_mixed(const char *fname) } /** - * __cap_capable - Determine whether a task has a particular effective capability + * cap_capable - Determine whether a task has a particular effective capability * @cred: The credentials to use * @ns: The user namespace in which we need the capability * @cap: The capability to check for @@ -72,8 +72,8 @@ static void warn_setuid_and_fcaps_mixed(const char *fname) * cap_has_capability() returns 0 when a task has a capability, but the * kernel's capable() and has_capability() returns 1 for this case. */ -int __cap_capable(const struct cred *cred, struct user_namespace *targ_ns, - int cap, int audit) +int cap_capable(const struct cred *cred, struct user_namespace *targ_ns, + int cap, unsigned int opts) { struct user_namespace *ns = targ_ns; @@ -110,27 +110,6 @@ int __cap_capable(const struct cred *cred, struct user_namespace *targ_ns, /* We never get here */ } -int cap_capable(const struct cred *cred, struct user_namespace *targ_ns, - int cap, int audit) -{ - int ret = __cap_capable(cred, targ_ns, cap, audit); - -#ifdef CONFIG_ANDROID_PARANOID_NETWORK - if (ret != 0 && cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW)) { - printk("Process %s granted CAP_NET_RAW from Android group net_raw.\n", current->comm); - printk(" Please update the .rc file to explictly set 'capabilities NET_RAW'\n"); - printk(" Implicit grants are deprecated and will be removed in the future.\n"); - return 0; - } - if (ret != 0 && cap == CAP_NET_ADMIN && in_egroup_p(AID_NET_ADMIN)) { - printk("Process %s granted CAP_NET_ADMIN from Android group net_admin.\n", current->comm); - printk(" Please update the .rc file to explictly set 'capabilities NET_ADMIN'\n"); - printk(" Implicit grants are deprecated and will be removed in the future.\n"); - return 0; - } -#endif - return ret; -} /** * cap_settime - Determine whether the current process may set the system clock * @ts: The time to set @@ -248,12 +227,11 @@ int cap_capget(struct task_struct *target, kernel_cap_t *effective, */ static inline int cap_inh_is_capped(void) { - /* they are so limited unless the current task has the CAP_SETPCAP * capability */ if (cap_capable(current_cred(), current_cred()->user_ns, - CAP_SETPCAP, SECURITY_CAP_AUDIT) == 0) + CAP_SETPCAP, CAP_OPT_NONE) == 0) return 0; return 1; } @@ -1237,8 +1215,9 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3, || ((old->securebits & SECURE_ALL_LOCKS & ~arg2)) /*[2]*/ || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS)) /*[3]*/ || (cap_capable(current_cred(), - current_cred()->user_ns, CAP_SETPCAP, - SECURITY_CAP_AUDIT) != 0) /*[4]*/ + current_cred()->user_ns, + CAP_SETPCAP, + CAP_OPT_NONE) != 0) /*[4]*/ /* * [1] no changing of bits that are locked * [2] no unlocking of locks @@ -1333,9 +1312,10 @@ int cap_vm_enough_memory(struct mm_struct *mm, long pages) { int cap_sys_admin = 0; - if (cap_capable(current_cred(), &init_user_ns, CAP_SYS_ADMIN, - SECURITY_CAP_NOAUDIT) == 0) + if (cap_capable(current_cred(), &init_user_ns, + CAP_SYS_ADMIN, CAP_OPT_NOAUDIT) == 0) cap_sys_admin = 1; + return cap_sys_admin; } @@ -1354,7 +1334,7 @@ int cap_mmap_addr(unsigned long addr) if (addr < dac_mmap_min_addr) { ret = cap_capable(current_cred(), &init_user_ns, CAP_SYS_RAWIO, - SECURITY_CAP_AUDIT); + CAP_OPT_NONE); /* set PF_SUPERPRIV if it turns out we allow the low mmap */ if (ret == 0) current->flags |= PF_SUPERPRIV; diff --git a/security/keys/internal.h b/security/keys/internal.h index a02742621c8d3925ba3bb463844745998112ee22..d91833e7af9c2f0d6f215792a99871a2ad8670f1 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -158,8 +158,6 @@ extern struct key *request_key_and_link(struct key_type *type, extern bool lookup_user_key_possessed(const struct key *key, const struct key_match_data *match_data); -extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags, - key_perm_t perm); #define KEY_LOOKUP_CREATE 0x01 #define KEY_LOOKUP_PARTIAL 0x02 #define KEY_LOOKUP_FOR_UNLINK 0x04 diff --git a/security/keys/key.c b/security/keys/key.c index 249a6da4d27701be2ba416e8451a7edea0cbd5b4..749a5cf27a192efca427cfba758d49d89b199b0b 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -297,6 +297,7 @@ struct key *key_alloc(struct key_type *type, const char *desc, key->gid = gid; key->perm = perm; key->restrict_link = restrict_link; + key->last_used_at = ktime_get_real_seconds(); if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) key->flags |= 1 << KEY_FLAG_IN_QUOTA; diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index d776d2114651127a3856084590cd980adb75049d..85a24c965ae66e4013be03c9eeabbe13c907c55c 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -756,6 +756,7 @@ key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags, put_cred(ctx.cred); goto try_again; } +EXPORT_SYMBOL(lookup_user_key); /* * Join the named keyring as the session keyring if possible else attempt to diff --git a/security/security.c b/security/security.c index 1baf58502368afc12cf4a206b63769e4678670c8..4a32448e7d267e04b5ad6d641dab285124a20352 100644 --- a/security/security.c +++ b/security/security.c @@ -283,16 +283,12 @@ int security_capset(struct cred *new, const struct cred *old, effective, inheritable, permitted); } -int security_capable(const struct cred *cred, struct user_namespace *ns, - int cap) +int security_capable(const struct cred *cred, + struct user_namespace *ns, + int cap, + unsigned int opts) { - return call_int_hook(capable, 0, cred, ns, cap, SECURITY_CAP_AUDIT); -} - -int security_capable_noaudit(const struct cred *cred, struct user_namespace *ns, - int cap) -{ - return call_int_hook(capable, 0, cred, ns, cap, SECURITY_CAP_NOAUDIT); + return call_int_hook(capable, 0, cred, ns, cap, opts); } int security_quotactl(int cmds, int type, int id, struct super_block *sb) diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 3d1cff23a4583e09b11c84ae81851028f1c57b71..6c7702071c04559f706b555896adbc91158c9d16 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -498,7 +498,7 @@ static inline int avc_xperms_audit(struct selinux_state *state, if (likely(!audited)) return 0; return slow_avc_audit(state, ssid, tsid, tclass, requested, - audited, denied, result, ad, 0); + audited, denied, result, ad); } static void avc_node_free(struct rcu_head *rhead) @@ -766,8 +766,7 @@ static void avc_audit_post_callback(struct audit_buffer *ab, void *a) noinline int slow_avc_audit(struct selinux_state *state, u32 ssid, u32 tsid, u16 tclass, u32 requested, u32 audited, u32 denied, int result, - struct common_audit_data *a, - unsigned int flags) + struct common_audit_data *a) { struct common_audit_data stack_data; struct selinux_audit_data sad; @@ -777,17 +776,6 @@ noinline int slow_avc_audit(struct selinux_state *state, a->type = LSM_AUDIT_DATA_NONE; } - /* - * When in a RCU walk do the audit on the RCU retry. This is because - * the collection of the dname in an inode audit message is not RCU - * safe. Note this may drop some audits when the situation changes - * during retry. However this is logically just as if the operation - * happened a little later. - */ - if ((a->type == LSM_AUDIT_DATA_INODE) && - (flags & MAY_NOT_BLOCK)) - return -ECHILD; - sad.tclass = tclass; sad.requested = requested; sad.ssid = ssid; @@ -860,16 +848,14 @@ static int avc_update_node(struct selinux_avc *avc, /* * If we are in a non-blocking code path, e.g. VFS RCU walk, * then we must not add permissions to a cache entry - * because we cannot safely audit the denial. Otherwise, + * because we will not audit the denial. Otherwise, * during the subsequent blocking retry (e.g. VFS ref walk), we * will find the permissions already granted in the cache entry * and won't audit anything at all, leading to silent denials in * permissive mode that only appear when in enforcing mode. * - * See the corresponding handling in slow_avc_audit(), and the - * logic in selinux_inode_follow_link and selinux_inode_permission - * for the VFS MAY_NOT_BLOCK flag, which is transliterated into - * AVC_NONBLOCKING for avc_has_perm_noaudit(). + * See the corresponding handling of MAY_NOT_BLOCK in avc_audit() + * and selinux_inode_permission(). */ if (flags & AVC_NONBLOCKING) return 0; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index d36e9c3cb41d1c5f31fce01ba2666533241ca203..b0195a9d10a7fa08c057194e6a287f8c34b135e2 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -871,6 +871,7 @@ static int selinux_set_mnt_opts(struct super_block *sb, !strcmp(sb->s_type->name, "tracefs") || !strcmp(sb->s_type->name, "sysfs") || !strcmp(sb->s_type->name, "pstore") || + !strcmp(sb->s_type->name, "binder") || !strcmp(sb->s_type->name, "cgroup") || !strcmp(sb->s_type->name, "cgroup2")) sbsec->flags |= SE_SBGENFS; @@ -1794,7 +1795,7 @@ static inline u32 signal_to_av(int sig) /* Check whether a task is allowed to use a capability. */ static int cred_has_capability(const struct cred *cred, - int cap, int audit, bool initns) + int cap, unsigned int opts, bool initns) { struct common_audit_data ad; struct av_decision avd; @@ -1821,7 +1822,7 @@ static int cred_has_capability(const struct cred *cred, rc = avc_has_perm_noaudit(&selinux_state, sid, sid, sclass, av, 0, &avd); - if (audit == SECURITY_CAP_AUDIT) { + if (!(opts & CAP_OPT_NOAUDIT)) { int rc2 = avc_audit(&selinux_state, sid, sid, sclass, av, &avd, rc, &ad, 0); if (rc2) @@ -2341,9 +2342,9 @@ static int selinux_capset(struct cred *new, const struct cred *old, */ static int selinux_capable(const struct cred *cred, struct user_namespace *ns, - int cap, int audit) + int cap, unsigned int opts) { - return cred_has_capability(cred, cap, audit, ns == &init_user_ns); + return cred_has_capability(cred, cap, opts, ns == &init_user_ns); } static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb) @@ -2417,7 +2418,7 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages) int rc, cap_sys_admin = 0; rc = cred_has_capability(current_cred(), CAP_SYS_ADMIN, - SECURITY_CAP_NOAUDIT, true); + CAP_OPT_NOAUDIT, true); if (rc == 0) cap_sys_admin = 1; @@ -3171,8 +3172,7 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct inode *inode, static noinline int audit_inode_permission(struct inode *inode, u32 perms, u32 audited, u32 denied, - int result, - unsigned flags) + int result) { struct common_audit_data ad; struct inode_security_struct *isec = inode->i_security; @@ -3183,7 +3183,7 @@ static noinline int audit_inode_permission(struct inode *inode, rc = slow_avc_audit(&selinux_state, current_sid(), isec->sid, isec->sclass, perms, - audited, denied, result, &ad, flags); + audited, denied, result, &ad); if (rc) return rc; return 0; @@ -3230,7 +3230,11 @@ static int selinux_inode_permission(struct inode *inode, int mask) if (likely(!audited)) return rc; - rc2 = audit_inode_permission(inode, perms, audited, denied, rc, flags); + /* fall back to ref-walk if we have to generate audit */ + if (flags & MAY_NOT_BLOCK) + return -ECHILD; + + rc2 = audit_inode_permission(inode, perms, audited, denied, rc); if (rc2) return rc2; return rc; @@ -3272,11 +3276,11 @@ static int selinux_inode_getattr(const struct path *path) static bool has_cap_mac_admin(bool audit) { const struct cred *cred = current_cred(); - int cap_audit = audit ? SECURITY_CAP_AUDIT : SECURITY_CAP_NOAUDIT; + unsigned int opts = audit ? CAP_OPT_NONE : CAP_OPT_NOAUDIT; - if (cap_capable(cred, &init_user_ns, CAP_MAC_ADMIN, cap_audit)) + if (cap_capable(cred, &init_user_ns, CAP_MAC_ADMIN, opts)) return false; - if (cred_has_capability(cred, CAP_MAC_ADMIN, cap_audit, true)) + if (cred_has_capability(cred, CAP_MAC_ADMIN, opts, true)) return false; return true; } @@ -3680,7 +3684,7 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd, case KDSKBENT: case KDSKBSENT: error = cred_has_capability(cred, CAP_SYS_TTY_CONFIG, - SECURITY_CAP_AUDIT, true); + CAP_OPT_NONE, true); break; /* default case assumes that the command will go diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index 74ea50977c201fb815431fb356e0858e4239881c..cf4cc3ef959b5b88c13b785e54394a0d45765610 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h @@ -100,8 +100,7 @@ static inline u32 avc_audit_required(u32 requested, int slow_avc_audit(struct selinux_state *state, u32 ssid, u32 tsid, u16 tclass, u32 requested, u32 audited, u32 denied, int result, - struct common_audit_data *a, - unsigned flags); + struct common_audit_data *a); /** * avc_audit - Audit the granting or denial of permissions. @@ -135,9 +134,12 @@ static inline int avc_audit(struct selinux_state *state, audited = avc_audit_required(requested, avd, result, 0, &denied); if (likely(!audited)) return 0; + /* fall back to ref-walk if we have to generate audit */ + if (flags & MAY_NOT_BLOCK) + return -ECHILD; return slow_avc_audit(state, ssid, tsid, tclass, requested, audited, denied, result, - a, flags); + a); } #define AVC_STRICT 1 /* Ignore permissive mode. */ diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index f068ee12a92e023d1ed480d3a6c883668ad2cb2a..779f94086c3cf4e6d1d515052fbf881740cb42d7 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -398,6 +398,7 @@ extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm); extern void avtab_cache_init(void); extern void ebitmap_cache_init(void); extern void hashtab_cache_init(void); +extern void selinux_nlmsg_init(void); extern int security_sidtab_hash_stats(struct selinux_state *state, char *page); extern void selinux_nlmsg_init(void); diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index c071c356a963737583a0354d3c5e9ee8a0c105c3..a7855c61c05cd2383a267511393e3599ecdc7626 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -640,7 +640,7 @@ bool smack_privileged_cred(int cap, const struct cred *cred) struct smack_known_list_elem *sklep; int rc; - rc = cap_capable(cred, &init_user_ns, cap, SECURITY_CAP_AUDIT); + rc = cap_capable(cred, &init_user_ns, cap, CAP_OPT_NONE); if (rc) return false; diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 03923a138ef54430612eceeee9d962b9f37f6699..ca0c98240e0d6cd6522243c2ae7683f8d6b3912b 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -2254,9 +2254,9 @@ static const char * const tomoyo_memory_headers[TOMOYO_MAX_MEMORY_STAT] = { [TOMOYO_MEMORY_QUERY] = "query message:", }; -/* Timestamp counter for last updated. */ -static unsigned int tomoyo_stat_updated[TOMOYO_MAX_POLICY_STAT]; /* Counter for number of updates. */ +static atomic_t tomoyo_stat_updated[TOMOYO_MAX_POLICY_STAT]; +/* Timestamp counter for last updated. */ static time64_t tomoyo_stat_modified[TOMOYO_MAX_POLICY_STAT]; /** @@ -2268,10 +2268,7 @@ static time64_t tomoyo_stat_modified[TOMOYO_MAX_POLICY_STAT]; */ void tomoyo_update_stat(const u8 index) { - /* - * I don't use atomic operations because race condition is not fatal. - */ - tomoyo_stat_updated[index]++; + atomic_inc(&tomoyo_stat_updated[index]); tomoyo_stat_modified[index] = ktime_get_real_seconds(); } @@ -2291,7 +2288,7 @@ static void tomoyo_read_stat(struct tomoyo_io_buffer *head) for (i = 0; i < TOMOYO_MAX_POLICY_STAT; i++) { tomoyo_io_printf(head, "Policy %-30s %10u", tomoyo_policy_headers[i], - tomoyo_stat_updated[i]); + atomic_read(&tomoyo_stat_updated[i])); if (tomoyo_stat_modified[i]) { struct tomoyo_time stamp; tomoyo_convert_time(tomoyo_stat_modified[i], &stamp); diff --git a/sound/aoa/codecs/onyx.c b/sound/aoa/codecs/onyx.c index d2d96ca082b71a204f3f18e7fa207e929564e4da..6224fd3bbf7ccf516ae4c417c806f56904c37169 100644 --- a/sound/aoa/codecs/onyx.c +++ b/sound/aoa/codecs/onyx.c @@ -74,8 +74,10 @@ static int onyx_read_register(struct onyx *onyx, u8 reg, u8 *value) return 0; } v = i2c_smbus_read_byte_data(onyx->i2c, reg); - if (v < 0) + if (v < 0) { + *value = 0; return -1; + } *value = (u8)v; onyx->cache[ONYX_REG_CONTROL-FIRSTREGISTER] = *value; return 0; diff --git a/sound/core/control.c b/sound/core/control.c index 649d3217590ed465c6e13e39bdeb2f9e53a4a2e5..d1312f14d78fbbab98ea47ef09dded5a62fb0118 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -1468,8 +1468,9 @@ static int call_tlv_handler(struct snd_ctl_file *file, int op_flag, if (kctl->tlv.c == NULL) return -ENXIO; - /* When locked, this is unavailable. */ - if (vd->owner != NULL && vd->owner != file) + /* Write and command operations are not allowed for locked element. */ + if (op_flag != SNDRV_CTL_TLV_OP_READ && + vd->owner != NULL && vd->owner != file) return -EPERM; return kctl->tlv.c(kctl, op_flag, size, buf); diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index bd3d68e0489dd9d7ef28643f40d41ddec49c4659..aaf9c419c3ddeedcfdf11bc5e8cc35a7d0b00c84 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -563,7 +563,7 @@ static int update_timestamp_of_queue(struct snd_seq_event *event, event->queue = queue; event->flags &= ~SNDRV_SEQ_TIME_STAMP_MASK; if (real_time) { - event->time.time = snd_seq_timer_get_cur_time(q->timer); + event->time.time = snd_seq_timer_get_cur_time(q->timer, true); event->flags |= SNDRV_SEQ_TIME_STAMP_REAL; } else { event->time.tick = snd_seq_timer_get_cur_tick(q->timer); @@ -1642,7 +1642,7 @@ static int snd_seq_ioctl_get_queue_status(struct snd_seq_client *client, tmr = queue->timer; status->events = queue->tickq->cells + queue->timeq->cells; - status->time = snd_seq_timer_get_cur_time(tmr); + status->time = snd_seq_timer_get_cur_time(tmr, true); status->tick = snd_seq_timer_get_cur_tick(tmr); status->running = tmr->running; diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c index 3b3ac96f1f5f2eace0c0a3d939164b778567ec03..28b4dd45b8d1d06c11f9cd0ed2e21baa0bade4d5 100644 --- a/sound/core/seq/seq_queue.c +++ b/sound/core/seq/seq_queue.c @@ -251,6 +251,8 @@ void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop) { unsigned long flags; struct snd_seq_event_cell *cell; + snd_seq_tick_time_t cur_tick; + snd_seq_real_time_t cur_time; if (q == NULL) return; @@ -267,17 +269,18 @@ void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop) __again: /* Process tick queue... */ + cur_tick = snd_seq_timer_get_cur_tick(q->timer); for (;;) { - cell = snd_seq_prioq_cell_out(q->tickq, - &q->timer->tick.cur_tick); + cell = snd_seq_prioq_cell_out(q->tickq, &cur_tick); if (!cell) break; snd_seq_dispatch_event(cell, atomic, hop); } /* Process time queue... */ + cur_time = snd_seq_timer_get_cur_time(q->timer, false); for (;;) { - cell = snd_seq_prioq_cell_out(q->timeq, &q->timer->cur_time); + cell = snd_seq_prioq_cell_out(q->timeq, &cur_time); if (!cell) break; snd_seq_dispatch_event(cell, atomic, hop); @@ -405,6 +408,7 @@ int snd_seq_queue_check_access(int queueid, int client) int snd_seq_queue_set_owner(int queueid, int client, int locked) { struct snd_seq_queue *q = queueptr(queueid); + unsigned long flags; if (q == NULL) return -EINVAL; @@ -414,8 +418,10 @@ int snd_seq_queue_set_owner(int queueid, int client, int locked) return -EPERM; } + spin_lock_irqsave(&q->owner_lock, flags); q->locked = locked ? 1 : 0; q->owner = client; + spin_unlock_irqrestore(&q->owner_lock, flags); queue_access_unlock(q); queuefree(q); @@ -552,15 +558,17 @@ void snd_seq_queue_client_termination(int client) unsigned long flags; int i; struct snd_seq_queue *q; + bool matched; for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { if ((q = queueptr(i)) == NULL) continue; spin_lock_irqsave(&q->owner_lock, flags); - if (q->owner == client) + matched = (q->owner == client); + if (matched) q->klocked = 1; spin_unlock_irqrestore(&q->owner_lock, flags); - if (q->owner == client) { + if (matched) { if (q->timer->running) snd_seq_timer_stop(q->timer); snd_seq_timer_reset(q->timer); @@ -752,6 +760,8 @@ void snd_seq_info_queues_read(struct snd_info_entry *entry, int i, bpm; struct snd_seq_queue *q; struct snd_seq_timer *tmr; + bool locked; + int owner; for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { if ((q = queueptr(i)) == NULL) @@ -763,9 +773,14 @@ void snd_seq_info_queues_read(struct snd_info_entry *entry, else bpm = 0; + spin_lock_irq(&q->owner_lock); + locked = q->locked; + owner = q->owner; + spin_unlock_irq(&q->owner_lock); + snd_iprintf(buffer, "queue %d: [%s]\n", q->queue, q->name); - snd_iprintf(buffer, "owned by client : %d\n", q->owner); - snd_iprintf(buffer, "lock status : %s\n", q->locked ? "Locked" : "Free"); + snd_iprintf(buffer, "owned by client : %d\n", owner); + snd_iprintf(buffer, "lock status : %s\n", locked ? "Locked" : "Free"); snd_iprintf(buffer, "queued time events : %d\n", snd_seq_prioq_avail(q->timeq)); snd_iprintf(buffer, "queued tick events : %d\n", snd_seq_prioq_avail(q->tickq)); snd_iprintf(buffer, "timer state : %s\n", tmr->running ? "Running" : "Stopped"); diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c index f587d0e2747606e116a26e71722a0b705190bcfd..3da44a4f9257122d40c1db9641bf80517bd57b43 100644 --- a/sound/core/seq/seq_timer.c +++ b/sound/core/seq/seq_timer.c @@ -437,14 +437,15 @@ int snd_seq_timer_continue(struct snd_seq_timer *tmr) } /* return current 'real' time. use timeofday() to get better granularity. */ -snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr) +snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr, + bool adjust_ktime) { snd_seq_real_time_t cur_time; unsigned long flags; spin_lock_irqsave(&tmr->lock, flags); cur_time = tmr->cur_time; - if (tmr->running) { + if (adjust_ktime && tmr->running) { struct timespec64 tm; ktime_get_ts64(&tm); @@ -461,7 +462,13 @@ snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr) high PPQ values) */ snd_seq_tick_time_t snd_seq_timer_get_cur_tick(struct snd_seq_timer *tmr) { - return tmr->tick.cur_tick; + snd_seq_tick_time_t cur_tick; + unsigned long flags; + + spin_lock_irqsave(&tmr->lock, flags); + cur_tick = tmr->tick.cur_tick; + spin_unlock_irqrestore(&tmr->lock, flags); + return cur_tick; } @@ -480,15 +487,19 @@ void snd_seq_info_timer_read(struct snd_info_entry *entry, q = queueptr(idx); if (q == NULL) continue; - if ((tmr = q->timer) == NULL || - (ti = tmr->timeri) == NULL) { - queuefree(q); - continue; - } + mutex_lock(&q->timer_mutex); + tmr = q->timer; + if (!tmr) + goto unlock; + ti = tmr->timeri; + if (!ti) + goto unlock; snd_iprintf(buffer, "Timer for queue %i : %s\n", q->queue, ti->timer->name); resolution = snd_timer_resolution(ti) * tmr->ticks; snd_iprintf(buffer, " Period time : %lu.%09lu\n", resolution / 1000000000, resolution % 1000000000); snd_iprintf(buffer, " Skew : %u / %u\n", tmr->skew, tmr->skew_base); +unlock: + mutex_unlock(&q->timer_mutex); queuefree(q); } } diff --git a/sound/core/seq/seq_timer.h b/sound/core/seq/seq_timer.h index 62f3906710961164975efd18267fb0c50505ddfd..44f52f5963db98103eb09a61b126cf9818ce2128 100644 --- a/sound/core/seq/seq_timer.h +++ b/sound/core/seq/seq_timer.h @@ -135,7 +135,8 @@ int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq); int snd_seq_timer_set_position_tick(struct snd_seq_timer *tmr, snd_seq_tick_time_t position); int snd_seq_timer_set_position_time(struct snd_seq_timer *tmr, snd_seq_real_time_t position); int snd_seq_timer_set_skew(struct snd_seq_timer *tmr, unsigned int skew, unsigned int base); -snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr); +snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr, + bool adjust_ktime); snd_seq_tick_time_t snd_seq_timer_get_cur_tick(struct snd_seq_timer *tmr); extern int seq_default_timer_class; diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index 9af154db530a803943c9dc420d06e5a180d0789f..b78cc8d86a8bbff3363758ae9acb0a02c5c8a593 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -929,7 +929,7 @@ static void print_formats(struct snd_dummy *dummy, { int i; - for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) { + for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) { if (dummy->pcm_hw.formats & (1ULL << i)) snd_iprintf(buffer, " %s", snd_pcm_format_name(i)); } diff --git a/sound/firewire/dice/dice-extension.c b/sound/firewire/dice/dice-extension.c index a63fcbc875adc3b9906f90d13fb3f5c8ea4ebbec..02f4a8318e38e7309da42b3274a59fee95d8349f 100644 --- a/sound/firewire/dice/dice-extension.c +++ b/sound/firewire/dice/dice-extension.c @@ -159,8 +159,11 @@ int snd_dice_detect_extension_formats(struct snd_dice *dice) int j; for (j = i + 1; j < 9; ++j) { - if (pointers[i * 2] == pointers[j * 2]) + if (pointers[i * 2] == pointers[j * 2]) { + // Fallback to limited functionality. + err = -ENXIO; goto end; + } } } diff --git a/sound/hda/ext/hdac_ext_controller.c b/sound/hda/ext/hdac_ext_controller.c index 60cb00fd0c6936a4a3db6751496c15eab2af2e3d..84b44cdae28a1fabdc32bbfb0bb309bd2f64b6d8 100644 --- a/sound/hda/ext/hdac_ext_controller.c +++ b/sound/hda/ext/hdac_ext_controller.c @@ -262,6 +262,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down_all); int snd_hdac_ext_bus_link_get(struct hdac_bus *bus, struct hdac_ext_link *link) { + unsigned long codec_mask; int ret = 0; mutex_lock(&bus->lock); @@ -283,9 +284,11 @@ int snd_hdac_ext_bus_link_get(struct hdac_bus *bus, * HDA spec section 4.3 - Codec Discovery */ udelay(521); - bus->codec_mask = snd_hdac_chip_readw(bus, STATESTS); - dev_dbg(bus->dev, "codec_mask = 0x%lx\n", bus->codec_mask); - snd_hdac_chip_writew(bus, STATESTS, bus->codec_mask); + codec_mask = snd_hdac_chip_readw(bus, STATESTS); + dev_dbg(bus->dev, "codec_mask = 0x%lx\n", codec_mask); + snd_hdac_chip_writew(bus, STATESTS, codec_mask); + if (!bus->codec_mask) + bus->codec_mask = codec_mask; } mutex_unlock(&bus->lock); diff --git a/sound/hda/hdmi_chmap.c b/sound/hda/hdmi_chmap.c index f21633cd9b38ea9a8f6a3c955f9209a8f66ecf2f..acbe61b8db7b26b9fa535fb41f3845b612ac0e19 100644 --- a/sound/hda/hdmi_chmap.c +++ b/sound/hda/hdmi_chmap.c @@ -249,7 +249,7 @@ void snd_hdac_print_channel_allocation(int spk_alloc, char *buf, int buflen) for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) { if (spk_alloc & (1 << i)) - j += snprintf(buf + j, buflen - j, " %s", + j += scnprintf(buf + j, buflen - j, " %s", cea_speaker_allocation_names[i]); } buf[j] = '\0'; /* necessary when j == 0 */ diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 82b0dc9f528f031a4e0f09d315236beee226e869..f3a6b1d869d8a2e06cf4ff05999e859b227b490f 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -4019,7 +4019,7 @@ void snd_print_pcm_bits(int pcm, char *buf, int buflen) for (i = 0, j = 0; i < ARRAY_SIZE(bits); i++) if (pcm & (AC_SUPPCM_BITS_8 << i)) - j += snprintf(buf + j, buflen - j, " %d", bits[i]); + j += scnprintf(buf + j, buflen - j, " %d", bits[i]); buf[j] = '\0'; /* necessary when j == 0 */ } diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h index 8a9dd4767b1ecb65c4ab4e801c29e1ff9f188b9e..63cc10604afc7c70db2ad58ce5ec2300c3b0ad95 100644 --- a/sound/pci/hda/hda_controller.h +++ b/sound/pci/hda/hda_controller.h @@ -176,11 +176,10 @@ struct azx { #define azx_bus(chip) (&(chip)->bus.core) #define bus_to_azx(_bus) container_of(_bus, struct azx, bus.core) -#ifdef CONFIG_X86 -#define azx_snoop(chip) ((chip)->snoop) -#else -#define azx_snoop(chip) true -#endif +static inline bool azx_snoop(struct azx *chip) +{ + return !IS_ENABLED(CONFIG_X86) || chip->snoop; +} /* * macros for easy use diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index ba7fe9b6655c1f25279e5489081f34327f215d45..864cc8c9ada0ae6112445d3a6622f337067e778f 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -373,7 +373,7 @@ static void hdmi_print_pcm_rates(int pcm, char *buf, int buflen) for (i = 0, j = 0; i < ARRAY_SIZE(alsa_rates); i++) if (pcm & (1 << i)) - j += snprintf(buf + j, buflen - j, " %d", + j += scnprintf(buf + j, buflen - j, " %d", alsa_rates[i]); buf[j] = '\0'; /* necessary when j == 0 */ diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index d63fea5d1c92921e8c1930f56bfafe9cfb55ce5a..3ee5b7b9b595319bac92a7e8ba4e1a81165d3798 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2324,6 +2324,8 @@ static struct snd_pci_quirk power_save_blacklist[] = { /* https://bugzilla.redhat.com/show_bug.cgi?id=1581607 */ SND_PCI_QUIRK(0x1558, 0x3501, "Clevo W35xSS_370SS", 0), /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */ + SND_PCI_QUIRK(0x1558, 0x6504, "Clevo W65_67SB", 0), + /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */ SND_PCI_QUIRK(0x1028, 0x0497, "Dell Precision T3600", 0), /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */ /* Note the P55A-UD3 and Z87-D3HP share the subsys id for the HDA dev */ diff --git a/sound/pci/hda/hda_sysfs.c b/sound/pci/hda/hda_sysfs.c index 6ec79c58d48de7e93504f2176aabd0c6dc0530c4..6535155e992dfe4cfb0d223bb16037c665953c1e 100644 --- a/sound/pci/hda/hda_sysfs.c +++ b/sound/pci/hda/hda_sysfs.c @@ -221,7 +221,7 @@ static ssize_t init_verbs_show(struct device *dev, int i, len = 0; mutex_lock(&codec->user_mutex); snd_array_for_each(&codec->init_verbs, i, v) { - len += snprintf(buf + len, PAGE_SIZE - len, + len += scnprintf(buf + len, PAGE_SIZE - len, "0x%02x 0x%03x 0x%04x\n", v->nid, v->verb, v->param); } @@ -271,7 +271,7 @@ static ssize_t hints_show(struct device *dev, int i, len = 0; mutex_lock(&codec->user_mutex); snd_array_for_each(&codec->hints, i, hint) { - len += snprintf(buf + len, PAGE_SIZE - len, + len += scnprintf(buf + len, PAGE_SIZE - len, "%s = %s\n", hint->key, hint->val); } mutex_unlock(&codec->user_mutex); diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 5500dd437b44d399d149c82cef97c15aaba31d88..78bb96263bc271d42c342e1e01f154673af7d219 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -935,6 +935,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = { SND_PCI_QUIRK(0x17aa, 0x215f, "Lenovo T510", CXT_PINCFG_LENOVO_TP410), SND_PCI_QUIRK(0x17aa, 0x21ce, "Lenovo T420", CXT_PINCFG_LENOVO_TP410), SND_PCI_QUIRK(0x17aa, 0x21cf, "Lenovo T520", CXT_PINCFG_LENOVO_TP410), + SND_PCI_QUIRK(0x17aa, 0x21d2, "Lenovo T420s", CXT_PINCFG_LENOVO_TP410), SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT_PINCFG_LENOVO_TP410), SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT_PINCFG_LENOVO_TP410), SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo IdeaPad Z560", CXT_FIXUP_MUTE_LED_EAPD), diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index c827a2a89cc3d772e42662206342ef93efeae17a..c67fadd5aae533af205e1cc21fef65a1835f3f18 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -2604,9 +2604,12 @@ static int alloc_intel_hdmi(struct hda_codec *codec) /* parse and post-process for Intel codecs */ static int parse_intel_hdmi(struct hda_codec *codec) { - int err; + int err, retries = 3; + + do { + err = hdmi_parse_codec(codec); + } while (err < 0 && retries--); - err = hdmi_parse_codec(codec); if (err < 0) { generic_spec_free(codec); return err; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 1f6a036627e83a4b86bc80c595321453335c3d15..86ab022f386ec9cfdafcdb7cb6dd8da7f98179aa 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -424,6 +424,7 @@ static void alc_fill_eapd_coef(struct hda_codec *codec) case 0x10ec0672: alc_update_coef_idx(codec, 0xd, 0, 1<<14); /* EAPD Ctrl */ break; + case 0x10ec0222: case 0x10ec0623: alc_update_coef_idx(codec, 0x19, 1<<13, 0); break; @@ -442,6 +443,7 @@ static void alc_fill_eapd_coef(struct hda_codec *codec) break; case 0x10ec0899: case 0x10ec0900: + case 0x10ec0b00: case 0x10ec1168: case 0x10ec1220: alc_update_coef_idx(codec, 0x7, 1<<1, 0); @@ -2440,6 +2442,10 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC882_FIXUP_EAPD), SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte EP45-DS3/Z87X-UD3H", ALC889_FIXUP_FRONT_HP_NO_PRESENCE), SND_PCI_QUIRK(0x1458, 0xa0b8, "Gigabyte AZ370-Gaming", ALC1220_FIXUP_GB_DUAL_CODECS), + SND_PCI_QUIRK(0x1458, 0xa0cd, "Gigabyte X570 Aorus Master", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1462, 0x1228, "MSI-GP63", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1462, 0x1276, "MSI-GL73", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1462, 0x1293, "MSI-GP65", ALC1220_FIXUP_CLEVO_P950), SND_PCI_QUIRK(0x1462, 0x7350, "MSI-7350", ALC889_FIXUP_CD), SND_PCI_QUIRK(0x1462, 0xda57, "MSI Z270-Gaming", ALC1220_FIXUP_GB_DUAL_CODECS), SND_PCI_QUIRK_VENDOR(0x1462, "MSI", ALC882_FIXUP_GPIO3), @@ -2521,6 +2527,7 @@ static int patch_alc882(struct hda_codec *codec) case 0x10ec0882: case 0x10ec0885: case 0x10ec0900: + case 0x10ec0b00: case 0x10ec1220: break; default: @@ -6459,6 +6466,8 @@ static const struct hda_fixup alc269_fixups[] = { [ALC285_FIXUP_SPEAKER2_TO_DAC1] = { .type = HDA_FIXUP_FUNC, .v.func = alc285_fixup_speaker2_to_dac1, + .chained = true, + .chain_id = ALC269_FIXUP_THINKPAD_ACPI }, [ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER] = { .type = HDA_FIXUP_PINS, @@ -6880,6 +6889,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x0935, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB), SND_PCI_QUIRK(0x1028, 0x097e, "Dell Precision", ALC289_FIXUP_DUAL_SPK), SND_PCI_QUIRK(0x1028, 0x097d, "Dell Precision", ALC289_FIXUP_DUAL_SPK), + SND_PCI_QUIRK(0x1028, 0x098d, "Dell Precision", ALC233_FIXUP_ASUS_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x09bf, "Dell Precision", ALC233_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2), @@ -7039,6 +7050,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x224c, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x224d, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x225d, "Thinkpad T480", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x2292, "Thinkpad X1 Yoga 7th", ALC285_FIXUP_SPEAKER2_TO_DAC1), SND_PCI_QUIRK(0x17aa, 0x2293, "Thinkpad X1 Carbon 7th", ALC285_FIXUP_SPEAKER2_TO_DAC1), 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), @@ -8989,6 +9001,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = { HDA_CODEC_ENTRY(0x10ec0892, "ALC892", patch_alc662), HDA_CODEC_ENTRY(0x10ec0899, "ALC898", patch_alc882), HDA_CODEC_ENTRY(0x10ec0900, "ALC1150", patch_alc882), + HDA_CODEC_ENTRY(0x10ec0b00, "ALCS1200A", patch_alc882), HDA_CODEC_ENTRY(0x10ec1168, "ALC1220", patch_alc882), HDA_CODEC_ENTRY(0x10ec1220, "ALC1220", patch_alc882), {} /* terminator */ diff --git a/sound/sh/aica.c b/sound/sh/aica.c index 2b26311405a422206e869ce186bd17255a847d0a..69ac44b335602c9c529ce2e0e6e075fbbbc46ddd 100644 --- a/sound/sh/aica.c +++ b/sound/sh/aica.c @@ -117,10 +117,10 @@ static void spu_memset(u32 toi, u32 what, int length) } /* spu_memload - write to SPU address space */ -static void spu_memload(u32 toi, void *from, int length) +static void spu_memload(u32 toi, const void *from, int length) { unsigned long flags; - u32 *froml = from; + const u32 *froml = from; u32 __iomem *to = (u32 __iomem *) (SPU_MEMORY_BASE + toi); int i; u32 val; @@ -303,7 +303,7 @@ static void aica_period_elapsed(struct timer_list *t) { struct snd_card_aica *dreamcastcard = from_timer(dreamcastcard, t, timer); - struct snd_pcm_substream *substream = dreamcastcard->timer_substream; + struct snd_pcm_substream *substream = dreamcastcard->substream; /*timer function - so cannot sleep */ int play_period; struct snd_pcm_runtime *runtime; @@ -335,13 +335,6 @@ static void spu_begin_dma(struct snd_pcm_substream *substream) dreamcastcard = substream->pcm->private_data; /*get the queue to do the work */ schedule_work(&(dreamcastcard->spu_dma_work)); - /* Timer may already be running */ - if (unlikely(dreamcastcard->timer_substream)) { - mod_timer(&dreamcastcard->timer, jiffies + 4); - return; - } - timer_setup(&dreamcastcard->timer, aica_period_elapsed, 0); - dreamcastcard->timer_substream = substream; mod_timer(&dreamcastcard->timer, jiffies + 4); } @@ -379,8 +372,8 @@ static int snd_aicapcm_pcm_close(struct snd_pcm_substream { struct snd_card_aica *dreamcastcard = substream->pcm->private_data; flush_work(&(dreamcastcard->spu_dma_work)); - if (dreamcastcard->timer_substream) - del_timer(&dreamcastcard->timer); + del_timer(&dreamcastcard->timer); + dreamcastcard->substream = NULL; kfree(dreamcastcard->channel); spu_disable(); return 0; @@ -615,6 +608,7 @@ static int snd_aica_probe(struct platform_device *devptr) "Yamaha AICA Super Intelligent Sound Processor for SEGA Dreamcast"); /* Prepare to use the queue */ INIT_WORK(&(dreamcastcard->spu_dma_work), run_spu_dma); + timer_setup(&dreamcastcard->timer, aica_period_elapsed, 0); /* Load the PCM 'chip' */ err = snd_aicapcmchip(dreamcastcard, 0); if (unlikely(err < 0)) diff --git a/sound/sh/sh_dac_audio.c b/sound/sh/sh_dac_audio.c index 834b2574786f50da6e986ff24eeffe8b3d3178db..6251b5e1b64a2b0594ee0e2095456ba5511d2df6 100644 --- a/sound/sh/sh_dac_audio.c +++ b/sound/sh/sh_dac_audio.c @@ -190,7 +190,6 @@ static int snd_sh_dac_pcm_copy(struct snd_pcm_substream *substream, { /* channel is not used (interleaved data) */ struct snd_sh_dac *chip = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; if (copy_from_user_toio(chip->data_buffer + pos, src, count)) return -EFAULT; @@ -210,7 +209,6 @@ static int snd_sh_dac_pcm_copy_kernel(struct snd_pcm_substream *substream, { /* channel is not used (interleaved data) */ struct snd_sh_dac *chip = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; memcpy_toio(chip->data_buffer + pos, src, count); chip->buffer_end = chip->data_buffer + pos + count; @@ -229,7 +227,6 @@ static int snd_sh_dac_pcm_silence(struct snd_pcm_substream *substream, { /* channel is not used (interleaved data) */ struct snd_sh_dac *chip = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; memset_io(chip->data_buffer + pos, 0, count); chip->buffer_end = chip->data_buffer + pos + count; diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index 64b784e96f844d3c77efe4eb8623dc3a23de2d20..dad778e5884bd35d4097fa333dd5112b6b553e20 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -25,6 +25,8 @@ config SND_ATMEL_SOC_DMA config SND_ATMEL_SOC_SSC_DMA tristate + select SND_ATMEL_SOC_DMA + select SND_ATMEL_SOC_PDC config SND_ATMEL_SOC_SSC tristate diff --git a/sound/soc/codecs/cs4349.c b/sound/soc/codecs/cs4349.c index bee0e343723fee44c3661d3446f2382a49b58d4e..f08d7a296c0cc9d0f3894fd4ae9f2a88f573852c 100644 --- a/sound/soc/codecs/cs4349.c +++ b/sound/soc/codecs/cs4349.c @@ -381,6 +381,7 @@ static struct i2c_driver cs4349_i2c_driver = { .driver = { .name = "cs4349", .of_match_table = cs4349_of_match, + .pm = &cs4349_runtime_pm, }, .id_table = cs4349_i2c_id, .probe = cs4349_i2c_probe, diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index e9fc2fd97d2f4f6b13c46e7b0a58e03d2bc1a7ff..3afa163f7652d36b2878e79b1feeb690e08a4cb4 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -231,7 +231,7 @@ static const struct soc_enum es8328_rline_enum = ARRAY_SIZE(es8328_line_texts), es8328_line_texts); static const struct snd_kcontrol_new es8328_right_line_controls = - SOC_DAPM_ENUM("Route", es8328_lline_enum); + SOC_DAPM_ENUM("Route", es8328_rline_enum); /* Left Mixer */ static const struct snd_kcontrol_new es8328_left_mixer_controls[] = { diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c index 4f0497ab1aa80752dbeb0a709414a5e6098e273e..cbdb6d4bb91ef1c50161b970c2603ccd68e7bcb9 100644 --- a/sound/soc/codecs/msm8916-wcd-analog.c +++ b/sound/soc/codecs/msm8916-wcd-analog.c @@ -388,9 +388,6 @@ static int pm8916_wcd_analog_enable_micbias_int(struct snd_soc_component switch (event) { case SND_SOC_DAPM_PRE_PMU: - snd_soc_component_update_bits(component, CDC_A_MICB_1_INT_RBIAS, - MICB_1_INT_TX2_INT_RBIAS_EN_MASK, - MICB_1_INT_TX2_INT_RBIAS_EN_ENABLE); snd_soc_component_update_bits(component, reg, MICB_1_EN_PULL_DOWN_EN_MASK, 0); snd_soc_component_update_bits(component, CDC_A_MICB_1_EN, MICB_1_EN_OPA_STG2_TAIL_CURR_MASK, @@ -440,6 +437,14 @@ static int pm8916_wcd_analog_enable_micbias_int1(struct struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct pm8916_wcd_analog_priv *wcd = snd_soc_component_get_drvdata(component); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, CDC_A_MICB_1_INT_RBIAS, + MICB_1_INT_TX1_INT_RBIAS_EN_MASK, + MICB_1_INT_TX1_INT_RBIAS_EN_ENABLE); + break; + } + return pm8916_wcd_analog_enable_micbias_int(component, event, w->reg, wcd->micbias1_cap_mode); } @@ -550,6 +555,11 @@ static int pm8916_wcd_analog_enable_micbias_int2(struct struct pm8916_wcd_analog_priv *wcd = snd_soc_component_get_drvdata(component); switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, CDC_A_MICB_1_INT_RBIAS, + MICB_1_INT_TX2_INT_RBIAS_EN_MASK, + MICB_1_INT_TX2_INT_RBIAS_EN_ENABLE); + break; case SND_SOC_DAPM_POST_PMU: pm8916_mbhc_configure_bias(wcd, true); break; @@ -885,10 +895,10 @@ static const struct snd_soc_dapm_widget pm8916_wcd_analog_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("MIC BIAS External1", CDC_A_MICB_1_EN, 7, 0, pm8916_wcd_analog_enable_micbias_ext1, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_SUPPLY("MIC BIAS External2", CDC_A_MICB_2_EN, 7, 0, pm8916_wcd_analog_enable_micbias_ext2, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_ADC_E("ADC1", NULL, CDC_A_TX_1_EN, 7, 0, pm8916_wcd_analog_enable_adc, diff --git a/sound/soc/codecs/msm8916-wcd-digital.c b/sound/soc/codecs/msm8916-wcd-digital.c index 3063dedd21cff7255c7f437405a9595902352e57..6de2ab6f970685a7f081f4f57ecc64e62d7c62e8 100644 --- a/sound/soc/codecs/msm8916-wcd-digital.c +++ b/sound/soc/codecs/msm8916-wcd-digital.c @@ -357,6 +357,12 @@ static int msm8916_wcd_digital_enable_interpolator( snd_soc_component_write(component, rx_gain_reg[w->shift], snd_soc_component_read32(component, rx_gain_reg[w->shift])); break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, LPASS_CDC_CLK_RX_RESET_CTL, + 1 << w->shift, 1 << w->shift); + snd_soc_component_update_bits(component, LPASS_CDC_CLK_RX_RESET_CTL, + 1 << w->shift, 0x0); + break; } return 0; } diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index f0f2d4fd3769f5785c55d42a613697a6682042b6..5272c81641c1cca61f83e65094427ee00affd71e 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -1437,13 +1437,15 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap) } pcm512x->sclk = devm_clk_get(dev, NULL); - if (PTR_ERR(pcm512x->sclk) == -EPROBE_DEFER) - return -EPROBE_DEFER; + if (PTR_ERR(pcm512x->sclk) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto err; + } if (!IS_ERR(pcm512x->sclk)) { ret = clk_prepare_enable(pcm512x->sclk); if (ret != 0) { dev_err(dev, "Failed to enable SCLK: %d\n", ret); - return ret; + goto err; } } diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 27770143ae8f2210b6461dff39ef90cf2b690f86..974e1a4491723562da66c08f02fdb1e365858c7a 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -2435,6 +2435,13 @@ static void rt5640_disable_jack_detect(struct snd_soc_component *component) { struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + /* + * soc_remove_component() force-disables jack and thus rt5640->jack + * could be NULL at the time of driver's module unloading. + */ + if (!rt5640->jack) + return; + disable_irq(rt5640->irq); rt5640_cancel_work(rt5640); diff --git a/sound/soc/codecs/wm8737.c b/sound/soc/codecs/wm8737.c index e9ae821e76098907aa3da856b0aaa39a656203b8..568b77692f5f0ba489d5aa3409aa146e9d5cb3e6 100644 --- a/sound/soc/codecs/wm8737.c +++ b/sound/soc/codecs/wm8737.c @@ -170,7 +170,7 @@ SOC_DOUBLE("Polarity Invert Switch", WM8737_ADC_CONTROL, 5, 6, 1, 0), SOC_SINGLE("3D Switch", WM8737_3D_ENHANCE, 0, 1, 0), SOC_SINGLE("3D Depth", WM8737_3D_ENHANCE, 1, 15, 0), SOC_ENUM("3D Low Cut-off", low_3d), -SOC_ENUM("3D High Cut-off", low_3d), +SOC_ENUM("3D High Cut-off", high_3d), SOC_SINGLE_TLV("3D ADC Volume", WM8737_3D_ENHANCE, 7, 1, 1, adc_tlv), SOC_SINGLE("Noise Gate Switch", WM8737_NOISE_GATE, 0, 1, 0), diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c index ccdf088461b7f078e1f10891e2ce0db3db4c7023..54c306707c02cc67c360445ad758c977bacb88e3 100644 --- a/sound/soc/codecs/wm9705.c +++ b/sound/soc/codecs/wm9705.c @@ -325,8 +325,7 @@ static int wm9705_soc_probe(struct snd_soc_component *component) if (wm9705->mfd_pdata) { wm9705->ac97 = wm9705->mfd_pdata->ac97; regmap = wm9705->mfd_pdata->regmap; - } else { -#ifdef CONFIG_SND_SOC_AC97_BUS + } else if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS)) { wm9705->ac97 = snd_soc_new_ac97_component(component, WM9705_VENDOR_ID, WM9705_VENDOR_ID_MASK); if (IS_ERR(wm9705->ac97)) { @@ -339,7 +338,8 @@ static int wm9705_soc_probe(struct snd_soc_component *component) snd_soc_free_ac97_component(wm9705->ac97); return PTR_ERR(regmap); } -#endif + } else { + return -ENXIO; } snd_soc_component_set_drvdata(component, wm9705->ac97); @@ -350,14 +350,12 @@ static int wm9705_soc_probe(struct snd_soc_component *component) static void wm9705_soc_remove(struct snd_soc_component *component) { -#ifdef CONFIG_SND_SOC_AC97_BUS struct wm9705_priv *wm9705 = snd_soc_component_get_drvdata(component); - if (!wm9705->mfd_pdata) { + if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS) && !wm9705->mfd_pdata) { snd_soc_component_exit_regmap(component); snd_soc_free_ac97_component(wm9705->ac97); } -#endif } static const struct snd_soc_component_driver soc_component_dev_wm9705 = { diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index ade34c26ad2f3381d806ff069044d77c4186c3d8..01949eaba4fd49f4f6980e3092e1abb233165734 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -638,13 +638,13 @@ static int wm9712_soc_probe(struct snd_soc_component *component) { struct wm9712_priv *wm9712 = snd_soc_component_get_drvdata(component); struct regmap *regmap; - int ret; if (wm9712->mfd_pdata) { wm9712->ac97 = wm9712->mfd_pdata->ac97; regmap = wm9712->mfd_pdata->regmap; - } else { -#ifdef CONFIG_SND_SOC_AC97_BUS + } else if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS)) { + int ret; + wm9712->ac97 = snd_soc_new_ac97_component(component, WM9712_VENDOR_ID, WM9712_VENDOR_ID_MASK); if (IS_ERR(wm9712->ac97)) { @@ -659,7 +659,8 @@ static int wm9712_soc_probe(struct snd_soc_component *component) snd_soc_free_ac97_component(wm9712->ac97); return PTR_ERR(regmap); } -#endif + } else { + return -ENXIO; } snd_soc_component_init_regmap(component, regmap); @@ -672,14 +673,12 @@ static int wm9712_soc_probe(struct snd_soc_component *component) static void wm9712_soc_remove(struct snd_soc_component *component) { -#ifdef CONFIG_SND_SOC_AC97_BUS struct wm9712_priv *wm9712 = snd_soc_component_get_drvdata(component); - if (!wm9712->mfd_pdata) { + if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS) && !wm9712->mfd_pdata) { snd_soc_component_exit_regmap(component); snd_soc_free_ac97_component(wm9712->ac97); } -#endif } static const struct snd_soc_component_driver soc_component_dev_wm9712 = { diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 643863bb32e0d34996c854890d7cbf60b24bd9ef..5a2fdf4f69bf3bc0f72dbd7b6ded0c3252314a7b 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -1214,8 +1214,7 @@ static int wm9713_soc_probe(struct snd_soc_component *component) if (wm9713->mfd_pdata) { wm9713->ac97 = wm9713->mfd_pdata->ac97; regmap = wm9713->mfd_pdata->regmap; - } else { -#ifdef CONFIG_SND_SOC_AC97_BUS + } else if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS)) { wm9713->ac97 = snd_soc_new_ac97_component(component, WM9713_VENDOR_ID, WM9713_VENDOR_ID_MASK); if (IS_ERR(wm9713->ac97)) @@ -1225,7 +1224,8 @@ static int wm9713_soc_probe(struct snd_soc_component *component) snd_soc_free_ac97_component(wm9713->ac97); return PTR_ERR(regmap); } -#endif + } else { + return -ENXIO; } snd_soc_component_init_regmap(component, regmap); @@ -1238,14 +1238,12 @@ static int wm9713_soc_probe(struct snd_soc_component *component) static void wm9713_soc_remove(struct snd_soc_component *component) { -#ifdef CONFIG_SND_SOC_AC97_BUS struct wm9713_priv *wm9713 = snd_soc_component_get_drvdata(component); - if (!wm9713->mfd_pdata) { + if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS) && !wm9713->mfd_pdata) { snd_soc_component_exit_regmap(component); snd_soc_free_ac97_component(wm9713->ac97); } -#endif } static const struct snd_soc_component_driver soc_component_dev_wm9713 = { diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 6a8c279a4b20b472a2c245b8d7c6aacc794c3d31..14ab16e1369f8cf59501956c5cdb6828f69146e3 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -874,14 +874,13 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream, active_slots = hweight32(mcasp->tdm_mask[stream]); active_serializers = (channels + active_slots - 1) / active_slots; - if (active_serializers == 1) { + if (active_serializers == 1) active_slots = channels; - for (i = 0; i < total_slots; i++) { - if ((1 << i) & mcasp->tdm_mask[stream]) { - mask |= (1 << i); - if (--active_slots <= 0) - break; - } + for (i = 0; i < total_slots; i++) { + if ((1 << i) & mcasp->tdm_mask[stream]) { + mask |= (1 << i); + if (--active_slots <= 0) + break; } } } else { diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c index 9b9a7ec52905559a367bc1038e08ea604d5885d2..4bd8da3a5f5b8b7531ba8861e030d92e61bb4099 100644 --- a/sound/soc/fsl/imx-sgtl5000.c +++ b/sound/soc/fsl/imx-sgtl5000.c @@ -112,7 +112,8 @@ static int imx_sgtl5000_probe(struct platform_device *pdev) codec_dev = of_find_i2c_device_by_node(codec_np); if (!codec_dev) { dev_err(&pdev->dev, "failed to find codec platform device\n"); - return -EPROBE_DEFER; + ret = -EPROBE_DEFER; + goto fail; } data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index faf1cba57abbbf7c0cb400acbbb5dd6251259a9a..a0714c0e6e8b4d68f466535e6848a2c6f04689ca 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -42,8 +42,8 @@ static ssize_t skl_print_pins(struct skl_module_pin *m_pin, char *buf, int i; ssize_t ret = 0; - for (i = 0; i < max_pin; i++) - ret += snprintf(buf + size, MOD_BUF - size, + for (i = 0; i < max_pin; i++) { + ret += scnprintf(buf + size, MOD_BUF - size, "%s %d\n\tModule %d\n\tInstance %d\n\t" "In-used %s\n\tType %s\n" "\tState %d\n\tIndex %d\n", @@ -53,13 +53,15 @@ static ssize_t skl_print_pins(struct skl_module_pin *m_pin, char *buf, m_pin[i].in_use ? "Used" : "Unused", m_pin[i].is_dynamic ? "Dynamic" : "Static", m_pin[i].pin_state, i); + size += ret; + } return ret; } static ssize_t skl_print_fmt(struct skl_module_fmt *fmt, char *buf, ssize_t size, bool direction) { - return snprintf(buf + size, MOD_BUF - size, + return scnprintf(buf + size, MOD_BUF - size, "%s\n\tCh %d\n\tFreq %d\n\tBit depth %d\n\t" "Valid bit depth %d\n\tCh config %#x\n\tInterleaving %d\n\t" "Sample Type %d\n\tCh Map %#x\n", @@ -81,16 +83,16 @@ static ssize_t module_read(struct file *file, char __user *user_buf, if (!buf) return -ENOMEM; - ret = snprintf(buf, MOD_BUF, "Module:\n\tUUID %pUL\n\tModule id %d\n" + ret = scnprintf(buf, MOD_BUF, "Module:\n\tUUID %pUL\n\tModule id %d\n" "\tInstance id %d\n\tPvt_id %d\n", mconfig->guid, mconfig->id.module_id, mconfig->id.instance_id, mconfig->id.pvt_id); - ret += snprintf(buf + ret, MOD_BUF - ret, + ret += scnprintf(buf + ret, MOD_BUF - ret, "Resources:\n\tMCPS %#x\n\tIBS %#x\n\tOBS %#x\t\n", mconfig->mcps, mconfig->ibs, mconfig->obs); - ret += snprintf(buf + ret, MOD_BUF - ret, + ret += scnprintf(buf + ret, MOD_BUF - ret, "Module data:\n\tCore %d\n\tIn queue %d\n\t" "Out queue %d\n\tType %s\n", mconfig->core_id, mconfig->max_in_queue, @@ -100,38 +102,38 @@ static ssize_t module_read(struct file *file, char __user *user_buf, ret += skl_print_fmt(mconfig->in_fmt, buf, ret, true); ret += skl_print_fmt(mconfig->out_fmt, buf, ret, false); - ret += snprintf(buf + ret, MOD_BUF - ret, + ret += scnprintf(buf + ret, MOD_BUF - ret, "Fixup:\n\tParams %#x\n\tConverter %#x\n", mconfig->params_fixup, mconfig->converter); - ret += snprintf(buf + ret, MOD_BUF - ret, + ret += scnprintf(buf + ret, MOD_BUF - ret, "Module Gateway:\n\tType %#x\n\tVbus %#x\n\tHW conn %#x\n\tSlot %#x\n", mconfig->dev_type, mconfig->vbus_id, mconfig->hw_conn_type, mconfig->time_slot); - ret += snprintf(buf + ret, MOD_BUF - ret, + ret += scnprintf(buf + ret, MOD_BUF - ret, "Pipeline:\n\tID %d\n\tPriority %d\n\tConn Type %d\n\t" "Pages %#x\n", mconfig->pipe->ppl_id, mconfig->pipe->pipe_priority, mconfig->pipe->conn_type, mconfig->pipe->memory_pages); - ret += snprintf(buf + ret, MOD_BUF - ret, + ret += scnprintf(buf + ret, MOD_BUF - ret, "\tParams:\n\t\tHost DMA %d\n\t\tLink DMA %d\n", mconfig->pipe->p_params->host_dma_id, mconfig->pipe->p_params->link_dma_id); - ret += snprintf(buf + ret, MOD_BUF - ret, + ret += scnprintf(buf + ret, MOD_BUF - ret, "\tPCM params:\n\t\tCh %d\n\t\tFreq %d\n\t\tFormat %d\n", mconfig->pipe->p_params->ch, mconfig->pipe->p_params->s_freq, mconfig->pipe->p_params->s_fmt); - ret += snprintf(buf + ret, MOD_BUF - ret, + ret += scnprintf(buf + ret, MOD_BUF - ret, "\tLink %#x\n\tStream %#x\n", mconfig->pipe->p_params->linktype, mconfig->pipe->p_params->stream); - ret += snprintf(buf + ret, MOD_BUF - ret, + ret += scnprintf(buf + ret, MOD_BUF - ret, "\tState %d\n\tPassthru %s\n", mconfig->pipe->state, mconfig->pipe->passthru ? "true" : "false"); @@ -141,7 +143,7 @@ static ssize_t module_read(struct file *file, char __user *user_buf, ret += skl_print_pins(mconfig->m_out_pin, buf, mconfig->max_out_queue, ret, false); - ret += snprintf(buf + ret, MOD_BUF - ret, + ret += scnprintf(buf + ret, MOD_BUF - ret, "Other:\n\tDomain %d\n\tHomogeneous Input %s\n\t" "Homogeneous Output %s\n\tIn Queue Mask %d\n\t" "Out Queue Mask %d\n\tDMA ID %d\n\tMem Pages %d\n\t" @@ -199,7 +201,7 @@ static ssize_t fw_softreg_read(struct file *file, char __user *user_buf, __ioread32_copy(d->fw_read_buff, fw_reg_addr, w0_stat_sz >> 2); for (offset = 0; offset < FW_REG_SIZE; offset += 16) { - ret += snprintf(tmp + ret, FW_REG_BUF - ret, "%#.4x: ", offset); + ret += scnprintf(tmp + ret, FW_REG_BUF - ret, "%#.4x: ", offset); hex_dump_to_buffer(d->fw_read_buff + offset, 16, 16, 4, tmp + ret, FW_REG_BUF - ret, 0); ret += strlen(tmp + ret); diff --git a/sound/soc/meson/axg-tdmin.c b/sound/soc/meson/axg-tdmin.c index bbac44c816886f79a5aab22e01ad07d179985498..37207bbebb2a4fc6741e4d89f59326d5cc176796 100644 --- a/sound/soc/meson/axg-tdmin.c +++ b/sound/soc/meson/axg-tdmin.c @@ -119,7 +119,6 @@ static int axg_tdmin_prepare(struct regmap *map, struct axg_tdm_stream *ts) break; case SND_SOC_DAIFMT_LEFT_J: - case SND_SOC_DAIFMT_RIGHT_J: case SND_SOC_DAIFMT_DSP_B: val = TDMIN_CTRL_IN_BIT_SKEW(2); break; diff --git a/sound/soc/meson/axg-tdmout.c b/sound/soc/meson/axg-tdmout.c index f73368ee1088fd156657ffa891c6447b3cdd7903..d11acb3cc696f156cad3e8fe390869f9aa356767 100644 --- a/sound/soc/meson/axg-tdmout.c +++ b/sound/soc/meson/axg-tdmout.c @@ -136,7 +136,6 @@ static int axg_tdmout_prepare(struct regmap *map, struct axg_tdm_stream *ts) break; case SND_SOC_DAIFMT_LEFT_J: - case SND_SOC_DAIFMT_RIGHT_J: case SND_SOC_DAIFMT_DSP_B: val |= TDMOUT_CTRL0_INIT_BITNUM(2); break; diff --git a/sound/soc/qcom/apq8016_sbc.c b/sound/soc/qcom/apq8016_sbc.c index 1dd23bba1bedb1e0e6359b778956a324fb40fc40..4b559932adc3377b00a10b50d0d6ef91e2dbdcf0 100644 --- a/sound/soc/qcom/apq8016_sbc.c +++ b/sound/soc/qcom/apq8016_sbc.c @@ -164,41 +164,52 @@ static struct apq8016_sbc_data *apq8016_sbc_parse_of(struct snd_soc_card *card) if (!cpu || !codec) { dev_err(dev, "Can't find cpu/codec DT node\n"); - return ERR_PTR(-EINVAL); + ret = -EINVAL; + goto error; } link->cpu_of_node = of_parse_phandle(cpu, "sound-dai", 0); if (!link->cpu_of_node) { dev_err(card->dev, "error getting cpu phandle\n"); - return ERR_PTR(-EINVAL); + ret = -EINVAL; + goto error; } ret = snd_soc_of_get_dai_name(cpu, &link->cpu_dai_name); if (ret) { dev_err(card->dev, "error getting cpu dai name\n"); - return ERR_PTR(ret); + goto error; } ret = snd_soc_of_get_dai_link_codecs(dev, codec, link); if (ret < 0) { dev_err(card->dev, "error getting codec dai name\n"); - return ERR_PTR(ret); + goto error; } link->platform_of_node = link->cpu_of_node; ret = of_property_read_string(np, "link-name", &link->name); if (ret) { dev_err(card->dev, "error getting codec dai_link name\n"); - return ERR_PTR(ret); + goto error; } link->stream_name = link->name; link->init = apq8016_sbc_dai_init; link++; + + of_node_put(cpu); + of_node_put(codec); } return data; + + error: + of_node_put(np); + of_node_put(cpu); + of_node_put(codec); + return ERR_PTR(ret); } static const struct snd_soc_dapm_widget apq8016_sbc_dapm_widgets[] = { diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 727a1b42768f4cab8dee82babaacb991ffd0f715..3679936eb1ae3225107f3413decc84684204f4f3 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -754,6 +754,28 @@ struct snd_soc_component *soc_find_component( } EXPORT_SYMBOL(soc_find_component); +/** + * soc_find_component_locked: soc_find_component with client lock acquired + * + * @of_node: of_node of the component to query. + * @name: name of the component to query. + * + * function to find out if a component is already registered with ASoC core. + * + * Returns component handle for success, else NULL error. + */ +struct snd_soc_component *soc_find_component_locked( + const struct device_node *of_node, const char *name) +{ + struct snd_soc_component *component = NULL; + + mutex_lock(&client_mutex); + component = soc_find_component(of_node, name); + mutex_unlock(&client_mutex); + return component; +} +EXPORT_SYMBOL(soc_find_component_locked); + /** * snd_soc_find_dai - Find a registered DAI * @@ -1938,6 +1960,8 @@ static void soc_check_tplg_fes(struct snd_soc_card *card) /* convert non BE into BE */ dai_link->no_pcm = 1; + dai_link->dpcm_playback = 1; + dai_link->dpcm_capture = 1; /* override any BE fixups */ dai_link->be_hw_params_fixup = diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 7cdca89f49251b3dd5c26c0b524f16712b6e5e1c..475605e2a9d79993b03edb244f86c4767abf22ef 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -4562,7 +4562,7 @@ static void soc_dapm_shutdown_dapm(struct snd_soc_dapm_context *dapm) continue; if (w->power) { dapm_seq_insert(w, &down_list, false); - w->power = 0; + w->new_power = 0; powerdown = 1; } } diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 615844f742466e858eca9acd83147d92e7e8979c..fe6c529fe1c592b7bbe723cad65564c6c86048e2 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -67,8 +67,8 @@ static bool snd_soc_dai_stream_valid(struct snd_soc_dai *dai, int stream) else codec_stream = &dai->driver->capture; - /* If the codec specifies any rate at all, it supports the stream. */ - return codec_stream->rates; + /* If the codec specifies any channels at all, it supports the stream */ + return codec_stream->channels_min; } /** @@ -2530,42 +2530,81 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, } EXPORT_SYMBOL_GPL(dpcm_be_dai_trigger); +static int dpcm_dai_trigger_fe_be(struct snd_pcm_substream *substream, + int cmd, bool fe_first) +{ + struct snd_soc_pcm_runtime *fe = substream->private_data; + int ret; + + /* call trigger on the frontend before the backend. */ + if (fe_first) { + dev_dbg(fe->dev, "ASoC: pre trigger FE %s cmd %d\n", + fe->dai_link->name, cmd); + + ret = soc_pcm_trigger(substream, cmd); + if (ret < 0) + return ret; + + ret = dpcm_be_dai_trigger(fe, substream->stream, cmd); + return ret; + } + + /* call trigger on the frontend after the backend. */ + ret = dpcm_be_dai_trigger(fe, substream->stream, cmd); + if (ret < 0) + return ret; + + dev_dbg(fe->dev, "ASoC: post trigger FE %s cmd %d\n", + fe->dai_link->name, cmd); + + ret = soc_pcm_trigger(substream, cmd); + + return ret; +} + static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *fe = substream->private_data; - int stream = substream->stream, ret; + int stream = substream->stream; + int ret = 0; enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream]; fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; switch (trigger) { case SND_SOC_DPCM_TRIGGER_PRE: - /* call trigger on the frontend before the backend. */ - - dev_dbg(fe->dev, "ASoC: pre trigger FE %s cmd %d\n", - fe->dai_link->name, cmd); - - ret = soc_pcm_trigger(substream, cmd); - if (ret < 0) { - dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret); - goto out; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ret = dpcm_dai_trigger_fe_be(substream, cmd, true); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + ret = dpcm_dai_trigger_fe_be(substream, cmd, false); + break; + default: + ret = -EINVAL; + break; } - - ret = dpcm_be_dai_trigger(fe, substream->stream, cmd); break; case SND_SOC_DPCM_TRIGGER_POST: - /* call trigger on the frontend after the backend. */ - - ret = dpcm_be_dai_trigger(fe, substream->stream, cmd); - if (ret < 0) { - dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret); - goto out; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ret = dpcm_dai_trigger_fe_be(substream, cmd, false); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + ret = dpcm_dai_trigger_fe_be(substream, cmd, true); + break; + default: + ret = -EINVAL; + break; } - - dev_dbg(fe->dev, "ASoC: post trigger FE %s cmd %d\n", - fe->dai_link->name, cmd); - - ret = soc_pcm_trigger(substream, cmd); break; case SND_SOC_DPCM_TRIGGER_BESPOKE: /* bespoke trigger() - handles both FE and BEs */ @@ -2574,10 +2613,6 @@ static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd) fe->dai_link->name, cmd); ret = soc_pcm_bespoke_trigger(substream, cmd); - if (ret < 0) { - dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret); - goto out; - } break; default: dev_err(fe->dev, "ASoC: invalid trigger cmd %d for %s\n", cmd, @@ -2586,6 +2621,12 @@ static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd) goto out; } + if (ret < 0) { + dev_err(fe->dev, "ASoC: trigger FE cmd: %d failed: %d\n", + cmd, ret); + goto out; + } + switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: @@ -3702,16 +3743,16 @@ static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe, ssize_t offset = 0; /* FE state */ - offset += snprintf(buf + offset, size - offset, + offset += scnprintf(buf + offset, size - offset, "[%s - %s]\n", fe->dai_link->name, stream ? "Capture" : "Playback"); - offset += snprintf(buf + offset, size - offset, "State: %s\n", + offset += scnprintf(buf + offset, size - offset, "State: %s\n", dpcm_state_string(fe->dpcm[stream].state)); if ((fe->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) && (fe->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP)) - offset += snprintf(buf + offset, size - offset, + offset += scnprintf(buf + offset, size - offset, "Hardware Params: " "Format = %s, Channels = %d, Rate = %d\n", snd_pcm_format_name(params_format(params)), @@ -3719,10 +3760,10 @@ static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe, params_rate(params)); /* BEs state */ - offset += snprintf(buf + offset, size - offset, "Backends:\n"); + offset += scnprintf(buf + offset, size - offset, "Backends:\n"); if (list_empty(&fe->dpcm[stream].be_clients)) { - offset += snprintf(buf + offset, size - offset, + offset += scnprintf(buf + offset, size - offset, " No active DSP links\n"); goto out; } @@ -3731,16 +3772,16 @@ static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe, struct snd_soc_pcm_runtime *be = dpcm->be; params = &dpcm->hw_params; - offset += snprintf(buf + offset, size - offset, + offset += scnprintf(buf + offset, size - offset, "- %s\n", be->dai_link->name); - offset += snprintf(buf + offset, size - offset, + offset += scnprintf(buf + offset, size - offset, " State: %s\n", dpcm_state_string(be->dpcm[stream].state)); if ((be->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) && (be->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP)) - offset += snprintf(buf + offset, size - offset, + offset += scnprintf(buf + offset, size - offset, " Hardware Params: " "Format = %s, Channels = %d, Rate = %d\n", snd_pcm_format_name(params_format(params)), diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 069f38fbf07b89f8295bd0a49232ae7b09116110..30fc45aa18690547407ec191a37b31ba0918bd02 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -2154,8 +2154,11 @@ static int soc_tplg_link_elems_load(struct soc_tplg *tplg, } ret = soc_tplg_link_config(tplg, _link); - if (ret < 0) + if (ret < 0) { + if (!abi_match) + kfree(_link); return ret; + } /* offset by version-specific struct size and * real priv data size @@ -2310,7 +2313,7 @@ static int soc_tplg_manifest_load(struct soc_tplg *tplg, { struct snd_soc_tplg_manifest *manifest, *_manifest; bool abi_match; - int err; + int ret = 0; if (tplg->pass != SOC_TPLG_PASS_MANIFEST) return 0; @@ -2323,19 +2326,19 @@ static int soc_tplg_manifest_load(struct soc_tplg *tplg, _manifest = manifest; } else { abi_match = false; - err = manifest_new_ver(tplg, manifest, &_manifest); - if (err < 0) - return err; + ret = manifest_new_ver(tplg, manifest, &_manifest); + if (ret < 0) + return ret; } /* pass control to component driver for optional further init */ if (tplg->comp && tplg->ops && tplg->ops->manifest) - return tplg->ops->manifest(tplg->comp, tplg->index, _manifest); + ret = tplg->ops->manifest(tplg->comp, tplg->index, _manifest); if (!abi_match) /* free the duplicated one */ kfree(_manifest); - return 0; + return ret; } /* validate header magic, size and type */ diff --git a/sound/soc/sti/uniperif_player.c b/sound/soc/sti/uniperif_player.c index 313dab2857ef9152435649aafb5c0ed744e4e545..4b0beb372cd9405e359c0554816fc44462c6aac0 100644 --- a/sound/soc/sti/uniperif_player.c +++ b/sound/soc/sti/uniperif_player.c @@ -226,7 +226,6 @@ static void uni_player_set_channel_status(struct uniperif *player, * sampling frequency. If no sample rate is already specified, then * set one. */ - mutex_lock(&player->ctrl_lock); if (runtime) { switch (runtime->rate) { case 22050: @@ -303,7 +302,6 @@ static void uni_player_set_channel_status(struct uniperif *player, player->stream_settings.iec958.status[3 + (n * 4)] << 24; SET_UNIPERIF_CHANNEL_STA_REGN(player, n, status); } - mutex_unlock(&player->ctrl_lock); /* Update the channel status */ if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) @@ -365,8 +363,10 @@ static int uni_player_prepare_iec958(struct uniperif *player, SET_UNIPERIF_CTRL_ZERO_STUFF_HW(player); + mutex_lock(&player->ctrl_lock); /* Update the channel status */ uni_player_set_channel_status(player, runtime); + mutex_unlock(&player->ctrl_lock); /* Clear the user validity user bits */ SET_UNIPERIF_USER_VALIDITY_VALIDITY_LR(player, 0); @@ -598,7 +598,6 @@ static int uni_player_ctl_iec958_put(struct snd_kcontrol *kcontrol, iec958->status[1] = ucontrol->value.iec958.status[1]; iec958->status[2] = ucontrol->value.iec958.status[2]; iec958->status[3] = ucontrol->value.iec958.status[3]; - mutex_unlock(&player->ctrl_lock); spin_lock_irqsave(&player->irq_lock, flags); if (player->substream && player->substream->runtime) @@ -608,6 +607,8 @@ static int uni_player_ctl_iec958_put(struct snd_kcontrol *kcontrol, uni_player_set_channel_status(player, NULL); spin_unlock_irqrestore(&player->irq_lock, flags); + mutex_unlock(&player->ctrl_lock); + return 0; } diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c index 373df4f24be1a9a5e298e772cba1d6161b5e6fff..07082f7efdfa9e9024a3f378957a8f37edb1fe65 100644 --- a/sound/soc/stm/stm32_spdifrx.c +++ b/sound/soc/stm/stm32_spdifrx.c @@ -213,6 +213,7 @@ * @slave_config: dma slave channel runtime config pointer * @phys_addr: SPDIFRX registers physical base address * @lock: synchronization enabling lock + * @irq_lock: prevent race condition with IRQ on stream state * @cs: channel status buffer * @ub: user data buffer * @irq: SPDIFRX interrupt line @@ -233,6 +234,7 @@ struct stm32_spdifrx_data { struct dma_slave_config slave_config; dma_addr_t phys_addr; spinlock_t lock; /* Sync enabling lock */ + spinlock_t irq_lock; /* Prevent race condition on stream state */ unsigned char cs[SPDIFRX_CS_BYTES_NB]; unsigned char ub[SPDIFRX_UB_BYTES_NB]; int irq; @@ -313,6 +315,7 @@ static void stm32_spdifrx_dma_ctrl_stop(struct stm32_spdifrx_data *spdifrx) static int stm32_spdifrx_start_sync(struct stm32_spdifrx_data *spdifrx) { int cr, cr_mask, imr, ret; + unsigned long flags; /* Enable IRQs */ imr = SPDIFRX_IMR_IFEIE | SPDIFRX_IMR_SYNCDIE | SPDIFRX_IMR_PERRIE; @@ -320,7 +323,7 @@ static int stm32_spdifrx_start_sync(struct stm32_spdifrx_data *spdifrx) if (ret) return ret; - spin_lock(&spdifrx->lock); + spin_lock_irqsave(&spdifrx->lock, flags); spdifrx->refcount++; @@ -353,7 +356,7 @@ static int stm32_spdifrx_start_sync(struct stm32_spdifrx_data *spdifrx) "Failed to start synchronization\n"); } - spin_unlock(&spdifrx->lock); + spin_unlock_irqrestore(&spdifrx->lock, flags); return ret; } @@ -361,11 +364,12 @@ static int stm32_spdifrx_start_sync(struct stm32_spdifrx_data *spdifrx) static void stm32_spdifrx_stop(struct stm32_spdifrx_data *spdifrx) { int cr, cr_mask, reg; + unsigned long flags; - spin_lock(&spdifrx->lock); + spin_lock_irqsave(&spdifrx->lock, flags); if (--spdifrx->refcount) { - spin_unlock(&spdifrx->lock); + spin_unlock_irqrestore(&spdifrx->lock, flags); return; } @@ -384,7 +388,7 @@ static void stm32_spdifrx_stop(struct stm32_spdifrx_data *spdifrx) regmap_read(spdifrx->regmap, STM32_SPDIFRX_DR, ®); regmap_read(spdifrx->regmap, STM32_SPDIFRX_CSR, ®); - spin_unlock(&spdifrx->lock); + spin_unlock_irqrestore(&spdifrx->lock, flags); } static int stm32_spdifrx_dma_ctrl_register(struct device *dev, @@ -643,7 +647,6 @@ static const struct regmap_config stm32_h7_spdifrx_regmap_conf = { static irqreturn_t stm32_spdifrx_isr(int irq, void *devid) { struct stm32_spdifrx_data *spdifrx = (struct stm32_spdifrx_data *)devid; - struct snd_pcm_substream *substream = spdifrx->substream; struct platform_device *pdev = spdifrx->pdev; unsigned int cr, mask, sr, imr; unsigned int flags; @@ -711,14 +714,19 @@ static irqreturn_t stm32_spdifrx_isr(int irq, void *devid) regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR, SPDIFRX_CR_SPDIFEN_MASK, cr); - if (substream) - snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); + spin_lock(&spdifrx->irq_lock); + if (spdifrx->substream) + snd_pcm_stop(spdifrx->substream, + SNDRV_PCM_STATE_DISCONNECTED); + spin_unlock(&spdifrx->irq_lock); return IRQ_HANDLED; } - if (err_xrun && substream) - snd_pcm_stop_xrun(substream); + spin_lock(&spdifrx->irq_lock); + if (err_xrun && spdifrx->substream) + snd_pcm_stop_xrun(spdifrx->substream); + spin_unlock(&spdifrx->irq_lock); return IRQ_HANDLED; } @@ -727,9 +735,12 @@ static int stm32_spdifrx_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct stm32_spdifrx_data *spdifrx = snd_soc_dai_get_drvdata(cpu_dai); + unsigned long flags; int ret; + spin_lock_irqsave(&spdifrx->irq_lock, flags); spdifrx->substream = substream; + spin_unlock_irqrestore(&spdifrx->irq_lock, flags); ret = clk_prepare_enable(spdifrx->kclk); if (ret) @@ -805,8 +816,12 @@ static void stm32_spdifrx_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct stm32_spdifrx_data *spdifrx = snd_soc_dai_get_drvdata(cpu_dai); + unsigned long flags; + spin_lock_irqsave(&spdifrx->irq_lock, flags); spdifrx->substream = NULL; + spin_unlock_irqrestore(&spdifrx->irq_lock, flags); + clk_disable_unprepare(spdifrx->kclk); } @@ -910,6 +925,7 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) spdifrx->pdev = pdev; init_completion(&spdifrx->cs_completion); spin_lock_init(&spdifrx->lock); + spin_lock_init(&spdifrx->irq_lock); platform_set_drvdata(pdev, spdifrx); diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 18cf8404d27ca351c7225b5b1e210d07c28576db..f248e563986c3c90c0fb34b98c492d323dc3d6b3 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -80,8 +80,8 @@ #define SUN4I_I2S_CLK_DIV_MCLK_MASK GENMASK(3, 0) #define SUN4I_I2S_CLK_DIV_MCLK(mclk) ((mclk) << 0) -#define SUN4I_I2S_RX_CNT_REG 0x28 -#define SUN4I_I2S_TX_CNT_REG 0x2c +#define SUN4I_I2S_TX_CNT_REG 0x28 +#define SUN4I_I2S_RX_CNT_REG 0x2c #define SUN4I_I2S_TX_CHAN_SEL_REG 0x30 #define SUN4I_I2S_CHAN_SEL(num_chan) (((num_chan) - 1) << 0) diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index bf615fa16dc8ad36facacf30658263b25240acd5..8bcdeb281770fd5143d682c2ae2cae42fe2f1c9a 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c @@ -89,6 +89,7 @@ #define SUN8I_SYS_SR_CTRL_AIF1_FS_MASK GENMASK(15, 12) #define SUN8I_SYS_SR_CTRL_AIF2_FS_MASK GENMASK(11, 8) +#define SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT_MASK GENMASK(3, 2) #define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK GENMASK(5, 4) #define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK GENMASK(8, 6) #define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK GENMASK(12, 9) @@ -250,7 +251,7 @@ static int sun8i_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return -EINVAL; } regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL, - BIT(SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT), + SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT_MASK, value << SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT); return 0; @@ -465,7 +466,11 @@ static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = { { "Right Digital DAC Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 Slot 0 Right"}, - /* ADC routes */ + /* ADC Routes */ + { "AIF1 Slot 0 Right ADC", NULL, "ADC" }, + { "AIF1 Slot 0 Left ADC", NULL, "ADC" }, + + /* ADC Mixer Routes */ { "Left Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch", "AIF1 Slot 0 Left ADC" }, { "Right Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch", diff --git a/sound/usb/clock.c b/sound/usb/clock.c index e31349865f202d0367e69ee00a5cac2d7789dcb0..bfe5540030b8066f0ff0a4cf0f0afb4f16b28e95 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -165,8 +165,34 @@ static int uac_clock_selector_set_val(struct snd_usb_audio *chip, int selector_i return ret; } +/* + * Assume the clock is valid if clock source supports only one single sample + * rate, the terminal is connected directly to it (there is no clock selector) + * and clock type is internal. This is to deal with some Denon DJ controllers + * that always reports that clock is invalid. + */ +static bool uac_clock_source_is_valid_quirk(struct snd_usb_audio *chip, + struct audioformat *fmt, + int source_id) +{ + if (fmt->protocol == UAC_VERSION_2) { + struct uac_clock_source_descriptor *cs_desc = + snd_usb_find_clock_source(chip->ctrl_intf, source_id); + + if (!cs_desc) + return false; + + return (fmt->nr_rates == 1 && + (fmt->clock & 0xff) == cs_desc->bClockID && + (cs_desc->bmAttributes & 0x3) != + UAC_CLOCK_SOURCE_TYPE_EXT); + } + + return false; +} + static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, - int protocol, + struct audioformat *fmt, int source_id) { int err; @@ -174,26 +200,26 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, struct usb_device *dev = chip->dev; u32 bmControls; - if (protocol == UAC_VERSION_3) { + if (fmt->protocol == UAC_VERSION_3) { struct uac3_clock_source_descriptor *cs_desc = snd_usb_find_clock_source_v3(chip->ctrl_intf, source_id); if (!cs_desc) - return 0; + return false; bmControls = le32_to_cpu(cs_desc->bmControls); } else { /* UAC_VERSION_1/2 */ struct uac_clock_source_descriptor *cs_desc = snd_usb_find_clock_source(chip->ctrl_intf, source_id); if (!cs_desc) - return 0; + return false; bmControls = cs_desc->bmControls; } /* If a clock source can't tell us whether it's valid, we assume it is */ if (!uac_v2v3_control_is_readable(bmControls, UAC2_CS_CONTROL_CLOCK_VALID)) - return 1; + return true; err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, @@ -205,13 +231,17 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, dev_warn(&dev->dev, "%s(): cannot get clock validity for id %d\n", __func__, source_id); - return 0; + return false; } - return !!data; + if (data) + return true; + else + return uac_clock_source_is_valid_quirk(chip, fmt, source_id); } -static int __uac_clock_find_source(struct snd_usb_audio *chip, int entity_id, +static int __uac_clock_find_source(struct snd_usb_audio *chip, + struct audioformat *fmt, int entity_id, unsigned long *visited, bool validate) { struct uac_clock_source_descriptor *source; @@ -231,7 +261,7 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip, int entity_id, source = snd_usb_find_clock_source(chip->ctrl_intf, entity_id); if (source) { entity_id = source->bClockID; - if (validate && !uac_clock_source_is_valid(chip, UAC_VERSION_2, + if (validate && !uac_clock_source_is_valid(chip, fmt, entity_id)) { usb_audio_err(chip, "clock source %d is not valid, cannot use\n", @@ -262,8 +292,9 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip, int entity_id, } cur = ret; - ret = __uac_clock_find_source(chip, selector->baCSourceID[ret - 1], - visited, validate); + ret = __uac_clock_find_source(chip, fmt, + selector->baCSourceID[ret - 1], + visited, validate); if (!validate || ret > 0 || !chip->autoclock) return ret; @@ -274,8 +305,9 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip, int entity_id, if (i == cur) continue; - ret = __uac_clock_find_source(chip, selector->baCSourceID[i - 1], - visited, true); + ret = __uac_clock_find_source(chip, fmt, + selector->baCSourceID[i - 1], + visited, true); if (ret < 0) continue; @@ -295,14 +327,16 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip, int entity_id, /* FIXME: multipliers only act as pass-thru element for now */ multiplier = snd_usb_find_clock_multiplier(chip->ctrl_intf, entity_id); if (multiplier) - return __uac_clock_find_source(chip, multiplier->bCSourceID, - visited, validate); + return __uac_clock_find_source(chip, fmt, + multiplier->bCSourceID, + visited, validate); return -EINVAL; } -static int __uac3_clock_find_source(struct snd_usb_audio *chip, int entity_id, - unsigned long *visited, bool validate) +static int __uac3_clock_find_source(struct snd_usb_audio *chip, + struct audioformat *fmt, int entity_id, + unsigned long *visited, bool validate) { struct uac3_clock_source_descriptor *source; struct uac3_clock_selector_descriptor *selector; @@ -321,7 +355,7 @@ static int __uac3_clock_find_source(struct snd_usb_audio *chip, int entity_id, source = snd_usb_find_clock_source_v3(chip->ctrl_intf, entity_id); if (source) { entity_id = source->bClockID; - if (validate && !uac_clock_source_is_valid(chip, UAC_VERSION_3, + if (validate && !uac_clock_source_is_valid(chip, fmt, entity_id)) { usb_audio_err(chip, "clock source %d is not valid, cannot use\n", @@ -352,7 +386,8 @@ static int __uac3_clock_find_source(struct snd_usb_audio *chip, int entity_id, } cur = ret; - ret = __uac3_clock_find_source(chip, selector->baCSourceID[ret - 1], + ret = __uac3_clock_find_source(chip, fmt, + selector->baCSourceID[ret - 1], visited, validate); if (!validate || ret > 0 || !chip->autoclock) return ret; @@ -364,8 +399,9 @@ static int __uac3_clock_find_source(struct snd_usb_audio *chip, int entity_id, if (i == cur) continue; - ret = __uac3_clock_find_source(chip, selector->baCSourceID[i - 1], - visited, true); + ret = __uac3_clock_find_source(chip, fmt, + selector->baCSourceID[i - 1], + visited, true); if (ret < 0) continue; @@ -386,7 +422,8 @@ static int __uac3_clock_find_source(struct snd_usb_audio *chip, int entity_id, multiplier = snd_usb_find_clock_multiplier_v3(chip->ctrl_intf, entity_id); if (multiplier) - return __uac3_clock_find_source(chip, multiplier->bCSourceID, + return __uac3_clock_find_source(chip, fmt, + multiplier->bCSourceID, visited, validate); return -EINVAL; @@ -403,18 +440,18 @@ static int __uac3_clock_find_source(struct snd_usb_audio *chip, int entity_id, * * Returns the clock source UnitID (>=0) on success, or an error. */ -int snd_usb_clock_find_source(struct snd_usb_audio *chip, int protocol, - int entity_id, bool validate) +int snd_usb_clock_find_source(struct snd_usb_audio *chip, + struct audioformat *fmt, bool validate) { DECLARE_BITMAP(visited, 256); memset(visited, 0, sizeof(visited)); - switch (protocol) { + switch (fmt->protocol) { case UAC_VERSION_2: - return __uac_clock_find_source(chip, entity_id, visited, + return __uac_clock_find_source(chip, fmt, fmt->clock, visited, validate); case UAC_VERSION_3: - return __uac3_clock_find_source(chip, entity_id, visited, + return __uac3_clock_find_source(chip, fmt, fmt->clock, visited, validate); default: return -EINVAL; @@ -515,8 +552,7 @@ static int set_sample_rate_v2v3(struct snd_usb_audio *chip, int iface, * automatic clock selection if the current clock is not * valid. */ - clock = snd_usb_clock_find_source(chip, fmt->protocol, - fmt->clock, true); + clock = snd_usb_clock_find_source(chip, fmt, true); if (clock < 0) { /* We did not find a valid clock, but that might be * because the current sample rate does not match an @@ -524,8 +560,7 @@ static int set_sample_rate_v2v3(struct snd_usb_audio *chip, int iface, * and we will do another validation after setting the * rate. */ - clock = snd_usb_clock_find_source(chip, fmt->protocol, - fmt->clock, false); + clock = snd_usb_clock_find_source(chip, fmt, false); if (clock < 0) return clock; } @@ -591,7 +626,7 @@ static int set_sample_rate_v2v3(struct snd_usb_audio *chip, int iface, validation: /* validate clock after rate change */ - if (!uac_clock_source_is_valid(chip, fmt->protocol, clock)) + if (!uac_clock_source_is_valid(chip, fmt, clock)) return -ENXIO; return 0; } diff --git a/sound/usb/clock.h b/sound/usb/clock.h index 076e31b79ee0ff69a137d8367af1041d12686e66..68df0fbe09d0073c681dd521a8164ab15e518d7d 100644 --- a/sound/usb/clock.h +++ b/sound/usb/clock.h @@ -6,7 +6,7 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface, struct usb_host_interface *alts, struct audioformat *fmt, int rate); -int snd_usb_clock_find_source(struct snd_usb_audio *chip, int protocol, - int entity_id, bool validate); +int snd_usb_clock_find_source(struct snd_usb_audio *chip, + struct audioformat *fmt, bool validate); #endif /* __USBAUDIO_CLOCK_H */ diff --git a/sound/usb/format.c b/sound/usb/format.c index fd13ac11b13674e1c06500f7ab3eef151b1226e2..9d27429ed40362e5f2c22e096ba7b8589602ba3c 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -306,8 +306,7 @@ static int parse_audio_format_rates_v2v3(struct snd_usb_audio *chip, struct usb_device *dev = chip->dev; unsigned char tmp[2], *data; int nr_triplets, data_size, ret = 0; - int clock = snd_usb_clock_find_source(chip, fp->protocol, - fp->clock, false); + int clock = snd_usb_clock_find_source(chip, fp, false); if (clock < 0) { dev_err(&dev->dev, diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 606e316586ee0ae86d87a869332462ae12fd8e54..680a0c6d9ba977341498c4b1121f89ce2bb2fac2 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -912,6 +912,15 @@ static int parse_term_proc_unit(struct mixer_build *state, return 0; } +static int parse_term_effect_unit(struct mixer_build *state, + struct usb_audio_term *term, + void *p1, int id) +{ + term->type = UAC3_EFFECT_UNIT << 16; /* virtual type */ + term->id = id; + return 0; +} + static int parse_term_uac2_clock_source(struct mixer_build *state, struct usb_audio_term *term, void *p1, int id) @@ -996,8 +1005,7 @@ static int __check_input_term(struct mixer_build *state, int id, UAC3_PROCESSING_UNIT); case PTYPE(UAC_VERSION_2, UAC2_EFFECT_UNIT): case PTYPE(UAC_VERSION_3, UAC3_EFFECT_UNIT): - return parse_term_proc_unit(state, term, p1, id, - UAC3_EFFECT_UNIT); + return parse_term_effect_unit(state, term, p1, id); case PTYPE(UAC_VERSION_1, UAC1_EXTENSION_UNIT): case PTYPE(UAC_VERSION_2, UAC2_EXTENSION_UNIT_V2): case PTYPE(UAC_VERSION_3, UAC3_EXTENSION_UNIT): @@ -3482,7 +3490,9 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, if (err < 0) goto _error; - snd_usb_mixer_apply_create_quirk(mixer); + err = snd_usb_mixer_apply_create_quirk(mixer); + if (err < 0) + goto _error; err = snd_device_new(chip->card, SNDRV_DEV_CODEC, mixer, &dev_ops); if (err < 0) diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 1e282800e983d46f7d0ec9b72312419d63018839..67f5740398f8519d8bad9f024a2543223f12c625 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -442,7 +442,7 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, add_sync_ep_from_ifnum: iface = usb_ifnum_to_if(dev, ifnum); - if (!iface || iface->num_altsetting == 0) + if (!iface || iface->num_altsetting < 2) return -EINVAL; alts = &iface->altsetting[1]; diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 65f9c4ba62ee1c7ba235a8339182bc19911e39b1..90d4f61cc2308619164b284813c7ee1d4fe57f8b 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -3349,19 +3349,14 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), .ifnum = 0, .type = QUIRK_AUDIO_STANDARD_MIXER, }, - /* Capture */ - { - .ifnum = 1, - .type = QUIRK_IGNORE_INTERFACE, - }, /* Playback */ { - .ifnum = 2, + .ifnum = 1, .type = QUIRK_AUDIO_FIXED_ENDPOINT, .data = &(const struct audioformat) { .formats = SNDRV_PCM_FMTBIT_S16_LE, .channels = 2, - .iface = 2, + .iface = 1, .altsetting = 1, .altset_idx = 1, .attributes = UAC_EP_CS_ATTR_FILL_MAX | diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index bc70212031df4cc1cd8d740489ede26f9d9b4590..5bbfd7577b33e521591385f9b87927f8993aada8 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1177,10 +1177,12 @@ bool snd_usb_get_sample_rate_quirk(struct snd_usb_audio *chip) case USB_ID(0x04D8, 0xFEEA): /* Benchmark DAC1 Pre */ case USB_ID(0x0556, 0x0014): /* Phoenix Audio TMX320VC */ case USB_ID(0x05A3, 0x9420): /* ELP HD USB Camera */ + case USB_ID(0x05a7, 0x1020): /* Bose Companion 5 */ case USB_ID(0x074D, 0x3553): /* Outlaw RR2150 (Micronas UAC3553B) */ case USB_ID(0x1395, 0x740a): /* Sennheiser DECT */ case USB_ID(0x1901, 0x0191): /* GE B850V3 CP2114 audio interface */ case USB_ID(0x21B4, 0x0081): /* AudioQuest DragonFly */ + case USB_ID(0x2912, 0x30c8): /* Audioengine D1 */ return true; } diff --git a/sound/usb/usb_audio_qmi_svc.c b/sound/usb/usb_audio_qmi_svc.c index 5b38bcae2691beef3dba07419889c1c045a14f1e..6297d720a498e85ca7ce760f8c3ccbc1a87a1771 100644 --- a/sound/usb/usb_audio_qmi_svc.c +++ b/sound/usb/usb_audio_qmi_svc.c @@ -894,10 +894,13 @@ static void uaudio_disconnect_cb(struct snd_usb_audio *chip) ret = wait_event_interruptible_timeout(dev->disconnect_wq, !atomic_read(&dev->in_use), msecs_to_jiffies(DEV_RELEASE_WAIT_TIMEOUT)); - if (!ret) + if (!ret) { uaudio_err("timeout while waiting for dev_release\n"); - else if (ret < 0) + atomic_set(&dev->in_use, 0); + } else if (ret < 0) { uaudio_err("failed with ret %d\n", ret); + atomic_set(&dev->in_use, 0); + } mutex_lock(&chip->dev_lock); } @@ -1156,22 +1159,20 @@ static void handle_uaudio_stream_req(struct qmi_handle *handle, mutex_unlock(&chip->dev_lock); response: - if (!req_msg->enable && ret != -EINVAL) { - if (ret != -ENODEV) { - if (info_idx >= 0) { - mutex_lock(&chip->dev_lock); - info = &uadev[pcm_card_num].info[info_idx]; - uaudio_dev_intf_cleanup( - uadev[pcm_card_num].udev, - info); - uaudio_dbg("release resources: intf# %d card# %d\n", - subs->interface, pcm_card_num); - mutex_unlock(&chip->dev_lock); - } + if (!req_msg->enable && (ret != -EINVAL || ret != -ENODEV)) { + mutex_lock(&chip->dev_lock); + if (info_idx >= 0) { + info = &uadev[pcm_card_num].info[info_idx]; + uaudio_dev_intf_cleanup( + uadev[pcm_card_num].udev, + info); + uaudio_dbg("release resources: intf# %d card# %d\n", + subs->interface, pcm_card_num); } if (atomic_read(&uadev[pcm_card_num].in_use)) kref_put(&uadev[pcm_card_num].kref, uaudio_dev_release); + mutex_unlock(&chip->dev_lock); } resp.usb_token = req_msg->usb_token; diff --git a/sound/usb/usx2y/usX2Yhwdep.c b/sound/usb/usx2y/usX2Yhwdep.c index c1dd9a7b48df6749d8c566fc6fbac2a7fb1d630b..36b3459703640a1ef96a5c3199519f284d8da806 100644 --- a/sound/usb/usx2y/usX2Yhwdep.c +++ b/sound/usb/usx2y/usX2Yhwdep.c @@ -131,7 +131,7 @@ static int snd_usX2Y_hwdep_dsp_status(struct snd_hwdep *hw, info->num_dsps = 2; // 0: Prepad Data, 1: FPGA Code if (us428->chip_status & USX2Y_STAT_CHIP_INIT) info->chip_ready = 1; - info->version = USX2Y_DRIVER_VERSION; + info->version = USX2Y_DRIVER_VERSION; return 0; } diff --git a/sound/usb/validate.c b/sound/usb/validate.c index 389e8657434ac40e8521e6b2858af1da7b5af9a6..5a3c4f7882b00e94fe074e97416c293c087e8a4d 100644 --- a/sound/usb/validate.c +++ b/sound/usb/validate.c @@ -110,7 +110,7 @@ static bool validate_processing_unit(const void *p, default: if (v->type == UAC1_EXTENSION_UNIT) return true; /* OK */ - switch (d->wProcessType) { + switch (le16_to_cpu(d->wProcessType)) { case UAC_PROCESS_UP_DOWNMIX: case UAC_PROCESS_DOLBY_PROLOGIC: if (d->bLength < len + 1) /* bNrModes */ @@ -125,7 +125,7 @@ static bool validate_processing_unit(const void *p, case UAC_VERSION_2: if (v->type == UAC2_EXTENSION_UNIT_V2) return true; /* OK */ - switch (d->wProcessType) { + switch (le16_to_cpu(d->wProcessType)) { case UAC2_PROCESS_UP_DOWNMIX: case UAC2_PROCESS_DOLBY_PROLOCIC: /* SiC! */ if (d->bLength < len + 1) /* bNrModes */ @@ -142,7 +142,7 @@ static bool validate_processing_unit(const void *p, len += 2; /* wClusterDescrID */ break; } - switch (d->wProcessType) { + switch (le16_to_cpu(d->wProcessType)) { case UAC3_PROCESS_UP_DOWNMIX: if (d->bLength < len + 1) /* bNrModes */ return false; diff --git a/tools/bpf/bpftool/btf_dumper.c b/tools/bpf/bpftool/btf_dumper.c index e4e6e2b3fd84742758a53c97d1c55df009fb2fbc..ff0cc3c17141173560427a45e5d65b8db86e9b1a 100644 --- a/tools/bpf/bpftool/btf_dumper.c +++ b/tools/bpf/bpftool/btf_dumper.c @@ -26,9 +26,9 @@ static void btf_dumper_ptr(const void *data, json_writer_t *jw, bool is_plain_text) { if (is_plain_text) - jsonw_printf(jw, "%p", *(unsigned long *)data); + jsonw_printf(jw, "%p", data); else - jsonw_printf(jw, "%u", *(unsigned long *)data); + jsonw_printf(jw, "%lu", *(unsigned long *)data); } static int btf_dumper_modifier(const struct btf_dumper *d, __u32 type_id, @@ -129,7 +129,7 @@ static int btf_dumper_int(const struct btf_type *t, __u8 bit_offset, switch (BTF_INT_ENCODING(*int_type)) { case 0: if (BTF_INT_BITS(*int_type) == 64) - jsonw_printf(jw, "%lu", *(__u64 *)data); + jsonw_printf(jw, "%llu", *(__u64 *)data); else if (BTF_INT_BITS(*int_type) == 32) jsonw_printf(jw, "%u", *(__u32 *)data); else if (BTF_INT_BITS(*int_type) == 16) @@ -142,7 +142,7 @@ static int btf_dumper_int(const struct btf_type *t, __u8 bit_offset, break; case BTF_INT_SIGNED: if (BTF_INT_BITS(*int_type) == 64) - jsonw_printf(jw, "%ld", *(long long *)data); + jsonw_printf(jw, "%lld", *(long long *)data); else if (BTF_INT_BITS(*int_type) == 32) jsonw_printf(jw, "%d", *(int *)data); else if (BTF_INT_BITS(*int_type) == 16) diff --git a/tools/bpf/bpftool/cgroup.c b/tools/bpf/bpftool/cgroup.c index ee7a9765c6b32f3eb9e22b79f8a112dd38983405..adbcd84818f7407d3091d91945e971f6f88d50dd 100644 --- a/tools/bpf/bpftool/cgroup.c +++ b/tools/bpf/bpftool/cgroup.c @@ -164,7 +164,7 @@ static int do_show(int argc, char **argv) cgroup_fd = open(argv[0], O_RDONLY); if (cgroup_fd < 0) { - p_err("can't open cgroup %s", argv[1]); + p_err("can't open cgroup %s", argv[0]); goto exit; } @@ -345,7 +345,7 @@ static int do_attach(int argc, char **argv) cgroup_fd = open(argv[0], O_RDONLY); if (cgroup_fd < 0) { - p_err("can't open cgroup %s", argv[1]); + p_err("can't open cgroup %s", argv[0]); goto exit; } @@ -403,7 +403,7 @@ static int do_detach(int argc, char **argv) cgroup_fd = open(argv[0], O_RDONLY); if (cgroup_fd < 0) { - p_err("can't open cgroup %s", argv[1]); + p_err("can't open cgroup %s", argv[0]); goto exit; } diff --git a/tools/bpf/bpftool/map_perf_ring.c b/tools/bpf/bpftool/map_perf_ring.c index 6d41323be291bbf7e18348375aa88006d36facda..8ec0148d7426d14c49407d4ea017e02575c6c0ba 100644 --- a/tools/bpf/bpftool/map_perf_ring.c +++ b/tools/bpf/bpftool/map_perf_ring.c @@ -205,7 +205,7 @@ int do_event_pipe(int argc, char **argv) NEXT_ARG(); cpu = strtoul(*argv, &endptr, 0); if (*endptr) { - p_err("can't parse %s as CPU ID", **argv); + p_err("can't parse %s as CPU ID", *argv); goto err_close_map; } @@ -216,7 +216,7 @@ int do_event_pipe(int argc, char **argv) NEXT_ARG(); index = strtoul(*argv, &endptr, 0); if (*endptr) { - p_err("can't parse %s as index", **argv); + p_err("can't parse %s as index", *argv); goto err_close_map; } diff --git a/tools/include/linux/string.h b/tools/include/linux/string.h index 6c3e2cc274c5546ce64709e444701bdec582390b..0ec646f127dc6b63686920b880e5ea2eda6b077c 100644 --- a/tools/include/linux/string.h +++ b/tools/include/linux/string.h @@ -14,7 +14,15 @@ int strtobool(const char *s, bool *res); * However uClibc headers also define __GLIBC__ hence the hack below */ #if defined(__GLIBC__) && !defined(__UCLIBC__) +// pragma diagnostic was introduced in gcc 4.6 +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wredundant-decls" +#endif extern size_t strlcpy(char *dest, const char *src, size_t size); +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +#pragma GCC diagnostic pop +#endif #endif char *str_error_r(int errnum, char *buf, size_t buflen); diff --git a/tools/include/uapi/linux/sched.h b/tools/include/uapi/linux/sched.h index 22627f80063e3a0bcd36cbaf978c28fe6e631740..e5489d7a83249a74edb5e275019835a715d15dbf 100644 --- a/tools/include/uapi/linux/sched.h +++ b/tools/include/uapi/linux/sched.h @@ -2,6 +2,8 @@ #ifndef _UAPI_LINUX_SCHED_H #define _UAPI_LINUX_SCHED_H +#include + /* * cloning flags: */ @@ -30,6 +32,20 @@ #define CLONE_NEWNET 0x40000000 /* New network namespace */ #define CLONE_IO 0x80000000 /* Clone io context */ +/* + * Arguments for the clone3 syscall + */ +struct clone_args { + __aligned_u64 flags; + __aligned_u64 pidfd; + __aligned_u64 child_tid; + __aligned_u64 parent_tid; + __aligned_u64 exit_signal; + __aligned_u64 stack; + __aligned_u64 stack_size; + __aligned_u64 tls; +}; + /* * Scheduling policies */ @@ -50,9 +66,21 @@ #define SCHED_FLAG_RESET_ON_FORK 0x01 #define SCHED_FLAG_RECLAIM 0x02 #define SCHED_FLAG_DL_OVERRUN 0x04 +#define SCHED_FLAG_KEEP_POLICY 0x08 +#define SCHED_FLAG_KEEP_PARAMS 0x10 +#define SCHED_FLAG_UTIL_CLAMP_MIN 0x20 +#define SCHED_FLAG_UTIL_CLAMP_MAX 0x40 + +#define SCHED_FLAG_KEEP_ALL (SCHED_FLAG_KEEP_POLICY | \ + SCHED_FLAG_KEEP_PARAMS) + +#define SCHED_FLAG_UTIL_CLAMP (SCHED_FLAG_UTIL_CLAMP_MIN | \ + SCHED_FLAG_UTIL_CLAMP_MAX) #define SCHED_FLAG_ALL (SCHED_FLAG_RESET_ON_FORK | \ SCHED_FLAG_RECLAIM | \ - SCHED_FLAG_DL_OVERRUN) + SCHED_FLAG_DL_OVERRUN | \ + SCHED_FLAG_KEEP_ALL | \ + SCHED_FLAG_UTIL_CLAMP) #endif /* _UAPI_LINUX_SCHED_H */ diff --git a/tools/kvm/kvm_stat/kvm_stat b/tools/kvm/kvm_stat/kvm_stat index ba7ee74ee53305f0a37d805dbc8671c65de68a63..f6ca0a216f3c018654b03ae0fe1da1af023a09ff 100755 --- a/tools/kvm/kvm_stat/kvm_stat +++ b/tools/kvm/kvm_stat/kvm_stat @@ -271,6 +271,7 @@ class ArchX86(Arch): def __init__(self, exit_reasons): self.sc_perf_evt_open = 298 self.ioctl_numbers = IOCTL_NUMBERS + self.exit_reason_field = 'exit_reason' self.exit_reasons = exit_reasons def debugfs_is_child(self, field): @@ -290,6 +291,7 @@ class ArchPPC(Arch): # numbers depend on the wordsize. char_ptr_size = ctypes.sizeof(ctypes.c_char_p) self.ioctl_numbers['SET_FILTER'] = 0x80002406 | char_ptr_size << 16 + self.exit_reason_field = 'exit_nr' self.exit_reasons = {} def debugfs_is_child(self, field): @@ -301,6 +303,7 @@ class ArchA64(Arch): def __init__(self): self.sc_perf_evt_open = 241 self.ioctl_numbers = IOCTL_NUMBERS + self.exit_reason_field = 'esr_ec' self.exit_reasons = AARCH64_EXIT_REASONS def debugfs_is_child(self, field): @@ -312,6 +315,7 @@ class ArchS390(Arch): def __init__(self): self.sc_perf_evt_open = 331 self.ioctl_numbers = IOCTL_NUMBERS + self.exit_reason_field = None self.exit_reasons = None def debugfs_is_child(self, field): @@ -542,8 +546,8 @@ class TracepointProvider(Provider): """ filters = {} filters['kvm_userspace_exit'] = ('reason', USERSPACE_EXIT_REASONS) - if ARCH.exit_reasons: - filters['kvm_exit'] = ('exit_reason', ARCH.exit_reasons) + if ARCH.exit_reason_field and ARCH.exit_reasons: + filters['kvm_exit'] = (ARCH.exit_reason_field, ARCH.exit_reasons) return filters def _get_available_fields(self): diff --git a/tools/lib/api/fs/fs.c b/tools/lib/api/fs/fs.c index 7aba8243a0e7cbd3fa4726910db27bcaa3be6d1a..bd021a0eeef8c97f47161eb2bfaa3e6428ccd3ad 100644 --- a/tools/lib/api/fs/fs.c +++ b/tools/lib/api/fs/fs.c @@ -210,6 +210,7 @@ static bool fs__env_override(struct fs *fs) size_t name_len = strlen(fs->name); /* name + "_PATH" + '\0' */ char upper_name[name_len + 5 + 1]; + memcpy(upper_name, fs->name, name_len); mem_toupper(upper_name, name_len); strcpy(&upper_name[name_len], "_PATH"); @@ -219,7 +220,8 @@ static bool fs__env_override(struct fs *fs) return false; fs->found = true; - strncpy(fs->path, override_path, sizeof(fs->path)); + strncpy(fs->path, override_path, sizeof(fs->path) - 1); + fs->path[sizeof(fs->path) - 1] = '\0'; return true; } diff --git a/tools/lib/string.c b/tools/lib/string.c index 93b3d4b6feac378d81c2f189310399518efc611f..ee0afcbdd696279bd228b77c4dcb56a3a5c2f9f1 100644 --- a/tools/lib/string.c +++ b/tools/lib/string.c @@ -95,6 +95,10 @@ int strtobool(const char *s, bool *res) * If libc has strlcpy() then that version will override this * implementation: */ +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wignored-attributes" +#endif size_t __weak strlcpy(char *dest, const char *src, size_t size) { size_t ret = strlen(src); @@ -106,3 +110,6 @@ size_t __weak strlcpy(char *dest, const char *src, size_t size) } return ret; } +#ifdef __clang__ +#pragma clang diagnostic pop +#endif diff --git a/tools/lib/traceevent/parse-filter.c b/tools/lib/traceevent/parse-filter.c index 2700f1f17876ee668f5134bde4fbc9ae83927bd1..27248a0aad84a6d51952d0bdaee8cfcab78f5126 100644 --- a/tools/lib/traceevent/parse-filter.c +++ b/tools/lib/traceevent/parse-filter.c @@ -1227,8 +1227,10 @@ filter_event(struct event_filter *filter, struct event_format *event, } filter_type = add_filter_type(filter, event->id); - if (filter_type == NULL) + if (filter_type == NULL) { + free_arg(arg); return TEP_ERRNO__MEM_ALLOC_FAILED; + } if (filter_type->filter) free_arg(filter_type->filter); diff --git a/tools/objtool/arch/x86/lib/x86-opcode-map.txt b/tools/objtool/arch/x86/lib/x86-opcode-map.txt index 0a0e9112f2842e5b40e4e2284b2c061c4d98370a..5cb9f009f2be3f32c2bd8316d0e5f9805f6b4ea5 100644 --- a/tools/objtool/arch/x86/lib/x86-opcode-map.txt +++ b/tools/objtool/arch/x86/lib/x86-opcode-map.txt @@ -909,7 +909,7 @@ EndTable GrpTable: Grp3_2 0: TEST Ev,Iz -1: +1: TEST Ev,Iz 2: NOT Ev 3: NEG Ev 4: MUL rAX,Ev diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index 1452e5153c604addec427302bdcff0a8d2924459..2bd39fdc8ab0356e2aff2b4ac821eb0d3a9b6ae4 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -586,8 +586,8 @@ tot_hitm_cmp(struct perf_hpp_fmt *fmt __maybe_unused, { struct c2c_hist_entry *c2c_left; struct c2c_hist_entry *c2c_right; - unsigned int tot_hitm_left; - unsigned int tot_hitm_right; + uint64_t tot_hitm_left; + uint64_t tot_hitm_right; c2c_left = container_of(left, struct c2c_hist_entry, he); c2c_right = container_of(right, struct c2c_hist_entry, he); @@ -620,7 +620,8 @@ __f ## _cmp(struct perf_hpp_fmt *fmt __maybe_unused, \ \ c2c_left = container_of(left, struct c2c_hist_entry, he); \ c2c_right = container_of(right, struct c2c_hist_entry, he); \ - return c2c_left->stats.__f - c2c_right->stats.__f; \ + return (uint64_t) c2c_left->stats.__f - \ + (uint64_t) c2c_right->stats.__f; \ } #define STAT_FN(__f) \ @@ -673,7 +674,8 @@ ld_llcmiss_cmp(struct perf_hpp_fmt *fmt __maybe_unused, c2c_left = container_of(left, struct c2c_hist_entry, he); c2c_right = container_of(right, struct c2c_hist_entry, he); - return llc_miss(&c2c_left->stats) - llc_miss(&c2c_right->stats); + return (uint64_t) llc_miss(&c2c_left->stats) - + (uint64_t) llc_miss(&c2c_right->stats); } static uint64_t total_records(struct c2c_stats *stats) diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 2f94f7ad65bdcfa3095ad01af42188a8b70d797a..5312c761a5dba3d51dc4f86f5ec3211987c6c958 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -383,10 +383,10 @@ static int report__setup_sample_type(struct report *rep) PERF_SAMPLE_BRANCH_ANY)) rep->nonany_branch_mode = true; -#ifndef HAVE_LIBUNWIND_SUPPORT +#if !defined(HAVE_LIBUNWIND_SUPPORT) && !defined(HAVE_DWARF_SUPPORT) if (dwarf_callchain_users) { - ui__warning("Please install libunwind development packages " - "during the perf build.\n"); + ui__warning("Please install libunwind or libdw " + "development packages during the perf build.\n"); } #endif @@ -961,6 +961,7 @@ int cmd_report(int argc, const char **argv) struct stat st; bool has_br_stack = false; int branch_mode = -1; + int last_key = 0; bool branch_call_mode = false; #define CALLCHAIN_DEFAULT_OPT "graph,0.5,caller,function,percent" const char report_callchain_help[] = "Display call graph (stack chain/backtrace):\n\n" @@ -1292,7 +1293,8 @@ int cmd_report(int argc, const char **argv) else use_browser = 0; - if (setup_sorting(session->evlist) < 0) { + if ((last_key != K_SWITCH_INPUT_DATA) && + (setup_sorting(session->evlist) < 0)) { if (sort_order) parse_options_usage(report_usage, options, "s", 1); if (field_order) @@ -1390,6 +1392,7 @@ int cmd_report(int argc, const char **argv) ret = __cmd_report(&report); if (ret == K_SWITCH_INPUT_DATA) { perf_session__delete(session); + last_key = K_SWITCH_INPUT_DATA; goto repeat; } else ret = 0; diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 692d2fa31c351b0ea681ffc1b285b05f591bf31c..ed34902022c1f33a231daf161db0f8e8113e01b2 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -2931,6 +2931,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, continue; } + actions->ms.map = map; top = pstack__peek(browser->pstack); if (top == &browser->hists->dso_filter) { /* diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 3badd7f1e1b81aa24d204d755ef8dfb883076a66..7173e1f410930eacbeebe186faaa1760adf7e6e8 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -324,10 +324,10 @@ static inline void perf_hpp__prepend_sort_field(struct perf_hpp_fmt *format) list_for_each_entry_safe(format, tmp, &(_list)->sorts, sort_list) #define hists__for_each_format(hists, format) \ - perf_hpp_list__for_each_format((hists)->hpp_list, fmt) + perf_hpp_list__for_each_format((hists)->hpp_list, format) #define hists__for_each_sort_list(hists, format) \ - perf_hpp_list__for_each_sort_list((hists)->hpp_list, fmt) + perf_hpp_list__for_each_sort_list((hists)->hpp_list, format) extern struct perf_hpp_fmt perf_hpp__format[]; diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 21f867a543e0286729195704baa7543d3c6c94da..ab208400ea1407e78454191f2705ed737c098f2b 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -681,24 +681,6 @@ int machine__process_switch_event(struct machine *machine __maybe_unused, return 0; } -static void dso__adjust_kmod_long_name(struct dso *dso, const char *filename) -{ - const char *dup_filename; - - if (!filename || !dso || !dso->long_name) - return; - if (dso->long_name[0] != '[') - return; - if (!strchr(filename, '/')) - return; - - dup_filename = strdup(filename); - if (!dup_filename) - return; - - dso__set_long_name(dso, dup_filename, true); -} - struct map *machine__findnew_module_map(struct machine *machine, u64 start, const char *filename) { @@ -710,15 +692,8 @@ struct map *machine__findnew_module_map(struct machine *machine, u64 start, return NULL; map = map_groups__find_by_name(&machine->kmaps, m.name); - if (map) { - /* - * If the map's dso is an offline module, give dso__load() - * a chance to find the file path of that module by fixing - * long_name. - */ - dso__adjust_kmod_long_name(map->dso, filename); + if (map) goto out; - } dso = machine__findnew_module_dso(machine, &m, filename); if (dso == NULL) diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 7ccabb891e5a351c0dee5b8b7fa5c0b908278c23..a6aaac24ce0554789769142fce3e616788c8c564 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -612,38 +612,26 @@ static int convert_to_trace_point(Dwarf_Die *sp_die, Dwfl_Module *mod, const char *function, struct probe_trace_point *tp) { - Dwarf_Addr eaddr, highaddr; + Dwarf_Addr eaddr; GElf_Sym sym; const char *symbol; /* Verify the address is correct */ - if (dwarf_entrypc(sp_die, &eaddr) != 0) { - pr_warning("Failed to get entry address of %s\n", - dwarf_diename(sp_die)); - return -ENOENT; - } - if (dwarf_highpc(sp_die, &highaddr) != 0) { - pr_warning("Failed to get end address of %s\n", - dwarf_diename(sp_die)); - return -ENOENT; - } - if (paddr > highaddr) { - pr_warning("Offset specified is greater than size of %s\n", + if (!dwarf_haspc(sp_die, paddr)) { + pr_warning("Specified offset is out of %s\n", dwarf_diename(sp_die)); return -EINVAL; } - symbol = dwarf_diename(sp_die); + /* Try to get actual symbol name from symtab */ + symbol = dwfl_module_addrsym(mod, paddr, &sym, NULL); if (!symbol) { - /* Try to get the symbol name from symtab */ - symbol = dwfl_module_addrsym(mod, paddr, &sym, NULL); - if (!symbol) { - pr_warning("Failed to find symbol at 0x%lx\n", - (unsigned long)paddr); - return -ENOENT; - } - eaddr = sym.st_value; + pr_warning("Failed to find symbol at 0x%lx\n", + (unsigned long)paddr); + return -ENOENT; } + eaddr = sym.st_value; + tp->offset = (unsigned long)(paddr - eaddr); tp->address = (unsigned long)paddr; tp->symbol = strdup(symbol); diff --git a/tools/perf/util/stat-shadow.c b/tools/perf/util/stat-shadow.c index bbb0e042d8e5802a8de01f50e1c2eebbcf02bfe2..59475287e2e12670eb249464320f4655324e91f9 100644 --- a/tools/perf/util/stat-shadow.c +++ b/tools/perf/util/stat-shadow.c @@ -209,12 +209,12 @@ void perf_stat__update_shadow_stats(struct perf_evsel *counter, u64 count, int cpu, struct runtime_stat *st) { int ctx = evsel_context(counter); + u64 count_ns = count; count *= counter->scale; - if (perf_evsel__match(counter, SOFTWARE, SW_TASK_CLOCK) || - perf_evsel__match(counter, SOFTWARE, SW_CPU_CLOCK)) - update_runtime_stat(st, STAT_NSECS, 0, cpu, count); + if (perf_evsel__is_clock(counter)) + update_runtime_stat(st, STAT_NSECS, 0, cpu, count_ns); else if (perf_evsel__match(counter, HARDWARE, HW_CPU_CYCLES)) update_runtime_stat(st, STAT_CYCLES, ctx, cpu, count); else if (perf_stat_evsel__is(counter, CYCLES_IN_TX)) diff --git a/tools/power/acpi/Makefile.config b/tools/power/acpi/Makefile.config index f304be71c278ca0c7bc1850f3daf37b41ca19f43..fc116c060b98dccf829268efcff885d96c43450f 100644 --- a/tools/power/acpi/Makefile.config +++ b/tools/power/acpi/Makefile.config @@ -18,7 +18,7 @@ include $(srctree)/../../scripts/Makefile.include OUTPUT=$(srctree)/ ifeq ("$(origin O)", "command line") - OUTPUT := $(O)/power/acpi/ + OUTPUT := $(O)/tools/power/acpi/ endif #$(info Determined 'OUTPUT' to be $(OUTPUT)) diff --git a/tools/testing/selftests/bpf/test_select_reuseport.c b/tools/testing/selftests/bpf/test_select_reuseport.c index 75646d9b34aaa7db511471b6fa51843a8f92cb81..cdbbdab2725fcf1cd0a2953ccf754fe29af830ee 100644 --- a/tools/testing/selftests/bpf/test_select_reuseport.c +++ b/tools/testing/selftests/bpf/test_select_reuseport.c @@ -30,7 +30,7 @@ #define REUSEPORT_ARRAY_SIZE 32 static int result_map, tmp_index_ovr_map, linum_map, data_check_map; -static enum result expected_results[NR_RESULTS]; +static __u32 expected_results[NR_RESULTS]; static int sk_fds[REUSEPORT_ARRAY_SIZE]; static int reuseport_array, outer_map; static int select_by_skb_data_prog; @@ -610,7 +610,19 @@ static void setup_per_test(int type, unsigned short family, bool inany) static void cleanup_per_test(void) { - int i, err; + int i, err, zero = 0; + + memset(expected_results, 0, sizeof(expected_results)); + + for (i = 0; i < NR_RESULTS; i++) { + err = bpf_map_update_elem(result_map, &i, &zero, BPF_ANY); + RET_IF(err, "reset elem in result_map", + "i:%u err:%d errno:%d\n", i, err, errno); + } + + err = bpf_map_update_elem(linum_map, &zero, &zero, BPF_ANY); + RET_IF(err, "reset line number in linum_map", "err:%d errno:%d\n", + err, errno); for (i = 0; i < REUSEPORT_ARRAY_SIZE; i++) close(sk_fds[i]); diff --git a/tools/testing/selftests/filesystems/incfs/.gitignore b/tools/testing/selftests/filesystems/incfs/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..4cba9c219a92b6a5eec6ecc56e4f82e35d1c1542 --- /dev/null +++ b/tools/testing/selftests/filesystems/incfs/.gitignore @@ -0,0 +1 @@ +incfs_test \ No newline at end of file diff --git a/tools/testing/selftests/filesystems/incfs/Makefile b/tools/testing/selftests/filesystems/incfs/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..1f13573d36179148e00b20a1e7aa1f8ec3d0263d --- /dev/null +++ b/tools/testing/selftests/filesystems/incfs/Makefile @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0 +CFLAGS += -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -Wall -lssl -lcrypto -llz4 +CFLAGS += -I../../../../../usr/include/ +CFLAGS += -I../../../../include/uapi/ +CFLAGS += -I../../../../lib + +EXTRA_SOURCES := utils.c +CFLAGS += $(EXTRA_SOURCES) + +TEST_GEN_PROGS := incfs_test + +include ../../lib.mk + +$(OUTPUT)incfs_test: incfs_test.c $(EXTRA_SOURCES) +all: $(OUTPUT)incfs_test + +clean: + rm -rf $(OUTPUT)incfs_test *.o diff --git a/tools/testing/selftests/filesystems/incfs/config b/tools/testing/selftests/filesystems/incfs/config new file mode 100644 index 0000000000000000000000000000000000000000..b6749837a31878434de0f4a44e09ae34c55563f6 --- /dev/null +++ b/tools/testing/selftests/filesystems/incfs/config @@ -0,0 +1 @@ +CONFIG_INCREMENTAL_FS=y \ No newline at end of file diff --git a/tools/testing/selftests/filesystems/incfs/incfs_test.c b/tools/testing/selftests/filesystems/incfs/incfs_test.c new file mode 100644 index 0000000000000000000000000000000000000000..dd70e019dc4ce7182ec0f267bf3ef95fd21d50e3 --- /dev/null +++ b/tools/testing/selftests/filesystems/incfs/incfs_test.c @@ -0,0 +1,2422 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2018 Google LLC + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../kselftest.h" + +#include "lz4.h" +#include "utils.h" + +#define __packed __attribute__((__packed__)) + +#define TEST_FAILURE 1 +#define TEST_SUCCESS 0 +#define INCFS_MAX_MTREE_LEVELS 8 + +#define INCFS_ROOT_INODE 0 + +struct hash_block { + char data[INCFS_DATA_FILE_BLOCK_SIZE]; +}; + +struct test_signature { + void *data; + size_t size; + + char add_data[100]; + size_t add_data_size; +}; + +struct test_file { + int index; + incfs_uuid_t id; + char *name; + off_t size; + char root_hash[INCFS_MAX_HASH_SIZE]; + struct hash_block *mtree; + int mtree_block_count; + struct test_signature sig; +}; + +struct test_files_set { + struct test_file *files; + int files_count; +}; + +struct linux_dirent64 { + uint64_t d_ino; + int64_t d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[0]; +} __packed; + +/* + * The certificate below and the private key were created by calling: + * openssl req -x509 -newkey rsa:4096 -keyout private.key -out cert.crt + * -days 1000 -sha256 -nodes -outform PEM -subj + * "/C=US/ST=WA/L=Kirkland/O=Example/OU=Org/CN=www.example.com" + */ +char x509_cert[] = +"-----BEGIN CERTIFICATE-----\n" +"MIIFvzCCA6egAwIBAgIUXpwqelEljm6BBllRQGHLrls2MYgwDQYJKoZIhvcNAQEL\n" +"BQAwbzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xETAPBgNVBAcM\n" +"CEtpcmtsYW5kMRAwDgYDVQQKDAdFeGFtcGxlMQwwCgYDVQQLDANPcmcxGDAWBgNV\n" +"BAMMD3d3dy5leGFtcGxlLmNvbTAeFw0xOTA4MDgyMzA3MDZaFw0yMjA1MDQyMzA3\n" +"MDZaMG8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMREwDwYDVQQH\n" +"DAhLaXJrbGFuZDEQMA4GA1UECgwHRXhhbXBsZTEMMAoGA1UECwwDT3JnMRgwFgYD\n" +"VQQDDA93d3cuZXhhbXBsZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK\n" +"AoICAQC1LuFW/lDV/GflqFMz7RDvFFgWld982ZuDJRaK55JNj+MI4RZNL61PDw43\n" +"NeeJtqUoVxSLS9wHURjSjD/CV5GudUOnzGfbwFlLko+jhYRT4HNFS+5ys1FEJLtA\n" +"uYcY4P9GHQEXYUX+ue82A2kJ91oY6G3vCQYJFiGteb6TRDICmug31x4pBfB8rOdt\n" +"4/NXS/Dn+S0/mJlxw34IKfqrlFjzUziRZtAWWqDcfxFDUizSggkdXIUq4GY38RAD\n" +"qGewNNCab3ClJDP7/M32BhSNgsIKhgtSTM2+ocfvBhwup+BjV6UbL21DPAshlolV\n" +"gSL1HM2jin5bi4bpFMreY0LXwFih87/6AVSfQHY9TZrombVZnMxvB7NG1NCSwDBT\n" +"qjjFb3oiSMugJzY+MhISM754m46fwUyHZ1ylWCLJEU8kQ5A1q9vvqMcaDa4uTGP3\n" +"UgC6SyVmZxG2o+AO6m8TRTCtqHN41mPTM9HK4T1UyuzVpykSc2LlYkKE517SyEiV\n" +"XDmotNb2myXNYHHTjRYNxkq75Lbii2I4Q4z8XtDngaIrhZqACKSqIt2CocGjx61S\n" +"oxKWi+LGa7B4NaCMjz1LnaOIsXn1rJDRnUWL49T42g4kOi/5QaC2JDygfefw1hAb\n" +"uxkq9EYUDg+w9broltiBf4rKAnw8JMySARnyPZbj0lhZK3va5wIDAQABo1MwUTAd\n" +"BgNVHQ4EFgQUo6JN3gY2yGbzOTNj8Al7hNB3rw0wHwYDVR0jBBgwFoAUo6JN3gY2\n" +"yGbzOTNj8Al7hNB3rw0wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC\n" +"AgEAQb3pJqOzM4whfNVdpEOswd1EApcWNM1ps9iTlEEjDoRv9F7F1PW0uXCIpk3B\n" +"j5JgCmIxAcPnzj42rduRSx421hHMZhbAIWI/JL4ZSF64qlG0YrmJDXlJgSMoyst5\n" +"biUqeWgO7Js5udPt3zhkeA62z3hGM6dE5B3k7gHTaKKtK17+UeR9imZKsOK8GBnM\n" +"rxMPI6XghxxAK2OQ/r09DHDiyf/GxgOE46oknfXfMPx3HaSvDKrZUTZ+UvVbM5c2\n" +"5eXOgH5UO/e4llLknJK7CoP/R6G7pV44iT4t4t9FMnvCYvavAHwfR+6z5vTF3o8a\n" +"wd80fC8z1vfLsIPLROdzBl9rGCvv536fPiEA677CM1AZkjfT0a9DVzrE1NDvuCUF\n" +"0KgEdiNwux+hO6dbTyiS38yPT6TbpoWJptJmFhFkC4hGvUgoX/TI0covSyf74VRH\n" +"k3BHojOBMYiX1K66xoN7fhlGK8cith3L0XXPB8CgSEUPWURvm8RCaGuX2T3FZomF\n" +"BCnNpN+WNnN3Yf4OkjtuvtxxktUU7pfVLsUxrdpo/ph4rWm6U83VT/Zlq92aF4vW\n" +"QJ+7uraQFip7e+Gy9g3UJINm3B7b1C4ch/Z/upCZESOI/23sVGzkfTgOrS+23i6/\n" +"Vi9YW75zySC2FCa1AWMS1NmS5qfDSycJUgD6YvOUg0C54ZI=\n" +"-----END CERTIFICATE-----"; + +char private_key[] = +"-----BEGIN PRIVATE KEY-----\n" +"MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQC1LuFW/lDV/Gfl\n" +"qFMz7RDvFFgWld982ZuDJRaK55JNj+MI4RZNL61PDw43NeeJtqUoVxSLS9wHURjS\n" +"jD/CV5GudUOnzGfbwFlLko+jhYRT4HNFS+5ys1FEJLtAuYcY4P9GHQEXYUX+ue82\n" +"A2kJ91oY6G3vCQYJFiGteb6TRDICmug31x4pBfB8rOdt4/NXS/Dn+S0/mJlxw34I\n" +"KfqrlFjzUziRZtAWWqDcfxFDUizSggkdXIUq4GY38RADqGewNNCab3ClJDP7/M32\n" +"BhSNgsIKhgtSTM2+ocfvBhwup+BjV6UbL21DPAshlolVgSL1HM2jin5bi4bpFMre\n" +"Y0LXwFih87/6AVSfQHY9TZrombVZnMxvB7NG1NCSwDBTqjjFb3oiSMugJzY+MhIS\n" +"M754m46fwUyHZ1ylWCLJEU8kQ5A1q9vvqMcaDa4uTGP3UgC6SyVmZxG2o+AO6m8T\n" +"RTCtqHN41mPTM9HK4T1UyuzVpykSc2LlYkKE517SyEiVXDmotNb2myXNYHHTjRYN\n" +"xkq75Lbii2I4Q4z8XtDngaIrhZqACKSqIt2CocGjx61SoxKWi+LGa7B4NaCMjz1L\n" +"naOIsXn1rJDRnUWL49T42g4kOi/5QaC2JDygfefw1hAbuxkq9EYUDg+w9broltiB\n" +"f4rKAnw8JMySARnyPZbj0lhZK3va5wIDAQABAoICAQCMKul/0J2e/ncub6t2t4dr\n" +"PnTrfCT6xKqPqciny4Ee6hr9So1jR2gvink380bd/mQFMmEdZqGhM3cdpAzLf82f\n" +"hu7BSNxsYIF0er0PB4MZFMJ4sMaXC+zp5/TJnP5MG/zBND0c5k8tQpEyWy8O28Jj\n" +"FKW/0F5P90Q0ncP20EJUS50tXgniOMsU2Prtw/UE6yZDgD0mPxsurMu66ycXSFwM\n" +"WqyfqEeBk7lw/AjR6Sft71W31lTbl+DclG0MN2OIKUPcxiwCRmDFKI36MDgERk1x\n" +"sMPfdrWRLj2ryDFTUuLAWBTOVEGWS0RdRsWWVaJCuHbKd6FLl0TW2xQbOfWDTjYC\n" +"Ps31ejh163qdbk7OGOZIbd83fP3jsyL+4eNzhUpeXMKhfG58mFIv4yhdZIUOpuL6\n" +"aqnoU9z9wEsJKj/SrKr3nw6tuTnmbXgNjun9LfTFmqqDRBYd0Okiprw6jHNM1jgA\n" +"GG0kC/K7r89jKymVDABwGMFCS33ynR1Tb6zG+cqgNMPw19Fy3uQuW21CjqSzCOyP\n" +"aEVCEUZeP+ofql5+7ZKi6Dj+EdTfeKt2ihgheHZZoaYSINb8tsnKbdJhwBfW9PFT\n" +"aT/hu3bnO2FPC8H2NGOqxOEeel9ALU4SFu1pOknEhiL3/mNfOQ+KgrSRDtNRlcL0\n" +"cto05J90u0cmqwWKlshfaQKCAQEA5dcklxs4ezyzt28NcsiyS02oZ+9TkQp6pCXV\n" +"kx7AwhivAmVTlJ+c6BegA5EPd7A1gknM3+EKzGpoBOqmlF45G57phVIAphAp4oCH\n" +"UOVtIQgM8p4EU2gtX+uNOopdYlpBQnWimXaHA2sOD9/yTbZ03j/McRH6D15+iCld\n" +"3880GHdZaYYbQmHoSDg39LRRO1bdS3WC0oKBD2gPi3K0b9RaZSwKzuVrmlvrLURj\n" +"WMZfmkGl4BsITfuoTxbWFVncG3Kb9eYkYUFZy4M2G/s849PS/HjrN7BvgpanjtVp\n" +"1/39APQfAYfUuBPbKYnb6F8dE0pb5cVd4uMZklAeTb3bXjOO9QKCAQEAyc4CxWXr\n" +"bG6Do5dGpWudQ7ucq00MR0T3MHQIu5XTn6BsPHAJ9ZgrQw9C24PXm2VEjjsrMs5T\n" +"rHNF9oeO39s25Za1iyJ+893icqA3h3ivCUOOoVE54BkuJK6REhkXPD5G1ubmxeBz\n" +"MKNehlpd/eSbJJArkzKFZ8sBtLt8i9VFhRnXSpDAbiMpCbjW+bem9MWdLmkenSnu\n" +"OUbnqYcJhFBCvOT7ZCHFCDNUNPfHcaReSY2EYjw0ZqtqAZD0Q+DL+RkLz7l1+/bF\n" +"eEwNjmjFTcwRyawqf38D4miU0H6ca16FkeSlbmM5p3HdwZK2HVYYz3FSwhox6Ebd\n" +"n6in42qfL4Ug6wKCAQAh9IDRWhIkErmyNdPUy1WbzmM8x5ye5t9rdLNywq5TfnYM\n" +"co/AezwhBax8GmgglIWzM9fykzqXLHklkMz/SlRBgl6ZdZ3m6qhlb/uNtfdDU/8l\n" +"sLaO4+sgKpp4tYxKRW8ytFJLPbmAhcZUDg+r73KgiuhXJAK/VoR29TWLJP9bRfaN\n" +"omRQkEpSsQuDOUhu7cxPo5KqKuGKNyNkxJNnmgWowLLwEfCtozrBO0M6EER7c4tf\n" +"6l51tuIMnSEPknD0FSB5WYCyZYcwi7fotlsuhVK8PdjyJzyyHDOw5FJ4uGsyQt55\n" +"yWlhsH1GS7mTQMn42Zlt/pR6OnbCqNdxQMUxy4gpAoIBAFvMbs5E0pb8nr0n72cI\n" +"UP2itl3mKpOw95D+94n9WcrfOt0zShSCKAvVQWCB1O5HXqwklj4CRWXI+iZu+7sx\n" +"CQPfTq3//ygH4x6paxkg+N6J8LPJMz6Rtb/R+QP2je9FlQvk9U1GEKArcLBFI0R/\n" +"XWOAgZHwBWd1nU0NjFY/qeQmIR02Q5LWQ7C8eG4X8MafriSShO6RSGCdtHwVhWq+\n" +"59ztfL3L7skQMFn37K3xS0LCMVpOcLfTeeFEgxjthVvG3OydPOJlGubiEbiaSEZf\n" +"cif/PUXKDYZMdIVzUsw0ryXykJ5qXKuizHFlv5oQtDCJKFBLgjBbLC2YluaIdekz\n" +"8gkCggEBAJWxS7EuB/qL7fOz0o3HRy0plR3qbwZ0pLoCz0Ii7WxraBS1yQwmxif1\n" +"Rgv89GyFqg1yQl3CSrMiw7oC9WxxxuiEZDO18c4KO3NTv9K4itN9OPQVBTHmEhod\n" +"KWcyP4/W/Sfuae77PyclSqUsAARRrKYn2fpLTS5ibaU0QZgHmdPgYDUrPr+6PHKK\n" +"ZfQKU2uBfuo6zoMbMmFi3UYG49j9rv4d6v+44vS1MPHV9JK/LD8YfBhgx8Pg/u6D\n" +"nUgipS48pkGjJr2u2Vu7Mx70vqz0Yf2neyyDbdLtkYauC4w7YKPTD0yzDJyGuAeB\n" +"GyPbW1yZa5vE302a1Cr0Cd7RC4AFAAw=\n" +"-----END PRIVATE KEY-----"; + +struct test_files_set get_test_files_set(void) +{ + static struct test_file files[] = { + { .index = 0, .name = "file_one_byte", .size = 1 }, + { .index = 1, + .name = "file_one_block", + .size = INCFS_DATA_FILE_BLOCK_SIZE }, + { .index = 2, + .name = "file_one_and_a_half_blocks", + .size = INCFS_DATA_FILE_BLOCK_SIZE + + INCFS_DATA_FILE_BLOCK_SIZE / 2 }, + { .index = 3, + .name = "file_three", + .size = 300 * INCFS_DATA_FILE_BLOCK_SIZE + 3 }, + { .index = 4, + .name = "file_four", + .size = 400 * INCFS_DATA_FILE_BLOCK_SIZE + 7 }, + { .index = 5, + .name = "file_five", + .size = 500 * INCFS_DATA_FILE_BLOCK_SIZE + 7 }, + { .index = 6, + .name = "file_six", + .size = 600 * INCFS_DATA_FILE_BLOCK_SIZE + 7 }, + { .index = 7, + .name = "file_seven", + .size = 700 * INCFS_DATA_FILE_BLOCK_SIZE + 7 }, + { .index = 8, + .name = "file_eight", + .size = 800 * INCFS_DATA_FILE_BLOCK_SIZE + 7 }, + { .index = 9, + .name = "file_nine", + .size = 900 * INCFS_DATA_FILE_BLOCK_SIZE + 7 }, + { .index = 10, .name = "file_big", .size = 500 * 1024 * 1024 } + }; + return (struct test_files_set){ .files = files, + .files_count = ARRAY_SIZE(files) }; +} + +struct test_files_set get_small_test_files_set(void) +{ + static struct test_file files[] = { + { .index = 0, .name = "file_one_byte", .size = 1 }, + { .index = 1, + .name = "file_one_block", + .size = INCFS_DATA_FILE_BLOCK_SIZE }, + { .index = 2, + .name = "file_one_and_a_half_blocks", + .size = INCFS_DATA_FILE_BLOCK_SIZE + + INCFS_DATA_FILE_BLOCK_SIZE / 2 }, + { .index = 3, + .name = "file_three", + .size = 300 * INCFS_DATA_FILE_BLOCK_SIZE + 3 }, + { .index = 4, + .name = "file_four", + .size = 400 * INCFS_DATA_FILE_BLOCK_SIZE + 7 } + }; + return (struct test_files_set){ .files = files, + .files_count = ARRAY_SIZE(files) }; +} + +static int get_file_block_seed(int file, int block) +{ + return 7919 * file + block; +} + +static loff_t min(loff_t a, loff_t b) +{ + return a < b ? a : b; +} + +static pid_t flush_and_fork(void) +{ + fflush(stdout); + return fork(); +} + +static void print_error(char *msg) +{ + ksft_print_msg("%s: %s\n", msg, strerror(errno)); +} + +static int wait_for_process(pid_t pid) +{ + int status; + int wait_res; + + wait_res = waitpid(pid, &status, 0); + if (wait_res <= 0) { + print_error("Can't wait for the child"); + return -EINVAL; + } + if (!WIFEXITED(status)) { + ksft_print_msg("Unexpected child status pid=%d\n", pid); + return -EINVAL; + } + status = WEXITSTATUS(status); + if (status != 0) + return status; + return 0; +} + +static void rnd_buf(uint8_t *data, size_t len, unsigned int seed) +{ + int i; + + for (i = 0; i < len; i++) { + seed = 1103515245 * seed + 12345; + data[i] = (uint8_t)(seed >> (i % 13)); + } +} + +char *bin2hex(char *dst, const void *src, size_t count) +{ + const unsigned char *_src = src; + static const char hex_asc[] = "0123456789abcdef"; + + while (count--) { + unsigned char x = *_src++; + + *dst++ = hex_asc[(x & 0xf0) >> 4]; + *dst++ = hex_asc[(x & 0x0f)]; + } + *dst = 0; + return dst; +} + +static char *get_index_filename(char *mnt_dir, incfs_uuid_t id) +{ + char path[FILENAME_MAX]; + char str_id[1 + 2 * sizeof(id)]; + + bin2hex(str_id, id.bytes, sizeof(id.bytes)); + snprintf(path, ARRAY_SIZE(path), "%s/.index/%s", mnt_dir, str_id); + + return strdup(path); +} + +int open_file_by_id(char *mnt_dir, incfs_uuid_t id) +{ + char *path = get_index_filename(mnt_dir, id); + int fd = open(path, O_RDWR); + + free(path); + if (fd < 0) { + print_error("Can't open file by id."); + return -errno; + } + + return fd; +} + +int get_file_attr(char *mnt_dir, incfs_uuid_t id, char *value, int size) +{ + char *path = get_index_filename(mnt_dir, id); + int res; + + res = getxattr(path, INCFS_XATTR_METADATA_NAME, value, size); + if (res < 0) + res = -errno; + + free(path); + return res; +} + +static bool same_id(incfs_uuid_t *id1, incfs_uuid_t *id2) +{ + return !memcmp(id1->bytes, id2->bytes, sizeof(id1->bytes)); +} + +static int emit_test_blocks(char *mnt_dir, struct test_file *file, + int blocks[], int count) +{ + uint8_t data[INCFS_DATA_FILE_BLOCK_SIZE]; + uint8_t comp_data[2 * INCFS_DATA_FILE_BLOCK_SIZE]; + int block_count = (count > 32) ? 32 : count; + int data_buf_size = 2 * INCFS_DATA_FILE_BLOCK_SIZE * block_count; + uint8_t *data_buf = malloc(data_buf_size); + uint8_t *current_data = data_buf; + uint8_t *data_end = data_buf + data_buf_size; + struct incfs_new_data_block *block_buf = + calloc(block_count, sizeof(*block_buf)); + ssize_t write_res = 0; + int fd; + int error = 0; + int i = 0; + int blocks_written = 0; + + fd = open_file_by_id(mnt_dir, file->id); + if (fd <= 0) { + error = -errno; + goto out; + } + + for (i = 0; i < block_count; i++) { + int block_index = blocks[i]; + bool compress = (file->index + block_index) % 2 == 0; + int seed = get_file_block_seed(file->index, block_index); + off_t block_offset = + ((off_t)block_index) * INCFS_DATA_FILE_BLOCK_SIZE; + size_t block_size = 0; + + if (block_offset > file->size) { + error = -EINVAL; + break; + } + if (file->size - block_offset > + INCFS_DATA_FILE_BLOCK_SIZE) + block_size = INCFS_DATA_FILE_BLOCK_SIZE; + else + block_size = file->size - block_offset; + + rnd_buf(data, block_size, seed); + if (compress) { + size_t comp_size = LZ4_compress_default( + (char *)data, (char *)comp_data, block_size, + ARRAY_SIZE(comp_data)); + + if (comp_size <= 0) { + error = -EBADMSG; + break; + } + if (current_data + comp_size > data_end) { + error = -ENOMEM; + break; + } + memcpy(current_data, comp_data, comp_size); + block_size = comp_size; + block_buf[i].compression = COMPRESSION_LZ4; + } else { + if (current_data + block_size > data_end) { + error = -ENOMEM; + break; + } + memcpy(current_data, data, block_size); + block_buf[i].compression = COMPRESSION_NONE; + } + + block_buf[i].block_index = block_index; + block_buf[i].data_len = block_size; + block_buf[i].data = ptr_to_u64(current_data); + block_buf[i].compression = + compress ? COMPRESSION_LZ4 : COMPRESSION_NONE; + current_data += block_size; + } + + if (!error) { + write_res = write(fd, block_buf, sizeof(*block_buf) * i); + if (write_res < 0) + error = -errno; + else + blocks_written = write_res / sizeof(*block_buf); + } + if (error) { + ksft_print_msg( + "Writing data block error. Write returned: %d. Error:%s\n", + write_res, strerror(-error)); + } + +out: + free(block_buf); + free(data_buf); + close(fd); + return (error < 0) ? error : blocks_written; +} + +static int emit_test_block(char *mnt_dir, struct test_file *file, + int block_index) +{ + int res = emit_test_blocks(mnt_dir, file, &block_index, 1); + + if (res == 0) + return -EINVAL; + if (res == 1) + return 0; + return res; +} + +static void shuffle(int array[], int count, unsigned int seed) +{ + int i; + + for (i = 0; i < count - 1; i++) { + int items_left = count - i; + int shuffle_index; + int v; + + seed = 1103515245 * seed + 12345; + shuffle_index = i + seed % items_left; + + v = array[shuffle_index]; + array[shuffle_index] = array[i]; + array[i] = v; + } +} + +static int emit_test_file_data(char *mount_dir, struct test_file *file) +{ + int i; + int block_cnt = 1 + (file->size - 1) / INCFS_DATA_FILE_BLOCK_SIZE; + int *block_indexes = NULL; + int result = 0; + int blocks_written = 0; + + if (file->size == 0) + return 0; + + block_indexes = calloc(block_cnt, sizeof(*block_indexes)); + for (i = 0; i < block_cnt; i++) + block_indexes[i] = i; + shuffle(block_indexes, block_cnt, file->index); + + for (i = 0; i < block_cnt; i += blocks_written) { + blocks_written = emit_test_blocks(mount_dir, file, + block_indexes + i, block_cnt - i); + if (blocks_written < 0) { + result = blocks_written; + goto out; + } + if (blocks_written == 0) { + result = -EIO; + goto out; + } + } +out: + free(block_indexes); + return result; +} + +static loff_t read_whole_file(char *filename) +{ + int fd = -1; + loff_t result; + loff_t bytes_read = 0; + uint8_t buff[16 * 1024]; + + fd = open(filename, O_RDONLY); + if (fd <= 0) + return fd; + + while (1) { + int read_result = read(fd, buff, ARRAY_SIZE(buff)); + + if (read_result < 0) { + print_error("Error during reading from a file."); + result = -errno; + goto cleanup; + } else if (read_result == 0) + break; + + bytes_read += read_result; + } + result = bytes_read; + +cleanup: + close(fd); + return result; +} + +static int read_test_file(uint8_t *buf, size_t len, char *filename, + int block_idx) +{ + int fd = -1; + int result; + int bytes_read = 0; + size_t bytes_to_read = len; + off_t offset = ((off_t)block_idx) * INCFS_DATA_FILE_BLOCK_SIZE; + + fd = open(filename, O_RDONLY); + if (fd <= 0) + return fd; + + if (lseek(fd, offset, SEEK_SET) != offset) { + print_error("Seek error"); + return -errno; + } + + while (bytes_read < bytes_to_read) { + int read_result = + read(fd, buf + bytes_read, bytes_to_read - bytes_read); + if (read_result < 0) { + result = -errno; + goto cleanup; + } else if (read_result == 0) + break; + + bytes_read += read_result; + } + result = bytes_read; + +cleanup: + close(fd); + return result; +} + +static char *create_backing_dir(char *mount_dir) +{ + struct stat st; + char backing_dir_name[255]; + + snprintf(backing_dir_name, ARRAY_SIZE(backing_dir_name), "%s-src", + mount_dir); + + if (stat(backing_dir_name, &st) == 0) { + if (S_ISDIR(st.st_mode)) { + int error = delete_dir_tree(backing_dir_name); + + if (error) { + ksft_print_msg( + "Can't delete existing backing dir. %d\n", + error); + return NULL; + } + } else { + if (unlink(backing_dir_name)) { + print_error("Can't clear backing dir"); + return NULL; + } + } + } + + if (mkdir(backing_dir_name, 0777)) { + if (errno != EEXIST) { + print_error("Can't open/create backing dir"); + return NULL; + } + } + + return strdup(backing_dir_name); +} + +static int validate_test_file_content_with_seed(char *mount_dir, + struct test_file *file, + unsigned int shuffle_seed) +{ + int error = -1; + char *filename = concat_file_name(mount_dir, file->name); + off_t size = file->size; + loff_t actual_size = get_file_size(filename); + int block_cnt = 1 + (size - 1) / INCFS_DATA_FILE_BLOCK_SIZE; + int *block_indexes = NULL; + int i; + + block_indexes = alloca(sizeof(int) * block_cnt); + for (i = 0; i < block_cnt; i++) + block_indexes[i] = i; + + if (shuffle_seed != 0) + shuffle(block_indexes, block_cnt, shuffle_seed); + + if (actual_size != size) { + ksft_print_msg( + "File size doesn't match. name: %s expected size:%ld actual size:%ld\n", + filename, size, actual_size); + error = -1; + goto failure; + } + + for (i = 0; i < block_cnt; i++) { + int block_idx = block_indexes[i]; + uint8_t expected_block[INCFS_DATA_FILE_BLOCK_SIZE]; + uint8_t actual_block[INCFS_DATA_FILE_BLOCK_SIZE]; + int seed = get_file_block_seed(file->index, block_idx); + size_t bytes_to_compare = min( + (off_t)INCFS_DATA_FILE_BLOCK_SIZE, + size - ((off_t)block_idx) * INCFS_DATA_FILE_BLOCK_SIZE); + int read_result = + read_test_file(actual_block, INCFS_DATA_FILE_BLOCK_SIZE, + filename, block_idx); + if (read_result < 0) { + ksft_print_msg( + "Error reading block %d from file %s. Error: %s\n", + block_idx, filename, strerror(-read_result)); + error = read_result; + goto failure; + } + rnd_buf(expected_block, INCFS_DATA_FILE_BLOCK_SIZE, seed); + if (memcmp(expected_block, actual_block, bytes_to_compare)) { + ksft_print_msg( + "File contents don't match. name: %s block:%d\n", + file->name, block_idx); + error = -2; + goto failure; + } + } + free(filename); + return 0; + +failure: + free(filename); + return error; +} + +static int validate_test_file_content(char *mount_dir, struct test_file *file) +{ + return validate_test_file_content_with_seed(mount_dir, file, 0); +} + +static int data_producer(char *mount_dir, struct test_files_set *test_set) +{ + int ret = 0; + int timeout_ms = 1000; + struct incfs_pending_read_info prs[100] = {}; + int prs_size = ARRAY_SIZE(prs); + int fd = open_commands_file(mount_dir); + + if (fd < 0) + return -errno; + + while ((ret = wait_for_pending_reads(fd, timeout_ms, prs, prs_size)) > + 0) { + int read_count = ret; + int i; + + for (i = 0; i < read_count; i++) { + int j = 0; + struct test_file *file = NULL; + + for (j = 0; j < test_set->files_count; j++) { + bool same = same_id(&(test_set->files[j].id), + &(prs[i].file_id)); + + if (same) { + file = &test_set->files[j]; + break; + } + } + if (!file) { + ksft_print_msg( + "Unknown file in pending reads.\n"); + break; + } + + ret = emit_test_block(mount_dir, file, + prs[i].block_index); + if (ret < 0) { + ksft_print_msg("Emitting test data error: %s\n", + strerror(-ret)); + break; + } + } + } + close(fd); + return ret; +} + +static int build_mtree(struct test_file *file) +{ + char data[INCFS_DATA_FILE_BLOCK_SIZE] = {}; + const int digest_size = SHA256_DIGEST_SIZE; + const int hash_per_block = INCFS_DATA_FILE_BLOCK_SIZE / digest_size; + int block_count = 0; + int hash_block_count = 0; + int total_tree_block_count = 0; + int tree_lvl_index[INCFS_MAX_MTREE_LEVELS] = {}; + int tree_lvl_count[INCFS_MAX_MTREE_LEVELS] = {}; + int levels_count = 0; + char data_to_sign[256] = {}; + int sig_data_size; + int i, level; + + if (file->size == 0) + return 0; + + block_count = 1 + (file->size - 1) / INCFS_DATA_FILE_BLOCK_SIZE; + hash_block_count = block_count; + for (i = 0; hash_block_count > 1; i++) { + hash_block_count = (hash_block_count + hash_per_block - 1) + / hash_per_block; + tree_lvl_count[i] = hash_block_count; + total_tree_block_count += hash_block_count; + } + levels_count = i; + + for (i = 0; i < levels_count; i++) { + int prev_lvl_base = (i == 0) ? total_tree_block_count : + tree_lvl_index[i - 1]; + + tree_lvl_index[i] = prev_lvl_base - tree_lvl_count[i]; + } + + file->mtree_block_count = total_tree_block_count; + if (block_count == 1) { + int seed = get_file_block_seed(file->index, 0); + + rnd_buf((uint8_t *)data, file->size, seed); + sha256(data, file->size, file->root_hash); + return 0; + } + + file->mtree = calloc(total_tree_block_count, sizeof(*file->mtree)); + /* Build level 0 hashes. */ + for (i = 0; i < block_count; i++) { + off_t offset = i * INCFS_DATA_FILE_BLOCK_SIZE; + size_t block_size = INCFS_DATA_FILE_BLOCK_SIZE; + int block_index = tree_lvl_index[0] + + i / hash_per_block; + int block_off = (i % hash_per_block) * digest_size; + int seed = get_file_block_seed(file->index, i); + char *hash_ptr = file->mtree[block_index].data + block_off; + + if (file->size - offset < block_size) + block_size = file->size - offset; + + rnd_buf((uint8_t *)data, block_size, seed); + sha256(data, block_size, hash_ptr); + } + + /* Build higher levels of hash tree. */ + for (level = 1; level < levels_count; level++) { + int prev_lvl_base = tree_lvl_index[level - 1]; + int prev_lvl_count = tree_lvl_count[level - 1]; + + for (i = 0; i < prev_lvl_count; i++) { + int block_index = + i / hash_per_block + tree_lvl_index[level]; + int block_off = (i % hash_per_block) * digest_size; + char *hash_ptr = + file->mtree[block_index].data + block_off; + + sha256(file->mtree[i + prev_lvl_base].data, + INCFS_DATA_FILE_BLOCK_SIZE, hash_ptr); + } + } + + /* Calculate root hash from the top block */ + sha256(file->mtree[0].data, + INCFS_DATA_FILE_BLOCK_SIZE, file->root_hash); + + /* Calculating digital signature */ + snprintf(file->sig.add_data, sizeof(file->sig.add_data), "%ld", + file->size); + memcpy(data_to_sign, file->root_hash, SHA256_DIGEST_SIZE); + memcpy(data_to_sign + SHA256_DIGEST_SIZE, file->sig.add_data, + strlen(file->sig.add_data)); + sig_data_size = SHA256_DIGEST_SIZE + strlen(file->sig.add_data); + if (!sign_pkcs7(data_to_sign, sig_data_size, private_key, x509_cert, + &file->sig.data, &file->sig.size)) { + ksft_print_msg("Signing failed.\n"); + return -EINVAL; + } + + return 0; +} + +static int load_hash_tree(const char *mount_dir, struct test_file *file) +{ + int err; + int i; + int fd; + + size_t blocks_size = + file->mtree_block_count * sizeof(struct incfs_new_data_block); + struct incfs_new_data_block *blocks = NULL; + char *file_path; + + if (blocks_size == 0) + return 0; + + blocks = malloc(blocks_size); + if (!blocks) + return -ENOMEM; + + for (i = 0; i < file->mtree_block_count; i++) { + blocks[i] = (struct incfs_new_data_block){ + .block_index = i, + .data_len = INCFS_DATA_FILE_BLOCK_SIZE, + .data = ptr_to_u64(file->mtree[i].data), + .flags = INCFS_BLOCK_FLAGS_HASH + }; + } + + file_path = concat_file_name(mount_dir, file->name); + fd = open(file_path, O_RDWR); + free(file_path); + if (fd < 0) { + err = errno; + goto failure; + } + + err = write(fd, blocks, blocks_size); + close(fd); + + if (err < blocks_size) + err = errno; + else { + err = 0; + free(file->mtree); + } + +failure: + free(blocks); + return err; +} + +static int cant_touch_index_test(char *mount_dir) +{ + char *file_name = "test_file"; + int file_size = 123; + incfs_uuid_t file_id; + char *index_path = concat_file_name(mount_dir, ".index"); + char *subdir = concat_file_name(index_path, "subdir"); + char *dst_name = concat_file_name(mount_dir, "something"); + char *filename_in_index = NULL; + char *file_path = concat_file_name(mount_dir, file_name); + char *backing_dir; + int cmd_fd = -1; + int err; + + backing_dir = create_backing_dir(mount_dir); + if (!backing_dir) + goto failure; + + /* Mount FS and release the backing file. */ + if (mount_fs(mount_dir, backing_dir, 50) != 0) + goto failure; + free(backing_dir); + + cmd_fd = open_commands_file(mount_dir); + if (cmd_fd < 0) + goto failure; + + + err = mkdir(subdir, 0777); + if (err == 0 || errno != EBUSY) { + print_error("Shouldn't be able to crate subdir in index\n"); + goto failure; + } + + err = emit_file(cmd_fd, ".index", file_name, &file_id, + file_size, NULL); + if (err != -EBUSY) { + print_error("Shouldn't be able to crate a file in index\n"); + goto failure; + } + + err = emit_file(cmd_fd, NULL, file_name, &file_id, + file_size, NULL); + if (err < 0) + goto failure; + filename_in_index = get_index_filename(mount_dir, file_id); + + err = unlink(filename_in_index); + if (err == 0 || errno != EBUSY) { + print_error("Shouldn't be delete from index\n"); + goto failure; + } + + + err = rename(filename_in_index, dst_name); + if (err == 0 || errno != EBUSY) { + print_error("Shouldn't be able to move from index\n"); + goto failure; + } + + free(filename_in_index); + filename_in_index = concat_file_name(index_path, "abc"); + err = link(file_path, filename_in_index); + if (err == 0 || errno != EBUSY) { + print_error("Shouldn't be able to link inside index\n"); + goto failure; + } + + close(cmd_fd); + free(subdir); + free(index_path); + free(dst_name); + free(filename_in_index); + if (umount(mount_dir) != 0) { + print_error("Can't unmout FS"); + goto failure; + } + + return TEST_SUCCESS; + +failure: + free(subdir); + free(dst_name); + free(index_path); + free(filename_in_index); + close(cmd_fd); + umount(mount_dir); + return TEST_FAILURE; +} + +static bool iterate_directory(char *dir_to_iterate, bool root, int file_count) +{ + struct expected_name { + const char *name; + bool root_only; + bool found; + } names[] = { + {INCFS_LOG_FILENAME, true, false}, + {INCFS_PENDING_READS_FILENAME, true, false}, + {".index", true, false}, + {"..", false, false}, + {".", false, false}, + }; + + bool pass = true, found; + int i; + + /* Test directory iteration */ + int fd = open(dir_to_iterate, O_RDONLY | O_DIRECTORY); + + if (fd < 0) { + print_error("Can't open directory\n"); + return false; + } + + for (;;) { + /* Enough space for one dirent - no name over 30 */ + char buf[sizeof(struct linux_dirent64) + NAME_MAX]; + struct linux_dirent64 *dirent = (struct linux_dirent64 *) buf; + int nread; + int i; + + for (i = 0; i < NAME_MAX; ++i) { + nread = syscall(__NR_getdents64, fd, buf, + sizeof(struct linux_dirent64) + i); + + if (nread >= 0) + break; + if (errno != EINVAL) + break; + } + + if (nread == 0) + break; + if (nread < 0) { + print_error("Error iterating directory\n"); + pass = false; + goto failure; + } + + /* Expected size is rounded up to 8 byte boundary. Not sure if + * this is universal truth or just happenstance, but useful test + * for the moment + */ + if (nread != (((sizeof(struct linux_dirent64) + + strlen(dirent->d_name) + 1) + 7) & ~7)) { + print_error("Wrong dirent size"); + pass = false; + goto failure; + } + + found = false; + for (i = 0; i < sizeof(names) / sizeof(*names); ++i) + if (!strcmp(dirent->d_name, names[i].name)) { + if (names[i].root_only && !root) { + print_error("Root file error"); + pass = false; + goto failure; + } + + if (names[i].found) { + print_error("File appears twice"); + pass = false; + goto failure; + } + + names[i].found = true; + found = true; + break; + } + + if (!found) + --file_count; + } + + for (i = 0; i < sizeof(names) / sizeof(*names); ++i) { + if (!names[i].found) + if (root || !names[i].root_only) { + print_error("Expected file not present"); + pass = false; + goto failure; + } + } + + if (file_count) { + print_error("Wrong number of files\n"); + pass = false; + goto failure; + } + +failure: + close(fd); + return pass; +} + +static int basic_file_ops_test(char *mount_dir) +{ + struct test_files_set test = get_test_files_set(); + const int file_num = test.files_count; + char *subdir1 = concat_file_name(mount_dir, "subdir1"); + char *subdir2 = concat_file_name(mount_dir, "subdir2"); + char *backing_dir; + int cmd_fd = -1; + int i, err; + + backing_dir = create_backing_dir(mount_dir); + if (!backing_dir) + goto failure; + + /* Mount FS and release the backing file. */ + if (mount_fs(mount_dir, backing_dir, 50) != 0) + goto failure; + free(backing_dir); + + cmd_fd = open_commands_file(mount_dir); + if (cmd_fd < 0) + goto failure; + + err = mkdir(subdir1, 0777); + if (err < 0 && errno != EEXIST) { + print_error("Can't create subdir1\n"); + goto failure; + } + + err = mkdir(subdir2, 0777); + if (err < 0 && errno != EEXIST) { + print_error("Can't create subdir2\n"); + goto failure; + } + + /* Create all test files in subdir1 directory */ + for (i = 0; i < file_num; i++) { + struct test_file *file = &test.files[i]; + loff_t size; + char *file_path = concat_file_name(subdir1, file->name); + + err = emit_file(cmd_fd, "subdir1", file->name, &file->id, + file->size, NULL); + if (err < 0) + goto failure; + + size = get_file_size(file_path); + free(file_path); + if (size != file->size) { + ksft_print_msg("Wrong size %lld of %s.\n", + size, file->name); + goto failure; + } + } + + if (!iterate_directory(subdir1, false, file_num)) + goto failure; + + /* Link the files to subdir2 */ + for (i = 0; i < file_num; i++) { + struct test_file *file = &test.files[i]; + char *src_name = concat_file_name(subdir1, file->name); + char *dst_name = concat_file_name(subdir2, file->name); + loff_t size; + + err = link(src_name, dst_name); + if (err < 0) { + print_error("Can't move file\n"); + goto failure; + } + + size = get_file_size(dst_name); + if (size != file->size) { + ksft_print_msg("Wrong size %lld of %s.\n", + size, file->name); + goto failure; + } + free(src_name); + free(dst_name); + } + + /* Move the files from subdir2 to the mount dir */ + for (i = 0; i < file_num; i++) { + struct test_file *file = &test.files[i]; + char *src_name = concat_file_name(subdir2, file->name); + char *dst_name = concat_file_name(mount_dir, file->name); + loff_t size; + + err = rename(src_name, dst_name); + if (err < 0) { + print_error("Can't move file\n"); + goto failure; + } + + size = get_file_size(dst_name); + if (size != file->size) { + ksft_print_msg("Wrong size %lld of %s.\n", + size, file->name); + goto failure; + } + free(src_name); + free(dst_name); + } + + /* +2 because there are 2 subdirs */ + if (!iterate_directory(mount_dir, true, file_num + 2)) + goto failure; + + /* Open and close all files from the mount dir */ + for (i = 0; i < file_num; i++) { + struct test_file *file = &test.files[i]; + char *path = concat_file_name(mount_dir, file->name); + int fd; + + fd = open(path, O_RDWR); + free(path); + if (fd <= 0) { + print_error("Can't open file"); + goto failure; + } + if (close(fd)) { + print_error("Can't close file"); + goto failure; + } + } + + /* Delete all files from the mount dir */ + for (i = 0; i < file_num; i++) { + struct test_file *file = &test.files[i]; + char *path = concat_file_name(mount_dir, file->name); + + err = unlink(path); + free(path); + if (err < 0) { + print_error("Can't unlink file"); + goto failure; + } + } + + err = delete_dir_tree(subdir1); + if (err) { + ksft_print_msg("Error deleting subdir1 %d", err); + goto failure; + } + + err = rmdir(subdir2); + if (err) { + print_error("Error deleting subdir2"); + goto failure; + } + + close(cmd_fd); + cmd_fd = -1; + if (umount(mount_dir) != 0) { + print_error("Can't unmout FS"); + goto failure; + } + + return TEST_SUCCESS; + +failure: + close(cmd_fd); + umount(mount_dir); + return TEST_FAILURE; +} + +static int dynamic_files_and_data_test(char *mount_dir) +{ + struct test_files_set test = get_test_files_set(); + const int file_num = test.files_count; + const int missing_file_idx = 5; + int cmd_fd = -1; + char *backing_dir; + int i; + + backing_dir = create_backing_dir(mount_dir); + if (!backing_dir) + goto failure; + + /* Mount FS and release the backing file. */ + if (mount_fs(mount_dir, backing_dir, 50) != 0) + goto failure; + free(backing_dir); + + cmd_fd = open_commands_file(mount_dir); + if (cmd_fd < 0) + goto failure; + + /* Check that test files don't exist in the filesystem. */ + for (i = 0; i < file_num; i++) { + struct test_file *file = &test.files[i]; + char *filename = concat_file_name(mount_dir, file->name); + + if (access(filename, F_OK) != -1) { + ksft_print_msg( + "File %s somehow already exists in a clean FS.\n", + filename); + goto failure; + } + free(filename); + } + + /* Write test data into the command file. */ + for (i = 0; i < file_num; i++) { + struct test_file *file = &test.files[i]; + int res; + + build_mtree(file); + res = emit_file(cmd_fd, NULL, file->name, &file->id, + file->size, NULL); + if (res < 0) { + ksft_print_msg("Error %s emiting file %s.\n", + strerror(-res), file->name); + goto failure; + } + + /* Skip writing data to one file so we can check */ + /* that it's missing later. */ + if (i == missing_file_idx) + continue; + + res = load_hash_tree(mount_dir, file); + if (res) { + ksft_print_msg("Can't load hashes for %s. error: %s\n", + file->name, strerror(-res)); + goto failure; + } + + res = emit_test_file_data(mount_dir, file); + if (res) { + ksft_print_msg("Error %s emiting data for %s.\n", + strerror(-res), file->name); + goto failure; + } + } + + /* Validate contents of the FS */ + for (i = 0; i < file_num; i++) { + struct test_file *file = &test.files[i]; + + if (i == missing_file_idx) { + /* No data has been written to this file. */ + /* Check for read error; */ + uint8_t buf; + char *filename = + concat_file_name(mount_dir, file->name); + int res = read_test_file(&buf, 1, filename, 0); + + free(filename); + if (res > 0) { + ksft_print_msg( + "Data present, even though never writtern.\n"); + goto failure; + } + if (res != -ETIME) { + ksft_print_msg("Wrong error code: %d.\n", res); + goto failure; + } + } else { + if (validate_test_file_content(mount_dir, file) < 0) + goto failure; + } + } + + close(cmd_fd); + cmd_fd = -1; + if (umount(mount_dir) != 0) { + print_error("Can't unmout FS"); + goto failure; + } + + return TEST_SUCCESS; + +failure: + close(cmd_fd); + umount(mount_dir); + return TEST_FAILURE; +} + +static int concurrent_reads_and_writes_test(char *mount_dir) +{ + struct test_files_set test = get_test_files_set(); + const int file_num = test.files_count; + /* Validate each file from that many child processes. */ + const int child_multiplier = 3; + int cmd_fd = -1; + char *backing_dir; + int status; + int i; + pid_t producer_pid; + pid_t *child_pids = alloca(child_multiplier * file_num * sizeof(pid_t)); + + backing_dir = create_backing_dir(mount_dir); + if (!backing_dir) + goto failure; + + /* Mount FS and release the backing file. */ + if (mount_fs(mount_dir, backing_dir, 50) != 0) + goto failure; + free(backing_dir); + + cmd_fd = open_commands_file(mount_dir); + if (cmd_fd < 0) + goto failure; + + /* Tell FS about the files, without actually providing the data. */ + for (i = 0; i < file_num; i++) { + struct test_file *file = &test.files[i]; + int res; + + res = emit_file(cmd_fd, NULL, file->name, &file->id, + file->size, NULL); + if (res) + goto failure; + } + + /* Start child processes acessing data in the files */ + for (i = 0; i < file_num * child_multiplier; i++) { + struct test_file *file = &test.files[i / child_multiplier]; + pid_t child_pid = flush_and_fork(); + + if (child_pid == 0) { + /* This is a child process, do the data validation. */ + int ret = validate_test_file_content_with_seed( + mount_dir, file, i); + if (ret >= 0) { + /* Zero exit status if data is valid. */ + exit(0); + } + + /* Positive status if validation error found. */ + exit(-ret); + } else if (child_pid > 0) { + child_pids[i] = child_pid; + } else { + print_error("Fork error"); + goto failure; + } + } + + producer_pid = flush_and_fork(); + if (producer_pid == 0) { + int ret; + /* + * This is a child that should provide data to + * pending reads. + */ + + ret = data_producer(mount_dir, &test); + exit(-ret); + } else { + status = wait_for_process(producer_pid); + if (status != 0) { + ksft_print_msg("Data produces failed. %d(%s) ", status, + strerror(status)); + goto failure; + } + } + + /* Check that all children has finished with 0 exit status */ + for (i = 0; i < file_num * child_multiplier; i++) { + struct test_file *file = &test.files[i / child_multiplier]; + + status = wait_for_process(child_pids[i]); + if (status != 0) { + ksft_print_msg( + "Validation for the file %s failed with code %d (%s)\n", + file->name, status, strerror(status)); + goto failure; + } + } + + /* Check that there are no pending reads left */ + { + struct incfs_pending_read_info prs[1] = {}; + int timeout = 0; + int read_count = wait_for_pending_reads(cmd_fd, timeout, prs, + ARRAY_SIZE(prs)); + + if (read_count) { + ksft_print_msg( + "Pending reads pending when all data written\n"); + goto failure; + } + } + + close(cmd_fd); + cmd_fd = -1; + if (umount(mount_dir) != 0) { + print_error("Can't unmout FS"); + goto failure; + } + + return TEST_SUCCESS; + +failure: + close(cmd_fd); + umount(mount_dir); + return TEST_FAILURE; +} + +static int work_after_remount_test(char *mount_dir) +{ + struct test_files_set test = get_test_files_set(); + const int file_num = test.files_count; + const int file_num_stage1 = file_num / 2; + const int file_num_stage2 = file_num; + char *backing_dir = NULL; + int i = 0; + int cmd_fd = -1; + + backing_dir = create_backing_dir(mount_dir); + if (!backing_dir) + goto failure; + + /* Mount FS and release the backing file. */ + if (mount_fs(mount_dir, backing_dir, 50) != 0) + goto failure; + + cmd_fd = open_commands_file(mount_dir); + if (cmd_fd < 0) + goto failure; + + /* Write first half of the data into the command file. (stage 1) */ + for (i = 0; i < file_num_stage1; i++) { + struct test_file *file = &test.files[i]; + int res; + + build_mtree(file); + if (emit_file(cmd_fd, NULL, file->name, &file->id, + file->size, NULL)) + goto failure; + + if (emit_test_file_data(mount_dir, file)) + goto failure; + + res = load_hash_tree(mount_dir, file); + if (res) { + ksft_print_msg("Can't load hashes for %s. error: %s\n", + file->name, strerror(-res)); + goto failure; + } +} + + /* Unmount and mount again, to see that data is persistent. */ + close(cmd_fd); + cmd_fd = -1; + if (umount(mount_dir) != 0) { + print_error("Can't unmout FS"); + goto failure; + } + + if (mount_fs(mount_dir, backing_dir, 50) != 0) + goto failure; + + cmd_fd = open_commands_file(mount_dir); + if (cmd_fd < 0) + goto failure; + + /* Write the second half of the data into the command file. (stage 2) */ + for (; i < file_num_stage2; i++) { + struct test_file *file = &test.files[i]; + int res = emit_file(cmd_fd, NULL, file->name, &file->id, + file->size, NULL); + + if (res) + goto failure; + + if (emit_test_file_data(mount_dir, file)) + goto failure; + } + + /* Validate contents of the FS */ + for (i = 0; i < file_num_stage2; i++) { + struct test_file *file = &test.files[i]; + + if (validate_test_file_content(mount_dir, file) < 0) + goto failure; + } + + /* Delete all files */ + for (i = 0; i < file_num; i++) { + struct test_file *file = &test.files[i]; + char *filename = concat_file_name(mount_dir, file->name); + char *filename_in_index = get_index_filename(mount_dir, + file->id); + + if (access(filename, F_OK) != 0) { + ksft_print_msg("File %s is not visible.\n", filename); + goto failure; + } + + if (access(filename_in_index, F_OK) != 0) { + ksft_print_msg("File %s is not visible.\n", + filename_in_index); + goto failure; + } + + unlink(filename); + + if (access(filename, F_OK) != -1) { + ksft_print_msg("File %s is still present.\n", filename); + goto failure; + } + + if (access(filename_in_index, F_OK) != 0) { + ksft_print_msg("File %s is still present.\n", + filename_in_index); + goto failure; + } + free(filename); + free(filename_in_index); + } + + /* Unmount and mount again, to see that deleted files stay deleted. */ + close(cmd_fd); + cmd_fd = -1; + if (umount(mount_dir) != 0) { + print_error("Can't unmout FS"); + goto failure; + } + + if (mount_fs(mount_dir, backing_dir, 50) != 0) + goto failure; + + cmd_fd = open_commands_file(mount_dir); + if (cmd_fd < 0) + goto failure; + + /* Validate all deleted files are still deleted. */ + for (i = 0; i < file_num; i++) { + struct test_file *file = &test.files[i]; + char *filename = concat_file_name(mount_dir, file->name); + + if (access(filename, F_OK) != -1) { + ksft_print_msg("File %s is still visible.\n", filename); + goto failure; + } + free(filename); + } + + /* Final unmount */ + close(cmd_fd); + free(backing_dir); + cmd_fd = -1; + if (umount(mount_dir) != 0) { + print_error("Can't unmout FS"); + goto failure; + } + + return TEST_SUCCESS; + +failure: + close(cmd_fd); + free(backing_dir); + umount(mount_dir); + return TEST_FAILURE; +} + +static int attribute_test(char *mount_dir) +{ + char file_attr[] = "metadata123123"; + char attr_buf[INCFS_MAX_FILE_ATTR_SIZE] = {}; + int cmd_fd = -1; + incfs_uuid_t file_id; + int attr_res = 0; + char *backing_dir; + + + backing_dir = create_backing_dir(mount_dir); + if (!backing_dir) + goto failure; + + /* Mount FS and release the backing file. */ + if (mount_fs(mount_dir, backing_dir, 50) != 0) + goto failure; + + + cmd_fd = open_commands_file(mount_dir); + if (cmd_fd < 0) + goto failure; + + if (emit_file(cmd_fd, NULL, "file", &file_id, 12, file_attr)) + goto failure; + + /* Test attribute values */ + attr_res = get_file_attr(mount_dir, file_id, attr_buf, + ARRAY_SIZE(attr_buf)); + if (attr_res != strlen(file_attr)) { + ksft_print_msg("Get file attr error: %d\n", attr_res); + goto failure; + } + if (strcmp(attr_buf, file_attr) != 0) { + ksft_print_msg("Incorrect file attr value: '%s'", attr_buf); + goto failure; + } + + /* Unmount and mount again, to see that attributes are persistent. */ + close(cmd_fd); + cmd_fd = -1; + if (umount(mount_dir) != 0) { + print_error("Can't unmout FS"); + goto failure; + } + + if (mount_fs(mount_dir, backing_dir, 50) != 0) + goto failure; + + cmd_fd = open_commands_file(mount_dir); + if (cmd_fd < 0) + goto failure; + + /* Test attribute values again after remount*/ + attr_res = get_file_attr(mount_dir, file_id, attr_buf, + ARRAY_SIZE(attr_buf)); + if (attr_res != strlen(file_attr)) { + ksft_print_msg("Get dir attr error: %d\n", attr_res); + goto failure; + } + if (strcmp(attr_buf, file_attr) != 0) { + ksft_print_msg("Incorrect file attr value: '%s'", attr_buf); + goto failure; + } + + /* Final unmount */ + close(cmd_fd); + free(backing_dir); + cmd_fd = -1; + if (umount(mount_dir) != 0) { + print_error("Can't unmout FS"); + goto failure; + } + + return TEST_SUCCESS; + +failure: + close(cmd_fd); + free(backing_dir); + umount(mount_dir); + return TEST_FAILURE; +} + +static int child_procs_waiting_for_data_test(char *mount_dir) +{ + struct test_files_set test = get_test_files_set(); + const int file_num = test.files_count; + int cmd_fd = -1; + int i; + pid_t *child_pids = alloca(file_num * sizeof(pid_t)); + char *backing_dir; + + backing_dir = create_backing_dir(mount_dir); + if (!backing_dir) + goto failure; + + /* Mount FS and release the backing file. (10s wait time) */ + if (mount_fs(mount_dir, backing_dir, 10000) != 0) + goto failure; + + + cmd_fd = open_commands_file(mount_dir); + if (cmd_fd < 0) + goto failure; + + /* Tell FS about the files, without actually providing the data. */ + for (i = 0; i < file_num; i++) { + struct test_file *file = &test.files[i]; + + emit_file(cmd_fd, NULL, file->name, &file->id, + file->size, NULL); + } + + /* Start child processes acessing data in the files */ + for (i = 0; i < file_num; i++) { + struct test_file *file = &test.files[i]; + pid_t child_pid = flush_and_fork(); + + if (child_pid == 0) { + /* This is a child process, do the data validation. */ + int ret = validate_test_file_content(mount_dir, file); + + if (ret >= 0) { + /* Zero exit status if data is valid. */ + exit(0); + } + + /* Positive status if validation error found. */ + exit(-ret); + } else if (child_pid > 0) { + child_pids[i] = child_pid; + } else { + print_error("Fork error"); + goto failure; + } + } + + /* Write test data into the command file. */ + for (i = 0; i < file_num; i++) { + struct test_file *file = &test.files[i]; + + if (emit_test_file_data(mount_dir, file)) + goto failure; + } + + /* Check that all children has finished with 0 exit status */ + for (i = 0; i < file_num; i++) { + struct test_file *file = &test.files[i]; + int status = wait_for_process(child_pids[i]); + + if (status != 0) { + ksft_print_msg( + "Validation for the file %s failed with code %d (%s)\n", + file->name, status, strerror(status)); + goto failure; + } + } + + close(cmd_fd); + free(backing_dir); + cmd_fd = -1; + if (umount(mount_dir) != 0) { + print_error("Can't unmout FS"); + goto failure; + } + + return TEST_SUCCESS; + +failure: + close(cmd_fd); + free(backing_dir); + umount(mount_dir); + return TEST_FAILURE; +} + +static int multiple_providers_test(char *mount_dir) +{ + struct test_files_set test = get_test_files_set(); + const int file_num = test.files_count; + const int producer_count = 5; + int cmd_fd = -1; + int status; + int i; + pid_t *producer_pids = alloca(producer_count * sizeof(pid_t)); + char *backing_dir; + + backing_dir = create_backing_dir(mount_dir); + if (!backing_dir) + goto failure; + + /* Mount FS and release the backing file. (10s wait time) */ + if (mount_fs(mount_dir, backing_dir, 10000) != 0) + goto failure; + + cmd_fd = open_commands_file(mount_dir); + if (cmd_fd < 0) + goto failure; + + /* Tell FS about the files, without actually providing the data. */ + for (i = 0; i < file_num; i++) { + struct test_file *file = &test.files[i]; + + if (emit_file(cmd_fd, NULL, file->name, &file->id, + file->size, NULL) < 0) + goto failure; + } + + /* Start producer processes */ + for (i = 0; i < producer_count; i++) { + pid_t producer_pid = flush_and_fork(); + + if (producer_pid == 0) { + int ret; + /* + * This is a child that should provide data to + * pending reads. + */ + + ret = data_producer(mount_dir, &test); + exit(-ret); + } else if (producer_pid > 0) { + producer_pids[i] = producer_pid; + } else { + print_error("Fork error"); + goto failure; + } + } + + /* Validate FS content */ + for (i = 0; i < file_num; i++) { + struct test_file *file = &test.files[i]; + char *filename = concat_file_name(mount_dir, file->name); + loff_t read_result = read_whole_file(filename); + + free(filename); + if (read_result != file->size) { + ksft_print_msg( + "Error validating file %s. Result: %ld\n", + file->name, read_result); + goto failure; + } + } + + /* Check that all producers has finished with 0 exit status */ + for (i = 0; i < producer_count; i++) { + status = wait_for_process(producer_pids[i]); + if (status != 0) { + ksft_print_msg("Producer %d failed with code (%s)\n", i, + strerror(status)); + goto failure; + } + } + + close(cmd_fd); + free(backing_dir); + cmd_fd = -1; + if (umount(mount_dir) != 0) { + print_error("Can't unmout FS"); + goto failure; + } + + return TEST_SUCCESS; + +failure: + close(cmd_fd); + free(backing_dir); + umount(mount_dir); + return TEST_FAILURE; +} + +static int signature_test(char *mount_dir) +{ + struct test_files_set test = get_test_files_set(); + const int file_num = test.files_count; + int i = 0; + unsigned char sig_buf[INCFS_MAX_SIGNATURE_SIZE]; + char *backing_dir; + int cmd_fd = -1; + + backing_dir = create_backing_dir(mount_dir); + if (!backing_dir) + goto failure; + + /* Mount FS and release the backing file. (10s wait time) */ + if (mount_fs(mount_dir, backing_dir, 10000) != 0) + goto failure; + + cmd_fd = open_commands_file(mount_dir); + if (cmd_fd < 0) + goto failure; + + /* Write hashes and data. */ + for (i = 0; i < file_num; i++) { + struct test_file *file = &test.files[i]; + int res; + + build_mtree(file); + + res = crypto_emit_file(cmd_fd, NULL, file->name, &file->id, + file->size, file->root_hash, + file->sig.data, file->sig.size, file->sig.add_data); + + if (res) { + ksft_print_msg("Emit failed for %s. error: %s\n", + file->name, strerror(-res)); + goto failure; + } + + if (emit_test_file_data(mount_dir, file)) + goto failure; + + res = load_hash_tree(mount_dir, file); + if (res) { + ksft_print_msg("Can't load hashes for %s. error: %s\n", + file->name, strerror(-res)); + goto failure; + } + } + + /* Validate data */ + for (i = 0; i < file_num; i++) { + struct test_file *file = &test.files[i]; + int sig_len; + char *path; + int fd; + + if (validate_test_file_content(mount_dir, file) < 0) + goto failure; + + path = concat_file_name(mount_dir, file->name); + fd = open(path, O_RDWR); + free(path); + if (fd < 0) { + print_error("Can't open file"); + goto failure; + } + + sig_len = get_file_signature(fd, sig_buf, ARRAY_SIZE(sig_buf)); + + if (close(fd)) { + print_error("Can't close file"); + goto failure; + } + + if (sig_len < 0) { + ksft_print_msg("Can't load signature %s. error: %s\n", + file->name, strerror(-sig_len)); + goto failure; + } + + if (sig_len != file->sig.size || + memcmp(sig_buf, file->sig.data, sig_len)) { + ksft_print_msg("Signature mismatch %s.\n", + file->name); + goto failure; + } + } + + /* Unmount and mount again, to make sure the signature is persistent. */ + close(cmd_fd); + cmd_fd = -1; + if (umount(mount_dir) != 0) { + print_error("Can't unmout FS"); + goto failure; + } + if (mount_fs(mount_dir, backing_dir, 50) != 0) + goto failure; + + cmd_fd = open_commands_file(mount_dir); + if (cmd_fd < 0) + goto failure; + + /* Validate data again */ + for (i = 0; i < file_num; i++) { + struct test_file *file = &test.files[i]; + int sig_len; + char *path; + int fd; + + if (validate_test_file_content(mount_dir, file) < 0) + goto failure; + + path = concat_file_name(mount_dir, file->name); + fd = open(path, O_RDWR); + free(path); + if (fd < 0) { + print_error("Can't open file"); + goto failure; + } + + sig_len = get_file_signature(fd, sig_buf, ARRAY_SIZE(sig_buf)); + + if (close(fd)) { + print_error("Can't close file"); + goto failure; + } + + if (sig_len < 0) { + ksft_print_msg("Can't load signature %s. error: %s\n", + file->name, strerror(-sig_len)); + goto failure; + } + if (sig_len != file->sig.size || + memcmp(sig_buf, file->sig.data, sig_len)) { + ksft_print_msg("Signature mismatch %s.\n", + file->name); + goto failure; + } + } + + /* Final unmount */ + close(cmd_fd); + cmd_fd = -1; + if (umount(mount_dir) != 0) { + print_error("Can't unmout FS"); + goto failure; + } + return TEST_SUCCESS; + +failure: + close(cmd_fd); + free(backing_dir); + umount(mount_dir); + return TEST_FAILURE; +} + +static int hash_tree_test(char *mount_dir) +{ + char *backing_dir; + struct test_files_set test = get_test_files_set(); + const int file_num = test.files_count; + const int corrupted_file_idx = 5; + int i = 0; + int cmd_fd = -1; + + backing_dir = create_backing_dir(mount_dir); + if (!backing_dir) + goto failure; + + /* Mount FS and release the backing file. */ + if (mount_fs(mount_dir, backing_dir, 50) != 0) + goto failure; + + cmd_fd = open_commands_file(mount_dir); + if (cmd_fd < 0) + goto failure; + + /* Write hashes and data. */ + for (i = 0; i < file_num; i++) { + struct test_file *file = &test.files[i]; + int res; + + build_mtree(file); + res = crypto_emit_file(cmd_fd, NULL, file->name, &file->id, + file->size, file->root_hash, + file->sig.data, file->sig.size, file->sig.add_data); + + if (i == corrupted_file_idx) { + /* Corrupt third blocks hash */ + file->mtree[0].data[2 * SHA256_DIGEST_SIZE] ^= 0xff; + } + if (emit_test_file_data(mount_dir, file)) + goto failure; + + res = load_hash_tree(mount_dir, file); + if (res) { + ksft_print_msg("Can't load hashes for %s. error: %s\n", + file->name, strerror(-res)); + goto failure; + } + } + + /* Validate data */ + for (i = 0; i < file_num; i++) { + struct test_file *file = &test.files[i]; + + if (i == corrupted_file_idx) { + uint8_t data[INCFS_DATA_FILE_BLOCK_SIZE]; + char *filename = + concat_file_name(mount_dir, file->name); + int res; + + res = read_test_file(data, INCFS_DATA_FILE_BLOCK_SIZE, + filename, 2); + free(filename); + if (res != -EBADMSG) { + ksft_print_msg("Hash violation missed1. %d\n", + res); + goto failure; + } + } else if (validate_test_file_content(mount_dir, file) < 0) + goto failure; + } + + /* Unmount and mount again, to that hashes are persistent. */ + close(cmd_fd); + cmd_fd = -1; + if (umount(mount_dir) != 0) { + print_error("Can't unmout FS"); + goto failure; + } + if (mount_fs(mount_dir, backing_dir, 50) != 0) + goto failure; + + cmd_fd = open_commands_file(mount_dir); + if (cmd_fd < 0) + goto failure; + + /* Validate data again */ + for (i = 0; i < file_num; i++) { + struct test_file *file = &test.files[i]; + + if (i == corrupted_file_idx) { + uint8_t data[INCFS_DATA_FILE_BLOCK_SIZE]; + char *filename = + concat_file_name(mount_dir, file->name); + int res; + + res = read_test_file(data, INCFS_DATA_FILE_BLOCK_SIZE, + filename, 2); + free(filename); + if (res != -EBADMSG) { + ksft_print_msg("Hash violation missed2. %d\n", + res); + goto failure; + } + } else if (validate_test_file_content(mount_dir, file) < 0) + goto failure; + } + + /* Final unmount */ + close(cmd_fd); + cmd_fd = -1; + if (umount(mount_dir) != 0) { + print_error("Can't unmout FS"); + goto failure; + } + return TEST_SUCCESS; + +failure: + close(cmd_fd); + free(backing_dir); + umount(mount_dir); + return TEST_FAILURE; +} + +static int validate_logs(char *mount_dir, int log_fd, struct test_file *file) +{ + uint8_t data[INCFS_DATA_FILE_BLOCK_SIZE]; + struct incfs_pending_read_info prs[100] = {}; + int prs_size = ARRAY_SIZE(prs); + int block_cnt = 1 + (file->size - 1) / INCFS_DATA_FILE_BLOCK_SIZE; + int res; + int read_count; + int i; + char *filename = concat_file_name(mount_dir, file->name); + int fd; + + fd = open(filename, O_RDONLY); + free(filename); + if (fd <= 0) + return TEST_FAILURE; + + if (block_cnt > prs_size) + block_cnt = prs_size; + + for (i = 0; i < block_cnt; i++) { + res = pread(fd, data, sizeof(data), + INCFS_DATA_FILE_BLOCK_SIZE * i); + if (res <= 0) + goto failure; + } + + read_count = wait_for_pending_reads(log_fd, 0, prs, prs_size); + if (read_count < 0) { + ksft_print_msg("Error reading logged reads %s.\n", + strerror(-read_count)); + goto failure; + } + + if (read_count != block_cnt) { + ksft_print_msg("Bad log read count %s %d %d.\n", file->name, + read_count, block_cnt); + goto failure; + } + + for (i = 0; i < read_count; i++) { + struct incfs_pending_read_info *read = &prs[i]; + + if (!same_id(&read->file_id, &file->id)) { + ksft_print_msg("Bad log read ino %s\n", file->name); + goto failure; + } + + if (read->block_index != i) { + ksft_print_msg("Bad log read ino %s %d %d.\n", + file->name, read->block_index, i); + goto failure; + } + + if (i != 0) { + unsigned long psn = prs[i - 1].serial_number; + + if (read->serial_number != psn + 1) { + ksft_print_msg("Bad log read sn %s %d %d.\n", + file->name, read->serial_number, + psn); + goto failure; + } + } + + if (read->timestamp_us == 0) { + ksft_print_msg("Bad log read timestamp %s.\n", + file->name); + goto failure; + } + } + close(fd); + return TEST_SUCCESS; + +failure: + close(fd); + return TEST_FAILURE; +} + +static int read_log_test(char *mount_dir) +{ + struct test_files_set test = get_test_files_set(); + const int file_num = test.files_count; + int i = 0; + int cmd_fd = -1, log_fd = -1; + char *backing_dir; + + backing_dir = create_backing_dir(mount_dir); + if (!backing_dir) + goto failure; + + if (mount_fs_opt(mount_dir, backing_dir, "readahead=0") != 0) + goto failure; + + cmd_fd = open_commands_file(mount_dir); + if (cmd_fd < 0) + goto failure; + + log_fd = open_log_file(mount_dir); + if (cmd_fd < 0) + ksft_print_msg("Can't open log file.\n"); + + /* Write data. */ + for (i = 0; i < file_num; i++) { + struct test_file *file = &test.files[i]; + + if (emit_file(cmd_fd, NULL, file->name, &file->id, + file->size, NULL)) + goto failure; + + if (emit_test_file_data(mount_dir, file)) + goto failure; + } + + /* Validate data */ + for (i = 0; i < file_num; i++) { + struct test_file *file = &test.files[i]; + + if (validate_logs(mount_dir, log_fd, file)) + goto failure; + } + + /* Unmount and mount again, to see that logs work after remount. */ + close(cmd_fd); + close(log_fd); + cmd_fd = -1; + if (umount(mount_dir) != 0) { + print_error("Can't unmout FS"); + goto failure; + } + + if (mount_fs_opt(mount_dir, backing_dir, "readahead=0") != 0) + goto failure; + + cmd_fd = open_commands_file(mount_dir); + if (cmd_fd < 0) + goto failure; + + log_fd = open_log_file(mount_dir); + if (cmd_fd < 0) + ksft_print_msg("Can't open log file.\n"); + + /* Validate data again */ + for (i = 0; i < file_num; i++) { + struct test_file *file = &test.files[i]; + + if (validate_logs(mount_dir, log_fd, file)) + goto failure; + } + + /* Final unmount */ + close(cmd_fd); + close(log_fd); + free(backing_dir); + if (umount(mount_dir) != 0) { + print_error("Can't unmout FS"); + goto failure; + } + + return TEST_SUCCESS; + +failure: + close(cmd_fd); + close(log_fd); + free(backing_dir); + umount(mount_dir); + return TEST_FAILURE; +} + +static char *setup_mount_dir() +{ + struct stat st; + char *current_dir = getcwd(NULL, 0); + char *mount_dir = concat_file_name(current_dir, "incfs-mount-dir"); + + free(current_dir); + if (stat(mount_dir, &st) == 0) { + if (S_ISDIR(st.st_mode)) + return mount_dir; + + ksft_print_msg("%s is a file, not a dir.\n", mount_dir); + return NULL; + } + + if (mkdir(mount_dir, 0777)) { + print_error("Can't create mount dir."); + return NULL; + } + + return mount_dir; +} + +int main(int argc, char *argv[]) +{ + char *mount_dir = NULL; + int fails = 0; + int i; + int fd, count; + + // Seed randomness pool for testing on QEMU + // NOTE - this abuses the concept of randomness - do *not* ever do this + // on a machine for production use - the device will think it has good + // randomness when it does not. + fd = open("/dev/urandom", O_WRONLY); + count = 4096; + for (int i = 0; i < 128; ++i) + ioctl(fd, RNDADDTOENTCNT, &count); + close(fd); + + ksft_print_header(); + + if (geteuid() != 0) + ksft_print_msg("Not a root, might fail to mount.\n"); + + mount_dir = setup_mount_dir(); + if (mount_dir == NULL) + ksft_exit_fail_msg("Can't create a mount dir\n"); + +#define MAKE_TEST(test) \ + { \ + test, #test \ + } + struct { + int (*pfunc)(char *dir); + const char *name; + } cases[] = { + MAKE_TEST(basic_file_ops_test), + MAKE_TEST(cant_touch_index_test), + MAKE_TEST(dynamic_files_and_data_test), + MAKE_TEST(concurrent_reads_and_writes_test), + MAKE_TEST(attribute_test), + MAKE_TEST(work_after_remount_test), + MAKE_TEST(child_procs_waiting_for_data_test), + MAKE_TEST(multiple_providers_test), + MAKE_TEST(signature_test), + MAKE_TEST(hash_tree_test), + MAKE_TEST(read_log_test), + }; +#undef MAKE_TEST + + /* Bring back for kernel 5.x */ + /* ksft_set_plan(ARRAY_SIZE(cases)); */ + + for (i = 0; i < ARRAY_SIZE(cases); ++i) { + ksft_print_msg("Running %s\n", cases[i].name); + if (cases[i].pfunc(mount_dir) == TEST_SUCCESS) + ksft_test_result_pass("%s\n", cases[i].name); + else { + ksft_test_result_fail("%s\n", cases[i].name); + fails++; + } + } + + umount2(mount_dir, MNT_FORCE); + rmdir(mount_dir); + + if (fails > 0) + ksft_exit_pass(); + else + ksft_exit_pass(); + return 0; +} diff --git a/tools/testing/selftests/filesystems/incfs/utils.c b/tools/testing/selftests/filesystems/incfs/utils.c new file mode 100644 index 0000000000000000000000000000000000000000..08b8452ad0bcdbbe5982d1780b6f1dd71ef68396 --- /dev/null +++ b/tools/testing/selftests/filesystems/incfs/utils.c @@ -0,0 +1,377 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2018 Google LLC + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" + +int mount_fs(char *mount_dir, char *backing_dir, int read_timeout_ms) +{ + static const char fs_name[] = INCFS_NAME; + char mount_options[512]; + int result; + + snprintf(mount_options, ARRAY_SIZE(mount_options), + "read_timeout_ms=%u", + read_timeout_ms); + + result = mount(backing_dir, mount_dir, fs_name, 0, mount_options); + if (result != 0) + perror("Error mounting fs."); + return result; +} + +int mount_fs_opt(char *mount_dir, char *backing_dir, char *opt) +{ + static const char fs_name[] = INCFS_NAME; + int result; + + result = mount(backing_dir, mount_dir, fs_name, 0, opt); + if (result != 0) + perror("Error mounting fs."); + return result; +} + +int unlink_node(int fd, int parent_ino, char *filename) +{ + return 0; +} + + +static EVP_PKEY *deserialize_private_key(const char *pem_key) +{ + BIO *bio = NULL; + EVP_PKEY *pkey = NULL; + int len = strlen(pem_key); + + bio = BIO_new_mem_buf(pem_key, len); + if (!bio) + return NULL; + + pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); + BIO_free(bio); + return pkey; +} + +static X509 *deserialize_cert(const char *pem_cert) +{ + BIO *bio = NULL; + X509 *cert = NULL; + int len = strlen(pem_cert); + + bio = BIO_new_mem_buf(pem_cert, len); + if (!bio) + return NULL; + + cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); + BIO_free(bio); + return cert; +} + +bool sign_pkcs7(const void *data_to_sign, size_t data_size, + char *pkey_pem, char *cert_pem, + void **sig_ret, size_t *sig_size_ret) +{ + /* + * PKCS#7 signing flags: + * + * - PKCS7_BINARY signing binary data, so skip MIME translation + * + * - PKCS7_NOATTR omit extra authenticated attributes, such as + * SMIMECapabilities + * + * - PKCS7_PARTIAL PKCS7_sign() creates a handle only, then + * PKCS7_sign_add_signer() can add a signer later. + * This is necessary to change the message digest + * algorithm from the default of SHA-1. Requires + * OpenSSL 1.0.0 or later. + */ + int pkcs7_flags = PKCS7_BINARY | PKCS7_NOATTR | PKCS7_PARTIAL; + void *sig; + size_t sig_size; + BIO *bio = NULL; + PKCS7 *p7 = NULL; + EVP_PKEY *pkey = NULL; + X509 *cert = NULL; + bool ok = false; + + const EVP_MD *md = EVP_sha256(); + + pkey = deserialize_private_key(pkey_pem); + if (!pkey) { + printf("deserialize_private_key failed\n"); + goto out; + } + + cert = deserialize_cert(cert_pem); + if (!cert) { + printf("deserialize_cert failed\n"); + goto out; + } + + bio = BIO_new_mem_buf(data_to_sign, data_size); + if (!bio) + goto out; + + p7 = PKCS7_sign(NULL, NULL, NULL, bio, pkcs7_flags); + if (!p7) { + printf("failed to initialize PKCS#7 signature object\n"); + goto out; + } + + if (!PKCS7_sign_add_signer(p7, cert, pkey, md, pkcs7_flags)) { + printf("failed to add signer to PKCS#7 signature object\n"); + goto out; + } + + if (PKCS7_final(p7, bio, pkcs7_flags) != 1) { + printf("failed to finalize PKCS#7 signature\n"); + goto out; + } + + BIO_free(bio); + bio = BIO_new(BIO_s_mem()); + if (!bio) { + printf("out of memory\n"); + goto out; + } + + if (i2d_PKCS7_bio(bio, p7) != 1) { + printf("failed to DER-encode PKCS#7 signature object\n"); + goto out; + } + + sig_size = BIO_get_mem_data(bio, &sig); + *sig_ret = malloc(sig_size); + memcpy(*sig_ret, sig, sig_size); + *sig_size_ret = sig_size; + ok = true; +out: + PKCS7_free(p7); + BIO_free(bio); + return ok; +} + +int crypto_emit_file(int fd, char *dir, char *filename, incfs_uuid_t *id_out, + size_t size, const char *root_hash, char *sig, size_t sig_size, + char *add_data) +{ + int mode = __S_IFREG | 0555; + struct incfs_file_signature_info sig_info = { + .hash_tree_alg = root_hash + ? INCFS_HASH_TREE_SHA256 + : 0, + .root_hash = ptr_to_u64(root_hash), + .additional_data = ptr_to_u64(add_data), + .additional_data_size = strlen(add_data), + .signature = ptr_to_u64(sig), + .signature_size = sig_size, + }; + + struct incfs_new_file_args args = { + .size = size, + .mode = mode, + .file_name = ptr_to_u64(filename), + .directory_path = ptr_to_u64(dir), + .signature_info = ptr_to_u64(&sig_info), + .file_attr = 0, + .file_attr_len = 0 + }; + + md5(filename, strlen(filename), (char *)args.file_id.bytes); + + if (ioctl(fd, INCFS_IOC_CREATE_FILE, &args) != 0) + return -errno; + + *id_out = args.file_id; + return 0; +} + + +int emit_file(int fd, char *dir, char *filename, incfs_uuid_t *id_out, + size_t size, char *attr) +{ + int mode = __S_IFREG | 0555; + struct incfs_file_signature_info sig_info = { + .hash_tree_alg = 0, + .root_hash = ptr_to_u64(NULL) + }; + struct incfs_new_file_args args = { + .size = size, + .mode = mode, + .file_name = ptr_to_u64(filename), + .directory_path = ptr_to_u64(dir), + .signature_info = ptr_to_u64(&sig_info), + .file_attr = ptr_to_u64(attr), + .file_attr_len = attr ? strlen(attr) : 0 + }; + + md5(filename, strlen(filename), (char *)args.file_id.bytes); + + if (ioctl(fd, INCFS_IOC_CREATE_FILE, &args) != 0) + return -errno; + + *id_out = args.file_id; + return 0; +} + +int get_file_bmap(int cmd_fd, int ino, unsigned char *buf, int buf_size) +{ + return 0; +} + +int get_file_signature(int fd, unsigned char *buf, int buf_size) +{ + struct incfs_get_file_sig_args args = { + .file_signature = ptr_to_u64(buf), + .file_signature_buf_size = buf_size + }; + + if (ioctl(fd, INCFS_IOC_READ_FILE_SIGNATURE, &args) == 0) + return args.file_signature_len_out; + return -errno; +} + +loff_t get_file_size(char *name) +{ + struct stat st; + + if (stat(name, &st) == 0) + return st.st_size; + return -ENOENT; +} + +int open_commands_file(char *mount_dir) +{ + char cmd_file[255]; + int cmd_fd; + + snprintf(cmd_file, ARRAY_SIZE(cmd_file), + "%s/%s", mount_dir, INCFS_PENDING_READS_FILENAME); + cmd_fd = open(cmd_file, O_RDONLY); + + if (cmd_fd < 0) + perror("Can't open commands file"); + return cmd_fd; +} + +int open_log_file(char *mount_dir) +{ + char cmd_file[255]; + int cmd_fd; + + snprintf(cmd_file, ARRAY_SIZE(cmd_file), "%s/.log", mount_dir); + cmd_fd = open(cmd_file, O_RDWR); + if (cmd_fd < 0) + perror("Can't open log file"); + return cmd_fd; +} + +int wait_for_pending_reads(int fd, int timeout_ms, + struct incfs_pending_read_info *prs, int prs_count) +{ + ssize_t read_res = 0; + + if (timeout_ms > 0) { + int poll_res = 0; + struct pollfd pollfd = { + .fd = fd, + .events = POLLIN + }; + + poll_res = poll(&pollfd, 1, timeout_ms); + if (poll_res < 0) + return -errno; + if (poll_res == 0) + return 0; + if (!(pollfd.revents | POLLIN)) + return 0; + } + + read_res = read(fd, prs, prs_count * sizeof(*prs)); + if (read_res < 0) + return -errno; + + return read_res / sizeof(*prs); +} + +char *concat_file_name(const char *dir, char *file) +{ + char full_name[FILENAME_MAX] = ""; + + if (snprintf(full_name, ARRAY_SIZE(full_name), "%s/%s", dir, file) < 0) + return NULL; + return strdup(full_name); +} + +int delete_dir_tree(const char *dir_path) +{ + DIR *dir = NULL; + struct dirent *dp; + int result = 0; + + dir = opendir(dir_path); + if (!dir) { + result = -errno; + goto out; + } + + while ((dp = readdir(dir))) { + char *full_path; + + if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) + continue; + + full_path = concat_file_name(dir_path, dp->d_name); + if (dp->d_type == DT_DIR) + result = delete_dir_tree(full_path); + else + result = unlink(full_path); + free(full_path); + if (result) + goto out; + } + +out: + if (dir) + closedir(dir); + if (!result) + rmdir(dir_path); + return result; +} + +void sha256(char *data, size_t dsize, char *hash) +{ + SHA256_CTX ctx; + + SHA256_Init(&ctx); + SHA256_Update(&ctx, data, dsize); + SHA256_Final((unsigned char *)hash, &ctx); +} + +void md5(char *data, size_t dsize, char *hash) +{ + MD5_CTX ctx; + + MD5_Init(&ctx); + MD5_Update(&ctx, data, dsize); + MD5_Final((unsigned char *)hash, &ctx); +} diff --git a/tools/testing/selftests/filesystems/incfs/utils.h b/tools/testing/selftests/filesystems/incfs/utils.h new file mode 100644 index 0000000000000000000000000000000000000000..9c9ba3c5f70a54b69cfffebba7692d34395e9d1f --- /dev/null +++ b/tools/testing/selftests/filesystems/incfs/utils.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2019 Google LLC + */ +#include +#include + +#include "../../include/uapi/linux/incrementalfs.h" + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) + +#ifdef __LP64__ +#define ptr_to_u64(p) ((__u64)p) +#else +#define ptr_to_u64(p) ((__u64)(__u32)p) +#endif + +#define SHA256_DIGEST_SIZE 32 + +int mount_fs(char *mount_dir, char *backing_dir, int read_timeout_ms); + +int mount_fs_opt(char *mount_dir, char *backing_dir, char *opt); + +int get_file_bmap(int cmd_fd, int ino, unsigned char *buf, int buf_size); + +int get_file_signature(int fd, unsigned char *buf, int buf_size); + +int emit_node(int fd, char *filename, int *ino_out, int parent_ino, + size_t size, mode_t mode, char *attr); + +int emit_file(int fd, char *dir, char *filename, incfs_uuid_t *id_out, + size_t size, char *attr); + +int crypto_emit_file(int fd, char *dir, char *filename, incfs_uuid_t *id_out, + size_t size, const char *root_hash, char *sig, size_t sig_size, + char *add_data); + +int unlink_node(int fd, int parent_ino, char *filename); + +loff_t get_file_size(char *name); + +int open_commands_file(char *mount_dir); + +int open_log_file(char *mount_dir); + +int wait_for_pending_reads(int fd, int timeout_ms, + struct incfs_pending_read_info *prs, int prs_count); + +char *concat_file_name(const char *dir, char *file); + +void sha256(char *data, size_t dsize, char *hash); + +void md5(char *data, size_t dsize, char *hash); + +bool sign_pkcs7(const void *data_to_sign, size_t data_size, + char *pkey_pem, char *cert_pem, + void **sig_ret, size_t *sig_size_ret); + +int delete_dir_tree(const char *path); diff --git a/tools/testing/selftests/firmware/fw_lib.sh b/tools/testing/selftests/firmware/fw_lib.sh index 1cbb12e284a689329feb78eeddfa8896cc21f3a5..8a853ace55a24e3710731d3af1ed9e74f0a492b5 100755 --- a/tools/testing/selftests/firmware/fw_lib.sh +++ b/tools/testing/selftests/firmware/fw_lib.sh @@ -28,6 +28,12 @@ test_modprobe() check_mods() { + local uid=$(id -u) + if [ $uid -ne 0 ]; then + echo "skip all tests: must be run as root" >&2 + exit $ksft_skip + fi + trap "test_modprobe" EXIT if [ ! -d $DIR ]; then modprobe test_firmware diff --git a/tools/testing/selftests/ipc/msgque.c b/tools/testing/selftests/ipc/msgque.c index dac927e823363b8668ab535f7fcdc693e4b8d831..4c156aeab6b80c011ab6ad503c9521bc10341c7b 100644 --- a/tools/testing/selftests/ipc/msgque.c +++ b/tools/testing/selftests/ipc/msgque.c @@ -1,9 +1,10 @@ // SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE #include #include #include #include -#include +#include #include #include "../kselftest.h" @@ -73,7 +74,7 @@ int restore_queue(struct msgque_data *msgque) return 0; destroy: - if (msgctl(id, IPC_RMID, 0)) + if (msgctl(id, IPC_RMID, NULL)) printf("Failed to destroy queue: %d\n", -errno); return ret; } @@ -120,7 +121,7 @@ int check_and_destroy_queue(struct msgque_data *msgque) ret = 0; err: - if (msgctl(msgque->msq_id, IPC_RMID, 0)) { + if (msgctl(msgque->msq_id, IPC_RMID, NULL)) { printf("Failed to destroy queue: %d\n", -errno); return -errno; } @@ -129,7 +130,7 @@ int check_and_destroy_queue(struct msgque_data *msgque) int dump_queue(struct msgque_data *msgque) { - struct msqid64_ds ds; + struct msqid_ds ds; int kern_id; int i, ret; @@ -245,7 +246,7 @@ int main(int argc, char **argv) return ksft_exit_pass(); err_destroy: - if (msgctl(msgque.msq_id, IPC_RMID, 0)) { + if (msgctl(msgque.msq_id, IPC_RMID, NULL)) { printf("Failed to destroy queue: %d\n", -errno); return ksft_exit_fail(); } diff --git a/tools/testing/selftests/lib.mk b/tools/testing/selftests/lib.mk index 8b0f16409ed7eb534714b6ca9d92a2721bef52da..0ef203ec59fdcc9703343adb6961654fafb9f65b 100644 --- a/tools/testing/selftests/lib.mk +++ b/tools/testing/selftests/lib.mk @@ -85,17 +85,20 @@ else $(call RUN_TESTS, $(TEST_GEN_PROGS) $(TEST_CUSTOM_PROGS) $(TEST_PROGS)) endif +define INSTALL_SINGLE_RULE + $(if $(INSTALL_LIST),@mkdir -p $(INSTALL_PATH)) + $(if $(INSTALL_LIST),@echo rsync -a $(INSTALL_LIST) $(INSTALL_PATH)/) + $(if $(INSTALL_LIST),@rsync -a $(INSTALL_LIST) $(INSTALL_PATH)/) +endef + define INSTALL_RULE - @if [ "X$(TEST_PROGS)$(TEST_PROGS_EXTENDED)$(TEST_FILES)" != "X" ]; then \ - mkdir -p ${INSTALL_PATH}; \ - echo "rsync -a $(TEST_PROGS) $(TEST_PROGS_EXTENDED) $(TEST_FILES) $(INSTALL_PATH)/"; \ - rsync -a $(TEST_PROGS) $(TEST_PROGS_EXTENDED) $(TEST_FILES) $(INSTALL_PATH)/; \ - fi - @if [ "X$(TEST_GEN_PROGS)$(TEST_CUSTOM_PROGS)$(TEST_GEN_PROGS_EXTENDED)$(TEST_GEN_FILES)" != "X" ]; then \ - mkdir -p ${INSTALL_PATH}; \ - echo "rsync -a $(TEST_GEN_PROGS) $(TEST_CUSTOM_PROGS) $(TEST_GEN_PROGS_EXTENDED) $(TEST_GEN_FILES) $(INSTALL_PATH)/"; \ - rsync -a $(TEST_GEN_PROGS) $(TEST_CUSTOM_PROGS) $(TEST_GEN_PROGS_EXTENDED) $(TEST_GEN_FILES) $(INSTALL_PATH)/; \ - fi + $(eval INSTALL_LIST = $(TEST_PROGS)) $(INSTALL_SINGLE_RULE) + $(eval INSTALL_LIST = $(TEST_PROGS_EXTENDED)) $(INSTALL_SINGLE_RULE) + $(eval INSTALL_LIST = $(TEST_FILES)) $(INSTALL_SINGLE_RULE) + $(eval INSTALL_LIST = $(TEST_GEN_PROGS)) $(INSTALL_SINGLE_RULE) + $(eval INSTALL_LIST = $(TEST_CUSTOM_PROGS)) $(INSTALL_SINGLE_RULE) + $(eval INSTALL_LIST = $(TEST_GEN_PROGS_EXTENDED)) $(INSTALL_SINGLE_RULE) + $(eval INSTALL_LIST = $(TEST_GEN_FILES)) $(INSTALL_SINGLE_RULE) endef install: all diff --git a/tools/testing/selftests/net/fib_tests.sh b/tools/testing/selftests/net/fib_tests.sh index c0885fb657670735d642c4230f1da26014121e4e..7d1a7c0dc56aae50a007733f537d9de5979e8c3d 100755 --- a/tools/testing/selftests/net/fib_tests.sh +++ b/tools/testing/selftests/net/fib_tests.sh @@ -848,6 +848,12 @@ ipv6_rt_replace_mpath() check_route6 "2001:db8:104::/64 via 2001:db8:101::3 dev veth1 metric 1024" log_test $? 0 "Multipath with single path via multipath attribute" + # multipath with dev-only + add_initial_route6 "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2" + run_cmd "$IP -6 ro replace 2001:db8:104::/64 dev veth1" + check_route6 "2001:db8:104::/64 dev veth1 metric 1024" + log_test $? 0 "Multipath with dev-only" + # route replace fails - invalid nexthop 1 add_initial_route6 "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2" run_cmd "$IP -6 ro replace 2001:db8:104::/64 nexthop via 2001:db8:111::3 nexthop via 2001:db8:103::3" diff --git a/tools/testing/selftests/net/forwarding/mirror_gre.sh b/tools/testing/selftests/net/forwarding/mirror_gre.sh index e6fd7a18c655ff32e4f4d5ffd033acd866a70c96..0266443601bc0bb90504132a1c1b57aced95089b 100755 --- a/tools/testing/selftests/net/forwarding/mirror_gre.sh +++ b/tools/testing/selftests/net/forwarding/mirror_gre.sh @@ -63,22 +63,23 @@ test_span_gre_mac() { local tundev=$1; shift local direction=$1; shift - local prot=$1; shift local what=$1; shift - local swp3mac=$(mac_get $swp3) - local h3mac=$(mac_get $h3) + case "$direction" in + ingress) local src_mac=$(mac_get $h1); local dst_mac=$(mac_get $h2) + ;; + egress) local src_mac=$(mac_get $h2); local dst_mac=$(mac_get $h1) + ;; + esac RET=0 mirror_install $swp1 $direction $tundev "matchall $tcflags" - tc filter add dev $h3 ingress pref 77 prot $prot \ - flower ip_proto 0x2f src_mac $swp3mac dst_mac $h3mac \ - action pass + icmp_capture_install h3-${tundev} "src_mac $src_mac dst_mac $dst_mac" - mirror_test v$h1 192.0.2.1 192.0.2.2 $h3 77 10 + mirror_test v$h1 192.0.2.1 192.0.2.2 h3-${tundev} 100 10 - tc filter del dev $h3 ingress pref 77 + icmp_capture_uninstall h3-${tundev} mirror_uninstall $swp1 $direction log_test "$direction $what: envelope MAC ($tcflags)" @@ -120,14 +121,14 @@ test_ip6gretap() test_gretap_mac() { - test_span_gre_mac gt4 ingress ip "mirror to gretap" - test_span_gre_mac gt4 egress ip "mirror to gretap" + test_span_gre_mac gt4 ingress "mirror to gretap" + test_span_gre_mac gt4 egress "mirror to gretap" } test_ip6gretap_mac() { - test_span_gre_mac gt6 ingress ipv6 "mirror to ip6gretap" - test_span_gre_mac gt6 egress ipv6 "mirror to ip6gretap" + test_span_gre_mac gt6 ingress "mirror to ip6gretap" + test_span_gre_mac gt6 egress "mirror to ip6gretap" } test_all() diff --git a/tools/testing/selftests/rseq/settings b/tools/testing/selftests/rseq/settings new file mode 100644 index 0000000000000000000000000000000000000000..e7b9417537fbc4626153b72e8f295ab4594c844b --- /dev/null +++ b/tools/testing/selftests/rseq/settings @@ -0,0 +1 @@ +timeout=0 diff --git a/tools/testing/selftests/size/get_size.c b/tools/testing/selftests/size/get_size.c index d4b59ab979a0939f71684c4fcbfa09d8be7c907e..f55943b6d1e2ab9796c844438e2e964c8bd6724a 100644 --- a/tools/testing/selftests/size/get_size.c +++ b/tools/testing/selftests/size/get_size.c @@ -12,23 +12,35 @@ * own execution. It also attempts to have as few dependencies * on kernel features as possible. * - * It should be statically linked, with startup libs avoided. - * It uses no library calls, and only the following 3 syscalls: + * It should be statically linked, with startup libs avoided. It uses + * no library calls except the syscall() function for the following 3 + * syscalls: * sysinfo(), write(), and _exit() * * For output, it avoids printf (which in some C libraries * has large external dependencies) by implementing it's own * number output and print routines, and using __builtin_strlen() + * + * The test may crash if any of the above syscalls fails because in some + * libc implementations (e.g. the GNU C Library) errno is saved in + * thread-local storage, which does not get initialized due to avoiding + * startup libs. */ #include #include +#include #define STDOUT_FILENO 1 static int print(const char *s) { - return write(STDOUT_FILENO, s, __builtin_strlen(s)); + size_t len = 0; + + while (s[len] != '\0') + len++; + + return syscall(SYS_write, STDOUT_FILENO, s, len); } static inline char *num_to_str(unsigned long num, char *buf, int len) @@ -80,12 +92,12 @@ void _start(void) print("TAP version 13\n"); print("# Testing system size.\n"); - ccode = sysinfo(&info); + ccode = syscall(SYS_sysinfo, &info); if (ccode < 0) { print("not ok 1"); print(test_name); print(" ---\n reason: \"could not get sysinfo\"\n ...\n"); - _exit(ccode); + syscall(SYS_exit, ccode); } print("ok 1"); print(test_name); @@ -101,5 +113,5 @@ void _start(void) print(" ...\n"); print("1..1\n"); - _exit(0); + syscall(SYS_exit, 0); } diff --git a/tools/usb/usbip/src/usbip_network.c b/tools/usb/usbip/src/usbip_network.c index 8ffcd47d96385edc404dbfaca9fe50b11e6de1a3..902f55208e2398f6dae835dd89e345797baf34ea 100644 --- a/tools/usb/usbip/src/usbip_network.c +++ b/tools/usb/usbip/src/usbip_network.c @@ -62,39 +62,39 @@ void usbip_setup_port_number(char *arg) info("using port %d (\"%s\")", usbip_port, usbip_port_string); } -void usbip_net_pack_uint32_t(int pack, uint32_t *num) +uint32_t usbip_net_pack_uint32_t(int pack, uint32_t num) { uint32_t i; if (pack) - i = htonl(*num); + i = htonl(num); else - i = ntohl(*num); + i = ntohl(num); - *num = i; + return i; } -void usbip_net_pack_uint16_t(int pack, uint16_t *num) +uint16_t usbip_net_pack_uint16_t(int pack, uint16_t num) { uint16_t i; if (pack) - i = htons(*num); + i = htons(num); else - i = ntohs(*num); + i = ntohs(num); - *num = i; + return i; } void usbip_net_pack_usb_device(int pack, struct usbip_usb_device *udev) { - usbip_net_pack_uint32_t(pack, &udev->busnum); - usbip_net_pack_uint32_t(pack, &udev->devnum); - usbip_net_pack_uint32_t(pack, &udev->speed); + udev->busnum = usbip_net_pack_uint32_t(pack, udev->busnum); + udev->devnum = usbip_net_pack_uint32_t(pack, udev->devnum); + udev->speed = usbip_net_pack_uint32_t(pack, udev->speed); - usbip_net_pack_uint16_t(pack, &udev->idVendor); - usbip_net_pack_uint16_t(pack, &udev->idProduct); - usbip_net_pack_uint16_t(pack, &udev->bcdDevice); + udev->idVendor = usbip_net_pack_uint16_t(pack, udev->idVendor); + udev->idProduct = usbip_net_pack_uint16_t(pack, udev->idProduct); + udev->bcdDevice = usbip_net_pack_uint16_t(pack, udev->bcdDevice); } void usbip_net_pack_usb_interface(int pack __attribute__((unused)), @@ -141,6 +141,14 @@ ssize_t usbip_net_send(int sockfd, void *buff, size_t bufflen) return usbip_net_xmit(sockfd, buff, bufflen, 1); } +static inline void usbip_net_pack_op_common(int pack, + struct op_common *op_common) +{ + op_common->version = usbip_net_pack_uint16_t(pack, op_common->version); + op_common->code = usbip_net_pack_uint16_t(pack, op_common->code); + op_common->status = usbip_net_pack_uint32_t(pack, op_common->status); +} + int usbip_net_send_op_common(int sockfd, uint32_t code, uint32_t status) { struct op_common op_common; @@ -152,7 +160,7 @@ int usbip_net_send_op_common(int sockfd, uint32_t code, uint32_t status) op_common.code = code; op_common.status = status; - PACK_OP_COMMON(1, &op_common); + usbip_net_pack_op_common(1, &op_common); rc = usbip_net_send(sockfd, &op_common, sizeof(op_common)); if (rc < 0) { @@ -176,7 +184,7 @@ int usbip_net_recv_op_common(int sockfd, uint16_t *code, int *status) goto err; } - PACK_OP_COMMON(0, &op_common); + usbip_net_pack_op_common(0, &op_common); if (op_common.version != USBIP_VERSION) { err("USBIP Kernel and tool version mismatch: %d %d:", diff --git a/tools/usb/usbip/src/usbip_network.h b/tools/usb/usbip/src/usbip_network.h index 555215eae43e9ee9fd75d789cdb2250d9b62086a..83b4c5344f7217a941cc0c850f9796fcd63a220e 100644 --- a/tools/usb/usbip/src/usbip_network.h +++ b/tools/usb/usbip/src/usbip_network.h @@ -32,12 +32,6 @@ struct op_common { } __attribute__((packed)); -#define PACK_OP_COMMON(pack, op_common) do {\ - usbip_net_pack_uint16_t(pack, &(op_common)->version);\ - usbip_net_pack_uint16_t(pack, &(op_common)->code);\ - usbip_net_pack_uint32_t(pack, &(op_common)->status);\ -} while (0) - /* ---------------------------------------------------------------------- */ /* Dummy Code */ #define OP_UNSPEC 0x00 @@ -163,11 +157,11 @@ struct op_devlist_reply_extra { } while (0) #define PACK_OP_DEVLIST_REPLY(pack, reply) do {\ - usbip_net_pack_uint32_t(pack, &(reply)->ndev);\ + (reply)->ndev = usbip_net_pack_uint32_t(pack, (reply)->ndev);\ } while (0) -void usbip_net_pack_uint32_t(int pack, uint32_t *num); -void usbip_net_pack_uint16_t(int pack, uint16_t *num); +uint32_t usbip_net_pack_uint32_t(int pack, uint32_t num); +uint16_t usbip_net_pack_uint16_t(int pack, uint16_t num); void usbip_net_pack_usb_device(int pack, struct usbip_usb_device *udev); void usbip_net_pack_usb_interface(int pack, struct usbip_usb_interface *uinf); diff --git a/virt/kvm/arm/aarch32.c b/virt/kvm/arm/aarch32.c index 6880236974b85469e3416551ab6aa04bef1484f7..18d6d5124397893dd978ab88a6fc7f4c3596fbdd 100644 --- a/virt/kvm/arm/aarch32.c +++ b/virt/kvm/arm/aarch32.c @@ -21,10 +21,15 @@ * along with this program. If not, see . */ +#include #include #include #include +#define DFSR_FSC_EXTABT_LPAE 0x10 +#define DFSR_FSC_EXTABT_nLPAE 0x08 +#define DFSR_LPAE BIT(9) + /* * Table taken from ARMv8 ARM DDI0487B-B, table G1-10. */ @@ -39,25 +44,115 @@ static const u8 return_offsets[8][2] = { [7] = { 4, 4 }, /* FIQ, unused */ }; +/* + * When an exception is taken, most CPSR fields are left unchanged in the + * handler. However, some are explicitly overridden (e.g. M[4:0]). + * + * The SPSR/SPSR_ELx layouts differ, and the below is intended to work with + * either format. Note: SPSR.J bit doesn't exist in SPSR_ELx, but this bit was + * obsoleted by the ARMv7 virtualization extensions and is RES0. + * + * For the SPSR layout seen from AArch32, see: + * - ARM DDI 0406C.d, page B1-1148 + * - ARM DDI 0487E.a, page G8-6264 + * + * For the SPSR_ELx layout for AArch32 seen from AArch64, see: + * - ARM DDI 0487E.a, page C5-426 + * + * Here we manipulate the fields in order of the AArch32 SPSR_ELx layout, from + * MSB to LSB. + */ +static unsigned long get_except32_cpsr(struct kvm_vcpu *vcpu, u32 mode) +{ + u32 sctlr = vcpu_cp15(vcpu, c1_SCTLR); + unsigned long old, new; + + old = *vcpu_cpsr(vcpu); + new = 0; + + new |= (old & PSR_AA32_N_BIT); + new |= (old & PSR_AA32_Z_BIT); + new |= (old & PSR_AA32_C_BIT); + new |= (old & PSR_AA32_V_BIT); + new |= (old & PSR_AA32_Q_BIT); + + // CPSR.IT[7:0] are set to zero upon any exception + // See ARM DDI 0487E.a, section G1.12.3 + // See ARM DDI 0406C.d, section B1.8.3 + + new |= (old & PSR_AA32_DIT_BIT); + + // CPSR.SSBS is set to SCTLR.DSSBS upon any exception + // See ARM DDI 0487E.a, page G8-6244 + if (sctlr & BIT(31)) + new |= PSR_AA32_SSBS_BIT; + + // CPSR.PAN is unchanged unless SCTLR.SPAN == 0b0 + // SCTLR.SPAN is RES1 when ARMv8.1-PAN is not implemented + // See ARM DDI 0487E.a, page G8-6246 + new |= (old & PSR_AA32_PAN_BIT); + if (!(sctlr & BIT(23))) + new |= PSR_AA32_PAN_BIT; + + // SS does not exist in AArch32, so ignore + + // CPSR.IL is set to zero upon any exception + // See ARM DDI 0487E.a, page G1-5527 + + new |= (old & PSR_AA32_GE_MASK); + + // CPSR.IT[7:0] are set to zero upon any exception + // See prior comment above + + // CPSR.E is set to SCTLR.EE upon any exception + // See ARM DDI 0487E.a, page G8-6245 + // See ARM DDI 0406C.d, page B4-1701 + if (sctlr & BIT(25)) + new |= PSR_AA32_E_BIT; + + // CPSR.A is unchanged upon an exception to Undefined, Supervisor + // CPSR.A is set upon an exception to other modes + // See ARM DDI 0487E.a, pages G1-5515 to G1-5516 + // See ARM DDI 0406C.d, page B1-1182 + new |= (old & PSR_AA32_A_BIT); + if (mode != PSR_AA32_MODE_UND && mode != PSR_AA32_MODE_SVC) + new |= PSR_AA32_A_BIT; + + // CPSR.I is set upon any exception + // See ARM DDI 0487E.a, pages G1-5515 to G1-5516 + // See ARM DDI 0406C.d, page B1-1182 + new |= PSR_AA32_I_BIT; + + // CPSR.F is set upon an exception to FIQ + // CPSR.F is unchanged upon an exception to other modes + // See ARM DDI 0487E.a, pages G1-5515 to G1-5516 + // See ARM DDI 0406C.d, page B1-1182 + new |= (old & PSR_AA32_F_BIT); + if (mode == PSR_AA32_MODE_FIQ) + new |= PSR_AA32_F_BIT; + + // CPSR.T is set to SCTLR.TE upon any exception + // See ARM DDI 0487E.a, page G8-5514 + // See ARM DDI 0406C.d, page B1-1181 + if (sctlr & BIT(30)) + new |= PSR_AA32_T_BIT; + + new |= mode; + + return new; +} + static void prepare_fault32(struct kvm_vcpu *vcpu, u32 mode, u32 vect_offset) { - unsigned long cpsr; - unsigned long new_spsr_value = *vcpu_cpsr(vcpu); - bool is_thumb = (new_spsr_value & PSR_AA32_T_BIT); + unsigned long spsr = *vcpu_cpsr(vcpu); + bool is_thumb = (spsr & PSR_AA32_T_BIT); u32 return_offset = return_offsets[vect_offset >> 2][is_thumb]; u32 sctlr = vcpu_cp15(vcpu, c1_SCTLR); - cpsr = mode | PSR_AA32_I_BIT; - - if (sctlr & (1 << 30)) - cpsr |= PSR_AA32_T_BIT; - if (sctlr & (1 << 25)) - cpsr |= PSR_AA32_E_BIT; - - *vcpu_cpsr(vcpu) = cpsr; + *vcpu_cpsr(vcpu) = get_except32_cpsr(vcpu, mode); /* Note: These now point to the banked copies */ - vcpu_write_spsr(vcpu, new_spsr_value); + vcpu_write_spsr(vcpu, host_spsr_to_spsr32(spsr)); *vcpu_reg32(vcpu, 14) = *vcpu_pc(vcpu) + return_offset; /* Branch to exception vector */ @@ -95,16 +190,18 @@ static void inject_abt32(struct kvm_vcpu *vcpu, bool is_pabt, fsr = &vcpu_cp15(vcpu, c5_DFSR); } - prepare_fault32(vcpu, PSR_AA32_MODE_ABT | PSR_AA32_A_BIT, vect_offset); + prepare_fault32(vcpu, PSR_AA32_MODE_ABT, vect_offset); *far = addr; /* Give the guest an IMPLEMENTATION DEFINED exception */ is_lpae = (vcpu_cp15(vcpu, c2_TTBCR) >> 31); - if (is_lpae) - *fsr = 1 << 9 | 0x34; - else - *fsr = 0x14; + if (is_lpae) { + *fsr = DFSR_LPAE | DFSR_FSC_EXTABT_LPAE; + } else { + /* no need to shuffle FS[4] into DFSR[10] as its 0 */ + *fsr = DFSR_FSC_EXTABT_nLPAE; + } } void kvm_inject_dabt32(struct kvm_vcpu *vcpu, unsigned long addr) diff --git a/virt/kvm/arm/mmio.c b/virt/kvm/arm/mmio.c index 3caee91bca08907ad7aa18a822e1cb40525ae279..878e0edb2e1b7fe84ba11c33568e7a61e6f259ad 100644 --- a/virt/kvm/arm/mmio.c +++ b/virt/kvm/arm/mmio.c @@ -117,6 +117,9 @@ int kvm_handle_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run) data = (data ^ mask) - mask; } + if (!vcpu->arch.mmio_decode.sixty_four) + data = data & 0xffffffff; + trace_kvm_mmio(KVM_TRACE_MMIO_READ, len, run->mmio.phys_addr, &data); data = vcpu_data_host_to_guest(vcpu, data, len); @@ -137,6 +140,7 @@ static int decode_hsr(struct kvm_vcpu *vcpu, bool *is_write, int *len) unsigned long rt; int access_size; bool sign_extend; + bool sixty_four; if (kvm_vcpu_dabt_iss1tw(vcpu)) { /* page table accesses IO mem: tell guest to fix its TTBR */ @@ -150,11 +154,13 @@ static int decode_hsr(struct kvm_vcpu *vcpu, bool *is_write, int *len) *is_write = kvm_vcpu_dabt_iswrite(vcpu); sign_extend = kvm_vcpu_dabt_issext(vcpu); + sixty_four = kvm_vcpu_dabt_issf(vcpu); rt = kvm_vcpu_dabt_get_rd(vcpu); *len = access_size; vcpu->arch.mmio_decode.sign_extend = sign_extend; vcpu->arch.mmio_decode.rt = rt; + vcpu->arch.mmio_decode.sixty_four = sixty_four; return 0; } diff --git a/virt/kvm/arm/mmu.c b/virt/kvm/arm/mmu.c index bf330b493c1e7be80308e2347e1d5c812a6cc46a..a5bc10d30618f2023427874564a8160c0dacf5c9 100644 --- a/virt/kvm/arm/mmu.c +++ b/virt/kvm/arm/mmu.c @@ -1925,7 +1925,8 @@ int kvm_test_age_hva(struct kvm *kvm, unsigned long hva) if (!kvm->arch.pgd) return 0; trace_kvm_test_age_hva(hva); - return handle_hva_to_gpa(kvm, hva, hva, kvm_test_age_hva_handler, NULL); + return handle_hva_to_gpa(kvm, hva, hva + PAGE_SIZE, + kvm_test_age_hva_handler, NULL); } void kvm_mmu_free_memory_caches(struct kvm_vcpu *vcpu) diff --git a/virt/kvm/arm/pmu.c b/virt/kvm/arm/pmu.c index 1c5b76c46e26670eb676c6bacd516ab209026e9f..6d52fd50c1ffe8405e46ecb63637b28ed17aaaca 100644 --- a/virt/kvm/arm/pmu.c +++ b/virt/kvm/arm/pmu.c @@ -316,6 +316,9 @@ void kvm_pmu_software_increment(struct kvm_vcpu *vcpu, u64 val) if (val == 0) return; + if (!(__vcpu_sys_reg(vcpu, PMCR_EL0) & ARMV8_PMU_PMCR_E)) + return; + enable = __vcpu_sys_reg(vcpu, PMCNTENSET_EL0); for (i = 0; i < ARMV8_PMU_CYCLE_IDX; i++) { if (!(val & BIT(i))) diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c index 0dbe332eb343e3d697e57e10a9174d6f8e1c02b8..9295addea7ecf2e235e318118d4092cd83fa9dd5 100644 --- a/virt/kvm/arm/vgic/vgic-its.c +++ b/virt/kvm/arm/vgic/vgic-its.c @@ -2292,7 +2292,8 @@ static int vgic_its_restore_cte(struct vgic_its *its, gpa_t gpa, int esz) target_addr = (u32)(val >> KVM_ITS_CTE_RDBASE_SHIFT); coll_id = val & KVM_ITS_CTE_ICID_MASK; - if (target_addr >= atomic_read(&kvm->online_vcpus)) + if (target_addr != COLLECTION_NOT_MAPPED && + target_addr >= atomic_read(&kvm->online_vcpus)) return -EINVAL; collection = find_collection(its, coll_id); diff --git a/virt/kvm/async_pf.c b/virt/kvm/async_pf.c index 23c2519c5b32a40a2513c7c974ec44ed7199159e..c9861c2315e8ea19c65b09118ba52ae8f7dd1fd5 100644 --- a/virt/kvm/async_pf.c +++ b/virt/kvm/async_pf.c @@ -76,7 +76,7 @@ static void async_pf_execute(struct work_struct *work) struct mm_struct *mm = apf->mm; struct kvm_vcpu *vcpu = apf->vcpu; unsigned long addr = apf->addr; - gva_t gva = apf->gva; + gpa_t cr2_or_gpa = apf->cr2_or_gpa; int locked = 1; might_sleep(); @@ -104,7 +104,7 @@ static void async_pf_execute(struct work_struct *work) * this point */ - trace_kvm_async_pf_completed(addr, gva); + trace_kvm_async_pf_completed(addr, cr2_or_gpa); if (swq_has_sleeper(&vcpu->wq)) swake_up_one(&vcpu->wq); @@ -177,8 +177,8 @@ void kvm_check_async_pf_completion(struct kvm_vcpu *vcpu) } } -int kvm_setup_async_pf(struct kvm_vcpu *vcpu, gva_t gva, unsigned long hva, - struct kvm_arch_async_pf *arch) +int kvm_setup_async_pf(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, + unsigned long hva, struct kvm_arch_async_pf *arch) { struct kvm_async_pf *work; @@ -197,7 +197,7 @@ int kvm_setup_async_pf(struct kvm_vcpu *vcpu, gva_t gva, unsigned long hva, work->wakeup_all = false; work->vcpu = vcpu; - work->gva = gva; + work->cr2_or_gpa = cr2_or_gpa; work->addr = hva; work->arch = *arch; work->mm = current->mm; diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 9502b1a44232ca4b7d10a3ee55fdf5a98d045930..4e499b78569b7a7f2236830914c6f184349985e0 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -1294,14 +1294,14 @@ bool kvm_is_visible_gfn(struct kvm *kvm, gfn_t gfn) } EXPORT_SYMBOL_GPL(kvm_is_visible_gfn); -unsigned long kvm_host_page_size(struct kvm *kvm, gfn_t gfn) +unsigned long kvm_host_page_size(struct kvm_vcpu *vcpu, gfn_t gfn) { struct vm_area_struct *vma; unsigned long addr, size; size = PAGE_SIZE; - addr = gfn_to_hva(kvm, gfn); + addr = kvm_vcpu_gfn_to_hva_prot(vcpu, gfn, NULL); if (kvm_is_error_hva(addr)) return PAGE_SIZE; @@ -2024,12 +2024,12 @@ int kvm_write_guest_offset_cached(struct kvm *kvm, struct gfn_to_hva_cache *ghc, if (slots->generation != ghc->generation) __kvm_gfn_to_hva_cache_init(slots, ghc, ghc->gpa, ghc->len); - if (unlikely(!ghc->memslot)) - return kvm_write_guest(kvm, gpa, data, len); - if (kvm_is_error_hva(ghc->hva)) return -EFAULT; + if (unlikely(!ghc->memslot)) + return kvm_write_guest(kvm, gpa, data, len); + r = __copy_to_user((void __user *)ghc->hva + offset, data, len); if (r) return -EFAULT; @@ -2057,12 +2057,12 @@ int kvm_read_guest_cached(struct kvm *kvm, struct gfn_to_hva_cache *ghc, if (slots->generation != ghc->generation) __kvm_gfn_to_hva_cache_init(slots, ghc, ghc->gpa, ghc->len); - if (unlikely(!ghc->memslot)) - return kvm_read_guest(kvm, ghc->gpa, data, len); - if (kvm_is_error_hva(ghc->hva)) return -EFAULT; + if (unlikely(!ghc->memslot)) + return kvm_read_guest(kvm, ghc->gpa, data, len); + r = __copy_from_user(data, (void __user *)ghc->hva, len); if (r) return -EFAULT;