diff options
662 files changed, 16372 insertions, 3156 deletions
diff --git a/Documentation/ABI/testing/procfs-smaps_rollup b/Documentation/ABI/testing/procfs-smaps_rollup new file mode 100644 index 000000000000..0a54ed0d63c9 --- /dev/null +++ b/Documentation/ABI/testing/procfs-smaps_rollup @@ -0,0 +1,31 @@ +What: /proc/pid/smaps_rollup +Date: August 2017 +Contact: Daniel Colascione <dancol@google.com> +Description: + This file provides pre-summed memory information for a + process. The format is identical to /proc/pid/smaps, + except instead of an entry for each VMA in a process, + smaps_rollup has a single entry (tagged "[rollup]") + for which each field is the sum of the corresponding + fields from all the maps in /proc/pid/smaps. + For more details, see the procfs man page. + + Typical output looks like this: + + 00100000-ff709000 ---p 00000000 00:00 0 [rollup] + Rss: 884 kB + Pss: 385 kB + Shared_Clean: 696 kB + Shared_Dirty: 0 kB + Private_Clean: 120 kB + Private_Dirty: 68 kB + Referenced: 884 kB + Anonymous: 68 kB + LazyFree: 0 kB + AnonHugePages: 0 kB + ShmemPmdMapped: 0 kB + Shared_Hugetlb: 0 kB + Private_Hugetlb: 0 kB + Swap: 0 kB + SwapPss: 0 kB + Locked: 385 kB diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu index 50f95689ab38..e4cd3be77663 100644 --- a/Documentation/ABI/testing/sysfs-devices-system-cpu +++ b/Documentation/ABI/testing/sysfs-devices-system-cpu @@ -277,6 +277,8 @@ What: /sys/devices/system/cpu/vulnerabilities /sys/devices/system/cpu/vulnerabilities/spectre_v1 /sys/devices/system/cpu/vulnerabilities/spectre_v2 /sys/devices/system/cpu/vulnerabilities/spec_store_bypass + /sys/devices/system/cpu/vulnerabilities/l1tf + /sys/devices/system/cpu/vulnerabilities/mds Date: January 2018 Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org> Description: Information about CPU vulnerabilities diff --git a/Documentation/devicetree/bindings/bt-fm/silabs_fm.txt b/Documentation/devicetree/bindings/bt-fm/silabs_fm.txt new file mode 100644 index 000000000000..1429afe31a4e --- /dev/null +++ b/Documentation/devicetree/bindings/bt-fm/silabs_fm.txt @@ -0,0 +1,13 @@ +Silicon Labs FM radio device + +-FM RX playback with RDS + FM signal is demodulated then audio L/R samples are sent to external audio codec. + FM Rx RDS data received sent to host processor on I2C. + +Required Properties: +- compatible: "silabs,silabs-fm" + +Example: + silabs,silabs-fm { + compatible = "silabs,silabs-fm"; + }; diff --git a/Documentation/devicetree/bindings/bt-fm/silabs_fm_i2c_si4705.txt b/Documentation/devicetree/bindings/bt-fm/silabs_fm_i2c_si4705.txt new file mode 100644 index 000000000000..3baedd84e3fe --- /dev/null +++ b/Documentation/devicetree/bindings/bt-fm/silabs_fm_i2c_si4705.txt @@ -0,0 +1,73 @@ +Silicon Labs Si4705 FM radio + +Silabs Si4705 is a FM radio chip which sits on the i2c bus. +This node must live under i2c controller. + +Required properties: + + - compatible : should be "silabs,si4705" + - reg : i2c slave address of the device + - interrupt-parent : parent of interrupt + - interrupts : interrupt to indicate completion of on going tune, seek etc. + - silabs,int-gpio : interrupt gpio + - silabs,reset-gpio : reset gpio + - silabs,status-gpio : status gpio + - pinctrl-names : This should be defined if a target uses pinctrl framework. + See "pinctrl" in Documentation/devicetree/bindings/pinctrl/msm-pinctrl.txt. + It should specify the names of the configs that pinctrl can install in driver. + Following are the pinctrl configs that can be installed: + "pmx_fm_active" : Active configuration of pins, this should specify active + config defined in pin groups of interrupt and reset gpio. + "pmx_fm_suspend" : Disabled configuration of pins, this should specify sleep + config defined in pin groups of interrupt and reset gpio. + +- pinctrl : Active and suspend configuration of interrupt and reset pins. + +- interrupt-names : Name of interrupt, must be "silabs_fm_int" + +Optional properties: + - vdd-supply : Digital power supply needed to power device + - va-supply : Analog power supply needed to power device + - silabs,va-supply-voltage : specifies VA voltage levels for supply. + Should be specified in pair (min, max), units uV. + - silabs,vdd-supply-voltage : specifies VDD voltage levels for supply. + Should be specified in pairs (min, max), units uV. + +Example: + i2c_5: i2c@f9967000 { /* BLSP2 QUP5 */ + compatible = "qcom,i2c-msm-v2"; + #address-cells = <1>; + #size-cells = <0>; + reg-names = "qup_phys_addr", "bam_phys_addr"; + reg = <0xf9967000 0x1000>, + <0xf9944000 0x19000>; + interrupt-names = "qup_irq", "bam_irq"; + interrupts = <0 105 0>, <0 239 0>; + qcom,clk-freq-out = <100000>; + qcom,clk-freq-in = <19200000>; + clock-names = "iface_clk", "core_clk"; + clocks = <&clock_gcc clk_gcc_blsp2_ahb_clk>, + <&clock_gcc clk_gcc_blsp2_qup5_i2c_apps_clk>; + + pinctrl-names = "i2c_active", "i2c_sleep"; + pinctrl-0 = <&i2c_5_active>; + pinctrl-1 = <&i2c_5_sleep>; + + silabs4705@11 { /* SiLabs FM chip, slave id 0x11*/ + status = "ok"; + compatible = "silabs,si4705"; + reg = <0x11>; + vdd-supply = <&pm8994_s4>; + silabs,va-supply-voltage = <3300000 3300000>; + silabs,vdd-supply-voltage = <1800000 1800000>; + pinctrl-names = "pmx_fm_active","pmx_fm_suspend"; + pinctrl-0 = <&fm_int_active &fm_rst_active>; + pinctrl-1 = <&fm_int_suspend &fm_rst_suspend>; + silabs,reset-gpio = <&msm_gpio 62 0>; + silabs,int-gpio = <&msm_gpio 9 0>; + silabs,status-gpio = <&msm_gpio 11 0>; + interrupt-parent = <&msm_gpio>; + interrupts = <9 2>; + interrupt-names = "silabs_fm_int"; + }; + }; diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smbcharger.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smbcharger.txt index efd64cd90878..d239d4ef791c 100644 --- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smbcharger.txt +++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smbcharger.txt @@ -281,6 +281,7 @@ Optional Properties: suspending USB path for fake battery. - qcom,vchg_sns-vadc Phandle of the VADC node. +- qcom,usbin-vadc Phandle of the VADC node. - qcom,vchg-adc-channel-id The ADC channel to which the VCHG is routed. Example: diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt index 102857a13a86..2a737bb69bc9 100644 --- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt +++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt @@ -8,6 +8,15 @@ Required properties: - qcom,msm-pcm-dsp-id : device node id + Optional properties + + - qcom,avs-version: + This property can be used to specify the ADSP version/name. + Based on ADSP version, we decide if we have to use older + ADSP APIs or newer. Right now we are using "AVS 2.7" for + 8996 purpose.If the ADSP version is anything other than this + we use new ADSP APIs. + * msm-pcm-low-latency Required properties: @@ -28,6 +37,13 @@ Required properties: ultra : ultra low latency stream ull-pp : ultra low latency stream with post-processing capability + - qcom,avs-version: + This property can be used to specify the ADSP version/name. + Based on ADSP version, we decide if we have to use older + ADSP APIs or newer. Right now we are using "AVS 2.7" for + 8996 purpose.If the ADSP version is anything other than this + we use new ADSP APIs. + * msm-pcm-dsp-noirq Required properties: @@ -75,6 +91,13 @@ Optional properties 8909 purpose.If the ADSP version is anything other than this we use new ADSP APIs. + - qcom,avs-version: + This property can be used to specify the ADSP version/name. + Based on ADSP version, we decide if we have to use older + ADSP APIs or newer. Right now we are using "AVS 2.7" for + 8996 purpose.If the ADSP version is anything other than this + we use new ADSP APIs. + * msm-voip-dsp Required properties: diff --git a/Documentation/devicetree/bindings/usb/msm-ssusb.txt b/Documentation/devicetree/bindings/usb/msm-ssusb.txt index 54792335e67e..a8229051cbd8 100644 --- a/Documentation/devicetree/bindings/usb/msm-ssusb.txt +++ b/Documentation/devicetree/bindings/usb/msm-ssusb.txt @@ -66,6 +66,8 @@ Optional properties : which is used as a vote by driver to get max performance in perf mode. - qcom,no-wakeup-src-in-hostmode: If present then driver doesn't use wakeup_source APIs in host mode. This allows PM suspend to happen irrespective of runtimePM state of host. +- qcom,check-for-float: If present, the driver will always check for possible + float connection irrespective of the charger type. Sub nodes: - Sub node for "DWC3- USB3 controller". diff --git a/Documentation/hw-vuln/mds.rst b/Documentation/hw-vuln/mds.rst new file mode 100644 index 000000000000..3f92728be021 --- /dev/null +++ b/Documentation/hw-vuln/mds.rst @@ -0,0 +1,305 @@ +MDS - Microarchitectural Data Sampling +====================================== + +Microarchitectural Data Sampling is a hardware vulnerability which allows +unprivileged speculative access to data which is available in various CPU +internal buffers. + +Affected processors +------------------- + +This vulnerability affects a wide range of Intel processors. The +vulnerability is not present on: + + - Processors from AMD, Centaur and other non Intel vendors + + - Older processor models, where the CPU family is < 6 + + - Some Atoms (Bonnell, Saltwell, Goldmont, GoldmontPlus) + + - Intel processors which have the ARCH_CAP_MDS_NO bit set in the + IA32_ARCH_CAPABILITIES MSR. + +Whether a processor is affected or not can be read out from the MDS +vulnerability file in sysfs. See :ref:`mds_sys_info`. + +Not all processors are affected by all variants of MDS, but the mitigation +is identical for all of them so the kernel treats them as a single +vulnerability. + +Related CVEs +------------ + +The following CVE entries are related to the MDS vulnerability: + + ============== ===== =================================================== + CVE-2018-12126 MSBDS Microarchitectural Store Buffer Data Sampling + CVE-2018-12130 MFBDS Microarchitectural Fill Buffer Data Sampling + CVE-2018-12127 MLPDS Microarchitectural Load Port Data Sampling + CVE-2019-11091 MDSUM Microarchitectural Data Sampling Uncacheable Memory + ============== ===== =================================================== + +Problem +------- + +When performing store, load, L1 refill operations, processors write data +into temporary microarchitectural structures (buffers). The data in the +buffer can be forwarded to load operations as an optimization. + +Under certain conditions, usually a fault/assist caused by a load +operation, data unrelated to the load memory address can be speculatively +forwarded from the buffers. Because the load operation causes a fault or +assist and its result will be discarded, the forwarded data will not cause +incorrect program execution or state changes. But a malicious operation +may be able to forward this speculative data to a disclosure gadget which +allows in turn to infer the value via a cache side channel attack. + +Because the buffers are potentially shared between Hyper-Threads cross +Hyper-Thread attacks are possible. + +Deeper technical information is available in the MDS specific x86 +architecture section: :ref:`Documentation/x86/mds.rst <mds>`. + + +Attack scenarios +---------------- + +Attacks against the MDS vulnerabilities can be mounted from malicious non +priviledged user space applications running on hosts or guest. Malicious +guest OSes can obviously mount attacks as well. + +Contrary to other speculation based vulnerabilities the MDS vulnerability +does not allow the attacker to control the memory target address. As a +consequence the attacks are purely sampling based, but as demonstrated with +the TLBleed attack samples can be postprocessed successfully. + +Web-Browsers +^^^^^^^^^^^^ + + It's unclear whether attacks through Web-Browsers are possible at + all. The exploitation through Java-Script is considered very unlikely, + but other widely used web technologies like Webassembly could possibly be + abused. + + +.. _mds_sys_info: + +MDS system information +----------------------- + +The Linux kernel provides a sysfs interface to enumerate the current MDS +status of the system: whether the system is vulnerable, and which +mitigations are active. The relevant sysfs file is: + +/sys/devices/system/cpu/vulnerabilities/mds + +The possible values in this file are: + + .. list-table:: + + * - 'Not affected' + - The processor is not vulnerable + * - 'Vulnerable' + - The processor is vulnerable, but no mitigation enabled + * - 'Vulnerable: Clear CPU buffers attempted, no microcode' + - The processor is vulnerable but microcode is not updated. + + The mitigation is enabled on a best effort basis. See :ref:`vmwerv` + * - 'Mitigation: Clear CPU buffers' + - The processor is vulnerable and the CPU buffer clearing mitigation is + enabled. + +If the processor is vulnerable then the following information is appended +to the above information: + + ======================== ============================================ + 'SMT vulnerable' SMT is enabled + 'SMT mitigated' SMT is enabled and mitigated + 'SMT disabled' SMT is disabled + 'SMT Host state unknown' Kernel runs in a VM, Host SMT state unknown + ======================== ============================================ + +.. _vmwerv: + +Best effort mitigation mode +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + If the processor is vulnerable, but the availability of the microcode based + mitigation mechanism is not advertised via CPUID the kernel selects a best + effort mitigation mode. This mode invokes the mitigation instructions + without a guarantee that they clear the CPU buffers. + + This is done to address virtualization scenarios where the host has the + microcode update applied, but the hypervisor is not yet updated to expose + the CPUID to the guest. If the host has updated microcode the protection + takes effect otherwise a few cpu cycles are wasted pointlessly. + + The state in the mds sysfs file reflects this situation accordingly. + + +Mitigation mechanism +------------------------- + +The kernel detects the affected CPUs and the presence of the microcode +which is required. + +If a CPU is affected and the microcode is available, then the kernel +enables the mitigation by default. The mitigation can be controlled at boot +time via a kernel command line option. See +:ref:`mds_mitigation_control_command_line`. + +.. _cpu_buffer_clear: + +CPU buffer clearing +^^^^^^^^^^^^^^^^^^^ + + The mitigation for MDS clears the affected CPU buffers on return to user + space and when entering a guest. + + If SMT is enabled it also clears the buffers on idle entry when the CPU + is only affected by MSBDS and not any other MDS variant, because the + other variants cannot be protected against cross Hyper-Thread attacks. + + For CPUs which are only affected by MSBDS the user space, guest and idle + transition mitigations are sufficient and SMT is not affected. + +.. _virt_mechanism: + +Virtualization mitigation +^^^^^^^^^^^^^^^^^^^^^^^^^ + + The protection for host to guest transition depends on the L1TF + vulnerability of the CPU: + + - CPU is affected by L1TF: + + If the L1D flush mitigation is enabled and up to date microcode is + available, the L1D flush mitigation is automatically protecting the + guest transition. + + If the L1D flush mitigation is disabled then the MDS mitigation is + invoked explicit when the host MDS mitigation is enabled. + + For details on L1TF and virtualization see: + :ref:`Documentation/hw-vuln//l1tf.rst <mitigation_control_kvm>`. + + - CPU is not affected by L1TF: + + CPU buffers are flushed before entering the guest when the host MDS + mitigation is enabled. + + The resulting MDS protection matrix for the host to guest transition: + + ============ ===== ============= ============ ================= + L1TF MDS VMX-L1FLUSH Host MDS MDS-State + + Don't care No Don't care N/A Not affected + + Yes Yes Disabled Off Vulnerable + + Yes Yes Disabled Full Mitigated + + Yes Yes Enabled Don't care Mitigated + + No Yes N/A Off Vulnerable + + No Yes N/A Full Mitigated + ============ ===== ============= ============ ================= + + This only covers the host to guest transition, i.e. prevents leakage from + host to guest, but does not protect the guest internally. Guests need to + have their own protections. + +.. _xeon_phi: + +XEON PHI specific considerations +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + The XEON PHI processor family is affected by MSBDS which can be exploited + cross Hyper-Threads when entering idle states. Some XEON PHI variants allow + to use MWAIT in user space (Ring 3) which opens an potential attack vector + for malicious user space. The exposure can be disabled on the kernel + command line with the 'ring3mwait=disable' command line option. + + XEON PHI is not affected by the other MDS variants and MSBDS is mitigated + before the CPU enters a idle state. As XEON PHI is not affected by L1TF + either disabling SMT is not required for full protection. + +.. _mds_smt_control: + +SMT control +^^^^^^^^^^^ + + All MDS variants except MSBDS can be attacked cross Hyper-Threads. That + means on CPUs which are affected by MFBDS or MLPDS it is necessary to + disable SMT for full protection. These are most of the affected CPUs; the + exception is XEON PHI, see :ref:`xeon_phi`. + + Disabling SMT can have a significant performance impact, but the impact + depends on the type of workloads. + + See the relevant chapter in the L1TF mitigation documentation for details: + :ref:`Documentation/hw-vuln/l1tf.rst <smt_control>`. + + +.. _mds_mitigation_control_command_line: + +Mitigation control on the kernel command line +--------------------------------------------- + +The kernel command line allows to control the MDS mitigations at boot +time with the option "mds=". The valid arguments for this option are: + + ============ ============================================================= + full If the CPU is vulnerable, enable all available mitigations + for the MDS vulnerability, CPU buffer clearing on exit to + userspace and when entering a VM. Idle transitions are + protected as well if SMT is enabled. + + It does not automatically disable SMT. + + off Disables MDS mitigations completely. + + ============ ============================================================= + +Not specifying this option is equivalent to "mds=full". + + +Mitigation selection guide +-------------------------- + +1. Trusted userspace +^^^^^^^^^^^^^^^^^^^^ + + If all userspace applications are from a trusted source and do not + execute untrusted code which is supplied externally, then the mitigation + can be disabled. + + +2. Virtualization with trusted guests +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + The same considerations as above versus trusted user space apply. + +3. Virtualization with untrusted guests +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + The protection depends on the state of the L1TF mitigations. + See :ref:`virt_mechanism`. + + If the MDS mitigation is enabled and SMT is disabled, guest to host and + guest to guest attacks are prevented. + +.. _mds_default_mitigations: + +Default mitigations +------------------- + + The kernel default mitigations for vulnerable processors are: + + - Enable CPU buffer clearing + + The kernel does not by default enforce the disabling of SMT, which leaves + SMT systems vulnerable when running untrusted code. The same rationale as + for L1TF applies. + See :ref:`Documentation/hw-vuln//l1tf.rst <default_mitigations>`. diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index a0179a6a2a11..69b096e6ac45 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2086,6 +2086,30 @@ bytes respectively. Such letter suffixes can also be entirely omitted. Format: <first>,<last> Specifies range of consoles to be captured by the MDA. + mds= [X86,INTEL] + Control mitigation for the Micro-architectural Data + Sampling (MDS) vulnerability. + + Certain CPUs are vulnerable to an exploit against CPU + internal buffers which can forward information to a + disclosure gadget under certain conditions. + + In vulnerable processors, the speculatively + forwarded data can be used in a cache side channel + attack, to access data to which the attacker does + not have direct access. + + This parameter controls the MDS mitigation. The + options are: + + full - Enable MDS mitigation on vulnerable CPUs + off - Unconditionally disable MDS mitigation + + Not specifying this option is equivalent to + mds=full. + + For details see: Documentation/hw-vuln/mds.rst + mem=nn[KMG] [KNL,BOOT] Force usage of a specific amount of memory Amount of memory to be used when the kernel is not able to see the whole system memory or for test. @@ -2200,6 +2224,30 @@ bytes respectively. Such letter suffixes can also be entirely omitted. in the "bleeding edge" mini2440 support kernel at http://repo.or.cz/w/linux-2.6/mini2440.git + mitigations= + [X86] Control optional mitigations for CPU + vulnerabilities. This is a set of curated, + arch-independent options, each of which is an + aggregation of existing arch-specific options. + + off + Disable all optional CPU mitigations. This + improves system performance, but it may also + expose users to several CPU vulnerabilities. + Equivalent to: nopti [X86] + nospectre_v2 [X86] + spectre_v2_user=off [X86] + spec_store_bypass_disable=off [X86] + mds=off [X86] + + auto (default) + Mitigate all CPU vulnerabilities, but leave SMT + enabled, even if it's vulnerable. This is for + users who don't want to be surprised by SMT + getting disabled across kernel upgrades, or who + have other ways of avoiding SMT-based attacks. + Equivalent to: (default behavior) + mminit_loglevel= [KNL] When CONFIG_DEBUG_MEMORY_INIT is set, this parameter allows control of the logging verbosity for @@ -2520,7 +2568,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted. nohugeiomap [KNL,x86] Disable kernel huge I/O mappings. - nospectre_v2 [X86] Disable all mitigations for the Spectre variant 2 + nospectre_v1 [PPC] Disable mitigations for Spectre Variant 1 (bounds + check bypass). With this option data leaks are possible + in the system. + + nospectre_v2 [X86,PPC_FSL_BOOK3E] Disable all mitigations for the Spectre variant 2 (indirect branch prediction) vulnerability. System may allow data leaks with this option, which is equivalent to spectre_v2=off. @@ -3679,9 +3731,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted. spectre_v2= [X86] Control mitigation of Spectre variant 2 (indirect branch speculation) vulnerability. + The default operation protects the kernel from + user space attacks. - on - unconditionally enable - off - unconditionally disable + on - unconditionally enable, implies + spectre_v2_user=on + off - unconditionally disable, implies + spectre_v2_user=off auto - kernel detects whether your CPU model is vulnerable @@ -3691,6 +3747,12 @@ bytes respectively. Such letter suffixes can also be entirely omitted. CONFIG_RETPOLINE configuration option, and the compiler with which the kernel was built. + Selecting 'on' will also enable the mitigation + against user space to user space task attacks. + + Selecting 'off' will disable both the kernel and + the user space protections. + Specific mitigations can also be selected manually: retpoline - replace indirect branches @@ -3700,6 +3762,48 @@ bytes respectively. Such letter suffixes can also be entirely omitted. Not specifying this option is equivalent to spectre_v2=auto. + spectre_v2_user= + [X86] Control mitigation of Spectre variant 2 + (indirect branch speculation) vulnerability between + user space tasks + + on - Unconditionally enable mitigations. Is + enforced by spectre_v2=on + + off - Unconditionally disable mitigations. Is + enforced by spectre_v2=off + + prctl - Indirect branch speculation is enabled, + but mitigation can be enabled via prctl + per thread. The mitigation control state + is inherited on fork. + + prctl,ibpb + - Like "prctl" above, but only STIBP is + controlled per thread. IBPB is issued + always when switching between different user + space processes. + + seccomp + - Same as "prctl" above, but all seccomp + threads will enable the mitigation unless + they explicitly opt out. + + seccomp,ibpb + - Like "seccomp" above, but only STIBP is + controlled per thread. IBPB is issued + always when switching between different + user space processes. + + auto - Kernel selects the mitigation depending on + the available CPU features and vulnerability. + + Default mitigation: + If CONFIG_SECCOMP=y then "seccomp", otherwise "prctl" + + Not specifying this option is equivalent to + spectre_v2_user=auto. + spec_store_bypass_disable= [HW] Control Speculative Store Bypass (SSB) Disable mitigation (Speculative Store Bypass vulnerability) diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 71cfca514893..a7516e4c8ce8 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -220,6 +220,14 @@ tcp_base_mss - INTEGER Path MTU discovery (MTU probing). If MTU probing is enabled, this is the initial MSS used by the connection. +tcp_min_snd_mss - INTEGER + TCP SYN and SYNACK messages usually advertise an ADVMSS option, + as described in RFC 1122 and RFC 6691. + If this ADVMSS option is smaller than tcp_min_snd_mss, + it is silently capped to tcp_min_snd_mss. + + Default : 48 (at least 8 bytes of payload per segment) + tcp_congestion_control - STRING Set the congestion control algorithm to be used for new connections. The algorithm "reno" is always available, but @@ -387,6 +395,7 @@ tcp_min_rtt_wlen - INTEGER minimum RTT when it is moved to a longer path (e.g., due to traffic engineering). A longer window makes the filter more resistant to RTT inflations such as transient congestion. The unit is seconds. + Possible values: 0 - 86400 (1 day) Default: 300 tcp_moderate_rcvbuf - BOOLEAN diff --git a/Documentation/spec_ctrl.txt b/Documentation/spec_ctrl.txt index 32f3d55c54b7..c4dbe6f7cdae 100644 --- a/Documentation/spec_ctrl.txt +++ b/Documentation/spec_ctrl.txt @@ -92,3 +92,12 @@ Speculation misfeature controls * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, PR_SPEC_ENABLE, 0, 0); * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, PR_SPEC_DISABLE, 0, 0); * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, PR_SPEC_FORCE_DISABLE, 0, 0); + +- PR_SPEC_INDIR_BRANCH: Indirect Branch Speculation in User Processes + (Mitigate Spectre V2 style attacks against user processes) + + Invocations: + * prctl(PR_GET_SPECULATION_CTRL, PR_SPEC_INDIRECT_BRANCH, 0, 0, 0); + * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_INDIRECT_BRANCH, PR_SPEC_ENABLE, 0, 0); + * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_INDIRECT_BRANCH, PR_SPEC_DISABLE, 0, 0); + * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_INDIRECT_BRANCH, PR_SPEC_FORCE_DISABLE, 0, 0); diff --git a/Documentation/usb/power-management.txt b/Documentation/usb/power-management.txt index 0a94ffe17ab6..b13e031beaa6 100644 --- a/Documentation/usb/power-management.txt +++ b/Documentation/usb/power-management.txt @@ -365,11 +365,15 @@ autosuspend the interface's device. When the usage counter is = 0 then the interface is considered to be idle, and the kernel may autosuspend the device. -Drivers need not be concerned about balancing changes to the usage -counter; the USB core will undo any remaining "get"s when a driver -is unbound from its interface. As a corollary, drivers must not call -any of the usb_autopm_* functions after their disconnect() routine has -returned. +Drivers must be careful to balance their overall changes to the usage +counter. Unbalanced "get"s will remain in effect when a driver is +unbound from its interface, preventing the device from going into +runtime suspend should the interface be bound to a driver again. On +the other hand, drivers are allowed to achieve this balance by calling +the ``usb_autopm_*`` functions even after their ``disconnect`` routine +has returned -- say from within a work-queue routine -- provided they +retain an active reference to the interface (via ``usb_get_intf`` and +``usb_put_intf``). Drivers using the async routines are responsible for their own synchronization and mutual exclusion. diff --git a/Documentation/x86/mds.rst b/Documentation/x86/mds.rst new file mode 100644 index 000000000000..5d4330be200f --- /dev/null +++ b/Documentation/x86/mds.rst @@ -0,0 +1,193 @@ +Microarchitectural Data Sampling (MDS) mitigation +================================================= + +.. _mds: + +Overview +-------- + +Microarchitectural Data Sampling (MDS) is a family of side channel attacks +on internal buffers in Intel CPUs. The variants are: + + - Microarchitectural Store Buffer Data Sampling (MSBDS) (CVE-2018-12126) + - Microarchitectural Fill Buffer Data Sampling (MFBDS) (CVE-2018-12130) + - Microarchitectural Load Port Data Sampling (MLPDS) (CVE-2018-12127) + - Microarchitectural Data Sampling Uncacheable Memory (MDSUM) (CVE-2019-11091) + +MSBDS leaks Store Buffer Entries which can be speculatively forwarded to a +dependent load (store-to-load forwarding) as an optimization. The forward +can also happen to a faulting or assisting load operation for a different +memory address, which can be exploited under certain conditions. Store +buffers are partitioned between Hyper-Threads so cross thread forwarding is +not possible. But if a thread enters or exits a sleep state the store +buffer is repartitioned which can expose data from one thread to the other. + +MFBDS leaks Fill Buffer Entries. Fill buffers are used internally to manage +L1 miss situations and to hold data which is returned or sent in response +to a memory or I/O operation. Fill buffers can forward data to a load +operation and also write data to the cache. When the fill buffer is +deallocated it can retain the stale data of the preceding operations which +can then be forwarded to a faulting or assisting load operation, which can +be exploited under certain conditions. Fill buffers are shared between +Hyper-Threads so cross thread leakage is possible. + +MLPDS leaks Load Port Data. Load ports are used to perform load operations +from memory or I/O. The received data is then forwarded to the register +file or a subsequent operation. In some implementations the Load Port can +contain stale data from a previous operation which can be forwarded to +faulting or assisting loads under certain conditions, which again can be +exploited eventually. Load ports are shared between Hyper-Threads so cross +thread leakage is possible. + +MDSUM is a special case of MSBDS, MFBDS and MLPDS. An uncacheable load from +memory that takes a fault or assist can leave data in a microarchitectural +structure that may later be observed using one of the same methods used by +MSBDS, MFBDS or MLPDS. + +Exposure assumptions +-------------------- + +It is assumed that attack code resides in user space or in a guest with one +exception. The rationale behind this assumption is that the code construct +needed for exploiting MDS requires: + + - to control the load to trigger a fault or assist + + - to have a disclosure gadget which exposes the speculatively accessed + data for consumption through a side channel. + + - to control the pointer through which the disclosure gadget exposes the + data + +The existence of such a construct in the kernel cannot be excluded with +100% certainty, but the complexity involved makes it extremly unlikely. + +There is one exception, which is untrusted BPF. The functionality of +untrusted BPF is limited, but it needs to be thoroughly investigated +whether it can be used to create such a construct. + + +Mitigation strategy +------------------- + +All variants have the same mitigation strategy at least for the single CPU +thread case (SMT off): Force the CPU to clear the affected buffers. + +This is achieved by using the otherwise unused and obsolete VERW +instruction in combination with a microcode update. The microcode clears +the affected CPU buffers when the VERW instruction is executed. + +For virtualization there are two ways to achieve CPU buffer +clearing. Either the modified VERW instruction or via the L1D Flush +command. The latter is issued when L1TF mitigation is enabled so the extra +VERW can be avoided. If the CPU is not affected by L1TF then VERW needs to +be issued. + +If the VERW instruction with the supplied segment selector argument is +executed on a CPU without the microcode update there is no side effect +other than a small number of pointlessly wasted CPU cycles. + +This does not protect against cross Hyper-Thread attacks except for MSBDS +which is only exploitable cross Hyper-thread when one of the Hyper-Threads +enters a C-state. + +The kernel provides a function to invoke the buffer clearing: + + mds_clear_cpu_buffers() + +The mitigation is invoked on kernel/userspace, hypervisor/guest and C-state +(idle) transitions. + +As a special quirk to address virtualization scenarios where the host has +the microcode updated, but the hypervisor does not (yet) expose the +MD_CLEAR CPUID bit to guests, the kernel issues the VERW instruction in the +hope that it might actually clear the buffers. The state is reflected +accordingly. + +According to current knowledge additional mitigations inside the kernel +itself are not required because the necessary gadgets to expose the leaked +data cannot be controlled in a way which allows exploitation from malicious +user space or VM guests. + +Kernel internal mitigation modes +-------------------------------- + + ======= ============================================================ + off Mitigation is disabled. Either the CPU is not affected or + mds=off is supplied on the kernel command line + + full Mitigation is enabled. CPU is affected and MD_CLEAR is + advertised in CPUID. + + vmwerv Mitigation is enabled. CPU is affected and MD_CLEAR is not + advertised in CPUID. That is mainly for virtualization + scenarios where the host has the updated microcode but the + hypervisor does not expose MD_CLEAR in CPUID. It's a best + effort approach without guarantee. + ======= ============================================================ + +If the CPU is affected and mds=off is not supplied on the kernel command +line then the kernel selects the appropriate mitigation mode depending on +the availability of the MD_CLEAR CPUID bit. + +Mitigation points +----------------- + +1. Return to user space +^^^^^^^^^^^^^^^^^^^^^^^ + + When transitioning from kernel to user space the CPU buffers are flushed + on affected CPUs when the mitigation is not disabled on the kernel + command line. The migitation is enabled through the static key + mds_user_clear. + + The mitigation is invoked in prepare_exit_to_usermode() which covers + all but one of the kernel to user space transitions. The exception + is when we return from a Non Maskable Interrupt (NMI), which is + handled directly in do_nmi(). + + (The reason that NMI is special is that prepare_exit_to_usermode() can + enable IRQs. In NMI context, NMIs are blocked, and we don't want to + enable IRQs with NMIs blocked.) + + +2. C-State transition +^^^^^^^^^^^^^^^^^^^^^ + + When a CPU goes idle and enters a C-State the CPU buffers need to be + cleared on affected CPUs when SMT is active. This addresses the + repartitioning of the store buffer when one of the Hyper-Threads enters + a C-State. + + When SMT is inactive, i.e. either the CPU does not support it or all + sibling threads are offline CPU buffer clearing is not required. + + The idle clearing is enabled on CPUs which are only affected by MSBDS + and not by any other MDS variant. The other MDS variants cannot be + protected against cross Hyper-Thread attacks because the Fill Buffer and + the Load Ports are shared. So on CPUs affected by other variants, the + idle clearing would be a window dressing exercise and is therefore not + activated. + + The invocation is controlled by the static key mds_idle_clear which is + switched depending on the chosen mitigation mode and the SMT state of + the system. + + The buffer clear is only invoked before entering the C-State to prevent + that stale data from the idling CPU from spilling to the Hyper-Thread + sibling after the store buffer got repartitioned and all entries are + available to the non idle sibling. + + When coming out of idle the store buffer is partitioned again so each + sibling has half of it available. The back from idle CPU could be then + speculatively exposed to contents of the sibling. The buffers are + flushed either on exit to user space or on VMENTER so malicious code + in user space or the guest cannot speculatively access them. + + The mitigation is hooked into all variants of halt()/mwait(), but does + not cover the legacy ACPI IO-Port mechanism because the ACPI idle driver + has been superseded by the intel_idle driver around 2010 and is + preferred on all affected CPUs which are expected to gain the MD_CLEAR + functionality in microcode. Aside of that the IO-Port mechanism is a + legacy interface which is only used on older systems which are either + not affected or do not receive microcode updates anymore. @@ -1,6 +1,6 @@ VERSION = 4 PATCHLEVEL = 4 -SUBLEVEL = 179 +SUBLEVEL = 183 EXTRAVERSION = NAME = Blurry Fish Butt diff --git a/arch/arm/boot/dts/exynos5420-arndale-octa.dts b/arch/arm/boot/dts/exynos5420-arndale-octa.dts index 4ecef6981d5c..b54c0b8a5b34 100644 --- a/arch/arm/boot/dts/exynos5420-arndale-octa.dts +++ b/arch/arm/boot/dts/exynos5420-arndale-octa.dts @@ -97,6 +97,7 @@ regulator-name = "PVDD_APIO_1V8"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; + regulator-always-on; }; ldo3_reg: LDO3 { @@ -135,6 +136,7 @@ regulator-name = "PVDD_ABB_1V8"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; + regulator-always-on; }; ldo9_reg: LDO9 { diff --git a/arch/arm/boot/dts/imx6qdl-phytec-pfla02.dtsi b/arch/arm/boot/dts/imx6qdl-phytec-pfla02.dtsi index d6d98d426384..cae04e806036 100644 --- a/arch/arm/boot/dts/imx6qdl-phytec-pfla02.dtsi +++ b/arch/arm/boot/dts/imx6qdl-phytec-pfla02.dtsi @@ -90,6 +90,7 @@ pinctrl-names = "default"; pinctrl-0 = <&pinctrl_enet>; phy-mode = "rgmii"; + phy-reset-duration = <10>; /* in msecs */ phy-reset-gpios = <&gpio3 23 GPIO_ACTIVE_LOW>; phy-supply = <&vdd_eth_io_reg>; status = "disabled"; diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi index e6af41c4bbc1..3992b8ea1c48 100644 --- a/arch/arm/boot/dts/imx6qdl.dtsi +++ b/arch/arm/boot/dts/imx6qdl.dtsi @@ -853,7 +853,7 @@ compatible = "fsl,imx6q-sdma", "fsl,imx35-sdma"; reg = <0x020ec000 0x4000>; interrupts = <0 2 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&clks IMX6QDL_CLK_SDMA>, + clocks = <&clks IMX6QDL_CLK_IPG>, <&clks IMX6QDL_CLK_SDMA>; clock-names = "ipg", "ahb"; #dma-cells = <3>; diff --git a/arch/arm/boot/dts/imx6sl.dtsi b/arch/arm/boot/dts/imx6sl.dtsi index d8ba99f1d87b..ac820dfef977 100644 --- a/arch/arm/boot/dts/imx6sl.dtsi +++ b/arch/arm/boot/dts/imx6sl.dtsi @@ -657,7 +657,7 @@ reg = <0x020ec000 0x4000>; interrupts = <0 2 IRQ_TYPE_LEVEL_HIGH>; clocks = <&clks IMX6SL_CLK_SDMA>, - <&clks IMX6SL_CLK_SDMA>; + <&clks IMX6SL_CLK_AHB>; clock-names = "ipg", "ahb"; #dma-cells = <3>; /* imx6sl reuses imx6q sdma firmware */ diff --git a/arch/arm/boot/dts/imx6sx.dtsi b/arch/arm/boot/dts/imx6sx.dtsi index 6963dff815dc..5783eb8541ed 100644 --- a/arch/arm/boot/dts/imx6sx.dtsi +++ b/arch/arm/boot/dts/imx6sx.dtsi @@ -732,7 +732,7 @@ compatible = "fsl,imx6sx-sdma", "fsl,imx6q-sdma"; reg = <0x020ec000 0x4000>; interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&clks IMX6SX_CLK_SDMA>, + clocks = <&clks IMX6SX_CLK_IPG>, <&clks IMX6SX_CLK_SDMA>; clock-names = "ipg", "ahb"; #dma-cells = <3>; diff --git a/arch/arm/boot/dts/qcom/msm8996-auto.dtsi b/arch/arm/boot/dts/qcom/msm8996-auto.dtsi index ba075694d8c9..7dd486618cde 100644 --- a/arch/arm/boot/dts/qcom/msm8996-auto.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-auto.dtsi @@ -11,7 +11,11 @@ */ &android_vendor { - fsmgr_flags = "wait,slotselect,verify"; + fsmgr_flags = "wait,slotselect,avb"; +}; + +&android_vbmeta { + parts = "vbmeta,boot,system,vendor,dtbo"; }; &peripheral_mem { diff --git a/arch/arm/boot/dts/qcom/msm8996-cdp.dtsi b/arch/arm/boot/dts/qcom/msm8996-cdp.dtsi index c19899567b58..3988953f6963 100644 --- a/arch/arm/boot/dts/qcom/msm8996-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-cdp.dtsi @@ -680,8 +680,9 @@ sound-9335 { qcom,model = "msm8996-tasha-cdp-snd-card"; qcom,hdmi-audio-rx; - asoc-codec = <&stub_codec>, <&hdmi_audio>; - asoc-codec-names = "msm-stub-codec.1", "msm-hdmi-audio-codec-rx"; + asoc-codec = <&stub_codec>, <&ext_disp_audio_codec>; + asoc-codec-names = "msm-stub-codec.1", + "msm-ext-disp-audio-codec-rx"; qcom,us-euro-gpios = <&pm8994_mpps 2 0>; qcom,wsa-max-devs = <2>; qcom,wsa-devs = <&wsa881x_211>, <&wsa881x_212>, @@ -786,6 +787,7 @@ &usb3 { extcon = <&pmi8994_charger>; + qcom,check-for-float; }; &usb2s { diff --git a/arch/arm/boot/dts/qcom/msm8996-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8996-mtp.dtsi index c47953271f28..38f3715973cd 100644 --- a/arch/arm/boot/dts/qcom/msm8996-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-mtp.dtsi @@ -511,6 +511,7 @@ &usb3 { extcon = <&pmi8994_charger>; + qcom,check-for-float; }; &i2c_7 { @@ -525,6 +526,7 @@ &pmi8994_charger { dpdm-supply = <&qusb_phy0>; + qcom,usbin-vadc = <&pmi8994_vadc>; }; &pmi8994_fg { @@ -707,6 +709,7 @@ sound-9335 { qcom,model = "msm8996-tasha-mtp-snd-card"; qcom,tdm-audio-intf; + qcom,hdmi-audio-rx; qcom,audio-routing = "AIF4 VI", "MCLK", @@ -738,9 +741,9 @@ "MIC BIAS4", "Digital Mic5", "SpkrLeft IN", "SPK1 OUT", "SpkrRight IN", "SPK2 OUT"; - - asoc-codec = <&stub_codec>, <&hdmi_audio>; - asoc-codec-names = "msm-stub-codec.1", "msm-hdmi-audio-codec-rx"; + asoc-codec = <&stub_codec>, <&ext_disp_audio_codec>; + asoc-codec-names = "msm-stub-codec.1", + "msm-ext-disp-audio-codec-rx"; asoc-cpu = <&dai_pri_auxpcm>, <&dai_sec_auxpcm>, <&dai_hdmi>, <&dai_mi2s>, <&sb_0_rx>, <&sb_0_tx>, @@ -810,6 +813,23 @@ qcom,wsa-aux-dev-prefix = "SpkrLeft", "SpkrRight", "SpkrLeft", "SpkrRight"; }; + + qcom,msm-pcm { + qcom,avs-version; + }; + + qcom,msm-pcm-low-latency { + qcom,avs-version; + }; + + qcom,msm-ultra-low-latency { + qcom,avs-version; + }; + + qcom,msm-compress-dsp { + qcom,avs-version; + }; + qcom,msm-dai-tdm-tert-rx { qcom,msm-cpudai-tdm-clk-internal = <1>; qcom,msm-cpudai-tdm-sync-mode = <0>; diff --git a/arch/arm/boot/dts/qcom/msm8996.dtsi b/arch/arm/boot/dts/qcom/msm8996.dtsi index 593b227d540f..fd49714e2a5c 100644 --- a/arch/arm/boot/dts/qcom/msm8996.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996.dtsi @@ -186,7 +186,7 @@ firmware: firmware { android { compatible = "android,firmware"; - vbmeta { + android_vbmeta: vbmeta { compatible = "android,vbmeta"; parts = "vbmeta,boot,system,vendor,dtbo,recovery"; }; @@ -2177,6 +2177,7 @@ phy_type= "utmi"; qcom,phy-clk-scheme = "cmos"; qcom,major-rev = <1>; + qcom,enable-dpdm-pulsing; clocks = <&clock_gcc clk_gcc_usb_phy_cfg_ahb2phy_clk>, <&clock_gcc clk_ln_bb_clk>; diff --git a/arch/arm/boot/dts/qcom/msm8996pro-auto.dtsi b/arch/arm/boot/dts/qcom/msm8996pro-auto.dtsi index 262f8397d975..b9ae88fa8865 100644 --- a/arch/arm/boot/dts/qcom/msm8996pro-auto.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996pro-auto.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, 2019 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 @@ -18,7 +18,7 @@ fstab { /delete-node/ system; vendor { - fsmgr_flags = "wait,slotselect,verify"; + fsmgr_flags = "wait,slotselect,avb"; status = "ok"; }; }; diff --git a/arch/arm/configs/msmcortex_defconfig b/arch/arm/configs/msmcortex_defconfig index 4af551449778..8e086631ce1e 100644 --- a/arch/arm/configs/msmcortex_defconfig +++ b/arch/arm/configs/msmcortex_defconfig @@ -484,9 +484,7 @@ CONFIG_ANDROID=y CONFIG_ANDROID_BINDER_IPC=y CONFIG_MSM_TZ_LOG=y CONFIG_SENSORS_SSC=y -CONFIG_EXT2_FS=y -CONFIG_EXT2_FS_XATTR=y -CONFIG_EXT3_FS=y +CONFIG_EXT4_FS=y CONFIG_EXT4_FS_SECURITY=y CONFIG_FUSE_FS=y CONFIG_MSDOS_FS=y diff --git a/arch/arm/crypto/aesbs-glue.c b/arch/arm/crypto/aesbs-glue.c index 648d5fac9cbf..3271c836e1a1 100644 --- a/arch/arm/crypto/aesbs-glue.c +++ b/arch/arm/crypto/aesbs-glue.c @@ -259,6 +259,8 @@ static int aesbs_xts_encrypt(struct blkcipher_desc *desc, blkcipher_walk_init(&walk, dst, src, nbytes); err = blkcipher_walk_virt_block(desc, &walk, 8 * AES_BLOCK_SIZE); + if (err) + return err; /* generate the initial tweak */ AES_encrypt(walk.iv, walk.iv, &ctx->twkey); @@ -283,6 +285,8 @@ static int aesbs_xts_decrypt(struct blkcipher_desc *desc, blkcipher_walk_init(&walk, dst, src, nbytes); err = blkcipher_walk_virt_block(desc, &walk, 8 * AES_BLOCK_SIZE); + if (err) + return err; /* generate the initial tweak */ AES_encrypt(walk.iv, walk.iv, &ctx->twkey); diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c index 4cddf20cdb82..5475e684a6b4 100644 --- a/arch/arm/kvm/arm.c +++ b/arch/arm/kvm/arm.c @@ -748,7 +748,7 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level, static int kvm_vcpu_set_target(struct kvm_vcpu *vcpu, const struct kvm_vcpu_init *init) { - unsigned int i; + unsigned int i, ret; int phys_target = kvm_target_cpu(); if (init->target != phys_target) @@ -783,9 +783,14 @@ static int kvm_vcpu_set_target(struct kvm_vcpu *vcpu, vcpu->arch.target = phys_target; /* Now we know what it is, we can reset it. */ - return kvm_reset_vcpu(vcpu); -} + ret = kvm_reset_vcpu(vcpu); + if (ret) { + vcpu->arch.target = -1; + bitmap_zero(vcpu->arch.features, KVM_VCPU_MAX_FEATURES); + } + return ret; +} static int kvm_arch_vcpu_ioctl_vcpu_init(struct kvm_vcpu *vcpu, struct kvm_vcpu_init *init) diff --git a/arch/arm/mach-exynos/firmware.c b/arch/arm/mach-exynos/firmware.c index 111cfbf66fdb..7bfe2bd17400 100644 --- a/arch/arm/mach-exynos/firmware.c +++ b/arch/arm/mach-exynos/firmware.c @@ -207,6 +207,7 @@ void __init exynos_firmware_init(void) return; addr = of_get_address(nd, 0, NULL, NULL); + of_node_put(nd); if (!addr) { pr_err("%s: No address specified.\n", __func__); return; diff --git a/arch/arm/mach-exynos/suspend.c b/arch/arm/mach-exynos/suspend.c index e8adb428dddb..013f4d55ede8 100644 --- a/arch/arm/mach-exynos/suspend.c +++ b/arch/arm/mach-exynos/suspend.c @@ -508,8 +508,27 @@ early_wakeup: static void exynos5420_prepare_pm_resume(void) { + unsigned int mpidr, cluster; + + mpidr = read_cpuid_mpidr(); + cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); + if (IS_ENABLED(CONFIG_EXYNOS5420_MCPM)) WARN_ON(mcpm_cpu_powered_up()); + + if (IS_ENABLED(CONFIG_HW_PERF_EVENTS) && cluster != 0) { + /* + * When system is resumed on the LITTLE/KFC core (cluster 1), + * the DSCR is not properly updated until the power is turned + * on also for the cluster 0. Enable it for a while to + * propagate the SPNIDEN and SPIDEN signals from Secure JTAG + * block and avoid undefined instruction issue on CP14 reset. + */ + pmu_raw_writel(S5P_CORE_LOCAL_PWR_EN, + EXYNOS_COMMON_CONFIGURATION(0)); + pmu_raw_writel(0, + EXYNOS_COMMON_CONFIGURATION(0)); + } } static void exynos5420_pm_resume(void) @@ -725,8 +744,10 @@ void __init exynos_pm_init(void) if (WARN_ON(!of_find_property(np, "interrupt-controller", NULL))) { pr_warn("Outdated DT detected, suspend/resume will NOT work\n"); + of_node_put(np); return; } + of_node_put(np); pm_data = (const struct exynos_pm_data *) match->data; diff --git a/arch/arm/mach-iop13xx/setup.c b/arch/arm/mach-iop13xx/setup.c index 53c316f7301e..fe4932fda01d 100644 --- a/arch/arm/mach-iop13xx/setup.c +++ b/arch/arm/mach-iop13xx/setup.c @@ -300,7 +300,7 @@ static struct resource iop13xx_adma_2_resources[] = { } }; -static u64 iop13xx_adma_dmamask = DMA_BIT_MASK(64); +static u64 iop13xx_adma_dmamask = DMA_BIT_MASK(32); static struct iop_adma_platform_data iop13xx_adma_0_data = { .hw_id = 0, .pool_size = PAGE_SIZE, @@ -324,7 +324,7 @@ static struct platform_device iop13xx_adma_0_channel = { .resource = iop13xx_adma_0_resources, .dev = { .dma_mask = &iop13xx_adma_dmamask, - .coherent_dma_mask = DMA_BIT_MASK(64), + .coherent_dma_mask = DMA_BIT_MASK(32), .platform_data = (void *) &iop13xx_adma_0_data, }, }; @@ -336,7 +336,7 @@ static struct platform_device iop13xx_adma_1_channel = { .resource = iop13xx_adma_1_resources, .dev = { .dma_mask = &iop13xx_adma_dmamask, - .coherent_dma_mask = DMA_BIT_MASK(64), + .coherent_dma_mask = DMA_BIT_MASK(32), .platform_data = (void *) &iop13xx_adma_1_data, }, }; @@ -348,7 +348,7 @@ static struct platform_device iop13xx_adma_2_channel = { .resource = iop13xx_adma_2_resources, .dev = { .dma_mask = &iop13xx_adma_dmamask, - .coherent_dma_mask = DMA_BIT_MASK(64), + .coherent_dma_mask = DMA_BIT_MASK(32), .platform_data = (void *) &iop13xx_adma_2_data, }, }; diff --git a/arch/arm/mach-iop13xx/tpmi.c b/arch/arm/mach-iop13xx/tpmi.c index db511ec2b1df..116feb6b261e 100644 --- a/arch/arm/mach-iop13xx/tpmi.c +++ b/arch/arm/mach-iop13xx/tpmi.c @@ -152,7 +152,7 @@ static struct resource iop13xx_tpmi_3_resources[] = { } }; -u64 iop13xx_tpmi_mask = DMA_BIT_MASK(64); +u64 iop13xx_tpmi_mask = DMA_BIT_MASK(32); static struct platform_device iop13xx_tpmi_0_device = { .name = "iop-tpmi", .id = 0, @@ -160,7 +160,7 @@ static struct platform_device iop13xx_tpmi_0_device = { .resource = iop13xx_tpmi_0_resources, .dev = { .dma_mask = &iop13xx_tpmi_mask, - .coherent_dma_mask = DMA_BIT_MASK(64), + .coherent_dma_mask = DMA_BIT_MASK(32), }, }; @@ -171,7 +171,7 @@ static struct platform_device iop13xx_tpmi_1_device = { .resource = iop13xx_tpmi_1_resources, .dev = { .dma_mask = &iop13xx_tpmi_mask, - .coherent_dma_mask = DMA_BIT_MASK(64), + .coherent_dma_mask = DMA_BIT_MASK(32), }, }; @@ -182,7 +182,7 @@ static struct platform_device iop13xx_tpmi_2_device = { .resource = iop13xx_tpmi_2_resources, .dev = { .dma_mask = &iop13xx_tpmi_mask, - .coherent_dma_mask = DMA_BIT_MASK(64), + .coherent_dma_mask = DMA_BIT_MASK(32), }, }; @@ -193,7 +193,7 @@ static struct platform_device iop13xx_tpmi_3_device = { .resource = iop13xx_tpmi_3_resources, .dev = { .dma_mask = &iop13xx_tpmi_mask, - .coherent_dma_mask = DMA_BIT_MASK(64), + .coherent_dma_mask = DMA_BIT_MASK(32), }, }; diff --git a/arch/arm/plat-iop/adma.c b/arch/arm/plat-iop/adma.c index a4d1f8de3b5b..d9612221e484 100644 --- a/arch/arm/plat-iop/adma.c +++ b/arch/arm/plat-iop/adma.c @@ -143,7 +143,7 @@ struct platform_device iop3xx_dma_0_channel = { .resource = iop3xx_dma_0_resources, .dev = { .dma_mask = &iop3xx_adma_dmamask, - .coherent_dma_mask = DMA_BIT_MASK(64), + .coherent_dma_mask = DMA_BIT_MASK(32), .platform_data = (void *) &iop3xx_dma_0_data, }, }; @@ -155,7 +155,7 @@ struct platform_device iop3xx_dma_1_channel = { .resource = iop3xx_dma_1_resources, .dev = { .dma_mask = &iop3xx_adma_dmamask, - .coherent_dma_mask = DMA_BIT_MASK(64), + .coherent_dma_mask = DMA_BIT_MASK(32), .platform_data = (void *) &iop3xx_dma_1_data, }, }; @@ -167,7 +167,7 @@ struct platform_device iop3xx_aau_channel = { .resource = iop3xx_aau_resources, .dev = { .dma_mask = &iop3xx_adma_dmamask, - .coherent_dma_mask = DMA_BIT_MASK(64), + .coherent_dma_mask = DMA_BIT_MASK(32), .platform_data = (void *) &iop3xx_aau_data, }, }; diff --git a/arch/arm/plat-orion/common.c b/arch/arm/plat-orion/common.c index 8861c367d061..51c3737ddba7 100644 --- a/arch/arm/plat-orion/common.c +++ b/arch/arm/plat-orion/common.c @@ -645,7 +645,7 @@ static struct platform_device orion_xor0_shared = { .resource = orion_xor0_shared_resources, .dev = { .dma_mask = &orion_xor_dmamask, - .coherent_dma_mask = DMA_BIT_MASK(64), + .coherent_dma_mask = DMA_BIT_MASK(32), .platform_data = &orion_xor0_pdata, }, }; @@ -706,7 +706,7 @@ static struct platform_device orion_xor1_shared = { .resource = orion_xor1_shared_resources, .dev = { .dma_mask = &orion_xor_dmamask, - .coherent_dma_mask = DMA_BIT_MASK(64), + .coherent_dma_mask = DMA_BIT_MASK(32), .platform_data = &orion_xor1_pdata, }, }; diff --git a/arch/arm64/configs/cuttlefish_defconfig b/arch/arm64/configs/cuttlefish_defconfig index c4ce7cc2d4e5..c0cf4f692acd 100644 --- a/arch/arm64/configs/cuttlefish_defconfig +++ b/arch/arm64/configs/cuttlefish_defconfig @@ -185,7 +185,6 @@ CONFIG_MAC80211=y # CONFIG_MAC80211_RC_MINSTREL is not set CONFIG_RFKILL=y # CONFIG_UEVENT_HELPER is not set -CONFIG_DEVTMPFS=y # CONFIG_ALLOW_DEV_COREDUMP is not set CONFIG_DEBUG_DEVRES=y CONFIG_OF_UNITTEST=y diff --git a/arch/arm64/configs/msm-perf_defconfig b/arch/arm64/configs/msm-perf_defconfig index 0a180afaa086..878ac2482361 100644 --- a/arch/arm64/configs/msm-perf_defconfig +++ b/arch/arm64/configs/msm-perf_defconfig @@ -237,6 +237,7 @@ CONFIG_RFKILL=y CONFIG_IPC_ROUTER=y CONFIG_IPC_ROUTER_SECURITY=y CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y +CONFIG_REGMAP_ALLOW_WRITE_DEBUGFS=y CONFIG_DMA_CMA=y # CONFIG_PNP_DEBUG_MESSAGES is not set CONFIG_ZRAM=y @@ -370,6 +371,7 @@ CONFIG_REGULATOR_MEM_ACC=y CONFIG_REGULATOR_PROXY_CONSUMER=y CONFIG_MEDIA_SUPPORT=y CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_RADIO_SUPPORT=y CONFIG_MEDIA_CONTROLLER=y CONFIG_VIDEO_V4L2_SUBDEV_API=y CONFIG_V4L_PLATFORM_DRIVERS=y @@ -406,6 +408,7 @@ CONFIG_MSM_VIDC_V4L2=y CONFIG_MSM_VIDC_VMEM=y CONFIG_MSM_VIDC_GOVERNORS=y CONFIG_MSM_SDE_ROTATOR=y +CONFIG_RADIO_SILABS=y CONFIG_QCOM_KGSL=y CONFIG_FB=y CONFIG_FB_MSM=y diff --git a/arch/arm64/configs/msm_defconfig b/arch/arm64/configs/msm_defconfig index 25c5ddea5bdb..3d0d78603f2a 100644 --- a/arch/arm64/configs/msm_defconfig +++ b/arch/arm64/configs/msm_defconfig @@ -230,6 +230,7 @@ CONFIG_RFKILL=y CONFIG_IPC_ROUTER=y CONFIG_IPC_ROUTER_SECURITY=y CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y +CONFIG_REGMAP_ALLOW_WRITE_DEBUGFS=y CONFIG_DMA_CMA=y CONFIG_ZRAM=y CONFIG_BLK_DEV_LOOP=y @@ -357,6 +358,7 @@ CONFIG_REGULATOR_MEM_ACC=y CONFIG_REGULATOR_PROXY_CONSUMER=y CONFIG_MEDIA_SUPPORT=y CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_RADIO_SUPPORT=y CONFIG_MEDIA_CONTROLLER=y CONFIG_VIDEO_V4L2_SUBDEV_API=y CONFIG_VIDEO_ADV_DEBUG=y @@ -394,6 +396,7 @@ CONFIG_MSM_VIDC_V4L2=y CONFIG_MSM_VIDC_VMEM=y CONFIG_MSM_VIDC_GOVERNORS=y CONFIG_MSM_SDE_ROTATOR=y +CONFIG_RADIO_SILABS=y CONFIG_QCOM_KGSL=y CONFIG_FB=y CONFIG_FB_MSM=y diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig index 570a21b464ca..8275c554d8a5 100644 --- a/arch/arm64/configs/msmcortex-perf_defconfig +++ b/arch/arm64/configs/msmcortex-perf_defconfig @@ -609,9 +609,7 @@ CONFIG_ANDROID=y CONFIG_ANDROID_BINDER_IPC=y CONFIG_MSM_TZ_LOG=y CONFIG_SENSORS_SSC=y -CONFIG_EXT2_FS=y -CONFIG_EXT2_FS_XATTR=y -CONFIG_EXT3_FS=y +CONFIG_EXT4_FS=y CONFIG_EXT4_FS_SECURITY=y CONFIG_EXT4_ENCRYPTION=y CONFIG_EXT4_FS_ENCRYPTION=y diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig index 93b9e27c41c7..b97567119ae0 100644 --- a/arch/arm64/configs/msmcortex_defconfig +++ b/arch/arm64/configs/msmcortex_defconfig @@ -636,9 +636,7 @@ CONFIG_ANDROID=y CONFIG_ANDROID_BINDER_IPC=y CONFIG_MSM_TZ_LOG=y CONFIG_SENSORS_SSC=y -CONFIG_EXT2_FS=y -CONFIG_EXT2_FS_XATTR=y -CONFIG_EXT3_FS=y +CONFIG_EXT4_FS=y CONFIG_EXT4_FS_SECURITY=y CONFIG_EXT4_ENCRYPTION=y CONFIG_EXT4_FS_ENCRYPTION=y diff --git a/arch/arm64/kernel/cpu_ops.c b/arch/arm64/kernel/cpu_ops.c index c7cfb8fe06f9..6342f1c61a09 100644 --- a/arch/arm64/kernel/cpu_ops.c +++ b/arch/arm64/kernel/cpu_ops.c @@ -84,6 +84,7 @@ static const char *__init cpu_read_enable_method(int cpu) pr_err("%s: missing enable-method property\n", dn->full_name); } + of_node_put(dn); } else { enable_method = acpi_get_enable_method(cpu); if (!enable_method) { diff --git a/arch/ia64/mm/numa.c b/arch/ia64/mm/numa.c index aa19b7ac8222..476c7b4be378 100644 --- a/arch/ia64/mm/numa.c +++ b/arch/ia64/mm/numa.c @@ -49,6 +49,7 @@ paddr_to_nid(unsigned long paddr) return (i < num_node_memblks) ? node_memblk[i].nid : (num_node_memblks ? -1 : 0); } +EXPORT_SYMBOL(paddr_to_nid); #if defined(CONFIG_SPARSEMEM) && defined(CONFIG_NUMA) /* diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S index 87c697181d25..4faff3e77b25 100644 --- a/arch/mips/kernel/scall64-o32.S +++ b/arch/mips/kernel/scall64-o32.S @@ -126,7 +126,7 @@ trace_a_syscall: subu t1, v0, __NR_O32_Linux move a1, v0 bnez t1, 1f /* __NR_syscall at offset 0 */ - lw a1, PT_R4(sp) /* Arg1 for __NR_syscall case */ + ld a1, PT_R4(sp) /* Arg1 for __NR_syscall case */ .set pop 1: jal syscall_trace_enter diff --git a/arch/mips/pistachio/Platform b/arch/mips/pistachio/Platform index d80cd612df1f..c3592b374ad2 100644 --- a/arch/mips/pistachio/Platform +++ b/arch/mips/pistachio/Platform @@ -6,3 +6,4 @@ cflags-$(CONFIG_MACH_PISTACHIO) += \ -I$(srctree)/arch/mips/include/asm/mach-pistachio load-$(CONFIG_MACH_PISTACHIO) += 0xffffffff80400000 zload-$(CONFIG_MACH_PISTACHIO) += 0xffffffff81000000 +all-$(CONFIG_MACH_PISTACHIO) := uImage.gz diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index f3a47e9962c9..9058c060374e 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -137,7 +137,7 @@ config PPC select GENERIC_SMP_IDLE_THREAD select GENERIC_CMOS_UPDATE select GENERIC_TIME_VSYSCALL_OLD - select GENERIC_CPU_VULNERABILITIES if PPC_BOOK3S_64 + select GENERIC_CPU_VULNERABILITIES if PPC_BARRIER_NOSPEC select GENERIC_CLOCKEVENTS select GENERIC_CLOCKEVENTS_BROADCAST if SMP select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST @@ -164,6 +164,11 @@ config PPC select HAVE_ARCH_SECCOMP_FILTER select HAVE_ARCH_HARDENED_USERCOPY +config PPC_BARRIER_NOSPEC + bool + default y + depends on PPC_BOOK3S_64 || PPC_FSL_BOOK3E + config GENERIC_CSUM def_bool CPU_LITTLE_ENDIAN diff --git a/arch/powerpc/boot/addnote.c b/arch/powerpc/boot/addnote.c index 9d9f6f334d3c..3da3e2b1b51b 100644 --- a/arch/powerpc/boot/addnote.c +++ b/arch/powerpc/boot/addnote.c @@ -223,7 +223,11 @@ main(int ac, char **av) PUT_16(E_PHNUM, np + 2); /* write back */ - lseek(fd, (long) 0, SEEK_SET); + i = lseek(fd, (long) 0, SEEK_SET); + if (i < 0) { + perror("lseek"); + exit(1); + } i = write(fd, buf, n); if (i < 0) { perror("write"); diff --git a/arch/powerpc/include/asm/asm-prototypes.h b/arch/powerpc/include/asm/asm-prototypes.h new file mode 100644 index 000000000000..8944c55591cf --- /dev/null +++ b/arch/powerpc/include/asm/asm-prototypes.h @@ -0,0 +1,21 @@ +#ifndef _ASM_POWERPC_ASM_PROTOTYPES_H +#define _ASM_POWERPC_ASM_PROTOTYPES_H +/* + * This file is for prototypes of C functions that are only called + * from asm, and any associated variables. + * + * Copyright 2016, Daniel Axtens, IBM Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + */ + +/* Patch sites */ +extern s32 patch__call_flush_count_cache; +extern s32 patch__flush_count_cache_return; + +extern long flush_count_cache; + +#endif /* _ASM_POWERPC_ASM_PROTOTYPES_H */ diff --git a/arch/powerpc/include/asm/barrier.h b/arch/powerpc/include/asm/barrier.h index b9e16855a037..e7cb72cdb2ba 100644 --- a/arch/powerpc/include/asm/barrier.h +++ b/arch/powerpc/include/asm/barrier.h @@ -92,4 +92,25 @@ do { \ #define smp_mb__after_atomic() smp_mb() #define smp_mb__before_spinlock() smp_mb() +#ifdef CONFIG_PPC_BOOK3S_64 +#define NOSPEC_BARRIER_SLOT nop +#elif defined(CONFIG_PPC_FSL_BOOK3E) +#define NOSPEC_BARRIER_SLOT nop; nop +#endif + +#ifdef CONFIG_PPC_BARRIER_NOSPEC +/* + * Prevent execution of subsequent instructions until preceding branches have + * been fully resolved and are no longer executing speculatively. + */ +#define barrier_nospec_asm NOSPEC_BARRIER_FIXUP_SECTION; NOSPEC_BARRIER_SLOT + +// This also acts as a compiler barrier due to the memory clobber. +#define barrier_nospec() asm (stringify_in_c(barrier_nospec_asm) ::: "memory") + +#else /* !CONFIG_PPC_BARRIER_NOSPEC */ +#define barrier_nospec_asm +#define barrier_nospec() +#endif /* CONFIG_PPC_BARRIER_NOSPEC */ + #endif /* _ASM_POWERPC_BARRIER_H */ diff --git a/arch/powerpc/include/asm/code-patching-asm.h b/arch/powerpc/include/asm/code-patching-asm.h new file mode 100644 index 000000000000..ed7b1448493a --- /dev/null +++ b/arch/powerpc/include/asm/code-patching-asm.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2018, Michael Ellerman, IBM Corporation. + */ +#ifndef _ASM_POWERPC_CODE_PATCHING_ASM_H +#define _ASM_POWERPC_CODE_PATCHING_ASM_H + +/* Define a "site" that can be patched */ +.macro patch_site label name + .pushsection ".rodata" + .balign 4 + .global \name +\name: + .4byte \label - . + .popsection +.endm + +#endif /* _ASM_POWERPC_CODE_PATCHING_ASM_H */ diff --git a/arch/powerpc/include/asm/code-patching.h b/arch/powerpc/include/asm/code-patching.h index 840a5509b3f1..a734b4b34d26 100644 --- a/arch/powerpc/include/asm/code-patching.h +++ b/arch/powerpc/include/asm/code-patching.h @@ -28,6 +28,8 @@ unsigned int create_cond_branch(const unsigned int *addr, unsigned long target, int flags); int patch_branch(unsigned int *addr, unsigned long target, int flags); int patch_instruction(unsigned int *addr, unsigned int instr); +int patch_instruction_site(s32 *addr, unsigned int instr); +int patch_branch_site(s32 *site, unsigned long target, int flags); int instr_is_relative_branch(unsigned int instr); int instr_is_branch_to_addr(const unsigned int *instr, unsigned long addr); diff --git a/arch/powerpc/include/asm/exception-64s.h b/arch/powerpc/include/asm/exception-64s.h index 9bddbec441b8..3ed536bec462 100644 --- a/arch/powerpc/include/asm/exception-64s.h +++ b/arch/powerpc/include/asm/exception-64s.h @@ -50,6 +50,27 @@ #define EX_PPR 88 /* SMT thread status register (priority) */ #define EX_CTR 96 +#define STF_ENTRY_BARRIER_SLOT \ + STF_ENTRY_BARRIER_FIXUP_SECTION; \ + nop; \ + nop; \ + nop + +#define STF_EXIT_BARRIER_SLOT \ + STF_EXIT_BARRIER_FIXUP_SECTION; \ + nop; \ + nop; \ + nop; \ + nop; \ + nop; \ + nop + +/* + * r10 must be free to use, r13 must be paca + */ +#define INTERRUPT_TO_KERNEL \ + STF_ENTRY_BARRIER_SLOT + /* * Macros for annotating the expected destination of (h)rfid * @@ -66,16 +87,19 @@ rfid #define RFI_TO_USER \ + STF_EXIT_BARRIER_SLOT; \ RFI_FLUSH_SLOT; \ rfid; \ b rfi_flush_fallback #define RFI_TO_USER_OR_KERNEL \ + STF_EXIT_BARRIER_SLOT; \ RFI_FLUSH_SLOT; \ rfid; \ b rfi_flush_fallback #define RFI_TO_GUEST \ + STF_EXIT_BARRIER_SLOT; \ RFI_FLUSH_SLOT; \ rfid; \ b rfi_flush_fallback @@ -84,21 +108,25 @@ hrfid #define HRFI_TO_USER \ + STF_EXIT_BARRIER_SLOT; \ RFI_FLUSH_SLOT; \ hrfid; \ b hrfi_flush_fallback #define HRFI_TO_USER_OR_KERNEL \ + STF_EXIT_BARRIER_SLOT; \ RFI_FLUSH_SLOT; \ hrfid; \ b hrfi_flush_fallback #define HRFI_TO_GUEST \ + STF_EXIT_BARRIER_SLOT; \ RFI_FLUSH_SLOT; \ hrfid; \ b hrfi_flush_fallback #define HRFI_TO_UNKNOWN \ + STF_EXIT_BARRIER_SLOT; \ RFI_FLUSH_SLOT; \ hrfid; \ b hrfi_flush_fallback @@ -226,6 +254,7 @@ END_FTR_SECTION_NESTED(ftr,ftr,943) #define __EXCEPTION_PROLOG_1(area, extra, vec) \ OPT_SAVE_REG_TO_PACA(area+EX_PPR, r9, CPU_FTR_HAS_PPR); \ OPT_SAVE_REG_TO_PACA(area+EX_CFAR, r10, CPU_FTR_CFAR); \ + INTERRUPT_TO_KERNEL; \ SAVE_CTR(r10, area); \ mfcr r9; \ extra(vec); \ @@ -512,6 +541,12 @@ label##_relon_hv: \ #define _MASKABLE_EXCEPTION_PSERIES(vec, label, h, extra) \ __MASKABLE_EXCEPTION_PSERIES(vec, label, h, extra) +#define MASKABLE_EXCEPTION_OOL(vec, label) \ + .globl label##_ool; \ +label##_ool: \ + EXCEPTION_PROLOG_1(PACA_EXGEN, SOFTEN_TEST_PR, vec); \ + EXCEPTION_PROLOG_PSERIES_1(label##_common, EXC_STD); + #define MASKABLE_EXCEPTION_PSERIES(loc, vec, label) \ . = loc; \ .globl label##_pSeries; \ diff --git a/arch/powerpc/include/asm/feature-fixups.h b/arch/powerpc/include/asm/feature-fixups.h index 7068bafbb2d6..145a37ab2d3e 100644 --- a/arch/powerpc/include/asm/feature-fixups.h +++ b/arch/powerpc/include/asm/feature-fixups.h @@ -184,6 +184,22 @@ label##3: \ FTR_ENTRY_OFFSET label##1b-label##3b; \ .popsection; +#define STF_ENTRY_BARRIER_FIXUP_SECTION \ +953: \ + .pushsection __stf_entry_barrier_fixup,"a"; \ + .align 2; \ +954: \ + FTR_ENTRY_OFFSET 953b-954b; \ + .popsection; + +#define STF_EXIT_BARRIER_FIXUP_SECTION \ +955: \ + .pushsection __stf_exit_barrier_fixup,"a"; \ + .align 2; \ +956: \ + FTR_ENTRY_OFFSET 955b-956b; \ + .popsection; + #define RFI_FLUSH_FIXUP_SECTION \ 951: \ .pushsection __rfi_flush_fixup,"a"; \ @@ -192,10 +208,34 @@ label##3: \ FTR_ENTRY_OFFSET 951b-952b; \ .popsection; +#define NOSPEC_BARRIER_FIXUP_SECTION \ +953: \ + .pushsection __barrier_nospec_fixup,"a"; \ + .align 2; \ +954: \ + FTR_ENTRY_OFFSET 953b-954b; \ + .popsection; + +#define START_BTB_FLUSH_SECTION \ +955: \ + +#define END_BTB_FLUSH_SECTION \ +956: \ + .pushsection __btb_flush_fixup,"a"; \ + .align 2; \ +957: \ + FTR_ENTRY_OFFSET 955b-957b; \ + FTR_ENTRY_OFFSET 956b-957b; \ + .popsection; #ifndef __ASSEMBLY__ +extern long stf_barrier_fallback; +extern long __start___stf_entry_barrier_fixup, __stop___stf_entry_barrier_fixup; +extern long __start___stf_exit_barrier_fixup, __stop___stf_exit_barrier_fixup; extern long __start___rfi_flush_fixup, __stop___rfi_flush_fixup; +extern long __start___barrier_nospec_fixup, __stop___barrier_nospec_fixup; +extern long __start__btb_flush_fixup, __stop__btb_flush_fixup; #endif diff --git a/arch/powerpc/include/asm/hvcall.h b/arch/powerpc/include/asm/hvcall.h index 449bbb87c257..b57db9d09db9 100644 --- a/arch/powerpc/include/asm/hvcall.h +++ b/arch/powerpc/include/asm/hvcall.h @@ -292,10 +292,15 @@ #define H_CPU_CHAR_L1D_FLUSH_ORI30 (1ull << 61) // IBM bit 2 #define H_CPU_CHAR_L1D_FLUSH_TRIG2 (1ull << 60) // IBM bit 3 #define H_CPU_CHAR_L1D_THREAD_PRIV (1ull << 59) // IBM bit 4 +#define H_CPU_CHAR_BRANCH_HINTS_HONORED (1ull << 58) // IBM bit 5 +#define H_CPU_CHAR_THREAD_RECONFIG_CTRL (1ull << 57) // IBM bit 6 +#define H_CPU_CHAR_COUNT_CACHE_DISABLED (1ull << 56) // IBM bit 7 +#define H_CPU_CHAR_BCCTR_FLUSH_ASSIST (1ull << 54) // IBM bit 9 #define H_CPU_BEHAV_FAVOUR_SECURITY (1ull << 63) // IBM bit 0 #define H_CPU_BEHAV_L1D_FLUSH_PR (1ull << 62) // IBM bit 1 #define H_CPU_BEHAV_BNDS_CHK_SPEC_BAR (1ull << 61) // IBM bit 2 +#define H_CPU_BEHAV_FLUSH_COUNT_CACHE (1ull << 58) // IBM bit 5 #ifndef __ASSEMBLY__ #include <linux/types.h> diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h index a92d95aee42d..1883627eb12c 100644 --- a/arch/powerpc/include/asm/kvm_host.h +++ b/arch/powerpc/include/asm/kvm_host.h @@ -250,6 +250,7 @@ struct kvm_arch { #ifdef CONFIG_PPC_BOOK3S_64 struct list_head spapr_tce_tables; struct list_head rtas_tokens; + struct mutex rtas_token_lock; DECLARE_BITMAP(enabled_hcalls, MAX_HCALL_OPCODE/4 + 1); #endif #ifdef CONFIG_KVM_MPIC diff --git a/arch/powerpc/include/asm/paca.h b/arch/powerpc/include/asm/paca.h index 45e2aefece16..08e5df3395fa 100644 --- a/arch/powerpc/include/asm/paca.h +++ b/arch/powerpc/include/asm/paca.h @@ -199,8 +199,7 @@ struct paca_struct { */ u64 exrfi[13] __aligned(0x80); void *rfi_flush_fallback_area; - u64 l1d_flush_congruence; - u64 l1d_flush_sets; + u64 l1d_flush_size; #endif }; diff --git a/arch/powerpc/include/asm/ppc-opcode.h b/arch/powerpc/include/asm/ppc-opcode.h index 7ab04fc59e24..faf1bb045dee 100644 --- a/arch/powerpc/include/asm/ppc-opcode.h +++ b/arch/powerpc/include/asm/ppc-opcode.h @@ -147,6 +147,7 @@ #define PPC_INST_LWSYNC 0x7c2004ac #define PPC_INST_SYNC 0x7c0004ac #define PPC_INST_SYNC_MASK 0xfc0007fe +#define PPC_INST_ISYNC 0x4c00012c #define PPC_INST_LXVD2X 0x7c000698 #define PPC_INST_MCRXR 0x7c000400 #define PPC_INST_MCRXR_MASK 0xfc0007fe diff --git a/arch/powerpc/include/asm/ppc_asm.h b/arch/powerpc/include/asm/ppc_asm.h index 160bb2311bbb..d219816b3e19 100644 --- a/arch/powerpc/include/asm/ppc_asm.h +++ b/arch/powerpc/include/asm/ppc_asm.h @@ -821,4 +821,15 @@ END_FTR_SECTION_NESTED(CPU_FTR_HAS_PPR,CPU_FTR_HAS_PPR,945) .long 0x2400004c /* rfid */ #endif /* !CONFIG_PPC_BOOK3E */ #endif /* __ASSEMBLY__ */ + +#ifdef CONFIG_PPC_FSL_BOOK3E +#define BTB_FLUSH(reg) \ + lis reg,BUCSR_INIT@h; \ + ori reg,reg,BUCSR_INIT@l; \ + mtspr SPRN_BUCSR,reg; \ + isync; +#else +#define BTB_FLUSH(reg) +#endif /* CONFIG_PPC_FSL_BOOK3E */ + #endif /* _ASM_POWERPC_PPC_ASM_H */ diff --git a/arch/powerpc/include/asm/reg_booke.h b/arch/powerpc/include/asm/reg_booke.h index 2fef74b474f0..410ebee9e339 100644 --- a/arch/powerpc/include/asm/reg_booke.h +++ b/arch/powerpc/include/asm/reg_booke.h @@ -41,7 +41,7 @@ #if defined(CONFIG_PPC_BOOK3E_64) #define MSR_64BIT MSR_CM -#define MSR_ (MSR_ME | MSR_CE) +#define MSR_ (MSR_ME | MSR_RI | MSR_CE) #define MSR_KERNEL (MSR_ | MSR_64BIT) #define MSR_USER32 (MSR_ | MSR_PR | MSR_EE) #define MSR_USER64 (MSR_USER32 | MSR_64BIT) diff --git a/arch/powerpc/include/asm/security_features.h b/arch/powerpc/include/asm/security_features.h new file mode 100644 index 000000000000..759597bf0fd8 --- /dev/null +++ b/arch/powerpc/include/asm/security_features.h @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Security related feature bit definitions. + * + * Copyright 2018, Michael Ellerman, IBM Corporation. + */ + +#ifndef _ASM_POWERPC_SECURITY_FEATURES_H +#define _ASM_POWERPC_SECURITY_FEATURES_H + + +extern unsigned long powerpc_security_features; +extern bool rfi_flush; + +/* These are bit flags */ +enum stf_barrier_type { + STF_BARRIER_NONE = 0x1, + STF_BARRIER_FALLBACK = 0x2, + STF_BARRIER_EIEIO = 0x4, + STF_BARRIER_SYNC_ORI = 0x8, +}; + +void setup_stf_barrier(void); +void do_stf_barrier_fixups(enum stf_barrier_type types); +void setup_count_cache_flush(void); + +static inline void security_ftr_set(unsigned long feature) +{ + powerpc_security_features |= feature; +} + +static inline void security_ftr_clear(unsigned long feature) +{ + powerpc_security_features &= ~feature; +} + +static inline bool security_ftr_enabled(unsigned long feature) +{ + return !!(powerpc_security_features & feature); +} + + +// Features indicating support for Spectre/Meltdown mitigations + +// The L1-D cache can be flushed with ori r30,r30,0 +#define SEC_FTR_L1D_FLUSH_ORI30 0x0000000000000001ull + +// The L1-D cache can be flushed with mtspr 882,r0 (aka SPRN_TRIG2) +#define SEC_FTR_L1D_FLUSH_TRIG2 0x0000000000000002ull + +// ori r31,r31,0 acts as a speculation barrier +#define SEC_FTR_SPEC_BAR_ORI31 0x0000000000000004ull + +// Speculation past bctr is disabled +#define SEC_FTR_BCCTRL_SERIALISED 0x0000000000000008ull + +// Entries in L1-D are private to a SMT thread +#define SEC_FTR_L1D_THREAD_PRIV 0x0000000000000010ull + +// Indirect branch prediction cache disabled +#define SEC_FTR_COUNT_CACHE_DISABLED 0x0000000000000020ull + +// bcctr 2,0,0 triggers a hardware assisted count cache flush +#define SEC_FTR_BCCTR_FLUSH_ASSIST 0x0000000000000800ull + + +// Features indicating need for Spectre/Meltdown mitigations + +// The L1-D cache should be flushed on MSR[HV] 1->0 transition (hypervisor to guest) +#define SEC_FTR_L1D_FLUSH_HV 0x0000000000000040ull + +// The L1-D cache should be flushed on MSR[PR] 0->1 transition (kernel to userspace) +#define SEC_FTR_L1D_FLUSH_PR 0x0000000000000080ull + +// A speculation barrier should be used for bounds checks (Spectre variant 1) +#define SEC_FTR_BNDS_CHK_SPEC_BAR 0x0000000000000100ull + +// Firmware configuration indicates user favours security over performance +#define SEC_FTR_FAVOUR_SECURITY 0x0000000000000200ull + +// Software required to flush count cache on context switch +#define SEC_FTR_FLUSH_COUNT_CACHE 0x0000000000000400ull + + +// Features enabled by default +#define SEC_FTR_DEFAULT \ + (SEC_FTR_L1D_FLUSH_HV | \ + SEC_FTR_L1D_FLUSH_PR | \ + SEC_FTR_BNDS_CHK_SPEC_BAR | \ + SEC_FTR_FAVOUR_SECURITY) + +#endif /* _ASM_POWERPC_SECURITY_FEATURES_H */ diff --git a/arch/powerpc/include/asm/setup.h b/arch/powerpc/include/asm/setup.h index 7916b56f2e60..d299479c770b 100644 --- a/arch/powerpc/include/asm/setup.h +++ b/arch/powerpc/include/asm/setup.h @@ -8,6 +8,7 @@ extern void ppc_printk_progress(char *s, unsigned short hex); extern unsigned int rtas_data; extern unsigned long long memory_limit; +extern bool init_mem_is_free; extern unsigned long klimit; extern void *zalloc_maybe_bootmem(size_t size, gfp_t mask); @@ -36,8 +37,28 @@ enum l1d_flush_type { L1D_FLUSH_MTTRIG = 0x8, }; -void __init setup_rfi_flush(enum l1d_flush_type, bool enable); +void setup_rfi_flush(enum l1d_flush_type, bool enable); void do_rfi_flush_fixups(enum l1d_flush_type types); +#ifdef CONFIG_PPC_BARRIER_NOSPEC +void setup_barrier_nospec(void); +#else +static inline void setup_barrier_nospec(void) { }; +#endif +void do_barrier_nospec_fixups(bool enable); +extern bool barrier_nospec_enabled; + +#ifdef CONFIG_PPC_BARRIER_NOSPEC +void do_barrier_nospec_fixups_range(bool enable, void *start, void *end); +#else +static inline void do_barrier_nospec_fixups_range(bool enable, void *start, void *end) { }; +#endif + +#ifdef CONFIG_PPC_FSL_BOOK3E +void setup_spectre_v2(void); +#else +static inline void setup_spectre_v2(void) {}; +#endif +void do_btb_flush_fixups(void); #endif /* !__ASSEMBLY__ */ diff --git a/arch/powerpc/include/asm/uaccess.h b/arch/powerpc/include/asm/uaccess.h index 1542c4e009c8..f86fb4243a08 100644 --- a/arch/powerpc/include/asm/uaccess.h +++ b/arch/powerpc/include/asm/uaccess.h @@ -269,6 +269,7 @@ do { \ __chk_user_ptr(ptr); \ if (!is_kernel_addr((unsigned long)__gu_addr)) \ might_fault(); \ + barrier_nospec(); \ __get_user_size(__gu_val, __gu_addr, (size), __gu_err); \ (x) = (__typeof__(*(ptr)))__gu_val; \ __gu_err; \ @@ -283,6 +284,7 @@ do { \ __chk_user_ptr(ptr); \ if (!is_kernel_addr((unsigned long)__gu_addr)) \ might_fault(); \ + barrier_nospec(); \ __get_user_size(__gu_val, __gu_addr, (size), __gu_err); \ (x) = (__force __typeof__(*(ptr)))__gu_val; \ __gu_err; \ @@ -295,8 +297,10 @@ do { \ unsigned long __gu_val = 0; \ __typeof__(*(ptr)) __user *__gu_addr = (ptr); \ might_fault(); \ - if (access_ok(VERIFY_READ, __gu_addr, (size))) \ + if (access_ok(VERIFY_READ, __gu_addr, (size))) { \ + barrier_nospec(); \ __get_user_size(__gu_val, __gu_addr, (size), __gu_err); \ + } \ (x) = (__force __typeof__(*(ptr)))__gu_val; \ __gu_err; \ }) @@ -307,6 +311,7 @@ do { \ unsigned long __gu_val; \ __typeof__(*(ptr)) __user *__gu_addr = (ptr); \ __chk_user_ptr(ptr); \ + barrier_nospec(); \ __get_user_size(__gu_val, __gu_addr, (size), __gu_err); \ (x) = (__force __typeof__(*(ptr)))__gu_val; \ __gu_err; \ @@ -325,6 +330,7 @@ static inline unsigned long copy_from_user(void *to, { if (likely(access_ok(VERIFY_READ, from, n))) { check_object_size(to, n, false); + barrier_nospec(); return __copy_tofrom_user((__force void __user *)to, from, n); } memset(to, 0, n); @@ -363,15 +369,19 @@ static inline unsigned long __copy_from_user_inatomic(void *to, switch (n) { case 1: + barrier_nospec(); __get_user_size(*(u8 *)to, from, 1, ret); break; case 2: + barrier_nospec(); __get_user_size(*(u16 *)to, from, 2, ret); break; case 4: + barrier_nospec(); __get_user_size(*(u32 *)to, from, 4, ret); break; case 8: + barrier_nospec(); __get_user_size(*(u64 *)to, from, 8, ret); break; } @@ -381,6 +391,7 @@ static inline unsigned long __copy_from_user_inatomic(void *to, check_object_size(to, n, false); + barrier_nospec(); return __copy_tofrom_user((__force void __user *)to, from, n); } diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index ba336930d448..22ed3c32fca8 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -44,6 +44,7 @@ obj-$(CONFIG_PPC_BOOK3S_64) += cpu_setup_power.o obj-$(CONFIG_PPC_BOOK3S_64) += mce.o mce_power.o obj64-$(CONFIG_RELOCATABLE) += reloc_64.o obj-$(CONFIG_PPC_BOOK3E_64) += exceptions-64e.o idle_book3e.o +obj-$(CONFIG_PPC_BARRIER_NOSPEC) += security.o obj-$(CONFIG_PPC64) += vdso64/ obj-$(CONFIG_ALTIVEC) += vecemu.o obj-$(CONFIG_PPC_970_NAP) += idle_power4.o diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index d92705e3a0c1..de3c29c51503 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -245,8 +245,7 @@ int main(void) DEFINE(PACA_IN_MCE, offsetof(struct paca_struct, in_mce)); DEFINE(PACA_RFI_FLUSH_FALLBACK_AREA, offsetof(struct paca_struct, rfi_flush_fallback_area)); DEFINE(PACA_EXRFI, offsetof(struct paca_struct, exrfi)); - DEFINE(PACA_L1D_FLUSH_CONGRUENCE, offsetof(struct paca_struct, l1d_flush_congruence)); - DEFINE(PACA_L1D_FLUSH_SETS, offsetof(struct paca_struct, l1d_flush_sets)); + DEFINE(PACA_L1D_FLUSH_SIZE, offsetof(struct paca_struct, l1d_flush_size)); #endif DEFINE(PACAHWCPUID, offsetof(struct paca_struct, hw_cpu_id)); DEFINE(PACAKEXECSTATE, offsetof(struct paca_struct, kexec_state)); diff --git a/arch/powerpc/kernel/entry_32.S b/arch/powerpc/kernel/entry_32.S index 3728e617e17e..609bc7d01f13 100644 --- a/arch/powerpc/kernel/entry_32.S +++ b/arch/powerpc/kernel/entry_32.S @@ -33,6 +33,7 @@ #include <asm/unistd.h> #include <asm/ftrace.h> #include <asm/ptrace.h> +#include <asm/barrier.h> /* * MSR_KERNEL is > 0x10000 on 4xx/Book-E since it include MSR_CE. @@ -340,6 +341,15 @@ syscall_dotrace_cont: ori r10,r10,sys_call_table@l slwi r0,r0,2 bge- 66f + + barrier_nospec_asm + /* + * Prevent the load of the handler below (based on the user-passed + * system call number) being speculatively executed until the test + * against NR_syscalls and branch to .66f above has + * committed. + */ + lwzx r10,r10,r0 /* Fetch system call handler [ptr] */ mtlr r10 addi r9,r1,STACK_FRAME_OVERHEAD diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index 59be96917369..6d36a4fb4acf 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -25,6 +25,7 @@ #include <asm/page.h> #include <asm/mmu.h> #include <asm/thread_info.h> +#include <asm/code-patching-asm.h> #include <asm/ppc_asm.h> #include <asm/asm-offsets.h> #include <asm/cputable.h> @@ -36,6 +37,7 @@ #include <asm/hw_irq.h> #include <asm/context_tracking.h> #include <asm/tm.h> +#include <asm/barrier.h> #ifdef CONFIG_PPC_BOOK3S #include <asm/exception-64s.h> #else @@ -75,6 +77,11 @@ END_FTR_SECTION_IFSET(CPU_FTR_TM) std r0,GPR0(r1) std r10,GPR1(r1) beq 2f /* if from kernel mode */ +#ifdef CONFIG_PPC_FSL_BOOK3E +START_BTB_FLUSH_SECTION + BTB_FLUSH(r10) +END_BTB_FLUSH_SECTION +#endif ACCOUNT_CPU_USER_ENTRY(r10, r11) 2: std r2,GPR2(r1) std r3,GPR3(r1) @@ -177,6 +184,15 @@ system_call: /* label this so stack traces look sane */ clrldi r8,r8,32 15: slwi r0,r0,4 + + barrier_nospec_asm + /* + * Prevent the load of the handler below (based on the user-passed + * system call number) being speculatively executed until the test + * against NR_syscalls and branch to .Lsyscall_enosys above has + * committed. + */ + ldx r12,r11,r0 /* Fetch system call handler [ptr] */ mtctr r12 bctrl /* Call handler */ @@ -440,6 +456,57 @@ _GLOBAL(ret_from_kernel_thread) li r3,0 b .Lsyscall_exit +#ifdef CONFIG_PPC_BOOK3S_64 + +#define FLUSH_COUNT_CACHE \ +1: nop; \ + patch_site 1b, patch__call_flush_count_cache + + +#define BCCTR_FLUSH .long 0x4c400420 + +.macro nops number + .rept \number + nop + .endr +.endm + +.balign 32 +.global flush_count_cache +flush_count_cache: + /* Save LR into r9 */ + mflr r9 + + .rept 64 + bl .+4 + .endr + b 1f + nops 6 + + .balign 32 + /* Restore LR */ +1: mtlr r9 + li r9,0x7fff + mtctr r9 + + BCCTR_FLUSH + +2: nop + patch_site 2b patch__flush_count_cache_return + + nops 3 + + .rept 278 + .balign 32 + BCCTR_FLUSH + nops 7 + .endr + + blr +#else +#define FLUSH_COUNT_CACHE +#endif /* CONFIG_PPC_BOOK3S_64 */ + /* * This routine switches between two different tasks. The process * state of one is saved on its kernel stack. Then the state @@ -503,6 +570,8 @@ BEGIN_FTR_SECTION END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S) #endif + FLUSH_COUNT_CACHE + #ifdef CONFIG_SMP /* We need a sync somewhere here to make sure that if the * previous task gets rescheduled on another CPU, it sees all diff --git a/arch/powerpc/kernel/exceptions-64e.S b/arch/powerpc/kernel/exceptions-64e.S index 5cc93f0b52ca..48ec841ea1bf 100644 --- a/arch/powerpc/kernel/exceptions-64e.S +++ b/arch/powerpc/kernel/exceptions-64e.S @@ -295,7 +295,8 @@ ret_from_mc_except: andi. r10,r11,MSR_PR; /* save stack pointer */ \ beq 1f; /* branch around if supervisor */ \ ld r1,PACAKSAVE(r13); /* get kernel stack coming from usr */\ -1: cmpdi cr1,r1,0; /* check if SP makes sense */ \ +1: type##_BTB_FLUSH \ + cmpdi cr1,r1,0; /* check if SP makes sense */ \ bge- cr1,exc_##n##_bad_stack;/* bad stack (TODO: out of line) */ \ mfspr r10,SPRN_##type##_SRR0; /* read SRR0 before touching stack */ @@ -327,6 +328,30 @@ ret_from_mc_except: #define SPRN_MC_SRR0 SPRN_MCSRR0 #define SPRN_MC_SRR1 SPRN_MCSRR1 +#ifdef CONFIG_PPC_FSL_BOOK3E +#define GEN_BTB_FLUSH \ + START_BTB_FLUSH_SECTION \ + beq 1f; \ + BTB_FLUSH(r10) \ + 1: \ + END_BTB_FLUSH_SECTION + +#define CRIT_BTB_FLUSH \ + START_BTB_FLUSH_SECTION \ + BTB_FLUSH(r10) \ + END_BTB_FLUSH_SECTION + +#define DBG_BTB_FLUSH CRIT_BTB_FLUSH +#define MC_BTB_FLUSH CRIT_BTB_FLUSH +#define GDBELL_BTB_FLUSH GEN_BTB_FLUSH +#else +#define GEN_BTB_FLUSH +#define CRIT_BTB_FLUSH +#define DBG_BTB_FLUSH +#define MC_BTB_FLUSH +#define GDBELL_BTB_FLUSH +#endif + #define NORMAL_EXCEPTION_PROLOG(n, intnum, addition) \ EXCEPTION_PROLOG(n, intnum, GEN, addition##_GEN(n)) diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index 938a30fef031..10e7cec9553d 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -36,6 +36,7 @@ BEGIN_FTR_SECTION \ END_FTR_SECTION_IFSET(CPU_FTR_REAL_LE) \ mr r9,r13 ; \ GET_PACA(r13) ; \ + INTERRUPT_TO_KERNEL ; \ mfspr r11,SPRN_SRR0 ; \ 0: @@ -292,7 +293,9 @@ hardware_interrupt_hv: . = 0x900 .globl decrementer_pSeries decrementer_pSeries: - _MASKABLE_EXCEPTION_PSERIES(0x900, decrementer, EXC_STD, SOFTEN_TEST_PR) + SET_SCRATCH0(r13) + EXCEPTION_PROLOG_0(PACA_EXGEN) + b decrementer_ool STD_EXCEPTION_HV(0x980, 0x982, hdecrementer) @@ -319,6 +322,7 @@ system_call_pSeries: OPT_GET_SPR(r9, SPRN_PPR, CPU_FTR_HAS_PPR); HMT_MEDIUM; std r10,PACA_EXGEN+EX_R10(r13) + INTERRUPT_TO_KERNEL OPT_SAVE_REG_TO_PACA(PACA_EXGEN+EX_PPR, r9, CPU_FTR_HAS_PPR); mfcr r9 KVMTEST(0xc00) @@ -607,6 +611,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_CFAR) .align 7 /* moved from 0xe00 */ + MASKABLE_EXCEPTION_OOL(0x900, decrementer) STD_EXCEPTION_HV_OOL(0xe02, h_data_storage) KVM_HANDLER_SKIP(PACA_EXGEN, EXC_HV, 0xe02) STD_EXCEPTION_HV_OOL(0xe22, h_instr_storage) @@ -1564,6 +1569,21 @@ power4_fixup_nap: blr #endif + .balign 16 + .globl stf_barrier_fallback +stf_barrier_fallback: + std r9,PACA_EXRFI+EX_R9(r13) + std r10,PACA_EXRFI+EX_R10(r13) + sync + ld r9,PACA_EXRFI+EX_R9(r13) + ld r10,PACA_EXRFI+EX_R10(r13) + ori 31,31,0 + .rept 14 + b 1f +1: + .endr + blr + .globl rfi_flush_fallback rfi_flush_fallback: SET_SCRATCH0(r13); @@ -1571,39 +1591,37 @@ rfi_flush_fallback: std r9,PACA_EXRFI+EX_R9(r13) std r10,PACA_EXRFI+EX_R10(r13) std r11,PACA_EXRFI+EX_R11(r13) - std r12,PACA_EXRFI+EX_R12(r13) - std r8,PACA_EXRFI+EX_R13(r13) mfctr r9 ld r10,PACA_RFI_FLUSH_FALLBACK_AREA(r13) - ld r11,PACA_L1D_FLUSH_SETS(r13) - ld r12,PACA_L1D_FLUSH_CONGRUENCE(r13) - /* - * The load adresses are at staggered offsets within cachelines, - * which suits some pipelines better (on others it should not - * hurt). - */ - addi r12,r12,8 + ld r11,PACA_L1D_FLUSH_SIZE(r13) + srdi r11,r11,(7 + 3) /* 128 byte lines, unrolled 8x */ mtctr r11 DCBT_STOP_ALL_STREAM_IDS(r11) /* Stop prefetch streams */ /* order ld/st prior to dcbt stop all streams with flushing */ sync -1: li r8,0 - .rept 8 /* 8-way set associative */ - ldx r11,r10,r8 - add r8,r8,r12 - xor r11,r11,r11 // Ensure r11 is 0 even if fallback area is not - add r8,r8,r11 // Add 0, this creates a dependency on the ldx - .endr - addi r10,r10,128 /* 128 byte cache line */ + + /* + * The load adresses are at staggered offsets within cachelines, + * which suits some pipelines better (on others it should not + * hurt). + */ +1: + ld r11,(0x80 + 8)*0(r10) + ld r11,(0x80 + 8)*1(r10) + ld r11,(0x80 + 8)*2(r10) + ld r11,(0x80 + 8)*3(r10) + ld r11,(0x80 + 8)*4(r10) + ld r11,(0x80 + 8)*5(r10) + ld r11,(0x80 + 8)*6(r10) + ld r11,(0x80 + 8)*7(r10) + addi r10,r10,0x80*8 bdnz 1b mtctr r9 ld r9,PACA_EXRFI+EX_R9(r13) ld r10,PACA_EXRFI+EX_R10(r13) ld r11,PACA_EXRFI+EX_R11(r13) - ld r12,PACA_EXRFI+EX_R12(r13) - ld r8,PACA_EXRFI+EX_R13(r13) GET_SCRATCH0(r13); rfid @@ -1614,39 +1632,37 @@ hrfi_flush_fallback: std r9,PACA_EXRFI+EX_R9(r13) std r10,PACA_EXRFI+EX_R10(r13) std r11,PACA_EXRFI+EX_R11(r13) - std r12,PACA_EXRFI+EX_R12(r13) - std r8,PACA_EXRFI+EX_R13(r13) mfctr r9 ld r10,PACA_RFI_FLUSH_FALLBACK_AREA(r13) - ld r11,PACA_L1D_FLUSH_SETS(r13) - ld r12,PACA_L1D_FLUSH_CONGRUENCE(r13) - /* - * The load adresses are at staggered offsets within cachelines, - * which suits some pipelines better (on others it should not - * hurt). - */ - addi r12,r12,8 + ld r11,PACA_L1D_FLUSH_SIZE(r13) + srdi r11,r11,(7 + 3) /* 128 byte lines, unrolled 8x */ mtctr r11 DCBT_STOP_ALL_STREAM_IDS(r11) /* Stop prefetch streams */ /* order ld/st prior to dcbt stop all streams with flushing */ sync -1: li r8,0 - .rept 8 /* 8-way set associative */ - ldx r11,r10,r8 - add r8,r8,r12 - xor r11,r11,r11 // Ensure r11 is 0 even if fallback area is not - add r8,r8,r11 // Add 0, this creates a dependency on the ldx - .endr - addi r10,r10,128 /* 128 byte cache line */ + + /* + * The load adresses are at staggered offsets within cachelines, + * which suits some pipelines better (on others it should not + * hurt). + */ +1: + ld r11,(0x80 + 8)*0(r10) + ld r11,(0x80 + 8)*1(r10) + ld r11,(0x80 + 8)*2(r10) + ld r11,(0x80 + 8)*3(r10) + ld r11,(0x80 + 8)*4(r10) + ld r11,(0x80 + 8)*5(r10) + ld r11,(0x80 + 8)*6(r10) + ld r11,(0x80 + 8)*7(r10) + addi r10,r10,0x80*8 bdnz 1b mtctr r9 ld r9,PACA_EXRFI+EX_R9(r13) ld r10,PACA_EXRFI+EX_R10(r13) ld r11,PACA_EXRFI+EX_R11(r13) - ld r12,PACA_EXRFI+EX_R12(r13) - ld r8,PACA_EXRFI+EX_R13(r13) GET_SCRATCH0(r13); hrfid diff --git a/arch/powerpc/kernel/head_booke.h b/arch/powerpc/kernel/head_booke.h index a620203f7de3..7b98c7351f6c 100644 --- a/arch/powerpc/kernel/head_booke.h +++ b/arch/powerpc/kernel/head_booke.h @@ -31,6 +31,16 @@ */ #define THREAD_NORMSAVE(offset) (THREAD_NORMSAVES + (offset * 4)) +#ifdef CONFIG_PPC_FSL_BOOK3E +#define BOOKE_CLEAR_BTB(reg) \ +START_BTB_FLUSH_SECTION \ + BTB_FLUSH(reg) \ +END_BTB_FLUSH_SECTION +#else +#define BOOKE_CLEAR_BTB(reg) +#endif + + #define NORMAL_EXCEPTION_PROLOG(intno) \ mtspr SPRN_SPRG_WSCRATCH0, r10; /* save one register */ \ mfspr r10, SPRN_SPRG_THREAD; \ @@ -42,6 +52,7 @@ andi. r11, r11, MSR_PR; /* check whether user or kernel */\ mr r11, r1; \ beq 1f; \ + BOOKE_CLEAR_BTB(r11) \ /* if from user, start at top of this thread's kernel stack */ \ lwz r11, THREAD_INFO-THREAD(r10); \ ALLOC_STACK_FRAME(r11, THREAD_SIZE); \ @@ -127,6 +138,7 @@ stw r9,_CCR(r8); /* save CR on stack */\ mfspr r11,exc_level_srr1; /* check whether user or kernel */\ DO_KVM BOOKE_INTERRUPT_##intno exc_level_srr1; \ + BOOKE_CLEAR_BTB(r10) \ andi. r11,r11,MSR_PR; \ mfspr r11,SPRN_SPRG_THREAD; /* if from user, start at top of */\ lwz r11,THREAD_INFO-THREAD(r11); /* this thread's kernel stack */\ diff --git a/arch/powerpc/kernel/head_fsl_booke.S b/arch/powerpc/kernel/head_fsl_booke.S index fffd1f96bb1d..275769b6fb0d 100644 --- a/arch/powerpc/kernel/head_fsl_booke.S +++ b/arch/powerpc/kernel/head_fsl_booke.S @@ -451,6 +451,13 @@ END_FTR_SECTION_IFSET(CPU_FTR_EMB_HV) mfcr r13 stw r13, THREAD_NORMSAVE(3)(r10) DO_KVM BOOKE_INTERRUPT_DTLB_MISS SPRN_SRR1 +START_BTB_FLUSH_SECTION + mfspr r11, SPRN_SRR1 + andi. r10,r11,MSR_PR + beq 1f + BTB_FLUSH(r10) +1: +END_BTB_FLUSH_SECTION mfspr r10, SPRN_DEAR /* Get faulting address */ /* If we are faulting a kernel address, we have to use the @@ -545,6 +552,14 @@ END_FTR_SECTION_IFSET(CPU_FTR_EMB_HV) mfcr r13 stw r13, THREAD_NORMSAVE(3)(r10) DO_KVM BOOKE_INTERRUPT_ITLB_MISS SPRN_SRR1 +START_BTB_FLUSH_SECTION + mfspr r11, SPRN_SRR1 + andi. r10,r11,MSR_PR + beq 1f + BTB_FLUSH(r10) +1: +END_BTB_FLUSH_SECTION + mfspr r10, SPRN_SRR0 /* Get faulting address */ /* If we are faulting a kernel address, we have to use the diff --git a/arch/powerpc/kernel/module.c b/arch/powerpc/kernel/module.c index 9547381b631a..ff009be97a42 100644 --- a/arch/powerpc/kernel/module.c +++ b/arch/powerpc/kernel/module.c @@ -67,7 +67,15 @@ int module_finalize(const Elf_Ehdr *hdr, do_feature_fixups(powerpc_firmware_features, (void *)sect->sh_addr, (void *)sect->sh_addr + sect->sh_size); -#endif +#endif /* CONFIG_PPC64 */ + +#ifdef CONFIG_PPC_BARRIER_NOSPEC + sect = find_section(hdr, sechdrs, "__spec_barrier_fixup"); + if (sect != NULL) + do_barrier_nospec_fixups_range(barrier_nospec_enabled, + (void *)sect->sh_addr, + (void *)sect->sh_addr + sect->sh_size); +#endif /* CONFIG_PPC_BARRIER_NOSPEC */ sect = find_section(hdr, sechdrs, "__lwsync_fixup"); if (sect != NULL) diff --git a/arch/powerpc/kernel/security.c b/arch/powerpc/kernel/security.c new file mode 100644 index 000000000000..fe30ddfd51ee --- /dev/null +++ b/arch/powerpc/kernel/security.c @@ -0,0 +1,434 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Security related flags and so on. +// +// Copyright 2018, Michael Ellerman, IBM Corporation. + +#include <linux/cpu.h> +#include <linux/kernel.h> +#include <linux/debugfs.h> +#include <linux/device.h> +#include <linux/seq_buf.h> + +#include <asm/debug.h> +#include <asm/asm-prototypes.h> +#include <asm/code-patching.h> +#include <asm/security_features.h> +#include <asm/setup.h> + + +unsigned long powerpc_security_features __read_mostly = SEC_FTR_DEFAULT; + +enum count_cache_flush_type { + COUNT_CACHE_FLUSH_NONE = 0x1, + COUNT_CACHE_FLUSH_SW = 0x2, + COUNT_CACHE_FLUSH_HW = 0x4, +}; +static enum count_cache_flush_type count_cache_flush_type = COUNT_CACHE_FLUSH_NONE; + +bool barrier_nospec_enabled; +static bool no_nospec; +static bool btb_flush_enabled; +#ifdef CONFIG_PPC_FSL_BOOK3E +static bool no_spectrev2; +#endif + +static void enable_barrier_nospec(bool enable) +{ + barrier_nospec_enabled = enable; + do_barrier_nospec_fixups(enable); +} + +void setup_barrier_nospec(void) +{ + bool enable; + + /* + * It would make sense to check SEC_FTR_SPEC_BAR_ORI31 below as well. + * But there's a good reason not to. The two flags we check below are + * both are enabled by default in the kernel, so if the hcall is not + * functional they will be enabled. + * On a system where the host firmware has been updated (so the ori + * functions as a barrier), but on which the hypervisor (KVM/Qemu) has + * not been updated, we would like to enable the barrier. Dropping the + * check for SEC_FTR_SPEC_BAR_ORI31 achieves that. The only downside is + * we potentially enable the barrier on systems where the host firmware + * is not updated, but that's harmless as it's a no-op. + */ + enable = security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) && + security_ftr_enabled(SEC_FTR_BNDS_CHK_SPEC_BAR); + + if (!no_nospec) + enable_barrier_nospec(enable); +} + +static int __init handle_nospectre_v1(char *p) +{ + no_nospec = true; + + return 0; +} +early_param("nospectre_v1", handle_nospectre_v1); + +#ifdef CONFIG_DEBUG_FS +static int barrier_nospec_set(void *data, u64 val) +{ + switch (val) { + case 0: + case 1: + break; + default: + return -EINVAL; + } + + if (!!val == !!barrier_nospec_enabled) + return 0; + + enable_barrier_nospec(!!val); + + return 0; +} + +static int barrier_nospec_get(void *data, u64 *val) +{ + *val = barrier_nospec_enabled ? 1 : 0; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(fops_barrier_nospec, + barrier_nospec_get, barrier_nospec_set, "%llu\n"); + +static __init int barrier_nospec_debugfs_init(void) +{ + debugfs_create_file("barrier_nospec", 0600, powerpc_debugfs_root, NULL, + &fops_barrier_nospec); + return 0; +} +device_initcall(barrier_nospec_debugfs_init); +#endif /* CONFIG_DEBUG_FS */ + +#ifdef CONFIG_PPC_FSL_BOOK3E +static int __init handle_nospectre_v2(char *p) +{ + no_spectrev2 = true; + + return 0; +} +early_param("nospectre_v2", handle_nospectre_v2); +void setup_spectre_v2(void) +{ + if (no_spectrev2) + do_btb_flush_fixups(); + else + btb_flush_enabled = true; +} +#endif /* CONFIG_PPC_FSL_BOOK3E */ + +#ifdef CONFIG_PPC_BOOK3S_64 +ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr, char *buf) +{ + bool thread_priv; + + thread_priv = security_ftr_enabled(SEC_FTR_L1D_THREAD_PRIV); + + if (rfi_flush || thread_priv) { + struct seq_buf s; + seq_buf_init(&s, buf, PAGE_SIZE - 1); + + seq_buf_printf(&s, "Mitigation: "); + + if (rfi_flush) + seq_buf_printf(&s, "RFI Flush"); + + if (rfi_flush && thread_priv) + seq_buf_printf(&s, ", "); + + if (thread_priv) + seq_buf_printf(&s, "L1D private per thread"); + + seq_buf_printf(&s, "\n"); + + return s.len; + } + + if (!security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) && + !security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR)) + return sprintf(buf, "Not affected\n"); + + return sprintf(buf, "Vulnerable\n"); +} +#endif + +ssize_t cpu_show_spectre_v1(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct seq_buf s; + + seq_buf_init(&s, buf, PAGE_SIZE - 1); + + if (security_ftr_enabled(SEC_FTR_BNDS_CHK_SPEC_BAR)) { + if (barrier_nospec_enabled) + seq_buf_printf(&s, "Mitigation: __user pointer sanitization"); + else + seq_buf_printf(&s, "Vulnerable"); + + if (security_ftr_enabled(SEC_FTR_SPEC_BAR_ORI31)) + seq_buf_printf(&s, ", ori31 speculation barrier enabled"); + + seq_buf_printf(&s, "\n"); + } else + seq_buf_printf(&s, "Not affected\n"); + + return s.len; +} + +ssize_t cpu_show_spectre_v2(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct seq_buf s; + bool bcs, ccd; + + seq_buf_init(&s, buf, PAGE_SIZE - 1); + + bcs = security_ftr_enabled(SEC_FTR_BCCTRL_SERIALISED); + ccd = security_ftr_enabled(SEC_FTR_COUNT_CACHE_DISABLED); + + if (bcs || ccd) { + seq_buf_printf(&s, "Mitigation: "); + + if (bcs) + seq_buf_printf(&s, "Indirect branch serialisation (kernel only)"); + + if (bcs && ccd) + seq_buf_printf(&s, ", "); + + if (ccd) + seq_buf_printf(&s, "Indirect branch cache disabled"); + } else if (count_cache_flush_type != COUNT_CACHE_FLUSH_NONE) { + seq_buf_printf(&s, "Mitigation: Software count cache flush"); + + if (count_cache_flush_type == COUNT_CACHE_FLUSH_HW) + seq_buf_printf(&s, " (hardware accelerated)"); + } else if (btb_flush_enabled) { + seq_buf_printf(&s, "Mitigation: Branch predictor state flush"); + } else { + seq_buf_printf(&s, "Vulnerable"); + } + + seq_buf_printf(&s, "\n"); + + return s.len; +} + +#ifdef CONFIG_PPC_BOOK3S_64 +/* + * Store-forwarding barrier support. + */ + +static enum stf_barrier_type stf_enabled_flush_types; +static bool no_stf_barrier; +bool stf_barrier; + +static int __init handle_no_stf_barrier(char *p) +{ + pr_info("stf-barrier: disabled on command line."); + no_stf_barrier = true; + return 0; +} + +early_param("no_stf_barrier", handle_no_stf_barrier); + +/* This is the generic flag used by other architectures */ +static int __init handle_ssbd(char *p) +{ + if (!p || strncmp(p, "auto", 5) == 0 || strncmp(p, "on", 2) == 0 ) { + /* Until firmware tells us, we have the barrier with auto */ + return 0; + } else if (strncmp(p, "off", 3) == 0) { + handle_no_stf_barrier(NULL); + return 0; + } else + return 1; + + return 0; +} +early_param("spec_store_bypass_disable", handle_ssbd); + +/* This is the generic flag used by other architectures */ +static int __init handle_no_ssbd(char *p) +{ + handle_no_stf_barrier(NULL); + return 0; +} +early_param("nospec_store_bypass_disable", handle_no_ssbd); + +static void stf_barrier_enable(bool enable) +{ + if (enable) + do_stf_barrier_fixups(stf_enabled_flush_types); + else + do_stf_barrier_fixups(STF_BARRIER_NONE); + + stf_barrier = enable; +} + +void setup_stf_barrier(void) +{ + enum stf_barrier_type type; + bool enable, hv; + + hv = cpu_has_feature(CPU_FTR_HVMODE); + + /* Default to fallback in case fw-features are not available */ + if (cpu_has_feature(CPU_FTR_ARCH_207S)) + type = STF_BARRIER_SYNC_ORI; + else if (cpu_has_feature(CPU_FTR_ARCH_206)) + type = STF_BARRIER_FALLBACK; + else + type = STF_BARRIER_NONE; + + enable = security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) && + (security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR) || + (security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) && hv)); + + if (type == STF_BARRIER_FALLBACK) { + pr_info("stf-barrier: fallback barrier available\n"); + } else if (type == STF_BARRIER_SYNC_ORI) { + pr_info("stf-barrier: hwsync barrier available\n"); + } else if (type == STF_BARRIER_EIEIO) { + pr_info("stf-barrier: eieio barrier available\n"); + } + + stf_enabled_flush_types = type; + + if (!no_stf_barrier) + stf_barrier_enable(enable); +} + +ssize_t cpu_show_spec_store_bypass(struct device *dev, struct device_attribute *attr, char *buf) +{ + if (stf_barrier && stf_enabled_flush_types != STF_BARRIER_NONE) { + const char *type; + switch (stf_enabled_flush_types) { + case STF_BARRIER_EIEIO: + type = "eieio"; + break; + case STF_BARRIER_SYNC_ORI: + type = "hwsync"; + break; + case STF_BARRIER_FALLBACK: + type = "fallback"; + break; + default: + type = "unknown"; + } + return sprintf(buf, "Mitigation: Kernel entry/exit barrier (%s)\n", type); + } + + if (!security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) && + !security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR)) + return sprintf(buf, "Not affected\n"); + + return sprintf(buf, "Vulnerable\n"); +} + +#ifdef CONFIG_DEBUG_FS +static int stf_barrier_set(void *data, u64 val) +{ + bool enable; + + if (val == 1) + enable = true; + else if (val == 0) + enable = false; + else + return -EINVAL; + + /* Only do anything if we're changing state */ + if (enable != stf_barrier) + stf_barrier_enable(enable); + + return 0; +} + +static int stf_barrier_get(void *data, u64 *val) +{ + *val = stf_barrier ? 1 : 0; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(fops_stf_barrier, stf_barrier_get, stf_barrier_set, "%llu\n"); + +static __init int stf_barrier_debugfs_init(void) +{ + debugfs_create_file("stf_barrier", 0600, powerpc_debugfs_root, NULL, &fops_stf_barrier); + return 0; +} +device_initcall(stf_barrier_debugfs_init); +#endif /* CONFIG_DEBUG_FS */ + +static void toggle_count_cache_flush(bool enable) +{ + if (!enable || !security_ftr_enabled(SEC_FTR_FLUSH_COUNT_CACHE)) { + patch_instruction_site(&patch__call_flush_count_cache, PPC_INST_NOP); + count_cache_flush_type = COUNT_CACHE_FLUSH_NONE; + pr_info("count-cache-flush: software flush disabled.\n"); + return; + } + + patch_branch_site(&patch__call_flush_count_cache, + (u64)&flush_count_cache, BRANCH_SET_LINK); + + if (!security_ftr_enabled(SEC_FTR_BCCTR_FLUSH_ASSIST)) { + count_cache_flush_type = COUNT_CACHE_FLUSH_SW; + pr_info("count-cache-flush: full software flush sequence enabled.\n"); + return; + } + + patch_instruction_site(&patch__flush_count_cache_return, PPC_INST_BLR); + count_cache_flush_type = COUNT_CACHE_FLUSH_HW; + pr_info("count-cache-flush: hardware assisted flush sequence enabled\n"); +} + +void setup_count_cache_flush(void) +{ + toggle_count_cache_flush(true); +} + +#ifdef CONFIG_DEBUG_FS +static int count_cache_flush_set(void *data, u64 val) +{ + bool enable; + + if (val == 1) + enable = true; + else if (val == 0) + enable = false; + else + return -EINVAL; + + toggle_count_cache_flush(enable); + + return 0; +} + +static int count_cache_flush_get(void *data, u64 *val) +{ + if (count_cache_flush_type == COUNT_CACHE_FLUSH_NONE) + *val = 0; + else + *val = 1; + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(fops_count_cache_flush, count_cache_flush_get, + count_cache_flush_set, "%llu\n"); + +static __init int count_cache_flush_debugfs_init(void) +{ + debugfs_create_file("count_cache_flush", 0600, powerpc_debugfs_root, + NULL, &fops_count_cache_flush); + return 0; +} +device_initcall(count_cache_flush_debugfs_init); +#endif /* CONFIG_DEBUG_FS */ +#endif /* CONFIG_PPC_BOOK3S_64 */ diff --git a/arch/powerpc/kernel/setup_32.c b/arch/powerpc/kernel/setup_32.c index ad8c9db61237..cb37f27bb928 100644 --- a/arch/powerpc/kernel/setup_32.c +++ b/arch/powerpc/kernel/setup_32.c @@ -322,6 +322,9 @@ void __init setup_arch(char **cmdline_p) ppc_md.setup_arch(); if ( ppc_md.progress ) ppc_md.progress("arch: exit", 0x3eab); + setup_barrier_nospec(); + setup_spectre_v2(); + paging_init(); /* Initialize the MMU context management stuff */ diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c index 9eb469bed22b..11590f6cb2f9 100644 --- a/arch/powerpc/kernel/setup_64.c +++ b/arch/powerpc/kernel/setup_64.c @@ -736,6 +736,9 @@ void __init setup_arch(char **cmdline_p) if (ppc_md.setup_arch) ppc_md.setup_arch(); + setup_barrier_nospec(); + setup_spectre_v2(); + paging_init(); /* Initialize the MMU context management stuff */ @@ -873,9 +876,6 @@ static void do_nothing(void *unused) void rfi_flush_enable(bool enable) { - if (rfi_flush == enable) - return; - if (enable) { do_rfi_flush_fixups(enabled_flush_types); on_each_cpu(do_nothing, NULL, 1); @@ -885,11 +885,15 @@ void rfi_flush_enable(bool enable) rfi_flush = enable; } -static void init_fallback_flush(void) +static void __ref init_fallback_flush(void) { u64 l1d_size, limit; int cpu; + /* Only allocate the fallback flush area once (at boot time). */ + if (l1d_flush_fallback_area) + return; + l1d_size = ppc64_caches.dsize; limit = min(safe_stack_limit(), ppc64_rma_size); @@ -902,34 +906,23 @@ static void init_fallback_flush(void) memset(l1d_flush_fallback_area, 0, l1d_size * 2); for_each_possible_cpu(cpu) { - /* - * The fallback flush is currently coded for 8-way - * associativity. Different associativity is possible, but it - * will be treated as 8-way and may not evict the lines as - * effectively. - * - * 128 byte lines are mandatory. - */ - u64 c = l1d_size / 8; - paca[cpu].rfi_flush_fallback_area = l1d_flush_fallback_area; - paca[cpu].l1d_flush_congruence = c; - paca[cpu].l1d_flush_sets = c / 128; + paca[cpu].l1d_flush_size = l1d_size; } } -void __init setup_rfi_flush(enum l1d_flush_type types, bool enable) +void setup_rfi_flush(enum l1d_flush_type types, bool enable) { if (types & L1D_FLUSH_FALLBACK) { - pr_info("rfi-flush: Using fallback displacement flush\n"); + pr_info("rfi-flush: fallback displacement flush available\n"); init_fallback_flush(); } if (types & L1D_FLUSH_ORI) - pr_info("rfi-flush: Using ori type flush\n"); + pr_info("rfi-flush: ori type flush available\n"); if (types & L1D_FLUSH_MTTRIG) - pr_info("rfi-flush: Using mttrig type flush\n"); + pr_info("rfi-flush: mttrig type flush available\n"); enabled_flush_types = types; @@ -940,13 +933,19 @@ void __init setup_rfi_flush(enum l1d_flush_type types, bool enable) #ifdef CONFIG_DEBUG_FS static int rfi_flush_set(void *data, u64 val) { + bool enable; + if (val == 1) - rfi_flush_enable(true); + enable = true; else if (val == 0) - rfi_flush_enable(false); + enable = false; else return -EINVAL; + /* Only do anything if we're changing state */ + if (enable != rfi_flush) + rfi_flush_enable(enable); + return 0; } @@ -965,12 +964,4 @@ static __init int rfi_flush_debugfs_init(void) } device_initcall(rfi_flush_debugfs_init); #endif - -ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr, char *buf) -{ - if (rfi_flush) - return sprintf(buf, "Mitigation: RFI Flush\n"); - - return sprintf(buf, "Vulnerable\n"); -} #endif /* CONFIG_PPC_BOOK3S_64 */ diff --git a/arch/powerpc/kernel/vmlinux.lds.S b/arch/powerpc/kernel/vmlinux.lds.S index 30f90e8f5dd6..9f6d79714373 100644 --- a/arch/powerpc/kernel/vmlinux.lds.S +++ b/arch/powerpc/kernel/vmlinux.lds.S @@ -75,13 +75,44 @@ SECTIONS #ifdef CONFIG_PPC64 . = ALIGN(8); + __stf_entry_barrier_fixup : AT(ADDR(__stf_entry_barrier_fixup) - LOAD_OFFSET) { + __start___stf_entry_barrier_fixup = .; + *(__stf_entry_barrier_fixup) + __stop___stf_entry_barrier_fixup = .; + } + + . = ALIGN(8); + __stf_exit_barrier_fixup : AT(ADDR(__stf_exit_barrier_fixup) - LOAD_OFFSET) { + __start___stf_exit_barrier_fixup = .; + *(__stf_exit_barrier_fixup) + __stop___stf_exit_barrier_fixup = .; + } + + . = ALIGN(8); __rfi_flush_fixup : AT(ADDR(__rfi_flush_fixup) - LOAD_OFFSET) { __start___rfi_flush_fixup = .; *(__rfi_flush_fixup) __stop___rfi_flush_fixup = .; } -#endif +#endif /* CONFIG_PPC64 */ +#ifdef CONFIG_PPC_BARRIER_NOSPEC + . = ALIGN(8); + __spec_barrier_fixup : AT(ADDR(__spec_barrier_fixup) - LOAD_OFFSET) { + __start___barrier_nospec_fixup = .; + *(__barrier_nospec_fixup) + __stop___barrier_nospec_fixup = .; + } +#endif /* CONFIG_PPC_BARRIER_NOSPEC */ + +#ifdef CONFIG_PPC_FSL_BOOK3E + . = ALIGN(8); + __spec_btb_flush_fixup : AT(ADDR(__spec_btb_flush_fixup) - LOAD_OFFSET) { + __start__btb_flush_fixup = .; + *(__btb_flush_fixup) + __stop__btb_flush_fixup = .; + } +#endif EXCEPTION_TABLE(0) NOTES :kernel :notes diff --git a/arch/powerpc/kvm/book3s.c b/arch/powerpc/kvm/book3s.c index 099c79d8c160..4aab1c9c83e1 100644 --- a/arch/powerpc/kvm/book3s.c +++ b/arch/powerpc/kvm/book3s.c @@ -809,6 +809,7 @@ int kvmppc_core_init_vm(struct kvm *kvm) #ifdef CONFIG_PPC64 INIT_LIST_HEAD(&kvm->arch.spapr_tce_tables); INIT_LIST_HEAD(&kvm->arch.rtas_tokens); + mutex_init(&kvm->arch.rtas_token_lock); #endif return kvm->arch.kvm_ops->init_vm(kvm); diff --git a/arch/powerpc/kvm/book3s_rtas.c b/arch/powerpc/kvm/book3s_rtas.c index ef27fbd5d9c5..b1b2273d1f6d 100644 --- a/arch/powerpc/kvm/book3s_rtas.c +++ b/arch/powerpc/kvm/book3s_rtas.c @@ -133,7 +133,7 @@ static int rtas_token_undefine(struct kvm *kvm, char *name) { struct rtas_token_definition *d, *tmp; - lockdep_assert_held(&kvm->lock); + lockdep_assert_held(&kvm->arch.rtas_token_lock); list_for_each_entry_safe(d, tmp, &kvm->arch.rtas_tokens, list) { if (rtas_name_matches(d->handler->name, name)) { @@ -154,7 +154,7 @@ static int rtas_token_define(struct kvm *kvm, char *name, u64 token) bool found; int i; - lockdep_assert_held(&kvm->lock); + lockdep_assert_held(&kvm->arch.rtas_token_lock); list_for_each_entry(d, &kvm->arch.rtas_tokens, list) { if (d->token == token) @@ -193,14 +193,14 @@ int kvm_vm_ioctl_rtas_define_token(struct kvm *kvm, void __user *argp) if (copy_from_user(&args, argp, sizeof(args))) return -EFAULT; - mutex_lock(&kvm->lock); + mutex_lock(&kvm->arch.rtas_token_lock); if (args.token) rc = rtas_token_define(kvm, args.name, args.token); else rc = rtas_token_undefine(kvm, args.name); - mutex_unlock(&kvm->lock); + mutex_unlock(&kvm->arch.rtas_token_lock); return rc; } @@ -232,7 +232,7 @@ int kvmppc_rtas_hcall(struct kvm_vcpu *vcpu) orig_rets = args.rets; args.rets = &args.args[be32_to_cpu(args.nargs)]; - mutex_lock(&vcpu->kvm->lock); + mutex_lock(&vcpu->kvm->arch.rtas_token_lock); rc = -ENOENT; list_for_each_entry(d, &vcpu->kvm->arch.rtas_tokens, list) { @@ -243,7 +243,7 @@ int kvmppc_rtas_hcall(struct kvm_vcpu *vcpu) } } - mutex_unlock(&vcpu->kvm->lock); + mutex_unlock(&vcpu->kvm->arch.rtas_token_lock); if (rc == 0) { args.rets = orig_rets; @@ -269,8 +269,6 @@ void kvmppc_rtas_tokens_free(struct kvm *kvm) { struct rtas_token_definition *d, *tmp; - lockdep_assert_held(&kvm->lock); - list_for_each_entry_safe(d, tmp, &kvm->arch.rtas_tokens, list) { list_del(&d->list); kfree(d); diff --git a/arch/powerpc/kvm/bookehv_interrupts.S b/arch/powerpc/kvm/bookehv_interrupts.S index 81bd8a07aa51..612b7f6a887f 100644 --- a/arch/powerpc/kvm/bookehv_interrupts.S +++ b/arch/powerpc/kvm/bookehv_interrupts.S @@ -75,6 +75,10 @@ PPC_LL r1, VCPU_HOST_STACK(r4) PPC_LL r2, HOST_R2(r1) +START_BTB_FLUSH_SECTION + BTB_FLUSH(r10) +END_BTB_FLUSH_SECTION + mfspr r10, SPRN_PID lwz r8, VCPU_HOST_PID(r4) PPC_LL r11, VCPU_SHARED(r4) diff --git a/arch/powerpc/kvm/e500_emulate.c b/arch/powerpc/kvm/e500_emulate.c index 990db69a1d0b..fa88f641ac03 100644 --- a/arch/powerpc/kvm/e500_emulate.c +++ b/arch/powerpc/kvm/e500_emulate.c @@ -277,6 +277,13 @@ int kvmppc_core_emulate_mtspr_e500(struct kvm_vcpu *vcpu, int sprn, ulong spr_va vcpu->arch.pwrmgtcr0 = spr_val; break; + case SPRN_BUCSR: + /* + * If we are here, it means that we have already flushed the + * branch predictor, so just return to guest. + */ + break; + /* extra exceptions */ #ifdef CONFIG_SPE_POSSIBLE case SPRN_IVOR32: diff --git a/arch/powerpc/lib/code-patching.c b/arch/powerpc/lib/code-patching.c index d5edbeb8eb82..31d31a10f71f 100644 --- a/arch/powerpc/lib/code-patching.c +++ b/arch/powerpc/lib/code-patching.c @@ -14,12 +14,25 @@ #include <asm/page.h> #include <asm/code-patching.h> #include <asm/uaccess.h> +#include <asm/setup.h> +#include <asm/sections.h> +static inline bool is_init(unsigned int *addr) +{ + return addr >= (unsigned int *)__init_begin && addr < (unsigned int *)__init_end; +} + int patch_instruction(unsigned int *addr, unsigned int instr) { int err; + /* Make sure we aren't patching a freed init section */ + if (*PTRRELOC(&init_mem_is_free) && is_init(addr)) { + pr_debug("Skipping init section patching addr: 0x%px\n", addr); + return 0; + } + __put_user_size(instr, addr, 4, err); if (err) return err; @@ -32,6 +45,22 @@ int patch_branch(unsigned int *addr, unsigned long target, int flags) return patch_instruction(addr, create_branch(addr, target, flags)); } +int patch_branch_site(s32 *site, unsigned long target, int flags) +{ + unsigned int *addr; + + addr = (unsigned int *)((unsigned long)site + *site); + return patch_instruction(addr, create_branch(addr, target, flags)); +} + +int patch_instruction_site(s32 *site, unsigned int instr) +{ + unsigned int *addr; + + addr = (unsigned int *)((unsigned long)site + *site); + return patch_instruction(addr, instr); +} + unsigned int create_branch(const unsigned int *addr, unsigned long target, int flags) { diff --git a/arch/powerpc/lib/feature-fixups.c b/arch/powerpc/lib/feature-fixups.c index 3af014684872..7bdfc19a491d 100644 --- a/arch/powerpc/lib/feature-fixups.c +++ b/arch/powerpc/lib/feature-fixups.c @@ -21,7 +21,7 @@ #include <asm/page.h> #include <asm/sections.h> #include <asm/setup.h> - +#include <asm/security_features.h> struct fixup_entry { unsigned long mask; @@ -115,6 +115,120 @@ void do_feature_fixups(unsigned long value, void *fixup_start, void *fixup_end) } #ifdef CONFIG_PPC_BOOK3S_64 +void do_stf_entry_barrier_fixups(enum stf_barrier_type types) +{ + unsigned int instrs[3], *dest; + long *start, *end; + int i; + + start = PTRRELOC(&__start___stf_entry_barrier_fixup), + end = PTRRELOC(&__stop___stf_entry_barrier_fixup); + + instrs[0] = 0x60000000; /* nop */ + instrs[1] = 0x60000000; /* nop */ + instrs[2] = 0x60000000; /* nop */ + + i = 0; + if (types & STF_BARRIER_FALLBACK) { + instrs[i++] = 0x7d4802a6; /* mflr r10 */ + instrs[i++] = 0x60000000; /* branch patched below */ + instrs[i++] = 0x7d4803a6; /* mtlr r10 */ + } else if (types & STF_BARRIER_EIEIO) { + instrs[i++] = 0x7e0006ac; /* eieio + bit 6 hint */ + } else if (types & STF_BARRIER_SYNC_ORI) { + instrs[i++] = 0x7c0004ac; /* hwsync */ + instrs[i++] = 0xe94d0000; /* ld r10,0(r13) */ + instrs[i++] = 0x63ff0000; /* ori 31,31,0 speculation barrier */ + } + + for (i = 0; start < end; start++, i++) { + dest = (void *)start + *start; + + pr_devel("patching dest %lx\n", (unsigned long)dest); + + patch_instruction(dest, instrs[0]); + + if (types & STF_BARRIER_FALLBACK) + patch_branch(dest + 1, (unsigned long)&stf_barrier_fallback, + BRANCH_SET_LINK); + else + patch_instruction(dest + 1, instrs[1]); + + patch_instruction(dest + 2, instrs[2]); + } + + printk(KERN_DEBUG "stf-barrier: patched %d entry locations (%s barrier)\n", i, + (types == STF_BARRIER_NONE) ? "no" : + (types == STF_BARRIER_FALLBACK) ? "fallback" : + (types == STF_BARRIER_EIEIO) ? "eieio" : + (types == (STF_BARRIER_SYNC_ORI)) ? "hwsync" + : "unknown"); +} + +void do_stf_exit_barrier_fixups(enum stf_barrier_type types) +{ + unsigned int instrs[6], *dest; + long *start, *end; + int i; + + start = PTRRELOC(&__start___stf_exit_barrier_fixup), + end = PTRRELOC(&__stop___stf_exit_barrier_fixup); + + instrs[0] = 0x60000000; /* nop */ + instrs[1] = 0x60000000; /* nop */ + instrs[2] = 0x60000000; /* nop */ + instrs[3] = 0x60000000; /* nop */ + instrs[4] = 0x60000000; /* nop */ + instrs[5] = 0x60000000; /* nop */ + + i = 0; + if (types & STF_BARRIER_FALLBACK || types & STF_BARRIER_SYNC_ORI) { + if (cpu_has_feature(CPU_FTR_HVMODE)) { + instrs[i++] = 0x7db14ba6; /* mtspr 0x131, r13 (HSPRG1) */ + instrs[i++] = 0x7db04aa6; /* mfspr r13, 0x130 (HSPRG0) */ + } else { + instrs[i++] = 0x7db243a6; /* mtsprg 2,r13 */ + instrs[i++] = 0x7db142a6; /* mfsprg r13,1 */ + } + instrs[i++] = 0x7c0004ac; /* hwsync */ + instrs[i++] = 0xe9ad0000; /* ld r13,0(r13) */ + instrs[i++] = 0x63ff0000; /* ori 31,31,0 speculation barrier */ + if (cpu_has_feature(CPU_FTR_HVMODE)) { + instrs[i++] = 0x7db14aa6; /* mfspr r13, 0x131 (HSPRG1) */ + } else { + instrs[i++] = 0x7db242a6; /* mfsprg r13,2 */ + } + } else if (types & STF_BARRIER_EIEIO) { + instrs[i++] = 0x7e0006ac; /* eieio + bit 6 hint */ + } + + for (i = 0; start < end; start++, i++) { + dest = (void *)start + *start; + + pr_devel("patching dest %lx\n", (unsigned long)dest); + + patch_instruction(dest, instrs[0]); + patch_instruction(dest + 1, instrs[1]); + patch_instruction(dest + 2, instrs[2]); + patch_instruction(dest + 3, instrs[3]); + patch_instruction(dest + 4, instrs[4]); + patch_instruction(dest + 5, instrs[5]); + } + printk(KERN_DEBUG "stf-barrier: patched %d exit locations (%s barrier)\n", i, + (types == STF_BARRIER_NONE) ? "no" : + (types == STF_BARRIER_FALLBACK) ? "fallback" : + (types == STF_BARRIER_EIEIO) ? "eieio" : + (types == (STF_BARRIER_SYNC_ORI)) ? "hwsync" + : "unknown"); +} + + +void do_stf_barrier_fixups(enum stf_barrier_type types) +{ + do_stf_entry_barrier_fixups(types); + do_stf_exit_barrier_fixups(types); +} + void do_rfi_flush_fixups(enum l1d_flush_type types) { unsigned int instrs[3], *dest; @@ -151,10 +265,110 @@ void do_rfi_flush_fixups(enum l1d_flush_type types) patch_instruction(dest + 2, instrs[2]); } - printk(KERN_DEBUG "rfi-flush: patched %d locations\n", i); + printk(KERN_DEBUG "rfi-flush: patched %d locations (%s flush)\n", i, + (types == L1D_FLUSH_NONE) ? "no" : + (types == L1D_FLUSH_FALLBACK) ? "fallback displacement" : + (types & L1D_FLUSH_ORI) ? (types & L1D_FLUSH_MTTRIG) + ? "ori+mttrig type" + : "ori type" : + (types & L1D_FLUSH_MTTRIG) ? "mttrig type" + : "unknown"); +} + +void do_barrier_nospec_fixups_range(bool enable, void *fixup_start, void *fixup_end) +{ + unsigned int instr, *dest; + long *start, *end; + int i; + + start = fixup_start; + end = fixup_end; + + instr = 0x60000000; /* nop */ + + if (enable) { + pr_info("barrier-nospec: using ORI speculation barrier\n"); + instr = 0x63ff0000; /* ori 31,31,0 speculation barrier */ + } + + for (i = 0; start < end; start++, i++) { + dest = (void *)start + *start; + + pr_devel("patching dest %lx\n", (unsigned long)dest); + patch_instruction(dest, instr); + } + + printk(KERN_DEBUG "barrier-nospec: patched %d locations\n", i); } + #endif /* CONFIG_PPC_BOOK3S_64 */ +#ifdef CONFIG_PPC_BARRIER_NOSPEC +void do_barrier_nospec_fixups(bool enable) +{ + void *start, *end; + + start = PTRRELOC(&__start___barrier_nospec_fixup), + end = PTRRELOC(&__stop___barrier_nospec_fixup); + + do_barrier_nospec_fixups_range(enable, start, end); +} +#endif /* CONFIG_PPC_BARRIER_NOSPEC */ + +#ifdef CONFIG_PPC_FSL_BOOK3E +void do_barrier_nospec_fixups_range(bool enable, void *fixup_start, void *fixup_end) +{ + unsigned int instr[2], *dest; + long *start, *end; + int i; + + start = fixup_start; + end = fixup_end; + + instr[0] = PPC_INST_NOP; + instr[1] = PPC_INST_NOP; + + if (enable) { + pr_info("barrier-nospec: using isync; sync as speculation barrier\n"); + instr[0] = PPC_INST_ISYNC; + instr[1] = PPC_INST_SYNC; + } + + for (i = 0; start < end; start++, i++) { + dest = (void *)start + *start; + + pr_devel("patching dest %lx\n", (unsigned long)dest); + patch_instruction(dest, instr[0]); + patch_instruction(dest + 1, instr[1]); + } + + printk(KERN_DEBUG "barrier-nospec: patched %d locations\n", i); +} + +static void patch_btb_flush_section(long *curr) +{ + unsigned int *start, *end; + + start = (void *)curr + *curr; + end = (void *)curr + *(curr + 1); + for (; start < end; start++) { + pr_devel("patching dest %lx\n", (unsigned long)start); + patch_instruction(start, PPC_INST_NOP); + } +} + +void do_btb_flush_fixups(void) +{ + long *start, *end; + + start = PTRRELOC(&__start__btb_flush_fixup); + end = PTRRELOC(&__stop__btb_flush_fixup); + + for (; start < end; start += 2) + patch_btb_flush_section(start); +} +#endif /* CONFIG_PPC_FSL_BOOK3E */ + void do_lwsync_fixups(unsigned long value, void *fixup_start, void *fixup_end) { long *start, *end; diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c index 22d94c3e6fc4..1efe5ca5c3bc 100644 --- a/arch/powerpc/mm/mem.c +++ b/arch/powerpc/mm/mem.c @@ -62,6 +62,7 @@ #endif unsigned long long memory_limit; +bool init_mem_is_free; #ifdef CONFIG_HIGHMEM pte_t *kmap_pte; @@ -381,6 +382,7 @@ void __init mem_init(void) void free_initmem(void) { ppc_md.progress = ppc_printk_progress; + init_mem_is_free = true; free_initmem_default(POISON_FREE_INITMEM); } diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index bb3df222ae71..215bff2b8470 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -1611,6 +1611,9 @@ int start_topology_update(void) { int rc = 0; + if (!topology_updates_enabled) + return 0; + if (firmware_has_feature(FW_FEATURE_PRRN)) { if (!prrn_enabled) { prrn_enabled = 1; @@ -1640,6 +1643,9 @@ int stop_topology_update(void) { int rc = 0; + if (!topology_updates_enabled) + return 0; + if (prrn_enabled) { prrn_enabled = 0; #ifdef CONFIG_SMP @@ -1685,11 +1691,13 @@ static ssize_t topology_write(struct file *file, const char __user *buf, kbuf[read_len] = '\0'; - if (!strncmp(kbuf, "on", 2)) + if (!strncmp(kbuf, "on", 2)) { + topology_updates_enabled = true; start_topology_update(); - else if (!strncmp(kbuf, "off", 3)) + } else if (!strncmp(kbuf, "off", 3)) { stop_topology_update(); - else + topology_updates_enabled = false; + } else return -EINVAL; return count; @@ -1704,9 +1712,7 @@ static const struct file_operations topology_ops = { static int topology_update_init(void) { - /* Do not poll for changes if disabled at boot */ - if (topology_updates_enabled) - start_topology_update(); + start_topology_update(); if (!proc_create("powerpc/topology_updates", 0644, NULL, &topology_ops)) return -ENOMEM; diff --git a/arch/powerpc/mm/tlb_low_64e.S b/arch/powerpc/mm/tlb_low_64e.S index 29d6987c37ba..5486d56da289 100644 --- a/arch/powerpc/mm/tlb_low_64e.S +++ b/arch/powerpc/mm/tlb_low_64e.S @@ -69,6 +69,13 @@ END_FTR_SECTION_IFSET(CPU_FTR_EMB_HV) std r15,EX_TLB_R15(r12) std r10,EX_TLB_CR(r12) #ifdef CONFIG_PPC_FSL_BOOK3E +START_BTB_FLUSH_SECTION + mfspr r11, SPRN_SRR1 + andi. r10,r11,MSR_PR + beq 1f + BTB_FLUSH(r10) +1: +END_BTB_FLUSH_SECTION std r7,EX_TLB_R7(r12) #endif TLB_MISS_PROLOG_STATS diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c index c57afc619b20..e14b52c7ebd8 100644 --- a/arch/powerpc/platforms/powernv/setup.c +++ b/arch/powerpc/platforms/powernv/setup.c @@ -37,53 +37,99 @@ #include <asm/smp.h> #include <asm/tm.h> #include <asm/setup.h> +#include <asm/security_features.h> #include "powernv.h" + +static bool fw_feature_is(const char *state, const char *name, + struct device_node *fw_features) +{ + struct device_node *np; + bool rc = false; + + np = of_get_child_by_name(fw_features, name); + if (np) { + rc = of_property_read_bool(np, state); + of_node_put(np); + } + + return rc; +} + +static void init_fw_feat_flags(struct device_node *np) +{ + if (fw_feature_is("enabled", "inst-spec-barrier-ori31,31,0", np)) + security_ftr_set(SEC_FTR_SPEC_BAR_ORI31); + + if (fw_feature_is("enabled", "fw-bcctrl-serialized", np)) + security_ftr_set(SEC_FTR_BCCTRL_SERIALISED); + + if (fw_feature_is("enabled", "inst-l1d-flush-ori30,30,0", np)) + security_ftr_set(SEC_FTR_L1D_FLUSH_ORI30); + + if (fw_feature_is("enabled", "inst-l1d-flush-trig2", np)) + security_ftr_set(SEC_FTR_L1D_FLUSH_TRIG2); + + if (fw_feature_is("enabled", "fw-l1d-thread-split", np)) + security_ftr_set(SEC_FTR_L1D_THREAD_PRIV); + + if (fw_feature_is("enabled", "fw-count-cache-disabled", np)) + security_ftr_set(SEC_FTR_COUNT_CACHE_DISABLED); + + if (fw_feature_is("enabled", "fw-count-cache-flush-bcctr2,0,0", np)) + security_ftr_set(SEC_FTR_BCCTR_FLUSH_ASSIST); + + if (fw_feature_is("enabled", "needs-count-cache-flush-on-context-switch", np)) + security_ftr_set(SEC_FTR_FLUSH_COUNT_CACHE); + + /* + * The features below are enabled by default, so we instead look to see + * if firmware has *disabled* them, and clear them if so. + */ + if (fw_feature_is("disabled", "speculation-policy-favor-security", np)) + security_ftr_clear(SEC_FTR_FAVOUR_SECURITY); + + if (fw_feature_is("disabled", "needs-l1d-flush-msr-pr-0-to-1", np)) + security_ftr_clear(SEC_FTR_L1D_FLUSH_PR); + + if (fw_feature_is("disabled", "needs-l1d-flush-msr-hv-1-to-0", np)) + security_ftr_clear(SEC_FTR_L1D_FLUSH_HV); + + if (fw_feature_is("disabled", "needs-spec-barrier-for-bound-checks", np)) + security_ftr_clear(SEC_FTR_BNDS_CHK_SPEC_BAR); +} + static void pnv_setup_rfi_flush(void) { struct device_node *np, *fw_features; enum l1d_flush_type type; - int enable; + bool enable; /* Default to fallback in case fw-features are not available */ type = L1D_FLUSH_FALLBACK; - enable = 1; np = of_find_node_by_name(NULL, "ibm,opal"); fw_features = of_get_child_by_name(np, "fw-features"); of_node_put(np); if (fw_features) { - np = of_get_child_by_name(fw_features, "inst-l1d-flush-trig2"); - if (np && of_property_read_bool(np, "enabled")) - type = L1D_FLUSH_MTTRIG; + init_fw_feat_flags(fw_features); + of_node_put(fw_features); - of_node_put(np); + if (security_ftr_enabled(SEC_FTR_L1D_FLUSH_TRIG2)) + type = L1D_FLUSH_MTTRIG; - np = of_get_child_by_name(fw_features, "inst-l1d-flush-ori30,30,0"); - if (np && of_property_read_bool(np, "enabled")) + if (security_ftr_enabled(SEC_FTR_L1D_FLUSH_ORI30)) type = L1D_FLUSH_ORI; - - of_node_put(np); - - /* Enable unless firmware says NOT to */ - enable = 2; - np = of_get_child_by_name(fw_features, "needs-l1d-flush-msr-hv-1-to-0"); - if (np && of_property_read_bool(np, "disabled")) - enable--; - - of_node_put(np); - - np = of_get_child_by_name(fw_features, "needs-l1d-flush-msr-pr-0-to-1"); - if (np && of_property_read_bool(np, "disabled")) - enable--; - - of_node_put(np); - of_node_put(fw_features); } - setup_rfi_flush(type, enable > 0); + enable = security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) && \ + (security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR) || \ + security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV)); + + setup_rfi_flush(type, enable); + setup_count_cache_flush(); } static void __init pnv_setup_arch(void) @@ -91,6 +137,7 @@ static void __init pnv_setup_arch(void) set_arch_panic_timeout(10, ARCH_PANIC_TIMEOUT); pnv_setup_rfi_flush(); + setup_stf_barrier(); /* Initialize SMP */ pnv_smp_init(); diff --git a/arch/powerpc/platforms/pseries/mobility.c b/arch/powerpc/platforms/pseries/mobility.c index 8dd0c8edefd6..c773396d0969 100644 --- a/arch/powerpc/platforms/pseries/mobility.c +++ b/arch/powerpc/platforms/pseries/mobility.c @@ -314,6 +314,9 @@ void post_mobility_fixup(void) printk(KERN_ERR "Post-mobility device tree update " "failed: %d\n", rc); + /* Possibly switch to a new RFI flush type */ + pseries_setup_rfi_flush(); + return; } diff --git a/arch/powerpc/platforms/pseries/pseries.h b/arch/powerpc/platforms/pseries/pseries.h index 8411c27293e4..e7d80797384d 100644 --- a/arch/powerpc/platforms/pseries/pseries.h +++ b/arch/powerpc/platforms/pseries/pseries.h @@ -81,4 +81,6 @@ extern struct pci_controller_ops pseries_pci_controller_ops; unsigned long pseries_memory_block_size(void); +void pseries_setup_rfi_flush(void); + #endif /* _PSERIES_PSERIES_H */ diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index dd2545fc9947..9cc976ff7fec 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -67,6 +67,7 @@ #include <asm/eeh.h> #include <asm/reg.h> #include <asm/plpar_wrappers.h> +#include <asm/security_features.h> #include "pseries.h" @@ -499,37 +500,87 @@ static void __init find_and_init_phbs(void) of_pci_check_probe_only(); } -static void pseries_setup_rfi_flush(void) +static void init_cpu_char_feature_flags(struct h_cpu_char_result *result) +{ + /* + * The features below are disabled by default, so we instead look to see + * if firmware has *enabled* them, and set them if so. + */ + if (result->character & H_CPU_CHAR_SPEC_BAR_ORI31) + security_ftr_set(SEC_FTR_SPEC_BAR_ORI31); + + if (result->character & H_CPU_CHAR_BCCTRL_SERIALISED) + security_ftr_set(SEC_FTR_BCCTRL_SERIALISED); + + if (result->character & H_CPU_CHAR_L1D_FLUSH_ORI30) + security_ftr_set(SEC_FTR_L1D_FLUSH_ORI30); + + if (result->character & H_CPU_CHAR_L1D_FLUSH_TRIG2) + security_ftr_set(SEC_FTR_L1D_FLUSH_TRIG2); + + if (result->character & H_CPU_CHAR_L1D_THREAD_PRIV) + security_ftr_set(SEC_FTR_L1D_THREAD_PRIV); + + if (result->character & H_CPU_CHAR_COUNT_CACHE_DISABLED) + security_ftr_set(SEC_FTR_COUNT_CACHE_DISABLED); + + if (result->character & H_CPU_CHAR_BCCTR_FLUSH_ASSIST) + security_ftr_set(SEC_FTR_BCCTR_FLUSH_ASSIST); + + if (result->behaviour & H_CPU_BEHAV_FLUSH_COUNT_CACHE) + security_ftr_set(SEC_FTR_FLUSH_COUNT_CACHE); + + /* + * The features below are enabled by default, so we instead look to see + * if firmware has *disabled* them, and clear them if so. + */ + if (!(result->behaviour & H_CPU_BEHAV_FAVOUR_SECURITY)) + security_ftr_clear(SEC_FTR_FAVOUR_SECURITY); + + if (!(result->behaviour & H_CPU_BEHAV_L1D_FLUSH_PR)) + security_ftr_clear(SEC_FTR_L1D_FLUSH_PR); + + if (!(result->behaviour & H_CPU_BEHAV_BNDS_CHK_SPEC_BAR)) + security_ftr_clear(SEC_FTR_BNDS_CHK_SPEC_BAR); +} + +void pseries_setup_rfi_flush(void) { struct h_cpu_char_result result; enum l1d_flush_type types; bool enable; long rc; - /* Enable by default */ - enable = true; + /* + * Set features to the defaults assumed by init_cpu_char_feature_flags() + * so it can set/clear again any features that might have changed after + * migration, and in case the hypercall fails and it is not even called. + */ + powerpc_security_features = SEC_FTR_DEFAULT; rc = plpar_get_cpu_characteristics(&result); - if (rc == H_SUCCESS) { - types = L1D_FLUSH_NONE; + if (rc == H_SUCCESS) + init_cpu_char_feature_flags(&result); - if (result.character & H_CPU_CHAR_L1D_FLUSH_TRIG2) - types |= L1D_FLUSH_MTTRIG; - if (result.character & H_CPU_CHAR_L1D_FLUSH_ORI30) - types |= L1D_FLUSH_ORI; + /* + * We're the guest so this doesn't apply to us, clear it to simplify + * handling of it elsewhere. + */ + security_ftr_clear(SEC_FTR_L1D_FLUSH_HV); - /* Use fallback if nothing set in hcall */ - if (types == L1D_FLUSH_NONE) - types = L1D_FLUSH_FALLBACK; + types = L1D_FLUSH_FALLBACK; - if (!(result.behaviour & H_CPU_BEHAV_L1D_FLUSH_PR)) - enable = false; - } else { - /* Default to fallback if case hcall is not available */ - types = L1D_FLUSH_FALLBACK; - } + if (security_ftr_enabled(SEC_FTR_L1D_FLUSH_TRIG2)) + types |= L1D_FLUSH_MTTRIG; + + if (security_ftr_enabled(SEC_FTR_L1D_FLUSH_ORI30)) + types |= L1D_FLUSH_ORI; + + enable = security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) && \ + security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR); setup_rfi_flush(types, enable); + setup_count_cache_flush(); } static void __init pSeries_setup_arch(void) @@ -549,6 +600,7 @@ static void __init pSeries_setup_arch(void) fwnmi_init(); pseries_setup_rfi_flush(); + setup_stf_barrier(); /* By default, only probe PCI (can be overridden by rtas_pci) */ pci_add_flags(PCI_PROBE_ONLY); diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c index 786bf01691c9..83619ebede93 100644 --- a/arch/powerpc/xmon/xmon.c +++ b/arch/powerpc/xmon/xmon.c @@ -2144,6 +2144,8 @@ static void dump_one_paca(int cpu) DUMP(p, slb_cache_ptr, "x"); for (i = 0; i < SLB_CACHE_ENTRIES; i++) printf(" slb_cache[%d]: = 0x%016lx\n", i, p->slb_cache[i]); + + DUMP(p, rfi_flush_fallback_area, "px"); #endif DUMP(p, dscr_default, "llx"); #ifdef CONFIG_PPC_BOOK3E diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 5ddb1debba95..23911ecfbad6 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -2721,21 +2721,28 @@ void kvm_arch_commit_memory_region(struct kvm *kvm, const struct kvm_memory_slot *new, enum kvm_mr_change change) { - int rc; - - /* If the basics of the memslot do not change, we do not want - * to update the gmap. Every update causes several unnecessary - * segment translation exceptions. This is usually handled just - * fine by the normal fault handler + gmap, but it will also - * cause faults on the prefix page of running guest CPUs. - */ - if (old->userspace_addr == mem->userspace_addr && - old->base_gfn * PAGE_SIZE == mem->guest_phys_addr && - old->npages * PAGE_SIZE == mem->memory_size) - return; + int rc = 0; - rc = gmap_map_segment(kvm->arch.gmap, mem->userspace_addr, - mem->guest_phys_addr, mem->memory_size); + switch (change) { + case KVM_MR_DELETE: + rc = gmap_unmap_segment(kvm->arch.gmap, old->base_gfn * PAGE_SIZE, + old->npages * PAGE_SIZE); + break; + case KVM_MR_MOVE: + rc = gmap_unmap_segment(kvm->arch.gmap, old->base_gfn * PAGE_SIZE, + old->npages * PAGE_SIZE); + if (rc) + break; + /* FALLTHROUGH */ + case KVM_MR_CREATE: + rc = gmap_map_segment(kvm->arch.gmap, mem->userspace_addr, + mem->guest_phys_addr, mem->memory_size); + break; + case KVM_MR_FLAGS_ONLY: + break; + default: + WARN(1, "Unknown KVM MR CHANGE: %d\n", change); + } if (rc) pr_warn("failed to commit memory region\n"); return; diff --git a/arch/sparc/mm/ultra.S b/arch/sparc/mm/ultra.S index fcf4d27a38fb..e09f7b440b8c 100644 --- a/arch/sparc/mm/ultra.S +++ b/arch/sparc/mm/ultra.S @@ -586,7 +586,7 @@ xcall_flush_tlb_kernel_range: /* 44 insns */ sub %g7, %g1, %g3 srlx %g3, 18, %g2 brnz,pn %g2, 2f - add %g2, 1, %g2 + sethi %hi(PAGE_SIZE), %g2 sub %g3, %g2, %g3 or %g1, 0x20, %g1 ! Nucleus 1: stxa %g0, [%g1 + %g3] ASI_DMMU_DEMAP @@ -750,7 +750,7 @@ __cheetah_xcall_flush_tlb_kernel_range: /* 44 insns */ sub %g7, %g1, %g3 srlx %g3, 18, %g2 brnz,pn %g2, 2f - add %g2, 1, %g2 + sethi %hi(PAGE_SIZE), %g2 sub %g3, %g2, %g3 or %g1, 0x20, %g1 ! Nucleus 1: stxa %g0, [%g1 + %g3] ASI_DMMU_DEMAP diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 2d63673f2505..75b5d8f59940 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -916,13 +916,7 @@ config NR_CPUS approximately eight kilobytes to the kernel image. config SCHED_SMT - bool "SMT (Hyperthreading) scheduler support" - depends on SMP - ---help--- - SMT scheduler support improves the CPU scheduler's decision making - when dealing with Intel Pentium 4 chips with HyperThreading at a - cost of slightly increased overhead in some places. If unsure say - N here. + def_bool y if SMP config SCHED_MC def_bool y diff --git a/arch/x86/Makefile b/arch/x86/Makefile index 568930248ad0..063de6441f8d 100644 --- a/arch/x86/Makefile +++ b/arch/x86/Makefile @@ -47,7 +47,7 @@ export REALMODE_CFLAGS export BITS ifdef CONFIG_X86_NEED_RELOCS - LDFLAGS_vmlinux := --emit-relocs + LDFLAGS_vmlinux := --emit-relocs --discard-none endif # diff --git a/arch/x86/configs/x86_64_cuttlefish_defconfig b/arch/x86/configs/x86_64_cuttlefish_defconfig index faa0479f17bd..4c80a8b6f0ce 100644 --- a/arch/x86/configs/x86_64_cuttlefish_defconfig +++ b/arch/x86/configs/x86_64_cuttlefish_defconfig @@ -197,7 +197,6 @@ CONFIG_CFG80211=y CONFIG_MAC80211=y CONFIG_RFKILL=y CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" -CONFIG_DEVTMPFS=y CONFIG_DEBUG_DEVRES=y CONFIG_OF=y CONFIG_OF_UNITTEST=y diff --git a/arch/x86/crypto/crct10dif-pclmul_glue.c b/arch/x86/crypto/crct10dif-pclmul_glue.c index cd4df9322501..7bbfe7d35da7 100644 --- a/arch/x86/crypto/crct10dif-pclmul_glue.c +++ b/arch/x86/crypto/crct10dif-pclmul_glue.c @@ -76,15 +76,14 @@ static int chksum_final(struct shash_desc *desc, u8 *out) return 0; } -static int __chksum_finup(__u16 *crcp, const u8 *data, unsigned int len, - u8 *out) +static int __chksum_finup(__u16 crc, const u8 *data, unsigned int len, u8 *out) { if (irq_fpu_usable()) { kernel_fpu_begin(); - *(__u16 *)out = crc_t10dif_pcl(*crcp, data, len); + *(__u16 *)out = crc_t10dif_pcl(crc, data, len); kernel_fpu_end(); } else - *(__u16 *)out = crc_t10dif_generic(*crcp, data, len); + *(__u16 *)out = crc_t10dif_generic(crc, data, len); return 0; } @@ -93,15 +92,13 @@ static int chksum_finup(struct shash_desc *desc, const u8 *data, { struct chksum_desc_ctx *ctx = shash_desc_ctx(desc); - return __chksum_finup(&ctx->crc, data, len, out); + return __chksum_finup(ctx->crc, data, len, out); } static int chksum_digest(struct shash_desc *desc, const u8 *data, unsigned int length, u8 *out) { - struct chksum_desc_ctx *ctx = shash_desc_ctx(desc); - - return __chksum_finup(&ctx->crc, data, length, out); + return __chksum_finup(0, data, length, out); } static struct shash_alg alg = { diff --git a/arch/x86/entry/common.c b/arch/x86/entry/common.c index c611c1ed064b..cef47fe7817e 100644 --- a/arch/x86/entry/common.c +++ b/arch/x86/entry/common.c @@ -29,6 +29,7 @@ #include <asm/vdso.h> #include <asm/uaccess.h> #include <asm/cpufeature.h> +#include <asm/nospec-branch.h> #define CREATE_TRACE_POINTS #include <trace/events/syscalls.h> @@ -232,6 +233,8 @@ __visible inline void prepare_exit_to_usermode(struct pt_regs *regs) #endif user_enter(); + + mds_user_clear_cpu_buffers(); } #define SYSCALL_EXIT_WORK_FLAGS \ diff --git a/arch/x86/entry/vdso/Makefile b/arch/x86/entry/vdso/Makefile index c2962dac577b..4b66d42c56fb 100644 --- a/arch/x86/entry/vdso/Makefile +++ b/arch/x86/entry/vdso/Makefile @@ -162,7 +162,8 @@ quiet_cmd_vdso = VDSO $@ sh $(srctree)/$(src)/checkundef.sh '$(NM)' '$@' VDSO_LDFLAGS = -shared $(call ld-option, --hash-style=both) \ - $(call ld-option, --build-id) -Bsymbolic + $(call ld-option, --build-id) $(call ld-option, --eh-frame-hdr) \ + -Bsymbolic GCOV_PROFILE := n # diff --git a/arch/x86/ia32/ia32_signal.c b/arch/x86/ia32/ia32_signal.c index 0552884da18d..a7b9acd709db 100644 --- a/arch/x86/ia32/ia32_signal.c +++ b/arch/x86/ia32/ia32_signal.c @@ -60,9 +60,8 @@ } while (0) #define RELOAD_SEG(seg) { \ - unsigned int pre = GET_SEG(seg); \ + unsigned int pre = (seg) | 3; \ unsigned int cur = get_user_seg(seg); \ - pre |= 3; \ if (pre != cur) \ set_user_seg(seg, pre); \ } @@ -71,6 +70,7 @@ static int ia32_restore_sigcontext(struct pt_regs *regs, struct sigcontext_32 __user *sc) { unsigned int tmpflags, err = 0; + u16 gs, fs, es, ds; void __user *buf; u32 tmp; @@ -78,16 +78,10 @@ static int ia32_restore_sigcontext(struct pt_regs *regs, current->restart_block.fn = do_no_restart_syscall; get_user_try { - /* - * Reload fs and gs if they have changed in the signal - * handler. This does not handle long fs/gs base changes in - * the handler, but does not clobber them at least in the - * normal case. - */ - RELOAD_SEG(gs); - RELOAD_SEG(fs); - RELOAD_SEG(ds); - RELOAD_SEG(es); + gs = GET_SEG(gs); + fs = GET_SEG(fs); + ds = GET_SEG(ds); + es = GET_SEG(es); COPY(di); COPY(si); COPY(bp); COPY(sp); COPY(bx); COPY(dx); COPY(cx); COPY(ip); COPY(ax); @@ -105,6 +99,17 @@ static int ia32_restore_sigcontext(struct pt_regs *regs, buf = compat_ptr(tmp); } get_user_catch(err); + /* + * Reload fs and gs if they have changed in the signal + * handler. This does not handle long fs/gs base changes in + * the handler, but does not clobber them at least in the + * normal case. + */ + RELOAD_SEG(gs); + RELOAD_SEG(fs); + RELOAD_SEG(ds); + RELOAD_SEG(es); + err |= fpu__restore_sig(buf, 1); force_iret(); diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h index a5fa3195a230..d9f7d1770e98 100644 --- a/arch/x86/include/asm/cpufeatures.h +++ b/arch/x86/include/asm/cpufeatures.h @@ -214,6 +214,7 @@ #define X86_FEATURE_STIBP ( 7*32+27) /* Single Thread Indirect Branch Predictors */ #define X86_FEATURE_ZEN ( 7*32+28) /* "" CPU is AMD family 0x17 (Zen) */ #define X86_FEATURE_L1TF_PTEINV ( 7*32+29) /* "" L1TF workaround PTE inversion */ +#define X86_FEATURE_IBRS_ENHANCED ( 7*32+30) /* Enhanced IBRS */ /* Virtualization flags: Linux defined, word 8 */ #define X86_FEATURE_TPR_SHADOW ( 8*32+ 0) /* Intel TPR Shadow */ @@ -265,10 +266,12 @@ /* AMD-defined CPU features, CPUID level 0x80000008 (ebx), word 13 */ #define X86_FEATURE_CLZERO (13*32+0) /* CLZERO instruction */ -#define X86_FEATURE_AMD_IBPB (13*32+12) /* Indirect Branch Prediction Barrier */ -#define X86_FEATURE_AMD_IBRS (13*32+14) /* Indirect Branch Restricted Speculation */ -#define X86_FEATURE_AMD_STIBP (13*32+15) /* Single Thread Indirect Branch Predictors */ +#define X86_FEATURE_AMD_IBPB (13*32+12) /* "" Indirect Branch Prediction Barrier */ +#define X86_FEATURE_AMD_IBRS (13*32+14) /* "" Indirect Branch Restricted Speculation */ +#define X86_FEATURE_AMD_STIBP (13*32+15) /* "" Single Thread Indirect Branch Predictors */ +#define X86_FEATURE_AMD_SSBD (13*32+24) /* "" Speculative Store Bypass Disable */ #define X86_FEATURE_VIRT_SSBD (13*32+25) /* Virtualized Speculative Store Bypass Disable */ +#define X86_FEATURE_AMD_SSB_NO (13*32+26) /* "" Speculative Store Bypass is fixed in hardware. */ /* Thermal and Power Management Leaf, CPUID level 0x00000006 (eax), word 14 */ #define X86_FEATURE_DTHERM (14*32+ 0) /* Digital Thermal Sensor */ @@ -307,6 +310,7 @@ /* Intel-defined CPU features, CPUID level 0x00000007:0 (EDX), word 18 */ #define X86_FEATURE_AVX512_4VNNIW (18*32+ 2) /* AVX-512 Neural Network Instructions */ #define X86_FEATURE_AVX512_4FMAPS (18*32+ 3) /* AVX-512 Multiply Accumulation Single precision */ +#define X86_FEATURE_MD_CLEAR (18*32+10) /* VERW clears CPU buffers */ #define X86_FEATURE_SPEC_CTRL (18*32+26) /* "" Speculation Control (IBRS + IBPB) */ #define X86_FEATURE_INTEL_STIBP (18*32+27) /* "" Single Thread Indirect Branch Predictors */ #define X86_FEATURE_FLUSH_L1D (18*32+28) /* Flush L1D cache */ @@ -332,5 +336,7 @@ #define X86_BUG_SPECTRE_V2 X86_BUG(16) /* CPU is affected by Spectre variant 2 attack with indirect branches */ #define X86_BUG_SPEC_STORE_BYPASS X86_BUG(17) /* CPU is affected by speculative store bypass attack */ #define X86_BUG_L1TF X86_BUG(18) /* CPU is affected by L1 Terminal Fault */ +#define X86_BUG_MDS X86_BUG(19) /* CPU is affected by Microarchitectural data sampling */ +#define X86_BUG_MSBDS_ONLY X86_BUG(20) /* CPU is only affected by the MSDBS variant of BUG_MDS */ #endif /* _ASM_X86_CPUFEATURES_H */ diff --git a/arch/x86/include/asm/intel-family.h b/arch/x86/include/asm/intel-family.h index e13ff5a14633..6801f958e254 100644 --- a/arch/x86/include/asm/intel-family.h +++ b/arch/x86/include/asm/intel-family.h @@ -50,19 +50,23 @@ /* "Small Core" Processors (Atom) */ -#define INTEL_FAM6_ATOM_PINEVIEW 0x1C -#define INTEL_FAM6_ATOM_LINCROFT 0x26 -#define INTEL_FAM6_ATOM_PENWELL 0x27 -#define INTEL_FAM6_ATOM_CLOVERVIEW 0x35 -#define INTEL_FAM6_ATOM_CEDARVIEW 0x36 -#define INTEL_FAM6_ATOM_SILVERMONT1 0x37 /* BayTrail/BYT / Valleyview */ -#define INTEL_FAM6_ATOM_SILVERMONT2 0x4D /* Avaton/Rangely */ -#define INTEL_FAM6_ATOM_AIRMONT 0x4C /* CherryTrail / Braswell */ -#define INTEL_FAM6_ATOM_MERRIFIELD 0x4A /* Tangier */ -#define INTEL_FAM6_ATOM_MOOREFIELD 0x5A /* Annidale */ -#define INTEL_FAM6_ATOM_GOLDMONT 0x5C -#define INTEL_FAM6_ATOM_DENVERTON 0x5F /* Goldmont Microserver */ -#define INTEL_FAM6_ATOM_GEMINI_LAKE 0x7A +#define INTEL_FAM6_ATOM_BONNELL 0x1C /* Diamondville, Pineview */ +#define INTEL_FAM6_ATOM_BONNELL_MID 0x26 /* Silverthorne, Lincroft */ + +#define INTEL_FAM6_ATOM_SALTWELL 0x36 /* Cedarview */ +#define INTEL_FAM6_ATOM_SALTWELL_MID 0x27 /* Penwell */ +#define INTEL_FAM6_ATOM_SALTWELL_TABLET 0x35 /* Cloverview */ + +#define INTEL_FAM6_ATOM_SILVERMONT 0x37 /* Bay Trail, Valleyview */ +#define INTEL_FAM6_ATOM_SILVERMONT_X 0x4D /* Avaton, Rangely */ +#define INTEL_FAM6_ATOM_SILVERMONT_MID 0x4A /* Merriefield */ + +#define INTEL_FAM6_ATOM_AIRMONT 0x4C /* Cherry Trail, Braswell */ +#define INTEL_FAM6_ATOM_AIRMONT_MID 0x5A /* Moorefield */ + +#define INTEL_FAM6_ATOM_GOLDMONT 0x5C /* Apollo Lake */ +#define INTEL_FAM6_ATOM_GOLDMONT_X 0x5F /* Denverton */ +#define INTEL_FAM6_ATOM_GOLDMONT_PLUS 0x7A /* Gemini Lake */ /* Xeon Phi */ diff --git a/arch/x86/include/asm/irqflags.h b/arch/x86/include/asm/irqflags.h index 8afbdcd3032b..46d8b99a0ff1 100644 --- a/arch/x86/include/asm/irqflags.h +++ b/arch/x86/include/asm/irqflags.h @@ -4,6 +4,9 @@ #include <asm/processor-flags.h> #ifndef __ASSEMBLY__ + +#include <asm/nospec-branch.h> + /* * Interrupt control: */ @@ -49,11 +52,13 @@ static inline void native_irq_enable(void) static inline void native_safe_halt(void) { + mds_idle_clear_cpu_buffers(); asm volatile("sti; hlt": : :"memory"); } static inline void native_halt(void) { + mds_idle_clear_cpu_buffers(); asm volatile("hlt": : :"memory"); } diff --git a/arch/x86/include/asm/microcode_intel.h b/arch/x86/include/asm/microcode_intel.h index 8559b0102ea1..90343ba50485 100644 --- a/arch/x86/include/asm/microcode_intel.h +++ b/arch/x86/include/asm/microcode_intel.h @@ -53,6 +53,21 @@ struct extended_sigtable { #define exttable_size(et) ((et)->count * EXT_SIGNATURE_SIZE + EXT_HEADER_SIZE) +static inline u32 intel_get_microcode_revision(void) +{ + u32 rev, dummy; + + native_wrmsrl(MSR_IA32_UCODE_REV, 0); + + /* As documented in the SDM: Do a CPUID 1 here */ + sync_core(); + + /* get the current revision from MSR 0x8B */ + native_rdmsr(MSR_IA32_UCODE_REV, dummy, rev); + + return rev; +} + extern int has_newer_microcode(void *mc, unsigned int csig, int cpf, int rev); extern int microcode_sanity_check(void *mc, int print_err); extern int find_matching_signature(void *mc, unsigned int csig, int cpf); diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index caa00191e565..d4f5b8209393 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h @@ -1,6 +1,8 @@ #ifndef _ASM_X86_MSR_INDEX_H #define _ASM_X86_MSR_INDEX_H +#include <linux/bits.h> + /* CPU model specific register (MSR) numbers */ /* x86-64 specific MSRs */ @@ -33,13 +35,14 @@ /* Intel MSRs. Some also available on other CPUs */ #define MSR_IA32_SPEC_CTRL 0x00000048 /* Speculation Control */ -#define SPEC_CTRL_IBRS (1 << 0) /* Indirect Branch Restricted Speculation */ -#define SPEC_CTRL_STIBP (1 << 1) /* Single Thread Indirect Branch Predictors */ +#define SPEC_CTRL_IBRS BIT(0) /* Indirect Branch Restricted Speculation */ +#define SPEC_CTRL_STIBP_SHIFT 1 /* Single Thread Indirect Branch Predictor (STIBP) bit */ +#define SPEC_CTRL_STIBP BIT(SPEC_CTRL_STIBP_SHIFT) /* STIBP mask */ #define SPEC_CTRL_SSBD_SHIFT 2 /* Speculative Store Bypass Disable bit */ -#define SPEC_CTRL_SSBD (1 << SPEC_CTRL_SSBD_SHIFT) /* Speculative Store Bypass Disable */ +#define SPEC_CTRL_SSBD BIT(SPEC_CTRL_SSBD_SHIFT) /* Speculative Store Bypass Disable */ #define MSR_IA32_PRED_CMD 0x00000049 /* Prediction Command */ -#define PRED_CMD_IBPB (1 << 0) /* Indirect Branch Prediction Barrier */ +#define PRED_CMD_IBPB BIT(0) /* Indirect Branch Prediction Barrier */ #define MSR_IA32_PERFCTR0 0x000000c1 #define MSR_IA32_PERFCTR1 0x000000c2 @@ -56,13 +59,18 @@ #define MSR_MTRRcap 0x000000fe #define MSR_IA32_ARCH_CAPABILITIES 0x0000010a -#define ARCH_CAP_RDCL_NO (1 << 0) /* Not susceptible to Meltdown */ -#define ARCH_CAP_IBRS_ALL (1 << 1) /* Enhanced IBRS support */ -#define ARCH_CAP_SSB_NO (1 << 4) /* - * Not susceptible to Speculative Store Bypass - * attack, so no Speculative Store Bypass - * control required. - */ +#define ARCH_CAP_RDCL_NO BIT(0) /* Not susceptible to Meltdown */ +#define ARCH_CAP_IBRS_ALL BIT(1) /* Enhanced IBRS support */ +#define ARCH_CAP_SSB_NO BIT(4) /* + * Not susceptible to Speculative Store Bypass + * attack, so no Speculative Store Bypass + * control required. + */ +#define ARCH_CAP_MDS_NO BIT(5) /* + * Not susceptible to + * Microarchitectural Data + * Sampling (MDS) vulnerabilities. + */ #define MSR_IA32_BBL_CR_CTL 0x00000119 #define MSR_IA32_BBL_CR_CTL3 0x0000011e diff --git a/arch/x86/include/asm/mwait.h b/arch/x86/include/asm/mwait.h index 0deeb2d26df7..b98dbdaee8ac 100644 --- a/arch/x86/include/asm/mwait.h +++ b/arch/x86/include/asm/mwait.h @@ -4,6 +4,7 @@ #include <linux/sched.h> #include <asm/cpufeature.h> +#include <asm/nospec-branch.h> #define MWAIT_SUBSTATE_MASK 0xf #define MWAIT_CSTATE_MASK 0xf @@ -38,6 +39,8 @@ static inline void __monitorx(const void *eax, unsigned long ecx, static inline void __mwait(unsigned long eax, unsigned long ecx) { + mds_idle_clear_cpu_buffers(); + /* "mwait %eax, %ecx;" */ asm volatile(".byte 0x0f, 0x01, 0xc9;" :: "a" (eax), "c" (ecx)); @@ -72,6 +75,8 @@ static inline void __mwait(unsigned long eax, unsigned long ecx) static inline void __mwaitx(unsigned long eax, unsigned long ebx, unsigned long ecx) { + /* No MDS buffer clear as this is AMD/HYGON only */ + /* "mwaitx %eax, %ebx, %ecx;" */ asm volatile(".byte 0x0f, 0x01, 0xfb;" :: "a" (eax), "b" (ebx), "c" (ecx)); @@ -79,6 +84,8 @@ static inline void __mwaitx(unsigned long eax, unsigned long ebx, static inline void __sti_mwait(unsigned long eax, unsigned long ecx) { + mds_idle_clear_cpu_buffers(); + trace_hardirqs_on(); /* "mwait %eax, %ecx;" */ asm volatile("sti; .byte 0x0f, 0x01, 0xc9;" diff --git a/arch/x86/include/asm/nospec-branch.h b/arch/x86/include/asm/nospec-branch.h index b4c74c24c890..e58c078f3d96 100644 --- a/arch/x86/include/asm/nospec-branch.h +++ b/arch/x86/include/asm/nospec-branch.h @@ -3,6 +3,8 @@ #ifndef _ASM_X86_NOSPEC_BRANCH_H_ #define _ASM_X86_NOSPEC_BRANCH_H_ +#include <linux/static_key.h> + #include <asm/alternative.h> #include <asm/alternative-asm.h> #include <asm/cpufeatures.h> @@ -169,7 +171,15 @@ enum spectre_v2_mitigation { SPECTRE_V2_RETPOLINE_MINIMAL_AMD, SPECTRE_V2_RETPOLINE_GENERIC, SPECTRE_V2_RETPOLINE_AMD, - SPECTRE_V2_IBRS, + SPECTRE_V2_IBRS_ENHANCED, +}; + +/* The indirect branch speculation control variants */ +enum spectre_v2_user_mitigation { + SPECTRE_V2_USER_NONE, + SPECTRE_V2_USER_STRICT, + SPECTRE_V2_USER_PRCTL, + SPECTRE_V2_USER_SECCOMP, }; /* The Speculative Store Bypass disable variants */ @@ -248,6 +258,60 @@ do { \ preempt_enable(); \ } while (0) +DECLARE_STATIC_KEY_FALSE(switch_to_cond_stibp); +DECLARE_STATIC_KEY_FALSE(switch_mm_cond_ibpb); +DECLARE_STATIC_KEY_FALSE(switch_mm_always_ibpb); + +DECLARE_STATIC_KEY_FALSE(mds_user_clear); +DECLARE_STATIC_KEY_FALSE(mds_idle_clear); + +#include <asm/segment.h> + +/** + * mds_clear_cpu_buffers - Mitigation for MDS vulnerability + * + * This uses the otherwise unused and obsolete VERW instruction in + * combination with microcode which triggers a CPU buffer flush when the + * instruction is executed. + */ +static inline void mds_clear_cpu_buffers(void) +{ + static const u16 ds = __KERNEL_DS; + + /* + * Has to be the memory-operand variant because only that + * guarantees the CPU buffer flush functionality according to + * documentation. The register-operand variant does not. + * Works with any segment selector, but a valid writable + * data segment is the fastest variant. + * + * "cc" clobber is required because VERW modifies ZF. + */ + asm volatile("verw %[ds]" : : [ds] "m" (ds) : "cc"); +} + +/** + * mds_user_clear_cpu_buffers - Mitigation for MDS vulnerability + * + * Clear CPU buffers if the corresponding static key is enabled + */ +static inline void mds_user_clear_cpu_buffers(void) +{ + if (static_branch_likely(&mds_user_clear)) + mds_clear_cpu_buffers(); +} + +/** + * mds_idle_clear_cpu_buffers - Mitigation for MDS vulnerability + * + * Clear CPU buffers if the corresponding static key is enabled + */ +static inline void mds_idle_clear_cpu_buffers(void) +{ + if (static_branch_likely(&mds_idle_clear)) + mds_clear_cpu_buffers(); +} + #endif /* __ASSEMBLY__ */ /* diff --git a/arch/x86/include/asm/pgtable_64.h b/arch/x86/include/asm/pgtable_64.h index 221a32ed1372..f12e61e2a86b 100644 --- a/arch/x86/include/asm/pgtable_64.h +++ b/arch/x86/include/asm/pgtable_64.h @@ -44,15 +44,15 @@ struct mm_struct; void set_pte_vaddr_pud(pud_t *pud_page, unsigned long vaddr, pte_t new_pte); -static inline void native_pte_clear(struct mm_struct *mm, unsigned long addr, - pte_t *ptep) +static inline void native_set_pte(pte_t *ptep, pte_t pte) { - *ptep = native_make_pte(0); + WRITE_ONCE(*ptep, pte); } -static inline void native_set_pte(pte_t *ptep, pte_t pte) +static inline void native_pte_clear(struct mm_struct *mm, unsigned long addr, + pte_t *ptep) { - *ptep = pte; + native_set_pte(ptep, native_make_pte(0)); } static inline void native_set_pte_atomic(pte_t *ptep, pte_t pte) @@ -62,7 +62,7 @@ static inline void native_set_pte_atomic(pte_t *ptep, pte_t pte) static inline void native_set_pmd(pmd_t *pmdp, pmd_t pmd) { - *pmdp = pmd; + WRITE_ONCE(*pmdp, pmd); } static inline void native_pmd_clear(pmd_t *pmd) @@ -98,7 +98,7 @@ static inline pmd_t native_pmdp_get_and_clear(pmd_t *xp) static inline void native_set_pud(pud_t *pudp, pud_t pud) { - *pudp = pud; + WRITE_ONCE(*pudp, pud); } static inline void native_pud_clear(pud_t *pud) @@ -131,7 +131,7 @@ static inline pgd_t *native_get_shadow_pgd(pgd_t *pgdp) static inline void native_set_pgd(pgd_t *pgdp, pgd_t pgd) { - *pgdp = kaiser_set_shadow_pgd(pgdp, pgd); + WRITE_ONCE(*pgdp, kaiser_set_shadow_pgd(pgdp, pgd)); } static inline void native_pgd_clear(pgd_t *pgd) diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index 440a948c4feb..dab73faef9b0 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h @@ -845,4 +845,11 @@ bool xen_set_default_idle(void); void stop_this_cpu(void *dummy); void df_debug(struct pt_regs *regs, long error_code); + +enum mds_mitigations { + MDS_MITIGATION_OFF, + MDS_MITIGATION_FULL, + MDS_MITIGATION_VMWERV, +}; + #endif /* _ASM_X86_PROCESSOR_H */ diff --git a/arch/x86/include/asm/spec-ctrl.h b/arch/x86/include/asm/spec-ctrl.h index ae7c2c5cd7f0..5393babc0598 100644 --- a/arch/x86/include/asm/spec-ctrl.h +++ b/arch/x86/include/asm/spec-ctrl.h @@ -53,12 +53,24 @@ static inline u64 ssbd_tif_to_spec_ctrl(u64 tifn) return (tifn & _TIF_SSBD) >> (TIF_SSBD - SPEC_CTRL_SSBD_SHIFT); } +static inline u64 stibp_tif_to_spec_ctrl(u64 tifn) +{ + BUILD_BUG_ON(TIF_SPEC_IB < SPEC_CTRL_STIBP_SHIFT); + return (tifn & _TIF_SPEC_IB) >> (TIF_SPEC_IB - SPEC_CTRL_STIBP_SHIFT); +} + static inline unsigned long ssbd_spec_ctrl_to_tif(u64 spec_ctrl) { BUILD_BUG_ON(TIF_SSBD < SPEC_CTRL_SSBD_SHIFT); return (spec_ctrl & SPEC_CTRL_SSBD) << (TIF_SSBD - SPEC_CTRL_SSBD_SHIFT); } +static inline unsigned long stibp_spec_ctrl_to_tif(u64 spec_ctrl) +{ + BUILD_BUG_ON(TIF_SPEC_IB < SPEC_CTRL_STIBP_SHIFT); + return (spec_ctrl & SPEC_CTRL_STIBP) << (TIF_SPEC_IB - SPEC_CTRL_STIBP_SHIFT); +} + static inline u64 ssbd_tif_to_amd_ls_cfg(u64 tifn) { return (tifn & _TIF_SSBD) ? x86_amd_ls_cfg_ssbd_mask : 0ULL; @@ -70,11 +82,7 @@ extern void speculative_store_bypass_ht_init(void); static inline void speculative_store_bypass_ht_init(void) { } #endif -extern void speculative_store_bypass_update(unsigned long tif); - -static inline void speculative_store_bypass_update_current(void) -{ - speculative_store_bypass_update(current_thread_info()->flags); -} +extern void speculation_ctrl_update(unsigned long tif); +extern void speculation_ctrl_update_current(void); #endif diff --git a/arch/x86/include/asm/switch_to.h b/arch/x86/include/asm/switch_to.h index 025ecfaba9c9..4ff0878f4633 100644 --- a/arch/x86/include/asm/switch_to.h +++ b/arch/x86/include/asm/switch_to.h @@ -6,9 +6,6 @@ struct task_struct; /* one of the stranger aspects of C forward declarations */ __visible struct task_struct *__switch_to(struct task_struct *prev, struct task_struct *next); -struct tss_struct; -void __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p, - struct tss_struct *tss); #ifdef CONFIG_X86_32 diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h index c14f699f5c36..53e99f741705 100644 --- a/arch/x86/include/asm/thread_info.h +++ b/arch/x86/include/asm/thread_info.h @@ -92,10 +92,12 @@ struct thread_info { #define TIF_SIGPENDING 2 /* signal pending */ #define TIF_NEED_RESCHED 3 /* rescheduling necessary */ #define TIF_SINGLESTEP 4 /* reenable singlestep on user return*/ -#define TIF_SSBD 5 /* Reduced data speculation */ +#define TIF_SSBD 5 /* Speculative store bypass disable */ #define TIF_SYSCALL_EMU 6 /* syscall emulation active */ #define TIF_SYSCALL_AUDIT 7 /* syscall auditing active */ #define TIF_SECCOMP 8 /* secure computing */ +#define TIF_SPEC_IB 9 /* Indirect branch speculation mitigation */ +#define TIF_SPEC_FORCE_UPDATE 10 /* Force speculation MSR update in context switch */ #define TIF_USER_RETURN_NOTIFY 11 /* notify kernel of userspace return */ #define TIF_UPROBE 12 /* breakpointed or singlestepping */ #define TIF_NOTSC 16 /* TSC is not accessible in userland */ @@ -122,6 +124,8 @@ struct thread_info { #define _TIF_SYSCALL_EMU (1 << TIF_SYSCALL_EMU) #define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT) #define _TIF_SECCOMP (1 << TIF_SECCOMP) +#define _TIF_SPEC_IB (1 << TIF_SPEC_IB) +#define _TIF_SPEC_FORCE_UPDATE (1 << TIF_SPEC_FORCE_UPDATE) #define _TIF_USER_RETURN_NOTIFY (1 << TIF_USER_RETURN_NOTIFY) #define _TIF_UPROBE (1 << TIF_UPROBE) #define _TIF_NOTSC (1 << TIF_NOTSC) @@ -150,8 +154,18 @@ struct thread_info { _TIF_NOHZ | _TIF_FSCHECK) /* flags to check in __switch_to() */ -#define _TIF_WORK_CTXSW \ - (_TIF_IO_BITMAP|_TIF_NOTSC|_TIF_BLOCKSTEP|_TIF_SSBD) +#define _TIF_WORK_CTXSW_BASE \ + (_TIF_IO_BITMAP|_TIF_NOTSC|_TIF_BLOCKSTEP| \ + _TIF_SSBD | _TIF_SPEC_FORCE_UPDATE) + +/* + * Avoid calls to __switch_to_xtra() on UP as STIBP is not evaluated. + */ +#ifdef CONFIG_SMP +# define _TIF_WORK_CTXSW (_TIF_WORK_CTXSW_BASE | _TIF_SPEC_IB) +#else +# define _TIF_WORK_CTXSW (_TIF_WORK_CTXSW_BASE) +#endif #define _TIF_WORK_CTXSW_PREV (_TIF_WORK_CTXSW|_TIF_USER_RETURN_NOTIFY) #define _TIF_WORK_CTXSW_NEXT (_TIF_WORK_CTXSW) diff --git a/arch/x86/include/asm/tlbflush.h b/arch/x86/include/asm/tlbflush.h index 72cfe3e53af1..8dab88b85785 100644 --- a/arch/x86/include/asm/tlbflush.h +++ b/arch/x86/include/asm/tlbflush.h @@ -68,8 +68,12 @@ static inline void invpcid_flush_all_nonglobals(void) struct tlb_state { struct mm_struct *active_mm; int state; - /* last user mm's ctx id */ - u64 last_ctx_id; + + /* Last user mm for optimizing IBPB */ + union { + struct mm_struct *last_user_mm; + unsigned long last_user_mm_ibpb; + }; /* * Access to this CR4 shadow and to H/W CR4 is protected by diff --git a/arch/x86/include/uapi/asm/Kbuild b/arch/x86/include/uapi/asm/Kbuild index 3dec769cadf7..1c532b3f18ea 100644 --- a/arch/x86/include/uapi/asm/Kbuild +++ b/arch/x86/include/uapi/asm/Kbuild @@ -27,7 +27,6 @@ header-y += ldt.h header-y += mce.h header-y += mman.h header-y += msgbuf.h -header-y += msr-index.h header-y += msr.h header-y += mtrr.h header-y += param.h diff --git a/arch/x86/include/uapi/asm/mce.h b/arch/x86/include/uapi/asm/mce.h index 03429da2fa80..83b9be4e0492 100644 --- a/arch/x86/include/uapi/asm/mce.h +++ b/arch/x86/include/uapi/asm/mce.h @@ -26,6 +26,10 @@ struct mce { __u32 socketid; /* CPU socket ID */ __u32 apicid; /* CPU initial apic ID */ __u64 mcgcap; /* MCGCAP MSR: machine check capabilities of CPU */ + __u64 synd; /* MCA_SYND MSR: only valid on SMCA systems */ + __u64 ipid; /* MCA_IPID MSR: only valid on SMCA systems */ + __u64 ppin; /* Protected Processor Inventory Number */ + __u32 microcode;/* Microcode revision */ }; #define MCE_GET_RECORD_LEN _IOR('M', 1, int) diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index e94e6f16172b..6f2483292de0 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -717,8 +717,11 @@ static void init_amd_zn(struct cpuinfo_x86 *c) { set_cpu_cap(c, X86_FEATURE_ZEN); - /* Fix erratum 1076: CPB feature bit not being set in CPUID. */ - if (!cpu_has(c, X86_FEATURE_CPB)) + /* + * Fix erratum 1076: CPB feature bit not being set in CPUID. + * Always set it, except when running under a hypervisor. + */ + if (!cpu_has(c, X86_FEATURE_HYPERVISOR) && !cpu_has(c, X86_FEATURE_CPB)) set_cpu_cap(c, X86_FEATURE_CPB); } diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c index 621bc6561189..2017fa20611c 100644 --- a/arch/x86/kernel/cpu/bugs.c +++ b/arch/x86/kernel/cpu/bugs.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/nospec.h> #include <linux/prctl.h> +#include <linux/sched/smt.h> #include <asm/spec-ctrl.h> #include <asm/cmdline.h> @@ -23,6 +24,7 @@ #include <asm/msr.h> #include <asm/paravirt.h> #include <asm/alternative.h> +#include <asm/hypervisor.h> #include <asm/pgtable.h> #include <asm/cacheflush.h> #include <asm/intel-family.h> @@ -31,13 +33,12 @@ static void __init spectre_v2_select_mitigation(void); static void __init ssb_select_mitigation(void); static void __init l1tf_select_mitigation(void); +static void __init mds_select_mitigation(void); -/* - * Our boot-time value of the SPEC_CTRL MSR. We read it once so that any - * writes to SPEC_CTRL contain whatever reserved bits have been set. - */ +/* The base value of the SPEC_CTRL MSR that always has to be preserved. */ u64 x86_spec_ctrl_base; EXPORT_SYMBOL_GPL(x86_spec_ctrl_base); +static DEFINE_MUTEX(spec_ctrl_mutex); /* * The vendor and possibly platform specific bits which can be modified in @@ -52,6 +53,19 @@ static u64 x86_spec_ctrl_mask = SPEC_CTRL_IBRS; u64 x86_amd_ls_cfg_base; u64 x86_amd_ls_cfg_ssbd_mask; +/* Control conditional STIPB in switch_to() */ +DEFINE_STATIC_KEY_FALSE(switch_to_cond_stibp); +/* Control conditional IBPB in switch_mm() */ +DEFINE_STATIC_KEY_FALSE(switch_mm_cond_ibpb); +/* Control unconditional IBPB in switch_mm() */ +DEFINE_STATIC_KEY_FALSE(switch_mm_always_ibpb); + +/* Control MDS CPU buffer clear before returning to user space */ +DEFINE_STATIC_KEY_FALSE(mds_user_clear); +/* Control MDS CPU buffer clear before idling (halt, mwait) */ +DEFINE_STATIC_KEY_FALSE(mds_idle_clear); +EXPORT_SYMBOL_GPL(mds_idle_clear); + void __init check_bugs(void) { identify_boot_cpu(); @@ -84,6 +98,10 @@ void __init check_bugs(void) l1tf_select_mitigation(); + mds_select_mitigation(); + + arch_smt_update(); + #ifdef CONFIG_X86_32 /* * Check whether we are able to run this kernel safely on SMP. @@ -116,29 +134,6 @@ void __init check_bugs(void) #endif } -/* The kernel command line selection */ -enum spectre_v2_mitigation_cmd { - SPECTRE_V2_CMD_NONE, - SPECTRE_V2_CMD_AUTO, - SPECTRE_V2_CMD_FORCE, - SPECTRE_V2_CMD_RETPOLINE, - SPECTRE_V2_CMD_RETPOLINE_GENERIC, - SPECTRE_V2_CMD_RETPOLINE_AMD, -}; - -static const char *spectre_v2_strings[] = { - [SPECTRE_V2_NONE] = "Vulnerable", - [SPECTRE_V2_RETPOLINE_MINIMAL] = "Vulnerable: Minimal generic ASM retpoline", - [SPECTRE_V2_RETPOLINE_MINIMAL_AMD] = "Vulnerable: Minimal AMD ASM retpoline", - [SPECTRE_V2_RETPOLINE_GENERIC] = "Mitigation: Full generic retpoline", - [SPECTRE_V2_RETPOLINE_AMD] = "Mitigation: Full AMD retpoline", -}; - -#undef pr_fmt -#define pr_fmt(fmt) "Spectre V2 : " fmt - -static enum spectre_v2_mitigation spectre_v2_enabled = SPECTRE_V2_NONE; - void x86_virt_spec_ctrl(u64 guest_spec_ctrl, u64 guest_virt_spec_ctrl, bool setguest) { @@ -156,9 +151,14 @@ x86_virt_spec_ctrl(u64 guest_spec_ctrl, u64 guest_virt_spec_ctrl, bool setguest) guestval |= guest_spec_ctrl & x86_spec_ctrl_mask; /* SSBD controlled in MSR_SPEC_CTRL */ - if (static_cpu_has(X86_FEATURE_SPEC_CTRL_SSBD)) + if (static_cpu_has(X86_FEATURE_SPEC_CTRL_SSBD) || + static_cpu_has(X86_FEATURE_AMD_SSBD)) hostval |= ssbd_tif_to_spec_ctrl(ti->flags); + /* Conditional STIBP enabled? */ + if (static_branch_unlikely(&switch_to_cond_stibp)) + hostval |= stibp_tif_to_spec_ctrl(ti->flags); + if (hostval != guestval) { msrval = setguest ? guestval : hostval; wrmsrl(MSR_IA32_SPEC_CTRL, msrval); @@ -192,7 +192,7 @@ x86_virt_spec_ctrl(u64 guest_spec_ctrl, u64 guest_virt_spec_ctrl, bool setguest) tif = setguest ? ssbd_spec_ctrl_to_tif(guestval) : ssbd_spec_ctrl_to_tif(hostval); - speculative_store_bypass_update(tif); + speculation_ctrl_update(tif); } } EXPORT_SYMBOL_GPL(x86_virt_spec_ctrl); @@ -207,6 +207,57 @@ static void x86_amd_ssb_disable(void) wrmsrl(MSR_AMD64_LS_CFG, msrval); } +#undef pr_fmt +#define pr_fmt(fmt) "MDS: " fmt + +/* Default mitigation for MDS-affected CPUs */ +static enum mds_mitigations mds_mitigation = MDS_MITIGATION_FULL; + +static const char * const mds_strings[] = { + [MDS_MITIGATION_OFF] = "Vulnerable", + [MDS_MITIGATION_FULL] = "Mitigation: Clear CPU buffers", + [MDS_MITIGATION_VMWERV] = "Vulnerable: Clear CPU buffers attempted, no microcode", +}; + +static void __init mds_select_mitigation(void) +{ + if (!boot_cpu_has_bug(X86_BUG_MDS) || cpu_mitigations_off()) { + mds_mitigation = MDS_MITIGATION_OFF; + return; + } + + if (mds_mitigation == MDS_MITIGATION_FULL) { + if (!boot_cpu_has(X86_FEATURE_MD_CLEAR)) + mds_mitigation = MDS_MITIGATION_VMWERV; + static_branch_enable(&mds_user_clear); + } + pr_info("%s\n", mds_strings[mds_mitigation]); +} + +static int __init mds_cmdline(char *str) +{ + if (!boot_cpu_has_bug(X86_BUG_MDS)) + return 0; + + if (!str) + return -EINVAL; + + if (!strcmp(str, "off")) + mds_mitigation = MDS_MITIGATION_OFF; + else if (!strcmp(str, "full")) + mds_mitigation = MDS_MITIGATION_FULL; + + return 0; +} +early_param("mds", mds_cmdline); + +#undef pr_fmt +#define pr_fmt(fmt) "Spectre V2 : " fmt + +static enum spectre_v2_mitigation spectre_v2_enabled = SPECTRE_V2_NONE; + +static enum spectre_v2_user_mitigation spectre_v2_user = SPECTRE_V2_USER_NONE; + #ifdef RETPOLINE static bool spectre_v2_bad_module; @@ -228,67 +279,224 @@ static inline const char *spectre_v2_module_string(void) static inline const char *spectre_v2_module_string(void) { return ""; } #endif -static void __init spec2_print_if_insecure(const char *reason) +static inline bool match_option(const char *arg, int arglen, const char *opt) { - if (boot_cpu_has_bug(X86_BUG_SPECTRE_V2)) - pr_info("%s selected on command line.\n", reason); + int len = strlen(opt); + + return len == arglen && !strncmp(arg, opt, len); } -static void __init spec2_print_if_secure(const char *reason) +/* The kernel command line selection for spectre v2 */ +enum spectre_v2_mitigation_cmd { + SPECTRE_V2_CMD_NONE, + SPECTRE_V2_CMD_AUTO, + SPECTRE_V2_CMD_FORCE, + SPECTRE_V2_CMD_RETPOLINE, + SPECTRE_V2_CMD_RETPOLINE_GENERIC, + SPECTRE_V2_CMD_RETPOLINE_AMD, +}; + +enum spectre_v2_user_cmd { + SPECTRE_V2_USER_CMD_NONE, + SPECTRE_V2_USER_CMD_AUTO, + SPECTRE_V2_USER_CMD_FORCE, + SPECTRE_V2_USER_CMD_PRCTL, + SPECTRE_V2_USER_CMD_PRCTL_IBPB, + SPECTRE_V2_USER_CMD_SECCOMP, + SPECTRE_V2_USER_CMD_SECCOMP_IBPB, +}; + +static const char * const spectre_v2_user_strings[] = { + [SPECTRE_V2_USER_NONE] = "User space: Vulnerable", + [SPECTRE_V2_USER_STRICT] = "User space: Mitigation: STIBP protection", + [SPECTRE_V2_USER_PRCTL] = "User space: Mitigation: STIBP via prctl", + [SPECTRE_V2_USER_SECCOMP] = "User space: Mitigation: STIBP via seccomp and prctl", +}; + +static const struct { + const char *option; + enum spectre_v2_user_cmd cmd; + bool secure; +} v2_user_options[] __initconst = { + { "auto", SPECTRE_V2_USER_CMD_AUTO, false }, + { "off", SPECTRE_V2_USER_CMD_NONE, false }, + { "on", SPECTRE_V2_USER_CMD_FORCE, true }, + { "prctl", SPECTRE_V2_USER_CMD_PRCTL, false }, + { "prctl,ibpb", SPECTRE_V2_USER_CMD_PRCTL_IBPB, false }, + { "seccomp", SPECTRE_V2_USER_CMD_SECCOMP, false }, + { "seccomp,ibpb", SPECTRE_V2_USER_CMD_SECCOMP_IBPB, false }, +}; + +static void __init spec_v2_user_print_cond(const char *reason, bool secure) { - if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V2)) - pr_info("%s selected on command line.\n", reason); + if (boot_cpu_has_bug(X86_BUG_SPECTRE_V2) != secure) + pr_info("spectre_v2_user=%s forced on command line.\n", reason); } -static inline bool retp_compiler(void) +static enum spectre_v2_user_cmd __init +spectre_v2_parse_user_cmdline(enum spectre_v2_mitigation_cmd v2_cmd) { - return __is_defined(RETPOLINE); + char arg[20]; + int ret, i; + + switch (v2_cmd) { + case SPECTRE_V2_CMD_NONE: + return SPECTRE_V2_USER_CMD_NONE; + case SPECTRE_V2_CMD_FORCE: + return SPECTRE_V2_USER_CMD_FORCE; + default: + break; + } + + ret = cmdline_find_option(boot_command_line, "spectre_v2_user", + arg, sizeof(arg)); + if (ret < 0) + return SPECTRE_V2_USER_CMD_AUTO; + + for (i = 0; i < ARRAY_SIZE(v2_user_options); i++) { + if (match_option(arg, ret, v2_user_options[i].option)) { + spec_v2_user_print_cond(v2_user_options[i].option, + v2_user_options[i].secure); + return v2_user_options[i].cmd; + } + } + + pr_err("Unknown user space protection option (%s). Switching to AUTO select\n", arg); + return SPECTRE_V2_USER_CMD_AUTO; } -static inline bool match_option(const char *arg, int arglen, const char *opt) +static void __init +spectre_v2_user_select_mitigation(enum spectre_v2_mitigation_cmd v2_cmd) { - int len = strlen(opt); + enum spectre_v2_user_mitigation mode = SPECTRE_V2_USER_NONE; + bool smt_possible = IS_ENABLED(CONFIG_SMP); + enum spectre_v2_user_cmd cmd; - return len == arglen && !strncmp(arg, opt, len); + if (!boot_cpu_has(X86_FEATURE_IBPB) && !boot_cpu_has(X86_FEATURE_STIBP)) + return; + + if (!IS_ENABLED(CONFIG_SMP)) + smt_possible = false; + + cmd = spectre_v2_parse_user_cmdline(v2_cmd); + switch (cmd) { + case SPECTRE_V2_USER_CMD_NONE: + goto set_mode; + case SPECTRE_V2_USER_CMD_FORCE: + mode = SPECTRE_V2_USER_STRICT; + break; + case SPECTRE_V2_USER_CMD_PRCTL: + case SPECTRE_V2_USER_CMD_PRCTL_IBPB: + mode = SPECTRE_V2_USER_PRCTL; + break; + case SPECTRE_V2_USER_CMD_AUTO: + case SPECTRE_V2_USER_CMD_SECCOMP: + case SPECTRE_V2_USER_CMD_SECCOMP_IBPB: + if (IS_ENABLED(CONFIG_SECCOMP)) + mode = SPECTRE_V2_USER_SECCOMP; + else + mode = SPECTRE_V2_USER_PRCTL; + break; + } + + /* Initialize Indirect Branch Prediction Barrier */ + if (boot_cpu_has(X86_FEATURE_IBPB)) { + setup_force_cpu_cap(X86_FEATURE_USE_IBPB); + + switch (cmd) { + case SPECTRE_V2_USER_CMD_FORCE: + case SPECTRE_V2_USER_CMD_PRCTL_IBPB: + case SPECTRE_V2_USER_CMD_SECCOMP_IBPB: + static_branch_enable(&switch_mm_always_ibpb); + break; + case SPECTRE_V2_USER_CMD_PRCTL: + case SPECTRE_V2_USER_CMD_AUTO: + case SPECTRE_V2_USER_CMD_SECCOMP: + static_branch_enable(&switch_mm_cond_ibpb); + break; + default: + break; + } + + pr_info("mitigation: Enabling %s Indirect Branch Prediction Barrier\n", + static_key_enabled(&switch_mm_always_ibpb) ? + "always-on" : "conditional"); + } + + /* If enhanced IBRS is enabled no STIPB required */ + if (spectre_v2_enabled == SPECTRE_V2_IBRS_ENHANCED) + return; + + /* + * If SMT is not possible or STIBP is not available clear the STIPB + * mode. + */ + if (!smt_possible || !boot_cpu_has(X86_FEATURE_STIBP)) + mode = SPECTRE_V2_USER_NONE; +set_mode: + spectre_v2_user = mode; + /* Only print the STIBP mode when SMT possible */ + if (smt_possible) + pr_info("%s\n", spectre_v2_user_strings[mode]); } +static const char * const spectre_v2_strings[] = { + [SPECTRE_V2_NONE] = "Vulnerable", + [SPECTRE_V2_RETPOLINE_MINIMAL] = "Vulnerable: Minimal generic ASM retpoline", + [SPECTRE_V2_RETPOLINE_MINIMAL_AMD] = "Vulnerable: Minimal AMD ASM retpoline", + [SPECTRE_V2_RETPOLINE_GENERIC] = "Mitigation: Full generic retpoline", + [SPECTRE_V2_RETPOLINE_AMD] = "Mitigation: Full AMD retpoline", + [SPECTRE_V2_IBRS_ENHANCED] = "Mitigation: Enhanced IBRS", +}; + static const struct { const char *option; enum spectre_v2_mitigation_cmd cmd; bool secure; -} mitigation_options[] = { - { "off", SPECTRE_V2_CMD_NONE, false }, - { "on", SPECTRE_V2_CMD_FORCE, true }, - { "retpoline", SPECTRE_V2_CMD_RETPOLINE, false }, - { "retpoline,amd", SPECTRE_V2_CMD_RETPOLINE_AMD, false }, - { "retpoline,generic", SPECTRE_V2_CMD_RETPOLINE_GENERIC, false }, - { "auto", SPECTRE_V2_CMD_AUTO, false }, +} mitigation_options[] __initconst = { + { "off", SPECTRE_V2_CMD_NONE, false }, + { "on", SPECTRE_V2_CMD_FORCE, true }, + { "retpoline", SPECTRE_V2_CMD_RETPOLINE, false }, + { "retpoline,amd", SPECTRE_V2_CMD_RETPOLINE_AMD, false }, + { "retpoline,generic", SPECTRE_V2_CMD_RETPOLINE_GENERIC, false }, + { "auto", SPECTRE_V2_CMD_AUTO, false }, }; +static void __init spec_v2_print_cond(const char *reason, bool secure) +{ + if (boot_cpu_has_bug(X86_BUG_SPECTRE_V2) != secure) + pr_info("%s selected on command line.\n", reason); +} + +static inline bool retp_compiler(void) +{ + return __is_defined(RETPOLINE); +} + static enum spectre_v2_mitigation_cmd __init spectre_v2_parse_cmdline(void) { + enum spectre_v2_mitigation_cmd cmd = SPECTRE_V2_CMD_AUTO; char arg[20]; int ret, i; - enum spectre_v2_mitigation_cmd cmd = SPECTRE_V2_CMD_AUTO; - if (cmdline_find_option_bool(boot_command_line, "nospectre_v2")) + if (cmdline_find_option_bool(boot_command_line, "nospectre_v2") || + cpu_mitigations_off()) return SPECTRE_V2_CMD_NONE; - else { - ret = cmdline_find_option(boot_command_line, "spectre_v2", arg, sizeof(arg)); - if (ret < 0) - return SPECTRE_V2_CMD_AUTO; - for (i = 0; i < ARRAY_SIZE(mitigation_options); i++) { - if (!match_option(arg, ret, mitigation_options[i].option)) - continue; - cmd = mitigation_options[i].cmd; - break; - } + ret = cmdline_find_option(boot_command_line, "spectre_v2", arg, sizeof(arg)); + if (ret < 0) + return SPECTRE_V2_CMD_AUTO; - if (i >= ARRAY_SIZE(mitigation_options)) { - pr_err("unknown option (%s). Switching to AUTO select\n", arg); - return SPECTRE_V2_CMD_AUTO; - } + for (i = 0; i < ARRAY_SIZE(mitigation_options); i++) { + if (!match_option(arg, ret, mitigation_options[i].option)) + continue; + cmd = mitigation_options[i].cmd; + break; + } + + if (i >= ARRAY_SIZE(mitigation_options)) { + pr_err("unknown option (%s). Switching to AUTO select\n", arg); + return SPECTRE_V2_CMD_AUTO; } if ((cmd == SPECTRE_V2_CMD_RETPOLINE || @@ -305,11 +513,8 @@ static enum spectre_v2_mitigation_cmd __init spectre_v2_parse_cmdline(void) return SPECTRE_V2_CMD_AUTO; } - if (mitigation_options[i].secure) - spec2_print_if_secure(mitigation_options[i].option); - else - spec2_print_if_insecure(mitigation_options[i].option); - + spec_v2_print_cond(mitigation_options[i].option, + mitigation_options[i].secure); return cmd; } @@ -332,6 +537,13 @@ static void __init spectre_v2_select_mitigation(void) case SPECTRE_V2_CMD_FORCE: case SPECTRE_V2_CMD_AUTO: + if (boot_cpu_has(X86_FEATURE_IBRS_ENHANCED)) { + mode = SPECTRE_V2_IBRS_ENHANCED; + /* Force it so VMEXIT will restore correctly */ + x86_spec_ctrl_base |= SPEC_CTRL_IBRS; + wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base); + goto specv2_set_mode; + } if (IS_ENABLED(CONFIG_RETPOLINE)) goto retpoline_auto; break; @@ -369,6 +581,7 @@ retpoline_auto: setup_force_cpu_cap(X86_FEATURE_RETPOLINE); } +specv2_set_mode: spectre_v2_enabled = mode; pr_info("%s\n", spectre_v2_strings[mode]); @@ -383,20 +596,114 @@ retpoline_auto: setup_force_cpu_cap(X86_FEATURE_RSB_CTXSW); pr_info("Spectre v2 / SpectreRSB mitigation: Filling RSB on context switch\n"); - /* Initialize Indirect Branch Prediction Barrier if supported */ - if (boot_cpu_has(X86_FEATURE_IBPB)) { - setup_force_cpu_cap(X86_FEATURE_USE_IBPB); - pr_info("Spectre v2 mitigation: Enabling Indirect Branch Prediction Barrier\n"); - } - /* * Retpoline means the kernel is safe because it has no indirect - * branches. But firmware isn't, so use IBRS to protect that. + * branches. Enhanced IBRS protects firmware too, so, enable restricted + * speculation around firmware calls only when Enhanced IBRS isn't + * supported. + * + * Use "mode" to check Enhanced IBRS instead of boot_cpu_has(), because + * the user might select retpoline on the kernel command line and if + * the CPU supports Enhanced IBRS, kernel might un-intentionally not + * enable IBRS around firmware calls. */ - if (boot_cpu_has(X86_FEATURE_IBRS)) { + if (boot_cpu_has(X86_FEATURE_IBRS) && mode != SPECTRE_V2_IBRS_ENHANCED) { setup_force_cpu_cap(X86_FEATURE_USE_IBRS_FW); pr_info("Enabling Restricted Speculation for firmware calls\n"); } + + /* Set up IBPB and STIBP depending on the general spectre V2 command */ + spectre_v2_user_select_mitigation(cmd); +} + +static void update_stibp_msr(void * __unused) +{ + wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base); +} + +/* Update x86_spec_ctrl_base in case SMT state changed. */ +static void update_stibp_strict(void) +{ + u64 mask = x86_spec_ctrl_base & ~SPEC_CTRL_STIBP; + + if (sched_smt_active()) + mask |= SPEC_CTRL_STIBP; + + if (mask == x86_spec_ctrl_base) + return; + + pr_info("Update user space SMT mitigation: STIBP %s\n", + mask & SPEC_CTRL_STIBP ? "always-on" : "off"); + x86_spec_ctrl_base = mask; + on_each_cpu(update_stibp_msr, NULL, 1); +} + +/* Update the static key controlling the evaluation of TIF_SPEC_IB */ +static void update_indir_branch_cond(void) +{ + if (sched_smt_active()) + static_branch_enable(&switch_to_cond_stibp); + else + static_branch_disable(&switch_to_cond_stibp); +} + +#undef pr_fmt +#define pr_fmt(fmt) fmt + +/* Update the static key controlling the MDS CPU buffer clear in idle */ +static void update_mds_branch_idle(void) +{ + /* + * Enable the idle clearing if SMT is active on CPUs which are + * affected only by MSBDS and not any other MDS variant. + * + * The other variants cannot be mitigated when SMT is enabled, so + * clearing the buffers on idle just to prevent the Store Buffer + * repartitioning leak would be a window dressing exercise. + */ + if (!boot_cpu_has_bug(X86_BUG_MSBDS_ONLY)) + return; + + if (sched_smt_active()) + static_branch_enable(&mds_idle_clear); + else + static_branch_disable(&mds_idle_clear); +} + +#define MDS_MSG_SMT "MDS CPU bug present and SMT on, data leak possible. See https://www.kernel.org/doc/html/latest/admin-guide/hw-vuln/mds.html for more details.\n" + +void arch_smt_update(void) +{ + /* Enhanced IBRS implies STIBP. No update required. */ + if (spectre_v2_enabled == SPECTRE_V2_IBRS_ENHANCED) + return; + + mutex_lock(&spec_ctrl_mutex); + + switch (spectre_v2_user) { + case SPECTRE_V2_USER_NONE: + break; + case SPECTRE_V2_USER_STRICT: + update_stibp_strict(); + break; + case SPECTRE_V2_USER_PRCTL: + case SPECTRE_V2_USER_SECCOMP: + update_indir_branch_cond(); + break; + } + + switch (mds_mitigation) { + case MDS_MITIGATION_FULL: + case MDS_MITIGATION_VMWERV: + if (sched_smt_active() && !boot_cpu_has(X86_BUG_MSBDS_ONLY)) + pr_warn_once(MDS_MSG_SMT); + update_mds_branch_idle(); + break; + case MDS_MITIGATION_OFF: + break; + } + + mutex_unlock(&spec_ctrl_mutex); } #undef pr_fmt @@ -413,7 +720,7 @@ enum ssb_mitigation_cmd { SPEC_STORE_BYPASS_CMD_SECCOMP, }; -static const char *ssb_strings[] = { +static const char * const ssb_strings[] = { [SPEC_STORE_BYPASS_NONE] = "Vulnerable", [SPEC_STORE_BYPASS_DISABLE] = "Mitigation: Speculative Store Bypass disabled", [SPEC_STORE_BYPASS_PRCTL] = "Mitigation: Speculative Store Bypass disabled via prctl", @@ -423,7 +730,7 @@ static const char *ssb_strings[] = { static const struct { const char *option; enum ssb_mitigation_cmd cmd; -} ssb_mitigation_options[] = { +} ssb_mitigation_options[] __initconst = { { "auto", SPEC_STORE_BYPASS_CMD_AUTO }, /* Platform decides */ { "on", SPEC_STORE_BYPASS_CMD_ON }, /* Disable Speculative Store Bypass */ { "off", SPEC_STORE_BYPASS_CMD_NONE }, /* Don't touch Speculative Store Bypass */ @@ -437,7 +744,8 @@ static enum ssb_mitigation_cmd __init ssb_parse_cmdline(void) char arg[20]; int ret, i; - if (cmdline_find_option_bool(boot_command_line, "nospec_store_bypass_disable")) { + if (cmdline_find_option_bool(boot_command_line, "nospec_store_bypass_disable") || + cpu_mitigations_off()) { return SPEC_STORE_BYPASS_CMD_NONE; } else { ret = cmdline_find_option(boot_command_line, "spec_store_bypass_disable", @@ -507,18 +815,16 @@ static enum ssb_mitigation __init __ssb_select_mitigation(void) if (mode == SPEC_STORE_BYPASS_DISABLE) { setup_force_cpu_cap(X86_FEATURE_SPEC_STORE_BYPASS_DISABLE); /* - * Intel uses the SPEC CTRL MSR Bit(2) for this, while AMD uses - * a completely different MSR and bit dependent on family. + * Intel uses the SPEC CTRL MSR Bit(2) for this, while AMD may + * use a completely different MSR and bit dependent on family. */ - switch (boot_cpu_data.x86_vendor) { - case X86_VENDOR_INTEL: + if (!static_cpu_has(X86_FEATURE_SPEC_CTRL_SSBD) && + !static_cpu_has(X86_FEATURE_AMD_SSBD)) { + x86_amd_ssb_disable(); + } else { x86_spec_ctrl_base |= SPEC_CTRL_SSBD; x86_spec_ctrl_mask |= SPEC_CTRL_SSBD; wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base); - break; - case X86_VENDOR_AMD: - x86_amd_ssb_disable(); - break; } } @@ -536,10 +842,25 @@ static void ssb_select_mitigation(void) #undef pr_fmt #define pr_fmt(fmt) "Speculation prctl: " fmt -static int ssb_prctl_set(struct task_struct *task, unsigned long ctrl) +static void task_update_spec_tif(struct task_struct *tsk) { - bool update; + /* Force the update of the real TIF bits */ + set_tsk_thread_flag(tsk, TIF_SPEC_FORCE_UPDATE); + + /* + * Immediately update the speculation control MSRs for the current + * task, but for a non-current task delay setting the CPU + * mitigation until it is scheduled next. + * + * This can only happen for SECCOMP mitigation. For PRCTL it's + * always the current task. + */ + if (tsk == current) + speculation_ctrl_update_current(); +} +static int ssb_prctl_set(struct task_struct *task, unsigned long ctrl) +{ if (ssb_mode != SPEC_STORE_BYPASS_PRCTL && ssb_mode != SPEC_STORE_BYPASS_SECCOMP) return -ENXIO; @@ -550,28 +871,56 @@ static int ssb_prctl_set(struct task_struct *task, unsigned long ctrl) if (task_spec_ssb_force_disable(task)) return -EPERM; task_clear_spec_ssb_disable(task); - update = test_and_clear_tsk_thread_flag(task, TIF_SSBD); + task_update_spec_tif(task); break; case PR_SPEC_DISABLE: task_set_spec_ssb_disable(task); - update = !test_and_set_tsk_thread_flag(task, TIF_SSBD); + task_update_spec_tif(task); break; case PR_SPEC_FORCE_DISABLE: task_set_spec_ssb_disable(task); task_set_spec_ssb_force_disable(task); - update = !test_and_set_tsk_thread_flag(task, TIF_SSBD); + task_update_spec_tif(task); break; default: return -ERANGE; } + return 0; +} - /* - * If being set on non-current task, delay setting the CPU - * mitigation until it is next scheduled. - */ - if (task == current && update) - speculative_store_bypass_update_current(); - +static int ib_prctl_set(struct task_struct *task, unsigned long ctrl) +{ + switch (ctrl) { + case PR_SPEC_ENABLE: + if (spectre_v2_user == SPECTRE_V2_USER_NONE) + return 0; + /* + * Indirect branch speculation is always disabled in strict + * mode. + */ + if (spectre_v2_user == SPECTRE_V2_USER_STRICT) + return -EPERM; + task_clear_spec_ib_disable(task); + task_update_spec_tif(task); + break; + case PR_SPEC_DISABLE: + case PR_SPEC_FORCE_DISABLE: + /* + * Indirect branch speculation is always allowed when + * mitigation is force disabled. + */ + if (spectre_v2_user == SPECTRE_V2_USER_NONE) + return -EPERM; + if (spectre_v2_user == SPECTRE_V2_USER_STRICT) + return 0; + task_set_spec_ib_disable(task); + if (ctrl == PR_SPEC_FORCE_DISABLE) + task_set_spec_ib_force_disable(task); + task_update_spec_tif(task); + break; + default: + return -ERANGE; + } return 0; } @@ -581,6 +930,8 @@ int arch_prctl_spec_ctrl_set(struct task_struct *task, unsigned long which, switch (which) { case PR_SPEC_STORE_BYPASS: return ssb_prctl_set(task, ctrl); + case PR_SPEC_INDIRECT_BRANCH: + return ib_prctl_set(task, ctrl); default: return -ENODEV; } @@ -591,6 +942,8 @@ void arch_seccomp_spec_mitigate(struct task_struct *task) { if (ssb_mode == SPEC_STORE_BYPASS_SECCOMP) ssb_prctl_set(task, PR_SPEC_FORCE_DISABLE); + if (spectre_v2_user == SPECTRE_V2_USER_SECCOMP) + ib_prctl_set(task, PR_SPEC_FORCE_DISABLE); } #endif @@ -613,11 +966,35 @@ static int ssb_prctl_get(struct task_struct *task) } } +static int ib_prctl_get(struct task_struct *task) +{ + if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V2)) + return PR_SPEC_NOT_AFFECTED; + + switch (spectre_v2_user) { + case SPECTRE_V2_USER_NONE: + return PR_SPEC_ENABLE; + case SPECTRE_V2_USER_PRCTL: + case SPECTRE_V2_USER_SECCOMP: + if (task_spec_ib_force_disable(task)) + return PR_SPEC_PRCTL | PR_SPEC_FORCE_DISABLE; + if (task_spec_ib_disable(task)) + return PR_SPEC_PRCTL | PR_SPEC_DISABLE; + return PR_SPEC_PRCTL | PR_SPEC_ENABLE; + case SPECTRE_V2_USER_STRICT: + return PR_SPEC_DISABLE; + default: + return PR_SPEC_NOT_AFFECTED; + } +} + int arch_prctl_spec_ctrl_get(struct task_struct *task, unsigned long which) { switch (which) { case PR_SPEC_STORE_BYPASS: return ssb_prctl_get(task); + case PR_SPEC_INDIRECT_BRANCH: + return ib_prctl_get(task); default: return -ENODEV; } @@ -694,16 +1071,66 @@ static void __init l1tf_select_mitigation(void) pr_info("You may make it effective by booting the kernel with mem=%llu parameter.\n", half_pa); pr_info("However, doing so will make a part of your RAM unusable.\n"); - pr_info("Reading https://www.kernel.org/doc/html/latest/admin-guide/l1tf.html might help you decide.\n"); + pr_info("Reading https://www.kernel.org/doc/html/latest/admin-guide/hw-vuln/l1tf.html might help you decide.\n"); return; } setup_force_cpu_cap(X86_FEATURE_L1TF_PTEINV); } #undef pr_fmt +#define pr_fmt(fmt) fmt #ifdef CONFIG_SYSFS +static ssize_t mds_show_state(char *buf) +{ +#ifdef CONFIG_HYPERVISOR_GUEST + if (x86_hyper) { + return sprintf(buf, "%s; SMT Host state unknown\n", + mds_strings[mds_mitigation]); + } +#endif + + if (boot_cpu_has(X86_BUG_MSBDS_ONLY)) { + return sprintf(buf, "%s; SMT %s\n", mds_strings[mds_mitigation], + (mds_mitigation == MDS_MITIGATION_OFF ? "vulnerable" : + sched_smt_active() ? "mitigated" : "disabled")); + } + + return sprintf(buf, "%s; SMT %s\n", mds_strings[mds_mitigation], + sched_smt_active() ? "vulnerable" : "disabled"); +} + +static char *stibp_state(void) +{ + if (spectre_v2_enabled == SPECTRE_V2_IBRS_ENHANCED) + return ""; + + switch (spectre_v2_user) { + case SPECTRE_V2_USER_NONE: + return ", STIBP: disabled"; + case SPECTRE_V2_USER_STRICT: + return ", STIBP: forced"; + case SPECTRE_V2_USER_PRCTL: + case SPECTRE_V2_USER_SECCOMP: + if (static_key_enabled(&switch_to_cond_stibp)) + return ", STIBP: conditional"; + } + return ""; +} + +static char *ibpb_state(void) +{ + if (boot_cpu_has(X86_FEATURE_IBPB)) { + if (static_key_enabled(&switch_mm_always_ibpb)) + return ", IBPB: always-on"; + if (static_key_enabled(&switch_mm_cond_ibpb)) + return ", IBPB: conditional"; + return ", IBPB: disabled"; + } + return ""; +} + static ssize_t cpu_show_common(struct device *dev, struct device_attribute *attr, char *buf, unsigned int bug) { @@ -721,9 +1148,11 @@ static ssize_t cpu_show_common(struct device *dev, struct device_attribute *attr return sprintf(buf, "Mitigation: __user pointer sanitization\n"); case X86_BUG_SPECTRE_V2: - return sprintf(buf, "%s%s%s%s\n", spectre_v2_strings[spectre_v2_enabled], - boot_cpu_has(X86_FEATURE_USE_IBPB) ? ", IBPB" : "", + return sprintf(buf, "%s%s%s%s%s%s\n", spectre_v2_strings[spectre_v2_enabled], + ibpb_state(), boot_cpu_has(X86_FEATURE_USE_IBRS_FW) ? ", IBRS_FW" : "", + stibp_state(), + boot_cpu_has(X86_FEATURE_RSB_CTXSW) ? ", RSB filling" : "", spectre_v2_module_string()); case X86_BUG_SPEC_STORE_BYPASS: @@ -731,9 +1160,12 @@ static ssize_t cpu_show_common(struct device *dev, struct device_attribute *attr case X86_BUG_L1TF: if (boot_cpu_has(X86_FEATURE_L1TF_PTEINV)) - return sprintf(buf, "Mitigation: Page Table Inversion\n"); + return sprintf(buf, "Mitigation: PTE Inversion\n"); break; + case X86_BUG_MDS: + return mds_show_state(buf); + default: break; } @@ -765,4 +1197,9 @@ ssize_t cpu_show_l1tf(struct device *dev, struct device_attribute *attr, char *b { return cpu_show_common(dev, attr, buf, X86_BUG_L1TF); } + +ssize_t cpu_show_mds(struct device *dev, struct device_attribute *attr, char *buf) +{ + return cpu_show_common(dev, attr, buf, X86_BUG_MDS); +} #endif diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index e8b46f575306..4bce77bc7e61 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -709,6 +709,12 @@ static void init_speculation_control(struct cpuinfo_x86 *c) set_cpu_cap(c, X86_FEATURE_STIBP); set_cpu_cap(c, X86_FEATURE_MSR_SPEC_CTRL); } + + if (cpu_has(c, X86_FEATURE_AMD_SSBD)) { + set_cpu_cap(c, X86_FEATURE_SSBD); + set_cpu_cap(c, X86_FEATURE_MSR_SPEC_CTRL); + clear_cpu_cap(c, X86_FEATURE_VIRT_SSBD); + } } void get_cpu_cap(struct cpuinfo_x86 *c) @@ -841,81 +847,95 @@ static void identify_cpu_without_cpuid(struct cpuinfo_x86 *c) #endif } -static const __initconst struct x86_cpu_id cpu_no_speculation[] = { - { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_CEDARVIEW, X86_FEATURE_ANY }, - { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_CLOVERVIEW, X86_FEATURE_ANY }, - { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_LINCROFT, X86_FEATURE_ANY }, - { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_PENWELL, X86_FEATURE_ANY }, - { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_PINEVIEW, X86_FEATURE_ANY }, - { X86_VENDOR_CENTAUR, 5 }, - { X86_VENDOR_INTEL, 5 }, - { X86_VENDOR_NSC, 5 }, - { X86_VENDOR_ANY, 4 }, +#define NO_SPECULATION BIT(0) +#define NO_MELTDOWN BIT(1) +#define NO_SSB BIT(2) +#define NO_L1TF BIT(3) +#define NO_MDS BIT(4) +#define MSBDS_ONLY BIT(5) + +#define VULNWL(_vendor, _family, _model, _whitelist) \ + { X86_VENDOR_##_vendor, _family, _model, X86_FEATURE_ANY, _whitelist } + +#define VULNWL_INTEL(model, whitelist) \ + VULNWL(INTEL, 6, INTEL_FAM6_##model, whitelist) + +#define VULNWL_AMD(family, whitelist) \ + VULNWL(AMD, family, X86_MODEL_ANY, whitelist) + +static const __initconst struct x86_cpu_id cpu_vuln_whitelist[] = { + VULNWL(ANY, 4, X86_MODEL_ANY, NO_SPECULATION), + VULNWL(CENTAUR, 5, X86_MODEL_ANY, NO_SPECULATION), + VULNWL(INTEL, 5, X86_MODEL_ANY, NO_SPECULATION), + VULNWL(NSC, 5, X86_MODEL_ANY, NO_SPECULATION), + + /* Intel Family 6 */ + VULNWL_INTEL(ATOM_SALTWELL, NO_SPECULATION), + VULNWL_INTEL(ATOM_SALTWELL_TABLET, NO_SPECULATION), + VULNWL_INTEL(ATOM_SALTWELL_MID, NO_SPECULATION), + VULNWL_INTEL(ATOM_BONNELL, NO_SPECULATION), + VULNWL_INTEL(ATOM_BONNELL_MID, NO_SPECULATION), + + VULNWL_INTEL(ATOM_SILVERMONT, NO_SSB | NO_L1TF | MSBDS_ONLY), + VULNWL_INTEL(ATOM_SILVERMONT_X, NO_SSB | NO_L1TF | MSBDS_ONLY), + VULNWL_INTEL(ATOM_SILVERMONT_MID, NO_SSB | NO_L1TF | MSBDS_ONLY), + VULNWL_INTEL(ATOM_AIRMONT, NO_SSB | NO_L1TF | MSBDS_ONLY), + VULNWL_INTEL(XEON_PHI_KNL, NO_SSB | NO_L1TF | MSBDS_ONLY), + VULNWL_INTEL(XEON_PHI_KNM, NO_SSB | NO_L1TF | MSBDS_ONLY), + + VULNWL_INTEL(CORE_YONAH, NO_SSB), + + VULNWL_INTEL(ATOM_AIRMONT_MID, NO_L1TF | MSBDS_ONLY), + + VULNWL_INTEL(ATOM_GOLDMONT, NO_MDS | NO_L1TF), + VULNWL_INTEL(ATOM_GOLDMONT_X, NO_MDS | NO_L1TF), + VULNWL_INTEL(ATOM_GOLDMONT_PLUS, NO_MDS | NO_L1TF), + + /* AMD Family 0xf - 0x12 */ + VULNWL_AMD(0x0f, NO_MELTDOWN | NO_SSB | NO_L1TF | NO_MDS), + VULNWL_AMD(0x10, NO_MELTDOWN | NO_SSB | NO_L1TF | NO_MDS), + VULNWL_AMD(0x11, NO_MELTDOWN | NO_SSB | NO_L1TF | NO_MDS), + VULNWL_AMD(0x12, NO_MELTDOWN | NO_SSB | NO_L1TF | NO_MDS), + + /* FAMILY_ANY must be last, otherwise 0x0f - 0x12 matches won't work */ + VULNWL_AMD(X86_FAMILY_ANY, NO_MELTDOWN | NO_L1TF | NO_MDS), {} }; -static const __initconst struct x86_cpu_id cpu_no_meltdown[] = { - { X86_VENDOR_AMD }, - {} -}; - -static const __initconst struct x86_cpu_id cpu_no_spec_store_bypass[] = { - { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_PINEVIEW }, - { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_LINCROFT }, - { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_PENWELL }, - { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_CLOVERVIEW }, - { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_CEDARVIEW }, - { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_SILVERMONT1 }, - { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_AIRMONT }, - { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_SILVERMONT2 }, - { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_MERRIFIELD }, - { X86_VENDOR_INTEL, 6, INTEL_FAM6_CORE_YONAH }, - { X86_VENDOR_INTEL, 6, INTEL_FAM6_XEON_PHI_KNL }, - { X86_VENDOR_INTEL, 6, INTEL_FAM6_XEON_PHI_KNM }, - { X86_VENDOR_CENTAUR, 5, }, - { X86_VENDOR_INTEL, 5, }, - { X86_VENDOR_NSC, 5, }, - { X86_VENDOR_AMD, 0x12, }, - { X86_VENDOR_AMD, 0x11, }, - { X86_VENDOR_AMD, 0x10, }, - { X86_VENDOR_AMD, 0xf, }, - { X86_VENDOR_ANY, 4, }, - {} -}; +static bool __init cpu_matches(unsigned long which) +{ + const struct x86_cpu_id *m = x86_match_cpu(cpu_vuln_whitelist); -static const __initconst struct x86_cpu_id cpu_no_l1tf[] = { - /* in addition to cpu_no_speculation */ - { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_SILVERMONT1 }, - { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_SILVERMONT2 }, - { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_AIRMONT }, - { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_MERRIFIELD }, - { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_MOOREFIELD }, - { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_GOLDMONT }, - { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_DENVERTON }, - { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_GEMINI_LAKE }, - { X86_VENDOR_INTEL, 6, INTEL_FAM6_XEON_PHI_KNL }, - { X86_VENDOR_INTEL, 6, INTEL_FAM6_XEON_PHI_KNM }, - {} -}; + return m && !!(m->driver_data & which); +} static void __init cpu_set_bug_bits(struct cpuinfo_x86 *c) { u64 ia32_cap = 0; + if (cpu_matches(NO_SPECULATION)) + return; + + setup_force_cpu_bug(X86_BUG_SPECTRE_V1); + setup_force_cpu_bug(X86_BUG_SPECTRE_V2); + if (cpu_has(c, X86_FEATURE_ARCH_CAPABILITIES)) rdmsrl(MSR_IA32_ARCH_CAPABILITIES, ia32_cap); - if (!x86_match_cpu(cpu_no_spec_store_bypass) && - !(ia32_cap & ARCH_CAP_SSB_NO)) + if (!cpu_matches(NO_SSB) && !(ia32_cap & ARCH_CAP_SSB_NO) && + !cpu_has(c, X86_FEATURE_AMD_SSB_NO)) setup_force_cpu_bug(X86_BUG_SPEC_STORE_BYPASS); - if (x86_match_cpu(cpu_no_speculation)) - return; + if (ia32_cap & ARCH_CAP_IBRS_ALL) + setup_force_cpu_cap(X86_FEATURE_IBRS_ENHANCED); - setup_force_cpu_bug(X86_BUG_SPECTRE_V1); - setup_force_cpu_bug(X86_BUG_SPECTRE_V2); + if (!cpu_matches(NO_MDS) && !(ia32_cap & ARCH_CAP_MDS_NO)) { + setup_force_cpu_bug(X86_BUG_MDS); + if (cpu_matches(MSBDS_ONLY)) + setup_force_cpu_bug(X86_BUG_MSBDS_ONLY); + } - if (x86_match_cpu(cpu_no_meltdown)) + if (cpu_matches(NO_MELTDOWN)) return; /* Rogue Data Cache Load? No! */ @@ -924,7 +944,7 @@ static void __init cpu_set_bug_bits(struct cpuinfo_x86 *c) setup_force_cpu_bug(X86_BUG_CPU_MELTDOWN); - if (x86_match_cpu(cpu_no_l1tf)) + if (cpu_matches(NO_L1TF)) return; setup_force_cpu_bug(X86_BUG_L1TF); diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c index b18fe3d245fe..b0e0c7a12e61 100644 --- a/arch/x86/kernel/cpu/intel.c +++ b/arch/x86/kernel/cpu/intel.c @@ -14,6 +14,7 @@ #include <asm/bugs.h> #include <asm/cpu.h> #include <asm/intel-family.h> +#include <asm/microcode_intel.h> #ifdef CONFIG_X86_64 #include <linux/topology.h> @@ -102,14 +103,8 @@ static void early_init_intel(struct cpuinfo_x86 *c) (c->x86 == 0x6 && c->x86_model >= 0x0e)) set_cpu_cap(c, X86_FEATURE_CONSTANT_TSC); - if (c->x86 >= 6 && !cpu_has(c, X86_FEATURE_IA64)) { - unsigned lower_word; - - wrmsr(MSR_IA32_UCODE_REV, 0, 0); - /* Required by the SDM */ - sync_core(); - rdmsr(MSR_IA32_UCODE_REV, lower_word, c->microcode); - } + if (c->x86 >= 6 && !cpu_has(c, X86_FEATURE_IA64)) + c->microcode = intel_get_microcode_revision(); /* Now if any of them are set, check the blacklist and clear the lot */ if ((cpu_has(c, X86_FEATURE_SPEC_CTRL) || diff --git a/arch/x86/kernel/cpu/mcheck/mce-severity.c b/arch/x86/kernel/cpu/mcheck/mce-severity.c index 9c682c222071..1ce85ba50005 100644 --- a/arch/x86/kernel/cpu/mcheck/mce-severity.c +++ b/arch/x86/kernel/cpu/mcheck/mce-severity.c @@ -132,6 +132,11 @@ static struct severity { SER, MASK(MCI_STATUS_OVER|MCI_UC_SAR|MCI_ADDR|MCACOD, MCI_UC_SAR|MCI_ADDR|MCACOD_INSTR), USER ), + MCESEV( + PANIC, "Instruction fetch error in kernel", + SER, MASK(MCI_STATUS_OVER|MCI_UC_SAR|MCI_ADDR|MCACOD, MCI_UC_SAR|MCI_ADDR|MCACOD_INSTR), + KERNEL + ), #endif MCESEV( PANIC, "Action required: unknown MCACOD", diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c index 77f7580e22c6..4b9cfdcc3aaa 100644 --- a/arch/x86/kernel/cpu/mcheck/mce.c +++ b/arch/x86/kernel/cpu/mcheck/mce.c @@ -138,6 +138,8 @@ void mce_setup(struct mce *m) m->socketid = cpu_data(m->extcpu).phys_proc_id; m->apicid = cpu_data(m->extcpu).initial_apicid; rdmsrl(MSR_IA32_MCG_CAP, m->mcgcap); + + m->microcode = boot_cpu_data.microcode; } DEFINE_PER_CPU(struct mce, injectm); @@ -258,7 +260,7 @@ static void print_mce(struct mce *m) */ pr_emerg(HW_ERR "PROCESSOR %u:%x TIME %llu SOCKET %u APIC %x microcode %x\n", m->cpuvendor, m->cpuid, m->time, m->socketid, m->apicid, - cpu_data(m->extcpu).microcode); + m->microcode); /* * Print out human-readable details about the MCE error, diff --git a/arch/x86/kernel/cpu/microcode/amd.c b/arch/x86/kernel/cpu/microcode/amd.c index 6da6f9cd6d2d..ca5b45799264 100644 --- a/arch/x86/kernel/cpu/microcode/amd.c +++ b/arch/x86/kernel/cpu/microcode/amd.c @@ -695,22 +695,26 @@ int apply_microcode_amd(int cpu) return -1; /* need to apply patch? */ - if (rev >= mc_amd->hdr.patch_id) { - c->microcode = rev; - uci->cpu_sig.rev = rev; - return 0; - } + if (rev >= mc_amd->hdr.patch_id) + goto out; if (__apply_microcode_amd(mc_amd)) { pr_err("CPU%d: update failed for patch_level=0x%08x\n", cpu, mc_amd->hdr.patch_id); return -1; } - pr_info("CPU%d: new patch_level=0x%08x\n", cpu, - mc_amd->hdr.patch_id); - uci->cpu_sig.rev = mc_amd->hdr.patch_id; - c->microcode = mc_amd->hdr.patch_id; + rev = mc_amd->hdr.patch_id; + + pr_info("CPU%d: new patch_level=0x%08x\n", cpu, rev); + +out: + uci->cpu_sig.rev = rev; + c->microcode = rev; + + /* Update boot_cpu_data's revision too, if we're on the BSP: */ + if (c->cpu_index == boot_cpu_data.cpu_index) + boot_cpu_data.microcode = rev; return 0; } diff --git a/arch/x86/kernel/cpu/microcode/intel.c b/arch/x86/kernel/cpu/microcode/intel.c index 2f38a99cdb98..afaf648386e9 100644 --- a/arch/x86/kernel/cpu/microcode/intel.c +++ b/arch/x86/kernel/cpu/microcode/intel.c @@ -376,15 +376,8 @@ static int collect_cpu_info_early(struct ucode_cpu_info *uci) native_rdmsr(MSR_IA32_PLATFORM_ID, val[0], val[1]); csig.pf = 1 << ((val[1] >> 18) & 7); } - native_wrmsr(MSR_IA32_UCODE_REV, 0, 0); - /* As documented in the SDM: Do a CPUID 1 here */ - sync_core(); - - /* get the current revision from MSR 0x8B */ - native_rdmsr(MSR_IA32_UCODE_REV, val[0], val[1]); - - csig.rev = val[1]; + csig.rev = intel_get_microcode_revision(); uci->cpu_sig = csig; uci->valid = 1; @@ -654,31 +647,37 @@ static inline void print_ucode(struct ucode_cpu_info *uci) static int apply_microcode_early(struct ucode_cpu_info *uci, bool early) { struct microcode_intel *mc_intel; - unsigned int val[2]; + u32 rev; mc_intel = uci->mc; if (mc_intel == NULL) return 0; + /* + * Save us the MSR write below - which is a particular expensive + * operation - when the other hyperthread has updated the microcode + * already. + */ + rev = intel_get_microcode_revision(); + if (rev >= mc_intel->hdr.rev) { + uci->cpu_sig.rev = rev; + return 0; + } + /* write microcode via MSR 0x79 */ native_wrmsr(MSR_IA32_UCODE_WRITE, (unsigned long) mc_intel->bits, (unsigned long) mc_intel->bits >> 16 >> 16); - native_wrmsr(MSR_IA32_UCODE_REV, 0, 0); - - /* As documented in the SDM: Do a CPUID 1 here */ - sync_core(); - /* get the current revision from MSR 0x8B */ - native_rdmsr(MSR_IA32_UCODE_REV, val[0], val[1]); - if (val[1] != mc_intel->hdr.rev) + rev = intel_get_microcode_revision(); + if (rev != mc_intel->hdr.rev) return -1; #ifdef CONFIG_X86_64 /* Flush global tlb. This is precaution. */ flush_tlb_early(); #endif - uci->cpu_sig.rev = val[1]; + uci->cpu_sig.rev = rev; if (early) print_ucode(uci); @@ -852,7 +851,7 @@ static int apply_microcode_intel(int cpu) { struct microcode_intel *mc_intel; struct ucode_cpu_info *uci; - unsigned int val[2]; + u32 rev; int cpu_num = raw_smp_processor_id(); struct cpuinfo_x86 *c = &cpu_data(cpu_num); @@ -873,31 +872,40 @@ static int apply_microcode_intel(int cpu) if (get_matching_mc(mc_intel, cpu) == 0) return 0; + /* + * Save us the MSR write below - which is a particular expensive + * operation - when the other hyperthread has updated the microcode + * already. + */ + rev = intel_get_microcode_revision(); + if (rev >= mc_intel->hdr.rev) + goto out; + /* write microcode via MSR 0x79 */ wrmsr(MSR_IA32_UCODE_WRITE, (unsigned long) mc_intel->bits, (unsigned long) mc_intel->bits >> 16 >> 16); - wrmsr(MSR_IA32_UCODE_REV, 0, 0); - - /* As documented in the SDM: Do a CPUID 1 here */ - sync_core(); - /* get the current revision from MSR 0x8B */ - rdmsr(MSR_IA32_UCODE_REV, val[0], val[1]); + rev = intel_get_microcode_revision(); - if (val[1] != mc_intel->hdr.rev) { + if (rev != mc_intel->hdr.rev) { pr_err("CPU%d update to revision 0x%x failed\n", cpu_num, mc_intel->hdr.rev); return -1; } pr_info("CPU%d updated to revision 0x%x, date = %04x-%02x-%02x\n", - cpu_num, val[1], + cpu_num, rev, mc_intel->hdr.date & 0xffff, mc_intel->hdr.date >> 24, (mc_intel->hdr.date >> 16) & 0xff); - uci->cpu_sig.rev = val[1]; - c->microcode = val[1]; +out: + uci->cpu_sig.rev = rev; + c->microcode = rev; + + /* Update boot_cpu_data's revision too, if we're on the BSP: */ + if (c->cpu_index == boot_cpu_data.cpu_index) + boot_cpu_data.microcode = rev; return 0; } diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index 7b79c80ce029..3572434a73cb 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -2513,7 +2513,7 @@ static int intel_pmu_hw_config(struct perf_event *event) return ret; if (event->attr.precise_ip) { - if (!event->attr.freq) { + if (!(event->attr.freq || (event->attr.wakeup_events && !event->attr.watermark))) { event->hw.flags |= PERF_X86_EVENT_AUTO_RELOAD; if (!(event->attr.sample_type & ~intel_pmu_free_running_flags(event))) diff --git a/arch/x86/kernel/irq_64.c b/arch/x86/kernel/irq_64.c index 206d0b90a3ab..e39d7197f9fb 100644 --- a/arch/x86/kernel/irq_64.c +++ b/arch/x86/kernel/irq_64.c @@ -25,9 +25,18 @@ int sysctl_panic_on_stackoverflow; /* * Probabilistic stack overflow check: * - * Only check the stack in process context, because everything else - * runs on the big interrupt stacks. Checking reliably is too expensive, - * so we just check from interrupts. + * Regular device interrupts can enter on the following stacks: + * + * - User stack + * + * - Kernel task stack + * + * - Interrupt stack if a device driver reenables interrupts + * which should only happen in really old drivers. + * + * - Debug IST stack + * + * All other contexts are invalid. */ static inline void stack_overflow_check(struct pt_regs *regs) { @@ -53,8 +62,8 @@ static inline void stack_overflow_check(struct pt_regs *regs) return; oist = this_cpu_ptr(&orig_ist); - estack_top = (u64)oist->ist[0] - EXCEPTION_STKSZ + STACK_TOP_MARGIN; - estack_bottom = (u64)oist->ist[N_EXCEPTION_STACKS - 1]; + estack_bottom = (u64)oist->ist[DEBUG_STACK]; + estack_top = estack_bottom - DEBUG_STKSZ + STACK_TOP_MARGIN; if (regs->sp >= estack_top && regs->sp <= estack_bottom) return; diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c index 697f90db0e37..a4df15f3878e 100644 --- a/arch/x86/kernel/nmi.c +++ b/arch/x86/kernel/nmi.c @@ -29,6 +29,7 @@ #include <asm/mach_traps.h> #include <asm/nmi.h> #include <asm/x86_init.h> +#include <asm/nospec-branch.h> #define CREATE_TRACE_POINTS #include <trace/events/nmi.h> @@ -522,6 +523,9 @@ nmi_restart: write_cr2(this_cpu_read(nmi_cr2)); if (this_cpu_dec_return(nmi_state)) goto nmi_restart; + + if (user_mode(regs)) + mds_user_clear_cpu_buffers(); } NOKPROBE_SYMBOL(do_nmi); diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index 911ca11eb489..f69bae6b29e1 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c @@ -33,6 +33,8 @@ #include <asm/vm86.h> #include <asm/spec-ctrl.h> +#include "process.h" + /* * per-CPU TSS segments. Threads are completely 'soft' on Linux, * no more per-task TSS's. The TSS size is kept cacheline-aligned @@ -165,11 +167,12 @@ int set_tsc_mode(unsigned int val) return 0; } -static inline void switch_to_bitmap(struct tss_struct *tss, - struct thread_struct *prev, +static inline void switch_to_bitmap(struct thread_struct *prev, struct thread_struct *next, unsigned long tifp, unsigned long tifn) { + struct tss_struct *tss = this_cpu_ptr(&cpu_tss); + if (tifn & _TIF_IO_BITMAP) { /* * Copy the relevant range of the IO bitmap. @@ -303,32 +306,85 @@ static __always_inline void amd_set_ssb_virt_state(unsigned long tifn) wrmsrl(MSR_AMD64_VIRT_SPEC_CTRL, ssbd_tif_to_spec_ctrl(tifn)); } -static __always_inline void intel_set_ssb_state(unsigned long tifn) +/* + * Update the MSRs managing speculation control, during context switch. + * + * tifp: Previous task's thread flags + * tifn: Next task's thread flags + */ +static __always_inline void __speculation_ctrl_update(unsigned long tifp, + unsigned long tifn) { - u64 msr = x86_spec_ctrl_base | ssbd_tif_to_spec_ctrl(tifn); + unsigned long tif_diff = tifp ^ tifn; + u64 msr = x86_spec_ctrl_base; + bool updmsr = false; + + /* + * If TIF_SSBD is different, select the proper mitigation + * method. Note that if SSBD mitigation is disabled or permanentely + * enabled this branch can't be taken because nothing can set + * TIF_SSBD. + */ + if (tif_diff & _TIF_SSBD) { + if (static_cpu_has(X86_FEATURE_VIRT_SSBD)) { + amd_set_ssb_virt_state(tifn); + } else if (static_cpu_has(X86_FEATURE_LS_CFG_SSBD)) { + amd_set_core_ssb_state(tifn); + } else if (static_cpu_has(X86_FEATURE_SPEC_CTRL_SSBD) || + static_cpu_has(X86_FEATURE_AMD_SSBD)) { + msr |= ssbd_tif_to_spec_ctrl(tifn); + updmsr = true; + } + } + + /* + * Only evaluate TIF_SPEC_IB if conditional STIBP is enabled, + * otherwise avoid the MSR write. + */ + if (IS_ENABLED(CONFIG_SMP) && + static_branch_unlikely(&switch_to_cond_stibp)) { + updmsr |= !!(tif_diff & _TIF_SPEC_IB); + msr |= stibp_tif_to_spec_ctrl(tifn); + } - wrmsrl(MSR_IA32_SPEC_CTRL, msr); + if (updmsr) + wrmsrl(MSR_IA32_SPEC_CTRL, msr); } -static __always_inline void __speculative_store_bypass_update(unsigned long tifn) +static unsigned long speculation_ctrl_update_tif(struct task_struct *tsk) { - if (static_cpu_has(X86_FEATURE_VIRT_SSBD)) - amd_set_ssb_virt_state(tifn); - else if (static_cpu_has(X86_FEATURE_LS_CFG_SSBD)) - amd_set_core_ssb_state(tifn); - else - intel_set_ssb_state(tifn); + if (test_and_clear_tsk_thread_flag(tsk, TIF_SPEC_FORCE_UPDATE)) { + if (task_spec_ssb_disable(tsk)) + set_tsk_thread_flag(tsk, TIF_SSBD); + else + clear_tsk_thread_flag(tsk, TIF_SSBD); + + if (task_spec_ib_disable(tsk)) + set_tsk_thread_flag(tsk, TIF_SPEC_IB); + else + clear_tsk_thread_flag(tsk, TIF_SPEC_IB); + } + /* Return the updated threadinfo flags*/ + return task_thread_info(tsk)->flags; } -void speculative_store_bypass_update(unsigned long tif) +void speculation_ctrl_update(unsigned long tif) { + /* Forced update. Make sure all relevant TIF flags are different */ preempt_disable(); - __speculative_store_bypass_update(tif); + __speculation_ctrl_update(~tif, tif); preempt_enable(); } -void __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p, - struct tss_struct *tss) +/* Called from seccomp/prctl update */ +void speculation_ctrl_update_current(void) +{ + preempt_disable(); + speculation_ctrl_update(speculation_ctrl_update_tif(current)); + preempt_enable(); +} + +void __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p) { struct thread_struct *prev, *next; unsigned long tifp, tifn; @@ -338,7 +394,7 @@ void __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p, tifn = READ_ONCE(task_thread_info(next_p)->flags); tifp = READ_ONCE(task_thread_info(prev_p)->flags); - switch_to_bitmap(tss, prev, next, tifp, tifn); + switch_to_bitmap(prev, next, tifp, tifn); propagate_user_return_notify(prev_p, next_p); @@ -356,8 +412,15 @@ void __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p, if ((tifp ^ tifn) & _TIF_NOTSC) cr4_toggle_bits(X86_CR4_TSD); - if ((tifp ^ tifn) & _TIF_SSBD) - __speculative_store_bypass_update(tifn); + if (likely(!((tifp | tifn) & _TIF_SPEC_FORCE_UPDATE))) { + __speculation_ctrl_update(tifp, tifn); + } else { + speculation_ctrl_update_tif(prev_p); + tifn = speculation_ctrl_update_tif(next_p); + + /* Enforce MSR update to ensure consistent state */ + __speculation_ctrl_update(~tifn, tifn); + } } /* diff --git a/arch/x86/kernel/process.h b/arch/x86/kernel/process.h new file mode 100644 index 000000000000..898e97cf6629 --- /dev/null +++ b/arch/x86/kernel/process.h @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Code shared between 32 and 64 bit + +#include <asm/spec-ctrl.h> + +void __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p); + +/* + * This needs to be inline to optimize for the common case where no extra + * work needs to be done. + */ +static inline void switch_to_extra(struct task_struct *prev, + struct task_struct *next) +{ + unsigned long next_tif = task_thread_info(next)->flags; + unsigned long prev_tif = task_thread_info(prev)->flags; + + if (IS_ENABLED(CONFIG_SMP)) { + /* + * Avoid __switch_to_xtra() invocation when conditional + * STIPB is disabled and the only different bit is + * TIF_SPEC_IB. For CONFIG_SMP=n TIF_SPEC_IB is not + * in the TIF_WORK_CTXSW masks. + */ + if (!static_branch_likely(&switch_to_cond_stibp)) { + prev_tif &= ~_TIF_SPEC_IB; + next_tif &= ~_TIF_SPEC_IB; + } + } + + /* + * __switch_to_xtra() handles debug registers, i/o bitmaps, + * speculation mitigations etc. + */ + if (unlikely(next_tif & _TIF_WORK_CTXSW_NEXT || + prev_tif & _TIF_WORK_CTXSW_PREV)) + __switch_to_xtra(prev, next); +} diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c index 9f950917528b..85b112efac30 100644 --- a/arch/x86/kernel/process_32.c +++ b/arch/x86/kernel/process_32.c @@ -55,6 +55,8 @@ #include <asm/switch_to.h> #include <asm/vm86.h> +#include "process.h" + asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); asmlinkage void ret_from_kernel_thread(void) __asm__("ret_from_kernel_thread"); @@ -279,12 +281,7 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p) if (get_kernel_rpl() && unlikely(prev->iopl != next->iopl)) set_iopl_mask(next->iopl); - /* - * Now maybe handle debug registers and/or IO bitmaps - */ - if (unlikely(task_thread_info(prev_p)->flags & _TIF_WORK_CTXSW_PREV || - task_thread_info(next_p)->flags & _TIF_WORK_CTXSW_NEXT)) - __switch_to_xtra(prev_p, next_p, tss); + switch_to_extra(prev_p, next_p); /* * Leave lazy mode, flushing any hypercalls made here. diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index c7cc81e9bb84..618565fecb1c 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c @@ -50,6 +50,8 @@ #include <asm/switch_to.h> #include <asm/xen/hypervisor.h> +#include "process.h" + asmlinkage extern void ret_from_fork(void); __visible DEFINE_PER_CPU(unsigned long, rsp_scratch); @@ -406,12 +408,7 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p) /* Reload esp0 and ss1. This changes current_thread_info(). */ load_sp0(tss, next); - /* - * Now maybe reload the debug registers and handle I/O bitmaps - */ - if (unlikely(task_thread_info(next_p)->flags & _TIF_WORK_CTXSW_NEXT || - task_thread_info(prev_p)->flags & _TIF_WORK_CTXSW_PREV)) - __switch_to_xtra(prev_p, next_p, tss); + switch_to_extra(prev_p, next_p); #ifdef CONFIG_XEN /* diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index b857bb9f6f23..53918abccbc3 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c @@ -343,7 +343,8 @@ static inline int __do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function, /* cpuid 0x80000008.ebx */ const u32 kvm_cpuid_8000_0008_ebx_x86_features = - F(AMD_IBPB) | F(AMD_IBRS) | F(VIRT_SSBD); + F(AMD_IBPB) | F(AMD_IBRS) | F(AMD_SSBD) | F(VIRT_SSBD) | + F(AMD_SSB_NO) | F(AMD_STIBP); /* cpuid 0xC0000001.edx */ const u32 kvm_supported_word5_x86_features = @@ -364,7 +365,8 @@ static inline int __do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function, /* cpuid 7.0.edx*/ const u32 kvm_cpuid_7_0_edx_x86_features = - F(SPEC_CTRL) | F(SPEC_CTRL_SSBD) | F(ARCH_CAPABILITIES); + F(SPEC_CTRL) | F(SPEC_CTRL_SSBD) | F(ARCH_CAPABILITIES) | + F(INTEL_STIBP) | F(MD_CLEAR); /* all calls to cpuid_count() should be made on the same cpu */ get_cpu(); @@ -607,7 +609,12 @@ static inline int __do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function, entry->ebx |= F(VIRT_SSBD); entry->ebx &= kvm_cpuid_8000_0008_ebx_x86_features; cpuid_mask(&entry->ebx, CPUID_8000_0008_EBX); - if (boot_cpu_has(X86_FEATURE_LS_CFG_SSBD)) + /* + * The preference is to use SPEC CTRL MSR instead of the + * VIRT_SPEC MSR. + */ + if (boot_cpu_has(X86_FEATURE_LS_CFG_SSBD) && + !boot_cpu_has(X86_FEATURE_AMD_SSBD)) entry->ebx |= F(VIRT_SSBD); break; } diff --git a/arch/x86/kvm/cpuid.h b/arch/x86/kvm/cpuid.h index 72f159f4d456..8c28926dc900 100644 --- a/arch/x86/kvm/cpuid.h +++ b/arch/x86/kvm/cpuid.h @@ -175,7 +175,7 @@ static inline bool guest_cpuid_has_spec_ctrl(struct kvm_vcpu *vcpu) struct kvm_cpuid_entry2 *best; best = kvm_find_cpuid_entry(vcpu, 0x80000008, 0); - if (best && (best->ebx & bit(X86_FEATURE_AMD_IBRS))) + if (best && (best->ebx & (bit(X86_FEATURE_AMD_IBRS | bit(X86_FEATURE_AMD_SSBD))))) return true; best = kvm_find_cpuid_entry(vcpu, 7, 0); return best && (best->edx & (bit(X86_FEATURE_SPEC_CTRL) | bit(X86_FEATURE_SPEC_CTRL_SSBD))); diff --git a/arch/x86/kvm/pmu_intel.c b/arch/x86/kvm/pmu_intel.c index 23a7c7ba377a..8fc07ea23344 100644 --- a/arch/x86/kvm/pmu_intel.c +++ b/arch/x86/kvm/pmu_intel.c @@ -235,11 +235,14 @@ static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) } break; default: - if ((pmc = get_gp_pmc(pmu, msr, MSR_IA32_PERFCTR0)) || - (pmc = get_fixed_pmc(pmu, msr))) { - if (!msr_info->host_initiated) - data = (s64)(s32)data; - pmc->counter += data - pmc_read_counter(pmc); + if ((pmc = get_gp_pmc(pmu, msr, MSR_IA32_PERFCTR0))) { + if (msr_info->host_initiated) + pmc->counter = data; + else + pmc->counter = (s32)data; + return 0; + } else if ((pmc = get_fixed_pmc(pmu, msr))) { + pmc->counter = data; return 0; } else if ((pmc = get_gp_pmc(pmu, msr, MSR_P6_EVNTSEL0))) { if (data == pmc->eventsel) diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c index acbde1249b6f..9fc536657492 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c @@ -3197,7 +3197,7 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr) return 1; /* The STIBP bit doesn't fault even if it's not advertised */ - if (data & ~(SPEC_CTRL_IBRS | SPEC_CTRL_STIBP)) + if (data & ~(SPEC_CTRL_IBRS | SPEC_CTRL_STIBP | SPEC_CTRL_SSBD)) return 1; svm->spec_ctrl = data; @@ -3928,8 +3928,6 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu) clgi(); - local_irq_enable(); - /* * If this vCPU has touched SPEC_CTRL, restore the guest's value if * it's non-zero. Since vmentry is serialising on affected CPUs, there @@ -3938,6 +3936,8 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu) */ x86_spec_ctrl_set_guest(svm->spec_ctrl, svm->virt_spec_ctrl); + local_irq_enable(); + asm volatile ( "push %%" _ASM_BP "; \n\t" "mov %c[rbx](%[svm]), %%" _ASM_BX " \n\t" @@ -4060,12 +4060,12 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu) if (!msr_write_intercepted(vcpu, MSR_IA32_SPEC_CTRL)) svm->spec_ctrl = native_read_msr(MSR_IA32_SPEC_CTRL); - x86_spec_ctrl_restore_host(svm->spec_ctrl, svm->virt_spec_ctrl); - reload_tss(vcpu); local_irq_disable(); + x86_spec_ctrl_restore_host(svm->spec_ctrl, svm->virt_spec_ctrl); + vcpu->arch.cr2 = svm->vmcb->save.cr2; vcpu->arch.regs[VCPU_REGS_RAX] = svm->vmcb->save.rax; vcpu->arch.regs[VCPU_REGS_RSP] = svm->vmcb->save.rsp; diff --git a/arch/x86/kvm/trace.h b/arch/x86/kvm/trace.h index ab9ae67a80e4..0ec94c6b4757 100644 --- a/arch/x86/kvm/trace.h +++ b/arch/x86/kvm/trace.h @@ -434,13 +434,13 @@ TRACE_EVENT(kvm_apic_ipi, ); TRACE_EVENT(kvm_apic_accept_irq, - TP_PROTO(__u32 apicid, __u16 dm, __u8 tm, __u8 vec), + TP_PROTO(__u32 apicid, __u16 dm, __u16 tm, __u8 vec), TP_ARGS(apicid, dm, tm, vec), TP_STRUCT__entry( __field( __u32, apicid ) __field( __u16, dm ) - __field( __u8, tm ) + __field( __u16, tm ) __field( __u8, vec ) ), diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 706c5d63a53f..516d8b1562c8 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -990,11 +990,8 @@ static u32 emulated_msrs[] = { static unsigned num_emulated_msrs; -bool kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer) +static bool __kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer) { - if (efer & efer_reserved_bits) - return false; - if (efer & EFER_FFXSR) { struct kvm_cpuid_entry2 *feat; @@ -1012,19 +1009,33 @@ bool kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer) } return true; + +} +bool kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer) +{ + if (efer & efer_reserved_bits) + return false; + + return __kvm_valid_efer(vcpu, efer); } EXPORT_SYMBOL_GPL(kvm_valid_efer); -static int set_efer(struct kvm_vcpu *vcpu, u64 efer) +static int set_efer(struct kvm_vcpu *vcpu, struct msr_data *msr_info) { u64 old_efer = vcpu->arch.efer; + u64 efer = msr_info->data; - if (!kvm_valid_efer(vcpu, efer)) + if (efer & efer_reserved_bits) return 1; - if (is_paging(vcpu) - && (vcpu->arch.efer & EFER_LME) != (efer & EFER_LME)) - return 1; + if (!msr_info->host_initiated) { + if (!__kvm_valid_efer(vcpu, efer)) + return 1; + + if (is_paging(vcpu) && + (vcpu->arch.efer & EFER_LME) != (efer & EFER_LME)) + return 1; + } efer &= ~EFER_LMA; efer |= vcpu->arch.efer & EFER_LMA; @@ -2055,7 +2066,7 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) break; case MSR_EFER: - return set_efer(vcpu, data); + return set_efer(vcpu, msr_info); case MSR_K7_HWCR: data &= ~(u64)0x40; /* ignore flush filter disable */ data &= ~(u64)0x100; /* ignore ignne emulation enable */ @@ -2972,6 +2983,10 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu, | KVM_VCPUEVENT_VALID_SMM)) return -EINVAL; + if (events->exception.injected && + (events->exception.nr > 31 || events->exception.nr == NMI_VECTOR)) + return -EINVAL; + /* INITs are latched while in SMM */ if (events->flags & KVM_VCPUEVENT_VALID_SMM && (events->smi.smm || events->smi.pending) && diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index c4dffae5d939..462c5c30b9a2 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c @@ -373,8 +373,6 @@ static noinline int vmalloc_fault(unsigned long address) if (!(address >= VMALLOC_START && address < VMALLOC_END)) return -1; - WARN_ON_ONCE(in_nmi()); - /* * Copy kernel mappings over when needed. This can also * happen within a race in page table update. In the later diff --git a/arch/x86/mm/kaiser.c b/arch/x86/mm/kaiser.c index 7a72e32e4806..2cbcd6f3317d 100644 --- a/arch/x86/mm/kaiser.c +++ b/arch/x86/mm/kaiser.c @@ -10,6 +10,7 @@ #include <linux/mm.h> #include <linux/uaccess.h> #include <linux/ftrace.h> +#include <linux/cpu.h> #undef pr_fmt #define pr_fmt(fmt) "Kernel/User page tables isolation: " fmt @@ -297,7 +298,8 @@ void __init kaiser_check_boottime_disable(void) goto skip; } - if (cmdline_find_option_bool(boot_command_line, "nopti")) + if (cmdline_find_option_bool(boot_command_line, "nopti") || + cpu_mitigations_off()) goto disable; skip: diff --git a/arch/x86/mm/pgtable.c b/arch/x86/mm/pgtable.c index 55c7446311a7..50f75768aadd 100644 --- a/arch/x86/mm/pgtable.c +++ b/arch/x86/mm/pgtable.c @@ -247,7 +247,7 @@ static void pgd_mop_up_pmds(struct mm_struct *mm, pgd_t *pgdp) if (pgd_val(pgd) != 0) { pmd_t *pmd = (pmd_t *)pgd_page_vaddr(pgd); - pgdp[i] = native_make_pgd(0); + pgd_clear(&pgdp[i]); paravirt_release_pmd(pgd_val(pgd) >> PAGE_SHIFT); pmd_free(mm, pmd); @@ -424,7 +424,7 @@ int ptep_set_access_flags(struct vm_area_struct *vma, int changed = !pte_same(*ptep, entry); if (changed && dirty) { - *ptep = entry; + set_pte(ptep, entry); pte_update_defer(vma->vm_mm, address, ptep); } @@ -441,7 +441,7 @@ int pmdp_set_access_flags(struct vm_area_struct *vma, VM_BUG_ON(address & ~HPAGE_PMD_MASK); if (changed && dirty) { - *pmdp = entry; + set_pmd(pmdp, entry); pmd_update_defer(vma->vm_mm, address, pmdp); /* * We had a write-protection fault here and changed the pmd diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c index 6d683bbb3502..f3237e4cb18f 100644 --- a/arch/x86/mm/tlb.c +++ b/arch/x86/mm/tlb.c @@ -30,6 +30,12 @@ * Implement flush IPI by CALL_FUNCTION_VECTOR, Alex Shi */ +/* + * Use bit 0 to mangle the TIF_SPEC_IB state into the mm pointer which is + * stored in cpu_tlb_state.last_user_mm_ibpb. + */ +#define LAST_USER_MM_IBPB 0x1UL + atomic64_t last_mm_ctx_id = ATOMIC64_INIT(1); struct flush_tlb_info { @@ -101,41 +107,101 @@ void switch_mm(struct mm_struct *prev, struct mm_struct *next, local_irq_restore(flags); } -void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next, - struct task_struct *tsk) +static inline unsigned long mm_mangle_tif_spec_ib(struct task_struct *next) { - unsigned cpu = smp_processor_id(); + unsigned long next_tif = task_thread_info(next)->flags; + unsigned long ibpb = (next_tif >> TIF_SPEC_IB) & LAST_USER_MM_IBPB; - if (likely(prev != next)) { - u64 last_ctx_id = this_cpu_read(cpu_tlbstate.last_ctx_id); + return (unsigned long)next->mm | ibpb; +} + +static void cond_ibpb(struct task_struct *next) +{ + if (!next || !next->mm) + return; + + /* + * Both, the conditional and the always IBPB mode use the mm + * pointer to avoid the IBPB when switching between tasks of the + * same process. Using the mm pointer instead of mm->context.ctx_id + * opens a hypothetical hole vs. mm_struct reuse, which is more or + * less impossible to control by an attacker. Aside of that it + * would only affect the first schedule so the theoretically + * exposed data is not really interesting. + */ + if (static_branch_likely(&switch_mm_cond_ibpb)) { + unsigned long prev_mm, next_mm; /* - * Avoid user/user BTB poisoning by flushing the branch - * predictor when switching between processes. This stops - * one process from doing Spectre-v2 attacks on another. + * This is a bit more complex than the always mode because + * it has to handle two cases: + * + * 1) Switch from a user space task (potential attacker) + * which has TIF_SPEC_IB set to a user space task + * (potential victim) which has TIF_SPEC_IB not set. + * + * 2) Switch from a user space task (potential attacker) + * which has TIF_SPEC_IB not set to a user space task + * (potential victim) which has TIF_SPEC_IB set. + * + * This could be done by unconditionally issuing IBPB when + * a task which has TIF_SPEC_IB set is either scheduled in + * or out. Though that results in two flushes when: + * + * - the same user space task is scheduled out and later + * scheduled in again and only a kernel thread ran in + * between. + * + * - a user space task belonging to the same process is + * scheduled in after a kernel thread ran in between * - * As an optimization, flush indirect branches only when - * switching into processes that disable dumping. This - * protects high value processes like gpg, without having - * too high performance overhead. IBPB is *expensive*! + * - a user space task belonging to the same process is + * scheduled in immediately. * - * This will not flush branches when switching into kernel - * threads. It will also not flush if we switch to idle - * thread and back to the same process. It will flush if we - * switch to a different non-dumpable process. + * Optimize this with reasonably small overhead for the + * above cases. Mangle the TIF_SPEC_IB bit into the mm + * pointer of the incoming task which is stored in + * cpu_tlbstate.last_user_mm_ibpb for comparison. */ - if (tsk && tsk->mm && - tsk->mm->context.ctx_id != last_ctx_id && - get_dumpable(tsk->mm) != SUID_DUMP_USER) + next_mm = mm_mangle_tif_spec_ib(next); + prev_mm = this_cpu_read(cpu_tlbstate.last_user_mm_ibpb); + + /* + * Issue IBPB only if the mm's are different and one or + * both have the IBPB bit set. + */ + if (next_mm != prev_mm && + (next_mm | prev_mm) & LAST_USER_MM_IBPB) indirect_branch_prediction_barrier(); + this_cpu_write(cpu_tlbstate.last_user_mm_ibpb, next_mm); + } + + if (static_branch_unlikely(&switch_mm_always_ibpb)) { /* - * Record last user mm's context id, so we can avoid - * flushing branch buffer with IBPB if we switch back - * to the same user. + * Only flush when switching to a user space task with a + * different context than the user space task which ran + * last on this CPU. + */ + if (this_cpu_read(cpu_tlbstate.last_user_mm) != next->mm) { + indirect_branch_prediction_barrier(); + this_cpu_write(cpu_tlbstate.last_user_mm, next->mm); + } + } +} + +void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next, + struct task_struct *tsk) +{ + unsigned cpu = smp_processor_id(); + + if (likely(prev != next)) { + /* + * Avoid user/user BTB poisoning by flushing the branch + * predictor when switching between processes. This stops + * one process from doing Spectre-v2 attacks on another. */ - if (next != &init_mm) - this_cpu_write(cpu_tlbstate.last_ctx_id, next->context.ctx_id); + cond_ibpb(tsk); this_cpu_write(cpu_tlbstate.state, TLBSTATE_OK); this_cpu_write(cpu_tlbstate.active_mm, next); diff --git a/arch/x86/pci/irq.c b/arch/x86/pci/irq.c index 9bd115484745..5f0e596b0519 100644 --- a/arch/x86/pci/irq.c +++ b/arch/x86/pci/irq.c @@ -1117,6 +1117,8 @@ static struct dmi_system_id __initdata pciirq_dmi_table[] = { void __init pcibios_irq_init(void) { + struct irq_routing_table *rtable = NULL; + DBG(KERN_DEBUG "PCI: IRQ init\n"); if (raw_pci_ops == NULL) @@ -1127,8 +1129,10 @@ void __init pcibios_irq_init(void) pirq_table = pirq_find_routing_table(); #ifdef CONFIG_PCI_BIOS - if (!pirq_table && (pci_probe & PCI_BIOS_IRQ_SCAN)) + if (!pirq_table && (pci_probe & PCI_BIOS_IRQ_SCAN)) { pirq_table = pcibios_get_irq_routing_table(); + rtable = pirq_table; + } #endif if (pirq_table) { pirq_peer_trick(); @@ -1143,8 +1147,10 @@ void __init pcibios_irq_init(void) * If we're using the I/O APIC, avoid using the PCI IRQ * routing table */ - if (io_apic_assign_pci_irqs) + if (io_apic_assign_pci_irqs) { + kfree(rtable); pirq_table = NULL; + } } x86_init.pci.fixup_irqs(); diff --git a/build.config.cuttlefish.aarch64 b/build.config.cuttlefish.aarch64 index 6bf6755ab3a8..818b3dc1b3a3 100644 --- a/build.config.cuttlefish.aarch64 +++ b/build.config.cuttlefish.aarch64 @@ -6,7 +6,7 @@ DEFCONFIG=cuttlefish_defconfig EXTRA_CMDS='' KERNEL_DIR=common POST_DEFCONFIG_CMDS="check_defconfig" -CLANG_PREBUILT_BIN=prebuilts-master/clang/host/linux-x86/clang-r349610/bin +CLANG_PREBUILT_BIN=prebuilts-master/clang/host/linux-x86/clang-r353983c/bin LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin FILES=" arch/arm64/boot/Image.gz diff --git a/build.config.cuttlefish.x86_64 b/build.config.cuttlefish.x86_64 index e3477c3b3cbe..84556472221d 100644 --- a/build.config.cuttlefish.x86_64 +++ b/build.config.cuttlefish.x86_64 @@ -6,7 +6,7 @@ DEFCONFIG=x86_64_cuttlefish_defconfig EXTRA_CMDS='' KERNEL_DIR=common POST_DEFCONFIG_CMDS="check_defconfig" -CLANG_PREBUILT_BIN=prebuilts-master/clang/host/linux-x86/clang-r349610/bin +CLANG_PREBUILT_BIN=prebuilts-master/clang/host/linux-x86/clang-r353983c/bin LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/bin FILES=" arch/x86/boot/bzImage diff --git a/crypto/chacha20poly1305.c b/crypto/chacha20poly1305.c index 0214600ba071..6c4724222e3a 100644 --- a/crypto/chacha20poly1305.c +++ b/crypto/chacha20poly1305.c @@ -637,8 +637,8 @@ static int chachapoly_create(struct crypto_template *tmpl, struct rtattr **tb, err = -ENAMETOOLONG; if (snprintf(inst->alg.base.cra_name, CRYPTO_MAX_ALG_NAME, - "%s(%s,%s)", name, chacha_name, - poly_name) >= CRYPTO_MAX_ALG_NAME) + "%s(%s,%s)", name, chacha->cra_name, + poly->cra_name) >= CRYPTO_MAX_ALG_NAME) goto out_drop_chacha; if (snprintf(inst->alg.base.cra_driver_name, CRYPTO_MAX_ALG_NAME, "%s(%s,%s)", name, chacha->cra_driver_name, diff --git a/crypto/crct10dif_generic.c b/crypto/crct10dif_generic.c index c1229614c7e3..eed577714975 100644 --- a/crypto/crct10dif_generic.c +++ b/crypto/crct10dif_generic.c @@ -65,10 +65,9 @@ static int chksum_final(struct shash_desc *desc, u8 *out) return 0; } -static int __chksum_finup(__u16 *crcp, const u8 *data, unsigned int len, - u8 *out) +static int __chksum_finup(__u16 crc, const u8 *data, unsigned int len, u8 *out) { - *(__u16 *)out = crc_t10dif_generic(*crcp, data, len); + *(__u16 *)out = crc_t10dif_generic(crc, data, len); return 0; } @@ -77,15 +76,13 @@ static int chksum_finup(struct shash_desc *desc, const u8 *data, { struct chksum_desc_ctx *ctx = shash_desc_ctx(desc); - return __chksum_finup(&ctx->crc, data, len, out); + return __chksum_finup(ctx->crc, data, len, out); } static int chksum_digest(struct shash_desc *desc, const u8 *data, unsigned int length, u8 *out) { - struct chksum_desc_ctx *ctx = shash_desc_ctx(desc); - - return __chksum_finup(&ctx->crc, data, length, out); + return __chksum_finup(0, data, length, out); } static struct shash_alg alg = { diff --git a/crypto/gcm.c b/crypto/gcm.c index 0a12c09d7cb2..9d3bffc0238f 100644 --- a/crypto/gcm.c +++ b/crypto/gcm.c @@ -616,7 +616,6 @@ static void crypto_gcm_free(struct aead_instance *inst) static int crypto_gcm_create_common(struct crypto_template *tmpl, struct rtattr **tb, - const char *full_name, const char *ctr_name, const char *ghash_name) { @@ -657,7 +656,8 @@ static int crypto_gcm_create_common(struct crypto_template *tmpl, goto err_free_inst; err = -EINVAL; - if (ghash->digestsize != 16) + if (strcmp(ghash->base.cra_name, "ghash") != 0 || + ghash->digestsize != 16) goto err_drop_ghash; crypto_set_skcipher_spawn(&ctx->ctr, aead_crypto_instance(inst)); @@ -669,24 +669,24 @@ static int crypto_gcm_create_common(struct crypto_template *tmpl, ctr = crypto_skcipher_spawn_alg(&ctx->ctr); - /* We only support 16-byte blocks. */ - if (ctr->cra_ablkcipher.ivsize != 16) - goto out_put_ctr; - - /* Not a stream cipher? */ + /* The skcipher algorithm must be CTR mode, using 16-byte blocks. */ err = -EINVAL; - if (ctr->cra_blocksize != 1) + if (strncmp(ctr->cra_name, "ctr(", 4) != 0 || + ctr->cra_ablkcipher.ivsize != 16 || + ctr->cra_blocksize != 1) goto out_put_ctr; err = -ENAMETOOLONG; + if (snprintf(inst->alg.base.cra_name, CRYPTO_MAX_ALG_NAME, + "gcm(%s", ctr->cra_name + 4) >= CRYPTO_MAX_ALG_NAME) + goto out_put_ctr; + if (snprintf(inst->alg.base.cra_driver_name, CRYPTO_MAX_ALG_NAME, "gcm_base(%s,%s)", ctr->cra_driver_name, ghash_alg->cra_driver_name) >= CRYPTO_MAX_ALG_NAME) goto out_put_ctr; - memcpy(inst->alg.base.cra_name, full_name, CRYPTO_MAX_ALG_NAME); - inst->alg.base.cra_flags = (ghash->base.cra_flags | ctr->cra_flags) & CRYPTO_ALG_ASYNC; inst->alg.base.cra_priority = (ghash->base.cra_priority + @@ -727,7 +727,6 @@ static int crypto_gcm_create(struct crypto_template *tmpl, struct rtattr **tb) { const char *cipher_name; char ctr_name[CRYPTO_MAX_ALG_NAME]; - char full_name[CRYPTO_MAX_ALG_NAME]; cipher_name = crypto_attr_alg_name(tb[1]); if (IS_ERR(cipher_name)) @@ -737,12 +736,7 @@ static int crypto_gcm_create(struct crypto_template *tmpl, struct rtattr **tb) CRYPTO_MAX_ALG_NAME) return -ENAMETOOLONG; - if (snprintf(full_name, CRYPTO_MAX_ALG_NAME, "gcm(%s)", cipher_name) >= - CRYPTO_MAX_ALG_NAME) - return -ENAMETOOLONG; - - return crypto_gcm_create_common(tmpl, tb, full_name, - ctr_name, "ghash"); + return crypto_gcm_create_common(tmpl, tb, ctr_name, "ghash"); } static struct crypto_template crypto_gcm_tmpl = { @@ -756,7 +750,6 @@ static int crypto_gcm_base_create(struct crypto_template *tmpl, { const char *ctr_name; const char *ghash_name; - char full_name[CRYPTO_MAX_ALG_NAME]; ctr_name = crypto_attr_alg_name(tb[1]); if (IS_ERR(ctr_name)) @@ -766,12 +759,7 @@ static int crypto_gcm_base_create(struct crypto_template *tmpl, if (IS_ERR(ghash_name)) return PTR_ERR(ghash_name); - if (snprintf(full_name, CRYPTO_MAX_ALG_NAME, "gcm_base(%s,%s)", - ctr_name, ghash_name) >= CRYPTO_MAX_ALG_NAME) - return -ENAMETOOLONG; - - return crypto_gcm_create_common(tmpl, tb, full_name, - ctr_name, ghash_name); + return crypto_gcm_create_common(tmpl, tb, ctr_name, ghash_name); } static struct crypto_template crypto_gcm_base_tmpl = { diff --git a/crypto/salsa20_generic.c b/crypto/salsa20_generic.c index d7da0eea5622..319d9962552e 100644 --- a/crypto/salsa20_generic.c +++ b/crypto/salsa20_generic.c @@ -186,7 +186,7 @@ static int encrypt(struct blkcipher_desc *desc, blkcipher_walk_init(&walk, dst, src, nbytes); err = blkcipher_walk_virt_block(desc, &walk, 64); - salsa20_ivsetup(ctx, walk.iv); + salsa20_ivsetup(ctx, desc->info); while (walk.nbytes >= 64) { salsa20_encrypt_bytes(ctx, walk.dst.virt.addr, diff --git a/drivers/android/binder.c b/drivers/android/binder.c index bff9f44161c2..91fe6c39c804 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -3130,6 +3130,7 @@ static void binder_transaction(struct binder_proc *proc, if (target_node && target_node->txn_security_ctx) { u32 secid; + size_t added_size; security_task_getsecid(proc->tsk, &secid); ret = security_secid_to_secctx(secid, &secctx, &secctx_sz); @@ -3139,7 +3140,15 @@ static void binder_transaction(struct binder_proc *proc, return_error_line = __LINE__; goto err_get_secctx_failed; } - extra_buffers_size += ALIGN(secctx_sz, sizeof(u64)); + added_size = ALIGN(secctx_sz, sizeof(u64)); + extra_buffers_size += added_size; + if (extra_buffers_size < added_size) { + /* integer overflow of extra_buffers_size */ + return_error = BR_FAILED_REPLY; + return_error_param = EINVAL; + return_error_line = __LINE__; + goto err_bad_extra_size; + } } trace_binder_transaction(reply, t, target_node); @@ -3441,6 +3450,7 @@ err_copy_data_failed: t->buffer->transaction = NULL; binder_alloc_free_buf(&target_proc->alloc, t->buffer); err_binder_alloc_buf_failed: +err_bad_extra_size: if (secctx) security_release_secctx(secctx, secctx_sz); err_get_secctx_failed: diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c index baecb4bf9d9b..c8614f3c70c4 100644 --- a/drivers/android/binder_alloc.c +++ b/drivers/android/binder_alloc.c @@ -924,15 +924,14 @@ enum lru_status binder_alloc_free_page(struct list_head *item, index = page - alloc->pages; page_addr = (uintptr_t)alloc->buffer + index * PAGE_SIZE; + + mm = alloc->vma_vm_mm; + /* Same as mmget_not_zero() in later kernel versions */ + if (!atomic_inc_not_zero(&alloc->vma_vm_mm->mm_users)) + goto err_mmget; + if (!down_write_trylock(&mm->mmap_sem)) + goto err_down_write_mmap_sem_failed; vma = alloc->vma; - if (vma) { - /* Same as mmget_not_zero() in later kernel versions */ - if (!atomic_inc_not_zero(&alloc->vma_vm_mm->mm_users)) - goto err_mmget; - mm = alloc->vma_vm_mm; - if (!down_write_trylock(&mm->mmap_sem)) - goto err_down_write_mmap_sem_failed; - } list_lru_isolate(lru, item); spin_unlock(lock); @@ -946,10 +945,9 @@ enum lru_status binder_alloc_free_page(struct list_head *item, PAGE_SIZE, NULL); trace_binder_unmap_user_end(alloc, index); - - up_write(&mm->mmap_sem); - mmput(mm); } + up_write(&mm->mmap_sem); + mmput(mm); trace_binder_unmap_kernel_start(alloc, index); diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index d543172b20b3..a352f09baef6 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4176,9 +4176,12 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { { "ST3320[68]13AS", "SD1[5-9]", ATA_HORKAGE_NONCQ | ATA_HORKAGE_FIRMWARE_WARN }, - /* drives which fail FPDMA_AA activation (some may freeze afterwards) */ - { "ST1000LM024 HN-M101MBB", "2AR10001", ATA_HORKAGE_BROKEN_FPDMA_AA }, - { "ST1000LM024 HN-M101MBB", "2BA30001", ATA_HORKAGE_BROKEN_FPDMA_AA }, + /* drives which fail FPDMA_AA activation (some may freeze afterwards) + the ST disks also have LPM issues */ + { "ST1000LM024 HN-M101MBB", "2AR10001", ATA_HORKAGE_BROKEN_FPDMA_AA | + ATA_HORKAGE_NOLPM, }, + { "ST1000LM024 HN-M101MBB", "2BA30001", ATA_HORKAGE_BROKEN_FPDMA_AA | + ATA_HORKAGE_NOLPM, }, { "VB0250EAVER", "HPG7", ATA_HORKAGE_BROKEN_FPDMA_AA }, /* Blacklist entries taken from Silicon Image 3124/3132 diff --git a/drivers/ata/libata-zpodd.c b/drivers/ata/libata-zpodd.c index 0ad96c647541..7017a81d53cf 100644 --- a/drivers/ata/libata-zpodd.c +++ b/drivers/ata/libata-zpodd.c @@ -51,38 +51,52 @@ static int eject_tray(struct ata_device *dev) /* Per the spec, only slot type and drawer type ODD can be supported */ static enum odd_mech_type zpodd_get_mech_type(struct ata_device *dev) { - char buf[16]; + char *buf; unsigned int ret; - struct rm_feature_desc *desc = (void *)(buf + 8); + struct rm_feature_desc *desc; struct ata_taskfile tf; static const char cdb[] = { GPCMD_GET_CONFIGURATION, 2, /* only 1 feature descriptor requested */ 0, 3, /* 3, removable medium feature */ 0, 0, 0,/* reserved */ - 0, sizeof(buf), + 0, 16, 0, 0, 0, }; + buf = kzalloc(16, GFP_KERNEL); + if (!buf) + return ODD_MECH_TYPE_UNSUPPORTED; + desc = (void *)(buf + 8); + ata_tf_init(dev, &tf); tf.flags = ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; tf.command = ATA_CMD_PACKET; tf.protocol = ATAPI_PROT_PIO; - tf.lbam = sizeof(buf); + tf.lbam = 16; ret = ata_exec_internal(dev, &tf, cdb, DMA_FROM_DEVICE, - buf, sizeof(buf), 0); - if (ret) + buf, 16, 0); + if (ret) { + kfree(buf); return ODD_MECH_TYPE_UNSUPPORTED; + } - if (be16_to_cpu(desc->feature_code) != 3) + if (be16_to_cpu(desc->feature_code) != 3) { + kfree(buf); return ODD_MECH_TYPE_UNSUPPORTED; + } - if (desc->mech_type == 0 && desc->load == 0 && desc->eject == 1) + if (desc->mech_type == 0 && desc->load == 0 && desc->eject == 1) { + kfree(buf); return ODD_MECH_TYPE_SLOT; - else if (desc->mech_type == 1 && desc->load == 0 && desc->eject == 1) + } else if (desc->mech_type == 1 && desc->load == 0 && + desc->eject == 1) { + kfree(buf); return ODD_MECH_TYPE_DRAWER; - else + } else { + kfree(buf); return ODD_MECH_TYPE_UNSUPPORTED; + } } /* Test if ODD is zero power ready by sense code */ diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index fb2a1e605c86..e5ac568c7c9b 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -705,11 +705,18 @@ ssize_t __weak cpu_show_l1tf(struct device *dev, return sprintf(buf, "Not affected\n"); } +ssize_t __weak cpu_show_mds(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "Not affected\n"); +} + static DEVICE_ATTR(meltdown, 0444, cpu_show_meltdown, NULL); static DEVICE_ATTR(spectre_v1, 0444, cpu_show_spectre_v1, NULL); static DEVICE_ATTR(spectre_v2, 0444, cpu_show_spectre_v2, NULL); static DEVICE_ATTR(spec_store_bypass, 0444, cpu_show_spec_store_bypass, NULL); static DEVICE_ATTR(l1tf, 0444, cpu_show_l1tf, NULL); +static DEVICE_ATTR(mds, 0444, cpu_show_mds, NULL); static struct attribute *cpu_root_vulnerabilities_attrs[] = { &dev_attr_meltdown.attr, @@ -717,6 +724,7 @@ static struct attribute *cpu_root_vulnerabilities_attrs[] = { &dev_attr_spectre_v2.attr, &dev_attr_spec_store_bypass.attr, &dev_attr_l1tf.attr, + &dev_attr_mds.attr, NULL }; diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 38857ab563f7..4192e46eda37 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -1385,6 +1385,10 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) if (dev->power.syscore) goto Complete; + /* Avoid direct_complete to let wakeup_path propagate. */ + if (device_may_wakeup(dev) || dev->power.wakeup_path) + dev->power.direct_complete = false; + if (dev->power.direct_complete) { if (pm_runtime_status_suspended(dev)) { pm_runtime_disable(dev); diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 91f19dfc388d..1a1b094da6cb 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -82,7 +82,6 @@ static DEFINE_IDR(loop_index_idr); static DEFINE_MUTEX(loop_index_mutex); -static DEFINE_MUTEX(loop_ctl_mutex); static int max_part; static int part_shift; @@ -1045,7 +1044,7 @@ static int loop_clr_fd(struct loop_device *lo) */ if (atomic_read(&lo->lo_refcnt) > 1) { lo->lo_flags |= LO_FLAGS_AUTOCLEAR; - mutex_unlock(&loop_ctl_mutex); + mutex_unlock(&lo->lo_ctl_mutex); return 0; } @@ -1095,12 +1094,12 @@ static int loop_clr_fd(struct loop_device *lo) if (!part_shift) lo->lo_disk->flags |= GENHD_FL_NO_PART_SCAN; loop_unprepare_queue(lo); - mutex_unlock(&loop_ctl_mutex); + mutex_unlock(&lo->lo_ctl_mutex); /* - * Need not hold loop_ctl_mutex to fput backing file. - * Calling fput holding loop_ctl_mutex triggers a circular + * Need not hold lo_ctl_mutex to fput backing file. + * Calling fput holding lo_ctl_mutex triggers a circular * lock dependency possibility warning as fput can take - * bd_mutex which is usually taken before loop_ctl_mutex. + * bd_mutex which is usually taken before lo_ctl_mutex. */ fput(filp); return 0; @@ -1413,7 +1412,7 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode, struct loop_device *lo = bdev->bd_disk->private_data; int err; - mutex_lock_nested(&loop_ctl_mutex, 1); + mutex_lock_nested(&lo->lo_ctl_mutex, 1); switch (cmd) { case LOOP_SET_FD: err = loop_set_fd(lo, mode, bdev, arg); @@ -1422,7 +1421,7 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode, err = loop_change_fd(lo, bdev, arg); break; case LOOP_CLR_FD: - /* loop_clr_fd would have unlocked loop_ctl_mutex on success */ + /* loop_clr_fd would have unlocked lo_ctl_mutex on success */ err = loop_clr_fd(lo); if (!err) goto out_unlocked; @@ -1463,7 +1462,7 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode, default: err = lo->ioctl ? lo->ioctl(lo, cmd, arg) : -EINVAL; } - mutex_unlock(&loop_ctl_mutex); + mutex_unlock(&lo->lo_ctl_mutex); out_unlocked: return err; @@ -1596,16 +1595,16 @@ static int lo_compat_ioctl(struct block_device *bdev, fmode_t mode, switch(cmd) { case LOOP_SET_STATUS: - mutex_lock(&loop_ctl_mutex); + mutex_lock(&lo->lo_ctl_mutex); err = loop_set_status_compat( lo, (const struct compat_loop_info __user *) arg); - mutex_unlock(&loop_ctl_mutex); + mutex_unlock(&lo->lo_ctl_mutex); break; case LOOP_GET_STATUS: - mutex_lock(&loop_ctl_mutex); + mutex_lock(&lo->lo_ctl_mutex); err = loop_get_status_compat( lo, (struct compat_loop_info __user *) arg); - mutex_unlock(&loop_ctl_mutex); + mutex_unlock(&lo->lo_ctl_mutex); break; case LOOP_SET_CAPACITY: case LOOP_CLR_FD: @@ -1650,7 +1649,7 @@ static void __lo_release(struct loop_device *lo) if (atomic_dec_return(&lo->lo_refcnt)) return; - mutex_lock(&loop_ctl_mutex); + mutex_lock(&lo->lo_ctl_mutex); if (lo->lo_flags & LO_FLAGS_AUTOCLEAR) { /* * In autoclear mode, stop the loop thread @@ -1667,7 +1666,7 @@ static void __lo_release(struct loop_device *lo) loop_flush(lo); } - mutex_unlock(&loop_ctl_mutex); + mutex_unlock(&lo->lo_ctl_mutex); } static void lo_release(struct gendisk *disk, fmode_t mode) @@ -1713,10 +1712,10 @@ static int unregister_transfer_cb(int id, void *ptr, void *data) struct loop_device *lo = ptr; struct loop_func_table *xfer = data; - mutex_lock(&loop_ctl_mutex); + mutex_lock(&lo->lo_ctl_mutex); if (lo->lo_encryption == xfer) loop_release_xfer(lo); - mutex_unlock(&loop_ctl_mutex); + mutex_unlock(&lo->lo_ctl_mutex); return 0; } @@ -1879,6 +1878,7 @@ static int loop_add(struct loop_device **l, int i) if (!part_shift) disk->flags |= GENHD_FL_NO_PART_SCAN; disk->flags |= GENHD_FL_EXT_DEVT; + mutex_init(&lo->lo_ctl_mutex); atomic_set(&lo->lo_refcnt, 0); lo->lo_number = i; spin_lock_init(&lo->lo_lock); @@ -1991,19 +1991,19 @@ static long loop_control_ioctl(struct file *file, unsigned int cmd, ret = loop_lookup(&lo, parm); if (ret < 0) break; - mutex_lock(&loop_ctl_mutex); + mutex_lock(&lo->lo_ctl_mutex); if (lo->lo_state != Lo_unbound) { ret = -EBUSY; - mutex_unlock(&loop_ctl_mutex); + mutex_unlock(&lo->lo_ctl_mutex); break; } if (atomic_read(&lo->lo_refcnt) > 0) { ret = -EBUSY; - mutex_unlock(&loop_ctl_mutex); + mutex_unlock(&lo->lo_ctl_mutex); break; } lo->lo_disk->private_data = NULL; - mutex_unlock(&loop_ctl_mutex); + mutex_unlock(&lo->lo_ctl_mutex); idr_remove(&loop_index_idr, lo->lo_number); loop_remove(lo); break; diff --git a/drivers/block/loop.h b/drivers/block/loop.h index a923e74495ce..60f0fd2c0c65 100644 --- a/drivers/block/loop.h +++ b/drivers/block/loop.h @@ -55,6 +55,7 @@ struct loop_device { spinlock_t lo_lock; int lo_state; + struct mutex lo_ctl_mutex; struct kthread_worker worker; struct task_struct *worker_task; bool use_dio; diff --git a/drivers/block/xsysace.c b/drivers/block/xsysace.c index c4328d9d9981..f838119d12b2 100644 --- a/drivers/block/xsysace.c +++ b/drivers/block/xsysace.c @@ -1062,6 +1062,8 @@ static int ace_setup(struct ace_device *ace) return 0; err_read: + /* prevent double queue cleanup */ + ace->gd->queue = NULL; put_disk(ace->gd); err_alloc_disk: blk_cleanup_queue(ace->queue); diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index e9be78e24ac7..0e7befd4a146 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -179,10 +179,12 @@ struct smq_invoke_ctx { int tgid; remote_arg_t *lpra; remote_arg64_t *rpra; + remote_arg64_t *lrpra; /* Local copy of rpra for put_args */ int *fds; unsigned *attrs; struct fastrpc_mmap **maps; struct fastrpc_buf *buf; + struct fastrpc_buf *lbuf; size_t used; struct fastrpc_file *fl; uint32_t sc; @@ -1090,6 +1092,7 @@ static void context_free(struct smq_invoke_ctx *ctx) for (i = 0; i < nbufs; ++i) fastrpc_mmap_free(ctx->maps[i]); fastrpc_buf_free(ctx->buf, 1); + fastrpc_buf_free(ctx->lbuf, 1); ctx->magic = 0; ctx->ctxid = 0; @@ -1202,7 +1205,7 @@ static void fastrpc_file_list_dtor(struct fastrpc_apps *me) static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx) { - remote_arg64_t *rpra; + remote_arg64_t *rpra, *lrpra; remote_arg_t *lpra = ctx->lpra; struct smq_invoke_buf *list; struct smq_phy_page *pages, *ipage; @@ -1211,10 +1214,11 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx) int outbufs = REMOTE_SCALARS_OUTBUFS(sc); int bufs = inbufs + outbufs; uintptr_t args; - size_t rlen = 0, copylen = 0, metalen = 0; + size_t rlen = 0, copylen = 0, metalen = 0, lrpralen = 0; int i, inh, oix; int err = 0; int mflags = 0; + DEFINE_DMA_ATTRS(ctx_attrs); /* calculate size of the metadata */ rpra = NULL; @@ -1233,7 +1237,22 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx) ipage += 1; } metalen = copylen = (size_t)&ipage[0]; - /* calculate len requreed for copying */ + + /* allocate new local rpra buffer */ + lrpralen = (size_t)&list[0]; + if (lrpralen) { + err = fastrpc_buf_alloc(ctx->fl, lrpralen, ctx_attrs, + 0, 0, &ctx->lbuf); + if (err) + goto bail; + } + if (ctx->lbuf->virt) + memset(ctx->lbuf->virt, 0, lrpralen); + + lrpra = ctx->lbuf->virt; + ctx->lrpra = lrpra; + + /* calculate len required for copying */ for (oix = 0; oix < inbufs + outbufs; ++oix) { int i = ctx->overps[oix]->raix; uintptr_t mstart, mend; @@ -1259,8 +1278,6 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx) /* allocate new buffer */ if (copylen) { - DEFINE_DMA_ATTRS(ctx_attrs); - err = fastrpc_buf_alloc(ctx->fl, copylen, ctx_attrs, 0, 0, &ctx->buf); if (err) @@ -1292,13 +1309,13 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx) } /* map ion buffers */ PERF(ctx->fl->profile, ctx->fl->perf.map, - for (i = 0; i < inbufs + outbufs; ++i) { + for (i = 0; rpra && lrpra && i < inbufs + outbufs; ++i) { struct fastrpc_mmap *map = ctx->maps[i]; uint64_t buf = ptr_to_uint64(lpra[i].buf.pv); size_t len = lpra[i].buf.len; - rpra[i].buf.pv = 0; - rpra[i].buf.len = len; + rpra[i].buf.pv = lrpra[i].buf.pv = 0; + rpra[i].buf.len = lrpra[i].buf.len = len; if (!len) continue; if (map) { @@ -1326,14 +1343,14 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx) pages[idx].addr = map->phys + offset; pages[idx].size = num << PAGE_SHIFT; } - rpra[i].buf.pv = buf; + rpra[i].buf.pv = lrpra[i].buf.pv = buf; } PERF_END); /* copy non ion buffers */ PERF(ctx->fl->profile, ctx->fl->perf.copy, rlen = copylen - metalen; - for (oix = 0; rpra && oix < inbufs + outbufs; ++oix) { + for (oix = 0; rpra && lrpra && oix < inbufs + outbufs; ++oix) { int i = ctx->overps[oix]->raix; struct fastrpc_mmap *map = ctx->maps[i]; size_t mlen; @@ -1352,7 +1369,8 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx) VERIFY(err, rlen >= mlen); if (err) goto bail; - rpra[i].buf.pv = (args - ctx->overps[oix]->offset); + rpra[i].buf.pv = lrpra[i].buf.pv = + (args - ctx->overps[oix]->offset); pages[list[i].pgidx].addr = ctx->buf->phys - ctx->overps[oix]->offset + (copylen - rlen); @@ -1384,7 +1402,8 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx) if (map && (map->attr & FASTRPC_ATTR_COHERENT)) continue; - if (rpra && rpra[i].buf.len && ctx->overps[oix]->mstart) { + if (rpra && lrpra && rpra[i].buf.len && + ctx->overps[oix]->mstart) { if (map && map->handle) msm_ion_do_cache_op(ctx->fl->apps->client, map->handle, @@ -1400,10 +1419,12 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx) PERF_END); inh = inbufs + outbufs; - for (i = 0; rpra && i < REMOTE_SCALARS_INHANDLES(sc); i++) { - rpra[inh + i].buf.pv = ptr_to_uint64(ctx->lpra[inh + i].buf.pv); - rpra[inh + i].buf.len = ctx->lpra[inh + i].buf.len; - rpra[inh + i].h = ctx->lpra[inh + i].h; + for (i = 0; rpra && lrpra && i < REMOTE_SCALARS_INHANDLES(sc); i++) { + rpra[inh + i].buf.pv = lrpra[inh + i].buf.pv = + ptr_to_uint64(ctx->lpra[inh + i].buf.pv); + rpra[inh + i].buf.len = lrpra[inh + i].buf.len = + ctx->lpra[inh + i].buf.len; + rpra[inh + i].h = lrpra[inh + i].h = ctx->lpra[inh + i].h; } bail: @@ -1414,7 +1435,7 @@ static int put_args(uint32_t kernel, struct smq_invoke_ctx *ctx, remote_arg_t *upra) { uint32_t sc = ctx->sc; - remote_arg64_t *rpra = ctx->rpra; + remote_arg64_t *rpra = ctx->lrpra; int i, inbufs, outbufs, outh, size; int err = 0; @@ -1503,7 +1524,7 @@ static void inv_args(struct smq_invoke_ctx *ctx) { int i, inbufs, outbufs; uint32_t sc = ctx->sc; - remote_arg64_t *rpra = ctx->rpra; + remote_arg64_t *rpra = ctx->lrpra; int inv = 0; inbufs = REMOTE_SCALARS_INBUFS(sc); diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c index 8d5f505e4e34..d18768b0e9da 100644 --- a/drivers/char/diag/diag_dci.c +++ b/drivers/char/diag/diag_dci.c @@ -1947,7 +1947,7 @@ static int diag_process_dci_pkt_rsp(unsigned char *buf, int len) if (!buf) return -EIO; - if (len <= (sizeof(struct dci_pkt_req_t) + + if (len < (sizeof(struct dci_pkt_req_t) + sizeof(struct diag_pkt_header_t)) || len > DCI_REQ_BUF_SIZE) { pr_err("diag: dci: Invalid length %d len in %s", len, __func__); @@ -1960,7 +1960,6 @@ static int diag_process_dci_pkt_rsp(unsigned char *buf, int len) req_len -= sizeof(struct dci_pkt_req_t); req_buf = temp; /* Start of the Request */ header = (struct diag_pkt_header_t *)temp; - temp += sizeof(struct diag_pkt_header_t); read_len += sizeof(struct diag_pkt_header_t); if (read_len >= DCI_REQ_BUF_SIZE) { pr_err("diag: dci: In %s, invalid read_len: %d\n", __func__, @@ -2072,9 +2071,9 @@ int diag_process_dci_transaction(unsigned char *buf, int len) uint8_t *event_mask_ptr; struct diag_dci_client_tbl *dci_entry = NULL; - if (!temp) { - pr_err("diag: Invalid buffer in %s\n", __func__); - return -ENOMEM; + if (!temp || len < sizeof(int)) { + pr_err("diag: Invalid input in %s\n", __func__); + return -EINVAL; } /* This is Pkt request/response transaction */ @@ -2129,7 +2128,7 @@ int diag_process_dci_transaction(unsigned char *buf, int len) count = 0; /* iterator for extracting log codes */ while (count < num_codes) { - if (read_len >= USER_SPACE_DATA) { + if (read_len + sizeof(uint16_t) > len) { pr_err("diag: dci: Invalid length for log type in %s", __func__); mutex_unlock(&driver->dci_mutex); @@ -2242,7 +2241,7 @@ int diag_process_dci_transaction(unsigned char *buf, int len) pr_debug("diag: head of dci event mask %pK\n", event_mask_ptr); count = 0; /* iterator for extracting log codes */ while (count < num_codes) { - if (read_len >= USER_SPACE_DATA) { + if (read_len + sizeof(int) > len) { pr_err("diag: dci: Invalid length for event type in %s", __func__); mutex_unlock(&driver->dci_mutex); diff --git a/drivers/char/diag/diag_dci.h b/drivers/char/diag/diag_dci.h index c2a8ac1e3854..800a4f7312cf 100644 --- a/drivers/char/diag/diag_dci.h +++ b/drivers/char/diag/diag_dci.h @@ -27,7 +27,7 @@ #define DISABLE_LOG_MASK 0 #define MAX_EVENT_SIZE 512 #define DCI_CLIENT_INDEX_INVALID -1 -#define DCI_LOG_CON_MIN_LEN 14 +#define DCI_LOG_CON_MIN_LEN 16 #define DCI_EVENT_CON_MIN_LEN 16 #define EXT_HDR_LEN 8 diff --git a/drivers/char/diag/diag_masks.c b/drivers/char/diag/diag_masks.c index 04d827de8570..519c01e3ca78 100644 --- a/drivers/char/diag/diag_masks.c +++ b/drivers/char/diag/diag_masks.c @@ -643,7 +643,7 @@ static int diag_cmd_get_build_mask(unsigned char *src_buf, int src_len, struct diag_build_mask_req_t *req = NULL; struct diag_msg_build_mask_t rsp; - if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 || + if (!src_buf || !dest_buf || dest_len <= 0 || src_len < sizeof(struct diag_build_mask_req_t)) { pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d\n", __func__, src_buf, src_len, dest_buf, dest_len); @@ -705,7 +705,7 @@ static int diag_cmd_get_msg_mask(unsigned char *src_buf, int src_len, info = diag_md_session_get_pid(pid); mask_info = (!info) ? &msg_mask : info->msg_mask; - if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 || + if (!src_buf || !dest_buf || dest_len <= 0 || !mask_info || (src_len < sizeof(struct diag_build_mask_req_t))) { pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n", __func__, src_buf, src_len, dest_buf, dest_len, @@ -787,8 +787,8 @@ static int diag_cmd_set_msg_mask(unsigned char *src_buf, int src_len, info = diag_md_session_get_pid(pid); mask_info = (!info) ? &msg_mask : info->msg_mask; - if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 || - !mask_info) { + if (!src_buf || !dest_buf || dest_len <= 0 || !mask_info || + (src_len < sizeof(struct diag_msg_build_mask_t))) { pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n", __func__, src_buf, src_len, dest_buf, dest_len, mask_info); @@ -872,7 +872,9 @@ static int diag_cmd_set_msg_mask(unsigned char *src_buf, int src_len, break; } mask_size = mask_size * sizeof(uint32_t); - memcpy(mask->ptr + offset, src_buf + header_len, mask_size); + if (mask_size && src_len >= header_len + mask_size) + memcpy(mask->ptr + offset, src_buf + header_len, + mask_size); mutex_unlock(&mask->lock); mask_info->status = DIAG_CTRL_MASK_VALID; break; @@ -929,8 +931,8 @@ static int diag_cmd_set_all_msg_mask(unsigned char *src_buf, int src_len, info = diag_md_session_get_pid(pid); mask_info = (!info) ? &msg_mask : info->msg_mask; - if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 || - !mask_info) { + if (!src_buf || !dest_buf || dest_len <= 0 || !mask_info || + (src_len < sizeof(struct diag_msg_config_rsp_t))) { pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n", __func__, src_buf, src_len, dest_buf, dest_len, mask_info); @@ -1049,8 +1051,8 @@ static int diag_cmd_update_event_mask(unsigned char *src_buf, int src_len, mutex_lock(&driver->md_session_lock); info = diag_md_session_get_pid(pid); mask_info = (!info) ? &event_mask : info->event_mask; - if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 || - !mask_info) { + if (!src_buf || !dest_buf || dest_len <= 0 || !mask_info || + src_len < sizeof(struct diag_event_mask_config_t)) { pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n", __func__, src_buf, src_len, dest_buf, dest_len, mask_info); @@ -1073,7 +1075,8 @@ static int diag_cmd_update_event_mask(unsigned char *src_buf, int src_len, } mutex_lock(&mask_info->lock); - memcpy(mask_info->ptr, src_buf + header_len, mask_len); + if (src_len >= header_len + mask_len) + memcpy(mask_info->ptr, src_buf + header_len, mask_len); mask_info->status = DIAG_CTRL_MASK_VALID; mutex_unlock(&mask_info->lock); mutex_unlock(&driver->md_session_lock); @@ -1117,8 +1120,8 @@ static int diag_cmd_toggle_events(unsigned char *src_buf, int src_len, mutex_lock(&driver->md_session_lock); info = diag_md_session_get_pid(pid); mask_info = (!info) ? &event_mask : info->event_mask; - if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 || - !mask_info) { + if (!src_buf || !dest_buf || src_len <= sizeof(uint8_t) || + dest_len <= 0 || !mask_info) { pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n", __func__, src_buf, src_len, dest_buf, dest_len, mask_info); @@ -1185,8 +1188,8 @@ static int diag_cmd_get_log_mask(unsigned char *src_buf, int src_len, info = diag_md_session_get_pid(pid); mask_info = (!info) ? &log_mask : info->log_mask; - if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 || - !mask_info) { + if (!src_buf || !dest_buf || dest_len <= 0 || !mask_info || + src_len < sizeof(struct diag_log_config_req_t)) { pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n", __func__, src_buf, src_len, dest_buf, dest_len, mask_info); @@ -1329,8 +1332,8 @@ static int diag_cmd_set_log_mask(unsigned char *src_buf, int src_len, info = diag_md_session_get_pid(pid); mask_info = (!info) ? &log_mask : info->log_mask; - if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 || - !mask_info) { + if (!src_buf || !dest_buf || dest_len <= 0 || !mask_info || + src_len < sizeof(struct diag_log_config_req_t)) { pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n", __func__, src_buf, src_len, dest_buf, dest_len, mask_info); @@ -1404,7 +1407,7 @@ static int diag_cmd_set_log_mask(unsigned char *src_buf, int src_len, mask->range_tools = mask_size; } req->num_items = mask->num_items_tools; - if (mask_size > 0) + if (mask_size > 0 && src_len >= read_len + mask_size) memcpy(mask->ptr, src_buf + read_len, mask_size); DIAG_LOG(DIAG_DEBUG_MASKS, "copying log mask, e %d num %d range %d size %d\n", @@ -2270,6 +2273,8 @@ int diag_process_apps_masks(unsigned char *buf, int len, int pid) return -EINVAL; if (*buf == DIAG_CMD_LOG_CONFIG) { + if (len < (2 * sizeof(int))) + return -EINVAL; sub_cmd = *(int *)(buf + sizeof(int)); switch (sub_cmd) { case DIAG_CMD_OP_LOG_DISABLE: @@ -2286,6 +2291,8 @@ int diag_process_apps_masks(unsigned char *buf, int len, int pid) break; } } else if (*buf == DIAG_CMD_MSG_CONFIG) { + if (len < (2 * sizeof(uint8_t))) + return -EINVAL; sub_cmd = *(uint8_t *)(buf + sizeof(uint8_t)); switch (sub_cmd) { case DIAG_CMD_OP_GET_SSID_RANGE: diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c index 520f553200f5..bed9ef14ff78 100644 --- a/drivers/char/diag/diagchar_core.c +++ b/drivers/char/diag/diagchar_core.c @@ -1188,15 +1188,19 @@ static int diag_process_userspace_remote(int proc, void *buf, int len) } #endif -static int mask_request_validate(unsigned char mask_buf[]) +static int mask_request_validate(unsigned char mask_buf[], int len) { uint8_t packet_id; uint8_t subsys_id; uint16_t ss_cmd; + if (len <= 0) + return 0; packet_id = mask_buf[0]; if (packet_id == DIAG_CMD_DIAG_SUBSYS_DELAY) { + if (len < 2*sizeof(uint8_t) + sizeof(uint16_t)) + return 0; subsys_id = mask_buf[1]; ss_cmd = *(uint16_t *)(mask_buf + 2); switch (subsys_id) { @@ -1212,6 +1216,8 @@ static int mask_request_validate(unsigned char mask_buf[]) return 0; } } else if (packet_id == 0x4B) { + if (len < 2*sizeof(uint8_t) + sizeof(uint16_t)) + return 0; subsys_id = mask_buf[1]; ss_cmd = *(uint16_t *)(mask_buf + 2); /* Packets with SSID which are allowed */ @@ -2893,7 +2899,8 @@ static int diag_user_process_raw_data(const char __user *buf, int len) } /* Check for proc_type */ - remote_proc = diag_get_remote(*(int *)user_space_data); + if (len >= sizeof(int)) + remote_proc = diag_get_remote(*(int *)user_space_data); if (remote_proc) { token_offset = sizeof(int); if (len <= MIN_SIZ_ALLOW) { @@ -2907,7 +2914,7 @@ static int diag_user_process_raw_data(const char __user *buf, int len) } if (driver->mask_check) { if (!mask_request_validate(user_space_data + - token_offset)) { + token_offset, len)) { pr_alert("diag: mask request Invalid\n"); diagmem_free(driver, user_space_data, mempool); user_space_data = NULL; @@ -2985,7 +2992,7 @@ static int diag_user_process_userspace_data(const char __user *buf, int len) /* Check masks for On-Device logging */ if (driver->mask_check) { if (!mask_request_validate(driver->user_space_data_buf + - token_offset)) { + token_offset, len)) { pr_alert("diag: mask request Invalid\n"); return -EFAULT; } diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c index 75c68c903371..711da56468df 100644 --- a/drivers/char/diag/diagfwd.c +++ b/drivers/char/diag/diagfwd.c @@ -1648,7 +1648,7 @@ void diag_process_non_hdlc_pkt(unsigned char *buf, int len, int pid) if (*(uint8_t *)(data_ptr + actual_pkt->length) != CONTROL_CHAR) { mutex_unlock(&driver->hdlc_recovery_mutex); - diag_hdlc_start_recovery(buf, len, pid); + diag_hdlc_start_recovery(buf, (len - read_bytes), pid); mutex_lock(&driver->hdlc_recovery_mutex); } err = diag_process_apps_pkt(data_ptr, @@ -1674,8 +1674,8 @@ start: pkt_len = actual_pkt->length; if (actual_pkt->start != CONTROL_CHAR) { - diag_hdlc_start_recovery(buf, len, pid); - diag_send_error_rsp(buf, len, pid); + diag_hdlc_start_recovery(buf, (len - read_bytes), pid); + diag_send_error_rsp(buf, (len - read_bytes), pid); goto end; } mutex_lock(&driver->hdlc_recovery_mutex); @@ -1683,7 +1683,7 @@ start: pr_err("diag: In %s, incoming data is too large for the request buffer %d\n", __func__, pkt_len); mutex_unlock(&driver->hdlc_recovery_mutex); - diag_hdlc_start_recovery(buf, len, pid); + diag_hdlc_start_recovery(buf, (len - read_bytes), pid); break; } if ((pkt_len + header_len) > (len - read_bytes)) { @@ -1700,7 +1700,7 @@ start: if (*(uint8_t *)(data_ptr + actual_pkt->length) != CONTROL_CHAR) { mutex_unlock(&driver->hdlc_recovery_mutex); - diag_hdlc_start_recovery(buf, len, pid); + diag_hdlc_start_recovery(buf, (len - read_bytes), pid); mutex_lock(&driver->hdlc_recovery_mutex); } else diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index b2da2382d544..67d23ed2d1a0 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -695,12 +695,16 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, /* End of read */ len = ssif_info->multi_len; data = ssif_info->data; - } else if (blocknum != ssif_info->multi_pos) { + } else if (blocknum + 1 != ssif_info->multi_pos) { /* * Out of sequence block, just abort. Block * numbers start at zero for the second block, * but multi_pos starts at one, so the +1. */ + if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) + dev_dbg(&ssif_info->client->dev, + "Received message out of sequence, expected %u, got %u\n", + ssif_info->multi_pos - 1, blocknum); result = -EIO; } else { ssif_inc_stat(ssif_info, received_message_parts); diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 2aca689061e1..df9eab91c2d2 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -76,7 +76,7 @@ struct ports_driver_data { /* All the console devices handled by this driver */ struct list_head consoles; }; -static struct ports_driver_data pdrvdata; +static struct ports_driver_data pdrvdata = { .next_vtermno = 1}; static DEFINE_SPINLOCK(pdrvdata_lock); static DECLARE_COMPLETION(early_console_added); @@ -1419,6 +1419,7 @@ static int add_port(struct ports_device *portdev, u32 id) port->async_queue = NULL; port->cons.ws.ws_row = port->cons.ws.ws_col = 0; + port->cons.vtermno = 0; port->host_connected = port->guest_connected = false; port->stats = (struct port_stats) { 0 }; diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c index 9040878e3e2b..a6cda84b67da 100644 --- a/drivers/clk/rockchip/clk-rk3288.c +++ b/drivers/clk/rockchip/clk-rk3288.c @@ -797,6 +797,9 @@ static const int rk3288_saved_cru_reg_ids[] = { RK3288_CLKSEL_CON(10), RK3288_CLKSEL_CON(33), RK3288_CLKSEL_CON(37), + + /* We turn aclk_dmac1 on for suspend; this will restore it */ + RK3288_CLKGATE_CON(10), }; static u32 rk3288_saved_cru_regs[ARRAY_SIZE(rk3288_saved_cru_reg_ids)]; @@ -813,6 +816,14 @@ static int rk3288_clk_suspend(void) } /* + * Going into deep sleep (specifically setting PMU_CLR_DMA in + * RK3288_PMU_PWRMODE_CON1) appears to fail unless + * "aclk_dmac1" is on. + */ + writel_relaxed(1 << (12 + 16), + rk3288_cru_base + RK3288_CLKGATE_CON(10)); + + /* * Switch PLLs other than DPLL (for SDRAM) to slow mode to * avoid crashes on resume. The Mask ROM on the system will * put APLL, CPLL, and GPLL into slow mode at resume time diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index d6d4ecb88e94..311f6e62264f 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -492,8 +492,8 @@ static void _update_pll_mnp(struct tegra_clk_pll *pll, pll_override_writel(val, params->pmc_divp_reg, pll); val = pll_override_readl(params->pmc_divnm_reg, pll); - val &= ~(divm_mask(pll) << div_nmp->override_divm_shift) | - ~(divn_mask(pll) << div_nmp->override_divn_shift); + val &= ~((divm_mask(pll) << div_nmp->override_divm_shift) | + (divn_mask(pll) << div_nmp->override_divn_shift)); val |= (cfg->m << div_nmp->override_divm_shift) | (cfg->n << div_nmp->override_divn_shift); pll_override_writel(val, params->pmc_divnm_reg, pll); diff --git a/drivers/cpufreq/pasemi-cpufreq.c b/drivers/cpufreq/pasemi-cpufreq.c index 35dd4d7ffee0..58c933f48300 100644 --- a/drivers/cpufreq/pasemi-cpufreq.c +++ b/drivers/cpufreq/pasemi-cpufreq.c @@ -146,6 +146,7 @@ static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy) cpu = of_get_cpu_node(policy->cpu, NULL); + of_node_put(cpu); if (!cpu) goto out; diff --git a/drivers/cpufreq/pmac32-cpufreq.c b/drivers/cpufreq/pmac32-cpufreq.c index 1f49d97a70ea..14928e0dc326 100644 --- a/drivers/cpufreq/pmac32-cpufreq.c +++ b/drivers/cpufreq/pmac32-cpufreq.c @@ -549,6 +549,7 @@ static int pmac_cpufreq_init_7447A(struct device_node *cpunode) volt_gpio_np = of_find_node_by_name(NULL, "cpu-vcore-select"); if (volt_gpio_np) voltage_gpio = read_gpio(volt_gpio_np); + of_node_put(volt_gpio_np); if (!voltage_gpio){ printk(KERN_ERR "cpufreq: missing cpu-vcore-select gpio\n"); return 1; @@ -585,6 +586,7 @@ static int pmac_cpufreq_init_750FX(struct device_node *cpunode) if (volt_gpio_np) voltage_gpio = read_gpio(volt_gpio_np); + of_node_put(volt_gpio_np); pvr = mfspr(SPRN_PVR); has_cpu_l2lve = !((pvr & 0xf00) == 0x100); diff --git a/drivers/cpufreq/ppc_cbe_cpufreq.c b/drivers/cpufreq/ppc_cbe_cpufreq.c index 5a4c5a639f61..2eaeebcc93af 100644 --- a/drivers/cpufreq/ppc_cbe_cpufreq.c +++ b/drivers/cpufreq/ppc_cbe_cpufreq.c @@ -86,6 +86,7 @@ static int cbe_cpufreq_cpu_init(struct cpufreq_policy *policy) if (!cbe_get_cpu_pmd_regs(policy->cpu) || !cbe_get_cpu_mic_tm_regs(policy->cpu)) { pr_info("invalid CBE regs pointers for cpufreq\n"); + of_node_put(cpu); return -EINVAL; } diff --git a/drivers/crypto/amcc/crypto4xx_alg.c b/drivers/crypto/amcc/crypto4xx_alg.c index e3b8bebfdd30..4afca3968773 100644 --- a/drivers/crypto/amcc/crypto4xx_alg.c +++ b/drivers/crypto/amcc/crypto4xx_alg.c @@ -138,8 +138,7 @@ static int crypto4xx_setkey_aes(struct crypto_ablkcipher *cipher, sa = (struct dynamic_sa_ctl *) ctx->sa_in; ctx->hash_final = 0; - set_dynamic_sa_command_0(sa, SA_NOT_SAVE_HASH, (cm == CRYPTO_MODE_CBC ? - SA_SAVE_IV : SA_NOT_SAVE_IV), + set_dynamic_sa_command_0(sa, SA_NOT_SAVE_HASH, SA_NOT_SAVE_IV, SA_LOAD_HASH_FROM_SA, SA_LOAD_IV_FROM_STATE, SA_NO_HEADER_PROC, SA_HASH_ALG_NULL, SA_CIPHER_ALG_AES, SA_PAD_TYPE_ZERO, diff --git a/drivers/crypto/amcc/crypto4xx_core.c b/drivers/crypto/amcc/crypto4xx_core.c index 1e810f5f03fa..78d0722feacb 100644 --- a/drivers/crypto/amcc/crypto4xx_core.c +++ b/drivers/crypto/amcc/crypto4xx_core.c @@ -645,15 +645,6 @@ static u32 crypto4xx_ablkcipher_done(struct crypto4xx_device *dev, addr = dma_map_page(dev->core_dev->device, sg_page(dst), dst->offset, dst->length, DMA_FROM_DEVICE); } - - if (pd_uinfo->sa_va->sa_command_0.bf.save_iv == SA_SAVE_IV) { - struct crypto_skcipher *skcipher = crypto_skcipher_reqtfm(req); - - crypto4xx_memcpy_from_le32((u32 *)req->iv, - pd_uinfo->sr_va->save_iv, - crypto_skcipher_ivsize(skcipher)); - } - crypto4xx_ret_sg_desc(dev, pd_uinfo); if (ablk_req->base.complete != NULL) ablk_req->base.complete(&ablk_req->base, 0); diff --git a/drivers/crypto/vmx/aesp8-ppc.pl b/drivers/crypto/vmx/aesp8-ppc.pl index 228053921b3f..4277fdd037bb 100644 --- a/drivers/crypto/vmx/aesp8-ppc.pl +++ b/drivers/crypto/vmx/aesp8-ppc.pl @@ -1298,7 +1298,7 @@ Loop_ctr32_enc: addi $idx,$idx,16 bdnz Loop_ctr32_enc - vadduwm $ivec,$ivec,$one + vadduqm $ivec,$ivec,$one vmr $dat,$inptail lvx $inptail,0,$inp addi $inp,$inp,16 @@ -1795,7 +1795,7 @@ Lctr32_enc8x_three: stvx_u $out1,$x10,$out stvx_u $out2,$x20,$out addi $out,$out,0x30 - b Lcbc_dec8x_done + b Lctr32_enc8x_done .align 5 Lctr32_enc8x_two: @@ -1807,7 +1807,7 @@ Lctr32_enc8x_two: stvx_u $out0,$x00,$out stvx_u $out1,$x10,$out addi $out,$out,0x20 - b Lcbc_dec8x_done + b Lctr32_enc8x_done .align 5 Lctr32_enc8x_one: diff --git a/drivers/crypto/vmx/ghash.c b/drivers/crypto/vmx/ghash.c index 84b9389bf1ed..d6b68cf7bba7 100644 --- a/drivers/crypto/vmx/ghash.c +++ b/drivers/crypto/vmx/ghash.c @@ -1,22 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0 /** * GHASH routines supporting VMX instructions on the Power 8 * - * Copyright (C) 2015 International Business Machines Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 only. - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * Copyright (C) 2015, 2019 International Business Machines Inc. * * Author: Marcelo Henrique Cerri <mhcerri@br.ibm.com> + * + * Extended by Daniel Axtens <dja@axtens.net> to replace the fallback + * mechanism. The new approach is based on arm64 code, which is: + * Copyright (C) 2014 - 2018 Linaro Ltd. <ard.biesheuvel@linaro.org> */ #include <linux/types.h> @@ -39,71 +31,25 @@ void gcm_ghash_p8(u64 Xi[2], const u128 htable[16], const u8 *in, size_t len); struct p8_ghash_ctx { + /* key used by vector asm */ u128 htable[16]; - struct crypto_shash *fallback; + /* key used by software fallback */ + be128 key; }; struct p8_ghash_desc_ctx { u64 shash[2]; u8 buffer[GHASH_DIGEST_SIZE]; int bytes; - struct shash_desc fallback_desc; }; -static int p8_ghash_init_tfm(struct crypto_tfm *tfm) -{ - const char *alg = "ghash-generic"; - struct crypto_shash *fallback; - struct crypto_shash *shash_tfm = __crypto_shash_cast(tfm); - struct p8_ghash_ctx *ctx = crypto_tfm_ctx(tfm); - - fallback = crypto_alloc_shash(alg, 0, CRYPTO_ALG_NEED_FALLBACK); - if (IS_ERR(fallback)) { - printk(KERN_ERR - "Failed to allocate transformation for '%s': %ld\n", - alg, PTR_ERR(fallback)); - return PTR_ERR(fallback); - } - - crypto_shash_set_flags(fallback, - crypto_shash_get_flags((struct crypto_shash - *) tfm)); - - /* Check if the descsize defined in the algorithm is still enough. */ - if (shash_tfm->descsize < sizeof(struct p8_ghash_desc_ctx) - + crypto_shash_descsize(fallback)) { - printk(KERN_ERR - "Desc size of the fallback implementation (%s) does not match the expected value: %lu vs %u\n", - alg, - shash_tfm->descsize - sizeof(struct p8_ghash_desc_ctx), - crypto_shash_descsize(fallback)); - return -EINVAL; - } - ctx->fallback = fallback; - - return 0; -} - -static void p8_ghash_exit_tfm(struct crypto_tfm *tfm) -{ - struct p8_ghash_ctx *ctx = crypto_tfm_ctx(tfm); - - if (ctx->fallback) { - crypto_free_shash(ctx->fallback); - ctx->fallback = NULL; - } -} - static int p8_ghash_init(struct shash_desc *desc) { - struct p8_ghash_ctx *ctx = crypto_tfm_ctx(crypto_shash_tfm(desc->tfm)); struct p8_ghash_desc_ctx *dctx = shash_desc_ctx(desc); dctx->bytes = 0; memset(dctx->shash, 0, GHASH_DIGEST_SIZE); - dctx->fallback_desc.tfm = ctx->fallback; - dctx->fallback_desc.flags = desc->flags; - return crypto_shash_init(&dctx->fallback_desc); + return 0; } static int p8_ghash_setkey(struct crypto_shash *tfm, const u8 *key, @@ -122,7 +68,53 @@ static int p8_ghash_setkey(struct crypto_shash *tfm, const u8 *key, gcm_init_p8(ctx->htable, (const u64 *) key); pagefault_enable(); preempt_enable(); - return crypto_shash_setkey(ctx->fallback, key, keylen); + + memcpy(&ctx->key, key, GHASH_BLOCK_SIZE); + + return 0; +} + +static inline void __ghash_block(struct p8_ghash_ctx *ctx, + struct p8_ghash_desc_ctx *dctx) +{ + if (!IN_INTERRUPT) { + preempt_disable(); + pagefault_disable(); + enable_kernel_altivec(); + enable_kernel_vsx(); + enable_kernel_fp(); + gcm_ghash_p8(dctx->shash, ctx->htable, + dctx->buffer, GHASH_DIGEST_SIZE); + pagefault_enable(); + preempt_enable(); + } else { + crypto_xor((u8 *)dctx->shash, dctx->buffer, GHASH_BLOCK_SIZE); + gf128mul_lle((be128 *)dctx->shash, &ctx->key); + } +} + +static inline void __ghash_blocks(struct p8_ghash_ctx *ctx, + struct p8_ghash_desc_ctx *dctx, + const u8 *src, unsigned int srclen) +{ + if (!IN_INTERRUPT) { + preempt_disable(); + pagefault_disable(); + enable_kernel_altivec(); + enable_kernel_vsx(); + enable_kernel_fp(); + gcm_ghash_p8(dctx->shash, ctx->htable, + src, srclen); + pagefault_enable(); + preempt_enable(); + } else { + while (srclen >= GHASH_BLOCK_SIZE) { + crypto_xor((u8 *)dctx->shash, src, GHASH_BLOCK_SIZE); + gf128mul_lle((be128 *)dctx->shash, &ctx->key); + srclen -= GHASH_BLOCK_SIZE; + src += GHASH_BLOCK_SIZE; + } + } } static int p8_ghash_update(struct shash_desc *desc, @@ -132,51 +124,33 @@ static int p8_ghash_update(struct shash_desc *desc, struct p8_ghash_ctx *ctx = crypto_tfm_ctx(crypto_shash_tfm(desc->tfm)); struct p8_ghash_desc_ctx *dctx = shash_desc_ctx(desc); - if (IN_INTERRUPT) { - return crypto_shash_update(&dctx->fallback_desc, src, - srclen); - } else { - if (dctx->bytes) { - if (dctx->bytes + srclen < GHASH_DIGEST_SIZE) { - memcpy(dctx->buffer + dctx->bytes, src, - srclen); - dctx->bytes += srclen; - return 0; - } + if (dctx->bytes) { + if (dctx->bytes + srclen < GHASH_DIGEST_SIZE) { memcpy(dctx->buffer + dctx->bytes, src, - GHASH_DIGEST_SIZE - dctx->bytes); - preempt_disable(); - pagefault_disable(); - enable_kernel_altivec(); - enable_kernel_vsx(); - enable_kernel_fp(); - gcm_ghash_p8(dctx->shash, ctx->htable, - dctx->buffer, GHASH_DIGEST_SIZE); - pagefault_enable(); - preempt_enable(); - src += GHASH_DIGEST_SIZE - dctx->bytes; - srclen -= GHASH_DIGEST_SIZE - dctx->bytes; - dctx->bytes = 0; - } - len = srclen & ~(GHASH_DIGEST_SIZE - 1); - if (len) { - preempt_disable(); - pagefault_disable(); - enable_kernel_altivec(); - enable_kernel_vsx(); - enable_kernel_fp(); - gcm_ghash_p8(dctx->shash, ctx->htable, src, len); - pagefault_enable(); - preempt_enable(); - src += len; - srclen -= len; - } - if (srclen) { - memcpy(dctx->buffer, src, srclen); - dctx->bytes = srclen; + srclen); + dctx->bytes += srclen; + return 0; } - return 0; + memcpy(dctx->buffer + dctx->bytes, src, + GHASH_DIGEST_SIZE - dctx->bytes); + + __ghash_block(ctx, dctx); + + src += GHASH_DIGEST_SIZE - dctx->bytes; + srclen -= GHASH_DIGEST_SIZE - dctx->bytes; + dctx->bytes = 0; + } + len = srclen & ~(GHASH_DIGEST_SIZE - 1); + if (len) { + __ghash_blocks(ctx, dctx, src, len); + src += len; + srclen -= len; } + if (srclen) { + memcpy(dctx->buffer, src, srclen); + dctx->bytes = srclen; + } + return 0; } static int p8_ghash_final(struct shash_desc *desc, u8 *out) @@ -185,26 +159,14 @@ static int p8_ghash_final(struct shash_desc *desc, u8 *out) struct p8_ghash_ctx *ctx = crypto_tfm_ctx(crypto_shash_tfm(desc->tfm)); struct p8_ghash_desc_ctx *dctx = shash_desc_ctx(desc); - if (IN_INTERRUPT) { - return crypto_shash_final(&dctx->fallback_desc, out); - } else { - if (dctx->bytes) { - for (i = dctx->bytes; i < GHASH_DIGEST_SIZE; i++) - dctx->buffer[i] = 0; - preempt_disable(); - pagefault_disable(); - enable_kernel_altivec(); - enable_kernel_vsx(); - enable_kernel_fp(); - gcm_ghash_p8(dctx->shash, ctx->htable, - dctx->buffer, GHASH_DIGEST_SIZE); - pagefault_enable(); - preempt_enable(); - dctx->bytes = 0; - } - memcpy(out, dctx->shash, GHASH_DIGEST_SIZE); - return 0; + if (dctx->bytes) { + for (i = dctx->bytes; i < GHASH_DIGEST_SIZE; i++) + dctx->buffer[i] = 0; + __ghash_block(ctx, dctx); + dctx->bytes = 0; } + memcpy(out, dctx->shash, GHASH_DIGEST_SIZE); + return 0; } struct shash_alg p8_ghash_alg = { @@ -219,11 +181,9 @@ struct shash_alg p8_ghash_alg = { .cra_name = "ghash", .cra_driver_name = "p8_ghash", .cra_priority = 1000, - .cra_flags = CRYPTO_ALG_TYPE_SHASH | CRYPTO_ALG_NEED_FALLBACK, + .cra_flags = CRYPTO_ALG_TYPE_SHASH, .cra_blocksize = GHASH_BLOCK_SIZE, .cra_ctxsize = sizeof(struct p8_ghash_ctx), .cra_module = THIS_MODULE, - .cra_init = p8_ghash_init_tfm, - .cra_exit = p8_ghash_exit_tfm, }, }; diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c index af24c5bf32d6..8aa3ccf42e55 100644 --- a/drivers/dma/at_xdmac.c +++ b/drivers/dma/at_xdmac.c @@ -1608,7 +1608,11 @@ static void at_xdmac_tasklet(unsigned long data) struct at_xdmac_desc, xfer_node); dev_vdbg(chan2dev(&atchan->chan), "%s: desc 0x%p\n", __func__, desc); - BUG_ON(!desc->active_xfer); + if (!desc->active_xfer) { + dev_err(chan2dev(&atchan->chan), "Xfer not active: exiting"); + spin_unlock_bh(&atchan->lock); + return; + } txd = &desc->tx_dma_desc; diff --git a/drivers/dma/idma64.c b/drivers/dma/idma64.c index 7d56b47e4fcf..25e25b64bc89 100644 --- a/drivers/dma/idma64.c +++ b/drivers/dma/idma64.c @@ -594,7 +594,7 @@ static int idma64_probe(struct idma64_chip *chip) idma64->dma.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); idma64->dma.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; - idma64->dma.dev = chip->dev; + idma64->dma.dev = chip->sysdev; ret = dma_async_device_register(&idma64->dma); if (ret) @@ -632,6 +632,7 @@ static int idma64_platform_probe(struct platform_device *pdev) { struct idma64_chip *chip; struct device *dev = &pdev->dev; + struct device *sysdev = dev->parent; struct resource *mem; int ret; @@ -648,11 +649,12 @@ static int idma64_platform_probe(struct platform_device *pdev) if (IS_ERR(chip->regs)) return PTR_ERR(chip->regs); - ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + ret = dma_coerce_mask_and_coherent(sysdev, DMA_BIT_MASK(64)); if (ret) return ret; chip->dev = dev; + chip->sysdev = sysdev; ret = idma64_probe(chip); if (ret) diff --git a/drivers/dma/idma64.h b/drivers/dma/idma64.h index f6aeff0af8a5..e40c69bd1fb5 100644 --- a/drivers/dma/idma64.h +++ b/drivers/dma/idma64.h @@ -215,12 +215,14 @@ static inline void idma64_writel(struct idma64 *idma64, int offset, u32 value) /** * struct idma64_chip - representation of iDMA 64-bit controller hardware * @dev: struct device of the DMA controller + * @sysdev: struct device of the physical device that does DMA * @irq: irq line * @regs: memory mapped I/O space * @idma64: struct idma64 that is filed by idma64_probe() */ struct idma64_chip { struct device *dev; + struct device *sysdev; int irq; void __iomem *regs; struct idma64 *idma64; diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 95619ee33112..799c182c3eac 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -1006,6 +1006,7 @@ static void _stop(struct pl330_thread *thrd) { void __iomem *regs = thrd->dmac->base; u8 insn[6] = {0, 0, 0, 0, 0, 0}; + u32 inten = readl(regs + INTEN); if (_state(thrd) == PL330_STATE_FAULT_COMPLETING) UNTIL(thrd, PL330_STATE_FAULTING | PL330_STATE_KILLING); @@ -1018,10 +1019,13 @@ static void _stop(struct pl330_thread *thrd) _emit_KILL(0, insn); - /* Stop generating interrupts for SEV */ - writel(readl(regs + INTEN) & ~(1 << thrd->ev), regs + INTEN); - _execute_DBGINSN(thrd, insn, is_manager(thrd)); + + /* clear the event */ + if (inten & (1 << thrd->ev)) + writel(1 << thrd->ev, regs + INTCLR); + /* Stop generating interrupts for SEV */ + writel(inten & ~(1 << thrd->ev), regs + INTEN); } /* Start doing req 'idx' of thread 'thrd' */ diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index e4890dd4fefd..38fb212e58ee 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -1616,6 +1616,16 @@ static int arizona_extcon_remove(struct platform_device *pdev) struct arizona_extcon_info *info = platform_get_drvdata(pdev); struct arizona *arizona = info->arizona; int jack_irq_rise, jack_irq_fall; + bool change; + + regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1, + ARIZONA_MICD_ENA, 0, + &change); + + if (change) { + regulator_disable(info->micvdd); + pm_runtime_put(info->dev); + } gpiod_put(info->micd_pol_gpio); diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index d4729fa59edb..1491dc41cf38 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -598,6 +598,7 @@ config GPIO_ADP5588 config GPIO_ADP5588_IRQ bool "Interrupt controller support for ADP5588" depends on GPIO_ADP5588=y + select GPIOLIB_IRQCHIP help Say yes here to enable the adp5588 to be used as an interrupt controller. It requires the driver to be built in the kernel. diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 9943273ec981..c8c49b1d5f9f 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -292,6 +292,22 @@ static void omap_clear_gpio_debounce(struct gpio_bank *bank, unsigned offset) } } +/* + * Off mode wake-up capable GPIOs in bank(s) that are in the wakeup domain. + * See TRM section for GPIO for "Wake-Up Generation" for the list of GPIOs + * in wakeup domain. If bank->non_wakeup_gpios is not configured, assume none + * are capable waking up the system from off mode. + */ +static bool omap_gpio_is_off_wakeup_capable(struct gpio_bank *bank, u32 gpio_mask) +{ + u32 no_wake = bank->non_wakeup_gpios; + + if (no_wake) + return !!(~no_wake & gpio_mask); + + return false; +} + static inline void omap_set_gpio_trigger(struct gpio_bank *bank, int gpio, unsigned trigger) { @@ -323,13 +339,7 @@ static inline void omap_set_gpio_trigger(struct gpio_bank *bank, int gpio, } /* This part needs to be executed always for OMAP{34xx, 44xx} */ - if (!bank->regs->irqctrl) { - /* On omap24xx proceed only when valid GPIO bit is set */ - if (bank->non_wakeup_gpios) { - if (!(bank->non_wakeup_gpios & gpio_bit)) - goto exit; - } - + if (!bank->regs->irqctrl && !omap_gpio_is_off_wakeup_capable(bank, gpio)) { /* * Log the edge gpio and manually trigger the IRQ * after resume if the input level changes @@ -342,7 +352,6 @@ static inline void omap_set_gpio_trigger(struct gpio_bank *bank, int gpio, bank->enabled_non_wakeup_gpios &= ~gpio_bit; } -exit: bank->level_mask = readl_relaxed(bank->base + bank->regs->leveldetect0) | readl_relaxed(bank->base + bank->regs->leveldetect1); diff --git a/drivers/gpu/drm/gma500/cdv_intel_lvds.c b/drivers/gpu/drm/gma500/cdv_intel_lvds.c index 211069b2b951..010fe3fc5ecf 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_lvds.c +++ b/drivers/gpu/drm/gma500/cdv_intel_lvds.c @@ -620,6 +620,9 @@ void cdv_intel_lvds_init(struct drm_device *dev, int pipe; u8 pin; + if (!dev_priv->lvds_enabled_in_vbt) + return; + pin = GMBUS_PORT_PANEL; if (!lvds_is_present_in_vbt(dev, &pin)) { DRM_DEBUG_KMS("LVDS is not present in VBT\n"); diff --git a/drivers/gpu/drm/gma500/intel_bios.c b/drivers/gpu/drm/gma500/intel_bios.c index 63bde4e86c6a..e019ea271ffc 100644 --- a/drivers/gpu/drm/gma500/intel_bios.c +++ b/drivers/gpu/drm/gma500/intel_bios.c @@ -436,6 +436,9 @@ parse_driver_features(struct drm_psb_private *dev_priv, if (driver->lvds_config == BDB_DRIVER_FEATURE_EDP) dev_priv->edp.support = 1; + dev_priv->lvds_enabled_in_vbt = driver->lvds_config != 0; + DRM_DEBUG_KMS("LVDS VBT config bits: 0x%x\n", driver->lvds_config); + /* This bit means to use 96Mhz for DPLL_A or not */ if (driver->primary_lfp_id) dev_priv->dplla_96mhz = true; diff --git a/drivers/gpu/drm/gma500/psb_drv.h b/drivers/gpu/drm/gma500/psb_drv.h index e21726ecac32..4c7cc8a5edbd 100644 --- a/drivers/gpu/drm/gma500/psb_drv.h +++ b/drivers/gpu/drm/gma500/psb_drv.h @@ -536,6 +536,7 @@ struct drm_psb_private { int lvds_ssc_freq; bool is_lvds_on; bool is_mipi_on; + bool lvds_enabled_in_vbt; u32 mipi_ctrl_display; unsigned int core_freq; diff --git a/drivers/gpu/drm/i2c/adv7511.c b/drivers/gpu/drm/i2c/adv7511.c index c7c243e9b808..4300e27ed113 100644 --- a/drivers/gpu/drm/i2c/adv7511.c +++ b/drivers/gpu/drm/i2c/adv7511.c @@ -781,11 +781,11 @@ static void adv7511_encoder_mode_set(struct drm_encoder *encoder, vsync_polarity = 1; } - if (mode->vrefresh <= 24000) + if (drm_mode_vrefresh(mode) <= 24) low_refresh_rate = ADV7511_LOW_REFRESH_RATE_24HZ; - else if (mode->vrefresh <= 25000) + else if (drm_mode_vrefresh(mode) <= 25) low_refresh_rate = ADV7511_LOW_REFRESH_RATE_25HZ; - else if (mode->vrefresh <= 30000) + else if (drm_mode_vrefresh(mode) <= 30) low_refresh_rate = ADV7511_LOW_REFRESH_RATE_30HZ; else low_refresh_rate = ADV7511_LOW_REFRESH_RATE_NONE; diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/i2c.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/i2c.h index 6b6224dbd5bb..943eb2971c3e 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/i2c.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/i2c.h @@ -37,6 +37,7 @@ struct nvkm_i2c_bus { struct mutex mutex; struct list_head head; struct i2c_adapter i2c; + u8 enabled; }; int nvkm_i2c_bus_acquire(struct nvkm_i2c_bus *); @@ -56,6 +57,7 @@ struct nvkm_i2c_aux { struct mutex mutex; struct list_head head; struct i2c_adapter i2c; + u8 enabled; u32 intr; }; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c index f0851d57df2f..f89692cb2bc7 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c @@ -105,9 +105,15 @@ nvkm_i2c_aux_acquire(struct nvkm_i2c_aux *aux) { struct nvkm_i2c_pad *pad = aux->pad; int ret; + AUX_TRACE(aux, "acquire"); mutex_lock(&aux->mutex); - ret = nvkm_i2c_pad_acquire(pad, NVKM_I2C_PAD_AUX); + + if (aux->enabled) + ret = nvkm_i2c_pad_acquire(pad, NVKM_I2C_PAD_AUX); + else + ret = -EIO; + if (ret) mutex_unlock(&aux->mutex); return ret; @@ -141,6 +147,24 @@ nvkm_i2c_aux_del(struct nvkm_i2c_aux **paux) } } +void +nvkm_i2c_aux_init(struct nvkm_i2c_aux *aux) +{ + AUX_TRACE(aux, "init"); + mutex_lock(&aux->mutex); + aux->enabled = true; + mutex_unlock(&aux->mutex); +} + +void +nvkm_i2c_aux_fini(struct nvkm_i2c_aux *aux) +{ + AUX_TRACE(aux, "fini"); + mutex_lock(&aux->mutex); + aux->enabled = false; + mutex_unlock(&aux->mutex); +} + int nvkm_i2c_aux_ctor(const struct nvkm_i2c_aux_func *func, struct nvkm_i2c_pad *pad, int id, diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.h b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.h index 35a892e4a4c3..04885c097a32 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.h +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.h @@ -14,6 +14,8 @@ int nvkm_i2c_aux_ctor(const struct nvkm_i2c_aux_func *, struct nvkm_i2c_pad *, int nvkm_i2c_aux_new_(const struct nvkm_i2c_aux_func *, struct nvkm_i2c_pad *, int id, struct nvkm_i2c_aux **); void nvkm_i2c_aux_del(struct nvkm_i2c_aux **); +void nvkm_i2c_aux_init(struct nvkm_i2c_aux *); +void nvkm_i2c_aux_fini(struct nvkm_i2c_aux *); int nvkm_i2c_aux_xfer(struct nvkm_i2c_aux *, bool retry, u8 type, u32 addr, u8 *data, u8 size); diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/base.c index 243a71ff0a0d..2acc5cbcb6fb 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/base.c @@ -160,8 +160,18 @@ nvkm_i2c_fini(struct nvkm_subdev *subdev, bool suspend) { struct nvkm_i2c *i2c = nvkm_i2c(subdev); struct nvkm_i2c_pad *pad; + struct nvkm_i2c_bus *bus; + struct nvkm_i2c_aux *aux; u32 mask; + list_for_each_entry(aux, &i2c->aux, head) { + nvkm_i2c_aux_fini(aux); + } + + list_for_each_entry(bus, &i2c->bus, head) { + nvkm_i2c_bus_fini(bus); + } + if ((mask = (1 << i2c->func->aux) - 1), i2c->func->aux_stat) { i2c->func->aux_mask(i2c, NVKM_I2C_ANY, mask, 0); i2c->func->aux_stat(i2c, &mask, &mask, &mask, &mask); @@ -180,6 +190,7 @@ nvkm_i2c_init(struct nvkm_subdev *subdev) struct nvkm_i2c *i2c = nvkm_i2c(subdev); struct nvkm_i2c_bus *bus; struct nvkm_i2c_pad *pad; + struct nvkm_i2c_aux *aux; list_for_each_entry(pad, &i2c->pad, head) { nvkm_i2c_pad_init(pad); @@ -189,6 +200,10 @@ nvkm_i2c_init(struct nvkm_subdev *subdev) nvkm_i2c_bus_init(bus); } + list_for_each_entry(aux, &i2c->aux, head) { + nvkm_i2c_aux_init(aux); + } + return 0; } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bus.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bus.c index 807a2b67bd64..ed50cc3736b9 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bus.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bus.c @@ -110,6 +110,19 @@ nvkm_i2c_bus_init(struct nvkm_i2c_bus *bus) BUS_TRACE(bus, "init"); if (bus->func->init) bus->func->init(bus); + + mutex_lock(&bus->mutex); + bus->enabled = true; + mutex_unlock(&bus->mutex); +} + +void +nvkm_i2c_bus_fini(struct nvkm_i2c_bus *bus) +{ + BUS_TRACE(bus, "fini"); + mutex_lock(&bus->mutex); + bus->enabled = false; + mutex_unlock(&bus->mutex); } void @@ -126,9 +139,15 @@ nvkm_i2c_bus_acquire(struct nvkm_i2c_bus *bus) { struct nvkm_i2c_pad *pad = bus->pad; int ret; + BUS_TRACE(bus, "acquire"); mutex_lock(&bus->mutex); - ret = nvkm_i2c_pad_acquire(pad, NVKM_I2C_PAD_I2C); + + if (bus->enabled) + ret = nvkm_i2c_pad_acquire(pad, NVKM_I2C_PAD_I2C); + else + ret = -EIO; + if (ret) mutex_unlock(&bus->mutex); return ret; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bus.h b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bus.h index e1be14c23e54..2fdb1b8e7164 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bus.h +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bus.h @@ -17,6 +17,7 @@ int nvkm_i2c_bus_new_(const struct nvkm_i2c_bus_func *, struct nvkm_i2c_pad *, int id, struct nvkm_i2c_bus **); void nvkm_i2c_bus_del(struct nvkm_i2c_bus **); void nvkm_i2c_bus_init(struct nvkm_i2c_bus *); +void nvkm_i2c_bus_fini(struct nvkm_i2c_bus *); int nvkm_i2c_bit_xfer(struct nvkm_i2c_bus *, struct i2c_msg *, int); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index ad0dd566aded..8dba10135d53 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -2442,7 +2442,8 @@ static int vmw_cmd_dx_set_shader(struct vmw_private *dev_priv, cmd = container_of(header, typeof(*cmd), header); - if (cmd->body.type >= SVGA3D_SHADERTYPE_DX10_MAX) { + if (cmd->body.type >= SVGA3D_SHADERTYPE_DX10_MAX || + cmd->body.type < SVGA3D_SHADERTYPE_MIN) { DRM_ERROR("Illegal shader type %u.\n", (unsigned) cmd->body.type); return -EINVAL; @@ -2681,6 +2682,10 @@ static int vmw_cmd_dx_view_define(struct vmw_private *dev_priv, if (view_type == vmw_view_max) return -EINVAL; cmd = container_of(header, typeof(*cmd), header); + if (unlikely(cmd->sid == SVGA3D_INVALID_ID)) { + DRM_ERROR("Invalid surface id.\n"); + return -EINVAL; + } ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, user_surface_converter, &cmd->sid, &srf_node); diff --git a/drivers/gpu/ipu-v3/ipu-dp.c b/drivers/gpu/ipu-v3/ipu-dp.c index 98686edbcdbb..33de3a1bac49 100644 --- a/drivers/gpu/ipu-v3/ipu-dp.c +++ b/drivers/gpu/ipu-v3/ipu-dp.c @@ -195,7 +195,8 @@ int ipu_dp_setup_channel(struct ipu_dp *dp, ipu_dp_csc_init(flow, flow->foreground.in_cs, flow->out_cs, DP_COM_CONF_CSC_DEF_BOTH); } else { - if (flow->foreground.in_cs == flow->out_cs) + if (flow->foreground.in_cs == IPUV3_COLORSPACE_UNKNOWN || + flow->foreground.in_cs == flow->out_cs) /* * foreground identical to output, apply color * conversion on background @@ -261,6 +262,8 @@ void ipu_dp_disable_channel(struct ipu_dp *dp) struct ipu_dp_priv *priv = flow->priv; u32 reg, csc; + dp->in_cs = IPUV3_COLORSPACE_UNKNOWN; + if (!dp->foreground) return; @@ -268,8 +271,9 @@ void ipu_dp_disable_channel(struct ipu_dp *dp) reg = readl(flow->base + DP_COM_CONF); csc = reg & DP_COM_CONF_CSC_DEF_MASK; - if (csc == DP_COM_CONF_CSC_DEF_FG) - reg &= ~DP_COM_CONF_CSC_DEF_MASK; + reg &= ~DP_COM_CONF_CSC_DEF_MASK; + if (csc == DP_COM_CONF_CSC_DEF_BOTH || csc == DP_COM_CONF_CSC_DEF_BG) + reg |= DP_COM_CONF_CSC_DEF_BG; reg &= ~DP_COM_CONF_FG_EN; writel(reg, flow->base + DP_COM_CONF); @@ -350,6 +354,8 @@ int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, unsigned long base) mutex_init(&priv->mutex); for (i = 0; i < IPUV3_NUM_FLOWS; i++) { + priv->flow[i].background.in_cs = IPUV3_COLORSPACE_UNKNOWN; + priv->flow[i].foreground.in_cs = IPUV3_COLORSPACE_UNKNOWN; priv->flow[i].foreground.foreground = true; priv->flow[i].base = priv->base + ipu_dp_flow_base[i]; priv->flow[i].priv = priv; diff --git a/drivers/gpu/msm/adreno_snapshot.c b/drivers/gpu/msm/adreno_snapshot.c index 6392ec1f069b..3cf601f3b5b8 100644 --- a/drivers/gpu/msm/adreno_snapshot.c +++ b/drivers/gpu/msm/adreno_snapshot.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2019 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 @@ -70,6 +70,19 @@ void kgsl_snapshot_push_object(struct kgsl_process_private *process, for (index = 0; index < objbufptr; index++) { if (objbuf[index].gpuaddr == gpuaddr && objbuf[index].entry->priv == process) { + /* + * Check if newly requested size is within the + * allocated range or not, otherwise continue + * with previous size. + */ + if (!kgsl_gpuaddr_in_memdesc( + &objbuf[index].entry->memdesc, + gpuaddr, dwords << 2)) { + KGSL_CORE_ERR( + "snapshot: IB 0x%016llx size is not within the memdesc range\n", + gpuaddr); + return; + } objbuf[index].size = max_t(uint64_t, objbuf[index].size, diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index b1b0b69d55ba..c916822036c1 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2019, 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 @@ -61,7 +61,7 @@ static const char * const clocks[] = { "iref_clk" }; -static unsigned int ib_votes[KGSL_MAX_BUSLEVELS]; +static unsigned long ib_votes[KGSL_MAX_BUSLEVELS]; static int last_vote_buslevel; static int max_vote_buslevel; @@ -123,7 +123,7 @@ static void _record_pwrevent(struct kgsl_device *device, /** * kgsl_get_bw() - Return latest msm bus IB vote */ -static unsigned int kgsl_get_bw(void) +static unsigned long kgsl_get_bw(void) { return ib_votes[last_vote_buslevel]; } @@ -137,8 +137,9 @@ static unsigned int kgsl_get_bw(void) static void _ab_buslevel_update(struct kgsl_pwrctrl *pwr, unsigned long *ab) { - unsigned int ib = ib_votes[last_vote_buslevel]; - unsigned int max_bw = ib_votes[max_vote_buslevel]; + unsigned long ib = ib_votes[last_vote_buslevel]; + unsigned long max_bw = ib_votes[max_vote_buslevel]; + if (!ab) return; if (ib == 0) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 7f6c13740f4f..fd8cd41e6377 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -200,13 +200,14 @@ static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type) * Add a usage to the temporary parser table. */ -static int hid_add_usage(struct hid_parser *parser, unsigned usage) +static int hid_add_usage(struct hid_parser *parser, unsigned usage, u8 size) { if (parser->local.usage_index >= HID_MAX_USAGES) { hid_err(parser->device, "usage index exceeded\n"); return -1; } parser->local.usage[parser->local.usage_index] = usage; + parser->local.usage_size[parser->local.usage_index] = size; parser->local.collection_index[parser->local.usage_index] = parser->collection_stack_ptr ? parser->collection_stack[parser->collection_stack_ptr - 1] : 0; @@ -463,10 +464,7 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item) return 0; } - if (item->size <= 2) - data = (parser->global.usage_page << 16) + data; - - return hid_add_usage(parser, data); + return hid_add_usage(parser, data, item->size); case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM: @@ -475,9 +473,6 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item) return 0; } - if (item->size <= 2) - data = (parser->global.usage_page << 16) + data; - parser->local.usage_minimum = data; return 0; @@ -488,9 +483,6 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item) return 0; } - if (item->size <= 2) - data = (parser->global.usage_page << 16) + data; - count = data - parser->local.usage_minimum; if (count + parser->local.usage_index >= HID_MAX_USAGES) { /* @@ -510,7 +502,7 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item) } for (n = parser->local.usage_minimum; n <= data; n++) - if (hid_add_usage(parser, n)) { + if (hid_add_usage(parser, n, item->size)) { dbg_hid("hid_add_usage failed\n"); return -1; } @@ -525,6 +517,22 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item) } /* + * Concatenate Usage Pages into Usages where relevant: + * As per specification, 6.2.2.8: "When the parser encounters a main item it + * concatenates the last declared Usage Page with a Usage to form a complete + * usage value." + */ + +static void hid_concatenate_usage_page(struct hid_parser *parser) +{ + int i; + + for (i = 0; i < parser->local.usage_index; i++) + if (parser->local.usage_size[i] <= 2) + parser->local.usage[i] += parser->global.usage_page << 16; +} + +/* * Process a main item. */ @@ -533,6 +541,8 @@ static int hid_parser_main(struct hid_parser *parser, struct hid_item *item) __u32 data; int ret; + hid_concatenate_usage_page(parser); + data = item_udata(item); switch (item->tag) { @@ -746,6 +756,8 @@ static int hid_scan_main(struct hid_parser *parser, struct hid_item *item) __u32 data; int i; + hid_concatenate_usage_page(parser); + data = item_udata(item); switch (item->tag) { diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index d7179dd3c9ef..3cafa1d28fed 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -1058,10 +1058,15 @@ static int hid_debug_rdesc_show(struct seq_file *f, void *p) seq_printf(f, "\n\n"); /* dump parsed data and input mappings */ + if (down_interruptible(&hdev->driver_input_lock)) + return 0; + hid_dump_device(hdev, f); seq_printf(f, "\n"); hid_dump_input_mapping(hdev, f); + up(&hdev->driver_input_lock); + return 0; } diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 8d74e691ac90..ee3c66c02043 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -783,6 +783,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x074: map_key_clear(KEY_BRIGHTNESS_MAX); break; case 0x075: map_key_clear(KEY_BRIGHTNESS_AUTO); break; + case 0x079: map_key_clear(KEY_KBDILLUMUP); break; + case 0x07a: map_key_clear(KEY_KBDILLUMDOWN); break; + case 0x07c: map_key_clear(KEY_KBDILLUMTOGGLE); break; + case 0x082: map_key_clear(KEY_VIDEO_NEXT); break; case 0x083: map_key_clear(KEY_LAST); break; case 0x084: map_key_clear(KEY_ENTER); break; @@ -913,6 +917,8 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x2cb: map_key_clear(KEY_KBDINPUTASSIST_ACCEPT); break; case 0x2cc: map_key_clear(KEY_KBDINPUTASSIST_CANCEL); break; + case 0x29f: map_key_clear(KEY_SCALE); break; + default: map_key_clear(KEY_UNKNOWN); } break; diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 5fd97860aec4..3666e5064d0d 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -414,13 +414,16 @@ static int hidpp_root_get_feature(struct hidpp_device *hidpp, u16 feature, static int hidpp_root_get_protocol_version(struct hidpp_device *hidpp) { + const u8 ping_byte = 0x5a; + u8 ping_data[3] = { 0, 0, ping_byte }; struct hidpp_report response; int ret; - ret = hidpp_send_fap_command_sync(hidpp, + ret = hidpp_send_rap_command_sync(hidpp, + REPORT_ID_HIDPP_SHORT, HIDPP_PAGE_ROOT_IDX, CMD_ROOT_GET_PROTOCOL_VERSION, - NULL, 0, &response); + ping_data, sizeof(ping_data), &response); if (ret == HIDPP_ERROR_INVALID_SUBID) { hidpp->protocol_major = 1; @@ -440,8 +443,14 @@ static int hidpp_root_get_protocol_version(struct hidpp_device *hidpp) if (ret) return ret; - hidpp->protocol_major = response.fap.params[0]; - hidpp->protocol_minor = response.fap.params[1]; + if (response.rap.params[2] != ping_byte) { + hid_err(hidpp->hid_dev, "%s: ping mismatch 0x%02x != 0x%02x\n", + __func__, response.rap.params[2], ping_byte); + return -EPROTO; + } + + hidpp->protocol_major = response.rap.params[0]; + hidpp->protocol_minor = response.rap.params[1]; return ret; } diff --git a/drivers/hwmon/f71805f.c b/drivers/hwmon/f71805f.c index facd05cda26d..e8c089886427 100644 --- a/drivers/hwmon/f71805f.c +++ b/drivers/hwmon/f71805f.c @@ -96,17 +96,23 @@ superio_select(int base, int ld) outb(ld, base + 1); } -static inline void +static inline int superio_enter(int base) { + if (!request_muxed_region(base, 2, DRVNAME)) + return -EBUSY; + outb(0x87, base); outb(0x87, base); + + return 0; } static inline void superio_exit(int base) { outb(0xaa, base); + release_region(base, 2); } /* @@ -1561,7 +1567,7 @@ exit: static int __init f71805f_find(int sioaddr, unsigned short *address, struct f71805f_sio_data *sio_data) { - int err = -ENODEV; + int err; u16 devid; static const char * const names[] = { @@ -1569,8 +1575,11 @@ static int __init f71805f_find(int sioaddr, unsigned short *address, "F71872F/FG or F71806F/FG", }; - superio_enter(sioaddr); + err = superio_enter(sioaddr); + if (err) + return err; + err = -ENODEV; devid = superio_inw(sioaddr, SIO_REG_MANID); if (devid != SIO_FINTEK_ID) goto exit; diff --git a/drivers/hwmon/pc87427.c b/drivers/hwmon/pc87427.c index cb9fdd37bd0d..2b5b8c3de8fc 100644 --- a/drivers/hwmon/pc87427.c +++ b/drivers/hwmon/pc87427.c @@ -106,6 +106,13 @@ static const char *logdev_str[2] = { DRVNAME " FMC", DRVNAME " HMC" }; #define LD_IN 1 #define LD_TEMP 1 +static inline int superio_enter(int sioaddr) +{ + if (!request_muxed_region(sioaddr, 2, DRVNAME)) + return -EBUSY; + return 0; +} + static inline void superio_outb(int sioaddr, int reg, int val) { outb(reg, sioaddr); @@ -122,6 +129,7 @@ static inline void superio_exit(int sioaddr) { outb(0x02, sioaddr); outb(0x02, sioaddr + 1); + release_region(sioaddr, 2); } /* @@ -1220,7 +1228,11 @@ static int __init pc87427_find(int sioaddr, struct pc87427_sio_data *sio_data) { u16 val; u8 cfg, cfg_b; - int i, err = 0; + int i, err; + + err = superio_enter(sioaddr); + if (err) + return err; /* Identify device */ val = force_id ? force_id : superio_inb(sioaddr, SIOREG_DEVID); diff --git a/drivers/hwmon/smsc47b397.c b/drivers/hwmon/smsc47b397.c index 6bd200756560..cbdb5c4991ae 100644 --- a/drivers/hwmon/smsc47b397.c +++ b/drivers/hwmon/smsc47b397.c @@ -72,14 +72,19 @@ static inline void superio_select(int ld) superio_outb(0x07, ld); } -static inline void superio_enter(void) +static inline int superio_enter(void) { + if (!request_muxed_region(REG, 2, DRVNAME)) + return -EBUSY; + outb(0x55, REG); + return 0; } static inline void superio_exit(void) { outb(0xAA, REG); + release_region(REG, 2); } #define SUPERIO_REG_DEVID 0x20 @@ -300,8 +305,12 @@ static int __init smsc47b397_find(void) u8 id, rev; char *name; unsigned short addr; + int err; + + err = superio_enter(); + if (err) + return err; - superio_enter(); id = force_id ? force_id : superio_inb(SUPERIO_REG_DEVID); switch (id) { diff --git a/drivers/hwmon/smsc47m1.c b/drivers/hwmon/smsc47m1.c index 5d323186d2c1..d24df0c50bea 100644 --- a/drivers/hwmon/smsc47m1.c +++ b/drivers/hwmon/smsc47m1.c @@ -73,16 +73,21 @@ superio_inb(int reg) /* logical device for fans is 0x0A */ #define superio_select() superio_outb(0x07, 0x0A) -static inline void +static inline int superio_enter(void) { + if (!request_muxed_region(REG, 2, DRVNAME)) + return -EBUSY; + outb(0x55, REG); + return 0; } static inline void superio_exit(void) { outb(0xAA, REG); + release_region(REG, 2); } #define SUPERIO_REG_ACT 0x30 @@ -531,8 +536,12 @@ static int __init smsc47m1_find(struct smsc47m1_sio_data *sio_data) { u8 val; unsigned short addr; + int err; + + err = superio_enter(); + if (err) + return err; - superio_enter(); val = force_id ? force_id : superio_inb(SUPERIO_REG_DEVID); /* @@ -608,13 +617,14 @@ static int __init smsc47m1_find(struct smsc47m1_sio_data *sio_data) static void smsc47m1_restore(const struct smsc47m1_sio_data *sio_data) { if ((sio_data->activate & 0x01) == 0) { - superio_enter(); - superio_select(); - - pr_info("Disabling device\n"); - superio_outb(SUPERIO_REG_ACT, sio_data->activate); - - superio_exit(); + if (!superio_enter()) { + superio_select(); + pr_info("Disabling device\n"); + superio_outb(SUPERIO_REG_ACT, sio_data->activate); + superio_exit(); + } else { + pr_warn("Failed to disable device\n"); + } } } diff --git a/drivers/hwmon/vt1211.c b/drivers/hwmon/vt1211.c index 3a6bfa51cb94..95d5e8ec8b7f 100644 --- a/drivers/hwmon/vt1211.c +++ b/drivers/hwmon/vt1211.c @@ -226,15 +226,21 @@ static inline void superio_select(int sio_cip, int ldn) outb(ldn, sio_cip + 1); } -static inline void superio_enter(int sio_cip) +static inline int superio_enter(int sio_cip) { + if (!request_muxed_region(sio_cip, 2, DRVNAME)) + return -EBUSY; + outb(0x87, sio_cip); outb(0x87, sio_cip); + + return 0; } static inline void superio_exit(int sio_cip) { outb(0xaa, sio_cip); + release_region(sio_cip, 2); } /* --------------------------------------------------------------------- @@ -1282,11 +1288,14 @@ EXIT: static int __init vt1211_find(int sio_cip, unsigned short *address) { - int err = -ENODEV; + int err; int devid; - superio_enter(sio_cip); + err = superio_enter(sio_cip); + if (err) + return err; + err = -ENODEV; devid = force_id ? force_id : superio_inb(sio_cip, SIO_VT1211_DEVID); if (devid != SIO_VT1211_ID) goto EXIT; diff --git a/drivers/hwtracing/intel_th/gth.c b/drivers/hwtracing/intel_th/gth.c index eb43943cdf07..189eb6269971 100644 --- a/drivers/hwtracing/intel_th/gth.c +++ b/drivers/hwtracing/intel_th/gth.c @@ -597,7 +597,7 @@ static void intel_th_gth_unassign(struct intel_th_device *thdev, othdev->output.port = -1; othdev->output.active = false; gth->output[port].output = NULL; - for (master = 0; master < TH_CONFIGURABLE_MASTERS; master++) + for (master = 0; master <= TH_CONFIGURABLE_MASTERS; master++) if (gth->master[master] == port) gth->master[master] = -1; spin_unlock(>h->gth_lock); diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c index 9d9e47eb0842..7d5c53a1abe4 100644 --- a/drivers/hwtracing/intel_th/msu.c +++ b/drivers/hwtracing/intel_th/msu.c @@ -90,6 +90,7 @@ struct msc_iter { * @reg_base: register window base address * @thdev: intel_th_device pointer * @win_list: list of windows in multiblock mode + * @single_sgt: single mode buffer * @nr_pages: total number of pages allocated for this buffer * @single_sz: amount of data in single mode * @single_wrap: single mode wrap occurred @@ -110,6 +111,7 @@ struct msc { struct intel_th_device *thdev; struct list_head win_list; + struct sg_table single_sgt; unsigned long nr_pages; unsigned long single_sz; unsigned int single_wrap : 1; @@ -610,22 +612,45 @@ static void intel_th_msc_deactivate(struct intel_th_device *thdev) */ static int msc_buffer_contig_alloc(struct msc *msc, unsigned long size) { + unsigned long nr_pages = size >> PAGE_SHIFT; unsigned int order = get_order(size); struct page *page; + int ret; if (!size) return 0; + ret = sg_alloc_table(&msc->single_sgt, 1, GFP_KERNEL); + if (ret) + goto err_out; + + ret = -ENOMEM; page = alloc_pages(GFP_KERNEL | __GFP_ZERO, order); if (!page) - return -ENOMEM; + goto err_free_sgt; split_page(page, order); - msc->nr_pages = size >> PAGE_SHIFT; + sg_set_buf(msc->single_sgt.sgl, page_address(page), size); + + ret = dma_map_sg(msc_dev(msc)->parent->parent, msc->single_sgt.sgl, 1, + DMA_FROM_DEVICE); + if (ret < 0) + goto err_free_pages; + + msc->nr_pages = nr_pages; msc->base = page_address(page); - msc->base_addr = page_to_phys(page); + msc->base_addr = sg_dma_address(msc->single_sgt.sgl); return 0; + +err_free_pages: + __free_pages(page, order); + +err_free_sgt: + sg_free_table(&msc->single_sgt); + +err_out: + return ret; } /** @@ -636,6 +661,10 @@ static void msc_buffer_contig_free(struct msc *msc) { unsigned long off; + dma_unmap_sg(msc_dev(msc)->parent->parent, msc->single_sgt.sgl, + 1, DMA_FROM_DEVICE); + sg_free_table(&msc->single_sgt); + for (off = 0; off < msc->nr_pages << PAGE_SHIFT; off += PAGE_SIZE) { struct page *page = virt_to_page(msc->base + off); diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index b862b8e3f579..0f41fef8deac 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -225,8 +225,8 @@ stm_output_disclaim(struct stm_device *stm, struct stm_output *output) bitmap_release_region(&master->chan_map[0], output->channel, ilog2(output->nr_chans)); - output->nr_chans = 0; master->nr_free += output->nr_chans; + output->nr_chans = 0; } /* diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 5b57c7edbc72..151e9c039957 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -1209,5 +1209,11 @@ config I2C_MSM_V2 This driver can also be built as a module. If so, the module will be called i2c-msm-v2. +config VIRTIO_I2C + tristate "VIRTIO_I2C" + depends on VIRTIO + help + If you say yes to this option, the virtio i2c will be + supported. endmenu diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 0c9891812aa8..47e5ed02ea7f 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -97,6 +97,7 @@ 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_VIRTIO_I2C) += virtio-i2c.o # External I2C/SMBus adapter drivers obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o diff --git a/drivers/i2c/busses/i2c-acorn.c b/drivers/i2c/busses/i2c-acorn.c index 9d7be5af2bf2..6618db75fa25 100644 --- a/drivers/i2c/busses/i2c-acorn.c +++ b/drivers/i2c/busses/i2c-acorn.c @@ -83,6 +83,7 @@ static struct i2c_algo_bit_data ioc_data = { static struct i2c_adapter ioc_ops = { .nr = 0, + .name = "ioc", .algo_data = &ioc_data, }; diff --git a/drivers/i2c/busses/virtio-i2c.c b/drivers/i2c/busses/virtio-i2c.c new file mode 100644 index 000000000000..674c38aae987 --- /dev/null +++ b/drivers/i2c/busses/virtio-i2c.c @@ -0,0 +1,348 @@ +/* Copyright (c) 2019, 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 <linux/kernel.h> +#include <linux/module.h> +#include <linux/kthread.h> +#include <linux/scatterlist.h> +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/idr.h> +#include <linux/jiffies.h> +#include <linux/sched.h> +#include <linux/wait.h> +#include <linux/spinlock.h> +#include <linux/virtio.h> +#include <uapi/linux/virtio_ids.h> +#include <linux/virtio_config.h> +#include <linux/i2c.h> + +#define I2C_ADAPTER_NR 0x00 + +#define I2C_VIRTIO_RD 0x01 +#define I2C_VIRTIO_WR 0x02 +#define I2C_VIRTIO_RDWR 0x03 + +struct i2c_transfer_head { + u32 type; /* read or write from or to slave */ + u32 addr; /* slave addr */ + u32 length; /* buffer length */ + u32 total_length; /* merge write and read will use this segment */ +}; + +struct i2c_transfer_end { + u32 result; /* return value from backend */ +}; + +struct virtio_i2c_req { + struct i2c_transfer_head head; + char *buf; + struct i2c_transfer_end end; +}; + +/** + * struct virtio_i2c - virtio i2c device + * @adapter: i2c adapter + * @vdev: the virtio device + * @i2c_req: description of the format of transfer data + * @vq: i2c virtqueue + */ +struct virtio_i2c { + struct i2c_adapter adapter; + struct virtio_device *vdev; + struct virtio_i2c_req i2c_req; + struct virtqueue *vq; + wait_queue_head_t inq; +}; + +static int virti2c_transfer(struct virtio_i2c *vi2c, + struct virtio_i2c_req *i2c_req) +{ + struct virtqueue *vq = vi2c->vq; + struct scatterlist outhdr, bufhdr, inhdr, *sgs[3]; + unsigned int num_out = 0, num_in = 0, err, len; + struct virtio_i2c_req *req_handled = NULL; + + /* send the head queue to the backend */ + sg_init_one(&outhdr, &i2c_req->head, sizeof(i2c_req->head)); + sgs[num_out++] = &outhdr; + + /* send the buffer queue to the backend */ + sg_init_one(&bufhdr, i2c_req->buf, + (i2c_req->head.type == I2C_VIRTIO_RDWR) ? + i2c_req->head.total_length : i2c_req->head.length); + if (i2c_req->head.type & I2C_VIRTIO_WR) + sgs[num_out++] = &bufhdr; + else + sgs[num_out + num_in++] = &bufhdr; + + /* send the result queue to the backend */ + sg_init_one(&inhdr, &i2c_req->end, sizeof(i2c_req->end)); + sgs[num_out + num_in++] = &inhdr; + + /* call the virtqueue function */ + err = virtqueue_add_sgs(vq, sgs, num_out, num_in, i2c_req, GFP_KERNEL); + if (err) + goto req_exit; + + /* Tell Host to go! */ + err = virtqueue_kick(vq); + + wait_event(vi2c->inq, + (req_handled = virtqueue_get_buf(vq, &len))); + + if (i2c_req->head.type == I2C_VIRTIO_RDWR) { + if (i2c_req->end.result == + i2c_req->head.total_length - i2c_req->head.length) + err = 0; + else + err = -EINVAL; + } else { + if (i2c_req->end.result == i2c_req->head.length) + err = 0; + else + err = -EINVAL; + } +req_exit: + return err; +} + +/* prepare the transfer req */ +static int virti2c_transfer_prepare(struct i2c_msg *msg_1, + struct i2c_msg *msg_2, struct virtio_i2c_req *i2c_req) +{ + if (IS_ERR_OR_NULL(msg_1) || !msg_1->len || + IS_ERR_OR_NULL(msg_1->buf)) + return -EINVAL; + + i2c_req->head.addr = msg_1->addr; + i2c_req->head.length = msg_1->len; + + if (IS_ERR_OR_NULL(msg_2)) { + i2c_req->buf = kzalloc(msg_1->len, GFP_KERNEL); + if (!i2c_req->buf) + return -ENOMEM; + + if (msg_1->flags & I2C_M_RD) { + i2c_req->head.type = I2C_VIRTIO_RD; + } else { + i2c_req->head.type = I2C_VIRTIO_WR; + memcpy(i2c_req->buf, msg_1->buf, msg_1->len); + } + + } else { + if (!msg_2->len || IS_ERR_OR_NULL(msg_2->buf)) + return -EINVAL; + + i2c_req->buf = kzalloc((msg_1->len + msg_2->len), GFP_KERNEL); + if (!i2c_req->buf) + return -ENOMEM; + + i2c_req->head.type = I2C_VIRTIO_RDWR; + i2c_req->head.total_length = msg_1->len + msg_2->len; + + memcpy(i2c_req->buf, msg_1->buf, msg_1->len); + } + + return 0; +} + +static void virti2c_transfer_end(struct virtio_i2c_req *req, + struct i2c_msg *msg) +{ + if (req->head.type == I2C_VIRTIO_RDWR) + memcpy(msg->buf, req->buf + req->head.length, msg->len); + else if (req->head.type == I2C_VIRTIO_RD) + memcpy(msg->buf, req->buf, msg->len); + kfree(req->buf); + req->buf = NULL; +} + +static int virtio_i2c_master_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + int i, ret; + struct virtio_i2c *vi2c = i2c_get_adapdata(adap); + struct virtio_i2c_req *i2c_req = &vi2c->i2c_req; + + if (num < 1) { + dev_err(&vi2c->vdev->dev, + "error on number of msgs(%d) received\n", num); + return -EINVAL; + } + + if (IS_ERR_OR_NULL(msgs)) { + dev_err(&vi2c->vdev->dev, " error no msgs Accessing invalid pointer location\n"); + return PTR_ERR(msgs); + } + + for (i = 0; i < num; i++) { + + if (msgs[i].flags & I2C_M_RD) { + /* read the data from slave to master*/ + ret = virti2c_transfer_prepare(&msgs[i], NULL, i2c_req); + + } else if ((i + 1 < num) && (msgs[i + 1].flags & I2C_M_RD) && + (msgs[i].addr == msgs[i + 1].addr)) { + /* write then read from same address*/ + ret = virti2c_transfer_prepare(&msgs[i], + &msgs[i+1], i2c_req); + i += 1; + + } else { + /* write the data to slave */ + ret = virti2c_transfer_prepare(&msgs[i], NULL, i2c_req); + } + + if (ret) + goto err; + ret = virti2c_transfer(vi2c, i2c_req); + virti2c_transfer_end(i2c_req, &msgs[i]); + if (ret) + goto err; + } + return num; +err: + return ret; +} + +static u32 virtio_i2c_functionality(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static struct i2c_algorithm virtio_i2c_algorithm = { + .master_xfer = virtio_i2c_master_xfer, + .functionality = virtio_i2c_functionality, +}; + +/* virtqueue incoming data interrupt IRQ */ +static void virti2c_vq_isr(struct virtqueue *vq) +{ + struct virtio_i2c *vi2c = vq->vdev->priv; + + wake_up(&vi2c->inq); +} + +static int virti2c_init_vqs(struct virtio_i2c *vi2c) +{ + struct virtqueue *vqs[1]; + vq_callback_t *cbs[] = { virti2c_vq_isr }; + static const char * const names[] = { "virti2c_vq_isr" }; + int err; + + err = vi2c->vdev->config->find_vqs(vi2c->vdev, 1, vqs, cbs, names); + if (err) + return err; + vi2c->vq = vqs[0]; + + return 0; +} + +static void virti2c_del_vqs(struct virtio_i2c *vi2c) +{ + vi2c->vdev->config->del_vqs(vi2c->vdev); +} + +static int virti2c_init_hw(struct virtio_device *vdev, + struct virtio_i2c *vi2c) +{ + int err; + + i2c_set_adapdata(&vi2c->adapter, vi2c); + vi2c->adapter.algo = &virtio_i2c_algorithm; + + vi2c->adapter.owner = THIS_MODULE; + vi2c->adapter.dev.parent = &vdev->dev; + vi2c->adapter.dev.of_node = vdev->dev.parent->of_node; + + /* read virtio i2c config info */ + vi2c->adapter.nr = virtio_cread32(vdev, I2C_ADAPTER_NR); + snprintf(vi2c->adapter.name, sizeof(vi2c->adapter.name), + "virtio_i2c_%d", vi2c->adapter.nr); + + err = i2c_add_numbered_adapter(&vi2c->adapter); + if (err) + return err; + return 0; +} + +static int virti2c_probe(struct virtio_device *vdev) +{ + struct virtio_i2c *vi2c; + int err = 0; + + if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) + return -ENODEV; + + vi2c = kzalloc(sizeof(*vi2c), GFP_KERNEL); + if (!vi2c) + return -ENOMEM; + + vi2c->vdev = vdev; + vdev->priv = vi2c; + init_waitqueue_head(&vi2c->inq); + + err = virti2c_init_vqs(vi2c); + if (err) + goto err_init_vq; + + err = virti2c_init_hw(vdev, vi2c); + if (err) + goto err_init_hw; + + virtio_device_ready(vdev); + + virtqueue_enable_cb(vi2c->vq); + return 0; + +err_init_hw: + virti2c_del_vqs(vi2c); +err_init_vq: + kfree(vi2c); + return err; +} +static void virti2c_remove(struct virtio_device *vdev) +{ + struct virtio_i2c *vi2c = vdev->priv; + + i2c_del_adapter(&vi2c->adapter); + vdev->config->reset(vdev); + virti2c_del_vqs(vi2c); + kfree(vi2c); +} + +static unsigned int features[] = { + /* none */ +}; +static struct virtio_device_id id_table[] = { + { VIRTIO_ID_I2C, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + +static struct virtio_driver virtio_i2c_driver = { + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .feature_table = features, + .feature_table_size = ARRAY_SIZE(features), + .id_table = id_table, + .probe = virti2c_probe, + .remove = virti2c_remove, +}; + +module_virtio_driver(virtio_i2c_driver); +MODULE_DEVICE_TABLE(virtio, id_table); + +MODULE_DESCRIPTION("Virtio i2c frontend driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index 57e3790c87b1..e56b774e7cf9 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c @@ -295,6 +295,7 @@ static noinline int i2cdev_ioctl_rdwr(struct i2c_client *client, rdwr_pa[i].buf[0] < 1 || rdwr_pa[i].len < rdwr_pa[i].buf[0] + I2C_SMBUS_BLOCK_MAX) { + i++; res = -EINVAL; break; } diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index a1d072ecb717..30f200ad6b97 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -62,7 +62,7 @@ int ad_sd_write_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg, struct spi_transfer t = { .tx_buf = data, .len = size + 1, - .cs_change = sigma_delta->bus_locked, + .cs_change = sigma_delta->keep_cs_asserted, }; struct spi_message m; int ret; @@ -217,6 +217,7 @@ static int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta, spi_bus_lock(sigma_delta->spi->master); sigma_delta->bus_locked = true; + sigma_delta->keep_cs_asserted = true; reinit_completion(&sigma_delta->completion); ret = ad_sigma_delta_set_mode(sigma_delta, mode); @@ -234,9 +235,10 @@ static int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta, ret = 0; } out: + sigma_delta->keep_cs_asserted = false; + ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); sigma_delta->bus_locked = false; spi_bus_unlock(sigma_delta->spi->master); - ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); return ret; } @@ -288,6 +290,7 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev, spi_bus_lock(sigma_delta->spi->master); sigma_delta->bus_locked = true; + sigma_delta->keep_cs_asserted = true; reinit_completion(&sigma_delta->completion); ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_SINGLE); @@ -297,9 +300,6 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev, ret = wait_for_completion_interruptible_timeout( &sigma_delta->completion, HZ); - sigma_delta->bus_locked = false; - spi_bus_unlock(sigma_delta->spi->master); - if (ret == 0) ret = -EIO; if (ret < 0) @@ -315,7 +315,10 @@ out: sigma_delta->irq_dis = true; } + sigma_delta->keep_cs_asserted = false; ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); + sigma_delta->bus_locked = false; + spi_bus_unlock(sigma_delta->spi->master); mutex_unlock(&indio_dev->mlock); if (ret) @@ -352,6 +355,8 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) spi_bus_lock(sigma_delta->spi->master); sigma_delta->bus_locked = true; + sigma_delta->keep_cs_asserted = true; + ret = ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_CONTINUOUS); if (ret) goto err_unlock; @@ -380,6 +385,7 @@ static int ad_sd_buffer_postdisable(struct iio_dev *indio_dev) sigma_delta->irq_dis = true; } + sigma_delta->keep_cs_asserted = false; ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); sigma_delta->bus_locked = false; diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c index 475c5a74f2d1..6398e86a272b 100644 --- a/drivers/iio/adc/xilinx-xadc-core.c +++ b/drivers/iio/adc/xilinx-xadc-core.c @@ -1299,7 +1299,7 @@ static int xadc_remove(struct platform_device *pdev) } free_irq(irq, indio_dev); clk_disable_unprepare(xadc->clk); - cancel_delayed_work(&xadc->zynq_unmask_work); + cancel_delayed_work_sync(&xadc->zynq_unmask_work); kfree(xadc->data); kfree(indio_dev->channels); diff --git a/drivers/iio/common/ssp_sensors/ssp_iio.c b/drivers/iio/common/ssp_sensors/ssp_iio.c index a3ae165f8d9f..16180e6321bd 100644 --- a/drivers/iio/common/ssp_sensors/ssp_iio.c +++ b/drivers/iio/common/ssp_sensors/ssp_iio.c @@ -80,7 +80,7 @@ int ssp_common_process_data(struct iio_dev *indio_dev, void *buf, unsigned int len, int64_t timestamp) { __le32 time; - int64_t calculated_time; + int64_t calculated_time = 0; struct ssp_sensor_data *spd = iio_priv(indio_dev); if (indio_dev->scan_bytes == 0) diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index c9cffced00ca..54fd4d81a3f1 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -360,6 +360,8 @@ static struct sk_buff *get_skb(struct sk_buff *skb, int len, gfp_t gfp) skb_reset_transport_header(skb); } else { skb = alloc_skb(len, gfp); + if (!skb) + return NULL; } t4_set_arp_err_handler(skb, NULL, NULL); return skb; diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index 67c4c73343d4..6968154a073e 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -1042,6 +1042,8 @@ static void mlx4_ib_disassociate_ucontext(struct ib_ucontext *ibcontext) * mlx4_ib_vma_close(). */ down_write(&owning_mm->mmap_sem); + if (!mmget_still_valid(owning_mm)) + goto skip_mm; for (i = 0; i < HW_BAR_COUNT; i++) { vma = context->hw_bar_info[i].vma; if (!vma) @@ -1061,6 +1063,7 @@ static void mlx4_ib_disassociate_ucontext(struct ib_ucontext *ibcontext) context->hw_bar_info[i].vma->vm_ops = NULL; } +skip_mm: up_write(&owning_mm->mmap_sem); mmput(owning_mm); put_task_struct(owning_process); diff --git a/drivers/input/keyboard/snvs_pwrkey.c b/drivers/input/keyboard/snvs_pwrkey.c index 9adf13a5864a..57143365e945 100644 --- a/drivers/input/keyboard/snvs_pwrkey.c +++ b/drivers/input/keyboard/snvs_pwrkey.c @@ -156,6 +156,9 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev) return error; } + pdata->input = input; + platform_set_drvdata(pdev, pdata); + error = devm_request_irq(&pdev->dev, pdata->irq, imx_snvs_pwrkey_interrupt, 0, pdev->name, pdev); @@ -172,9 +175,6 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev) return error; } - pdata->input = input; - platform_set_drvdata(pdev, pdata); - device_init_wakeup(&pdev->dev, pdata->wakeup); return 0; diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index 94f1bf772ec9..db85cc5791dc 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c @@ -295,7 +295,7 @@ static void iommu_write_l2(struct amd_iommu *iommu, u8 address, u32 val) static void iommu_set_exclusion_range(struct amd_iommu *iommu) { u64 start = iommu->exclusion_start & PAGE_MASK; - u64 limit = (start + iommu->exclusion_length) & PAGE_MASK; + u64 limit = (start + iommu->exclusion_length - 1) & PAGE_MASK; u64 entry; if (!iommu->exclusion_start) diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 3e97c4b2ebed..b965561a4162 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -3983,9 +3983,7 @@ static void __init init_no_remapping_devices(void) /* This IOMMU has *only* gfx devices. Either bypass it or set the gfx_mapped flag, as appropriate */ - if (dmar_map_gfx) { - intel_iommu_gfx_mapped = 1; - } else { + if (!dmar_map_gfx) { drhd->ignored = 1; for_each_active_dev_scope(drhd->devices, drhd->devices_cnt, i, dev) @@ -4694,6 +4692,9 @@ int __init intel_iommu_init(void) goto out_free_reserved_range; } + if (dmar_map_gfx) + intel_iommu_gfx_mapped = 1; + init_no_remapping_devices(); ret = init_dmars(); diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index 9305964250ac..c4eb293b1524 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -91,7 +91,6 @@ static inline u32 smmu_readl(struct tegra_smmu *smmu, unsigned long offset) #define SMMU_TLB_FLUSH_VA_MATCH_ALL (0 << 0) #define SMMU_TLB_FLUSH_VA_MATCH_SECTION (2 << 0) #define SMMU_TLB_FLUSH_VA_MATCH_GROUP (3 << 0) -#define SMMU_TLB_FLUSH_ASID(x) (((x) & 0x7f) << 24) #define SMMU_TLB_FLUSH_VA_SECTION(addr) ((((addr) & 0xffc00000) >> 12) | \ SMMU_TLB_FLUSH_VA_MATCH_SECTION) #define SMMU_TLB_FLUSH_VA_GROUP(addr) ((((addr) & 0xffffc000) >> 12) | \ @@ -194,8 +193,12 @@ static inline void smmu_flush_tlb_asid(struct tegra_smmu *smmu, { u32 value; - value = SMMU_TLB_FLUSH_ASID_MATCH | SMMU_TLB_FLUSH_ASID(asid) | - SMMU_TLB_FLUSH_VA_MATCH_ALL; + if (smmu->soc->num_asids == 4) + value = (asid & 0x3) << 29; + else + value = (asid & 0x7f) << 24; + + value |= SMMU_TLB_FLUSH_ASID_MATCH | SMMU_TLB_FLUSH_VA_MATCH_ALL; smmu_writel(smmu, value, SMMU_TLB_FLUSH); } @@ -205,8 +208,12 @@ static inline void smmu_flush_tlb_section(struct tegra_smmu *smmu, { u32 value; - value = SMMU_TLB_FLUSH_ASID_MATCH | SMMU_TLB_FLUSH_ASID(asid) | - SMMU_TLB_FLUSH_VA_SECTION(iova); + if (smmu->soc->num_asids == 4) + value = (asid & 0x3) << 29; + else + value = (asid & 0x7f) << 24; + + value |= SMMU_TLB_FLUSH_ASID_MATCH | SMMU_TLB_FLUSH_VA_SECTION(iova); smmu_writel(smmu, value, SMMU_TLB_FLUSH); } @@ -216,8 +223,12 @@ static inline void smmu_flush_tlb_group(struct tegra_smmu *smmu, { u32 value; - value = SMMU_TLB_FLUSH_ASID_MATCH | SMMU_TLB_FLUSH_ASID(asid) | - SMMU_TLB_FLUSH_VA_GROUP(iova); + if (smmu->soc->num_asids == 4) + value = (asid & 0x3) << 29; + else + value = (asid & 0x7f) << 24; + + value |= SMMU_TLB_FLUSH_ASID_MATCH | SMMU_TLB_FLUSH_VA_GROUP(iova); smmu_writel(smmu, value, SMMU_TLB_FLUSH); } diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c index 0d29b5a6356d..8cbb75d09a1d 100644 --- a/drivers/isdn/mISDN/socket.c +++ b/drivers/isdn/mISDN/socket.c @@ -394,7 +394,7 @@ data_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) memcpy(di.channelmap, dev->channelmap, sizeof(di.channelmap)); di.nrbchan = dev->nrbchan; - strcpy(di.name, dev_name(&dev->dev)); + strscpy(di.name, dev_name(&dev->dev), sizeof(di.name)); if (copy_to_user((void __user *)arg, &di, sizeof(di))) err = -EFAULT; } else @@ -678,7 +678,7 @@ base_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) memcpy(di.channelmap, dev->channelmap, sizeof(di.channelmap)); di.nrbchan = dev->nrbchan; - strcpy(di.name, dev_name(&dev->dev)); + strscpy(di.name, dev_name(&dev->dev), sizeof(di.name)); if (copy_to_user((void __user *)arg, &di, sizeof(di))) err = -EFAULT; } else @@ -692,6 +692,7 @@ base_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) err = -EFAULT; break; } + dn.name[sizeof(dn.name) - 1] = '\0'; dev = get_mdevice(dn.id); if (dev) err = device_rename(&dev->dev, dn.name); diff --git a/drivers/md/bcache/alloc.c b/drivers/md/bcache/alloc.c index 16c3390e5d9f..d82ae445c9ee 100644 --- a/drivers/md/bcache/alloc.c +++ b/drivers/md/bcache/alloc.c @@ -324,10 +324,11 @@ static int bch_allocator_thread(void *arg) * possibly issue discards to them, then we add the bucket to * the free list: */ - while (!fifo_empty(&ca->free_inc)) { + while (1) { long bucket; - fifo_pop(&ca->free_inc, bucket); + if (!fifo_pop(&ca->free_inc, bucket)) + break; if (ca->discard) { mutex_unlock(&ca->set->bucket_lock); diff --git a/drivers/md/bcache/bset.c b/drivers/md/bcache/bset.c index 646fe85261c1..158eae17031c 100644 --- a/drivers/md/bcache/bset.c +++ b/drivers/md/bcache/bset.c @@ -823,12 +823,22 @@ unsigned bch_btree_insert_key(struct btree_keys *b, struct bkey *k, struct bset *i = bset_tree_last(b)->data; struct bkey *m, *prev = NULL; struct btree_iter iter; + struct bkey preceding_key_on_stack = ZERO_KEY; + struct bkey *preceding_key_p = &preceding_key_on_stack; BUG_ON(b->ops->is_extents && !KEY_SIZE(k)); - m = bch_btree_iter_init(b, &iter, b->ops->is_extents - ? PRECEDING_KEY(&START_KEY(k)) - : PRECEDING_KEY(k)); + /* + * If k has preceding key, preceding_key_p will be set to address + * of k's preceding key; otherwise preceding_key_p will be set + * to NULL inside preceding_key(). + */ + if (b->ops->is_extents) + preceding_key(&START_KEY(k), &preceding_key_p); + else + preceding_key(k, &preceding_key_p); + + m = bch_btree_iter_init(b, &iter, preceding_key_p); if (b->ops->insert_fixup(b, k, &iter, replace_key)) return status; diff --git a/drivers/md/bcache/bset.h b/drivers/md/bcache/bset.h index ae964624efb2..b935839ab79c 100644 --- a/drivers/md/bcache/bset.h +++ b/drivers/md/bcache/bset.h @@ -417,20 +417,26 @@ static inline bool bch_cut_back(const struct bkey *where, struct bkey *k) return __bch_cut_back(where, k); } -#define PRECEDING_KEY(_k) \ -({ \ - struct bkey *_ret = NULL; \ - \ - if (KEY_INODE(_k) || KEY_OFFSET(_k)) { \ - _ret = &KEY(KEY_INODE(_k), KEY_OFFSET(_k), 0); \ - \ - if (!_ret->low) \ - _ret->high--; \ - _ret->low--; \ - } \ - \ - _ret; \ -}) +/* + * Pointer '*preceding_key_p' points to a memory object to store preceding + * key of k. If the preceding key does not exist, set '*preceding_key_p' to + * NULL. So the caller of preceding_key() needs to take care of memory + * which '*preceding_key_p' pointed to before calling preceding_key(). + * Currently the only caller of preceding_key() is bch_btree_insert_key(), + * and it points to an on-stack variable, so the memory release is handled + * by stackframe itself. + */ +static inline void preceding_key(struct bkey *k, struct bkey **preceding_key_p) +{ + if (KEY_INODE(k) || KEY_OFFSET(k)) { + (**preceding_key_p) = KEY(KEY_INODE(k), KEY_OFFSET(k), 0); + if (!(*preceding_key_p)->low) + (*preceding_key_p)->high--; + (*preceding_key_p)->low--; + } else { + (*preceding_key_p) = NULL; + } +} static inline bool bch_ptr_invalid(struct btree_keys *b, const struct bkey *k) { diff --git a/drivers/md/bcache/journal.c b/drivers/md/bcache/journal.c index 6ed066a0e7c0..6f9db98f2dfd 100644 --- a/drivers/md/bcache/journal.c +++ b/drivers/md/bcache/journal.c @@ -309,6 +309,18 @@ void bch_journal_mark(struct cache_set *c, struct list_head *list) } } +bool is_discard_enabled(struct cache_set *s) +{ + struct cache *ca; + unsigned int i; + + for_each_cache(ca, s, i) + if (ca->discard) + return true; + + return false; +} + int bch_journal_replay(struct cache_set *s, struct list_head *list) { int ret = 0, keys = 0, entries = 0; @@ -322,9 +334,17 @@ int bch_journal_replay(struct cache_set *s, struct list_head *list) list_for_each_entry(i, list, list) { BUG_ON(i->pin && atomic_read(i->pin) != 1); - cache_set_err_on(n != i->j.seq, s, -"bcache: journal entries %llu-%llu missing! (replaying %llu-%llu)", - n, i->j.seq - 1, start, end); + if (n != i->j.seq) { + if (n == start && is_discard_enabled(s)) + pr_info("bcache: journal entries %llu-%llu may be discarded! (replaying %llu-%llu)", + n, i->j.seq - 1, start, end); + else { + pr_err("bcache: journal entries %llu-%llu missing! (replaying %llu-%llu)", + n, i->j.seq - 1, start, end); + ret = -EIO; + goto err; + } + } for (k = i->j.start; k < bset_bkey_last(&i->j); @@ -513,11 +533,11 @@ static void journal_reclaim(struct cache_set *c) ca->sb.nr_this_dev); } - bkey_init(k); - SET_KEY_PTRS(k, n); - - if (n) + if (n) { + bkey_init(k); + SET_KEY_PTRS(k, n); c->journal.blocks_free = c->sb.bucket_size >> c->block_bits; + } out: if (!journal_full(&c->journal)) __closure_wake_up(&c->journal.wait); @@ -641,6 +661,9 @@ static void journal_write_unlocked(struct closure *cl) ca->journal.seq[ca->journal.cur_idx] = w->data->seq; } + /* If KEY_PTRS(k) == 0, this jset gets lost in air */ + BUG_ON(i == 0); + atomic_dec_bug(&fifo_back(&c->journal.pin)); bch_journal_next(&c->journal); journal_reclaim(c); diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c index 69356e9a3be5..bfbf5dd06ac5 100644 --- a/drivers/md/bcache/super.c +++ b/drivers/md/bcache/super.c @@ -1355,6 +1355,7 @@ static void cache_set_free(struct closure *cl) bch_btree_cache_free(c); bch_journal_free(c); + mutex_lock(&bch_register_lock); for_each_cache(ca, c, i) if (ca) { ca->set = NULL; @@ -1377,7 +1378,6 @@ static void cache_set_free(struct closure *cl) mempool_destroy(c->search); kfree(c->devices); - mutex_lock(&bch_register_lock); list_del(&c->list); mutex_unlock(&bch_register_lock); @@ -1558,7 +1558,7 @@ err: return NULL; } -static void run_cache_set(struct cache_set *c) +static int run_cache_set(struct cache_set *c) { const char *err = "cannot allocate memory"; struct cached_dev *dc, *t; @@ -1650,7 +1650,9 @@ static void run_cache_set(struct cache_set *c) if (j->version < BCACHE_JSET_VERSION_UUID) __uuid_write(c); - bch_journal_replay(c, &journal); + err = "bcache: replay journal failed"; + if (bch_journal_replay(c, &journal)) + goto err; } else { pr_notice("invalidating existing data"); @@ -1718,11 +1720,13 @@ static void run_cache_set(struct cache_set *c) flash_devs_run(c); set_bit(CACHE_SET_RUNNING, &c->flags); - return; + return 0; err: closure_sync(&cl); /* XXX: test this, it's broken */ bch_cache_set_error(c, "%s", err); + + return -EIO; } static bool can_attach_cache(struct cache *ca, struct cache_set *c) @@ -1786,8 +1790,11 @@ found: ca->set->cache[ca->sb.nr_this_dev] = ca; c->cache_by_alloc[c->caches_loaded++] = ca; - if (c->caches_loaded == c->sb.nr_in_set) - run_cache_set(c); + if (c->caches_loaded == c->sb.nr_in_set) { + err = "failed to run cache set"; + if (run_cache_set(c) < 0) + goto err; + } return NULL; err: diff --git a/drivers/md/dm-delay.c b/drivers/md/dm-delay.c index b4c356a21123..e789a4aae4e7 100644 --- a/drivers/md/dm-delay.c +++ b/drivers/md/dm-delay.c @@ -222,7 +222,8 @@ static void delay_dtr(struct dm_target *ti) { struct delay_c *dc = ti->private; - destroy_workqueue(dc->kdelayd_wq); + if (dc->kdelayd_wq) + destroy_workqueue(dc->kdelayd_wq); dm_put_device(ti, dc->dev_read); diff --git a/drivers/md/md.c b/drivers/md/md.c index e60d855b82e0..68ebab54883e 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -2690,8 +2690,10 @@ state_store(struct md_rdev *rdev, const char *buf, size_t len) err = 0; } } else if (cmd_match(buf, "re-add")) { - if (test_bit(Faulty, &rdev->flags) && (rdev->raid_disk == -1) && - rdev->saved_raid_disk >= 0) { + if (!rdev->mddev->pers) + err = -EINVAL; + else if (test_bit(Faulty, &rdev->flags) && (rdev->raid_disk == -1) && + rdev->saved_raid_disk >= 0) { /* clear_bit is performed _after_ all the devices * have their local Faulty bit cleared. If any writes * happen in the meantime in the local node, they diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index c6133d19ae28..38e62418c6ff 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -3861,7 +3861,7 @@ static void handle_parity_checks6(struct r5conf *conf, struct stripe_head *sh, /* now write out any block on a failed drive, * or P or Q if they were recomputed */ - BUG_ON(s->uptodate < disks - 1); /* We don't need Q to recover */ + dev = NULL; if (s->failed == 2) { dev = &sh->dev[s->failed_num[1]]; s->locked++; @@ -3886,6 +3886,14 @@ static void handle_parity_checks6(struct r5conf *conf, struct stripe_head *sh, set_bit(R5_LOCKED, &dev->flags); set_bit(R5_Wantwrite, &dev->flags); } + if (WARN_ONCE(dev && !test_bit(R5_UPTODATE, &dev->flags), + "%s: disk%td not up to date\n", + mdname(conf->mddev), + dev - (struct r5dev *) &sh->dev)) { + clear_bit(R5_LOCKED, &dev->flags); + clear_bit(R5_Wantwrite, &dev->flags); + s->locked--; + } clear_bit(STRIPE_DEGRADED, &sh->state); set_bit(STRIPE_INSYNC, &sh->state); diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c index d14d075ab1d6..9f0956e739a4 100644 --- a/drivers/media/dvb-frontends/m88ds3103.c +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -309,6 +309,9 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) u16 u16tmp, divide_ratio = 0; u32 tuner_frequency, target_mclk; s32 s32tmp; + static const struct reg_sequence reset_buf[] = { + {0x07, 0x80}, {0x07, 0x00} + }; dev_dbg(&client->dev, "delivery_system=%d modulation=%d frequency=%u symbol_rate=%d inversion=%d pilot=%d rolloff=%d\n", @@ -321,11 +324,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) } /* reset */ - ret = regmap_write(dev->regmap, 0x07, 0x80); - if (ret) - goto err; - - ret = regmap_write(dev->regmap, 0x07, 0x00); + ret = regmap_multi_reg_write(dev->regmap, reset_buf, 2); if (ret) goto err; diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c index 49109f4f5bb4..fadec1d70582 100644 --- a/drivers/media/i2c/ov2659.c +++ b/drivers/media/i2c/ov2659.c @@ -1117,8 +1117,10 @@ static int ov2659_set_fmt(struct v4l2_subdev *sd, if (ov2659_formats[index].code == mf->code) break; - if (index < 0) - return -EINVAL; + if (index < 0) { + index = 0; + mf->code = ov2659_formats[index].code; + } mf->colorspace = V4L2_COLORSPACE_SRGB; mf->code = ov2659_formats[index].code; diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index e1b5dc84c14e..24a0c21a3d8d 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -155,10 +155,10 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); #define REG_GFIX 0x69 /* Fix gain control */ #define REG_DBLV 0x6b /* PLL control an debugging */ -#define DBLV_BYPASS 0x00 /* Bypass PLL */ -#define DBLV_X4 0x01 /* clock x4 */ -#define DBLV_X6 0x10 /* clock x6 */ -#define DBLV_X8 0x11 /* clock x8 */ +#define DBLV_BYPASS 0x0a /* Bypass PLL */ +#define DBLV_X4 0x4a /* clock x4 */ +#define DBLV_X6 0x8a /* clock x6 */ +#define DBLV_X8 0xca /* clock x8 */ #define REG_REG76 0x76 /* OV's name */ #define R76_BLKPCOR 0x80 /* Black pixel correction enable */ @@ -833,7 +833,7 @@ static int ov7675_set_framerate(struct v4l2_subdev *sd, if (ret < 0) return ret; - return ov7670_write(sd, REG_DBLV, DBLV_X4); + return 0; } static void ov7670_get_framerate_legacy(struct v4l2_subdev *sd, @@ -1578,11 +1578,7 @@ static int ov7670_probe(struct i2c_client *client, if (config->clock_speed) info->clock_speed = config->clock_speed; - /* - * It should be allowed for ov7670 too when it is migrated to - * the new frame rate formula. - */ - if (config->pll_bypass && id->driver_data != MODEL_OV7670) + if (config->pll_bypass) info->pll_bypass = true; if (config->pclk_hb_disable) diff --git a/drivers/media/i2c/soc_camera/ov6650.c b/drivers/media/i2c/soc_camera/ov6650.c index 1e4783b51a35..4e19f5e5d8cf 100644 --- a/drivers/media/i2c/soc_camera/ov6650.c +++ b/drivers/media/i2c/soc_camera/ov6650.c @@ -839,9 +839,18 @@ static int ov6650_video_probe(struct i2c_client *client) u8 pidh, pidl, midh, midl; int ret; + priv->clk = v4l2_clk_get(&client->dev, NULL); + if (IS_ERR(priv->clk)) { + ret = PTR_ERR(priv->clk); + dev_err(&client->dev, "v4l2_clk request err: %d\n", ret); + return ret; + } + ret = ov6650_s_power(&priv->subdev, 1); if (ret < 0) - return ret; + goto eclkput; + + msleep(20); /* * check and show product ID and manufacturer ID @@ -876,6 +885,11 @@ static int ov6650_video_probe(struct i2c_client *client) done: ov6650_s_power(&priv->subdev, 0); + if (!ret) + return 0; +eclkput: + v4l2_clk_put(priv->clk); + return ret; } @@ -1033,18 +1047,9 @@ static int ov6650_probe(struct i2c_client *client, priv->code = MEDIA_BUS_FMT_YUYV8_2X8; priv->colorspace = V4L2_COLORSPACE_JPEG; - priv->clk = v4l2_clk_get(&client->dev, NULL); - if (IS_ERR(priv->clk)) { - ret = PTR_ERR(priv->clk); - goto eclkget; - } - ret = ov6650_video_probe(client); - if (ret) { - v4l2_clk_put(priv->clk); -eclkget: + if (ret) v4l2_ctrl_handler_free(&priv->hdl); - } return ret; } diff --git a/drivers/media/pci/saa7146/hexium_gemini.c b/drivers/media/pci/saa7146/hexium_gemini.c index 03cbcd2095c6..d4b3ce828285 100644 --- a/drivers/media/pci/saa7146/hexium_gemini.c +++ b/drivers/media/pci/saa7146/hexium_gemini.c @@ -270,9 +270,8 @@ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_d /* enable i2c-port pins */ saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26)); - hexium->i2c_adapter = (struct i2c_adapter) { - .name = "hexium gemini", - }; + strscpy(hexium->i2c_adapter.name, "hexium gemini", + sizeof(hexium->i2c_adapter.name)); saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480); if (i2c_add_adapter(&hexium->i2c_adapter) < 0) { DEB_S("cannot register i2c-device. skipping.\n"); diff --git a/drivers/media/pci/saa7146/hexium_orion.c b/drivers/media/pci/saa7146/hexium_orion.c index 15f0d66ff78a..214396b1ca73 100644 --- a/drivers/media/pci/saa7146/hexium_orion.c +++ b/drivers/media/pci/saa7146/hexium_orion.c @@ -232,9 +232,8 @@ static int hexium_probe(struct saa7146_dev *dev) saa7146_write(dev, DD1_STREAM_B, 0x00000000); saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); - hexium->i2c_adapter = (struct i2c_adapter) { - .name = "hexium orion", - }; + strscpy(hexium->i2c_adapter.name, "hexium orion", + sizeof(hexium->i2c_adapter.name)); saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480); if (i2c_add_adapter(&hexium->i2c_adapter) < 0) { DEB_S("cannot register i2c-device. skipping.\n"); diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index d76511c1c1e3..a4639813cf35 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -1829,6 +1829,9 @@ static int coda_prepare_decode(struct coda_ctx *ctx) /* Clear decode success flag */ coda_write(dev, 0, CODA_RET_DEC_PIC_SUCCESS); + /* Clear error return value */ + coda_write(dev, 0, CODA_RET_DEC_PIC_ERR_MB); + trace_coda_dec_pic_run(ctx, meta); coda_command_async(ctx, CODA_COMMAND_PIC_RUN); diff --git a/drivers/media/platform/msm/ais/camera/camera.c b/drivers/media/platform/msm/ais/camera/camera.c index 353b74794bc8..dafd4be11bba 100644 --- a/drivers/media/platform/msm/ais/camera/camera.c +++ b/drivers/media/platform/msm/ais/camera/camera.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2019, 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 @@ -370,8 +370,12 @@ static int camera_v4l2_s_fmt_vid_cap_mplane(struct file *filep, void *fh, if (pfmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { - if (WARN_ON(!sp->vb2_q.drv_priv)) - return -ENOMEM; + mutex_lock(sp->vb2_q.lock); + if (WARN_ON(!sp->vb2_q.drv_priv)) { + rc = -ENOMEM; + mutex_unlock(sp->vb2_q.lock); + goto done; + } memcpy(sp->vb2_q.drv_priv, pfmt->fmt.raw_data, sizeof(struct msm_v4l2_format_data)); @@ -382,27 +386,30 @@ static int camera_v4l2_s_fmt_vid_cap_mplane(struct file *filep, void *fh, /* num_planes need to bound checked, otherwise for loop * can execute forever */ - if (WARN_ON(user_fmt->num_planes > VIDEO_MAX_PLANES)) - return -EINVAL; + if (WARN_ON(user_fmt->num_planes > VIDEO_MAX_PLANES)) { + rc = -EINVAL; + mutex_unlock(sp->vb2_q.lock); + goto done; + } for (i = 0; i < user_fmt->num_planes; i++) pr_debug("%s: plane size[%d]\n", __func__, user_fmt->plane_sizes[i]); - + mutex_unlock(sp->vb2_q.lock); if (msm_is_daemon_present() != false) { camera_pack_event(filep, MSM_CAMERA_SET_PARM, MSM_CAMERA_PRIV_S_FMT, -1, &event); rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT); if (rc < 0) - return rc; + goto done; rc = camera_check_event_status(&event); if (rc < 0) - return rc; + goto done; } sp->is_vb2_valid = 1; } - +done: return rc; } @@ -600,6 +607,12 @@ static int camera_v4l2_vb2_q_init(struct file *filep) pr_err("%s : memory not available\n", __func__); return -ENOMEM; } + q->lock = kzalloc(sizeof(struct mutex), GFP_KERNEL); + if (!q->lock) { + kzfree(q->drv_priv); + return -ENOMEM; + } + mutex_init(q->lock); q->mem_ops = msm_vb2_get_q_mem_ops(); q->ops = msm_vb2_get_q_ops(); @@ -619,6 +632,8 @@ static void camera_v4l2_vb2_q_release(struct file *filep) kzfree(sp->vb2_q.drv_priv); mutex_lock(&sp->lock); vb2_queue_release(&sp->vb2_q); + mutex_destroy(sp->vb2_q.lock); + kzfree(sp->vb2_q.lock); mutex_unlock(&sp->lock); } diff --git a/drivers/media/platform/msm/ais/common/msm_camera_io_util.c b/drivers/media/platform/msm/ais/common/msm_camera_io_util.c index c6c2e0d02b65..8cee210095eb 100644 --- a/drivers/media/platform/msm/ais/common/msm_camera_io_util.c +++ b/drivers/media/platform/msm/ais/common/msm_camera_io_util.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2019, 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 @@ -357,12 +357,13 @@ int msm_cam_clk_enable(struct device *dev, struct msm_cam_clk_info *clk_info, } } else { for (i = num_clk - 1; i >= 0; i--) { - if (clk_ptr[i] != NULL) { + if (!IS_ERR_OR_NULL(clk_ptr[i])) { CDBG("%s disable %s\n", __func__, clk_info[i].clk_name); clk_disable(clk_ptr[i]); clk_unprepare(clk_ptr[i]); clk_put(clk_ptr[i]); + clk_ptr[i] = NULL; } } } @@ -378,10 +379,11 @@ cam_clk_set_err: clk_put(clk_ptr[i]); cam_clk_get_err: for (i--; i >= 0; i--) { - if (clk_ptr[i] != NULL) { + if (!IS_ERR_OR_NULL(clk_ptr[i])) { clk_disable(clk_ptr[i]); clk_unprepare(clk_ptr[i]); clk_put(clk_ptr[i]); + clk_ptr[i] = NULL; } } return rc; diff --git a/drivers/media/platform/msm/ais/msm_vb2/msm_vb2.c b/drivers/media/platform/msm/ais/msm_vb2/msm_vb2.c index 1cbc49c8485c..4fe40a5b34bd 100644 --- a/drivers/media/platform/msm/ais/msm_vb2/msm_vb2.c +++ b/drivers/media/platform/msm/ais/msm_vb2/msm_vb2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, 2019 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 @@ -19,15 +19,19 @@ static int msm_vb2_queue_setup(struct vb2_queue *q, unsigned int sizes[], void *alloc_ctxs[]) { int i; - struct msm_v4l2_format_data *data = q->drv_priv; + struct msm_v4l2_format_data *data = NULL; + int rc = -EINVAL; + + mutex_lock(q->lock); + data = q->drv_priv; if (!data) { pr_err("%s: drv_priv NULL\n", __func__); - return -EINVAL; + goto done; } if (data->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { if (WARN_ON(data->num_planes > VIDEO_MAX_PLANES)) - return -EINVAL; + goto done; *num_planes = data->num_planes; @@ -36,9 +40,11 @@ static int msm_vb2_queue_setup(struct vb2_queue *q, } else { pr_err("%s: Unsupported buf type :%d\n", __func__, data->type); - return -EINVAL; + goto done; } - return 0; +done: + mutex_unlock(q->lock); + return rc; } static int msm_vb2_buf_init(struct vb2_buffer *vb) diff --git a/drivers/media/platform/msm/ais/sensor/actuator/msm_actuator.c b/drivers/media/platform/msm/ais/sensor/actuator/msm_actuator.c index 4a23fe0e9c7e..9b67fb55a886 100644 --- a/drivers/media/platform/msm/ais/sensor/actuator/msm_actuator.c +++ b/drivers/media/platform/msm/ais/sensor/actuator/msm_actuator.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2019, 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 @@ -768,6 +768,8 @@ static int32_t msm_actuator_bivcm_move_focus( a_ctrl->curr_step_pos, dest_step_pos, curr_lens_pos); while (a_ctrl->curr_step_pos != dest_step_pos) { + if (a_ctrl->curr_region_index >= a_ctrl->region_size) + break; step_boundary = a_ctrl->region_params[a_ctrl->curr_region_index]. step_bound[dir]; diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c index 18899373de8a..e5fad84020bf 100644 --- a/drivers/media/platform/msm/vidc/venus_hfi.c +++ b/drivers/media/platform/msm/vidc/venus_hfi.c @@ -39,6 +39,7 @@ #define FIRMWARE_SIZE 0X00A00000 #define REG_ADDR_OFFSET_BITMASK 0x000FFFFF #define QDSS_IOVA_START 0x80001000 +#define MIN_PAYLOAD_SIZE 3 static struct hal_device_data hal_ctxt; @@ -3447,23 +3448,55 @@ static void __flush_debug_queue(struct venus_hfi_device *device, u8 *packet) log_level = VIDC_ERR; } +#define SKIP_INVALID_PKT(pkt_size, payload_size, pkt_hdr_size) ({ \ + if (pkt_size < pkt_hdr_size || \ + payload_size < MIN_PAYLOAD_SIZE || \ + payload_size > \ + (pkt_size - pkt_hdr_size + sizeof(u8))) { \ + dprintk(VIDC_ERR, \ + "%s: invalid msg size - %d\n", \ + __func__, pkt->msg_size); \ + continue; \ + } \ + }) + while (!__iface_dbgq_read(device, packet)) { - struct hfi_msg_sys_coverage_packet *pkt = - (struct hfi_msg_sys_coverage_packet *) packet; + struct hfi_packet_header *pkt = + (struct hfi_packet_header *) packet; + + if (pkt->size < sizeof(struct hfi_packet_header)) { + dprintk(VIDC_ERR, "Invalid pkt size - %s\n", + __func__); + continue; + } + if (pkt->packet_type == HFI_MSG_SYS_COV) { + struct hfi_msg_sys_coverage_packet *pkt = + (struct hfi_msg_sys_coverage_packet *) packet; int stm_size = 0; + + SKIP_INVALID_PKT(pkt->size, + pkt->msg_size, sizeof(*pkt)); + stm_size = stm_log_inv_ts(0, 0, pkt->rg_msg_data, pkt->msg_size); if (stm_size == 0) dprintk(VIDC_ERR, "In %s, stm_log returned size of 0\n", __func__); - } else { + + } else if (pkt->packet_type == HFI_MSG_SYS_DEBUG) { struct hfi_msg_sys_debug_packet *pkt = (struct hfi_msg_sys_debug_packet *) packet; + + SKIP_INVALID_PKT(pkt->size, + pkt->msg_size, sizeof(*pkt)); + + pkt->rg_msg_data[pkt->msg_size-1] = '\0'; dprintk(log_level, "%s", pkt->rg_msg_data); } } +#undef SKIP_INVALID_PKT if (local_packet) kfree(packet); diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h index 1218f0a86bc4..3709ad9fc658 100644 --- a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h +++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, 2019, 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 @@ -976,6 +976,11 @@ struct vidc_hal_session_cmd_pkt { u32 session_id; }; +struct hfi_packet_header { + u32 size; + u32 packet_type; +}; + struct hfi_cmd_sys_init_packet { u32 size; u32 packet_type; diff --git a/drivers/media/platform/vivid/vivid-vid-cap.c b/drivers/media/platform/vivid/vivid-vid-cap.c index a84954f1be34..9eb0bc4a8d97 100644 --- a/drivers/media/platform/vivid/vivid-vid-cap.c +++ b/drivers/media/platform/vivid/vivid-vid-cap.c @@ -993,7 +993,7 @@ int vivid_vid_cap_s_selection(struct file *file, void *fh, struct v4l2_selection rect_map_inside(&s->r, &dev->fmt_cap_rect); if (dev->bitmap_cap && (compose->width != s->r.width || compose->height != s->r.height)) { - kfree(dev->bitmap_cap); + vfree(dev->bitmap_cap); dev->bitmap_cap = NULL; } *compose = s->r; diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 192f36f2f4aa..8aac8074e4e1 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -498,4 +498,11 @@ config RADIO_ZOLTRIX_PORT endif # V4L_RADIO_ISA_DRIVERS +config RADIO_SILABS + tristate "SILABS FM" + depends on I2C && VIDEO_V4L2 + ---help--- + Say Y here if you want to use the SiLabs' FM chip + with I2C as transport. + endif # RADIO_ADAPTERS diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile index 120e791199b2..cc8eabb0d9c3 100644 --- a/drivers/media/radio/Makefile +++ b/drivers/media/radio/Makefile @@ -33,7 +33,7 @@ obj-$(CONFIG_RADIO_WL1273) += radio-wl1273.o obj-$(CONFIG_RADIO_WL128X) += wl128x/ obj-$(CONFIG_RADIO_TEA575X) += tea575x.o obj-$(CONFIG_USB_RAREMONO) += radio-raremono.o - +obj-$(CONFIG_RADIO_SILABS) += silabs/ shark2-objs := radio-shark2.o radio-tea5777.o ccflags-y += -Isound diff --git a/drivers/media/radio/silabs/Makefile b/drivers/media/radio/silabs/Makefile new file mode 100644 index 000000000000..48d34fc4683d --- /dev/null +++ b/drivers/media/radio/silabs/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_RADIO_SILABS) += radio-silabs.o diff --git a/drivers/media/radio/silabs/radio-silabs.c b/drivers/media/radio/silabs/radio-silabs.c new file mode 100644 index 000000000000..5e41fa747896 --- /dev/null +++ b/drivers/media/radio/silabs/radio-silabs.c @@ -0,0 +1,3963 @@ +/* Copyright (c) 2014-2015, 2019, 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. + */ + + +#define DRIVER_NAME "radio-silabs" +#define DRIVER_CARD "Silabs FM Radio Receiver" +#define DRIVER_DESC "Driver for Silabs FM Radio receiver" + +#include <linux/version.h> +#include <linux/init.h> /* Initdata */ +#include <linux/delay.h> /* udelay */ +#include <linux/uaccess.h> /* copy to/from user */ +#include <linux/kfifo.h> /* lock free circular buffer */ +#include <linux/param.h> +#include <linux/i2c.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> + +/* kernel includes */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/videodev2.h> +#include <linux/mutex.h> +#include <linux/unistd.h> +#include <linux/atomic.h> +#include <linux/workqueue.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/pwm.h> +#include <linux/regulator/consumer.h> +#include <linux/pinctrl/consumer.h> +#include <linux/clk.h> +#include <linux/of_gpio.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-device.h> +#include "radio-silabs.h" + +struct silabs_fm_device { + struct i2c_client *client; + struct pwm_device *pwm; + bool is_len_gpio_valid; + struct fm_power_vreg_data *dreg; + struct fm_power_vreg_data *areg; + int reset_gpio; + int int_gpio; + int status_gpio; + struct pinctrl *fm_pinctrl; + struct pinctrl_state *gpio_state_active; + struct pinctrl_state *gpio_state_suspend; + struct video_device *videodev; + struct v4l2_device v4l2_dev; + /* driver management */ + atomic_t users; + /* To send commands*/ + u8 write_buf[WRITE_REG_NUM]; + /* TO read events, data*/ + u8 read_buf[READ_REG_NUM]; + /*RDS buffers + Radio event buffer*/ + struct kfifo data_buf[SILABS_FM_BUF_MAX]; + struct silabs_fm_recv_conf_req recv_conf; + struct completion sync_req_done; + /* for the first tune, we need to set properties for digital audio. */ + u8 first_tune; + int tune_req; + /* 1 if tune is pending, 2 if seek is pending, 0 otherwise.*/ + u8 seek_tune_status; + /* command that is being sent to chip. */ + u8 cmd; + u8 antenna; + u8 g_search_mode; + bool is_search_cancelled; + unsigned int mode; + /* regional settings */ + enum silabs_region_t region; + /* power mode */ + bool lp_mode; + int handle_irq; + /* global lock */ + struct mutex lock; + /* buffer locks*/ + spinlock_t buf_lock[SILABS_FM_BUF_MAX]; + /* work queue */ + struct workqueue_struct *wqueue; + struct workqueue_struct *wqueue_scan; + struct workqueue_struct *wqueue_af; + struct workqueue_struct *wqueue_rds; + struct work_struct rds_worker; + struct delayed_work work; + struct delayed_work work_scan; + struct delayed_work work_af; + /* wait queue for blocking event read */ + wait_queue_head_t event_queue; + /* wait queue for raw rds read */ + wait_queue_head_t read_queue; + int irq; + int status_irq; + int tuned_freq_khz; + int dwell_time_sec; + u16 pi; /* PI of tuned channel */ + u8 pty; /* programe type of the tuned channel */ + u16 block[NO_OF_RDS_BLKS]; + u8 rt_display[MAX_RT_LEN]; /* RT that will be displayed */ + u8 rt_tmp0[MAX_RT_LEN]; /* high probability RT */ + u8 rt_tmp1[MAX_RT_LEN]; /* low probability RT */ + u8 rt_cnt[MAX_RT_LEN]; /* high probability RT's hit count */ + u8 rt_flag; /* A/B flag of RT */ + bool valid_rt_flg; /* validity of A/B flag */ + u8 ps_display[MAX_PS_LEN]; /* PS that will be displayed */ + u8 ps_tmp0[MAX_PS_LEN]; /* high probability PS */ + u8 ps_tmp1[MAX_PS_LEN]; /* low probability PS */ + u8 ps_cnt[MAX_PS_LEN]; /* high probability PS's hit count */ + u8 rt_plus_carrier; + u8 ert_carrier; + u8 ert_buf[MAX_ERT_LEN]; + u8 ert_len; + u8 c_byt_pair_index; + u8 utf_8_flag; + u8 rt_ert_flag; + u8 formatting_dir; + bool is_af_jump_enabled; + bool is_af_tune_in_progress; + u16 af_avg_th; + u8 af_wait_timer; + u8 af_rssi_th; /* allowed rssi is 0-127 */ + u8 rssi_th; /* 0 - 127 */ + u8 sinr_th; /* 0 - 127 */ + u8 rds_fifo_cnt; /* 0 - 25 */ + struct silabs_af_info af_info1; + struct silabs_af_info af_info2; + struct silabs_srch_list_compl srch_list; +}; + +static int silabs_fm_request_irq(struct silabs_fm_device *radio); +static int tune(struct silabs_fm_device *radio, u32 freq); +static int silabs_seek(struct silabs_fm_device *radio, int dir, int wrap); +static int cancel_seek(struct silabs_fm_device *radio); +static int configure_interrupts(struct silabs_fm_device *radio, u8 val); +static void silabs_fm_q_event(struct silabs_fm_device *radio, + enum silabs_evt_t event); + +static bool is_valid_rssi(int rssi) +{ + if ((rssi >= MIN_RSSI) && + (rssi <= MAX_RSSI)) + return true; + else + return false; +} + +static bool is_valid_sinr(int sinr) +{ + if ((sinr >= MIN_SNR) && + (sinr <= MAX_SNR)) + return true; + else + return false; +} + +static bool is_valid_rds_fifo_cnt(int cnt) +{ + if ((cnt >= MIN_RDS_FIFO_CNT) && + (cnt <= MAX_RDS_FIFO_CNT)) + return true; + else + return false; +} + +static int silabs_fm_i2c_read(struct silabs_fm_device *radio, u8 len) +{ + int i = 0, retval = 0; + struct i2c_msg msgs[1]; + + msgs[0].addr = radio->client->addr; + msgs[0].len = len; + msgs[0].flags = I2C_M_RD; + msgs[0].buf = (u8 *)radio->read_buf; + + for (i = 0; i < 2; i++) { + retval = i2c_transfer(radio->client->adapter, msgs, 1); + if (retval == 1) + break; + } + + return retval; +} + +static int silabs_fm_i2c_write(struct silabs_fm_device *radio, u8 len) +{ + struct i2c_msg msgs[1]; + int i = 0, retval = 0; + + msgs[0].addr = radio->client->addr; + msgs[0].len = len; + msgs[0].flags = 0; + msgs[0].buf = (u8 *)radio->write_buf; + + for (i = 0; i < 2; i++) { + retval = i2c_transfer(radio->client->adapter, msgs, 1); + if (retval == 1) + break; + } + + return retval; +} + +static int silabs_fm_pinctrl_select(struct silabs_fm_device *radio, bool on) +{ + struct pinctrl_state *pins_state; + int ret; + + pins_state = on ? radio->gpio_state_active + : radio->gpio_state_suspend; + + if (!IS_ERR_OR_NULL(pins_state)) { + ret = pinctrl_select_state(radio->fm_pinctrl, pins_state); + if (ret) { + FMDERR("%s: cannot set pin state\n", __func__); + return ret; + } + } else { + FMDERR("%s: not a valid %s pin state\n", __func__, + on ? "pmx_fm_active" : "pmx_fm_suspend"); + } + + return 0; +} + +static int fm_configure_gpios(struct silabs_fm_device *radio, bool on) +{ + int rc = 0; + int fm_reset_gpio = radio->reset_gpio; + int fm_int_gpio = radio->int_gpio; + int fm_status_gpio = radio->status_gpio; + + if (on) { + /* + * Turn ON sequence + * GPO1/status gpio configuration. + * Keep the GPO1 to high till device comes out of reset. + */ + if (fm_status_gpio > 0) { + FMDERR("status gpio is provided, setting it to high\n"); + rc = gpio_direction_output(fm_status_gpio, 1); + if (rc) { + FMDERR("unable to set gpio %d direction(%d)\n", + fm_status_gpio, rc); + return rc; + } + /* Wait for the value to take effect on gpio. */ + msleep(100); + } + + /* + * GPO2/Interrupt gpio configuration. + * Keep the GPO2 to low till device comes out of reset. + */ + rc = gpio_direction_output(fm_int_gpio, 0); + if (rc) { + FMDERR("unable to set the gpio %d direction(%d)\n", + fm_int_gpio, rc); + return rc; + } + /* Wait for the value to take effect on gpio. */ + msleep(100); + + /* + * Reset pin configuration. + * write "0'' to make sure the chip is in reset. + */ + rc = gpio_direction_output(fm_reset_gpio, 0); + if (rc) { + FMDERR("Unable to set direction\n"); + return rc; + } + /* Wait for the value to take effect on gpio. */ + msleep(100); + /* write "1" to bring the chip out of reset.*/ + rc = gpio_direction_output(fm_reset_gpio, 1); + if (rc) { + FMDERR("Unable to set direction\n"); + return rc; + } + /* Wait for the value to take effect on gpio. */ + msleep(100); + + rc = gpio_direction_input(fm_int_gpio); + if (rc) { + FMDERR("unable to set the gpio %d direction(%d)\n", + fm_int_gpio, rc); + return rc; + } + /* Wait for the value to take effect on gpio. */ + msleep(100); + + if (fm_status_gpio > 0) { + FMDERR("setting status gpio as input\n"); + rc = gpio_direction_input(fm_status_gpio); + if (rc) { + FMDERR("unable to set gpio %d direction(%d)\n", + fm_status_gpio, rc); + return rc; + } + /* Wait for the value to take effect on gpio. */ + msleep(100); + } + + + } else { + /*Turn OFF sequence */ + gpio_set_value(fm_reset_gpio, 0); + + rc = gpio_direction_input(fm_reset_gpio); + if (rc) + FMDERR("Unable to set direction\n"); + /* Wait for some time for the value to take effect. */ + msleep(100); + if (fm_status_gpio > 0) { + rc = gpio_direction_input(fm_status_gpio); + if (rc) + FMDERR("Unable to set dir for status gpio\n"); + msleep(100); + } + } + return rc; +} + +static int silabs_fm_areg_cfg(struct silabs_fm_device *radio, bool on) +{ + int rc = 0; + struct fm_power_vreg_data *vreg; + + vreg = radio->areg; + if (!vreg) { + FMDERR("In %s, areg is NULL\n", __func__); + return rc; + } + if (on) { + FMDBG("vreg is : %s", vreg->name); + if (vreg->set_voltage_sup) { + rc = regulator_set_voltage(vreg->reg, + vreg->low_vol_level, + vreg->high_vol_level); + if (rc < 0) { + FMDERR("set_vol(%s) fail %d\n", vreg->name, rc); + return rc; + } + } + rc = regulator_enable(vreg->reg); + if (rc < 0) { + FMDERR("reg enable(%s) failed.rc=%d\n", vreg->name, rc); + if (vreg->set_voltage_sup) { + regulator_set_voltage(vreg->reg, + 0, + vreg->high_vol_level); + } + return rc; + } + vreg->is_enabled = true; + + } else { + rc = regulator_disable(vreg->reg); + if (rc < 0) { + FMDERR("reg disable(%s) fail rc=%d\n", vreg->name, rc); + return rc; + } + vreg->is_enabled = false; + + if (vreg->set_voltage_sup) { + /* Set the min voltage to 0 */ + rc = regulator_set_voltage(vreg->reg, + 0, + vreg->high_vol_level); + if (rc < 0) { + FMDERR("set_vol(%s) fail %d\n", vreg->name, rc); + return rc; + } + } + } + return rc; +} + +static int silabs_fm_dreg_cfg(struct silabs_fm_device *radio, bool on) +{ + int rc = 0; + struct fm_power_vreg_data *vreg; + + vreg = radio->dreg; + if (!vreg) { + FMDERR("In %s, dreg is NULL\n", __func__); + return rc; + } + + if (on) { + FMDBG("vreg is : %s", vreg->name); + if (vreg->set_voltage_sup) { + rc = regulator_set_voltage(vreg->reg, + vreg->low_vol_level, + vreg->high_vol_level); + if (rc < 0) { + FMDERR("set_vol(%s) fail %d\n", vreg->name, rc); + return rc; + } + } + + rc = regulator_enable(vreg->reg); + if (rc < 0) { + FMDERR("reg enable(%s) failed.rc=%d\n", vreg->name, rc); + if (vreg->set_voltage_sup) { + regulator_set_voltage(vreg->reg, + 0, + vreg->high_vol_level); + } + return rc; + } + vreg->is_enabled = true; + } else { + rc = regulator_disable(vreg->reg); + if (rc < 0) { + FMDERR("reg disable(%s) fail. rc=%d\n", vreg->name, rc); + return rc; + } + vreg->is_enabled = false; + + if (vreg->set_voltage_sup) { + /* Set the min voltage to 0 */ + rc = regulator_set_voltage(vreg->reg, + 0, + vreg->high_vol_level); + if (rc < 0) { + FMDERR("set_vol(%s) fail %d\n", vreg->name, rc); + return rc; + } + } + } + return rc; +} + +static int silabs_fm_power_cfg(struct silabs_fm_device *radio, bool on) +{ + int rc = 0; + + if (on) { + /* Turn ON sequence */ + rc = silabs_fm_dreg_cfg(radio, on); + if (rc < 0) { + FMDERR("In %s, dreg cfg failed %x\n", __func__, rc); + return rc; + } + rc = silabs_fm_areg_cfg(radio, on); + if (rc < 0) { + FMDERR("In %s, areg cfg failed %x\n", __func__, rc); + silabs_fm_dreg_cfg(radio, false); + return rc; + } + /* If pinctrl is supported, select active state */ + if (radio->fm_pinctrl) { + rc = silabs_fm_pinctrl_select(radio, true); + if (rc) + FMDERR("%s: error setting active pin state\n", + __func__); + } + + rc = fm_configure_gpios(radio, on); + if (rc < 0) { + FMDERR("fm_power gpio config failed\n"); + silabs_fm_dreg_cfg(radio, false); + silabs_fm_areg_cfg(radio, false); + return rc; + } + } else { + /* Turn OFF sequence */ + rc = fm_configure_gpios(radio, on); + if (rc < 0) + FMDERR("fm_power gpio config failed"); + + /* If pinctrl is supported, select suspend state */ + if (radio->fm_pinctrl) { + rc = silabs_fm_pinctrl_select(radio, false); + if (rc) + FMDERR("%s: error setting suspend pin state\n", + __func__); + } + rc = silabs_fm_dreg_cfg(radio, on); + if (rc < 0) + FMDERR("In %s, dreg cfg failed %x\n", __func__, rc); + rc = silabs_fm_areg_cfg(radio, on); + if (rc < 0) + FMDERR("In %s, areg cfg failed %x\n", __func__, rc); + } + return rc; +} + +static bool is_enable_rx_possible(struct silabs_fm_device *radio) +{ + bool retval = true; + + if (radio->mode == FM_OFF || radio->mode == FM_RECV) + retval = false; + + return retval; +} + +static int read_cts_bit(struct silabs_fm_device *radio) +{ + int retval = 1, i = 0; + + for (i = 0; i < CTS_RETRY_COUNT; i++) { + memset(radio->read_buf, 0, READ_REG_NUM); + + retval = silabs_fm_i2c_read(radio, READ_REG_NUM); + + if (retval < 0) { + FMDERR("%s: failure reading the response, error %d\n", + __func__, retval); + continue; + } else + FMDBG("%s: successfully read the response from soc\n", + __func__); + + if (radio->read_buf[0] & ERR_BIT_MASK) { + FMDERR("%s: error bit set\n", __func__); + switch (radio->read_buf[1]) { + case BAD_CMD: + FMDERR("%s: cmd %d, error BAD_CMD\n", + __func__, radio->cmd); + break; + case BAD_ARG1: + FMDERR("%s: cmd %d, error BAD_ARG1\n", + __func__, radio->cmd); + break; + case BAD_ARG2: + FMDERR("%s: cmd %d, error BAD_ARG2\n", + __func__, radio->cmd); + break; + case BAD_ARG3: + FMDERR("%s: cmd %d, error BAD_ARG3\n", + __func__, radio->cmd); + break; + case BAD_ARG4: + FMDERR("%s: cmd %d, error BAD_ARG4\n", + __func__, radio->cmd); + break; + case BAD_ARG5: + FMDERR("%s: cmd %d, error BAD_ARG5\n", + __func__, radio->cmd); + case BAD_ARG6: + FMDERR("%s: cmd %d, error BAD_ARG6\n", + __func__, radio->cmd); + break; + case BAD_ARG7: + FMDERR("%s: cmd %d, error BAD_ARG7\n", + __func__, radio->cmd); + break; + case BAD_PROP: + FMDERR("%s: cmd %d, error BAD_PROP\n", + __func__, radio->cmd); + break; + case BAD_BOOT_MODE: + FMDERR("%s:cmd %d,err BAD_BOOT_MODE\n", + __func__, radio->cmd); + break; + default: + FMDERR("%s: cmd %d, unknown error\n", + __func__, radio->cmd); + break; + } + retval = -EINVAL; + goto bad_cmd_arg; + + } + + if (radio->read_buf[0] & CTS_INT_BIT_MASK) { + FMDBG("In %s, CTS bit is set\n", __func__); + break; + } + /* + * Give some time if the chip is not done with processing + * previous command. + */ + msleep(100); + } + + FMDBG("In %s, status byte is %x\n", __func__, radio->read_buf[0]); + +bad_cmd_arg: + return retval; +} + +static int send_cmd(struct silabs_fm_device *radio, u8 total_len) +{ + int retval = 0; + + retval = silabs_fm_i2c_write(radio, total_len); + + if (retval > 0) { + FMDBG("In %s, successfully written command %x to soc\n", + __func__, radio->write_buf[0]); + } else { + FMDERR("In %s, error %d writing command %d to soc\n", + __func__, retval, radio->write_buf[1]); + } + + retval = read_cts_bit(radio); + + return retval; +} + +static int get_property(struct silabs_fm_device *radio, u16 prop, u16 *pvalue) +{ + int retval = 0; + + mutex_lock(&radio->lock); + memset(radio->write_buf, 0, WRITE_REG_NUM); + + /* track command that is being sent to chip. */ + radio->cmd = GET_PROPERTY_CMD; + radio->write_buf[0] = GET_PROPERTY_CMD; + /* reserved, always write 0 */ + radio->write_buf[1] = 0; + /* property high byte */ + radio->write_buf[2] = HIGH_BYTE_16BIT(prop); + /* property low byte */ + radio->write_buf[3] = LOW_BYTE_16BIT(prop); + + FMDBG("in %s, radio->write_buf[2] is %x\n", + __func__, radio->write_buf[2]); + FMDBG("in %s, radio->write_buf[3] is %x\n", + __func__, radio->write_buf[3]); + + retval = send_cmd(radio, GET_PROP_CMD_LEN); + if (retval < 0) + FMDERR("In %s, error getting property %d\n", __func__, prop); + else + *pvalue = (radio->read_buf[2] << 8) + radio->read_buf[3]; + + mutex_unlock(&radio->lock); + + return retval; +} + + +static int set_property(struct silabs_fm_device *radio, u16 prop, u16 value) +{ + int retval = 0; + + mutex_lock(&radio->lock); + + memset(radio->write_buf, 0, WRITE_REG_NUM); + + /* track command that is being sent to chip. */ + radio->cmd = SET_PROPERTY_CMD; + radio->write_buf[0] = SET_PROPERTY_CMD; + /* reserved, always write 0 */ + radio->write_buf[1] = 0; + /* property high byte */ + radio->write_buf[2] = HIGH_BYTE_16BIT(prop); + /* property low byte */ + radio->write_buf[3] = LOW_BYTE_16BIT(prop); + + /* value high byte */ + radio->write_buf[4] = HIGH_BYTE_16BIT(value); + /* value low byte */ + radio->write_buf[5] = LOW_BYTE_16BIT(value); + + retval = send_cmd(radio, SET_PROP_CMD_LEN); + if (retval < 0) + FMDERR("In %s, error setting property %d\n", __func__, prop); + + mutex_unlock(&radio->lock); + + return retval; +} + +static void update_search_list(struct silabs_fm_device *radio, int freq) +{ + int temp_freq = freq; + + temp_freq = temp_freq - + (radio->recv_conf.band_low_limit * TUNE_STEP_SIZE); + temp_freq = temp_freq / 50; + radio->srch_list.rel_freq[radio->srch_list.num_stations_found]. + rel_freq_lsb = GET_LSB(temp_freq); + radio->srch_list.rel_freq[radio->srch_list.num_stations_found]. + rel_freq_msb = GET_MSB(temp_freq); + radio->srch_list.num_stations_found++; +} + +static void silabs_scan(struct work_struct *work) +{ + struct silabs_fm_device *radio; + int current_freq_khz; + u8 valid; + u8 bltf; + u32 temp_freq_khz; + int retval = 0; + struct kfifo *data_b; + int len = 0; + + FMDBG("+%s, getting radio handle from work struct\n", __func__); + radio = container_of(work, struct silabs_fm_device, work_scan.work); + + if (unlikely(radio == NULL)) { + FMDERR(":radio is null"); + return; + } + + current_freq_khz = radio->tuned_freq_khz; + FMDBG("current freq is %d\n", current_freq_khz); + + radio->seek_tune_status = SCAN_PENDING; + /* tune to lowest freq of the band */ + retval = tune(radio, radio->recv_conf.band_low_limit * TUNE_STEP_SIZE); + if (retval < 0) { + FMDERR("%s: Tune to lower band limit failed with error %d\n", + __func__, retval); + goto seek_tune_fail; + } + + /* wait for tune to complete. */ + if (!wait_for_completion_timeout(&radio->sync_req_done, + msecs_to_jiffies(WAIT_TIMEOUT_MSEC))) + FMDERR("In %s, didn't receive STC for tune\n", __func__); + else + FMDBG("In %s, received STC for tune\n", __func__); + while (1) { + /* If scan is cancelled or FM is not ON, break */ + if (radio->is_search_cancelled == true) { + FMDBG("%s: scan cancelled\n", __func__); + if (radio->g_search_mode == SCAN_FOR_STRONG) + goto seek_tune_fail; + else + goto seek_cancelled; + } else if (radio->mode != FM_RECV) { + FMDERR("%s: FM is not in proper state\n", __func__); + return; + } + + retval = silabs_seek(radio, SRCH_DIR_UP, WRAP_DISABLE); + if (retval < 0) { + FMDERR("Scan operation failed with error %d\n", retval); + goto seek_tune_fail; + } + /* wait for seek to complete */ + if (!wait_for_completion_timeout(&radio->sync_req_done, + msecs_to_jiffies(WAIT_TIMEOUT_MSEC))) { + FMDERR("%s: didn't receive STC for seek\n", __func__); + /* FM is not correct state or scan is cancelled */ + continue; + } else + FMDBG("%s: received STC for seek\n", __func__); + + mutex_lock(&radio->lock); + memset(radio->write_buf, 0, WRITE_REG_NUM); + + radio->cmd = FM_TUNE_STATUS_CMD; + + radio->write_buf[0] = FM_TUNE_STATUS_CMD; + radio->write_buf[1] = 0; + + retval = send_cmd(radio, TUNE_STATUS_CMD_LEN); + if (retval < 0) { + FMDERR("%s: FM_TUNE_STATUS_CMD failed with error %d\n", + __func__, retval); + } + + valid = radio->read_buf[1] & VALID_MASK; + bltf = radio->read_buf[1] & BLTF_MASK; + + temp_freq_khz = ((u32)(radio->read_buf[2] << 8) + + radio->read_buf[3])* + TUNE_STEP_SIZE; + mutex_unlock(&radio->lock); + FMDBG("In %s, freq is %d\n", __func__, temp_freq_khz); + + if ((valid) && (radio->g_search_mode == SCAN)) { + FMDBG("val bit set, posting SILABS_EVT_TUNE_SUCC\n"); + silabs_fm_q_event(radio, SILABS_EVT_TUNE_SUCC); + } + + if (bltf) { + FMDBG("bltf bit is set\n"); + break; + } + /* + * If scan is cancelled or FM is not ON, break ASAP so that we + * don't need to sleep for dwell time. + */ + if (radio->is_search_cancelled == true) { + FMDBG("%s: scan cancelled\n", __func__); + if (radio->g_search_mode == SCAN_FOR_STRONG) + goto seek_tune_fail; + else + goto seek_cancelled; + } else if (radio->mode != FM_RECV) { + FMDERR("%s: FM is not in proper state\n", __func__); + return; + } + + if (radio->g_search_mode == SCAN) { + /* sleep for dwell period */ + msleep(radio->dwell_time_sec * 1000); + /* need to queue the event when the seek completes */ + silabs_fm_q_event(radio, SILABS_EVT_SCAN_NEXT); + } else if ((valid) && + (radio->g_search_mode == SCAN_FOR_STRONG)) { + update_search_list(radio, temp_freq_khz); + } + } + +seek_tune_fail: + if (radio->g_search_mode == SCAN_FOR_STRONG) { + len = radio->srch_list.num_stations_found * 2 + + sizeof(radio->srch_list.num_stations_found); + data_b = &radio->data_buf[SILABS_FM_BUF_SRCH_LIST]; + kfifo_in_locked(data_b, &radio->srch_list, len, + &radio->buf_lock[SILABS_FM_BUF_SRCH_LIST]); + silabs_fm_q_event(radio, SILABS_EVT_NEW_SRCH_LIST); + } + /* tune to original frequency */ + retval = tune(radio, current_freq_khz); + if (retval < 0) + FMDERR("%s: Tune to orig freq failed with error %d\n", + __func__, retval); + else { + if (!wait_for_completion_timeout(&radio->sync_req_done, + msecs_to_jiffies(WAIT_TIMEOUT_MSEC))) + FMDERR("%s: didn't receive STC for tune\n", __func__); + else + FMDBG("%s: received STC for tune\n", __func__); + } +seek_cancelled: + silabs_fm_q_event(radio, SILABS_EVT_SEEK_COMPLETE); + radio->seek_tune_status = NO_SEEK_TUNE_PENDING; +} + +static void silabs_search(struct silabs_fm_device *radio, bool on) +{ + int current_freq_khz; + + current_freq_khz = radio->tuned_freq_khz; + + if (on) { + FMDBG("%s: Queuing the work onto scan work q\n", __func__); + queue_delayed_work(radio->wqueue_scan, &radio->work_scan, + msecs_to_jiffies(SILABS_DELAY_MSEC)); + } else { + cancel_seek(radio); + silabs_fm_q_event(radio, SILABS_EVT_SEEK_COMPLETE); + } +} + +static void get_rds_status(struct silabs_fm_device *radio) +{ + int retval = 0; + + mutex_lock(&radio->lock); + memset(radio->write_buf, 0, WRITE_REG_NUM); + radio->cmd = FM_RDS_STATUS_CMD; + radio->write_buf[0] = FM_RDS_STATUS_CMD; + radio->write_buf[1] |= FM_RDS_STATUS_IN_INTACK; + + retval = send_cmd(radio, RDS_CMD_LEN); + if (retval < 0) { + FMDERR("In %s, Get RDS failed %d\n", __func__, retval); + mutex_unlock(&radio->lock); + return; + } + + memset(radio->read_buf, 0, sizeof(radio->read_buf)); + + retval = silabs_fm_i2c_read(radio, RDS_RSP_LEN); + + if (retval < 0) { + FMDERR("In %s, failed to read the resp from soc %d\n", + __func__, retval); + mutex_unlock(&radio->lock); + return; + } + FMDBG("In %s, successfully read the response from soc\n", + __func__); + + radio->block[0] = ((u16)radio->read_buf[MSB_OF_BLK_0] << 8) | + (u16)radio->read_buf[LSB_OF_BLK_0]; + radio->block[1] = ((u16)radio->read_buf[MSB_OF_BLK_1] << 8) | + (u16)radio->read_buf[LSB_OF_BLK_1]; + radio->block[2] = ((u16)radio->read_buf[MSB_OF_BLK_2] << 8) | + (u16)radio->read_buf[LSB_OF_BLK_2]; + radio->block[3] = ((u16)radio->read_buf[MSB_OF_BLK_3] << 8) | + (u16)radio->read_buf[LSB_OF_BLK_3]; + mutex_unlock(&radio->lock); +} + +static void pi_handler(struct silabs_fm_device *radio, u16 current_pi) +{ + if (radio->pi != current_pi) { + FMDBG("PI code of radio->block[0] = %x\n", current_pi); + radio->pi = current_pi; + } else { + FMDBG(" Received same PI code\n"); + } +} + +static void pty_handler(struct silabs_fm_device *radio, u8 current_pty) +{ + if (radio->pty != current_pty) { + FMDBG("PTY code of radio->block[1] = %x\n", current_pty); + radio->pty = current_pty; + } else { + FMDBG("PTY repeated\n"); + } +} + +static void update_ps(struct silabs_fm_device *radio, u8 addr, u8 ps) +{ + u8 i; + bool ps_txt_chg = false; + bool ps_cmplt = true; + u8 *data; + struct kfifo *data_b; + + if (radio->ps_tmp0[addr] == ps) { + if (radio->ps_cnt[addr] < PS_VALIDATE_LIMIT) { + radio->ps_cnt[addr]++; + } else { + radio->ps_cnt[addr] = PS_VALIDATE_LIMIT; + radio->ps_tmp1[addr] = ps; + } + } else if (radio->ps_tmp1[addr] == ps) { + if (radio->ps_cnt[addr] >= PS_VALIDATE_LIMIT) { + ps_txt_chg = true; + radio->ps_cnt[addr] = PS_VALIDATE_LIMIT + 1; + } else { + radio->ps_cnt[addr] = PS_VALIDATE_LIMIT; + } + radio->ps_tmp1[addr] = radio->ps_tmp0[addr]; + radio->ps_tmp0[addr] = ps; + } else if (!radio->ps_cnt[addr]) { + radio->ps_tmp0[addr] = ps; + radio->ps_cnt[addr] = 1; + } else { + radio->ps_tmp1[addr] = ps; + } + + if (ps_txt_chg) { + for (i = 0; i < MAX_PS_LEN; i++) { + if (radio->ps_cnt[i] > 1) + radio->ps_cnt[i]--; + } + } + + for (i = 0; i < MAX_PS_LEN; i++) { + if (radio->ps_cnt[i] < PS_VALIDATE_LIMIT) { + ps_cmplt = false; + return; + } + } + + if (ps_cmplt) { + for (i = 0; (i < MAX_PS_LEN) && + (radio->ps_display[i] == radio->ps_tmp0[i]); i++) + ; + + if (i == MAX_PS_LEN) { + FMDBG("Same PS string repeated\n"); + return; + } + + for (i = 0; i < MAX_PS_LEN; i++) + radio->ps_display[i] = radio->ps_tmp0[i]; + + data = kmalloc(PS_EVT_DATA_LEN, GFP_ATOMIC); + if (data != NULL) { + data[0] = NO_OF_PS; + data[1] = radio->pty; + data[2] = (radio->pi >> 8) & 0xFF; + data[3] = (radio->pi & 0xFF); + data[4] = 0; + memcpy(data + OFFSET_OF_PS, + radio->ps_tmp0, MAX_PS_LEN); + data_b = &radio->data_buf[SILABS_FM_BUF_PS_RDS]; + kfifo_in_locked(data_b, data, PS_EVT_DATA_LEN, + &radio->buf_lock[SILABS_FM_BUF_PS_RDS]); + FMDBG("Q the PS event\n"); + silabs_fm_q_event(radio, SILABS_EVT_NEW_PS_RDS); + kfree(data); + } else { + FMDERR("Memory allocation failed for PTY\n"); + } + } +} + +static void display_rt(struct silabs_fm_device *radio) +{ + u8 len = 0, i = 0; + u8 *data; + struct kfifo *data_b; + bool rt_cmplt = true; + + for (i = 0; i < MAX_RT_LEN; i++) { + if (radio->rt_cnt[i] < RT_VALIDATE_LIMIT) { + rt_cmplt = false; + return; + } + if (radio->rt_tmp0[i] == END_OF_RT) + break; + } + + if (rt_cmplt) { + while ((len < MAX_RT_LEN) && (radio->rt_tmp0[len] != END_OF_RT)) + len++; + + for (i = 0; (i < len) && + (radio->rt_display[i] == radio->rt_tmp0[i]); i++) + ; + + if (i == len) { + FMDBG("Same RT string repeated\n"); + return; + } + for (i = 0; i < len; i++) + radio->rt_display[i] = radio->rt_tmp0[i]; + data = kmalloc(len + OFFSET_OF_RT, GFP_ATOMIC); + if (data != NULL) { + data[0] = len; /* len of RT */ + data[1] = radio->pty; + data[2] = (radio->pi >> 8) & 0xFF; + data[3] = (radio->pi & 0xFF); + data[4] = radio->rt_flag; + memcpy(data + OFFSET_OF_RT, radio->rt_display, len); + data_b = &radio->data_buf[SILABS_FM_BUF_RT_RDS]; + kfifo_in_locked(data_b, data, OFFSET_OF_RT + len, + &radio->buf_lock[SILABS_FM_BUF_RT_RDS]); + FMDBG("Q the RT event\n"); + silabs_fm_q_event(radio, SILABS_EVT_NEW_RT_RDS); + kfree(data); + } else { + FMDERR("Memory allocation failed for PTY\n"); + } + } +} + +static void rt_handler(struct silabs_fm_device *radio, u8 ab_flg, + u8 cnt, u8 addr, u8 *rt) +{ + u8 i; + bool rt_txt_chg = 0; + + if (ab_flg != radio->rt_flag && radio->valid_rt_flg) { + for (i = 0; i < sizeof(radio->rt_cnt); i++) { + if (!radio->rt_tmp0[i]) { + radio->rt_tmp0[i] = ' '; + radio->rt_cnt[i]++; + } + } + memset(radio->rt_cnt, 0, sizeof(radio->rt_cnt)); + memset(radio->rt_tmp0, 0, sizeof(radio->rt_tmp0)); + memset(radio->rt_tmp1, 0, sizeof(radio->rt_tmp1)); + } + + radio->rt_flag = ab_flg; + radio->valid_rt_flg = true; + + for (i = 0; i < cnt; i++) { + if (radio->rt_tmp0[addr+i] == rt[i]) { + if (radio->rt_cnt[addr+i] < RT_VALIDATE_LIMIT) { + radio->rt_cnt[addr+i]++; + } else { + radio->rt_cnt[addr+i] = RT_VALIDATE_LIMIT; + radio->rt_tmp1[addr+i] = rt[i]; + } + } else if (radio->rt_tmp1[addr+i] == rt[i]) { + if (radio->rt_cnt[addr+i] >= RT_VALIDATE_LIMIT) { + rt_txt_chg = true; + radio->rt_cnt[addr+i] = RT_VALIDATE_LIMIT + 1; + } else { + radio->rt_cnt[addr+i] = RT_VALIDATE_LIMIT; + } + radio->rt_tmp1[addr+i] = radio->rt_tmp0[addr+i]; + radio->rt_tmp0[addr+i] = rt[i]; + } else if (!radio->rt_cnt[addr+i]) { + radio->rt_tmp0[addr+i] = rt[i]; + radio->rt_cnt[addr+i] = 1; + } else { + radio->rt_tmp1[addr+i] = rt[i]; + } + } + + if (rt_txt_chg) { + for (i = 0; i < MAX_RT_LEN; i++) { + if (radio->rt_cnt[i] > 1) + radio->rt_cnt[i]--; + } + } + display_rt(radio); +} + +static void silabs_ev_ert(struct silabs_fm_device *radio) +{ + u8 *data = NULL; + struct kfifo *data_b; + + if (radio->ert_len <= 0) + return; + + data = kmalloc((radio->ert_len + ERT_OFFSET), GFP_ATOMIC); + if (data != NULL) { + data[0] = radio->ert_len; + data[1] = radio->utf_8_flag; + data[2] = radio->formatting_dir; + memcpy((data + ERT_OFFSET), radio->ert_buf, radio->ert_len); + data_b = &radio->data_buf[SILABS_FM_BUF_ERT]; + kfifo_in_locked(data_b, data, (radio->ert_len + ERT_OFFSET), + &radio->buf_lock[SILABS_FM_BUF_ERT]); + silabs_fm_q_event(radio, SILABS_EVT_NEW_ERT); + kfree(data); + } +} + +static void silabs_buff_ert(struct silabs_fm_device *radio) +{ + int i; + u16 info_byte = 0; + u8 byte_pair_index; + + byte_pair_index = radio->block[1] & APP_GRP_typ_MASK; + if (byte_pair_index == 0) { + radio->c_byt_pair_index = 0; + radio->ert_len = 0; + } + FMDBG("c_byt_pair_index = %x\n", radio->c_byt_pair_index); + if (radio->c_byt_pair_index == byte_pair_index) { + for (i = 2; i <= 3; i++) { + info_byte = radio->block[i]; + FMDBG("info_byte = %x\n", info_byte); + FMDBG("ert_len = %x\n", radio->ert_len); + if (radio->ert_len > (MAX_ERT_LEN - 2)) + return; + radio->ert_buf[radio->ert_len] = radio->block[i] >> 8; + radio->ert_buf[radio->ert_len + 1] = + radio->block[i] & 0xFF; + radio->ert_len += ERT_CNT_PER_BLK; + FMDBG("utf_8_flag = %d\n", radio->utf_8_flag); + if ((radio->utf_8_flag == 0) && + (info_byte == END_OF_RT)) { + radio->ert_len -= ERT_CNT_PER_BLK; + break; + } else if ((radio->utf_8_flag == 1) && + (radio->block[i] >> 8 == END_OF_RT)) { + info_byte = END_OF_RT; + radio->ert_len -= ERT_CNT_PER_BLK; + break; + } else if ((radio->utf_8_flag == 1) && + ((radio->block[i] & 0xFF) + == END_OF_RT)) { + info_byte = END_OF_RT; + radio->ert_len--; + break; + } + } + if ((byte_pair_index == MAX_ERT_SEGMENT) || + (info_byte == END_OF_RT)) { + silabs_ev_ert(radio); + radio->c_byt_pair_index = 0; + radio->ert_len = 0; + } + radio->c_byt_pair_index++; + } else { + radio->ert_len = 0; + radio->c_byt_pair_index = 0; + } +} + + +static void silabs_rt_plus(struct silabs_fm_device *radio) +{ + u8 tag_type1, tag_type2; + u8 *data = NULL; + int len = 0; + u16 grp_typ; + struct kfifo *data_b; + + grp_typ = radio->block[1] & APP_GRP_typ_MASK; + /* + *right most 3 bits of Lsb of block 2 + * and left most 3 bits of Msb of block 3 + */ + tag_type1 = (((grp_typ & TAG1_MSB_MASK) << TAG1_MSB_OFFSET) | + (radio->block[2] >> TAG1_LSB_OFFSET)); + /* + *right most 1 bit of lsb of 3rd block + * and left most 5 bits of Msb of 4th block + */ + tag_type2 = (((radio->block[2] & TAG2_MSB_MASK) + << TAG2_MSB_OFFSET) | + (radio->block[2] >> TAG2_LSB_OFFSET)); + + if (tag_type1 != DUMMY_CLASS) + len += RT_PLUS_LEN_1_TAG; + if (tag_type2 != DUMMY_CLASS) + len += RT_PLUS_LEN_1_TAG; + + if (len != 0) { + len += RT_PLUS_OFFSET; + data = kmalloc(len, GFP_ATOMIC); + } else { + FMDERR("%s:Len is zero\n", __func__); + return; + } + if (data != NULL) { + data[0] = len; + len = RT_ERT_FLAG_OFFSET; + data[len++] = radio->rt_ert_flag; + if (tag_type1 != DUMMY_CLASS) { + data[len++] = tag_type1; + /* + *start position of tag1 + *right most 5 bits of msb of 3rd block + *and left most bit of lsb of 3rd block + */ + data[len++] = (radio->block[2] >> TAG1_POS_LSB_OFFSET) + & TAG1_POS_MSB_MASK; + /* + *length of tag1 + *left most 6 bits of lsb of 3rd block + */ + data[len++] = (radio->block[2] >> TAG1_LEN_OFFSET) & + TAG1_LEN_MASK; + } + if (tag_type2 != DUMMY_CLASS) { + data[len++] = tag_type2; + /* + *start position of tag2 + *right most 3 bit of msb of 4th block + *and left most 3 bits of lsb of 4th block + */ + data[len++] = (radio->block[3] >> TAG2_POS_LSB_OFFSET) & + TAG2_POS_MSB_MASK; + /* + *length of tag2 + *right most 5 bits of lsb of 4th block + */ + data[len++] = radio->block[3] & TAG2_LEN_MASK; + } + data_b = &radio->data_buf[SILABS_FM_BUF_RT_PLUS]; + kfifo_in_locked(data_b, data, len, + &radio->buf_lock[SILABS_FM_BUF_RT_PLUS]); + silabs_fm_q_event(radio, SILABS_EVT_NEW_RT_PLUS); + kfree(data); + } else { + FMDERR("%s:memory allocation failed\n", __func__); + } +} + +static void silabs_raw_rds_handler(struct silabs_fm_device *radio) +{ + u16 aid, app_grp_typ; + + aid = radio->block[3]; + app_grp_typ = radio->block[1] & APP_GRP_typ_MASK; + FMDBG("app_grp_typ = %x\n", app_grp_typ); + FMDBG("AID = %x", aid); + + switch (aid) { + case ERT_AID: + radio->utf_8_flag = (radio->block[2] & 1); + radio->formatting_dir = EXTRACT_BIT(radio->block[2], + ERT_FORMAT_DIR_BIT); + if (radio->ert_carrier != app_grp_typ) { + silabs_fm_q_event(radio, SILABS_EVT_NEW_ODA); + radio->ert_carrier = app_grp_typ; + } + break; + case RT_PLUS_AID: + /*Extract 5th bit of MSB (b7b6b5b4b3b2b1b0)*/ + radio->rt_ert_flag = EXTRACT_BIT(radio->block[2], + RT_ERT_FLAG_BIT); + if (radio->rt_plus_carrier != app_grp_typ) { + silabs_fm_q_event(radio, SILABS_EVT_NEW_ODA); + radio->rt_plus_carrier = app_grp_typ; + } + break; + default: + FMDBG("Not handling the AID of %x\n", aid); + break; + } +} + +static int set_hard_mute(struct silabs_fm_device *radio, bool val) +{ + int retval = 0; + + if (val == true) { + retval = set_property(radio, RX_HARD_MUTE_PROP, HARD_MUTE_MASK); + + if (retval < 0) + FMDERR("%s: set_hard_mute failed with error %d\n", + __func__, retval); + } else { + retval = set_property(radio, RX_HARD_MUTE_PROP, 0); + + if (retval < 0) + FMDERR("%s: set_hard_mute failed with error %d\n", + __func__, retval); + } + + return retval; +} + +static int set_mute_mode(struct silabs_fm_device *radio, u16 val) +{ + int retval = 0; + + retval = set_property(radio, RX_HARD_MUTE_PROP, val); + + if (retval < 0) + FMDERR("%s: set_mute_mode failed with error %d\n", + __func__, retval); + return retval; +} +static int get_rssi(struct silabs_fm_device *radio, u8 *prssi) +{ + int retval = 0; + + mutex_lock(&radio->lock); + + memset(radio->write_buf, 0, WRITE_REG_NUM); + + /* track command that is being sent to chip.*/ + radio->cmd = FM_RSQ_STATUS_CMD; + radio->write_buf[0] = FM_RSQ_STATUS_CMD; + radio->write_buf[1] = 1; + + retval = send_cmd(radio, RSQ_STATUS_CMD_LEN); + + if (retval < 0) + FMDERR("%s: get_rsq_status failed with error %d\n", + __func__, retval); + + FMDBG("%s: rssi is %d\n", __func__, radio->read_buf[4]); + *prssi = radio->read_buf[4]; + mutex_unlock(&radio->lock); + + return retval; +} + +static bool is_valid_freq(struct silabs_fm_device *radio, u32 freq) +{ + u32 band_low_limit = radio->recv_conf.band_low_limit * TUNE_STEP_SIZE; + u32 band_high_limit = radio->recv_conf.band_high_limit * TUNE_STEP_SIZE; + u8 spacing = 0; + + if (radio->recv_conf.ch_spacing == 0) + spacing = CH_SPACING_200; + else if (radio->recv_conf.ch_spacing == 1) + spacing = CH_SPACING_100; + else if (radio->recv_conf.ch_spacing == 2) + spacing = CH_SPACING_50; + else + return false; + + if ((freq >= band_low_limit) && + (freq <= band_high_limit) && + ((freq - band_low_limit) % spacing == 0)) + return true; + + return false; +} + +static bool is_new_freq(struct silabs_fm_device *radio, u32 freq) +{ + u8 i = 0; + + for (i = 0; i < radio->af_info2.size; i++) { + if (freq == radio->af_info2.af_list[i]) + return false; + } + + return true; +} + +static bool is_different_af_list(struct silabs_fm_device *radio) +{ + u8 i = 0, j = 0; + u32 freq; + + if (radio->af_info1.orig_freq_khz != radio->af_info2.orig_freq_khz) + return true; + + /* freq is same, check if the AFs are same. */ + for (i = 0; i < radio->af_info1.size; i++) { + freq = radio->af_info1.af_list[i]; + for (j = 0; j < radio->af_info2.size; j++) { + if (freq == radio->af_info2.af_list[j]) + break; + } + + /* freq is not there in list2 i.e list1, list2 are different.*/ + if (j == radio->af_info2.size) + return true; + } + + return false; +} + +static void reset_af_info(struct silabs_fm_device *radio) +{ + radio->af_info1.inval_freq_cnt = 0; + radio->af_info1.cnt = 0; + radio->af_info1.index = 0; + radio->af_info1.size = 0; + radio->af_info1.orig_freq_khz = 0; + memset(radio->af_info1.af_list, 0, sizeof(radio->af_info1.af_list)); + + radio->af_info2.inval_freq_cnt = 0; + radio->af_info2.cnt = 0; + radio->af_info2.index = 0; + radio->af_info2.size = 0; + radio->af_info2.orig_freq_khz = 0; + memset(radio->af_info2.af_list, 0, sizeof(radio->af_info2.af_list)); +} + +static void update_af_list(struct silabs_fm_device *radio) +{ + bool retval; + u8 i = 0; + u8 af_data = radio->block[2] >> 8; + u32 af_freq_khz; + + struct kfifo *buff; + struct af_list_ev ev; + spinlock_t lock = radio->buf_lock[SILABS_FM_BUF_AF_LIST]; + + for (; i < NO_OF_AF_IN_GRP; i++, af_data = radio->block[2] & 0xFF) { + + if (af_data >= MIN_AF_CNT_CODE && af_data <= MAX_AF_CNT_CODE) { + + FMDBG("%s: resetting af info, freq %u, pi %u\n", + __func__, radio->tuned_freq_khz, radio->pi); + radio->af_info2.inval_freq_cnt = 0; + radio->af_info2.cnt = 0; + radio->af_info2.orig_freq_khz = 0; + + /* AF count. */ + radio->af_info2.cnt = af_data - NO_AF_CNT_CODE; + radio->af_info2.orig_freq_khz = radio->tuned_freq_khz; + radio->af_info2.pi = radio->pi; + + FMDBG("%s: current freq is %u, AF cnt is %u\n", + __func__, radio->tuned_freq_khz, radio->af_info2.cnt); + + } else if (af_data >= MIN_AF_FREQ_CODE && + af_data <= MAX_AF_FREQ_CODE && + radio->af_info2.orig_freq_khz != 0 && + radio->af_info2.size < MAX_NO_OF_AF) { + + af_freq_khz = SCALE_AF_CODE_TO_FREQ_KHZ(af_data); + retval = is_valid_freq(radio, af_freq_khz); + if (retval == false) { + FMDBG("%s: Invalid AF\n", __func__); + radio->af_info2.inval_freq_cnt++; + continue; + } + + retval = is_new_freq(radio, af_freq_khz); + if (retval == false) { + FMDBG("%s: Duplicate AF\n", __func__); + radio->af_info2.inval_freq_cnt++; + continue; + } + + /* update the AF list */ + radio->af_info2.af_list[radio->af_info2.size++] = + af_freq_khz; + FMDBG("%s: AF is %u\n", __func__, af_freq_khz); + if ((radio->af_info2.size + + radio->af_info2.inval_freq_cnt == + radio->af_info2.cnt) && + is_different_af_list(radio)) { + + /* Copy the list to af_info1. */ + radio->af_info1.cnt = radio->af_info2.cnt; + radio->af_info1.size = radio->af_info2.size; + radio->af_info1.pi = radio->af_info2.pi; + radio->af_info1.orig_freq_khz = + radio->af_info2.orig_freq_khz; + memset(radio->af_info1.af_list, + 0, + sizeof(radio->af_info1.af_list)); + + memcpy(radio->af_info1.af_list, + radio->af_info2.af_list, + sizeof(radio->af_info2.af_list)); + + /* AF list changed, post it to user space */ + memset(&ev, 0, sizeof(struct af_list_ev)); + + ev.tune_freq_khz = + radio->af_info1.orig_freq_khz; + ev.pi_code = radio->pi; + ev.af_size = radio->af_info1.size; + + memcpy(&ev.af_list[0], + radio->af_info1.af_list, + GET_AF_LIST_LEN(ev.af_size)); + + buff = &radio->data_buf[SILABS_FM_BUF_AF_LIST]; + kfifo_in_locked(buff, + (u8 *)&ev, + GET_AF_EVT_LEN(ev.af_size), + &lock); + + FMDBG("%s: posting AF list evt, curr freq %u\n", + __func__, ev.tune_freq_khz); + + silabs_fm_q_event(radio, + SILABS_EVT_NEW_AF_LIST); + } + } + } +} + +static void silabs_af_tune(struct work_struct *work) +{ + struct silabs_fm_device *radio; + int retval = 0, i = 0; + u8 rssi = 0; + u32 freq = 0; + + radio = container_of(work, struct silabs_fm_device, work_af.work); + + if (radio->af_info2.size == 0) { + FMDBG("%s: Empty AF list\n", __func__); + radio->is_af_tune_in_progress = false; + return; + } + radio->af_avg_th = 0; + for (i = 0; i < radio->af_wait_timer; i++) { + retval = get_rssi(radio, &rssi); + if (retval < 0) + FMDERR("%s: getting rssi failed\n", __func__); + radio->af_avg_th += rssi; + msleep(1000); + } + radio->af_avg_th = radio->af_avg_th/radio->af_wait_timer; + if (radio->af_avg_th >= radio->af_rssi_th) { + FMDBG("Not required to do Af jump\n"); + return; + } + + + /* Disable all other interrupts except STC, RDS */ + retval = configure_interrupts(radio, ENABLE_STC_RDS_INTERRUPTS); + + /* Mute until AF tuning finishes */ + retval = set_hard_mute(radio, true); + + while (1) { + if (radio->mode != FM_RECV) { + FMDERR("%s: Drv is not in proper state\n", __func__); + goto end; + } + + if (radio->seek_tune_status != NO_SEEK_TUNE_PENDING) { + FMDBG("%s: manual tune, search issued\n", __func__); + break; + } + + if (radio->is_af_jump_enabled != true) { + FMDBG("%s: AF jump is disabled\n", __func__); + break; + } + + /* If no more AFs left, tune to original frequency and break */ + if (radio->af_info2.index >= radio->af_info2.size) { + FMDBG("%s: No more AFs, tuning to original freq %u\n", + __func__, radio->af_info2.orig_freq_khz); + + freq = radio->af_info2.orig_freq_khz; + + retval = tune(radio, freq); + if (retval < 0) { + FMDERR("%s: tune failed, error %d\n", + __func__, retval); + goto err_tune_fail; + } + + /* wait for tune to finish */ + if (!wait_for_completion_timeout(&radio->sync_req_done, + msecs_to_jiffies(WAIT_TIMEOUT_MSEC))) { + FMDERR("%s: didn't receive STC for tune\n", + __func__); + /* FM is not correct state */ + continue; + } else + FMDBG("%s: received STC for tune\n", __func__); + + goto err_tune_fail; + } + + freq = radio->af_info2.af_list[radio->af_info2.index++]; + + FMDBG("%s: tuning to freq %u\n", __func__, freq); + + retval = tune(radio, freq); + if (retval < 0) { + FMDERR("%s: tune failed, error %d\n", + __func__, retval); + goto err_tune_fail; + } + + /* wait for tune to finish */ + if (!wait_for_completion_timeout(&radio->sync_req_done, + msecs_to_jiffies(WAIT_TIMEOUT_MSEC))) { + FMDERR("%s: didn't receive STC for tune\n", + __func__); + /* FM is not correct state */ + continue; + } else + FMDBG("%s: received STC for tune\n", __func__); + + retval = get_rssi(radio, &rssi); + if (retval < 0) { + FMDERR("%s: getting rssi failed\n", __func__); + goto err_tune_fail; + } + + if (rssi >= radio->af_rssi_th) { + u8 j = 0; + /* clear stale RDS interrupt */ + get_rds_status(radio); + for (; j < AF_PI_WAIT_TIME && radio->pi == 0; j++) { + /* Wait for PI to be received. */ + msleep(100); + FMDBG("%s: sleeping for 100ms for PI\n", + __func__); + } + + if (radio->pi == 0 || radio->pi != radio->af_info2.pi) { + FMDBG("%s: pi %d, af freq pi %d not equal\n", + __func__, radio->pi, radio->af_info2.pi); + continue; + } + + FMDBG("%s: found AF freq(%u) >= AF th with pi %d\n", + __func__, freq, radio->pi); + /* Notify FM UI about the new freq */ + FMDBG("%s: posting TUNE_SUCC event\n", __func__); + silabs_fm_q_event(radio, SILABS_EVT_TUNE_SUCC); + + break; + } + FMDBG("%s: rssi: %u, af_rssi_th: %u not eq contnuing\n", + __func__, rssi, radio->af_rssi_th); + } + +err_tune_fail: + /* + * At this point, we are tuned to either original freq or AF with >= + * AF rssi threshold + */ + if (freq != radio->af_info2.orig_freq_khz) { + FMDBG("tuned freq different than original,reset af info\n"); + reset_af_info(radio); + } + + radio->is_af_tune_in_progress = false; + + /* Clear the stale RDS int bit. */ + get_rds_status(radio); + retval = configure_interrupts(radio, ENABLE_STC_RDS_INTERRUPTS); + + /* Clear the stale RSQ int bit. */ + get_rssi(radio, &rssi); + retval = configure_interrupts(radio, ENABLE_RSQ_INTERRUPTS); + +end: + /* Unmute */ + retval = set_hard_mute(radio, false); +} + +/* When RDS interrupt is received, read and process RDS data. */ +static void rds_handler(struct work_struct *worker) +{ + struct silabs_fm_device *radio; + u8 rt_blks[NO_OF_RDS_BLKS]; + u8 grp_type, addr, ab_flg; + + radio = container_of(worker, struct silabs_fm_device, rds_worker); + + if (!radio) { + FMDERR("%s:radio is null\n", __func__); + return; + } + + FMDBG("Entered rds_handler\n"); + + get_rds_status(radio); + + pi_handler(radio, radio->block[0]); + + grp_type = radio->block[1] >> OFFSET_OF_GRP_TYP; + + FMDBG("grp_type = %d\n", grp_type); + + if (grp_type & 0x01) + pi_handler(radio, radio->block[2]); + + pty_handler(radio, (radio->block[1] >> OFFSET_OF_PTY) & PTY_MASK); + + switch (grp_type) { + case RDS_TYPE_0A: + update_af_list(radio); + /* fall through */ + case RDS_TYPE_0B: + addr = (radio->block[1] & PS_MASK) * NO_OF_CHARS_IN_EACH_ADD; + FMDBG("RDS is PS\n"); + update_ps(radio, addr+0, radio->block[3] >> 8); + update_ps(radio, addr+1, radio->block[3] & 0xff); + break; + case RDS_TYPE_2A: + FMDBG("RDS is RT 2A group\n"); + rt_blks[0] = (u8)(radio->block[2] >> 8); + rt_blks[1] = (u8)(radio->block[2] & 0xFF); + rt_blks[2] = (u8)(radio->block[3] >> 8); + rt_blks[3] = (u8)(radio->block[3] & 0xFF); + addr = (radio->block[1] & 0xf) * 4; + ab_flg = (radio->block[1] & 0x0010) >> 4; + rt_handler(radio, ab_flg, CNT_FOR_2A_GRP_RT, addr, rt_blks); + break; + case RDS_TYPE_2B: + FMDBG("RDS is RT 2B group\n"); + rt_blks[0] = (u8)(radio->block[3] >> 8); + rt_blks[1] = (u8)(radio->block[3] & 0xFF); + rt_blks[2] = 0; + rt_blks[3] = 0; + addr = (radio->block[1] & 0xf) * 2; + ab_flg = (radio->block[1] & 0x0010) >> 4; + radio->rt_tmp0[MAX_LEN_2B_GRP_RT] = END_OF_RT; + radio->rt_tmp1[MAX_LEN_2B_GRP_RT] = END_OF_RT; + radio->rt_cnt[MAX_LEN_2B_GRP_RT] = RT_VALIDATE_LIMIT; + rt_handler(radio, ab_flg, CNT_FOR_2B_GRP_RT, addr, rt_blks); + break; + case RDS_TYPE_3A: + FMDBG("RDS is 3A group\n"); + silabs_raw_rds_handler(radio); + break; + default: + FMDERR("Not handling the group type %d\n", grp_type); + break; + } + FMDBG("rt_plus_carrier = %x\n", radio->rt_plus_carrier); + FMDBG("ert_carrier = %x\n", radio->ert_carrier); + if (radio->rt_plus_carrier && (grp_type == radio->rt_plus_carrier)) + silabs_rt_plus(radio); + else if (radio->ert_carrier && (grp_type == radio->ert_carrier)) + silabs_buff_ert(radio); +} + +/* to enable, disable interrupts. */ +static int configure_interrupts(struct silabs_fm_device *radio, u8 val) +{ + int retval = 0; + u16 prop_val = 0; + + switch (val) { + case DISABLE_ALL_INTERRUPTS: + prop_val = 0; + retval = set_property(radio, GPO_IEN_PROP, prop_val); + if (retval < 0) + FMDERR("%s: error disabling interrupts\n", __func__); + break; + case ENABLE_STC_RDS_INTERRUPTS: + /* enable STC and RDS interrupts. */ + prop_val = RDS_INT_BIT_MASK | STC_INT_BIT_MASK; + + retval = set_property(radio, GPO_IEN_PROP, prop_val); + if (retval < 0) + FMDERR("%s: error enabling STC, RDS interrupts\n", + __func__); + break; + case ENABLE_STC_INTERRUPTS: + /* enable STC interrupts only. */ + prop_val = STC_INT_BIT_MASK; + + retval = set_property(radio, GPO_IEN_PROP, prop_val); + if (retval < 0) + FMDERR("%s: error enabling STC interrupts\n", __func__); + break; + case ENABLE_RDS_INTERRUPTS: + /* enable RDS interrupts. */ + prop_val = RDS_INT_BIT_MASK | STC_INT_BIT_MASK; + if (radio->is_af_jump_enabled) + prop_val |= RSQ_INT_BIT_MASK; + + retval = set_property(radio, GPO_IEN_PROP, prop_val); + if (retval < 0) + FMDERR("%s: error enabling RDS interrupts\n", + __func__); + break; + case DISABLE_RDS_INTERRUPTS: + /* disable RDS interrupts. */ + prop_val = STC_INT_BIT_MASK; + if (radio->is_af_jump_enabled) + prop_val |= RSQ_INT_BIT_MASK; + + retval = set_property(radio, GPO_IEN_PROP, prop_val); + if (retval < 0) + FMDERR("%s: error disabling RDS interrupts\n", + __func__); + break; + case ENABLE_RSQ_INTERRUPTS: + /* enable RSQ interrupts. */ + prop_val = RSQ_INT_BIT_MASK | STC_INT_BIT_MASK; + if (radio->lp_mode != true) + prop_val |= RDS_INT_BIT_MASK; + + retval = set_property(radio, GPO_IEN_PROP, prop_val); + if (retval < 0) + FMDERR("%s: error enabling RSQ interrupts\n", + __func__); + break; + case DISABLE_RSQ_INTERRUPTS: + /* disable RSQ interrupts. */ + prop_val = STC_INT_BIT_MASK; + if (radio->lp_mode != true) + prop_val |= RDS_INT_BIT_MASK; + + retval = set_property(radio, GPO_IEN_PROP, prop_val); + if (retval < 0) + FMDERR("%s: error disabling RSQ interrupts\n", + __func__); + break; + default: + FMDERR("%s: invalid value %u\n", __func__, val); + retval = -EINVAL; + break; + } + + return retval; +} + +static int get_int_status(struct silabs_fm_device *radio) +{ + int retval = 0; + + mutex_lock(&radio->lock); + + memset(radio->write_buf, 0, WRITE_REG_NUM); + + /* track command that is being sent to chip.*/ + radio->cmd = GET_INT_STATUS_CMD; + radio->write_buf[0] = GET_INT_STATUS_CMD; + + retval = send_cmd(radio, GET_INT_STATUS_CMD_LEN); + + if (retval < 0) + FMDERR("%s: get_int_status failed with error %d\n", + __func__, retval); + + mutex_unlock(&radio->lock); + + return retval; +} + +static void reset_rds(struct silabs_fm_device *radio) +{ + radio->pi = 0; + /* reset PS bufferes */ + memset(radio->ps_display, 0, sizeof(radio->ps_display)); + memset(radio->ps_tmp0, 0, sizeof(radio->ps_tmp0)); + memset(radio->ps_cnt, 0, sizeof(radio->ps_cnt)); + + /* reset RT buffers */ + memset(radio->rt_display, 0, sizeof(radio->rt_display)); + memset(radio->rt_tmp0, 0, sizeof(radio->rt_tmp0)); + memset(radio->rt_tmp1, 0, sizeof(radio->rt_tmp1)); + memset(radio->rt_cnt, 0, sizeof(radio->rt_cnt)); +} + +static int initialize_recv(struct silabs_fm_device *radio) +{ + int retval = 0; + + retval = set_property(radio, + FM_SEEK_TUNE_SNR_THRESHOLD_PROP, + DEFAULT_SNR_TH); + if (retval < 0) { + FMDERR("%s: FM_SEEK_TUNE_SNR_THRESHOLD_PROP fail error %d\n", + __func__, retval); + goto set_prop_fail; + } + + radio->sinr_th = DEFAULT_SNR_TH; + + retval = set_property(radio, + FM_SEEK_TUNE_RSSI_THRESHOLD_PROP, + DEFAULT_RSSI_TH); + if (retval < 0) { + FMDERR("%s: FM_SEEK_TUNE_RSSI_THRESHOLD_PROP fail error %d\n", + __func__, retval); + goto set_prop_fail; + } + + radio->rssi_th = DEFAULT_RSSI_TH; + + retval = set_property(radio, + FM_RSQ_RSSI_LO_THRESHOLD_PROP, + DEFAULT_AF_RSSI_LOW_TH); + if (retval < 0) { + FMDERR("%s: FM_RSQ_RSSI_LO_THRESHOLD_PROP fail error %d\n", + __func__, retval); + goto set_prop_fail; + } + + radio->af_rssi_th = DEFAULT_AF_RSSI_LOW_TH; + + retval = set_property(radio, + FM_RSQ_INT_SOURCE_PROP, + RSSI_LOW_TH_INT_BIT_MASK); + if (retval < 0) { + FMDERR("%s: FM_RSQ_INT_SOURCE_PROP fail error %d\n", + __func__, retval); + goto set_prop_fail; + } + + retval = set_property(radio, + FM_RDS_INT_FIFO_COUNT_PROP, + FIFO_CNT_16); + if (retval < 0) { + FMDERR("%s: FM_RDS_INT_FIFO_COUNT_PROP fail error %d\n", + __func__, retval); + goto set_prop_fail; + } + + radio->rds_fifo_cnt = FIFO_CNT_16; + +set_prop_fail: + return retval; + +} + +static void init_ssr(struct silabs_fm_device *radio) +{ + int retval = 0; + + mutex_lock(&radio->lock); + + memset(radio->write_buf, 0, WRITE_REG_NUM); + /* + * Configure status gpio to low in active state. When chip is reset for + * some reason, status gpio becomes high since pull-up resistor is + * installed. No need to return error even if it fails, since normal + * FM functionality can still work fine. + */ + + radio->cmd = GPIO_CTL_CMD; + radio->write_buf[0] = GPIO_CTL_CMD; + radio->write_buf[1] = GPIO1_OUTPUT_ENABLE_MASK; + + retval = send_cmd(radio, GPIO_CTL_CMD_LEN); + + if (retval < 0) { + FMDERR("%s: setting Silabs gpio1 as op to chip fail, err %d\n", + __func__, retval); + goto end; + } + + memset(radio->write_buf, 0, WRITE_REG_NUM); + + /* track command that is being sent to chip.*/ + radio->cmd = GPIO_SET_CMD; + radio->write_buf[0] = GPIO_SET_CMD; + radio->write_buf[1] = GPIO_OUTPUT_LOW_MASK; + + retval = send_cmd(radio, GPIO_SET_CMD_LEN); + + if (retval < 0) + FMDERR("%s: setting gpios to low failed, error %d\n", + __func__, retval); + +end: + mutex_unlock(&radio->lock); +} + +static int enable(struct silabs_fm_device *radio) +{ + int retval = 0; + + retval = read_cts_bit(radio); + + if (retval < 0) + return retval; + + mutex_lock(&radio->lock); + + memset(radio->write_buf, 0, WRITE_REG_NUM); + + /* track command that is being sent to chip.*/ + radio->cmd = POWER_UP_CMD; + radio->write_buf[0] = POWER_UP_CMD; + radio->write_buf[1] = ENABLE_GPO2_INT_MASK; + + radio->write_buf[2] = AUDIO_OPMODE_DIGITAL; + + retval = send_cmd(radio, POWER_UP_CMD_LEN); + + if (retval < 0) { + FMDERR("%s: enable failed with error %d\n", __func__, retval); + mutex_unlock(&radio->lock); + goto send_cmd_fail; + } + + mutex_unlock(&radio->lock); + + /* enable interrupts */ + retval = configure_interrupts(radio, ENABLE_STC_RDS_INTERRUPTS); + if (retval < 0) + FMDERR("In %s, configure_interrupts failed with error %d\n", + __func__, retval); + + /* initialize with default configuration */ + retval = initialize_recv(radio); + reset_rds(radio); /* Clear the existing RDS data */ + init_ssr(radio); + if (retval >= 0) { + if (radio->mode == FM_RECV_TURNING_ON) { + FMDBG("In %s, posting SILABS_EVT_RADIO_READY event\n", + __func__); + silabs_fm_q_event(radio, SILABS_EVT_RADIO_READY); + radio->mode = FM_RECV; + } + } +send_cmd_fail: + return retval; + +} + +static int disable(struct silabs_fm_device *radio) +{ + int retval = 0; + + mutex_lock(&radio->lock); + + memset(radio->write_buf, 0, WRITE_REG_NUM); + + /* track command that is being sent to chip. */ + radio->cmd = POWER_DOWN_CMD; + radio->write_buf[0] = POWER_DOWN_CMD; + + retval = send_cmd(radio, POWER_DOWN_CMD_LEN); + if (retval < 0) + FMDERR("%s: disable failed with error %d\n", + __func__, retval); + + mutex_unlock(&radio->lock); + + if (radio->mode == FM_TURNING_OFF || radio->mode == FM_RECV) { + FMDBG("%s: posting SILABS_EVT_RADIO_DISABLED event\n", + __func__); + silabs_fm_q_event(radio, SILABS_EVT_RADIO_DISABLED); + radio->mode = FM_OFF; + } + + return retval; +} + +static int set_chan_spacing(struct silabs_fm_device *radio, u16 spacing) +{ + int retval = 0; + u16 prop_val = 0; + + if (spacing == 0) + prop_val = FM_RX_SPACE_200KHZ; + else if (spacing == 1) + prop_val = FM_RX_SPACE_100KHZ; + else if (spacing == 2) + prop_val = FM_RX_SPACE_50KHZ; + + retval = set_property(radio, FM_SEEK_FREQ_SPACING_PROP, prop_val); + if (retval < 0) + FMDERR("In %s, error setting channel spacing\n", __func__); + else + radio->recv_conf.ch_spacing = spacing; + + return retval; + +} + +static int set_emphasis(struct silabs_fm_device *radio, u16 emp) +{ + int retval = 0; + u16 prop_val = 0; + + if (emp == 0) + prop_val = FM_RX_EMP75; + else if (emp == 1) + prop_val = FM_RX_EMP50; + + retval = set_property(radio, FM_DEEMPHASIS_PROP, prop_val); + if (retval < 0) + FMDERR("In %s, error setting emphasis\n", __func__); + else + radio->recv_conf.emphasis = emp; + + return retval; + +} + +static int tune(struct silabs_fm_device *radio, u32 freq_khz) +{ + int retval = 0; + u16 freq_16bit = (u16)(freq_khz/TUNE_STEP_SIZE); + + FMDBG("In %s, freq is %d\n", __func__, freq_khz); + + /* + * when we are tuning for the first time, we must set digital audio + * properties. + */ + if (radio->first_tune) { + /* I2S mode, rising edge */ + retval = set_property(radio, DIGITAL_OUTPUT_FORMAT_PROP, 0); + if (retval < 0) { + FMDERR("%s: set output format prop failed, error %d\n", + __func__, retval); + goto set_prop_fail; + } + + /* 48khz sample rate */ + retval = set_property(radio, + DIGITAL_OUTPUT_SAMPLE_RATE_PROP, + SAMPLE_RATE_48_KHZ); + if (retval < 0) { + FMDERR("%s: set sample rate prop failed, error %d\n", + __func__, retval); + goto set_prop_fail; + } + radio->first_tune = false; + } + + mutex_lock(&radio->lock); + + memset(radio->write_buf, 0, WRITE_REG_NUM); + + /* track command that is being sent to chip.*/ + radio->cmd = FM_TUNE_FREQ_CMD; + + radio->write_buf[0] = FM_TUNE_FREQ_CMD; + /* reserved */ + radio->write_buf[1] = 0; + /* freq high byte */ + radio->write_buf[2] = HIGH_BYTE_16BIT(freq_16bit); + /* freq low byte */ + radio->write_buf[3] = LOW_BYTE_16BIT(freq_16bit); + radio->write_buf[4] = 0; + + FMDBG("In %s, radio->write_buf[2] %x, radio->write_buf[3]%x\n", + __func__, radio->write_buf[2], radio->write_buf[3]); + + retval = send_cmd(radio, TUNE_FREQ_CMD_LEN); + if (retval < 0) + FMDERR("In %s, tune failed with error %d\n", __func__, retval); + + mutex_unlock(&radio->lock); + +set_prop_fail: + return retval; +} + +static int silabs_seek(struct silabs_fm_device *radio, int dir, int wrap) +{ + int retval = 0; + + mutex_lock(&radio->lock); + + memset(radio->write_buf, 0, WRITE_REG_NUM); + + /* track command that is being sent to chip. */ + radio->cmd = FM_SEEK_START_CMD; + + radio->write_buf[0] = FM_SEEK_START_CMD; + if (wrap) + radio->write_buf[1] = SEEK_WRAP_MASK; + + if (dir == SRCH_DIR_UP) + radio->write_buf[1] |= SEEK_UP_MASK; + + retval = send_cmd(radio, SEEK_CMD_LEN); + if (retval < 0) + FMDERR("In %s, seek failed with error %d\n", __func__, retval); + + mutex_unlock(&radio->lock); + return retval; +} + + +static int cancel_seek(struct silabs_fm_device *radio) +{ + int retval = 0; + + mutex_lock(&radio->lock); + + memset(radio->write_buf, 0, WRITE_REG_NUM); + + /* track command that is being sent to chip. */ + radio->cmd = FM_TUNE_STATUS_CMD; + + radio->write_buf[0] = FM_TUNE_STATUS_CMD; + radio->write_buf[1] = CANCEL_SEEK_MASK; + + retval = send_cmd(radio, TUNE_STATUS_CMD_LEN); + if (retval < 0) + FMDERR("%s: cancel_seek failed, error %d\n", __func__, retval); + + mutex_unlock(&radio->lock); + radio->is_search_cancelled = true; + + return retval; + +} + +static void silabs_fm_q_event(struct silabs_fm_device *radio, + enum silabs_evt_t event) +{ + + struct kfifo *data_b; + unsigned char evt = event; + + data_b = &radio->data_buf[SILABS_FM_BUF_EVENTS]; + + FMDBG("updating event_q with event %x\n", event); + if (kfifo_in_locked(data_b, + &evt, + 1, + &radio->buf_lock[SILABS_FM_BUF_EVENTS])) + wake_up_interruptible(&radio->event_queue); +} + +static int clear_stc_int(struct silabs_fm_device *radio) +{ + int retval = 0; + + mutex_lock(&radio->lock); + + memset(radio->write_buf, 0, WRITE_REG_NUM); + + /* track command that is being sent to chip. */ + radio->cmd = FM_TUNE_STATUS_CMD; + + radio->write_buf[0] = FM_TUNE_STATUS_CMD; + radio->write_buf[1] = INTACK_MASK; + + retval = send_cmd(radio, TUNE_STATUS_CMD_LEN); + if (retval < 0) + FMDERR("%s: clear_stc_int fail, error %d\n", __func__, retval); + + mutex_unlock(&radio->lock); + + return retval; +} + +static void silabs_interrupts_handler(struct silabs_fm_device *radio) +{ + int retval = 0; + u8 rssi = 0; + + if (unlikely(radio == NULL)) { + FMDERR("%s:radio is null", __func__); + return; + } + + FMDBG("%s: ISR fired for cmd %x, reading status bytes\n", + __func__, radio->cmd); + + /* Get int status to know which interrupt is this(STC/RDS/etc) */ + retval = get_int_status(radio); + + if (retval < 0) { + FMDERR("%s: failure reading the resp from soc with error %d\n", + __func__, retval); + return; + } + FMDBG("%s: successfully read the resp from soc, status byte is %x\n", + __func__, radio->read_buf[0]); + + + if (radio->read_buf[0] & STC_INT_BIT_MASK) { + FMDBG("%s: STC bit set for cmd %x\n", __func__, radio->cmd); + if (radio->seek_tune_status == TUNE_PENDING) { + FMDBG("In %s, posting SILABS_EVT_TUNE_SUCC event\n", + __func__); + silabs_fm_q_event(radio, SILABS_EVT_TUNE_SUCC); + radio->seek_tune_status = NO_SEEK_TUNE_PENDING; + radio->is_af_tune_in_progress = false; + } else if (radio->seek_tune_status == SEEK_PENDING) { + FMDBG("%s: posting SILABS_EVT_SEEK_COMPLETE event\n", + __func__); + silabs_fm_q_event(radio, SILABS_EVT_SEEK_COMPLETE); + /* post tune comp evt since seek results in a tune.*/ + FMDBG("%s: posting SILABS_EVT_TUNE_SUCC\n", + __func__); + silabs_fm_q_event(radio, SILABS_EVT_TUNE_SUCC); + radio->seek_tune_status = NO_SEEK_TUNE_PENDING; + + } else if (radio->seek_tune_status == SCAN_PENDING) { + /* + * when scan is pending and STC int is set, signal + * so that scan can proceed + */ + FMDBG("In %s, signalling scan thread\n", __func__); + complete(&radio->sync_req_done); + } else if (radio->is_af_tune_in_progress == true) { + /* + * when AF tune is going on and STC int is set, signal + * so that AF tune can proceed. + */ + FMDBG("In %s, signalling AF tune thread\n", __func__); + complete(&radio->sync_req_done); + } + /* clear the STC interrupt. */ + clear_stc_int(radio); + reset_rds(radio); /* Clear the existing RDS data */ + return; + } + + if (radio->read_buf[0] & RSQ_INT_BIT_MASK) { + FMDBG("RSQ interrupt received, clearing the RSQ int bit\n"); + + /* clear RSQ interrupt bits until AF tune is complete. */ + (void)get_rssi(radio, &rssi); + /* Don't process RSQ until AF tune is complete. */ + if (radio->is_af_tune_in_progress == true) + return; + + if (radio->is_af_jump_enabled && + radio->af_info2.size != 0 && + rssi <= radio->af_rssi_th) { + + radio->is_af_tune_in_progress = true; + FMDBG("%s: Queuing to AF work Q, freq %u, rssi %u\n", + __func__, radio->tuned_freq_khz, rssi); + queue_delayed_work(radio->wqueue_af, &radio->work_af, + msecs_to_jiffies(SILABS_DELAY_MSEC)); + } + return; + } + + if (radio->read_buf[0] & RDS_INT_BIT_MASK) { + FMDBG("RDS interrupt received\n"); + /* Don't process RDS until AF tune is complete. */ + if (radio->is_af_tune_in_progress == true) { + /* get PI only */ + get_rds_status(radio); + pi_handler(radio, radio->block[0]); + return; + } + schedule_work(&radio->rds_worker); + return; + } +} + +static void read_int_stat(struct work_struct *work) +{ + struct silabs_fm_device *radio; + + radio = container_of(work, struct silabs_fm_device, work.work); + + silabs_interrupts_handler(radio); +} + +static void silabs_fm_disable_irq(struct silabs_fm_device *radio) +{ + int irq; + + irq = radio->irq; + disable_irq_wake(irq); + free_irq(irq, radio); + + irq = radio->status_irq; + disable_irq_wake(irq); + free_irq(irq, radio); + + cancel_work_sync(&radio->rds_worker); + flush_workqueue(radio->wqueue_rds); + cancel_delayed_work_sync(&radio->work); + flush_workqueue(radio->wqueue); + cancel_delayed_work_sync(&radio->work_scan); + flush_workqueue(radio->wqueue_scan); + cancel_delayed_work_sync(&radio->work_af); + flush_workqueue(radio->wqueue_af); +} + +static irqreturn_t silabs_fm_isr(int irq, void *dev_id) +{ + struct silabs_fm_device *radio = dev_id; + /* + * The call to queue_delayed_work ensures that a minimum delay + * (in jiffies) passes before the work is actually executed. The return + * value from the function is nonzero if the work_struct was actually + * added to queue (otherwise, it may have already been there and will + * not be added a second time). + */ + + queue_delayed_work(radio->wqueue, &radio->work, + msecs_to_jiffies(SILABS_DELAY_MSEC)); + + return IRQ_HANDLED; +} + +static irqreturn_t silabs_fm_status_isr(int irq, void *dev_id) +{ + struct silabs_fm_device *radio = dev_id; + + if (radio->mode == FM_TURNING_OFF || radio->mode == FM_RECV) { + FMDERR("%s: chip in bad state, posting DISABLED event\n", + __func__); + silabs_fm_q_event(radio, SILABS_EVT_RADIO_DISABLED); + radio->mode = FM_OFF; + } + + return IRQ_HANDLED; +} + +static int silabs_fm_request_irq(struct silabs_fm_device *radio) +{ + int retval; + int irq; + + irq = radio->irq; + + /* + * Use request_any_context_irq, So that it might work for nested or + * nested interrupts. + */ + retval = request_any_context_irq(irq, silabs_fm_isr, + IRQ_TYPE_EDGE_FALLING, "fm interrupt", radio); + if (retval < 0) { + FMDERR("Couldn't acquire FM gpio %d\n", irq); + return retval; + } + FMDBG("FM GPIO %d registered\n", irq); + + retval = enable_irq_wake(irq); + if (retval < 0) { + FMDERR("Could not enable FM interrupt\n "); + free_irq(irq, radio); + return retval; + } + + irq = radio->status_irq; + + retval = request_any_context_irq(irq, silabs_fm_status_isr, + IRQ_TYPE_EDGE_RISING, "fm status interrupt", + radio); + if (retval < 0) { + FMDERR("Couldn't acquire FM status gpio %d\n", irq); + /* Do not error out for status int. FM can work without it. */ + return 0; + } + FMDBG("FM status GPIO %d registered\n", irq); + + retval = enable_irq_wake(irq); + if (retval < 0) { + FMDERR("Could not enable FM status interrupt\n "); + free_irq(irq, radio); + /* Do not error out for status int. FM can work without it. */ + return 0; + } + + return retval; +} + +static int silabs_fm_fops_open(struct file *file) +{ + struct silabs_fm_device *radio = video_get_drvdata(video_devdata(file)); + int retval = -ENODEV; + + if (unlikely(radio == NULL)) { + FMDERR("%s:radio is null", __func__); + return -EINVAL; + } + + INIT_DELAYED_WORK(&radio->work, read_int_stat); + INIT_DELAYED_WORK(&radio->work_scan, silabs_scan); + INIT_DELAYED_WORK(&radio->work_af, silabs_af_tune); + INIT_WORK(&radio->rds_worker, rds_handler); + + init_completion(&radio->sync_req_done); + if (!atomic_dec_and_test(&radio->users)) { + FMDBG("%s: Device already in use. Try again later", __func__); + atomic_inc(&radio->users); + return -EBUSY; + } + + /* initial gpio pin config & Power up */ + retval = silabs_fm_power_cfg(radio, TURNING_ON); + if (retval) { + FMDERR("%s: failed config gpio & pmic\n", __func__); + goto open_err_setup; + } + radio->irq = gpio_to_irq(radio->int_gpio); + + if (radio->irq < 0) { + FMDERR("%s: gpio_to_irq returned %d\n", __func__, radio->irq); + goto open_err_req_irq; + } + + FMDBG("irq number is = %d\n", radio->irq); + + if (radio->status_gpio > 0) { + radio->status_irq = gpio_to_irq(radio->status_gpio); + + if (radio->status_irq < 0) { + FMDERR("%s: gpio_to_irq returned %d for status gpio\n", + __func__, radio->irq); + goto open_err_req_irq; + } + + FMDBG("status irq number is = %d\n", radio->status_irq); + } + + /* enable irq */ + retval = silabs_fm_request_irq(radio); + if (retval < 0) { + FMDERR("%s: failed to request irq\n", __func__); + goto open_err_req_irq; + } + + radio->handle_irq = 0; + radio->first_tune = true; + return 0; + +open_err_req_irq: + silabs_fm_power_cfg(radio, TURNING_OFF); +open_err_setup: + radio->handle_irq = 1; + atomic_inc(&radio->users); + return retval; +} + +static int silabs_fm_fops_release(struct file *file) +{ + struct silabs_fm_device *radio = video_get_drvdata(video_devdata(file)); + int retval = 0; + + if (unlikely(radio == NULL)) + return -EINVAL; + + if (radio->mode == FM_RECV) { + radio->mode = FM_OFF; + retval = disable(radio); + if (retval < 0) + FMDERR("Err on disable FM %d\n", retval); + } + + FMDBG("%s, Disabling the IRQs\n", __func__); + /* disable irq */ + silabs_fm_disable_irq(radio); + + retval = silabs_fm_power_cfg(radio, TURNING_OFF); + if (retval < 0) + FMDERR("%s: failed to configure gpios\n", __func__); + + atomic_inc(&radio->users); + + return retval; +} + +static struct v4l2_queryctrl silabs_fm_v4l2_queryctrl[] = { + { + .id = V4L2_CID_AUDIO_VOLUME, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Volume", + .minimum = 0, + .maximum = 15, + .step = 1, + .default_value = 15, + }, + { + .id = V4L2_CID_AUDIO_BALANCE, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + { + .id = V4L2_CID_AUDIO_BASS, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + { + .id = V4L2_CID_AUDIO_TREBLE, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + { + .id = V4L2_CID_AUDIO_MUTE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, + { + .id = V4L2_CID_AUDIO_LOUDNESS, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + { + .id = V4L2_CID_PRIVATE_SILABS_SRCHON, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Search on/off", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + + }, + { + .id = V4L2_CID_PRIVATE_SILABS_STATE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "radio 0ff/rx/tx/reset", + .minimum = 0, + .maximum = 3, + .step = 1, + .default_value = 1, + + }, + { + .id = V4L2_CID_PRIVATE_SILABS_REGION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "radio standard", + .minimum = 0, + .maximum = 2, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_PRIVATE_SILABS_SIGNAL_TH, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Signal Threshold", + .minimum = 0x80, + .maximum = 0x7F, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_PRIVATE_SILABS_EMPHASIS, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Emphasis", + .minimum = 0, + .maximum = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_PRIVATE_SILABS_RDS_STD, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "RDS standard", + .minimum = 0, + .maximum = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_PRIVATE_SILABS_SPACING, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Channel spacing", + .minimum = 0, + .maximum = 2, + .default_value = 0, + }, + { + .id = V4L2_CID_PRIVATE_SILABS_RDSON, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "RDS on/off", + .minimum = 0, + .maximum = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_PRIVATE_SILABS_RDSGROUP_MASK, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "RDS group mask", + .minimum = 0, + .maximum = 0xFFFFFFFF, + .default_value = 0, + }, + { + .id = V4L2_CID_PRIVATE_SILABS_RDSGROUP_PROC, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "RDS processing", + .minimum = 0, + .maximum = 0xFF, + .default_value = 0, + }, + { + .id = V4L2_CID_PRIVATE_SILABS_RDSD_BUF, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "RDS data groups to buffer", + .minimum = 1, + .maximum = 21, + .default_value = 0, + }, + { + .id = V4L2_CID_PRIVATE_SILABS_PSALL, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "pass all ps strings", + .minimum = 0, + .maximum = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_PRIVATE_SILABS_LP_MODE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Low power mode", + .minimum = 0, + .maximum = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_PRIVATE_SILABS_ANTENNA, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "headset/internal", + .minimum = 0, + .maximum = 1, + .default_value = 0, + }, + +}; + +static int silabs_fm_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *capability) +{ + struct silabs_fm_device *radio = video_get_drvdata(video_devdata(file)); + + if (unlikely(radio == NULL)) { + FMDERR("%s:radio is null", __func__); + return -EINVAL; + } + + if (unlikely(capability == NULL)) { + FMDERR("%s:capability is null", __func__); + return -EINVAL; + } + + strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver)); + strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card)); + snprintf(capability->bus_info, 4, "I2C"); + capability->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; + + return 0; +} + +static int silabs_fm_vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + unsigned char i; + int retval = -EINVAL; + + if (unlikely(qc == NULL)) { + FMDERR("%s:qc is null", __func__); + return -EINVAL; + } + + + for (i = 0; i < ARRAY_SIZE(silabs_fm_v4l2_queryctrl); i++) { + if (qc->id && qc->id == silabs_fm_v4l2_queryctrl[i].id) { + memcpy(qc, &(silabs_fm_v4l2_queryctrl[i]), + sizeof(*qc)); + retval = 0; + break; + } + } + if (retval < 0) + FMDERR("query conv4ltrol failed with %d\n", retval); + + return retval; +} + +static int silabs_fm_vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct silabs_fm_device *radio = video_get_drvdata(video_devdata(file)); + int retval = 0; + + if (unlikely(radio == NULL)) { + FMDERR(":radio is null"); + return -EINVAL; + } + + if (ctrl == NULL) { + FMDERR("%s, v4l2 ctrl is null\n", __func__); + return -EINVAL; + } + + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: + break; + case V4L2_CID_AUDIO_MUTE: + break; + + case V4L2_CID_PRIVATE_SILABS_RDSGROUP_PROC: + ctrl->value = 0; + retval = 0; + break; + case V4L2_CID_PRIVATE_SILABS_GET_SINR: + + mutex_lock(&radio->lock); + radio->cmd = FM_TUNE_STATUS_CMD; + + radio->write_buf[0] = FM_TUNE_STATUS_CMD; + radio->write_buf[1] = 0; + + retval = send_cmd(radio, TUNE_STATUS_CMD_LEN); + if (retval < 0) { + FMDERR("%s: FM_TUNE_STATUS_CMD failed with error %d\n", + __func__, retval); + mutex_unlock(&radio->lock); + break; + } + + /* sinr */ + ctrl->value = radio->read_buf[5]; + mutex_unlock(&radio->lock); + FMDBG("%s: V4L2_CID_PRIVATE_SILABS_GET_SINR, val %d\n", + __func__, ctrl->value); + break; + case V4L2_CID_PRIVATE_SILABS_SINR_THRESHOLD: + FMDBG("%s: V4L2_CID_PRIVATE_SILABS_SINR_THRESHOLD, val %d\n", + __func__, radio->sinr_th); + + ctrl->value = radio->sinr_th; + break; + case V4L2_CID_PRIVATE_SILABS_RSSI_TH: + FMDBG("%s: V4L2_CID_PRIVATE_SILABS_RSSI_TH, val %d\n", + __func__, radio->rssi_th); + + ctrl->value = radio->rssi_th; + break; + case V4L2_CID_PRIVATE_SILABS_AF_JUMP_RSSI_TH: + FMDBG("%s: V4L2_CID_PRIVATE_SILABS_AF_JUMP_RSSI_TH, val %d\n", + __func__, radio->af_rssi_th); + + ctrl->value = radio->af_rssi_th; + break; + + case V4L2_CID_PRIVATE_SILABS_RDSD_BUF: + FMDBG("%s: V4L2_CID_PRIVATE_SILABS_RDSD_BUF, val %d\n", + __func__, radio->rds_fifo_cnt); + + ctrl->value = radio->rds_fifo_cnt; + break; + case V4L2_CID_PRIVATE_SILABS_AF_RMSSI_SAMPLES: + FMDBG("%s: V4L2_CID_PRIVATE_SILABS_AF_RMSSI_SAMPLES, val %d\n", + __func__, ctrl->value); + ctrl->value = radio->af_wait_timer; + break; + default: + retval = -EINVAL; + break; + } + + if (retval < 0) + FMDERR("get control failed with %d, id: %x\n", + retval, ctrl->id); + + return retval; +} + +static int silabs_fm_vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct silabs_fm_device *radio = video_get_drvdata(video_devdata(file)); + int retval = 0; + + if (unlikely(radio == NULL)) { + FMDERR("%s:radio is null", __func__); + return -EINVAL; + } + + if (unlikely(ctrl == NULL)) { + FMDERR("%s:ctrl is null", __func__); + return -EINVAL; + } + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if ((ctrl->value >= 0) && (ctrl->value <= 3)) { + set_mute_mode(radio, ctrl->value); + } else { + FMDERR("%s: Mute mode states are out of range\n", + __func__); + retval = -EINVAL; + goto end; + } + break; + case V4L2_CID_PRIVATE_SILABS_STATE: + /* check if already on */ + if (ctrl->value == FM_RECV) { + if (is_enable_rx_possible(radio) != 0) { + FMDERR("%s: fm is not in proper state\n", + __func__); + retval = -EINVAL; + goto end; + } + radio->mode = FM_RECV_TURNING_ON; + + retval = enable(radio); + if (retval < 0) { + FMDERR("Error while enabling RECV FM %d\n", + retval); + radio->mode = FM_OFF; + goto end; + } + } else if (ctrl->value == FM_OFF) { + retval = configure_interrupts(radio, + DISABLE_ALL_INTERRUPTS); + if (retval < 0) + FMDERR("configure_interrupts failed %d\n", + retval); + flush_workqueue(radio->wqueue); + cancel_work_sync(&radio->rds_worker); + flush_workqueue(radio->wqueue_rds); + radio->mode = FM_TURNING_OFF; + retval = disable(radio); + if (retval < 0) { + FMDERR("Err on disable recv FM %d\n", retval); + radio->mode = FM_RECV; + goto end; + } + } + break; + case V4L2_CID_PRIVATE_SILABS_SPACING: + if (!is_valid_chan_spacing(ctrl->value)) { + retval = -EINVAL; + FMDERR("%s: channel spacing is not valid\n", __func__); + goto end; + } + retval = set_chan_spacing(radio, (u16)ctrl->value); + if (retval < 0) { + FMDERR("Error in setting channel spacing\n"); + goto end; + } + break; + case V4L2_CID_PRIVATE_SILABS_EMPHASIS: + retval = set_emphasis(radio, (u16)ctrl->value); + if (retval < 0) { + FMDERR("Error in setting emphasis\n"); + goto end; + } + break; + case V4L2_CID_PRIVATE_SILABS_ANTENNA: + if (ctrl->value == 0 || ctrl->value == 1) { + retval = set_property(radio, + FM_ANTENNA_INPUT_PROP, + ctrl->value); + if (retval < 0) + FMDERR("Setting antenna type failed\n"); + else + radio->antenna = ctrl->value; + } else { + retval = -EINVAL; + FMDERR("%s: antenna type is not valid\n", __func__); + goto end; + } + break; + case V4L2_CID_PRIVATE_SILABS_SOFT_MUTE: + retval = 0; + break; + case V4L2_CID_PRIVATE_SILABS_REGION: + case V4L2_CID_PRIVATE_SILABS_SRCH_ALGORITHM: + case V4L2_CID_PRIVATE_SILABS_SET_AUDIO_PATH: + /* + * These private controls are place holders to keep the + * driver compatible with changes done in the frameworks + * which are specific to TAVARUA. + */ + retval = 0; + break; + case V4L2_CID_PRIVATE_SILABS_SRCHMODE: + if (is_valid_srch_mode(ctrl->value)) { + radio->g_search_mode = ctrl->value; + } else { + FMDERR("%s: srch mode is not valid\n", __func__); + retval = -EINVAL; + goto end; + } + break; + case V4L2_CID_PRIVATE_SILABS_SCANDWELL: + if ((ctrl->value >= MIN_DWELL_TIME) && + (ctrl->value <= MAX_DWELL_TIME)) { + radio->dwell_time_sec = ctrl->value; + } else { + FMDERR("%s: scandwell period is not valid\n", __func__); + retval = -EINVAL; + } + break; + case V4L2_CID_PRIVATE_SILABS_SRCHON: + silabs_search(radio, (bool)ctrl->value); + break; + case V4L2_CID_PRIVATE_SILABS_RDS_STD: + return retval; + case V4L2_CID_PRIVATE_SILABS_RDSON: + return retval; + case V4L2_CID_PRIVATE_SILABS_RDSGROUP_MASK: + retval = set_property(radio, + FM_RDS_INT_SOURCE_PROP, + RDS_INT_BIT); + if (retval < 0) { + FMDERR("In %s, FM_RDS_INT_SOURCE_PROP failed %d\n", + __func__, retval); + goto end; + } + break; + case V4L2_CID_PRIVATE_SILABS_RDSD_BUF: + if (is_valid_rds_fifo_cnt(ctrl->value)) { + retval = set_property(radio, + FM_RDS_INT_FIFO_COUNT_PROP, + ctrl->value); + if (retval < 0) { + FMDERR("%s: setting rds fifo cnt failed %d\n", + __func__, retval); + goto end; + } + + radio->rds_fifo_cnt = ctrl->value; + } else + retval = -EINVAL; + + break; + case V4L2_CID_PRIVATE_SILABS_RDSGROUP_PROC: + /* Enabled all with uncorrectable */ + retval = set_property(radio, + FM_RDS_CONFIG_PROP, + UNCORRECTABLE_RDS_EN); + if (retval < 0) { + FMDERR("In %s, FM_RDS_CONFIG_PROP failed %d\n", + __func__, retval); + goto end; + } + break; + case V4L2_CID_PRIVATE_SILABS_LP_MODE: + FMDBG("In %s, V4L2_CID_PRIVATE_SILABS_LP_MODE, val is %d\n", + __func__, ctrl->value); + if (ctrl->value) { + /* disable RDS interrupts */ + retval = configure_interrupts(radio, + ENABLE_RDS_INTERRUPTS); + radio->lp_mode = true; + } else { + /* enable RDS interrupts */ + retval = configure_interrupts(radio, + DISABLE_RDS_INTERRUPTS); + radio->lp_mode = false; + } + + if (retval < 0) { + FMDERR("In %s, setting low power mode failed %d\n", + __func__, retval); + goto end; + } + break; + case V4L2_CID_PRIVATE_SILABS_AF_JUMP: + FMDBG("%s: V4L2_CID_PRIVATE_SILABS_AF_JUMP, val is %d\n", + __func__, ctrl->value); + if (ctrl->value) + /* enable RSQ interrupts */ + retval = configure_interrupts(radio, + ENABLE_RSQ_INTERRUPTS); + else + /* disable RSQ interrupts */ + retval = configure_interrupts(radio, + DISABLE_RSQ_INTERRUPTS); + if (retval < 0) { + FMDERR("%s: setting AF jump mode failed %d\n", + __func__, retval); + goto end; + } + /* Save the AF jump state */ + radio->is_af_jump_enabled = ctrl->value; + break; + case V4L2_CID_PRIVATE_SILABS_SINR_THRESHOLD: + FMDBG("%s: V4L2_CID_PRIVATE_SILABS_SINR_THRESHOLD, val is %d\n", + __func__, ctrl->value); + if (is_valid_sinr(ctrl->value)) { + retval = set_property(radio, + FM_SEEK_TUNE_SNR_THRESHOLD_PROP, + ctrl->value); + if (retval < 0) { + FMDERR("%s: setting sinr th failed, error %d\n", + __func__, retval); + goto end; + } + + radio->sinr_th = ctrl->value; + + } else { + retval = -EINVAL; + FMDERR("%s: Invalid sinr\n", __func__); + } + break; + case V4L2_CID_PRIVATE_SILABS_RSSI_TH: + FMDBG("%s: V4L2_CID_PRIVATE_SILABS_RSSI_TH, val is %d\n", + __func__, ctrl->value); + if (is_valid_rssi(ctrl->value)) { + retval = set_property(radio, + FM_SEEK_TUNE_RSSI_THRESHOLD_PROP, + ctrl->value); + if (retval < 0) { + FMDERR("%s: setting rssi th failed, error %d\n", + __func__, retval); + goto end; + } + + radio->rssi_th = ctrl->value; + + } else { + retval = -EINVAL; + FMDERR("%s: Invalid sinr\n", __func__); + } + break; + case V4L2_CID_PRIVATE_SILABS_AF_JUMP_RSSI_TH: + FMDBG("%s: V4L2_CID_PRIVATE_SILABS_AF_JUMP_RSSI_TH, val %d\n", + __func__, ctrl->value); + if (is_valid_rssi(ctrl->value)) { + retval = set_property(radio, + FM_RSQ_RSSI_LO_THRESHOLD_PROP, + ctrl->value); + if (retval < 0) { + FMDERR("%s: setting af rssi th failed err %d\n", + __func__, retval); + goto end; + } + + radio->af_rssi_th = ctrl->value; + + } else { + retval = -EINVAL; + FMDERR("%s: Invalid sinr\n", __func__); + } + + break; + case V4L2_CID_PRIVATE_SILABS_AF_RMSSI_SAMPLES: + FMDBG("%s: V4L2_CID_PRIVATE_SILABS_AF_RMSSI_SAMPLES, val %d\n", + __func__, ctrl->value); + if ((ctrl->value >= 0) || (ctrl->value <= MAX_AF_WAIT_SEC)) + radio->af_wait_timer = ctrl->value; + break; + case V4L2_CID_PRIVATE_SILABS_SRCH_CNT: + retval = 0; + break; + default: + retval = -EINVAL; + break; + } + + if (retval < 0) + FMDERR("set control failed with %d, id:%x\n", retval, ctrl->id); + +end: + return retval; +} + +static int silabs_fm_vidioc_s_tuner(struct file *file, void *priv, + const struct v4l2_tuner *tuner) +{ + struct silabs_fm_device *radio = video_get_drvdata(video_devdata(file)); + int retval = 0; + u16 prop_val = 0; + + if (unlikely(radio == NULL)) { + FMDERR("%s:radio is null", __func__); + return -EINVAL; + } + + if (unlikely(tuner == NULL)) { + FMDERR("%s:tuner is null", __func__); + return -EINVAL; + } + + if (tuner->index > 0) + return -EINVAL; + + FMDBG("In %s, setting top and bottom band limits\n", __func__); + + prop_val = (u16)((tuner->rangelow / TUNE_PARAM) / TUNE_STEP_SIZE); + FMDBG("In %s, tuner->rangelow is %d, setting bottom band to %d\n", + __func__, tuner->rangelow, prop_val); + + retval = set_property(radio, FM_SEEK_BAND_BOTTOM_PROP, prop_val); + if (retval < 0) + FMDERR("In %s, error %d setting lower limit freq\n", + __func__, retval); + else + radio->recv_conf.band_low_limit = prop_val; + + prop_val = (u16)((tuner->rangehigh / TUNE_PARAM) / TUNE_STEP_SIZE); + FMDBG("In %s, tuner->rangehigh is %d, setting top band to %d\n", + __func__, tuner->rangehigh, prop_val); + + retval = set_property(radio, FM_SEEK_BAND_TOP_PROP, prop_val); + if (retval < 0) + FMDERR("In %s, error %d setting upper limit freq\n", + __func__, retval); + else + radio->recv_conf.band_high_limit = prop_val; + + if (retval < 0) + FMDERR(": set tuner failed with %d\n", retval); + + return retval; +} + +static int silabs_fm_vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *tuner) +{ + int retval = 0; + struct silabs_fm_device *radio = video_get_drvdata(video_devdata(file)); + + if (unlikely(radio == NULL)) { + FMDERR(":radio is null"); + return -EINVAL; + } + if (tuner == NULL) { + FMDERR("%s, tuner is null\n", __func__); + return -EINVAL; + } + if (tuner->index > 0) { + FMDERR("Invalid Tuner Index"); + return -EINVAL; + } + + mutex_lock(&radio->lock); + + memset(radio->write_buf, 0, WRITE_REG_NUM); + + /* track command that is being sent to chip. */ + radio->cmd = FM_TUNE_STATUS_CMD; + + radio->write_buf[0] = FM_TUNE_STATUS_CMD; + radio->write_buf[1] = 0; + + retval = send_cmd(radio, TUNE_STATUS_CMD_LEN); + if (retval < 0) { + FMDERR("In %s, FM_TUNE_STATUS_CMD failed with error %d\n", + __func__, retval); + mutex_unlock(&radio->lock); + goto get_prop_fail; + } + + /* rssi */ + tuner->signal = radio->read_buf[4]; + mutex_unlock(&radio->lock); + + retval = get_property(radio, + FM_SEEK_BAND_BOTTOM_PROP, + &radio->recv_conf.band_low_limit); + if (retval < 0) { + FMDERR("%s: get FM_SEEK_BAND_BOTTOM_PROP failed, error %d\n", + __func__, retval); + goto get_prop_fail; + } + + FMDBG("In %s, radio->recv_conf.band_low_limit is %d\n", + __func__, radio->recv_conf.band_low_limit); + retval = get_property(radio, + FM_SEEK_BAND_TOP_PROP, + &radio->recv_conf.band_high_limit); + if (retval < 0) { + FMDERR("In %s, get FM_SEEK_BAND_TOP_PROP failed, error %d\n", + __func__, retval); + goto get_prop_fail; + } + FMDBG("In %s, radio->recv_conf.band_high_limit is %d\n", + __func__, radio->recv_conf.band_high_limit); + + tuner->type = V4L2_TUNER_RADIO; + tuner->rangelow = + radio->recv_conf.band_low_limit * TUNE_STEP_SIZE * TUNE_PARAM; + tuner->rangehigh = + radio->recv_conf.band_high_limit * TUNE_STEP_SIZE * TUNE_PARAM; + tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; + tuner->capability = V4L2_TUNER_CAP_LOW; + + tuner->audmode = 0; + tuner->afc = 0; + +get_prop_fail: + return retval; +} + +static int silabs_fm_vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *freq) +{ + struct silabs_fm_device *radio = video_get_drvdata(video_devdata(file)); + u32 f; + u8 snr, rssi; + int retval = 0; + + if (unlikely(radio == NULL)) { + FMDERR(":radio is null"); + return -EINVAL; + } + + if (freq == NULL) { + FMDERR("%s, v4l2 freq is null\n", __func__); + return -EINVAL; + } + + mutex_lock(&radio->lock); + memset(radio->write_buf, 0, WRITE_REG_NUM); + + /* track command that is being sent to chip. */ + radio->cmd = FM_TUNE_STATUS_CMD; + + radio->write_buf[0] = FM_TUNE_STATUS_CMD; + radio->write_buf[1] = 0; + + retval = send_cmd(radio, TUNE_STATUS_CMD_LEN); + if (retval < 0) { + FMDERR("In %s, get station freq cmd failed with error %d\n", + __func__, retval); + mutex_unlock(&radio->lock); + goto send_cmd_fail; + } + + f = (radio->read_buf[2] << 8) + radio->read_buf[3]; + freq->frequency = f * TUNE_PARAM * TUNE_STEP_SIZE; + radio->tuned_freq_khz = f * TUNE_STEP_SIZE; + + rssi = radio->read_buf[4]; + snr = radio->read_buf[5]; + mutex_unlock(&radio->lock); + + FMDBG("In %s, freq is %d, rssi %u, snr %u\n", + __func__, f * TUNE_STEP_SIZE, rssi, snr); + +send_cmd_fail: + return retval; +} + +static int silabs_fm_vidioc_s_frequency(struct file *file, void *priv, + const struct v4l2_frequency *freq) +{ + struct silabs_fm_device *radio = video_get_drvdata(video_devdata(file)); + int retval = -1; + u32 f = 0; + + if (unlikely(radio == NULL)) { + FMDERR("%s:radio is null", __func__); + return -EINVAL; + } + + if (unlikely(freq == NULL)) { + FMDERR("%s:freq is null", __func__); + return -EINVAL; + } + + if (freq->type != V4L2_TUNER_RADIO) + return -EINVAL; + + f = (freq->frequency)/TUNE_PARAM; + + FMDBG("Calling tune with freq %u\n", f); + + radio->seek_tune_status = TUNE_PENDING; + + retval = tune(radio, f); + + /* save the current frequency if tune is successful. */ + if (retval > 0) { + radio->tuned_freq_khz = f; + /* Clear AF list */ + reset_af_info(radio); + cancel_delayed_work_sync(&radio->work_af); + flush_workqueue(radio->wqueue_af); + } + + return retval; +} + +static int silabs_fm_vidioc_s_hw_freq_seek(struct file *file, void *priv, + const struct v4l2_hw_freq_seek *seek) +{ + struct silabs_fm_device *radio = video_get_drvdata(video_devdata(file)); + int dir; + int retval = 0; + + if (unlikely(radio == NULL)) { + FMDERR("%s:radio is null", __func__); + return -EINVAL; + } + + if (unlikely(seek == NULL)) { + FMDERR("%s:seek is null", __func__); + return -EINVAL; + } + + if (seek->seek_upward) + dir = SRCH_DIR_UP; + else + dir = SRCH_DIR_DOWN; + + radio->is_search_cancelled = false; + + if (radio->g_search_mode == SEEK) { + /* seek */ + FMDBG("starting seek\n"); + + radio->seek_tune_status = SEEK_PENDING; + + retval = silabs_seek(radio, dir, WRAP_ENABLE); + + } else if ((radio->g_search_mode == SCAN) || + (radio->g_search_mode == SCAN_FOR_STRONG)) { + /* scan */ + if (radio->g_search_mode == SCAN_FOR_STRONG) { + FMDBG("starting search list\n"); + memset(&radio->srch_list, 0, + sizeof(struct silabs_srch_list_compl)); + } else { + FMDBG("starting scan\n"); + } + silabs_search(radio, START_SCAN); + + } else { + retval = -EINVAL; + FMDERR("In %s, invalid search mode %d\n", + __func__, radio->g_search_mode); + } + + if (retval > 0) { + /* Clear AF list */ + reset_af_info(radio); + cancel_delayed_work_sync(&radio->work_af); + flush_workqueue(radio->wqueue_af); + } + + return retval; +} + +static int silabs_fm_vidioc_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buffer) +{ + + struct silabs_fm_device *radio = video_get_drvdata(video_devdata(file)); + enum silabs_buf_t buf_type = -1; + u8 buf_fifo[STD_BUF_SIZE] = {0}; + struct kfifo *data_fifo = NULL; + u8 *buf = NULL; + int len = 0, retval = -1; + + if ((radio == NULL) || (buffer == NULL)) { + FMDERR("radio/buffer is NULL\n"); + return -ENXIO; + } + buf_type = buffer->index; + buf = (u8 *)buffer->m.userptr; + len = buffer->length; + FMDBG("%s: requesting buffer %d\n", __func__, buf_type); + + if ((buf_type < SILABS_FM_BUF_MAX) && (buf_type >= 0)) { + data_fifo = &radio->data_buf[buf_type]; + if (buf_type == SILABS_FM_BUF_EVENTS) { + if (wait_event_interruptible(radio->event_queue, + kfifo_len(data_fifo)) < 0) { + return -EINTR; + } + } + } else { + FMDERR("invalid buffer type\n"); + return -EINVAL; + } + if (len <= STD_BUF_SIZE) { + buffer->bytesused = kfifo_out_locked(data_fifo, &buf_fifo[0], + len, &radio->buf_lock[buf_type]); + } else { + FMDERR("kfifo_out_locked can not use len more than 128\n"); + return -EINVAL; + } + retval = copy_to_user((void __user *)buf, &buf_fifo[0], + buffer->bytesused); + if (retval > 0) { + FMDERR("Failed to copy %d bytes of data\n", retval); + return -EAGAIN; + } + + return retval; +} + +static int silabs_fm_pinctrl_init(struct silabs_fm_device *radio) +{ + int retval = 0; + + radio->fm_pinctrl = devm_pinctrl_get(&radio->client->dev); + if (IS_ERR_OR_NULL(radio->fm_pinctrl)) { + FMDERR("%s: target does not use pinctrl\n", __func__); + retval = PTR_ERR(radio->fm_pinctrl); + return retval; + } + + radio->gpio_state_active = + pinctrl_lookup_state(radio->fm_pinctrl, + "pmx_fm_active"); + if (IS_ERR_OR_NULL(radio->gpio_state_active)) { + FMDERR("%s: cannot get FM active state\n", __func__); + retval = PTR_ERR(radio->gpio_state_active); + goto err_active_state; + } + + radio->gpio_state_suspend = + pinctrl_lookup_state(radio->fm_pinctrl, + "pmx_fm_suspend"); + if (IS_ERR_OR_NULL(radio->gpio_state_suspend)) { + FMDERR("%s: cannot get FM suspend state\n", __func__); + retval = PTR_ERR(radio->gpio_state_suspend); + goto err_suspend_state; + } + + return retval; + +err_suspend_state: + radio->gpio_state_suspend = NULL; + +err_active_state: + radio->gpio_state_active = NULL; + + return retval; +} + +static int silabs_parse_dt(struct device *dev, + struct silabs_fm_device *radio) +{ + int rc = 0; + struct device_node *np = dev->of_node; + + radio->reset_gpio = of_get_named_gpio(np, "silabs,reset-gpio", 0); + if (radio->reset_gpio < 0) { + FMDERR("silabs-reset-gpio not provided in device tree"); + return radio->reset_gpio; + } + + rc = gpio_request(radio->reset_gpio, "fm_rst_gpio_n"); + if (rc) { + FMDERR("unable to request gpio %d (%d)\n", + radio->reset_gpio, rc); + return rc; + } + + radio->int_gpio = of_get_named_gpio(np, "silabs,int-gpio", 0); + if (radio->int_gpio < 0) { + FMDERR("silabs-int-gpio not provided in device tree"); + rc = radio->int_gpio; + goto err_int_gpio; + } + + rc = gpio_request(radio->int_gpio, "silabs_fm_int_n"); + if (rc) { + FMDERR("unable to request gpio %d (%d)\n", + radio->int_gpio, rc); + goto err_int_gpio; + } + + radio->status_gpio = of_get_named_gpio(np, "silabs,status-gpio", 0); + if (radio->status_gpio < 0) { + FMDERR("silabs-status-gpio not provided in device tree"); + } else { + rc = gpio_request(radio->status_gpio, "silabs_fm_stat_n"); + if (rc) { + FMDERR("unable to request status gpio %d (%d)\n", + radio->status_gpio, rc); + goto err_status_gpio; + } + } + return rc; + +err_status_gpio: + gpio_free(radio->int_gpio); +err_int_gpio: + gpio_free(radio->reset_gpio); + + return rc; +} + +static int silabs_dt_parse_vreg_info(struct device *dev, + struct fm_power_vreg_data *vreg, const char *vreg_name) +{ + int ret = 0; + u32 vol_suply[2]; + struct device_node *np = dev->of_node; + + ret = of_property_read_u32_array(np, vreg_name, vol_suply, 2); + if (ret < 0) { + FMDERR("Invalid property name\n"); + ret = -EINVAL; + } else { + vreg->low_vol_level = vol_suply[0]; + vreg->high_vol_level = vol_suply[1]; + } + return ret; +} + +static int silabs_fm_vidioc_g_fmt_type_private(struct file *file, void *priv, + struct v4l2_format *f) +{ + return 0; + +} + +static const struct v4l2_ioctl_ops silabs_fm_ioctl_ops = { + .vidioc_querycap = silabs_fm_vidioc_querycap, + .vidioc_queryctrl = silabs_fm_vidioc_queryctrl, + .vidioc_g_ctrl = silabs_fm_vidioc_g_ctrl, + .vidioc_s_ctrl = silabs_fm_vidioc_s_ctrl, + .vidioc_g_tuner = silabs_fm_vidioc_g_tuner, + .vidioc_s_tuner = silabs_fm_vidioc_s_tuner, + .vidioc_g_frequency = silabs_fm_vidioc_g_frequency, + .vidioc_s_frequency = silabs_fm_vidioc_s_frequency, + .vidioc_s_hw_freq_seek = silabs_fm_vidioc_s_hw_freq_seek, + .vidioc_dqbuf = silabs_fm_vidioc_dqbuf, + .vidioc_g_fmt_type_private = silabs_fm_vidioc_g_fmt_type_private, +}; + +static const struct v4l2_file_operations silabs_fm_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = v4l2_compat_ioctl32, +#endif + .open = silabs_fm_fops_open, + .release = silabs_fm_fops_release, +}; + + +static const struct video_device silabs_fm_viddev_template = { + .fops = &silabs_fm_fops, + .ioctl_ops = &silabs_fm_ioctl_ops, + .name = DRIVER_NAME, + .release = video_device_release, +}; + +static int silabs_fm_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + + struct silabs_fm_device *radio; + struct regulator *vreg = NULL; + int retval = 0; + int i = 0; + int kfifo_alloc_rc = 0; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + FMDERR("%s: no support for i2c read/write byte data\n", + __func__); + return -EIO; + } + + vreg = regulator_get(&client->dev, "va"); + + if (IS_ERR(vreg)) { + /* + * if analog voltage regulator, VA is not ready yet, return + * -EPROBE_DEFER to kernel so that probe will be called at + * later point of time. + */ + if (PTR_ERR(vreg) == -EPROBE_DEFER) { + FMDERR("In %s, areg probe defer\n", __func__); + return PTR_ERR(vreg); + } + } + /* private data allocation */ + radio = kzalloc(sizeof(struct silabs_fm_device), GFP_KERNEL); + if (!radio) { + FMDERR("Memory not allocated for radio\n"); + retval = -ENOMEM; + goto err_initial; + } + + retval = silabs_parse_dt(&client->dev, radio); + if (retval) { + FMDERR("%s: Parsing DT failed(%d)", __func__, retval); + regulator_put(vreg); + kfree(radio); + return retval; + } + + radio->client = client; + + i2c_set_clientdata(client, radio); + if (!IS_ERR(vreg)) { + radio->areg = devm_kzalloc(&client->dev, + sizeof(struct fm_power_vreg_data), + GFP_KERNEL); + if (!radio->areg) { + FMDERR("%s: allocating memory for areg failed\n", + __func__); + regulator_put(vreg); + kfree(radio); + return -ENOMEM; + } + + radio->areg->reg = vreg; + radio->areg->name = "va"; + radio->areg->is_enabled = 0; + retval = silabs_dt_parse_vreg_info(&client->dev, + radio->areg, "silabs,va-supply-voltage"); + if (retval < 0) { + FMDERR("%s: parsing va-supply failed\n", __func__); + goto mem_alloc_fail; + } + } + + vreg = regulator_get(&client->dev, "vdd"); + + if (IS_ERR(vreg)) { + FMDERR("In %s, vdd supply is not provided\n", __func__); + } else { + radio->dreg = devm_kzalloc(&client->dev, + sizeof(struct fm_power_vreg_data), + GFP_KERNEL); + if (!radio->dreg) { + FMDERR("%s: allocating memory for dreg failed\n", + __func__); + retval = -ENOMEM; + regulator_put(vreg); + goto mem_alloc_fail; + } + + radio->dreg->reg = vreg; + radio->dreg->name = "vdd"; + radio->dreg->is_enabled = 0; + retval = silabs_dt_parse_vreg_info(&client->dev, + radio->dreg, "silabs,vdd-supply-voltage"); + if (retval < 0) { + FMDERR("%s: parsing vdd-supply failed\n", __func__); + goto err_dreg; + } + } + + /* Initialize pin control*/ + retval = silabs_fm_pinctrl_init(radio); + if (retval) { + FMDERR("%s: silabs_fm_pinctrl_init returned %d\n", + __func__, retval); + /* if pinctrl is not supported, -EINVAL is returned*/ + if (retval == -EINVAL) + retval = 0; + } else { + FMDBG("silabs_fm_pinctrl_init success\n"); + } + + radio->wqueue = NULL; + radio->wqueue_scan = NULL; + radio->wqueue_af = NULL; + radio->wqueue_rds = NULL; + + /* video device allocation */ + radio->videodev = video_device_alloc(); + if (!radio->videodev) { + FMDERR("radio->videodev is NULL\n"); + goto err_dreg; + } + /* initial configuration */ + memcpy(radio->videodev, &silabs_fm_viddev_template, + sizeof(silabs_fm_viddev_template)); + strlcpy(radio->v4l2_dev.name, DRIVER_NAME, + sizeof(radio->v4l2_dev.name)); + retval = v4l2_device_register(NULL, &radio->v4l2_dev); + if (retval) + goto err_dreg; + radio->videodev->v4l2_dev = &radio->v4l2_dev; + + /*allocate internal buffers for decoded rds and event buffer*/ + for (i = 0; i < SILABS_FM_BUF_MAX; i++) { + spin_lock_init(&radio->buf_lock[i]); + + if (i == SILABS_FM_BUF_RAW_RDS) + kfifo_alloc_rc = kfifo_alloc(&radio->data_buf[i], + FM_RDS_BUF * 3, GFP_KERNEL); + else if (i == SILABS_FM_BUF_RT_RDS) + kfifo_alloc_rc = kfifo_alloc(&radio->data_buf[i], + STD_BUF_SIZE * 2, GFP_KERNEL); + else + kfifo_alloc_rc = kfifo_alloc(&radio->data_buf[i], + STD_BUF_SIZE, GFP_KERNEL); + + if (kfifo_alloc_rc != 0) { + FMDERR("%s: failed allocating buffers %d\n", + __func__, kfifo_alloc_rc); + retval = -ENOMEM; + goto err_fifo_alloc; + } + } + /* initializing the device count */ + atomic_set(&radio->users, 1); + + /* radio initializes to normal mode */ + radio->lp_mode = 0; + radio->handle_irq = 1; + radio->af_wait_timer = AF_WAIT_SEC; + /* init lock */ + mutex_init(&radio->lock); + radio->tune_req = 0; + radio->seek_tune_status = 0; + init_completion(&radio->sync_req_done); + /* initialize wait queue for event read */ + init_waitqueue_head(&radio->event_queue); + /* initialize wait queue for raw rds read */ + init_waitqueue_head(&radio->read_queue); + + video_set_drvdata(radio->videodev, radio); + + /* + * Start the worker thread for event handling and register read_int_stat + * as worker function + */ + radio->wqueue = create_singlethread_workqueue("sifmradio"); + + if (!radio->wqueue) { + retval = -ENOMEM; + goto err_fifo_alloc; + } + + FMDBG("%s: creating work q for scan\n", __func__); + radio->wqueue_scan = create_singlethread_workqueue("sifmradioscan"); + + if (!radio->wqueue_scan) { + retval = -ENOMEM; + goto err_wqueue_scan; + } + + FMDBG("%s: creating work q for af\n", __func__); + radio->wqueue_af = create_singlethread_workqueue("sifmradioaf"); + + if (!radio->wqueue_af) { + retval = -ENOMEM; + goto err_wqueue_af; + } + + radio->wqueue_rds = create_singlethread_workqueue("sifmradiords"); + + if (!radio->wqueue_rds) { + retval = -ENOMEM; + goto err_wqueue_rds; + } + + /* register video device */ + retval = video_register_device(radio->videodev, + VFL_TYPE_RADIO, + RADIO_NR); + if (retval != 0) { + FMDERR("Could not register video device\n"); + goto err_all; + } + return 0; + +err_all: + destroy_workqueue(radio->wqueue_rds); +err_wqueue_rds: + destroy_workqueue(radio->wqueue_af); +err_wqueue_af: + destroy_workqueue(radio->wqueue_scan); +err_wqueue_scan: + destroy_workqueue(radio->wqueue); +err_fifo_alloc: + for (i--; i >= 0; i--) + kfifo_free(&radio->data_buf[i]); + video_device_release(radio->videodev); +err_dreg: + if (radio->dreg && radio->dreg->reg) { + regulator_put(radio->dreg->reg); + devm_kfree(&client->dev, radio->dreg); + } +mem_alloc_fail: + if (radio->areg && radio->areg->reg) { + regulator_put(radio->areg->reg); + devm_kfree(&client->dev, radio->areg); + } + kfree(radio); +err_initial: + return retval; +} + +static int silabs_fm_remove(struct i2c_client *client) +{ + int i; + struct silabs_fm_device *radio = i2c_get_clientdata(client); + + if (unlikely(radio == NULL)) { + FMDERR("%s:radio is null", __func__); + return -EINVAL; + } + + if (radio->dreg && radio->dreg->reg) { + regulator_put(radio->dreg->reg); + devm_kfree(&client->dev, radio->dreg); + } + + if (radio->areg && radio->areg->reg) { + regulator_put(radio->areg->reg); + devm_kfree(&client->dev, radio->areg); + } + /* disable irq */ + destroy_workqueue(radio->wqueue); + destroy_workqueue(radio->wqueue_scan); + destroy_workqueue(radio->wqueue_af); + destroy_workqueue(radio->wqueue_rds); + + video_unregister_device(radio->videodev); + + /* free internal buffers */ + for (i = 0; i < SILABS_FM_BUF_MAX; i++) + kfifo_free(&radio->data_buf[i]); + + /* free state struct */ + kfree(radio); + + return 0; +} + +static const struct i2c_device_id silabs_i2c_id[] = { + { DRIVER_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, silabs_i2c_id); + +static const struct of_device_id silabs_fm_match[] = { + {.compatible = "silabs,si4705"}, + {} +}; + +static struct i2c_driver silabs_fm_driver = { + .probe = silabs_fm_probe, + .driver = { + .owner = THIS_MODULE, + .name = "silabs-fm", + .of_match_table = silabs_fm_match, + }, + .remove = silabs_fm_remove, + .id_table = silabs_i2c_id, +}; + + +static int __init radio_module_init(void) +{ + return i2c_add_driver(&silabs_fm_driver); +} +module_init(radio_module_init); + +static void __exit radio_module_exit(void) +{ + i2c_del_driver(&silabs_fm_driver); +} +module_exit(radio_module_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION(DRIVER_DESC); + diff --git a/drivers/media/radio/silabs/radio-silabs.h b/drivers/media/radio/silabs/radio-silabs.h new file mode 100644 index 000000000000..b602d65a913c --- /dev/null +++ b/drivers/media/radio/silabs/radio-silabs.h @@ -0,0 +1,532 @@ +/* Copyright (c) 2014, 2019, 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 __RADIO_SILABS_H +#define __RADIO_SILABS_H + +#include <linux/ioctl.h> +#include <linux/videodev2.h> + +#define WRITE_REG_NUM 8 +#define READ_REG_NUM 16 + +#define FMDBG(fmt, args...) pr_debug("silabs_radio: " fmt, ##args) + +#define FMDERR(fmt, args...) pr_err("silabs_radio: " fmt, ##args) + +/* For bounds checking. */ +static const unsigned char MIN_RDS_STD; +static const unsigned char MAX_RDS_STD = 0x02; +static const unsigned char MIN_SRCH_MODE; +static const unsigned char MAX_SRCH_MODE = 0x02; + +/* Standard buffer size */ +#define STD_BUF_SIZE (256) +/* Search direction */ +#define SRCH_DIR_UP (1) +#define SRCH_DIR_DOWN (0) +#define WAIT_TIMEOUT_MSEC 2000 +#define SILABS_DELAY_MSEC 10 +#define CTS_RETRY_COUNT 10 +#define RADIO_NR -1 +#define TURNING_ON 1 +#define TURNING_OFF 0 +#define CH_SPACING_200 200 +#define CH_SPACING_100 100 +#define CH_SPACING_50 50 + +/* to distinguish between seek, tune during STC int. */ +#define NO_SEEK_TUNE_PENDING 0 +#define TUNE_PENDING 1 +#define SEEK_PENDING 2 +#define SCAN_PENDING 3 +#define WRAP_ENABLE 1 +#define WRAP_DISABLE 0 +#define VALID_MASK 0x01 +/* it will check whether UPPER band reached or not */ +#define BLTF_MASK 0x80 +#define SAMPLE_RATE_48_KHZ 0xBB80 +#define MIN_DWELL_TIME 0x00 +#define MAX_DWELL_TIME 0x0F +#define START_SCAN 1 +#define GET_MSB(x)((x >> 8) & 0xFF) +#define GET_LSB(x)((x) & 0xFF) +/* + * When tuning, we need to divide freq by TUNE_STEP_SIZE + * before sending it to chip + */ +#define TUNE_STEP_SIZE 10 + +#define TUNE_PARAM 16 + +#define OFFSET_OF_GRP_TYP 11 +#define RDS_INT_BIT 0x01 +#define FIFO_CNT_16 0x10 +#define UNCORRECTABLE_RDS_EN 0xFF01 +#define NO_OF_RDS_BLKS 4 +#define MSB_OF_BLK_0 4 +#define LSB_OF_BLK_0 5 +#define MSB_OF_BLK_1 6 +#define LSB_OF_BLK_1 7 +#define MSB_OF_BLK_2 8 +#define LSB_OF_BLK_2 9 +#define MSB_OF_BLK_3 10 +#define LSB_OF_BLK_3 11 +#define MAX_RT_LEN 64 +#define END_OF_RT 0x0d +#define MAX_PS_LEN 8 +#define OFFSET_OF_PS 5 +#define PS_VALIDATE_LIMIT 2 +#define RT_VALIDATE_LIMIT 2 +#define RDS_CMD_LEN 3 +#define RDS_RSP_LEN 13 +#define PS_EVT_DATA_LEN (MAX_PS_LEN + OFFSET_OF_PS) +#define NO_OF_PS 1 +#define OFFSET_OF_RT 5 +#define OFFSET_OF_PTY 5 +#define MAX_LEN_2B_GRP_RT 32 +#define CNT_FOR_2A_GRP_RT 4 +#define CNT_FOR_2B_GRP_RT 2 +#define PS_MASK 0x3 +#define PTY_MASK 0x1F +#define NO_OF_CHARS_IN_EACH_ADD 2 + +#define MIN_SNR 0 +#define MAX_SNR 127 +#define MIN_RSSI 0 +#define MAX_RSSI 127 +#define MIN_RDS_FIFO_CNT 0 +#define MAX_RDS_FIFO_CNT 25 + +#define DEFAULT_SNR_TH 2 +#define DEFAULT_RSSI_TH 7 + +#define DEFAULT_AF_RSSI_LOW_TH 25 +#define NO_OF_AF_IN_GRP 2 +#define MAX_NO_OF_AF 25 +#define MAX_AF_LIST_SIZE (MAX_NO_OF_AF * 4) /* 4 bytes per freq */ +#define GET_AF_EVT_LEN(x) (7 + x*4) +#define GET_AF_LIST_LEN(x) (x*4) +#define MIN_AF_FREQ_CODE 1 +#define MAX_AF_FREQ_CODE 204 +/* 25 AFs supported for a freq. 224 means 1 AF. 225 means 2 AFs and so on */ +#define NO_AF_CNT_CODE 224 +#define MIN_AF_CNT_CODE 225 +#define MAX_AF_CNT_CODE 249 +#define AF_WAIT_SEC 10 +#define MAX_AF_WAIT_SEC 255 +#define AF_PI_WAIT_TIME 50 /* 50*100msec = 5sec */ +/* freqs are divided by 10. */ +#define SCALE_AF_CODE_TO_FREQ_KHZ(x) (87500 + (x*100)) + + +/* commands */ +#define POWER_UP_CMD 0x01 +#define GET_REV_CMD 0x10 +#define POWER_DOWN_CMD 0x11 +#define SET_PROPERTY_CMD 0x12 +#define GET_PROPERTY_CMD 0x13 +#define GET_INT_STATUS_CMD 0x14 +#define PATCH_ARGS_CMD 0x15 +#define PATCH_DATA_CMD 0x16 +#define FM_TUNE_FREQ_CMD 0x20 +#define FM_SEEK_START_CMD 0x21 +#define FM_TUNE_STATUS_CMD 0x22 +#define FM_RSQ_STATUS_CMD 0x23 +#define FM_RDS_STATUS_CMD 0x24 +#define FM_AGC_STATUS_CMD 0x27 +#define FM_AGC_OVERRIDE_CMD 0x28 +#define GPIO_CTL_CMD 0x80 +#define GPIO_SET_CMD 0x81 + +/* properties */ +#define GPO_IEN_PROP 0x0001 +#define DIGITAL_OUTPUT_FORMAT_PROP 0x0102 +#define DIGITAL_OUTPUT_SAMPLE_RATE_PROP 0x0104 +#define REFCLK_FREQ_PROP 0x0201 +#define REFCLK_PRESCALE_PROP 0x0202 +#define FM_DEEMPHASIS_PROP 0x1100 +#define FM_CHANNEL_FILTER_PROP 0x1102 +#define FM_ANTENNA_INPUT_PROP 0x1107 +#define FM_RSQ_INT_SOURCE_PROP 0x1200 +#define FM_RSQ_RSSI_LO_THRESHOLD_PROP 0x1204 + +#define FM_SEEK_BAND_BOTTOM_PROP 0x1400 +#define FM_SEEK_BAND_TOP_PROP 0x1401 +#define FM_SEEK_FREQ_SPACING_PROP 0x1402 +#define FM_SEEK_TUNE_SNR_THRESHOLD_PROP 0x1403 +#define FM_SEEK_TUNE_RSSI_THRESHOLD_PROP 0x1404 + +#define FM_RDS_INT_SOURCE_PROP 0x1500 +#define FM_RDS_INT_FIFO_COUNT_PROP 0x1501 +#define FM_RDS_CONFIG_PROP 0x1502 + +#define RX_HARD_MUTE_PROP 0x4001 + +/* BIT MASKS */ +#define ENABLE_CTS_INT_MASK (1 << 7) +#define ENABLE_GPO2_INT_MASK (1 << 6) +#define PATCH_ENABLE_MASK (1 << 5) +/* to use clock present on daughter card or MSM's */ +#define CLOCK_ENABLE_MASK (1 << 4) +#define FUNC_QUERY_LIB_ID_MASK 15 +#define CANCEL_SEEK_MASK (1 << 1) +#define INTACK_MASK 1 +#define SEEK_WRAP_MASK (1 << 2) +#define SEEK_UP_MASK (1 << 3) + + +/* BIT MASKS to parse response bytes */ +#define CTS_INT_BIT_MASK (1 << 7) +#define ERR_BIT_MASK (1 << 6) +#define RSQ_INT_BIT_MASK (1 << 3) +/* set RDS repeat int bit along with RDS int bit */ +#define RDS_INT_BIT_MASK (0x0404) +#define STC_INT_BIT_MASK 1 +#define RSSI_LOW_TH_INT_BIT_MASK 1 +#define RDS_INT_DISABLE_MASK 0x9 +#define RSQ_INT_DISABLE_MASK 0x5 +#define HARD_MUTE_MASK 0x3 +#define GPIO1_OUTPUT_ENABLE_MASK (1 << 1) +#define GPIO_OUTPUT_LOW_MASK 0 + +#define DCLK_FALLING_EDGE_MASK (1 << 7) + +/* Command lengths */ +#define SET_PROP_CMD_LEN 6 +#define GET_PROP_CMD_LEN 4 +#define GET_INT_STATUS_CMD_LEN 1 +#define POWER_UP_CMD_LEN 3 +#define POWER_DOWN_CMD_LEN 1 +#define TUNE_FREQ_CMD_LEN 5 +#define SEEK_CMD_LEN 2 +#define TUNE_STATUS_CMD_LEN 2 +#define RSQ_STATUS_CMD_LEN 2 +#define GPIO_CTL_CMD_LEN 2 +#define GPIO_SET_CMD_LEN 2 + +#define HIGH_BYTE_16BIT(x) (x >> 8) +#define LOW_BYTE_16BIT(x) (x & 0xFF) + + +#define AUDIO_OPMODE_ANALOG 0x05 +#define AUDIO_OPMODE_DIGITAL 0xB0 + +/* ERROR codes */ +#define BAD_CMD 0x10 +#define BAD_ARG1 0x11 +#define BAD_ARG2 0x12 +#define BAD_ARG3 0x13 +#define BAD_ARG4 0x14 +#define BAD_ARG5 0x15 +#define BAD_ARG6 0x16 +#define BAD_ARG7 0x17 +#define BAD_PROP 0x20 +#define BAD_BOOT_MODE 0x30 + +/* RDS */ +#define FM_RDS_BUF 100 +#define FM_RDS_STATUS_IN_INTACK 0x01 +#define FM_RDS_STATUS_IN_MTFIFO 0x02 +#define FM_RDS_STATUS_OUT_GRPLOST 0x04 +#define FM_RDS_STATUS_OUT_BLED 0x03 +#define FM_RDS_STATUS_OUT_BLEC 0x0C +#define FM_RDS_STATUS_OUT_BLEB 0x30 +#define FM_RDS_STATUS_OUT_BLEA 0xC0 +#define FM_RDS_STATUS_OUT_BLED_SHFT 0 +#define FM_RDS_STATUS_OUT_BLEC_SHFT 2 +#define FM_RDS_STATUS_OUT_BLEB_SHFT 4 +#define FM_RDS_STATUS_OUT_BLEA_SHFT 6 +#define RDS_TYPE_0A (0 * 2 + 0) +#define RDS_TYPE_0B (0 * 2 + 1) +#define RDS_TYPE_2A (2 * 2 + 0) +#define RDS_TYPE_2B (2 * 2 + 1) +#define RDS_TYPE_3A (3 * 2 + 0) +#define UNCORRECTABLE 3 +#define RT_VALIDATE_LIMIT 2 + +#define APP_GRP_typ_MASK 0x1F +/*ERT*/ +#define ERT_AID 0x6552 +#define MAX_ERT_SEGMENT 31 +#define MAX_ERT_LEN 256 +#define ERT_OFFSET 3 +#define ERT_FORMAT_DIR_BIT 1 +#define ERT_CNT_PER_BLK 2 +/*RT PLUS*/ +#define DUMMY_CLASS 0 +#define RT_PLUS_LEN_1_TAG 3 +#define RT_ERT_FLAG_BIT 13 +#define RT_PLUS_AID 0x4bd7 +#define RT_ERT_FLAG_OFFSET 1 +#define RT_PLUS_OFFSET 2 +/*TAG1*/ +#define TAG1_MSB_OFFSET 3 +#define TAG1_MSB_MASK 7 +#define TAG1_LSB_OFFSET 13 +#define TAG1_POS_MSB_MASK 0x3F +#define TAG1_POS_MSB_OFFSET 1 +#define TAG1_POS_LSB_OFFSET 7 +#define TAG1_LEN_OFFSET 1 +#define TAG1_LEN_MASK 0x3F +/*TAG2*/ +#define TAG2_MSB_OFFSET 5 +#define TAG2_MSB_MASK 9 +#define TAG2_LSB_OFFSET 11 +#define TAG2_POS_MSB_MASK 0x3F +#define TAG2_POS_MSB_OFFSET 3 +#define TAG2_POS_LSB_OFFSET 5 +#define TAG2_LEN_MASK 0x1F + +#define EXTRACT_BIT(data, bit_pos) ((data >> bit_pos) & 1) + +/* FM states */ +enum radio_state_t { + FM_OFF, + FM_RECV, + FM_RESET, + FM_CALIB, + FM_TURNING_OFF, + FM_RECV_TURNING_ON, + FM_MAX_NO_STATES, +}; + +enum emphasis_type { + FM_RX_EMP75 = 0x0002, + FM_RX_EMP50 = 0x0001 +}; + +/* 3 valid values: 5 (50 kHz), 10 (100 kHz), and 20 (200 kHz). */ +enum channel_space_type { + FM_RX_SPACE_200KHZ = 0x0014, + FM_RX_SPACE_100KHZ = 0x000A, + FM_RX_SPACE_50KHZ = 0x0005 +}; + +enum v4l2_cid_private_silabs_fm_t { + V4L2_CID_PRIVATE_SILABS_SRCHMODE = (V4L2_CID_PRIVATE_BASE + 1), + V4L2_CID_PRIVATE_SILABS_SCANDWELL, + V4L2_CID_PRIVATE_SILABS_SRCHON, + V4L2_CID_PRIVATE_SILABS_STATE, + V4L2_CID_PRIVATE_SILABS_TRANSMIT_MODE, + V4L2_CID_PRIVATE_SILABS_RDSGROUP_MASK, + V4L2_CID_PRIVATE_SILABS_REGION, + V4L2_CID_PRIVATE_SILABS_SIGNAL_TH, + V4L2_CID_PRIVATE_SILABS_SRCH_PTY, + V4L2_CID_PRIVATE_SILABS_SRCH_PI, + V4L2_CID_PRIVATE_SILABS_SRCH_CNT, + V4L2_CID_PRIVATE_SILABS_EMPHASIS, + V4L2_CID_PRIVATE_SILABS_RDS_STD, + V4L2_CID_PRIVATE_SILABS_SPACING, + V4L2_CID_PRIVATE_SILABS_RDSON, + V4L2_CID_PRIVATE_SILABS_RDSGROUP_PROC, + V4L2_CID_PRIVATE_SILABS_LP_MODE, + V4L2_CID_PRIVATE_SILABS_ANTENNA, + V4L2_CID_PRIVATE_SILABS_RDSD_BUF, + V4L2_CID_PRIVATE_SILABS_PSALL, + /*v4l2 Tx controls*/ + V4L2_CID_PRIVATE_SILABS_TX_SETPSREPEATCOUNT, + V4L2_CID_PRIVATE_SILABS_STOP_RDS_TX_PS_NAME, + V4L2_CID_PRIVATE_SILABS_STOP_RDS_TX_RT, + V4L2_CID_PRIVATE_SILABS_IOVERC, + V4L2_CID_PRIVATE_SILABS_INTDET, + V4L2_CID_PRIVATE_SILABS_MPX_DCC, + V4L2_CID_PRIVATE_SILABS_AF_JUMP, + V4L2_CID_PRIVATE_SILABS_RSSI_DELTA, + V4L2_CID_PRIVATE_SILABS_HLSI, + + /* + * Here we have IOCTl's that are specific to IRIS + * (V4L2_CID_PRIVATE_BASE + 0x1E to V4L2_CID_PRIVATE_BASE + 0x28) + */ + V4L2_CID_PRIVATE_SILABS_SOFT_MUTE,/* 0x800001E*/ + V4L2_CID_PRIVATE_SILABS_RIVA_ACCS_ADDR, + V4L2_CID_PRIVATE_SILABS_RIVA_ACCS_LEN, + V4L2_CID_PRIVATE_SILABS_RIVA_PEEK, + V4L2_CID_PRIVATE_SILABS_RIVA_POKE, + V4L2_CID_PRIVATE_SILABS_SSBI_ACCS_ADDR, + V4L2_CID_PRIVATE_SILABS_SSBI_PEEK, + V4L2_CID_PRIVATE_SILABS_SSBI_POKE, + V4L2_CID_PRIVATE_SILABS_TX_TONE, + V4L2_CID_PRIVATE_SILABS_RDS_GRP_COUNTERS, + V4L2_CID_PRIVATE_SILABS_SET_NOTCH_FILTER,/* 0x8000028 */ + + V4L2_CID_PRIVATE_SILABS_SET_AUDIO_PATH,/* 0x8000029 */ + V4L2_CID_PRIVATE_SILABS_DO_CALIBRATION,/* 0x800002A : IRIS */ + V4L2_CID_PRIVATE_SILABS_SRCH_ALGORITHM,/* 0x800002B */ + V4L2_CID_PRIVATE_SILABS_GET_SINR, /* 0x800002C : IRIS */ + V4L2_CID_PRIVATE_SILABS_INTF_LOW_THRESHOLD, /* 0x800002D */ + V4L2_CID_PRIVATE_SILABS_INTF_HIGH_THRESHOLD, /* 0x800002E */ + V4L2_CID_PRIVATE_SILABS_SINR_THRESHOLD, /* 0x800002F : IRIS */ + V4L2_CID_PRIVATE_SILABS_SINR_SAMPLES, /* 0x8000030 : IRIS */ + V4L2_CID_PRIVATE_SILABS_SPUR_FREQ, + V4L2_CID_PRIVATE_SILABS_SPUR_FREQ_RMSSI, + V4L2_CID_PRIVATE_SILABS_SPUR_SELECTION, + V4L2_CID_PRIVATE_SILABS_UPDATE_SPUR_TABLE, + V4L2_CID_PRIVATE_SILABS_VALID_CHANNEL, + V4L2_CID_PRIVATE_SILABS_AF_RMSSI_TH, + V4L2_CID_PRIVATE_SILABS_AF_RMSSI_SAMPLES, + V4L2_CID_PRIVATE_SILABS_GOOD_CH_RMSSI_TH, + V4L2_CID_PRIVATE_SILABS_SRCHALGOTYPE, + V4L2_CID_PRIVATE_SILABS_CF0TH12, + V4L2_CID_PRIVATE_SILABS_SINRFIRSTSTAGE, + V4L2_CID_PRIVATE_SILABS_RMSSIFIRSTSTAGE, + V4L2_CID_PRIVATE_SILABS_RXREPEATCOUNT, + V4L2_CID_PRIVATE_SILABS_RSSI_TH, /* 0x800003E */ + V4L2_CID_PRIVATE_SILABS_AF_JUMP_RSSI_TH /* 0x800003F */ + +}; + +enum silabs_buf_t { + SILABS_FM_BUF_SRCH_LIST, + SILABS_FM_BUF_EVENTS, + SILABS_FM_BUF_RT_RDS, + SILABS_FM_BUF_PS_RDS, + SILABS_FM_BUF_RAW_RDS, + SILABS_FM_BUF_AF_LIST, + SILABS_FM_BUF_RT_PLUS = 11, + SILABS_FM_BUF_ERT, + SILABS_FM_BUF_MAX +}; + +enum silabs_evt_t { + SILABS_EVT_RADIO_READY, + SILABS_EVT_TUNE_SUCC, + SILABS_EVT_SEEK_COMPLETE, + SILABS_EVT_SCAN_NEXT, + SILABS_EVT_NEW_RAW_RDS, + SILABS_EVT_NEW_RT_RDS, + SILABS_EVT_NEW_PS_RDS, + SILABS_EVT_ERROR, + SILABS_EVT_BELOW_TH, + SILABS_EVT_ABOVE_TH, + SILABS_EVT_STEREO, + SILABS_EVT_MONO, + SILABS_EVT_RDS_AVAIL, + SILABS_EVT_RDS_NOT_AVAIL, + SILABS_EVT_NEW_SRCH_LIST, + SILABS_EVT_NEW_AF_LIST, + SILABS_EVT_TXRDSDAT, + SILABS_EVT_TXRDSDONE, + SILABS_EVT_RADIO_DISABLED, + SILABS_EVT_NEW_ODA, + SILABS_EVT_NEW_RT_PLUS, + SILABS_EVT_NEW_ERT +}; + +enum silabs_region_t { + SILABS_REGION_US, + SILABS_REGION_EU, + SILABS_REGION_JAPAN, + SILABS_REGION_JAPAN_WIDE, + SILABS_REGION_OTHER +}; + +enum silabs_interrupts_t { + DISABLE_ALL_INTERRUPTS, + ENABLE_STC_RDS_INTERRUPTS, + ENABLE_STC_INTERRUPTS, + ENABLE_RDS_INTERRUPTS, + DISABLE_RDS_INTERRUPTS, + ENABLE_RSQ_INTERRUPTS, + DISABLE_RSQ_INTERRUPTS +}; + +struct silabs_fm_recv_conf_req { + __u16 emphasis; + __u16 ch_spacing; + /* limits stored as actual freq / TUNE_STEP_SIZE */ + __u16 band_low_limit; + __u16 band_high_limit; +}; + +struct af_list_ev { + __le32 tune_freq_khz; + __le16 pi_code; + __u8 af_size; + __u8 af_list[MAX_AF_LIST_SIZE]; +} __packed; + +struct silabs_af_info { + /* no. of invalid AFs. */ + u8 inval_freq_cnt; + /* no. of AFs in the list. */ + u8 cnt; + /* actual size of the list */ + u8 size; + /* index of currently tuned station in the AF list. */ + u8 index; + /* PI of the frequency */ + u16 pi; + /* freq to which AF list belongs to. */ + u32 orig_freq_khz; + /* AF list */ + u32 af_list[MAX_NO_OF_AF]; +}; + +static inline bool is_valid_chan_spacing(int spacing) +{ + if ((spacing == 0) || + (spacing == 1) || + (spacing == 2)) + return 1; + else + return 0; +} + +static inline bool is_valid_rds_std(int rds_std) +{ + if ((rds_std >= MIN_RDS_STD) && + (rds_std <= MAX_RDS_STD)) + return 1; + else + return 0; +} + +static inline bool is_valid_srch_mode(int srch_mode) +{ + if ((srch_mode >= MIN_SRCH_MODE) && + (srch_mode <= MAX_SRCH_MODE)) + return 1; + else + return 0; +} + +struct fm_power_vreg_data { + /* voltage regulator handle */ + struct regulator *reg; + /* regulator name */ + const char *name; + /* voltage levels to be set */ + unsigned int low_vol_level; + unsigned int high_vol_level; + bool set_voltage_sup; + /* is this regulator enabled? */ + bool is_enabled; +}; + +struct silabs_rel_freq { + __u8 rel_freq_msb; + __u8 rel_freq_lsb; +} __packed; + +struct silabs_srch_list_compl { + __u8 num_stations_found; + struct silabs_rel_freq rel_freq[20]; +} __packed; + +enum search_t { + SEEK, + SCAN, + SCAN_FOR_STRONG, +}; +#endif /* __RADIO_SILABS_H */ diff --git a/drivers/media/radio/wl128x/fmdrv_common.c b/drivers/media/radio/wl128x/fmdrv_common.c index ebc73b034249..51639a3f7abe 100644 --- a/drivers/media/radio/wl128x/fmdrv_common.c +++ b/drivers/media/radio/wl128x/fmdrv_common.c @@ -494,7 +494,8 @@ int fmc_send_cmd(struct fmdev *fmdev, u8 fm_op, u16 type, void *payload, return -EIO; } /* Send response data to caller */ - if (response != NULL && response_len != NULL && evt_hdr->dlen) { + if (response != NULL && response_len != NULL && evt_hdr->dlen && + evt_hdr->dlen <= payload_len) { /* Skip header info and copy only response data */ skb_pull(skb, sizeof(struct fm_event_msg_hdr)); memcpy(response, skb->data, evt_hdr->dlen); @@ -590,6 +591,8 @@ static void fm_irq_handle_flag_getcmd_resp(struct fmdev *fmdev) return; fm_evt_hdr = (void *)skb->data; + if (fm_evt_hdr->dlen > sizeof(fmdev->irq_info.flag)) + return; /* Skip header info and copy only response data */ skb_pull(skb, sizeof(struct fm_event_msg_hdr)); @@ -1315,7 +1318,7 @@ static int load_default_rx_configuration(struct fmdev *fmdev) static int fm_power_up(struct fmdev *fmdev, u8 mode) { u16 payload; - __be16 asic_id, asic_ver; + __be16 asic_id = 0, asic_ver = 0; int resp_len, ret; u8 fw_name[50]; diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index 7b2fe1b56039..1df23c01ad37 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -711,6 +711,9 @@ static int au0828_analog_stream_enable(struct au0828_dev *d) dprintk(1, "au0828_analog_stream_enable called\n"); + if (test_bit(DEV_DISCONNECTED, &d->dev_state)) + return -ENODEV; + iface = usb_ifnum_to_if(d->usbdev, 0); if (iface && iface->cur_altsetting->desc.bAlternateSetting != 5) { dprintk(1, "Changing intf#0 to alt 5\n"); @@ -799,9 +802,9 @@ int au0828_start_analog_streaming(struct vb2_queue *vq, unsigned int count) return rc; } + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1); + if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - v4l2_device_call_all(&dev->v4l2_dev, 0, video, - s_stream, 1); dev->vid_timeout_running = 1; mod_timer(&dev->vid_timeout, jiffies + (HZ / 10)); } else if (vq->type == V4L2_BUF_TYPE_VBI_CAPTURE) { @@ -821,10 +824,11 @@ static void au0828_stop_streaming(struct vb2_queue *vq) dprintk(1, "au0828_stop_streaming called %d\n", dev->streaming_users); - if (dev->streaming_users-- == 1) + if (dev->streaming_users-- == 1) { au0828_uninit_isoc(dev); + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0); + } - v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0); dev->vid_timeout_running = 0; del_timer_sync(&dev->vid_timeout); @@ -853,8 +857,10 @@ void au0828_stop_vbi_streaming(struct vb2_queue *vq) dprintk(1, "au0828_stop_vbi_streaming called %d\n", dev->streaming_users); - if (dev->streaming_users-- == 1) + if (dev->streaming_users-- == 1) { au0828_uninit_isoc(dev); + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0); + } spin_lock_irqsave(&dev->slock, flags); if (dev->isoc_ctl.vbi_buf != NULL) { diff --git a/drivers/media/usb/cpia2/cpia2_v4l.c b/drivers/media/usb/cpia2/cpia2_v4l.c index d793c630f1dd..05e7edb213de 100644 --- a/drivers/media/usb/cpia2/cpia2_v4l.c +++ b/drivers/media/usb/cpia2/cpia2_v4l.c @@ -1248,8 +1248,7 @@ static int __init cpia2_init(void) LOG("%s v%s\n", ABOUT, CPIA_VERSION); check_parameters(); - cpia2_usb_init(); - return 0; + return cpia2_usb_init(); } diff --git a/drivers/media/usb/go7007/go7007-fw.c b/drivers/media/usb/go7007/go7007-fw.c index 60bf5f0644d1..a5efcd4f7b4f 100644 --- a/drivers/media/usb/go7007/go7007-fw.c +++ b/drivers/media/usb/go7007/go7007-fw.c @@ -1499,8 +1499,8 @@ static int modet_to_package(struct go7007 *go, __le16 *code, int space) return cnt; } -static int do_special(struct go7007 *go, u16 type, __le16 *code, int space, - int *framelen) +static noinline_for_stack int do_special(struct go7007 *go, u16 type, + __le16 *code, int space, int *framelen) { switch (type) { case SPECIAL_FRM_HEAD: diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c index 0533ef20decf..232b0fd3e478 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c @@ -670,6 +670,8 @@ static int ctrl_get_input(struct pvr2_ctrl *cptr,int *vp) static int ctrl_check_input(struct pvr2_ctrl *cptr,int v) { + if (v < 0 || v > PVR2_CVAL_INPUT_MAX) + return 0; return ((1 << v) & cptr->hdw->input_allowed_mask) != 0; } diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw.h b/drivers/media/usb/pvrusb2/pvrusb2-hdw.h index a82a00dd7329..80869990ffbb 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-hdw.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.h @@ -54,6 +54,7 @@ #define PVR2_CVAL_INPUT_COMPOSITE 2 #define PVR2_CVAL_INPUT_SVIDEO 3 #define PVR2_CVAL_INPUT_RADIO 4 +#define PVR2_CVAL_INPUT_MAX PVR2_CVAL_INPUT_RADIO enum pvr2_config { pvr2_config_empty, /* No configuration */ diff --git a/drivers/media/usb/siano/smsusb.c b/drivers/media/usb/siano/smsusb.c index ec30a004f319..e8f7a1f56be5 100644 --- a/drivers/media/usb/siano/smsusb.c +++ b/drivers/media/usb/siano/smsusb.c @@ -391,6 +391,7 @@ static int smsusb_init_device(struct usb_interface *intf, int board_id) struct smsusb_device_t *dev; void *mdev; int i, rc; + int align = 0; /* create device object */ dev = kzalloc(sizeof(struct smsusb_device_t), GFP_KERNEL); @@ -402,6 +403,24 @@ static int smsusb_init_device(struct usb_interface *intf, int board_id) dev->udev = interface_to_usbdev(intf); dev->state = SMSUSB_DISCONNECTED; + for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) { + struct usb_endpoint_descriptor *desc = + &intf->cur_altsetting->endpoint[i].desc; + + if (desc->bEndpointAddress & USB_DIR_IN) { + dev->in_ep = desc->bEndpointAddress; + align = usb_endpoint_maxp(desc) - sizeof(struct sms_msg_hdr); + } else { + dev->out_ep = desc->bEndpointAddress; + } + } + + pr_debug("in_ep = %02x, out_ep = %02x\n", dev->in_ep, dev->out_ep); + if (!dev->in_ep || !dev->out_ep || align < 0) { /* Missing endpoints? */ + smsusb_term_device(intf); + return -ENODEV; + } + params.device_type = sms_get_board(board_id)->type; switch (params.device_type) { @@ -416,24 +435,12 @@ static int smsusb_init_device(struct usb_interface *intf, int board_id) /* fall-thru */ default: dev->buffer_size = USB2_BUFFER_SIZE; - dev->response_alignment = - le16_to_cpu(dev->udev->ep_in[1]->desc.wMaxPacketSize) - - sizeof(struct sms_msg_hdr); + dev->response_alignment = align; params.flags |= SMS_DEVICE_FAMILY2; break; } - for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) { - if (intf->cur_altsetting->endpoint[i].desc. bEndpointAddress & USB_DIR_IN) - dev->in_ep = intf->cur_altsetting->endpoint[i].desc.bEndpointAddress; - else - dev->out_ep = intf->cur_altsetting->endpoint[i].desc.bEndpointAddress; - } - - pr_debug("in_ep = %02x, out_ep = %02x\n", - dev->in_ep, dev->out_ep); - params.device = &dev->udev->dev; params.buffer_size = dev->buffer_size; params.num_buffers = MAX_BUFFERS; diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index f2e3fdf385cc..ebd1b882556d 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -868,7 +868,7 @@ static struct uvc_entity *uvc_alloc_entity(u16 type, u8 id, unsigned int size; unsigned int i; - extra_size = ALIGN(extra_size, sizeof(*entity->pads)); + extra_size = roundup(extra_size, sizeof(*entity->pads)); num_inputs = (type & UVC_TERM_OUTPUT) ? num_pads : num_pads - 1; size = sizeof(*entity) + extra_size + sizeof(*entity->pads) * num_pads + num_inputs; diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 6b1eaeddbdb3..02532c8c69ca 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -706,6 +706,9 @@ static void determine_valid_ioctls(struct video_device *vdev) SET_VALID_IOCTL(ops, VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings); SET_VALID_IOCTL(ops, VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap); SET_VALID_IOCTL(ops, VIDIOC_G_EDID, vidioc_g_edid); + } else { + /* ioctls valid for radio */ + SET_VALID_IOCTL(ops, VIDIOC_DQBUF, vidioc_dqbuf); } if (is_tx && (is_radio || is_sdr)) { /* radio transmitter only ioctls */ diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 79829f56a816..2fd84486c054 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -980,6 +980,10 @@ static int check_fmt(struct file *file, enum v4l2_buf_type type) if (is_sdr && is_tx && ops->vidioc_g_fmt_sdr_out) return 0; break; + case V4L2_BUF_TYPE_PRIVATE: + if (ops->vidioc_g_fmt_type_private) + return 0; + break; default: break; } diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index 6ab481ee8ece..8ac02b6c162f 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c @@ -72,7 +72,7 @@ static int tegra_mc_setup_latency_allowance(struct tegra_mc *mc) u32 value; /* compute the number of MC clock cycles per tick */ - tick = mc->tick * clk_get_rate(mc->clk); + tick = (unsigned long long)mc->tick * clk_get_rate(mc->clk); do_div(tick, NSEC_PER_SEC); value = readl(mc->regs + MC_EMEM_ARB_CFG); diff --git a/drivers/mfd/intel-lpss.c b/drivers/mfd/intel-lpss.c index ac867489b5a9..498875193386 100644 --- a/drivers/mfd/intel-lpss.c +++ b/drivers/mfd/intel-lpss.c @@ -267,6 +267,9 @@ static void intel_lpss_init_dev(const struct intel_lpss *lpss) { u32 value = LPSS_PRIV_SSP_REG_DIS_DMA_FIN; + /* Set the device in reset state */ + writel(0, lpss->priv + LPSS_PRIV_RESETS); + intel_lpss_deassert_reset(lpss); intel_lpss_set_remap_addr(lpss); diff --git a/drivers/mfd/twl6040.c b/drivers/mfd/twl6040.c index 72aab60ae846..db8684430f02 100644 --- a/drivers/mfd/twl6040.c +++ b/drivers/mfd/twl6040.c @@ -316,8 +316,19 @@ int twl6040_power(struct twl6040 *twl6040, int on) } } + /* + * Register access can produce errors after power-up unless we + * wait at least 8ms based on measurements on duovero. + */ + usleep_range(10000, 12000); + /* Sync with the HW */ - regcache_sync(twl6040->regmap); + ret = regcache_sync(twl6040->regmap); + if (ret) { + dev_err(twl6040->dev, "Failed to sync with the HW: %i\n", + ret); + goto out; + } /* Default PLL configuration after power up */ twl6040->pll = TWL6040_SYSCLK_SEL_LPPLL; diff --git a/drivers/misc/genwqe/card_dev.c b/drivers/misc/genwqe/card_dev.c index c0012ca4229e..74923ffb0df1 100644 --- a/drivers/misc/genwqe/card_dev.c +++ b/drivers/misc/genwqe/card_dev.c @@ -782,6 +782,8 @@ static int genwqe_pin_mem(struct genwqe_file *cfile, struct genwqe_mem *m) if ((m->addr == 0x0) || (m->size == 0)) return -EINVAL; + if (m->size > ULONG_MAX - PAGE_SIZE - (m->addr & ~PAGE_MASK)) + return -EINVAL; map_addr = (m->addr & PAGE_MASK); map_size = round_up(m->size + (m->addr & ~PAGE_MASK), PAGE_SIZE); diff --git a/drivers/misc/genwqe/card_utils.c b/drivers/misc/genwqe/card_utils.c index 0c15ba21fa54..d4c719683a8a 100644 --- a/drivers/misc/genwqe/card_utils.c +++ b/drivers/misc/genwqe/card_utils.c @@ -582,6 +582,10 @@ int genwqe_user_vmap(struct genwqe_dev *cd, struct dma_mapping *m, void *uaddr, /* determine space needed for page_list. */ data = (unsigned long)uaddr; offs = offset_in_page(data); + if (size > ULONG_MAX - PAGE_SIZE - offs) { + m->size = 0; /* mark unused and not added */ + return -EINVAL; + } m->nr_pages = DIV_ROUND_UP(offs + size, PAGE_SIZE); m->page_list = kcalloc(m->nr_pages, diff --git a/drivers/misc/kgdbts.c b/drivers/misc/kgdbts.c index 99635dd9dbac..bb3a76ad80da 100644 --- a/drivers/misc/kgdbts.c +++ b/drivers/misc/kgdbts.c @@ -1132,7 +1132,7 @@ static void kgdbts_put_char(u8 chr) static int param_set_kgdbts_var(const char *kmessage, struct kernel_param *kp) { - int len = strlen(kmessage); + size_t len = strlen(kmessage); if (len >= MAX_CONFIG_LEN) { printk(KERN_ERR "kgdbts: config string too long\n"); @@ -1152,7 +1152,7 @@ static int param_set_kgdbts_var(const char *kmessage, struct kernel_param *kp) strcpy(config, kmessage); /* Chop out \n char as a result of echo */ - if (config[len - 1] == '\n') + if (len && config[len - 1] == '\n') config[len - 1] = '\0'; /* Go and configure with the new params. */ diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 9a7f9d27be1f..f1a4abf616be 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -224,6 +224,14 @@ static int mmc_decode_scr(struct mmc_card *card) if (scr->sda_spec3) scr->cmds = UNSTUFF_BITS(resp, 32, 2); + + /* SD Spec says: any SD Card shall set at least bits 0 and 2 */ + if (!(scr->bus_widths & SD_SCR_BUS_WIDTH_1) || + !(scr->bus_widths & SD_SCR_BUS_WIDTH_4)) { + pr_err("%s: invalid bus width\n", mmc_hostname(card->host)); + return -EINVAL; + } + return 0; } diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index e03ec74f3fb0..40a369c7005a 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -819,6 +819,10 @@ mmc_spi_readblock(struct mmc_spi_host *host, struct spi_transfer *t, } status = spi_sync_locked(spi, &host->m); + if (status < 0) { + dev_dbg(&spi->dev, "read error %d\n", status); + return status; + } if (host->dma_dev) { dma_sync_single_for_cpu(host->dma_dev, diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index ac66c61d9433..356b294c93c9 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -624,6 +624,11 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) if (esdhc->vendor_ver > VENDOR_V_22) host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ; + if (of_find_compatible_node(NULL, NULL, "fsl,p2020-esdhc")) { + host->quirks2 |= SDHCI_QUIRK_RESET_AFTER_REQUEST; + host->quirks2 |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; + } + if (of_device_is_compatible(np, "fsl,p5040-esdhc") || of_device_is_compatible(np, "fsl,p5020-esdhc") || of_device_is_compatible(np, "fsl,p4080-esdhc") || diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c index 66560a8fcfa2..1022e80aaf97 100644 --- a/drivers/net/bonding/bond_options.c +++ b/drivers/net/bonding/bond_options.c @@ -1066,13 +1066,6 @@ static int bond_option_arp_validate_set(struct bonding *bond, { netdev_info(bond->dev, "Setting arp_validate to %s (%llu)\n", newval->string, newval->value); - - if (bond->dev->flags & IFF_UP) { - if (!newval->value) - bond->recv_probe = NULL; - else if (bond->params.arp_interval) - bond->recv_probe = bond_arp_rcv; - } bond->params.arp_validate = newval->value; return 0; diff --git a/drivers/net/bonding/bond_sysfs_slave.c b/drivers/net/bonding/bond_sysfs_slave.c index 7d16c51e6913..641a532b67cb 100644 --- a/drivers/net/bonding/bond_sysfs_slave.c +++ b/drivers/net/bonding/bond_sysfs_slave.c @@ -55,7 +55,9 @@ static SLAVE_ATTR_RO(link_failure_count); static ssize_t perm_hwaddr_show(struct slave *slave, char *buf) { - return sprintf(buf, "%pM\n", slave->perm_hwaddr); + return sprintf(buf, "%*phC\n", + slave->dev->addr_len, + slave->perm_hwaddr); } static SLAVE_ATTR_RO(perm_hwaddr); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 403fa8d98aa3..d450d8b3708c 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -12824,6 +12824,24 @@ static netdev_features_t bnx2x_features_check(struct sk_buff *skb, struct net_device *dev, netdev_features_t features) { + /* + * A skb with gso_size + header length > 9700 will cause a + * firmware panic. Drop GSO support. + * + * Eventually the upper layer should not pass these packets down. + * + * For speed, if the gso_size is <= 9000, assume there will + * not be 700 bytes of headers and pass it through. Only do a + * full (slow) validation if the gso_size is > 9000. + * + * (Due to the way SKB_BY_FRAGS works this will also do a full + * validation in that case.) + */ + if (unlikely(skb_is_gso(skb) && + (skb_shinfo(skb)->gso_size > 9000) && + !skb_gso_validate_mac_len(skb, 9700))) + features &= ~NETIF_F_GSO_MASK; + features = vlan_features_check(skb, features); return vxlan_features_check(skb, features); } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 00bd7be85679..81282b811a6c 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -1140,6 +1140,8 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_napi *bnapi, u32 *raw_cons, skb = bnxt_copy_skb(bnapi, data, len, dma_addr); bnxt_reuse_rx_data(rxr, cons, data); if (!skb) { + if (agg_bufs) + bnxt_reuse_rx_agg_bufs(bnapi, cp_cons, agg_bufs); rc = -ENOMEM; goto next_rx; } @@ -4957,8 +4959,15 @@ static int bnxt_cfg_rx_mode(struct bnxt *bp) skip_uc: rc = bnxt_hwrm_cfa_l2_set_rx_mask(bp, 0); + if (rc && vnic->mc_list_count) { + netdev_info(bp->dev, "Failed setting MC filters rc: %d, turning on ALL_MCAST mode\n", + rc); + vnic->rx_mask |= CFA_L2_SET_RX_MASK_REQ_MASK_ALL_MCAST; + vnic->mc_list_count = 0; + rc = bnxt_hwrm_cfa_l2_set_rx_mask(bp, 0); + } if (rc) - netdev_err(bp->dev, "HWRM cfa l2 rx mask failure rc: %x\n", + netdev_err(bp->dev, "HWRM cfa l2 rx mask failure rc: %d\n", rc); return rc; diff --git a/drivers/net/ethernet/chelsio/cxgb3/l2t.h b/drivers/net/ethernet/chelsio/cxgb3/l2t.h index 8cffcdfd5678..38b5858c335a 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/l2t.h +++ b/drivers/net/ethernet/chelsio/cxgb3/l2t.h @@ -75,8 +75,8 @@ struct l2t_data { struct l2t_entry *rover; /* starting point for next allocation */ atomic_t nfree; /* number of free entries */ rwlock_t lock; - struct l2t_entry l2tab[0]; struct rcu_head rcu_head; /* to handle rcu cleanup */ + struct l2t_entry l2tab[]; }; typedef void (*arp_failure_handler_func)(struct t3cdev * dev, diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index a3e1498ca67c..3b96622de8ff 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -5061,15 +5061,24 @@ static int __init cxgb4_init_module(void) ret = pci_register_driver(&cxgb4_driver); if (ret < 0) - debugfs_remove(cxgb4_debugfs_root); + goto err_pci; #if IS_ENABLED(CONFIG_IPV6) if (!inet6addr_registered) { - register_inet6addr_notifier(&cxgb4_inet6addr_notifier); - inet6addr_registered = true; + ret = register_inet6addr_notifier(&cxgb4_inet6addr_notifier); + if (ret) + pci_unregister_driver(&cxgb4_driver); + else + inet6addr_registered = true; } #endif + if (ret == 0) + return ret; + +err_pci: + debugfs_remove(cxgb4_debugfs_root); + return ret; } diff --git a/drivers/net/ethernet/dec/tulip/de4x5.c b/drivers/net/ethernet/dec/tulip/de4x5.c index 3acde3b9b767..7799cf33cc6e 100644 --- a/drivers/net/ethernet/dec/tulip/de4x5.c +++ b/drivers/net/ethernet/dec/tulip/de4x5.c @@ -2106,7 +2106,6 @@ static struct eisa_driver de4x5_eisa_driver = { .remove = de4x5_eisa_remove, } }; -MODULE_DEVICE_TABLE(eisa, de4x5_eisa_ids); #endif #ifdef CONFIG_PCI diff --git a/drivers/net/ethernet/emulex/benet/be_ethtool.c b/drivers/net/ethernet/emulex/benet/be_ethtool.c index 734f655c99c1..51bfe74be8d4 100644 --- a/drivers/net/ethernet/emulex/benet/be_ethtool.c +++ b/drivers/net/ethernet/emulex/benet/be_ethtool.c @@ -1050,7 +1050,7 @@ static int be_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd, cmd->data = be_get_rss_hash_opts(adapter, cmd->flow_type); break; case ETHTOOL_GRXRINGS: - cmd->data = adapter->num_rx_qs - 1; + cmd->data = adapter->num_rx_qs; break; default: return -EINVAL; diff --git a/drivers/net/ethernet/freescale/ucc_geth_ethtool.c b/drivers/net/ethernet/freescale/ucc_geth_ethtool.c index 89714f5e0dfc..c8b9a73d6b1b 100644 --- a/drivers/net/ethernet/freescale/ucc_geth_ethtool.c +++ b/drivers/net/ethernet/freescale/ucc_geth_ethtool.c @@ -253,14 +253,12 @@ uec_set_ringparam(struct net_device *netdev, return -EINVAL; } + if (netif_running(netdev)) + return -EBUSY; + ug_info->bdRingLenRx[queue] = ring->rx_pending; ug_info->bdRingLenTx[queue] = ring->tx_pending; - if (netif_running(netdev)) { - /* FIXME: restart automatically */ - netdev_info(netdev, "Please re-open the interface\n"); - } - return ret; } diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.c b/drivers/net/ethernet/hisilicon/hns/hnae.c index b3645297477e..3ce41efe8a94 100644 --- a/drivers/net/ethernet/hisilicon/hns/hnae.c +++ b/drivers/net/ethernet/hisilicon/hns/hnae.c @@ -144,7 +144,6 @@ out_buffer_fail: /* free desc along with its attached buffer */ static void hnae_free_desc(struct hnae_ring *ring) { - hnae_free_buffers(ring); dma_unmap_single(ring_to_dev(ring), ring->desc_dma_addr, ring->desc_num * sizeof(ring->desc[0]), ring_to_dma_dir(ring)); @@ -177,6 +176,9 @@ static int hnae_alloc_desc(struct hnae_ring *ring) /* fini ring, also free the buffer for the ring */ static void hnae_fini_ring(struct hnae_ring *ring) { + if (is_rx_ring(ring)) + hnae_free_buffers(ring); + hnae_free_desc(ring); kfree(ring->desc_cb); ring->desc_cb = NULL; diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c index 7948c5a9e5fc..bd5c22986343 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c @@ -28,9 +28,6 @@ #define SERVICE_TIMER_HZ (1 * HZ) -#define NIC_TX_CLEAN_MAX_NUM 256 -#define NIC_RX_CLEAN_MAX_NUM 64 - #define RCB_IRQ_NOT_INITED 0 #define RCB_IRQ_INITED 1 @@ -1406,7 +1403,7 @@ static int hns_nic_init_ring_data(struct hns_nic_priv *priv) rd->fini_process = hns_nic_tx_fini_pro; netif_napi_add(priv->netdev, &rd->napi, - hns_nic_common_poll, NIC_TX_CLEAN_MAX_NUM); + hns_nic_common_poll, NAPI_POLL_WEIGHT); rd->ring->irq_init_flag = RCB_IRQ_NOT_INITED; } for (i = h->q_num; i < h->q_num * 2; i++) { @@ -1418,7 +1415,7 @@ static int hns_nic_init_ring_data(struct hns_nic_priv *priv) rd->fini_process = hns_nic_rx_fini_pro; netif_napi_add(priv->netdev, &rd->napi, - hns_nic_common_poll, NIC_RX_CLEAN_MAX_NUM); + hns_nic_common_poll, NAPI_POLL_WEIGHT); rd->ring->irq_init_flag = RCB_IRQ_NOT_INITED; } diff --git a/drivers/net/ethernet/ibm/ehea/ehea_main.c b/drivers/net/ethernet/ibm/ehea/ehea_main.c index 2a0dc127df3f..1a56de06b014 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_main.c +++ b/drivers/net/ethernet/ibm/ehea/ehea_main.c @@ -3183,6 +3183,7 @@ static ssize_t ehea_probe_port(struct device *dev, if (ehea_add_adapter_mr(adapter)) { pr_err("creating MR failed\n"); + of_node_put(eth_dn); return -EIO; } diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 06b38f50980c..22c43a776c6c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -2263,6 +2263,10 @@ void i40e_vlan_stripping_enable(struct i40e_vsi *vsi) struct i40e_vsi_context ctxt; i40e_status ret; + /* Don't modify stripping options if a port VLAN is active */ + if (vsi->info.pvid) + return; + if ((vsi->info.valid_sections & cpu_to_le16(I40E_AQ_VSI_PROP_VLAN_VALID)) && ((vsi->info.port_vlan_flags & I40E_AQ_VSI_PVLAN_MODE_MASK) == 0)) @@ -2293,6 +2297,10 @@ void i40e_vlan_stripping_disable(struct i40e_vsi *vsi) struct i40e_vsi_context ctxt; i40e_status ret; + /* Don't modify stripping options if a port VLAN is active */ + if (vsi->info.pvid) + return; + if ((vsi->info.valid_sections & cpu_to_le16(I40E_AQ_VSI_PROP_VLAN_VALID)) && ((vsi->info.port_vlan_flags & I40E_AQ_VSI_PVLAN_EMOD_MASK) == diff --git a/drivers/net/ethernet/intel/igb/e1000_defines.h b/drivers/net/ethernet/intel/igb/e1000_defines.h index b1915043bc0c..7b9fb71137da 100644 --- a/drivers/net/ethernet/intel/igb/e1000_defines.h +++ b/drivers/net/ethernet/intel/igb/e1000_defines.h @@ -193,6 +193,8 @@ /* enable link status from external LINK_0 and LINK_1 pins */ #define E1000_CTRL_SWDPIN0 0x00040000 /* SWDPIN 0 value */ #define E1000_CTRL_SWDPIN1 0x00080000 /* SWDPIN 1 value */ +#define E1000_CTRL_ADVD3WUC 0x00100000 /* D3 WUC */ +#define E1000_CTRL_EN_PHY_PWR_MGMT 0x00200000 /* PHY PM enable */ #define E1000_CTRL_SDP0_DIR 0x00400000 /* SDP0 Data direction */ #define E1000_CTRL_SDP1_DIR 0x00800000 /* SDP1 Data direction */ #define E1000_CTRL_RST 0x04000000 /* Global reset */ diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index c1796aa2dde5..70ed5e5c3514 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -7325,9 +7325,7 @@ static int __igb_shutdown(struct pci_dev *pdev, bool *enable_wake, struct e1000_hw *hw = &adapter->hw; u32 ctrl, rctl, status; u32 wufc = runtime ? E1000_WUFC_LNKC : adapter->wol; -#ifdef CONFIG_PM - int retval = 0; -#endif + bool wake; rtnl_lock(); netif_device_detach(netdev); @@ -7338,14 +7336,6 @@ static int __igb_shutdown(struct pci_dev *pdev, bool *enable_wake, igb_clear_interrupt_scheme(adapter); rtnl_unlock(); -#ifdef CONFIG_PM - if (!runtime) { - retval = pci_save_state(pdev); - if (retval) - return retval; - } -#endif - status = rd32(E1000_STATUS); if (status & E1000_STATUS_LU) wufc &= ~E1000_WUFC_LNKC; @@ -7362,10 +7352,6 @@ static int __igb_shutdown(struct pci_dev *pdev, bool *enable_wake, } ctrl = rd32(E1000_CTRL); - /* advertise wake from D3Cold */ - #define E1000_CTRL_ADVD3WUC 0x00100000 - /* phy power management enable */ - #define E1000_CTRL_EN_PHY_PWR_MGMT 0x00200000 ctrl |= E1000_CTRL_ADVD3WUC; wr32(E1000_CTRL, ctrl); @@ -7379,12 +7365,15 @@ static int __igb_shutdown(struct pci_dev *pdev, bool *enable_wake, wr32(E1000_WUFC, 0); } - *enable_wake = wufc || adapter->en_mng_pt; - if (!*enable_wake) + wake = wufc || adapter->en_mng_pt; + if (!wake) igb_power_down_link(adapter); else igb_power_up_link(adapter); + if (enable_wake) + *enable_wake = wake; + /* Release control of h/w to f/w. If f/w is AMT enabled, this * would have already happened in close and is redundant. */ @@ -7399,22 +7388,7 @@ static int __igb_shutdown(struct pci_dev *pdev, bool *enable_wake, #ifdef CONFIG_PM_SLEEP static int igb_suspend(struct device *dev) { - int retval; - bool wake; - struct pci_dev *pdev = to_pci_dev(dev); - - retval = __igb_shutdown(pdev, &wake, 0); - if (retval) - return retval; - - if (wake) { - pci_prepare_to_sleep(pdev); - } else { - pci_wake_from_d3(pdev, false); - pci_set_power_state(pdev, PCI_D3hot); - } - - return 0; + return __igb_shutdown(to_pci_dev(dev), NULL, 0); } #endif /* CONFIG_PM_SLEEP */ @@ -7483,22 +7457,7 @@ static int igb_runtime_idle(struct device *dev) static int igb_runtime_suspend(struct device *dev) { - struct pci_dev *pdev = to_pci_dev(dev); - int retval; - bool wake; - - retval = __igb_shutdown(pdev, &wake, 1); - if (retval) - return retval; - - if (wake) { - pci_prepare_to_sleep(pdev); - } else { - pci_wake_from_d3(pdev, false); - pci_set_power_state(pdev, PCI_D3hot); - } - - return 0; + return __igb_shutdown(to_pci_dev(dev), NULL, 1); } static int igb_runtime_resume(struct device *dev) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 42305f3234ff..03f0d20aa08b 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -3940,7 +3940,7 @@ static inline void mvpp2_gmac_max_rx_size_set(struct mvpp2_port *port) /* Set defaults to the MVPP2 port */ static void mvpp2_defaults_set(struct mvpp2_port *port) { - int tx_port_num, val, queue, ptxq, lrxq; + int tx_port_num, val, queue, lrxq; /* Configure port to loopback if needed */ if (port->flags & MVPP2_F_LOOPBACK) @@ -3960,11 +3960,9 @@ static void mvpp2_defaults_set(struct mvpp2_port *port) mvpp2_write(port->priv, MVPP2_TXP_SCHED_CMD_1_REG, 0); /* Close bandwidth for all queues */ - for (queue = 0; queue < MVPP2_MAX_TXQ; queue++) { - ptxq = mvpp2_txq_phys(port->id, queue); + for (queue = 0; queue < MVPP2_MAX_TXQ; queue++) mvpp2_write(port->priv, - MVPP2_TXQ_SCHED_TOKEN_CNTR_REG(ptxq), 0); - } + MVPP2_TXQ_SCHED_TOKEN_CNTR_REG(queue), 0); /* Set refill period to 1 usec, refill tokens * and bucket size to maximum @@ -4722,7 +4720,7 @@ static void mvpp2_txq_deinit(struct mvpp2_port *port, txq->descs_phys = 0; /* Set minimum bandwidth for disabled TXQs */ - mvpp2_write(port->priv, MVPP2_TXQ_SCHED_TOKEN_CNTR_REG(txq->id), 0); + mvpp2_write(port->priv, MVPP2_TXQ_SCHED_TOKEN_CNTR_REG(txq->log_id), 0); /* Set Tx descriptors queue starting address and size */ mvpp2_write(port->priv, MVPP2_TXQ_NUM_REG, txq->id); diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c index bcfac000199e..fcd1e6b3950d 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c @@ -1906,6 +1906,8 @@ static int mlx4_en_set_tunable(struct net_device *dev, return ret; } +#define MLX4_EEPROM_PAGE_LEN 256 + static int mlx4_en_get_module_info(struct net_device *dev, struct ethtool_modinfo *modinfo) { @@ -1940,7 +1942,7 @@ static int mlx4_en_get_module_info(struct net_device *dev, break; case MLX4_MODULE_ID_SFP: modinfo->type = ETH_MODULE_SFF_8472; - modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN; + modinfo->eeprom_len = MLX4_EEPROM_PAGE_LEN; break; default: return -ENOSYS; diff --git a/drivers/net/ethernet/mellanox/mlx4/mcg.c b/drivers/net/ethernet/mellanox/mlx4/mcg.c index 897d061e4f03..3bf63de3a725 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mcg.c +++ b/drivers/net/ethernet/mellanox/mlx4/mcg.c @@ -1485,7 +1485,7 @@ int mlx4_flow_steer_promisc_add(struct mlx4_dev *dev, u8 port, rule.port = port; rule.qpn = qpn; INIT_LIST_HEAD(&rule.list); - mlx4_err(dev, "going promisc on %x\n", port); + mlx4_info(dev, "going promisc on %x\n", port); return mlx4_flow_attach(dev, &rule, regid_p); } diff --git a/drivers/net/ethernet/mellanox/mlx4/port.c b/drivers/net/ethernet/mellanox/mlx4/port.c index c2b21313dba7..a9c4818448f9 100644 --- a/drivers/net/ethernet/mellanox/mlx4/port.c +++ b/drivers/net/ethernet/mellanox/mlx4/port.c @@ -1398,11 +1398,6 @@ int mlx4_get_module_info(struct mlx4_dev *dev, u8 port, size -= offset + size - I2C_PAGE_SIZE; i2c_addr = I2C_ADDR_LOW; - if (offset >= I2C_PAGE_SIZE) { - /* Reset offset to high page */ - i2c_addr = I2C_ADDR_HIGH; - offset -= I2C_PAGE_SIZE; - } cable_info = (struct mlx4_cable_info *)inmad->data; cable_info->dev_mem_address = cpu_to_be16(offset); diff --git a/drivers/net/ethernet/micrel/ks8851.c b/drivers/net/ethernet/micrel/ks8851.c index 1edc973df4c4..7377dca6eb57 100644 --- a/drivers/net/ethernet/micrel/ks8851.c +++ b/drivers/net/ethernet/micrel/ks8851.c @@ -547,9 +547,8 @@ static void ks8851_rx_pkts(struct ks8851_net *ks) /* set dma read address */ ks8851_wrreg16(ks, KS_RXFDPR, RXFDPR_RXFPAI | 0x00); - /* start the packet dma process, and set auto-dequeue rx */ - ks8851_wrreg16(ks, KS_RXQCR, - ks->rc_rxqcr | RXQCR_SDA | RXQCR_ADRFE); + /* start DMA access */ + ks8851_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr | RXQCR_SDA); if (rxlen > 4) { unsigned int rxalign; @@ -580,7 +579,8 @@ static void ks8851_rx_pkts(struct ks8851_net *ks) } } - ks8851_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr); + /* end DMA access and dequeue packet */ + ks8851_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr | RXQCR_RRXEF); } } @@ -797,6 +797,15 @@ static void ks8851_tx_work(struct work_struct *work) static int ks8851_net_open(struct net_device *dev) { struct ks8851_net *ks = netdev_priv(dev); + int ret; + + ret = request_threaded_irq(dev->irq, NULL, ks8851_irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + dev->name, ks); + if (ret < 0) { + netdev_err(dev, "failed to get irq\n"); + return ret; + } /* lock the card, even if we may not actually be doing anything * else at the moment */ @@ -861,6 +870,7 @@ static int ks8851_net_open(struct net_device *dev) netif_dbg(ks, ifup, ks->netdev, "network device up\n"); mutex_unlock(&ks->lock); + mii_check_link(&ks->mii); return 0; } @@ -911,6 +921,8 @@ static int ks8851_net_stop(struct net_device *dev) dev_kfree_skb(txb); } + free_irq(dev->irq, ks); + return 0; } @@ -1516,6 +1528,7 @@ static int ks8851_probe(struct spi_device *spi) spi_set_drvdata(spi, ks); + netif_carrier_off(ks->netdev); ndev->if_port = IF_PORT_100BASET; ndev->netdev_ops = &ks8851_netdev_ops; ndev->irq = spi->irq; @@ -1542,14 +1555,6 @@ static int ks8851_probe(struct spi_device *spi) ks8851_read_selftest(ks); ks8851_init_mac(ks); - ret = request_threaded_irq(spi->irq, NULL, ks8851_irq, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - ndev->name, ks); - if (ret < 0) { - dev_err(&spi->dev, "failed to get irq\n"); - goto err_irq; - } - ret = register_netdev(ndev); if (ret) { dev_err(&spi->dev, "failed to register network device\n"); @@ -1562,14 +1567,10 @@ static int ks8851_probe(struct spi_device *spi) return 0; - err_netdev: - free_irq(ndev->irq, ks); - -err_irq: +err_id: if (gpio_is_valid(gpio)) gpio_set_value(gpio, 0); -err_id: regulator_disable(ks->vdd_reg); err_reg: regulator_disable(ks->vdd_io); @@ -1587,7 +1588,6 @@ static int ks8851_remove(struct spi_device *spi) dev_info(&spi->dev, "remove\n"); unregister_netdev(priv->netdev); - free_irq(spi->irq, priv); if (gpio_is_valid(priv->gpio)) gpio_set_value(priv->gpio, 0); regulator_disable(priv->vdd_reg); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c index 0a2318cad34d..63ebc491057b 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c @@ -1038,6 +1038,8 @@ int qlcnic_do_lb_test(struct qlcnic_adapter *adapter, u8 mode) for (i = 0; i < QLCNIC_NUM_ILB_PKT; i++) { skb = netdev_alloc_skb(adapter->netdev, QLCNIC_ILB_PKT_SIZE); + if (!skb) + break; qlcnic_create_loopback_buff(skb->data, adapter->mac_addr); skb_put(skb, QLCNIC_ILB_PKT_SIZE); adapter->ahw->diag_cnt = 0; diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index afaf79b8761f..2d9f4ed9a65e 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -1408,6 +1408,10 @@ static void sh_eth_dev_exit(struct net_device *ndev) sh_eth_get_stats(ndev); sh_eth_reset(ndev); + /* Set the RMII mode again if required */ + if (mdp->cd->rmiimode) + sh_eth_write(ndev, 0x1, RMIIMODE); + /* Set MAC address again */ update_mac_address(ndev); } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 059113dce6e0..f4d6512f066c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1792,8 +1792,6 @@ static int stmmac_open(struct net_device *dev) struct stmmac_priv *priv = netdev_priv(dev); int ret; - stmmac_check_ether_addr(priv); - if (priv->pcs != STMMAC_PCS_RGMII && priv->pcs != STMMAC_PCS_TBI && priv->pcs != STMMAC_PCS_RTBI) { ret = stmmac_init_phy(dev); @@ -2929,6 +2927,8 @@ int stmmac_dvr_probe(struct device *device, if (ret) goto error_hw_init; + stmmac_check_ether_addr(priv); + ndev->netdev_ops = &stmmac_netdev_ops; ndev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c index 90d95b3654f5..6bdde92869fb 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c @@ -154,7 +154,8 @@ int stmmac_mdio_reset(struct mii_bus *bus) of_property_read_u32_array(np, "snps,reset-delays-us", data->delays, 3); - if (gpio_request(data->reset_gpio, "mdio-reset")) + if (devm_gpio_request(priv->device, data->reset_gpio, + "mdio-reset")) return 0; } diff --git a/drivers/net/ethernet/ti/netcp_ethss.c b/drivers/net/ethernet/ti/netcp_ethss.c index 4e70e7586a09..a5732edc8437 100644 --- a/drivers/net/ethernet/ti/netcp_ethss.c +++ b/drivers/net/ethernet/ti/netcp_ethss.c @@ -3122,12 +3122,16 @@ static int gbe_probe(struct netcp_device *netcp_device, struct device *dev, ret = netcp_txpipe_init(&gbe_dev->tx_pipe, netcp_device, gbe_dev->dma_chan_name, gbe_dev->tx_queue_id); - if (ret) + if (ret) { + of_node_put(interfaces); return ret; + } ret = netcp_txpipe_open(&gbe_dev->tx_pipe); - if (ret) + if (ret) { + of_node_put(interfaces); return ret; + } /* Create network interfaces */ INIT_LIST_HEAD(&gbe_dev->gbe_intf_head); diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index 4684644703cc..58ba579793f8 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -1595,12 +1595,14 @@ static int axienet_probe(struct platform_device *pdev) ret = of_address_to_resource(np, 0, &dmares); if (ret) { dev_err(&pdev->dev, "unable to get DMA resource\n"); + of_node_put(np); goto free_netdev; } lp->dma_regs = devm_ioremap_resource(&pdev->dev, &dmares); if (IS_ERR(lp->dma_regs)) { dev_err(&pdev->dev, "could not map DMA regs\n"); ret = PTR_ERR(lp->dma_regs); + of_node_put(np); goto free_netdev; } lp->rx_irq = irq_of_parse_and_map(np, 1); diff --git a/drivers/net/ppp/ppp_deflate.c b/drivers/net/ppp/ppp_deflate.c index b5edc7f96a39..685e875f5164 100644 --- a/drivers/net/ppp/ppp_deflate.c +++ b/drivers/net/ppp/ppp_deflate.c @@ -610,12 +610,20 @@ static struct compressor ppp_deflate_draft = { static int __init deflate_init(void) { - int answer = ppp_register_compressor(&ppp_deflate); - if (answer == 0) - printk(KERN_INFO - "PPP Deflate Compression module registered\n"); - ppp_register_compressor(&ppp_deflate_draft); - return answer; + int rc; + + rc = ppp_register_compressor(&ppp_deflate); + if (rc) + return rc; + + rc = ppp_register_compressor(&ppp_deflate_draft); + if (rc) { + ppp_unregister_compressor(&ppp_deflate); + return rc; + } + + pr_info("PPP Deflate Compression module registered\n"); + return 0; } static void __exit deflate_cleanup(void) diff --git a/drivers/net/slip/slhc.c b/drivers/net/slip/slhc.c index cfd81eb1b532..ddceed3c5a4a 100644 --- a/drivers/net/slip/slhc.c +++ b/drivers/net/slip/slhc.c @@ -153,7 +153,7 @@ out_fail: void slhc_free(struct slcompress *comp) { - if ( comp == NULLSLCOMPR ) + if ( IS_ERR_OR_NULL(comp) ) return; if ( comp->tstate != NULLSLSTATE ) diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 267a90423154..7b3ef6dc45a4 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -1136,6 +1136,12 @@ static int team_port_add(struct team *team, struct net_device *port_dev) return -EINVAL; } + if (netdev_has_upper_dev(dev, port_dev)) { + netdev_err(dev, "Device %s is already an upper device of the team interface\n", + portname); + return -EBUSY; + } + if (port_dev->features & NETIF_F_VLAN_CHALLENGED && vlan_uses_dev(dev)) { netdev_err(dev, "Device %s is VLAN challenged and team device has VLAN set up\n", diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 36e1377fc954..1e921e5eddc7 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -727,7 +727,7 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_ int err; u8 iface_no; struct usb_cdc_parsed_header hdr; - u16 curr_ntb_format; + __le16 curr_ntb_format; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -841,7 +841,7 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_ goto error2; } - if (curr_ntb_format == USB_CDC_NCM_NTB32_FORMAT) { + if (curr_ntb_format == cpu_to_le16(USB_CDC_NCM_NTB32_FORMAT)) { dev_info(&intf->dev, "resetting NTB format to 16-bit"); err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_FORMAT, USB_TYPE_CLASS | USB_DIR_OUT diff --git a/drivers/net/usb/ipheth.c b/drivers/net/usb/ipheth.c index f1f8227e7342..2b16a5fed9de 100644 --- a/drivers/net/usb/ipheth.c +++ b/drivers/net/usb/ipheth.c @@ -148,6 +148,7 @@ struct ipheth_device { u8 bulk_in; u8 bulk_out; struct delayed_work carrier_work; + bool confirmed_pairing; }; static int ipheth_rx_submit(struct ipheth_device *dev, gfp_t mem_flags); @@ -259,7 +260,7 @@ static void ipheth_rcvbulk_callback(struct urb *urb) dev->net->stats.rx_packets++; dev->net->stats.rx_bytes += len; - + dev->confirmed_pairing = true; netif_rx(skb); ipheth_rx_submit(dev, GFP_ATOMIC); } @@ -280,14 +281,24 @@ static void ipheth_sndbulk_callback(struct urb *urb) dev_err(&dev->intf->dev, "%s: urb status: %d\n", __func__, status); - netif_wake_queue(dev->net); + if (status == 0) + netif_wake_queue(dev->net); + else + // on URB error, trigger immediate poll + schedule_delayed_work(&dev->carrier_work, 0); } static int ipheth_carrier_set(struct ipheth_device *dev) { - struct usb_device *udev = dev->udev; + struct usb_device *udev; int retval; + if (!dev) + return 0; + if (!dev->confirmed_pairing) + return 0; + + udev = dev->udev; retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, IPHETH_CTRL_ENDP), IPHETH_CMD_CARRIER_CHECK, /* request */ @@ -302,11 +313,14 @@ static int ipheth_carrier_set(struct ipheth_device *dev) return retval; } - if (dev->ctrl_buf[0] == IPHETH_CARRIER_ON) + if (dev->ctrl_buf[0] == IPHETH_CARRIER_ON) { netif_carrier_on(dev->net); - else + if (dev->tx_urb->status != -EINPROGRESS) + netif_wake_queue(dev->net); + } else { netif_carrier_off(dev->net); - + netif_stop_queue(dev->net); + } return 0; } @@ -386,7 +400,6 @@ static int ipheth_open(struct net_device *net) return retval; schedule_delayed_work(&dev->carrier_work, IPHETH_CARRIER_CHECK_TIMEOUT); - netif_start_queue(net); return retval; } @@ -424,17 +437,18 @@ static int ipheth_tx(struct sk_buff *skb, struct net_device *net) dev); dev->tx_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + netif_stop_queue(net); retval = usb_submit_urb(dev->tx_urb, GFP_ATOMIC); if (retval) { dev_err(&dev->intf->dev, "%s: usb_submit_urb: %d\n", __func__, retval); dev->net->stats.tx_errors++; dev_kfree_skb_any(skb); + netif_wake_queue(net); } else { dev->net->stats.tx_packets++; dev->net->stats.tx_bytes += skb->len; dev_consume_skb_any(skb); - netif_stop_queue(net); } return NETDEV_TX_OK; @@ -489,7 +503,7 @@ static int ipheth_probe(struct usb_interface *intf, dev->udev = udev; dev->net = netdev; dev->intf = intf; - + dev->confirmed_pairing = false; /* Set up endpoints */ hintf = usb_altnum_to_altsetting(intf, IPHETH_ALT_INTFNUM); if (hintf == NULL) { @@ -540,7 +554,9 @@ static int ipheth_probe(struct usb_interface *intf, retval = -EIO; goto err_register_netdev; } - + // carrier down and transmit queues stopped until packet from device + netif_carrier_off(netdev); + netif_tx_stop_all_queues(netdev); dev_info(&intf->dev, "Apple iPhone USB Ethernet device attached\n"); return 0; diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 932c759c7ecb..a3a66c481e91 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -501,6 +501,7 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags) if (netif_running (dev->net) && netif_device_present (dev->net) && + test_bit(EVENT_DEV_OPEN, &dev->flags) && !test_bit (EVENT_RX_HALT, &dev->flags) && !test_bit (EVENT_DEV_ASLEEP, &dev->flags)) { switch (retval = usb_submit_urb (urb, GFP_ATOMIC)) { @@ -1387,6 +1388,11 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb, spin_unlock_irqrestore(&dev->txq.lock, flags); goto drop; } + if (netif_queue_stopped(net)) { + usb_autopm_put_interface_async(dev->intf); + spin_unlock_irqrestore(&dev->txq.lock, flags); + goto drop; + } #ifdef CONFIG_PM /* if this triggers the device is still a sleep */ diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c index dab25136214a..da14eca2aa2c 100644 --- a/drivers/net/wireless/at76c50x-usb.c +++ b/drivers/net/wireless/at76c50x-usb.c @@ -2582,8 +2582,8 @@ static int __init at76_mod_init(void) if (result < 0) printk(KERN_ERR DRIVER_NAME ": usb_register failed (status %d)\n", result); - - led_trigger_register_simple("at76_usb-tx", &ledtrig_tx); + else + led_trigger_register_simple("at76_usb-tx", &ledtrig_tx); return result; } diff --git a/drivers/net/wireless/b43/phy_lp.c b/drivers/net/wireless/b43/phy_lp.c index 058a9f232050..55cb07693ae8 100644 --- a/drivers/net/wireless/b43/phy_lp.c +++ b/drivers/net/wireless/b43/phy_lp.c @@ -1834,7 +1834,7 @@ static void lpphy_papd_cal(struct b43_wldev *dev, struct lpphy_tx_gains gains, static void lpphy_papd_cal_txpwr(struct b43_wldev *dev) { struct b43_phy_lp *lpphy = dev->phy.lp; - struct lpphy_tx_gains gains, oldgains; + struct lpphy_tx_gains oldgains; int old_txpctl, old_afe_ovr, old_rf, old_bbmult; lpphy_read_tx_pctl_mode_from_hardware(dev); @@ -1848,9 +1848,9 @@ static void lpphy_papd_cal_txpwr(struct b43_wldev *dev) lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF); if (dev->dev->chip_id == 0x4325 && dev->dev->chip_rev == 0) - lpphy_papd_cal(dev, gains, 0, 1, 30); + lpphy_papd_cal(dev, oldgains, 0, 1, 30); else - lpphy_papd_cal(dev, gains, 0, 1, 65); + lpphy_papd_cal(dev, oldgains, 0, 1, 65); if (old_afe_ovr) lpphy_set_tx_gains(dev, oldgains); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/brcm80211/brcmfmac/bus.h index 230cad788ace..84b8b1eaa22c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bus.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/bus.h @@ -214,7 +214,9 @@ bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, struct sk_buff *pkt, int prec); /* Receive frame for delivery to OS. Callee disposes of rxp. */ -void brcmf_rx_frame(struct device *dev, struct sk_buff *rxp); +void brcmf_rx_frame(struct device *dev, struct sk_buff *rxp, bool handle_event); +/* Receive async event packet from firmware. Callee disposes of rxp. */ +void brcmf_rx_event(struct device *dev, struct sk_buff *rxp); /* Indication from bus module regarding presence/insertion of dongle. */ int brcmf_attach(struct device *dev); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index ad35e760ed3f..231c0ba6acb9 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -3328,9 +3328,15 @@ brcmf_notify_sched_scan_results(struct brcmf_if *ifp, struct brcmf_pno_scanresults_le *pfn_result; u32 result_count; u32 status; + u32 datalen; brcmf_dbg(SCAN, "Enter\n"); + if (e->datalen < (sizeof(*pfn_result) + sizeof(*netinfo))) { + brcmf_dbg(SCAN, "Event data to small. Ignore\n"); + return 0; + } + if (e->event_code == BRCMF_E_PFN_NET_LOST) { brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n"); return 0; @@ -3349,6 +3355,14 @@ brcmf_notify_sched_scan_results(struct brcmf_if *ifp, if (result_count > 0) { int i; + data += sizeof(struct brcmf_pno_scanresults_le); + netinfo_start = (struct brcmf_pno_net_info_le *)data; + datalen = e->datalen - ((void *)netinfo_start - (void *)pfn_result); + if (datalen < result_count * sizeof(*netinfo)) { + brcmf_err("insufficient event data\n"); + goto out_err; + } + request = kzalloc(sizeof(*request), GFP_KERNEL); ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL); channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL); @@ -3358,9 +3372,6 @@ brcmf_notify_sched_scan_results(struct brcmf_if *ifp, } request->wiphy = wiphy; - data += sizeof(struct brcmf_pno_scanresults_le); - netinfo_start = (struct brcmf_pno_net_info_le *)data; - for (i = 0; i < result_count; i++) { netinfo = &netinfo_start[i]; if (!netinfo) { @@ -3370,6 +3381,8 @@ brcmf_notify_sched_scan_results(struct brcmf_if *ifp, goto out_err; } + if (netinfo->SSID_len > IEEE80211_MAX_SSID_LEN) + netinfo->SSID_len = IEEE80211_MAX_SSID_LEN; brcmf_dbg(SCAN, "SSID:%s Channel:%d\n", netinfo->SSID, netinfo->channel); memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len); @@ -4836,6 +4849,8 @@ static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg, conn_info->req_ie = kmemdup(cfg->extra_buf, conn_info->req_ie_len, GFP_KERNEL); + if (!conn_info->req_ie) + conn_info->req_ie_len = 0; } else { conn_info->req_ie_len = 0; conn_info->req_ie = NULL; @@ -4852,6 +4867,8 @@ static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg, conn_info->resp_ie = kmemdup(cfg->extra_buf, conn_info->resp_ie_len, GFP_KERNEL); + if (!conn_info->resp_ie) + conn_info->resp_ie_len = 0; } else { conn_info->resp_ie_len = 0; conn_info->resp_ie = NULL; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c index 82753e7c7e7c..3082391c3062 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c @@ -303,15 +303,9 @@ void brcmf_txflowblock(struct device *dev, bool state) void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb) { - skb->dev = ifp->ndev; - skb->protocol = eth_type_trans(skb, skb->dev); - if (skb->pkt_type == PACKET_MULTICAST) ifp->stats.multicast++; - /* Process special event packets */ - brcmf_fweh_process_skb(ifp->drvr, skb); - if (!(ifp->ndev->flags & IFF_UP)) { brcmu_pkt_buf_free_skb(skb); return; @@ -526,7 +520,7 @@ netif_rx: } } -void brcmf_rx_frame(struct device *dev, struct sk_buff *skb) +void brcmf_rx_frame(struct device *dev, struct sk_buff *skb, bool handle_event) { struct brcmf_if *ifp; struct brcmf_bus *bus_if = dev_get_drvdata(dev); @@ -546,11 +540,44 @@ void brcmf_rx_frame(struct device *dev, struct sk_buff *skb) return; } + skb->protocol = eth_type_trans(skb, ifp->ndev); + rd = (struct brcmf_skb_reorder_data *)skb->cb; - if (rd->reorder) + if (rd->reorder) { brcmf_rxreorder_process_info(ifp, rd->reorder, skb); - else + } else { + /* Process special event packets */ + if (handle_event) + brcmf_fweh_process_skb(ifp->drvr, skb, + BCMILCP_SUBTYPE_VENDOR_LONG); + brcmf_netif_rx(ifp, skb); + } +} + +void brcmf_rx_event(struct device *dev, struct sk_buff *skb) +{ + struct brcmf_if *ifp; + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pub *drvr = bus_if->drvr; + int ret; + + brcmf_dbg(EVENT, "Enter: %s: rxp=%p\n", dev_name(dev), skb); + + /* process and remove protocol-specific header */ + ret = brcmf_proto_hdrpull(drvr, true, skb, &ifp); + + if (ret || !ifp || !ifp->ndev) { + if (ret != -ENODATA && ifp) + ifp->stats.rx_errors++; + brcmu_pkt_buf_free_skb(skb); + return; + } + + skb->protocol = eth_type_trans(skb, ifp->ndev); + + brcmf_fweh_process_skb(ifp->drvr, skb, 0); + brcmu_pkt_buf_free_skb(skb); } void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c index 3878b6f6cfce..f9aa37032c2d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c @@ -26,50 +26,6 @@ #include "fwil.h" /** - * struct brcm_ethhdr - broadcom specific ether header. - * - * @subtype: subtype for this packet. - * @length: TODO: length of appended data. - * @version: version indication. - * @oui: OUI of this packet. - * @usr_subtype: subtype for this OUI. - */ -struct brcm_ethhdr { - __be16 subtype; - __be16 length; - u8 version; - u8 oui[3]; - __be16 usr_subtype; -} __packed; - -struct brcmf_event_msg_be { - __be16 version; - __be16 flags; - __be32 event_type; - __be32 status; - __be32 reason; - __be32 auth_type; - __be32 datalen; - u8 addr[ETH_ALEN]; - char ifname[IFNAMSIZ]; - u8 ifidx; - u8 bsscfgidx; -} __packed; - -/** - * struct brcmf_event - contents of broadcom event packet. - * - * @eth: standard ether header. - * @hdr: broadcom specific ether header. - * @msg: common part of the actual event message. - */ -struct brcmf_event { - struct ethhdr eth; - struct brcm_ethhdr hdr; - struct brcmf_event_msg_be msg; -} __packed; - -/** * struct brcmf_fweh_queue_item - event item on event queue. * * @q: list element for queuing. @@ -85,6 +41,7 @@ struct brcmf_fweh_queue_item { u8 ifidx; u8 ifaddr[ETH_ALEN]; struct brcmf_event_msg_be emsg; + u32 datalen; u8 data[0]; }; @@ -294,6 +251,11 @@ static void brcmf_fweh_event_worker(struct work_struct *work) brcmf_dbg_hex_dump(BRCMF_EVENT_ON(), event->data, min_t(u32, emsg.datalen, 64), "event payload, len=%d\n", emsg.datalen); + if (emsg.datalen > event->datalen) { + brcmf_err("event invalid length header=%d, msg=%d\n", + event->datalen, emsg.datalen); + goto event_free; + } /* special handling of interface event */ if (event->code == BRCMF_E_IF) { @@ -439,7 +401,8 @@ int brcmf_fweh_activate_events(struct brcmf_if *ifp) * dispatch the event to a registered handler (using worker). */ void brcmf_fweh_process_event(struct brcmf_pub *drvr, - struct brcmf_event *event_packet) + struct brcmf_event *event_packet, + u32 packet_len) { enum brcmf_fweh_event_code code; struct brcmf_fweh_info *fweh = &drvr->fweh; @@ -459,6 +422,9 @@ void brcmf_fweh_process_event(struct brcmf_pub *drvr, if (code != BRCMF_E_IF && !fweh->evt_handler[code]) return; + if (datalen > BRCMF_DCMD_MAXLEN) + return; + if (in_interrupt()) alloc_flag = GFP_ATOMIC; @@ -472,6 +438,7 @@ void brcmf_fweh_process_event(struct brcmf_pub *drvr, /* use memcpy to get aligned event message */ memcpy(&event->emsg, &event_packet->msg, sizeof(event->emsg)); memcpy(event->data, data, datalen); + event->datalen = datalen; memcpy(event->ifaddr, event_packet->eth.h_dest, ETH_ALEN); brcmf_fweh_queue_event(fweh, event); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.h b/drivers/net/wireless/brcm80211/brcmfmac/fweh.h index d9a942842382..b53db92341ce 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.h @@ -27,7 +27,6 @@ struct brcmf_pub; struct brcmf_if; struct brcmf_cfg80211_info; -struct brcmf_event; /* list of firmware events */ #define BRCMF_FWEH_EVENT_ENUM_DEFLIST \ @@ -180,11 +179,53 @@ enum brcmf_fweh_event_code { /** * definitions for event packet validation. */ -#define BRCMF_EVENT_OUI_OFFSET 19 -#define BRCM_OUI "\x00\x10\x18" -#define DOT11_OUI_LEN 3 -#define BCMILCP_BCM_SUBTYPE_EVENT 1 +#define BRCM_OUI "\x00\x10\x18" +#define BCMILCP_BCM_SUBTYPE_EVENT 1 +#define BCMILCP_SUBTYPE_VENDOR_LONG 32769 +/** + * struct brcm_ethhdr - broadcom specific ether header. + * + * @subtype: subtype for this packet. + * @length: TODO: length of appended data. + * @version: version indication. + * @oui: OUI of this packet. + * @usr_subtype: subtype for this OUI. + */ +struct brcm_ethhdr { + __be16 subtype; + __be16 length; + u8 version; + u8 oui[3]; + __be16 usr_subtype; +} __packed; + +struct brcmf_event_msg_be { + __be16 version; + __be16 flags; + __be32 event_type; + __be32 status; + __be32 reason; + __be32 auth_type; + __be32 datalen; + u8 addr[ETH_ALEN]; + char ifname[IFNAMSIZ]; + u8 ifidx; + u8 bsscfgidx; +} __packed; + +/** + * struct brcmf_event - contents of broadcom event packet. + * + * @eth: standard ether header. + * @hdr: broadcom specific ether header. + * @msg: common part of the actual event message. + */ +struct brcmf_event { + struct ethhdr eth; + struct brcm_ethhdr hdr; + struct brcmf_event_msg_be msg; +} __packed; /** * struct brcmf_event_msg - firmware event message. @@ -256,34 +297,43 @@ void brcmf_fweh_unregister(struct brcmf_pub *drvr, enum brcmf_fweh_event_code code); int brcmf_fweh_activate_events(struct brcmf_if *ifp); void brcmf_fweh_process_event(struct brcmf_pub *drvr, - struct brcmf_event *event_packet); + struct brcmf_event *event_packet, + u32 packet_len); void brcmf_fweh_p2pdev_setup(struct brcmf_if *ifp, bool ongoing); static inline void brcmf_fweh_process_skb(struct brcmf_pub *drvr, - struct sk_buff *skb) + struct sk_buff *skb, u16 stype) { struct brcmf_event *event_packet; - u8 *data; - u16 usr_stype; + u16 subtype, usr_stype; /* only process events when protocol matches */ if (skb->protocol != cpu_to_be16(ETH_P_LINK_CTL)) return; - /* check for BRCM oui match */ + if ((skb->len + ETH_HLEN) < sizeof(*event_packet)) + return; + event_packet = (struct brcmf_event *)skb_mac_header(skb); - data = (u8 *)event_packet; - data += BRCMF_EVENT_OUI_OFFSET; - if (memcmp(BRCM_OUI, data, DOT11_OUI_LEN)) + + /* check subtype if needed */ + if (unlikely(stype)) { + subtype = get_unaligned_be16(&event_packet->hdr.subtype); + if (subtype != stype) + return; + } + + /* check for BRCM oui match */ + if (memcmp(BRCM_OUI, &event_packet->hdr.oui[0], + sizeof(event_packet->hdr.oui))) return; /* final match on usr_subtype */ - data += DOT11_OUI_LEN; - usr_stype = get_unaligned_be16(data); + usr_stype = get_unaligned_be16(&event_packet->hdr.usr_subtype); if (usr_stype != BCMILCP_BCM_SUBTYPE_EVENT) return; - brcmf_fweh_process_event(drvr, event_packet); + brcmf_fweh_process_event(drvr, event_packet, skb->len + ETH_HLEN); } #endif /* FWEH_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index 44e618f9d890..6f7138cea555 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -20,6 +20,7 @@ #include <linux/types.h> #include <linux/netdevice.h> +#include <linux/etherdevice.h> #include <brcmu_utils.h> #include <brcmu_wifi.h> @@ -1076,28 +1077,13 @@ static void brcmf_msgbuf_rxbuf_event_post(struct brcmf_msgbuf *msgbuf) } -static void -brcmf_msgbuf_rx_skb(struct brcmf_msgbuf *msgbuf, struct sk_buff *skb, - u8 ifidx) -{ - struct brcmf_if *ifp; - - ifp = brcmf_get_ifp(msgbuf->drvr, ifidx); - if (!ifp || !ifp->ndev) { - brcmf_err("Received pkt for invalid ifidx %d\n", ifidx); - brcmu_pkt_buf_free_skb(skb); - return; - } - brcmf_netif_rx(ifp, skb); -} - - static void brcmf_msgbuf_process_event(struct brcmf_msgbuf *msgbuf, void *buf) { struct msgbuf_rx_event *event; u32 idx; u16 buflen; struct sk_buff *skb; + struct brcmf_if *ifp; event = (struct msgbuf_rx_event *)buf; idx = le32_to_cpu(event->msg.request_id); @@ -1117,7 +1103,19 @@ static void brcmf_msgbuf_process_event(struct brcmf_msgbuf *msgbuf, void *buf) skb_trim(skb, buflen); - brcmf_msgbuf_rx_skb(msgbuf, skb, event->msg.ifidx); + ifp = brcmf_get_ifp(msgbuf->drvr, event->msg.ifidx); + if (!ifp || !ifp->ndev) { + brcmf_err("Received pkt for invalid ifidx %d\n", + event->msg.ifidx); + goto exit; + } + + skb->protocol = eth_type_trans(skb, ifp->ndev); + + brcmf_fweh_process_skb(ifp->drvr, skb, 0); + +exit: + brcmu_pkt_buf_free_skb(skb); } @@ -1129,6 +1127,7 @@ brcmf_msgbuf_process_rx_complete(struct brcmf_msgbuf *msgbuf, void *buf) u16 data_offset; u16 buflen; u32 idx; + struct brcmf_if *ifp; brcmf_msgbuf_update_rxbufpost_count(msgbuf, 1); @@ -1149,7 +1148,14 @@ brcmf_msgbuf_process_rx_complete(struct brcmf_msgbuf *msgbuf, void *buf) skb_trim(skb, buflen); - brcmf_msgbuf_rx_skb(msgbuf, skb, rx_complete->msg.ifidx); + ifp = brcmf_get_ifp(msgbuf->drvr, rx_complete->msg.ifidx); + if (!ifp || !ifp->ndev) { + brcmf_err("Received pkt for invalid ifidx %d\n", + rx_complete->msg.ifidx); + brcmu_pkt_buf_free_skb(skb); + return; + } + brcmf_netif_rx(ifp, skb); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c index 3196245ab820..e6c8b0d5afe0 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c @@ -1365,6 +1365,11 @@ int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp, u16 mgmt_type; u8 action; + if (e->datalen < sizeof(*rxframe)) { + brcmf_dbg(SCAN, "Event data to small. Ignore\n"); + return 0; + } + ch.chspec = be16_to_cpu(rxframe->chanspec); cfg->d11inf.decchspec(&ch); /* Check if wpa_supplicant has registered for this frame */ @@ -1862,6 +1867,11 @@ s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp, brcmf_dbg(INFO, "Enter: event %d reason %d\n", e->event_code, e->reason); + if (e->datalen < sizeof(*rxframe)) { + brcmf_dbg(SCAN, "Event data to small. Ignore\n"); + return 0; + } + ch.chspec = be16_to_cpu(rxframe->chanspec); cfg->d11inf.decchspec(&ch); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index b4d9b47584d6..b082b8859b17 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -1394,6 +1394,17 @@ static inline u8 brcmf_sdio_getdatoffset(u8 *swheader) return (u8)((hdrvalue & SDPCM_DOFFSET_MASK) >> SDPCM_DOFFSET_SHIFT); } +static inline bool brcmf_sdio_fromevntchan(u8 *swheader) +{ + u32 hdrvalue; + u8 ret; + + hdrvalue = *(u32 *)swheader; + ret = (u8)((hdrvalue & SDPCM_CHANNEL_MASK) >> SDPCM_CHANNEL_SHIFT); + + return (ret == SDPCM_EVENT_CHANNEL); +} + static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header, struct brcmf_sdio_hdrinfo *rd, enum brcmf_sdio_frmtype type) @@ -1754,7 +1765,11 @@ static u8 brcmf_sdio_rxglom(struct brcmf_sdio *bus, u8 rxseq) pfirst->len, pfirst->next, pfirst->prev); skb_unlink(pfirst, &bus->glom); - brcmf_rx_frame(bus->sdiodev->dev, pfirst); + if (brcmf_sdio_fromevntchan(&dptr[SDPCM_HWHDR_LEN])) + brcmf_rx_event(bus->sdiodev->dev, pfirst); + else + brcmf_rx_frame(bus->sdiodev->dev, pfirst, + false); bus->sdcnt.rxglompkts++; } @@ -2081,18 +2096,19 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) __skb_trim(pkt, rd->len); skb_pull(pkt, rd->dat_offset); + if (pkt->len == 0) + brcmu_pkt_buf_free_skb(pkt); + else if (rd->channel == SDPCM_EVENT_CHANNEL) + brcmf_rx_event(bus->sdiodev->dev, pkt); + else + brcmf_rx_frame(bus->sdiodev->dev, pkt, + false); + /* prepare the descriptor for the next read */ rd->len = rd->len_nxtfrm << 4; rd->len_nxtfrm = 0; /* treat all packet as event if we don't know */ rd->channel = SDPCM_EVENT_CHANNEL; - - if (pkt->len == 0) { - brcmu_pkt_buf_free_skb(pkt); - continue; - } - - brcmf_rx_frame(bus->sdiodev->dev, pkt); } rxcount = maxframes - rxleft; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 689e64d004bc..3002268e57f3 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -144,7 +144,7 @@ struct brcmf_usbdev_info { struct usb_device *usbdev; struct device *dev; - struct mutex dev_init_lock; + struct completion dev_init_done; int ctl_in_pipe, ctl_out_pipe; struct urb *ctl_urb; /* URB for control endpoint */ @@ -502,7 +502,7 @@ static void brcmf_usb_rx_complete(struct urb *urb) if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) { skb_put(skb, urb->actual_length); - brcmf_rx_frame(devinfo->dev, skb); + brcmf_rx_frame(devinfo->dev, skb, true); brcmf_usb_rx_refill(devinfo, req); } else { brcmu_pkt_buf_free_skb(skb); @@ -669,12 +669,18 @@ static int brcmf_usb_up(struct device *dev) static void brcmf_cancel_all_urbs(struct brcmf_usbdev_info *devinfo) { + int i; + if (devinfo->ctl_urb) usb_kill_urb(devinfo->ctl_urb); if (devinfo->bulk_urb) usb_kill_urb(devinfo->bulk_urb); - brcmf_usb_free_q(&devinfo->tx_postq, true); - brcmf_usb_free_q(&devinfo->rx_postq, true); + if (devinfo->tx_reqs) + for (i = 0; i < devinfo->bus_pub.ntxq; i++) + usb_kill_urb(devinfo->tx_reqs[i].urb); + if (devinfo->rx_reqs) + for (i = 0; i < devinfo->bus_pub.nrxq; i++) + usb_kill_urb(devinfo->rx_reqs[i].urb); } static void brcmf_usb_down(struct device *dev) @@ -1226,11 +1232,11 @@ static void brcmf_usb_probe_phase2(struct device *dev, if (ret) goto error; - mutex_unlock(&devinfo->dev_init_lock); + complete(&devinfo->dev_init_done); return; error: brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), ret); - mutex_unlock(&devinfo->dev_init_lock); + complete(&devinfo->dev_init_done); device_release_driver(dev); } @@ -1268,7 +1274,7 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) if (ret) goto fail; /* we are done */ - mutex_unlock(&devinfo->dev_init_lock); + complete(&devinfo->dev_init_done); return 0; } bus->chip = bus_pub->devid; @@ -1322,11 +1328,10 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) devinfo->usbdev = usb; devinfo->dev = &usb->dev; - /* Take an init lock, to protect for disconnect while still loading. + /* Init completion, to protect for disconnect while still loading. * Necessary because of the asynchronous firmware load construction */ - mutex_init(&devinfo->dev_init_lock); - mutex_lock(&devinfo->dev_init_lock); + init_completion(&devinfo->dev_init_done); usb_set_intfdata(intf, devinfo); @@ -1402,7 +1407,7 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) return 0; fail: - mutex_unlock(&devinfo->dev_init_lock); + complete(&devinfo->dev_init_done); kfree(devinfo); usb_set_intfdata(intf, NULL); return ret; @@ -1417,7 +1422,7 @@ brcmf_usb_disconnect(struct usb_interface *intf) devinfo = (struct brcmf_usbdev_info *)usb_get_intfdata(intf); if (devinfo) { - mutex_lock(&devinfo->dev_init_lock); + wait_for_completion(&devinfo->dev_init_done); /* Make sure that devinfo still exists. Firmware probe routines * may have released the device and cleared the intfdata. */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c index 8eff2753abad..d493021f6031 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c @@ -35,9 +35,10 @@ static int brcmf_cfg80211_vndr_cmds_dcmd_handler(struct wiphy *wiphy, struct brcmf_if *ifp; const struct brcmf_vndr_dcmd_hdr *cmdhdr = data; struct sk_buff *reply; - int ret, payload, ret_len; + unsigned int payload, ret_len; void *dcmd_buf = NULL, *wr_pointer; u16 msglen, maxmsglen = PAGE_SIZE - 0x100; + int ret; if (len < sizeof(*cmdhdr)) { brcmf_err("vendor command too short: %d\n", len); @@ -65,7 +66,7 @@ static int brcmf_cfg80211_vndr_cmds_dcmd_handler(struct wiphy *wiphy, brcmf_err("oversize return buffer %d\n", ret_len); ret_len = BRCMF_DCMD_MAXLEN; } - payload = max(ret_len, len) + 1; + payload = max_t(unsigned int, ret_len, len) + 1; dcmd_buf = vzalloc(payload); if (NULL == dcmd_buf) return -ENOMEM; diff --git a/drivers/net/wireless/cnss2/debug.c b/drivers/net/wireless/cnss2/debug.c index 577cbb4482b3..e4db712d1db7 100644 --- a/drivers/net/wireless/cnss2/debug.c +++ b/drivers/net/wireless/cnss2/debug.c @@ -114,12 +114,33 @@ static int cnss_stats_show_state(struct seq_file *s, return 0; } +static int cnss_stats_show_capability(struct seq_file *s, + struct cnss_plat_data *plat_priv) +{ + if (test_bit(CNSS_FW_READY, &plat_priv->driver_state)) { + seq_puts(s, "\n<---------- FW Capability ----------->\n"); + seq_printf(s, "Chip ID: 0x%x\n", + plat_priv->chip_info.chip_id); + seq_printf(s, "Chip family: 0x%x\n", + plat_priv->chip_info.chip_family); + seq_printf(s, "Board ID: 0x%x\n", + plat_priv->board_info.board_id); + seq_printf(s, "SOC Info: 0x%x\n", + plat_priv->soc_info.soc_id); + seq_printf(s, "Firmware Version: 0x%x\n", + plat_priv->fw_version_info.fw_version); + seq_printf(s, "Firmware Build Timestamp: %s\n", + plat_priv->fw_version_info.fw_build_timestamp); + } + return 0; +} + static int cnss_stats_show(struct seq_file *s, void *data) { struct cnss_plat_data *plat_priv = s->private; cnss_stats_show_state(s, plat_priv); - + cnss_stats_show_capability(s, plat_priv); return 0; } diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c index 3926023a1493..7dc419753a80 100644 --- a/drivers/net/wireless/cnss2/pci.c +++ b/drivers/net/wireless/cnss2/pci.c @@ -749,6 +749,12 @@ int cnss_wlan_register_driver(struct cnss_wlan_driver *driver_ops) return -ENODEV; } + if (plat_priv->bus_type != CNSS_BUS_PCI) { + cnss_pr_err("Wrong bus type. Expected bus_type %d\n", + plat_priv->bus_type); + return -EFAULT; + } + pci_priv = plat_priv->bus_priv; if (!pci_priv) { cnss_pr_err("pci_priv is NULL\n"); diff --git a/drivers/net/wireless/cnss2/qmi.c b/drivers/net/wireless/cnss2/qmi.c index d86213175495..7cc44634f031 100644 --- a/drivers/net/wireless/cnss2/qmi.c +++ b/drivers/net/wireless/cnss2/qmi.c @@ -22,9 +22,10 @@ #define WLFW_SERVICE_INS_ID_V01 1 #define WLFW_CLIENT_ID 0x4b4e454c -#define MAX_BDF_FILE_NAME 11 -#define DEFAULT_BDF_FILE_NAME "bdwlan.elf" -#define BDF_FILE_NAME_PREFIX "bdwlan.e" +#define MAX_BDF_FILE_NAME 32 +#define BDF_FILE_NAME_PREFIX "bdwlan" +#define DEFAULT_ELF_BDF_FILE_NAME "bdwlan.elf" +#define ELF_BDF_FILE_NAME_PREFIX "bdwlan.e" #define BIN_BDF_FILE_NAME_PREFIX "bdwlan.b" #define DEFAULT_BIN_BDF_FILE_NAME "bdwlan.bin" @@ -799,22 +800,33 @@ int cnss_wlfw_bdf_dnld_send_sync(struct cnss_plat_data *plat_priv) plat_priv->device_id == QCN7605_VER20_COMPOSITE_DEVICE_ID) bdf_type = CNSS_BDF_BIN; - if (plat_priv->board_info.board_id == 0xFF) + if (plat_priv->board_info.board_id == 0xFF) { if (bdf_type == CNSS_BDF_BIN) snprintf(filename, sizeof(filename), DEFAULT_BIN_BDF_FILE_NAME); else snprintf(filename, sizeof(filename), - DEFAULT_BDF_FILE_NAME); - else { + DEFAULT_ELF_BDF_FILE_NAME); + } else if (plat_priv->board_info.board_id < 0xFF) { if (bdf_type == CNSS_BDF_BIN) snprintf(filename, sizeof(filename), BIN_BDF_FILE_NAME_PREFIX "%02x", plat_priv->board_info.board_id); else snprintf(filename, sizeof(filename), - BDF_FILE_NAME_PREFIX "%02x", + ELF_BDF_FILE_NAME_PREFIX "%02x", plat_priv->board_info.board_id); + } else { + if (bdf_type == CNSS_BDF_BIN) + snprintf(filename, sizeof(filename), + BDF_FILE_NAME_PREFIX "%02x.b%02x", + plat_priv->board_info.board_id >> 8 & 0xFF, + plat_priv->board_info.board_id & 0xFF); + else + snprintf(filename, sizeof(filename), + BDF_FILE_NAME_PREFIX "%02x.e%02x", + plat_priv->board_info.board_id >> 8 & 0xFF, + plat_priv->board_info.board_id & 0xFF); } if (bdf_bypass) { diff --git a/drivers/net/wireless/cnss2/usb.c b/drivers/net/wireless/cnss2/usb.c index a15f967f986d..3d3932371b26 100644 --- a/drivers/net/wireless/cnss2/usb.c +++ b/drivers/net/wireless/cnss2/usb.c @@ -73,6 +73,12 @@ int cnss_usb_wlan_register_driver(struct cnss_usb_wlan_driver *driver_ops) return -ENODEV; } + if (plat_priv->bus_type != CNSS_BUS_USB) { + cnss_pr_err("Wrong bus type. Expected bus_type %d\n", + plat_priv->bus_type); + return -EFAULT; + } + usb_priv = plat_priv->bus_priv; usb_priv->plat_priv = plat_priv; diff --git a/drivers/net/wireless/cw1200/main.c b/drivers/net/wireless/cw1200/main.c index 0e51e27d2e3f..317daa968e03 100644 --- a/drivers/net/wireless/cw1200/main.c +++ b/drivers/net/wireless/cw1200/main.c @@ -345,6 +345,11 @@ static struct ieee80211_hw *cw1200_init_common(const u8 *macaddr, mutex_init(&priv->wsm_cmd_mux); mutex_init(&priv->conf_mutex); priv->workqueue = create_singlethread_workqueue("cw1200_wq"); + if (!priv->workqueue) { + ieee80211_free_hw(hw); + return NULL; + } + sema_init(&priv->scan.lock, 1); INIT_WORK(&priv->scan.work, cw1200_scan_work); INIT_DELAYED_WORK(&priv->scan.probe_work, cw1200_probe_work); diff --git a/drivers/net/wireless/cw1200/scan.c b/drivers/net/wireless/cw1200/scan.c index 9f1037e7e55c..2ce0193614f2 100644 --- a/drivers/net/wireless/cw1200/scan.c +++ b/drivers/net/wireless/cw1200/scan.c @@ -84,8 +84,11 @@ int cw1200_hw_scan(struct ieee80211_hw *hw, frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0, req->ie_len); - if (!frame.skb) + if (!frame.skb) { + mutex_unlock(&priv->conf_mutex); + up(&priv->scan.lock); return -ENOMEM; + } if (req->ie_len) memcpy(skb_put(frame.skb, req->ie_len), req->ie, req->ie_len); diff --git a/drivers/net/wireless/mwifiex/cfp.c b/drivers/net/wireless/mwifiex/cfp.c index 3ddb8ec676ed..6dd331dfb517 100644 --- a/drivers/net/wireless/mwifiex/cfp.c +++ b/drivers/net/wireless/mwifiex/cfp.c @@ -533,5 +533,8 @@ u8 mwifiex_adjust_data_rate(struct mwifiex_private *priv, rate_index = (rx_rate > MWIFIEX_RATE_INDEX_OFDM0) ? rx_rate - 1 : rx_rate; + if (rate_index >= MWIFIEX_MAX_AC_RX_RATES) + rate_index = MWIFIEX_MAX_AC_RX_RATES - 1; + return rate_index; } diff --git a/drivers/net/wireless/realtek/rtlwifi/base.c b/drivers/net/wireless/realtek/rtlwifi/base.c index aab752328c26..5013d8c1d4a6 100644 --- a/drivers/net/wireless/realtek/rtlwifi/base.c +++ b/drivers/net/wireless/realtek/rtlwifi/base.c @@ -466,6 +466,11 @@ static void _rtl_init_deferred_work(struct ieee80211_hw *hw) /* <2> work queue */ rtlpriv->works.hw = hw; rtlpriv->works.rtl_wq = alloc_workqueue("%s", 0, 0, rtlpriv->cfg->name); + if (unlikely(!rtlpriv->works.rtl_wq)) { + pr_err("Failed to allocate work queue\n"); + return; + } + INIT_DELAYED_WORK(&rtlpriv->works.watchdog_wq, (void *)rtl_watchdog_wq_callback); INIT_DELAYED_WORK(&rtlpriv->works.ips_nic_off_wq, diff --git a/drivers/nvdimm/btt_devs.c b/drivers/nvdimm/btt_devs.c index cb477518dd0e..4c129450495d 100644 --- a/drivers/nvdimm/btt_devs.c +++ b/drivers/nvdimm/btt_devs.c @@ -170,14 +170,15 @@ static struct device *__nd_btt_create(struct nd_region *nd_region, return NULL; nd_btt->id = ida_simple_get(&nd_region->btt_ida, 0, 0, GFP_KERNEL); - if (nd_btt->id < 0) { - kfree(nd_btt); - return NULL; - } + if (nd_btt->id < 0) + goto out_nd_btt; nd_btt->lbasize = lbasize; - if (uuid) + if (uuid) { uuid = kmemdup(uuid, 16, GFP_KERNEL); + if (!uuid) + goto out_put_id; + } nd_btt->uuid = uuid; dev = &nd_btt->dev; dev_set_name(dev, "btt%d.%d", nd_region->id, nd_btt->id); @@ -192,6 +193,13 @@ static struct device *__nd_btt_create(struct nd_region *nd_region, return NULL; } return dev; + +out_put_id: + ida_simple_remove(&nd_region->btt_ida, nd_btt->id); + +out_nd_btt: + kfree(nd_btt); + return NULL; } struct device *nd_btt_create(struct nd_region *nd_region) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 6fd4e5a5ef4a..931cc33e46f0 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -789,7 +789,7 @@ static inline void nvmem_shift_read_buffer_in_place(struct nvmem_cell *cell, void *buf) { u8 *p, *b; - int i, bit_offset = cell->bit_offset; + int i, extra, bit_offset = cell->bit_offset; p = b = buf; if (bit_offset) { @@ -804,11 +804,16 @@ static inline void nvmem_shift_read_buffer_in_place(struct nvmem_cell *cell, p = b; *b++ >>= bit_offset; } - - /* result fits in less bytes */ - if (cell->bytes != DIV_ROUND_UP(cell->nbits, BITS_PER_BYTE)) - *p-- = 0; + } else { + /* point to the msb */ + p += cell->bytes - 1; } + + /* result fits in less bytes */ + extra = cell->bytes - DIV_ROUND_UP(cell->nbits, BITS_PER_BYTE); + while (--extra >= 0) + *p-- = 0; + /* clear msb bits if any leftover in the last byte */ *p &= GENMASK((cell->nbits%BITS_PER_BYTE) - 1, 0); } diff --git a/drivers/parisc/ccio-dma.c b/drivers/parisc/ccio-dma.c index 34f1d6b41fb9..cc3708ea8084 100644 --- a/drivers/parisc/ccio-dma.c +++ b/drivers/parisc/ccio-dma.c @@ -563,8 +563,6 @@ ccio_io_pdir_entry(u64 *pdir_ptr, space_t sid, unsigned long vba, /* We currently only support kernel addresses */ BUG_ON(sid != KERNEL_SPACE); - mtsp(sid,1); - /* ** WORD 1 - low order word ** "hints" parm includes the VALID bit! @@ -595,7 +593,7 @@ ccio_io_pdir_entry(u64 *pdir_ptr, space_t sid, unsigned long vba, ** Grab virtual index [0:11] ** Deposit virt_idx bits into I/O PDIR word */ - asm volatile ("lci %%r0(%%sr1, %1), %0" : "=r" (ci) : "r" (vba)); + asm volatile ("lci %%r0(%1), %0" : "=r" (ci) : "r" (vba)); asm volatile ("extru %1,19,12,%0" : "+r" (ci) : "r" (ci)); asm volatile ("depw %1,15,12,%0" : "+r" (pa) : "r" (ci)); diff --git a/drivers/parisc/sba_iommu.c b/drivers/parisc/sba_iommu.c index d6326144ce01..f3b9746157f8 100644 --- a/drivers/parisc/sba_iommu.c +++ b/drivers/parisc/sba_iommu.c @@ -573,8 +573,7 @@ sba_io_pdir_entry(u64 *pdir_ptr, space_t sid, unsigned long vba, pa = virt_to_phys(vba); pa &= IOVP_MASK; - mtsp(sid,1); - asm("lci 0(%%sr1, %1), %0" : "=r" (ci) : "r" (vba)); + asm("lci 0(%1), %0" : "=r" (ci) : "r" (vba)); pa |= (ci >> PAGE_SHIFT) & 0xff; /* move CI (8 bits) into lowest byte */ pa |= SBA_PDIR_VALID_BIT; /* set "valid" bit */ diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c index 414c33686621..b18cf12731ee 100644 --- a/drivers/pci/host/pcie-rcar.c +++ b/drivers/pci/host/pcie-rcar.c @@ -737,6 +737,10 @@ static int rcar_pcie_enable_msi(struct rcar_pcie *pcie) /* setup MSI data target */ msi->pages = __get_free_pages(GFP_KERNEL, 0); + if (!msi->pages) { + err = -ENOMEM; + goto err; + } base = virt_to_phys((void *)msi->pages); rcar_pci_write_reg(pcie, base | MSIFE, PCIEMSIALR); diff --git a/drivers/pci/host/pcie-xilinx.c b/drivers/pci/host/pcie-xilinx.c index 4cfa46360d12..6a2499f4d610 100644 --- a/drivers/pci/host/pcie-xilinx.c +++ b/drivers/pci/host/pcie-xilinx.c @@ -349,14 +349,19 @@ static const struct irq_domain_ops msi_domain_ops = { * xilinx_pcie_enable_msi - Enable MSI support * @port: PCIe port information */ -static void xilinx_pcie_enable_msi(struct xilinx_pcie_port *port) +static int xilinx_pcie_enable_msi(struct xilinx_pcie_port *port) { phys_addr_t msg_addr; port->msi_pages = __get_free_pages(GFP_KERNEL, 0); + if (!port->msi_pages) + return -ENOMEM; + msg_addr = virt_to_phys((void *)port->msi_pages); pcie_write(port, 0x0, XILINX_PCIE_REG_MSIBASE1); pcie_write(port, msg_addr, XILINX_PCIE_REG_MSIBASE2); + + return 0; } /* INTx Functions */ @@ -555,6 +560,7 @@ static int xilinx_pcie_init_irq_domain(struct xilinx_pcie_port *port) struct device *dev = port->dev; struct device_node *node = dev->of_node; struct device_node *pcie_intc_node; + int ret; /* Setup INTx */ pcie_intc_node = of_get_next_child(node, NULL); @@ -582,7 +588,9 @@ static int xilinx_pcie_init_irq_domain(struct xilinx_pcie_port *port) return PTR_ERR(port->irq_domain); } - xilinx_pcie_enable_msi(port); + ret = xilinx_pcie_enable_msi(port); + if (ret) + return ret; } return 0; diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c index f2fcbe944d94..aae295708ea7 100644 --- a/drivers/pci/hotplug/rpadlpar_core.c +++ b/drivers/pci/hotplug/rpadlpar_core.c @@ -55,6 +55,7 @@ static struct device_node *find_vio_slot_node(char *drc_name) if ((rc == 0) && (!strcmp(drc_name, name))) break; } + of_node_put(parent); return dn; } @@ -78,6 +79,7 @@ static struct device_node *find_php_slot_pci_node(char *drc_name, return np; } +/* Returns a device_node with its reference count incremented */ static struct device_node *find_dlpar_node(char *drc_name, int *node_type) { struct device_node *dn; @@ -314,6 +316,7 @@ int dlpar_add_slot(char *drc_name) rc = dlpar_add_phb(drc_name, dn); break; } + of_node_put(dn); printk(KERN_INFO "%s: slot %s added\n", DLPAR_MODULE_NAME, drc_name); exit: @@ -447,6 +450,7 @@ int dlpar_remove_slot(char *drc_name) rc = dlpar_remove_pci_slot(drc_name, dn); break; } + of_node_put(dn); vm_unmap_aliases(); printk(KERN_INFO "%s: slot %s removed\n", DLPAR_MODULE_NAME, drc_name); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index d85010ebac5a..36c6f3702167 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3141,6 +3141,7 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0030, quirk_no_bus_reset); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0032, quirk_no_bus_reset); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x003c, quirk_no_bus_reset); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0033, quirk_no_bus_reset); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0034, quirk_no_bus_reset); static void quirk_no_pm_reset(struct pci_dev *dev) { diff --git a/drivers/pinctrl/pinctrl-pistachio.c b/drivers/pinctrl/pinctrl-pistachio.c index 98a459b1c095..86e8d989092c 100644 --- a/drivers/pinctrl/pinctrl-pistachio.c +++ b/drivers/pinctrl/pinctrl-pistachio.c @@ -1373,6 +1373,7 @@ static int pistachio_gpio_register(struct pistachio_pinctrl *pctl) if (!of_find_property(child, "gpio-controller", NULL)) { dev_err(pctl->dev, "No gpio-controller property for bank %u\n", i); + of_node_put(child); ret = -ENODEV; goto err; } @@ -1380,6 +1381,7 @@ static int pistachio_gpio_register(struct pistachio_pinctrl *pctl) irq = irq_of_parse_and_map(child, 0); if (irq < 0) { dev_err(pctl->dev, "No IRQ for bank %u: %d\n", i, irq); + of_node_put(child); ret = irq; goto err; } diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c index a0b8c8a8c323..5c285f2b3a65 100644 --- a/drivers/platform/chrome/cros_ec_proto.c +++ b/drivers/platform/chrome/cros_ec_proto.c @@ -66,6 +66,17 @@ static int send_command(struct cros_ec_device *ec_dev, else xfer_fxn = ec_dev->cmd_xfer; + if (!xfer_fxn) { + /* + * This error can happen if a communication error happened and + * the EC is trying to use protocol v2, on an underlying + * communication mechanism that does not support v2. + */ + dev_err_once(ec_dev->dev, + "missing EC transfer API, cannot send command\n"); + return -EIO; + } + ret = (*xfer_fxn)(ec_dev, msg); if (msg->result == EC_RES_IN_PROGRESS) { int i; diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c index af8d9dcf8afc..007f92bcee13 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c @@ -1066,9 +1066,8 @@ static int __ipa_add_rt_rule(enum ipa_ip_type ip, const char *name, * tables */ if (!strncmp(tbl->name, IPA_DFLT_RT_TBL_NAME, IPA_RESOURCE_NAME_MAX) && - (tbl->rule_cnt > 0) && (at_rear != 0)) { - IPAERR("cannot add rule at end of tbl rule_cnt=%d at_rear=%d\n", - tbl->rule_cnt, at_rear); + (tbl->rule_cnt > 0)) { + IPAERR("cannot add rules to default rt table\n"); goto error; } @@ -1605,6 +1604,11 @@ static int __ipa_mdfy_rt_rule(struct ipa_rt_rule_mdfy *rtrule) goto error; } + if (!strcmp(entry->tbl->name, IPA_DFLT_RT_TBL_NAME)) { + IPAERR_RL("Default tbl rule cannot be modified\n"); + return -EINVAL; + } + /* Adding check to confirm still * header entry present in header table or not */ 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 6b90abf787b9..d8e128eee8cc 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2019, 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 @@ -779,7 +779,7 @@ int ipa3_qmi_ul_filter_request_send( { struct ipa_configure_ul_firewall_rules_resp_msg_v01 resp; struct msg_desc req_desc, resp_desc; - int rc; + int rc, i; IPAWANDBG("IPACM pass %u rules to Q6\n", req->firewall_rules_list_len); @@ -799,6 +799,38 @@ int ipa3_qmi_ul_filter_request_send( } mutex_unlock(&ipa3_qmi_lock); + /* check if modem is up */ + if (!ipa3_qmi_indication_fin || + !ipa3_qmi_modem_init_fin || + !ipa_q6_clnt) { + IPAWANDBG("modem QMI service is not up yet\n"); + return -EINVAL; + } + + /* Passing 0 rules means that firewall is disabled */ + if (req->firewall_rules_list_len == 0) + IPAWANDBG("IPACM passed 0 rules to Q6\n"); + + if (req->firewall_rules_list_len >= QMI_IPA_MAX_UL_FIREWALL_RULES_V01) { + IPAWANERR( + "Number of rules passed by IPACM, %d, exceed limit %d\n", + req->firewall_rules_list_len, + QMI_IPA_MAX_UL_FIREWALL_RULES_V01); + return -EINVAL; + } + + /* Check for valid IP type */ + for (i = 0; i < req->firewall_rules_list_len; i++) { + if (req->firewall_rules_list[i].ip_type != + QMI_IPA_IP_TYPE_V4_V01 && + req->firewall_rules_list[i].ip_type != + QMI_IPA_IP_TYPE_V6_V01) { + IPAWANERR("Invalid IP type %d\n", + req->firewall_rules_list[i].ip_type); + return -EINVAL; + } + } + req_desc.max_msg_len = QMI_IPA_INSTALL_UL_FIREWALL_RULES_REQ_MAX_MSG_LEN_V01; req_desc.msg_id = QMI_IPA_INSTALL_UL_FIREWALL_RULES_REQ_V01; diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index f73c29558cd3..c54ff94c491d 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -4394,14 +4394,16 @@ sony_pic_read_possible_resource(struct acpi_resource *resource, void *context) } return AE_OK; } + + case ACPI_RESOURCE_TYPE_END_TAG: + return AE_OK; + default: dprintk("Resource %d isn't an IRQ nor an IO port\n", resource->type); + return AE_CTRL_TERMINATE; - case ACPI_RESOURCE_TYPE_END_TAG: - return AE_OK; } - return AE_CTRL_TERMINATE; } static int sony_pic_possible_resources(struct acpi_device *device) diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 41dd7ddb5e40..36217ae57146 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -395,15 +395,11 @@ int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env) char *prop_buf; char *attrname; - dev_dbg(dev, "uevent\n"); - if (!psy || !psy->desc) { dev_dbg(dev, "No power supply yet\n"); return ret; } - dev_dbg(dev, "POWER_SUPPLY_NAME=%s\n", psy->desc->name); - ret = add_uevent_var(env, "POWER_SUPPLY_NAME=%s", psy->desc->name); if (ret) return ret; @@ -439,8 +435,6 @@ int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env) goto out; } - dev_dbg(dev, "prop %s=%s\n", attrname, prop_buf); - ret = add_uevent_var(env, "POWER_SUPPLY_%s=%s", attrname, prop_buf); kfree(attrname); if (ret) diff --git a/drivers/power/supply/qcom/qpnp-smbcharger.c b/drivers/power/supply/qcom/qpnp-smbcharger.c index 6abe0e021877..2ebefb072685 100644 --- a/drivers/power/supply/qcom/qpnp-smbcharger.c +++ b/drivers/power/supply/qcom/qpnp-smbcharger.c @@ -249,6 +249,7 @@ struct smbchg_chip { struct power_supply *dc_psy; struct power_supply *bms_psy; struct power_supply *typec_psy; + struct power_supply *dpdm_psy; int dc_psy_type; const char *bms_psy_name; const char *battery_psy_name; @@ -277,6 +278,7 @@ struct smbchg_chip { bool skip_usb_notification; u32 vchg_adc_channel; struct qpnp_vadc_chip *vchg_vadc_dev; + struct qpnp_vadc_chip *vusb_vadc_dev; /* voters */ struct votable *fcc_votable; @@ -4604,12 +4606,19 @@ static void smbchg_hvdcp_det_work(struct work_struct *work) smbchg_relax(chip, PM_DETECT_HVDCP); } -static int set_usb_psy_dp_dm(struct smbchg_chip *chip, int state) +static int set_dpdm_psy(struct smbchg_chip *chip, int state) { int rc; u8 reg; union power_supply_propval pval = {0, }; + if (!chip->dpdm_psy) + chip->dpdm_psy = power_supply_get_by_name("dpdm"); + + if (!chip->dpdm_psy) { + pr_err("dpdm_psy not found\n"); + return -EINVAL; + } /* * ensure that we are not in the middle of an insertion where usbin_uv * is low and src_detect hasnt gone high. If so force dp=F dm=F @@ -4625,9 +4634,9 @@ static int set_usb_psy_dp_dm(struct smbchg_chip *chip, int state) return rc; } } - pr_smb(PR_MISC, "setting usb psy dp dm = %d\n", state); + pr_smb(PR_MISC, "setting dpdm psy = %d\n", state); pval.intval = state; - return power_supply_set_property(chip->usb_psy, + return power_supply_set_property(chip->dpdm_psy, POWER_SUPPLY_PROP_DP_DM, &pval); } @@ -5239,7 +5248,7 @@ static int smbchg_prepare_for_pulsing(struct smbchg_chip *chip) smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG, AICL_EN_BIT, AICL_EN_BIT); - set_usb_psy_dp_dm(chip, POWER_SUPPLY_DP_DM_DP0P6_DMF); + set_dpdm_psy(chip, POWER_SUPPLY_DP_DM_DP0P6_DMF); /* * DCP will switch to HVDCP in this time by removing the short * between DP DM @@ -5262,7 +5271,7 @@ static int smbchg_prepare_for_pulsing(struct smbchg_chip *chip) goto out; } - set_usb_psy_dp_dm(chip, POWER_SUPPLY_DP_DM_DP0P6_DM3P3); + set_dpdm_psy(chip, POWER_SUPPLY_DP_DM_DP0P6_DM3P3); /* Wait 60mS after entering continuous mode */ msleep(60); @@ -5664,8 +5673,7 @@ static int smbchg_dp_dm(struct smbchg_chip *chip, int val) break; case POWER_SUPPLY_DP_DM_DP_PULSE: if (chip->schg_version == QPNP_SCHG) - rc = set_usb_psy_dp_dm(chip, - POWER_SUPPLY_DP_DM_DP_PULSE); + rc = set_dpdm_psy(chip, POWER_SUPPLY_DP_DM_DP_PULSE); else rc = smbchg_dp_pulse_lite(chip); if (!rc) @@ -5674,8 +5682,7 @@ static int smbchg_dp_dm(struct smbchg_chip *chip, int val) break; case POWER_SUPPLY_DP_DM_DM_PULSE: if (chip->schg_version == QPNP_SCHG) - rc = set_usb_psy_dp_dm(chip, - POWER_SUPPLY_DP_DM_DM_PULSE); + rc = set_dpdm_psy(chip, POWER_SUPPLY_DP_DM_DM_PULSE); else rc = smbchg_dm_pulse_lite(chip); if (!rc && chip->pulse_cnt) @@ -5744,9 +5751,43 @@ static void update_typec_otg_status(struct smbchg_chip *chip, int mode, } } +static int smbchg_get_vusb(struct smbchg_chip *chip) +{ + int rc; + struct qpnp_vadc_result adc_result; + + if (!is_usb_present(chip)) + return 0; + + if (chip->vusb_vadc_dev) { + rc = qpnp_vadc_read(chip->vusb_vadc_dev, + USBIN, &adc_result); + if (rc < 0) { + pr_smb(PR_STATUS, + "error in vusb_uv read rc = %d\n", rc); + return rc; + } + } else { + return -ENODATA; + } + pr_smb(PR_STATUS, "The value of vusb_uv %lld\n", adc_result.physical); + + return adc_result.physical; +} + static int smbchg_set_sdp_current(struct smbchg_chip *chip, int current_ma) { if (chip->usb_supply_type == POWER_SUPPLY_TYPE_USB) { + if (current_ma == -ETIMEDOUT) { + /* float charger */ + current_ma = CURRENT_1500_MA; + pr_smb(PR_MISC, + "Update usb_type to FLOAT current=%dmA\n", + current_ma); + chip->usb_psy_d.type = POWER_SUPPLY_TYPE_USB_FLOAT; + } else { + current_ma = current_ma / 1000; + } /* Override if type-c charger used */ if (chip->typec_current_ma > 500 && current_ma < chip->typec_current_ma) { @@ -5789,6 +5830,9 @@ static int smbchg_usb_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_HEALTH: val->intval = chip->usb_health; break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = smbchg_get_vusb(chip); + break; default: return -EINVAL; } @@ -5807,7 +5851,7 @@ static int smbchg_usb_set_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CURRENT_MAX: case POWER_SUPPLY_PROP_SDP_CURRENT_MAX: - smbchg_set_sdp_current(chip, val->intval / 1000); + smbchg_set_sdp_current(chip, val->intval); default: return -EINVAL; } @@ -5844,6 +5888,7 @@ static enum power_supply_property smbchg_usb_properties[] = { POWER_SUPPLY_PROP_REAL_TYPE, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_SDP_CURRENT_MAX, + POWER_SUPPLY_PROP_VOLTAGE_NOW, }; #define CHARGE_OUTPUT_VTG_RATIO 840 @@ -8191,6 +8236,7 @@ static int smbchg_probe(struct platform_device *pdev) struct smbchg_chip *chip; struct power_supply *typec_psy = NULL; struct qpnp_vadc_chip *vadc_dev = NULL, *vchg_vadc_dev = NULL; + struct qpnp_vadc_chip *vusb_vadc_dev = NULL; const char *typec_psy_name; struct power_supply_config usb_psy_cfg = {}; struct power_supply_config batt_psy_cfg = {}; @@ -8239,6 +8285,18 @@ static int smbchg_probe(struct platform_device *pdev) } } + vusb_vadc_dev = NULL; + if (of_find_property(pdev->dev.of_node, "qcom,usbin-vadc", NULL)) { + vusb_vadc_dev = qpnp_get_vadc(&pdev->dev, "usbin"); + if (IS_ERR(vusb_vadc_dev)) { + rc = PTR_ERR(vusb_vadc_dev); + if (rc != -EPROBE_DEFER) + dev_err(&pdev->dev, "Couldn't get vadc 'usbin' rc=%d\n", + rc); + return rc; + } + } + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); if (!chip) @@ -8344,6 +8402,7 @@ static int smbchg_probe(struct platform_device *pdev) init_completion(&chip->usbin_uv_raised); chip->vadc_dev = vadc_dev; chip->vchg_vadc_dev = vchg_vadc_dev; + chip->vusb_vadc_dev = vusb_vadc_dev; chip->pdev = pdev; chip->dev = &pdev->dev; diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index ec84ff8ad1b4..6911f9662300 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -284,10 +284,12 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip, if (IS_ENABLED(CONFIG_OF)) of_pwmchip_add(chip); - pwmchip_sysfs_export(chip); - out: mutex_unlock(&pwm_lock); + + if (!ret) + pwmchip_sysfs_export(chip); + return ret; } EXPORT_SYMBOL_GPL(pwmchip_add_with_polarity); @@ -321,7 +323,7 @@ int pwmchip_remove(struct pwm_chip *chip) unsigned int i; int ret = 0; - pwmchip_sysfs_unexport_children(chip); + pwmchip_sysfs_unexport(chip); mutex_lock(&pwm_lock); @@ -341,8 +343,6 @@ int pwmchip_remove(struct pwm_chip *chip) free_pwms(chip); - pwmchip_sysfs_unexport(chip); - out: mutex_unlock(&pwm_lock); return ret; diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c index 062dff1c902d..ede17f89d57f 100644 --- a/drivers/pwm/pwm-tiehrpwm.c +++ b/drivers/pwm/pwm-tiehrpwm.c @@ -385,6 +385,8 @@ static void ehrpwm_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) } /* Update shadow register first before modifying active register */ + ehrpwm_modify(pc->mmio_base, AQSFRC, AQSFRC_RLDCSF_MASK, + AQSFRC_RLDCSF_ZRO); ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val); /* * Changes to immediate action on Action Qualifier. This puts diff --git a/drivers/pwm/sysfs.c b/drivers/pwm/sysfs.c index 375008e2be20..199370e41da9 100644 --- a/drivers/pwm/sysfs.c +++ b/drivers/pwm/sysfs.c @@ -340,19 +340,6 @@ void pwmchip_sysfs_export(struct pwm_chip *chip) void pwmchip_sysfs_unexport(struct pwm_chip *chip) { struct device *parent; - - parent = class_find_device(&pwm_class, NULL, chip, - pwmchip_sysfs_match); - if (parent) { - /* for class_find_device() */ - put_device(parent); - device_unregister(parent); - } -} - -void pwmchip_sysfs_unexport_children(struct pwm_chip *chip) -{ - struct device *parent; unsigned int i; parent = class_find_device(&pwm_class, NULL, chip, @@ -368,6 +355,7 @@ void pwmchip_sysfs_unexport_children(struct pwm_chip *chip) } put_device(parent); + device_unregister(parent); } static int __init pwm_sysfs_init(void) diff --git a/drivers/rtc/rtc-88pm860x.c b/drivers/rtc/rtc-88pm860x.c index 19e53b3b8e00..166faae3a59c 100644 --- a/drivers/rtc/rtc-88pm860x.c +++ b/drivers/rtc/rtc-88pm860x.c @@ -414,7 +414,7 @@ static int pm860x_rtc_remove(struct platform_device *pdev) struct pm860x_rtc_info *info = platform_get_drvdata(pdev); #ifdef VRTC_CALIBRATION - flush_scheduled_work(); + cancel_delayed_work_sync(&info->calib_work); /* disable measurement */ pm860x_set_bits(info->i2c, PM8607_MEAS_EN2, MEAS2_VRTC, 0); #endif /* VRTC_CALIBRATION */ diff --git a/drivers/rtc/rtc-da9063.c b/drivers/rtc/rtc-da9063.c index d6c853bbfa9f..e93beecd5010 100644 --- a/drivers/rtc/rtc-da9063.c +++ b/drivers/rtc/rtc-da9063.c @@ -491,6 +491,13 @@ static int da9063_rtc_probe(struct platform_device *pdev) da9063_data_to_tm(data, &rtc->alarm_time, rtc); rtc->rtc_sync = false; + /* + * TODO: some models have alarms on a minute boundary but still support + * real hardware interrupts. Add this once the core supports it. + */ + if (config->rtc_data_start != RTC_SEC) + rtc->rtc_dev->uie_unsupported = 1; + irq_alarm = platform_get_irq_byname(pdev, "ALARM"); ret = devm_request_threaded_irq(&pdev->dev, irq_alarm, NULL, da9063_alarm_event, diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c index 2b81dd4baf17..104c854d6a8a 100644 --- a/drivers/rtc/rtc-sh.c +++ b/drivers/rtc/rtc-sh.c @@ -455,7 +455,7 @@ static int sh_rtc_set_time(struct device *dev, struct rtc_time *tm) static inline int sh_rtc_read_alarm_value(struct sh_rtc *rtc, int reg_off) { unsigned int byte; - int value = 0xff; /* return 0xff for ignored values */ + int value = -1; /* return -1 for ignored values */ byte = readb(rtc->regbase + reg_off); if (byte & AR_ENB) { diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 80a43074c2f9..c530610f61ac 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -2066,14 +2066,14 @@ static int dasd_eckd_end_analysis(struct dasd_block *block) blk_per_trk = recs_per_track(&private->rdc_data, 0, block->bp_block); raw: - block->blocks = (private->real_cyl * + block->blocks = ((unsigned long) private->real_cyl * private->rdc_data.trk_per_cyl * blk_per_trk); dev_info(&device->cdev->dev, - "DASD with %d KB/block, %d KB total size, %d KB/track, " + "DASD with %u KB/block, %lu KB total size, %u KB/track, " "%s\n", (block->bp_block >> 10), - ((private->real_cyl * + (((unsigned long) private->real_cyl * private->rdc_data.trk_per_cyl * blk_per_trk * (block->bp_block >> 9)) >> 1), ((blk_per_trk * block->bp_block) >> 10), diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index bae98521c808..3e5a7912044f 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -627,7 +627,7 @@ con3270_init(void) (void (*)(unsigned long)) con3270_read_tasklet, (unsigned long) condev->read); - raw3270_add_view(&condev->view, &con3270_fn, 1); + raw3270_add_view(&condev->view, &con3270_fn, 1, RAW3270_VIEW_LOCK_IRQ); INIT_LIST_HEAD(&condev->freemem); for (i = 0; i < CON3270_STRING_PAGES; i++) { diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c index 71e974738014..f0c86bcbe316 100644 --- a/drivers/s390/char/fs3270.c +++ b/drivers/s390/char/fs3270.c @@ -463,7 +463,8 @@ fs3270_open(struct inode *inode, struct file *filp) init_waitqueue_head(&fp->wait); fp->fs_pid = get_pid(task_pid(current)); - rc = raw3270_add_view(&fp->view, &fs3270_fn, minor); + rc = raw3270_add_view(&fp->view, &fs3270_fn, minor, + RAW3270_VIEW_LOCK_BH); if (rc) { fs3270_free_view(&fp->view); goto out; diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 220acb4cbee5..9c350e6d75bf 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -956,7 +956,7 @@ raw3270_deactivate_view(struct raw3270_view *view) * Add view to device with minor "minor". */ int -raw3270_add_view(struct raw3270_view *view, struct raw3270_fn *fn, int minor) +raw3270_add_view(struct raw3270_view *view, struct raw3270_fn *fn, int minor, int subclass) { unsigned long flags; struct raw3270 *rp; @@ -978,6 +978,7 @@ raw3270_add_view(struct raw3270_view *view, struct raw3270_fn *fn, int minor) view->cols = rp->cols; view->ascebc = rp->ascebc; spin_lock_init(&view->lock); + lockdep_set_subclass(&view->lock, subclass); list_add(&view->list, &rp->view_list); rc = 0; spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); diff --git a/drivers/s390/char/raw3270.h b/drivers/s390/char/raw3270.h index e1e41c2861fb..5ae54317857a 100644 --- a/drivers/s390/char/raw3270.h +++ b/drivers/s390/char/raw3270.h @@ -155,6 +155,8 @@ struct raw3270_fn { struct raw3270_view { struct list_head list; spinlock_t lock; +#define RAW3270_VIEW_LOCK_IRQ 0 +#define RAW3270_VIEW_LOCK_BH 1 atomic_t ref_count; struct raw3270 *dev; struct raw3270_fn *fn; @@ -163,7 +165,7 @@ struct raw3270_view { unsigned char *ascebc; /* ascii -> ebcdic table */ }; -int raw3270_add_view(struct raw3270_view *, struct raw3270_fn *, int); +int raw3270_add_view(struct raw3270_view *, struct raw3270_fn *, int, int); int raw3270_activate_view(struct raw3270_view *); void raw3270_del_view(struct raw3270_view *); void raw3270_deactivate_view(struct raw3270_view *); diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c index e96fc7fd9498..ab95d24b991b 100644 --- a/drivers/s390/char/tty3270.c +++ b/drivers/s390/char/tty3270.c @@ -937,7 +937,8 @@ static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty) return PTR_ERR(tp); rc = raw3270_add_view(&tp->view, &tty3270_fn, - tty->index + RAW3270_FIRSTMINOR); + tty->index + RAW3270_FIRSTMINOR, + RAW3270_VIEW_LOCK_BH); if (rc) { tty3270_free_view(tp); return rc; diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h index a01376ae1749..fdb87520543f 100644 --- a/drivers/s390/cio/cio.h +++ b/drivers/s390/cio/cio.h @@ -102,7 +102,7 @@ struct subchannel { struct schib_config config; } __attribute__ ((aligned(8))); -DECLARE_PER_CPU(struct irb, cio_irb); +DECLARE_PER_CPU_ALIGNED(struct irb, cio_irb); #define to_subchannel(n) container_of(n, struct subchannel, dev) diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c index 05c37d6d4afe..a31821d94677 100644 --- a/drivers/s390/net/ctcm_main.c +++ b/drivers/s390/net/ctcm_main.c @@ -1595,6 +1595,7 @@ static int ctcm_new_device(struct ccwgroup_device *cgdev) if (priv->channel[direction] == NULL) { if (direction == CTCM_WRITE) channel_free(priv->channel[CTCM_READ]); + result = -ENODEV; goto out_dev; } priv->channel[direction]->netdev = dev; diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index a39a74500e23..aeb93478482f 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -161,6 +161,7 @@ extern const struct attribute_group *zfcp_port_attr_groups[]; extern struct mutex zfcp_sysfs_port_units_mutex; extern struct device_attribute *zfcp_sysfs_sdev_attrs[]; extern struct device_attribute *zfcp_sysfs_shost_attrs[]; +bool zfcp_sysfs_port_is_removing(const struct zfcp_port *const port); /* zfcp_unit.c */ extern int zfcp_unit_add(struct zfcp_port *, u64); diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c index 237688af179b..f7630cf581cd 100644 --- a/drivers/s390/scsi/zfcp_fc.c +++ b/drivers/s390/scsi/zfcp_fc.c @@ -238,10 +238,6 @@ static void _zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req, u32 range, list_for_each_entry(port, &adapter->port_list, list) { if ((port->d_id & range) == (ntoh24(page->rscn_fid) & range)) zfcp_fc_test_link(port); - if (!port->d_id) - zfcp_erp_port_reopen(port, - ZFCP_STATUS_COMMON_ERP_FAILED, - "fcrscn1"); } read_unlock_irqrestore(&adapter->port_list_lock, flags); } @@ -249,6 +245,7 @@ static void _zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req, u32 range, static void zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req) { struct fsf_status_read_buffer *status_buffer = (void *)fsf_req->data; + struct zfcp_adapter *adapter = fsf_req->adapter; struct fc_els_rscn *head; struct fc_els_rscn_page *page; u16 i; @@ -261,6 +258,22 @@ static void zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req) /* see FC-FS */ no_entries = head->rscn_plen / sizeof(struct fc_els_rscn_page); + if (no_entries > 1) { + /* handle failed ports */ + unsigned long flags; + struct zfcp_port *port; + + read_lock_irqsave(&adapter->port_list_lock, flags); + list_for_each_entry(port, &adapter->port_list, list) { + if (port->d_id) + continue; + zfcp_erp_port_reopen(port, + ZFCP_STATUS_COMMON_ERP_FAILED, + "fcrscn1"); + } + read_unlock_irqrestore(&adapter->port_list_lock, flags); + } + for (i = 1; i < no_entries; i++) { /* skip head and start with 1st element */ page++; diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index bdb257eaa2e5..68146b398603 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -124,6 +124,15 @@ static int zfcp_scsi_slave_alloc(struct scsi_device *sdev) zfcp_sdev->erp_action.port = port; + mutex_lock(&zfcp_sysfs_port_units_mutex); + if (zfcp_sysfs_port_is_removing(port)) { + /* port is already gone */ + mutex_unlock(&zfcp_sysfs_port_units_mutex); + put_device(&port->dev); /* undo zfcp_get_port_by_wwpn() */ + return -ENXIO; + } + mutex_unlock(&zfcp_sysfs_port_units_mutex); + unit = zfcp_unit_find(port, zfcp_scsi_dev_lun(sdev)); if (unit) put_device(&unit->dev); diff --git a/drivers/s390/scsi/zfcp_sysfs.c b/drivers/s390/scsi/zfcp_sysfs.c index 96a0be13e841..5df597d1b978 100644 --- a/drivers/s390/scsi/zfcp_sysfs.c +++ b/drivers/s390/scsi/zfcp_sysfs.c @@ -237,6 +237,53 @@ static ZFCP_DEV_ATTR(adapter, port_rescan, S_IWUSR, NULL, DEFINE_MUTEX(zfcp_sysfs_port_units_mutex); +static void zfcp_sysfs_port_set_removing(struct zfcp_port *const port) +{ + lockdep_assert_held(&zfcp_sysfs_port_units_mutex); + atomic_set(&port->units, -1); +} + +bool zfcp_sysfs_port_is_removing(const struct zfcp_port *const port) +{ + lockdep_assert_held(&zfcp_sysfs_port_units_mutex); + return atomic_read(&port->units) == -1; +} + +static bool zfcp_sysfs_port_in_use(struct zfcp_port *const port) +{ + struct zfcp_adapter *const adapter = port->adapter; + unsigned long flags; + struct scsi_device *sdev; + bool in_use = true; + + mutex_lock(&zfcp_sysfs_port_units_mutex); + if (atomic_read(&port->units) > 0) + goto unlock_port_units_mutex; /* zfcp_unit(s) under port */ + + spin_lock_irqsave(adapter->scsi_host->host_lock, flags); + __shost_for_each_device(sdev, adapter->scsi_host) { + const struct zfcp_scsi_dev *zsdev = sdev_to_zfcp(sdev); + + if (sdev->sdev_state == SDEV_DEL || + sdev->sdev_state == SDEV_CANCEL) + continue; + if (zsdev->port != port) + continue; + /* alive scsi_device under port of interest */ + goto unlock_host_lock; + } + + /* port is about to be removed, so no more unit_add or slave_alloc */ + zfcp_sysfs_port_set_removing(port); + in_use = false; + +unlock_host_lock: + spin_unlock_irqrestore(adapter->scsi_host->host_lock, flags); +unlock_port_units_mutex: + mutex_unlock(&zfcp_sysfs_port_units_mutex); + return in_use; +} + static ssize_t zfcp_sysfs_port_remove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -259,15 +306,11 @@ static ssize_t zfcp_sysfs_port_remove_store(struct device *dev, else retval = 0; - mutex_lock(&zfcp_sysfs_port_units_mutex); - if (atomic_read(&port->units) > 0) { + if (zfcp_sysfs_port_in_use(port)) { retval = -EBUSY; - mutex_unlock(&zfcp_sysfs_port_units_mutex); + put_device(&port->dev); /* undo zfcp_get_port_by_wwpn() */ goto out; } - /* port is about to be removed, so no more unit_add */ - atomic_set(&port->units, -1); - mutex_unlock(&zfcp_sysfs_port_units_mutex); write_lock_irq(&adapter->port_list_lock); list_del(&port->list); diff --git a/drivers/s390/scsi/zfcp_unit.c b/drivers/s390/scsi/zfcp_unit.c index 157d3d203ba1..f00693698abc 100644 --- a/drivers/s390/scsi/zfcp_unit.c +++ b/drivers/s390/scsi/zfcp_unit.c @@ -122,7 +122,7 @@ int zfcp_unit_add(struct zfcp_port *port, u64 fcp_lun) int retval = 0; mutex_lock(&zfcp_sysfs_port_units_mutex); - if (atomic_read(&port->units) == -1) { + if (zfcp_sysfs_port_is_removing(port)) { /* port is already gone */ retval = -ENODEV; goto out; @@ -166,8 +166,14 @@ int zfcp_unit_add(struct zfcp_port *port, u64 fcp_lun) write_lock_irq(&port->unit_list_lock); list_add_tail(&unit->list, &port->unit_list); write_unlock_irq(&port->unit_list_lock); + /* + * lock order: shost->scan_mutex before zfcp_sysfs_port_units_mutex + * due to zfcp_unit_scsi_scan() => zfcp_scsi_slave_alloc() + */ + mutex_unlock(&zfcp_sysfs_port_units_mutex); zfcp_unit_scsi_scan(unit); + return retval; out: mutex_unlock(&zfcp_sysfs_port_units_mutex); diff --git a/drivers/scsi/bnx2fc/bnx2fc_hwi.c b/drivers/scsi/bnx2fc/bnx2fc_hwi.c index 28c671b609b2..0c71b69b9f88 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_hwi.c +++ b/drivers/scsi/bnx2fc/bnx2fc_hwi.c @@ -829,7 +829,7 @@ ret_err_rqe: ((u64)err_entry->data.err_warn_bitmap_hi << 32) | (u64)err_entry->data.err_warn_bitmap_lo; for (i = 0; i < BNX2FC_NUM_ERR_BITS; i++) { - if (err_warn_bit_map & (u64) (1 << i)) { + if (err_warn_bit_map & ((u64)1 << i)) { err_warn = i; break; } diff --git a/drivers/scsi/csiostor/csio_scsi.c b/drivers/scsi/csiostor/csio_scsi.c index c2a6f9f29427..ddbdaade654d 100644 --- a/drivers/scsi/csiostor/csio_scsi.c +++ b/drivers/scsi/csiostor/csio_scsi.c @@ -1713,8 +1713,11 @@ csio_scsi_err_handler(struct csio_hw *hw, struct csio_ioreq *req) } out: - if (req->nsge > 0) + if (req->nsge > 0) { scsi_dma_unmap(cmnd); + if (req->dcopy && (host_status == DID_OK)) + host_status = csio_scsi_copy_to_sgl(hw, req); + } cmnd->result = (((host_status) << 16) | scsi_status); cmnd->scsi_done(cmnd); diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c index f3bb7af4e984..5eaf14c15590 100644 --- a/drivers/scsi/cxgbi/libcxgbi.c +++ b/drivers/scsi/cxgbi/libcxgbi.c @@ -634,6 +634,10 @@ static struct cxgbi_sock *cxgbi_check_route(struct sockaddr *dst_addr) if (ndev->flags & IFF_LOOPBACK) { ndev = ip_dev_find(&init_net, daddr->sin_addr.s_addr); + if (!ndev) { + err = -ENETUNREACH; + goto rel_neigh; + } mtu = ndev->mtu; pr_info("rt dev %s, loopback -> %s, mtu %u.\n", n->dev->name, ndev->name, mtu); diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 7be581f7c35d..400eee9d7783 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -47,17 +47,16 @@ static void smp_task_timedout(unsigned long _task) unsigned long flags; spin_lock_irqsave(&task->task_state_lock, flags); - if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) + if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { task->task_state_flags |= SAS_TASK_STATE_ABORTED; + complete(&task->slow_task->completion); + } spin_unlock_irqrestore(&task->task_state_lock, flags); - - complete(&task->slow_task->completion); } static void smp_task_done(struct sas_task *task) { - if (!del_timer(&task->slow_task->timer)) - return; + del_timer(&task->slow_task->timer); complete(&task->slow_task->completion); } @@ -979,6 +978,8 @@ static struct domain_device *sas_ex_discover_expander( list_del(&child->dev_list_node); spin_unlock_irq(&parent->port->dev_list_lock); sas_put_device(child); + sas_port_delete(phy->port); + phy->port = NULL; return NULL; } list_add_tail(&child->siblings, &parent->ex_dev.children); @@ -2028,6 +2029,11 @@ static int sas_rediscover_dev(struct domain_device *dev, int phy_id, bool last) if ((SAS_ADDR(sas_addr) == 0) || (res == -ECOMM)) { phy->phy_state = PHY_EMPTY; sas_unregister_devs_sas_addr(dev, phy_id, last); + /* + * Even though the PHY is empty, for convenience we discover + * the PHY to update the PHY info, like negotiated linkrate. + */ + sas_ex_phy_discover(dev, phy_id); return res; } else if (SAS_ADDR(sas_addr) == SAS_ADDR(phy->attached_sas_addr) && dev_type_flutter(type, phy->attached_dev_type)) { diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index 398c9a0a5ade..82a690924f5e 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -6498,7 +6498,10 @@ int lpfc_send_rrq(struct lpfc_hba *phba, struct lpfc_node_rrq *rrq) { struct lpfc_nodelist *ndlp = lpfc_findnode_did(rrq->vport, - rrq->nlp_DID); + rrq->nlp_DID); + if (!ndlp) + return 1; + if (lpfc_test_rrq_active(phba, ndlp, rrq->xritag)) return lpfc_issue_els_rrq(rrq->vport, ndlp, rrq->nlp_DID, rrq); diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index 4131addfb872..a67950908db1 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -902,7 +902,11 @@ lpfc_linkdown(struct lpfc_hba *phba) lpfc_linkdown_port(vports[i]); } lpfc_destroy_vport_work_array(phba, vports); - /* Clean up any firmware default rpi's */ + + /* Clean up any SLI3 firmware default rpi's */ + if (phba->sli_rev > LPFC_SLI_REV3) + goto skip_unreg_did; + mb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (mb) { lpfc_unreg_did(phba, 0xffff, LPFC_UNREG_ALL_DFLT_RPIS, mb); @@ -914,6 +918,7 @@ lpfc_linkdown(struct lpfc_hba *phba) } } + skip_unreg_did: /* Setup myDID for link up if we are in pt2pt mode */ if (phba->pport->fc_flag & FC_PT2PT) { phba->pport->fc_myDID = 0; @@ -4647,6 +4652,10 @@ lpfc_unreg_default_rpis(struct lpfc_vport *vport) LPFC_MBOXQ_t *mbox; int rc; + /* Unreg DID is an SLI3 operation. */ + if (phba->sli_rev > LPFC_SLI_REV3) + return; + mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (mbox) { lpfc_unreg_did(phba, vport->vpi, LPFC_UNREG_ALL_DFLT_RPIS, diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index ac12ee844bfc..31c29a5d1f38 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -431,7 +431,7 @@ qla2x00_sysfs_write_optrom_ctl(struct file *filp, struct kobject *kobj, } ha->optrom_region_start = start; - ha->optrom_region_size = start + size; + ha->optrom_region_size = size; ha->optrom_state = QLA_SREADING; ha->optrom_buffer = vmalloc(ha->optrom_region_size); @@ -504,7 +504,7 @@ qla2x00_sysfs_write_optrom_ctl(struct file *filp, struct kobject *kobj, } ha->optrom_region_start = start; - ha->optrom_region_size = start + size; + ha->optrom_region_size = size; ha->optrom_state = QLA_SWRITING; ha->optrom_buffer = vmalloc(ha->optrom_region_size); diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index f9f899ec9427..d220b4f691c7 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -3207,6 +3207,8 @@ static int qla4xxx_conn_bind(struct iscsi_cls_session *cls_session, if (iscsi_conn_bind(cls_session, cls_conn, is_leading)) return -EINVAL; ep = iscsi_lookup_endpoint(transport_fd); + if (!ep) + return -EINVAL; conn = cls_conn->dd_data; qla_conn = conn->dd_data; qla_conn->qla_ep = ep->dd_data; @@ -5937,7 +5939,7 @@ static int get_fw_boot_info(struct scsi_qla_host *ha, uint16_t ddb_index[]) val = rd_nvram_byte(ha, sec_addr); if (val & BIT_7) ddb_index[1] = (val & 0x7f); - + goto exit_boot_info; } else if (is_qla80XX(ha)) { buf = dma_alloc_coherent(&ha->pdev->dev, size, &buf_dma, GFP_KERNEL); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 605092092049..bfd7e3840874 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -2415,7 +2415,6 @@ sd_read_write_protect_flag(struct scsi_disk *sdkp, unsigned char *buffer) int res; struct scsi_device *sdp = sdkp->device; struct scsi_mode_data data; - int disk_ro = get_disk_ro(sdkp->disk); set_disk_ro(sdkp->disk, 0); if (sdp->skip_ms_page_3f) { @@ -2455,7 +2454,7 @@ sd_read_write_protect_flag(struct scsi_disk *sdkp, unsigned char *buffer) "Test WP failed, assume Write Enabled\n"); } else { sdkp->write_prot = ((data.device_specific & 0x80) != 0); - set_disk_ro(sdkp->disk, sdkp->write_prot || disk_ro); + set_disk_ro(sdkp->disk, sdkp->write_prot); } } diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index 44b7a69d022a..45cd4cf93af3 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -613,13 +613,22 @@ static void handle_sc_creation(struct vmbus_channel *new_sc) static void handle_multichannel_storage(struct hv_device *device, int max_chns) { struct storvsc_device *stor_device; - int num_cpus = num_online_cpus(); int num_sc; struct storvsc_cmd_request *request; struct vstor_packet *vstor_packet; int ret, t; - num_sc = ((max_chns > num_cpus) ? num_cpus : max_chns); + /* + * If the number of CPUs is artificially restricted, such as + * with maxcpus=1 on the kernel boot line, Hyper-V could offer + * sub-channels >= the number of CPUs. These sub-channels + * should not be created. The primary channel is already created + * and assigned to one CPU, so check against # CPUs - 1. + */ + num_sc = min((int)(num_online_cpus() - 1), max_chns); + if (!num_sc) + return; + stor_device = get_out_stor_device(device); if (!stor_device) return; diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 86a5bae0513b..2c1ee272f4f7 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -7162,19 +7162,19 @@ static u32 ufshcd_find_max_sup_active_icc_level(struct ufs_hba *hba, goto out; } - if (hba->vreg_info.vcc) + if (hba->vreg_info.vcc && hba->vreg_info.vcc->max_uA) icc_level = ufshcd_get_max_icc_level( hba->vreg_info.vcc->max_uA, POWER_DESC_MAX_ACTV_ICC_LVLS - 1, &desc_buf[PWR_DESC_ACTIVE_LVLS_VCC_0]); - if (hba->vreg_info.vccq) + if (hba->vreg_info.vccq && hba->vreg_info.vccq->max_uA) icc_level = ufshcd_get_max_icc_level( hba->vreg_info.vccq->max_uA, icc_level, &desc_buf[PWR_DESC_ACTIVE_LVLS_VCCQ_0]); - if (hba->vreg_info.vccq2) + if (hba->vreg_info.vccq2 && hba->vreg_info.vccq2->max_uA) icc_level = ufshcd_get_max_icc_level( hba->vreg_info.vccq2->max_uA, icc_level, @@ -7950,6 +7950,15 @@ static int ufshcd_config_vreg_load(struct device *dev, struct ufs_vreg *vreg, if (!vreg) return 0; + /* + * "set_load" operation shall be required on those regulators + * which specifically configured current limitation. Otherwise + * zero max_uA may cause unexpected behavior when regulator is + * enabled or set as high power mode. + */ + if (!vreg->max_uA) + return 0; + ret = regulator_set_load(vreg->reg, ua); if (ret < 0) { dev_err(dev, "%s: %s set load (ua=%d) failed, err=%d\n", @@ -8001,12 +8010,15 @@ static int ufshcd_config_vreg(struct device *dev, if (ret) goto out; - min_uV = on ? vreg->min_uV : 0; - ret = regulator_set_voltage(reg, min_uV, vreg->max_uV); - if (ret) { - dev_err(dev, "%s: %s set voltage failed, err=%d\n", + if (vreg->min_uV && vreg->max_uV) { + min_uV = on ? vreg->min_uV : 0; + ret = regulator_set_voltage(reg, min_uV, vreg->max_uV); + if (ret) { + dev_err(dev, + "%s: %s set voltage failed, err=%d\n", __func__, name, ret); - goto out; + goto out; + } } } out: diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c index 105597a885cb..33b10dd7d87e 100644 --- a/drivers/soc/mediatek/mtk-pmic-wrap.c +++ b/drivers/soc/mediatek/mtk-pmic-wrap.c @@ -591,7 +591,7 @@ static bool pwrap_is_pmic_cipher_ready(struct pmic_wrapper *wrp) static int pwrap_init_cipher(struct pmic_wrapper *wrp) { int ret; - u32 rdata; + u32 rdata = 0; pwrap_writel(wrp, 0x1, PWRAP_CIPHER_SWRST); pwrap_writel(wrp, 0x0, PWRAP_CIPHER_SWRST); diff --git a/drivers/soc/qcom/glink_spi_xprt.c b/drivers/soc/qcom/glink_spi_xprt.c index ade731cf3251..818051e6e9cb 100644 --- a/drivers/soc/qcom/glink_spi_xprt.c +++ b/drivers/soc/qcom/glink_spi_xprt.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2019, 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 @@ -641,10 +641,13 @@ static int glink_spi_xprt_tx_cmd(struct edge_info *einfo, void *src, * is read. * @frag_size: Size of the data fragment to read. * @size_remaining: Size of data left to be read in this packet. + * @rx_size: Max size of data that can be read in this packet + * from source. */ static void process_rx_data(struct edge_info *einfo, uint16_t cmd_id, uint32_t rcid, uint32_t intent_id, void *src, - uint32_t frag_size, uint32_t size_remaining) + uint32_t frag_size, uint32_t size_remaining, + int rx_size) { struct glink_core_rx_intent *intent; int rc = 0; @@ -667,8 +670,14 @@ static void process_rx_data(struct edge_info *einfo, uint16_t cmd_id, return; } - if (cmd_id == TX_SHORT_DATA_CMD) + if (cmd_id == TX_SHORT_DATA_CMD) { + if (frag_size > rx_size) { + GLINK_ERR("%s: frag size:%d greater than rx size %d\n", + __func__, frag_size, rx_size); + return; + } memcpy(intent->data + intent->write_offset, src, frag_size); + } else rc = glink_spi_xprt_rx_data(einfo, src, intent->data + intent->write_offset, frag_size); @@ -838,7 +847,8 @@ static void process_rx_cmd(struct edge_info *einfo, process_rx_data(einfo, cmd->id, cmd->param1, cmd->param2, (void *)(uintptr_t)(rx_descp->addr), - rx_descp->size, rx_descp->size_left); + rx_descp->size, rx_descp->size_left, + rx_size); break; case TX_SHORT_DATA_CMD: @@ -849,7 +859,7 @@ static void process_rx_cmd(struct edge_info *einfo, offset += sizeof(*rx_sd_descp); process_rx_data(einfo, cmd->id, cmd->param1, cmd->param2, (void *)rx_sd_descp->data, - cmd->param3, cmd->param4); + cmd->param3, cmd->param4, rx_size); break; case READ_NOTIF_CMD: diff --git a/drivers/soc/qcom/hab/ghs_comm.c b/drivers/soc/qcom/hab/ghs_comm.c index 1fdb1668295c..fbd579793879 100644 --- a/drivers/soc/qcom/hab/ghs_comm.c +++ b/drivers/soc/qcom/hab/ghs_comm.c @@ -20,19 +20,28 @@ int physical_channel_read(struct physical_channel *pchan, { struct ghs_vdev *dev = (struct ghs_vdev *)pchan->hyp_data; + if (!payload || !dev->read_data) { + pr_err("invalid parameters %pK %pK offset %d read %zd\n", + payload, dev->read_data, dev->read_offset, read_size); + return 0; + } + /* size in header is only for payload excluding the header itself */ - if (dev->read_size < read_size + sizeof(struct hab_header)) { - pr_warn("read %zd is less than requested %zd plus header %zd\n", - dev->read_size, read_size, sizeof(struct hab_header)); - read_size = dev->read_size; + if (dev->read_size < read_size + sizeof(struct hab_header) + + dev->read_offset) { + pr_warn("read %zd is less than requested %zd header %zd offset %d\n", + dev->read_size, read_size, + sizeof(struct hab_header), dev->read_offset); + read_size = dev->read_size - dev->read_offset - + sizeof(struct hab_header); } /* always skip the header */ memcpy(payload, (unsigned char *)dev->read_data + sizeof(struct hab_header) + dev->read_offset, read_size); - dev->read_offset += read_size; + dev->read_offset += (int)read_size; - return read_size; + return (int)read_size; } int physical_channel_send(struct physical_channel *pchan, @@ -41,8 +50,8 @@ int physical_channel_send(struct physical_channel *pchan, { size_t sizebytes = HAB_HEADER_GET_SIZE(*header); struct ghs_vdev *dev = (struct ghs_vdev *)pchan->hyp_data; - GIPC_Result result; - uint8_t *msg; + GIPC_Result result = GIPC_Success; + uint8_t *msg = NULL; spin_lock_bh(&dev->io_lock); @@ -61,7 +70,7 @@ int physical_channel_send(struct physical_channel *pchan, } if (HAB_HEADER_GET_TYPE(*header) == HAB_PAYLOAD_TYPE_PROFILE) { - struct timeval tv; + struct timeval tv = {0}; struct habmm_xing_vm_stat *pstat = (struct habmm_xing_vm_stat *)payload; @@ -90,11 +99,11 @@ int physical_channel_send(struct physical_channel *pchan, void physical_channel_rx_dispatch(unsigned long physical_channel) { - struct hab_header header; + struct hab_header header = {0}; struct physical_channel *pchan = (struct physical_channel *)physical_channel; struct ghs_vdev *dev = (struct ghs_vdev *)pchan->hyp_data; - GIPC_Result result; + GIPC_Result result = GIPC_Success; uint32_t events; unsigned long flags; @@ -119,7 +128,7 @@ void physical_channel_rx_dispatch(unsigned long physical_channel) dev->read_data, GIPC_RECV_BUFF_SIZE_BYTES, &dev->read_size, - &header.id_type_size); + (uint32_t *)&header.id_type_size); if (result == GIPC_Success || dev->read_size > 0) { /* handle corrupted msg? */ diff --git a/drivers/soc/qcom/hab/hab.c b/drivers/soc/qcom/hab/hab.c index b6dfe2d9ae22..f4fef27aeee1 100644 --- a/drivers/soc/qcom/hab/hab.c +++ b/drivers/soc/qcom/hab/hab.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2019, 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 @@ -21,7 +21,7 @@ .openlock = __SPIN_LOCK_UNLOCKED(&hab_devices[__num__].openlock)\ } -static const char hab_info_str[] = "Change: 17280941 Revision: #81"; +static const char hab_info_str[] = "Change: 19231400 Revision: #95"; /* * The following has to match habmm definitions, order does not matter if @@ -60,7 +60,7 @@ struct hab_driver hab_driver = { struct uhab_context *hab_ctx_alloc(int kernel) { - struct uhab_context *ctx; + struct uhab_context *ctx = NULL; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -79,6 +79,9 @@ struct uhab_context *hab_ctx_alloc(int kernel) rwlock_init(&ctx->exp_lock); rwlock_init(&ctx->ctx_lock); + INIT_LIST_HEAD(&ctx->forbidden_chans); + spin_lock_init(&ctx->forbidden_lock); + INIT_LIST_HEAD(&ctx->pending_open); kref_init(&ctx->refcount); ctx->import_ctx = habmem_imp_hyp_open(); @@ -106,18 +109,29 @@ void hab_ctx_free(struct kref *ref) { struct uhab_context *ctx = container_of(ref, struct uhab_context, refcount); - struct hab_export_ack_recvd *ack_recvd, *tmp; - struct virtual_channel *vchan; - struct physical_channel *pchan; - int i; - struct uhab_context *ctxdel, *ctxtmp; - struct hab_open_node *node; - struct export_desc *exp, *exp_tmp; + struct hab_export_ack_recvd *ack_recvd = NULL, *tmp = NULL; + struct virtual_channel *vchan = NULL; + struct physical_channel *pchan = NULL; + int i = 0; + struct uhab_context *ctxdel = NULL, *ctxtmp = NULL; + struct hab_open_node *node = NULL; + struct export_desc *exp = NULL, *exp_tmp = NULL; + struct hab_forbidden_node *forbidden = NULL, *forbidden_tmp = NULL; + + spin_lock_bh(&ctx->forbidden_lock); + list_for_each_entry_safe(forbidden, forbidden_tmp, + &ctx->forbidden_chans, node) { + list_del(&forbidden->node); + pr_debug("Remove mmid 0x%x from forbidden list, ctx %p\n", + forbidden->mmid, ctx); + kfree(forbidden); + } + spin_unlock_bh(&ctx->forbidden_lock); /* garbage-collect exp/imp buffers */ write_lock_bh(&ctx->exp_lock); list_for_each_entry_safe(exp, exp_tmp, &ctx->exp_whse, node) { - list_del(&exp->node); + list_del((struct list_head *)&exp->node); pr_debug("potential leak exp %d vcid %X recovered\n", exp->export_id, exp->vcid_local); habmem_hyp_revoke(exp->payload, exp->payload_count); @@ -127,7 +141,7 @@ void hab_ctx_free(struct kref *ref) spin_lock_bh(&ctx->imp_lock); list_for_each_entry_safe(exp, exp_tmp, &ctx->imp_whse, node) { - list_del(&exp->node); + list_del((struct list_head *)&exp->node); ctx->import_total--; pr_debug("leaked imp %d vcid %X for ctx is collected total %d\n", exp->export_id, exp->vcid_local, @@ -211,7 +225,7 @@ void hab_ctx_free(struct kref *ref) struct virtual_channel *hab_get_vchan_fromvcid(int32_t vcid, struct uhab_context *ctx, int ignore_remote) { - struct virtual_channel *vchan; + struct virtual_channel *vchan = NULL; read_lock(&ctx->ctx_lock); list_for_each_entry(vchan, &ctx->vchannels, node) { @@ -236,7 +250,7 @@ struct virtual_channel *hab_get_vchan_fromvcid(int32_t vcid, struct hab_device *find_hab_device(unsigned int mm_id) { - int i; + int i = 0; for (i = 0; i < hab_driver.ndevices; i++) { if (hab_driver.devp[i].id == HAB_MMID_GET_MAJOR(mm_id)) @@ -259,13 +273,13 @@ struct virtual_channel *frontend_open(struct uhab_context *ctx, unsigned int mm_id, int dom_id) { - int ret, ret2, open_id = 0; + int ret = 0, ret2 = 0, open_id = 0; struct physical_channel *pchan = NULL; - struct hab_device *dev; + struct hab_device *dev = NULL; struct virtual_channel *vchan = NULL; static atomic_t open_id_counter = ATOMIC_INIT(0); - struct hab_open_request request; - struct hab_open_request *recv_request; + struct hab_open_request request = {0}; + struct hab_open_request *recv_request = NULL; int sub_id = HAB_MMID_GET_MINOR(mm_id); struct hab_open_node pending_open = { { 0 } }; @@ -284,7 +298,7 @@ struct virtual_channel *frontend_open(struct uhab_context *ctx, goto err; } - open_id = atomic_inc_return(&open_id_counter); + open_id = (int)atomic_inc_return(&open_id_counter); vchan = hab_vchan_alloc(ctx, pchan, open_id); if (!vchan) { pr_err("vchan alloc failed\n"); @@ -336,7 +350,7 @@ struct virtual_channel *frontend_open(struct uhab_context *ctx, vchan->id); hab_open_pending_exit(ctx, pchan, &pending_open); - if (ret != -EINTR) + if (ret != -EINTR && ret != -ENXIO) ret = -EINVAL; goto err; } @@ -377,15 +391,15 @@ err: struct virtual_channel *backend_listen(struct uhab_context *ctx, unsigned int mm_id, int timeout) { - int ret, ret2; - int open_id, ver_fe; + int ret = 0, ret2 = 0; + int open_id = 0, ver_fe = 0; int sub_id = HAB_MMID_GET_MINOR(mm_id); struct physical_channel *pchan = NULL; - struct hab_device *dev; + struct hab_device *dev = NULL; struct virtual_channel *vchan = NULL; - struct hab_open_request request; - struct hab_open_request *recv_request; - uint32_t otherend_vchan_id; + struct hab_open_request request = {0}; + struct hab_open_request *recv_request = NULL; + uint32_t otherend_vchan_id = 0; struct hab_open_node pending_open = { { 0 } }; dev = find_hab_device(mm_id); @@ -407,6 +421,8 @@ struct virtual_channel *backend_listen(struct uhab_context *ctx, ret = -EINVAL; if (-EAGAIN == ret) { ret = -ETIMEDOUT; + } else if (-ENXIO == ret) { + pr_warn("open request canceling\n"); } else { /* device is closed */ pr_err("open request wait failed ctx closing %d\n", @@ -416,7 +432,7 @@ struct virtual_channel *backend_listen(struct uhab_context *ctx, } else if (!ret && recv_request && ((recv_request->xdata.ver_fe & 0xFFFF0000) != (HAB_API_VER & 0xFFFF0000))) { - int ret2; + int ret2 = 0; /* version check */ pr_err("version mismatch fe %X be %X on mmid %d\n", recv_request->xdata.ver_fe, HAB_API_VER, mm_id); @@ -467,11 +483,11 @@ struct virtual_channel *backend_listen(struct uhab_context *ctx, hab_open_request_init(&request, HAB_PAYLOAD_TYPE_INIT_DONE, pchan, 0, sub_id, open_id); ret = hab_open_listen(ctx, dev, &request, &recv_request, - HAB_HS_TIMEOUT); + HAB_HS_INIT_DONE_TIMEOUT); hab_open_pending_exit(ctx, pchan, &pending_open); - if (ret && recv_request && + if (!ret && recv_request && recv_request->type == HAB_PAYLOAD_TYPE_INIT_CANCEL) { - pr_err("listen cancelled vcid %x subid %d openid %d ret %d\n", + pr_warn("open rmt cancelled vcid %x subid %d openid %d ret %d\n", request.xdata.vchan_id, request.xdata.sub_id, request.xdata.open_id, ret); @@ -487,9 +503,12 @@ struct virtual_channel *backend_listen(struct uhab_context *ctx, ret2, vchan->id); hab_open_pending_exit(ctx, pchan, &pending_open); - ret = -ENODEV; /* open request cancelled remotely */ + ret = -ENODEV; /* open request FE cancelled remotely */ break; - } else if (ret != -EAGAIN) { + } else if (-ENXIO == ret) { + pr_warn("backend mmid %d listen canceling\n", mm_id); + goto err; + } else if (ret != -EAGAIN && ret != -EINTR) { hab_open_pending_exit(ctx, pchan, &pending_open); break; /* received something. good case! */ } @@ -540,8 +559,8 @@ long hab_vchan_send(struct uhab_context *ctx, void *data, unsigned int flags) { - struct virtual_channel *vchan; - int ret; + struct virtual_channel *vchan = NULL; + int ret = 0; struct hab_header header = HAB_HEADER_INITIALIZER; int nonblocking_flag = flags & HABMM_SOCKET_SEND_FLAGS_NON_BLOCKING; @@ -557,7 +576,7 @@ long hab_vchan_send(struct uhab_context *ctx, goto err; } - HAB_HEADER_SET_SIZE(header, sizebytes); + HAB_HEADER_SET_SIZE(header, (uint32_t)sizebytes); if (flags & HABMM_SOCKET_SEND_FLAGS_XING_VM_STAT) { HAB_HEADER_SET_TYPE(header, HAB_PAYLOAD_TYPE_PROFILE); if (sizebytes < sizeof(struct habmm_xing_vm_stat)) { @@ -602,11 +621,11 @@ int hab_vchan_recv(struct uhab_context *ctx, int *rsize, unsigned int flags) { - struct virtual_channel *vchan; + struct virtual_channel *vchan = NULL; int ret = 0; int nonblocking_flag = flags & HABMM_SOCKET_RECV_FLAGS_NON_BLOCKING; - vchan = hab_get_vchan_fromvcid(vcid, ctx, 1); + vchan = hab_get_vchan_fromvcid(vcid, ctx, 1); /* to drain local q */ if (!vchan) { pr_err("vcid %X vchan 0x%pK ctx %pK\n", vcid, vchan, ctx); return -ENODEV; @@ -640,6 +659,68 @@ bool hab_is_loopback(void) return hab_driver.b_loopback; } +static int hab_stop(struct uhab_context *ctx, unsigned int mmid) +{ + struct hab_forbidden_node *node = NULL, *tmp = NULL; + struct hab_device *dev = NULL; + + dev = find_hab_device(mmid); + if (dev == NULL) { + pr_err("failed to find dev based on id 0x%x\n", mmid); + return -EINVAL; + } + + spin_lock_bh(&ctx->forbidden_lock); + + list_for_each_entry_safe(node, tmp, &ctx->forbidden_chans, node) { + if (node->mmid == mmid) { + pr_info("mmid 0x%x has been in forbidden list, ctx %p\n", + mmid, ctx); + spin_unlock_bh(&ctx->forbidden_lock); + return 0; + } + } + + pr_info("Add mmid 0x%x into forbidden list, ctx %p\n", + mmid, ctx); + + node = kzalloc(sizeof(*node), GFP_ATOMIC); + if (!node) { + spin_unlock_bh(&ctx->forbidden_lock); + return -ENOMEM; + } + node->mmid = mmid; + list_add_tail(&node->node, &ctx->forbidden_chans); + + spin_unlock_bh(&ctx->forbidden_lock); + + wake_up_interruptible(&dev->openq); + + return 0; +} + +int hab_is_forbidden(struct uhab_context *ctx, + struct hab_device *dev, + uint32_t sub_id) +{ + struct hab_forbidden_node *node = NULL, *tmp = NULL; + + if (!dev) + return 0; + + spin_lock_bh(&ctx->forbidden_lock); + list_for_each_entry_safe(node, tmp, &ctx->forbidden_chans, node) { + if ((HAB_MMID_GET_MAJOR(node->mmid) == dev->id) && + (HAB_MMID_GET_MINOR(node->mmid) == sub_id)) { + spin_unlock_bh(&ctx->forbidden_lock); + return 1; + } + } + spin_unlock_bh(&ctx->forbidden_lock); + + return 0; +} + int hab_vchan_open(struct uhab_context *ctx, unsigned int mmid, int32_t *vcid, @@ -647,8 +728,9 @@ int hab_vchan_open(struct uhab_context *ctx, uint32_t flags) { struct virtual_channel *vchan = NULL; - struct hab_device *dev; + struct hab_device *dev = NULL; + (void)flags; pr_debug("Open mmid=%d, loopback mode=%d, loopback be ctx %d\n", mmid, hab_driver.b_loopback, ctx->lb_be); @@ -664,6 +746,13 @@ int hab_vchan_open(struct uhab_context *ctx, } else { dev = find_hab_device(mmid); + if (hab_is_forbidden(ctx, + dev, HAB_MMID_GET_MINOR(mmid))) { + pr_err("mmid 0x%x has been forbidden", + mmid); + return -ENXIO; + } + if (dev) { struct physical_channel *pchan = hab_pchan_find_domid(dev, @@ -689,7 +778,7 @@ int hab_vchan_open(struct uhab_context *ctx, if (IS_ERR(vchan)) { if (-ETIMEDOUT != PTR_ERR(vchan) && -EAGAIN != PTR_ERR(vchan)) pr_err("vchan open failed mmid=%d\n", mmid); - return PTR_ERR(vchan); + return (int)PTR_ERR(vchan); } pr_debug("vchan id %x remote id %x session %d\n", vchan->id, @@ -720,7 +809,8 @@ void hab_send_close_msg(struct virtual_channel *vchan) void hab_vchan_close(struct uhab_context *ctx, int32_t vcid) { - struct virtual_channel *vchan, *tmp; + struct virtual_channel *vchan = NULL, *tmp = NULL; + int vchan_found = 0; if (!ctx) return; @@ -744,10 +834,14 @@ void hab_vchan_close(struct uhab_context *ctx, int32_t vcid) hab_vchan_stop_notify(vchan); hab_vchan_put(vchan); /* there is a lock inside */ write_lock(&ctx->ctx_lock); + vchan_found = 1; break; } } write_unlock(&ctx->ctx_lock); + + if (!vchan_found) + hab_stop(ctx, vcid); } /* @@ -760,9 +854,9 @@ void hab_vchan_close(struct uhab_context *ctx, int32_t vcid) static int hab_initialize_pchan_entry(struct hab_device *mmid_device, int vmid_local, int vmid_remote, int is_be) { - char pchan_name[MAX_VMID_NAME_SIZE]; + char pchan_name[MAX_VMID_NAME_SIZE] = {0}; struct physical_channel *pchan = NULL; - int ret; + int ret = 0; int vmid = is_be ? vmid_remote : vmid_local; /* used for naming only */ if (!mmid_device) { @@ -799,7 +893,7 @@ static int hab_initialize_pchan_entry(struct hab_device *mmid_device, */ static int hab_generate_pchan(struct local_vmid *settings, int i, int j) { - int k, ret = 0; + int k = 0, ret = 0; pr_debug("%d as mmid %d in vmid %d\n", HABCFG_GET_MMID(settings, i, j), j, i); @@ -922,13 +1016,13 @@ static int hab_generate_pchan(struct local_vmid *settings, int i, int j) */ static int hab_generate_pchan_list(struct local_vmid *settings) { - int i, j, ret = 0; + int i = 0, j = 0, ret = 0; /* scan by valid VMs, then mmid */ pr_debug("self vmid is %d\n", settings->self); for (i = 0; i < HABCFG_VMID_MAX; i++) { - if (HABCFG_GET_VMID(settings, i) != HABCFG_VMID_INVALID && - HABCFG_GET_VMID(settings, i) != settings->self) { + if (HABCFG_GET_VMID(settings, i) != HABCFG_VMID_INVALID + && HABCFG_GET_VMID(settings, i) != settings->self) { pr_debug("create pchans for vm %d\n", i); for (j = 1; j <= HABCFG_MMID_AREA_MAX; j++) { @@ -954,9 +1048,9 @@ static int hab_generate_pchan_list(struct local_vmid *settings) int do_hab_parse(void) { - int result; - int i; - struct hab_device *device; + int result = 0; + int i = 0; + struct hab_device *device = NULL; /* single GVM is 2, multigvm is 2 or 3. GHS LV-GVM 2, LA-GVM 3 */ int default_gvmid = DEFAULT_GVMID; @@ -966,7 +1060,8 @@ int do_hab_parse(void) /* first check if hypervisor plug-in is ready */ result = hab_hypervisor_register(); if (result) { - pr_err("register HYP plug-in failed, ret %d\n", result); + pr_err("register HYP plug-in failed, ret %d driver version %s\n", + result, hab_info_str); return result; } @@ -1014,13 +1109,13 @@ int get_refcnt(struct kref ref) void hab_hypervisor_unregister_common(void) { - int status, i; - struct uhab_context *ctx; - struct virtual_channel *vchan; + int status = 0, i = 0; + struct uhab_context *ctx = NULL; + struct virtual_channel *vchan = NULL; for (i = 0; i < hab_driver.ndevices; i++) { struct hab_device *habdev = &hab_driver.devp[i]; - struct physical_channel *pchan, *pchan_tmp; + struct physical_channel *pchan = NULL, *pchan_tmp = NULL; list_for_each_entry_safe(pchan, pchan_tmp, &habdev->pchannels, node) { @@ -1194,13 +1289,11 @@ static long hab_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) recv_param->sizebytes = 0; ret = -EFAULT; } - } else if (ret && msg) { - pr_warn("vcid %X recv failed %d and msg is still of %zd bytes\n", - recv_param->vcid, (int)ret, msg->sizebytes); - } - - if (msg) hab_msg_free(msg); + } else + pr_warn("vcid %X recv failed %d buf size %d\n", + recv_param->vcid, (int)ret, + recv_param->sizebytes); break; case IOCTL_HAB_VC_EXPORT: ret = hab_mem_export(ctx, (struct hab_export *)data, 0); diff --git a/drivers/soc/qcom/hab/hab.h b/drivers/soc/qcom/hab/hab.h index 7d0c5f2673a2..cb7ed52050b2 100644 --- a/drivers/soc/qcom/hab/hab.h +++ b/drivers/soc/qcom/hab/hab.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2019, 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 @@ -46,6 +46,7 @@ #include <linux/kobject.h> #include <linux/sysfs.h> #include <linux/delay.h> +#include <linux/version.h> #include <soc/qcom/boot_stats.h> enum hab_payload_type { @@ -161,13 +162,13 @@ struct hab_header { #define HAB_HEADER_SET_SIZE(header, size) \ ((header).id_type_size = ((header).id_type_size & \ - (~HAB_HEADER_SIZE_MASK)) | \ + (uint32_t)(~HAB_HEADER_SIZE_MASK)) | \ (((size) << HAB_HEADER_SIZE_SHIFT) & \ HAB_HEADER_SIZE_MASK)) #define HAB_HEADER_SET_TYPE(header, type) \ ((header).id_type_size = ((header).id_type_size & \ - (~HAB_HEADER_TYPE_MASK)) | \ + (uint32_t)(~HAB_HEADER_TYPE_MASK)) | \ (((type) << HAB_HEADER_TYPE_SHIFT) & \ HAB_HEADER_TYPE_MASK)) @@ -192,6 +193,7 @@ struct hab_header { #define HAB_HEADER_GET_SESSION_ID(header) ((header).session_id) #define HAB_HS_TIMEOUT (10*1000*1000) +#define HAB_HS_INIT_DONE_TIMEOUT (3*1000) struct physical_channel { struct list_head node; @@ -265,6 +267,11 @@ struct hab_message { uint32_t data[]; }; +struct hab_forbidden_node { + struct list_head node; + uint32_t mmid; +}; + /* for all the pchans of same kind */ struct hab_device { char name[MAX_VMID_NAME_SIZE]; @@ -302,6 +309,9 @@ struct uhab_context { struct list_head pending_open; /* sent to remote */ int pending_cnt; + struct list_head forbidden_chans; + spinlock_t forbidden_lock; + rwlock_t ctx_lock; int closing; int kernel; @@ -403,6 +413,9 @@ struct export_desc { unsigned char payload[1]; } __packed; +int hab_is_forbidden(struct uhab_context *ctx, + struct hab_device *dev, + uint32_t sub_id); int hab_vchan_open(struct uhab_context *ctx, unsigned int mmid, int32_t *vcid, int32_t timeout, uint32_t flags); @@ -521,7 +534,7 @@ static inline void hab_ctx_get(struct uhab_context *ctx) static inline void hab_ctx_put(struct uhab_context *ctx) { if (ctx) - kref_put(&ctx->refcount, hab_ctx_free); + kref_put(&ctx->refcount, &hab_ctx_free); } void hab_send_close_msg(struct virtual_channel *vchan); diff --git a/drivers/soc/qcom/hab/hab_ghs.c b/drivers/soc/qcom/hab/hab_ghs.c index a445aa1a6707..2c8fb14dad1f 100644 --- a/drivers/soc/qcom/hab/hab_ghs.c +++ b/drivers/soc/qcom/hab/hab_ghs.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2018-2019, 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 @@ -93,7 +93,7 @@ static int get_dt_name_idx(int vmid_base, int mmid, struct ghs_vmm_plugin_info_s *plugin_info) { int idx = -1; - int i; + int i = 0; if (vmid_base < 0 || vmid_base > plugin_info->probe_cnt / GIPC_VM_SET_CNT) { @@ -169,6 +169,7 @@ int habhyp_commdev_alloc(void **commdev, int is_be, char *name, int vmid_remote, mmid_device->name, mmid_device->id, dt_name_idx); + of_node_put(gvh_dn); ret = -ENOENT; goto err; } @@ -176,9 +177,13 @@ int habhyp_commdev_alloc(void **commdev, int is_be, char *name, int vmid_remote, ret = of_property_read_string(gvh_dn, ghs_vmm_plugin_info.dt_name[dt_name_idx], &ep_path); - if (ret) + if (ret) { pr_err("failed to read endpoint str ret %d\n", ret); + of_node_put(gvh_dn); + ret = -ENOENT; + goto err; + } of_node_put(gvh_dn); ep_dn = of_find_node_by_path(ep_path); diff --git a/drivers/soc/qcom/hab/hab_mem_linux.c b/drivers/soc/qcom/hab/hab_mem_linux.c index 13bfab41f75d..6c61fbd00ded 100644 --- a/drivers/soc/qcom/hab/hab_mem_linux.c +++ b/drivers/soc/qcom/hab/hab_mem_linux.c @@ -598,6 +598,9 @@ static int hab_mem_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) vma->vm_private_data = pglist; vma->vm_flags |= VM_MIXEDMAP; + if (!(pglist->userflags & HABMM_IMPORT_FLAGS_CACHED)) + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + return 0; } diff --git a/drivers/soc/qcom/hab/hab_mimex.c b/drivers/soc/qcom/hab/hab_mimex.c index b03739974390..c761099186d4 100644 --- a/drivers/soc/qcom/hab/hab_mimex.c +++ b/drivers/soc/qcom/hab/hab_mimex.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2019, 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 @@ -31,15 +31,14 @@ static int hab_export_ack_find(struct uhab_context *ctx, struct hab_export_ack *expect_ack, struct virtual_channel *vchan) { int ret = 0; - struct hab_export_ack_recvd *ack_recvd, *tmp; + struct hab_export_ack_recvd *ack_recvd = NULL, *tmp = NULL; spin_lock_bh(&ctx->expq_lock); list_for_each_entry_safe(ack_recvd, tmp, &ctx->exp_rxq, node) { - if ((ack_recvd->ack.export_id == expect_ack->export_id && + if (ack_recvd->ack.export_id == expect_ack->export_id && ack_recvd->ack.vcid_local == expect_ack->vcid_local && - ack_recvd->ack.vcid_remote == expect_ack->vcid_remote) - || vchan->otherend_closed) { + ack_recvd->ack.vcid_remote == expect_ack->vcid_remote) { list_del(&ack_recvd->node); kfree(ack_recvd); ret = 1; @@ -52,6 +51,12 @@ static int hab_export_ack_find(struct uhab_context *ctx, } } + if (!ret && vchan->otherend_closed) { + pr_info("no expected ack, but vchan %x is remotely closed\n", + vchan->id); + ret = 1; + } + spin_unlock_bh(&ctx->expq_lock); return ret; @@ -60,7 +65,7 @@ static int hab_export_ack_find(struct uhab_context *ctx, static int hab_export_ack_wait(struct uhab_context *ctx, struct hab_export_ack *expect_ack, struct virtual_channel *vchan) { - int ret; + int ret = 0; ret = wait_event_interruptible_timeout(ctx->exp_wq, hab_export_ack_find(ctx, expect_ack, vchan), @@ -83,8 +88,8 @@ static struct export_desc *habmem_add_export(struct virtual_channel *vchan, int sizebytes, uint32_t flags) { - struct uhab_context *ctx; - struct export_desc *exp; + struct uhab_context *ctx = NULL; + struct export_desc *exp = NULL; if (!vchan || !sizebytes) return NULL; @@ -112,7 +117,7 @@ static struct export_desc *habmem_add_export(struct virtual_channel *vchan, ctx = vchan->ctx; write_lock(&ctx->exp_lock); ctx->export_total++; - list_add_tail(&exp->node, &ctx->exp_whse); + list_add_tail((struct list_head *)&exp->node, &ctx->exp_whse); write_unlock(&ctx->exp_lock); return exp; @@ -120,8 +125,8 @@ static struct export_desc *habmem_add_export(struct virtual_channel *vchan, void habmem_remove_export(struct export_desc *exp) { - struct physical_channel *pchan; - struct uhab_context *ctx; + struct physical_channel *pchan = NULL; + struct uhab_context *ctx = NULL; if (!exp || !exp->ctx || !exp->pchan) { if (exp) @@ -146,7 +151,7 @@ void habmem_remove_export(struct export_desc *exp) static int compress_pfns(void **pfns, int npages, unsigned int *data_size) { - int i, j = 0; + int i = 0, j = 0; struct grantable *item = (struct grantable *)*pfns; int region_size = 1; struct compressed_pfns *new_table = @@ -162,8 +167,8 @@ static int compress_pfns(void **pfns, int npages, unsigned int *data_size) region_size++; /* continuous pfn */ } else { new_table->region[j].size = region_size; - new_table->region[j].space = item[i].pfn - - item[i-1].pfn - 1; + new_table->region[j].space = (int)(item[i].pfn - + item[i-1].pfn - 1); j++; region_size = 1; } @@ -173,8 +178,8 @@ static int compress_pfns(void **pfns, int npages, unsigned int *data_size) new_table->nregions = j+1; vfree(*pfns); - *data_size = sizeof(struct compressed_pfns) + - sizeof(struct region)*new_table->nregions; + *data_size = (int)(sizeof(struct compressed_pfns) + + sizeof(struct region)*new_table->nregions); *pfns = new_table; return 0; } @@ -191,9 +196,9 @@ static int habmem_export_vchan(struct uhab_context *ctx, int nunits, uint32_t flags, uint32_t *export_id) { - int ret; - struct export_desc *exp; - uint32_t sizebytes = sizeof(*exp) + payload_size; + int ret = 0; + struct export_desc *exp = NULL; + uint32_t sizebytes = (uint32_t)(sizeof(*exp) + payload_size); struct hab_export_ack expected_ack = {0}; struct hab_header header = HAB_HEADER_INITIALIZER; @@ -239,8 +244,8 @@ int hab_mem_export(struct uhab_context *ctx, void *pdata_exp = NULL; unsigned int pdata_size = 0; uint32_t export_id = 0; - struct virtual_channel *vchan; - int page_count; + struct virtual_channel *vchan = NULL; + int page_count = 0; int compressed = 0; if (!ctx || !param || !param->buffer) @@ -266,7 +271,7 @@ int hab_mem_export(struct uhab_context *ctx, vchan->pchan->dom_id, pdata_exp, &compressed, - &pdata_size); + (int *)&pdata_size); } else { ret = habmem_hyp_grant_user((unsigned long)param->buffer, page_count, @@ -274,7 +279,7 @@ int hab_mem_export(struct uhab_context *ctx, vchan->pchan->dom_id, pdata_exp, &compressed, - &pdata_size); + (int *)&pdata_size); } if (ret < 0) { pr_err("habmem_hyp_grant vc %x failed size=%d ret=%d\n", @@ -306,9 +311,10 @@ int hab_mem_unexport(struct uhab_context *ctx, int kernel) { int ret = 0, found = 0; - struct export_desc *exp, *tmp; - struct virtual_channel *vchan; + struct export_desc *exp = NULL, *tmp = NULL; + struct virtual_channel *vchan = NULL; + (void)kernel; if (!ctx || !param) return -EINVAL; @@ -322,8 +328,9 @@ int hab_mem_unexport(struct uhab_context *ctx, write_lock(&ctx->exp_lock); list_for_each_entry_safe(exp, tmp, &ctx->exp_whse, node) { if (param->exportid == exp->export_id && - vchan->pchan == exp->pchan) { - list_del(&exp->node); + vchan->pchan == exp->pchan && + param->vcid == exp->vcid_local) { + list_del((struct list_head *)&exp->node); found = 1; break; } @@ -355,7 +362,7 @@ int hab_mem_import(struct uhab_context *ctx, { int ret = 0, found = 0; struct export_desc *exp = NULL; - struct virtual_channel *vchan; + struct virtual_channel *vchan = NULL; if (!ctx || !param) return -EINVAL; @@ -368,8 +375,9 @@ int hab_mem_import(struct uhab_context *ctx, spin_lock_bh(&ctx->imp_lock); list_for_each_entry(exp, &ctx->imp_whse, node) { - if ((exp->export_id == param->exportid) && - (exp->pchan == vchan->pchan)) { + if (exp->export_id == param->exportid && + exp->pchan == vchan->pchan && + param->vcid == exp->vcid_local) { found = 1; break; } @@ -414,8 +422,8 @@ int hab_mem_unimport(struct uhab_context *ctx, int kernel) { int ret = 0, found = 0; - struct export_desc *exp = NULL, *exp_tmp; - struct virtual_channel *vchan; + struct export_desc *exp = NULL, *exp_tmp = NULL; + struct virtual_channel *vchan = NULL; if (!ctx || !param) return -EINVAL; @@ -430,9 +438,10 @@ int hab_mem_unimport(struct uhab_context *ctx, spin_lock_bh(&ctx->imp_lock); list_for_each_entry_safe(exp, exp_tmp, &ctx->imp_whse, node) { if (exp->export_id == param->exportid && - exp->pchan == vchan->pchan) { + exp->pchan == vchan->pchan && + param->vcid == exp->vcid_local) { /* same pchan is expected here */ - list_del(&exp->node); + list_del((struct list_head *)&exp->node); ctx->import_total--; found = 1; break; diff --git a/drivers/soc/qcom/hab/hab_msg.c b/drivers/soc/qcom/hab/hab_msg.c index e0f4970ce14e..9f92074665df 100644 --- a/drivers/soc/qcom/hab/hab_msg.c +++ b/drivers/soc/qcom/hab/hab_msg.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2019, 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 @@ -14,7 +14,7 @@ static int hab_rx_queue_empty(struct virtual_channel *vchan) { - int ret; + int ret = 0; spin_lock_bh(&vchan->rx_lock); ret = list_empty(&vchan->rx_list); @@ -25,11 +25,11 @@ static int hab_rx_queue_empty(struct virtual_channel *vchan) static struct hab_message* hab_msg_alloc(struct physical_channel *pchan, size_t sizebytes) { - struct hab_message *message; + struct hab_message *message = NULL; if (sizebytes > HAB_HEADER_SIZE_MASK) { - pr_err("pchan %s send size too large %zd\n", - pchan->name, sizebytes); + pr_err("pchan %s send size too large %zd header %zd\n", + pchan->name, sizebytes, sizeof(*message)); return NULL; } @@ -81,16 +81,16 @@ hab_msg_dequeue(struct virtual_channel *vchan, struct hab_message **msg, message = list_first_entry(&vchan->rx_list, struct hab_message, node); if (message) { - if (*rsize >= message->sizebytes) { + if (*rsize >= (int)message->sizebytes) { /* msg can be safely retrieved in full */ list_del(&message->node); ret = 0; - *rsize = message->sizebytes; + *rsize = (int)message->sizebytes; } else { pr_err("vcid %x rcv buf too small %d < %zd\n", vchan->id, *rsize, message->sizebytes); - *rsize = message->sizebytes; + *rsize = (int)message->sizebytes; message = NULL; ret = -EOVERFLOW; /* come back again */ } @@ -121,7 +121,7 @@ static int hab_export_enqueue(struct virtual_channel *vchan, struct uhab_context *ctx = vchan->ctx; spin_lock_bh(&ctx->imp_lock); - list_add_tail(&exp->node, &ctx->imp_whse); + list_add_tail((struct list_head *)&exp->node, &ctx->imp_whse); ctx->import_total++; spin_unlock_bh(&ctx->imp_lock); @@ -139,7 +139,7 @@ static int hab_send_export_ack(struct virtual_channel *vchan, }; struct hab_header header = HAB_HEADER_INITIALIZER; - HAB_HEADER_SET_SIZE(header, sizeof(exp_ack)); + HAB_HEADER_SET_SIZE(header, (uint32_t)sizeof(exp_ack)); HAB_HEADER_SET_TYPE(header, HAB_PAYLOAD_TYPE_EXPORT_ACK); HAB_HEADER_SET_ID(header, exp->vcid_local); HAB_HEADER_SET_SESSION_ID(header, vchan->session_id); @@ -159,9 +159,9 @@ static int hab_receive_create_export_ack(struct physical_channel *pchan, pr_err("exp ack size %zu is not as arrived %zu\n", sizeof(ack_recvd->ack), sizebytes); - if (sizebytes > HAB_HEADER_SIZE_MASK) { - pr_err("pchan %s read size too large %zd\n", - pchan->name, sizebytes); + if (sizebytes > sizeof(ack_recvd->ack)) { + pr_err("pchan %s read size too large %zd %zd\n", + pchan->name, sizebytes, sizeof(ack_recvd->ack)); return -EINVAL; } @@ -197,16 +197,16 @@ int hab_msg_recv(struct physical_channel *pchan, struct hab_header *header) { int ret = 0; - struct hab_message *message; + struct hab_message *message = NULL; struct hab_device *dev = pchan->habdev; size_t sizebytes = HAB_HEADER_GET_SIZE(*header); uint32_t payload_type = HAB_HEADER_GET_TYPE(*header); uint32_t vchan_id = HAB_HEADER_GET_ID(*header); uint32_t session_id = HAB_HEADER_GET_SESSION_ID(*header); struct virtual_channel *vchan = NULL; - struct export_desc *exp_desc; - struct timeval tv; - unsigned long long rx_mpm_tv; + struct export_desc *exp_desc = NULL, exp_ack = {0}; + struct timeval tv = {0}; + unsigned long long rx_mpm_tv = 0; /* get the local virtual channel if it isn't an open message */ if (payload_type != HAB_PAYLOAD_TYPE_INIT && @@ -296,8 +296,8 @@ int hab_msg_recv(struct physical_channel *pchan, case HAB_PAYLOAD_TYPE_EXPORT: if (sizebytes > HAB_HEADER_SIZE_MASK) { - pr_err("%s exp size too large %zd\n", - pchan->name, sizebytes); + pr_err("%s exp size too large %zd header %zd\n", + pchan->name, sizebytes, sizeof(*exp_desc)); break; } @@ -322,9 +322,12 @@ int hab_msg_recv(struct physical_channel *pchan, exp_desc->domid_remote = pchan->vmid_remote; exp_desc->domid_local = pchan->vmid_local; exp_desc->pchan = pchan; + exp_ack = *exp_desc; /* preserve exporter's info for ack */ + exp_desc->vcid_remote = exp_desc->vcid_local; + exp_desc->vcid_local = vchan->id; - hab_export_enqueue(vchan, exp_desc); - hab_send_export_ack(vchan, pchan, exp_desc); + hab_export_enqueue(vchan, exp_desc); /* for local use */ + hab_send_export_ack(vchan, pchan, &exp_ack); /* ack exporter */ break; case HAB_PAYLOAD_TYPE_EXPORT_ACK: diff --git a/drivers/soc/qcom/hab/hab_open.c b/drivers/soc/qcom/hab/hab_open.c index bc2b774883f4..72dc9a54d20e 100644 --- a/drivers/soc/qcom/hab/hab_open.c +++ b/drivers/soc/qcom/hab/hab_open.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2019, 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 @@ -32,7 +32,8 @@ int hab_open_request_send(struct hab_open_request *request) { struct hab_header header = HAB_HEADER_INITIALIZER; - HAB_HEADER_SET_SIZE(header, sizeof(struct hab_open_send_data)); + HAB_HEADER_SET_SIZE(header, + (uint32_t)sizeof(struct hab_open_send_data)); HAB_HEADER_SET_TYPE(header, request->type); return physical_channel_send(request->pchan, &header, &request->xdata); @@ -42,12 +43,12 @@ int hab_open_request_send(struct hab_open_request *request) int hab_open_request_add(struct physical_channel *pchan, size_t sizebytes, int request_type) { - struct hab_open_node *node; + struct hab_open_node *node = NULL; struct hab_device *dev = pchan->habdev; - struct hab_open_request *request; - struct timeval tv; + struct hab_open_request *request = NULL; + struct timeval tv = {0}; - if (sizebytes > HAB_HEADER_SIZE_MASK) { + if (sizebytes > sizeof(request->xdata)) { pr_err("pchan %s request size too large %zd\n", pchan->name, sizebytes); return -EINVAL; @@ -83,9 +84,9 @@ static int hab_open_request_find(struct uhab_context *ctx, struct hab_open_request *listen, struct hab_open_request **recv_request) { - struct hab_open_node *node, *tmp; - struct hab_open_request *request; - struct timeval tv; + struct hab_open_node *node = NULL, *tmp = NULL; + struct hab_open_request *request = NULL; + struct timeval tv = {0}; int ret = 0; if (ctx->closing || @@ -126,6 +127,10 @@ static int hab_open_request_find(struct uhab_context *ctx, done: spin_unlock_bh(&dev->openlock); + + if (hab_is_forbidden(ctx, dev, listen->xdata.sub_id)) + ret = 1; + return ret; } @@ -165,6 +170,9 @@ int hab_open_listen(struct uhab_context *ctx, pr_warn("something failed in open listen ret %d\n", ret); ret = -EINTR; /* condition not met */ + } else if (hab_is_forbidden(ctx, dev, listen->xdata.sub_id)) { + pr_warn("local open cancelled ret %d\n", ret); + ret = -ENXIO; } else if (ret > 0) ret = 0; /* condition met */ } else { /* fe case */ @@ -176,6 +184,9 @@ int hab_open_listen(struct uhab_context *ctx, } else if (-ERESTARTSYS == ret) { pr_warn("local interrupted ret %d\n", ret); ret = -EINTR; + } else if (hab_is_forbidden(ctx, dev, listen->xdata.sub_id)) { + pr_warn("local open cancelled ret %d\n", ret); + ret = -ENXIO; } } @@ -187,15 +198,15 @@ int hab_open_receive_cancel(struct physical_channel *pchan, size_t sizebytes) { struct hab_device *dev = pchan->habdev; - struct hab_open_send_data data; - struct hab_open_request *request; - struct hab_open_node *node, *tmp; + struct hab_open_send_data data = {0}; + struct hab_open_request *request = NULL; + struct hab_open_node *node = NULL, *tmp = NULL; int bfound = 0; - struct timeval tv; + struct timeval tv = {0}; - if (sizebytes > HAB_HEADER_SIZE_MASK) { - pr_err("pchan %s cancel size too large %zd\n", - pchan->name, sizebytes); + if (sizebytes > sizeof(data)) { + pr_err("pchan %s cancel size too large %zd header %zd\n", + pchan->name, sizebytes, sizeof(data)); return -EINVAL; } @@ -260,7 +271,8 @@ int hab_open_cancel_notify(struct hab_open_request *request) { struct hab_header header = HAB_HEADER_INITIALIZER; - HAB_HEADER_SET_SIZE(header, sizeof(struct hab_open_send_data)); + HAB_HEADER_SET_SIZE(header, + (uint32_t)sizeof(struct hab_open_send_data)); HAB_HEADER_SET_TYPE(header, HAB_PAYLOAD_TYPE_INIT_CANCEL); return physical_channel_send(request->pchan, &header, &request->xdata); @@ -270,6 +282,7 @@ int hab_open_pending_enter(struct uhab_context *ctx, struct physical_channel *pchan, struct hab_open_node *pending) { + (void)pchan; write_lock(&ctx->ctx_lock); list_add_tail(&pending->node, &ctx->pending_open); ctx->pending_cnt++; @@ -282,9 +295,10 @@ int hab_open_pending_exit(struct uhab_context *ctx, struct physical_channel *pchan, struct hab_open_node *pending) { - struct hab_open_node *node, *tmp; + struct hab_open_node *node = NULL, *tmp = NULL; int ret = -ENOENT; + (void)pchan; write_lock(&ctx->ctx_lock); list_for_each_entry_safe(node, tmp, &ctx->pending_open, node) { if ((node->request.type == pending->request.type) && diff --git a/drivers/soc/qcom/hab/hab_parser.c b/drivers/soc/qcom/hab/hab_parser.c index c332587e2b47..1c53877005a0 100644 --- a/drivers/soc/qcom/hab/hab_parser.c +++ b/drivers/soc/qcom/hab/hab_parser.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2017-2019, 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 @@ -23,7 +23,7 @@ static int fill_vmid_mmid_tbl(struct vmid_mmid_desc *tbl, int32_t vm_start, int32_t mmid_range, int32_t be) { int ret = 0; - int i, j; + int i = 0, j = 0; for (i = vm_start; i < vm_start+vm_range; i++) { tbl[i].vmid = i; /* set valid vmid value to make it usable */ @@ -43,6 +43,7 @@ static int fill_vmid_mmid_tbl(struct vmid_mmid_desc *tbl, int32_t vm_start, void dump_settings(struct local_vmid *settings) { + (void)settings; pr_debug("self vmid is %d\n", settings->self); } @@ -152,7 +153,7 @@ static int hab_parse_dt(struct local_vmid *settings) */ int hab_parse(struct local_vmid *settings) { - int ret; + int ret = 0; ret = hab_parse_dt(settings); diff --git a/drivers/soc/qcom/hab/hab_pchan.c b/drivers/soc/qcom/hab/hab_pchan.c index 8a9a6dfd1e0c..d82321a2b9cf 100644 --- a/drivers/soc/qcom/hab/hab_pchan.c +++ b/drivers/soc/qcom/hab/hab_pchan.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2019, 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 @@ -47,7 +47,7 @@ static void hab_pchan_free(struct kref *ref) { struct physical_channel *pchan = container_of(ref, struct physical_channel, refcount); - struct virtual_channel *vchan; + struct virtual_channel *vchan = NULL; pr_debug("pchan %s refcnt %d\n", pchan->name, get_refcnt(pchan->refcount)); @@ -67,14 +67,13 @@ static void hab_pchan_free(struct kref *ref) } read_unlock(&pchan->vchans_lock); - kfree(pchan->hyp_data); kfree(pchan); } struct physical_channel * hab_pchan_find_domid(struct hab_device *dev, int dom_id) { - struct physical_channel *pchan; + struct physical_channel *pchan = NULL; spin_lock_bh(&dev->pchan_lock); list_for_each_entry(pchan, &dev->pchannels, node) @@ -104,5 +103,5 @@ void hab_pchan_get(struct physical_channel *pchan) void hab_pchan_put(struct physical_channel *pchan) { if (pchan) - kref_put(&pchan->refcount, hab_pchan_free); + kref_put(&pchan->refcount, &hab_pchan_free); } diff --git a/drivers/soc/qcom/hab/hab_stat.c b/drivers/soc/qcom/hab/hab_stat.c index b78e5e7e9867..b72528cc4d5f 100644 --- a/drivers/soc/qcom/hab/hab_stat.c +++ b/drivers/soc/qcom/hab/hab_stat.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2018-2019, 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 @@ -32,28 +32,28 @@ int hab_stat_deinit(struct hab_driver *driver) static int hab_stat_buffer_print(char *dest, int dest_size, const char *fmt, ...) { - va_list args; - char line[MAX_LINE_SIZE]; - int ret; + va_list args = {0}; + char line[MAX_LINE_SIZE] = {0}; + int ret = 0; va_start(args, fmt); ret = vsnprintf(line, sizeof(line), fmt, args); va_end(args); if (ret > 0) - ret = strlcat(dest, line, dest_size); + ret = (int)strlcat(dest, line, dest_size); return ret; } int hab_stat_show_vchan(struct hab_driver *driver, char *buf, int size) { - int i, ret = 0; + int i = 0, ret = 0; - ret = strlcpy(buf, "", size); + ret = (int)strlcpy(buf, "", size); for (i = 0; i < driver->ndevices; i++) { struct hab_device *dev = &driver->devp[i]; - struct physical_channel *pchan; - struct virtual_channel *vc; + struct physical_channel *pchan = NULL; + struct virtual_channel *vc = NULL; spin_lock_bh(&dev->pchan_lock); list_for_each_entry(pchan, &dev->pchannels, node) { @@ -86,9 +86,9 @@ int hab_stat_show_ctx(struct hab_driver *driver, char *buf, int size) { int ret = 0; - struct uhab_context *ctx; + struct uhab_context *ctx = NULL; - ret = strlcpy(buf, "", size); + ret = (int)strlcpy(buf, "", size); spin_lock_bh(&hab_driver.drvlock); ret = hab_stat_buffer_print(buf, size, @@ -96,10 +96,11 @@ int hab_stat_show_ctx(struct hab_driver *driver, driver->ctx_cnt); list_for_each_entry(ctx, &hab_driver.uctx_list, node) { ret = hab_stat_buffer_print(buf, size, - "ctx %d K %d close %d vc %d exp %d imp %d open %d\n", + "ctx %d K %d close %d vc %d exp %d imp %d open %d ref %d\n", ctx->owner, ctx->kernel, ctx->closing, ctx->vcnt, ctx->export_total, - ctx->import_total, ctx->pending_cnt); + ctx->import_total, ctx->pending_cnt, + get_refcnt(ctx->refcount)); } spin_unlock_bh(&hab_driver.drvlock); @@ -108,7 +109,7 @@ int hab_stat_show_ctx(struct hab_driver *driver, static int get_pft_tbl_total_size(struct compressed_pfns *pfn_table) { - int i, total_size = 0; + int i = 0, total_size = 0; for (i = 0; i < pfn_table->nregions; i++) total_size += pfn_table->region[i].size * PAGE_SIZE; @@ -119,27 +120,40 @@ static int get_pft_tbl_total_size(struct compressed_pfns *pfn_table) static int print_ctx_total_expimp(struct uhab_context *ctx, char *buf, int size) { - struct compressed_pfns *pfn_table; + struct compressed_pfns *pfn_table = NULL; int exp_total = 0, imp_total = 0; int exp_cnt = 0, imp_cnt = 0; - struct export_desc *exp; + struct export_desc *exp = NULL; + int exim_size = 0; read_lock(&ctx->exp_lock); + hab_stat_buffer_print(buf, size, "export[expid:vcid:size]: "); list_for_each_entry(exp, &ctx->exp_whse, node) { pfn_table = (struct compressed_pfns *)exp->payload; - exp_total += get_pft_tbl_total_size(pfn_table); + exim_size = get_pft_tbl_total_size(pfn_table); + exp_total += exim_size; exp_cnt++; + hab_stat_buffer_print(buf, size, + "[%d:%x:%d] ", exp->export_id, + exp->vcid_local, exim_size); } + hab_stat_buffer_print(buf, size, "\n"); read_unlock(&ctx->exp_lock); spin_lock_bh(&ctx->imp_lock); + hab_stat_buffer_print(buf, size, "import[expid:vcid:size]: "); list_for_each_entry(exp, &ctx->imp_whse, node) { if (habmm_imp_hyp_map_check(ctx->import_ctx, exp)) { pfn_table = (struct compressed_pfns *)exp->payload; - imp_total += get_pft_tbl_total_size(pfn_table); + exim_size = get_pft_tbl_total_size(pfn_table); + imp_total += exim_size; imp_cnt++; + hab_stat_buffer_print(buf, size, + "[%d:%x:%d] ", exp->export_id, + exp->vcid_local, exim_size); } } + hab_stat_buffer_print(buf, size, "\n"); spin_unlock_bh(&ctx->imp_lock); if (exp_cnt || exp_total || imp_cnt || imp_total) @@ -154,10 +168,11 @@ static int print_ctx_total_expimp(struct uhab_context *ctx, int hab_stat_show_expimp(struct hab_driver *driver, int pid, char *buf, int size) { - struct uhab_context *ctx; - int ret; + struct uhab_context *ctx = NULL; + int ret = 0; - ret = strlcpy(buf, "", size); + (void)driver; + ret = (int)strlcpy(buf, "", size); spin_lock_bh(&hab_driver.drvlock); list_for_each_entry(ctx, &hab_driver.uctx_list, node) { diff --git a/drivers/soc/qcom/hab/hab_vchan.c b/drivers/soc/qcom/hab/hab_vchan.c index a95d6c7451c9..a6ee00211347 100644 --- a/drivers/soc/qcom/hab/hab_vchan.c +++ b/drivers/soc/qcom/hab/hab_vchan.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2019, 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 @@ -16,8 +16,8 @@ struct virtual_channel * hab_vchan_alloc(struct uhab_context *ctx, struct physical_channel *pchan, int openid) { - int id; - struct virtual_channel *vchan; + int id = 0; + struct virtual_channel *vchan = NULL; if (!pchan || !ctx) return NULL; @@ -73,10 +73,10 @@ hab_vchan_free(struct kref *ref) { struct virtual_channel *vchan = container_of(ref, struct virtual_channel, refcount); - struct hab_message *message, *msg_tmp; + struct hab_message *message = NULL, *msg_tmp = NULL; struct physical_channel *pchan = vchan->pchan; struct uhab_context *ctx = vchan->ctx; - struct virtual_channel *vc, *vc_tmp; + struct virtual_channel *vc = NULL, *vc_tmp = NULL; spin_lock_bh(&vchan->rx_lock); list_for_each_entry_safe(message, msg_tmp, &vchan->rx_list, node) { @@ -117,7 +117,7 @@ hab_vchan_free(struct kref *ref) struct virtual_channel* hab_vchan_get(struct physical_channel *pchan, struct hab_header *header) { - struct virtual_channel *vchan; + struct virtual_channel *vchan = NULL; uint32_t vchan_id = HAB_HEADER_GET_ID(*header); uint32_t session_id = HAB_HEADER_GET_SESSION_ID(*header); size_t sizebytes = HAB_HEADER_GET_SIZE(*header); @@ -182,7 +182,7 @@ void hab_vchan_stop(struct virtual_channel *vchan) void hab_vchans_stop(struct physical_channel *pchan) { - struct virtual_channel *vchan, *tmp; + struct virtual_channel *vchan = NULL, *tmp = NULL; read_lock(&pchan->vchans_lock); list_for_each_entry_safe(vchan, tmp, &pchan->vchannels, pnode) { @@ -200,12 +200,12 @@ void hab_vchan_stop_notify(struct virtual_channel *vchan) static int hab_vchans_per_pchan_empty(struct physical_channel *pchan) { - int empty; + int empty = 0; read_lock(&pchan->vchans_lock); empty = list_empty(&pchan->vchannels); if (!empty) { - struct virtual_channel *vchan; + struct virtual_channel *vchan = NULL; int vcnt = pchan->vcnt; list_for_each_entry(vchan, &pchan->vchannels, pnode) { @@ -230,9 +230,9 @@ static int hab_vchans_per_pchan_empty(struct physical_channel *pchan) static int hab_vchans_empty(int vmid) { - int i, empty = 1; - struct physical_channel *pchan; - struct hab_device *hab_dev; + int i = 0, empty = 1; + struct physical_channel *pchan = NULL; + struct hab_device *hab_dev = NULL; for (i = 0; i < hab_driver.ndevices; i++) { hab_dev = &hab_driver.devp[i]; @@ -277,13 +277,15 @@ int hab_vchan_find_domid(struct virtual_channel *vchan) void hab_vchan_put(struct virtual_channel *vchan) { if (vchan) - kref_put(&vchan->refcount, hab_vchan_free); + kref_put(&vchan->refcount, &hab_vchan_free); } int hab_vchan_query(struct uhab_context *ctx, int32_t vcid, uint64_t *ids, char *names, size_t name_size, uint32_t flags) { - struct virtual_channel *vchan; + struct virtual_channel *vchan = NULL; + + (void)flags; vchan = hab_get_vchan_fromvcid(vcid, ctx, 1); if (!vchan) diff --git a/drivers/soc/qcom/hab/khab.c b/drivers/soc/qcom/hab/khab.c index c4acf12fd553..b9b508e8e72d 100644 --- a/drivers/soc/qcom/hab/khab.c +++ b/drivers/soc/qcom/hab/khab.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2019, 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 @@ -47,7 +47,7 @@ int32_t habmm_socket_recv(int32_t handle, void *dst_buff, uint32_t *size_bytes, uint32_t timeout, uint32_t flags) { int ret = 0; - struct hab_message *msg; + struct hab_message *msg = NULL; if (!size_bytes || !dst_buff) return -EINVAL; diff --git a/drivers/soc/qcom/ipc_router_mhi_xprt.c b/drivers/soc/qcom/ipc_router_mhi_xprt.c index 104053406ea9..9da9d41e2aa6 100644 --- a/drivers/soc/qcom/ipc_router_mhi_xprt.c +++ b/drivers/soc/qcom/ipc_router_mhi_xprt.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2019, 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 @@ -876,6 +876,12 @@ static int ipc_router_mhi_config_init( spin_lock_init(&mhi_xprtp->rx_addr_map_list_lock); rc = ipc_router_mhi_driver_register(mhi_xprtp, dev); + if (rc < 0) { + IPC_RTR_ERR("%s: mhi registration failed\n", __func__); + destroy_workqueue(mhi_xprtp->wq); + kfree(mhi_xprtp); + }; + return rc; } diff --git a/drivers/soc/qcom/msm_smem.c b/drivers/soc/qcom/msm_smem.c index cf3e0e084ab4..0a4e10256998 100644 --- a/drivers/soc/qcom/msm_smem.c +++ b/drivers/soc/qcom/msm_smem.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2019, 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 @@ -182,6 +182,20 @@ static struct restart_notifier_block restart_notifiers[] = { static int init_smem_remote_spinlock(void); /** + * smem_get_toc() - Used for getting partitions TOC + * + * @return - Base address off partitions TOC + * + * Helper function to get base address of partition TOC, + * that is present in top 4K of first smem region. + */ +static struct smem_toc __iomem *smem_get_toc(void) +{ + return smem_areas[0].virt_addr + + smem_areas[0].size - 4 * 1024; +} + +/** * is_probe_done() - Did the probe function successfully complete * * @return - true if probe successfully completed, false if otherwise @@ -315,6 +329,7 @@ static void *__smem_get_entry_nonsecure(unsigned id, unsigned *size, int use_spinlocks = spinlocks_initialized && use_rspinlock; void *ret = 0; unsigned long flags = 0; + uint32_t e_size; int rc; if (!skip_init_check && !smem_initialized_check()) @@ -333,7 +348,11 @@ static void *__smem_get_entry_nonsecure(unsigned id, unsigned *size, if (toc[id].allocated) { phys_addr_t phys_base; - *size = toc[id].size; + e_size = toc[id].size; + if (e_size > smem_ram_size) + return ret; + *size = e_size; + barrier(); phys_base = toc[id].reserved & BASE_ADDR_MASK; @@ -368,12 +387,19 @@ static void *__smem_get_entry_secure(unsigned id, bool skip_init_check, bool use_rspinlock) { + struct smem_partition_allocation_header *alloc_hdr; struct smem_partition_header *hdr; + uint32_t offset_free_uncached; + struct smem_toc __iomem *toc; + uint32_t offset_free_cached; unsigned long lflags = 0; - void *item = NULL; - struct smem_partition_allocation_header *alloc_hdr; + uint32_t partition_size; uint32_t partition_num; + uint32_t padding_data; + uint32_t padding_hdr; uint32_t a_hdr_size; + uint32_t item_size; + void *item = NULL; int rc; SMEM_DBG("%s(%u, %u, %u, %d, %d)\n", __func__, id, to_proc, @@ -393,9 +419,13 @@ static void *__smem_get_entry_secure(unsigned id, return NULL; } + toc = smem_get_toc(); + if (flags & SMEM_ANY_HOST_FLAG || !partitions[to_proc].offset) { if (use_comm_partition) { partition_num = comm_partition.partition_num; + partition_size = + readl_relaxed(&toc->entry[partition_num].size); hdr = smem_areas[0].virt_addr + comm_partition.offset; } else { return __smem_get_entry_nonsecure(id, size, @@ -403,6 +433,7 @@ static void *__smem_get_entry_secure(unsigned id, } } else { partition_num = partitions[to_proc].partition_num; + partition_size = readl_relaxed(&toc->entry[partition_num].size); hdr = smem_areas[0].virt_addr + partitions[to_proc].offset; } if (unlikely(!spinlocks_initialized)) { @@ -433,11 +464,20 @@ static void *__smem_get_entry_secure(unsigned id, if (flags & SMEM_ITEM_CACHED_FLAG) { a_hdr_size = ALIGN(sizeof(*alloc_hdr), partitions[to_proc].size_cacheline); - for (alloc_hdr = (void *)(hdr) + hdr->size - a_hdr_size; + offset_free_cached = hdr->offset_free_cached; + if (WARN_ON(offset_free_cached > partition_size)) + return NULL; + + for (alloc_hdr = (void *)(hdr) + partition_size - a_hdr_size; (void *)(alloc_hdr) > (void *)(hdr) + - hdr->offset_free_cached; + offset_free_cached; alloc_hdr = (void *)(alloc_hdr) - - alloc_hdr->size - a_hdr_size) { + item_size - a_hdr_size) { + item_size = alloc_hdr->size; + padding_data = alloc_hdr->padding_data; + if (WARN_ON(padding_data > item_size + || item_size > partition_size)) + return NULL; if (alloc_hdr->canary != SMEM_ALLOCATION_CANARY) { LOG_ERR( "%s: SMEM corruption detected. Partition %d to %d at %p\n", @@ -450,20 +490,30 @@ static void *__smem_get_entry_secure(unsigned id, } if (alloc_hdr->smem_type == id) { /* 8 byte alignment to match legacy */ - *size = ALIGN(alloc_hdr->size - - alloc_hdr->padding_data, 8); - item = (void *)(alloc_hdr) - alloc_hdr->size; + *size = ALIGN(item_size - padding_data, 8); + item = (void *)(alloc_hdr) - item_size; break; } } } else { + offset_free_uncached = hdr->offset_free_uncached; + if (WARN_ON(offset_free_uncached > partition_size)) + return NULL; + for (alloc_hdr = (void *)(hdr) + sizeof(*hdr); (void *)(alloc_hdr) < (void *)(hdr) + - hdr->offset_free_uncached; + offset_free_uncached; alloc_hdr = (void *)(alloc_hdr) + sizeof(*alloc_hdr) + - alloc_hdr->padding_hdr + - alloc_hdr->size) { + padding_hdr + + item_size) { + padding_hdr = alloc_hdr->padding_hdr; + padding_data = alloc_hdr->padding_data; + item_size = alloc_hdr->size; + if (WARN_ON(padding_hdr > partition_size + || item_size > partition_size + || padding_data > item_size)) + return NULL; if (alloc_hdr->canary != SMEM_ALLOCATION_CANARY) { LOG_ERR( "%s: SMEM corruption detected. Partition %d to %d at %p\n", @@ -476,11 +526,10 @@ static void *__smem_get_entry_secure(unsigned id, } if (alloc_hdr->smem_type == id) { /* 8 byte alignment to match legacy */ - *size = ALIGN(alloc_hdr->size - - alloc_hdr->padding_data, 8); + *size = ALIGN(item_size - padding_data, 8); item = (void *)(alloc_hdr) + sizeof(*alloc_hdr) + - alloc_hdr->padding_hdr; + padding_hdr; break; } } @@ -569,10 +618,17 @@ static void *alloc_item_nonsecure(unsigned id, unsigned size_in) void *smem_base = smem_ram_base; struct smem_shared *shared = smem_base; struct smem_heap_entry *toc = shared->heap_toc; + uint32_t free_offset, heap_remaining; void *ret = NULL; - if (shared->heap_info.heap_remaining >= size_in) { - toc[id].offset = shared->heap_info.free_offset; + heap_remaining = shared->heap_info.heap_remaining; + free_offset = shared->heap_info.free_offset; + if (WARN_ON(heap_remaining > smem_ram_size + || free_offset > smem_ram_size)) + return NULL; + + if (heap_remaining >= size_in) { + toc[id].offset = free_offset; toc[id].size = size_in; /* * wmb() is necessary to ensure the allocation data is @@ -584,7 +640,7 @@ static void *alloc_item_nonsecure(unsigned id, unsigned size_in) shared->heap_info.free_offset += size_in; shared->heap_info.heap_remaining -= size_in; - ret = smem_base + toc[id].offset; + ret = smem_base + free_offset; /* * wmb() is necessary to ensure the heap data is consistent * before continuing to prevent race conditions with remote @@ -620,11 +676,15 @@ static void *alloc_item_secure(unsigned id, unsigned size_in, unsigned to_proc, void *smem_base = smem_ram_base; struct smem_partition_header *hdr; struct smem_partition_allocation_header *alloc_hdr; + uint32_t offset_free_uncached; + struct smem_toc __iomem *toc; + uint32_t offset_free_cached; + uint32_t partition_size; + uint32_t partition_num; uint32_t a_hdr_size; uint32_t a_data_size; uint32_t size_cacheline; uint32_t free_space; - uint32_t partition_num; void *ret = NULL; if (to_proc == SMEM_COMM_HOST) { @@ -651,27 +711,35 @@ static void *alloc_item_secure(unsigned id, unsigned size_in, unsigned to_proc, BUG(); } - free_space = hdr->offset_free_cached - - hdr->offset_free_uncached; + toc = smem_get_toc(); + partition_size = readl_relaxed(&toc->entry[partition_num].size); + + offset_free_cached = hdr->offset_free_cached; + offset_free_uncached = hdr->offset_free_uncached; + if (WARN_ON(offset_free_uncached > offset_free_cached + || offset_free_cached > partition_size)) + return NULL; + + free_space = offset_free_cached - offset_free_uncached; if (flags & SMEM_ITEM_CACHED_FLAG) { a_hdr_size = ALIGN(sizeof(*alloc_hdr), size_cacheline); a_data_size = ALIGN(size_in, size_cacheline); - if (free_space < a_hdr_size + a_data_size) { + if (free_space < a_hdr_size + a_data_size + || free_space < size_in) { SMEM_INFO( - "%s: id %u not enough memory %u (required %u)\n", - __func__, id, free_space, - a_hdr_size + a_data_size); + "%s: id %u not enough memory %u (required %u), (size_in %u)\n", + __func__, id, free_space, + a_hdr_size + a_data_size, size_in); return ret; } - alloc_hdr = (void *)(hdr) + hdr->offset_free_cached - - a_hdr_size; + alloc_hdr = (void *)(hdr) + offset_free_cached - a_hdr_size; alloc_hdr->canary = SMEM_ALLOCATION_CANARY; alloc_hdr->smem_type = id; alloc_hdr->size = a_data_size; alloc_hdr->padding_data = a_data_size - size_in; alloc_hdr->padding_hdr = a_hdr_size - sizeof(*alloc_hdr); - hdr->offset_free_cached = hdr->offset_free_cached - + hdr->offset_free_cached = offset_free_cached - a_hdr_size - a_data_size; ret = (void *)(alloc_hdr) - a_data_size; /* @@ -686,20 +754,21 @@ static void *alloc_item_secure(unsigned id, unsigned size_in, unsigned to_proc, } else { a_hdr_size = sizeof(*alloc_hdr); a_data_size = ALIGN(size_in, 8); - if (free_space < a_hdr_size + a_data_size) { + if (free_space < a_hdr_size + a_data_size + || free_space < size_in) { SMEM_INFO( - "%s: id %u not enough memory %u (required %u)\n", - __func__, id, free_space, - a_hdr_size + a_data_size); + "%s: id %u not enough memory %u (required %u) (size_in %u)\n", + __func__, id, free_space, + a_hdr_size + a_data_size, size_in); return ret; } - alloc_hdr = (void *)(hdr) + hdr->offset_free_uncached; + alloc_hdr = (void *)(hdr) + offset_free_uncached; alloc_hdr->canary = SMEM_ALLOCATION_CANARY; alloc_hdr->smem_type = id; alloc_hdr->size = a_data_size; alloc_hdr->padding_data = a_data_size - size_in; alloc_hdr->padding_hdr = a_hdr_size - sizeof(*alloc_hdr); - hdr->offset_free_uncached = hdr->offset_free_uncached + + hdr->offset_free_uncached = offset_free_uncached + a_hdr_size + a_data_size; ret = alloc_hdr + 1; } @@ -891,6 +960,12 @@ unsigned smem_get_free_space(unsigned to_proc) { struct smem_partition_header *hdr; struct smem_shared *shared; + uint32_t offset_free_uncached; + struct smem_toc __iomem *toc; + uint32_t offset_free_cached; + uint32_t heap_remaining; + uint32_t p_size; + uint32_t p_num; if (to_proc >= NUM_SMEM_SUBSYSTEMS) { pr_err("%s: invalid to_proc:%d\n", __func__, to_proc); @@ -905,11 +980,24 @@ unsigned smem_get_free_space(unsigned to_proc) return UINT_MAX; } hdr = smem_areas[0].virt_addr + partitions[to_proc].offset; - return hdr->offset_free_cached - hdr->offset_free_uncached; - } else { - shared = smem_ram_base; - return shared->heap_info.heap_remaining; + offset_free_cached = hdr->offset_free_cached; + offset_free_uncached = hdr->offset_free_uncached; + + toc = smem_get_toc(); + p_num = partitions[to_proc].partition_num; + p_size = readl_relaxed(&toc->entry[p_num].size); + if (WARN_ON(offset_free_uncached > offset_free_cached + || offset_free_cached > p_size)) + return -EINVAL; + + return offset_free_cached - offset_free_uncached; } + shared = smem_ram_base; + heap_remaining = shared->heap_info.heap_remaining; + if (WARN_ON(heap_remaining > smem_ram_size)) + return -EINVAL; + + return heap_remaining; } EXPORT_SYMBOL(smem_get_free_space); @@ -1214,8 +1302,8 @@ static void smem_init_security_partition(struct smem_toc_entry *entry, LOG_ERR("Smem partition %d hdr magic is bad\n", num); BUG(); } - if (!hdr->size) { - LOG_ERR("Smem partition %d size is 0\n", num); + if (hdr->size != entry->size) { + LOG_ERR("Smem partition %d size is invalid\n", num); BUG(); } if (hdr->offset_free_uncached > hdr->size) { diff --git a/drivers/soc/qcom/smcinvoke.c b/drivers/soc/qcom/smcinvoke.c index f69ff47ae0f7..1d51970df961 100644 --- a/drivers/soc/qcom/smcinvoke.c +++ b/drivers/soc/qcom/smcinvoke.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017,2019 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 @@ -302,7 +302,7 @@ static int marshal_in(const struct smcinvoke_cmd_req *req, const union smcinvoke_arg *args_buf, uint32_t tzhandle, uint8_t *buf, size_t buf_size, struct file **arr_filp) { - int ret = -EINVAL, i = 0; + int ret = -EINVAL, i = 0, j = 0; union smcinvoke_tz_args *tz_args = NULL; struct smcinvoke_msg_hdr msg_hdr = {tzhandle, req->op, req->counts}; uint32_t offset = sizeof(struct smcinvoke_msg_hdr) + @@ -347,7 +347,7 @@ static int marshal_in(const struct smcinvoke_cmd_req *req, } FOR_ARGS(i, req->counts, OI) { if (get_tzhandle_from_fd(args_buf[i].o.fd, - &arr_filp[i], &(tz_args->tzhandle))) + &arr_filp[j++], &(tz_args->tzhandle))) goto out; tz_args++; } diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c index 19019aa092e8..2c77f9051a26 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2015, Sony Mobile Communications AB. - * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2013, 2019 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 @@ -228,7 +228,7 @@ struct smem_region { * struct qcom_smem - device data for the smem device * @dev: device pointer * @hwlock: reference to a hwspinlock - * @partitions: list of pointers to partitions affecting the current + * @ptable_entries: list of pointers to partitions table entry of current * processor/host * @num_regions: number of @regions * @regions: list of the memory regions defining the shared memory @@ -238,12 +238,24 @@ struct qcom_smem { struct hwspinlock *hwlock; - struct smem_partition_header *partitions[SMEM_HOST_COUNT]; + struct smem_ptable_entry *ptable_entries[SMEM_HOST_COUNT]; unsigned num_regions; struct smem_region regions[0]; }; +/* Pointer to the one and only smem handle */ +static struct qcom_smem *__smem; + +/* Timeout (ms) for the trylock of remote spinlocks */ +#define HWSPINLOCK_TIMEOUT 1000 + +static struct smem_partition_header * +ptable_entry_to_phdr(struct smem_ptable_entry *entry) +{ + return __smem->regions[0].virt_base + le32_to_cpu(entry->offset); +} + static struct smem_private_entry * phdr_to_last_private_entry(struct smem_partition_header *phdr) { @@ -283,32 +295,33 @@ static void *entry_to_item(struct smem_private_entry *e) return p + sizeof(*e) + le16_to_cpu(e->padding_hdr); } -/* Pointer to the one and only smem handle */ -static struct qcom_smem *__smem; - -/* Timeout (ms) for the trylock of remote spinlocks */ -#define HWSPINLOCK_TIMEOUT 1000 - static int qcom_smem_alloc_private(struct qcom_smem *smem, - unsigned host, + struct smem_ptable_entry *entry, unsigned item, size_t size) { struct smem_partition_header *phdr; struct smem_private_entry *hdr, *end; + struct smem_partition_header *phdr; size_t alloc_size; void *cached; + void *p_end; + + phdr = ptable_entry_to_phdr(entry); + p_end = (void *)phdr + le32_to_cpu(entry->size); - phdr = smem->partitions[host]; hdr = phdr_to_first_private_entry(phdr); end = phdr_to_last_private_entry(phdr); cached = phdr_to_first_cached_entry(phdr); + if (WARN_ON((void *)end > p_end || (void *)cached > p_end)) + return -EINVAL; + while (hdr < end) { if (hdr->canary != SMEM_PRIVATE_CANARY) { dev_err(smem->dev, - "Found invalid canary in host %d partition\n", - host); + "Found invalid canary in host %d:%d partition\n", + phdr->host0, phdr->host1); return -EINVAL; } @@ -317,6 +330,8 @@ static int qcom_smem_alloc_private(struct qcom_smem *smem, hdr = private_entry_next(hdr); } + if (WARN_ON((void *)hdr > p_end)) + return -EINVAL; /* Check that we don't grow into the cached region */ alloc_size = sizeof(*hdr) + ALIGN(size, 8); @@ -389,6 +404,7 @@ static int qcom_smem_alloc_global(struct qcom_smem *smem, */ int qcom_smem_alloc(unsigned host, unsigned item, size_t size) { + struct smem_ptable_entry *entry; unsigned long flags; int ret; @@ -407,10 +423,12 @@ int qcom_smem_alloc(unsigned host, unsigned item, size_t size) if (ret) return ret; - if (host < SMEM_HOST_COUNT && __smem->partitions[host]) - ret = qcom_smem_alloc_private(__smem, host, item, size); - else + if (host < SMEM_HOST_COUNT && __smem->ptable_entries[host]) { + entry = __smem->ptable_entries[host]; + ret = qcom_smem_alloc_private(__smem, entry, item, size); + } else { ret = qcom_smem_alloc_global(__smem, item, size); + } hwspin_unlock_irqrestore(__smem->hwlock, &flags); @@ -422,9 +440,11 @@ static void *qcom_smem_get_global(struct qcom_smem *smem, unsigned item, size_t *size) { + struct smem_global_entry *entry; struct smem_header *header; struct smem_region *area; - struct smem_global_entry *entry; + u64 entry_offset; + u32 e_size; u32 aux_base; unsigned i; @@ -442,9 +462,16 @@ static void *qcom_smem_get_global(struct qcom_smem *smem, area = &smem->regions[i]; if (area->aux_base == aux_base || !aux_base) { + e_size = le32_to_cpu(entry->size); + entry_offset = le32_to_cpu(entry->offset); + + if (WARN_ON(e_size + entry_offset > area->size)) + return ERR_PTR(-EINVAL); + if (size != NULL) - *size = le32_to_cpu(entry->size); - return area->virt_base + le32_to_cpu(entry->offset); + *size = e_size; + + return area->virt_base + entry_offset; } } @@ -452,35 +479,58 @@ static void *qcom_smem_get_global(struct qcom_smem *smem, } static void *qcom_smem_get_private(struct qcom_smem *smem, - unsigned host, + struct smem_ptable_entry *entry, unsigned item, size_t *size) { struct smem_partition_header *phdr; struct smem_private_entry *e, *end; + void *item_ptr, *p_end; + u32 partition_size; + u32 padding_data; + u32 e_size; + + phdr = ptable_entry_to_phdr(entry); + partition_size = le32_to_cpu(entry->size); + p_end = (void *)phdr + partition_size; - phdr = smem->partitions[host]; e = phdr_to_first_private_entry(phdr); end = phdr_to_last_private_entry(phdr); + if (WARN_ON((void *)end > p_end)) + return ERR_PTR(-EINVAL); + while (e < end) { if (e->canary != SMEM_PRIVATE_CANARY) { dev_err(smem->dev, - "Found invalid canary in host %d partition\n", - host); + "Found invalid canary in host %d:%d partition\n", + phdr->host0, phdr->host1); return ERR_PTR(-EINVAL); } if (le16_to_cpu(e->item) == item) { - if (size != NULL) - *size = le32_to_cpu(e->size) - - le16_to_cpu(e->padding_data); - - return entry_to_item(e); + if (size != NULL) { + e_size = le32_to_cpu(e->size); + padding_data = le16_to_cpu(e->padding_data); + + if (e_size < partition_size + && padding_data < e_size) + *size = e_size - padding_data; + else + return ERR_PTR(-EINVAL); + } + + item_ptr = entry_to_item(e); + if (WARN_ON(item_ptr > p_end)) + return ERR_PTR(-EINVAL); + + return item_ptr; } e = private_entry_next(e); } + if (WARN_ON((void *)e > p_end)) + return ERR_PTR(-EINVAL); return ERR_PTR(-ENOENT); } @@ -496,6 +546,7 @@ static void *qcom_smem_get_private(struct qcom_smem *smem, */ void *qcom_smem_get(unsigned host, unsigned item, size_t *size) { + struct smem_ptable_entry *entry; unsigned long flags; int ret; void *ptr = ERR_PTR(-EPROBE_DEFER); @@ -509,11 +560,12 @@ void *qcom_smem_get(unsigned host, unsigned item, size_t *size) if (ret) return ERR_PTR(ret); - if (host < SMEM_HOST_COUNT && __smem->partitions[host]) - ptr = qcom_smem_get_private(__smem, host, item, size); - else + if (host < SMEM_HOST_COUNT && __smem->ptable_entries[host]) { + entry = __smem->ptable_entries[host]; + ptr = qcom_smem_get_private(__smem, entry, item, size); + } else { ptr = qcom_smem_get_global(__smem, item, size); - + } hwspin_unlock_irqrestore(__smem->hwlock, &flags); return ptr; @@ -531,19 +583,28 @@ EXPORT_SYMBOL(qcom_smem_get); int qcom_smem_get_free_space(unsigned host) { struct smem_partition_header *phdr; + struct smem_ptable_entry *entry; struct smem_header *header; unsigned ret; if (!__smem) return -EPROBE_DEFER; - if (host < SMEM_HOST_COUNT && __smem->partitions[host]) { - phdr = __smem->partitions[host]; + if (host < SMEM_HOST_COUNT && __smem->ptable_entries[host]) { + entry = __smem->ptable_entries[host]; + phdr = ptable_entry_to_phdr(entry); + ret = le32_to_cpu(phdr->offset_free_cached) - le32_to_cpu(phdr->offset_free_uncached); + + if (ret > le32_to_cpu(entry->size)) + return -EINVAL; } else { header = __smem->regions[0].virt_base; ret = le32_to_cpu(header->available); + + if (ret > __smem->regions[0].size) + return -EINVAL; } return ret; @@ -616,7 +677,7 @@ static int qcom_smem_enumerate_partitions(struct qcom_smem *smem, return -EINVAL; } - if (smem->partitions[remote_host]) { + if (smem->ptable_entries[remote_host]) { dev_err(smem->dev, "Already found a partition for host %d\n", remote_host); @@ -658,7 +719,7 @@ static int qcom_smem_enumerate_partitions(struct qcom_smem *smem, return -EINVAL; } - smem->partitions[remote_host] = header; + smem->ptable_entries[remote_host] = entry; } return 0; diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 3cac73e4c3e4..193aa3da5033 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -859,10 +859,14 @@ static unsigned int ssp_get_clk_div(struct driver_data *drv_data, int rate) rate = min_t(int, ssp_clk, rate); + /* + * Calculate the divisor for the SCR (Serial Clock Rate), avoiding + * that the SSP transmission rate can be greater than the device rate + */ if (ssp->type == PXA25x_SSP || ssp->type == CE4100_SSP) - return (ssp_clk / (2 * rate) - 1) & 0xff; + return (DIV_ROUND_UP(ssp_clk, 2 * rate) - 1) & 0xff; else - return (ssp_clk / rate - 1) & 0xfff; + return (DIV_ROUND_UP(ssp_clk, rate) - 1) & 0xfff; } static unsigned int pxa2xx_ssp_get_clk_div(struct driver_data *drv_data, @@ -1367,12 +1371,7 @@ static const struct pci_device_id pxa2xx_spi_pci_compound_match[] = { static bool pxa2xx_spi_idma_filter(struct dma_chan *chan, void *param) { - struct device *dev = param; - - if (dev != chan->device->dev->parent) - return false; - - return true; + return param == chan->device->dev; } static struct pxa2xx_spi_master * diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 9882d93e7566..0556259377f7 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -279,7 +279,8 @@ static int rspi_set_config_register(struct rspi_data *rspi, int access_size) /* Sets parity, interrupt mask */ rspi_write8(rspi, 0x00, RSPI_SPCR2); - /* Sets SPCMD */ + /* Resets sequencer */ + rspi_write8(rspi, 0, RSPI_SPSCR); rspi->spcmd |= SPCMD_SPB_8_TO_16(access_size); rspi_write16(rspi, rspi->spcmd, RSPI_SPCMD0); @@ -313,7 +314,8 @@ static int rspi_rz_set_config_register(struct rspi_data *rspi, int access_size) rspi_write8(rspi, 0x00, RSPI_SSLND); rspi_write8(rspi, 0x00, RSPI_SPND); - /* Sets SPCMD */ + /* Resets sequencer */ + rspi_write8(rspi, 0, RSPI_SPSCR); rspi->spcmd |= SPCMD_SPB_8_TO_16(access_size); rspi_write16(rspi, rspi->spcmd, RSPI_SPCMD0); @@ -364,7 +366,8 @@ static int qspi_set_config_register(struct rspi_data *rspi, int access_size) /* Sets buffer to allow normal operation */ rspi_write8(rspi, 0x00, QSPI_SPBFCR); - /* Sets SPCMD */ + /* Resets sequencer */ + rspi_write8(rspi, 0, RSPI_SPSCR); rspi_write16(rspi, rspi->spcmd, RSPI_SPCMD0); /* Enables SPI function in master mode */ diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index 73779cecc3bb..705f515863d4 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -1067,27 +1067,19 @@ static int tegra_spi_probe(struct platform_device *pdev) spi_irq = platform_get_irq(pdev, 0); tspi->irq = spi_irq; - ret = request_threaded_irq(tspi->irq, tegra_spi_isr, - tegra_spi_isr_thread, IRQF_ONESHOT, - dev_name(&pdev->dev), tspi); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n", - tspi->irq); - goto exit_free_master; - } tspi->clk = devm_clk_get(&pdev->dev, "spi"); if (IS_ERR(tspi->clk)) { dev_err(&pdev->dev, "can not get clock\n"); ret = PTR_ERR(tspi->clk); - goto exit_free_irq; + goto exit_free_master; } tspi->rst = devm_reset_control_get(&pdev->dev, "spi"); if (IS_ERR(tspi->rst)) { dev_err(&pdev->dev, "can not get reset\n"); ret = PTR_ERR(tspi->rst); - goto exit_free_irq; + goto exit_free_master; } tspi->max_buf_size = SPI_FIFO_DEPTH << 2; @@ -1095,7 +1087,7 @@ static int tegra_spi_probe(struct platform_device *pdev) ret = tegra_spi_init_dma_param(tspi, true); if (ret < 0) - goto exit_free_irq; + goto exit_free_master; ret = tegra_spi_init_dma_param(tspi, false); if (ret < 0) goto exit_rx_dma_free; @@ -1117,18 +1109,32 @@ static int tegra_spi_probe(struct platform_device *pdev) dev_err(&pdev->dev, "pm runtime get failed, e = %d\n", ret); goto exit_pm_disable; } + + reset_control_assert(tspi->rst); + udelay(2); + reset_control_deassert(tspi->rst); tspi->def_command1_reg = SPI_M_S; tegra_spi_writel(tspi, tspi->def_command1_reg, SPI_COMMAND1); pm_runtime_put(&pdev->dev); + ret = request_threaded_irq(tspi->irq, tegra_spi_isr, + tegra_spi_isr_thread, IRQF_ONESHOT, + dev_name(&pdev->dev), tspi); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n", + tspi->irq); + goto exit_pm_disable; + } master->dev.of_node = pdev->dev.of_node; ret = devm_spi_register_master(&pdev->dev, master); if (ret < 0) { dev_err(&pdev->dev, "can not register to master err %d\n", ret); - goto exit_pm_disable; + goto exit_free_irq; } return ret; +exit_free_irq: + free_irq(spi_irq, tspi); exit_pm_disable: pm_runtime_disable(&pdev->dev); if (!pm_runtime_status_suspended(&pdev->dev)) @@ -1136,8 +1142,6 @@ exit_pm_disable: tegra_spi_deinit_dma_param(tspi, false); exit_rx_dma_free: tegra_spi_deinit_dma_param(tspi, true); -exit_free_irq: - free_irq(spi_irq, tspi); exit_free_master: spi_master_put(master); return ret; diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c index 93dfcee0f987..9f30a4ab2004 100644 --- a/drivers/spi/spi-topcliff-pch.c +++ b/drivers/spi/spi-topcliff-pch.c @@ -1326,18 +1326,27 @@ static void pch_free_dma_buf(struct pch_spi_board_data *board_dat, return; } -static void pch_alloc_dma_buf(struct pch_spi_board_data *board_dat, +static int pch_alloc_dma_buf(struct pch_spi_board_data *board_dat, struct pch_spi_data *data) { struct pch_spi_dma_ctrl *dma; + int ret; dma = &data->dma; + ret = 0; /* Get Consistent memory for Tx DMA */ dma->tx_buf_virt = dma_alloc_coherent(&board_dat->pdev->dev, PCH_BUF_SIZE, &dma->tx_buf_dma, GFP_KERNEL); + if (!dma->tx_buf_virt) + ret = -ENOMEM; + /* Get Consistent memory for Rx DMA */ dma->rx_buf_virt = dma_alloc_coherent(&board_dat->pdev->dev, PCH_BUF_SIZE, &dma->rx_buf_dma, GFP_KERNEL); + if (!dma->rx_buf_virt) + ret = -ENOMEM; + + return ret; } static int pch_spi_pd_probe(struct platform_device *plat_dev) @@ -1414,7 +1423,9 @@ static int pch_spi_pd_probe(struct platform_device *plat_dev) if (use_dma) { dev_info(&plat_dev->dev, "Use DMA for data transfers\n"); - pch_alloc_dma_buf(board_dat, data); + ret = pch_alloc_dma_buf(board_dat, data); + if (ret) + goto err_spi_register_master; } ret = spi_register_master(master); diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 63446500d49c..5c7fd1571a01 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -903,6 +903,8 @@ static int spi_map_msg(struct spi_master *master, struct spi_message *msg) if (max_tx || max_rx) { list_for_each_entry(xfer, &msg->transfers, transfer_list) { + if (!xfer->len) + continue; if (!xfer->tx_buf) xfer->tx_buf = master->dummy_tx; if (!xfer->rx_buf) diff --git a/drivers/ssb/bridge_pcmcia_80211.c b/drivers/ssb/bridge_pcmcia_80211.c index d70568ea02d5..2ff7d90e166a 100644 --- a/drivers/ssb/bridge_pcmcia_80211.c +++ b/drivers/ssb/bridge_pcmcia_80211.c @@ -113,16 +113,21 @@ static struct pcmcia_driver ssb_host_pcmcia_driver = { .resume = ssb_host_pcmcia_resume, }; +static int pcmcia_init_failed; + /* * These are not module init/exit functions! * The module_pcmcia_driver() helper cannot be used here. */ int ssb_host_pcmcia_init(void) { - return pcmcia_register_driver(&ssb_host_pcmcia_driver); + pcmcia_init_failed = pcmcia_register_driver(&ssb_host_pcmcia_driver); + + return pcmcia_init_failed; } void ssb_host_pcmcia_exit(void) { - pcmcia_unregister_driver(&ssb_host_pcmcia_driver); + if (!pcmcia_init_failed) + pcmcia_unregister_driver(&ssb_host_pcmcia_driver); } diff --git a/drivers/staging/iio/addac/adt7316.c b/drivers/staging/iio/addac/adt7316.c index 3adc4516918c..8c5cfb9400d0 100644 --- a/drivers/staging/iio/addac/adt7316.c +++ b/drivers/staging/iio/addac/adt7316.c @@ -47,6 +47,8 @@ #define ADT7516_MSB_AIN3 0xA #define ADT7516_MSB_AIN4 0xB #define ADT7316_DA_DATA_BASE 0x10 +#define ADT7316_DA_10_BIT_LSB_SHIFT 6 +#define ADT7316_DA_12_BIT_LSB_SHIFT 4 #define ADT7316_DA_MSB_DATA_REGS 4 #define ADT7316_LSB_DAC_A 0x10 #define ADT7316_MSB_DAC_A 0x11 @@ -1092,7 +1094,7 @@ static ssize_t adt7316_store_DAC_internal_Vref(struct device *dev, ldac_config = chip->ldac_config & (~ADT7516_DAC_IN_VREF_MASK); if (data & 0x1) ldac_config |= ADT7516_DAC_AB_IN_VREF; - else if (data & 0x2) + if (data & 0x2) ldac_config |= ADT7516_DAC_CD_IN_VREF; } else { ret = kstrtou8(buf, 16, &data); @@ -1414,7 +1416,7 @@ static IIO_DEVICE_ATTR(ex_analog_temp_offset, S_IRUGO | S_IWUSR, static ssize_t adt7316_show_DAC(struct adt7316_chip_info *chip, int channel, char *buf) { - u16 data; + u16 data = 0; u8 msb, lsb, offset; int ret; @@ -1439,7 +1441,11 @@ static ssize_t adt7316_show_DAC(struct adt7316_chip_info *chip, if (ret) return -EIO; - data = (msb << offset) + (lsb & ((1 << offset) - 1)); + if (chip->dac_bits == 12) + data = lsb >> ADT7316_DA_12_BIT_LSB_SHIFT; + else if (chip->dac_bits == 10) + data = lsb >> ADT7316_DA_10_BIT_LSB_SHIFT; + data |= msb << offset; return sprintf(buf, "%d\n", data); } @@ -1447,7 +1453,7 @@ static ssize_t adt7316_show_DAC(struct adt7316_chip_info *chip, static ssize_t adt7316_store_DAC(struct adt7316_chip_info *chip, int channel, const char *buf, size_t len) { - u8 msb, lsb, offset; + u8 msb, lsb, lsb_reg, offset; u16 data; int ret; @@ -1465,9 +1471,13 @@ static ssize_t adt7316_store_DAC(struct adt7316_chip_info *chip, return -EINVAL; if (chip->dac_bits > 8) { - lsb = data & (1 << offset); + lsb = data & ((1 << offset) - 1); + if (chip->dac_bits == 12) + lsb_reg = lsb << ADT7316_DA_12_BIT_LSB_SHIFT; + else + lsb_reg = lsb << ADT7316_DA_10_BIT_LSB_SHIFT; ret = chip->bus.write(chip->bus.client, - ADT7316_DA_DATA_BASE + channel * 2, lsb); + ADT7316_DA_DATA_BASE + channel * 2, lsb_reg); if (ret) return -EIO; } diff --git a/drivers/staging/iio/magnetometer/hmc5843_i2c.c b/drivers/staging/iio/magnetometer/hmc5843_i2c.c index 3e06ceb32059..676a8e329eeb 100644 --- a/drivers/staging/iio/magnetometer/hmc5843_i2c.c +++ b/drivers/staging/iio/magnetometer/hmc5843_i2c.c @@ -59,8 +59,13 @@ static const struct regmap_config hmc5843_i2c_regmap_config = { static int hmc5843_i2c_probe(struct i2c_client *cli, const struct i2c_device_id *id) { + struct regmap *regmap = devm_regmap_init_i2c(cli, + &hmc5843_i2c_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + return hmc5843_common_probe(&cli->dev, - devm_regmap_init_i2c(cli, &hmc5843_i2c_regmap_config), + regmap, id->driver_data, id->name); } diff --git a/drivers/staging/iio/magnetometer/hmc5843_spi.c b/drivers/staging/iio/magnetometer/hmc5843_spi.c index 8be198058ea2..fded442a3c1d 100644 --- a/drivers/staging/iio/magnetometer/hmc5843_spi.c +++ b/drivers/staging/iio/magnetometer/hmc5843_spi.c @@ -59,6 +59,7 @@ static const struct regmap_config hmc5843_spi_regmap_config = { static int hmc5843_spi_probe(struct spi_device *spi) { int ret; + struct regmap *regmap; const struct spi_device_id *id = spi_get_device_id(spi); spi->mode = SPI_MODE_3; @@ -68,8 +69,12 @@ static int hmc5843_spi_probe(struct spi_device *spi) if (ret) return ret; + regmap = devm_regmap_init_spi(spi, &hmc5843_spi_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + return hmc5843_common_probe(&spi->dev, - devm_regmap_init_spi(spi, &hmc5843_spi_regmap_config), + regmap, id->driver_data, id->name); } diff --git a/drivers/thermal/msm_lmh_dcvs.c b/drivers/thermal/msm_lmh_dcvs.c index 215c37526081..7e141f15a544 100644 --- a/drivers/thermal/msm_lmh_dcvs.c +++ b/drivers/thermal/msm_lmh_dcvs.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, 2019 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 @@ -54,7 +54,7 @@ #define MSM_LIMITS_CLUSTER_0 0x6370302D #define MSM_LIMITS_CLUSTER_1 0x6370312D -#define MSM_LIMITS_DOMAIN_MAX 0x444D4158 +#define MSM_LIMIT_FREQ_CAP 0x46434150 #define MSM_LIMITS_HIGH_THRESHOLD_VAL 95000 #define MSM_LIMITS_ARM_THRESHOLD_VAL 65000 @@ -194,34 +194,40 @@ static irqreturn_t lmh_dcvs_handle_isr(int irq, void *data) } static int msm_lmh_dcvs_write(uint32_t node_id, uint32_t fn, - uint32_t setting, uint32_t val) + uint32_t setting, uint32_t val, uint32_t val1, + bool enable_val1) { int ret; struct scm_desc desc_arg; uint32_t *payload = NULL; + uint32_t payload_len; - payload = kzalloc(sizeof(uint32_t) * 5, GFP_KERNEL); + payload_len = ((enable_val1) ? 6 : 5) * sizeof(uint32_t); + payload = kcalloc((enable_val1) ? 6 : 5, sizeof(uint32_t), GFP_KERNEL); if (!payload) return -ENOMEM; payload[0] = fn; /* algorithm */ payload[1] = 0; /* unused sub-algorithm */ payload[2] = setting; - payload[3] = 1; /* number of values */ + payload[3] = enable_val1 ? 2 : 1; /* number of values */ payload[4] = val; + if (enable_val1) + payload[5] = val1; desc_arg.args[0] = SCM_BUFFER_PHYS(payload); - desc_arg.args[1] = sizeof(uint32_t) * 5; + desc_arg.args[1] = payload_len; desc_arg.args[2] = MSM_LIMITS_NODE_DCVS; desc_arg.args[3] = node_id; desc_arg.args[4] = 0; /* version */ desc_arg.arginfo = SCM_ARGS(5, SCM_RO, SCM_VAL, SCM_VAL, SCM_VAL, SCM_VAL); - dmac_flush_range(payload, (void *)payload + 5 * (sizeof(uint32_t))); + dmac_flush_range(payload, (void *)payload + payload_len); ret = scm_call2(SCM_SIP_FNID(SCM_SVC_LMH, MSM_LIMITS_DCVSH), &desc_arg); kfree(payload); + return ret; } @@ -265,7 +271,7 @@ static int lmh_activate_trip(struct thermal_zone_device *dev, case LIMITS_TRIP_LO: ret = msm_lmh_dcvs_write(hw->affinity, MSM_LIMITS_SUB_FN_THERMAL, - MSM_LIMITS_ARM_THRESHOLD, temp); + MSM_LIMITS_ARM_THRESHOLD, temp, 0, 0); break; case LIMITS_TRIP_HI: /* @@ -276,13 +282,13 @@ static int lmh_activate_trip(struct thermal_zone_device *dev, return -EINVAL; ret = msm_lmh_dcvs_write(hw->affinity, MSM_LIMITS_SUB_FN_THERMAL, - MSM_LIMITS_HI_THRESHOLD, temp); + MSM_LIMITS_HI_THRESHOLD, temp, 0, 0); if (ret) break; ret = msm_lmh_dcvs_write(hw->affinity, MSM_LIMITS_SUB_FN_THERMAL, MSM_LIMITS_LOW_THRESHOLD, temp - - MSM_LIMITS_LOW_THRESHOLD_OFFSET); + MSM_LIMITS_LOW_THRESHOLD_OFFSET, 0, 0); break; default: return -EINVAL; @@ -347,8 +353,9 @@ static int lmh_set_max_limit(int cpu, u32 freq) if (!hw) return -EINVAL; - return msm_lmh_dcvs_write(hw->affinity, MSM_LIMITS_SUB_FN_GENERAL, - MSM_LIMITS_DOMAIN_MAX, freq); + return msm_lmh_dcvs_write(hw->affinity, MSM_LIMITS_SUB_FN_THERMAL, + MSM_LIMIT_FREQ_CAP, freq, + freq >= hw->max_freq ? 0 : 1, 1); } static int lmh_get_cur_limit(int cpu, unsigned long *freq) @@ -457,7 +464,7 @@ static int msm_lmh_dcvs_probe(struct platform_device *pdev) /* Enable the thermal algorithm early */ ret = msm_lmh_dcvs_write(hw->affinity, MSM_LIMITS_SUB_FN_THERMAL, - MSM_LIMITS_ALGO_MODE_ENABLE, 1); + MSM_LIMITS_ALGO_MODE_ENABLE, 1, 0, 0); if (ret) return ret; diff --git a/drivers/thermal/msm_thermal.c b/drivers/thermal/msm_thermal.c index 7beef24035f8..389a75695355 100644 --- a/drivers/thermal/msm_thermal.c +++ b/drivers/thermal/msm_thermal.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2019, 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 @@ -59,11 +59,11 @@ #define MSM_LIMITS_DCVSH 0x10 #define MSM_LIMITS_NODE_DCVS 0x44435653 +#define MSM_LIMITS_SUB_FN_THERMAL 0x54484D4C #define MSM_LIMITS_SUB_FN_GENERAL 0x47454E00 #define MSM_LIMITS_SUB_FN_CRNT 0x43524E54 #define MSM_LIMITS_SUB_FN_REL 0x52454C00 -#define MSM_LIMITS_DOMAIN_MAX 0x444D4158 -#define MSM_LIMITS_DOMAIN_MIN 0x444D494E +#define MSM_LIMITS_FREQ_CAP 0x46434150 #define MSM_LIMITS_CLUSTER_0 0x6370302D #define MSM_LIMITS_CLUSTER_1 0x6370312D #define MSM_LIMITS_ALGO_MODE_ENABLE 0x454E424C @@ -1018,55 +1018,58 @@ static struct notifier_block msm_thermal_cpufreq_notifier = { .notifier_call = msm_thermal_cpufreq_callback, }; -static int msm_lmh_dcvs_write(uint32_t node_id, uint32_t fn, uint32_t setting, - uint32_t val) +static int msm_lmh_dcvs_write(uint32_t node_id, uint32_t fn, + uint32_t setting, uint32_t val, uint32_t val1, + bool enable_val1) { int ret; struct scm_desc desc_arg; uint32_t *payload = NULL; + uint32_t payload_len; - payload = kzalloc(sizeof(uint32_t) * 5, GFP_KERNEL); + payload_len = ((enable_val1) ? 6 : 5) * sizeof(uint32_t); + payload = kcalloc((enable_val1) ? 6 : 5, sizeof(uint32_t), GFP_KERNEL); if (!payload) return -ENOMEM; - payload[0] = fn; + payload[0] = fn; /* algorithm */ payload[1] = 0; /* unused sub-algorithm */ payload[2] = setting; - payload[3] = 1; /* number of values */ + payload[3] = enable_val1 ? 2 : 1; /* number of values */ payload[4] = val; + if (enable_val1) + payload[5] = val1; desc_arg.args[0] = SCM_BUFFER_PHYS(payload); - desc_arg.args[1] = sizeof(uint32_t) * 5; + desc_arg.args[1] = payload_len; desc_arg.args[2] = MSM_LIMITS_NODE_DCVS; desc_arg.args[3] = node_id; desc_arg.args[4] = 0; /* version */ desc_arg.arginfo = SCM_ARGS(5, SCM_RO, SCM_VAL, SCM_VAL, SCM_VAL, SCM_VAL); - dmac_flush_range(payload, (void *)payload + 5 * (sizeof(uint32_t))); + dmac_flush_range(payload, (void *)payload + payload_len); ret = scm_call2(SCM_SIP_FNID(SCM_SVC_LMH, MSM_LIMITS_DCVSH), &desc_arg); kfree(payload); + return ret; } static int msm_lmh_dcvs_update(int cpu) { uint32_t id = cpus[cpu].parent_ptr->cluster_id; - uint32_t max_freq = cpus[cpu].limited_max_freq; - uint32_t min_freq = cpus[cpu].limited_min_freq; + uint32_t max_freq = cpus[cpu].limited_max_freq, hw_max_freq = U32_MAX; uint32_t affinity; int ret; /* - * It is better to use max/min limits of cluster for given + * It is better to use max limits of cluster for given * cpu if cluster mitigation is supported. It ensures that it - * requests aggregated max/min limits of all cpus in that cluster. + * requests aggregated max limits of all cpus in that cluster. */ - if (core_ptr) { + if (core_ptr) max_freq = cpus[cpu].parent_ptr->limited_max_freq; - min_freq = cpus[cpu].parent_ptr->limited_min_freq; - } switch (id) { case 0: @@ -1080,13 +1083,14 @@ static int msm_lmh_dcvs_update(int cpu) return -EINVAL; }; - ret = msm_lmh_dcvs_write(affinity, MSM_LIMITS_SUB_FN_GENERAL, - MSM_LIMITS_DOMAIN_MAX, max_freq); - if (ret) - return ret; + if (cpus[cpu].parent_ptr->freq_table) + hw_max_freq = + cpus[cpu].parent_ptr->freq_table[ + cpus[cpu].parent_ptr->freq_idx_high].frequency; - ret = msm_lmh_dcvs_write(affinity, MSM_LIMITS_SUB_FN_GENERAL, - MSM_LIMITS_DOMAIN_MIN, min_freq); + ret = msm_lmh_dcvs_write(affinity, MSM_LIMITS_SUB_FN_THERMAL, + MSM_LIMITS_FREQ_CAP, max_freq, + max_freq >= hw_max_freq ? 0 : 1, 1); if (ret) return ret; /* @@ -1729,23 +1733,23 @@ static int msm_thermal_lmh_dcvs_init(struct platform_device *pdev) */ ret = msm_lmh_dcvs_write(MSM_LIMITS_CLUSTER_0, MSM_LIMITS_SUB_FN_REL, - MSM_LIMITS_ALGO_MODE_ENABLE, 1); + MSM_LIMITS_ALGO_MODE_ENABLE, 1, 0, 0); if (ret) pr_err("Unable to enable REL algo for cluster0\n"); ret = msm_lmh_dcvs_write(MSM_LIMITS_CLUSTER_1, MSM_LIMITS_SUB_FN_REL, - MSM_LIMITS_ALGO_MODE_ENABLE, 1); + MSM_LIMITS_ALGO_MODE_ENABLE, 1, 0, 0); if (ret) pr_err("Unable to enable REL algo for cluster1\n"); ret = msm_lmh_dcvs_write(MSM_LIMITS_CLUSTER_0, MSM_LIMITS_SUB_FN_CRNT, - MSM_LIMITS_ALGO_MODE_ENABLE, 1); + MSM_LIMITS_ALGO_MODE_ENABLE, 1, 0, 0); if (ret) pr_err("Unable enable CRNT algo for cluster0\n"); ret = msm_lmh_dcvs_write(MSM_LIMITS_CLUSTER_1, MSM_LIMITS_SUB_FN_CRNT, - MSM_LIMITS_ALGO_MODE_ENABLE, 1); + MSM_LIMITS_ALGO_MODE_ENABLE, 1, 0, 0); if (ret) pr_err("Unable enable CRNT algo for cluster1\n"); diff --git a/drivers/tty/ipwireless/main.c b/drivers/tty/ipwireless/main.c index 655c7948261c..2fa4f9123469 100644 --- a/drivers/tty/ipwireless/main.c +++ b/drivers/tty/ipwireless/main.c @@ -113,6 +113,10 @@ static int ipwireless_probe(struct pcmcia_device *p_dev, void *priv_data) ipw->common_memory = ioremap(p_dev->resource[2]->start, resource_size(p_dev->resource[2])); + if (!ipw->common_memory) { + ret = -ENOMEM; + goto exit1; + } if (!request_mem_region(p_dev->resource[2]->start, resource_size(p_dev->resource[2]), IPWIRELESS_PCCARD_NAME)) { @@ -133,6 +137,10 @@ static int ipwireless_probe(struct pcmcia_device *p_dev, void *priv_data) ipw->attr_memory = ioremap(p_dev->resource[3]->start, resource_size(p_dev->resource[3])); + if (!ipw->attr_memory) { + ret = -ENOMEM; + goto exit3; + } if (!request_mem_region(p_dev->resource[3]->start, resource_size(p_dev->resource[3]), IPWIRELESS_PCCARD_NAME)) { diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index a30d68c4b689..039837db65fc 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -258,7 +258,7 @@ static bool dw8250_fallback_dma_filter(struct dma_chan *chan, void *param) static bool dw8250_idma_filter(struct dma_chan *chan, void *param) { - return param == chan->device->dev->parent; + return param == chan->device->dev; } static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data) @@ -290,7 +290,7 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data) data->uart_16550_compatible = true; } - /* Platforms with iDMA */ + /* Platforms with iDMA 64-bit */ if (platform_get_resource_byname(to_platform_device(p->dev), IORESOURCE_MEM, "lpss_priv")) { p->set_termios = dw8250_set_termios; diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index be55fb6def89..0ac0c618954e 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -571,7 +571,7 @@ static int max310x_set_ref_clk(struct max310x_port *s, unsigned long freq, } /* Configure clock source */ - clksrc = xtal ? MAX310X_CLKSRC_CRYST_BIT : MAX310X_CLKSRC_EXTCLK_BIT; + clksrc = MAX310X_CLKSRC_EXTCLK_BIT | (xtal ? MAX310X_CLKSRC_CRYST_BIT : 0); /* Configure PLL */ if (pllcfg) { diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index efaac5eb6592..eb66f7a68dc4 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -872,6 +872,7 @@ static void msm_handle_tx(struct uart_port *port) struct circ_buf *xmit = &msm_port->uart.state->xmit; struct msm_dma *dma = &msm_port->tx_dma; unsigned int pio_count, dma_count, dma_min; + char buf[4] = { 0 }; void __iomem *tf; int err = 0; @@ -881,10 +882,12 @@ static void msm_handle_tx(struct uart_port *port) else tf = port->membase + UART_TF; + buf[0] = port->x_char; + if (msm_port->is_uartdm) msm_reset_dm_count(port, 1); - iowrite8_rep(tf, &port->x_char, 1); + iowrite32_rep(tf, buf, 1); port->icount.tx++; port->x_char = 0; return; diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index 064494366f01..9a9ea7c62db1 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -3,7 +3,7 @@ * MSM 7k High speed uart driver * * Copyright (c) 2008 Google Inc. - * Copyright (c) 2007-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2007-2019, The Linux Foundation. All rights reserved. * Modified: Nick Pelly <npelly@google.com> * * All source code in this file is licensed under the following license @@ -1185,6 +1185,7 @@ static void msm_hs_set_termios(struct uart_port *uport, data |= EIGHT_BPC; break; } + uport->status &= ~(UPSTAT_AUTOCTS); /* stop bits */ if (c_cflag & CSTOPB) { data |= STOP_BIT_TWO; @@ -1229,6 +1230,7 @@ static void msm_hs_set_termios(struct uart_port *uport, if (c_cflag & CRTSCTS) { data |= UARTDM_MR1_CTS_CTL_BMSK; data |= UARTDM_MR1_RX_RDY_CTL_BMSK; + uport->status |= UPSTAT_AUTOCTS; msm_uport->flow_control = true; } msm_hs_write(uport, UART_DM_MR1, data); diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 17a22073d226..032f3c13b8c4 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -1448,7 +1448,7 @@ static int __init sc16is7xx_init(void) ret = i2c_add_driver(&sc16is7xx_i2c_uart_driver); if (ret < 0) { pr_err("failed to init sc16is7xx i2c --> %d\n", ret); - return ret; + goto err_i2c; } #endif @@ -1456,10 +1456,18 @@ static int __init sc16is7xx_init(void) ret = spi_register_driver(&sc16is7xx_spi_uart_driver); if (ret < 0) { pr_err("failed to init sc16is7xx spi --> %d\n", ret); - return ret; + goto err_spi; } #endif return ret; + +err_spi: +#ifdef CONFIG_SERIAL_SC16IS7XX_I2C + i2c_del_driver(&sc16is7xx_i2c_uart_driver); +#endif +err_i2c: + uart_unregister_driver(&sc16is7xx_uart); + return ret; } module_init(sc16is7xx_init); diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c index 59828d819145..5ad978acd90c 100644 --- a/drivers/tty/serial/sunhv.c +++ b/drivers/tty/serial/sunhv.c @@ -392,7 +392,7 @@ static struct uart_ops sunhv_pops = { static struct uart_driver sunhv_reg = { .owner = THIS_MODULE, .driver_name = "sunhv", - .dev_name = "ttyS", + .dev_name = "ttyHV", .major = TTY_MAJOR, }; diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index 988c564b61a8..fd92c842504d 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -121,6 +121,7 @@ static const int NR_TYPES = ARRAY_SIZE(max_vals); static struct input_handler kbd_handler; static DEFINE_SPINLOCK(kbd_event_lock); static DEFINE_SPINLOCK(led_lock); +static DEFINE_SPINLOCK(func_buf_lock); /* guard 'func_buf' and friends */ static unsigned long key_down[BITS_TO_LONGS(KEY_CNT)]; /* keyboard key bitmap */ static unsigned char shift_down[NR_SHIFT]; /* shift state counters.. */ static bool dead_key_next; @@ -1969,11 +1970,12 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm) char *p; u_char *q; u_char __user *up; - int sz; + int sz, fnw_sz; int delta; char *first_free, *fj, *fnw; int i, j, k; int ret; + unsigned long flags; if (!capable(CAP_SYS_TTY_CONFIG)) perm = 0; @@ -2016,7 +2018,14 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm) goto reterr; } + fnw = NULL; + fnw_sz = 0; + /* race aginst other writers */ + again: + spin_lock_irqsave(&func_buf_lock, flags); q = func_table[i]; + + /* fj pointer to next entry after 'q' */ first_free = funcbufptr + (funcbufsize - funcbufleft); for (j = i+1; j < MAX_NR_FUNC && !func_table[j]; j++) ; @@ -2024,10 +2033,12 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm) fj = func_table[j]; else fj = first_free; - + /* buffer usage increase by new entry */ delta = (q ? -strlen(q) : 1) + strlen(kbs->kb_string); + if (delta <= funcbufleft) { /* it fits in current buf */ if (j < MAX_NR_FUNC) { + /* make enough space for new entry at 'fj' */ memmove(fj + delta, fj, first_free - fj); for (k = j; k < MAX_NR_FUNC; k++) if (func_table[k]) @@ -2040,20 +2051,28 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm) sz = 256; while (sz < funcbufsize - funcbufleft + delta) sz <<= 1; - fnw = kmalloc(sz, GFP_KERNEL); - if(!fnw) { - ret = -ENOMEM; - goto reterr; + if (fnw_sz != sz) { + spin_unlock_irqrestore(&func_buf_lock, flags); + kfree(fnw); + fnw = kmalloc(sz, GFP_KERNEL); + fnw_sz = sz; + if (!fnw) { + ret = -ENOMEM; + goto reterr; + } + goto again; } if (!q) func_table[i] = fj; + /* copy data before insertion point to new location */ if (fj > funcbufptr) memmove(fnw, funcbufptr, fj - funcbufptr); for (k = 0; k < j; k++) if (func_table[k]) func_table[k] = fnw + (func_table[k] - funcbufptr); + /* copy data after insertion point to new location */ if (first_free > fj) { memmove(fnw + (fj - funcbufptr) + delta, fj, first_free - fj); for (k = j; k < MAX_NR_FUNC; k++) @@ -2066,7 +2085,9 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm) funcbufleft = funcbufleft - delta + sz - funcbufsize; funcbufsize = sz; } + /* finally insert item itself */ strcpy(func_table[i], kbs->kb_string); + spin_unlock_irqrestore(&func_buf_lock, flags); break; } ret = 0; diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index d551194a6ad6..421f1d3fbdb8 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -905,8 +905,8 @@ int usb_get_bos_descriptor(struct usb_device *dev) /* Get BOS descriptor */ ret = usb_get_descriptor(dev, USB_DT_BOS, 0, bos, USB_DT_BOS_SIZE); - if (ret < USB_DT_BOS_SIZE) { - dev_err(ddev, "unable to get BOS descriptor\n"); + if (ret < USB_DT_BOS_SIZE || bos->bLength < USB_DT_BOS_SIZE) { + dev_err(ddev, "unable to get BOS descriptor or descriptor too short\n"); if (ret >= 0) ret = -ENOMSG; kfree(bos); diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 7dae981b66b1..8089e5820be4 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -470,11 +470,6 @@ static int usb_unbind_interface(struct device *dev) pm_runtime_disable(dev); pm_runtime_set_suspended(dev); - /* Undo any residual pm_autopm_get_interface_* calls */ - for (r = atomic_read(&intf->pm_usage_cnt); r > 0; --r) - usb_autopm_put_interface_no_suspend(intf); - atomic_set(&intf->pm_usage_cnt, 0); - if (!error) usb_autosuspend_device(udev); @@ -1638,7 +1633,6 @@ void usb_autopm_put_interface(struct usb_interface *intf) int status; usb_mark_last_busy(udev); - atomic_dec(&intf->pm_usage_cnt); status = pm_runtime_put_sync(&intf->dev); dev_vdbg(&intf->dev, "%s: cnt %d -> %d\n", __func__, atomic_read(&intf->dev.power.usage_count), @@ -1667,7 +1661,6 @@ void usb_autopm_put_interface_async(struct usb_interface *intf) int status; usb_mark_last_busy(udev); - atomic_dec(&intf->pm_usage_cnt); status = pm_runtime_put(&intf->dev); dev_vdbg(&intf->dev, "%s: cnt %d -> %d\n", __func__, atomic_read(&intf->dev.power.usage_count), @@ -1689,7 +1682,6 @@ void usb_autopm_put_interface_no_suspend(struct usb_interface *intf) struct usb_device *udev = interface_to_usbdev(intf); usb_mark_last_busy(udev); - atomic_dec(&intf->pm_usage_cnt); pm_runtime_put_noidle(&intf->dev); } EXPORT_SYMBOL_GPL(usb_autopm_put_interface_no_suspend); @@ -1720,8 +1712,6 @@ int usb_autopm_get_interface(struct usb_interface *intf) status = pm_runtime_get_sync(&intf->dev); if (status < 0) pm_runtime_put_sync(&intf->dev); - else - atomic_inc(&intf->pm_usage_cnt); dev_vdbg(&intf->dev, "%s: cnt %d -> %d\n", __func__, atomic_read(&intf->dev.power.usage_count), status); @@ -1755,8 +1745,6 @@ int usb_autopm_get_interface_async(struct usb_interface *intf) status = pm_runtime_get(&intf->dev); if (status < 0 && status != -EINPROGRESS) pm_runtime_put_noidle(&intf->dev); - else - atomic_inc(&intf->pm_usage_cnt); dev_vdbg(&intf->dev, "%s: cnt %d -> %d\n", __func__, atomic_read(&intf->dev.power.usage_count), status); @@ -1780,7 +1768,6 @@ void usb_autopm_get_interface_no_resume(struct usb_interface *intf) struct usb_device *udev = interface_to_usbdev(intf); usb_mark_last_busy(udev); - atomic_inc(&intf->pm_usage_cnt); pm_runtime_get_noresume(&intf->dev); } EXPORT_SYMBOL_GPL(usb_autopm_get_interface_no_resume); @@ -1901,14 +1888,11 @@ int usb_runtime_idle(struct device *dev) return -EBUSY; } -int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable) +static int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable) { struct usb_hcd *hcd = bus_to_hcd(udev->bus); int ret = -EPERM; - if (enable && !udev->usb2_hw_lpm_allowed) - return 0; - if (hcd->driver->set_usb2_hw_lpm) { ret = hcd->driver->set_usb2_hw_lpm(hcd, udev, enable); if (!ret) @@ -1918,6 +1902,24 @@ int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable) return ret; } +int usb_enable_usb2_hardware_lpm(struct usb_device *udev) +{ + if (!udev->usb2_hw_lpm_capable || + !udev->usb2_hw_lpm_allowed || + udev->usb2_hw_lpm_enabled) + return 0; + + return usb_set_usb2_hardware_lpm(udev, 1); +} + +int usb_disable_usb2_hardware_lpm(struct usb_device *udev) +{ + if (!udev->usb2_hw_lpm_enabled) + return 0; + + return usb_set_usb2_hardware_lpm(udev, 0); +} + #endif /* CONFIG_PM */ struct bus_type usb_bus_type = { diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 8d732e9f74fa..4740c307e02d 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -3068,6 +3068,9 @@ usb_hcd_platform_shutdown(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); + /* No need for pm_runtime_put(), we're shutting down */ + pm_runtime_get_sync(&dev->dev); + if (hcd->driver->shutdown) hcd->driver->shutdown(hcd); } diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 4c1cfbf23487..9d46cb7cb21d 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3127,8 +3127,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) } /* disable USB2 hardware LPM */ - if (udev->usb2_hw_lpm_enabled == 1) - usb_set_usb2_hardware_lpm(udev, 0); + usb_disable_usb2_hardware_lpm(udev); if (usb_disable_ltm(udev)) { dev_err(&udev->dev, "Failed to disable LTM before suspend\n."); @@ -3174,8 +3173,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) usb_enable_ltm(udev); err_ltm: /* Try to enable USB2 hardware LPM again */ - if (udev->usb2_hw_lpm_capable == 1) - usb_set_usb2_hardware_lpm(udev, 1); + usb_enable_usb2_hardware_lpm(udev); if (udev->do_remote_wakeup) (void) usb_disable_remote_wakeup(udev); @@ -3456,8 +3454,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) hub_port_logical_disconnect(hub, port1); } else { /* Try to enable USB2 hardware LPM */ - if (udev->usb2_hw_lpm_capable == 1) - usb_set_usb2_hardware_lpm(udev, 1); + usb_enable_usb2_hardware_lpm(udev); /* Try to enable USB3 LTM and LPM */ usb_enable_ltm(udev); @@ -4283,7 +4280,7 @@ static void hub_set_initial_usb2_lpm_policy(struct usb_device *udev) if ((udev->bos->ext_cap->bmAttributes & cpu_to_le32(USB_BESL_SUPPORT)) || connect_type == USB_PORT_CONNECT_TYPE_HARD_WIRED) { udev->usb2_hw_lpm_allowed = 1; - usb_set_usb2_hardware_lpm(udev, 1); + usb_enable_usb2_hardware_lpm(udev); } } @@ -5434,8 +5431,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev) /* Disable USB2 hardware LPM. * It will be re-enabled by the enumeration process. */ - if (udev->usb2_hw_lpm_enabled == 1) - usb_set_usb2_hardware_lpm(udev, 0); + usb_disable_usb2_hardware_lpm(udev); /* Disable LPM and LTM while we reset the device and reinstall the alt * settings. Device-initiated LPM settings, and system exit latency @@ -5545,7 +5541,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev) done: /* Now that the alt settings are re-installed, enable LTM and LPM. */ - usb_set_usb2_hardware_lpm(udev, 1); + usb_enable_usb2_hardware_lpm(udev); usb_unlocked_enable_lpm(udev); usb_enable_ltm(udev); usb_release_bos_descriptor(udev); @@ -5660,7 +5656,10 @@ int usb_reset_device(struct usb_device *udev) cintf->needs_binding = 1; } } - usb_unbind_and_rebind_marked_interfaces(udev); + + /* If the reset failed, hub_wq will unbind drivers later */ + if (ret == 0) + usb_unbind_and_rebind_marked_interfaces(udev); } usb_autosuspend_device(udev); diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 530817c6783d..ec35df3813c6 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -821,9 +821,11 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) if (dev->state == USB_STATE_SUSPENDED) return -EHOSTUNREACH; - if (size <= 0 || !buf || !index) + if (size <= 0 || !buf) return -EINVAL; buf[0] = 0; + if (index <= 0 || index >= 256) + return -EINVAL; tbuf = kmalloc(256, GFP_NOIO); if (!tbuf) return -ENOMEM; @@ -1185,8 +1187,7 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0) dev->actconfig->interface[i] = NULL; } - if (dev->usb2_hw_lpm_enabled == 1) - usb_set_usb2_hardware_lpm(dev, 0); + usb_disable_usb2_hardware_lpm(dev); usb_unlocked_disable_lpm(dev); usb_disable_ltm(dev); diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 733479ddf8a7..19e819aa2419 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -64,9 +64,15 @@ static const struct usb_device_id usb_quirk_list[] = { /* Microsoft LifeCam-VX700 v2.0 */ { USB_DEVICE(0x045e, 0x0770), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Microsoft Surface Dock Ethernet (RTL8153 GigE) */ + { USB_DEVICE(0x045e, 0x07c6), .driver_info = USB_QUIRK_NO_LPM }, + /* Cherry Stream G230 2.0 (G85-231) and 3.0 (G85-232) */ { USB_DEVICE(0x046a, 0x0023), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Logitech HD Webcam C270 */ + { USB_DEVICE(0x046d, 0x0825), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Logitech HD Pro Webcams C920, C920-C, C925e and C930e */ { USB_DEVICE(0x046d, 0x082d), .driver_info = USB_QUIRK_DELAY_INIT }, { USB_DEVICE(0x046d, 0x0841), .driver_info = USB_QUIRK_DELAY_INIT }, diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 65b6e6b84043..6dc0f4e25cf3 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -472,7 +472,10 @@ static ssize_t usb2_hardware_lpm_store(struct device *dev, if (!ret) { udev->usb2_hw_lpm_allowed = value; - ret = usb_set_usb2_hardware_lpm(udev, value); + if (value) + ret = usb_enable_usb2_hardware_lpm(udev); + else + ret = usb_disable_usb2_hardware_lpm(udev); } usb_unlock_device(udev); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index fbff25ff23a9..dde0e997799e 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -84,7 +84,8 @@ extern int usb_remote_wakeup(struct usb_device *dev); extern int usb_runtime_suspend(struct device *dev); extern int usb_runtime_resume(struct device *dev); extern int usb_runtime_idle(struct device *dev); -extern int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable); +extern int usb_enable_usb2_hardware_lpm(struct usb_device *udev); +extern int usb_disable_usb2_hardware_lpm(struct usb_device *udev); #else @@ -104,7 +105,12 @@ static inline int usb_autoresume_device(struct usb_device *udev) return 0; } -static inline int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable) +static inline int usb_enable_usb2_hardware_lpm(struct usb_device *udev) +{ + return 0; +} + +static inline int usb_disable_usb2_hardware_lpm(struct usb_device *udev) { return 0; } diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index 1c7d7af6787a..0650f0b69de7 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2019, 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 @@ -66,6 +66,9 @@ /* AHB2PHY read/write waite value */ #define ONE_READ_WRITE_WAIT 0x11 +/* DP_DM linestate float */ +#define DP_DM_STATE_FLOAT 0x02 + /* cpu to fix usb interrupt */ static int cpu_to_affin; module_param(cpu_to_affin, int, S_IRUGO|S_IWUSR); @@ -130,6 +133,34 @@ struct dwc3_msm_req_complete { struct usb_request *req); }; +enum dwc3_drd_state { + DRD_STATE_UNDEFINED = 0, + + DRD_STATE_IDLE, + DRD_STATE_PERIPHERAL, + DRD_STATE_PERIPHERAL_SUSPEND, + + DRD_STATE_HOST_IDLE, + DRD_STATE_HOST, +}; + +static const char *const state_names[] = { + [DRD_STATE_UNDEFINED] = "undefined", + [DRD_STATE_IDLE] = "idle", + [DRD_STATE_PERIPHERAL] = "peripheral", + [DRD_STATE_PERIPHERAL_SUSPEND] = "peripheral_suspend", + [DRD_STATE_HOST_IDLE] = "host_idle", + [DRD_STATE_HOST] = "host", +}; + +static const char *dwc3_drd_state_string(enum dwc3_drd_state state) +{ + if (state < 0 || state >= ARRAY_SIZE(state_names)) + return "UNKNOWN"; + + return state_names[state]; +} + enum dwc3_id_state { DWC3_ID_GROUND = 0, DWC3_ID_FLOAT, @@ -147,6 +178,7 @@ enum plug_orientation { #define ID 0 #define B_SESS_VLD 1 #define B_SUSPEND 2 +#define WAIT_FOR_LPM 3 #define PM_QOS_SAMPLE_SEC 2 #define PM_QOS_THRESHOLD 400 @@ -193,7 +225,7 @@ struct dwc3_msm { unsigned long inputs; unsigned max_power; bool charging_disabled; - enum usb_otg_state otg_state; + enum dwc3_drd_state drd_state; enum usb_chg_state chg_state; struct work_struct bus_vote_w; unsigned int bus_vote; @@ -220,6 +252,8 @@ struct dwc3_msm { bool hc_died; bool xhci_ss_compliance_enable; bool no_wakeup_src_in_hostmode; + bool check_for_float; + bool float_detected; struct extcon_dev *extcon_vbus; struct extcon_dev *extcon_id; @@ -1934,7 +1968,7 @@ static void dwc3_msm_power_collapse_por(struct dwc3_msm *mdwc) atomic_set(&mdwc->in_p3, val == DWC3_LINK_STATE_U3); dwc3_msm_write_reg_field(mdwc->base, PWR_EVNT_IRQ_MASK_REG, PWR_EVNT_POWERDOWN_IN_P3_MASK, 1); - if (mdwc->otg_state == OTG_STATE_A_HOST) { + if (mdwc->drd_state == DRD_STATE_HOST) { dev_dbg(mdwc->dev, "%s: set the core in host mode\n", __func__); dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST); @@ -2056,7 +2090,7 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc, bool hibernation) } if (!mdwc->vbus_active && dwc->is_drd && - mdwc->otg_state == OTG_STATE_B_PERIPHERAL) { + mdwc->drd_state == DRD_STATE_PERIPHERAL) { /* * In some cases, the pm_runtime_suspend may be called by * usb_bam when there is pending lpm flag. However, if this is @@ -2078,7 +2112,7 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc, bool hibernation) * then check controller state of L2 and break * LPM sequence. Check this for device bus suspend case. */ - if ((dwc->is_drd && mdwc->otg_state == OTG_STATE_B_SUSPEND) && + if ((dwc->is_drd && mdwc->drd_state == DRD_STATE_PERIPHERAL_SUSPEND) && (dwc->gadget.state != USB_STATE_CONFIGURED)) { pr_err("%s(): Trying to go in LPM with state:%d\n", __func__, dwc->gadget.state); @@ -2192,7 +2226,13 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc, bool hibernation) dev_info(mdwc->dev, "DWC3 in low power mode\n"); dbg_event(0xFF, "Ctl Sus", atomic_read(&dwc->in_lpm)); + + /* kick_sm if it is waiting for lpm sequence to finish */ + if (test_and_clear_bit(WAIT_FOR_LPM, &mdwc->inputs)) + schedule_delayed_work(&mdwc->sm_work, 0); + mutex_unlock(&mdwc->suspend_resume_mutex); + return 0; } @@ -2724,6 +2764,7 @@ static int dwc3_msm_vbus_notifier(struct notifier_block *nb, if (mdwc->vbus_active == event) return NOTIFY_DONE; + mdwc->float_detected = false; cc_state = extcon_get_cable_state_(edev, EXTCON_USB_CC); if (cc_state < 0) mdwc->typec_orientation = ORIENTATION_NONE; @@ -3272,6 +3313,8 @@ static int dwc3_msm_probe(struct platform_device *pdev) if (of_property_read_bool(node, "qcom,disable-dev-mode-pm")) pm_runtime_get_noresume(mdwc->dev); + mdwc->check_for_float = of_property_read_bool(node, + "qcom,check-for-float"); ret = dwc3_msm_extcon_register(mdwc); if (ret) goto put_dwc3; @@ -3702,6 +3745,9 @@ static int dwc3_otg_start_host(struct dwc3_msm *mdwc, int on) if (!mdwc->host_only_mode) dwc3_post_host_reset_core_init(dwc); + /* wait for LPM, to ensure h/w is reset after stop_host */ + set_bit(WAIT_FOR_LPM, &mdwc->inputs); + pm_runtime_mark_last_busy(mdwc->dev); pm_runtime_put_sync_autosuspend(mdwc->dev); dbg_event(0xFF, "StopHost psync", @@ -3784,6 +3830,9 @@ static int dwc3_otg_start_peripheral(struct dwc3_msm *mdwc, int on) usb_phy_notify_disconnect(mdwc->ss_phy, USB_SPEED_SUPER); dwc3_override_vbus_status(mdwc, false); dwc3_usb3_phy_suspend(dwc, false); + + /* wait for LPM, to ensure h/w is reset after stop_peripheral */ + set_bit(WAIT_FOR_LPM, &mdwc->inputs); } pm_runtime_put_sync(mdwc->dev); @@ -3820,7 +3869,8 @@ static int dwc3_msm_gadget_vbus_draw(struct dwc3_msm *mdwc, unsigned mA) int ret, psy_type; psy_type = get_psy_type(mdwc); - if (psy_type == POWER_SUPPLY_TYPE_USB_FLOAT) { + if (psy_type == POWER_SUPPLY_TYPE_USB_FLOAT + || (mdwc->check_for_float && mdwc->float_detected)) { if (!mA) pval.intval = -ETIMEDOUT; else @@ -3853,7 +3903,7 @@ set_prop: * * @w: Pointer to the dwc3 otg workqueue * - * NOTE: After any change in otg_state, we must reschdule the state machine. + * NOTE: After any change in drd_state, we must reschdule the state machine. */ static void dwc3_otg_sm_work(struct work_struct *w) { @@ -3872,13 +3922,13 @@ static void dwc3_otg_sm_work(struct work_struct *w) return; } - state = usb_otg_state_string(mdwc->otg_state); + state = dwc3_drd_state_string(mdwc->drd_state); dev_dbg(mdwc->dev, "%s state\n", state); dbg_event(0xFF, state, 0); /* Check OTG state */ - switch (mdwc->otg_state) { - case OTG_STATE_UNDEFINED: + switch (mdwc->drd_state) { + case DRD_STATE_UNDEFINED: /* put controller and phy in suspend if no cable connected */ if (test_bit(ID, &mdwc->inputs) && !test_bit(B_SESS_VLD, &mdwc->inputs)) { @@ -3890,36 +3940,57 @@ static void dwc3_otg_sm_work(struct work_struct *w) pm_runtime_put_sync(mdwc->dev); dbg_event(0xFF, "Undef NoUSB", atomic_read(&mdwc->dev->power.usage_count)); - mdwc->otg_state = OTG_STATE_B_IDLE; + mdwc->drd_state = DRD_STATE_IDLE; break; } dbg_event(0xFF, "Exit UNDEF", 0); - mdwc->otg_state = OTG_STATE_B_IDLE; + mdwc->drd_state = DRD_STATE_IDLE; pm_runtime_set_suspended(mdwc->dev); pm_runtime_enable(mdwc->dev); /* fall-through */ - case OTG_STATE_B_IDLE: + case DRD_STATE_IDLE: + if (test_bit(WAIT_FOR_LPM, &mdwc->inputs)) { + dev_dbg(mdwc->dev, "still not in lpm, wait.\n"); + break; + } + if (!test_bit(ID, &mdwc->inputs)) { dev_dbg(mdwc->dev, "!id\n"); - mdwc->otg_state = OTG_STATE_A_IDLE; + mdwc->drd_state = DRD_STATE_HOST_IDLE; work = 1; } else if (test_bit(B_SESS_VLD, &mdwc->inputs)) { dev_dbg(mdwc->dev, "b_sess_vld\n"); + mdwc->float_detected = false; if (get_psy_type(mdwc) == POWER_SUPPLY_TYPE_USB_FLOAT) queue_delayed_work(mdwc->dwc3_wq, &mdwc->sdp_check, msecs_to_jiffies(SDP_CONNETION_CHECK_TIME)); /* * Increment pm usage count upon cable connect. Count - * is decremented in OTG_STATE_B_PERIPHERAL state on + * is decremented in DRD_STATE_PERIPHERAL state on * cable disconnect or in bus suspend. */ pm_runtime_get_sync(mdwc->dev); dbg_event(0xFF, "BIDLE gsync", atomic_read(&mdwc->dev->power.usage_count)); + if (mdwc->check_for_float) { + /* + * If DP_DM are found to be floating, do not + * start the peripheral mode. + */ + if (usb_phy_dpdm_with_idp_src(mdwc->hs_phy) == + DP_DM_STATE_FLOAT) { + mdwc->float_detected = true; + dwc3_msm_gadget_vbus_draw(mdwc, 0); + pm_runtime_put_sync(mdwc->dev); + dbg_event(0xFF, "FLT sync", atomic_read( + &mdwc->dev->power.usage_count)); + break; + } + } dwc3_otg_start_peripheral(mdwc, 1); - mdwc->otg_state = OTG_STATE_B_PERIPHERAL; + mdwc->drd_state = DRD_STATE_PERIPHERAL; work = 1; break; } else { @@ -3929,17 +4000,17 @@ static void dwc3_otg_sm_work(struct work_struct *w) } break; - case OTG_STATE_B_PERIPHERAL: + case DRD_STATE_PERIPHERAL: if (!test_bit(B_SESS_VLD, &mdwc->inputs) || !test_bit(ID, &mdwc->inputs)) { dev_dbg(mdwc->dev, "!id || !bsv\n"); - mdwc->otg_state = OTG_STATE_B_IDLE; + mdwc->drd_state = DRD_STATE_IDLE; cancel_delayed_work_sync(&mdwc->sdp_check); dwc3_otg_start_peripheral(mdwc, 0); /* * Decrement pm usage count upon cable disconnect * which was incremented upon cable connect in - * OTG_STATE_B_IDLE state + * DRD_STATE_IDLE state */ pm_runtime_put_sync(mdwc->dev); dbg_event(0xFF, "!BSV psync", @@ -3948,13 +4019,13 @@ static void dwc3_otg_sm_work(struct work_struct *w) } else if (test_bit(B_SUSPEND, &mdwc->inputs) && test_bit(B_SESS_VLD, &mdwc->inputs)) { dev_dbg(mdwc->dev, "BPER bsv && susp\n"); - mdwc->otg_state = OTG_STATE_B_SUSPEND; + mdwc->drd_state = DRD_STATE_PERIPHERAL_SUSPEND; /* * Decrement pm usage count upon bus suspend. * Count was incremented either upon cable - * connect in OTG_STATE_B_IDLE or host + * connect in DRD_STATE_IDLE or host * initiated resume after bus suspend in - * OTG_STATE_B_SUSPEND state + * DRD_STATE_PERIPHERAL_SUSPEND state */ pm_runtime_mark_last_busy(mdwc->dev); pm_runtime_put_autosuspend(mdwc->dev); @@ -3963,20 +4034,20 @@ static void dwc3_otg_sm_work(struct work_struct *w) } break; - case OTG_STATE_B_SUSPEND: + case DRD_STATE_PERIPHERAL_SUSPEND: if (!test_bit(B_SESS_VLD, &mdwc->inputs)) { dev_dbg(mdwc->dev, "BSUSP: !bsv\n"); - mdwc->otg_state = OTG_STATE_B_IDLE; + mdwc->drd_state = DRD_STATE_IDLE; cancel_delayed_work_sync(&mdwc->sdp_check); dwc3_otg_start_peripheral(mdwc, 0); } else if (!test_bit(B_SUSPEND, &mdwc->inputs)) { dev_dbg(mdwc->dev, "BSUSP !susp\n"); - mdwc->otg_state = OTG_STATE_B_PERIPHERAL; + mdwc->drd_state = DRD_STATE_PERIPHERAL; /* * Increment pm usage count upon host * initiated resume. Count was decremented * upon bus suspend in - * OTG_STATE_B_PERIPHERAL state. + * DRD_STATE_PERIPHERAL state. */ pm_runtime_get_sync(mdwc->dev); dbg_event(0xFF, "!SUSP gsync", @@ -3984,15 +4055,15 @@ static void dwc3_otg_sm_work(struct work_struct *w) } break; - case OTG_STATE_A_IDLE: + case DRD_STATE_HOST_IDLE: /* Switch to A-Device*/ if (test_bit(ID, &mdwc->inputs)) { dev_dbg(mdwc->dev, "id\n"); - mdwc->otg_state = OTG_STATE_B_IDLE; + mdwc->drd_state = DRD_STATE_IDLE; mdwc->vbus_retry_count = 0; work = 1; } else { - mdwc->otg_state = OTG_STATE_A_HOST; + mdwc->drd_state = DRD_STATE_HOST; ret = dwc3_otg_start_host(mdwc, 1); if ((ret == -EPROBE_DEFER) && mdwc->vbus_retry_count < 3) { @@ -4000,14 +4071,14 @@ static void dwc3_otg_sm_work(struct work_struct *w) * Get regulator failed as regulator driver is * not up yet. Will try to start host after 1sec */ - mdwc->otg_state = OTG_STATE_A_IDLE; + mdwc->drd_state = DRD_STATE_HOST_IDLE; dev_dbg(mdwc->dev, "Unable to get vbus regulator. Retrying...\n"); delay = VBUS_REG_CHECK_DELAY; work = 1; mdwc->vbus_retry_count++; } else if (ret) { dev_err(mdwc->dev, "unable to start host\n"); - mdwc->otg_state = OTG_STATE_A_IDLE; + mdwc->drd_state = DRD_STATE_HOST_IDLE; goto ret; } if (mdwc->no_wakeup_src_in_hostmode) { @@ -4017,12 +4088,12 @@ static void dwc3_otg_sm_work(struct work_struct *w) } break; - case OTG_STATE_A_HOST: + case DRD_STATE_HOST: if (test_bit(ID, &mdwc->inputs) || mdwc->hc_died) { dbg_event(0xFF, "id || hc_died", 0); dev_dbg(mdwc->dev, "%s state id || hc_died\n", state); dwc3_otg_start_host(mdwc, 0); - mdwc->otg_state = OTG_STATE_B_IDLE; + mdwc->drd_state = DRD_STATE_IDLE; mdwc->vbus_retry_count = 0; mdwc->hc_died = false; work = 1; @@ -4175,7 +4246,7 @@ static int dwc3_msm_pm_restore(struct device *dev) pm_runtime_enable(dev); /* Restore PHY flags if hibernated in host mode */ - if (mdwc->otg_state == OTG_STATE_A_HOST) { + if (mdwc->drd_state == DRD_STATE_HOST) { mdwc->hs_phy->flags |= PHY_HOST_MODE; if (mdwc->ss_phy) { mdwc->ss_phy->flags |= PHY_HOST_MODE; diff --git a/drivers/usb/gadget/function/f_gsi.h b/drivers/usb/gadget/function/f_gsi.h index 829e1fcbe156..96f1b5011960 100644 --- a/drivers/usb/gadget/function/f_gsi.h +++ b/drivers/usb/gadget/function/f_gsi.h @@ -31,7 +31,7 @@ #define GSI_MBIM_CTRL_NAME "android_mbim" #define GSI_DPL_CTRL_NAME "dpl_ctrl" #define GSI_CTRL_NAME_LEN (sizeof(GSI_MBIM_CTRL_NAME)+2) -#define GSI_MAX_CTRL_PKT_SIZE 4096 +#define GSI_MAX_CTRL_PKT_SIZE 8192 #define GSI_CTRL_DTR (1 << 0) diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c index 3b6e34fc032b..553922c3be85 100644 --- a/drivers/usb/gadget/udc/net2272.c +++ b/drivers/usb/gadget/udc/net2272.c @@ -962,6 +962,7 @@ net2272_dequeue(struct usb_ep *_ep, struct usb_request *_req) break; } if (&req->req != _req) { + ep->stopped = stopped; spin_unlock_irqrestore(&ep->dev->lock, flags); return -EINVAL; } diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 8efeadf30b4d..3a8d056a5d16 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -870,9 +870,6 @@ static void start_queue(struct net2280_ep *ep, u32 dmactl, u32 td_dma) (void) readl(&ep->dev->pci->pcimstctl); writel(BIT(DMA_START), &dma->dmastat); - - if (!ep->is_in) - stop_out_naking(ep); } static void start_dma(struct net2280_ep *ep, struct net2280_request *req) @@ -911,6 +908,7 @@ static void start_dma(struct net2280_ep *ep, struct net2280_request *req) writel(BIT(DMA_START), &dma->dmastat); return; } + stop_out_naking(ep); } tmp = dmactl_default; @@ -1272,9 +1270,9 @@ static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req) break; } if (&req->req != _req) { + ep->stopped = stopped; spin_unlock_irqrestore(&ep->dev->lock, flags); - dev_err(&ep->dev->pdev->dev, "%s: Request mismatch\n", - __func__); + ep_dbg(ep->dev, "%s: Request mismatch\n", __func__); return -EINVAL; } diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c index d5434e7a3b2e..86f9944f337d 100644 --- a/drivers/usb/host/u132-hcd.c +++ b/drivers/usb/host/u132-hcd.c @@ -3214,6 +3214,9 @@ static int __init u132_hcd_init(void) printk(KERN_INFO "driver %s\n", hcd_name); workqueue = create_singlethread_workqueue("u132"); retval = platform_driver_register(&u132_platform_driver); + if (retval) + destroy_workqueue(workqueue); + return retval; } diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 0ccd5cec42ff..a283665e2ed5 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -21,6 +21,7 @@ */ #include <linux/pci.h> +#include <linux/iopoll.h> #include <linux/irq.h> #include <linux/log2.h> #include <linux/module.h> @@ -46,7 +47,6 @@ static unsigned int quirks; module_param(quirks, uint, S_IRUGO); MODULE_PARM_DESC(quirks, "Bit flags for quirks to be enabled as default"); -/* TODO: copied from ehci-hcd.c - can this be refactored? */ /* * xhci_handshake - spin reading hc until handshake completes or fails * @ptr: address of hc register to be read @@ -63,18 +63,16 @@ MODULE_PARM_DESC(quirks, "Bit flags for quirks to be enabled as default"); int xhci_handshake(void __iomem *ptr, u32 mask, u32 done, int usec) { u32 result; + int ret; - do { - result = readl(ptr); - if (result == ~(u32)0) /* card removed */ - return -ENODEV; - result &= mask; - if (result == done) - return 0; - udelay(1); - usec--; - } while (usec > 0); - return -ETIMEDOUT; + ret = readl_poll_timeout_atomic(ptr, result, + (result & mask) == done || + result == U32_MAX, + 1, usec); + if (result == U32_MAX) /* card removed */ + return -ENODEV; + + return ret; } int xhci_handshake_check_state(struct xhci_hcd *xhci, @@ -4239,7 +4237,6 @@ int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, pm_addr = port_array[port_num] + PORTPMSC; pm_val = readl(pm_addr); hlpm_addr = port_array[port_num] + PORTHLPMC; - field = le32_to_cpu(udev->bos->ext_cap->bmAttributes); xhci_dbg(xhci, "%s port %d USB2 hardware LPM\n", enable ? "enable" : "disable", port_num + 1); @@ -4251,6 +4248,7 @@ int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, * default one which works with mixed HIRD and BESL * systems. See XHCI_DEFAULT_BESL definition in xhci.h */ + field = le32_to_cpu(udev->bos->ext_cap->bmAttributes); if ((field & USB_BESL_SUPPORT) && (field & USB_BESL_BASELINE_VALID)) hird = USB_GET_BESL_BASELINE(field); diff --git a/drivers/usb/misc/rio500.c b/drivers/usb/misc/rio500.c index 13731d512624..6e761fabffca 100644 --- a/drivers/usb/misc/rio500.c +++ b/drivers/usb/misc/rio500.c @@ -103,9 +103,22 @@ static int close_rio(struct inode *inode, struct file *file) { struct rio_usb_data *rio = &rio_instance; - rio->isopen = 0; + /* against disconnect() */ + mutex_lock(&rio500_mutex); + mutex_lock(&(rio->lock)); - dev_info(&rio->rio_dev->dev, "Rio closed.\n"); + rio->isopen = 0; + if (!rio->present) { + /* cleanup has been delayed */ + kfree(rio->ibuf); + kfree(rio->obuf); + rio->ibuf = NULL; + rio->obuf = NULL; + } else { + dev_info(&rio->rio_dev->dev, "Rio closed.\n"); + } + mutex_unlock(&(rio->lock)); + mutex_unlock(&rio500_mutex); return 0; } @@ -464,15 +477,23 @@ static int probe_rio(struct usb_interface *intf, { struct usb_device *dev = interface_to_usbdev(intf); struct rio_usb_data *rio = &rio_instance; - int retval; + int retval = 0; - dev_info(&intf->dev, "USB Rio found at address %d\n", dev->devnum); + mutex_lock(&rio500_mutex); + if (rio->present) { + dev_info(&intf->dev, "Second USB Rio at address %d refused\n", dev->devnum); + retval = -EBUSY; + goto bail_out; + } else { + dev_info(&intf->dev, "USB Rio found at address %d\n", dev->devnum); + } retval = usb_register_dev(intf, &usb_rio_class); if (retval) { dev_err(&dev->dev, "Not able to get a minor for this device.\n"); - return -ENOMEM; + retval = -ENOMEM; + goto bail_out; } rio->rio_dev = dev; @@ -481,7 +502,8 @@ static int probe_rio(struct usb_interface *intf, dev_err(&dev->dev, "probe_rio: Not enough memory for the output buffer\n"); usb_deregister_dev(intf, &usb_rio_class); - return -ENOMEM; + retval = -ENOMEM; + goto bail_out; } dev_dbg(&intf->dev, "obuf address:%p\n", rio->obuf); @@ -490,7 +512,8 @@ static int probe_rio(struct usb_interface *intf, "probe_rio: Not enough memory for the input buffer\n"); usb_deregister_dev(intf, &usb_rio_class); kfree(rio->obuf); - return -ENOMEM; + retval = -ENOMEM; + goto bail_out; } dev_dbg(&intf->dev, "ibuf address:%p\n", rio->ibuf); @@ -498,8 +521,10 @@ static int probe_rio(struct usb_interface *intf, usb_set_intfdata (intf, rio); rio->present = 1; +bail_out: + mutex_unlock(&rio500_mutex); - return 0; + return retval; } static void disconnect_rio(struct usb_interface *intf) diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c index 306d6852ebc7..b9d50020c684 100644 --- a/drivers/usb/misc/sisusbvga/sisusb.c +++ b/drivers/usb/misc/sisusbvga/sisusb.c @@ -3103,6 +3103,13 @@ static int sisusb_probe(struct usb_interface *intf, mutex_init(&(sisusb->lock)); + sisusb->sisusb_dev = dev; + sisusb->vrambase = SISUSB_PCI_MEMBASE; + sisusb->mmiobase = SISUSB_PCI_MMIOBASE; + sisusb->mmiosize = SISUSB_PCI_MMIOSIZE; + sisusb->ioportbase = SISUSB_PCI_IOPORTBASE; + /* Everything else is zero */ + /* Register device */ retval = usb_register_dev(intf, &usb_sisusb_class); if (retval) { @@ -3112,13 +3119,7 @@ static int sisusb_probe(struct usb_interface *intf, goto error_1; } - sisusb->sisusb_dev = dev; - sisusb->minor = intf->minor; - sisusb->vrambase = SISUSB_PCI_MEMBASE; - sisusb->mmiobase = SISUSB_PCI_MMIOBASE; - sisusb->mmiosize = SISUSB_PCI_MMIOSIZE; - sisusb->ioportbase = SISUSB_PCI_IOPORTBASE; - /* Everything else is zero */ + sisusb->minor = intf->minor; /* Allocate buffers */ sisusb->ibufsize = SISUSB_IBUF_SIZE; diff --git a/drivers/usb/misc/yurex.c b/drivers/usb/misc/yurex.c index 5594a4a4a83f..a8b6d0036e5d 100644 --- a/drivers/usb/misc/yurex.c +++ b/drivers/usb/misc/yurex.c @@ -332,6 +332,7 @@ static void yurex_disconnect(struct usb_interface *interface) usb_deregister_dev(interface, &yurex_class); /* prevent more I/O from starting */ + usb_poison_urb(dev->urb); mutex_lock(&dev->io_mutex); dev->interface = NULL; mutex_unlock(&dev->io_mutex); diff --git a/drivers/usb/phy/phy-msm-qusb.c b/drivers/usb/phy/phy-msm-qusb.c index be63c6c0a86a..ae72ec6b3d19 100644 --- a/drivers/usb/phy/phy-msm-qusb.c +++ b/drivers/usb/phy/phy-msm-qusb.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2017,2019 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 @@ -29,11 +29,23 @@ #include <linux/usb/msm_hsusb.h> #include <linux/reset.h> +#define QUSB2PHY_PLL_PWR_CTL 0x18 +#define REF_BUF_EN BIT(0) +#define REXT_EN BIT(1) +#define PLL_BYPASSNL BIT(2) +#define REXT_TRIM_0 BIT(4) + +#define QUSB2PHY_PLL_AUTOPGM_CTL1 0x1C +#define PLL_RESET_N_CNT_5 0x5 +#define PLL_RESET_N BIT(4) +#define PLL_AUTOPGM_EN BIT(7) + #define QUSB2PHY_PLL_STATUS 0x38 #define QUSB2PHY_PLL_LOCK BIT(5) #define QUSB2PHY_PORT_QC1 0x70 #define VDM_SRC_EN BIT(4) +#define IDP_SRC_EN BIT(3) #define VDP_SRC_EN BIT(2) #define QUSB2PHY_PORT_QC2 0x74 @@ -57,6 +69,7 @@ #define CORE_READY_STATUS BIT(0) #define QUSB2PHY_PORT_UTMI_CTRL1 0xC0 +#define SUSPEND_N BIT(5) #define TERM_SELECT BIT(4) #define XCVR_SELECT_FS BIT(2) #define OP_MODE_NON_DRIVE BIT(0) @@ -84,6 +97,7 @@ #define DPSE_INTR_HIGH_SEL BIT(1) #define DPSE_INTR_EN BIT(0) +#define QUSB2PHY_PORT_INT_STATUS 0xF0 #define QUSB2PHY_PORT_UTMI_STATUS 0xF4 #define LINESTATE_DP BIT(0) #define LINESTATE_DM BIT(1) @@ -156,6 +170,10 @@ struct qusb_phy { struct regulator_desc dpdm_rdesc; struct regulator_dev *dpdm_rdev; + bool dpdm_pulsing_enabled; + struct power_supply *dpdm_psy; + struct power_supply_desc dpdm_psy_desc; + /* emulation targets specific */ void __iomem *emu_phy_base; bool emulation; @@ -167,6 +185,11 @@ struct qusb_phy { int emu_dcm_reset_seq_len; bool put_into_high_z_state; struct mutex phy_lock; + spinlock_t pulse_lock; +}; + +static enum power_supply_property dpdm_props[] = { + POWER_SUPPLY_PROP_DP_DM, }; static void qusb_phy_enable_clocks(struct qusb_phy *qphy, bool on) @@ -331,10 +354,13 @@ err_vdd: return ret; } +#define PHY_PULSE_TIME_USEC 250 static int qusb_phy_update_dpdm(struct usb_phy *phy, int value) { struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy); int ret = 0; + unsigned long flags; + u32 reg; dev_dbg(phy->dev, "%s value:%d rm_pulldown:%d\n", __func__, value, qphy->rm_pulldown); @@ -393,6 +419,21 @@ static int qusb_phy_update_dpdm(struct usb_phy *phy, int value) qusb_phy_enable_clocks(qphy, false); } } + + /* Clear QC1 and QC2 registers when rm_pulldown = 1 */ + if (qphy->dpdm_pulsing_enabled && qphy->rm_pulldown) { + dev_dbg(phy->dev, "clearing qc1 and qc2 registers.\n"); + ret = clk_prepare_enable(qphy->cfg_ahb_clk); + if (ret) + goto clk_error; + + /* Clear qc1 and qc2 registers */ + writel_relaxed(0x00, qphy->base + QUSB2PHY_PORT_QC1); + writel_relaxed(0x00, qphy->base + QUSB2PHY_PORT_QC2); + /* to make sure above write goes through */ + mb(); + clk_disable_unprepare(qphy->cfg_ahb_clk); + } mutex_unlock(&qphy->phy_lock); break; @@ -401,6 +442,22 @@ static int qusb_phy_update_dpdm(struct usb_phy *phy, int value) dev_dbg(phy->dev, "POWER_SUPPLY_DP_DM_DPR_DMR\n"); mutex_lock(&qphy->phy_lock); if (qphy->rm_pulldown) { + dev_dbg(phy->dev, "clearing qc1 and qc2 registers.\n"); + if (qphy->dpdm_pulsing_enabled) { + ret = clk_prepare_enable(qphy->cfg_ahb_clk); + if (ret) + goto clk_error; + + /* Clear qc1 and qc2 registers */ + writel_relaxed(0x00, + qphy->base + QUSB2PHY_PORT_QC1); + writel_relaxed(0x00, + qphy->base + QUSB2PHY_PORT_QC2); + /* to make sure above write goes through */ + mb(); + clk_disable_unprepare(qphy->cfg_ahb_clk); + } + if (!qphy->cable_connected) { if (qphy->tcsr_clamp_dig_n) writel_relaxed(0x0, @@ -417,15 +474,172 @@ static int qusb_phy_update_dpdm(struct usb_phy *phy, int value) mutex_unlock(&qphy->phy_lock); break; + case POWER_SUPPLY_DP_DM_DP0P6_DMF: + if (!qphy->dpdm_pulsing_enabled) + break; + + dev_dbg(phy->dev, "POWER_SUPPLY_DP_DM_DP0P6_DMF\n"); + ret = clk_prepare_enable(qphy->cfg_ahb_clk); + if (ret) + goto clk_error; + + /* Set DP to 0.6v and DM to High Z state */ + writel_relaxed(VDP_SRC_EN, qphy->base + QUSB2PHY_PORT_QC1); + /* complete above write */ + mb(); + clk_disable_unprepare(qphy->cfg_ahb_clk); + break; + + case POWER_SUPPLY_DP_DM_DP0P6_DM3P3: + if (!qphy->dpdm_pulsing_enabled) + break; + + dev_dbg(phy->dev, "POWER_SUPPLY_DP_DM_DP0PHVDCP_36_DM3P3\n"); + ret = clk_prepare_enable(qphy->cfg_ahb_clk); + if (ret) + goto clk_error; + + /* Set DP to 0.6v */ + writel_relaxed(VDP_SRC_EN, qphy->base + QUSB2PHY_PORT_QC1); + /* Set DM to 3.075v */ + writel_relaxed(RPUM_LOW_EN | RDM_UP_EN, + qphy->base + QUSB2PHY_PORT_QC2); + /* complete above write */ + mb(); + clk_disable_unprepare(qphy->cfg_ahb_clk); + break; + + case POWER_SUPPLY_DP_DM_DP_PULSE: + if (!qphy->dpdm_pulsing_enabled) + break; + + dev_dbg(phy->dev, "POWER_SUPPLY_DP_DM_DP_PULSE\n"); + ret = clk_prepare_enable(qphy->cfg_ahb_clk); + if (ret) + goto clk_error; + + spin_lock_irqsave(&qphy->pulse_lock, flags); + /*Set DP to 3.075v, sleep for .25 ms */ + reg = readl_relaxed(qphy->base + QUSB2PHY_PORT_QC2); + reg |= (RDP_UP_EN | RPUP_LOW_EN); + writel_relaxed(reg, qphy->base + QUSB2PHY_PORT_QC2); + + /* complete above write */ + mb(); + + /* + * It is recommended to wait here to get voltage change on + * DP/DM line. + */ + udelay(PHY_PULSE_TIME_USEC); + + /* Set DP to 0.6v, sleep 2-3ms */ + reg = readl_relaxed(qphy->base + QUSB2PHY_PORT_QC1); + reg |= VDP_SRC_EN; + writel_relaxed(reg, qphy->base + QUSB2PHY_PORT_QC1); + + reg = readl_relaxed(qphy->base + QUSB2PHY_PORT_QC2); + reg &= ~(RDP_UP_EN | RPUP_LOW_EN); + writel_relaxed(reg, qphy->base + QUSB2PHY_PORT_QC2); + /* complete above write */ + mb(); + spin_unlock_irqrestore(&qphy->pulse_lock, flags); + /* + * It is recommended to wait here to get voltage change on + * DP/DM line. + */ + usleep_range(2000, 3000); + clk_disable_unprepare(qphy->cfg_ahb_clk); + break; + + case POWER_SUPPLY_DP_DM_DM_PULSE: + if (!qphy->dpdm_pulsing_enabled) + break; + + dev_dbg(phy->dev, "POWER_SUPPLY_DP_DM_DM_PULSE\n"); + ret = clk_prepare_enable(qphy->cfg_ahb_clk); + if (ret) + goto clk_error; + + spin_lock_irqsave(&qphy->pulse_lock, flags); + /* Set DM to 0.6v, sleep .25 ms */ + reg = readl_relaxed(qphy->base + QUSB2PHY_PORT_QC1); + reg |= VDM_SRC_EN; + writel_relaxed(reg, qphy->base + QUSB2PHY_PORT_QC1); + + reg = readl_relaxed(qphy->base + QUSB2PHY_PORT_QC2); + reg &= ~(RDM_UP_EN | RPUM_LOW_EN); + writel_relaxed(reg, qphy->base + QUSB2PHY_PORT_QC2); + + /* complete above write */ + mb(); + + /* + * It is recommended to wait here to get voltage change on + * DP/DM line. + */ + udelay(PHY_PULSE_TIME_USEC); + + /* DM to 3.075v, sleep 2-3ms */ + reg = readl_relaxed(qphy->base + QUSB2PHY_PORT_QC2); + reg |= (RPUM_LOW_EN | RDM_UP_EN); + writel_relaxed(reg, qphy->base + QUSB2PHY_PORT_QC2); + + reg = readl_relaxed(qphy->base + QUSB2PHY_PORT_QC1); + reg &= ~VDM_SRC_EN; + writel_relaxed(reg, qphy->base + QUSB2PHY_PORT_QC1); + + /* complete above write */ + mb(); + spin_unlock_irqrestore(&qphy->pulse_lock, flags); + + /* + * It is recommended to wait here to get voltage change on + * DP/DM line. + */ + usleep_range(2000, 3000); + clk_disable_unprepare(qphy->cfg_ahb_clk); + break; default: ret = -EINVAL; dev_err(phy->dev, "Invalid power supply property(%d)\n", value); break; } +clk_error: return ret; } +static int qusb_phy_get_property_usb(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + return -EINVAL; +} + +static int qusb_phy_set_property_usb(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + struct qusb_phy *qphy = power_supply_get_drvdata(psy); + int ret = 0; + + switch (prop) { + case POWER_SUPPLY_PROP_DP_DM: + ret = qusb_phy_update_dpdm(&qphy->phy, val->intval); + if (ret) { + dev_dbg(qphy->phy.dev, "error in dpdm update: %d\n", + ret); + return ret; + } + break; + default: + return -EINVAL; + } + + return 0; +} + static void qusb_phy_get_tune2_param(struct qusb_phy *qphy) { u8 num_of_bits; @@ -692,6 +906,83 @@ static void qusb_phy_shutdown(struct usb_phy *phy) qusb_phy_enable_clocks(qphy, false); } + +/** + * Returns DP/DM linestate with Idp_src enabled to detect if lines are floating + * + * @uphy - usb phy pointer. + * + */ +static int qusb_phy_linestate_with_idp_src(struct usb_phy *phy) +{ + struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy); + u8 int_status, ret; + + /* Disable/powerdown the PHY */ + writel_relaxed(CLAMP_N_EN | FREEZIO_N | POWER_DOWN, + qphy->base + QUSB2PHY_PORT_POWERDOWN); + + /* Put PHY in non-driving mode */ + writel_relaxed(TERM_SELECT | XCVR_SELECT_FS | OP_MODE_NON_DRIVE | + SUSPEND_N, qphy->base + QUSB2PHY_PORT_UTMI_CTRL1); + + /* Switch PHY to utmi register mode */ + writel_relaxed(UTMI_ULPI_SEL | UTMI_TEST_MUX_SEL, + qphy->base + QUSB2PHY_PORT_UTMI_CTRL2); + + writel_relaxed(PLL_RESET_N_CNT_5, + qphy->base + QUSB2PHY_PLL_AUTOPGM_CTL1); + + /* Enable PHY */ + writel_relaxed(CLAMP_N_EN | FREEZIO_N, + qphy->base + QUSB2PHY_PORT_POWERDOWN); + + writel_relaxed(REF_BUF_EN | REXT_EN | PLL_BYPASSNL | REXT_TRIM_0, + qphy->base + QUSB2PHY_PLL_PWR_CTL); + + usleep_range(5, 1000); + + writel_relaxed(PLL_RESET_N | PLL_RESET_N_CNT_5, + qphy->base + QUSB2PHY_PLL_AUTOPGM_CTL1); + usleep_range(50, 1000); + + writel_relaxed(0x00, qphy->base + QUSB2PHY_PORT_QC1); + writel_relaxed(0x00, qphy->base + QUSB2PHY_PORT_QC2); + + /* Enable all chg_det events from PHY */ + writel_relaxed(0x1F, qphy->base + QUSB2PHY_PORT_INTR_CTRL); + /* Enable Idp_src */ + writel_relaxed(IDP_SRC_EN, qphy->base + QUSB2PHY_PORT_QC1); + + usleep_range(1000, 2000); + int_status = readl_relaxed(qphy->base + QUSB2PHY_PORT_INT_STATUS); + + /* Exit chg_det mode, set PHY regs to default values */ + writel_relaxed(CLAMP_N_EN | FREEZIO_N | POWER_DOWN, + qphy->base + QUSB2PHY_PORT_POWERDOWN); /* 23 */ + + writel_relaxed(PLL_AUTOPGM_EN | PLL_RESET_N | PLL_RESET_N_CNT_5, + qphy->base + QUSB2PHY_PLL_AUTOPGM_CTL1); + + writel_relaxed(UTMI_ULPI_SEL, qphy->base + QUSB2PHY_PORT_UTMI_CTRL2); + + writel_relaxed(TERM_SELECT, qphy->base + QUSB2PHY_PORT_UTMI_CTRL1); + + writel_relaxed(CLAMP_N_EN | FREEZIO_N, + qphy->base + QUSB2PHY_PORT_POWERDOWN); + + int_status = int_status & 0x5; + + /* + * int_status's Bit(0) is DP and Bit(2) is DM. + * Caller expects bit(1) as DP and bit(0) DM i.e. usual linestate format + */ + ret = (int_status >> 2) | ((int_status & 0x1) << 1); + pr_debug("%s: int_status:%x, dpdm:%x\n", __func__, int_status, ret); + + return ret; +} + /** * Performs QUSB2 PHY suspend/resume functionality. * @@ -902,6 +1193,7 @@ static int qusb_phy_probe(struct platform_device *pdev) int ret = 0, size = 0; const char *phy_type; bool hold_phy_reset; + struct power_supply_config dpdm_cfg = {}; qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL); if (!qphy) @@ -1141,6 +1433,7 @@ static int qusb_phy_probe(struct platform_device *pdev) } mutex_init(&qphy->phy_lock); + spin_lock_init(&qphy->pulse_lock); platform_set_drvdata(pdev, qphy); qphy->phy.label = "msm-qusb-phy"; @@ -1150,6 +1443,7 @@ static int qusb_phy_probe(struct platform_device *pdev) qphy->phy.type = USB_PHY_TYPE_USB2; qphy->phy.notify_connect = qusb_phy_notify_connect; qphy->phy.notify_disconnect = qusb_phy_notify_disconnect; + qphy->phy.dpdm_with_idp_src = qusb_phy_linestate_with_idp_src; /* * On some platforms multiple QUSB PHYs are available. If QUSB PHY is @@ -1162,25 +1456,57 @@ static int qusb_phy_probe(struct platform_device *pdev) dev_err(dev, "%s:phy_reset assert failed\n", __func__); } + qphy->dpdm_pulsing_enabled = of_property_read_bool(dev->of_node, + "qcom,enable-dpdm-pulsing"); + + if (qphy->dpdm_pulsing_enabled) { + qphy->dpdm_psy_desc.name = "dpdm"; + qphy->dpdm_psy_desc.type = POWER_SUPPLY_TYPE_USB; + qphy->dpdm_psy_desc.properties = dpdm_props; + qphy->dpdm_psy_desc.num_properties = ARRAY_SIZE(dpdm_props); + qphy->dpdm_psy_desc.set_property = qusb_phy_set_property_usb; + qphy->dpdm_psy_desc.get_property = qusb_phy_get_property_usb; + + dpdm_cfg.drv_data = qphy; + dpdm_cfg.of_node = dev->of_node; + qphy->dpdm_psy = power_supply_register(&pdev->dev, + &qphy->dpdm_psy_desc, &dpdm_cfg); + if (IS_ERR(qphy->dpdm_psy)) { + dev_err(&pdev->dev, "%s:dpdm power_supply_register failed\n", + __func__); + return PTR_ERR(qphy->dpdm_psy); + } + } + ret = usb_add_phy_dev(&qphy->phy); if (ret) - return ret; + goto unregister_psy; ret = qusb_phy_regulator_init(qphy); if (ret) - usb_remove_phy(&qphy->phy); + goto remove_phy; /* de-assert clamp dig n to reduce leakage on 1p8 upon boot up */ if (qphy->tcsr_clamp_dig_n) writel_relaxed(0x0, qphy->tcsr_clamp_dig_n); return ret; + +remove_phy: + usb_remove_phy(&qphy->phy); +unregister_psy: + if (qphy->dpdm_psy) + power_supply_unregister(qphy->dpdm_psy); + + return ret; } static int qusb_phy_remove(struct platform_device *pdev) { struct qusb_phy *qphy = platform_get_drvdata(pdev); + if (qphy->dpdm_psy) + power_supply_unregister(qphy->dpdm_psy); usb_remove_phy(&qphy->phy); if (qphy->clocks_enabled) { diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 54e170dd3dad..faead4f32b1c 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -350,39 +350,59 @@ void usb_serial_generic_read_bulk_callback(struct urb *urb) struct usb_serial_port *port = urb->context; unsigned char *data = urb->transfer_buffer; unsigned long flags; + bool stopped = false; + int status = urb->status; int i; for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) { if (urb == port->read_urbs[i]) break; } - set_bit(i, &port->read_urbs_free); dev_dbg(&port->dev, "%s - urb %d, len %d\n", __func__, i, urb->actual_length); - switch (urb->status) { + switch (status) { case 0: + usb_serial_debug_data(&port->dev, __func__, urb->actual_length, + data); + port->serial->type->process_read_urb(urb); break; case -ENOENT: case -ECONNRESET: case -ESHUTDOWN: dev_dbg(&port->dev, "%s - urb stopped: %d\n", - __func__, urb->status); - return; + __func__, status); + stopped = true; + break; case -EPIPE: dev_err(&port->dev, "%s - urb stopped: %d\n", - __func__, urb->status); - return; + __func__, status); + stopped = true; + break; default: dev_dbg(&port->dev, "%s - nonzero urb status: %d\n", - __func__, urb->status); - goto resubmit; + __func__, status); + break; } - usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data); - port->serial->type->process_read_urb(urb); + /* + * Make sure URB processing is done before marking as free to avoid + * racing with unthrottle() on another CPU. Matches the barriers + * implied by the test_and_clear_bit() in + * usb_serial_generic_submit_read_urb(). + */ + smp_mb__before_atomic(); + set_bit(i, &port->read_urbs_free); + /* + * Make sure URB is marked as free before checking the throttled flag + * to avoid racing with unthrottle() on another CPU. Matches the + * smp_mb() in unthrottle(). + */ + smp_mb__after_atomic(); + + if (stopped) + return; -resubmit: /* Throttle the device if requested by tty */ spin_lock_irqsave(&port->lock, flags); port->throttled = port->throttle_req; @@ -399,6 +419,7 @@ void usb_serial_generic_write_bulk_callback(struct urb *urb) { unsigned long flags; struct usb_serial_port *port = urb->context; + int status = urb->status; int i; for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) { @@ -410,22 +431,22 @@ void usb_serial_generic_write_bulk_callback(struct urb *urb) set_bit(i, &port->write_urbs_free); spin_unlock_irqrestore(&port->lock, flags); - switch (urb->status) { + switch (status) { case 0: break; case -ENOENT: case -ECONNRESET: case -ESHUTDOWN: dev_dbg(&port->dev, "%s - urb stopped: %d\n", - __func__, urb->status); + __func__, status); return; case -EPIPE: dev_err_console(port, "%s - urb stopped: %d\n", - __func__, urb->status); + __func__, status); return; default: dev_err_console(port, "%s - nonzero urb status: %d\n", - __func__, urb->status); + __func__, status); goto resubmit; } @@ -456,6 +477,12 @@ void usb_serial_generic_unthrottle(struct tty_struct *tty) port->throttled = port->throttle_req = 0; spin_unlock_irq(&port->lock); + /* + * Matches the smp_mb__after_atomic() in + * usb_serial_generic_read_bulk_callback(). + */ + smp_mb(); + if (was_throttled) usb_serial_generic_submit_read_urbs(port, GFP_KERNEL); } diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 9f96dd274370..1effe74ec638 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1166,6 +1166,10 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1213, 0xff) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1214), .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) | RSVD(3) }, + { USB_DEVICE(TELIT_VENDOR_ID, 0x1260), + .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) }, + { USB_DEVICE(TELIT_VENDOR_ID, 0x1261), + .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) }, { USB_DEVICE(TELIT_VENDOR_ID, 0x1900), /* Telit LN940 (QMI) */ .driver_info = NCTRL(0) | RSVD(1) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1901, 0xff), /* Telit LN940 (MBIM) */ @@ -1767,6 +1771,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(ALINK_VENDOR_ID, SIMCOM_PRODUCT_SIM7100E), .driver_info = RSVD(5) | RSVD(6) }, { USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9003, 0xff) }, /* Simcom SIM7500/SIM7600 MBIM mode */ + { USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9011, 0xff), /* Simcom SIM7500/SIM7600 RNDIS mode */ + .driver_info = RSVD(7) }, { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_X060S_X200), .driver_info = NCTRL(0) | NCTRL(1) | RSVD(4) }, { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_X220_X500D), diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 9706d214c409..8fd5e19846ef 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -101,6 +101,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(SANWA_VENDOR_ID, SANWA_PRODUCT_ID) }, { USB_DEVICE(ADLINK_VENDOR_ID, ADLINK_ND6530_PRODUCT_ID) }, { USB_DEVICE(SMART_VENDOR_ID, SMART_PRODUCT_ID) }, + { USB_DEVICE(AT_VENDOR_ID, AT_VTKIT3_PRODUCT_ID) }, { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index d84c3b3d477b..496cbccbf26c 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -159,3 +159,6 @@ #define SMART_VENDOR_ID 0x0b8c #define SMART_PRODUCT_ID 0x2303 +/* Allied Telesis VT-Kit3 */ +#define AT_VENDOR_ID 0x0caa +#define AT_VTKIT3_PRODUCT_ID 0x3001 diff --git a/drivers/usb/storage/realtek_cr.c b/drivers/usb/storage/realtek_cr.c index 20433563a601..be432bec0c5b 100644 --- a/drivers/usb/storage/realtek_cr.c +++ b/drivers/usb/storage/realtek_cr.c @@ -772,18 +772,16 @@ static void rts51x_suspend_timer_fn(unsigned long data) break; case RTS51X_STAT_IDLE: case RTS51X_STAT_SS: - usb_stor_dbg(us, "RTS51X_STAT_SS, intf->pm_usage_cnt:%d, power.usage:%d\n", - atomic_read(&us->pusb_intf->pm_usage_cnt), + usb_stor_dbg(us, "RTS51X_STAT_SS, power.usage:%d\n", atomic_read(&us->pusb_intf->dev.power.usage_count)); - if (atomic_read(&us->pusb_intf->pm_usage_cnt) > 0) { + if (atomic_read(&us->pusb_intf->dev.power.usage_count) > 0) { usb_stor_dbg(us, "Ready to enter SS state\n"); rts51x_set_stat(chip, RTS51X_STAT_SS); /* ignore mass storage interface's children */ pm_suspend_ignore_children(&us->pusb_intf->dev, true); usb_autopm_put_interface_async(us->pusb_intf); - usb_stor_dbg(us, "RTS51X_STAT_SS 01, intf->pm_usage_cnt:%d, power.usage:%d\n", - atomic_read(&us->pusb_intf->pm_usage_cnt), + usb_stor_dbg(us, "RTS51X_STAT_SS 01, power.usage:%d\n", atomic_read(&us->pusb_intf->dev.power.usage_count)); } break; @@ -816,11 +814,10 @@ static void rts51x_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) int ret; if (working_scsi(srb)) { - usb_stor_dbg(us, "working scsi, intf->pm_usage_cnt:%d, power.usage:%d\n", - atomic_read(&us->pusb_intf->pm_usage_cnt), + usb_stor_dbg(us, "working scsi, power.usage:%d\n", atomic_read(&us->pusb_intf->dev.power.usage_count)); - if (atomic_read(&us->pusb_intf->pm_usage_cnt) <= 0) { + if (atomic_read(&us->pusb_intf->dev.power.usage_count) <= 0) { ret = usb_autopm_get_interface(us->pusb_intf); usb_stor_dbg(us, "working scsi, ret=%d\n", ret); } diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 6cac8f26b97a..e657b111b320 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -772,23 +772,33 @@ static int uas_slave_alloc(struct scsi_device *sdev) { struct uas_dev_info *devinfo = (struct uas_dev_info *)sdev->host->hostdata; + int maxp; sdev->hostdata = devinfo; - /* USB has unusual DMA-alignment requirements: Although the - * starting address of each scatter-gather element doesn't matter, - * the length of each element except the last must be divisible - * by the Bulk maxpacket value. There's currently no way to - * express this by block-layer constraints, so we'll cop out - * and simply require addresses to be aligned at 512-byte - * boundaries. This is okay since most block I/O involves - * hardware sectors that are multiples of 512 bytes in length, - * and since host controllers up through USB 2.0 have maxpacket - * values no larger than 512. - * - * But it doesn't suffice for Wireless USB, where Bulk maxpacket - * values can be as large as 2048. To make that work properly - * will require changes to the block layer. + /* + * We have two requirements here. We must satisfy the requirements + * of the physical HC and the demands of the protocol, as we + * definitely want no additional memory allocation in this path + * ruling out using bounce buffers. + * + * For a transmission on USB to continue we must never send + * a package that is smaller than maxpacket. Hence the length of each + * scatterlist element except the last must be divisible by the + * Bulk maxpacket value. + * If the HC does not ensure that through SG, + * the upper layer must do that. We must assume nothing + * about the capabilities off the HC, so we use the most + * pessimistic requirement. + */ + + maxp = usb_maxpacket(devinfo->udev, devinfo->data_in_pipe, 0); + blk_queue_virt_boundary(sdev->request_queue, maxp - 1); + + /* + * The protocol has no requirements on alignment in the strict sense. + * Controllers may or may not have alignment restrictions. + * As this is not exported, we use an extremely conservative guess. */ blk_queue_update_dma_alignment(sdev->request_queue, (512 - 1)); diff --git a/drivers/usb/storage/unusual_realtek.h b/drivers/usb/storage/unusual_realtek.h index f5fc3271e19c..e2c5491a411b 100644 --- a/drivers/usb/storage/unusual_realtek.h +++ b/drivers/usb/storage/unusual_realtek.h @@ -28,6 +28,11 @@ UNUSUAL_DEV(0x0bda, 0x0138, 0x0000, 0x9999, "USB Card Reader", USB_SC_DEVICE, USB_PR_DEVICE, init_realtek_cr, 0), +UNUSUAL_DEV(0x0bda, 0x0153, 0x0000, 0x9999, + "Realtek", + "USB Card Reader", + USB_SC_DEVICE, USB_PR_DEVICE, init_realtek_cr, 0), + UNUSUAL_DEV(0x0bda, 0x0158, 0x0000, 0x9999, "Realtek", "USB Card Reader", diff --git a/drivers/usb/usbip/stub_rx.c b/drivers/usb/usbip/stub_rx.c index 56cacb68040c..808e3a317954 100644 --- a/drivers/usb/usbip/stub_rx.c +++ b/drivers/usb/usbip/stub_rx.c @@ -380,22 +380,10 @@ static int get_pipe(struct stub_device *sdev, struct usbip_header *pdu) } if (usb_endpoint_xfer_isoc(epd)) { - /* validate packet size and number of packets */ - unsigned int maxp, packets, bytes; - -#define USB_EP_MAXP_MULT_SHIFT 11 -#define USB_EP_MAXP_MULT_MASK (3 << USB_EP_MAXP_MULT_SHIFT) -#define USB_EP_MAXP_MULT(m) \ - (((m) & USB_EP_MAXP_MULT_MASK) >> USB_EP_MAXP_MULT_SHIFT) - - maxp = usb_endpoint_maxp(epd); - maxp *= (USB_EP_MAXP_MULT( - __le16_to_cpu(epd->wMaxPacketSize)) + 1); - bytes = pdu->u.cmd_submit.transfer_buffer_length; - packets = DIV_ROUND_UP(bytes, maxp); - + /* validate number of packets */ if (pdu->u.cmd_submit.number_of_packets < 0 || - pdu->u.cmd_submit.number_of_packets > packets) { + pdu->u.cmd_submit.number_of_packets > + USBIP_MAX_ISO_PACKETS) { dev_err(&sdev->udev->dev, "CMD_SUBMIT: isoc invalid num packets %d\n", pdu->u.cmd_submit.number_of_packets); diff --git a/drivers/usb/usbip/usbip_common.h b/drivers/usb/usbip/usbip_common.h index 0fc5ace57c0e..af903aa4ad90 100644 --- a/drivers/usb/usbip/usbip_common.h +++ b/drivers/usb/usbip/usbip_common.h @@ -134,6 +134,13 @@ extern struct device_attribute dev_attr_usbip_debug; #define USBIP_DIR_OUT 0x00 #define USBIP_DIR_IN 0x01 +/* + * Arbitrary limit for the maximum number of isochronous packets in an URB, + * compare for example the uhci_submit_isochronous function in + * drivers/usb/host/uhci-q.c + */ +#define USBIP_MAX_ISO_PACKETS 1024 + /** * struct usbip_header_basic - data pertinent to every request * @command: the usbip request type diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c index b31b84f56e8f..47b229fa5e8e 100644 --- a/drivers/vfio/pci/vfio_pci.c +++ b/drivers/vfio/pci/vfio_pci.c @@ -1191,11 +1191,11 @@ static void __init vfio_pci_fill_ids(void) rc = pci_add_dynid(&vfio_pci_driver, vendor, device, subvendor, subdevice, class, class_mask, 0); if (rc) - pr_warn("failed to add dynamic id [%04hx:%04hx[%04hx:%04hx]] class %#08x/%08x (%d)\n", + pr_warn("failed to add dynamic id [%04x:%04x[%04x:%04x]] class %#08x/%08x (%d)\n", vendor, device, subvendor, subdevice, class, class_mask, rc); else - pr_info("add [%04hx:%04hx[%04hx:%04hx]] class %#08x/%08x\n", + pr_info("add [%04x:%04x[%04x:%04x]] class %#08x/%08x\n", vendor, device, subvendor, subdevice, class, class_mask); } diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c index 2fa280671c1e..875634d0d020 100644 --- a/drivers/vfio/vfio_iommu_type1.c +++ b/drivers/vfio/vfio_iommu_type1.c @@ -53,10 +53,16 @@ module_param_named(disable_hugepages, MODULE_PARM_DESC(disable_hugepages, "Disable VFIO IOMMU support for IOMMU hugepages."); +static unsigned int dma_entry_limit __read_mostly = U16_MAX; +module_param_named(dma_entry_limit, dma_entry_limit, uint, 0644); +MODULE_PARM_DESC(dma_entry_limit, + "Maximum number of user DMA mappings per container (65535)."); + struct vfio_iommu { struct list_head domain_list; struct mutex lock; struct rb_root dma_list; + unsigned int dma_avail; bool v2; bool nesting; }; @@ -382,6 +388,7 @@ static void vfio_remove_dma(struct vfio_iommu *iommu, struct vfio_dma *dma) vfio_unmap_unpin(iommu, dma); vfio_unlink_dma(iommu, dma); kfree(dma); + iommu->dma_avail++; } static unsigned long vfio_pgsize_bitmap(struct vfio_iommu *iommu) @@ -582,12 +589,18 @@ static int vfio_dma_do_map(struct vfio_iommu *iommu, return -EEXIST; } + if (!iommu->dma_avail) { + mutex_unlock(&iommu->lock); + return -ENOSPC; + } + dma = kzalloc(sizeof(*dma), GFP_KERNEL); if (!dma) { mutex_unlock(&iommu->lock); return -ENOMEM; } + iommu->dma_avail--; dma->iova = iova; dma->vaddr = vaddr; dma->prot = prot; @@ -903,6 +916,7 @@ static void *vfio_iommu_type1_open(unsigned long arg) INIT_LIST_HEAD(&iommu->domain_list); iommu->dma_list = RB_ROOT; + iommu->dma_avail = dma_entry_limit; mutex_init(&iommu->lock); return iommu; diff --git a/drivers/video/fbdev/core/fbcmap.c b/drivers/video/fbdev/core/fbcmap.c index a04fa896361a..1f8b480b1a4d 100644 --- a/drivers/video/fbdev/core/fbcmap.c +++ b/drivers/video/fbdev/core/fbcmap.c @@ -94,6 +94,8 @@ int fb_alloc_cmap_gfp(struct fb_cmap *cmap, int len, int transp, gfp_t flags) int size = len * sizeof(u16); int ret = -ENOMEM; + flags |= __GFP_NOWARN; + if (cmap->len != len) { fb_dealloc_cmap(cmap); if (!len) diff --git a/drivers/video/fbdev/core/modedb.c b/drivers/video/fbdev/core/modedb.c index de119f11b78f..455a15f70172 100644 --- a/drivers/video/fbdev/core/modedb.c +++ b/drivers/video/fbdev/core/modedb.c @@ -933,6 +933,9 @@ void fb_var_to_videomode(struct fb_videomode *mode, if (var->vmode & FB_VMODE_DOUBLE) vtotal *= 2; + if (!htotal || !vtotal) + return; + hfreq = pixclock/htotal; mode->refresh = hfreq/vtotal; } diff --git a/drivers/video/fbdev/hgafb.c b/drivers/video/fbdev/hgafb.c index 15d3ccff2965..4a397c7c1b56 100644 --- a/drivers/video/fbdev/hgafb.c +++ b/drivers/video/fbdev/hgafb.c @@ -285,6 +285,8 @@ static int hga_card_detect(void) hga_vram_len = 0x08000; hga_vram = ioremap(0xb0000, hga_vram_len); + if (!hga_vram) + goto error; if (request_region(0x3b0, 12, "hgafb")) release_io_ports = 1; diff --git a/drivers/video/fbdev/imsttfb.c b/drivers/video/fbdev/imsttfb.c index 9b167f7ef6c6..4994a540f680 100644 --- a/drivers/video/fbdev/imsttfb.c +++ b/drivers/video/fbdev/imsttfb.c @@ -1517,6 +1517,11 @@ static int imsttfb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) info->fix.smem_start = addr; info->screen_base = (__u8 *)ioremap(addr, par->ramdac == IBM ? 0x400000 : 0x800000); + if (!info->screen_base) { + release_mem_region(addr, size); + framebuffer_release(info); + return -ENOMEM; + } info->fix.mmio_start = addr + 0x800000; par->dc_regs = ioremap(addr + 0x800000, 0x1000); par->cmap_regs_phys = addr + 0x840000; diff --git a/drivers/video/fbdev/msm/mdss_debug.c b/drivers/video/fbdev/msm/mdss_debug.c index e9989fbdd2ba..9ccd290ba2a6 100644 --- a/drivers/video/fbdev/msm/mdss_debug.c +++ b/drivers/video/fbdev/msm/mdss_debug.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2009-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2009-2019, 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 @@ -1450,6 +1450,9 @@ static inline struct mdss_mdp_misr_map *mdss_misr_get_map(u32 block_id, case MDSS_MDP_INTF2: block_id = DISPLAY_MISR_DSI1; break; + case MDSS_MDP_INTF3: + block_id = DISPLAY_MISR_HDMI; + break; default: pr_err("Unmatch INTF for Dual LM single display configuration, INTF:%d\n", ctl->intf_num); diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c index 695db66d8c26..419991a98d4e 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.c +++ b/drivers/video/fbdev/msm/mdss_dsi.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2019, 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 @@ -925,7 +925,8 @@ static ssize_t mdss_dsi_cmd_write(struct file *file, const char __user *p, static int mdss_dsi_cmd_flush(struct file *file, fl_owner_t id) { struct buf_data *pcmds = file->private_data; - int blen, len, i; + unsigned int len; + int blen, i; char *buf, *bufp, *bp; struct dsi_ctrl_hdr *dchdr; @@ -968,7 +969,7 @@ static int mdss_dsi_cmd_flush(struct file *file, fl_owner_t id) while (len >= sizeof(*dchdr)) { dchdr = (struct dsi_ctrl_hdr *)bp; dchdr->dlen = ntohs(dchdr->dlen); - if (dchdr->dlen > len || dchdr->dlen < 0) { + if (dchdr->dlen > (len - sizeof(*dchdr)) || dchdr->dlen < 0) { pr_err("%s: dtsi cmd=%x error, len=%d\n", __func__, dchdr->dtype, dchdr->dlen); kfree(buf); diff --git a/drivers/video/fbdev/sm712.h b/drivers/video/fbdev/sm712.h index aad1cc4be34a..c7ebf03b8d53 100644 --- a/drivers/video/fbdev/sm712.h +++ b/drivers/video/fbdev/sm712.h @@ -15,14 +15,10 @@ #define FB_ACCEL_SMI_LYNX 88 -#define SCREEN_X_RES 1024 -#define SCREEN_Y_RES 600 -#define SCREEN_BPP 16 - -/*Assume SM712 graphics chip has 4MB VRAM */ -#define SM712_VIDEOMEMORYSIZE 0x00400000 -/*Assume SM722 graphics chip has 8MB VRAM */ -#define SM722_VIDEOMEMORYSIZE 0x00800000 +#define SCREEN_X_RES 1024 +#define SCREEN_Y_RES_PC 768 +#define SCREEN_Y_RES_NETBOOK 600 +#define SCREEN_BPP 16 #define dac_reg (0x3c8) #define dac_val (0x3c9) diff --git a/drivers/video/fbdev/sm712fb.c b/drivers/video/fbdev/sm712fb.c index 86ae1d4556fc..589ac7e75413 100644 --- a/drivers/video/fbdev/sm712fb.c +++ b/drivers/video/fbdev/sm712fb.c @@ -530,6 +530,65 @@ static const struct modeinit vgamode[] = { 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x15, 0x03, }, }, + { /* 1024 x 768 16Bpp 60Hz */ + 1024, 768, 16, 60, + /* Init_MISC */ + 0xEB, + { /* Init_SR0_SR4 */ + 0x03, 0x01, 0x0F, 0x03, 0x0E, + }, + { /* Init_SR10_SR24 */ + 0xF3, 0xB6, 0xC0, 0xDD, 0x00, 0x0E, 0x17, 0x2C, + 0x99, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xC4, 0x30, 0x02, 0x01, 0x01, + }, + { /* Init_SR30_SR75 */ + 0x38, 0x03, 0x20, 0x09, 0xC0, 0x3A, 0x3A, 0x3A, + 0x3A, 0x3A, 0x3A, 0x3A, 0x00, 0x00, 0x03, 0xFF, + 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC, + 0x20, 0x0C, 0x44, 0x20, 0x00, 0x00, 0x00, 0x3A, + 0x06, 0x68, 0xA7, 0x7F, 0x83, 0x24, 0xFF, 0x03, + 0x0F, 0x60, 0x59, 0x3A, 0x3A, 0x00, 0x00, 0x3A, + 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00, + 0x50, 0x03, 0x74, 0x14, 0x3B, 0x0D, 0x09, 0x02, + 0x04, 0x45, 0x30, 0x30, 0x40, 0x20, + }, + { /* Init_SR80_SR93 */ + 0xFF, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3A, + 0xF7, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3A, 0x3A, + 0x00, 0x00, 0x00, 0x00, + }, + { /* Init_SRA0_SRAF */ + 0x00, 0xFB, 0x9F, 0x01, 0x00, 0xED, 0xED, 0xED, + 0x7B, 0xFB, 0xFF, 0xFF, 0x97, 0xEF, 0xBF, 0xDF, + }, + { /* Init_GR00_GR08 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F, + 0xFF, + }, + { /* Init_AR00_AR14 */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x41, 0x00, 0x0F, 0x00, 0x00, + }, + { /* Init_CR00_CR18 */ + 0xA3, 0x7F, 0x7F, 0x00, 0x85, 0x16, 0x24, 0xF5, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x09, 0xFF, 0x80, 0x40, 0xFF, 0x00, 0xE3, + 0xFF, + }, + { /* Init_CR30_CR4D */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20, + 0x00, 0x00, 0x00, 0x40, 0x00, 0xFF, 0xBF, 0xFF, + 0xA3, 0x7F, 0x00, 0x86, 0x15, 0x24, 0xFF, 0x00, + 0x01, 0x07, 0xE5, 0x20, 0x7F, 0xFF, + }, + { /* Init_CR90_CRA7 */ + 0x55, 0xD9, 0x5D, 0xE1, 0x86, 0x1B, 0x8E, 0x26, + 0xDA, 0x8D, 0xDE, 0x94, 0x00, 0x00, 0x18, 0x00, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x15, 0x03, + }, + }, { /* mode#5: 1024 x 768 24Bpp 60Hz */ 1024, 768, 24, 60, /* Init_MISC */ @@ -827,67 +886,80 @@ static inline unsigned int chan_to_field(unsigned int chan, static int smtc_blank(int blank_mode, struct fb_info *info) { + struct smtcfb_info *sfb = info->par; + /* clear DPMS setting */ switch (blank_mode) { case FB_BLANK_UNBLANK: /* Screen On: HSync: On, VSync : On */ + + switch (sfb->chip_id) { + case 0x710: + case 0x712: + smtc_seqw(0x6a, 0x16); + smtc_seqw(0x6b, 0x02); + break; + case 0x720: + smtc_seqw(0x6a, 0x0d); + smtc_seqw(0x6b, 0x02); + break; + } + + smtc_seqw(0x23, (smtc_seqr(0x23) & (~0xc0))); smtc_seqw(0x01, (smtc_seqr(0x01) & (~0x20))); - smtc_seqw(0x6a, 0x16); - smtc_seqw(0x6b, 0x02); smtc_seqw(0x21, (smtc_seqr(0x21) & 0x77)); smtc_seqw(0x22, (smtc_seqr(0x22) & (~0x30))); - smtc_seqw(0x23, (smtc_seqr(0x23) & (~0xc0))); - smtc_seqw(0x24, (smtc_seqr(0x24) | 0x01)); smtc_seqw(0x31, (smtc_seqr(0x31) | 0x03)); + smtc_seqw(0x24, (smtc_seqr(0x24) | 0x01)); break; case FB_BLANK_NORMAL: /* Screen Off: HSync: On, VSync : On Soft blank */ + smtc_seqw(0x24, (smtc_seqr(0x24) | 0x01)); + smtc_seqw(0x31, ((smtc_seqr(0x31) & (~0x07)) | 0x00)); + smtc_seqw(0x23, (smtc_seqr(0x23) & (~0xc0))); smtc_seqw(0x01, (smtc_seqr(0x01) & (~0x20))); + smtc_seqw(0x22, (smtc_seqr(0x22) & (~0x30))); smtc_seqw(0x6a, 0x16); smtc_seqw(0x6b, 0x02); - smtc_seqw(0x22, (smtc_seqr(0x22) & (~0x30))); - smtc_seqw(0x23, (smtc_seqr(0x23) & (~0xc0))); - smtc_seqw(0x24, (smtc_seqr(0x24) | 0x01)); - smtc_seqw(0x31, ((smtc_seqr(0x31) & (~0x07)) | 0x00)); break; case FB_BLANK_VSYNC_SUSPEND: /* Screen On: HSync: On, VSync : Off */ + smtc_seqw(0x24, (smtc_seqr(0x24) & (~0x01))); + smtc_seqw(0x31, ((smtc_seqr(0x31) & (~0x07)) | 0x00)); + smtc_seqw(0x23, ((smtc_seqr(0x23) & (~0xc0)) | 0x20)); smtc_seqw(0x01, (smtc_seqr(0x01) | 0x20)); - smtc_seqw(0x20, (smtc_seqr(0x20) & (~0xB0))); - smtc_seqw(0x6a, 0x0c); - smtc_seqw(0x6b, 0x02); smtc_seqw(0x21, (smtc_seqr(0x21) | 0x88)); + smtc_seqw(0x20, (smtc_seqr(0x20) & (~0xB0))); smtc_seqw(0x22, ((smtc_seqr(0x22) & (~0x30)) | 0x20)); - smtc_seqw(0x23, ((smtc_seqr(0x23) & (~0xc0)) | 0x20)); - smtc_seqw(0x24, (smtc_seqr(0x24) & (~0x01))); - smtc_seqw(0x31, ((smtc_seqr(0x31) & (~0x07)) | 0x00)); smtc_seqw(0x34, (smtc_seqr(0x34) | 0x80)); + smtc_seqw(0x6a, 0x0c); + smtc_seqw(0x6b, 0x02); break; case FB_BLANK_HSYNC_SUSPEND: /* Screen On: HSync: Off, VSync : On */ + smtc_seqw(0x24, (smtc_seqr(0x24) & (~0x01))); + smtc_seqw(0x31, ((smtc_seqr(0x31) & (~0x07)) | 0x00)); + smtc_seqw(0x23, ((smtc_seqr(0x23) & (~0xc0)) | 0xD8)); smtc_seqw(0x01, (smtc_seqr(0x01) | 0x20)); - smtc_seqw(0x20, (smtc_seqr(0x20) & (~0xB0))); - smtc_seqw(0x6a, 0x0c); - smtc_seqw(0x6b, 0x02); smtc_seqw(0x21, (smtc_seqr(0x21) | 0x88)); + smtc_seqw(0x20, (smtc_seqr(0x20) & (~0xB0))); smtc_seqw(0x22, ((smtc_seqr(0x22) & (~0x30)) | 0x10)); - smtc_seqw(0x23, ((smtc_seqr(0x23) & (~0xc0)) | 0xD8)); - smtc_seqw(0x24, (smtc_seqr(0x24) & (~0x01))); - smtc_seqw(0x31, ((smtc_seqr(0x31) & (~0x07)) | 0x00)); smtc_seqw(0x34, (smtc_seqr(0x34) | 0x80)); + smtc_seqw(0x6a, 0x0c); + smtc_seqw(0x6b, 0x02); break; case FB_BLANK_POWERDOWN: /* Screen On: HSync: Off, VSync : Off */ + smtc_seqw(0x24, (smtc_seqr(0x24) & (~0x01))); + smtc_seqw(0x31, ((smtc_seqr(0x31) & (~0x07)) | 0x00)); + smtc_seqw(0x23, ((smtc_seqr(0x23) & (~0xc0)) | 0xD8)); smtc_seqw(0x01, (smtc_seqr(0x01) | 0x20)); - smtc_seqw(0x20, (smtc_seqr(0x20) & (~0xB0))); - smtc_seqw(0x6a, 0x0c); - smtc_seqw(0x6b, 0x02); smtc_seqw(0x21, (smtc_seqr(0x21) | 0x88)); + smtc_seqw(0x20, (smtc_seqr(0x20) & (~0xB0))); smtc_seqw(0x22, ((smtc_seqr(0x22) & (~0x30)) | 0x30)); - smtc_seqw(0x23, ((smtc_seqr(0x23) & (~0xc0)) | 0xD8)); - smtc_seqw(0x24, (smtc_seqr(0x24) & (~0x01))); - smtc_seqw(0x31, ((smtc_seqr(0x31) & (~0x07)) | 0x00)); smtc_seqw(0x34, (smtc_seqr(0x34) | 0x80)); + smtc_seqw(0x6a, 0x0c); + smtc_seqw(0x6b, 0x02); break; default: return -EINVAL; @@ -1144,8 +1216,10 @@ static void sm7xx_set_timing(struct smtcfb_info *sfb) /* init SEQ register SR30 - SR75 */ for (i = 0; i < SIZE_SR30_SR75; i++) - if ((i + 0x30) != 0x62 && (i + 0x30) != 0x6a && - (i + 0x30) != 0x6b) + if ((i + 0x30) != 0x30 && (i + 0x30) != 0x62 && + (i + 0x30) != 0x6a && (i + 0x30) != 0x6b && + (i + 0x30) != 0x70 && (i + 0x30) != 0x71 && + (i + 0x30) != 0x74 && (i + 0x30) != 0x75) smtc_seqw(i + 0x30, vgamode[j].init_sr30_sr75[i]); @@ -1170,8 +1244,12 @@ static void sm7xx_set_timing(struct smtcfb_info *sfb) smtc_crtcw(i, vgamode[j].init_cr00_cr18[i]); /* init CRTC register CR30 - CR4D */ - for (i = 0; i < SIZE_CR30_CR4D; i++) + for (i = 0; i < SIZE_CR30_CR4D; i++) { + if ((i + 0x30) >= 0x3B && (i + 0x30) <= 0x3F) + /* side-effect, don't write to CR3B-CR3F */ + continue; smtc_crtcw(i + 0x30, vgamode[j].init_cr30_cr4d[i]); + } /* init CRTC register CR90 - CRA7 */ for (i = 0; i < SIZE_CR90_CRA7; i++) @@ -1322,6 +1400,11 @@ static int smtc_map_smem(struct smtcfb_info *sfb, { sfb->fb->fix.smem_start = pci_resource_start(pdev, 0); + if (sfb->chip_id == 0x720) + /* on SM720, the framebuffer starts at the 1 MB offset */ + sfb->fb->fix.smem_start += 0x00200000; + + /* XXX: is it safe for SM720 on Big-Endian? */ if (sfb->fb->var.bits_per_pixel == 32) sfb->fb->fix.smem_start += big_addr; @@ -1359,12 +1442,82 @@ static inline void sm7xx_init_hw(void) outb_p(0x11, 0x3c5); } +static u_long sm7xx_vram_probe(struct smtcfb_info *sfb) +{ + u8 vram; + + switch (sfb->chip_id) { + case 0x710: + case 0x712: + /* + * Assume SM712 graphics chip has 4MB VRAM. + * + * FIXME: SM712 can have 2MB VRAM, which is used on earlier + * laptops, such as IBM Thinkpad 240X. This driver would + * probably crash on those machines. If anyone gets one of + * those and is willing to help, run "git blame" and send me + * an E-mail. + */ + return 0x00400000; + case 0x720: + outb_p(0x76, 0x3c4); + vram = inb_p(0x3c5) >> 6; + + if (vram == 0x00) + return 0x00800000; /* 8 MB */ + else if (vram == 0x01) + return 0x01000000; /* 16 MB */ + else if (vram == 0x02) + return 0x00400000; /* illegal, fallback to 4 MB */ + else if (vram == 0x03) + return 0x00400000; /* 4 MB */ + } + return 0; /* unknown hardware */ +} + +static void sm7xx_resolution_probe(struct smtcfb_info *sfb) +{ + /* get mode parameter from smtc_scr_info */ + if (smtc_scr_info.lfb_width != 0) { + sfb->fb->var.xres = smtc_scr_info.lfb_width; + sfb->fb->var.yres = smtc_scr_info.lfb_height; + sfb->fb->var.bits_per_pixel = smtc_scr_info.lfb_depth; + goto final; + } + + /* + * No parameter, default resolution is 1024x768-16. + * + * FIXME: earlier laptops, such as IBM Thinkpad 240X, has a 800x600 + * panel, also see the comments about Thinkpad 240X above. + */ + sfb->fb->var.xres = SCREEN_X_RES; + sfb->fb->var.yres = SCREEN_Y_RES_PC; + sfb->fb->var.bits_per_pixel = SCREEN_BPP; + +#ifdef CONFIG_MIPS + /* + * Loongson MIPS netbooks use 1024x600 LCD panels, which is the original + * target platform of this driver, but nearly all old x86 laptops have + * 1024x768. Lighting 768 panels using 600's timings would partially + * garble the display, so we don't want that. But it's not possible to + * distinguish them reliably. + * + * So we change the default to 768, but keep 600 as-is on MIPS. + */ + sfb->fb->var.yres = SCREEN_Y_RES_NETBOOK; +#endif + +final: + big_pixel_depth(sfb->fb->var.bits_per_pixel, smtc_scr_info.lfb_depth); +} + static int smtcfb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct smtcfb_info *sfb; struct fb_info *info; - u_long smem_size = 0x00800000; /* default 8MB */ + u_long smem_size; int err; unsigned long mmio_base; @@ -1404,29 +1557,19 @@ static int smtcfb_pci_probe(struct pci_dev *pdev, sm7xx_init_hw(); - /* get mode parameter from smtc_scr_info */ - if (smtc_scr_info.lfb_width != 0) { - sfb->fb->var.xres = smtc_scr_info.lfb_width; - sfb->fb->var.yres = smtc_scr_info.lfb_height; - sfb->fb->var.bits_per_pixel = smtc_scr_info.lfb_depth; - } else { - /* default resolution 1024x600 16bit mode */ - sfb->fb->var.xres = SCREEN_X_RES; - sfb->fb->var.yres = SCREEN_Y_RES; - sfb->fb->var.bits_per_pixel = SCREEN_BPP; - } - - big_pixel_depth(sfb->fb->var.bits_per_pixel, smtc_scr_info.lfb_depth); /* Map address and memory detection */ mmio_base = pci_resource_start(pdev, 0); pci_read_config_byte(pdev, PCI_REVISION_ID, &sfb->chip_rev_id); + smem_size = sm7xx_vram_probe(sfb); + dev_info(&pdev->dev, "%lu MiB of VRAM detected.\n", + smem_size / 1048576); + switch (sfb->chip_id) { case 0x710: case 0x712: sfb->fb->fix.mmio_start = mmio_base + 0x00400000; sfb->fb->fix.mmio_len = 0x00400000; - smem_size = SM712_VIDEOMEMORYSIZE; sfb->lfb = ioremap(mmio_base, mmio_addr); if (!sfb->lfb) { dev_err(&pdev->dev, @@ -1458,8 +1601,7 @@ static int smtcfb_pci_probe(struct pci_dev *pdev, case 0x720: sfb->fb->fix.mmio_start = mmio_base; sfb->fb->fix.mmio_len = 0x00200000; - smem_size = SM722_VIDEOMEMORYSIZE; - sfb->dp_regs = ioremap(mmio_base, 0x00a00000); + sfb->dp_regs = ioremap(mmio_base, 0x00200000 + smem_size); sfb->lfb = sfb->dp_regs + 0x00200000; sfb->mmio = (smtc_regbaseaddress = sfb->dp_regs + 0x000c0000); @@ -1476,6 +1618,9 @@ static int smtcfb_pci_probe(struct pci_dev *pdev, goto failed_fb; } + /* probe and decide resolution */ + sm7xx_resolution_probe(sfb); + /* can support 32 bpp */ if (15 == sfb->fb->var.bits_per_pixel) sfb->fb->var.bits_per_pixel = 16; @@ -1486,7 +1631,11 @@ static int smtcfb_pci_probe(struct pci_dev *pdev, if (err) goto failed; - smtcfb_setmode(sfb); + /* + * The screen would be temporarily garbled when sm712fb takes over + * vesafb or VGA text mode. Zero the framebuffer. + */ + memset_io(sfb->lfb, 0, sfb->fb->fix.smem_len); err = register_framebuffer(info); if (err < 0) diff --git a/drivers/virt/fsl_hypervisor.c b/drivers/virt/fsl_hypervisor.c index 590a0f51a249..9f96c7e61387 100644 --- a/drivers/virt/fsl_hypervisor.c +++ b/drivers/virt/fsl_hypervisor.c @@ -215,6 +215,9 @@ static long ioctl_memcpy(struct fsl_hv_ioctl_memcpy __user *p) * hypervisor. */ lb_offset = param.local_vaddr & (PAGE_SIZE - 1); + if (param.count == 0 || + param.count > U64_MAX - lb_offset - PAGE_SIZE + 1) + return -EINVAL; num_pages = (param.count + lb_offset + PAGE_SIZE - 1) >> PAGE_SHIFT; /* Allocate the buffers we need */ @@ -335,8 +338,8 @@ static long ioctl_dtprop(struct fsl_hv_ioctl_prop __user *p, int set) struct fsl_hv_ioctl_prop param; char __user *upath, *upropname; void __user *upropval; - char *path = NULL, *propname = NULL; - void *propval = NULL; + char *path, *propname; + void *propval; int ret = 0; /* Get the parameters from the user. */ @@ -348,32 +351,30 @@ static long ioctl_dtprop(struct fsl_hv_ioctl_prop __user *p, int set) upropval = (void __user *)(uintptr_t)param.propval; path = strndup_user(upath, FH_DTPROP_MAX_PATHLEN); - if (IS_ERR(path)) { - ret = PTR_ERR(path); - goto out; - } + if (IS_ERR(path)) + return PTR_ERR(path); propname = strndup_user(upropname, FH_DTPROP_MAX_PATHLEN); if (IS_ERR(propname)) { ret = PTR_ERR(propname); - goto out; + goto err_free_path; } if (param.proplen > FH_DTPROP_MAX_PROPLEN) { ret = -EINVAL; - goto out; + goto err_free_propname; } propval = kmalloc(param.proplen, GFP_KERNEL); if (!propval) { ret = -ENOMEM; - goto out; + goto err_free_propname; } if (set) { if (copy_from_user(propval, upropval, param.proplen)) { ret = -EFAULT; - goto out; + goto err_free_propval; } param.ret = fh_partition_set_dtprop(param.handle, @@ -392,7 +393,7 @@ static long ioctl_dtprop(struct fsl_hv_ioctl_prop __user *p, int set) if (copy_to_user(upropval, propval, param.proplen) || put_user(param.proplen, &p->proplen)) { ret = -EFAULT; - goto out; + goto err_free_propval; } } } @@ -400,10 +401,12 @@ static long ioctl_dtprop(struct fsl_hv_ioctl_prop __user *p, int set) if (put_user(param.ret, &p->ret)) ret = -EFAULT; -out: - kfree(path); +err_free_propval: kfree(propval); +err_free_propname: kfree(propname); +err_free_path: + kfree(path); return ret; } diff --git a/drivers/w1/masters/ds2490.c b/drivers/w1/masters/ds2490.c index 59d74d1b47a8..2287e1be0e55 100644 --- a/drivers/w1/masters/ds2490.c +++ b/drivers/w1/masters/ds2490.c @@ -1039,15 +1039,15 @@ static int ds_probe(struct usb_interface *intf, /* alternative 3, 1ms interrupt (greatly speeds search), 64 byte bulk */ alt = 3; err = usb_set_interface(dev->udev, - intf->altsetting[alt].desc.bInterfaceNumber, alt); + intf->cur_altsetting->desc.bInterfaceNumber, alt); if (err) { dev_err(&dev->udev->dev, "Failed to set alternative setting %d " "for %d interface: err=%d.\n", alt, - intf->altsetting[alt].desc.bInterfaceNumber, err); + intf->cur_altsetting->desc.bInterfaceNumber, err); goto err_out_clear; } - iface_desc = &intf->altsetting[alt]; + iface_desc = intf->cur_altsetting; if (iface_desc->desc.bNumEndpoints != NUM_EP-1) { pr_info("Num endpoints=%d. It is not DS9490R.\n", iface_desc->desc.bNumEndpoints); diff --git a/drivers/w1/w1_io.c b/drivers/w1/w1_io.c index 282092421cc9..1a9d9ec8db4d 100644 --- a/drivers/w1/w1_io.c +++ b/drivers/w1/w1_io.c @@ -437,8 +437,7 @@ int w1_reset_resume_command(struct w1_master *dev) if (w1_reset_bus(dev)) return -1; - /* This will make only the last matched slave perform a skip ROM. */ - w1_write_8(dev, W1_RESUME_CMD); + w1_write_8(dev, dev->slave_count > 1 ? W1_RESUME_CMD : W1_SKIP_ROM); return 0; } EXPORT_SYMBOL_GPL(w1_reset_resume_command); diff --git a/drivers/xen/xen-pciback/pciback_ops.c b/drivers/xen/xen-pciback/pciback_ops.c index fb0221434f81..49c5f0e9600a 100644 --- a/drivers/xen/xen-pciback/pciback_ops.c +++ b/drivers/xen/xen-pciback/pciback_ops.c @@ -126,8 +126,6 @@ void xen_pcibk_reset_device(struct pci_dev *dev) if (pci_is_enabled(dev)) pci_disable_device(dev); - pci_write_config_word(dev, PCI_COMMAND, 0); - dev->is_busmaster = 0; } else { pci_read_config_word(dev, PCI_COMMAND, &cmd); diff --git a/drivers/xen/xenbus/xenbus_dev_frontend.c b/drivers/xen/xenbus/xenbus_dev_frontend.c index 816a0e08ef10..d7591efa7775 100644 --- a/drivers/xen/xenbus/xenbus_dev_frontend.c +++ b/drivers/xen/xenbus/xenbus_dev_frontend.c @@ -536,7 +536,7 @@ static int xenbus_file_open(struct inode *inode, struct file *filp) if (xen_store_evtchn == 0) return -ENOENT; - nonseekable_open(inode, filp); + stream_open(inode, filp); u = kzalloc(sizeof(*u), GFP_KERNEL); if (u == NULL) diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c index e2f659dc5745..81c5d07a2af1 100644 --- a/fs/btrfs/backref.c +++ b/fs/btrfs/backref.c @@ -1685,13 +1685,19 @@ int iterate_extent_inodes(struct btrfs_fs_info *fs_info, extent_item_objectid); if (!search_commit_root) { - trans = btrfs_join_transaction(fs_info->extent_root); - if (IS_ERR(trans)) - return PTR_ERR(trans); + trans = btrfs_attach_transaction(fs_info->extent_root); + if (IS_ERR(trans)) { + if (PTR_ERR(trans) != -ENOENT && + PTR_ERR(trans) != -EROFS) + return PTR_ERR(trans); + trans = NULL; + } + } + + if (trans) btrfs_get_tree_mod_seq(fs_info, &tree_mod_seq_elem); - } else { + else down_read(&fs_info->commit_root_sem); - } ret = btrfs_find_all_leafs(trans, fs_info, extent_item_objectid, tree_mod_seq_elem.seq, &refs, @@ -1721,7 +1727,7 @@ int iterate_extent_inodes(struct btrfs_fs_info *fs_info, free_leaf_list(refs); out: - if (!search_commit_root) { + if (trans) { btrfs_put_tree_mod_seq(fs_info, &tree_mod_seq_elem); btrfs_end_transaction(trans, fs_info->extent_root); } else { diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 978bbfed5a2c..df2bb4b61a00 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -10730,9 +10730,9 @@ int btrfs_error_unpin_extent_range(struct btrfs_root *root, u64 start, u64 end) * transaction. */ static int btrfs_trim_free_extents(struct btrfs_device *device, - u64 minlen, u64 *trimmed) + struct fstrim_range *range, u64 *trimmed) { - u64 start = 0, len = 0; + u64 start = range->start, len = 0; int ret; *trimmed = 0; @@ -10768,8 +10768,8 @@ static int btrfs_trim_free_extents(struct btrfs_device *device, atomic_inc(&trans->use_count); spin_unlock(&fs_info->trans_lock); - ret = find_free_dev_extent_start(trans, device, minlen, start, - &start, &len); + ret = find_free_dev_extent_start(trans, device, range->minlen, + start, &start, &len); if (trans) btrfs_put_transaction(trans); @@ -10781,6 +10781,16 @@ static int btrfs_trim_free_extents(struct btrfs_device *device, break; } + /* If we are out of the passed range break */ + if (start > range->start + range->len - 1) { + mutex_unlock(&fs_info->chunk_mutex); + ret = 0; + break; + } + + start = max(range->start, start); + len = min(range->len, len); + ret = btrfs_issue_discard(device->bdev, start, len, &bytes); up_read(&fs_info->commit_root_sem); mutex_unlock(&fs_info->chunk_mutex); @@ -10791,6 +10801,10 @@ static int btrfs_trim_free_extents(struct btrfs_device *device, start += len; *trimmed += bytes; + /* We've trimmed enough */ + if (*trimmed >= range->len) + break; + if (fatal_signal_pending(current)) { ret = -ERESTARTSYS; break; @@ -10857,8 +10871,7 @@ int btrfs_trim_fs(struct btrfs_root *root, struct fstrim_range *range) mutex_lock(&root->fs_info->fs_devices->device_list_mutex); devices = &root->fs_info->fs_devices->devices; list_for_each_entry(device, devices, dev_list) { - ret = btrfs_trim_free_extents(device, range->minlen, - &group_trimmed); + ret = btrfs_trim_free_extents(device, range, &group_trimmed); if (ret) break; diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 052973620595..d056060529f8 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1901,6 +1901,18 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) u64 len; /* + * If the inode needs a full sync, make sure we use a full range to + * avoid log tree corruption, due to hole detection racing with ordered + * extent completion for adjacent ranges, and assertion failures during + * hole detection. + */ + if (test_bit(BTRFS_INODE_NEEDS_FULL_SYNC, + &BTRFS_I(inode)->runtime_flags)) { + start = 0; + end = LLONG_MAX; + } + + /* * The range length can be represented by u64, we have to do the typecasts * to avoid signed overflow if it's [0, LLONG_MAX] eg. from fsync() */ diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c index e0ac85949067..e6aaa15505c5 100644 --- a/fs/btrfs/sysfs.c +++ b/fs/btrfs/sysfs.c @@ -733,7 +733,12 @@ int btrfs_sysfs_add_fsid(struct btrfs_fs_devices *fs_devs, fs_devs->fsid_kobj.kset = btrfs_kset; error = kobject_init_and_add(&fs_devs->fsid_kobj, &btrfs_ktype, parent, "%pU", fs_devs->fsid); - return error; + if (error) { + kobject_put(&fs_devs->fsid_kobj); + return error; + } + + return 0; } int btrfs_sysfs_add_mounted(struct btrfs_fs_info *fs_info) diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index c7190f322576..57a46093656a 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -2809,6 +2809,12 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans, log->log_transid = root->log_transid; root->log_start_pid = 0; /* + * Update or create log root item under the root's log_mutex to prevent + * races with concurrent log syncs that can lead to failure to update + * log root item because it was not created yet. + */ + ret = update_log_root(trans, log); + /* * IO has been started, blocks of the log tree have WRITTEN flag set * in their headers. new modifications of the log will be written to * new positions. so it's safe to allow log writers to go in. @@ -2827,8 +2833,6 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans, mutex_unlock(&log_root_tree->log_mutex); - ret = update_log_root(trans, log); - mutex_lock(&log_root_tree->log_mutex); if (atomic_dec_and_test(&log_root_tree->log_writers)) { /* diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index be7d187d53fd..d636e2660e62 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -1288,6 +1288,7 @@ void ceph_dentry_lru_del(struct dentry *dn) unsigned ceph_dentry_hash(struct inode *dir, struct dentry *dn) { struct ceph_inode_info *dci = ceph_inode(dir); + unsigned hash; switch (dci->i_dir_layout.dl_dir_hash) { case 0: /* for backward compat */ @@ -1295,8 +1296,11 @@ unsigned ceph_dentry_hash(struct inode *dir, struct dentry *dn) return dn->d_name.hash; default: - return ceph_str_hash(dci->i_dir_layout.dl_dir_hash, + spin_lock(&dn->d_lock); + hash = ceph_str_hash(dci->i_dir_layout.dl_dir_hash, dn->d_name.name, dn->d_name.len); + spin_unlock(&dn->d_lock); + return hash; } } diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 9f0d99094cc1..a663b676d566 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -474,6 +474,7 @@ static void ceph_i_callback(struct rcu_head *head) struct inode *inode = container_of(head, struct inode, i_rcu); struct ceph_inode_info *ci = ceph_inode(inode); + kfree(ci->i_symlink); kmem_cache_free(ceph_inode_cachep, ci); } @@ -505,7 +506,6 @@ void ceph_destroy_inode(struct inode *inode) ceph_put_snap_realm(mdsc, realm); } - kfree(ci->i_symlink); while ((n = rb_first(&ci->i_fragtree)) != NULL) { frag = rb_entry(n, struct ceph_inode_frag, node); rb_erase(n, &ci->i_fragtree); diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 35e6e0b2cf34..a5de8e22629b 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -1198,6 +1198,15 @@ static int remove_session_caps_cb(struct inode *inode, struct ceph_cap *cap, list_add(&ci->i_prealloc_cap_flush->list, &to_remove); ci->i_prealloc_cap_flush = NULL; } + + if (drop && + ci->i_wrbuffer_ref_head == 0 && + ci->i_wr_ref == 0 && + ci->i_dirty_caps == 0 && + ci->i_flushing_caps == 0) { + ceph_put_snap_context(ci->i_head_snapc); + ci->i_head_snapc = NULL; + } } spin_unlock(&ci->i_ceph_lock); while (!list_empty(&to_remove)) { diff --git a/fs/ceph/snap.c b/fs/ceph/snap.c index a485d0cdc559..3d876a1cf567 100644 --- a/fs/ceph/snap.c +++ b/fs/ceph/snap.c @@ -567,7 +567,12 @@ void ceph_queue_cap_snap(struct ceph_inode_info *ci) capsnap = NULL; update_snapc: - if (ci->i_head_snapc) { + if (ci->i_wrbuffer_ref_head == 0 && + ci->i_wr_ref == 0 && + ci->i_dirty_caps == 0 && + ci->i_flushing_caps == 0) { + ci->i_head_snapc = NULL; + } else { ci->i_head_snapc = ceph_get_snap_context(new_snapc); dout(" new snapc is %p\n", new_snapc); } diff --git a/fs/ceph/super.c b/fs/ceph/super.c index f446afada328..ab8a8c9c74f2 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -712,6 +712,12 @@ static void ceph_umount_begin(struct super_block *sb) return; } +static int ceph_remount(struct super_block *sb, int *flags, char *data) +{ + sync_filesystem(sb); + return 0; +} + static const struct super_operations ceph_super_ops = { .alloc_inode = ceph_alloc_inode, .destroy_inode = ceph_destroy_inode, @@ -719,6 +725,7 @@ static const struct super_operations ceph_super_ops = { .drop_inode = ceph_drop_inode, .sync_fs = ceph_sync_fs, .put_super = ceph_put_super, + .remount_fs = ceph_remount, .show_options = ceph_show_options, .statfs = ceph_statfs, .umount_begin = ceph_umount_begin, diff --git a/fs/char_dev.c b/fs/char_dev.c index 24b142569ca9..d0655ca89481 100644 --- a/fs/char_dev.c +++ b/fs/char_dev.c @@ -130,6 +130,12 @@ __register_chrdev_region(unsigned int major, unsigned int baseminor, ret = -EBUSY; goto out; } + + if (new_min < old_min && new_max > old_max) { + ret = -EBUSY; + goto out; + } + } cd->next = *cp; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 1904203566ae..5ec1662cfdd3 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -2829,7 +2829,9 @@ cifs_read_allocate_pages(struct cifs_readdata *rdata, unsigned int nr_pages) } if (rc) { - for (i = 0; i < nr_pages; i++) { + unsigned int nr_page_failed = i; + + for (i = 0; i < nr_page_failed; i++) { put_page(rdata->pages[i]); rdata->pages[i] = NULL; } diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index d8bd8dd36211..0f210cb5038a 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -1669,6 +1669,10 @@ cifs_do_rename(const unsigned int xid, struct dentry *from_dentry, if (rc == 0 || rc != -EBUSY) goto do_rename_exit; + /* Don't fall back to using SMB on SMB 2+ mount */ + if (server->vals->protocol_id != 0) + goto do_rename_exit; + /* open-file renames don't work across directories */ if (to_dentry->d_parent != from_dentry->d_parent) goto do_rename_exit; diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index eae3cdffaf7f..591c93de8c20 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -1329,26 +1329,28 @@ smb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, unsigned int epoch, bool *purge_cache) { char message[5] = {0}; + unsigned int new_oplock = 0; oplock &= 0xFF; if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE) return; - cinode->oplock = 0; if (oplock & SMB2_LEASE_READ_CACHING_HE) { - cinode->oplock |= CIFS_CACHE_READ_FLG; + new_oplock |= CIFS_CACHE_READ_FLG; strcat(message, "R"); } if (oplock & SMB2_LEASE_HANDLE_CACHING_HE) { - cinode->oplock |= CIFS_CACHE_HANDLE_FLG; + new_oplock |= CIFS_CACHE_HANDLE_FLG; strcat(message, "H"); } if (oplock & SMB2_LEASE_WRITE_CACHING_HE) { - cinode->oplock |= CIFS_CACHE_WRITE_FLG; + new_oplock |= CIFS_CACHE_WRITE_FLG; strcat(message, "W"); } - if (!cinode->oplock) - strcat(message, "None"); + if (!new_oplock) + strncpy(message, "None", sizeof(message)); + + cinode->oplock = new_oplock; cifs_dbg(FYI, "%s Lease granted on inode %p\n", message, &cinode->vfs_inode); } diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index a7a1b218f308..8e709b641b55 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -58,15 +58,13 @@ static void configfs_d_iput(struct dentry * dentry, if (sd) { /* Coordinate with configfs_readdir */ spin_lock(&configfs_dirent_lock); - /* Coordinate with configfs_attach_attr where will increase - * sd->s_count and update sd->s_dentry to new allocated one. - * Only set sd->dentry to null when this dentry is the only - * sd owner. - * If not do so, configfs_d_iput may run just after - * configfs_attach_attr and set sd->s_dentry to null - * even it's still in use. + /* + * Set sd->s_dentry to null only when this dentry is the one + * that is going to be killed. Otherwise configfs_d_iput may + * run just after configfs_attach_attr and set sd->s_dentry to + * NULL even it's still in use. */ - if (atomic_read(&sd->s_count) <= 2) + if (sd->s_dentry == dentry) sd->s_dentry = NULL; spin_unlock(&configfs_dirent_lock); diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index 22fe11baef2b..3530e1c3ff56 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -164,19 +164,24 @@ static int debugfs_show_options(struct seq_file *m, struct dentry *root) return 0; } -static void debugfs_evict_inode(struct inode *inode) +static void debugfs_i_callback(struct rcu_head *head) { - truncate_inode_pages_final(&inode->i_data); - clear_inode(inode); + struct inode *inode = container_of(head, struct inode, i_rcu); if (S_ISLNK(inode->i_mode)) kfree(inode->i_link); + free_inode_nonrcu(inode); +} + +static void debugfs_destroy_inode(struct inode *inode) +{ + call_rcu(&inode->i_rcu, debugfs_i_callback); } static const struct super_operations debugfs_super_operations = { .statfs = simple_statfs, .remount_fs = debugfs_remount, .show_options = debugfs_show_options, - .evict_inode = debugfs_evict_inode, + .destroy_inode = debugfs_destroy_inode, }; static struct vfsmount *debugfs_automount(struct path *path) diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 4a0b3a521399..a88c1ed81411 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -1049,6 +1049,7 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode, __le32 border; ext4_fsblk_t *ablocks = NULL; /* array of allocated blocks */ int err = 0; + size_t ext_size = 0; /* make decision: where to split? */ /* FIXME: now decision is simplest: at current extent */ @@ -1140,6 +1141,10 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode, le16_add_cpu(&neh->eh_entries, m); } + /* zero out unused area in the extent block */ + ext_size = sizeof(struct ext4_extent_header) + + sizeof(struct ext4_extent) * le16_to_cpu(neh->eh_entries); + memset(bh->b_data + ext_size, 0, inode->i_sb->s_blocksize - ext_size); ext4_extent_block_csum_set(inode, neh); set_buffer_uptodate(bh); unlock_buffer(bh); @@ -1219,6 +1224,11 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode, sizeof(struct ext4_extent_idx) * m); le16_add_cpu(&neh->eh_entries, m); } + /* zero out unused area in the extent block */ + ext_size = sizeof(struct ext4_extent_header) + + (sizeof(struct ext4_extent) * le16_to_cpu(neh->eh_entries)); + memset(bh->b_data + ext_size, 0, + inode->i_sb->s_blocksize - ext_size); ext4_extent_block_csum_set(inode, neh); set_buffer_uptodate(bh); unlock_buffer(bh); @@ -1284,6 +1294,7 @@ static int ext4_ext_grow_indepth(handle_t *handle, struct inode *inode, ext4_fsblk_t newblock, goal = 0; struct ext4_super_block *es = EXT4_SB(inode->i_sb)->s_es; int err = 0; + size_t ext_size = 0; /* Try to prepend new index to old one */ if (ext_depth(inode)) @@ -1309,9 +1320,11 @@ static int ext4_ext_grow_indepth(handle_t *handle, struct inode *inode, goto out; } + ext_size = sizeof(EXT4_I(inode)->i_data); /* move top-level index/leaf into new block */ - memmove(bh->b_data, EXT4_I(inode)->i_data, - sizeof(EXT4_I(inode)->i_data)); + memmove(bh->b_data, EXT4_I(inode)->i_data, ext_size); + /* zero out unused area in the extent block */ + memset(bh->b_data + ext_size, 0, inode->i_sb->s_blocksize - ext_size); /* set size of new block */ neh = ext_block_hdr(bh); diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 12ca9da02fdc..6691d42ede93 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -5020,7 +5020,7 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) up_write(&EXT4_I(inode)->i_data_sem); ext4_journal_stop(handle); if (error) { - if (orphan) + if (orphan && inode->i_nlink) ext4_orphan_del(NULL, inode); goto err_out; } diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index 49857101bb57..06ca0e647a97 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -577,7 +577,7 @@ group_add_out: if (err == 0) err = err2; mnt_drop_write_file(filp); - if (!err && (o_group > EXT4_SB(sb)->s_groups_count) && + if (!err && (o_group < EXT4_SB(sb)->s_groups_count) && ext4_has_group_desc_csum(sb) && test_opt(sb, INIT_INODE_TABLE)) err = ext4_register_li_request(sb, o_group); diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 0ec3689a8990..9a652931eef8 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -3866,7 +3866,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) "data=, fs mounted w/o journal"); goto failed_mount_wq; } - sbi->s_def_mount_opt &= EXT4_MOUNT_JOURNAL_CHECKSUM; + sbi->s_def_mount_opt &= ~EXT4_MOUNT_JOURNAL_CHECKSUM; clear_opt(sb, JOURNAL_CHECKSUM); clear_opt(sb, DATA_FLAGS); sbi->s_journal = NULL; diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c index 7ebf7b958f9e..3d7b46d82929 100644 --- a/fs/f2fs/acl.c +++ b/fs/f2fs/acl.c @@ -288,7 +288,7 @@ static int f2fs_acl_create_masq(struct posix_acl *acl, umode_t *mode_p) /* assert(atomic_read(acl->a_refcount) == 1); */ FOREACH_ACL_ENTRY(pa, acl, pe) { - switch(pa->e_tag) { + switch (pa->e_tag) { case ACL_USER_OBJ: pa->e_perm &= (mode >> 6) | ~S_IRWXO; mode &= (pa->e_perm << 6) | ~S_IRWXU; @@ -329,7 +329,7 @@ static int f2fs_acl_create_masq(struct posix_acl *acl, umode_t *mode_p) } *mode_p = (*mode_p & ~S_IRWXUGO) | mode; - return not_equiv; + return not_equiv; } static int f2fs_acl_create(struct inode *dir, umode_t *mode, diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 06554ea87b0a..b3cd53acbcd4 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -66,7 +66,7 @@ static struct page *__get_meta_page(struct f2fs_sb_info *sbi, pgoff_t index, .old_blkaddr = index, .new_blkaddr = index, .encrypted_page = NULL, - .is_meta = is_meta, + .is_por = !is_meta, }; int err; @@ -130,6 +130,30 @@ struct page *f2fs_get_tmp_page(struct f2fs_sb_info *sbi, pgoff_t index) return __get_meta_page(sbi, index, false); } +static bool __is_bitmap_valid(struct f2fs_sb_info *sbi, block_t blkaddr, + int type) +{ + struct seg_entry *se; + unsigned int segno, offset; + bool exist; + + if (type != DATA_GENERIC_ENHANCE && type != DATA_GENERIC_ENHANCE_READ) + return true; + + segno = GET_SEGNO(sbi, blkaddr); + offset = GET_BLKOFF_FROM_SEG0(sbi, blkaddr); + se = get_seg_entry(sbi, segno); + + exist = f2fs_test_bit(offset, se->cur_valid_map); + if (!exist && type == DATA_GENERIC_ENHANCE) { + f2fs_msg(sbi->sb, KERN_ERR, "Inconsistent error " + "blkaddr:%u, sit bitmap:%d", blkaddr, exist); + set_sbi_flag(sbi, SBI_NEED_FSCK); + WARN_ON(1); + } + return exist; +} + bool f2fs_is_valid_blkaddr(struct f2fs_sb_info *sbi, block_t blkaddr, int type) { @@ -151,15 +175,22 @@ bool f2fs_is_valid_blkaddr(struct f2fs_sb_info *sbi, return false; break; case META_POR: + if (unlikely(blkaddr >= MAX_BLKADDR(sbi) || + blkaddr < MAIN_BLKADDR(sbi))) + return false; + break; case DATA_GENERIC: + case DATA_GENERIC_ENHANCE: + case DATA_GENERIC_ENHANCE_READ: if (unlikely(blkaddr >= MAX_BLKADDR(sbi) || - blkaddr < MAIN_BLKADDR(sbi))) { - if (type == DATA_GENERIC) { - f2fs_msg(sbi->sb, KERN_WARNING, - "access invalid blkaddr:%u", blkaddr); - WARN_ON(1); - } + blkaddr < MAIN_BLKADDR(sbi))) { + f2fs_msg(sbi->sb, KERN_WARNING, + "access invalid blkaddr:%u", blkaddr); + set_sbi_flag(sbi, SBI_NEED_FSCK); + WARN_ON(1); return false; + } else { + return __is_bitmap_valid(sbi, blkaddr, type); } break; case META_GENERIC: @@ -190,7 +221,7 @@ int f2fs_ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages, REQ_RAHEAD, .encrypted_page = NULL, .in_list = false, - .is_meta = (type != META_POR), + .is_por = (type == META_POR), }; struct blk_plug plug; @@ -645,6 +676,12 @@ int f2fs_recover_orphan_inodes(struct f2fs_sb_info *sbi) if (!is_set_ckpt_flags(sbi, CP_ORPHAN_PRESENT_FLAG)) return 0; + if (bdev_read_only(sbi->sb->s_bdev)) { + f2fs_msg(sbi->sb, KERN_INFO, "write access " + "unavailable, skipping orphan cleanup"); + return 0; + } + if (s_flags & MS_RDONLY) { f2fs_msg(sbi->sb, KERN_INFO, "orphan cleanup on readonly fs"); sbi->sb->s_flags &= ~MS_RDONLY; @@ -759,13 +796,27 @@ static void write_orphan_inodes(struct f2fs_sb_info *sbi, block_t start_blk) } } +static __u32 f2fs_checkpoint_chksum(struct f2fs_sb_info *sbi, + struct f2fs_checkpoint *ckpt) +{ + unsigned int chksum_ofs = le32_to_cpu(ckpt->checksum_offset); + __u32 chksum; + + chksum = f2fs_crc32(sbi, ckpt, chksum_ofs); + if (chksum_ofs < CP_CHKSUM_OFFSET) { + chksum_ofs += sizeof(chksum); + chksum = f2fs_chksum(sbi, chksum, (__u8 *)ckpt + chksum_ofs, + F2FS_BLKSIZE - chksum_ofs); + } + return chksum; +} + static int get_checkpoint_version(struct f2fs_sb_info *sbi, block_t cp_addr, struct f2fs_checkpoint **cp_block, struct page **cp_page, unsigned long long *version) { - unsigned long blk_size = sbi->blocksize; size_t crc_offset = 0; - __u32 crc = 0; + __u32 crc; *cp_page = f2fs_get_meta_page(sbi, cp_addr); if (IS_ERR(*cp_page)) @@ -774,15 +825,27 @@ static int get_checkpoint_version(struct f2fs_sb_info *sbi, block_t cp_addr, *cp_block = (struct f2fs_checkpoint *)page_address(*cp_page); crc_offset = le32_to_cpu((*cp_block)->checksum_offset); - if (crc_offset > (blk_size - sizeof(__le32))) { + if (crc_offset < CP_MIN_CHKSUM_OFFSET || + crc_offset > CP_CHKSUM_OFFSET) { f2fs_put_page(*cp_page, 1); f2fs_msg(sbi->sb, KERN_WARNING, "invalid crc_offset: %zu", crc_offset); return -EINVAL; } - crc = cur_cp_crc(*cp_block); - if (!f2fs_crc_valid(sbi, crc, *cp_block, crc_offset)) { + if (__is_set_ckpt_flags(*cp_block, CP_LARGE_NAT_BITMAP_FLAG)) { + if (crc_offset != CP_MIN_CHKSUM_OFFSET) { + f2fs_put_page(*cp_page, 1); + f2fs_msg(sbi->sb, KERN_WARNING, + "layout of large_nat_bitmap is deprecated, " + "run fsck to repair, chksum_offset: %zu", + crc_offset); + return -EINVAL; + } + } + + crc = f2fs_checkpoint_chksum(sbi, *cp_block); + if (crc != cur_cp_crc(*cp_block)) { f2fs_put_page(*cp_page, 1); f2fs_msg(sbi->sb, KERN_WARNING, "invalid crc value"); return -EINVAL; @@ -1010,13 +1073,11 @@ retry: if (inode) { unsigned long cur_ino = inode->i_ino; - if (is_dir) - F2FS_I(inode)->cp_task = current; + F2FS_I(inode)->cp_task = current; filemap_fdatawrite(inode->i_mapping); - if (is_dir) - F2FS_I(inode)->cp_task = NULL; + F2FS_I(inode)->cp_task = NULL; iput(inode); /* We need to give cpu to another writers. */ @@ -1268,10 +1329,8 @@ static void update_ckpt_flags(struct f2fs_sb_info *sbi, struct cp_control *cpc) if (is_sbi_flag_set(sbi, SBI_QUOTA_SKIP_FLUSH)) __set_ckpt_flags(ckpt, CP_QUOTA_NEED_FSCK_FLAG); - /* - * TODO: we count on fsck.f2fs to clear this flag until we figure out - * missing cases which clear it incorrectly. - */ + else + __clear_ckpt_flags(ckpt, CP_QUOTA_NEED_FSCK_FLAG); if (is_sbi_flag_set(sbi, SBI_QUOTA_NEED_REPAIR)) __set_ckpt_flags(ckpt, CP_QUOTA_NEED_FSCK_FLAG); @@ -1392,7 +1451,7 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) get_sit_bitmap(sbi, __bitmap_ptr(sbi, SIT_BITMAP)); get_nat_bitmap(sbi, __bitmap_ptr(sbi, NAT_BITMAP)); - crc32 = f2fs_crc32(sbi, ckpt, le32_to_cpu(ckpt->checksum_offset)); + crc32 = f2fs_checkpoint_chksum(sbi, ckpt); *((__le32 *)((unsigned char *)ckpt + le32_to_cpu(ckpt->checksum_offset))) = cpu_to_le32(crc32); @@ -1476,7 +1535,11 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) clear_sbi_flag(sbi, SBI_IS_DIRTY); clear_sbi_flag(sbi, SBI_NEED_CP); clear_sbi_flag(sbi, SBI_QUOTA_SKIP_FLUSH); + + spin_lock(&sbi->stat_lock); sbi->unusable_block_count = 0; + spin_unlock(&sbi->stat_lock); + __set_cp_next_pack(sbi); /* @@ -1501,6 +1564,9 @@ int f2fs_write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) unsigned long long ckpt_ver; int err = 0; + if (f2fs_readonly(sbi->sb) || f2fs_hw_is_readonly(sbi)) + return -EROFS; + if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) { if (cpc->reason != CP_PAUSE) return 0; @@ -1517,10 +1583,6 @@ int f2fs_write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) err = -EIO; goto out; } - if (f2fs_readonly(sbi->sb)) { - err = -EROFS; - goto out; - } trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "start block_ops"); diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 11f38e2a1a27..380d5b5eec41 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -226,12 +226,14 @@ struct block_device *f2fs_target_device(struct f2fs_sb_info *sbi, struct block_device *bdev = sbi->sb->s_bdev; int i; - for (i = 0; i < sbi->s_ndevs; i++) { - if (FDEV(i).start_blk <= blk_addr && - FDEV(i).end_blk >= blk_addr) { - blk_addr -= FDEV(i).start_blk; - bdev = FDEV(i).bdev; - break; + if (f2fs_is_multi_device(sbi)) { + for (i = 0; i < sbi->s_ndevs; i++) { + if (FDEV(i).start_blk <= blk_addr && + FDEV(i).end_blk >= blk_addr) { + blk_addr -= FDEV(i).start_blk; + bdev = FDEV(i).bdev; + break; + } } } if (bio) { @@ -245,6 +247,9 @@ int f2fs_target_device_index(struct f2fs_sb_info *sbi, block_t blkaddr) { int i; + if (!f2fs_is_multi_device(sbi)) + return 0; + for (i = 0; i < sbi->s_ndevs; i++) if (FDEV(i).start_blk <= blkaddr && FDEV(i).end_blk >= blkaddr) return i; @@ -453,7 +458,7 @@ static void __submit_merged_write_cond(struct f2fs_sb_info *sbi, void f2fs_submit_merged_write(struct f2fs_sb_info *sbi, enum page_type type) { - __submit_merged_write_cond(sbi, NULL, 0, 0, type, true); + __submit_merged_write_cond(sbi, NULL, NULL, 0, type, true); } void f2fs_submit_merged_write_cond(struct f2fs_sb_info *sbi, @@ -481,7 +486,8 @@ int f2fs_submit_page_bio(struct f2fs_io_info *fio) fio->encrypted_page : fio->page; if (!f2fs_is_valid_blkaddr(fio->sbi, fio->new_blkaddr, - __is_meta_io(fio) ? META_GENERIC : DATA_GENERIC)) + fio->is_por ? META_POR : (__is_meta_io(fio) ? + META_GENERIC : DATA_GENERIC_ENHANCE))) return -EFAULT; trace_f2fs_submit_page_bio(page, fio); @@ -531,9 +537,7 @@ next: spin_unlock(&io->io_lock); } - if (__is_valid_data_blkaddr(fio->old_blkaddr)) - verify_block_addr(fio, fio->old_blkaddr); - verify_block_addr(fio, fio->new_blkaddr); + verify_fio_blkaddr(fio); bio_page = fio->encrypted_page ? fio->encrypted_page : fio->page; @@ -590,9 +594,6 @@ static struct bio *f2fs_grab_read_bio(struct inode *inode, block_t blkaddr, struct bio_post_read_ctx *ctx; unsigned int post_read_steps = 0; - if (!f2fs_is_valid_blkaddr(sbi, blkaddr, DATA_GENERIC)) - return ERR_PTR(-EFAULT); - bio = f2fs_bio_alloc(sbi, min_t(int, nr_pages, BIO_MAX_PAGES), false); if (!bio) return ERR_PTR(-ENOMEM); @@ -620,8 +621,10 @@ static struct bio *f2fs_grab_read_bio(struct inode *inode, block_t blkaddr, static int f2fs_submit_page_read(struct inode *inode, struct page *page, block_t blkaddr) { - struct bio *bio = f2fs_grab_read_bio(inode, blkaddr, 1, 0); + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct bio *bio; + bio = f2fs_grab_read_bio(inode, blkaddr, 1, 0); if (IS_ERR(bio)) return PTR_ERR(bio); @@ -633,8 +636,8 @@ static int f2fs_submit_page_read(struct inode *inode, struct page *page, return -EFAULT; } ClearPageError(page); - inc_page_count(F2FS_I_SB(inode), F2FS_RD_DATA); - __f2fs_submit_read_bio(F2FS_I_SB(inode), bio, DATA); + inc_page_count(sbi, F2FS_RD_DATA); + __f2fs_submit_read_bio(sbi, bio, DATA); return 0; } @@ -762,6 +765,11 @@ struct page *f2fs_get_read_data_page(struct inode *inode, pgoff_t index, if (f2fs_lookup_extent_cache(inode, index, &ei)) { dn.data_blkaddr = ei.blk + index - ei.fofs; + if (!f2fs_is_valid_blkaddr(F2FS_I_SB(inode), dn.data_blkaddr, + DATA_GENERIC_ENHANCE_READ)) { + err = -EFAULT; + goto put_err; + } goto got_it; } @@ -775,6 +783,13 @@ struct page *f2fs_get_read_data_page(struct inode *inode, pgoff_t index, err = -ENOENT; goto put_err; } + if (dn.data_blkaddr != NEW_ADDR && + !f2fs_is_valid_blkaddr(F2FS_I_SB(inode), + dn.data_blkaddr, + DATA_GENERIC_ENHANCE)) { + err = -EFAULT; + goto put_err; + } got_it: if (PageUptodate(page)) { unlock_page(page); @@ -1117,12 +1132,12 @@ next_block: blkaddr = datablock_addr(dn.inode, dn.node_page, dn.ofs_in_node); if (__is_valid_data_blkaddr(blkaddr) && - !f2fs_is_valid_blkaddr(sbi, blkaddr, DATA_GENERIC)) { + !f2fs_is_valid_blkaddr(sbi, blkaddr, DATA_GENERIC_ENHANCE)) { err = -EFAULT; goto sync_out; } - if (is_valid_data_blkaddr(sbi, blkaddr)) { + if (__is_valid_data_blkaddr(blkaddr)) { /* use out-place-update for driect IO under LFS mode */ if (test_opt(sbi, LFS) && flag == F2FS_GET_BLOCK_DIO && map->m_may_create) { @@ -1532,6 +1547,118 @@ out: return ret; } +static int f2fs_read_single_page(struct inode *inode, struct page *page, + unsigned nr_pages, + struct f2fs_map_blocks *map, + struct bio **bio_ret, + sector_t *last_block_in_bio, + bool is_readahead) +{ + struct bio *bio = *bio_ret; + const unsigned blkbits = inode->i_blkbits; + const unsigned blocksize = 1 << blkbits; + sector_t block_in_file; + sector_t last_block; + sector_t last_block_in_file; + sector_t block_nr; + int ret = 0; + + block_in_file = (sector_t)page->index; + last_block = block_in_file + nr_pages; + last_block_in_file = (i_size_read(inode) + blocksize - 1) >> + blkbits; + if (last_block > last_block_in_file) + last_block = last_block_in_file; + + /* just zeroing out page which is beyond EOF */ + if (block_in_file >= last_block) + goto zero_out; + /* + * Map blocks using the previous result first. + */ + if ((map->m_flags & F2FS_MAP_MAPPED) && + block_in_file > map->m_lblk && + block_in_file < (map->m_lblk + map->m_len)) + goto got_it; + + /* + * Then do more f2fs_map_blocks() calls until we are + * done with this page. + */ + map->m_lblk = block_in_file; + map->m_len = last_block - block_in_file; + + ret = f2fs_map_blocks(inode, map, 0, F2FS_GET_BLOCK_DEFAULT); + if (ret) + goto out; +got_it: + if ((map->m_flags & F2FS_MAP_MAPPED)) { + block_nr = map->m_pblk + block_in_file - map->m_lblk; + SetPageMappedToDisk(page); + + if (!PageUptodate(page) && !cleancache_get_page(page)) { + SetPageUptodate(page); + goto confused; + } + + if (!f2fs_is_valid_blkaddr(F2FS_I_SB(inode), block_nr, + DATA_GENERIC_ENHANCE_READ)) { + ret = -EFAULT; + goto out; + } + } else { +zero_out: + zero_user_segment(page, 0, PAGE_SIZE); + if (!PageUptodate(page)) + SetPageUptodate(page); + unlock_page(page); + goto out; + } + + /* + * This page will go to BIO. Do we need to send this + * BIO off first? + */ + if (bio && (*last_block_in_bio != block_nr - 1 || + !__same_bdev(F2FS_I_SB(inode), block_nr, bio))) { +submit_and_realloc: + __f2fs_submit_read_bio(F2FS_I_SB(inode), bio, DATA); + bio = NULL; + } + if (bio == NULL) { + bio = f2fs_grab_read_bio(inode, block_nr, nr_pages, + is_readahead ? REQ_RAHEAD : 0); + if (IS_ERR(bio)) { + ret = PTR_ERR(bio); + bio = NULL; + goto out; + } + } + + /* + * If the page is under writeback, we need to wait for + * its completion to see the correct decrypted data. + */ + f2fs_wait_on_block_writeback(inode, block_nr); + + if (bio_add_page(bio, page, blocksize, 0) < blocksize) + goto submit_and_realloc; + + inc_page_count(F2FS_I_SB(inode), F2FS_RD_DATA); + ClearPageError(page); + *last_block_in_bio = block_nr; + goto out; +confused: + if (bio) { + __f2fs_submit_read_bio(F2FS_I_SB(inode), bio, DATA); + bio = NULL; + } + unlock_page(page); +out: + *bio_ret = bio; + return ret; +} + /* * 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. @@ -1548,13 +1675,8 @@ static int f2fs_mpage_readpages(struct address_space *mapping, struct bio *bio = NULL; sector_t last_block_in_bio = 0; struct inode *inode = mapping->host; - const unsigned blkbits = inode->i_blkbits; - const unsigned blocksize = 1 << blkbits; - sector_t block_in_file; - sector_t last_block; - sector_t last_block_in_file; - sector_t block_nr; struct f2fs_map_blocks map; + int ret = 0; map.m_pblk = 0; map.m_lblk = 0; @@ -1576,98 +1698,13 @@ static int f2fs_mpage_readpages(struct address_space *mapping, goto next_page; } - block_in_file = (sector_t)page->index; - last_block = block_in_file + nr_pages; - last_block_in_file = (i_size_read(inode) + blocksize - 1) >> - blkbits; - if (last_block > last_block_in_file) - last_block = last_block_in_file; - - /* just zeroing out page which is beyond EOF */ - if (block_in_file >= last_block) - goto zero_out; - /* - * Map blocks using the previous result first. - */ - if ((map.m_flags & F2FS_MAP_MAPPED) && - block_in_file > map.m_lblk && - block_in_file < (map.m_lblk + map.m_len)) - goto got_it; - - /* - * Then do more f2fs_map_blocks() calls until we are - * done with this page. - */ - map.m_lblk = block_in_file; - map.m_len = last_block - block_in_file; - - if (f2fs_map_blocks(inode, &map, 0, F2FS_GET_BLOCK_DEFAULT)) - goto set_error_page; -got_it: - if ((map.m_flags & F2FS_MAP_MAPPED)) { - block_nr = map.m_pblk + block_in_file - map.m_lblk; - SetPageMappedToDisk(page); - - if (!PageUptodate(page) && !cleancache_get_page(page)) { - SetPageUptodate(page); - goto confused; - } - - if (!f2fs_is_valid_blkaddr(F2FS_I_SB(inode), block_nr, - DATA_GENERIC)) - goto set_error_page; - } else { -zero_out: + ret = f2fs_read_single_page(inode, page, nr_pages, &map, &bio, + &last_block_in_bio, is_readahead); + if (ret) { + SetPageError(page); zero_user_segment(page, 0, PAGE_SIZE); - if (!PageUptodate(page)) - SetPageUptodate(page); unlock_page(page); - goto next_page; } - - /* - * This page will go to BIO. Do we need to send this - * BIO off first? - */ - if (bio && (last_block_in_bio != block_nr - 1 || - !__same_bdev(F2FS_I_SB(inode), block_nr, bio))) { -submit_and_realloc: - __f2fs_submit_read_bio(F2FS_I_SB(inode), bio, DATA); - bio = NULL; - } - if (bio == NULL) { - bio = f2fs_grab_read_bio(inode, block_nr, nr_pages, - is_readahead ? REQ_RAHEAD : 0); - if (IS_ERR(bio)) { - bio = NULL; - goto set_error_page; - } - } - - /* - * If the page is under writeback, we need to wait for - * its completion to see the correct decrypted data. - */ - f2fs_wait_on_block_writeback(inode, block_nr); - - if (bio_add_page(bio, page, blocksize, 0) < blocksize) - goto submit_and_realloc; - - inc_page_count(F2FS_I_SB(inode), F2FS_RD_DATA); - ClearPageError(page); - last_block_in_bio = block_nr; - goto next_page; -set_error_page: - SetPageError(page); - zero_user_segment(page, 0, PAGE_SIZE); - unlock_page(page); - goto next_page; -confused: - if (bio) { - __f2fs_submit_read_bio(F2FS_I_SB(inode), bio, DATA); - bio = NULL; - } - unlock_page(page); next_page: if (pages) put_page(page); @@ -1675,7 +1712,7 @@ next_page: BUG_ON(pages && !list_empty(pages)); if (bio) __f2fs_submit_read_bio(F2FS_I_SB(inode), bio, DATA); - return 0; + return pages ? 0 : ret; } static int f2fs_read_data_page(struct file *file, struct page *page) @@ -1845,7 +1882,7 @@ int f2fs_do_write_data_page(struct f2fs_io_info *fio) fio->old_blkaddr = ei.blk + page->index - ei.fofs; if (!f2fs_is_valid_blkaddr(fio->sbi, fio->old_blkaddr, - DATA_GENERIC)) + DATA_GENERIC_ENHANCE)) return -EFAULT; ipu_force = true; @@ -1872,7 +1909,7 @@ int f2fs_do_write_data_page(struct f2fs_io_info *fio) got_it: if (__is_valid_data_blkaddr(fio->old_blkaddr) && !f2fs_is_valid_blkaddr(fio->sbi, fio->old_blkaddr, - DATA_GENERIC)) { + DATA_GENERIC_ENHANCE)) { err = -EFAULT; goto out_writepage; } @@ -1880,7 +1917,8 @@ got_it: * If current allocation needs SSR, * it had better in-place writes for updated data. */ - if (ipu_force || (is_valid_data_blkaddr(fio->sbi, fio->old_blkaddr) && + if (ipu_force || + (__is_valid_data_blkaddr(fio->old_blkaddr) && need_inplace_update(fio))) { err = encrypt_one_page(fio); if (err) @@ -1898,9 +1936,10 @@ got_it: true); if (PageWriteback(page)) end_page_writeback(page); + } else { + set_inode_flag(inode, FI_UPDATE_WRITE); } trace_f2fs_do_write_data_page(fio->page, IPU); - set_inode_flag(inode, FI_UPDATE_WRITE); return err; } @@ -2062,7 +2101,8 @@ out: } unlock_page(page); - if (!S_ISDIR(inode->i_mode) && !IS_NOQUOTA(inode)) + if (!S_ISDIR(inode->i_mode) && !IS_NOQUOTA(inode) && + !F2FS_I(inode)->cp_task) f2fs_balance_fs(sbi, need_balance_fs); if (unlikely(f2fs_cp_error(sbi))) { @@ -2533,6 +2573,11 @@ repeat: zero_user_segment(page, 0, PAGE_SIZE); SetPageUptodate(page); } else { + if (!f2fs_is_valid_blkaddr(sbi, blkaddr, + DATA_GENERIC_ENHANCE_READ)) { + err = -EFAULT; + goto fail; + } err = f2fs_submit_page_read(inode, page, blkaddr); if (err) goto fail; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 0a14e06fbc58..b70d99f6ff18 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -274,7 +274,14 @@ enum { META_SSA, META_MAX, META_POR, - DATA_GENERIC, + DATA_GENERIC, /* check range only */ + DATA_GENERIC_ENHANCE, /* strong check on range and segment bitmap */ + DATA_GENERIC_ENHANCE_READ, /* + * strong check on range and segment + * bitmap but no warning due to race + * condition of read on truncated area + * by extent_cache + */ META_GENERIC, }; @@ -1102,7 +1109,7 @@ struct f2fs_io_info { 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_meta; /* indicate borrow meta inode mapping or not */ + bool is_por; /* indicate IO is from recovery or not */ bool retry; /* need to reallocate block address */ enum iostat_type io_type; /* io type */ struct writeback_control *io_wbc; /* writeback control */ @@ -1129,8 +1136,8 @@ struct f2fs_dev_info { block_t start_blk; block_t end_blk; #ifdef CONFIG_BLK_DEV_ZONED - unsigned int nr_blkz; /* Total number of zones */ - u8 *blkz_type; /* Array of zones type */ + unsigned int nr_blkz; /* Total number of zones */ + unsigned long *blkz_seq; /* Bitmap indicating sequential zones */ #endif }; @@ -1427,6 +1434,17 @@ static inline bool time_to_inject(struct f2fs_sb_info *sbi, int type) } #endif +/* + * Test if the mounted volume is a multi-device volume. + * - For a single regular disk volume, sbi->s_ndevs is 0. + * - For a single zoned disk volume, sbi->s_ndevs is 1. + * - For a multi-device volume, sbi->s_ndevs is always 2 or more. + */ +static inline bool f2fs_is_multi_device(struct f2fs_sb_info *sbi) +{ + return sbi->s_ndevs > 1; +} + /* For write statistics. Suppose sector size is 512 bytes, * and the return value is in kbytes. s is of struct f2fs_sb_info. */ @@ -1839,6 +1857,7 @@ enospc: return -ENOSPC; } +void f2fs_msg(struct super_block *sb, const char *level, const char *fmt, ...); static inline void dec_valid_block_count(struct f2fs_sb_info *sbi, struct inode *inode, block_t count) @@ -1847,13 +1866,21 @@ static inline void dec_valid_block_count(struct f2fs_sb_info *sbi, spin_lock(&sbi->stat_lock); f2fs_bug_on(sbi, sbi->total_valid_block_count < (block_t) count); - f2fs_bug_on(sbi, inode->i_blocks < sectors); sbi->total_valid_block_count -= (block_t)count; if (sbi->reserved_blocks && sbi->current_reserved_blocks < sbi->reserved_blocks) sbi->current_reserved_blocks = min(sbi->reserved_blocks, sbi->current_reserved_blocks + count); spin_unlock(&sbi->stat_lock); + if (unlikely(inode->i_blocks < sectors)) { + f2fs_msg(sbi->sb, KERN_WARNING, + "Inconsistent i_blocks, ino:%lu, iblocks:%llu, sectors:%llu", + inode->i_ino, + (unsigned long long)inode->i_blocks, + (unsigned long long)sectors); + set_sbi_flag(sbi, SBI_NEED_FSCK); + return; + } f2fs_i_blocks_write(inode, count, false, true); } @@ -1951,7 +1978,11 @@ static inline void *__bitmap_ptr(struct f2fs_sb_info *sbi, int flag) if (is_set_ckpt_flags(sbi, CP_LARGE_NAT_BITMAP_FLAG)) { offset = (flag == SIT_BITMAP) ? le32_to_cpu(ckpt->nat_ver_bitmap_bytesize) : 0; - return &ckpt->sit_nat_version_bitmap + offset; + /* + * if large_nat_bitmap feature is enabled, leave checksum + * protection for all nat/sit bitmaps. + */ + return &ckpt->sit_nat_version_bitmap + offset + sizeof(__le32); } if (__cp_payload(sbi) > 0) { @@ -2070,7 +2101,6 @@ static inline void dec_valid_node_count(struct f2fs_sb_info *sbi, f2fs_bug_on(sbi, !sbi->total_valid_block_count); f2fs_bug_on(sbi, !sbi->total_valid_node_count); - f2fs_bug_on(sbi, !is_inode && !inode->i_blocks); sbi->total_valid_node_count--; sbi->total_valid_block_count--; @@ -2080,10 +2110,19 @@ static inline void dec_valid_node_count(struct f2fs_sb_info *sbi, spin_unlock(&sbi->stat_lock); - if (is_inode) + if (is_inode) { dquot_free_inode(inode); - else + } else { + if (unlikely(inode->i_blocks == 0)) { + f2fs_msg(sbi->sb, KERN_WARNING, + "Inconsistent i_blocks, ino:%lu, iblocks:%llu", + inode->i_ino, + (unsigned long long)inode->i_blocks); + set_sbi_flag(sbi, SBI_NEED_FSCK); + return; + } f2fs_i_blocks_write(inode, 1, false, true); + } } static inline unsigned int valid_node_count(struct f2fs_sb_info *sbi) @@ -2605,9 +2644,18 @@ static inline int f2fs_has_inline_xattr(struct inode *inode) return is_inode_flag_set(inode, FI_INLINE_XATTR); } +#define ALIGN_DOWN(x, a) __ALIGN_KERNEL((x) - ((a) - 1), (a)) + static inline unsigned int addrs_per_inode(struct inode *inode) { - return CUR_ADDRS_PER_INODE(inode) - get_inline_xattr_addrs(inode); + unsigned int addrs = CUR_ADDRS_PER_INODE(inode) - + get_inline_xattr_addrs(inode); + return ALIGN_DOWN(addrs, 1); +} + +static inline unsigned int addrs_per_block(struct inode *inode) +{ + return ALIGN_DOWN(DEF_ADDRS_PER_BLOCK, 1); } static inline void *inline_xattr_addr(struct inode *inode, struct page *page) @@ -2620,7 +2668,9 @@ static inline void *inline_xattr_addr(struct inode *inode, struct page *page) static inline int inline_xattr_size(struct inode *inode) { - return get_inline_xattr_addrs(inode) * sizeof(__le32); + if (f2fs_has_inline_xattr(inode)) + return get_inline_xattr_addrs(inode) * sizeof(__le32); + return 0; } static inline int f2fs_has_inline_data(struct inode *inode) @@ -2882,12 +2932,10 @@ static inline void f2fs_update_iostat(struct f2fs_sb_info *sbi, #define __is_large_section(sbi) ((sbi)->segs_per_sec > 1) -#define __is_meta_io(fio) (PAGE_TYPE_OF_BIO((fio)->type) == META && \ - (!is_read_io((fio)->op) || (fio)->is_meta)) +#define __is_meta_io(fio) (PAGE_TYPE_OF_BIO((fio)->type) == META) bool f2fs_is_valid_blkaddr(struct f2fs_sb_info *sbi, block_t blkaddr, int type); -void f2fs_msg(struct super_block *sb, const char *level, const char *fmt, ...); static inline void verify_blkaddr(struct f2fs_sb_info *sbi, block_t blkaddr, int type) { @@ -2906,15 +2954,6 @@ static inline bool __is_valid_data_blkaddr(block_t blkaddr) return true; } -static inline bool is_valid_data_blkaddr(struct f2fs_sb_info *sbi, - block_t blkaddr) -{ - if (!__is_valid_data_blkaddr(blkaddr)) - return false; - verify_blkaddr(sbi, blkaddr, DATA_GENERIC); - return true; -} - static inline void f2fs_set_page_private(struct page *page, unsigned long data) { @@ -3615,16 +3654,12 @@ F2FS_FEATURE_FUNCS(lost_found, LOST_FOUND); F2FS_FEATURE_FUNCS(sb_chksum, SB_CHKSUM); #ifdef CONFIG_BLK_DEV_ZONED -static inline int get_blkz_type(struct f2fs_sb_info *sbi, - struct block_device *bdev, block_t blkaddr) +static inline bool f2fs_blkz_is_seq(struct f2fs_sb_info *sbi, int devi, + block_t blkaddr) { unsigned int zno = blkaddr >> sbi->log_blocks_per_blkz; - int i; - for (i = 0; i < sbi->s_ndevs; i++) - if (FDEV(i).bdev == bdev) - return FDEV(i).blkz_type[zno]; - return -EINVAL; + return test_bit(zno, FDEV(devi).blkz_seq); } #endif @@ -3633,9 +3668,27 @@ static inline bool f2fs_hw_should_discard(struct f2fs_sb_info *sbi) return f2fs_sb_has_blkzoned(sbi); } +static inline bool f2fs_bdev_support_discard(struct block_device *bdev) +{ + return blk_queue_discard(bdev_get_queue(bdev)) || +#ifdef CONFIG_BLK_DEV_ZONED + bdev_is_zoned(bdev); +#else + 0; +#endif +} + static inline bool f2fs_hw_support_discard(struct f2fs_sb_info *sbi) { - return blk_queue_discard(bdev_get_queue(sbi->sb->s_bdev)); + int i; + + if (!f2fs_is_multi_device(sbi)) + return f2fs_bdev_support_discard(sbi->sb->s_bdev); + + for (i = 0; i < sbi->s_ndevs; i++) + if (f2fs_bdev_support_discard(FDEV(i).bdev)) + return true; + return false; } static inline bool f2fs_realtime_discard_enable(struct f2fs_sb_info *sbi) @@ -3644,6 +3697,20 @@ static inline bool f2fs_realtime_discard_enable(struct f2fs_sb_info *sbi) f2fs_hw_should_discard(sbi); } +static inline bool f2fs_hw_is_readonly(struct f2fs_sb_info *sbi) +{ + int i; + + if (!f2fs_is_multi_device(sbi)) + return bdev_read_only(sbi->sb->s_bdev); + + for (i = 0; i < sbi->s_ndevs; i++) + if (bdev_read_only(FDEV(i).bdev)) + return true; + return false; +} + + static inline void set_opt_mode(struct f2fs_sb_info *sbi, unsigned int mt) { clear_opt(sbi, ADAPTIVE); @@ -3699,7 +3766,7 @@ static inline bool f2fs_force_buffered_io(struct inode *inode, if (f2fs_post_read_required(inode)) return true; - if (sbi->s_ndevs) + if (f2fs_is_multi_device(sbi)) return true; /* * for blkzoned device, fallback direct IO to buffered IO, so @@ -3736,4 +3803,4 @@ static inline bool is_journalled_quota(struct f2fs_sb_info *sbi) return false; } -#endif +#endif /* _LINUX_F2FS_H */ diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 637a9dfbd578..8a1ad6cd63c5 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -41,6 +41,8 @@ static int f2fs_filemap_fault(struct vm_area_struct *vma, err = filemap_fault(vma, vmf); up_read(&F2FS_I(inode)->i_mmap_sem); + trace_f2fs_filemap_fault(inode, vmf->pgoff, (unsigned long)err); + return err; } @@ -359,7 +361,7 @@ static bool __found_offset(struct f2fs_sb_info *sbi, block_t blkaddr, switch (whence) { case SEEK_DATA: if ((blkaddr == NEW_ADDR && dirty == pgofs) || - is_valid_data_blkaddr(sbi, blkaddr)) + __is_valid_data_blkaddr(blkaddr)) return true; break; case SEEK_HOLE: @@ -425,7 +427,7 @@ static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence) if (__is_valid_data_blkaddr(blkaddr) && !f2fs_is_valid_blkaddr(F2FS_I_SB(inode), - blkaddr, DATA_GENERIC)) { + blkaddr, DATA_GENERIC_ENHANCE)) { f2fs_put_dnode(&dn); goto fail; } @@ -526,7 +528,8 @@ void f2fs_truncate_data_blocks_range(struct dnode_of_data *dn, int count) f2fs_set_data_blkaddr(dn); if (__is_valid_data_blkaddr(blkaddr) && - !f2fs_is_valid_blkaddr(sbi, blkaddr, DATA_GENERIC)) + !f2fs_is_valid_blkaddr(sbi, blkaddr, + DATA_GENERIC_ENHANCE)) continue; f2fs_invalidate_blocks(sbi, blkaddr); @@ -555,7 +558,7 @@ void f2fs_truncate_data_blocks_range(struct dnode_of_data *dn, int count) void f2fs_truncate_data_blocks(struct dnode_of_data *dn) { - f2fs_truncate_data_blocks_range(dn, ADDRS_PER_BLOCK); + f2fs_truncate_data_blocks_range(dn, ADDRS_PER_BLOCK(dn->inode)); } static int truncate_partial_data_page(struct inode *inode, u64 from, @@ -1013,7 +1016,8 @@ next_dnode: } else if (ret == -ENOENT) { if (dn.max_level == 0) return -ENOENT; - done = min((pgoff_t)ADDRS_PER_BLOCK - 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; @@ -1024,6 +1028,14 @@ next_dnode: for (i = 0; i < done; i++, blkaddr++, do_replace++, dn.ofs_in_node++) { *blkaddr = datablock_addr(dn.inode, dn.node_page, dn.ofs_in_node); + + if (__is_valid_data_blkaddr(*blkaddr) && + !f2fs_is_valid_blkaddr(sbi, *blkaddr, + DATA_GENERIC_ENHANCE)) { + f2fs_put_dnode(&dn); + return -EFAULT; + } + if (!f2fs_is_checkpointed_data(sbi, *blkaddr)) { if (test_opt(sbi, LFS)) { @@ -1164,7 +1176,7 @@ static int __exchange_data_block(struct inode *src_inode, int ret; while (len) { - olen = min((pgoff_t)4 * ADDRS_PER_BLOCK, len); + olen = min((pgoff_t)4 * ADDRS_PER_BLOCK(src_inode), len); src_blkaddr = f2fs_kvzalloc(F2FS_I_SB(src_inode), array_size(olen, sizeof(block_t)), @@ -2573,10 +2585,10 @@ static int f2fs_ioc_flush_device(struct file *filp, unsigned long arg) sizeof(range))) return -EFAULT; - if (sbi->s_ndevs <= 1 || sbi->s_ndevs - 1 <= range.dev_num || + if (!f2fs_is_multi_device(sbi) || sbi->s_ndevs - 1 <= range.dev_num || __is_large_section(sbi)) { f2fs_msg(sbi->sb, KERN_WARNING, - "Can't flush %u in %d for segs_per_sec %u != 1\n", + "Can't flush %u in %d for segs_per_sec %u != 1", range.dev_num, sbi->s_ndevs, sbi->segs_per_sec); return -EINVAL; @@ -2638,7 +2650,7 @@ int f2fs_pin_file_control(struct inode *inode, bool inc) if (fi->i_gc_failures[GC_FAILURE_PIN] > sbi->gc_pin_file_threshold) { f2fs_msg(sbi->sb, KERN_WARNING, - "%s: Enable GC = ino %lx after %x GC trials\n", + "%s: Enable GC = ino %lx after %x GC trials", __func__, inode->i_ino, fi->i_gc_failures[GC_FAILURE_PIN]); clear_inode_flag(inode, FI_PIN_FILE); @@ -2811,15 +2823,21 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) struct inode *inode = file_inode(file); ssize_t ret; - if (unlikely(f2fs_cp_error(F2FS_I_SB(inode)))) - return -EIO; + if (unlikely(f2fs_cp_error(F2FS_I_SB(inode)))) { + ret = -EIO; + goto out; + } - if ((iocb->ki_flags & IOCB_NOWAIT) && !(iocb->ki_flags & IOCB_DIRECT)) - return -EINVAL; + if ((iocb->ki_flags & IOCB_NOWAIT) && !(iocb->ki_flags & IOCB_DIRECT)) { + ret = -EINVAL; + goto out; + } if (!inode_trylock(inode)) { - if (iocb->ki_flags & IOCB_NOWAIT) - return -EAGAIN; + if (iocb->ki_flags & IOCB_NOWAIT) { + ret = -EAGAIN; + goto out; + } inode_lock(inode); } @@ -2832,19 +2850,16 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) if (iov_iter_fault_in_readable(from, iov_iter_count(from))) set_inode_flag(inode, FI_NO_PREALLOC); - if ((iocb->ki_flags & IOCB_NOWAIT) && - (iocb->ki_flags & IOCB_DIRECT)) { - if (!f2fs_overwrite_io(inode, iocb->ki_pos, + if ((iocb->ki_flags & IOCB_NOWAIT)) { + if (!f2fs_overwrite_io(inode, iocb->ki_pos, iov_iter_count(from)) || - f2fs_has_inline_data(inode) || - f2fs_force_buffered_io(inode, - iocb, from)) { - clear_inode_flag(inode, - FI_NO_PREALLOC); - inode_unlock(inode); - return -EAGAIN; - } - + f2fs_has_inline_data(inode) || + f2fs_force_buffered_io(inode, iocb, from)) { + clear_inode_flag(inode, FI_NO_PREALLOC); + inode_unlock(inode); + ret = -EAGAIN; + goto out; + } } else { preallocated = true; target_size = iocb->ki_pos + iov_iter_count(from); @@ -2853,7 +2868,8 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) if (err) { clear_inode_flag(inode, FI_NO_PREALLOC); inode_unlock(inode); - return err; + ret = err; + goto out; } } ret = __generic_file_write_iter(iocb, from); @@ -2867,7 +2883,9 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) f2fs_update_iostat(F2FS_I_SB(inode), APP_WRITE_IO, ret); } inode_unlock(inode); - +out: + trace_f2fs_file_write_iter(inode, iocb->ki_pos, + iov_iter_count(from), ret); if (ret > 0) { ssize_t err; diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 3624d1336bd1..e610a94c7da7 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -591,7 +591,7 @@ block_t f2fs_start_bidx_of_node(unsigned int node_ofs, struct inode *inode) int dec = (node_ofs - indirect_blks - 3) / (NIDS_PER_BLOCK + 1); bidx = node_ofs - 5 - dec; } - return bidx * ADDRS_PER_BLOCK + ADDRS_PER_INODE(inode); + return bidx * ADDRS_PER_BLOCK(inode) + ADDRS_PER_INODE(inode); } static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, @@ -656,6 +656,11 @@ static int ra_data_block(struct inode *inode, pgoff_t index) if (f2fs_lookup_extent_cache(inode, index, &ei)) { dn.data_blkaddr = ei.blk + index - ei.fofs; + if (unlikely(!f2fs_is_valid_blkaddr(sbi, dn.data_blkaddr, + DATA_GENERIC_ENHANCE_READ))) { + err = -EFAULT; + goto put_page; + } goto got_it; } @@ -665,8 +670,12 @@ static int ra_data_block(struct inode *inode, pgoff_t index) goto put_page; f2fs_put_dnode(&dn); + if (!__is_valid_data_blkaddr(dn.data_blkaddr)) { + err = -ENOENT; + goto put_page; + } if (unlikely(!f2fs_is_valid_blkaddr(sbi, dn.data_blkaddr, - DATA_GENERIC))) { + DATA_GENERIC_ENHANCE))) { err = -EFAULT; goto put_page; } @@ -1175,6 +1184,7 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, "type [%d, %d] in SSA and SIT", segno, type, GET_SUM_TYPE((&sum->footer))); set_sbi_flag(sbi, SBI_NEED_FSCK); + f2fs_stop_checkpoint(sbi, false); goto skip; } @@ -1346,7 +1356,7 @@ void f2fs_build_gc_manager(struct f2fs_sb_info *sbi) sbi->gc_pin_file_threshold = DEF_GC_FAILED_PINNED_FILES; /* give warm/cold data area from slower device */ - if (sbi->s_ndevs && !__is_large_section(sbi)) + if (f2fs_is_multi_device(sbi) && !__is_large_section(sbi)) SIT_I(sbi)->last_victim[ALLOC_NEXT] = GET_SEGNO(sbi, FDEV(0).end_blk) + 1; } diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index dcc9ba43d627..7ad88aa2aa9f 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -438,6 +438,14 @@ static int f2fs_move_inline_dirents(struct inode *dir, struct page *ipage, stat_dec_inline_dir(dir); clear_inode_flag(dir, FI_INLINE_DENTRY); + /* + * should retrieve reserved space which was used to keep + * inline_dentry's structure for backward compatibility. + */ + if (!f2fs_sb_has_flexible_inline_xattr(F2FS_I_SB(dir)) && + !f2fs_has_inline_xattr(dir)) + F2FS_I(dir)->i_inline_xattr_size = 0; + f2fs_i_depth_write(dir, 1); if (i_size_read(dir) < PAGE_SIZE) f2fs_i_size_write(dir, PAGE_SIZE); @@ -519,6 +527,15 @@ static int f2fs_move_rehashed_dirents(struct inode *dir, struct page *ipage, stat_dec_inline_dir(dir); clear_inode_flag(dir, FI_INLINE_DENTRY); + + /* + * should retrieve reserved space which was used to keep + * inline_dentry's structure for backward compatibility. + */ + if (!f2fs_sb_has_flexible_inline_xattr(F2FS_I_SB(dir)) && + !f2fs_has_inline_xattr(dir)) + F2FS_I(dir)->i_inline_xattr_size = 0; + kvfree(backup_dentry); return 0; recover: diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index d33f860c4e63..63a0fcd84492 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -73,7 +73,7 @@ static int __written_first_block(struct f2fs_sb_info *sbi, if (!__is_valid_data_blkaddr(addr)) return 1; - if (!f2fs_is_valid_blkaddr(sbi, addr, DATA_GENERIC)) + if (!f2fs_is_valid_blkaddr(sbi, addr, DATA_GENERIC_ENHANCE)) return -EFAULT; return 0; } @@ -177,8 +177,8 @@ bool f2fs_inode_chksum_verify(struct f2fs_sb_info *sbi, struct page *page) if (provided != calculated) f2fs_msg(sbi->sb, KERN_WARNING, - "checksum invalid, ino = %x, %x vs. %x", - ino_of_node(page), provided, calculated); + "checksum invalid, nid = %lu, ino_of_node = %x, %x vs. %x", + page->index, ino_of_node(page), provided, calculated); return provided == calculated; } @@ -267,9 +267,10 @@ static bool sanity_check_inode(struct inode *inode, struct page *node_page) struct extent_info *ei = &F2FS_I(inode)->extent_tree->largest; if (ei->len && - (!f2fs_is_valid_blkaddr(sbi, ei->blk, DATA_GENERIC) || + (!f2fs_is_valid_blkaddr(sbi, ei->blk, + DATA_GENERIC_ENHANCE) || !f2fs_is_valid_blkaddr(sbi, ei->blk + ei->len - 1, - DATA_GENERIC))) { + DATA_GENERIC_ENHANCE))) { set_sbi_flag(sbi, SBI_NEED_FSCK); f2fs_msg(sbi->sb, KERN_WARNING, "%s: inode (ino=%lx) extent info [%u, %u, %u] " @@ -488,6 +489,7 @@ make_now: return inode; bad_inode: + f2fs_inode_synced(inode); iget_failed(inode); trace_f2fs_iget_exit(inode, ret); return ERR_PTR(ret); diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index cd6530d22be7..aa82a5d25b66 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -143,7 +143,7 @@ fail_drop: return ERR_PTR(err); } -static int is_extension_exist(const unsigned char *s, const char *sub) +static inline int is_extension_exist(const unsigned char *s, const char *sub) { size_t slen = strlen(s); size_t sublen = strlen(sub); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index f527c2b08798..a39aa8856d17 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -454,7 +454,7 @@ static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni, new_blkaddr == NULL_ADDR); f2fs_bug_on(sbi, nat_get_blkaddr(e) == NEW_ADDR && new_blkaddr == NEW_ADDR); - f2fs_bug_on(sbi, is_valid_data_blkaddr(sbi, nat_get_blkaddr(e)) && + f2fs_bug_on(sbi, __is_valid_data_blkaddr(nat_get_blkaddr(e)) && new_blkaddr == NEW_ADDR); /* increment version no as node is removed */ @@ -465,7 +465,7 @@ static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni, /* change address */ nat_set_blkaddr(e, new_blkaddr); - if (!is_valid_data_blkaddr(sbi, new_blkaddr)) + if (!__is_valid_data_blkaddr(new_blkaddr)) set_nat_flag(e, IS_CHECKPOINTED, false); __set_nat_cache_dirty(nm_i, e); @@ -526,6 +526,7 @@ int f2fs_get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct f2fs_nat_entry ne; struct nat_entry *e; pgoff_t index; + block_t blkaddr; int i; ni->nid = nid; @@ -569,6 +570,11 @@ int f2fs_get_node_info(struct f2fs_sb_info *sbi, nid_t nid, node_info_from_raw_nat(ni, &ne); f2fs_put_page(page, 1); cache: + blkaddr = le32_to_cpu(ne.block_addr); + if (__is_valid_data_blkaddr(blkaddr) && + !f2fs_is_valid_blkaddr(sbi, blkaddr, DATA_GENERIC_ENHANCE)) + return -EFAULT; + /* cache nat entry */ cache_nat_entry(sbi, nid, &ne); return 0; @@ -600,9 +606,9 @@ static void f2fs_ra_node_pages(struct page *parent, int start, int n) pgoff_t f2fs_get_next_page_offset(struct dnode_of_data *dn, pgoff_t pgofs) { const long direct_index = ADDRS_PER_INODE(dn->inode); - const long direct_blks = ADDRS_PER_BLOCK; - const long indirect_blks = ADDRS_PER_BLOCK * NIDS_PER_BLOCK; - unsigned int skipped_unit = ADDRS_PER_BLOCK; + const long direct_blks = ADDRS_PER_BLOCK(dn->inode); + const long indirect_blks = ADDRS_PER_BLOCK(dn->inode) * NIDS_PER_BLOCK; + unsigned int skipped_unit = ADDRS_PER_BLOCK(dn->inode); int cur_level = dn->cur_level; int max_level = dn->max_level; pgoff_t base = 0; @@ -636,9 +642,9 @@ static int get_node_path(struct inode *inode, long block, int offset[4], unsigned int noffset[4]) { const long direct_index = ADDRS_PER_INODE(inode); - const long direct_blks = ADDRS_PER_BLOCK; + const long direct_blks = ADDRS_PER_BLOCK(inode); const long dptrs_per_blk = NIDS_PER_BLOCK; - const long indirect_blks = ADDRS_PER_BLOCK * NIDS_PER_BLOCK; + const long indirect_blks = ADDRS_PER_BLOCK(inode) * NIDS_PER_BLOCK; const long dindirect_blks = indirect_blks * NIDS_PER_BLOCK; int n = 0; int level = 0; @@ -1179,8 +1185,14 @@ int f2fs_remove_inode_page(struct inode *inode) f2fs_put_dnode(&dn); return -EIO; } - f2fs_bug_on(F2FS_I_SB(inode), - inode->i_blocks != 0 && inode->i_blocks != 8); + + if (unlikely(inode->i_blocks != 0 && inode->i_blocks != 8)) { + f2fs_msg(F2FS_I_SB(inode)->sb, KERN_WARNING, + "Inconsistent i_blocks, ino:%lu, iblocks:%llu", + inode->i_ino, + (unsigned long long)inode->i_blocks); + set_sbi_flag(F2FS_I_SB(inode), SBI_NEED_FSCK); + } /* will put inode & node pages */ err = truncate_node(&dn); @@ -1275,9 +1287,10 @@ static int read_node_page(struct page *page, int op_flags) int err; if (PageUptodate(page)) { -#ifdef CONFIG_F2FS_CHECK_FS - f2fs_bug_on(sbi, !f2fs_inode_chksum_verify(sbi, page)); -#endif + if (!f2fs_inode_chksum_verify(sbi, page)) { + ClearPageUptodate(page); + return -EBADMSG; + } return LOCKED_PAGE; } @@ -1543,7 +1556,8 @@ static int __write_node_page(struct page *page, bool atomic, bool *submitted, } if (__is_valid_data_blkaddr(ni.blk_addr) && - !f2fs_is_valid_blkaddr(sbi, ni.blk_addr, DATA_GENERIC)) { + !f2fs_is_valid_blkaddr(sbi, ni.blk_addr, + DATA_GENERIC_ENHANCE)) { up_read(&sbi->node_write); goto redirty_out; } @@ -2081,6 +2095,9 @@ static bool add_free_nid(struct f2fs_sb_info *sbi, if (unlikely(nid == 0)) return false; + if (unlikely(f2fs_check_nid_range(sbi, nid))) + return false; + i = f2fs_kmem_cache_alloc(free_nid_slab, GFP_NOFS); i->nid = nid; i->state = FREE_NID; diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index aa3f5633a20c..67206eff5cca 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -317,8 +317,10 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head, break; } - if (!is_recoverable_dnode(page)) + if (!is_recoverable_dnode(page)) { + f2fs_put_page(page, 1); break; + } if (!is_fsync_dnode(page)) goto next; @@ -330,8 +332,10 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head, if (!check_only && IS_INODE(page) && is_dent_dnode(page)) { err = f2fs_recover_inode_page(sbi, page); - if (err) + if (err) { + f2fs_put_page(page, 1); break; + } quota_inode = true; } @@ -347,6 +351,7 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head, err = 0; goto next; } + f2fs_put_page(page, 1); break; } } @@ -362,6 +367,7 @@ next: "%s: detect looped node chain, " "blkaddr:%u, next:%u", __func__, blkaddr, next_blkaddr_of_node(page)); + f2fs_put_page(page, 1); err = -EINVAL; break; } @@ -372,7 +378,6 @@ next: f2fs_ra_meta_pages_cond(sbi, blkaddr); } - f2fs_put_page(page, 1); return err; } @@ -538,7 +543,15 @@ retry_dn: goto err; f2fs_bug_on(sbi, ni.ino != ino_of_node(page)); - f2fs_bug_on(sbi, ofs_of_node(dn.node_page) != ofs_of_node(page)); + + if (ofs_of_node(dn.node_page) != ofs_of_node(page)) { + f2fs_msg(sbi->sb, KERN_WARNING, + "Inconsistent ofs_of_node, ino:%lu, ofs:%u, %u", + inode->i_ino, ofs_of_node(dn.node_page), + ofs_of_node(page)); + err = -EFAULT; + goto err; + } for (; start < end; start++, dn.ofs_in_node++) { block_t src, dest; @@ -546,6 +559,18 @@ retry_dn: src = datablock_addr(dn.inode, dn.node_page, dn.ofs_in_node); dest = datablock_addr(dn.inode, page, dn.ofs_in_node); + if (__is_valid_data_blkaddr(src) && + !f2fs_is_valid_blkaddr(sbi, src, META_POR)) { + err = -EFAULT; + goto err; + } + + if (__is_valid_data_blkaddr(dest) && + !f2fs_is_valid_blkaddr(sbi, dest, META_POR)) { + err = -EFAULT; + goto err; + } + /* skip recovering if dest is the same as src */ if (src == dest) continue; @@ -658,8 +683,10 @@ static int recover_data(struct f2fs_sb_info *sbi, struct list_head *inode_list, */ if (IS_INODE(page)) { err = recover_inode(entry->inode, page); - if (err) + if (err) { + f2fs_put_page(page, 1); break; + } } if (entry->last_dentry == blkaddr) { err = recover_dentry(entry->inode, page, dir_list); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index cbfa80812a66..af4770f0672a 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -580,7 +580,7 @@ static int submit_flush_wait(struct f2fs_sb_info *sbi, nid_t ino) int ret = 0; int i; - if (!sbi->s_ndevs) + if (!f2fs_is_multi_device(sbi)) return __submit_flush_wait(sbi, sbi->sb->s_bdev); for (i = 0; i < sbi->s_ndevs; i++) { @@ -648,7 +648,8 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi, nid_t ino) return ret; } - if (atomic_inc_return(&fcc->queued_flush) == 1 || sbi->s_ndevs > 1) { + if (atomic_inc_return(&fcc->queued_flush) == 1 || + f2fs_is_multi_device(sbi)) { ret = submit_flush_wait(sbi, ino); atomic_dec(&fcc->queued_flush); @@ -754,7 +755,7 @@ int f2fs_flush_device_cache(struct f2fs_sb_info *sbi) { int ret = 0, i; - if (!sbi->s_ndevs) + if (!f2fs_is_multi_device(sbi)) return 0; for (i = 1; i < sbi->s_ndevs; i++) { @@ -1443,9 +1444,12 @@ static int __queue_discard_cmd(struct f2fs_sb_info *sbi, { block_t lblkstart = blkstart; + if (!f2fs_bdev_support_discard(bdev)) + return 0; + trace_f2fs_queue_discard(bdev, blkstart, blklen); - if (sbi->s_ndevs) { + if (f2fs_is_multi_device(sbi)) { int devi = f2fs_target_device_index(sbi, blkstart); blkstart -= FDEV(devi).start_blk; @@ -1808,42 +1812,36 @@ static int __f2fs_issue_discard_zone(struct f2fs_sb_info *sbi, block_t lblkstart = blkstart; int devi = 0; - if (sbi->s_ndevs) { + if (f2fs_is_multi_device(sbi)) { devi = f2fs_target_device_index(sbi, blkstart); + if (blkstart < FDEV(devi).start_blk || + blkstart > FDEV(devi).end_blk) { + f2fs_msg(sbi->sb, KERN_ERR, "Invalid block %x", + blkstart); + return -EIO; + } blkstart -= FDEV(devi).start_blk; } - /* - * We need to know the type of the zone: for conventional zones, - * use regular discard if the drive supports it. For sequential - * zones, reset the zone write pointer. - */ - switch (get_blkz_type(sbi, bdev, blkstart)) { - - case BLK_ZONE_TYPE_CONVENTIONAL: - if (!blk_queue_discard(bdev_get_queue(bdev))) - return 0; - return __queue_discard_cmd(sbi, bdev, lblkstart, blklen); - case BLK_ZONE_TYPE_SEQWRITE_REQ: - case BLK_ZONE_TYPE_SEQWRITE_PREF: + /* For sequential zones, reset the zone write pointer */ + if (f2fs_blkz_is_seq(sbi, devi, blkstart)) { sector = SECTOR_FROM_BLOCK(blkstart); nr_sects = SECTOR_FROM_BLOCK(blklen); if (sector & (bdev_zone_sectors(bdev) - 1) || nr_sects != bdev_zone_sectors(bdev)) { - f2fs_msg(sbi->sb, KERN_INFO, - "(%d) %s: Unaligned discard attempted (block %x + %x)", + f2fs_msg(sbi->sb, KERN_ERR, + "(%d) %s: Unaligned zone reset attempted (block %x + %x)", devi, sbi->s_ndevs ? FDEV(devi).path: "", blkstart, blklen); return -EIO; } trace_f2fs_issue_reset_zone(bdev, blkstart); - return blkdev_reset_zones(bdev, sector, - nr_sects, GFP_NOFS); - default: - /* Unknown zone type: broken device ? */ - return -EIO; + return blkdev_reset_zones(bdev, sector, nr_sects, GFP_NOFS); } + + /* For conventional zones, use regular discard if supported */ + return __queue_discard_cmd(sbi, bdev, lblkstart, blklen); } #endif @@ -1851,8 +1849,7 @@ static int __issue_discard_async(struct f2fs_sb_info *sbi, struct block_device *bdev, block_t blkstart, block_t blklen) { #ifdef CONFIG_BLK_DEV_ZONED - if (f2fs_sb_has_blkzoned(sbi) && - bdev_zoned_model(bdev) != BLK_ZONED_NONE) + if (f2fs_sb_has_blkzoned(sbi) && bdev_is_zoned(bdev)) return __f2fs_issue_discard_zone(sbi, bdev, blkstart, blklen); #endif return __queue_discard_cmd(sbi, bdev, blkstart, blklen); @@ -2248,8 +2245,11 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del) * before, we must track that to know how much space we * really have. */ - if (f2fs_test_bit(offset, se->ckpt_valid_map)) + if (f2fs_test_bit(offset, se->ckpt_valid_map)) { + spin_lock(&sbi->stat_lock); sbi->unusable_block_count++; + spin_unlock(&sbi->stat_lock); + } } if (f2fs_test_and_clear_bit(offset, se->discard_map)) @@ -2296,7 +2296,7 @@ bool f2fs_is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr) struct seg_entry *se; bool is_cp = false; - if (!is_valid_data_blkaddr(sbi, blkaddr)) + if (!__is_valid_data_blkaddr(blkaddr)) return true; down_read(&sit_i->sentry_lock); @@ -3166,7 +3166,7 @@ static void update_device_state(struct f2fs_io_info *fio) struct f2fs_sb_info *sbi = fio->sbi; unsigned int devidx; - if (!sbi->s_ndevs) + if (!f2fs_is_multi_device(sbi)) return; devidx = f2fs_target_device_index(sbi, fio->new_blkaddr); @@ -3264,13 +3264,18 @@ int f2fs_inplace_write_data(struct f2fs_io_info *fio) { int err; struct f2fs_sb_info *sbi = fio->sbi; + unsigned int segno; fio->new_blkaddr = fio->old_blkaddr; /* i/o temperature is needed for passing down write hints */ __get_segment_type(fio); - f2fs_bug_on(sbi, !IS_DATASEG(get_seg_entry(sbi, - GET_SEGNO(sbi, fio->new_blkaddr))->type)); + segno = GET_SEGNO(sbi, fio->new_blkaddr); + + if (!IS_DATASEG(get_seg_entry(sbi, segno)->type)) { + set_sbi_flag(sbi, SBI_NEED_FSCK); + return -EFAULT; + } stat_inc_inplace_blocks(fio->sbi); @@ -3413,7 +3418,7 @@ void f2fs_wait_on_block_writeback(struct inode *inode, block_t blkaddr) if (!f2fs_post_read_required(inode)) return; - if (!is_valid_data_blkaddr(sbi, blkaddr)) + if (!__is_valid_data_blkaddr(blkaddr)) return; cpage = find_lock_page(META_MAPPING(sbi), blkaddr); diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 5c7ed0442d6e..429007b8036e 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -82,7 +82,7 @@ (GET_SEGOFF_FROM_SEG0(sbi, blk_addr) & ((sbi)->blocks_per_seg - 1)) #define GET_SEGNO(sbi, blk_addr) \ - ((!is_valid_data_blkaddr(sbi, blk_addr)) ? \ + ((!__is_valid_data_blkaddr(blk_addr)) ? \ NULL_SEGNO : GET_L2R_SEGNO(FREE_I(sbi), \ GET_SEGNO_FROM_SEG0(sbi, blk_addr))) #define BLKS_PER_SEC(sbi) \ @@ -656,14 +656,15 @@ static inline void check_seg_range(struct f2fs_sb_info *sbi, unsigned int segno) f2fs_bug_on(sbi, segno > TOTAL_SEGS(sbi) - 1); } -static inline void verify_block_addr(struct f2fs_io_info *fio, block_t blk_addr) +static inline void verify_fio_blkaddr(struct f2fs_io_info *fio) { struct f2fs_sb_info *sbi = fio->sbi; - if (__is_meta_io(fio)) - verify_blkaddr(sbi, blk_addr, META_GENERIC); - else - verify_blkaddr(sbi, blk_addr, DATA_GENERIC); + if (__is_valid_data_blkaddr(fio->old_blkaddr)) + verify_blkaddr(sbi, fio->old_blkaddr, __is_meta_io(fio) ? + META_GENERIC : DATA_GENERIC); + verify_blkaddr(sbi, fio->new_blkaddr, __is_meta_io(fio) ? + META_GENERIC : DATA_GENERIC_ENHANCE); } /* @@ -672,7 +673,6 @@ static inline void verify_block_addr(struct f2fs_io_info *fio, block_t blk_addr) static inline int check_block_count(struct f2fs_sb_info *sbi, int segno, struct f2fs_sit_entry *raw_sit) { -#ifdef CONFIG_F2FS_CHECK_FS bool is_valid = test_bit_le(0, raw_sit->valid_map) ? true : false; int valid_blocks = 0; int cur_pos = 0, next_pos; @@ -699,7 +699,7 @@ static inline int check_block_count(struct f2fs_sb_info *sbi, set_sbi_flag(sbi, SBI_NEED_FSCK); return -EINVAL; } -#endif + /* check segment usage, and check boundary of a given segment number */ if (unlikely(GET_SIT_VBLOCKS(raw_sit) > sbi->blocks_per_seg || segno > TOTAL_SEGS(sbi) - 1)) { diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 5418b1d55a4f..ff908f108eca 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1024,7 +1024,7 @@ static void destroy_device_list(struct f2fs_sb_info *sbi) for (i = 0; i < sbi->s_ndevs; i++) { blkdev_put(FDEV(i).bdev, FMODE_EXCL); #ifdef CONFIG_BLK_DEV_ZONED - kvfree(FDEV(i).blkz_type); + kvfree(FDEV(i).blkz_seq); #endif } kvfree(sbi->devs); @@ -1227,10 +1227,13 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_blocks = total_count - start_count; buf->f_bfree = user_block_count - valid_user_blocks(sbi) - sbi->current_reserved_blocks; + + spin_lock(&sbi->stat_lock); if (unlikely(buf->f_bfree <= sbi->unusable_block_count)) buf->f_bfree = 0; else buf->f_bfree -= sbi->unusable_block_count; + spin_unlock(&sbi->stat_lock); if (buf->f_bfree > F2FS_OPTION(sbi).root_reserved_blocks) buf->f_bavail = buf->f_bfree - @@ -1505,9 +1508,15 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi) mutex_lock(&sbi->gc_mutex); cpc.reason = CP_PAUSE; set_sbi_flag(sbi, SBI_CP_DISABLED); - f2fs_write_checkpoint(sbi, &cpc); + err = f2fs_write_checkpoint(sbi, &cpc); + if (err) + goto out_unlock; + spin_lock(&sbi->stat_lock); sbi->unusable_block_count = 0; + spin_unlock(&sbi->stat_lock); + +out_unlock: mutex_unlock(&sbi->gc_mutex); restore_flag: sbi->sb->s_flags = s_flags; /* Restore MS_RDONLY status */ @@ -2280,7 +2289,7 @@ static const struct export_operations f2fs_export_ops = { static loff_t max_file_blocks(void) { loff_t result = 0; - loff_t leaf_count = ADDRS_PER_BLOCK; + loff_t leaf_count = DEF_ADDRS_PER_BLOCK; /* * note: previously, result is equal to (DEF_ADDRS_PER_INODE - @@ -2458,7 +2467,7 @@ static int sanity_check_raw_super(struct f2fs_sb_info *sbi, /* Currently, support only 4KB page cache size */ if (F2FS_BLKSIZE != PAGE_SIZE) { f2fs_msg(sb, KERN_INFO, - "Invalid page_cache_size (%lu), supports only 4KB\n", + "Invalid page_cache_size (%lu), supports only 4KB", PAGE_SIZE); return 1; } @@ -2467,7 +2476,7 @@ static int sanity_check_raw_super(struct f2fs_sb_info *sbi, blocksize = 1 << le32_to_cpu(raw_super->log_blocksize); if (blocksize != F2FS_BLKSIZE) { f2fs_msg(sb, KERN_INFO, - "Invalid blocksize (%u), supports only 4KB\n", + "Invalid blocksize (%u), supports only 4KB", blocksize); return 1; } @@ -2475,7 +2484,7 @@ static int sanity_check_raw_super(struct f2fs_sb_info *sbi, /* check log blocks per segment */ if (le32_to_cpu(raw_super->log_blocks_per_seg) != 9) { f2fs_msg(sb, KERN_INFO, - "Invalid log blocks per segment (%u)\n", + "Invalid log blocks per segment (%u)", le32_to_cpu(raw_super->log_blocks_per_seg)); return 1; } @@ -2596,7 +2605,8 @@ int f2fs_sanity_check_ckpt(struct f2fs_sb_info *sbi) unsigned int log_blocks_per_seg; unsigned int segment_count_main; unsigned int cp_pack_start_sum, cp_payload; - block_t user_block_count; + block_t user_block_count, valid_user_blocks; + block_t avail_node_count, valid_node_count; int i, j; total = le32_to_cpu(raw_super->segment_count); @@ -2631,6 +2641,24 @@ int f2fs_sanity_check_ckpt(struct f2fs_sb_info *sbi) return 1; } + valid_user_blocks = le64_to_cpu(ckpt->valid_block_count); + if (valid_user_blocks > user_block_count) { + f2fs_msg(sbi->sb, KERN_ERR, + "Wrong valid_user_blocks: %u, user_block_count: %u", + valid_user_blocks, user_block_count); + return 1; + } + + valid_node_count = le32_to_cpu(ckpt->valid_node_count); + avail_node_count = sbi->total_node_count - sbi->nquota_files - + F2FS_RESERVED_NODE_NUM; + if (valid_node_count > avail_node_count) { + f2fs_msg(sbi->sb, KERN_ERR, + "Wrong valid_node_count: %u, avail_node_count: %u", + valid_node_count, avail_node_count); + return 1; + } + main_segs = le32_to_cpu(raw_super->segment_count_main); blocks_per_seg = sbi->blocks_per_seg; @@ -2802,9 +2830,11 @@ static int init_blkz_info(struct f2fs_sb_info *sbi, int devi) if (nr_sectors & (bdev_zone_sectors(bdev) - 1)) FDEV(devi).nr_blkz++; - FDEV(devi).blkz_type = f2fs_kmalloc(sbi, FDEV(devi).nr_blkz, - GFP_KERNEL); - if (!FDEV(devi).blkz_type) + FDEV(devi).blkz_seq = f2fs_kzalloc(sbi, + BITS_TO_LONGS(FDEV(devi).nr_blkz) + * sizeof(unsigned long), + GFP_KERNEL); + if (!FDEV(devi).blkz_seq) return -ENOMEM; #define F2FS_REPORT_NR_ZONES 4096 @@ -2831,7 +2861,8 @@ static int init_blkz_info(struct f2fs_sb_info *sbi, int devi) } for (i = 0; i < nr_zones; i++) { - FDEV(devi).blkz_type[n] = zones[i].type; + if (zones[i].type != BLK_ZONE_TYPE_CONVENTIONAL) + set_bit(n, FDEV(devi).blkz_seq); sector += zones[i].len; n++; } @@ -3118,7 +3149,7 @@ try_onemore: #ifndef CONFIG_BLK_DEV_ZONED if (f2fs_sb_has_blkzoned(sbi)) { f2fs_msg(sb, KERN_ERR, - "Zoned block device support is not enabled\n"); + "Zoned block device support is not enabled"); err = -EOPNOTSUPP; goto free_sb_buf; } @@ -3142,10 +3173,7 @@ try_onemore: #ifdef CONFIG_QUOTA sb->dq_op = &f2fs_quota_operations; - if (f2fs_sb_has_quota_ino(sbi)) - sb->s_qcop = &dquot_quotactl_sysfile_ops; - else - sb->s_qcop = &f2fs_quotactl_ops; + sb->s_qcop = &f2fs_quotactl_ops; sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP | QTYPE_MASK_PRJ; if (f2fs_sb_has_quota_ino(sbi)) { @@ -3362,10 +3390,17 @@ try_onemore: * mount should be failed, when device has readonly mode, and * previous checkpoint was not done by clean system shutdown. */ - if (bdev_read_only(sb->s_bdev) && - !is_set_ckpt_flags(sbi, CP_UMOUNT_FLAG)) { - err = -EROFS; - goto free_meta; + if (f2fs_hw_is_readonly(sbi)) { + if (!is_set_ckpt_flags(sbi, CP_UMOUNT_FLAG)) { + err = -EROFS; + f2fs_msg(sb, KERN_ERR, + "Need to recover fsync data, but " + "write access unavailable"); + goto free_meta; + } + f2fs_msg(sbi->sb, KERN_INFO, "write access " + "unavailable, skipping recovery"); + goto reset_checkpoint; } if (need_fsck) diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 8c6161ea4e23..376db951b7c4 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -248,12 +248,17 @@ static inline const struct xattr_handler *f2fs_xattr_handler(int index) return handler; } -static struct f2fs_xattr_entry *__find_xattr(void *base_addr, int index, - size_t len, const char *name) +static struct f2fs_xattr_entry *__find_xattr(void *base_addr, + void *last_base_addr, int index, + size_t len, const char *name) { struct f2fs_xattr_entry *entry; list_for_each_xattr(entry, base_addr) { + if ((void *)(entry) + sizeof(__u32) > last_base_addr || + (void *)XATTR_NEXT_ENTRY(entry) > last_base_addr) + return NULL; + if (entry->e_name_index != index) continue; if (entry->e_name_len != len) @@ -343,20 +348,22 @@ static int lookup_all_xattrs(struct inode *inode, struct page *ipage, const char *name, struct f2fs_xattr_entry **xe, void **base_addr, int *base_size) { - void *cur_addr, *txattr_addr, *last_addr = NULL; + void *cur_addr, *txattr_addr, *last_txattr_addr; + void *last_addr = NULL; nid_t xnid = F2FS_I(inode)->i_xattr_nid; - unsigned int size = xnid ? VALID_XATTR_BLOCK_SIZE : 0; unsigned int inline_size = inline_xattr_size(inode); int err = 0; - if (!size && !inline_size) + if (!xnid && !inline_size) return -ENODATA; - *base_size = inline_size + size + XATTR_PADDING_SIZE; + *base_size = XATTR_SIZE(xnid, inode) + XATTR_PADDING_SIZE; txattr_addr = f2fs_kzalloc(F2FS_I_SB(inode), *base_size, GFP_NOFS); if (!txattr_addr) return -ENOMEM; + last_txattr_addr = (void *)txattr_addr + XATTR_SIZE(xnid, inode); + /* read from inline xattr */ if (inline_size) { err = read_inline_xattr(inode, ipage, txattr_addr); @@ -383,7 +390,11 @@ static int lookup_all_xattrs(struct inode *inode, struct page *ipage, else cur_addr = txattr_addr; - *xe = __find_xattr(cur_addr, index, len, name); + *xe = __find_xattr(cur_addr, last_txattr_addr, index, len, name); + if (!*xe) { + err = -EFAULT; + goto out; + } check: if (IS_XATTR_LAST_ENTRY(*xe)) { err = -ENODATA; @@ -620,7 +631,8 @@ static int __f2fs_setxattr(struct inode *inode, int index, struct page *ipage, int flags) { struct f2fs_xattr_entry *here, *last; - void *base_addr; + void *base_addr, *last_base_addr; + nid_t xnid = F2FS_I(inode)->i_xattr_nid; int found, newsize; size_t len; __u32 new_hsize; @@ -644,8 +656,14 @@ static int __f2fs_setxattr(struct inode *inode, int index, if (error) return error; + last_base_addr = (void *)base_addr + XATTR_SIZE(xnid, inode); + /* find entry with wanted name. */ - here = __find_xattr(base_addr, index, len, name); + here = __find_xattr(base_addr, last_base_addr, index, len, name); + if (!here) { + error = -EFAULT; + goto exit; + } found = IS_XATTR_LAST_ENTRY(here) ? 0 : 1; diff --git a/fs/f2fs/xattr.h b/fs/f2fs/xattr.h index 12a5ef3a607d..2909c9241145 100644 --- a/fs/f2fs/xattr.h +++ b/fs/f2fs/xattr.h @@ -71,6 +71,8 @@ struct f2fs_xattr_entry { entry = XATTR_NEXT_ENTRY(entry)) #define VALID_XATTR_BLOCK_SIZE (PAGE_SIZE - sizeof(struct node_footer)) #define XATTR_PADDING_SIZE (sizeof(__u32)) +#define XATTR_SIZE(x,i) (((x) ? VALID_XATTR_BLOCK_SIZE : 0) + \ + (inline_xattr_size(i))) #define MIN_OFFSET(i) XATTR_ALIGN(inline_xattr_size(i) + \ VALID_XATTR_BLOCK_SIZE) diff --git a/fs/fat/file.c b/fs/fat/file.c index a08f1039909a..d3f655ae020b 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c @@ -156,12 +156,17 @@ static int fat_file_release(struct inode *inode, struct file *filp) int fat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync) { struct inode *inode = filp->f_mapping->host; - int res, err; + int err; + + err = __generic_file_fsync(filp, start, end, datasync); + if (err) + return err; - res = generic_file_fsync(filp, start, end, datasync); err = sync_mapping_buffers(MSDOS_SB(inode->i_sb)->fat_inode->i_mapping); + if (err) + return err; - return res ? res : err; + return blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL); } diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 0fe11984db09..de1138e4b049 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -331,11 +331,22 @@ struct inode_switch_wbs_context { struct work_struct work; }; +static void bdi_down_write_wb_switch_rwsem(struct backing_dev_info *bdi) +{ + down_write(&bdi->wb_switch_rwsem); +} + +static void bdi_up_write_wb_switch_rwsem(struct backing_dev_info *bdi) +{ + up_write(&bdi->wb_switch_rwsem); +} + static void inode_switch_wbs_work_fn(struct work_struct *work) { struct inode_switch_wbs_context *isw = container_of(work, struct inode_switch_wbs_context, work); struct inode *inode = isw->inode; + struct backing_dev_info *bdi = inode_to_bdi(inode); struct address_space *mapping = inode->i_mapping; struct bdi_writeback *old_wb = inode->i_wb; struct bdi_writeback *new_wb = isw->new_wb; @@ -344,6 +355,12 @@ static void inode_switch_wbs_work_fn(struct work_struct *work) void **slot; /* + * If @inode switches cgwb membership while sync_inodes_sb() is + * being issued, sync_inodes_sb() might miss it. Synchronize. + */ + down_read(&bdi->wb_switch_rwsem); + + /* * By the time control reaches here, RCU grace period has passed * since I_WB_SWITCH assertion and all wb stat update transactions * between unlocked_inode_to_wb_begin/end() are guaranteed to be @@ -435,6 +452,8 @@ skip_switch: spin_unlock(&new_wb->list_lock); spin_unlock(&old_wb->list_lock); + up_read(&bdi->wb_switch_rwsem); + if (switched) { wb_wakeup(new_wb); wb_put(old_wb); @@ -475,9 +494,18 @@ static void inode_switch_wbs(struct inode *inode, int new_wb_id) if (inode->i_state & I_WB_SWITCH) return; + /* + * Avoid starting new switches while sync_inodes_sb() is in + * progress. Otherwise, if the down_write protected issue path + * blocks heavily, we might end up starting a large number of + * switches which will block on the rwsem. + */ + if (!down_read_trylock(&bdi->wb_switch_rwsem)) + return; + isw = kzalloc(sizeof(*isw), GFP_ATOMIC); if (!isw) - return; + goto out_unlock; /* find and pin the new wb */ rcu_read_lock(); @@ -502,8 +530,6 @@ static void inode_switch_wbs(struct inode *inode, int new_wb_id) ihold(inode); isw->inode = inode; - atomic_inc(&isw_nr_in_flight); - /* * In addition to synchronizing among switchers, I_WB_SWITCH tells * the RCU protected stat update paths to grab the mapping's @@ -511,12 +537,17 @@ static void inode_switch_wbs(struct inode *inode, int new_wb_id) * Let's continue after I_WB_SWITCH is guaranteed to be visible. */ call_rcu(&isw->rcu_head, inode_switch_wbs_rcu_fn); - return; + + atomic_inc(&isw_nr_in_flight); + + goto out_unlock; out_free: if (isw->new_wb) wb_put(isw->new_wb); kfree(isw); +out_unlock: + up_read(&bdi->wb_switch_rwsem); } /** @@ -880,7 +911,11 @@ restart: void cgroup_writeback_umount(void) { if (atomic_read(&isw_nr_in_flight)) { - synchronize_rcu(); + /* + * Use rcu_barrier() to wait for all pending callbacks to + * ensure that all in-flight wb switches are in the workqueue. + */ + rcu_barrier(); flush_workqueue(isw_wq); } } @@ -896,6 +931,9 @@ fs_initcall(cgroup_writeback_init); #else /* CONFIG_CGROUP_WRITEBACK */ +static void bdi_down_write_wb_switch_rwsem(struct backing_dev_info *bdi) { } +static void bdi_up_write_wb_switch_rwsem(struct backing_dev_info *bdi) { } + static struct bdi_writeback * locked_inode_to_wb_and_lock_list(struct inode *inode) __releases(&inode->i_lock) @@ -2341,8 +2379,11 @@ void sync_inodes_sb(struct super_block *sb) return; WARN_ON(!rwsem_is_locked(&sb->s_umount)); + /* protect against inode wb switch, see inode_switch_wbs_work_fn() */ + bdi_down_write_wb_switch_rwsem(bdi); bdi_split_work_to_wbs(bdi, &work, false); wb_wait_for_completion(bdi, &done); + bdi_up_write_wb_switch_rwsem(bdi); wait_sb_inodes(sb); } diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 0fc9e87802b5..817e46537e3c 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1734,7 +1734,7 @@ static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode, offset = outarg->offset & ~PAGE_CACHE_MASK; file_size = i_size_read(inode); - num = outarg->size; + num = min(outarg->size, fc->max_write); if (outarg->offset > file_size) num = 0; else if (outarg->offset + num > file_size) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 690bbb22ed6b..56c9caa7b16a 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -194,7 +194,9 @@ void fuse_finish_open(struct inode *inode, struct file *file) file->f_op = &fuse_direct_io_file_operations; if (!(ff->open_flags & FOPEN_KEEP_CACHE)) invalidate_inode_pages2(inode->i_mapping); - if (ff->open_flags & FOPEN_NONSEEKABLE) + if (ff->open_flags & FOPEN_STREAM) + stream_open(inode, file); + else if (ff->open_flags & FOPEN_NONSEEKABLE) nonseekable_open(inode, file); if (fc->atomic_o_trunc && (file->f_flags & O_TRUNC)) { struct fuse_inode *fi = get_fuse_inode(inode); @@ -1602,7 +1604,7 @@ __acquires(fc->lock) { struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_inode *fi = get_fuse_inode(inode); - size_t crop = i_size_read(inode); + loff_t crop = i_size_read(inode); struct fuse_req *req; while (fi->writectr >= 0 && !list_empty(&fi->queued_writes)) { @@ -3022,6 +3024,13 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, } } + if (!(mode & FALLOC_FL_KEEP_SIZE) && + offset + length > i_size_read(inode)) { + err = inode_newsize_ok(inode, offset + length); + if (err) + goto out; + } + if (!(mode & FALLOC_FL_KEEP_SIZE)) set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state); diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index 09a0cf5f3dd8..1eb737c466dd 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -136,22 +136,26 @@ static int demote_ok(const struct gfs2_glock *gl) void gfs2_glock_add_to_lru(struct gfs2_glock *gl) { + if (!(gl->gl_ops->go_flags & GLOF_LRU)) + return; + spin_lock(&lru_lock); - if (!list_empty(&gl->gl_lru)) - list_del_init(&gl->gl_lru); - else + list_del(&gl->gl_lru); + list_add_tail(&gl->gl_lru, &lru_list); + + if (!test_bit(GLF_LRU, &gl->gl_flags)) { + set_bit(GLF_LRU, &gl->gl_flags); atomic_inc(&lru_count); + } - list_add_tail(&gl->gl_lru, &lru_list); - set_bit(GLF_LRU, &gl->gl_flags); spin_unlock(&lru_lock); } static void gfs2_glock_remove_from_lru(struct gfs2_glock *gl) { spin_lock(&lru_lock); - if (!list_empty(&gl->gl_lru)) { + if (test_bit(GLF_LRU, &gl->gl_flags)) { list_del_init(&gl->gl_lru); atomic_dec(&lru_count); clear_bit(GLF_LRU, &gl->gl_flags); @@ -1040,8 +1044,7 @@ void gfs2_glock_dq(struct gfs2_holder *gh) !test_bit(GLF_DEMOTE, &gl->gl_flags)) fast_path = 1; } - if (!test_bit(GLF_LFLUSH, &gl->gl_flags) && demote_ok(gl) && - (glops->go_flags & GLOF_LRU)) + if (!test_bit(GLF_LFLUSH, &gl->gl_flags) && demote_ok(gl)) gfs2_glock_add_to_lru(gl); trace_gfs2_glock_queue(gh, 0); @@ -1341,6 +1344,7 @@ __acquires(&lru_lock) if (!spin_trylock(&gl->gl_lockref.lock)) { add_back_to_lru: list_add(&gl->gl_lru, &lru_list); + set_bit(GLF_LRU, &gl->gl_flags); atomic_inc(&lru_count); continue; } @@ -1348,7 +1352,6 @@ add_back_to_lru: spin_unlock(&gl->gl_lockref.lock); goto add_back_to_lru; } - clear_bit(GLF_LRU, &gl->gl_flags); gl->gl_lockref.count++; if (demote_ok(gl)) handle_callback(gl, LM_ST_UNLOCKED, 0, false); @@ -1384,6 +1387,7 @@ static long gfs2_scan_glock_lru(int nr) if (!test_bit(GLF_LOCK, &gl->gl_flags)) { list_move(&gl->gl_lru, &dispose); atomic_dec(&lru_count); + clear_bit(GLF_LRU, &gl->gl_flags); freed++; continue; } diff --git a/fs/gfs2/lock_dlm.c b/fs/gfs2/lock_dlm.c index 8b907c5cc913..3c3d037df824 100644 --- a/fs/gfs2/lock_dlm.c +++ b/fs/gfs2/lock_dlm.c @@ -32,9 +32,10 @@ extern struct workqueue_struct *gfs2_control_wq; * @delta is the difference between the current rtt sample and the * running average srtt. We add 1/8 of that to the srtt in order to * update the current srtt estimate. The variance estimate is a bit - * more complicated. We subtract the abs value of the @delta from - * the current variance estimate and add 1/4 of that to the running - * total. + * more complicated. We subtract the current variance estimate from + * the abs value of the @delta and add 1/4 of that to the running + * total. That's equivalent to 3/4 of the current variance + * estimate plus 1/4 of the abs of @delta. * * Note that the index points at the array entry containing the smoothed * mean value, and the variance is always in the following entry @@ -50,7 +51,7 @@ static inline void gfs2_update_stats(struct gfs2_lkstats *s, unsigned index, s64 delta = sample - s->stats[index]; s->stats[index] += (delta >> 3); index++; - s->stats[index] += ((abs(delta) - s->stats[index]) >> 2); + s->stats[index] += (s64)(abs(delta) - s->stats[index]) >> 2; } /** diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index cefae2350da5..937c6ee1786f 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -414,9 +414,7 @@ static void remove_inode_hugepages(struct inode *inode, loff_t lstart, if (next >= end) break; - hash = hugetlb_fault_mutex_hash(h, current->mm, - &pseudo_vma, - mapping, next, 0); + hash = hugetlb_fault_mutex_hash(h, mapping, next, 0); mutex_lock(&hugetlb_fault_mutex_table[hash]); lock_page(page); @@ -569,7 +567,6 @@ static long hugetlbfs_fallocate(struct file *file, int mode, loff_t offset, struct address_space *mapping = inode->i_mapping; struct hstate *h = hstate_inode(inode); struct vm_area_struct pseudo_vma; - struct mm_struct *mm = current->mm; loff_t hpage_size = huge_page_size(h); unsigned long hpage_shift = huge_page_shift(h); pgoff_t start, index, end; @@ -633,8 +630,7 @@ static long hugetlbfs_fallocate(struct file *file, int mode, loff_t offset, addr = index * hpage_size; /* mutex taken here, fault path and hole punch */ - hash = hugetlb_fault_mutex_hash(h, mm, &pseudo_vma, mapping, - index, addr); + hash = hugetlb_fault_mutex_hash(h, mapping, index, addr); mutex_lock(&hugetlb_fault_mutex_table[hash]); /* See if already present in mapping to avoid alloc/free */ @@ -745,11 +741,17 @@ static struct inode *hugetlbfs_get_inode(struct super_block *sb, umode_t mode, dev_t dev) { struct inode *inode; - struct resv_map *resv_map; + struct resv_map *resv_map = NULL; - resv_map = resv_map_alloc(); - if (!resv_map) - return NULL; + /* + * Reserve maps are only needed for inodes that can have associated + * page allocations. + */ + if (S_ISREG(mode) || S_ISLNK(mode)) { + resv_map = resv_map_alloc(); + if (!resv_map) + return NULL; + } inode = new_inode(sb); if (inode) { @@ -790,8 +792,10 @@ static struct inode *hugetlbfs_get_inode(struct super_block *sb, break; } lockdep_annotate_inode_mutex_key(inode); - } else - kref_put(&resv_map->refs, resv_map_release); + } else { + if (resv_map) + kref_put(&resv_map->refs, resv_map_release); + } return inode; } diff --git a/fs/inode.c b/fs/inode.c index 9f98d1d0769d..10015f17b8ea 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1745,8 +1745,13 @@ int file_remove_privs(struct file *file) int kill; int error = 0; - /* Fast path for nothing security related */ - if (IS_NOSEC(inode)) + /* + * Fast path for nothing security related. + * As well for non-regular files, e.g. blkdev inodes. + * For example, blkdev_write_iter() might get here + * trying to remove privs which it is not allowed to. + */ + if (IS_NOSEC(inode) || !S_ISREG(inode->i_mode)) return 0; kill = dentry_needs_remove_privs(dentry); diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index bfebbf13698c..5b52ea41b84f 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -1414,11 +1414,6 @@ void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f) jffs2_kill_fragtree(&f->fragtree, deleted?c:NULL); - if (f->target) { - kfree(f->target); - f->target = NULL; - } - fds = f->dents; while(fds) { fd = fds; diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index 023e7f32ee1b..9fc297df8c75 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -47,7 +47,10 @@ static struct inode *jffs2_alloc_inode(struct super_block *sb) static void jffs2_i_callback(struct rcu_head *head) { struct inode *inode = container_of(head, struct inode, i_rcu); - kmem_cache_free(jffs2_inode_cachep, JFFS2_INODE_INFO(inode)); + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + + kfree(f->target); + kmem_cache_free(jffs2_inode_cachep, f); } static void jffs2_destroy_inode(struct inode *inode) diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 44f5cea49699..5be61affeefd 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -140,6 +140,10 @@ int nfs40_discover_server_trunking(struct nfs_client *clp, /* Sustain the lease, even if it's empty. If the clientid4 * goes stale it's of no use for trunking discovery. */ nfs4_schedule_state_renewal(*result); + + /* If the client state need to recover, do it. */ + if (clp->cl_state) + nfs4_schedule_state_manager(clp); } out: return status; diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 9b42139a479b..dced329a8584 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -2020,7 +2020,8 @@ static int nfs23_validate_mount_data(void *options, memcpy(sap, &data->addr, sizeof(data->addr)); args->nfs_server.addrlen = sizeof(data->addr); args->nfs_server.port = ntohs(data->addr.sin_port); - if (!nfs_verify_server_address(sap)) + if (sap->sa_family != AF_INET || + !nfs_verify_server_address(sap)) goto out_no_address; if (!(data->flags & NFS_MOUNT_TCP)) diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 24ace275160c..4fa3f0ba9ab3 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -874,8 +874,9 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata) cb->cb_seq_status = 1; cb->cb_status = 0; if (minorversion) { - if (!nfsd41_cb_get_slot(clp, task)) + if (!cb->cb_holds_slot && !nfsd41_cb_get_slot(clp, task)) return; + cb->cb_holds_slot = true; } rpc_call_start(task); } @@ -902,6 +903,9 @@ static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback return true; } + if (!cb->cb_holds_slot) + goto need_restart; + switch (cb->cb_seq_status) { case 0: /* @@ -939,6 +943,7 @@ static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback cb->cb_seq_status); } + cb->cb_holds_slot = false; clear_bit(0, &clp->cl_cb_slot_busy); rpc_wake_up_next(&clp->cl_cb_waitq); dprintk("%s: freed slot, new seqid=%d\n", __func__, @@ -1146,6 +1151,7 @@ void nfsd4_init_cb(struct nfsd4_callback *cb, struct nfs4_client *clp, cb->cb_seq_status = 1; cb->cb_status = 0; cb->cb_need_restart = false; + cb->cb_holds_slot = false; } void nfsd4_run_cb(struct nfsd4_callback *cb) diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 86af697c21d3..2c26bedda7be 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -70,6 +70,7 @@ struct nfsd4_callback { int cb_seq_status; int cb_status; bool cb_need_restart; + bool cb_holds_slot; }; struct nfsd4_callback_ops { diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h index fcfc48cbe136..128d6e216fd7 100644 --- a/fs/nfsd/vfs.h +++ b/fs/nfsd/vfs.h @@ -109,8 +109,11 @@ void nfsd_put_raparams(struct file *file, struct raparms *ra); static inline int fh_want_write(struct svc_fh *fh) { - int ret = mnt_want_write(fh->fh_export->ex_path.mnt); + int ret; + if (fh->fh_want_write) + return 0; + ret = mnt_want_write(fh->fh_export->ex_path.mnt); if (!ret) fh->fh_want_write = true; return ret; diff --git a/fs/ocfs2/dcache.c b/fs/ocfs2/dcache.c index 290373024d9d..e8ace3b54e9c 100644 --- a/fs/ocfs2/dcache.c +++ b/fs/ocfs2/dcache.c @@ -310,6 +310,18 @@ int ocfs2_dentry_attach_lock(struct dentry *dentry, out_attach: spin_lock(&dentry_attach_lock); + if (unlikely(dentry->d_fsdata && !alias)) { + /* d_fsdata is set by a racing thread which is doing + * the same thing as this thread is doing. Leave the racing + * thread going ahead and we return here. + */ + spin_unlock(&dentry_attach_lock); + iput(dl->dl_inode); + ocfs2_lock_res_free(&dl->dl_lockres); + kfree(dl); + return 0; + } + dentry->d_fsdata = dl; dl->dl_count++; spin_unlock(&dentry_attach_lock); diff --git a/fs/ocfs2/export.c b/fs/ocfs2/export.c index 3494e220b510..bed15dec3c16 100644 --- a/fs/ocfs2/export.c +++ b/fs/ocfs2/export.c @@ -148,16 +148,24 @@ static struct dentry *ocfs2_get_parent(struct dentry *child) u64 blkno; struct dentry *parent; struct inode *dir = d_inode(child); + int set; trace_ocfs2_get_parent(child, child->d_name.len, child->d_name.name, (unsigned long long)OCFS2_I(dir)->ip_blkno); + status = ocfs2_nfs_sync_lock(OCFS2_SB(dir->i_sb), 1); + if (status < 0) { + mlog(ML_ERROR, "getting nfs sync lock(EX) failed %d\n", status); + parent = ERR_PTR(status); + goto bail; + } + status = ocfs2_inode_lock(dir, NULL, 0); if (status < 0) { if (status != -ENOENT) mlog_errno(status); parent = ERR_PTR(status); - goto bail; + goto unlock_nfs_sync; } status = ocfs2_lookup_ino_from_name(dir, "..", 2, &blkno); @@ -166,11 +174,31 @@ static struct dentry *ocfs2_get_parent(struct dentry *child) goto bail_unlock; } + status = ocfs2_test_inode_bit(OCFS2_SB(dir->i_sb), blkno, &set); + if (status < 0) { + if (status == -EINVAL) { + status = -ESTALE; + } else + mlog(ML_ERROR, "test inode bit failed %d\n", status); + parent = ERR_PTR(status); + goto bail_unlock; + } + + trace_ocfs2_get_dentry_test_bit(status, set); + if (!set) { + status = -ESTALE; + parent = ERR_PTR(status); + goto bail_unlock; + } + parent = d_obtain_alias(ocfs2_iget(OCFS2_SB(dir->i_sb), blkno, 0, 0)); bail_unlock: ocfs2_inode_unlock(dir, 0); +unlock_nfs_sync: + ocfs2_nfs_sync_unlock(OCFS2_SB(dir->i_sb), 1); + bail: trace_ocfs2_get_parent_end(parent); diff --git a/fs/open.c b/fs/open.c index 1fd96c5d3895..e6a55dd45cba 100644 --- a/fs/open.c +++ b/fs/open.c @@ -1165,3 +1165,21 @@ int nonseekable_open(struct inode *inode, struct file *filp) } EXPORT_SYMBOL(nonseekable_open); + +/* + * stream_open is used by subsystems that want stream-like file descriptors. + * Such file descriptors are not seekable and don't have notion of position + * (file.f_pos is always 0). Contrary to file descriptors of other regular + * files, .read() and .write() can run simultaneously. + * + * stream_open never fails and is marked to return int so that it could be + * directly used as file_operations.open . + */ +int stream_open(struct inode *inode, struct file *filp) +{ + filp->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE | FMODE_ATOMIC_POS); + filp->f_mode |= FMODE_STREAM; + return 0; +} + +EXPORT_SYMBOL(stream_open); diff --git a/fs/proc/base.c b/fs/proc/base.c index 86cc5554198f..ae5350d61b0a 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -3119,6 +3119,7 @@ static const struct pid_entry tgid_base_stuff[] = { #ifdef CONFIG_PROC_PAGE_MONITOR REG("clear_refs", S_IWUSR, proc_clear_refs_operations), REG("smaps", S_IRUGO, proc_pid_smaps_operations), + REG("smaps_rollup", S_IRUGO, proc_pid_smaps_rollup_operations), REG("pagemap", S_IRUSR, proc_pagemap_operations), #endif #ifdef CONFIG_SECURITY @@ -3513,6 +3514,7 @@ static const struct pid_entry tid_base_stuff[] = { #ifdef CONFIG_PROC_PAGE_MONITOR REG("clear_refs", S_IWUSR, proc_clear_refs_operations), REG("smaps", S_IRUGO, proc_tid_smaps_operations), + REG("smaps_rollup", S_IRUGO, proc_pid_smaps_rollup_operations), REG("pagemap", S_IRUSR, proc_pagemap_operations), #endif #ifdef CONFIG_SECURITY diff --git a/fs/proc/internal.h b/fs/proc/internal.h index 1f01beda3bf3..2bfff313bf94 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -285,10 +285,12 @@ extern int proc_remount(struct super_block *, int *, char *); /* * task_[no]mmu.c */ +struct mem_size_stats; struct proc_maps_private { struct inode *inode; struct task_struct *task; struct mm_struct *mm; + struct mem_size_stats *rollup; #ifdef CONFIG_MMU struct vm_area_struct *tail_vma; #endif @@ -304,6 +306,7 @@ extern const struct file_operations proc_tid_maps_operations; extern const struct file_operations proc_pid_numa_maps_operations; extern const struct file_operations proc_tid_numa_maps_operations; extern const struct file_operations proc_pid_smaps_operations; +extern const struct file_operations proc_pid_smaps_rollup_operations; extern const struct file_operations proc_tid_smaps_operations; extern const struct file_operations proc_clear_refs_operations; extern const struct file_operations proc_pagemap_operations; diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index c7e32a891502..2eea16a81500 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -1550,9 +1550,11 @@ static void drop_sysctl_table(struct ctl_table_header *header) if (--header->nreg) return; - if (parent) + if (parent) { put_links(header); - start_unregistering(header); + start_unregistering(header); + } + if (!--header->count) kfree_rcu(header, rcu); diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 2df3b2358b0a..c3faa39c0ce1 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -290,6 +290,7 @@ static int proc_map_release(struct inode *inode, struct file *file) if (priv->mm) mmdrop(priv->mm); + kfree(priv->rollup); return seq_release_private(inode, file); } @@ -316,6 +317,23 @@ static int is_stack(struct proc_maps_private *priv, vma->vm_end >= vma->vm_mm->start_stack; } +static void show_vma_header_prefix(struct seq_file *m, + unsigned long start, unsigned long end, + vm_flags_t flags, unsigned long long pgoff, + dev_t dev, unsigned long ino) +{ + seq_setwidth(m, 25 + sizeof(void *) * 6 - 1); + seq_printf(m, "%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu ", + start, + end, + flags & VM_READ ? 'r' : '-', + flags & VM_WRITE ? 'w' : '-', + flags & VM_EXEC ? 'x' : '-', + flags & VM_MAYSHARE ? 's' : 'p', + pgoff, + MAJOR(dev), MINOR(dev), ino); +} + static void show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid) { @@ -339,17 +357,7 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid) /* We don't show the stack guard page in /proc/maps */ start = vma->vm_start; end = vma->vm_end; - - seq_setwidth(m, 25 + sizeof(void *) * 6 - 1); - seq_printf(m, "%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu ", - start, - end, - flags & VM_READ ? 'r' : '-', - flags & VM_WRITE ? 'w' : '-', - flags & VM_EXEC ? 'x' : '-', - flags & VM_MAYSHARE ? 's' : 'p', - pgoff, - MAJOR(dev), MINOR(dev), ino); + show_vma_header_prefix(m, start, end, flags, pgoff, dev, ino); /* * Print the dentry name for named mappings, and a @@ -474,6 +482,7 @@ const struct file_operations proc_tid_maps_operations = { #ifdef CONFIG_PROC_PAGE_MONITOR struct mem_size_stats { + bool first; unsigned long resident; unsigned long shared_clean; unsigned long shared_dirty; @@ -485,7 +494,9 @@ struct mem_size_stats { unsigned long swap; unsigned long shared_hugetlb; unsigned long private_hugetlb; + unsigned long first_vma_start; u64 pss; + u64 pss_locked; u64 swap_pss; }; @@ -695,69 +706,105 @@ static int smaps_hugetlb_range(pte_t *pte, unsigned long hmask, static int show_smap(struct seq_file *m, void *v, int is_pid) { + struct proc_maps_private *priv = m->private; struct vm_area_struct *vma = v; - struct mem_size_stats mss; + struct mem_size_stats mss_stack; + struct mem_size_stats *mss; struct mm_walk smaps_walk = { .pmd_entry = smaps_pte_range, #ifdef CONFIG_HUGETLB_PAGE .hugetlb_entry = smaps_hugetlb_range, #endif .mm = vma->vm_mm, - .private = &mss, }; + int ret = 0; + bool rollup_mode; + bool last_vma; + + if (priv->rollup) { + rollup_mode = true; + mss = priv->rollup; + if (mss->first) { + mss->first_vma_start = vma->vm_start; + mss->first = false; + } + last_vma = !m_next_vma(priv, vma); + } else { + rollup_mode = false; + memset(&mss_stack, 0, sizeof(mss_stack)); + mss = &mss_stack; + } + + smaps_walk.private = mss; - memset(&mss, 0, sizeof mss); /* mmap_sem is held in m_start */ walk_page_vma(vma, &smaps_walk); + if (vma->vm_flags & VM_LOCKED) + mss->pss_locked += mss->pss; + + if (!rollup_mode) { + show_map_vma(m, vma, is_pid); + } else if (last_vma) { + show_vma_header_prefix( + m, mss->first_vma_start, vma->vm_end, 0, 0, 0, 0); + seq_pad(m, ' '); + seq_puts(m, "[rollup]\n"); + } else { + ret = SEQ_SKIP; + } - show_map_vma(m, vma, is_pid); - - if (vma_get_anon_name(vma)) { + if (!rollup_mode && vma_get_anon_name(vma)) { seq_puts(m, "Name: "); seq_print_vma_name(m, vma); seq_putc(m, '\n'); } - seq_printf(m, - "Size: %8lu kB\n" - "Rss: %8lu kB\n" - "Pss: %8lu kB\n" - "Shared_Clean: %8lu kB\n" - "Shared_Dirty: %8lu kB\n" - "Private_Clean: %8lu kB\n" - "Private_Dirty: %8lu kB\n" - "Referenced: %8lu kB\n" - "Anonymous: %8lu kB\n" - "AnonHugePages: %8lu kB\n" - "Shared_Hugetlb: %8lu kB\n" - "Private_Hugetlb: %7lu kB\n" - "Swap: %8lu kB\n" - "SwapPss: %8lu kB\n" - "KernelPageSize: %8lu kB\n" - "MMUPageSize: %8lu kB\n" - "Locked: %8lu kB\n", - (vma->vm_end - vma->vm_start) >> 10, - mss.resident >> 10, - (unsigned long)(mss.pss >> (10 + PSS_SHIFT)), - mss.shared_clean >> 10, - mss.shared_dirty >> 10, - mss.private_clean >> 10, - mss.private_dirty >> 10, - mss.referenced >> 10, - mss.anonymous >> 10, - mss.anonymous_thp >> 10, - mss.shared_hugetlb >> 10, - mss.private_hugetlb >> 10, - mss.swap >> 10, - (unsigned long)(mss.swap_pss >> (10 + PSS_SHIFT)), - vma_kernel_pagesize(vma) >> 10, - vma_mmu_pagesize(vma) >> 10, - (vma->vm_flags & VM_LOCKED) ? - (unsigned long)(mss.pss >> (10 + PSS_SHIFT)) : 0); - - show_smap_vma_flags(m, vma); + if (!rollup_mode) + seq_printf(m, + "Size: %8lu kB\n" + "KernelPageSize: %8lu kB\n" + "MMUPageSize: %8lu kB\n", + (vma->vm_end - vma->vm_start) >> 10, + vma_kernel_pagesize(vma) >> 10, + vma_mmu_pagesize(vma) >> 10); + + + if (!rollup_mode || last_vma) + seq_printf(m, + "Rss: %8lu kB\n" + "Pss: %8lu kB\n" + "Shared_Clean: %8lu kB\n" + "Shared_Dirty: %8lu kB\n" + "Private_Clean: %8lu kB\n" + "Private_Dirty: %8lu kB\n" + "Referenced: %8lu kB\n" + "Anonymous: %8lu kB\n" + "AnonHugePages: %8lu kB\n" + "Shared_Hugetlb: %8lu kB\n" + "Private_Hugetlb: %7lu kB\n" + "Swap: %8lu kB\n" + "SwapPss: %8lu kB\n" + "Locked: %8lu kB\n", + mss->resident >> 10, + (unsigned long)(mss->pss >> (10 + PSS_SHIFT)), + mss->shared_clean >> 10, + mss->shared_dirty >> 10, + mss->private_clean >> 10, + mss->private_dirty >> 10, + mss->referenced >> 10, + mss->anonymous >> 10, + mss->anonymous_thp >> 10, + mss->shared_hugetlb >> 10, + mss->private_hugetlb >> 10, + mss->swap >> 10, + (unsigned long)(mss->swap_pss >> (10 + PSS_SHIFT)), + (unsigned long)(mss->pss >> (10 + PSS_SHIFT))); + + if (!rollup_mode) { + show_smap_vma_flags(m, vma); + } m_cache_vma(m, vma); - return 0; + return ret; } static int show_pid_smap(struct seq_file *m, void *v) @@ -789,6 +836,25 @@ static int pid_smaps_open(struct inode *inode, struct file *file) return do_maps_open(inode, file, &proc_pid_smaps_op); } +static int pid_smaps_rollup_open(struct inode *inode, struct file *file) +{ + struct seq_file *seq; + struct proc_maps_private *priv; + int ret = do_maps_open(inode, file, &proc_pid_smaps_op); + + if (ret < 0) + return ret; + seq = file->private_data; + priv = seq->private; + priv->rollup = kzalloc(sizeof(*priv->rollup), GFP_KERNEL); + if (!priv->rollup) { + proc_map_release(inode, file); + return -ENOMEM; + } + priv->rollup->first = true; + return 0; +} + static int tid_smaps_open(struct inode *inode, struct file *file) { return do_maps_open(inode, file, &proc_tid_smaps_op); @@ -801,6 +867,13 @@ const struct file_operations proc_pid_smaps_operations = { .release = proc_map_release, }; +const struct file_operations proc_pid_smaps_rollup_operations = { + .open = pid_smaps_rollup_open, + .read = seq_read, + .llseek = seq_lseek, + .release = proc_map_release, +}; + const struct file_operations proc_tid_smaps_operations = { .open = tid_smaps_open, .read = seq_read, @@ -1011,6 +1084,24 @@ static ssize_t clear_refs_write(struct file *file, const char __user *buf, continue; up_read(&mm->mmap_sem); down_write(&mm->mmap_sem); + /* + * Avoid to modify vma->vm_flags + * without locked ops while the + * coredump reads the vm_flags. + */ + if (!mmget_still_valid(mm)) { + /* + * Silently return "count" + * like if get_task_mm() + * failed. FIXME: should this + * function have returned + * -ESRCH if get_task_mm() + * failed like if + * get_proc_task() fails? + */ + up_write(&mm->mmap_sem); + goto out_mm; + } for (vma = mm->mmap; vma; vma = vma->vm_next) { vma->vm_flags &= ~VM_SOFTDIRTY; vma_set_page_prot(vma); diff --git a/fs/read_write.c b/fs/read_write.c index 16e554ba885d..7b175b9134ec 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -553,12 +553,13 @@ EXPORT_SYMBOL(vfs_write); static inline loff_t file_pos_read(struct file *file) { - return file->f_pos; + return file->f_mode & FMODE_STREAM ? 0 : file->f_pos; } static inline void file_pos_write(struct file *file, loff_t pos) { - file->f_pos = pos; + if ((file->f_mode & FMODE_STREAM) == 0) + file->f_pos = pos; } SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count) diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index 7693b0e8efef..45cfa73b285e 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -313,6 +313,8 @@ static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m, seq_printf(m, ",reserved=%uMB", opts->reserved_mb); if (opts->nocache) seq_printf(m, ",nocache"); + if (opts->unshared_obb) + seq_printf(m, ",unshared_obb"); return 0; }; diff --git a/fs/ufs/util.h b/fs/ufs/util.h index 3f9463f8cf2f..f877d5cadd98 100644 --- a/fs/ufs/util.h +++ b/fs/ufs/util.h @@ -228,7 +228,7 @@ ufs_get_inode_gid(struct super_block *sb, struct ufs_inode *inode) case UFS_UID_44BSD: return fs32_to_cpu(sb, inode->ui_u3.ui_44.ui_gid); case UFS_UID_EFT: - if (inode->ui_u1.oldids.ui_suid == 0xFFFF) + if (inode->ui_u1.oldids.ui_sgid == 0xFFFF) return fs32_to_cpu(sb, inode->ui_u3.ui_sun.ui_gid); /* Fall through */ default: diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c index e7cc0d860499..08cc09b999fd 100644 --- a/fs/userfaultfd.c +++ b/fs/userfaultfd.c @@ -137,7 +137,7 @@ static void userfaultfd_ctx_put(struct userfaultfd_ctx *ctx) VM_BUG_ON(waitqueue_active(&ctx->fault_wqh)); VM_BUG_ON(spin_is_locked(&ctx->fd_wqh.lock)); VM_BUG_ON(waitqueue_active(&ctx->fd_wqh)); - mmput(ctx->mm); + mmdrop(ctx->mm); kmem_cache_free(userfaultfd_ctx_cachep, ctx); } } @@ -434,6 +434,9 @@ static int userfaultfd_release(struct inode *inode, struct file *file) ACCESS_ONCE(ctx->released) = true; + if (!mmget_not_zero(mm)) + goto wakeup; + /* * Flush page faults out of all CPUs. NOTE: all page faults * must be retried without returning VM_FAULT_SIGBUS if @@ -443,6 +446,8 @@ static int userfaultfd_release(struct inode *inode, struct file *file) * taking the mmap_sem for writing. */ down_write(&mm->mmap_sem); + if (!mmget_still_valid(mm)) + goto skip_mm; prev = NULL; for (vma = mm->mmap; vma; vma = vma->vm_next) { cond_resched(); @@ -466,8 +471,10 @@ static int userfaultfd_release(struct inode *inode, struct file *file) vma->vm_flags = new_flags; vma->vm_userfaultfd_ctx = NULL_VM_UFFD_CTX; } +skip_mm: up_write(&mm->mmap_sem); - + mmput(mm); +wakeup: /* * After no new page faults can wait on this fault_*wqh, flush * the last page faults that may have been already waiting on @@ -761,10 +768,14 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx, start = uffdio_register.range.start; end = start + uffdio_register.range.len; + ret = -ENOMEM; + if (!mmget_not_zero(mm)) + goto out; + down_write(&mm->mmap_sem); + if (!mmget_still_valid(mm)) + goto out_unlock; vma = find_vma_prev(mm, start, &prev); - - ret = -ENOMEM; if (!vma) goto out_unlock; @@ -879,6 +890,7 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx, } while (vma && vma->vm_start < end); out_unlock: up_write(&mm->mmap_sem); + mmput(mm); if (!ret) { /* * Now that we scanned all vmas we can already tell @@ -917,10 +929,14 @@ static int userfaultfd_unregister(struct userfaultfd_ctx *ctx, start = uffdio_unregister.start; end = start + uffdio_unregister.len; + ret = -ENOMEM; + if (!mmget_not_zero(mm)) + goto out; + down_write(&mm->mmap_sem); + if (!mmget_still_valid(mm)) + goto out_unlock; vma = find_vma_prev(mm, start, &prev); - - ret = -ENOMEM; if (!vma) goto out_unlock; @@ -1015,6 +1031,7 @@ static int userfaultfd_unregister(struct userfaultfd_ctx *ctx, } while (vma && vma->vm_start < end); out_unlock: up_write(&mm->mmap_sem); + mmput(mm); out: return ret; } @@ -1084,9 +1101,11 @@ static int userfaultfd_copy(struct userfaultfd_ctx *ctx, goto out; if (uffdio_copy.mode & ~UFFDIO_COPY_MODE_DONTWAKE) goto out; - - ret = mcopy_atomic(ctx->mm, uffdio_copy.dst, uffdio_copy.src, - uffdio_copy.len); + if (mmget_not_zero(ctx->mm)) { + ret = mcopy_atomic(ctx->mm, uffdio_copy.dst, uffdio_copy.src, + uffdio_copy.len); + mmput(ctx->mm); + } if (unlikely(put_user(ret, &user_uffdio_copy->copy))) return -EFAULT; if (ret < 0) @@ -1127,8 +1146,11 @@ static int userfaultfd_zeropage(struct userfaultfd_ctx *ctx, if (uffdio_zeropage.mode & ~UFFDIO_ZEROPAGE_MODE_DONTWAKE) goto out; - ret = mfill_zeropage(ctx->mm, uffdio_zeropage.range.start, - uffdio_zeropage.range.len); + if (mmget_not_zero(ctx->mm)) { + ret = mfill_zeropage(ctx->mm, uffdio_zeropage.range.start, + uffdio_zeropage.range.len); + mmput(ctx->mm); + } if (unlikely(put_user(ret, &user_uffdio_zeropage->zeropage))) return -EFAULT; if (ret < 0) @@ -1306,12 +1328,12 @@ static struct file *userfaultfd_file_create(int flags) ctx->released = false; ctx->mm = current->mm; /* prevent the mm struct to be freed */ - atomic_inc(&ctx->mm->mm_users); + atomic_inc(&ctx->mm->mm_count); file = anon_inode_getfile("[userfaultfd]", &userfaultfd_fops, ctx, O_RDWR | (flags & UFFD_SHARED_FCNTL_FLAGS)); if (IS_ERR(file)) { - mmput(ctx->mm); + mmdrop(ctx->mm); kmem_cache_free(userfaultfd_ctx_cachep, ctx); } out: diff --git a/include/linux/backing-dev-defs.h b/include/linux/backing-dev-defs.h index a6296c2ffd93..b90dff08edb0 100644 --- a/include/linux/backing-dev-defs.h +++ b/include/linux/backing-dev-defs.h @@ -160,6 +160,7 @@ struct backing_dev_info { struct radix_tree_root cgwb_tree; /* radix tree of active cgroup wbs */ struct rb_root cgwb_congested_tree; /* their congested states */ atomic_t usage_cnt; /* counts both cgwbs and cgwb_contested's */ + struct rw_semaphore wb_switch_rwsem; /* no cgwb switch while syncing */ #else struct bdi_writeback_congested *wb_congested; #endif diff --git a/include/linux/bio.h b/include/linux/bio.h index 42e4e3cbb001..0ffb9a397620 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -290,7 +290,7 @@ static inline void bio_cnt_set(struct bio *bio, unsigned int count) { if (count != 1) { bio->bi_flags |= (1 << BIO_REFFED); - smp_mb__before_atomic(); + smp_mb(); } atomic_set(&bio->__bi_cnt, count); } diff --git a/include/linux/bitops.h b/include/linux/bitops.h index defeaac0745f..83edade218fa 100644 --- a/include/linux/bitops.h +++ b/include/linux/bitops.h @@ -1,28 +1,9 @@ #ifndef _LINUX_BITOPS_H #define _LINUX_BITOPS_H #include <asm/types.h> +#include <linux/bits.h> -#ifdef __KERNEL__ -#define BIT(nr) (1UL << (nr)) -#define BIT_ULL(nr) (1ULL << (nr)) -#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) -#define BIT_WORD(nr) ((nr) / BITS_PER_LONG) -#define BIT_ULL_MASK(nr) (1ULL << ((nr) % BITS_PER_LONG_LONG)) -#define BIT_ULL_WORD(nr) ((nr) / BITS_PER_LONG_LONG) -#define BITS_PER_BYTE 8 #define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) -#endif - -/* - * Create a contiguous bitmask starting at bit position @l and ending at - * position @h. For example - * GENMASK_ULL(39, 21) gives us the 64bit vector 0x000000ffffe00000. - */ -#define GENMASK(h, l) \ - (((~0UL) << (l)) & (~0UL >> (BITS_PER_LONG - 1 - (h)))) - -#define GENMASK_ULL(h, l) \ - (((~0ULL) << (l)) & (~0ULL >> (BITS_PER_LONG_LONG - 1 - (h)))) extern unsigned int __sw_hweight8(unsigned int w); extern unsigned int __sw_hweight16(unsigned int w); @@ -87,7 +68,7 @@ static __always_inline unsigned long hweight_long(unsigned long w) */ static inline __u64 rol64(__u64 word, unsigned int shift) { - return (word << shift) | (word >> (64 - shift)); + return (word << (shift & 63)) | (word >> ((-shift) & 63)); } /** @@ -97,7 +78,7 @@ static inline __u64 rol64(__u64 word, unsigned int shift) */ static inline __u64 ror64(__u64 word, unsigned int shift) { - return (word >> shift) | (word << (64 - shift)); + return (word >> (shift & 63)) | (word << ((-shift) & 63)); } /** @@ -107,7 +88,7 @@ static inline __u64 ror64(__u64 word, unsigned int shift) */ static inline __u32 rol32(__u32 word, unsigned int shift) { - return (word << shift) | (word >> ((-shift) & 31)); + return (word << (shift & 31)) | (word >> ((-shift) & 31)); } /** @@ -117,7 +98,7 @@ static inline __u32 rol32(__u32 word, unsigned int shift) */ static inline __u32 ror32(__u32 word, unsigned int shift) { - return (word >> shift) | (word << (32 - shift)); + return (word >> (shift & 31)) | (word << ((-shift) & 31)); } /** @@ -127,7 +108,7 @@ static inline __u32 ror32(__u32 word, unsigned int shift) */ static inline __u16 rol16(__u16 word, unsigned int shift) { - return (word << shift) | (word >> (16 - shift)); + return (word << (shift & 15)) | (word >> ((-shift) & 15)); } /** @@ -137,7 +118,7 @@ static inline __u16 rol16(__u16 word, unsigned int shift) */ static inline __u16 ror16(__u16 word, unsigned int shift) { - return (word >> shift) | (word << (16 - shift)); + return (word >> (shift & 15)) | (word << ((-shift) & 15)); } /** @@ -147,7 +128,7 @@ static inline __u16 ror16(__u16 word, unsigned int shift) */ static inline __u8 rol8(__u8 word, unsigned int shift) { - return (word << shift) | (word >> (8 - shift)); + return (word << (shift & 7)) | (word >> ((-shift) & 7)); } /** @@ -157,7 +138,7 @@ static inline __u8 rol8(__u8 word, unsigned int shift) */ static inline __u8 ror8(__u8 word, unsigned int shift) { - return (word >> shift) | (word << (8 - shift)); + return (word >> (shift & 7)) | (word << ((-shift) & 7)); } /** diff --git a/include/linux/bits.h b/include/linux/bits.h new file mode 100644 index 000000000000..2b7b532c1d51 --- /dev/null +++ b/include/linux/bits.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LINUX_BITS_H +#define __LINUX_BITS_H +#include <asm/bitsperlong.h> + +#define BIT(nr) (1UL << (nr)) +#define BIT_ULL(nr) (1ULL << (nr)) +#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) +#define BIT_WORD(nr) ((nr) / BITS_PER_LONG) +#define BIT_ULL_MASK(nr) (1ULL << ((nr) % BITS_PER_LONG_LONG)) +#define BIT_ULL_WORD(nr) ((nr) / BITS_PER_LONG_LONG) +#define BITS_PER_BYTE 8 + +/* + * Create a contiguous bitmask starting at bit position @l and ending at + * position @h. For example + * GENMASK_ULL(39, 21) gives us the 64bit vector 0x000000ffffe00000. + */ +#define GENMASK(h, l) \ + (((~0UL) - (1UL << (l)) + 1) & (~0UL >> (BITS_PER_LONG - 1 - (h)))) + +#define GENMASK_ULL(h, l) \ + (((~0ULL) - (1ULL << (l)) + 1) & \ + (~0ULL >> (BITS_PER_LONG_LONG - 1 - (h)))) + +#endif /* __LINUX_BITS_H */ diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 210ccc4ea44b..8607c937145f 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -453,7 +453,7 @@ static inline struct cgroup_subsys_state *task_css(struct task_struct *task, * * Find the css for the (@task, @subsys_id) combination, increment a * reference on and return it. This function is guaranteed to return a - * valid css. + * valid css. The returned css may already have been offlined. */ static inline struct cgroup_subsys_state * task_get_css(struct task_struct *task, int subsys_id) @@ -463,7 +463,13 @@ task_get_css(struct task_struct *task, int subsys_id) rcu_read_lock(); while (true) { css = task_css(task, subsys_id); - if (likely(css_tryget_online(css))) + /* + * Can't use css_tryget_online() here. A task which has + * PF_EXITING set may stay associated with an offline css. + * If such task calls this function, css_tryget_online() + * will keep failing. + */ + if (likely(css_tryget(css))) break; cpu_relax(); } diff --git a/include/linux/cpu.h b/include/linux/cpu.h index ec088f5729fb..960f2750bc42 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -63,6 +63,8 @@ extern ssize_t cpu_show_spec_store_bypass(struct device *dev, struct device_attribute *attr, char *buf); extern ssize_t cpu_show_l1tf(struct device *dev, struct device_attribute *attr, char *buf); +extern ssize_t cpu_show_mds(struct device *dev, + struct device_attribute *attr, char *buf); extern __printf(4, 5) struct device *cpu_device_create(struct device *parent, void *drvdata, @@ -312,6 +314,23 @@ bool cpu_wait_death(unsigned int cpu, int seconds); bool cpu_report_death(void); #endif /* #ifdef CONFIG_HOTPLUG_CPU */ +/* + * These are used for a global "mitigations=" cmdline option for toggling + * optional CPU mitigations. + */ +enum cpu_mitigations { + CPU_MITIGATIONS_OFF, + CPU_MITIGATIONS_AUTO, +}; + +extern enum cpu_mitigations cpu_mitigations; + +/* mitigations=off */ +static inline bool cpu_mitigations_off(void) +{ + return cpu_mitigations == CPU_MITIGATIONS_OFF; +} + #define IDLE_START 1 #define IDLE_END 2 diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index b19433738690..de8f11461536 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -164,6 +164,10 @@ struct f2fs_checkpoint { unsigned char sit_nat_version_bitmap[1]; } __packed; +#define CP_CHKSUM_OFFSET 4092 /* default chksum offset in checkpoint */ +#define CP_MIN_CHKSUM_OFFSET \ + (offsetof(struct f2fs_checkpoint, sit_nat_version_bitmap)) + /* * For orphan inode management */ @@ -198,11 +202,12 @@ struct f2fs_extent { get_extra_isize(inode)) #define DEF_NIDS_PER_INODE 5 /* Node IDs in an Inode */ #define ADDRS_PER_INODE(inode) addrs_per_inode(inode) -#define ADDRS_PER_BLOCK 1018 /* Address Pointers in a Direct Block */ +#define DEF_ADDRS_PER_BLOCK 1018 /* Address Pointers in a Direct Block */ +#define ADDRS_PER_BLOCK(inode) addrs_per_block(inode) #define NIDS_PER_BLOCK 1018 /* Node IDs in an Indirect Block */ #define ADDRS_PER_PAGE(page, inode) \ - (IS_INODE(page) ? ADDRS_PER_INODE(inode) : ADDRS_PER_BLOCK) + (IS_INODE(page) ? ADDRS_PER_INODE(inode) : ADDRS_PER_BLOCK(inode)) #define NODE_DIR1_BLOCK (DEF_ADDRS_PER_INODE + 1) #define NODE_DIR2_BLOCK (DEF_ADDRS_PER_INODE + 2) @@ -267,7 +272,7 @@ struct f2fs_inode { } __packed; struct direct_node { - __le32 addr[ADDRS_PER_BLOCK]; /* array of data block address */ + __le32 addr[DEF_ADDRS_PER_BLOCK]; /* array of data block address */ } __packed; struct indirect_node { diff --git a/include/linux/fs.h b/include/linux/fs.h index e0472dc3a1a0..7493e807f073 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -143,6 +143,9 @@ typedef void (dax_iodone_t)(struct buffer_head *bh_map, int uptodate); /* Has write method(s) */ #define FMODE_CAN_WRITE ((__force fmode_t)0x40000) +/* File is stream-like */ +#define FMODE_STREAM ((__force fmode_t)0x200000) + /* File was opened by fanotify and shouldn't generate fanotify events */ #define FMODE_NONOTIFY ((__force fmode_t)0x4000000) @@ -2770,6 +2773,7 @@ extern loff_t fixed_size_llseek(struct file *file, loff_t offset, int whence, loff_t size); extern int generic_file_open(struct inode * inode, struct file * filp); extern int nonseekable_open(struct inode * inode, struct file * filp); +extern int stream_open(struct inode * inode, struct file * filp); #ifdef CONFIG_BLOCK typedef void (dio_submit_t)(int rw, struct bio *bio, struct inode *inode, diff --git a/include/linux/hid.h b/include/linux/hid.h index d16de62231d3..5f1e901353ed 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -378,6 +378,7 @@ struct hid_global { struct hid_local { unsigned usage[HID_MAX_USAGES]; /* usage array */ + u8 usage_size[HID_MAX_USAGES]; /* usage size array */ unsigned collection_index[HID_MAX_USAGES]; /* collection index array */ unsigned usage_index; unsigned usage_minimum; diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index ac19fa4829aa..b71d142a93f3 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -91,9 +91,7 @@ void putback_active_hugepage(struct page *page); void free_huge_page(struct page *page); void hugetlb_fix_reserve_counts(struct inode *inode, bool restore_reserve); extern struct mutex *hugetlb_fault_mutex_table; -u32 hugetlb_fault_mutex_hash(struct hstate *h, struct mm_struct *mm, - struct vm_area_struct *vma, - struct address_space *mapping, +u32 hugetlb_fault_mutex_hash(struct hstate *h, struct address_space *mapping, pgoff_t idx, unsigned long address); pte_t *huge_pmd_share(struct mm_struct *mm, unsigned long addr, pud_t *pud); diff --git a/include/linux/iio/adc/ad_sigma_delta.h b/include/linux/iio/adc/ad_sigma_delta.h index 6cc48ac55fd2..40b14736c73d 100644 --- a/include/linux/iio/adc/ad_sigma_delta.h +++ b/include/linux/iio/adc/ad_sigma_delta.h @@ -66,6 +66,7 @@ struct ad_sigma_delta { bool irq_dis; bool bus_locked; + bool keep_cs_asserted; uint8_t comm; diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h index 68904469fba1..2209eb0740b0 100644 --- a/include/linux/jump_label.h +++ b/include/linux/jump_label.h @@ -267,9 +267,15 @@ struct static_key_false { #define DEFINE_STATIC_KEY_TRUE(name) \ struct static_key_true name = STATIC_KEY_TRUE_INIT +#define DECLARE_STATIC_KEY_TRUE(name) \ + extern struct static_key_true name + #define DEFINE_STATIC_KEY_FALSE(name) \ struct static_key_false name = STATIC_KEY_FALSE_INIT +#define DECLARE_STATIC_KEY_FALSE(name) \ + extern struct static_key_false name + extern bool ____wrong_branch_error(void); #define static_key_enabled(x) \ diff --git a/include/linux/list_lru.h b/include/linux/list_lru.h index 743b34f56f2b..f9a8889e134a 100644 --- a/include/linux/list_lru.h +++ b/include/linux/list_lru.h @@ -51,6 +51,7 @@ struct list_lru { struct list_lru_node *node; #ifdef CONFIG_MEMCG_KMEM struct list_head list; + bool memcg_aware; #endif }; diff --git a/include/linux/mfd/da9063/registers.h b/include/linux/mfd/da9063/registers.h index 2e0ba6d5fbc3..f97173bef581 100644 --- a/include/linux/mfd/da9063/registers.h +++ b/include/linux/mfd/da9063/registers.h @@ -215,9 +215,9 @@ /* DA9063 Configuration registers */ /* OTP */ -#define DA9063_REG_OPT_COUNT 0x101 -#define DA9063_REG_OPT_ADDR 0x102 -#define DA9063_REG_OPT_DATA 0x103 +#define DA9063_REG_OTP_CONT 0x101 +#define DA9063_REG_OTP_ADDR 0x102 +#define DA9063_REG_OTP_DATA 0x103 /* Customer Trim and Configuration */ #define DA9063_REG_T_OFFSET 0x104 diff --git a/include/linux/mm.h b/include/linux/mm.h index 47c7bb494c74..ecdabf3597ae 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1115,6 +1115,27 @@ void zap_page_range(struct vm_area_struct *vma, unsigned long address, void unmap_vmas(struct mmu_gather *tlb, struct vm_area_struct *start_vma, unsigned long start, unsigned long end); +/* + * This has to be called after a get_task_mm()/mmget_not_zero() + * followed by taking the mmap_sem for writing before modifying the + * vmas or anything the coredump pretends not to change from under it. + * + * NOTE: find_extend_vma() called from GUP context is the only place + * that can modify the "mm" (notably the vm_start/end) under mmap_sem + * for reading and outside the context of the process, so it is also + * the only case that holds the mmap_sem for reading that must call + * this function. Generally if the mmap_sem is hold for reading + * there's no need of this check after get_task_mm()/mmget_not_zero(). + * + * This function can be obsoleted and the check can be removed, after + * the coredump code will hold the mmap_sem for writing before + * invoking the ->core_dump methods. + */ +static inline bool mmget_still_valid(struct mm_struct *mm) +{ + return likely(!mm->core_state); +} + /** * mm_walk - callbacks for walk_page_range * @pmd_entry: if set, called for each non-empty PMD (3rd-level) entry diff --git a/include/linux/of.h b/include/linux/of.h index d9371c9cd88a..2772f027f88f 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -199,8 +199,8 @@ extern struct device_node *of_find_all_nodes(struct device_node *prev); static inline u64 of_read_number(const __be32 *cell, int size) { u64 r = 0; - while (size--) - r = (r << 32) | be32_to_cpu(*(cell++)); + for (; size--; cell++) + r = (r << 32) | be32_to_cpu(*cell); return r; } diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index 81fdf4b8aba4..8b1e2bd46bb7 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -57,14 +57,17 @@ extern void exit_ptrace(struct task_struct *tracer, struct list_head *dead); #define PTRACE_MODE_READ 0x01 #define PTRACE_MODE_ATTACH 0x02 #define PTRACE_MODE_NOAUDIT 0x04 -#define PTRACE_MODE_FSCREDS 0x08 -#define PTRACE_MODE_REALCREDS 0x10 +#define PTRACE_MODE_FSCREDS 0x08 +#define PTRACE_MODE_REALCREDS 0x10 +#define PTRACE_MODE_SCHED 0x20 +#define PTRACE_MODE_IBPB 0x40 /* shorthands for READ/ATTACH and FSCREDS/REALCREDS combinations */ #define PTRACE_MODE_READ_FSCREDS (PTRACE_MODE_READ | PTRACE_MODE_FSCREDS) #define PTRACE_MODE_READ_REALCREDS (PTRACE_MODE_READ | PTRACE_MODE_REALCREDS) #define PTRACE_MODE_ATTACH_FSCREDS (PTRACE_MODE_ATTACH | PTRACE_MODE_FSCREDS) #define PTRACE_MODE_ATTACH_REALCREDS (PTRACE_MODE_ATTACH | PTRACE_MODE_REALCREDS) +#define PTRACE_MODE_SPEC_IBPB (PTRACE_MODE_ATTACH_REALCREDS | PTRACE_MODE_IBPB) /** * ptrace_may_access - check whether the caller is permitted to access @@ -82,6 +85,20 @@ extern void exit_ptrace(struct task_struct *tracer, struct list_head *dead); */ extern bool ptrace_may_access(struct task_struct *task, unsigned int mode); +/** + * ptrace_may_access - check whether the caller is permitted to access + * a target task. + * @task: target task + * @mode: selects type of access and caller credentials + * + * Returns true on success, false on denial. + * + * Similar to ptrace_may_access(). Only to be called from context switch + * code. Does not call into audit and the regular LSM hooks due to locking + * constraints. + */ +extern bool ptrace_may_access_sched(struct task_struct *task, unsigned int mode); + static inline int ptrace_reparented(struct task_struct *child) { return !same_thread_group(child->real_parent, child->parent); diff --git a/include/linux/pwm.h b/include/linux/pwm.h index aa8736d5b2f3..cfc3ed46cad2 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -331,7 +331,6 @@ static inline void pwm_remove_table(struct pwm_lookup *table, size_t num) #ifdef CONFIG_PWM_SYSFS void pwmchip_sysfs_export(struct pwm_chip *chip); void pwmchip_sysfs_unexport(struct pwm_chip *chip); -void pwmchip_sysfs_unexport_children(struct pwm_chip *chip); #else static inline void pwmchip_sysfs_export(struct pwm_chip *chip) { @@ -340,10 +339,6 @@ static inline void pwmchip_sysfs_export(struct pwm_chip *chip) static inline void pwmchip_sysfs_unexport(struct pwm_chip *chip) { } - -static inline void pwmchip_sysfs_unexport_children(struct pwm_chip *chip) -{ -} #endif /* CONFIG_PWM_SYSFS */ #endif /* __LINUX_PWM_H */ diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index a0189ba67fde..addd03641e1a 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -297,14 +297,12 @@ void synchronize_rcu(void); static inline void __rcu_read_lock(void) { - if (IS_ENABLED(CONFIG_PREEMPT_COUNT)) - preempt_disable(); + preempt_disable(); } static inline void __rcu_read_unlock(void) { - if (IS_ENABLED(CONFIG_PREEMPT_COUNT)) - preempt_enable(); + preempt_enable(); } static inline void synchronize_rcu(void) diff --git a/include/linux/sched.h b/include/linux/sched.h index ded334b5cf8d..2378cbf2612d 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -378,7 +378,7 @@ extern int lockdep_tasklist_lock_is_held(void); extern void sched_init(void); extern void sched_init_smp(void); extern asmlinkage void schedule_tail(struct task_struct *prev); -extern void init_idle(struct task_struct *idle, int cpu, bool hotplug); +extern void init_idle(struct task_struct *idle, int cpu); extern void init_idle_bootup_task(struct task_struct *idle); extern cpumask_var_t cpu_isolated_map; @@ -2459,6 +2459,8 @@ static inline void memalloc_noio_restore(unsigned int flags) #define PFA_SPREAD_SLAB 2 /* Spread some slab caches over cpuset */ #define PFA_SPEC_SSB_DISABLE 4 /* Speculative Store Bypass disabled */ #define PFA_SPEC_SSB_FORCE_DISABLE 5 /* Speculative Store Bypass force disabled*/ +#define PFA_SPEC_IB_DISABLE 6 /* Indirect branch speculation restricted */ +#define PFA_SPEC_IB_FORCE_DISABLE 7 /* Indirect branch speculation permanently restricted */ #define TASK_PFA_TEST(name, func) \ @@ -2489,6 +2491,13 @@ TASK_PFA_CLEAR(SPEC_SSB_DISABLE, spec_ssb_disable) TASK_PFA_TEST(SPEC_SSB_FORCE_DISABLE, spec_ssb_force_disable) TASK_PFA_SET(SPEC_SSB_FORCE_DISABLE, spec_ssb_force_disable) +TASK_PFA_TEST(SPEC_IB_DISABLE, spec_ib_disable) +TASK_PFA_SET(SPEC_IB_DISABLE, spec_ib_disable) +TASK_PFA_CLEAR(SPEC_IB_DISABLE, spec_ib_disable) + +TASK_PFA_TEST(SPEC_IB_FORCE_DISABLE, spec_ib_force_disable) +TASK_PFA_SET(SPEC_IB_FORCE_DISABLE, spec_ib_force_disable) + /* * task->jobctl flags */ @@ -3007,12 +3016,17 @@ extern struct mm_struct * mm_alloc(void); /* mmdrop drops the mm and the page tables */ extern void __mmdrop(struct mm_struct *); -static inline void mmdrop(struct mm_struct * mm) +static inline void mmdrop(struct mm_struct *mm) { if (unlikely(atomic_dec_and_test(&mm->mm_count))) __mmdrop(mm); } +static inline bool mmget_not_zero(struct mm_struct *mm) +{ + return atomic_inc_not_zero(&mm->mm_users); +} + /* mmput gets rid of the mappings and all user-space */ extern int mmput(struct mm_struct *); /* same as above but performs the slow path from the async kontext. Can diff --git a/include/linux/sched/smt.h b/include/linux/sched/smt.h new file mode 100644 index 000000000000..559ac4590593 --- /dev/null +++ b/include/linux/sched/smt.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_SCHED_SMT_H +#define _LINUX_SCHED_SMT_H + +#include <linux/atomic.h> + +#ifdef CONFIG_SCHED_SMT +extern atomic_t sched_smt_present; + +static __always_inline bool sched_smt_active(void) +{ + return atomic_read(&sched_smt_present); +} +#else +static inline bool sched_smt_active(void) { return false; } +#endif + +void arch_smt_update(void); + +#endif diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 502787c29ce9..a2f12d377d23 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -3664,5 +3664,35 @@ static inline unsigned int skb_gso_network_seglen(const struct sk_buff *skb) return hdr_len + skb_gso_transport_seglen(skb); } +/** + * skb_gso_mac_seglen - Return length of individual segments of a gso packet + * + * @skb: GSO skb + * + * skb_gso_mac_seglen is used to determine the real size of the + * individual segments, including MAC/L2, Layer3 (IP, IPv6) and L4 + * headers (TCP/UDP). + */ +static inline unsigned int skb_gso_mac_seglen(const struct sk_buff *skb) +{ + unsigned int hdr_len = skb_transport_header(skb) - skb_mac_header(skb); + return hdr_len + skb_gso_transport_seglen(skb); +} + +/** + * skb_gso_validate_mac_len - Will a split GSO skb fit in a given length? + * + * @skb: GSO skb + * @len: length to validate against + * + * skb_gso_validate_mac_len validates if a given skb will fit a wanted + * length once split, including L2, L3 and L4 headers and the payload. + */ +static inline bool +skb_gso_validate_mac_len(const struct sk_buff *skb, unsigned int len) +{ + return skb_gso_mac_seglen(skb) <= len; +} + #endif /* __KERNEL__ */ #endif /* _LINUX_SKBUFF_H */ diff --git a/include/linux/smpboot.h b/include/linux/smpboot.h index 12910cf19869..12a4b09f4d08 100644 --- a/include/linux/smpboot.h +++ b/include/linux/smpboot.h @@ -30,7 +30,7 @@ struct smpboot_thread_data; * @thread_comm: The base name of the thread */ struct smp_hotplug_thread { - struct task_struct __percpu **store; + struct task_struct * __percpu *store; struct list_head list; int (*thread_should_run)(unsigned int cpu); void (*thread_fn)(unsigned int cpu); diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 747404dbe506..085da1707cea 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -419,4 +419,7 @@ static inline void tcp_saved_syn_free(struct tcp_sock *tp) tp->saved_syn = NULL; } +int tcp_skb_shift(struct sk_buff *to, struct sk_buff *from, int pcount, + int shiftlen); + #endif /* _LINUX_TCP_H */ diff --git a/include/linux/usb.h b/include/linux/usb.h index e27df29a298e..eab41e3be816 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -127,7 +127,6 @@ enum usb_interface_condition { * @dev: driver model's view of this device * @usb_dev: if an interface is bound to the USB major, this will point * to the sysfs representation for that device. - * @pm_usage_cnt: PM usage counter for this interface * @reset_ws: Used for scheduling resets from atomic context. * @resetting_device: USB core reset the device, so use alt setting 0 as * current; needs bandwidth alloc after reset. @@ -184,7 +183,6 @@ struct usb_interface { struct device dev; /* interface specific device info */ struct device *usb_dev; - atomic_t pm_usage_cnt; /* usage counter for autosuspend */ struct work_struct reset_ws; /* for resets in atomic context */ }; #define to_usb_interface(d) container_of(d, struct usb_interface, dev) diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 2bf825f5b711..b661b6e3496b 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -778,7 +778,9 @@ static inline struct usb_gadget *dev_to_usb_gadget(struct device *dev) */ static inline size_t usb_ep_align(struct usb_ep *ep, size_t len) { - return round_up(len, (size_t)le16_to_cpu(ep->desc->wMaxPacketSize)); + int max_packet_size = (size_t)usb_endpoint_maxp(ep->desc) & 0x7ff; + + return round_up(len, max_packet_size); } /** diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h index d25125402fda..f066d65ac3dd 100644 --- a/include/linux/usb/phy.h +++ b/include/linux/usb/phy.h @@ -126,6 +126,9 @@ struct usb_phy { /* reset the PHY clocks */ int (*reset)(struct usb_phy *x); + + /* return linestate with Idp_src (used for DCD with USB2 PHY) */ + int (*dpdm_with_idp_src)(struct usb_phy *x); }; /** @@ -209,6 +212,15 @@ usb_phy_reset(struct usb_phy *x) return 0; } +static inline int +usb_phy_dpdm_with_idp_src(struct usb_phy *x) +{ + if (x && x->dpdm_with_idp_src) + return x->dpdm_with_idp_src(x); + + return 0; +} + /* for usb host and peripheral controller drivers */ #if IS_ENABLED(CONFIG_USB_PHY) extern struct usb_phy *usb_get_phy(enum usb_phy_type type); diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index 017ffb2220c7..6e61ef44be04 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -65,6 +65,8 @@ struct v4l2_ioctl_ops { int (*vidioc_g_fmt_sdr_out) (struct file *file, void *fh, struct v4l2_format *f); + int (*vidioc_g_fmt_type_private)(struct file *file, void *fh, + struct v4l2_format *f); /* VIDIOC_S_FMT handlers */ int (*vidioc_s_fmt_vid_cap) (struct file *file, void *fh, struct v4l2_format *f); diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 9cba4907695c..c796f93aaa70 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -163,6 +163,7 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr); int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr); +void __ipv6_sock_mc_close(struct sock *sk); void ipv6_sock_mc_close(struct sock *sk); bool inet6_mc_check(struct sock *sk, const struct in6_addr *mc_addr, const struct in6_addr *src_addr); diff --git a/include/net/arp.h b/include/net/arp.h index 1b3f86981757..92d2f7d7d1cb 100644 --- a/include/net/arp.h +++ b/include/net/arp.h @@ -17,6 +17,7 @@ static inline u32 arp_hashfn(const void *pkey, const struct net_device *dev, u32 return val * hash_rnd[0]; } +#ifdef CONFIG_INET static inline struct neighbour *__ipv4_neigh_lookup_noref(struct net_device *dev, u32 key) { if (dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) @@ -24,6 +25,13 @@ static inline struct neighbour *__ipv4_neigh_lookup_noref(struct net_device *dev return ___neigh_lookup_noref(&arp_tbl, neigh_key_eq32, arp_hashfn, &key, dev); } +#else +static inline +struct neighbour *__ipv4_neigh_lookup_noref(struct net_device *dev, u32 key) +{ + return NULL; +} +#endif static inline struct neighbour *__ipv4_neigh_lookup(struct net_device *dev, u32 key) { diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index c68926b4899c..61c38f87ea07 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -88,6 +88,7 @@ struct netns_ipv4 { int sysctl_tcp_fwmark_accept; int sysctl_tcp_mtu_probing; int sysctl_tcp_base_mss; + int sysctl_tcp_min_snd_mss; int sysctl_tcp_probe_threshold; u32 sysctl_tcp_probe_interval; diff --git a/include/net/tcp.h b/include/net/tcp.h index dd161b625cd7..b9b5bc27b844 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -54,6 +54,8 @@ void tcp_time_wait(struct sock *sk, int state, int timeo); #define MAX_TCP_HEADER (128 + MAX_HEADER) #define MAX_TCP_OPTION_SPACE 40 +#define TCP_MIN_SND_MSS 48 +#define TCP_MIN_GSO_SIZE (TCP_MIN_SND_MSS - MAX_TCP_OPTION_SPACE) /* * Never offer a window over 32767 without using window scaling. Some diff --git a/include/sound/q6core.h b/include/sound/q6core.h index f4e048d0aa6f..773fc45a6734 100644 --- a/include/sound/q6core.h +++ b/include/sound/q6core.h @@ -185,13 +185,17 @@ struct avcs_cmd_get_version_result { #define AVCS_CMDRSP_Q6_ID_2_6 0x00040000 #define AVCS_CMDRSP_Q6_ID_2_7 0x00040001 #define AVCS_CMDRSP_Q6_ID_2_8 0x00040002 +#define AVCS_CMDRSP_Q6_ID_2_9 0x00040003 enum q6_subsys_image { Q6_SUBSYS_AVS2_6 = 1, Q6_SUBSYS_AVS2_7, Q6_SUBSYS_AVS2_8, + Q6_SUBSYS_AVS2_9, Q6_SUBSYS_INVALID, }; + enum q6_subsys_image q6core_get_avs_version(void); + int core_get_adsp_ver(void); #endif /* __Q6CORE_H__ */ diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index fe009522f4e1..e84cbb11fe7f 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -525,6 +525,37 @@ TRACE_EVENT(f2fs_truncate_partial_nodes, __entry->err) ); +TRACE_EVENT(f2fs_file_write_iter, + + TP_PROTO(struct inode *inode, unsigned long offset, + unsigned long length, int ret), + + TP_ARGS(inode, offset, length, ret), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(unsigned long, offset) + __field(unsigned long, length) + __field(int, ret) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->offset = offset; + __entry->length = length; + __entry->ret = ret; + ), + + TP_printk("dev = (%d,%d), ino = %lu, " + "offset = %lu, length = %lu, written(err) = %d", + show_dev_ino(__entry), + __entry->offset, + __entry->length, + __entry->ret) +); + TRACE_EVENT(f2fs_map_blocks, TP_PROTO(struct inode *inode, struct f2fs_map_blocks *map, int ret), @@ -1241,6 +1272,32 @@ DEFINE_EVENT(f2fs__page, f2fs_commit_inmem_page, TP_ARGS(page, type) ); +TRACE_EVENT(f2fs_filemap_fault, + + TP_PROTO(struct inode *inode, pgoff_t index, unsigned long ret), + + TP_ARGS(inode, index, ret), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(pgoff_t, index) + __field(unsigned long, ret) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->index = index; + __entry->ret = ret; + ), + + TP_printk("dev = (%d,%d), ino = %lu, index = %lu, ret = %lx", + show_dev_ino(__entry), + (unsigned long)__entry->index, + __entry->ret) +); + TRACE_EVENT(f2fs_writepages, TP_PROTO(struct inode *inode, struct writeback_control *wbc, int type), diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index 8a702e7b3f71..5846f050e90f 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -205,10 +205,12 @@ struct fuse_file_lock { * FOPEN_DIRECT_IO: bypass page cache for this open file * FOPEN_KEEP_CACHE: don't invalidate the data cache on open * FOPEN_NONSEEKABLE: the file is not seekable + * FOPEN_STREAM: the file is stream-like (no file position at all) */ #define FOPEN_DIRECT_IO (1 << 0) #define FOPEN_KEEP_CACHE (1 << 1) #define FOPEN_NONSEEKABLE (1 << 2) +#define FOPEN_STREAM (1 << 4) /** * INIT request/reply flags diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h index f0e9f0460ee7..4d05aad2e4c5 100644 --- a/include/uapi/linux/prctl.h +++ b/include/uapi/linux/prctl.h @@ -211,6 +211,7 @@ struct prctl_mm_map { #define PR_SET_SPECULATION_CTRL 53 /* Speculation control variants */ # define PR_SPEC_STORE_BYPASS 0 +# define PR_SPEC_INDIRECT_BRANCH 1 /* Return and control values for PR_SET/GET_SPECULATION_CTRL */ # define PR_SPEC_NOT_AFFECTED 0 # define PR_SPEC_PRCTL (1UL << 0) diff --git a/include/uapi/linux/snmp.h b/include/uapi/linux/snmp.h index 9de808ebce05..422183f396d5 100644 --- a/include/uapi/linux/snmp.h +++ b/include/uapi/linux/snmp.h @@ -281,6 +281,7 @@ enum LINUX_MIB_TCPKEEPALIVE, /* TCPKeepAlive */ LINUX_MIB_TCPMTUPFAIL, /* TCPMTUPFail */ LINUX_MIB_TCPMTUPSUCCESS, /* TCPMTUPSuccess */ + LINUX_MIB_TCPWQUEUETOOBIG, /* TCPWqueueTooBig */ __LINUX_MIB_MAX }; diff --git a/include/uapi/linux/tipc_config.h b/include/uapi/linux/tipc_config.h index 087b0ef82c07..bbebd258cf07 100644 --- a/include/uapi/linux/tipc_config.h +++ b/include/uapi/linux/tipc_config.h @@ -301,8 +301,10 @@ static inline int TLV_SET(void *tlv, __u16 type, void *data, __u16 len) tlv_ptr = (struct tlv_desc *)tlv; tlv_ptr->tlv_type = htons(type); tlv_ptr->tlv_len = htons(tlv_len); - if (len && data) - memcpy(TLV_DATA(tlv_ptr), data, tlv_len); + if (len && data) { + memcpy(TLV_DATA(tlv_ptr), data, len); + memset(TLV_DATA(tlv_ptr) + len, 0, TLV_SPACE(len) - tlv_len); + } return TLV_SPACE(len); } @@ -399,8 +401,10 @@ static inline int TCM_SET(void *msg, __u16 cmd, __u16 flags, tcm_hdr->tcm_len = htonl(msg_len); tcm_hdr->tcm_type = htons(cmd); tcm_hdr->tcm_flags = htons(flags); - if (data_len && data) + if (data_len && data) { memcpy(TCM_DATA(msg), data, data_len); + memset(TCM_DATA(msg) + data_len, 0, TCM_SPACE(data_len) - msg_len); + } return TCM_SPACE(data_len); } diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h index 3228d582234a..4914c3037a03 100644 --- a/include/uapi/linux/virtio_ids.h +++ b/include/uapi/linux/virtio_ids.h @@ -43,4 +43,6 @@ #define VIRTIO_ID_INPUT 18 /* virtio input */ #define VIRTIO_ID_VSOCK 19 /* virtio vsock transport */ +#define VIRTIO_ID_I2C 32 /* virtio i2c */ + #endif /* _LINUX_VIRTIO_IDS_H */ diff --git a/init/main.c b/init/main.c index 84d4bceb1db1..33cd39aebf46 100644 --- a/init/main.c +++ b/init/main.c @@ -534,6 +534,8 @@ asmlinkage __visible void __init start_kernel(void) page_alloc_init(); pr_notice("Kernel command line: %s\n", boot_command_line); + /* parameters may set static keys */ + jump_label_init(); parse_early_param(); after_dashes = parse_args("Booting kernel", static_command_line, __start___param, @@ -543,8 +545,6 @@ asmlinkage __visible void __init start_kernel(void) parse_args("Setting init args", after_dashes, NULL, 0, -1, -1, NULL, set_init_arg); - jump_label_init(); - /* * These use large bootmem allocations and must precede * kmem_cache_init() diff --git a/ipc/mqueue.c b/ipc/mqueue.c index 46436543ad28..598b8bf8d80c 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -373,7 +373,8 @@ static void mqueue_evict_inode(struct inode *inode) struct user_struct *user; unsigned long mq_bytes, mq_treesize; struct ipc_namespace *ipc_ns; - struct msg_msg *msg; + struct msg_msg *msg, *nmsg; + LIST_HEAD(tmp_msg); clear_inode(inode); @@ -384,10 +385,15 @@ static void mqueue_evict_inode(struct inode *inode) info = MQUEUE_I(inode); spin_lock(&info->lock); while ((msg = msg_get(info)) != NULL) - free_msg(msg); + list_add_tail(&msg->m_list, &tmp_msg); kfree(info->node_cache); spin_unlock(&info->lock); + list_for_each_entry_safe(msg, nmsg, &tmp_msg, m_list) { + list_del(&msg->m_list); + free_msg(msg); + } + /* Total amount of bytes accounted for the mqueue */ mq_treesize = info->attr.mq_maxmsg * sizeof(struct msg_msg) + min_t(unsigned int, info->attr.mq_maxmsg, MQ_PRIO_MAX) * diff --git a/ipc/msgutil.c b/ipc/msgutil.c index ed81aafd2392..9467307487f7 100644 --- a/ipc/msgutil.c +++ b/ipc/msgutil.c @@ -18,6 +18,7 @@ #include <linux/utsname.h> #include <linux/proc_ns.h> #include <linux/uaccess.h> +#include <linux/sched.h> #include "util.h" @@ -66,6 +67,9 @@ static struct msg_msg *alloc_msg(size_t len) pseg = &msg->next; while (len > 0) { struct msg_msgseg *seg; + + cond_resched(); + alen = min(len, DATALEN_SEG); seg = kmalloc(sizeof(*seg) + alen, GFP_KERNEL); if (seg == NULL) @@ -178,6 +182,8 @@ void free_msg(struct msg_msg *msg) kfree(msg); while (seg != NULL) { struct msg_msgseg *tmp = seg->next; + + cond_resched(); kfree(seg); seg = tmp; } diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index b57f929f1b46..cf7aa656b308 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -1095,22 +1095,24 @@ int audit_rule_change(int type, __u32 portid, int seq, void *data, int err = 0; struct audit_entry *entry; - entry = audit_data_to_entry(data, datasz); - if (IS_ERR(entry)) - return PTR_ERR(entry); - switch (type) { case AUDIT_ADD_RULE: + entry = audit_data_to_entry(data, datasz); + if (IS_ERR(entry)) + return PTR_ERR(entry); err = audit_add_rule(entry); audit_log_rule_change("add_rule", &entry->rule, !err); break; case AUDIT_DEL_RULE: + entry = audit_data_to_entry(data, datasz); + if (IS_ERR(entry)) + return PTR_ERR(entry); err = audit_del_rule(entry); audit_log_rule_change("remove_rule", &entry->rule, !err); break; default: - err = -EINVAL; WARN_ON(1); + return -EINVAL; } if (err || type == AUDIT_DEL_RULE) { diff --git a/kernel/cpu.c b/kernel/cpu.c index 5b4440d57f89..d484f2869861 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -8,6 +8,7 @@ #include <linux/init.h> #include <linux/notifier.h> #include <linux/sched.h> +#include <linux/sched/smt.h> #include <linux/unistd.h> #include <linux/cpu.h> #include <linux/oom.h> @@ -206,6 +207,12 @@ void cpu_hotplug_enable(void) EXPORT_SYMBOL_GPL(cpu_hotplug_enable); #endif /* CONFIG_HOTPLUG_CPU */ +/* + * Architectures that need SMT-specific errata handling during SMT hotplug + * should override this. + */ +void __weak arch_smt_update(void) { } + /* Need to know about CPUs going up/down? */ int register_cpu_notifier(struct notifier_block *nb) { @@ -445,6 +452,7 @@ out_release: trace_sched_cpu_hotplug(cpu, err, 0); if (!err) cpu_notify_nofail(CPU_POST_DEAD | mod, hcpu); + arch_smt_update(); return err; } @@ -549,7 +557,7 @@ out_notify: out: cpu_hotplug_done(); trace_sched_cpu_hotplug(cpu, ret, 1); - + arch_smt_update(); return ret; } @@ -911,6 +919,19 @@ void init_cpu_isolated(const struct cpumask *src) cpumask_copy(to_cpumask(cpu_isolated_bits), src); } +enum cpu_mitigations cpu_mitigations = CPU_MITIGATIONS_AUTO; + +static int __init mitigations_parse_cmdline(char *arg) +{ + if (!strcmp(arg, "off")) + cpu_mitigations = CPU_MITIGATIONS_OFF; + else if (!strcmp(arg, "auto")) + cpu_mitigations = CPU_MITIGATIONS_AUTO; + + return 0; +} +early_param("mitigations", mitigations_parse_cmdline); + static ATOMIC_NOTIFIER_HEAD(idle_notifier); void idle_notifier_register(struct notifier_block *n) diff --git a/kernel/cpuset.c b/kernel/cpuset.c index a599351997ad..2ce1a5297b92 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -419,14 +419,19 @@ static struct cpuset *alloc_trial_cpuset(struct cpuset *cs) if (!alloc_cpumask_var(&trial->cpus_allowed, GFP_KERNEL)) goto free_cs; + if (!alloc_cpumask_var(&trial->cpus_requested, GFP_KERNEL)) + goto free_allowed; if (!alloc_cpumask_var(&trial->effective_cpus, GFP_KERNEL)) goto free_cpus; cpumask_copy(trial->cpus_allowed, cs->cpus_allowed); + cpumask_copy(trial->cpus_requested, cs->cpus_requested); cpumask_copy(trial->effective_cpus, cs->effective_cpus); return trial; free_cpus: + free_cpumask_var(trial->cpus_requested); +free_allowed: free_cpumask_var(trial->cpus_allowed); free_cs: kfree(trial); @@ -440,6 +445,7 @@ free_cs: static void free_trial_cpuset(struct cpuset *trial) { free_cpumask_var(trial->effective_cpus); + free_cpumask_var(trial->cpus_requested); free_cpumask_var(trial->cpus_allowed); kfree(trial); } @@ -948,23 +954,23 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs, return -EACCES; /* - * An empty cpus_allowed is ok only if the cpuset has no tasks. + * An empty cpus_requested is ok only if the cpuset has no tasks. * Since cpulist_parse() fails on an empty mask, we special case * that parsing. The validate_change() call ensures that cpusets * with tasks have cpus. */ if (!*buf) { - cpumask_clear(trialcs->cpus_allowed); + cpumask_clear(trialcs->cpus_requested); } else { retval = cpulist_parse(buf, trialcs->cpus_requested); if (retval < 0) return retval; + } - if (!cpumask_subset(trialcs->cpus_requested, cpu_present_mask)) - return -EINVAL; + if (!cpumask_subset(trialcs->cpus_requested, cpu_present_mask)) + return -EINVAL; - cpumask_and(trialcs->cpus_allowed, trialcs->cpus_requested, cpu_active_mask); - } + cpumask_and(trialcs->cpus_allowed, trialcs->cpus_requested, cpu_active_mask); /* Nothing to do if the cpus didn't change */ if (cpumask_equal(cs->cpus_requested, trialcs->cpus_requested)) diff --git a/kernel/cred.c b/kernel/cred.c index ff8606f77d90..098af0bc0b7e 100644 --- a/kernel/cred.c +++ b/kernel/cred.c @@ -447,6 +447,15 @@ int commit_creds(struct cred *new) if (task->mm) set_dumpable(task->mm, suid_dumpable); task->pdeath_signal = 0; + /* + * If a task drops privileges and becomes nondumpable, + * the dumpability change must become visible before + * the credential change; otherwise, a __ptrace_may_access() + * racing with this change may be able to attach to a task it + * shouldn't be able to attach to (as if the task had dropped + * privileges without becoming nondumpable). + * Pairs with a read barrier in __ptrace_may_access(). + */ smp_wmb(); } diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index 424f5a5fa5a2..b128cc829bd8 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c @@ -49,14 +49,30 @@ static void perf_output_put_handle(struct perf_output_handle *handle) unsigned long head; again: + /* + * In order to avoid publishing a head value that goes backwards, + * we must ensure the load of @rb->head happens after we've + * incremented @rb->nest. + * + * Otherwise we can observe a @rb->head value before one published + * by an IRQ/NMI happening between the load and the increment. + */ + barrier(); head = local_read(&rb->head); /* - * IRQ/NMI can happen here, which means we can miss a head update. + * IRQ/NMI can happen here and advance @rb->head, causing our + * load above to be stale. */ - if (!local_dec_and_test(&rb->nest)) + /* + * If this isn't the outermost nesting, we don't have to update + * @rb->user_page->data_head. + */ + if (local_read(&rb->nest) > 1) { + local_dec(&rb->nest); goto out; + } /* * Since the mmap() consumer (userspace) can run on a different CPU: @@ -88,9 +104,18 @@ again: rb->user_page->data_head = head; /* - * Now check if we missed an update -- rely on previous implied - * compiler barriers to force a re-read. + * We must publish the head before decrementing the nest count, + * otherwise an IRQ/NMI can publish a more recent head value and our + * write will (temporarily) publish a stale value. + */ + barrier(); + local_set(&rb->nest, 0); + + /* + * Ensure we decrement @rb->nest before we validate the @rb->head. + * Otherwise we cannot be sure we caught the 'last' nested update. */ + barrier(); if (unlikely(head != local_read(&rb->head))) { local_inc(&rb->nest); goto again; diff --git a/kernel/fork.c b/kernel/fork.c index 25d0a60d166c..e8bc7ed77b59 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1769,7 +1769,7 @@ struct task_struct *fork_idle(int cpu) cpu_to_node(cpu)); if (!IS_ERR(task)) { init_idle_pids(task->pids); - init_idle(task, cpu, false); + init_idle(task, cpu); } return task; diff --git a/kernel/futex.c b/kernel/futex.c index 39c2b3eda1b9..d24d164e9bdb 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -593,8 +593,8 @@ again: * applies. If this is really a shmem page then the page lock * will prevent unexpected transitions. */ - lock_page(page); - shmem_swizzled = PageSwapCache(page) || page->mapping; + lock_page(page_head); + shmem_swizzled = PageSwapCache(page_head) || page_head->mapping; unlock_page(page_head); put_page(page_head); diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 36a13b75b48f..d0193c0b2531 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -334,11 +334,10 @@ irq_set_affinity_notifier(unsigned int irq, struct irq_affinity_notify *notify) desc->affinity_notify = notify; raw_spin_unlock_irqrestore(&desc->lock, flags); - if (!notify && old_notify) + if (old_notify) { cancel_work_sync(&old_notify->work); - - if (old_notify) kref_put(&old_notify->kref, old_notify->release); + } return 0; } diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 5e2cd1030702..1aa33fe37aa8 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -228,6 +228,9 @@ static int ptrace_check_attach(struct task_struct *child, bool ignore_state) static int ptrace_has_cap(struct user_namespace *ns, unsigned int mode) { + if (mode & PTRACE_MODE_SCHED) + return false; + if (mode & PTRACE_MODE_NOAUDIT) return has_ns_capability_noaudit(current, ns, CAP_SYS_PTRACE); else @@ -289,15 +292,32 @@ static int __ptrace_may_access(struct task_struct *task, unsigned int mode) return -EPERM; ok: rcu_read_unlock(); + /* + * If a task drops privileges and becomes nondumpable (through a syscall + * like setresuid()) while we are trying to access it, we must ensure + * that the dumpability is read after the credentials; otherwise, + * we may be able to attach to a task that we shouldn't be able to + * attach to (as if the task had dropped privileges without becoming + * nondumpable). + * Pairs with a write barrier in commit_creds(). + */ + smp_rmb(); mm = task->mm; if (mm && ((get_dumpable(mm) != SUID_DUMP_USER) && !ptrace_has_cap(mm->user_ns, mode))) return -EPERM; + if (mode & PTRACE_MODE_SCHED) + return 0; return security_ptrace_access_check(task, mode); } +bool ptrace_may_access_sched(struct task_struct *task, unsigned int mode) +{ + return __ptrace_may_access(task, mode | PTRACE_MODE_SCHED); +} + bool ptrace_may_access(struct task_struct *task, unsigned int mode) { int err; @@ -663,6 +683,10 @@ static int ptrace_peek_siginfo(struct task_struct *child, if (arg.nr < 0) return -EINVAL; + /* Ensure arg.off fits in an unsigned long */ + if (arg.off > ULONG_MAX) + return 0; + if (arg.flags & PTRACE_PEEKSIGINFO_SHARED) pending = &child->signal->shared_pending; else @@ -670,18 +694,20 @@ static int ptrace_peek_siginfo(struct task_struct *child, for (i = 0; i < arg.nr; ) { siginfo_t info; - s32 off = arg.off + i; + unsigned long off = arg.off + i; + bool found = false; spin_lock_irq(&child->sighand->siglock); list_for_each_entry(q, &pending->list, list) { if (!off--) { + found = true; copy_siginfo(&info, &q->info); break; } } spin_unlock_irq(&child->sighand->siglock); - if (off >= 0) /* beyond the end of the list */ + if (!found) /* beyond the end of the list */ break; #ifdef CONFIG_COMPAT diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index d89328e260df..041a02b334d7 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c @@ -1603,6 +1603,10 @@ rcu_torture_cleanup(void) cur_ops->cb_barrier(); return; } + if (!cur_ops) { + torture_cleanup_end(); + return; + } rcu_torture_barrier_cleanup(); torture_stop_kthread(rcu_torture_stall, stall_task); @@ -1741,6 +1745,7 @@ rcu_torture_init(void) pr_alert(" %s", torture_ops[i]->name); pr_alert("\n"); firsterr = -EINVAL; + cur_ops = NULL; goto unwind; } if (cur_ops->fqs == NULL && fqs_duration != 0) { diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 6b9021b9bc26..543f7113b1d2 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2447,7 +2447,7 @@ int sched_fork(unsigned long clone_flags, struct task_struct *p) unsigned long flags; int cpu; - init_new_task_load(p, false); + init_new_task_load(p); cpu = get_cpu(); __sched_fork(clone_flags, p); @@ -5407,19 +5407,15 @@ void init_idle_bootup_task(struct task_struct *idle) * init_idle - set up an idle thread for a given CPU * @idle: task in question * @cpu: cpu the idle task belongs to - * @cpu_up: differentiate between initial boot vs hotplug * * NOTE: this function does not set the idle thread's NEED_RESCHED * flag, to make booting more robust. */ -void init_idle(struct task_struct *idle, int cpu, bool cpu_up) +void init_idle(struct task_struct *idle, int cpu) { struct rq *rq = cpu_rq(cpu); unsigned long flags; - if (!cpu_up) - init_new_task_load(idle, true); - raw_spin_lock_irqsave(&idle->pi_lock, flags); raw_spin_lock(&rq->lock); @@ -6373,6 +6369,10 @@ static void set_cpu_rq_start_time(void) rq->age_stamp = sched_clock_cpu(cpu); } +#ifdef CONFIG_SCHED_SMT +atomic_t sched_smt_present = ATOMIC_INIT(0); +#endif + static int sched_cpu_active(struct notifier_block *nfb, unsigned long action, void *hcpu) { @@ -6389,11 +6389,23 @@ static int sched_cpu_active(struct notifier_block *nfb, * set_cpu_online(). But it might not yet have marked itself * as active, which is essential from here on. */ +#ifdef CONFIG_SCHED_SMT + /* + * When going up, increment the number of cores with SMT present. + */ + if (cpumask_weight(cpu_smt_mask(cpu)) == 2) + atomic_inc(&sched_smt_present); +#endif set_cpu_active(cpu, true); stop_machine_unpark(cpu); return NOTIFY_OK; case CPU_DOWN_FAILED: +#ifdef CONFIG_SCHED_SMT + /* Same as for CPU_ONLINE */ + if (cpumask_weight(cpu_smt_mask(cpu)) == 2) + atomic_inc(&sched_smt_present); +#endif set_cpu_active(cpu, true); return NOTIFY_OK; @@ -6408,7 +6420,15 @@ static int sched_cpu_inactive(struct notifier_block *nfb, switch (action & ~CPU_TASKS_FROZEN) { case CPU_DOWN_PREPARE: set_cpu_active((long)hcpu, false); +#ifdef CONFIG_SCHED_SMT + /* + * When going down, decrement the number of cores with SMT present. + */ + if (cpumask_weight(cpu_smt_mask((long)hcpu)) == 2) + atomic_dec(&sched_smt_present); +#endif return NOTIFY_OK; + default: return NOTIFY_DONE; } @@ -8547,7 +8567,8 @@ void __init sched_init(void) * but because we are the idle thread, we just pick up running again * when this runqueue becomes "idle". */ - init_idle(current, smp_processor_id(), false); + init_idle(current, smp_processor_id()); + init_new_task_load(current); calc_load_update = jiffies + LOAD_FREQ; @@ -9356,6 +9377,8 @@ static void cpu_cgroup_attach(struct cgroup_taskset *tset) static int cpu_shares_write_u64(struct cgroup_subsys_state *css, struct cftype *cftype, u64 shareval) { + if (shareval > scale_load_down(ULONG_MAX)) + shareval = MAX_SHARES; return sched_group_set_shares(css_tg(css), scale_load(shareval)); } @@ -9455,8 +9478,10 @@ int tg_set_cfs_quota(struct task_group *tg, long cfs_quota_us) period = ktime_to_ns(tg->cfs_bandwidth.period); if (cfs_quota_us < 0) quota = RUNTIME_INF; - else + else if ((u64)cfs_quota_us <= U64_MAX / NSEC_PER_USEC) quota = (u64)cfs_quota_us * NSEC_PER_USEC; + else + return -EINVAL; return tg_set_cfs_bandwidth(tg, period, quota); } @@ -9478,6 +9503,9 @@ int tg_set_cfs_period(struct task_group *tg, long cfs_period_us) { u64 quota, period; + if ((u64)cfs_period_us > U64_MAX / NSEC_PER_USEC) + return -EINVAL; + period = (u64)cfs_period_us * NSEC_PER_USEC; quota = tg->cfs_bandwidth.quota; diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index c759c6a3ea20..1eac0642fa86 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1901,6 +1901,10 @@ static u64 numa_get_avg_runtime(struct task_struct *p, u64 *period) if (p->last_task_numa_placement) { delta = runtime - p->last_sum_exec_runtime; *period = now - p->last_task_numa_placement; + + /* Avoid time going backwards, prevent potential divide error: */ + if (unlikely((s64)*period < 0)) + *period = 0; } else { delta = p->se.avg.load_sum / p->se.load.weight; *period = LOAD_AVG_MAX; diff --git a/kernel/sched/hmp.c b/kernel/sched/hmp.c index ddcf7cfb7248..5337ac7fcba1 100644 --- a/kernel/sched/hmp.c +++ b/kernel/sched/hmp.c @@ -1544,7 +1544,7 @@ void free_task_load_ptrs(struct task_struct *p) p->ravg.prev_window_cpu = NULL; } -void init_new_task_load(struct task_struct *p, bool idle_task) +void init_new_task_load(struct task_struct *p) { int i; u32 init_load_windows = sched_init_task_load_windows; @@ -1571,9 +1571,6 @@ void init_new_task_load(struct task_struct *p, bool idle_task) /* Don't have much choice. CPU frequency would be bogus */ BUG_ON(!p->ravg.curr_window_cpu || !p->ravg.prev_window_cpu); - if (idle_task) - return; - if (init_load_pct) init_load_windows = div64_u64((u64)init_load_pct * (u64)sched_ravg_window, 100); diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 8cb8b1c555c8..ffae8d49d988 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -2,6 +2,7 @@ #include <linux/sched.h> #include <linux/sched/sysctl.h> #include <linux/sched/rt.h> +#include <linux/sched/smt.h> #include <linux/sched/deadline.h> #include <linux/mutex.h> #include <linux/spinlock.h> @@ -1153,7 +1154,7 @@ extern unsigned int __read_mostly sched_downmigrate; extern unsigned int __read_mostly sysctl_sched_spill_nr_run; extern unsigned int __read_mostly sched_load_granule; -extern void init_new_task_load(struct task_struct *p, bool idle_task); +extern void init_new_task_load(struct task_struct *p); extern u64 sched_ktime_clock(void); extern int got_boost_kick(void); extern int register_cpu_cycle_counter_cb(struct cpu_cycle_counter_cb *cb); @@ -1643,7 +1644,7 @@ static inline struct sched_cluster *rq_cluster(struct rq *rq) return NULL; } -static inline void init_new_task_load(struct task_struct *p, bool idle_task) +static inline void init_new_task_load(struct task_struct *p) { } diff --git a/kernel/signal.c b/kernel/signal.c index 96e8c3cbfa38..072fd152ab01 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -2244,6 +2244,8 @@ relock: if (signal_group_exit(signal)) { ksig->info.si_signo = signr = SIGKILL; sigdelset(¤t->pending.signal, SIGKILL); + trace_signal_deliver(SIGKILL, SEND_SIG_NOINFO, + &sighand->action[SIGKILL - 1]); recalc_sigpending(); goto fatal; } diff --git a/kernel/smpboot.c b/kernel/smpboot.c index 552e154fc77e..979248f5e21a 100644 --- a/kernel/smpboot.c +++ b/kernel/smpboot.c @@ -32,7 +32,7 @@ struct task_struct *idle_thread_get(unsigned int cpu) if (!tsk) return ERR_PTR(-ENOMEM); - init_idle(tsk, cpu, true); + init_idle(tsk, cpu); return tsk; } diff --git a/kernel/sys.c b/kernel/sys.c index ede0c1f4b860..29413e2bee41 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -1764,7 +1764,7 @@ static int validate_prctl_map(struct prctl_mm_map *prctl_map) ((unsigned long)prctl_map->__m1 __op \ (unsigned long)prctl_map->__m2) ? 0 : -EINVAL error = __prctl_check_order(start_code, <, end_code); - error |= __prctl_check_order(start_data, <, end_data); + error |= __prctl_check_order(start_data,<=, end_data); error |= __prctl_check_order(start_brk, <=, brk); error |= __prctl_check_order(arg_start, <=, arg_end); error |= __prctl_check_order(env_start, <=, env_end); diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 43a049f9264c..b25717eb3d3a 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -2782,8 +2782,10 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int if (neg) continue; val = convmul * val / convdiv; - if ((min && val < *min) || (max && val > *max)) - continue; + if ((min && val < *min) || (max && val > *max)) { + err = -EINVAL; + break; + } *i = val; } else { val = convdiv * (*i) / convmul; diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c index ab861771e37f..0e0dc5d89911 100644 --- a/kernel/time/ntp.c +++ b/kernel/time/ntp.c @@ -633,7 +633,7 @@ static inline void process_adjtimex_modes(struct timex *txc, time_constant = max(time_constant, 0l); } - if (txc->modes & ADJ_TAI && txc->constant > 0) + if (txc->modes & ADJ_TAI && txc->constant >= 0) *time_tai = txc->constant; if (txc->modes & ADJ_OFFSET) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 5e091614fe39..1cf2402c6922 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -701,7 +701,7 @@ u64 ring_buffer_time_stamp(struct ring_buffer *buffer, int cpu) preempt_disable_notrace(); time = rb_time_stamp(buffer); - preempt_enable_no_resched_notrace(); + preempt_enable_notrace(); return time; } diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 1235f9fd9fbd..d0451cfd9daa 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -1289,9 +1289,6 @@ event_id_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) char buf[32]; int len; - if (*ppos) - return 0; - if (unlikely(!id)) return -ENODEV; diff --git a/lib/strncpy_from_user.c b/lib/strncpy_from_user.c index b4aacfc0b4b3..9c7b353aacb7 100644 --- a/lib/strncpy_from_user.c +++ b/lib/strncpy_from_user.c @@ -23,10 +23,11 @@ * hit it), 'max' is the address space maximum (and we return * -EFAULT if we hit it). */ -static inline long do_strncpy_from_user(char *dst, const char __user *src, long count, unsigned long max) +static inline long do_strncpy_from_user(char *dst, const char __user *src, + unsigned long count, unsigned long max) { const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS; - long res = 0; + unsigned long res = 0; /* * Truncate 'max' to the user-specified limit, so that diff --git a/lib/strnlen_user.c b/lib/strnlen_user.c index 8e105ed4df12..9ff4f3bbb1aa 100644 --- a/lib/strnlen_user.c +++ b/lib/strnlen_user.c @@ -27,7 +27,7 @@ static inline long do_strnlen_user(const char __user *src, unsigned long count, unsigned long max) { const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS; - long align, res = 0; + unsigned long align, res = 0; unsigned long c; /* @@ -41,7 +41,7 @@ static inline long do_strnlen_user(const char __user *src, unsigned long count, * Do everything aligned. But that means that we * need to also expand the maximum.. */ - align = (sizeof(long) - 1) & (unsigned long)src; + align = (sizeof(unsigned long) - 1) & (unsigned long)src; src -= align; max += align; diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 3081f1234d4e..b12a49bf78de 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -670,6 +670,7 @@ static int cgwb_bdi_init(struct backing_dev_info *bdi) INIT_RADIX_TREE(&bdi->cgwb_tree, GFP_ATOMIC); bdi->cgwb_congested_tree = RB_ROOT; atomic_set(&bdi->usage_cnt, 1); + init_rwsem(&bdi->wb_switch_rwsem); ret = wb_init(&bdi->wb, bdi, 1, GFP_KERNEL); if (!ret) { @@ -101,8 +101,10 @@ static int __init cma_activate_area(struct cma *cma) cma->bitmap = kzalloc(bitmap_size, GFP_KERNEL); - if (!cma->bitmap) + if (!cma->bitmap) { + cma->count = 0; return -ENOMEM; + } WARN_ON_ONCE(!pfn_valid(pfn)); zone = page_zone(pfn_to_page(pfn)); diff --git a/mm/cma_debug.c b/mm/cma_debug.c index f8e4b60db167..da50dab56b70 100644 --- a/mm/cma_debug.c +++ b/mm/cma_debug.c @@ -57,7 +57,7 @@ static int cma_maxchunk_get(void *data, u64 *val) mutex_lock(&cma->lock); for (;;) { start = find_next_zero_bit(cma->bitmap, bitmap_maxno, end); - if (start >= cma->count) + if (start >= bitmap_maxno) break; end = find_next_bit(cma->bitmap, bitmap_maxno, start); maxchunk = max(end - start, maxchunk); diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 324b2953e57e..fd932e7a25dd 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -1221,12 +1221,23 @@ void free_huge_page(struct page *page) ClearPagePrivate(page); /* - * A return code of zero implies that the subpool will be under its - * minimum size if the reservation is not restored after page is free. - * Therefore, force restore_reserve operation. + * If PagePrivate() was set on page, page allocation consumed a + * reservation. If the page was associated with a subpool, there + * would have been a page reserved in the subpool before allocation + * via hugepage_subpool_get_pages(). Since we are 'restoring' the + * reservtion, do not call hugepage_subpool_put_pages() as this will + * remove the reserved page from the subpool. */ - if (hugepage_subpool_put_pages(spool, 1) == 0) - restore_reserve = true; + if (!restore_reserve) { + /* + * A return code of zero implies that the subpool will be + * under its minimum size if the reservation is not restored + * after page is free. Therefore, force restore_reserve + * operation. + */ + if (hugepage_subpool_put_pages(spool, 1) == 0) + restore_reserve = true; + } spin_lock(&hugetlb_lock); clear_page_huge_active(page); @@ -3703,21 +3714,14 @@ backout_unlocked: } #ifdef CONFIG_SMP -u32 hugetlb_fault_mutex_hash(struct hstate *h, struct mm_struct *mm, - struct vm_area_struct *vma, - struct address_space *mapping, +u32 hugetlb_fault_mutex_hash(struct hstate *h, struct address_space *mapping, pgoff_t idx, unsigned long address) { unsigned long key[2]; u32 hash; - if (vma->vm_flags & VM_SHARED) { - key[0] = (unsigned long) mapping; - key[1] = idx; - } else { - key[0] = (unsigned long) mm; - key[1] = address >> huge_page_shift(h); - } + key[0] = (unsigned long) mapping; + key[1] = idx; hash = jhash2((u32 *)&key, sizeof(key)/sizeof(u32), 0); @@ -3728,9 +3732,7 @@ u32 hugetlb_fault_mutex_hash(struct hstate *h, struct mm_struct *mm, * For uniprocesor systems we always use a single mutex, so just * return 0 and avoid the hashing overhead. */ -u32 hugetlb_fault_mutex_hash(struct hstate *h, struct mm_struct *mm, - struct vm_area_struct *vma, - struct address_space *mapping, +u32 hugetlb_fault_mutex_hash(struct hstate *h, struct address_space *mapping, pgoff_t idx, unsigned long address) { return 0; @@ -3776,7 +3778,7 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma, * get spurious allocation failures if two CPUs race to instantiate * the same page in the page cache. */ - hash = hugetlb_fault_mutex_hash(h, mm, vma, mapping, idx, address); + hash = hugetlb_fault_mutex_hash(h, mapping, idx, address); mutex_lock(&hugetlb_fault_mutex_table[hash]); entry = huge_ptep_get(ptep); diff --git a/mm/list_lru.c b/mm/list_lru.c index 786176b1a0ee..4aa714db2fcf 100644 --- a/mm/list_lru.c +++ b/mm/list_lru.c @@ -42,11 +42,7 @@ static void list_lru_unregister(struct list_lru *lru) #ifdef CONFIG_MEMCG_KMEM static inline bool list_lru_memcg_aware(struct list_lru *lru) { - /* - * This needs node 0 to be always present, even - * in the systems supporting sparse numa ids. - */ - return !!lru->node[0].memcg_lrus; + return lru->memcg_aware; } static inline struct list_lru_one * @@ -317,7 +313,7 @@ static int __memcg_init_list_lru_node(struct list_lru_memcg *memcg_lrus, } return 0; fail: - __memcg_destroy_list_lru_node(memcg_lrus, begin, i - 1); + __memcg_destroy_list_lru_node(memcg_lrus, begin, i); return -ENOMEM; } @@ -389,6 +385,8 @@ static int memcg_init_list_lru(struct list_lru *lru, bool memcg_aware) { int i; + lru->memcg_aware = memcg_aware; + if (!memcg_aware) return 0; diff --git a/mm/mincore.c b/mm/mincore.c index 14bb9fb37f0c..9700c2303941 100644 --- a/mm/mincore.c +++ b/mm/mincore.c @@ -165,6 +165,22 @@ out: return 0; } +static inline bool can_do_mincore(struct vm_area_struct *vma) +{ + if (vma_is_anonymous(vma)) + return true; + if (!vma->vm_file) + return false; + /* + * Reveal pagecache information only for non-anonymous mappings that + * correspond to the files the calling process could (if tried) open + * for writing; otherwise we'd be including shared non-exclusive + * mappings, which opens a side channel. + */ + return inode_owner_or_capable(file_inode(vma->vm_file)) || + inode_permission(file_inode(vma->vm_file), MAY_WRITE) == 0; +} + /* * Do a chunk of "sys_mincore()". We've already checked * all the arguments, we hold the mmap semaphore: we should @@ -185,8 +201,13 @@ static long do_mincore(unsigned long addr, unsigned long pages, unsigned char *v vma = find_vma(current->mm, addr); if (!vma || addr < vma->vm_start) return -ENOMEM; - mincore_walk.mm = vma->vm_mm; end = min(vma->vm_end, addr + (pages << PAGE_SHIFT)); + if (!can_do_mincore(vma)) { + unsigned long pages = DIV_ROUND_UP(end - addr, PAGE_SIZE); + memset(vec, 1, pages); + return pages; + } + mincore_walk.mm = vma->vm_mm; err = walk_page_range(addr, end, &mincore_walk); if (err < 0) return err; diff --git a/mm/mmap.c b/mm/mmap.c index ae4f0fc2f01b..3bb666ced2b8 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -42,6 +42,7 @@ #include <linux/memory.h> #include <linux/printk.h> #include <linux/userfaultfd_k.h> +#include <linux/mm.h> #include <asm/uaccess.h> #include <asm/cacheflush.h> @@ -2435,7 +2436,8 @@ find_extend_vma(struct mm_struct *mm, unsigned long addr) vma = find_vma_prev(mm, addr, &prev); if (vma && (vma->vm_start <= addr)) return vma; - if (!prev || expand_stack(prev, addr)) + /* don't alter vm_end if the coredump is running */ + if (!prev || !mmget_still_valid(mm) || expand_stack(prev, addr)) return NULL; if (prev->vm_flags & VM_LOCKED) populate_vma_page_range(prev, addr, prev->vm_end, NULL); @@ -2461,6 +2463,9 @@ find_extend_vma(struct mm_struct *mm, unsigned long addr) return vma; if (!(vma->vm_flags & VM_GROWSDOWN)) return NULL; + /* don't alter vm_start if the coredump is running */ + if (!mmget_still_valid(mm)) + return NULL; start = vma->vm_start; if (expand_stack(vma, addr)) return NULL; diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index ac9791dd4768..5139c4ebb96b 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -363,10 +363,12 @@ static int vlan_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) ifrr.ifr_ifru = ifr->ifr_ifru; switch (cmd) { + case SIOCSHWTSTAMP: + if (!net_eq(dev_net(dev), &init_net)) + break; case SIOCGMIIPHY: case SIOCGMIIREG: case SIOCSMIIREG: - case SIOCSHWTSTAMP: case SIOCGHWTSTAMP: if (netif_device_present(real_dev) && ops->ndo_do_ioctl) err = ops->ndo_do_ioctl(real_dev, &ifrr, cmd); diff --git a/net/ax25/ax25_route.c b/net/ax25/ax25_route.c index 149f82bd83fd..6ba56f215229 100644 --- a/net/ax25/ax25_route.c +++ b/net/ax25/ax25_route.c @@ -443,9 +443,11 @@ int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr) } if (ax25->sk != NULL) { + local_bh_disable(); bh_lock_sock(ax25->sk); sock_reset_flag(ax25->sk, SOCK_ZAPPED); bh_unlock_sock(ax25->sk); + local_bh_enable(); } put: diff --git a/net/bluetooth/hidp/sock.c b/net/bluetooth/hidp/sock.c index c06f9a0107d6..cb2df55492bb 100644 --- a/net/bluetooth/hidp/sock.c +++ b/net/bluetooth/hidp/sock.c @@ -76,6 +76,7 @@ static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long sockfd_put(csock); return err; } + ca.name[sizeof(ca.name)-1] = 0; err = hidp_connection_add(&ca, csock, isock); if (!err && copy_to_user(argp, &ca, sizeof(ca))) diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 50e84e634dfe..c7a281549d91 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -471,13 +471,15 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) call_netdevice_notifiers(NETDEV_JOIN, dev); err = dev_set_allmulti(dev, 1); - if (err) - goto put_back; + if (err) { + kfree(p); /* kobject not yet init'd, manually free */ + goto err1; + } err = kobject_init_and_add(&p->kobj, &brport_ktype, &(dev->dev.kobj), SYSFS_BRIDGE_PORT_ATTR); if (err) - goto err1; + goto err2; err = br_sysfs_addif(p); if (err) @@ -551,12 +553,9 @@ err3: sysfs_remove_link(br->ifobj, p->dev->name); err2: kobject_put(&p->kobj); - p = NULL; /* kobject_put frees */ -err1: dev_set_allmulti(dev, -1); -put_back: +err1: dev_put(dev); - kfree(p); return err; } diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c index 93b5525bcccf..2ae0451fd634 100644 --- a/net/bridge/br_netfilter_hooks.c +++ b/net/bridge/br_netfilter_hooks.c @@ -507,6 +507,7 @@ static unsigned int br_nf_pre_routing(void *priv, nf_bridge->ipv4_daddr = ip_hdr(skb)->daddr; skb->protocol = htons(ETH_P_IP); + skb->transport_header = skb->network_header + ip_hdr(skb)->ihl * 4; NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, state->net, state->sk, skb, skb->dev, NULL, diff --git a/net/bridge/br_netfilter_ipv6.c b/net/bridge/br_netfilter_ipv6.c index 69dfd212e50d..f94c83f5cc37 100644 --- a/net/bridge/br_netfilter_ipv6.c +++ b/net/bridge/br_netfilter_ipv6.c @@ -237,6 +237,8 @@ unsigned int br_nf_pre_routing_ipv6(void *priv, nf_bridge->ipv6_daddr = ipv6_hdr(skb)->daddr; skb->protocol = htons(ETH_P_IPV6); + skb->transport_header = skb->network_header + sizeof(struct ipv6hdr); + NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, state->net, state->sk, skb, skb->dev, NULL, br_nf_pre_routing_finish_ipv6); diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index f13402d407e4..1a87cf78fadc 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -2046,7 +2046,8 @@ static int ebt_size_mwt(struct compat_ebt_entry_mwt *match32, if (match_kern) match_kern->match_size = ret; - if (WARN_ON(type == EBT_COMPAT_TARGET && size_left)) + /* rule should have no remaining data after target */ + if (type == EBT_COMPAT_TARGET && size_left) return -EINVAL; match32 = (struct compat_ebt_entry_mwt *) buf; diff --git a/net/core/dev.c b/net/core/dev.c index f2181624567a..1dbfbb4a41ce 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4558,7 +4558,6 @@ static struct sk_buff *napi_frags_skb(struct napi_struct *napi) skb_reset_mac_header(skb); skb_gro_reset_offset(skb); - eth = skb_gro_header_fast(skb, 0); if (unlikely(skb_gro_header_hard(skb, hlen))) { eth = skb_gro_header_slow(skb, hlen, 0); if (unlikely(!eth)) { @@ -4566,6 +4565,7 @@ static struct sk_buff *napi_frags_skb(struct napi_struct *napi) return NULL; } } else { + eth = (const struct ethhdr *)skb->data; gro_pull_from_frag0(skb, hlen); NAPI_GRO_CB(skb)->frag0 += hlen; NAPI_GRO_CB(skb)->frag0_len -= hlen; @@ -7022,7 +7022,7 @@ static void netdev_wait_allrefs(struct net_device *dev) refcnt = netdev_refcnt_read(dev); - if (time_after(jiffies, warning_time + 10 * HZ)) { + if (refcnt && time_after(jiffies, warning_time + 10 * HZ)) { pr_emerg("unregister_netdevice: waiting for %s to become free. Usage count = %d\n", dev->name, refcnt); warning_time = jiffies; diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 9a53c66deb64..66428c0eb663 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -428,8 +428,13 @@ static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev, if (rc >= 0) info.n_priv_flags = rc; } - if (ops->get_regs_len) - info.regdump_len = ops->get_regs_len(dev); + if (ops->get_regs_len) { + int ret = ops->get_regs_len(dev); + + if (ret > 0) + info.regdump_len = ret; + } + if (ops->get_eeprom_len) info.eedump_len = ops->get_eeprom_len(dev); @@ -883,6 +888,9 @@ static int ethtool_get_regs(struct net_device *dev, char __user *useraddr) return -EFAULT; reglen = ops->get_regs_len(dev); + if (reglen <= 0) + return reglen; + if (regs.len > reglen) regs.len = reglen; @@ -893,13 +901,16 @@ static int ethtool_get_regs(struct net_device *dev, char __user *useraddr) return -ENOMEM; } + if (regs.len < reglen) + reglen = regs.len; + ops->get_regs(dev, ®s, regbuf); ret = -EFAULT; if (copy_to_user(useraddr, ®s, sizeof(regs))) goto out; useraddr += offsetof(struct ethtool_regs, data); - if (regbuf && copy_to_user(useraddr, regbuf, regs.len)) + if (copy_to_user(useraddr, regbuf, reglen)) goto out; ret = 0; diff --git a/net/core/filter.c b/net/core/filter.c index 1a9ded6af138..3c5f51198c41 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -742,6 +742,17 @@ static bool chk_code_allowed(u16 code_to_probe) return codes[code_to_probe]; } +static bool bpf_check_basics_ok(const struct sock_filter *filter, + unsigned int flen) +{ + if (filter == NULL) + return false; + if (flen == 0 || flen > BPF_MAXINSNS) + return false; + + return true; +} + /** * bpf_check_classic - verify socket filter code * @filter: filter to verify @@ -762,9 +773,6 @@ static int bpf_check_classic(const struct sock_filter *filter, bool anc_found; int pc; - if (flen == 0 || flen > BPF_MAXINSNS) - return -EINVAL; - /* Check the filter code now */ for (pc = 0; pc < flen; pc++) { const struct sock_filter *ftest = &filter[pc]; @@ -1057,7 +1065,7 @@ int bpf_prog_create(struct bpf_prog **pfp, struct sock_fprog_kern *fprog) struct bpf_prog *fp; /* Make sure new filter is there and in the right amounts. */ - if (fprog->filter == NULL) + if (!bpf_check_basics_ok(fprog->filter, fprog->len)) return -EINVAL; fp = bpf_prog_alloc(bpf_prog_size(fprog->len), 0); @@ -1104,7 +1112,7 @@ int bpf_prog_create_from_user(struct bpf_prog **pfp, struct sock_fprog *fprog, int err; /* Make sure new filter is there and in the right amounts. */ - if (fprog->filter == NULL) + if (!bpf_check_basics_ok(fprog->filter, fprog->len)) return -EINVAL; fp = bpf_prog_alloc(bpf_prog_size(fprog->len), 0); @@ -1184,7 +1192,6 @@ int __sk_attach_filter(struct sock_fprog *fprog, struct sock *sk, bool locked) { unsigned int fsize = bpf_classic_proglen(fprog); - unsigned int bpf_fsize = bpf_prog_size(fprog->len); struct bpf_prog *prog; int err; @@ -1192,10 +1199,10 @@ int __sk_attach_filter(struct sock_fprog *fprog, struct sock *sk, return -EPERM; /* Make sure new filter is there and in the right amounts. */ - if (fprog->filter == NULL) + if (!bpf_check_basics_ok(fprog->filter, fprog->len)) return -EINVAL; - prog = bpf_prog_alloc(bpf_fsize, 0); + prog = bpf_prog_alloc(bpf_prog_size(fprog->len), 0); if (!prog) return -ENOMEM; diff --git a/net/core/neighbour.c b/net/core/neighbour.c index fef2043cdb1d..2572b51e86f4 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -30,6 +30,7 @@ #include <linux/times.h> #include <net/net_namespace.h> #include <net/neighbour.h> +#include <net/arp.h> #include <net/dst.h> #include <net/sock.h> #include <net/netevent.h> @@ -2490,7 +2491,13 @@ int neigh_xmit(int index, struct net_device *dev, if (!tbl) goto out; rcu_read_lock_bh(); - neigh = __neigh_lookup_noref(tbl, addr, dev); + if (index == NEIGH_ARP_TABLE) { + u32 key = *((u32 *)addr); + + neigh = __ipv4_neigh_lookup_noref(dev, key); + } else { + neigh = __neigh_lookup_noref(tbl, addr, dev); + } if (!neigh) neigh = __neigh_create(tbl, addr, dev, false); err = PTR_ERR(neigh); @@ -2698,6 +2705,7 @@ static void *neigh_get_idx_any(struct seq_file *seq, loff_t *pos) } void *neigh_seq_start(struct seq_file *seq, loff_t *pos, struct neigh_table *tbl, unsigned int neigh_seq_flags) + __acquires(tbl->lock) __acquires(rcu_bh) { struct neigh_seq_state *state = seq->private; @@ -2708,6 +2716,7 @@ void *neigh_seq_start(struct seq_file *seq, loff_t *pos, struct neigh_table *tbl rcu_read_lock_bh(); state->nht = rcu_dereference_bh(tbl->nht); + read_lock(&tbl->lock); return *pos ? neigh_get_idx_any(seq, pos) : SEQ_START_TOKEN; } @@ -2741,8 +2750,13 @@ out: EXPORT_SYMBOL(neigh_seq_next); void neigh_seq_stop(struct seq_file *seq, void *v) + __releases(tbl->lock) __releases(rcu_bh) { + struct neigh_seq_state *state = seq->private; + struct neigh_table *tbl = state->tbl; + + read_unlock(&tbl->lock); rcu_read_unlock_bh(); } EXPORT_SYMBOL(neigh_seq_stop); diff --git a/net/core/pktgen.c b/net/core/pktgen.c index b6327601f979..4ea957c1e7ee 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -3139,7 +3139,13 @@ static int pktgen_wait_thread_run(struct pktgen_thread *t) { while (thread_is_running(t)) { + /* note: 't' will still be around even after the unlock/lock + * cycle because pktgen_thread threads are only cleared at + * net exit + */ + mutex_unlock(&pktgen_thread_lock); msleep_interruptible(100); + mutex_lock(&pktgen_thread_lock); if (signal_pending(current)) goto signal; @@ -3154,6 +3160,10 @@ static int pktgen_wait_all_threads_run(struct pktgen_net *pn) struct pktgen_thread *t; int sig = 1; + /* prevent from racing with rmmod */ + if (!try_module_get(THIS_MODULE)) + return sig; + mutex_lock(&pktgen_thread_lock); list_for_each_entry(t, &pn->pktgen_threads, th_list) { @@ -3167,6 +3177,7 @@ static int pktgen_wait_all_threads_run(struct pktgen_net *pn) t->control |= (T_STOP); mutex_unlock(&pktgen_thread_lock); + module_put(THIS_MODULE); return sig; } diff --git a/net/core/sockev_nlmcast.c b/net/core/sockev_nlmcast.c index 1e92c5632b97..0a8b0ed99356 100644 --- a/net/core/sockev_nlmcast.c +++ b/net/core/sockev_nlmcast.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2015, 2018 The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2015, 2018-2019 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 @@ -73,11 +73,13 @@ static int sockev_client_cb(struct notifier_block *nb, sock = (struct socket *)data; if (!socknlmsgsk || !sock) - goto done; + goto sk_null; sk = sock->sk; if (!sk) - goto done; + goto sk_null; + + sock_hold(sk); if (sk->sk_family != AF_INET && sk->sk_family != AF_INET6) goto done; @@ -108,6 +110,8 @@ static int sockev_client_cb(struct notifier_block *nb, smsg->skflags = sk->sk_flags; nlmsg_notify(socknlmsgsk, skb, 0, SKNLGRP_SOCKEV, 0, GFP_KERNEL); done: + sock_put(sk); +sk_null: return 0; } diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index ac2966f02d07..c2380bb1fdab 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -475,6 +475,7 @@ static void ip_copy_metadata(struct sk_buff *to, struct sk_buff *from) to->pkt_type = from->pkt_type; to->priority = from->priority; to->protocol = from->protocol; + to->skb_iif = from->skb_iif; skb_dst_drop(to); skb_dst_copy(to, from); to->dev = from->dev; diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c index fcf327ebd134..bbcbbc1cc2cc 100644 --- a/net/ipv4/ip_vti.c +++ b/net/ipv4/ip_vti.c @@ -648,9 +648,9 @@ static int __init vti_init(void) return err; rtnl_link_failed: - xfrm4_protocol_deregister(&vti_ipcomp4_protocol, IPPROTO_COMP); -xfrm_tunnel_failed: xfrm4_tunnel_deregister(&ipip_handler, AF_INET); +xfrm_tunnel_failed: + xfrm4_protocol_deregister(&vti_ipcomp4_protocol, IPPROTO_COMP); xfrm_proto_comp_failed: xfrm4_protocol_deregister(&vti_ah4_protocol, IPPROTO_AH); xfrm_proto_ah_failed: @@ -666,6 +666,7 @@ pernet_dev_failed: static void __exit vti_fini(void) { rtnl_link_unregister(&vti_link_ops); + xfrm4_tunnel_deregister(&ipip_handler, AF_INET); xfrm4_protocol_deregister(&vti_ipcomp4_protocol, IPPROTO_COMP); xfrm4_protocol_deregister(&vti_ah4_protocol, IPPROTO_AH); xfrm4_protocol_deregister(&vti_esp4_protocol, IPPROTO_ESP); diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index b001ad668108..555586fc7840 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -303,6 +303,7 @@ static const struct snmp_mib snmp4_net_list[] = { SNMP_MIB_ITEM("TCPKeepAlive", LINUX_MIB_TCPKEEPALIVE), SNMP_MIB_ITEM("TCPMTUPFail", LINUX_MIB_TCPMTUPFAIL), SNMP_MIB_ITEM("TCPMTUPSuccess", LINUX_MIB_TCPMTUPSUCCESS), + SNMP_MIB_ITEM("TCPWqueueTooBig", LINUX_MIB_TCPWQUEUETOOBIG), SNMP_MIB_SENTINEL }; diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 7541427537d0..69e1a11f4ca8 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -167,6 +167,7 @@ static int icmp_filter(const struct sock *sk, const struct sk_buff *skb) */ static int raw_v4_input(struct sk_buff *skb, const struct iphdr *iph, int hash) { + int dif = inet_iif(skb); struct sock *sk; struct hlist_head *head; int delivered = 0; @@ -179,8 +180,7 @@ static int raw_v4_input(struct sk_buff *skb, const struct iphdr *iph, int hash) net = dev_net(skb->dev); sk = __raw_v4_lookup(net, __sk_head(head), iph->protocol, - iph->saddr, iph->daddr, - skb->dev->ifindex); + iph->saddr, iph->daddr, dif); while (sk) { delivered = 1; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 773c71771ffd..2c661934e557 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1168,25 +1168,39 @@ static struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie) return dst; } -static void ipv4_link_failure(struct sk_buff *skb) +static void ipv4_send_dest_unreach(struct sk_buff *skb) { struct ip_options opt; - struct rtable *rt; int res; /* Recompile ip options since IPCB may not be valid anymore. + * Also check we have a reasonable ipv4 header. */ - memset(&opt, 0, sizeof(opt)); - opt.optlen = ip_hdr(skb)->ihl*4 - sizeof(struct iphdr); + if (!pskb_network_may_pull(skb, sizeof(struct iphdr)) || + ip_hdr(skb)->version != 4 || ip_hdr(skb)->ihl < 5) + return; - rcu_read_lock(); - res = __ip_options_compile(dev_net(skb->dev), &opt, skb, NULL); - rcu_read_unlock(); + memset(&opt, 0, sizeof(opt)); + if (ip_hdr(skb)->ihl > 5) { + if (!pskb_network_may_pull(skb, ip_hdr(skb)->ihl * 4)) + return; + opt.optlen = ip_hdr(skb)->ihl * 4 - sizeof(struct iphdr); - if (res) - return; + rcu_read_lock(); + res = __ip_options_compile(dev_net(skb->dev), &opt, skb, NULL); + rcu_read_unlock(); + if (res) + return; + } __icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0, &opt); +} + +static void ipv4_link_failure(struct sk_buff *skb) +{ + struct rtable *rt; + + ipv4_send_dest_unreach(skb); rt = skb_rtable(skb); if (rt) diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 5d563615718d..a56b3eb930b9 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -36,6 +36,8 @@ static int ip_local_port_range_min[] = { 1, 1 }; static int ip_local_port_range_max[] = { 65535, 65535 }; static int tcp_adv_win_scale_min = -31; static int tcp_adv_win_scale_max = 31; +static int tcp_min_snd_mss_min = TCP_MIN_SND_MSS; +static int tcp_min_snd_mss_max = 65535; static int ip_ttl_min = 1; static int ip_ttl_max = 255; static int tcp_syn_retries_min = 1; @@ -46,6 +48,7 @@ static int tcp_delack_seg_min = TCP_DELACK_MIN; static int tcp_delack_seg_max = 60; static int tcp_use_userconfig_min; static int tcp_use_userconfig_max = 1; +static int one_day_secs = 24 * 3600; /* Update system visible IP port range */ static void set_local_port_range(struct net *net, int range[2]) @@ -616,7 +619,9 @@ static struct ctl_table ipv4_table[] = { .data = &sysctl_tcp_min_rtt_wlen, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &one_day_secs }, { .procname = "tcp_low_latency", @@ -991,6 +996,15 @@ static struct ctl_table ipv4_net_table[] = { .proc_handler = proc_dointvec, }, { + .procname = "tcp_min_snd_mss", + .data = &init_net.ipv4.sysctl_tcp_min_snd_mss, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &tcp_min_snd_mss_min, + .extra2 = &tcp_min_snd_mss_max, + }, + { .procname = "tcp_probe_threshold", .data = &init_net.ipv4.sysctl_tcp_probe_threshold, .maxlen = sizeof(int), diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 16d2b59989ec..0fe1eaf0bc31 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3207,6 +3207,7 @@ void __init tcp_init(void) int max_rshare, max_wshare, cnt; unsigned int i; + BUILD_BUG_ON(TCP_MIN_SND_MSS <= MAX_TCP_OPTION_SPACE); sock_skb_cb_check_size(sizeof(struct tcp_skb_cb)); percpu_counter_init(&tcp_sockets_allocated, 0, GFP_KERNEL); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 95273c47a513..2fb43a1f259d 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -1276,7 +1276,7 @@ static bool tcp_shifted_skb(struct sock *sk, struct sk_buff *skb, TCP_SKB_CB(skb)->seq += shifted; tcp_skb_pcount_add(prev, pcount); - BUG_ON(tcp_skb_pcount(skb) < pcount); + WARN_ON_ONCE(tcp_skb_pcount(skb) < pcount); tcp_skb_pcount_add(skb, -pcount); /* When we're adding to gso_segs == 1, gso_size will be zero, @@ -1338,6 +1338,21 @@ static int skb_can_shift(const struct sk_buff *skb) return !skb_headlen(skb) && skb_is_nonlinear(skb); } +int tcp_skb_shift(struct sk_buff *to, struct sk_buff *from, + int pcount, int shiftlen) +{ + /* TCP min gso_size is 8 bytes (TCP_MIN_GSO_SIZE) + * Since TCP_SKB_CB(skb)->tcp_gso_segs is 16 bits, we need + * to make sure not storing more than 65535 * 8 bytes per skb, + * even if current MSS is bigger. + */ + if (unlikely(to->len + shiftlen >= 65535 * TCP_MIN_GSO_SIZE)) + return 0; + if (unlikely(tcp_skb_pcount(to) + pcount > 65535)) + return 0; + return skb_shift(to, from, shiftlen); +} + /* Try collapsing SACK blocks spanning across multiple skbs to a single * skb. */ @@ -1349,6 +1364,7 @@ static struct sk_buff *tcp_shift_skb_data(struct sock *sk, struct sk_buff *skb, struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *prev; int mss; + int next_pcount; int pcount = 0; int len; int in_sack; @@ -1443,7 +1459,7 @@ static struct sk_buff *tcp_shift_skb_data(struct sock *sk, struct sk_buff *skb, if (!after(TCP_SKB_CB(skb)->seq + len, tp->snd_una)) goto fallback; - if (!skb_shift(prev, skb, len)) + if (!tcp_skb_shift(prev, skb, pcount, len)) goto fallback; if (!tcp_shifted_skb(sk, skb, state, pcount, len, mss, dup_sack)) goto out; @@ -1462,11 +1478,11 @@ static struct sk_buff *tcp_shift_skb_data(struct sock *sk, struct sk_buff *skb, goto out; len = skb->len; - if (skb_shift(prev, skb, len)) { - pcount += tcp_skb_pcount(skb); - tcp_shifted_skb(sk, skb, state, tcp_skb_pcount(skb), len, mss, 0); + next_pcount = tcp_skb_pcount(skb); + if (tcp_skb_shift(prev, skb, next_pcount, len)) { + pcount += next_pcount; + tcp_shifted_skb(sk, skb, state, next_pcount, len, mss, 0); } - out: state->fack_count += pcount; return prev; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index d1b911cde946..d2d95cf398e8 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2435,6 +2435,7 @@ static int __net_init tcp_sk_init(struct net *net) net->ipv4.sysctl_tcp_ecn_fallback = 1; net->ipv4.sysctl_tcp_base_mss = TCP_BASE_MSS; + net->ipv4.sysctl_tcp_min_snd_mss = TCP_MIN_SND_MSS; net->ipv4.sysctl_tcp_probe_threshold = TCP_PROBE_THRESHOLD; net->ipv4.sysctl_tcp_probe_interval = TCP_PROBE_INTERVAL; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 9ada5e4a28e5..6f7303d51274 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1161,6 +1161,11 @@ int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len, if (nsize < 0) nsize = 0; + if (unlikely((sk->sk_wmem_queued >> 1) > sk->sk_sndbuf)) { + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPWQUEUETOOBIG); + return -ENOMEM; + } + if (skb_unclone(skb, gfp)) return -ENOMEM; @@ -1327,8 +1332,7 @@ static inline int __tcp_mtu_to_mss(struct sock *sk, int pmtu) mss_now -= icsk->icsk_ext_hdr_len; /* Then reserve room for full set of TCP options and 8 bytes of data */ - if (mss_now < 48) - mss_now = 48; + mss_now = max(mss_now, sock_net(sk)->ipv4.sysctl_tcp_min_snd_mss); return mss_now; } diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index 2221e3c36bd5..28fed4ade750 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -166,6 +166,7 @@ static void tcp_mtu_probing(struct inet_connection_sock *icsk, struct sock *sk) mss = tcp_mtu_to_mss(sk, icsk->icsk_mtup.search_low) >> 1; mss = min(net->ipv4.sysctl_tcp_base_mss, mss); mss = max(mss, 68 - tp->tcp_header_len); + mss = max(mss, net->ipv4.sysctl_tcp_min_snd_mss); icsk->icsk_mtup.search_low = tcp_mss_to_mtu(sk, mss); tcp_sync_mss(sk, icsk->icsk_pmtu_cookie); } diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index 528f13718687..0a6bcf7e7b54 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -112,7 +112,8 @@ static void _decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse) { const struct iphdr *iph = ip_hdr(skb); - u8 *xprth = skb_network_header(skb) + iph->ihl * 4; + int ihl = iph->ihl; + u8 *xprth = skb_network_header(skb) + ihl * 4; struct flowi4 *fl4 = &fl->u.ip4; int oif = 0; @@ -123,6 +124,11 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse) fl4->flowi4_mark = skb->mark; fl4->flowi4_oif = reverse ? skb->skb_iif : oif; + fl4->flowi4_proto = iph->protocol; + fl4->daddr = reverse ? iph->saddr : iph->daddr; + fl4->saddr = reverse ? iph->daddr : iph->saddr; + fl4->flowi4_tos = iph->tos; + if (!ip_is_fragment(iph)) { switch (iph->protocol) { case IPPROTO_UDP: @@ -134,7 +140,7 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse) pskb_may_pull(skb, xprth + 4 - skb->data)) { __be16 *ports; - xprth = skb_network_header(skb) + iph->ihl * 4; + xprth = skb_network_header(skb) + ihl * 4; ports = (__be16 *)xprth; fl4->fl4_sport = ports[!!reverse]; @@ -147,7 +153,7 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse) pskb_may_pull(skb, xprth + 2 - skb->data)) { u8 *icmp; - xprth = skb_network_header(skb) + iph->ihl * 4; + xprth = skb_network_header(skb) + ihl * 4; icmp = xprth; fl4->fl4_icmp_type = icmp[0]; @@ -160,7 +166,7 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse) pskb_may_pull(skb, xprth + 4 - skb->data)) { __be32 *ehdr; - xprth = skb_network_header(skb) + iph->ihl * 4; + xprth = skb_network_header(skb) + ihl * 4; ehdr = (__be32 *)xprth; fl4->fl4_ipsec_spi = ehdr[0]; @@ -172,7 +178,7 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse) pskb_may_pull(skb, xprth + 8 - skb->data)) { __be32 *ah_hdr; - xprth = skb_network_header(skb) + iph->ihl * 4; + xprth = skb_network_header(skb) + ihl * 4; ah_hdr = (__be32 *)xprth; fl4->fl4_ipsec_spi = ah_hdr[1]; @@ -184,7 +190,7 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse) pskb_may_pull(skb, xprth + 4 - skb->data)) { __be16 *ipcomp_hdr; - xprth = skb_network_header(skb) + iph->ihl * 4; + xprth = skb_network_header(skb) + ihl * 4; ipcomp_hdr = (__be16 *)xprth; fl4->fl4_ipsec_spi = htonl(ntohs(ipcomp_hdr[1])); @@ -197,7 +203,7 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse) __be16 *greflags; __be32 *gre_hdr; - xprth = skb_network_header(skb) + iph->ihl * 4; + xprth = skb_network_header(skb) + ihl * 4; greflags = (__be16 *)xprth; gre_hdr = (__be32 *)xprth; @@ -214,10 +220,6 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse) break; } } - fl4->flowi4_proto = iph->protocol; - fl4->daddr = reverse ? iph->saddr : iph->daddr; - fl4->saddr = reverse ? iph->daddr : iph->saddr; - fl4->flowi4_tos = iph->tos; } static inline int xfrm4_garbage_collect(struct dst_ops *ops) diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index f3a0a9c0f61e..8a6c682bfc22 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c @@ -94,15 +94,21 @@ static struct ip6_flowlabel *fl_lookup(struct net *net, __be32 label) return fl; } +static void fl_free_rcu(struct rcu_head *head) +{ + struct ip6_flowlabel *fl = container_of(head, struct ip6_flowlabel, rcu); + + if (fl->share == IPV6_FL_S_PROCESS) + put_pid(fl->owner.pid); + kfree(fl->opt); + kfree(fl); +} + static void fl_free(struct ip6_flowlabel *fl) { - if (fl) { - if (fl->share == IPV6_FL_S_PROCESS) - put_pid(fl->owner.pid); - kfree(fl->opt); - kfree_rcu(fl, rcu); - } + if (fl) + call_rcu(&fl->rcu, fl_free_rcu); } static void fl_release(struct ip6_flowlabel *fl) @@ -248,9 +254,9 @@ struct ip6_flowlabel *fl6_sock_lookup(struct sock *sk, __be32 label) rcu_read_lock_bh(); for_each_sk_fl_rcu(np, sfl) { struct ip6_flowlabel *fl = sfl->fl; - if (fl->label == label) { + + if (fl->label == label && atomic_inc_not_zero(&fl->users)) { fl->lastuse = jiffies; - atomic_inc(&fl->users); rcu_read_unlock_bh(); return fl; } @@ -616,7 +622,8 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) goto done; } fl1 = sfl->fl; - atomic_inc(&fl1->users); + if (!atomic_inc_not_zero(&fl1->users)) + fl1 = NULL; break; } } @@ -633,9 +640,9 @@ recheck: if (fl1->share == IPV6_FL_S_EXCL || fl1->share != fl->share || ((fl1->share == IPV6_FL_S_PROCESS) && - (fl1->owner.pid == fl->owner.pid)) || + (fl1->owner.pid != fl->owner.pid)) || ((fl1->share == IPV6_FL_S_USER) && - uid_eq(fl1->owner.uid, fl->owner.uid))) + !uid_eq(fl1->owner.uid, fl->owner.uid))) goto release; err = -ENOMEM; diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index 8d11a034ca3f..71263754b19b 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -121,6 +121,7 @@ struct ipv6_txoptions *ipv6_update_options(struct sock *sk, static bool setsockopt_needs_rtnl(int optname) { switch (optname) { + case IPV6_ADDRFORM: case IPV6_ADD_MEMBERSHIP: case IPV6_DROP_MEMBERSHIP: case IPV6_JOIN_ANYCAST: @@ -199,7 +200,7 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, } fl6_free_socklist(sk); - ipv6_sock_mc_close(sk); + __ipv6_sock_mc_close(sk); /* * Sock is moving from IPv6 to IPv4 (sk_prot), so diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index a5ec9a0cbb80..976c8133a281 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -276,16 +276,14 @@ static struct inet6_dev *ip6_mc_find_dev_rcu(struct net *net, return idev; } -void ipv6_sock_mc_close(struct sock *sk) +void __ipv6_sock_mc_close(struct sock *sk) { struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6_mc_socklist *mc_lst; struct net *net = sock_net(sk); - if (!rcu_access_pointer(np->ipv6_mc_list)) - return; + ASSERT_RTNL(); - rtnl_lock(); while ((mc_lst = rtnl_dereference(np->ipv6_mc_list)) != NULL) { struct net_device *dev; @@ -303,8 +301,17 @@ void ipv6_sock_mc_close(struct sock *sk) atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc); kfree_rcu(mc_lst, rcu); - } +} + +void ipv6_sock_mc_close(struct sock *sk) +{ + struct ipv6_pinfo *np = inet6_sk(sk); + + if (!rcu_access_pointer(np->ipv6_mc_list)) + return; + rtnl_lock(); + __ipv6_sock_mc_close(sk); rtnl_unlock(); } diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 0ef8e114c8ab..30db0167bd19 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -283,7 +283,9 @@ static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) /* Binding to link-local address requires an interface */ if (!sk->sk_bound_dev_if) goto out_unlock; + } + if (sk->sk_bound_dev_if) { err = -ENODEV; dev = dev_get_by_index_rcu(sock_net(sk), sk->sk_bound_dev_if); diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 77736190dc15..5039486c4f86 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -1076,7 +1076,7 @@ static void ipip6_tunnel_bind_dev(struct net_device *dev) if (!tdev && tunnel->parms.link) tdev = __dev_get_by_index(tunnel->net, tunnel->parms.link); - if (tdev) { + if (tdev && !netif_is_l3_master(tdev)) { int t_hlen = tunnel->hlen + sizeof(struct iphdr); dev->hard_header_len = tdev->hard_header_len + sizeof(struct iphdr); diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c index 56b72cada346..f9d493c59d6c 100644 --- a/net/ipv6/xfrm6_tunnel.c +++ b/net/ipv6/xfrm6_tunnel.c @@ -391,6 +391,10 @@ static void __exit xfrm6_tunnel_fini(void) xfrm6_tunnel_deregister(&xfrm6_tunnel_handler, AF_INET6); xfrm_unregister_type(&xfrm6_tunnel_type, AF_INET6); unregister_pernet_subsys(&xfrm6_tunnel_net_ops); + /* Someone maybe has gotten the xfrm6_tunnel_spi. + * So need to wait it. + */ + rcu_barrier(); kmem_cache_destroy(xfrm6_tunnel_spi_kmem); } diff --git a/net/lapb/lapb_iface.c b/net/lapb/lapb_iface.c index fc60d9d738b5..cdb913e7627e 100644 --- a/net/lapb/lapb_iface.c +++ b/net/lapb/lapb_iface.c @@ -182,6 +182,7 @@ int lapb_unregister(struct net_device *dev) lapb = __lapb_devtostruct(dev); if (!lapb) goto out; + lapb_put(lapb); lapb_stop_t1timer(lapb); lapb_stop_t2timer(lapb); diff --git a/net/llc/llc_output.c b/net/llc/llc_output.c index 94425e421213..9e4b6bcf6920 100644 --- a/net/llc/llc_output.c +++ b/net/llc/llc_output.c @@ -72,6 +72,8 @@ int llc_build_and_send_ui_pkt(struct llc_sap *sap, struct sk_buff *skb, rc = llc_mac_hdr_init(skb, skb->dev->dev_addr, dmac); if (likely(!rc)) rc = dev_queue_xmit(skb); + else + kfree_skb(skb); return rc; } diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 9066003b21b1..9e86e8f717d2 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1104,9 +1104,6 @@ static void ieee80211_chswitch_work(struct work_struct *work) goto out; } - /* XXX: shouldn't really modify cfg80211-owned data! */ - ifmgd->associated->channel = sdata->csa_chandef.chan; - ifmgd->csa_waiting_bcn = true; ieee80211_sta_reset_beacon_monitor(sdata); diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index ac212542a217..c4509a10ce52 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -1484,7 +1484,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, if (!cp) { int v; - if (!sysctl_schedule_icmp(ipvs)) + if (ipip || !sysctl_schedule_icmp(ipvs)) return NF_ACCEPT; if (!ip_vs_try_to_schedule(ipvs, AF_INET, skb, pd, &v, &cp, &ciph)) diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index b6e72af15237..cdafbd38a456 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -1699,7 +1699,7 @@ static int __init xt_init(void) seqcount_init(&per_cpu(xt_recseq, i)); } - xt = kmalloc(sizeof(struct xt_af) * NFPROTO_NUMPROTO, GFP_KERNEL); + xt = kcalloc(NFPROTO_NUMPROTO, sizeof(struct xt_af), GFP_KERNEL); if (!xt) return -ENOMEM; diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 7d93228ba1e1..c78bcc13ebab 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -2490,8 +2490,8 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) void *ph; DECLARE_SOCKADDR(struct sockaddr_ll *, saddr, msg->msg_name); bool need_wait = !(msg->msg_flags & MSG_DONTWAIT); + unsigned char *addr = NULL; int tp_len, size_max; - unsigned char *addr; int len_sum = 0; int status = TP_STATUS_AVAILABLE; int hlen, tlen; @@ -2511,10 +2511,13 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) sll_addr))) goto out; proto = saddr->sll_protocol; - addr = saddr->sll_halen ? saddr->sll_addr : NULL; dev = dev_get_by_index(sock_net(&po->sk), saddr->sll_ifindex); - if (addr && dev && saddr->sll_halen < dev->addr_len) - goto out_put; + if (po->sk.sk_socket->type == SOCK_DGRAM) { + if (dev && msg->msg_namelen < dev->addr_len + + offsetof(struct sockaddr_ll, sll_addr)) + goto out_put; + addr = saddr->sll_addr; + } } err = -ENXIO; @@ -2652,7 +2655,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) struct sk_buff *skb; struct net_device *dev; __be16 proto; - unsigned char *addr; + unsigned char *addr = NULL; int err, reserve = 0; struct sockcm_cookie sockc; struct virtio_net_hdr vnet_hdr = { 0 }; @@ -2672,7 +2675,6 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) if (likely(saddr == NULL)) { dev = packet_cached_dev_get(po); proto = po->num; - addr = NULL; } else { err = -EINVAL; if (msg->msg_namelen < sizeof(struct sockaddr_ll)) @@ -2680,10 +2682,13 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) if (msg->msg_namelen < (saddr->sll_halen + offsetof(struct sockaddr_ll, sll_addr))) goto out; proto = saddr->sll_protocol; - addr = saddr->sll_halen ? saddr->sll_addr : NULL; dev = dev_get_by_index(sock_net(sk), saddr->sll_ifindex); - if (addr && dev && saddr->sll_halen < dev->addr_len) - goto out_unlock; + if (sock->type == SOCK_DGRAM) { + if (dev && msg->msg_namelen < dev->addr_len + + offsetof(struct sockaddr_ll, sll_addr)) + goto out_unlock; + addr = saddr->sll_addr; + } } err = -ENXIO; @@ -4518,14 +4523,29 @@ static void __exit packet_exit(void) static int __init packet_init(void) { - int rc = proto_register(&packet_proto, 0); + int rc; - if (rc != 0) + rc = proto_register(&packet_proto, 0); + if (rc) goto out; + rc = sock_register(&packet_family_ops); + if (rc) + goto out_proto; + rc = register_pernet_subsys(&packet_net_ops); + if (rc) + goto out_sock; + rc = register_netdevice_notifier(&packet_netdev_notifier); + if (rc) + goto out_pernet; - sock_register(&packet_family_ops); - register_pernet_subsys(&packet_net_ops); - register_netdevice_notifier(&packet_netdev_notifier); + return 0; + +out_pernet: + unregister_pernet_subsys(&packet_net_ops); +out_sock: + sock_unregister(PF_PACKET); +out_proto: + proto_unregister(&packet_proto); out: return rc; } diff --git a/net/rds/ib_rdma.c b/net/rds/ib_rdma.c index a2340748ec86..d7888924166e 100644 --- a/net/rds/ib_rdma.c +++ b/net/rds/ib_rdma.c @@ -725,12 +725,14 @@ static int rds_ib_flush_mr_pool(struct rds_ib_mr_pool *pool, wait_clean_list_grace(); list_to_llist_nodes(pool, &unmap_list, &clean_nodes, &clean_tail); - if (ibmr_ret) + if (ibmr_ret) { *ibmr_ret = llist_entry(clean_nodes, struct rds_ib_mr, llnode); - + clean_nodes = clean_nodes->next; + } /* more than one entry in llist nodes */ - if (clean_nodes->next) - llist_add_batch(clean_nodes->next, clean_tail, &pool->clean_list); + if (clean_nodes) + llist_add_batch(clean_nodes, clean_tail, + &pool->clean_list); } diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c index a06c9d6bfc9c..05c7a66f64da 100644 --- a/net/sched/sch_tbf.c +++ b/net/sched/sch_tbf.c @@ -142,16 +142,6 @@ static u64 psched_ns_t2l(const struct psched_ratecfg *r, return len; } -/* - * Return length of individual segments of a gso packet, - * including all headers (MAC, IP, TCP/UDP) - */ -static unsigned int skb_gso_mac_seglen(const struct sk_buff *skb) -{ - unsigned int hdr_len = skb_transport_header(skb) - skb_mac_header(skb); - return hdr_len + skb_gso_transport_seglen(skb); -} - /* GSO packet is too big, segment it so that tbf can transmit * each segment in time */ diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index af17b00145e1..a8ab98b53a3a 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -54,6 +54,7 @@ static void cache_init(struct cache_head *h, struct cache_detail *detail) h->last_refresh = now; } +static inline int cache_is_valid(struct cache_head *h); static void cache_fresh_locked(struct cache_head *head, time_t expiry, struct cache_detail *detail); static void cache_fresh_unlocked(struct cache_head *head, @@ -100,6 +101,8 @@ struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail, if (cache_is_expired(detail, tmp)) { hlist_del_init(&tmp->cache_list); detail->entries --; + if (cache_is_valid(tmp) == -EAGAIN) + set_bit(CACHE_NEGATIVE, &tmp->flags); cache_fresh_locked(tmp, 0, detail); freeme = tmp; break; diff --git a/net/tipc/core.c b/net/tipc/core.c index e2bdb07a49a2..c306e9bc1621 100644 --- a/net/tipc/core.c +++ b/net/tipc/core.c @@ -70,9 +70,6 @@ static int __net_init tipc_init_net(struct net *net) goto out_nametbl; INIT_LIST_HEAD(&tn->dist_queue); - err = tipc_topsrv_start(net); - if (err) - goto out_subscr; err = tipc_bcast_init(net); if (err) @@ -81,8 +78,6 @@ static int __net_init tipc_init_net(struct net *net) return 0; out_bclink: - tipc_bcast_stop(net); -out_subscr: tipc_nametbl_stop(net); out_nametbl: tipc_sk_rht_destroy(net); @@ -92,7 +87,6 @@ out_sk_rht: static void __net_exit tipc_exit_net(struct net *net) { - tipc_topsrv_stop(net); tipc_net_stop(net); tipc_bcast_stop(net); tipc_nametbl_stop(net); @@ -106,6 +100,11 @@ static struct pernet_operations tipc_net_ops = { .size = sizeof(struct tipc_net), }; +static struct pernet_operations tipc_topsrv_net_ops = { + .init = tipc_topsrv_init_net, + .exit = tipc_topsrv_exit_net, +}; + static int __init tipc_init(void) { int err; @@ -126,10 +125,6 @@ static int __init tipc_init(void) if (err) goto out_netlink_compat; - err = tipc_socket_init(); - if (err) - goto out_socket; - err = tipc_register_sysctl(); if (err) goto out_sysctl; @@ -138,6 +133,14 @@ static int __init tipc_init(void) if (err) goto out_pernet; + err = tipc_socket_init(); + if (err) + goto out_socket; + + err = register_pernet_subsys(&tipc_topsrv_net_ops); + if (err) + goto out_pernet_topsrv; + err = tipc_bearer_setup(); if (err) goto out_bearer; @@ -145,12 +148,14 @@ static int __init tipc_init(void) pr_info("Started in single node mode\n"); return 0; out_bearer: + unregister_pernet_subsys(&tipc_topsrv_net_ops); +out_pernet_topsrv: + tipc_socket_stop(); +out_socket: unregister_pernet_subsys(&tipc_net_ops); out_pernet: tipc_unregister_sysctl(); out_sysctl: - tipc_socket_stop(); -out_socket: tipc_netlink_compat_stop(); out_netlink_compat: tipc_netlink_stop(); @@ -162,10 +167,11 @@ out_netlink: static void __exit tipc_exit(void) { tipc_bearer_cleanup(); + unregister_pernet_subsys(&tipc_topsrv_net_ops); + tipc_socket_stop(); unregister_pernet_subsys(&tipc_net_ops); tipc_netlink_stop(); tipc_netlink_compat_stop(); - tipc_socket_stop(); tipc_unregister_sysctl(); pr_info("Deactivated\n"); diff --git a/net/tipc/netlink_compat.c b/net/tipc/netlink_compat.c index e9653c42cdd1..8400211537a2 100644 --- a/net/tipc/netlink_compat.c +++ b/net/tipc/netlink_compat.c @@ -262,8 +262,14 @@ static int tipc_nl_compat_dumpit(struct tipc_nl_compat_cmd_dump *cmd, if (msg->rep_type) tipc_tlv_init(msg->rep, msg->rep_type); - if (cmd->header) - (*cmd->header)(msg); + if (cmd->header) { + err = (*cmd->header)(msg); + if (err) { + kfree_skb(msg->rep); + msg->rep = NULL; + return err; + } + } arg = nlmsg_new(0, GFP_KERNEL); if (!arg) { @@ -382,7 +388,12 @@ static int tipc_nl_compat_bearer_enable(struct tipc_nl_compat_cmd_doit *cmd, if (!bearer) return -EMSGSIZE; - len = min_t(int, TLV_GET_DATA_LEN(msg->req), TIPC_MAX_BEARER_NAME); + len = TLV_GET_DATA_LEN(msg->req); + len -= offsetof(struct tipc_bearer_config, name); + if (len <= 0) + return -EINVAL; + + len = min_t(int, len, TIPC_MAX_BEARER_NAME); if (!string_is_valid(b->name, len)) return -EINVAL; @@ -727,7 +738,12 @@ static int tipc_nl_compat_link_set(struct tipc_nl_compat_cmd_doit *cmd, lc = (struct tipc_link_config *)TLV_DATA(msg->req); - len = min_t(int, TLV_GET_DATA_LEN(msg->req), TIPC_MAX_LINK_NAME); + len = TLV_GET_DATA_LEN(msg->req); + len -= offsetof(struct tipc_link_config, name); + if (len <= 0) + return -EINVAL; + + len = min_t(int, len, TIPC_MAX_LINK_NAME); if (!string_is_valid(lc->name, len)) return -EINVAL; diff --git a/net/tipc/subscr.c b/net/tipc/subscr.c index 500c9e614a06..4f80f00cd8f9 100644 --- a/net/tipc/subscr.c +++ b/net/tipc/subscr.c @@ -306,7 +306,7 @@ static void *tipc_subscrb_connect_cb(int conid) return (void *)tipc_subscrb_create(conid); } -int tipc_topsrv_start(struct net *net) +static int tipc_topsrv_start(struct net *net) { struct tipc_net *tn = net_generic(net, tipc_net_id); const char name[] = "topology_server"; @@ -344,7 +344,7 @@ int tipc_topsrv_start(struct net *net) return tipc_server_start(topsrv); } -void tipc_topsrv_stop(struct net *net) +static void tipc_topsrv_stop(struct net *net) { struct tipc_net *tn = net_generic(net, tipc_net_id); struct tipc_server *topsrv = tn->topsrv; @@ -353,3 +353,13 @@ void tipc_topsrv_stop(struct net *net) kfree(topsrv->saddr); kfree(topsrv); } + +int __net_init tipc_topsrv_init_net(struct net *net) +{ + return tipc_topsrv_start(net); +} + +void __net_exit tipc_topsrv_exit_net(struct net *net) +{ + tipc_topsrv_stop(net); +} diff --git a/net/tipc/subscr.h b/net/tipc/subscr.h index 92ee18cc5fe6..e9aa8c7a6fec 100644 --- a/net/tipc/subscr.h +++ b/net/tipc/subscr.h @@ -77,7 +77,8 @@ int tipc_subscrp_check_overlap(struct tipc_subscription *sub, u32 found_lower, void tipc_subscrp_report_overlap(struct tipc_subscription *sub, u32 found_lower, u32 found_upper, u32 event, u32 port_ref, u32 node, int must); -int tipc_topsrv_start(struct net *net); -void tipc_topsrv_stop(struct net *net); + +int __net_init tipc_topsrv_init_net(struct net *net); +void __net_exit tipc_topsrv_exit_net(struct net *net); #endif diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 9336f6e1bf60..2bac09d3ec8c 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -13475,6 +13475,11 @@ void cfg80211_ch_switch_notify(struct net_device *dev, wdev->chandef = *chandef; wdev->preset_chandef = *chandef; + + if (wdev->iftype == NL80211_IFTYPE_STATION && + !WARN_ON(!wdev->current_bss)) + wdev->current_bss->pub.channel = chandef->chan; + nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL, NL80211_CMD_CH_SWITCH_NOTIFY, 0); } diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 9d57500bbbbc..4804f29fd011 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -1348,7 +1348,7 @@ static int verify_newpolicy_info(struct xfrm_userpolicy_info *p) ret = verify_policy_dir(p->dir); if (ret) return ret; - if (p->index && ((p->index & XFRM_POLICY_MAX) != p->dir)) + if (p->index && (xfrm_policy_id2dir(p->index) != p->dir)) return -EINVAL; return 0; diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include index 92d68c355fd7..374d5460caa5 100644 --- a/scripts/Kbuild.include +++ b/scripts/Kbuild.include @@ -165,9 +165,7 @@ cc-ldoption = $(call try-run,\ # ld-option # Usage: LDFLAGS += $(call ld-option, -X) -ld-option = $(call try-run,\ - $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -x c /dev/null -c -o "$$TMPO"; \ - $(LD) $(LDFLAGS) $(1) "$$TMPO" -o "$$TMP",$(1),$(2)) +ld-option = $(call try-run, $(LD) $(LDFLAGS) $(1) -v,$(1),$(2)) # ar-option # Usage: KBUILD_ARFLAGS := $(call ar-option,D) diff --git a/scripts/coccinelle/api/stream_open.cocci b/scripts/coccinelle/api/stream_open.cocci new file mode 100644 index 000000000000..350145da7669 --- /dev/null +++ b/scripts/coccinelle/api/stream_open.cocci @@ -0,0 +1,363 @@ +// SPDX-License-Identifier: GPL-2.0 +// Author: Kirill Smelkov (kirr@nexedi.com) +// +// Search for stream-like files that are using nonseekable_open and convert +// them to stream_open. A stream-like file is a file that does not use ppos in +// its read and write. Rationale for the conversion is to avoid deadlock in +// between read and write. + +virtual report +virtual patch +virtual explain // explain decisions in the patch (SPFLAGS="-D explain") + +// stream-like reader & writer - ones that do not depend on f_pos. +@ stream_reader @ +identifier readstream, ppos; +identifier f, buf, len; +type loff_t; +@@ + ssize_t readstream(struct file *f, char *buf, size_t len, loff_t *ppos) + { + ... when != ppos + } + +@ stream_writer @ +identifier writestream, ppos; +identifier f, buf, len; +type loff_t; +@@ + ssize_t writestream(struct file *f, const char *buf, size_t len, loff_t *ppos) + { + ... when != ppos + } + + +// a function that blocks +@ blocks @ +identifier block_f; +identifier wait_event =~ "^wait_event_.*"; +@@ + block_f(...) { + ... when exists + wait_event(...) + ... when exists + } + +// stream_reader that can block inside. +// +// XXX wait_* can be called not directly from current function (e.g. func -> f -> g -> wait()) +// XXX currently reader_blocks supports only direct and 1-level indirect cases. +@ reader_blocks_direct @ +identifier stream_reader.readstream; +identifier wait_event =~ "^wait_event_.*"; +@@ + readstream(...) + { + ... when exists + wait_event(...) + ... when exists + } + +@ reader_blocks_1 @ +identifier stream_reader.readstream; +identifier blocks.block_f; +@@ + readstream(...) + { + ... when exists + block_f(...) + ... when exists + } + +@ reader_blocks depends on reader_blocks_direct || reader_blocks_1 @ +identifier stream_reader.readstream; +@@ + readstream(...) { + ... + } + + +// file_operations + whether they have _any_ .read, .write, .llseek ... at all. +// +// XXX add support for file_operations xxx[N] = ... (sound/core/pcm_native.c) +@ fops0 @ +identifier fops; +@@ + struct file_operations fops = { + ... + }; + +@ has_read @ +identifier fops0.fops; +identifier read_f; +@@ + struct file_operations fops = { + .read = read_f, + }; + +@ has_read_iter @ +identifier fops0.fops; +identifier read_iter_f; +@@ + struct file_operations fops = { + .read_iter = read_iter_f, + }; + +@ has_write @ +identifier fops0.fops; +identifier write_f; +@@ + struct file_operations fops = { + .write = write_f, + }; + +@ has_write_iter @ +identifier fops0.fops; +identifier write_iter_f; +@@ + struct file_operations fops = { + .write_iter = write_iter_f, + }; + +@ has_llseek @ +identifier fops0.fops; +identifier llseek_f; +@@ + struct file_operations fops = { + .llseek = llseek_f, + }; + +@ has_no_llseek @ +identifier fops0.fops; +@@ + struct file_operations fops = { + .llseek = no_llseek, + }; + +@ has_mmap @ +identifier fops0.fops; +identifier mmap_f; +@@ + struct file_operations fops = { + .mmap = mmap_f, + }; + +@ has_copy_file_range @ +identifier fops0.fops; +identifier copy_file_range_f; +@@ + struct file_operations fops = { + .copy_file_range = copy_file_range_f, + }; + +@ has_remap_file_range @ +identifier fops0.fops; +identifier remap_file_range_f; +@@ + struct file_operations fops = { + .remap_file_range = remap_file_range_f, + }; + +@ has_splice_read @ +identifier fops0.fops; +identifier splice_read_f; +@@ + struct file_operations fops = { + .splice_read = splice_read_f, + }; + +@ has_splice_write @ +identifier fops0.fops; +identifier splice_write_f; +@@ + struct file_operations fops = { + .splice_write = splice_write_f, + }; + + +// file_operations that is candidate for stream_open conversion - it does not +// use mmap and other methods that assume @offset access to file. +// +// XXX for simplicity require no .{read/write}_iter and no .splice_{read/write} for now. +// XXX maybe_steam.fops cannot be used in other rules - it gives "bad rule maybe_stream or bad variable fops". +@ maybe_stream depends on (!has_llseek || has_no_llseek) && !has_mmap && !has_copy_file_range && !has_remap_file_range && !has_read_iter && !has_write_iter && !has_splice_read && !has_splice_write @ +identifier fops0.fops; +@@ + struct file_operations fops = { + }; + + +// ---- conversions ---- + +// XXX .open = nonseekable_open -> .open = stream_open +// XXX .open = func -> openfunc -> nonseekable_open + +// read & write +// +// if both are used in the same file_operations together with an opener - +// under that conditions we can use stream_open instead of nonseekable_open. +@ fops_rw depends on maybe_stream @ +identifier fops0.fops, openfunc; +identifier stream_reader.readstream; +identifier stream_writer.writestream; +@@ + struct file_operations fops = { + .open = openfunc, + .read = readstream, + .write = writestream, + }; + +@ report_rw depends on report @ +identifier fops_rw.openfunc; +position p1; +@@ + openfunc(...) { + <... + nonseekable_open@p1 + ...> + } + +@ script:python depends on report && reader_blocks @ +fops << fops0.fops; +p << report_rw.p1; +@@ +coccilib.report.print_report(p[0], + "ERROR: %s: .read() can deadlock .write(); change nonseekable_open -> stream_open to fix." % (fops,)) + +@ script:python depends on report && !reader_blocks @ +fops << fops0.fops; +p << report_rw.p1; +@@ +coccilib.report.print_report(p[0], + "WARNING: %s: .read() and .write() have stream semantic; safe to change nonseekable_open -> stream_open." % (fops,)) + + +@ explain_rw_deadlocked depends on explain && reader_blocks @ +identifier fops_rw.openfunc; +@@ + openfunc(...) { + <... +- nonseekable_open ++ nonseekable_open /* read & write (was deadlock) */ + ...> + } + + +@ explain_rw_nodeadlock depends on explain && !reader_blocks @ +identifier fops_rw.openfunc; +@@ + openfunc(...) { + <... +- nonseekable_open ++ nonseekable_open /* read & write (no direct deadlock) */ + ...> + } + +@ patch_rw depends on patch @ +identifier fops_rw.openfunc; +@@ + openfunc(...) { + <... +- nonseekable_open ++ stream_open + ...> + } + + +// read, but not write +@ fops_r depends on maybe_stream && !has_write @ +identifier fops0.fops, openfunc; +identifier stream_reader.readstream; +@@ + struct file_operations fops = { + .open = openfunc, + .read = readstream, + }; + +@ report_r depends on report @ +identifier fops_r.openfunc; +position p1; +@@ + openfunc(...) { + <... + nonseekable_open@p1 + ...> + } + +@ script:python depends on report @ +fops << fops0.fops; +p << report_r.p1; +@@ +coccilib.report.print_report(p[0], + "WARNING: %s: .read() has stream semantic; safe to change nonseekable_open -> stream_open." % (fops,)) + +@ explain_r depends on explain @ +identifier fops_r.openfunc; +@@ + openfunc(...) { + <... +- nonseekable_open ++ nonseekable_open /* read only */ + ...> + } + +@ patch_r depends on patch @ +identifier fops_r.openfunc; +@@ + openfunc(...) { + <... +- nonseekable_open ++ stream_open + ...> + } + + +// write, but not read +@ fops_w depends on maybe_stream && !has_read @ +identifier fops0.fops, openfunc; +identifier stream_writer.writestream; +@@ + struct file_operations fops = { + .open = openfunc, + .write = writestream, + }; + +@ report_w depends on report @ +identifier fops_w.openfunc; +position p1; +@@ + openfunc(...) { + <... + nonseekable_open@p1 + ...> + } + +@ script:python depends on report @ +fops << fops0.fops; +p << report_w.p1; +@@ +coccilib.report.print_report(p[0], + "WARNING: %s: .write() has stream semantic; safe to change nonseekable_open -> stream_open." % (fops,)) + +@ explain_w depends on explain @ +identifier fops_w.openfunc; +@@ + openfunc(...) { + <... +- nonseekable_open ++ nonseekable_open /* write only */ + ...> + } + +@ patch_w depends on patch @ +identifier fops_w.openfunc; +@@ + openfunc(...) { + <... +- nonseekable_open ++ stream_open + ...> + } + + +// no read, no write - don't change anything diff --git a/scripts/kconfig/lxdialog/inputbox.c b/scripts/kconfig/lxdialog/inputbox.c index d58de1dc5360..510049a7bd1d 100644 --- a/scripts/kconfig/lxdialog/inputbox.c +++ b/scripts/kconfig/lxdialog/inputbox.c @@ -126,7 +126,8 @@ do_resize: case KEY_DOWN: break; case KEY_BACKSPACE: - case 127: + case 8: /* ^H */ + case 127: /* ^? */ if (pos) { wattrset(dialog, dlg.inputbox.atr); if (input_x == 0) { diff --git a/scripts/kconfig/nconf.c b/scripts/kconfig/nconf.c index d42d534a66cd..f7049e288e93 100644 --- a/scripts/kconfig/nconf.c +++ b/scripts/kconfig/nconf.c @@ -1046,7 +1046,7 @@ static int do_match(int key, struct match_state *state, int *ans) state->match_direction = FIND_NEXT_MATCH_UP; *ans = get_mext_match(state->pattern, state->match_direction); - } else if (key == KEY_BACKSPACE || key == 127) { + } else if (key == KEY_BACKSPACE || key == 8 || key == 127) { state->pattern[strlen(state->pattern)-1] = '\0'; adj_match_dir(&state->match_direction); } else diff --git a/scripts/kconfig/nconf.gui.c b/scripts/kconfig/nconf.gui.c index 4b2f44c20caf..9a65035cf787 100644 --- a/scripts/kconfig/nconf.gui.c +++ b/scripts/kconfig/nconf.gui.c @@ -439,7 +439,8 @@ int dialog_inputbox(WINDOW *main_window, case KEY_F(F_EXIT): case KEY_F(F_BACK): break; - case 127: + case 8: /* ^H */ + case 127: /* ^? */ case KEY_BACKSPACE: if (cursor_position > 0) { memmove(&result[cursor_position-1], diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index e3f8891c5d72..f7e2cd8c3e53 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -396,22 +396,44 @@ static int may_context_mount_inode_relabel(u32 sid, return rc; } -static int selinux_is_sblabel_mnt(struct super_block *sb) +static int selinux_is_genfs_special_handling(struct super_block *sb) { - struct superblock_security_struct *sbsec = sb->s_security; - - return sbsec->behavior == SECURITY_FS_USE_XATTR || - sbsec->behavior == SECURITY_FS_USE_TRANS || - sbsec->behavior == SECURITY_FS_USE_TASK || - sbsec->behavior == SECURITY_FS_USE_NATIVE || - /* Special handling. Genfs but also in-core setxattr handler */ - !strcmp(sb->s_type->name, "sysfs") || + /* Special handling. Genfs but also in-core setxattr handler */ + return !strcmp(sb->s_type->name, "sysfs") || !strcmp(sb->s_type->name, "pstore") || !strcmp(sb->s_type->name, "debugfs") || !strcmp(sb->s_type->name, "tracefs") || !strcmp(sb->s_type->name, "rootfs"); } +static int selinux_is_sblabel_mnt(struct super_block *sb) +{ + struct superblock_security_struct *sbsec = sb->s_security; + + /* + * IMPORTANT: Double-check logic in this function when adding a new + * SECURITY_FS_USE_* definition! + */ + BUILD_BUG_ON(SECURITY_FS_USE_MAX != 7); + + switch (sbsec->behavior) { + case SECURITY_FS_USE_XATTR: + case SECURITY_FS_USE_TRANS: + case SECURITY_FS_USE_TASK: + case SECURITY_FS_USE_NATIVE: + return 1; + + case SECURITY_FS_USE_GENFS: + return selinux_is_genfs_special_handling(sb); + + /* Never allow relabeling on context mounts */ + case SECURITY_FS_USE_MNTPOINT: + case SECURITY_FS_USE_NONE: + default: + return 0; + } +} + static int sb_finish_set_opts(struct super_block *sb) { struct superblock_security_struct *sbsec = sb->s_security; diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c index f04714d70bf7..a42e2ce4a726 100644 --- a/sound/core/seq/seq_ports.c +++ b/sound/core/seq/seq_ports.c @@ -550,10 +550,10 @@ static void delete_and_unsubscribe_port(struct snd_seq_client *client, list_del_init(list); grp->exclusive = 0; write_unlock_irq(&grp->list_lock); - up_write(&grp->list_mutex); if (!empty) unsubscribe_port(client, port, grp, &subs->info, ack); + up_write(&grp->list_mutex); } /* connect two ports */ diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 74c9600876d6..ef8955abd918 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1707,9 +1707,6 @@ static int azx_first_init(struct azx *chip) chip->msi = 0; } - if (azx_acquire_irq(chip, 0) < 0) - return -EBUSY; - pci_set_master(pci); synchronize_irq(bus->irq); @@ -1820,6 +1817,9 @@ static int azx_first_init(struct azx *chip) return -ENODEV; } + if (azx_acquire_irq(chip, 0) < 0) + return -EBUSY; + strcpy(card->driver, "HDA-Intel"); strlcpy(card->shortname, driver_short_names[chip->driver_type], sizeof(card->shortname)); diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index a8045b8a2a18..b249b1b85746 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1636,9 +1636,11 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) ret = !repoll || !pin_eld->monitor_present || pin_eld->eld_valid; jack = snd_hda_jack_tbl_get(codec, pin_nid); - if (jack) + if (jack) { jack->block_report = !ret; - + jack->pin_sense = (eld->monitor_present && eld->eld_valid) ? + AC_PINSENSE_PRESENCE : 0; + } mutex_unlock(&per_pin->lock); snd_hda_power_down_pm(codec); return ret; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 5d8ac2d798df..68d96c2e8cde 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -772,11 +772,10 @@ static int alc_init(struct hda_codec *codec) if (spec->init_hook) spec->init_hook(codec); + snd_hda_gen_init(codec); alc_fix_pll(codec); alc_auto_init_amp(codec, spec->init_amp); - snd_hda_gen_init(codec); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT); return 0; @@ -5779,7 +5778,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3112, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI), SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC), - SND_PCI_QUIRK(0x17aa, 0x3978, "IdeaPad Y410P", ALC269_FIXUP_NO_SHUTUP), + SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo B50-70", ALC269_FIXUP_DMIC_THINKPAD_ACPI), SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x501a, "Thinkpad", ALC283_FIXUP_INT_MIC), SND_PCI_QUIRK(0x17aa, 0x501e, "Thinkpad L440", ALC292_FIXUP_TPT440_DOCK), @@ -6237,7 +6236,7 @@ static int patch_alc269(struct hda_codec *codec) spec = codec->spec; spec->gen.shared_mic_vref_pin = 0x18; - codec->power_save_node = 1; + codec->power_save_node = 0; #ifdef CONFIG_PM codec->patch_ops.suspend = alc269_suspend; diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 3670086b9227..f273533c6653 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -641,6 +641,7 @@ static const struct regmap_config cs4270_regmap = { .reg_defaults = cs4270_reg_defaults, .num_reg_defaults = ARRAY_SIZE(cs4270_reg_defaults), .cache_type = REGCACHE_RBTREE, + .write_flag_mask = CS4270_I2C_INCR, .readable_reg = cs4270_reg_is_readable, .volatile_reg = cs4270_reg_is_volatile, diff --git a/sound/soc/codecs/cs42xx8.c b/sound/soc/codecs/cs42xx8.c index d562e1b9a5d1..5b079709ec8a 100644 --- a/sound/soc/codecs/cs42xx8.c +++ b/sound/soc/codecs/cs42xx8.c @@ -561,6 +561,7 @@ static int cs42xx8_runtime_resume(struct device *dev) msleep(5); regcache_cache_only(cs42xx8->regmap, false); + regcache_mark_dirty(cs42xx8->regmap); ret = regcache_sync(cs42xx8->regmap); if (ret) { diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index 584aab83e478..3e65dc74eb33 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -1209,14 +1209,14 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = { &max98090_right_rcv_mixer_controls[0], ARRAY_SIZE(max98090_right_rcv_mixer_controls)), - SND_SOC_DAPM_MUX("LINMOD Mux", M98090_REG_LOUTR_MIXER, - M98090_LINMOD_SHIFT, 0, &max98090_linmod_mux), + SND_SOC_DAPM_MUX("LINMOD Mux", SND_SOC_NOPM, 0, 0, + &max98090_linmod_mux), - SND_SOC_DAPM_MUX("MIXHPLSEL Mux", M98090_REG_HP_CONTROL, - M98090_MIXHPLSEL_SHIFT, 0, &max98090_mixhplsel_mux), + SND_SOC_DAPM_MUX("MIXHPLSEL Mux", SND_SOC_NOPM, 0, 0, + &max98090_mixhplsel_mux), - SND_SOC_DAPM_MUX("MIXHPRSEL Mux", M98090_REG_HP_CONTROL, - M98090_MIXHPRSEL_SHIFT, 0, &max98090_mixhprsel_mux), + SND_SOC_DAPM_MUX("MIXHPRSEL Mux", SND_SOC_NOPM, 0, 0, + &max98090_mixhprsel_mux), SND_SOC_DAPM_PGA("HP Left Out", M98090_REG_OUTPUT_ENABLE, M98090_HPLEN_SHIFT, 0, NULL, 0), diff --git a/sound/soc/codecs/rt5677-spi.c b/sound/soc/codecs/rt5677-spi.c index 91879ea95415..01aa75cde571 100644 --- a/sound/soc/codecs/rt5677-spi.c +++ b/sound/soc/codecs/rt5677-spi.c @@ -60,13 +60,15 @@ static DEFINE_MUTEX(spi_mutex); * RT5677_SPI_READ/WRITE_32: Transfer 4 bytes * RT5677_SPI_READ/WRITE_BURST: Transfer any multiples of 8 bytes * - * For example, reading 260 bytes at 0x60030002 uses the following commands: - * 0x60030002 RT5677_SPI_READ_16 2 bytes + * Note: + * 16 Bit writes and reads are restricted to the address range + * 0x18020000 ~ 0x18021000 + * + * For example, reading 256 bytes at 0x60030004 uses the following commands: * 0x60030004 RT5677_SPI_READ_32 4 bytes * 0x60030008 RT5677_SPI_READ_BURST 240 bytes * 0x600300F8 RT5677_SPI_READ_BURST 8 bytes * 0x60030100 RT5677_SPI_READ_32 4 bytes - * 0x60030104 RT5677_SPI_READ_16 2 bytes * * Input: * @read: true for read commands; false for write commands @@ -81,15 +83,13 @@ static u8 rt5677_spi_select_cmd(bool read, u32 align, u32 remain, u32 *len) { u8 cmd; - if (align == 2 || align == 6 || remain == 2) { - cmd = RT5677_SPI_READ_16; - *len = 2; - } else if (align == 4 || remain <= 6) { + if (align == 4 || remain <= 4) { cmd = RT5677_SPI_READ_32; *len = 4; } else { cmd = RT5677_SPI_READ_BURST; - *len = min_t(u32, remain & ~7, RT5677_SPI_BURST_LEN); + *len = (((remain - 1) >> 3) + 1) << 3; + *len = min_t(u32, *len, RT5677_SPI_BURST_LEN); } return read ? cmd : cmd + 1; } @@ -110,7 +110,7 @@ static void rt5677_spi_reverse(u8 *dst, u32 dstlen, const u8 *src, u32 srclen) } } -/* Read DSP address space using SPI. addr and len have to be 2-byte aligned. */ +/* Read DSP address space using SPI. addr and len have to be 4-byte aligned. */ int rt5677_spi_read(u32 addr, void *rxbuf, size_t len) { u32 offset; @@ -126,7 +126,7 @@ int rt5677_spi_read(u32 addr, void *rxbuf, size_t len) if (!g_spi) return -ENODEV; - if ((addr & 1) || (len & 1)) { + if ((addr & 3) || (len & 3)) { dev_err(&g_spi->dev, "Bad read align 0x%x(%zu)\n", addr, len); return -EACCES; } @@ -161,13 +161,13 @@ int rt5677_spi_read(u32 addr, void *rxbuf, size_t len) } EXPORT_SYMBOL_GPL(rt5677_spi_read); -/* Write DSP address space using SPI. addr has to be 2-byte aligned. - * If len is not 2-byte aligned, an extra byte of zero is written at the end +/* Write DSP address space using SPI. addr has to be 4-byte aligned. + * If len is not 4-byte aligned, then extra zeros are written at the end * as padding. */ int rt5677_spi_write(u32 addr, const void *txbuf, size_t len) { - u32 offset, len_with_pad = len; + u32 offset; int status = 0; struct spi_transfer t; struct spi_message m; @@ -180,22 +180,19 @@ int rt5677_spi_write(u32 addr, const void *txbuf, size_t len) if (!g_spi) return -ENODEV; - if (addr & 1) { + if (addr & 3) { dev_err(&g_spi->dev, "Bad write align 0x%x(%zu)\n", addr, len); return -EACCES; } - if (len & 1) - len_with_pad = len + 1; - memset(&t, 0, sizeof(t)); t.tx_buf = buf; t.speed_hz = RT5677_SPI_FREQ; spi_message_init_with_transfers(&m, &t, 1); - for (offset = 0; offset < len_with_pad;) { + for (offset = 0; offset < len;) { spi_cmd = rt5677_spi_select_cmd(false, (addr + offset) & 7, - len_with_pad - offset, &t.len); + len - offset, &t.len); /* Construct SPI message header */ buf[0] = spi_cmd; diff --git a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c index fe455c9b8c25..94122de3e758 100644 --- a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c +++ b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2019, 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 @@ -3421,8 +3421,8 @@ static const struct snd_soc_dapm_widget msm_anlg_cdc_dapm_widgets[] = { msm_anlg_cdc_hph_pa_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_PGA_E("SPK PA", SND_SOC_NOPM, - 0, 0, NULL, 0, msm_anlg_cdc_codec_enable_spk_pa, + SND_SOC_DAPM_PGA_E("SPK PA", MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, + 7, 0, NULL, 0, msm_anlg_cdc_codec_enable_spk_pa, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_PGA_E("LINEOUT PA", MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index f2d3191961e1..714bd0e3fc71 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -234,6 +234,8 @@ static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = { SND_SOC_DAPM_INPUT("IN2_R"), SND_SOC_DAPM_INPUT("IN3_L"), SND_SOC_DAPM_INPUT("IN3_R"), + SND_SOC_DAPM_INPUT("CM_L"), + SND_SOC_DAPM_INPUT("CM_R"), }; static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = { diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 2ccb8bccc9d4..512ec25c9ead 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -43,6 +43,7 @@ #define MCASP_MAX_AFIFO_DEPTH 64 +#ifdef CONFIG_PM static u32 context_regs[] = { DAVINCI_MCASP_TXFMCTL_REG, DAVINCI_MCASP_RXFMCTL_REG, @@ -65,6 +66,7 @@ struct davinci_mcasp_context { u32 *xrsr_regs; /* for serializer configuration */ bool pm_state; }; +#endif struct davinci_mcasp_ruledata { struct davinci_mcasp *mcasp; diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 3066e068aae5..d420995ed45b 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -171,16 +171,17 @@ config SND_MPC52xx_SOC_EFIKA endif # SND_POWERPC_SOC +config SND_SOC_IMX_PCM_FIQ + tristate + default y if SND_SOC_IMX_SSI=y && (SND_SOC_FSL_SSI=m || SND_SOC_FSL_SPDIF=m) && (MXC_TZIC || MXC_AVIC) + select FIQ + if SND_IMX_SOC config SND_SOC_IMX_SSI tristate select SND_SOC_FSL_UTILS -config SND_SOC_IMX_PCM_FIQ - tristate - select FIQ - comment "SoC Audio support for Freescale i.MX boards:" config SND_MXC_SOC_WM1133_EV1 diff --git a/sound/soc/fsl/eukrea-tlv320.c b/sound/soc/fsl/eukrea-tlv320.c index 883087f2b092..38132143b7d5 100644 --- a/sound/soc/fsl/eukrea-tlv320.c +++ b/sound/soc/fsl/eukrea-tlv320.c @@ -119,13 +119,13 @@ static int eukrea_tlv320_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "fsl,mux-int-port node missing or invalid.\n"); - return ret; + goto err; } ret = of_property_read_u32(np, "fsl,mux-ext-port", &ext_port); if (ret) { dev_err(&pdev->dev, "fsl,mux-ext-port node missing or invalid.\n"); - return ret; + goto err; } /* diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 08b460ba06ef..61d2d955f26a 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -260,12 +260,14 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, case SND_SOC_DAIFMT_CBS_CFS: val_cr2 |= FSL_SAI_CR2_BCD_MSTR; val_cr4 |= FSL_SAI_CR4_FSD_MSTR; + sai->is_slave_mode = false; break; case SND_SOC_DAIFMT_CBM_CFM: sai->is_slave_mode = true; break; case SND_SOC_DAIFMT_CBS_CFM: val_cr2 |= FSL_SAI_CR2_BCD_MSTR; + sai->is_slave_mode = false; break; case SND_SOC_DAIFMT_CBM_CFS: val_cr4 |= FSL_SAI_CR4_FSD_MSTR; diff --git a/sound/soc/fsl/fsl_utils.c b/sound/soc/fsl/fsl_utils.c index b9e42b503a37..4f8bdb7650e8 100644 --- a/sound/soc/fsl/fsl_utils.c +++ b/sound/soc/fsl/fsl_utils.c @@ -75,6 +75,7 @@ int fsl_asoc_get_dma_channel(struct device_node *ssi_np, iprop = of_get_property(dma_np, "cell-index", NULL); if (!iprop) { of_node_put(dma_np); + of_node_put(dma_channel_np); return -EINVAL; } *dma_id = be32_to_cpup(iprop); diff --git a/sound/soc/intel/common/sst-dsp.c b/sound/soc/intel/common/sst-dsp.c index c9452e02e0dd..c0a50ecb6dbd 100644 --- a/sound/soc/intel/common/sst-dsp.c +++ b/sound/soc/intel/common/sst-dsp.c @@ -463,11 +463,15 @@ struct sst_dsp *sst_dsp_new(struct device *dev, goto irq_err; err = sst_dma_new(sst); - if (err) - dev_warn(dev, "sst_dma_new failed %d\n", err); + if (err) { + dev_err(dev, "sst_dma_new failed %d\n", err); + goto dma_err; + } return sst; +dma_err: + free_irq(sst->irq, sst); irq_err: if (sst->ops->free) sst->ops->free(sst); diff --git a/sound/soc/msm/msm8996.c b/sound/soc/msm/msm8996.c index 49a70a7395ac..47cd28af1b84 100644 --- a/sound/soc/msm/msm8996.c +++ b/sound/soc/msm/msm8996.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2017, 2019, 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 @@ -8254,7 +8254,7 @@ static struct snd_soc_dai_link msm8996_hdmi_dai_link[] = { .stream_name = "HDMI Playback", .cpu_dai_name = "msm-dai-q6-hdmi.8", .platform_name = "msm-pcm-routing", - .codec_name = "msm-hdmi-audio-codec-rx", + .codec_name = "msm-ext-disp-audio-codec-rx", .codec_dai_name = "msm_hdmi_audio_codec_rx_dai", .no_pcm = 1, .dpcm_playback = 1, diff --git a/sound/soc/msm/msm8998.c b/sound/soc/msm/msm8998.c index 1e97a0cd76ac..1c1f65fb3f5c 100644 --- a/sound/soc/msm/msm8998.c +++ b/sound/soc/msm/msm8998.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2019, 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 @@ -72,8 +72,8 @@ #define WCN_CDC_SLIM_RX_CH_MAX 2 #define WCN_CDC_SLIM_TX_CH_MAX 3 -#define TDM_CHANNEL_MAX 8 -#define TDM_SLOT_OFFSET_MAX 8 +#define TDM_CHANNEL_MAX 16 +#define TDM_SLOT_OFFSET_MAX 32 #define MSM_HIFI_ON 1 @@ -396,6 +396,117 @@ static struct dev_config aux_pcm_tx_cfg[] = { [QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, }; +/* TDM default slot config */ +struct tdm_slot_cfg { + u32 width; + u32 num; +}; + +static struct tdm_slot_cfg tdm_slot[TDM_INTERFACE_MAX] = { + /* PRI TDM */ + {32, 8}, + /* SEC TDM */ + {32, 8}, + /* TERT TDM */ + {32, 8}, + /* QUAT TDM */ + {32, 8} +}; + +static unsigned int tdm_rx_slot_offset + [TDM_INTERFACE_MAX][TDM_PORT_MAX][TDM_SLOT_OFFSET_MAX] = { + {/* PRI TDM */ + {0, 4, 8, 12, 16, 20, 24, 28, + 32, 36, 40, 44, 48, 52, 56, 60, 0xFFFF}, + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + }, + {/* SEC TDM */ + {0, 4, 8, 12, 16, 20, 24, 28, + 32, 36, 40, 44, 48, 52, 56, 60, 0xFFFF}, + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + }, + {/* TERT TDM */ + {0, 4, 8, 12, 16, 20, 24, 28, + 32, 36, 40, 44, 48, 52, 56, 60, 0xFFFF}, + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + }, + {/* QUAT TDM */ + {0, 4, 8, 12, 16, 20, 24, 28, + 32, 36, 40, 44, 48, 52, 56, 60, 0xFFFF}, + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + } +}; + +static unsigned int tdm_tx_slot_offset + [TDM_INTERFACE_MAX][TDM_PORT_MAX][TDM_SLOT_OFFSET_MAX] = { + {/* PRI TDM */ + {0, 4, 8, 12, 16, 20, 24, 28, + 32, 36, 40, 44, 48, 52, 56, 60, 0xFFFF}, + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + }, + {/* SEC TDM */ + {0, 4, 8, 12, 16, 20, 24, 28, + 32, 36, 40, 44, 48, 52, 56, 60, 0xFFFF}, + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + }, + {/* TERT TDM */ + {0, 4, 8, 12, 16, 20, 24, 28, + 32, 36, 40, 44, 48, 52, 56, 60, 0xFFFF}, + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + }, + {/* QUAT TDM */ + {0, 4, 8, 12, 16, 20, 24, 28, + 32, 36, 40, 44, 48, 52, 56, 60, 0xFFFF},/*MIC ARR*/ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + {0xFFFF}, /* not used */ + } +}; static int msm_vi_feed_tx_ch = 2; static const char *const slim_rx_ch_text[] = {"One", "Two", "Three", "Four", "Five", "Six", "Seven", @@ -428,11 +539,17 @@ static char const *ext_disp_sample_rate_text[] = {"KHZ_48", "KHZ_96", "KHZ_192", "KHZ_32", "KHZ_44P1", "KHZ_88P2", "KHZ_176P4"}; static char const *tdm_ch_text[] = {"One", "Two", "Three", "Four", - "Five", "Six", "Seven", "Eight"}; + "Five", "Six", "Seven", "Eight", + "Nine", "Ten", "Eleven", "Twelve", + "Thirteen", "Fourteen", "Fifteen", + "Sixteen"}; static char const *tdm_bit_format_text[] = {"S16_LE", "S24_LE", "S32_LE"}; static char const *tdm_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_32", "KHZ_44P1", "KHZ_48", "KHZ_96", "KHZ_192", "KHZ_352P8", "KHZ_384"}; +static const char *const tdm_slot_num_text[] = {"One", "Two", "Four", + "Eight", "Sixteen", "ThirtyTwo"}; +static const char *const tdm_slot_width_text[] = {"16", "24", "32"}; static const char *const auxpcm_rate_text[] = {"KHZ_8", "KHZ_16"}; static char const *mi2s_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_32", "KHZ_44P1", "KHZ_48", @@ -479,6 +596,8 @@ static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_sample_rate, tdm_sample_rate_text); static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_chs, tdm_ch_text); static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_format, tdm_bit_format_text); static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_sample_rate, tdm_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_slot_num, tdm_slot_num_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_slot_width, tdm_slot_width_text); static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_rx_sample_rate, auxpcm_rate_text); static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_rx_sample_rate, auxpcm_rate_text); static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_rx_sample_rate, auxpcm_rate_text); @@ -1812,73 +1931,95 @@ static int aux_pcm_get_sample_rate_val(int sample_rate) return sample_rate_val; } +static int tdm_get_mode(struct snd_kcontrol *kcontrol) +{ + int mode; + + if (strnstr(kcontrol->id.name, "PRI", + sizeof(kcontrol->id.name))) { + mode = TDM_PRI; + } else if (strnstr(kcontrol->id.name, "SEC", + sizeof(kcontrol->id.name))) { + mode = TDM_SEC; + } else if (strnstr(kcontrol->id.name, "TERT", + sizeof(kcontrol->id.name))) { + mode = TDM_TERT; + } else if (strnstr(kcontrol->id.name, "QUAT", + sizeof(kcontrol->id.name))) { + mode = TDM_QUAT; + } else { + pr_err("%s: unsupported mode in: %s", + __func__, kcontrol->id.name); + mode = -EINVAL; + } + + return mode; +} + +static int tdm_get_channel(struct snd_kcontrol *kcontrol) +{ + int channel; + + if (strnstr(kcontrol->id.name, "RX_0", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_0", + sizeof(kcontrol->id.name))) { + channel = TDM_0; + } else if (strnstr(kcontrol->id.name, "RX_1", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_1", + sizeof(kcontrol->id.name))) { + channel = TDM_1; + } else if (strnstr(kcontrol->id.name, "RX_2", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_2", + sizeof(kcontrol->id.name))) { + channel = TDM_2; + } else if (strnstr(kcontrol->id.name, "RX_3", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_3", + sizeof(kcontrol->id.name))) { + channel = TDM_3; + } else if (strnstr(kcontrol->id.name, "RX_4", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_4", + sizeof(kcontrol->id.name))) { + channel = TDM_4; + } else if (strnstr(kcontrol->id.name, "RX_5", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_5", + sizeof(kcontrol->id.name))) { + channel = TDM_5; + } else if (strnstr(kcontrol->id.name, "RX_6", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_6", + sizeof(kcontrol->id.name))) { + channel = TDM_6; + } else if (strnstr(kcontrol->id.name, "RX_7", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_7", + sizeof(kcontrol->id.name))) { + channel = TDM_7; + } else { + pr_err("%s: unsupported channel in: %s", + __func__, kcontrol->id.name); + channel = -EINVAL; + } + + return channel; +} + static int tdm_get_port_idx(struct snd_kcontrol *kcontrol, struct tdm_port *port) { if (port) { - if (strnstr(kcontrol->id.name, "PRI", - sizeof(kcontrol->id.name))) { - port->mode = TDM_PRI; - } else if (strnstr(kcontrol->id.name, "SEC", - sizeof(kcontrol->id.name))) { - port->mode = TDM_SEC; - } else if (strnstr(kcontrol->id.name, "TERT", - sizeof(kcontrol->id.name))) { - port->mode = TDM_TERT; - } else if (strnstr(kcontrol->id.name, "QUAT", - sizeof(kcontrol->id.name))) { - port->mode = TDM_QUAT; - } else { - pr_err("%s: unsupported mode in: %s", - __func__, kcontrol->id.name); - return -EINVAL; - } + port->mode = tdm_get_mode(kcontrol); + if (port->mode < 0) + return port->mode; - if (strnstr(kcontrol->id.name, "RX_0", - sizeof(kcontrol->id.name)) || - strnstr(kcontrol->id.name, "TX_0", - sizeof(kcontrol->id.name))) { - port->channel = TDM_0; - } else if (strnstr(kcontrol->id.name, "RX_1", - sizeof(kcontrol->id.name)) || - strnstr(kcontrol->id.name, "TX_1", - sizeof(kcontrol->id.name))) { - port->channel = TDM_1; - } else if (strnstr(kcontrol->id.name, "RX_2", - sizeof(kcontrol->id.name)) || - strnstr(kcontrol->id.name, "TX_2", - sizeof(kcontrol->id.name))) { - port->channel = TDM_2; - } else if (strnstr(kcontrol->id.name, "RX_3", - sizeof(kcontrol->id.name)) || - strnstr(kcontrol->id.name, "TX_3", - sizeof(kcontrol->id.name))) { - port->channel = TDM_3; - } else if (strnstr(kcontrol->id.name, "RX_4", - sizeof(kcontrol->id.name)) || - strnstr(kcontrol->id.name, "TX_4", - sizeof(kcontrol->id.name))) { - port->channel = TDM_4; - } else if (strnstr(kcontrol->id.name, "RX_5", - sizeof(kcontrol->id.name)) || - strnstr(kcontrol->id.name, "TX_5", - sizeof(kcontrol->id.name))) { - port->channel = TDM_5; - } else if (strnstr(kcontrol->id.name, "RX_6", - sizeof(kcontrol->id.name)) || - strnstr(kcontrol->id.name, "TX_6", - sizeof(kcontrol->id.name))) { - port->channel = TDM_6; - } else if (strnstr(kcontrol->id.name, "RX_7", - sizeof(kcontrol->id.name)) || - strnstr(kcontrol->id.name, "TX_7", - sizeof(kcontrol->id.name))) { - port->channel = TDM_7; - } else { - pr_err("%s: unsupported channel in: %s", - __func__, kcontrol->id.name); - return -EINVAL; - } + port->channel = tdm_get_channel(kcontrol); + if (port->channel < 0) + return port->channel; } else return -EINVAL; return 0; @@ -2167,6 +2308,316 @@ static int tdm_tx_ch_put(struct snd_kcontrol *kcontrol, return ret; } +static int tdm_get_slot_num_val(int slot_num) +{ + int slot_num_val; + + switch (slot_num) { + case 1: + slot_num_val = 0; + break; + case 2: + slot_num_val = 1; + break; + case 4: + slot_num_val = 2; + break; + case 8: + slot_num_val = 3; + break; + case 16: + slot_num_val = 4; + break; + case 32: + slot_num_val = 5; + break; + default: + slot_num_val = 5; + break; + } + return slot_num_val; +} + +static int tdm_slot_num_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int mode = tdm_get_mode(kcontrol); + + if (mode < 0) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + return mode; + } + + ucontrol->value.enumerated.item[0] = + tdm_get_slot_num_val(tdm_slot[mode].num); + + pr_debug("%s: mode = %d, tdm_slot_num = %d, item = %d\n", __func__, + mode, tdm_slot[mode].num, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int tdm_get_slot_num(int value) +{ + int slot_num; + + switch (value) { + case 0: + slot_num = 1; + break; + case 1: + slot_num = 2; + break; + case 2: + slot_num = 4; + break; + case 3: + slot_num = 8; + break; + case 4: + slot_num = 16; + break; + case 5: + slot_num = 32; + break; + default: + slot_num = 8; + break; + } + return slot_num; +} + +static int tdm_slot_num_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int mode = tdm_get_mode(kcontrol); + + if (mode < 0) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + return mode; + } + + tdm_slot[mode].num = + tdm_get_slot_num(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: mode = %d, tdm_slot_num = %d, item = %d\n", __func__, + mode, tdm_slot[mode].num, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int tdm_get_slot_width_val(int slot_width) +{ + int slot_width_val; + + switch (slot_width) { + case 16: + slot_width_val = 0; + break; + case 24: + slot_width_val = 1; + break; + case 32: + slot_width_val = 2; + break; + default: + slot_width_val = 2; + break; + } + return slot_width_val; +} + +static int tdm_slot_width_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int mode = tdm_get_mode(kcontrol); + + if (mode < 0) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + return mode; + } + + ucontrol->value.enumerated.item[0] = + tdm_get_slot_width_val(tdm_slot[mode].width); + + pr_debug("%s: mode = %d, tdm_slot_width = %d, item = %d\n", __func__, + mode, tdm_slot[mode].width, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int tdm_get_slot_width(int value) +{ + int slot_width; + + switch (value) { + case 0: + slot_width = 16; + break; + case 1: + slot_width = 24; + break; + case 2: + slot_width = 32; + break; + default: + slot_width = 32; + break; + } + return slot_width; +} + +static int tdm_slot_width_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int mode = tdm_get_mode(kcontrol); + + if (mode < 0) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + return mode; + } + + tdm_slot[mode].width = + tdm_get_slot_width(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: mode = %d, tdm_slot_width = %d, item = %d\n", __func__, + mode, tdm_slot[mode].width, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int tdm_rx_slot_mapping_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int *slot_offset; + int i; + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + if (port.mode < TDM_INTERFACE_MAX && + port.channel < TDM_PORT_MAX) { + slot_offset = + tdm_rx_slot_offset[port.mode][port.channel]; + pr_debug("%s: mode = %d, channel = %d\n", + __func__, port.mode, port.channel); + for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) { + ucontrol->value.integer.value[i] = + slot_offset[i]; + pr_debug("%s: offset %d, value %d\n", + __func__, i, slot_offset[i]); + } + } else { + pr_err("%s: unsupported mode/channel", __func__); + } + } + return ret; +} + +static int tdm_rx_slot_mapping_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int *slot_offset; + int i; + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + if (port.mode < TDM_INTERFACE_MAX && + port.channel < TDM_PORT_MAX) { + slot_offset = + tdm_rx_slot_offset[port.mode][port.channel]; + pr_debug("%s: mode = %d, channel = %d\n", + __func__, port.mode, port.channel); + for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) { + slot_offset[i] = + ucontrol->value.integer.value[i]; + pr_debug("%s: offset %d, value %d\n", + __func__, i, slot_offset[i]); + } + } else { + pr_err("%s: unsupported mode/channel", __func__); + } + } + return ret; +} + +static int tdm_tx_slot_mapping_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int *slot_offset; + int i; + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + if (port.mode < TDM_INTERFACE_MAX && + port.channel < TDM_PORT_MAX) { + slot_offset = + tdm_tx_slot_offset[port.mode][port.channel]; + pr_debug("%s: mode = %d, channel = %d\n", + __func__, port.mode, port.channel); + for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) { + ucontrol->value.integer.value[i] = + slot_offset[i]; + pr_debug("%s: offset %d, value %d\n", + __func__, i, slot_offset[i]); + } + } else { + pr_err("%s: unsupported mode/channel", __func__); + } + } + return ret; +} + +static int tdm_tx_slot_mapping_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int *slot_offset; + int i; + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + if (port.mode < TDM_INTERFACE_MAX && + port.channel < TDM_PORT_MAX) { + slot_offset = + tdm_tx_slot_offset[port.mode][port.channel]; + pr_debug("%s: mode = %d, channel = %d\n", + __func__, port.mode, port.channel); + for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) { + slot_offset[i] = + ucontrol->value.integer.value[i]; + pr_debug("%s: offset %d, value %d\n", + __func__, i, slot_offset[i]); + } + } else { + pr_err("%s: unsupported mode/channel", __func__); + } + } + return ret; +} + static int aux_pcm_get_port_idx(struct snd_kcontrol *kcontrol) { int idx; @@ -2825,6 +3276,214 @@ static const struct snd_kcontrol_new msm_snd_controls[] = { SOC_ENUM_EXT("QUAT_TDM_TX_0 Channels", tdm_tx_chs, tdm_tx_ch_get, tdm_tx_ch_put), + SOC_ENUM_EXT("PRI_TDM SlotNumber", tdm_slot_num, + tdm_slot_num_get, tdm_slot_num_put), + SOC_ENUM_EXT("PRI_TDM SlotWidth", tdm_slot_width, + tdm_slot_width_get, tdm_slot_width_put), + SOC_ENUM_EXT("SEC_TDM SlotNumber", tdm_slot_num, + tdm_slot_num_get, tdm_slot_num_put), + SOC_ENUM_EXT("SEC_TDM SlotWidth", tdm_slot_width, + tdm_slot_width_get, tdm_slot_width_put), + SOC_ENUM_EXT("TERT_TDM SlotNumber", tdm_slot_num, + tdm_slot_num_get, tdm_slot_num_put), + SOC_ENUM_EXT("TERT_TDM SlotWidth", tdm_slot_width, + tdm_slot_width_get, tdm_slot_width_put), + SOC_ENUM_EXT("QUAT_TDM SlotNumber", tdm_slot_num, + tdm_slot_num_get, tdm_slot_num_put), + SOC_ENUM_EXT("QUAT_TDM SlotWidth", tdm_slot_width, + tdm_slot_width_get, tdm_slot_width_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_0 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_1 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_2 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_3 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_4 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_5 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_6 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_7 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_0 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_1 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_2 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_3 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_4 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_5 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_6 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_7 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_0 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_1 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_2 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_3 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_4 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_5 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_6 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_7 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_0 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_1 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_2 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_3 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_4 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_5 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_6 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_7 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_0 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_1 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_2 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_3 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_4 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_5 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_6 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_7 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_0 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_1 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_2 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_3 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_4 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_5 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_6 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_7 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_0 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_1 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_2 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_3 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_4 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_5 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_6 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_7 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_0 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_1 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_2 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_3 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_4 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_5 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_6 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_7 SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), SOC_ENUM_EXT("PRIM_AUX_PCM_RX SampleRate", prim_aux_pcm_rx_sample_rate, aux_pcm_rx_sample_rate_get, aux_pcm_rx_sample_rate_put), @@ -4478,21 +5137,520 @@ static int msm_tdm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - - if (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX) { + switch (cpu_dai->id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + channels->min = channels->max = + tdm_rx_cfg[TDM_PRI][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_PRI][TDM_0].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_PRI][TDM_0].sample_rate; + break; + case AFE_PORT_ID_PRIMARY_TDM_RX_1: + channels->min = channels->max = + tdm_rx_cfg[TDM_PRI][TDM_1].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_PRI][TDM_1].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_PRI][TDM_1].sample_rate; + break; + case AFE_PORT_ID_PRIMARY_TDM_RX_2: + channels->min = channels->max = + tdm_rx_cfg[TDM_PRI][TDM_2].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_PRI][TDM_2].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_PRI][TDM_2].sample_rate; + break; + case AFE_PORT_ID_PRIMARY_TDM_RX_3: + channels->min = channels->max = + tdm_rx_cfg[TDM_PRI][TDM_3].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_PRI][TDM_3].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_PRI][TDM_3].sample_rate; + break; + case AFE_PORT_ID_PRIMARY_TDM_RX_4: + channels->min = channels->max = + tdm_rx_cfg[TDM_PRI][TDM_4].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_PRI][TDM_4].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_PRI][TDM_4].sample_rate; + break; + case AFE_PORT_ID_PRIMARY_TDM_RX_5: + channels->min = channels->max = + tdm_rx_cfg[TDM_PRI][TDM_5].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_PRI][TDM_5].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_PRI][TDM_5].sample_rate; + break; + case AFE_PORT_ID_PRIMARY_TDM_RX_6: + channels->min = channels->max = + tdm_rx_cfg[TDM_PRI][TDM_6].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_PRI][TDM_6].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_PRI][TDM_6].sample_rate; + break; + case AFE_PORT_ID_PRIMARY_TDM_RX_7: + channels->min = channels->max = + tdm_rx_cfg[TDM_PRI][TDM_7].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_PRI][TDM_7].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_PRI][TDM_7].sample_rate; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX: + channels->min = channels->max = + tdm_tx_cfg[TDM_PRI][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_PRI][TDM_0].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_PRI][TDM_0].sample_rate; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX_1: + channels->min = channels->max = + tdm_tx_cfg[TDM_PRI][TDM_1].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_PRI][TDM_1].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_PRI][TDM_1].sample_rate; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX_2: + channels->min = channels->max = + tdm_tx_cfg[TDM_PRI][TDM_2].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_PRI][TDM_2].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_PRI][TDM_2].sample_rate; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX_3: + channels->min = channels->max = + tdm_tx_cfg[TDM_PRI][TDM_3].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_PRI][TDM_3].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_PRI][TDM_3].sample_rate; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX_4: + channels->min = channels->max = + tdm_tx_cfg[TDM_PRI][TDM_4].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_PRI][TDM_4].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_PRI][TDM_4].sample_rate; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX_5: + channels->min = channels->max = + tdm_tx_cfg[TDM_PRI][TDM_5].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_PRI][TDM_5].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_PRI][TDM_5].sample_rate; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX_6: + channels->min = channels->max = + tdm_tx_cfg[TDM_PRI][TDM_6].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_PRI][TDM_6].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_PRI][TDM_6].sample_rate; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX_7: + channels->min = channels->max = + tdm_tx_cfg[TDM_PRI][TDM_7].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_PRI][TDM_7].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_PRI][TDM_7].sample_rate; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX: + channels->min = channels->max = + tdm_rx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_SEC][TDM_0].sample_rate; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX_1: + channels->min = channels->max = + tdm_rx_cfg[TDM_SEC][TDM_1].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEC][TDM_1].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_SEC][TDM_1].sample_rate; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX_2: + channels->min = channels->max = + tdm_rx_cfg[TDM_SEC][TDM_2].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEC][TDM_2].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_SEC][TDM_2].sample_rate; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX_3: + channels->min = channels->max = + tdm_rx_cfg[TDM_SEC][TDM_3].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEC][TDM_3].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_SEC][TDM_3].sample_rate; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX_4: + channels->min = channels->max = + tdm_rx_cfg[TDM_SEC][TDM_4].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEC][TDM_4].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_SEC][TDM_4].sample_rate; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX_5: + channels->min = channels->max = + tdm_rx_cfg[TDM_SEC][TDM_5].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEC][TDM_5].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_SEC][TDM_5].sample_rate; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX_6: + channels->min = channels->max = + tdm_rx_cfg[TDM_SEC][TDM_6].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEC][TDM_6].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_SEC][TDM_6].sample_rate; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX_7: + channels->min = channels->max = + tdm_rx_cfg[TDM_SEC][TDM_7].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEC][TDM_7].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_SEC][TDM_7].sample_rate; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX: + channels->min = channels->max = + tdm_tx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_SEC][TDM_0].sample_rate; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX_1: + channels->min = channels->max = + tdm_tx_cfg[TDM_SEC][TDM_1].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_SEC][TDM_1].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_SEC][TDM_1].sample_rate; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX_2: + channels->min = channels->max = + tdm_tx_cfg[TDM_SEC][TDM_2].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_SEC][TDM_2].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_SEC][TDM_2].sample_rate; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX_3: + channels->min = channels->max = + tdm_tx_cfg[TDM_SEC][TDM_3].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_SEC][TDM_3].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_SEC][TDM_3].sample_rate; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX_4: + channels->min = channels->max = + tdm_tx_cfg[TDM_SEC][TDM_4].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_SEC][TDM_4].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_SEC][TDM_4].sample_rate; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX_5: + channels->min = channels->max = + tdm_tx_cfg[TDM_SEC][TDM_5].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_SEC][TDM_5].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_SEC][TDM_5].sample_rate; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX_6: + channels->min = channels->max = + tdm_tx_cfg[TDM_SEC][TDM_6].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_SEC][TDM_6].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_SEC][TDM_6].sample_rate; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX_7: + channels->min = channels->max = + tdm_tx_cfg[TDM_SEC][TDM_7].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_SEC][TDM_7].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_SEC][TDM_7].sample_rate; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX: + channels->min = channels->max = + tdm_rx_cfg[TDM_TERT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_TERT][TDM_0].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_TERT][TDM_0].sample_rate; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX_1: + channels->min = channels->max = + tdm_rx_cfg[TDM_TERT][TDM_1].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_TERT][TDM_1].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_TERT][TDM_1].sample_rate; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX_2: + channels->min = channels->max = + tdm_rx_cfg[TDM_TERT][TDM_2].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_TERT][TDM_2].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_TERT][TDM_2].sample_rate; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX_3: + channels->min = channels->max = + tdm_rx_cfg[TDM_TERT][TDM_3].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_TERT][TDM_3].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_TERT][TDM_3].sample_rate; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX_4: + channels->min = channels->max = + tdm_rx_cfg[TDM_TERT][TDM_4].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_TERT][TDM_4].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_TERT][TDM_4].sample_rate; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX_5: + channels->min = channels->max = + tdm_rx_cfg[TDM_TERT][TDM_5].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_TERT][TDM_5].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_TERT][TDM_5].sample_rate; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX_6: + channels->min = channels->max = + tdm_rx_cfg[TDM_TERT][TDM_6].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_TERT][TDM_6].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_TERT][TDM_6].sample_rate; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX_7: + channels->min = channels->max = + tdm_rx_cfg[TDM_TERT][TDM_7].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_TERT][TDM_7].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_TERT][TDM_7].sample_rate; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX: + channels->min = channels->max = + tdm_tx_cfg[TDM_TERT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_TERT][TDM_0].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_TERT][TDM_0].sample_rate; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX_1: + channels->min = channels->max = + tdm_tx_cfg[TDM_TERT][TDM_1].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_TERT][TDM_1].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_TERT][TDM_1].sample_rate; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX_2: + channels->min = channels->max = + tdm_tx_cfg[TDM_TERT][TDM_2].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_TERT][TDM_2].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_TERT][TDM_2].sample_rate; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX_3: + channels->min = channels->max = + tdm_tx_cfg[TDM_TERT][TDM_3].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_TERT][TDM_3].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_TERT][TDM_3].sample_rate; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX_4: + channels->min = channels->max = + tdm_tx_cfg[TDM_TERT][TDM_4].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_TERT][TDM_4].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_TERT][TDM_4].sample_rate; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX_5: + channels->min = channels->max = + tdm_tx_cfg[TDM_TERT][TDM_5].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_TERT][TDM_5].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_TERT][TDM_5].sample_rate; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX_6: + channels->min = channels->max = + tdm_tx_cfg[TDM_TERT][TDM_6].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_TERT][TDM_6].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_TERT][TDM_6].sample_rate; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX_7: + channels->min = channels->max = + tdm_tx_cfg[TDM_TERT][TDM_7].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_TERT][TDM_7].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_TERT][TDM_7].sample_rate; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX: channels->min = channels->max = tdm_rx_cfg[TDM_QUAT][TDM_0].channels; param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, tdm_rx_cfg[TDM_QUAT][TDM_0].bit_format); rate->min = rate->max = tdm_rx_cfg[TDM_QUAT][TDM_0].sample_rate; - } else if (cpu_dai->id == AFE_PORT_ID_SECONDARY_TDM_RX) { + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX_1: channels->min = channels->max = - tdm_rx_cfg[TDM_SEC][TDM_0].channels; + tdm_rx_cfg[TDM_QUAT][TDM_1].channels; param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, - tdm_rx_cfg[TDM_SEC][TDM_0].bit_format); - rate->min = rate->max = tdm_rx_cfg[TDM_SEC][TDM_0].sample_rate; - } else { + tdm_rx_cfg[TDM_QUAT][TDM_1].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_QUAT][TDM_1].sample_rate; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX_2: + channels->min = channels->max = + tdm_rx_cfg[TDM_QUAT][TDM_2].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUAT][TDM_2].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_QUAT][TDM_2].sample_rate; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX_3: + channels->min = channels->max = + tdm_rx_cfg[TDM_QUAT][TDM_3].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUAT][TDM_3].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_QUAT][TDM_3].sample_rate; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX_4: + channels->min = channels->max = + tdm_rx_cfg[TDM_QUAT][TDM_4].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUAT][TDM_4].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_QUAT][TDM_4].sample_rate; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX_5: + channels->min = channels->max = + tdm_rx_cfg[TDM_QUAT][TDM_5].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUAT][TDM_5].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_QUAT][TDM_5].sample_rate; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX_6: + channels->min = channels->max = + tdm_rx_cfg[TDM_QUAT][TDM_6].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUAT][TDM_6].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_QUAT][TDM_6].sample_rate; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX_7: + channels->min = channels->max = + tdm_rx_cfg[TDM_QUAT][TDM_7].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUAT][TDM_7].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_QUAT][TDM_7].sample_rate; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_QUAT][TDM_0].sample_rate; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX_1: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUAT][TDM_1].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUAT][TDM_1].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_QUAT][TDM_1].sample_rate; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX_2: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUAT][TDM_2].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUAT][TDM_2].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_QUAT][TDM_2].sample_rate; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX_3: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUAT][TDM_3].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUAT][TDM_3].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_QUAT][TDM_3].sample_rate; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX_4: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUAT][TDM_4].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUAT][TDM_4].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_QUAT][TDM_4].sample_rate; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX_5: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUAT][TDM_5].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUAT][TDM_5].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_QUAT][TDM_5].sample_rate; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX_6: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUAT][TDM_6].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUAT][TDM_6].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_QUAT][TDM_6].sample_rate; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX_7: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUAT][TDM_7].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUAT][TDM_7].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_QUAT][TDM_7].sample_rate; + break; + default: pr_err("%s: dai id 0x%x not supported\n", __func__, cpu_dai->id); return -EINVAL; @@ -4505,25 +5663,411 @@ static int msm_tdm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } +static unsigned int tdm_param_set_slot_mask(int slots) +{ + unsigned int slot_mask = 0; + int i = 0; + + if ((slots <= 0) || (slots > 32)) { + pr_err("%s: invalid slot number %d\n", __func__, slots); + return -EINVAL; + } + + for (i = 0; i < slots ; i++) + slot_mask |= 1 << i; + + return slot_mask; +} + static int msm8998_tdm_snd_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; int ret = 0; - int channels, slot_width, slots; + int channels, slot_width, slots, rate, format; unsigned int slot_mask; - unsigned int slot_offset[8] = {0, 4, 8, 12, 16, 20, 24, 28}; + unsigned int *slot_offset; + int offset_channels = 0; + int i; + int clk_freq; pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id); - slots = tdm_rx_cfg[TDM_QUAT][TDM_0].channels; - /*2 slot config - bits 0 and 1 set for the first two slots */ - slot_mask = 0x0000FFFF >> (16-slots); - slot_width = 32; - channels = slots; + channels = params_channels(params); + if (channels < 1 || channels > 32) { + pr_err("%s: invalid param channels %d\n", + __func__, channels); + return -EINVAL; + } + + format = params_format(params); + if (format != SNDRV_PCM_FORMAT_S32_LE && + format != SNDRV_PCM_FORMAT_S24_LE && + format != SNDRV_PCM_FORMAT_S16_LE) { + /* + * Up to 8 channel HW configuration should + * use 32 bit slot width for max support of + * stream bit width. (slot_width > bit_width) + */ + pr_err("%s: invalid param format 0x%x\n", + __func__, format); + return -EINVAL; + } + + switch (cpu_dai->id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + slots = tdm_slot[TDM_PRI].num; + slot_width = tdm_slot[TDM_PRI].width; + slot_offset = tdm_rx_slot_offset[TDM_PRI][TDM_0]; + break; + case AFE_PORT_ID_PRIMARY_TDM_RX_1: + slots = tdm_slot[TDM_PRI].num; + slot_width = tdm_slot[TDM_PRI].width; + slot_offset = tdm_rx_slot_offset[TDM_PRI][TDM_1]; + break; + case AFE_PORT_ID_PRIMARY_TDM_RX_2: + slots = tdm_slot[TDM_PRI].num; + slot_width = tdm_slot[TDM_PRI].width; + slot_offset = tdm_rx_slot_offset[TDM_PRI][TDM_2]; + break; + case AFE_PORT_ID_PRIMARY_TDM_RX_3: + slots = tdm_slot[TDM_PRI].num; + slot_width = tdm_slot[TDM_PRI].width; + slot_offset = tdm_rx_slot_offset[TDM_PRI][TDM_3]; + break; + case AFE_PORT_ID_PRIMARY_TDM_RX_4: + slots = tdm_slot[TDM_PRI].num; + slot_width = tdm_slot[TDM_PRI].width; + slot_offset = tdm_rx_slot_offset[TDM_PRI][TDM_4]; + break; + case AFE_PORT_ID_PRIMARY_TDM_RX_5: + slots = tdm_slot[TDM_PRI].num; + slot_width = tdm_slot[TDM_PRI].width; + slot_offset = tdm_rx_slot_offset[TDM_PRI][TDM_5]; + break; + case AFE_PORT_ID_PRIMARY_TDM_RX_6: + slots = tdm_slot[TDM_PRI].num; + slot_width = tdm_slot[TDM_PRI].width; + slot_offset = tdm_rx_slot_offset[TDM_PRI][TDM_6]; + break; + case AFE_PORT_ID_PRIMARY_TDM_RX_7: + slots = tdm_slot[TDM_PRI].num; + slot_width = tdm_slot[TDM_PRI].width; + slot_offset = tdm_rx_slot_offset[TDM_PRI][TDM_7]; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX: + slots = tdm_slot[TDM_PRI].num; + slot_width = tdm_slot[TDM_PRI].width; + slot_offset = tdm_tx_slot_offset[TDM_PRI][TDM_0]; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX_1: + slots = tdm_slot[TDM_PRI].num; + slot_width = tdm_slot[TDM_PRI].width; + slot_offset = tdm_tx_slot_offset[TDM_PRI][TDM_1]; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX_2: + slots = tdm_slot[TDM_PRI].num; + slot_width = tdm_slot[TDM_PRI].width; + slot_offset = tdm_tx_slot_offset[TDM_PRI][TDM_2]; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX_3: + slots = tdm_slot[TDM_PRI].num; + slot_width = tdm_slot[TDM_PRI].width; + slot_offset = tdm_tx_slot_offset[TDM_PRI][TDM_3]; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX_4: + slots = tdm_slot[TDM_PRI].num; + slot_width = tdm_slot[TDM_PRI].width; + slot_offset = tdm_tx_slot_offset[TDM_PRI][TDM_4]; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX_5: + slots = tdm_slot[TDM_PRI].num; + slot_width = tdm_slot[TDM_PRI].width; + slot_offset = tdm_tx_slot_offset[TDM_PRI][TDM_5]; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX_6: + slots = tdm_slot[TDM_PRI].num; + slot_width = tdm_slot[TDM_PRI].width; + slot_offset = tdm_tx_slot_offset[TDM_PRI][TDM_6]; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX_7: + slots = tdm_slot[TDM_PRI].num; + slot_width = tdm_slot[TDM_PRI].width; + slot_offset = tdm_tx_slot_offset[TDM_PRI][TDM_7]; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX: + slots = tdm_slot[TDM_SEC].num; + slot_width = tdm_slot[TDM_SEC].width; + slot_offset = tdm_rx_slot_offset[TDM_SEC][TDM_0]; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX_1: + slots = tdm_slot[TDM_SEC].num; + slot_width = tdm_slot[TDM_SEC].width; + slot_offset = tdm_rx_slot_offset[TDM_SEC][TDM_1]; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX_2: + slots = tdm_slot[TDM_SEC].num; + slot_width = tdm_slot[TDM_SEC].width; + slot_offset = tdm_rx_slot_offset[TDM_SEC][TDM_2]; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX_3: + slots = tdm_slot[TDM_SEC].num; + slot_width = tdm_slot[TDM_SEC].width; + slot_offset = tdm_rx_slot_offset[TDM_SEC][TDM_3]; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX_4: + slots = tdm_slot[TDM_SEC].num; + slot_width = tdm_slot[TDM_SEC].width; + slot_offset = tdm_rx_slot_offset[TDM_SEC][TDM_4]; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX_5: + slots = tdm_slot[TDM_SEC].num; + slot_width = tdm_slot[TDM_SEC].width; + slot_offset = tdm_rx_slot_offset[TDM_SEC][TDM_5]; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX_6: + slots = tdm_slot[TDM_SEC].num; + slot_width = tdm_slot[TDM_SEC].width; + slot_offset = tdm_rx_slot_offset[TDM_SEC][TDM_6]; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX_7: + slots = tdm_slot[TDM_SEC].num; + slot_width = tdm_slot[TDM_SEC].width; + slot_offset = tdm_rx_slot_offset[TDM_SEC][TDM_7]; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX: + slots = tdm_slot[TDM_SEC].num; + slot_width = tdm_slot[TDM_SEC].width; + slot_offset = tdm_tx_slot_offset[TDM_SEC][TDM_0]; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX_1: + slots = tdm_slot[TDM_SEC].num; + slot_width = tdm_slot[TDM_SEC].width; + slot_offset = tdm_tx_slot_offset[TDM_SEC][TDM_1]; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX_2: + slots = tdm_slot[TDM_SEC].num; + slot_width = tdm_slot[TDM_SEC].width; + slot_offset = tdm_tx_slot_offset[TDM_SEC][TDM_2]; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX_3: + slots = tdm_slot[TDM_SEC].num; + slot_width = tdm_slot[TDM_SEC].width; + slot_offset = tdm_tx_slot_offset[TDM_SEC][TDM_3]; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX_4: + slots = tdm_slot[TDM_SEC].num; + slot_width = tdm_slot[TDM_SEC].width; + slot_offset = tdm_tx_slot_offset[TDM_SEC][TDM_4]; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX_5: + slots = tdm_slot[TDM_SEC].num; + slot_width = tdm_slot[TDM_SEC].width; + slot_offset = tdm_tx_slot_offset[TDM_SEC][TDM_5]; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX_6: + slots = tdm_slot[TDM_SEC].num; + slot_width = tdm_slot[TDM_SEC].width; + slot_offset = tdm_tx_slot_offset[TDM_SEC][TDM_6]; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX_7: + slots = tdm_slot[TDM_SEC].num; + slot_width = tdm_slot[TDM_SEC].width; + slot_offset = tdm_tx_slot_offset[TDM_SEC][TDM_7]; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX: + slots = tdm_slot[TDM_TERT].num; + slot_width = tdm_slot[TDM_TERT].width; + slot_offset = tdm_rx_slot_offset[TDM_TERT][TDM_0]; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX_1: + slots = tdm_slot[TDM_TERT].num; + slot_width = tdm_slot[TDM_TERT].width; + slot_offset = tdm_rx_slot_offset[TDM_TERT][TDM_1]; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX_2: + slots = tdm_slot[TDM_TERT].num; + slot_width = tdm_slot[TDM_TERT].width; + slot_offset = tdm_rx_slot_offset[TDM_TERT][TDM_2]; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX_3: + slots = tdm_slot[TDM_TERT].num; + slot_width = tdm_slot[TDM_TERT].width; + slot_offset = tdm_rx_slot_offset[TDM_TERT][TDM_3]; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX_4: + slots = tdm_slot[TDM_TERT].num; + slot_width = tdm_slot[TDM_TERT].width; + slot_offset = tdm_rx_slot_offset[TDM_TERT][TDM_4]; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX_5: + slots = tdm_slot[TDM_TERT].num; + slot_width = tdm_slot[TDM_TERT].width; + slot_offset = tdm_rx_slot_offset[TDM_TERT][TDM_5]; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX_6: + slots = tdm_slot[TDM_TERT].num; + slot_width = tdm_slot[TDM_TERT].width; + slot_offset = tdm_rx_slot_offset[TDM_TERT][TDM_6]; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX_7: + slots = tdm_slot[TDM_TERT].num; + slot_width = tdm_slot[TDM_TERT].width; + slot_offset = tdm_rx_slot_offset[TDM_TERT][TDM_7]; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX: + slots = tdm_slot[TDM_TERT].num; + slot_width = tdm_slot[TDM_TERT].width; + slot_offset = tdm_tx_slot_offset[TDM_TERT][TDM_0]; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX_1: + slots = tdm_slot[TDM_TERT].num; + slot_width = tdm_slot[TDM_TERT].width; + slot_offset = tdm_tx_slot_offset[TDM_TERT][TDM_1]; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX_2: + slots = tdm_slot[TDM_TERT].num; + slot_width = tdm_slot[TDM_TERT].width; + slot_offset = tdm_tx_slot_offset[TDM_TERT][TDM_2]; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX_3: + slots = tdm_slot[TDM_TERT].num; + slot_width = tdm_slot[TDM_TERT].width; + slot_offset = tdm_tx_slot_offset[TDM_TERT][TDM_3]; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX_4: + slots = tdm_slot[TDM_TERT].num; + slot_width = tdm_slot[TDM_TERT].width; + slot_offset = tdm_tx_slot_offset[TDM_TERT][TDM_4]; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX_5: + slots = tdm_slot[TDM_TERT].num; + slot_width = tdm_slot[TDM_TERT].width; + slot_offset = tdm_tx_slot_offset[TDM_TERT][TDM_5]; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX_6: + slots = tdm_slot[TDM_TERT].num; + slot_width = tdm_slot[TDM_TERT].width; + slot_offset = tdm_tx_slot_offset[TDM_TERT][TDM_6]; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX_7: + slots = tdm_slot[TDM_TERT].num; + slot_width = tdm_slot[TDM_TERT].width; + slot_offset = tdm_tx_slot_offset[TDM_TERT][TDM_7]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX: + slots = tdm_slot[TDM_QUAT].num; + slot_width = tdm_slot[TDM_QUAT].width; + slot_offset = tdm_rx_slot_offset[TDM_QUAT][TDM_0]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX_1: + slots = tdm_slot[TDM_QUAT].num; + slot_width = tdm_slot[TDM_QUAT].width; + slot_offset = tdm_rx_slot_offset[TDM_QUAT][TDM_1]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX_2: + slots = tdm_slot[TDM_QUAT].num; + slot_width = tdm_slot[TDM_QUAT].width; + slot_offset = tdm_rx_slot_offset[TDM_QUAT][TDM_2]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX_3: + slots = tdm_slot[TDM_QUAT].num; + slot_width = tdm_slot[TDM_QUAT].width; + slot_offset = tdm_rx_slot_offset[TDM_QUAT][TDM_3]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX_4: + slots = tdm_slot[TDM_QUAT].num; + slot_width = tdm_slot[TDM_QUAT].width; + slot_offset = tdm_rx_slot_offset[TDM_QUAT][TDM_4]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX_5: + slots = tdm_slot[TDM_QUAT].num; + slot_width = tdm_slot[TDM_QUAT].width; + slot_offset = tdm_rx_slot_offset[TDM_QUAT][TDM_5]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX_6: + slots = tdm_slot[TDM_QUAT].num; + slot_width = tdm_slot[TDM_QUAT].width; + slot_offset = tdm_rx_slot_offset[TDM_QUAT][TDM_6]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX_7: + slots = tdm_slot[TDM_QUAT].num; + slot_width = tdm_slot[TDM_QUAT].width; + slot_offset = tdm_rx_slot_offset[TDM_QUAT][TDM_7]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX: + slots = tdm_slot[TDM_QUAT].num; + slot_width = tdm_slot[TDM_QUAT].width; + slot_offset = tdm_tx_slot_offset[TDM_QUAT][TDM_0]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX_1: + slots = tdm_slot[TDM_QUAT].num; + slot_width = tdm_slot[TDM_QUAT].width; + slot_offset = tdm_tx_slot_offset[TDM_QUAT][TDM_1]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX_2: + slots = tdm_slot[TDM_QUAT].num; + slot_width = tdm_slot[TDM_QUAT].width; + slot_offset = tdm_tx_slot_offset[TDM_QUAT][TDM_2]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX_3: + slots = tdm_slot[TDM_QUAT].num; + slot_width = tdm_slot[TDM_QUAT].width; + slot_offset = tdm_tx_slot_offset[TDM_QUAT][TDM_3]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX_4: + slots = tdm_slot[TDM_QUAT].num; + slot_width = tdm_slot[TDM_QUAT].width; + slot_offset = tdm_tx_slot_offset[TDM_QUAT][TDM_4]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX_5: + slots = tdm_slot[TDM_QUAT].num; + slot_width = tdm_slot[TDM_QUAT].width; + slot_offset = tdm_tx_slot_offset[TDM_QUAT][TDM_5]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX_6: + slots = tdm_slot[TDM_QUAT].num; + slot_width = tdm_slot[TDM_QUAT].width; + slot_offset = tdm_tx_slot_offset[TDM_QUAT][TDM_6]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX_7: + slots = tdm_slot[TDM_QUAT].num; + slot_width = tdm_slot[TDM_QUAT].width; + slot_offset = tdm_tx_slot_offset[TDM_QUAT][TDM_7]; + break; + default: + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + + for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) { + if (slot_offset[i] != AFE_SLOT_MAPPING_OFFSET_INVALID) + offset_channels++; + else + break; + } + + if (offset_channels == 0) { + pr_err("%s: invalid offset_channels %d\n", + __func__, offset_channels); + return -EINVAL; + } + + if (channels > offset_channels) { + pr_err("%s: channels %d exceed offset_channels %d\n", + __func__, channels, offset_channels); + return -EINVAL; + } + + slot_mask = tdm_param_set_slot_mask(slots); + if (!slot_mask) { + pr_err("%s: invalid slot_mask 0x%x\n", + __func__, slot_mask); + return -EINVAL; + } - pr_debug("%s: slot_width %d slots %d\n", __func__, slot_width, slots); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { pr_debug("%s: slot_width %d\n", __func__, slot_width); ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask, @@ -4541,9 +6085,35 @@ static int msm8998_tdm_snd_hw_params(struct snd_pcm_substream *substream, __func__, ret); goto end; } + } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + ret = snd_soc_dai_set_tdm_slot(cpu_dai, slot_mask, 0, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, + channels, slot_offset, 0, NULL); + if (ret < 0) { + pr_err("%s: failed to set channel map, err:%d\n", + __func__, ret); + goto end; + } } else { + ret = -EINVAL; pr_err("%s: invalid use case, err:%d\n", __func__, ret); + goto end; + } + + rate = params_rate(params); + clk_freq = rate * slot_width * slots; + ret = snd_soc_dai_set_sysclk(cpu_dai, 0, clk_freq, SND_SOC_CLOCK_OUT); + if (ret < 0) { + pr_err("%s: failed to set tdm clk, err:%d\n", + __func__, ret); } end: @@ -4560,7 +6130,7 @@ static int msm8998_tdm_snd_startup(struct snd_pcm_substream *substream) ret = msm_set_pinctrl(pinctrl_info, STATE_TDM_ACTIVE); if (ret) - pr_err("%s: MI2S TLMM pinctrl set failed with %d\n", + pr_err("%s: TDM TLMM pinctrl set failed with %d\n", __func__, ret); return ret; @@ -4576,7 +6146,7 @@ static void msm8998_tdm_snd_shutdown(struct snd_pcm_substream *substream) ret = msm_set_pinctrl(pinctrl_info, STATE_DISABLE); if (ret) - pr_err("%s: MI2S TLMM pinctrl set failed with %d\n", + pr_err("%s: TDM TLMM pinctrl set failed with %d\n", __func__, ret); } @@ -4712,28 +6282,6 @@ static struct snd_soc_ops msm_aux_pcm_be_ops = { .shutdown = msm_aux_pcm_snd_shutdown, }; -static unsigned int tdm_param_set_slot_mask(u16 port_id, int slot_width, - int slots) -{ - unsigned int slot_mask = 0; - int i, j; - unsigned int *slot_offset; - - for (i = TDM_0; i < TDM_PORT_MAX; i++) { - slot_offset = tdm_slot_offset[i]; - - for (j = 0; j < TDM_SLOT_OFFSET_MAX; j++) { - if (slot_offset[j] != AFE_SLOT_MAPPING_OFFSET_INVALID) - slot_mask |= - (1 << ((slot_offset[j] * 8) / slot_width)); - else - break; - } - } - - return slot_mask; -} - static int msm_tdm_snd_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -4775,9 +6323,7 @@ static int msm_tdm_snd_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } slots = 8; - slot_mask = tdm_param_set_slot_mask(cpu_dai->id, - slot_width, - slots); + slot_mask = tdm_param_set_slot_mask(slots); if (!slot_mask) { pr_err("%s: invalid slot_mask 0x%x\n", __func__, slot_mask); @@ -5859,8 +7405,8 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .no_pcm = 1, .dpcm_playback = 1, .be_id = MSM_BACKEND_DAI_PRI_TDM_RX_0, - .be_hw_params_fixup = msm_be_hw_params_fixup, - .ops = &msm_tdm_be_ops, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &msm8998_tdm_be_ops, .ignore_suspend = 1, }, { @@ -5873,8 +7419,8 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .no_pcm = 1, .dpcm_capture = 1, .be_id = MSM_BACKEND_DAI_PRI_TDM_TX_0, - .be_hw_params_fixup = msm_be_hw_params_fixup, - .ops = &msm_tdm_be_ops, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &msm8998_tdm_be_ops, .ignore_suspend = 1, }, { @@ -5957,8 +7503,8 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .no_pcm = 1, .dpcm_capture = 1, .be_id = MSM_BACKEND_DAI_QUAT_TDM_TX_0, - .be_hw_params_fixup = msm_be_hw_params_fixup, - .ops = &msm_tdm_be_ops, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &msm8998_tdm_be_ops, .ignore_suspend = 1, }, }; diff --git a/sound/soc/msm/qdsp6v2/msm-audio-effects-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-audio-effects-q6-v2.c index 37c43253a5bd..131a9bd87183 100644 --- a/sound/soc/msm/qdsp6v2/msm-audio-effects-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-audio-effects-q6-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, 2019 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 @@ -11,6 +11,7 @@ */ #include <linux/slab.h> +#include <linux/ratelimit.h> #include <sound/apr_audio-v2.h> #include <sound/q6asm-v2.h> #include <sound/compress_params.h> @@ -23,7 +24,8 @@ #define GET_NEXT(ptr, upper_limit, rc) \ ({ \ if (((ptr) + 1) > (upper_limit)) { \ - pr_err("%s: param list out of boundary\n", __func__); \ + pr_err_ratelimited("%s: param list out of boundary\n", \ + __func__); \ (rc) = -EINVAL; \ } \ ((rc) == 0) ? *(ptr)++ : -EINVAL; \ @@ -32,7 +34,8 @@ #define CHECK_PARAM_LEN(len, max_len, tag, rc) \ do { \ if ((len) > (max_len)) { \ - pr_err("%s: params length overflows\n", (tag)); \ + pr_err_ratelimited("%s: params length overflows\n", \ + (tag)); \ (rc) = -EINVAL; \ } \ } while (0) @@ -234,7 +237,8 @@ int msm_audio_effects_virtualizer_handler(struct audio_client *ac, param_data = (u8 *) &virtualizer->gain_adjust; break; default: - pr_err("%s: Invalid command to set config\n", __func__); + pr_err_ratelimited("%s: Invalid command to set config\n", + __func__); continue; } if (rc) @@ -656,7 +660,8 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, param_data = (u8 *) &reverb->density; break; default: - pr_err("%s: Invalid command to set config\n", __func__); + pr_err_ratelimited("%s: Invalid command to set config\n", + __func__); continue; } if (rc) @@ -797,7 +802,8 @@ int msm_audio_effects_bass_boost_handler(struct audio_client *ac, param_data = (u8 *) &bass_boost->strength; break; default: - pr_err("%s: Invalid command to set config\n", __func__); + pr_err_ratelimited("%s: Invalid command to set config\n", + __func__); continue; } if (rc) @@ -910,7 +916,8 @@ int msm_audio_effects_pbe_handler(struct audio_client *ac, param_data = (u8 *) values; break; default: - pr_err("%s: Invalid command to set config\n", __func__); + pr_err_ratelimited("%s: Invalid command to set config\n", + __func__); continue; } if (rc) @@ -1151,7 +1158,8 @@ int msm_audio_effects_popless_eq_handler(struct audio_client *ac, param_data = (u8 *) &eq->freq_millihertz; break; default: - pr_err("%s: Invalid command to set config\n", __func__); + pr_err_ratelimited("%s: Invalid command to set config\n", + __func__); continue; } if (rc) @@ -1270,7 +1278,7 @@ static int __msm_audio_effects_volume_handler(struct audio_client *ac, "VOLUME/VOLUME2_GAIN_MASTER", rc); break; default: - pr_err("%s: Invalid command id: %d to set config\n", + pr_err_ratelimited("%s: Invalid command id: %d to set config\n", __func__, command_id); continue; } diff --git a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c index baca08f3923b..3746a201b23c 100644 --- a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c @@ -106,6 +106,7 @@ struct msm_compr_pdata { struct msm_compr_ch_map *ch_map[MSM_FRONTEND_DAI_MAX]; int32_t ion_fd[MSM_FRONTEND_DAI_MAX]; bool is_in_use[MSM_FRONTEND_DAI_MAX]; + bool avs_ver; }; struct msm_compr_audio { @@ -1017,7 +1018,18 @@ static int msm_compr_send_media_format_block(struct snd_compr_stream *cstream, sample_word_size = 16; break; } - ret = q6asm_media_format_block_pcm_format_support_v4( + if (pdata->avs_ver && + (q6core_get_avs_version() == Q6_SUBSYS_AVS2_7)) + ret = q6asm_media_format_block_pcm_format_support_v3( + prtd->audio_client, + prtd->sample_rate, + prtd->num_channels, + bit_width, stream_id, + use_default_chmap, + chmap, + sample_word_size); + else + ret = q6asm_media_format_block_pcm_format_support_v4( prtd->audio_client, prtd->sample_rate, prtd->num_channels, @@ -1273,6 +1285,8 @@ static int msm_compr_configure_dsp_for_playback uint16_t bits_per_sample = 16; int dir = IN, ret = 0; struct audio_client *ac = prtd->audio_client; + struct msm_compr_pdata *pdata = + snd_soc_platform_get_drvdata(soc_prtd->platform); uint32_t stream_index; struct asm_softpause_params softpause = { .enable = SOFT_PAUSE_ENABLE, @@ -1323,7 +1337,14 @@ static int msm_compr_configure_dsp_for_playback } else { pr_debug("%s: stream_id %d bits_per_sample %d\n", __func__, ac->stream_id, bits_per_sample); - ret = q6asm_stream_open_write_v4(ac, + if (pdata->avs_ver && + (q6core_get_avs_version() == Q6_SUBSYS_AVS2_7)) + ret = q6asm_stream_open_write_v3(ac, + prtd->codec, bits_per_sample, + ac->stream_id, + prtd->gapless_state.use_dsp_gapless_mode); + else + ret = q6asm_stream_open_write_v4(ac, prtd->codec, bits_per_sample, ac->stream_id, prtd->gapless_state.use_dsp_gapless_mode); @@ -2639,7 +2660,14 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) pr_debug("%s: open_write stream_id %d bits_per_sample %d", __func__, stream_id, bits_per_sample); - rc = q6asm_stream_open_write_v4(prtd->audio_client, + if (pdata->avs_ver && + (q6core_get_avs_version() == Q6_SUBSYS_AVS2_7)) + rc = q6asm_stream_open_write_v3(prtd->audio_client, + prtd->codec, bits_per_sample, + stream_id, + prtd->gapless_state.use_dsp_gapless_mode); + else + rc = q6asm_stream_open_write_v4(prtd->audio_client, prtd->codec, bits_per_sample, stream_id, prtd->gapless_state.use_dsp_gapless_mode); @@ -4091,6 +4119,15 @@ static int msm_compr_probe(struct snd_soc_platform *platform) pdata->use_legacy_api = false; pr_debug("%s: use legacy api %d\n", __func__, pdata->use_legacy_api); + + if (of_property_read_bool(platform->dev->of_node, + "qcom,avs-version")) + pdata->avs_ver = true; + else + pdata->avs_ver = false; + + pr_debug("%s: avs_ver = %d\n", __func__, pdata->avs_ver); + /* * use_dsp_gapless_mode part of platform data(pdata) is updated from HAL * through a mixer control before compress driver is opened. The mixer diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c index 0c23e1eef483..39abecb64f0e 100644 --- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2019, 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 @@ -1510,12 +1510,18 @@ static int msm_dai_q6_prepare(struct snd_pcm_substream *substream, if (dai_data->enc_config.format != ENC_FMT_NONE) { int bitwidth = 0; - if (dai_data->afe_in_bitformat == - SNDRV_PCM_FORMAT_S24_LE) + switch (dai_data->afe_in_bitformat) { + case SNDRV_PCM_FORMAT_S32_LE: + bitwidth = 32; + break; + case SNDRV_PCM_FORMAT_S24_LE: bitwidth = 24; - else if (dai_data->afe_in_bitformat == - SNDRV_PCM_FORMAT_S16_LE) + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: bitwidth = 16; + break; + } pr_debug("%s: calling AFE_PORT_START_V2 with enc_format: %d\n", __func__, dai_data->enc_config.format); rc = afe_port_start_v2(dai->id, &dai_data->port_config, @@ -2337,6 +2343,9 @@ static int msm_dai_q6_afe_input_bit_format_get( } switch (dai_data->afe_in_bitformat) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 2; + break; case SNDRV_PCM_FORMAT_S24_LE: ucontrol->value.integer.value[0] = 1; break; @@ -2362,6 +2371,9 @@ static int msm_dai_q6_afe_input_bit_format_put( return -EINVAL; } switch (ucontrol->value.integer.value[0]) { + case 2: + dai_data->afe_in_bitformat = SNDRV_PCM_FORMAT_S32_LE; + break; case 1: dai_data->afe_in_bitformat = SNDRV_PCM_FORMAT_S24_LE; break; @@ -7424,11 +7436,17 @@ static int msm_dai_q6_tdm_set_tdm_slot(struct snd_soc_dai *dai, return -EINVAL; } - /* HW only supports 16 and 8 slots configuration */ + /* HW supports 1-32 slots configuration. Typical: 1, 2, 4, 8, 16, 32 */ switch (slots) { + case 1: + cap_mask = 0x01; + break; case 2: cap_mask = 0x03; break; + case 4: + cap_mask = 0x0F; + break; case 8: cap_mask = 0xFF; break; @@ -7532,27 +7550,13 @@ static int msm_dai_q6_tdm_set_sysclk(struct snd_soc_dai *dai, struct msm_dai_q6_tdm_dai_data *dai_data = dev_get_drvdata(dai->dev); - switch (dai->id) { - case AFE_PORT_ID_PRIMARY_TDM_RX: - case AFE_PORT_ID_PRIMARY_TDM_RX_1: - case AFE_PORT_ID_PRIMARY_TDM_RX_2: - case AFE_PORT_ID_PRIMARY_TDM_RX_3: - case AFE_PORT_ID_PRIMARY_TDM_RX_4: - case AFE_PORT_ID_PRIMARY_TDM_RX_5: - case AFE_PORT_ID_PRIMARY_TDM_RX_6: - case AFE_PORT_ID_PRIMARY_TDM_RX_7: - case AFE_PORT_ID_PRIMARY_TDM_TX: - case AFE_PORT_ID_PRIMARY_TDM_TX_1: - case AFE_PORT_ID_PRIMARY_TDM_TX_2: - case AFE_PORT_ID_PRIMARY_TDM_TX_3: - case AFE_PORT_ID_PRIMARY_TDM_TX_4: - case AFE_PORT_ID_PRIMARY_TDM_TX_5: - case AFE_PORT_ID_PRIMARY_TDM_TX_6: - case AFE_PORT_ID_PRIMARY_TDM_TX_7: + if ((dai->id >= AFE_PORT_ID_PRIMARY_TDM_RX) && + (dai->id <= AFE_PORT_ID_QUATERNARY_TDM_TX_7)) { dai_data->clk_set.clk_freq_in_hz = freq; - break; - default: - return 0; + } else { + dev_err(dai->dev, "%s: invalid dai id 0x%x\n", + __func__, dai->id); + return -EINVAL; } dev_dbg(dai->dev, "%s: dai id = 0x%x group clk_freq %d\n", @@ -8116,7 +8120,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8136,7 +8140,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8156,7 +8160,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8176,7 +8180,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8196,7 +8200,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8216,7 +8220,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8236,7 +8240,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8256,7 +8260,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8276,7 +8280,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8296,7 +8300,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8316,7 +8320,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8336,7 +8340,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8356,7 +8360,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8376,7 +8380,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8396,7 +8400,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8416,7 +8420,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8436,7 +8440,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8456,7 +8460,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8476,7 +8480,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8496,7 +8500,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8516,7 +8520,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8536,7 +8540,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8556,7 +8560,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8576,7 +8580,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8596,7 +8600,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8616,7 +8620,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8636,7 +8640,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8656,7 +8660,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8676,7 +8680,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8696,7 +8700,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8716,7 +8720,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8736,7 +8740,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8756,7 +8760,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8776,7 +8780,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8796,7 +8800,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8816,7 +8820,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8836,7 +8840,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8856,7 +8860,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8876,7 +8880,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8896,7 +8900,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8916,7 +8920,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8936,7 +8940,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8956,7 +8960,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8976,7 +8980,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -8996,7 +9000,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -9016,7 +9020,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -9036,7 +9040,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -9056,7 +9060,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -9076,7 +9080,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -9096,7 +9100,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -9116,7 +9120,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -9136,7 +9140,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -9156,7 +9160,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -9176,7 +9180,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -9196,7 +9200,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -9216,7 +9220,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -9236,7 +9240,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -9256,7 +9260,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -9276,7 +9280,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -9296,7 +9300,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -9316,7 +9320,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -9336,7 +9340,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -9356,7 +9360,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, @@ -9376,7 +9380,7 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, - .channels_max = 8, + .channels_max = 16, .rate_min = 8000, .rate_max = 352800, }, diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c index 0b55f71c23f6..a7aab839e6db 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c @@ -384,6 +384,17 @@ static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream) prtd->audio_client = NULL; return -ENOMEM; } + } else if (pdata->avs_ver && + (q6core_get_avs_version() == Q6_SUBSYS_AVS2_7)) { + ret = q6asm_open_write_v3(prtd->audio_client, + FORMAT_LINEAR_PCM, bits_per_sample); + if (ret < 0) { + pr_err("%s: q6asm_open_write_v3 failed (%d)\n", + __func__, ret); + q6asm_audio_client_free(prtd->audio_client); + prtd->audio_client = NULL; + return -ENOMEM; + } } else { ret = q6asm_open_write_with_retry(prtd->audio_client, fmt_type, bits_per_sample); @@ -433,6 +444,14 @@ static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream) prtd->channel_map, bits_per_sample, sample_word_size, ASM_LITTLE_ENDIAN, DEFAULT_QF); + else if (pdata->avs_ver && + (q6core_get_avs_version() == + Q6_SUBSYS_AVS2_7)) + ret = q6asm_media_format_block_multi_ch_pcm_v3( + prtd->audio_client, runtime->rate, + runtime->channels, !prtd->set_channel_map, + prtd->channel_map, bits_per_sample, + sample_word_size); else ret = q6asm_media_format_block_multi_ch_pcm_v4( prtd->audio_client, runtime->rate, @@ -497,8 +516,13 @@ static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream) pr_debug("%s Opening %d-ch PCM read stream, perf_mode %d\n", __func__, params_channels(params), prtd->audio_client->perf_mode); - - ret = q6asm_open_read_with_retry(prtd->audio_client, + if (pdata->avs_ver && + (q6core_get_avs_version() == Q6_SUBSYS_AVS2_7)) + ret = q6asm_open_read_v3(prtd->audio_client, + FORMAT_LINEAR_PCM, + bits_per_sample); + else + ret = q6asm_open_read_with_retry(prtd->audio_client, FORMAT_LINEAR_PCM, bits_per_sample, false); if (ret < 0) { @@ -577,6 +601,15 @@ static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream) sample_word_size, ASM_LITTLE_ENDIAN, DEFAULT_QF); + else if (pdata->avs_ver && + (q6core_get_avs_version() == + Q6_SUBSYS_AVS2_7)) + ret = q6asm_enc_cfg_blk_pcm_format_support_v3( + prtd->audio_client, + prtd->samp_rate, + prtd->channel_mode, + bits_per_sample, + sample_word_size); else ret = q6asm_enc_cfg_blk_pcm_format_support_v4( prtd->audio_client, @@ -1550,7 +1583,7 @@ static int msm_pcm_chmap_ctl_put(struct snd_kcontrol *kcontrol, prtd = substream->runtime->private_data; if (prtd) { prtd->set_channel_map = true; - for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++) + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V2; i++) prtd->channel_map[i] = (char)(ucontrol->value.integer.value[i]); } @@ -1578,11 +1611,11 @@ static int msm_pcm_chmap_ctl_get(struct snd_kcontrol *kcontrol, prtd = substream->runtime->private_data; if (prtd && prtd->set_channel_map == true) { - for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++) + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V2; i++) ucontrol->value.integer.value[i] = (int)prtd->channel_map[i]; } else { - for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++) + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V2; i++) ucontrol->value.integer.value[i] = 0; } @@ -1600,7 +1633,7 @@ static int msm_pcm_add_chmap_controls(struct snd_soc_pcm_runtime *rtd) pr_debug("%s, Channel map cntrl add\n", __func__); ret = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, snd_pcm_std_chmaps, - PCM_FORMAT_MAX_NUM_CHANNEL, 0, + PCM_FORMAT_MAX_NUM_CHANNEL_V2, 0, &chmap_info); if (ret < 0) { pr_err("%s, channel map cntrl add failed\n", __func__); @@ -2465,7 +2498,7 @@ static int msm_pcm_channel_mixer_output_map_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 32; + uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V2; uinfo->value.integer.min = 1; uinfo->value.integer.max = 64; return 0; @@ -2567,7 +2600,7 @@ static int msm_pcm_channel_mixer_input_map_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 32; + uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V2; uinfo->value.integer.min = 1; uinfo->value.integer.max = 64; return 0; @@ -2782,7 +2815,7 @@ static int msm_pcm_channel_mixer_weight_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 32; + uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V2; uinfo->value.integer.min = 0; uinfo->value.integer.max = 0x4000; return 0; @@ -3048,6 +3081,14 @@ static int msm_pcm_probe(struct platform_device *pdev) pdata->perf_mode = LEGACY_PCM_MODE; } + if (of_property_read_bool(pdev->dev.of_node, + "qcom,avs-version")) + pdata->avs_ver = true; + else + pdata->avs_ver = false; + + pr_debug("%s: avs_ver = %d\n", __func__, pdata->avs_ver); + dev_set_drvdata(&pdev->dev, pdata); dev_dbg(&pdev->dev, "%s: dev name %s\n", diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h index afe314455fbf..a90e9f7d4fc9 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h +++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h @@ -123,6 +123,7 @@ struct output_meta_data_st { struct msm_plat_data { int perf_mode; + bool avs_ver; struct snd_pcm *pcm; struct snd_pcm *pcm_device[MSM_FRONTEND_DAI_MM_SIZE]; struct msm_pcm_channel_mixer chmixer_pspd[MSM_FRONTEND_DAI_MM_SIZE][2]; diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/sound/soc/msm/qdsp6v2/q6afe.c index 4e1965302ba1..c8a2cc37363c 100644 --- a/sound/soc/msm/qdsp6v2/q6afe.c +++ b/sound/soc/msm/qdsp6v2/q6afe.c @@ -332,6 +332,11 @@ static int32_t sp_make_afe_callback(uint32_t opcode, uint32_t *payload, /* Set command specific details */ switch (opcode) { case AFE_PORT_CMDRSP_GET_PARAM_V2: + if (payload_size < (5 * sizeof(uint32_t))) { + pr_err("%s: Error: size %d is less than expected\n", + __func__, payload_size); + return -EINVAL; + } expected_size += sizeof(struct param_hdr_v1); param_hdr.module_id = payload[1]; param_hdr.instance_id = INSTANCE_ID_0; @@ -340,7 +345,17 @@ static int32_t sp_make_afe_callback(uint32_t opcode, uint32_t *payload, data_start = &payload[4]; break; case AFE_PORT_CMDRSP_GET_PARAM_V3: + if (payload_size < (6 * sizeof(uint32_t))) { + pr_err("%s: Error: size %d is less than expected\n", + __func__, payload_size); + return -EINVAL; + } expected_size += sizeof(struct param_hdr_v3); + if (payload_size < expected_size) { + pr_err("%s: Error: size %d is less than expected\n", + __func__, payload_size); + return -EINVAL; + } memcpy(¶m_hdr, &payload[1], sizeof(struct param_hdr_v3)); data_start = &payload[5]; break; diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index c3f173fbb135..e9412b1d68e7 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -950,10 +950,13 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, codec_params = *params; /* fixup params based on TDM slot masks */ - if (codec_dai->tx_mask) + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + codec_dai->tx_mask) soc_pcm_codec_params_fixup(&codec_params, codec_dai->tx_mask); - if (codec_dai->rx_mask) + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && + codec_dai->rx_mask) soc_pcm_codec_params_fixup(&codec_params, codec_dai->rx_mask); diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c index be78078a10ba..954dc4423cb0 100644 --- a/sound/usb/line6/driver.c +++ b/sound/usb/line6/driver.c @@ -307,12 +307,16 @@ int line6_read_data(struct usb_line6 *line6, unsigned address, void *data, { struct usb_device *usbdev = line6->usbdev; int ret; - unsigned char len; + unsigned char *len; unsigned count; if (address > 0xffff || datalen > 0xff) return -EINVAL; + len = kmalloc(sizeof(*len), GFP_KERNEL); + if (!len) + return -ENOMEM; + /* query the serial number: */ ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, @@ -321,7 +325,7 @@ int line6_read_data(struct usb_line6 *line6, unsigned address, void *data, if (ret < 0) { dev_err(line6->ifcdev, "read request failed (error %d)\n", ret); - return ret; + goto exit; } /* Wait for data length. We'll get 0xff until length arrives. */ @@ -331,28 +335,29 @@ int line6_read_data(struct usb_line6 *line6, unsigned address, void *data, ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0x0012, 0x0000, &len, 1, + 0x0012, 0x0000, len, 1, LINE6_TIMEOUT * HZ); if (ret < 0) { dev_err(line6->ifcdev, "receive length failed (error %d)\n", ret); - return ret; + goto exit; } - if (len != 0xff) + if (*len != 0xff) break; } - if (len == 0xff) { + ret = -EIO; + if (*len == 0xff) { dev_err(line6->ifcdev, "read failed after %d retries\n", count); - return -EIO; - } else if (len != datalen) { + goto exit; + } else if (*len != datalen) { /* should be equal or something went wrong */ dev_err(line6->ifcdev, "length mismatch (expected %d, got %d)\n", - (int)datalen, (int)len); - return -EIO; + (int)datalen, (int)*len); + goto exit; } /* receive the result: */ @@ -361,12 +366,12 @@ int line6_read_data(struct usb_line6 *line6, unsigned address, void *data, 0x0013, 0x0000, data, datalen, LINE6_TIMEOUT * HZ); - if (ret < 0) { + if (ret < 0) dev_err(line6->ifcdev, "read failed (error %d)\n", ret); - return ret; - } - return 0; +exit: + kfree(len); + return ret; } EXPORT_SYMBOL_GPL(line6_read_data); @@ -378,12 +383,16 @@ int line6_write_data(struct usb_line6 *line6, unsigned address, void *data, { struct usb_device *usbdev = line6->usbdev; int ret; - unsigned char status; + unsigned char *status; int count; if (address > 0xffff || datalen > 0xffff) return -EINVAL; + status = kmalloc(sizeof(*status), GFP_KERNEL); + if (!status) + return -ENOMEM; + ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, 0x0022, address, data, datalen, @@ -392,7 +401,7 @@ int line6_write_data(struct usb_line6 *line6, unsigned address, void *data, if (ret < 0) { dev_err(line6->ifcdev, "write request failed (error %d)\n", ret); - return ret; + goto exit; } for (count = 0; count < LINE6_READ_WRITE_MAX_RETRIES; count++) { @@ -403,28 +412,29 @@ int line6_write_data(struct usb_line6 *line6, unsigned address, void *data, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 0x0012, 0x0000, - &status, 1, LINE6_TIMEOUT * HZ); + status, 1, LINE6_TIMEOUT * HZ); if (ret < 0) { dev_err(line6->ifcdev, "receiving status failed (error %d)\n", ret); - return ret; + goto exit; } - if (status != 0xff) + if (*status != 0xff) break; } - if (status == 0xff) { + if (*status == 0xff) { dev_err(line6->ifcdev, "write failed after %d retries\n", count); - return -EIO; - } else if (status != 0) { + ret = -EIO; + } else if (*status != 0) { dev_err(line6->ifcdev, "write failed (error %d)\n", ret); - return -EIO; + ret = -EIO; } - - return 0; +exit: + kfree(status); + return ret; } EXPORT_SYMBOL_GPL(line6_write_data); diff --git a/sound/usb/line6/toneport.c b/sound/usb/line6/toneport.c index 6d4c50c9b17d..5512b3d532e7 100644 --- a/sound/usb/line6/toneport.c +++ b/sound/usb/line6/toneport.c @@ -365,15 +365,20 @@ static bool toneport_has_source_select(struct usb_line6_toneport *toneport) /* Setup Toneport device. */ -static void toneport_setup(struct usb_line6_toneport *toneport) +static int toneport_setup(struct usb_line6_toneport *toneport) { - int ticks; + int *ticks; struct usb_line6 *line6 = &toneport->line6; struct usb_device *usbdev = line6->usbdev; + ticks = kmalloc(sizeof(*ticks), GFP_KERNEL); + if (!ticks) + return -ENOMEM; + /* sync time on device with host: */ - ticks = (int)get_seconds(); - line6_write_data(line6, 0x80c6, &ticks, 4); + *ticks = (int)get_seconds(); + line6_write_data(line6, 0x80c6, ticks, 4); + kfree(ticks); /* enable device: */ toneport_send_cmd(usbdev, 0x0301, 0x0000); @@ -388,6 +393,7 @@ static void toneport_setup(struct usb_line6_toneport *toneport) toneport_update_led(toneport); mod_timer(&toneport->timer, jiffies + TONEPORT_PCM_DELAY * HZ); + return 0; } /* @@ -451,7 +457,9 @@ static int toneport_init(struct usb_line6 *line6, return err; } - toneport_setup(toneport); + err = toneport_setup(toneport); + if (err) + return err; /* register audio system: */ return snd_card_register(line6->card); @@ -463,7 +471,11 @@ static int toneport_init(struct usb_line6 *line6, */ static int toneport_reset_resume(struct usb_interface *interface) { - toneport_setup(usb_get_intfdata(interface)); + int err; + + err = toneport_setup(usb_get_intfdata(interface)); + if (err) + return err; return line6_resume(interface); } #endif diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 80d7a380fade..de8fe209b772 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -2376,6 +2376,8 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, kctl = snd_ctl_new1(&mixer_selectunit_ctl, cval); if (! kctl) { usb_audio_err(state->chip, "cannot malloc kcontrol\n"); + for (i = 0; i < desc->bNrInPins; i++) + kfree(namelist[i]); kfree(namelist); kfree(cval); return -ENOMEM; diff --git a/tools/include/linux/bitops.h b/tools/include/linux/bitops.h index 5ad9ee1dd7f6..57187d6790c1 100644 --- a/tools/include/linux/bitops.h +++ b/tools/include/linux/bitops.h @@ -3,17 +3,14 @@ #include <asm/types.h> #include <linux/kernel.h> -#include <linux/compiler.h> - #ifndef __WORDSIZE #define __WORDSIZE (__SIZEOF_LONG__ * 8) #endif #define BITS_PER_LONG __WORDSIZE +#include <linux/bits.h> +#include <linux/compiler.h> -#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) -#define BIT_WORD(nr) ((nr) / BITS_PER_LONG) -#define BITS_PER_BYTE 8 #define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) #define BITS_TO_U64(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u64)) #define BITS_TO_U32(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u32)) diff --git a/tools/include/linux/bits.h b/tools/include/linux/bits.h new file mode 100644 index 000000000000..2b7b532c1d51 --- /dev/null +++ b/tools/include/linux/bits.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LINUX_BITS_H +#define __LINUX_BITS_H +#include <asm/bitsperlong.h> + +#define BIT(nr) (1UL << (nr)) +#define BIT_ULL(nr) (1ULL << (nr)) +#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) +#define BIT_WORD(nr) ((nr) / BITS_PER_LONG) +#define BIT_ULL_MASK(nr) (1ULL << ((nr) % BITS_PER_LONG_LONG)) +#define BIT_ULL_WORD(nr) ((nr) / BITS_PER_LONG_LONG) +#define BITS_PER_BYTE 8 + +/* + * Create a contiguous bitmask starting at bit position @l and ending at + * position @h. For example + * GENMASK_ULL(39, 21) gives us the 64bit vector 0x000000ffffe00000. + */ +#define GENMASK(h, l) \ + (((~0UL) - (1UL << (l)) + 1) & (~0UL >> (BITS_PER_LONG - 1 - (h)))) + +#define GENMASK_ULL(h, l) \ + (((~0ULL) - (1ULL << (l)) + 1) & \ + (~0ULL >> (BITS_PER_LONG_LONG - 1 - (h)))) + +#endif /* __LINUX_BITS_H */ diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c index 743746a3c50d..df3c73e9dea4 100644 --- a/tools/lib/traceevent/event-parse.c +++ b/tools/lib/traceevent/event-parse.c @@ -2201,7 +2201,7 @@ eval_type_str(unsigned long long val, const char *type, int pointer) return val & 0xffffffff; if (strcmp(type, "u64") == 0 || - strcmp(type, "s64")) + strcmp(type, "s64") == 0) return val; if (strcmp(type, "s8") == 0) diff --git a/tools/perf/bench/numa.c b/tools/perf/bench/numa.c index 73d192f57dc3..df41deed0320 100644 --- a/tools/perf/bench/numa.c +++ b/tools/perf/bench/numa.c @@ -32,6 +32,10 @@ #include <numa.h> #include <numaif.h> +#ifndef RUSAGE_THREAD +# define RUSAGE_THREAD 1 +#endif + /* * Regular printout to the terminal, supressed if -q is specified: */ diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c index 62b38f2ff60d..c1944765533c 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c +++ b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c @@ -58,6 +58,7 @@ enum intel_pt_pkt_state { INTEL_PT_STATE_NO_IP, INTEL_PT_STATE_ERR_RESYNC, INTEL_PT_STATE_IN_SYNC, + INTEL_PT_STATE_TNT_CONT, INTEL_PT_STATE_TNT, INTEL_PT_STATE_TIP, INTEL_PT_STATE_TIP_PGD, @@ -72,8 +73,9 @@ static inline bool intel_pt_sample_time(enum intel_pt_pkt_state pkt_state) case INTEL_PT_STATE_NO_IP: case INTEL_PT_STATE_ERR_RESYNC: case INTEL_PT_STATE_IN_SYNC: - case INTEL_PT_STATE_TNT: + case INTEL_PT_STATE_TNT_CONT: return true; + case INTEL_PT_STATE_TNT: case INTEL_PT_STATE_TIP: case INTEL_PT_STATE_TIP_PGD: case INTEL_PT_STATE_FUP: @@ -854,16 +856,20 @@ static uint64_t intel_pt_next_period(struct intel_pt_decoder *decoder) timestamp = decoder->timestamp + decoder->timestamp_insn_cnt; masked_timestamp = timestamp & decoder->period_mask; if (decoder->continuous_period) { - if (masked_timestamp != decoder->last_masked_timestamp) + if (masked_timestamp > decoder->last_masked_timestamp) return 1; } else { timestamp += 1; masked_timestamp = timestamp & decoder->period_mask; - if (masked_timestamp != decoder->last_masked_timestamp) { + if (masked_timestamp > decoder->last_masked_timestamp) { decoder->last_masked_timestamp = masked_timestamp; decoder->continuous_period = true; } } + + if (masked_timestamp < decoder->last_masked_timestamp) + return decoder->period_ticks; + return decoder->period_ticks - (timestamp - masked_timestamp); } @@ -892,7 +898,10 @@ static void intel_pt_sample_insn(struct intel_pt_decoder *decoder) case INTEL_PT_PERIOD_TICKS: timestamp = decoder->timestamp + decoder->timestamp_insn_cnt; masked_timestamp = timestamp & decoder->period_mask; - decoder->last_masked_timestamp = masked_timestamp; + if (masked_timestamp > decoder->last_masked_timestamp) + decoder->last_masked_timestamp = masked_timestamp; + else + decoder->last_masked_timestamp += decoder->period_ticks; break; case INTEL_PT_PERIOD_NONE: case INTEL_PT_PERIOD_MTC: @@ -1141,7 +1150,9 @@ static int intel_pt_walk_tnt(struct intel_pt_decoder *decoder) return -ENOENT; } decoder->tnt.count -= 1; - if (!decoder->tnt.count) + if (decoder->tnt.count) + decoder->pkt_state = INTEL_PT_STATE_TNT_CONT; + else decoder->pkt_state = INTEL_PT_STATE_IN_SYNC; decoder->tnt.payload <<= 1; decoder->state.from_ip = decoder->ip; @@ -1172,7 +1183,9 @@ static int intel_pt_walk_tnt(struct intel_pt_decoder *decoder) if (intel_pt_insn.branch == INTEL_PT_BR_CONDITIONAL) { decoder->tnt.count -= 1; - if (!decoder->tnt.count) + if (decoder->tnt.count) + decoder->pkt_state = INTEL_PT_STATE_TNT_CONT; + else decoder->pkt_state = INTEL_PT_STATE_IN_SYNC; if (decoder->tnt.payload & BIT63) { decoder->tnt.payload <<= 1; @@ -1192,8 +1205,11 @@ static int intel_pt_walk_tnt(struct intel_pt_decoder *decoder) return 0; } decoder->ip += intel_pt_insn.length; - if (!decoder->tnt.count) + if (!decoder->tnt.count) { + decoder->sample_timestamp = decoder->timestamp; + decoder->sample_insn_cnt = decoder->timestamp_insn_cnt; return -EAGAIN; + } decoder->tnt.payload <<= 1; continue; } @@ -2116,6 +2132,7 @@ const struct intel_pt_state *intel_pt_decode(struct intel_pt_decoder *decoder) err = intel_pt_walk_trace(decoder); break; case INTEL_PT_STATE_TNT: + case INTEL_PT_STATE_TNT_CONT: err = intel_pt_walk_tnt(decoder); if (err == -EAGAIN) err = intel_pt_walk_trace(decoder); diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index accb7ece1d3c..3d7af024c73f 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c @@ -1,4 +1,5 @@ #include "util.h" +#include <linux/compiler.h> #include "linux/string.h" #define K 1024LL diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index dcc659017976..ee5d1dfe13da 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -76,7 +76,6 @@ #include <sys/ttydefaults.h> #include <api/fs/tracing_path.h> #include <termios.h> -#include <linux/bitops.h> #include <termios.h> extern const char *graph_line; diff --git a/tools/power/x86/turbostat/Makefile b/tools/power/x86/turbostat/Makefile index e367b1a85d70..3c04e2a85599 100644 --- a/tools/power/x86/turbostat/Makefile +++ b/tools/power/x86/turbostat/Makefile @@ -8,7 +8,7 @@ ifeq ("$(origin O)", "command line") endif turbostat : turbostat.c -CFLAGS += -Wall +CFLAGS += -Wall -I../../../include CFLAGS += -DMSRHEADER='"../../../../arch/x86/include/asm/msr-index.h"' %: %.c diff --git a/tools/testing/selftests/net/run_netsocktests b/tools/testing/selftests/net/run_netsocktests index 16058bbea7a8..c195b4478662 100755 --- a/tools/testing/selftests/net/run_netsocktests +++ b/tools/testing/selftests/net/run_netsocktests @@ -6,7 +6,7 @@ echo "--------------------" ./socket if [ $? -ne 0 ]; then echo "[FAIL]" + exit 1 else echo "[PASS]" fi - |
