diff options
867 files changed, 25132 insertions, 10262 deletions
diff --git a/Documentation/devicetree/bindings/arm/msm/clock-cpu-8996.txt b/Documentation/devicetree/bindings/arm/msm/clock-cpu-8996.txt index f3c6cc747710..bb74c167a324 100644 --- a/Documentation/devicetree/bindings/arm/msm/clock-cpu-8996.txt +++ b/Documentation/devicetree/bindings/arm/msm/clock-cpu-8996.txt @@ -8,6 +8,7 @@ PLL FMAXes etc. Required properties: - compatible: Must be either "qcom,cpu-clock-8996" or "qcom,cpu-clock-8996-v3" or "qcom,cpu-clock-8996-pro" + or "qcom,cpu-clock-8996-auto" - reg: Pairs of physical base addresses and region sizes of memory mapped registers. - reg-names: Names of the bases for the above registers. Expected @@ -39,6 +40,11 @@ Required properties: clock for the CBF. - cbf-dev: The CBF cache device to which the OPP table for the CBF clock domain will be added. + +Optional properties: +- qcom,pwrcl-early-boot-freq: Power cluster early boot up frequency in HZ. +- qcom,perfcl-early-boot-freq: Perf cluster early boot up frequency in HZ. + Example: clock_cpu: qcom,cpu-clock-8996@ { compatible = "qcom,cpu-clock-8996"; diff --git a/Documentation/devicetree/bindings/arm/msm/mdm-modem.txt b/Documentation/devicetree/bindings/arm/msm/mdm-modem.txt index a6537ebd2512..7d5e8a1c910a 100644 --- a/Documentation/devicetree/bindings/arm/msm/mdm-modem.txt +++ b/Documentation/devicetree/bindings/arm/msm/mdm-modem.txt @@ -6,8 +6,8 @@ to be reset. Required Properties: - compatible: The bus devices need to be compatible with - "qcom,mdm2-modem", "qcom,ext-mdm9x25", "qcom,ext-mdm9x35", "qcom, ext-mdm9x45", - "qcom,ext-mdm9x55". + "qcom,mdm2-modem", "qcom,ext-mdm9x25", "qcom,ext-mdm9x35", "qcom,ext-mdm9x45", + "qcom,ext-mdm9x55", "qcom,ext-apq8096". Required named gpio properties: - qcom,mdm2ap-errfatal-gpio: gpio for the external modem to indicate to the apps processor @@ -110,6 +110,10 @@ Optional driver parameters: on behalf of the subsystem driver. - qcom,mdm-link-info: a string indicating additional info about the physical link. For example: "devID_domain.bus.slot" in case of PCIe. +- qcom,mdm-auto-boot: Boolean. To indicate this instance of esoc boots independently. +- qcom,mdm-statusline-not-a-powersource: Boolean. If set, status line to esoc device is not a + power source. +- qcom,mdm-userspace-handle-shutdown: Boolean. If set, userspace handles shutdown requests. Example: mdm0: qcom,mdm0 { diff --git a/Documentation/devicetree/bindings/arm/msm/msm.txt b/Documentation/devicetree/bindings/arm/msm/msm.txt index 5647b22eede0..d442cf02a816 100644 --- a/Documentation/devicetree/bindings/arm/msm/msm.txt +++ b/Documentation/devicetree/bindings/arm/msm/msm.txt @@ -186,6 +186,7 @@ compatible = "qcom,apq8094-fluid" compatible = "qcom,apq8094-liquid" compatible = "qcom,apq8094-mtp" compatible = "qcom,apq8094-dragonboard" +compatible = "qcom,apq8096-adp" compatible = "qcom,apq8096-cdp" compatible = "qcom,apq8096-mtp" compatible = "qcom,apq8096-dragonboard" @@ -280,6 +281,7 @@ compatible = "qcom,sdm660-rumi" compatible = "qcom,sdm660-cdp" compatible = "qcom,sdm660-mtp" compatible = "qcom,sdm660-qrd" +compatible = "qcom,sda660-qrd" compatible = "qcom,sda658-mtp" compatible = "qcom,sda658-cdp" compatible = "qcom,sda660-mtp" @@ -290,6 +292,7 @@ compatible = "qcom,sdm630-cdp" compatible = "qcom,sda630-mtp" compatible = "qcom,sda630-cdp" compatible = "qcom,sdm630-qrd" +compatible = "qcom,sda630-qrd" compatible = "qcom,msm8952-rumi" compatible = "qcom,msm8952-sim" compatible = "qcom,msm8952-qrd" diff --git a/Documentation/devicetree/bindings/cache/msm_cache_erp64.txt b/Documentation/devicetree/bindings/cache/msm_cache_erp64.txt new file mode 100644 index 000000000000..5dfb6d60529d --- /dev/null +++ b/Documentation/devicetree/bindings/cache/msm_cache_erp64.txt @@ -0,0 +1,26 @@ +* MSM 64bit L0, L1, L2 and L3 cache error reporting driver + +Required properties: +- compatible: Should be "qcom,kryo_cache_erp64" +- reg: I/O address L3 hardware block. +- interrupts: Should contain the L0/L1, L2 and L3 cache error interrupt number. +- interrupt-names: Should contain the interrupt names "l1_irq", "l2_irq_info_0", + "l2_irq_info_1", "l2_irq_err_0", "l2_irq_err_1", "l3_irq". + +Example: + qcom,cache_erp64@6500000 { + compatible = "qcom,kryo_cache_erp64"; + reg = <0x6500000 0x4000>; + /* + * PPI 0 for L0/L1 + * SPI 1 for Cluster 1 L2 Info + * SPI 9 for Cluster 2 L2 Info + * SPI 2 for Cluster 1 L2 Error + * SPI 10 for Cluster 2 L2 Error + * SPI 17 for L3 error + */ + interrupts = <1 0 0>, <0 1 0>, <0 9 0>, <0 2 0>, <0 10 0>, + <0 17 0>; + interrupt-names = "l1_irq", "l2_irq_info_0", "l2_irq_info_1", + "l2_irq_err_0", "l2_irq_err_1", "l3_irq"; + }; diff --git a/Documentation/devicetree/bindings/cache/msm_m4m_erp.txt b/Documentation/devicetree/bindings/cache/msm_m4m_erp.txt new file mode 100644 index 000000000000..5526f1188528 --- /dev/null +++ b/Documentation/devicetree/bindings/cache/msm_m4m_erp.txt @@ -0,0 +1,15 @@ +* MSM M4M error reporting driver + +Required properties: +- compatible: Should be "qcom,m4m_erp". +- reg: I/O address M4M hardware block. +- interrupts: Should contain the M4M error interrupt number. +- interrupt-names: Should contain the interrupt names "m4m_irq". + +Example: + qcom,m4m_erp64@9A40000 { + compatible = "qcom,m4m_erp"; + reg = <0x9A40000 0x40000>; + interrupts = <0 22 0>; + interrupt-names = "m4m_irq"; + }; diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index 8a47b77abfca..e8c74a6e738b 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -18,6 +18,7 @@ Required properties: "allwinner,sun4i-a10-cpu-clk" - for the CPU multiplexer clock "allwinner,sun4i-a10-axi-clk" - for the AXI clock "allwinner,sun8i-a23-axi-clk" - for the AXI clock on A23 + "allwinner,sun4i-a10-gates-clk" - for generic gates on all compatible SoCs "allwinner,sun4i-a10-axi-gates-clk" - for the AXI gates "allwinner,sun4i-a10-ahb-clk" - for the AHB clock "allwinner,sun5i-a13-ahb-clk" - for the AHB clock on A13 @@ -43,6 +44,7 @@ Required properties: "allwinner,sun6i-a31-apb0-gates-clk" - for the APB0 gates on A31 "allwinner,sun7i-a20-apb0-gates-clk" - for the APB0 gates on A20 "allwinner,sun8i-a23-apb0-gates-clk" - for the APB0 gates on A23 + "allwinner,sun8i-h3-apb0-gates-clk" - for the APB0 gates on H3 "allwinner,sun9i-a80-apb0-gates-clk" - for the APB0 gates on A80 "allwinner,sun4i-a10-apb1-clk" - for the APB1 clock "allwinner,sun9i-a80-apb1-clk" - for the APB1 bus clock on A80 diff --git a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt index ce5ee56ada68..1f8458cd0659 100644 --- a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt +++ b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt @@ -563,6 +563,7 @@ Optional properites: to a non-DSI interface. - qcom,bridge-name: A string to indicate the name of the bridge chip connected to DSI. qcom,bridge-name is required if qcom,dba-panel is defined for the panel. +- qcom,hdmi-mode: Indicates where current panel is HDMI mode, otherwise, it will be DVI mode. - qcom,adjust-timer-wakeup-ms: An integer value to indicate the timer delay(in ms) to accommodate s/w delay while configuring the event timer wakeup logic. diff --git a/Documentation/devicetree/bindings/fb/mdss-dsi.txt b/Documentation/devicetree/bindings/fb/mdss-dsi.txt index 6c20d22f98b4..0d55389f3790 100644 --- a/Documentation/devicetree/bindings/fb/mdss-dsi.txt +++ b/Documentation/devicetree/bindings/fb/mdss-dsi.txt @@ -50,6 +50,8 @@ Optional properties: -- qcom,supply-post-on-sleep: time to sleep (ms) after turning on -- qcom,supply-pre-off-sleep: time to sleep (ms) before turning off -- qcom,supply-post-off-sleep: time to sleep (ms) after turning off + -- qcom,supply-lp-mode-disable-allowed: supply can be turned off in + low power state. - pll-src-config Specified the source PLL for the DSI link clocks: "PLL0" - Clocks sourced out of DSI PLL0 diff --git a/Documentation/devicetree/bindings/input/qpnp-power-on.txt b/Documentation/devicetree/bindings/input/qpnp-power-on.txt index a596aa1c595d..c2550e6fe26c 100644 --- a/Documentation/devicetree/bindings/input/qpnp-power-on.txt +++ b/Documentation/devicetree/bindings/input/qpnp-power-on.txt @@ -82,6 +82,8 @@ Optional properties: - qcom,shutdown-poweroff-type Same description as qcom,warm-reset-poweroff- type but this applies for the system shutdown case. +- qcom,kpdpwr-sw-debounce Boolean property to enable the debounce logic + on the KPDPWR_N rising edge. All the below properties are in the sub-node section (properties of the child diff --git a/Documentation/devicetree/bindings/net/neutrino_avb.txt b/Documentation/devicetree/bindings/net/neutrino_avb.txt new file mode 100644 index 000000000000..46c6a5208eba --- /dev/null +++ b/Documentation/devicetree/bindings/net/neutrino_avb.txt @@ -0,0 +1,28 @@ +* Neutrino Ethernet * +This driver implements Ethernet driver for Neutrino ethernet controller +Required properties: + - compatible: Should be "qcom,ntn_avb" + - ntn-rst-gpio: Neutrino reset GPIO + - vdd-ntn-hsic-supply: neutrino HSIC power supply + - vdd-ntn-pci-supply: PCIe core power supply + - vdd-ntn-io-supply: Neutrino IO power supply + - vdd-ntn-phy-supply: Ethernet Phy power supply + - vdd-ntn-core-supply: Neutrino core power supply + - pinctrl-names: should contain GPIO details + - pinctrl-0: Neutrino reset GPIO [this is from MSM] + - ntn-rst-delay-msec: dealy (msec) required after PCIe reset for stabilization + - ntn-rc-num: PCIe root complex number on which Neutrino is connected +Example: + qcom,ntn_avb { + compatible = "qcom,ntn_avb"; + ntn-rst-gpio = <&pm8994_gpios 13 0>; + vdd-ntn-hsic-supply = <&pm8994_l25>; + vdd-ntn-pci-supply = <&pm8994_s4>; + vdd-ntn-io-supply = <&pm8994_s4>; + vdd-ntn-phy-supply = <&pm8994_l9>; + vdd-ntn-core-supply = <&pm8994_l19>; + pinctrl-names = "default"; + pinctrl-0 = <&ntn_default>; + qcom,ntn-rst-delay-msec = <100>; + qcom,ntn-rc-num = <1>; + }; diff --git a/Documentation/devicetree/bindings/pil/subsys-pil-tz.txt b/Documentation/devicetree/bindings/pil/subsys-pil-tz.txt index d7edafc9a46b..4a69e03951b7 100644 --- a/Documentation/devicetree/bindings/pil/subsys-pil-tz.txt +++ b/Documentation/devicetree/bindings/pil/subsys-pil-tz.txt @@ -67,6 +67,7 @@ Optional properties: - qcom,complete-ramdump: Boolean. If set, complete ramdump i.e. region between start address of first segment to end address of last segment will be collected without leaving any hole in between. +- qcom,ignore-ssr-failure: Boolean. If set, SSR failures are not considered fatal. Example: qcom,venus@fdce0000 { diff --git a/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt b/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt index 5529e30811fb..92ef23c3a290 100644 --- a/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt +++ b/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt @@ -22,8 +22,7 @@ Charger specific properties: Definition: String which indicates the charging mode. Can be one of the following: Standalone/Parallel Master - "qcom,smb138x-charger" - smb138x Parallel Slave - "qcom,smb138x-parallel-slave" - smb1355 Parallel Slave - "qcom,smb1355-parallel-slave", + Parallel Slave - "qcom,smb138x-parallel-slave" - qcom,pmic-revid Usage: required @@ -36,8 +35,7 @@ Charger specific properties: Usage: optional Value type: <u32> Definition: Specifies parallel charging mode. If not specified, MID-MID - option is selected by default. Note that smb1355 can only - run in MID-MID configuration. + option is selected by default. - qcom,suspend-input Usage: optional @@ -127,7 +125,7 @@ Example ======= smb138x_charger: qcom,smb138x-charger { - compatible = "qcom,smb138x-charger"; + compatible = "qcom,qpnp-smb138x-charger"; #address-cells = <1>; #size-cells = <1>; diff --git a/Documentation/devicetree/bindings/regulator/onsemi-ncp6335d.txt b/Documentation/devicetree/bindings/regulator/onsemi-ncp6335d.txt new file mode 100644 index 000000000000..7d492f5d7e25 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/onsemi-ncp6335d.txt @@ -0,0 +1,72 @@ +ON Semiconductor NCP6335d regulator + +NCP6335d is able to deliver up to 5.0 A, with programmable output voltage from +0.6 V to 1.87 V in 10mV steps, with synchronous rectification and automatic PWM/ +PFM transitions, enable pins and power good/fail signaling. + +The NCP6335d interface is via I2C bus. + +Required Properties: +- compatible: Must be "onnn,ncp6335d-regulator". +- reg: The device 8-bit I2C address. +- regulator-min-microvolt: Minimum voltage in microvolts supported by this + regulator. +- regulator-max-microvolt: Maximum voltage in microvolts supported by this + regulator. +- onnn,min-setpoint: Minimum setpoint voltage in microvolts supported + by this regulator. +- onnn,step-size: The step size of the regulator, in uV. +- onnn,min-slew-ns: Minimum time in ns needed to change voltage by + one step size. This value corresponds to DVS + mode bit of 00b in command register. +- onnn,max-slew-ns: Maximum time in ns needed to change voltage by + one step size. This value corresponds to DVS + mode bit of 11b in command register. +- onnn,vsel: Working vsel register. Supported value are 0 + or 1. +- onnn,slew-ns: Time in ns needed to change voltage by one step + size. Supported value are 333, 666, 1333, 2666. + +Optional Properties: +- onnn,discharge-enable: Present: discharge enabled. + Not Present: discharge disabled. +- onnn,restore-reg: Present: Restore vsel register from backup register. + Not Present: No restore. +- onnn,vsel-gpio: Present: GPIO connects to the VSEL pin and set the + VSEL pin according to device tree flag. + Not Present: No GPIO is connected to vsel pin. +- pinctrl-names: The state name of the VSEL pin configuration. + Only support: "default" +- pinctrl-0: The phandles of the pin configuration node in + pinctrl for VSEL pin. + For details of pinctrl properties, please refer to: + "Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt" +- onnn,sleep-enable: Present: Forced in sleep mode when EN and VSEL + pins are low. + Not Present: Low quiescent current mode when EN and VSEL + pins are low. +- onnn,mode: A string which specifies the initial mode to use for the regulator. + Supported values are "pwm" and "auto". PWM mode is more + robust, but draws more current than auto mode. If this propery + is not specified, then the regulator will be in the hardware default mode. + +Example: + i2c_0 { + ncp6335d-regulator@1c { + compatible = "onnn,ncp6335d-regulator"; + reg = <0x1c>; + onnn,vsel = <0>; + onnn,slew-rate-ns = <2666>; + onnn,discharge-enable; + onnn,step-size = <10000>; + onnn,min-slew-ns = <333>; + onnn,max-slew-ns = <2666>; + pintrl-names = "default"; + pinctrl-0 = <&ext_buck_vsel_default>; + + regulator-min-microvolt = <1050000>; + regulator-max-microvolt = <1350000>; + onnn,min-setpoint = <600000>; + onnn,vsel-gpio = <&msmgpio 2 1>; + }; + }; diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 2d8fe6499090..f6851d94c1af 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -1418,11 +1418,20 @@ accept_ra_pinfo - BOOLEAN Functional default: enabled if accept_ra is enabled. disabled if accept_ra is disabled. +accept_ra_rt_info_min_plen - INTEGER + Minimum prefix length of Route Information in RA. + + Route Information w/ prefix smaller than this variable shall + be ignored. + + Functional default: 0 if accept_ra_rtr_pref is enabled. + -1 if accept_ra_rtr_pref is disabled. + accept_ra_rt_info_max_plen - INTEGER Maximum prefix length of Route Information in RA. - Route Information w/ prefix larger than or equal to this - variable shall be ignored. + Route Information w/ prefix larger than this variable shall + be ignored. Functional default: 0 if accept_ra_rtr_pref is enabled. -1 if accept_ra_rtr_pref is disabled. diff --git a/Documentation/networking/netlink_mmap.txt b/Documentation/networking/netlink_mmap.txt deleted file mode 100644 index 54f10478e8e3..000000000000 --- a/Documentation/networking/netlink_mmap.txt +++ /dev/null @@ -1,332 +0,0 @@ -This file documents how to use memory mapped I/O with netlink. - -Author: Patrick McHardy <kaber@trash.net> - -Overview --------- - -Memory mapped netlink I/O can be used to increase throughput and decrease -overhead of unicast receive and transmit operations. Some netlink subsystems -require high throughput, these are mainly the netfilter subsystems -nfnetlink_queue and nfnetlink_log, but it can also help speed up large -dump operations of f.i. the routing database. - -Memory mapped netlink I/O used two circular ring buffers for RX and TX which -are mapped into the processes address space. - -The RX ring is used by the kernel to directly construct netlink messages into -user-space memory without copying them as done with regular socket I/O, -additionally as long as the ring contains messages no recvmsg() or poll() -syscalls have to be issued by user-space to get more message. - -The TX ring is used to process messages directly from user-space memory, the -kernel processes all messages contained in the ring using a single sendmsg() -call. - -Usage overview --------------- - -In order to use memory mapped netlink I/O, user-space needs three main changes: - -- ring setup -- conversion of the RX path to get messages from the ring instead of recvmsg() -- conversion of the TX path to construct messages into the ring - -Ring setup is done using setsockopt() to provide the ring parameters to the -kernel, then a call to mmap() to map the ring into the processes address space: - -- setsockopt(fd, SOL_NETLINK, NETLINK_RX_RING, ¶ms, sizeof(params)); -- setsockopt(fd, SOL_NETLINK, NETLINK_TX_RING, ¶ms, sizeof(params)); -- ring = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0) - -Usage of either ring is optional, but even if only the RX ring is used the -mapping still needs to be writable in order to update the frame status after -processing. - -Conversion of the reception path involves calling poll() on the file -descriptor, once the socket is readable the frames from the ring are -processed in order until no more messages are available, as indicated by -a status word in the frame header. - -On kernel side, in order to make use of memory mapped I/O on receive, the -originating netlink subsystem needs to support memory mapped I/O, otherwise -it will use an allocated socket buffer as usual and the contents will be - copied to the ring on transmission, nullifying most of the performance gains. -Dumps of kernel databases automatically support memory mapped I/O. - -Conversion of the transmit path involves changing message construction to -use memory from the TX ring instead of (usually) a buffer declared on the -stack and setting up the frame header appropriately. Optionally poll() can -be used to wait for free frames in the TX ring. - -Structured and definitions for using memory mapped I/O are contained in -<linux/netlink.h>. - -RX and TX rings ----------------- - -Each ring contains a number of continuous memory blocks, containing frames of -fixed size dependent on the parameters used for ring setup. - -Ring: [ block 0 ] - [ frame 0 ] - [ frame 1 ] - [ block 1 ] - [ frame 2 ] - [ frame 3 ] - ... - [ block n ] - [ frame 2 * n ] - [ frame 2 * n + 1 ] - -The blocks are only visible to the kernel, from the point of view of user-space -the ring just contains the frames in a continuous memory zone. - -The ring parameters used for setting up the ring are defined as follows: - -struct nl_mmap_req { - unsigned int nm_block_size; - unsigned int nm_block_nr; - unsigned int nm_frame_size; - unsigned int nm_frame_nr; -}; - -Frames are grouped into blocks, where each block is a continuous region of memory -and holds nm_block_size / nm_frame_size frames. The total number of frames in -the ring is nm_frame_nr. The following invariants hold: - -- frames_per_block = nm_block_size / nm_frame_size - -- nm_frame_nr = frames_per_block * nm_block_nr - -Some parameters are constrained, specifically: - -- nm_block_size must be a multiple of the architectures memory page size. - The getpagesize() function can be used to get the page size. - -- nm_frame_size must be equal or larger to NL_MMAP_HDRLEN, IOW a frame must be - able to hold at least the frame header - -- nm_frame_size must be smaller or equal to nm_block_size - -- nm_frame_size must be a multiple of NL_MMAP_MSG_ALIGNMENT - -- nm_frame_nr must equal the actual number of frames as specified above. - -When the kernel can't allocate physically continuous memory for a ring block, -it will fall back to use physically discontinuous memory. This might affect -performance negatively, in order to avoid this the nm_frame_size parameter -should be chosen to be as small as possible for the required frame size and -the number of blocks should be increased instead. - -Ring frames ------------- - -Each frames contain a frame header, consisting of a synchronization word and some -meta-data, and the message itself. - -Frame: [ header message ] - -The frame header is defined as follows: - -struct nl_mmap_hdr { - unsigned int nm_status; - unsigned int nm_len; - __u32 nm_group; - /* credentials */ - __u32 nm_pid; - __u32 nm_uid; - __u32 nm_gid; -}; - -- nm_status is used for synchronizing processing between the kernel and user- - space and specifies ownership of the frame as well as the operation to perform - -- nm_len contains the length of the message contained in the data area - -- nm_group specified the destination multicast group of message - -- nm_pid, nm_uid and nm_gid contain the netlink pid, UID and GID of the sending - process. These values correspond to the data available using SOCK_PASSCRED in - the SCM_CREDENTIALS cmsg. - -The possible values in the status word are: - -- NL_MMAP_STATUS_UNUSED: - RX ring: frame belongs to the kernel and contains no message - for user-space. Approriate action is to invoke poll() - to wait for new messages. - - TX ring: frame belongs to user-space and can be used for - message construction. - -- NL_MMAP_STATUS_RESERVED: - RX ring only: frame is currently used by the kernel for message - construction and contains no valid message yet. - Appropriate action is to invoke poll() to wait for - new messages. - -- NL_MMAP_STATUS_VALID: - RX ring: frame contains a valid message. Approriate action is - to process the message and release the frame back to - the kernel by setting the status to - NL_MMAP_STATUS_UNUSED or queue the frame by setting the - status to NL_MMAP_STATUS_SKIP. - - TX ring: the frame contains a valid message from user-space to - be processed by the kernel. After completing processing - the kernel will release the frame back to user-space by - setting the status to NL_MMAP_STATUS_UNUSED. - -- NL_MMAP_STATUS_COPY: - RX ring only: a message is ready to be processed but could not be - stored in the ring, either because it exceeded the - frame size or because the originating subsystem does - not support memory mapped I/O. Appropriate action is - to invoke recvmsg() to receive the message and release - the frame back to the kernel by setting the status to - NL_MMAP_STATUS_UNUSED. - -- NL_MMAP_STATUS_SKIP: - RX ring only: user-space queued the message for later processing, but - processed some messages following it in the ring. The - kernel should skip this frame when looking for unused - frames. - -The data area of a frame begins at a offset of NL_MMAP_HDRLEN relative to the -frame header. - -TX limitations --------------- - -As of Jan 2015 the message is always copied from the ring frame to an -allocated buffer due to unresolved security concerns. -See commit 4682a0358639b29cf ("netlink: Always copy on mmap TX."). - -Example -------- - -Ring setup: - - unsigned int block_size = 16 * getpagesize(); - struct nl_mmap_req req = { - .nm_block_size = block_size, - .nm_block_nr = 64, - .nm_frame_size = 16384, - .nm_frame_nr = 64 * block_size / 16384, - }; - unsigned int ring_size; - void *rx_ring, *tx_ring; - - /* Configure ring parameters */ - if (setsockopt(fd, SOL_NETLINK, NETLINK_RX_RING, &req, sizeof(req)) < 0) - exit(1); - if (setsockopt(fd, SOL_NETLINK, NETLINK_TX_RING, &req, sizeof(req)) < 0) - exit(1) - - /* Calculate size of each individual ring */ - ring_size = req.nm_block_nr * req.nm_block_size; - - /* Map RX/TX rings. The TX ring is located after the RX ring */ - rx_ring = mmap(NULL, 2 * ring_size, PROT_READ | PROT_WRITE, - MAP_SHARED, fd, 0); - if ((long)rx_ring == -1L) - exit(1); - tx_ring = rx_ring + ring_size: - -Message reception: - -This example assumes some ring parameters of the ring setup are available. - - unsigned int frame_offset = 0; - struct nl_mmap_hdr *hdr; - struct nlmsghdr *nlh; - unsigned char buf[16384]; - ssize_t len; - - while (1) { - struct pollfd pfds[1]; - - pfds[0].fd = fd; - pfds[0].events = POLLIN | POLLERR; - pfds[0].revents = 0; - - if (poll(pfds, 1, -1) < 0 && errno != -EINTR) - exit(1); - - /* Check for errors. Error handling omitted */ - if (pfds[0].revents & POLLERR) - <handle error> - - /* If no new messages, poll again */ - if (!(pfds[0].revents & POLLIN)) - continue; - - /* Process all frames */ - while (1) { - /* Get next frame header */ - hdr = rx_ring + frame_offset; - - if (hdr->nm_status == NL_MMAP_STATUS_VALID) { - /* Regular memory mapped frame */ - nlh = (void *)hdr + NL_MMAP_HDRLEN; - len = hdr->nm_len; - - /* Release empty message immediately. May happen - * on error during message construction. - */ - if (len == 0) - goto release; - } else if (hdr->nm_status == NL_MMAP_STATUS_COPY) { - /* Frame queued to socket receive queue */ - len = recv(fd, buf, sizeof(buf), MSG_DONTWAIT); - if (len <= 0) - break; - nlh = buf; - } else - /* No more messages to process, continue polling */ - break; - - process_msg(nlh); -release: - /* Release frame back to the kernel */ - hdr->nm_status = NL_MMAP_STATUS_UNUSED; - - /* Advance frame offset to next frame */ - frame_offset = (frame_offset + frame_size) % ring_size; - } - } - -Message transmission: - -This example assumes some ring parameters of the ring setup are available. -A single message is constructed and transmitted, to send multiple messages -at once they would be constructed in consecutive frames before a final call -to sendto(). - - unsigned int frame_offset = 0; - struct nl_mmap_hdr *hdr; - struct nlmsghdr *nlh; - struct sockaddr_nl addr = { - .nl_family = AF_NETLINK, - }; - - hdr = tx_ring + frame_offset; - if (hdr->nm_status != NL_MMAP_STATUS_UNUSED) - /* No frame available. Use poll() to avoid. */ - exit(1); - - nlh = (void *)hdr + NL_MMAP_HDRLEN; - - /* Build message */ - build_message(nlh); - - /* Fill frame header: length and status need to be set */ - hdr->nm_len = nlh->nlmsg_len; - hdr->nm_status = NL_MMAP_STATUS_VALID; - - if (sendto(fd, NULL, 0, 0, &addr, sizeof(addr)) < 0) - exit(1); - - /* Advance frame offset to next frame */ - frame_offset = (frame_offset + frame_size) % ring_size; diff --git a/Documentation/sysctl/fs.txt b/Documentation/sysctl/fs.txt index 302b5ed616a6..35e17f748ca7 100644 --- a/Documentation/sysctl/fs.txt +++ b/Documentation/sysctl/fs.txt @@ -265,6 +265,13 @@ aio-nr can grow to. ============================================================== +mount-max: + +This denotes the maximum number of mounts that may exist +in a mount namespace. + +============================================================== + 2. /proc/sys/fs/binfmt_misc ---------------------------------------------------------- @@ -1,6 +1,6 @@ VERSION = 4 PATCHLEVEL = 4 -SUBLEVEL = 55 +SUBLEVEL = 66 EXTRAVERSION = NAME = Blurry Fish Butt @@ -146,7 +146,7 @@ PHONY += $(MAKECMDGOALS) sub-make $(filter-out _all sub-make $(CURDIR)/Makefile, $(MAKECMDGOALS)) _all: sub-make @: -sub-make: FORCE +sub-make: $(Q)$(MAKE) -C $(KBUILD_OUTPUT) KBUILD_SRC=$(CURDIR) \ -f $(CURDIR)/Makefile $(filter-out _all sub-make,$(MAKECMDGOALS)) @@ -1005,7 +1005,7 @@ prepare1: prepare2 $(version_h) include/generated/utsrelease.h \ archprepare: archheaders archscripts prepare1 scripts_basic -prepare0: archprepare FORCE +prepare0: archprepare $(Q)$(MAKE) $(build)=. # All the preparing.. @@ -1050,7 +1050,7 @@ INSTALL_FW_PATH=$(INSTALL_MOD_PATH)/lib/firmware export INSTALL_FW_PATH PHONY += firmware_install -firmware_install: FORCE +firmware_install: @mkdir -p $(objtree)/firmware $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.fwinst obj=firmware __fw_install @@ -1070,7 +1070,7 @@ PHONY += archscripts archscripts: PHONY += __headers -__headers: $(version_h) scripts_basic asm-generic archheaders archscripts FORCE +__headers: $(version_h) scripts_basic asm-generic archheaders archscripts $(Q)$(MAKE) $(build)=scripts build_unifdef PHONY += headers_install_all diff --git a/android/configs/android-base.cfg b/android/configs/android-base.cfg index 7f672485f9c2..67842e7007a8 100644 --- a/android/configs/android-base.cfg +++ b/android/configs/android-base.cfg @@ -1,10 +1,12 @@ # KEEP ALPHABETICALLY SORTED # CONFIG_DEVKMEM is not set # CONFIG_DEVMEM is not set +# CONFIG_FHANDLE is not set # CONFIG_INET_LRO is not set # CONFIG_MODULES is not set # CONFIG_OABI_COMPAT is not set # CONFIG_SYSVIPC is not set +# CONFIG_USELIB is not set CONFIG_ANDROID=y CONFIG_ANDROID_BINDER_IPC=y CONFIG_ANDROID_BINDER_DEVICES=binder,hwbinder,vndbinder @@ -12,7 +14,6 @@ CONFIG_ANDROID_LOW_MEMORY_KILLER=y CONFIG_ARMV8_DEPRECATED=y CONFIG_ASHMEM=y CONFIG_AUDIT=y -CONFIG_BLK_DEV_DM=y CONFIG_BLK_DEV_INITRD=y CONFIG_CGROUPS=y CONFIG_CGROUP_CPUACCT=y @@ -20,14 +21,15 @@ CONFIG_CGROUP_DEBUG=y CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_SCHED=y CONFIG_CP15_BARRIER_EMULATION=y -CONFIG_DM_CRYPT=y -CONFIG_DM_VERITY=y -CONFIG_DM_VERITY_FEC=y +CONFIG_DEFAULT_SECURITY_SELINUX=y CONFIG_EMBEDDED=y CONFIG_FB=y CONFIG_HARDENED_USERCOPY=y CONFIG_HIGH_RES_TIMERS=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y CONFIG_INET6_AH=y +CONFIG_INET6_DIAG_DESTROY=y CONFIG_INET6_ESP=y CONFIG_INET6_IPCOMP=y CONFIG_INET=y @@ -43,7 +45,6 @@ CONFIG_IPV6=y CONFIG_IPV6_MIP6=y CONFIG_IPV6_MULTIPLE_TABLES=y CONFIG_IPV6_OPTIMISTIC_DAD=y -CONFIG_IPV6_PRIVACY=y CONFIG_IPV6_ROUTER_PREF=y CONFIG_IPV6_ROUTE_INFO=y CONFIG_IP_ADVANCED_ROUTER=y @@ -65,6 +66,9 @@ CONFIG_IP_NF_TARGET_MASQUERADE=y CONFIG_IP_NF_TARGET_NETMAP=y CONFIG_IP_NF_TARGET_REDIRECT=y CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODVERSIONS=y CONFIG_NET=y CONFIG_NETDEVICES=y CONFIG_NETFILTER=y @@ -158,16 +162,16 @@ CONFIG_STAGING=y CONFIG_SWP_EMULATION=y CONFIG_SYNC=y CONFIG_TUN=y -CONFIG_UID_CPUTIME=y +CONFIG_UID_SYS_STATS=y CONFIG_UNIX=y -CONFIG_USB_GADGET=y CONFIG_USB_CONFIGFS=y +CONFIG_USB_CONFIGFS_F_ACC=y +CONFIG_USB_CONFIGFS_F_AUDIO_SRC=y CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_MIDI=y CONFIG_USB_CONFIGFS_F_MTP=y CONFIG_USB_CONFIGFS_F_PTP=y -CONFIG_USB_CONFIGFS_F_ACC=y -CONFIG_USB_CONFIGFS_F_AUDIO_SRC=y CONFIG_USB_CONFIGFS_UEVENT=y -CONFIG_USB_CONFIGFS_F_MIDI=y +CONFIG_USB_GADGET=y CONFIG_USB_OTG_WAKELOCK=y CONFIG_XFRM_USER=y diff --git a/android/configs/android-recommended.cfg b/android/configs/android-recommended.cfg index 70aaae17ad29..11df0892301b 100644 --- a/android/configs/android-recommended.cfg +++ b/android/configs/android-recommended.cfg @@ -7,16 +7,22 @@ # CONFIG_PM_WAKELOCKS_GC is not set # CONFIG_VT is not set CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ARM64_SW_TTBR0_PAN=y CONFIG_ARM_KERNMEM_PERMS=y CONFIG_ARM64_SW_TTBR0_PAN=y CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_BLK_DEV_DM=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 CONFIG_CC_STACKPROTECTOR_STRONG=y CONFIG_COMPACTION=y +CONFIG_CPU_SW_DOMAIN_PAN=y CONFIG_DEBUG_RODATA=y +CONFIG_DM_CRYPT=y CONFIG_DM_UEVENT=y +CONFIG_DM_VERITY=y +CONFIG_DM_VERITY_FEC=y CONFIG_DRAGONRISE_FF=y CONFIG_ENABLE_DEFAULT_TRACERS=y CONFIG_EXT4_FS=y @@ -92,6 +98,7 @@ CONFIG_LOGIRUMBLEPAD2_FF=y CONFIG_LOGITECH_FF=y CONFIG_MD=y CONFIG_MEDIA_SUPPORT=y +CONFIG_MEMORY_STATE_TIME=y CONFIG_MSDOS_FS=y CONFIG_PANIC_TIMEOUT=5 CONFIG_PANTHERLORD_FF=y @@ -121,7 +128,6 @@ CONFIG_TIMER_STATS=y CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y CONFIG_UHID=y -CONFIG_MEMORY_STATE_TIME=y CONFIG_USB_ANNOUNCE_NEW_DEVICES=y CONFIG_USB_EHCI_HCD=y CONFIG_USB_HIDDEV=y diff --git a/arch/arc/include/asm/entry-arcv2.h b/arch/arc/include/asm/entry-arcv2.h index b5ff87e6f4b7..aee1a77934cf 100644 --- a/arch/arc/include/asm/entry-arcv2.h +++ b/arch/arc/include/asm/entry-arcv2.h @@ -16,6 +16,7 @@ ; ; Now manually save: r12, sp, fp, gp, r25 + PUSH r30 PUSH r12 ; Saving pt_regs->sp correctly requires some extra work due to the way @@ -72,6 +73,7 @@ POPAX AUX_USER_SP 1: POP r12 + POP r30 .endm diff --git a/arch/arc/include/asm/ptrace.h b/arch/arc/include/asm/ptrace.h index 69095da1fcfd..47111d565a95 100644 --- a/arch/arc/include/asm/ptrace.h +++ b/arch/arc/include/asm/ptrace.h @@ -84,7 +84,7 @@ struct pt_regs { unsigned long fp; unsigned long sp; /* user/kernel sp depending on where we came from */ - unsigned long r12; + unsigned long r12, r30; /*------- Below list auto saved by h/w -----------*/ unsigned long r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11; diff --git a/arch/arm/boot/Makefile b/arch/arm/boot/Makefile index 8849c2d20ac5..a5278293bd15 100644 --- a/arch/arm/boot/Makefile +++ b/arch/arm/boot/Makefile @@ -69,7 +69,7 @@ $(obj)/zImage: $(obj)/compressed/vmlinux FORCE $(obj)/zImage-dtb: $(obj)/zImage $(DTB_OBJS) FORCE $(call if_changed,cat) - @echo ' Kernel: $@ is ready' + @$(kecho) ' Kernel: $@ is ready' endif diff --git a/arch/arm/boot/dts/qcom/Makefile b/arch/arm/boot/dts/qcom/Makefile index f5c0da0cd8e7..8e7f26bc85ae 100644 --- a/arch/arm/boot/dts/qcom/Makefile +++ b/arch/arm/boot/dts/qcom/Makefile @@ -10,6 +10,7 @@ dtb-$(CONFIG_ARCH_MSM8996) += msm8996-v2-pmi8994-cdp.dtb \ msm8996-v2-liquid.dtb \ msm8996-v2-dtp.dtb \ msm8996-v3-auto-cdp.dtb \ + msm8996-v3-auto-adp.dtb \ msm8996-v3-pmi8994-cdp.dtb \ msm8996-v3-pmi8994-mtp.dtb \ msm8996-v3-pmi8994-pmk8001-cdp.dtb \ @@ -27,6 +28,10 @@ dtb-$(CONFIG_ARCH_MSM8996) += msm8996-v2-pmi8994-cdp.dtb \ msm8996-v3-dtp.dtb \ msm8996-v3-pm8004-mmxf-adp.dtb \ msm8996-v3-pm8004-agave-adp.dtb \ + msm8996-v3-pm8004-agave-adp-lite.dtb \ + msm8996pro-auto-adp.dtb \ + msm8996pro-auto-adp-lite.dtb \ + msm8996pro-auto-cdp.dtb \ msm8996pro-pmi8994-cdp.dtb \ msm8996pro-pmi8994-mtp.dtb \ msm8996pro-pmi8994-pmk8001-cdp.dtb \ @@ -39,6 +44,7 @@ dtb-$(CONFIG_ARCH_MSM8996) += msm8996-v2-pmi8994-cdp.dtb \ msm8996pro-pmi8996-mtp.dtb \ msm8996pro-pmi8996-pmk8001-cdp.dtb \ msm8996pro-pmi8996-pmk8001-mtp.dtb \ + msm8996pro-v1.1-auto-cdp.dtb \ msm8996pro-v1.1-pmi8994-cdp.dtb \ msm8996pro-v1.1-pmi8994-mtp.dtb \ msm8996pro-v1.1-pmi8994-pmk8001-cdp.dtb \ @@ -51,7 +57,11 @@ dtb-$(CONFIG_ARCH_MSM8996) += msm8996-v2-pmi8994-cdp.dtb \ msm8996pro-v1.1-pmi8996-mtp.dtb \ msm8996pro-v1.1-pmi8996-pmk8001-cdp.dtb \ msm8996pro-v1.1-pmi8996-pmk8001-mtp.dtb \ + apq8096pro-auto-cdp.dtb \ + apq8096pro-v1.1-auto-adp.dtb \ + apq8096pro-v1.1-auto-adp-lite.dtb \ apq8096pro-liquid.dtb \ + apq8096pro-v1.1-auto-cdp.dtb \ msm8996-v3.0-pmi8994-cdp.dtb \ msm8996-v3.0-pmi8994-mtp.dtb \ msm8996-v3.0-pmi8994-pm8004-cdp.dtb \ @@ -82,6 +92,8 @@ dtb-$(CONFIG_ARCH_MSM8996) += msm8996-v2-pmi8994-cdp.dtb \ apq8096-v3-dragonboard.dtb \ apq8096-v3-sbc.dtb \ apq8096-v3-auto-dragonboard.dtb \ + apq8096-v3-auto-adp.dtb \ + apq8096-v3-auto-cdp.dtb \ apq8096-v3.0-pmi8994-cdp.dtb \ apq8096-v3.0-pmi8994-mtp.dtb \ apq8096-v3.0-pmi8994-pm8004-cdp.dtb \ @@ -185,6 +197,7 @@ dtb-$(CONFIG_ARCH_SDM660) += sdm660-sim.dtb \ sda660-pm660a-cdp.dtb \ sda660-pm660a-mtp.dtb \ sda660-pm660a-rcm.dtb \ + sda660-pm660a-qrd-hdk.dtb \ sdm660-headset-jacktype-no-cdp.dtb \ sdm660-headset-jacktype-no-rcm.dtb \ sdm660-pm660a-headset-jacktype-no-cdp.dtb \ @@ -235,6 +248,7 @@ dtb-$(CONFIG_ARCH_SDM630) += sdm630-rumi.dtb \ sda630-pm660a-mtp.dtb \ sda630-pm660a-cdp.dtb \ sda630-pm660a-rcm.dtb \ + sda630-pm660a-qrd-hdk.dtb \ sdm630-headset-jacktype-no-cdp.dtb \ sdm630-headset-jacktype-no-rcm.dtb \ sdm630-pm660a-headset-jacktype-no-cdp.dtb \ diff --git a/arch/arm/boot/dts/qcom/apq8096-auto-dragonboard.dtsi b/arch/arm/boot/dts/qcom/apq8096-auto-dragonboard.dtsi index 57a0467c6fc2..549bb9c169ce 100644 --- a/arch/arm/boot/dts/qcom/apq8096-auto-dragonboard.dtsi +++ b/arch/arm/boot/dts/qcom/apq8096-auto-dragonboard.dtsi @@ -528,6 +528,19 @@ }; &soc { + qcom,ntn_avb { + compatible = "qcom,ntn_avb"; + + ntn-rst-gpio = <&pm8994_gpios 13 0>; + + vdd-ntn-hsic-supply = <&pm8994_l25>; + vdd-ntn-pci-supply = <&pm8994_s4>; + vdd-ntn-io-supply = <&pm8994_s4>; + + qcom,ntn-rst-delay-msec = <100>; + qcom,ntn-rc-num = <1>; + }; + qcom,cnss { wlan-bootstrap-gpio = <&tlmm 46 0>; wlan-en-gpio = <&pm8994_gpios 8 0>; @@ -789,11 +802,14 @@ status = "okay"; }; - gpio@cc00 { /* GPIO 13 */ - qcom,mode = <1>; /* DIGITAL OUT */ - qcom,vin-sel = <2>; /* 1.8 */ - qcom,src-sel = <0>; /* GPIO */ - qcom,master-en = <1>; /* Enable GPIO */ + gpio@cc00 { /* GPIO 13 - NTN_RST */ + qcom,mode = <1>; /* DIGITAL OUT */ + qcom,output-type = <0>; /* CMOS logic */ + qcom,pull = <5>; + qcom,vin-sel = <2>; /* 1.8 */ + qcom,out-strength = <1>; + qcom,src-sel = <0>; /* GPIO */ + qcom,master-en = <1>; /* Enable GPIO */ status = "okay"; }; diff --git a/arch/arm/boot/dts/qcom/apq8096-v3-auto-adp.dts b/arch/arm/boot/dts/qcom/apq8096-v3-auto-adp.dts new file mode 100644 index 000000000000..a91ec5eeb2e7 --- /dev/null +++ b/arch/arm/boot/dts/qcom/apq8096-v3-auto-adp.dts @@ -0,0 +1,161 @@ +/* Copyright (c) 2015-2016, 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. + */ + +/dts-v1/; + +#include <dt-bindings/gpio/gpio.h> +#include "apq8096-v3.dtsi" +#include "msm8996-pm8994.dtsi" +#include "msm8996-agave-adp.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. APQ 8096 v3 AUTO ADP"; + compatible = "qcom,apq8096-adp", "qcom,apq8096", "qcom,adp"; + qcom,msm-id = <311 0x30001>; + qcom,board-id = <0x02010019 0>; +}; + +&spi_9 { + status = "ok"; + can-controller@0 { + compatible = "renesas,rh850"; + reg = <0>; + interrupt-parent = <&tlmm>; + interrupts = <122 0>; + spi-max-frequency = <5000000>; + }; +}; + +&soc { + qcom,msm-ssc-sensors { + status = "disabled"; + }; + + qcom,msm-thermal { + qcom,hotplug-temp = <115>; + qcom,hotplug-temp-hysteresis = <25>; + qcom,therm-reset-temp = <119>; + }; +}; + +&slim_msm { + status = "disabled"; +}; + +&pm8994_mpps { + mpp@a500 { /* MPP 6 */ + qcom,mode = <1>; /* Digital output */ + qcom,output-type = <0>; /* CMOS logic */ + qcom,vin-sel = <2>; /* S4 1.8V */ + qcom,src-sel = <0>; /* Constant */ + qcom,master-en = <1>; /* Enable GPIO */ + status = "okay"; + }; +}; + +&hl7509_en_vreg { + status = "ok"; +}; + +&hl7509_vreg { + status = "ok"; +}; + +&sdhc_2 { + cd-gpios = <&tlmm 38 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_cd_on_sbc>; + pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off + &sdc2_cd_on_sbc>; +}; + +&i2c_7 { + silabs4705@11 { /* SiLabs FM chip, slave id 0x11*/ + status = "disabled"; + }; +}; + +&gfx_cpr { + vdd-supply = <&hl7509_vreg>; + qcom,cpr-step-quot-init-min = <20>; + qcom,cpr-step-quot-init-max = <26>; + qcom,voltage-step = <10000>; + /delete-property/ qcom,cpr-enable; +}; + +&gfx_vreg { + qcom,cpr-voltage-ceiling = + <600000 670000 670000 750000 830000 + 910000 960000 1020000>; + qcom,cpr-voltage-floor = + <600000 600000 600000 600000 600000 + 600000 600000 600000>; +}; + +&pm8994_l3 { + regulator-min-microvolt = <875000>; + regulator-max-microvolt = <875000>; + qcom,init-voltage = <875000>; +}; + +&pm8994_l11 { + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <850000>; + qcom,init-voltage = <850000>; +}; + +&pm8994_l17 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + qcom,init-voltage = <1800000>; +}; + +&pm8994_l23 { + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + qcom,init-voltage = <1100000>; +}; + +&pm8994_l27 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <800000>; + qcom,init-voltage = <800000>; +}; + +&pm8994_l29 { + regulator-min-microvolt = <2500000>; + regulator-max-microvolt = <2500000>; + qcom,init-voltage = <2500000>; +}; + +&rpm_bus { + rpm-regulator-ldoa26 { + /delete-node/ pm8994_l26_corner; + /delete-node/ pm8994_l26_floor_corner; + + pm8994_l26: regulator-l26 { + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + qcom,init-voltage = <1100000>; + status = "okay"; + }; + }; + + rpm-regulator-ldoa31 { + status = "okay"; + pm8994_l31: regulator-l31 { + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + qcom,init-voltage = <1100000>; + status = "okay"; + }; + }; +}; diff --git a/arch/arm/boot/dts/qcom/apq8096-v3-auto-cdp.dts b/arch/arm/boot/dts/qcom/apq8096-v3-auto-cdp.dts new file mode 100644 index 000000000000..e59003f2d316 --- /dev/null +++ b/arch/arm/boot/dts/qcom/apq8096-v3-auto-cdp.dts @@ -0,0 +1,44 @@ +/* Copyright (c) 2015-2016, 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. + */ + +/dts-v1/; + +#include "apq8096-v3.dtsi" +#include "msm8996-pm8994.dtsi" +#include "msm8996-auto-cdp.dtsi" +#include "msm8996v3-auto.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. APQ 8096 v3 AUTO CDP"; + compatible = "qcom,apq8096-cdp", "qcom,apq8096", "qcom,cdp"; + qcom,msm-id = <311 0x30001>; + qcom,board-id = <0x03010001 0>; +}; + +&soc { + qcom,msm-thermal { + qcom,hotplug-temp = <115>; + qcom,hotplug-temp-hysteresis = <25>; + qcom,therm-reset-temp = <119>; + }; +}; + +&spi_9 { + status = "ok"; + can-controller@0 { + compatible = "renesas,rh850"; + reg = <0>; + interrupt-parent = <&tlmm>; + interrupts = <127 0>; + spi-max-frequency = <5000000>; + }; +}; diff --git a/arch/arm/boot/dts/qcom/apq8096pro-auto-cdp.dts b/arch/arm/boot/dts/qcom/apq8096pro-auto-cdp.dts new file mode 100644 index 000000000000..d438bbe828ed --- /dev/null +++ b/arch/arm/boot/dts/qcom/apq8096pro-auto-cdp.dts @@ -0,0 +1,44 @@ +/* Copyright (c) 2015-2016, 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. + */ + +/dts-v1/; + +#include "apq8096pro.dtsi" +#include "msm8996-pm8994.dtsi" +#include "msm8996-auto-cdp.dtsi" +#include "msm8996pro-auto.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. APQ 8096 pro AUTO CDP"; + compatible = "qcom,apq8096-cdp", "qcom,apq8096", "qcom,cdp"; + qcom,msm-id = <316 0x10000>; + qcom,board-id = <0x03010001 0>; +}; + +&spi_9 { + status = "ok"; + can-controller@0 { + compatible = "renesas,rh850"; + reg = <0>; + interrupt-parent = <&tlmm>; + interrupts = <127 0>; + spi-max-frequency = <5000000>; + }; +}; + +&soc { + qcom,msm-thermal { + qcom,hotplug-temp = <115>; + qcom,hotplug-temp-hysteresis = <25>; + qcom,therm-reset-temp = <119>; + }; +}; diff --git a/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp-lite.dts b/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp-lite.dts new file mode 100644 index 000000000000..9c4ff9f184e7 --- /dev/null +++ b/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp-lite.dts @@ -0,0 +1,86 @@ +/* Copyright (c) 2015-2016, 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. + */ + +/dts-v1/; + +#include <dt-bindings/gpio/gpio.h> +#include "apq8096pro.dtsi" +#include "msm8996-pm8994.dtsi" +#include "msm8996-agave-adp.dtsi" +#include "msm8996pro-auto.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. APQ 8096pro V1.1 AUTO ADP LITE"; + compatible = "qcom,apq8096-adp", "qcom,msm8996", "qcom,adp"; + qcom,msm-id = <316 0x10001>; + qcom,board-id = <0x03010019 0>; +}; + +&spi_9 { + status = "disabled"; +}; + +&soc { + qcom,msm-ssc-sensors { + status = "disabled"; + }; + + qcom,msm-thermal { + qcom,hotplug-temp = <115>; + qcom,hotplug-temp-hysteresis = <25>; + qcom,therm-reset-temp = <119>; + }; + + i2c@75b6000 { /* BLSP8 */ + /* ADV7533 HDMI Bridge Chip removed on ADP Lite */ + adv7533@3d { + status = "disabled"; + }; + adv7533@39 { + status = "disabled"; + }; + }; +}; + +&pil_modem { + pinctrl-names = "default"; + pinctrl-0 = <&modem_mux>; +}; + +&slim_msm { + status = "disabled"; +}; + +&pm8994_mpps { + mpp@a500 { /* MPP 6 */ + qcom,mode = <1>; /* Digital output */ + qcom,output-type = <0>; /* CMOS logic */ + qcom,vin-sel = <2>; /* S4 1.8V */ + qcom,src-sel = <0>; /* Constant */ + qcom,master-en = <1>; /* Enable GPIO */ + status = "okay"; + }; +}; + +&sdhc_2 { + cd-gpios = <&tlmm 38 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_cd_on_sbc>; + pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off + &sdc2_cd_on_sbc>; +}; + +&i2c_7 { + silabs4705@11 { /* SiLabs FM chip, slave id 0x11*/ + status = "disabled"; + }; +}; + diff --git a/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp.dts b/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp.dts new file mode 100644 index 000000000000..497f3f10fe24 --- /dev/null +++ b/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-adp.dts @@ -0,0 +1,88 @@ +/* Copyright (c) 2015-2016, 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. + */ + +/dts-v1/; + +#include <dt-bindings/gpio/gpio.h> +#include "apq8096pro-v1.1.dtsi" +#include "msm8996-pm8994.dtsi" +#include "msm8996-agave-adp.dtsi" +#include "msm8996pro-auto.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM 8996pro AUTO ADP"; + compatible = "qcom,apq8096-adp", "qcom,msm8996", "qcom,adp"; + qcom,msm-id = <316 0x10001>; + qcom,board-id = <0x02010019 0>; +}; + +&spi_9 { + status = "ok"; + can-controller@0 { + compatible = "renesas,rh850"; + reg = <0>; + interrupt-parent = <&tlmm>; + interrupts = <122 0>; + spi-max-frequency = <5000000>; + }; +}; + +&soc { + qcom,msm-ssc-sensors { + status = "disabled"; + }; + + qcom,msm-thermal { + qcom,hotplug-temp = <115>; + qcom,hotplug-temp-hysteresis = <25>; + qcom,therm-reset-temp = <119>; + }; + + qcom,adv7481@70 { + qcom,cam-vreg-min-voltage = <1300000 0 1800000>; + qcom,cam-vreg-max-voltage = <1300000 0 1800000>; + }; +}; + +&pil_modem { + pinctrl-names = "default"; + pinctrl-0 = <&modem_mux>; +}; + +&slim_msm { + status = "disabled"; +}; + +&pm8994_mpps { + mpp@a500 { /* MPP 6 */ + qcom,mode = <1>; /* Digital output */ + qcom,output-type = <0>; /* CMOS logic */ + qcom,vin-sel = <2>; /* S4 1.8V */ + qcom,src-sel = <0>; /* Constant */ + qcom,master-en = <1>; /* Enable GPIO */ + status = "okay"; + }; +}; + +&sdhc_2 { + cd-gpios = <&tlmm 38 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_cd_on_sbc>; + pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off + &sdc2_cd_on_sbc>; +}; + +&i2c_7 { + silabs4705@11 { /* SiLabs FM chip, slave id 0x11*/ + status = "disabled"; + }; +}; + diff --git a/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-cdp.dts b/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-cdp.dts new file mode 100644 index 000000000000..2c54dfe19e18 --- /dev/null +++ b/arch/arm/boot/dts/qcom/apq8096pro-v1.1-auto-cdp.dts @@ -0,0 +1,37 @@ +/* Copyright (c) 2015-2016, 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. + */ + +/dts-v1/; + +#include "apq8096pro-v1.1.dtsi" +#include "msm8996-pm8994.dtsi" +#include "msm8996-auto-cdp.dtsi" +#include "msm8996pro-auto.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. APQ 8096 pro v1.1 AUTO CDP"; + compatible = "qcom,msm8996-cdp", "qcom,msm8996", "qcom,cdp"; + qcom,msm-id = <316 0x10001>; + qcom,board-id = <0x03010001 0>; +}; + +&spi_9 { + status = "ok"; + can-controller@0 { + compatible = "renesas,rh850"; + reg = <0>; + interrupt-parent = <&tlmm>; + interrupts = <127 0>; + spi-max-frequency = <5000000>; + }; +}; + diff --git a/arch/arm/boot/dts/qcom/apq8096pro-v1.1.dtsi b/arch/arm/boot/dts/qcom/apq8096pro-v1.1.dtsi new file mode 100644 index 000000000000..043309f47bc6 --- /dev/null +++ b/arch/arm/boot/dts/qcom/apq8096pro-v1.1.dtsi @@ -0,0 +1,34 @@ +/* Copyright (c) 2014-2016, 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. + */ + +/* + * As a general rule, only version-specific property overrides should be placed + * inside this file. Common device definitions should be placed inside the + * msm8996.dtsi file. + */ + +#include "msm8996pro-v1.1.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. APQ 8096 pro v1.1"; + qcom,msm-id = <312 0x10001>; +}; + +&soc { + qcom,rmnet-ipa { + status = "disabled"; + }; +}; + +&ipa_hw { + status = "disabled"; +}; diff --git a/arch/arm/boot/dts/qcom/apq8098-cdp.dts b/arch/arm/boot/dts/qcom/apq8098-cdp.dts index c8011ff957c0..54a41ab04498 100644 --- a/arch/arm/boot/dts/qcom/apq8098-cdp.dts +++ b/arch/arm/boot/dts/qcom/apq8098-cdp.dts @@ -13,6 +13,7 @@ /dts-v1/; #include "apq8098.dtsi" +#include "msm8998-mdss-panels.dtsi" #include "msm8998-cdp.dtsi" / { diff --git a/arch/arm/boot/dts/qcom/apq8098-mtp.dts b/arch/arm/boot/dts/qcom/apq8098-mtp.dts index 7af82a91e935..afb0f7b3fb91 100644 --- a/arch/arm/boot/dts/qcom/apq8098-mtp.dts +++ b/arch/arm/boot/dts/qcom/apq8098-mtp.dts @@ -13,6 +13,7 @@ /dts-v1/; #include "apq8098.dtsi" +#include "msm8998-mdss-panels.dtsi" #include "msm8998-mtp.dtsi" / { diff --git a/arch/arm/boot/dts/qcom/apq8098-v2-cdp.dts b/arch/arm/boot/dts/qcom/apq8098-v2-cdp.dts index b01c85bdbac6..764847846a5b 100644 --- a/arch/arm/boot/dts/qcom/apq8098-v2-cdp.dts +++ b/arch/arm/boot/dts/qcom/apq8098-v2-cdp.dts @@ -13,6 +13,7 @@ /dts-v1/; #include "apq8098-v2.dtsi" +#include "msm8998-mdss-panels.dtsi" #include "msm8998-cdp.dtsi" / { diff --git a/arch/arm/boot/dts/qcom/apq8098-v2-mtp.dts b/arch/arm/boot/dts/qcom/apq8098-v2-mtp.dts index a6e6f15bfa34..77bae625e241 100644 --- a/arch/arm/boot/dts/qcom/apq8098-v2-mtp.dts +++ b/arch/arm/boot/dts/qcom/apq8098-v2-mtp.dts @@ -13,6 +13,7 @@ /dts-v1/; #include "apq8098-v2.dtsi" +#include "msm8998-mdss-panels.dtsi" #include "msm8998-mtp.dtsi" / { diff --git a/arch/arm/boot/dts/qcom/apq8098-v2-qrd-skuk-hdk.dts b/arch/arm/boot/dts/qcom/apq8098-v2-qrd-skuk-hdk.dts index 7aeb8e36cca2..2207ab694917 100644 --- a/arch/arm/boot/dts/qcom/apq8098-v2-qrd-skuk-hdk.dts +++ b/arch/arm/boot/dts/qcom/apq8098-v2-qrd-skuk-hdk.dts @@ -14,6 +14,7 @@ /dts-v1/; #include "apq8098-v2.dtsi" +#include "msm8998-mdss-panels.dtsi" #include "msm8998-qrd-skuk-hdk.dtsi" / { diff --git a/arch/arm/boot/dts/qcom/apq8098-v2-qrd.dts b/arch/arm/boot/dts/qcom/apq8098-v2-qrd.dts index 0b808e47c915..9e7b88a2d78c 100644 --- a/arch/arm/boot/dts/qcom/apq8098-v2-qrd.dts +++ b/arch/arm/boot/dts/qcom/apq8098-v2-qrd.dts @@ -13,6 +13,7 @@ /dts-v1/; #include "apq8098-v2.dtsi" +#include "msm8998-mdss-panels.dtsi" #include "msm8998-qrd.dtsi" / { diff --git a/arch/arm/boot/dts/qcom/apq8098-v2.1-cdp.dts b/arch/arm/boot/dts/qcom/apq8098-v2.1-cdp.dts index e41a66eff103..a24835e59249 100644 --- a/arch/arm/boot/dts/qcom/apq8098-v2.1-cdp.dts +++ b/arch/arm/boot/dts/qcom/apq8098-v2.1-cdp.dts @@ -13,6 +13,7 @@ /dts-v1/; #include "apq8098-v2.1.dtsi" +#include "msm8998-mdss-panels.dtsi" #include "msm8998-cdp.dtsi" / { diff --git a/arch/arm/boot/dts/qcom/apq8098-v2.1-mediabox.dts b/arch/arm/boot/dts/qcom/apq8098-v2.1-mediabox.dts index d9b1cb8741eb..03e1e05a3739 100644 --- a/arch/arm/boot/dts/qcom/apq8098-v2.1-mediabox.dts +++ b/arch/arm/boot/dts/qcom/apq8098-v2.1-mediabox.dts @@ -13,6 +13,7 @@ /dts-v1/; #include "apq8098-v2.1.dtsi" +#include "msm8998-mdss-panels.dtsi" #include "msm8998-cdp.dtsi" / { @@ -34,10 +35,6 @@ qcom,mdss-pref-prim-intf = "hdmi"; }; -&msm_gpu { - dma-coherent; -}; - &sde_hdmi { qcom,display-type = "primary"; }; diff --git a/arch/arm/boot/dts/qcom/apq8098-v2.1-mtp.dts b/arch/arm/boot/dts/qcom/apq8098-v2.1-mtp.dts index 5684e1849b0d..93887a39eecf 100644 --- a/arch/arm/boot/dts/qcom/apq8098-v2.1-mtp.dts +++ b/arch/arm/boot/dts/qcom/apq8098-v2.1-mtp.dts @@ -13,6 +13,7 @@ /dts-v1/; #include "apq8098-v2.1.dtsi" +#include "msm8998-mdss-panels.dtsi" #include "msm8998-mtp.dtsi" / { diff --git a/arch/arm/boot/dts/qcom/apq8098-v2.1-qrd.dts b/arch/arm/boot/dts/qcom/apq8098-v2.1-qrd.dts index 0f315d7c893d..fb5e4579abb6 100644 --- a/arch/arm/boot/dts/qcom/apq8098-v2.1-qrd.dts +++ b/arch/arm/boot/dts/qcom/apq8098-v2.1-qrd.dts @@ -13,6 +13,7 @@ /dts-v1/; #include "apq8098-v2.1.dtsi" +#include "msm8998-mdss-panels.dtsi" #include "msm8998-qrd.dtsi" / { diff --git a/arch/arm/boot/dts/qcom/dsi-panel-nt36850-truly-dualmipi-wqhd-cmd.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-nt36850-truly-dualmipi-wqhd-cmd.dtsi index 7774a28ff495..1d7836ca4759 100644 --- a/arch/arm/boot/dts/qcom/dsi-panel-nt36850-truly-dualmipi-wqhd-cmd.dtsi +++ b/arch/arm/boot/dts/qcom/dsi-panel-nt36850-truly-dualmipi-wqhd-cmd.dtsi @@ -192,8 +192,8 @@ 15 01 00 00 00 00 02 0b 55 15 01 00 00 00 00 02 0c 14 15 01 00 00 00 00 02 0d 28 - 15 01 00 00 00 00 02 0e 00 - 15 01 00 00 00 00 02 0f 00 + 15 01 00 00 00 00 02 0e 40 + 15 01 00 00 00 00 02 0f 80 15 01 00 00 00 00 02 10 00 15 01 00 00 00 00 02 11 22 15 01 00 00 00 00 02 12 0a @@ -208,17 +208,17 @@ 15 01 00 00 00 00 02 1d 00 15 01 00 00 00 00 02 1e 80 15 01 00 00 00 00 02 1f 00 - 15 01 00 00 00 00 02 20 00 + 15 01 00 00 00 00 02 20 03 15 01 00 00 00 00 02 21 03 - 15 01 00 00 00 00 02 22 22 + 15 01 00 00 00 00 02 22 25 15 01 00 00 00 00 02 23 25 15 01 00 00 00 00 02 24 00 15 01 00 00 00 00 02 25 a7 - 15 01 00 00 00 00 02 26 00 + 15 01 00 00 00 00 02 26 80 15 01 00 00 00 00 02 27 a5 15 01 00 00 00 00 02 28 06 15 01 00 00 00 00 02 29 85 - 15 01 00 00 00 00 02 2a 3f + 15 01 00 00 00 00 02 2a 30 15 01 00 00 00 00 02 2b 97 15 01 00 00 00 00 02 2f 25 15 01 00 00 00 00 02 30 26 @@ -240,7 +240,7 @@ 15 01 00 00 00 00 02 45 00 15 01 00 00 00 00 02 46 00 15 01 00 00 00 00 02 47 00 - 15 01 00 00 00 00 02 48 00 + 15 01 00 00 00 00 02 48 03 15 01 00 00 00 00 02 49 03 15 01 00 00 00 00 02 4a 00 15 01 00 00 00 00 02 4b 00 @@ -250,7 +250,7 @@ 15 01 00 00 00 00 02 4f 4c 15 01 00 00 00 00 02 50 0d 15 01 00 00 00 00 02 51 0e - 15 01 00 00 00 00 02 52 23 + 15 01 00 00 00 00 02 52 20 15 01 00 00 00 00 02 53 97 15 01 00 00 00 00 02 54 4b 15 01 00 00 00 00 02 55 4c diff --git a/arch/arm/boot/dts/qcom/dsi-panel-rm67195-amoled-fhd-cmd.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-rm67195-amoled-fhd-cmd.dtsi index 7d3e3d362946..6ed6a1b3da6c 100644 --- a/arch/arm/boot/dts/qcom/dsi-panel-rm67195-amoled-fhd-cmd.dtsi +++ b/arch/arm/boot/dts/qcom/dsi-panel-rm67195-amoled-fhd-cmd.dtsi @@ -37,6 +37,7 @@ qcom,mdss-dsi-color-order = "rgb_swap_rgb"; qcom,mdss-dsi-on-command = [ 15 01 00 00 00 00 02 fe 0d + 15 01 00 00 00 00 02 0b c0 15 01 00 00 00 00 02 42 00 15 01 00 00 00 00 02 18 08 15 01 00 00 00 00 02 08 41 diff --git a/arch/arm/boot/dts/qcom/external-mdm9640.dtsi b/arch/arm/boot/dts/qcom/external-mdm9640.dtsi new file mode 100644 index 000000000000..4c0170a70bf5 --- /dev/null +++ b/arch/arm/boot/dts/qcom/external-mdm9640.dtsi @@ -0,0 +1,52 @@ +/* Copyright (c) 2017, 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. + */ + +&soc { + mdm0: qcom,mdm0 { + compatible = "qcom,ext-mdm9x45"; + cell-index = <0>; + #address-cells = <0>; + interrupt-parent = <&mdm0>; + #interrupt-cells = <1>; + interrupt-map-mask = <0xffffffff>; + interrupt-names = + "err_fatal_irq", + "status_irq"; + status = "disabled"; + }; + + mdm1: qcom,mdm1 { + compatible = "qcom,ext-mdm9x45"; + cell-index = <0>; + #address-cells = <0>; + interrupt-parent = <&mdm1>; + #interrupt-cells = <1>; + interrupt-map-mask = <0xffffffff>; + interrupt-names = + "err_fatal_irq", + "status_irq"; + status = "disabled"; + }; + + mdm2: qcom,mdm2 { + compatible = "qcom,ext-mdm9x45"; + cell-index = <0>; + #address-cells = <0>; + interrupt-parent = <&mdm2>; + #interrupt-cells = <1>; + interrupt-map-mask = <0xffffffff>; + interrupt-names = + "err_fatal_irq", + "status_irq"; + status = "disabled"; + }; +}; diff --git a/arch/arm/boot/dts/qcom/msm-arm-smmu-8998.dtsi b/arch/arm/boot/dts/qcom/msm-arm-smmu-8998.dtsi index ecfff13f9355..da28e56bc2df 100644 --- a/arch/arm/boot/dts/qcom/msm-arm-smmu-8998.dtsi +++ b/arch/arm/boot/dts/qcom/msm-arm-smmu-8998.dtsi @@ -176,4 +176,16 @@ */ iommus = <&mmss_smmu 42>; }; + + iommu_coherent_test_device { + compatible = "iommu-debug-test"; + /* + * 43 shouldn't be used by anyone on the mmss_smmu. We just + * need _something_ here to get this node recognized by the + * SMMU driver. Our test uses ATOS, which doesn't use SIDs + * anyways, so using a dummy value is ok. + */ + iommus = <&mmss_smmu 43>; + dma-coherent; + }; }; diff --git a/arch/arm/boot/dts/qcom/msm-pm660.dtsi b/arch/arm/boot/dts/qcom/msm-pm660.dtsi index f20238123bca..9bdaedd72585 100644 --- a/arch/arm/boot/dts/qcom/msm-pm660.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pm660.dtsi @@ -41,6 +41,7 @@ interrupt-names = "kpdpwr", "resin", "resin-bark", "kpdpwr-resin-bark"; qcom,pon-dbc-delay = <15625>; + qcom,kpdpwr-sw-debounce; qcom,system-reset; qcom,store-hard-reset-reason; diff --git a/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi b/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi index c1fa33f5ca32..fc26c16f6bf5 100644 --- a/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi @@ -44,6 +44,258 @@ status = "ok"; }; +&pcie0 { + qcom,phy-sequence = <0x404 0x01 0x00 + 0x034 0x1c 0x00 + 0x038 0x10 0x00 + 0x174 0x33 0x00 + 0x194 0x06 0x00 + 0x0c8 0x42 0x00 + 0x128 0x00 0x00 + 0x144 0xff 0x00 + 0x148 0x1f 0x00 + 0x178 0x01 0x00 + 0x19c 0x01 0x00 + 0x18c 0x00 0x00 + 0x184 0x0a 0x00 + 0x00c 0x09 0x00 + 0x0d0 0x82 0x00 + 0x0e4 0x03 0x00 + 0x0e0 0x55 0x00 + 0x0dc 0x55 0x00 + 0x054 0x00 0x00 + 0x050 0x1a 0x00 + 0x04c 0x0a 0x00 + 0x174 0x33 0x00 + 0x03c 0x02 0x00 + 0x040 0x1f 0x00 + 0x0ac 0x04 0x00 + 0x078 0x0b 0x00 + 0x084 0x16 0x00 + 0x090 0x28 0x00 + 0x10c 0x00 0x00 + 0x108 0x80 0x00 + 0x010 0x01 0x00 + 0x01c 0x31 0x00 + 0x020 0x01 0x00 + 0x014 0x02 0x00 + 0x018 0x00 0x00 + 0x024 0x2f 0x00 + 0x028 0x19 0x00 + 0x0c4 0x15 0x00 + 0x070 0x0f 0x00 + 0x048 0x0f 0x00 + 0x074 0x19 0x00 + 0x038 0x10 0x00 + 0x178 0x00 0x00 + 0x0c4 0x40 0x00 + 0x400 0x00 0x00 + 0x408 0x03 0x00>; + + qcom,port-phy-sequence = <0x1068 0x45 0x00 + 0x1094 0x06 0x00 + 0x1310 0x1c 0x00 + 0x1318 0x17 0x00 + 0x12d8 0x01 0x00 + 0x12dc 0x00 0x00 + 0x12e0 0xdb 0x00 + 0x1320 0x18 0x00 + 0x121c 0x04 0x00 + 0x1210 0x04 0x00 + 0x1458 0x4c 0x00 + 0x14a0 0x00 0x00 + 0x14a4 0x01 0x00 + 0x14a8 0x05 0x00 + 0x1248 0x4b 0x00 + 0x131c 0x14 0x00 + 0x1454 0x05 0x00 + 0x1404 0x02 0x00 + 0x146c 0x00 0x00 + 0x1460 0xa3 0x00 + 0x1318 0x19 0x00 + 0x1428 0x0e 0x00 + 0x1054 0x08 0x00 + 0x14f8 0x04 0x00 + 0x14ec 0x06 0x00 + 0x104c 0x2e 0x00 + 0x1404 0x03 0x0a + 0x1400 0x00 0x00 + 0x1408 0x0a 0x00>; + + /delete-property/ qcom,l1-supported; + /delete-property/ qcom,l1ss-supported; + /delete-property/ qcom,aux-clk-sync; +}; + +&pcie1 { + qcom,phy-sequence = <0x404 0x01 0x00 + 0x034 0x1c 0x00 + 0x038 0x10 0x00 + 0x174 0x33 0x00 + 0x194 0x06 0x00 + 0x0c8 0x42 0x00 + 0x128 0x00 0x00 + 0x144 0xff 0x00 + 0x148 0x1f 0x00 + 0x178 0x01 0x00 + 0x19c 0x01 0x00 + 0x18c 0x00 0x00 + 0x184 0x0a 0x00 + 0x00c 0x09 0x00 + 0x0d0 0x82 0x00 + 0x0e4 0x03 0x00 + 0x0e0 0x55 0x00 + 0x0dc 0x55 0x00 + 0x054 0x00 0x00 + 0x050 0x1a 0x00 + 0x04c 0x0a 0x00 + 0x174 0x33 0x00 + 0x03c 0x02 0x00 + 0x040 0x1f 0x00 + 0x0ac 0x04 0x00 + 0x078 0x0b 0x00 + 0x084 0x16 0x00 + 0x090 0x28 0x00 + 0x10c 0x00 0x00 + 0x108 0x80 0x00 + 0x010 0x01 0x00 + 0x01c 0x31 0x00 + 0x020 0x01 0x00 + 0x014 0x02 0x00 + 0x018 0x00 0x00 + 0x024 0x2f 0x00 + 0x028 0x19 0x00 + 0x0c4 0x15 0x00 + 0x070 0x0f 0x00 + 0x048 0x0f 0x00 + 0x074 0x19 0x00 + 0x038 0x10 0x00 + 0x178 0x00 0x00 + 0x0c4 0x40 0x00 + 0x400 0x00 0x00 + 0x408 0x03 0x00>; + + qcom,port-phy-sequence = <0x2068 0x45 0x00 + 0x2094 0x06 0x00 + 0x2310 0x1c 0x00 + 0x2318 0x17 0x00 + 0x22d8 0x01 0x00 + 0x22dc 0x00 0x00 + 0x22e0 0xdb 0x00 + 0x2320 0x18 0x00 + 0x221c 0x04 0x00 + 0x2210 0x04 0x00 + 0x2458 0x4c 0x00 + 0x24a0 0x00 0x00 + 0x24a4 0x01 0x00 + 0x24a8 0x05 0x00 + 0x2248 0x4b 0x00 + 0x231c 0x14 0x00 + 0x2454 0x05 0x00 + 0x2404 0x02 0x00 + 0x246c 0x00 0x00 + 0x2460 0xa3 0x00 + 0x2318 0x19 0x00 + 0x2428 0x0e 0x00 + 0x2054 0x08 0x00 + 0x24f8 0x04 0x00 + 0x24ec 0x06 0x00 + 0x204c 0x2e 0x00 + 0x2404 0x03 0x0a + 0x2400 0x00 0x00 + 0x2408 0x0a 0x00>; + + qcom,msi-gicm-addr = <0x09BD0040>; + qcom,msi-gicm-base = <0x240>; + + /delete-property/ qcom,l1-supported; + /delete-property/ qcom,l1ss-supported; + /delete-property/ qcom,aux-clk-sync; +}; + +&pcie2 { + qcom,phy-sequence = <0x404 0x01 0x00 + 0x034 0x1c 0x00 + 0x038 0x10 0x00 + 0x174 0x33 0x00 + 0x194 0x06 0x00 + 0x0c8 0x42 0x00 + 0x128 0x00 0x00 + 0x144 0xff 0x00 + 0x148 0x1f 0x00 + 0x178 0x01 0x00 + 0x19c 0x01 0x00 + 0x18c 0x00 0x00 + 0x184 0x0a 0x00 + 0x00c 0x09 0x00 + 0x0d0 0x82 0x00 + 0x0e4 0x03 0x00 + 0x0e0 0x55 0x00 + 0x0dc 0x55 0x00 + 0x054 0x00 0x00 + 0x050 0x1a 0x00 + 0x04c 0x0a 0x00 + 0x174 0x33 0x00 + 0x03c 0x02 0x00 + 0x040 0x1f 0x00 + 0x0ac 0x04 0x00 + 0x078 0x0b 0x00 + 0x084 0x16 0x00 + 0x090 0x28 0x00 + 0x10c 0x00 0x00 + 0x108 0x80 0x00 + 0x010 0x01 0x00 + 0x01c 0x31 0x00 + 0x020 0x01 0x00 + 0x014 0x02 0x00 + 0x018 0x00 0x00 + 0x024 0x2f 0x00 + 0x028 0x19 0x00 + 0x0c4 0x15 0x00 + 0x070 0x0f 0x00 + 0x048 0x0f 0x00 + 0x074 0x19 0x00 + 0x038 0x10 0x00 + 0x178 0x00 0x00 + 0x0c4 0x40 0x00 + 0x400 0x00 0x00 + 0x408 0x03 0x00>; + + qcom,port-phy-sequence = <0x3068 0x45 0x00 + 0x3094 0x06 0x00 + 0x3310 0x1c 0x00 + 0x3318 0x17 0x00 + 0x32d8 0x01 0x00 + 0x32dc 0x00 0x00 + 0x32e0 0xdb 0x00 + 0x3320 0x18 0x00 + 0x321c 0x04 0x00 + 0x3210 0x04 0x00 + 0x3458 0x4c 0x00 + 0x34a0 0x00 0x00 + 0x34a4 0x01 0x00 + 0x34a8 0x05 0x00 + 0x3248 0x4b 0x00 + 0x331c 0x14 0x00 + 0x3454 0x05 0x00 + 0x3404 0x02 0x00 + 0x346c 0x00 0x00 + 0x3460 0xa3 0x00 + 0x3318 0x19 0x00 + 0x3428 0x0e 0x00 + 0x3054 0x08 0x00 + 0x34f8 0x04 0x00 + 0x34ec 0x06 0x00 + 0x304c 0x2e 0x00 + 0x3404 0x03 0x0a + 0x3400 0x00 0x00 + 0x3408 0x0a 0x00>; + + /delete-property/ qcom,l1-supported; + /delete-property/ qcom,l1ss-supported; + /delete-property/ qcom,aux-clk-sync; +}; + &uartblsp2dm1 { status = "ok"; pinctrl-names = "default"; @@ -292,12 +544,68 @@ &mdss_hdmi_cec_suspend>; }; -#include "msm8996-mdss-panels.dtsi" +#include "msm8996-sde-display.dtsi" &mdss_mdp { qcom,mdss-pref-prim-intf = "dsi"; }; +&dsi_adv_7533_1 { + qcom,dsi-display-active; + qcom,dsi-panel = <&dsi_adv7533_1080p>; + + qcom,panel-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + + qcom,panel-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vdd"; + qcom,supply-min-voltage = <3300000>; + qcom,supply-max-voltage = <3300000>; + qcom,supply-enable-load = <100000>; + qcom,supply-disable-load = <100>; + }; + + qcom,panel-supply-entry@1 { + reg = <1>; + qcom,supply-name = "vddio"; + qcom,supply-min-voltage = <1800000>; + qcom,supply-max-voltage = <1800000>; + qcom,supply-enable-load = <100000>; + qcom,supply-disable-load = <100>; + }; + }; +}; + +&dsi_adv_7533_2 { + qcom,dsi-display-active; + qcom,dsi-panel = <&dsi_adv7533_1080p>; + + qcom,panel-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + + qcom,panel-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vdd"; + qcom,supply-min-voltage = <3300000>; + qcom,supply-max-voltage = <3300000>; + qcom,supply-enable-load = <100000>; + qcom,supply-disable-load = <100>; + }; + + qcom,panel-supply-entry@1 { + reg = <1>; + qcom,supply-name = "vddio"; + qcom,supply-min-voltage = <1800000>; + qcom,supply-max-voltage = <1800000>; + qcom,supply-enable-load = <100000>; + qcom,supply-disable-load = <100>; + }; + }; +}; + &mdss_dsi { hw-config = "dual_dsi"; }; @@ -478,6 +786,19 @@ }; &soc { + qcom,ntn_avb { + compatible = "qcom,ntn_avb"; + + ntn-rst-gpio = <&pm8994_gpios 13 0>; + + vdd-ntn-hsic-supply = <&pm8994_l25>; + vdd-ntn-pci-supply = <&pm8994_s4>; + vdd-ntn-io-supply = <&pm8994_s4>; + + qcom,ntn-rst-delay-msec = <100>; + qcom,ntn-rc-num = <1>; + }; + i2c@75ba000 { synaptics@20 { compatible = "synaptics,dsx"; @@ -499,6 +820,7 @@ clock-names = "iface_clk", "core_clk"; clocks = <&clock_gcc clk_gcc_blsp2_ahb_clk>, <&clock_gcc clk_gcc_blsp2_qup6_i2c_apps_clk>; + status = "disabled"; }; }; @@ -645,9 +967,17 @@ asoc-codec-names = "msm-stub-codec.1"; }; - usb_detect { + usb_detect: usb_detect { compatible = "qcom,gpio-usbdetect"; - qcom,vbus-det-irq = <&pm8994_gpios 17 0>; + qcom,vbus-det-gpio = <&pm8994_gpios 17 0>; + interrupt-parent = <&spmi_bus>; + interrupts = <0x0 0x9 0x0 IRQ_TYPE_NONE>; + interrupt-names ="pmic_id_irq"; + }; + + loopback1: qcom,msm-pcm-loopback-low-latency { + compatible = "qcom,msm-pcm-loopback"; + qcom,msm-pcm-loopback-low-latency; }; loopback1: qcom,msm-pcm-loopback-low-latency { @@ -788,14 +1118,14 @@ status = "okay"; }; - gpio@cc00 { /* GPIO 13 - HPH_EN0 */ - qcom,mode = <1>; - qcom,output-type = <0>; + gpio@cc00 { /* GPIO 13 - NTN_RST */ + qcom,mode = <1>; /* DIGITAL OUT */ + qcom,output-type = <0>; /* CMOS logic */ qcom,pull = <5>; - qcom,vin-sel = <2>; + qcom,vin-sel = <2>; /* 1.8 */ qcom,out-strength = <1>; - qcom,src-sel = <2>; - qcom,master-en = <1>; + qcom,src-sel = <0>; /* GPIO */ + qcom,master-en = <1>; /* Enable GPIO */ status = "okay"; }; @@ -849,15 +1179,7 @@ }; &usb3 { - interrupt-parent = <&usb3>; - interrupts = <0 1 2>; - #interrupt-cells = <1>; - interrupt-map-mask = <0x0 0xffffffff>; - interrupt-map = <0x0 0 &intc 0 133 0 - 0x0 1 &intc 0 180 0 - 0x0 2 &spmi_bus 0x0 0x0 0x9 0x0>; - interrupt-names = "hs_phy_irq", "pwr_event_irq", "pmic_id_irq"; - + extcon = <&usb_detect>; vbus_dwc3-supply = <&usb_otg_switch>; vdda33-supply = <&pm8994_l24>; vdda18-supply = <&pm8994_l12>; @@ -920,6 +1242,7 @@ pinctrl-1 = <&nfc_int_suspend &nfc_disable_suspend>; clocks = <&clock_gcc clk_bb_clk2_pin>; clock-names = "ref_clk"; + status = "disabled"; }; }; diff --git a/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi b/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi index a5a6bad16176..01f67b041b76 100644 --- a/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi @@ -454,6 +454,19 @@ }; &soc { + qcom,ntn_avb { + compatible = "qcom,ntn_avb"; + + ntn-rst-gpio = <&pm8994_gpios 13 0>; + + vdd-ntn-hsic-supply = <&pm8994_l25>; + vdd-ntn-pci-supply = <&pm8994_s4>; + vdd-ntn-io-supply = <&pm8994_s4>; + + qcom,ntn-rst-delay-msec = <100>; + qcom,ntn-rc-num = <1>; + }; + i2c@75ba000 { synaptics@20 { compatible = "synaptics,dsx"; @@ -820,14 +833,14 @@ status = "okay"; }; - gpio@cc00 { /* GPIO 13 - HPH_EN0 */ - qcom,mode = <1>; - qcom,output-type = <0>; + gpio@cc00 { /* GPIO 13 - NTN_RST */ + qcom,mode = <1>; /* DIGITAL OUT */ + qcom,output-type = <0>; /* CMOS logic */ qcom,pull = <5>; - qcom,vin-sel = <2>; + qcom,vin-sel = <2>; /* 1.8 */ qcom,out-strength = <1>; - qcom,src-sel = <2>; - qcom,master-en = <1>; + qcom,src-sel = <0>; /* GPIO */ + qcom,master-en = <1>; /* Enable GPIO */ status = "okay"; }; @@ -994,8 +1007,21 @@ /delete-property/ vin-supply; }; +&pcie0 { + /delete-property/ qcom,l1-supported; + /delete-property/ qcom,l1ss-supported; + /delete-property/ qcom,aux-clk-sync; +}; + &pcie1 { + qcom,msi-gicm-addr = <0x09BD0040>; + qcom,msi-gicm-base = <0x240>; + /delete-property/ qcom,boot-option; + /delete-property/ qcom,l1-supported; + /delete-property/ qcom,l1ss-supported; + /delete-property/ qcom,aux-clk-sync; + /delete-property/ qcom,ep-wakeirq; }; &pcie2 { @@ -1003,6 +1029,10 @@ wake-gpio = <&tlmm 54 0>; /delete-property/ qcom,boot-option; + /delete-property/ qcom,l1-supported; + /delete-property/ qcom,l1ss-supported; + /delete-property/ qcom,aux-clk-sync; + /delete-property/ qcom,ep-wakeirq; }; &wsa881x_211 { @@ -1021,87 +1051,4 @@ /delete-property/ qcom,spkr-sd-n-gpio; }; -&hl7509_en_vreg { - status = "ok"; -}; - -&hl7509_vreg { - status = "ok"; -}; - -&gfx_cpr { - vdd-supply = <&hl7509_vreg>; - qcom,cpr-step-quot-init-min = <20>; - qcom,cpr-step-quot-init-max = <26>; - qcom,voltage-step = <10000>; - /delete-property/ qcom,cpr-enable; -}; - -&gfx_vreg { - qcom,cpr-voltage-ceiling = - <600000 670000 670000 750000 830000 - 910000 960000 1020000>; - qcom,cpr-voltage-floor = - <600000 600000 600000 600000 600000 - 600000 600000 600000>; -}; - -&pm8994_l3 { - regulator-min-microvolt = <875000>; - regulator-max-microvolt = <875000>; - qcom,init-voltage = <875000>; -}; - -&pm8994_l11 { - regulator-min-microvolt = <850000>; - regulator-max-microvolt = <850000>; - qcom,init-voltage = <850000>; -}; - -&pm8994_l17 { - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - qcom,init-voltage = <1800000>; -}; - -&pm8994_l23 { - regulator-min-microvolt = <1100000>; - regulator-max-microvolt = <1100000>; - qcom,init-voltage = <1100000>; -}; - -&pm8994_l27 { - regulator-min-microvolt = <800000>; - regulator-max-microvolt = <800000>; - qcom,init-voltage = <800000>; -}; -&pm8994_l29 { - regulator-min-microvolt = <2500000>; - regulator-max-microvolt = <2500000>; - qcom,init-voltage = <2500000>; -}; - -&rpm_bus { - rpm-regulator-ldoa26 { - /delete-node/ pm8994_l26_corner; - /delete-node/ pm8994_l26_floor_corner; - - pm8994_l26: regulator-l26 { - regulator-min-microvolt = <1100000>; - regulator-max-microvolt = <1100000>; - qcom,init-voltage = <1100000>; - status = "okay"; - }; - }; - - rpm-regulator-ldoa31 { - status = "okay"; - pm8994_l31: regulator-l31 { - regulator-min-microvolt = <1100000>; - regulator-max-microvolt = <1100000>; - qcom,init-voltage = <1100000>; - status = "okay"; - }; - }; -}; diff --git a/arch/arm/boot/dts/qcom/msm8996-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msm8996-pinctrl.dtsi index 0b7be6a056f3..ff128acb376a 100644 --- a/arch/arm/boot/dts/qcom/msm8996-pinctrl.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-pinctrl.dtsi @@ -20,6 +20,20 @@ interrupt-controller; #interrupt-cells = <2>; + /* pin governing mux between QSPI and modem on auto boards */ + modem_mux: mdm_mux { + mux { + pins = "gpio121"; + function = "gpio"; + }; + + config { + pins = "gpio121"; + drive-strength = <2>; + bias-pull-up; + output-high; + }; + }; /* add pingrp for adv7533 */ pmx_adv7533: pmx_adv7533 { adv7533_0_int_active: adv7533_0_int_active { diff --git a/arch/arm/boot/dts/qcom/msm8996-pm.dtsi b/arch/arm/boot/dts/qcom/msm8996-pm.dtsi index c09b18b51656..74c964bc466d 100644 --- a/arch/arm/boot/dts/qcom/msm8996-pm.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-pm.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -161,7 +161,7 @@ qcom,psci-cpu-mode = <4>; qcom,latency-us = <80>; qcom,ss-power = <196>; - qcom,energy-overhead = <45300>; + qcom,energy-overhead = <62248>; qcom,time-overhead = <210>; qcom,reset-level = <LPM_RESET_LVL_PC>; @@ -250,7 +250,7 @@ qcom,psci-cpu-mode = <4>; qcom,latency-us = <80>; qcom,ss-power = <196>; - qcom,energy-overhead = <45300>; + qcom,energy-overhead = <62248>; qcom,time-overhead = <210>; qcom,reset-level = <LPM_RESET_LVL_PC>; @@ -276,6 +276,8 @@ qcom,gic-map = <2 216>, /* tsens_upper_lower_int */ <79 379>, /* qusb2phy_dmse_hv_prim */ <80 384>, /* qusb2phy_dmse_hv_sec */ + <81 379>, /* qusb2phy_dpse_hv_prim */ + <82 384>, /* qusb2phy_dpse_hv_sec */ <52 275>, /* qmp_usb3_lfps_rxterm_irq */ <87 358>, /* ee0_krait_hlos_spmi_periph_irq */ <0xff 16>, /* APCj_qgicdrCpu0HwFaultIrptReq */ @@ -314,6 +316,7 @@ <0xff 94>, /* osmmu_CIrpt[0] */ <0xff 97>, /* iommu_nonsecure_irq */ <0xff 99>, /* msm_iommu_pmon_nonsecure_irq */ + <0xff 101>, /* camss_jpeg_mmu_cirpt */ <0xff 102>, /* osmmu_CIrpt[1] */ <0xff 105>, /* iommu_pmon_nonsecure_irq */ <0xff 108>, /* osmmu_PMIrpt */ @@ -327,6 +330,7 @@ <0xff 132>, /* blsp1_qup_irq(5) */ <0xff 133>, /* blsp2_qup_irq(0) */ <0xff 134>, /* blsp2_qup_irq(1) */ + <0xff 135>, /* blsp2_qup_irq(2) */ <0xff 138>, /* blsp2_qup_irq(5) */ <0xff 140>, /* blsp1_uart_irq(1) */ <0xff 146>, /* blsp2_uart_irq(1) */ @@ -337,6 +341,8 @@ <0xff 165>, /* usb30_hs_phy_irq */ <0xff 166>, /* sdc1_pwr_cmd_irq */ <0xff 170>, /* sdcc_pwr_cmd_irq */ + <0xff 171>, /* usb20_hs_phy_irq */ + <0xff 172>, /* usb20_power_event_irq */ <0xff 173>, /* sdc1_irq[0] */ <0xff 174>, /* o_wcss_apss_smd_med */ <0xff 175>, /* o_wcss_apss_smd_low */ @@ -368,6 +374,7 @@ <0xff 208>, <0xff 210>, <0xff 211>, /* usb_dwc3_otg */ + <0xff 212>, /* usb30_power_event_irq */ <0xff 215>, /* o_bimc_intr(0) */ <0xff 224>, /* spdm_realtime_irq[1] */ <0xff 238>, /* crypto_bam_irq[0] */ @@ -386,6 +393,7 @@ <0xff 295>, /* camss_cpp_mmu_cirpt[0] */ <0xff 296>, /* camss_cpp_mmu_pmirpt */ <0xff 297>, /* ufs_intrq */ + <0xff 298>, /* camss_cpp_mmu_cirpt */ <0xff 302>, /* qdss_etrbytecnt_irq */ <0xff 310>, /* pcie20_1_int_pls_err */ <0xff 311>, /* pcie20_1_int_aer_legacy */ @@ -400,6 +408,7 @@ <0xff 330>, /* camss_irq4 */ <0xff 331>, /* camss_irq5 */ <0xff 332>, /* sps */ + <0xff 341>, /* camss_irq6 */ <0xff 346>, /* camss_irq8 */ <0xff 347>, /* camss_irq9 */ <0xff 352>, /* mdss_mmu_cirpt[0] */ diff --git a/arch/arm/boot/dts/qcom/msm8996-regulator.dtsi b/arch/arm/boot/dts/qcom/msm8996-regulator.dtsi index c70003a0a6dd..b86542a174da 100644 --- a/arch/arm/boot/dts/qcom/msm8996-regulator.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-regulator.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2016, 2017 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 @@ -1950,4 +1950,21 @@ regulator-ramp-delay = <500>; status = "disabled"; }; + + ncp6335d_vreg: ncp6335d-regulator@68 { + compatible = "onnn,ncp6335d-regulator"; + reg = <0x68>; + vin-supply = <&hl7509_en_vreg>; + onnn,vsel = <0>; + onnn,slew-ns = <2666>; + onnn,step-size = <6250>; + onnn,min-slew-ns = <333>; + onnn,max-slew-ns = <2666>; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1143750>; + onnn,min-setpoint = <600000>; + onnn,discharge-enable; + onnn,restore-reg; + status = "disabled"; + }; }; diff --git a/arch/arm/boot/dts/qcom/msm8996-sde-display.dtsi b/arch/arm/boot/dts/qcom/msm8996-sde-display.dtsi index 1396f27159df..061301f1c479 100644 --- a/arch/arm/boot/dts/qcom/msm8996-sde-display.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-sde-display.dtsi @@ -10,21 +10,7 @@ * GNU General Public License for more details. */ -#include "dsi-panel-toshiba-720p-video.dtsi" -#include "dsi-panel-sharp-dualmipi-wqxga-video.dtsi" -#include "dsi-panel-nt35597-dualmipi-wqxga-video.dtsi" -#include "dsi-panel-nt35597-dualmipi-wqxga-cmd.dtsi" -#include "dsi-panel-nt35597-dsc-wqxga-video.dtsi" -#include "dsi-panel-jdi-dualmipi-video.dtsi" -#include "dsi-panel-jdi-dualmipi-cmd.dtsi" -#include "dsi-panel-jdi-4k-dualmipi-video-nofbc.dtsi" -#include "dsi-panel-sim-video.dtsi" -#include "dsi-panel-sim-dualmipi-video.dtsi" -#include "dsi-panel-sim-cmd.dtsi" -#include "dsi-panel-sim-dualmipi-cmd.dtsi" -#include "dsi-panel-nt35597-dsc-wqxga-cmd.dtsi" -#include "dsi-panel-hx8379a-truly-fwvga-video.dtsi" -#include "dsi-panel-r69007-dualdsi-wqxga-cmd.dtsi" +#include "msm8996-mdss-panels.dtsi" #include "dsi-panel-jdi-1080p-video.dtsi" #include "dsi-panel-sharp-1080p-cmd.dtsi" @@ -122,9 +108,9 @@ qcom,dsi-panel = <&dsi_dual_sharp_video>; vddio-supply = <&pm8994_l14>; - lab-supply = <&lab_regulator>; - ibb-supply = <&ibb_regulator>; - qcom,dsi-display-active; + /delete-property/ lab-supply; + /delete-property/ ibb-supply; + /delete-property/ qcom,dsi-display-active; }; single_dsi_sim_vid: qcom,dsi-display@1 { @@ -145,8 +131,8 @@ qcom,dsi-panel = <&dsi_sim_vid>; vddio-supply = <&pm8994_l14>; - lab-supply = <&lab_regulator>; - ibb-supply = <&ibb_regulator>; + /delete-property/ lab-supply; + /delete-property/ ibb-supply; }; dsi_toshiba_720p_vid: qcom,dsi-display@2 { @@ -207,8 +193,8 @@ qcom,dsi-panel = <&dsi_sharp_1080_cmd>; vddio-supply = <&pm8994_l14>; vdd-supply = <&pm8994_l19>; - lab-supply = <&lab_regulator>; - ibb-supply = <&ibb_regulator>; + /delete-property/ lab-supply; + /delete-property/ ibb-supply; }; sde_wb: qcom,wb-display@0 { @@ -236,8 +222,8 @@ qcom,dsi-panel = <&dsi_dual_nt35597_cmd>; vddio-supply = <&pm8994_l14>; - lab-supply = <&lab_regulator>; - ibb-supply = <&ibb_regulator>; + /delete-property/ lab-supply; + /delete-property/ ibb-supply; }; dsi_dual_nt35597_video_1: qcom,dsi-display@6 { @@ -258,14 +244,61 @@ qcom,dsi-panel = <&dsi_dual_nt35597_video>; vddio-supply = <&pm8994_l14>; - lab-supply = <&lab_regulator>; - ibb-supply = <&ibb_regulator>; + /delete-property/ lab-supply; + /delete-property/ ibb-supply; + }; + + dsi_adv_7533_1: qcom,dsi-display@7 { + compatible = "qcom,dsi-display"; + label = "dsi_adv_7533_1"; + qcom,display-type = "secondary"; + + qcom,dsi-ctrl = <&mdss_dsi0>; + qcom,dsi-phy = <&mdss_dsi_phy0>; + clocks = <&clock_mmss clk_ext_byte0_clk_src>, + <&clock_mmss clk_ext_pclk0_clk_src>; + clock-names = "src_byte_clk", "src_pixel_clk"; + + qcom,dsi-panel = <&dsi_adv7533_720p>; + vddio-supply = <&pm8994_l14>; + qcom,bridge-index = <0>; + }; + + dsi_adv_7533_2: qcom,dsi-display@8 { + compatible = "qcom,dsi-display"; + label = "dsi_adv_7533_2"; + qcom,display-type = "tertiary"; + + qcom,dsi-ctrl = <&mdss_dsi1>; + qcom,dsi-phy = <&mdss_dsi_phy1>; + clocks = <&clock_mmss clk_ext_byte1_clk_src>, + <&clock_mmss clk_ext_pclk1_clk_src>; + clock-names = "src_byte_clk", "src_pixel_clk"; + + qcom,dsi-panel = <&dsi_adv7533_720p>; + vddio-supply = <&pm8994_l14>; + qcom,bridge-index = <1>; + }; + + msm_ext_disp: qcom,msm_ext_disp { + compatible = "qcom,msm-ext-disp"; + + ext_disp_audio_codec: qcom,msm-ext-disp-audio-codec-rx { + compatible = "qcom,msm-ext-disp-audio-codec-rx"; + qcom,msm_ext_disp = <&msm_ext_disp>; + }; + }; + + sde_hdmi: qcom,hdmi-display { + compatible = "qcom,hdmi-display"; + label = "sde_hdmi"; + qcom,display-type = "primary"; + qcom,msm_ext_disp = <&msm_ext_disp>; }; }; &mdss_mdp { - connectors = <&dsi_dual_sharp_video_1 - &sde_wb>; + connectors = <&mdss_hdmi &sde_hdmi &dsi_adv_7533_1 &dsi_adv_7533_2>; }; &dsi_dual_sharp_video { diff --git a/arch/arm/boot/dts/qcom/msm8996-sde.dtsi b/arch/arm/boot/dts/qcom/msm8996-sde.dtsi index cb33df82da0d..f0fa5dcb2224 100644 --- a/arch/arm/boot/dts/qcom/msm8996-sde.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-sde.dtsi @@ -185,21 +185,6 @@ iommus = <&mdp_smmu 0>; }; - smmu_rot_unsec: qcom,smmu_rot_unsec_cb { - compatible = "qcom,smmu_rot_unsec"; - iommus = <&rot_smmu 0>; - }; - - smmu_mdp_sec: qcom,smmu_mdp_sec_cb { - compatible = "qcom,smmu_mdp_sec"; - iommus = <&mdp_smmu 1>; - }; - - smmu_rot_sec: qcom,smmu_rot_sec_cb { - compatible = "qcom,smmu_rot_sec"; - iommus = <&rot_smmu 1>; - }; - /* data and reg bus scale settings */ qcom,sde-data-bus { qcom,msm-bus,name = "mdss_sde"; diff --git a/arch/arm/boot/dts/qcom/msm8996-v3-auto-adp.dts b/arch/arm/boot/dts/qcom/msm8996-v3-auto-adp.dts new file mode 100644 index 000000000000..68956d71b74d --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8996-v3-auto-adp.dts @@ -0,0 +1,83 @@ +/* Copyright (c) 2015-2016, 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. + */ + +/dts-v1/; + +#include <dt-bindings/gpio/gpio.h> +#include "msm8996-v3.dtsi" +#include "msm8996-pm8994.dtsi" +#include "msm8996-agave-adp.dtsi" +#include "msm8996v3-auto.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM 8996 v3 AUTO ADP"; + compatible = "qcom,msm8996-adp", "qcom,msm8996", "qcom,adp"; + qcom,msm-id = <310 0x30001>; + qcom,board-id = <0x02010019 0>; +}; + +&spi_9 { + status = "ok"; + can-controller@0 { + compatible = "renesas,rh850"; + reg = <0>; + interrupt-parent = <&tlmm>; + interrupts = <122 0>; + spi-max-frequency = <5000000>; + }; +}; + +&soc { + qcom,msm-ssc-sensors { + status = "disabled"; + }; + + qcom,msm-thermal { + qcom,hotplug-temp = <115>; + qcom,hotplug-temp-hysteresis = <25>; + qcom,therm-reset-temp = <119>; + }; +}; + +&pil_modem { + pinctrl-names = "default"; + pinctrl-0 = <&modem_mux>; +}; + +&slim_msm { + status = "disabled"; +}; + +&pm8994_mpps { + mpp@a500 { /* MPP 6 */ + qcom,mode = <1>; /* Digital output */ + qcom,output-type = <0>; /* CMOS logic */ + qcom,vin-sel = <2>; /* S4 1.8V */ + qcom,src-sel = <0>; /* Constant */ + qcom,master-en = <1>; /* Enable GPIO */ + status = "okay"; + }; +}; + +&sdhc_2 { + cd-gpios = <&tlmm 38 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_cd_on_sbc>; + pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off + &sdc2_cd_on_sbc>; +}; + +&i2c_7 { + silabs4705@11 { /* SiLabs FM chip, slave id 0x11*/ + status = "disabled"; + }; +}; + diff --git a/arch/arm/boot/dts/qcom/msm8996-v3-auto-cdp.dts b/arch/arm/boot/dts/qcom/msm8996-v3-auto-cdp.dts index 1c92a9a1a8c1..8ca2b30b3779 100644 --- a/arch/arm/boot/dts/qcom/msm8996-v3-auto-cdp.dts +++ b/arch/arm/boot/dts/qcom/msm8996-v3-auto-cdp.dts @@ -15,6 +15,7 @@ #include "msm8996-v3.dtsi" #include "msm8996-pm8994.dtsi" #include "msm8996-auto-cdp.dtsi" +#include "msm8996v3-auto.dtsi" / { model = "Qualcomm Technologies, Inc. MSM 8996 v3 AUTO CDP"; @@ -23,6 +24,19 @@ qcom,board-id = <0x03010001 0>; }; +&soc { + qcom,msm-thermal { + qcom,hotplug-temp = <115>; + qcom,hotplug-temp-hysteresis = <25>; + qcom,therm-reset-temp = <119>; + }; +}; + +&pil_modem { + pinctrl-names = "default"; + pinctrl-0 = <&modem_mux>; +}; + &spi_9 { status = "ok"; can-controller@0 { diff --git a/arch/arm/boot/dts/qcom/msm8996-v3-pm8004-agave-adp-lite.dts b/arch/arm/boot/dts/qcom/msm8996-v3-pm8004-agave-adp-lite.dts new file mode 100644 index 000000000000..eb8591b4ded7 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8996-v3-pm8004-agave-adp-lite.dts @@ -0,0 +1,27 @@ +/* Copyright (c) 2015-2016, 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. + */ + +/dts-v1/; + +#include "msm8996-v3.dtsi" +#include "msm8996-pm8994-pm8004.dtsi" +#include "msm8996-agave-adp.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM 8996 v3 + PM8004 ADP LITE"; + compatible = "qcom,msm8996-adp", "qcom,msm8996", "qcom,adp"; + qcom,board-id = <0x03010019 0>; +}; + +&spi_9 { + status = "disabled"; +}; diff --git a/arch/arm/boot/dts/qcom/msm8996.dtsi b/arch/arm/boot/dts/qcom/msm8996.dtsi index 398aee9d3ab8..1752256029e7 100644 --- a/arch/arm/boot/dts/qcom/msm8996.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996.dtsi @@ -21,6 +21,10 @@ qcom,pmic-id = <0x20009 0x2000A 0x0 0x0>; interrupt-parent = <&intc>; + chosen { + bootargs = "fpsimd.fpsimd_settings=1 app_setting.use_app_setting=1 app_setting.use_32bit_app_setting=1"; + }; + aliases { sdhc1 = &sdhc_1; /* SDC1 eMMC slot */ sdhc2 = &sdhc_2; /* SDC2 SD card slot */ @@ -298,13 +302,23 @@ intc: interrupt-controller@09bc0000 { compatible = "arm,gic-v3"; + reg = <0x9bc0000 0x10000>, /* GICD */ + <0x9c00000 0x100000>; /* GICR * 4 */ #interrupt-cells = <3>; + #address-cells = <1>; + #size-cells = <1>; + ranges; interrupt-controller; #redistributor-regions = <1>; redistributor-stride = <0x0 0x40000>; - reg = <0x09bc0000 0x10000>, /* GICD */ - <0x09c00000 0x100000>; /* GICR * 4 */ + interrupts = <1 9 4>; + + gic-its@09BE0000 { + compatible = "arm,gic-v3-its"; + msi-contoller; + reg = <0x9be0000 0x20000>; + }; }; timer { diff --git a/arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts b/arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts new file mode 100644 index 000000000000..668cb2844363 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8996pro-auto-adp-lite.dts @@ -0,0 +1,86 @@ +/* Copyright (c) 2015-2016, 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. + */ + +/dts-v1/; + +#include <dt-bindings/gpio/gpio.h> +#include "msm8996pro.dtsi" +#include "msm8996-pm8994.dtsi" +#include "msm8996-agave-adp.dtsi" +#include "msm8996pro-auto.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM 8996pro AUTO ADP LITE"; + compatible = "qcom,msm8996-adp", "qcom,msm8996", "qcom,adp"; + qcom,msm-id = <315 0x10000>; + qcom,board-id = <0x03010019 0>; +}; + +&spi_9 { + status = "disabled"; +}; + +&soc { + qcom,msm-ssc-sensors { + status = "disabled"; + }; + + qcom,msm-thermal { + qcom,hotplug-temp = <115>; + qcom,hotplug-temp-hysteresis = <25>; + qcom,therm-reset-temp = <119>; + }; + + i2c@75b6000 { /* BLSP8 */ + /* ADV7533 HDMI Bridge Chip removed on ADP Lite */ + adv7533@3d { + status = "disabled"; + }; + adv7533@39 { + status = "disabled"; + }; + }; +}; + +&pil_modem { + pinctrl-names = "default"; + pinctrl-0 = <&modem_mux>; +}; + +&slim_msm { + status = "disabled"; +}; + +&pm8994_mpps { + mpp@a500 { /* MPP 6 */ + qcom,mode = <1>; /* Digital output */ + qcom,output-type = <0>; /* CMOS logic */ + qcom,vin-sel = <2>; /* S4 1.8V */ + qcom,src-sel = <0>; /* Constant */ + qcom,master-en = <1>; /* Enable GPIO */ + status = "okay"; + }; +}; + +&sdhc_2 { + cd-gpios = <&tlmm 38 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_cd_on_sbc>; + pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off + &sdc2_cd_on_sbc>; +}; + +&i2c_7 { + silabs4705@11 { /* SiLabs FM chip, slave id 0x11*/ + status = "disabled"; + }; +}; + diff --git a/arch/arm/boot/dts/qcom/msm8996pro-auto-adp.dts b/arch/arm/boot/dts/qcom/msm8996pro-auto-adp.dts new file mode 100644 index 000000000000..1ab8ee9cd538 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8996pro-auto-adp.dts @@ -0,0 +1,83 @@ +/* Copyright (c) 2015-2016, 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. + */ + +/dts-v1/; + +#include <dt-bindings/gpio/gpio.h> +#include "msm8996pro.dtsi" +#include "msm8996-pm8994.dtsi" +#include "msm8996-agave-adp.dtsi" +#include "msm8996pro-auto.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM 8996pro AUTO ADP"; + compatible = "qcom,msm8996-adp", "qcom,msm8996", "qcom,adp"; + qcom,msm-id = <315 0x10000>; + qcom,board-id = <0x02010019 0>; +}; + +&spi_9 { + status = "ok"; + can-controller@0 { + compatible = "renesas,rh850"; + reg = <0>; + interrupt-parent = <&tlmm>; + interrupts = <122 0>; + spi-max-frequency = <5000000>; + }; +}; + +&soc { + qcom,msm-ssc-sensors { + status = "disabled"; + }; + + qcom,msm-thermal { + qcom,hotplug-temp = <115>; + qcom,hotplug-temp-hysteresis = <25>; + qcom,therm-reset-temp = <119>; + }; +}; + +&pil_modem { + pinctrl-names = "default"; + pinctrl-0 = <&modem_mux>; +}; + +&slim_msm { + status = "disabled"; +}; + +&pm8994_mpps { + mpp@a500 { /* MPP 6 */ + qcom,mode = <1>; /* Digital output */ + qcom,output-type = <0>; /* CMOS logic */ + qcom,vin-sel = <2>; /* S4 1.8V */ + qcom,src-sel = <0>; /* Constant */ + qcom,master-en = <1>; /* Enable GPIO */ + status = "okay"; + }; +}; + +&sdhc_2 { + cd-gpios = <&tlmm 38 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_cd_on_sbc>; + pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off + &sdc2_cd_on_sbc>; +}; + +&i2c_7 { + silabs4705@11 { /* SiLabs FM chip, slave id 0x11*/ + status = "disabled"; + }; +}; + diff --git a/arch/arm/boot/dts/qcom/msm8996pro-auto-cdp.dts b/arch/arm/boot/dts/qcom/msm8996pro-auto-cdp.dts new file mode 100644 index 000000000000..e104be7c2742 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8996pro-auto-cdp.dts @@ -0,0 +1,49 @@ +/* Copyright (c) 2015-2016, 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. + */ + +/dts-v1/; + +#include "msm8996pro.dtsi" +#include "msm8996-pm8994.dtsi" +#include "msm8996-auto-cdp.dtsi" +#include "msm8996pro-auto.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM 8996pro AUTO CDP"; + compatible = "qcom,msm8996-cdp", "qcom,msm8996", "qcom,cdp"; + qcom,msm-id = <315 0x10000>; + qcom,board-id = <0x03010001 0>; +}; + +&spi_9 { + status = "ok"; + can-controller@0 { + compatible = "renesas,rh850"; + reg = <0>; + interrupt-parent = <&tlmm>; + interrupts = <127 0>; + spi-max-frequency = <5000000>; + }; +}; + +&pil_modem { + pinctrl-names = "default"; + pinctrl-0 = <&modem_mux>; +}; + +&soc { + qcom,msm-thermal { + qcom,hotplug-temp = <115>; + qcom,hotplug-temp-hysteresis = <25>; + qcom,therm-reset-temp = <119>; + }; +}; diff --git a/arch/arm/boot/dts/qcom/msm8996pro-auto.dtsi b/arch/arm/boot/dts/qcom/msm8996pro-auto.dtsi new file mode 100644 index 000000000000..48d544e18889 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8996pro-auto.dtsi @@ -0,0 +1,461 @@ +/* Copyright (c) 2016-2017, 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 "msm8996v3-auto.dtsi" + +&gfx_vreg { + qcom,cpr-fuse-combos = <24>; + qcom,cpr-speed-bins = <3>; + qcom,cpr-speed-bin-corners = <8 8 8>; + qcom,cpr-corners = <8>; + + qcom,cpr-corner-fmax-map = + <2 4 6 8>; + + qcom,cpr-voltage-ceiling = + <600000 670000 670000 750000 830000 910000 960000 + 1020000>; + + qcom,cpr-voltage-floor = + <600000 600000 600000 600000 600000 600000 600000 + 600000>; + + qcom,mem-acc-voltage = + <1 1 1 1 2 2 2 2>; + + qcom,corner-frequencies = + <0 133000000 214000000 315000000 401800000 510000000 560000000 + 624000000>; + + qcom,cpr-target-quotients = + <0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>, + <0 0 0 0 0 0 185 179 291 299 304 319 0 0 0 0>, + <0 0 0 0 0 0 287 273 425 426 443 453 0 0 0 0>, + <0 0 0 0 0 0 414 392 584 576 608 612 0 0 0 0>, + <0 0 0 0 0 0 459 431 684 644 692 679 0 0 0 0>, + <0 0 0 0 0 0 577 543 798 768 823 810 0 0 0 0>, + <0 0 0 0 0 0 669 629 886 864 924 911 0 0 0 0>, + <0 0 0 0 0 0 771 725 984 970 1036 1024 0 0 0 0>; + + qcom,cpr-ro-scaling-factor = + <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>, + <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>, + <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>, + <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>, + <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>, + <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>, + <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>, + <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>; + + qcom,cpr-open-loop-voltage-fuse-adjustment = + <0 (-20000) (-20000) (-40000)>; + + qcom,cpr-closed-loop-voltage-adjustment = + <0 0 30000 10000 10000 45000 25000 25000>; + + qcom,cpr-floor-to-ceiling-max-range = + <0 70000 70000 75000 80000 90000 95000 100000>; + + qcom,cpr-fused-closed-loop-voltage-adjustment-map = + <0 2 2 2 2 0 0 4>; +}; + +&apcc_cpr { + /delete-property/ qcom,cpr-enable; +}; + +&apc0_pwrcl_vreg { + regulator-max-microvolt = <19>; + qcom,cpr-fuse-combos = <24>; + qcom,cpr-speed-bins = <3>; + qcom,cpr-speed-bin-corners = <19 19 19>; + qcom,cpr-corners = <19>; + + qcom,cpr-corner-fmax-map = + <3 6 9 14 19>; + + qcom,cpr-voltage-ceiling = + <670000 670000 670000 670000 670000 670000 745000 745000 + 745000 905000 905000 905000 905000 905000 1140000 1140000 + 1140000 1140000 1140000>; + + qcom,cpr-voltage-floor = + <470000 470000 470000 470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 470000 470000 470000 + 470000 470000 470000>; + + qcom,cpr-floor-to-ceiling-max-range = + <0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>; + + qcom,corner-frequencies = + <307200000 384000000 460800000 537600000 614400000 + 691200000 768000000 844800000 902400000 979200000 + 1056000000 1132800000 1209600000 1286400000 1363200000 + 1440000000 1516800000 1593600000 1785600000>; + + qcom,cpr-open-loop-voltage-fuse-adjustment = + <0 0 0 0 0 >; + + qcom,cpr-closed-loop-voltage-fuse-adjustment = + <0 0 0 0 0 >; + + qcom,cpr-open-loop-voltage-adjustment = + <0 0 0 0 0 0 0 0 0 (-2000) (-4000) (-6000) (-8000) (-10000) + (-11000) (-12000) (-12000) (-13000) (-15000)>; + + qcom,cpr-open-loop-voltage-min-diff = + <0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>; + + qcom,cpr-closed-loop-voltage-adjustment = + <0 0 0 0 0 0 0 0 0 (-2000) (-4000) (-6000) (-8000) (-10000) + (-11000) (-12000) (-12000) (-13000) (-15000)>; + + qcom,allow-aging-voltage-adjustment = <0>; + qcom,allow-aging-open-loop-voltage-adjustment = <0>; +}; + +&apc0_cbf_vreg { + qcom,cpr-fuse-combos = <24>; + qcom,cpr-speed-bins = <3>; + qcom,cpr-speed-bin-corners = <19 19 19>; + qcom,cpr-corners = <19>; + + qcom,cpr-corner-fmax-map = + <1 4 7 14 19>; + + qcom,cpr-voltage-ceiling = + <670000 670000 670000 670000 745000 745000 745000 905000 + 905000 905000 905000 905000 905000 905000 1140000 1140000 + 1140000 1140000 1140000>; + + qcom,cpr-voltage-floor = + <470000 470000 470000 470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 470000 470000 470000 + 470000 470000 470000>; + + qcom,cpr-floor-to-ceiling-max-range = + <0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>; + + qcom,corner-frequencies = + <192000000 307200000 384000000 441600000 537600000 + 614400000 691200000 768000000 844800000 902400000 + 979200000 1056000000 1132800000 1190400000 1286400000 + 1363200000 1440000000 1516800000 1593600000>; + + qcom,cpr-open-loop-voltage-fuse-adjustment = + <0 0 0 15000 0 >; + + qcom,cpr-closed-loop-voltage-fuse-adjustment = + <0 0 0 0 0 >; + + qcom,allow-aging-voltage-adjustment = <0>; + qcom,allow-aging-open-loop-voltage-adjustment = <0>; +}; + +&apc1_vreg { + qcom,cpr-fuse-combos = <24>; + qcom,cpr-speed-bins = <3>; + qcom,cpr-speed-bin-corners = <25 25 25>; + qcom,cpr-corners = <25>; + + qcom,cpr-corner-fmax-map = + <4 7 10 15 25>; + + qcom,cpr-voltage-ceiling = + <670000 670000 670000 670000 670000 670000 670000 745000 + 745000 745000 905000 905000 905000 905000 905000 1140000 + 1140000 1140000 1140000 1140000 1140000 1140000 1140000 1140000 + 1140000>; + + qcom,cpr-voltage-floor = + <470000 470000 470000 470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 470000 470000 470000 + 470000 470000 470000 470000 470000 470000 470000 470000 + 470000>; + + qcom,cpr-floor-to-ceiling-max-range = + <0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>; + + qcom,corner-frequencies = + <307200000 384000000 460800000 537600000 614400000 + 691200000 748800000 825600000 902400000 979200000 + 1056000000 1132800000 1209600000 1286400000 1363200000 + 1440000000 1516800000 1593600000 1670400000 1747200000 + 1824000000 1900800000 1977600000 2054400000 2150400000>; + + qcom,cpr-open-loop-voltage-fuse-adjustment = + <0 0 0 0 0 >; + + qcom,cpr-closed-loop-voltage-fuse-adjustment = + <0 0 0 0 0 >; + + qcom,cpr-open-loop-voltage-adjustment = + <0 0 0 0 0 0 0 0 0 0 (-2000) (-4000) (-6000) (-8000) (-10000) + (-10000) (-11000) (-11000) (-12000) (-12000) (-13000) (-13000) + (-14000) (-14000) (-15000)>; + + qcom,cpr-open-loop-voltage-min-diff = + <0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>; + + qcom,cpr-closed-loop-voltage-adjustment = + <0 0 0 0 0 0 0 0 0 0 (-2000) (-4000) (-6000) (-8000) (-10000) + (-10000) (-11000) (-11000) (-12000) (-12000) (-13000) (-13000) + (-14000) (-14000) (-15000)>; + + qcom,allow-aging-voltage-adjustment = <0>; + qcom,allow-aging-open-loop-voltage-adjustment = <0>; +}; + +&clock_cpu { + compatible = "qcom,cpu-clock-8996-auto"; + qcom,pwrcl-early-boot-freq = < 1286400000 >; + qcom,perfcl-early-boot-freq = < 1363200000 >; + + qcom,pwrcl-speedbin0-v0 = + < 0 0 >, + < 768000000 7 >, + < 844800000 8 >, + < 902400000 9 >, + < 979200000 10 >, + < 1056000000 11 >, + < 1132800000 12 >, + < 1209600000 13 >, + < 1286400000 14 >, + < 1363200000 15 >, + < 1440000000 16 >, + < 1516800000 17 >, + < 1593600000 18 >; + + qcom,pwrcl-speedbin1-v0 = + < 0 0 >, + < 768000000 7 >, + < 844800000 8 >, + < 902400000 9 >, + < 979200000 10 >, + < 1056000000 11 >, + < 1132800000 12 >, + < 1209600000 13 >, + < 1286400000 14 >, + < 1363200000 15 >, + < 1440000000 16 >, + < 1516800000 17 >, + < 1593600000 18 >; + + qcom,pwrcl-speedbin2-v0 = + < 0 0 >, + < 768000000 7 >, + < 844800000 8 >, + < 902400000 9 >, + < 979200000 10 >, + < 1056000000 11 >, + < 1132800000 12 >, + < 1209600000 13 >, + < 1286400000 14 >; + + qcom,perfcl-speedbin0-v0 = + < 0 0 >, + < 825600000 8 >, + < 902400000 9 >, + < 979200000 10 >, + < 1056000000 11 >, + < 1132800000 12 >, + < 1209600000 13 >, + < 1286400000 14 >, + < 1363200000 15 >, + < 1440000000 16 >, + < 1516800000 17 >, + < 1593600000 18 >, + < 1670400000 19 >, + < 1747200000 20 >, + < 1824000000 21 >, + < 1900800000 22 >, + < 1977600000 23 >, + < 2054400000 24 >; + + qcom,perfcl-speedbin1-v0 = + < 0 0 >, + < 825600000 8 >, + < 902400000 9 >, + < 979200000 10 >, + < 1056000000 11 >, + < 1132800000 12 >, + < 1209600000 13 >, + < 1286400000 14 >, + < 1363200000 15 >, + < 1440000000 16 >, + < 1516800000 17 >, + < 1593600000 18 >, + < 1670400000 19 >; + + qcom,perfcl-speedbin2-v0 = + < 0 0 >, + < 825600000 8 >, + < 902400000 9 >, + < 979200000 10 >, + < 1056000000 11 >, + < 1132800000 12 >, + < 1209600000 13 >, + < 1286400000 14 >, + < 1363200000 15 >, + < 1440000000 16 >, + < 1516800000 17 >; + + qcom,cbf-speedbin0-v0 = + < 0 0 >, + < 537600000 5 >, + < 614400000 6 >, + < 691200000 7 >, + < 768000000 8 >, + < 844800000 9 >, + < 902400000 10 >, + < 979200000 11 >, + < 1056000000 12 >, + < 1132800000 13 >, + < 1190400000 14 >, + < 1286400000 15 >, + < 1363200000 16 >, + < 1440000000 17 >, + < 1516800000 18 >, + < 1593600000 19 >; + + qcom,cbf-speedbin1-v0 = + < 0 0 >, + < 537600000 5 >, + < 614400000 6 >, + < 691200000 7 >, + < 768000000 8 >, + < 844800000 9 >, + < 902400000 10 >, + < 979200000 11 >, + < 1056000000 12 >, + < 1132800000 13 >, + < 1190400000 14 >, + < 1286400000 15 >, + < 1363200000 16 >, + < 1440000000 17 >, + < 1516800000 18 >, + < 1593600000 19 >; + + qcom,cbf-speedbin2-v0 = + < 0 0 >, + < 537600000 5 >, + < 614400000 6 >, + < 691200000 7 >, + < 768000000 8 >, + < 844800000 9 >, + < 902400000 10 >, + < 979200000 11 >, + < 1056000000 12 >, + < 1132800000 13 >, + < 1190400000 14 >; +}; + +&msm_cpufreq { + qcom,cpufreq-table-0 = + < 768000 >, + < 844800 >, + < 902400 >, + < 979200 >, + < 1056000 >, + < 1132800 >, + < 1209600 >, + < 1286400 >, + < 1363200 >, + < 1440000 >, + < 1516800 >, + < 1593600 >; + qcom,cpufreq-table-2 = + < 825600 >, + < 902400 >, + < 979200 >, + < 1056000 >, + < 1132800 >, + < 1209600 >, + < 1286400 >, + < 1363200 >, + < 1440000 >, + < 1516800 >, + < 1593600 >, + < 1670400 >, + < 1747200 >, + < 1824000 >, + < 1900800 >, + < 1977600 >, + < 2054400 >; +}; + +&m4m_cache { + freq-tbl-khz = + < 537600 >, + < 614400 >, + < 691200 >, + < 768000 >, + < 844800 >, + < 902400 >, + < 979200 >, + < 1056000 >, + < 1132800 >, + < 1190400 >, + < 1286400 >, + < 1363200 >, + < 1440000 >, + < 1516800 >, + < 1593600 >; +}; + +&devfreq_cpufreq { + mincpubw-cpufreq { + cpu-to-dev-map-0 = + < 1593600 1525 >; + cpu-to-dev-map-2 = + < 1977600 1525 >, + < 2054400 5195 >; + }; +}; + +&clock_gpu { + qcom,gfxfreq-speedbin0 = + < 0 0 0 >, + < 315000000 4 4 >, + < 401800000 5 5 >, + < 510000000 6 5 >, + < 560000000 7 7 >, + < 624000000 8 7 >; + + qcom,gfxfreq-mx-speedbin0 = + < 0 0 >, + < 315000000 4 >, + < 401800000 5 >, + < 510000000 5 >, + < 560000000 7 >, + < 624000000 7 >; + + qcom,gfxfreq-speedbin1 = + < 0 0 0 >, + < 315000000 4 4 >, + < 401800000 5 5 >, + < 510000000 6 5 >; + + qcom,gfxfreq-mx-speedbin1 = + < 0 0 >, + < 315000000 4 >, + < 401800000 5 >, + < 510000000 5 >; + + qcom,gfxfreq-speedbin2 = + < 0 0 0 >, + < 315000000 4 4 >; + + qcom,gfxfreq-mx-speedbin2 = + < 0 0 >, + < 315000000 4 >; +}; diff --git a/arch/arm/boot/dts/qcom/msm8996pro-v1.1-auto-cdp.dts b/arch/arm/boot/dts/qcom/msm8996pro-v1.1-auto-cdp.dts new file mode 100644 index 000000000000..06d040aa6bcb --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8996pro-v1.1-auto-cdp.dts @@ -0,0 +1,42 @@ +/* Copyright (c) 2015-2016, 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. + */ + +/dts-v1/; + +#include "msm8996pro-v1.1.dtsi" +#include "msm8996-pm8994.dtsi" +#include "msm8996-auto-cdp.dtsi" +#include "msm8996pro-auto.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM 8996pro v1.1 AUTO CDP"; + compatible = "qcom,msm8996-cdp", "qcom,msm8996", "qcom,cdp"; + qcom,msm-id = <315 0x10001>; + qcom,board-id = <0x03010001 0>; +}; + +&spi_9 { + status = "ok"; + can-controller@0 { + compatible = "renesas,rh850"; + reg = <0>; + interrupt-parent = <&tlmm>; + interrupts = <127 0>; + spi-max-frequency = <5000000>; + }; +}; + +&pil_modem { + pinctrl-names = "default"; + pinctrl-0 = <&modem_mux>; +}; + diff --git a/arch/arm/boot/dts/qcom/msm8996pro.dtsi b/arch/arm/boot/dts/qcom/msm8996pro.dtsi index 28577dc6d72f..ca89a517df5c 100644 --- a/arch/arm/boot/dts/qcom/msm8996pro.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996pro.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, 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 @@ -20,6 +20,10 @@ / { model = "Qualcomm Technologies, Inc. MSM 8996pro"; qcom,msm-id = <305 0x10000>; + + chosen { + bootargs = "fpsimd.fpsimd_settings=1 app_setting.use_app_setting=0 app_setting.use_32bit_app_setting_pro=1"; + }; }; &apc_apm { @@ -958,6 +962,26 @@ < 1516800000 17 >, < 1593600000 18 >, < 1996800000 20 >; + qcom,pwrcl-speedbin2-v0 = + < 0 0 >, + < 307200000 1 >, + < 384000000 2 >, + < 460800000 3 >, + < 537600000 4 >, + < 614400000 5 >, + < 691200000 6 >, + < 768000000 7 >, + < 844800000 8 >, + < 902400000 9 >, + < 979200000 10 >, + < 1056000000 11 >, + < 1132800000 12 >, + < 1209600000 13 >, + < 1286400000 14 >, + < 1363200000 15 >, + < 1440000000 16 >, + < 1516800000 17 >, + < 1593600000 18 >; qcom,perfcl-speedbin0-v0 = < 0 0 >, < 307200000 1 >, @@ -1014,6 +1038,30 @@ < 1977600000 23 >, < 2054400000 24 >, < 2150400000 25 >; + qcom,perfcl-speedbin2-v0 = + < 0 0 >, + < 307200000 1 >, + < 384000000 2 >, + < 460800000 3 >, + < 537600000 4 >, + < 614400000 5 >, + < 691200000 6 >, + < 748800000 7 >, + < 825600000 8 >, + < 902400000 9 >, + < 979200000 10 >, + < 1056000000 11 >, + < 1132800000 12 >, + < 1209600000 13 >, + < 1286400000 14 >, + < 1363200000 15 >, + < 1440000000 16 >, + < 1516800000 17 >, + < 1593600000 18 >, + < 1670400000 19 >, + < 1747200000 20 >, + < 1824000000 21 >, + < 1900800000 22 >; qcom,cbf-speedbin0-v0 = < 0 0 >, < 192000000 1 >, @@ -1056,6 +1104,27 @@ < 1440000000 17 >, < 1516800000 18 >, < 1593600000 19 >; + qcom,cbf-speedbin2-v0 = + < 0 0 >, + < 192000000 1 >, + < 307200000 2 >, + < 384000000 3 >, + < 441600000 4 >, + < 537600000 5 >, + < 614400000 6 >, + < 691200000 7 >, + < 768000000 8 >, + < 844800000 9 >, + < 902400000 10 >, + < 979200000 11 >, + < 1056000000 12 >, + < 1132800000 13 >, + < 1190400000 14 >, + < 1286400000 15 >, + < 1363200000 16 >, + < 1440000000 17 >, + < 1516800000 18 >, + < 1593600000 19 >; }; &clock_mmss { @@ -1106,6 +1175,20 @@ < 510000000 5 >, < 560000000 7 >, < 624000000 7 >; + qcom,gfxfreq-speedbin2 = + < 0 0 0 >, + < 133000000 2 4 >, + < 214000000 3 4 >, + < 315000000 4 4 >, + < 401800000 5 5 >, + < 510000000 6 5 >; + qcom,gfxfreq-mx-speedbin2 = + < 0 0 >, + < 133000000 4 >, + < 214000000 4 >, + < 315000000 4 >, + < 401800000 5 >, + < 510000000 5 >; }; &msm_cpufreq { @@ -1422,5 +1505,62 @@ qcom,bus-max = <0>; }; }; + + qcom,gpu-pwrlevels-2 { + #address-cells = <1>; + #size-cells = <0>; + + qcom,speed-bin = <2>; + + qcom,initial-pwrlevel = <3>; + + qcom,gpu-pwrlevel@0 { + reg = <0>; + qcom,gpu-freq = <510000000>; + qcom,bus-freq = <11>; + qcom,bus-min = <11>; + qcom,bus-max = <11>; + }; + + qcom,gpu-pwrlevel@1 { + reg = <1>; + qcom,gpu-freq = <401800000>; + qcom,bus-freq = <8>; + qcom,bus-min = <7>; + qcom,bus-max = <9>; + }; + + qcom,gpu-pwrlevel@2 { + reg = <2>; + qcom,gpu-freq = <315000000>; + qcom,bus-freq = <6>; + qcom,bus-min = <5>; + qcom,bus-max = <7>; + }; + + qcom,gpu-pwrlevel@3 { + reg = <3>; + qcom,gpu-freq = <214000000>; + qcom,bus-freq = <4>; + qcom,bus-min = <3>; + qcom,bus-max = <5>; + }; + + qcom,gpu-pwrlevel@4 { + reg = <4>; + qcom,gpu-freq = <133000000>; + qcom,bus-freq = <3>; + qcom,bus-min = <2>; + qcom,bus-max = <4>; + }; + + qcom,gpu-pwrlevel@5 { + reg = <5>; + qcom,gpu-freq = <27000000>; + qcom,bus-freq = <0>; + qcom,bus-min = <0>; + qcom,bus-max = <0>; + }; + }; }; }; diff --git a/arch/arm/boot/dts/qcom/msm8996v3-auto.dtsi b/arch/arm/boot/dts/qcom/msm8996v3-auto.dtsi new file mode 100644 index 000000000000..32adb9a36dd4 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8996v3-auto.dtsi @@ -0,0 +1,169 @@ +/* Copyright (c) 2016, 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. + */ + +&hl7509_en_vreg { + status = "ok"; +}; + +&hl7509_vreg { + status = "ok"; +}; + +&gfx_cpr { + vdd-supply = <&hl7509_vreg>; + qcom,cpr-step-quot-init-min = <20>; + qcom,cpr-step-quot-init-max = <26>; + qcom,voltage-step = <10000>; + /delete-property/ qcom,cpr-enable; +}; + +&gfx_vreg { + qcom,cpr-voltage-ceiling = + <600000 670000 670000 750000 830000 + 910000 960000 1020000>; + qcom,cpr-voltage-floor = + <600000 600000 600000 600000 600000 + 600000 600000 600000>; +}; + +&pm8994_l3 { + regulator-min-microvolt = <875000>; + regulator-max-microvolt = <875000>; + qcom,init-voltage = <875000>; +}; + +&pm8994_l11 { + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <850000>; + qcom,init-voltage = <850000>; +}; + +&pm8994_l17 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + qcom,init-voltage = <1800000>; +}; + +&pm8994_l23 { + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + qcom,init-voltage = <1100000>; +}; + +&pm8994_l27 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <800000>; + qcom,init-voltage = <800000>; +}; + +&pm8994_l29 { + regulator-min-microvolt = <2500000>; + regulator-max-microvolt = <2500000>; + qcom,init-voltage = <2500000>; +}; + +&rpm_bus { + rpm-regulator-ldoa26 { + /delete-node/ pm8994_l26_corner; + /delete-node/ pm8994_l26_floor_corner; + + pm8994_l26: regulator-l26 { + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + qcom,init-voltage = <1100000>; + status = "okay"; + }; + }; + + rpm-regulator-ldoa31 { + status = "okay"; + pm8994_l31: regulator-l31 { + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + qcom,init-voltage = <1100000>; + status = "okay"; + }; + }; +}; + +&clock_cpu { + qcom,pwrcl-speedbin0-v0 = + < 0 0 >, + < 768000000 7 >, + < 844800000 8 >, + < 902400000 9 >, + < 979200000 10 >, + < 1056000000 11 >, + < 1132800000 12 >, + < 1209600000 13 >, + < 1286400000 14 >, + < 1363200000 15 >, + < 1440000000 16 >, + < 1516800000 17 >, + < 1593600000 18 >; + + qcom,perfcl-speedbin0-v0 = + < 0 0 >, + < 825600000 8 >, + < 902400000 9 >, + < 979200000 10 >, + < 1056000000 11 >, + < 1132800000 12 >, + < 1209600000 13 >, + < 1286400000 14 >, + < 1363200000 15 >, + < 1440000000 16 >, + < 1516800000 17 >, + < 1593600000 18 >, + < 1670400000 19 >, + < 1747200000 20 >, + < 1824000000 21 >, + < 1900800000 22 >, + < 1977600000 23 >, + < 2054400000 24 >; + + qcom,cbf-speedbin0-v0 = + < 0 0 >, + < 537600000 5 >, + < 614400000 6 >, + < 691200000 7 >, + < 768000000 8 >, + < 844800000 9 >, + < 902400000 10 >, + < 979200000 11 >, + < 1056000000 12 >, + < 1132800000 13 >, + < 1190400000 14 >, + < 1286400000 15 >, + < 1363200000 16 >, + < 1440000000 17 >, + < 1516800000 18 >, + < 1593600000 19 >; +}; + +&clock_gpu { + qcom,gfxfreq-speedbin0 = + < 0 0 0 >, + < 315000000 4 4 >, + < 401800000 5 5 >, + < 510000000 6 5 >, + < 560000000 7 7 >, + < 624000000 8 7 >; + + qcom,gfxfreq-mx-speedbin0 = + < 0 0 >, + < 315000000 4 >, + < 401800000 5 >, + < 510000000 5 >, + < 560000000 7 >, + < 624000000 7 >; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-cdp-overlay.dts b/arch/arm/boot/dts/qcom/msm8998-cdp-overlay.dts index 5fe0d0176618..9cf94012695e 100644 --- a/arch/arm/boot/dts/qcom/msm8998-cdp-overlay.dts +++ b/arch/arm/boot/dts/qcom/msm8998-cdp-overlay.dts @@ -18,6 +18,7 @@ #include <dt-bindings/regulator/qcom,rpm-smd-regulator.h> #include <dt-bindings/interrupt-controller/arm-gic.h> +#include "msm8998-mdss-panels.dtsi" #include "msm8998-cdp.dtsi" / { diff --git a/arch/arm/boot/dts/qcom/msm8998-cdp.dts b/arch/arm/boot/dts/qcom/msm8998-cdp.dts index 487d71d4b6a8..3d8ccbd39c95 100644 --- a/arch/arm/boot/dts/qcom/msm8998-cdp.dts +++ b/arch/arm/boot/dts/qcom/msm8998-cdp.dts @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, 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,6 +14,7 @@ /dts-v1/; #include "msm8998.dtsi" +#include "msm8998-mdss-panels.dtsi" #include "msm8998-cdp.dtsi" / { diff --git a/arch/arm/boot/dts/qcom/msm8998-mdss.dtsi b/arch/arm/boot/dts/qcom/msm8998-mdss.dtsi index 2b9e13ea24f2..1b746a593579 100644 --- a/arch/arm/boot/dts/qcom/msm8998-mdss.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-mdss.dtsi @@ -335,6 +335,7 @@ qcom,supply-max-voltage = <0>; qcom,supply-enable-load = <0>; qcom,supply-disable-load = <0>; + qcom,supply-lp-mode-disable-allowed; }; }; @@ -363,6 +364,7 @@ qcom,supply-max-voltage = <880000>; qcom,supply-enable-load = <73400>; qcom,supply-disable-load = <32>; + qcom,supply-lp-mode-disable-allowed; }; }; @@ -660,5 +662,3 @@ qcom,pluggable; }; }; - -#include "msm8998-mdss-panels.dtsi" diff --git a/arch/arm/boot/dts/qcom/msm8998-mtp-overlay.dts b/arch/arm/boot/dts/qcom/msm8998-mtp-overlay.dts index 8525d781aae2..0659ced82a56 100644 --- a/arch/arm/boot/dts/qcom/msm8998-mtp-overlay.dts +++ b/arch/arm/boot/dts/qcom/msm8998-mtp-overlay.dts @@ -18,6 +18,7 @@ #include <dt-bindings/regulator/qcom,rpm-smd-regulator.h> #include <dt-bindings/interrupt-controller/arm-gic.h> +#include "msm8998-mdss-panels.dtsi" #include "msm8998-mtp.dtsi" / { diff --git a/arch/arm/boot/dts/qcom/msm8998-mtp.dts b/arch/arm/boot/dts/qcom/msm8998-mtp.dts index f608f5f59a80..8a38d1eeea50 100644 --- a/arch/arm/boot/dts/qcom/msm8998-mtp.dts +++ b/arch/arm/boot/dts/qcom/msm8998-mtp.dts @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, 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,6 +14,7 @@ /dts-v1/; #include "msm8998.dtsi" +#include "msm8998-mdss-panels.dtsi" #include "msm8998-mtp.dtsi" / { diff --git a/arch/arm/boot/dts/qcom/msm8998-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msm8998-pinctrl.dtsi index 220bad31d7f8..cdf4bede6eb3 100644 --- a/arch/arm/boot/dts/qcom/msm8998-pinctrl.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-pinctrl.dtsi @@ -585,6 +585,19 @@ bias-pull-down; }; }; + + pcie0_wake_sleep: pcie0_wake_sleep { + mux { + pins = "gpio37"; + function = "gpio"; + }; + + config { + pins = "gpio37"; + drive-strength = <2>; + bias-disable; + }; + }; }; hph_en0_ctrl { diff --git a/arch/arm/boot/dts/qcom/msm8998-qrd-skuk.dts b/arch/arm/boot/dts/qcom/msm8998-qrd-skuk.dts index 753f01ea2ea7..b3062c48e051 100644 --- a/arch/arm/boot/dts/qcom/msm8998-qrd-skuk.dts +++ b/arch/arm/boot/dts/qcom/msm8998-qrd-skuk.dts @@ -14,6 +14,7 @@ /dts-v1/; #include "msm8998.dtsi" +#include "msm8998-mdss-panels.dtsi" #include "msm8998-qrd-skuk.dtsi" #include "msm8998-camera-sensor-skuk.dtsi" diff --git a/arch/arm/boot/dts/qcom/msm8998-qrd-vr1.dts b/arch/arm/boot/dts/qcom/msm8998-qrd-vr1.dts index f5780529c99a..9b8e631f80a6 100644 --- a/arch/arm/boot/dts/qcom/msm8998-qrd-vr1.dts +++ b/arch/arm/boot/dts/qcom/msm8998-qrd-vr1.dts @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, 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,6 +14,7 @@ /dts-v1/; #include "msm8998.dtsi" +#include "msm8998-mdss-panels.dtsi" #include "msm8998-qrd-vr1.dtsi" / { diff --git a/arch/arm/boot/dts/qcom/msm8998-qrd.dts b/arch/arm/boot/dts/qcom/msm8998-qrd.dts index 952b9a5cec6f..4d50d8611050 100644 --- a/arch/arm/boot/dts/qcom/msm8998-qrd.dts +++ b/arch/arm/boot/dts/qcom/msm8998-qrd.dts @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, 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,6 +14,7 @@ /dts-v1/; #include "msm8998.dtsi" +#include "msm8998-mdss-panels.dtsi" #include "msm8998-qrd.dtsi" / { diff --git a/arch/arm/boot/dts/qcom/msm8998-regulator.dtsi b/arch/arm/boot/dts/qcom/msm8998-regulator.dtsi index 045cdda09d18..be70f129e272 100644 --- a/arch/arm/boot/dts/qcom/msm8998-regulator.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-regulator.dtsi @@ -84,6 +84,8 @@ pm8998_s5: regulator-s5 { regulator-min-microvolt = <1904000>; regulator-max-microvolt = <2040000>; + qcom,init-pin-ctrl-mode = <8>; /* PMIC_AWAKE */ + qcom,send-defaults; status = "okay"; }; }; @@ -93,6 +95,8 @@ pm8998_s7: regulator-s7 { regulator-min-microvolt = <900000>; regulator-max-microvolt = <1028000>; + qcom,init-pin-ctrl-mode = <8>; /* PMIC_AWAKE */ + qcom,send-defaults; status = "okay"; }; }; @@ -407,7 +411,7 @@ rpm-regulator-ldoa24 { status = "okay"; pm8998_l24: regulator-l24 { - regulator-min-microvolt = <3088000>; + regulator-min-microvolt = <1848000>; regulator-max-microvolt = <3088000>; parent-supply = <&pm8998_l12>; status = "okay"; diff --git a/arch/arm/boot/dts/qcom/msm8998-v2-cdp-overlay.dts b/arch/arm/boot/dts/qcom/msm8998-v2-cdp-overlay.dts index c602459815b2..1ae671663edb 100644 --- a/arch/arm/boot/dts/qcom/msm8998-v2-cdp-overlay.dts +++ b/arch/arm/boot/dts/qcom/msm8998-v2-cdp-overlay.dts @@ -18,6 +18,7 @@ #include <dt-bindings/regulator/qcom,rpm-smd-regulator.h> #include <dt-bindings/interrupt-controller/arm-gic.h> +#include "msm8998-mdss-panels.dtsi" #include "msm8998-cdp.dtsi" / { diff --git a/arch/arm/boot/dts/qcom/msm8998-v2-cdp.dts b/arch/arm/boot/dts/qcom/msm8998-v2-cdp.dts index 0f5ad5366c84..f54017dcae9e 100644 --- a/arch/arm/boot/dts/qcom/msm8998-v2-cdp.dts +++ b/arch/arm/boot/dts/qcom/msm8998-v2-cdp.dts @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, 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,6 +14,7 @@ /dts-v1/; #include "msm8998-v2.dtsi" +#include "msm8998-mdss-panels.dtsi" #include "msm8998-cdp.dtsi" / { diff --git a/arch/arm/boot/dts/qcom/msm8998-v2-mtp-overlay.dts b/arch/arm/boot/dts/qcom/msm8998-v2-mtp-overlay.dts index 660fc4d51f9f..c446a6a78f42 100644 --- a/arch/arm/boot/dts/qcom/msm8998-v2-mtp-overlay.dts +++ b/arch/arm/boot/dts/qcom/msm8998-v2-mtp-overlay.dts @@ -18,6 +18,7 @@ #include <dt-bindings/regulator/qcom,rpm-smd-regulator.h> #include <dt-bindings/interrupt-controller/arm-gic.h> +#include "msm8998-mdss-panels.dtsi" #include "msm8998-mtp.dtsi" / { diff --git a/arch/arm/boot/dts/qcom/msm8998-v2-mtp.dts b/arch/arm/boot/dts/qcom/msm8998-v2-mtp.dts index 0708573934f3..0375ee29984c 100644 --- a/arch/arm/boot/dts/qcom/msm8998-v2-mtp.dts +++ b/arch/arm/boot/dts/qcom/msm8998-v2-mtp.dts @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, 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,6 +14,7 @@ /dts-v1/; #include "msm8998-v2.dtsi" +#include "msm8998-mdss-panels.dtsi" #include "msm8998-mtp.dtsi" / { diff --git a/arch/arm/boot/dts/qcom/msm8998-v2-qrd-skuk-evt3.dts b/arch/arm/boot/dts/qcom/msm8998-v2-qrd-skuk-evt3.dts index a64f620f3999..b1d00eda89ef 100644 --- a/arch/arm/boot/dts/qcom/msm8998-v2-qrd-skuk-evt3.dts +++ b/arch/arm/boot/dts/qcom/msm8998-v2-qrd-skuk-evt3.dts @@ -14,6 +14,7 @@ /dts-v1/; #include "msm8998-v2.dtsi" +#include "msm8998-mdss-panels.dtsi" #include "msm8998-qrd-skuk.dtsi" #include "msm8998-camera-sensor-skuk-evt3.dtsi" diff --git a/arch/arm/boot/dts/qcom/msm8998-v2-qrd-skuk-hdk.dts b/arch/arm/boot/dts/qcom/msm8998-v2-qrd-skuk-hdk.dts index f3ba42e4dfd9..4e75d161ec72 100644 --- a/arch/arm/boot/dts/qcom/msm8998-v2-qrd-skuk-hdk.dts +++ b/arch/arm/boot/dts/qcom/msm8998-v2-qrd-skuk-hdk.dts @@ -13,6 +13,7 @@ /dts-v1/; #include "msm8998-v2.dtsi" +#include "msm8998-mdss-panels.dtsi" #include "msm8998-qrd-skuk-hdk.dtsi" / { diff --git a/arch/arm/boot/dts/qcom/msm8998-v2-qrd-skuk.dts b/arch/arm/boot/dts/qcom/msm8998-v2-qrd-skuk.dts index 7e7eb96c01d6..5727f3312a49 100644 --- a/arch/arm/boot/dts/qcom/msm8998-v2-qrd-skuk.dts +++ b/arch/arm/boot/dts/qcom/msm8998-v2-qrd-skuk.dts @@ -14,6 +14,7 @@ /dts-v1/; #include "msm8998-v2.dtsi" +#include "msm8998-mdss-panels.dtsi" #include "msm8998-qrd-skuk.dtsi" #include "msm8998-camera-sensor-skuk.dtsi" diff --git a/arch/arm/boot/dts/qcom/msm8998-v2-qrd-vr1.dts b/arch/arm/boot/dts/qcom/msm8998-v2-qrd-vr1.dts index 58c7dc3bac11..ebd5b307388c 100644 --- a/arch/arm/boot/dts/qcom/msm8998-v2-qrd-vr1.dts +++ b/arch/arm/boot/dts/qcom/msm8998-v2-qrd-vr1.dts @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, 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,6 +14,7 @@ /dts-v1/; #include "msm8998-v2.dtsi" +#include "msm8998-mdss-panels.dtsi" #include "msm8998-qrd-vr1.dtsi" / { diff --git a/arch/arm/boot/dts/qcom/msm8998-v2-qrd.dts b/arch/arm/boot/dts/qcom/msm8998-v2-qrd.dts index 37ad18278e5c..ed3a73e0b811 100644 --- a/arch/arm/boot/dts/qcom/msm8998-v2-qrd.dts +++ b/arch/arm/boot/dts/qcom/msm8998-v2-qrd.dts @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, 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 @@ -13,6 +13,7 @@ /dts-v1/; #include "msm8998-v2.dtsi" +#include "msm8998-mdss-panels.dtsi" #include "msm8998-qrd.dtsi" / { diff --git a/arch/arm/boot/dts/qcom/msm8998-v2.1-cdp-overlay.dts b/arch/arm/boot/dts/qcom/msm8998-v2.1-cdp-overlay.dts index 47c55a045017..f57238978baf 100644 --- a/arch/arm/boot/dts/qcom/msm8998-v2.1-cdp-overlay.dts +++ b/arch/arm/boot/dts/qcom/msm8998-v2.1-cdp-overlay.dts @@ -18,6 +18,7 @@ #include <dt-bindings/regulator/qcom,rpm-smd-regulator.h> #include <dt-bindings/interrupt-controller/arm-gic.h> +#include "msm8998-mdss-panels.dtsi" #include "msm8998-cdp.dtsi" / { diff --git a/arch/arm/boot/dts/qcom/msm8998-v2.1-cdp.dts b/arch/arm/boot/dts/qcom/msm8998-v2.1-cdp.dts index 871d13bdaa1a..5d007dd55910 100644 --- a/arch/arm/boot/dts/qcom/msm8998-v2.1-cdp.dts +++ b/arch/arm/boot/dts/qcom/msm8998-v2.1-cdp.dts @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, 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,6 +14,7 @@ /dts-v1/; #include "msm8998-v2.1.dtsi" +#include "msm8998-mdss-panels.dtsi" #include "msm8998-cdp.dtsi" / { diff --git a/arch/arm/boot/dts/qcom/msm8998-v2.1-interposer-sdm660-cdp.dts b/arch/arm/boot/dts/qcom/msm8998-v2.1-interposer-sdm660-cdp.dts index 6884397bf0ba..6e4247fd884f 100644 --- a/arch/arm/boot/dts/qcom/msm8998-v2.1-interposer-sdm660-cdp.dts +++ b/arch/arm/boot/dts/qcom/msm8998-v2.1-interposer-sdm660-cdp.dts @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, 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,6 +14,7 @@ /dts-v1/; #include "msm8998-v2.1-interposer-sdm660.dtsi" +#include "msm8998-mdss-panels.dtsi" #include "msm8998-interposer-sdm660-cdp.dtsi" #include "msm8998-interposer-pm660.dtsi" #include "msm8998-interposer-sdm660-audio.dtsi" diff --git a/arch/arm/boot/dts/qcom/msm8998-v2.1-interposer-sdm660-mtp.dts b/arch/arm/boot/dts/qcom/msm8998-v2.1-interposer-sdm660-mtp.dts index 2e5de95de0c5..cb7ee2266d52 100644 --- a/arch/arm/boot/dts/qcom/msm8998-v2.1-interposer-sdm660-mtp.dts +++ b/arch/arm/boot/dts/qcom/msm8998-v2.1-interposer-sdm660-mtp.dts @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, 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,6 +14,7 @@ /dts-v1/; #include "msm8998-v2.1-interposer-sdm660.dtsi" +#include "msm8998-mdss-panels.dtsi" #include "msm8998-interposer-sdm660-mtp.dtsi" #include "msm8998-interposer-pm660.dtsi" #include "msm8998-interposer-sdm660-audio.dtsi" diff --git a/arch/arm/boot/dts/qcom/msm8998-v2.1-interposer-sdm660-qrd.dts b/arch/arm/boot/dts/qcom/msm8998-v2.1-interposer-sdm660-qrd.dts index 180cc9128f53..9376f52e0737 100644 --- a/arch/arm/boot/dts/qcom/msm8998-v2.1-interposer-sdm660-qrd.dts +++ b/arch/arm/boot/dts/qcom/msm8998-v2.1-interposer-sdm660-qrd.dts @@ -13,6 +13,8 @@ /dts-v1/; +#include "msm8998-v2.1-interposer-sdm660.dtsi" +#include "msm8998-mdss-panels.dtsi" #include "msm8998-v2.1-interposer-sdm660-qrd.dtsi" #include "msm8998-interposer-pm660.dtsi" #include "msm8998-interposer-sdm660-audio.dtsi" diff --git a/arch/arm/boot/dts/qcom/msm8998-v2.1-mtp-4k-display.dts b/arch/arm/boot/dts/qcom/msm8998-v2.1-mtp-4k-display.dts index 7d537aa35533..eb2d812aa1f8 100644 --- a/arch/arm/boot/dts/qcom/msm8998-v2.1-mtp-4k-display.dts +++ b/arch/arm/boot/dts/qcom/msm8998-v2.1-mtp-4k-display.dts @@ -14,6 +14,7 @@ /dts-v1/; #include "msm8998-v2.1.dtsi" +#include "msm8998-mdss-panels.dtsi" #include "msm8998-mtp.dtsi" / { diff --git a/arch/arm/boot/dts/qcom/msm8998-v2.1-mtp-overlay.dts b/arch/arm/boot/dts/qcom/msm8998-v2.1-mtp-overlay.dts index 54a0e98cdce7..5f17f73df8d6 100644 --- a/arch/arm/boot/dts/qcom/msm8998-v2.1-mtp-overlay.dts +++ b/arch/arm/boot/dts/qcom/msm8998-v2.1-mtp-overlay.dts @@ -18,6 +18,7 @@ #include <dt-bindings/regulator/qcom,rpm-smd-regulator.h> #include <dt-bindings/interrupt-controller/arm-gic.h> +#include "msm8998-mdss-panels.dtsi" #include "msm8998-mtp.dtsi" / { diff --git a/arch/arm/boot/dts/qcom/msm8998-v2.1-mtp.dts b/arch/arm/boot/dts/qcom/msm8998-v2.1-mtp.dts index 9d7cbc93d9df..0bf3e62766c4 100644 --- a/arch/arm/boot/dts/qcom/msm8998-v2.1-mtp.dts +++ b/arch/arm/boot/dts/qcom/msm8998-v2.1-mtp.dts @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, 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,6 +14,7 @@ /dts-v1/; #include "msm8998-v2.1.dtsi" +#include "msm8998-mdss-panels.dtsi" #include "msm8998-mtp.dtsi" / { diff --git a/arch/arm/boot/dts/qcom/msm8998-v2.1-qrd.dts b/arch/arm/boot/dts/qcom/msm8998-v2.1-qrd.dts index b20c888ad933..4d64740143e8 100644 --- a/arch/arm/boot/dts/qcom/msm8998-v2.1-qrd.dts +++ b/arch/arm/boot/dts/qcom/msm8998-v2.1-qrd.dts @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, 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,6 +14,7 @@ /dts-v1/; #include "msm8998-v2.1.dtsi" +#include "msm8998-mdss-panels.dtsi" #include "msm8998-qrd.dtsi" / { diff --git a/arch/arm/boot/dts/qcom/msm8998.dtsi b/arch/arm/boot/dts/qcom/msm8998.dtsi index 9b5092cf7f14..c1d785e42669 100644 --- a/arch/arm/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998.dtsi @@ -2662,10 +2662,13 @@ 0x800 0x00 0x00 0x808 0x03 0x00>; - pinctrl-names = "default"; + pinctrl-names = "default", "sleep"; pinctrl-0 = <&pcie0_clkreq_default &pcie0_perst_default &pcie0_wake_default>; + pinctrl-1 = <&pcie0_clkreq_default + &pcie0_perst_default + &pcie0_wake_sleep>; perst-gpio = <&tlmm 35 0>; wake-gpio = <&tlmm 37 0>; @@ -3197,6 +3200,10 @@ <&clock_gcc clk_rf_clk3_pin>; clock-names = "rf_clk3_clk", "rf_clk3_pin_clk"; qcom,smmu-support; + qcom,smmu-s1-en; + qcom,smmu-fast-map; + qcom,smmu-coherent; + qcom,smmu-mapping = <0x20000000 0xe0000000>; status = "disabled"; }; diff --git a/arch/arm/boot/dts/qcom/sda630-pm660a-qrd-hdk.dts b/arch/arm/boot/dts/qcom/sda630-pm660a-qrd-hdk.dts new file mode 100644 index 000000000000..480a69601541 --- /dev/null +++ b/arch/arm/boot/dts/qcom/sda630-pm660a-qrd-hdk.dts @@ -0,0 +1,85 @@ +/* Copyright (c) 2017, 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. + */ + + +/dts-v1/; + +#include "sda630.dtsi" +#include "sdm630-qrd.dtsi" +#include "msm-pm660a.dtsi" +#include "sdm660-internal-codec.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM 630 PM660 + PM660A QRD HDK630"; + compatible = "qcom,sda630-qrd", "qcom,sda630", "qcom,qrd"; + qcom,board-id = <0x0006000b 0x00>; + qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>; +}; + +&int_codec { + qcom,model = "sdm660-snd-card-skush"; + /delete-property/ qcom,us-euro-gpios; + qcom,audio-routing = + "RX_BIAS", "INT_MCLK0", + "SPK_RX_BIAS", "INT_MCLK0", + "INT_LDO_H", "INT_MCLK0", + "MIC BIAS External2", "Headset Mic", + "AMIC2", "MIC BIAS External2", + "MIC BIAS External", "Digital Mic1", + "DMIC1", "MIC BIAS External", + "MIC BIAS External", "Digital Mic3", + "DMIC3", "MIC BIAS External", + "MIC BIAS External", "Digital Mic4", + "DMIC4", "MIC BIAS External", + "SpkrLeft IN", "SPK1 OUT", + "PDM_IN_RX1", "PDM_OUT_RX1", + "PDM_IN_RX2", "PDM_OUT_RX2", + "PDM_IN_RX3", "PDM_OUT_RX3", + "ADC1_IN", "ADC1_OUT", + "ADC2_IN", "ADC2_OUT", + "ADC3_IN", "ADC3_OUT"; + qcom,wsa-max-devs = <1>; + qcom,wsa-devs = <&wsa881x_211_en>, <&wsa881x_213_en>; + qcom,wsa-aux-dev-prefix = "SpkrLeft", "SpkrLeft"; +}; + +&pm660a_oledb { + status = "okay"; + qcom,oledb-default-voltage-mv = <6400>; +}; + +&mdss_mdp { + qcom,mdss-pref-prim-intf = "dsi"; +}; + +&mdss_dsi { + hw-config = "single_dsi"; +}; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_rm67195_amoled_fhd_cmd>; + pinctrl-names = "mdss_default", "mdss_sleep"; + pinctrl-0 = <&mdss_dsi_active &mdss_te_active>; + pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>; + oledb-supply = <&pm660a_oledb>; + lab-supply = <&lab_regulator>; + ibb-supply = <&ibb_regulator>; + qcom,platform-reset-gpio = <&tlmm 53 0>; + qcom,platform-te-gpio = <&tlmm 59 0>; +}; + +&dsi_rm67195_amoled_fhd_cmd { + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <255>; + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_labibb_amoled>; +}; diff --git a/arch/arm/boot/dts/qcom/sda660-pm660a-qrd-hdk.dts b/arch/arm/boot/dts/qcom/sda660-pm660a-qrd-hdk.dts new file mode 100644 index 000000000000..a767853cd829 --- /dev/null +++ b/arch/arm/boot/dts/qcom/sda660-pm660a-qrd-hdk.dts @@ -0,0 +1,56 @@ +/* Copyright (c) 2017, 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. + */ + + +/dts-v1/; + +#include "sda660.dtsi" +#include "sdm660-qrd.dtsi" +#include "msm-pm660a.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDA 660 PM660 + PM660A QRD HDK660"; + compatible = "qcom,sda660-qrd", "qcom,sda660", "qcom,qrd"; + qcom,board-id = <0x0006000b 0>; + qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>; +}; + +&pm660a_oledb { + status = "okay"; + qcom,oledb-default-voltage-mv = <6400>; +}; + +&mdss_mdp { + qcom,mdss-pref-prim-intf = "dsi"; +}; + +&mdss_dsi { + hw-config = "single_dsi"; +}; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_rm67195_amoled_fhd_cmd>; + pinctrl-names = "mdss_default", "mdss_sleep"; + pinctrl-0 = <&mdss_dsi_active &mdss_te_active>; + pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>; + lab-supply = <&lab_regulator>; + ibb-supply = <&ibb_regulator>; + qcom,platform-reset-gpio = <&tlmm 53 0>; + qcom,platform-te-gpio = <&tlmm 59 0>; +}; + +&dsi_rm67195_amoled_fhd_cmd { + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <255>; + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_labibb_amoled>; +}; diff --git a/arch/arm/boot/dts/qcom/sdm630-cdp.dtsi b/arch/arm/boot/dts/qcom/sdm630-cdp.dtsi index 9df521f33e05..fd109450ad81 100644 --- a/arch/arm/boot/dts/qcom/sdm630-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/sdm630-cdp.dtsi @@ -215,3 +215,7 @@ qcom,mdss-dsi-bl-pmic-pwm-frequency = <100>; qcom,panel-supply-entries = <&dsi_panel_split_link_pwr_supply>; }; + +&dsi_rm67195_amoled_fhd_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_labibb_amoled>; +}; diff --git a/arch/arm/boot/dts/qcom/sdm630-mdss.dtsi b/arch/arm/boot/dts/qcom/sdm630-mdss.dtsi index d35704224f45..49e4fd7e5ba7 100644 --- a/arch/arm/boot/dts/qcom/sdm630-mdss.dtsi +++ b/arch/arm/boot/dts/qcom/sdm630-mdss.dtsi @@ -112,13 +112,14 @@ clocks = <&clock_mmss MMSS_MNOC_AHB_CLK>, <&clock_mmss MMSS_MDSS_AHB_CLK>, <&clock_mmss MMSS_MDSS_AXI_CLK>, + <&clock_mmss MMSS_THROTTLE_MDSS_AXI_CLK>, <&clock_mmss MDP_CLK_SRC>, <&clock_mmss MMSS_MDSS_MDP_CLK>, <&clock_mmss MMSS_MDSS_VSYNC_CLK>, <&clock_mmss MDP_CLK_SRC>; clock-names = "mnoc_clk", "iface_clk", "bus_clk", - "core_clk_src", "core_clk", "vsync_clk", - "lut_clk"; + "throttle_bus_clk", "core_clk_src", + "core_clk", "vsync_clk", "lut_clk"; qcom,mdp-settings = <0x01190 0x00000000>, <0x012ac 0xc0000ccc>, diff --git a/arch/arm/boot/dts/qcom/sdm630-mtp.dtsi b/arch/arm/boot/dts/qcom/sdm630-mtp.dtsi index 9e61f48e3503..bb50f23d8126 100644 --- a/arch/arm/boot/dts/qcom/sdm630-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/sdm630-mtp.dtsi @@ -202,3 +202,7 @@ qcom,parallel-en-pin-polarity = <1>; }; }; + +&dsi_rm67195_amoled_fhd_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_labibb_amoled>; +}; diff --git a/arch/arm/boot/dts/qcom/sdm630-pm660a-cdp.dts b/arch/arm/boot/dts/qcom/sdm630-pm660a-cdp.dts index d9b6a8ae9d34..7e3e9a0cca59 100644 --- a/arch/arm/boot/dts/qcom/sdm630-pm660a-cdp.dts +++ b/arch/arm/boot/dts/qcom/sdm630-pm660a-cdp.dts @@ -34,3 +34,10 @@ qcom,msm-mbhc-hphl-swh = <0>; qcom,msm-mbhc-gnd-swh = <0>; }; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_rm67195_amoled_fhd_cmd>; + oledb-supply = <&pm660a_oledb>; + lab-supply = <&lab_regulator>; + ibb-supply = <&ibb_regulator>; +}; diff --git a/arch/arm/boot/dts/qcom/sdm630-pm660a-mtp.dts b/arch/arm/boot/dts/qcom/sdm630-pm660a-mtp.dts index 8ebdbc08a00c..a522b7ad1d5f 100644 --- a/arch/arm/boot/dts/qcom/sdm630-pm660a-mtp.dts +++ b/arch/arm/boot/dts/qcom/sdm630-pm660a-mtp.dts @@ -28,3 +28,10 @@ &tavil_snd { qcom,msm-mbhc-moist-cfg = <0>, <0>, <3>; }; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_rm67195_amoled_fhd_cmd>; + oledb-supply = <&pm660a_oledb>; + lab-supply = <&lab_regulator>; + ibb-supply = <&ibb_regulator>; +}; diff --git a/arch/arm/boot/dts/qcom/sdm630-qrd.dtsi b/arch/arm/boot/dts/qcom/sdm630-qrd.dtsi index c677df529dd7..5fac5ce2abe2 100644 --- a/arch/arm/boot/dts/qcom/sdm630-qrd.dtsi +++ b/arch/arm/boot/dts/qcom/sdm630-qrd.dtsi @@ -159,6 +159,7 @@ &pm660_fg { qcom,battery-data = <&qrd_batterydata>; qcom,fg-jeita-thresholds = <0 5 55 55>; + qcom,battery-thermal-coefficients = [9d 50 ff]; }; &uartblsp1dm1 { diff --git a/arch/arm/boot/dts/qcom/sdm660-cdp.dtsi b/arch/arm/boot/dts/qcom/sdm660-cdp.dtsi index 33303f1e2a74..4d05ea75b576 100644 --- a/arch/arm/boot/dts/qcom/sdm660-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-cdp.dtsi @@ -180,6 +180,10 @@ qcom,panel-roi-alignment = <2 2 4 2 1080 2>; }; +&dsi_rm67195_amoled_fhd_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_labibb_amoled>; +}; + &mdss_dp_ctrl { pinctrl-names = "mdss_dp_active", "mdss_dp_sleep"; pinctrl-0 = <&mdss_dp_aux_active &mdss_dp_usbplug_cc_active>; diff --git a/arch/arm/boot/dts/qcom/sdm660-mdss.dtsi b/arch/arm/boot/dts/qcom/sdm660-mdss.dtsi index 787c4f1e2fb6..ab4e71e3cd65 100644 --- a/arch/arm/boot/dts/qcom/sdm660-mdss.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-mdss.dtsi @@ -117,13 +117,14 @@ clocks = <&clock_mmss MMSS_MNOC_AHB_CLK>, <&clock_mmss MMSS_MDSS_AHB_CLK>, <&clock_mmss MMSS_MDSS_AXI_CLK>, + <&clock_mmss MMSS_THROTTLE_MDSS_AXI_CLK>, <&clock_mmss MDP_CLK_SRC>, <&clock_mmss MMSS_MDSS_MDP_CLK>, <&clock_mmss MMSS_MDSS_VSYNC_CLK>, <&clock_mmss MDP_CLK_SRC>; clock-names = "mnoc_clk", "iface_clk", "bus_clk", - "core_clk_src", "core_clk", "vsync_clk", - "lut_clk"; + "throttle_bus_clk", "core_clk_src", + "core_clk", "vsync_clk", "lut_clk"; qcom,mdp-settings = <0x01190 0x00000000>, <0x012ac 0xc0000ccc>, diff --git a/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi b/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi index ed3b3d89d392..cb554a639d71 100644 --- a/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi @@ -170,6 +170,10 @@ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; }; +&dsi_rm67195_amoled_fhd_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_labibb_amoled>; +}; + &sdhc_1 { /* device core power supply */ vdd-supply = <&pm660l_l4>; diff --git a/arch/arm/boot/dts/qcom/sdm660-pm660a-cdp.dts b/arch/arm/boot/dts/qcom/sdm660-pm660a-cdp.dts index 7ca31fcc41a2..c27f76d3027b 100644 --- a/arch/arm/boot/dts/qcom/sdm660-pm660a-cdp.dts +++ b/arch/arm/boot/dts/qcom/sdm660-pm660a-cdp.dts @@ -25,13 +25,19 @@ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>; }; +&mdss_dsi { + hw-config = "single_dsi"; +}; + &mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_rm67195_amoled_fhd_cmd>; oledb-supply = <&pm660a_oledb>; lab-supply = <&lab_regulator>; ibb-supply = <&ibb_regulator>; }; &mdss_dsi1 { + status = "disabled"; oledb-supply = <&pm660a_oledb>; lab-supply = <&lab_regulator>; ibb-supply = <&ibb_regulator>; diff --git a/arch/arm/boot/dts/qcom/sdm660-pm660a-mtp.dts b/arch/arm/boot/dts/qcom/sdm660-pm660a-mtp.dts index d6e1f6a32def..eb5e4999fb67 100644 --- a/arch/arm/boot/dts/qcom/sdm660-pm660a-mtp.dts +++ b/arch/arm/boot/dts/qcom/sdm660-pm660a-mtp.dts @@ -25,13 +25,19 @@ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>; }; +&mdss_dsi { + hw-config = "single_dsi"; +}; + &mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_rm67195_amoled_fhd_cmd>; oledb-supply = <&pm660a_oledb>; lab-supply = <&lab_regulator>; ibb-supply = <&ibb_regulator>; }; &mdss_dsi1 { + status = "disabled"; oledb-supply = <&pm660a_oledb>; lab-supply = <&lab_regulator>; ibb-supply = <&ibb_regulator>; diff --git a/arch/arm/boot/dts/qcom/sdm660-qrd.dtsi b/arch/arm/boot/dts/qcom/sdm660-qrd.dtsi index 3d2cfedc1009..86da804a1b18 100644 --- a/arch/arm/boot/dts/qcom/sdm660-qrd.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-qrd.dtsi @@ -230,6 +230,7 @@ &pm660_fg { qcom,battery-data = <&qrd_batterydata>; qcom,fg-jeita-thresholds = <0 5 55 55>; + qcom,battery-thermal-coefficients = [9d 50 ff]; }; &i2c_2 { diff --git a/arch/arm/boot/dts/sama5d2.dtsi b/arch/arm/boot/dts/sama5d2.dtsi index 4dfca8fc49b3..1bc61ece2589 100644 --- a/arch/arm/boot/dts/sama5d2.dtsi +++ b/arch/arm/boot/dts/sama5d2.dtsi @@ -856,6 +856,13 @@ compatible = "atmel,at91sam9260-usart"; reg = <0xf801c000 0x100>; interrupts = <24 IRQ_TYPE_LEVEL_HIGH 7>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(35))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(36))>; + dma-names = "tx", "rx"; clocks = <&uart0_clk>; clock-names = "usart"; status = "disabled"; @@ -865,6 +872,13 @@ compatible = "atmel,at91sam9260-usart"; reg = <0xf8020000 0x100>; interrupts = <25 IRQ_TYPE_LEVEL_HIGH 7>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(37))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(38))>; + dma-names = "tx", "rx"; clocks = <&uart1_clk>; clock-names = "usart"; status = "disabled"; @@ -874,6 +888,13 @@ compatible = "atmel,at91sam9260-usart"; reg = <0xf8024000 0x100>; interrupts = <26 IRQ_TYPE_LEVEL_HIGH 7>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(39))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(40))>; + dma-names = "tx", "rx"; clocks = <&uart2_clk>; clock-names = "usart"; status = "disabled"; @@ -985,6 +1006,13 @@ compatible = "atmel,at91sam9260-usart"; reg = <0xfc008000 0x100>; interrupts = <27 IRQ_TYPE_LEVEL_HIGH 7>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(41))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(42))>; + dma-names = "tx", "rx"; clocks = <&uart3_clk>; clock-names = "usart"; status = "disabled"; @@ -993,6 +1021,13 @@ uart4: serial@fc00c000 { compatible = "atmel,at91sam9260-usart"; reg = <0xfc00c000 0x100>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(43))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(44))>; + dma-names = "tx", "rx"; interrupts = <28 IRQ_TYPE_LEVEL_HIGH 7>; clocks = <&uart4_clk>; clock-names = "usart"; diff --git a/arch/arm/configs/sdm660-perf_defconfig b/arch/arm/configs/sdm660-perf_defconfig index 32cf48661c9b..fe6988ebbb53 100644 --- a/arch/arm/configs/sdm660-perf_defconfig +++ b/arch/arm/configs/sdm660-perf_defconfig @@ -3,6 +3,9 @@ CONFIG_AUDIT=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_TASKSTATS=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y CONFIG_RCU_EXPERT=y CONFIG_RCU_FAST_NO_HZ=y CONFIG_IKCONFIG=y @@ -242,7 +245,7 @@ CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 CONFIG_QSEECOM=y CONFIG_HDCP_QSEECOM=y -CONFIG_UID_CPUTIME=y +CONFIG_UID_SYS_STATS=y CONFIG_QPNP_MISC=y CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y @@ -359,6 +362,7 @@ CONFIG_REGULATOR_MSM_GFX_LDO=y CONFIG_REGULATOR_RPM_SMD=y CONFIG_REGULATOR_QPNP=y CONFIG_REGULATOR_QPNP_LABIBB=y +CONFIG_REGULATOR_QPNP_LCDB=y CONFIG_REGULATOR_QPNP_OLEDB=y CONFIG_REGULATOR_SPM=y CONFIG_REGULATOR_CPR3_HMSS=y @@ -526,7 +530,6 @@ CONFIG_MSM_MMCC_660=y CONFIG_CLOCK_CPU_OSM=y CONFIG_QCOM_MDSS_PLL=y CONFIG_REMOTE_SPINLOCK_MSM=y -CONFIG_IOMMU_IO_PGTABLE_FAST=y CONFIG_ARM_SMMU=y CONFIG_IOMMU_DEBUG=y CONFIG_IOMMU_DEBUG_TRACKING=y diff --git a/arch/arm/configs/sdm660_defconfig b/arch/arm/configs/sdm660_defconfig index 30a71904747f..3942da1a768d 100644 --- a/arch/arm/configs/sdm660_defconfig +++ b/arch/arm/configs/sdm660_defconfig @@ -3,6 +3,9 @@ CONFIG_AUDIT=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_TASKSTATS=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y CONFIG_RCU_EXPERT=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y @@ -241,7 +244,7 @@ CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 CONFIG_QSEECOM=y CONFIG_HDCP_QSEECOM=y -CONFIG_UID_CPUTIME=y +CONFIG_UID_SYS_STATS=y CONFIG_QPNP_MISC=y CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y @@ -526,8 +529,6 @@ CONFIG_MSM_MMCC_660=y CONFIG_CLOCK_CPU_OSM=y CONFIG_QCOM_MDSS_PLL=y CONFIG_REMOTE_SPINLOCK_MSM=y -CONFIG_IOMMU_IO_PGTABLE_FAST=y -CONFIG_IOMMU_IO_PGTABLE_FAST_SELFTEST=y CONFIG_ARM_SMMU=y CONFIG_IOMMU_DEBUG=y CONFIG_IOMMU_DEBUG_TRACKING=y @@ -642,7 +643,6 @@ CONFIG_DEBUG_OBJECTS_TIMERS=y CONFIG_DEBUG_OBJECTS_WORK=y CONFIG_DEBUG_OBJECTS_RCU_HEAD=y CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=y -CONFIG_SLUB_DEBUG_ON=y CONFIG_DEBUG_KMEMLEAK=y CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=4000 CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y diff --git a/arch/arm/crypto/aes-ce-glue.c b/arch/arm/crypto/aes-ce-glue.c index 679c589c4828..1f7b98e1a00d 100644 --- a/arch/arm/crypto/aes-ce-glue.c +++ b/arch/arm/crypto/aes-ce-glue.c @@ -369,7 +369,7 @@ static struct crypto_alg aes_algs[] = { { .cra_blkcipher = { .min_keysize = AES_MIN_KEY_SIZE, .max_keysize = AES_MAX_KEY_SIZE, - .ivsize = AES_BLOCK_SIZE, + .ivsize = 0, .setkey = ce_aes_setkey, .encrypt = ecb_encrypt, .decrypt = ecb_decrypt, @@ -446,7 +446,7 @@ static struct crypto_alg aes_algs[] = { { .cra_ablkcipher = { .min_keysize = AES_MIN_KEY_SIZE, .max_keysize = AES_MAX_KEY_SIZE, - .ivsize = AES_BLOCK_SIZE, + .ivsize = 0, .setkey = ablk_set_key, .encrypt = ablk_encrypt, .decrypt = ablk_decrypt, diff --git a/arch/arm/include/asm/dma-contiguous.h b/arch/arm/include/asm/dma-contiguous.h index 4f8e9e5514b1..d54f8feec78f 100644 --- a/arch/arm/include/asm/dma-contiguous.h +++ b/arch/arm/include/asm/dma-contiguous.h @@ -1,14 +1,25 @@ +/* + * Copyright (c) 2016-2017, 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 ASMARM_DMA_CONTIGUOUS_H #define ASMARM_DMA_CONTIGUOUS_H #ifdef __KERNEL__ -#ifdef CONFIG_DMA_CMA #include <linux/types.h> void dma_contiguous_early_fixup(phys_addr_t base, unsigned long size); #endif -#endif #endif diff --git a/arch/arm/kernel/vdso.c b/arch/arm/kernel/vdso.c index 54a5aeab988d..bbbffe946122 100644 --- a/arch/arm/kernel/vdso.c +++ b/arch/arm/kernel/vdso.c @@ -17,6 +17,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <linux/cache.h> #include <linux/elf.h> #include <linux/err.h> #include <linux/kernel.h> @@ -39,7 +40,7 @@ static struct page **vdso_text_pagelist; /* Total number of pages needed for the data and text portions of the VDSO. */ -unsigned int vdso_total_pages __read_mostly; +unsigned int vdso_total_pages __ro_after_init; /* * The VDSO data page. @@ -47,13 +48,13 @@ unsigned int vdso_total_pages __read_mostly; static union vdso_data_store vdso_data_store __page_aligned_data; static struct vdso_data *vdso_data = &vdso_data_store.data; -static struct page *vdso_data_page; -static struct vm_special_mapping vdso_data_mapping = { +static struct page *vdso_data_page __ro_after_init; +static const struct vm_special_mapping vdso_data_mapping = { .name = "[vvar]", .pages = &vdso_data_page, }; -static struct vm_special_mapping vdso_text_mapping = { +static struct vm_special_mapping vdso_text_mapping __ro_after_init = { .name = "[vdso]", }; @@ -67,7 +68,7 @@ struct elfinfo { /* Cached result of boot-time check for whether the arch timer exists, * and if so, whether the virtual counter is useable. */ -static bool cntvct_ok __read_mostly; +static bool cntvct_ok __ro_after_init; static bool __init cntvct_functional(void) { @@ -224,7 +225,7 @@ static int install_vvar(struct mm_struct *mm, unsigned long addr) VM_READ | VM_MAYREAD, &vdso_data_mapping); - return IS_ERR(vma) ? PTR_ERR(vma) : 0; + return PTR_ERR_OR_ZERO(vma); } /* assumes mmap_sem is write-locked */ diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c index 767872411d97..33ee522bb76f 100644 --- a/arch/arm/kvm/mmu.c +++ b/arch/arm/kvm/mmu.c @@ -301,6 +301,14 @@ static void unmap_range(struct kvm *kvm, pgd_t *pgdp, next = kvm_pgd_addr_end(addr, end); if (!pgd_none(*pgd)) unmap_puds(kvm, pgd, addr, next); + /* + * If we are dealing with a large range in + * stage2 table, release the kvm->mmu_lock + * to prevent starvation and lockup detector + * warnings. + */ + if (kvm && (next != end)) + cond_resched_lock(&kvm->mmu_lock); } while (pgd++, addr = next, addr != end); } @@ -745,6 +753,7 @@ int kvm_alloc_stage2_pgd(struct kvm *kvm) */ static void unmap_stage2_range(struct kvm *kvm, phys_addr_t start, u64 size) { + assert_spin_locked(&kvm->mmu_lock); unmap_range(kvm, kvm->arch.pgd, start, size); } @@ -803,6 +812,7 @@ void stage2_unmap_vm(struct kvm *kvm) int idx; idx = srcu_read_lock(&kvm->srcu); + down_read(¤t->mm->mmap_sem); spin_lock(&kvm->mmu_lock); slots = kvm_memslots(kvm); @@ -810,6 +820,7 @@ void stage2_unmap_vm(struct kvm *kvm) stage2_unmap_memslot(kvm, memslot); spin_unlock(&kvm->mmu_lock); + up_read(¤t->mm->mmap_sem); srcu_read_unlock(&kvm->srcu, idx); } @@ -829,7 +840,10 @@ void kvm_free_stage2_pgd(struct kvm *kvm) if (kvm->arch.pgd == NULL) return; + spin_lock(&kvm->mmu_lock); unmap_stage2_range(kvm, 0, KVM_PHYS_SIZE); + spin_unlock(&kvm->mmu_lock); + kvm_free_hwpgd(kvm_get_hwpgd(kvm)); if (KVM_PREALLOC_LEVEL > 0) kfree(kvm->arch.pgd); @@ -1771,6 +1785,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, (KVM_PHYS_SIZE >> PAGE_SHIFT)) return -EFAULT; + down_read(¤t->mm->mmap_sem); /* * A memory region could potentially cover multiple VMAs, and any holes * between them, so iterate over all of them to find out if we can map @@ -1814,8 +1829,10 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, pa += vm_start - vma->vm_start; /* IO region dirty page logging not allowed */ - if (memslot->flags & KVM_MEM_LOG_DIRTY_PAGES) - return -EINVAL; + if (memslot->flags & KVM_MEM_LOG_DIRTY_PAGES) { + ret = -EINVAL; + goto out; + } ret = kvm_phys_addr_ioremap(kvm, gpa, pa, vm_end - vm_start, @@ -1827,7 +1844,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, } while (hva < reg_end); if (change == KVM_MR_FLAGS_ONLY) - return ret; + goto out; spin_lock(&kvm->mmu_lock); if (ret) @@ -1835,6 +1852,8 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, else stage2_flush_memslot(kvm, memslot); spin_unlock(&kvm->mmu_lock); +out: + up_read(¤t->mm->mmap_sem); return ret; } diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c index 23726fb31741..d687f860a2da 100644 --- a/arch/arm/mach-at91/pm.c +++ b/arch/arm/mach-at91/pm.c @@ -286,6 +286,22 @@ static void at91_ddr_standby(void) at91_ramc_write(1, AT91_DDRSDRC_LPR, saved_lpr1); } +static void sama5d3_ddr_standby(void) +{ + u32 lpr0; + u32 saved_lpr0; + + saved_lpr0 = at91_ramc_read(0, AT91_DDRSDRC_LPR); + lpr0 = saved_lpr0 & ~AT91_DDRSDRC_LPCB; + lpr0 |= AT91_DDRSDRC_LPCB_POWER_DOWN; + + at91_ramc_write(0, AT91_DDRSDRC_LPR, lpr0); + + cpu_do_idle(); + + at91_ramc_write(0, AT91_DDRSDRC_LPR, saved_lpr0); +} + /* We manage both DDRAM/SDRAM controllers, we need more than one value to * remember. */ @@ -320,7 +336,7 @@ static const struct of_device_id const ramc_ids[] __initconst = { { .compatible = "atmel,at91rm9200-sdramc", .data = at91rm9200_standby }, { .compatible = "atmel,at91sam9260-sdramc", .data = at91sam9_sdram_standby }, { .compatible = "atmel,at91sam9g45-ddramc", .data = at91_ddr_standby }, - { .compatible = "atmel,sama5d3-ddramc", .data = at91_ddr_standby }, + { .compatible = "atmel,sama5d3-ddramc", .data = sama5d3_ddr_standby }, { /*sentinel*/ } }; diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c index f86692dbcfd5..83fc403aec3c 100644 --- a/arch/arm/mach-omap2/timer.c +++ b/arch/arm/mach-omap2/timer.c @@ -496,8 +496,7 @@ void __init omap_init_time(void) __omap_sync32k_timer_init(1, "timer_32k_ck", "ti,timer-alwon", 2, "timer_sys_ck", NULL, false); - if (of_have_populated_dt()) - clocksource_probe(); + clocksource_probe(); } #if defined(CONFIG_ARCH_OMAP3) || defined(CONFIG_SOC_AM43XX) @@ -505,6 +504,8 @@ void __init omap3_secure_sync32k_timer_init(void) { __omap_sync32k_timer_init(12, "secure_32k_fck", "ti,timer-secure", 2, "timer_sys_ck", NULL, false); + + clocksource_probe(); } #endif /* CONFIG_ARCH_OMAP3 */ @@ -513,6 +514,8 @@ void __init omap3_gptimer_timer_init(void) { __omap_sync32k_timer_init(2, "timer_sys_ck", NULL, 1, "timer_sys_ck", "ti,timer-alwon", true); + + clocksource_probe(); } #endif diff --git a/arch/arm/vdso/Makefile b/arch/arm/vdso/Makefile index 1160434eece0..59a8fa7b8a3b 100644 --- a/arch/arm/vdso/Makefile +++ b/arch/arm/vdso/Makefile @@ -74,5 +74,5 @@ $(MODLIB)/vdso: FORCE @mkdir -p $(MODLIB)/vdso PHONY += vdso_install -vdso_install: $(obj)/vdso.so.dbg $(MODLIB)/vdso FORCE +vdso_install: $(obj)/vdso.so.dbg $(MODLIB)/vdso $(call cmd,vdso_install) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 3c89828b9af2..0c369a5d59f9 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -540,6 +540,37 @@ config ARM64_64K_PAGES endchoice +config MSM_APP_API + bool "API support to enable / disable app settings for MSM8996" + depends on ARCH_MSM8996 && (ENABLE_FP_SIMD_SETTINGS || MSM_APP_SETTINGS) + help + Add API support to enable / disable the app settings to be used + at runtime. These APIs are used to enable / disable app setting + when specific aarch32 or aarch64 processes are running. + + If you are not sure what to do, select 'N' here. + +config ENABLE_FP_SIMD_SETTINGS + bool "Enable FP(Floating Point) Settings for Qualcomm MSM8996" + depends on ARCH_MSM8996 + select MSM_APP_API + help + Enable FP(Floating Point) and SIMD settings for the MSM8996 during + the execution of the aarch32 processes and disable these settings + when you switch to the aarch64 processes. + + If you are not sure what to do, select 'N' here. + +config MSM_APP_SETTINGS + bool "Support to enable / disable app settings for MSM8996" + depends on ARCH_MSM8996 + select MSM_APP_API + help + Expose an interface used by the userspace at runtime to + enable / disable the app specific settings. + + If you are not sure what to do, select 'N' here. + choice prompt "Virtual address space size" default ARM64_VA_BITS_39 if ARM64_4K_PAGES @@ -1023,6 +1054,26 @@ config BUILD_ARM64_APPENDED_DTB_IMAGE DTBs to be built by default (instead of a standalone Image.gz.) The image will built in arch/arm64/boot/Image.gz-dtb +choice + prompt "Appended DTB Kernel Image name" + depends on BUILD_ARM64_APPENDED_DTB_IMAGE + help + Enabling this option will cause a specific kernel image Image or + Image.gz to be used for final image creation. + The image will built in arch/arm64/boot/IMAGE-NAME-dtb + + config IMG_GZ_DTB + bool "Image.gz-dtb" + config IMG_DTB + bool "Image-dtb" +endchoice + +config BUILD_ARM64_APPENDED_KERNEL_IMAGE_NAME + string + depends on BUILD_ARM64_APPENDED_DTB_IMAGE + default "Image.gz-dtb" if IMG_GZ_DTB + default "Image-dtb" if IMG_DTB + config BUILD_ARM64_APPENDED_DTB_IMAGE_NAMES string "Default dtb names" depends on BUILD_ARM64_APPENDED_DTB_IMAGE diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile index fcfa3c7dedc1..f656c52d7e10 100644 --- a/arch/arm64/Makefile +++ b/arch/arm64/Makefile @@ -85,7 +85,7 @@ core-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a # Default target when executing plain make ifeq ($(CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE),y) -KBUILD_IMAGE := Image.gz-dtb +KBUILD_IMAGE := $(subst $\",,$(CONFIG_BUILD_ARM64_APPENDED_KERNEL_IMAGE_NAME)) else KBUILD_IMAGE := Image.gz endif @@ -132,6 +132,16 @@ archclean: $(Q)$(MAKE) $(clean)=$(boot) $(Q)$(MAKE) $(clean)=$(boot)/dts +# We need to generate vdso-offsets.h before compiling certain files in kernel/. +# In order to do that, we should use the archprepare target, but we can't since +# asm-offsets.h is included in some files used to generate vdso-offsets.h, and +# asm-offsets.h is built in prepare0, for which archprepare is a dependency. +# Therefore we need to generate the header after prepare0 has been made, hence +# this hack. +prepare: vdso_prepare +vdso_prepare: prepare0 + $(Q)$(MAKE) $(build)=arch/arm64/kernel/vdso include/generated/vdso-offsets.h + define archhelp echo '* Image.gz - Compressed kernel image (arch/$(ARCH)/boot/Image.gz)' echo ' Image - Uncompressed kernel image (arch/$(ARCH)/boot/Image)' diff --git a/arch/arm64/boot/dts/arm/juno-r1.dts b/arch/arm64/boot/dts/arm/juno-r1.dts index 93bc3d7d51c0..29315af22147 100644 --- a/arch/arm64/boot/dts/arm/juno-r1.dts +++ b/arch/arm64/boot/dts/arm/juno-r1.dts @@ -60,6 +60,28 @@ }; }; + idle-states { + entry-method = "arm,psci"; + + CPU_SLEEP_0: cpu-sleep-0 { + compatible = "arm,idle-state"; + arm,psci-suspend-param = <0x0010000>; + local-timer-stop; + entry-latency-us = <300>; + exit-latency-us = <1200>; + min-residency-us = <2000>; + }; + + CLUSTER_SLEEP_0: cluster-sleep-0 { + compatible = "arm,idle-state"; + arm,psci-suspend-param = <0x1010000>; + local-timer-stop; + entry-latency-us = <400>; + exit-latency-us = <1200>; + min-residency-us = <2500>; + }; + }; + A57_0: cpu@0 { compatible = "arm,cortex-a57","arm,armv8"; reg = <0x0 0x0>; @@ -67,6 +89,7 @@ enable-method = "psci"; next-level-cache = <&A57_L2>; clocks = <&scpi_dvfs 0>; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; }; A57_1: cpu@1 { @@ -76,6 +99,7 @@ enable-method = "psci"; next-level-cache = <&A57_L2>; clocks = <&scpi_dvfs 0>; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; }; A53_0: cpu@100 { @@ -85,6 +109,7 @@ enable-method = "psci"; next-level-cache = <&A53_L2>; clocks = <&scpi_dvfs 1>; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; }; A53_1: cpu@101 { @@ -94,6 +119,7 @@ enable-method = "psci"; next-level-cache = <&A53_L2>; clocks = <&scpi_dvfs 1>; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; }; A53_2: cpu@102 { @@ -103,6 +129,7 @@ enable-method = "psci"; next-level-cache = <&A53_L2>; clocks = <&scpi_dvfs 1>; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; }; A53_3: cpu@103 { @@ -112,6 +139,7 @@ enable-method = "psci"; next-level-cache = <&A53_L2>; clocks = <&scpi_dvfs 1>; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; }; A57_L2: l2-cache0 { diff --git a/arch/arm64/boot/dts/arm/juno-sched-energy.dtsi b/arch/arm64/boot/dts/arm/juno-sched-energy.dtsi new file mode 100644 index 000000000000..38207e4391ab --- /dev/null +++ b/arch/arm64/boot/dts/arm/juno-sched-energy.dtsi @@ -0,0 +1,147 @@ +/* + * ARM JUNO specific energy cost model data. There are no unit requirements for + * the data. Data can be normalized to any reference point, but the + * normalization must be consistent. That is, one bogo-joule/watt must be the + * same quantity for all data, but we don't care what it is. + */ + +/* static struct idle_state idle_states_cluster_a53[] = { */ +/* { .power = 56 }, /\* arch_cpu_idle() (active idle) = WFI *\/ */ +/* { .power = 56 }, /\* WFI *\/ */ +/* { .power = 56 }, /\* cpu-sleep-0 *\/ */ +/* { .power = 17 }, /\* cluster-sleep-0 *\/ */ +/* }; */ + +/* static struct idle_state idle_states_cluster_a57[] = { */ +/* { .power = 65 }, /\* arch_cpu_idle() (active idle) = WFI *\/ */ +/* { .power = 65 }, /\* WFI *\/ */ +/* { .power = 65 }, /\* cpu-sleep-0 *\/ */ +/* { .power = 24 }, /\* cluster-sleep-0 *\/ */ +/* }; */ + +/* static struct capacity_state cap_states_cluster_a53[] = { */ +/* /\* Power per cluster *\/ */ +/* { .cap = 235, .power = 26, }, /\* 450 MHz *\/ */ +/* { .cap = 303, .power = 30, }, /\* 575 MHz *\/ */ +/* { .cap = 368, .power = 39, }, /\* 700 MHz *\/ */ +/* { .cap = 406, .power = 47, }, /\* 775 MHz *\/ */ +/* { .cap = 447, .power = 57, }, /\* 850 Mhz *\/ */ +/* }; */ + +/* static struct capacity_state cap_states_cluster_a57[] = { */ +/* /\* Power per cluster *\/ */ +/* { .cap = 417, .power = 24, }, /\* 450 MHz *\/ */ +/* { .cap = 579, .power = 32, }, /\* 625 MHz *\/ */ +/* { .cap = 744, .power = 43, }, /\* 800 MHz *\/ */ +/* { .cap = 883, .power = 49, }, /\* 950 MHz *\/ */ +/* { .cap = 1024, .power = 64, }, /\* 1100 MHz *\/ */ +/* }; */ + +/* static struct sched_group_energy energy_cluster_a53 = { */ +/* .nr_idle_states = ARRAY_SIZE(idle_states_cluster_a53), */ +/* .idle_states = idle_states_cluster_a53, */ +/* .nr_cap_states = ARRAY_SIZE(cap_states_cluster_a53), */ +/* .cap_states = cap_states_cluster_a53, */ +/* }; */ + +/* static struct sched_group_energy energy_cluster_a57 = { */ +/* .nr_idle_states = ARRAY_SIZE(idle_states_cluster_a57), */ +/* .idle_states = idle_states_cluster_a57, */ +/* .nr_cap_states = ARRAY_SIZE(cap_states_cluster_a57), */ +/* .cap_states = cap_states_cluster_a57, */ +/* }; */ + +/* static struct idle_state idle_states_core_a53[] = { */ +/* { .power = 6 }, /\* arch_cpu_idle() (active idle) = WFI *\/ */ +/* { .power = 6 }, /\* WFI *\/ */ +/* { .power = 0 }, /\* cpu-sleep-0 *\/ */ +/* { .power = 0 }, /\* cluster-sleep-0 *\/ */ +/* }; */ + +/* static struct idle_state idle_states_core_a57[] = { */ +/* { .power = 15 }, /\* arch_cpu_idle() (active idle) = WFI *\/ */ +/* { .power = 15 }, /\* WFI *\/ */ +/* { .power = 0 }, /\* cpu-sleep-0 *\/ */ +/* { .power = 0 }, /\* cluster-sleep-0 *\/ */ +/* }; */ + +/* static struct capacity_state cap_states_core_a53[] = { */ +/* /\* Power per cpu *\/ */ +/* { .cap = 235, .power = 33, }, /\* 450 MHz *\/ */ +/* { .cap = 302, .power = 46, }, /\* 575 MHz *\/ */ +/* { .cap = 368, .power = 61, }, /\* 700 MHz *\/ */ +/* { .cap = 406, .power = 76, }, /\* 775 MHz *\/ */ +/* { .cap = 447, .power = 93, }, /\* 850 Mhz *\/ */ +/* }; */ + +/* static struct capacity_state cap_states_core_a57[] = { */ +/* /\* Power per cpu *\/ */ +/* { .cap = 417, .power = 168, }, /\* 450 MHz *\/ */ +/* { .cap = 579, .power = 251, }, /\* 625 MHz *\/ */ +/* { .cap = 744, .power = 359, }, /\* 800 MHz *\/ */ +/* { .cap = 883, .power = 479, }, /\* 950 MHz *\/ */ +/* { .cap = 1024, .power = 616, }, /\* 1100 MHz *\/ */ +/* }; */ + +energy-costs { + CPU_COST_A57: core-cost0 { + busy-cost-data = < + 417 168 + 579 251 + 744 359 + 883 479 + 1023 616 + >; + idle-cost-data = < + 15 + 15 + 0 + 0 + >; + }; + CPU_COST_A53: core-cost1 { + busy-cost-data = < + 235 33 + 302 46 + 368 61 + 406 76 + 447 93 + >; + idle-cost-data = < + 6 + 6 + 0 + 0 + >; + }; + CLUSTER_COST_A57: cluster-cost0 { + busy-cost-data = < + 417 24 + 579 32 + 744 43 + 883 49 + 1024 64 + >; + idle-cost-data = < + 65 + 65 + 65 + 24 + >; + }; + CLUSTER_COST_A53: cluster-cost1 { + busy-cost-data = < + 235 26 + 303 30 + 368 39 + 406 47 + 447 57 + >; + idle-cost-data = < + 56 + 56 + 56 + 17 + >; + }; +}; diff --git a/arch/arm64/boot/dts/arm/juno.dts b/arch/arm64/boot/dts/arm/juno.dts index 3e1a84b01b50..68816f71fa51 100644 --- a/arch/arm64/boot/dts/arm/juno.dts +++ b/arch/arm64/boot/dts/arm/juno.dts @@ -60,6 +60,28 @@ }; }; + idle-states { + entry-method = "arm,psci"; + + CPU_SLEEP_0: cpu-sleep-0 { + compatible = "arm,idle-state"; + arm,psci-suspend-param = <0x0010000>; + local-timer-stop; + entry-latency-us = <300>; + exit-latency-us = <1200>; + min-residency-us = <2000>; + }; + + CLUSTER_SLEEP_0: cluster-sleep-0 { + compatible = "arm,idle-state"; + arm,psci-suspend-param = <0x1010000>; + local-timer-stop; + entry-latency-us = <400>; + exit-latency-us = <1200>; + min-residency-us = <2500>; + }; + }; + A57_0: cpu@0 { compatible = "arm,cortex-a57","arm,armv8"; reg = <0x0 0x0>; @@ -67,6 +89,8 @@ enable-method = "psci"; next-level-cache = <&A57_L2>; clocks = <&scpi_dvfs 0>; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; + sched-energy-costs = <&CPU_COST_A57 &CLUSTER_COST_A57>; }; A57_1: cpu@1 { @@ -76,6 +100,8 @@ enable-method = "psci"; next-level-cache = <&A57_L2>; clocks = <&scpi_dvfs 0>; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; + sched-energy-costs = <&CPU_COST_A57 &CLUSTER_COST_A57>; }; A53_0: cpu@100 { @@ -85,6 +111,8 @@ enable-method = "psci"; next-level-cache = <&A53_L2>; clocks = <&scpi_dvfs 1>; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; + sched-energy-costs = <&CPU_COST_A53 &CLUSTER_COST_A53>; }; A53_1: cpu@101 { @@ -94,6 +122,8 @@ enable-method = "psci"; next-level-cache = <&A53_L2>; clocks = <&scpi_dvfs 1>; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; + sched-energy-costs = <&CPU_COST_A53 &CLUSTER_COST_A53>; }; A53_2: cpu@102 { @@ -103,6 +133,8 @@ enable-method = "psci"; next-level-cache = <&A53_L2>; clocks = <&scpi_dvfs 1>; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; + sched-energy-costs = <&CPU_COST_A53 &CLUSTER_COST_A53>; }; A53_3: cpu@103 { @@ -112,6 +144,8 @@ enable-method = "psci"; next-level-cache = <&A53_L2>; clocks = <&scpi_dvfs 1>; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; + sched-energy-costs = <&CPU_COST_A53 &CLUSTER_COST_A53>; }; A57_L2: l2-cache0 { @@ -121,6 +155,8 @@ A53_L2: l2-cache1 { compatible = "cache"; }; + + /include/ "juno-sched-energy.dtsi" }; pmu_a57 { diff --git a/arch/arm64/configs/msm-auto-perf_defconfig b/arch/arm64/configs/msm-auto-perf_defconfig new file mode 100644 index 000000000000..eb1ebee6c2a0 --- /dev/null +++ b/arch/arm64/configs/msm-auto-perf_defconfig @@ -0,0 +1,634 @@ +CONFIG_LOCALVERSION="-perf" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_AUDIT=y +# CONFIG_AUDITSYSCALL is not set +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_TASKSTATS=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_RCU_EXPERT=y +CONFIG_RCU_FAST_NO_HZ=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_CPU_MAX_BUF_SHIFT=15 +CONFIG_CGROUPS=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_SCHED_HMP=y +CONFIG_NAMESPACES=y +# CONFIG_UTS_NS is not set +# CONFIG_PID_NS is not set +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +# CONFIG_RD_LZ4 is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_KALLSYMS_ALL=y +# CONFIG_MEMBARRIER is not set +CONFIG_EMBEDDED=y +# CONFIG_SLUB_DEBUG is not set +# CONFIG_COMPAT_BRK is not set +CONFIG_PROFILING=y +CONFIG_CC_STACKPROTECTOR_REGULAR=y +CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16 +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SIG=y +CONFIG_MODULE_SIG_FORCE=y +CONFIG_MODULE_SIG_SHA512=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_ARCH_QCOM=y +CONFIG_ARCH_MSM8996=y +CONFIG_PCI=y +CONFIG_PCI_MSM=y +CONFIG_ENABLE_FP_SIMD_SETTINGS=y +CONFIG_MSM_APP_SETTINGS=y +CONFIG_SCHED_MC=y +CONFIG_NR_CPUS=8 +CONFIG_PREEMPT=y +CONFIG_HZ_100=y +CONFIG_CMA=y +CONFIG_ZSMALLOC=y +CONFIG_BALANCE_ANON_FILE_RECLAIM=y +CONFIG_FORCE_ALLOC_FROM_DMA_ZONE=y +CONFIG_SECCOMP=y +CONFIG_ARMV8_DEPRECATED=y +CONFIG_SWP_EMULATION=y +# CONFIG_EFI is not set +CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_COMPAT=y +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=0 +# CONFIG_PM_WAKELOCKS_GC is not set +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_BOOST=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=y +CONFIG_XFRM_STATISTICS=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_INET_AH=y +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=y +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG_DESTROY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_HARDIDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_LOG=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_NOTRACK=y +CONFIG_NETFILTER_XT_TARGET_TEE=y +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TRACE=y +CONFIG_NETFILTER_XT_TARGET_SECMARK=y +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_DSCP=y +CONFIG_NETFILTER_XT_MATCH_ESP=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +# CONFIG_NETFILTER_XT_MATCH_L2TP is not set +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_RPFILTER=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_SECURITY=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_RPFILTER=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_BRIDGE_NF_EBTABLES=y +CONFIG_BRIDGE_EBT_BROUTE=y +CONFIG_L2TP=y +CONFIG_L2TP_V3=y +CONFIG_L2TP_IP=y +CONFIG_L2TP_ETH=y +CONFIG_BRIDGE=y +CONFIG_VLAN_8021Q=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_PRIO=y +CONFIG_NET_SCH_MULTIQ=y +CONFIG_NET_SCH_INGRESS=y +CONFIG_NET_CLS_FW=y +CONFIG_NET_CLS_U32=y +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_FLOW=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_CMP=y +CONFIG_NET_EMATCH_NBYTE=y +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_EMATCH_META=y +CONFIG_NET_EMATCH_TEXT=y +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_GACT=y +CONFIG_NET_ACT_MIRRED=y +CONFIG_NET_ACT_SKBEDIT=y +CONFIG_RMNET_DATA=y +CONFIG_RMNET_DATA_FC=y +CONFIG_RMNET_DATA_DEBUG_PKT=y +CONFIG_SOCKEV_NLMCAST=y +CONFIG_CAN=y +CONFIG_CAN_RH850=y +CONFIG_BT=y +CONFIG_MSM_BT_POWER=y +CONFIG_BTFM_SLIM=y +CONFIG_BTFM_SLIM_WCN3990=y +CONFIG_CFG80211=y +CONFIG_RFKILL=y +CONFIG_IPC_ROUTER=y +CONFIG_IPC_ROUTER_SECURITY=y +CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y +CONFIG_DMA_CMA=y +# CONFIG_PNP_DEBUG_MESSAGES is not set +CONFIG_ZRAM=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_QSEECOM=y +CONFIG_HDCP_QSEECOM=y +CONFIG_PROFILER=y +CONFIG_UID_SYS_STATS=y +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_CHR_DEV_SCH=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +CONFIG_SCSI_UFSHCD=y +CONFIG_SCSI_UFSHCD_PLATFORM=y +CONFIG_SCSI_UFS_QCOM=y +CONFIG_SCSI_UFS_QCOM_ICE=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_CRYPT=y +CONFIG_DM_REQ_CRYPT=y +CONFIG_DM_UEVENT=y +CONFIG_DM_VERITY=y +CONFIG_NETDEVICES=y +CONFIG_BONDING=y +CONFIG_DUMMY=y +CONFIG_TUN=y +CONFIG_E1000E=y +CONFIG_MSM_RMNET_MHI=y +CONFIG_RNDIS_IPA=y +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_MPPE=y +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +CONFIG_USB_USBNET=y +CONFIG_WCNSS_MEM_PRE_ALLOC=y +CONFIG_CNSS_CRYPTO=y +CONFIG_ATH_CARDS=y +CONFIG_WIL6210=m +CONFIG_CNSS=y +CONFIG_CNSS_ASYNC=y +CONFIG_CLD_LL_CORE=y +CONFIG_BUS_AUTO_SUSPEND=y +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_KEYRESET=y +CONFIG_KEYBOARD_GPIO=y +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_JOYSTICK=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y +CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y +CONFIG_TOUCHSCREEN_ATMEL_MXT=y +CONFIG_TOUCHSCREEN_ATMEL_MAXTOUCH_TS=y +CONFIG_SECURE_TOUCH=y +CONFIG_TOUCHSCREEN_GEN_VKEYS=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_HBTP_INPUT=y +CONFIG_INPUT_QPNP_POWER_ON=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_SERIO_SERPORT is not set +# CONFIG_VT is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_MSM_HS=y +CONFIG_SERIAL_MSM_SMD=y +CONFIG_DIAG_CHAR=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_MSM_LEGACY=y +CONFIG_MSM_ADSPRPC=y +CONFIG_MSM_RDBG=m +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_QUP=y +CONFIG_I2C_MSM_V2=y +CONFIG_SLIMBUS_MSM_NGD=y +CONFIG_SOUNDWIRE=y +CONFIG_SPI=y +CONFIG_SPI_QUP=y +CONFIG_SPI_SPIDEV=y +CONFIG_SPMI=y +CONFIG_PINCTRL_MSM8996=y +CONFIG_GPIOLIB=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_QPNP_PIN=y +CONFIG_POWER_RESET_QCOM=y +CONFIG_QCOM_DLOAD_MODE=y +CONFIG_POWER_RESET_XGENE=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_SMB135X_CHARGER=y +CONFIG_SMB1351_USB_CHARGER=y +CONFIG_MSM_BCL_CTL=y +CONFIG_MSM_BCL_PERIPHERAL_CTL=y +CONFIG_MSM_PM=y +CONFIG_APSS_CORE_EA=y +CONFIG_MSM_APM=y +CONFIG_SENSORS_EPM_ADC=y +CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y +CONFIG_LIMITS_MONITOR=y +CONFIG_LIMITS_LITE_HW=y +CONFIG_THERMAL_MONITOR=y +CONFIG_THERMAL_TSENS8974=y +CONFIG_THERMAL_QPNP_ADC_TM=y +CONFIG_MFD_SPMI_PMIC=y +CONFIG_WCD9335_CODEC=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_FAN53555=y +CONFIG_REGULATOR_ONSEMI_NCP6335D=y +CONFIG_REGULATOR_RPM_SMD=y +CONFIG_REGULATOR_QPNP=y +CONFIG_REGULATOR_QPNP_LABIBB=y +CONFIG_REGULATOR_SPM=y +CONFIG_REGULATOR_CPR3_HMSS=y +CONFIG_REGULATOR_CPR3_MMSS=y +CONFIG_REGULATOR_KRYO=y +CONFIG_REGULATOR_MEM_ACC=y +CONFIG_REGULATOR_PROXY_CONSUMER=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_SOC_CAMERA=y +CONFIG_SOC_CAMERA_PLATFORM=y +CONFIG_MSM_CAMERA=y +CONFIG_MSM_CAMERA_DEBUG=y +CONFIG_MSMB_CAMERA=y +CONFIG_MSMB_CAMERA_DEBUG=y +CONFIG_MSM_CAMERA_SENSOR=y +CONFIG_MSM_CPP=y +CONFIG_MSM_CCI=y +CONFIG_MSM_CSI20_HEADER=y +CONFIG_MSM_CSI22_HEADER=y +CONFIG_MSM_CSI30_HEADER=y +CONFIG_MSM_CSI31_HEADER=y +CONFIG_MSM_CSIPHY=y +CONFIG_MSM_CSID=y +CONFIG_MSM_EEPROM=y +CONFIG_MSM_ISPIF=y +CONFIG_IMX134=y +CONFIG_IMX132=y +CONFIG_OV9724=y +CONFIG_OV5648=y +CONFIG_GC0339=y +CONFIG_OV8825=y +CONFIG_OV8865=y +CONFIG_s5k4e1=y +CONFIG_OV12830=y +CONFIG_MSMB_JPEG=y +CONFIG_MSM_FD=y +CONFIG_MSM_JPEGDMA=y +CONFIG_MSM_VIDC_V4L2=y +CONFIG_MSM_VIDC_VMEM=y +CONFIG_MSM_VIDC_GOVERNORS=y +CONFIG_MSM_SDE_ROTATOR=y +CONFIG_QCOM_KGSL=y +CONFIG_FB=y +CONFIG_FB_MSM=y +CONFIG_FB_MSM_MDSS=y +CONFIG_FB_MSM_MDSS_WRITEBACK=y +CONFIG_FB_MSM_MDSS_HDMI_PANEL=y +CONFIG_FB_MSM_MDSS_XLOG_DEBUG=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_GENERIC=m +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_USB_AUDIO=y +CONFIG_SND_SOC=y +CONFIG_SND_SOC_MSM8996=y +CONFIG_UHID=y +CONFIG_HID_APPLE=y +CONFIG_HID_ELECOM=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MULTITOUCH=y +CONFIG_USB=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_HCD_PLATFORM=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD_PLATFORM=y +CONFIG_USB_STORAGE=y +CONFIG_USB_DWC3=y +CONFIG_USB_ISP1760=y +CONFIG_USB_QTI_KS_BRIDGE=y +CONFIG_NOP_USB_XCEIV=y +CONFIG_USB_MSM_SSPHY_QMP=y +CONFIG_MSM_QUSB_PHY=y +CONFIG_USB_ULPI=y +CONFIG_USB_GADGET=y +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_CONFIGFS=y +CONFIG_USB_CONFIGFS_SERIAL=y +CONFIG_USB_CONFIGFS_NCM=y +CONFIG_USB_CONFIGFS_ECM=y +CONFIG_USB_CONFIGFS_QCRNDIS=y +CONFIG_USB_CONFIGFS_RMNET_BAM=y +CONFIG_USB_CONFIGFS_MASS_STORAGE=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_MTP=y +CONFIG_USB_CONFIGFS_F_PTP=y +CONFIG_USB_CONFIGFS_F_ACC=y +CONFIG_USB_CONFIGFS_UEVENT=y +CONFIG_USB_CONFIGFS_F_DIAG=y +CONFIG_USB_CONFIGFS_F_CDEV=y +CONFIG_USB_CONFIGFS_F_QDSS=y +CONFIG_MMC=y +CONFIG_MMC_PERF_PROFILING=y +CONFIG_MMC_PARANOID_SD_INIT=y +CONFIG_MMC_CLKGATE=y +CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_TEST=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_MSM=y +CONFIG_MMC_SPI=y +CONFIG_MMC_DW=y +CONFIG_MMC_DW_EXYNOS=y +CONFIG_MMC_CQ_HCI=y +CONFIG_LEDS_QPNP=y +CONFIG_LEDS_QPNP_FLASH_V2=y +CONFIG_LEDS_QPNP_WLED=y +CONFIG_LEDS_SYSCON=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_CPU=y +CONFIG_SWITCH=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_QPNP=y +CONFIG_ESOC=y +CONFIG_ESOC_DEV=y +CONFIG_ESOC_CLIENT=y +CONFIG_ESOC_MDM_4x=y +CONFIG_DMADEVICES=y +CONFIG_QCOM_BAM_DMA=y +CONFIG_QCOM_SPS_DMA=y +CONFIG_UIO=y +CONFIG_UIO_MSM_SHAREDMEM=y +CONFIG_STAGING=y +CONFIG_ASHMEM=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_SW_SYNC_USER=y +CONFIG_ION=y +CONFIG_ION_MSM=y +CONFIG_QPNP_REVID=y +CONFIG_QPNP_COINCELL=y +CONFIG_SPS=y +CONFIG_SPS_SUPPORT_NDP_BAM=y +CONFIG_IPA=y +CONFIG_RMNET_IPA=y +CONFIG_GSI=y +CONFIG_IPA3=y +CONFIG_RMNET_IPA3=y +CONFIG_IPA_UT=y +CONFIG_GPIO_USB_DETECT=y +CONFIG_MSM_MHI=y +CONFIG_MSM_MHI_UCI=y +CONFIG_SEEMP_CORE=y +CONFIG_USB_BAM=y +CONFIG_MSM_MDSS_PLL=y +CONFIG_REMOTE_SPINLOCK_MSM=y +CONFIG_IOMMU_IO_PGTABLE_FAST=y +CONFIG_ARM_SMMU=y +CONFIG_IOMMU_DEBUG=y +CONFIG_IOMMU_TESTS=y +CONFIG_MSM_SMEM=y +CONFIG_QPNP_HAPTIC=y +CONFIG_MSM_SMD=y +CONFIG_MSM_GLINK=y +CONFIG_MSM_GLINK_LOOPBACK_SERVER=y +CONFIG_MSM_GLINK_SMD_XPRT=y +CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y +CONFIG_MSM_SMEM_LOGGING=y +CONFIG_MSM_SMP2P=y +CONFIG_MSM_SMP2P_TEST=y +CONFIG_MSM_QMI_INTERFACE=y +CONFIG_MSM_RPM_SMD=y +CONFIG_QCOM_BUS_SCALING=y +CONFIG_MSM_SERVICE_LOCATOR=y +CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y +CONFIG_MSM_SYSMON_GLINK_COMM=y +CONFIG_MSM_IPC_ROUTER_MHI_XPRT=y +CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y +CONFIG_MSM_GLINK_PKT=y +CONFIG_MSM_SPM=y +CONFIG_MSM_L2_SPM=y +CONFIG_QCOM_SCM=y +CONFIG_QCOM_SCM_XPU=y +CONFIG_QCOM_WATCHDOG_V2=y +CONFIG_QCOM_MEMORY_DUMP_V2=y +CONFIG_MSM_RUN_QUEUE_STATS=y +CONFIG_MSM_BOOT_STATS=y +CONFIG_MSM_BOOT_TIME_MARKER=y +CONFIG_MSM_ADSP_LOADER=y +CONFIG_MSM_PERFORMANCE=y +CONFIG_MSM_SUBSYSTEM_RESTART=y +CONFIG_MSM_PIL=y +CONFIG_MSM_PIL_SSR_GENERIC=y +CONFIG_MSM_PIL_MSS_QDSP6V5=y +CONFIG_TRACER_PKT=y +CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y +CONFIG_MSM_MPM_OF=y +CONFIG_MSM_EVENT_TIMER=y +CONFIG_MSM_AVTIMER=y +CONFIG_QCOM_REMOTEQDSS=y +CONFIG_MSM_SERVICE_NOTIFIER=y +CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y +CONFIG_MSM_RPM_LOG=y +CONFIG_MSM_RPM_STATS_LOG=y +CONFIG_QCOM_SMCINVOKE=y +CONFIG_MSM_CACHE_M4M_ERP64=y +CONFIG_MSM_CACHE_M4M_ERP64_PANIC_ON_CE=y +CONFIG_MSM_CACHE_M4M_ERP64_PANIC_ON_UE=y +CONFIG_MEM_SHARE_QMI_SERVICE=y +CONFIG_QCOM_BIMC_BWMON=y +CONFIG_ARM_MEMLAT_MON=y +CONFIG_QCOM_M4M_HWMON=y +CONFIG_DEVFREQ_GOV_QCOM_BW_HWMON=y +CONFIG_DEVFREQ_GOV_QCOM_CACHE_HWMON=y +CONFIG_DEVFREQ_GOV_MEMLAT=y +CONFIG_DEVFREQ_SIMPLE_DEV=y +CONFIG_QCOM_DEVFREQ_DEVBW=y +CONFIG_EXTCON=y +CONFIG_PWM=y +CONFIG_PWM_QPNP=y +CONFIG_ARM_GIC_V3_ACL=y +CONFIG_PHY_XGENE=y +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_SECURITY=y +CONFIG_EXT4_ENCRYPTION=y +CONFIG_EXT4_FS_ENCRYPTION=y +CONFIG_EXT4_FS_ICE_ENCRYPTION=y +CONFIG_FUSE_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_ECRYPT_FS=y +CONFIG_ECRYPT_FS_MESSAGING=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_DEBUG_INFO=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +CONFIG_IPC_LOGGING=y +CONFIG_CPU_FREQ_SWITCH_PROFILER=y +CONFIG_DEBUG_SET_MODULE_RONX=y +CONFIG_DEBUG_ALIGN_RODATA=y +CONFIG_CORESIGHT=y +CONFIG_CORESIGHT_EVENT=y +CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y +CONFIG_CORESIGHT_QCOM_REPLICATOR=y +CONFIG_CORESIGHT_STM=y +CONFIG_CORESIGHT_HWEVENT=y +CONFIG_CORESIGHT_CTI=y +CONFIG_CORESIGHT_TPDA=y +CONFIG_CORESIGHT_TPDM=y +CONFIG_CORESIGHT_QPDI=y +CONFIG_CORESIGHT_SOURCE_DUMMY=y +CONFIG_PFK=y +CONFIG_SECURITY=y +CONFIG_SECURITY_SELINUX=y +CONFIG_SECURITY_SMACK=y +CONFIG_CRYPTO_ECHAINIV=y +CONFIG_CRYPTO_XCBC=y +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_ANSI_CPRNG=y +CONFIG_CRYPTO_DEV_QCRYPTO=y +CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y +CONFIG_CRYPTO_DEV_QCEDEV=y +CONFIG_CRYPTO_DEV_OTA_CRYPTO=y +CONFIG_CRYPTO_DEV_QCOM_ICE=y +CONFIG_ARM64_CRYPTO=y +CONFIG_CRYPTO_SHA1_ARM64_CE=y +CONFIG_CRYPTO_SHA2_ARM64_CE=y +CONFIG_CRYPTO_GHASH_ARM64_CE=y +CONFIG_CRYPTO_AES_ARM64_CE_CCM=y +CONFIG_CRYPTO_AES_ARM64_CE_BLK=y +CONFIG_CRYPTO_AES_ARM64_NEON_BLK=y +CONFIG_CRYPTO_CRC32_ARM64=y +CONFIG_QMI_ENCDEC=y diff --git a/arch/arm64/configs/msm-auto_defconfig b/arch/arm64/configs/msm-auto_defconfig new file mode 100644 index 000000000000..e3fbb5166478 --- /dev/null +++ b/arch/arm64/configs/msm-auto_defconfig @@ -0,0 +1,679 @@ +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_AUDIT=y +# CONFIG_AUDITSYSCALL is not set +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_TASKSTATS=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_RCU_EXPERT=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_CPU_MAX_BUF_SHIFT=15 +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_SCHED_HMP=y +CONFIG_NAMESPACES=y +# CONFIG_UTS_NS is not set +# CONFIG_PID_NS is not set +CONFIG_BLK_DEV_INITRD=y +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +# CONFIG_RD_LZ4 is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_KALLSYMS_ALL=y +# CONFIG_MEMBARRIER is not set +CONFIG_EMBEDDED=y +# CONFIG_COMPAT_BRK is not set +CONFIG_PROFILING=y +CONFIG_CC_STACKPROTECTOR_REGULAR=y +CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16 +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SIG=y +CONFIG_MODULE_SIG_FORCE=y +CONFIG_MODULE_SIG_SHA512=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_ARCH_QCOM=y +CONFIG_ARCH_MSM8996=y +CONFIG_PCI=y +CONFIG_PCI_MSM=y +CONFIG_ENABLE_FP_SIMD_SETTINGS=y +CONFIG_MSM_APP_SETTINGS=y +CONFIG_SCHED_MC=y +CONFIG_NR_CPUS=8 +CONFIG_PREEMPT=y +CONFIG_HZ_100=y +CONFIG_CMA=y +CONFIG_CMA_DEBUGFS=y +CONFIG_ZSMALLOC=y +CONFIG_BALANCE_ANON_FILE_RECLAIM=y +CONFIG_FORCE_ALLOC_FROM_DMA_ZONE=y +CONFIG_SECCOMP=y +CONFIG_ARMV8_DEPRECATED=y +CONFIG_SWP_EMULATION=y +CONFIG_CMDLINE="console=ttyAMA0" +# CONFIG_EFI is not set +CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_COMPAT=y +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=0 +# CONFIG_PM_WAKELOCKS_GC is not set +CONFIG_PM_DEBUG=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_BOOST=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=y +CONFIG_XFRM_STATISTICS=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_INET_AH=y +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=y +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG_DESTROY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_HARDIDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_LOG=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_NOTRACK=y +CONFIG_NETFILTER_XT_TARGET_TEE=y +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TRACE=y +CONFIG_NETFILTER_XT_TARGET_SECMARK=y +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_DSCP=y +CONFIG_NETFILTER_XT_MATCH_ESP=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +# CONFIG_NETFILTER_XT_MATCH_L2TP is not set +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_RPFILTER=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_SECURITY=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_RPFILTER=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_BRIDGE_NF_EBTABLES=y +CONFIG_BRIDGE_EBT_BROUTE=y +CONFIG_L2TP=y +CONFIG_L2TP_DEBUGFS=y +CONFIG_L2TP_V3=y +CONFIG_L2TP_IP=y +CONFIG_L2TP_ETH=y +CONFIG_BRIDGE=y +CONFIG_VLAN_8021Q=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_PRIO=y +CONFIG_NET_SCH_MULTIQ=y +CONFIG_NET_SCH_INGRESS=y +CONFIG_NET_CLS_FW=y +CONFIG_NET_CLS_U32=y +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_FLOW=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_CMP=y +CONFIG_NET_EMATCH_NBYTE=y +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_EMATCH_META=y +CONFIG_NET_EMATCH_TEXT=y +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_GACT=y +CONFIG_NET_ACT_MIRRED=y +CONFIG_NET_ACT_SKBEDIT=y +CONFIG_DNS_RESOLVER=y +CONFIG_RMNET_DATA=y +CONFIG_RMNET_DATA_FC=y +CONFIG_RMNET_DATA_DEBUG_PKT=y +CONFIG_SOCKEV_NLMCAST=y +CONFIG_CAN=y +CONFIG_CAN_RH850=y +CONFIG_BT=y +CONFIG_MSM_BT_POWER=y +CONFIG_BTFM_SLIM=y +CONFIG_BTFM_SLIM_WCN3990=y +CONFIG_CFG80211=y +CONFIG_RFKILL=y +CONFIG_IPC_ROUTER=y +CONFIG_IPC_ROUTER_SECURITY=y +CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y +CONFIG_DMA_CMA=y +CONFIG_ZRAM=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_QSEECOM=y +CONFIG_HDCP_QSEECOM=y +CONFIG_PROFILER=y +CONFIG_UID_SYS_STATS=y +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_CHR_DEV_SCH=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +CONFIG_SCSI_UFSHCD=y +CONFIG_SCSI_UFSHCD_PLATFORM=y +CONFIG_SCSI_UFS_QCOM=y +CONFIG_SCSI_UFS_QCOM_ICE=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_CRYPT=y +CONFIG_DM_REQ_CRYPT=y +CONFIG_DM_UEVENT=y +CONFIG_DM_VERITY=y +CONFIG_NETDEVICES=y +CONFIG_BONDING=y +CONFIG_DUMMY=y +CONFIG_TUN=y +CONFIG_E1000E=y +CONFIG_MSM_RMNET_MHI=y +CONFIG_RNDIS_IPA=y +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_MPPE=y +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +CONFIG_USB_USBNET=y +CONFIG_WCNSS_MEM_PRE_ALLOC=y +CONFIG_CNSS_CRYPTO=y +CONFIG_ATH_CARDS=y +CONFIG_WIL6210=m +CONFIG_CNSS=y +CONFIG_CNSS_ASYNC=y +CONFIG_CLD_LL_CORE=y +CONFIG_BUS_AUTO_SUSPEND=y +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_KEYRESET=y +CONFIG_KEYBOARD_GPIO=y +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_JOYSTICK=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y +CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y +CONFIG_TOUCHSCREEN_ATMEL_MXT=y +CONFIG_TOUCHSCREEN_ATMEL_MAXTOUCH_TS=y +CONFIG_SECURE_TOUCH=y +CONFIG_TOUCHSCREEN_GEN_VKEYS=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_HBTP_INPUT=y +CONFIG_INPUT_QPNP_POWER_ON=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_SERIO_SERPORT is not set +# CONFIG_VT is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +CONFIG_SERIAL_MSM_HS=y +CONFIG_SERIAL_MSM_SMD=y +CONFIG_DIAG_CHAR=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_MSM_LEGACY=y +CONFIG_MSM_ADSPRPC=y +CONFIG_MSM_RDBG=m +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_QUP=y +CONFIG_I2C_MSM_V2=y +CONFIG_SLIMBUS_MSM_NGD=y +CONFIG_SOUNDWIRE=y +CONFIG_SPI=y +CONFIG_SPI_QUP=y +CONFIG_SPI_SPIDEV=y +CONFIG_SPMI=y +CONFIG_PINCTRL_MSM8996=y +CONFIG_GPIOLIB=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_QPNP_PIN=y +CONFIG_POWER_RESET_QCOM=y +CONFIG_QCOM_DLOAD_MODE=y +CONFIG_POWER_RESET_XGENE=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_SMB135X_CHARGER=y +CONFIG_SMB1351_USB_CHARGER=y +CONFIG_MSM_BCL_CTL=y +CONFIG_MSM_BCL_PERIPHERAL_CTL=y +CONFIG_MSM_PM=y +CONFIG_APSS_CORE_EA=y +CONFIG_MSM_APM=y +CONFIG_SENSORS_EPM_ADC=y +CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y +CONFIG_LIMITS_MONITOR=y +CONFIG_LIMITS_LITE_HW=y +CONFIG_THERMAL_MONITOR=y +CONFIG_THERMAL_TSENS8974=y +CONFIG_THERMAL_QPNP_ADC_TM=y +CONFIG_MFD_SPMI_PMIC=y +CONFIG_WCD9335_CODEC=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_FAN53555=y +CONFIG_REGULATOR_ONSEMI_NCP6335D=y +CONFIG_REGULATOR_RPM_SMD=y +CONFIG_REGULATOR_QPNP=y +CONFIG_REGULATOR_QPNP_LABIBB=y +CONFIG_REGULATOR_SPM=y +CONFIG_REGULATOR_CPR3_HMSS=y +CONFIG_REGULATOR_CPR3_MMSS=y +CONFIG_REGULATOR_KRYO=y +CONFIG_REGULATOR_MEM_ACC=y +CONFIG_REGULATOR_PROXY_CONSUMER=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y +CONFIG_VIDEO_ADV_DEBUG=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_SOC_CAMERA=y +CONFIG_SOC_CAMERA_PLATFORM=y +CONFIG_MSM_CAMERA=y +CONFIG_MSM_CAMERA_DEBUG=y +CONFIG_MSMB_CAMERA=y +CONFIG_MSMB_CAMERA_DEBUG=y +CONFIG_MSM_CAMERA_SENSOR=y +CONFIG_MSM_CPP=y +CONFIG_MSM_CCI=y +CONFIG_MSM_CSI20_HEADER=y +CONFIG_MSM_CSI22_HEADER=y +CONFIG_MSM_CSI30_HEADER=y +CONFIG_MSM_CSI31_HEADER=y +CONFIG_MSM_CSIPHY=y +CONFIG_MSM_CSID=y +CONFIG_MSM_EEPROM=y +CONFIG_MSM_ISPIF=y +CONFIG_IMX134=y +CONFIG_IMX132=y +CONFIG_OV9724=y +CONFIG_OV5648=y +CONFIG_GC0339=y +CONFIG_OV8825=y +CONFIG_OV8865=y +CONFIG_s5k4e1=y +CONFIG_OV12830=y +CONFIG_MSMB_JPEG=y +CONFIG_MSM_FD=y +CONFIG_MSM_JPEGDMA=y +CONFIG_MSM_VIDC_V4L2=y +CONFIG_MSM_VIDC_VMEM=y +CONFIG_MSM_VIDC_GOVERNORS=y +CONFIG_MSM_SDE_ROTATOR=y +CONFIG_QCOM_KGSL=y +CONFIG_FB=y +CONFIG_FB_MSM=y +CONFIG_FB_MSM_MDSS=y +CONFIG_FB_MSM_MDSS_WRITEBACK=y +CONFIG_FB_MSM_MDSS_HDMI_PANEL=y +CONFIG_FB_MSM_MDSS_XLOG_DEBUG=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_GENERIC=m +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_USB_AUDIO=y +CONFIG_SND_SOC=y +CONFIG_SND_SOC_MSM8996=y +CONFIG_UHID=y +CONFIG_HID_APPLE=y +CONFIG_HID_ELECOM=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MULTITOUCH=y +CONFIG_USB=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_HCD_PLATFORM=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD_PLATFORM=y +CONFIG_USB_STORAGE=y +CONFIG_USB_DWC3=y +CONFIG_USB_ISP1760=y +CONFIG_USB_QTI_KS_BRIDGE=y +CONFIG_NOP_USB_XCEIV=y +CONFIG_USB_MSM_SSPHY_QMP=y +CONFIG_MSM_QUSB_PHY=y +CONFIG_USB_ULPI=y +CONFIG_USB_GADGET=y +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_CONFIGFS=y +CONFIG_USB_CONFIGFS_SERIAL=y +CONFIG_USB_CONFIGFS_NCM=y +CONFIG_USB_CONFIGFS_ECM=y +CONFIG_USB_CONFIGFS_QCRNDIS=y +CONFIG_USB_CONFIGFS_RMNET_BAM=y +CONFIG_USB_CONFIGFS_MASS_STORAGE=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_MTP=y +CONFIG_USB_CONFIGFS_F_PTP=y +CONFIG_USB_CONFIGFS_F_ACC=y +CONFIG_USB_CONFIGFS_UEVENT=y +CONFIG_USB_CONFIGFS_F_DIAG=y +CONFIG_USB_CONFIGFS_F_CDEV=y +CONFIG_USB_CONFIGFS_F_QDSS=y +CONFIG_MMC=y +CONFIG_MMC_PERF_PROFILING=y +CONFIG_MMC_PARANOID_SD_INIT=y +CONFIG_MMC_CLKGATE=y +CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_TEST=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_MSM=y +CONFIG_MMC_SPI=y +CONFIG_MMC_DW=y +CONFIG_MMC_DW_EXYNOS=y +CONFIG_MMC_CQ_HCI=y +CONFIG_LEDS_QPNP=y +CONFIG_LEDS_QPNP_FLASH_V2=y +CONFIG_LEDS_QPNP_WLED=y +CONFIG_LEDS_SYSCON=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_CPU=y +CONFIG_SWITCH=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_QPNP=y +CONFIG_ESOC=y +CONFIG_ESOC_DEV=y +CONFIG_ESOC_CLIENT=y +CONFIG_ESOC_DEBUG=y +CONFIG_ESOC_MDM_4x=y +CONFIG_DMADEVICES=y +CONFIG_QCOM_BAM_DMA=y +CONFIG_QCOM_SPS_DMA=y +CONFIG_UIO=y +CONFIG_UIO_MSM_SHAREDMEM=y +CONFIG_STAGING=y +CONFIG_ASHMEM=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_SW_SYNC_USER=y +CONFIG_ION=y +CONFIG_ION_MSM=y +CONFIG_QPNP_REVID=y +CONFIG_QPNP_COINCELL=y +CONFIG_SPS=y +CONFIG_SPS_SUPPORT_NDP_BAM=y +CONFIG_IPA=y +CONFIG_RMNET_IPA=y +CONFIG_GSI=y +CONFIG_IPA3=y +CONFIG_RMNET_IPA3=y +CONFIG_IPA_UT=y +CONFIG_GPIO_USB_DETECT=y +CONFIG_MSM_MHI=y +CONFIG_MSM_MHI_UCI=y +CONFIG_MSM_MHI_DEBUG=y +CONFIG_SEEMP_CORE=y +CONFIG_USB_BAM=y +CONFIG_MSM_MDSS_PLL=y +CONFIG_REMOTE_SPINLOCK_MSM=y +CONFIG_IOMMU_IO_PGTABLE_FAST=y +CONFIG_IOMMU_IO_PGTABLE_FAST_SELFTEST=y +CONFIG_ARM_SMMU=y +CONFIG_IOMMU_DEBUG=y +CONFIG_IOMMU_DEBUG_TRACKING=y +CONFIG_IOMMU_TESTS=y +CONFIG_MSM_SMEM=y +CONFIG_QPNP_HAPTIC=y +CONFIG_MSM_SMD=y +CONFIG_MSM_SMD_DEBUG=y +CONFIG_MSM_GLINK=y +CONFIG_MSM_GLINK_LOOPBACK_SERVER=y +CONFIG_MSM_GLINK_SMD_XPRT=y +CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y +CONFIG_MSM_SMEM_LOGGING=y +CONFIG_MSM_SMP2P=y +CONFIG_MSM_SMP2P_TEST=y +CONFIG_MSM_QMI_INTERFACE=y +CONFIG_MSM_RPM_SMD=y +CONFIG_QCOM_BUS_SCALING=y +CONFIG_MSM_SERVICE_LOCATOR=y +CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y +CONFIG_MSM_SYSMON_GLINK_COMM=y +CONFIG_MSM_IPC_ROUTER_MHI_XPRT=y +CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y +CONFIG_MSM_GLINK_PKT=y +CONFIG_MSM_SPM=y +CONFIG_MSM_L2_SPM=y +CONFIG_QCOM_SCM=y +CONFIG_QCOM_SCM_XPU=y +CONFIG_QCOM_WATCHDOG_V2=y +CONFIG_QCOM_MEMORY_DUMP_V2=y +CONFIG_MSM_RUN_QUEUE_STATS=y +CONFIG_MSM_BOOT_STATS=y +CONFIG_MSM_BOOT_TIME_MARKER=y +CONFIG_MSM_ADSP_LOADER=y +CONFIG_MSM_PERFORMANCE=y +CONFIG_MSM_SUBSYSTEM_RESTART=y +CONFIG_MSM_PIL=y +CONFIG_MSM_PIL_SSR_GENERIC=y +CONFIG_MSM_PIL_MSS_QDSP6V5=y +CONFIG_TRACER_PKT=y +CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y +CONFIG_MSM_MPM_OF=y +CONFIG_MSM_EVENT_TIMER=y +CONFIG_MSM_AVTIMER=y +CONFIG_QCOM_REMOTEQDSS=y +CONFIG_MSM_SERVICE_NOTIFIER=y +CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y +CONFIG_MSM_RPM_LOG=y +CONFIG_MSM_RPM_STATS_LOG=y +CONFIG_QCOM_SMCINVOKE=y +CONFIG_MSM_CACHE_M4M_ERP64=y +CONFIG_MSM_CACHE_M4M_ERP64_PANIC_ON_CE=y +CONFIG_MSM_CACHE_M4M_ERP64_PANIC_ON_UE=y +CONFIG_MEM_SHARE_QMI_SERVICE=y +CONFIG_QCOM_BIMC_BWMON=y +CONFIG_ARM_MEMLAT_MON=y +CONFIG_QCOM_M4M_HWMON=y +CONFIG_DEVFREQ_GOV_QCOM_BW_HWMON=y +CONFIG_DEVFREQ_GOV_QCOM_CACHE_HWMON=y +CONFIG_DEVFREQ_GOV_MEMLAT=y +CONFIG_DEVFREQ_SIMPLE_DEV=y +CONFIG_QCOM_DEVFREQ_DEVBW=y +CONFIG_EXTCON=y +CONFIG_PWM=y +CONFIG_PWM_QPNP=y +CONFIG_ARM_GIC_V3_ACL=y +CONFIG_PHY_XGENE=y +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_SECURITY=y +CONFIG_EXT4_ENCRYPTION=y +CONFIG_EXT4_FS_ENCRYPTION=y +CONFIG_EXT4_FS_ICE_ENCRYPTION=y +CONFIG_FUSE_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_ECRYPT_FS=y +CONFIG_ECRYPT_FS_MESSAGING=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_DYNAMIC_DEBUG=y +CONFIG_DEBUG_INFO=y +CONFIG_PAGE_OWNER=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_OBJECTS=y +CONFIG_DEBUG_OBJECTS_FREE=y +CONFIG_DEBUG_OBJECTS_TIMERS=y +CONFIG_DEBUG_OBJECTS_WORK=y +CONFIG_DEBUG_OBJECTS_RCU_HEAD=y +CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=y +CONFIG_DEBUG_KMEMLEAK=y +CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y +CONFIG_DEBUG_STACK_USAGE=y +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_LOCKUP_DETECTOR=y +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y +# CONFIG_DETECT_HUNG_TASK is not set +CONFIG_PANIC_TIMEOUT=5 +CONFIG_PANIC_ON_SCHED_BUG=y +CONFIG_PANIC_ON_RT_THROTTLING=y +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_MUTEXES=y +CONFIG_DEBUG_ATOMIC_SLEEP=y +CONFIG_DEBUG_LIST=y +CONFIG_FAULT_INJECTION=y +CONFIG_FAIL_PAGE_ALLOC=y +CONFIG_UFS_FAULT_INJECTION=y +CONFIG_FAULT_INJECTION_DEBUG_FS=y +CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y +CONFIG_IPC_LOGGING=y +CONFIG_QCOM_RTB=y +CONFIG_QCOM_RTB_SEPARATE_CPUS=y +CONFIG_IRQSOFF_TRACER=y +CONFIG_PREEMPT_TRACER=y +CONFIG_BLK_DEV_IO_TRACE=y +CONFIG_CPU_FREQ_SWITCH_PROFILER=y +CONFIG_PANIC_ON_DATA_CORRUPTION=y +CONFIG_ARM64_PTDUMP=y +CONFIG_DEBUG_SET_MODULE_RONX=y +CONFIG_FREE_PAGES_RDONLY=y +CONFIG_KERNEL_TEXT_RDONLY=y +CONFIG_CORESIGHT=y +CONFIG_CORESIGHT_EVENT=y +CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y +CONFIG_CORESIGHT_SOURCE_ETM4X=y +CONFIG_CORESIGHT_REMOTE_ETM=y +CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0 +CONFIG_CORESIGHT_QCOM_REPLICATOR=y +CONFIG_CORESIGHT_STM=y +CONFIG_CORESIGHT_HWEVENT=y +CONFIG_CORESIGHT_CTI=y +CONFIG_CORESIGHT_TPDA=y +CONFIG_CORESIGHT_TPDM=y +CONFIG_CORESIGHT_QPDI=y +CONFIG_CORESIGHT_SOURCE_DUMMY=y +CONFIG_PFK=y +CONFIG_SECURITY=y +CONFIG_SECURITY_SELINUX=y +CONFIG_SECURITY_SMACK=y +CONFIG_CRYPTO_ECHAINIV=y +CONFIG_CRYPTO_XCBC=y +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_ANSI_CPRNG=y +CONFIG_CRYPTO_DEV_QCRYPTO=y +CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y +CONFIG_CRYPTO_DEV_QCEDEV=y +CONFIG_CRYPTO_DEV_OTA_CRYPTO=y +CONFIG_CRYPTO_DEV_QCOM_ICE=y +CONFIG_ARM64_CRYPTO=y +CONFIG_CRYPTO_SHA1_ARM64_CE=y +CONFIG_CRYPTO_SHA2_ARM64_CE=y +CONFIG_CRYPTO_GHASH_ARM64_CE=y +CONFIG_CRYPTO_AES_ARM64_CE_CCM=y +CONFIG_CRYPTO_AES_ARM64_CE_BLK=y +CONFIG_CRYPTO_AES_ARM64_NEON_BLK=y +CONFIG_CRYPTO_CRC32_ARM64=y +CONFIG_XZ_DEC=y +CONFIG_QMI_ENCDEC=y diff --git a/arch/arm64/configs/msm-perf_defconfig b/arch/arm64/configs/msm-perf_defconfig index d75ca2f34360..3cd68ecf8634 100644 --- a/arch/arm64/configs/msm-perf_defconfig +++ b/arch/arm64/configs/msm-perf_defconfig @@ -5,6 +5,9 @@ CONFIG_AUDIT=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_TASKSTATS=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y CONFIG_RCU_EXPERT=y CONFIG_RCU_FAST_NO_HZ=y CONFIG_IKCONFIG=y @@ -236,7 +239,7 @@ CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 CONFIG_QSEECOM=y CONFIG_HDCP_QSEECOM=y -CONFIG_UID_CPUTIME=y +CONFIG_UID_SYS_STATS=y CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y CONFIG_CHR_DEV_SG=y @@ -516,6 +519,7 @@ CONFIG_MSM_IPC_ROUTER_MHI_XPRT=y CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y CONFIG_MSM_GLINK_PKT=y CONFIG_MSM_SPM=y +CONFIG_MSM_L2_SPM=y CONFIG_QCOM_SCM=y CONFIG_QCOM_SCM_XPU=y CONFIG_QCOM_WATCHDOG_V2=y @@ -530,6 +534,7 @@ CONFIG_MSM_PIL_SSR_GENERIC=y CONFIG_MSM_PIL_MSS_QDSP6V5=y CONFIG_TRACER_PKT=y CONFIG_MSM_MPM_OF=y +CONFIG_MSM_EVENT_TIMER=y CONFIG_MSM_AVTIMER=y CONFIG_MSM_QBT1000=y CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y diff --git a/arch/arm64/configs/msm_defconfig b/arch/arm64/configs/msm_defconfig index 6c58da68aa14..80e737e5726c 100644 --- a/arch/arm64/configs/msm_defconfig +++ b/arch/arm64/configs/msm_defconfig @@ -4,6 +4,9 @@ CONFIG_AUDIT=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_TASKSTATS=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y CONFIG_RCU_EXPERT=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y @@ -228,7 +231,7 @@ CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 CONFIG_QSEECOM=y CONFIG_HDCP_QSEECOM=y -CONFIG_UID_CPUTIME=y +CONFIG_UID_SYS_STATS=y CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y CONFIG_CHR_DEV_SG=y @@ -517,6 +520,7 @@ CONFIG_MSM_IPC_ROUTER_MHI_XPRT=y CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y CONFIG_MSM_GLINK_PKT=y CONFIG_MSM_SPM=y +CONFIG_MSM_L2_SPM=y CONFIG_QCOM_SCM=y CONFIG_QCOM_SCM_XPU=y CONFIG_QCOM_WATCHDOG_V2=y @@ -532,6 +536,7 @@ CONFIG_MSM_PIL_MSS_QDSP6V5=y CONFIG_TRACER_PKT=y CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y CONFIG_MSM_MPM_OF=y +CONFIG_MSM_EVENT_TIMER=y CONFIG_MSM_AVTIMER=y CONFIG_QCOM_REMOTEQDSS=y CONFIG_MSM_SERVICE_NOTIFIER=y diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig index cd73dd99fd09..2fda53fb832b 100644 --- a/arch/arm64/configs/msmcortex-perf_defconfig +++ b/arch/arm64/configs/msmcortex-perf_defconfig @@ -5,6 +5,9 @@ CONFIG_AUDIT=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_TASKSTATS=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y CONFIG_RCU_EXPERT=y CONFIG_RCU_FAST_NO_HZ=y CONFIG_RCU_NOCB_CPU=y @@ -68,6 +71,7 @@ CONFIG_ARMV8_DEPRECATED=y CONFIG_SWP_EMULATION=y CONFIG_CP15_BARRIER_EMULATION=y CONFIG_SETEND_EMULATION=y +CONFIG_ARM64_SW_TTBR0_PAN=y CONFIG_RANDOMIZE_BASE=y # CONFIG_RANDOMIZE_MODULE_REGION_FULL is not set # CONFIG_EFI is not set @@ -243,7 +247,7 @@ CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 CONFIG_QSEECOM=y CONFIG_HDCP_QSEECOM=y -CONFIG_UID_CPUTIME=y +CONFIG_UID_SYS_STATS=y CONFIG_QPNP_MISC=y CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y @@ -271,6 +275,7 @@ CONFIG_BONDING=y CONFIG_DUMMY=y CONFIG_TUN=y CONFIG_SKY2=y +CONFIG_MSM_RMNET_MHI=y CONFIG_RNDIS_IPA=y CONFIG_SMSC911X=y CONFIG_PPP=y @@ -516,6 +521,8 @@ CONFIG_GSI=y CONFIG_IPA3=y CONFIG_RMNET_IPA3=y CONFIG_GPIO_USB_DETECT=y +CONFIG_MSM_MHI=y +CONFIG_MSM_MHI_UCI=y CONFIG_SEEMP_CORE=y CONFIG_USB_BAM=y CONFIG_MSM_MDSS_PLL=y diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig index a46f062e10c7..5c88fd4985e1 100644 --- a/arch/arm64/configs/msmcortex_defconfig +++ b/arch/arm64/configs/msmcortex_defconfig @@ -4,6 +4,9 @@ CONFIG_AUDIT=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_TASKSTATS=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y CONFIG_RCU_EXPERT=y CONFIG_RCU_FAST_NO_HZ=y CONFIG_RCU_NOCB_CPU=y @@ -68,6 +71,7 @@ CONFIG_ARMV8_DEPRECATED=y CONFIG_SWP_EMULATION=y CONFIG_CP15_BARRIER_EMULATION=y CONFIG_SETEND_EMULATION=y +CONFIG_ARM64_SW_TTBR0_PAN=y CONFIG_RANDOMIZE_BASE=y CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set @@ -246,7 +250,7 @@ CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 CONFIG_QSEECOM=y CONFIG_HDCP_QSEECOM=y -CONFIG_UID_CPUTIME=y +CONFIG_UID_SYS_STATS=y CONFIG_QPNP_MISC=y CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y @@ -273,6 +277,7 @@ CONFIG_NETDEVICES=y CONFIG_BONDING=y CONFIG_DUMMY=y CONFIG_TUN=y +CONFIG_MSM_RMNET_MHI=y CONFIG_RNDIS_IPA=y CONFIG_PPP=y CONFIG_PPP_BSDCOMP=y @@ -528,6 +533,9 @@ CONFIG_GSI=y CONFIG_IPA3=y CONFIG_RMNET_IPA3=y CONFIG_GPIO_USB_DETECT=y +CONFIG_MSM_MHI=y +CONFIG_MSM_MHI_UCI=y +CONFIG_MSM_MHI_DEBUG=y CONFIG_SEEMP_CORE=y CONFIG_USB_BAM=y CONFIG_MSM_MDSS_PLL=y diff --git a/arch/arm64/configs/msmcortex_mediabox-perf_defconfig b/arch/arm64/configs/msmcortex_mediabox-perf_defconfig new file mode 100644 index 000000000000..cb0a5977c167 --- /dev/null +++ b/arch/arm64/configs/msmcortex_mediabox-perf_defconfig @@ -0,0 +1,642 @@ +CONFIG_LOCALVERSION="-perf" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_AUDIT=y +# CONFIG_AUDITSYSCALL is not set +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_RCU_EXPERT=y +CONFIG_RCU_FAST_NO_HZ=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_CPU_MAX_BUF_SHIFT=17 +CONFIG_CGROUP_FREEZER=y +CONFIG_CPUSETS=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_SCHEDTUNE=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_SCHED_HMP=y +CONFIG_SCHED_HMP_CSTATE_AWARE=y +CONFIG_SCHED_CORE_CTL=y +CONFIG_NAMESPACES=y +# CONFIG_UTS_NS is not set +# CONFIG_PID_NS is not set +CONFIG_SCHED_AUTOGROUP=y +CONFIG_SCHED_TUNE=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +# CONFIG_RD_LZ4 is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_KALLSYMS_ALL=y +# CONFIG_AIO is not set +# CONFIG_MEMBARRIER is not set +CONFIG_EMBEDDED=y +# CONFIG_SLUB_DEBUG is not set +# CONFIG_COMPAT_BRK is not set +CONFIG_PROFILING=y +CONFIG_CC_STACKPROTECTOR_REGULAR=y +CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16 +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SIG=y +CONFIG_MODULE_SIG_FORCE=y +CONFIG_MODULE_SIG_SHA512=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_ARCH_QCOM=y +CONFIG_ARCH_MSM8998=y +CONFIG_ARCH_MSMHAMSTER=y +CONFIG_PCI=y +CONFIG_PCI_MSM=y +CONFIG_SCHED_MC=y +CONFIG_NR_CPUS=8 +CONFIG_QCOM_TLB_EL2_HANDLER=y +CONFIG_PREEMPT=y +CONFIG_HZ_100=y +CONFIG_ARM64_REG_REBALANCE_ON_CTX_SW=y +CONFIG_CMA=y +CONFIG_CMA_DEBUGFS=y +CONFIG_ZSMALLOC=y +CONFIG_BALANCE_ANON_FILE_RECLAIM=y +CONFIG_FORCE_ALLOC_FROM_DMA_ZONE=y +CONFIG_SECCOMP=y +CONFIG_ARMV8_DEPRECATED=y +CONFIG_SWP_EMULATION=y +CONFIG_CP15_BARRIER_EMULATION=y +CONFIG_SETEND_EMULATION=y +CONFIG_RANDOMIZE_BASE=y +# CONFIG_RANDOMIZE_MODULE_REGION_FULL is not set +# CONFIG_EFI is not set +CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_COMPAT=y +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=0 +# CONFIG_PM_WAKELOCKS_GC is not set +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_BOOST=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=y +CONFIG_XFRM_STATISTICS=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_INET_AH=y +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=y +CONFIG_INET_DIAG_DESTROY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_HARDIDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_LOG=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_NOTRACK=y +CONFIG_NETFILTER_XT_TARGET_TEE=y +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TRACE=y +CONFIG_NETFILTER_XT_TARGET_SECMARK=y +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_DSCP=y +CONFIG_NETFILTER_XT_MATCH_ESP=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +# CONFIG_NETFILTER_XT_MATCH_L2TP is not set +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_RPFILTER=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_SECURITY=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_RPFILTER=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_BRIDGE_NF_EBTABLES=y +CONFIG_BRIDGE_EBT_BROUTE=y +CONFIG_L2TP=y +CONFIG_L2TP_V3=y +CONFIG_L2TP_IP=y +CONFIG_L2TP_ETH=y +CONFIG_BRIDGE=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_PRIO=y +CONFIG_NET_SCH_MULTIQ=y +CONFIG_NET_SCH_INGRESS=y +CONFIG_NET_CLS_FW=y +CONFIG_NET_CLS_U32=y +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_FLOW=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_CMP=y +CONFIG_NET_EMATCH_NBYTE=y +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_EMATCH_META=y +CONFIG_NET_EMATCH_TEXT=y +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_GACT=y +CONFIG_NET_ACT_MIRRED=y +CONFIG_NET_ACT_SKBEDIT=y +CONFIG_RMNET_DATA=y +CONFIG_RMNET_DATA_FC=y +CONFIG_RMNET_DATA_DEBUG_PKT=y +CONFIG_SOCKEV_NLMCAST=y +CONFIG_BT=y +CONFIG_MSM_BT_POWER=y +CONFIG_BTFM_SLIM=y +CONFIG_BTFM_SLIM_WCN3990=y +CONFIG_CFG80211=y +CONFIG_CFG80211_INTERNAL_REGDB=y +CONFIG_CFG80211_WEXT=y +CONFIG_MAC80211=m +CONFIG_MAC80211_MESH=y +CONFIG_MAC80211_LEDS=y +CONFIG_RFKILL=y +CONFIG_NFC_NQ=y +CONFIG_IPC_ROUTER=y +CONFIG_IPC_ROUTER_SECURITY=y +CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y +CONFIG_DMA_CMA=y +# CONFIG_PNP_DEBUG_MESSAGES is not set +CONFIG_ZRAM=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_QSEECOM=y +CONFIG_HDCP_QSEECOM=y +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_CHR_DEV_SCH=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +CONFIG_SCSI_UFSHCD=y +CONFIG_SCSI_UFSHCD_PLATFORM=y +CONFIG_SCSI_UFS_QCOM=y +CONFIG_SCSI_UFS_QCOM_ICE=y +CONFIG_MD=y +CONFIG_BLK_DEV_MD=y +CONFIG_MD_LINEAR=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_CRYPT=y +CONFIG_DM_REQ_CRYPT=y +CONFIG_DM_UEVENT=y +CONFIG_DM_VERITY=y +CONFIG_DM_VERITY_FEC=y +CONFIG_DM_ANDROID_VERITY=y +CONFIG_NETDEVICES=y +CONFIG_BONDING=y +CONFIG_DUMMY=y +CONFIG_TUN=y +CONFIG_SKY2=y +CONFIG_RNDIS_IPA=y +CONFIG_SMSC911X=y +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_MPPE=y +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +CONFIG_USB_USBNET=y +CONFIG_WCNSS_MEM_PRE_ALLOC=y +CONFIG_ATH_CARDS=y +CONFIG_WIL6210=m +CONFIG_ATH10K=m +CONFIG_ATH10K_TARGET_SNOC=m +CONFIG_ATH10K_SNOC=y +CONFIG_CLD_LL_CORE=y +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_KEYRESET=y +CONFIG_KEYBOARD_GPIO=y +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y +CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y +CONFIG_SECURE_TOUCH=y +CONFIG_TOUCHSCREEN_ST=y +CONFIG_TOUCHSCREEN_ST_I2C=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_HBTP_INPUT=y +CONFIG_INPUT_QPNP_POWER_ON=y +CONFIG_INPUT_UINPUT=y +# CONFIG_SERIO_SERPORT is not set +# CONFIG_VT is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_MSM_HS=y +CONFIG_SERIAL_MSM_SMD=y +CONFIG_DIAG_CHAR=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_MSM_LEGACY=y +CONFIG_MSM_ADSPRPC=y +CONFIG_MSM_RDBG=m +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MSM_V2=y +CONFIG_SLIMBUS_MSM_NGD=y +CONFIG_SOUNDWIRE=y +CONFIG_SPI=y +CONFIG_SPI_QUP=y +CONFIG_SPI_SPIDEV=y +CONFIG_SPMI=y +CONFIG_PINCTRL_MSM8998=y +CONFIG_PINCTRL_SDM660=y +CONFIG_GPIOLIB=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_QPNP_PIN=y +CONFIG_POWER_RESET_QCOM=y +CONFIG_QCOM_DLOAD_MODE=y +CONFIG_POWER_RESET_XGENE=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_QPNP_FG_GEN3=y +CONFIG_MSM_BCL_CTL=y +CONFIG_MSM_BCL_PERIPHERAL_CTL=y +CONFIG_BATTERY_BCL=y +CONFIG_QPNP_SMB2=y +CONFIG_SMB138X_CHARGER=y +CONFIG_QPNP_QNOVO=y +CONFIG_MSM_PM=y +CONFIG_APSS_CORE_EA=y +CONFIG_MSM_APM=y +CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y +CONFIG_CPU_THERMAL=y +CONFIG_LIMITS_MONITOR=y +CONFIG_LIMITS_LITE_HW=y +CONFIG_THERMAL_MONITOR=y +CONFIG_THERMAL_TSENS8974=y +CONFIG_THERMAL_QPNP=y +CONFIG_THERMAL_QPNP_ADC_TM=y +CONFIG_QCOM_THERMAL_LIMITS_DCVS=y +CONFIG_MFD_SPMI_PMIC=y +CONFIG_MFD_I2C_PMIC=y +CONFIG_WCD9335_CODEC=y +CONFIG_WCD934X_CODEC=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_RPM_SMD=y +CONFIG_REGULATOR_QPNP=y +CONFIG_REGULATOR_QPNP_LABIBB=y +CONFIG_REGULATOR_QPNP_LCDB=y +CONFIG_REGULATOR_SPM=y +CONFIG_REGULATOR_CPR3_HMSS=y +CONFIG_REGULATOR_CPR3_MMSS=y +CONFIG_REGULATOR_CPRH_KBSS=y +CONFIG_REGULATOR_MEM_ACC=y +CONFIG_REGULATOR_PROXY_CONSUMER=y +CONFIG_REGULATOR_STUB=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y +CONFIG_VIDEO_ADV_DEBUG=y +CONFIG_VIDEO_FIXED_MINOR_RANGES=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_MSM_CAMERA=y +CONFIG_MSM_CAMERA_DEBUG=y +CONFIG_MSMB_CAMERA=y +CONFIG_MSMB_CAMERA_DEBUG=y +CONFIG_MSM_CAMERA_SENSOR=y +CONFIG_MSM_CPP=y +CONFIG_MSM_CCI=y +CONFIG_MSM_CSI20_HEADER=y +CONFIG_MSM_CSI22_HEADER=y +CONFIG_MSM_CSI30_HEADER=y +CONFIG_MSM_CSI31_HEADER=y +CONFIG_MSM_CSIPHY=y +CONFIG_MSM_CSID=y +CONFIG_MSM_EEPROM=y +CONFIG_MSM_ISPIF=y +CONFIG_IMX134=y +CONFIG_IMX132=y +CONFIG_OV9724=y +CONFIG_OV5648=y +CONFIG_GC0339=y +CONFIG_OV8825=y +CONFIG_OV8865=y +CONFIG_s5k4e1=y +CONFIG_OV12830=y +CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE=y +CONFIG_MSMB_JPEG=y +CONFIG_MSM_FD=y +CONFIG_MSM_JPEGDMA=y +CONFIG_MSM_VIDC_V4L2=y +CONFIG_MSM_VIDC_VMEM=y +CONFIG_MSM_VIDC_GOVERNORS=y +CONFIG_MSM_SDE_ROTATOR=y +CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y +CONFIG_DVB_MPQ=m +CONFIG_DVB_MPQ_DEMUX=m +CONFIG_DVB_MPQ_MEDIA_BOX_DEMUX=y +CONFIG_TSPP=m +CONFIG_DRM=y +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_USB_AUDIO=y +CONFIG_SND_USB_AUDIO_QMI=y +CONFIG_SND_SOC=y +CONFIG_SND_SOC_MSM8998=y +CONFIG_UHID=y +CONFIG_HID_APPLE=y +CONFIG_HID_ELECOM=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MULTITOUCH=y +CONFIG_HID_PLANTRONICS=y +CONFIG_USB=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_HCD_PLATFORM=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD_PLATFORM=y +CONFIG_USB_STORAGE=y +CONFIG_USB_DWC3=y +CONFIG_USB_ISP1760=y +CONFIG_USB_ISP1760_HOST_ROLE=y +CONFIG_USB_PD_POLICY=y +CONFIG_QPNP_USB_PDPHY=y +CONFIG_USB_EHSET_TEST_FIXTURE=y +CONFIG_USB_OTG_WAKELOCK=y +CONFIG_NOP_USB_XCEIV=y +CONFIG_USB_MSM_SSPHY_QMP=y +CONFIG_MSM_QUSB_PHY=y +CONFIG_DUAL_ROLE_USB_INTF=y +CONFIG_USB_GADGET=y +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_CONFIGFS=y +CONFIG_USB_CONFIGFS_NCM=y +CONFIG_USB_CONFIGFS_MASS_STORAGE=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_MTP=y +CONFIG_USB_CONFIGFS_F_PTP=y +CONFIG_USB_CONFIGFS_F_ACC=y +CONFIG_USB_CONFIGFS_UEVENT=y +CONFIG_USB_CONFIGFS_F_MIDI=y +CONFIG_USB_CONFIGFS_F_HID=y +CONFIG_USB_CONFIGFS_F_DIAG=y +CONFIG_USB_CONFIGFS_F_GSI=y +CONFIG_USB_CONFIGFS_F_CDEV=y +CONFIG_USB_CONFIGFS_F_QDSS=y +CONFIG_USB_CONFIGFS_F_CCID=y +CONFIG_MMC=y +CONFIG_MMC_PERF_PROFILING=y +CONFIG_MMC_CLKGATE=y +CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_TEST=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_MSM=y +CONFIG_LEDS_QPNP=y +CONFIG_LEDS_QPNP_FLASH_V2=y +CONFIG_LEDS_QPNP_WLED=y +CONFIG_LEDS_SYSCON=y +CONFIG_SWITCH=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_QPNP=y +CONFIG_ESOC=y +CONFIG_ESOC_DEV=y +CONFIG_ESOC_CLIENT=y +CONFIG_ESOC_MDM_4x=y +CONFIG_ESOC_MDM_DRV=y +CONFIG_DMADEVICES=y +CONFIG_QCOM_SPS_DMA=y +CONFIG_UIO=y +CONFIG_UIO_MSM_SHAREDMEM=y +CONFIG_STAGING=y +CONFIG_ASHMEM=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_ION=y +CONFIG_ION_MSM=y +CONFIG_QPNP_REVID=y +CONFIG_QPNP_COINCELL=y +CONFIG_SPS=y +CONFIG_SPS_SUPPORT_NDP_BAM=y +CONFIG_IPA=y +CONFIG_RMNET_IPA=y +CONFIG_GSI=y +CONFIG_IPA3=y +CONFIG_RMNET_IPA3=y +CONFIG_GPIO_USB_DETECT=y +CONFIG_SEEMP_CORE=y +CONFIG_USB_BAM=y +CONFIG_MSM_MDSS_PLL=y +CONFIG_REMOTE_SPINLOCK_MSM=y +CONFIG_MSM_TIMER_LEAP=y +CONFIG_IOMMU_IO_PGTABLE_FAST=y +CONFIG_ARM_SMMU=y +CONFIG_IOMMU_DEBUG=y +CONFIG_IOMMU_DEBUG_TRACKING=y +CONFIG_IOMMU_TESTS=y +CONFIG_MSM_SMEM=y +CONFIG_QPNP_HAPTIC=y +CONFIG_MSM_SMD=y +CONFIG_MSM_GLINK=y +CONFIG_MSM_GLINK_LOOPBACK_SERVER=y +CONFIG_MSM_GLINK_SMD_XPRT=y +CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y +CONFIG_MSM_GLINK_SPI_XPRT=y +CONFIG_MSM_SPCOM=y +CONFIG_MSM_SPSS_UTILS=y +CONFIG_MSM_SMEM_LOGGING=y +CONFIG_MSM_SMP2P=y +CONFIG_MSM_SMP2P_TEST=y +CONFIG_MSM_QMI_INTERFACE=y +CONFIG_MSM_RPM_SMD=y +CONFIG_QCOM_BUS_SCALING=y +CONFIG_MSM_SERVICE_LOCATOR=y +CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y +CONFIG_MSM_SYSMON_GLINK_COMM=y +CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y +CONFIG_MSM_GLINK_PKT=y +CONFIG_MSM_SPM=y +CONFIG_QCOM_WATCHDOG_V2=y +CONFIG_QCOM_IRQ_HELPER=y +CONFIG_QCOM_MEMORY_DUMP_V2=y +CONFIG_ICNSS=y +CONFIG_MSM_RUN_QUEUE_STATS=y +CONFIG_MSM_BOOT_STATS=y +CONFIG_MSM_ADSP_LOADER=y +CONFIG_MSM_PERFORMANCE=y +CONFIG_MSM_SUBSYSTEM_RESTART=y +CONFIG_MSM_PIL=y +CONFIG_MSM_PIL_SSR_GENERIC=y +CONFIG_MSM_PIL_MSS_QDSP6V5=y +CONFIG_TRACER_PKT=y +CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y +CONFIG_MSM_MPM_OF=y +CONFIG_MSM_EVENT_TIMER=y +CONFIG_MSM_AVTIMER=y +CONFIG_QCOM_REMOTEQDSS=y +CONFIG_MSM_SERVICE_NOTIFIER=y +CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y +CONFIG_MSM_RPM_LOG=y +CONFIG_MSM_RPM_STATS_LOG=y +CONFIG_QSEE_IPC_IRQ_BRIDGE=y +CONFIG_QCOM_SMCINVOKE=y +CONFIG_QCOM_EARLY_RANDOM=y +CONFIG_MEM_SHARE_QMI_SERVICE=y +CONFIG_QCOM_BIMC_BWMON=y +CONFIG_ARM_MEMLAT_MON=y +CONFIG_DEVFREQ_GOV_QCOM_BW_HWMON=y +CONFIG_DEVFREQ_GOV_MEMLAT=y +CONFIG_QCOM_DEVFREQ_DEVBW=y +CONFIG_SPDM_SCM=y +CONFIG_DEVFREQ_SPDM=y +CONFIG_EXTCON=y +CONFIG_IIO=y +CONFIG_QCOM_RRADC=y +CONFIG_QCOM_TADC=y +CONFIG_PWM=y +CONFIG_PWM_QPNP=y +CONFIG_ARM_GIC_V3_ACL=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_BINDER_DEVICES="binder,hwbinder" +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_SECURITY=y +CONFIG_EXT4_ENCRYPTION=y +CONFIG_EXT4_FS_ENCRYPTION=y +CONFIG_EXT4_FS_ICE_ENCRYPTION=y +CONFIG_FUSE_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_ECRYPT_FS=y +CONFIG_ECRYPT_FS_MESSAGING=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_DEBUG_INFO=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_PREEMPT is not set +CONFIG_IPC_LOGGING=y +CONFIG_CPU_FREQ_SWITCH_PROFILER=y +CONFIG_DEBUG_SET_MODULE_RONX=y +CONFIG_DEBUG_ALIGN_RODATA=y +CONFIG_CORESIGHT=y +CONFIG_CORESIGHT_EVENT=y +CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y +CONFIG_CORESIGHT_QCOM_REPLICATOR=y +CONFIG_CORESIGHT_STM=y +CONFIG_CORESIGHT_HWEVENT=y +CONFIG_CORESIGHT_CTI=y +CONFIG_CORESIGHT_TPDA=y +CONFIG_CORESIGHT_TPDM=y +CONFIG_CORESIGHT_QPDI=y +CONFIG_CORESIGHT_SOURCE_DUMMY=y +CONFIG_PFK=y +CONFIG_SECURITY=y +CONFIG_SECURITY_SELINUX=y +CONFIG_SECURITY_SMACK=y +CONFIG_CRYPTO_ECHAINIV=y +CONFIG_CRYPTO_XCBC=y +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_ANSI_CPRNG=y +CONFIG_CRYPTO_DEV_QCRYPTO=y +CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y +CONFIG_CRYPTO_DEV_QCEDEV=y +CONFIG_CRYPTO_DEV_OTA_CRYPTO=y +CONFIG_CRYPTO_DEV_QCOM_ICE=y +CONFIG_SYSTEM_TRUSTED_KEYS="verity.x509.pem" +CONFIG_ARM64_CRYPTO=y +CONFIG_CRYPTO_SHA1_ARM64_CE=y +CONFIG_CRYPTO_SHA2_ARM64_CE=y +CONFIG_CRYPTO_GHASH_ARM64_CE=y +CONFIG_CRYPTO_AES_ARM64_CE_CCM=y +CONFIG_CRYPTO_AES_ARM64_CE_BLK=y +CONFIG_CRYPTO_AES_ARM64_NEON_BLK=y +CONFIG_CRYPTO_CRC32_ARM64=y +CONFIG_QMI_ENCDEC=y diff --git a/arch/arm64/configs/msmcortex_mediabox_defconfig b/arch/arm64/configs/msmcortex_mediabox_defconfig index 3322f8fa11fc..28d900cf5b18 100644 --- a/arch/arm64/configs/msmcortex_mediabox_defconfig +++ b/arch/arm64/configs/msmcortex_mediabox_defconfig @@ -4,6 +4,9 @@ CONFIG_AUDIT=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_TASKSTATS=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y CONFIG_RCU_EXPERT=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y @@ -242,7 +245,7 @@ CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 CONFIG_QSEECOM=y CONFIG_HDCP_QSEECOM=y -CONFIG_UID_CPUTIME=y +CONFIG_UID_SYS_STATS=y CONFIG_QPNP_MISC=y CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y diff --git a/arch/arm64/configs/sdm660-perf_defconfig b/arch/arm64/configs/sdm660-perf_defconfig index 9a4841db89be..b75b4244aa72 100644 --- a/arch/arm64/configs/sdm660-perf_defconfig +++ b/arch/arm64/configs/sdm660-perf_defconfig @@ -545,6 +545,7 @@ CONFIG_MSM_QMI_INTERFACE=y CONFIG_MSM_RPM_SMD=y CONFIG_QCOM_BUS_SCALING=y CONFIG_MSM_SERVICE_LOCATOR=y +CONFIG_QCOM_DCC=y CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y CONFIG_MSM_SYSMON_GLINK_COMM=y CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y @@ -557,7 +558,6 @@ CONFIG_QCOM_MEMORY_DUMP_V2=y CONFIG_ICNSS=y CONFIG_MSM_RUN_QUEUE_STATS=y CONFIG_MSM_BOOT_STATS=y -CONFIG_QCOM_CPUSS_DUMP=y CONFIG_MSM_ADSP_LOADER=y CONFIG_MSM_CDSP_LOADER=y CONFIG_MSM_PERFORMANCE=y diff --git a/arch/arm64/configs/sdm660_defconfig b/arch/arm64/configs/sdm660_defconfig index 13ae21bdd562..3fe9e2bda6d2 100644 --- a/arch/arm64/configs/sdm660_defconfig +++ b/arch/arm64/configs/sdm660_defconfig @@ -4,6 +4,9 @@ CONFIG_AUDIT=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_TASKSTATS=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y CONFIG_RCU_EXPERT=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y @@ -242,7 +245,7 @@ CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 CONFIG_QSEECOM=y CONFIG_HDCP_QSEECOM=y -CONFIG_UID_CPUTIME=y +CONFIG_UID_SYS_STATS=y CONFIG_QPNP_MISC=y CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y @@ -653,7 +656,6 @@ CONFIG_DEBUG_OBJECTS_TIMERS=y CONFIG_DEBUG_OBJECTS_WORK=y CONFIG_DEBUG_OBJECTS_RCU_HEAD=y CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=y -CONFIG_SLUB_DEBUG_ON=y CONFIG_DEBUG_KMEMLEAK=y CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=4000 CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y diff --git a/arch/arm64/crypto/Kconfig b/arch/arm64/crypto/Kconfig index 2cf32e9887e1..de1aab4b5da8 100644 --- a/arch/arm64/crypto/Kconfig +++ b/arch/arm64/crypto/Kconfig @@ -23,6 +23,11 @@ config CRYPTO_GHASH_ARM64_CE depends on ARM64 && KERNEL_MODE_NEON select CRYPTO_HASH +config CRYPTO_POLY_HASH_ARM64_CE + tristate "poly_hash (for HEH encryption mode) using ARMv8 Crypto Extensions" + depends on ARM64 && KERNEL_MODE_NEON + select CRYPTO_HASH + config CRYPTO_AES_ARM64_CE tristate "AES core cipher using ARMv8 Crypto Extensions" depends on ARM64 && KERNEL_MODE_NEON diff --git a/arch/arm64/crypto/Makefile b/arch/arm64/crypto/Makefile index abb79b3cfcfe..f0a8f2475ea3 100644 --- a/arch/arm64/crypto/Makefile +++ b/arch/arm64/crypto/Makefile @@ -17,6 +17,9 @@ sha2-ce-y := sha2-ce-glue.o sha2-ce-core.o obj-$(CONFIG_CRYPTO_GHASH_ARM64_CE) += ghash-ce.o ghash-ce-y := ghash-ce-glue.o ghash-ce-core.o +obj-$(CONFIG_CRYPTO_POLY_HASH_ARM64_CE) += poly-hash-ce.o +poly-hash-ce-y := poly-hash-ce-glue.o poly-hash-ce-core.o + obj-$(CONFIG_CRYPTO_AES_ARM64_CE) += aes-ce-cipher.o CFLAGS_aes-ce-cipher.o += -march=armv8-a+crypto diff --git a/arch/arm64/crypto/aes-glue.c b/arch/arm64/crypto/aes-glue.c index 6a51dfccfe71..448b874a4826 100644 --- a/arch/arm64/crypto/aes-glue.c +++ b/arch/arm64/crypto/aes-glue.c @@ -294,7 +294,7 @@ static struct crypto_alg aes_algs[] = { { .cra_blkcipher = { .min_keysize = AES_MIN_KEY_SIZE, .max_keysize = AES_MAX_KEY_SIZE, - .ivsize = AES_BLOCK_SIZE, + .ivsize = 0, .setkey = aes_setkey, .encrypt = ecb_encrypt, .decrypt = ecb_decrypt, @@ -371,7 +371,7 @@ static struct crypto_alg aes_algs[] = { { .cra_ablkcipher = { .min_keysize = AES_MIN_KEY_SIZE, .max_keysize = AES_MAX_KEY_SIZE, - .ivsize = AES_BLOCK_SIZE, + .ivsize = 0, .setkey = ablk_set_key, .encrypt = ablk_encrypt, .decrypt = ablk_decrypt, diff --git a/arch/arm64/crypto/poly-hash-ce-core.S b/arch/arm64/crypto/poly-hash-ce-core.S new file mode 100644 index 000000000000..8ccb544c5526 --- /dev/null +++ b/arch/arm64/crypto/poly-hash-ce-core.S @@ -0,0 +1,163 @@ +/* + * Accelerated poly_hash implementation with ARMv8 PMULL instructions. + * + * Based on ghash-ce-core.S. + * + * Copyright (C) 2014 Linaro Ltd. <ard.biesheuvel@linaro.org> + * Copyright (C) 2017 Google, Inc. <ebiggers@google.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include <linux/linkage.h> +#include <asm/assembler.h> + + KEY .req v0 + KEY2 .req v1 + T1 .req v2 + T2 .req v3 + GSTAR .req v4 + XL .req v5 + XM .req v6 + XH .req v7 + + .text + .arch armv8-a+crypto + + /* 16-byte aligned (2**4 = 16); not required, but might as well */ + .align 4 +.Lgstar: + .quad 0x87, 0x87 + +/* + * void pmull_poly_hash_update(le128 *digest, const le128 *key, + * const u8 *src, unsigned int blocks, + * unsigned int partial); + */ +ENTRY(pmull_poly_hash_update) + + /* Load digest into XL */ + ld1 {XL.16b}, [x0] + + /* Load key into KEY */ + ld1 {KEY.16b}, [x1] + + /* Load g*(x) = g(x) + x^128 = x^7 + x^2 + x + 1 into both halves of + * GSTAR */ + adr x1, .Lgstar + ld1 {GSTAR.2d}, [x1] + + /* Set KEY2 to (KEY[1]+KEY[0]):(KEY[1]+KEY[0]). This is needed for + * Karatsuba multiplication. */ + ext KEY2.16b, KEY.16b, KEY.16b, #8 + eor KEY2.16b, KEY2.16b, KEY.16b + + /* If 'partial' is nonzero, then we're finishing a pending block and + * should go right to the multiplication. */ + cbnz w4, 1f + +0: + /* Add the next block from 'src' to the digest */ + ld1 {T1.16b}, [x2], #16 + eor XL.16b, XL.16b, T1.16b + sub w3, w3, #1 + +1: + /* + * Multiply the current 128-bit digest (a1:a0, in XL) by the 128-bit key + * (b1:b0, in KEY) using Karatsuba multiplication. + */ + + /* T1 = (a1+a0):(a1+a0) */ + ext T1.16b, XL.16b, XL.16b, #8 + eor T1.16b, T1.16b, XL.16b + + /* XH = a1 * b1 */ + pmull2 XH.1q, XL.2d, KEY.2d + + /* XL = a0 * b0 */ + pmull XL.1q, XL.1d, KEY.1d + + /* XM = (a1+a0) * (b1+b0) */ + pmull XM.1q, T1.1d, KEY2.1d + + /* XM += (XH[0]:XL[1]) + XL + XH */ + ext T1.16b, XL.16b, XH.16b, #8 + eor T2.16b, XL.16b, XH.16b + eor XM.16b, XM.16b, T1.16b + eor XM.16b, XM.16b, T2.16b + + /* + * Now the 256-bit product is in XH[1]:XM:XL[0]. It represents a + * polynomial over GF(2) with degree as large as 255. We need to + * compute its remainder modulo g(x) = x^128+x^7+x^2+x+1. For this it + * is sufficient to compute the remainder of the high half 'c(x)x^128' + * add it to the low half. To reduce the high half we use the Barrett + * reduction method. The basic idea is that we can express the + * remainder p(x) as g(x)q(x) mod x^128, where q(x) = (c(x)x^128)/g(x). + * As detailed in [1], to avoid having to divide by g(x) at runtime the + * following equivalent expression can be derived: + * + * p(x) = [ g*(x)((c(x)q+(x))/x^128) ] mod x^128 + * + * where g*(x) = x^128+g(x) = x^7+x^2+x+1, and q+(x) = x^256/g(x) = g(x) + * in this case. This is also equivalent to: + * + * p(x) = [ g*(x)((c(x)(x^128 + g*(x)))/x^128) ] mod x^128 + * = [ g*(x)(c(x) + (c(x)g*(x))/x^128) ] mod x^128 + * + * Since deg g*(x) < 64: + * + * p(x) = [ g*(x)(c(x) + ((c(x)/x^64)g*(x))/x^64) ] mod x^128 + * = [ g*(x)((c(x)/x^64)x^64 + (c(x) mod x^64) + + * ((c(x)/x^64)g*(x))/x^64) ] mod x^128 + * + * Letting t(x) = g*(x)(c(x)/x^64): + * + * p(x) = [ t(x)x^64 + g*(x)((c(x) mod x^64) + t(x)/x^64) ] mod x^128 + * + * Therefore, to do the reduction we only need to issue two 64-bit => + * 128-bit carryless multiplications: g*(x) times c(x)/x^64, and g*(x) + * times ((c(x) mod x^64) + t(x)/x^64). (Multiplication by x^64 doesn't + * count since it is simply a shift or move.) + * + * An alternate reduction method, also based on Barrett reduction and + * described in [1], uses only shifts and XORs --- no multiplications. + * However, the method with multiplications requires fewer instructions + * and is faster on processors with fast carryless multiplication. + * + * [1] "Intel Carry-Less Multiplication Instruction and its Usage for + * Computing the GCM Mode", + * https://software.intel.com/sites/default/files/managed/72/cc/clmul-wp-rev-2.02-2014-04-20.pdf + */ + + /* 256-bit product is XH[1]:XM:XL[0], so c(x) is XH[1]:XM[1] */ + + /* T1 = t(x) = g*(x)(c(x)/x^64) */ + pmull2 T1.1q, GSTAR.2d, XH.2d + + /* T2 = g*(x)((c(x) mod x^64) + t(x)/x^64) */ + eor T2.16b, XM.16b, T1.16b + pmull2 T2.1q, GSTAR.2d, T2.2d + + /* Make XL[0] be the low half of the 128-bit result by adding the low 64 + * bits of the T2 term to what was already there. The 't(x)x^64' term + * makes no difference, so skip it. */ + eor XL.16b, XL.16b, T2.16b + + /* Make XL[1] be the high half of the 128-bit result by adding the high + * 64 bits of the 't(x)x^64' and T2 terms to what was already in XM[0], + * then moving XM[0] to XL[1]. */ + eor XM.16b, XM.16b, T1.16b + ext T2.16b, T2.16b, T2.16b, #8 + eor XM.16b, XM.16b, T2.16b + mov XL.d[1], XM.d[0] + + /* If more blocks remain, then loop back to process the next block; + * else, store the digest and return. */ + cbnz w3, 0b + st1 {XL.16b}, [x0] + ret +ENDPROC(pmull_poly_hash_update) diff --git a/arch/arm64/crypto/poly-hash-ce-glue.c b/arch/arm64/crypto/poly-hash-ce-glue.c new file mode 100644 index 000000000000..e195740c9ecf --- /dev/null +++ b/arch/arm64/crypto/poly-hash-ce-glue.c @@ -0,0 +1,166 @@ +/* + * Accelerated poly_hash implementation with ARMv8 PMULL instructions. + * + * Based on ghash-ce-glue.c. + * + * poly_hash is part of the HEH (Hash-Encrypt-Hash) encryption mode, proposed in + * Internet Draft https://tools.ietf.org/html/draft-cope-heh-01. + * + * poly_hash is very similar to GHASH: both algorithms are keyed hashes which + * interpret their input data as coefficients of a polynomial over GF(2^128), + * then calculate a hash value by evaluating that polynomial at the point given + * by the key, e.g. using Horner's rule. The difference is that poly_hash uses + * the more natural "ble" convention to represent GF(2^128) elements, whereas + * GHASH uses the less natural "lle" convention (see include/crypto/gf128mul.h). + * The ble convention makes it simpler to implement GF(2^128) multiplication. + * + * Copyright (C) 2014 Linaro Ltd. <ard.biesheuvel@linaro.org> + * Copyright (C) 2017 Google Inc. <ebiggers@google.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include <asm/neon.h> +#include <crypto/b128ops.h> +#include <crypto/internal/hash.h> +#include <linux/cpufeature.h> +#include <linux/crypto.h> +#include <linux/module.h> + +/* + * Note: in this algorithm we currently use 'le128' to represent GF(2^128) + * elements, even though poly_hash-generic uses 'be128'. Both types are + * actually "wrong" because the elements are actually in 'ble' format, and there + * should be a ble type to represent this --- as well as lle, bbe, and lbe types + * for the other conventions for representing GF(2^128) elements. But + * practically it doesn't matter which type we choose here, so we just use le128 + * since it's arguably more accurate, while poly_hash-generic still has to use + * be128 because the generic GF(2^128) multiplication functions all take be128. + */ + +struct poly_hash_desc_ctx { + le128 digest; + unsigned int count; +}; + +asmlinkage void pmull_poly_hash_update(le128 *digest, const le128 *key, + const u8 *src, unsigned int blocks, + unsigned int partial); + +static int poly_hash_setkey(struct crypto_shash *tfm, + const u8 *key, unsigned int keylen) +{ + if (keylen != sizeof(le128)) { + crypto_shash_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN); + return -EINVAL; + } + + memcpy(crypto_shash_ctx(tfm), key, sizeof(le128)); + return 0; +} + +static int poly_hash_init(struct shash_desc *desc) +{ + struct poly_hash_desc_ctx *ctx = shash_desc_ctx(desc); + + ctx->digest = (le128) { 0 }; + ctx->count = 0; + return 0; +} + +static int poly_hash_update(struct shash_desc *desc, const u8 *src, + unsigned int len) +{ + struct poly_hash_desc_ctx *ctx = shash_desc_ctx(desc); + unsigned int partial = ctx->count % sizeof(le128); + u8 *dst = (u8 *)&ctx->digest + partial; + + ctx->count += len; + + /* Finishing at least one block? */ + if (partial + len >= sizeof(le128)) { + const le128 *key = crypto_shash_ctx(desc->tfm); + + if (partial) { + /* Finish the pending block. */ + unsigned int n = sizeof(le128) - partial; + + len -= n; + do { + *dst++ ^= *src++; + } while (--n); + } + + /* + * Do the real work. If 'partial' is nonzero, this starts by + * multiplying 'digest' by 'key'. Then for each additional full + * block it adds the block to 'digest' and multiplies by 'key'. + */ + kernel_neon_begin_partial(8); + pmull_poly_hash_update(&ctx->digest, key, src, + len / sizeof(le128), partial); + kernel_neon_end(); + + src += len - (len % sizeof(le128)); + len %= sizeof(le128); + dst = (u8 *)&ctx->digest; + } + + /* Continue adding the next block to 'digest'. */ + while (len--) + *dst++ ^= *src++; + return 0; +} + +static int poly_hash_final(struct shash_desc *desc, u8 *out) +{ + struct poly_hash_desc_ctx *ctx = shash_desc_ctx(desc); + unsigned int partial = ctx->count % sizeof(le128); + + /* Finish the last block if needed. */ + if (partial) { + const le128 *key = crypto_shash_ctx(desc->tfm); + + kernel_neon_begin_partial(8); + pmull_poly_hash_update(&ctx->digest, key, NULL, 0, partial); + kernel_neon_end(); + } + + memcpy(out, &ctx->digest, sizeof(le128)); + return 0; +} + +static struct shash_alg poly_hash_alg = { + .digestsize = sizeof(le128), + .init = poly_hash_init, + .update = poly_hash_update, + .final = poly_hash_final, + .setkey = poly_hash_setkey, + .descsize = sizeof(struct poly_hash_desc_ctx), + .base = { + .cra_name = "poly_hash", + .cra_driver_name = "poly_hash-ce", + .cra_priority = 300, + .cra_ctxsize = sizeof(le128), + .cra_module = THIS_MODULE, + }, +}; + +static int __init poly_hash_ce_mod_init(void) +{ + return crypto_register_shash(&poly_hash_alg); +} + +static void __exit poly_hash_ce_mod_exit(void) +{ + crypto_unregister_shash(&poly_hash_alg); +} + +MODULE_DESCRIPTION("Polynomial evaluation hash using ARMv8 Crypto Extensions"); +MODULE_AUTHOR("Eric Biggers <ebiggers@google.com>"); +MODULE_LICENSE("GPL v2"); + +module_cpu_feature_match(PMULL, poly_hash_ce_mod_init); +module_exit(poly_hash_ce_mod_exit); diff --git a/arch/arm64/include/asm/app_api.h b/arch/arm64/include/asm/app_api.h new file mode 100644 index 000000000000..0e6a469cd683 --- /dev/null +++ b/arch/arm64/include/asm/app_api.h @@ -0,0 +1,50 @@ +/* Copyright (c) 2016-2017, 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 __ASM_APP_API_H +#define __ASM_APP_API_H + +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/fs.h> + +#define APP_SETTING_BIT 30 +#define MAX_ENTRIES 10 + +/* + * APIs to set / clear the app setting bits + * in the register. + */ +#ifdef CONFIG_MSM_APP_API +extern void set_app_setting_bit(uint32_t bit); +extern void clear_app_setting_bit(uint32_t bit); +extern void set_app_setting_bit_for_32bit_apps(void); +extern void clear_app_setting_bit_for_32bit_apps(void); +#else +static inline void set_app_setting_bit(uint32_t bit) {} +static inline void clear_app_setting_bit(uint32_t bit) {} +static inline void set_app_setting_bit_for_32bit_apps(void) {} +static inline void clear_app_setting_bit_for_32bit_apps(void) {} +#endif + +#ifdef CONFIG_MSM_APP_SETTINGS +extern void switch_app_setting_bit(struct task_struct *prev, + struct task_struct *next); +extern void switch_32bit_app_setting_bit(struct task_struct *prev, + struct task_struct *next); +extern void apply_app_setting_bit(struct file *file); +extern bool use_app_setting; +extern bool use_32bit_app_setting; +extern bool use_32bit_app_setting_pro; +#endif + +#endif diff --git a/arch/arm64/include/asm/dma-contiguous.h b/arch/arm64/include/asm/dma-contiguous.h index 61507394a99b..e77da2002bc9 100644 --- a/arch/arm64/include/asm/dma-contiguous.h +++ b/arch/arm64/include/asm/dma-contiguous.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Copyright (c) 2013,2016-2017, 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 @@ -15,7 +15,6 @@ #define _ASM_DMA_CONTIGUOUS_H #ifdef __KERNEL__ -#ifdef CONFIG_DMA_CMA #include <linux/types.h> @@ -23,5 +22,3 @@ void dma_contiguous_early_fixup(phys_addr_t base, unsigned long size); #endif #endif - -#endif diff --git a/arch/arm64/include/asm/dma-iommu.h b/arch/arm64/include/asm/dma-iommu.h index ba8f19a13a31..c16cf151f689 100644 --- a/arch/arm64/include/asm/dma-iommu.h +++ b/arch/arm64/include/asm/dma-iommu.h @@ -9,7 +9,6 @@ #include <linux/dma-debug.h> #include <linux/kmemcheck.h> #include <linux/kref.h> -#include <linux/dma-mapping-fast.h> struct dma_iommu_mapping { /* iommu specific data */ @@ -21,8 +20,9 @@ struct dma_iommu_mapping { spinlock_t lock; struct kref kref; - +#ifdef CONFIG_IOMMU_IO_PGTABLE_FAST struct dma_fast_smmu_mapping *fast; +#endif }; #ifdef CONFIG_ARM64_DMA_USE_IOMMU diff --git a/arch/arm64/include/asm/elf.h b/arch/arm64/include/asm/elf.h index 7875c886ad24..a383c288ef49 100644 --- a/arch/arm64/include/asm/elf.h +++ b/arch/arm64/include/asm/elf.h @@ -23,6 +23,7 @@ */ #include <asm/ptrace.h> #include <asm/user.h> +#include <asm/fpsimd.h> /* * AArch64 static relocation types. @@ -182,7 +183,11 @@ typedef compat_elf_greg_t compat_elf_gregset_t[COMPAT_ELF_NGREG]; ((x)->e_flags & EF_ARM_EABI_MASK)) #define compat_start_thread compat_start_thread -#define COMPAT_SET_PERSONALITY(ex) set_thread_flag(TIF_32BIT); +#define COMPAT_SET_PERSONALITY(ex) \ +do { \ + set_thread_flag(TIF_32BIT); \ +} while (0) + #define COMPAT_ARCH_DLINFO extern int aarch32_setup_vectors_page(struct linux_binprm *bprm, int uses_interp); diff --git a/arch/arm64/include/asm/fpsimd.h b/arch/arm64/include/asm/fpsimd.h index 50f559f574fe..3efaa5cebc03 100644 --- a/arch/arm64/include/asm/fpsimd.h +++ b/arch/arm64/include/asm/fpsimd.h @@ -81,6 +81,14 @@ extern void fpsimd_save_partial_state(struct fpsimd_partial_state *state, u32 num_regs); extern void fpsimd_load_partial_state(struct fpsimd_partial_state *state); +#ifdef CONFIG_ENABLE_FP_SIMD_SETTINGS +extern void fpsimd_disable_trap(void); +extern void fpsimd_enable_trap(void); +#else +static inline void fpsimd_disable_trap(void) {} +static inline void fpsimd_enable_trap(void) {} +#endif + #endif #endif diff --git a/arch/arm64/include/asm/hw_breakpoint.h b/arch/arm64/include/asm/hw_breakpoint.h index 9732908bfc8a..c72b8e201ab4 100644 --- a/arch/arm64/include/asm/hw_breakpoint.h +++ b/arch/arm64/include/asm/hw_breakpoint.h @@ -68,7 +68,11 @@ static inline void decode_ctrl_reg(u32 reg, /* Lengths */ #define ARM_BREAKPOINT_LEN_1 0x1 #define ARM_BREAKPOINT_LEN_2 0x3 +#define ARM_BREAKPOINT_LEN_3 0x7 #define ARM_BREAKPOINT_LEN_4 0xf +#define ARM_BREAKPOINT_LEN_5 0x1f +#define ARM_BREAKPOINT_LEN_6 0x3f +#define ARM_BREAKPOINT_LEN_7 0x7f #define ARM_BREAKPOINT_LEN_8 0xff /* Kernel stepping */ @@ -110,7 +114,7 @@ struct perf_event; struct pmu; extern int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl, - int *gen_len, int *gen_type); + int *gen_len, int *gen_type, int *offset); extern int arch_check_bp_in_kernelspace(struct perf_event *bp); extern int arch_validate_hwbkpt_settings(struct perf_event *bp); extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused, diff --git a/arch/arm64/include/asm/vdso_datapage.h b/arch/arm64/include/asm/vdso_datapage.h index de66199673d7..2b9a63771eda 100644 --- a/arch/arm64/include/asm/vdso_datapage.h +++ b/arch/arm64/include/asm/vdso_datapage.h @@ -22,6 +22,8 @@ struct vdso_data { __u64 cs_cycle_last; /* Timebase at clocksource init */ + __u64 raw_time_sec; /* Raw time */ + __u64 raw_time_nsec; __u64 xtime_clock_sec; /* Kernel time */ __u64 xtime_clock_nsec; __u64 xtime_coarse_sec; /* Coarse time */ @@ -29,8 +31,10 @@ struct vdso_data { __u64 wtm_clock_sec; /* Wall to monotonic time */ __u64 wtm_clock_nsec; __u32 tb_seq_count; /* Timebase sequence counter */ - __u32 cs_mult; /* Clocksource multiplier */ - __u32 cs_shift; /* Clocksource shift */ + /* cs_* members must be adjacent and in this order (ldp accesses) */ + __u32 cs_mono_mult; /* NTP-adjusted clocksource multiplier */ + __u32 cs_shift; /* Clocksource shift (mono = raw) */ + __u32 cs_raw_mult; /* Raw clocksource multiplier */ __u32 tz_minuteswest; /* Whacky timezone stuff */ __u32 tz_dsttime; __u32 use_syscall; diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index d3cfa681654f..99f4410833b4 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -44,6 +44,8 @@ arm64-obj-$(CONFIG_PCI) += pci.o arm64-obj-$(CONFIG_ARMV8_DEPRECATED) += armv8_deprecated.o arm64-obj-$(CONFIG_ACPI) += acpi.o arm64-obj-$(CONFIG_RANDOMIZE_BASE) += kaslr.o +arm64-obj-$(CONFIG_MSM_APP_API) += app_api.o +arm64-obj-$(CONFIG_MSM_APP_SETTINGS) += app_setting.o arm64-obj-$(CONFIG_HIBERNATION) += hibernate.o hibernate-asm.o arm64-obj-$(CONFIG_ARM64_ACPI_PARKING_PROTOCOL) += acpi_parking_protocol.o arm64-obj-$(CONFIG_PARAVIRT) += paravirt.o @@ -52,7 +54,3 @@ obj-y += $(arm64-obj-y) vdso/ probes/ obj-m += $(arm64-obj-m) head-y := head.o extra-y += $(head-y) vmlinux.lds - -# vDSO - this must be built first to generate the symbol offsets -$(call objectify,$(arm64-obj-y)): $(obj)/vdso/vdso-offsets.h -$(obj)/vdso/vdso-offsets.h: $(obj)/vdso diff --git a/arch/arm64/kernel/app_api.c b/arch/arm64/kernel/app_api.c new file mode 100644 index 000000000000..e995bbf3c7b4 --- /dev/null +++ b/arch/arm64/kernel/app_api.c @@ -0,0 +1,135 @@ +/* Copyright (c) 2016-2017, 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/bitops.h> +#include <linux/spinlock.h> +#include <linux/cpu.h> +#include <linux/export.h> + +#include <asm/app_api.h> + +static spinlock_t spinlock; +static spinlock_t spinlock_32bit_app; +static DEFINE_PER_CPU(int, app_config_applied); +static unsigned long app_config_set[NR_CPUS]; +static unsigned long app_config_clear[NR_CPUS]; + +void set_app_setting_bit(uint32_t bit) +{ + unsigned long flags; + uint64_t reg; + int cpu; + + spin_lock_irqsave(&spinlock, flags); + asm volatile("mrs %0, S3_1_C15_C15_0" : "=r" (reg)); + reg = reg | BIT(bit); + isb(); + asm volatile("msr S3_1_C15_C15_0, %0" : : "r" (reg)); + isb(); + if (bit == APP_SETTING_BIT) { + cpu = raw_smp_processor_id(); + app_config_set[cpu]++; + + this_cpu_write(app_config_applied, 1); + } + spin_unlock_irqrestore(&spinlock, flags); + +} +EXPORT_SYMBOL(set_app_setting_bit); + +void clear_app_setting_bit(uint32_t bit) +{ + unsigned long flags; + uint64_t reg; + int cpu; + + spin_lock_irqsave(&spinlock, flags); + asm volatile("mrs %0, S3_1_C15_C15_0" : "=r" (reg)); + reg = reg & ~BIT(bit); + isb(); + asm volatile("msr S3_1_C15_C15_0, %0" : : "r" (reg)); + isb(); + if (bit == APP_SETTING_BIT) { + cpu = raw_smp_processor_id(); + app_config_clear[cpu]++; + + this_cpu_write(app_config_applied, 0); + } + spin_unlock_irqrestore(&spinlock, flags); +} +EXPORT_SYMBOL(clear_app_setting_bit); + +void set_app_setting_bit_for_32bit_apps(void) +{ + unsigned long flags; + uint64_t reg; + + spin_lock_irqsave(&spinlock_32bit_app, flags); + if (use_32bit_app_setting) { + asm volatile("mrs %0, S3_0_c15_c15_0 " : "=r" (reg)); + reg = reg | BIT(24); + isb(); + asm volatile("msr S3_0_c15_c15_0, %0" : : "r" (reg)); + isb(); + asm volatile("mrs %0, S3_0_c15_c15_1 " : "=r" (reg)); + reg = reg | BIT(18) | BIT(2) | BIT(0); + isb(); + asm volatile("msr S3_0_c15_c15_1, %0" : : "r" (reg)); + isb(); + } else if (use_32bit_app_setting_pro) { + asm volatile("mrs %0, S3_0_c15_c15_1 " : "=r" (reg)); + reg = reg | BIT(18); + isb(); + asm volatile("msr S3_0_c15_c15_1, %0" : : "r" (reg)); + isb(); + } + spin_unlock_irqrestore(&spinlock_32bit_app, flags); +} +EXPORT_SYMBOL(set_app_setting_bit_for_32bit_apps); + +void clear_app_setting_bit_for_32bit_apps(void) +{ + unsigned long flags; + uint64_t reg; + + spin_lock_irqsave(&spinlock_32bit_app, flags); + if (use_32bit_app_setting) { + asm volatile("mrs %0, S3_0_c15_c15_0 " : "=r" (reg)); + reg = reg & ~BIT(24); + isb(); + asm volatile("msr S3_0_c15_c15_0, %0" : : "r" (reg)); + isb(); + asm volatile("mrs %0, S3_0_c15_c15_1 " : "=r" (reg)); + reg = reg & ~BIT(18); + reg = reg & ~BIT(2); + reg = reg & ~BIT(0); + isb(); + asm volatile("msr S3_0_c15_c15_1, %0" : : "r" (reg)); + isb(); + } else if (use_32bit_app_setting_pro) { + asm volatile("mrs %0, S3_0_c15_c15_1 " : "=r" (reg)); + reg = reg & ~BIT(18); + isb(); + asm volatile("msr S3_0_c15_c15_1, %0" : : "r" (reg)); + isb(); + } + spin_unlock_irqrestore(&spinlock_32bit_app, flags); +} +EXPORT_SYMBOL(clear_app_setting_bit_for_32bit_apps); + +static int __init init_app_api(void) +{ + spin_lock_init(&spinlock); + spin_lock_init(&spinlock_32bit_app); + return 0; +} +early_initcall(init_app_api); diff --git a/arch/arm64/kernel/app_setting.c b/arch/arm64/kernel/app_setting.c new file mode 100644 index 000000000000..0c6b00317645 --- /dev/null +++ b/arch/arm64/kernel/app_setting.c @@ -0,0 +1,139 @@ +/* Copyright (c) 2016-2017, 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/module.h> +#include <linux/cpu.h> +#include <linux/slab.h> +#include <linux/notifier.h> + +#include <asm/app_api.h> + +#define MAX_LEN 100 + +static char *lib_names[MAX_ENTRIES]; +static unsigned int count; +static struct mutex mutex; + +static char lib_str[MAX_LEN] = ""; +static struct kparam_string kps = { + .string = lib_str, + .maxlen = MAX_LEN, +}; +static int set_name(const char *str, struct kernel_param *kp); +module_param_call(lib_name, set_name, param_get_string, &kps, S_IWUSR); + +bool use_app_setting = true; +module_param(use_app_setting, bool, 0644); +MODULE_PARM_DESC(use_app_setting, "control use of app specific settings"); + +bool use_32bit_app_setting = true; +module_param(use_32bit_app_setting, bool, 0644); +MODULE_PARM_DESC(use_32bit_app_setting, "control use of 32 bit app specific settings"); + +bool use_32bit_app_setting_pro; +module_param(use_32bit_app_setting_pro, bool, 0644); +MODULE_PARM_DESC(use_32bit_app_setting_pro, "control use of 32 bit app specific settings"); + +static int set_name(const char *str, struct kernel_param *kp) +{ + int len = strlen(str); + char *name; + + if (len >= MAX_LEN) { + pr_err("app_setting: name string too long\n"); + return -ENOSPC; + } + + /* + * echo adds '\n' which we need to chop off later + */ + name = kzalloc(len + 1, GFP_KERNEL); + if (!name) + return -ENOMEM; + + strlcpy(name, str, len + 1); + + if (name[len - 1] == '\n') + name[len - 1] = '\0'; + + mutex_lock(&mutex); + if (count < MAX_ENTRIES) { + lib_names[count] = name; + /* + * mb to ensure that the new lib_names entry is present + * before updating the view presented by get_lib_names + */ + mb(); + count++; + } else { + pr_err("app_setting: set name failed. Max entries reached\n"); + kfree(name); + mutex_unlock(&mutex); + return -EPERM; + } + mutex_unlock(&mutex); + + return 0; +} + +void switch_app_setting_bit(struct task_struct *prev, struct task_struct *next) +{ + if (prev->mm && unlikely(prev->mm->app_setting)) + clear_app_setting_bit(APP_SETTING_BIT); + + if (next->mm && unlikely(next->mm->app_setting)) + set_app_setting_bit(APP_SETTING_BIT); +} +EXPORT_SYMBOL(switch_app_setting_bit); + +void switch_32bit_app_setting_bit(struct task_struct *prev, + struct task_struct *next) +{ + if (prev->mm && unlikely(is_compat_thread(task_thread_info(prev)))) + clear_app_setting_bit_for_32bit_apps(); + + if (next->mm && unlikely(is_compat_thread(task_thread_info(next)))) + set_app_setting_bit_for_32bit_apps(); +} +EXPORT_SYMBOL(switch_32bit_app_setting_bit); + +void apply_app_setting_bit(struct file *file) +{ + bool found = false; + int i; + + if (file && file->f_path.dentry) { + const char *name = file->f_path.dentry->d_name.name; + + for (i = 0; i < count; i++) { + if (unlikely(!strcmp(name, lib_names[i]))) { + found = true; + break; + } + } + if (found) { + preempt_disable(); + set_app_setting_bit(APP_SETTING_BIT); + /* This will take care of child processes as well */ + current->mm->app_setting = 1; + preempt_enable(); + } + } +} +EXPORT_SYMBOL(apply_app_setting_bit); + +static int __init app_setting_init(void) +{ + mutex_init(&mutex); + return 0; +} +module_init(app_setting_init); diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c index c9ea87198789..350c0e99fc6b 100644 --- a/arch/arm64/kernel/asm-offsets.c +++ b/arch/arm64/kernel/asm-offsets.c @@ -92,6 +92,7 @@ int main(void) BLANK(); DEFINE(CLOCK_REALTIME, CLOCK_REALTIME); DEFINE(CLOCK_MONOTONIC, CLOCK_MONOTONIC); + DEFINE(CLOCK_MONOTONIC_RAW, CLOCK_MONOTONIC_RAW); DEFINE(CLOCK_REALTIME_RES, MONOTONIC_RES_NSEC); DEFINE(CLOCK_REALTIME_COARSE, CLOCK_REALTIME_COARSE); DEFINE(CLOCK_MONOTONIC_COARSE,CLOCK_MONOTONIC_COARSE); @@ -99,6 +100,8 @@ int main(void) DEFINE(NSEC_PER_SEC, NSEC_PER_SEC); BLANK(); DEFINE(VDSO_CS_CYCLE_LAST, offsetof(struct vdso_data, cs_cycle_last)); + DEFINE(VDSO_RAW_TIME_SEC, offsetof(struct vdso_data, raw_time_sec)); + DEFINE(VDSO_RAW_TIME_NSEC, offsetof(struct vdso_data, raw_time_nsec)); DEFINE(VDSO_XTIME_CLK_SEC, offsetof(struct vdso_data, xtime_clock_sec)); DEFINE(VDSO_XTIME_CLK_NSEC, offsetof(struct vdso_data, xtime_clock_nsec)); DEFINE(VDSO_XTIME_CRS_SEC, offsetof(struct vdso_data, xtime_coarse_sec)); @@ -106,7 +109,8 @@ int main(void) DEFINE(VDSO_WTM_CLK_SEC, offsetof(struct vdso_data, wtm_clock_sec)); DEFINE(VDSO_WTM_CLK_NSEC, offsetof(struct vdso_data, wtm_clock_nsec)); DEFINE(VDSO_TB_SEQ_COUNT, offsetof(struct vdso_data, tb_seq_count)); - DEFINE(VDSO_CS_MULT, offsetof(struct vdso_data, cs_mult)); + DEFINE(VDSO_CS_MONO_MULT, offsetof(struct vdso_data, cs_mono_mult)); + DEFINE(VDSO_CS_RAW_MULT, offsetof(struct vdso_data, cs_raw_mult)); DEFINE(VDSO_CS_SHIFT, offsetof(struct vdso_data, cs_shift)); DEFINE(VDSO_TZ_MINWEST, offsetof(struct vdso_data, tz_minuteswest)); DEFINE(VDSO_TZ_DSTTIME, offsetof(struct vdso_data, tz_dsttime)); diff --git a/arch/arm64/kernel/entry-fpsimd.S b/arch/arm64/kernel/entry-fpsimd.S index c44a82f146b1..1ffe15459c92 100644 --- a/arch/arm64/kernel/entry-fpsimd.S +++ b/arch/arm64/kernel/entry-fpsimd.S @@ -64,4 +64,20 @@ ENTRY(fpsimd_load_partial_state) ret ENDPROC(fpsimd_load_partial_state) +#ifdef CONFIG_ENABLE_FP_SIMD_SETTINGS +ENTRY(fpsimd_enable_trap) + mrs x0, cpacr_el1 + bic x0, x0, #(3 << 20) + orr x0, x0, #(1 << 20) + msr cpacr_el1, x0 + ret +ENDPROC(fpsimd_enable_trap) +ENTRY(fpsimd_disable_trap) + mrs x0, cpacr_el1 + orr x0, x0, #(3 << 20) + msr cpacr_el1, x0 + ret +ENDPROC(fpsimd_disable_trap) +#endif + #endif diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 191e3136fa6e..0ea65307f866 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -561,7 +561,7 @@ el0_sync_compat: cmp x24, #ESR_ELx_EC_IABT_LOW // instruction abort in EL0 b.eq el0_ia cmp x24, #ESR_ELx_EC_FP_ASIMD // FP/ASIMD access - b.eq el0_fpsimd_acc + b.eq el0_fpsimd_acc_compat cmp x24, #ESR_ELx_EC_FP_EXC32 // FP/ASIMD exception b.eq el0_fpsimd_exc cmp x24, #ESR_ELx_EC_PC_ALIGN // pc alignment exception @@ -632,6 +632,17 @@ el0_fpsimd_acc: mov x1, sp bl do_fpsimd_acc b ret_to_user +el0_fpsimd_acc_compat: + /* + * Floating Point or Advanced SIMD access + */ + enable_dbg + ct_user_exit + mov x0, x25 + mov x1, sp + bl do_fpsimd_acc_compat + b ret_to_user + el0_fpsimd_exc: /* * Floating Point or Advanced SIMD exception diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c index acc1afd5c749..790d27e3b997 100644 --- a/arch/arm64/kernel/fpsimd.c +++ b/arch/arm64/kernel/fpsimd.c @@ -20,6 +20,7 @@ #include <linux/cpu.h> #include <linux/cpu_pm.h> #include <linux/kernel.h> +#include <linux/module.h> #include <linux/init.h> #include <linux/sched.h> #include <linux/signal.h> @@ -27,6 +28,7 @@ #include <asm/fpsimd.h> #include <asm/cputype.h> +#include <asm/app_api.h> #define FPEXC_IOF (1 << 0) #define FPEXC_DZF (1 << 1) @@ -35,6 +37,8 @@ #define FPEXC_IXF (1 << 4) #define FPEXC_IDF (1 << 7) +#define FP_SIMD_BIT 31 + /* * In order to reduce the number of times the FPSIMD state is needlessly saved * and restored, we need to keep track of two things: @@ -88,14 +92,42 @@ * whatever is in the FPSIMD registers is not saved to memory, but discarded. */ static DEFINE_PER_CPU(struct fpsimd_state *, fpsimd_last_state); +static DEFINE_PER_CPU(int, fpsimd_stg_enable); + +static int fpsimd_settings = 0x1; /* default = 0x1 */ +module_param(fpsimd_settings, int, 0644); + +void fpsimd_settings_enable(void) +{ + set_app_setting_bit(FP_SIMD_BIT); +} + +void fpsimd_settings_disable(void) +{ + clear_app_setting_bit(FP_SIMD_BIT); +} /* * Trapped FP/ASIMD access. */ void do_fpsimd_acc(unsigned int esr, struct pt_regs *regs) { - /* TODO: implement lazy context saving/restoring */ - WARN_ON(1); + if (!fpsimd_settings) + return; + + fpsimd_disable_trap(); + fpsimd_settings_disable(); + this_cpu_write(fpsimd_stg_enable, 0); +} + +void do_fpsimd_acc_compat(unsigned int esr, struct pt_regs *regs) +{ + if (!fpsimd_settings) + return; + + fpsimd_disable_trap(); + fpsimd_settings_enable(); + this_cpu_write(fpsimd_stg_enable, 1); } /* @@ -135,6 +167,11 @@ void fpsimd_thread_switch(struct task_struct *next) if (current->mm && !test_thread_flag(TIF_FOREIGN_FPSTATE)) fpsimd_save_state(¤t->thread.fpsimd_state); + if (fpsimd_settings && __this_cpu_read(fpsimd_stg_enable)) { + fpsimd_settings_disable(); + this_cpu_write(fpsimd_stg_enable, 0); + } + if (next->mm) { /* * If we are switching to a task whose most recent userland @@ -152,6 +189,14 @@ void fpsimd_thread_switch(struct task_struct *next) else set_ti_thread_flag(task_thread_info(next), TIF_FOREIGN_FPSTATE); + + if (!fpsimd_settings) + return; + + if (test_ti_thread_flag(task_thread_info(next), TIF_32BIT)) + fpsimd_enable_trap(); + else + fpsimd_disable_trap(); } } diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c index 367a954f9937..f4dfd8c41e06 100644 --- a/arch/arm64/kernel/hw_breakpoint.c +++ b/arch/arm64/kernel/hw_breakpoint.c @@ -317,9 +317,21 @@ static int get_hbp_len(u8 hbp_len) case ARM_BREAKPOINT_LEN_2: len_in_bytes = 2; break; + case ARM_BREAKPOINT_LEN_3: + len_in_bytes = 3; + break; case ARM_BREAKPOINT_LEN_4: len_in_bytes = 4; break; + case ARM_BREAKPOINT_LEN_5: + len_in_bytes = 5; + break; + case ARM_BREAKPOINT_LEN_6: + len_in_bytes = 6; + break; + case ARM_BREAKPOINT_LEN_7: + len_in_bytes = 7; + break; case ARM_BREAKPOINT_LEN_8: len_in_bytes = 8; break; @@ -349,7 +361,7 @@ int arch_check_bp_in_kernelspace(struct perf_event *bp) * to generic breakpoint descriptions. */ int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl, - int *gen_len, int *gen_type) + int *gen_len, int *gen_type, int *offset) { /* Type */ switch (ctrl.type) { @@ -369,17 +381,33 @@ int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl, return -EINVAL; } + if (!ctrl.len) + return -EINVAL; + *offset = __ffs(ctrl.len); + /* Len */ - switch (ctrl.len) { + switch (ctrl.len >> *offset) { case ARM_BREAKPOINT_LEN_1: *gen_len = HW_BREAKPOINT_LEN_1; break; case ARM_BREAKPOINT_LEN_2: *gen_len = HW_BREAKPOINT_LEN_2; break; + case ARM_BREAKPOINT_LEN_3: + *gen_len = HW_BREAKPOINT_LEN_3; + break; case ARM_BREAKPOINT_LEN_4: *gen_len = HW_BREAKPOINT_LEN_4; break; + case ARM_BREAKPOINT_LEN_5: + *gen_len = HW_BREAKPOINT_LEN_5; + break; + case ARM_BREAKPOINT_LEN_6: + *gen_len = HW_BREAKPOINT_LEN_6; + break; + case ARM_BREAKPOINT_LEN_7: + *gen_len = HW_BREAKPOINT_LEN_7; + break; case ARM_BREAKPOINT_LEN_8: *gen_len = HW_BREAKPOINT_LEN_8; break; @@ -423,9 +451,21 @@ static int arch_build_bp_info(struct perf_event *bp) case HW_BREAKPOINT_LEN_2: info->ctrl.len = ARM_BREAKPOINT_LEN_2; break; + case HW_BREAKPOINT_LEN_3: + info->ctrl.len = ARM_BREAKPOINT_LEN_3; + break; case HW_BREAKPOINT_LEN_4: info->ctrl.len = ARM_BREAKPOINT_LEN_4; break; + case HW_BREAKPOINT_LEN_5: + info->ctrl.len = ARM_BREAKPOINT_LEN_5; + break; + case HW_BREAKPOINT_LEN_6: + info->ctrl.len = ARM_BREAKPOINT_LEN_6; + break; + case HW_BREAKPOINT_LEN_7: + info->ctrl.len = ARM_BREAKPOINT_LEN_7; + break; case HW_BREAKPOINT_LEN_8: info->ctrl.len = ARM_BREAKPOINT_LEN_8; break; @@ -517,18 +557,17 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp) default: return -EINVAL; } - - info->address &= ~alignment_mask; - info->ctrl.len <<= offset; } else { if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) alignment_mask = 0x3; else alignment_mask = 0x7; - if (info->address & alignment_mask) - return -EINVAL; + offset = info->address & alignment_mask; } + info->address &= ~alignment_mask; + info->ctrl.len <<= offset; + /* * Disallow per-task kernel breakpoints since these would * complicate the stepping code. @@ -661,12 +700,47 @@ unlock: } NOKPROBE_SYMBOL(breakpoint_handler); +/* + * Arm64 hardware does not always report a watchpoint hit address that matches + * one of the watchpoints set. It can also report an address "near" the + * watchpoint if a single instruction access both watched and unwatched + * addresses. There is no straight-forward way, short of disassembling the + * offending instruction, to map that address back to the watchpoint. This + * function computes the distance of the memory access from the watchpoint as a + * heuristic for the likelyhood that a given access triggered the watchpoint. + * + * See Section D2.10.5 "Determining the memory location that caused a Watchpoint + * exception" of ARMv8 Architecture Reference Manual for details. + * + * The function returns the distance of the address from the bytes watched by + * the watchpoint. In case of an exact match, it returns 0. + */ +static u64 get_distance_from_watchpoint(unsigned long addr, u64 val, + struct arch_hw_breakpoint_ctrl *ctrl) +{ + u64 wp_low, wp_high; + u32 lens, lene; + + lens = __ffs(ctrl->len); + lene = __fls(ctrl->len); + + wp_low = val + lens; + wp_high = val + lene; + if (addr < wp_low) + return wp_low - addr; + else if (addr > wp_high) + return addr - wp_high; + else + return 0; +} + static int watchpoint_handler(unsigned long addr, unsigned int esr, struct pt_regs *regs) { - int i, step = 0, *kernel_step, access; + int i, step = 0, *kernel_step, access, closest_match = 0; + u64 min_dist = -1, dist; u32 ctrl_reg; - u64 val, alignment_mask; + u64 val; struct perf_event *wp, **slots; struct debug_info *debug_info; struct arch_hw_breakpoint *info; @@ -675,35 +749,15 @@ static int watchpoint_handler(unsigned long addr, unsigned int esr, slots = this_cpu_ptr(wp_on_reg); debug_info = ¤t->thread.debug; + /* + * Find all watchpoints that match the reported address. If no exact + * match is found. Attribute the hit to the closest watchpoint. + */ + rcu_read_lock(); for (i = 0; i < core_num_wrps; ++i) { - rcu_read_lock(); - wp = slots[i]; - if (wp == NULL) - goto unlock; - - info = counter_arch_bp(wp); - /* AArch32 watchpoints are either 4 or 8 bytes aligned. */ - if (is_compat_task()) { - if (info->ctrl.len == ARM_BREAKPOINT_LEN_8) - alignment_mask = 0x7; - else - alignment_mask = 0x3; - } else { - alignment_mask = 0x7; - } - - /* Check if the watchpoint value matches. */ - val = read_wb_reg(AARCH64_DBG_REG_WVR, i); - if (val != (addr & ~alignment_mask)) - goto unlock; - - /* Possible match, check the byte address select to confirm. */ - ctrl_reg = read_wb_reg(AARCH64_DBG_REG_WCR, i); - decode_ctrl_reg(ctrl_reg, &ctrl); - if (!((1 << (addr & alignment_mask)) & ctrl.len)) - goto unlock; + continue; /* * Check that the access type matches. @@ -712,18 +766,41 @@ static int watchpoint_handler(unsigned long addr, unsigned int esr, access = (esr & AARCH64_ESR_ACCESS_MASK) ? HW_BREAKPOINT_W : HW_BREAKPOINT_R; if (!(access & hw_breakpoint_type(wp))) - goto unlock; + continue; + /* Check if the watchpoint value and byte select match. */ + val = read_wb_reg(AARCH64_DBG_REG_WVR, i); + ctrl_reg = read_wb_reg(AARCH64_DBG_REG_WCR, i); + decode_ctrl_reg(ctrl_reg, &ctrl); + dist = get_distance_from_watchpoint(addr, val, &ctrl); + if (dist < min_dist) { + min_dist = dist; + closest_match = i; + } + /* Is this an exact match? */ + if (dist != 0) + continue; + + info = counter_arch_bp(wp); info->trigger = addr; perf_bp_event(wp, regs); /* Do we need to handle the stepping? */ if (!wp->overflow_handler) step = 1; + } + if (min_dist > 0 && min_dist != -1) { + /* No exact match found. */ + wp = slots[closest_match]; + info = counter_arch_bp(wp); + info->trigger = addr; + perf_bp_event(wp, regs); -unlock: - rcu_read_unlock(); + /* Do we need to handle the stepping? */ + if (!wp->overflow_handler) + step = 1; } + rcu_read_unlock(); if (!step) return 0; diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index c5ef05959813..6204b7600d1b 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -327,13 +327,13 @@ static int ptrace_hbp_fill_attr_ctrl(unsigned int note_type, struct arch_hw_breakpoint_ctrl ctrl, struct perf_event_attr *attr) { - int err, len, type, disabled = !ctrl.enabled; + int err, len, type, offset, disabled = !ctrl.enabled; attr->disabled = disabled; if (disabled) return 0; - err = arch_bp_generic_fields(ctrl, &len, &type); + err = arch_bp_generic_fields(ctrl, &len, &type, &offset); if (err) return err; @@ -352,6 +352,7 @@ static int ptrace_hbp_fill_attr_ctrl(unsigned int note_type, attr->bp_len = len; attr->bp_type = type; + attr->bp_addr += offset; return 0; } @@ -404,7 +405,7 @@ static int ptrace_hbp_get_addr(unsigned int note_type, if (IS_ERR(bp)) return PTR_ERR(bp); - *addr = bp ? bp->attr.bp_addr : 0; + *addr = bp ? counter_arch_bp(bp)->address : 0; return 0; } diff --git a/arch/arm64/kernel/sleep.S b/arch/arm64/kernel/sleep.S index c2bf5a58039f..9a3aec97ac09 100644 --- a/arch/arm64/kernel/sleep.S +++ b/arch/arm64/kernel/sleep.S @@ -132,6 +132,11 @@ ENTRY(_cpu_resume) */ bl cpu_do_resume +#ifdef CONFIG_KASAN + mov x0, sp + bl kasan_unpoison_remaining_stack +#endif + ldp x19, x20, [x29, #16] ldp x21, x22, [x29, #32] ldp x23, x24, [x29, #48] diff --git a/arch/arm64/kernel/vdso.c b/arch/arm64/kernel/vdso.c index 97bc68f4c689..3b8acfae7797 100644 --- a/arch/arm64/kernel/vdso.c +++ b/arch/arm64/kernel/vdso.c @@ -55,7 +55,7 @@ struct vdso_data *vdso_data = &vdso_data_store.data; */ static struct page *vectors_page[1]; -static int alloc_vectors_page(void) +static int __init alloc_vectors_page(void) { extern char __kuser_helper_start[], __kuser_helper_end[]; extern char __aarch32_sigret_code_start[], __aarch32_sigret_code_end[]; @@ -88,7 +88,7 @@ int aarch32_setup_vectors_page(struct linux_binprm *bprm, int uses_interp) { struct mm_struct *mm = current->mm; unsigned long addr = AARCH32_VECTORS_BASE; - static struct vm_special_mapping spec = { + static const struct vm_special_mapping spec = { .name = "[vectors]", .pages = vectors_page, @@ -212,10 +212,16 @@ void update_vsyscall(struct timekeeper *tk) vdso_data->wtm_clock_nsec = tk->wall_to_monotonic.tv_nsec; if (!use_syscall) { + /* tkr_mono.cycle_last == tkr_raw.cycle_last */ vdso_data->cs_cycle_last = tk->tkr_mono.cycle_last; + vdso_data->raw_time_sec = tk->raw_time.tv_sec; + vdso_data->raw_time_nsec = tk->raw_time.tv_nsec; vdso_data->xtime_clock_sec = tk->xtime_sec; vdso_data->xtime_clock_nsec = tk->tkr_mono.xtime_nsec; - vdso_data->cs_mult = tk->tkr_mono.mult; + /* tkr_raw.xtime_nsec == 0 */ + vdso_data->cs_mono_mult = tk->tkr_mono.mult; + vdso_data->cs_raw_mult = tk->tkr_raw.mult; + /* tkr_mono.shift == tkr_raw.shift */ vdso_data->cs_shift = tk->tkr_mono.shift; } diff --git a/arch/arm64/kernel/vdso/Makefile b/arch/arm64/kernel/vdso/Makefile index b467fd0a384b..62c84f7cb01b 100644 --- a/arch/arm64/kernel/vdso/Makefile +++ b/arch/arm64/kernel/vdso/Makefile @@ -23,7 +23,7 @@ GCOV_PROFILE := n ccflags-y += -Wl,-shared obj-y += vdso.o -extra-y += vdso.lds vdso-offsets.h +extra-y += vdso.lds CPPFLAGS_vdso.lds += -P -C -U$(ARCH) # Force dependency (incbin is bad) @@ -42,11 +42,10 @@ $(obj)/%.so: $(obj)/%.so.dbg FORCE gen-vdsosym := $(srctree)/$(src)/gen_vdso_offsets.sh quiet_cmd_vdsosym = VDSOSYM $@ define cmd_vdsosym - $(NM) $< | $(gen-vdsosym) | LC_ALL=C sort > $@ && \ - cp $@ include/generated/ + $(NM) $< | $(gen-vdsosym) | LC_ALL=C sort > $@ endef -$(obj)/vdso-offsets.h: $(obj)/vdso.so.dbg FORCE +include/generated/vdso-offsets.h: $(obj)/vdso.so.dbg FORCE $(call if_changed,vdsosym) # Assembly rules for the .S files diff --git a/arch/arm64/kernel/vdso/gettimeofday.S b/arch/arm64/kernel/vdso/gettimeofday.S index 9f8eeccae67c..e00b4671bd7c 100644 --- a/arch/arm64/kernel/vdso/gettimeofday.S +++ b/arch/arm64/kernel/vdso/gettimeofday.S @@ -26,24 +26,109 @@ #define NSEC_PER_SEC_HI16 0x3b9a vdso_data .req x6 -use_syscall .req w7 -seqcnt .req w8 +seqcnt .req w7 +w_tmp .req w8 +x_tmp .req x8 + +/* + * Conventions for macro arguments: + * - An argument is write-only if its name starts with "res". + * - All other arguments are read-only, unless otherwise specified. + */ .macro seqcnt_acquire 9999: ldr seqcnt, [vdso_data, #VDSO_TB_SEQ_COUNT] tbnz seqcnt, #0, 9999b dmb ishld - ldr use_syscall, [vdso_data, #VDSO_USE_SYSCALL] .endm - .macro seqcnt_read, cnt + .macro seqcnt_check fail dmb ishld - ldr \cnt, [vdso_data, #VDSO_TB_SEQ_COUNT] + ldr w_tmp, [vdso_data, #VDSO_TB_SEQ_COUNT] + cmp w_tmp, seqcnt + b.ne \fail .endm - .macro seqcnt_check, cnt, fail - cmp \cnt, seqcnt - b.ne \fail + .macro syscall_check fail + ldr w_tmp, [vdso_data, #VDSO_USE_SYSCALL] + cbnz w_tmp, \fail + .endm + + .macro get_nsec_per_sec res + mov \res, #NSEC_PER_SEC_LO16 + movk \res, #NSEC_PER_SEC_HI16, lsl #16 + .endm + + /* + * Returns the clock delta, in nanoseconds left-shifted by the clock + * shift. + */ + .macro get_clock_shifted_nsec res, cycle_last, mult + /* Read the virtual counter. */ + isb + mrs x_tmp, cntvct_el0 + /* Calculate cycle delta and convert to ns. */ + sub \res, x_tmp, \cycle_last + /* We can only guarantee 56 bits of precision. */ + movn x_tmp, #0xff00, lsl #48 + and \res, x_tmp, \res + mul \res, \res, \mult + .endm + + /* + * Returns in res_{sec,nsec} the REALTIME timespec, based on the + * "wall time" (xtime) and the clock_mono delta. + */ + .macro get_ts_realtime res_sec, res_nsec, \ + clock_nsec, xtime_sec, xtime_nsec, nsec_to_sec + add \res_nsec, \clock_nsec, \xtime_nsec + udiv x_tmp, \res_nsec, \nsec_to_sec + add \res_sec, \xtime_sec, x_tmp + msub \res_nsec, x_tmp, \nsec_to_sec, \res_nsec + .endm + + /* + * Returns in res_{sec,nsec} the timespec based on the clock_raw delta, + * used for CLOCK_MONOTONIC_RAW. + */ + .macro get_ts_clock_raw res_sec, res_nsec, clock_nsec, nsec_to_sec + udiv \res_sec, \clock_nsec, \nsec_to_sec + msub \res_nsec, \res_sec, \nsec_to_sec, \clock_nsec + .endm + + /* sec and nsec are modified in place. */ + .macro add_ts sec, nsec, ts_sec, ts_nsec, nsec_to_sec + /* Add timespec. */ + add \sec, \sec, \ts_sec + add \nsec, \nsec, \ts_nsec + + /* Normalise the new timespec. */ + cmp \nsec, \nsec_to_sec + b.lt 9999f + sub \nsec, \nsec, \nsec_to_sec + add \sec, \sec, #1 +9999: + cmp \nsec, #0 + b.ge 9998f + add \nsec, \nsec, \nsec_to_sec + sub \sec, \sec, #1 +9998: + .endm + + .macro clock_gettime_return, shift=0 + .if \shift == 1 + lsr x11, x11, x12 + .endif + stp x10, x11, [x1, #TSPEC_TV_SEC] + mov x0, xzr + ret + .endm + + .macro jump_slot jumptable, index, label + .if (. - \jumptable) != 4 * (\index) + .error "Jump slot index mismatch" + .endif + b \label .endm .text @@ -51,18 +136,25 @@ seqcnt .req w8 /* int __kernel_gettimeofday(struct timeval *tv, struct timezone *tz); */ ENTRY(__kernel_gettimeofday) .cfi_startproc - mov x2, x30 - .cfi_register x30, x2 - - /* Acquire the sequence counter and get the timespec. */ adr vdso_data, _vdso_data -1: seqcnt_acquire - cbnz use_syscall, 4f - /* If tv is NULL, skip to the timezone code. */ cbz x0, 2f - bl __do_get_tspec - seqcnt_check w9, 1b + + /* Compute the time of day. */ +1: seqcnt_acquire + syscall_check fail=4f + ldr x10, [vdso_data, #VDSO_CS_CYCLE_LAST] + /* w11 = cs_mono_mult, w12 = cs_shift */ + ldp w11, w12, [vdso_data, #VDSO_CS_MONO_MULT] + ldp x13, x14, [vdso_data, #VDSO_XTIME_CLK_SEC] + seqcnt_check fail=1b + + get_nsec_per_sec res=x9 + lsl x9, x9, x12 + + get_clock_shifted_nsec res=x15, cycle_last=x10, mult=x11 + get_ts_realtime res_sec=x10, res_nsec=x11, \ + clock_nsec=x15, xtime_sec=x13, xtime_nsec=x14, nsec_to_sec=x9 /* Convert ns to us. */ mov x13, #1000 @@ -76,95 +168,126 @@ ENTRY(__kernel_gettimeofday) stp w4, w5, [x1, #TZ_MINWEST] 3: mov x0, xzr - ret x2 + ret 4: /* Syscall fallback. */ mov x8, #__NR_gettimeofday svc #0 - ret x2 + ret .cfi_endproc ENDPROC(__kernel_gettimeofday) +#define JUMPSLOT_MAX CLOCK_MONOTONIC_COARSE + /* int __kernel_clock_gettime(clockid_t clock_id, struct timespec *tp); */ ENTRY(__kernel_clock_gettime) .cfi_startproc - cmp w0, #CLOCK_REALTIME - ccmp w0, #CLOCK_MONOTONIC, #0x4, ne - b.ne 2f + cmp w0, #JUMPSLOT_MAX + b.hi syscall + adr vdso_data, _vdso_data + adr x_tmp, jumptable + add x_tmp, x_tmp, w0, uxtw #2 + br x_tmp + + ALIGN +jumptable: + jump_slot jumptable, CLOCK_REALTIME, realtime + jump_slot jumptable, CLOCK_MONOTONIC, monotonic + b syscall + b syscall + jump_slot jumptable, CLOCK_MONOTONIC_RAW, monotonic_raw + jump_slot jumptable, CLOCK_REALTIME_COARSE, realtime_coarse + jump_slot jumptable, CLOCK_MONOTONIC_COARSE, monotonic_coarse + + .if (. - jumptable) != 4 * (JUMPSLOT_MAX + 1) + .error "Wrong jumptable size" + .endif + + ALIGN +realtime: + seqcnt_acquire + syscall_check fail=syscall + ldr x10, [vdso_data, #VDSO_CS_CYCLE_LAST] + /* w11 = cs_mono_mult, w12 = cs_shift */ + ldp w11, w12, [vdso_data, #VDSO_CS_MONO_MULT] + ldp x13, x14, [vdso_data, #VDSO_XTIME_CLK_SEC] + seqcnt_check fail=realtime - mov x2, x30 - .cfi_register x30, x2 + /* All computations are done with left-shifted nsecs. */ + get_nsec_per_sec res=x9 + lsl x9, x9, x12 - /* Get kernel timespec. */ - adr vdso_data, _vdso_data -1: seqcnt_acquire - cbnz use_syscall, 7f + get_clock_shifted_nsec res=x15, cycle_last=x10, mult=x11 + get_ts_realtime res_sec=x10, res_nsec=x11, \ + clock_nsec=x15, xtime_sec=x13, xtime_nsec=x14, nsec_to_sec=x9 + clock_gettime_return, shift=1 - bl __do_get_tspec - seqcnt_check w9, 1b + ALIGN +monotonic: + seqcnt_acquire + syscall_check fail=syscall + ldr x10, [vdso_data, #VDSO_CS_CYCLE_LAST] + /* w11 = cs_mono_mult, w12 = cs_shift */ + ldp w11, w12, [vdso_data, #VDSO_CS_MONO_MULT] + ldp x13, x14, [vdso_data, #VDSO_XTIME_CLK_SEC] + ldp x3, x4, [vdso_data, #VDSO_WTM_CLK_SEC] + seqcnt_check fail=monotonic - mov x30, x2 + /* All computations are done with left-shifted nsecs. */ + lsl x4, x4, x12 + get_nsec_per_sec res=x9 + lsl x9, x9, x12 - cmp w0, #CLOCK_MONOTONIC - b.ne 6f + get_clock_shifted_nsec res=x15, cycle_last=x10, mult=x11 + get_ts_realtime res_sec=x10, res_nsec=x11, \ + clock_nsec=x15, xtime_sec=x13, xtime_nsec=x14, nsec_to_sec=x9 - /* Get wtm timespec. */ - ldp x13, x14, [vdso_data, #VDSO_WTM_CLK_SEC] + add_ts sec=x10, nsec=x11, ts_sec=x3, ts_nsec=x4, nsec_to_sec=x9 + clock_gettime_return, shift=1 - /* Check the sequence counter. */ - seqcnt_read w9 - seqcnt_check w9, 1b - b 4f -2: - cmp w0, #CLOCK_REALTIME_COARSE - ccmp w0, #CLOCK_MONOTONIC_COARSE, #0x4, ne - b.ne 8f + ALIGN +monotonic_raw: + seqcnt_acquire + syscall_check fail=syscall + ldr x10, [vdso_data, #VDSO_CS_CYCLE_LAST] + /* w11 = cs_raw_mult, w12 = cs_shift */ + ldp w12, w11, [vdso_data, #VDSO_CS_SHIFT] + ldp x13, x14, [vdso_data, #VDSO_RAW_TIME_SEC] + seqcnt_check fail=monotonic_raw - /* xtime_coarse_nsec is already right-shifted */ - mov x12, #0 + /* All computations are done with left-shifted nsecs. */ + lsl x14, x14, x12 + get_nsec_per_sec res=x9 + lsl x9, x9, x12 - /* Get coarse timespec. */ - adr vdso_data, _vdso_data -3: seqcnt_acquire - ldp x10, x11, [vdso_data, #VDSO_XTIME_CRS_SEC] + get_clock_shifted_nsec res=x15, cycle_last=x10, mult=x11 + get_ts_clock_raw res_sec=x10, res_nsec=x11, \ + clock_nsec=x15, nsec_to_sec=x9 - /* Get wtm timespec. */ - ldp x13, x14, [vdso_data, #VDSO_WTM_CLK_SEC] + add_ts sec=x10, nsec=x11, ts_sec=x13, ts_nsec=x14, nsec_to_sec=x9 + clock_gettime_return, shift=1 - /* Check the sequence counter. */ - seqcnt_read w9 - seqcnt_check w9, 3b + ALIGN +realtime_coarse: + seqcnt_acquire + ldp x10, x11, [vdso_data, #VDSO_XTIME_CRS_SEC] + seqcnt_check fail=realtime_coarse + clock_gettime_return - cmp w0, #CLOCK_MONOTONIC_COARSE - b.ne 6f -4: - /* Add on wtm timespec. */ - add x10, x10, x13 - lsl x14, x14, x12 - add x11, x11, x14 + ALIGN +monotonic_coarse: + seqcnt_acquire + ldp x10, x11, [vdso_data, #VDSO_XTIME_CRS_SEC] + ldp x13, x14, [vdso_data, #VDSO_WTM_CLK_SEC] + seqcnt_check fail=monotonic_coarse - /* Normalise the new timespec. */ - mov x15, #NSEC_PER_SEC_LO16 - movk x15, #NSEC_PER_SEC_HI16, lsl #16 - lsl x15, x15, x12 - cmp x11, x15 - b.lt 5f - sub x11, x11, x15 - add x10, x10, #1 -5: - cmp x11, #0 - b.ge 6f - add x11, x11, x15 - sub x10, x10, #1 + /* Computations are done in (non-shifted) nsecs. */ + get_nsec_per_sec res=x9 + add_ts sec=x10, nsec=x11, ts_sec=x13, ts_nsec=x14, nsec_to_sec=x9 + clock_gettime_return -6: /* Store to the user timespec. */ - lsr x11, x11, x12 - stp x10, x11, [x1, #TSPEC_TV_SEC] - mov x0, xzr - ret -7: - mov x30, x2 -8: /* Syscall fallback. */ + ALIGN +syscall: /* Syscall fallback. */ mov x8, #__NR_clock_gettime svc #0 ret @@ -176,6 +299,7 @@ ENTRY(__kernel_clock_getres) .cfi_startproc cmp w0, #CLOCK_REALTIME ccmp w0, #CLOCK_MONOTONIC, #0x4, ne + ccmp w0, #CLOCK_MONOTONIC_RAW, #0x4, ne b.ne 1f ldr x2, 5f @@ -203,55 +327,3 @@ ENTRY(__kernel_clock_getres) .quad CLOCK_COARSE_RES .cfi_endproc ENDPROC(__kernel_clock_getres) - -/* - * Read the current time from the architected counter. - * Expects vdso_data to be initialised. - * Clobbers the temporary registers (x9 - x15). - * Returns: - * - w9 = vDSO sequence counter - * - (x10, x11) = (ts->tv_sec, shifted ts->tv_nsec) - * - w12 = cs_shift - */ -ENTRY(__do_get_tspec) - .cfi_startproc - - /* Read the virtual counter. */ - isb -#if IS_ENABLED(CONFIG_MSM_TIMER_LEAP) -#define LEAST_32BITS 0x00000000FFFFFFFF -reread: - mrs x15, cntvct_el0 - and x13, x15, #LEAST_32BITS - eor x13, x13, #LEAST_32BITS - cbz x13, reread -#else - mrs x15, cntvct_el0 -#endif - - /* Read from the vDSO data page. */ - ldr x10, [vdso_data, #VDSO_CS_CYCLE_LAST] - ldp x13, x14, [vdso_data, #VDSO_XTIME_CLK_SEC] - ldp w11, w12, [vdso_data, #VDSO_CS_MULT] - seqcnt_read w9 - - /* Calculate cycle delta and convert to ns. */ - sub x10, x15, x10 - /* We can only guarantee 56 bits of precision. */ - movn x15, #0xff00, lsl #48 - and x10, x15, x10 - mul x10, x10, x11 - - /* Use the kernel time to calculate the new timespec. */ - mov x11, #NSEC_PER_SEC_LO16 - movk x11, #NSEC_PER_SEC_HI16, lsl #16 - lsl x11, x11, x12 - add x15, x10, x14 - udiv x14, x15, x11 - add x10, x13, x14 - mul x13, x14, x11 - sub x11, x15, x13 - - ret - .cfi_endproc -ENDPROC(__do_get_tspec) diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index a13b9a65322f..1804aea44faa 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c @@ -708,5 +708,6 @@ int cpu_enable_pan(void *__unused) int cpu_enable_uao(void *__unused) { asm(SET_PSTATE_UAO(1)); + return 0; } #endif /* CONFIG_ARM64_UAO */ diff --git a/arch/c6x/kernel/ptrace.c b/arch/c6x/kernel/ptrace.c index 3c494e84444d..a511ac16a8e3 100644 --- a/arch/c6x/kernel/ptrace.c +++ b/arch/c6x/kernel/ptrace.c @@ -69,46 +69,6 @@ static int gpr_get(struct task_struct *target, 0, sizeof(*regs)); } -static int gpr_set(struct task_struct *target, - const struct user_regset *regset, - unsigned int pos, unsigned int count, - const void *kbuf, const void __user *ubuf) -{ - int ret; - struct pt_regs *regs = task_pt_regs(target); - - /* Don't copyin TSR or CSR */ - ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, - ®s, - 0, PT_TSR * sizeof(long)); - if (ret) - return ret; - - ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, - PT_TSR * sizeof(long), - (PT_TSR + 1) * sizeof(long)); - if (ret) - return ret; - - ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, - ®s, - (PT_TSR + 1) * sizeof(long), - PT_CSR * sizeof(long)); - if (ret) - return ret; - - ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, - PT_CSR * sizeof(long), - (PT_CSR + 1) * sizeof(long)); - if (ret) - return ret; - - ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, - ®s, - (PT_CSR + 1) * sizeof(long), -1); - return ret; -} - enum c6x_regset { REGSET_GPR, }; @@ -120,7 +80,6 @@ static const struct user_regset c6x_regsets[] = { .size = sizeof(u32), .align = sizeof(u32), .get = gpr_get, - .set = gpr_set }, }; diff --git a/arch/h8300/kernel/ptrace.c b/arch/h8300/kernel/ptrace.c index 92075544a19a..0dc1c8f622bc 100644 --- a/arch/h8300/kernel/ptrace.c +++ b/arch/h8300/kernel/ptrace.c @@ -95,7 +95,8 @@ static int regs_get(struct task_struct *target, long *reg = (long *)®s; /* build user regs in buffer */ - for (r = 0; r < ARRAY_SIZE(register_offset); r++) + BUILD_BUG_ON(sizeof(regs) % sizeof(long) != 0); + for (r = 0; r < sizeof(regs) / sizeof(long); r++) *reg++ = h8300_get_reg(target, r); return user_regset_copyout(&pos, &count, &kbuf, &ubuf, @@ -113,7 +114,8 @@ static int regs_set(struct task_struct *target, long *reg; /* build user regs in buffer */ - for (reg = (long *)®s, r = 0; r < ARRAY_SIZE(register_offset); r++) + BUILD_BUG_ON(sizeof(regs) % sizeof(long) != 0); + for (reg = (long *)®s, r = 0; r < sizeof(regs) / sizeof(long); r++) *reg++ = h8300_get_reg(target, r); ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, @@ -122,7 +124,7 @@ static int regs_set(struct task_struct *target, return ret; /* write back to pt_regs */ - for (reg = (long *)®s, r = 0; r < ARRAY_SIZE(register_offset); r++) + for (reg = (long *)®s, r = 0; r < sizeof(regs) / sizeof(long); r++) h8300_put_reg(target, r, *reg++); return 0; } diff --git a/arch/ia64/Makefile b/arch/ia64/Makefile index 970d0bd99621..648f1cef33fa 100644 --- a/arch/ia64/Makefile +++ b/arch/ia64/Makefile @@ -95,8 +95,8 @@ define archhelp echo '* unwcheck - Check vmlinux for invalid unwind info' endef -archprepare: make_nr_irqs_h FORCE +archprepare: make_nr_irqs_h PHONY += make_nr_irqs_h FORCE -make_nr_irqs_h: FORCE +make_nr_irqs_h: $(Q)$(MAKE) $(build)=arch/ia64/kernel include/generated/nr-irqs.h diff --git a/arch/metag/include/asm/uaccess.h b/arch/metag/include/asm/uaccess.h index 273e61225c27..07238b39638c 100644 --- a/arch/metag/include/asm/uaccess.h +++ b/arch/metag/include/asm/uaccess.h @@ -197,20 +197,21 @@ extern long __must_check strnlen_user(const char __user *src, long count); #define strlen_user(str) strnlen_user(str, 32767) -extern unsigned long __must_check __copy_user_zeroing(void *to, - const void __user *from, - unsigned long n); +extern unsigned long raw_copy_from_user(void *to, const void __user *from, + unsigned long n); static inline unsigned long copy_from_user(void *to, const void __user *from, unsigned long n) { + unsigned long res = n; if (likely(access_ok(VERIFY_READ, from, n))) - return __copy_user_zeroing(to, from, n); - memset(to, 0, n); - return n; + res = raw_copy_from_user(to, from, n); + if (unlikely(res)) + memset(to + (n - res), 0, res); + return res; } -#define __copy_from_user(to, from, n) __copy_user_zeroing(to, from, n) +#define __copy_from_user(to, from, n) raw_copy_from_user(to, from, n) #define __copy_from_user_inatomic __copy_from_user extern unsigned long __must_check __copy_user(void __user *to, diff --git a/arch/metag/kernel/ptrace.c b/arch/metag/kernel/ptrace.c index 7563628822bd..5e2dc7defd2c 100644 --- a/arch/metag/kernel/ptrace.c +++ b/arch/metag/kernel/ptrace.c @@ -24,6 +24,16 @@ * user_regset definitions. */ +static unsigned long user_txstatus(const struct pt_regs *regs) +{ + unsigned long data = (unsigned long)regs->ctx.Flags; + + if (regs->ctx.SaveMask & TBICTX_CBUF_BIT) + data |= USER_GP_REGS_STATUS_CATCH_BIT; + + return data; +} + int metag_gp_regs_copyout(const struct pt_regs *regs, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) @@ -62,9 +72,7 @@ int metag_gp_regs_copyout(const struct pt_regs *regs, if (ret) goto out; /* TXSTATUS */ - data = (unsigned long)regs->ctx.Flags; - if (regs->ctx.SaveMask & TBICTX_CBUF_BIT) - data |= USER_GP_REGS_STATUS_CATCH_BIT; + data = user_txstatus(regs); ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &data, 4*25, 4*26); if (ret) @@ -119,6 +127,7 @@ int metag_gp_regs_copyin(struct pt_regs *regs, if (ret) goto out; /* TXSTATUS */ + data = user_txstatus(regs); ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &data, 4*25, 4*26); if (ret) @@ -244,6 +253,8 @@ int metag_rp_state_copyin(struct pt_regs *regs, unsigned long long *ptr; int ret, i; + if (count < 4*13) + return -EINVAL; /* Read the entire pipeline before making any changes */ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &rp, 0, 4*13); @@ -303,7 +314,7 @@ static int metag_tls_set(struct task_struct *target, const void *kbuf, const void __user *ubuf) { int ret; - void __user *tls; + void __user *tls = target->thread.tls_ptr; ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &tls, 0, -1); if (ret) diff --git a/arch/metag/lib/usercopy.c b/arch/metag/lib/usercopy.c index b3ebfe9c8e88..2792fc621088 100644 --- a/arch/metag/lib/usercopy.c +++ b/arch/metag/lib/usercopy.c @@ -29,7 +29,6 @@ COPY \ "1:\n" \ " .section .fixup,\"ax\"\n" \ - " MOV D1Ar1,#0\n" \ FIXUP \ " MOVT D1Ar1,#HI(1b)\n" \ " JUMP D1Ar1,#LO(1b)\n" \ @@ -260,27 +259,31 @@ "MGETL D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \ "22:\n" \ "MSETL [%0++], D0FrT, D0.5, D0.6, D0.7\n" \ - "SUB %3, %3, #32\n" \ "23:\n" \ - "MGETL D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \ + "SUB %3, %3, #32\n" \ "24:\n" \ + "MGETL D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \ + "25:\n" \ "MSETL [%0++], D0FrT, D0.5, D0.6, D0.7\n" \ + "26:\n" \ "SUB %3, %3, #32\n" \ "DCACHE [%1+#-64], D0Ar6\n" \ "BR $Lloop"id"\n" \ \ "MOV RAPF, %1\n" \ - "25:\n" \ + "27:\n" \ "MGETL D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \ - "26:\n" \ + "28:\n" \ "MSETL [%0++], D0FrT, D0.5, D0.6, D0.7\n" \ + "29:\n" \ "SUB %3, %3, #32\n" \ - "27:\n" \ + "30:\n" \ "MGETL D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \ - "28:\n" \ + "31:\n" \ "MSETL [%0++], D0FrT, D0.5, D0.6, D0.7\n" \ + "32:\n" \ "SUB %0, %0, #8\n" \ - "29:\n" \ + "33:\n" \ "SETL [%0++], D0.7, D1.7\n" \ "SUB %3, %3, #32\n" \ "1:" \ @@ -312,11 +315,15 @@ " .long 26b,3b\n" \ " .long 27b,3b\n" \ " .long 28b,3b\n" \ - " .long 29b,4b\n" \ + " .long 29b,3b\n" \ + " .long 30b,3b\n" \ + " .long 31b,3b\n" \ + " .long 32b,3b\n" \ + " .long 33b,4b\n" \ " .previous\n" \ : "=r" (to), "=r" (from), "=r" (ret), "=d" (n) \ : "0" (to), "1" (from), "2" (ret), "3" (n) \ - : "D1Ar1", "D0Ar2", "memory") + : "D1Ar1", "D0Ar2", "cc", "memory") /* rewind 'to' and 'from' pointers when a fault occurs * @@ -342,7 +349,7 @@ #define __asm_copy_to_user_64bit_rapf_loop(to, from, ret, n, id)\ __asm_copy_user_64bit_rapf_loop(to, from, ret, n, id, \ "LSR D0Ar2, D0Ar2, #8\n" \ - "AND D0Ar2, D0Ar2, #0x7\n" \ + "ANDS D0Ar2, D0Ar2, #0x7\n" \ "ADDZ D0Ar2, D0Ar2, #4\n" \ "SUB D0Ar2, D0Ar2, #1\n" \ "MOV D1Ar1, #4\n" \ @@ -403,47 +410,55 @@ "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \ "22:\n" \ "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \ - "SUB %3, %3, #16\n" \ "23:\n" \ - "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \ - "24:\n" \ - "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \ "SUB %3, %3, #16\n" \ - "25:\n" \ + "24:\n" \ "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \ - "26:\n" \ + "25:\n" \ "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \ + "26:\n" \ "SUB %3, %3, #16\n" \ "27:\n" \ "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \ "28:\n" \ "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \ + "29:\n" \ + "SUB %3, %3, #16\n" \ + "30:\n" \ + "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \ + "31:\n" \ + "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \ + "32:\n" \ "SUB %3, %3, #16\n" \ "DCACHE [%1+#-64], D0Ar6\n" \ "BR $Lloop"id"\n" \ \ "MOV RAPF, %1\n" \ - "29:\n" \ + "33:\n" \ "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \ - "30:\n" \ + "34:\n" \ "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \ + "35:\n" \ "SUB %3, %3, #16\n" \ - "31:\n" \ + "36:\n" \ "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \ - "32:\n" \ + "37:\n" \ "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \ + "38:\n" \ "SUB %3, %3, #16\n" \ - "33:\n" \ + "39:\n" \ "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \ - "34:\n" \ + "40:\n" \ "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \ + "41:\n" \ "SUB %3, %3, #16\n" \ - "35:\n" \ + "42:\n" \ "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \ - "36:\n" \ + "43:\n" \ "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \ + "44:\n" \ "SUB %0, %0, #4\n" \ - "37:\n" \ + "45:\n" \ "SETD [%0++], D0.7\n" \ "SUB %3, %3, #16\n" \ "1:" \ @@ -483,11 +498,19 @@ " .long 34b,3b\n" \ " .long 35b,3b\n" \ " .long 36b,3b\n" \ - " .long 37b,4b\n" \ + " .long 37b,3b\n" \ + " .long 38b,3b\n" \ + " .long 39b,3b\n" \ + " .long 40b,3b\n" \ + " .long 41b,3b\n" \ + " .long 42b,3b\n" \ + " .long 43b,3b\n" \ + " .long 44b,3b\n" \ + " .long 45b,4b\n" \ " .previous\n" \ : "=r" (to), "=r" (from), "=r" (ret), "=d" (n) \ : "0" (to), "1" (from), "2" (ret), "3" (n) \ - : "D1Ar1", "D0Ar2", "memory") + : "D1Ar1", "D0Ar2", "cc", "memory") /* rewind 'to' and 'from' pointers when a fault occurs * @@ -513,7 +536,7 @@ #define __asm_copy_to_user_32bit_rapf_loop(to, from, ret, n, id)\ __asm_copy_user_32bit_rapf_loop(to, from, ret, n, id, \ "LSR D0Ar2, D0Ar2, #8\n" \ - "AND D0Ar2, D0Ar2, #0x7\n" \ + "ANDS D0Ar2, D0Ar2, #0x7\n" \ "ADDZ D0Ar2, D0Ar2, #4\n" \ "SUB D0Ar2, D0Ar2, #1\n" \ "MOV D1Ar1, #4\n" \ @@ -538,23 +561,31 @@ unsigned long __copy_user(void __user *pdst, const void *psrc, if ((unsigned long) src & 1) { __asm_copy_to_user_1(dst, src, retn); n--; + if (retn) + return retn + n; } if ((unsigned long) dst & 1) { /* Worst case - byte copy */ while (n > 0) { __asm_copy_to_user_1(dst, src, retn); n--; + if (retn) + return retn + n; } } if (((unsigned long) src & 2) && n >= 2) { __asm_copy_to_user_2(dst, src, retn); n -= 2; + if (retn) + return retn + n; } if ((unsigned long) dst & 2) { /* Second worst case - word copy */ while (n >= 2) { __asm_copy_to_user_2(dst, src, retn); n -= 2; + if (retn) + return retn + n; } } @@ -569,6 +600,8 @@ unsigned long __copy_user(void __user *pdst, const void *psrc, while (n >= 8) { __asm_copy_to_user_8x64(dst, src, retn); n -= 8; + if (retn) + return retn + n; } } if (n >= RAPF_MIN_BUF_SIZE) { @@ -581,6 +614,8 @@ unsigned long __copy_user(void __user *pdst, const void *psrc, while (n >= 8) { __asm_copy_to_user_8x64(dst, src, retn); n -= 8; + if (retn) + return retn + n; } } #endif @@ -588,11 +623,15 @@ unsigned long __copy_user(void __user *pdst, const void *psrc, while (n >= 16) { __asm_copy_to_user_16(dst, src, retn); n -= 16; + if (retn) + return retn + n; } while (n >= 4) { __asm_copy_to_user_4(dst, src, retn); n -= 4; + if (retn) + return retn + n; } switch (n) { @@ -609,6 +648,10 @@ unsigned long __copy_user(void __user *pdst, const void *psrc, break; } + /* + * If we get here, retn correctly reflects the number of failing + * bytes. + */ return retn; } EXPORT_SYMBOL(__copy_user); @@ -617,16 +660,14 @@ EXPORT_SYMBOL(__copy_user); __asm_copy_user_cont(to, from, ret, \ " GETB D1Ar1,[%1++]\n" \ "2: SETB [%0++],D1Ar1\n", \ - "3: ADD %2,%2,#1\n" \ - " SETB [%0++],D1Ar1\n", \ + "3: ADD %2,%2,#1\n", \ " .long 2b,3b\n") #define __asm_copy_from_user_2x_cont(to, from, ret, COPY, FIXUP, TENTRY) \ __asm_copy_user_cont(to, from, ret, \ " GETW D1Ar1,[%1++]\n" \ "2: SETW [%0++],D1Ar1\n" COPY, \ - "3: ADD %2,%2,#2\n" \ - " SETW [%0++],D1Ar1\n" FIXUP, \ + "3: ADD %2,%2,#2\n" FIXUP, \ " .long 2b,3b\n" TENTRY) #define __asm_copy_from_user_2(to, from, ret) \ @@ -636,145 +677,26 @@ EXPORT_SYMBOL(__copy_user); __asm_copy_from_user_2x_cont(to, from, ret, \ " GETB D1Ar1,[%1++]\n" \ "4: SETB [%0++],D1Ar1\n", \ - "5: ADD %2,%2,#1\n" \ - " SETB [%0++],D1Ar1\n", \ + "5: ADD %2,%2,#1\n", \ " .long 4b,5b\n") #define __asm_copy_from_user_4x_cont(to, from, ret, COPY, FIXUP, TENTRY) \ __asm_copy_user_cont(to, from, ret, \ " GETD D1Ar1,[%1++]\n" \ "2: SETD [%0++],D1Ar1\n" COPY, \ - "3: ADD %2,%2,#4\n" \ - " SETD [%0++],D1Ar1\n" FIXUP, \ + "3: ADD %2,%2,#4\n" FIXUP, \ " .long 2b,3b\n" TENTRY) #define __asm_copy_from_user_4(to, from, ret) \ __asm_copy_from_user_4x_cont(to, from, ret, "", "", "") -#define __asm_copy_from_user_5(to, from, ret) \ - __asm_copy_from_user_4x_cont(to, from, ret, \ - " GETB D1Ar1,[%1++]\n" \ - "4: SETB [%0++],D1Ar1\n", \ - "5: ADD %2,%2,#1\n" \ - " SETB [%0++],D1Ar1\n", \ - " .long 4b,5b\n") - -#define __asm_copy_from_user_6x_cont(to, from, ret, COPY, FIXUP, TENTRY) \ - __asm_copy_from_user_4x_cont(to, from, ret, \ - " GETW D1Ar1,[%1++]\n" \ - "4: SETW [%0++],D1Ar1\n" COPY, \ - "5: ADD %2,%2,#2\n" \ - " SETW [%0++],D1Ar1\n" FIXUP, \ - " .long 4b,5b\n" TENTRY) - -#define __asm_copy_from_user_6(to, from, ret) \ - __asm_copy_from_user_6x_cont(to, from, ret, "", "", "") - -#define __asm_copy_from_user_7(to, from, ret) \ - __asm_copy_from_user_6x_cont(to, from, ret, \ - " GETB D1Ar1,[%1++]\n" \ - "6: SETB [%0++],D1Ar1\n", \ - "7: ADD %2,%2,#1\n" \ - " SETB [%0++],D1Ar1\n", \ - " .long 6b,7b\n") - -#define __asm_copy_from_user_8x_cont(to, from, ret, COPY, FIXUP, TENTRY) \ - __asm_copy_from_user_4x_cont(to, from, ret, \ - " GETD D1Ar1,[%1++]\n" \ - "4: SETD [%0++],D1Ar1\n" COPY, \ - "5: ADD %2,%2,#4\n" \ - " SETD [%0++],D1Ar1\n" FIXUP, \ - " .long 4b,5b\n" TENTRY) - -#define __asm_copy_from_user_8(to, from, ret) \ - __asm_copy_from_user_8x_cont(to, from, ret, "", "", "") - -#define __asm_copy_from_user_9(to, from, ret) \ - __asm_copy_from_user_8x_cont(to, from, ret, \ - " GETB D1Ar1,[%1++]\n" \ - "6: SETB [%0++],D1Ar1\n", \ - "7: ADD %2,%2,#1\n" \ - " SETB [%0++],D1Ar1\n", \ - " .long 6b,7b\n") - -#define __asm_copy_from_user_10x_cont(to, from, ret, COPY, FIXUP, TENTRY) \ - __asm_copy_from_user_8x_cont(to, from, ret, \ - " GETW D1Ar1,[%1++]\n" \ - "6: SETW [%0++],D1Ar1\n" COPY, \ - "7: ADD %2,%2,#2\n" \ - " SETW [%0++],D1Ar1\n" FIXUP, \ - " .long 6b,7b\n" TENTRY) - -#define __asm_copy_from_user_10(to, from, ret) \ - __asm_copy_from_user_10x_cont(to, from, ret, "", "", "") - -#define __asm_copy_from_user_11(to, from, ret) \ - __asm_copy_from_user_10x_cont(to, from, ret, \ - " GETB D1Ar1,[%1++]\n" \ - "8: SETB [%0++],D1Ar1\n", \ - "9: ADD %2,%2,#1\n" \ - " SETB [%0++],D1Ar1\n", \ - " .long 8b,9b\n") - -#define __asm_copy_from_user_12x_cont(to, from, ret, COPY, FIXUP, TENTRY) \ - __asm_copy_from_user_8x_cont(to, from, ret, \ - " GETD D1Ar1,[%1++]\n" \ - "6: SETD [%0++],D1Ar1\n" COPY, \ - "7: ADD %2,%2,#4\n" \ - " SETD [%0++],D1Ar1\n" FIXUP, \ - " .long 6b,7b\n" TENTRY) - -#define __asm_copy_from_user_12(to, from, ret) \ - __asm_copy_from_user_12x_cont(to, from, ret, "", "", "") - -#define __asm_copy_from_user_13(to, from, ret) \ - __asm_copy_from_user_12x_cont(to, from, ret, \ - " GETB D1Ar1,[%1++]\n" \ - "8: SETB [%0++],D1Ar1\n", \ - "9: ADD %2,%2,#1\n" \ - " SETB [%0++],D1Ar1\n", \ - " .long 8b,9b\n") - -#define __asm_copy_from_user_14x_cont(to, from, ret, COPY, FIXUP, TENTRY) \ - __asm_copy_from_user_12x_cont(to, from, ret, \ - " GETW D1Ar1,[%1++]\n" \ - "8: SETW [%0++],D1Ar1\n" COPY, \ - "9: ADD %2,%2,#2\n" \ - " SETW [%0++],D1Ar1\n" FIXUP, \ - " .long 8b,9b\n" TENTRY) - -#define __asm_copy_from_user_14(to, from, ret) \ - __asm_copy_from_user_14x_cont(to, from, ret, "", "", "") - -#define __asm_copy_from_user_15(to, from, ret) \ - __asm_copy_from_user_14x_cont(to, from, ret, \ - " GETB D1Ar1,[%1++]\n" \ - "10: SETB [%0++],D1Ar1\n", \ - "11: ADD %2,%2,#1\n" \ - " SETB [%0++],D1Ar1\n", \ - " .long 10b,11b\n") - -#define __asm_copy_from_user_16x_cont(to, from, ret, COPY, FIXUP, TENTRY) \ - __asm_copy_from_user_12x_cont(to, from, ret, \ - " GETD D1Ar1,[%1++]\n" \ - "8: SETD [%0++],D1Ar1\n" COPY, \ - "9: ADD %2,%2,#4\n" \ - " SETD [%0++],D1Ar1\n" FIXUP, \ - " .long 8b,9b\n" TENTRY) - -#define __asm_copy_from_user_16(to, from, ret) \ - __asm_copy_from_user_16x_cont(to, from, ret, "", "", "") - #define __asm_copy_from_user_8x64(to, from, ret) \ asm volatile ( \ " GETL D0Ar2,D1Ar1,[%1++]\n" \ "2: SETL [%0++],D0Ar2,D1Ar1\n" \ "1:\n" \ " .section .fixup,\"ax\"\n" \ - " MOV D1Ar1,#0\n" \ - " MOV D0Ar2,#0\n" \ "3: ADD %2,%2,#8\n" \ - " SETL [%0++],D0Ar2,D1Ar1\n" \ " MOVT D0Ar2,#HI(1b)\n" \ " JUMP D0Ar2,#LO(1b)\n" \ " .previous\n" \ @@ -789,36 +711,57 @@ EXPORT_SYMBOL(__copy_user); * * Rationale: * A fault occurs while reading from user buffer, which is the - * source. Since the fault is at a single address, we only - * need to rewind by 8 bytes. + * source. * Since we don't write to kernel buffer until we read first, * the kernel buffer is at the right state and needn't be - * corrected. + * corrected, but the source must be rewound to the beginning of + * the block, which is LSM_STEP*8 bytes. + * LSM_STEP is bits 10:8 in TXSTATUS which is already read + * and stored in D0Ar2 + * + * NOTE: If a fault occurs at the last operation in M{G,S}ETL + * LSM_STEP will be 0. ie: we do 4 writes in our case, if + * a fault happens at the 4th write, LSM_STEP will be 0 + * instead of 4. The code copes with that. */ #define __asm_copy_from_user_64bit_rapf_loop(to, from, ret, n, id) \ __asm_copy_user_64bit_rapf_loop(to, from, ret, n, id, \ - "SUB %1, %1, #8\n") + "LSR D0Ar2, D0Ar2, #5\n" \ + "ANDS D0Ar2, D0Ar2, #0x38\n" \ + "ADDZ D0Ar2, D0Ar2, #32\n" \ + "SUB %1, %1, D0Ar2\n") /* rewind 'from' pointer when a fault occurs * * Rationale: * A fault occurs while reading from user buffer, which is the - * source. Since the fault is at a single address, we only - * need to rewind by 4 bytes. + * source. * Since we don't write to kernel buffer until we read first, * the kernel buffer is at the right state and needn't be - * corrected. + * corrected, but the source must be rewound to the beginning of + * the block, which is LSM_STEP*4 bytes. + * LSM_STEP is bits 10:8 in TXSTATUS which is already read + * and stored in D0Ar2 + * + * NOTE: If a fault occurs at the last operation in M{G,S}ETL + * LSM_STEP will be 0. ie: we do 4 writes in our case, if + * a fault happens at the 4th write, LSM_STEP will be 0 + * instead of 4. The code copes with that. */ #define __asm_copy_from_user_32bit_rapf_loop(to, from, ret, n, id) \ __asm_copy_user_32bit_rapf_loop(to, from, ret, n, id, \ - "SUB %1, %1, #4\n") + "LSR D0Ar2, D0Ar2, #6\n" \ + "ANDS D0Ar2, D0Ar2, #0x1c\n" \ + "ADDZ D0Ar2, D0Ar2, #16\n" \ + "SUB %1, %1, D0Ar2\n") -/* Copy from user to kernel, zeroing the bytes that were inaccessible in - userland. The return-value is the number of bytes that were - inaccessible. */ -unsigned long __copy_user_zeroing(void *pdst, const void __user *psrc, - unsigned long n) +/* + * Copy from user to kernel. The return-value is the number of bytes that were + * inaccessible. + */ +unsigned long raw_copy_from_user(void *pdst, const void __user *psrc, + unsigned long n) { register char *dst asm ("A0.2") = pdst; register const char __user *src asm ("A1.2") = psrc; @@ -830,6 +773,8 @@ unsigned long __copy_user_zeroing(void *pdst, const void __user *psrc, if ((unsigned long) src & 1) { __asm_copy_from_user_1(dst, src, retn); n--; + if (retn) + return retn + n; } if ((unsigned long) dst & 1) { /* Worst case - byte copy */ @@ -837,12 +782,14 @@ unsigned long __copy_user_zeroing(void *pdst, const void __user *psrc, __asm_copy_from_user_1(dst, src, retn); n--; if (retn) - goto copy_exception_bytes; + return retn + n; } } if (((unsigned long) src & 2) && n >= 2) { __asm_copy_from_user_2(dst, src, retn); n -= 2; + if (retn) + return retn + n; } if ((unsigned long) dst & 2) { /* Second worst case - word copy */ @@ -850,16 +797,10 @@ unsigned long __copy_user_zeroing(void *pdst, const void __user *psrc, __asm_copy_from_user_2(dst, src, retn); n -= 2; if (retn) - goto copy_exception_bytes; + return retn + n; } } - /* We only need one check after the unalignment-adjustments, - because if both adjustments were done, either both or - neither reference had an exception. */ - if (retn != 0) - goto copy_exception_bytes; - #ifdef USE_RAPF /* 64 bit copy loop */ if (!(((unsigned long) src | (unsigned long) dst) & 7)) { @@ -872,7 +813,7 @@ unsigned long __copy_user_zeroing(void *pdst, const void __user *psrc, __asm_copy_from_user_8x64(dst, src, retn); n -= 8; if (retn) - goto copy_exception_bytes; + return retn + n; } } @@ -888,7 +829,7 @@ unsigned long __copy_user_zeroing(void *pdst, const void __user *psrc, __asm_copy_from_user_8x64(dst, src, retn); n -= 8; if (retn) - goto copy_exception_bytes; + return retn + n; } } #endif @@ -898,7 +839,7 @@ unsigned long __copy_user_zeroing(void *pdst, const void __user *psrc, n -= 4; if (retn) - goto copy_exception_bytes; + return retn + n; } /* If we get here, there were no memory read faults. */ @@ -924,21 +865,8 @@ unsigned long __copy_user_zeroing(void *pdst, const void __user *psrc, /* If we get here, retn correctly reflects the number of failing bytes. */ return retn; - - copy_exception_bytes: - /* We already have "retn" bytes cleared, and need to clear the - remaining "n" bytes. A non-optimized simple byte-for-byte in-line - memset is preferred here, since this isn't speed-critical code and - we'd rather have this a leaf-function than calling memset. */ - { - char *endp; - for (endp = dst + n; dst < endp; dst++) - *dst = 0; - } - - return retn + n; } -EXPORT_SYMBOL(__copy_user_zeroing); +EXPORT_SYMBOL(raw_copy_from_user); #define __asm_clear_8x64(to, ret) \ asm volatile ( \ diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index db459612de44..8b0424abc84c 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -9,6 +9,7 @@ config MIPS select HAVE_CONTEXT_TRACKING select HAVE_GENERIC_DMA_COHERENT select HAVE_IDE + select HAVE_IRQ_EXIT_ON_IRQ_STACK select HAVE_OPROFILE select HAVE_PERF_EVENTS select PERF_USE_VMALLOC @@ -1463,7 +1464,7 @@ config CPU_MIPS64_R6 select CPU_SUPPORTS_HIGHMEM select CPU_SUPPORTS_MSA select GENERIC_CSUM - select MIPS_O32_FP64_SUPPORT if MIPS32_O32 + select MIPS_O32_FP64_SUPPORT if 32BIT || MIPS32_O32 help Choose this option to build a kernel for release 6 or later of the MIPS64 architecture. New MIPS processors, starting with the Warrior diff --git a/arch/mips/include/asm/irq.h b/arch/mips/include/asm/irq.h index 15e0fecbc300..ebb9efb02502 100644 --- a/arch/mips/include/asm/irq.h +++ b/arch/mips/include/asm/irq.h @@ -17,6 +17,18 @@ #include <irq.h> +#define IRQ_STACK_SIZE THREAD_SIZE + +extern void *irq_stack[NR_CPUS]; + +static inline bool on_irq_stack(int cpu, unsigned long sp) +{ + unsigned long low = (unsigned long)irq_stack[cpu]; + unsigned long high = low + IRQ_STACK_SIZE; + + return (low <= sp && sp <= high); +} + #ifdef CONFIG_I8259 static inline int irq_canonicalize(int irq) { diff --git a/arch/mips/include/asm/spinlock.h b/arch/mips/include/asm/spinlock.h index 40196bebe849..2365ce0ad8f2 100644 --- a/arch/mips/include/asm/spinlock.h +++ b/arch/mips/include/asm/spinlock.h @@ -112,7 +112,7 @@ static inline void arch_spin_lock(arch_spinlock_t *lock) " andi %[ticket], %[ticket], 0xffff \n" " bne %[ticket], %[my_ticket], 4f \n" " subu %[ticket], %[my_ticket], %[ticket] \n" - "2: \n" + "2: .insn \n" " .subsection 2 \n" "4: andi %[ticket], %[ticket], 0xffff \n" " sll %[ticket], 5 \n" @@ -187,7 +187,7 @@ static inline unsigned int arch_spin_trylock(arch_spinlock_t *lock) " sc %[ticket], %[ticket_ptr] \n" " beqz %[ticket], 1b \n" " li %[ticket], 1 \n" - "2: \n" + "2: .insn \n" " .subsection 2 \n" "3: b 2b \n" " li %[ticket], 0 \n" @@ -367,7 +367,7 @@ static inline int arch_read_trylock(arch_rwlock_t *rw) " .set reorder \n" __WEAK_LLSC_MB " li %2, 1 \n" - "2: \n" + "2: .insn \n" : "=" GCC_OFF_SMALL_ASM() (rw->lock), "=&r" (tmp), "=&r" (ret) : GCC_OFF_SMALL_ASM() (rw->lock) : "memory"); @@ -407,7 +407,7 @@ static inline int arch_write_trylock(arch_rwlock_t *rw) " lui %1, 0x8000 \n" " sc %1, %0 \n" " li %2, 1 \n" - "2: \n" + "2: .insn \n" : "=" GCC_OFF_SMALL_ASM() (rw->lock), "=&r" (tmp), "=&r" (ret) : GCC_OFF_SMALL_ASM() (rw->lock) diff --git a/arch/mips/include/asm/stackframe.h b/arch/mips/include/asm/stackframe.h index a71da576883c..5347f130f536 100644 --- a/arch/mips/include/asm/stackframe.h +++ b/arch/mips/include/asm/stackframe.h @@ -216,12 +216,19 @@ LONG_S $25, PT_R25(sp) LONG_S $28, PT_R28(sp) LONG_S $31, PT_R31(sp) + + /* Set thread_info if we're coming from user mode */ + mfc0 k0, CP0_STATUS + sll k0, 3 /* extract cu0 bit */ + bltz k0, 9f + ori $28, sp, _THREAD_MASK xori $28, _THREAD_MASK #ifdef CONFIG_CPU_CAVIUM_OCTEON .set mips64 pref 0, 0($28) /* Prefetch the current pointer */ #endif +9: .set pop .endm diff --git a/arch/mips/kernel/asm-offsets.c b/arch/mips/kernel/asm-offsets.c index 154e2039ea5e..ec053ce7bb38 100644 --- a/arch/mips/kernel/asm-offsets.c +++ b/arch/mips/kernel/asm-offsets.c @@ -101,6 +101,7 @@ void output_thread_info_defines(void) OFFSET(TI_REGS, thread_info, regs); DEFINE(_THREAD_SIZE, THREAD_SIZE); DEFINE(_THREAD_MASK, THREAD_MASK); + DEFINE(_IRQ_STACK_SIZE, IRQ_STACK_SIZE); BLANK(); } diff --git a/arch/mips/kernel/crash.c b/arch/mips/kernel/crash.c index d434d5d5ae6e..610f0f3bdb34 100644 --- a/arch/mips/kernel/crash.c +++ b/arch/mips/kernel/crash.c @@ -14,12 +14,22 @@ static int crashing_cpu = -1; static cpumask_t cpus_in_crash = CPU_MASK_NONE; #ifdef CONFIG_SMP -static void crash_shutdown_secondary(void *ignore) +static void crash_shutdown_secondary(void *passed_regs) { - struct pt_regs *regs; + struct pt_regs *regs = passed_regs; int cpu = smp_processor_id(); - regs = task_pt_regs(current); + /* + * If we are passed registers, use those. Otherwise get the + * regs from the last interrupt, which should be correct, as + * we are in an interrupt. But if the regs are not there, + * pull them from the top of the stack. They are probably + * wrong, but we need something to keep from crashing again. + */ + if (!regs) + regs = get_irq_regs(); + if (!regs) + regs = task_pt_regs(current); if (!cpu_online(cpu)) return; diff --git a/arch/mips/kernel/elf.c b/arch/mips/kernel/elf.c index 4a4d9e067c89..3afffc30ee12 100644 --- a/arch/mips/kernel/elf.c +++ b/arch/mips/kernel/elf.c @@ -206,7 +206,7 @@ int arch_check_elf(void *_ehdr, bool has_interpreter, else if ((prog_req.fr1 && prog_req.frdefault) || (prog_req.single && !prog_req.frdefault)) /* Make sure 64-bit MIPS III/IV/64R1 will not pick FR1 */ - state->overall_fp_mode = ((current_cpu_data.fpu_id & MIPS_FPIR_F64) && + state->overall_fp_mode = ((raw_current_cpu_data.fpu_id & MIPS_FPIR_F64) && cpu_has_mips_r2_r6) ? FP_FR1 : FP_FR0; else if (prog_req.fr1) diff --git a/arch/mips/kernel/genex.S b/arch/mips/kernel/genex.S index baa7b6fc0a60..619e30e2c4f0 100644 --- a/arch/mips/kernel/genex.S +++ b/arch/mips/kernel/genex.S @@ -188,9 +188,44 @@ NESTED(handle_int, PT_SIZE, sp) LONG_L s0, TI_REGS($28) LONG_S sp, TI_REGS($28) - PTR_LA ra, ret_from_irq - PTR_LA v0, plat_irq_dispatch - jr v0 + + /* + * SAVE_ALL ensures we are using a valid kernel stack for the thread. + * Check if we are already using the IRQ stack. + */ + move s1, sp # Preserve the sp + + /* Get IRQ stack for this CPU */ + ASM_CPUID_MFC0 k0, ASM_SMP_CPUID_REG +#if defined(CONFIG_32BIT) || defined(KBUILD_64BIT_SYM32) + lui k1, %hi(irq_stack) +#else + lui k1, %highest(irq_stack) + daddiu k1, %higher(irq_stack) + dsll k1, 16 + daddiu k1, %hi(irq_stack) + dsll k1, 16 +#endif + LONG_SRL k0, SMP_CPUID_PTRSHIFT + LONG_ADDU k1, k0 + LONG_L t0, %lo(irq_stack)(k1) + + # Check if already on IRQ stack + PTR_LI t1, ~(_THREAD_SIZE-1) + and t1, t1, sp + beq t0, t1, 2f + + /* Switch to IRQ stack */ + li t1, _IRQ_STACK_SIZE + PTR_ADD sp, t0, t1 + +2: + jal plat_irq_dispatch + + /* Restore sp */ + move sp, s1 + + j ret_from_irq #ifdef CONFIG_CPU_MICROMIPS nop #endif @@ -263,8 +298,44 @@ NESTED(except_vec_vi_handler, 0, sp) LONG_L s0, TI_REGS($28) LONG_S sp, TI_REGS($28) - PTR_LA ra, ret_from_irq - jr v0 + + /* + * SAVE_ALL ensures we are using a valid kernel stack for the thread. + * Check if we are already using the IRQ stack. + */ + move s1, sp # Preserve the sp + + /* Get IRQ stack for this CPU */ + ASM_CPUID_MFC0 k0, ASM_SMP_CPUID_REG +#if defined(CONFIG_32BIT) || defined(KBUILD_64BIT_SYM32) + lui k1, %hi(irq_stack) +#else + lui k1, %highest(irq_stack) + daddiu k1, %higher(irq_stack) + dsll k1, 16 + daddiu k1, %hi(irq_stack) + dsll k1, 16 +#endif + LONG_SRL k0, SMP_CPUID_PTRSHIFT + LONG_ADDU k1, k0 + LONG_L t0, %lo(irq_stack)(k1) + + # Check if already on IRQ stack + PTR_LI t1, ~(_THREAD_SIZE-1) + and t1, t1, sp + beq t0, t1, 2f + + /* Switch to IRQ stack */ + li t1, _IRQ_STACK_SIZE + PTR_ADD sp, t0, t1 + +2: + jalr v0 + + /* Restore sp */ + move sp, s1 + + j ret_from_irq END(except_vec_vi_handler) /* diff --git a/arch/mips/kernel/irq.c b/arch/mips/kernel/irq.c index 8eb5af805964..dc1180a8bfa1 100644 --- a/arch/mips/kernel/irq.c +++ b/arch/mips/kernel/irq.c @@ -25,6 +25,8 @@ #include <linux/atomic.h> #include <asm/uaccess.h> +void *irq_stack[NR_CPUS]; + /* * 'what should we do if we get a hw irq event on an illegal vector'. * each architecture has to answer this themselves. @@ -55,6 +57,15 @@ void __init init_IRQ(void) irq_set_noprobe(i); arch_init_irq(); + + for_each_possible_cpu(i) { + int irq_pages = IRQ_STACK_SIZE / PAGE_SIZE; + void *s = (void *)__get_free_pages(GFP_KERNEL, irq_pages); + + irq_stack[i] = s; + pr_debug("CPU%d IRQ stack at 0x%p - 0x%p\n", i, + irq_stack[i], irq_stack[i] + IRQ_STACK_SIZE); + } } #ifdef CONFIG_DEBUG_STACKOVERFLOW diff --git a/arch/mips/kernel/kgdb.c b/arch/mips/kernel/kgdb.c index de63d36af895..732d6171ac6a 100644 --- a/arch/mips/kernel/kgdb.c +++ b/arch/mips/kernel/kgdb.c @@ -244,9 +244,6 @@ static int compute_signal(int tt) void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) { int reg; - struct thread_info *ti = task_thread_info(p); - unsigned long ksp = (unsigned long)ti + THREAD_SIZE - 32; - struct pt_regs *regs = (struct pt_regs *)ksp - 1; #if (KGDB_GDB_REG_SIZE == 32) u32 *ptr = (u32 *)gdb_regs; #else @@ -254,25 +251,46 @@ void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) #endif for (reg = 0; reg < 16; reg++) - *(ptr++) = regs->regs[reg]; + *(ptr++) = 0; /* S0 - S7 */ - for (reg = 16; reg < 24; reg++) - *(ptr++) = regs->regs[reg]; + *(ptr++) = p->thread.reg16; + *(ptr++) = p->thread.reg17; + *(ptr++) = p->thread.reg18; + *(ptr++) = p->thread.reg19; + *(ptr++) = p->thread.reg20; + *(ptr++) = p->thread.reg21; + *(ptr++) = p->thread.reg22; + *(ptr++) = p->thread.reg23; for (reg = 24; reg < 28; reg++) *(ptr++) = 0; /* GP, SP, FP, RA */ - for (reg = 28; reg < 32; reg++) - *(ptr++) = regs->regs[reg]; - - *(ptr++) = regs->cp0_status; - *(ptr++) = regs->lo; - *(ptr++) = regs->hi; - *(ptr++) = regs->cp0_badvaddr; - *(ptr++) = regs->cp0_cause; - *(ptr++) = regs->cp0_epc; + *(ptr++) = (long)p; + *(ptr++) = p->thread.reg29; + *(ptr++) = p->thread.reg30; + *(ptr++) = p->thread.reg31; + + *(ptr++) = p->thread.cp0_status; + + /* lo, hi */ + *(ptr++) = 0; + *(ptr++) = 0; + + /* + * BadVAddr, Cause + * Ideally these would come from the last exception frame up the stack + * but that requires unwinding, otherwise we can't know much for sure. + */ + *(ptr++) = 0; + *(ptr++) = 0; + + /* + * PC + * use return address (RA), i.e. the moment after return from resume() + */ + *(ptr++) = p->thread.reg31; } void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc) diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index fc537d1b649d..8c26ecac930d 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c @@ -32,6 +32,7 @@ #include <asm/cpu.h> #include <asm/dsp.h> #include <asm/fpu.h> +#include <asm/irq.h> #include <asm/msa.h> #include <asm/pgtable.h> #include <asm/mipsregs.h> @@ -552,7 +553,19 @@ EXPORT_SYMBOL(unwind_stack_by_address); unsigned long unwind_stack(struct task_struct *task, unsigned long *sp, unsigned long pc, unsigned long *ra) { - unsigned long stack_page = (unsigned long)task_stack_page(task); + unsigned long stack_page = 0; + int cpu; + + for_each_possible_cpu(cpu) { + if (on_irq_stack(cpu, *sp)) { + stack_page = (unsigned long)irq_stack[cpu]; + break; + } + } + + if (!stack_page) + stack_page = (unsigned long)task_stack_page(task); + return unwind_stack_by_address(stack_page, sp, pc, ra); } #endif diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index 74d581569778..c95bf18260f8 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -485,7 +485,8 @@ static int fpr_set(struct task_struct *target, &target->thread.fpu, 0, sizeof(elf_fpregset_t)); - for (i = 0; i < NUM_FPU_REGS; i++) { + BUILD_BUG_ON(sizeof(fpr_val) != sizeof(elf_fpreg_t)); + for (i = 0; i < NUM_FPU_REGS && count >= sizeof(elf_fpreg_t); i++) { err = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &fpr_val, i * sizeof(elf_fpreg_t), (i + 1) * sizeof(elf_fpreg_t)); diff --git a/arch/mips/lantiq/xway/sysctrl.c b/arch/mips/lantiq/xway/sysctrl.c index 3e390a4e3897..daf580ce5ca2 100644 --- a/arch/mips/lantiq/xway/sysctrl.c +++ b/arch/mips/lantiq/xway/sysctrl.c @@ -467,7 +467,7 @@ void __init ltq_soc_init(void) if (!np_xbar) panic("Failed to load xbar nodes from devicetree"); - if (of_address_to_resource(np_pmu, 0, &res_xbar)) + if (of_address_to_resource(np_xbar, 0, &res_xbar)) panic("Failed to get xbar resources"); if (request_mem_region(res_xbar.start, resource_size(&res_xbar), res_xbar.name) < 0) diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c index 29f73e00253d..63b7d6f82d24 100644 --- a/arch/mips/mm/tlbex.c +++ b/arch/mips/mm/tlbex.c @@ -757,7 +757,8 @@ static void build_huge_update_entries(u32 **p, unsigned int pte, static void build_huge_handler_tail(u32 **p, struct uasm_reloc **r, struct uasm_label **l, unsigned int pte, - unsigned int ptr) + unsigned int ptr, + unsigned int flush) { #ifdef CONFIG_SMP UASM_i_SC(p, pte, 0, ptr); @@ -766,6 +767,22 @@ static void build_huge_handler_tail(u32 **p, struct uasm_reloc **r, #else UASM_i_SW(p, pte, 0, ptr); #endif + if (cpu_has_ftlb && flush) { + BUG_ON(!cpu_has_tlbinv); + + UASM_i_MFC0(p, ptr, C0_ENTRYHI); + uasm_i_ori(p, ptr, ptr, MIPS_ENTRYHI_EHINV); + UASM_i_MTC0(p, ptr, C0_ENTRYHI); + build_tlb_write_entry(p, l, r, tlb_indexed); + + uasm_i_xori(p, ptr, ptr, MIPS_ENTRYHI_EHINV); + UASM_i_MTC0(p, ptr, C0_ENTRYHI); + build_huge_update_entries(p, pte, ptr); + build_huge_tlb_write_entry(p, l, r, pte, tlb_random, 0); + + return; + } + build_huge_update_entries(p, pte, ptr); build_huge_tlb_write_entry(p, l, r, pte, tlb_indexed, 0); } @@ -2082,7 +2099,7 @@ static void build_r4000_tlb_load_handler(void) uasm_l_tlbl_goaround2(&l, p); } uasm_i_ori(&p, wr.r1, wr.r1, (_PAGE_ACCESSED | _PAGE_VALID)); - build_huge_handler_tail(&p, &r, &l, wr.r1, wr.r2); + build_huge_handler_tail(&p, &r, &l, wr.r1, wr.r2, 1); #endif uasm_l_nopage_tlbl(&l, p); @@ -2137,7 +2154,7 @@ static void build_r4000_tlb_store_handler(void) build_tlb_probe_entry(&p); uasm_i_ori(&p, wr.r1, wr.r1, _PAGE_ACCESSED | _PAGE_MODIFIED | _PAGE_VALID | _PAGE_DIRTY); - build_huge_handler_tail(&p, &r, &l, wr.r1, wr.r2); + build_huge_handler_tail(&p, &r, &l, wr.r1, wr.r2, 1); #endif uasm_l_nopage_tlbs(&l, p); @@ -2193,7 +2210,7 @@ static void build_r4000_tlb_modify_handler(void) build_tlb_probe_entry(&p); uasm_i_ori(&p, wr.r1, wr.r1, _PAGE_ACCESSED | _PAGE_MODIFIED | _PAGE_VALID | _PAGE_DIRTY); - build_huge_handler_tail(&p, &r, &l, wr.r1, wr.r2); + build_huge_handler_tail(&p, &r, &l, wr.r1, wr.r2, 0); #endif uasm_l_nopage_tlbm(&l, p); diff --git a/arch/mips/ralink/rt3883.c b/arch/mips/ralink/rt3883.c index f42834c7f007..3c575093f8f1 100644 --- a/arch/mips/ralink/rt3883.c +++ b/arch/mips/ralink/rt3883.c @@ -36,7 +36,7 @@ static struct rt2880_pmx_func uartlite_func[] = { FUNC("uartlite", 0, 15, 2) }; static struct rt2880_pmx_func jtag_func[] = { FUNC("jtag", 0, 17, 5) }; static struct rt2880_pmx_func mdio_func[] = { FUNC("mdio", 0, 22, 2) }; static struct rt2880_pmx_func lna_a_func[] = { FUNC("lna a", 0, 32, 3) }; -static struct rt2880_pmx_func lna_g_func[] = { FUNC("lna a", 0, 35, 3) }; +static struct rt2880_pmx_func lna_g_func[] = { FUNC("lna g", 0, 35, 3) }; static struct rt2880_pmx_func pci_func[] = { FUNC("pci-dev", 0, 40, 32), FUNC("pci-host2", 1, 40, 32), @@ -44,7 +44,7 @@ static struct rt2880_pmx_func pci_func[] = { FUNC("pci-fnc", 3, 40, 32) }; static struct rt2880_pmx_func ge1_func[] = { FUNC("ge1", 0, 72, 12) }; -static struct rt2880_pmx_func ge2_func[] = { FUNC("ge1", 0, 84, 12) }; +static struct rt2880_pmx_func ge2_func[] = { FUNC("ge2", 0, 84, 12) }; static struct rt2880_pmx_group rt3883_pinmux_data[] = { GRP("i2c", i2c_func, 1, RT3883_GPIO_MODE_I2C), diff --git a/arch/nios2/kernel/prom.c b/arch/nios2/kernel/prom.c index 718dd197909f..de73beb36910 100644 --- a/arch/nios2/kernel/prom.c +++ b/arch/nios2/kernel/prom.c @@ -48,6 +48,13 @@ void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align) return alloc_bootmem_align(size, align); } +int __init early_init_dt_reserve_memory_arch(phys_addr_t base, phys_addr_t size, + bool nomap) +{ + reserve_bootmem(base, size, BOOTMEM_DEFAULT); + return 0; +} + void __init early_init_devtree(void *params) { __be32 *dtb = (u32 *)__dtb_start; diff --git a/arch/nios2/kernel/setup.c b/arch/nios2/kernel/setup.c index a4ff86d58d5c..6c4e351a7930 100644 --- a/arch/nios2/kernel/setup.c +++ b/arch/nios2/kernel/setup.c @@ -195,6 +195,9 @@ void __init setup_arch(char **cmdline_p) } #endif /* CONFIG_BLK_DEV_INITRD */ + early_init_fdt_reserve_self(); + early_init_fdt_scan_reserved_mem(); + unflatten_and_copy_device_tree(); setup_cpuinfo(); diff --git a/arch/powerpc/boot/zImage.lds.S b/arch/powerpc/boot/zImage.lds.S index 861e72109df2..f080abfc2f83 100644 --- a/arch/powerpc/boot/zImage.lds.S +++ b/arch/powerpc/boot/zImage.lds.S @@ -68,6 +68,7 @@ SECTIONS } #ifdef CONFIG_PPC64_BOOT_WRAPPER + . = ALIGN(256); .got : { __toc_start = .; diff --git a/arch/powerpc/kernel/align.c b/arch/powerpc/kernel/align.c index 86150fbb42c3..91e5c1758b5c 100644 --- a/arch/powerpc/kernel/align.c +++ b/arch/powerpc/kernel/align.c @@ -808,14 +808,25 @@ int fix_alignment(struct pt_regs *regs) nb = aligninfo[instr].len; flags = aligninfo[instr].flags; - /* ldbrx/stdbrx overlap lfs/stfs in the DSISR unfortunately */ - if (IS_XFORM(instruction) && ((instruction >> 1) & 0x3ff) == 532) { - nb = 8; - flags = LD+SW; - } else if (IS_XFORM(instruction) && - ((instruction >> 1) & 0x3ff) == 660) { - nb = 8; - flags = ST+SW; + /* + * Handle some cases which give overlaps in the DSISR values. + */ + if (IS_XFORM(instruction)) { + switch (get_xop(instruction)) { + case 532: /* ldbrx */ + nb = 8; + flags = LD+SW; + break; + case 660: /* stdbrx */ + nb = 8; + flags = ST+SW; + break; + case 20: /* lwarx */ + case 84: /* ldarx */ + case 116: /* lharx */ + case 276: /* lqarx */ + return 0; /* not emulated ever */ + } } /* Byteswap little endian loads and stores */ diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index edba294620db..f6fd0332c3a2 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -716,7 +716,7 @@ resume_kernel: addi r8,r1,INT_FRAME_SIZE /* Get the kprobed function entry */ - lwz r3,GPR1(r1) + ld r3,GPR1(r1) subi r3,r3,INT_FRAME_SIZE /* dst: Allocate a trampoline exception frame */ mr r4,r1 /* src: current exception frame */ mr r1,r3 /* Reroute the trampoline frame to r1 */ @@ -730,8 +730,8 @@ resume_kernel: addi r6,r6,8 bdnz 2b - /* Do real store operation to complete stwu */ - lwz r5,GPR1(r1) + /* Do real store operation to complete stdu */ + ld r5,GPR1(r1) std r8,0(r5) /* Clear _TIF_EMULATE_STACK_STORE flag */ diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c index 5c03a6a9b054..a20823210ac0 100644 --- a/arch/powerpc/kernel/setup_64.c +++ b/arch/powerpc/kernel/setup_64.c @@ -220,6 +220,15 @@ static void cpu_ready_for_interrupts(void) unsigned long lpcr = mfspr(SPRN_LPCR); mtspr(SPRN_LPCR, lpcr | LPCR_AIL_3); } + + /* + * Fixup HFSCR:TM based on CPU features. The bit is set by our + * early asm init because at that point we haven't updated our + * CPU features from firmware and device-tree. Here we have, + * so let's do it. + */ + if (cpu_has_feature(CPU_FTR_HVMODE) && !cpu_has_feature(CPU_FTR_TM_COMP)) + mtspr(SPRN_HFSCR, mfspr(SPRN_HFSCR) & ~HFSCR_TM); } /* diff --git a/arch/powerpc/kvm/emulate.c b/arch/powerpc/kvm/emulate.c index 5cc2e7af3a7b..b379146de55b 100644 --- a/arch/powerpc/kvm/emulate.c +++ b/arch/powerpc/kvm/emulate.c @@ -302,7 +302,6 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu) advance = 0; printk(KERN_ERR "Couldn't emulate instruction 0x%08x " "(op %d xop %d)\n", inst, get_op(inst), get_xop(inst)); - kvmppc_core_queue_program(vcpu, 0); } } diff --git a/arch/powerpc/mm/hash_native_64.c b/arch/powerpc/mm/hash_native_64.c index c8822af10a58..19d9b2d2d212 100644 --- a/arch/powerpc/mm/hash_native_64.c +++ b/arch/powerpc/mm/hash_native_64.c @@ -645,6 +645,10 @@ static void native_flush_hash_range(unsigned long number, int local) unsigned long psize = batch->psize; int ssize = batch->ssize; int i; + unsigned int use_local; + + use_local = local && mmu_has_feature(MMU_FTR_TLBIEL) && + mmu_psize_defs[psize].tlbiel && !cxl_ctx_in_use(); local_irq_save(flags); @@ -671,8 +675,7 @@ static void native_flush_hash_range(unsigned long number, int local) } pte_iterate_hashed_end(); } - if (mmu_has_feature(MMU_FTR_TLBIEL) && - mmu_psize_defs[psize].tlbiel && local) { + if (use_local) { asm volatile("ptesync":::"memory"); for (i = 0; i < number; i++) { vpn = batch->vpn[i]; diff --git a/arch/s390/boot/compressed/misc.c b/arch/s390/boot/compressed/misc.c index 4da604ebf6fd..ca15613eaaa4 100644 --- a/arch/s390/boot/compressed/misc.c +++ b/arch/s390/boot/compressed/misc.c @@ -141,31 +141,34 @@ static void check_ipl_parmblock(void *start, unsigned long size) unsigned long decompress_kernel(void) { - unsigned long output_addr; - unsigned char *output; + void *output, *kernel_end; - output_addr = ((unsigned long) &_end + HEAP_SIZE + 4095UL) & -4096UL; - check_ipl_parmblock((void *) 0, output_addr + SZ__bss_start); - memset(&_bss, 0, &_ebss - &_bss); - free_mem_ptr = (unsigned long)&_end; - free_mem_end_ptr = free_mem_ptr + HEAP_SIZE; - output = (unsigned char *) output_addr; + output = (void *) ALIGN((unsigned long) &_end + HEAP_SIZE, PAGE_SIZE); + kernel_end = output + SZ__bss_start; + check_ipl_parmblock((void *) 0, (unsigned long) kernel_end); #ifdef CONFIG_BLK_DEV_INITRD /* * Move the initrd right behind the end of the decompressed - * kernel image. + * kernel image. This also prevents initrd corruption caused by + * bss clearing since kernel_end will always be located behind the + * current bss section.. */ - if (INITRD_START && INITRD_SIZE && - INITRD_START < (unsigned long) output + SZ__bss_start) { - check_ipl_parmblock(output + SZ__bss_start, - INITRD_START + INITRD_SIZE); - memmove(output + SZ__bss_start, - (void *) INITRD_START, INITRD_SIZE); - INITRD_START = (unsigned long) output + SZ__bss_start; + if (INITRD_START && INITRD_SIZE && kernel_end > (void *) INITRD_START) { + check_ipl_parmblock(kernel_end, INITRD_SIZE); + memmove(kernel_end, (void *) INITRD_START, INITRD_SIZE); + INITRD_START = (unsigned long) kernel_end; } #endif + /* + * Clear bss section. free_mem_ptr and free_mem_end_ptr need to be + * initialized afterwards since they reside in bss. + */ + memset(&_bss, 0, &_ebss - &_bss); + free_mem_ptr = (unsigned long) &_end; + free_mem_end_ptr = free_mem_ptr + HEAP_SIZE; + puts("Uncompressing Linux... "); __decompress(input_data, input_len, NULL, NULL, output, 0, NULL, error); puts("Ok, booting the kernel.\n"); diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index 024f85f947ae..e2c0e4eab037 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h @@ -829,6 +829,8 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr, { pgste_t pgste; + if (pte_present(entry)) + pte_val(entry) &= ~_PAGE_UNUSED; if (mm_has_pgste(mm)) { pgste = pgste_get_lock(ptep); pgste_val(pgste) &= ~_PGSTE_GPS_ZERO; diff --git a/arch/s390/include/asm/uaccess.h b/arch/s390/include/asm/uaccess.h index 5c7381c5ad7f..c8d837f0fbbc 100644 --- a/arch/s390/include/asm/uaccess.h +++ b/arch/s390/include/asm/uaccess.h @@ -150,7 +150,7 @@ unsigned long __must_check __copy_to_user(void __user *to, const void *from, " jg 2b\n" \ ".popsection\n" \ EX_TABLE(0b,3b) EX_TABLE(1b,3b) \ - : "=d" (__rc), "=Q" (*(to)) \ + : "=d" (__rc), "+Q" (*(to)) \ : "d" (size), "Q" (*(from)), \ "d" (__reg0), "K" (-EFAULT) \ : "cc"); \ diff --git a/arch/s390/pci/pci_dma.c b/arch/s390/pci/pci_dma.c index 3a40f718baef..4004e03267cd 100644 --- a/arch/s390/pci/pci_dma.c +++ b/arch/s390/pci/pci_dma.c @@ -455,7 +455,7 @@ int zpci_dma_init_device(struct zpci_dev *zdev) zdev->dma_table = dma_alloc_cpu_table(); if (!zdev->dma_table) { rc = -ENOMEM; - goto out_clean; + goto out; } /* @@ -475,18 +475,22 @@ int zpci_dma_init_device(struct zpci_dev *zdev) zdev->iommu_bitmap = vzalloc(zdev->iommu_pages / 8); if (!zdev->iommu_bitmap) { rc = -ENOMEM; - goto out_reg; + goto free_dma_table; } rc = zpci_register_ioat(zdev, 0, zdev->start_dma, zdev->end_dma, (u64) zdev->dma_table); if (rc) - goto out_reg; - return 0; + goto free_bitmap; -out_reg: + return 0; +free_bitmap: + vfree(zdev->iommu_bitmap); + zdev->iommu_bitmap = NULL; +free_dma_table: dma_free_cpu_table(zdev->dma_table); -out_clean: + zdev->dma_table = NULL; +out: return rc; } diff --git a/arch/sparc/include/asm/pgtable_64.h b/arch/sparc/include/asm/pgtable_64.h index 408b715c95a5..9d81579f3d54 100644 --- a/arch/sparc/include/asm/pgtable_64.h +++ b/arch/sparc/include/asm/pgtable_64.h @@ -668,26 +668,27 @@ static inline unsigned long pmd_pfn(pmd_t pmd) return pte_pfn(pte); } -#ifdef CONFIG_TRANSPARENT_HUGEPAGE -static inline unsigned long pmd_dirty(pmd_t pmd) +#define __HAVE_ARCH_PMD_WRITE +static inline unsigned long pmd_write(pmd_t pmd) { pte_t pte = __pte(pmd_val(pmd)); - return pte_dirty(pte); + return pte_write(pte); } -static inline unsigned long pmd_young(pmd_t pmd) +#ifdef CONFIG_TRANSPARENT_HUGEPAGE +static inline unsigned long pmd_dirty(pmd_t pmd) { pte_t pte = __pte(pmd_val(pmd)); - return pte_young(pte); + return pte_dirty(pte); } -static inline unsigned long pmd_write(pmd_t pmd) +static inline unsigned long pmd_young(pmd_t pmd) { pte_t pte = __pte(pmd_val(pmd)); - return pte_write(pte); + return pte_young(pte); } static inline unsigned long pmd_trans_huge(pmd_t pmd) diff --git a/arch/sparc/kernel/ptrace_64.c b/arch/sparc/kernel/ptrace_64.c index 9ddc4928a089..c1566170964f 100644 --- a/arch/sparc/kernel/ptrace_64.c +++ b/arch/sparc/kernel/ptrace_64.c @@ -311,7 +311,7 @@ static int genregs64_set(struct task_struct *target, } if (!ret) { - unsigned long y; + unsigned long y = regs->y; ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &y, diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c index 3d3414c14792..965655afdbb6 100644 --- a/arch/sparc/mm/init_64.c +++ b/arch/sparc/mm/init_64.c @@ -1493,7 +1493,7 @@ bool kern_addr_valid(unsigned long addr) if ((long)addr < 0L) { unsigned long pa = __pa(addr); - if ((addr >> max_phys_bits) != 0UL) + if ((pa >> max_phys_bits) != 0UL) return false; return pfn_valid(pa >> PAGE_SHIFT); diff --git a/arch/x86/crypto/ghash-clmulni-intel_glue.c b/arch/x86/crypto/ghash-clmulni-intel_glue.c index 440df0c7a2ee..a69321a77783 100644 --- a/arch/x86/crypto/ghash-clmulni-intel_glue.c +++ b/arch/x86/crypto/ghash-clmulni-intel_glue.c @@ -219,6 +219,29 @@ static int ghash_async_final(struct ahash_request *req) } } +static int ghash_async_import(struct ahash_request *req, const void *in) +{ + struct ahash_request *cryptd_req = ahash_request_ctx(req); + struct shash_desc *desc = cryptd_shash_desc(cryptd_req); + struct ghash_desc_ctx *dctx = shash_desc_ctx(desc); + + ghash_async_init(req); + memcpy(dctx, in, sizeof(*dctx)); + return 0; + +} + +static int ghash_async_export(struct ahash_request *req, void *out) +{ + struct ahash_request *cryptd_req = ahash_request_ctx(req); + struct shash_desc *desc = cryptd_shash_desc(cryptd_req); + struct ghash_desc_ctx *dctx = shash_desc_ctx(desc); + + memcpy(out, dctx, sizeof(*dctx)); + return 0; + +} + static int ghash_async_digest(struct ahash_request *req) { struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); @@ -288,8 +311,11 @@ static struct ahash_alg ghash_async_alg = { .final = ghash_async_final, .setkey = ghash_async_setkey, .digest = ghash_async_digest, + .export = ghash_async_export, + .import = ghash_async_import, .halg = { .digestsize = GHASH_DIGEST_SIZE, + .statesize = sizeof(struct ghash_desc_ctx), .base = { .cra_name = "ghash", .cra_driver_name = "ghash-clmulni", diff --git a/arch/x86/entry/vdso/Makefile b/arch/x86/entry/vdso/Makefile index 265c0ed68118..7af017a8958f 100644 --- a/arch/x86/entry/vdso/Makefile +++ b/arch/x86/entry/vdso/Makefile @@ -187,10 +187,10 @@ vdso_img_insttargets := $(vdso_img_sodbg:%.dbg=install_%) $(MODLIB)/vdso: FORCE @mkdir -p $(MODLIB)/vdso -$(vdso_img_insttargets): install_%: $(obj)/%.dbg $(MODLIB)/vdso FORCE +$(vdso_img_insttargets): install_%: $(obj)/%.dbg $(MODLIB)/vdso $(call cmd,vdso_install) PHONY += vdso_install $(vdso_img_insttargets) -vdso_install: $(vdso_img_insttargets) FORCE +vdso_install: $(vdso_img_insttargets) clean-files := vdso32.so vdso32.so.dbg vdso64* vdso-image-*.c vdsox32.so* diff --git a/arch/x86/entry/vdso/vdso32-setup.c b/arch/x86/entry/vdso/vdso32-setup.c index 08a317a9ae4b..a7508d7e20b7 100644 --- a/arch/x86/entry/vdso/vdso32-setup.c +++ b/arch/x86/entry/vdso/vdso32-setup.c @@ -31,8 +31,10 @@ static int __init vdso32_setup(char *s) { vdso32_enabled = simple_strtoul(s, NULL, 0); - if (vdso32_enabled > 1) + if (vdso32_enabled > 1) { pr_warn("vdso32 values other than 0 and 1 are no longer allowed; vdso disabled\n"); + vdso32_enabled = 0; + } return 1; } @@ -63,13 +65,18 @@ subsys_initcall(sysenter_setup); /* Register vsyscall32 into the ABI table */ #include <linux/sysctl.h> +static const int zero; +static const int one = 1; + static struct ctl_table abi_table2[] = { { .procname = "vsyscall32", .data = &vdso32_enabled, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec + .proc_handler = proc_dointvec_minmax, + .extra1 = (int *)&zero, + .extra2 = (int *)&one, }, {} }; diff --git a/arch/x86/include/asm/elf.h b/arch/x86/include/asm/elf.h index 1514753fd435..d262f985bbc8 100644 --- a/arch/x86/include/asm/elf.h +++ b/arch/x86/include/asm/elf.h @@ -278,7 +278,7 @@ struct task_struct; #define ARCH_DLINFO_IA32 \ do { \ - if (vdso32_enabled) { \ + if (VDSO_CURRENT_BASE) { \ NEW_AUX_ENT(AT_SYSINFO, VDSO_ENTRY); \ NEW_AUX_ENT(AT_SYSINFO_EHDR, VDSO_CURRENT_BASE); \ } \ diff --git a/arch/x86/include/asm/pmem.h b/arch/x86/include/asm/pmem.h index d8ce3ec816ab..bd8ce6bcdfc9 100644 --- a/arch/x86/include/asm/pmem.h +++ b/arch/x86/include/asm/pmem.h @@ -72,8 +72,8 @@ static inline void arch_wmb_pmem(void) * @size: number of bytes to write back * * Write back a cache range using the CLWB (cache line write back) - * instruction. This function requires explicit ordering with an - * arch_wmb_pmem() call. This API is internal to the x86 PMEM implementation. + * instruction. Note that @size is internally rounded up to be cache + * line size aligned. */ static inline void __arch_wb_cache_pmem(void *vaddr, size_t size) { @@ -87,15 +87,6 @@ static inline void __arch_wb_cache_pmem(void *vaddr, size_t size) clwb(p); } -/* - * copy_from_iter_nocache() on x86 only uses non-temporal stores for iovec - * iterators, so for other types (bvec & kvec) we must do a cache write-back. - */ -static inline bool __iter_needs_pmem_wb(struct iov_iter *i) -{ - return iter_is_iovec(i) == false; -} - /** * arch_copy_from_iter_pmem - copy data from an iterator to PMEM * @addr: PMEM destination address @@ -114,8 +105,36 @@ static inline size_t arch_copy_from_iter_pmem(void __pmem *addr, size_t bytes, /* TODO: skip the write-back by always using non-temporal stores */ len = copy_from_iter_nocache(vaddr, bytes, i); - if (__iter_needs_pmem_wb(i)) - __arch_wb_cache_pmem(vaddr, bytes); + /* + * In the iovec case on x86_64 copy_from_iter_nocache() uses + * non-temporal stores for the bulk of the transfer, but we need + * to manually flush if the transfer is unaligned. A cached + * memory copy is used when destination or size is not naturally + * aligned. That is: + * - Require 8-byte alignment when size is 8 bytes or larger. + * - Require 4-byte alignment when size is 4 bytes. + * + * In the non-iovec case the entire destination needs to be + * flushed. + */ + if (iter_is_iovec(i)) { + unsigned long flushed, dest = (unsigned long) addr; + + if (bytes < 8) { + if (!IS_ALIGNED(dest, 4) || (bytes != 4)) + __arch_wb_cache_pmem(addr, 1); + } else { + if (!IS_ALIGNED(dest, 8)) { + dest = ALIGN(dest, boot_cpu_data.x86_clflush_size); + __arch_wb_cache_pmem(addr, 1); + } + + flushed = dest - (unsigned long) addr; + if (bytes > flushed && !IS_ALIGNED(bytes - flushed, 8)) + __arch_wb_cache_pmem(addr + bytes - 1, 1); + } + } else + __arch_wb_cache_pmem(addr, bytes); return len; } diff --git a/arch/x86/kernel/cpu/mcheck/mce_amd.c b/arch/x86/kernel/cpu/mcheck/mce_amd.c index e99b15077e94..62aca448726a 100644 --- a/arch/x86/kernel/cpu/mcheck/mce_amd.c +++ b/arch/x86/kernel/cpu/mcheck/mce_amd.c @@ -53,7 +53,7 @@ static const char * const th_names[] = { "load_store", "insn_fetch", "combined_unit", - "", + "decode_unit", "northbridge", "execution_unit", }; diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c index cfc4a966e2b9..83b5f7a323a9 100644 --- a/arch/x86/kernel/cpu/mshyperv.c +++ b/arch/x86/kernel/cpu/mshyperv.c @@ -30,6 +30,7 @@ #include <asm/apic.h> #include <asm/timer.h> #include <asm/reboot.h> +#include <asm/nmi.h> struct ms_hyperv_info ms_hyperv; EXPORT_SYMBOL_GPL(ms_hyperv); @@ -157,6 +158,26 @@ static unsigned char hv_get_nmi_reason(void) return 0; } +#ifdef CONFIG_X86_LOCAL_APIC +/* + * Prior to WS2016 Debug-VM sends NMIs to all CPUs which makes + * it dificult to process CHANNELMSG_UNLOAD in case of crash. Handle + * unknown NMI on the first CPU which gets it. + */ +static int hv_nmi_unknown(unsigned int val, struct pt_regs *regs) +{ + static atomic_t nmi_cpu = ATOMIC_INIT(-1); + + if (!unknown_nmi_panic) + return NMI_DONE; + + if (atomic_cmpxchg(&nmi_cpu, -1, raw_smp_processor_id()) != -1) + return NMI_HANDLED; + + return NMI_DONE; +} +#endif + static void __init ms_hyperv_init_platform(void) { /* @@ -182,6 +203,9 @@ static void __init ms_hyperv_init_platform(void) printk(KERN_INFO "HyperV: LAPIC Timer Frequency: %#x\n", lapic_timer_frequency); } + + register_nmi_handler(NMI_UNKNOWN, hv_nmi_unknown, NMI_FLAG_FIRST, + "hv_nmi_unknown"); #endif if (ms_hyperv.features & HV_X64_MSR_TIME_REF_COUNT_AVAILABLE) diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 1a8256dd6729..5b2f2306fbcc 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -1996,8 +1996,8 @@ static int x86_pmu_event_init(struct perf_event *event) static void refresh_pce(void *ignored) { - if (current->mm) - load_mm_cr4(current->mm); + if (current->active_mm) + load_mm_cr4(current->active_mm); } static void x86_pmu_event_mapped(struct perf_event *event) diff --git a/arch/x86/kernel/cpu/perf_event_intel_lbr.c b/arch/x86/kernel/cpu/perf_event_intel_lbr.c index 659f01e165d5..8900400230c6 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_lbr.c +++ b/arch/x86/kernel/cpu/perf_event_intel_lbr.c @@ -410,6 +410,9 @@ static void intel_pmu_lbr_read_32(struct cpu_hw_events *cpuc) cpuc->lbr_entries[i].to = msr_lastbranch.to; cpuc->lbr_entries[i].mispred = 0; cpuc->lbr_entries[i].predicted = 0; + cpuc->lbr_entries[i].in_tx = 0; + cpuc->lbr_entries[i].abort = 0; + cpuc->lbr_entries[i].cycles = 0; cpuc->lbr_entries[i].reserved = 0; } cpuc->lbr_stack.nr = i; diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c index eb6bd34582c6..1b96bfe09d42 100644 --- a/arch/x86/kernel/ftrace.c +++ b/arch/x86/kernel/ftrace.c @@ -977,6 +977,18 @@ void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent, unsigned long return_hooker = (unsigned long) &return_to_handler; + /* + * When resuming from suspend-to-ram, this function can be indirectly + * called from early CPU startup code while the CPU is in real mode, + * which would fail miserably. Make sure the stack pointer is a + * virtual address. + * + * This check isn't as accurate as virt_addr_valid(), but it should be + * good enough for this purpose, and it's fast. + */ + if (unlikely((long)__builtin_frame_address(0) >= 0)) + return; + if (unlikely(ftrace_graph_is_dead())) return; diff --git a/arch/x86/kernel/head64.c b/arch/x86/kernel/head64.c index f129a9af6357..b6b0077da1af 100644 --- a/arch/x86/kernel/head64.c +++ b/arch/x86/kernel/head64.c @@ -4,6 +4,7 @@ * Copyright (C) 2000 Andrea Arcangeli <andrea@suse.de> SuSE */ +#define DISABLE_BRANCH_PROFILING #include <linux/init.h> #include <linux/linkage.h> #include <linux/types.h> diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 3a7ae80dc49d..0a472e9865c5 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -6678,14 +6678,20 @@ static int nested_vmx_check_vmptr(struct kvm_vcpu *vcpu, int exit_reason, } page = nested_get_page(vcpu, vmptr); - if (page == NULL || - *(u32 *)kmap(page) != VMCS12_REVISION) { + if (page == NULL) { nested_vmx_failInvalid(vcpu); + skip_emulated_instruction(vcpu); + return 1; + } + if (*(u32 *)kmap(page) != VMCS12_REVISION) { kunmap(page); + nested_release_page_clean(page); + nested_vmx_failInvalid(vcpu); skip_emulated_instruction(vcpu); return 1; } kunmap(page); + nested_release_page_clean(page); vmx->nested.vmxon_ptr = vmptr; break; case EXIT_REASON_VMCLEAR: diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index 493f54172b4a..3aebbd6c6f5f 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -628,21 +628,40 @@ void __init init_mem_mapping(void) * devmem_is_allowed() checks to see if /dev/mem access to a certain address * is valid. The argument is a physical page number. * - * - * On x86, access has to be given to the first megabyte of ram because that area - * contains BIOS code and data regions used by X and dosemu and similar apps. - * Access has to be given to non-kernel-ram areas as well, these contain the PCI - * mmio resources as well as potential bios/acpi data regions. + * On x86, access has to be given to the first megabyte of RAM because that + * area traditionally contains BIOS code and data regions used by X, dosemu, + * and similar apps. Since they map the entire memory range, the whole range + * must be allowed (for mapping), but any areas that would otherwise be + * disallowed are flagged as being "zero filled" instead of rejected. + * Access has to be given to non-kernel-ram areas as well, these contain the + * PCI mmio resources as well as potential bios/acpi data regions. */ int devmem_is_allowed(unsigned long pagenr) { - if (pagenr < 256) - return 1; - if (iomem_is_exclusive(pagenr << PAGE_SHIFT)) + if (page_is_ram(pagenr)) { + /* + * For disallowed memory regions in the low 1MB range, + * request that the page be shown as all zeros. + */ + if (pagenr < 256) + return 2; + + return 0; + } + + /* + * This must follow RAM test, since System RAM is considered a + * restricted resource under CONFIG_STRICT_IOMEM. + */ + if (iomem_is_exclusive(pagenr << PAGE_SHIFT)) { + /* Low 1MB bypasses iomem restrictions. */ + if (pagenr < 256) + return 1; + return 0; - if (!page_is_ram(pagenr)) - return 1; - return 0; + } + + return 1; } void free_init_pages(char *what, unsigned long begin, unsigned long end) diff --git a/arch/x86/mm/kasan_init_64.c b/arch/x86/mm/kasan_init_64.c index d470cf219a2d..4e5ac46adc9d 100644 --- a/arch/x86/mm/kasan_init_64.c +++ b/arch/x86/mm/kasan_init_64.c @@ -1,3 +1,4 @@ +#define DISABLE_BRANCH_PROFILING #define pr_fmt(fmt) "kasan: " fmt #include <linux/bootmem.h> #include <linux/kasan.h> diff --git a/arch/x86/pci/xen.c b/arch/x86/pci/xen.c index c6d6efed392a..7575f0798194 100644 --- a/arch/x86/pci/xen.c +++ b/arch/x86/pci/xen.c @@ -231,23 +231,14 @@ static int xen_hvm_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) return 1; for_each_pci_msi_entry(msidesc, dev) { - __pci_read_msi_msg(msidesc, &msg); - pirq = MSI_ADDR_EXT_DEST_ID(msg.address_hi) | - ((msg.address_lo >> MSI_ADDR_DEST_ID_SHIFT) & 0xff); - if (msg.data != XEN_PIRQ_MSI_DATA || - xen_irq_from_pirq(pirq) < 0) { - pirq = xen_allocate_pirq_msi(dev, msidesc); - if (pirq < 0) { - irq = -ENODEV; - goto error; - } - xen_msi_compose_msg(dev, pirq, &msg); - __pci_write_msi_msg(msidesc, &msg); - dev_dbg(&dev->dev, "xen: msi bound to pirq=%d\n", pirq); - } else { - dev_dbg(&dev->dev, - "xen: msi already bound to pirq=%d\n", pirq); + pirq = xen_allocate_pirq_msi(dev, msidesc); + if (pirq < 0) { + irq = -ENODEV; + goto error; } + xen_msi_compose_msg(dev, pirq, &msg); + __pci_write_msi_msg(msidesc, &msg); + dev_dbg(&dev->dev, "xen: msi bound to pirq=%d\n", pirq); irq = xen_bind_pirq_msi_to_irq(dev, msidesc, pirq, (type == PCI_CAP_ID_MSI) ? nvec : 1, (type == PCI_CAP_ID_MSIX) ? diff --git a/arch/x86/xen/setup.c b/arch/x86/xen/setup.c index e345891450c3..df8844a1853a 100644 --- a/arch/x86/xen/setup.c +++ b/arch/x86/xen/setup.c @@ -713,10 +713,9 @@ static void __init xen_reserve_xen_mfnlist(void) size = PFN_PHYS(xen_start_info->nr_p2m_frames); } - if (!xen_is_e820_reserved(start, size)) { - memblock_reserve(start, size); + memblock_reserve(start, size); + if (!xen_is_e820_reserved(start, size)) return; - } #ifdef CONFIG_X86_32 /* @@ -727,6 +726,7 @@ static void __init xen_reserve_xen_mfnlist(void) BUG(); #else xen_relocate_p2m(); + memblock_free(start, size); #endif } diff --git a/arch/x86/xen/spinlock.c b/arch/x86/xen/spinlock.c index 9e2ba5c6e1dd..f42e78de1e10 100644 --- a/arch/x86/xen/spinlock.c +++ b/arch/x86/xen/spinlock.c @@ -27,6 +27,12 @@ static bool xen_pvspin = true; static void xen_qlock_kick(int cpu) { + int irq = per_cpu(lock_kicker_irq, cpu); + + /* Don't kick if the target's kicker interrupt is not initialized. */ + if (irq == -1) + return; + xen_send_IPI_one(cpu, XEN_SPIN_UNLOCK_VECTOR); } diff --git a/arch/x86/xen/time.c b/arch/x86/xen/time.c index f1ba6a092854..8846257d8792 100644 --- a/arch/x86/xen/time.c +++ b/arch/x86/xen/time.c @@ -343,11 +343,11 @@ static int xen_vcpuop_set_next_event(unsigned long delta, WARN_ON(!clockevent_state_oneshot(evt)); single.timeout_abs_ns = get_abs_timeout(delta); - single.flags = VCPU_SSHOTTMR_future; + /* Get an event anyway, even if the timeout is already expired */ + single.flags = 0; ret = HYPERVISOR_vcpu_op(VCPUOP_set_singleshot_timer, cpu, &single); - - BUG_ON(ret != 0 && ret != -ETIME); + BUG_ON(ret != 0); return ret; } diff --git a/block/bio.c b/block/bio.c index 02c4d9bf1590..89782722f3ee 100644 --- a/block/bio.c +++ b/block/bio.c @@ -375,10 +375,14 @@ static void punt_bios_to_rescuer(struct bio_set *bs) bio_list_init(&punt); bio_list_init(&nopunt); - while ((bio = bio_list_pop(current->bio_list))) + while ((bio = bio_list_pop(¤t->bio_list[0]))) bio_list_add(bio->bi_pool == bs ? &punt : &nopunt, bio); + current->bio_list[0] = nopunt; - *current->bio_list = nopunt; + bio_list_init(&nopunt); + while ((bio = bio_list_pop(¤t->bio_list[1]))) + bio_list_add(bio->bi_pool == bs ? &punt : &nopunt, bio); + current->bio_list[1] = nopunt; spin_lock(&bs->rescue_lock); bio_list_merge(&bs->rescue_list, &punt); @@ -466,7 +470,9 @@ struct bio *bio_alloc_bioset(gfp_t gfp_mask, int nr_iovecs, struct bio_set *bs) * we retry with the original gfp_flags. */ - if (current->bio_list && !bio_list_empty(current->bio_list)) + if (current->bio_list && + (!bio_list_empty(¤t->bio_list[0]) || + !bio_list_empty(¤t->bio_list[1]))) gfp_mask &= ~__GFP_DIRECT_RECLAIM; p = mempool_alloc(bs->bio_pool, gfp_mask); diff --git a/block/blk-core.c b/block/blk-core.c index 500447be3db4..3115494c4cb2 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -2038,7 +2038,14 @@ end_io: */ blk_qc_t generic_make_request(struct bio *bio) { - struct bio_list bio_list_on_stack; + /* + * bio_list_on_stack[0] contains bios submitted by the current + * make_request_fn. + * bio_list_on_stack[1] contains bios that were submitted before + * the current make_request_fn, but that haven't been processed + * yet. + */ + struct bio_list bio_list_on_stack[2]; blk_qc_t ret = BLK_QC_T_NONE; if (!generic_make_request_checks(bio)) @@ -2055,7 +2062,7 @@ blk_qc_t generic_make_request(struct bio *bio) * should be added at the tail */ if (current->bio_list) { - bio_list_add(current->bio_list, bio); + bio_list_add(¤t->bio_list[0], bio); goto out; } @@ -2074,24 +2081,39 @@ blk_qc_t generic_make_request(struct bio *bio) * bio_list, and call into ->make_request() again. */ BUG_ON(bio->bi_next); - bio_list_init(&bio_list_on_stack); - current->bio_list = &bio_list_on_stack; + bio_list_init(&bio_list_on_stack[0]); + current->bio_list = bio_list_on_stack; do { struct request_queue *q = bdev_get_queue(bio->bi_bdev); if (likely(blk_queue_enter(q, __GFP_DIRECT_RECLAIM) == 0)) { + struct bio_list lower, same; + + /* Create a fresh bio_list for all subordinate requests */ + bio_list_on_stack[1] = bio_list_on_stack[0]; + bio_list_init(&bio_list_on_stack[0]); ret = q->make_request_fn(q, bio); blk_queue_exit(q); - - bio = bio_list_pop(current->bio_list); + /* sort new bios into those for a lower level + * and those for the same level + */ + bio_list_init(&lower); + bio_list_init(&same); + while ((bio = bio_list_pop(&bio_list_on_stack[0])) != NULL) + if (q == bdev_get_queue(bio->bi_bdev)) + bio_list_add(&same, bio); + else + bio_list_add(&lower, bio); + /* now assemble so we handle the lowest level first */ + bio_list_merge(&bio_list_on_stack[0], &lower); + bio_list_merge(&bio_list_on_stack[0], &same); + bio_list_merge(&bio_list_on_stack[0], &bio_list_on_stack[1]); } else { - struct bio *bio_next = bio_list_pop(current->bio_list); - bio_io_error(bio); - bio = bio_next; } + bio = bio_list_pop(&bio_list_on_stack[0]); } while (bio); current->bio_list = NULL; /* deactivate */ diff --git a/block/blk-mq.c b/block/blk-mq.c index 8bd548378822..1452db06ba45 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -1470,7 +1470,7 @@ static struct blk_mq_tags *blk_mq_init_rq_map(struct blk_mq_tag_set *set, INIT_LIST_HEAD(&tags->page_list); tags->rqs = kzalloc_node(set->queue_depth * sizeof(struct request *), - GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY, + GFP_NOIO | __GFP_NOWARN | __GFP_NORETRY, set->numa_node); if (!tags->rqs) { blk_mq_free_tags(tags); @@ -1496,7 +1496,7 @@ static struct blk_mq_tags *blk_mq_init_rq_map(struct blk_mq_tag_set *set, do { page = alloc_pages_node(set->numa_node, - GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY | __GFP_ZERO, + GFP_NOIO | __GFP_NOWARN | __GFP_NORETRY | __GFP_ZERO, this_order); if (page) break; @@ -1517,7 +1517,7 @@ static struct blk_mq_tags *blk_mq_init_rq_map(struct blk_mq_tag_set *set, * Allow kmemleak to scan these pages as they contain pointers * to additional allocations like via ops->init_request(). */ - kmemleak_alloc(p, order_to_size(this_order), 1, GFP_KERNEL); + kmemleak_alloc(p, order_to_size(this_order), 1, GFP_NOIO); entries_per_page = order_to_size(this_order) / rq_size; to_do = min(entries_per_page, set->queue_depth - i); left -= to_do * rq_size; diff --git a/block/genhd.c b/block/genhd.c index 7f1e8f81ceb4..de2a6162e3f3 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -673,7 +673,6 @@ void del_gendisk(struct gendisk *disk) kobject_put(disk->part0.holder_dir); kobject_put(disk->slave_dir); - disk->driverfs_dev = NULL; if (!sysfs_deprecated) sysfs_remove_link(block_depr, dev_name(disk_to_dev(disk))); pm_runtime_set_memalloc_noio(disk_to_dev(disk), false); diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c index 0774799942e0..c6fee7437be4 100644 --- a/block/scsi_ioctl.c +++ b/block/scsi_ioctl.c @@ -182,6 +182,9 @@ static void blk_set_cmd_filter_defaults(struct blk_cmd_filter *filter) __set_bit(WRITE_16, filter->write_ok); __set_bit(WRITE_LONG, filter->write_ok); __set_bit(WRITE_LONG_2, filter->write_ok); + __set_bit(WRITE_SAME, filter->write_ok); + __set_bit(WRITE_SAME_16, filter->write_ok); + __set_bit(WRITE_SAME_32, filter->write_ok); __set_bit(ERASE, filter->write_ok); __set_bit(GPCMD_MODE_SELECT_10, filter->write_ok); __set_bit(MODE_SELECT, filter->write_ok); diff --git a/build.config.goldfish.arm b/build.config.goldfish.arm new file mode 100644 index 000000000000..866da9361b71 --- /dev/null +++ b/build.config.goldfish.arm @@ -0,0 +1,12 @@ +ARCH=arm +BRANCH=android-4.4 +CROSS_COMPILE=arm-linux-androidkernel- +DEFCONFIG=ranchu_defconfig +EXTRA_CMDS='' +KERNEL_DIR=common +LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/bin +FILES=" +arch/arm/boot/zImage +vmlinux +System.map +" diff --git a/build.config.goldfish.arm64 b/build.config.goldfish.arm64 new file mode 100644 index 000000000000..9c963cf4a3d8 --- /dev/null +++ b/build.config.goldfish.arm64 @@ -0,0 +1,12 @@ +ARCH=arm64 +BRANCH=android-4.4 +CROSS_COMPILE=aarch64-linux-android- +DEFCONFIG=ranchu64_defconfig +EXTRA_CMDS='' +KERNEL_DIR=common +LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin +FILES=" +arch/arm64/boot/Image +vmlinux +System.map +" diff --git a/build.config.goldfish.mips b/build.config.goldfish.mips new file mode 100644 index 000000000000..8af53d2c2940 --- /dev/null +++ b/build.config.goldfish.mips @@ -0,0 +1,11 @@ +ARCH=mips +BRANCH=android-4.4 +CROSS_COMPILE=mips64el-linux-android- +DEFCONFIG=ranchu_defconfig +EXTRA_CMDS='' +KERNEL_DIR=common +LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/mips/mips64el-linux-android-4.9/bin +FILES=" +vmlinux +System.map +" diff --git a/build.config.goldfish.mips64 b/build.config.goldfish.mips64 new file mode 100644 index 000000000000..2a33d36dc4c8 --- /dev/null +++ b/build.config.goldfish.mips64 @@ -0,0 +1,11 @@ +ARCH=mips +BRANCH=android-4.4 +CROSS_COMPILE=mips64el-linux-android- +DEFCONFIG=ranchu64_defconfig +EXTRA_CMDS='' +KERNEL_DIR=common +LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/mips/mips64el-linux-android-4.9/bin +FILES=" +vmlinux +System.map +" diff --git a/build.config.goldfish.x86 b/build.config.goldfish.x86 new file mode 100644 index 000000000000..f86253f58d4d --- /dev/null +++ b/build.config.goldfish.x86 @@ -0,0 +1,12 @@ +ARCH=x86 +BRANCH=android-4.4 +CROSS_COMPILE=x86_64-linux-android- +DEFCONFIG=i386_ranchu_defconfig +EXTRA_CMDS='' +KERNEL_DIR=common +LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/bin +FILES=" +arch/x86/boot/bzImage +vmlinux +System.map +" diff --git a/build.config.goldfish.x86_64 b/build.config.goldfish.x86_64 new file mode 100644 index 000000000000..e1738861ec5c --- /dev/null +++ b/build.config.goldfish.x86_64 @@ -0,0 +1,12 @@ +ARCH=x86_64 +BRANCH=android-4.4 +CROSS_COMPILE=x86_64-linux-android- +DEFCONFIG=x86_64_ranchu_defconfig +EXTRA_CMDS='' +KERNEL_DIR=common +LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/bin +FILES=" +arch/x86/boot/bzImage +vmlinux +System.map +" diff --git a/crypto/Kconfig b/crypto/Kconfig index 7240821137fd..3240d394426c 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -289,6 +289,24 @@ config CRYPTO_CBC CBC: Cipher Block Chaining mode This block cipher algorithm is required for IPSec. +config CRYPTO_HEH + tristate "HEH support" + select CRYPTO_CMAC + select CRYPTO_ECB + select CRYPTO_GF128MUL + select CRYPTO_MANAGER + select CRYPTO_POLY_HASH_ARM64_CE if ARM64 && KERNEL_MODE_NEON + help + HEH: Hash-Encrypt-Hash mode + HEH is a proposed block cipher mode of operation which extends the + strong pseudo-random permutation (SPRP) property of block ciphers to + arbitrary-length input strings. This provides a stronger notion of + security than existing block cipher modes of operation (e.g. CBC, CTR, + XTS), though it is usually less performant. Applications include disk + encryption and encryption of file names and contents. Currently, this + implementation only provides a symmetric cipher interface, so it can't + yet be used as an AEAD. + config CRYPTO_CTR tristate "CTR support" select CRYPTO_BLKCIPHER diff --git a/crypto/Makefile b/crypto/Makefile index 03e66097eb0c..8507d1fab3ac 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -67,6 +67,7 @@ obj-$(CONFIG_CRYPTO_TGR192) += tgr192.o obj-$(CONFIG_CRYPTO_GF128MUL) += gf128mul.o obj-$(CONFIG_CRYPTO_ECB) += ecb.o obj-$(CONFIG_CRYPTO_CBC) += cbc.o +obj-$(CONFIG_CRYPTO_HEH) += heh.o obj-$(CONFIG_CRYPTO_PCBC) += pcbc.o obj-$(CONFIG_CRYPTO_CTS) += cts.o obj-$(CONFIG_CRYPTO_LRW) += lrw.o diff --git a/crypto/ahash.c b/crypto/ahash.c index dac1c24e9c3e..f9caf0f74199 100644 --- a/crypto/ahash.c +++ b/crypto/ahash.c @@ -31,6 +31,7 @@ struct ahash_request_priv { crypto_completion_t complete; void *data; u8 *result; + u32 flags; void *ubuf[] CRYPTO_MINALIGN_ATTR; }; @@ -270,6 +271,8 @@ static int ahash_save_req(struct ahash_request *req, crypto_completion_t cplt) priv->result = req->result; priv->complete = req->base.complete; priv->data = req->base.data; + priv->flags = req->base.flags; + /* * WARNING: We do not backup req->priv here! The req->priv * is for internal use of the Crypto API and the @@ -284,38 +287,44 @@ static int ahash_save_req(struct ahash_request *req, crypto_completion_t cplt) return 0; } -static void ahash_restore_req(struct ahash_request *req) +static void ahash_restore_req(struct ahash_request *req, int err) { struct ahash_request_priv *priv = req->priv; + if (!err) + memcpy(priv->result, req->result, + crypto_ahash_digestsize(crypto_ahash_reqtfm(req))); + /* Restore the original crypto request. */ req->result = priv->result; - req->base.complete = priv->complete; - req->base.data = priv->data; + + ahash_request_set_callback(req, priv->flags, + priv->complete, priv->data); req->priv = NULL; /* Free the req->priv.priv from the ADJUSTED request. */ kzfree(priv); } -static void ahash_op_unaligned_finish(struct ahash_request *req, int err) +static void ahash_notify_einprogress(struct ahash_request *req) { struct ahash_request_priv *priv = req->priv; + struct crypto_async_request oreq; - if (err == -EINPROGRESS) - return; - - if (!err) - memcpy(priv->result, req->result, - crypto_ahash_digestsize(crypto_ahash_reqtfm(req))); + oreq.data = priv->data; - ahash_restore_req(req); + priv->complete(&oreq, -EINPROGRESS); } static void ahash_op_unaligned_done(struct crypto_async_request *req, int err) { struct ahash_request *areq = req->data; + if (err == -EINPROGRESS) { + ahash_notify_einprogress(areq); + return; + } + /* * Restore the original request, see ahash_op_unaligned() for what * goes where. @@ -326,7 +335,7 @@ static void ahash_op_unaligned_done(struct crypto_async_request *req, int err) */ /* First copy req->result into req->priv.result */ - ahash_op_unaligned_finish(areq, err); + ahash_restore_req(areq, err); /* Complete the ORIGINAL request. */ areq->base.complete(&areq->base, err); @@ -342,7 +351,12 @@ static int ahash_op_unaligned(struct ahash_request *req, return err; err = op(req); - ahash_op_unaligned_finish(req, err); + if (err == -EINPROGRESS || + (err == -EBUSY && (ahash_request_flags(req) & + CRYPTO_TFM_REQ_MAY_BACKLOG))) + return err; + + ahash_restore_req(req, err); return err; } @@ -377,25 +391,14 @@ int crypto_ahash_digest(struct ahash_request *req) } EXPORT_SYMBOL_GPL(crypto_ahash_digest); -static void ahash_def_finup_finish2(struct ahash_request *req, int err) +static void ahash_def_finup_done2(struct crypto_async_request *req, int err) { - struct ahash_request_priv *priv = req->priv; + struct ahash_request *areq = req->data; if (err == -EINPROGRESS) return; - if (!err) - memcpy(priv->result, req->result, - crypto_ahash_digestsize(crypto_ahash_reqtfm(req))); - - ahash_restore_req(req); -} - -static void ahash_def_finup_done2(struct crypto_async_request *req, int err) -{ - struct ahash_request *areq = req->data; - - ahash_def_finup_finish2(areq, err); + ahash_restore_req(areq, err); areq->base.complete(&areq->base, err); } @@ -406,11 +409,15 @@ static int ahash_def_finup_finish1(struct ahash_request *req, int err) goto out; req->base.complete = ahash_def_finup_done2; - req->base.flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; + err = crypto_ahash_reqtfm(req)->final(req); + if (err == -EINPROGRESS || + (err == -EBUSY && (ahash_request_flags(req) & + CRYPTO_TFM_REQ_MAY_BACKLOG))) + return err; out: - ahash_def_finup_finish2(req, err); + ahash_restore_req(req, err); return err; } @@ -418,7 +425,16 @@ static void ahash_def_finup_done1(struct crypto_async_request *req, int err) { struct ahash_request *areq = req->data; + if (err == -EINPROGRESS) { + ahash_notify_einprogress(areq); + return; + } + + areq->base.flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; + err = ahash_def_finup_finish1(areq, err); + if (areq->priv) + return; areq->base.complete(&areq->base, err); } @@ -433,6 +449,11 @@ static int ahash_def_finup(struct ahash_request *req) return err; err = tfm->update(req); + if (err == -EINPROGRESS || + (err == -EBUSY && (ahash_request_flags(req) & + CRYPTO_TFM_REQ_MAY_BACKLOG))) + return err; + return ahash_def_finup_finish1(req, err); } diff --git a/crypto/algif_hash.c b/crypto/algif_hash.c index 68a5ceaa04c8..8d8b3eeba725 100644 --- a/crypto/algif_hash.c +++ b/crypto/algif_hash.c @@ -184,7 +184,7 @@ static int hash_accept(struct socket *sock, struct socket *newsock, int flags) struct alg_sock *ask = alg_sk(sk); struct hash_ctx *ctx = ask->private; struct ahash_request *req = &ctx->req; - char state[crypto_ahash_statesize(crypto_ahash_reqtfm(req))]; + char state[crypto_ahash_statesize(crypto_ahash_reqtfm(req)) ? : 1]; struct sock *sk2; struct alg_sock *ask2; struct hash_ctx *ctx2; diff --git a/crypto/blkcipher.c b/crypto/blkcipher.c index dca7bc87dad9..7bbfadc195a6 100644 --- a/crypto/blkcipher.c +++ b/crypto/blkcipher.c @@ -373,6 +373,27 @@ int blkcipher_aead_walk_virt_block(struct blkcipher_desc *desc, } EXPORT_SYMBOL_GPL(blkcipher_aead_walk_virt_block); +/* + * This function allows ablkcipher algorithms to use the blkcipher_walk API to + * walk over their data. The specified crypto_ablkcipher tfm is used to + * initialize the struct blkcipher_walk, and the crypto_blkcipher specified in + * desc->tfm is never used so it can be left NULL. (Yes, this design is ugly, + * but it parallels blkcipher_aead_walk_virt_block() above. In the 4.10 kernel + * this is starting to be cleaned up...) + */ +int blkcipher_ablkcipher_walk_virt(struct blkcipher_desc *desc, + struct blkcipher_walk *walk, + struct crypto_ablkcipher *tfm) +{ + walk->flags &= ~BLKCIPHER_WALK_PHYS; + walk->walk_blocksize = crypto_ablkcipher_blocksize(tfm); + walk->cipher_blocksize = walk->walk_blocksize; + walk->ivsize = crypto_ablkcipher_ivsize(tfm); + walk->alignmask = crypto_ablkcipher_alignmask(tfm); + return blkcipher_walk_first(desc, walk); +} +EXPORT_SYMBOL_GPL(blkcipher_ablkcipher_walk_virt); + static int setkey_unaligned(struct crypto_tfm *tfm, const u8 *key, unsigned int keylen) { diff --git a/crypto/cryptd.c b/crypto/cryptd.c index e7aa904cb20b..26a504db3f53 100644 --- a/crypto/cryptd.c +++ b/crypto/cryptd.c @@ -642,6 +642,7 @@ static int cryptd_create_hash(struct crypto_template *tmpl, struct rtattr **tb, inst->alg.halg.base.cra_flags = type; inst->alg.halg.digestsize = salg->digestsize; + inst->alg.halg.statesize = salg->statesize; inst->alg.halg.base.cra_ctxsize = sizeof(struct cryptd_hash_ctx); inst->alg.halg.base.cra_init = cryptd_hash_init_tfm; diff --git a/crypto/gf128mul.c b/crypto/gf128mul.c index 5276607c72d0..f3d9f6da0767 100644 --- a/crypto/gf128mul.c +++ b/crypto/gf128mul.c @@ -44,7 +44,7 @@ --------------------------------------------------------------------------- Issue 31/01/2006 - This file provides fast multiplication in GF(128) as required by several + This file provides fast multiplication in GF(2^128) as required by several cryptographic authentication modes */ @@ -88,37 +88,52 @@ q(0xf8), q(0xf9), q(0xfa), q(0xfb), q(0xfc), q(0xfd), q(0xfe), q(0xff) \ } -/* Given the value i in 0..255 as the byte overflow when a field element - in GHASH is multiplied by x^8, this function will return the values that - are generated in the lo 16-bit word of the field value by applying the - modular polynomial. The values lo_byte and hi_byte are returned via the - macro xp_fun(lo_byte, hi_byte) so that the values can be assembled into - memory as required by a suitable definition of this macro operating on - the table above -*/ - -#define xx(p, q) 0x##p##q +/* + * Given a value i in 0..255 as the byte overflow when a field element + * in GF(2^128) is multiplied by x^8, the following macro returns the + * 16-bit value that must be XOR-ed into the low-degree end of the + * product to reduce it modulo the irreducible polynomial x^128 + x^7 + + * x^2 + x + 1. + * + * There are two versions of the macro, and hence two tables: one for + * the "be" convention where the highest-order bit is the coefficient of + * the highest-degree polynomial term, and one for the "le" convention + * where the highest-order bit is the coefficient of the lowest-degree + * polynomial term. In both cases the values are stored in CPU byte + * endianness such that the coefficients are ordered consistently across + * bytes, i.e. in the "be" table bits 15..0 of the stored value + * correspond to the coefficients of x^15..x^0, and in the "le" table + * bits 15..0 correspond to the coefficients of x^0..x^15. + * + * Therefore, provided that the appropriate byte endianness conversions + * are done by the multiplication functions (and these must be in place + * anyway to support both little endian and big endian CPUs), the "be" + * table can be used for multiplications of both "bbe" and "ble" + * elements, and the "le" table can be used for multiplications of both + * "lle" and "lbe" elements. + */ -#define xda_bbe(i) ( \ - (i & 0x80 ? xx(43, 80) : 0) ^ (i & 0x40 ? xx(21, c0) : 0) ^ \ - (i & 0x20 ? xx(10, e0) : 0) ^ (i & 0x10 ? xx(08, 70) : 0) ^ \ - (i & 0x08 ? xx(04, 38) : 0) ^ (i & 0x04 ? xx(02, 1c) : 0) ^ \ - (i & 0x02 ? xx(01, 0e) : 0) ^ (i & 0x01 ? xx(00, 87) : 0) \ +#define xda_be(i) ( \ + (i & 0x80 ? 0x4380 : 0) ^ (i & 0x40 ? 0x21c0 : 0) ^ \ + (i & 0x20 ? 0x10e0 : 0) ^ (i & 0x10 ? 0x0870 : 0) ^ \ + (i & 0x08 ? 0x0438 : 0) ^ (i & 0x04 ? 0x021c : 0) ^ \ + (i & 0x02 ? 0x010e : 0) ^ (i & 0x01 ? 0x0087 : 0) \ ) -#define xda_lle(i) ( \ - (i & 0x80 ? xx(e1, 00) : 0) ^ (i & 0x40 ? xx(70, 80) : 0) ^ \ - (i & 0x20 ? xx(38, 40) : 0) ^ (i & 0x10 ? xx(1c, 20) : 0) ^ \ - (i & 0x08 ? xx(0e, 10) : 0) ^ (i & 0x04 ? xx(07, 08) : 0) ^ \ - (i & 0x02 ? xx(03, 84) : 0) ^ (i & 0x01 ? xx(01, c2) : 0) \ +#define xda_le(i) ( \ + (i & 0x80 ? 0xe100 : 0) ^ (i & 0x40 ? 0x7080 : 0) ^ \ + (i & 0x20 ? 0x3840 : 0) ^ (i & 0x10 ? 0x1c20 : 0) ^ \ + (i & 0x08 ? 0x0e10 : 0) ^ (i & 0x04 ? 0x0708 : 0) ^ \ + (i & 0x02 ? 0x0384 : 0) ^ (i & 0x01 ? 0x01c2 : 0) \ ) -static const u16 gf128mul_table_lle[256] = gf128mul_dat(xda_lle); -static const u16 gf128mul_table_bbe[256] = gf128mul_dat(xda_bbe); +static const u16 gf128mul_table_le[256] = gf128mul_dat(xda_le); +static const u16 gf128mul_table_be[256] = gf128mul_dat(xda_be); -/* These functions multiply a field element by x, by x^4 and by x^8 - * in the polynomial field representation. It uses 32-bit word operations - * to gain speed but compensates for machine endianess and hence works +/* + * The following functions multiply a field element by x or by x^8 in + * the polynomial field representation. They use 64-bit word operations + * to gain speed but compensate for machine endianness and hence work * correctly on both styles of machine. */ @@ -126,7 +141,7 @@ static void gf128mul_x_lle(be128 *r, const be128 *x) { u64 a = be64_to_cpu(x->a); u64 b = be64_to_cpu(x->b); - u64 _tt = gf128mul_table_lle[(b << 7) & 0xff]; + u64 _tt = gf128mul_table_le[(b << 7) & 0xff]; r->b = cpu_to_be64((b >> 1) | (a << 63)); r->a = cpu_to_be64((a >> 1) ^ (_tt << 48)); @@ -136,7 +151,7 @@ static void gf128mul_x_bbe(be128 *r, const be128 *x) { u64 a = be64_to_cpu(x->a); u64 b = be64_to_cpu(x->b); - u64 _tt = gf128mul_table_bbe[a >> 63]; + u64 _tt = gf128mul_table_be[a >> 63]; r->a = cpu_to_be64((a << 1) | (b >> 63)); r->b = cpu_to_be64((b << 1) ^ _tt); @@ -146,7 +161,7 @@ void gf128mul_x_ble(be128 *r, const be128 *x) { u64 a = le64_to_cpu(x->a); u64 b = le64_to_cpu(x->b); - u64 _tt = gf128mul_table_bbe[b >> 63]; + u64 _tt = gf128mul_table_be[b >> 63]; r->a = cpu_to_le64((a << 1) ^ _tt); r->b = cpu_to_le64((b << 1) | (a >> 63)); @@ -157,7 +172,7 @@ static void gf128mul_x8_lle(be128 *x) { u64 a = be64_to_cpu(x->a); u64 b = be64_to_cpu(x->b); - u64 _tt = gf128mul_table_lle[b & 0xff]; + u64 _tt = gf128mul_table_le[b & 0xff]; x->b = cpu_to_be64((b >> 8) | (a << 56)); x->a = cpu_to_be64((a >> 8) ^ (_tt << 48)); @@ -167,12 +182,22 @@ static void gf128mul_x8_bbe(be128 *x) { u64 a = be64_to_cpu(x->a); u64 b = be64_to_cpu(x->b); - u64 _tt = gf128mul_table_bbe[a >> 56]; + u64 _tt = gf128mul_table_be[a >> 56]; x->a = cpu_to_be64((a << 8) | (b >> 56)); x->b = cpu_to_be64((b << 8) ^ _tt); } +static void gf128mul_x8_ble(be128 *x) +{ + u64 a = le64_to_cpu(x->b); + u64 b = le64_to_cpu(x->a); + u64 _tt = gf128mul_table_be[a >> 56]; + + x->b = cpu_to_le64((a << 8) | (b >> 56)); + x->a = cpu_to_le64((b << 8) ^ _tt); +} + void gf128mul_lle(be128 *r, const be128 *b) { be128 p[8]; @@ -249,9 +274,48 @@ void gf128mul_bbe(be128 *r, const be128 *b) } EXPORT_SYMBOL(gf128mul_bbe); +void gf128mul_ble(be128 *r, const be128 *b) +{ + be128 p[8]; + int i; + + p[0] = *r; + for (i = 0; i < 7; ++i) + gf128mul_x_ble((be128 *)&p[i + 1], (be128 *)&p[i]); + + memset(r, 0, sizeof(*r)); + for (i = 0;;) { + u8 ch = ((u8 *)b)[15 - i]; + + if (ch & 0x80) + be128_xor(r, r, &p[7]); + if (ch & 0x40) + be128_xor(r, r, &p[6]); + if (ch & 0x20) + be128_xor(r, r, &p[5]); + if (ch & 0x10) + be128_xor(r, r, &p[4]); + if (ch & 0x08) + be128_xor(r, r, &p[3]); + if (ch & 0x04) + be128_xor(r, r, &p[2]); + if (ch & 0x02) + be128_xor(r, r, &p[1]); + if (ch & 0x01) + be128_xor(r, r, &p[0]); + + if (++i >= 16) + break; + + gf128mul_x8_ble(r); + } +} +EXPORT_SYMBOL(gf128mul_ble); + + /* This version uses 64k bytes of table space. A 16 byte buffer has to be multiplied by a 16 byte key - value in GF(128). If we consider a GF(128) value in + value in GF(2^128). If we consider a GF(2^128) value in the buffer's lowest byte, we can construct a table of the 256 16 byte values that result from the 256 values of this byte. This requires 4096 bytes. But we also @@ -352,8 +416,8 @@ void gf128mul_free_64k(struct gf128mul_64k *t) int i; for (i = 0; i < 16; i++) - kfree(t->t[i]); - kfree(t); + kzfree(t->t[i]); + kzfree(t); } EXPORT_SYMBOL(gf128mul_free_64k); @@ -385,7 +449,7 @@ EXPORT_SYMBOL(gf128mul_64k_bbe); /* This version uses 4k bytes of table space. A 16 byte buffer has to be multiplied by a 16 byte key - value in GF(128). If we consider a GF(128) value in a + value in GF(2^128). If we consider a GF(2^128) value in a single byte, we can construct a table of the 256 16 byte values that result from the 256 values of this byte. This requires 4096 bytes. If we take the highest byte in @@ -443,6 +507,28 @@ out: } EXPORT_SYMBOL(gf128mul_init_4k_bbe); +struct gf128mul_4k *gf128mul_init_4k_ble(const be128 *g) +{ + struct gf128mul_4k *t; + int j, k; + + t = kzalloc(sizeof(*t), GFP_KERNEL); + if (!t) + goto out; + + t->t[1] = *g; + for (j = 1; j <= 64; j <<= 1) + gf128mul_x_ble(&t->t[j + j], &t->t[j]); + + for (j = 2; j < 256; j += j) + for (k = 1; k < j; ++k) + be128_xor(&t->t[j + k], &t->t[j], &t->t[k]); + +out: + return t; +} +EXPORT_SYMBOL(gf128mul_init_4k_ble); + void gf128mul_4k_lle(be128 *a, struct gf128mul_4k *t) { u8 *ap = (u8 *)a; @@ -473,5 +559,20 @@ void gf128mul_4k_bbe(be128 *a, struct gf128mul_4k *t) } EXPORT_SYMBOL(gf128mul_4k_bbe); +void gf128mul_4k_ble(be128 *a, struct gf128mul_4k *t) +{ + u8 *ap = (u8 *)a; + be128 r[1]; + int i = 15; + + *r = t->t[ap[15]]; + while (i--) { + gf128mul_x8_ble(r); + be128_xor(r, r, &t->t[ap[i]]); + } + *a = *r; +} +EXPORT_SYMBOL(gf128mul_4k_ble); + MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Functions for multiplying elements of GF(2^128)"); diff --git a/crypto/heh.c b/crypto/heh.c new file mode 100644 index 000000000000..10c00aaf797e --- /dev/null +++ b/crypto/heh.c @@ -0,0 +1,1033 @@ +/* + * HEH: Hash-Encrypt-Hash mode + * + * Copyright (c) 2016 Google Inc. + * + * Authors: + * Alex Cope <alexcope@google.com> + * Eric Biggers <ebiggers@google.com> + */ + +/* + * Hash-Encrypt-Hash (HEH) is a proposed block cipher mode of operation which + * extends the strong pseudo-random permutation (SPRP) property of block ciphers + * (e.g. AES) to arbitrary length input strings. It uses two keyed invertible + * hash functions with a layer of ECB encryption applied in-between. The + * algorithm is specified by the following Internet Draft: + * + * https://tools.ietf.org/html/draft-cope-heh-01 + * + * Although HEH can be used as either a regular symmetric cipher or as an AEAD, + * currently this module only provides it as a symmetric cipher. Additionally, + * only 16-byte nonces are supported. + */ + +#include <crypto/gf128mul.h> +#include <crypto/internal/hash.h> +#include <crypto/internal/skcipher.h> +#include <crypto/scatterwalk.h> +#include <crypto/skcipher.h> +#include "internal.h" + +/* + * The block size is the size of GF(2^128) elements and also the required block + * size of the underlying block cipher. + */ +#define HEH_BLOCK_SIZE 16 + +struct heh_instance_ctx { + struct crypto_shash_spawn cmac; + struct crypto_shash_spawn poly_hash; + struct crypto_skcipher_spawn ecb; +}; + +struct heh_tfm_ctx { + struct crypto_shash *cmac; + struct crypto_shash *poly_hash; /* keyed with tau_key */ + struct crypto_ablkcipher *ecb; +}; + +struct heh_cmac_data { + u8 nonce[HEH_BLOCK_SIZE]; + __le32 nonce_length; + __le32 aad_length; + __le32 message_length; + __le32 padding; +}; + +struct heh_req_ctx { /* aligned to alignmask */ + be128 beta1_key; + be128 beta2_key; + union { + struct { + struct heh_cmac_data data; + struct shash_desc desc; + /* + crypto_shash_descsize(cmac) */ + } cmac; + struct { + struct shash_desc desc; + /* + crypto_shash_descsize(poly_hash) */ + } poly_hash; + struct { + u8 keystream[HEH_BLOCK_SIZE]; + u8 tmp[HEH_BLOCK_SIZE]; + struct scatterlist tmp_sgl[2]; + struct ablkcipher_request req; + /* + crypto_ablkcipher_reqsize(ecb) */ + } ecb; + } u; +}; + +/* + * Get the offset in bytes to the last full block, or equivalently the length of + * all full blocks excluding the last + */ +static inline unsigned int get_tail_offset(unsigned int len) +{ + len -= len % HEH_BLOCK_SIZE; + return len - HEH_BLOCK_SIZE; +} + +static inline struct heh_req_ctx *heh_req_ctx(struct ablkcipher_request *req) +{ + unsigned int alignmask = crypto_ablkcipher_alignmask( + crypto_ablkcipher_reqtfm(req)); + + return (void *)PTR_ALIGN((u8 *)ablkcipher_request_ctx(req), + alignmask + 1); +} + +static inline void async_done(struct crypto_async_request *areq, int err, + int (*next_step)(struct ablkcipher_request *, + u32)) +{ + struct ablkcipher_request *req = areq->data; + + if (err) + goto out; + + err = next_step(req, req->base.flags & ~CRYPTO_TFM_REQ_MAY_SLEEP); + if (err == -EINPROGRESS || + (err == -EBUSY && (req->base.flags & CRYPTO_TFM_REQ_MAY_BACKLOG))) + return; +out: + ablkcipher_request_complete(req, err); +} + +/* + * Generate the per-message "beta" keys used by the hashing layers of HEH. The + * first beta key is the CMAC of the nonce, the additional authenticated data + * (AAD), and the lengths in bytes of the nonce, AAD, and message. The nonce + * and AAD are each zero-padded to the next 16-byte block boundary, and the + * lengths are serialized as 4-byte little endian integers and zero-padded to + * the next 16-byte block boundary. + * The second beta key is the first one interpreted as an element in GF(2^128) + * and multiplied by x. + * + * Note that because the nonce and AAD may, in general, be variable-length, the + * key generation must be done by a pseudo-random function (PRF) on + * variable-length inputs. CBC-MAC does not satisfy this, as it is only a PRF + * on fixed-length inputs. CMAC remedies this flaw. Including the lengths of + * the nonce, AAD, and message is also critical to avoid collisions. + * + * That being said, this implementation does not yet operate as an AEAD and + * therefore there is never any AAD, nor are variable-length nonces supported. + */ +static int generate_betas(struct ablkcipher_request *req, + be128 *beta1_key, be128 *beta2_key) +{ + struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req); + struct heh_tfm_ctx *ctx = crypto_ablkcipher_ctx(tfm); + struct heh_req_ctx *rctx = heh_req_ctx(req); + struct heh_cmac_data *data = &rctx->u.cmac.data; + struct shash_desc *desc = &rctx->u.cmac.desc; + int err; + + BUILD_BUG_ON(sizeof(*data) != 2 * HEH_BLOCK_SIZE); + memcpy(data->nonce, req->info, HEH_BLOCK_SIZE); + data->nonce_length = cpu_to_le32(HEH_BLOCK_SIZE); + data->aad_length = cpu_to_le32(0); + data->message_length = cpu_to_le32(req->nbytes); + data->padding = cpu_to_le32(0); + + desc->tfm = ctx->cmac; + desc->flags = req->base.flags; + + err = crypto_shash_digest(desc, (const u8 *)data, sizeof(*data), + (u8 *)beta1_key); + if (err) + return err; + + gf128mul_x_ble(beta2_key, beta1_key); + return 0; +} + +/*****************************************************************************/ + +/* + * This is the generic version of poly_hash. It does the GF(2^128) + * multiplication by 'tau_key' using a precomputed table, without using any + * special CPU instructions. On some platforms, an accelerated version (with + * higher cra_priority) may be used instead. + */ + +struct poly_hash_tfm_ctx { + struct gf128mul_4k *tau_key; +}; + +struct poly_hash_desc_ctx { + be128 digest; + unsigned int count; +}; + +static int poly_hash_setkey(struct crypto_shash *tfm, + const u8 *key, unsigned int keylen) +{ + struct poly_hash_tfm_ctx *tctx = crypto_shash_ctx(tfm); + be128 key128; + + if (keylen != HEH_BLOCK_SIZE) { + crypto_shash_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN); + return -EINVAL; + } + + if (tctx->tau_key) + gf128mul_free_4k(tctx->tau_key); + memcpy(&key128, key, HEH_BLOCK_SIZE); + tctx->tau_key = gf128mul_init_4k_ble(&key128); + if (!tctx->tau_key) + return -ENOMEM; + return 0; +} + +static int poly_hash_init(struct shash_desc *desc) +{ + struct poly_hash_desc_ctx *ctx = shash_desc_ctx(desc); + + ctx->digest = (be128) { 0 }; + ctx->count = 0; + return 0; +} + +static int poly_hash_update(struct shash_desc *desc, const u8 *src, + unsigned int len) +{ + struct poly_hash_tfm_ctx *tctx = crypto_shash_ctx(desc->tfm); + struct poly_hash_desc_ctx *ctx = shash_desc_ctx(desc); + unsigned int partial = ctx->count % HEH_BLOCK_SIZE; + u8 *dst = (u8 *)&ctx->digest + partial; + + ctx->count += len; + + /* Finishing at least one block? */ + if (partial + len >= HEH_BLOCK_SIZE) { + + if (partial) { + /* Finish the pending block. */ + unsigned int n = HEH_BLOCK_SIZE - partial; + + len -= n; + do { + *dst++ ^= *src++; + } while (--n); + + gf128mul_4k_ble(&ctx->digest, tctx->tau_key); + } + + /* Process zero or more full blocks. */ + while (len >= HEH_BLOCK_SIZE) { + be128 coeff; + + memcpy(&coeff, src, HEH_BLOCK_SIZE); + be128_xor(&ctx->digest, &ctx->digest, &coeff); + src += HEH_BLOCK_SIZE; + len -= HEH_BLOCK_SIZE; + gf128mul_4k_ble(&ctx->digest, tctx->tau_key); + } + dst = (u8 *)&ctx->digest; + } + + /* Continue adding the next block to 'digest'. */ + while (len--) + *dst++ ^= *src++; + return 0; +} + +static int poly_hash_final(struct shash_desc *desc, u8 *out) +{ + struct poly_hash_desc_ctx *ctx = shash_desc_ctx(desc); + + /* Finish the last block if needed. */ + if (ctx->count % HEH_BLOCK_SIZE) { + struct poly_hash_tfm_ctx *tctx = crypto_shash_ctx(desc->tfm); + + gf128mul_4k_ble(&ctx->digest, tctx->tau_key); + } + + memcpy(out, &ctx->digest, HEH_BLOCK_SIZE); + return 0; +} + +static void poly_hash_exit(struct crypto_tfm *tfm) +{ + struct poly_hash_tfm_ctx *tctx = crypto_tfm_ctx(tfm); + + gf128mul_free_4k(tctx->tau_key); +} + +static struct shash_alg poly_hash_alg = { + .digestsize = HEH_BLOCK_SIZE, + .init = poly_hash_init, + .update = poly_hash_update, + .final = poly_hash_final, + .setkey = poly_hash_setkey, + .descsize = sizeof(struct poly_hash_desc_ctx), + .base = { + .cra_name = "poly_hash", + .cra_driver_name = "poly_hash-generic", + .cra_priority = 100, + .cra_ctxsize = sizeof(struct poly_hash_tfm_ctx), + .cra_exit = poly_hash_exit, + .cra_module = THIS_MODULE, + }, +}; + +/*****************************************************************************/ + +/* + * Split the message into 16 byte blocks, padding out the last block, and use + * the blocks as coefficients in the evaluation of a polynomial over GF(2^128) + * at the secret point 'tau_key'. For ease of implementing the higher-level + * heh_hash_inv() function, the constant and degree-1 coefficients are swapped + * if there is a partial block. + * + * Mathematically, compute: + * if (no partial block) + * k^{N-1} * m_0 + ... + k * m_{N-2} + m_{N-1} + * else if (partial block) + * k^N * m_0 + ... + k^2 * m_{N-2} + k * m_N + m_{N-1} + * + * where: + * t is tau_key + * N is the number of full blocks in the message + * m_i is the i-th full block in the message for i = 0 to N-1 inclusive + * m_N is the partial block of the message zero-padded up to 16 bytes + * + * Note that most of this is now separated out into its own keyed hash + * algorithm, to allow optimized implementations. However, we still handle the + * swapping of the last two coefficients here in the HEH template because this + * simplifies the poly_hash algorithms: they don't have to buffer an extra + * block, don't have to duplicate as much code, and are more similar to GHASH. + */ +static int poly_hash(struct ablkcipher_request *req, struct scatterlist *sgl, + be128 *hash) +{ + struct heh_req_ctx *rctx = heh_req_ctx(req); + struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req); + struct heh_tfm_ctx *ctx = crypto_ablkcipher_ctx(tfm); + struct shash_desc *desc = &rctx->u.poly_hash.desc; + unsigned int tail_offset = get_tail_offset(req->nbytes); + unsigned int tail_len = req->nbytes - tail_offset; + be128 tail[2]; + unsigned int i, n; + struct sg_mapping_iter miter; + int err; + + desc->tfm = ctx->poly_hash; + desc->flags = req->base.flags; + + /* Handle all full blocks except the last */ + err = crypto_shash_init(desc); + sg_miter_start(&miter, sgl, sg_nents(sgl), + SG_MITER_FROM_SG | SG_MITER_ATOMIC); + for (i = 0; i < tail_offset && !err; i += n) { + sg_miter_next(&miter); + n = min_t(unsigned int, miter.length, tail_offset - i); + err = crypto_shash_update(desc, miter.addr, n); + } + sg_miter_stop(&miter); + if (err) + return err; + + /* Handle the last full block and the partial block */ + scatterwalk_map_and_copy(tail, sgl, tail_offset, tail_len, 0); + + if (tail_len != HEH_BLOCK_SIZE) { + /* handle the partial block */ + memset((u8 *)tail + tail_len, 0, sizeof(tail) - tail_len); + err = crypto_shash_update(desc, (u8 *)&tail[1], HEH_BLOCK_SIZE); + if (err) + return err; + } + err = crypto_shash_final(desc, (u8 *)hash); + if (err) + return err; + be128_xor(hash, hash, &tail[0]); + return 0; +} + +/* + * Transform all full blocks except the last. + * This is used by both the hash and inverse hash phases. + */ +static int heh_tfm_blocks(struct ablkcipher_request *req, + struct scatterlist *src_sgl, + struct scatterlist *dst_sgl, unsigned int len, + const be128 *hash, const be128 *beta_key) +{ + struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req); + struct blkcipher_desc desc = { .flags = req->base.flags }; + struct blkcipher_walk walk; + be128 e = *beta_key; + int err; + unsigned int nbytes; + + blkcipher_walk_init(&walk, dst_sgl, src_sgl, len); + + err = blkcipher_ablkcipher_walk_virt(&desc, &walk, tfm); + + while ((nbytes = walk.nbytes)) { + const be128 *src = (be128 *)walk.src.virt.addr; + be128 *dst = (be128 *)walk.dst.virt.addr; + + do { + gf128mul_x_ble(&e, &e); + be128_xor(dst, src, hash); + be128_xor(dst, dst, &e); + src++; + dst++; + } while ((nbytes -= HEH_BLOCK_SIZE) >= HEH_BLOCK_SIZE); + err = blkcipher_walk_done(&desc, &walk, nbytes); + } + return err; +} + +/* + * The hash phase of HEH. Given a message, compute: + * + * (m_0 + H, ..., m_{N-2} + H, H, m_N) + (xb, x^2b, ..., x^{N-1}b, b, 0) + * + * where: + * N is the number of full blocks in the message + * m_i is the i-th full block in the message for i = 0 to N-1 inclusive + * m_N is the unpadded partial block, possibly empty + * H is the poly_hash() of the message, keyed by tau_key + * b is beta_key + * x is the element x in our representation of GF(2^128) + * + * Note that the partial block remains unchanged, but it does affect the result + * of poly_hash() and therefore the transformation of all the full blocks. + */ +static int heh_hash(struct ablkcipher_request *req, const be128 *beta_key) +{ + be128 hash; + unsigned int tail_offset = get_tail_offset(req->nbytes); + unsigned int partial_len = req->nbytes % HEH_BLOCK_SIZE; + int err; + + /* poly_hash() the full message including the partial block */ + err = poly_hash(req, req->src, &hash); + if (err) + return err; + + /* Transform all full blocks except the last */ + err = heh_tfm_blocks(req, req->src, req->dst, tail_offset, &hash, + beta_key); + if (err) + return err; + + /* Set the last full block to hash XOR beta_key */ + be128_xor(&hash, &hash, beta_key); + scatterwalk_map_and_copy(&hash, req->dst, tail_offset, HEH_BLOCK_SIZE, + 1); + + /* Copy the partial block if needed */ + if (partial_len != 0 && req->src != req->dst) { + unsigned int offs = tail_offset + HEH_BLOCK_SIZE; + + scatterwalk_map_and_copy(&hash, req->src, offs, partial_len, 0); + scatterwalk_map_and_copy(&hash, req->dst, offs, partial_len, 1); + } + return 0; +} + +/* + * The inverse hash phase of HEH. This undoes the result of heh_hash(). + */ +static int heh_hash_inv(struct ablkcipher_request *req, const be128 *beta_key) +{ + be128 hash; + be128 tmp; + struct scatterlist tmp_sgl[2]; + struct scatterlist *tail_sgl; + unsigned int tail_offset = get_tail_offset(req->nbytes); + struct scatterlist *sgl = req->dst; + int err; + + /* + * The last full block was computed as hash XOR beta_key, so XOR it with + * beta_key to recover hash. + */ + tail_sgl = scatterwalk_ffwd(tmp_sgl, sgl, tail_offset); + scatterwalk_map_and_copy(&hash, tail_sgl, 0, HEH_BLOCK_SIZE, 0); + be128_xor(&hash, &hash, beta_key); + + /* Transform all full blocks except the last */ + err = heh_tfm_blocks(req, sgl, sgl, tail_offset, &hash, beta_key); + if (err) + return err; + + /* + * Recover the last full block. We know 'hash', i.e. the poly_hash() of + * the the original message. The last full block was the constant term + * of the polynomial. To recover the last full block, temporarily zero + * it, compute the poly_hash(), and take the difference from 'hash'. + */ + memset(&tmp, 0, sizeof(tmp)); + scatterwalk_map_and_copy(&tmp, tail_sgl, 0, HEH_BLOCK_SIZE, 1); + err = poly_hash(req, sgl, &tmp); + if (err) + return err; + be128_xor(&tmp, &tmp, &hash); + scatterwalk_map_and_copy(&tmp, tail_sgl, 0, HEH_BLOCK_SIZE, 1); + return 0; +} + +static int heh_hash_inv_step(struct ablkcipher_request *req, u32 flags) +{ + struct heh_req_ctx *rctx = heh_req_ctx(req); + + return heh_hash_inv(req, &rctx->beta2_key); +} + +static int heh_ecb_step_3(struct ablkcipher_request *req, u32 flags) +{ + struct heh_req_ctx *rctx = heh_req_ctx(req); + u8 partial_block[HEH_BLOCK_SIZE] __aligned(__alignof__(u32)); + unsigned int tail_offset = get_tail_offset(req->nbytes); + unsigned int partial_offset = tail_offset + HEH_BLOCK_SIZE; + unsigned int partial_len = req->nbytes - partial_offset; + + /* + * Extract the pad in req->dst at tail_offset, and xor the partial block + * with it to create encrypted partial block + */ + scatterwalk_map_and_copy(rctx->u.ecb.keystream, req->dst, tail_offset, + HEH_BLOCK_SIZE, 0); + scatterwalk_map_and_copy(partial_block, req->dst, partial_offset, + partial_len, 0); + crypto_xor(partial_block, rctx->u.ecb.keystream, partial_len); + + /* + * Store the encrypted final block and partial block back in dst_sg + */ + scatterwalk_map_and_copy(&rctx->u.ecb.tmp, req->dst, tail_offset, + HEH_BLOCK_SIZE, 1); + scatterwalk_map_and_copy(partial_block, req->dst, partial_offset, + partial_len, 1); + + return heh_hash_inv_step(req, flags); +} + +static void heh_ecb_step_2_done(struct crypto_async_request *areq, int err) +{ + return async_done(areq, err, heh_ecb_step_3); +} + +static int heh_ecb_step_2(struct ablkcipher_request *req, u32 flags) +{ + struct heh_req_ctx *rctx = heh_req_ctx(req); + unsigned int partial_len = req->nbytes % HEH_BLOCK_SIZE; + struct scatterlist *tmp_sgl; + int err; + unsigned int tail_offset = get_tail_offset(req->nbytes); + + if (partial_len == 0) + return heh_hash_inv_step(req, flags); + + /* + * Extract the final full block, store it in tmp, and then xor that with + * the value saved in u.ecb.keystream + */ + scatterwalk_map_and_copy(rctx->u.ecb.tmp, req->dst, tail_offset, + HEH_BLOCK_SIZE, 0); + crypto_xor(rctx->u.ecb.keystream, rctx->u.ecb.tmp, HEH_BLOCK_SIZE); + + /* + * Encrypt the value in rctx->u.ecb.keystream to create the pad for the + * partial block. + * We cannot encrypt stack buffers, so re-use the dst_sg to do this + * encryption to avoid a malloc. The value at tail_offset is stored in + * tmp, and will be restored later. + */ + scatterwalk_map_and_copy(rctx->u.ecb.keystream, req->dst, tail_offset, + HEH_BLOCK_SIZE, 1); + tmp_sgl = scatterwalk_ffwd(rctx->u.ecb.tmp_sgl, req->dst, tail_offset); + ablkcipher_request_set_callback(&rctx->u.ecb.req, flags, + heh_ecb_step_2_done, req); + ablkcipher_request_set_crypt(&rctx->u.ecb.req, tmp_sgl, tmp_sgl, + HEH_BLOCK_SIZE, NULL); + err = crypto_ablkcipher_encrypt(&rctx->u.ecb.req); + if (err) + return err; + return heh_ecb_step_3(req, flags); +} + +static void heh_ecb_full_done(struct crypto_async_request *areq, int err) +{ + return async_done(areq, err, heh_ecb_step_2); +} + +/* + * The encrypt phase of HEH. This uses ECB encryption, with special handling + * for the partial block at the end if any. The source data is already in + * req->dst, so the encryption happens in-place. + * + * After the encrypt phase we continue on to the inverse hash phase. The + * functions calls are chained to support asynchronous ECB algorithms. + */ +static int heh_ecb(struct ablkcipher_request *req, bool decrypt) +{ + struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req); + struct heh_tfm_ctx *ctx = crypto_ablkcipher_ctx(tfm); + struct heh_req_ctx *rctx = heh_req_ctx(req); + struct ablkcipher_request *ecb_req = &rctx->u.ecb.req; + unsigned int tail_offset = get_tail_offset(req->nbytes); + unsigned int full_len = tail_offset + HEH_BLOCK_SIZE; + int err; + + /* + * Save the last full block before it is encrypted/decrypted. This will + * be used later to encrypt/decrypt the partial block + */ + scatterwalk_map_and_copy(rctx->u.ecb.keystream, req->dst, tail_offset, + HEH_BLOCK_SIZE, 0); + + /* Encrypt/decrypt all full blocks */ + ablkcipher_request_set_tfm(ecb_req, ctx->ecb); + ablkcipher_request_set_callback(ecb_req, req->base.flags, + heh_ecb_full_done, req); + ablkcipher_request_set_crypt(ecb_req, req->dst, req->dst, full_len, + NULL); + if (decrypt) + err = crypto_ablkcipher_decrypt(ecb_req); + else + err = crypto_ablkcipher_encrypt(ecb_req); + if (err) + return err; + + return heh_ecb_step_2(req, req->base.flags); +} + +static int heh_crypt(struct ablkcipher_request *req, bool decrypt) +{ + struct heh_req_ctx *rctx = heh_req_ctx(req); + int err; + + /* Inputs must be at least one full block */ + if (req->nbytes < HEH_BLOCK_SIZE) + return -EINVAL; + + err = generate_betas(req, &rctx->beta1_key, &rctx->beta2_key); + if (err) + return err; + + if (decrypt) + swap(rctx->beta1_key, rctx->beta2_key); + + err = heh_hash(req, &rctx->beta1_key); + if (err) + return err; + + return heh_ecb(req, decrypt); +} + +static int heh_encrypt(struct ablkcipher_request *req) +{ + return heh_crypt(req, false); +} + +static int heh_decrypt(struct ablkcipher_request *req) +{ + return heh_crypt(req, true); +} + +static int heh_setkey(struct crypto_ablkcipher *parent, const u8 *key, + unsigned int keylen) +{ + struct heh_tfm_ctx *ctx = crypto_ablkcipher_ctx(parent); + struct crypto_shash *cmac = ctx->cmac; + struct crypto_ablkcipher *ecb = ctx->ecb; + SHASH_DESC_ON_STACK(desc, cmac); + u8 *derived_keys; + u8 digest[HEH_BLOCK_SIZE]; + unsigned int i; + int err; + + /* set prf_key = key */ + crypto_shash_clear_flags(cmac, CRYPTO_TFM_REQ_MASK); + crypto_shash_set_flags(cmac, crypto_ablkcipher_get_flags(parent) & + CRYPTO_TFM_REQ_MASK); + err = crypto_shash_setkey(cmac, key, keylen); + crypto_ablkcipher_set_flags(parent, crypto_shash_get_flags(cmac) & + CRYPTO_TFM_RES_MASK); + if (err) + return err; + + /* + * Generate tau_key and ecb_key as follows: + * tau_key = cmac(prf_key, 0x00...01) + * ecb_key = cmac(prf_key, 0x00...02) || cmac(prf_key, 0x00...03) || ... + * truncated to keylen bytes + */ + derived_keys = kzalloc(round_up(HEH_BLOCK_SIZE + keylen, + HEH_BLOCK_SIZE), GFP_KERNEL); + if (!derived_keys) + return -ENOMEM; + desc->tfm = cmac; + desc->flags = (crypto_shash_get_flags(cmac) & CRYPTO_TFM_REQ_MASK); + for (i = 0; i < keylen + HEH_BLOCK_SIZE; i += HEH_BLOCK_SIZE) { + derived_keys[i + HEH_BLOCK_SIZE - 1] = + 0x01 + i / HEH_BLOCK_SIZE; + err = crypto_shash_digest(desc, derived_keys + i, + HEH_BLOCK_SIZE, digest); + if (err) + goto out; + memcpy(derived_keys + i, digest, HEH_BLOCK_SIZE); + } + + err = crypto_shash_setkey(ctx->poly_hash, derived_keys, HEH_BLOCK_SIZE); + if (err) + goto out; + + crypto_ablkcipher_clear_flags(ecb, CRYPTO_TFM_REQ_MASK); + crypto_ablkcipher_set_flags(ecb, crypto_ablkcipher_get_flags(parent) & + CRYPTO_TFM_REQ_MASK); + err = crypto_ablkcipher_setkey(ecb, derived_keys + HEH_BLOCK_SIZE, + keylen); + crypto_ablkcipher_set_flags(parent, crypto_ablkcipher_get_flags(ecb) & + CRYPTO_TFM_RES_MASK); +out: + kzfree(derived_keys); + return err; +} + +static int heh_init_tfm(struct crypto_tfm *tfm) +{ + struct crypto_instance *inst = crypto_tfm_alg_instance(tfm); + struct heh_instance_ctx *ictx = crypto_instance_ctx(inst); + struct heh_tfm_ctx *ctx = crypto_tfm_ctx(tfm); + struct crypto_shash *cmac; + struct crypto_shash *poly_hash; + struct crypto_ablkcipher *ecb; + unsigned int reqsize; + int err; + + cmac = crypto_spawn_shash(&ictx->cmac); + if (IS_ERR(cmac)) + return PTR_ERR(cmac); + + poly_hash = crypto_spawn_shash(&ictx->poly_hash); + err = PTR_ERR(poly_hash); + if (IS_ERR(poly_hash)) + goto err_free_cmac; + + ecb = crypto_spawn_skcipher(&ictx->ecb); + err = PTR_ERR(ecb); + if (IS_ERR(ecb)) + goto err_free_poly_hash; + + ctx->cmac = cmac; + ctx->poly_hash = poly_hash; + ctx->ecb = ecb; + + reqsize = crypto_tfm_alg_alignmask(tfm) & + ~(crypto_tfm_ctx_alignment() - 1); + reqsize += max3(offsetof(struct heh_req_ctx, u.cmac.desc) + + sizeof(struct shash_desc) + + crypto_shash_descsize(cmac), + offsetof(struct heh_req_ctx, u.poly_hash.desc) + + sizeof(struct shash_desc) + + crypto_shash_descsize(poly_hash), + offsetof(struct heh_req_ctx, u.ecb.req) + + sizeof(struct ablkcipher_request) + + crypto_ablkcipher_reqsize(ecb)); + tfm->crt_ablkcipher.reqsize = reqsize; + + return 0; + +err_free_poly_hash: + crypto_free_shash(poly_hash); +err_free_cmac: + crypto_free_shash(cmac); + return err; +} + +static void heh_exit_tfm(struct crypto_tfm *tfm) +{ + struct heh_tfm_ctx *ctx = crypto_tfm_ctx(tfm); + + crypto_free_shash(ctx->cmac); + crypto_free_shash(ctx->poly_hash); + crypto_free_ablkcipher(ctx->ecb); +} + +static void heh_free_instance(struct crypto_instance *inst) +{ + struct heh_instance_ctx *ctx = crypto_instance_ctx(inst); + + crypto_drop_shash(&ctx->cmac); + crypto_drop_shash(&ctx->poly_hash); + crypto_drop_skcipher(&ctx->ecb); + kfree(inst); +} + +/* + * Create an instance of HEH as a ablkcipher. + * + * This relies on underlying CMAC and ECB algorithms, usually cmac(aes) and + * ecb(aes). For performance reasons we support asynchronous ECB algorithms. + * However, we do not yet support asynchronous CMAC algorithms because CMAC is + * only used on a small fixed amount of data per request, independent of the + * request length. This would change if AEAD or variable-length nonce support + * were to be exposed. + */ +static int heh_create_common(struct crypto_template *tmpl, struct rtattr **tb, + const char *full_name, const char *cmac_name, + const char *poly_hash_name, const char *ecb_name) +{ + struct crypto_attr_type *algt; + struct crypto_instance *inst; + struct heh_instance_ctx *ctx; + struct shash_alg *cmac; + struct shash_alg *poly_hash; + struct crypto_alg *ecb; + int err; + + algt = crypto_get_attr_type(tb); + if (IS_ERR(algt)) + return PTR_ERR(algt); + + /* User must be asking for something compatible with ablkcipher */ + if ((algt->type ^ CRYPTO_ALG_TYPE_ABLKCIPHER) & algt->mask) + return -EINVAL; + + /* Allocate the ablkcipher instance */ + inst = kzalloc(sizeof(*inst) + sizeof(*ctx), GFP_KERNEL); + if (!inst) + return -ENOMEM; + + ctx = crypto_instance_ctx(inst); + + /* Set up the cmac spawn */ + ctx->cmac.base.inst = inst; + err = crypto_grab_shash(&ctx->cmac, cmac_name, 0, 0); + if (err) + goto err_free_inst; + cmac = crypto_spawn_shash_alg(&ctx->cmac); + err = -EINVAL; + if (cmac->digestsize != HEH_BLOCK_SIZE) + goto err_drop_cmac; + + /* Set up the poly_hash spawn */ + ctx->poly_hash.base.inst = inst; + err = crypto_grab_shash(&ctx->poly_hash, poly_hash_name, 0, 0); + if (err) + goto err_drop_cmac; + poly_hash = crypto_spawn_shash_alg(&ctx->poly_hash); + err = -EINVAL; + if (poly_hash->digestsize != HEH_BLOCK_SIZE) + goto err_drop_poly_hash; + + /* Set up the ecb spawn */ + ctx->ecb.base.inst = inst; + err = crypto_grab_skcipher(&ctx->ecb, ecb_name, 0, + crypto_requires_sync(algt->type, + algt->mask)); + if (err) + goto err_drop_poly_hash; + ecb = crypto_skcipher_spawn_alg(&ctx->ecb); + + /* HEH only supports block ciphers with 16 byte block size */ + err = -EINVAL; + if (ecb->cra_blocksize != HEH_BLOCK_SIZE) + goto err_drop_ecb; + + /* The underlying "ECB" algorithm must not require an IV */ + err = -EINVAL; + if ((ecb->cra_flags & CRYPTO_ALG_TYPE_MASK) == CRYPTO_ALG_TYPE_BLKCIPHER) { + if (ecb->cra_blkcipher.ivsize != 0) + goto err_drop_ecb; + } else { + if (ecb->cra_ablkcipher.ivsize != 0) + goto err_drop_ecb; + } + + /* Set the instance names */ + err = -ENAMETOOLONG; + if (snprintf(inst->alg.cra_driver_name, CRYPTO_MAX_ALG_NAME, + "heh_base(%s,%s,%s)", cmac->base.cra_driver_name, + poly_hash->base.cra_driver_name, + ecb->cra_driver_name) >= CRYPTO_MAX_ALG_NAME) + goto err_drop_ecb; + + err = -ENAMETOOLONG; + if (snprintf(inst->alg.cra_name, CRYPTO_MAX_ALG_NAME, + "%s", full_name) >= CRYPTO_MAX_ALG_NAME) + goto err_drop_ecb; + + /* Finish initializing the instance */ + + inst->alg.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | + (ecb->cra_flags & CRYPTO_ALG_ASYNC); + inst->alg.cra_blocksize = HEH_BLOCK_SIZE; + inst->alg.cra_ctxsize = sizeof(struct heh_tfm_ctx); + inst->alg.cra_alignmask = ecb->cra_alignmask | (__alignof__(be128) - 1); + inst->alg.cra_priority = ecb->cra_priority; + inst->alg.cra_type = &crypto_ablkcipher_type; + inst->alg.cra_init = heh_init_tfm; + inst->alg.cra_exit = heh_exit_tfm; + + inst->alg.cra_ablkcipher.setkey = heh_setkey; + inst->alg.cra_ablkcipher.encrypt = heh_encrypt; + inst->alg.cra_ablkcipher.decrypt = heh_decrypt; + if ((ecb->cra_flags & CRYPTO_ALG_TYPE_MASK) == CRYPTO_ALG_TYPE_BLKCIPHER) { + inst->alg.cra_ablkcipher.min_keysize = ecb->cra_blkcipher.min_keysize; + inst->alg.cra_ablkcipher.max_keysize = ecb->cra_blkcipher.max_keysize; + } else { + inst->alg.cra_ablkcipher.min_keysize = ecb->cra_ablkcipher.min_keysize; + inst->alg.cra_ablkcipher.max_keysize = ecb->cra_ablkcipher.max_keysize; + } + inst->alg.cra_ablkcipher.ivsize = HEH_BLOCK_SIZE; + + /* Register the instance */ + err = crypto_register_instance(tmpl, inst); + if (err) + goto err_drop_ecb; + return 0; + +err_drop_ecb: + crypto_drop_skcipher(&ctx->ecb); +err_drop_poly_hash: + crypto_drop_shash(&ctx->poly_hash); +err_drop_cmac: + crypto_drop_shash(&ctx->cmac); +err_free_inst: + kfree(inst); + return err; +} + +static int heh_create(struct crypto_template *tmpl, struct rtattr **tb) +{ + const char *cipher_name; + char full_name[CRYPTO_MAX_ALG_NAME]; + char cmac_name[CRYPTO_MAX_ALG_NAME]; + char ecb_name[CRYPTO_MAX_ALG_NAME]; + + /* Get the name of the requested block cipher (e.g. aes) */ + cipher_name = crypto_attr_alg_name(tb[1]); + if (IS_ERR(cipher_name)) + return PTR_ERR(cipher_name); + + if (snprintf(full_name, CRYPTO_MAX_ALG_NAME, "heh(%s)", cipher_name) >= + CRYPTO_MAX_ALG_NAME) + return -ENAMETOOLONG; + + if (snprintf(cmac_name, CRYPTO_MAX_ALG_NAME, "cmac(%s)", cipher_name) >= + CRYPTO_MAX_ALG_NAME) + return -ENAMETOOLONG; + + if (snprintf(ecb_name, CRYPTO_MAX_ALG_NAME, "ecb(%s)", cipher_name) >= + CRYPTO_MAX_ALG_NAME) + return -ENAMETOOLONG; + + return heh_create_common(tmpl, tb, full_name, cmac_name, "poly_hash", + ecb_name); +} + +static struct crypto_template heh_tmpl = { + .name = "heh", + .create = heh_create, + .free = heh_free_instance, + .module = THIS_MODULE, +}; + +static int heh_base_create(struct crypto_template *tmpl, struct rtattr **tb) +{ + char full_name[CRYPTO_MAX_ALG_NAME]; + const char *cmac_name; + const char *poly_hash_name; + const char *ecb_name; + + cmac_name = crypto_attr_alg_name(tb[1]); + if (IS_ERR(cmac_name)) + return PTR_ERR(cmac_name); + + poly_hash_name = crypto_attr_alg_name(tb[2]); + if (IS_ERR(poly_hash_name)) + return PTR_ERR(poly_hash_name); + + ecb_name = crypto_attr_alg_name(tb[3]); + if (IS_ERR(ecb_name)) + return PTR_ERR(ecb_name); + + if (snprintf(full_name, CRYPTO_MAX_ALG_NAME, "heh_base(%s,%s,%s)", + cmac_name, poly_hash_name, ecb_name) >= + CRYPTO_MAX_ALG_NAME) + return -ENAMETOOLONG; + + return heh_create_common(tmpl, tb, full_name, cmac_name, poly_hash_name, + ecb_name); +} + +/* + * If HEH is instantiated as "heh_base" instead of "heh", then specific + * implementations of cmac, poly_hash, and ecb can be specified instead of just + * the cipher. + */ +static struct crypto_template heh_base_tmpl = { + .name = "heh_base", + .create = heh_base_create, + .free = heh_free_instance, + .module = THIS_MODULE, +}; + +static int __init heh_module_init(void) +{ + int err; + + err = crypto_register_template(&heh_tmpl); + if (err) + return err; + + err = crypto_register_template(&heh_base_tmpl); + if (err) + goto out_undo_heh; + + err = crypto_register_shash(&poly_hash_alg); + if (err) + goto out_undo_heh_base; + + return 0; + +out_undo_heh_base: + crypto_unregister_template(&heh_base_tmpl); +out_undo_heh: + crypto_unregister_template(&heh_tmpl); + return err; +} + +static void __exit heh_module_exit(void) +{ + crypto_unregister_template(&heh_tmpl); + crypto_unregister_template(&heh_base_tmpl); + crypto_unregister_shash(&poly_hash_alg); +} + +module_init(heh_module_init); +module_exit(heh_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Hash-Encrypt-Hash block cipher mode"); +MODULE_ALIAS_CRYPTO("heh"); +MODULE_ALIAS_CRYPTO("heh_base"); diff --git a/crypto/mcryptd.c b/crypto/mcryptd.c index a0ceb41d5ccc..b4f3930266b1 100644 --- a/crypto/mcryptd.c +++ b/crypto/mcryptd.c @@ -531,6 +531,7 @@ static int mcryptd_create_hash(struct crypto_template *tmpl, struct rtattr **tb, inst->alg.halg.base.cra_flags = type; inst->alg.halg.digestsize = salg->digestsize; + inst->alg.halg.statesize = salg->statesize; inst->alg.halg.base.cra_ctxsize = sizeof(struct mcryptd_hash_ctx); inst->alg.halg.base.cra_init = mcryptd_hash_init_tfm; diff --git a/crypto/shash.c b/crypto/shash.c index 359754591653..9ae1e891308d 100644 --- a/crypto/shash.c +++ b/crypto/shash.c @@ -683,6 +683,14 @@ void shash_free_instance(struct crypto_instance *inst) } EXPORT_SYMBOL_GPL(shash_free_instance); +int crypto_grab_shash(struct crypto_shash_spawn *spawn, + const char *name, u32 type, u32 mask) +{ + spawn->base.frontend = &crypto_shash_type; + return crypto_grab_spawn(&spawn->base, name, type, mask); +} +EXPORT_SYMBOL_GPL(crypto_grab_shash); + int crypto_init_shash_spawn(struct crypto_shash_spawn *spawn, struct shash_alg *alg, struct crypto_instance *inst) diff --git a/crypto/testmgr.c b/crypto/testmgr.c index d4944318ca1f..6d4da8fd24fd 100644 --- a/crypto/testmgr.c +++ b/crypto/testmgr.c @@ -488,6 +488,8 @@ static int __test_aead(struct crypto_aead *tfm, int enc, aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, tcrypt_complete, &result); + iv_len = crypto_aead_ivsize(tfm); + for (i = 0, j = 0; i < tcount; i++) { if (template[i].np) continue; @@ -508,7 +510,6 @@ static int __test_aead(struct crypto_aead *tfm, int enc, memcpy(input, template[i].input, template[i].ilen); memcpy(assoc, template[i].assoc, template[i].alen); - iv_len = crypto_aead_ivsize(tfm); if (template[i].iv) memcpy(iv, template[i].iv, iv_len); else @@ -617,7 +618,7 @@ static int __test_aead(struct crypto_aead *tfm, int enc, j++; if (template[i].iv) - memcpy(iv, template[i].iv, MAX_IVLEN); + memcpy(iv, template[i].iv, iv_len); else memset(iv, 0, MAX_IVLEN); @@ -3214,6 +3215,21 @@ static const struct alg_test_desc alg_test_descs[] = { } } }, { + .alg = "heh(aes)", + .test = alg_test_skcipher, + .suite = { + .cipher = { + .enc = { + .vecs = aes_heh_enc_tv_template, + .count = AES_HEH_ENC_TEST_VECTORS + }, + .dec = { + .vecs = aes_heh_dec_tv_template, + .count = AES_HEH_DEC_TEST_VECTORS + } + } + } + }, { .alg = "hmac(crc32)", .test = alg_test_hash, .suite = { diff --git a/crypto/testmgr.h b/crypto/testmgr.h index 0e02c60a57b6..ba6530d8ba58 100644 --- a/crypto/testmgr.h +++ b/crypto/testmgr.h @@ -14139,6 +14139,8 @@ static struct cipher_testvec cast6_xts_dec_tv_template[] = { #define AES_DEC_TEST_VECTORS 4 #define AES_CBC_ENC_TEST_VECTORS 5 #define AES_CBC_DEC_TEST_VECTORS 5 +#define AES_HEH_ENC_TEST_VECTORS 4 +#define AES_HEH_DEC_TEST_VECTORS 4 #define HMAC_MD5_ECB_CIPHER_NULL_ENC_TEST_VECTORS 2 #define HMAC_MD5_ECB_CIPHER_NULL_DEC_TEST_VECTORS 2 #define HMAC_SHA1_ECB_CIPHER_NULL_ENC_TEST_VEC 2 @@ -14511,6 +14513,198 @@ static struct cipher_testvec aes_dec_tv_template[] = { }, }; +static struct cipher_testvec aes_heh_enc_tv_template[] = { + { + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", + .klen = 16, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", + .ilen = 16, + .result = "\xd8\xbd\x40\xbf\xca\xe5\xee\x81" + "\x0f\x3d\x1f\x1f\xae\x89\x07\x55", + .rlen = 16, + .also_non_np = 1, + .np = 2, + .tap = { 8, 8 }, + }, { + .key = "\xa8\xda\x24\x9b\x5e\xfa\x13\xc2" + "\xc1\x94\xbf\x32\xba\x38\xa3\x77", + .klen = 16, + .iv = "\x4d\x47\x61\x37\x2b\x47\x86\xf0" + "\xd6\x47\xb5\xc2\xe8\xcf\x85\x27", + .input = "\xb8\xee\x29\xe4\xa5\xd1\xe7\x55" + "\xd0\xfd\xe7\x22\x63\x76\x36\xe2" + "\xf8\x0c\xf8\xfe\x65\x76\xe7\xca" + "\xc1\x42\xf5\xca\x5a\xa8\xac\x2a", + .ilen = 32, + .result = "\x59\xf2\x78\x4e\x10\x94\xf9\x5c" + "\x22\x23\x78\x2a\x30\x48\x11\x97" + "\xb1\xfe\x70\xc4\xef\xdf\x04\xef" + "\x16\x39\x04\xcf\xc0\x95\x9a\x98", + .rlen = 32, + .also_non_np = 1, + .np = 3, + .tap = { 16, 13, 3 }, + }, { + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", + .klen = 16, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00", + .ilen = 63, + .result = "\xe0\x40\xeb\xe9\x52\xbe\x65\x60" + "\xe4\x68\x68\xa3\x73\x75\xb8\x52" + "\xef\x38\x6a\x87\x25\x25\xf6\x04" + "\xe5\x8e\xbe\x14\x8b\x02\x14\x1f" + "\xa9\x73\xb7\xad\x15\xbe\x9c\xa0" + "\xd2\x8a\x2c\xdc\xd4\xe3\x05\x55" + "\x0a\xf5\xf8\x51\xee\xe5\x62\xa5" + "\x71\xa7\x7c\x15\x5d\x7a\x9e", + .rlen = 63, + .also_non_np = 1, + .np = 8, + .tap = { 20, 20, 10, 8, 2, 1, 1, 1 }, + }, { + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", + .klen = 16, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x01" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00", + .ilen = 63, + .result = "\x4b\x1a\x15\xa0\xaf\x08\x6d\x70" + "\xf0\xa7\x97\xb5\x31\x4b\x8c\xc3" + "\x4d\xf2\x7a\x9d\xdd\xd4\x15\x99" + "\x57\xad\xc6\xb1\x35\x69\xf5\x6a" + "\x2d\x70\xe4\x97\x49\xb2\x9f\x71" + "\xde\x22\xb5\x70\x8c\x69\x24\xd3" + "\xad\x80\x58\x48\x90\xe4\xed\xba" + "\x76\x3d\x71\x7c\x57\x25\x87", + .rlen = 63, + .also_non_np = 1, + .np = 8, + .tap = { 20, 20, 10, 8, 2, 1, 1, 1 }, + } +}; + +static struct cipher_testvec aes_heh_dec_tv_template[] = { + { + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", + .klen = 16, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\xd8\xbd\x40\xbf\xca\xe5\xee\x81" + "\x0f\x3d\x1f\x1f\xae\x89\x07\x55", + .ilen = 16, + .result = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", + .rlen = 16, + .also_non_np = 1, + .np = 2, + .tap = { 8, 8 }, + }, { + .key = "\xa8\xda\x24\x9b\x5e\xfa\x13\xc2" + "\xc1\x94\xbf\x32\xba\x38\xa3\x77", + .klen = 16, + .iv = "\x4d\x47\x61\x37\x2b\x47\x86\xf0" + "\xd6\x47\xb5\xc2\xe8\xcf\x85\x27", + .input = "\x59\xf2\x78\x4e\x10\x94\xf9\x5c" + "\x22\x23\x78\x2a\x30\x48\x11\x97" + "\xb1\xfe\x70\xc4\xef\xdf\x04\xef" + "\x16\x39\x04\xcf\xc0\x95\x9a\x98", + .ilen = 32, + .result = "\xb8\xee\x29\xe4\xa5\xd1\xe7\x55" + "\xd0\xfd\xe7\x22\x63\x76\x36\xe2" + "\xf8\x0c\xf8\xfe\x65\x76\xe7\xca" + "\xc1\x42\xf5\xca\x5a\xa8\xac\x2a", + .rlen = 32, + .also_non_np = 1, + .np = 3, + .tap = { 16, 13, 3 }, + }, { + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", + .klen = 16, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\xe0\x40\xeb\xe9\x52\xbe\x65\x60" + "\xe4\x68\x68\xa3\x73\x75\xb8\x52" + "\xef\x38\x6a\x87\x25\x25\xf6\x04" + "\xe5\x8e\xbe\x14\x8b\x02\x14\x1f" + "\xa9\x73\xb7\xad\x15\xbe\x9c\xa0" + "\xd2\x8a\x2c\xdc\xd4\xe3\x05\x55" + "\x0a\xf5\xf8\x51\xee\xe5\x62\xa5" + "\x71\xa7\x7c\x15\x5d\x7a\x9e", + .ilen = 63, + .result = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00", + .rlen = 63, + .also_non_np = 1, + .np = 8, + .tap = { 20, 20, 10, 8, 2, 1, 1, 1 }, + }, { + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", + .klen = 16, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x4b\x1a\x15\xa0\xaf\x08\x6d\x70" + "\xf0\xa7\x97\xb5\x31\x4b\x8c\xc3" + "\x4d\xf2\x7a\x9d\xdd\xd4\x15\x99" + "\x57\xad\xc6\xb1\x35\x69\xf5\x6a" + "\x2d\x70\xe4\x97\x49\xb2\x9f\x71" + "\xde\x22\xb5\x70\x8c\x69\x24\xd3" + "\xad\x80\x58\x48\x90\xe4\xed\xba" + "\x76\x3d\x71\x7c\x57\x25\x87", + .ilen = 63, + .result = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x01" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00", + .rlen = 63, + .also_non_np = 1, + .np = 8, + .tap = { 20, 20, 10, 8, 2, 1, 1, 1 }, + } +}; + static struct cipher_testvec aes_cbc_enc_tv_template[] = { { /* From RFC 3602 */ .key = "\x06\xa9\x21\x40\x36\xb8\xa1\x5b" diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 675eaf337178..b9cebca376f9 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -2,7 +2,6 @@ # Makefile for the Linux ACPI interpreter # -ccflags-y := -Os ccflags-$(CONFIG_ACPI_DEBUG) += -DACPI_DEBUG_OUTPUT # diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index 296b7a14893a..5365ff6e69c1 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c @@ -24,9 +24,11 @@ ACPI_MODULE_NAME("platform"); static const struct acpi_device_id forbidden_id_list[] = { - {"PNP0000", 0}, /* PIC */ - {"PNP0100", 0}, /* Timer */ - {"PNP0200", 0}, /* AT DMA Controller */ + {"PNP0000", 0}, /* PIC */ + {"PNP0100", 0}, /* Timer */ + {"PNP0200", 0}, /* AT DMA Controller */ + {"ACPI0009", 0}, /* IOxAPIC */ + {"ACPI000A", 0}, /* IOAPIC */ {"", 0}, }; diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index 5fdac394207a..549cdbed7b0e 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -1211,6 +1211,9 @@ static int acpi_video_device_enumerate(struct acpi_video_bus *video) union acpi_object *dod = NULL; union acpi_object *obj; + if (!video->cap._DOD) + return AE_NOT_EXIST; + status = acpi_evaluate_object(video->device->handle, "_DOD", NULL, &buffer); if (!ACPI_SUCCESS(status)) { ACPI_EXCEPTION((AE_INFO, status, "Evaluating _DOD")); diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c index 96809cd99ace..2f24b578bcaf 100644 --- a/drivers/acpi/blacklist.c +++ b/drivers/acpi/blacklist.c @@ -346,6 +346,34 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { DMI_MATCH(DMI_PRODUCT_NAME, "XPS 13 9343"), }, }, + { + .callback = dmi_enable_rev_override, + .ident = "DELL Precision 5520", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Precision 5520"), + }, + }, + { + .callback = dmi_enable_rev_override, + .ident = "DELL Precision 3520", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Precision 3520"), + }, + }, + /* + * Resolves a quirk with the Dell Latitude 3350 that + * causes the ethernet adapter to not function. + */ + { + .callback = dmi_enable_rev_override, + .ident = "DELL Latitude 3350", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Latitude 3350"), + }, + }, #endif {} }; diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c index 14c2a07c9f3f..67d7489ced01 100644 --- a/drivers/acpi/nfit.c +++ b/drivers/acpi/nfit.c @@ -979,7 +979,11 @@ static int cmp_map(const void *m0, const void *m1) const struct nfit_set_info_map *map0 = m0; const struct nfit_set_info_map *map1 = m1; - return map0->region_offset - map1->region_offset; + if (map0->region_offset < map1->region_offset) + return -1; + else if (map0->region_offset > map1->region_offset) + return 1; + return 0; } /* Retrieve the nth entry referencing this spa */ diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index fcd4ce6f78d5..1c2b846c5776 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -200,6 +200,7 @@ static int acpi_power_get_list_state(struct list_head *list, int *state) return -EINVAL; /* The state of the list is 'on' IFF all resources are 'on'. */ + cur_state = 0; list_for_each_entry(entry, list, node) { struct acpi_power_resource *resource = entry->resource; acpi_handle handle = resource->device.handle; diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index 09c07f519952..0e494108c20c 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -1042,7 +1042,7 @@ static int print_wakeup_source_stats(struct seq_file *m, active_time = ktime_set(0, 0); } - seq_printf(m, "%-12s\t%lu\t\t%lu\t\t%lu\t\t%lu\t\t%lld\t\t%lld\t\t%lld\t\t%lld\t\t%lld\n", + seq_printf(m, "%-32s\t%lu\t\t%lu\t\t%lu\t\t%lu\t\t%lld\t\t%lld\t\t%lld\t\t%lld\t\t%lld\n", ws->name, active_count, ws->event_count, ws->wakeup_count, ws->expire_count, ktime_to_ms(active_time), ktime_to_ms(total_time), @@ -1062,7 +1062,7 @@ static int wakeup_sources_stats_show(struct seq_file *m, void *unused) { struct wakeup_source *ws; - seq_puts(m, "name\t\tactive_count\tevent_count\twakeup_count\t" + seq_puts(m, "name\t\t\t\t\tactive_count\tevent_count\twakeup_count\t" "expire_count\tactive_since\ttotal_time\tmax_time\t" "last_change\tprevent_suspend_time\n"); diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 76ac3179f25c..c5a2057ef668 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -581,13 +581,13 @@ static int zram_decompress_page(struct zram *zram, char *mem, u32 index) if (!handle || zram_test_flag(meta, index, ZRAM_ZERO)) { bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value); - clear_page(mem); + memset(mem, 0, PAGE_SIZE); return 0; } cmem = zs_map_object(meta->mem_pool, handle, ZS_MM_RO); if (size == PAGE_SIZE) - copy_page(mem, cmem); + memcpy(mem, cmem, PAGE_SIZE); else ret = zcomp_decompress(zram->comp, cmem, size, mem); zs_unmap_object(meta->mem_pool, handle); @@ -750,7 +750,7 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, if ((clen == PAGE_SIZE) && !is_partial_io(bvec)) { src = kmap_atomic(page); - copy_page(cmem, src); + memcpy(cmem, src, PAGE_SIZE); kunmap_atomic(src); } else { memcpy(cmem, src, clen); diff --git a/drivers/bluetooth/btfm_slim.h b/drivers/bluetooth/btfm_slim.h index 5d105fba2193..e67c6964ee65 100644 --- a/drivers/bluetooth/btfm_slim.h +++ b/drivers/bluetooth/btfm_slim.h @@ -13,7 +13,7 @@ #define BTFM_SLIM_H #include <linux/slimbus/slimbus.h> -#define BTFMSLIM_DBG(fmt, arg...) pr_debug(fmt "\n", ## arg) +#define BTFMSLIM_DBG(fmt, arg...) pr_debug("%s: " fmt "\n", __func__, ## arg) #define BTFMSLIM_INFO(fmt, arg...) pr_info("%s: " fmt "\n", __func__, ## arg) #define BTFMSLIM_ERR(fmt, arg...) pr_err("%s: " fmt "\n", __func__, ## arg) diff --git a/drivers/bluetooth/btfm_slim_wcn3990.c b/drivers/bluetooth/btfm_slim_wcn3990.c index c93b29281e35..a451ff33103c 100644 --- a/drivers/bluetooth/btfm_slim_wcn3990.c +++ b/drivers/bluetooth/btfm_slim_wcn3990.c @@ -83,19 +83,34 @@ int btfm_slim_chrk_enable_port(struct btfmslim *btfmslim, uint8_t port_num, { int ret = 0; uint8_t reg_val = 0; + uint8_t port_bit = 0; uint16_t reg; BTFMSLIM_DBG("port(%d) enable(%d)", port_num, enable); if (rxport) { + if (enable) { + /* For SCO Rx, A2DP Rx */ + reg_val = 0x1; + port_bit = port_num - 0x10; + reg = CHRK_SB_PGD_RX_PORTn_MULTI_CHNL_0(port_bit); + BTFMSLIM_DBG("writing reg_val (%d) to reg(%x)", + reg_val, reg); + ret = btfm_slim_write(btfmslim, reg, 1, ®_val, IFD); + if (ret) { + BTFMSLIM_ERR("failed to write (%d) reg 0x%x", + ret, reg); + goto error; + } + } /* Port enable */ reg = CHRK_SB_PGD_PORT_RX_CFGN(port_num - 0x10); goto enable_disable_rxport; } - /* txport */ if (!enable) goto enable_disable_txport; - /* Multiple Channel Setting - only for FM Tx */ + /* txport */ + /* Multiple Channel Setting */ if (is_fm_port(port_num)) { reg_val = (0x1 << CHRK_SB_PGD_PORT_TX1_FM) | (0x1 << CHRK_SB_PGD_PORT_TX2_FM); @@ -105,6 +120,18 @@ int btfm_slim_chrk_enable_port(struct btfmslim *btfmslim, uint8_t port_num, BTFMSLIM_ERR("failed to write (%d) reg 0x%x", ret, reg); goto error; } + } else if (port_num == CHRK_SB_PGD_PORT_TX_SCO) { + /* SCO Tx */ + reg_val = 0x1 << CHRK_SB_PGD_PORT_TX_SCO; + reg = CHRK_SB_PGD_TX_PORTn_MULTI_CHNL_0(port_num); + BTFMSLIM_DBG("writing reg_val (%d) to reg(%x)", + reg_val, reg); + ret = btfm_slim_write(btfmslim, reg, 1, ®_val, IFD); + if (ret) { + BTFMSLIM_ERR("failed to write (%d) reg 0x%x", + ret, reg); + goto error; + } } /* Enable Tx port hw auto recovery for underrun or overrun error */ diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 4bbe4e5f9a6d..db8eb7ccd744 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -585,10 +585,12 @@ config TELCLOCK controlling the behavior of this hardware. config DEVPORT - bool - depends on !M68K + bool "/dev/port character device" depends on ISA || PCI default y + help + Say Y here if you want to support the /dev/port device. The /dev/port + device is similar to /dev/mem, but for I/O ports. source "drivers/s390/char/Kconfig" diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index 10c4d8ce2410..b1432ccf5358 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -1465,6 +1465,7 @@ static void fastrpc_init(struct fastrpc_apps *me) { int i; INIT_HLIST_HEAD(&me->drivers); + INIT_HLIST_HEAD(&me->maps); spin_lock_init(&me->hlock); mutex_init(&me->smd_mutex); me->channel = &gcinfo[0]; @@ -2125,11 +2126,9 @@ void fastrpc_glink_notify_state(void *handle, const void *priv, unsigned event) link->port_state = FASTRPC_LINK_DISCONNECTED; break; case GLINK_REMOTE_DISCONNECTED: - if (me->channel[cid].chan && - link->link_state == FASTRPC_LINK_STATE_UP) { + if (me->channel[cid].chan) { fastrpc_glink_close(me->channel[cid].chan, cid); me->channel[cid].chan = 0; - link->port_state = FASTRPC_LINK_DISCONNECTED; } break; default: @@ -2295,10 +2294,9 @@ static int fastrpc_glink_open(int cid) if (err) goto bail; - if (link->port_state == FASTRPC_LINK_CONNECTED || - link->port_state == FASTRPC_LINK_CONNECTING) { + VERIFY(err, (link->port_state == FASTRPC_LINK_DISCONNECTED)); + if (err) goto bail; - } link->port_state = FASTRPC_LINK_CONNECTING; cfg->priv = (void *)(uintptr_t)cid; @@ -2457,7 +2455,9 @@ static int fastrpc_channel_open(struct fastrpc_file *fl) if ((kref_get_unless_zero(&me->channel[cid].kref) == 0) || (me->channel[cid].chan == 0)) { if (me->glink) { - fastrpc_glink_register(cid, me); + VERIFY(err, 0 == fastrpc_glink_register(cid, me)); + if (err) + goto bail; VERIFY(err, 0 == fastrpc_glink_open(cid)); } else { VERIFY(err, !smd_named_open_on_edge(FASTRPC_SMD_GUID, @@ -2791,7 +2791,7 @@ static int fastrpc_cb_probe(struct device *dev) start = 0x60000000; VERIFY(err, !IS_ERR_OR_NULL(sess->smmu.mapping = arm_iommu_create_mapping(&platform_bus_type, - start, 0x7fffffff))); + start, 0x70000000))); if (err) goto bail; iommu_set_fault_handler(sess->smmu.mapping->domain, diff --git a/drivers/char/diag/diag_memorydevice.c b/drivers/char/diag/diag_memorydevice.c index bd34e6cceec0..a5d92c51cc0b 100644 --- a/drivers/char/diag/diag_memorydevice.c +++ b/drivers/char/diag/diag_memorydevice.c @@ -252,6 +252,7 @@ int diag_md_copy_to_user(char __user *buf, int *pret, size_t buf_size, uint8_t drain_again = 0; uint8_t peripheral = 0; struct diag_md_session_t *session_info = NULL; + struct pid *pid_struct = NULL; mutex_lock(&driver->diagfwd_untag_mutex); @@ -278,6 +279,14 @@ int diag_md_copy_to_user(char __user *buf, int *pret, size_t buf_size, if ((info && (info->peripheral_mask & MD_PERIPHERAL_MASK(peripheral)) == 0)) goto drop_data; + pid_struct = find_get_pid(session_info->pid); + if (!pid_struct) { + err = -ESRCH; + DIAG_LOG(DIAG_DEBUG_PERIPHERALS, + "diag: No such md_session_map[%d] with pid = %d err=%d exists..\n", + peripheral, session_info->pid, err); + goto drop_data; + } /* * If the data is from remote processor, copy the remote * token first @@ -297,27 +306,35 @@ int diag_md_copy_to_user(char __user *buf, int *pret, size_t buf_size, } if (i > 0) { remote_token = diag_get_remote(i); - err = copy_to_user(buf + ret, &remote_token, - sizeof(int)); + if (get_pid_task(pid_struct, PIDTYPE_PID)) { + err = copy_to_user(buf + ret, + &remote_token, + sizeof(int)); + if (err) + goto drop_data; + ret += sizeof(int); + } + } + + /* Copy the length of data being passed */ + if (get_pid_task(pid_struct, PIDTYPE_PID)) { + err = copy_to_user(buf + ret, + (void *)&(entry->len), + sizeof(int)); if (err) goto drop_data; ret += sizeof(int); } - /* Copy the length of data being passed */ - err = copy_to_user(buf + ret, (void *)&(entry->len), - sizeof(int)); - if (err) - goto drop_data; - ret += sizeof(int); - /* Copy the actual data being passed */ - err = copy_to_user(buf + ret, (void *)entry->buf, - entry->len); - if (err) - goto drop_data; - ret += entry->len; - + if (get_pid_task(pid_struct, PIDTYPE_PID)) { + err = copy_to_user(buf + ret, + (void *)entry->buf, + entry->len); + if (err) + goto drop_data; + ret += entry->len; + } /* * The data is now copied to the user space client, * Notify that the write is complete and delete its @@ -339,7 +356,15 @@ drop_data: } *pret = ret; - err = copy_to_user(buf + sizeof(int), (void *)&num_data, sizeof(int)); + if (pid_struct && get_pid_task(pid_struct, PIDTYPE_PID)) { + err = copy_to_user(buf + sizeof(int), + (void *)&num_data, + sizeof(int)); + } else { + DIAG_LOG(DIAG_DEBUG_PERIPHERALS, + "diag: md_session_map[%d] with pid = %d Exited..\n", + peripheral, driver->md_session_map[peripheral]->pid); + } diag_ws_on_copy_complete(DIAG_WS_MUX); if (drain_again) chk_logging_wakeup(); diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c index 574a13de6a0d..dc2d9fc4282c 100644 --- a/drivers/char/diag/diagchar_core.c +++ b/drivers/char/diag/diagchar_core.c @@ -996,6 +996,11 @@ static int diag_send_raw_data_remote(int proc, void *buf, int len, hdlc_disabled = driver->hdlc_disabled; if (hdlc_disabled) { payload = *(uint16_t *)(buf + 2); + if (payload > DIAG_MAX_HDLC_BUF_SIZE) { + pr_err("diag: Dropping packet, payload size is %d\n", + payload); + return -EBADMSG; + } driver->hdlc_encode_buf_len = payload; /* * Adding 4 bytes for start (1 byte), version (1 byte) and @@ -2336,7 +2341,9 @@ long diagchar_ioctl(struct file *filp, mutex_unlock(&driver->dci_mutex); break; case DIAG_IOCTL_DCI_EVENT_STATUS: + mutex_lock(&driver->dci_mutex); result = diag_ioctl_dci_event_status(ioarg); + mutex_unlock(&driver->dci_mutex); break; case DIAG_IOCTL_DCI_CLEAR_LOGS: mutex_lock(&driver->dci_mutex); diff --git a/drivers/char/diag/diagfwd_glink.c b/drivers/char/diag/diagfwd_glink.c index 2784cf71cc2b..03d496c2dd91 100644 --- a/drivers/char/diag/diagfwd_glink.c +++ b/drivers/char/diag/diagfwd_glink.c @@ -487,6 +487,18 @@ static void diag_glink_remote_disconnect_work_fn(struct work_struct *work) atomic_set(&glink_info->tx_intent_ready, 0); } +static void diag_glink_late_init_work_fn(struct work_struct *work) +{ + struct diag_glink_info *glink_info = container_of(work, + struct diag_glink_info, + late_init_work); + if (!glink_info || !glink_info->hdl) + return; + DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "p: %d t: %d\n", + glink_info->peripheral, glink_info->type); + diagfwd_channel_open(glink_info->fwd_ctxt); +} + static void diag_glink_transport_notify_state(void *handle, const void *priv, unsigned event) { @@ -617,7 +629,7 @@ static void glink_late_init(struct diag_glink_info *glink_info) glink_info->inited = 1; if (atomic_read(&glink_info->opened)) - diagfwd_channel_open(glink_info->fwd_ctxt); + queue_work(glink_info->wq, &(glink_info->late_init_work)); DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s exiting\n", glink_info->name); @@ -665,6 +677,7 @@ static void __diag_glink_init(struct diag_glink_info *glink_info) INIT_WORK(&(glink_info->connect_work), diag_glink_connect_work_fn); INIT_WORK(&(glink_info->remote_disconnect_work), diag_glink_remote_disconnect_work_fn); + INIT_WORK(&(glink_info->late_init_work), diag_glink_late_init_work_fn); link_info.glink_link_state_notif_cb = diag_glink_notify_cb; link_info.transport = NULL; link_info.edge = glink_info->edge; diff --git a/drivers/char/diag/diagfwd_glink.h b/drivers/char/diag/diagfwd_glink.h index 5c1abeffd498..a84fa4edfca0 100644 --- a/drivers/char/diag/diagfwd_glink.h +++ b/drivers/char/diag/diagfwd_glink.h @@ -37,6 +37,7 @@ struct diag_glink_info { struct work_struct read_work; struct work_struct connect_work; struct work_struct remote_disconnect_work; + struct work_struct late_init_work; struct diagfwd_info *fwd_ctxt; }; diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 6b1721f978c2..e901463d4972 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -59,6 +59,10 @@ static inline int valid_mmap_phys_addr_range(unsigned long pfn, size_t size) #endif #ifdef CONFIG_STRICT_DEVMEM +static inline int page_is_allowed(unsigned long pfn) +{ + return devmem_is_allowed(pfn); +} static inline int range_is_allowed(unsigned long pfn, unsigned long size) { u64 from = ((u64)pfn) << PAGE_SHIFT; @@ -78,6 +82,10 @@ static inline int range_is_allowed(unsigned long pfn, unsigned long size) return 1; } #else +static inline int page_is_allowed(unsigned long pfn) +{ + return 1; +} static inline int range_is_allowed(unsigned long pfn, unsigned long size) { return 1; @@ -125,23 +133,31 @@ static ssize_t read_mem(struct file *file, char __user *buf, while (count > 0) { unsigned long remaining; + int allowed; sz = size_inside_page(p, count); - if (!range_is_allowed(p >> PAGE_SHIFT, count)) + allowed = page_is_allowed(p >> PAGE_SHIFT); + if (!allowed) return -EPERM; + if (allowed == 2) { + /* Show zeros for restricted memory. */ + remaining = clear_user(buf, sz); + } else { + /* + * On ia64 if a page has been mapped somewhere as + * uncached, then it must also be accessed uncached + * by the kernel or data corruption may occur. + */ + ptr = xlate_dev_mem_ptr(p); + if (!ptr) + return -EFAULT; - /* - * On ia64 if a page has been mapped somewhere as uncached, then - * it must also be accessed uncached by the kernel or data - * corruption may occur. - */ - ptr = xlate_dev_mem_ptr(p); - if (!ptr) - return -EFAULT; + remaining = copy_to_user(buf, ptr, sz); + + unxlate_dev_mem_ptr(p, ptr); + } - remaining = copy_to_user(buf, ptr, sz); - unxlate_dev_mem_ptr(p, ptr); if (remaining) return -EFAULT; @@ -184,30 +200,36 @@ static ssize_t write_mem(struct file *file, const char __user *buf, #endif while (count > 0) { + int allowed; + sz = size_inside_page(p, count); - if (!range_is_allowed(p >> PAGE_SHIFT, sz)) + allowed = page_is_allowed(p >> PAGE_SHIFT); + if (!allowed) return -EPERM; - /* - * On ia64 if a page has been mapped somewhere as uncached, then - * it must also be accessed uncached by the kernel or data - * corruption may occur. - */ - ptr = xlate_dev_mem_ptr(p); - if (!ptr) { - if (written) - break; - return -EFAULT; - } + /* Skip actual writing when a page is marked as restricted. */ + if (allowed == 1) { + /* + * On ia64 if a page has been mapped somewhere as + * uncached, then it must also be accessed uncached + * by the kernel or data corruption may occur. + */ + ptr = xlate_dev_mem_ptr(p); + if (!ptr) { + if (written) + break; + return -EFAULT; + } - copied = copy_from_user(ptr, buf, sz); - unxlate_dev_mem_ptr(p, ptr); - if (copied) { - written += sz - copied; - if (written) - break; - return -EFAULT; + copied = copy_from_user(ptr, buf, sz); + unxlate_dev_mem_ptr(p, ptr); + if (copied) { + written += sz - copied; + if (written) + break; + return -EFAULT; + } } buf += sz; diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 65f7eecc45b0..f10a107614b4 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -401,7 +401,7 @@ static void disable_interrupts(struct tpm_chip *chip) iowrite32(intmask, chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality)); - free_irq(chip->vendor.irq, chip); + devm_free_irq(chip->pdev, chip->vendor.irq, chip); chip->vendor.irq = 0; } diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 090183f812be..31e8ae916ba0 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1130,6 +1130,8 @@ static int put_chars(u32 vtermno, const char *buf, int count) { struct port *port; struct scatterlist sg[1]; + void *data; + int ret; if (unlikely(early_put_chars)) return early_put_chars(vtermno, buf, count); @@ -1138,8 +1140,14 @@ static int put_chars(u32 vtermno, const char *buf, int count) if (!port) return -EPIPE; - sg_init_one(sg, buf, count); - return __send_to_port(port, sg, 1, count, (void *)buf, false); + data = kmemdup(buf, count, GFP_ATOMIC); + if (!data) + return -ENOMEM; + + sg_init_one(sg, data, count); + ret = __send_to_port(port, sg, 1, count, data, false); + kfree(data); + return ret; } /* diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 4a9e034f939f..4996f4f312f4 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -2326,6 +2326,56 @@ static struct hlist_head *orphan_list[] = { NULL, }; +static void clk_state_subtree(struct clk_core *c) +{ + int vdd_level = 0; + struct clk_core *child; + + if (!c) + return; + + if (c->vdd_class) { + vdd_level = clk_find_vdd_level(c, c->rate); + if (vdd_level < 0) + vdd_level = 0; + } + + trace_clk_state(c->name, c->prepare_count, c->enable_count, + c->rate, vdd_level); + + hlist_for_each_entry(child, &c->children, child_node) + clk_state_subtree(child); +} + +static int clk_state_show(struct seq_file *s, void *data) +{ + struct clk_core *c; + struct hlist_head **lists = (struct hlist_head **)s->private; + + clk_prepare_lock(); + + for (; *lists; lists++) + hlist_for_each_entry(c, *lists, child_node) + clk_state_subtree(c); + + clk_prepare_unlock(); + + return 0; +} + + +static int clk_state_open(struct inode *inode, struct file *file) +{ + return single_open(file, clk_state_show, inode->i_private); +} + +static const struct file_operations clk_state_fops = { + .open = clk_state_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + static void clk_summary_show_one(struct seq_file *s, struct clk_core *c, int level) { @@ -3002,6 +3052,11 @@ static int __init clk_debug_init(void) if (!d) return -ENOMEM; + d = debugfs_create_file("trace_clocks", S_IRUGO, rootdir, &all_lists, + &clk_state_fops); + if (!d) + return -ENOMEM; + mutex_lock(&clk_debug_lock); hlist_for_each_entry(core, &clk_debug_list, debug_node) clk_debug_create_one(core, rootdir); diff --git a/drivers/clk/msm/clock-cpu-8996.c b/drivers/clk/msm/clock-cpu-8996.c index bcda6f31d6f5..bca8ada97f7d 100644 --- a/drivers/clk/msm/clock-cpu-8996.c +++ b/drivers/clk/msm/clock-cpu-8996.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2017, 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 @@ -1308,6 +1308,7 @@ static int cpu_clock_8996_driver_probe(struct platform_device *pdev) unsigned long pwrclrate, perfclrate, cbfrate; int pvs_ver = 0; u32 pte_efuse; + u32 clk_rate; char perfclspeedbinstr[] = "qcom,perfcl-speedbinXX-vXX"; char pwrclspeedbinstr[] = "qcom,pwrcl-speedbinXX-vXX"; char cbfspeedbinstr[] = "qcom,cbf-speedbinXX-vXX"; @@ -1435,6 +1436,18 @@ static int cpu_clock_8996_driver_probe(struct platform_device *pdev) clk_prepare_enable(&pwrcl_alt_pll.c); clk_prepare_enable(&cbf_pll.c); + /* Override the existing ealry boot frequency for power cluster */ + ret = of_property_read_u32(pdev->dev.of_node, + "qcom,pwrcl-early-boot-freq", &clk_rate); + if (!ret) + pwrcl_early_boot_rate = clk_rate; + + /* Override the existing ealry boot frequency for perf cluster */ + ret = of_property_read_u32(pdev->dev.of_node, + "qcom,perfcl-early-boot-freq", &clk_rate); + if (!ret) + perfcl_early_boot_rate = clk_rate; + /* Set the early boot rate. This may also switch us to the ACD leg */ clk_set_rate(&pwrcl_clk.c, pwrcl_early_boot_rate); clk_set_rate(&perfcl_clk.c, perfcl_early_boot_rate); @@ -1450,6 +1463,7 @@ static struct of_device_id match_table[] = { { .compatible = "qcom,cpu-clock-8996" }, { .compatible = "qcom,cpu-clock-8996-v3" }, { .compatible = "qcom,cpu-clock-8996-pro" }, + { .compatible = "qcom,cpu-clock-8996-auto" }, {} }; @@ -1499,6 +1513,9 @@ module_exit(cpu_clock_8996_exit); #define HF_MUX_SEL_LF_MUX 0x1 #define LF_MUX_SEL_ALT_PLL 0x1 +#define PWRCL_EARLY_BOOT_RATE 1286400000 +#define PERFCL_EARLY_BOOT_RATE 1363200000 + static int use_alt_pll; module_param(use_alt_pll, int, 0444); @@ -1537,6 +1554,12 @@ int __init cpu_clock_8996_early_init(void) cpu_clocks_v3 = true; cpu_clocks_pro = true; } else if (of_find_compatible_node(NULL, NULL, + "qcom,cpu-clock-8996-auto")) { + cpu_clocks_v3 = true; + cpu_clocks_pro = true; + pwrcl_early_boot_rate = PWRCL_EARLY_BOOT_RATE; + perfcl_early_boot_rate = PERFCL_EARLY_BOOT_RATE; + } else if (of_find_compatible_node(NULL, NULL, "qcom,cpu-clock-8996-v3")) { cpu_clocks_v3 = true; } else if (!of_find_compatible_node(NULL, NULL, diff --git a/drivers/clk/msm/clock-gcc-8996.c b/drivers/clk/msm/clock-gcc-8996.c index e93e9c494023..6dd2cf879c49 100644 --- a/drivers/clk/msm/clock-gcc-8996.c +++ b/drivers/clk/msm/clock-gcc-8996.c @@ -3670,14 +3670,6 @@ static int msm_gcc_8996_probe(struct platform_device *pdev) regval |= BIT(21); writel_relaxed(regval, virt_base + GCC_APCS_CLOCK_BRANCH_ENA_VOTE); - /* - * Set the HMSS_AHB_CLK_SLEEP_ENA bit to allow the hmss_ahb_clk to be - * turned off by hardware during certain apps low power modes. - */ - regval = readl_relaxed(virt_base + GCC_APCS_CLOCK_SLEEP_ENA_VOTE); - regval |= BIT(21); - writel_relaxed(regval, virt_base + GCC_APCS_CLOCK_SLEEP_ENA_VOTE); - vdd_dig.vdd_uv[1] = RPM_REGULATOR_CORNER_SVS_KRAIT; vdd_dig.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_dig"); if (IS_ERR(vdd_dig.regulator[0])) { diff --git a/drivers/clk/sunxi/clk-simple-gates.c b/drivers/clk/sunxi/clk-simple-gates.c index 0214c6548afd..97cb4221de25 100644 --- a/drivers/clk/sunxi/clk-simple-gates.c +++ b/drivers/clk/sunxi/clk-simple-gates.c @@ -98,6 +98,8 @@ static void __init sunxi_simple_gates_init(struct device_node *node) sunxi_simple_gates_setup(node, NULL, 0); } +CLK_OF_DECLARE(sun4i_a10_gates, "allwinner,sun4i-a10-gates-clk", + sunxi_simple_gates_init); CLK_OF_DECLARE(sun4i_a10_apb0, "allwinner,sun4i-a10-apb0-gates-clk", sunxi_simple_gates_init); CLK_OF_DECLARE(sun4i_a10_apb1, "allwinner,sun4i-a10-apb1-gates-clk", diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index e4d9aef1dda4..a0dba9beac05 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -698,9 +698,11 @@ static ssize_t show_cpuinfo_cur_freq(struct cpufreq_policy *policy, char *buf) { unsigned int cur_freq = __cpufreq_get(policy); - if (!cur_freq) - return sprintf(buf, "<unknown>"); - return sprintf(buf, "%u\n", cur_freq); + + if (cur_freq) + return sprintf(buf, "%u\n", cur_freq); + + return sprintf(buf, "<unknown>\n"); } /** diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 24ac49019b29..b91e115462ae 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -419,13 +419,13 @@ static u64 update_load(int cpu) ppol->policy->governor_data; u64 now; u64 now_idle; - unsigned int delta_idle; - unsigned int delta_time; + u64 delta_idle; + u64 delta_time; u64 active_time; now_idle = get_cpu_idle_time(cpu, &now, tunables->io_is_busy); - delta_idle = (unsigned int)(now_idle - pcpu->time_in_idle); - delta_time = (unsigned int)(now - pcpu->time_in_idle_timestamp); + delta_idle = (now_idle - pcpu->time_in_idle); + delta_time = (now - pcpu->time_in_idle_timestamp); if (delta_time <= delta_idle) active_time = 0; diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c index 69d4a1326fee..53e61459c69f 100644 --- a/drivers/crypto/caam/ctrl.c +++ b/drivers/crypto/caam/ctrl.c @@ -278,7 +278,8 @@ static int deinstantiate_rng(struct device *ctrldev, int state_handle_mask) /* Try to run it through DECO0 */ ret = run_descriptor_deco0(ctrldev, desc, &status); - if (ret || status) { + if (ret || + (status && status != JRSTA_SSRC_JUMP_HALT_CC)) { dev_err(ctrldev, "Failed to deinstantiate RNG4 SH%d\n", sh_idx); diff --git a/drivers/esoc/esoc-mdm-4x.c b/drivers/esoc/esoc-mdm-4x.c index 1e5f35d8422d..26f69fa61ba1 100644 --- a/drivers/esoc/esoc-mdm-4x.c +++ b/drivers/esoc/esoc-mdm-4x.c @@ -179,26 +179,48 @@ static int mdm_cmd_exe(enum esoc_cmd cmd, struct esoc_clink *esoc) struct device *dev = mdm->dev; int ret; bool graceful_shutdown = false; + u32 status, err_fatal; switch (cmd) { case ESOC_PWR_ON: + if (esoc->auto_boot) { + /* + * If esoc has already booted, we would have missed + * status change interrupt. Read status and err_fatal + * signals to arrive at the state of esoc. + */ + esoc->clink_ops->get_status(&status, esoc); + esoc->clink_ops->get_err_fatal(&err_fatal, esoc); + if (err_fatal) + return -EIO; + if (status && !mdm->ready) { + mdm->ready = true; + esoc->clink_ops->notify(ESOC_BOOT_DONE, esoc); + } + } gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 0); - mdm_enable_irqs(mdm); mdm->init = 1; mdm_do_first_power_on(mdm); + mdm_enable_irqs(mdm); break; case ESOC_PWR_OFF: mdm_disable_irqs(mdm); mdm->debug = 0; mdm->ready = false; mdm->trig_cnt = 0; + if (esoc->primary) + break; graceful_shutdown = true; - ret = sysmon_send_shutdown(&esoc->subsys); - if (ret) { - dev_err(mdm->dev, "sysmon shutdown fail, ret = %d\n", - ret); - graceful_shutdown = false; - goto force_poff; + if (!esoc->userspace_handle_shutdown) { + ret = sysmon_send_shutdown(&esoc->subsys); + if (ret) { + dev_err(mdm->dev, + "sysmon shutdown fail, ret = %d\n", ret); + graceful_shutdown = false; + goto force_poff; + } + } else { + esoc_clink_queue_request(ESOC_REQ_SEND_SHUTDOWN, esoc); } dev_dbg(mdm->dev, "Waiting for status gpio go low\n"); status_down = false; @@ -228,12 +250,17 @@ force_poff: esoc->subsys.sysmon_shutdown_ret); } + if (esoc->primary) + break; /* * Force a shutdown of the mdm. This is required in order * to prevent the mdm from immediately powering back on - * after the shutdown + * after the shutdown. Avoid setting status to 0, if line is + * monitored by multiple mdms(might be wrongly interpreted as + * a primary crash). */ - gpio_set_value(MDM_GPIO(mdm, AP2MDM_STATUS), 0); + if (esoc->statusline_not_a_powersource == false) + gpio_set_value(MDM_GPIO(mdm, AP2MDM_STATUS), 0); esoc_clink_queue_request(ESOC_REQ_SHUTDOWN, esoc); mdm_power_down(mdm); mdm_update_gpio_configs(mdm, GPIO_UPDATE_BOOTING_CONFIG); @@ -249,9 +276,12 @@ force_poff: */ mdm->ready = false; cancel_delayed_work(&mdm->mdm2ap_status_check_work); - gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 1); - dev_dbg(mdm->dev, "set ap2mdm errfatal to force reset\n"); - msleep(mdm->ramdump_delay_ms); + if (!mdm->esoc->auto_boot) { + gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 1); + dev_dbg(mdm->dev, + "set ap2mdm errfatal to force reset\n"); + msleep(mdm->ramdump_delay_ms); + } break; case ESOC_EXE_DEBUG: mdm->debug = 1; @@ -378,6 +408,8 @@ static void mdm_notify(enum esoc_notify notify, struct esoc_clink *esoc) status_down = false; dev_dbg(dev, "signal apq err fatal for graceful restart\n"); gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 1); + if (esoc->primary) + break; timeout = local_clock(); do_div(timeout, NSEC_PER_MSEC); timeout += MDM_MODEM_TIMEOUT; @@ -420,7 +452,8 @@ static irqreturn_t mdm_errfatal(int irq, void *dev_id) goto mdm_pwroff_irq; esoc = mdm->esoc; dev_err(dev, "%s: mdm sent errfatal interrupt\n", - __func__); + __func__); + subsys_set_crash_status(esoc->subsys_dev, true); /* disable irq ?*/ esoc_clink_evt_notify(ESOC_ERR_FATAL, esoc); return IRQ_HANDLED; @@ -441,11 +474,26 @@ static irqreturn_t mdm_status_change(int irq, void *dev_id) return IRQ_HANDLED; dev = mdm->dev; esoc = mdm->esoc; + /* + * On auto boot devices, there is a possibility of receiving + * status change interrupt before esoc_clink structure is + * initialized. Ignore them. + */ + if (!esoc) + return IRQ_HANDLED; value = gpio_get_value(MDM_GPIO(mdm, MDM2AP_STATUS)); if (value == 0 && mdm->ready) { dev_err(dev, "unexpected reset external modem\n"); + subsys_set_crash_status(esoc->subsys_dev, true); esoc_clink_evt_notify(ESOC_UNEXPECTED_RESET, esoc); } else if (value == 1) { + /* + * In auto_boot cases, bailout early if mdm + * is up already. + */ + if (esoc->auto_boot && mdm->ready) + return IRQ_HANDLED; + cancel_delayed_work(&mdm->mdm2ap_status_check_work); dev_dbg(dev, "status = 1: mdm is now ready\n"); mdm->ready = true; @@ -453,6 +501,8 @@ static irqreturn_t mdm_status_change(int irq, void *dev_id) queue_work(mdm->mdm_queue, &mdm->mdm_status_work); if (mdm->get_restart_reason) queue_work(mdm->mdm_queue, &mdm->restart_reason_work); + if (esoc->auto_boot) + esoc->clink_ops->notify(ESOC_BOOT_DONE, esoc); } return IRQ_HANDLED; } @@ -481,7 +531,7 @@ static irqreturn_t mdm_pblrdy_change(int irq, void *dev_id) return IRQ_HANDLED; } -static int mdm_get_status(u32 *status, struct esoc_clink *esoc) +static void mdm_get_status(u32 *status, struct esoc_clink *esoc) { struct mdm_ctrl *mdm = get_esoc_clink_data(esoc); @@ -489,7 +539,16 @@ static int mdm_get_status(u32 *status, struct esoc_clink *esoc) *status = 0; else *status = 1; - return 0; +} + +static void mdm_get_err_fatal(u32 *status, struct esoc_clink *esoc) +{ + struct mdm_ctrl *mdm = get_esoc_clink_data(esoc); + + if (gpio_get_value(MDM_GPIO(mdm, MDM2AP_ERRFATAL)) == 0) + *status = 0; + else + *status = 1; } static void mdm_configure_debug(struct mdm_ctrl *mdm) @@ -573,13 +632,21 @@ static int mdm_configure_ipc(struct mdm_ctrl *mdm, struct platform_device *pdev) &mdm->ramdump_delay_ms); if (ret) mdm->ramdump_delay_ms = DEF_RAMDUMP_DELAY; - /* Multilple gpio_request calls are allowed */ + /* + * In certain scenarios, multiple esoc devices are monitoring + * same AP2MDM_STATUS line. But only one of them will have a + * successful gpio_request call. Initialize gpio only if request + * succeeds. + */ if (gpio_request(MDM_GPIO(mdm, AP2MDM_STATUS), "AP2MDM_STATUS")) dev_err(dev, "Failed to configure AP2MDM_STATUS gpio\n"); - /* Multilple gpio_request calls are allowed */ + else + gpio_direction_output(MDM_GPIO(mdm, AP2MDM_STATUS), 0); if (gpio_request(MDM_GPIO(mdm, AP2MDM_ERRFATAL), "AP2MDM_ERRFATAL")) dev_err(dev, "%s Failed to configure AP2MDM_ERRFATAL gpio\n", __func__); + else + gpio_direction_output(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 0); if (gpio_request(MDM_GPIO(mdm, MDM2AP_STATUS), "MDM2AP_STATUS")) { dev_err(dev, "%s Failed to configure MDM2AP_STATUS gpio\n", __func__); @@ -612,9 +679,6 @@ static int mdm_configure_ipc(struct mdm_ctrl *mdm, struct platform_device *pdev) } } - gpio_direction_output(MDM_GPIO(mdm, AP2MDM_STATUS), 0); - gpio_direction_output(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 0); - if (gpio_is_valid(MDM_GPIO(mdm, AP2MDM_CHNLRDY))) gpio_direction_output(MDM_GPIO(mdm, AP2MDM_CHNLRDY), 0); @@ -748,6 +812,7 @@ static int mdm9x25_setup_hw(struct mdm_ctrl *mdm, dev_err(mdm->dev, "cannot allocate esoc device\n"); return PTR_ERR(esoc); } + esoc->pdev = pdev; mdm->mdm_queue = alloc_workqueue("mdm_queue", 0, 0); if (!mdm->mdm_queue) { dev_err(mdm->dev, "could not create mdm_queue\n"); @@ -818,6 +883,7 @@ static int mdm9x35_setup_hw(struct mdm_ctrl *mdm, dev_err(mdm->dev, "cannot allocate esoc device\n"); return PTR_ERR(esoc); } + esoc->pdev = pdev; mdm->mdm_queue = alloc_workqueue("mdm_queue", 0, 0); if (!mdm->mdm_queue) { dev_err(mdm->dev, "could not create mdm_queue\n"); @@ -888,6 +954,84 @@ static int mdm9x35_setup_hw(struct mdm_ctrl *mdm, return 0; } +static int mdm9x45_setup_hw(struct mdm_ctrl *mdm, + const struct mdm_ops *ops, + struct platform_device *pdev) +{ + int ret; + struct esoc_clink *esoc; + const struct esoc_clink_ops *const clink_ops = ops->clink_ops; + const struct mdm_pon_ops *pon_ops = ops->pon_ops; + + mdm->dev = &pdev->dev; + mdm->pon_ops = pon_ops; + esoc = devm_kzalloc(mdm->dev, sizeof(*esoc), GFP_KERNEL); + if (IS_ERR_OR_NULL(esoc)) { + dev_err(mdm->dev, "cannot allocate esoc device\n"); + return PTR_ERR(esoc); + } + esoc->pdev = pdev; + mdm->mdm_queue = alloc_workqueue("mdm_queue", 0, 0); + if (!mdm->mdm_queue) { + dev_err(mdm->dev, "could not create mdm_queue\n"); + return -ENOMEM; + } + mdm->irq_mask = 0; + mdm->ready = false; + ret = mdm_dt_parse_gpios(mdm); + if (ret) + return ret; + dev_err(mdm->dev, "parsing gpio done\n"); + ret = mdm_pon_dt_init(mdm); + if (ret) + return ret; + dev_dbg(mdm->dev, "pon dt init done\n"); + ret = mdm_pinctrl_init(mdm); + if (ret) + return ret; + dev_err(mdm->dev, "pinctrl init done\n"); + ret = mdm_pon_setup(mdm); + if (ret) + return ret; + dev_dbg(mdm->dev, "pon setup done\n"); + ret = mdm_configure_ipc(mdm, pdev); + if (ret) + return ret; + mdm_configure_debug(mdm); + dev_err(mdm->dev, "ipc configure done\n"); + esoc->name = MDM9x45_LABEL; + esoc->link_name = MDM9x45_PCIE; + esoc->clink_ops = clink_ops; + esoc->parent = mdm->dev; + esoc->owner = THIS_MODULE; + esoc->np = pdev->dev.of_node; + + esoc->auto_boot = of_property_read_bool(esoc->np, + "qcom,mdm-auto-boot"); + esoc->statusline_not_a_powersource = of_property_read_bool(esoc->np, + "qcom,mdm-statusline-not-a-powersource"); + esoc->userspace_handle_shutdown = of_property_read_bool(esoc->np, + "qcom,mdm-userspace-handle-shutdown"); + set_esoc_clink_data(esoc, mdm); + ret = esoc_clink_register(esoc); + if (ret) { + dev_err(mdm->dev, "esoc registration failed\n"); + return ret; + } + dev_dbg(mdm->dev, "esoc registration done\n"); + init_completion(&mdm->debug_done); + INIT_WORK(&mdm->mdm_status_work, mdm_status_fn); + INIT_WORK(&mdm->restart_reason_work, mdm_get_restart_reason); + INIT_DELAYED_WORK(&mdm->mdm2ap_status_check_work, mdm2ap_status_check); + mdm->get_restart_reason = false; + mdm->debug_fail = false; + mdm->esoc = esoc; + mdm->init = 0; + if (esoc->auto_boot) + gpio_direction_output(MDM_GPIO(mdm, AP2MDM_STATUS), 1); + return 0; +} + static int mdm9x55_setup_hw(struct mdm_ctrl *mdm, const struct mdm_ops *ops, struct platform_device *pdev) @@ -906,6 +1050,7 @@ static int mdm9x55_setup_hw(struct mdm_ctrl *mdm, dev_err(mdm->dev, "cannot allocate esoc device\n"); return PTR_ERR(esoc); } + esoc->pdev = pdev; mdm->mdm_queue = alloc_workqueue("mdm_queue", 0, 0); if (!mdm->mdm_queue) { dev_err(mdm->dev, "could not create mdm_queue\n"); @@ -963,9 +1108,86 @@ static int mdm9x55_setup_hw(struct mdm_ctrl *mdm, return 0; } +static int apq8096_setup_hw(struct mdm_ctrl *mdm, + const struct mdm_ops *ops, + struct platform_device *pdev) +{ + int ret; + struct device_node *node; + struct esoc_clink *esoc; + const struct esoc_clink_ops *const clink_ops = ops->clink_ops; + const struct mdm_pon_ops *pon_ops = ops->pon_ops; + + mdm->dev = &pdev->dev; + mdm->pon_ops = pon_ops; + node = pdev->dev.of_node; + esoc = devm_kzalloc(mdm->dev, sizeof(*esoc), GFP_KERNEL); + if (IS_ERR_OR_NULL(esoc)) { + dev_err(mdm->dev, "cannot allocate esoc device\n"); + return PTR_ERR(esoc); + } + esoc->pdev = pdev; + mdm->mdm_queue = alloc_workqueue("mdm_queue", 0, 0); + if (!mdm->mdm_queue) { + dev_err(mdm->dev, "could not create mdm_queue\n"); + return -ENOMEM; + } + mdm->irq_mask = 0; + mdm->ready = false; + ret = mdm_dt_parse_gpios(mdm); + if (ret) + return ret; + dev_dbg(mdm->dev, "parsing gpio done\n"); + ret = mdm_pon_dt_init(mdm); + if (ret) + return ret; + dev_dbg(mdm->dev, "pon dt init done\n"); + ret = mdm_pinctrl_init(mdm); + if (ret) + return ret; + dev_dbg(mdm->dev, "pinctrl init done\n"); + ret = mdm_pon_setup(mdm); + if (ret) + return ret; + dev_dbg(mdm->dev, "pon setup done\n"); + ret = mdm_configure_ipc(mdm, pdev); + if (ret) + return ret; + dev_dbg(mdm->dev, "ipc configure done\n"); + esoc->name = APQ8096_LABEL; + esoc->link_name = APQ8096_PCIE; + esoc->clink_ops = clink_ops; + esoc->parent = mdm->dev; + esoc->owner = THIS_MODULE; + esoc->np = pdev->dev.of_node; + esoc->auto_boot = of_property_read_bool(esoc->np, + "qcom,mdm-auto-boot"); + esoc->primary = of_property_read_bool(esoc->np, + "qcom,mdm-primary"); + set_esoc_clink_data(esoc, mdm); + ret = esoc_clink_register(esoc); + if (ret) { + dev_err(mdm->dev, "esoc registration failed\n"); + return ret; + } + dev_dbg(mdm->dev, "esoc registration done\n"); + init_completion(&mdm->debug_done); + INIT_WORK(&mdm->mdm_status_work, mdm_status_fn); + INIT_WORK(&mdm->restart_reason_work, mdm_get_restart_reason); + INIT_DELAYED_WORK(&mdm->mdm2ap_status_check_work, mdm2ap_status_check); + mdm->get_restart_reason = false; + mdm->debug_fail = false; + mdm->esoc = esoc; + mdm->init = 0; + gpio_direction_output(MDM_GPIO(mdm, AP2MDM_STATUS), 1); + gpio_direction_output(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 0); + return 0; +} + static struct esoc_clink_ops mdm_cops = { .cmd_exe = mdm_cmd_exe, .get_status = mdm_get_status, + .get_err_fatal = mdm_get_err_fatal, .notify = mdm_notify, }; @@ -981,6 +1203,18 @@ static struct mdm_ops mdm9x35_ops = { .pon_ops = &mdm9x35_pon_ops, }; +static struct mdm_ops mdm9x45_ops = { + .clink_ops = &mdm_cops, + .config_hw = mdm9x45_setup_hw, + .pon_ops = &mdm9x45_pon_ops, +}; + +static struct mdm_ops apq8096_ops = { + .clink_ops = &mdm_cops, + .config_hw = apq8096_setup_hw, + .pon_ops = &apq8096_pon_ops, +}; + static struct mdm_ops mdm9x55_ops = { .clink_ops = &mdm_cops, .config_hw = mdm9x55_setup_hw, @@ -992,8 +1226,12 @@ static const struct of_device_id mdm_dt_match[] = { .data = &mdm9x25_ops, }, { .compatible = "qcom,ext-mdm9x35", .data = &mdm9x35_ops, }, + { .compatible = "qcom,ext-mdm9x45", + .data = &mdm9x45_ops, }, { .compatible = "qcom,ext-mdm9x55", .data = &mdm9x55_ops, }, + { .compatible = "qcom,ext-apq8096", + .data = &apq8096_ops, }, {}, }; MODULE_DEVICE_TABLE(of, mdm_dt_match); diff --git a/drivers/esoc/esoc-mdm-drv.c b/drivers/esoc/esoc-mdm-drv.c index 8697428eceb2..9c2c68dfef65 100644 --- a/drivers/esoc/esoc-mdm-drv.c +++ b/drivers/esoc/esoc-mdm-drv.c @@ -13,6 +13,7 @@ #include <linux/delay.h> #include <linux/workqueue.h> #include <linux/reboot.h> +#include <linux/of.h> #include "esoc.h" #include "mdm-dbg.h" @@ -72,7 +73,14 @@ static void mdm_handle_clink_evt(enum esoc_evt evt, break; case ESOC_UNEXPECTED_RESET: case ESOC_ERR_FATAL: - if (mdm_drv->mode == CRASH) + /* + * Modem can crash while we are waiting for boot_done during + * a subsystem_get(). Setting mode to CRASH will prevent a + * subsequent subsystem_get() from entering poweron ops. Avoid + * this by seting mode to CRASH only if device was up and + * running. + */ + if (mdm_drv->mode == CRASH || mdm_drv->mode != RUN) return; mdm_drv->mode = CRASH; queue_work(mdm_drv->mdm_queue, &mdm_drv->ssr_work); @@ -161,8 +169,9 @@ static int mdm_subsys_powerup(const struct subsys_desc *crashed_subsys) subsys); struct mdm_drv *mdm_drv = esoc_get_drv_data(esoc_clink); const struct esoc_clink_ops const *clink_ops = esoc_clink->clink_ops; + int timeout = INT_MAX; - if (!esoc_req_eng_enabled(esoc_clink)) { + if (!esoc_clink->auto_boot && !esoc_req_eng_enabled(esoc_clink)) { dev_dbg(&esoc_clink->dev, "Wait for req eng registration\n"); wait_for_completion(&mdm_drv->req_eng_wait); } @@ -187,8 +196,17 @@ static int mdm_subsys_powerup(const struct subsys_desc *crashed_subsys) return ret; } } - wait_for_completion(&mdm_drv->boot_done); - if (mdm_drv->boot_fail) { + + /* + * In autoboot case, it is possible that we can forever wait for + * boot completion, when esoc fails to boot. This is because there + * is no helper application which can alert esoc driver about boot + * failure. Prevent going to wait forever in such case. + */ + if (esoc_clink->auto_boot) + timeout = 10 * HZ; + ret = wait_for_completion_timeout(&mdm_drv->boot_done, timeout); + if (mdm_drv->boot_fail || ret <= 0) { dev_err(&esoc_clink->dev, "booting failed\n"); return -EIO; } @@ -216,10 +234,12 @@ static int mdm_subsys_ramdumps(int want_dumps, static int mdm_register_ssr(struct esoc_clink *esoc_clink) { - esoc_clink->subsys.shutdown = mdm_subsys_shutdown; - esoc_clink->subsys.ramdump = mdm_subsys_ramdumps; - esoc_clink->subsys.powerup = mdm_subsys_powerup; - esoc_clink->subsys.crash_shutdown = mdm_crash_shutdown; + struct subsys_desc *subsys = &esoc_clink->subsys; + + subsys->shutdown = mdm_subsys_shutdown; + subsys->ramdump = mdm_subsys_ramdumps; + subsys->powerup = mdm_subsys_powerup; + subsys->crash_shutdown = mdm_crash_shutdown; return esoc_clink_register_ssr(esoc_clink); } @@ -286,6 +306,14 @@ static struct esoc_compat compat_table[] = { .name = "MDM9x55", .data = NULL, }, + { + .name = "MDM9x45", + .data = NULL, + }, + { + .name = "APQ8096", + .data = NULL, + }, }; static struct esoc_drv esoc_ssr_drv = { diff --git a/drivers/esoc/esoc-mdm-pon.c b/drivers/esoc/esoc-mdm-pon.c index acda06485364..4ae3b7520f77 100644 --- a/drivers/esoc/esoc-mdm-pon.c +++ b/drivers/esoc/esoc-mdm-pon.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2015, 2017, 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 @@ -60,6 +60,29 @@ static int mdm9x55_toggle_soft_reset(struct mdm_ctrl *mdm, bool atomic) return 0; } +/* This function can be called from atomic context. */ +static int mdm9x45_toggle_soft_reset(struct mdm_ctrl *mdm, bool atomic) +{ + int soft_reset_direction_assert = 0, + soft_reset_direction_de_assert = 1; + + if (mdm->soft_reset_inverted) { + soft_reset_direction_assert = 1; + soft_reset_direction_de_assert = 0; + } + gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET), + soft_reset_direction_assert); + /* + * Allow PS hold assert to be detected + */ + if (!atomic) + usleep_range(1000000, 1005000); + else + mdelay(1000); + gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET), + soft_reset_direction_de_assert); + return 0; +} static int mdm4x_do_first_power_on(struct mdm_ctrl *mdm) { @@ -68,6 +91,9 @@ static int mdm4x_do_first_power_on(struct mdm_ctrl *mdm) struct device *dev = mdm->dev; dev_dbg(dev, "Powering on modem for the first time\n"); + if (mdm->esoc->auto_boot) + return 0; + mdm_toggle_soft_reset(mdm, false); /* Add a delay to allow PON sequence to complete*/ msleep(50); @@ -132,8 +158,31 @@ static int mdm9x55_power_down(struct mdm_ctrl *mdm) return 0; } +static int mdm9x45_power_down(struct mdm_ctrl *mdm) +{ + int soft_reset_direction_assert = 0, + soft_reset_direction_de_assert = 1; + + if (mdm->soft_reset_inverted) { + soft_reset_direction_assert = 1; + soft_reset_direction_de_assert = 0; + } + gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET), + soft_reset_direction_assert); + /* + * Allow PS hold assert to be detected + */ + msleep(3003); + gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET), + soft_reset_direction_de_assert); + return 0; +} + static void mdm4x_cold_reset(struct mdm_ctrl *mdm) { + if (!gpio_is_valid(MDM_GPIO(mdm, AP2MDM_SOFT_RESET))) + return; + dev_dbg(mdm->dev, "Triggering mdm cold reset"); gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET), !!mdm->soft_reset_inverted); @@ -152,6 +201,11 @@ static void mdm9x55_cold_reset(struct mdm_ctrl *mdm) !mdm->soft_reset_inverted); } +static int apq8096_pon_dt_init(struct mdm_ctrl *mdm) +{ + return 0; +} + static int mdm4x_pon_dt_init(struct mdm_ctrl *mdm) { int val; @@ -183,6 +237,21 @@ static int mdm4x_pon_setup(struct mdm_ctrl *mdm) return 0; } +/* This function can be called from atomic context. */ +static int apq8096_toggle_soft_reset(struct mdm_ctrl *mdm, bool atomic) +{ + return 0; +} + +static int apq8096_power_down(struct mdm_ctrl *mdm) +{ + return 0; +} + +static void apq8096_cold_reset(struct mdm_ctrl *mdm) +{ +} + struct mdm_pon_ops mdm9x25_pon_ops = { .pon = mdm4x_do_first_power_on, .soft_reset = mdm4x_toggle_soft_reset, @@ -203,8 +272,8 @@ struct mdm_pon_ops mdm9x35_pon_ops = { struct mdm_pon_ops mdm9x45_pon_ops = { .pon = mdm4x_do_first_power_on, - .soft_reset = mdm4x_toggle_soft_reset, - .poff_force = mdm4x_power_down, + .soft_reset = mdm9x45_toggle_soft_reset, + .poff_force = mdm9x45_power_down, .cold_reset = mdm4x_cold_reset, .dt_init = mdm4x_pon_dt_init, .setup = mdm4x_pon_setup, @@ -218,3 +287,12 @@ struct mdm_pon_ops mdm9x55_pon_ops = { .dt_init = mdm4x_pon_dt_init, .setup = mdm4x_pon_setup, }; + +struct mdm_pon_ops apq8096_pon_ops = { + .pon = mdm4x_do_first_power_on, + .soft_reset = apq8096_toggle_soft_reset, + .poff_force = apq8096_power_down, + .cold_reset = apq8096_cold_reset, + .dt_init = apq8096_pon_dt_init, + .setup = mdm4x_pon_setup, +}; diff --git a/drivers/esoc/esoc-mdm.h b/drivers/esoc/esoc-mdm.h index ac811720b035..9343e49559f2 100644 --- a/drivers/esoc/esoc-mdm.h +++ b/drivers/esoc/esoc-mdm.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2015, 2017, 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 @@ -37,6 +37,8 @@ #define MDM9x45_PCIE "PCIe" #define MDM9x55_LABEL "MDM9x55" #define MDM9x55_PCIE "PCIe" +#define APQ8096_LABEL "APQ8096" +#define APQ8096_PCIE "PCIe" #define MDM2AP_STATUS_TIMEOUT_MS 120000L #define MDM_MODEM_TIMEOUT 3000 #define DEF_RAMDUMP_TIMEOUT 120000 @@ -153,4 +155,5 @@ extern struct mdm_pon_ops mdm9x25_pon_ops; extern struct mdm_pon_ops mdm9x35_pon_ops; extern struct mdm_pon_ops mdm9x45_pon_ops; extern struct mdm_pon_ops mdm9x55_pon_ops; +extern struct mdm_pon_ops apq8096_pon_ops; #endif diff --git a/drivers/esoc/esoc.h b/drivers/esoc/esoc.h index 755fb24bd60a..ee54908ce486 100644 --- a/drivers/esoc/esoc.h +++ b/drivers/esoc/esoc.h @@ -49,6 +49,7 @@ struct esoc_eng { * @link_info: additional info about the physical link. * @parent: parent device. * @dev: device for userspace interface. + * @pdev: platform device to interface with SSR driver. * @id: id of the external device. * @owner: owner of the device. * @clink_ops: control operations for the control link @@ -59,6 +60,12 @@ struct esoc_eng { * @subsys_desc: descriptor for subsystem restart * @subsys_dev: ssr device handle. * @np: device tree node for esoc_clink. + * @auto_boot: boots independently. + * @primary: primary esoc controls(reset/poweroff) all secondary + * esocs, but not otherway around. + * @statusline_not_a_powersource: True if status line to esoc is not a + * power source. + * @userspace_handle_shutdown: True if user space handles shutdown requests. */ struct esoc_clink { const char *name; @@ -66,6 +73,7 @@ struct esoc_clink { const char *link_info; struct device *parent; struct device dev; + struct platform_device *pdev; unsigned int id; struct module *owner; const struct esoc_clink_ops const *clink_ops; @@ -77,17 +85,23 @@ struct esoc_clink { struct subsys_desc subsys; struct subsys_device *subsys_dev; struct device_node *np; + bool auto_boot; + bool primary; + bool statusline_not_a_powersource; + bool userspace_handle_shutdown; }; /** * struct esoc_clink_ops: Operations to control external soc * @cmd_exe: Execute control command * @get_status: Get current status, or response to previous command + * @get_err_fatal: Get status of err fatal signal * @notify_esoc: notify external soc of events */ struct esoc_clink_ops { int (*cmd_exe)(enum esoc_cmd cmd, struct esoc_clink *dev); - int (*get_status)(u32 *status, struct esoc_clink *dev); + void (*get_status)(u32 *status, struct esoc_clink *dev); + void (*get_err_fatal)(u32 *status, struct esoc_clink *dev); void (*notify)(enum esoc_notify notify, struct esoc_clink *dev); }; diff --git a/drivers/esoc/esoc_bus.c b/drivers/esoc/esoc_bus.c index f925607511ba..94f52764c8c3 100644 --- a/drivers/esoc/esoc_bus.c +++ b/drivers/esoc/esoc_bus.c @@ -189,7 +189,7 @@ int esoc_clink_register_ssr(struct esoc_clink *esoc_clink) snprintf(subsys_name, len, "esoc%d", esoc_clink->id); esoc_clink->subsys.name = subsys_name; esoc_clink->dev.of_node = esoc_clink->np; - esoc_clink->subsys.dev = &esoc_clink->dev; + esoc_clink->subsys.dev = &esoc_clink->pdev->dev; esoc_clink->subsys_dev = subsys_register(&esoc_clink->subsys); if (IS_ERR_OR_NULL(esoc_clink->subsys_dev)) { dev_err(&esoc_clink->dev, "failed to register ssr node\n"); diff --git a/drivers/esoc/esoc_dev.c b/drivers/esoc/esoc_dev.c index bbe1d24fb1f6..a1e7a52a8c26 100644 --- a/drivers/esoc/esoc_dev.c +++ b/drivers/esoc/esoc_dev.c @@ -224,9 +224,11 @@ static long esoc_dev_ioctl(struct file *file, unsigned int cmd, clink_ops->notify(esoc_cmd, esoc_clink); break; case ESOC_GET_STATUS: - err = clink_ops->get_status(&status, esoc_clink); - if (err) - return err; + clink_ops->get_status(&status, esoc_clink); + put_user(status, (unsigned int __user *)uarg); + break; + case ESOC_GET_ERR_FATAL: + clink_ops->get_err_fatal(&status, esoc_clink); put_user(status, (unsigned int __user *)uarg); break; case ESOC_WAIT_FOR_CRASH: diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c index 7c42ff670080..a0924330d125 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c @@ -25,6 +25,7 @@ * Alex Deucher * Jerome Glisse */ +#include <linux/irq.h> #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> #include <drm/amdgpu_drm.h> diff --git a/drivers/gpu/drm/amd/amdgpu/kv_dpm.c b/drivers/gpu/drm/amd/amdgpu/kv_dpm.c index 7e9154c7f1db..d1c9525d81eb 100644 --- a/drivers/gpu/drm/amd/amdgpu/kv_dpm.c +++ b/drivers/gpu/drm/amd/amdgpu/kv_dpm.c @@ -2258,7 +2258,7 @@ static void kv_apply_state_adjust_rules(struct amdgpu_device *adev, if (pi->caps_stable_p_state) { stable_p_state_sclk = (max_limits->sclk * 75) / 100; - for (i = table->count - 1; i >= 0; i++) { + for (i = table->count - 1; i >= 0; i--) { if (stable_p_state_sclk >= table->entries[i].clk) { stable_p_state_sclk = table->entries[i].clk; break; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index fb9f647bb5cd..5044f2257e89 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1159,7 +1159,7 @@ struct intel_gen6_power_mgmt { struct intel_rps_client semaphores, mmioflips; /* manual wa residency calculations */ - struct intel_rps_ei up_ei, down_ei; + struct intel_rps_ei ei; /* * Protects RPS/RC6 register access and PCU communication. diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 0f42a2782afc..b7b0a38acd67 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -994,68 +994,51 @@ static void vlv_c0_read(struct drm_i915_private *dev_priv, ei->media_c0 = I915_READ(VLV_MEDIA_C0_COUNT); } -static bool vlv_c0_above(struct drm_i915_private *dev_priv, - const struct intel_rps_ei *old, - const struct intel_rps_ei *now, - int threshold) -{ - u64 time, c0; - unsigned int mul = 100; - - if (old->cz_clock == 0) - return false; - - if (I915_READ(VLV_COUNTER_CONTROL) & VLV_COUNT_RANGE_HIGH) - mul <<= 8; - - time = now->cz_clock - old->cz_clock; - time *= threshold * dev_priv->czclk_freq; - - /* Workload can be split between render + media, e.g. SwapBuffers - * being blitted in X after being rendered in mesa. To account for - * this we need to combine both engines into our activity counter. - */ - c0 = now->render_c0 - old->render_c0; - c0 += now->media_c0 - old->media_c0; - c0 *= mul * VLV_CZ_CLOCK_TO_MILLI_SEC; - - return c0 >= time; -} - void gen6_rps_reset_ei(struct drm_i915_private *dev_priv) { - vlv_c0_read(dev_priv, &dev_priv->rps.down_ei); - dev_priv->rps.up_ei = dev_priv->rps.down_ei; + memset(&dev_priv->rps.ei, 0, sizeof(dev_priv->rps.ei)); } static u32 vlv_wa_c0_ei(struct drm_i915_private *dev_priv, u32 pm_iir) { + const struct intel_rps_ei *prev = &dev_priv->rps.ei; struct intel_rps_ei now; u32 events = 0; - if ((pm_iir & (GEN6_PM_RP_DOWN_EI_EXPIRED | GEN6_PM_RP_UP_EI_EXPIRED)) == 0) + if ((pm_iir & GEN6_PM_RP_UP_EI_EXPIRED) == 0) return 0; vlv_c0_read(dev_priv, &now); if (now.cz_clock == 0) return 0; - if (pm_iir & GEN6_PM_RP_DOWN_EI_EXPIRED) { - if (!vlv_c0_above(dev_priv, - &dev_priv->rps.down_ei, &now, - dev_priv->rps.down_threshold)) - events |= GEN6_PM_RP_DOWN_THRESHOLD; - dev_priv->rps.down_ei = now; - } + if (prev->cz_clock) { + u64 time, c0; + unsigned int mul; - if (pm_iir & GEN6_PM_RP_UP_EI_EXPIRED) { - if (vlv_c0_above(dev_priv, - &dev_priv->rps.up_ei, &now, - dev_priv->rps.up_threshold)) - events |= GEN6_PM_RP_UP_THRESHOLD; - dev_priv->rps.up_ei = now; + mul = VLV_CZ_CLOCK_TO_MILLI_SEC * 100; /* scale to threshold% */ + if (I915_READ(VLV_COUNTER_CONTROL) & VLV_COUNT_RANGE_HIGH) + mul <<= 8; + + time = now.cz_clock - prev->cz_clock; + time *= dev_priv->czclk_freq; + + /* Workload can be split between render + media, + * e.g. SwapBuffers being blitted in X after being rendered in + * mesa. To account for this we need to combine both engines + * into our activity counter. + */ + c0 = now.render_c0 - prev->render_c0; + c0 += now.media_c0 - prev->media_c0; + c0 *= mul; + + if (c0 > time * dev_priv->rps.up_threshold) + events = GEN6_PM_RP_UP_THRESHOLD; + else if (c0 < time * dev_priv->rps.down_threshold) + events = GEN6_PM_RP_DOWN_THRESHOLD; } + dev_priv->rps.ei = now; return events; } @@ -4390,7 +4373,7 @@ void intel_irq_init(struct drm_i915_private *dev_priv) /* Let's track the enabled rps events */ if (IS_VALLEYVIEW(dev_priv) && !IS_CHERRYVIEW(dev_priv)) /* WaGsvRC0ResidencyMethod:vlv */ - dev_priv->pm_rps_events = GEN6_PM_RP_DOWN_EI_EXPIRED | GEN6_PM_RP_UP_EI_EXPIRED; + dev_priv->pm_rps_events = GEN6_PM_RP_UP_EI_EXPIRED; else dev_priv->pm_rps_events = GEN6_PM_RPS_EVENTS; diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index e7c18519274a..fd4690ed93c0 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4376,6 +4376,12 @@ static void gen6_set_rps_thresholds(struct drm_i915_private *dev_priv, u8 val) break; } + /* When byt can survive without system hang with dynamic + * sw freq adjustments, this restriction can be lifted. + */ + if (IS_VALLEYVIEW(dev_priv)) + goto skip_hw_write; + I915_WRITE(GEN6_RP_UP_EI, GT_INTERVAL_FROM_US(dev_priv, ei_up)); I915_WRITE(GEN6_RP_UP_THRESHOLD, @@ -4394,6 +4400,7 @@ static void gen6_set_rps_thresholds(struct drm_i915_private *dev_priv, u8 val) GEN6_RP_UP_BUSY_AVG | GEN6_RP_DOWN_IDLE_AVG); +skip_hw_write: dev_priv->rps.power = new_power; dev_priv->rps.up_threshold = threshold_up; dev_priv->rps.down_threshold = threshold_down; @@ -4404,8 +4411,9 @@ static u32 gen6_rps_pm_mask(struct drm_i915_private *dev_priv, u8 val) { u32 mask = 0; + /* We use UP_EI_EXPIRED interupts for both up/down in manual mode */ if (val > dev_priv->rps.min_freq_softlimit) - mask |= GEN6_PM_RP_DOWN_EI_EXPIRED | GEN6_PM_RP_DOWN_THRESHOLD | GEN6_PM_RP_DOWN_TIMEOUT; + mask |= GEN6_PM_RP_UP_EI_EXPIRED | GEN6_PM_RP_DOWN_THRESHOLD | GEN6_PM_RP_DOWN_TIMEOUT; if (val < dev_priv->rps.max_freq_softlimit) mask |= GEN6_PM_RP_UP_EI_EXPIRED | GEN6_PM_RP_UP_THRESHOLD; @@ -4509,7 +4517,7 @@ void gen6_rps_busy(struct drm_i915_private *dev_priv) { mutex_lock(&dev_priv->rps.hw_lock); if (dev_priv->rps.enabled) { - if (dev_priv->pm_rps_events & (GEN6_PM_RP_DOWN_EI_EXPIRED | GEN6_PM_RP_UP_EI_EXPIRED)) + if (dev_priv->pm_rps_events & GEN6_PM_RP_UP_EI_EXPIRED) gen6_rps_reset_ei(dev_priv); I915_WRITE(GEN6_PMINTRMSK, gen6_rps_pm_mask(dev_priv, dev_priv->rps.cur_freq)); diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 3d0617dbc514..f3a8a8416c7a 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -49,6 +49,7 @@ msm_drm-y := \ sde/sde_vbif.o \ sde_dbg_evtlog.o \ sde_io_util.o \ + dba_bridge.o \ sde_edid_parser.o # use drm gpu driver only if qcom_kgsl driver not available diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c index c4f886fd6037..a417e42944fc 100644 --- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c @@ -466,6 +466,7 @@ struct msm_gpu *a3xx_gpu_init(struct drm_device *dev) struct msm_gpu *gpu; struct msm_drm_private *priv = dev->dev_private; struct platform_device *pdev = priv->gpu_pdev; + struct msm_gpu_config a3xx_config = { 0 }; int ret; if (!pdev) { @@ -491,7 +492,13 @@ struct msm_gpu *a3xx_gpu_init(struct drm_device *dev) adreno_gpu->registers = a3xx_registers; adreno_gpu->reg_offsets = a3xx_register_offsets; - ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs, 1); + a3xx_config.ioname = MSM_GPU_DEFAULT_IONAME; + a3xx_config.irqname = MSM_GPU_DEFAULT_IRQNAME; + a3xx_config.nr_rings = 1; + a3xx_config.va_start = 0x300000; + a3xx_config.va_end = 0xffffffff; + + ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs, &a3xx_config); if (ret) goto fail; diff --git a/drivers/gpu/drm/msm/adreno/a4xx_gpu.c b/drivers/gpu/drm/msm/adreno/a4xx_gpu.c index 534a7c3fbdca..069823f054f7 100644 --- a/drivers/gpu/drm/msm/adreno/a4xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a4xx_gpu.c @@ -543,6 +543,7 @@ struct msm_gpu *a4xx_gpu_init(struct drm_device *dev) struct msm_gpu *gpu; struct msm_drm_private *priv = dev->dev_private; struct platform_device *pdev = priv->gpu_pdev; + struct msm_gpu_config a4xx_config = { 0 }; int ret; if (!pdev) { @@ -568,7 +569,13 @@ struct msm_gpu *a4xx_gpu_init(struct drm_device *dev) adreno_gpu->registers = a4xx_registers; adreno_gpu->reg_offsets = a4xx_register_offsets; - ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs, 1); + a4xx_config.ioname = MSM_GPU_DEFAULT_IONAME; + a4xx_config.irqname = MSM_GPU_DEFAULT_IRQNAME; + a4xx_config.nr_rings = 1; + a4xx_config.va_start = 0x300000; + a4xx_config.va_end = 0xffffffff; + + ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs, &a4xx_config); if (ret) goto fail; diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c index 9ceef8f437b5..de2ee1ffb735 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c @@ -1163,7 +1163,7 @@ static const u32 a5xx_registers[] = { 0xe9c0, 0xe9c7, 0xe9d0, 0xe9d1, 0xea00, 0xea01, 0xea10, 0xea1c, 0xea40, 0xea68, 0xea80, 0xea80, 0xea82, 0xeaa3, 0xeaa5, 0xeac2, 0xeb80, 0xeb8f, 0xebb0, 0xebb0, 0xec00, 0xec05, 0xec08, 0xece9, - 0xecf0, 0xecf0, 0xf400, 0xf400, 0xf800, 0xf807, + 0xecf0, 0xecf0, 0xf800, 0xf807, ~0 }; @@ -1368,6 +1368,7 @@ struct msm_gpu *a5xx_gpu_init(struct drm_device *dev) struct a5xx_gpu *a5xx_gpu = NULL; struct adreno_gpu *adreno_gpu; struct msm_gpu *gpu; + struct msm_gpu_config a5xx_config = { 0 }; int ret; if (!pdev) { @@ -1391,7 +1392,20 @@ struct msm_gpu *a5xx_gpu_init(struct drm_device *dev) /* Check the efuses for some configuration */ a5xx_efuses_read(pdev, adreno_gpu); - ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs, 4); + a5xx_config.ioname = MSM_GPU_DEFAULT_IONAME; + a5xx_config.irqname = MSM_GPU_DEFAULT_IRQNAME; + + /* Set the number of rings to 4 - yay preemption */ + a5xx_config.nr_rings = 4; + + /* + * Set the user domain range to fall into the TTBR1 region for global + * objects + */ + a5xx_config.va_start = 0x800000000; + a5xx_config.va_end = 0x8ffffffff; + + ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs, &a5xx_config); if (ret) { a5xx_destroy(&(a5xx_gpu->base.base)); return ERR_PTR(ret); diff --git a/drivers/gpu/drm/msm/adreno/a5xx_snapshot.c b/drivers/gpu/drm/msm/adreno/a5xx_snapshot.c index 5a2edb0ea518..690e6f546e60 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_snapshot.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_snapshot.c @@ -733,6 +733,35 @@ static void a5xx_snapshot_indexed_registers(struct msm_gpu *gpu, } } +static void a5xx_snapshot_preemption(struct msm_gpu *gpu, struct msm_snapshot + *snapshot) +{ + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); + struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu); + struct msm_snapshot_gpu_object header = { + .type = SNAPSHOT_GPU_OBJECT_GLOBAL, + .size = A5XX_PREEMPT_RECORD_SIZE >> 2, + .pt_base = 0, + }; + int index; + + if (gpu->nr_rings <= 1) + return; + + for (index = 0; index < gpu->nr_rings; index++) { + + header.gpuaddr = a5xx_gpu->preempt_iova[index]; + + if (!SNAPSHOT_HEADER(snapshot, header, + SNAPSHOT_SECTION_GPU_OBJECT_V2, + A5XX_PREEMPT_RECORD_SIZE >> 2)) + return; + + SNAPSHOT_MEMCPY(snapshot, a5xx_gpu->preempt[index], + A5XX_PREEMPT_RECORD_SIZE); + } +} + int a5xx_snapshot(struct msm_gpu *gpu, struct msm_snapshot *snapshot) { struct crashdump crashdump = { 0 }; @@ -787,6 +816,9 @@ int a5xx_snapshot(struct msm_gpu *gpu, struct msm_snapshot *snapshot) /* CP MERCIU */ a5xx_snapshot_cp_merciu(gpu, snapshot); + /* Preemption records*/ + a5xx_snapshot_preemption(gpu, snapshot); + crashdump_destroy(gpu, &crashdump); snapshot->priv = NULL; diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 19267b2a3b49..9952fa8dcda5 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -405,10 +405,6 @@ void adreno_wait_ring(struct msm_ringbuffer *ring, uint32_t ndwords) ring->gpu->name, ring->id); } -static const char *iommu_ports[] = { - "gfx3d_user", -}; - /* Read the set of powerlevels */ static int _adreno_get_pwrlevels(struct msm_gpu *gpu, struct device_node *node) { @@ -524,10 +520,10 @@ static int adreno_of_parse(struct platform_device *pdev, struct msm_gpu *gpu) int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev, struct adreno_gpu *adreno_gpu, - const struct adreno_gpu_funcs *funcs, int nr_rings) + const struct adreno_gpu_funcs *funcs, + struct msm_gpu_config *gpu_config) { struct adreno_platform_config *config = pdev->dev.platform_data; - struct msm_gpu_config adreno_gpu_config = { 0 }; struct msm_gpu *gpu = &adreno_gpu->base; struct msm_mmu *mmu; int ret; @@ -541,26 +537,8 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev, /* Get the rest of the target configuration from the device tree */ adreno_of_parse(pdev, gpu); - adreno_gpu_config.ioname = "kgsl_3d0_reg_memory"; - adreno_gpu_config.irqname = "kgsl_3d0_irq"; - adreno_gpu_config.nr_rings = nr_rings; - - adreno_gpu_config.va_start = SZ_16M; - adreno_gpu_config.va_end = 0xffffffff; - - if (adreno_gpu->revn >= 500) { - /* 5XX targets use a 64 bit region */ - adreno_gpu_config.va_start = 0x800000000; - adreno_gpu_config.va_end = 0x8ffffffff; - } else { - adreno_gpu_config.va_start = 0x300000; - adreno_gpu_config.va_end = 0xffffffff; - } - - adreno_gpu_config.nr_rings = nr_rings; - ret = msm_gpu_init(drm, pdev, &adreno_gpu->base, &funcs->base, - adreno_gpu->info->name, &adreno_gpu_config); + adreno_gpu->info->name, gpu_config); if (ret) return ret; @@ -580,8 +558,7 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev, mmu = gpu->aspace->mmu; if (mmu) { - ret = mmu->funcs->attach(mmu, iommu_ports, - ARRAY_SIZE(iommu_ports)); + ret = mmu->funcs->attach(mmu, NULL, 0); if (ret) return ret; } @@ -722,7 +699,7 @@ static struct adreno_counter_group *get_counter_group(struct msm_gpu *gpu, return ERR_PTR(-ENODEV); if (groupid >= adreno_gpu->nr_counter_groups) - return ERR_PTR(-EINVAL); + return ERR_PTR(-ENODEV); return (struct adreno_counter_group *) adreno_gpu->counter_groups[groupid]; @@ -745,7 +722,7 @@ u64 adreno_read_counter(struct msm_gpu *gpu, u32 groupid, int counterid) struct adreno_counter_group *group = get_counter_group(gpu, groupid); - if (!IS_ERR(group) && group->funcs.read) + if (!IS_ERR_OR_NULL(group) && group->funcs.read) return group->funcs.read(gpu, group, counterid); return 0; @@ -756,6 +733,6 @@ void adreno_put_counter(struct msm_gpu *gpu, u32 groupid, int counterid) struct adreno_counter_group *group = get_counter_group(gpu, groupid); - if (!IS_ERR(group) && group->funcs.put) + if (!IS_ERR_OR_NULL(group) && group->funcs.put) group->funcs.put(gpu, group, counterid); } diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h index 8e8f3e5182d6..3f9bc655c383 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h @@ -257,7 +257,7 @@ struct msm_ringbuffer *adreno_active_ring(struct msm_gpu *gpu); int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev, struct adreno_gpu *gpu, const struct adreno_gpu_funcs *funcs, - int nr_rings); + struct msm_gpu_config *config); void adreno_gpu_cleanup(struct adreno_gpu *gpu); void adreno_snapshot(struct msm_gpu *gpu, struct msm_snapshot *snapshot); diff --git a/drivers/gpu/drm/msm/dba_bridge.c b/drivers/gpu/drm/msm/dba_bridge.c new file mode 100644 index 000000000000..f933a7f3dcfb --- /dev/null +++ b/drivers/gpu/drm/msm/dba_bridge.c @@ -0,0 +1,357 @@ +/* + * Copyright (c) 2016-2017, 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 <video/msm_dba.h> +#include "drm_edid.h" +#include "sde_kms.h" +#include "dba_bridge.h" + +#undef pr_fmt +#define pr_fmt(fmt) "dba_bridge:[%s] " fmt, __func__ + +/** + * struct dba_bridge - DBA bridge information + * @base: drm_bridge base + * @client_name: Client's name who calls the init + * @chip_name: Bridge chip name + * @name: Bridge chip name + * @id: Bridge driver index + * @display: Private display handle + * @list: Bridge chip driver list node + * @ops: DBA operation container + * @dba_ctx: DBA context + * @mode: DRM mode info + * @hdmi_mode: HDMI or DVI mode for the sink + * @num_of_input_lanes: Number of input lanes in case of DSI/LVDS + * @pluggable: If it's pluggable + * @panel_count: Number of panels attached to this display + */ +struct dba_bridge { + struct drm_bridge base; + char client_name[MSM_DBA_CLIENT_NAME_LEN]; + char chip_name[MSM_DBA_CHIP_NAME_MAX_LEN]; + u32 id; + void *display; + struct list_head list; + struct msm_dba_ops ops; + void *dba_ctx; + struct drm_display_mode mode; + bool hdmi_mode; + u32 num_of_input_lanes; + bool pluggable; + u32 panel_count; +}; +#define to_dba_bridge(x) container_of((x), struct dba_bridge, base) + +static void _dba_bridge_cb(void *data, enum msm_dba_callback_event event) +{ + struct dba_bridge *d_bridge = data; + + if (!d_bridge) { + SDE_ERROR("Invalid data\n"); + return; + } + + DRM_DEBUG("event: %d\n", event); + + switch (event) { + case MSM_DBA_CB_HPD_CONNECT: + DRM_DEBUG("HPD CONNECT\n"); + break; + case MSM_DBA_CB_HPD_DISCONNECT: + DRM_DEBUG("HPD DISCONNECT\n"); + break; + default: + DRM_DEBUG("event:%d is not supported\n", event); + break; + } +} + +static int _dba_bridge_attach(struct drm_bridge *bridge) +{ + struct dba_bridge *d_bridge = to_dba_bridge(bridge); + struct msm_dba_reg_info info; + int ret = 0; + + if (!bridge) { + SDE_ERROR("Invalid params\n"); + return -EINVAL; + } + + memset(&info, 0, sizeof(info)); + /* initialize DBA registration data */ + strlcpy(info.client_name, d_bridge->client_name, + MSM_DBA_CLIENT_NAME_LEN); + strlcpy(info.chip_name, d_bridge->chip_name, + MSM_DBA_CHIP_NAME_MAX_LEN); + info.instance_id = d_bridge->id; + info.cb = _dba_bridge_cb; + info.cb_data = d_bridge; + + /* register client with DBA and get device's ops*/ + if (IS_ENABLED(CONFIG_MSM_DBA)) { + d_bridge->dba_ctx = msm_dba_register_client(&info, + &d_bridge->ops); + if (IS_ERR_OR_NULL(d_bridge->dba_ctx)) { + SDE_ERROR("dba register failed\n"); + ret = PTR_ERR(d_bridge->dba_ctx); + goto error; + } + } else { + SDE_ERROR("DBA not enabled\n"); + ret = -ENODEV; + goto error; + } + + DRM_INFO("client:%s bridge:[%s:%d] attached\n", + d_bridge->client_name, d_bridge->chip_name, d_bridge->id); + +error: + return ret; +} + +static void _dba_bridge_pre_enable(struct drm_bridge *bridge) +{ + if (!bridge) { + SDE_ERROR("Invalid params\n"); + return; + } +} + +static void _dba_bridge_enable(struct drm_bridge *bridge) +{ + int rc = 0; + struct dba_bridge *d_bridge = to_dba_bridge(bridge); + struct msm_dba_video_cfg video_cfg; + struct drm_display_mode *mode; + struct hdmi_avi_infoframe avi_frame; + + if (!bridge) { + SDE_ERROR("Invalid params\n"); + return; + } + + memset(&video_cfg, 0, sizeof(video_cfg)); + memset(&avi_frame, 0, sizeof(avi_frame)); + mode = &d_bridge->mode; + video_cfg.h_active = mode->hdisplay; + video_cfg.v_active = mode->vdisplay; + video_cfg.h_front_porch = mode->hsync_start - mode->hdisplay; + video_cfg.v_front_porch = mode->vsync_start - mode->vdisplay; + video_cfg.h_back_porch = mode->htotal - mode->hsync_end; + video_cfg.v_back_porch = mode->vtotal - mode->vsync_end; + video_cfg.h_pulse_width = mode->hsync_end - mode->hsync_start; + video_cfg.v_pulse_width = mode->vsync_end - mode->vsync_start; + video_cfg.pclk_khz = mode->clock; + video_cfg.hdmi_mode = d_bridge->hdmi_mode; + video_cfg.num_of_input_lanes = d_bridge->num_of_input_lanes; + + SDE_DEBUG( + "video=h[%d,%d,%d,%d] v[%d,%d,%d,%d] pclk=%d hdmi=%d lane=%d\n", + video_cfg.h_active, video_cfg.h_front_porch, + video_cfg.h_pulse_width, video_cfg.h_back_porch, + video_cfg.v_active, video_cfg.v_front_porch, + video_cfg.v_pulse_width, video_cfg.v_back_porch, + video_cfg.pclk_khz, video_cfg.hdmi_mode, + video_cfg.num_of_input_lanes); + + rc = drm_hdmi_avi_infoframe_from_display_mode(&avi_frame, mode); + if (rc) { + SDE_ERROR("get avi frame failed ret=%d\n", rc); + } else { + video_cfg.scaninfo = avi_frame.scan_mode; + switch (avi_frame.picture_aspect) { + case HDMI_PICTURE_ASPECT_4_3: + video_cfg.ar = MSM_DBA_AR_4_3; + break; + case HDMI_PICTURE_ASPECT_16_9: + video_cfg.ar = MSM_DBA_AR_16_9; + break; + default: + break; + } + video_cfg.vic = avi_frame.video_code; + DRM_INFO("scaninfo=%d ar=%d vic=%d\n", + video_cfg.scaninfo, video_cfg.ar, video_cfg.vic); + } + + if (d_bridge->ops.video_on) { + rc = d_bridge->ops.video_on(d_bridge->dba_ctx, true, + &video_cfg, 0); + if (rc) + SDE_ERROR("video on failed ret=%d\n", rc); + } +} + +static void _dba_bridge_disable(struct drm_bridge *bridge) +{ + int rc = 0; + struct dba_bridge *d_bridge = to_dba_bridge(bridge); + + if (!bridge) { + SDE_ERROR("Invalid params\n"); + return; + } + + if (d_bridge->ops.video_on) { + rc = d_bridge->ops.video_on(d_bridge->dba_ctx, false, NULL, 0); + if (rc) + SDE_ERROR("video off failed ret=%d\n", rc); + } +} + +static void _dba_bridge_post_disable(struct drm_bridge *bridge) +{ + if (!bridge) { + SDE_ERROR("Invalid params\n"); + return; + } +} + +static void _dba_bridge_mode_set(struct drm_bridge *bridge, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct dba_bridge *d_bridge = to_dba_bridge(bridge); + + if (!bridge || !mode || !adjusted_mode || !d_bridge) { + SDE_ERROR("Invalid params\n"); + return; + } else if (!d_bridge->panel_count) { + SDE_ERROR("Panel count is 0\n"); + return; + } + + d_bridge->mode = *adjusted_mode; + /* Adjust mode according to number of panels */ + d_bridge->mode.hdisplay /= d_bridge->panel_count; + d_bridge->mode.hsync_start /= d_bridge->panel_count; + d_bridge->mode.hsync_end /= d_bridge->panel_count; + d_bridge->mode.htotal /= d_bridge->panel_count; + d_bridge->mode.clock /= d_bridge->panel_count; +} + +static bool _dba_bridge_mode_fixup(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + bool ret = true; + + if (!bridge || !mode || !adjusted_mode) { + SDE_ERROR("Invalid params\n"); + return false; + } + + return ret; +} + +static const struct drm_bridge_funcs _dba_bridge_ops = { + .attach = _dba_bridge_attach, + .mode_fixup = _dba_bridge_mode_fixup, + .pre_enable = _dba_bridge_pre_enable, + .enable = _dba_bridge_enable, + .disable = _dba_bridge_disable, + .post_disable = _dba_bridge_post_disable, + .mode_set = _dba_bridge_mode_set, +}; + +struct drm_bridge *dba_bridge_init(struct drm_device *dev, + struct drm_encoder *encoder, + struct dba_bridge_init *data) +{ + int rc = 0; + struct dba_bridge *bridge; + struct msm_drm_private *priv = NULL; + + if (!dev || !encoder || !data) { + SDE_ERROR("dev=%pK or encoder=%pK or data=%pK is NULL\n", + dev, encoder, data); + rc = -EINVAL; + goto error; + } + + priv = dev->dev_private; + if (!priv) { + SDE_ERROR("Private data is not present\n"); + rc = -EINVAL; + goto error; + } + + bridge = kzalloc(sizeof(*bridge), GFP_KERNEL); + if (!bridge) { + SDE_ERROR("out of memory\n"); + rc = -ENOMEM; + goto error; + } + + INIT_LIST_HEAD(&bridge->list); + strlcpy(bridge->client_name, data->client_name, + MSM_DBA_CLIENT_NAME_LEN); + strlcpy(bridge->chip_name, data->chip_name, + MSM_DBA_CHIP_NAME_MAX_LEN); + bridge->id = data->id; + bridge->display = data->display; + bridge->hdmi_mode = data->hdmi_mode; + bridge->num_of_input_lanes = data->num_of_input_lanes; + bridge->pluggable = data->pluggable; + bridge->panel_count = data->panel_count; + bridge->base.funcs = &_dba_bridge_ops; + bridge->base.encoder = encoder; + + rc = drm_bridge_attach(dev, &bridge->base); + if (rc) { + SDE_ERROR("failed to attach bridge, rc=%d\n", rc); + goto error_free_bridge; + } + + if (data->precede_bridge) { + /* Insert current bridge */ + bridge->base.next = data->precede_bridge->next; + data->precede_bridge->next = &bridge->base; + } else { + encoder->bridge = &bridge->base; + } + + if (!bridge->pluggable) { + if (bridge->ops.power_on) + bridge->ops.power_on(bridge->dba_ctx, true, 0); + if (bridge->ops.check_hpd) + bridge->ops.check_hpd(bridge->dba_ctx, 0); + } + + return &bridge->base; + +error_free_bridge: + kfree(bridge); +error: + return ERR_PTR(rc); +} + +void dba_bridge_cleanup(struct drm_bridge *bridge) +{ + struct dba_bridge *d_bridge = to_dba_bridge(bridge); + + if (!bridge) + return; + + if (IS_ENABLED(CONFIG_MSM_DBA)) { + if (!IS_ERR_OR_NULL(d_bridge->dba_ctx)) + msm_dba_deregister_client(d_bridge->dba_ctx); + } + + if (d_bridge->base.encoder) + d_bridge->base.encoder->bridge = NULL; + + kfree(bridge); +} diff --git a/drivers/gpu/drm/msm/dba_bridge.h b/drivers/gpu/drm/msm/dba_bridge.h new file mode 100644 index 000000000000..5562d2b2aef9 --- /dev/null +++ b/drivers/gpu/drm/msm/dba_bridge.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2016-2017, 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 _DBA_BRIDGE_H_ +#define _DBA_BRIDGE_H_ + +#include <linux/types.h> +#include <drm/drmP.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> + +#include "msm_drv.h" + +/** + * struct dba_bridge_init - Init parameters for DBA bridge + * @client_name: Client's name who calls the init + * @chip_name: Bridge chip name + * @id: Bridge driver index + * @display: Private display handle + * @hdmi_mode: HDMI or DVI mode for the sink + * @num_of_input_lanes: Number of input lanes in case of DSI/LVDS + * @precede_bridge: Precede bridge chip + * @pluggable: If it's pluggable + * @panel_count: Number of panels attached to this display + */ +struct dba_bridge_init { + const char *client_name; + const char *chip_name; + u32 id; + void *display; + bool hdmi_mode; + u32 num_of_input_lanes; + struct drm_bridge *precede_bridge; + bool pluggable; + u32 panel_count; +}; + +/** + * dba_bridge_init - Initialize the DBA bridge + * @dev: Pointer to drm device handle + * @encoder: Pointer to drm encoder handle + * @data: Pointer to init data + * Returns: pointer of struct drm_bridge + */ +struct drm_bridge *dba_bridge_init(struct drm_device *dev, + struct drm_encoder *encoder, + struct dba_bridge_init *data); + +/** + * dba_bridge_cleanup - Clean up the DBA bridge + * @bridge: Pointer to DBA bridge handle + * Returns: void + */ +void dba_bridge_cleanup(struct drm_bridge *bridge); + +#endif /* _DBA_BRIDGE_H_ */ diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_1_4.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_1_4.c index ca04eedd6af1..2f0f6c2f1b01 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_1_4.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_1_4.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2017, 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 @@ -85,6 +85,14 @@ void dsi_ctrl_hw_14_host_setup(struct dsi_ctrl_hw *ctrl, DSI_W32(ctrl, DSI_CTRL, reg_value); + /* Force clock lane in HS */ + reg_value = DSI_R32(ctrl, DSI_LANE_CTRL); + if (cfg->force_clk_lane_hs) + reg_value |= BIT(28); + else + reg_value &= ~BIT(28); + DSI_W32(ctrl, DSI_LANE_CTRL, reg_value); + pr_debug("[DSI_%d]Host configuration complete\n", ctrl->index); } @@ -604,8 +612,9 @@ void dsi_ctrl_hw_14_ulps_request(struct dsi_ctrl_hw *ctrl, u32 lanes) { u32 reg = 0; + reg = DSI_R32(ctrl, DSI_LANE_CTRL); if (lanes & DSI_CLOCK_LANE) - reg = BIT(4); + reg |= BIT(4); if (lanes & DSI_DATA_LANE_0) reg |= BIT(0); if (lanes & DSI_DATA_LANE_1) @@ -664,7 +673,8 @@ void dsi_ctrl_hw_14_clear_ulps_request(struct dsi_ctrl_hw *ctrl, u32 lanes) u32 reg = 0; reg = DSI_R32(ctrl, DSI_LANE_CTRL); - reg &= ~BIT(4); /* clock lane */ + if (lanes & DSI_CLOCK_LANE) + reg &= ~BIT(4); /* clock lane */ if (lanes & DSI_DATA_LANE_0) reg &= ~BIT(0); if (lanes & DSI_DATA_LANE_1) @@ -679,7 +689,18 @@ void dsi_ctrl_hw_14_clear_ulps_request(struct dsi_ctrl_hw *ctrl, u32 lanes) * HPG recommends separate writes for clearing ULPS_REQUEST and * ULPS_EXIT. */ - DSI_W32(ctrl, DSI_LANE_CTRL, 0x0); + reg = DSI_R32(ctrl, DSI_LANE_CTRL); + if (lanes & DSI_CLOCK_LANE) + reg &= ~BIT(12); + if (lanes & DSI_DATA_LANE_0) + reg &= ~BIT(8); + if (lanes & DSI_DATA_LANE_1) + reg &= ~BIT(9); + if (lanes & DSI_DATA_LANE_2) + reg &= ~BIT(10); + if (lanes & DSI_DATA_LANE_3) + reg &= ~BIT(11); + DSI_W32(ctrl, DSI_LANE_CTRL, reg); pr_debug("[DSI_%d] ULPS request cleared\n", ctrl->index); } diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h b/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h index 2caa32ea8f0c..d9fcec60693d 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, 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 @@ -259,6 +259,7 @@ struct dsi_lane_mapping { * @ignore_rx_eot: Ignore Rx EOT packets if set to true. * @append_tx_eot: Append EOT packets for forward transmissions if set to * true. + * @force_clk_lane_hs: Force clock lane in high speed mode. */ struct dsi_host_common_cfg { enum dsi_pixel_format dst_format; @@ -277,6 +278,7 @@ struct dsi_host_common_cfg { u32 t_clk_pre; bool ignore_rx_eot; bool append_tx_eot; + bool force_clk_lane_hs; }; /** diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c index 5a166a4bae93..f2412daee8b6 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, 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,13 +18,16 @@ #include <linux/of.h> #include "msm_drv.h" +#include "sde_kms.h" #include "dsi_display.h" #include "dsi_panel.h" #include "dsi_ctrl.h" #include "dsi_ctrl_hw.h" #include "dsi_drm.h" +#include "dba_bridge.h" #define to_dsi_display(x) container_of(x, struct dsi_display, host) +#define DSI_DBA_CLIENT_NAME "dsi" static DEFINE_MUTEX(dsi_display_list_lock); static LIST_HEAD(dsi_display_list); @@ -45,7 +48,7 @@ int dsi_display_set_backlight(void *display, u32 bl_lvl) if (dsi_display == NULL) return -EINVAL; - panel = dsi_display->panel; + panel = dsi_display->panel[0]; rc = dsi_panel_set_backlight(panel, bl_lvl); if (rc) @@ -87,8 +90,9 @@ static ssize_t debugfs_dump_info_read(struct file *file, display->ctrl[i].phy->name); } - len += snprintf(buf + len, (SZ_4K - len), - "\tPanel = %s\n", display->panel->name); + for (i = 0; i < display->panel_count; i++) + len += snprintf(buf + len, (SZ_4K - len), + "\tPanel_%d = %s\n", i, display->panel[i]->name); len += snprintf(buf + len, (SZ_4K - len), "\tClock master = %s\n", @@ -1108,7 +1112,7 @@ static int dsi_display_parse_lane_map(struct dsi_display *display) static int dsi_display_parse_dt(struct dsi_display *display) { int rc = 0; - int i; + int i, size; u32 phy_count = 0; struct device_node *of_node; @@ -1151,14 +1155,69 @@ static int dsi_display_parse_dt(struct dsi_display *display) goto error; } - of_node = of_parse_phandle(display->pdev->dev.of_node, - "qcom,dsi-panel", 0); - if (!of_node) { - pr_err("No Panel device present\n"); + if (of_get_property(display->pdev->dev.of_node, "qcom,dsi-panel", + &size)) { + display->panel_count = size / sizeof(int); + display->panel_of = devm_kzalloc(&display->pdev->dev, + sizeof(struct device_node *) * display->panel_count, + GFP_KERNEL); + if (!display->panel_of) { + SDE_ERROR("out of memory for panel_of\n"); + rc = -ENOMEM; + goto error; + } + display->panel = devm_kzalloc(&display->pdev->dev, + sizeof(struct dsi_panel *) * display->panel_count, + GFP_KERNEL); + if (!display->panel) { + SDE_ERROR("out of memory for panel\n"); + rc = -ENOMEM; + goto error; + } + for (i = 0; i < display->panel_count; i++) { + display->panel_of[i] = + of_parse_phandle(display->pdev->dev.of_node, + "qcom,dsi-panel", i); + if (!display->panel_of[i]) { + SDE_ERROR("of_parse dsi-panel failed\n"); + rc = -ENODEV; + goto error; + } + } + } else { + SDE_ERROR("No qcom,dsi-panel of node\n"); rc = -ENODEV; goto error; - } else { - display->panel_of = of_node; + } + + if (of_get_property(display->pdev->dev.of_node, "qcom,bridge-index", + &size)) { + if (size / sizeof(int) != display->panel_count) { + SDE_ERROR("size=%lu is different than count=%u\n", + size / sizeof(int), display->panel_count); + rc = -EINVAL; + goto error; + } + display->bridge_idx = devm_kzalloc(&display->pdev->dev, + sizeof(u32) * display->panel_count, GFP_KERNEL); + if (!display->bridge_idx) { + SDE_ERROR("out of memory for bridge_idx\n"); + rc = -ENOMEM; + goto error; + } + for (i = 0; i < display->panel_count; i++) { + rc = of_property_read_u32_index( + display->pdev->dev.of_node, + "qcom,bridge-index", i, + &(display->bridge_idx[i])); + if (rc) { + SDE_ERROR( + "read bridge-index error,i=%d rc=%d\n", + i, rc); + rc = -ENODEV; + goto error; + } + } } rc = dsi_display_parse_lane_map(display); @@ -1167,6 +1226,16 @@ static int dsi_display_parse_dt(struct dsi_display *display) goto error; } error: + if (rc) { + if (display->panel_of) + for (i = 0; i < display->panel_count; i++) + if (display->panel_of[i]) + of_node_put(display->panel_of[i]); + devm_kfree(&display->pdev->dev, display->panel_of); + devm_kfree(&display->pdev->dev, display->panel); + devm_kfree(&display->pdev->dev, display->bridge_idx); + display->panel_count = 0; + } return rc; } @@ -1196,12 +1265,15 @@ static int dsi_display_res_init(struct dsi_display *display) } } - display->panel = dsi_panel_get(&display->pdev->dev, display->panel_of); - if (IS_ERR_OR_NULL(display->panel)) { - rc = PTR_ERR(display->panel); - pr_err("failed to get panel, rc=%d\n", rc); - display->panel = NULL; - goto error_ctrl_put; + for (i = 0; i < display->panel_count; i++) { + display->panel[i] = dsi_panel_get(&display->pdev->dev, + display->panel_of[i]); + if (IS_ERR_OR_NULL(display->panel)) { + rc = PTR_ERR(display->panel); + pr_err("failed to get panel, rc=%d\n", rc); + display->panel[i] = NULL; + goto error_ctrl_put; + } } rc = dsi_display_clocks_init(display); @@ -1230,6 +1302,9 @@ static int dsi_display_res_deinit(struct dsi_display *display) if (rc) pr_err("clocks deinit failed, rc=%d\n", rc); + for (i = 0; i < display->panel_count; i++) + dsi_panel_put(display->panel[i]); + for (i = 0; i < display->ctrl_count; i++) { ctrl = &display->ctrl[i]; dsi_phy_put(ctrl->phy); @@ -1279,7 +1354,7 @@ static bool dsi_display_is_seamless_dfps_possible( return false; } - cur = &display->panel->mode; + cur = &display->panel[0]->mode; if (cur->timing.h_active != tgt->timing.h_active) { pr_debug("timing.h_active differs %d %d\n", @@ -1388,7 +1463,7 @@ static int dsi_display_dfps_update(struct dsi_display *display, } timing = &dsi_mode->timing; - dsi_panel_get_dfps_caps(display->panel, &dfps_caps); + dsi_panel_get_dfps_caps(display->panel[0], &dfps_caps); if (!dfps_caps.dfps_support) { pr_err("dfps not supported\n"); return -ENOTSUPP; @@ -1425,7 +1500,7 @@ static int dsi_display_dfps_update(struct dsi_display *display, } } - panel_mode = &display->panel->mode; + panel_mode = &display->panel[0]->mode; memcpy(panel_mode, dsi_mode, sizeof(*panel_mode)); error: @@ -1493,7 +1568,8 @@ static int dsi_display_get_dfps_timing(struct dsi_display *display, } m_ctrl = display->ctrl[display->clk_master_idx].ctrl; - dsi_panel_get_dfps_caps(display->panel, &dfps_caps); + /* Only check the first panel */ + dsi_panel_get_dfps_caps(display->panel[0], &dfps_caps); if (!dfps_caps.dfps_support) { pr_err("dfps not supported by panel\n"); return -EINVAL; @@ -1574,7 +1650,7 @@ static int dsi_display_set_mode_sub(struct dsi_display *display, int i; struct dsi_display_ctrl *ctrl; - rc = dsi_panel_get_host_cfg_for_mode(display->panel, + rc = dsi_panel_get_host_cfg_for_mode(display->panel[0], mode, &display->config); if (rc) { @@ -1687,7 +1763,7 @@ static int dsi_display_bind(struct device *dev, struct drm_device *drm; struct dsi_display *display; struct platform_device *pdev = to_platform_device(dev); - int i, rc = 0; + int i, j, rc = 0; if (!dev || !pdev || !master) { pr_err("invalid param(s), dev %pK, pdev %pK, master %pK\n", @@ -1737,15 +1813,19 @@ static int dsi_display_bind(struct device *dev, goto error_ctrl_deinit; } - rc = dsi_panel_drv_init(display->panel, &display->host); - if (rc) { - if (rc != -EPROBE_DEFER) - pr_err("[%s] failed to initialize panel driver, rc=%d\n", - display->name, rc); - goto error_host_deinit; + for (j = 0; j < display->panel_count; j++) { + rc = dsi_panel_drv_init(display->panel[j], &display->host); + if (rc) { + if (rc != -EPROBE_DEFER) + SDE_ERROR( + "[%s]Failed to init panel driver, rc=%d\n", + display->name, rc); + goto error_panel_deinit; + } } - rc = dsi_panel_get_mode_count(display->panel, &display->num_of_modes); + rc = dsi_panel_get_mode_count(display->panel[0], + &display->num_of_modes); if (rc) { pr_err("[%s] failed to get mode count, rc=%d\n", display->name, rc); @@ -1756,8 +1836,8 @@ static int dsi_display_bind(struct device *dev, goto error; error_panel_deinit: - (void)dsi_panel_drv_deinit(display->panel); -error_host_deinit: + for (j--; j >= 0; j--) + (void)dsi_panel_drv_deinit(display->panel[j]); (void)dsi_display_mipi_host_deinit(display); error_ctrl_deinit: for (i = i - 1; i >= 0; i--) { @@ -1798,10 +1878,12 @@ static void dsi_display_unbind(struct device *dev, mutex_lock(&display->display_lock); - rc = dsi_panel_drv_deinit(display->panel); - if (rc) - pr_err("[%s] failed to deinit panel driver, rc=%d\n", - display->name, rc); + for (i = 0; i < display->panel_count; i++) { + rc = dsi_panel_drv_deinit(display->panel[i]); + if (rc) + SDE_ERROR("[%s] failed to deinit panel driver, rc=%d\n", + display->name, rc); + } rc = dsi_display_mipi_host_deinit(display); if (rc) @@ -1870,7 +1952,7 @@ int dsi_display_dev_probe(struct platform_device *pdev) display->pdev = pdev; platform_set_drvdata(pdev, display); mutex_lock(&dsi_display_list_lock); - list_add(&display->list, &dsi_display_list); + list_add_tail(&display->list, &dsi_display_list); mutex_unlock(&dsi_display_list_lock); if (display->is_active) { @@ -1890,7 +1972,7 @@ int dsi_display_dev_probe(struct platform_device *pdev) int dsi_display_dev_remove(struct platform_device *pdev) { - int rc = 0; + int rc = 0, i; struct dsi_display *display; struct dsi_display *pos, *tmp; @@ -1913,6 +1995,13 @@ int dsi_display_dev_remove(struct platform_device *pdev) mutex_unlock(&dsi_display_list_lock); platform_set_drvdata(pdev, NULL); + if (display->panel_of) + for (i = 0; i < display->panel_count; i++) + if (display->panel_of[i]) + of_node_put(display->panel_of[i]); + devm_kfree(&pdev->dev, display->panel_of); + devm_kfree(&pdev->dev, display->panel); + devm_kfree(&pdev->dev, display->bridge_idx); devm_kfree(&pdev->dev, display); return rc; } @@ -1984,9 +2073,15 @@ void dsi_display_set_active_state(struct dsi_display *display, bool is_active) int dsi_display_drm_bridge_init(struct dsi_display *display, struct drm_encoder *enc) { - int rc = 0; + int rc = 0, i; struct dsi_bridge *bridge; + struct drm_bridge *dba_bridge; + struct dba_bridge_init init_data; + struct drm_bridge *precede_bridge; struct msm_drm_private *priv = NULL; + struct dsi_panel *panel; + u32 *bridge_idx; + u32 num_of_lanes = 0; if (!display || !display->drm_dev || !enc) { pr_err("invalid param(s)\n"); @@ -1997,44 +2092,112 @@ int dsi_display_drm_bridge_init(struct dsi_display *display, priv = display->drm_dev->dev_private; if (!priv) { - pr_err("Private data is not present\n"); + SDE_ERROR("Private data is not present\n"); rc = -EINVAL; - goto error; + goto out; } if (display->bridge) { - pr_err("display is already initialize\n"); - goto error; + SDE_ERROR("display is already initialize\n"); + goto out; } bridge = dsi_drm_bridge_init(display, display->drm_dev, enc); if (IS_ERR_OR_NULL(bridge)) { rc = PTR_ERR(bridge); - pr_err("[%s] brige init failed, %d\n", display->name, rc); - goto error; + SDE_ERROR("[%s] brige init failed, %d\n", display->name, rc); + goto out; } display->bridge = bridge; priv->bridges[priv->num_bridges++] = &bridge->base; + precede_bridge = &bridge->base; + + if (display->panel_count >= MAX_BRIDGES - 1) { + SDE_ERROR("too many bridge chips=%d\n", display->panel_count); + goto error_bridge; + } + + for (i = 0; i < display->panel_count; i++) { + panel = display->panel[i]; + if (panel && display->bridge_idx && + panel->dba_config.dba_panel) { + bridge_idx = display->bridge_idx + i; + num_of_lanes = 0; + memset(&init_data, 0x00, sizeof(init_data)); + if (panel->host_config.data_lanes & DSI_DATA_LANE_0) + num_of_lanes++; + if (panel->host_config.data_lanes & DSI_DATA_LANE_1) + num_of_lanes++; + if (panel->host_config.data_lanes & DSI_DATA_LANE_2) + num_of_lanes++; + if (panel->host_config.data_lanes & DSI_DATA_LANE_3) + num_of_lanes++; + init_data.client_name = DSI_DBA_CLIENT_NAME; + init_data.chip_name = panel->dba_config.bridge_name; + init_data.id = *bridge_idx; + init_data.display = display; + init_data.hdmi_mode = panel->dba_config.hdmi_mode; + init_data.num_of_input_lanes = num_of_lanes; + init_data.precede_bridge = precede_bridge; + init_data.panel_count = display->panel_count; + dba_bridge = dba_bridge_init(display->drm_dev, enc, + &init_data); + if (IS_ERR_OR_NULL(dba_bridge)) { + rc = PTR_ERR(dba_bridge); + SDE_ERROR("[%s:%d] dba brige init failed, %d\n", + init_data.chip_name, init_data.id, rc); + goto error_dba_bridge; + } + priv->bridges[priv->num_bridges++] = dba_bridge; + precede_bridge = dba_bridge; + } + } -error: + goto out; + +error_dba_bridge: + for (i = 1; i < MAX_BRIDGES; i++) { + dba_bridge_cleanup(priv->bridges[i]); + priv->bridges[i] = NULL; + } +error_bridge: + dsi_drm_bridge_cleanup(display->bridge); + display->bridge = NULL; + priv->bridges[0] = NULL; + priv->num_bridges = 0; +out: mutex_unlock(&display->display_lock); return rc; } int dsi_display_drm_bridge_deinit(struct dsi_display *display) { - int rc = 0; + int rc = 0, i; + struct msm_drm_private *priv = NULL; if (!display) { - pr_err("Invalid params\n"); + SDE_ERROR("Invalid params\n"); + return -EINVAL; + } + priv = display->drm_dev->dev_private; + + if (!priv) { + SDE_ERROR("Private data is not present\n"); return -EINVAL; } mutex_lock(&display->display_lock); + for (i = 1; i < MAX_BRIDGES; i++) { + dba_bridge_cleanup(priv->bridges[i]); + priv->bridges[i] = NULL; + } + dsi_drm_bridge_cleanup(display->bridge); display->bridge = NULL; + priv->bridges[0] = NULL; + priv->num_bridges = 0; mutex_unlock(&display->display_lock); return rc; @@ -2053,7 +2216,7 @@ int dsi_display_get_info(struct msm_display_info *info, void *disp) display = disp; mutex_lock(&display->display_lock); - rc = dsi_panel_get_phy_props(display->panel, &phy_props); + rc = dsi_panel_get_phy_props(display->panel[0], &phy_props); if (rc) { pr_err("[%s] failed to get panel phy props, rc=%d\n", display->name, rc); @@ -2073,7 +2236,7 @@ int dsi_display_get_info(struct msm_display_info *info, void *disp) info->max_height = 1080; info->compression = MSM_DISPLAY_COMPRESS_NONE; - switch (display->panel->mode.panel_mode) { + switch (display->panel[0]->mode.panel_mode) { case DSI_OP_VIDEO_MODE: info->capabilities |= MSM_DISPLAY_CAP_VID_MODE; break; @@ -2082,7 +2245,7 @@ int dsi_display_get_info(struct msm_display_info *info, void *disp) break; default: pr_err("unknwown dsi panel mode %d\n", - display->panel->mode.panel_mode); + display->panel[0]->mode.panel_mode); break; } error: @@ -2106,7 +2269,7 @@ int dsi_display_get_modes(struct dsi_display *display, mutex_lock(&display->display_lock); - rc = dsi_panel_get_dfps_caps(display->panel, &dfps_caps); + rc = dsi_panel_get_dfps_caps(display->panel[0], &dfps_caps); if (rc) { pr_err("[%s] failed to get dfps caps from panel\n", display->name); @@ -2127,7 +2290,8 @@ int dsi_display_get_modes(struct dsi_display *display, /* Insert the dfps "sub-modes" between main panel modes */ int panel_mode_idx = i / num_dfps_rates; - rc = dsi_panel_get_mode(display->panel, panel_mode_idx, modes); + rc = dsi_panel_get_mode(display->panel[0], panel_mode_idx, + modes); if (rc) { pr_err("[%s] failed to get mode from panel\n", display->name); @@ -2178,7 +2342,7 @@ int dsi_display_validate_mode(struct dsi_display *display, adj_mode = *mode; adjust_timing_by_ctrl_count(display, &adj_mode); - rc = dsi_panel_validate_mode(display->panel, &adj_mode); + rc = dsi_panel_validate_mode(display->panel[0], &adj_mode); if (rc) { pr_err("[%s] panel mode validation failed, rc=%d\n", display->name, rc); @@ -2278,7 +2442,7 @@ error: int dsi_display_prepare(struct dsi_display *display) { - int rc = 0; + int rc = 0, i, j; if (!display) { pr_err("Invalid params\n"); @@ -2287,11 +2451,13 @@ int dsi_display_prepare(struct dsi_display *display) mutex_lock(&display->display_lock); - rc = dsi_panel_pre_prepare(display->panel); - if (rc) { - pr_err("[%s] panel pre-prepare failed, rc=%d\n", - display->name, rc); - goto error; + for (i = 0; i < display->panel_count; i++) { + rc = dsi_panel_pre_prepare(display->panel[i]); + if (rc) { + SDE_ERROR("[%s] panel pre-prepare failed, rc=%d\n", + display->name, rc); + goto error_panel_post_unprep; + } } rc = dsi_display_ctrl_power_on(display); @@ -2349,15 +2515,20 @@ int dsi_display_prepare(struct dsi_display *display) goto error_ctrl_link_off; } - rc = dsi_panel_prepare(display->panel); - if (rc) { - pr_err("[%s] panel prepare failed, rc=%d\n", display->name, rc); - goto error_host_engine_off; + for (j = 0; j < display->panel_count; j++) { + rc = dsi_panel_prepare(display->panel[j]); + if (rc) { + SDE_ERROR("[%s] panel prepare failed, rc=%d\n", + display->name, rc); + goto error_panel_unprep; + } } goto error; -error_host_engine_off: +error_panel_unprep: + for (j--; j >= 0; j--) + (void)dsi_panel_unprepare(display->panel[j]); (void)dsi_display_ctrl_host_disable(display); error_ctrl_link_off: (void)dsi_display_ctrl_link_clk_off(display); @@ -2372,7 +2543,8 @@ error_phy_pwr_off: error_ctrl_pwr_off: (void)dsi_display_ctrl_power_off(display); error_panel_post_unprep: - (void)dsi_panel_post_unprepare(display->panel); + for (i--; i >= 0; i--) + (void)dsi_panel_post_unprepare(display->panel[i]); error: mutex_unlock(&display->display_lock); return rc; @@ -2380,7 +2552,7 @@ error: int dsi_display_enable(struct dsi_display *display) { - int rc = 0; + int rc = 0, i; if (!display) { pr_err("Invalid params\n"); @@ -2389,11 +2561,13 @@ int dsi_display_enable(struct dsi_display *display) mutex_lock(&display->display_lock); - rc = dsi_panel_enable(display->panel); - if (rc) { - pr_err("[%s] failed to enable DSI panel, rc=%d\n", - display->name, rc); - goto error; + for (i = 0; i < display->panel_count; i++) { + rc = dsi_panel_enable(display->panel[i]); + if (rc) { + SDE_ERROR("[%s] failed to enable DSI panel, rc=%d\n", + display->name, rc); + goto error_disable_panel; + } } if (display->config.panel_mode == DSI_OP_VIDEO_MODE) { @@ -2419,7 +2593,8 @@ int dsi_display_enable(struct dsi_display *display) goto error; error_disable_panel: - (void)dsi_panel_disable(display->panel); + for (i--; i >= 0; i--) + (void)dsi_panel_disable(display->panel[i]); error: mutex_unlock(&display->display_lock); return rc; @@ -2427,7 +2602,7 @@ error: int dsi_display_post_enable(struct dsi_display *display) { - int rc = 0; + int rc = 0, i; if (!display) { pr_err("Invalid params\n"); @@ -2436,10 +2611,12 @@ int dsi_display_post_enable(struct dsi_display *display) mutex_lock(&display->display_lock); - rc = dsi_panel_post_enable(display->panel); - if (rc) - pr_err("[%s] panel post-enable failed, rc=%d\n", - display->name, rc); + for (i = 0; i < display->panel_count; i++) { + rc = dsi_panel_post_enable(display->panel[i]); + if (rc) + SDE_ERROR("[%s] panel post-enable failed, rc=%d\n", + display->name, rc); + } mutex_unlock(&display->display_lock); return rc; @@ -2447,7 +2624,7 @@ int dsi_display_post_enable(struct dsi_display *display) int dsi_display_pre_disable(struct dsi_display *display) { - int rc = 0; + int rc = 0, i; if (!display) { pr_err("Invalid params\n"); @@ -2456,10 +2633,12 @@ int dsi_display_pre_disable(struct dsi_display *display) mutex_lock(&display->display_lock); - rc = dsi_panel_pre_disable(display->panel); - if (rc) - pr_err("[%s] panel pre-disable failed, rc=%d\n", - display->name, rc); + for (i = 0; i < display->panel_count; i++) { + rc = dsi_panel_pre_disable(display->panel[i]); + if (rc) + SDE_ERROR("[%s] panel pre-disable failed, rc=%d\n", + display->name, rc); + } mutex_unlock(&display->display_lock); return rc; @@ -2467,7 +2646,7 @@ int dsi_display_pre_disable(struct dsi_display *display) int dsi_display_disable(struct dsi_display *display) { - int rc = 0; + int rc = 0, i; if (!display) { pr_err("Invalid params\n"); @@ -2481,10 +2660,12 @@ int dsi_display_disable(struct dsi_display *display) pr_err("[%s] display wake up failed, rc=%d\n", display->name, rc); - rc = dsi_panel_disable(display->panel); - if (rc) - pr_err("[%s] failed to disable DSI panel, rc=%d\n", - display->name, rc); + for (i = 0; i < display->panel_count; i++) { + rc = dsi_panel_disable(display->panel[i]); + if (rc) + SDE_ERROR("[%s] failed to disable DSI panel, rc=%d\n", + display->name, rc); + } if (display->config.panel_mode == DSI_OP_VIDEO_MODE) { rc = dsi_display_vid_engine_disable(display); @@ -2507,7 +2688,7 @@ int dsi_display_disable(struct dsi_display *display) int dsi_display_unprepare(struct dsi_display *display) { - int rc = 0; + int rc = 0, i; if (!display) { pr_err("Invalid params\n"); @@ -2521,10 +2702,12 @@ int dsi_display_unprepare(struct dsi_display *display) pr_err("[%s] display wake up failed, rc=%d\n", display->name, rc); - rc = dsi_panel_unprepare(display->panel); - if (rc) - pr_err("[%s] panel unprepare failed, rc=%d\n", - display->name, rc); + for (i = 0; i < display->panel_count; i++) { + rc = dsi_panel_unprepare(display->panel[i]); + if (rc) + SDE_ERROR("[%s] panel unprepare failed, rc=%d\n", + display->name, rc); + } rc = dsi_display_ctrl_host_disable(display); if (rc) @@ -2561,10 +2744,12 @@ int dsi_display_unprepare(struct dsi_display *display) pr_err("[%s] failed to power DSI vregs, rc=%d\n", display->name, rc); - rc = dsi_panel_post_unprepare(display->panel); - if (rc) - pr_err("[%s] panel post-unprepare failed, rc=%d\n", - display->name, rc); + for (i = 0; i < display->panel_count; i++) { + rc = dsi_panel_post_unprepare(display->panel[i]); + if (rc) + pr_err("[%s] panel post-unprepare failed, rc=%d\n", + display->name, rc); + } mutex_unlock(&display->display_lock); return rc; diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h index b77bf268dbd1..210b8d00850b 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2017, 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 @@ -102,8 +102,11 @@ struct dsi_display_clk_info { * @display_lock: Mutex for dsi_display interface. * @ctrl_count: Number of DSI interfaces required by panel. * @ctrl: Controller information for DSI display. + * @panel_count: Number of DSI panel. * @panel: Handle to DSI panel. - * @panel_of: pHandle to DSI panel. + * @panel_of: pHandle to DSI panel, it's an array with panel_count + * of struct device_node pointers. + * @bridge_idx: Bridge chip index for each panel_of. * @type: DSI display type. * @clk_master_idx: The master controller for controlling clocks. This is an * index into the ctrl[MAX_DSI_CTRLS_PER_DISPLAY] array. @@ -133,8 +136,10 @@ struct dsi_display { struct dsi_display_ctrl ctrl[MAX_DSI_CTRLS_PER_DISPLAY]; /* panel info */ - struct dsi_panel *panel; - struct device_node *panel_of; + u32 panel_count; + struct dsi_panel **panel; + struct device_node **panel_of; + u32 *bridge_idx; enum dsi_display_type type; u32 clk_master_idx; diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c index a1adecf81cc0..995cda97a2f0 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, 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 @@ -213,18 +213,21 @@ static void dsi_bridge_mode_set(struct drm_bridge *bridge, struct drm_display_mode *adjusted_mode) { struct dsi_bridge *c_bridge = to_dsi_bridge(bridge); + struct dsi_panel *panel; - if (!bridge || !mode || !adjusted_mode) { + if (!bridge || !mode || !adjusted_mode || !c_bridge->display || + !c_bridge->display->panel[0]) { pr_err("Invalid params\n"); return; } + /* dsi drm bridge is always the first panel */ + panel = c_bridge->display->panel[0]; memset(&(c_bridge->dsi_mode), 0x0, sizeof(struct dsi_display_mode)); convert_to_dsi_mode(adjusted_mode, &(c_bridge->dsi_mode)); pr_debug("note: using panel cmd/vid mode instead of user val\n"); - c_bridge->dsi_mode.panel_mode = - c_bridge->display->panel->mode.panel_mode; + c_bridge->dsi_mode.panel_mode = panel->mode.panel_mode; } static bool dsi_bridge_mode_fixup(struct drm_bridge *bridge, @@ -271,6 +274,7 @@ int dsi_conn_post_init(struct drm_connector *connector, { struct dsi_display *dsi_display = display; struct dsi_panel *panel; + int i; if (!info || !dsi_display) return -EINVAL; @@ -299,60 +303,65 @@ int dsi_conn_post_init(struct drm_connector *connector, break; } - if (!dsi_display->panel) { - pr_debug("invalid panel data\n"); - goto end; - } - - panel = dsi_display->panel; - sde_kms_info_add_keystr(info, "panel name", panel->name); - - switch (panel->mode.panel_mode) { - case DSI_OP_VIDEO_MODE: - sde_kms_info_add_keystr(info, "panel mode", "video"); - break; - case DSI_OP_CMD_MODE: - sde_kms_info_add_keystr(info, "panel mode", "command"); - sde_kms_info_add_keyint(info, "mdp_transfer_time_us", - panel->cmd_config.mdp_transfer_time_us); - break; - default: - pr_debug("invalid panel type:%d\n", panel->mode.panel_mode); - break; - } - sde_kms_info_add_keystr(info, "dfps support", - panel->dfps_caps.dfps_support ? "true" : "false"); + for (i = 0; i < dsi_display->panel_count; i++) { + if (!dsi_display->panel[i]) { + pr_debug("invalid panel data\n"); + goto end; + } - switch (panel->phy_props.rotation) { - case DSI_PANEL_ROTATE_NONE: - sde_kms_info_add_keystr(info, "panel orientation", "none"); - break; - case DSI_PANEL_ROTATE_H_FLIP: - sde_kms_info_add_keystr(info, "panel orientation", "horz flip"); - break; - case DSI_PANEL_ROTATE_V_FLIP: - sde_kms_info_add_keystr(info, "panel orientation", "vert flip"); - break; - default: - pr_debug("invalid panel rotation:%d\n", + panel = dsi_display->panel[i]; + sde_kms_info_add_keystr(info, "panel name", panel->name); + + switch (panel->mode.panel_mode) { + case DSI_OP_VIDEO_MODE: + sde_kms_info_add_keystr(info, "panel mode", "video"); + break; + case DSI_OP_CMD_MODE: + sde_kms_info_add_keystr(info, "panel mode", "command"); + break; + default: + pr_debug("invalid panel type:%d\n", + panel->mode.panel_mode); + break; + } + sde_kms_info_add_keystr(info, "dfps support", + panel->dfps_caps.dfps_support ? + "true" : "false"); + + switch (panel->phy_props.rotation) { + case DSI_PANEL_ROTATE_NONE: + sde_kms_info_add_keystr(info, "panel orientation", + "none"); + break; + case DSI_PANEL_ROTATE_H_FLIP: + sde_kms_info_add_keystr(info, "panel orientation", + "horz flip"); + break; + case DSI_PANEL_ROTATE_V_FLIP: + sde_kms_info_add_keystr(info, "panel orientation", + "vert flip"); + break; + default: + pr_debug("invalid panel rotation:%d\n", panel->phy_props.rotation); - break; - } + break; + } - switch (panel->bl_config.type) { - case DSI_BACKLIGHT_PWM: - sde_kms_info_add_keystr(info, "backlight type", "pwm"); - break; - case DSI_BACKLIGHT_WLED: - sde_kms_info_add_keystr(info, "backlight type", "wled"); - break; - case DSI_BACKLIGHT_DCS: - sde_kms_info_add_keystr(info, "backlight type", "dcs"); - break; - default: - pr_debug("invalid panel backlight type:%d\n", - panel->bl_config.type); - break; + switch (panel->bl_config.type) { + case DSI_BACKLIGHT_PWM: + sde_kms_info_add_keystr(info, "backlight type", "pwm"); + break; + case DSI_BACKLIGHT_WLED: + sde_kms_info_add_keystr(info, "backlight type", "wled"); + break; + case DSI_BACKLIGHT_DCS: + sde_kms_info_add_keystr(info, "backlight type", "dcs"); + break; + default: + pr_debug("invalid panel backlight type:%d\n", + panel->bl_config.type); + break; + } } end: diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c index a7a39e685d4d..b1319a68429f 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, 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 @@ -17,6 +17,7 @@ #include <linux/gpio.h> #include <linux/of_gpio.h> +#include "sde_kms.h" #include "dsi_panel.h" #include "dsi_ctrl_hw.h" @@ -171,10 +172,12 @@ static int dsi_panel_set_pinctrl_state(struct dsi_panel *panel, bool enable) else state = panel->pinctrl.suspend; - rc = pinctrl_select_state(panel->pinctrl.pinctrl, state); - if (rc) - pr_err("[%s] failed to set pin state, rc=%d\n", panel->name, - rc); + if (panel->pinctrl.pinctrl && state) { + rc = pinctrl_select_state(panel->pinctrl.pinctrl, state); + if (rc) + pr_err("[%s] failed to set pin state, rc=%d\n", + panel->name, rc); + } return rc; } @@ -386,6 +389,9 @@ static int dsi_panel_bl_register(struct dsi_panel *panel) case DSI_BACKLIGHT_WLED: rc = dsi_panel_led_bl_register(panel, bl); break; + case DSI_BACKLIGHT_UNKNOWN: + DRM_INFO("backlight type is unknown\n"); + break; default: pr_err("Backlight type(%d) not supported\n", bl->type); rc = -ENOTSUPP; @@ -704,6 +710,8 @@ static int dsi_panel_parse_misc_host_config(struct dsi_host_common_cfg *host, host->append_tx_eot = of_property_read_bool(of_node, "qcom,mdss-dsi-tx-eot-append"); + host->force_clk_lane_hs = of_property_read_bool(of_node, + "qcom,mdss-dsi-force-clock-lane-hs"); return 0; } @@ -1348,6 +1356,8 @@ static int dsi_panel_parse_gpios(struct dsi_panel *panel, { int rc = 0; + /* Need to set GPIO default value to -1, since 0 is a valid value */ + panel->reset_config.disp_en_gpio = -1; panel->reset_config.reset_gpio = of_get_named_gpio(of_node, "qcom,platform-reset-gpio", 0); @@ -1496,6 +1506,33 @@ error: return rc; } +static int dsi_panel_parse_dba_config(struct dsi_panel *panel, + struct device_node *of_node) +{ + int rc = 0, len = 0; + + panel->dba_config.dba_panel = of_property_read_bool(of_node, + "qcom,dba-panel"); + + if (panel->dba_config.dba_panel) { + panel->dba_config.hdmi_mode = of_property_read_bool(of_node, + "qcom,hdmi-mode"); + + panel->dba_config.bridge_name = of_get_property(of_node, + "qcom,bridge-name", &len); + if (!panel->dba_config.bridge_name || len <= 0) { + SDE_ERROR( + "%s:%d Unable to read bridge_name, data=%pK,len=%d\n", + __func__, __LINE__, panel->dba_config.bridge_name, len); + rc = -EINVAL; + goto error; + } + } + +error: + return rc; +} + struct dsi_panel *dsi_panel_get(struct device *parent, struct device_node *of_node) { @@ -1560,6 +1597,10 @@ struct dsi_panel *dsi_panel_get(struct device *parent, if (rc) pr_err("failed to parse backlight config, rc=%d\n", rc); + rc = dsi_panel_parse_dba_config(panel, of_node); + if (rc) + pr_err("failed to parse dba config, rc=%d\n", rc); + panel->panel_of_node = of_node; drm_panel_init(&panel->drm_panel); mutex_init(&panel->panel_lock); @@ -1574,6 +1615,9 @@ void dsi_panel_put(struct dsi_panel *panel) { u32 i; + if (!panel) + return; + for (i = 0; i < DSI_CMD_SET_MAX; i++) dsi_panel_destroy_cmd_packets(&panel->cmd_sets[i]); @@ -1614,10 +1658,8 @@ int dsi_panel_drv_init(struct dsi_panel *panel, } rc = dsi_panel_pinctrl_init(panel); - if (rc) { + if (rc) pr_err("[%s] failed to init pinctrl, rc=%d\n", panel->name, rc); - goto error_vreg_put; - } rc = dsi_panel_gpio_request(panel); if (rc) { @@ -1640,7 +1682,6 @@ error_gpio_release: (void)dsi_panel_gpio_release(panel); error_pinctrl_deinit: (void)dsi_panel_pinctrl_deinit(panel); -error_vreg_put: (void)dsi_panel_vreg_put(panel); exit: mutex_unlock(&panel->panel_lock); diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h index 4d21a4cf6428..8106ed1261b4 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, 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 @@ -132,6 +132,18 @@ struct dsi_panel_reset_config { int disp_en_gpio; }; +/** + * struct dsi_panel_dba - DSI DBA panel information + * @dba_panel: Indicate if it's DBA panel + * @bridge_name: Bridge chip name + * @hdmi_mode: If bridge chip is in hdmi mode. + */ +struct dsi_panel_dba { + bool dba_panel; + const char *bridge_name; + bool hdmi_mode; +}; + struct dsi_panel { const char *name; struct device_node *panel_of_node; @@ -158,6 +170,8 @@ struct dsi_panel { struct dsi_panel_reset_config reset_config; struct dsi_pinctrl_info pinctrl; + struct dsi_panel_dba dba_config; + bool lp11_init; }; diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c index 6a6d02c5444d..c377f3759e67 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c @@ -401,12 +401,111 @@ static const struct file_operations edid_vendor_name_fops = { .read = _sde_hdmi_edid_vendor_name_read, }; +static u64 _sde_hdmi_clip_valid_pclk(struct drm_display_mode *mode, u64 pclk_in) +{ + u32 pclk_delta, pclk; + u64 pclk_clip = pclk_in; + + /* as per standard, 0.5% of deviation is allowed */ + pclk = mode->clock * HDMI_KHZ_TO_HZ; + pclk_delta = pclk * 5 / 1000; + + if (pclk_in < (pclk - pclk_delta)) + pclk_clip = pclk - pclk_delta; + else if (pclk_in > (pclk + pclk_delta)) + pclk_clip = pclk + pclk_delta; + + if (pclk_in != pclk_clip) + pr_warn("clip pclk from %lld to %lld\n", pclk_in, pclk_clip); + + return pclk_clip; +} + +/** + * _sde_hdmi_update_pll_delta() - Update the HDMI pixel clock as per input ppm + * + * @ppm: ppm is parts per million multiplied by 1000. + * return: 0 on success, non-zero in case of failure. + * + * The input ppm will be clipped if it's more than or less than 5% of the TMDS + * clock rate defined by HDMI spec. + */ +static int _sde_hdmi_update_pll_delta(struct sde_hdmi *display, s32 ppm) +{ + struct hdmi *hdmi = display->ctrl.ctrl; + struct drm_display_mode *current_mode = &display->mode; + u64 cur_pclk, dst_pclk; + u64 clip_pclk; + int rc = 0; + + if (!hdmi->power_on || !display->connected) { + SDE_ERROR("HDMI display is not ready\n"); + return -EINVAL; + } + + /* get current pclk */ + cur_pclk = hdmi->pixclock; + /* get desired pclk */ + dst_pclk = cur_pclk * (1000000000 + ppm); + do_div(dst_pclk, 1000000000); + + clip_pclk = _sde_hdmi_clip_valid_pclk(current_mode, dst_pclk); + + /* update pclk */ + if (clip_pclk != cur_pclk) { + SDE_DEBUG("PCLK changes from %llu to %llu when delta is %d\n", + cur_pclk, clip_pclk, ppm); + + rc = clk_set_rate(hdmi->pwr_clks[0], clip_pclk); + if (rc < 0) { + SDE_ERROR("PLL update failed, reset clock rate\n"); + return rc; + } + + hdmi->pixclock = clip_pclk; + } + + return rc; +} + +static ssize_t _sde_hdmi_debugfs_pll_delta_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct sde_hdmi *display = file->private_data; + char buf[10]; + int ppm = 0; + + if (!display) + return -ENODEV; + + if (count >= sizeof(buf)) + return -EFAULT; + + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + buf[count] = 0; /* end of string */ + + if (kstrtoint(buf, 0, &ppm)) + return -EFAULT; + + if (ppm) + _sde_hdmi_update_pll_delta(display, ppm); + + return count; +} + +static const struct file_operations pll_delta_fops = { + .open = simple_open, + .write = _sde_hdmi_debugfs_pll_delta_write, +}; + static int _sde_hdmi_debugfs_init(struct sde_hdmi *display) { int rc = 0; struct dentry *dir, *dump_file, *edid_modes; struct dentry *edid_vsdb_info, *edid_hdr_info, *edid_hfvsdb_info; - struct dentry *edid_vcdb_info, *edid_vendor_name; + struct dentry *edid_vcdb_info, *edid_vendor_name, *pll_file; dir = debugfs_create_dir(display->name, NULL); if (!dir) { @@ -423,7 +522,19 @@ static int _sde_hdmi_debugfs_init(struct sde_hdmi *display) &dump_info_fops); if (IS_ERR_OR_NULL(dump_file)) { rc = PTR_ERR(dump_file); - SDE_ERROR("[%s]debugfs create file failed, rc=%d\n", + SDE_ERROR("[%s]debugfs create dump_info file failed, rc=%d\n", + display->name, rc); + goto error_remove_dir; + } + + pll_file = debugfs_create_file("pll_delta", + 0644, + dir, + display, + &pll_delta_fops); + if (IS_ERR_OR_NULL(pll_file)) { + rc = PTR_ERR(pll_file); + SDE_ERROR("[%s]debugfs create pll_delta file failed, rc=%d\n", display->name, rc); goto error_remove_dir; } @@ -823,7 +934,6 @@ static void _sde_hdmi_hotplug_work(struct work_struct *work) } else sde_free_edid((void **)&sde_hdmi->edid_ctrl); - sde_hdmi_notify_clients(connector, sde_hdmi->connected); drm_helper_hpd_irq_event(connector->dev); } @@ -853,8 +963,7 @@ static void _sde_hdmi_connector_irq(struct sde_hdmi *sde_hdmi) hpd_int_ctrl |= HDMI_HPD_INT_CTRL_INT_CONNECT; hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, hpd_int_ctrl); - if (!sde_hdmi->non_pluggable) - queue_work(hdmi->workq, &sde_hdmi->hpd_work); + queue_work(hdmi->workq, &sde_hdmi->hpd_work); } } @@ -943,6 +1052,25 @@ static int _sde_hdmi_get_cable_status(struct platform_device *pdev, u32 vote) return hdmi->power_on && display->connected; } +static void _sde_hdmi_audio_codec_ready(struct platform_device *pdev) +{ + struct sde_hdmi *display = platform_get_drvdata(pdev); + + if (!display) { + SDE_ERROR("invalid param(s), display %pK\n", display); + return; + } + + mutex_lock(&display->display_lock); + if (!display->codec_ready) { + display->codec_ready = true; + + if (display->client_notify_pending) + sde_hdmi_notify_clients(display, display->connected); + } + mutex_unlock(&display->display_lock); +} + static int _sde_hdmi_ext_disp_init(struct sde_hdmi *display) { int rc = 0; @@ -962,6 +1090,8 @@ static int _sde_hdmi_ext_disp_init(struct sde_hdmi *display) _sde_hdmi_get_audio_edid_blk; display->ext_audio_data.codec_ops.cable_status = _sde_hdmi_get_cable_status; + display->ext_audio_data.codec_ops.codec_ready = + _sde_hdmi_audio_codec_ready; if (!display->pdev->dev.of_node) { SDE_ERROR("[%s]cannot find sde_hdmi of_node\n", display->name); @@ -990,17 +1120,14 @@ static int _sde_hdmi_ext_disp_init(struct sde_hdmi *display) return rc; } -void sde_hdmi_notify_clients(struct drm_connector *connector, - bool connected) +void sde_hdmi_notify_clients(struct sde_hdmi *display, bool connected) { - struct sde_connector *c_conn = to_sde_connector(connector); - struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display; int state = connected ? EXT_DISPLAY_CABLE_CONNECT : EXT_DISPLAY_CABLE_DISCONNECT; if (display && display->ext_audio_data.intf_ops.hpd) { struct hdmi *hdmi = display->ctrl.ctrl; - u32 flags = MSM_EXT_DISP_HPD_VIDEO; + u32 flags = MSM_EXT_DISP_HPD_ASYNC_VIDEO; if (hdmi->hdmi_mode) flags |= MSM_EXT_DISP_HPD_AUDIO; @@ -1010,21 +1137,6 @@ void sde_hdmi_notify_clients(struct drm_connector *connector, } } -void sde_hdmi_ack_state(struct drm_connector *connector, - enum drm_connector_status status) -{ - struct sde_connector *c_conn = to_sde_connector(connector); - struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display; - - if (display) { - struct hdmi *hdmi = display->ctrl.ctrl; - - if (hdmi->hdmi_mode && display->ext_audio_data.intf_ops.notify) - display->ext_audio_data.intf_ops.notify( - display->ext_pdev, status); - } -} - void sde_hdmi_set_mode(struct hdmi *hdmi, bool power_on) { uint32_t ctrl = 0; @@ -1316,14 +1428,36 @@ int sde_hdmi_get_info(struct msm_display_info *info, MSM_DISPLAY_CAP_EDID | MSM_DISPLAY_CAP_VID_MODE; } info->is_connected = hdmi_display->connected; - info->max_width = 1920; - info->max_height = 1080; + info->max_width = 4096; + info->max_height = 2160; info->compression = MSM_DISPLAY_COMPRESS_NONE; mutex_unlock(&hdmi_display->display_lock); return rc; } +int sde_hdmi_set_property(struct drm_connector *connector, + struct drm_connector_state *state, + int property_index, + uint64_t value, + void *display) +{ + int rc = 0; + + if (!connector || !display) { + SDE_ERROR("connector=%pK or display=%pK is NULL\n", + connector, display); + return 0; + } + + SDE_DEBUG("\n"); + + if (property_index == CONNECTOR_PROP_PLL_DELTA) + rc = _sde_hdmi_update_pll_delta(display, value); + + return rc; +} + u32 sde_hdmi_get_num_of_displays(void) { u32 count = 0; diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h index bb3061a6ed00..ffa9a27e7dfe 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h @@ -80,9 +80,12 @@ struct sde_hdmi_ctrl { * @non_pluggable: If HDMI display is non pluggable * @num_of_modes: Number of modes supported by display if non pluggable. * @mode_list: Mode list if non pluggable. + * @mode: Current display mode. * @connected: If HDMI display is connected. * @is_tpg_enabled: TPG state. * @hpd_work: HPD work structure. + * @codec_ready: If audio codec is ready. + * @client_notify_pending: If there is client notification pending. * @root: Debug fs root entry. */ struct sde_hdmi { @@ -103,10 +106,13 @@ struct sde_hdmi { bool non_pluggable; u32 num_of_modes; struct list_head mode_list; + struct drm_display_mode mode; bool connected; bool is_tpg_enabled; struct work_struct hpd_work; + bool codec_ready; + bool client_notify_pending; /* DEBUG FS */ struct dentry *root; @@ -270,6 +276,22 @@ int sde_hdmi_get_info(struct msm_display_info *info, void *display); /** + * sde_hdmi_set_property() - set the connector properties + * @connector: Handle to the connector. + * @state: Handle to the connector state. + * @property_index: property index. + * @value: property value. + * @display: Handle to the display. + * + * Return: error code. + */ +int sde_hdmi_set_property(struct drm_connector *connector, + struct drm_connector_state *state, + int property_index, + uint64_t value, + void *display); + +/** * sde_hdmi_bridge_init() - init sde hdmi bridge * @hdmi: Handle to the hdmi. * @@ -361,13 +383,12 @@ int sde_hdmi_config_avmute(struct hdmi *hdmi, bool set); /** * sde_hdmi_notify_clients() - notify hdmi clients of the connection status. - * @connector: Handle to the drm_connector. + * @display: Handle to sde_hdmi. * @connected: connection status. * * Return: void. */ -void sde_hdmi_notify_clients(struct drm_connector *connector, - bool connected); +void sde_hdmi_notify_clients(struct sde_hdmi *display, bool connected); /** * sde_hdmi_ack_state() - acknowledge the connection status. @@ -453,5 +474,15 @@ static inline int sde_hdmi_get_info(struct msm_display_info *info, { return 0; } + +static inline int sde_hdmi_set_property(struct drm_connector *connector, + struct drm_connector_state *state, + int property_index, + uint64_t value, + void *display) +{ + return 0; +} + #endif /*#else of CONFIG_DRM_SDE_HDMI*/ #endif /* _SDE_HDMI_H_ */ diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_audio.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_audio.c index 13ea49cfa42d..48a3a9316a41 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_audio.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_audio.c @@ -30,8 +30,6 @@ #define HDMI_AUDIO_INFO_FRAME_PACKET_VERSION 0x1 #define HDMI_AUDIO_INFO_FRAME_PACKET_LENGTH 0x0A -#define HDMI_KHZ_TO_HZ 1000 -#define HDMI_MHZ_TO_HZ 1000000 #define HDMI_ACR_N_MULTIPLIER 128 #define DEFAULT_AUDIO_SAMPLE_RATE_HZ 48000 diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c index c76e42c67885..34268aaedfc0 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c @@ -372,6 +372,8 @@ static void _sde_hdmi_bridge_pre_enable(struct drm_bridge *bridge) struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge); struct hdmi *hdmi = sde_hdmi_bridge->hdmi; struct hdmi_phy *phy = hdmi->phy; + struct sde_connector *c_conn = to_sde_connector(hdmi->connector); + struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display; DRM_DEBUG("power up"); @@ -388,41 +390,20 @@ static void _sde_hdmi_bridge_pre_enable(struct drm_bridge *bridge) if (hdmi->hdcp_ctrl && hdmi->is_hdcp_supported) hdmi_hdcp_ctrl_on(hdmi->hdcp_ctrl); - sde_hdmi_ack_state(hdmi->connector, EXT_DISPLAY_CABLE_CONNECT); -} - -static void sde_hdmi_force_update_audio(struct drm_connector *connector, - enum drm_connector_status status) -{ - struct sde_connector *c_conn = to_sde_connector(connector); - struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display; - - if (display && display->non_pluggable) { - display->ext_audio_data.intf_ops.hpd(display->ext_pdev, - display->ext_audio_data.type, - status, - MSM_EXT_DISP_HPD_AUDIO); - } + mutex_lock(&display->display_lock); + if (display->codec_ready) + sde_hdmi_notify_clients(display, display->connected); + else + display->client_notify_pending = true; + mutex_unlock(&display->display_lock); } static void _sde_hdmi_bridge_enable(struct drm_bridge *bridge) { - struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge); - struct hdmi *hdmi = sde_hdmi_bridge->hdmi; - - /* force update audio ops when there's no HPD event */ - sde_hdmi_force_update_audio(hdmi->connector, - EXT_DISPLAY_CABLE_CONNECT); } static void _sde_hdmi_bridge_disable(struct drm_bridge *bridge) { - struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge); - struct hdmi *hdmi = sde_hdmi_bridge->hdmi; - - /* force update audio ops when there's no HPD event */ - sde_hdmi_force_update_audio(hdmi->connector, - EXT_DISPLAY_CABLE_DISCONNECT); } static void _sde_hdmi_bridge_post_disable(struct drm_bridge *bridge) @@ -430,6 +411,10 @@ static void _sde_hdmi_bridge_post_disable(struct drm_bridge *bridge) struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge); struct hdmi *hdmi = sde_hdmi_bridge->hdmi; struct hdmi_phy *phy = hdmi->phy; + struct sde_connector *c_conn = to_sde_connector(hdmi->connector); + struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display; + + sde_hdmi_notify_clients(display, display->connected); if (hdmi->hdcp_ctrl && hdmi->is_hdcp_supported) hdmi_hdcp_ctrl_off(hdmi->hdcp_ctrl); @@ -446,8 +431,6 @@ static void _sde_hdmi_bridge_post_disable(struct drm_bridge *bridge) _sde_hdmi_bridge_power_off(bridge); hdmi->power_on = false; } - - sde_hdmi_ack_state(hdmi->connector, EXT_DISPLAY_CABLE_DISCONNECT); } static void _sde_hdmi_bridge_set_avi_infoframe(struct hdmi *hdmi, @@ -568,6 +551,15 @@ static void _sde_hdmi_bridge_set_spd_infoframe(struct hdmi *hdmi, hdmi_write(hdmi, REG_HDMI_GEN_PKT_CTRL, packet_control); } +static inline void _sde_hdmi_save_mode(struct hdmi *hdmi, + struct drm_display_mode *mode) +{ + struct sde_connector *c_conn = to_sde_connector(hdmi->connector); + struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display; + + drm_mode_copy(&display->mode, mode); +} + static void _sde_hdmi_bridge_mode_set(struct drm_bridge *bridge, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -640,6 +632,8 @@ static void _sde_hdmi_bridge_mode_set(struct drm_bridge *bridge, DRM_DEBUG("hdmi setup info frame\n"); } _sde_hdmi_bridge_setup_scrambler(hdmi, mode); + + _sde_hdmi_save_mode(hdmi, mode); } static const struct drm_bridge_funcs _sde_hdmi_bridge_funcs = { diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c index b6cddee0cf34..119221eacb43 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c @@ -184,8 +184,7 @@ static void mdp4_preclose(struct msm_kms *kms, struct drm_file *file) mdp4_crtc_cancel_pending_flip(priv->crtcs[i], file); if (aspace) { - aspace->mmu->funcs->detach(aspace->mmu, - iommu_ports, ARRAY_SIZE(iommu_ports)); + aspace->mmu->funcs->detach(aspace->mmu); msm_gem_address_space_destroy(aspace); } } @@ -202,8 +201,7 @@ static void mdp4_destroy(struct msm_kms *kms) drm_gem_object_unreference_unlocked(mdp4_kms->blank_cursor_bo); if (aspace) { - aspace->mmu->funcs->detach(aspace->mmu, - iommu_ports, ARRAY_SIZE(iommu_ports)); + aspace->mmu->funcs->detach(aspace->mmu); msm_gem_address_space_put(aspace); } @@ -416,10 +414,6 @@ fail: return ret; } -static const char *iommu_ports[] = { - "mdp_port0_cb0", "mdp_port1_cb0", -}; - struct msm_kms *mdp4_kms_init(struct drm_device *dev) { struct platform_device *pdev = dev->platformdev; @@ -515,15 +509,11 @@ struct msm_kms *mdp4_kms_init(struct drm_device *dev) mdelay(16); if (config->iommu) { - struct msm_mmu *mmu = msm_iommu_new(&pdev->dev, config->iommu); - - if (IS_ERR(mmu)) { - ret = PTR_ERR(mmu); - goto fail; - } + config->iommu->geometry.aperture_start = 0x1000; + config->iommu->geometry.aperture_end = 0xffffffff; aspace = msm_gem_address_space_create(&pdev->dev, - mmu, "mdp4", 0x1000, 0xffffffff); + config->iommu, MSM_IOMMU_DOMAIN_DEFAULT, "mdp4"); if (IS_ERR(aspace)) { ret = PTR_ERR(aspace); goto fail; @@ -531,8 +521,7 @@ struct msm_kms *mdp4_kms_init(struct drm_device *dev) mdp4_kms->aspace = aspace; - ret = aspace->mmu->funcs->attach(aspace->mmu, iommu_ports, - ARRAY_SIZE(iommu_ports)); + ret = aspace->mmu->funcs->attach(aspace->mmu, NULL, 0); if (ret) goto fail; } else { diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c index e4e69ebd116e..4dbf456504b7 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c @@ -22,10 +22,6 @@ #include "msm_mmu.h" #include "mdp5_kms.h" -static const char *iommu_ports[] = { - "mdp_0", -}; - static int mdp5_hw_init(struct msm_kms *kms) { struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); @@ -613,8 +609,7 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev) mdp5_kms->aspace = aspace; - ret = aspace->mmu->funcs->attach(aspace->mmu, iommu_ports, - ARRAY_SIZE(iommu_ports)); + ret = aspace->mmu->funcs->attach(aspace->mmu, NULL, 0); if (ret) { dev_err(&pdev->dev, "failed to attach iommu: %d\n", ret); diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 276329b7b10c..7d378f7ebaa4 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -1537,6 +1537,37 @@ static int msm_ioctl_deregister_event(struct drm_device *dev, void *data, return 0; } +static int msm_ioctl_gem_sync(struct drm_device *dev, void *data, + struct drm_file *file) +{ + + struct drm_msm_gem_sync *arg = data; + int i; + + for (i = 0; i < arg->nr_ops; i++) { + struct drm_msm_gem_syncop syncop; + struct drm_gem_object *obj; + int ret; + void __user *ptr = + (void __user *)(uintptr_t) + (arg->ops + (i * sizeof(syncop))); + + ret = copy_from_user(&syncop, ptr, sizeof(syncop)); + if (ret) + return -EFAULT; + + obj = drm_gem_object_lookup(dev, file, syncop.handle); + if (!obj) + return -ENOENT; + + msm_gem_sync(obj, syncop.op); + + drm_gem_object_unreference_unlocked(obj); + } + + return 0; +} + void msm_send_crtc_notification(struct drm_crtc *crtc, struct drm_event *event, u8 *payload) { @@ -1665,6 +1696,8 @@ static const struct drm_ioctl_desc msm_ioctls[] = { DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(MSM_COUNTER_READ, msm_ioctl_counter_read, DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(MSM_GEM_SYNC, msm_ioctl_gem_sync, + DRM_AUTH|DRM_RENDER_ALLOW), }; static const struct vm_operations_struct vm_ops = { diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index d2d118cf7e07..bd75a1ba1f8b 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -148,6 +148,7 @@ enum msm_mdp_conn_property { CONNECTOR_PROP_DST_Y, CONNECTOR_PROP_DST_W, CONNECTOR_PROP_DST_H, + CONNECTOR_PROP_PLL_DELTA, /* enum/bitmask properties */ CONNECTOR_PROP_TOPOLOGY_NAME, @@ -412,7 +413,7 @@ void msm_gem_address_space_put(struct msm_gem_address_space *aspace); /* For GPU and legacy display */ struct msm_gem_address_space * msm_gem_address_space_create(struct device *dev, struct iommu_domain *domain, - const char *name); + int type, const char *name); struct msm_gem_address_space * msm_gem_address_space_create_instance(struct msm_mmu *parent, const char *name, uint64_t start, uint64_t end); @@ -469,6 +470,7 @@ struct drm_gem_object *msm_gem_new(struct drm_device *dev, uint32_t size, uint32_t flags); struct drm_gem_object *msm_gem_import(struct drm_device *dev, uint32_t size, struct sg_table *sgt); +void msm_gem_sync(struct drm_gem_object *obj, u32 op); int msm_framebuffer_prepare(struct drm_framebuffer *fb, struct msm_gem_address_space *aspace); diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index d1455fbc980e..d35d03c2935d 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -546,6 +546,26 @@ int msm_gem_cpu_fini(struct drm_gem_object *obj) return 0; } +void msm_gem_sync(struct drm_gem_object *obj, u32 op) +{ + struct drm_device *dev = obj->dev; + struct msm_gem_object *msm_obj = to_msm_bo(obj); + + if (msm_obj->flags & (MSM_BO_WC|MSM_BO_UNCACHED)) + return; + + switch (op) { + case MSM_GEM_SYNC_TO_CPU: + dma_sync_sg_for_cpu(dev->dev, msm_obj->sgt->sgl, + msm_obj->sgt->nents, DMA_BIDIRECTIONAL); + break; + case MSM_GEM_SYNC_TO_DEV: + dma_sync_sg_for_device(dev->dev, msm_obj->sgt->sgl, + msm_obj->sgt->nents, DMA_BIDIRECTIONAL); + break; + } +} + #ifdef CONFIG_DEBUG_FS void msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m) { diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h index 2045dc34c20a..0b19d11bc666 100644 --- a/drivers/gpu/drm/msm/msm_gem.h +++ b/drivers/gpu/drm/msm/msm_gem.h @@ -25,21 +25,12 @@ /* Additional internal-use only BO flags: */ #define MSM_BO_STOLEN 0x10000000 /* try to use stolen/splash memory */ -struct msm_gem_aspace_ops { - int (*map)(struct msm_gem_address_space *, struct msm_gem_vma *, - struct sg_table *sgt, void *priv, unsigned int flags); - - void (*unmap)(struct msm_gem_address_space *, struct msm_gem_vma *, - struct sg_table *sgt, void *priv); - - void (*destroy)(struct msm_gem_address_space *); -}; - struct msm_gem_address_space { const char *name; struct msm_mmu *mmu; - const struct msm_gem_aspace_ops *ops; struct kref kref; + struct drm_mm mm; + u64 va_len; }; struct msm_gem_vma { diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c index 52fc81420690..8e0f15c416fd 100644 --- a/drivers/gpu/drm/msm/msm_gem_submit.c +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -82,13 +82,16 @@ static int submit_lookup_objects(struct msm_gem_submit *submit, void __user *userptr = to_user_ptr(args->bos + (i * sizeof(submit_bo))); - ret = copy_from_user_inatomic(&submit_bo, userptr, sizeof(submit_bo)); - if (unlikely(ret)) { + if (copy_from_user_inatomic(&submit_bo, userptr, + sizeof(submit_bo))) { pagefault_enable(); spin_unlock(&file->table_lock); - ret = copy_from_user(&submit_bo, userptr, sizeof(submit_bo)); - if (ret) + if (copy_from_user(&submit_bo, userptr, + sizeof(submit_bo))) { + ret = -EFAULT; goto out; + } + spin_lock(&file->table_lock); pagefault_disable(); } @@ -283,8 +286,8 @@ static int submit_reloc(struct msm_gem_submit *submit, struct msm_gem_object *ob uint32_t off; bool valid; - ret = copy_from_user(&submit_reloc, userptr, sizeof(submit_reloc)); - if (ret) + if (copy_from_user(&submit_reloc, userptr, + sizeof(submit_reloc))) return -EFAULT; if (submit_reloc.submit_offset % 4) { diff --git a/drivers/gpu/drm/msm/msm_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c index a227f1ba0573..47f7436854fb 100644 --- a/drivers/gpu/drm/msm/msm_gem_vma.c +++ b/drivers/gpu/drm/msm/msm_gem_vma.c @@ -25,8 +25,10 @@ msm_gem_address_space_destroy(struct kref *kref) struct msm_gem_address_space *aspace = container_of(kref, struct msm_gem_address_space, kref); - if (aspace->ops->destroy) - aspace->ops->destroy(aspace); + if (aspace->va_len) + drm_mm_takedown(&aspace->mm); + + aspace->mmu->funcs->destroy(aspace->mmu); kfree(aspace); } @@ -37,57 +39,9 @@ void msm_gem_address_space_put(struct msm_gem_address_space *aspace) kref_put(&aspace->kref, msm_gem_address_space_destroy); } -/* SDE address space operations */ -static void smmu_aspace_unmap_vma(struct msm_gem_address_space *aspace, - struct msm_gem_vma *vma, struct sg_table *sgt, - void *priv) -{ - struct dma_buf *buf = priv; - - if (buf) - aspace->mmu->funcs->unmap_dma_buf(aspace->mmu, - sgt, buf, DMA_BIDIRECTIONAL); - else - aspace->mmu->funcs->unmap_sg(aspace->mmu, sgt, - DMA_BIDIRECTIONAL); - - vma->iova = 0; - - msm_gem_address_space_put(aspace); -} - - -static int smmu_aspace_map_vma(struct msm_gem_address_space *aspace, - struct msm_gem_vma *vma, struct sg_table *sgt, - void *priv, unsigned int flags) -{ - struct dma_buf *buf = priv; - int ret; - - if (buf) - ret = aspace->mmu->funcs->map_dma_buf(aspace->mmu, sgt, buf, - DMA_BIDIRECTIONAL); - else - ret = aspace->mmu->funcs->map_sg(aspace->mmu, sgt, - DMA_BIDIRECTIONAL); - - if (!ret) - vma->iova = sg_dma_address(sgt->sgl); - - /* Get a reference to the aspace to keep it around */ - kref_get(&aspace->kref); - - return ret; -} - -static const struct msm_gem_aspace_ops smmu_aspace_ops = { - .map = smmu_aspace_map_vma, - .unmap = smmu_aspace_unmap_vma, -}; - -struct msm_gem_address_space * -msm_gem_smmu_address_space_create(struct device *dev, struct msm_mmu *mmu, - const char *name) +static struct msm_gem_address_space * +msm_gem_address_space_new(struct msm_mmu *mmu, const char *name, + uint64_t start, uint64_t end) { struct msm_gem_address_space *aspace; @@ -100,57 +54,28 @@ msm_gem_smmu_address_space_create(struct device *dev, struct msm_mmu *mmu, aspace->name = name; aspace->mmu = mmu; - aspace->ops = &smmu_aspace_ops; - - kref_init(&aspace->kref); - - return aspace; -} -/* GPU address space operations */ -struct msm_iommu_aspace { - struct msm_gem_address_space base; - struct drm_mm mm; -}; + aspace->va_len = end - start; -#define to_iommu_aspace(aspace) \ - ((struct msm_iommu_aspace *) \ - container_of(aspace, struct msm_iommu_aspace, base)) + if (aspace->va_len) + drm_mm_init(&aspace->mm, (start >> PAGE_SHIFT), + (end >> PAGE_SHIFT) - 1); -static void iommu_aspace_unmap_vma(struct msm_gem_address_space *aspace, - struct msm_gem_vma *vma, struct sg_table *sgt, void *priv) -{ - if (!vma->iova) - return; - - if (aspace->mmu) - aspace->mmu->funcs->unmap(aspace->mmu, vma->iova, sgt); - - drm_mm_remove_node(&vma->node); - - vma->iova = 0; + kref_init(&aspace->kref); - msm_gem_address_space_put(aspace); + return aspace; } -static int iommu_aspace_map_vma(struct msm_gem_address_space *aspace, - struct msm_gem_vma *vma, struct sg_table *sgt, void *priv, - unsigned int flags) +static int allocate_iova(struct msm_gem_address_space *aspace, + struct msm_gem_vma *vma, struct sg_table *sgt, + u64 *iova) { - struct msm_iommu_aspace *local = to_iommu_aspace(aspace); - size_t size = 0; struct scatterlist *sg; + size_t size = 0; int ret, i; - int iommu_flags = IOMMU_READ; - - if (!(flags & MSM_BO_GPU_READONLY)) - iommu_flags |= IOMMU_WRITE; - if (flags & MSM_BO_PRIVILEGED) - iommu_flags |= IOMMU_PRIV; - - if ((flags & MSM_BO_CACHED) && msm_iommu_coherent(aspace->mmu)) - iommu_flags |= IOMMU_CACHE; + if (!aspace->va_len) + return 0; if (WARN_ON(drm_mm_node_allocated(&vma->node))) return 0; @@ -158,84 +83,73 @@ static int iommu_aspace_map_vma(struct msm_gem_address_space *aspace, for_each_sg(sgt->sgl, sg, sgt->nents, i) size += sg->length + sg->offset; - ret = drm_mm_insert_node(&local->mm, &vma->node, size >> PAGE_SHIFT, + ret = drm_mm_insert_node(&aspace->mm, &vma->node, size >> PAGE_SHIFT, 0, DRM_MM_SEARCH_DEFAULT); - if (ret) - return ret; - - vma->iova = vma->node.start << PAGE_SHIFT; - - if (aspace->mmu) - ret = aspace->mmu->funcs->map(aspace->mmu, vma->iova, sgt, - iommu_flags); - /* Get a reference to the aspace to keep it around */ - kref_get(&aspace->kref); + if (!ret && iova) + *iova = vma->node.start << PAGE_SHIFT; return ret; } -static void iommu_aspace_destroy(struct msm_gem_address_space *aspace) +int msm_gem_map_vma(struct msm_gem_address_space *aspace, + struct msm_gem_vma *vma, struct sg_table *sgt, + void *priv, unsigned int flags) { - struct msm_iommu_aspace *local = to_iommu_aspace(aspace); - - drm_mm_takedown(&local->mm); - aspace->mmu->funcs->destroy(aspace->mmu); -} - -static const struct msm_gem_aspace_ops msm_iommu_aspace_ops = { - .map = iommu_aspace_map_vma, - .unmap = iommu_aspace_unmap_vma, - .destroy = iommu_aspace_destroy, -}; + u64 iova = 0; + int ret; -static struct msm_gem_address_space * -msm_gem_address_space_new(struct msm_mmu *mmu, const char *name, - uint64_t start, uint64_t end) -{ - struct msm_iommu_aspace *local; + if (!aspace) + return -EINVAL; - if (!mmu) - return ERR_PTR(-EINVAL); + ret = allocate_iova(aspace, vma, sgt, &iova); + if (ret) + return ret; - local = kzalloc(sizeof(*local), GFP_KERNEL); - if (!local) - return ERR_PTR(-ENOMEM); + ret = aspace->mmu->funcs->map(aspace->mmu, iova, sgt, + flags, priv); - drm_mm_init(&local->mm, (start >> PAGE_SHIFT), - (end >> PAGE_SHIFT) - 1); + if (ret) { + if (drm_mm_node_allocated(&vma->node)) + drm_mm_remove_node(&vma->node); - local->base.name = name; - local->base.mmu = mmu; - local->base.ops = &msm_iommu_aspace_ops; + return ret; + } - kref_init(&local->base.kref); + vma->iova = sg_dma_address(sgt->sgl); + kref_get(&aspace->kref); - return &local->base; + return 0; } -int msm_gem_map_vma(struct msm_gem_address_space *aspace, - struct msm_gem_vma *vma, struct sg_table *sgt, - void *priv, unsigned int flags) +void msm_gem_unmap_vma(struct msm_gem_address_space *aspace, + struct msm_gem_vma *vma, struct sg_table *sgt, void *priv) { - if (aspace && aspace->ops->map) - return aspace->ops->map(aspace, vma, sgt, priv, flags); + if (!aspace || !vma->iova) + return; - return -EINVAL; + aspace->mmu->funcs->unmap(aspace->mmu, vma->iova, sgt, priv); + + if (drm_mm_node_allocated(&vma->node)) + drm_mm_remove_node(&vma->node); + + vma->iova = 0; + + msm_gem_address_space_put(aspace); } -void msm_gem_unmap_vma(struct msm_gem_address_space *aspace, - struct msm_gem_vma *vma, struct sg_table *sgt, void *priv) +struct msm_gem_address_space * +msm_gem_smmu_address_space_create(struct device *dev, struct msm_mmu *mmu, + const char *name) { - if (aspace && aspace->ops->unmap) - aspace->ops->unmap(aspace, vma, sgt, priv); + return msm_gem_address_space_new(mmu, name, 0, 0); } struct msm_gem_address_space * msm_gem_address_space_create(struct device *dev, struct iommu_domain *domain, - const char *name) + int type, const char *name) { - struct msm_mmu *mmu = msm_iommu_new(dev, domain); + struct msm_mmu *mmu = msm_iommu_new(dev, type, domain); if (IS_ERR(mmu)) return (struct msm_gem_address_space *) mmu; diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c index 5a505a8bf328..2f01db8b08c3 100644 --- a/drivers/gpu/drm/msm/msm_gpu.c +++ b/drivers/gpu/drm/msm/msm_gpu.c @@ -183,6 +183,9 @@ int msm_gpu_pm_resume(struct msm_gpu *gpu) if (ret) return ret; + if (gpu->aspace && gpu->aspace->mmu) + msm_mmu_enable(gpu->aspace->mmu); + return 0; } @@ -203,6 +206,9 @@ int msm_gpu_pm_suspend(struct msm_gpu *gpu) if (WARN_ON(gpu->active_cnt < 0)) return -EINVAL; + if (gpu->aspace && gpu->aspace->mmu) + msm_mmu_disable(gpu->aspace->mmu); + ret = disable_axi(gpu); if (ret) return ret; @@ -837,7 +843,7 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev, dev_info(drm->dev, "%s: using IOMMU\n", name); gpu->aspace = msm_gem_address_space_create(&pdev->dev, - iommu, "gpu"); + iommu, MSM_IOMMU_DOMAIN_USER, "gpu"); if (IS_ERR(gpu->aspace)) { ret = PTR_ERR(gpu->aspace); dev_err(drm->dev, "failed to init iommu: %d\n", ret); diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h index 3fac423929c5..29e2e59b580b 100644 --- a/drivers/gpu/drm/msm/msm_gpu.h +++ b/drivers/gpu/drm/msm/msm_gpu.h @@ -29,6 +29,9 @@ struct msm_gem_submit; struct msm_gpu_perfcntr; +#define MSM_GPU_DEFAULT_IONAME "kgsl_3d0_reg_memory" +#define MSM_GPU_DEFAULT_IRQNAME "kgsl_3d0_irq" + struct msm_gpu_config { const char *ioname; const char *irqname; diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c index 3af24646f4f1..b52c4752c5fe 100644 --- a/drivers/gpu/drm/msm/msm_iommu.c +++ b/drivers/gpu/drm/msm/msm_iommu.c @@ -17,6 +17,7 @@ #include <linux/of_platform.h> #include <linux/of_address.h> +#include <soc/qcom/secure_buffer.h> #include "msm_drv.h" #include "msm_iommu.h" @@ -27,31 +28,17 @@ static int msm_fault_handler(struct iommu_domain *iommu, struct device *dev, return 0; } -/* - * Get and enable the IOMMU clocks so that we can make - * sure they stay on the entire duration so that we can - * safely change the pagetable from the GPU - */ -static void _get_iommu_clocks(struct msm_mmu *mmu, struct platform_device *pdev) +static void iommu_get_clocks(struct msm_iommu *iommu, struct device *dev) { - struct msm_iommu *iommu = to_msm_iommu(mmu); - struct device *dev; struct property *prop; const char *name; int i = 0; - if (WARN_ON(!pdev)) - return; - - dev = &pdev->dev; - iommu->nr_clocks = of_property_count_strings(dev->of_node, "clock-names"); - if (iommu->nr_clocks < 0) { - iommu->nr_clocks = 0; + if (iommu->nr_clocks < 0) return; - } if (WARN_ON(iommu->nr_clocks > ARRAY_SIZE(iommu->clocks))) iommu->nr_clocks = ARRAY_SIZE(iommu->clocks); @@ -60,78 +47,58 @@ static void _get_iommu_clocks(struct msm_mmu *mmu, struct platform_device *pdev) if (i == iommu->nr_clocks) break; - iommu->clocks[i] = clk_get(dev, name); - if (iommu->clocks[i]) - clk_prepare_enable(iommu->clocks[i]); - - i++; + iommu->clocks[i++] = clk_get(dev, name); } } -static int _attach_iommu_device(struct msm_mmu *mmu, - struct iommu_domain *domain, const char **names, int cnt) + +static void msm_iommu_clocks_enable(struct msm_mmu *mmu) { + struct msm_iommu *iommu = to_msm_iommu(mmu); int i; - /* See if there is a iommus member in the current device. If not, look - * for the names and see if there is one in there. - */ + if (!iommu->nr_clocks) + iommu_get_clocks(iommu, mmu->dev->parent); - if (of_find_property(mmu->dev->of_node, "iommus", NULL)) - return iommu_attach_device(domain, mmu->dev); - - /* Look through the list of names for a target */ - for (i = 0; i < cnt; i++) { - struct device_node *node = - of_find_node_by_name(mmu->dev->of_node, names[i]); - - if (!node) - continue; - - if (of_find_property(node, "iommus", NULL)) { - struct platform_device *pdev; - - /* Get the platform device for the node */ - of_platform_populate(node->parent, NULL, NULL, - mmu->dev); - - pdev = of_find_device_by_node(node); - - if (!pdev) - continue; - - _get_iommu_clocks(mmu, - of_find_device_by_node(node->parent)); + for (i = 0; i < iommu->nr_clocks; i++) { + if (iommu->clocks[i]) + clk_prepare_enable(iommu->clocks[i]); + } +} - mmu->dev = &pdev->dev; +static void msm_iommu_clocks_disable(struct msm_mmu *mmu) +{ + struct msm_iommu *iommu = to_msm_iommu(mmu); + int i; - return iommu_attach_device(domain, mmu->dev); - } + for (i = 0; i < iommu->nr_clocks; i++) { + if (iommu->clocks[i]) + clk_disable_unprepare(iommu->clocks[i]); } +} - dev_err(mmu->dev, "Couldn't find a IOMMU device\n"); - return -ENODEV; +static int msm_iommu_attach(struct msm_mmu *mmu, const char **names, + int cnt) +{ + struct msm_iommu *iommu = to_msm_iommu(mmu); + + return iommu_attach_device(iommu->domain, mmu->dev); } -static int msm_iommu_attach(struct msm_mmu *mmu, const char **names, int cnt) +static int msm_iommu_attach_user(struct msm_mmu *mmu, const char **names, + int cnt) { struct msm_iommu *iommu = to_msm_iommu(mmu); - int val = 1, ret; + int ret, val = 1; /* Hope springs eternal */ - iommu->allow_dynamic = true; - - /* per-instance pagetables need TTBR1 support in the IOMMU driver */ - ret = iommu_domain_set_attr(iommu->domain, - DOMAIN_ATTR_ENABLE_TTBR1, &val); - if (ret) - iommu->allow_dynamic = false; + iommu->allow_dynamic = !iommu_domain_set_attr(iommu->domain, + DOMAIN_ATTR_ENABLE_TTBR1, &val) ? true : false; /* Mark the GPU as I/O coherent if it is supported */ iommu->is_coherent = of_dma_is_coherent(mmu->dev->of_node); - /* Attach the device to the domain */ - ret = _attach_iommu_device(mmu, iommu->domain, names, cnt); + ret = iommu_attach_device(iommu->domain, mmu->dev); if (ret) return ret; @@ -176,17 +143,25 @@ static int msm_iommu_attach_dynamic(struct msm_mmu *mmu, const char **names, return 0; } +static int msm_iommu_attach_secure(struct msm_mmu *mmu, const char **names, + int cnt) +{ + struct msm_iommu *iommu = to_msm_iommu(mmu); + int ret, vmid = VMID_CP_PIXEL; + + ret = iommu_domain_set_attr(iommu->domain, DOMAIN_ATTR_SECURE_VMID, + &vmid); + if (ret) + return ret; + + return iommu_attach_device(iommu->domain, mmu->dev); +} + static void msm_iommu_detach(struct msm_mmu *mmu) { struct msm_iommu *iommu = to_msm_iommu(mmu); - int i; iommu_detach_device(iommu->domain, mmu->dev); - - for (i = 0; i < iommu->nr_clocks; i++) { - if (iommu->clocks[i]) - clk_disable(iommu->clocks[i]); - } } static void msm_iommu_detach_dynamic(struct msm_mmu *mmu) @@ -196,69 +171,50 @@ static void msm_iommu_detach_dynamic(struct msm_mmu *mmu) } static int msm_iommu_map(struct msm_mmu *mmu, uint64_t iova, - struct sg_table *sgt, int prot) + struct sg_table *sgt, u32 flags, void *priv) { struct msm_iommu *iommu = to_msm_iommu(mmu); struct iommu_domain *domain = iommu->domain; - struct scatterlist *sg; - uint64_t da = iova; - unsigned int i, j; int ret; + u32 prot = IOMMU_READ; if (!domain || !sgt) return -EINVAL; - for_each_sg(sgt->sgl, sg, sgt->nents, i) { - phys_addr_t pa = sg_phys(sg) - sg->offset; - size_t bytes = sg->length + sg->offset; + if (!(flags & MSM_BO_GPU_READONLY)) + prot |= IOMMU_WRITE; - VERB("map[%d]: %016llx %pa(%zx)", i, iova, &pa, bytes); + if (flags & MSM_BO_PRIVILEGED) + prot |= IOMMU_PRIV; - ret = iommu_map(domain, da, pa, bytes, prot); - if (ret) - goto fail; + if ((flags & MSM_BO_CACHED) && msm_iommu_coherent(mmu)) + prot |= IOMMU_CACHE; - da += bytes; - } - - return 0; - -fail: - da = iova; + /* iommu_map_sg returns the number of bytes mapped */ + ret = iommu_map_sg(domain, iova, sgt->sgl, sgt->nents, prot); + if (ret) + sgt->sgl->dma_address = iova; - for_each_sg(sgt->sgl, sg, i, j) { - size_t bytes = sg->length + sg->offset; - iommu_unmap(domain, da, bytes); - da += bytes; - } - return ret; + return ret ? 0 : -ENOMEM; } -static int msm_iommu_unmap(struct msm_mmu *mmu, uint64_t iova, - struct sg_table *sgt) +static void msm_iommu_unmap(struct msm_mmu *mmu, uint64_t iova, + struct sg_table *sgt, void *priv) { struct msm_iommu *iommu = to_msm_iommu(mmu); struct iommu_domain *domain = iommu->domain; struct scatterlist *sg; - uint64_t da = iova; - int i; + size_t len = 0; + int ret, i; - for_each_sg(sgt->sgl, sg, sgt->nents, i) { - size_t bytes = sg->length + sg->offset; - size_t unmapped; + for_each_sg(sgt->sgl, sg, sgt->nents, i) + len += sg->length; - unmapped = iommu_unmap(domain, da, bytes); - if (unmapped < bytes) - return unmapped; + ret = iommu_unmap(domain, iova, len); + if (ret != len) + dev_warn(mmu->dev, "could not unmap iova %llx\n", iova); - VERB("unmap[%d]: %016llx(%zx)", i, iova, bytes); - - BUG_ON(!PAGE_ALIGNED(bytes)); - - da += bytes; - } - - return 0; + sgt->sgl->dma_address = 0; } static void msm_iommu_destroy(struct msm_mmu *mmu) @@ -268,7 +224,30 @@ static void msm_iommu_destroy(struct msm_mmu *mmu) kfree(iommu); } -static const struct msm_mmu_funcs funcs = { +static struct device *find_context_bank(const char *name) +{ + struct device_node *node = of_find_node_by_name(NULL, name); + struct platform_device *pdev, *parent; + + if (!node) + return ERR_PTR(-ENODEV); + + if (!of_find_property(node, "iommus", NULL)) + return ERR_PTR(-ENODEV); + + /* Get the parent device */ + parent = of_find_device_by_node(node->parent); + + /* Populate the sub nodes */ + of_platform_populate(parent->dev.of_node, NULL, NULL, &parent->dev); + + /* Get the context bank device */ + pdev = of_find_device_by_node(node); + + return pdev ? &pdev->dev : ERR_PTR(-ENODEV); +} + +static const struct msm_mmu_funcs default_funcs = { .attach = msm_iommu_attach, .detach = msm_iommu_detach, .map = msm_iommu_map, @@ -276,6 +255,24 @@ static const struct msm_mmu_funcs funcs = { .destroy = msm_iommu_destroy, }; +static const struct msm_mmu_funcs user_funcs = { + .attach = msm_iommu_attach_user, + .detach = msm_iommu_detach, + .map = msm_iommu_map, + .unmap = msm_iommu_unmap, + .destroy = msm_iommu_destroy, + .enable = msm_iommu_clocks_enable, + .disable = msm_iommu_clocks_disable, +}; + +static const struct msm_mmu_funcs secure_funcs = { + .attach = msm_iommu_attach_secure, + .detach = msm_iommu_detach, + .map = msm_iommu_map, + .unmap = msm_iommu_unmap, + .destroy = msm_iommu_destroy, +}; + static const struct msm_mmu_funcs dynamic_funcs = { .attach = msm_iommu_attach_dynamic, .detach = msm_iommu_detach_dynamic, @@ -284,8 +281,26 @@ static const struct msm_mmu_funcs dynamic_funcs = { .destroy = msm_iommu_destroy, }; -struct msm_mmu *_msm_iommu_new(struct device *dev, struct iommu_domain *domain, - const struct msm_mmu_funcs *funcs) +static const struct { + const char *cbname; + const struct msm_mmu_funcs *funcs; +} msm_iommu_domains[] = { + [MSM_IOMMU_DOMAIN_DEFAULT] = { + .cbname = NULL, + .funcs = &default_funcs, + }, + [MSM_IOMMU_DOMAIN_USER] = { + .cbname = "gfx3d_user", + .funcs = &user_funcs, + }, + [MSM_IOMMU_DOMAIN_SECURE] = { + .cbname = "gfx3d_secure", + .funcs = &secure_funcs + }, +}; + +static struct msm_mmu *iommu_create(struct device *dev, + struct iommu_domain *domain, const struct msm_mmu_funcs *funcs) { struct msm_iommu *iommu; @@ -299,9 +314,23 @@ struct msm_mmu *_msm_iommu_new(struct device *dev, struct iommu_domain *domain, return &iommu->base; } -struct msm_mmu *msm_iommu_new(struct device *dev, struct iommu_domain *domain) + +struct msm_mmu *msm_iommu_new(struct device *parent, + enum msm_iommu_domain_type type, struct iommu_domain *domain) { - return _msm_iommu_new(dev, domain, &funcs); + struct device *dev = parent; + + if (type >= ARRAY_SIZE(msm_iommu_domains) || + !msm_iommu_domains[type].funcs) + return ERR_PTR(-ENODEV); + + if (msm_iommu_domains[type].cbname) { + dev = find_context_bank(msm_iommu_domains[type].cbname); + if (IS_ERR(dev)) + return ERR_CAST(dev); + } + + return iommu_create(dev, domain, msm_iommu_domains[type].funcs); } /* @@ -326,7 +355,7 @@ struct msm_mmu *msm_iommu_new_dynamic(struct msm_mmu *base) if (!domain) return ERR_PTR(-ENODEV); - mmu = _msm_iommu_new(base->dev, domain, &dynamic_funcs); + mmu = iommu_create(base->dev, domain, &dynamic_funcs); if (IS_ERR(mmu)) { if (domain) diff --git a/drivers/gpu/drm/msm/msm_mmu.h b/drivers/gpu/drm/msm/msm_mmu.h index 501f12bef00d..033370ccbe24 100644 --- a/drivers/gpu/drm/msm/msm_mmu.h +++ b/drivers/gpu/drm/msm/msm_mmu.h @@ -30,21 +30,22 @@ enum msm_mmu_domain_type { MSM_SMMU_DOMAIN_MAX, }; +enum msm_iommu_domain_type { + MSM_IOMMU_DOMAIN_DEFAULT, + MSM_IOMMU_DOMAIN_USER, + MSM_IOMMU_DOMAIN_SECURE, +}; + struct msm_mmu_funcs { int (*attach)(struct msm_mmu *mmu, const char **names, int cnt); void (*detach)(struct msm_mmu *mmu); int (*map)(struct msm_mmu *mmu, uint64_t iova, struct sg_table *sgt, - int prot); - int (*unmap)(struct msm_mmu *mmu, uint64_t iova, struct sg_table *sgt); - int (*map_sg)(struct msm_mmu *mmu, struct sg_table *sgt, - enum dma_data_direction dir); - void (*unmap_sg)(struct msm_mmu *mmu, struct sg_table *sgt, - enum dma_data_direction dir); - int (*map_dma_buf)(struct msm_mmu *mmu, struct sg_table *sgt, - struct dma_buf *dma_buf, int dir); - void (*unmap_dma_buf)(struct msm_mmu *mmu, struct sg_table *sgt, - struct dma_buf *dma_buf, int dir); + u32 flags, void *priv); + void (*unmap)(struct msm_mmu *mmu, uint64_t iova, struct sg_table *sgt, + void *priv); void (*destroy)(struct msm_mmu *mmu); + void (*enable)(struct msm_mmu *mmu); + void (*disable)(struct msm_mmu *mmu); }; struct msm_mmu { @@ -59,9 +60,27 @@ static inline void msm_mmu_init(struct msm_mmu *mmu, struct device *dev, mmu->funcs = funcs; } -struct msm_mmu *msm_iommu_new(struct device *dev, struct iommu_domain *domain); +/* Create a new SDE mmu device */ struct msm_mmu *msm_smmu_new(struct device *dev, enum msm_mmu_domain_type domain); + +/* Create a new legacy MDP4 or GPU mmu device */ +struct msm_mmu *msm_iommu_new(struct device *parent, + enum msm_iommu_domain_type type, struct iommu_domain *domain); + +/* Create a new dynamic domain for GPU */ struct msm_mmu *msm_iommu_new_dynamic(struct msm_mmu *orig); +static inline void msm_mmu_enable(struct msm_mmu *mmu) +{ + if (mmu->funcs->enable) + mmu->funcs->enable(mmu); +} + +static inline void msm_mmu_disable(struct msm_mmu *mmu) +{ + if (mmu->funcs->disable) + mmu->funcs->disable(mmu); +} + #endif /* __MSM_MMU_H__ */ diff --git a/drivers/gpu/drm/msm/msm_prop.c b/drivers/gpu/drm/msm/msm_prop.c index 5a9e472ea59b..5f3d1b6356aa 100644 --- a/drivers/gpu/drm/msm/msm_prop.c +++ b/drivers/gpu/drm/msm/msm_prop.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, 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 @@ -183,6 +183,55 @@ static void _msm_property_install_integer(struct msm_property_info *info, } } +/** + * _msm_property_install_integer - install signed drm range property + * @info: Pointer to property info container struct + * @name: Property name + * @flags: Other property type flags, e.g. DRM_MODE_PROP_IMMUTABLE + * @min: Min property value + * @max: Max property value + * @init: Default Property value + * @property_idx: Property index + * @force_dirty: Whether or not to filter 'dirty' status on unchanged values + */ +static void _msm_property_install_signed_integer(struct msm_property_info *info, + const char *name, int flags, int64_t min, int64_t max, + int64_t init, uint32_t property_idx, bool force_dirty) +{ + struct drm_property **prop; + + if (!info) + return; + + ++info->install_request; + + if (!name || (property_idx >= info->property_count)) { + DRM_ERROR("invalid argument(s), %s\n", name ? name : "null"); + } else { + prop = &info->property_array[property_idx]; + /* + * Properties need to be attached to each drm object that + * uses them, but only need to be created once + */ + if (*prop == 0) { + *prop = drm_property_create_signed_range(info->dev, + flags, name, min, max); + if (*prop == 0) + DRM_ERROR("create %s property failed\n", name); + } + + /* save init value for later */ + info->property_data[property_idx].default_value = I642U64(init); + info->property_data[property_idx].force_dirty = force_dirty; + + /* always attach property, if created */ + if (*prop) { + drm_object_attach_property(info->base, *prop, init); + ++info->install_count; + } + } +} + void msm_property_install_range(struct msm_property_info *info, const char *name, int flags, uint64_t min, uint64_t max, uint64_t init, uint32_t property_idx) @@ -199,6 +248,22 @@ void msm_property_install_volatile_range(struct msm_property_info *info, min, max, init, property_idx, true); } +void msm_property_install_signed_range(struct msm_property_info *info, + const char *name, int flags, int64_t min, int64_t max, + int64_t init, uint32_t property_idx) +{ + _msm_property_install_signed_integer(info, name, flags, + min, max, init, property_idx, false); +} + +void msm_property_install_volatile_signed_range(struct msm_property_info *info, + const char *name, int flags, int64_t min, int64_t max, + int64_t init, uint32_t property_idx) +{ + _msm_property_install_signed_integer(info, name, flags, + min, max, init, property_idx, true); +} + void msm_property_install_rotation(struct msm_property_info *info, unsigned int supported_rotations, uint32_t property_idx) { diff --git a/drivers/gpu/drm/msm/msm_prop.h b/drivers/gpu/drm/msm/msm_prop.h index dbe28bdf5638..1430551700c7 100644 --- a/drivers/gpu/drm/msm/msm_prop.h +++ b/drivers/gpu/drm/msm/msm_prop.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, 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 @@ -208,6 +208,45 @@ void msm_property_install_volatile_range(struct msm_property_info *info, uint32_t property_idx); /** + * msm_property_install_signed_range - install signed drm range property + * @info: Pointer to property info container struct + * @name: Property name + * @flags: Other property type flags, e.g. DRM_MODE_PROP_IMMUTABLE + * @min: Min property value + * @max: Max property value + * @init: Default Property value + * @property_idx: Property index + */ +void msm_property_install_signed_range(struct msm_property_info *info, + const char *name, + int flags, + int64_t min, + int64_t max, + int64_t init, + uint32_t property_idx); + +/** + * msm_property_install_volatile_signed_range - install signed range property + * This function is similar to msm_property_install_range, but assumes + * that the property is meant for holding user pointers or descriptors + * that may reference volatile data without having an updated value. + * @info: Pointer to property info container struct + * @name: Property name + * @flags: Other property type flags, e.g. DRM_MODE_PROP_IMMUTABLE + * @min: Min property value + * @max: Max property value + * @init: Default Property value + * @property_idx: Property index + */ +void msm_property_install_volatile_signed_range(struct msm_property_info *info, + const char *name, + int flags, + int64_t min, + int64_t max, + int64_t init, + uint32_t property_idx); + +/** * msm_property_install_rotation - install standard drm rotation property * @info: Pointer to property info container struct * @supported_rotations: Bitmask of supported rotation values (see diff --git a/drivers/gpu/drm/msm/msm_smmu.c b/drivers/gpu/drm/msm/msm_smmu.c index c99f51e09700..c2dd5f96521e 100644 --- a/drivers/gpu/drm/msm/msm_smmu.c +++ b/drivers/gpu/drm/msm/msm_smmu.c @@ -53,6 +53,17 @@ struct msm_smmu_domain { #define to_msm_smmu(x) container_of(x, struct msm_smmu, base) #define msm_smmu_to_client(smmu) (smmu->client) + +static int msm_smmu_fault_handler(struct iommu_domain *iommu, + struct device *dev, unsigned long iova, int flags, void *arg) +{ + + dev_info(dev, "%s: iova=0x%08lx, flags=0x%x, iommu=%pK\n", __func__, + iova, flags, iommu); + return 0; +} + + static int _msm_smmu_create_mapping(struct msm_smmu_client *client, const struct msm_smmu_domain *domain); @@ -105,106 +116,34 @@ static void msm_smmu_detach(struct msm_mmu *mmu) } static int msm_smmu_map(struct msm_mmu *mmu, uint64_t iova, - struct sg_table *sgt, int prot) + struct sg_table *sgt, u32 flags, void *priv) { struct msm_smmu *smmu = to_msm_smmu(mmu); struct msm_smmu_client *client = msm_smmu_to_client(smmu); - struct iommu_domain *domain; - struct scatterlist *sg; - uint64_t da = iova; - unsigned int i, j; int ret; - if (!client) - return -ENODEV; - - domain = client->mmu_mapping->domain; - if (!domain || !sgt) - return -EINVAL; - - for_each_sg(sgt->sgl, sg, sgt->nents, i) { - u32 pa = sg_phys(sg) - sg->offset; - size_t bytes = sg->length + sg->offset; - - VERB("map[%d]: %16llx %08x(%zx)", i, iova, pa, bytes); - - ret = iommu_map(domain, da, pa, bytes, prot); - if (ret) - goto fail; - - da += bytes; - } - - return 0; - -fail: - da = iova; + if (priv) + ret = msm_dma_map_sg_lazy(client->dev, sgt->sgl, sgt->nents, + DMA_BIDIRECTIONAL, priv); + else + ret = dma_map_sg(client->dev, sgt->sgl, sgt->nents, + DMA_BIDIRECTIONAL); - for_each_sg(sgt->sgl, sg, i, j) { - size_t bytes = sg->length + sg->offset; - - iommu_unmap(domain, da, bytes); - da += bytes; - } - return ret; + return (ret != sgt->nents) ? -ENOMEM : 0; } -static int msm_smmu_map_sg(struct msm_mmu *mmu, struct sg_table *sgt, - enum dma_data_direction dir) +static void msm_smmu_unmap(struct msm_mmu *mmu, uint64_t iova, + struct sg_table *sgt, void *priv) { struct msm_smmu *smmu = to_msm_smmu(mmu); struct msm_smmu_client *client = msm_smmu_to_client(smmu); - int ret; - ret = dma_map_sg(client->dev, sgt->sgl, sgt->nents, dir); - if (ret != sgt->nents) - return -ENOMEM; - - return 0; -} - -static void msm_smmu_unmap_sg(struct msm_mmu *mmu, struct sg_table *sgt, - enum dma_data_direction dir) -{ - struct msm_smmu *smmu = to_msm_smmu(mmu); - struct msm_smmu_client *client = msm_smmu_to_client(smmu); - - dma_unmap_sg(client->dev, sgt->sgl, sgt->nents, dir); -} - -static int msm_smmu_unmap(struct msm_mmu *mmu, uint64_t iova, - struct sg_table *sgt) -{ - struct msm_smmu *smmu = to_msm_smmu(mmu); - struct msm_smmu_client *client = msm_smmu_to_client(smmu); - struct iommu_domain *domain; - struct scatterlist *sg; - uint64_t da = iova; - int i; - - if (!client) - return -ENODEV; - - domain = client->mmu_mapping->domain; - if (!domain || !sgt) - return -EINVAL; - - for_each_sg(sgt->sgl, sg, sgt->nents, i) { - size_t bytes = sg->length + sg->offset; - size_t unmapped; - - unmapped = iommu_unmap(domain, da, bytes); - if (unmapped < bytes) - return unmapped; - - VERB("unmap[%d]: %16llx(%zx)", i, iova, bytes); - - WARN_ON(!PAGE_ALIGNED(bytes)); - - da += bytes; - } - - return 0; + if (priv) + msm_dma_unmap_sg(client->dev, sgt->sgl, sgt->nents, + DMA_BIDIRECTIONAL, priv); + else + dma_unmap_sg(client->dev, sgt->sgl, sgt->nents, + DMA_BIDIRECTIONAL); } static void msm_smmu_destroy(struct msm_mmu *mmu) @@ -217,42 +156,11 @@ static void msm_smmu_destroy(struct msm_mmu *mmu) kfree(smmu); } -static int msm_smmu_map_dma_buf(struct msm_mmu *mmu, struct sg_table *sgt, - struct dma_buf *dma_buf, int dir) -{ - struct msm_smmu *smmu = to_msm_smmu(mmu); - struct msm_smmu_client *client = msm_smmu_to_client(smmu); - int ret; - - ret = msm_dma_map_sg_lazy(client->dev, sgt->sgl, sgt->nents, dir, - dma_buf); - if (ret != sgt->nents) { - DRM_ERROR("dma map sg failed\n"); - return -ENOMEM; - } - - return 0; -} - - -static void msm_smmu_unmap_dma_buf(struct msm_mmu *mmu, struct sg_table *sgt, - struct dma_buf *dma_buf, int dir) -{ - struct msm_smmu *smmu = to_msm_smmu(mmu); - struct msm_smmu_client *client = msm_smmu_to_client(smmu); - - msm_dma_unmap_sg(client->dev, sgt->sgl, sgt->nents, dir, dma_buf); -} - static const struct msm_mmu_funcs funcs = { .attach = msm_smmu_attach, .detach = msm_smmu_detach, .map = msm_smmu_map, - .map_sg = msm_smmu_map_sg, - .unmap_sg = msm_smmu_unmap_sg, .unmap = msm_smmu_unmap, - .map_dma_buf = msm_smmu_map_dma_buf, - .unmap_dma_buf = msm_smmu_unmap_dma_buf, .destroy = msm_smmu_destroy, }; @@ -362,6 +270,7 @@ struct msm_mmu *msm_smmu_new(struct device *dev, { struct msm_smmu *smmu; struct device *client_dev; + struct msm_smmu_client *client; smmu = kzalloc(sizeof(*smmu), GFP_KERNEL); if (!smmu) @@ -376,6 +285,11 @@ struct msm_mmu *msm_smmu_new(struct device *dev, smmu->client_dev = client_dev; msm_mmu_init(&smmu->base, dev, &funcs); + client = msm_smmu_to_client(smmu); + if (client) + iommu_set_fault_handler(client->mmu_mapping->domain, + msm_smmu_fault_handler, dev); + return &smmu->base; } diff --git a/drivers/gpu/drm/msm/msm_snapshot.h b/drivers/gpu/drm/msm/msm_snapshot.h index 247e1358c885..fd560b2129f1 100644 --- a/drivers/gpu/drm/msm/msm_snapshot.h +++ b/drivers/gpu/drm/msm/msm_snapshot.h @@ -71,8 +71,8 @@ static inline bool _snapshot_header(struct msm_snapshot *snapshot, */ #define SNAPSHOT_HEADER(_snapshot, _header, _id, _dwords) \ _snapshot_header((_snapshot), \ - (struct msm_snapshot_section_header *) &(header), \ - sizeof(header), (_dwords) << 2, (_id)) + (struct msm_snapshot_section_header *) &(_header), \ + sizeof(_header), (_dwords) << 2, (_id)) struct msm_gpu; diff --git a/drivers/gpu/drm/msm/msm_snapshot_api.h b/drivers/gpu/drm/msm/msm_snapshot_api.h index 9f0adb9ee784..7ad6f0498423 100644 --- a/drivers/gpu/drm/msm/msm_snapshot_api.h +++ b/drivers/gpu/drm/msm/msm_snapshot_api.h @@ -118,4 +118,17 @@ struct msm_snapshot_shader { __u32 size; } __packed; +#define SNAPSHOT_GPU_OBJECT_SHADER 1 +#define SNAPSHOT_GPU_OBJECT_IB 2 +#define SNAPSHOT_GPU_OBJECT_GENERIC 3 +#define SNAPSHOT_GPU_OBJECT_DRAW 4 +#define SNAPSHOT_GPU_OBJECT_GLOBAL 5 + +struct msm_snapshot_gpu_object { + struct msm_snapshot_section_header header; + __u32 type; + __u64 gpuaddr; + __u64 pt_base; + __u64 size; +} __packed; #endif diff --git a/drivers/gpu/drm/msm/sde/sde_backlight.c b/drivers/gpu/drm/msm/sde/sde_backlight.c index 9034eeb944fe..78df28a0016b 100644 --- a/drivers/gpu/drm/msm/sde/sde_backlight.c +++ b/drivers/gpu/drm/msm/sde/sde_backlight.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, 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 @@ -37,15 +37,15 @@ static int sde_backlight_device_update_status(struct backlight_device *bd) connector = bl_get_data(bd); c_conn = to_sde_connector(connector); display = (struct dsi_display *) c_conn->display; - if (brightness > display->panel->bl_config.bl_max_level) - brightness = display->panel->bl_config.bl_max_level; + if (brightness > display->panel[0]->bl_config.bl_max_level) + brightness = display->panel[0]->bl_config.bl_max_level; /* This maps UI brightness into driver backlight level with * rounding */ SDE_BRIGHT_TO_BL(bl_lvl, brightness, - display->panel->bl_config.bl_max_level, - display->panel->bl_config.brightness_max_level); + display->panel[0]->bl_config.bl_max_level, + display->panel[0]->bl_config.brightness_max_level); if (!bl_lvl && brightness) bl_lvl = 1; @@ -85,7 +85,7 @@ int sde_backlight_setup(struct drm_connector *connector) switch (c_conn->connector_type) { case DRM_MODE_CONNECTOR_DSI: display = (struct dsi_display *) c_conn->display; - bl_config = &display->panel->bl_config; + bl_config = &display->panel[0]->bl_config; props.max_brightness = bl_config->brightness_max_level; props.brightness = bl_config->brightness_max_level; bd = backlight_device_register("sde-backlight", diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c index 31cf25ab5691..7538927a4993 100644 --- a/drivers/gpu/drm/msm/sde/sde_connector.c +++ b/drivers/gpu/drm/msm/sde/sde_connector.c @@ -540,14 +540,6 @@ struct drm_connector *sde_connector_init(struct drm_device *dev, goto error_unregister_conn; } - if (c_conn->ops.set_backlight) { - rc = sde_backlight_setup(&c_conn->base); - if (rc) { - pr_err("failed to setup backlight, rc=%d\n", rc); - goto error_unregister_conn; - } - } - /* create properties */ msm_property_init(&c_conn->property_info, &c_conn->base.base, dev, priv->conn_property, c_conn->property_data, @@ -586,6 +578,10 @@ struct drm_connector *sde_connector_init(struct drm_device *dev, msm_property_install_range(&c_conn->property_info, "RETIRE_FENCE", 0x0, 0, INR_OPEN_MAX, 0, CONNECTOR_PROP_RETIRE_FENCE); + msm_property_install_volatile_signed_range(&c_conn->property_info, + "PLL_DELTA", 0x0, INT_MIN, INT_MAX, 0, + CONNECTOR_PROP_PLL_DELTA); + /* enum/bitmask properties */ msm_property_install_enum(&c_conn->property_info, "topology_name", DRM_MODE_PROP_IMMUTABLE, 0, e_topology_name, diff --git a/drivers/gpu/drm/msm/sde/sde_formats.c b/drivers/gpu/drm/msm/sde/sde_formats.c index 23ffa9b554dd..a59ec31ba276 100644 --- a/drivers/gpu/drm/msm/sde/sde_formats.c +++ b/drivers/gpu/drm/msm/sde/sde_formats.c @@ -718,12 +718,12 @@ static int _sde_format_populate_addrs_linear( { unsigned int i; - /* Can now check the pitches given vs pitches expected */ + /* Update layout pitches from fb */ for (i = 0; i < layout->num_planes; ++i) { if (layout->plane_pitch[i] != fb->pitches[i]) { - DRM_ERROR("plane %u expected pitch %u, fb %u\n", + SDE_DEBUG("plane %u expected pitch %u, fb %u\n", i, layout->plane_pitch[i], fb->pitches[i]); - return -EINVAL; + layout->plane_pitch[i] = fb->pitches[i]; } } diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c index 581918da183f..709c9970b357 100644 --- a/drivers/gpu/drm/msm/sde/sde_kms.c +++ b/drivers/gpu/drm/msm/sde/sde_kms.c @@ -41,10 +41,6 @@ #define CREATE_TRACE_POINTS #include "sde_trace.h" -static const char * const iommu_ports[] = { - "mdp_0", -}; - /** * Controls size of event log buffer. Specified as a power of 2. */ @@ -416,6 +412,10 @@ static void sde_kms_wait_for_commit_done(struct msm_kms *kms, return; } + ret = drm_crtc_vblank_get(crtc); + if (ret) + return; + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { if (encoder->crtc != crtc) continue; @@ -431,6 +431,8 @@ static void sde_kms_wait_for_commit_done(struct msm_kms *kms, break; } } + + drm_crtc_vblank_put(crtc); } static void sde_kms_prepare_fence(struct msm_kms *kms, @@ -598,6 +600,7 @@ static int _sde_kms_setup_displays(struct drm_device *dev, .get_modes = sde_hdmi_connector_get_modes, .mode_valid = sde_hdmi_mode_valid, .get_info = sde_hdmi_get_info, + .set_property = sde_hdmi_set_property, }; struct msm_display_info info = {0}; struct drm_encoder *encoder; @@ -1076,8 +1079,7 @@ static int _sde_kms_mmu_init(struct sde_kms *sde_kms) sde_kms->aspace[i] = aspace; - ret = mmu->funcs->attach(mmu, (const char **)iommu_ports, - ARRAY_SIZE(iommu_ports)); + ret = mmu->funcs->attach(mmu, NULL, 0); if (ret) { SDE_ERROR("failed to attach iommu %d: %d\n", i, ret); msm_gem_address_space_put(aspace); diff --git a/drivers/gpu/drm/msm/sde/sde_rm.c b/drivers/gpu/drm/msm/sde/sde_rm.c index 1d27b27d265c..fca0768e2734 100644 --- a/drivers/gpu/drm/msm/sde/sde_rm.c +++ b/drivers/gpu/drm/msm/sde/sde_rm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, 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 @@ -1074,12 +1074,6 @@ void _sde_rm_release_rsvp( } kfree(rsvp); - - (void) msm_property_set_property( - sde_connector_get_propinfo(conn), - sde_connector_get_property_values(conn->state), - CONNECTOR_PROP_TOPOLOGY_NAME, - SDE_RM_TOPOLOGY_UNKNOWN); } void sde_rm_release(struct sde_rm *rm, struct drm_encoder *enc) @@ -1115,6 +1109,12 @@ void sde_rm_release(struct sde_rm *rm, struct drm_encoder *enc) SDE_DEBUG("release rsvp[s%de%d]\n", rsvp->seq, rsvp->enc_id); _sde_rm_release_rsvp(rm, rsvp, conn); + + (void) msm_property_set_property( + sde_connector_get_propinfo(conn), + sde_connector_get_property_values(conn->state), + CONNECTOR_PROP_TOPOLOGY_NAME, + SDE_RM_TOPOLOGY_UNKNOWN); } } @@ -1132,8 +1132,12 @@ static int _sde_rm_commit_rsvp( sde_connector_get_property_values(conn_state), CONNECTOR_PROP_TOPOLOGY_NAME, rsvp->topology); - if (ret) + if (ret) { + SDE_ERROR("failed to set topology name property, ret %d\n", + ret); _sde_rm_release_rsvp(rm, rsvp, conn_state->connector); + return ret; + } /* Swap next rsvp to be the active */ for (type = 0; type < SDE_HW_BLK_MAX; type++) { @@ -1226,6 +1230,12 @@ int sde_rm_reserve( _sde_rm_release_rsvp(rm, rsvp_cur, conn_state->connector); rsvp_cur = NULL; _sde_rm_print_rsvps(rm, SDE_RM_STAGE_AFTER_CLEAR); + (void) msm_property_set_property( + sde_connector_get_propinfo( + conn_state->connector), + sde_connector_get_property_values(conn_state), + CONNECTOR_PROP_TOPOLOGY_NAME, + SDE_RM_TOPOLOGY_UNKNOWN); } /* Check the proposed reservation, store it in hw's "next" field */ diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c index ece9f4102c0e..7f8acb3ebfcd 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c @@ -714,7 +714,7 @@ nv4a_chipset = { .i2c = nv04_i2c_new, .imem = nv40_instmem_new, .mc = nv44_mc_new, - .mmu = nv44_mmu_new, + .mmu = nv04_mmu_new, .pci = nv40_pci_new, .therm = nv40_therm_new, .timer = nv41_timer_new, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv31.c b/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv31.c index d4d8942b1347..e55f8302d08a 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv31.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv31.c @@ -198,7 +198,7 @@ nv31_mpeg_intr(struct nvkm_engine *engine) } if (type == 0x00000010) { - if (!nv31_mpeg_mthd(mpeg, mthd, data)) + if (nv31_mpeg_mthd(mpeg, mthd, data)) show &= ~0x01000000; } } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv44.c b/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv44.c index d433cfa4a8ab..36af0a8927fc 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv44.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/mpeg/nv44.c @@ -172,7 +172,7 @@ nv44_mpeg_intr(struct nvkm_engine *engine) } if (type == 0x00000010) { - if (!nv44_mpeg_mthd(subdev->device, mthd, data)) + if (nv44_mpeg_mthd(subdev->device, mthd, data)) show &= ~0x01000000; } } diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index 35310336dd0a..d684e2b79d2b 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -213,8 +213,8 @@ static void radeon_evict_flags(struct ttm_buffer_object *bo, rbo->placement.num_busy_placement = 0; for (i = 0; i < rbo->placement.num_placement; i++) { if (rbo->placements[i].flags & TTM_PL_FLAG_VRAM) { - if (rbo->placements[0].fpfn < fpfn) - rbo->placements[0].fpfn = fpfn; + if (rbo->placements[i].fpfn < fpfn) + rbo->placements[i].fpfn = fpfn; } else { rbo->placement.busy_placement = &rbo->placements[i]; diff --git a/drivers/gpu/drm/ttm/ttm_object.c b/drivers/gpu/drm/ttm/ttm_object.c index 4f5fa8d65fe9..144367c0c28f 100644 --- a/drivers/gpu/drm/ttm/ttm_object.c +++ b/drivers/gpu/drm/ttm/ttm_object.c @@ -179,7 +179,7 @@ int ttm_base_object_init(struct ttm_object_file *tfile, if (unlikely(ret != 0)) goto out_err0; - ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL); + ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL, false); if (unlikely(ret != 0)) goto out_err1; @@ -318,7 +318,8 @@ EXPORT_SYMBOL(ttm_ref_object_exists); int ttm_ref_object_add(struct ttm_object_file *tfile, struct ttm_base_object *base, - enum ttm_ref_type ref_type, bool *existed) + enum ttm_ref_type ref_type, bool *existed, + bool require_existed) { struct drm_open_hash *ht = &tfile->ref_hash[ref_type]; struct ttm_ref_object *ref; @@ -345,6 +346,9 @@ int ttm_ref_object_add(struct ttm_object_file *tfile, } rcu_read_unlock(); + if (require_existed) + return -EPERM; + ret = ttm_mem_global_alloc(mem_glob, sizeof(*ref), false, false); if (unlikely(ret != 0)) @@ -635,7 +639,7 @@ int ttm_prime_fd_to_handle(struct ttm_object_file *tfile, prime = (struct ttm_prime_object *) dma_buf->priv; base = &prime->base; *handle = base->hash.key; - ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL); + ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL, false); dma_buf_put(dma_buf); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c index 8e689b439890..6c649f7b5929 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c @@ -539,7 +539,7 @@ int vmw_fence_create(struct vmw_fence_manager *fman, struct vmw_fence_obj **p_fence) { struct vmw_fence_obj *fence; - int ret; + int ret; fence = kzalloc(sizeof(*fence), GFP_KERNEL); if (unlikely(fence == NULL)) @@ -702,6 +702,41 @@ void vmw_fence_fifo_up(struct vmw_fence_manager *fman) } +/** + * vmw_fence_obj_lookup - Look up a user-space fence object + * + * @tfile: A struct ttm_object_file identifying the caller. + * @handle: A handle identifying the fence object. + * @return: A struct vmw_user_fence base ttm object on success or + * an error pointer on failure. + * + * The fence object is looked up and type-checked. The caller needs + * to have opened the fence object first, but since that happens on + * creation and fence objects aren't shareable, that's not an + * issue currently. + */ +static struct ttm_base_object * +vmw_fence_obj_lookup(struct ttm_object_file *tfile, u32 handle) +{ + struct ttm_base_object *base = ttm_base_object_lookup(tfile, handle); + + if (!base) { + pr_err("Invalid fence object handle 0x%08lx.\n", + (unsigned long)handle); + return ERR_PTR(-EINVAL); + } + + if (base->refcount_release != vmw_user_fence_base_release) { + pr_err("Invalid fence object handle 0x%08lx.\n", + (unsigned long)handle); + ttm_base_object_unref(&base); + return ERR_PTR(-EINVAL); + } + + return base; +} + + int vmw_fence_obj_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -727,13 +762,9 @@ int vmw_fence_obj_wait_ioctl(struct drm_device *dev, void *data, arg->kernel_cookie = jiffies + wait_timeout; } - base = ttm_base_object_lookup(tfile, arg->handle); - if (unlikely(base == NULL)) { - printk(KERN_ERR "Wait invalid fence object handle " - "0x%08lx.\n", - (unsigned long)arg->handle); - return -EINVAL; - } + base = vmw_fence_obj_lookup(tfile, arg->handle); + if (IS_ERR(base)) + return PTR_ERR(base); fence = &(container_of(base, struct vmw_user_fence, base)->fence); @@ -772,13 +803,9 @@ int vmw_fence_obj_signaled_ioctl(struct drm_device *dev, void *data, struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; struct vmw_private *dev_priv = vmw_priv(dev); - base = ttm_base_object_lookup(tfile, arg->handle); - if (unlikely(base == NULL)) { - printk(KERN_ERR "Fence signaled invalid fence object handle " - "0x%08lx.\n", - (unsigned long)arg->handle); - return -EINVAL; - } + base = vmw_fence_obj_lookup(tfile, arg->handle); + if (IS_ERR(base)) + return PTR_ERR(base); fence = &(container_of(base, struct vmw_user_fence, base)->fence); fman = fman_from_fence(fence); @@ -1093,6 +1120,7 @@ int vmw_fence_event_ioctl(struct drm_device *dev, void *data, (struct drm_vmw_fence_event_arg *) data; struct vmw_fence_obj *fence = NULL; struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv); + struct ttm_object_file *tfile = vmw_fp->tfile; struct drm_vmw_fence_rep __user *user_fence_rep = (struct drm_vmw_fence_rep __user *)(unsigned long) arg->fence_rep; @@ -1106,24 +1134,18 @@ int vmw_fence_event_ioctl(struct drm_device *dev, void *data, */ if (arg->handle) { struct ttm_base_object *base = - ttm_base_object_lookup_for_ref(dev_priv->tdev, - arg->handle); - - if (unlikely(base == NULL)) { - DRM_ERROR("Fence event invalid fence object handle " - "0x%08lx.\n", - (unsigned long)arg->handle); - return -EINVAL; - } + vmw_fence_obj_lookup(tfile, arg->handle); + + if (IS_ERR(base)) + return PTR_ERR(base); + fence = &(container_of(base, struct vmw_user_fence, base)->fence); (void) vmw_fence_obj_reference(fence); if (user_fence_rep != NULL) { - bool existed; - ret = ttm_ref_object_add(vmw_fp->tfile, base, - TTM_REF_USAGE, &existed); + TTM_REF_USAGE, NULL, false); if (unlikely(ret != 0)) { DRM_ERROR("Failed to reference a fence " "object.\n"); @@ -1166,8 +1188,7 @@ int vmw_fence_event_ioctl(struct drm_device *dev, void *data, return 0; out_no_create: if (user_fence_rep != NULL) - ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile, - handle, TTM_REF_USAGE); + ttm_ref_object_base_unref(tfile, handle, TTM_REF_USAGE); out_no_ref_obj: vmw_fence_obj_unreference(&fence); return ret; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c index b8c6a03c8c54..5ec24fd801cd 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c @@ -114,8 +114,6 @@ int vmw_getparam_ioctl(struct drm_device *dev, void *data, param->value = dev_priv->has_dx; break; default: - DRM_ERROR("Illegal vmwgfx get param request: %d\n", - param->param); return -EINVAL; } @@ -186,7 +184,7 @@ int vmw_get_cap_3d_ioctl(struct drm_device *dev, void *data, bool gb_objects = !!(dev_priv->capabilities & SVGA_CAP_GBOBJECTS); struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv); - if (unlikely(arg->pad64 != 0)) { + if (unlikely(arg->pad64 != 0 || arg->max_size == 0)) { DRM_ERROR("Illegal GET_3D_CAP argument.\n"); return -EINVAL; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index e57667ca7557..dbca128a9aa6 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -591,7 +591,7 @@ static int vmw_user_dmabuf_synccpu_grab(struct vmw_user_dma_buffer *user_bo, return ret; ret = ttm_ref_object_add(tfile, &user_bo->prime.base, - TTM_REF_SYNCCPU_WRITE, &existed); + TTM_REF_SYNCCPU_WRITE, &existed, false); if (ret != 0 || existed) ttm_bo_synccpu_write_release(&user_bo->dma.base); @@ -775,7 +775,7 @@ int vmw_user_dmabuf_reference(struct ttm_object_file *tfile, *handle = user_bo->prime.base.hash.key; return ttm_ref_object_add(tfile, &user_bo->prime.base, - TTM_REF_USAGE, NULL); + TTM_REF_USAGE, NULL, false); } /* diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c index 7d620e82e000..c9c04ccccdd9 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c @@ -715,11 +715,14 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, 128; num_sizes = 0; - for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) + for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) { + if (req->mip_levels[i] > DRM_VMW_MAX_MIP_LEVELS) + return -EINVAL; num_sizes += req->mip_levels[i]; + } - if (num_sizes > DRM_VMW_MAX_SURFACE_FACES * - DRM_VMW_MAX_MIP_LEVELS) + if (num_sizes > DRM_VMW_MAX_SURFACE_FACES * DRM_VMW_MAX_MIP_LEVELS || + num_sizes == 0) return -EINVAL; size = vmw_user_surface_size + 128 + @@ -904,17 +907,16 @@ vmw_surface_handle_reference(struct vmw_private *dev_priv, uint32_t handle; struct ttm_base_object *base; int ret; + bool require_exist = false; if (handle_type == DRM_VMW_HANDLE_PRIME) { ret = ttm_prime_fd_to_handle(tfile, u_handle, &handle); if (unlikely(ret != 0)) return ret; } else { - if (unlikely(drm_is_render_client(file_priv))) { - DRM_ERROR("Render client refused legacy " - "surface reference.\n"); - return -EACCES; - } + if (unlikely(drm_is_render_client(file_priv))) + require_exist = true; + if (ACCESS_ONCE(vmw_fpriv(file_priv)->locked_master)) { DRM_ERROR("Locked master refused legacy " "surface reference.\n"); @@ -942,17 +944,14 @@ vmw_surface_handle_reference(struct vmw_private *dev_priv, /* * Make sure the surface creator has the same - * authenticating master. + * authenticating master, or is already registered with us. */ if (drm_is_primary_client(file_priv) && - user_srf->master != file_priv->master) { - DRM_ERROR("Trying to reference surface outside of" - " master domain.\n"); - ret = -EACCES; - goto out_bad_resource; - } + user_srf->master != file_priv->master) + require_exist = true; - ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL); + ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL, + require_exist); if (unlikely(ret != 0)) { DRM_ERROR("Could not add a reference to a surface.\n"); goto out_bad_resource; diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c index 6e2a0e3f2645..fa95b4dfe718 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.c +++ b/drivers/gpu/msm/kgsl_sharedmem.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2002,2007-2017 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 @@ -560,16 +560,70 @@ static inline unsigned int _fixup_cache_range_op(unsigned int op) } #endif -int kgsl_cache_range_op(struct kgsl_memdesc *memdesc, uint64_t offset, - uint64_t size, unsigned int op) +static int kgsl_do_cache_op(struct page *page, void *addr, + uint64_t offset, uint64_t size, unsigned int op) { + void (*cache_op)(const void *, const void *); + /* - * If the buffer is mapped in the kernel operate on that address - * otherwise use the user address + * The dmac_xxx_range functions handle addresses and sizes that + * are not aligned to the cacheline size correctly. */ + switch (_fixup_cache_range_op(op)) { + case KGSL_CACHE_OP_FLUSH: + cache_op = dmac_flush_range; + break; + case KGSL_CACHE_OP_CLEAN: + cache_op = dmac_clean_range; + break; + case KGSL_CACHE_OP_INV: + cache_op = dmac_inv_range; + break; + default: + return -EINVAL; + } - void *addr = (memdesc->hostptr) ? - memdesc->hostptr : (void *) memdesc->useraddr; + if (page != NULL) { + unsigned long pfn = page_to_pfn(page) + offset / PAGE_SIZE; + /* + * page_address() returns the kernel virtual address of page. + * For high memory kernel virtual address exists only if page + * has been mapped. So use a version of kmap rather than + * page_address() for high memory. + */ + if (PageHighMem(page)) { + offset &= ~PAGE_MASK; + + do { + unsigned int len = size; + + if (len + offset > PAGE_SIZE) + len = PAGE_SIZE - offset; + + page = pfn_to_page(pfn++); + addr = kmap_atomic(page); + cache_op(addr + offset, addr + offset + len); + kunmap_atomic(addr); + + size -= len; + offset = 0; + } while (size); + + return 0; + } + + addr = page_address(page); + } + + cache_op(addr + offset, addr + offset + (size_t) size); + return 0; +} + +int kgsl_cache_range_op(struct kgsl_memdesc *memdesc, uint64_t offset, + uint64_t size, unsigned int op) +{ + void *addr = NULL; + int ret = 0; if (size == 0 || size > UINT_MAX) return -EINVAL; @@ -578,38 +632,59 @@ int kgsl_cache_range_op(struct kgsl_memdesc *memdesc, uint64_t offset, if ((offset + size < offset) || (offset + size < size)) return -ERANGE; - /* Make sure the offset + size do not overflow the address */ - if (addr + ((size_t) offset + (size_t) size) < addr) - return -ERANGE; - /* Check that offset+length does not exceed memdesc->size */ if (offset + size > memdesc->size) return -ERANGE; - /* Return quietly if the buffer isn't mapped on the CPU */ - if (addr == NULL) - return 0; + if (memdesc->hostptr) { + addr = memdesc->hostptr; + /* Make sure the offset + size do not overflow the address */ + if (addr + ((size_t) offset + (size_t) size) < addr) + return -ERANGE; - addr = addr + offset; + ret = kgsl_do_cache_op(NULL, addr, offset, size, op); + return ret; + } /* - * The dmac_xxx_range functions handle addresses and sizes that - * are not aligned to the cacheline size correctly. + * If the buffer is not to mapped to kernel, perform cache + * operations after mapping to kernel. */ + if (memdesc->sgt != NULL) { + struct scatterlist *sg; + unsigned int i, pos = 0; - switch (_fixup_cache_range_op(op)) { - case KGSL_CACHE_OP_FLUSH: - dmac_flush_range(addr, addr + (size_t) size); - break; - case KGSL_CACHE_OP_CLEAN: - dmac_clean_range(addr, addr + (size_t) size); - break; - case KGSL_CACHE_OP_INV: - dmac_inv_range(addr, addr + (size_t) size); - break; - } + for_each_sg(memdesc->sgt->sgl, sg, memdesc->sgt->nents, i) { + uint64_t sg_offset, sg_left; - return 0; + if (offset >= (pos + sg->length)) { + pos += sg->length; + continue; + } + sg_offset = offset > pos ? offset - pos : 0; + sg_left = (sg->length - sg_offset > size) ? size : + sg->length - sg_offset; + ret = kgsl_do_cache_op(sg_page(sg), NULL, sg_offset, + sg_left, op); + size -= sg_left; + if (size == 0) + break; + pos += sg->length; + } + } else if (memdesc->pages != NULL) { + addr = vmap(memdesc->pages, memdesc->page_count, + VM_IOREMAP, pgprot_writecombine(PAGE_KERNEL)); + if (addr == NULL) + return -ENOMEM; + + /* Make sure the offset + size do not overflow the address */ + if (addr + ((size_t) offset + (size_t) size) < addr) + return -ERANGE; + + ret = kgsl_do_cache_op(NULL, addr, offset, size, op); + vunmap(addr); + } + return ret; } EXPORT_SYMBOL(kgsl_cache_range_op); diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 1ef37c727572..d037454fe7b8 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -73,7 +73,6 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, void *in, *out; unsigned long flags; int ret, err = 0; - unsigned long t; struct page *page; spin_lock_irqsave(&newchannel->lock, flags); @@ -183,11 +182,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, goto error1; } - t = wait_for_completion_timeout(&open_info->waitevent, 5*HZ); - if (t == 0) { - err = -ETIMEDOUT; - goto error1; - } + wait_for_completion(&open_info->waitevent); spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); list_del(&open_info->msglistentry); @@ -375,7 +370,7 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer, struct vmbus_channel_gpadl_header *gpadlmsg; struct vmbus_channel_gpadl_body *gpadl_body; struct vmbus_channel_msginfo *msginfo = NULL; - struct vmbus_channel_msginfo *submsginfo; + struct vmbus_channel_msginfo *submsginfo, *tmp; u32 msgcount; struct list_head *curr; u32 next_gpadl_handle; @@ -437,6 +432,13 @@ cleanup: list_del(&msginfo->msglistentry); spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); + if (msgcount > 1) { + list_for_each_entry_safe(submsginfo, tmp, &msginfo->submsglist, + msglistentry) { + kfree(submsginfo); + } + } + kfree(msginfo); return ret; } diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index 4fc2e8836e60..2bbc53025549 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -429,7 +429,7 @@ int vmbus_post_msg(void *buffer, size_t buflen) union hv_connection_id conn_id; int ret = 0; int retries = 0; - u32 msec = 1; + u32 usec = 1; conn_id.asu32 = 0; conn_id.u.id = VMBUS_MESSAGE_CONNECTION_ID; @@ -462,9 +462,9 @@ int vmbus_post_msg(void *buffer, size_t buflen) } retries++; - msleep(msec); - if (msec < 2048) - msec *= 2; + udelay(usec); + if (usec < 2048) + usec *= 2; } return ret; } diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 57c191798699..8ce1f2e22912 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -274,7 +274,7 @@ cleanup: * * This routine is called normally during driver unloading or exiting. */ -void hv_cleanup(void) +void hv_cleanup(bool crash) { union hv_x64_msr_hypercall_contents hypercall_msr; @@ -284,7 +284,8 @@ void hv_cleanup(void) if (hv_context.hypercall_page) { hypercall_msr.as_uint64 = 0; wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); - vfree(hv_context.hypercall_page); + if (!crash) + vfree(hv_context.hypercall_page); hv_context.hypercall_page = NULL; } @@ -304,8 +305,10 @@ void hv_cleanup(void) hypercall_msr.as_uint64 = 0; wrmsrl(HV_X64_MSR_REFERENCE_TSC, hypercall_msr.as_uint64); - vfree(hv_context.tsc_page); - hv_context.tsc_page = NULL; + if (!crash) { + vfree(hv_context.tsc_page); + hv_context.tsc_page = NULL; + } } #endif } diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index b853b4b083bd..354da7f207b7 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -430,16 +430,27 @@ struct dm_info_msg { * currently hot added. We hot add in multiples of 128M * chunks; it is possible that we may not be able to bring * online all the pages in the region. The range - * covered_end_pfn defines the pages that can + * covered_start_pfn:covered_end_pfn defines the pages that can * be brough online. */ struct hv_hotadd_state { struct list_head list; unsigned long start_pfn; + unsigned long covered_start_pfn; unsigned long covered_end_pfn; unsigned long ha_end_pfn; unsigned long end_pfn; + /* + * A list of gaps. + */ + struct list_head gap_list; +}; + +struct hv_hotadd_gap { + struct list_head list; + unsigned long start_pfn; + unsigned long end_pfn; }; struct balloon_state { @@ -595,18 +606,46 @@ static struct notifier_block hv_memory_nb = { .priority = 0 }; +/* Check if the particular page is backed and can be onlined and online it. */ +static void hv_page_online_one(struct hv_hotadd_state *has, struct page *pg) +{ + unsigned long cur_start_pgp; + unsigned long cur_end_pgp; + struct hv_hotadd_gap *gap; + + cur_start_pgp = (unsigned long)pfn_to_page(has->covered_start_pfn); + cur_end_pgp = (unsigned long)pfn_to_page(has->covered_end_pfn); + + /* The page is not backed. */ + if (((unsigned long)pg < cur_start_pgp) || + ((unsigned long)pg >= cur_end_pgp)) + return; + + /* Check for gaps. */ + list_for_each_entry(gap, &has->gap_list, list) { + cur_start_pgp = (unsigned long) + pfn_to_page(gap->start_pfn); + cur_end_pgp = (unsigned long) + pfn_to_page(gap->end_pfn); + if (((unsigned long)pg >= cur_start_pgp) && + ((unsigned long)pg < cur_end_pgp)) { + return; + } + } -static void hv_bring_pgs_online(unsigned long start_pfn, unsigned long size) + /* This frame is currently backed; online the page. */ + __online_page_set_limits(pg); + __online_page_increment_counters(pg); + __online_page_free(pg); +} + +static void hv_bring_pgs_online(struct hv_hotadd_state *has, + unsigned long start_pfn, unsigned long size) { int i; - for (i = 0; i < size; i++) { - struct page *pg; - pg = pfn_to_page(start_pfn + i); - __online_page_set_limits(pg); - __online_page_increment_counters(pg); - __online_page_free(pg); - } + for (i = 0; i < size; i++) + hv_page_online_one(has, pfn_to_page(start_pfn + i)); } static void hv_mem_hot_add(unsigned long start, unsigned long size, @@ -682,26 +721,25 @@ static void hv_online_page(struct page *pg) list_for_each(cur, &dm_device.ha_region_list) { has = list_entry(cur, struct hv_hotadd_state, list); - cur_start_pgp = (unsigned long)pfn_to_page(has->start_pfn); - cur_end_pgp = (unsigned long)pfn_to_page(has->covered_end_pfn); + cur_start_pgp = (unsigned long) + pfn_to_page(has->start_pfn); + cur_end_pgp = (unsigned long)pfn_to_page(has->end_pfn); - if (((unsigned long)pg >= cur_start_pgp) && - ((unsigned long)pg < cur_end_pgp)) { - /* - * This frame is currently backed; online the - * page. - */ - __online_page_set_limits(pg); - __online_page_increment_counters(pg); - __online_page_free(pg); - } + /* The page belongs to a different HAS. */ + if (((unsigned long)pg < cur_start_pgp) || + ((unsigned long)pg >= cur_end_pgp)) + continue; + + hv_page_online_one(has, pg); + break; } } -static bool pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt) +static int pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt) { struct list_head *cur; struct hv_hotadd_state *has; + struct hv_hotadd_gap *gap; unsigned long residual, new_inc; if (list_empty(&dm_device.ha_region_list)) @@ -714,8 +752,26 @@ static bool pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt) * If the pfn range we are dealing with is not in the current * "hot add block", move on. */ - if ((start_pfn >= has->end_pfn)) + if (start_pfn < has->start_pfn || start_pfn >= has->end_pfn) continue; + + /* + * If the current start pfn is not where the covered_end + * is, create a gap and update covered_end_pfn. + */ + if (has->covered_end_pfn != start_pfn) { + gap = kzalloc(sizeof(struct hv_hotadd_gap), GFP_ATOMIC); + if (!gap) + return -ENOMEM; + + INIT_LIST_HEAD(&gap->list); + gap->start_pfn = has->covered_end_pfn; + gap->end_pfn = start_pfn; + list_add_tail(&gap->list, &has->gap_list); + + has->covered_end_pfn = start_pfn; + } + /* * If the current hot add-request extends beyond * our current limit; extend it. @@ -732,19 +788,10 @@ static bool pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt) has->end_pfn += new_inc; } - /* - * If the current start pfn is not where the covered_end - * is, update it. - */ - - if (has->covered_end_pfn != start_pfn) - has->covered_end_pfn = start_pfn; - - return true; - + return 1; } - return false; + return 0; } static unsigned long handle_pg_range(unsigned long pg_start, @@ -768,7 +815,7 @@ static unsigned long handle_pg_range(unsigned long pg_start, * If the pfn range we are dealing with is not in the current * "hot add block", move on. */ - if ((start_pfn >= has->end_pfn)) + if (start_pfn < has->start_pfn || start_pfn >= has->end_pfn) continue; old_covered_state = has->covered_end_pfn; @@ -783,6 +830,8 @@ static unsigned long handle_pg_range(unsigned long pg_start, if (pgs_ol > pfn_cnt) pgs_ol = pfn_cnt; + has->covered_end_pfn += pgs_ol; + pfn_cnt -= pgs_ol; /* * Check if the corresponding memory block is already * online by checking its last previously backed page. @@ -791,10 +840,8 @@ static unsigned long handle_pg_range(unsigned long pg_start, */ if (start_pfn > has->start_pfn && !PageReserved(pfn_to_page(start_pfn - 1))) - hv_bring_pgs_online(start_pfn, pgs_ol); + hv_bring_pgs_online(has, start_pfn, pgs_ol); - has->covered_end_pfn += pgs_ol; - pfn_cnt -= pgs_ol; } if ((has->ha_end_pfn < has->end_pfn) && (pfn_cnt > 0)) { @@ -832,13 +879,19 @@ static unsigned long process_hot_add(unsigned long pg_start, unsigned long rg_size) { struct hv_hotadd_state *ha_region = NULL; + int covered; if (pfn_cnt == 0) return 0; - if (!dm_device.host_specified_ha_region) - if (pfn_covered(pg_start, pfn_cnt)) + if (!dm_device.host_specified_ha_region) { + covered = pfn_covered(pg_start, pfn_cnt); + if (covered < 0) + return 0; + + if (covered) goto do_pg_range; + } /* * If the host has specified a hot-add range; deal with it first. @@ -850,10 +903,12 @@ static unsigned long process_hot_add(unsigned long pg_start, return 0; INIT_LIST_HEAD(&ha_region->list); + INIT_LIST_HEAD(&ha_region->gap_list); list_add_tail(&ha_region->list, &dm_device.ha_region_list); ha_region->start_pfn = rg_start; ha_region->ha_end_pfn = rg_start; + ha_region->covered_start_pfn = pg_start; ha_region->covered_end_pfn = pg_start; ha_region->end_pfn = rg_start + rg_size; } @@ -1581,6 +1636,7 @@ static int balloon_remove(struct hv_device *dev) struct hv_dynmem_device *dm = hv_get_drvdata(dev); struct list_head *cur, *tmp; struct hv_hotadd_state *has; + struct hv_hotadd_gap *gap, *tmp_gap; if (dm->num_pages_ballooned != 0) pr_warn("Ballooned pages: %d\n", dm->num_pages_ballooned); @@ -1597,6 +1653,10 @@ static int balloon_remove(struct hv_device *dev) #endif list_for_each_safe(cur, tmp, &dm->ha_region_list) { has = list_entry(cur, struct hv_hotadd_state, list); + list_for_each_entry_safe(gap, tmp_gap, &has->gap_list, list) { + list_del(&gap->list); + kfree(gap); + } list_del(&has->list); kfree(has); } diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 12156db2e88e..75e383e6d03d 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -581,7 +581,7 @@ struct hv_ring_buffer_debug_info { extern int hv_init(void); -extern void hv_cleanup(void); +extern void hv_cleanup(bool crash); extern int hv_post_message(union hv_connection_id connection_id, enum hv_message_type message_type, diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 509ed9731630..802dcb409030 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -889,7 +889,7 @@ err_alloc: bus_unregister(&hv_bus); err_cleanup: - hv_cleanup(); + hv_cleanup(false); return ret; } @@ -1254,7 +1254,7 @@ static void hv_kexec_handler(void) vmbus_initiate_unload(); for_each_online_cpu(cpu) smp_call_function_single(cpu, hv_synic_cleanup, NULL, 1); - hv_cleanup(); + hv_cleanup(false); }; static void hv_crash_handler(struct pt_regs *regs) @@ -1266,7 +1266,7 @@ static void hv_crash_handler(struct pt_regs *regs) * for kdump. */ hv_synic_cleanup(NULL); - hv_cleanup(); + hv_cleanup(true); }; static int __init hv_acpi_init(void) @@ -1330,7 +1330,7 @@ static void __exit vmbus_exit(void) &hyperv_panic_block); } bus_unregister(&hv_bus); - hv_cleanup(); + hv_cleanup(false); for_each_online_cpu(cpu) { tasklet_kill(hv_context.event_dpc[cpu]); smp_call_function_single(cpu, hv_synic_cleanup, NULL, 1); diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index 0470fc843d4e..9b6854607d73 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -151,7 +151,9 @@ static irqreturn_t tiadc_irq_h(int irq, void *private) { struct iio_dev *indio_dev = private; struct tiadc_device *adc_dev = iio_priv(indio_dev); - unsigned int status, config; + unsigned int status, config, adc_fsm; + unsigned short count = 0; + status = tiadc_readl(adc_dev, REG_IRQSTATUS); /* @@ -165,6 +167,15 @@ static irqreturn_t tiadc_irq_h(int irq, void *private) tiadc_writel(adc_dev, REG_CTRL, config); tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW | IRQENB_FIFO1THRES); + + /* wait for idle state. + * ADC needs to finish the current conversion + * before disabling the module + */ + do { + adc_fsm = tiadc_readl(adc_dev, REG_ADCFSM); + } while (adc_fsm != 0x10 && count++ < 100); + tiadc_writel(adc_dev, REG_CTRL, (config | CNTRLREG_TSCSSENB)); return IRQ_HANDLED; } else if (status & IRQENB_FIFO1THRES) { diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c index 595511022795..0a86ef43e781 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c @@ -51,8 +51,6 @@ static int _hid_sensor_power_state(struct hid_sensor_common *st, bool state) st->report_state.report_id, st->report_state.index, HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM); - - poll_value = hid_sensor_read_poll_value(st); } else { int val; @@ -89,7 +87,9 @@ static int _hid_sensor_power_state(struct hid_sensor_common *st, bool state) sensor_hub_get_feature(st->hsdev, st->power_state.report_id, st->power_state.index, sizeof(state_val), &state_val); - if (state && poll_value) + if (state) + poll_value = hid_sensor_read_poll_value(st); + if (poll_value > 0) msleep_interruptible(poll_value * 2); return 0; diff --git a/drivers/iio/gyro/bmg160_core.c b/drivers/iio/gyro/bmg160_core.c index acb3b303d800..90841abd3ce4 100644 --- a/drivers/iio/gyro/bmg160_core.c +++ b/drivers/iio/gyro/bmg160_core.c @@ -28,6 +28,7 @@ #include <linux/iio/trigger_consumer.h> #include <linux/iio/triggered_buffer.h> #include <linux/regmap.h> +#include <linux/delay.h> #include "bmg160.h" #define BMG160_IRQ_NAME "bmg160_event" @@ -53,6 +54,9 @@ #define BMG160_NO_FILTER 0 #define BMG160_DEF_BW 100 +#define BMG160_GYRO_REG_RESET 0x14 +#define BMG160_GYRO_RESET_VAL 0xb6 + #define BMG160_REG_INT_MAP_0 0x17 #define BMG160_INT_MAP_0_BIT_ANY BIT(1) @@ -186,6 +190,14 @@ static int bmg160_chip_init(struct bmg160_data *data) int ret; unsigned int val; + /* + * Reset chip to get it in a known good state. A delay of 30ms after + * reset is required according to the datasheet. + */ + regmap_write(data->regmap, BMG160_GYRO_REG_RESET, + BMG160_GYRO_RESET_VAL); + usleep_range(30000, 30700); + ret = regmap_read(data->regmap, BMG160_REG_CHIP_ID, &val); if (ret < 0) { dev_err(data->dev, "Error reading reg_chip_id\n"); diff --git a/drivers/input/joystick/iforce/iforce-usb.c b/drivers/input/joystick/iforce/iforce-usb.c index d96aa27dfcdc..db64adfbe1af 100644 --- a/drivers/input/joystick/iforce/iforce-usb.c +++ b/drivers/input/joystick/iforce/iforce-usb.c @@ -141,6 +141,9 @@ static int iforce_usb_probe(struct usb_interface *intf, interface = intf->cur_altsetting; + if (interface->desc.bNumEndpoints < 2) + return -ENODEV; + epirq = &interface->endpoint[0].desc; epout = &interface->endpoint[1].desc; diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 16f000a76de5..3258baf3282e 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -189,6 +189,7 @@ static const struct xpad_device { { 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, { 0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller", 0, XTYPE_XBOX360 }, { 0x1532, 0x0037, "Razer Sabertooth", 0, XTYPE_XBOX360 }, + { 0x1532, 0x0a03, "Razer Wildcat", 0, XTYPE_XBOXONE }, { 0x15e4, 0x3f00, "Power A Mini Pro Elite", 0, XTYPE_XBOX360 }, { 0x15e4, 0x3f0a, "Xbox Airflo wired controller", 0, XTYPE_XBOX360 }, { 0x15e4, 0x3f10, "Batarang Xbox 360 controller", 0, XTYPE_XBOX360 }, @@ -310,6 +311,7 @@ static struct usb_device_id xpad_table[] = { XPAD_XBOX360_VENDOR(0x1689), /* Razer Onza */ XPAD_XBOX360_VENDOR(0x24c6), /* PowerA Controllers */ XPAD_XBOX360_VENDOR(0x1532), /* Razer Sabertooth */ + XPAD_XBOXONE_VENDOR(0x1532), /* Razer Wildcat */ XPAD_XBOX360_VENDOR(0x15e4), /* Numark X-Box 360 controllers */ XPAD_XBOX360_VENDOR(0x162e), /* Joytech X-Box 360 controllers */ { } diff --git a/drivers/input/misc/cm109.c b/drivers/input/misc/cm109.c index 9365535ba7f1..50a7faa504f7 100644 --- a/drivers/input/misc/cm109.c +++ b/drivers/input/misc/cm109.c @@ -675,6 +675,10 @@ static int cm109_usb_probe(struct usb_interface *intf, int error = -ENOMEM; interface = intf->cur_altsetting; + + if (interface->desc.bNumEndpoints < 1) + return -ENODEV; + endpoint = &interface->endpoint[0].desc; if (!usb_endpoint_is_int_in(endpoint)) diff --git a/drivers/input/misc/hbtp_input.c b/drivers/input/misc/hbtp_input.c index 21898c308075..0362095d4c38 100644 --- a/drivers/input/misc/hbtp_input.c +++ b/drivers/input/misc/hbtp_input.c @@ -62,7 +62,7 @@ struct hbtp_data { u32 ts_pinctrl_seq_delay; u32 ddic_pinctrl_seq_delay[HBTP_PINCTRL_DDIC_SEQ_NUM]; u32 fb_resume_seq_delay; - bool lcd_on; + int lcd_state; bool power_suspended; bool power_sync_enabled; bool power_sig_enabled; @@ -108,6 +108,7 @@ static int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data) { int blank; + int lcd_state; struct fb_event *evdata = data; struct fb_info *fbi = NULL; struct hbtp_data *hbtp_data = @@ -133,27 +134,32 @@ static int fb_notifier_callback(struct notifier_block *self, (event == FB_EARLY_EVENT_BLANK || event == FB_R_EARLY_EVENT_BLANK)) { blank = *(int *)(evdata->data); + lcd_state = hbtp->lcd_state; if (event == FB_EARLY_EVENT_BLANK) { - if (blank == FB_BLANK_UNBLANK) { + if (blank <= FB_BLANK_NORMAL && + lcd_state == FB_BLANK_POWERDOWN) { pr_debug("%s: receives EARLY_BLANK:UNBLANK\n", __func__); - hbtp_data->lcd_on = true; hbtp_fb_early_resume(hbtp_data); - } else if (blank == FB_BLANK_POWERDOWN) { + } else if (blank == FB_BLANK_POWERDOWN && + lcd_state <= FB_BLANK_NORMAL) { pr_debug("%s: receives EARLY_BLANK:POWERDOWN\n", __func__); - hbtp_data->lcd_on = false; + } else { + pr_debug("%s: receives EARLY_BLANK:%d in %d state\n", + __func__, blank, lcd_state); } } else if (event == FB_R_EARLY_EVENT_BLANK) { - if (blank == FB_BLANK_UNBLANK) { + if (blank <= FB_BLANK_NORMAL) { pr_debug("%s: receives R_EARLY_BALNK:UNBLANK\n", __func__); - hbtp_data->lcd_on = false; hbtp_fb_suspend(hbtp_data); } else if (blank == FB_BLANK_POWERDOWN) { pr_debug("%s: receives R_EARLY_BALNK:POWERDOWN\n", __func__); - hbtp_data->lcd_on = true; + } else { + pr_debug("%s: receives R_EARLY_BALNK:%d in %d state\n", + __func__, blank, lcd_state); } } } @@ -161,13 +167,20 @@ static int fb_notifier_callback(struct notifier_block *self, if (evdata->data && hbtp_data && event == FB_EVENT_BLANK) { blank = *(int *)(evdata->data); - if (blank == FB_BLANK_POWERDOWN) { + lcd_state = hbtp->lcd_state; + if (blank == FB_BLANK_POWERDOWN && + lcd_state <= FB_BLANK_NORMAL) { pr_debug("%s: receives BLANK:POWERDOWN\n", __func__); hbtp_fb_suspend(hbtp_data); - } else if (blank == FB_BLANK_UNBLANK) { + } else if (blank <= FB_BLANK_NORMAL && + lcd_state == FB_BLANK_POWERDOWN) { pr_debug("%s: receives BLANK:UNBLANK\n", __func__); hbtp_fb_resume(hbtp_data); + } else { + pr_debug("%s: receives BLANK:%d in %d state\n", + __func__, blank, lcd_state); } + hbtp_data->lcd_state = blank; } return 0; } @@ -1439,8 +1452,10 @@ static int __init hbtp_init(void) hbtp->sensor_data = kzalloc(sizeof(struct hbtp_sensor_data), GFP_KERNEL); - if (!hbtp->sensor_data) + if (!hbtp->sensor_data) { + error = -ENOMEM; goto err_sensordata; + } mutex_init(&hbtp->mutex); mutex_init(&hbtp->sensormutex); diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 9c0ea36913b4..f4e8fbec6a94 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -1667,6 +1667,10 @@ static int ims_pcu_parse_cdc_data(struct usb_interface *intf, struct ims_pcu *pc return -EINVAL; alt = pcu->ctrl_intf->cur_altsetting; + + if (alt->desc.bNumEndpoints < 1) + return -ENODEV; + pcu->ep_ctrl = &alt->endpoint[0].desc; pcu->max_ctrl_size = usb_endpoint_maxp(pcu->ep_ctrl); diff --git a/drivers/input/misc/qpnp-power-on.c b/drivers/input/misc/qpnp-power-on.c index add11d47ea2f..339f94c072f4 100644 --- a/drivers/input/misc/qpnp-power-on.c +++ b/drivers/input/misc/qpnp-power-on.c @@ -207,7 +207,7 @@ struct qpnp_pon { int pon_power_off_reason; int num_pon_reg; int num_pon_config; - u32 dbc; + u32 dbc_time_us; u32 uvlo; int warm_reset_poff_type; int hard_reset_poff_type; @@ -219,6 +219,8 @@ struct qpnp_pon { u8 warm_reset_reason2; bool is_spon; bool store_hard_reset_reason; + bool kpdpwr_dbc_enable; + ktime_t kpdpwr_last_release_time; }; static int pon_ship_mode_en; @@ -381,7 +383,7 @@ static int qpnp_pon_set_dbc(struct qpnp_pon *pon, u32 delay) int rc = 0; u32 val; - if (delay == pon->dbc) + if (delay == pon->dbc_time_us) goto out; if (pon->pon_input) @@ -409,7 +411,7 @@ static int qpnp_pon_set_dbc(struct qpnp_pon *pon, u32 delay) goto unlock; } - pon->dbc = delay; + pon->dbc_time_us = delay; unlock: if (pon->pon_input) @@ -418,12 +420,34 @@ out: return rc; } +static int qpnp_pon_get_dbc(struct qpnp_pon *pon, u32 *delay) +{ + int rc; + unsigned int val; + + rc = regmap_read(pon->regmap, QPNP_PON_DBC_CTL(pon), &val); + if (rc) { + pr_err("Unable to read pon_dbc_ctl rc=%d\n", rc); + return rc; + } + val &= QPNP_PON_DBC_DELAY_MASK(pon); + + if (is_pon_gen2(pon)) + *delay = USEC_PER_SEC / + (1 << (QPNP_PON_GEN2_DELAY_BIT_SHIFT - val)); + else + *delay = USEC_PER_SEC / + (1 << (QPNP_PON_DELAY_BIT_SHIFT - val)); + + return rc; +} + static ssize_t qpnp_pon_dbc_show(struct device *dev, struct device_attribute *attr, char *buf) { struct qpnp_pon *pon = dev_get_drvdata(dev); - return snprintf(buf, QPNP_PON_BUFFER_SIZE, "%d\n", pon->dbc); + return snprintf(buf, QPNP_PON_BUFFER_SIZE, "%d\n", pon->dbc_time_us); } static ssize_t qpnp_pon_dbc_store(struct device *dev, @@ -777,6 +801,7 @@ qpnp_pon_input_dispatch(struct qpnp_pon *pon, u32 pon_type) u8 pon_rt_bit = 0; u32 key_status; uint pon_rt_sts; + u64 elapsed_us; cfg = qpnp_get_cfg(pon, pon_type); if (!cfg) @@ -786,6 +811,15 @@ qpnp_pon_input_dispatch(struct qpnp_pon *pon, u32 pon_type) if (!cfg->key_code) return 0; + if (pon->kpdpwr_dbc_enable && cfg->pon_type == PON_KPDPWR) { + elapsed_us = ktime_us_delta(ktime_get(), + pon->kpdpwr_last_release_time); + if (elapsed_us < pon->dbc_time_us) { + pr_debug("Ignoring kpdpwr event - within debounce time\n"); + return 0; + } + } + /* check the RT status to get the current status of the line */ rc = regmap_read(pon->regmap, QPNP_PON_RT_STS(pon), &pon_rt_sts); if (rc) { @@ -814,6 +848,11 @@ qpnp_pon_input_dispatch(struct qpnp_pon *pon, u32 pon_type) cfg->key_code, pon_rt_sts); key_status = pon_rt_sts & pon_rt_bit; + if (pon->kpdpwr_dbc_enable && cfg->pon_type == PON_KPDPWR) { + if (!key_status) + pon->kpdpwr_last_release_time = ktime_get(); + } + /* * simulate press event in case release event occurred * without a press event @@ -2233,8 +2272,22 @@ static int qpnp_pon_probe(struct platform_device *pdev) } } else { rc = qpnp_pon_set_dbc(pon, delay); + if (rc) { + dev_err(&pdev->dev, + "Unable to set PON debounce delay rc=%d\n", rc); + return rc; + } + } + rc = qpnp_pon_get_dbc(pon, &pon->dbc_time_us); + if (rc) { + dev_err(&pdev->dev, + "Unable to get PON debounce delay rc=%d\n", rc); + return rc; } + pon->kpdpwr_dbc_enable = of_property_read_bool(pon->pdev->dev.of_node, + "qcom,kpdpwr-sw-debounce"); + rc = of_property_read_u32(pon->pdev->dev.of_node, "qcom,warm-reset-poweroff-type", &pon->warm_reset_poff_type); diff --git a/drivers/input/misc/yealink.c b/drivers/input/misc/yealink.c index 79c964c075f1..6e7ff9561d92 100644 --- a/drivers/input/misc/yealink.c +++ b/drivers/input/misc/yealink.c @@ -875,6 +875,10 @@ static int usb_probe(struct usb_interface *intf, const struct usb_device_id *id) int ret, pipe, i; interface = intf->cur_altsetting; + + if (interface->desc.bNumEndpoints < 1) + return -ENODEV; + endpoint = &interface->endpoint[0].desc; if (!usb_endpoint_is_int_in(endpoint)) return -ENODEV; diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index ed1935f300a7..da5458dfb1e3 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -218,17 +218,19 @@ static int elan_query_product(struct elan_tp_data *data) static int elan_check_ASUS_special_fw(struct elan_tp_data *data) { - if (data->ic_type != 0x0E) - return false; - - switch (data->product_id) { - case 0x05 ... 0x07: - case 0x09: - case 0x13: + if (data->ic_type == 0x0E) { + switch (data->product_id) { + case 0x05 ... 0x07: + case 0x09: + case 0x13: + return true; + } + } else if (data->ic_type == 0x08 && data->product_id == 0x26) { + /* ASUS EeeBook X205TA */ return true; - default: - return false; } + + return false; } static int __elan_initialize(struct elan_tp_data *data) diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 43482ae1e049..1a2b2620421e 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -1122,6 +1122,7 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse, * Asus UX32VD 0x361f02 00, 15, 0e clickpad * Avatar AVIU-145A2 0x361f00 ? clickpad * Fujitsu LIFEBOOK E544 0x470f00 d0, 12, 09 2 hw buttons + * Fujitsu LIFEBOOK E547 0x470f00 50, 12, 09 2 hw buttons * Fujitsu LIFEBOOK E554 0x570f01 40, 14, 0c 2 hw buttons * Fujitsu T725 0x470f01 05, 12, 09 2 hw buttons * Fujitsu H730 0x570f00 c0, 14, 0c 3 hw buttons (**) @@ -1528,6 +1529,13 @@ static const struct dmi_system_id elantech_dmi_force_crc_enabled[] = { }, }, { + /* Fujitsu LIFEBOOK E547 does not work with crc_enabled == 0 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E547"), + }, + }, + { /* Fujitsu LIFEBOOK E554 does not work with crc_enabled == 0 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index 0cdd95801a25..e7b96f1ac2c5 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -120,6 +120,13 @@ static const struct dmi_system_id __initconst i8042_dmi_noloop_table[] = { }, }, { + /* Dell Embedded Box PC 3000 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Embedded Box PC 3000"), + }, + }, + { /* OQO Model 01 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "OQO"), @@ -678,6 +685,13 @@ static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "20046"), }, }, + { + /* Clevo P650RS, 650RP6, Sager NP8152-S, and others */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Notebook"), + DMI_MATCH(DMI_PRODUCT_NAME, "P65xRP"), + }, + }, { } }; diff --git a/drivers/input/tablet/hanwang.c b/drivers/input/tablet/hanwang.c index cd852059b99e..df4bea96d7ed 100644 --- a/drivers/input/tablet/hanwang.c +++ b/drivers/input/tablet/hanwang.c @@ -340,6 +340,9 @@ static int hanwang_probe(struct usb_interface *intf, const struct usb_device_id int error; int i; + if (intf->cur_altsetting->desc.bNumEndpoints < 1) + return -ENODEV; + hanwang = kzalloc(sizeof(struct hanwang), GFP_KERNEL); input_dev = input_allocate_device(); if (!hanwang || !input_dev) { diff --git a/drivers/input/tablet/kbtab.c b/drivers/input/tablet/kbtab.c index d2ac7c2b5b82..2812f9236b7d 100644 --- a/drivers/input/tablet/kbtab.c +++ b/drivers/input/tablet/kbtab.c @@ -122,6 +122,9 @@ static int kbtab_probe(struct usb_interface *intf, const struct usb_device_id *i struct input_dev *input_dev; int error = -ENOMEM; + if (intf->cur_altsetting->desc.bNumEndpoints < 1) + return -ENODEV; + kbtab = kzalloc(sizeof(struct kbtab), GFP_KERNEL); input_dev = input_allocate_device(); if (!kbtab || !input_dev) diff --git a/drivers/input/touchscreen/st/fts.c b/drivers/input/touchscreen/st/fts.c index bbe872001407..78bdd24af28b 100644 --- a/drivers/input/touchscreen/st/fts.c +++ b/drivers/input/touchscreen/st/fts.c @@ -1252,7 +1252,7 @@ static void fts_event_handler(struct work_struct *work) static int cx_crc_check(void) { unsigned char regAdd1[3] = {FTS_CMD_HW_REG_R, ADDR_CRC_BYTE0, ADDR_CRC_BYTE1}; - unsigned char val; + unsigned char val = 0; unsigned char crc_status; unsigned int error; diff --git a/drivers/input/touchscreen/st/fts_lib/ftsFlash.c b/drivers/input/touchscreen/st/fts_lib/ftsFlash.c index 59c73f4c4edb..f3becac79102 100644 --- a/drivers/input/touchscreen/st/fts_lib/ftsFlash.c +++ b/drivers/input/touchscreen/st/fts_lib/ftsFlash.c @@ -93,7 +93,7 @@ int getFirmwareVersion(u16 *fw_vers, u16 *config_id) int flash_status(void) { u8 cmd[2] = {FLASH_CMD_READSTATUS, 0x00}; - u8 readData; + u8 readData = 0; logError(0, "%s Reading flash_status...\n", tag); if (fts_readCmd(cmd, 2, &readData, FLASH_STATUS_BYTES) < 0) { diff --git a/drivers/input/touchscreen/st/fts_lib/ftsGesture.c b/drivers/input/touchscreen/st/fts_lib/ftsGesture.c index fda4ab281948..ee97a417d4cb 100644 --- a/drivers/input/touchscreen/st/fts_lib/ftsGesture.c +++ b/drivers/input/touchscreen/st/fts_lib/ftsGesture.c @@ -28,7 +28,7 @@ static u8 custom_gesture_index[GESTURE_CUSTOM_NUMBER] = { 0 }; int enableGesture(u8 *mask, int size) { u8 cmd[size+2]; - u8 readData[FIFO_EVENT_SIZE]; + u8 readData[FIFO_EVENT_SIZE] = {0}; int i, res; int event_to_search[4] = {EVENTID_GESTURE, EVENT_TYPE_ENB, 0x00, GESTURE_ENABLE}; @@ -82,7 +82,7 @@ int enableGesture(u8 *mask, int size) int disableGesture(u8 *mask, int size) { u8 cmd[2+GESTURE_MASK_SIZE]; - u8 readData[FIFO_EVENT_SIZE]; + u8 readData[FIFO_EVENT_SIZE] = {0}; u8 temp; int i, res; int event_to_search[4] = { EVENTID_GESTURE, EVENT_TYPE_ENB, 0x00, GESTURE_DISABLE }; @@ -141,7 +141,7 @@ int startAddCustomGesture(u8 gestureID) { u8 cmd[3] = { FTS_CMD_GESTURE_CMD, GESTURE_START_ADD, gestureID }; int res; - u8 readData[FIFO_EVENT_SIZE]; + u8 readData[FIFO_EVENT_SIZE] = {0}; int event_to_search[4] = { EVENTID_GESTURE, EVENT_TYPE_ENB, gestureID, GESTURE_START_ADD }; res = fts_writeFwCmd(cmd, 3); @@ -168,7 +168,7 @@ int finishAddCustomGesture(u8 gestureID) { u8 cmd[3] = { FTS_CMD_GESTURE_CMD, GESTURE_FINISH_ADD, gestureID }; int res; - u8 readData[FIFO_EVENT_SIZE]; + u8 readData[FIFO_EVENT_SIZE] = {0}; int event_to_search[4] = { EVENTID_GESTURE, EVENT_TYPE_ENB, gestureID, GESTURE_FINISH_ADD }; res = fts_writeFwCmd(cmd, 3); @@ -199,7 +199,7 @@ int loadCustomGesture(u8 *template, u8 gestureID) int toWrite, offset = 0; u8 cmd[TEMPLATE_CHUNK + 5]; int event_to_search[4] = { EVENTID_GESTURE, EVENT_TYPE_ENB, gestureID, GESTURE_DATA_ADD }; - u8 readData[FIFO_EVENT_SIZE]; + u8 readData[FIFO_EVENT_SIZE] = {0}; logError(0, "%s Starting adding custom gesture procedure...\n", tag); @@ -359,7 +359,7 @@ int removeCustomGesture(u8 gestureID) int res, index; u8 cmd[3] = { FTS_CMD_GESTURE_CMD, GETURE_REMOVE_CUSTOM, gestureID }; int event_to_search[4] = {EVENTID_GESTURE, EVENT_TYPE_ENB, gestureID, GETURE_REMOVE_CUSTOM }; - u8 readData[FIFO_EVENT_SIZE]; + u8 readData[FIFO_EVENT_SIZE] = {0}; index = gestureID - GESTURE_CUSTOM_OFFSET; diff --git a/drivers/input/touchscreen/st/fts_lib/ftsTest.c b/drivers/input/touchscreen/st/fts_lib/ftsTest.c index 68bd04eff316..3810fd02001a 100644 --- a/drivers/input/touchscreen/st/fts_lib/ftsTest.c +++ b/drivers/input/touchscreen/st/fts_lib/ftsTest.c @@ -301,7 +301,7 @@ int production_test_ito(void) { int res = OK; u8 cmd; - u8 readData[FIFO_EVENT_SIZE]; + u8 readData[FIFO_EVENT_SIZE] = {0}; int eventToSearch[2] = {EVENTID_ERROR_EVENT, EVENT_TYPE_ITO}; /* look for ito event */ logError(0, "%s ITO Production test is starting...\n", tag); @@ -347,7 +347,7 @@ int production_test_initialization(void) { int res; u8 cmd; - u8 readData[FIFO_EVENT_SIZE]; + u8 readData[FIFO_EVENT_SIZE] = {0}; int eventToSearch[2] = {EVENTID_STATUS_UPDATE, EVENT_TYPE_FULL_INITIALIZATION}; logError(0, "%s INITIALIZATION Production test is starting...\n", tag); @@ -397,7 +397,7 @@ int ms_compensation_tuning(void) { int res; u8 cmd; - u8 readData[FIFO_EVENT_SIZE]; + u8 readData[FIFO_EVENT_SIZE] = {0}; int eventToSearch[2] = {EVENTID_STATUS_UPDATE, EVENT_TYPE_MS_TUNING_CMPL}; logError(0, "%s MS INITIALIZATION command sent...\n", tag); @@ -429,7 +429,7 @@ int ss_compensation_tuning(void) { int res; u8 cmd; - u8 readData[FIFO_EVENT_SIZE]; + u8 readData[FIFO_EVENT_SIZE] = {0}; int eventToSearch[2] = {EVENTID_STATUS_UPDATE, EVENT_TYPE_SS_TUNING_CMPL}; logError(0, "%s SS INITIALIZATION command sent...\n", tag); @@ -461,7 +461,7 @@ int lp_timer_calibration(void) { int res; u8 cmd; - u8 readData[FIFO_EVENT_SIZE]; + u8 readData[FIFO_EVENT_SIZE] = {0}; int eventToSearch[2] = {EVENTID_STATUS_UPDATE, EVENT_TYPE_LPTIMER_TUNING_CMPL}; logError(0, "%s LP TIMER CALIBRATION command sent...\n", tag); @@ -493,7 +493,7 @@ int save_cx_tuning(void) { int res; u8 cmd; - u8 readData[FIFO_EVENT_SIZE]; + u8 readData[FIFO_EVENT_SIZE] = {0}; int eventToSearch[2] = {EVENTID_STATUS_UPDATE, EVENT_TYPE_COMP_DATA_SAVED}; logError(0, "%s SAVE CX command sent...\n", tag); diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index 45b466e3bbe8..0146e2c74649 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -500,6 +500,9 @@ static int sur40_probe(struct usb_interface *interface, if (iface_desc->desc.bInterfaceClass != 0xFF) return -ENODEV; + if (iface_desc->desc.bNumEndpoints < 5) + return -ENODEV; + /* Use endpoint #4 (0x86). */ endpoint = &iface_desc->endpoint[4].desc; if (endpoint->bEndpointAddress != TOUCH_ENDPOINT) diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c index 9d61eb110e2f..c1cbec81d7d6 100644 --- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c +++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c @@ -355,18 +355,25 @@ static ssize_t rmidev_read(struct file *filp, char __user *buf, return -EBADF; } - if (count == 0) - return 0; + mutex_lock(&(dev_data->file_mutex)); if (count > (REG_ADDR_LIMIT - *f_pos)) count = REG_ADDR_LIMIT - *f_pos; - tmpbuf = kzalloc(count + 1, GFP_KERNEL); - if (!tmpbuf) - return -ENOMEM; - - mutex_lock(&(dev_data->file_mutex)); + if (count == 0) { + retval = 0; + goto unlock; + } + if (*f_pos > REG_ADDR_LIMIT) { + retval = -EFAULT; + goto unlock; + } + tmpbuf = kzalloc(count + 1, GFP_KERNEL); + if (!tmpbuf) { + retval = -ENOMEM; + goto unlock; + } retval = synaptics_rmi4_reg_read(rmidev->rmi4_data, *f_pos, tmpbuf, @@ -380,8 +387,9 @@ static ssize_t rmidev_read(struct file *filp, char __user *buf, *f_pos += retval; clean_up: - mutex_unlock(&(dev_data->file_mutex)); kfree(tmpbuf); +unlock: + mutex_unlock(&(dev_data->file_mutex)); return retval; } @@ -405,21 +413,31 @@ static ssize_t rmidev_write(struct file *filp, const char __user *buf, return -EBADF; } - if (count == 0) - return 0; + mutex_lock(&(dev_data->file_mutex)); + + if (*f_pos > REG_ADDR_LIMIT) { + retval = -EFAULT; + goto unlock; + } if (count > (REG_ADDR_LIMIT - *f_pos)) count = REG_ADDR_LIMIT - *f_pos; + if (count == 0) { + retval = 0; + goto unlock; + } + tmpbuf = kzalloc(count + 1, GFP_KERNEL); - if (!tmpbuf) - return -ENOMEM; + if (!tmpbuf) { + retval = -ENOMEM; + goto unlock; + } if (copy_from_user(tmpbuf, buf, count)) { - kfree(tmpbuf); - return -EFAULT; + retval = -EFAULT; + goto clean_up; } - mutex_lock(&(dev_data->file_mutex)); retval = synaptics_rmi4_reg_write(rmidev->rmi4_data, *f_pos, @@ -428,8 +446,10 @@ static ssize_t rmidev_write(struct file *filp, const char __user *buf, if (retval >= 0) *f_pos += retval; - mutex_unlock(&(dev_data->file_mutex)); +clean_up: kfree(tmpbuf); +unlock: + mutex_unlock(&(dev_data->file_mutex)); return retval; } diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index f64111886584..7a504b1ad94d 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -41,7 +41,7 @@ config IOMMU_IO_PGTABLE_LPAE_SELFTEST config IOMMU_IO_PGTABLE_FAST bool "Fast ARMv7/v8 Long Descriptor Format" - select IOMMU_IO_PGTABLE + depends on ARM64_DMA_USE_IOMMU help Enable support for a subset of the ARM long descriptor pagetable format. This allocator achieves fast performance by diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 7922608287d7..dc44b40a85f3 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -522,6 +522,8 @@ static bool arm_smmu_is_slave_side_secure(struct arm_smmu_domain *smmu_domain); static bool arm_smmu_has_secure_vmid(struct arm_smmu_domain *smmu_domain); static bool arm_smmu_is_iova_coherent(struct iommu_domain *domain, dma_addr_t iova); +static uint64_t arm_smmu_iova_to_pte(struct iommu_domain *domain, + dma_addr_t iova); static int arm_smmu_enable_s1_translations(struct arm_smmu_domain *smmu_domain); @@ -2190,8 +2192,8 @@ static int arm_smmu_attach_dynamic(struct iommu_domain *domain, smmu->num_context_banks + 2, MAX_ASID + 1, GFP_KERNEL); if (ret < 0) { - dev_err(smmu->dev, "dynamic ASID allocation failed: %d\n", - ret); + dev_err_ratelimited(smmu->dev, + "dynamic ASID allocation failed: %d\n", ret); goto out; } @@ -2536,6 +2538,23 @@ static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova, return ret; } +static uint64_t arm_smmu_iova_to_pte(struct iommu_domain *domain, + dma_addr_t iova) +{ + uint64_t ret; + unsigned long flags; + struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); + struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops; + + if (!ops) + return 0; + + flags = arm_smmu_pgtbl_lock(smmu_domain); + ret = ops->iova_to_pte(ops, iova); + arm_smmu_pgtbl_unlock(smmu_domain, flags); + return ret; +} + static size_t arm_smmu_map_sg(struct iommu_domain *domain, unsigned long iova, struct scatterlist *sg, unsigned int nents, int prot) { @@ -3437,6 +3456,7 @@ static struct iommu_ops arm_smmu_ops = { .enable_config_clocks = arm_smmu_enable_config_clocks, .disable_config_clocks = arm_smmu_disable_config_clocks, .is_iova_coherent = arm_smmu_is_iova_coherent, + .iova_to_pte = arm_smmu_iova_to_pte, }; static void arm_smmu_device_reset(struct arm_smmu_device *smmu) diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index f0fc6f7b5d98..0628372f3591 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -908,7 +908,7 @@ static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devf * which we used for the IOMMU lookup. Strictly speaking * we could do this for all PCI devices; we only need to * get the BDF# from the scope table for ACPI matches. */ - if (pdev->is_virtfn) + if (pdev && pdev->is_virtfn) goto got_pdev; *bus = drhd->devices[i].bus; diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c index 6a8a9492c771..2d2583c78bdb 100644 --- a/drivers/iommu/io-pgtable-arm.c +++ b/drivers/iommu/io-pgtable-arm.c @@ -903,6 +903,19 @@ found_translation: return 0; } +static uint64_t arm_lpae_iova_get_pte(struct io_pgtable_ops *ops, + unsigned long iova) +{ + struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops); + arm_lpae_iopte pte; + int lvl; + + if (!arm_lpae_iova_to_pte(data, iova, &lvl, &pte)) + return pte; + + return 0; +} + static phys_addr_t arm_lpae_iova_to_phys(struct io_pgtable_ops *ops, unsigned long iova) { @@ -1033,6 +1046,7 @@ arm_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg) .unmap = arm_lpae_unmap, .iova_to_phys = arm_lpae_iova_to_phys, .is_iova_coherent = arm_lpae_is_iova_coherent, + .iova_to_pte = arm_lpae_iova_get_pte, }; return data; diff --git a/drivers/iommu/io-pgtable.h b/drivers/iommu/io-pgtable.h index e6939c2212d4..2cf213514221 100644 --- a/drivers/iommu/io-pgtable.h +++ b/drivers/iommu/io-pgtable.h @@ -124,6 +124,8 @@ struct io_pgtable_ops { unsigned long iova); bool (*is_iova_coherent)(struct io_pgtable_ops *ops, unsigned long iova); + uint64_t (*iova_to_pte)(struct io_pgtable_ops *ops, + unsigned long iova); }; diff --git a/drivers/iommu/iommu-debug.c b/drivers/iommu/iommu-debug.c index ffeb47c6b367..776e06facc11 100644 --- a/drivers/iommu/iommu-debug.c +++ b/drivers/iommu/iommu-debug.c @@ -470,6 +470,7 @@ static inline void iommu_debug_destroy_tracking(void) { } static LIST_HEAD(iommu_debug_devices); static struct dentry *debugfs_tests_dir; static u32 iters_per_op = 1; +static void *virt_addr; struct iommu_debug_device { struct device *dev; @@ -1537,6 +1538,68 @@ out_domain_free: return -EIO; } +static ssize_t __iommu_debug_dma_attach_write(struct file *file, + const char __user *ubuf, + size_t count, loff_t *offset) +{ + struct iommu_debug_device *ddev = file->private_data; + struct device *dev = ddev->dev; + struct dma_iommu_mapping *dma_mapping; + ssize_t retval = -EINVAL; + int val; + + if (kstrtoint_from_user(ubuf, count, 0, &val)) { + pr_err("Invalid format. Expected a hex or decimal integer"); + retval = -EFAULT; + goto out; + } + + if (val) { + if (dev->archdata.mapping) + if (dev->archdata.mapping->domain) { + pr_err("Already attached.\n"); + retval = -EINVAL; + goto out; + } + if (WARN(dev->archdata.iommu, + "Attachment tracking out of sync with device\n")) { + retval = -EINVAL; + goto out; + } + + dma_mapping = arm_iommu_create_mapping(&platform_bus_type, 0, + (SZ_1G * 4ULL)); + + if (!dma_mapping) + goto out; + + if (arm_iommu_attach_device(dev, dma_mapping)) + goto out_release_mapping; + pr_err("Attached\n"); + } else { + if (!dev->archdata.mapping) { + pr_err("No mapping. Did you already attach?\n"); + retval = -EINVAL; + goto out; + } + if (!dev->archdata.mapping->domain) { + pr_err("No domain. Did you already attach?\n"); + retval = -EINVAL; + goto out; + } + arm_iommu_detach_device(dev); + arm_iommu_release_mapping(dev->archdata.mapping); + pr_err("Detached\n"); + } + retval = count; + return retval; + +out_release_mapping: + arm_iommu_release_mapping(dma_mapping); +out: + return retval; +} + static ssize_t __iommu_debug_attach_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset, @@ -1585,6 +1648,79 @@ out: return retval; } +static ssize_t iommu_debug_dma_attach_write(struct file *file, + const char __user *ubuf, + size_t count, loff_t *offset) +{ + return __iommu_debug_dma_attach_write(file, ubuf, count, offset); + +} + +static ssize_t iommu_debug_dma_attach_read(struct file *file, char __user *ubuf, + size_t count, loff_t *offset) +{ + struct iommu_debug_device *ddev = file->private_data; + struct device *dev = ddev->dev; + char c[2]; + + if (*offset) + return 0; + + if (!dev->archdata.mapping) + c[0] = '0'; + else + c[0] = dev->archdata.mapping->domain ? '1' : '0'; + + c[1] = '\n'; + if (copy_to_user(ubuf, &c, 2)) { + pr_err("copy_to_user failed\n"); + return -EFAULT; + } + *offset = 1; /* non-zero means we're done */ + + return 2; +} + +static const struct file_operations iommu_debug_dma_attach_fops = { + .open = simple_open, + .write = iommu_debug_dma_attach_write, + .read = iommu_debug_dma_attach_read, +}; + +static ssize_t iommu_debug_virt_addr_read(struct file *file, char __user *ubuf, + size_t count, loff_t *offset) +{ + char buf[100]; + ssize_t retval; + size_t buflen; + + if (*offset) + return 0; + + memset(buf, 0, 100); + + if (!virt_addr) + strlcpy(buf, "FAIL\n", 100); + else + snprintf(buf, 100, "0x%pK\n", virt_addr); + + buflen = strlen(buf); + if (copy_to_user(ubuf, buf, buflen)) { + pr_err("Couldn't copy_to_user\n"); + retval = -EFAULT; + } else { + *offset = 1; /* non-zero means we're done */ + retval = buflen; + } + + return retval; +} + +static const struct file_operations iommu_debug_virt_addr_fops = { + .open = simple_open, + .read = iommu_debug_virt_addr_read, +}; + static ssize_t iommu_debug_attach_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset) @@ -1635,6 +1771,75 @@ static const struct file_operations iommu_debug_secure_attach_fops = { .read = iommu_debug_attach_read, }; +static ssize_t iommu_debug_pte_write(struct file *file, + const char __user *ubuf, + size_t count, loff_t *offset) +{ + struct iommu_debug_device *ddev = file->private_data; + dma_addr_t iova; + + if (kstrtox_from_user(ubuf, count, 0, &iova)) { + pr_err("Invalid format for iova\n"); + ddev->iova = 0; + return -EINVAL; + } + + ddev->iova = iova; + pr_err("Saved iova=%pa for future PTE commands\n", &iova); + return count; +} + + +static ssize_t iommu_debug_pte_read(struct file *file, char __user *ubuf, + size_t count, loff_t *offset) +{ + struct iommu_debug_device *ddev = file->private_data; + struct device *dev = ddev->dev; + uint64_t pte; + char buf[100]; + ssize_t retval; + size_t buflen; + + if (!dev->archdata.mapping) { + pr_err("No mapping. Did you already attach?\n"); + return -EINVAL; + } + if (!dev->archdata.mapping->domain) { + pr_err("No domain. Did you already attach?\n"); + return -EINVAL; + } + + if (*offset) + return 0; + + memset(buf, 0, 100); + + pte = iommu_iova_to_pte(dev->archdata.mapping->domain, + ddev->iova); + + if (!pte) + strlcpy(buf, "FAIL\n", 100); + else + snprintf(buf, 100, "pte=%016llx\n", pte); + + buflen = strlen(buf); + if (copy_to_user(ubuf, buf, buflen)) { + pr_err("Couldn't copy_to_user\n"); + retval = -EFAULT; + } else { + *offset = 1; /* non-zero means we're done */ + retval = buflen; + } + + return retval; +} + +static const struct file_operations iommu_debug_pte_fops = { + .open = simple_open, + .write = iommu_debug_pte_write, + .read = iommu_debug_pte_read, +}; + static ssize_t iommu_debug_atos_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset) @@ -1673,10 +1878,14 @@ static ssize_t iommu_debug_atos_read(struct file *file, char __user *ubuf, memset(buf, 0, 100); phys = iommu_iova_to_phys_hard(ddev->domain, ddev->iova); - if (!phys) + if (!phys) { strlcpy(buf, "FAIL\n", 100); - else + phys = iommu_iova_to_phys(ddev->domain, ddev->iova); + dev_err(ddev->dev, "ATOS for %pa failed. Software walk returned: %pa\n", + &ddev->iova, &phys); + } else { snprintf(buf, 100, "%pa\n", &phys); + } buflen = strlen(buf); if (copy_to_user(ubuf, buf, buflen)) { @@ -1696,6 +1905,55 @@ static const struct file_operations iommu_debug_atos_fops = { .read = iommu_debug_atos_read, }; +static ssize_t iommu_debug_dma_atos_read(struct file *file, char __user *ubuf, + size_t count, loff_t *offset) +{ + struct iommu_debug_device *ddev = file->private_data; + struct device *dev = ddev->dev; + phys_addr_t phys; + char buf[100]; + ssize_t retval; + size_t buflen; + + if (!dev->archdata.mapping) { + pr_err("No mapping. Did you already attach?\n"); + return -EINVAL; + } + if (!dev->archdata.mapping->domain) { + pr_err("No domain. Did you already attach?\n"); + return -EINVAL; + } + + if (*offset) + return 0; + + memset(buf, 0, 100); + + phys = iommu_iova_to_phys_hard(dev->archdata.mapping->domain, + ddev->iova); + if (!phys) + strlcpy(buf, "FAIL\n", 100); + else + snprintf(buf, 100, "%pa\n", &phys); + + buflen = strlen(buf); + if (copy_to_user(ubuf, buf, buflen)) { + pr_err("Couldn't copy_to_user\n"); + retval = -EFAULT; + } else { + *offset = 1; /* non-zero means we're done */ + retval = buflen; + } + + return retval; +} + +static const struct file_operations iommu_debug_dma_atos_fops = { + .open = simple_open, + .write = iommu_debug_atos_write, + .read = iommu_debug_dma_atos_read, +}; + static ssize_t iommu_debug_map_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset) { @@ -1776,6 +2034,152 @@ static const struct file_operations iommu_debug_map_fops = { .write = iommu_debug_map_write, }; +static ssize_t iommu_debug_dma_map_write(struct file *file, + const char __user *ubuf, size_t count, loff_t *offset) +{ + ssize_t retval = -EINVAL; + int ret; + char *comma1, *comma2; + char buf[100]; + unsigned long addr; + void *v_addr; + dma_addr_t iova; + size_t size; + unsigned int attr; + struct dma_attrs coherent_attr; + struct dma_attrs *dma_attrs = &coherent_attr; + struct iommu_debug_device *ddev = file->private_data; + struct device *dev = ddev->dev; + + init_dma_attrs(dma_attrs); + + if (count >= 100) { + pr_err("Value too large\n"); + return -EINVAL; + } + + if (!dev->archdata.mapping) { + pr_err("No mapping. Did you already attach?\n"); + retval = -EINVAL; + goto out; + } + if (!dev->archdata.mapping->domain) { + pr_err("No domain. Did you already attach?\n"); + retval = -EINVAL; + goto out; + } + + memset(buf, 0, 100); + + if (copy_from_user(buf, ubuf, count)) { + pr_err("Couldn't copy from user\n"); + retval = -EFAULT; + goto out; + } + + comma1 = strnchr(buf, count, ','); + if (!comma1) + goto invalid_format; + + comma2 = strnchr(comma1 + 1, count, ','); + if (!comma2) + goto invalid_format; + + *comma1 = *comma2 = '\0'; + + if (kstrtoul(buf, 0, &addr)) + goto invalid_format; + v_addr = (void *)addr; + + if (kstrtosize_t(comma1 + 1, 0, &size)) + goto invalid_format; + + if (kstrtouint(comma2 + 1, 0, &attr)) + goto invalid_format; + + if (v_addr < virt_addr || v_addr > (virt_addr + SZ_1M - 1)) + goto invalid_addr; + + if (attr == 0) + dma_attrs = NULL; + else if (attr == 1) + dma_set_attr(DMA_ATTR_FORCE_COHERENT, dma_attrs); + else if (attr == 2) + dma_set_attr(DMA_ATTR_FORCE_NON_COHERENT, dma_attrs); + else + goto invalid_format; + + iova = dma_map_single_attrs(dev, v_addr, size, + DMA_TO_DEVICE, dma_attrs); + + if (dma_mapping_error(dev, iova)) { + pr_err("Failed to perform dma_map_single\n"); + ret = -EINVAL; + goto out; + } + + retval = count; + pr_err("Mapped 0x%p to %pa (len=0x%zx)\n", + v_addr, &iova, size); + ddev->iova = iova; + pr_err("Saved iova=%pa for future PTE commands\n", &iova); +out: + return retval; + +invalid_format: + pr_err("Invalid format. Expected: addr,len,dma attr where 'dma attr' is\n0: normal mapping\n1: force coherent\n2: force non-cohernet\n"); + return retval; + +invalid_addr: + pr_err("Invalid addr given! Address should be within 1MB size from start addr returned by doing 'cat virt_addr'.\n"); + return retval; +} + +static ssize_t iommu_debug_dma_map_read(struct file *file, char __user *ubuf, + size_t count, loff_t *offset) +{ + struct iommu_debug_device *ddev = file->private_data; + struct device *dev = ddev->dev; + char buf[100]; + ssize_t retval; + size_t buflen; + dma_addr_t iova; + + if (!dev->archdata.mapping) { + pr_err("No mapping. Did you already attach?\n"); + return -EINVAL; + } + if (!dev->archdata.mapping->domain) { + pr_err("No domain. Did you already attach?\n"); + return -EINVAL; + } + + if (*offset) + return 0; + + memset(buf, 0, 100); + + iova = ddev->iova; + snprintf(buf, 100, "%pa\n", &iova); + + buflen = strlen(buf); + if (copy_to_user(ubuf, buf, buflen)) { + pr_err("Couldn't copy_to_user\n"); + retval = -EFAULT; + } else { + *offset = 1; /* non-zero means we're done */ + retval = buflen; + } + + return retval; +} + +static const struct file_operations iommu_debug_dma_map_fops = { + .open = simple_open, + .write = iommu_debug_dma_map_write, + .read = iommu_debug_dma_map_read, +}; + static ssize_t iommu_debug_unmap_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset) @@ -1841,6 +2245,92 @@ static const struct file_operations iommu_debug_unmap_fops = { .write = iommu_debug_unmap_write, }; +static ssize_t iommu_debug_dma_unmap_write(struct file *file, + const char __user *ubuf, + size_t count, loff_t *offset) +{ + ssize_t retval = 0; + char *comma1, *comma2; + char buf[100]; + size_t size; + unsigned int attr; + dma_addr_t iova; + struct dma_attrs coherent_attr; + struct dma_attrs *dma_attrs = &coherent_attr; + struct iommu_debug_device *ddev = file->private_data; + struct device *dev = ddev->dev; + + init_dma_attrs(dma_attrs); + + if (count >= 100) { + pr_err("Value too large\n"); + return -EINVAL; + } + + if (!dev->archdata.mapping) { + pr_err("No mapping. Did you already attach?\n"); + retval = -EINVAL; + goto out; + } + if (!dev->archdata.mapping->domain) { + pr_err("No domain. Did you already attach?\n"); + retval = -EINVAL; + goto out; + } + + memset(buf, 0, 100); + + if (copy_from_user(buf, ubuf, count)) { + pr_err("Couldn't copy from user\n"); + retval = -EFAULT; + goto out; + } + + comma1 = strnchr(buf, count, ','); + if (!comma1) + goto invalid_format; + + comma2 = strnchr(comma1 + 1, count, ','); + if (!comma2) + goto invalid_format; + + *comma1 = *comma2 = '\0'; + + if (kstrtoux(buf, 0, &iova)) + goto invalid_format; + + if (kstrtosize_t(comma1 + 1, 0, &size)) + goto invalid_format; + + if (kstrtouint(comma2 + 1, 0, &attr)) + goto invalid_format; + + if (attr == 0) + dma_attrs = NULL; + else if (attr == 1) + dma_set_attr(DMA_ATTR_FORCE_COHERENT, dma_attrs); + else if (attr == 2) + dma_set_attr(DMA_ATTR_FORCE_NON_COHERENT, dma_attrs); + else + goto invalid_format; + + dma_unmap_single_attrs(dev, iova, size, DMA_TO_DEVICE, dma_attrs); + + retval = count; + pr_err("Unmapped %pa (len=0x%zx)\n", &iova, size); +out: + return retval; + +invalid_format: + pr_err("Invalid format. Expected: iova,len, dma attr\n"); + return retval; +} + +static const struct file_operations iommu_debug_dma_unmap_fops = { + .open = simple_open, + .write = iommu_debug_dma_unmap_write, +}; + static ssize_t iommu_debug_config_clocks_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset) @@ -1919,6 +2409,13 @@ static int snarf_iommu_devices(struct device *dev, const char *name) goto err_rmdir; } + if (!debugfs_create_file("virt_addr", S_IRUSR, dir, ddev, + &iommu_debug_virt_addr_fops)) { + pr_err("Couldn't create iommu/devices/%s/virt_addr debugfs file\n", + name); + goto err_rmdir; + } + if (!debugfs_create_file("profiling", S_IRUSR, dir, ddev, &iommu_debug_profiling_fops)) { pr_err("Couldn't create iommu/devices/%s/profiling debugfs file\n", @@ -1961,6 +2458,13 @@ static int snarf_iommu_devices(struct device *dev, const char *name) goto err_rmdir; } + if (!debugfs_create_file("dma_attach", S_IRUSR, dir, ddev, + &iommu_debug_dma_attach_fops)) { + pr_err("Couldn't create iommu/devices/%s/dma_attach debugfs file\n", + name); + goto err_rmdir; + } + if (!debugfs_create_file("attach", S_IRUSR, dir, ddev, &iommu_debug_attach_fops)) { pr_err("Couldn't create iommu/devices/%s/attach debugfs file\n", @@ -1982,6 +2486,13 @@ static int snarf_iommu_devices(struct device *dev, const char *name) goto err_rmdir; } + if (!debugfs_create_file("dma_atos", S_IWUSR, dir, ddev, + &iommu_debug_dma_atos_fops)) { + pr_err("Couldn't create iommu/devices/%s/dma_atos debugfs file\n", + name); + goto err_rmdir; + } + if (!debugfs_create_file("map", S_IWUSR, dir, ddev, &iommu_debug_map_fops)) { pr_err("Couldn't create iommu/devices/%s/map debugfs file\n", @@ -1989,6 +2500,13 @@ static int snarf_iommu_devices(struct device *dev, const char *name) goto err_rmdir; } + if (!debugfs_create_file("dma_map", S_IWUSR, dir, ddev, + &iommu_debug_dma_map_fops)) { + pr_err("Couldn't create iommu/devices/%s/dma_map debugfs file\n", + name); + goto err_rmdir; + } + if (!debugfs_create_file("unmap", S_IWUSR, dir, ddev, &iommu_debug_unmap_fops)) { pr_err("Couldn't create iommu/devices/%s/unmap debugfs file\n", @@ -1996,6 +2514,20 @@ static int snarf_iommu_devices(struct device *dev, const char *name) goto err_rmdir; } + if (!debugfs_create_file("dma_unmap", S_IWUSR, dir, ddev, + &iommu_debug_dma_unmap_fops)) { + pr_err("Couldn't create iommu/devices/%s/dma_unmap debugfs file\n", + name); + goto err_rmdir; + } + + if (!debugfs_create_file("pte", S_IWUSR, dir, ddev, + &iommu_debug_pte_fops)) { + pr_err("Couldn't create iommu/devices/%s/pte debugfs file\n", + name); + goto err_rmdir; + } + if (!debugfs_create_file("config_clocks", S_IWUSR, dir, ddev, &iommu_debug_config_clocks_fops)) { pr_err("Couldn't create iommu/devices/%s/config_clocks debugfs file\n", @@ -2050,6 +2582,11 @@ static int iommu_debug_init_tests(void) return -ENODEV; } + virt_addr = kzalloc(SZ_1M, GFP_KERNEL); + + if (!virt_addr) + return -ENOMEM; + return iommu_debug_populate_devices(); } diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index a77a45088b9d..b831796b5b7d 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -1336,6 +1336,15 @@ phys_addr_t iommu_iova_to_phys_hard(struct iommu_domain *domain, return domain->ops->iova_to_phys_hard(domain, iova); } +uint64_t iommu_iova_to_pte(struct iommu_domain *domain, + dma_addr_t iova) +{ + if (unlikely(domain->ops->iova_to_pte == NULL)) + return 0; + + return domain->ops->iova_to_pte(domain, iova); +} + bool iommu_is_iova_coherent(struct iommu_domain *domain, dma_addr_t iova) { if (unlikely(domain->ops->is_iova_coherent == NULL)) diff --git a/drivers/irqchip/irq-imx-gpcv2.c b/drivers/irqchip/irq-imx-gpcv2.c index 15af9a9753e5..2d203b422129 100644 --- a/drivers/irqchip/irq-imx-gpcv2.c +++ b/drivers/irqchip/irq-imx-gpcv2.c @@ -230,6 +230,8 @@ static int __init imx_gpcv2_irqchip_init(struct device_node *node, return -ENOMEM; } + raw_spin_lock_init(&cd->rlock); + cd->gpc_base = of_iomap(node, 0); if (!cd->gpc_base) { pr_err("fsl-gpcv2: unable to map gpc registers\n"); diff --git a/drivers/isdn/gigaset/bas-gigaset.c b/drivers/isdn/gigaset/bas-gigaset.c index aecec6d32463..7f1c625b08ec 100644 --- a/drivers/isdn/gigaset/bas-gigaset.c +++ b/drivers/isdn/gigaset/bas-gigaset.c @@ -2317,6 +2317,9 @@ static int gigaset_probe(struct usb_interface *interface, return -ENODEV; } + if (hostif->desc.bNumEndpoints < 1) + return -ENODEV; + dev_info(&udev->dev, "%s: Device matched (Vendor: 0x%x, Product: 0x%x)\n", __func__, le16_to_cpu(udev->descriptor.idVendor), diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index 36ca4e4cbfb7..4c15dee0857b 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -532,16 +532,27 @@ config DM_LOG_WRITES If unsure, say N. -config DM_ANDROID_VERITY - tristate "Android verity target support" +config DM_VERITY_AVB + tristate "Support AVB specific verity error behavior" depends on DM_VERITY + ---help--- + Enables Android Verified Boot platform-specific error + behavior. In particular, it will modify the vbmeta partition + specified on the kernel command-line when non-transient error + occurs (followed by a panic). + + If unsure, say N. + +config DM_ANDROID_VERITY + bool "Android verity target support" + depends on DM_VERITY=y depends on X509_CERTIFICATE_PARSER depends on SYSTEM_TRUSTED_KEYRING depends on PUBLIC_KEY_ALGO_RSA depends on KEYS depends on ASYMMETRIC_KEY_TYPE depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE - depends on MD_LINEAR + depends on MD_LINEAR=y select DM_VERITY_HASH_PREFETCH_MIN_SIZE_128 ---help--- This device-mapper target is virtually a VERITY target. This diff --git a/drivers/md/Makefile b/drivers/md/Makefile index 2b2ba36638cd..41ba86576d04 100644 --- a/drivers/md/Makefile +++ b/drivers/md/Makefile @@ -71,6 +71,6 @@ ifeq ($(CONFIG_DM_VERITY_FEC),y) dm-verity-objs += dm-verity-fec.o endif -ifeq ($(CONFIG_DM_ANDROID_VERITY),y) -dm-verity-objs += dm-android-verity.o +ifeq ($(CONFIG_DM_VERITY_AVB),y) +dm-verity-objs += dm-verity-avb.o endif diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index 13c60bee8af5..7cef735a01a7 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -115,6 +115,12 @@ static inline bool is_userdebug(void) return !strncmp(buildvariant, typeuserdebug, sizeof(typeuserdebug)); } +static inline bool is_unlocked(void) +{ + static const char unlocked[] = "orange"; + + return !strncmp(verifiedbootstate, unlocked, sizeof(unlocked)); +} static int table_extract_mpi_array(struct public_key_signature *pks, const void *data, size_t len) @@ -585,6 +591,8 @@ static int verify_verity_signature(char *key_id, if (IS_ERR(pks)) { DMERR("hashing failed"); + retval = PTR_ERR(pks); + pks = NULL; goto error; } @@ -648,6 +656,28 @@ static int add_as_linear_device(struct dm_target *ti, char *dev) return err; } +static int create_linear_device(struct dm_target *ti, dev_t dev, + char *target_device) +{ + u64 device_size = 0; + int err = find_size(dev, &device_size); + + if (err) { + DMERR("error finding bdev size"); + handle_error(); + return err; + } + + ti->len = device_size; + err = add_as_linear_device(ti, target_device); + if (err) { + handle_error(); + return err; + } + verity_enabled = false; + return 0; +} + /* * Target parameters: * <key id> Key id of the public key in the system keyring. @@ -671,7 +701,6 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) struct fec_ecc_metadata uninitialized_var(ecc); char buf[FEC_ARG_LENGTH], *buf_ptr; unsigned long long tmpll; - u64 uninitialized_var(device_size); if (argc == 1) { /* Use the default keyid */ @@ -699,23 +728,8 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) return -EINVAL; } - if (is_eng()) { - err = find_size(dev, &device_size); - if (err) { - DMERR("error finding bdev size"); - handle_error(); - return err; - } - - ti->len = device_size; - err = add_as_linear_device(ti, target_device); - if (err) { - handle_error(); - return err; - } - verity_enabled = false; - return 0; - } + if (is_eng()) + return create_linear_device(ti, dev, target_device); strreplace(key_id, '#', ' '); @@ -730,6 +744,11 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) err = extract_metadata(dev, &fec, &metadata, &verity_enabled); if (err) { + /* Allow invalid metadata when the device is unlocked */ + if (is_unlocked()) { + DMWARN("Allow invalid metadata when unlocked"); + return create_linear_device(ti, dev, target_device); + } DMERR("Error while extracting metadata"); handle_error(); goto free_metadata; diff --git a/drivers/md/dm-verity-avb.c b/drivers/md/dm-verity-avb.c new file mode 100644 index 000000000000..88487346c4c6 --- /dev/null +++ b/drivers/md/dm-verity-avb.c @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2017 Google. + * + * This file is released under the GPLv2. + * + * Based on drivers/md/dm-verity-chromeos.c + */ + +#include <linux/device-mapper.h> +#include <linux/module.h> +#include <linux/mount.h> + +#define DM_MSG_PREFIX "verity-avb" + +/* Set via module parameter. */ +static char avb_vbmeta_device[64]; + +static void invalidate_vbmeta_endio(struct bio *bio) +{ + complete(bio->bi_private); +} + +static int invalidate_vbmeta_submit(struct bio *bio, + struct block_device *bdev, + int rw, int access_last_sector, + struct page *page) +{ + DECLARE_COMPLETION_ONSTACK(wait); + + bio->bi_private = &wait; + bio->bi_end_io = invalidate_vbmeta_endio; + bio->bi_bdev = bdev; + + bio->bi_iter.bi_sector = 0; + if (access_last_sector) { + sector_t last_sector = (i_size_read(bdev->bd_inode)>>SECTOR_SHIFT) - 1; + bio->bi_iter.bi_sector = last_sector; + } + bio->bi_vcnt = 1; + bio->bi_iter.bi_idx = 0; + bio->bi_iter.bi_size = 512; + bio->bi_iter.bi_bvec_done = 0; + bio->bi_rw = rw; + bio->bi_io_vec[0].bv_page = page; + bio->bi_io_vec[0].bv_len = 512; + bio->bi_io_vec[0].bv_offset = 0; + + submit_bio(rw, bio); + /* Wait up to 2 seconds for completion or fail. */ + if (!wait_for_completion_timeout(&wait, msecs_to_jiffies(2000))) + return -EIO; + return 0; +} + +static int invalidate_vbmeta(dev_t vbmeta_devt) +{ + int ret = 0; + struct block_device *bdev; + struct bio *bio; + struct page *page; + fmode_t dev_mode; + /* Ensure we do synchronous unblocked I/O. We may also need + * sync_bdev() on completion, but it really shouldn't. + */ + int rw = REQ_SYNC | REQ_SOFTBARRIER | REQ_NOIDLE; + int access_last_sector = 0; + + /* First we open the device for reading. */ + dev_mode = FMODE_READ | FMODE_EXCL; + bdev = blkdev_get_by_dev(vbmeta_devt, dev_mode, + invalidate_vbmeta); + if (IS_ERR(bdev)) { + DMERR("invalidate_kernel: could not open device for reading"); + dev_mode = 0; + ret = -ENOENT; + goto failed_to_read; + } + + bio = bio_alloc(GFP_NOIO, 1); + if (!bio) { + ret = -ENOMEM; + goto failed_bio_alloc; + } + + page = alloc_page(GFP_NOIO); + if (!page) { + ret = -ENOMEM; + goto failed_to_alloc_page; + } + + access_last_sector = 0; + ret = invalidate_vbmeta_submit(bio, bdev, rw, access_last_sector, page); + if (ret) { + DMERR("invalidate_vbmeta: error reading"); + goto failed_to_submit_read; + } + + /* We have a page. Let's make sure it looks right. */ + if (memcmp("AVB0", page_address(page), 4) == 0) { + /* Stamp it. */ + memcpy(page_address(page), "AVE0", 4); + DMINFO("invalidate_vbmeta: found vbmeta partition"); + } else { + /* Could be this is on a AVB footer, check. Also, since the + * AVB footer is in the last 64 bytes, adjust for the fact that + * we're dealing with 512-byte sectors. + */ + size_t offset = (1<<SECTOR_SHIFT) - 64; + + access_last_sector = 1; + ret = invalidate_vbmeta_submit(bio, bdev, rw, + access_last_sector, page); + if (ret) { + DMERR("invalidate_vbmeta: error reading"); + goto failed_to_submit_read; + } + if (memcmp("AVBf", page_address(page) + offset, 4) != 0) { + DMERR("invalidate_vbmeta called on non-vbmeta partition"); + ret = -EINVAL; + goto invalid_header; + } + /* Stamp it. */ + memcpy(page_address(page) + offset, "AVE0", 4); + DMINFO("invalidate_vbmeta: found vbmeta footer partition"); + } + + /* Now rewrite the changed page - the block dev was being + * changed on read. Let's reopen here. + */ + blkdev_put(bdev, dev_mode); + dev_mode = FMODE_WRITE | FMODE_EXCL; + bdev = blkdev_get_by_dev(vbmeta_devt, dev_mode, + invalidate_vbmeta); + if (IS_ERR(bdev)) { + DMERR("invalidate_vbmeta: could not open device for writing"); + dev_mode = 0; + ret = -ENOENT; + goto failed_to_write; + } + + /* We re-use the same bio to do the write after the read. Need to reset + * it to initialize bio->bi_remaining. + */ + bio_reset(bio); + + rw |= REQ_WRITE; + ret = invalidate_vbmeta_submit(bio, bdev, rw, access_last_sector, page); + if (ret) { + DMERR("invalidate_vbmeta: error writing"); + goto failed_to_submit_write; + } + + DMERR("invalidate_vbmeta: completed."); + ret = 0; +failed_to_submit_write: +failed_to_write: +invalid_header: + __free_page(page); +failed_to_submit_read: + /* Technically, we'll leak a page with the pending bio, but + * we're about to reboot anyway. + */ +failed_to_alloc_page: + bio_put(bio); +failed_bio_alloc: + if (dev_mode) + blkdev_put(bdev, dev_mode); +failed_to_read: + return ret; +} + +void dm_verity_avb_error_handler(void) +{ + dev_t dev; + + DMINFO("AVB error handler called for %s", avb_vbmeta_device); + + if (avb_vbmeta_device[0] == '\0') { + DMERR("avb_vbmeta_device parameter not set"); + goto fail_no_dev; + } + + dev = name_to_dev_t(avb_vbmeta_device); + if (!dev) { + DMERR("No matching partition for device: %s", + avb_vbmeta_device); + goto fail_no_dev; + } + + invalidate_vbmeta(dev); + +fail_no_dev: + ; +} + +static int __init dm_verity_avb_init(void) +{ + DMINFO("AVB error handler initialized with vbmeta device: %s", + avb_vbmeta_device); + return 0; +} + +static void __exit dm_verity_avb_exit(void) +{ +} + +module_init(dm_verity_avb_init); +module_exit(dm_verity_avb_exit); + +MODULE_AUTHOR("David Zeuthen <zeuthen@google.com>"); +MODULE_DESCRIPTION("AVB-specific error handler for dm-verity"); +MODULE_LICENSE("GPL"); + +/* Declare parameter with no module prefix */ +#undef MODULE_PARAM_PREFIX +#define MODULE_PARAM_PREFIX "androidboot.vbmeta." +module_param_string(device, avb_vbmeta_device, sizeof(avb_vbmeta_device), 0); diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index c7e97cf6e7fb..e34cf53bd068 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -233,8 +233,12 @@ out: if (v->mode == DM_VERITY_MODE_LOGGING) return 0; - if (v->mode == DM_VERITY_MODE_RESTART) + if (v->mode == DM_VERITY_MODE_RESTART) { +#ifdef CONFIG_DM_VERITY_AVB + dm_verity_avb_error_handler(); +#endif kernel_restart("dm-verity device corrupted"); + } return 1; } diff --git a/drivers/md/dm-verity.h b/drivers/md/dm-verity.h index 75effca400a3..a90d1d416107 100644 --- a/drivers/md/dm-verity.h +++ b/drivers/md/dm-verity.h @@ -136,4 +136,5 @@ extern void verity_io_hints(struct dm_target *ti, struct queue_limits *limits); extern void verity_dtr(struct dm_target *ti); extern int verity_ctr(struct dm_target *ti, unsigned argc, char **argv); extern int verity_map(struct dm_target *ti, struct bio *bio); +extern void dm_verity_avb_error_handler(void); #endif /* DM_VERITY_H */ diff --git a/drivers/md/dm.c b/drivers/md/dm.c index e9b34de2319e..5d42d8f09421 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -1481,26 +1481,29 @@ static void flush_current_bio_list(struct blk_plug_cb *cb, bool from_schedule) struct dm_offload *o = container_of(cb, struct dm_offload, cb); struct bio_list list; struct bio *bio; + int i; INIT_LIST_HEAD(&o->cb.list); if (unlikely(!current->bio_list)) return; - list = *current->bio_list; - bio_list_init(current->bio_list); - - while ((bio = bio_list_pop(&list))) { - struct bio_set *bs = bio->bi_pool; - if (unlikely(!bs) || bs == fs_bio_set) { - bio_list_add(current->bio_list, bio); - continue; + for (i = 0; i < 2; i++) { + list = current->bio_list[i]; + bio_list_init(¤t->bio_list[i]); + + while ((bio = bio_list_pop(&list))) { + struct bio_set *bs = bio->bi_pool; + if (unlikely(!bs) || bs == fs_bio_set) { + bio_list_add(¤t->bio_list[i], bio); + continue; + } + + spin_lock(&bs->rescue_lock); + bio_list_add(&bs->rescue_list, bio); + queue_work(bs->rescue_workqueue, &bs->rescue_work); + spin_unlock(&bs->rescue_lock); } - - spin_lock(&bs->rescue_lock); - bio_list_add(&bs->rescue_list, bio); - queue_work(bs->rescue_workqueue, &bs->rescue_work); - spin_unlock(&bs->rescue_lock); } } diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 515554c7365b..d81be5e471d0 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -570,7 +570,7 @@ static int read_balance(struct r1conf *conf, struct r1bio *r1_bio, int *max_sect if (best_dist_disk < 0) { if (is_badblock(rdev, this_sector, sectors, &first_bad, &bad_sectors)) { - if (first_bad < this_sector) + if (first_bad <= this_sector) /* Cannot use this */ continue; best_good_sectors = first_bad - this_sector; @@ -877,7 +877,8 @@ static sector_t wait_barrier(struct r1conf *conf, struct bio *bio) ((conf->start_next_window < conf->next_resync + RESYNC_SECTORS) && current->bio_list && - !bio_list_empty(current->bio_list))), + (!bio_list_empty(¤t->bio_list[0]) || + !bio_list_empty(¤t->bio_list[1])))), conf->resync_lock); conf->nr_waiting--; } diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index ebb0dd612ebd..e5ee4e9e0ea5 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -946,7 +946,8 @@ static void wait_barrier(struct r10conf *conf) !conf->barrier || (conf->nr_pending && current->bio_list && - !bio_list_empty(current->bio_list)), + (!bio_list_empty(¤t->bio_list[0]) || + !bio_list_empty(¤t->bio_list[1]))), conf->resync_lock); conf->nr_waiting--; } @@ -1072,6 +1073,8 @@ static void __make_request(struct mddev *mddev, struct bio *bio) int max_sectors; int sectors; + md_write_start(mddev, bio); + /* * Register the new request and wait if the reconstruction * thread has put up a bar for new requests. @@ -1455,8 +1458,6 @@ static void make_request(struct mddev *mddev, struct bio *bio) return; } - md_write_start(mddev, bio); - do { /* @@ -1477,7 +1478,25 @@ static void make_request(struct mddev *mddev, struct bio *bio) split = bio; } + /* + * If a bio is splitted, the first part of bio will pass + * barrier but the bio is queued in current->bio_list (see + * generic_make_request). If there is a raise_barrier() called + * here, the second part of bio can't pass barrier. But since + * the first part bio isn't dispatched to underlaying disks + * yet, the barrier is never released, hence raise_barrier will + * alays wait. We have a deadlock. + * Note, this only happens in read path. For write path, the + * first part of bio is dispatched in a schedule() call + * (because of blk plug) or offloaded to raid10d. + * Quitting from the function immediately can change the bio + * order queued in bio_list and avoid the deadlock. + */ __make_request(mddev, split); + if (split != bio && bio_data_dir(bio) == READ) { + generic_make_request(bio); + break; + } } while (split != bio); /* In case raid10d snuck in to freeze_array */ diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c index 2a9bb6e8e505..c8f5c051424e 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c @@ -2068,7 +2068,7 @@ static void msm_isp_enqueue_tasklet_cmd(struct vfe_device *vfe_dev, spin_lock_irqsave(&tasklet->tasklet_lock, flags); queue_cmd = &tasklet->tasklet_queue_cmd[tasklet->taskletq_idx]; if (queue_cmd->cmd_used) { - pr_err("%s: Tasklet queue overflow: %d\n", + pr_err_ratelimited("%s: Tasklet queue overflow: %d\n", __func__, vfe_dev->pdev->id); spin_unlock_irqrestore(&tasklet->tasklet_lock, flags); return; diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c index caf6639f5151..24e3223a79d0 100644 --- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c +++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c @@ -67,6 +67,10 @@ static int pix_overflow_error_count[VFE_MAX] = { 0 }; #define CDBG(fmt, args...) #endif +/* Backward interface compatibility for 3D THRESHOLD calculation */ +#define ISPIF_USE_DEFAULT_THRESHOLD (0) +#define ISPIF_CALCULATE_THRESHOLD (1) + static int msm_ispif_clk_ahb_enable(struct ispif_device *ispif, int enable); static int ispif_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh); static long msm_ispif_subdev_ioctl_unlocked(struct v4l2_subdev *sd, @@ -452,7 +456,7 @@ static int msm_ispif_reset_hw(struct ispif_device *ispif) /* This is set when device is 8974 */ ispif->clk_idx = 1; } - + memset(ispif->stereo_configured, 0, sizeof(ispif->stereo_configured)); atomic_set(&ispif->reset_trig[VFE0], 1); /* initiate reset of ISPIF */ msm_camera_io_w(ISPIF_RST_CMD_MASK, @@ -1009,21 +1013,29 @@ static int msm_ispif_config(struct ispif_device *ispif, } static void msm_ispif_config_stereo(struct ispif_device *ispif, - struct msm_ispif_param_data_ext *params) { + struct msm_ispif_param_data_ext *params, int use_line_width) { int i; enum msm_ispif_vfe_intf vfe_intf; + uint32_t stereo_3d_threshold = STEREO_DEFAULT_3D_THRESHOLD; for (i = 0; i < params->num; i++) { + vfe_intf = params->entries[i].vfe_intf; if (params->entries[i].intftype == PIX0 && - params->stereo_enable && - params->right_entries[i].csid < CSID_MAX) { - vfe_intf = params->entries[i].vfe_intf; + params->stereo_enable && + params->right_entries[i].csid < CSID_MAX && + !ispif->stereo_configured[vfe_intf]) { msm_camera_io_w_mb(0x3, ispif->base + ISPIF_VFE_m_OUTPUT_SEL(vfe_intf)); - msm_camera_io_w_mb(STEREO_DEFAULT_3D_THRESHOLD, + if (use_line_width && + (params->line_width[vfe_intf] > 0)) + stereo_3d_threshold = + (params->line_width[vfe_intf] + + 2 * 6 - 1) / (2 * 6); + msm_camera_io_w_mb(stereo_3d_threshold, ispif->base + ISPIF_VFE_m_3D_THRESHOLD(vfe_intf)); + ispif->stereo_configured[vfe_intf] = 1; } } } @@ -1132,6 +1144,8 @@ static int msm_ispif_stop_immediately(struct ispif_device *ispif, msm_ispif_enable_intf_cids(ispif, params->entries[i].intftype, cid_mask, params->entries[i].vfe_intf, 0); if (params->stereo_enable) { + ispif->stereo_configured[ + params->entries[i].vfe_intf] = 0; cid_mask = msm_ispif_get_right_cids_mask_from_cfg( ¶ms->right_entries[i], params->entries[i].num_cids); @@ -1162,7 +1176,8 @@ static int msm_ispif_start_frame_boundary(struct ispif_device *ispif, rc = -EINVAL; return rc; } - msm_ispif_config_stereo(ispif, params); + + msm_ispif_config_stereo(ispif, params, ISPIF_USE_DEFAULT_THRESHOLD); msm_ispif_intf_cmd(ispif, ISPIF_INTF_CMD_ENABLE_FRAME_BOUNDARY, params); return rc; @@ -1392,6 +1407,8 @@ static int msm_ispif_stop_frame_boundary(struct ispif_device *ispif, if (rc < 0) goto end; if (cid_right_mask) { + ispif->stereo_configured[ + params->entries[i].vfe_intf] = 0; intf_addr = ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe_intf, 1); rc = readl_poll_timeout(ispif->base + intf_addr, stop_flag, @@ -1807,6 +1824,10 @@ static long msm_ispif_dispatch_cmd(enum ispif_cfg_type_t cmd, rc = msm_ispif_config2(ispif, params); msm_ispif_io_dump_reg(ispif); break; + case ISPIF_CFG_STEREO: + msm_ispif_config_stereo(ispif, params, + ISPIF_CALCULATE_THRESHOLD); + break; default: pr_err("%s: invalid cfg_type\n", __func__); rc = -EINVAL; diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.h b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.h index 61e8f1dd7aff..3e6680c63ee5 100644 --- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.h +++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, 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 @@ -77,5 +77,6 @@ struct ispif_device { int ispif_vdd_count; struct regulator *vfe_vdd[ISPIF_VFE_VDD_INFO_MAX]; int vfe_vdd_count; + int stereo_configured[VFE_MAX]; }; #endif diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c index c2b42a854d35..f95cc37f5c2c 100644 --- a/drivers/media/platform/msm/camera_v2/msm.c +++ b/drivers/media/platform/msm/camera_v2/msm.c @@ -707,6 +707,9 @@ static long msm_private_ioctl(struct file *file, void *fh, return 0; } + if (!event_data) + return -EINVAL; + memset(&event, 0, sizeof(struct v4l2_event)); session_id = event_data->session_id; stream_id = event_data->stream_id; @@ -1012,11 +1015,9 @@ static int msm_open(struct file *filep) BUG_ON(!pvdev); /* !!! only ONE open is allowed !!! */ - if (atomic_read(&pvdev->opened)) + if (atomic_cmpxchg(&pvdev->opened, 0, 1)) return -EBUSY; - atomic_set(&pvdev->opened, 1); - spin_lock_irqsave(&msm_pid_lock, flags); msm_pid = get_pid(task_pid(current)); spin_unlock_irqrestore(&msm_pid_lock, flags); diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c index 8402e31364b9..b7feb126f707 100644 --- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c +++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c @@ -2542,9 +2542,29 @@ static int msm_cpp_cfg_frame(struct cpp_device *cpp_dev, return -EINVAL; } - if (stripe_base == UINT_MAX || new_frame->num_strips > - (UINT_MAX - 1 - stripe_base) / stripe_size) { - pr_err("Invalid frame message,num_strips %d is large\n", + /* Stripe index starts at zero */ + if ((!new_frame->num_strips) || + (new_frame->first_stripe_index >= new_frame->num_strips) || + (new_frame->last_stripe_index >= new_frame->num_strips) || + (new_frame->first_stripe_index > + new_frame->last_stripe_index)) { + pr_err("Invalid frame message, #stripes=%d, stripe indices=[%d,%d]\n", + new_frame->num_strips, + new_frame->first_stripe_index, + new_frame->last_stripe_index); + return -EINVAL; + } + + if (!stripe_size) { + pr_err("Invalid frame message, invalid stripe_size (%d)!\n", + stripe_size); + return -EINVAL; + } + + if ((stripe_base == UINT_MAX) || + (new_frame->num_strips > + (UINT_MAX - 1 - stripe_base) / stripe_size)) { + pr_err("Invalid frame message, num_strips %d is large\n", new_frame->num_strips); return -EINVAL; } @@ -2785,13 +2805,14 @@ static int msm_cpp_cfg(struct cpp_device *cpp_dev, struct msm_cpp_frame_info_t *frame = NULL; struct msm_cpp_frame_info_t k_frame_info; int32_t rc = 0; - int32_t i = 0; - int32_t num_buff = sizeof(k_frame_info.output_buffer_info)/ + uint32_t i = 0; + uint32_t num_buff = sizeof(k_frame_info.output_buffer_info) / sizeof(struct msm_cpp_buffer_info_t); + if (copy_from_user(&k_frame_info, (void __user *)ioctl_ptr->ioctl_ptr, sizeof(k_frame_info))) - return -EFAULT; + return -EFAULT; frame = msm_cpp_get_frame(ioctl_ptr); if (!frame) { @@ -2953,8 +2974,9 @@ static int msm_cpp_validate_input(unsigned int cmd, void *arg, } *ioctl_ptr = arg; - if ((*ioctl_ptr == NULL) || - ((*ioctl_ptr)->ioctl_ptr == NULL)) { + if (((*ioctl_ptr) == NULL) || + ((*ioctl_ptr)->ioctl_ptr == NULL) || + ((*ioctl_ptr)->len == 0)) { pr_err("Error invalid ioctl argument cmd %u", cmd); return -EINVAL; } @@ -3503,13 +3525,18 @@ STREAM_BUFF_END: if (cpp_dev->iommu_state == CPP_IOMMU_STATE_DETACHED) { struct msm_camera_smmu_attach_type cpp_attach_info; + if (ioctl_ptr->len != + sizeof(struct msm_camera_smmu_attach_type)) { + rc = -EINVAL; + break; + } + memset(&cpp_attach_info, 0, sizeof(cpp_attach_info)); rc = msm_cpp_copy_from_ioctl_ptr(&cpp_attach_info, ioctl_ptr); if (rc < 0) { pr_err("CPP_IOMMU_ATTACH copy from user fail"); - ERR_COPY_FROM_USER(); - return -EINVAL; + break; } cpp_dev->security_mode = cpp_attach_info.attach; @@ -3538,16 +3565,20 @@ STREAM_BUFF_END: case VIDIOC_MSM_CPP_IOMMU_DETACH: { if ((cpp_dev->iommu_state == CPP_IOMMU_STATE_ATTACHED) && (cpp_dev->stream_cnt == 0)) { - struct msm_camera_smmu_attach_type cpp_attach_info; + if (ioctl_ptr->len != + sizeof(struct msm_camera_smmu_attach_type)) { + rc = -EINVAL; + break; + } + memset(&cpp_attach_info, 0, sizeof(cpp_attach_info)); rc = msm_cpp_copy_from_ioctl_ptr(&cpp_attach_info, ioctl_ptr); if (rc < 0) { pr_err("CPP_IOMMU_DETTACH copy from user fail"); - ERR_COPY_FROM_USER(); - return -EINVAL; + break; } cpp_dev->security_mode = cpp_attach_info.attach; @@ -3568,6 +3599,7 @@ STREAM_BUFF_END: } else { pr_err("%s:%d IOMMMU attach triggered in invalid state\n", __func__, __LINE__); + rc = -EINVAL; } break; } @@ -3883,6 +3915,7 @@ static long msm_cpp_subdev_fops_compat_ioctl(struct file *file, struct msm_cpp_stream_buff_info_t k_cpp_buff_info; struct msm_cpp_frame_info32_t k32_frame_info; struct msm_cpp_frame_info_t k64_frame_info; + struct msm_camera_smmu_attach_type kb_cpp_smmu_attach_info; uint32_t identity_k = 0; bool is_copytouser_req = true; void __user *up = (void __user *)arg; @@ -4187,11 +4220,23 @@ static long msm_cpp_subdev_fops_compat_ioctl(struct file *file, break; } case VIDIOC_MSM_CPP_IOMMU_ATTACH32: - cmd = VIDIOC_MSM_CPP_IOMMU_ATTACH; - break; case VIDIOC_MSM_CPP_IOMMU_DETACH32: - cmd = VIDIOC_MSM_CPP_IOMMU_DETACH; + { + if ((kp_ioctl.len != sizeof(struct msm_camera_smmu_attach_type)) + || (copy_from_user(&kb_cpp_smmu_attach_info, + (void __user *)kp_ioctl.ioctl_ptr, + sizeof(kb_cpp_smmu_attach_info)))) { + mutex_unlock(&cpp_dev->mutex); + return -EINVAL; + } + + kp_ioctl.ioctl_ptr = (void *)&kb_cpp_smmu_attach_info; + is_copytouser_req = false; + cmd = (cmd == VIDIOC_MSM_CPP_IOMMU_ATTACH32) ? + VIDIOC_MSM_CPP_IOMMU_ATTACH : + VIDIOC_MSM_CPP_IOMMU_DETACH; break; + } case MSM_SD_NOTIFY_FREEZE: break; case MSM_SD_UNNOTIFY_FREEZE: @@ -4202,7 +4247,8 @@ static long msm_cpp_subdev_fops_compat_ioctl(struct file *file, default: pr_err_ratelimited("%s: unsupported compat type :%x LOAD %lu\n", __func__, cmd, VIDIOC_MSM_CPP_LOAD_FIRMWARE); - break; + mutex_unlock(&cpp_dev->mutex); + return -EINVAL; } mutex_unlock(&cpp_dev->mutex); @@ -4233,7 +4279,7 @@ static long msm_cpp_subdev_fops_compat_ioctl(struct file *file, default: pr_err_ratelimited("%s: unsupported compat type :%d\n", __func__, cmd); - break; + return -EINVAL; } if (is_copytouser_req) { diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_formats.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_formats.c index 3b36b6bc76de..5eaa2910228e 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_formats.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_formats.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012, 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012, 2015-2017, 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 @@ -507,10 +507,15 @@ struct sde_mdp_format_params *sde_get_format_params(u32 format) if (!fmt_found) { for (i = 0; i < ARRAY_SIZE(sde_mdp_format_ubwc_map); i++) { fmt = &sde_mdp_format_ubwc_map[i].mdp_format; - if (format == fmt->format) + if (format == fmt->format) { + fmt_found = true; break; + } } } + /* If format not supported than return NULL */ + if (!fmt_found) + fmt = NULL; return fmt; } diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r1_ctl.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r1_ctl.c index fef4a8585eaa..74f362839ccc 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r1_ctl.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r1_ctl.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, 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 @@ -133,7 +133,7 @@ struct sde_mdp_mixer *sde_mdp_mixer_get(struct sde_mdp_ctl *ctl, int mux) int sde_mdp_get_pipe_flush_bits(struct sde_mdp_pipe *pipe) { - u32 flush_bits; + u32 flush_bits = 0; if (pipe->type == SDE_MDP_PIPE_TYPE_DMA) flush_bits |= BIT(pipe->num) << 5; diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c index f5df9eaba04f..9757f35cd5f5 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -1010,8 +1010,8 @@ EXPORT_SYMBOL(dvb_usbv2_probe); void dvb_usbv2_disconnect(struct usb_interface *intf) { struct dvb_usb_device *d = usb_get_intfdata(intf); - const char *name = d->name; - struct device dev = d->udev->dev; + const char *devname = kstrdup(dev_name(&d->udev->dev), GFP_KERNEL); + const char *drvname = d->name; dev_dbg(&d->udev->dev, "%s: bInterfaceNumber=%d\n", __func__, intf->cur_altsetting->desc.bInterfaceNumber); @@ -1021,8 +1021,9 @@ void dvb_usbv2_disconnect(struct usb_interface *intf) dvb_usbv2_exit(d); - dev_info(&dev, "%s: '%s' successfully deinitialized and disconnected\n", - KBUILD_MODNAME, name); + pr_info("%s: '%s:%s' successfully deinitialized and disconnected\n", + KBUILD_MODNAME, drvname, devname); + kfree(devname); } EXPORT_SYMBOL(dvb_usbv2_disconnect); diff --git a/drivers/media/usb/dvb-usb/dvb-usb-firmware.c b/drivers/media/usb/dvb-usb/dvb-usb-firmware.c index 733a7ff7b207..caad3b5c01ad 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb-firmware.c +++ b/drivers/media/usb/dvb-usb/dvb-usb-firmware.c @@ -35,42 +35,51 @@ static int usb_cypress_writemem(struct usb_device *udev,u16 addr,u8 *data, u8 le int usb_cypress_load_firmware(struct usb_device *udev, const struct firmware *fw, int type) { - struct hexline hx; - u8 reset; - int ret,pos=0; + struct hexline *hx; + u8 *buf; + int ret, pos = 0; + u16 cpu_cs_register = cypress[type].cpu_cs_register; + + buf = kmalloc(sizeof(*hx), GFP_KERNEL); + if (!buf) + return -ENOMEM; + hx = (struct hexline *)buf; /* stop the CPU */ - reset = 1; - if ((ret = usb_cypress_writemem(udev,cypress[type].cpu_cs_register,&reset,1)) != 1) + buf[0] = 1; + if (usb_cypress_writemem(udev, cpu_cs_register, buf, 1) != 1) err("could not stop the USB controller CPU."); - while ((ret = dvb_usb_get_hexline(fw,&hx,&pos)) > 0) { - deb_fw("writing to address 0x%04x (buffer: 0x%02x %02x)\n",hx.addr,hx.len,hx.chk); - ret = usb_cypress_writemem(udev,hx.addr,hx.data,hx.len); + while ((ret = dvb_usb_get_hexline(fw, hx, &pos)) > 0) { + deb_fw("writing to address 0x%04x (buffer: 0x%02x %02x)\n", hx->addr, hx->len, hx->chk); + ret = usb_cypress_writemem(udev, hx->addr, hx->data, hx->len); - if (ret != hx.len) { + if (ret != hx->len) { err("error while transferring firmware " "(transferred size: %d, block size: %d)", - ret,hx.len); + ret, hx->len); ret = -EINVAL; break; } } if (ret < 0) { err("firmware download failed at %d with %d",pos,ret); + kfree(buf); return ret; } if (ret == 0) { /* restart the CPU */ - reset = 0; - if (ret || usb_cypress_writemem(udev,cypress[type].cpu_cs_register,&reset,1) != 1) { + buf[0] = 0; + if (usb_cypress_writemem(udev, cpu_cs_register, buf, 1) != 1) { err("could not restart the USB controller CPU."); ret = -EINVAL; } } else ret = -EIO; + kfree(buf); + return ret; } EXPORT_SYMBOL(usb_cypress_load_firmware); diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 5cefca95734e..885f689ac870 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -1595,6 +1595,114 @@ static const char *uvc_print_chain(struct uvc_video_chain *chain) return buffer; } +static struct uvc_video_chain *uvc_alloc_chain(struct uvc_device *dev) +{ + struct uvc_video_chain *chain; + + chain = kzalloc(sizeof(*chain), GFP_KERNEL); + if (chain == NULL) + return NULL; + + INIT_LIST_HEAD(&chain->entities); + mutex_init(&chain->ctrl_mutex); + chain->dev = dev; + v4l2_prio_init(&chain->prio); + + return chain; +} + +/* + * Fallback heuristic for devices that don't connect units and terminals in a + * valid chain. + * + * Some devices have invalid baSourceID references, causing uvc_scan_chain() + * to fail, but if we just take the entities we can find and put them together + * in the most sensible chain we can think of, turns out they do work anyway. + * Note: This heuristic assumes there is a single chain. + * + * At the time of writing, devices known to have such a broken chain are + * - Acer Integrated Camera (5986:055a) + * - Realtek rtl157a7 (0bda:57a7) + */ +static int uvc_scan_fallback(struct uvc_device *dev) +{ + struct uvc_video_chain *chain; + struct uvc_entity *iterm = NULL; + struct uvc_entity *oterm = NULL; + struct uvc_entity *entity; + struct uvc_entity *prev; + + /* + * Start by locating the input and output terminals. We only support + * devices with exactly one of each for now. + */ + list_for_each_entry(entity, &dev->entities, list) { + if (UVC_ENTITY_IS_ITERM(entity)) { + if (iterm) + return -EINVAL; + iterm = entity; + } + + if (UVC_ENTITY_IS_OTERM(entity)) { + if (oterm) + return -EINVAL; + oterm = entity; + } + } + + if (iterm == NULL || oterm == NULL) + return -EINVAL; + + /* Allocate the chain and fill it. */ + chain = uvc_alloc_chain(dev); + if (chain == NULL) + return -ENOMEM; + + if (uvc_scan_chain_entity(chain, oterm) < 0) + goto error; + + prev = oterm; + + /* + * Add all Processing and Extension Units with two pads. The order + * doesn't matter much, use reverse list traversal to connect units in + * UVC descriptor order as we build the chain from output to input. This + * leads to units appearing in the order meant by the manufacturer for + * the cameras known to require this heuristic. + */ + list_for_each_entry_reverse(entity, &dev->entities, list) { + if (entity->type != UVC_VC_PROCESSING_UNIT && + entity->type != UVC_VC_EXTENSION_UNIT) + continue; + + if (entity->num_pads != 2) + continue; + + if (uvc_scan_chain_entity(chain, entity) < 0) + goto error; + + prev->baSourceID[0] = entity->id; + prev = entity; + } + + if (uvc_scan_chain_entity(chain, iterm) < 0) + goto error; + + prev->baSourceID[0] = iterm->id; + + list_add_tail(&chain->list, &dev->chains); + + uvc_trace(UVC_TRACE_PROBE, + "Found a video chain by fallback heuristic (%s).\n", + uvc_print_chain(chain)); + + return 0; + +error: + kfree(chain); + return -EINVAL; +} + /* * Scan the device for video chains and register video devices. * @@ -1617,15 +1725,10 @@ static int uvc_scan_device(struct uvc_device *dev) if (term->chain.next || term->chain.prev) continue; - chain = kzalloc(sizeof(*chain), GFP_KERNEL); + chain = uvc_alloc_chain(dev); if (chain == NULL) return -ENOMEM; - INIT_LIST_HEAD(&chain->entities); - mutex_init(&chain->ctrl_mutex); - chain->dev = dev; - v4l2_prio_init(&chain->prio); - term->flags |= UVC_ENTITY_FLAG_DEFAULT; if (uvc_scan_chain(chain, term) < 0) { @@ -1639,6 +1742,9 @@ static int uvc_scan_device(struct uvc_device *dev) list_add_tail(&chain->list, &dev->chains); } + if (list_empty(&dev->chains)) + uvc_scan_fallback(dev); + if (list_empty(&dev->chains)) { uvc_printk(KERN_INFO, "No valid video chain found.\n"); return -1; diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 12ffa4192605..2a4abf736d89 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -549,11 +549,13 @@ config VEXPRESS_SYSCFG bus. System Configuration interface is one of the possible means of generating transactions on this bus. -config UID_CPUTIME - bool "Per-UID cpu time statistics" +config UID_SYS_STATS + bool "Per-UID statistics" depends on PROFILING help Per UID based cpu time statistics exported to /proc/uid_cputime + Per UID based io statistics exported to /proc/uid_io + Per UID based procstat control in /proc/uid_procstat config QPNP_MISC tristate "QPNP Misc Peripheral" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index c2941afc961e..b0718228d2d9 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -62,7 +62,7 @@ endif obj-$(CONFIG_ECHO) += echo/ obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o obj-$(CONFIG_CXL_BASE) += cxl/ -obj-$(CONFIG_UID_CPUTIME) += uid_cputime.o +obj-$(CONFIG_UID_SYS_STATS) += uid_sys_stats.o obj-y += qcom/ obj-$(CONFIG_QPNP_MISC) += qpnp-misc.o obj-$(CONFIG_MEMORY_STATE_TIME) += memory_state_time.o diff --git a/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c b/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c index 84761747f129..e602650c4cb5 100644 --- a/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c +++ b/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c @@ -17,7 +17,6 @@ #include "q6audio_common.h" #include "audio_utils_aio.h" #include <sound/msm-audio-effects-q6-v2.h> -#include <sound/msm-dts-eagle.h> #define MAX_CHANNELS_SUPPORTED 8 #define WAIT_TIMEDOUT_DURATION_SECS 1 @@ -53,31 +52,11 @@ static void audio_effects_init_pp(struct audio_client *ac) pr_err("%s: audio client null to init pp\n", __func__); return; } - switch (ac->topology) { - case ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER: - - ret = q6asm_set_softvolume_v2(ac, &softvol, - SOFT_VOLUME_INSTANCE_1); - if (ret < 0) - pr_err("%s: Send SoftVolume1 Param failed ret=%d\n", - __func__, ret); - ret = q6asm_set_softvolume_v2(ac, &softvol, - SOFT_VOLUME_INSTANCE_2); - if (ret < 0) - pr_err("%s: Send SoftVolume2 Param failed ret=%d\n", - __func__, ret); - - msm_dts_eagle_init_master_module(ac); - - break; - default: - ret = q6asm_set_softvolume_v2(ac, &softvol, - SOFT_VOLUME_INSTANCE_1); - if (ret < 0) - pr_err("%s: Send SoftVolume Param failed ret=%d\n", - __func__, ret); - break; - } + ret = q6asm_set_softvolume_v2(ac, &softvol, + SOFT_VOLUME_INSTANCE_1); + if (ret < 0) + pr_err("%s: Send SoftVolume Param failed ret=%d\n", + __func__, ret); } static void audio_effects_deinit_pp(struct audio_client *ac) @@ -86,13 +65,6 @@ static void audio_effects_deinit_pp(struct audio_client *ac) pr_err("%s: audio client null to deinit pp\n", __func__); return; } - switch (ac->topology) { - case ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER: - msm_dts_eagle_deinit_master_module(ac); - break; - default: - break; - } } static void audio_effects_event_handler(uint32_t opcode, uint32_t token, @@ -428,33 +400,6 @@ static long audio_effects_set_pp_param(struct q6audio_effects *effects, &(effects->audio_effects.topo_switch_vol), (long *)&values[1], SOFT_VOLUME_INSTANCE_2); break; - case DTS_EAGLE_MODULE_ENABLE: - pr_debug("%s: DTS_EAGLE_MODULE_ENABLE\n", __func__); - if (msm_audio_effects_is_effmodule_supp_in_top( - effects_module, effects->ac->topology)) { - /* - * HPX->OFF: first disable HPX and then - * enable SA+ - * HPX->ON: first disable SA+ and then - * enable HPX - */ - bool hpx_state = (bool)values[1]; - if (hpx_state) - msm_audio_effects_enable_extn(effects->ac, - &(effects->audio_effects), - false); - msm_dts_eagle_enable_asm(effects->ac, - hpx_state, - AUDPROC_MODULE_ID_DTS_HPX_PREMIX); - msm_dts_eagle_enable_asm(effects->ac, - hpx_state, - AUDPROC_MODULE_ID_DTS_HPX_POSTMIX); - if (!hpx_state) - msm_audio_effects_enable_extn(effects->ac, - &(effects->audio_effects), - true); - } - break; default: pr_err("%s: Invalid effects config module\n", __func__); rc = -EINVAL; diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c index b7af80854420..e3f23caac5b8 100644 --- a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c +++ b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c @@ -851,6 +851,7 @@ static long audio_aio_process_event_req_compat(struct q6audio_aio *audio, long rc; struct msm_audio_event32 usr_evt_32; struct msm_audio_event usr_evt; + memset(&usr_evt, 0, sizeof(struct msm_audio_event)); if (copy_from_user(&usr_evt_32, arg, sizeof(struct msm_audio_event32))) { @@ -860,6 +861,11 @@ static long audio_aio_process_event_req_compat(struct q6audio_aio *audio, usr_evt.timeout_ms = usr_evt_32.timeout_ms; rc = audio_aio_process_event_req_common(audio, &usr_evt); + if (rc < 0) { + pr_err("%s: audio process event failed, rc = %ld", + __func__, rc); + return rc; + } usr_evt_32.event_type = usr_evt.event_type; switch (usr_evt_32.event_type) { diff --git a/drivers/misc/uid_cputime.c b/drivers/misc/uid_cputime.c deleted file mode 100644 index c1ad5246f564..000000000000 --- a/drivers/misc/uid_cputime.c +++ /dev/null @@ -1,240 +0,0 @@ -/* drivers/misc/uid_cputime.c - * - * Copyright (C) 2014 - 2015 Google, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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/atomic.h> -#include <linux/err.h> -#include <linux/hashtable.h> -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/list.h> -#include <linux/proc_fs.h> -#include <linux/profile.h> -#include <linux/sched.h> -#include <linux/seq_file.h> -#include <linux/slab.h> -#include <linux/uaccess.h> - -#define UID_HASH_BITS 10 -DECLARE_HASHTABLE(hash_table, UID_HASH_BITS); - -static DEFINE_MUTEX(uid_lock); -static struct proc_dir_entry *parent; - -struct uid_entry { - uid_t uid; - cputime_t utime; - cputime_t stime; - cputime_t active_utime; - cputime_t active_stime; - struct hlist_node hash; -}; - -static struct uid_entry *find_uid_entry(uid_t uid) -{ - struct uid_entry *uid_entry; - hash_for_each_possible(hash_table, uid_entry, hash, uid) { - if (uid_entry->uid == uid) - return uid_entry; - } - return NULL; -} - -static struct uid_entry *find_or_register_uid(uid_t uid) -{ - struct uid_entry *uid_entry; - - uid_entry = find_uid_entry(uid); - if (uid_entry) - return uid_entry; - - uid_entry = kzalloc(sizeof(struct uid_entry), GFP_ATOMIC); - if (!uid_entry) - return NULL; - - uid_entry->uid = uid; - - hash_add(hash_table, &uid_entry->hash, uid); - - return uid_entry; -} - -static int uid_stat_show(struct seq_file *m, void *v) -{ - struct uid_entry *uid_entry; - struct task_struct *task, *temp; - cputime_t utime; - cputime_t stime; - unsigned long bkt; - - mutex_lock(&uid_lock); - - hash_for_each(hash_table, bkt, uid_entry, hash) { - uid_entry->active_stime = 0; - uid_entry->active_utime = 0; - } - - read_lock(&tasklist_lock); - do_each_thread(temp, task) { - uid_entry = find_or_register_uid(from_kuid_munged( - current_user_ns(), task_uid(task))); - if (!uid_entry) { - read_unlock(&tasklist_lock); - mutex_unlock(&uid_lock); - pr_err("%s: failed to find the uid_entry for uid %d\n", - __func__, from_kuid_munged(current_user_ns(), - task_uid(task))); - return -ENOMEM; - } - task_cputime_adjusted(task, &utime, &stime); - uid_entry->active_utime += utime; - uid_entry->active_stime += stime; - } while_each_thread(temp, task); - read_unlock(&tasklist_lock); - - hash_for_each(hash_table, bkt, uid_entry, hash) { - cputime_t total_utime = uid_entry->utime + - uid_entry->active_utime; - cputime_t total_stime = uid_entry->stime + - uid_entry->active_stime; - seq_printf(m, "%d: %llu %llu\n", uid_entry->uid, - (unsigned long long)jiffies_to_msecs( - cputime_to_jiffies(total_utime)) * USEC_PER_MSEC, - (unsigned long long)jiffies_to_msecs( - cputime_to_jiffies(total_stime)) * USEC_PER_MSEC); - } - - mutex_unlock(&uid_lock); - return 0; -} - -static int uid_stat_open(struct inode *inode, struct file *file) -{ - return single_open(file, uid_stat_show, PDE_DATA(inode)); -} - -static const struct file_operations uid_stat_fops = { - .open = uid_stat_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int uid_remove_open(struct inode *inode, struct file *file) -{ - return single_open(file, NULL, NULL); -} - -static ssize_t uid_remove_write(struct file *file, - const char __user *buffer, size_t count, loff_t *ppos) -{ - struct uid_entry *uid_entry; - struct hlist_node *tmp; - char uids[128]; - char *start_uid, *end_uid = NULL; - long int uid_start = 0, uid_end = 0; - - if (count >= sizeof(uids)) - count = sizeof(uids) - 1; - - if (copy_from_user(uids, buffer, count)) - return -EFAULT; - - uids[count] = '\0'; - end_uid = uids; - start_uid = strsep(&end_uid, "-"); - - if (!start_uid || !end_uid) - return -EINVAL; - - if (kstrtol(start_uid, 10, &uid_start) != 0 || - kstrtol(end_uid, 10, &uid_end) != 0) { - return -EINVAL; - } - mutex_lock(&uid_lock); - - for (; uid_start <= uid_end; uid_start++) { - hash_for_each_possible_safe(hash_table, uid_entry, tmp, - hash, (uid_t)uid_start) { - if (uid_start == uid_entry->uid) { - hash_del(&uid_entry->hash); - kfree(uid_entry); - } - } - } - - mutex_unlock(&uid_lock); - return count; -} - -static const struct file_operations uid_remove_fops = { - .open = uid_remove_open, - .release = single_release, - .write = uid_remove_write, -}; - -static int process_notifier(struct notifier_block *self, - unsigned long cmd, void *v) -{ - struct task_struct *task = v; - struct uid_entry *uid_entry; - cputime_t utime, stime; - uid_t uid; - - if (!task) - return NOTIFY_OK; - - mutex_lock(&uid_lock); - uid = from_kuid_munged(current_user_ns(), task_uid(task)); - uid_entry = find_or_register_uid(uid); - if (!uid_entry) { - pr_err("%s: failed to find uid %d\n", __func__, uid); - goto exit; - } - - task_cputime_adjusted(task, &utime, &stime); - uid_entry->utime += utime; - uid_entry->stime += stime; - -exit: - mutex_unlock(&uid_lock); - return NOTIFY_OK; -} - -static struct notifier_block process_notifier_block = { - .notifier_call = process_notifier, -}; - -static int __init proc_uid_cputime_init(void) -{ - hash_init(hash_table); - - parent = proc_mkdir("uid_cputime", NULL); - if (!parent) { - pr_err("%s: failed to create proc entry\n", __func__); - return -ENOMEM; - } - - proc_create_data("remove_uid_range", S_IWUGO, parent, &uid_remove_fops, - NULL); - - proc_create_data("show_uid_stat", S_IRUGO, parent, &uid_stat_fops, - NULL); - - profile_event_register(PROFILE_TASK_EXIT, &process_notifier_block); - - return 0; -} - -early_initcall(proc_uid_cputime_init); diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c new file mode 100644 index 000000000000..ad21276c8d9e --- /dev/null +++ b/drivers/misc/uid_sys_stats.c @@ -0,0 +1,505 @@ +/* drivers/misc/uid_cputime.c + * + * Copyright (C) 2014 - 2015 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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/atomic.h> +#include <linux/err.h> +#include <linux/hashtable.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/proc_fs.h> +#include <linux/profile.h> +#include <linux/rtmutex.h> +#include <linux/sched.h> +#include <linux/seq_file.h> +#include <linux/slab.h> +#include <linux/uaccess.h> + +#define UID_HASH_BITS 10 +DECLARE_HASHTABLE(hash_table, UID_HASH_BITS); + +static DEFINE_RT_MUTEX(uid_lock); +static struct proc_dir_entry *cpu_parent; +static struct proc_dir_entry *io_parent; +static struct proc_dir_entry *proc_parent; + +struct io_stats { + u64 read_bytes; + u64 write_bytes; + u64 rchar; + u64 wchar; + u64 fsync; +}; + +#define UID_STATE_FOREGROUND 0 +#define UID_STATE_BACKGROUND 1 +#define UID_STATE_BUCKET_SIZE 2 + +#define UID_STATE_TOTAL_CURR 2 +#define UID_STATE_TOTAL_LAST 3 +#define UID_STATE_SIZE 4 + +struct uid_entry { + uid_t uid; + cputime_t utime; + cputime_t stime; + cputime_t active_utime; + cputime_t active_stime; + int state; + struct io_stats io[UID_STATE_SIZE]; + struct hlist_node hash; +}; + +static struct uid_entry *find_uid_entry(uid_t uid) +{ + struct uid_entry *uid_entry; + hash_for_each_possible(hash_table, uid_entry, hash, uid) { + if (uid_entry->uid == uid) + return uid_entry; + } + return NULL; +} + +static struct uid_entry *find_or_register_uid(uid_t uid) +{ + struct uid_entry *uid_entry; + + uid_entry = find_uid_entry(uid); + if (uid_entry) + return uid_entry; + + uid_entry = kzalloc(sizeof(struct uid_entry), GFP_ATOMIC); + if (!uid_entry) + return NULL; + + uid_entry->uid = uid; + + hash_add(hash_table, &uid_entry->hash, uid); + + return uid_entry; +} + +static int uid_cputime_show(struct seq_file *m, void *v) +{ + struct uid_entry *uid_entry; + struct task_struct *task, *temp; + struct user_namespace *user_ns = current_user_ns(); + cputime_t utime; + cputime_t stime; + unsigned long bkt; + uid_t uid; + + rt_mutex_lock(&uid_lock); + + hash_for_each(hash_table, bkt, uid_entry, hash) { + uid_entry->active_stime = 0; + uid_entry->active_utime = 0; + } + + read_lock(&tasklist_lock); + do_each_thread(temp, task) { + uid = from_kuid_munged(user_ns, task_uid(task)); + uid_entry = find_or_register_uid(uid); + if (!uid_entry) { + read_unlock(&tasklist_lock); + rt_mutex_unlock(&uid_lock); + pr_err("%s: failed to find the uid_entry for uid %d\n", + __func__, uid); + return -ENOMEM; + } + task_cputime_adjusted(task, &utime, &stime); + uid_entry->active_utime += utime; + uid_entry->active_stime += stime; + } while_each_thread(temp, task); + read_unlock(&tasklist_lock); + + hash_for_each(hash_table, bkt, uid_entry, hash) { + cputime_t total_utime = uid_entry->utime + + uid_entry->active_utime; + cputime_t total_stime = uid_entry->stime + + uid_entry->active_stime; + seq_printf(m, "%d: %llu %llu\n", uid_entry->uid, + (unsigned long long)jiffies_to_msecs( + cputime_to_jiffies(total_utime)) * USEC_PER_MSEC, + (unsigned long long)jiffies_to_msecs( + cputime_to_jiffies(total_stime)) * USEC_PER_MSEC); + } + + rt_mutex_unlock(&uid_lock); + return 0; +} + +static int uid_cputime_open(struct inode *inode, struct file *file) +{ + return single_open(file, uid_cputime_show, PDE_DATA(inode)); +} + +static const struct file_operations uid_cputime_fops = { + .open = uid_cputime_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int uid_remove_open(struct inode *inode, struct file *file) +{ + return single_open(file, NULL, NULL); +} + +static ssize_t uid_remove_write(struct file *file, + const char __user *buffer, size_t count, loff_t *ppos) +{ + struct uid_entry *uid_entry; + struct hlist_node *tmp; + char uids[128]; + char *start_uid, *end_uid = NULL; + long int uid_start = 0, uid_end = 0; + + if (count >= sizeof(uids)) + count = sizeof(uids) - 1; + + if (copy_from_user(uids, buffer, count)) + return -EFAULT; + + uids[count] = '\0'; + end_uid = uids; + start_uid = strsep(&end_uid, "-"); + + if (!start_uid || !end_uid) + return -EINVAL; + + if (kstrtol(start_uid, 10, &uid_start) != 0 || + kstrtol(end_uid, 10, &uid_end) != 0) { + return -EINVAL; + } + rt_mutex_lock(&uid_lock); + + for (; uid_start <= uid_end; uid_start++) { + hash_for_each_possible_safe(hash_table, uid_entry, tmp, + hash, (uid_t)uid_start) { + if (uid_start == uid_entry->uid) { + hash_del(&uid_entry->hash); + kfree(uid_entry); + } + } + } + + rt_mutex_unlock(&uid_lock); + return count; +} + +static const struct file_operations uid_remove_fops = { + .open = uid_remove_open, + .release = single_release, + .write = uid_remove_write, +}; + +static u64 compute_write_bytes(struct task_struct *task) +{ + if (task->ioac.write_bytes <= task->ioac.cancelled_write_bytes) + return 0; + + return task->ioac.write_bytes - task->ioac.cancelled_write_bytes; +} + +static void add_uid_io_curr_stats(struct uid_entry *uid_entry, + struct task_struct *task) +{ + struct io_stats *io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR]; + + io_curr->read_bytes += task->ioac.read_bytes; + io_curr->write_bytes += compute_write_bytes(task); + io_curr->rchar += task->ioac.rchar; + io_curr->wchar += task->ioac.wchar; + io_curr->fsync += task->ioac.syscfs; +} + +static void clean_uid_io_last_stats(struct uid_entry *uid_entry, + struct task_struct *task) +{ + struct io_stats *io_last = &uid_entry->io[UID_STATE_TOTAL_LAST]; + + io_last->read_bytes -= task->ioac.read_bytes; + io_last->write_bytes -= compute_write_bytes(task); + io_last->rchar -= task->ioac.rchar; + io_last->wchar -= task->ioac.wchar; + io_last->fsync -= task->ioac.syscfs; +} + +static void update_io_stats_all_locked(void) +{ + struct uid_entry *uid_entry; + struct task_struct *task, *temp; + struct io_stats *io_bucket, *io_curr, *io_last; + struct user_namespace *user_ns = current_user_ns(); + unsigned long bkt; + uid_t uid; + + hash_for_each(hash_table, bkt, uid_entry, hash) + memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0, + sizeof(struct io_stats)); + + rcu_read_lock(); + do_each_thread(temp, task) { + uid = from_kuid_munged(user_ns, task_uid(task)); + uid_entry = find_or_register_uid(uid); + if (!uid_entry) + continue; + add_uid_io_curr_stats(uid_entry, task); + } while_each_thread(temp, task); + rcu_read_unlock(); + + hash_for_each(hash_table, bkt, uid_entry, hash) { + io_bucket = &uid_entry->io[uid_entry->state]; + io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR]; + io_last = &uid_entry->io[UID_STATE_TOTAL_LAST]; + + io_bucket->read_bytes += + io_curr->read_bytes - io_last->read_bytes; + io_bucket->write_bytes += + io_curr->write_bytes - io_last->write_bytes; + io_bucket->rchar += io_curr->rchar - io_last->rchar; + io_bucket->wchar += io_curr->wchar - io_last->wchar; + io_bucket->fsync += io_curr->fsync - io_last->fsync; + + io_last->read_bytes = io_curr->read_bytes; + io_last->write_bytes = io_curr->write_bytes; + io_last->rchar = io_curr->rchar; + io_last->wchar = io_curr->wchar; + io_last->fsync = io_curr->fsync; + } +} + +static void update_io_stats_uid_locked(uid_t target_uid) +{ + struct uid_entry *uid_entry; + struct task_struct *task, *temp; + struct io_stats *io_bucket, *io_curr, *io_last; + struct user_namespace *user_ns = current_user_ns(); + + uid_entry = find_or_register_uid(target_uid); + if (!uid_entry) + return; + + memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0, + sizeof(struct io_stats)); + + rcu_read_lock(); + do_each_thread(temp, task) { + if (from_kuid_munged(user_ns, task_uid(task)) != target_uid) + continue; + add_uid_io_curr_stats(uid_entry, task); + } while_each_thread(temp, task); + rcu_read_unlock(); + + io_bucket = &uid_entry->io[uid_entry->state]; + io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR]; + io_last = &uid_entry->io[UID_STATE_TOTAL_LAST]; + + io_bucket->read_bytes += + io_curr->read_bytes - io_last->read_bytes; + io_bucket->write_bytes += + io_curr->write_bytes - io_last->write_bytes; + io_bucket->rchar += io_curr->rchar - io_last->rchar; + io_bucket->wchar += io_curr->wchar - io_last->wchar; + io_bucket->fsync += io_curr->fsync - io_last->fsync; + + io_last->read_bytes = io_curr->read_bytes; + io_last->write_bytes = io_curr->write_bytes; + io_last->rchar = io_curr->rchar; + io_last->wchar = io_curr->wchar; + io_last->fsync = io_curr->fsync; +} + +static int uid_io_show(struct seq_file *m, void *v) +{ + struct uid_entry *uid_entry; + unsigned long bkt; + + rt_mutex_lock(&uid_lock); + + update_io_stats_all_locked(); + + hash_for_each(hash_table, bkt, uid_entry, hash) { + seq_printf(m, "%d %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n", + uid_entry->uid, + uid_entry->io[UID_STATE_FOREGROUND].rchar, + uid_entry->io[UID_STATE_FOREGROUND].wchar, + uid_entry->io[UID_STATE_FOREGROUND].read_bytes, + uid_entry->io[UID_STATE_FOREGROUND].write_bytes, + uid_entry->io[UID_STATE_BACKGROUND].rchar, + uid_entry->io[UID_STATE_BACKGROUND].wchar, + uid_entry->io[UID_STATE_BACKGROUND].read_bytes, + uid_entry->io[UID_STATE_BACKGROUND].write_bytes, + uid_entry->io[UID_STATE_FOREGROUND].fsync, + uid_entry->io[UID_STATE_BACKGROUND].fsync); + } + + rt_mutex_unlock(&uid_lock); + + return 0; +} + +static int uid_io_open(struct inode *inode, struct file *file) +{ + return single_open(file, uid_io_show, PDE_DATA(inode)); +} + +static const struct file_operations uid_io_fops = { + .open = uid_io_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int uid_procstat_open(struct inode *inode, struct file *file) +{ + return single_open(file, NULL, NULL); +} + +static ssize_t uid_procstat_write(struct file *file, + const char __user *buffer, size_t count, loff_t *ppos) +{ + struct uid_entry *uid_entry; + uid_t uid; + int argc, state; + char input[128]; + + if (count >= sizeof(input)) + return -EINVAL; + + if (copy_from_user(input, buffer, count)) + return -EFAULT; + + input[count] = '\0'; + + argc = sscanf(input, "%u %d", &uid, &state); + if (argc != 2) + return -EINVAL; + + if (state != UID_STATE_BACKGROUND && state != UID_STATE_FOREGROUND) + return -EINVAL; + + rt_mutex_lock(&uid_lock); + + uid_entry = find_or_register_uid(uid); + if (!uid_entry) { + rt_mutex_unlock(&uid_lock); + return -EINVAL; + } + + if (uid_entry->state == state) { + rt_mutex_unlock(&uid_lock); + return count; + } + + update_io_stats_uid_locked(uid); + + uid_entry->state = state; + + rt_mutex_unlock(&uid_lock); + + return count; +} + +static const struct file_operations uid_procstat_fops = { + .open = uid_procstat_open, + .release = single_release, + .write = uid_procstat_write, +}; + +static int process_notifier(struct notifier_block *self, + unsigned long cmd, void *v) +{ + struct task_struct *task = v; + struct uid_entry *uid_entry; + cputime_t utime, stime; + uid_t uid; + + if (!task) + return NOTIFY_OK; + + rt_mutex_lock(&uid_lock); + uid = from_kuid_munged(current_user_ns(), task_uid(task)); + uid_entry = find_or_register_uid(uid); + if (!uid_entry) { + pr_err("%s: failed to find uid %d\n", __func__, uid); + goto exit; + } + + task_cputime_adjusted(task, &utime, &stime); + uid_entry->utime += utime; + uid_entry->stime += stime; + + update_io_stats_uid_locked(uid); + clean_uid_io_last_stats(uid_entry, task); + +exit: + rt_mutex_unlock(&uid_lock); + return NOTIFY_OK; +} + +static struct notifier_block process_notifier_block = { + .notifier_call = process_notifier, +}; + +static int __init proc_uid_sys_stats_init(void) +{ + hash_init(hash_table); + + cpu_parent = proc_mkdir("uid_cputime", NULL); + if (!cpu_parent) { + pr_err("%s: failed to create uid_cputime proc entry\n", + __func__); + goto err; + } + + proc_create_data("remove_uid_range", 0222, cpu_parent, + &uid_remove_fops, NULL); + proc_create_data("show_uid_stat", 0444, cpu_parent, + &uid_cputime_fops, NULL); + + io_parent = proc_mkdir("uid_io", NULL); + if (!io_parent) { + pr_err("%s: failed to create uid_io proc entry\n", + __func__); + goto err; + } + + proc_create_data("stats", 0444, io_parent, + &uid_io_fops, NULL); + + proc_parent = proc_mkdir("uid_procstat", NULL); + if (!proc_parent) { + pr_err("%s: failed to create uid_procstat proc entry\n", + __func__); + goto err; + } + + proc_create_data("set", 0222, proc_parent, + &uid_procstat_fops, NULL); + + profile_event_register(PROFILE_TASK_EXIT, &process_notifier_block); + + return 0; + +err: + remove_proc_subtree("uid_cputime", NULL); + remove_proc_subtree("uid_io", NULL); + remove_proc_subtree("uid_procstat", NULL); + return -ENOMEM; +} + +early_initcall(proc_uid_sys_stats_init); diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c index f5dbb67ba929..ccf22eb5bdc0 100644 --- a/drivers/mmc/card/queue.c +++ b/drivers/mmc/card/queue.c @@ -128,14 +128,11 @@ static int mmc_cmdq_thread(void *d) ret = mq->cmdq_issue_fn(mq, mq->cmdq_req_peeked); /* - * Don't requeue if issue_fn fails, just bug on. - * We don't expect failure here and there is no recovery other - * than fixing the actual issue if there is any. + * Don't requeue if issue_fn fails. + * Recovery will be come by completion softirq * Also we end the request if there is a partition switch error, * so we should not requeue the request here. */ - if (ret) - BUG_ON(1); } /* loop */ return 0; diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 41f0935440fd..c462eee4a5f7 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -3944,12 +3944,10 @@ static void mmc_hw_reset_for_init(struct mmc_host *host) */ int mmc_cmdq_hw_reset(struct mmc_host *host) { - if (!host->bus_ops->power_restore) - return -EOPNOTSUPP; + if (!host->bus_ops->reset) + return -EOPNOTSUPP; - mmc_power_cycle(host, host->ocr_avail); - mmc_select_voltage(host, host->card->ocr); - return host->bus_ops->power_restore(host); + return host->bus_ops->reset(host); } EXPORT_SYMBOL(mmc_cmdq_hw_reset); @@ -3969,8 +3967,9 @@ int mmc_hw_reset(struct mmc_host *host) ret = host->bus_ops->reset(host); mmc_bus_put(host); - if (ret != -EOPNOTSUPP) - pr_warn("%s: tried to reset card\n", mmc_hostname(host)); + if (ret) + pr_warn("%s: tried to reset card, got error %d\n", + mmc_hostname(host), ret); return ret; } diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index e8294502a701..9ca73a2b86db 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -653,19 +653,19 @@ static ssize_t store_enable(struct device *dev, mmc_get_card(host->card); if (!value) { - /*turning off clock scaling*/ - mmc_exit_clk_scaling(host); + /* Suspend the clock scaling and mask host capability */ + if (host->clk_scaling.enable) + mmc_suspend_clk_scaling(host); host->caps2 &= ~MMC_CAP2_CLK_SCALE; host->clk_scaling.state = MMC_LOAD_HIGH; /* Set to max. frequency when disabling */ mmc_clk_update_freq(host, host->card->clk_scaling_highest, host->clk_scaling.state); } else if (value) { - /* starting clock scaling, will restart in case started */ + /* Unmask host capability and resume scaling */ host->caps2 |= MMC_CAP2_CLK_SCALE; - if (host->clk_scaling.enable) - mmc_exit_clk_scaling(host); - mmc_init_clk_scaling(host); + if (!host->clk_scaling.enable) + mmc_resume_clk_scaling(host); } mmc_put_card(host->card); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 3b79f514350e..691287125895 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -680,6 +680,12 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd) card->ext_csd.ffu_capable = (ext_csd[EXT_CSD_SUPPORTED_MODE] & 0x1) && !(ext_csd[EXT_CSD_FW_CONFIG] & 0x1); + + card->ext_csd.pre_eol_info = ext_csd[EXT_CSD_PRE_EOL_INFO]; + card->ext_csd.device_life_time_est_typ_a = + ext_csd[EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_A]; + card->ext_csd.device_life_time_est_typ_b = + ext_csd[EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_B]; } out: return err; @@ -813,6 +819,11 @@ MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid); MMC_DEV_ATTR(name, "%s\n", card->cid.prod_name); MMC_DEV_ATTR(oemid, "0x%04x\n", card->cid.oemid); MMC_DEV_ATTR(prv, "0x%x\n", card->cid.prv); +MMC_DEV_ATTR(rev, "0x%x\n", card->ext_csd.rev); +MMC_DEV_ATTR(pre_eol_info, "%02x\n", card->ext_csd.pre_eol_info); +MMC_DEV_ATTR(life_time, "0x%02x 0x%02x\n", + card->ext_csd.device_life_time_est_typ_a, + card->ext_csd.device_life_time_est_typ_b); MMC_DEV_ATTR(serial, "0x%08x\n", card->cid.serial); MMC_DEV_ATTR(enhanced_area_offset, "%llu\n", card->ext_csd.enhanced_area_offset); @@ -851,6 +862,9 @@ static struct attribute *mmc_std_attrs[] = { &dev_attr_name.attr, &dev_attr_oemid.attr, &dev_attr_prv.attr, + &dev_attr_rev.attr, + &dev_attr_pre_eol_info.attr, + &dev_attr_life_time.attr, &dev_attr_serial.attr, &dev_attr_enhanced_area_offset.attr, &dev_attr_enhanced_area_size.attr, @@ -2878,23 +2892,42 @@ EXPORT_SYMBOL(mmc_can_reset); static int mmc_reset(struct mmc_host *host) { struct mmc_card *card = host->card; + int ret; + + if ((host->caps & MMC_CAP_HW_RESET) && host->ops->hw_reset && + mmc_can_reset(card)) { + /* If the card accept RST_n signal, send it. */ + mmc_set_clock(host, host->f_init); + host->ops->hw_reset(host); + /* Set initial state and call mmc_set_ios */ + mmc_set_initial_state(host); + } else { + /* Do a brute force power cycle */ + mmc_power_cycle(host, card->ocr); + } - if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset) - return -EOPNOTSUPP; - - if (!mmc_can_reset(card)) - return -EOPNOTSUPP; + /* Suspend clk scaling to avoid switching frequencies intermittently */ - mmc_host_clk_hold(host); - mmc_set_clock(host, host->f_init); + ret = mmc_suspend_clk_scaling(host); + if (ret) { + pr_err("%s: %s: fail to suspend clock scaling (%d)\n", + mmc_hostname(host), __func__, ret); + return ret; + } - host->ops->hw_reset(host); + ret = mmc_init_card(host, host->card->ocr, host->card); + if (ret) { + pr_err("%s: %s: mmc_init_card failed (%d)\n", + mmc_hostname(host), __func__, ret); + return ret; + } - /* Set initial state and call mmc_set_ios */ - mmc_set_initial_state(host); - mmc_host_clk_release(host); + ret = mmc_resume_clk_scaling(host); + if (ret) + pr_err("%s: %s: fail to resume clock scaling (%d)\n", + mmc_hostname(host), __func__, ret); - return mmc_init_card(host, card->ocr, card); + return ret; } static const struct mmc_bus_ops mmc_ops = { diff --git a/drivers/mmc/host/cmdq_hci.c b/drivers/mmc/host/cmdq_hci.c index a83960fd474f..3f741f83a436 100644 --- a/drivers/mmc/host/cmdq_hci.c +++ b/drivers/mmc/host/cmdq_hci.c @@ -1102,12 +1102,17 @@ skip_cqterri: * before setting doorbell, hence one is not needed here. */ for_each_set_bit(tag, &comp_status, cq_host->num_slots) { - /* complete the corresponding mrq */ - pr_debug("%s: completing tag -> %lu\n", - mmc_hostname(mmc), tag); - MMC_TRACE(mmc, "%s: completing tag -> %lu\n", - __func__, tag); + mrq = get_req_by_tag(cq_host, tag); + if (!((mrq->cmd && mrq->cmd->error) || + mrq->cmdq_req->resp_err || + (mrq->data && mrq->data->error))) { + /* complete the corresponding mrq */ + pr_debug("%s: completing tag -> %lu\n", + mmc_hostname(mmc), tag); + MMC_TRACE(mmc, "%s: completing tag -> %lu\n", + __func__, tag); cmdq_finish_data(mmc, tag); + } } } diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 1f1582f6cccb..8d838779fd1b 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -804,6 +804,7 @@ static int esdhc_change_pinstate(struct sdhci_host *host, switch (uhs) { case MMC_TIMING_UHS_SDR50: + case MMC_TIMING_UHS_DDR50: pinctrl = imx_data->pins_100mhz; break; case MMC_TIMING_UHS_SDR104: diff --git a/drivers/mmc/host/ushc.c b/drivers/mmc/host/ushc.c index d2c386f09d69..1d843357422e 100644 --- a/drivers/mmc/host/ushc.c +++ b/drivers/mmc/host/ushc.c @@ -426,6 +426,9 @@ static int ushc_probe(struct usb_interface *intf, const struct usb_device_id *id struct ushc_data *ushc; int ret; + if (intf->cur_altsetting->desc.bNumEndpoints < 1) + return -ENODEV; + mmc = mmc_alloc_host(sizeof(struct ushc_data), &intf->dev); if (mmc == NULL) return -ENOMEM; diff --git a/drivers/mtd/bcm47xxpart.c b/drivers/mtd/bcm47xxpart.c index c0720c1ee4c9..5abab8800891 100644 --- a/drivers/mtd/bcm47xxpart.c +++ b/drivers/mtd/bcm47xxpart.c @@ -225,12 +225,10 @@ static int bcm47xxpart_parse(struct mtd_info *master, last_trx_part = curr_part - 1; - /* - * We have whole TRX scanned, skip to the next part. Use - * roundown (not roundup), as the loop will increase - * offset in next step. - */ - offset = rounddown(offset + trx->length, blocksize); + /* Jump to the end of TRX */ + offset = roundup(offset + trx->length, blocksize); + /* Next loop iteration will increase the offset */ + offset -= blocksize; continue; } diff --git a/drivers/mtd/ubi/upd.c b/drivers/mtd/ubi/upd.c index 0134ba32a057..39712560b4c1 100644 --- a/drivers/mtd/ubi/upd.c +++ b/drivers/mtd/ubi/upd.c @@ -148,11 +148,11 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, return err; } - if (bytes == 0) { - err = ubi_wl_flush(ubi, UBI_ALL, UBI_ALL); - if (err) - return err; + err = ubi_wl_flush(ubi, UBI_ALL, UBI_ALL); + if (err) + return err; + if (bytes == 0) { err = clear_update_marker(ubi, vol, 0); if (err) return err; diff --git a/drivers/net/can/spi/rh850.c b/drivers/net/can/spi/rh850.c index c7a2182003bf..a93f979da9ed 100644 --- a/drivers/net/can/spi/rh850.c +++ b/drivers/net/can/spi/rh850.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, 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,6 +18,7 @@ #include <linux/spi/spi.h> #include <linux/can.h> #include <linux/can/dev.h> +#include <linux/completion.h> #define DEBUG_RH850 0 #if DEBUG_RH850 == 1 @@ -33,8 +34,11 @@ #define MAX_TX_BUFFERS 1 #define XFER_BUFFER_SIZE 64 #define RX_ASSEMBLY_BUFFER_SIZE 128 -#define RH850_CLOCK 80000000 +#define RH850_CLOCK 16000000 #define RH850_MAX_CHANNELS 4 +#define DRIVER_MODE_RAW_FRAMES 0 +#define DRIVER_MODE_PROPERTIES 1 +#define DRIVER_MODE_AMB 2 struct rh850_can { struct net_device *netdev[RH850_MAX_CHANNELS]; @@ -50,6 +54,10 @@ struct rh850_can { char *assembly_buffer; u8 assembly_buffer_size; atomic_t netif_queue_stop; + struct completion response_completion; + int wait_cmd; + int cmd_result; + int driver_mode; }; struct rh850_netdev_privdata { @@ -84,6 +92,36 @@ struct spi_miso { /* TLV for MISO line */ #define CMD_CAN_ADD_FILTER 0x83 #define CMD_CAN_REMOVE_FILTER 0x84 #define CMD_CAN_RECEIVE_FRAME 0x85 +#define CMD_CAN_CONFIG_BIT_TIMING 0x86 + +#define CMD_CAN_DATA_BUFF_ADD 0x87 +#define CMD_CAN_DATA_BUFF_REMOVE 0X88 +#define CMD_CAN_RELEASE_BUFFER 0x89 +#define CMD_CAN_DATA_BUFF_REMOVE_ALL 0x8A +#define CMD_PROPERTY_WRITE 0x8B +#define CMD_PROPERTY_READ 0x8C + +#define CMD_GET_FW_BR_VERSION 0x95 +#define CMD_BEGIN_FIRMWARE_UPGRADE 0x96 +#define CMD_FIRMWARE_UPGRADE_DATA 0x97 +#define CMD_END_FIRMWARE_UPGRADE 0x98 +#define CMD_BEGIN_BOOT_ROM_UPGRADE 0x99 +#define CMD_BOOT_ROM_UPGRADE_DATA 0x9A +#define CMD_END_BOOT_ROM_UPGRADE 0x9B + +#define IOCTL_RELEASE_CAN_BUFFER (SIOCDEVPRIVATE + 0) +#define IOCTL_ENABLE_BUFFERING (SIOCDEVPRIVATE + 1) +#define IOCTL_ADD_FRAME_FILTER (SIOCDEVPRIVATE + 2) +#define IOCTL_REMOVE_FRAME_FILTER (SIOCDEVPRIVATE + 3) +#define IOCTL_DISABLE_BUFFERING (SIOCDEVPRIVATE + 5) +#define IOCTL_DISABLE_ALL_BUFFERING (SIOCDEVPRIVATE + 6) +#define IOCTL_GET_FW_BR_VERSION (SIOCDEVPRIVATE + 7) +#define IOCTL_BEGIN_FIRMWARE_UPGRADE (SIOCDEVPRIVATE + 8) +#define IOCTL_FIRMWARE_UPGRADE_DATA (SIOCDEVPRIVATE + 9) +#define IOCTL_END_FIRMWARE_UPGRADE (SIOCDEVPRIVATE + 10) +#define IOCTL_BEGIN_BOOT_ROM_UPGRADE (SIOCDEVPRIVATE + 11) +#define IOCTL_BOOT_ROM_UPGRADE_DATA (SIOCDEVPRIVATE + 12) +#define IOCTL_END_BOOT_ROM_UPGRADE (SIOCDEVPRIVATE + 13) struct can_fw_resp { u8 maj; @@ -126,15 +164,82 @@ struct can_receive_frame { u8 data[]; } __packed; +struct can_config_bit_timing { + u8 can_if; + u32 brp; + u32 tseg1; + u32 tseg2; + u32 sjw; +} __packed; + +struct vehicle_property { + int id; + u64 ts; + int zone; + int val_type; + u32 data_len; + union { + u8 bval; + int val; + int val_arr[4]; + float f_value; + float float_arr[4]; + u8 str[36]; + }; +} __packed; + +/* IOCTL messages */ +struct rh850_release_can_buffer { + u8 enable; +} __packed; + +struct rh850_add_can_buffer { + u8 can_if; + u32 mid; + u32 mask; +} __packed; + +struct rh850_delete_can_buffer { + u8 can_if; + u32 mid; + u32 mask; +} __packed; + +struct can_fw_br_resp { + u8 maj; + u8 min; + u8 ver[32]; + u8 br_maj; + u8 br_min; + u8 curr_exec_mode; +} __packed; + +struct rh850_ioctl_req { + u8 len; + u8 data[]; +} __packed; + static struct can_bittiming_const rh850_bittiming_const = { .name = "rh850", - .tseg1_min = 4, + .tseg1_min = 1, .tseg1_max = 16, - .tseg2_min = 2, - .tseg2_max = 8, + .tseg2_min = 1, + .tseg2_max = 16, .sjw_max = 4, - .brp_min = 4, - .brp_max = 1023, + .brp_min = 1, + .brp_max = 70, + .brp_inc = 1, +}; + +static struct can_bittiming_const rh850_data_bittiming_const = { + .name = "rh850", + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 16, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 70, .brp_inc = 1, }; @@ -165,7 +270,7 @@ static void rh850_receive_frame(struct rh850_can *priv_data, } netdev = priv_data->netdev[frame->can_if]; skb = alloc_can_skb(netdev, &cf); - if (skb == NULL) { + if (!skb) { LOGDE("skb alloc failed. frame->can_if %d\n", frame->can_if); return; } @@ -191,9 +296,51 @@ static void rh850_receive_frame(struct rh850_can *priv_data, netdev->stats.rx_packets++; } -static void rh850_process_response(struct rh850_can *priv_data, - struct spi_miso *resp, int length) +static void rh850_receive_property(struct rh850_can *priv_data, + struct vehicle_property *property) { + struct canfd_frame *cfd; + u8 *p; + struct sk_buff *skb; + struct skb_shared_hwtstamps *skt; + struct timeval tv; + static u64 nanosec; + struct net_device *netdev; + int i; + + /* can0 as the channel with properties */ + netdev = priv_data->netdev[0]; + skb = alloc_canfd_skb(netdev, &cfd); + if (!skb) { + LOGDE("skb alloc failed. frame->can_if %d\n", 0); + return; + } + + LOGDI("rcv property:0x%x data:%2x %2x %2x %2x", + property->id, property->str[0], property->str[1], + property->str[2], property->str[3]); + cfd->can_id = 0x00; + cfd->len = sizeof(struct vehicle_property); + + p = (u8 *)property; + for (i = 0; i < cfd->len; i++) + cfd->data[i] = p[i]; + + nanosec = le64_to_cpu(property->ts); + tv.tv_sec = (int)(nanosec / 1000000000); + tv.tv_usec = (int)(nanosec - (u64)tv.tv_sec * 1000000000) / 1000; + skt = skb_hwtstamps(skb); + skt->hwtstamp = timeval_to_ktime(tv); + LOGDI(" hwtstamp %lld\n", ktime_to_ms(skt->hwtstamp)); + skb->tstamp = timeval_to_ktime(tv); + netif_rx(skb); + netdev->stats.rx_packets++; +} + +static int rh850_process_response(struct rh850_can *priv_data, + struct spi_miso *resp, int length) +{ + int ret = 0; LOGDI("<%x %2d [%d]\n", resp->cmd, resp->len, resp->seq); if (resp->cmd == CMD_CAN_RECEIVE_FRAME) { struct can_receive_frame *frame = @@ -208,19 +355,55 @@ static void rh850_process_response(struct rh850_can *priv_data, } else { rh850_receive_frame(priv_data, frame); } + } else if (resp->cmd == CMD_PROPERTY_READ) { + struct vehicle_property *property = + (struct vehicle_property *)&resp->data; + if (resp->len > length) { + LOGDE("Error. This should never happen\n"); + LOGDE("process_response: Saving %d bytes\n", + length); + memcpy(priv_data->assembly_buffer, (char *)resp, + length); + priv_data->assembly_buffer_size = length; + } else { + rh850_receive_property(priv_data, property); + } } else if (resp->cmd == CMD_GET_FW_VERSION) { struct can_fw_resp *fw_resp = (struct can_fw_resp *)resp->data; dev_info(&priv_data->spidev->dev, "fw %d.%d", fw_resp->maj, fw_resp->min); dev_info(&priv_data->spidev->dev, "fw string %s", fw_resp->ver); + } else if (resp->cmd == CMD_GET_FW_BR_VERSION) { + struct can_fw_br_resp *fw_resp = + (struct can_fw_br_resp *)resp->data; + + dev_info(&priv_data->spidev->dev, "fw_can %d.%d", + fw_resp->maj, fw_resp->min); + dev_info(&priv_data->spidev->dev, "fw string %s", + fw_resp->ver); + dev_info(&priv_data->spidev->dev, "fw_br %d.%d exec_mode %d", + fw_resp->br_maj, fw_resp->br_min, + fw_resp->curr_exec_mode); + ret = fw_resp->curr_exec_mode << 28; + ret |= (fw_resp->br_maj & 0xF) << 24; + ret |= (fw_resp->br_min & 0xFF) << 16; + ret |= (fw_resp->maj & 0xF) << 8; + ret |= (fw_resp->min & 0xFF); + } + + if (resp->cmd == priv_data->wait_cmd) { + priv_data->cmd_result = ret; + complete(&priv_data->response_completion); } + return ret; } -static void rh850_process_rx(struct rh850_can *priv_data, char *rx_buf) +static int rh850_process_rx(struct rh850_can *priv_data, char *rx_buf) { struct spi_miso *resp; int length_processed = 0, actual_length = priv_data->xfer_length; + int ret = 0; while (length_processed < actual_length) { int length_left = actual_length - length_processed; @@ -237,7 +420,8 @@ static void rh850_process_rx(struct rh850_can *priv_data, char *rx_buf) rx_buf, 2); data = priv_data->assembly_buffer; resp = (struct spi_miso *)data; - length = resp->len - priv_data->assembly_buffer_size; + length = resp->len + sizeof(*resp) + - priv_data->assembly_buffer_size; if (length > 0) memcpy(priv_data->assembly_buffer + priv_data->assembly_buffer_size, @@ -258,15 +442,11 @@ static void rh850_process_rx(struct rh850_can *priv_data, char *rx_buf) length_processed, length_left, priv_data->xfer_length); length_processed += length; if (length_left >= sizeof(*resp) && - resp->len <= length_left) { + resp->len + sizeof(*resp) <= length_left) { struct spi_miso *resp = (struct spi_miso *)data; - if (resp->len < sizeof(struct spi_miso)) { - LOGDE("Error resp->len is %d). Abort.\n", - resp->len); - break; - } - rh850_process_response(priv_data, resp, length_left); + ret = rh850_process_response(priv_data, resp, + length_left); } else if (length_left > 0) { /* Not full message. Store however much we have for */ /* later assembly */ @@ -277,6 +457,7 @@ static void rh850_process_rx(struct rh850_can *priv_data, char *rx_buf) break; } } + return ret; } static int rh850_do_spi_transaction(struct rh850_can *priv_data) @@ -291,15 +472,20 @@ static int rh850_do_spi_transaction(struct rh850_can *priv_data) msg = kzalloc(sizeof(*msg), GFP_KERNEL); if (xfer == 0 || msg == 0) return -ENOMEM; + LOGDI(">%x %2d [%d]\n", priv_data->tx_buf[0], + priv_data->tx_buf[1], priv_data->tx_buf[2]); spi_message_init(msg); spi_message_add_tail(xfer, msg); xfer->tx_buf = priv_data->tx_buf; xfer->rx_buf = priv_data->rx_buf; xfer->len = priv_data->xfer_length; ret = spi_sync(spi, msg); - LOGDI("spi_sync ret %d\n", ret); + LOGDI("spi_sync ret %d data %x %x %x %x %x %x %x %x\n", ret, + priv_data->rx_buf[0], priv_data->rx_buf[1], priv_data->rx_buf[2], + priv_data->rx_buf[3], priv_data->rx_buf[4], priv_data->rx_buf[5], + priv_data->rx_buf[6], priv_data->rx_buf[7]); if (ret == 0) - rh850_process_rx(priv_data, priv_data->rx_buf); + ret = rh850_process_rx(priv_data, priv_data->rx_buf); kfree(msg); kfree(xfer); return ret; @@ -347,8 +533,54 @@ static int rh850_query_firmware_version(struct rh850_can *priv_data) return ret; } +static int rh850_set_bitrate(struct net_device *netdev) +{ + char *tx_buf, *rx_buf; + int ret; + struct spi_mosi *req; + struct can_config_bit_timing *req_d; + struct rh850_can *priv_data; + struct can_priv *priv = netdev_priv(netdev); + struct rh850_netdev_privdata *rh850_priv; + + rh850_priv = netdev_priv(netdev); + priv_data = rh850_priv->rh850_can; + + netdev_info(netdev, "ch%i, bitrate setting>%i", + rh850_priv->netdev_index, priv->bittiming.bitrate); + LOGNI("sjw>%i brp>%i ph_sg1>%i ph_sg2>%i smpl_pt>%i tq>%i pr_seg>%i", + priv->bittiming.sjw, priv->bittiming.brp, + priv->bittiming.phase_seg1, + priv->bittiming.phase_seg2, + priv->bittiming.sample_point, + priv->bittiming.tq, priv->bittiming.prop_seg); + + mutex_lock(&priv_data->spi_lock); + tx_buf = priv_data->tx_buf; + rx_buf = priv_data->rx_buf; + memset(tx_buf, 0, XFER_BUFFER_SIZE); + memset(rx_buf, 0, XFER_BUFFER_SIZE); + priv_data->xfer_length = XFER_BUFFER_SIZE; + + req = (struct spi_mosi *)tx_buf; + req->cmd = CMD_CAN_CONFIG_BIT_TIMING; + req->len = sizeof(struct can_config_bit_timing); + req->seq = atomic_inc_return(&priv_data->msg_seq); + req_d = (struct can_config_bit_timing *)req->data; + req_d->can_if = rh850_priv->netdev_index; + req_d->brp = priv->bittiming.brp; + req_d->tseg1 = priv->bittiming.phase_seg1 + priv->bittiming.prop_seg; + req_d->tseg2 = priv->bittiming.phase_seg2; + req_d->sjw = priv->bittiming.sjw; + + ret = rh850_do_spi_transaction(priv_data); + mutex_unlock(&priv_data->spi_lock); + + return ret; +} + static int rh850_can_write(struct rh850_can *priv_data, - int can_channel, struct can_frame *cf) + int can_channel, struct canfd_frame *cf) { char *tx_buf, *rx_buf; int ret, i; @@ -369,16 +601,29 @@ static int rh850_can_write(struct rh850_can *priv_data, priv_data->xfer_length = XFER_BUFFER_SIZE; req = (struct spi_mosi *)tx_buf; - req->cmd = CMD_CAN_SEND_FRAME; - req->len = sizeof(struct can_write_req) + 8; - req->seq = atomic_inc_return(&priv_data->msg_seq); - - req_d = (struct can_write_req *)req->data; - req_d->can_if = can_channel; - req_d->mid = cf->can_id; - req_d->dlc = cf->can_dlc; - for (i = 0; i < cf->can_dlc; i++) - req_d->data[i] = cf->data[i]; + if (priv_data->driver_mode == DRIVER_MODE_RAW_FRAMES) { + req->cmd = CMD_CAN_SEND_FRAME; + req->len = sizeof(struct can_write_req) + 8; + req->seq = atomic_inc_return(&priv_data->msg_seq); + + req_d = (struct can_write_req *)req->data; + req_d->can_if = can_channel; + req_d->mid = cf->can_id; + req_d->dlc = cf->len; + + for (i = 0; i < cf->len; i++) + req_d->data[i] = cf->data[i]; + } else if (priv_data->driver_mode == DRIVER_MODE_PROPERTIES || + priv_data->driver_mode == DRIVER_MODE_AMB) { + req->cmd = CMD_PROPERTY_WRITE; + req->len = sizeof(struct vehicle_property); + req->seq = atomic_inc_return(&priv_data->msg_seq); + for (i = 0; i < cf->len; i++) + req->data[i] = cf->data[i]; + } else { + LOGDE("rh850_can_write: wrong driver mode %i", + priv_data->driver_mode); + } ret = rh850_do_spi_transaction(priv_data); netdev = priv_data->netdev[can_channel]; @@ -414,7 +659,7 @@ static int rh850_netdev_close(struct net_device *netdev) static void rh850_send_can_frame(struct work_struct *ws) { struct rh850_tx_work *tx_work; - struct can_frame *cf; + struct canfd_frame *cf; struct rh850_can *priv_data; struct net_device *netdev; struct rh850_netdev_privdata *netdev_priv_data; @@ -428,7 +673,7 @@ static void rh850_send_can_frame(struct work_struct *ws) LOGDI("send_can_frame ws %p\n", ws); LOGDI("send_can_frame tx %p\n", tx_work); - cf = (struct can_frame *)tx_work->skb->data; + cf = (struct canfd_frame *)tx_work->skb->data; rh850_can_write(priv_data, can_channel, cf); dev_kfree_skb(tx_work->skb); @@ -458,10 +703,304 @@ static netdev_tx_t rh850_netdev_start_xmit( return NETDEV_TX_OK; } +static int rh850_send_release_can_buffer_cmd(struct net_device *netdev) +{ + char *tx_buf, *rx_buf; + int ret; + struct spi_mosi *req; + struct rh850_can *priv_data; + struct rh850_netdev_privdata *netdev_priv_data; + int *mode; + + netdev_priv_data = netdev_priv(netdev); + priv_data = netdev_priv_data->rh850_can; + mutex_lock(&priv_data->spi_lock); + tx_buf = priv_data->tx_buf; + rx_buf = priv_data->rx_buf; + memset(tx_buf, 0, XFER_BUFFER_SIZE); + memset(rx_buf, 0, XFER_BUFFER_SIZE); + priv_data->xfer_length = XFER_BUFFER_SIZE; + + req = (struct spi_mosi *)tx_buf; + req->cmd = CMD_CAN_RELEASE_BUFFER; + req->len = sizeof(int); + req->seq = atomic_inc_return(&priv_data->msg_seq); + mode = (int *)req->data; + *mode = priv_data->driver_mode; + + ret = rh850_do_spi_transaction(priv_data); + mutex_unlock(&priv_data->spi_lock); + + return ret; +} + +static int rh850_data_buffering(struct net_device *netdev, + struct ifreq *ifr, int cmd) +{ + char *tx_buf, *rx_buf; + int ret; + struct spi_mosi *req; + struct rh850_add_can_buffer *enable_buffering; + struct rh850_add_can_buffer *add_request; + struct rh850_can *priv_data; + struct rh850_netdev_privdata *netdev_priv_data; + + netdev_priv_data = netdev_priv(netdev); + priv_data = netdev_priv_data->rh850_can; + + mutex_lock(&priv_data->spi_lock); + tx_buf = priv_data->tx_buf; + rx_buf = priv_data->rx_buf; + memset(tx_buf, 0, XFER_BUFFER_SIZE); + memset(rx_buf, 0, XFER_BUFFER_SIZE); + priv_data->xfer_length = XFER_BUFFER_SIZE; + + add_request = ifr->ifr_data; + req = (struct spi_mosi *)tx_buf; + + if (cmd == IOCTL_ENABLE_BUFFERING) + req->cmd = CMD_CAN_DATA_BUFF_ADD; + else + req->cmd = CMD_CAN_DATA_BUFF_REMOVE; + + req->len = sizeof(struct rh850_add_can_buffer); + req->seq = atomic_inc_return(&priv_data->msg_seq); + + enable_buffering = (struct rh850_add_can_buffer *)req->data; + enable_buffering->can_if = add_request->can_if; + enable_buffering->mid = add_request->mid; + enable_buffering->mask = add_request->mask; + + ret = rh850_do_spi_transaction(priv_data); + mutex_unlock(&priv_data->spi_lock); + + return ret; +} + +static int rh850_remove_all_buffering(struct net_device *netdev) +{ + char *tx_buf, *rx_buf; + int ret; + struct spi_mosi *req; + struct rh850_can *priv_data; + struct rh850_netdev_privdata *netdev_priv_data; + + netdev_priv_data = netdev_priv(netdev); + priv_data = netdev_priv_data->rh850_can; + + mutex_lock(&priv_data->spi_lock); + tx_buf = priv_data->tx_buf; + rx_buf = priv_data->rx_buf; + memset(tx_buf, 0, XFER_BUFFER_SIZE); + memset(rx_buf, 0, XFER_BUFFER_SIZE); + priv_data->xfer_length = XFER_BUFFER_SIZE; + + req = (struct spi_mosi *)tx_buf; + req->cmd = CMD_CAN_DATA_BUFF_REMOVE_ALL; + req->len = 0; + req->seq = atomic_inc_return(&priv_data->msg_seq); + + ret = rh850_do_spi_transaction(priv_data); + mutex_unlock(&priv_data->spi_lock); + + return ret; +} + +static int rh850_frame_filter(struct net_device *netdev, + struct ifreq *ifr, int cmd) +{ + char *tx_buf, *rx_buf; + int ret; + struct spi_mosi *req; + struct can_add_filter_req *add_filter; + struct can_add_filter_req *filter_request; + struct rh850_can *priv_data; + struct rh850_netdev_privdata *netdev_priv_data; + + netdev_priv_data = netdev_priv(netdev); + priv_data = netdev_priv_data->rh850_can; + + mutex_lock(&priv_data->spi_lock); + tx_buf = priv_data->tx_buf; + rx_buf = priv_data->rx_buf; + memset(tx_buf, 0, XFER_BUFFER_SIZE); + memset(rx_buf, 0, XFER_BUFFER_SIZE); + priv_data->xfer_length = XFER_BUFFER_SIZE; + + filter_request = ifr->ifr_data; + req = (struct spi_mosi *)tx_buf; + + if (cmd == IOCTL_ADD_FRAME_FILTER) + req->cmd = CMD_CAN_ADD_FILTER; + else + req->cmd = CMD_CAN_REMOVE_FILTER; + + req->len = sizeof(struct can_add_filter_req); + req->seq = atomic_inc_return(&priv_data->msg_seq); + + add_filter = (struct can_add_filter_req *)req->data; + add_filter->can_if = filter_request->can_if; + add_filter->mid = filter_request->mid; + add_filter->mask = filter_request->mask; + + ret = rh850_do_spi_transaction(priv_data); + mutex_unlock(&priv_data->spi_lock); + + return ret; +} + +static int rh850_send_spi_locked(struct rh850_can *priv_data, int cmd, int len, + u8 *data) +{ + char *tx_buf, *rx_buf; + struct spi_mosi *req; + int ret; + + LOGDI("rh850_send_spi_locked\n"); + + tx_buf = priv_data->tx_buf; + rx_buf = priv_data->rx_buf; + memset(tx_buf, 0, XFER_BUFFER_SIZE); + memset(rx_buf, 0, XFER_BUFFER_SIZE); + priv_data->xfer_length = XFER_BUFFER_SIZE; + + req = (struct spi_mosi *)tx_buf; + req->cmd = cmd; + req->len = len; + req->seq = atomic_inc_return(&priv_data->msg_seq); + + if (unlikely(len > 64)) + return -EINVAL; + memcpy(req->data, data, len); + + ret = rh850_do_spi_transaction(priv_data); + return ret; +} + +static int rh850_convert_ioctl_cmd_to_spi_cmd(int ioctl_cmd) +{ + switch (ioctl_cmd) { + case IOCTL_GET_FW_BR_VERSION: + return CMD_GET_FW_BR_VERSION; + case IOCTL_BEGIN_FIRMWARE_UPGRADE: + return CMD_BEGIN_FIRMWARE_UPGRADE; + case IOCTL_FIRMWARE_UPGRADE_DATA: + return CMD_FIRMWARE_UPGRADE_DATA; + case IOCTL_END_FIRMWARE_UPGRADE: + return CMD_END_FIRMWARE_UPGRADE; + case IOCTL_BEGIN_BOOT_ROM_UPGRADE: + return CMD_BEGIN_BOOT_ROM_UPGRADE; + case IOCTL_BOOT_ROM_UPGRADE_DATA: + return CMD_BOOT_ROM_UPGRADE_DATA; + case IOCTL_END_BOOT_ROM_UPGRADE: + return CMD_END_BOOT_ROM_UPGRADE; + } + return -EINVAL; +} + +static int rh850_do_blocking_ioctl(struct net_device *netdev, + struct ifreq *ifr, int cmd) +{ + int spi_cmd, ret; + + struct rh850_can *priv_data; + struct rh850_netdev_privdata *netdev_priv_data; + struct rh850_ioctl_req *ioctl_data; + int len = 0; + u8 *data = NULL; + + netdev_priv_data = netdev_priv(netdev); + priv_data = netdev_priv_data->rh850_can; + + spi_cmd = rh850_convert_ioctl_cmd_to_spi_cmd(cmd); + LOGDI("rh850_do_blocking_ioctl spi_cmd %x\n", spi_cmd); + if (spi_cmd < 0) { + LOGDE("rh850_do_blocking_ioctl wrong command %d\n", cmd); + return spi_cmd; + } + if (!ifr) + return -EINVAL; + ioctl_data = ifr->ifr_data; + /* Regular NULL check fails here as ioctl_data is at some offset */ + if ((void *)ioctl_data > (void *)0x100) { + len = ioctl_data->len; + data = ioctl_data->data; + } + LOGDI("rh850_do_blocking_ioctl len %d\n", len); + mutex_lock(&priv_data->spi_lock); + + priv_data->wait_cmd = spi_cmd; + priv_data->cmd_result = -1; + reinit_completion(&priv_data->response_completion); + + ret = rh850_send_spi_locked(priv_data, spi_cmd, len, data); + mutex_unlock(&priv_data->spi_lock); + + if (ret == 0) { + LOGDI("rh850_do_blocking_ioctl ready to wait for response\n"); + wait_for_completion_interruptible_timeout( + &priv_data->response_completion, 5 * HZ); + ret = priv_data->cmd_result; + } + return ret; +} + +static int rh850_netdev_do_ioctl(struct net_device *netdev, + struct ifreq *ifr, int cmd) +{ + struct rh850_can *priv_data; + struct rh850_netdev_privdata *netdev_priv_data; + int *mode; + int ret = -EINVAL; + + netdev_priv_data = netdev_priv(netdev); + priv_data = netdev_priv_data->rh850_can; + LOGDI("rh850_netdev_do_ioctl %x\n", cmd); + + switch (cmd) { + case IOCTL_RELEASE_CAN_BUFFER: + if (ifr->ifr_data > (void *)0x100) { + mode = ifr->ifr_data; + priv_data->driver_mode = *mode; + } + LOGDE("rh850_driver_mode %d\n", priv_data->driver_mode); + rh850_send_release_can_buffer_cmd(netdev); + ret = 0; + break; + case IOCTL_ENABLE_BUFFERING: + case IOCTL_DISABLE_BUFFERING: + rh850_data_buffering(netdev, ifr, cmd); + ret = 0; + break; + case IOCTL_DISABLE_ALL_BUFFERING: + rh850_remove_all_buffering(netdev); + ret = 0; + break; + case IOCTL_ADD_FRAME_FILTER: + case IOCTL_REMOVE_FRAME_FILTER: + rh850_frame_filter(netdev, ifr, cmd); + ret = 0; + break; + case IOCTL_GET_FW_BR_VERSION: + case IOCTL_BEGIN_FIRMWARE_UPGRADE: + case IOCTL_FIRMWARE_UPGRADE_DATA: + case IOCTL_END_FIRMWARE_UPGRADE: + case IOCTL_BEGIN_BOOT_ROM_UPGRADE: + case IOCTL_BOOT_ROM_UPGRADE_DATA: + case IOCTL_END_BOOT_ROM_UPGRADE: + ret = rh850_do_blocking_ioctl(netdev, ifr, cmd); + break; + } + LOGDI("rh850_netdev_do_ioctl ret %d\n", ret); + + return ret; +} + static const struct net_device_ops rh850_netdev_ops = { .ndo_open = rh850_netdev_open, .ndo_stop = rh850_netdev_close, .ndo_start_xmit = rh850_netdev_start_xmit, + .ndo_do_ioctl = rh850_netdev_do_ioctl, }; static int rh850_create_netdev(struct spi_device *spi, @@ -490,9 +1029,13 @@ static int rh850_create_netdev(struct spi_device *spi, netdev->netdev_ops = &rh850_netdev_ops; SET_NETDEV_DEV(netdev, &spi->dev); netdev_priv_data->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | - CAN_CTRLMODE_LISTENONLY; + CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_FD; netdev_priv_data->can.bittiming_const = &rh850_bittiming_const; + netdev_priv_data->can.data_bittiming_const = + &rh850_data_bittiming_const; netdev_priv_data->can.clock.freq = RH850_CLOCK; + netdev_priv_data->can.do_set_bittiming = rh850_set_bitrate; return 0; } @@ -534,9 +1077,11 @@ static struct rh850_can *rh850_create_priv_data(struct spi_device *spi) goto cleanup_privdata; } priv_data->xfer_length = 0; + priv_data->driver_mode = DRIVER_MODE_RAW_FRAMES; mutex_init(&priv_data->spi_lock); atomic_set(&priv_data->msg_seq, 0); + init_completion(&priv_data->response_completion); return priv_data; cleanup_privdata: diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-common.h b/drivers/net/ethernet/amd/xgbe/xgbe-common.h index b6fa89102526..66ba1e0ff37e 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-common.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe-common.h @@ -913,8 +913,8 @@ #define RX_PACKET_ATTRIBUTES_CSUM_DONE_WIDTH 1 #define RX_PACKET_ATTRIBUTES_VLAN_CTAG_INDEX 1 #define RX_PACKET_ATTRIBUTES_VLAN_CTAG_WIDTH 1 -#define RX_PACKET_ATTRIBUTES_INCOMPLETE_INDEX 2 -#define RX_PACKET_ATTRIBUTES_INCOMPLETE_WIDTH 1 +#define RX_PACKET_ATTRIBUTES_LAST_INDEX 2 +#define RX_PACKET_ATTRIBUTES_LAST_WIDTH 1 #define RX_PACKET_ATTRIBUTES_CONTEXT_NEXT_INDEX 3 #define RX_PACKET_ATTRIBUTES_CONTEXT_NEXT_WIDTH 1 #define RX_PACKET_ATTRIBUTES_CONTEXT_INDEX 4 @@ -923,6 +923,8 @@ #define RX_PACKET_ATTRIBUTES_RX_TSTAMP_WIDTH 1 #define RX_PACKET_ATTRIBUTES_RSS_HASH_INDEX 6 #define RX_PACKET_ATTRIBUTES_RSS_HASH_WIDTH 1 +#define RX_PACKET_ATTRIBUTES_FIRST_INDEX 7 +#define RX_PACKET_ATTRIBUTES_FIRST_WIDTH 1 #define RX_NORMAL_DESC0_OVT_INDEX 0 #define RX_NORMAL_DESC0_OVT_WIDTH 16 diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c index f6a7161e3b85..5e6238e0b2bd 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -1658,10 +1658,15 @@ static int xgbe_dev_read(struct xgbe_channel *channel) /* Get the header length */ if (XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, FD)) { + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + FIRST, 1); rdata->rx.hdr_len = XGMAC_GET_BITS_LE(rdesc->desc2, RX_NORMAL_DESC2, HL); if (rdata->rx.hdr_len) pdata->ext_stats.rx_split_header_packets++; + } else { + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + FIRST, 0); } /* Get the RSS hash */ @@ -1684,19 +1689,16 @@ static int xgbe_dev_read(struct xgbe_channel *channel) } } - /* Get the packet length */ - rdata->rx.len = XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, PL); - - if (!XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, LD)) { - /* Not all the data has been transferred for this packet */ - XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, - INCOMPLETE, 1); + /* Not all the data has been transferred for this packet */ + if (!XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, LD)) return 0; - } /* This is the last of the data for this packet */ XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, - INCOMPLETE, 0); + LAST, 1); + + /* Get the packet length */ + rdata->rx.len = XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, PL); /* Set checksum done indicator as appropriate */ if (netdev->features & NETIF_F_RXCSUM) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index 53ce1222b11d..865b7e0b133b 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -1760,13 +1760,12 @@ static struct sk_buff *xgbe_create_skb(struct xgbe_prv_data *pdata, { struct sk_buff *skb; u8 *packet; - unsigned int copy_len; skb = napi_alloc_skb(napi, rdata->rx.hdr.dma_len); if (!skb) return NULL; - /* Start with the header buffer which may contain just the header + /* Pull in the header buffer which may contain just the header * or the header plus data */ dma_sync_single_range_for_cpu(pdata->dev, rdata->rx.hdr.dma_base, @@ -1775,30 +1774,49 @@ static struct sk_buff *xgbe_create_skb(struct xgbe_prv_data *pdata, packet = page_address(rdata->rx.hdr.pa.pages) + rdata->rx.hdr.pa.pages_offset; - copy_len = (rdata->rx.hdr_len) ? rdata->rx.hdr_len : len; - copy_len = min(rdata->rx.hdr.dma_len, copy_len); - skb_copy_to_linear_data(skb, packet, copy_len); - skb_put(skb, copy_len); - - len -= copy_len; - if (len) { - /* Add the remaining data as a frag */ - dma_sync_single_range_for_cpu(pdata->dev, - rdata->rx.buf.dma_base, - rdata->rx.buf.dma_off, - rdata->rx.buf.dma_len, - DMA_FROM_DEVICE); - - skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, - rdata->rx.buf.pa.pages, - rdata->rx.buf.pa.pages_offset, - len, rdata->rx.buf.dma_len); - rdata->rx.buf.pa.pages = NULL; - } + skb_copy_to_linear_data(skb, packet, len); + skb_put(skb, len); return skb; } +static unsigned int xgbe_rx_buf1_len(struct xgbe_ring_data *rdata, + struct xgbe_packet_data *packet) +{ + /* Always zero if not the first descriptor */ + if (!XGMAC_GET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, FIRST)) + return 0; + + /* First descriptor with split header, return header length */ + if (rdata->rx.hdr_len) + return rdata->rx.hdr_len; + + /* First descriptor but not the last descriptor and no split header, + * so the full buffer was used + */ + if (!XGMAC_GET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, LAST)) + return rdata->rx.hdr.dma_len; + + /* First descriptor and last descriptor and no split header, so + * calculate how much of the buffer was used + */ + return min_t(unsigned int, rdata->rx.hdr.dma_len, rdata->rx.len); +} + +static unsigned int xgbe_rx_buf2_len(struct xgbe_ring_data *rdata, + struct xgbe_packet_data *packet, + unsigned int len) +{ + /* Always the full buffer if not the last descriptor */ + if (!XGMAC_GET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, LAST)) + return rdata->rx.buf.dma_len; + + /* Last descriptor so calculate how much of the buffer was used + * for the last bit of data + */ + return rdata->rx.len - len; +} + static int xgbe_tx_poll(struct xgbe_channel *channel) { struct xgbe_prv_data *pdata = channel->pdata; @@ -1881,8 +1899,8 @@ static int xgbe_rx_poll(struct xgbe_channel *channel, int budget) struct napi_struct *napi; struct sk_buff *skb; struct skb_shared_hwtstamps *hwtstamps; - unsigned int incomplete, error, context_next, context; - unsigned int len, rdesc_len, max_len; + unsigned int last, error, context_next, context; + unsigned int len, buf1_len, buf2_len, max_len; unsigned int received = 0; int packet_count = 0; @@ -1892,7 +1910,7 @@ static int xgbe_rx_poll(struct xgbe_channel *channel, int budget) if (!ring) return 0; - incomplete = 0; + last = 0; context_next = 0; napi = (pdata->per_channel_irq) ? &channel->napi : &pdata->napi; @@ -1926,9 +1944,8 @@ read_again: received++; ring->cur++; - incomplete = XGMAC_GET_BITS(packet->attributes, - RX_PACKET_ATTRIBUTES, - INCOMPLETE); + last = XGMAC_GET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + LAST); context_next = XGMAC_GET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, CONTEXT_NEXT); @@ -1937,7 +1954,7 @@ read_again: CONTEXT); /* Earlier error, just drain the remaining data */ - if ((incomplete || context_next) && error) + if ((!last || context_next) && error) goto read_again; if (error || packet->errors) { @@ -1949,16 +1966,22 @@ read_again: } if (!context) { - /* Length is cumulative, get this descriptor's length */ - rdesc_len = rdata->rx.len - len; - len += rdesc_len; + /* Get the data length in the descriptor buffers */ + buf1_len = xgbe_rx_buf1_len(rdata, packet); + len += buf1_len; + buf2_len = xgbe_rx_buf2_len(rdata, packet, len); + len += buf2_len; - if (rdesc_len && !skb) { + if (!skb) { skb = xgbe_create_skb(pdata, napi, rdata, - rdesc_len); - if (!skb) + buf1_len); + if (!skb) { error = 1; - } else if (rdesc_len) { + goto skip_data; + } + } + + if (buf2_len) { dma_sync_single_range_for_cpu(pdata->dev, rdata->rx.buf.dma_base, rdata->rx.buf.dma_off, @@ -1968,13 +1991,14 @@ read_again: skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, rdata->rx.buf.pa.pages, rdata->rx.buf.pa.pages_offset, - rdesc_len, + buf2_len, rdata->rx.buf.dma_len); rdata->rx.buf.pa.pages = NULL; } } - if (incomplete || context_next) +skip_data: + if (!last || context_next) goto read_again; if (!skb) @@ -2033,7 +2057,7 @@ next_packet: } /* Check if we need to save state before leaving */ - if (received && (incomplete || context_next)) { + if (received && (!last || context_next)) { rdata = XGBE_GET_DESC_DATA(ring, ring->cur); rdata->state_saved = 1; rdata->state.skb = skb; diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 91627561c58d..f971d92f7b41 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -3495,7 +3495,8 @@ static int bcmgenet_suspend(struct device *d) bcmgenet_netif_stop(dev); - phy_suspend(priv->phydev); + if (!device_may_wakeup(d)) + phy_suspend(priv->phydev); netif_device_detach(dev); @@ -3592,7 +3593,8 @@ static int bcmgenet_resume(struct device *d) netif_device_attach(dev); - phy_resume(priv->phydev); + if (!device_may_wakeup(d)) + phy_resume(priv->phydev); if (priv->eee.eee_enabled) bcmgenet_eee_enable_set(dev, true); diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c index 8bdfe53754ba..e96d1f95bb47 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmmii.c +++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c @@ -220,20 +220,6 @@ void bcmgenet_phy_power_set(struct net_device *dev, bool enable) udelay(60); } -static void bcmgenet_internal_phy_setup(struct net_device *dev) -{ - struct bcmgenet_priv *priv = netdev_priv(dev); - u32 reg; - - /* Power up PHY */ - bcmgenet_phy_power_set(dev, true); - /* enable APD */ - reg = bcmgenet_ext_readl(priv, EXT_EXT_PWR_MGMT); - reg |= EXT_PWR_DN_EN_LD; - bcmgenet_ext_writel(priv, reg, EXT_EXT_PWR_MGMT); - bcmgenet_mii_reset(dev); -} - static void bcmgenet_moca_phy_setup(struct bcmgenet_priv *priv) { u32 reg; @@ -281,7 +267,6 @@ int bcmgenet_mii_config(struct net_device *dev) if (priv->internal_phy) { phy_name = "internal PHY"; - bcmgenet_internal_phy_setup(dev); } else if (priv->phy_interface == PHY_INTERFACE_MODE_MOCA) { phy_name = "MoCA"; bcmgenet_moca_phy_setup(priv); diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 7445da218bd9..cc1725616f9d 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -2823,7 +2823,7 @@ static int liquidio_xmit(struct sk_buff *skb, struct net_device *netdev) if (!g) { netif_info(lio, tx_err, lio->netdev, "Transmit scatter gather: glist null!\n"); - goto lio_xmit_failed; + goto lio_xmit_dma_failed; } cmdsetup.s.gather = 1; @@ -2894,7 +2894,7 @@ static int liquidio_xmit(struct sk_buff *skb, struct net_device *netdev) else status = octnet_send_nic_data_pkt(oct, &ndata, xmit_more); if (status == IQ_SEND_FAILED) - goto lio_xmit_failed; + goto lio_xmit_dma_failed; netif_info(lio, tx_queued, lio->netdev, "Transmit queued successfully\n"); @@ -2908,12 +2908,13 @@ static int liquidio_xmit(struct sk_buff *skb, struct net_device *netdev) return NETDEV_TX_OK; +lio_xmit_dma_failed: + dma_unmap_single(&oct->pci_dev->dev, ndata.cmd.dptr, + ndata.datasize, DMA_TO_DEVICE); lio_xmit_failed: stats->tx_dropped++; netif_info(lio, tx_err, lio->netdev, "IQ%d Transmit dropped:%llu\n", iq_no, stats->tx_dropped); - dma_unmap_single(&oct->pci_dev->dev, ndata.cmd.dptr, - ndata.datasize, DMA_TO_DEVICE); recv_buffer_free(skb); return NETDEV_TX_OK; } diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c index 7af870a3c549..f9e4988ea30e 100644 --- a/drivers/net/ethernet/ibm/ibmveth.c +++ b/drivers/net/ethernet/ibm/ibmveth.c @@ -58,7 +58,7 @@ static struct kobj_type ktype_veth_pool; static const char ibmveth_driver_name[] = "ibmveth"; static const char ibmveth_driver_string[] = "IBM Power Virtual Ethernet Driver"; -#define ibmveth_driver_version "1.05" +#define ibmveth_driver_version "1.06" MODULE_AUTHOR("Santiago Leon <santil@linux.vnet.ibm.com>"); MODULE_DESCRIPTION("IBM Power Virtual Ethernet Driver"); @@ -137,6 +137,11 @@ static inline int ibmveth_rxq_frame_offset(struct ibmveth_adapter *adapter) return ibmveth_rxq_flags(adapter) & IBMVETH_RXQ_OFF_MASK; } +static inline int ibmveth_rxq_large_packet(struct ibmveth_adapter *adapter) +{ + return ibmveth_rxq_flags(adapter) & IBMVETH_RXQ_LRG_PKT; +} + static inline int ibmveth_rxq_frame_length(struct ibmveth_adapter *adapter) { return be32_to_cpu(adapter->rx_queue.queue_addr[adapter->rx_queue.index].length); @@ -1172,6 +1177,53 @@ map_failed: goto retry_bounce; } +static void ibmveth_rx_mss_helper(struct sk_buff *skb, u16 mss, int lrg_pkt) +{ + struct tcphdr *tcph; + int offset = 0; + int hdr_len; + + /* only TCP packets will be aggregated */ + if (skb->protocol == htons(ETH_P_IP)) { + struct iphdr *iph = (struct iphdr *)skb->data; + + if (iph->protocol == IPPROTO_TCP) { + offset = iph->ihl * 4; + skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; + } else { + return; + } + } else if (skb->protocol == htons(ETH_P_IPV6)) { + struct ipv6hdr *iph6 = (struct ipv6hdr *)skb->data; + + if (iph6->nexthdr == IPPROTO_TCP) { + offset = sizeof(struct ipv6hdr); + skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6; + } else { + return; + } + } else { + return; + } + /* if mss is not set through Large Packet bit/mss in rx buffer, + * expect that the mss will be written to the tcp header checksum. + */ + tcph = (struct tcphdr *)(skb->data + offset); + if (lrg_pkt) { + skb_shinfo(skb)->gso_size = mss; + } else if (offset) { + skb_shinfo(skb)->gso_size = ntohs(tcph->check); + tcph->check = 0; + } + + if (skb_shinfo(skb)->gso_size) { + hdr_len = offset + tcph->doff * 4; + skb_shinfo(skb)->gso_segs = + DIV_ROUND_UP(skb->len - hdr_len, + skb_shinfo(skb)->gso_size); + } +} + static int ibmveth_poll(struct napi_struct *napi, int budget) { struct ibmveth_adapter *adapter = @@ -1180,6 +1232,7 @@ static int ibmveth_poll(struct napi_struct *napi, int budget) int frames_processed = 0; unsigned long lpar_rc; struct iphdr *iph; + u16 mss = 0; restart_poll: while (frames_processed < budget) { @@ -1197,9 +1250,21 @@ restart_poll: int length = ibmveth_rxq_frame_length(adapter); int offset = ibmveth_rxq_frame_offset(adapter); int csum_good = ibmveth_rxq_csum_good(adapter); + int lrg_pkt = ibmveth_rxq_large_packet(adapter); skb = ibmveth_rxq_get_buffer(adapter); + /* if the large packet bit is set in the rx queue + * descriptor, the mss will be written by PHYP eight + * bytes from the start of the rx buffer, which is + * skb->data at this stage + */ + if (lrg_pkt) { + __be64 *rxmss = (__be64 *)(skb->data + 8); + + mss = (u16)be64_to_cpu(*rxmss); + } + new_skb = NULL; if (length < rx_copybreak) new_skb = netdev_alloc_skb(netdev, length); @@ -1233,11 +1298,15 @@ restart_poll: if (iph->check == 0xffff) { iph->check = 0; iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); - adapter->rx_large_packets++; } } } + if (length > netdev->mtu + ETH_HLEN) { + ibmveth_rx_mss_helper(skb, mss, lrg_pkt); + adapter->rx_large_packets++; + } + napi_gro_receive(napi, skb); /* send it up */ netdev->stats.rx_packets++; diff --git a/drivers/net/ethernet/ibm/ibmveth.h b/drivers/net/ethernet/ibm/ibmveth.h index 4eade67fe30c..7acda04d034e 100644 --- a/drivers/net/ethernet/ibm/ibmveth.h +++ b/drivers/net/ethernet/ibm/ibmveth.h @@ -209,6 +209,7 @@ struct ibmveth_rx_q_entry { #define IBMVETH_RXQ_TOGGLE 0x80000000 #define IBMVETH_RXQ_TOGGLE_SHIFT 31 #define IBMVETH_RXQ_VALID 0x40000000 +#define IBMVETH_RXQ_LRG_PKT 0x04000000 #define IBMVETH_RXQ_NO_CSUM 0x02000000 #define IBMVETH_RXQ_CSUM_GOOD 0x01000000 #define IBMVETH_RXQ_OFF_MASK 0x0000FFFF diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.c b/drivers/net/ethernet/intel/igb/e1000_phy.c index 23ec28f43f6d..afaa98d1d4e4 100644 --- a/drivers/net/ethernet/intel/igb/e1000_phy.c +++ b/drivers/net/ethernet/intel/igb/e1000_phy.c @@ -77,6 +77,10 @@ s32 igb_get_phy_id(struct e1000_hw *hw) s32 ret_val = 0; u16 phy_id; + /* ensure PHY page selection to fix misconfigured i210 */ + if ((hw->mac.type == e1000_i210) || (hw->mac.type == e1000_i211)) + phy->ops.write_reg(hw, I347AT4_PAGE_SELECT, 0); + ret_val = phy->ops.read_reg(hw, PHY_ID1, &phy_id); if (ret_val) goto out; diff --git a/drivers/net/ethernet/mellanox/mlx4/cq.c b/drivers/net/ethernet/mellanox/mlx4/cq.c index 3348e646db70..6eba58044456 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cq.c +++ b/drivers/net/ethernet/mellanox/mlx4/cq.c @@ -101,13 +101,19 @@ void mlx4_cq_completion(struct mlx4_dev *dev, u32 cqn) { struct mlx4_cq *cq; + rcu_read_lock(); cq = radix_tree_lookup(&mlx4_priv(dev)->cq_table.tree, cqn & (dev->caps.num_cqs - 1)); + rcu_read_unlock(); + if (!cq) { mlx4_dbg(dev, "Completion event for bogus CQ %08x\n", cqn); return; } + /* Acessing the CQ outside of rcu_read_lock is safe, because + * the CQ is freed only after interrupt handling is completed. + */ ++cq->arm_sn; cq->comp(cq); @@ -118,23 +124,19 @@ void mlx4_cq_event(struct mlx4_dev *dev, u32 cqn, int event_type) struct mlx4_cq_table *cq_table = &mlx4_priv(dev)->cq_table; struct mlx4_cq *cq; - spin_lock(&cq_table->lock); - + rcu_read_lock(); cq = radix_tree_lookup(&cq_table->tree, cqn & (dev->caps.num_cqs - 1)); - if (cq) - atomic_inc(&cq->refcount); - - spin_unlock(&cq_table->lock); + rcu_read_unlock(); if (!cq) { - mlx4_warn(dev, "Async event for bogus CQ %08x\n", cqn); + mlx4_dbg(dev, "Async event for bogus CQ %08x\n", cqn); return; } + /* Acessing the CQ outside of rcu_read_lock is safe, because + * the CQ is freed only after interrupt handling is completed. + */ cq->event(cq, event_type); - - if (atomic_dec_and_test(&cq->refcount)) - complete(&cq->free); } static int mlx4_SW2HW_CQ(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox, @@ -301,9 +303,9 @@ int mlx4_cq_alloc(struct mlx4_dev *dev, int nent, if (err) return err; - spin_lock_irq(&cq_table->lock); + spin_lock(&cq_table->lock); err = radix_tree_insert(&cq_table->tree, cq->cqn, cq); - spin_unlock_irq(&cq_table->lock); + spin_unlock(&cq_table->lock); if (err) goto err_icm; @@ -347,9 +349,9 @@ int mlx4_cq_alloc(struct mlx4_dev *dev, int nent, return 0; err_radix: - spin_lock_irq(&cq_table->lock); + spin_lock(&cq_table->lock); radix_tree_delete(&cq_table->tree, cq->cqn); - spin_unlock_irq(&cq_table->lock); + spin_unlock(&cq_table->lock); err_icm: mlx4_cq_free_icm(dev, cq->cqn); @@ -368,15 +370,15 @@ void mlx4_cq_free(struct mlx4_dev *dev, struct mlx4_cq *cq) if (err) mlx4_warn(dev, "HW2SW_CQ failed (%d) for CQN %06x\n", err, cq->cqn); + spin_lock(&cq_table->lock); + radix_tree_delete(&cq_table->tree, cq->cqn); + spin_unlock(&cq_table->lock); + synchronize_irq(priv->eq_table.eq[MLX4_CQ_TO_EQ_VECTOR(cq->vector)].irq); if (priv->eq_table.eq[MLX4_CQ_TO_EQ_VECTOR(cq->vector)].irq != priv->eq_table.eq[MLX4_EQ_ASYNC].irq) synchronize_irq(priv->eq_table.eq[MLX4_EQ_ASYNC].irq); - spin_lock_irq(&cq_table->lock); - radix_tree_delete(&cq_table->tree, cq->cqn); - spin_unlock_irq(&cq_table->lock); - if (atomic_dec_and_test(&cq->refcount)) complete(&cq->free); wait_for_completion(&cq->free); diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index 28a4b34310b2..82bf1b539d87 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -439,8 +439,14 @@ int mlx4_en_activate_rx_rings(struct mlx4_en_priv *priv) ring->cqn = priv->rx_cq[ring_ind]->mcq.cqn; ring->stride = stride; - if (ring->stride <= TXBB_SIZE) + if (ring->stride <= TXBB_SIZE) { + /* Stamp first unused send wqe */ + __be32 *ptr = (__be32 *)ring->buf; + __be32 stamp = cpu_to_be32(1 << STAMP_SHIFT); + *ptr = stamp; + /* Move pointer to start of rx section */ ring->buf += TXBB_SIZE; + } ring->log_stride = ffs(ring->stride) - 1; ring->buf_size = ring->size * ring->stride; diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index d314d96dcb1c..d1fc7fa87b05 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -2955,6 +2955,9 @@ int mlx4_RST2INIT_QP_wrapper(struct mlx4_dev *dev, int slave, put_res(dev, slave, srqn, RES_SRQ); qp->srq = srq; } + + /* Save param3 for dynamic changes from VST back to VGT */ + qp->param3 = qpc->param3; put_res(dev, slave, rcqn, RES_CQ); put_res(dev, slave, mtt_base, RES_MTT); res_end_move(dev, slave, RES_QP, qpn); @@ -3747,7 +3750,6 @@ int mlx4_INIT2RTR_QP_wrapper(struct mlx4_dev *dev, int slave, int qpn = vhcr->in_modifier & 0x7fffff; struct res_qp *qp; u8 orig_sched_queue; - __be32 orig_param3 = qpc->param3; u8 orig_vlan_control = qpc->pri_path.vlan_control; u8 orig_fvl_rx = qpc->pri_path.fvl_rx; u8 orig_pri_path_fl = qpc->pri_path.fl; @@ -3789,7 +3791,6 @@ out: */ if (!err) { qp->sched_queue = orig_sched_queue; - qp->param3 = orig_param3; qp->vlan_control = orig_vlan_control; qp->fvl_rx = orig_fvl_rx; qp->pri_path_fl = orig_pri_path_fl; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index cf0098596e85..e9408f5e2a1d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -197,6 +197,10 @@ static inline void mlx5e_build_rx_skb(struct mlx5_cqe64 *cqe, if (lro_num_seg > 1) { mlx5e_lro_update_hdr(skb, cqe); skb_shinfo(skb)->gso_size = DIV_ROUND_UP(cqe_bcnt, lro_num_seg); + /* Subtract one since we already counted this as one + * "regular" packet in mlx5e_complete_rx_cqe() + */ + rq->stats.packets += lro_num_seg - 1; rq->stats.lro_packets++; rq->stats.lro_bytes += cqe_bcnt; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index ba115ec7aa92..1e611980cf99 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -85,7 +85,7 @@ static struct mlx5_profile profile[] = { [2] = { .mask = MLX5_PROF_MASK_QP_SIZE | MLX5_PROF_MASK_MR_CACHE, - .log_max_qp = 17, + .log_max_qp = 18, .mr_cache[0] = { .size = 500, .limit = 250 diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index e8a09ff9e724..c8a7802d2953 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -197,65 +197,6 @@ static void *init_ppi_data(struct rndis_message *msg, u32 ppi_size, return ppi; } -union sub_key { - u64 k; - struct { - u8 pad[3]; - u8 kb; - u32 ka; - }; -}; - -/* Toeplitz hash function - * data: network byte order - * return: host byte order - */ -static u32 comp_hash(u8 *key, int klen, void *data, int dlen) -{ - union sub_key subk; - int k_next = 4; - u8 dt; - int i, j; - u32 ret = 0; - - subk.k = 0; - subk.ka = ntohl(*(u32 *)key); - - for (i = 0; i < dlen; i++) { - subk.kb = key[k_next]; - k_next = (k_next + 1) % klen; - dt = ((u8 *)data)[i]; - for (j = 0; j < 8; j++) { - if (dt & 0x80) - ret ^= subk.ka; - dt <<= 1; - subk.k <<= 1; - } - } - - return ret; -} - -static bool netvsc_set_hash(u32 *hash, struct sk_buff *skb) -{ - struct flow_keys flow; - int data_len; - - if (!skb_flow_dissect_flow_keys(skb, &flow, 0) || - !(flow.basic.n_proto == htons(ETH_P_IP) || - flow.basic.n_proto == htons(ETH_P_IPV6))) - return false; - - if (flow.basic.ip_proto == IPPROTO_TCP) - data_len = 12; - else - data_len = 8; - - *hash = comp_hash(netvsc_hash_key, HASH_KEYLEN, &flow, data_len); - - return true; -} - static u16 netvsc_select_queue(struct net_device *ndev, struct sk_buff *skb, void *accel_priv, select_queue_fallback_t fallback) { @@ -268,11 +209,9 @@ static u16 netvsc_select_queue(struct net_device *ndev, struct sk_buff *skb, if (nvsc_dev == NULL || ndev->real_num_tx_queues <= 1) return 0; - if (netvsc_set_hash(&hash, skb)) { - q_idx = nvsc_dev->send_table[hash % VRSS_SEND_TAB_SIZE] % - ndev->real_num_tx_queues; - skb_set_hash(skb, hash, PKT_HASH_TYPE_L3); - } + hash = skb_get_hash(skb); + q_idx = nvsc_dev->send_table[hash % VRSS_SEND_TAB_SIZE] % + ndev->real_num_tx_queues; return q_idx; } diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 06c8bfeaccd6..40cd86614677 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -1110,6 +1110,7 @@ static int macvlan_port_create(struct net_device *dev) static void macvlan_port_destroy(struct net_device *dev) { struct macvlan_port *port = macvlan_port_get_rtnl(dev); + struct sk_buff *skb; dev->priv_flags &= ~IFF_MACVLAN_PORT; netdev_rx_handler_unregister(dev); @@ -1118,7 +1119,15 @@ static void macvlan_port_destroy(struct net_device *dev) * but we need to cancel it and purge left skbs if any. */ cancel_work_sync(&port->bc_work); - __skb_queue_purge(&port->bc_queue); + + while ((skb = __skb_dequeue(&port->bc_queue))) { + const struct macvlan_dev *src = MACVLAN_SKB_CB(skb)->src; + + if (src) + dev_put(src->dev); + + kfree_skb(skb); + } kfree_rcu(port, rcu); } diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c index e6cefd0e3262..84b9cca152eb 100644 --- a/drivers/net/phy/dp83640.c +++ b/drivers/net/phy/dp83640.c @@ -1436,8 +1436,6 @@ static bool dp83640_rxtstamp(struct phy_device *phydev, skb_info->tmo = jiffies + SKB_TIMESTAMP_TIMEOUT; skb_queue_tail(&dp83640->rx_queue, skb); schedule_delayed_work(&dp83640->ts_work, SKB_TIMESTAMP_TIMEOUT); - } else { - netif_rx_ni(skb); } return true; diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index bba0ca786aaa..851c0e121807 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -538,7 +538,7 @@ void phy_stop_machine(struct phy_device *phydev) cancel_delayed_work_sync(&phydev->state_queue); mutex_lock(&phydev->lock); - if (phydev->state > PHY_UP) + if (phydev->state > PHY_UP && phydev->state != PHY_HALTED) phydev->state = PHY_UP; mutex_unlock(&phydev->lock); } diff --git a/drivers/net/ppp/pppolac.c b/drivers/net/ppp/pppolac.c index 0184c96579e9..3a45cf805288 100644 --- a/drivers/net/ppp/pppolac.c +++ b/drivers/net/ppp/pppolac.c @@ -206,7 +206,9 @@ static void pppolac_xmit_core(struct work_struct *delivery_work) while ((skb = skb_dequeue(&delivery_queue))) { struct sock *sk_udp = skb->sk; struct kvec iov = {.iov_base = skb->data, .iov_len = skb->len}; - struct msghdr msg = { 0 }; + struct msghdr msg = { + .msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT, + }; iov_iter_kvec(&msg.msg_iter, WRITE | ITER_KVEC, &iov, 1, skb->len); diff --git a/drivers/net/ppp/pppopns.c b/drivers/net/ppp/pppopns.c index d9e06039794e..cdb4fa1af734 100644 --- a/drivers/net/ppp/pppopns.c +++ b/drivers/net/ppp/pppopns.c @@ -189,7 +189,9 @@ static void pppopns_xmit_core(struct work_struct *delivery_work) while ((skb = skb_dequeue(&delivery_queue))) { struct sock *sk_raw = skb->sk; struct kvec iov = {.iov_base = skb->data, .iov_len = skb->len}; - struct msghdr msg = { 0 }; + struct msghdr msg = { + .msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT, + }; iov_iter_kvec(&msg.msg_iter, WRITE | ITER_KVEC, &iov, 1, skb->len); diff --git a/drivers/net/usb/catc.c b/drivers/net/usb/catc.c index 4e2b26a88b15..2aa1a1d29cb4 100644 --- a/drivers/net/usb/catc.c +++ b/drivers/net/usb/catc.c @@ -777,7 +777,7 @@ static int catc_probe(struct usb_interface *intf, const struct usb_device_id *id struct net_device *netdev; struct catc *catc; u8 broadcast[ETH_ALEN]; - int i, pktsz; + int pktsz, ret; if (usb_set_interface(usbdev, intf->altsetting->desc.bInterfaceNumber, 1)) { @@ -812,12 +812,8 @@ static int catc_probe(struct usb_interface *intf, const struct usb_device_id *id if ((!catc->ctrl_urb) || (!catc->tx_urb) || (!catc->rx_urb) || (!catc->irq_urb)) { dev_err(&intf->dev, "No free urbs available.\n"); - usb_free_urb(catc->ctrl_urb); - usb_free_urb(catc->tx_urb); - usb_free_urb(catc->rx_urb); - usb_free_urb(catc->irq_urb); - free_netdev(netdev); - return -ENOMEM; + ret = -ENOMEM; + goto fail_free; } /* The F5U011 has the same vendor/product as the netmate but a device version of 0x130 */ @@ -845,15 +841,24 @@ static int catc_probe(struct usb_interface *intf, const struct usb_device_id *id catc->irq_buf, 2, catc_irq_done, catc, 1); if (!catc->is_f5u011) { + u32 *buf; + int i; + dev_dbg(dev, "Checking memory size\n"); - i = 0x12345678; - catc_write_mem(catc, 0x7a80, &i, 4); - i = 0x87654321; - catc_write_mem(catc, 0xfa80, &i, 4); - catc_read_mem(catc, 0x7a80, &i, 4); + buf = kmalloc(4, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto fail_free; + } + + *buf = 0x12345678; + catc_write_mem(catc, 0x7a80, buf, 4); + *buf = 0x87654321; + catc_write_mem(catc, 0xfa80, buf, 4); + catc_read_mem(catc, 0x7a80, buf, 4); - switch (i) { + switch (*buf) { case 0x12345678: catc_set_reg(catc, TxBufCount, 8); catc_set_reg(catc, RxBufCount, 32); @@ -868,6 +873,8 @@ static int catc_probe(struct usb_interface *intf, const struct usb_device_id *id dev_dbg(dev, "32k Memory\n"); break; } + + kfree(buf); dev_dbg(dev, "Getting MAC from SEEROM.\n"); @@ -914,16 +921,21 @@ static int catc_probe(struct usb_interface *intf, const struct usb_device_id *id usb_set_intfdata(intf, catc); SET_NETDEV_DEV(netdev, &intf->dev); - if (register_netdev(netdev) != 0) { - usb_set_intfdata(intf, NULL); - usb_free_urb(catc->ctrl_urb); - usb_free_urb(catc->tx_urb); - usb_free_urb(catc->rx_urb); - usb_free_urb(catc->irq_urb); - free_netdev(netdev); - return -EIO; - } + ret = register_netdev(netdev); + if (ret) + goto fail_clear_intfdata; + return 0; + +fail_clear_intfdata: + usb_set_intfdata(intf, NULL); +fail_free: + usb_free_urb(catc->ctrl_urb); + usb_free_urb(catc->tx_urb); + usb_free_urb(catc->rx_urb); + usb_free_urb(catc->irq_urb); + free_netdev(netdev); + return ret; } static void catc_disconnect(struct usb_interface *intf) diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c index f84080215915..17fac0121e56 100644 --- a/drivers/net/usb/pegasus.c +++ b/drivers/net/usb/pegasus.c @@ -126,40 +126,61 @@ static void async_ctrl_callback(struct urb *urb) static int get_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data) { + u8 *buf; int ret; + buf = kmalloc(size, GFP_NOIO); + if (!buf) + return -ENOMEM; + ret = usb_control_msg(pegasus->usb, usb_rcvctrlpipe(pegasus->usb, 0), PEGASUS_REQ_GET_REGS, PEGASUS_REQT_READ, 0, - indx, data, size, 1000); + indx, buf, size, 1000); if (ret < 0) netif_dbg(pegasus, drv, pegasus->net, "%s returned %d\n", __func__, ret); + else if (ret <= size) + memcpy(data, buf, ret); + kfree(buf); return ret; } -static int set_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data) +static int set_registers(pegasus_t *pegasus, __u16 indx, __u16 size, + const void *data) { + u8 *buf; int ret; + buf = kmemdup(data, size, GFP_NOIO); + if (!buf) + return -ENOMEM; + ret = usb_control_msg(pegasus->usb, usb_sndctrlpipe(pegasus->usb, 0), PEGASUS_REQ_SET_REGS, PEGASUS_REQT_WRITE, 0, - indx, data, size, 100); + indx, buf, size, 100); if (ret < 0) netif_dbg(pegasus, drv, pegasus->net, "%s returned %d\n", __func__, ret); + kfree(buf); return ret; } static int set_register(pegasus_t *pegasus, __u16 indx, __u8 data) { + u8 *buf; int ret; + buf = kmemdup(&data, 1, GFP_NOIO); + if (!buf) + return -ENOMEM; + ret = usb_control_msg(pegasus->usb, usb_sndctrlpipe(pegasus->usb, 0), PEGASUS_REQ_SET_REG, PEGASUS_REQT_WRITE, data, - indx, &data, 1, 1000); + indx, buf, 1, 1000); if (ret < 0) netif_dbg(pegasus, drv, pegasus->net, "%s returned %d\n", __func__, ret); + kfree(buf); return ret; } diff --git a/drivers/net/usb/rtl8150.c b/drivers/net/usb/rtl8150.c index d37b7dce2d40..39672984dde1 100644 --- a/drivers/net/usb/rtl8150.c +++ b/drivers/net/usb/rtl8150.c @@ -155,16 +155,36 @@ static const char driver_name [] = "rtl8150"; */ static int get_registers(rtl8150_t * dev, u16 indx, u16 size, void *data) { - return usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), - RTL8150_REQ_GET_REGS, RTL8150_REQT_READ, - indx, 0, data, size, 500); + void *buf; + int ret; + + buf = kmalloc(size, GFP_NOIO); + if (!buf) + return -ENOMEM; + + ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), + RTL8150_REQ_GET_REGS, RTL8150_REQT_READ, + indx, 0, buf, size, 500); + if (ret > 0 && ret <= size) + memcpy(data, buf, ret); + kfree(buf); + return ret; } -static int set_registers(rtl8150_t * dev, u16 indx, u16 size, void *data) +static int set_registers(rtl8150_t * dev, u16 indx, u16 size, const void *data) { - return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), - RTL8150_REQ_SET_REGS, RTL8150_REQT_WRITE, - indx, 0, data, size, 500); + void *buf; + int ret; + + buf = kmemdup(data, size, GFP_NOIO); + if (!buf) + return -ENOMEM; + + ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), + RTL8150_REQ_SET_REGS, RTL8150_REQT_WRITE, + indx, 0, buf, size, 500); + kfree(buf); + return ret; } static void async_set_reg_cb(struct urb *urb) diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index d6b619667f1a..349aecbc210a 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -345,6 +345,7 @@ static netdev_tx_t is_ip_tx_frame(struct sk_buff *skb, struct net_device *dev) static netdev_tx_t vrf_xmit(struct sk_buff *skb, struct net_device *dev) { + int len = skb->len; netdev_tx_t ret = is_ip_tx_frame(skb, dev); if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) { @@ -352,7 +353,7 @@ static netdev_tx_t vrf_xmit(struct sk_buff *skb, struct net_device *dev) u64_stats_update_begin(&dstats->syncp); dstats->tx_pkts++; - dstats->tx_bytes += skb->len; + dstats->tx_bytes += len; u64_stats_update_end(&dstats->syncp); } else { this_cpu_inc(dev->dstats->tx_drps); diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 6fa8e165878e..590750ab6564 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -2600,7 +2600,7 @@ static int vxlan_validate(struct nlattr *tb[], struct nlattr *data[]) if (data[IFLA_VXLAN_ID]) { __u32 id = nla_get_u32(data[IFLA_VXLAN_ID]); - if (id >= VXLAN_VID_MASK) + if (id >= VXLAN_N_VID) return -ERANGE; } diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c index 84a9b1a9577c..9bab797dcdbc 100644 --- a/drivers/net/wireless/ath/ath10k/qmi.c +++ b/drivers/net/wireless/ath/ath10k/qmi.c @@ -13,6 +13,7 @@ #include <soc/qcom/subsystem_restart.h> #include <soc/qcom/service-notifier.h> #include <soc/qcom/msm_qmi_interface.h> +#include <soc/qcom/icnss.h> #include <soc/qcom/service-locator.h> #include "core.h" #include "qmi.h" @@ -448,6 +449,7 @@ int ath10k_snoc_qmi_wlan_enable(struct ath10k *ar, int ret; struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); struct ath10k_snoc_qmi_config *qmi_cfg = &ar_snoc->qmi_cfg; + unsigned long time_left; ath10k_dbg(ar, ATH10K_DBG_SNOC, "Mode: %d, config: %p, host_version: %s\n", @@ -461,10 +463,15 @@ int ath10k_snoc_qmi_wlan_enable(struct ath10k *ar, return ret; } - wait_event_timeout(ath10k_fw_ready_wait_event, + time_left = wait_event_timeout( + ath10k_fw_ready_wait_event, (atomic_read(&qmi_cfg->fw_ready) && atomic_read(&qmi_cfg->server_connected)), msecs_to_jiffies(ATH10K_SNOC_WLAN_FW_READY_TIMEOUT)); + if (time_left == 0) { + ath10k_err(ar, "Wait for FW ready and server connect timed out\n"); + return -ETIMEDOUT; + } req.host_version_valid = 1; strlcpy(req.host_version, host_version, @@ -854,9 +861,21 @@ int ath10k_snoc_start_qmi_service(struct ath10k *ar) goto out_destroy_wq; } + if (!icnss_is_fw_ready()) { + ath10k_err(ar, "failed to get fw ready indication\n"); + ret = -EFAULT; + goto err_fw_ready; + } + + atomic_set(&qmi_cfg->fw_ready, 1); ath10k_dbg(ar, ATH10K_DBG_SNOC, "QMI service started successfully\n"); return 0; +err_fw_ready: + qmi_svc_event_notifier_unregister(WLFW_SERVICE_ID_V01, + WLFW_SERVICE_VERS_V01, + WLFW_SERVICE_INS_ID_V01, + &qmi_cfg->wlfw_clnt_nb); out_destroy_wq: destroy_workqueue(qmi_cfg->event_wq); return ret; diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c index 2cbc8ee9abf9..08618cedf775 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.c +++ b/drivers/net/wireless/ath/ath10k/snoc.c @@ -1329,6 +1329,8 @@ static int ath10k_snoc_remove(struct platform_device *pdev) if (!ar_snoc) return -EINVAL; + ath10k_dbg(ar, ATH10K_DBG_SNOC, "%s:WCN3990 removed\n", __func__); + ath10k_core_unregister(ar); ath10k_snoc_pdr_unregister_notifier(ar); ath10k_snoc_modem_ssr_unregister_notifier(ar); @@ -1338,8 +1340,6 @@ static int ath10k_snoc_remove(struct platform_device *pdev) ath10k_snoc_stop_qmi_service(ar); ath10k_core_destroy(ar); - ath10k_dbg(ar, ATH10K_DBG_SNOC, "%s:WCN3990 removed\n", __func__); - return 0; } diff --git a/drivers/net/wireless/ath/ath9k/common-spectral.c b/drivers/net/wireless/ath/ath9k/common-spectral.c index a8762711ad74..03945731eb65 100644 --- a/drivers/net/wireless/ath/ath9k/common-spectral.c +++ b/drivers/net/wireless/ath/ath9k/common-spectral.c @@ -528,6 +528,9 @@ int ath_cmn_process_fft(struct ath_spec_scan_priv *spec_priv, struct ieee80211_h if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK)) return 0; + if (!spec_priv->rfs_chan_spec_scan) + return 1; + /* Output buffers are full, no need to process anything * since there is no space to put the result anyway */ @@ -1072,7 +1075,7 @@ static struct rchan_callbacks rfs_spec_scan_cb = { void ath9k_cmn_spectral_deinit_debug(struct ath_spec_scan_priv *spec_priv) { - if (config_enabled(CONFIG_ATH9K_DEBUGFS)) { + if (config_enabled(CONFIG_ATH9K_DEBUGFS) && spec_priv->rfs_chan_spec_scan) { relay_close(spec_priv->rfs_chan_spec_scan); spec_priv->rfs_chan_spec_scan = NULL; } @@ -1086,6 +1089,9 @@ void ath9k_cmn_spectral_init_debug(struct ath_spec_scan_priv *spec_priv, debugfs_phy, 1024, 256, &rfs_spec_scan_cb, NULL); + if (!spec_priv->rfs_chan_spec_scan) + return; + debugfs_create_file("spectral_scan_ctl", S_IRUSR | S_IWUSR, debugfs_phy, spec_priv, diff --git a/drivers/net/wireless/ath/wil6210/Kconfig b/drivers/net/wireless/ath/wil6210/Kconfig index 8f0bde5825d5..0e66348e7513 100644 --- a/drivers/net/wireless/ath/wil6210/Kconfig +++ b/drivers/net/wireless/ath/wil6210/Kconfig @@ -44,7 +44,7 @@ config WIL6210_TRACING config WIL6210_WRITE_IOCTL bool "wil6210 write ioctl to the device" depends on WIL6210 - default n + default y ---help--- Say Y here to allow write-access from user-space to the device memory through ioctl. This is useful for diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 37898146f01d..d68d6c926029 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -26,6 +26,10 @@ bool disable_ap_sme; module_param(disable_ap_sme, bool, 0444); MODULE_PARM_DESC(disable_ap_sme, " let user space handle AP mode SME"); +static bool ignore_reg_hints = true; +module_param(ignore_reg_hints, bool, 0444); +MODULE_PARM_DESC(ignore_reg_hints, " Ignore OTA regulatory hints (Default: true)"); + #define CHAN60G(_channel, _flags) { \ .band = IEEE80211_BAND_60GHZ, \ .center_freq = 56160 + (2160 * (_channel)), \ @@ -1650,12 +1654,6 @@ static int wil_cfg80211_set_power_mgmt(struct wiphy *wiphy, { struct wil6210_priv *wil = wiphy_to_wil(wiphy); enum wmi_ps_profile_type ps_profile; - int rc; - - if (!test_bit(WMI_FW_CAPABILITY_PS_CONFIG, wil->fw_capabilities)) { - wil_err(wil, "set_power_mgmt not supported\n"); - return -EOPNOTSUPP; - } wil_dbg_misc(wil, "enabled=%d, timeout=%d\n", enabled, timeout); @@ -1665,11 +1663,7 @@ static int wil_cfg80211_set_power_mgmt(struct wiphy *wiphy, else ps_profile = WMI_PS_PROFILE_TYPE_PS_DISABLED; - rc = wmi_ps_dev_profile_cfg(wil, ps_profile); - if (rc) - wil_err(wil, "wmi_ps_dev_profile_cfg failed (%d)\n", rc); - - return rc; + return wil_ps_update(wil, ps_profile); } static struct cfg80211_ops wil_cfg80211_ops = { @@ -1743,6 +1737,11 @@ static void wil_wiphy_init(struct wiphy *wiphy) wiphy->vendor_commands = wil_nl80211_vendor_commands; wiphy->vendor_events = wil_nl80211_vendor_events; wiphy->n_vendor_events = ARRAY_SIZE(wil_nl80211_vendor_events); + + if (ignore_reg_hints) { + wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS; + wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE; + } } struct wireless_dev *wil_cfg80211_init(struct device *dev) diff --git a/drivers/net/wireless/ath/wil6210/fw_inc.c b/drivers/net/wireless/ath/wil6210/fw_inc.c index f4901587c005..e01acac88825 100644 --- a/drivers/net/wireless/ath/wil6210/fw_inc.c +++ b/drivers/net/wireless/ath/wil6210/fw_inc.c @@ -554,5 +554,7 @@ bool wil_fw_verify_file_exists(struct wil6210_priv *wil, const char *name) rc = request_firmware(&fw, name, wil_to_dev(wil)); if (!rc) release_firmware(fw); - return rc != -ENOENT; + else + wil_dbg_fw(wil, "<%s> not available: %d\n", name, rc); + return !rc; } diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index fca8acffeed5..45a5fb6d23a0 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -576,6 +576,9 @@ int wil_priv_init(struct wil6210_priv *wil) if (rx_ring_overflow_thrsh == WIL6210_RX_HIGH_TRSH_INIT) rx_ring_overflow_thrsh = WIL6210_RX_HIGH_TRSH_DEFAULT; + + wil->ps_profile = WMI_PS_PROFILE_TYPE_DEFAULT; + return 0; out_wmi_wq: @@ -901,6 +904,24 @@ void wil_abort_scan(struct wil6210_priv *wil, bool sync) } } +int wil_ps_update(struct wil6210_priv *wil, enum wmi_ps_profile_type ps_profile) +{ + int rc; + + if (!test_bit(WMI_FW_CAPABILITY_PS_CONFIG, wil->fw_capabilities)) { + wil_err(wil, "set_power_mgmt not supported\n"); + return -EOPNOTSUPP; + } + + rc = wmi_ps_dev_profile_cfg(wil, ps_profile); + if (rc) + wil_err(wil, "wmi_ps_dev_profile_cfg failed (%d)\n", rc); + else + wil->ps_profile = ps_profile; + + return rc; +} + /* * We reset all the structures, and we reset the UMAC. * After calling this routine, you're expected to reload @@ -950,15 +971,15 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) /* Disable device led before reset*/ wmi_led_cfg(wil, false); + mutex_lock(&wil->p2p_wdev_mutex); + wil_abort_scan(wil, false); + mutex_unlock(&wil->p2p_wdev_mutex); + /* prevent NAPI from being scheduled and prevent wmi commands */ mutex_lock(&wil->wmi_mutex); bitmap_zero(wil->status, wil_status_last); mutex_unlock(&wil->wmi_mutex); - mutex_lock(&wil->p2p_wdev_mutex); - wil_abort_scan(wil, false); - mutex_unlock(&wil->p2p_wdev_mutex); - wil_mask_irq(wil); wmi_event_flush(wil); @@ -1035,6 +1056,9 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) return rc; } + if (wil->ps_profile != WMI_PS_PROFILE_TYPE_DEFAULT) + wil_ps_update(wil, wil->ps_profile); + if (wil->tt_data_set) wmi_set_tt_cfg(wil, &wil->tt_data); diff --git a/drivers/net/wireless/ath/wil6210/pmc.c b/drivers/net/wireless/ath/wil6210/pmc.c index b067fdf086d4..2e301b6b32a9 100644 --- a/drivers/net/wireless/ath/wil6210/pmc.c +++ b/drivers/net/wireless/ath/wil6210/pmc.c @@ -200,7 +200,7 @@ void wil_pmc_alloc(struct wil6210_priv *wil, release_pmc_skbs: wil_err(wil, "exit on error: Releasing skbs...\n"); - for (i = 0; pmc->descriptors[i].va && i < num_descriptors; i++) { + for (i = 0; i < num_descriptors && pmc->descriptors[i].va; i++) { dma_free_coherent(dev, descriptor_size, pmc->descriptors[i].va, @@ -283,7 +283,7 @@ void wil_pmc_free(struct wil6210_priv *wil, int send_pmc_cmd) int i; for (i = 0; - pmc->descriptors[i].va && i < pmc->num_descriptors; i++) { + i < pmc->num_descriptors && pmc->descriptors[i].va; i++) { dma_free_coherent(dev, pmc->descriptor_size, pmc->descriptors[i].va, diff --git a/drivers/net/wireless/ath/wil6210/rx_reorder.c b/drivers/net/wireless/ath/wil6210/rx_reorder.c index 7404b6f39c6a..a43cffcf1bbf 100644 --- a/drivers/net/wireless/ath/wil6210/rx_reorder.c +++ b/drivers/net/wireless/ath/wil6210/rx_reorder.c @@ -343,8 +343,16 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) wil_err(wil, "BACK requested unsupported ba_policy == 1\n"); status = WLAN_STATUS_INVALID_QOS_PARAM; } - if (status == WLAN_STATUS_SUCCESS) - agg_wsize = wil_agg_size(wil, req_agg_wsize); + if (status == WLAN_STATUS_SUCCESS) { + if (req_agg_wsize == 0) { + wil_dbg_misc(wil, "Suggest BACK wsize %d\n", + WIL_MAX_AGG_WSIZE); + agg_wsize = WIL_MAX_AGG_WSIZE; + } else { + agg_wsize = min_t(u16, + WIL_MAX_AGG_WSIZE, req_agg_wsize); + } + } rc = wmi_addba_rx_resp(wil, cid, tid, dialog_token, status, agg_amsdu, agg_wsize, agg_timeout); diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 8b5411e4dc34..35bbf3a91f3e 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -37,6 +37,10 @@ bool rx_align_2; module_param(rx_align_2, bool, 0444); MODULE_PARM_DESC(rx_align_2, " align Rx buffers on 4*n+2, default - no"); +bool rx_large_buf; +module_param(rx_large_buf, bool, 0444); +MODULE_PARM_DESC(rx_large_buf, " allocate 8KB RX buffers, default - no"); + static inline uint wil_rx_snaplen(void) { return rx_align_2 ? 6 : 0; @@ -255,7 +259,7 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring, u32 i, int headroom) { struct device *dev = wil_to_dev(wil); - unsigned int sz = mtu_max + ETH_HLEN + wil_rx_snaplen(); + unsigned int sz = wil->rx_buf_len + ETH_HLEN + wil_rx_snaplen(); struct vring_rx_desc dd, *d = ⅆ volatile struct vring_rx_desc *_d = &vring->va[i].rx; dma_addr_t pa; @@ -419,7 +423,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, struct sk_buff *skb; dma_addr_t pa; unsigned int snaplen = wil_rx_snaplen(); - unsigned int sz = mtu_max + ETH_HLEN + snaplen; + unsigned int sz = wil->rx_buf_len + ETH_HLEN + snaplen; u16 dmalen; u8 ftype; int cid; @@ -780,6 +784,20 @@ void wil_rx_handle(struct wil6210_priv *wil, int *quota) wil_rx_refill(wil, v->size); } +static void wil_rx_buf_len_init(struct wil6210_priv *wil) +{ + wil->rx_buf_len = rx_large_buf ? + WIL_MAX_ETH_MTU : TXRX_BUF_LEN_DEFAULT - WIL_MAX_MPDU_OVERHEAD; + if (mtu_max > wil->rx_buf_len) { + /* do not allow RX buffers to be smaller than mtu_max, for + * backward compatibility (mtu_max parameter was also used + * to support receiving large packets) + */ + wil_info(wil, "Override RX buffer to mtu_max(%d)\n", mtu_max); + wil->rx_buf_len = mtu_max; + } +} + int wil_rx_init(struct wil6210_priv *wil, u16 size) { struct vring *vring = &wil->vring_rx; @@ -792,6 +810,8 @@ int wil_rx_init(struct wil6210_priv *wil, u16 size) return -EINVAL; } + wil_rx_buf_len_init(wil); + vring->size = size; rc = wil_vring_alloc(wil, vring); if (rc) diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 0529d10a8268..eee528127e97 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -33,6 +33,7 @@ extern unsigned short rx_ring_overflow_thrsh; extern int agg_wsize; extern u32 vring_idle_trsh; extern bool rx_align_2; +extern bool rx_large_buf; extern bool debug_fw; extern bool disable_ap_sme; @@ -670,6 +671,7 @@ struct wil6210_priv { struct work_struct probe_client_worker; /* DMA related */ struct vring vring_rx; + unsigned int rx_buf_len; struct vring vring_tx[WIL6210_MAX_TX_RINGS]; struct vring_tx_data vring_tx_data[WIL6210_MAX_TX_RINGS]; u8 vring2cid_tid[WIL6210_MAX_TX_RINGS][2]; /* [0] - CID, [1] - TID */ @@ -705,6 +707,8 @@ struct wil6210_priv { /* High Access Latency Policy voting */ struct wil_halp halp; + enum wmi_ps_profile_type ps_profile; + struct wil_ftm_priv ftm; bool tt_data_set; struct wmi_tt_data tt_data; @@ -824,6 +828,8 @@ int wil_if_add(struct wil6210_priv *wil); void wil_if_remove(struct wil6210_priv *wil); int wil_priv_init(struct wil6210_priv *wil); void wil_priv_deinit(struct wil6210_priv *wil); +int wil_ps_update(struct wil6210_priv *wil, + enum wmi_ps_profile_type ps_profile); int wil_reset(struct wil6210_priv *wil, bool no_fw); void wil_fw_error_recovery(struct wil6210_priv *wil); void wil_set_recovery_state(struct wil6210_priv *wil, int state); diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 41afbdc34c18..97e22281314c 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -1436,7 +1436,8 @@ int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring) struct wmi_cfg_rx_chain_cmd cmd = { .action = WMI_RX_CHAIN_ADD, .rx_sw_ring = { - .max_mpdu_size = cpu_to_le16(wil_mtu2macbuf(mtu_max)), + .max_mpdu_size = cpu_to_le16( + wil_mtu2macbuf(wil->rx_buf_len)), .ring_mem_base = cpu_to_le64(vring->pa), .ring_size = cpu_to_le16(vring->size), }, diff --git a/drivers/net/wireless/cnss/Kconfig b/drivers/net/wireless/cnss/Kconfig index 4558dc30fec1..8e69a2b469b9 100644 --- a/drivers/net/wireless/cnss/Kconfig +++ b/drivers/net/wireless/cnss/Kconfig @@ -9,6 +9,15 @@ config CNSS This driver also adds support to integrate WLAN module to subsystem restart framework. +config CNSS_ASYNC + bool "Enable/disable cnss pci platform driver asynchronous probe" + depends on CNSS + ---help--- + If enabled, CNSS PCI platform driver would do asynchronous probe. + Using asynchronous probe will allow CNSS PCI platform driver to + probe in parallel with other device drivers and will help to + reduce kernel boot time. + config CNSS_MAC_BUG bool "Enable/disable 0-4K memory initialization for QCA6174" depends on CNSS diff --git a/drivers/net/wireless/cnss/cnss_pci.c b/drivers/net/wireless/cnss/cnss_pci.c index f53ed2693879..48d358c4722a 100644 --- a/drivers/net/wireless/cnss/cnss_pci.c +++ b/drivers/net/wireless/cnss/cnss_pci.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, 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 @@ -3067,6 +3067,9 @@ static struct platform_driver cnss_driver = { .name = "cnss", .owner = THIS_MODULE, .of_match_table = cnss_dt_match, +#ifdef CONFIG_CNSS_ASYNC + .probe_type = PROBE_PREFER_ASYNCHRONOUS, +#endif }, }; diff --git a/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c b/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c index 09c37c2383c6..af64b3dc4da8 100644 --- a/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c +++ b/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c @@ -228,6 +228,7 @@ void wcnss_prealloc_check_memory_leak(void) #else void wcnss_prealloc_check_memory_leak(void) {} #endif +EXPORT_SYMBOL(wcnss_prealloc_check_memory_leak); int wcnss_pre_alloc_reset(void) { @@ -243,6 +244,7 @@ int wcnss_pre_alloc_reset(void) return n; } +EXPORT_SYMBOL(wcnss_pre_alloc_reset); int prealloc_memory_stats_show(struct seq_file *fp, void *data) { diff --git a/drivers/net/wireless/hostap/hostap_hw.c b/drivers/net/wireless/hostap/hostap_hw.c index 6df3ee561d52..515aa3f993f3 100644 --- a/drivers/net/wireless/hostap/hostap_hw.c +++ b/drivers/net/wireless/hostap/hostap_hw.c @@ -836,25 +836,30 @@ static int hfa384x_get_rid(struct net_device *dev, u16 rid, void *buf, int len, spin_lock_bh(&local->baplock); res = hfa384x_setup_bap(dev, BAP0, rid, 0); - if (!res) - res = hfa384x_from_bap(dev, BAP0, &rec, sizeof(rec)); + if (res) + goto unlock; + + res = hfa384x_from_bap(dev, BAP0, &rec, sizeof(rec)); + if (res) + goto unlock; if (le16_to_cpu(rec.len) == 0) { /* RID not available */ res = -ENODATA; + goto unlock; } rlen = (le16_to_cpu(rec.len) - 1) * 2; - if (!res && exact_len && rlen != len) { + if (exact_len && rlen != len) { printk(KERN_DEBUG "%s: hfa384x_get_rid - RID len mismatch: " "rid=0x%04x, len=%d (expected %d)\n", dev->name, rid, rlen, len); res = -ENODATA; } - if (!res) - res = hfa384x_from_bap(dev, BAP0, buf, len); + res = hfa384x_from_bap(dev, BAP0, buf, len); +unlock: spin_unlock_bh(&local->baplock); mutex_unlock(&local->rid_bap_mtx); diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c index 5f47356d6942..254b0ee37039 100644 --- a/drivers/nvdimm/bus.c +++ b/drivers/nvdimm/bus.c @@ -590,8 +590,14 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm, rc = nd_desc->ndctl(nd_desc, nvdimm, cmd, buf, buf_len); if (rc < 0) goto out_unlock; + nvdimm_bus_unlock(&nvdimm_bus->dev); + if (copy_to_user(p, buf, buf_len)) rc = -EFAULT; + + vfree(buf); + return rc; + out_unlock: nvdimm_bus_unlock(&nvdimm_bus->dev); out: diff --git a/drivers/parport/share.c b/drivers/parport/share.c index 5ce5ef211bdb..754f21fd9768 100644 --- a/drivers/parport/share.c +++ b/drivers/parport/share.c @@ -936,8 +936,10 @@ parport_register_dev_model(struct parport *port, const char *name, * pardevice fields. -arca */ port->ops->init_state(par_dev, par_dev->state); - port->proc_device = par_dev; - parport_device_proc_register(par_dev); + if (!test_and_set_bit(PARPORT_DEVPROC_REGISTERED, &port->devflags)) { + port->proc_device = par_dev; + parport_device_proc_register(par_dev); + } return par_dev; diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c index f364882943e1..8bb759d10074 100644 --- a/drivers/pci/host/pci-msm.c +++ b/drivers/pci/host/pci-msm.c @@ -6449,7 +6449,6 @@ static int msm_pcie_probe(struct platform_device *pdev) } dev_set_drvdata(&msm_pcie_dev[rc_idx].pdev->dev, &msm_pcie_dev[rc_idx]); - msm_pcie_sysfs_init(&msm_pcie_dev[rc_idx]); ret = msm_pcie_get_resources(&msm_pcie_dev[rc_idx], msm_pcie_dev[rc_idx].pdev); @@ -6500,6 +6499,8 @@ static int msm_pcie_probe(struct platform_device *pdev) goto decrease_rc_num; } + msm_pcie_sysfs_init(&msm_pcie_dev[rc_idx]); + msm_pcie_dev[rc_idx].drv_ready = true; if (msm_pcie_dev[rc_idx].boot_option & @@ -6754,12 +6755,12 @@ static int msm_pcie_pm_suspend(struct pci_dev *dev, PCIE_DBG(pcie_dev, "RC%d: PM_Enter_L23 is NOT received\n", pcie_dev->rc_idx); - msm_pcie_disable(pcie_dev, PM_PIPE_CLK | PM_CLK | PM_VREG); - if (pcie_dev->use_pinctrl && pcie_dev->pins_sleep) pinctrl_select_state(pcie_dev->pinctrl, pcie_dev->pins_sleep); + msm_pcie_disable(pcie_dev, PM_PIPE_CLK | PM_CLK | PM_VREG); + PCIE_DBG(pcie_dev, "RC%d: exit\n", pcie_dev->rc_idx); return ret; diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 31f31d460fc9..357527712539 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -303,13 +303,6 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn) return rc; } - pci_iov_set_numvfs(dev, nr_virtfn); - iov->ctrl |= PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE; - pci_cfg_access_lock(dev); - pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); - msleep(100); - pci_cfg_access_unlock(dev); - iov->initial_VFs = initial; if (nr_virtfn < initial) initial = nr_virtfn; @@ -320,6 +313,13 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn) goto err_pcibios; } + pci_iov_set_numvfs(dev, nr_virtfn); + iov->ctrl |= PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE; + pci_cfg_access_lock(dev); + pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); + msleep(100); + pci_cfg_access_unlock(dev); + for (i = 0; i < initial; i++) { rc = virtfn_add(dev, i, 0); if (rc) @@ -555,21 +555,61 @@ void pci_iov_release(struct pci_dev *dev) } /** - * pci_iov_resource_bar - get position of the SR-IOV BAR + * pci_iov_update_resource - update a VF BAR * @dev: the PCI device * @resno: the resource number * - * Returns position of the BAR encapsulated in the SR-IOV capability. + * Update a VF BAR in the SR-IOV capability of a PF. */ -int pci_iov_resource_bar(struct pci_dev *dev, int resno) +void pci_iov_update_resource(struct pci_dev *dev, int resno) { - if (resno < PCI_IOV_RESOURCES || resno > PCI_IOV_RESOURCE_END) - return 0; + struct pci_sriov *iov = dev->is_physfn ? dev->sriov : NULL; + struct resource *res = dev->resource + resno; + int vf_bar = resno - PCI_IOV_RESOURCES; + struct pci_bus_region region; + u16 cmd; + u32 new; + int reg; + + /* + * The generic pci_restore_bars() path calls this for all devices, + * including VFs and non-SR-IOV devices. If this is not a PF, we + * have nothing to do. + */ + if (!iov) + return; + + pci_read_config_word(dev, iov->pos + PCI_SRIOV_CTRL, &cmd); + if ((cmd & PCI_SRIOV_CTRL_VFE) && (cmd & PCI_SRIOV_CTRL_MSE)) { + dev_WARN(&dev->dev, "can't update enabled VF BAR%d %pR\n", + vf_bar, res); + return; + } + + /* + * Ignore unimplemented BARs, unused resource slots for 64-bit + * BARs, and non-movable resources, e.g., those described via + * Enhanced Allocation. + */ + if (!res->flags) + return; + + if (res->flags & IORESOURCE_UNSET) + return; + + if (res->flags & IORESOURCE_PCI_FIXED) + return; - BUG_ON(!dev->is_physfn); + pcibios_resource_to_bus(dev->bus, ®ion, res); + new = region.start; + new |= res->flags & ~PCI_BASE_ADDRESS_MEM_MASK; - return dev->sriov->pos + PCI_SRIOV_BAR + - 4 * (resno - PCI_IOV_RESOURCES); + reg = iov->pos + PCI_SRIOV_BAR + 4 * vf_bar; + pci_write_config_dword(dev, reg, new); + if (res->flags & IORESOURCE_MEM_64) { + new = region.start >> 16 >> 16; + pci_write_config_dword(dev, reg + 4, new); + } } resource_size_t __weak pcibios_iov_resource_alignment(struct pci_dev *dev, diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index e311a9bf2c90..0e53488f8ec1 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -519,10 +519,6 @@ static void pci_restore_bars(struct pci_dev *dev) { int i; - /* Per SR-IOV spec 3.4.1.11, VF BARs are RO zero */ - if (dev->is_virtfn) - return; - for (i = 0; i < PCI_BRIDGE_RESOURCES; i++) pci_update_resource(dev, i); } @@ -4472,36 +4468,6 @@ int pci_select_bars(struct pci_dev *dev, unsigned long flags) } EXPORT_SYMBOL(pci_select_bars); -/** - * pci_resource_bar - get position of the BAR associated with a resource - * @dev: the PCI device - * @resno: the resource number - * @type: the BAR type to be filled in - * - * Returns BAR position in config space, or 0 if the BAR is invalid. - */ -int pci_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type) -{ - int reg; - - if (resno < PCI_ROM_RESOURCE) { - *type = pci_bar_unknown; - return PCI_BASE_ADDRESS_0 + 4 * resno; - } else if (resno == PCI_ROM_RESOURCE) { - *type = pci_bar_mem32; - return dev->rom_base_reg; - } else if (resno < PCI_BRIDGE_RESOURCES) { - /* device specific resource */ - *type = pci_bar_unknown; - reg = pci_iov_resource_bar(dev, resno); - if (reg) - return reg; - } - - dev_err(&dev->dev, "BAR %d: invalid resource\n", resno); - return 0; -} - /* Some architectures require additional programming to enable VGA */ static arch_set_vga_state_t arch_set_vga_state; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index d390fc1475ec..c43e448873ca 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -232,7 +232,6 @@ bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *pl, int pci_setup_device(struct pci_dev *dev); int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, struct resource *res, unsigned int reg); -int pci_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type); void pci_configure_ari(struct pci_dev *dev); void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head); @@ -276,7 +275,7 @@ static inline void pci_restore_ats_state(struct pci_dev *dev) #ifdef CONFIG_PCI_IOV int pci_iov_init(struct pci_dev *dev); void pci_iov_release(struct pci_dev *dev); -int pci_iov_resource_bar(struct pci_dev *dev, int resno); +void pci_iov_update_resource(struct pci_dev *dev, int resno); resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno); void pci_restore_iov_state(struct pci_dev *dev); int pci_iov_bus_range(struct pci_bus *bus); @@ -290,10 +289,6 @@ static inline void pci_iov_release(struct pci_dev *dev) { } -static inline int pci_iov_resource_bar(struct pci_dev *dev, int resno) -{ - return 0; -} static inline void pci_restore_iov_state(struct pci_dev *dev) { } diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 71d9a6d1bd56..b83df942794f 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -226,7 +226,8 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, mask64 = (u32)PCI_BASE_ADDRESS_MEM_MASK; } } else { - res->flags |= (l & IORESOURCE_ROM_ENABLE); + if (l & PCI_ROM_ADDRESS_ENABLE) + res->flags |= IORESOURCE_ROM_ENABLE; l64 = l & PCI_ROM_ADDRESS_MASK; sz64 = sz & PCI_ROM_ADDRESS_MASK; mask64 = (u32)PCI_ROM_ADDRESS_MASK; diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c index eb0ad530dc43..3eea7fc5e1a2 100644 --- a/drivers/pci/rom.c +++ b/drivers/pci/rom.c @@ -31,6 +31,11 @@ int pci_enable_rom(struct pci_dev *pdev) if (!res->flags) return -1; + /* + * Ideally pci_update_resource() would update the ROM BAR address, + * and we would only set the enable bit here. But apparently some + * devices have buggy ROM BARs that read as zero when disabled. + */ pcibios_resource_to_bus(pdev->bus, ®ion, res); pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr); rom_addr &= ~PCI_ROM_ADDRESS_MASK; diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 604011e047d6..25062966cbfa 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -25,21 +25,18 @@ #include <linux/slab.h> #include "pci.h" - -void pci_update_resource(struct pci_dev *dev, int resno) +static void pci_std_update_resource(struct pci_dev *dev, int resno) { struct pci_bus_region region; bool disable; u16 cmd; u32 new, check, mask; int reg; - enum pci_bar_type type; struct resource *res = dev->resource + resno; - if (dev->is_virtfn) { - dev_warn(&dev->dev, "can't update VF BAR%d\n", resno); + /* Per SR-IOV spec 3.4.1.11, VF BARs are RO zero */ + if (dev->is_virtfn) return; - } /* * Ignore resources for unimplemented BARs and unused resource slots @@ -60,21 +57,34 @@ void pci_update_resource(struct pci_dev *dev, int resno) return; pcibios_resource_to_bus(dev->bus, ®ion, res); + new = region.start; - new = region.start | (res->flags & PCI_REGION_FLAG_MASK); - if (res->flags & IORESOURCE_IO) + if (res->flags & IORESOURCE_IO) { mask = (u32)PCI_BASE_ADDRESS_IO_MASK; - else + new |= res->flags & ~PCI_BASE_ADDRESS_IO_MASK; + } else if (resno == PCI_ROM_RESOURCE) { + mask = (u32)PCI_ROM_ADDRESS_MASK; + } else { mask = (u32)PCI_BASE_ADDRESS_MEM_MASK; + new |= res->flags & ~PCI_BASE_ADDRESS_MEM_MASK; + } - reg = pci_resource_bar(dev, resno, &type); - if (!reg) - return; - if (type != pci_bar_unknown) { + if (resno < PCI_ROM_RESOURCE) { + reg = PCI_BASE_ADDRESS_0 + 4 * resno; + } else if (resno == PCI_ROM_RESOURCE) { + + /* + * Apparently some Matrox devices have ROM BARs that read + * as zero when disabled, so don't update ROM BARs unless + * they're enabled. See https://lkml.org/lkml/2005/8/30/138. + */ if (!(res->flags & IORESOURCE_ROM_ENABLE)) return; + + reg = dev->rom_base_reg; new |= PCI_ROM_ADDRESS_ENABLE; - } + } else + return; /* * We can't update a 64-bit BAR atomically, so when possible, @@ -110,6 +120,16 @@ void pci_update_resource(struct pci_dev *dev, int resno) pci_write_config_word(dev, PCI_COMMAND, cmd); } +void pci_update_resource(struct pci_dev *dev, int resno) +{ + if (resno <= PCI_ROM_RESOURCE) + pci_std_update_resource(dev, resno); +#ifdef CONFIG_PCI_IOV + else if (resno >= PCI_IOV_RESOURCES && resno <= PCI_IOV_RESOURCE_END) + pci_iov_update_resource(dev, resno); +#endif +} + int pci_claim_resource(struct pci_dev *dev, int resource) { struct resource *res = &dev->resource[resource]; diff --git a/drivers/pinctrl/intel/pinctrl-cherryview.c b/drivers/pinctrl/intel/pinctrl-cherryview.c index a009ae34c5ef..930f0f25c1ce 100644 --- a/drivers/pinctrl/intel/pinctrl-cherryview.c +++ b/drivers/pinctrl/intel/pinctrl-cherryview.c @@ -1466,12 +1466,11 @@ static int chv_gpio_probe(struct chv_pinctrl *pctrl, int irq) offset += range->npins; } - /* Mask and clear all interrupts */ - chv_writel(0, pctrl->regs + CHV_INTMASK); + /* Clear all interrupts */ chv_writel(0xffff, pctrl->regs + CHV_INTSTAT); ret = gpiochip_irqchip_add(chip, &chv_gpio_irqchip, 0, - handle_simple_irq, IRQ_TYPE_NONE); + handle_bad_irq, IRQ_TYPE_NONE); if (ret) { dev_err(pctrl->dev, "failed to add IRQ chip\n"); goto fail; diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index c1448955d3ed..c5a351e7bb4e 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -602,10 +602,6 @@ static void msm_gpio_irq_unmask(struct irq_data *d) spin_lock_irqsave(&pctrl->lock, flags); - val = readl(pctrl->regs + g->intr_status_reg); - val &= ~BIT(g->intr_status_bit); - writel(val, pctrl->regs + g->intr_status_reg); - val = readl(pctrl->regs + g->intr_cfg_reg); val |= BIT(g->intr_enable_bit); writel(val, pctrl->regs + g->intr_cfg_reg); diff --git a/drivers/platform/msm/gpio-usbdetect.c b/drivers/platform/msm/gpio-usbdetect.c index 80e16573e0aa..dc05d7108135 100644 --- a/drivers/platform/msm/gpio-usbdetect.c +++ b/drivers/platform/msm/gpio-usbdetect.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2015,2017, 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 @@ -20,57 +20,101 @@ #include <linux/interrupt.h> #include <linux/of_gpio.h> #include <linux/gpio.h> -#include <linux/power_supply.h> +#include <linux/extcon.h> #include <linux/regulator/consumer.h> struct gpio_usbdetect { struct platform_device *pdev; struct regulator *vin; - struct power_supply *usb_psy; int vbus_det_irq; + int id_det_irq; int gpio; + struct extcon_dev *extcon_dev; + int vbus_state; + bool id_state; +}; + +static const unsigned int gpio_usb_extcon_table[] = { + EXTCON_USB, + EXTCON_USB_HOST, + EXTCON_USB_CC, + EXTCON_USB_SPEED, + EXTCON_NONE, }; static irqreturn_t gpio_usbdetect_vbus_irq(int irq, void *data) { struct gpio_usbdetect *usb = data; - int vbus; - union power_supply_propval pval = {0,}; - vbus = gpio_get_value(usb->gpio); - if (vbus) - pval.intval = POWER_SUPPLY_TYPE_USB; - else - pval.intval = POWER_SUPPLY_TYPE_UNKNOWN; + usb->vbus_state = gpio_get_value(usb->gpio); + if (usb->vbus_state) { + dev_dbg(&usb->pdev->dev, "setting vbus notification\n"); + extcon_set_cable_state_(usb->extcon_dev, EXTCON_USB, 1); + } else { + dev_dbg(&usb->pdev->dev, "setting vbus removed notification\n"); + extcon_set_cable_state_(usb->extcon_dev, EXTCON_USB, 0); + } + + return IRQ_HANDLED; +} + +static irqreturn_t gpio_usbdetect_id_irq(int irq, void *data) +{ + struct gpio_usbdetect *usb = data; + int ret; + + ret = irq_get_irqchip_state(irq, IRQCHIP_STATE_LINE_LEVEL, + &usb->id_state); + if (ret < 0) { + dev_err(&usb->pdev->dev, "unable to read ID IRQ LINE\n"); + return IRQ_HANDLED; + } - power_supply_set_property(usb->usb_psy, - POWER_SUPPLY_PROP_TYPE, &pval); + return IRQ_WAKE_THREAD; +} - pval.intval = vbus; - power_supply_set_property(usb->usb_psy, POWER_SUPPLY_PROP_PRESENT, - &pval); +static irqreturn_t gpio_usbdetect_id_irq_thread(int irq, void *data) +{ + struct gpio_usbdetect *usb = data; + + if (usb->id_state) { + dev_dbg(&usb->pdev->dev, "stopping usb host\n"); + extcon_set_cable_state_(usb->extcon_dev, EXTCON_USB_HOST, 0); + enable_irq(usb->vbus_det_irq); + } else { + dev_dbg(&usb->pdev->dev, "starting usb HOST\n"); + disable_irq(usb->vbus_det_irq); + extcon_set_cable_state_(usb->extcon_dev, EXTCON_USB_HOST, 1); + } return IRQ_HANDLED; } +static const u32 gpio_usb_extcon_exclusive[] = {0x3, 0}; + static int gpio_usbdetect_probe(struct platform_device *pdev) { struct gpio_usbdetect *usb; - struct power_supply *usb_psy; int rc; - unsigned long flags; - - usb_psy = power_supply_get_by_name("usb"); - if (!usb_psy) { - dev_dbg(&pdev->dev, "USB power_supply not found, deferring probe\n"); - return -EPROBE_DEFER; - } usb = devm_kzalloc(&pdev->dev, sizeof(*usb), GFP_KERNEL); if (!usb) return -ENOMEM; usb->pdev = pdev; - usb->usb_psy = usb_psy; + + usb->extcon_dev = devm_extcon_dev_allocate(&pdev->dev, + gpio_usb_extcon_table); + if (IS_ERR(usb->extcon_dev)) { + dev_err(&pdev->dev, "failed to allocate a extcon device\n"); + return PTR_ERR(usb->extcon_dev); + } + + usb->extcon_dev->mutually_exclusive = gpio_usb_extcon_exclusive; + rc = devm_extcon_dev_register(&pdev->dev, usb->extcon_dev); + if (rc) { + dev_err(&pdev->dev, "failed to register extcon device\n"); + return rc; + } if (of_get_property(pdev->dev.of_node, "vin-supply", NULL)) { usb->vin = devm_regulator_get(&pdev->dev, "vin"); @@ -94,43 +138,63 @@ static int gpio_usbdetect_probe(struct platform_device *pdev) "qcom,vbus-det-gpio", 0); if (usb->gpio < 0) { dev_err(&pdev->dev, "Failed to get gpio: %d\n", usb->gpio); - return usb->gpio; + rc = usb->gpio; + goto error; } rc = gpio_request(usb->gpio, "vbus-det-gpio"); if (rc < 0) { dev_err(&pdev->dev, "Failed to request gpio: %d\n", rc); - return rc; + goto error; } usb->vbus_det_irq = gpio_to_irq(usb->gpio); if (usb->vbus_det_irq < 0) { - if (usb->vin) - regulator_disable(usb->vin); - return usb->vbus_det_irq; + dev_err(&pdev->dev, "get vbus_det_irq failed\n"); + rc = usb->vbus_det_irq; + goto error; } - rc = devm_request_irq(&pdev->dev, usb->vbus_det_irq, - gpio_usbdetect_vbus_irq, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - "vbus_det_irq", usb); + rc = devm_request_threaded_irq(&pdev->dev, usb->vbus_det_irq, + NULL, gpio_usbdetect_vbus_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, "vbus_det_irq", usb); if (rc) { dev_err(&pdev->dev, "request for vbus_det_irq failed: %d\n", rc); - if (usb->vin) - regulator_disable(usb->vin); - return rc; + goto error; + } + + usb->id_det_irq = platform_get_irq_byname(pdev, "pmic_id_irq"); + if (usb->id_det_irq < 0) { + dev_err(&pdev->dev, "get id_det_irq failed\n"); + rc = usb->id_det_irq; + goto error; + } + + rc = devm_request_threaded_irq(&pdev->dev, usb->id_det_irq, + gpio_usbdetect_id_irq, + gpio_usbdetect_id_irq_thread, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, "id_det_irq", usb); + if (rc) { + dev_err(&pdev->dev, "request for id_det_irq failed: %d\n", rc); + goto error; } enable_irq_wake(usb->vbus_det_irq); + enable_irq_wake(usb->id_det_irq); dev_set_drvdata(&pdev->dev, usb); /* Read and report initial VBUS state */ - local_irq_save(flags); gpio_usbdetect_vbus_irq(usb->vbus_det_irq, usb); - local_irq_restore(flags); return 0; + +error: + if (usb->vin) + regulator_disable(usb->vin); + return rc; } static int gpio_usbdetect_remove(struct platform_device *pdev) @@ -139,6 +203,8 @@ static int gpio_usbdetect_remove(struct platform_device *pdev) disable_irq_wake(usb->vbus_det_irq); disable_irq(usb->vbus_det_irq); + disable_irq_wake(usb->id_det_irq); + disable_irq(usb->id_det_irq); if (usb->vin) regulator_disable(usb->vin); diff --git a/drivers/platform/msm/gsi/gsi_dbg.c b/drivers/platform/msm/gsi/gsi_dbg.c index 717c891788f2..eaf50ca1cea5 100644 --- a/drivers/platform/msm/gsi/gsi_dbg.c +++ b/drivers/platform/msm/gsi/gsi_dbg.c @@ -29,6 +29,7 @@ static struct dentry *dent; static char dbg_buff[4096]; +static void *gsi_ipc_logbuf_low; static void gsi_wq_print_dp_stats(struct work_struct *work); static DECLARE_DELAYED_WORK(gsi_print_dp_stats_work, gsi_wq_print_dp_stats); @@ -764,22 +765,20 @@ static ssize_t gsi_enable_ipc_low(struct file *file, if (kstrtos8(dbg_buff, 0, &option)) return -EFAULT; + mutex_lock(&gsi_ctx->mlock); if (option) { - if (!gsi_ctx->ipc_logbuf_low) { - gsi_ctx->ipc_logbuf_low = + if (!gsi_ipc_logbuf_low) { + gsi_ipc_logbuf_low = ipc_log_context_create(GSI_IPC_LOG_PAGES, "gsi_low", 0); + if (gsi_ipc_logbuf_low == NULL) + TERR("failed to get ipc_logbuf_low\n"); } - - if (gsi_ctx->ipc_logbuf_low == NULL) { - TERR("failed to get ipc_logbuf_low\n"); - return -EFAULT; - } + gsi_ctx->ipc_logbuf_low = gsi_ipc_logbuf_low; } else { - if (gsi_ctx->ipc_logbuf_low) - ipc_log_context_destroy(gsi_ctx->ipc_logbuf_low); gsi_ctx->ipc_logbuf_low = NULL; } + mutex_unlock(&gsi_ctx->mlock); return count; } diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa.c b/drivers/platform/msm/ipa/ipa_v2/ipa.c index f01743d04e84..0629d6bca49a 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa.c @@ -3685,6 +3685,7 @@ void ipa_suspend_handler(enum ipa_irq_type interrupt, * pipe will be unsuspended as part of * enabling IPA clocks */ + mutex_lock(&ipa_ctx->sps_pm.sps_pm_lock); if (!atomic_read( &ipa_ctx->sps_pm.dec_clients) ) { @@ -3697,6 +3698,7 @@ void ipa_suspend_handler(enum ipa_irq_type interrupt, 1); ipa_sps_process_irq_schedule_rel(); } + mutex_unlock(&ipa_ctx->sps_pm.sps_pm_lock); } else { resource = ipa2_get_rm_resource_from_ep(i); res = ipa_rm_request_resource_with_timer( diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c index 3ee0741c9f1d..cb95f6e98956 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c @@ -126,6 +126,7 @@ static struct dentry *dfile_ipa_poll_iteration; static char dbg_buff[IPA_MAX_MSG_LEN]; static char *active_clients_buf; static s8 ep_reg_idx; +static void *ipa_ipc_low_buff; int _ipa_read_gen_reg_v1_1(char *buff, int max_len) { @@ -1826,23 +1827,20 @@ static ssize_t ipa_enable_ipc_low(struct file *file, if (kstrtos8(dbg_buff, 0, &option)) return -EFAULT; + mutex_lock(&ipa_ctx->lock); if (option) { - if (!ipa_ctx->logbuf_low) { - ipa_ctx->logbuf_low = + if (!ipa_ipc_low_buff) { + ipa_ipc_low_buff = ipc_log_context_create(IPA_IPC_LOG_PAGES, "ipa_low", 0); + if (ipa_ipc_low_buff == NULL) + IPAERR("failed to get logbuf_low\n"); } - - if (ipa_ctx->logbuf_low == NULL) { - IPAERR("failed to get logbuf_low\n"); - return -EFAULT; - } - + ipa_ctx->logbuf_low = ipa_ipc_low_buff; } else { - if (ipa_ctx->logbuf_low) - ipc_log_context_destroy(ipa_ctx->logbuf_low); - ipa_ctx->logbuf_low = NULL; + ipa_ctx->logbuf_low = NULL; } + mutex_unlock(&ipa_ctx->lock); return count; } diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c index 2fdb20d99ce2..23c8a5059c3b 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c @@ -2046,11 +2046,13 @@ static void ipa_alloc_wlan_rx_common_cache(u32 size) goto fail_dma_mapping; } + spin_lock_bh(&ipa_ctx->wc_memb.wlan_spinlock); list_add_tail(&rx_pkt->link, &ipa_ctx->wc_memb.wlan_comm_desc_list); rx_len_cached = ++ipa_ctx->wc_memb.wlan_comm_total_cnt; ipa_ctx->wc_memb.wlan_comm_free_cnt++; + spin_unlock_bh(&ipa_ctx->wc_memb.wlan_spinlock); } diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h index a14d1fee9c35..bb11230c960a 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h @@ -51,6 +51,7 @@ #define IPA_UC_FINISH_MAX 6 #define IPA_UC_WAIT_MIN_SLEEP 1000 #define IPA_UC_WAII_MAX_SLEEP 1200 +#define IPA_BAM_STOP_MAX_RETRY 10 #define IPA_MAX_STATUS_STAT_NUM 30 diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c index 11c77934e04f..4b6bc5b61bfc 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c @@ -1354,6 +1354,10 @@ int ipa2_get_rt_tbl(struct ipa_ioc_get_rt_tbl *lookup) mutex_lock(&ipa_ctx->lock); entry = __ipa_find_rt_tbl(lookup->ip, lookup->name); if (entry && entry->cookie == IPA_COOKIE) { + if (entry->ref_cnt == U32_MAX) { + IPAERR("fail: ref count crossed limit\n"); + goto ret; + } entry->ref_cnt++; lookup->hdl = entry->id; @@ -1363,6 +1367,8 @@ int ipa2_get_rt_tbl(struct ipa_ioc_get_rt_tbl *lookup) result = 0; } + +ret: mutex_unlock(&ipa_ctx->lock); return result; diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_uc.c b/drivers/platform/msm/ipa/ipa_v2/ipa_uc.c index 3dd2eb093317..364cd4b7d38a 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_uc.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_uc.c @@ -590,6 +590,7 @@ int ipa_uc_send_cmd(u32 cmd, u32 opcode, u32 expected_status, { int index; union IpaHwCpuCmdCompletedResponseData_t uc_rsp; + int retries = 0; mutex_lock(&ipa_ctx->uc_ctx.uc_lock); @@ -599,6 +600,7 @@ int ipa_uc_send_cmd(u32 cmd, u32 opcode, u32 expected_status, return -EBADF; } +send_cmd: init_completion(&ipa_ctx->uc_ctx.uc_completion); ipa_ctx->uc_ctx.uc_sram_mmio->cmdParams = cmd; @@ -658,6 +660,19 @@ int ipa_uc_send_cmd(u32 cmd, u32 opcode, u32 expected_status, } if (ipa_ctx->uc_ctx.uc_status != expected_status) { + if (IPA_HW_2_CPU_WDI_RX_FSM_TRANSITION_ERROR == + ipa_ctx->uc_ctx.uc_status) { + retries++; + if (retries == IPA_BAM_STOP_MAX_RETRY) { + IPAERR("Failed after %d tries\n", retries); + } else { + /* sleep for short period to flush IPA */ + usleep_range(IPA_UC_WAIT_MIN_SLEEP, + IPA_UC_WAII_MAX_SLEEP); + goto send_cmd; + } + } + IPAERR("Recevied status %u, Expected status %u\n", ipa_ctx->uc_ctx.uc_status, expected_status); ipa_ctx->uc_ctx.pending_cmd = -1; diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_offload_i.h b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_offload_i.h index 3bec471b4656..a98d60249c0e 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_offload_i.h +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_offload_i.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, 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 @@ -441,7 +441,7 @@ enum ipa_hw_offload_channel_states { /** - * enum ipa_hw_2_cpu_cmd_resp_status - Values that represent + * enum ipa_hw_2_cpu_offload_cmd_resp_status - Values that represent * offload related command response status to be sent to CPU. */ enum ipa_hw_2_cpu_offload_cmd_resp_status { @@ -478,6 +478,47 @@ enum ipa_hw_2_cpu_offload_cmd_resp_status { }; /** + * enum ipa_hw_2_cpu_cmd_resp_status - Values that represent WDI related + * command response status to be sent to CPU. + */ +enum ipa_hw_2_cpu_cmd_resp_status { + IPA_HW_2_CPU_WDI_CMD_STATUS_SUCCESS = + FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 0), + IPA_HW_2_CPU_MAX_WDI_TX_CHANNELS = + FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 1), + IPA_HW_2_CPU_WDI_CE_RING_OVERRUN_POSSIBILITY = + FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 2), + IPA_HW_2_CPU_WDI_CE_RING_SET_UP_FAILURE = + FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 3), + IPA_HW_2_CPU_WDI_CE_RING_PARAMS_UNALIGNED = + FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 4), + IPA_HW_2_CPU_WDI_COMP_RING_OVERRUN_POSSIBILITY = + FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 5), + IPA_HW_2_CPU_WDI_COMP_RING_SET_UP_FAILURE = + FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 6), + IPA_HW_2_CPU_WDI_COMP_RING_PARAMS_UNALIGNED = + FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 7), + IPA_HW_2_CPU_WDI_UNKNOWN_TX_CHANNEL = + FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 8), + IPA_HW_2_CPU_WDI_TX_INVALID_FSM_TRANSITION = + FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 9), + IPA_HW_2_CPU_WDI_TX_FSM_TRANSITION_ERROR = + FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 10), + IPA_HW_2_CPU_MAX_WDI_RX_CHANNELS = + FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 11), + IPA_HW_2_CPU_WDI_RX_RING_PARAMS_UNALIGNED = + FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 12), + IPA_HW_2_CPU_WDI_RX_RING_SET_UP_FAILURE = + FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 13), + IPA_HW_2_CPU_WDI_UNKNOWN_RX_CHANNEL = + FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 14), + IPA_HW_2_CPU_WDI_RX_INVALID_FSM_TRANSITION = + FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 15), + IPA_HW_2_CPU_WDI_RX_FSM_TRANSITION_ERROR = + FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 16), +}; + +/** * struct IpaHwSetUpCmd - * * diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_wdi.c b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_wdi.c index f60669132865..56b4bf1a2d1e 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_wdi.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_wdi.c @@ -110,47 +110,6 @@ enum ipa_cpu_2_hw_wdi_commands { }; /** - * enum ipa_hw_2_cpu_cmd_resp_status - Values that represent WDI related - * command response status to be sent to CPU. - */ -enum ipa_hw_2_cpu_cmd_resp_status { - IPA_HW_2_CPU_WDI_CMD_STATUS_SUCCESS = - FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 0), - IPA_HW_2_CPU_MAX_WDI_TX_CHANNELS = - FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 1), - IPA_HW_2_CPU_WDI_CE_RING_OVERRUN_POSSIBILITY = - FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 2), - IPA_HW_2_CPU_WDI_CE_RING_SET_UP_FAILURE = - FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 3), - IPA_HW_2_CPU_WDI_CE_RING_PARAMS_UNALIGNED = - FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 4), - IPA_HW_2_CPU_WDI_COMP_RING_OVERRUN_POSSIBILITY = - FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 5), - IPA_HW_2_CPU_WDI_COMP_RING_SET_UP_FAILURE = - FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 6), - IPA_HW_2_CPU_WDI_COMP_RING_PARAMS_UNALIGNED = - FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 7), - IPA_HW_2_CPU_WDI_UNKNOWN_TX_CHANNEL = - FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 8), - IPA_HW_2_CPU_WDI_TX_INVALID_FSM_TRANSITION = - FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 9), - IPA_HW_2_CPU_WDI_TX_FSM_TRANSITION_ERROR = - FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 10), - IPA_HW_2_CPU_MAX_WDI_RX_CHANNELS = - FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 11), - IPA_HW_2_CPU_WDI_RX_RING_PARAMS_UNALIGNED = - FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 12), - IPA_HW_2_CPU_WDI_RX_RING_SET_UP_FAILURE = - FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 13), - IPA_HW_2_CPU_WDI_UNKNOWN_RX_CHANNEL = - FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 14), - IPA_HW_2_CPU_WDI_RX_INVALID_FSM_TRANSITION = - FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 15), - IPA_HW_2_CPU_WDI_RX_FSM_TRANSITION_ERROR = - FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 16), -}; - -/** * enum ipa_hw_wdi_errors - WDI specific error types. * @IPA_HW_WDI_ERROR_NONE : No error persists * @IPA_HW_WDI_CHANNEL_ERROR : Error is specific to channel diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c index 5ee6e5d2d9e3..5a7a0e5000b9 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c @@ -3614,6 +3614,8 @@ void ipa3_suspend_handler(enum ipa_irq_type interrupt, * pipe will be unsuspended as part of * enabling IPA clocks */ + mutex_lock(&ipa3_ctx->transport_pm. + transport_pm_mutex); if (!atomic_read( &ipa3_ctx->transport_pm.dec_clients) ) { @@ -3626,6 +3628,8 @@ void ipa3_suspend_handler(enum ipa_irq_type interrupt, 1); ipa3_sps_process_irq_schedule_rel(); } + mutex_unlock(&ipa3_ctx->transport_pm. + transport_pm_mutex); } else { resource = ipa3_get_rm_resource_from_ep(i); res = diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c index 81eae05d7ed9..e349ade46075 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c @@ -857,6 +857,8 @@ static int ipa3_reset_with_open_aggr_frame_wa(u32 clnt_hdl, struct gsi_xfer_elem xfer_elem; int i; int aggr_active_bitmap = 0; + bool pipe_suspended = false; + struct ipa_ep_cfg_ctrl ctrl; IPADBG("Applying reset channel with open aggregation frame WA\n"); ipahal_write_reg(IPA_AGGR_FORCE_CLOSE, (1 << clnt_hdl)); @@ -883,6 +885,15 @@ static int ipa3_reset_with_open_aggr_frame_wa(u32 clnt_hdl, if (result) return -EFAULT; + ipahal_read_reg_n_fields(IPA_ENDP_INIT_CTRL_n, clnt_hdl, &ctrl); + if (ctrl.ipa_ep_suspend) { + IPADBG("pipe is suspended, remove suspend\n"); + pipe_suspended = true; + ctrl.ipa_ep_suspend = false; + ipahal_write_reg_n_fields(IPA_ENDP_INIT_CTRL_n, + clnt_hdl, &ctrl); + } + /* Start channel and put 1 Byte descriptor on it */ gsi_res = gsi_start_channel(ep->gsi_chan_hdl); if (gsi_res != GSI_STATUS_SUCCESS) { @@ -942,6 +953,13 @@ static int ipa3_reset_with_open_aggr_frame_wa(u32 clnt_hdl, */ msleep(IPA_POLL_AGGR_STATE_SLEEP_MSEC); + if (pipe_suspended) { + IPADBG("suspend the pipe again\n"); + ctrl.ipa_ep_suspend = true; + ipahal_write_reg_n_fields(IPA_ENDP_INIT_CTRL_n, + clnt_hdl, &ctrl); + } + /* Restore channels properties */ result = ipa3_restore_channel_properties(ep, &orig_chan_props, &orig_chan_scratch); @@ -956,6 +974,12 @@ queue_xfer_fail: ipa3_stop_gsi_channel(clnt_hdl); dma_free_coherent(ipa3_ctx->pdev, 1, buff, dma_addr); start_chan_fail: + if (pipe_suspended) { + IPADBG("suspend the pipe again\n"); + ctrl.ipa_ep_suspend = true; + ipahal_write_reg_n_fields(IPA_ENDP_INIT_CTRL_n, + clnt_hdl, &ctrl); + } ipa3_restore_channel_properties(ep, &orig_chan_props, &orig_chan_scratch); restore_props_fail: diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c index c43d4ee19ad1..fbf84ab7d2d4 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c @@ -109,6 +109,7 @@ static char dbg_buff[IPA_MAX_MSG_LEN]; static char *active_clients_buf; static s8 ep_reg_idx; +static void *ipa_ipc_low_buff; static ssize_t ipa3_read_gen_reg(struct file *file, char __user *ubuf, @@ -1781,22 +1782,20 @@ static ssize_t ipa3_enable_ipc_low(struct file *file, if (kstrtos8(dbg_buff, 0, &option)) return -EFAULT; + mutex_lock(&ipa3_ctx->lock); if (option) { - if (!ipa3_ctx->logbuf_low) { - ipa3_ctx->logbuf_low = + if (!ipa_ipc_low_buff) { + ipa_ipc_low_buff = ipc_log_context_create(IPA_IPC_LOG_PAGES, "ipa_low", 0); } - - if (ipa3_ctx->logbuf_low == NULL) { - IPAERR("failed to get logbuf_low\n"); - return -EFAULT; - } + if (ipa_ipc_low_buff == NULL) + IPAERR("failed to get logbuf_low\n"); + ipa3_ctx->logbuf_low = ipa_ipc_low_buff; } else { - if (ipa3_ctx->logbuf_low) - ipc_log_context_destroy(ipa3_ctx->logbuf_low); ipa3_ctx->logbuf_low = NULL; } + mutex_unlock(&ipa3_ctx->lock); return count; } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c index c5b56f16788a..0492fa27c5b7 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c @@ -1515,6 +1515,7 @@ int ipa3_teardown_sys_pipe(u32 clnt_hdl) struct ipa3_ep_context *ep; int empty; int result; + int i; if (clnt_hdl >= ipa3_ctx->ipa_num_pipes || ipa3_ctx->ep[clnt_hdl].valid == 0) { @@ -1551,13 +1552,23 @@ int ipa3_teardown_sys_pipe(u32 clnt_hdl) cancel_delayed_work_sync(&ep->sys->replenish_rx_work); flush_workqueue(ep->sys->wq); if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI) { - result = ipa3_stop_gsi_channel(clnt_hdl); + /* channel stop might fail on timeout if IPA is busy */ + for (i = 0; i < IPA_GSI_CHANNEL_STOP_MAX_RETRY; i++) { + result = ipa3_stop_gsi_channel(clnt_hdl); + if (result == GSI_STATUS_SUCCESS) + break; + + if (result != -GSI_STATUS_AGAIN && + result != -GSI_STATUS_TIMED_OUT) + break; + } + if (result != GSI_STATUS_SUCCESS) { IPAERR("GSI stop chan err: %d.\n", result); BUG(); return result; } - result = gsi_reset_channel(ep->gsi_chan_hdl); + result = ipa3_reset_gsi_channel(clnt_hdl); if (result != GSI_STATUS_SUCCESS) { IPAERR("Failed to reset chan: %d.\n", result); BUG(); 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 335e5283cc29..0269bfba993f 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c @@ -53,6 +53,8 @@ struct ipa3_qmi_context *ipa3_qmi_ctx; static bool workqueues_stopped; static bool ipa3_modem_init_cmplt; static bool first_time_handshake; +struct mutex ipa3_qmi_lock; + /* QMI A5 service */ static struct msg_desc ipa3_indication_reg_req_desc = { @@ -610,12 +612,17 @@ int ipa3_qmi_filter_request_send(struct ipa_install_fltr_rule_req_msg_v01 *req) req->filter_spec_ex_list_len); } - /* cache the qmi_filter_request */ - memcpy(&(ipa3_qmi_ctx->ipa_install_fltr_rule_req_msg_cache[ - ipa3_qmi_ctx->num_ipa_install_fltr_rule_req_msg]), - req, sizeof(struct ipa_install_fltr_rule_req_msg_v01)); - ipa3_qmi_ctx->num_ipa_install_fltr_rule_req_msg++; - ipa3_qmi_ctx->num_ipa_install_fltr_rule_req_msg %= 10; + mutex_lock(&ipa3_qmi_lock); + if (ipa3_qmi_ctx != NULL) { + /* cache the qmi_filter_request */ + memcpy(&(ipa3_qmi_ctx->ipa_install_fltr_rule_req_msg_cache[ + ipa3_qmi_ctx->num_ipa_install_fltr_rule_req_msg]), + req, + sizeof(struct ipa_install_fltr_rule_req_msg_v01)); + ipa3_qmi_ctx->num_ipa_install_fltr_rule_req_msg++; + ipa3_qmi_ctx->num_ipa_install_fltr_rule_req_msg %= 10; + } + mutex_unlock(&ipa3_qmi_lock); req_desc.max_msg_len = QMI_IPA_INSTALL_FILTER_RULE_REQ_MAX_MSG_LEN_V01; req_desc.msg_id = QMI_IPA_INSTALL_FILTER_RULE_REQ_V01; @@ -655,12 +662,17 @@ int ipa3_qmi_filter_request_ex_send( req->filter_spec_ex_list_len); } - /* cache the qmi_filter_request */ - memcpy(&(ipa3_qmi_ctx->ipa_install_fltr_rule_req_ex_msg_cache[ - ipa3_qmi_ctx->num_ipa_install_fltr_rule_req_ex_msg]), - req, sizeof(struct ipa_install_fltr_rule_req_ex_msg_v01)); - ipa3_qmi_ctx->num_ipa_install_fltr_rule_req_ex_msg++; - ipa3_qmi_ctx->num_ipa_install_fltr_rule_req_ex_msg %= 10; + mutex_lock(&ipa3_qmi_lock); + if (ipa3_qmi_ctx != NULL) { + /* cache the qmi_filter_request */ + memcpy(&(ipa3_qmi_ctx->ipa_install_fltr_rule_req_ex_msg_cache[ + ipa3_qmi_ctx->num_ipa_install_fltr_rule_req_ex_msg]), + req, + sizeof(struct ipa_install_fltr_rule_req_ex_msg_v01)); + ipa3_qmi_ctx->num_ipa_install_fltr_rule_req_ex_msg++; + ipa3_qmi_ctx->num_ipa_install_fltr_rule_req_ex_msg %= 10; + } + mutex_unlock(&ipa3_qmi_lock); req_desc.max_msg_len = QMI_IPA_INSTALL_FILTER_RULE_EX_REQ_MAX_MSG_LEN_V01; @@ -795,12 +807,17 @@ int ipa3_qmi_filter_notify_send( return -EINVAL; } - /* cache the qmi_filter_request */ - memcpy(&(ipa3_qmi_ctx->ipa_fltr_installed_notif_req_msg_cache[ - ipa3_qmi_ctx->num_ipa_fltr_installed_notif_req_msg]), - req, sizeof(struct ipa_fltr_installed_notif_req_msg_v01)); - ipa3_qmi_ctx->num_ipa_fltr_installed_notif_req_msg++; - ipa3_qmi_ctx->num_ipa_fltr_installed_notif_req_msg %= 10; + mutex_lock(&ipa3_qmi_lock); + if (ipa3_qmi_ctx != NULL) { + /* cache the qmi_filter_request */ + memcpy(&(ipa3_qmi_ctx->ipa_fltr_installed_notif_req_msg_cache[ + ipa3_qmi_ctx->num_ipa_fltr_installed_notif_req_msg]), + req, + sizeof(struct ipa_fltr_installed_notif_req_msg_v01)); + ipa3_qmi_ctx->num_ipa_fltr_installed_notif_req_msg++; + ipa3_qmi_ctx->num_ipa_fltr_installed_notif_req_msg %= 10; + } + mutex_unlock(&ipa3_qmi_lock); req_desc.max_msg_len = QMI_IPA_FILTER_INSTALLED_NOTIF_REQ_MAX_MSG_LEN_V01; @@ -1338,3 +1355,13 @@ int ipa3_qmi_stop_data_qouta(void) resp.resp.error, "ipa_stop_data_usage_quota_req_msg_v01"); } +void ipa3_qmi_init(void) +{ + mutex_init(&ipa3_qmi_lock); +} + +void ipa3_qmi_cleanup(void) +{ + mutex_destroy(&ipa3_qmi_lock); +} + diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h index eb38d08f01c7..d5d850309696 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h @@ -207,6 +207,10 @@ int ipa3_qmi_stop_data_qouta(void); void ipa3_q6_handshake_complete(bool ssr_bootup); +void ipa3_qmi_init(void); + +void ipa3_qmi_cleanup(void); + #else /* CONFIG_RMNET_IPA3 */ static inline int ipa3_qmi_service_init(uint32_t wan_platform_type) @@ -319,6 +323,14 @@ static inline int ipa3_qmi_stop_data_qouta(void) static inline void ipa3_q6_handshake_complete(bool ssr_bootup) { } +static inline void ipa3_qmi_init(void) +{ +} + +static inline void ipa3_qmi_cleanup(void) +{ +} + #endif /* CONFIG_RMNET_IPA3 */ #endif /* IPA_QMI_SERVICE_H */ diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c index ac7e57f10062..7212ba2a165c 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, 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 @@ -1438,6 +1438,10 @@ int ipa3_get_rt_tbl(struct ipa_ioc_get_rt_tbl *lookup) mutex_lock(&ipa3_ctx->lock); entry = __ipa3_find_rt_tbl(lookup->ip, lookup->name); if (entry && entry->cookie == IPA_COOKIE) { + if (entry->ref_cnt == U32_MAX) { + IPAERR("fail: ref count crossed limit\n"); + goto ret; + } entry->ref_cnt++; lookup->hdl = entry->id; @@ -1447,6 +1451,8 @@ int ipa3_get_rt_tbl(struct ipa_ioc_get_rt_tbl *lookup) result = 0; } + +ret: mutex_unlock(&ipa3_ctx->lock); return result; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c index 57fa2465c9ea..e49bdc8c7083 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c @@ -1205,6 +1205,8 @@ int ipa3_connect_wdi_pipe(struct ipa_wdi_in_params *in, IPADBG("Skipping endpoint configuration.\n"); } + ipa3_enable_data_path(ipa_ep_idx); + out->clnt_hdl = ipa_ep_idx; if (!ep->skip_ep_cfg && IPA_CLIENT_IS_PROD(in->sys.client)) diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c index c66fd21a078a..d19de2a7bdb5 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c @@ -3582,21 +3582,30 @@ int ipa3_stop_gsi_channel(u32 clnt_hdl) memset(&mem, 0, sizeof(mem)); + if (IPA_CLIENT_IS_PROD(ep->client)) { + IPADBG("Calling gsi_stop_channel ch:%lu\n", + ep->gsi_chan_hdl); + res = gsi_stop_channel(ep->gsi_chan_hdl); + IPADBG("gsi_stop_channel ch: %lu returned %d\n", + ep->gsi_chan_hdl, res); + goto end_sequence; + } + for (i = 0; i < IPA_GSI_CHANNEL_STOP_MAX_RETRY; i++) { - IPADBG("Calling gsi_stop_channel\n"); + IPADBG("Calling gsi_stop_channel ch:%lu\n", + ep->gsi_chan_hdl); res = gsi_stop_channel(ep->gsi_chan_hdl); - IPADBG("gsi_stop_channel returned %d\n", res); + IPADBG("gsi_stop_channel ch: %lu returned %d\n", + ep->gsi_chan_hdl, res); if (res != -GSI_STATUS_AGAIN && res != -GSI_STATUS_TIMED_OUT) goto end_sequence; - if (IPA_CLIENT_IS_CONS(ep->client)) { - IPADBG("Inject a DMA_TASK with 1B packet to IPA\n"); - /* Send a 1B packet DMA_TASK to IPA and try again */ - res = ipa3_inject_dma_task_for_gsi(); - if (res) { - IPAERR("Failed to inject DMA TASk for GSI\n"); - goto end_sequence; - } + IPADBG("Inject a DMA_TASK with 1B packet to IPA\n"); + /* Send a 1B packet DMA_TASK to IPA and try again */ + res = ipa3_inject_dma_task_for_gsi(); + if (res) { + IPAERR("Failed to inject DMA TASk for GSI\n"); + goto end_sequence; } /* sleep for short period to flush IPA */ diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c index 3855e0d46ca9..585f9e6bd492 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c @@ -646,6 +646,21 @@ static void ipareg_construct_endp_init_ctrl_n(enum ipahal_reg_name reg, IPA_ENDP_INIT_CTRL_n_ENDP_DELAY_BMSK); } +static void ipareg_parse_endp_init_ctrl_n(enum ipahal_reg_name reg, + void *fields, u32 val) +{ + struct ipa_ep_cfg_ctrl *ep_ctrl = + (struct ipa_ep_cfg_ctrl *)fields; + + ep_ctrl->ipa_ep_suspend = + ((val & IPA_ENDP_INIT_CTRL_n_ENDP_SUSPEND_BMSK) >> + IPA_ENDP_INIT_CTRL_n_ENDP_SUSPEND_SHFT); + + ep_ctrl->ipa_ep_delay = + ((val & IPA_ENDP_INIT_CTRL_n_ENDP_DELAY_BMSK) >> + IPA_ENDP_INIT_CTRL_n_ENDP_DELAY_SHFT); +} + static void ipareg_construct_endp_init_ctrl_scnd_n(enum ipahal_reg_name reg, const void *fields, u32 *val) { @@ -1018,7 +1033,8 @@ static struct ipahal_reg_obj ipahal_reg_objs[IPA_HW_MAX][IPA_REG_MAX] = { ipareg_construct_endp_init_nat_n, ipareg_parse_dummy, 0x0000080C, 0x70}, [IPA_HW_v3_0][IPA_ENDP_INIT_CTRL_n] = { - ipareg_construct_endp_init_ctrl_n, ipareg_parse_dummy, + ipareg_construct_endp_init_ctrl_n, + ipareg_parse_endp_init_ctrl_n, 0x00000800, 0x70}, [IPA_HW_v3_0][IPA_ENDP_INIT_CTRL_SCND_n] = { ipareg_construct_endp_init_ctrl_scnd_n, ipareg_parse_dummy, diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c index d37630f74b48..1bd4f7fda1b7 100644 --- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c @@ -3303,6 +3303,9 @@ static int __init ipa3_wwan_init(void) mutex_init(&rmnet_ipa3_ctx->pipe_handle_guard); rmnet_ipa3_ctx->ipa3_to_apps_hdl = -1; rmnet_ipa3_ctx->apps_to_ipa3_hdl = -1; + + ipa3_qmi_init(); + /* Register for Modem SSR */ rmnet_ipa3_ctx->subsys_notify_handle = subsys_notif_register_notifier( SUBSYS_MODEM, @@ -3316,6 +3319,7 @@ static int __init ipa3_wwan_init(void) static void __exit ipa3_wwan_cleanup(void) { int ret; + ipa3_qmi_cleanup(); mutex_destroy(&rmnet_ipa3_ctx->pipe_handle_guard); ret = subsys_notif_unregister_notifier( rmnet_ipa3_ctx->subsys_notify_handle, &ipa3_ssr_notifier); diff --git a/drivers/platform/msm/mhi/mhi.h b/drivers/platform/msm/mhi/mhi.h index 60e02fcb5e4b..b4f3df4ec3d2 100644 --- a/drivers/platform/msm/mhi/mhi.h +++ b/drivers/platform/msm/mhi/mhi.h @@ -338,9 +338,8 @@ struct db_mode { u32 db_mode : 1; enum MHI_BRSTMODE brstmode; void (*process_db)(struct mhi_device_ctxt *mhi_dev_ctxt, - void __iomem *io_addr, - uintptr_t chan, - u32 val); + void __iomem *io_addr, unsigned int chan, + dma_addr_t val); }; struct mhi_ring { @@ -728,15 +727,13 @@ int mhi_set_bus_request(struct mhi_device_ctxt *mhi_dev_ctxt, int index); int start_chan_sync(struct mhi_client_handle *client_handle); void mhi_process_db_brstmode(struct mhi_device_ctxt *mhi_dev_ctxt, - void __iomem *io_addr, - uintptr_t chan, - u32 val); + void __iomem *io_addr, unsigned int chan, + dma_addr_t val); void mhi_process_db_brstmode_disable(struct mhi_device_ctxt *mhi_dev_ctxt, - void __iomem *io_addr, - uintptr_t chan, - u32 val); + void __iomem *io_addr, unsigned int chan, + dma_addr_t val); void mhi_process_db(struct mhi_device_ctxt *mhi_dev_ctxt, void __iomem *io_addr, - uintptr_t io_offset, u32 val); + unsigned int chan, dma_addr_t val); void mhi_reg_write_field(struct mhi_device_ctxt *mhi_dev_ctxt, void __iomem *io_addr, uintptr_t io_offset, diff --git a/drivers/platform/msm/mhi/mhi_main.c b/drivers/platform/msm/mhi/mhi_main.c index 46baf7332900..78aa1beb870d 100644 --- a/drivers/platform/msm/mhi/mhi_main.c +++ b/drivers/platform/msm/mhi/mhi_main.c @@ -90,8 +90,9 @@ dma_pool_error: } static void mhi_write_db(struct mhi_device_ctxt *mhi_dev_ctxt, - void __iomem *io_addr_lower, - uintptr_t chan, u64 val) + void __iomem *io_addr_lower, + unsigned int chan, + dma_addr_t val) { uintptr_t io_offset = chan * sizeof(u64); void __iomem *io_addr_upper = @@ -1918,8 +1919,8 @@ EXPORT_SYMBOL(mhi_xfer_rddm); void mhi_process_db_brstmode(struct mhi_device_ctxt *mhi_dev_ctxt, void __iomem *io_addr, - uintptr_t chan, - u32 val) + unsigned int chan, + dma_addr_t val) { struct mhi_ring *ring_ctxt = &mhi_dev_ctxt->mhi_local_chan_ctxt[chan]; @@ -1932,7 +1933,7 @@ void mhi_process_db_brstmode(struct mhi_device_ctxt *mhi_dev_ctxt, mhi_local_event_ctxt[chan]; mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, - "db.set addr: %p io_offset 0x%lx val:0x%x\n", + "db.set addr: %p io_offset %u val:0x%llx\n", io_addr, chan, val); mhi_update_ctxt(mhi_dev_ctxt, io_addr, chan, val); @@ -1942,7 +1943,7 @@ void mhi_process_db_brstmode(struct mhi_device_ctxt *mhi_dev_ctxt, ring_ctxt->db_mode.db_mode = 0; } else { mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, - "Not ringing xfer db, chan %ld, brstmode %d db_mode %d\n", + "Not ringing xfer db, chan %u, brstmode %d db_mode %d\n", chan, ring_ctxt->db_mode.brstmode, ring_ctxt->db_mode.db_mode); } @@ -1950,23 +1951,24 @@ void mhi_process_db_brstmode(struct mhi_device_ctxt *mhi_dev_ctxt, void mhi_process_db_brstmode_disable(struct mhi_device_ctxt *mhi_dev_ctxt, void __iomem *io_addr, - uintptr_t chan, - u32 val) + unsigned int chan, + dma_addr_t val) { mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, - "db.set addr: %p io_offset 0x%lx val:0x%x\n", + "db.set addr: %p io_offset %u val:0x%llx\n", io_addr, chan, val); mhi_update_ctxt(mhi_dev_ctxt, io_addr, chan, val); mhi_write_db(mhi_dev_ctxt, io_addr, chan, val); } void mhi_process_db(struct mhi_device_ctxt *mhi_dev_ctxt, - void __iomem *io_addr, - uintptr_t chan, u32 val) + void __iomem *io_addr, + unsigned int chan, + dma_addr_t val) { mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, - "db.set addr: %p io_offset 0x%lx val:0x%x\n", + "db.set addr: %p io_offset %u val:0x%llx\n", io_addr, chan, val); mhi_update_ctxt(mhi_dev_ctxt, io_addr, chan, val); @@ -1981,7 +1983,7 @@ void mhi_process_db(struct mhi_device_ctxt *mhi_dev_ctxt, chan_ctxt->db_mode.db_mode = 0; } else { mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, - "Not ringing xfer db, chan %ld, brstmode %d db_mode %d\n", + "Not ringing xfer db, chan %u, brstmode %d db_mode %d\n", chan, chan_ctxt->db_mode.brstmode, chan_ctxt->db_mode.db_mode); } diff --git a/drivers/platform/msm/msm_ext_display.c b/drivers/platform/msm/msm_ext_display.c index 6f9a13040cd5..c7c1b1567bf3 100644 --- a/drivers/platform/msm/msm_ext_display.c +++ b/drivers/platform/msm/msm_ext_display.c @@ -654,6 +654,7 @@ int msm_ext_disp_register_audio_codec(struct platform_device *pdev, { int ret = 0; struct msm_ext_disp *ext_disp = NULL; + struct msm_ext_disp_list *node = NULL; if (!pdev || !ops) { pr_err("Invalid params\n"); @@ -671,17 +672,23 @@ int msm_ext_disp_register_audio_codec(struct platform_device *pdev, if ((ext_disp->current_disp != EXT_DISPLAY_TYPE_MAX) && ext_disp->ops) { pr_err("Codec already registered\n"); - ret = -EINVAL; - goto end; + mutex_unlock(&ext_disp->lock); + return -EINVAL; } ext_disp->ops = ops; - pr_debug("audio codec registered\n"); - -end: mutex_unlock(&ext_disp->lock); + list_for_each_entry(node, &ext_disp->display_list, list) { + struct msm_ext_disp_init_data *data = node->data; + + if (data->codec_ops.codec_ready) + data->codec_ops.codec_ready(data->pdev); + } + + pr_debug("audio codec registered\n"); + return ret; } diff --git a/drivers/platform/msm/seemp_core/seemp_logk.c b/drivers/platform/msm/seemp_core/seemp_logk.c index d0f21943cb0f..264db54f0d6e 100644 --- a/drivers/platform/msm/seemp_core/seemp_logk.c +++ b/drivers/platform/msm/seemp_core/seemp_logk.c @@ -287,7 +287,7 @@ static bool seemp_logk_get_bit_from_vector(__u8 *pVec, __u32 index) unsigned int bit_num = index%8; unsigned char byte; - if (DIV_ROUND_UP(index, 8) > MASK_BUFFER_SIZE) + if (byte_num >= MASK_BUFFER_SIZE) return false; byte = pVec[byte_num]; diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 1062fa42ff26..b2cdc1a1ad4f 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -1816,11 +1816,24 @@ static int __init acer_wmi_enable_lm(void) return status; } +#define ACER_WMID_ACCEL_HID "BST0001" + static acpi_status __init acer_wmi_get_handle_cb(acpi_handle ah, u32 level, void *ctx, void **retval) { + struct acpi_device *dev; + + if (!strcmp(ctx, "SENR")) { + if (acpi_bus_get_device(ah, &dev)) + return AE_OK; + if (!strcmp(ACER_WMID_ACCEL_HID, acpi_device_hid(dev))) + return AE_OK; + } else + return AE_OK; + *(acpi_handle *)retval = ah; - return AE_OK; + + return AE_CTRL_TERMINATE; } static int __init acer_wmi_get_handle(const char *name, const char *prop, @@ -1847,7 +1860,7 @@ static int __init acer_wmi_accel_setup(void) { int err; - err = acer_wmi_get_handle("SENR", "BST0001", &gsensor_handle); + err = acer_wmi_get_handle("SENR", ACER_WMID_ACCEL_HID, &gsensor_handle); if (err) return err; @@ -2185,10 +2198,11 @@ static int __init acer_wmi_init(void) err = acer_wmi_input_setup(); if (err) return err; + err = acer_wmi_accel_setup(); + if (err) + return err; } - acer_wmi_accel_setup(); - err = platform_driver_register(&acer_platform_driver); if (err) { pr_err("Unable to register platform driver\n"); diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 723b9eaf658a..8d7322a325de 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -114,7 +114,8 @@ static ssize_t power_supply_show_property(struct device *dev, return sprintf(buf, "%s\n", technology_text[value.intval]); else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL) return sprintf(buf, "%s\n", capacity_level_text[value.intval]); - else if (off == POWER_SUPPLY_PROP_TYPE) + else if (off == POWER_SUPPLY_PROP_TYPE || + off == POWER_SUPPLY_PROP_REAL_TYPE) return sprintf(buf, "%s\n", type_text[value.intval]); else if (off == POWER_SUPPLY_PROP_SCOPE) return sprintf(buf, "%s\n", scope_text[value.intval]); @@ -294,6 +295,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(connector_health), POWER_SUPPLY_ATTR(ctm_current_max), POWER_SUPPLY_ATTR(hw_current_max), + POWER_SUPPLY_ATTR(real_type), /* Local extensions of type int64_t */ POWER_SUPPLY_ATTR(charge_counter_ext), /* Properties of type `const char *' */ diff --git a/drivers/power/reset/at91-poweroff.c b/drivers/power/reset/at91-poweroff.c index e9e24df35f26..2579f025b90b 100644 --- a/drivers/power/reset/at91-poweroff.c +++ b/drivers/power/reset/at91-poweroff.c @@ -14,9 +14,12 @@ #include <linux/io.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_address.h> #include <linux/platform_device.h> #include <linux/printk.h> +#include <soc/at91/at91sam9_ddrsdr.h> + #define AT91_SHDW_CR 0x00 /* Shut Down Control Register */ #define AT91_SHDW_SHDW BIT(0) /* Shut Down command */ #define AT91_SHDW_KEY (0xa5 << 24) /* KEY Password */ @@ -50,6 +53,7 @@ static const char *shdwc_wakeup_modes[] = { static void __iomem *at91_shdwc_base; static struct clk *sclk; +static void __iomem *mpddrc_base; static void __init at91_wakeup_status(void) { @@ -73,6 +77,29 @@ static void at91_poweroff(void) writel(AT91_SHDW_KEY | AT91_SHDW_SHDW, at91_shdwc_base + AT91_SHDW_CR); } +static void at91_lpddr_poweroff(void) +{ + asm volatile( + /* Align to cache lines */ + ".balign 32\n\t" + + /* Ensure AT91_SHDW_CR is in the TLB by reading it */ + " ldr r6, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t" + + /* Power down SDRAM0 */ + " str %1, [%0, #" __stringify(AT91_DDRSDRC_LPR) "]\n\t" + /* Shutdown CPU */ + " str %3, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t" + + " b .\n\t" + : + : "r" (mpddrc_base), + "r" cpu_to_le32(AT91_DDRSDRC_LPDDR2_PWOFF), + "r" (at91_shdwc_base), + "r" cpu_to_le32(AT91_SHDW_KEY | AT91_SHDW_SHDW) + : "r0"); +} + static int at91_poweroff_get_wakeup_mode(struct device_node *np) { const char *pm; @@ -124,6 +151,8 @@ static void at91_poweroff_dt_set_wakeup_mode(struct platform_device *pdev) static int __init at91_poweroff_probe(struct platform_device *pdev) { struct resource *res; + struct device_node *np; + u32 ddr_type; int ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -150,12 +179,30 @@ static int __init at91_poweroff_probe(struct platform_device *pdev) pm_power_off = at91_poweroff; + np = of_find_compatible_node(NULL, NULL, "atmel,sama5d3-ddramc"); + if (!np) + return 0; + + mpddrc_base = of_iomap(np, 0); + of_node_put(np); + + if (!mpddrc_base) + return 0; + + ddr_type = readl(mpddrc_base + AT91_DDRSDRC_MDR) & AT91_DDRSDRC_MD; + if ((ddr_type == AT91_DDRSDRC_MD_LPDDR2) || + (ddr_type == AT91_DDRSDRC_MD_LPDDR3)) + pm_power_off = at91_lpddr_poweroff; + else + iounmap(mpddrc_base); + return 0; } static int __exit at91_poweroff_remove(struct platform_device *pdev) { - if (pm_power_off == at91_poweroff) + if (pm_power_off == at91_poweroff || + pm_power_off == at91_lpddr_poweroff) pm_power_off = NULL; clk_disable_unprepare(sclk); @@ -163,6 +210,11 @@ static int __exit at91_poweroff_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id at91_ramc_of_match[] = { + { .compatible = "atmel,sama5d3-ddramc", }, + { /* sentinel */ } +}; + static const struct of_device_id at91_poweroff_of_match[] = { { .compatible = "atmel,at91sam9260-shdwc", }, { .compatible = "atmel,at91sam9rl-shdwc", }, diff --git a/drivers/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c index 8d038ba0770d..acea4f213484 100644 --- a/drivers/power/reset/msm-poweroff.c +++ b/drivers/power/reset/msm-poweroff.c @@ -497,13 +497,13 @@ static size_t store_dload_mode(struct kobject *kobj, struct attribute *attr, if (sysfs_streq(buf, "full")) { dload_type = SCM_DLOAD_FULLDUMP; } else if (sysfs_streq(buf, "mini")) { - if (!msm_minidump_enabled()) { - pr_info("Minidump is not enabled\n"); + if (!minidump_enabled) { + pr_err("Minidump is not enabled\n"); return -ENODEV; } dload_type = SCM_DLOAD_MINIDUMP; } else { - pr_info("Invalid value. Use 'full' or 'mini'\n"); + pr_err("Invalid value. Use 'full' or 'mini'\n"); return -EINVAL; } diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c index 539e757d3e99..98d75f586b67 100644 --- a/drivers/power/supply/qcom/battery.c +++ b/drivers/power/supply/qcom/battery.c @@ -410,19 +410,6 @@ static int pl_fcc_vote_callback(struct votable *votable, void *data, if (!chip->main_psy) return 0; - if (chip->batt_psy) { - rc = power_supply_get_property(chip->batt_psy, - POWER_SUPPLY_PROP_CURRENT_QNOVO, - &pval); - if (rc < 0) { - pr_err("Couldn't get qnovo fcc, rc=%d\n", rc); - return rc; - } - - if (pval.intval != -EINVAL) - total_fcc_ua = pval.intval; - } - if (chip->pl_mode == POWER_SUPPLY_PL_NONE || get_effective_result_locked(chip->pl_disable_votable)) { pval.intval = total_fcc_ua; @@ -473,7 +460,6 @@ static int pl_fv_vote_callback(struct votable *votable, void *data, struct pl_data *chip = data; union power_supply_propval pval = {0, }; int rc = 0; - int effective_fv_uv = fv_uv; if (fv_uv < 0) return 0; @@ -481,20 +467,7 @@ static int pl_fv_vote_callback(struct votable *votable, void *data, if (!chip->main_psy) return 0; - if (chip->batt_psy) { - rc = power_supply_get_property(chip->batt_psy, - POWER_SUPPLY_PROP_VOLTAGE_QNOVO, - &pval); - if (rc < 0) { - pr_err("Couldn't get qnovo fv, rc=%d\n", rc); - return rc; - } - - if (pval.intval != -EINVAL) - effective_fv_uv = pval.intval; - } - - pval.intval = effective_fv_uv; + pval.intval = fv_uv; rc = power_supply_set_property(chip->main_psy, POWER_SUPPLY_PROP_VOLTAGE_MAX, &pval); @@ -930,11 +903,17 @@ static int pl_determine_initial_status(struct pl_data *chip) } #define DEFAULT_RESTRICTED_CURRENT_UA 1000000 -static int pl_init(void) +int qcom_batt_init(void) { struct pl_data *chip; int rc = 0; + /* initialize just once */ + if (the_chip) { + pr_err("was initialized earlier Failing now\n"); + return -EINVAL; + } + chip = kzalloc(sizeof(*chip), GFP_KERNEL); if (!chip) return -ENOMEM; @@ -1014,7 +993,9 @@ static int pl_init(void) goto unreg_notifier; } - return rc; + the_chip = chip; + + return 0; unreg_notifier: power_supply_unreg_notifier(&chip->nb); @@ -1031,21 +1012,23 @@ cleanup: return rc; } -static void pl_deinit(void) +void qcom_batt_deinit(void) { struct pl_data *chip = the_chip; + if (chip == NULL) + return; + + cancel_work_sync(&chip->status_change_work); + cancel_delayed_work_sync(&chip->pl_taper_work); + cancel_work_sync(&chip->pl_disable_forever_work); + power_supply_unreg_notifier(&chip->nb); destroy_votable(chip->pl_awake_votable); destroy_votable(chip->pl_disable_votable); destroy_votable(chip->fv_votable); destroy_votable(chip->fcc_votable); wakeup_source_unregister(chip->pl_ws); + the_chip = NULL; kfree(chip); } - -module_init(pl_init); -module_exit(pl_deinit) - -MODULE_DESCRIPTION(""); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/supply/qcom/battery.h b/drivers/power/supply/qcom/battery.h new file mode 100644 index 000000000000..38626e733a09 --- /dev/null +++ b/drivers/power/supply/qcom/battery.h @@ -0,0 +1,17 @@ +/* Copyright (c) 2017 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 __BATTERY_H +#define __BATTERY_H +int qcom_batt_init(void); +void qcom_batt_deinit(void); +#endif /* __BATTERY_H */ diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c index 4368709118ac..d0f7a5e1e227 100644 --- a/drivers/power/supply/qcom/qpnp-fg-gen3.c +++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c @@ -3366,6 +3366,16 @@ static int fg_hw_init(struct fg_chip *chip) return rc; } + if (is_debug_batt_id(chip)) { + val = ESR_NO_PULL_DOWN; + rc = fg_masked_write(chip, BATT_INFO_ESR_PULL_DN_CFG(chip), + ESR_PULL_DOWN_MODE_MASK, val); + if (rc < 0) { + pr_err("Error in writing esr_pull_down, rc=%d\n", rc); + return rc; + } + } + return 0; } diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c index 8855a1c74e0b..94b9e9c4d912 100644 --- a/drivers/power/supply/qcom/qpnp-smb2.c +++ b/drivers/power/supply/qcom/qpnp-smb2.c @@ -238,11 +238,9 @@ static struct smb_params pm660_params = { #define STEP_CHARGING_MAX_STEPS 5 struct smb_dt_props { - int fcc_ua; int usb_icl_ua; int dc_icl_ua; int boost_threshold_ua; - int fv_uv; int wipower_max_uw; int min_freq_khz; int max_freq_khz; @@ -310,14 +308,14 @@ static int smb2_parse_dt(struct smb2 *chip) "qcom,external-vconn"); rc = of_property_read_u32(node, - "qcom,fcc-max-ua", &chip->dt.fcc_ua); + "qcom,fcc-max-ua", &chg->batt_profile_fcc_ua); if (rc < 0) - chip->dt.fcc_ua = -EINVAL; + chg->batt_profile_fcc_ua = -EINVAL; rc = of_property_read_u32(node, - "qcom,fv-max-uv", &chip->dt.fv_uv); + "qcom,fv-max-uv", &chg->batt_profile_fv_uv); if (rc < 0) - chip->dt.fv_uv = -EINVAL; + chg->batt_profile_fv_uv = -EINVAL; rc = of_property_read_u32(node, "qcom,usb-icl-ua", &chip->dt.usb_icl_ua); @@ -429,6 +427,7 @@ static enum power_supply_property smb2_usb_props[] = { POWER_SUPPLY_PROP_PE_START, POWER_SUPPLY_PROP_CTM_CURRENT_MAX, POWER_SUPPLY_PROP_HW_CURRENT_MAX, + POWER_SUPPLY_PROP_REAL_TYPE, }; static int smb2_usb_get_prop(struct power_supply *psy, @@ -448,6 +447,16 @@ static int smb2_usb_get_prop(struct power_supply *psy, break; case POWER_SUPPLY_PROP_ONLINE: rc = smblib_get_prop_usb_online(chg, val); + if (!val->intval) + break; + + rc = smblib_get_prop_typec_mode(chg, val); + if ((val->intval == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT || + chg->micro_usb_mode) && + chg->real_charger_type == POWER_SUPPLY_TYPE_USB) + val->intval = 0; + else + val->intval = 1; break; case POWER_SUPPLY_PROP_VOLTAGE_MIN: val->intval = chg->voltage_min_uv; @@ -465,10 +474,13 @@ static int smb2_usb_get_prop(struct power_supply *psy, rc = smblib_get_prop_usb_current_max(chg, val); break; case POWER_SUPPLY_PROP_TYPE: + val->intval = POWER_SUPPLY_TYPE_USB_PD; + break; + case POWER_SUPPLY_PROP_REAL_TYPE: if (chip->bad_part) - val->intval = POWER_SUPPLY_TYPE_USB; + val->intval = POWER_SUPPLY_TYPE_USB_PD; else - val->intval = chg->usb_psy_desc.type; + val->intval = chg->real_charger_type; break; case POWER_SUPPLY_PROP_TYPEC_MODE: if (chg->micro_usb_mode) @@ -610,7 +622,7 @@ static int smb2_init_usb_psy(struct smb2 *chip) struct smb_charger *chg = &chip->chg; chg->usb_psy_desc.name = "usb"; - chg->usb_psy_desc.type = POWER_SUPPLY_TYPE_UNKNOWN; + chg->usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_PD; chg->usb_psy_desc.properties = smb2_usb_props; chg->usb_psy_desc.num_properties = ARRAY_SIZE(smb2_usb_props); chg->usb_psy_desc.get_property = smb2_usb_get_prop; @@ -619,7 +631,7 @@ static int smb2_init_usb_psy(struct smb2 *chip) usb_cfg.drv_data = chip; usb_cfg.of_node = chg->dev->of_node; - chg->usb_psy = devm_power_supply_register(chg->dev, + chg->usb_psy = power_supply_register(chg->dev, &chg->usb_psy_desc, &usb_cfg); if (IS_ERR(chg->usb_psy)) { @@ -630,6 +642,97 @@ static int smb2_init_usb_psy(struct smb2 *chip) return 0; } +/******************************** + * USB PC_PORT PSY REGISTRATION * + ********************************/ +static enum power_supply_property smb2_usb_port_props[] = { + POWER_SUPPLY_PROP_TYPE, + POWER_SUPPLY_PROP_ONLINE, +}; + +static int smb2_usb_port_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct smb2 *chip = power_supply_get_drvdata(psy); + struct smb_charger *chg = &chip->chg; + int rc = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_TYPE: + val->intval = POWER_SUPPLY_TYPE_USB; + break; + case POWER_SUPPLY_PROP_ONLINE: + rc = smblib_get_prop_usb_online(chg, val); + if (!val->intval) + break; + + rc = smblib_get_prop_typec_mode(chg, val); + if ((val->intval == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT || + chg->micro_usb_mode) && + chg->real_charger_type == POWER_SUPPLY_TYPE_USB) + val->intval = 1; + else + val->intval = 0; + break; + default: + pr_err_ratelimited("Get prop %d is not supported in pc_port\n", + psp); + return -EINVAL; + } + + if (rc < 0) { + pr_debug("Couldn't get prop %d rc = %d\n", psp, rc); + return -ENODATA; + } + + return 0; +} + +static int smb2_usb_port_set_prop(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + int rc = 0; + + switch (psp) { + default: + pr_err_ratelimited("Set prop %d is not supported in pc_port\n", + psp); + rc = -EINVAL; + break; + } + + return rc; +} + +static const struct power_supply_desc usb_port_psy_desc = { + .name = "pc_port", + .type = POWER_SUPPLY_TYPE_USB, + .properties = smb2_usb_port_props, + .num_properties = ARRAY_SIZE(smb2_usb_port_props), + .get_property = smb2_usb_port_get_prop, + .set_property = smb2_usb_port_set_prop, +}; + +static int smb2_init_usb_port_psy(struct smb2 *chip) +{ + struct power_supply_config usb_port_cfg = {}; + struct smb_charger *chg = &chip->chg; + + usb_port_cfg.drv_data = chip; + usb_port_cfg.of_node = chg->dev->of_node; + chg->usb_port_psy = power_supply_register(chg->dev, + &usb_port_psy_desc, + &usb_port_cfg); + if (IS_ERR(chg->usb_port_psy)) { + pr_err("Couldn't register USB pc_port power supply\n"); + return PTR_ERR(chg->usb_port_psy); + } + + return 0; +} + /***************************** * USB MAIN PSY REGISTRATION * *****************************/ @@ -677,7 +780,7 @@ static int smb2_usb_main_get_prop(struct power_supply *psy, rc = smblib_get_prop_fcc_delta(chg, val); break; case POWER_SUPPLY_PROP_CURRENT_MAX: - val->intval = get_effective_result(chg->usb_icl_votable); + rc = smblib_get_icl_current(chg, &val->intval); break; default: pr_debug("get prop %d is not supported in usb-main\n", psp); @@ -734,7 +837,7 @@ static int smb2_init_usb_main_psy(struct smb2 *chip) usb_main_cfg.drv_data = chip; usb_main_cfg.of_node = chg->dev->of_node; - chg->usb_main_psy = devm_power_supply_register(chg->dev, + chg->usb_main_psy = power_supply_register(chg->dev, &usb_main_psy_desc, &usb_main_cfg); if (IS_ERR(chg->usb_main_psy)) { @@ -836,7 +939,7 @@ static int smb2_init_dc_psy(struct smb2 *chip) dc_cfg.drv_data = chip; dc_cfg.of_node = chg->dev->of_node; - chg->dc_psy = devm_power_supply_register(chg->dev, + chg->dc_psy = power_supply_register(chg->dev, &dc_psy_desc, &dc_cfg); if (IS_ERR(chg->dc_psy)) { @@ -942,13 +1045,15 @@ static int smb2_batt_get_prop(struct power_supply *psy, rc = smblib_get_prop_charge_qnovo_enable(chg, val); break; case POWER_SUPPLY_PROP_VOLTAGE_QNOVO: - val->intval = chg->qnovo_fv_uv; + val->intval = get_client_vote_locked(chg->fv_votable, + QNOVO_VOTER); break; case POWER_SUPPLY_PROP_CURRENT_NOW: rc = smblib_get_prop_batt_current_now(chg, val); break; case POWER_SUPPLY_PROP_CURRENT_QNOVO: - val->intval = chg->qnovo_fcc_ua; + val->intval = get_client_vote_locked(chg->fcc_votable, + QNOVO_VOTER); break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: val->intval = get_client_vote(chg->fcc_votable, @@ -1014,23 +1119,37 @@ static int smb2_batt_set_prop(struct power_supply *psy, vote(chg->pl_disable_votable, USER_VOTER, (bool)val->intval, 0); break; case POWER_SUPPLY_PROP_VOLTAGE_MAX: - vote(chg->fv_votable, DEFAULT_VOTER, true, val->intval); + chg->batt_profile_fv_uv = val->intval; + vote(chg->fv_votable, BATT_PROFILE_VOTER, true, val->intval); break; case POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE: rc = smblib_set_prop_charge_qnovo_enable(chg, val); break; case POWER_SUPPLY_PROP_VOLTAGE_QNOVO: - chg->qnovo_fv_uv = val->intval; - rc = rerun_election(chg->fv_votable); + if (val->intval == -EINVAL) { + vote(chg->fv_votable, BATT_PROFILE_VOTER, + true, chg->batt_profile_fv_uv); + vote(chg->fv_votable, QNOVO_VOTER, false, 0); + } else { + vote(chg->fv_votable, QNOVO_VOTER, true, val->intval); + vote(chg->fv_votable, BATT_PROFILE_VOTER, false, 0); + } break; case POWER_SUPPLY_PROP_CURRENT_QNOVO: - chg->qnovo_fcc_ua = val->intval; vote(chg->pl_disable_votable, PL_QNOVO_VOTER, val->intval != -EINVAL && val->intval < 2000000, 0); - rc = rerun_election(chg->fcc_votable); + if (val->intval == -EINVAL) { + vote(chg->fcc_votable, BATT_PROFILE_VOTER, + true, chg->batt_profile_fcc_ua); + vote(chg->fcc_votable, QNOVO_VOTER, false, 0); + } else { + vote(chg->fcc_votable, QNOVO_VOTER, true, val->intval); + vote(chg->fcc_votable, BATT_PROFILE_VOTER, false, 0); + } break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: - vote(chg->fcc_votable, DEFAULT_VOTER, true, val->intval); + chg->batt_profile_fcc_ua = val->intval; + vote(chg->fcc_votable, BATT_PROFILE_VOTER, true, val->intval); break; case POWER_SUPPLY_PROP_SET_SHIP_MODE: /* Not in ship mode as long as the device is active */ @@ -1047,6 +1166,9 @@ static int smb2_batt_set_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_DP_DM: rc = smblib_dp_dm(chg, val->intval); break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED: + rc = smblib_set_prop_input_current_limited(chg, val); + break; default: rc = -EINVAL; } @@ -1064,6 +1186,7 @@ static int smb2_batt_prop_is_writeable(struct power_supply *psy, case POWER_SUPPLY_PROP_PARALLEL_DISABLE: case POWER_SUPPLY_PROP_DP_DM: case POWER_SUPPLY_PROP_RERUN_AICL: + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED: return 1; default: break; @@ -1090,7 +1213,7 @@ static int smb2_init_batt_psy(struct smb2 *chip) batt_cfg.drv_data = chg; batt_cfg.of_node = chg->dev->of_node; - chg->batt_psy = devm_power_supply_register(chg->dev, + chg->batt_psy = power_supply_register(chg->dev, &batt_psy_desc, &batt_cfg); if (IS_ERR(chg->batt_psy)) { @@ -1447,11 +1570,13 @@ static int smb2_init_hw(struct smb2 *chip) if (chip->dt.no_battery) chg->fake_capacity = 50; - if (chip->dt.fcc_ua < 0) - smblib_get_charge_param(chg, &chg->param.fcc, &chip->dt.fcc_ua); + if (chg->batt_profile_fcc_ua < 0) + smblib_get_charge_param(chg, &chg->param.fcc, + &chg->batt_profile_fcc_ua); - if (chip->dt.fv_uv < 0) - smblib_get_charge_param(chg, &chg->param.fv, &chip->dt.fv_uv); + if (chg->batt_profile_fv_uv < 0) + smblib_get_charge_param(chg, &chg->param.fv, + &chg->batt_profile_fv_uv); smblib_get_charge_param(chg, &chg->param.usb_icl, &chg->default_icl_ua); @@ -1512,9 +1637,9 @@ static int smb2_init_hw(struct smb2 *chip) vote(chg->dc_suspend_votable, DEFAULT_VOTER, chip->dt.no_battery, 0); vote(chg->fcc_votable, - DEFAULT_VOTER, true, chip->dt.fcc_ua); + BATT_PROFILE_VOTER, true, chg->batt_profile_fcc_ua); vote(chg->fv_votable, - DEFAULT_VOTER, true, chip->dt.fv_uv); + BATT_PROFILE_VOTER, true, chg->batt_profile_fv_uv); vote(chg->dc_icl_votable, DEFAULT_VOTER, true, chip->dt.dc_icl_ua); vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER, @@ -2058,6 +2183,21 @@ static int smb2_request_interrupts(struct smb2 *chip) return rc; } +static void smb2_free_interrupts(struct smb_charger *chg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(smb2_irqs); i++) { + if (smb2_irqs[i].irq > 0) { + if (smb2_irqs[i].wake) + disable_irq_wake(smb2_irqs[i].irq); + + devm_free_irq(chg->dev, smb2_irqs[i].irq, + smb2_irqs[i].irq_data); + } + } +} + static void smb2_disable_interrupts(struct smb_charger *chg) { int i; @@ -2235,7 +2375,13 @@ static int smb2_probe(struct platform_device *pdev) rc = smb2_init_usb_main_psy(chip); if (rc < 0) { - pr_err("Couldn't initialize usb psy rc=%d\n", rc); + pr_err("Couldn't initialize usb main psy rc=%d\n", rc); + goto cleanup; + } + + rc = smb2_init_usb_port_psy(chip); + if (rc < 0) { + pr_err("Couldn't initialize usb pc_port psy rc=%d\n", rc); goto cleanup; } @@ -2293,20 +2439,29 @@ static int smb2_probe(struct platform_device *pdev) device_init_wakeup(chg->dev, true); pr_info("QPNP SMB2 probed successfully usb:present=%d type=%d batt:present = %d health = %d charge = %d\n", - usb_present, chg->usb_psy_desc.type, + usb_present, chg->real_charger_type, batt_present, batt_health, batt_charge_type); return rc; cleanup: - smblib_deinit(chg); - if (chg->usb_psy) - power_supply_unregister(chg->usb_psy); + smb2_free_interrupts(chg); if (chg->batt_psy) power_supply_unregister(chg->batt_psy); + if (chg->usb_main_psy) + power_supply_unregister(chg->usb_main_psy); + if (chg->usb_psy) + power_supply_unregister(chg->usb_psy); + if (chg->usb_port_psy) + power_supply_unregister(chg->usb_port_psy); + if (chg->dc_psy) + power_supply_unregister(chg->dc_psy); if (chg->vconn_vreg && chg->vconn_vreg->rdev) - regulator_unregister(chg->vconn_vreg->rdev); + devm_regulator_unregister(chg->dev, chg->vconn_vreg->rdev); if (chg->vbus_vreg && chg->vbus_vreg->rdev) - regulator_unregister(chg->vbus_vreg->rdev); + devm_regulator_unregister(chg->dev, chg->vbus_vreg->rdev); + + smblib_deinit(chg); + platform_set_drvdata(pdev, NULL); return rc; } @@ -2318,6 +2473,7 @@ static int smb2_remove(struct platform_device *pdev) power_supply_unregister(chg->batt_psy); power_supply_unregister(chg->usb_psy); + power_supply_unregister(chg->usb_port_psy); regulator_unregister(chg->vconn_vreg->rdev); regulator_unregister(chg->vbus_vreg->rdev); diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c index 1e417e8aa22d..a191391a3904 100644 --- a/drivers/power/supply/qcom/smb-lib.c +++ b/drivers/power/supply/qcom/smb-lib.c @@ -19,10 +19,11 @@ #include <linux/qpnp/qpnp-revid.h> #include <linux/input/qpnp-power-on.h> #include <linux/irq.h> +#include <linux/pmic-voter.h> #include "smb-lib.h" #include "smb-reg.h" +#include "battery.h" #include "storm-watch.h" -#include <linux/pmic-voter.h> #define smblib_err(chg, fmt, ...) \ pr_err("%s: %s: " fmt, chg->name, \ @@ -549,9 +550,9 @@ static const struct apsd_result *smblib_update_usb_type(struct smb_charger *chg) /* if PD is active, APSD is disabled so won't have a valid result */ if (chg->pd_active) - chg->usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_PD; + chg->real_charger_type = POWER_SUPPLY_TYPE_USB_PD; else - chg->usb_psy_desc.type = apsd_result->pst; + chg->real_charger_type = apsd_result->pst; smblib_dbg(chg, PR_MISC, "APSD=%s PD=%d\n", apsd_result->name, chg->pd_active); @@ -813,6 +814,28 @@ static int set_sdp_current(struct smb_charger *chg, int icl_ua) return rc; } +static int get_sdp_current(struct smb_charger *chg, int *icl_ua) +{ + int rc; + u8 icl_options; + bool usb3 = false; + + rc = smblib_read(chg, USBIN_ICL_OPTIONS_REG, &icl_options); + if (rc < 0) { + smblib_err(chg, "Couldn't get ICL options rc=%d\n", rc); + return rc; + } + + usb3 = (icl_options & CFG_USB3P0_SEL_BIT); + + if (icl_options & USB51_MODE_BIT) + *icl_ua = usb3 ? USBIN_900MA : USBIN_500MA; + else + *icl_ua = usb3 ? USBIN_150MA : USBIN_100MA; + + return rc; +} + int smblib_set_icl_current(struct smb_charger *chg, int icl_ua) { int rc = 0; @@ -835,7 +858,7 @@ int smblib_set_icl_current(struct smb_charger *chg, int icl_ua) /* configure current */ if (pval.intval == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT - && (chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB)) { + && (chg->real_charger_type == POWER_SUPPLY_TYPE_USB)) { rc = set_sdp_current(chg, icl_ua); if (rc < 0) { smblib_err(chg, "Couldn't set SDP ICL rc=%d\n", rc); @@ -856,10 +879,10 @@ override_suspend_config: /* remove override if no voters - hw defaults is desired */ override = false; } else if (pval.intval == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) { - if (chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB) + if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB) /* For std cable with type = SDP never override */ override = false; - else if (chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB_CDP + else if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_CDP && icl_ua == 1500000) /* * For std cable with type = CDP override only if @@ -890,6 +913,48 @@ enable_icl_changed_interrupt: return rc; } +int smblib_get_icl_current(struct smb_charger *chg, int *icl_ua) +{ + int rc = 0; + u8 load_cfg; + bool override; + union power_supply_propval pval; + + rc = smblib_get_prop_typec_mode(chg, &pval); + if (rc < 0) { + smblib_err(chg, "Couldn't get typeC mode rc = %d\n", rc); + return rc; + } + + if ((pval.intval == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT + || chg->micro_usb_mode) + && (chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB)) { + rc = get_sdp_current(chg, icl_ua); + if (rc < 0) { + smblib_err(chg, "Couldn't get SDP ICL rc=%d\n", rc); + return rc; + } + } else { + rc = smblib_read(chg, USBIN_LOAD_CFG_REG, &load_cfg); + if (rc < 0) { + smblib_err(chg, "Couldn't get load cfg rc=%d\n", rc); + return rc; + } + override = load_cfg & ICL_OVERRIDE_AFTER_APSD_BIT; + if (!override) + return INT_MAX; + + /* override is set */ + rc = smblib_get_charge_param(chg, &chg->param.usb_icl, icl_ua); + if (rc < 0) { + smblib_err(chg, "Couldn't get HC ICL rc=%d\n", rc); + return rc; + } + } + + return 0; +} + /********************* * VOTABLE CALLBACKS * *********************/ @@ -1642,6 +1707,11 @@ int smblib_get_prop_input_current_limited(struct smb_charger *chg, u8 stat; int rc; + if (chg->fake_input_current_limited >= 0) { + val->intval = chg->fake_input_current_limited; + return 0; + } + rc = smblib_read(chg, AICL_STATUS_REG, &stat); if (rc < 0) { smblib_err(chg, "Couldn't read AICL_STATUS rc=%d\n", rc); @@ -1833,6 +1903,13 @@ int smblib_set_prop_charge_qnovo_enable(struct smb_charger *chg, return rc; } +int smblib_set_prop_input_current_limited(struct smb_charger *chg, + const union power_supply_propval *val) +{ + chg->fake_input_current_limited = val->intval; + return 0; +} + int smblib_rerun_aicl(struct smb_charger *chg) { int rc, settled_icl_ua; @@ -2816,7 +2893,7 @@ int smblib_get_prop_fcc_delta(struct smb_charger *chg, int smblib_get_charge_current(struct smb_charger *chg, int *total_current_ua) { - const struct apsd_result *apsd_result = smblib_update_usb_type(chg); + const struct apsd_result *apsd_result = smblib_get_apsd_result(chg); union power_supply_propval val = {0, }; int rc = 0, typec_source_rd, current_ua; bool non_compliant; @@ -3088,12 +3165,28 @@ static void smblib_micro_usb_plugin(struct smb_charger *chg, bool vbus_rising) } } -static void smblib_typec_usb_plugin(struct smb_charger *chg, bool vbus_rising) +void smblib_usb_plugin_hard_reset_locked(struct smb_charger *chg) { + int rc; + u8 stat; + bool vbus_rising; + + rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat); + if (rc < 0) { + smblib_err(chg, "Couldn't read USB_INT_RT_STS rc=%d\n", rc); + return; + } + + vbus_rising = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT); + if (vbus_rising) smblib_cc2_sink_removal_exit(chg); else smblib_cc2_sink_removal_enter(chg); + + power_supply_changed(chg->usb_psy); + smblib_dbg(chg, PR_INTERRUPT, "IRQ: usbin-plugin %s\n", + vbus_rising ? "attached" : "detached"); } #define PL_DELAY_MS 30000 @@ -3152,8 +3245,6 @@ void smblib_usb_plugin_locked(struct smb_charger *chg) if (chg->micro_usb_mode) smblib_micro_usb_plugin(chg, vbus_rising); - else - smblib_typec_usb_plugin(chg, vbus_rising); power_supply_changed(chg->usb_psy); smblib_dbg(chg, PR_INTERRUPT, "IRQ: usbin-plugin %s\n", @@ -3166,7 +3257,10 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data) struct smb_charger *chg = irq_data->parent_data; mutex_lock(&chg->lock); - smblib_usb_plugin_locked(chg); + if (chg->pd_hard_reset) + smblib_usb_plugin_hard_reset_locked(chg); + else + smblib_usb_plugin_locked(chg); mutex_unlock(&chg->lock); return IRQ_HANDLED; } @@ -3232,7 +3326,7 @@ static void smblib_hvdcp_adaptive_voltage_change(struct smb_charger *chg) int pulses; power_supply_changed(chg->usb_main_psy); - if (chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB_HVDCP) { + if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP) { rc = smblib_read(chg, QC_CHANGE_STATUS_REG, &stat); if (rc < 0) { smblib_err(chg, @@ -3260,7 +3354,7 @@ static void smblib_hvdcp_adaptive_voltage_change(struct smb_charger *chg) } } - if (chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB_HVDCP_3) { + if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP_3) { rc = smblib_read(chg, QC_PULSE_COUNT_STATUS_REG, &stat); if (rc < 0) { smblib_err(chg, @@ -3330,7 +3424,7 @@ static void smblib_handle_hvdcp_3p0_auth_done(struct smb_charger *chg, static void smblib_handle_hvdcp_check_timeout(struct smb_charger *chg, bool rising, bool qc_charger) { - const struct apsd_result *apsd_result = smblib_update_usb_type(chg); + const struct apsd_result *apsd_result = smblib_get_apsd_result(chg); /* Hold off PD only until hvdcp 2.0 detection timeout */ if (rising) { @@ -4187,26 +4281,30 @@ static int smblib_create_votables(struct smb_charger *chg) int rc = 0; chg->fcc_votable = find_votable("FCC"); - if (!chg->fcc_votable) { - rc = -EPROBE_DEFER; + if (chg->fcc_votable == NULL) { + rc = -EINVAL; + smblib_err(chg, "Couldn't find FCC votable rc=%d\n", rc); return rc; } chg->fv_votable = find_votable("FV"); - if (!chg->fv_votable) { - rc = -EPROBE_DEFER; + if (chg->fv_votable == NULL) { + rc = -EINVAL; + smblib_err(chg, "Couldn't find FV votable rc=%d\n", rc); return rc; } chg->usb_icl_votable = find_votable("USB_ICL"); if (!chg->usb_icl_votable) { - rc = -EPROBE_DEFER; + rc = -EINVAL; + smblib_err(chg, "Couldn't find USB_ICL votable rc=%d\n", rc); return rc; } chg->pl_disable_votable = find_votable("PL_DISABLE"); - if (!chg->pl_disable_votable) { - rc = -EPROBE_DEFER; + if (chg->pl_disable_votable == NULL) { + rc = -EINVAL; + smblib_err(chg, "Couldn't find votable PL_DISABLE rc=%d\n", rc); return rc; } vote(chg->pl_disable_votable, PL_INDIRECT_VOTER, true, 0); @@ -4385,11 +4483,17 @@ int smblib_init(struct smb_charger *chg) INIT_DELAYED_WORK(&chg->pl_enable_work, smblib_pl_enable_work); INIT_WORK(&chg->legacy_detection_work, smblib_legacy_detection_work); chg->fake_capacity = -EINVAL; + chg->fake_input_current_limited = -EINVAL; switch (chg->mode) { case PARALLEL_MASTER: - chg->qnovo_fcc_ua = -EINVAL; - chg->qnovo_fv_uv = -EINVAL; + rc = qcom_batt_init(); + if (rc < 0) { + smblib_err(chg, "Couldn't init qcom_batt_init rc=%d\n", + rc); + return rc; + } + rc = smblib_create_votables(chg); if (rc < 0) { smblib_err(chg, "Couldn't create votables rc=%d\n", @@ -4421,8 +4525,20 @@ int smblib_deinit(struct smb_charger *chg) { switch (chg->mode) { case PARALLEL_MASTER: + cancel_work_sync(&chg->bms_update_work); + cancel_work_sync(&chg->rdstd_cc2_detach_work); + cancel_delayed_work_sync(&chg->hvdcp_detect_work); + cancel_delayed_work_sync(&chg->step_soc_req_work); + cancel_delayed_work_sync(&chg->clear_hdc_work); + cancel_work_sync(&chg->otg_oc_work); + cancel_work_sync(&chg->vconn_oc_work); + cancel_delayed_work_sync(&chg->otg_ss_done_work); + cancel_delayed_work_sync(&chg->icl_change_work); + cancel_delayed_work_sync(&chg->pl_enable_work); + cancel_work_sync(&chg->legacy_detection_work); power_supply_unreg_notifier(&chg->nb); smblib_destroy_votables(chg); + qcom_batt_deinit(); break; case PARALLEL_SLAVE: break; diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h index b0d84f014b0d..5dc60ecb9436 100644 --- a/drivers/power/supply/qcom/smb-lib.h +++ b/drivers/power/supply/qcom/smb-lib.h @@ -62,6 +62,8 @@ enum print_reason { #define AICL_RERUN_VOTER "AICL_RERUN_VOTER" #define LEGACY_UNKNOWN_VOTER "LEGACY_UNKNOWN_VOTER" #define CC2_WA_VOTER "CC2_WA_VOTER" +#define QNOVO_VOTER "QNOVO_VOTER" +#define BATT_PROFILE_VOTER "BATT_PROFILE_VOTER" #define VCONN_MAX_ATTEMPTS 3 #define OTG_MAX_ATTEMPTS 3 @@ -242,6 +244,8 @@ struct smb_charger { struct power_supply *bms_psy; struct power_supply_desc usb_psy_desc; struct power_supply *usb_main_psy; + struct power_supply *usb_port_psy; + enum power_supply_type real_charger_type; /* notifiers */ struct notifier_block nb; @@ -314,6 +318,7 @@ struct smb_charger { bool typec_present; u8 typec_status[5]; bool typec_legacy_valid; + int fake_input_current_limited; /* workaround flag */ u32 wa_flags; @@ -325,9 +330,11 @@ struct smb_charger { /* extcon for VBUS / ID notification to USB for uUSB */ struct extcon_dev *extcon; + /* battery profile */ + int batt_profile_fcc_ua; + int batt_profile_fv_uv; + /* qnovo */ - int qnovo_fcc_ua; - int qnovo_fv_uv; int usb_icl_delta_ua; int pulse_cnt; }; @@ -415,6 +422,8 @@ int smblib_set_prop_batt_capacity(struct smb_charger *chg, const union power_supply_propval *val); int smblib_set_prop_system_temp_level(struct smb_charger *chg, const union power_supply_propval *val); +int smblib_set_prop_input_current_limited(struct smb_charger *chg, + const union power_supply_propval *val); int smblib_get_prop_dc_present(struct smb_charger *chg, union power_supply_propval *val); @@ -493,6 +502,7 @@ int smblib_icl_override(struct smb_charger *chg, bool override); int smblib_dp_dm(struct smb_charger *chg, int val); int smblib_rerun_aicl(struct smb_charger *chg); int smblib_set_icl_current(struct smb_charger *chg, int icl_ua); +int smblib_get_icl_current(struct smb_charger *chg, int *icl_ua); int smblib_get_charge_current(struct smb_charger *chg, int *total_current_ua); int smblib_init(struct smb_charger *chg); diff --git a/drivers/power/supply/qcom/smb-reg.h b/drivers/power/supply/qcom/smb-reg.h index 3f260a407721..167666a8c548 100644 --- a/drivers/power/supply/qcom/smb-reg.h +++ b/drivers/power/supply/qcom/smb-reg.h @@ -1025,14 +1025,4 @@ enum { /* CHGR FREQ Peripheral registers */ #define FREQ_CLK_DIV_REG (CHGR_FREQ_BASE + 0x50) -/* SMB1355 specific registers */ -#define SMB1355_TEMP_COMP_STATUS_REG (MISC_BASE + 0x07) -#define SKIN_TEMP_RST_HOT_BIT BIT(6) -#define SKIN_TEMP_UB_HOT_BIT BIT(5) -#define SKIN_TEMP_LB_HOT_BIT BIT(4) -#define DIE_TEMP_TSD_HOT_BIT BIT(3) -#define DIE_TEMP_RST_HOT_BIT BIT(2) -#define DIE_TEMP_UB_HOT_BIT BIT(1) -#define DIE_TEMP_LB_HOT_BIT BIT(0) - #endif /* __SMB2_CHARGER_REG_H */ diff --git a/drivers/power/supply/qcom/smb138x-charger.c b/drivers/power/supply/qcom/smb138x-charger.c index 4e710cae6b78..694591c3ec56 100644 --- a/drivers/power/supply/qcom/smb138x-charger.c +++ b/drivers/power/supply/qcom/smb138x-charger.c @@ -104,8 +104,6 @@ struct smb138x { struct smb_dt_props dt; struct power_supply *parallel_psy; u32 wa_flags; - struct pmic_revid_data *pmic_rev_id; - char *name; }; static int __debug_mask; @@ -169,14 +167,6 @@ static int smb138x_parse_dt(struct smb138x *chip) if (rc < 0) chip->dt.pl_mode = POWER_SUPPLY_PL_USBMID_USBMID; - /* check that smb1355 is configured to run in mid-mid mode */ - if (chip->pmic_rev_id->pmic_subtype == SMB1355_SUBTYPE - && chip->dt.pl_mode != POWER_SUPPLY_PL_USBMID_USBMID) { - pr_err("Smb1355 can only run in MID-MID mode, saw = %d mode\n", - chip->dt.pl_mode); - return -EINVAL; - } - chip->dt.suspend_input = of_property_read_bool(node, "qcom,suspend-input"); @@ -489,30 +479,6 @@ static int smb138x_init_batt_psy(struct smb138x *chip) * PARALLEL PSY REGISTRATION * *****************************/ -static int smb1355_get_prop_connector_health(struct smb138x *chip) -{ - struct smb_charger *chg = &chip->chg; - u8 temp; - int rc; - - rc = smblib_read(chg, SMB1355_TEMP_COMP_STATUS_REG, &temp); - if (rc < 0) { - pr_err("Couldn't read comp stat reg rc = %d\n", rc); - return POWER_SUPPLY_HEALTH_UNKNOWN; - } - - if (temp & SKIN_TEMP_RST_HOT_BIT) - return POWER_SUPPLY_HEALTH_OVERHEAT; - - if (temp & SKIN_TEMP_UB_HOT_BIT) - return POWER_SUPPLY_HEALTH_HOT; - - if (temp & SKIN_TEMP_LB_HOT_BIT) - return POWER_SUPPLY_HEALTH_WARM; - - return POWER_SUPPLY_HEALTH_COOL; -} - static int smb138x_get_prop_connector_health(struct smb138x *chip) { struct smb_charger *chg = &chip->chg; @@ -570,32 +536,16 @@ static enum power_supply_property smb138x_parallel_props[] = { POWER_SUPPLY_PROP_PIN_ENABLED, POWER_SUPPLY_PROP_INPUT_SUSPEND, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED, + POWER_SUPPLY_PROP_CURRENT_MAX, POWER_SUPPLY_PROP_VOLTAGE_MAX, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, - POWER_SUPPLY_PROP_MODEL_NAME, - POWER_SUPPLY_PROP_PARALLEL_MODE, - POWER_SUPPLY_PROP_CONNECTOR_HEALTH, - POWER_SUPPLY_PROP_SET_SHIP_MODE, + POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_CHARGER_TEMP, POWER_SUPPLY_PROP_CHARGER_TEMP_MAX, - POWER_SUPPLY_PROP_CURRENT_NOW, - POWER_SUPPLY_PROP_CURRENT_MAX, -}; - -static enum power_supply_property smb1355_parallel_props[] = { - POWER_SUPPLY_PROP_CHARGE_TYPE, - POWER_SUPPLY_PROP_CHARGING_ENABLED, - POWER_SUPPLY_PROP_PIN_ENABLED, - POWER_SUPPLY_PROP_INPUT_SUSPEND, - POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED, - POWER_SUPPLY_PROP_VOLTAGE_MAX, - POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_PARALLEL_MODE, POWER_SUPPLY_PROP_CONNECTOR_HEALTH, POWER_SUPPLY_PROP_SET_SHIP_MODE, - POWER_SUPPLY_PROP_CHARGER_TEMP, - POWER_SUPPLY_PROP_CHARGER_TEMP_MAX, }; static int smb138x_parallel_get_prop(struct power_supply *psy, @@ -633,6 +583,14 @@ static int smb138x_parallel_get_prop(struct power_supply *psy, else val->intval = 0; break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + if ((chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) + || (chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT)) + rc = smblib_get_charge_param(chg, &chg->param.usb_icl, + &val->intval); + else + val->intval = 0; + break; case POWER_SUPPLY_PROP_VOLTAGE_MAX: rc = smblib_get_charge_param(chg, &chg->param.fv, &val->intval); break; @@ -640,46 +598,28 @@ static int smb138x_parallel_get_prop(struct power_supply *psy, rc = smblib_get_charge_param(chg, &chg->param.fcc, &val->intval); break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + rc = smblib_get_prop_slave_current_now(chg, val); + break; + case POWER_SUPPLY_PROP_CHARGER_TEMP: + rc = smb138x_get_prop_charger_temp(chip, val); + break; + case POWER_SUPPLY_PROP_CHARGER_TEMP_MAX: + rc = smblib_get_prop_charger_temp_max(chg, val); + break; case POWER_SUPPLY_PROP_MODEL_NAME: - val->strval = chip->name; + val->strval = "smb138x"; break; case POWER_SUPPLY_PROP_PARALLEL_MODE: val->intval = chip->dt.pl_mode; break; case POWER_SUPPLY_PROP_CONNECTOR_HEALTH: - if (chip->pmic_rev_id->pmic_subtype != SMB1355_SUBTYPE) - val->intval = smb138x_get_prop_connector_health(chip); - else - val->intval = smb1355_get_prop_connector_health(chip); + val->intval = smb138x_get_prop_connector_health(chip); break; case POWER_SUPPLY_PROP_SET_SHIP_MODE: /* Not in ship mode as long as device is active */ val->intval = 0; break; - case POWER_SUPPLY_PROP_CHARGER_TEMP: - if (chip->pmic_rev_id->pmic_subtype != SMB1355_SUBTYPE) - rc = smb138x_get_prop_charger_temp(chip, val); - else - rc = smblib_get_prop_charger_temp(chg, val); - break; - case POWER_SUPPLY_PROP_CHARGER_TEMP_MAX: - rc = smblib_get_prop_charger_temp_max(chg, val); - break; - case POWER_SUPPLY_PROP_CURRENT_NOW: - if (chip->pmic_rev_id->pmic_subtype != SMB1355_SUBTYPE) - rc = smblib_get_prop_slave_current_now(chg, val); - else - rc = -ENODATA; - break; - case POWER_SUPPLY_PROP_CURRENT_MAX: - if ((chip->pmic_rev_id->pmic_subtype != SMB1355_SUBTYPE) - && ((chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) - || (chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT))) - rc = smblib_get_charge_param(chg, &chg->param.usb_icl, - &val->intval); - else - rc = -ENODATA; - break; default: pr_err("parallel power supply get prop %d not supported\n", prop); @@ -763,7 +703,7 @@ static int smb138x_parallel_prop_is_writeable(struct power_supply *psy, return 0; } -static struct power_supply_desc parallel_psy_desc = { +static const struct power_supply_desc parallel_psy_desc = { .name = "parallel", .type = POWER_SUPPLY_TYPE_PARALLEL, .properties = smb138x_parallel_props, @@ -791,28 +731,6 @@ static int smb138x_init_parallel_psy(struct smb138x *chip) return 0; } -static int smb1355_init_parallel_psy(struct smb138x *chip) -{ - struct power_supply_config parallel_cfg = {}; - struct smb_charger *chg = &chip->chg; - - parallel_cfg.drv_data = chip; - parallel_cfg.of_node = chg->dev->of_node; - - /* change to smb1355's property list */ - parallel_psy_desc.properties = smb1355_parallel_props; - parallel_psy_desc.num_properties = ARRAY_SIZE(smb1355_parallel_props); - chip->parallel_psy = devm_power_supply_register(chg->dev, - ¶llel_psy_desc, - ¶llel_cfg); - if (IS_ERR(chip->parallel_psy)) { - pr_err("Couldn't register parallel power supply\n"); - return PTR_ERR(chip->parallel_psy); - } - - return 0; -} - /****************************** * VBUS REGULATOR REGISTRATION * ******************************/ @@ -1132,6 +1050,7 @@ static int smb138x_init_hw(struct smb138x *chip) static int smb138x_setup_wa_flags(struct smb138x *chip) { + struct pmic_revid_data *pmic_rev_id; struct device_node *revid_dev_node; revid_dev_node = of_parse_phandle(chip->chg.dev->of_node, @@ -1141,8 +1060,8 @@ static int smb138x_setup_wa_flags(struct smb138x *chip) return -EINVAL; } - chip->pmic_rev_id = get_revid_data(revid_dev_node); - if (IS_ERR_OR_NULL(chip->pmic_rev_id)) { + pmic_rev_id = get_revid_data(revid_dev_node); + if (IS_ERR_OR_NULL(pmic_rev_id)) { /* * the revid peripheral must be registered, any failure * here only indicates that the rev-id module has not @@ -1151,14 +1070,14 @@ static int smb138x_setup_wa_flags(struct smb138x *chip) return -EPROBE_DEFER; } - switch (chip->pmic_rev_id->pmic_subtype) { + switch (pmic_rev_id->pmic_subtype) { case SMB1381_SUBTYPE: - if (chip->pmic_rev_id->rev4 < 2) /* SMB1381 rev 1.0 */ + if (pmic_rev_id->rev4 < 2) /* SMB1381 rev 1.0 */ chip->wa_flags |= OOB_COMP_WA_BIT; break; default: pr_err("PMIC subtype %d not supported\n", - chip->pmic_rev_id->pmic_subtype); + pmic_rev_id->pmic_subtype); return -EINVAL; } @@ -1456,7 +1375,6 @@ static int smb138x_master_probe(struct smb138x *chip) chg->param = v1_params; - chip->name = "smb1381"; rc = smblib_init(chg); if (rc < 0) { pr_err("Couldn't initialize smblib rc=%d\n", rc); @@ -1517,7 +1435,7 @@ static int smb138x_master_probe(struct smb138x *chip) return rc; } -static int smb1355_slave_probe(struct smb138x *chip) +static int smb138x_slave_probe(struct smb138x *chip) { struct smb_charger *chg = &chip->chg; int rc = 0; @@ -1530,55 +1448,6 @@ static int smb1355_slave_probe(struct smb138x *chip) goto cleanup; } - rc = smb138x_parse_dt(chip); - if (rc < 0) { - pr_err("Couldn't parse device tree rc=%d\n", rc); - goto cleanup; - } - - rc = smb138x_init_slave_hw(chip); - if (rc < 0) { - pr_err("Couldn't initialize hardware rc=%d\n", rc); - goto cleanup; - } - - rc = smb1355_init_parallel_psy(chip); - if (rc < 0) { - pr_err("Couldn't initialize parallel psy rc=%d\n", rc); - goto cleanup; - } - - rc = smb138x_determine_initial_slave_status(chip); - if (rc < 0) { - pr_err("Couldn't determine initial status rc=%d\n", rc); - goto cleanup; - } - - rc = smb138x_request_interrupts(chip); - if (rc < 0) { - pr_err("Couldn't request interrupts rc=%d\n", rc); - goto cleanup; - } - - return 0; - -cleanup: - smblib_deinit(chg); - return rc; -} - -static int smb1381_slave_probe(struct smb138x *chip) -{ - struct smb_charger *chg = &chip->chg; - int rc = 0; - - chg->param = v1_params; - - rc = smblib_init(chg); - if (rc < 0) { - pr_err("Couldn't initialize smblib rc=%d\n", rc); - goto cleanup; - } chg->iio.temp_max_chan = iio_channel_get(chg->dev, "charger_temp_max"); if (IS_ERR(chg->iio.temp_max_chan)) { rc = PTR_ERR(chg->iio.temp_max_chan); @@ -1646,71 +1515,25 @@ static int smb1381_slave_probe(struct smb138x *chip) goto cleanup; } - return 0; + return rc; cleanup: smblib_deinit(chg); + if (chip->parallel_psy) + power_supply_unregister(chip->parallel_psy); + if (chg->vbus_vreg && chg->vbus_vreg->rdev) + regulator_unregister(chg->vbus_vreg->rdev); return rc; } -static int slave_probe(struct smb138x *chip) -{ - struct device_node *revid_dev_node; - int rc = 0; - - revid_dev_node = of_parse_phandle(chip->chg.dev->of_node, - "qcom,pmic-revid", 0); - if (!revid_dev_node) { - pr_err("Missing qcom,pmic-revid property\n"); - return -EINVAL; - } - - chip->pmic_rev_id = get_revid_data(revid_dev_node); - if (IS_ERR_OR_NULL(chip->pmic_rev_id)) { - /* - * the revid peripheral must be registered, any failure - * here only indicates that the rev-id module has not - * probed yet. - */ - return -EPROBE_DEFER; - } - - switch (chip->pmic_rev_id->pmic_subtype) { - case SMB1355_SUBTYPE: - chip->name = "smb1355"; - rc = smb1355_slave_probe(chip); - break; - case SMB1381_SUBTYPE: - chip->name = "smb1381"; - rc = smb1381_slave_probe(chip); - break; - default: - pr_err("Unsupported pmic subtype = 0x%02x\n", - chip->pmic_rev_id->pmic_subtype); - rc = -EINVAL; - } - - if (rc < 0) { - if (rc != -EPROBE_DEFER) - pr_err("Couldn't probe SMB138X rc=%d\n", rc); - return rc; - } - - return 0; -} - static const struct of_device_id match_table[] = { { - .compatible = "qcom,smb138x-charger", - .data = (void *) PARALLEL_MASTER, - }, - { - .compatible = "qcom,smb138x-parallel-slave", - .data = (void *) PARALLEL_SLAVE, + .compatible = "qcom,smb138x-charger", + .data = (void *) PARALLEL_MASTER }, { - .compatible = "qcom,smb1355-parallel-slave", - .data = (void *) PARALLEL_SLAVE, + .compatible = "qcom,smb138x-parallel-slave", + .data = (void *) PARALLEL_SLAVE }, { }, }; @@ -1757,7 +1580,7 @@ static int smb138x_probe(struct platform_device *pdev) rc = smb138x_master_probe(chip); break; case PARALLEL_SLAVE: - rc = slave_probe(chip); + rc = smb138x_slave_probe(chip); break; default: pr_err("Couldn't find a matching mode %d\n", chip->chg.mode); @@ -1771,8 +1594,7 @@ static int smb138x_probe(struct platform_device *pdev) goto cleanup; } - pr_info("%s probed successfully mode=%d pl_mode = %d\n", - chip->name, chip->chg.mode, chip->dt.pl_mode); + pr_info("SMB138X probed successfully mode=%d\n", chip->chg.mode); return rc; cleanup: diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index e5ba63171eba..88956d3ba674 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -472,6 +472,15 @@ config REGULATOR_MT6397 This driver supports the control of different power rails of device through regulator interface. +config REGULATOR_ONSEMI_NCP6335D + tristate "OnSemi NCP6335D regulator support" + depends on I2C + help + This driver supports the OnSemi NCP6335D switching voltage regulator + (buck converter). The regulator is controlled using an I2C interface + and supports a programmable voltage range from 0.6V to 1.4V in steps + of 6.25mV. + config REGULATOR_PALMAS tristate "TI Palmas PMIC Regulators" depends on MFD_PALMAS diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 20cf304a4714..e345f10f94af 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -65,6 +65,7 @@ obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o obj-$(CONFIG_REGULATOR_QCOM_SMD_RPM) += qcom_smd-regulator.o obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o +obj-$(CONFIG_REGULATOR_ONSEMI_NCP6335D) += onsemi-ncp6335d.o obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o obj-$(CONFIG_REGULATOR_PWM) += pwm-regulator.o diff --git a/drivers/regulator/onsemi-ncp6335d.c b/drivers/regulator/onsemi-ncp6335d.c new file mode 100644 index 000000000000..3b4ca4f81527 --- /dev/null +++ b/drivers/regulator/onsemi-ncp6335d.c @@ -0,0 +1,775 @@ +/* Copyright (c) 2012-2014, 2017 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/module.h> +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/log2.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/regmap.h> +#include <linux/debugfs.h> +#include <linux/regulator/onsemi-ncp6335d.h> +#include <linux/string.h> + +/* registers */ +#define REG_NCP6335D_PID 0x03 +#define REG_NCP6335D_PROGVSEL1 0x10 +#define REG_NCP6335D_PROGVSEL0 0x11 +#define REG_NCP6335D_PGOOD 0x12 +#define REG_NCP6335D_TIMING 0x13 +#define REG_NCP6335D_COMMAND 0x14 + +/* constraints */ +#define NCP6335D_MIN_VOLTAGE_UV 600000 +#define NCP6335D_STEP_VOLTAGE_UV 6250 +#define NCP6335D_VOLTAGE_STEPS 128 +#define NCP6335D_MIN_SLEW_NS 166 +#define NCP6335D_MAX_SLEW_NS 1333 + +/* bits */ +#define NCP6335D_ENABLE BIT(7) +#define NCP6335D_DVS_PWM_MODE BIT(5) +#define NCP6335D_PWM_MODE1 BIT(6) +#define NCP6335D_PWM_MODE0 BIT(7) +#define NCP6335D_PGOOD_DISCHG BIT(4) +#define NCP6335D_SLEEP_MODE BIT(4) + +#define NCP6335D_VOUT_SEL_MASK 0x7F +#define NCP6335D_SLEW_MASK 0x18 +#define NCP6335D_SLEW_SHIFT 0x3 + +struct ncp6335d_info { + struct regulator_dev *regulator; + struct regulator_init_data *init_data; + struct regmap *regmap; + struct device *dev; + unsigned int vsel_reg; + unsigned int vsel_backup_reg; + unsigned int mode_bit; + int curr_voltage; + int slew_rate; + + unsigned int step_size; + unsigned int min_voltage; + unsigned int min_slew_ns; + unsigned int max_slew_ns; + unsigned int peek_poke_address; + + struct dentry *debug_root; +}; + +static int delay_array[] = {10, 20, 30, 40, 50}; + +static int ncp6335x_read(struct ncp6335d_info *dd, unsigned int reg, + unsigned int *val) +{ + int i = 0, rc = 0; + + rc = regmap_read(dd->regmap, reg, val); + for (i = 0; rc && i < ARRAY_SIZE(delay_array); i++) { + pr_debug("Failed reading reg=%u - retry(%d)\n", reg, i); + msleep(delay_array[i]); + rc = regmap_read(dd->regmap, reg, val); + } + + if (rc) + pr_err("Failed reading reg=%u rc=%d\n", reg, rc); + + return rc; +} + +static int ncp6335x_write(struct ncp6335d_info *dd, unsigned int reg, + unsigned int val) +{ + int i = 0, rc = 0; + + rc = regmap_write(dd->regmap, reg, val); + for (i = 0; rc && i < ARRAY_SIZE(delay_array); i++) { + pr_debug("Failed writing reg=%u - retry(%d)\n", reg, i); + msleep(delay_array[i]); + rc = regmap_write(dd->regmap, reg, val); + } + + if (rc) + pr_err("Failed writing reg=%u rc=%d\n", reg, rc); + + return rc; +} + +static int ncp6335x_update_bits(struct ncp6335d_info *dd, unsigned int reg, + unsigned int mask, unsigned int val) +{ + int i = 0, rc = 0; + + rc = regmap_update_bits(dd->regmap, reg, mask, val); + for (i = 0; rc && i < ARRAY_SIZE(delay_array); i++) { + pr_debug("Failed updating reg=%u- retry(%d)\n", reg, i); + msleep(delay_array[i]); + rc = regmap_update_bits(dd->regmap, reg, mask, val); + } + + if (rc) + pr_err("Failed updating reg=%u rc=%d\n", reg, rc); + + return rc; +} + +static void dump_registers(struct ncp6335d_info *dd, + unsigned int reg, const char *func) +{ + unsigned int val = 0; + + ncp6335x_read(dd, reg, &val); + dev_dbg(dd->dev, "%s: NCP6335D: Reg = %x, Val = %x\n", func, reg, val); +} + +static void ncp633d_slew_delay(struct ncp6335d_info *dd, + int prev_uV, int new_uV) +{ + u8 val; + int delay; + + val = abs(prev_uV - new_uV) / dd->step_size; + delay = ((val * dd->slew_rate) / 1000) + 1; + + dev_dbg(dd->dev, "Slew Delay = %d\n", delay); + + udelay(delay); +} + +static int ncp6335d_is_enabled(struct regulator_dev *rdev) +{ + int rc, val = 0; + struct ncp6335d_info *dd = rdev_get_drvdata(rdev); + + rc = ncp6335x_read(dd, dd->vsel_reg, &val); + if (rc) + dev_err(dd->dev, "Unable to read enable register rc(%d)", rc); + + dump_registers(dd, dd->vsel_reg, __func__); + + return ((val & NCP6335D_ENABLE) ? 1 : 0); +} + +static int ncp6335d_enable(struct regulator_dev *rdev) +{ + int rc; + struct ncp6335d_info *dd = rdev_get_drvdata(rdev); + + rc = ncp6335x_update_bits(dd, dd->vsel_reg, + NCP6335D_ENABLE, NCP6335D_ENABLE); + if (rc) + dev_err(dd->dev, "Unable to enable regualtor rc(%d)", rc); + + dump_registers(dd, dd->vsel_reg, __func__); + + return rc; +} + +static int ncp6335d_disable(struct regulator_dev *rdev) +{ + int rc; + struct ncp6335d_info *dd = rdev_get_drvdata(rdev); + + rc = ncp6335x_update_bits(dd, dd->vsel_reg, + NCP6335D_ENABLE, 0); + if (rc) + dev_err(dd->dev, "Unable to disable regualtor rc(%d)", rc); + + dump_registers(dd, dd->vsel_reg, __func__); + + return rc; +} + +static int ncp6335d_get_voltage(struct regulator_dev *rdev) +{ + unsigned int val; + int rc; + struct ncp6335d_info *dd = rdev_get_drvdata(rdev); + + rc = ncp6335x_read(dd, dd->vsel_reg, &val); + if (rc) { + dev_err(dd->dev, "Unable to get volatge rc(%d)", rc); + return rc; + } + dd->curr_voltage = ((val & NCP6335D_VOUT_SEL_MASK) * dd->step_size) + + dd->min_voltage; + + dump_registers(dd, dd->vsel_reg, __func__); + + return dd->curr_voltage; +} + +static int ncp6335d_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV, unsigned *selector) +{ + int rc, set_val, new_uV; + struct ncp6335d_info *dd = rdev_get_drvdata(rdev); + + set_val = DIV_ROUND_UP(min_uV - dd->min_voltage, dd->step_size); + new_uV = (set_val * dd->step_size) + dd->min_voltage; + if (new_uV > max_uV) { + dev_err(dd->dev, "Unable to set volatge (%d %d)\n", + min_uV, max_uV); + return -EINVAL; + } + + rc = ncp6335x_update_bits(dd, dd->vsel_reg, + NCP6335D_VOUT_SEL_MASK, (set_val & NCP6335D_VOUT_SEL_MASK)); + if (rc) { + dev_err(dd->dev, "Unable to set volatge (%d %d)\n", + min_uV, max_uV); + } else { + ncp633d_slew_delay(dd, dd->curr_voltage, new_uV); + dd->curr_voltage = new_uV; + } + + dump_registers(dd, dd->vsel_reg, __func__); + + return rc; +} + +static int ncp6335d_list_voltage(struct regulator_dev *rdev, + unsigned selector) +{ + struct ncp6335d_info *dd = rdev_get_drvdata(rdev); + + if (selector >= NCP6335D_VOLTAGE_STEPS) + return 0; + + return selector * dd->step_size + dd->min_voltage; +} + +static int ncp6335d_set_mode(struct regulator_dev *rdev, + unsigned int mode) +{ + int rc; + struct ncp6335d_info *dd = rdev_get_drvdata(rdev); + + /* only FAST and NORMAL mode types are supported */ + if (mode != REGULATOR_MODE_FAST && mode != REGULATOR_MODE_NORMAL) { + dev_err(dd->dev, "Mode %d not supported\n", mode); + return -EINVAL; + } + + rc = ncp6335x_update_bits(dd, REG_NCP6335D_COMMAND, dd->mode_bit, + (mode == REGULATOR_MODE_FAST) ? dd->mode_bit : 0); + if (rc) { + dev_err(dd->dev, "Unable to set operating mode rc(%d)", rc); + return rc; + } + + rc = ncp6335x_update_bits(dd, REG_NCP6335D_COMMAND, + NCP6335D_DVS_PWM_MODE, + (mode == REGULATOR_MODE_FAST) ? + NCP6335D_DVS_PWM_MODE : 0); + if (rc) + dev_err(dd->dev, "Unable to set DVS trans. mode rc(%d)", rc); + + dump_registers(dd, REG_NCP6335D_COMMAND, __func__); + + return rc; +} + +static unsigned int ncp6335d_get_mode(struct regulator_dev *rdev) +{ + unsigned int val; + int rc; + struct ncp6335d_info *dd = rdev_get_drvdata(rdev); + + rc = ncp6335x_read(dd, REG_NCP6335D_COMMAND, &val); + if (rc) { + dev_err(dd->dev, "Unable to get regulator mode rc(%d)\n", rc); + return rc; + } + + dump_registers(dd, REG_NCP6335D_COMMAND, __func__); + + if (val & dd->mode_bit) + return REGULATOR_MODE_FAST; + + return REGULATOR_MODE_NORMAL; +} + +static struct regulator_ops ncp6335d_ops = { + .set_voltage = ncp6335d_set_voltage, + .get_voltage = ncp6335d_get_voltage, + .list_voltage = ncp6335d_list_voltage, + .is_enabled = ncp6335d_is_enabled, + .enable = ncp6335d_enable, + .disable = ncp6335d_disable, + .set_mode = ncp6335d_set_mode, + .get_mode = ncp6335d_get_mode, +}; + +static struct regulator_desc rdesc = { + .name = "ncp6335d", + .supply_name = "vin", + .owner = THIS_MODULE, + .n_voltages = NCP6335D_VOLTAGE_STEPS, + .ops = &ncp6335d_ops, +}; + +static int ncp6335d_restore_working_reg(struct device_node *node, + struct ncp6335d_info *dd) +{ + int ret; + unsigned int val; + + /* Restore register from back up register */ + ret = ncp6335x_read(dd, dd->vsel_backup_reg, &val); + if (ret < 0) { + dev_err(dd->dev, "Failed to get backup data from reg %d, ret = %d\n", + dd->vsel_backup_reg, ret); + return ret; + } + + ret = ncp6335x_update_bits(dd, dd->vsel_reg, + NCP6335D_VOUT_SEL_MASK, val); + if (ret < 0) { + dev_err(dd->dev, "Failed to update working reg %d, ret = %d\n", + dd->vsel_reg, ret); + return ret; + } + + return ret; +} + +static int ncp6335d_parse_gpio(struct device_node *node, + struct ncp6335d_info *dd) +{ + int ret = 0, gpio; + enum of_gpio_flags flags; + + if (!of_find_property(node, "onnn,vsel-gpio", NULL)) + return ret; + + /* Get GPIO connected to vsel and set its output */ + gpio = of_get_named_gpio_flags(node, + "onnn,vsel-gpio", 0, &flags); + if (!gpio_is_valid(gpio)) { + if (gpio != -EPROBE_DEFER) + dev_err(dd->dev, "Could not get vsel, ret = %d\n", + gpio); + return gpio; + } + + ret = devm_gpio_request(dd->dev, gpio, "ncp6335d_vsel"); + if (ret) { + dev_err(dd->dev, "Failed to obtain gpio %d ret = %d\n", + gpio, ret); + return ret; + } + + ret = gpio_direction_output(gpio, flags & OF_GPIO_ACTIVE_LOW ? 0 : 1); + if (ret) { + dev_err(dd->dev, "Failed to set GPIO %d to: %s, ret = %d", + gpio, flags & OF_GPIO_ACTIVE_LOW ? + "GPIO_LOW" : "GPIO_HIGH", ret); + return ret; + } + + return ret; +} + +static int ncp6335d_init(struct i2c_client *client, struct ncp6335d_info *dd, + const struct ncp6335d_platform_data *pdata) +{ + int rc; + unsigned int val; + + switch (pdata->default_vsel) { + case NCP6335D_VSEL0: + dd->vsel_reg = REG_NCP6335D_PROGVSEL0; + dd->vsel_backup_reg = REG_NCP6335D_PROGVSEL1; + dd->mode_bit = NCP6335D_PWM_MODE0; + break; + case NCP6335D_VSEL1: + dd->vsel_reg = REG_NCP6335D_PROGVSEL1; + dd->vsel_backup_reg = REG_NCP6335D_PROGVSEL0; + dd->mode_bit = NCP6335D_PWM_MODE1; + break; + default: + dev_err(dd->dev, "Invalid VSEL ID %d\n", pdata->default_vsel); + return -EINVAL; + } + + if (of_property_read_bool(client->dev.of_node, "onnn,restore-reg")) { + rc = ncp6335d_restore_working_reg(client->dev.of_node, dd); + if (rc) + return rc; + } + + rc = ncp6335d_parse_gpio(client->dev.of_node, dd); + if (rc) + return rc; + + /* get the current programmed voltage */ + rc = ncp6335x_read(dd, dd->vsel_reg, &val); + if (rc) { + dev_err(dd->dev, "Unable to get volatge rc(%d)", rc); + return rc; + } + dd->curr_voltage = ((val & NCP6335D_VOUT_SEL_MASK) * + dd->step_size) + dd->min_voltage; + + /* set discharge */ + rc = ncp6335x_update_bits(dd, REG_NCP6335D_PGOOD, + NCP6335D_PGOOD_DISCHG, + (pdata->discharge_enable ? + NCP6335D_PGOOD_DISCHG : 0)); + if (rc) { + dev_err(dd->dev, "Unable to set Active Discharge rc(%d)\n", rc); + return -EINVAL; + } + + /* set slew rate */ + if (pdata->slew_rate_ns < dd->min_slew_ns || + pdata->slew_rate_ns > dd->max_slew_ns) { + dev_err(dd->dev, "Invalid slew rate %d\n", pdata->slew_rate_ns); + return -EINVAL; + } + + dd->slew_rate = pdata->slew_rate_ns; + val = DIV_ROUND_UP(pdata->slew_rate_ns, dd->min_slew_ns); + val = ilog2(val); + + rc = ncp6335x_update_bits(dd, REG_NCP6335D_TIMING, + NCP6335D_SLEW_MASK, val << NCP6335D_SLEW_SHIFT); + if (rc) + dev_err(dd->dev, "Unable to set slew rate rc(%d)\n", rc); + + /* Set Sleep mode bit */ + rc = ncp6335x_update_bits(dd, REG_NCP6335D_COMMAND, + NCP6335D_SLEEP_MODE, pdata->sleep_enable ? + NCP6335D_SLEEP_MODE : 0); + if (rc) + dev_err(dd->dev, "Unable to set sleep mode (%d)\n", rc); + + dump_registers(dd, REG_NCP6335D_COMMAND, __func__); + dump_registers(dd, REG_NCP6335D_PROGVSEL0, __func__); + dump_registers(dd, REG_NCP6335D_TIMING, __func__); + dump_registers(dd, REG_NCP6335D_PGOOD, __func__); + + return rc; +} + +static struct regmap_config ncp6335d_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int ncp6335d_parse_dt(struct i2c_client *client, + struct ncp6335d_info *dd) +{ + int rc; + + rc = of_property_read_u32(client->dev.of_node, + "onnn,step-size", &dd->step_size); + if (rc < 0) { + dev_err(&client->dev, "step size missing: rc = %d.\n", rc); + return rc; + } + + rc = of_property_read_u32(client->dev.of_node, + "onnn,min-slew-ns", &dd->min_slew_ns); + if (rc < 0) { + dev_err(&client->dev, "min slew us missing: rc = %d.\n", rc); + return rc; + } + + rc = of_property_read_u32(client->dev.of_node, + "onnn,max-slew-ns", &dd->max_slew_ns); + if (rc < 0) { + dev_err(&client->dev, "max slew us missing: rc = %d.\n", rc); + return rc; + } + + rc = of_property_read_u32(client->dev.of_node, + "onnn,min-setpoint", &dd->min_voltage); + if (rc < 0) { + dev_err(&client->dev, "min set point missing: rc = %d.\n", rc); + return rc; + } + + return rc; +} + +static struct ncp6335d_platform_data * + ncp6335d_get_of_platform_data(struct i2c_client *client) +{ + struct ncp6335d_platform_data *pdata = NULL; + struct regulator_init_data *init_data; + const char *mode_name; + int rc; + + init_data = of_get_regulator_init_data(&client->dev, + client->dev.of_node, &rdesc); + if (!init_data) { + dev_err(&client->dev, "regulator init data is missing\n"); + return pdata; + } + + pdata = devm_kzalloc(&client->dev, + sizeof(struct ncp6335d_platform_data), GFP_KERNEL); + if (!pdata) + return pdata; + + rc = of_property_read_u32(client->dev.of_node, + "onnn,vsel", &pdata->default_vsel); + if (rc < 0) { + dev_err(&client->dev, "onnn,vsel property missing: rc = %d.\n", + rc); + return NULL; + } + + rc = of_property_read_u32(client->dev.of_node, + "onnn,slew-ns", &pdata->slew_rate_ns); + if (rc < 0) { + dev_err(&client->dev, "onnn,slew-ns property missing: rc = %d.\n", + rc); + return NULL; + } + + pdata->discharge_enable = of_property_read_bool(client->dev.of_node, + "onnn,discharge-enable"); + + pdata->sleep_enable = of_property_read_bool(client->dev.of_node, + "onnn,sleep-enable"); + + pdata->init_data = init_data; + + init_data->constraints.input_uV = init_data->constraints.max_uV; + init_data->constraints.valid_ops_mask = + REGULATOR_CHANGE_VOLTAGE | + REGULATOR_CHANGE_STATUS | + REGULATOR_CHANGE_MODE; + init_data->constraints.valid_modes_mask = + REGULATOR_MODE_NORMAL | + REGULATOR_MODE_FAST; + + rc = of_property_read_string(client->dev.of_node, "onnn,mode", + &mode_name); + if (!rc) { + if (strcmp("pwm", mode_name) == 0) { + init_data->constraints.initial_mode = + REGULATOR_MODE_FAST; + } else if (strcmp("auto", mode_name) == 0) { + init_data->constraints.initial_mode = + REGULATOR_MODE_NORMAL; + } else { + dev_err(&client->dev, "onnn,mode, unknown regulator mode: %s\n", + mode_name); + return NULL; + } + } + + return pdata; +} + +static int get_reg(void *data, u64 *val) +{ + struct ncp6335d_info *dd = data; + int rc; + unsigned int temp = 0; + + rc = ncp6335x_read(dd, dd->peek_poke_address, &temp); + if (rc < 0) + dev_err(dd->dev, "Couldn't read reg %x rc = %d\n", + dd->peek_poke_address, rc); + else + *val = temp; + + return rc; +} + +static int set_reg(void *data, u64 val) +{ + struct ncp6335d_info *dd = data; + int rc; + unsigned int temp = 0; + + temp = (unsigned int) val; + rc = ncp6335x_write(dd, dd->peek_poke_address, temp); + if (rc < 0) + dev_err(dd->dev, "Couldn't write 0x%02x to 0x%02x rc= %d\n", + dd->peek_poke_address, temp, rc); + + return rc; +} +DEFINE_SIMPLE_ATTRIBUTE(poke_poke_debug_ops, get_reg, set_reg, "0x%02llx\n"); + +static int ncp6335d_regulator_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rc; + unsigned int val = 0; + struct ncp6335d_info *dd; + const struct ncp6335d_platform_data *pdata; + struct regulator_config config = { }; + + if (client->dev.of_node) + pdata = ncp6335d_get_of_platform_data(client); + else + pdata = client->dev.platform_data; + + if (!pdata) { + dev_err(&client->dev, "Platform data not specified\n"); + return -EINVAL; + } + + dd = devm_kzalloc(&client->dev, sizeof(*dd), GFP_KERNEL); + if (!dd) + return -ENOMEM; + + if (client->dev.of_node) { + rc = ncp6335d_parse_dt(client, dd); + if (rc) + return rc; + } else { + dd->step_size = NCP6335D_STEP_VOLTAGE_UV; + dd->min_voltage = NCP6335D_MIN_VOLTAGE_UV; + dd->min_slew_ns = NCP6335D_MIN_SLEW_NS; + dd->max_slew_ns = NCP6335D_MAX_SLEW_NS; + } + + dd->regmap = devm_regmap_init_i2c(client, &ncp6335d_regmap_config); + if (IS_ERR(dd->regmap)) { + dev_err(&client->dev, "Error allocating regmap\n"); + return PTR_ERR(dd->regmap); + } + + rc = ncp6335x_read(dd, REG_NCP6335D_PID, &val); + if (rc) { + dev_err(&client->dev, "Unable to identify NCP6335D, rc(%d)\n", + rc); + return rc; + } + dev_info(&client->dev, "Detected Regulator NCP6335D PID = %d\n", val); + + dd->init_data = pdata->init_data; + dd->dev = &client->dev; + i2c_set_clientdata(client, dd); + + rc = ncp6335d_init(client, dd, pdata); + if (rc) { + dev_err(&client->dev, "Unable to initialize the regulator\n"); + return -EINVAL; + } + + config.dev = &client->dev; + config.init_data = dd->init_data; + config.regmap = dd->regmap; + config.driver_data = dd; + config.of_node = client->dev.of_node; + + dd->regulator = regulator_register(&rdesc, &config); + + if (IS_ERR(dd->regulator)) { + dev_err(&client->dev, "Unable to register regulator rc(%ld)", + PTR_ERR(dd->regulator)); + + return PTR_ERR(dd->regulator); + } + + dd->debug_root = debugfs_create_dir("ncp6335x", NULL); + if (!dd->debug_root) + dev_err(&client->dev, "Couldn't create debug dir\n"); + + if (dd->debug_root) { + struct dentry *ent; + + ent = debugfs_create_x32("address", S_IFREG | S_IWUSR | S_IRUGO, + dd->debug_root, + &(dd->peek_poke_address)); + if (!ent) + dev_err(&client->dev, "Couldn't create address debug file rc = %d\n", + rc); + + ent = debugfs_create_file("data", S_IFREG | S_IWUSR | S_IRUGO, + dd->debug_root, dd, + &poke_poke_debug_ops); + if (!ent) + dev_err(&client->dev, "Couldn't create data debug file rc = %d\n", + rc); + } + + return 0; +} + +static int ncp6335d_regulator_remove(struct i2c_client *client) +{ + struct ncp6335d_info *dd = i2c_get_clientdata(client); + + regulator_unregister(dd->regulator); + + debugfs_remove_recursive(dd->debug_root); + + return 0; +} + +static const struct of_device_id ncp6335d_match_table[] = { + { .compatible = "onnn,ncp6335d-regulator", }, + {}, +}; +MODULE_DEVICE_TABLE(of, ncp6335d_match_table); + +static const struct i2c_device_id ncp6335d_id[] = { + {"ncp6335d", -1}, + { }, +}; + +static struct i2c_driver ncp6335d_regulator_driver = { + .driver = { + .name = "ncp6335d-regulator", + .owner = THIS_MODULE, + .of_match_table = ncp6335d_match_table, + }, + .probe = ncp6335d_regulator_probe, + .remove = ncp6335d_regulator_remove, + .id_table = ncp6335d_id, +}; + +/** + * ncp6335d_regulator_init() - initialized ncp6335d regulator driver + * This function registers the ncp6335d regulator platform driver. + * + * Returns 0 on success or errno on failure. + */ +int __init ncp6335d_regulator_init(void) +{ + static bool initialized; + + if (initialized) + return 0; + + initialized = true; + + return i2c_add_driver(&ncp6335d_regulator_driver); +} +EXPORT_SYMBOL(ncp6335d_regulator_init); +subsys_initcall(ncp6335d_regulator_init); + +static void __exit ncp6335d_regulator_exit(void) +{ + i2c_del_driver(&ncp6335d_regulator_driver); +} +module_exit(ncp6335d_regulator_exit); +MODULE_DESCRIPTION("OnSemi-NCP6335D regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-s35390a.c b/drivers/rtc/rtc-s35390a.c index f40afdd0e5f5..00662dd28d66 100644 --- a/drivers/rtc/rtc-s35390a.c +++ b/drivers/rtc/rtc-s35390a.c @@ -15,6 +15,7 @@ #include <linux/bitrev.h> #include <linux/bcd.h> #include <linux/slab.h> +#include <linux/delay.h> #define S35390A_CMD_STATUS1 0 #define S35390A_CMD_STATUS2 1 @@ -34,10 +35,14 @@ #define S35390A_ALRM_BYTE_HOURS 1 #define S35390A_ALRM_BYTE_MINS 2 +/* flags for STATUS1 */ #define S35390A_FLAG_POC 0x01 #define S35390A_FLAG_BLD 0x02 +#define S35390A_FLAG_INT2 0x04 #define S35390A_FLAG_24H 0x40 #define S35390A_FLAG_RESET 0x80 + +/* flag for STATUS2 */ #define S35390A_FLAG_TEST 0x01 #define S35390A_INT2_MODE_MASK 0xF0 @@ -94,19 +99,63 @@ static int s35390a_get_reg(struct s35390a *s35390a, int reg, char *buf, int len) return 0; } -static int s35390a_reset(struct s35390a *s35390a) +/* + * Returns <0 on error, 0 if rtc is setup fine and 1 if the chip was reset. + * To keep the information if an irq is pending, pass the value read from + * STATUS1 to the caller. + */ +static int s35390a_reset(struct s35390a *s35390a, char *status1) { - char buf[1]; - - if (s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, buf, sizeof(buf)) < 0) - return -EIO; - - if (!(buf[0] & (S35390A_FLAG_POC | S35390A_FLAG_BLD))) + char buf; + int ret; + unsigned initcount = 0; + + ret = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, status1, 1); + if (ret < 0) + return ret; + + if (*status1 & S35390A_FLAG_POC) + /* + * Do not communicate for 0.5 seconds since the power-on + * detection circuit is in operation. + */ + msleep(500); + else if (!(*status1 & S35390A_FLAG_BLD)) + /* + * If both POC and BLD are unset everything is fine. + */ return 0; - buf[0] |= (S35390A_FLAG_RESET | S35390A_FLAG_24H); - buf[0] &= 0xf0; - return s35390a_set_reg(s35390a, S35390A_CMD_STATUS1, buf, sizeof(buf)); + /* + * At least one of POC and BLD are set, so reinitialise chip. Keeping + * this information in the hardware to know later that the time isn't + * valid is unfortunately not possible because POC and BLD are cleared + * on read. So the reset is best done now. + * + * The 24H bit is kept over reset, so set it already here. + */ +initialize: + *status1 = S35390A_FLAG_24H; + buf = S35390A_FLAG_RESET | S35390A_FLAG_24H; + ret = s35390a_set_reg(s35390a, S35390A_CMD_STATUS1, &buf, 1); + + if (ret < 0) + return ret; + + ret = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, &buf, 1); + if (ret < 0) + return ret; + + if (buf & (S35390A_FLAG_POC | S35390A_FLAG_BLD)) { + /* Try up to five times to reset the chip */ + if (initcount < 5) { + ++initcount; + goto initialize; + } else + return -EIO; + } + + return 1; } static int s35390a_disable_test_mode(struct s35390a *s35390a) @@ -242,6 +291,8 @@ static int s35390a_set_alarm(struct i2c_client *client, struct rtc_wkalrm *alm) if (alm->time.tm_wday != -1) buf[S35390A_ALRM_BYTE_WDAY] = bin2bcd(alm->time.tm_wday) | 0x80; + else + buf[S35390A_ALRM_BYTE_WDAY] = 0; buf[S35390A_ALRM_BYTE_HOURS] = s35390a_hr2reg(s35390a, alm->time.tm_hour) | 0x80; @@ -265,27 +316,61 @@ static int s35390a_read_alarm(struct i2c_client *client, struct rtc_wkalrm *alm) char buf[3], sts; int i, err; + /* + * initialize all members to -1 to signal the core that they are not + * defined by the hardware. + */ + alm->time.tm_sec = -1; + alm->time.tm_min = -1; + alm->time.tm_hour = -1; + alm->time.tm_mday = -1; + alm->time.tm_mon = -1; + alm->time.tm_year = -1; + alm->time.tm_wday = -1; + alm->time.tm_yday = -1; + alm->time.tm_isdst = -1; + err = s35390a_get_reg(s35390a, S35390A_CMD_STATUS2, &sts, sizeof(sts)); if (err < 0) return err; - if (bitrev8(sts) != S35390A_INT2_MODE_ALARM) - return -EINVAL; + if ((bitrev8(sts) & S35390A_INT2_MODE_MASK) != S35390A_INT2_MODE_ALARM) { + /* + * When the alarm isn't enabled, the register to configure + * the alarm time isn't accessible. + */ + alm->enabled = 0; + return 0; + } else { + alm->enabled = 1; + } err = s35390a_get_reg(s35390a, S35390A_CMD_INT2_REG1, buf, sizeof(buf)); if (err < 0) return err; /* This chip returns the bits of each byte in reverse order */ - for (i = 0; i < 3; ++i) { + for (i = 0; i < 3; ++i) buf[i] = bitrev8(buf[i]); - buf[i] &= ~0x80; - } - alm->time.tm_wday = bcd2bin(buf[S35390A_ALRM_BYTE_WDAY]); - alm->time.tm_hour = s35390a_reg2hr(s35390a, - buf[S35390A_ALRM_BYTE_HOURS]); - alm->time.tm_min = bcd2bin(buf[S35390A_ALRM_BYTE_MINS]); + /* + * B0 of the three matching registers is an enable flag. Iff it is set + * the configured value is used for matching. + */ + if (buf[S35390A_ALRM_BYTE_WDAY] & 0x80) + alm->time.tm_wday = + bcd2bin(buf[S35390A_ALRM_BYTE_WDAY] & ~0x80); + + if (buf[S35390A_ALRM_BYTE_HOURS] & 0x80) + alm->time.tm_hour = + s35390a_reg2hr(s35390a, + buf[S35390A_ALRM_BYTE_HOURS] & ~0x80); + + if (buf[S35390A_ALRM_BYTE_MINS] & 0x80) + alm->time.tm_min = bcd2bin(buf[S35390A_ALRM_BYTE_MINS] & ~0x80); + + /* alarm triggers always at s=0 */ + alm->time.tm_sec = 0; dev_dbg(&client->dev, "%s: alm is mins=%d, hours=%d, wday=%d\n", __func__, alm->time.tm_min, alm->time.tm_hour, @@ -327,11 +412,11 @@ static struct i2c_driver s35390a_driver; static int s35390a_probe(struct i2c_client *client, const struct i2c_device_id *id) { - int err; + int err, err_reset; unsigned int i; struct s35390a *s35390a; struct rtc_time tm; - char buf[1]; + char buf, status1; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { err = -ENODEV; @@ -360,29 +445,35 @@ static int s35390a_probe(struct i2c_client *client, } } - err = s35390a_reset(s35390a); - if (err < 0) { + err_reset = s35390a_reset(s35390a, &status1); + if (err_reset < 0) { + err = err_reset; dev_err(&client->dev, "error resetting chip\n"); goto exit_dummy; } - err = s35390a_disable_test_mode(s35390a); - if (err < 0) { - dev_err(&client->dev, "error disabling test mode\n"); - goto exit_dummy; - } - - err = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, buf, sizeof(buf)); - if (err < 0) { - dev_err(&client->dev, "error checking 12/24 hour mode\n"); - goto exit_dummy; - } - if (buf[0] & S35390A_FLAG_24H) + if (status1 & S35390A_FLAG_24H) s35390a->twentyfourhour = 1; else s35390a->twentyfourhour = 0; - if (s35390a_get_datetime(client, &tm) < 0) + if (status1 & S35390A_FLAG_INT2) { + /* disable alarm (and maybe test mode) */ + buf = 0; + err = s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, &buf, 1); + if (err < 0) { + dev_err(&client->dev, "error disabling alarm"); + goto exit_dummy; + } + } else { + err = s35390a_disable_test_mode(s35390a); + if (err < 0) { + dev_err(&client->dev, "error disabling test mode\n"); + goto exit_dummy; + } + } + + if (err_reset > 0 || s35390a_get_datetime(client, &tm) < 0) dev_warn(&client->dev, "clock needs to be set\n"); device_set_wakeup_capable(&client->dev, 1); @@ -395,6 +486,10 @@ static int s35390a_probe(struct i2c_client *client, err = PTR_ERR(s35390a->rtc); goto exit_dummy; } + + if (status1 & S35390A_FLAG_INT2) + rtc_update_irq(s35390a->rtc, 1, RTC_AF); + return 0; exit_dummy: diff --git a/drivers/rtc/rtc-tegra.c b/drivers/rtc/rtc-tegra.c index 60232bd366ef..71216aa68905 100644 --- a/drivers/rtc/rtc-tegra.c +++ b/drivers/rtc/rtc-tegra.c @@ -18,6 +18,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include <linux/kernel.h> +#include <linux/clk.h> #include <linux/init.h> #include <linux/module.h> #include <linux/slab.h> @@ -59,6 +60,7 @@ struct tegra_rtc_info { struct platform_device *pdev; struct rtc_device *rtc_dev; void __iomem *rtc_base; /* NULL if not initialized. */ + struct clk *clk; int tegra_rtc_irq; /* alarm and periodic irq */ spinlock_t tegra_rtc_lock; }; @@ -332,6 +334,14 @@ static int __init tegra_rtc_probe(struct platform_device *pdev) if (info->tegra_rtc_irq <= 0) return -EBUSY; + info->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(info->clk)) + return PTR_ERR(info->clk); + + ret = clk_prepare_enable(info->clk); + if (ret < 0) + return ret; + /* set context info. */ info->pdev = pdev; spin_lock_init(&info->tegra_rtc_lock); @@ -352,7 +362,7 @@ static int __init tegra_rtc_probe(struct platform_device *pdev) ret = PTR_ERR(info->rtc_dev); dev_err(&pdev->dev, "Unable to register device (err=%d).\n", ret); - return ret; + goto disable_clk; } ret = devm_request_irq(&pdev->dev, info->tegra_rtc_irq, @@ -362,12 +372,25 @@ static int __init tegra_rtc_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Unable to request interrupt for device (err=%d).\n", ret); - return ret; + goto disable_clk; } dev_notice(&pdev->dev, "Tegra internal Real Time Clock\n"); return 0; + +disable_clk: + clk_disable_unprepare(info->clk); + return ret; +} + +static int tegra_rtc_remove(struct platform_device *pdev) +{ + struct tegra_rtc_info *info = platform_get_drvdata(pdev); + + clk_disable_unprepare(info->clk); + + return 0; } #ifdef CONFIG_PM_SLEEP @@ -419,6 +442,7 @@ static void tegra_rtc_shutdown(struct platform_device *pdev) MODULE_ALIAS("platform:tegra_rtc"); static struct platform_driver tegra_rtc_driver = { + .remove = tegra_rtc_remove, .shutdown = tegra_rtc_shutdown, .driver = { .name = "tegra_rtc", diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 24ec282e15d8..7c3b8d3516e3 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -1651,6 +1651,9 @@ static void ap_scan_bus(struct work_struct *unused) ap_dev->queue_depth = queue_depth; ap_dev->raw_hwtype = device_type; ap_dev->device_type = device_type; + /* CEX6 toleration: map to CEX5 */ + if (device_type == AP_DEVICE_TYPE_CEX6) + ap_dev->device_type = AP_DEVICE_TYPE_CEX5; ap_dev->functions = device_functions; spin_lock_init(&ap_dev->lock); INIT_LIST_HEAD(&ap_dev->pendingq); diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h index 6adcbdf225d1..cc741e948170 100644 --- a/drivers/s390/crypto/ap_bus.h +++ b/drivers/s390/crypto/ap_bus.h @@ -105,6 +105,7 @@ static inline int ap_test_bit(unsigned int *ptr, unsigned int nr) #define AP_DEVICE_TYPE_CEX3C 9 #define AP_DEVICE_TYPE_CEX4 10 #define AP_DEVICE_TYPE_CEX5 11 +#define AP_DEVICE_TYPE_CEX6 12 /* * Known function facilities diff --git a/drivers/scsi/cxlflash/common.h b/drivers/scsi/cxlflash/common.h index 5ada9268a450..a8ac4c0a1493 100644 --- a/drivers/scsi/cxlflash/common.h +++ b/drivers/scsi/cxlflash/common.h @@ -34,7 +34,6 @@ extern const struct file_operations cxlflash_cxl_fops; sectors */ -#define NUM_RRQ_ENTRY 16 /* for master issued cmds */ #define MAX_RHT_PER_CONTEXT (PAGE_SIZE / sizeof(struct sisl_rht_entry)) /* AFU command retry limit */ @@ -48,9 +47,12 @@ extern const struct file_operations cxlflash_cxl_fops; index derivation */ -#define CXLFLASH_MAX_CMDS 16 +#define CXLFLASH_MAX_CMDS 256 #define CXLFLASH_MAX_CMDS_PER_LUN CXLFLASH_MAX_CMDS +/* RRQ for master issued cmds */ +#define NUM_RRQ_ENTRY CXLFLASH_MAX_CMDS + static inline void check_sizes(void) { @@ -149,7 +151,7 @@ struct afu_cmd { struct afu { /* Stuff requiring alignment go first. */ - u64 rrq_entry[NUM_RRQ_ENTRY]; /* 128B RRQ */ + u64 rrq_entry[NUM_RRQ_ENTRY]; /* 2K RRQ */ /* * Command & data for AFU commands. */ diff --git a/drivers/scsi/cxlflash/main.c b/drivers/scsi/cxlflash/main.c index c86847c68448..2882bcac918a 100644 --- a/drivers/scsi/cxlflash/main.c +++ b/drivers/scsi/cxlflash/main.c @@ -2305,7 +2305,7 @@ static struct scsi_host_template driver_template = { .eh_device_reset_handler = cxlflash_eh_device_reset_handler, .eh_host_reset_handler = cxlflash_eh_host_reset_handler, .change_queue_depth = cxlflash_change_queue_depth, - .cmd_per_lun = 16, + .cmd_per_lun = CXLFLASH_MAX_CMDS_PER_LUN, .can_queue = CXLFLASH_MAX_CMDS, .this_id = -1, .sg_tablesize = SG_NONE, /* No scatter gather support */ diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 6bffd91b973a..c1ccf1ee99ea 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -560,8 +560,12 @@ static void iscsi_complete_task(struct iscsi_task *task, int state) WARN_ON_ONCE(task->state == ISCSI_TASK_FREE); task->state = state; - if (!list_empty(&task->running)) + spin_lock_bh(&conn->taskqueuelock); + if (!list_empty(&task->running)) { + pr_debug_once("%s while task on list", __func__); list_del_init(&task->running); + } + spin_unlock_bh(&conn->taskqueuelock); if (conn->task == task) conn->task = NULL; @@ -783,7 +787,9 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, if (session->tt->xmit_task(task)) goto free_task; } else { + spin_lock_bh(&conn->taskqueuelock); list_add_tail(&task->running, &conn->mgmtqueue); + spin_unlock_bh(&conn->taskqueuelock); iscsi_conn_queue_work(conn); } @@ -1474,8 +1480,10 @@ void iscsi_requeue_task(struct iscsi_task *task) * this may be on the requeue list already if the xmit_task callout * is handling the r2ts while we are adding new ones */ + spin_lock_bh(&conn->taskqueuelock); if (list_empty(&task->running)) list_add_tail(&task->running, &conn->requeue); + spin_unlock_bh(&conn->taskqueuelock); iscsi_conn_queue_work(conn); } EXPORT_SYMBOL_GPL(iscsi_requeue_task); @@ -1512,22 +1520,26 @@ static int iscsi_data_xmit(struct iscsi_conn *conn) * only have one nop-out as a ping from us and targets should not * overflow us with nop-ins */ + spin_lock_bh(&conn->taskqueuelock); check_mgmt: while (!list_empty(&conn->mgmtqueue)) { conn->task = list_entry(conn->mgmtqueue.next, struct iscsi_task, running); list_del_init(&conn->task->running); + spin_unlock_bh(&conn->taskqueuelock); if (iscsi_prep_mgmt_task(conn, conn->task)) { /* regular RX path uses back_lock */ spin_lock_bh(&conn->session->back_lock); __iscsi_put_task(conn->task); spin_unlock_bh(&conn->session->back_lock); conn->task = NULL; + spin_lock_bh(&conn->taskqueuelock); continue; } rc = iscsi_xmit_task(conn); if (rc) goto done; + spin_lock_bh(&conn->taskqueuelock); } /* process pending command queue */ @@ -1535,19 +1547,24 @@ check_mgmt: conn->task = list_entry(conn->cmdqueue.next, struct iscsi_task, running); list_del_init(&conn->task->running); + spin_unlock_bh(&conn->taskqueuelock); if (conn->session->state == ISCSI_STATE_LOGGING_OUT) { fail_scsi_task(conn->task, DID_IMM_RETRY); + spin_lock_bh(&conn->taskqueuelock); continue; } rc = iscsi_prep_scsi_cmd_pdu(conn->task); if (rc) { if (rc == -ENOMEM || rc == -EACCES) { + spin_lock_bh(&conn->taskqueuelock); list_add_tail(&conn->task->running, &conn->cmdqueue); conn->task = NULL; + spin_unlock_bh(&conn->taskqueuelock); goto done; } else fail_scsi_task(conn->task, DID_ABORT); + spin_lock_bh(&conn->taskqueuelock); continue; } rc = iscsi_xmit_task(conn); @@ -1558,6 +1575,7 @@ check_mgmt: * we need to check the mgmt queue for nops that need to * be sent to aviod starvation */ + spin_lock_bh(&conn->taskqueuelock); if (!list_empty(&conn->mgmtqueue)) goto check_mgmt; } @@ -1577,12 +1595,15 @@ check_mgmt: conn->task = task; list_del_init(&conn->task->running); conn->task->state = ISCSI_TASK_RUNNING; + spin_unlock_bh(&conn->taskqueuelock); rc = iscsi_xmit_task(conn); if (rc) goto done; + spin_lock_bh(&conn->taskqueuelock); if (!list_empty(&conn->mgmtqueue)) goto check_mgmt; } + spin_unlock_bh(&conn->taskqueuelock); spin_unlock_bh(&conn->session->frwd_lock); return -ENODATA; @@ -1738,7 +1759,9 @@ int iscsi_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc) goto prepd_reject; } } else { + spin_lock_bh(&conn->taskqueuelock); list_add_tail(&task->running, &conn->cmdqueue); + spin_unlock_bh(&conn->taskqueuelock); iscsi_conn_queue_work(conn); } @@ -2900,6 +2923,7 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, int dd_size, INIT_LIST_HEAD(&conn->mgmtqueue); INIT_LIST_HEAD(&conn->cmdqueue); INIT_LIST_HEAD(&conn->requeue); + spin_lock_init(&conn->taskqueuelock); INIT_WORK(&conn->xmitwork, iscsi_xmitworker); /* allocate login_task used for the login/text sequences */ diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 9c706d8c1441..6f5e2720ffad 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -218,7 +218,7 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) task->num_scatter = qc->n_elem; } else { for_each_sg(qc->sg, sg, qc->n_elem, si) - xfer += sg->length; + xfer += sg_dma_len(sg); task->total_xfer_len = xfer; task->num_scatter = si; diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index c14ab6c3ae40..60c21093f865 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -11387,6 +11387,7 @@ static struct pci_driver lpfc_driver = { .id_table = lpfc_id_table, .probe = lpfc_pci_probe_one, .remove = lpfc_pci_remove_one, + .shutdown = lpfc_pci_remove_one, .suspend = lpfc_pci_suspend_one, .resume = lpfc_pci_resume_one, .err_handler = &lpfc_err_handler, diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.h b/drivers/scsi/mpt3sas/mpt3sas_base.h index 92648a5ea2d2..63f5965acc89 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.h +++ b/drivers/scsi/mpt3sas/mpt3sas_base.h @@ -390,6 +390,7 @@ struct MPT3SAS_TARGET { * @eedp_enable: eedp support enable bit * @eedp_type: 0(type_1), 1(type_2), 2(type_3) * @eedp_block_length: block size + * @ata_command_pending: SATL passthrough outstanding for device */ struct MPT3SAS_DEVICE { struct MPT3SAS_TARGET *sas_target; @@ -398,6 +399,17 @@ struct MPT3SAS_DEVICE { u8 configured_lun; u8 block; u8 tlr_snoop_check; + /* + * Bug workaround for SATL handling: the mpt2/3sas firmware + * doesn't return BUSY or TASK_SET_FULL for subsequent + * commands while a SATL pass through is in operation as the + * spec requires, it simply does nothing with them until the + * pass through completes, causing them possibly to timeout if + * the passthrough is a long executing command (like format or + * secure erase). This variable allows us to do the right + * thing while a SATL command is pending. + */ + unsigned long ata_command_pending; }; #define MPT3_CMD_NOT_USED 0x8000 /* free */ diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index f6a8e9958e75..8a5fbdb45cfd 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -3707,9 +3707,18 @@ _scsih_temp_threshold_events(struct MPT3SAS_ADAPTER *ioc, } } -static inline bool ata_12_16_cmd(struct scsi_cmnd *scmd) +static int _scsih_set_satl_pending(struct scsi_cmnd *scmd, bool pending) { - return (scmd->cmnd[0] == ATA_12 || scmd->cmnd[0] == ATA_16); + struct MPT3SAS_DEVICE *priv = scmd->device->hostdata; + + if (scmd->cmnd[0] != ATA_12 && scmd->cmnd[0] != ATA_16) + return 0; + + if (pending) + return test_and_set_bit(0, &priv->ata_command_pending); + + clear_bit(0, &priv->ata_command_pending); + return 0; } /** @@ -3733,9 +3742,7 @@ _scsih_flush_running_cmds(struct MPT3SAS_ADAPTER *ioc) if (!scmd) continue; count++; - if (ata_12_16_cmd(scmd)) - scsi_internal_device_unblock(scmd->device, - SDEV_RUNNING); + _scsih_set_satl_pending(scmd, false); mpt3sas_base_free_smid(ioc, smid); scsi_dma_unmap(scmd); if (ioc->pci_error_recovery) @@ -3866,13 +3873,6 @@ scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) if (ioc->logging_level & MPT_DEBUG_SCSI) scsi_print_command(scmd); - /* - * Lock the device for any subsequent command until command is - * done. - */ - if (ata_12_16_cmd(scmd)) - scsi_internal_device_block(scmd->device); - sas_device_priv_data = scmd->device->hostdata; if (!sas_device_priv_data || !sas_device_priv_data->sas_target) { scmd->result = DID_NO_CONNECT << 16; @@ -3886,6 +3886,19 @@ scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) return 0; } + /* + * Bug work around for firmware SATL handling. The loop + * is based on atomic operations and ensures consistency + * since we're lockless at this point + */ + do { + if (test_bit(0, &sas_device_priv_data->ata_command_pending)) { + scmd->result = SAM_STAT_BUSY; + scmd->scsi_done(scmd); + return 0; + } + } while (_scsih_set_satl_pending(scmd, true)); + sas_target_priv_data = sas_device_priv_data->sas_target; /* invalid device handle */ @@ -4445,8 +4458,7 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) if (scmd == NULL) return 1; - if (ata_12_16_cmd(scmd)) - scsi_internal_device_unblock(scmd->device, SDEV_RUNNING); + _scsih_set_satl_pending(scmd, false); mpi_request = mpt3sas_base_get_msg_frame(ioc, smid); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 5d81bcc1dc75..9b3af788376c 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1995,6 +1995,22 @@ static void read_capacity_error(struct scsi_disk *sdkp, struct scsi_device *sdp, #define READ_CAPACITY_RETRIES_ON_RESET 10 +/* + * Ensure that we don't overflow sector_t when CONFIG_LBDAF is not set + * and the reported logical block size is bigger than 512 bytes. Note + * that last_sector is a u64 and therefore logical_to_sectors() is not + * applicable. + */ +static bool sd_addressable_capacity(u64 lba, unsigned int sector_size) +{ + u64 last_sector = (lba + 1ULL) << (ilog2(sector_size) - 9); + + if (sizeof(sector_t) == 4 && last_sector > U32_MAX) + return false; + + return true; +} + static int read_capacity_16(struct scsi_disk *sdkp, struct scsi_device *sdp, unsigned char *buffer) { @@ -2060,7 +2076,7 @@ static int read_capacity_16(struct scsi_disk *sdkp, struct scsi_device *sdp, return -ENODEV; } - if ((sizeof(sdkp->capacity) == 4) && (lba >= 0xffffffffULL)) { + if (!sd_addressable_capacity(lba, sector_size)) { sd_printk(KERN_ERR, sdkp, "Too big for this kernel. Use a " "kernel compiled with support for large block " "devices.\n"); @@ -2146,7 +2162,7 @@ static int read_capacity_10(struct scsi_disk *sdkp, struct scsi_device *sdp, return sector_size; } - if ((sizeof(sdkp->capacity) == 4) && (lba == 0xffffffff)) { + if (!sd_addressable_capacity(lba, sector_size)) { sd_printk(KERN_ERR, sdkp, "Too big for this kernel. Use a " "kernel compiled with support for large block " "devices.\n"); @@ -2810,7 +2826,8 @@ static int sd_revalidate_disk(struct gendisk *disk) rw_max = q->limits.io_opt = sdkp->opt_xfer_blocks * sdp->sector_size; else - rw_max = BLK_DEF_MAX_SECTORS; + rw_max = min_not_zero(logical_to_sectors(sdp, dev_max), + (sector_t)BLK_DEF_MAX_SECTORS); /* Combine with controller limits */ q->limits.max_sectors = min(rw_max, queue_max_hw_sectors(q)); diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 93a523b42d3d..29d8c74e85e3 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -1008,6 +1008,8 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) result = get_user(val, ip); if (result) return result; + if (val > SG_MAX_CDB_SIZE) + return -ENOMEM; sfp->next_cmd_len = (val > 0) ? val : 0; return 0; case SG_GET_VERSION_NUM: diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index 64c867405ad4..804586aeaffe 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -834,6 +834,7 @@ static void get_capabilities(struct scsi_cd *cd) unsigned char *buffer; struct scsi_mode_data data; struct scsi_sense_hdr sshdr; + unsigned int ms_len = 128; int rc, n; static const char *loadmech[] = @@ -860,10 +861,11 @@ static void get_capabilities(struct scsi_cd *cd) scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES, &sshdr); /* ask for mode page 0x2a */ - rc = scsi_mode_sense(cd->device, 0, 0x2a, buffer, 128, + rc = scsi_mode_sense(cd->device, 0, 0x2a, buffer, ms_len, SR_TIMEOUT, 3, &data, NULL); - if (!scsi_status_is_good(rc)) { + if (!scsi_status_is_good(rc) || data.length > ms_len || + data.header_length + data.block_descriptor_length > data.length) { /* failed, drive doesn't have capabilities mode page */ cd->cdi.speed = 1; cd->cdi.mask |= (CDC_CD_R | CDC_CD_RW | CDC_DVD_R | diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig index b3ed361eff26..fb2f2159c0e1 100644 --- a/drivers/scsi/ufs/Kconfig +++ b/drivers/scsi/ufs/Kconfig @@ -106,3 +106,15 @@ config SCSI_UFS_TEST The UFS unit-tests register as a block device test utility to the test-iosched and will be initiated when the test-iosched will be chosen to be the active I/O scheduler. + +config SCSI_UFSHCD_CMD_LOGGING + bool "Universal Flash Storage host controller driver layer command logging support" + depends on SCSI_UFSHCD + help + This selects the UFS host controller driver layer command logging. + UFS host controller driver layer command logging records all the + command information sent from UFS host controller for debugging + purpose. + + Select this if you want above mentioned debug information captured. + If unsure, say N. diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index c5393d517432..9e64e0c34f57 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -516,14 +516,145 @@ static inline void ufshcd_remove_non_printable(char *val) *val = ' '; } +#define UFSHCD_MAX_CMD_LOGGING 100 + #ifdef CONFIG_TRACEPOINTS -static void ufshcd_add_command_trace(struct ufs_hba *hba, - unsigned int tag, const char *str) +static inline void ufshcd_add_command_trace(struct ufs_hba *hba, + struct ufshcd_cmd_log_entry *entry, u8 opcode) +{ + if (trace_ufshcd_command_enabled()) { + u32 intr = ufshcd_readl(hba, REG_INTERRUPT_STATUS); + + trace_ufshcd_command(dev_name(hba->dev), entry->str, entry->tag, + entry->doorbell, entry->transfer_len, intr, + entry->lba, opcode); + } +} +#else +static inline void ufshcd_add_command_trace(struct ufs_hba *hba, + struct ufshcd_cmd_log_entry *entry, u8 opcode) +{ +} +#endif + +#ifdef CONFIG_SCSI_UFSHCD_CMD_LOGGING +static void ufshcd_cmd_log_init(struct ufs_hba *hba) +{ + /* Allocate log entries */ + if (!hba->cmd_log.entries) { + hba->cmd_log.entries = kzalloc(UFSHCD_MAX_CMD_LOGGING * + sizeof(struct ufshcd_cmd_log_entry), GFP_KERNEL); + if (!hba->cmd_log.entries) + return; + dev_dbg(hba->dev, "%s: cmd_log.entries initialized\n", + __func__); + } +} + +static void __ufshcd_cmd_log(struct ufs_hba *hba, char *str, char *cmd_type, + unsigned int tag, u8 cmd_id, u8 idn, u8 lun, + sector_t lba, int transfer_len, u8 opcode) +{ + struct ufshcd_cmd_log_entry *entry; + + if (!hba->cmd_log.entries) + return; + + entry = &hba->cmd_log.entries[hba->cmd_log.pos]; + entry->lun = lun; + entry->str = str; + entry->cmd_type = cmd_type; + entry->cmd_id = cmd_id; + entry->lba = lba; + entry->transfer_len = transfer_len; + entry->idn = idn; + entry->doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); + entry->tag = tag; + entry->tstamp = ktime_get(); + entry->outstanding_reqs = hba->outstanding_reqs; + entry->seq_num = hba->cmd_log.seq_num; + hba->cmd_log.seq_num++; + hba->cmd_log.pos = + (hba->cmd_log.pos + 1) % UFSHCD_MAX_CMD_LOGGING; + + ufshcd_add_command_trace(hba, entry, opcode); +} + +static void ufshcd_cmd_log(struct ufs_hba *hba, char *str, char *cmd_type, + unsigned int tag, u8 cmd_id, u8 idn) +{ + __ufshcd_cmd_log(hba, str, cmd_type, tag, cmd_id, idn, + 0xff, (sector_t)-1, -1, -1); +} + +static void ufshcd_dme_cmd_log(struct ufs_hba *hba, char *str, u8 cmd_id) +{ + ufshcd_cmd_log(hba, str, "dme", 0xff, cmd_id, 0xff); +} + +static void ufshcd_cmd_log_print(struct ufs_hba *hba) +{ + int i; + int pos; + struct ufshcd_cmd_log_entry *p; + + if (!hba->cmd_log.entries) + return; + + pos = hba->cmd_log.pos; + for (i = 0; i < UFSHCD_MAX_CMD_LOGGING; i++) { + p = &hba->cmd_log.entries[pos]; + pos = (pos + 1) % UFSHCD_MAX_CMD_LOGGING; + + if (ktime_to_us(p->tstamp)) { + pr_err("%s: %s: seq_no=%u lun=0x%x cmd_id=0x%02x lba=0x%llx txfer_len=%d tag=%u, doorbell=0x%x outstanding=0x%x idn=%d time=%lld us\n", + p->cmd_type, p->str, p->seq_num, + p->lun, p->cmd_id, (unsigned long long)p->lba, + p->transfer_len, p->tag, p->doorbell, + p->outstanding_reqs, p->idn, + ktime_to_us(p->tstamp)); + usleep_range(1000, 1100); + } + } +} +#else +static void ufshcd_cmd_log_init(struct ufs_hba *hba) +{ +} + +static void __ufshcd_cmd_log(struct ufs_hba *hba, char *str, char *cmd_type, + unsigned int tag, u8 cmd_id, u8 idn, u8 lun, + sector_t lba, int transfer_len, u8 opcode) +{ + struct ufshcd_cmd_log_entry entry; + + entry.str = str; + entry.lba = lba; + entry.transfer_len = transfer_len; + entry.doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); + entry.tag = tag; + + ufshcd_add_command_trace(hba, &entry, opcode); +} + +static void ufshcd_dme_cmd_log(struct ufs_hba *hba, char *str, u8 cmd_id) +{ +} + +static void ufshcd_cmd_log_print(struct ufs_hba *hba) +{ +} +#endif + +#ifdef CONFIG_TRACEPOINTS +static inline void ufshcd_cond_add_cmd_trace(struct ufs_hba *hba, + unsigned int tag, const char *str) { - sector_t lba = -1; - u8 opcode = 0; - u32 intr, doorbell; struct ufshcd_lrb *lrbp; + char *cmd_type; + u8 opcode = 0; + u8 cmd_id, idn = 0; + sector_t lba = -1; int transfer_len = -1; lrbp = &hba->lrb[tag]; @@ -537,23 +668,28 @@ static void ufshcd_add_command_trace(struct ufs_hba *hba, */ if (lrbp->cmd->request && lrbp->cmd->request->bio) lba = - lrbp->cmd->request->bio->bi_iter.bi_sector; + lrbp->cmd->request->bio->bi_iter.bi_sector; transfer_len = be32_to_cpu( lrbp->ucd_req_ptr->sc.exp_data_transfer_len); } } - intr = ufshcd_readl(hba, REG_INTERRUPT_STATUS); - doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); - trace_ufshcd_command(dev_name(hba->dev), str, tag, - doorbell, transfer_len, intr, lba, opcode); -} + if (lrbp->command_type == UTP_CMD_TYPE_SCSI) { + cmd_type = "scsi"; + cmd_id = (u8)(*lrbp->cmd->cmnd); + } else if (lrbp->command_type == UTP_CMD_TYPE_DEV_MANAGE) { + if (hba->dev_cmd.type == DEV_CMD_TYPE_NOP) { + cmd_type = "nop"; + cmd_id = 0; + } else if (hba->dev_cmd.type == DEV_CMD_TYPE_QUERY) { + cmd_type = "query"; + cmd_id = hba->dev_cmd.query.request.upiu_req.opcode; + idn = hba->dev_cmd.query.request.upiu_req.idn; + } + } -static inline void ufshcd_cond_add_cmd_trace(struct ufs_hba *hba, - unsigned int tag, const char *str) -{ - if (trace_ufshcd_command_enabled()) - ufshcd_add_command_trace(hba, tag, str); + __ufshcd_cmd_log(hba, (char *) str, cmd_type, tag, cmd_id, idn, + lrbp->lun, lba, transfer_len, opcode); } #else static inline void ufshcd_cond_add_cmd_trace(struct ufs_hba *hba, @@ -1370,6 +1506,7 @@ start: } spin_unlock_irqrestore(hba->host->host_lock, flags); out: + hba->ufs_stats.clk_hold.ts = ktime_get(); return rc; } EXPORT_SYMBOL_GPL(ufshcd_hold); @@ -1474,6 +1611,7 @@ static void __ufshcd_release(struct ufs_hba *hba, bool no_sched) hba->clk_gating.state = REQ_CLKS_OFF; trace_ufshcd_clk_gating(dev_name(hba->dev), hba->clk_gating.state); + hba->ufs_stats.clk_rel.ts = ktime_get(); hrtimer_start(&hba->clk_gating.gate_hrtimer, ms_to_ktime(hba->clk_gating.delay_ms), @@ -1920,8 +2058,10 @@ static void ufshcd_hibern8_exit_work(struct work_struct *work) /* Exit from hibern8 */ if (ufshcd_is_link_hibern8(hba)) { + hba->ufs_stats.clk_hold.ctx = H8_EXIT_WORK; ufshcd_hold(hba, false); ret = ufshcd_uic_hibern8_exit(hba); + hba->ufs_stats.clk_rel.ctx = H8_EXIT_WORK; ufshcd_release(hba, false); if (!ret) { spin_lock_irqsave(hba->host->host_lock, flags); @@ -2263,6 +2403,7 @@ ufshcd_dispatch_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) hba->active_uic_cmd = uic_cmd; + ufshcd_dme_cmd_log(hba, "send", hba->active_uic_cmd->command); /* Write Args */ ufshcd_writel(hba, uic_cmd->argument1, REG_UIC_COMMAND_ARG_1); ufshcd_writel(hba, uic_cmd->argument2, REG_UIC_COMMAND_ARG_2); @@ -2296,6 +2437,8 @@ ufshcd_wait_for_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) if (ret) ufsdbg_set_err_state(hba); + ufshcd_dme_cmd_log(hba, "cmp1", hba->active_uic_cmd->command); + spin_lock_irqsave(hba->host->host_lock, flags); hba->active_uic_cmd = NULL; spin_unlock_irqrestore(hba->host->host_lock, flags); @@ -2344,6 +2487,7 @@ ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) int ret; unsigned long flags; + hba->ufs_stats.clk_hold.ctx = UIC_CMD_SEND; ufshcd_hold_all(hba); mutex_lock(&hba->uic_cmd_mutex); ufshcd_add_delay_before_dme_cmd(hba); @@ -2357,6 +2501,7 @@ ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) ufshcd_save_tstamp_of_last_dme_cmd(hba); mutex_unlock(&hba->uic_cmd_mutex); ufshcd_release_all(hba); + hba->ufs_stats.clk_rel.ctx = UIC_CMD_SEND; ufsdbg_error_inject_dispatcher(hba, ERR_INJECT_UIC, 0, &ret); @@ -2834,6 +2979,7 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) goto out; } + hba->ufs_stats.clk_hold.ctx = QUEUE_CMD; err = ufshcd_hold(hba, true); if (err) { err = SCSI_MLQUEUE_HOST_BUSY; @@ -2847,6 +2993,7 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) if (err) { clear_bit_unlock(tag, &hba->lrb_in_use); err = SCSI_MLQUEUE_HOST_BUSY; + hba->ufs_stats.clk_rel.ctx = QUEUE_CMD; ufshcd_release(hba, true); goto out; } @@ -4113,6 +4260,8 @@ static int ufshcd_uic_pwr_ctrl(struct ufs_hba *hba, struct uic_command *cmd) cmd->command, status); ret = (status != PWR_OK) ? status : -1; } + ufshcd_dme_cmd_log(hba, "cmp2", hba->active_uic_cmd->command); + out: if (ret) { ufsdbg_set_err_state(hba); @@ -4216,8 +4365,10 @@ static int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode) uic_cmd.command = UIC_CMD_DME_SET; uic_cmd.argument1 = UIC_ARG_MIB(PA_PWRMODE); uic_cmd.argument3 = mode; + hba->ufs_stats.clk_hold.ctx = PWRCTL_CMD_SEND; ufshcd_hold_all(hba); ret = ufshcd_uic_pwr_ctrl(hba, &uic_cmd); + hba->ufs_stats.clk_rel.ctx = PWRCTL_CMD_SEND; ufshcd_release_all(hba); out: return ret; @@ -5385,6 +5536,7 @@ static void __ufshcd_transfer_req_compl(struct ufs_hba *hba, update_req_stats(hba, lrbp); /* Mark completed command as NULL in LRB */ lrbp->cmd = NULL; + hba->ufs_stats.clk_rel.ctx = XFR_REQ_COMPL; __ufshcd_release(hba, false); __ufshcd_hibern8_release(hba, false); if (cmd->request) { @@ -5421,7 +5573,7 @@ static void __ufshcd_transfer_req_compl(struct ufs_hba *hba, } else if (lrbp->command_type == UTP_CMD_TYPE_DEV_MANAGE) { if (hba->dev_cmd.complete) { ufshcd_cond_add_cmd_trace(hba, index, - "dev_complete"); + "dcmp"); complete(hba->dev_cmd.complete); } } @@ -5907,6 +6059,7 @@ static void ufshcd_err_handler(struct work_struct *work) if (unlikely((hba->clk_gating.state != CLKS_ON) && ufshcd_is_auto_hibern8_supported(hba))) { spin_unlock_irqrestore(hba->host->host_lock, flags); + hba->ufs_stats.clk_hold.ctx = ERR_HNDLR_WORK; ufshcd_hold(hba, false); spin_lock_irqsave(hba->host->host_lock, flags); clks_enabled = true; @@ -5943,6 +6096,7 @@ static void ufshcd_err_handler(struct work_struct *work) ufshcd_print_host_state(hba); ufshcd_print_pwr_info(hba); ufshcd_print_tmrs(hba, hba->outstanding_tasks); + ufshcd_cmd_log_print(hba); spin_lock_irqsave(hba->host->host_lock, flags); } } @@ -6049,8 +6203,10 @@ skip_err_handling: hba->silence_err_logs = false; - if (clks_enabled) + if (clks_enabled) { __ufshcd_release(hba, false); + hba->ufs_stats.clk_rel.ctx = ERR_HNDLR_WORK; + } out: ufshcd_clear_eh_in_progress(hba); spin_unlock_irqrestore(hba->host->host_lock, flags); @@ -6286,7 +6442,8 @@ static irqreturn_t ufshcd_intr(int irq, void *__hba) spin_lock(hba->host->host_lock); intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS); - + hba->ufs_stats.last_intr_status = intr_status; + hba->ufs_stats.last_intr_ts = ktime_get(); /* * There could be max of hba->nutrs reqs in flight and in worst case * if the reqs get finished 1 by 1 after the interrupt status is @@ -6365,6 +6522,7 @@ static int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int lun_id, int task_id, * the maximum wait time is bounded by %TM_CMD_TIMEOUT. */ wait_event(hba->tm_tag_wq, ufshcd_get_tm_free_slot(hba, &free_slot)); + hba->ufs_stats.clk_hold.ctx = TM_CMD_SEND; ufshcd_hold_all(hba); spin_lock_irqsave(host->host_lock, flags); @@ -6422,6 +6580,7 @@ static int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int lun_id, int task_id, clear_bit(free_slot, &hba->tm_condition); ufshcd_put_tm_slot(hba, free_slot); wake_up(&hba->tm_tag_wq); + hba->ufs_stats.clk_rel.ctx = TM_CMD_SEND; ufshcd_release_all(hba); return err; @@ -6449,6 +6608,7 @@ static int ufshcd_eh_device_reset_handler(struct scsi_cmnd *cmd) hba = shost_priv(host); tag = cmd->request->tag; + ufshcd_cmd_log_print(hba); lrbp = &hba->lrb[tag]; err = ufshcd_issue_tm_cmd(hba, lrbp->lun, 0, UFS_LOGICAL_RESET, &resp); if (err || resp != UPIU_TASK_MANAGEMENT_FUNC_COMPL) { @@ -9315,6 +9475,7 @@ static int ufshcd_devfreq_scale(struct ufs_hba *hba, bool scale_up) int ret = 0; /* let's not get into low power until clock scaling is completed */ + hba->ufs_stats.clk_hold.ctx = CLK_SCALE_WORK; ufshcd_hold_all(hba); ret = ufshcd_clock_scaling_prepare(hba); @@ -9378,6 +9539,7 @@ scale_up_gear: clk_scaling_unprepare: ufshcd_clock_scaling_unprepare(hba); out: + hba->ufs_stats.clk_rel.ctx = CLK_SCALE_WORK; ufshcd_release_all(hba); return ret; } @@ -9809,6 +9971,8 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) */ ufshcd_set_ufs_dev_active(hba); + ufshcd_cmd_log_init(hba); + async_schedule(ufshcd_async_scan, hba); ufsdbg_add_debugfs(hba); diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index c34a998aac17..d66205ff9f5d 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -3,7 +3,7 @@ * * This code is based on drivers/scsi/ufs/ufshcd.h * Copyright (C) 2011-2013 Samsung India Software Operations - * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * Authors: * Santosh Yaraganavi <santosh.sy@samsung.com> @@ -519,7 +519,7 @@ struct ufs_init_prefetch { u32 icc_level; }; -#define UIC_ERR_REG_HIST_LENGTH 8 +#define UIC_ERR_REG_HIST_LENGTH 20 /** * struct ufs_uic_err_reg_hist - keeps history of uic errors * @pos: index to indicate cyclic buffer position @@ -588,6 +588,22 @@ struct ufshcd_req_stat { }; #endif +enum ufshcd_ctx { + QUEUE_CMD, + ERR_HNDLR_WORK, + H8_EXIT_WORK, + UIC_CMD_SEND, + PWRCTL_CMD_SEND, + TM_CMD_SEND, + XFR_REQ_COMPL, + CLK_SCALE_WORK, +}; + +struct ufshcd_clk_ctx { + ktime_t ts; + enum ufshcd_ctx ctx; +}; + /** * struct ufs_stats - keeps usage/err statistics * @enabled: enable tag stats for debugfs @@ -616,6 +632,10 @@ struct ufs_stats { int query_stats_arr[UPIU_QUERY_OPCODE_MAX][MAX_QUERY_IDN]; #endif + u32 last_intr_status; + ktime_t last_intr_ts; + struct ufshcd_clk_ctx clk_hold; + struct ufshcd_clk_ctx clk_rel; u32 hibern8_exit_cnt; ktime_t last_hibern8_exit_tstamp; struct ufs_uic_err_reg_hist pa_err; @@ -641,6 +661,27 @@ struct ufs_stats { UFSHCD_DBG_PRINT_TMRS_EN | UFSHCD_DBG_PRINT_PWR_EN | \ UFSHCD_DBG_PRINT_HOST_STATE_EN) +struct ufshcd_cmd_log_entry { + char *str; /* context like "send", "complete" */ + char *cmd_type; /* "scsi", "query", "nop", "dme" */ + u8 lun; + u8 cmd_id; + sector_t lba; + int transfer_len; + u8 idn; /* used only for query idn */ + u32 doorbell; + u32 outstanding_reqs; + u32 seq_num; + unsigned int tag; + ktime_t tstamp; +}; + +struct ufshcd_cmd_log { + struct ufshcd_cmd_log_entry *entries; + int pos; + u32 seq_num; +}; + /** * struct ufs_hba - per adapter private structure * @mmio_base: UFSHCI base register address @@ -856,6 +897,7 @@ struct ufs_hba { struct ufs_clk_gating clk_gating; struct ufs_hibern8_on_idle hibern8_on_idle; + struct ufshcd_cmd_log cmd_log; /* Control to enable/disable host capabilities */ u32 caps; diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 34b0adb108eb..ea008ffbc856 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -367,6 +367,14 @@ config MSM_SPM driver allows configuring SPM to allow different low power modes for both core and L2. +config MSM_L2_SPM + bool "SPM support for L2 cache" + help + Enable SPM driver support for L2 cache. Some MSM chipsets allow + control of L2 cache low power mode with a Subsystem Power manager. + Enabling this driver allows configuring L2 SPM for low power modes + on supported chipsets + config QCOM_SCM bool "Secure Channel Manager (SCM) support" default n @@ -573,6 +581,16 @@ config MSM_BOOT_STATS This figures are reported in mpm sleep clock cycles and have a resolution of 31 bits as 1 bit is used as an overflow check. +config MSM_BOOT_TIME_MARKER + bool "Use MSM boot time marker reporting" + depends on MSM_BOOT_STATS + help + Use this to mark msm boot kpi for measurement. + An instrumentation for boot time measurement. + To create an entry, call "place_marker" function. + At userspace, write marker name to "/sys/kernel/debug/bootkpi/kpi_values" + If unsure, say N + config QCOM_CPUSS_DUMP bool "CPU Subsystem Dumping support" help @@ -900,4 +918,28 @@ config QCOM_CX_IPEAK clients are going to cross their thresholds then Cx ipeak hw module will raise an interrupt to cDSP block to throttle cDSP fmax. +config MSM_CACHE_M4M_ERP64 + bool "Cache and M4M error report" + depends on ARCH_MSM8996 + help + Say 'Y' here to enable reporting of cache and M4M errors to the kernel + log. The kernel log contains collected error syndrome and address + registers. These register dumps can be used as useful information + to find out possible hardware problems. + +config MSM_CACHE_M4M_ERP64_PANIC_ON_CE + bool "Panic on correctable cache/M4M errors" + help + Say 'Y' here to cause kernel panic when correctable cache/M4M errors + are detected. Enabling this is useful when you want to dump memory + and system state close to the time when the error occured. + + If unsure, say N. + +config MSM_CACHE_M4M_ERP64_PANIC_ON_UE + bool "Panic on uncorrectable cache/M4M errors" + help + Say 'Y' here to cause kernel panic when uncorrectable cache/M4M errors + are detected. + source "drivers/soc/qcom/memshare/Kconfig" diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 87698b75d3b8..5eeede23333d 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -86,6 +86,7 @@ obj-$(CONFIG_MSM_CORE_HANG_DETECT) += core_hang_detect.o obj-$(CONFIG_MSM_GLADIATOR_HANG_DETECT) += gladiator_hang_detect.o obj-$(CONFIG_MSM_RUN_QUEUE_STATS) += msm_rq_stats.o obj-$(CONFIG_MSM_BOOT_STATS) += boot_stats.o +obj-$(CONFIG_MSM_BOOT_TIME_MARKER) += boot_marker.o obj-$(CONFIG_MSM_AVTIMER) += avtimer.o ifdef CONFIG_ARCH_MSM8996 obj-$(CONFIG_HW_PERF_EVENTS) += perf_event_kryo.o @@ -104,3 +105,4 @@ obj-$(CONFIG_WCD_DSP_GLINK) += wcd-dsp-glink.o obj-$(CONFIG_QCOM_SMCINVOKE) += smcinvoke.o obj-$(CONFIG_QCOM_EARLY_RANDOM) += early_random.o obj-$(CONFIG_QCOM_CX_IPEAK) += cx_ipeak.o +obj-$(CONFIG_MSM_CACHE_M4M_ERP64) += cache_m4m_erp64.o diff --git a/drivers/soc/qcom/boot_marker.c b/drivers/soc/qcom/boot_marker.c new file mode 100644 index 000000000000..b3a6c9f8d054 --- /dev/null +++ b/drivers/soc/qcom/boot_marker.c @@ -0,0 +1,183 @@ +/* Copyright (c) 2016, 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/err.h> +#include <linux/io.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/debugfs.h> +#include <linux/fs.h> +#include <linux/time.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/list.h> +#include <linux/export.h> +#include <linux/types.h> +#include <linux/mutex.h> +#include <soc/qcom/boot_stats.h> + +#define MAX_STRING_LEN 256 +#define BOOT_MARKER_MAX_LEN 40 +static struct dentry *dent_bkpi, *dent_bkpi_status; +static struct boot_marker boot_marker_list; + +struct boot_marker { + char marker_name[BOOT_MARKER_MAX_LEN]; + unsigned long long int timer_value; + struct list_head list; + struct mutex lock; +}; + +static void _create_boot_marker(const char *name, + unsigned long long int timer_value) +{ + struct boot_marker *new_boot_marker; + + pr_debug("%-41s:%llu.%03llu seconds\n", name, + timer_value/TIMER_KHZ, + ((timer_value % TIMER_KHZ) + * 1000) / TIMER_KHZ); + + new_boot_marker = kmalloc(sizeof(*new_boot_marker), GFP_KERNEL); + if (!new_boot_marker) + return; + + strlcpy(new_boot_marker->marker_name, name, + sizeof(new_boot_marker->marker_name)); + new_boot_marker->timer_value = timer_value; + + mutex_lock(&boot_marker_list.lock); + list_add_tail(&(new_boot_marker->list), &(boot_marker_list.list)); + mutex_unlock(&boot_marker_list.lock); +} + +static void set_bootloader_stats(void) +{ + _create_boot_marker("M - APPSBL Start - ", + readl_relaxed(&boot_stats->bootloader_start)); + _create_boot_marker("M - APPSBL Display Init - ", + readl_relaxed(&boot_stats->bootloader_display)); + _create_boot_marker("M - APPSBL Early-Domain Start - ", + readl_relaxed(&boot_stats->bootloader_early_domain_start)); + _create_boot_marker("D - APPSBL Kernel Load Time - ", + readl_relaxed(&boot_stats->bootloader_load_kernel)); + _create_boot_marker("D - APPSBL Kernel Auth Time - ", + readl_relaxed(&boot_stats->bootloader_checksum)); + _create_boot_marker("M - APPSBL End - ", + readl_relaxed(&boot_stats->bootloader_end)); +} + +void place_marker(const char *name) +{ + _create_boot_marker((char *) name, msm_timer_get_sclk_ticks()); +} +EXPORT_SYMBOL(place_marker); + +static ssize_t bootkpi_reader(struct file *fp, char __user *user_buffer, + size_t count, loff_t *position) +{ + int rc = 0; + char *buf; + int temp = 0; + struct boot_marker *marker; + + buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + mutex_lock(&boot_marker_list.lock); + list_for_each_entry(marker, &boot_marker_list.list, list) { + temp += scnprintf(buf + temp, PAGE_SIZE - temp, + "%-41s:%llu.%03llu seconds\n", + marker->marker_name, + marker->timer_value/TIMER_KHZ, + (((marker->timer_value % TIMER_KHZ) + * 1000) / TIMER_KHZ)); + } + mutex_unlock(&boot_marker_list.lock); + rc = simple_read_from_buffer(user_buffer, count, position, buf, temp); + kfree(buf); + return rc; +} + +static ssize_t bootkpi_writer(struct file *fp, const char __user *user_buffer, + size_t count, loff_t *position) +{ + int rc = 0; + char buf[MAX_STRING_LEN]; + + if (count > MAX_STRING_LEN) + return -EINVAL; + rc = simple_write_to_buffer(buf, + sizeof(buf) - 1, position, user_buffer, count); + if (rc < 0) + return rc; + buf[rc] = '\0'; + place_marker(buf); + return rc; +} + +static int bootkpi_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static const struct file_operations fops_bkpi = { + .owner = THIS_MODULE, + .open = bootkpi_open, + .read = bootkpi_reader, + .write = bootkpi_writer, +}; + +static int __init init_bootkpi(void) +{ + dent_bkpi = debugfs_create_dir("bootkpi", NULL); + if (IS_ERR_OR_NULL(dent_bkpi)) + return -ENODEV; + + dent_bkpi_status = debugfs_create_file("kpi_values", + (S_IRUGO|S_IWUGO), dent_bkpi, 0, &fops_bkpi); + if (IS_ERR_OR_NULL(dent_bkpi_status)) { + debugfs_remove(dent_bkpi); + dent_bkpi = NULL; + pr_err("boot_marker: Could not create 'kpi_values' debugfs file\n"); + return -ENODEV; + } + + INIT_LIST_HEAD(&boot_marker_list.list); + mutex_init(&boot_marker_list.lock); + set_bootloader_stats(); + return 0; +} +subsys_initcall(init_bootkpi); + +static void __exit exit_bootkpi(void) +{ + struct boot_marker *marker; + struct boot_marker *temp_addr; + + debugfs_remove_recursive(dent_bkpi); + mutex_lock(&boot_marker_list.lock); + list_for_each_entry_safe(marker, temp_addr, &boot_marker_list.list, + list) { + list_del(&marker->list); + kfree(marker); + } + mutex_unlock(&boot_marker_list.lock); + boot_stats_exit(); +} +module_exit(exit_bootkpi); + +MODULE_DESCRIPTION("MSM boot key performance indicators"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/boot_stats.c b/drivers/soc/qcom/boot_stats.c index 2fc9cbf55d4b..eb5357e892eb 100644 --- a/drivers/soc/qcom/boot_stats.c +++ b/drivers/soc/qcom/boot_stats.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2014,2016, 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 @@ -15,6 +15,7 @@ #include <linux/io.h> #include <linux/init.h> #include <linux/delay.h> +#include <linux/slab.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/clk.h> @@ -22,17 +23,13 @@ #include <linux/sched.h> #include <linux/of.h> #include <linux/of_address.h> - -struct boot_stats { - uint32_t bootloader_start; - uint32_t bootloader_end; - uint32_t bootloader_display; - uint32_t bootloader_load_kernel; -}; +#include <linux/export.h> +#include <linux/types.h> +#include <soc/qcom/boot_stats.h> static void __iomem *mpm_counter_base; static uint32_t mpm_counter_freq; -static struct boot_stats __iomem *boot_stats; +struct boot_stats __iomem *boot_stats; static int mpm_parse_dt(void) { @@ -88,6 +85,42 @@ static void print_boot_stats(void) mpm_counter_freq); } +unsigned long long int msm_timer_get_sclk_ticks(void) +{ + unsigned long long int t1, t2; + int loop_count = 10; + int loop_zero_count = 3; + int tmp = USEC_PER_SEC; + void __iomem *sclk_tick; + + do_div(tmp, TIMER_KHZ); + tmp /= (loop_zero_count-1); + sclk_tick = mpm_counter_base; + if (!sclk_tick) + return -EINVAL; + while (loop_zero_count--) { + t1 = __raw_readl_no_log(sclk_tick); + do { + udelay(1); + t2 = t1; + t1 = __raw_readl_no_log(sclk_tick); + } while ((t2 != t1) && --loop_count); + if (!loop_count) { + pr_err("boot_stats: SCLK did not stabilize\n"); + return 0; + } + if (t1) + break; + + udelay(tmp); + } + if (!loop_zero_count) { + pr_err("boot_stats: SCLK reads zero\n"); + return 0; + } + return t1; +} + int boot_stats_init(void) { int ret; @@ -98,9 +131,14 @@ int boot_stats_init(void) print_boot_stats(); + if (!(boot_marker_enabled())) + boot_stats_exit(); + return 0; +} + +int boot_stats_exit(void) +{ iounmap(boot_stats); iounmap(mpm_counter_base); - return 0; } - diff --git a/drivers/soc/qcom/cache_m4m_erp64.c b/drivers/soc/qcom/cache_m4m_erp64.c new file mode 100644 index 000000000000..758e9d03e07b --- /dev/null +++ b/drivers/soc/qcom/cache_m4m_erp64.c @@ -0,0 +1,635 @@ +/* + * Copyright (c) 2012-2015, 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 pr_fmt(fmt) "msm_cache_erp64: " fmt + +#include <linux/printk.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/platform_device.h> +#include <linux/uaccess.h> +#include <linux/cpu.h> +#include <linux/workqueue.h> +#include <linux/of.h> +#include <linux/cpu_pm.h> +#include <linux/smp.h> + +#include <soc/qcom/kryo-l2-accessors.h> + +/* Instruction cache */ +#define ICECR_EL1 S3_1_c11_c1_0 +#define ICECR_IRQ_EN (BIT(1) | BIT(3) | BIT(5) | BIT(7)) +#define ICESR_EL1 S3_1_c11_c1_1 +#define ICESR_BIT_L1DPE BIT(3) +#define ICESR_BIT_L1TPE BIT(2) +#define ICESR_BIT_L0DPE BIT(1) +#define ICESR_BIT_L0TPE BIT(0) +#define ICESYNR0_EL1 S3_1_c11_c1_3 +#define ICESYNR1_EL1 S3_1_c11_c1_4 +#define ICEAR0_EL1 S3_1_c11_c1_5 +#define ICEAR1_EL1 S3_1_c11_c1_6 +#define ICESRS_EL1 S3_1_c11_c1_2 + +/* Data cache */ +#define DCECR_EL1 S3_1_c11_c5_0 +#define DCECR_IRQ_EN (BIT(1) | BIT(3) | BIT(5) | BIT(7) | \ + BIT(9)) +#define DCESR_EL1 S3_1_c11_c5_1 +#define DCESR_BIT_S1FTLBDPE BIT(4) +#define DCESR_BIT_S1FTLBTPE BIT(3) +#define DCESR_BIT_L1DPE BIT(2) +#define DCESR_BIT_L1PTPE BIT(1) +#define DCESR_BIT_L1VTPE BIT(0) +#define DCESYNR0_EL1 S3_1_c11_c5_3 +#define DCESYNR1_EL1 S3_1_c11_c5_4 +#define DCESRS_EL1 S3_1_c11_c5_2 +#define DCEAR0_EL1 S3_1_c11_c5_5 +#define DCEAR1_EL1 S3_1_c11_c5_6 + +/* L2 cache */ +#define L2CPUSRSELR_EL1I S3_3_c15_c0_6 +#define L2CPUSRDR_EL1 S3_3_c15_c0_7 +#define L2ECR0_IA 0x200 +#define L2ECR0_IRQ_EN (BIT(1) | BIT(3) | BIT(6) | BIT(9) | \ + BIT(11) | BIT(13) | BIT(16) | \ + BIT(19) | BIT(21) | BIT(23) | \ + BIT(26) | BIT(29)) + +#define L2ECR1_IA 0x201 +#define L2ECR1_IRQ_EN (BIT(1) | BIT(3) | BIT(6) | BIT(9) | \ + BIT(11) | BIT(13) | BIT(16) | \ + BIT(19) | BIT(21) | BIT(23) | BIT(29)) +#define L2ECR2_IA 0x202 +#define L2ECR2_IRQ_EN_MASK 0x3FFFFFF +#define L2ECR2_IRQ_EN (BIT(1) | BIT(3) | BIT(6) | BIT(9) | \ + BIT(12) | BIT(15) | BIT(17) | \ + BIT(19) | BIT(22) | BIT(25)) +#define L2ESR0_IA 0x204 +#define L2ESR0_MASK 0x00FFFFFF +#define L2ESR0_CE ((BIT(0) | BIT(1) | BIT(2) | BIT(3) | \ + BIT(4) | BIT(5) | BIT(12) | BIT(13) | \ + BIT(14) | BIT(15) | BIT(16) | BIT(17)) \ + & L2ESR0_MASK) +#define L2ESR0_UE (~L2ESR0_CE & L2ESR0_MASK) +#define L2ESRS0_IA 0x205 +#define L2ESR1_IA 0x206 +#define L2ESR1_MASK 0x80FFFBFF +#define L2ESRS1_IA 0x207 +#define L2ESYNR0_IA 0x208 +#define L2ESYNR1_IA 0x209 +#define L2ESYNR2_IA 0x20A +#define L2ESYNR3_IA 0x20B +#define L2ESYNR4_IA 0x20C +#define L2EAR0_IA 0x20E +#define L2EAR1_IA 0x20F + +#define L3_QLL_HML3_FIRA 0x3000 +#define L3_QLL_HML3_FIRA_CE (BIT(1) | BIT(3) | BIT(5)) +#define L3_QLL_HML3_FIRA_UE (BIT(2) | BIT(4) | BIT(6)) +#define L3_QLL_HML3_FIRAC 0x3008 +#define L3_QLL_HML3_FIRAS 0x3010 +#define L3_QLL_HML3_FIRAT0C 0x3020 +#define L3_QLL_HML3_FIRAT0C_IRQ_EN 0xFFFFFFFF +#define L3_QLL_HML3_FIRAT1C 0x3024 +#define L3_QLL_HML3_FIRAT1S 0x302C +#define L3_QLL_HML3_FIRAT1S_IRQ_EN 0x01EFC8FE +#define L3_QLL_HML3_FIRSYNA 0x3100 +#define L3_QLL_HML3_FIRSYNB 0x3104 +#define L3_QLL_HML3_FIRSYNC 0x3108 +#define L3_QLL_HML3_FIRSYND 0x310C + +#define M4M_ERR_STATUS 0x10000 +#define M4M_ERR_STATUS_MASK 0x1FF +#define M4M_ERR_Q22SIB_RET_DEC_ERR (BIT(7)) +#define M4M_ERR_Q22SIB_RET_SLV_ERR (BIT(6)) +#define M4M_ERR_CLR 0x10008 +#define M4M_INT_CTRL 0x10010 +#define M4M_INT_CTRL_IRQ_EN 0x1FF +#define M4M_ERR_CTRL 0x10018 +#define M4M_ERR_INJ 0x10020 +#define M4M_ERR_CAP_0 0x10030 +#define M4M_ERR_CAP_1 0x10038 +#define M4M_ERR_CAP_2 0x10040 +#define M4M_ERR_CAP_3 0x10048 + +#define AFFINITY_LEVEL_L3 3 + +#ifdef CONFIG_MSM_CACHE_M4M_ERP64_PANIC_ON_CE +static bool __read_mostly panic_on_ce = true; +#else +static bool __read_mostly panic_on_ce; +#endif + +#ifdef CONFIG_MSM_CACHE_M4M_ERP64_PANIC_ON_UE +static bool __read_mostly panic_on_ue = true; +#else +static bool __read_mostly panic_on_ue; +#endif + +module_param(panic_on_ce, bool, false); +module_param(panic_on_ue, bool, false); + +static void __iomem *hml3_base; +static void __iomem *m4m_base; + +enum erp_irq_index { IRQ_L1, IRQ_L2_INFO0, IRQ_L2_INFO1, IRQ_L2_ERR0, + IRQ_L2_ERR1, IRQ_L3, IRQ_M4M, IRQ_MAX }; +static const char * const erp_irq_names[] = { + "l1_irq", "l2_irq_info_0", "l2_irq_info_1", "l2_irq_err_0", + "l2_irq_err_1", "l3_irq", "m4m_irq" +}; +static int erp_irqs[IRQ_MAX]; + +struct msm_l1_err_stats { + /* nothing */ +}; + +static DEFINE_PER_CPU(struct msm_l1_err_stats, msm_l1_erp_stats); +static DEFINE_PER_CPU(struct call_single_data, handler_csd); + +#define erp_mrs(reg) ({ \ + u64 __val; \ + asm volatile("mrs %0, " __stringify(reg) : "=r" (__val)); \ + __val; \ +}) + +#define erp_msr(reg, val) { \ + asm volatile("msr " __stringify(reg) ", %0" : : "r" (val)); \ +} + +static void msm_erp_show_icache_error(void) +{ + u64 icesr; + int cpu = raw_smp_processor_id(); + + icesr = erp_mrs(ICESR_EL1); + if (!(icesr & (ICESR_BIT_L0TPE | ICESR_BIT_L0DPE | ICESR_BIT_L1TPE | + ICESR_BIT_L1DPE))) { + pr_debug("CPU%d: No I-cache error detected ICESR 0x%llx\n", + cpu, icesr); + goto clear_out; + } + + pr_alert("CPU%d: I-cache error\n", cpu); + pr_alert("CPU%d: ICESR_EL1 0x%llx ICESYNR0 0x%llx ICESYNR1 0x%llx ICEAR0 0x%llx IECAR1 0x%llx\n", + cpu, icesr, erp_mrs(ICESYNR0_EL1), erp_mrs(ICESYNR1_EL1), + erp_mrs(ICEAR0_EL1), erp_mrs(ICEAR1_EL1)); + + /* + * all detectable I-cache erros are recoverable as + * corrupted lines are refetched + */ + if (panic_on_ce) + BUG_ON(1); + else + WARN_ON(1); + +clear_out: + erp_msr(ICESR_EL1, icesr); +} + +static void msm_erp_show_dcache_error(void) +{ + u64 dcesr; + int cpu = raw_smp_processor_id(); + + dcesr = erp_mrs(DCESR_EL1); + if (!(dcesr & (DCESR_BIT_L1VTPE | DCESR_BIT_L1PTPE | DCESR_BIT_L1DPE | + DCESR_BIT_S1FTLBTPE | DCESR_BIT_S1FTLBDPE))) { + pr_debug("CPU%d: No D-cache error detected DCESR 0x%llx\n", + cpu, dcesr); + goto clear_out; + } + + pr_alert("CPU%d: D-cache error detected\n", cpu); + pr_alert("CPU%d: L1 DCESR 0x%llx, DCESYNR0 0x%llx, DCESYNR1 0x%llx, DCEAR0 0x%llx, DCEAR1 0x%llx\n", + cpu, dcesr, erp_mrs(DCESYNR0_EL1), erp_mrs(DCESYNR1_EL1), + erp_mrs(DCEAR0_EL1), erp_mrs(DCEAR1_EL1)); + + /* all D-cache erros are correctable */ + if (panic_on_ce) + BUG_ON(1); + else + WARN_ON(1); + +clear_out: + erp_msr(DCESR_EL1, dcesr); +} + +static irqreturn_t msm_l1_erp_irq(int irq, void *dev_id) +{ + msm_erp_show_icache_error(); + msm_erp_show_dcache_error(); + return IRQ_HANDLED; +} + +static DEFINE_SPINLOCK(local_handler_lock); +static void msm_l2_erp_local_handler(void *force) +{ + unsigned long flags; + u64 esr0, esr1; + bool parity_ue, parity_ce, misc_ue; + int cpu; + + spin_lock_irqsave(&local_handler_lock, flags); + + esr0 = get_l2_indirect_reg(L2ESR0_IA); + esr1 = get_l2_indirect_reg(L2ESR1_IA); + parity_ue = esr0 & L2ESR0_UE; + parity_ce = esr0 & L2ESR0_CE; + misc_ue = esr1; + cpu = raw_smp_processor_id(); + + if (force || parity_ue || parity_ce || misc_ue) { + if (parity_ue) + pr_alert("CPU%d: L2 uncorrectable parity error\n", cpu); + if (parity_ce) + pr_alert("CPU%d: L2 correctable parity error\n", cpu); + if (misc_ue) + pr_alert("CPU%d: L2 (non-parity) error\n", cpu); + pr_alert("CPU%d: L2ESR0 0x%llx, L2ESR1 0x%llx\n", + cpu, esr0, esr1); + pr_alert("CPU%d: L2ESYNR0 0x%llx, L2ESYNR1 0x%llx, L2ESYNR2 0x%llx\n", + cpu, get_l2_indirect_reg(L2ESYNR0_IA), + get_l2_indirect_reg(L2ESYNR1_IA), + get_l2_indirect_reg(L2ESYNR2_IA)); + pr_alert("CPU%d: L2EAR0 0x%llx, L2EAR1 0x%llx\n", cpu, + get_l2_indirect_reg(L2EAR0_IA), + get_l2_indirect_reg(L2EAR1_IA)); + } else { + pr_info("CPU%d: No L2 error detected in L2ESR0 0x%llx, L2ESR1 0x%llx)\n", + cpu, esr0, esr1); + } + + /* clear */ + set_l2_indirect_reg(L2ESR0_IA, esr0); + set_l2_indirect_reg(L2ESR1_IA, esr1); + + if (panic_on_ue) + BUG_ON(parity_ue || misc_ue); + else + WARN_ON(parity_ue || misc_ue); + + if (panic_on_ce) + BUG_ON(parity_ce); + else + WARN_ON(parity_ce); + + spin_unlock_irqrestore(&local_handler_lock, flags); +} + +static irqreturn_t msm_l2_erp_irq(int irq, void *dev_id) +{ + int cpu; + struct call_single_data *csd; + + for_each_online_cpu(cpu) { + csd = &per_cpu(handler_csd, cpu); + csd->func = msm_l2_erp_local_handler; + smp_call_function_single_async(cpu, csd); + } + + return IRQ_HANDLED; +} + +static irqreturn_t msm_l3_erp_irq(int irq, void *dev_id) +{ + u32 hml3_fira; + bool parity_ue, parity_ce, misc_ue; + + hml3_fira = readl_relaxed(hml3_base + L3_QLL_HML3_FIRA); + parity_ue = (hml3_fira & L3_QLL_HML3_FIRAT1S_IRQ_EN) & + L3_QLL_HML3_FIRA_UE; + parity_ce = (hml3_fira & L3_QLL_HML3_FIRAT1S_IRQ_EN) & + L3_QLL_HML3_FIRA_CE; + misc_ue = (hml3_fira & L3_QLL_HML3_FIRAT1S_IRQ_EN) & + ~(L3_QLL_HML3_FIRA_UE | L3_QLL_HML3_FIRA_CE); + if (parity_ue) + pr_alert("L3 uncorrectable parity error\n"); + if (parity_ce) + pr_alert("L3 correctable parity error\n"); + if (misc_ue) + pr_alert("L3 (non-parity) error\n"); + + pr_alert("HML3_FIRA 0x%0x\n", hml3_fira); + pr_alert("HML3_FIRSYNA 0x%0x, HML3_FIRSYNB 0x%0x\n", + readl_relaxed(hml3_base + L3_QLL_HML3_FIRSYNA), + readl_relaxed(hml3_base + L3_QLL_HML3_FIRSYNB)); + pr_alert("HML3_FIRSYNC 0x%0x, HML3_FIRSYND 0x%0x\n", + readl_relaxed(hml3_base + L3_QLL_HML3_FIRSYNC), + readl_relaxed(hml3_base + L3_QLL_HML3_FIRSYND)); + + if (panic_on_ue) + BUG_ON(parity_ue || misc_ue); + else + WARN_ON(parity_ue || misc_ue); + + if (panic_on_ce) + BUG_ON(parity_ce); + else + WARN_ON(parity_ce); + + writel_relaxed(hml3_fira, hml3_base + L3_QLL_HML3_FIRAC); + /* ensure of irq clear */ + wmb(); + return IRQ_HANDLED; +} + +static irqreturn_t msm_m4m_erp_irq(int irq, void *dev_id) +{ + u32 m4m_status; + + pr_alert("CPU%d: M4M error detected\n", raw_smp_processor_id()); + m4m_status = readl_relaxed(m4m_base + M4M_ERR_STATUS); + pr_alert("M4M_ERR_STATUS 0x%0x\n", m4m_status); + if ((m4m_status & M4M_ERR_STATUS_MASK) & + ~(M4M_ERR_Q22SIB_RET_DEC_ERR | M4M_ERR_Q22SIB_RET_SLV_ERR)) { + pr_alert("M4M_ERR_CAP_0 0x%0x, M4M_ERR_CAP_1 0x%x\n", + readl_relaxed(m4m_base + M4M_ERR_CAP_0), + readl_relaxed(m4m_base + M4M_ERR_CAP_1)); + pr_alert("M4M_ERR_CAP_2 0x%0x, M4M_ERR_CAP_3 0x%x\n", + readl_relaxed(m4m_base + M4M_ERR_CAP_2), + readl_relaxed(m4m_base + M4M_ERR_CAP_3)); + } else { + /* + * M4M error-capture registers not valid when error detected + * due to DEC_ERR or SLV_ERR. L2E registers are still valid. + */ + pr_alert("Omit dumping M4M_ERR_CAP\n"); + } + + /* + * On QSB errors, the L2 captures the bad address and syndrome in + * L2E error registers. Therefore dump L2E always whenever M4M error + * detected. + */ + on_each_cpu(msm_l2_erp_local_handler, (void *)1, 1); + writel_relaxed(1, m4m_base + M4M_ERR_CLR); + /* ensure of irq clear */ + wmb(); + + if (panic_on_ue) + BUG_ON(1); + else + WARN_ON(1); + + return IRQ_HANDLED; +} + +static void enable_erp_irq_callback(void *info) +{ + enable_percpu_irq(erp_irqs[IRQ_L1], IRQ_TYPE_NONE); +} + +static void disable_erp_irq_callback(void *info) +{ + disable_percpu_irq(erp_irqs[IRQ_L1]); +} + +static void msm_cache_erp_irq_init(void *param) +{ + u64 v; + /* Enable L0/L1 I/D cache error reporting. */ + erp_msr(ICECR_EL1, ICECR_IRQ_EN); + erp_msr(DCECR_EL1, DCECR_IRQ_EN); + /* + * Enable L2 data, tag, QSB and possion error reporting. + */ + set_l2_indirect_reg(L2ECR0_IA, L2ECR0_IRQ_EN); + set_l2_indirect_reg(L2ECR1_IA, L2ECR1_IRQ_EN); + v = (get_l2_indirect_reg(L2ECR2_IA) & ~L2ECR2_IRQ_EN_MASK) + | L2ECR2_IRQ_EN; + set_l2_indirect_reg(L2ECR2_IA, v); +} + +static void msm_cache_erp_l3_init(void) +{ + writel_relaxed(L3_QLL_HML3_FIRAT0C_IRQ_EN, + hml3_base + L3_QLL_HML3_FIRAT0C); + writel_relaxed(L3_QLL_HML3_FIRAT1S_IRQ_EN, + hml3_base + L3_QLL_HML3_FIRAT1S); +} + +static int cache_erp_cpu_pm_callback(struct notifier_block *self, + unsigned long cmd, void *v) +{ + unsigned long aff_level = (unsigned long) v; + + switch (cmd) { + case CPU_CLUSTER_PM_EXIT: + msm_cache_erp_irq_init(NULL); + + if (aff_level >= AFFINITY_LEVEL_L3) + msm_cache_erp_l3_init(); + break; + } + return NOTIFY_OK; +} + +static struct notifier_block cache_erp_cpu_pm_notifier = { + .notifier_call = cache_erp_cpu_pm_callback, +}; + +static int cache_erp_cpu_callback(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + switch (action & ~CPU_TASKS_FROZEN) { + case CPU_STARTING: + msm_cache_erp_irq_init(NULL); + enable_erp_irq_callback(NULL); + break; + case CPU_DYING: + disable_erp_irq_callback(NULL); + break; + } + return NOTIFY_OK; +} + +static struct notifier_block cache_erp_cpu_notifier = { + .notifier_call = cache_erp_cpu_callback, +}; + +static int msm_cache_erp_probe(struct platform_device *pdev) +{ + int i, ret = 0; + struct resource *r; + + dev_dbg(&pdev->dev, "enter\n"); + + /* L3 */ + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + hml3_base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(hml3_base)) { + dev_err(&pdev->dev, "failed to ioremap (0x%pK)\n", hml3_base); + return PTR_ERR(hml3_base); + } + + for (i = 0; i <= IRQ_L3; i++) { + r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, + erp_irq_names[i]); + if (!r) { + dev_err(&pdev->dev, "failed to get %s\n", + erp_irq_names[i]); + return -ENODEV; + } + erp_irqs[i] = r->start; + } + + msm_cache_erp_l3_init(); + + /* L0/L1 erp irq per cpu */ + dev_info(&pdev->dev, "Registering for L1 error interrupts\n"); + ret = request_percpu_irq(erp_irqs[IRQ_L1], msm_l1_erp_irq, + erp_irq_names[IRQ_L1], &msm_l1_erp_stats); + if (ret) { + dev_err(&pdev->dev, "failed to request L0/L1 ERP irq %s (%d)\n", + erp_irq_names[IRQ_L1], ret); + return ret; + } else { + dev_dbg(&pdev->dev, "requested L0/L1 ERP irq %s\n", + erp_irq_names[IRQ_L1]); + } + + get_online_cpus(); + register_hotcpu_notifier(&cache_erp_cpu_notifier); + cpu_pm_register_notifier(&cache_erp_cpu_pm_notifier); + + /* Perform L1/L2 cache error detection init on online cpus */ + on_each_cpu(msm_cache_erp_irq_init, NULL, 1); + /* Enable irqs */ + on_each_cpu(enable_erp_irq_callback, NULL, 1); + put_online_cpus(); + + /* L2 erp irq per cluster */ + dev_info(&pdev->dev, "Registering for L2 error interrupts\n"); + for (i = IRQ_L2_INFO0; i <= IRQ_L2_ERR1; i++) { + ret = devm_request_irq(&pdev->dev, erp_irqs[i], + msm_l2_erp_irq, + IRQF_ONESHOT | + IRQF_TRIGGER_HIGH, + erp_irq_names[i], NULL); + if (ret) { + dev_err(&pdev->dev, "failed to request irq %s (%d)\n", + erp_irq_names[i], ret); + goto cleanup; + } + } + + /* L3 erp irq */ + dev_info(&pdev->dev, "Registering for L3 error interrupts\n"); + ret = devm_request_irq(&pdev->dev, erp_irqs[IRQ_L3], msm_l3_erp_irq, + IRQF_ONESHOT | IRQF_TRIGGER_HIGH, + erp_irq_names[IRQ_L3], NULL); + if (ret) { + dev_err(&pdev->dev, "failed to request L3 irq %s (%d)\n", + erp_irq_names[IRQ_L3], ret); + goto cleanup; + } + + return 0; + +cleanup: + free_percpu_irq(erp_irqs[IRQ_L1], NULL); + return ret; +} + +static void msm_m4m_erp_irq_init(void) +{ + writel_relaxed(M4M_INT_CTRL_IRQ_EN, m4m_base + M4M_INT_CTRL); + writel_relaxed(0, m4m_base + M4M_ERR_CTRL); +} + +static int msm_m4m_erp_m4m_probe(struct platform_device *pdev) +{ + int ret = 0; + struct resource *r; + + dev_dbg(&pdev->dev, "enter\n"); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + m4m_base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(m4m_base)) { + dev_err(&pdev->dev, "failed to ioremap (0x%pK)\n", m4m_base); + return PTR_ERR(m4m_base); + } + + r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, + erp_irq_names[IRQ_M4M]); + if (!r) { + dev_err(&pdev->dev, "failed to get %s\n", + erp_irq_names[IRQ_M4M]); + ret = -ENODEV; + goto exit; + } + erp_irqs[IRQ_M4M] = r->start; + + dev_info(&pdev->dev, "Registering for M4M error interrupts\n"); + ret = devm_request_irq(&pdev->dev, erp_irqs[IRQ_M4M], + msm_m4m_erp_irq, + IRQF_ONESHOT | IRQF_TRIGGER_HIGH, + erp_irq_names[IRQ_M4M], NULL); + if (ret) { + dev_err(&pdev->dev, "failed to request irq %s (%d)\n", + erp_irq_names[IRQ_M4M], ret); + goto exit; + } + + msm_m4m_erp_irq_init(); + +exit: + return ret; +} + +static struct of_device_id cache_erp_dt_ids[] = { + { .compatible = "qcom,kryo_cache_erp64", }, + {} +}; +MODULE_DEVICE_TABLE(of, cache_erp_dt_ids); + +static struct platform_driver msm_cache_erp_driver = { + .probe = msm_cache_erp_probe, + .driver = { + .name = "msm_cache_erp64", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(cache_erp_dt_ids), + }, +}; + +static struct of_device_id m4m_erp_dt_ids[] = { + { .compatible = "qcom,m4m_erp", }, + {} +}; +MODULE_DEVICE_TABLE(of, m4m_erp_dt_ids); +static struct platform_driver msm_m4m_erp_driver = { + .probe = msm_m4m_erp_m4m_probe, + .driver = { + .name = "msm_m4m_erp", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(m4m_erp_dt_ids), + }, +}; + +static int __init msm_cache_erp_init(void) +{ + int r; + + r = platform_driver_register(&msm_cache_erp_driver); + if (!r) + r = platform_driver_register(&msm_m4m_erp_driver); + if (r) + pr_err("failed to register driver %d\n", r); + return r; +} + +arch_initcall(msm_cache_erp_init); diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index ab46eb70651c..8c242bc7a702 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -48,11 +48,6 @@ #include <soc/qcom/socinfo.h> #include <soc/qcom/ramdump.h> -#ifdef CONFIG_WCNSS_MEM_PRE_ALLOC -#include <net/cnss_prealloc.h> -#endif - - #include "wlan_firmware_service_v01.h" #ifdef CONFIG_ICNSS_DEBUG @@ -202,6 +197,7 @@ enum icnss_driver_state { ICNSS_MSA0_ASSIGNED, ICNSS_WLFW_EXISTS, ICNSS_WDOG_BITE, + ICNSS_SHUTDOWN_DONE, }; struct ce_irq_list { @@ -695,6 +691,8 @@ static int icnss_qmi_pin_connect_result_ind(void *msg, unsigned int msg_len) goto out; } + memset(&ind_msg, 0, sizeof(ind_msg)); + ind_desc.msg_id = QMI_WLFW_PIN_CONNECT_RESULT_IND_V01; ind_desc.max_msg_len = WLFW_PIN_CONNECT_RESULT_IND_MSG_V01_MAX_MSG_LEN; ind_desc.ei_array = wlfw_pin_connect_result_ind_msg_v01_ei; @@ -1968,8 +1966,6 @@ static int icnss_call_driver_probe(struct icnss_priv *priv) if (ret < 0) { icnss_pr_err("Driver probe failed: %d, state: 0x%lx\n", ret, priv->state); - wcnss_prealloc_check_memory_leak(); - wcnss_pre_alloc_reset(); goto out; } @@ -1990,9 +1986,13 @@ static int icnss_call_driver_shutdown(struct icnss_priv *priv) if (!priv->ops || !priv->ops->shutdown) goto out; + if (test_bit(ICNSS_SHUTDOWN_DONE, &penv->state)) + goto out; + icnss_pr_dbg("Calling driver shutdown state: 0x%lx\n", priv->state); priv->ops->shutdown(&priv->pdev->dev); + set_bit(ICNSS_SHUTDOWN_DONE, &penv->state); out: return 0; @@ -2030,6 +2030,7 @@ static int icnss_pd_restart_complete(struct icnss_priv *priv) } out: + clear_bit(ICNSS_SHUTDOWN_DONE, &penv->state); return 0; call_probe: @@ -2099,8 +2100,6 @@ static int icnss_driver_event_register_driver(void *data) if (ret) { icnss_pr_err("Driver probe failed: %d, state: 0x%lx\n", ret, penv->state); - wcnss_prealloc_check_memory_leak(); - wcnss_pre_alloc_reset(); goto power_off; } @@ -2125,8 +2124,6 @@ static int icnss_driver_event_unregister_driver(void *data) penv->ops->remove(&penv->pdev->dev); clear_bit(ICNSS_DRIVER_PROBED, &penv->state); - wcnss_prealloc_check_memory_leak(); - wcnss_pre_alloc_reset(); penv->ops = NULL; @@ -2151,8 +2148,6 @@ static int icnss_call_driver_remove(struct icnss_priv *priv) penv->ops->remove(&priv->pdev->dev); clear_bit(ICNSS_DRIVER_PROBED, &priv->state); - wcnss_prealloc_check_memory_leak(); - wcnss_pre_alloc_reset(); icnss_hw_power_off(penv); @@ -3667,6 +3662,9 @@ static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv) case ICNSS_WDOG_BITE: seq_puts(s, "MODEM WDOG BITE"); continue; + case ICNSS_SHUTDOWN_DONE: + seq_puts(s, "SHUTDOWN DONE"); + continue; } seq_printf(s, "UNKNOWN-%d", i); diff --git a/drivers/soc/qcom/ipc_router_mhi_xprt.c b/drivers/soc/qcom/ipc_router_mhi_xprt.c index f9d967fd0af6..e5f6104bd7de 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-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, 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 @@ -22,7 +22,7 @@ #include <linux/sched.h> #include <linux/skbuff.h> #include <linux/types.h> - +#include <linux/spinlock.h> static int ipc_router_mhi_xprt_debug_mask; module_param_named(debug_mask, ipc_router_mhi_xprt_debug_mask, @@ -123,9 +123,9 @@ struct ipc_router_mhi_xprt { struct completion sft_close_complete; unsigned xprt_version; unsigned xprt_option; - struct mutex tx_addr_map_list_lock; + spinlock_t tx_addr_map_list_lock; struct list_head tx_addr_map_list; - struct mutex rx_addr_map_list_lock; + spinlock_t rx_addr_map_list_lock; struct list_head rx_addr_map_list; }; @@ -179,16 +179,16 @@ void ipc_router_mhi_release_pkt(struct kref *ref) * Return: The mapped virtual Address if found, NULL otherwise. */ void *ipc_router_mhi_xprt_find_addr_map(struct list_head *addr_map_list, - struct mutex *addr_map_list_lock, - void *addr) + spinlock_t *addr_map_list_lock, void *addr) { struct ipc_router_mhi_addr_map *addr_mapping; struct ipc_router_mhi_addr_map *tmp_addr_mapping; + unsigned long flags; void *virt_addr; if (!addr_map_list || !addr_map_list_lock) return NULL; - mutex_lock(addr_map_list_lock); + spin_lock_irqsave(addr_map_list_lock, flags); list_for_each_entry_safe(addr_mapping, tmp_addr_mapping, addr_map_list, list_node) { if (addr_mapping->virt_addr == addr) { @@ -198,11 +198,11 @@ void *ipc_router_mhi_xprt_find_addr_map(struct list_head *addr_map_list, kref_put(&addr_mapping->pkt->ref, ipc_router_mhi_release_pkt); kfree(addr_mapping); - mutex_unlock(addr_map_list_lock); + spin_unlock_irqrestore(addr_map_list_lock, flags); return virt_addr; } } - mutex_unlock(addr_map_list_lock); + spin_unlock_irqrestore(addr_map_list_lock, flags); IPC_RTR_ERR( "%s: Virtual address mapping [%p] not found\n", __func__, (void *)addr); @@ -219,10 +219,11 @@ void *ipc_router_mhi_xprt_find_addr_map(struct list_head *addr_map_list, * Return: 0 on success, standard Linux error code otherwise. */ int ipc_router_mhi_xprt_add_addr_map(struct list_head *addr_map_list, - struct mutex *addr_map_list_lock, + spinlock_t *addr_map_list_lock, struct rr_packet *pkt, void *virt_addr) { struct ipc_router_mhi_addr_map *addr_mapping; + unsigned long flags; if (!addr_map_list || !addr_map_list_lock) return -EINVAL; @@ -231,11 +232,11 @@ int ipc_router_mhi_xprt_add_addr_map(struct list_head *addr_map_list, return -ENOMEM; addr_mapping->virt_addr = virt_addr; addr_mapping->pkt = pkt; - mutex_lock(addr_map_list_lock); + spin_lock_irqsave(addr_map_list_lock, flags); if (addr_mapping->pkt) kref_get(&addr_mapping->pkt->ref); list_add_tail(&addr_mapping->list_node, addr_map_list); - mutex_unlock(addr_map_list_lock); + spin_unlock_irqrestore(addr_map_list_lock, flags); return 0; } @@ -719,12 +720,11 @@ static void mhi_xprt_xfer_event(struct mhi_cb_info *cb_info) mhi_xprtp = (struct ipc_router_mhi_xprt *)(cb_info->result->user_data); if (cb_info->chan == mhi_xprtp->ch_hndl.out_chan_id) { out_addr = cb_info->result->buf_addr; - mutex_lock(&mhi_xprtp->ch_hndl.state_lock); - ipc_router_mhi_xprt_find_addr_map(&mhi_xprtp->tx_addr_map_list, + ipc_router_mhi_xprt_find_addr_map( + &mhi_xprtp->tx_addr_map_list, &mhi_xprtp->tx_addr_map_list_lock, out_addr); wake_up(&mhi_xprtp->write_wait_q); - mutex_unlock(&mhi_xprtp->ch_hndl.state_lock); } else if (cb_info->chan == mhi_xprtp->ch_hndl.in_chan_id) { queue_work(mhi_xprtp->wq, &mhi_xprtp->read_work); } else { @@ -875,9 +875,9 @@ static int ipc_router_mhi_config_init( mhi_xprtp->ch_hndl.num_trbs = IPC_ROUTER_MHI_XPRT_NUM_TRBS; mhi_xprtp->ch_hndl.mhi_xprtp = mhi_xprtp; INIT_LIST_HEAD(&mhi_xprtp->tx_addr_map_list); - mutex_init(&mhi_xprtp->tx_addr_map_list_lock); + spin_lock_init(&mhi_xprtp->tx_addr_map_list_lock); INIT_LIST_HEAD(&mhi_xprtp->rx_addr_map_list); - mutex_init(&mhi_xprtp->rx_addr_map_list_lock); + spin_lock_init(&mhi_xprtp->rx_addr_map_list_lock); rc = ipc_router_mhi_driver_register(mhi_xprtp); return rc; diff --git a/drivers/soc/qcom/memory_dump_v2.c b/drivers/soc/qcom/memory_dump_v2.c index 092b1c1af44b..924c826208dd 100644 --- a/drivers/soc/qcom/memory_dump_v2.c +++ b/drivers/soc/qcom/memory_dump_v2.c @@ -95,7 +95,7 @@ int msm_dump_data_add_minidump(struct msm_dump_entry *entry) data = (struct msm_dump_data *)(phys_to_virt(entry->addr)); if (!strcmp(data->name, "")) { - pr_info("Entry name is NULL, Use ID %d for minidump\n", + pr_debug("Entry name is NULL, Use ID %d for minidump\n", entry->id); snprintf(md_entry.name, sizeof(md_entry.name), "KMDT0x%X", entry->id); @@ -133,7 +133,7 @@ int msm_dump_data_register(enum msm_dump_table_ids id, dmac_flush_range(table, (void *)table + sizeof(struct msm_dump_table)); if (msm_dump_data_add_minidump(entry)) - pr_info("Failed to add entry in Minidump table\n"); + pr_err("Failed to add entry in Minidump table\n"); return 0; } diff --git a/drivers/soc/qcom/memshare/msm_memshare.c b/drivers/soc/qcom/memshare/msm_memshare.c index b8417513ca55..c11114528d2a 100644 --- a/drivers/soc/qcom/memshare/msm_memshare.c +++ b/drivers/soc/qcom/memshare/msm_memshare.c @@ -498,6 +498,7 @@ static int handle_alloc_generic_req(void *req_h, void *req, void *conn_h) struct mem_alloc_generic_resp_msg_v01 *alloc_resp; int rc, resp = 0; int client_id; + uint32_t size = 0; alloc_req = (struct mem_alloc_generic_req_msg_v01 *)req; pr_debug("memshare: alloc request client id: %d proc _id: %d\n", @@ -528,7 +529,11 @@ static int handle_alloc_generic_req(void *req_h, void *req, void *conn_h) __func__, memblock[client_id].client_id, memblock[client_id].free_memory); if (!memblock[client_id].alloted) { - rc = memshare_alloc(memsh_drv->dev, alloc_req->num_bytes, + if (alloc_req->client_id == 1 && alloc_req->num_bytes > 0) + size = alloc_req->num_bytes + MEMSHARE_GUARD_BYTES; + else + size = alloc_req->num_bytes; + rc = memshare_alloc(memsh_drv->dev, size, &memblock[client_id]); if (rc) { pr_err("In %s,Unable to allocate memory for requested client\n", @@ -963,8 +968,10 @@ static int memshare_child_probe(struct platform_device *pdev) * Memshare allocation for guaranteed clients */ if (memblock[num_clients].guarantee) { + if (client_id == 1 && size > 0) + size += MEMSHARE_GUARD_BYTES; rc = memshare_alloc(memsh_child->dev, - memblock[num_clients].size, + size, &memblock[num_clients]); if (rc) { pr_err("In %s, Unable to allocate memory for guaranteed clients, rc: %d\n", diff --git a/drivers/soc/qcom/memshare/msm_memshare.h b/drivers/soc/qcom/memshare/msm_memshare.h index 398907532977..c7123fb1314b 100644 --- a/drivers/soc/qcom/memshare/msm_memshare.h +++ b/drivers/soc/qcom/memshare/msm_memshare.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, 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 @@ -24,6 +24,7 @@ #define GPS 0 #define CHECK 0 #define FREE 1 +#define MEMSHARE_GUARD_BYTES (4*1024) struct mem_blocks { /* Client Id information */ diff --git a/drivers/soc/qcom/msm_minidump.c b/drivers/soc/qcom/msm_minidump.c index 1cb36bf98555..300233085161 100644 --- a/drivers/soc/qcom/msm_minidump.c +++ b/drivers/soc/qcom/msm_minidump.c @@ -62,24 +62,31 @@ struct md_table { struct md_region entry[MAX_NUM_ENTRIES]; }; +/* + * md_elfhdr: Minidump table elf header + * @md_ehdr: elf main header + * @shdr: Section header + * @phdr: Program header + * @elf_offset: section offset in elf + * @strtable_idx: string table current index position + */ +struct md_elfhdr { + struct elfhdr *md_ehdr; + struct elf_shdr *shdr; + struct elf_phdr *phdr; + u64 elf_offset; + u64 strtable_idx; +}; + /* Protect elfheader and smem table from deferred calls contention */ static DEFINE_SPINLOCK(mdt_lock); -static bool minidump_enabled; -static struct md_table minidump_table; +static struct md_table minidump_table; +static struct md_elfhdr minidump_elfheader; + +bool minidump_enabled; static unsigned int pendings; static unsigned int region_idx = 1; /* First entry is ELF header*/ -/* ELF Header */ -static struct elfhdr *md_ehdr; -/* ELF Program header */ -static struct elf_phdr *phdr; -/* ELF Section header */ -static struct elf_shdr *shdr; -/* Section offset in elf image */ -static u64 elf_offset; -/* String table index, first byte must be '\0' */ -static unsigned int stringtable_idx = 1; - static inline struct elf_shdr *elf_sheader(struct elfhdr *hdr) { return (struct elf_shdr *)((size_t)hdr + (size_t)hdr->e_shoff); @@ -90,6 +97,16 @@ static inline struct elf_shdr *elf_section(struct elfhdr *hdr, int idx) return &elf_sheader(hdr)[idx]; } +static inline struct elf_phdr *elf_pheader(struct elfhdr *hdr) +{ + return (struct elf_phdr *)((size_t)hdr + (size_t)hdr->e_phoff); +} + +static inline struct elf_phdr *elf_program(struct elfhdr *hdr, int idx) +{ + return &elf_pheader(hdr)[idx]; +} + static inline char *elf_str_table(struct elfhdr *hdr) { if (hdr->e_shstrndx == SHN_UNDEF) @@ -101,23 +118,24 @@ static inline char *elf_lookup_string(struct elfhdr *hdr, int offset) { char *strtab = elf_str_table(hdr); - if ((strtab == NULL) | (stringtable_idx < offset)) + if ((strtab == NULL) || (minidump_elfheader.strtable_idx < offset)) return NULL; return strtab + offset; } static inline unsigned int set_section_name(const char *name) { - char *strtab = elf_str_table(md_ehdr); + char *strtab = elf_str_table(minidump_elfheader.md_ehdr); + int idx = minidump_elfheader.strtable_idx; int ret = 0; - if ((strtab == NULL) | (name == NULL)) + if ((strtab == NULL) || (name == NULL)) return 0; - ret = stringtable_idx; - stringtable_idx += strlcpy((strtab + stringtable_idx), - name, MAX_NAME_LENGTH); - stringtable_idx += 1; + ret = idx; + idx += strlcpy((strtab + idx), name, MAX_NAME_LENGTH); + minidump_elfheader.strtable_idx = idx + 1; + return ret; } @@ -137,11 +155,9 @@ static inline bool md_check_name(const char *name) static int md_update_smem_table(const struct md_region *entry) { struct md_smem_region *mdr; - - if (!minidump_enabled) { - pr_info("Table in smem is not setup\n"); - return -ENODEV; - } + struct elfhdr *hdr = minidump_elfheader.md_ehdr; + struct elf_shdr *shdr = elf_section(hdr, hdr->e_shnum++); + struct elf_phdr *phdr = elf_program(hdr, hdr->e_phnum++); mdr = &minidump_table.region[region_idx++]; @@ -155,36 +171,21 @@ static int md_update_smem_table(const struct md_region *entry) shdr->sh_addr = (elf_addr_t)entry->virt_addr; shdr->sh_size = mdr->size; shdr->sh_flags = SHF_WRITE; - shdr->sh_offset = elf_offset; + shdr->sh_offset = minidump_elfheader.elf_offset; shdr->sh_entsize = 0; phdr->p_type = PT_LOAD; - phdr->p_offset = elf_offset; + phdr->p_offset = minidump_elfheader.elf_offset; phdr->p_vaddr = entry->virt_addr; phdr->p_paddr = entry->phys_addr; phdr->p_filesz = phdr->p_memsz = mdr->size; phdr->p_flags = PF_R | PF_W; - md_ehdr->e_shnum += 1; - md_ehdr->e_phnum += 1; - elf_offset += shdr->sh_size; - shdr++; - phdr++; + minidump_elfheader.elf_offset += shdr->sh_size; return 0; } -bool msm_minidump_enabled(void) -{ - bool ret; - - spin_lock(&mdt_lock); - ret = minidump_enabled; - spin_unlock(&mdt_lock); - return ret; -} -EXPORT_SYMBOL(msm_minidump_enabled); - int msm_minidump_add_region(const struct md_region *entry) { u32 entries; @@ -196,19 +197,19 @@ int msm_minidump_add_region(const struct md_region *entry) if (((strlen(entry->name) > MAX_NAME_LENGTH) || md_check_name(entry->name)) && !entry->virt_addr) { - pr_info("Invalid entry details\n"); + pr_err("Invalid entry details\n"); return -EINVAL; } if (!IS_ALIGNED(entry->size, 4)) { - pr_info("size should be 4 byte aligned\n"); + pr_err("size should be 4 byte aligned\n"); return -EINVAL; } spin_lock(&mdt_lock); entries = minidump_table.num_regions; if (entries >= MAX_NUM_ENTRIES) { - pr_info("Maximum entries reached.\n"); + pr_err("Maximum entries reached.\n"); spin_unlock(&mdt_lock); return -ENOMEM; } @@ -238,23 +239,32 @@ EXPORT_SYMBOL(msm_minidump_add_region); static int msm_minidump_add_header(void) { struct md_smem_region *mdreg = &minidump_table.region[0]; - char *banner; + struct elfhdr *md_ehdr; + struct elf_shdr *shdr; + struct elf_phdr *phdr; unsigned int strtbl_off, elfh_size, phdr_off; + char *banner; + /* Header buffer contains: + * elf header, MAX_NUM_ENTRIES+1 of section and program elf headers, + * string table section and linux banner. + */ elfh_size = sizeof(*md_ehdr) + MAX_STRTBL_SIZE + MAX_MEM_LENGTH + ((sizeof(*shdr) + sizeof(*phdr)) * (MAX_NUM_ENTRIES + 1)); - md_ehdr = kzalloc(elfh_size, GFP_KERNEL); - if (!md_ehdr) + minidump_elfheader.md_ehdr = kzalloc(elfh_size, GFP_KERNEL); + if (!minidump_elfheader.md_ehdr) return -ENOMEM; strlcpy(mdreg->name, "KELF_HEADER", sizeof(mdreg->name)); - mdreg->address = virt_to_phys(md_ehdr); + mdreg->address = virt_to_phys(minidump_elfheader.md_ehdr); mdreg->size = elfh_size; - /* Section headers*/ - shdr = (struct elf_shdr *)(md_ehdr + 1); - phdr = (struct elf_phdr *)(shdr + MAX_NUM_ENTRIES); + md_ehdr = minidump_elfheader.md_ehdr; + /* Assign section/program headers offset */ + minidump_elfheader.shdr = shdr = (struct elf_shdr *)(md_ehdr + 1); + minidump_elfheader.phdr = phdr = + (struct elf_phdr *)(shdr + MAX_NUM_ENTRIES); phdr_off = sizeof(*md_ehdr) + (sizeof(*shdr) * MAX_NUM_ENTRIES); memcpy(md_ehdr->e_ident, ELFMAG, SELFMAG); @@ -268,18 +278,19 @@ static int msm_minidump_add_header(void) md_ehdr->e_ehsize = sizeof(*md_ehdr); md_ehdr->e_phoff = phdr_off; md_ehdr->e_phentsize = sizeof(*phdr); - md_ehdr->e_phnum = 1; md_ehdr->e_shoff = sizeof(*md_ehdr); md_ehdr->e_shentsize = sizeof(*shdr); - md_ehdr->e_shnum = 3; /* NULL, STR TABLE, Linux banner */ md_ehdr->e_shstrndx = 1; - elf_offset = elfh_size; + minidump_elfheader.elf_offset = elfh_size; + + /* + * First section header should be NULL, + * 2nd section is string table. + */ + minidump_elfheader.strtable_idx = 1; strtbl_off = sizeof(*md_ehdr) + ((sizeof(*phdr) + sizeof(*shdr)) * MAX_NUM_ENTRIES); - /* First section header should be NULL - * 2nd entry for string table - */ shdr++; shdr->sh_type = SHT_STRTAB; shdr->sh_offset = (elf_addr_t)strtbl_off; @@ -289,7 +300,15 @@ static int msm_minidump_add_header(void) shdr->sh_name = set_section_name("STR_TBL"); shdr++; - /* 3rd entry for linux banner */ + /* 3rd section is for minidump_table VA, used by parsers */ + shdr->sh_type = SHT_PROGBITS; + shdr->sh_entsize = 0; + shdr->sh_flags = 0; + shdr->sh_addr = (elf_addr_t)&minidump_table; + shdr->sh_name = set_section_name("minidump_table"); + shdr++; + + /* 4th section is linux banner */ banner = (char *)md_ehdr + strtbl_off + MAX_STRTBL_SIZE; strlcpy(banner, linux_banner, MAX_MEM_LENGTH); @@ -300,7 +319,6 @@ static int msm_minidump_add_header(void) shdr->sh_entsize = 0; shdr->sh_flags = SHF_WRITE; shdr->sh_name = set_section_name("linux_banner"); - shdr++; phdr->p_type = PT_LOAD; phdr->p_offset = (elf_addr_t)(strtbl_off + MAX_STRTBL_SIZE); @@ -309,8 +327,9 @@ static int msm_minidump_add_header(void) phdr->p_filesz = phdr->p_memsz = strlen(linux_banner) + 1; phdr->p_flags = PF_R | PF_W; - md_ehdr->e_phnum += 1; - phdr++; + /* Update headers count*/ + md_ehdr->e_phnum = 1; + md_ehdr->e_shnum = 4; return 0; } @@ -325,13 +344,13 @@ static int __init msm_minidump_init(void) smem_table = smem_get_entry(SMEM_MINIDUMP_TABLE_ID, &size, 0, SMEM_ANY_HOST_FLAG); if (IS_ERR_OR_NULL(smem_table)) { - pr_info("SMEM is not initialized.\n"); + pr_err("SMEM is not initialized.\n"); return -ENODEV; } if ((smem_table->next_avail_offset + MAX_MEM_LENGTH) > smem_table->smem_length) { - pr_info("SMEM memory not available.\n"); + pr_err("SMEM memory not available.\n"); return -ENOMEM; } @@ -353,10 +372,10 @@ static int __init msm_minidump_init(void) for (i = 0; i < pendings; i++) { mdr = &minidump_table.entry[i]; if (md_update_smem_table(mdr)) { - pr_info("Unable to add entry %s to smem table\n", + pr_err("Unable to add entry %s to smem table\n", mdr->name); spin_unlock(&mdt_lock); - return -ENODEV; + return -ENOENT; } } diff --git a/drivers/soc/qcom/perf_event_kryo.c b/drivers/soc/qcom/perf_event_kryo.c index c61a86850777..519961440742 100644 --- a/drivers/soc/qcom/perf_event_kryo.c +++ b/drivers/soc/qcom/perf_event_kryo.c @@ -118,12 +118,7 @@ static void kryo_write_pmresr(int reg, int l_h, u32 val) static u32 kryo_read_pmresr(int reg, int l_h) { - u32 val; - - if (reg > KRYO_MAX_L1_REG) { - pr_err("Invalid read of RESR reg %d\n", reg); - return 0; - } + u32 val = 0; if (l_h == RESR_L) { switch (reg) { @@ -136,6 +131,9 @@ static u32 kryo_read_pmresr(int reg, int l_h) case 2: asm volatile("mrs %0, " pmresr2l_el0 : "=r" (val)); break; + default: + WARN_ONCE(1, "Invalid read of RESR reg %d\n", reg); + break; } } else { switch (reg) { @@ -148,6 +146,9 @@ static u32 kryo_read_pmresr(int reg, int l_h) case 2: asm volatile("mrs %0," pmresr2h_el0 : "=r" (val)); break; + default: + WARN_ONCE(1, "Invalid read of RESR reg %d\n", reg); + break; } } diff --git a/drivers/soc/qcom/peripheral-loader.c b/drivers/soc/qcom/peripheral-loader.c index 6e5ddc4a3a7d..3415338a1294 100644 --- a/drivers/soc/qcom/peripheral-loader.c +++ b/drivers/soc/qcom/peripheral-loader.c @@ -917,13 +917,13 @@ out: priv->region_start), VMID_HLOS); } + if (desc->clear_fw_region && priv->region_start) + pil_clear_segment(desc); dma_free_attrs(desc->dev, priv->region_size, priv->region, priv->region_start, &desc->attrs); priv->region = NULL; } - if (desc->clear_fw_region && priv->region_start) - pil_clear_segment(desc); pil_release_mmap(desc); } return ret; diff --git a/drivers/soc/qcom/qbt1000.c b/drivers/soc/qcom/qbt1000.c index 4ba92436bd06..6e7d34ac9163 100644 --- a/drivers/soc/qcom/qbt1000.c +++ b/drivers/soc/qcom/qbt1000.c @@ -377,6 +377,12 @@ static long qbt1000_ioctl(struct file *file, unsigned cmd, unsigned long arg) drvdata = file->private_data; + if (IS_ERR(priv_arg)) { + dev_err(drvdata->dev, "%s: invalid user space pointer %lu\n", + __func__, arg); + return -EINVAL; + } + mutex_lock(&drvdata->mutex); pr_debug("qbt1000_ioctl %d\n", cmd); @@ -401,6 +407,13 @@ static long qbt1000_ioctl(struct file *file, unsigned cmd, unsigned long arg) goto end; } + if (strcmp(app.name, FP_APP_NAME)) { + dev_err(drvdata->dev, "%s: Invalid app name\n", + __func__); + rc = -EINVAL; + goto end; + } + if (drvdata->app_handle) { dev_err(drvdata->dev, "%s: LOAD app already loaded, unloading first\n", __func__); @@ -414,6 +427,7 @@ static long qbt1000_ioctl(struct file *file, unsigned cmd, unsigned long arg) } pr_debug("app %s load before\n", app.name); + app.name[MAX_NAME_SIZE - 1] = '\0'; /* start the TZ app */ rc = qseecom_start_app( @@ -427,7 +441,8 @@ static long qbt1000_ioctl(struct file *file, unsigned cmd, unsigned long arg) pr_err("App %s failed to set bw\n", app.name); } } else { - pr_err("app %s failed to load\n", app.name); + dev_err(drvdata->dev, "%s: Fingerprint Trusted App failed to load\n", + __func__); goto end; } @@ -447,9 +462,7 @@ static long qbt1000_ioctl(struct file *file, unsigned cmd, unsigned long arg) pr_debug("app %s load after\n", app.name); - if (!strcmp(app.name, FP_APP_NAME)) - drvdata->fp_app_handle = drvdata->app_handle; - + drvdata->fp_app_handle = drvdata->app_handle; break; } case QBT1000_UNLOAD_APP: diff --git a/drivers/soc/qcom/qdsp6v2/apr.c b/drivers/soc/qcom/qdsp6v2/apr.c index 128ea434dcc8..a275537d4e08 100644 --- a/drivers/soc/qcom/qdsp6v2/apr.c +++ b/drivers/soc/qcom/qdsp6v2/apr.c @@ -514,19 +514,19 @@ struct apr_svc *apr_register(char *dest, char *svc_name, apr_fn svc_fn, mutex_unlock(&svc->m_lock); return NULL; } - if (!svc->port_cnt && !svc->svc_cnt) + if (!svc->svc_cnt) clnt->svc_cnt++; svc->port_cnt++; svc->port_fn[temp_port] = svc_fn; svc->port_priv[temp_port] = priv; + svc->svc_cnt++; } else { if (!svc->fn) { - if (!svc->port_cnt && !svc->svc_cnt) + if (!svc->svc_cnt) clnt->svc_cnt++; svc->fn = svc_fn; - if (svc->port_cnt) - svc->svc_cnt++; svc->priv = priv; + svc->svc_cnt++; } } @@ -745,29 +745,28 @@ int apr_deregister(void *handle) if (!handle) return -EINVAL; + if (!svc->svc_cnt) { + pr_err("%s: svc already deregistered. svc = %pK\n", + __func__, svc); + return -EINVAL; + } + mutex_lock(&svc->m_lock); dest_id = svc->dest_id; client_id = svc->client_id; clnt = &client[dest_id][client_id]; - if (svc->port_cnt > 0 || svc->svc_cnt > 0) { + if (svc->svc_cnt > 0) { if (svc->port_cnt) svc->port_cnt--; - else if (svc->svc_cnt) - svc->svc_cnt--; - if (!svc->port_cnt && !svc->svc_cnt) { + svc->svc_cnt--; + if (!svc->svc_cnt) { client[dest_id][client_id].svc_cnt--; - svc->need_reset = 0x0; - } - } else if (client[dest_id][client_id].svc_cnt > 0) { - client[dest_id][client_id].svc_cnt--; - if (!client[dest_id][client_id].svc_cnt) { - svc->need_reset = 0x0; pr_debug("%s: service is reset %pK\n", __func__, svc); } } - if (!svc->port_cnt && !svc->svc_cnt) { + if (!svc->svc_cnt) { svc->priv = NULL; svc->id = 0; svc->fn = NULL; diff --git a/drivers/soc/qcom/scm-boot.c b/drivers/soc/qcom/scm-boot.c index 369fb27ff447..f3e96f9afa12 100644 --- a/drivers/soc/qcom/scm-boot.c +++ b/drivers/soc/qcom/scm-boot.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010, 2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010, 2014, 2016, 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 @@ -24,11 +24,20 @@ int scm_set_boot_addr(phys_addr_t addr, unsigned int flags) u32 flags; u32 addr; } cmd; + struct scm_desc desc = {0}; + + if (!is_scm_armv8()) { + cmd.addr = addr; + cmd.flags = flags; + return scm_call(SCM_SVC_BOOT, SCM_BOOT_ADDR, + &cmd, sizeof(cmd), NULL, 0); + } + + desc.args[0] = addr; + desc.args[1] = flags; + desc.arginfo = SCM_ARGS(2); - cmd.addr = addr; - cmd.flags = flags; - return scm_call(SCM_SVC_BOOT, SCM_BOOT_ADDR, - &cmd, sizeof(cmd), NULL, 0); + return scm_call2(SCM_SIP_FNID(SCM_SVC_BOOT, SCM_BOOT_ADDR), &desc); } EXPORT_SYMBOL(scm_set_boot_addr); diff --git a/drivers/soc/qcom/service-notifier.c b/drivers/soc/qcom/service-notifier.c index 68592feccb33..b5681a5c6817 100644 --- a/drivers/soc/qcom/service-notifier.c +++ b/drivers/soc/qcom/service-notifier.c @@ -376,13 +376,6 @@ static void root_service_service_arrive(struct work_struct *work) mutex_unlock(&qmi_client_release_lock); pr_info("Connection established between QMI handle and %d service\n", data->instance_id); - /* Register for indication messages about service */ - rc = qmi_register_ind_cb(data->clnt_handle, root_service_service_ind_cb, - (void *)data); - if (rc < 0) - pr_err("Indication callback register failed(instance-id: %d) rc:%d\n", - data->instance_id, rc); - mutex_lock(¬if_add_lock); mutex_lock(&service_list_lock); list_for_each_entry(service_notif, &service_list, list) { @@ -405,6 +398,12 @@ static void root_service_service_arrive(struct work_struct *work) } mutex_unlock(&service_list_lock); mutex_unlock(¬if_add_lock); + /* Register for indication messages about service */ + rc = qmi_register_ind_cb(data->clnt_handle, + root_service_service_ind_cb, (void *)data); + if (rc < 0) + pr_err("Indication callback register failed(instance-id: %d) rc:%d\n", + data->instance_id, rc); } static void root_service_service_exit(struct qmi_client_info *data, diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index c1d8748a5d08..b9903fe86f60 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -65,6 +65,7 @@ enum { HW_PLATFORM_RCM = 21, HW_PLATFORM_STP = 23, HW_PLATFORM_SBC = 24, + HW_PLATFORM_ADP = 25, HW_PLATFORM_INVALID }; @@ -85,6 +86,7 @@ const char *hw_platform[] = { [HW_PLATFORM_DTV] = "DTV", [HW_PLATFORM_STP] = "STP", [HW_PLATFORM_SBC] = "SBC", + [HW_PLATFORM_ADP] = "ADP", }; enum { @@ -111,6 +113,22 @@ const char *qrd_hw_platform_subtype[] = { }; enum { + PLATFORM_SUBTYPE_MOJAVE_V1 = 0x0, + PLATFORM_SUBTYPE_MMX = 0x1, + PLATFORM_SUBTYPE_MOJAVE_FULL_V2 = 0x2, + PLATFORM_SUBTYPE_MOJAVE_BARE_V2 = 0x3, + PLATFORM_SUBTYPE_ADP_INVALID, +}; + +const char *adp_hw_platform_subtype[] = { + [PLATFORM_SUBTYPE_MOJAVE_V1] = "MOJAVE_V1", + [PLATFORM_SUBTYPE_MMX] = "MMX", + [PLATFORM_SUBTYPE_MOJAVE_FULL_V2] = "_MOJAVE_V2_FULL", + [PLATFORM_SUBTYPE_MOJAVE_BARE_V2] = "_MOJAVE_V2_BARE", + [PLATFORM_SUBTYPE_ADP_INVALID] = "INVALID", +}; + +enum { PLATFORM_SUBTYPE_UNKNOWN = 0x0, PLATFORM_SUBTYPE_CHARM = 0x1, PLATFORM_SUBTYPE_STRANGE = 0x2, @@ -514,11 +532,13 @@ static struct msm_soc_info cpu_of_id[] = { /* 8996 IDs */ [246] = {MSM_CPU_8996, "MSM8996"}, - [310] = {MSM_CPU_8996, "MSM8996"}, - [311] = {MSM_CPU_8996, "APQ8096"}, [291] = {MSM_CPU_8996, "APQ8096"}, [305] = {MSM_CPU_8996, "MSM8996pro"}, + [310] = {MSM_CPU_8996, "MSM8996"}, + [311] = {MSM_CPU_8996, "APQ8096"}, [312] = {MSM_CPU_8996, "APQ8096pro"}, + [315] = {MSM_CPU_8996, "MSM8996pro"}, + [316] = {MSM_CPU_8996, "APQ8096pro"}, /* 8976 ID */ [266] = {MSM_CPU_8976, "MSM8976"}, @@ -804,6 +824,14 @@ msm_get_platform_subtype(struct device *dev, } return snprintf(buf, PAGE_SIZE, "%-.32s\n", qrd_hw_platform_subtype[hw_subtype]); + } + if (socinfo_get_platform_type() == HW_PLATFORM_ADP) { + if (hw_subtype >= PLATFORM_SUBTYPE_ADP_INVALID) { + pr_err("Invalid hardware platform sub type for adp found\n"); + hw_subtype = PLATFORM_SUBTYPE_ADP_INVALID; + } + return snprintf(buf, PAGE_SIZE, "%-.32s\n", + adp_hw_platform_subtype[hw_subtype]); } else { if (hw_subtype >= PLATFORM_SUBTYPE_INVALID) { pr_err("Invalid hardware platform subtype\n"); @@ -1225,10 +1253,6 @@ static void * __init setup_dummy_socinfo(void) dummy_socinfo.id = 246; strlcpy(dummy_socinfo.build_id, "msm8996 - ", sizeof(dummy_socinfo.build_id)); - } else if (early_machine_is_msm8996_auto()) { - dummy_socinfo.id = 310; - strlcpy(dummy_socinfo.build_id, "msm8996-auto - ", - sizeof(dummy_socinfo.build_id)); } else if (early_machine_is_msm8929()) { dummy_socinfo.id = 268; strlcpy(dummy_socinfo.build_id, "msm8929 - ", diff --git a/drivers/soc/qcom/subsystem_restart.c b/drivers/soc/qcom/subsystem_restart.c index d3d0b8594c9f..51f4ec79db10 100644 --- a/drivers/soc/qcom/subsystem_restart.c +++ b/drivers/soc/qcom/subsystem_restart.c @@ -480,15 +480,19 @@ static void send_sysmon_notif(struct subsys_device *dev) mutex_unlock(&subsys_list_lock); } -static void for_each_subsys_device(struct subsys_device **list, unsigned count, - void *data, void (*fn)(struct subsys_device *, void *)) +static int for_each_subsys_device(struct subsys_device **list, unsigned count, + void *data, int (*fn)(struct subsys_device *, void *)) { + int ret; while (count--) { struct subsys_device *dev = *list++; if (!dev) continue; - fn(dev, data); + ret = fn(dev, data); + if (ret) + return ret; } + return 0; } static void notify_each_subsys_device(struct subsys_device **list, @@ -590,21 +594,31 @@ static int wait_for_err_ready(struct subsys_device *subsys) return 0; } -static void subsystem_shutdown(struct subsys_device *dev, void *data) +static int subsystem_shutdown(struct subsys_device *dev, void *data) { const char *name = dev->desc->name; + int ret; pr_info("[%s:%d]: Shutting down %s\n", current->comm, current->pid, name); - if (dev->desc->shutdown(dev->desc, true) < 0) - panic("subsys-restart: [%s:%d]: Failed to shutdown %s!", - current->comm, current->pid, name); + ret = dev->desc->shutdown(dev->desc, true); + if (ret < 0) { + if (!dev->desc->ignore_ssr_failure) { + panic("subsys-restart: [%s:%d]: Failed to shutdown %s!", + current->comm, current->pid, name); + } else { + pr_err("Shutdown failure on %s\n", name); + return ret; + } + } dev->crash_count++; subsys_set_state(dev, SUBSYS_OFFLINE); disable_all_irqs(dev); + + return 0; } -static void subsystem_ramdump(struct subsys_device *dev, void *data) +static int subsystem_ramdump(struct subsys_device *dev, void *data) { const char *name = dev->desc->name; @@ -613,15 +627,17 @@ static void subsystem_ramdump(struct subsys_device *dev, void *data) pr_warn("%s[%s:%d]: Ramdump failed.\n", name, current->comm, current->pid); dev->do_ramdump_on_put = false; + return 0; } -static void subsystem_free_memory(struct subsys_device *dev, void *data) +static int subsystem_free_memory(struct subsys_device *dev, void *data) { if (dev->desc->free_memory) dev->desc->free_memory(dev->desc); + return 0; } -static void subsystem_powerup(struct subsys_device *dev, void *data) +static int subsystem_powerup(struct subsys_device *dev, void *data) { const char *name = dev->desc->name; int ret; @@ -629,11 +645,17 @@ static void subsystem_powerup(struct subsys_device *dev, void *data) pr_info("[%s:%d]: Powering up %s\n", current->comm, current->pid, name); init_completion(&dev->err_ready); - if (dev->desc->powerup(dev->desc) < 0) { + ret = dev->desc->powerup(dev->desc); + if (ret < 0) { notify_each_subsys_device(&dev, 1, SUBSYS_POWERUP_FAILURE, NULL); - panic("[%s:%d]: Powerup error: %s!", - current->comm, current->pid, name); + if (!dev->desc->ignore_ssr_failure) { + panic("[%s:%d]: Powerup error: %s!", + current->comm, current->pid, name); + } else { + pr_err("Powerup failure on %s\n", name); + return ret; + } } enable_all_irqs(dev); @@ -641,11 +663,16 @@ static void subsystem_powerup(struct subsys_device *dev, void *data) if (ret) { notify_each_subsys_device(&dev, 1, SUBSYS_POWERUP_FAILURE, NULL); - panic("[%s:%d]: Timed out waiting for error ready: %s!", - current->comm, current->pid, name); + if (!dev->desc->ignore_ssr_failure) + panic("[%s:%d]: Timed out waiting for error ready: %s!", + current->comm, current->pid, name); + else + return ret; } subsys_set_state(dev, SUBSYS_ONLINE); subsys_set_crash_status(dev, CRASH_STATUS_NO_CRASH); + + return 0; } static int __find_subsys(struct device *dev, void *data) @@ -907,6 +934,7 @@ static void subsystem_restart_wq_func(struct work_struct *work) struct subsys_tracking *track; unsigned count; unsigned long flags; + int ret; /* * It's OK to not take the registration lock at this point. @@ -954,7 +982,9 @@ static void subsystem_restart_wq_func(struct work_struct *work) pr_debug("[%s:%d]: Starting restart sequence for %s\n", current->comm, current->pid, desc->name); notify_each_subsys_device(list, count, SUBSYS_BEFORE_SHUTDOWN, NULL); - for_each_subsys_device(list, count, NULL, subsystem_shutdown); + ret = for_each_subsys_device(list, count, NULL, subsystem_shutdown); + if (ret) + goto err; notify_each_subsys_device(list, count, SUBSYS_AFTER_SHUTDOWN, NULL); notify_each_subsys_device(list, count, SUBSYS_RAMDUMP_NOTIFICATION, @@ -970,12 +1000,19 @@ static void subsystem_restart_wq_func(struct work_struct *work) for_each_subsys_device(list, count, NULL, subsystem_free_memory); notify_each_subsys_device(list, count, SUBSYS_BEFORE_POWERUP, NULL); - for_each_subsys_device(list, count, NULL, subsystem_powerup); + ret = for_each_subsys_device(list, count, NULL, subsystem_powerup); + if (ret) + goto err; notify_each_subsys_device(list, count, SUBSYS_AFTER_POWERUP, NULL); pr_info("[%s:%d]: Restart sequence for %s completed.\n", current->comm, current->pid, desc->name); +err: + /* Reset subsys count */ + if (ret) + dev->count = 0; + mutex_unlock(&soc_order_reg_lock); mutex_unlock(&track->lock); @@ -1466,6 +1503,9 @@ static int subsys_parse_devicetree(struct subsys_desc *desc) desc->generic_irq = ret; } + desc->ignore_ssr_failure = of_property_read_bool(pdev->dev.of_node, + "qcom,ignore-ssr-failure"); + order = ssr_parse_restart_orders(desc); if (IS_ERR(order)) { pr_err("Could not initialize SSR restart order, err = %ld\n", diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c index 3f1133230a1a..28c9afe538ca 100644 --- a/drivers/staging/android/ashmem.c +++ b/drivers/staging/android/ashmem.c @@ -392,6 +392,7 @@ static int ashmem_mmap(struct file *file, struct vm_area_struct *vma) ret = PTR_ERR(vmfile); goto out; } + vmfile->f_mode |= FMODE_LSEEK; asma->file = vmfile; } get_file(asma->file); @@ -752,10 +753,12 @@ static long ashmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; case ASHMEM_SET_SIZE: ret = -EINVAL; + mutex_lock(&ashmem_mutex); if (!asma->file) { ret = 0; asma->size = (size_t)arg; } + mutex_unlock(&ashmem_mutex); break; case ASHMEM_GET_SIZE: ret = asma->size; diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c index 58bf3d2f52bd..bfb7dd2d920d 100644 --- a/drivers/staging/android/ion/ion.c +++ b/drivers/staging/android/ion/ion.c @@ -3,7 +3,7 @@ * drivers/staging/android/ion/ion.c * * Copyright (C) 2011 Google, Inc. - * Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -122,8 +122,6 @@ struct ion_handle { int id; }; -static struct ion_device *ion_dev; - bool ion_buffer_fault_user_mappings(struct ion_buffer *buffer) { return (buffer->flags & ION_FLAG_CACHED) && @@ -844,32 +842,45 @@ void ion_unmap_kernel(struct ion_client *client, struct ion_handle *handle) } EXPORT_SYMBOL(ion_unmap_kernel); -static int ion_debug_client_show(struct seq_file *s, void *unused) +static struct mutex debugfs_mutex; +static struct rb_root *ion_root_client; +static int is_client_alive(struct ion_client *client) { - struct ion_client *client = s->private; - struct rb_node *n, *cnode; - bool found = false; - - down_write(&ion_dev->lock); + struct rb_node *node; + struct ion_client *tmp; + struct ion_device *dev; - if (!client || (client->dev != ion_dev)) { - up_write(&ion_dev->lock); - return -EINVAL; - } + node = ion_root_client->rb_node; + dev = container_of(ion_root_client, struct ion_device, clients); - cnode = rb_first(&ion_dev->clients); - for ( ; cnode; cnode = rb_next(cnode)) { - struct ion_client *c = rb_entry(cnode, - struct ion_client, node); - if (client == c) { - found = true; - break; + down_read(&dev->lock); + while (node) { + tmp = rb_entry(node, struct ion_client, node); + if (client < tmp) { + node = node->rb_left; + } else if (client > tmp) { + node = node->rb_right; + } else { + up_read(&dev->lock); + return 1; } } - if (!found) { - up_write(&ion_dev->lock); - return -EINVAL; + up_read(&dev->lock); + return 0; +} + +static int ion_debug_client_show(struct seq_file *s, void *unused) +{ + struct ion_client *client = s->private; + struct rb_node *n; + + mutex_lock(&debugfs_mutex); + if (!is_client_alive(client)) { + seq_printf(s, "ion_client 0x%pK dead, can't dump its buffers\n", + client); + mutex_unlock(&debugfs_mutex); + return 0; } seq_printf(s, "%16.16s: %16.16s : %16.16s : %12.12s\n", @@ -890,7 +901,7 @@ static int ion_debug_client_show(struct seq_file *s, void *unused) seq_printf(s, "\n"); } mutex_unlock(&client->lock); - up_write(&ion_dev->lock); + mutex_unlock(&debugfs_mutex); return 0; } @@ -1021,7 +1032,7 @@ void ion_client_destroy(struct ion_client *client) struct rb_node *n; pr_debug("%s: %d\n", __func__, __LINE__); - mutex_lock(&client->lock); + mutex_lock(&debugfs_mutex); while ((n = rb_first(&client->handles))) { struct ion_handle *handle = rb_entry(n, struct ion_handle, node); @@ -1029,7 +1040,6 @@ void ion_client_destroy(struct ion_client *client) } idr_destroy(&client->idr); - mutex_unlock(&client->lock); down_write(&dev->lock); if (client->task) @@ -1042,6 +1052,7 @@ void ion_client_destroy(struct ion_client *client) kfree(client->display_name); kfree(client->name); kfree(client); + mutex_unlock(&debugfs_mutex); } EXPORT_SYMBOL(ion_client_destroy); @@ -1838,7 +1849,7 @@ static int ion_debug_heap_show(struct seq_file *s, void *unused) seq_printf(s, "%16s %16s %16s\n", "client", "pid", "size"); seq_puts(s, "----------------------------------------------------\n"); - down_read(&dev->lock); + mutex_lock(&debugfs_mutex); for (n = rb_first(&dev->clients); n; n = rb_next(n)) { struct ion_client *client = rb_entry(n, struct ion_client, node); @@ -1857,7 +1868,8 @@ static int ion_debug_heap_show(struct seq_file *s, void *unused) client->pid, size); } } - up_read(&dev->lock); + mutex_unlock(&debugfs_mutex); + seq_puts(s, "----------------------------------------------------\n"); seq_puts(s, "orphaned allocations (info is from last known client):\n"); mutex_lock(&dev->buffer_lock); @@ -2095,7 +2107,8 @@ debugfs_done: init_rwsem(&idev->lock); plist_head_init(&idev->heaps); idev->clients = RB_ROOT; - ion_dev = idev; + ion_root_client = &idev->clients; + mutex_init(&debugfs_mutex); return idev; } EXPORT_SYMBOL(ion_device_create); diff --git a/drivers/target/iscsi/iscsi_target_parameters.c b/drivers/target/iscsi/iscsi_target_parameters.c index 2cbea2af7cd0..6d1b0acbc5b3 100644 --- a/drivers/target/iscsi/iscsi_target_parameters.c +++ b/drivers/target/iscsi/iscsi_target_parameters.c @@ -781,22 +781,6 @@ static void iscsi_check_proposer_for_optional_reply(struct iscsi_param *param) if (!strcmp(param->name, MAXRECVDATASEGMENTLENGTH)) SET_PSTATE_REPLY_OPTIONAL(param); /* - * The GlobalSAN iSCSI Initiator for MacOSX does - * not respond to MaxBurstLength, FirstBurstLength, - * DefaultTime2Wait or DefaultTime2Retain parameter keys. - * So, we set them to 'reply optional' here, and assume the - * the defaults from iscsi_parameters.h if the initiator - * is not RFC compliant and the keys are not negotiated. - */ - if (!strcmp(param->name, MAXBURSTLENGTH)) - SET_PSTATE_REPLY_OPTIONAL(param); - if (!strcmp(param->name, FIRSTBURSTLENGTH)) - SET_PSTATE_REPLY_OPTIONAL(param); - if (!strcmp(param->name, DEFAULTTIME2WAIT)) - SET_PSTATE_REPLY_OPTIONAL(param); - if (!strcmp(param->name, DEFAULTTIME2RETAIN)) - SET_PSTATE_REPLY_OPTIONAL(param); - /* * Required for gPXE iSCSI boot client */ if (!strcmp(param->name, MAXCONNECTIONS)) diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c index 428b0d9e3dba..93590521ae33 100644 --- a/drivers/target/iscsi/iscsi_target_util.c +++ b/drivers/target/iscsi/iscsi_target_util.c @@ -731,21 +731,23 @@ void iscsit_free_cmd(struct iscsi_cmd *cmd, bool shutdown) { struct se_cmd *se_cmd = NULL; int rc; + bool op_scsi = false; /* * Determine if a struct se_cmd is associated with * this struct iscsi_cmd. */ switch (cmd->iscsi_opcode) { case ISCSI_OP_SCSI_CMD: - se_cmd = &cmd->se_cmd; - __iscsit_free_cmd(cmd, true, shutdown); + op_scsi = true; /* * Fallthrough */ case ISCSI_OP_SCSI_TMFUNC: - rc = transport_generic_free_cmd(&cmd->se_cmd, shutdown); - if (!rc && shutdown && se_cmd && se_cmd->se_sess) { - __iscsit_free_cmd(cmd, true, shutdown); + se_cmd = &cmd->se_cmd; + __iscsit_free_cmd(cmd, op_scsi, shutdown); + rc = transport_generic_free_cmd(se_cmd, shutdown); + if (!rc && shutdown && se_cmd->se_sess) { + __iscsit_free_cmd(cmd, op_scsi, shutdown); target_put_sess_cmd(se_cmd); } break; diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c index de18790eb21c..d72a4058fd08 100644 --- a/drivers/target/target_core_pscsi.c +++ b/drivers/target/target_core_pscsi.c @@ -154,7 +154,7 @@ static void pscsi_tape_read_blocksize(struct se_device *dev, buf = kzalloc(12, GFP_KERNEL); if (!buf) - return; + goto out_free; memset(cdb, 0, MAX_COMMAND_SIZE); cdb[0] = MODE_SENSE; @@ -169,9 +169,10 @@ static void pscsi_tape_read_blocksize(struct se_device *dev, * If MODE_SENSE still returns zero, set the default value to 1024. */ sdev->sector_size = (buf[9] << 16) | (buf[10] << 8) | (buf[11]); +out_free: if (!sdev->sector_size) sdev->sector_size = 1024; -out_free: + kfree(buf); } @@ -314,9 +315,10 @@ static int pscsi_add_device_to_list(struct se_device *dev, sd->lun, sd->queue_depth); } - dev->dev_attrib.hw_block_size = sd->sector_size; + dev->dev_attrib.hw_block_size = + min_not_zero((int)sd->sector_size, 512); dev->dev_attrib.hw_max_sectors = - min_t(int, sd->host->max_sectors, queue_max_hw_sectors(q)); + min_not_zero(sd->host->max_sectors, queue_max_hw_sectors(q)); dev->dev_attrib.hw_queue_depth = sd->queue_depth; /* @@ -339,8 +341,10 @@ static int pscsi_add_device_to_list(struct se_device *dev, /* * For TYPE_TAPE, attempt to determine blocksize with MODE_SENSE. */ - if (sd->type == TYPE_TAPE) + if (sd->type == TYPE_TAPE) { pscsi_tape_read_blocksize(dev, sd); + dev->dev_attrib.hw_block_size = sd->sector_size; + } return 0; } @@ -406,7 +410,7 @@ static int pscsi_create_type_disk(struct se_device *dev, struct scsi_device *sd) /* * Called with struct Scsi_Host->host_lock called. */ -static int pscsi_create_type_rom(struct se_device *dev, struct scsi_device *sd) +static int pscsi_create_type_nondisk(struct se_device *dev, struct scsi_device *sd) __releases(sh->host_lock) { struct pscsi_hba_virt *phv = dev->se_hba->hba_ptr; @@ -433,28 +437,6 @@ static int pscsi_create_type_rom(struct se_device *dev, struct scsi_device *sd) return 0; } -/* - * Called with struct Scsi_Host->host_lock called. - */ -static int pscsi_create_type_other(struct se_device *dev, - struct scsi_device *sd) - __releases(sh->host_lock) -{ - struct pscsi_hba_virt *phv = dev->se_hba->hba_ptr; - struct Scsi_Host *sh = sd->host; - int ret; - - spin_unlock_irq(sh->host_lock); - ret = pscsi_add_device_to_list(dev, sd); - if (ret) - return ret; - - pr_debug("CORE_PSCSI[%d] - Added Type: %s for %d:%d:%d:%llu\n", - phv->phv_host_id, scsi_device_type(sd->type), sh->host_no, - sd->channel, sd->id, sd->lun); - return 0; -} - static int pscsi_configure_device(struct se_device *dev) { struct se_hba *hba = dev->se_hba; @@ -542,11 +524,8 @@ static int pscsi_configure_device(struct se_device *dev) case TYPE_DISK: ret = pscsi_create_type_disk(dev, sd); break; - case TYPE_ROM: - ret = pscsi_create_type_rom(dev, sd); - break; default: - ret = pscsi_create_type_other(dev, sd); + ret = pscsi_create_type_nondisk(dev, sd); break; } @@ -611,8 +590,7 @@ static void pscsi_free_device(struct se_device *dev) else if (pdv->pdv_lld_host) scsi_host_put(pdv->pdv_lld_host); - if ((sd->type == TYPE_DISK) || (sd->type == TYPE_ROM)) - scsi_device_put(sd); + scsi_device_put(sd); pdv->pdv_sd = NULL; } @@ -1088,7 +1066,6 @@ static sector_t pscsi_get_blocks(struct se_device *dev) if (pdv->pdv_bd && pdv->pdv_bd->bd_part) return pdv->pdv_bd->bd_part->nr_sects; - dump_stack(); return 0; } diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index 2e27b1034ede..90c5dffc9fa4 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -1096,9 +1096,15 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops) return ret; break; case VERIFY: + case VERIFY_16: size = 0; - sectors = transport_get_sectors_10(cdb); - cmd->t_task_lba = transport_lba_32(cdb); + if (cdb[0] == VERIFY) { + sectors = transport_get_sectors_10(cdb); + cmd->t_task_lba = transport_lba_32(cdb); + } else { + sectors = transport_get_sectors_16(cdb); + cmd->t_task_lba = transport_lba_64(cdb); + } cmd->execute_cmd = sbc_emulate_noop; goto check_lba; case REZERO_UNIT: diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c index 80f9de907563..5cc80b80c82b 100644 --- a/drivers/tty/nozomi.c +++ b/drivers/tty/nozomi.c @@ -823,7 +823,7 @@ static int receive_data(enum port_type index, struct nozomi *dc) struct tty_struct *tty = tty_port_tty_get(&port->port); int i, ret; - read_mem32((u32 *) &size, addr, 4); + size = __le32_to_cpu(readl(addr)); /* DBG1( "%d bytes port: %d", size, index); */ if (tty && test_bit(TTY_THROTTLED, &tty->flags)) { diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 5b24ffd93649..83ff1724ec79 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -57,6 +57,7 @@ struct serial_private { unsigned int nr; void __iomem *remapped_bar[PCI_NUM_BAR_RESOURCES]; struct pci_serial_quirk *quirk; + const struct pciserial_board *board; int line[0]; }; @@ -4058,6 +4059,7 @@ pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board) } } priv->nr = i; + priv->board = board; return priv; err_deinit: @@ -4068,7 +4070,7 @@ err_out: } EXPORT_SYMBOL_GPL(pciserial_init_ports); -void pciserial_remove_ports(struct serial_private *priv) +void pciserial_detach_ports(struct serial_private *priv) { struct pci_serial_quirk *quirk; int i; @@ -4088,7 +4090,11 @@ void pciserial_remove_ports(struct serial_private *priv) quirk = find_quirk(priv->dev); if (quirk->exit) quirk->exit(priv->dev); +} +void pciserial_remove_ports(struct serial_private *priv) +{ + pciserial_detach_ports(priv); kfree(priv); } EXPORT_SYMBOL_GPL(pciserial_remove_ports); @@ -5819,7 +5825,7 @@ static pci_ers_result_t serial8250_io_error_detected(struct pci_dev *dev, return PCI_ERS_RESULT_DISCONNECT; if (priv) - pciserial_suspend_ports(priv); + pciserial_detach_ports(priv); pci_disable_device(dev); @@ -5844,9 +5850,18 @@ static pci_ers_result_t serial8250_io_slot_reset(struct pci_dev *dev) static void serial8250_io_resume(struct pci_dev *dev) { struct serial_private *priv = pci_get_drvdata(dev); + const struct pciserial_board *board; - if (priv) - pciserial_resume_ports(priv); + if (!priv) + return; + + board = priv->board; + kfree(priv); + priv = pciserial_init_ports(dev, board); + + if (!IS_ERR(priv)) { + pci_set_drvdata(dev, priv); + } } static const struct pci_error_handlers serial8250_err_handler = { diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index a0f911641b04..53e4d5056db7 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -810,6 +810,11 @@ static void atmel_complete_tx_dma(void *arg) */ if (!uart_circ_empty(xmit)) tasklet_schedule(&atmel_port->tasklet); + else if ((port->rs485.flags & SER_RS485_ENABLED) && + !(port->rs485.flags & SER_RS485_RX_DURING_TX)) { + /* DMA done, stop TX, start RX for RS485 */ + atmel_start_rx(port); + } spin_unlock_irqrestore(&port->lock, flags); } @@ -912,12 +917,6 @@ static void atmel_tx_dma(struct uart_port *port) desc->callback = atmel_complete_tx_dma; desc->callback_param = atmel_port; atmel_port->cookie_tx = dmaengine_submit(desc); - - } else { - if (port->rs485.flags & SER_RS485_ENABLED) { - /* DMA done, stop TX, start RX for RS485 */ - atmel_start_rx(port); - } } if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) @@ -1987,6 +1986,11 @@ static void atmel_flush_buffer(struct uart_port *port) atmel_uart_writel(port, ATMEL_PDC_TCR, 0); atmel_port->pdc_tx.ofs = 0; } + /* + * in uart_flush_buffer(), the xmit circular buffer has just + * been cleared, so we have to reset tx_len accordingly. + */ + atmel_port->tx_len = 0; } /* @@ -2499,6 +2503,9 @@ static void atmel_console_write(struct console *co, const char *s, u_int count) pdc_tx = atmel_uart_readl(port, ATMEL_PDC_PTSR) & ATMEL_PDC_TXTEN; atmel_uart_writel(port, ATMEL_PDC_PTCR, ATMEL_PDC_TXTDIS); + /* Make sure that tx path is actually able to send characters */ + atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN); + uart_console_write(port, s, count, atmel_console_putchar); /* diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index db329230c7ca..efaac5eb6592 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -303,15 +303,17 @@ static void msm_request_tx_dma(struct msm_port *msm_port, resource_size_t base) struct device *dev = msm_port->uart.dev; struct dma_slave_config conf; struct msm_dma *dma; + struct dma_chan *dma_chan; u32 crci = 0; int ret; dma = &msm_port->tx_dma; /* allocate DMA resources, if available */ - dma->chan = dma_request_slave_channel_reason(dev, "tx"); - if (IS_ERR(dma->chan)) + dma_chan = dma_request_slave_channel_reason(dev, "tx"); + if (IS_ERR(dma_chan)) goto no_tx; + dma->chan = dma_chan; of_property_read_u32(dev->of_node, "qcom,tx-crci", &crci); @@ -346,15 +348,17 @@ static void msm_request_rx_dma(struct msm_port *msm_port, resource_size_t base) struct device *dev = msm_port->uart.dev; struct dma_slave_config conf; struct msm_dma *dma; + struct dma_chan *dma_chan; u32 crci = 0; int ret; dma = &msm_port->rx_dma; /* allocate DMA resources, if available */ - dma->chan = dma_request_slave_channel_reason(dev, "rx"); - if (IS_ERR(dma->chan)) + dma_chan = dma_request_slave_channel_reason(dev, "rx"); + if (IS_ERR(dma_chan)) goto no_rx; + dma->chan = dma_chan; of_property_read_u32(dev->of_node, "qcom,rx-crci", &crci); @@ -1166,26 +1170,17 @@ static int msm_startup(struct uart_port *port) snprintf(msm_port->name, sizeof(msm_port->name), "msm_serial%d", port->line); - ret = request_irq(port->irq, msm_uart_irq, IRQF_TRIGGER_HIGH, - msm_port->name, port); - if (unlikely(ret)) - return ret; - /* * UART clk must be kept enabled to * avoid losing received character */ ret = clk_prepare_enable(msm_port->clk); - if (ret) { - goto err_clk; + if (ret) return ret; - } ret = clk_prepare_enable(msm_port->pclk); - if (ret) { + if (ret) goto err_pclk; - return ret; - } msm_serial_set_mnd_regs(port); @@ -1213,12 +1208,21 @@ static int msm_startup(struct uart_port *port) msm_request_rx_dma(msm_port, msm_port->uart.mapbase); } + ret = request_irq(port->irq, msm_uart_irq, IRQF_TRIGGER_HIGH, + msm_port->name, port); + if (unlikely(ret)) + goto err_irq; + return 0; +err_irq: + if (msm_port->is_uartdm) + msm_release_dma(msm_port); + + clk_disable_unprepare(msm_port->pclk); + err_pclk: clk_disable_unprepare(msm_port->clk); -err_clk: - free_irq(port->irq, port); return ret; } diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index deaddb950c20..24337ac3323f 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -1105,7 +1105,7 @@ static int usbtmc_probe(struct usb_interface *intf, dev_dbg(&intf->dev, "%s called\n", __func__); - data = kmalloc(sizeof(*data), GFP_KERNEL); + data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -1163,6 +1163,12 @@ static int usbtmc_probe(struct usb_interface *intf, } } + if (!data->bulk_out || !data->bulk_in) { + dev_err(&intf->dev, "bulk endpoints not found\n"); + retcode = -ENODEV; + goto err_put; + } + retcode = get_capabilities(data); if (retcode) dev_err(&intf->dev, "can't read capabilities\n"); @@ -1186,6 +1192,7 @@ static int usbtmc_probe(struct usb_interface *intf, error_register: sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp); sysfs_remove_group(&intf->dev.kobj, &data_attr_grp); +err_put: kref_put(&data->kref, usbtmc_delete); return retcode; } diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index ac30a051ad71..325cbc9c35d8 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -246,6 +246,16 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, /* * Adjust bInterval for quirked devices. + */ + /* + * This quirk fixes bIntervals reported in ms. + */ + if (to_usb_device(ddev)->quirks & + USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL) { + n = clamp(fls(d->bInterval) + 3, i, j); + i = j = n; + } + /* * This quirk fixes bIntervals reported in * linear microframes. */ diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 9a3bf5e2977f..aa00bb51940b 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -499,8 +499,10 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) */ tbuf_size = max_t(u16, sizeof(struct usb_hub_descriptor), wLength); tbuf = kzalloc(tbuf_size, GFP_KERNEL); - if (!tbuf) - return -ENOMEM; + if (!tbuf) { + status = -ENOMEM; + goto err_alloc; + } bufp = tbuf; @@ -705,6 +707,7 @@ error: } kfree(tbuf); + err_alloc: /* any errors get returned through the urb completion */ spin_lock_irq(&hcd_root_hub_lock); @@ -966,7 +969,7 @@ static void usb_bus_init (struct usb_bus *bus) bus->bandwidth_allocated = 0; bus->bandwidth_int_reqs = 0; bus->bandwidth_isoc_reqs = 0; - mutex_init(&bus->usb_address0_mutex); + mutex_init(&bus->devnum_next_mutex); INIT_LIST_HEAD (&bus->bus_list); } @@ -2555,6 +2558,14 @@ struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver, return NULL; } if (primary_hcd == NULL) { + hcd->address0_mutex = kmalloc(sizeof(*hcd->address0_mutex), + GFP_KERNEL); + if (!hcd->address0_mutex) { + kfree(hcd); + dev_dbg(dev, "hcd address0 mutex alloc failed\n"); + return NULL; + } + mutex_init(hcd->address0_mutex); hcd->bandwidth_mutex = kmalloc(sizeof(*hcd->bandwidth_mutex), GFP_KERNEL); if (!hcd->bandwidth_mutex) { @@ -2566,6 +2577,7 @@ struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver, dev_set_drvdata(dev, hcd); } else { mutex_lock(&usb_port_peer_mutex); + hcd->address0_mutex = primary_hcd->address0_mutex; hcd->bandwidth_mutex = primary_hcd->bandwidth_mutex; hcd->primary_hcd = primary_hcd; primary_hcd->primary_hcd = primary_hcd; @@ -2622,24 +2634,23 @@ EXPORT_SYMBOL_GPL(usb_create_hcd); * Don't deallocate the bandwidth_mutex until the last shared usb_hcd is * deallocated. * - * Make sure to only deallocate the bandwidth_mutex when the primary HCD is - * freed. When hcd_release() is called for either hcd in a peer set - * invalidate the peer's ->shared_hcd and ->primary_hcd pointers to - * block new peering attempts + * Make sure to deallocate the bandwidth_mutex only when the last HCD is + * freed. When hcd_release() is called for either hcd in a peer set, + * invalidate the peer's ->shared_hcd and ->primary_hcd pointers. */ static void hcd_release(struct kref *kref) { struct usb_hcd *hcd = container_of (kref, struct usb_hcd, kref); mutex_lock(&usb_port_peer_mutex); - if (hcd->primary_hcd == hcd) - kfree(hcd->bandwidth_mutex); if (hcd->shared_hcd) { struct usb_hcd *peer = hcd->shared_hcd; peer->shared_hcd = NULL; - if (peer->primary_hcd == hcd) - peer->primary_hcd = NULL; + peer->primary_hcd = NULL; + } else { + kfree(hcd->address0_mutex); + kfree(hcd->bandwidth_mutex); } mutex_unlock(&usb_port_peer_mutex); kfree(hcd); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index f84ef04284f5..87912ead87b7 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1991,7 +1991,7 @@ static void choose_devnum(struct usb_device *udev) struct usb_bus *bus = udev->bus; /* be safe when more hub events are proceed in parallel */ - mutex_lock(&bus->usb_address0_mutex); + mutex_lock(&bus->devnum_next_mutex); if (udev->wusb) { devnum = udev->portnum + 1; BUG_ON(test_bit(devnum, bus->devmap.devicemap)); @@ -2009,7 +2009,7 @@ static void choose_devnum(struct usb_device *udev) set_bit(devnum, bus->devmap.devicemap); udev->devnum = devnum; } - mutex_unlock(&bus->usb_address0_mutex); + mutex_unlock(&bus->devnum_next_mutex); } static void release_devnum(struct usb_device *udev) @@ -2613,8 +2613,15 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, if (ret < 0) return ret; - /* The port state is unknown until the reset completes. */ - if (!(portstatus & USB_PORT_STAT_RESET)) + /* + * The port state is unknown until the reset completes. + * + * On top of that, some chips may require additional time + * to re-establish a connection after the reset is complete, + * so also wait for the connection to be re-established. + */ + if (!(portstatus & USB_PORT_STAT_RESET) && + (portstatus & USB_PORT_STAT_CONNECTION)) break; /* switch to the long delay after two short delay failures */ @@ -4212,7 +4219,7 @@ static void hub_set_initial_usb2_lpm_policy(struct usb_device *udev) struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent); int connect_type = USB_PORT_CONNECT_TYPE_UNKNOWN; - if (!udev->usb2_hw_lpm_capable) + if (!udev->usb2_hw_lpm_capable || !udev->bos) return; if (hub) @@ -4275,7 +4282,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, if (oldspeed == USB_SPEED_LOW) delay = HUB_LONG_RESET_TIME; - mutex_lock(&hdev->bus->usb_address0_mutex); + mutex_lock(hcd->address0_mutex); /* Reset the device; full speed may morph to high speed */ /* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */ @@ -4561,7 +4568,7 @@ fail: hub_port_disable(hub, port1, 0); update_devnum(udev, devnum); /* for disconnect processing */ } - mutex_unlock(&hdev->bus->usb_address0_mutex); + mutex_unlock(hcd->address0_mutex); return retval; } diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 24f9f98968a5..96b21b0dac1e 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -170,6 +170,14 @@ static const struct usb_device_id usb_quirk_list[] = { /* M-Systems Flash Disk Pioneers */ { USB_DEVICE(0x08ec, 0x1000), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Baum Vario Ultra */ + { USB_DEVICE(0x0904, 0x6101), .driver_info = + USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL }, + { USB_DEVICE(0x0904, 0x6102), .driver_info = + USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL }, + { USB_DEVICE(0x0904, 0x6103), .driver_info = + USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL }, + /* Keytouch QWERTY Panel keyboard */ { USB_DEVICE(0x0926, 0x3333), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS }, diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index a80fb34cdce8..ad9d6cc4e23f 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -764,6 +764,7 @@ static int dwc3_msm_ep_queue(struct usb_ep *ep, return 0; err: + list_del(&req_complete->list_item); spin_unlock_irqrestore(&dwc->lock, flags); kfree(req_complete); return ret; @@ -3596,7 +3597,8 @@ static int dwc3_msm_gadget_vbus_draw(struct dwc3_msm *mdwc, unsigned mA) } } - power_supply_get_property(mdwc->usb_psy, POWER_SUPPLY_PROP_TYPE, &pval); + power_supply_get_property(mdwc->usb_psy, + POWER_SUPPLY_PROP_REAL_TYPE, &pval); if (pval.intval != POWER_SUPPLY_TYPE_USB) return 0; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 9608a79cbe40..658fcca485d8 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -261,6 +261,7 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, int status) { struct dwc3 *dwc = dep->dwc; + unsigned int unmap_after_complete = false; int i; if (req->queued) { @@ -285,11 +286,19 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, if (req->request.status == -EINPROGRESS) req->request.status = status; - if (dwc->ep0_bounced && dep->number <= 1) + /* + * NOTICE we don't want to unmap before calling ->complete() if we're + * dealing with a bounced ep0 request. If we unmap it here, we would end + * up overwritting the contents of req->buf and this could confuse the + * gadget driver. + */ + if (dwc->ep0_bounced && dep->number <= 1) { dwc->ep0_bounced = false; - - usb_gadget_unmap_request(&dwc->gadget, &req->request, - req->direction); + unmap_after_complete = true; + } else { + usb_gadget_unmap_request(&dwc->gadget, + &req->request, req->direction); + } dev_dbg(dwc->dev, "request %pK from %s completed %d/%d ===> %d\n", req, dep->name, req->request.actual, @@ -300,6 +309,10 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, spin_unlock(&dwc->lock); usb_gadget_giveback_request(&dep->endpoint, &req->request); spin_lock(&dwc->lock); + + if (unmap_after_complete) + usb_gadget_unmap_request(&dwc->gadget, + &req->request, req->direction); } int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param) diff --git a/drivers/usb/gadget/function/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c index cd096fb9078f..1ef3442cf618 100644 --- a/drivers/usb/gadget/function/f_accessory.c +++ b/drivers/usb/gadget/function/f_accessory.c @@ -77,9 +77,13 @@ struct acc_dev { struct usb_ep *ep_in; struct usb_ep *ep_out; - /* set to 1 when we connect */ + /* online indicates state of function_set_alt & function_unbind + * set to 1 when we connect + */ int online:1; - /* Set to 1 when we disconnect. + + /* disconnected indicates state of open & release + * Set to 1 when we disconnect. * Not cleared until our file is closed. */ int disconnected:1; @@ -307,7 +311,6 @@ static struct usb_request *req_get(struct acc_dev *dev, struct list_head *head) static void acc_set_disconnected(struct acc_dev *dev) { - dev->online = 0; dev->disconnected = 1; } @@ -721,9 +724,10 @@ static ssize_t acc_write(struct file *fp, const char __user *buf, req->zero = 0; } else { xfer = count; - /* If the data length is a multple of the + /* + * If the data length is a multple of the * maxpacket size then send a zero length packet(ZLP). - */ + */ req->zero = ((xfer % dev->ep_in->maxpacket) == 0); } if (copy_from_user(req->buf, buf, xfer)) { @@ -808,7 +812,10 @@ static int acc_release(struct inode *ip, struct file *fp) printk(KERN_INFO "acc_release\n"); WARN_ON(!atomic_xchg(&_acc_dev->open_excl, 0)); - _acc_dev->disconnected = 0; + /* indicate that we are disconnected + * still could be online so don't touch online flag + */ + _acc_dev->disconnected = 1; return 0; } @@ -868,11 +875,11 @@ int acc_ctrlrequest(struct usb_composite_dev *cdev, unsigned long flags; /* - printk(KERN_INFO "acc_ctrlrequest " - "%02x.%02x v%04x i%04x l%u\n", - b_requestType, b_request, - w_value, w_index, w_length); -*/ + * printk(KERN_INFO "acc_ctrlrequest " + * "%02x.%02x v%04x i%04x l%u\n", + * b_requestType, b_request, + * w_value, w_index, w_length); + */ if (b_requestType == (USB_DIR_OUT | USB_TYPE_VENDOR)) { if (b_request == ACCESSORY_START) { @@ -1069,6 +1076,10 @@ acc_function_unbind(struct usb_configuration *c, struct usb_function *f) struct usb_request *req; int i; + dev->online = 0; /* clear online flag */ + wake_up(&dev->read_wq); /* unblock reads on closure */ + wake_up(&dev->write_wq); /* likewise for writes */ + while ((req = req_get(dev, &dev->tx_idle))) acc_request_free(req, dev->ep_in); for (i = 0; i < RX_REQ_MAX; i++) @@ -1200,6 +1211,7 @@ static int acc_function_set_alt(struct usb_function *f, } dev->online = 1; + dev->disconnected = 0; /* if online then not disconnected */ /* readers may be blocked waiting for us to go online */ wake_up(&dev->read_wq); @@ -1212,7 +1224,8 @@ static void acc_function_disable(struct usb_function *f) struct usb_composite_dev *cdev = dev->cdev; DBG(cdev, "acc_function_disable\n"); - acc_set_disconnected(dev); + acc_set_disconnected(dev); /* this now only sets disconnected */ + dev->online = 0; /* so now need to clear online flag here too */ usb_ep_disable(dev->ep_in); usb_ep_disable(dev->ep_out); diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c index 651e4afe0520..1bcfe819fad3 100644 --- a/drivers/usb/gadget/function/f_acm.c +++ b/drivers/usb/gadget/function/f_acm.c @@ -535,13 +535,15 @@ static int acm_notify_serial_state(struct f_acm *acm) { struct usb_composite_dev *cdev = acm->port.func.config->cdev; int status; + __le16 serial_state; spin_lock(&acm->lock); if (acm->notify_req) { dev_dbg(&cdev->gadget->dev, "acm ttyGS%d serial state %04x\n", acm->port_num, acm->serial_state); + serial_state = cpu_to_le16(acm->serial_state); status = acm_cdc_notify(acm, USB_CDC_NOTIFY_SERIAL_STATE, - 0, &acm->serial_state, sizeof(acm->serial_state)); + 0, &serial_state, sizeof(acm->serial_state)); } else { acm->pending = true; status = 0; diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 5bcff5d2cd8d..79f554f1fb23 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -167,6 +167,15 @@ static struct usb_endpoint_descriptor bulk_in_desc = { .bmAttributes = USB_ENDPOINT_XFER_BULK, }; +static struct usb_ss_ep_comp_descriptor ss_bulk_comp_desc = { + .bLength = sizeof(ss_bulk_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /* the following 2 values can be tweaked if necessary */ + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ +}; + /* B.6.2 Class-specific MS Bulk IN Endpoint Descriptor */ static struct usb_ms_endpoint_descriptor_16 ms_in_desc = { /* .bLength = DYNAMIC */ @@ -361,7 +370,9 @@ static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt) /* allocate a bunch of read buffers and queue them all at once. */ for (i = 0; i < midi->qlen && err == 0; i++) { struct usb_request *req = - midi_alloc_ep_req(midi->out_ep, midi->buflen); + midi_alloc_ep_req(midi->out_ep, + max_t(unsigned, midi->buflen, + bulk_out_desc.wMaxPacketSize)); if (req == NULL) return -ENOMEM; @@ -720,6 +731,7 @@ fail: static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_descriptor_header **midi_function; + struct usb_descriptor_header **midi_ss_function; struct usb_midi_in_jack_descriptor jack_in_ext_desc[MAX_PORTS]; struct usb_midi_in_jack_descriptor jack_in_emb_desc[MAX_PORTS]; struct usb_midi_out_jack_descriptor_1 jack_out_ext_desc[MAX_PORTS]; @@ -727,7 +739,7 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) struct usb_composite_dev *cdev = c->cdev; struct f_midi *midi = func_to_midi(f); struct usb_string *us; - int status, n, jack = 1, i = 0; + int status, n, jack = 1, i = 0, j = 0; midi->gadget = cdev->gadget; tasklet_init(&midi->tasklet, f_midi_in_tasklet, (unsigned long) midi); @@ -767,11 +779,20 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) if (!midi->out_ep) goto fail; + /* allocate temporary function list for ss */ + midi_ss_function = kcalloc((MAX_PORTS * 4) + 11, + sizeof(*midi_ss_function), GFP_KERNEL); + if (!midi_ss_function) { + status = -ENOMEM; + goto fail; + } + /* allocate temporary function list */ midi_function = kcalloc((MAX_PORTS * 4) + 9, sizeof(*midi_function), GFP_KERNEL); if (!midi_function) { status = -ENOMEM; + kfree(midi_ss_function); goto fail; } @@ -785,6 +806,12 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) midi_function[i++] = (struct usb_descriptor_header *) &ac_interface_desc; midi_function[i++] = (struct usb_descriptor_header *) &ac_header_desc; midi_function[i++] = (struct usb_descriptor_header *) &ms_interface_desc; + midi_ss_function[j++] = + (struct usb_descriptor_header *) &ac_interface_desc; + midi_ss_function[j++] = + (struct usb_descriptor_header *) &ac_header_desc; + midi_ss_function[j++] = + (struct usb_descriptor_header *) &ms_interface_desc; /* calculate the header's wTotalLength */ n = USB_DT_MS_HEADER_SIZE @@ -793,6 +820,8 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) ms_header_desc.wTotalLength = cpu_to_le16(n); midi_function[i++] = (struct usb_descriptor_header *) &ms_header_desc; + midi_ss_function[j++] = + (struct usb_descriptor_header *) &ms_header_desc; /* configure the external IN jacks, each linked to an embedded OUT jack */ for (n = 0; n < midi->in_ports; n++) { @@ -806,6 +835,7 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) in_ext->bJackID = jack++; in_ext->iJack = 0; midi_function[i++] = (struct usb_descriptor_header *) in_ext; + midi_ss_function[j++] = (struct usb_descriptor_header *) in_ext; out_emb->bLength = USB_DT_MIDI_OUT_SIZE(1); out_emb->bDescriptorType = USB_DT_CS_INTERFACE; @@ -817,6 +847,8 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) out_emb->pins[0].baSourceID = in_ext->bJackID; out_emb->iJack = 0; midi_function[i++] = (struct usb_descriptor_header *) out_emb; + midi_ss_function[j++] = + (struct usb_descriptor_header *) out_emb; /* link it to the endpoint */ ms_in_desc.baAssocJackID[n] = out_emb->bJackID; @@ -834,6 +866,7 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) in_emb->bJackID = jack++; in_emb->iJack = 0; midi_function[i++] = (struct usb_descriptor_header *) in_emb; + midi_ss_function[j++] = (struct usb_descriptor_header *) in_emb; out_ext->bLength = USB_DT_MIDI_OUT_SIZE(1); out_ext->bDescriptorType = USB_DT_CS_INTERFACE; @@ -845,6 +878,8 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) out_ext->pins[0].baSourceID = in_emb->bJackID; out_ext->pins[0].baSourcePin = 1; midi_function[i++] = (struct usb_descriptor_header *) out_ext; + midi_ss_function[j++] = + (struct usb_descriptor_header *) out_ext; /* link it to the endpoint */ ms_out_desc.baAssocJackID[n] = in_emb->bJackID; @@ -864,6 +899,16 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) midi_function[i++] = (struct usb_descriptor_header *) &ms_in_desc; midi_function[i++] = NULL; + midi_ss_function[j++] = (struct usb_descriptor_header *) &bulk_out_desc; + midi_ss_function[j++] = + (struct usb_descriptor_header *) &ss_bulk_comp_desc; + midi_ss_function[j++] = (struct usb_descriptor_header *) &ms_out_desc; + midi_ss_function[j++] = (struct usb_descriptor_header *) &bulk_in_desc; + midi_ss_function[j++] = + (struct usb_descriptor_header *) &ss_bulk_comp_desc; + midi_ss_function[j++] = (struct usb_descriptor_header *) &ms_in_desc; + midi_ss_function[j++] = NULL; + /* * support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at @@ -882,13 +927,23 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) goto fail_f_midi; } + if (gadget_is_superspeed(c->cdev->gadget)) { + bulk_in_desc.wMaxPacketSize = cpu_to_le16(1024); + bulk_out_desc.wMaxPacketSize = cpu_to_le16(1024); + f->ss_descriptors = usb_copy_descriptors(midi_ss_function); + if (!f->ss_descriptors) + goto fail_f_midi; + } + kfree(midi_function); + kfree(midi_ss_function); return 0; fail_f_midi: kfree(midi_function); usb_free_descriptors(f->hs_descriptors); + kfree(midi_ss_function); fail: f_midi_unregister_card(midi); fail_register: diff --git a/drivers/usb/gadget/function/f_mtp.c b/drivers/usb/gadget/function/f_mtp.c index 4a0b3a0aa65e..6ee21d039415 100644 --- a/drivers/usb/gadget/function/f_mtp.c +++ b/drivers/usb/gadget/function/f_mtp.c @@ -1490,6 +1490,7 @@ mtp_function_bind(struct usb_configuration *c, struct usb_function *f) mtp_fullspeed_out_desc.bEndpointAddress; } + fi_mtp->func_inst.f = &dev->function; DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", gadget_is_superspeed(c->cdev->gadget) ? "super" : (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full"), @@ -1501,9 +1502,10 @@ static void mtp_function_unbind(struct usb_configuration *c, struct usb_function *f) { struct mtp_dev *dev = func_to_mtp(f); + struct mtp_instance *fi_mtp; struct usb_request *req; int i; - + fi_mtp = container_of(f->fi, struct mtp_instance, func_inst); mtp_string_defs[INTERFACE_STRING_INDEX].id = 0; mutex_lock(&dev->read_mutex); while ((req = mtp_req_get(dev, &dev->tx_idle))) @@ -1517,6 +1519,7 @@ mtp_function_unbind(struct usb_configuration *c, struct usb_function *f) dev->is_ptp = false; kfree(f->os_desc_table); f->os_desc_n = 0; + fi_mtp->func_inst.f = NULL; } static int mtp_function_set_alt(struct usb_function *f, @@ -1854,6 +1857,8 @@ struct usb_function_instance *alloc_inst_mtp_ptp(bool mtp_config) config_group_init_type_name(&fi_mtp->func_inst.group, "", &mtp_func_type); + mutex_init(&fi_mtp->dev->read_mutex); + return &fi_mtp->func_inst; } EXPORT_SYMBOL_GPL(alloc_inst_mtp_ptp); @@ -1914,9 +1919,7 @@ struct usb_function *function_alloc_mtp_ptp(struct usb_function_instance *fi, dev->function.setup = mtp_ctrlreq_configfs; dev->function.free_func = mtp_free; dev->is_ptp = !mtp_config; - fi->f = &dev->function; - mutex_init(&dev->read_mutex); return &dev->function; } EXPORT_SYMBOL_GPL(function_alloc_mtp_ptp); diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 29b41b5dee04..c7689d05356c 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -625,7 +625,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) uvc_ss_streaming_comp.bMaxBurst = opts->streaming_maxburst; uvc_ss_streaming_comp.wBytesPerInterval = cpu_to_le16(max_packet_size * max_packet_mult * - opts->streaming_maxburst); + (opts->streaming_maxburst + 1)); /* Allocate endpoints. */ ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep); diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index 43e054666b68..9123f1635843 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -66,7 +66,7 @@ struct eth_dev { spinlock_t req_lock; /* guard {rx,tx}_reqs */ struct list_head tx_reqs, rx_reqs; - unsigned tx_qlen; + atomic_t tx_qlen; /* Minimum number of TX USB request queued to UDC */ #define TX_REQ_THRESHOLD 5 int no_tx_req_used; @@ -568,6 +568,7 @@ static void tx_complete(struct usb_ep *ep, struct usb_request *req) dev_kfree_skb_any(skb); } + atomic_dec(&dev->tx_qlen); if (netif_carrier_ok(dev->net)) netif_wake_queue(dev->net); } @@ -741,20 +742,13 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, req->length = length; - /* throttle highspeed IRQ rate back slightly */ - if (gadget_is_dualspeed(dev->gadget) && - (dev->gadget->speed == USB_SPEED_HIGH) && - !list_empty(&dev->tx_reqs)) { - dev->tx_qlen++; - if (dev->tx_qlen == (dev->qmult/2)) { - req->no_interrupt = 0; - dev->tx_qlen = 0; - } else { - req->no_interrupt = 1; - } - } else { - req->no_interrupt = 0; - } + /* throttle high/super speed IRQ rate back slightly */ + if (gadget_is_dualspeed(dev->gadget)) + req->no_interrupt = (((dev->gadget->speed == USB_SPEED_HIGH || + dev->gadget->speed == USB_SPEED_SUPER)) && + !list_empty(&dev->tx_reqs)) + ? ((atomic_read(&dev->tx_qlen) % dev->qmult) != 0) + : 0; retval = usb_ep_queue(in, req, GFP_ATOMIC); switch (retval) { @@ -763,6 +757,7 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, break; case 0: net->trans_start = jiffies; + atomic_inc(&dev->tx_qlen); } if (retval) { @@ -791,7 +786,7 @@ static void eth_start(struct eth_dev *dev, gfp_t gfp_flags) rx_fill(dev, gfp_flags); /* and open the tx floodgates */ - dev->tx_qlen = 0; + atomic_set(&dev->tx_qlen, 0); netif_wake_queue(dev->net); } diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index f7a7fc21be8a..e8f9172880c4 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -268,3 +268,13 @@ config USB_CHAOSKEY To compile this driver as a module, choose M here: the module will be called chaoskey. + +config USB_QTI_KS_BRIDGE + tristate "USB QTI kick start bridge" + depends on USB + help + Say Y here if you have a QTI modem device connected via USB that + will be bridged in kernel space. This driver works as a bridge to pass + boot images, ram-dumps and efs sync. + To compile this driver as a module, choose M here: the module + will be called ks_bridge. If unsure, choose N. diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index 45fd4ac39d3e..616902bce450 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -29,3 +29,5 @@ obj-$(CONFIG_USB_CHAOSKEY) += chaoskey.o obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/ obj-$(CONFIG_USB_LINK_LAYER_TEST) += lvstest.o + +obj-$(CONFIG_USB_QTI_KS_BRIDGE) += ks_bridge.o diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c index 4e38683c653c..6d4e75785710 100644 --- a/drivers/usb/misc/idmouse.c +++ b/drivers/usb/misc/idmouse.c @@ -346,6 +346,9 @@ static int idmouse_probe(struct usb_interface *interface, if (iface_desc->desc.bInterfaceClass != 0x0A) return -ENODEV; + if (iface_desc->desc.bNumEndpoints < 1) + return -ENODEV; + /* allocate memory for our device state and initialize it */ dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (dev == NULL) diff --git a/drivers/usb/misc/ks_bridge.c b/drivers/usb/misc/ks_bridge.c new file mode 100644 index 000000000000..35f652c281bb --- /dev/null +++ b/drivers/usb/misc/ks_bridge.c @@ -0,0 +1,1105 @@ +/* + * Copyright (c) 2012-2014, 2017, 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. + */ + +/* add additional information to our printk's */ +#define pr_fmt(fmt) "%s: " fmt "\n", __func__ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/kref.h> +#include <linux/platform_device.h> +#include <linux/ratelimit.h> +#include <linux/uaccess.h> +#include <linux/usb.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/cdev.h> +#include <linux/list.h> +#include <linux/wait.h> +#include <linux/poll.h> + +#define DRIVER_DESC "USB host ks bridge driver" + +enum bus_id { + BUS_HSIC, + BUS_USB, + BUS_UNDEF, +}; + +#define BUSNAME_LEN 20 + +static enum bus_id str_to_busid(const char *name) +{ + if (!strncasecmp("msm_hsic_host", name, BUSNAME_LEN)) + return BUS_HSIC; + if (!strncasecmp("msm_ehci_host.0", name, BUSNAME_LEN)) + return BUS_USB; + if (!strncasecmp("xhci-hcd.0.auto", name, BUSNAME_LEN)) + return BUS_USB; + + return BUS_UNDEF; +} + +struct data_pkt { + int n_read; + char *buf; + size_t len; + struct list_head list; + void *ctxt; +}; + +#define FILE_OPENED BIT(0) +#define USB_DEV_CONNECTED BIT(1) +#define NO_RX_REQS 10 +#define NO_BRIDGE_INSTANCES 4 +#define EFS_HSIC_BRIDGE_INDEX 2 +#define EFS_USB_BRIDGE_INDEX 3 +#define MAX_DATA_PKT_SIZE 16384 +#define PENDING_URB_TIMEOUT 10 + +struct ksb_dev_info { + const char *name; +}; + +struct ks_bridge { + char *name; + spinlock_t lock; + struct workqueue_struct *wq; + struct work_struct to_mdm_work; + struct work_struct start_rx_work; + struct list_head to_mdm_list; + struct list_head to_ks_list; + wait_queue_head_t ks_wait_q; + wait_queue_head_t pending_urb_wait; + atomic_t tx_pending_cnt; + atomic_t rx_pending_cnt; + + struct ksb_dev_info id_info; + + /* cdev interface */ + dev_t cdev_start_no; + struct cdev cdev; + struct class *class; + struct device *device; + + /* usb specific */ + struct usb_device *udev; + struct usb_interface *ifc; + __u8 in_epAddr; + __u8 out_epAddr; + unsigned int in_pipe; + unsigned int out_pipe; + struct usb_anchor submitted; + + unsigned long flags; + + /* to handle INT IN ep */ + unsigned int period; + +#define DBG_MSG_LEN 40 +#define DBG_MAX_MSG 500 + unsigned int dbg_idx; + rwlock_t dbg_lock; + + char (dbgbuf[DBG_MAX_MSG])[DBG_MSG_LEN]; /* buffer */ +}; + +struct ks_bridge *__ksb[NO_BRIDGE_INSTANCES]; + +/* by default debugging is enabled */ +static unsigned int enable_dbg = 1; +module_param(enable_dbg, uint, S_IRUGO | S_IWUSR); + +static void +dbg_log_event(struct ks_bridge *ksb, char *event, int d1, int d2) +{ + unsigned long flags; + unsigned long long t; + unsigned long nanosec; + + if (!enable_dbg) + return; + + write_lock_irqsave(&ksb->dbg_lock, flags); + t = cpu_clock(smp_processor_id()); + nanosec = do_div(t, 1000000000)/1000; + scnprintf(ksb->dbgbuf[ksb->dbg_idx], DBG_MSG_LEN, "%5lu.%06lu:%s:%x:%x", + (unsigned long)t, nanosec, event, d1, d2); + + ksb->dbg_idx++; + ksb->dbg_idx = ksb->dbg_idx % DBG_MAX_MSG; + write_unlock_irqrestore(&ksb->dbg_lock, flags); +} + +static +struct data_pkt *ksb_alloc_data_pkt(size_t count, gfp_t flags, void *ctxt) +{ + struct data_pkt *pkt; + + pkt = kzalloc(sizeof(struct data_pkt), flags); + if (!pkt) + return ERR_PTR(-ENOMEM); + + pkt->buf = kmalloc(count, flags); + if (!pkt->buf) { + kfree(pkt); + return ERR_PTR(-ENOMEM); + } + + pkt->len = count; + INIT_LIST_HEAD(&pkt->list); + pkt->ctxt = ctxt; + + return pkt; +} + +static void ksb_free_data_pkt(struct data_pkt *pkt) +{ + kfree(pkt->buf); + kfree(pkt); +} + + +static void +submit_one_urb(struct ks_bridge *ksb, gfp_t flags, struct data_pkt *pkt); +static ssize_t ksb_fs_read(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + int ret; + unsigned long flags; + struct ks_bridge *ksb = fp->private_data; + struct data_pkt *pkt = NULL; + size_t space, copied; + +read_start: + if (!test_bit(USB_DEV_CONNECTED, &ksb->flags)) + return -ENODEV; + + spin_lock_irqsave(&ksb->lock, flags); + if (list_empty(&ksb->to_ks_list)) { + spin_unlock_irqrestore(&ksb->lock, flags); + ret = wait_event_interruptible(ksb->ks_wait_q, + !list_empty(&ksb->to_ks_list) || + !test_bit(USB_DEV_CONNECTED, &ksb->flags)); + if (ret < 0) + return ret; + + goto read_start; + } + + space = count; + copied = 0; + while (!list_empty(&ksb->to_ks_list) && space && + test_bit(USB_DEV_CONNECTED, &ksb->flags)) { + size_t len; + + pkt = list_first_entry(&ksb->to_ks_list, struct data_pkt, list); + list_del_init(&pkt->list); + len = min_t(size_t, space, pkt->len - pkt->n_read); + spin_unlock_irqrestore(&ksb->lock, flags); + + ret = copy_to_user(buf + copied, pkt->buf + pkt->n_read, len); + if (ret) { + dev_err(ksb->device, + "copy_to_user failed err:%d\n", ret); + ksb_free_data_pkt(pkt); + return -EFAULT; + } + + pkt->n_read += len; + space -= len; + copied += len; + + if (pkt->n_read == pkt->len) { + /* + * re-init the packet and queue it + * for more data. + */ + pkt->n_read = 0; + pkt->len = MAX_DATA_PKT_SIZE; + submit_one_urb(ksb, GFP_KERNEL, pkt); + pkt = NULL; + } + spin_lock_irqsave(&ksb->lock, flags); + } + + /* put the partial packet back in the list */ + if (!space && pkt && pkt->n_read != pkt->len) { + if (test_bit(USB_DEV_CONNECTED, &ksb->flags)) + list_add(&pkt->list, &ksb->to_ks_list); + else + ksb_free_data_pkt(pkt); + } + spin_unlock_irqrestore(&ksb->lock, flags); + + dbg_log_event(ksb, "KS_READ", copied, 0); + + dev_dbg(ksb->device, "count:%zu space:%zu copied:%zu", count, + space, copied); + + return copied; +} + +static void ksb_tx_cb(struct urb *urb) +{ + struct data_pkt *pkt = urb->context; + struct ks_bridge *ksb = pkt->ctxt; + + dbg_log_event(ksb, "C TX_URB", urb->status, 0); + dev_dbg(&ksb->udev->dev, "status:%d", urb->status); + + if (test_bit(USB_DEV_CONNECTED, &ksb->flags)) + usb_autopm_put_interface_async(ksb->ifc); + + if (urb->status < 0) + pr_err_ratelimited("%s: urb failed with err:%d", + ksb->id_info.name, urb->status); + + ksb_free_data_pkt(pkt); + + atomic_dec(&ksb->tx_pending_cnt); + wake_up(&ksb->pending_urb_wait); +} + +static void ksb_tomdm_work(struct work_struct *w) +{ + struct ks_bridge *ksb = container_of(w, struct ks_bridge, to_mdm_work); + struct data_pkt *pkt; + unsigned long flags; + struct urb *urb; + int ret; + + spin_lock_irqsave(&ksb->lock, flags); + while (!list_empty(&ksb->to_mdm_list) + && test_bit(USB_DEV_CONNECTED, &ksb->flags)) { + pkt = list_first_entry(&ksb->to_mdm_list, + struct data_pkt, list); + list_del_init(&pkt->list); + spin_unlock_irqrestore(&ksb->lock, flags); + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + dbg_log_event(ksb, "TX_URB_MEM_FAIL", -ENOMEM, 0); + pr_err_ratelimited("%s: unable to allocate urb", + ksb->id_info.name); + ksb_free_data_pkt(pkt); + return; + } + + ret = usb_autopm_get_interface(ksb->ifc); + if (ret < 0 && ret != -EAGAIN && ret != -EACCES) { + dbg_log_event(ksb, "TX_URB_AUTOPM_FAIL", ret, 0); + pr_err_ratelimited("%s: autopm_get failed:%d", + ksb->id_info.name, ret); + usb_free_urb(urb); + ksb_free_data_pkt(pkt); + return; + } + usb_fill_bulk_urb(urb, ksb->udev, ksb->out_pipe, + pkt->buf, pkt->len, ksb_tx_cb, pkt); + usb_anchor_urb(urb, &ksb->submitted); + + dbg_log_event(ksb, "S TX_URB", pkt->len, 0); + + atomic_inc(&ksb->tx_pending_cnt); + ret = usb_submit_urb(urb, GFP_KERNEL); + if (ret) { + dev_err(&ksb->udev->dev, "out urb submission failed"); + usb_unanchor_urb(urb); + usb_free_urb(urb); + ksb_free_data_pkt(pkt); + usb_autopm_put_interface(ksb->ifc); + atomic_dec(&ksb->tx_pending_cnt); + wake_up(&ksb->pending_urb_wait); + return; + } + + usb_free_urb(urb); + + spin_lock_irqsave(&ksb->lock, flags); + } + spin_unlock_irqrestore(&ksb->lock, flags); +} + +static ssize_t ksb_fs_write(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + int ret; + struct data_pkt *pkt; + unsigned long flags; + struct ks_bridge *ksb = fp->private_data; + + if (!test_bit(USB_DEV_CONNECTED, &ksb->flags)) + return -ENODEV; + + if (count > MAX_DATA_PKT_SIZE) + count = MAX_DATA_PKT_SIZE; + + pkt = ksb_alloc_data_pkt(count, GFP_KERNEL, ksb); + if (IS_ERR(pkt)) { + dev_err(ksb->device, + "unable to allocate data packet"); + return PTR_ERR(pkt); + } + + ret = copy_from_user(pkt->buf, buf, count); + if (ret) { + dev_err(ksb->device, + "copy_from_user failed: err:%d", ret); + ksb_free_data_pkt(pkt); + return ret; + } + + spin_lock_irqsave(&ksb->lock, flags); + list_add_tail(&pkt->list, &ksb->to_mdm_list); + spin_unlock_irqrestore(&ksb->lock, flags); + + queue_work(ksb->wq, &ksb->to_mdm_work); + + dbg_log_event(ksb, "KS_WRITE", count, 0); + + return count; +} + +static int ksb_fs_open(struct inode *ip, struct file *fp) +{ + struct ks_bridge *ksb = + container_of(ip->i_cdev, struct ks_bridge, cdev); + + if (IS_ERR(ksb)) { + pr_err("ksb device not found"); + return -ENODEV; + } + + dev_dbg(ksb->device, ":%s", ksb->id_info.name); + dbg_log_event(ksb, "FS-OPEN", 0, 0); + + fp->private_data = ksb; + set_bit(FILE_OPENED, &ksb->flags); + + if (test_bit(USB_DEV_CONNECTED, &ksb->flags)) + queue_work(ksb->wq, &ksb->start_rx_work); + + return 0; +} + +static unsigned int ksb_fs_poll(struct file *file, poll_table *wait) +{ + struct ks_bridge *ksb = file->private_data; + unsigned long flags; + int ret = 0; + + if (!test_bit(USB_DEV_CONNECTED, &ksb->flags)) + return POLLERR; + + poll_wait(file, &ksb->ks_wait_q, wait); + if (!test_bit(USB_DEV_CONNECTED, &ksb->flags)) + return POLLERR; + + spin_lock_irqsave(&ksb->lock, flags); + if (!list_empty(&ksb->to_ks_list)) + ret = POLLIN | POLLRDNORM; + spin_unlock_irqrestore(&ksb->lock, flags); + + return ret; +} + +static int ksb_fs_release(struct inode *ip, struct file *fp) +{ + struct ks_bridge *ksb = fp->private_data; + + if (test_bit(USB_DEV_CONNECTED, &ksb->flags)) + dev_dbg(ksb->device, ":%s", ksb->id_info.name); + dbg_log_event(ksb, "FS-RELEASE", 0, 0); + + clear_bit(FILE_OPENED, &ksb->flags); + fp->private_data = NULL; + + return 0; +} + +static const struct file_operations ksb_fops = { + .owner = THIS_MODULE, + .read = ksb_fs_read, + .write = ksb_fs_write, + .open = ksb_fs_open, + .release = ksb_fs_release, + .poll = ksb_fs_poll, +}; + +static struct ksb_dev_info ksb_fboot_dev[] = { + { + .name = "ks_hsic_bridge", + }, + { + .name = "ks_usb_bridge", + }, +}; + +static struct ksb_dev_info ksb_efs_hsic_dev = { + .name = "efs_hsic_bridge", +}; + +static struct ksb_dev_info ksb_efs_usb_dev = { + .name = "efs_usb_bridge", +}; +static const struct usb_device_id ksb_usb_ids[] = { + { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9008, 0), + .driver_info = (unsigned long)&ksb_fboot_dev, }, + { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9025, 0), }, + { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9091, 0), }, + { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x901D, 0), }, + { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x900E, 0), }, + { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9048, 2), + .driver_info = (unsigned long)&ksb_efs_hsic_dev, }, + { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x904C, 2), + .driver_info = (unsigned long)&ksb_efs_hsic_dev, }, + { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9075, 2), + .driver_info = (unsigned long)&ksb_efs_hsic_dev, }, + { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9079, 2), + .driver_info = (unsigned long)&ksb_efs_usb_dev, }, + { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x908A, 2), + .driver_info = (unsigned long)&ksb_efs_hsic_dev, }, + { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x908E, 3), + .driver_info = (unsigned long)&ksb_efs_hsic_dev, }, + { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x909C, 2), + .driver_info = (unsigned long)&ksb_efs_hsic_dev, }, + { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x909D, 2), + .driver_info = (unsigned long)&ksb_efs_hsic_dev, }, + { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x909E, 3), + .driver_info = (unsigned long)&ksb_efs_hsic_dev, }, + { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x909F, 2), + .driver_info = (unsigned long)&ksb_efs_hsic_dev, }, + { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x90A0, 2), + .driver_info = (unsigned long)&ksb_efs_hsic_dev, }, + { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x90A4, 3), + .driver_info = (unsigned long)&ksb_efs_hsic_dev, }, + + {} /* terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, ksb_usb_ids); + +static void ksb_rx_cb(struct urb *urb); +static void +submit_one_urb(struct ks_bridge *ksb, gfp_t flags, struct data_pkt *pkt) +{ + struct urb *urb; + int ret; + + urb = usb_alloc_urb(0, flags); + if (!urb) { + dev_err(&ksb->udev->dev, "unable to allocate urb"); + ksb_free_data_pkt(pkt); + return; + } + + if (ksb->period) + usb_fill_int_urb(urb, ksb->udev, ksb->in_pipe, + pkt->buf, pkt->len, + ksb_rx_cb, pkt, ksb->period); + else + usb_fill_bulk_urb(urb, ksb->udev, ksb->in_pipe, + pkt->buf, pkt->len, + ksb_rx_cb, pkt); + + usb_anchor_urb(urb, &ksb->submitted); + + if (!test_bit(USB_DEV_CONNECTED, &ksb->flags)) { + usb_unanchor_urb(urb); + usb_free_urb(urb); + ksb_free_data_pkt(pkt); + return; + } + + atomic_inc(&ksb->rx_pending_cnt); + ret = usb_submit_urb(urb, flags); + if (ret) { + dev_err(&ksb->udev->dev, "in urb submission failed"); + usb_unanchor_urb(urb); + usb_free_urb(urb); + ksb_free_data_pkt(pkt); + atomic_dec(&ksb->rx_pending_cnt); + wake_up(&ksb->pending_urb_wait); + return; + } + + dbg_log_event(ksb, "S RX_URB", pkt->len, 0); + + usb_free_urb(urb); +} +static void ksb_rx_cb(struct urb *urb) +{ + struct data_pkt *pkt = urb->context; + struct ks_bridge *ksb = pkt->ctxt; + bool wakeup = true; + + dbg_log_event(ksb, "C RX_URB", urb->status, urb->actual_length); + + dev_dbg(&ksb->udev->dev, "status:%d actual:%d", urb->status, + urb->actual_length); + + /*non zero len of data received while unlinking urb*/ + if (urb->status == -ENOENT && (urb->actual_length > 0)) { + /* + * If we wakeup the reader process now, it may + * queue the URB before its reject flag gets + * cleared. + */ + wakeup = false; + goto add_to_list; + } + + if (urb->status < 0) { + if (urb->status != -ESHUTDOWN && urb->status != -ENOENT + && urb->status != -EPROTO) + pr_err_ratelimited("%s: urb failed with err:%d", + ksb->id_info.name, urb->status); + + if (!urb->actual_length) { + ksb_free_data_pkt(pkt); + goto done; + } + } + + usb_mark_last_busy(ksb->udev); + + if (urb->actual_length == 0) { + submit_one_urb(ksb, GFP_ATOMIC, pkt); + goto done; + } + +add_to_list: + spin_lock(&ksb->lock); + pkt->len = urb->actual_length; + list_add_tail(&pkt->list, &ksb->to_ks_list); + spin_unlock(&ksb->lock); + /* wake up read thread */ + if (wakeup) + wake_up(&ksb->ks_wait_q); +done: + atomic_dec(&ksb->rx_pending_cnt); + wake_up(&ksb->pending_urb_wait); +} + +static void ksb_start_rx_work(struct work_struct *w) +{ + struct ks_bridge *ksb = + container_of(w, struct ks_bridge, start_rx_work); + struct data_pkt *pkt; + struct urb *urb; + int i = 0; + int ret; + bool put = true; + + ret = usb_autopm_get_interface(ksb->ifc); + if (ret < 0) { + if (ret != -EAGAIN && ret != -EACCES) { + pr_err_ratelimited("%s: autopm_get failed:%d", + ksb->id_info.name, ret); + return; + } + put = false; + } + for (i = 0; i < NO_RX_REQS; i++) { + + if (!test_bit(USB_DEV_CONNECTED, &ksb->flags)) + break; + + pkt = ksb_alloc_data_pkt(MAX_DATA_PKT_SIZE, GFP_KERNEL, ksb); + if (IS_ERR(pkt)) { + dev_err(&ksb->udev->dev, "unable to allocate data pkt"); + break; + } + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + dev_err(&ksb->udev->dev, "unable to allocate urb"); + ksb_free_data_pkt(pkt); + break; + } + + if (ksb->period) + usb_fill_int_urb(urb, ksb->udev, ksb->in_pipe, + pkt->buf, pkt->len, + ksb_rx_cb, pkt, ksb->period); + else + usb_fill_bulk_urb(urb, ksb->udev, ksb->in_pipe, + pkt->buf, pkt->len, + ksb_rx_cb, pkt); + + usb_anchor_urb(urb, &ksb->submitted); + + dbg_log_event(ksb, "S RX_URB", pkt->len, 0); + + atomic_inc(&ksb->rx_pending_cnt); + ret = usb_submit_urb(urb, GFP_KERNEL); + if (ret) { + dev_err(&ksb->udev->dev, "in urb submission failed"); + usb_unanchor_urb(urb); + usb_free_urb(urb); + ksb_free_data_pkt(pkt); + atomic_dec(&ksb->rx_pending_cnt); + wake_up(&ksb->pending_urb_wait); + break; + } + + usb_free_urb(urb); + } + if (put) + usb_autopm_put_interface_async(ksb->ifc); +} + +static int +ksb_usb_probe(struct usb_interface *ifc, const struct usb_device_id *id) +{ + __u8 ifc_num, ifc_count, ksb_port_num; + struct usb_host_interface *ifc_desc; + struct usb_endpoint_descriptor *ep_desc; + int i; + struct ks_bridge *ksb; + unsigned long flags; + struct data_pkt *pkt; + struct ksb_dev_info *mdev, *fbdev; + struct usb_device *udev; + unsigned int bus_id; + int ret; + bool free_mdev = false; + + ifc_num = ifc->cur_altsetting->desc.bInterfaceNumber; + + udev = interface_to_usbdev(ifc); + ifc_count = udev->actconfig->desc.bNumInterfaces; + fbdev = mdev = (struct ksb_dev_info *)id->driver_info; + + bus_id = str_to_busid(udev->bus->bus_name); + if (bus_id == BUS_UNDEF) { + dev_err(&udev->dev, "unknown usb bus %s, probe failed\n", + udev->bus->bus_name); + return -ENODEV; + } + + switch (id->idProduct) { + case 0x900E: + case 0x9025: + case 0x9091: + case 0x901D: + /* 1-1 mapping between ksb and udev port which starts with 1 */ + ksb_port_num = udev->portnum - 1; + dev_dbg(&udev->dev, "ifc_count: %u, port_num:%u\n", ifc_count, + ksb_port_num); + if (ifc_count > 1) + return -ENODEV; + if (ksb_port_num >= NO_BRIDGE_INSTANCES) { + dev_err(&udev->dev, "port-num:%u invalid. Try first\n", + ksb_port_num); + ksb_port_num = 0; + } + ksb = __ksb[ksb_port_num]; + if (ksb->ifc) { + dev_err(&udev->dev, "port already in use\n"); + return -ENODEV; + } + mdev = kzalloc(sizeof(struct ksb_dev_info), GFP_KERNEL); + if (!mdev) + return -ENOMEM; + free_mdev = true; + mdev->name = ksb->name; + break; + case 0x9008: + ksb = __ksb[bus_id]; + mdev = &fbdev[bus_id]; + break; + case 0x9048: + case 0x904C: + case 0x9075: + case 0x908A: + case 0x908E: + case 0x90A0: + case 0x909C: + case 0x909D: + case 0x909E: + case 0x909F: + case 0x90A4: + ksb = __ksb[EFS_HSIC_BRIDGE_INDEX]; + break; + case 0x9079: + if (ifc_num != 2) + return -ENODEV; + ksb = __ksb[EFS_USB_BRIDGE_INDEX]; + break; + default: + return -ENODEV; + } + + if (!ksb) { + pr_err("ksb is not initialized"); + return -ENODEV; + } + + ksb->udev = usb_get_dev(interface_to_usbdev(ifc)); + ksb->ifc = ifc; + ifc_desc = ifc->cur_altsetting; + ksb->id_info = *mdev; + + for (i = 0; i < ifc_desc->desc.bNumEndpoints; i++) { + ep_desc = &ifc_desc->endpoint[i].desc; + + if (!ksb->in_epAddr && (usb_endpoint_is_bulk_in(ep_desc))) { + ksb->in_epAddr = ep_desc->bEndpointAddress; + ksb->period = 0; + } + + if (!ksb->in_epAddr && (usb_endpoint_is_int_in(ep_desc))) { + ksb->in_epAddr = ep_desc->bEndpointAddress; + ksb->period = ep_desc->bInterval; + } + + if (!ksb->out_epAddr && usb_endpoint_is_bulk_out(ep_desc)) + ksb->out_epAddr = ep_desc->bEndpointAddress; + } + + if (!(ksb->in_epAddr && ksb->out_epAddr)) { + dev_err(&udev->dev, + "could not find bulk in and bulk out endpoints"); + usb_put_dev(ksb->udev); + ksb->ifc = NULL; + if (free_mdev) + kfree(mdev); + return -ENODEV; + } + + ksb->in_pipe = ksb->period ? + usb_rcvintpipe(ksb->udev, ksb->in_epAddr) : + usb_rcvbulkpipe(ksb->udev, ksb->in_epAddr); + + ksb->out_pipe = usb_sndbulkpipe(ksb->udev, ksb->out_epAddr); + + usb_set_intfdata(ifc, ksb); + set_bit(USB_DEV_CONNECTED, &ksb->flags); + atomic_set(&ksb->tx_pending_cnt, 0); + atomic_set(&ksb->rx_pending_cnt, 0); + + dbg_log_event(ksb, "PID-ATT", id->idProduct, 0); + + /*free up stale buffers if any from previous disconnect*/ + spin_lock_irqsave(&ksb->lock, flags); + while (!list_empty(&ksb->to_ks_list)) { + pkt = list_first_entry(&ksb->to_ks_list, + struct data_pkt, list); + list_del_init(&pkt->list); + ksb_free_data_pkt(pkt); + } + while (!list_empty(&ksb->to_mdm_list)) { + pkt = list_first_entry(&ksb->to_mdm_list, + struct data_pkt, list); + list_del_init(&pkt->list); + ksb_free_data_pkt(pkt); + } + spin_unlock_irqrestore(&ksb->lock, flags); + + ret = alloc_chrdev_region(&ksb->cdev_start_no, 0, 1, mdev->name); + if (ret < 0) { + dbg_log_event(ksb, "chr reg failed", ret, 0); + goto fail_chrdev_region; + } + + ksb->class = class_create(THIS_MODULE, mdev->name); + if (IS_ERR(ksb->class)) { + dbg_log_event(ksb, "clscr failed", PTR_ERR(ksb->class), 0); + goto fail_class_create; + } + + cdev_init(&ksb->cdev, &ksb_fops); + ksb->cdev.owner = THIS_MODULE; + + ret = cdev_add(&ksb->cdev, ksb->cdev_start_no, 1); + if (ret < 0) { + dbg_log_event(ksb, "cdev_add failed", ret, 0); + goto fail_class_create; + } + + ksb->device = device_create(ksb->class, &udev->dev, ksb->cdev_start_no, + NULL, mdev->name); + if (IS_ERR(ksb->device)) { + dbg_log_event(ksb, "devcrfailed", PTR_ERR(ksb->device), 0); + goto fail_device_create; + } + + if (device_can_wakeup(&ksb->udev->dev)) + ifc->needs_remote_wakeup = 1; + + if (free_mdev) + kfree(mdev); + dev_dbg(&udev->dev, "usb dev connected"); + + return 0; + +fail_device_create: + cdev_del(&ksb->cdev); +fail_class_create: + unregister_chrdev_region(ksb->cdev_start_no, 1); +fail_chrdev_region: + usb_set_intfdata(ifc, NULL); + clear_bit(USB_DEV_CONNECTED, &ksb->flags); + + if (free_mdev) + kfree(mdev); + + return -ENODEV; + +} + +static int ksb_usb_suspend(struct usb_interface *ifc, pm_message_t message) +{ + struct ks_bridge *ksb = usb_get_intfdata(ifc); + unsigned long flags; + + dbg_log_event(ksb, "SUSPEND", 0, 0); + + if (pm_runtime_autosuspend_expiration(&ksb->udev->dev)) { + dbg_log_event(ksb, "SUSP ABORT-TimeCheck", 0, 0); + return -EBUSY; + } + + usb_kill_anchored_urbs(&ksb->submitted); + + spin_lock_irqsave(&ksb->lock, flags); + if (!list_empty(&ksb->to_ks_list)) { + spin_unlock_irqrestore(&ksb->lock, flags); + dbg_log_event(ksb, "SUSPEND ABORT", 0, 0); + /* + * Now wakeup the reader process and queue + * Rx URBs for more data. + */ + wake_up(&ksb->ks_wait_q); + queue_work(ksb->wq, &ksb->start_rx_work); + return -EBUSY; + } + spin_unlock_irqrestore(&ksb->lock, flags); + + return 0; +} + +static int ksb_usb_resume(struct usb_interface *ifc) +{ + struct ks_bridge *ksb = usb_get_intfdata(ifc); + + dbg_log_event(ksb, "RESUME", 0, 0); + + if (test_bit(FILE_OPENED, &ksb->flags)) + queue_work(ksb->wq, &ksb->start_rx_work); + + return 0; +} + +static void ksb_usb_disconnect(struct usb_interface *ifc) +{ + struct ks_bridge *ksb = usb_get_intfdata(ifc); + unsigned long flags; + struct data_pkt *pkt; + + dbg_log_event(ksb, "PID-DETACH", 0, 0); + + clear_bit(USB_DEV_CONNECTED, &ksb->flags); + wake_up(&ksb->ks_wait_q); + cancel_work_sync(&ksb->to_mdm_work); + cancel_work_sync(&ksb->start_rx_work); + + device_destroy(ksb->class, ksb->cdev_start_no); + cdev_del(&ksb->cdev); + class_destroy(ksb->class); + unregister_chrdev_region(ksb->cdev_start_no, 1); + + usb_kill_anchored_urbs(&ksb->submitted); + + wait_event_interruptible_timeout( + ksb->pending_urb_wait, + !atomic_read(&ksb->tx_pending_cnt) && + !atomic_read(&ksb->rx_pending_cnt), + msecs_to_jiffies(PENDING_URB_TIMEOUT)); + + spin_lock_irqsave(&ksb->lock, flags); + while (!list_empty(&ksb->to_ks_list)) { + pkt = list_first_entry(&ksb->to_ks_list, + struct data_pkt, list); + list_del_init(&pkt->list); + ksb_free_data_pkt(pkt); + } + while (!list_empty(&ksb->to_mdm_list)) { + pkt = list_first_entry(&ksb->to_mdm_list, + struct data_pkt, list); + list_del_init(&pkt->list); + ksb_free_data_pkt(pkt); + } + spin_unlock_irqrestore(&ksb->lock, flags); + + ifc->needs_remote_wakeup = 0; + usb_put_dev(ksb->udev); + ksb->ifc = NULL; + usb_set_intfdata(ifc, NULL); +} + +static struct usb_driver ksb_usb_driver = { + .name = "ks_bridge", + .probe = ksb_usb_probe, + .disconnect = ksb_usb_disconnect, + .suspend = ksb_usb_suspend, + .resume = ksb_usb_resume, + .reset_resume = ksb_usb_resume, + .id_table = ksb_usb_ids, + .supports_autosuspend = 1, +}; + +static int ksb_debug_show(struct seq_file *s, void *unused) +{ + unsigned long flags; + struct ks_bridge *ksb = s->private; + int i; + + read_lock_irqsave(&ksb->dbg_lock, flags); + for (i = 0; i < DBG_MAX_MSG; i++) { + if (i == (ksb->dbg_idx - 1)) + seq_printf(s, "-->%s\n", ksb->dbgbuf[i]); + else + seq_printf(s, "%s\n", ksb->dbgbuf[i]); + } + read_unlock_irqrestore(&ksb->dbg_lock, flags); + + return 0; +} + +static int ksb_debug_open(struct inode *ip, struct file *fp) +{ + return single_open(fp, ksb_debug_show, ip->i_private); + + return 0; +} + +static const struct file_operations dbg_fops = { + .open = ksb_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct dentry *dbg_dir; + +static int __init ksb_init(void) +{ + struct ks_bridge *ksb; + int num_instances = 0; + int ret = 0; + int i; + + dbg_dir = debugfs_create_dir("ks_bridge", NULL); + if (IS_ERR(dbg_dir)) + pr_err("unable to create debug dir"); + + for (i = 0; i < NO_BRIDGE_INSTANCES; i++) { + ksb = kzalloc(sizeof(struct ks_bridge), GFP_KERNEL); + if (!ksb) { + pr_err("unable to allocat mem for ks_bridge"); + ret = -ENOMEM; + goto dev_free; + } + __ksb[i] = ksb; + + ksb->name = kasprintf(GFP_KERNEL, "ks_usb_bridge.%i", i); + if (!ksb->name) { + pr_info("unable to allocate name"); + kfree(ksb); + ret = -ENOMEM; + goto dev_free; + } + + spin_lock_init(&ksb->lock); + INIT_LIST_HEAD(&ksb->to_mdm_list); + INIT_LIST_HEAD(&ksb->to_ks_list); + init_waitqueue_head(&ksb->ks_wait_q); + init_waitqueue_head(&ksb->pending_urb_wait); + ksb->wq = create_singlethread_workqueue(ksb->name); + if (!ksb->wq) { + pr_err("unable to allocate workqueue"); + kfree(ksb->name); + kfree(ksb); + ret = -ENOMEM; + goto dev_free; + } + + INIT_WORK(&ksb->to_mdm_work, ksb_tomdm_work); + INIT_WORK(&ksb->start_rx_work, ksb_start_rx_work); + init_usb_anchor(&ksb->submitted); + + ksb->dbg_idx = 0; + ksb->dbg_lock = __RW_LOCK_UNLOCKED(lck); + + if (!IS_ERR(dbg_dir)) + debugfs_create_file(ksb->name, S_IRUGO, dbg_dir, + ksb, &dbg_fops); + + num_instances++; + } + + ret = usb_register(&ksb_usb_driver); + if (ret) { + pr_err("unable to register ks bridge driver"); + goto dev_free; + } + + pr_info("init done"); + + return 0; + +dev_free: + if (!IS_ERR(dbg_dir)) + debugfs_remove_recursive(dbg_dir); + + for (i = 0; i < num_instances; i++) { + ksb = __ksb[i]; + + destroy_workqueue(ksb->wq); + kfree(ksb->name); + kfree(ksb); + } + + return ret; + +} + +static void __exit ksb_exit(void) +{ + struct ks_bridge *ksb; + int i; + + if (!IS_ERR(dbg_dir)) + debugfs_remove_recursive(dbg_dir); + + usb_deregister(&ksb_usb_driver); + + for (i = 0; i < NO_BRIDGE_INSTANCES; i++) { + ksb = __ksb[i]; + + destroy_workqueue(ksb->wq); + kfree(ksb->name); + kfree(ksb); + } +} + +module_init(ksb_init); +module_exit(ksb_exit); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/misc/lvstest.c b/drivers/usb/misc/lvstest.c index 86b4e4b2ab9a..383fa007348f 100644 --- a/drivers/usb/misc/lvstest.c +++ b/drivers/usb/misc/lvstest.c @@ -370,6 +370,10 @@ static int lvs_rh_probe(struct usb_interface *intf, hdev = interface_to_usbdev(intf); desc = intf->cur_altsetting; + + if (desc->desc.bNumEndpoints < 1) + return -ENODEV; + endpoint = &desc->endpoint[0].desc; /* valid only for SS root hub */ diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c index bbd029c9c725..442b6631162e 100644 --- a/drivers/usb/misc/uss720.c +++ b/drivers/usb/misc/uss720.c @@ -711,6 +711,11 @@ static int uss720_probe(struct usb_interface *intf, interface = intf->cur_altsetting; + if (interface->desc.bNumEndpoints < 3) { + usb_put_dev(usbdev); + return -ENODEV; + } + /* * Allocate parport interface */ diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c index e499b862a946..88f26ac2a185 100644 --- a/drivers/usb/musb/musb_cppi41.c +++ b/drivers/usb/musb/musb_cppi41.c @@ -250,8 +250,27 @@ static void cppi41_dma_callback(void *private_data) transferred < cppi41_channel->packet_sz) cppi41_channel->prog_len = 0; - if (cppi41_channel->is_tx) - empty = musb_is_tx_fifo_empty(hw_ep); + if (cppi41_channel->is_tx) { + u8 type; + + if (is_host_active(musb)) + type = hw_ep->out_qh->type; + else + type = hw_ep->ep_in.type; + + if (type == USB_ENDPOINT_XFER_ISOC) + /* + * Don't use the early-TX-interrupt workaround below + * for Isoch transfter. Since Isoch are periodic + * transfer, by the time the next transfer is + * scheduled, the current one should be done already. + * + * This avoids audio playback underrun issue. + */ + empty = true; + else + empty = musb_is_tx_fifo_empty(hw_ep); + } if (!cppi41_channel->is_tx || empty) { cppi41_trans_done(cppi41_channel); diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index c76ca5a94557..055c6203577a 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -2365,7 +2365,7 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) pd->vbus_present = val.intval; ret = power_supply_get_property(pd->usb_psy, - POWER_SUPPLY_PROP_TYPE, &val); + POWER_SUPPLY_PROP_REAL_TYPE, &val); if (ret) { usbpd_err(&pd->dev, "Unable to read USB TYPE: %d\n", ret); return ret; diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 42cc72e54c05..af67a0de6b5d 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -233,6 +233,14 @@ static void option_instat_callback(struct urb *urb); #define BANDRICH_PRODUCT_1012 0x1012 #define QUALCOMM_VENDOR_ID 0x05C6 +/* These Quectel products use Qualcomm's vendor ID */ +#define QUECTEL_PRODUCT_UC20 0x9003 +#define QUECTEL_PRODUCT_UC15 0x9090 + +#define QUECTEL_VENDOR_ID 0x2c7c +/* These Quectel products use Quectel's vendor ID */ +#define QUECTEL_PRODUCT_EC21 0x0121 +#define QUECTEL_PRODUCT_EC25 0x0125 #define CMOTECH_VENDOR_ID 0x16d8 #define CMOTECH_PRODUCT_6001 0x6001 @@ -1161,7 +1169,14 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6613)}, /* Onda H600/ZTE MF330 */ { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x0023)}, /* ONYX 3G device */ { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x9000)}, /* SIMCom SIM5218 */ - { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x9003), /* Quectel UC20 */ + /* Quectel products using Qualcomm vendor ID */ + { USB_DEVICE(QUALCOMM_VENDOR_ID, QUECTEL_PRODUCT_UC15)}, + { USB_DEVICE(QUALCOMM_VENDOR_ID, QUECTEL_PRODUCT_UC20), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + /* Quectel products using Quectel vendor ID */ + { USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC21), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + { USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC25), .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6001) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CMU_300) }, diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index 696458db7e3c..38b3f0d8cd58 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -169,6 +169,8 @@ static const struct usb_device_id id_table[] = { {DEVICE_SWI(0x413c, 0x81a9)}, /* Dell Wireless 5808e Gobi(TM) 4G LTE Mobile Broadband Card */ {DEVICE_SWI(0x413c, 0x81b1)}, /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card */ {DEVICE_SWI(0x413c, 0x81b3)}, /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card (rev3) */ + {DEVICE_SWI(0x413c, 0x81b5)}, /* Dell Wireless 5811e QDL */ + {DEVICE_SWI(0x413c, 0x81b6)}, /* Dell Wireless 5811e QDL */ /* Huawei devices */ {DEVICE_HWI(0x03f0, 0x581d)}, /* HP lt4112 LTE/HSPA+ Gobi 4G Modem (Huawei me906e) */ diff --git a/drivers/usb/wusbcore/wa-hc.c b/drivers/usb/wusbcore/wa-hc.c index 252c7bd9218a..d01496fd27fe 100644 --- a/drivers/usb/wusbcore/wa-hc.c +++ b/drivers/usb/wusbcore/wa-hc.c @@ -39,6 +39,9 @@ int wa_create(struct wahc *wa, struct usb_interface *iface, int result; struct device *dev = &iface->dev; + if (iface->cur_altsetting->desc.bNumEndpoints < 3) + return -ENODEV; + result = wa_rpipes_create(wa); if (result < 0) goto error_rpipes_create; diff --git a/drivers/uwb/hwa-rc.c b/drivers/uwb/hwa-rc.c index 0257f35cfb9d..e75bbe5a10cd 100644 --- a/drivers/uwb/hwa-rc.c +++ b/drivers/uwb/hwa-rc.c @@ -825,6 +825,9 @@ static int hwarc_probe(struct usb_interface *iface, struct hwarc *hwarc; struct device *dev = &iface->dev; + if (iface->cur_altsetting->desc.bNumEndpoints < 1) + return -ENODEV; + result = -ENOMEM; uwb_rc = uwb_rc_alloc(); if (uwb_rc == NULL) { diff --git a/drivers/uwb/i1480/dfu/usb.c b/drivers/uwb/i1480/dfu/usb.c index 2bfc846ac071..6345e85822a4 100644 --- a/drivers/uwb/i1480/dfu/usb.c +++ b/drivers/uwb/i1480/dfu/usb.c @@ -362,6 +362,9 @@ int i1480_usb_probe(struct usb_interface *iface, const struct usb_device_id *id) result); } + if (iface->cur_altsetting->desc.bNumEndpoints < 1) + return -ENODEV; + result = -ENOMEM; i1480_usb = kzalloc(sizeof(*i1480_usb), GFP_KERNEL); if (i1480_usb == NULL) { diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c index 9982cb176ce8..830e2fd47642 100644 --- a/drivers/vfio/pci/vfio_pci.c +++ b/drivers/vfio/pci/vfio_pci.c @@ -562,8 +562,9 @@ static long vfio_pci_ioctl(void *device_data, } else if (cmd == VFIO_DEVICE_SET_IRQS) { struct vfio_irq_set hdr; + size_t size; u8 *data = NULL; - int ret = 0; + int max, ret = 0; minsz = offsetofend(struct vfio_irq_set, count); @@ -571,23 +572,31 @@ static long vfio_pci_ioctl(void *device_data, return -EFAULT; if (hdr.argsz < minsz || hdr.index >= VFIO_PCI_NUM_IRQS || + hdr.count >= (U32_MAX - hdr.start) || hdr.flags & ~(VFIO_IRQ_SET_DATA_TYPE_MASK | VFIO_IRQ_SET_ACTION_TYPE_MASK)) return -EINVAL; - if (!(hdr.flags & VFIO_IRQ_SET_DATA_NONE)) { - size_t size; - int max = vfio_pci_get_irq_count(vdev, hdr.index); + max = vfio_pci_get_irq_count(vdev, hdr.index); + if (hdr.start >= max || hdr.start + hdr.count > max) + return -EINVAL; - if (hdr.flags & VFIO_IRQ_SET_DATA_BOOL) - size = sizeof(uint8_t); - else if (hdr.flags & VFIO_IRQ_SET_DATA_EVENTFD) - size = sizeof(int32_t); - else - return -EINVAL; + switch (hdr.flags & VFIO_IRQ_SET_DATA_TYPE_MASK) { + case VFIO_IRQ_SET_DATA_NONE: + size = 0; + break; + case VFIO_IRQ_SET_DATA_BOOL: + size = sizeof(uint8_t); + break; + case VFIO_IRQ_SET_DATA_EVENTFD: + size = sizeof(int32_t); + break; + default: + return -EINVAL; + } - if (hdr.argsz - minsz < hdr.count * size || - hdr.start >= max || hdr.start + hdr.count > max) + if (size) { + if (hdr.argsz - minsz < hdr.count * size) return -EINVAL; data = memdup_user((void __user *)(arg + minsz), diff --git a/drivers/vfio/pci/vfio_pci_intrs.c b/drivers/vfio/pci/vfio_pci_intrs.c index 20e9a86d2dcf..5c8f767b6368 100644 --- a/drivers/vfio/pci/vfio_pci_intrs.c +++ b/drivers/vfio/pci/vfio_pci_intrs.c @@ -255,7 +255,7 @@ static int vfio_msi_enable(struct vfio_pci_device *vdev, int nvec, bool msix) if (!is_irq_none(vdev)) return -EINVAL; - vdev->ctx = kzalloc(nvec * sizeof(struct vfio_pci_irq_ctx), GFP_KERNEL); + vdev->ctx = kcalloc(nvec, sizeof(struct vfio_pci_irq_ctx), GFP_KERNEL); if (!vdev->ctx) return -ENOMEM; diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c b/drivers/vfio/vfio_iommu_spapr_tce.c index 0582b72ef377..1a9f18b40be6 100644 --- a/drivers/vfio/vfio_iommu_spapr_tce.c +++ b/drivers/vfio/vfio_iommu_spapr_tce.c @@ -511,6 +511,12 @@ static long tce_iommu_build_v2(struct tce_container *container, unsigned long hpa; enum dma_data_direction dirtmp; + if (!tbl->it_userspace) { + ret = tce_iommu_userspace_view_alloc(tbl); + if (ret) + return ret; + } + for (i = 0; i < pages; ++i) { struct mm_iommu_table_group_mem_t *mem = NULL; unsigned long *pua = IOMMU_TABLE_USERSPACE_ENTRY(tbl, @@ -584,15 +590,6 @@ static long tce_iommu_create_table(struct tce_container *container, WARN_ON(!ret && !(*ptbl)->it_ops->free); WARN_ON(!ret && ((*ptbl)->it_allocated_size != table_size)); - if (!ret && container->v2) { - ret = tce_iommu_userspace_view_alloc(*ptbl); - if (ret) - (*ptbl)->it_ops->free(*ptbl); - } - - if (ret) - decrement_locked_vm(table_size >> PAGE_SHIFT); - return ret; } @@ -1064,10 +1061,7 @@ static int tce_iommu_take_ownership(struct tce_container *container, if (!tbl || !tbl->it_map) continue; - rc = tce_iommu_userspace_view_alloc(tbl); - if (!rc) - rc = iommu_take_ownership(tbl); - + rc = iommu_take_ownership(tbl); if (rc) { for (j = 0; j < i; ++j) iommu_release_ownership( diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 6e92917ba77a..4e3c78d88832 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -1168,6 +1168,8 @@ static void fbcon_free_font(struct display *p, bool freefont) p->userfont = 0; } +static void set_vc_hi_font(struct vc_data *vc, bool set); + static void fbcon_deinit(struct vc_data *vc) { struct display *p = &fb_display[vc->vc_num]; @@ -1203,6 +1205,9 @@ finished: if (free_font) vc->vc_font.data = NULL; + if (vc->vc_hi_font_mask) + set_vc_hi_font(vc, false); + if (!con_is_bound(&fb_con)) fbcon_exit(); @@ -2439,32 +2444,10 @@ static int fbcon_get_font(struct vc_data *vc, struct console_font *font) return 0; } -static int fbcon_do_set_font(struct vc_data *vc, int w, int h, - const u8 * data, int userfont) +/* set/clear vc_hi_font_mask and update vc attrs accordingly */ +static void set_vc_hi_font(struct vc_data *vc, bool set) { - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; - struct fbcon_ops *ops = info->fbcon_par; - struct display *p = &fb_display[vc->vc_num]; - int resize; - int cnt; - char *old_data = NULL; - - if (CON_IS_VISIBLE(vc) && softback_lines) - fbcon_set_origin(vc); - - resize = (w != vc->vc_font.width) || (h != vc->vc_font.height); - if (p->userfont) - old_data = vc->vc_font.data; - if (userfont) - cnt = FNTCHARCNT(data); - else - cnt = 256; - vc->vc_font.data = (void *)(p->fontdata = data); - if ((p->userfont = userfont)) - REFCOUNT(data)++; - vc->vc_font.width = w; - vc->vc_font.height = h; - if (vc->vc_hi_font_mask && cnt == 256) { + if (!set) { vc->vc_hi_font_mask = 0; if (vc->vc_can_do_color) { vc->vc_complement_mask >>= 1; @@ -2487,7 +2470,7 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, ((c & 0xfe00) >> 1) | (c & 0xff); vc->vc_attr >>= 1; } - } else if (!vc->vc_hi_font_mask && cnt == 512) { + } else { vc->vc_hi_font_mask = 0x100; if (vc->vc_can_do_color) { vc->vc_complement_mask <<= 1; @@ -2519,8 +2502,38 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, } else vc->vc_video_erase_char = c & ~0x100; } - } +} + +static int fbcon_do_set_font(struct vc_data *vc, int w, int h, + const u8 * data, int userfont) +{ + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fbcon_ops *ops = info->fbcon_par; + struct display *p = &fb_display[vc->vc_num]; + int resize; + int cnt; + char *old_data = NULL; + + if (CON_IS_VISIBLE(vc) && softback_lines) + fbcon_set_origin(vc); + + resize = (w != vc->vc_font.width) || (h != vc->vc_font.height); + if (p->userfont) + old_data = vc->vc_font.data; + if (userfont) + cnt = FNTCHARCNT(data); + else + cnt = 256; + vc->vc_font.data = (void *)(p->fontdata = data); + if ((p->userfont = userfont)) + REFCOUNT(data)++; + vc->vc_font.width = w; + vc->vc_font.height = h; + if (vc->vc_hi_font_mask && cnt == 256) + set_vc_hi_font(vc, false); + else if (!vc->vc_hi_font_mask && cnt == 512) + set_vc_hi_font(vc, true); if (resize) { int cols, rows; diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile index f6f65ccce8e7..238e851c0705 100644 --- a/drivers/video/fbdev/Makefile +++ b/drivers/video/fbdev/Makefile @@ -126,7 +126,11 @@ obj-y += omap2/ obj-$(CONFIG_XEN_FBDEV_FRONTEND) += xen-fbfront.o obj-$(CONFIG_FB_CARMINE) += carminefb.o obj-$(CONFIG_FB_MB862XX) += mb862xx/ -obj-$(CONFIG_FB_MSM) += msm/ +ifeq ($(CONFIG_FB_MSM),y) +obj-y += msm/ +else +obj-$(CONFIG_MSM_DBA) += msm/msm_dba/ +endif obj-$(CONFIG_FB_NUC900) += nuc900fb.o obj-$(CONFIG_FB_JZ4740) += jz4740_fb.o obj-$(CONFIG_FB_PUV3_UNIGFX) += fb-puv3.o diff --git a/drivers/video/fbdev/msm/dsi_status_6g.c b/drivers/video/fbdev/msm/dsi_status_6g.c index 869ff1d9df37..d24b19ea77ad 100644 --- a/drivers/video/fbdev/msm/dsi_status_6g.c +++ b/drivers/video/fbdev/msm/dsi_status_6g.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, 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 @@ -17,6 +17,7 @@ #include "mdss_dsi.h" #include "mdss_mdp.h" +#include "mdss_debug.h" /* * mdss_check_te_status() - Check the status of panel for TE based ESD. @@ -157,6 +158,7 @@ void mdss_check_dsi_ctrl_status(struct work_struct *work, uint32_t interval) ctl->ops.wait_pingpong(ctl, NULL); pr_debug("%s: DSI ctrl wait for ping pong done\n", __func__); + MDSS_XLOG(mipi->mode); mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON); ret = ctrl_pdata->check_status(ctrl_pdata); diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h index 796246a856b4..1c984d02755e 100644 --- a/drivers/video/fbdev/msm/mdss.h +++ b/drivers/video/fbdev/msm/mdss.h @@ -45,6 +45,7 @@ enum mdss_mdp_clk_type { MDSS_CLK_MDP_LUT, MDSS_CLK_MDP_VSYNC, MDSS_CLK_MNOC_AHB, + MDSS_CLK_THROTTLE_AXI, MDSS_MAX_CLK }; diff --git a/drivers/video/fbdev/msm/mdss_compat_utils.c b/drivers/video/fbdev/msm/mdss_compat_utils.c index e9ba77501b38..2b9c71441d68 100644 --- a/drivers/video/fbdev/msm/mdss_compat_utils.c +++ b/drivers/video/fbdev/msm/mdss_compat_utils.c @@ -119,6 +119,9 @@ static unsigned int __do_compat_ioctl_nr(unsigned int cmd32) static void __copy_atomic_commit_struct(struct mdp_layer_commit *commit, struct mdp_layer_commit32 *commit32) { + unsigned int destSize = sizeof(commit->commit_v1.reserved); + unsigned int srcSize = sizeof(commit32->commit_v1.reserved); + unsigned int count = (destSize <= srcSize ? destSize : srcSize); commit->version = commit32->version; commit->commit_v1.flags = commit32->commit_v1.flags; commit->commit_v1.input_layer_cnt = @@ -127,7 +130,7 @@ static void __copy_atomic_commit_struct(struct mdp_layer_commit *commit, commit->commit_v1.right_roi = commit32->commit_v1.right_roi; commit->commit_v1.bl_level = commit32->commit_v1.bl_level; memcpy(&commit->commit_v1.reserved, &commit32->commit_v1.reserved, - sizeof(commit32->commit_v1.reserved)); + count); } static struct mdp_input_layer32 *__create_layer_list32( @@ -220,6 +223,7 @@ static struct mdp_input_layer *__create_layer_list( layer->flags = layer32->flags; layer->pipe_ndx = layer32->pipe_ndx; + layer->rect_num = layer32->rect_num; layer->horz_deci = layer32->horz_deci; layer->vert_deci = layer32->vert_deci; layer->z_order = layer32->z_order; diff --git a/drivers/video/fbdev/msm/mdss_compat_utils.h b/drivers/video/fbdev/msm/mdss_compat_utils.h index 4f44cd1c9471..b7fa401f52d2 100644 --- a/drivers/video/fbdev/msm/mdss_compat_utils.h +++ b/drivers/video/fbdev/msm/mdss_compat_utils.h @@ -515,7 +515,8 @@ struct mdp_input_layer32 { struct mdp_layer_buffer buffer; compat_caddr_t pp_info; int error_code; - uint32_t reserved[6]; + uint32_t rect_num; + uint32_t reserved[5]; }; struct mdp_output_layer32 { diff --git a/drivers/video/fbdev/msm/mdss_debug.c b/drivers/video/fbdev/msm/mdss_debug.c index ddb7a4c31f68..8cb6c7157230 100644 --- a/drivers/video/fbdev/msm/mdss_debug.c +++ b/drivers/video/fbdev/msm/mdss_debug.c @@ -1077,7 +1077,7 @@ static ssize_t mdss_debug_perf_bw_limit_read(struct file *file, struct mdss_data_type *mdata = file->private_data; struct mdss_max_bw_settings *temp_settings; int len = 0, i; - char buf[256]; + char buf[256] = {'\0'}; if (!mdata) return -ENODEV; diff --git a/drivers/video/fbdev/msm/mdss_debug_xlog.c b/drivers/video/fbdev/msm/mdss_debug_xlog.c index bf4117650e3c..aeefc81657b0 100644 --- a/drivers/video/fbdev/msm/mdss_debug_xlog.c +++ b/drivers/video/fbdev/msm/mdss_debug_xlog.c @@ -93,6 +93,48 @@ static inline bool mdss_xlog_is_enabled(u32 flag) (flag == MDSS_XLOG_ALL && mdss_dbg_xlog.xlog_enable); } +static void __halt_vbif_xin(void) +{ + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + + pr_err("Halting VBIF-XIN\n"); + MDSS_VBIF_WRITE(mdata, MMSS_VBIF_XIN_HALT_CTRL0, 0xFFFFFFFF, false); +} + +static void __halt_vbif_axi(void) +{ + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + + pr_err("Halting VBIF-AXI\n"); + MDSS_VBIF_WRITE(mdata, MMSS_VBIF_AXI_HALT_CTRL0, 0xFFFFFFFF, false); +} + +static void __dump_vbif_state(void) +{ + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + unsigned int reg_vbif_src_err, reg_vbif_err_info, + reg_vbif_xin_halt_ctrl0, reg_vbif_xin_halt_ctrl1, + reg_vbif_axi_halt_ctrl0, reg_vbif_axi_halt_ctrl1; + + reg_vbif_src_err = MDSS_VBIF_READ(mdata, + MMSS_VBIF_SRC_ERR, false); + reg_vbif_err_info = MDSS_VBIF_READ(mdata, + MMSS_VBIF_ERR_INFO, false); + reg_vbif_xin_halt_ctrl0 = MDSS_VBIF_READ(mdata, + MMSS_VBIF_XIN_HALT_CTRL0, false); + reg_vbif_xin_halt_ctrl1 = MDSS_VBIF_READ(mdata, + MMSS_VBIF_XIN_HALT_CTRL1, false); + reg_vbif_axi_halt_ctrl0 = MDSS_VBIF_READ(mdata, + MMSS_VBIF_AXI_HALT_CTRL0, false); + reg_vbif_axi_halt_ctrl1 = MDSS_VBIF_READ(mdata, + MMSS_VBIF_AXI_HALT_CTRL1, false); + pr_err("VBIF SRC_ERR=%x, ERR_INFO=%x\n", + reg_vbif_src_err, reg_vbif_err_info); + pr_err("VBIF XIN_HALT_CTRL0=%x, XIN_HALT_CTRL1=%x, AXI_HALT_CTRL0=%x, AXI_HALT_CTRL1=%x\n" + , reg_vbif_xin_halt_ctrl0, reg_vbif_xin_halt_ctrl1, + reg_vbif_axi_halt_ctrl0, reg_vbif_axi_halt_ctrl1); +} + void mdss_xlog(const char *name, int line, int flag, ...) { unsigned long flags; @@ -611,8 +653,17 @@ static void mdss_xlog_dump_array(struct mdss_debug_base *blk_arr[], mdss_dump_dsi_debug_bus(mdss_dbg_xlog.enable_dsi_dbgbus_dump, &mdss_dbg_xlog.dsi_dbgbus_dump); - if (dead && mdss_dbg_xlog.panic_on_err) + if (dead && mdss_dbg_xlog.panic_on_err) { + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON); + __dump_vbif_state(); + __halt_vbif_xin(); + usleep_range(10000, 10010); + __halt_vbif_axi(); + usleep_range(10000, 10010); + __dump_vbif_state(); + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF); panic(name); + } } static void xlog_debug_work(struct work_struct *work) diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c index 889fbe5c4aff..c7cac996e5c0 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.c +++ b/drivers/video/fbdev/msm/mdss_dsi.c @@ -662,7 +662,11 @@ static int mdss_dsi_get_dt_vreg_data(struct device *dev, mp->vreg_config[i].post_off_sleep = tmp; } - pr_debug("%s: %s min=%d, max=%d, enable=%d, disable=%d, preonsleep=%d, postonsleep=%d, preoffsleep=%d, postoffsleep=%d\n", + mp->vreg_config[i].lp_disable_allowed = + of_property_read_bool(supply_node, + "qcom,supply-lp-mode-disable-allowed"); + + pr_debug("%s: %s min=%d, max=%d, enable=%d, disable=%d, preonsleep=%d, postonsleep=%d, preoffsleep=%d, postoffsleep=%d lp_disable_allowed=%d\n", __func__, mp->vreg_config[i].vreg_name, mp->vreg_config[i].min_voltage, @@ -672,8 +676,8 @@ static int mdss_dsi_get_dt_vreg_data(struct device *dev, mp->vreg_config[i].pre_on_sleep, mp->vreg_config[i].post_on_sleep, mp->vreg_config[i].pre_off_sleep, - mp->vreg_config[i].post_off_sleep - ); + mp->vreg_config[i].post_off_sleep, + mp->vreg_config[i].lp_disable_allowed); ++i; } @@ -4360,6 +4364,9 @@ int dsi_panel_device_register(struct platform_device *ctrl_pdev, return rc; } + /* default state of gpio is false */ + ctrl_pdata->bklt_en_gpio_state = false; + pinfo->panel_max_fps = mdss_panel_get_framerate(pinfo); pinfo->panel_max_vtotal = mdss_panel_get_vtotal(pinfo); diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h index 335037860ffe..62d88f0af652 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.h +++ b/drivers/video/fbdev/msm/mdss_dsi.h @@ -454,6 +454,7 @@ struct mdss_dsi_ctrl_pdata { int disp_en_gpio; int bklt_en_gpio; bool bklt_en_gpio_invert; + bool bklt_en_gpio_state; int lcd_mode_sel_gpio; int bklt_ctrl; /* backlight ctrl */ bool pwm_pmi; diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c index 9faa1531c256..60012c71449c 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_panel.c +++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c @@ -260,16 +260,6 @@ static int mdss_dsi_request_gpios(struct mdss_dsi_ctrl_pdata *ctrl_pdata) rc); goto rst_gpio_err; } - if (gpio_is_valid(ctrl_pdata->bklt_en_gpio)) { - rc = gpio_request(ctrl_pdata->bklt_en_gpio, - "bklt_enable"); - if (rc) { - pr_err("request bklt gpio failed, rc=%d\n", - rc); - goto bklt_en_gpio_err; - } - } - if (gpio_is_valid(ctrl_pdata->lcd_mode_sel_gpio)) { rc = gpio_request(ctrl_pdata->lcd_mode_sel_gpio, "mode_sel"); if (rc) { @@ -282,9 +272,6 @@ static int mdss_dsi_request_gpios(struct mdss_dsi_ctrl_pdata *ctrl_pdata) return rc; lcd_mode_sel_gpio_err: - if (gpio_is_valid(ctrl_pdata->bklt_en_gpio)) - gpio_free(ctrl_pdata->bklt_en_gpio); -bklt_en_gpio_err: gpio_free(ctrl_pdata->rst_gpio); rst_gpio_err: if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) @@ -293,6 +280,81 @@ disp_en_gpio_err: return rc; } +int mdss_dsi_bl_gpio_ctrl(struct mdss_panel_data *pdata, int enable) +{ + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + int rc = 0, val = 0; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + if (ctrl_pdata == NULL) { + pr_err("%s: Invalid ctrl data\n", __func__); + return -EINVAL; + } + + /* if gpio is not valid */ + if (!gpio_is_valid(ctrl_pdata->bklt_en_gpio)) + return rc; + + pr_debug("%s: enable = %d\n", __func__, enable); + + /* + * if gpio state is false and enable (bl level) is + * non zero then toggle the gpio + */ + if (!ctrl_pdata->bklt_en_gpio_state && enable) { + rc = gpio_request(ctrl_pdata->bklt_en_gpio, "bklt_enable"); + if (rc) { + pr_err("request bklt gpio failed, rc=%d\n", rc); + goto free; + } + + if (ctrl_pdata->bklt_en_gpio_invert) + val = 0; + else + val = 1; + + rc = gpio_direction_output(ctrl_pdata->bklt_en_gpio, val); + if (rc) { + pr_err("%s: unable to set dir for bklt gpio val %d\n", + __func__, val); + goto free; + } + ctrl_pdata->bklt_en_gpio_state = true; + goto ret; + } else if (ctrl_pdata->bklt_en_gpio_state && !enable) { + /* + * if gpio state is true and enable (bl level) is + * zero then toggle the gpio + */ + if (ctrl_pdata->bklt_en_gpio_invert) + val = 1; + else + val = 0; + + rc = gpio_direction_output(ctrl_pdata->bklt_en_gpio, val); + if (rc) + pr_err("%s: unable to set dir for bklt gpio val %d\n", + __func__, val); + goto free; + } + + /* gpio state is true and bl level is non zero */ + goto ret; + +free: + pr_debug("%s: free bklt gpio\n", __func__); + ctrl_pdata->bklt_en_gpio_state = false; + gpio_free(ctrl_pdata->bklt_en_gpio); +ret: + return rc; +} + int mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable) { struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; @@ -362,26 +424,6 @@ int mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable) if (pdata->panel_info.rst_seq[++i]) usleep_range(pinfo->rst_seq[i] * 1000, pinfo->rst_seq[i] * 1000); } - - if (gpio_is_valid(ctrl_pdata->bklt_en_gpio)) { - - if (ctrl_pdata->bklt_en_gpio_invert) { - rc = gpio_direction_output( - ctrl_pdata->bklt_en_gpio, 0); - gpio_set_value( - (ctrl_pdata->bklt_en_gpio), 0); - } else { - rc = gpio_direction_output( - ctrl_pdata->bklt_en_gpio, 1); - gpio_set_value( - (ctrl_pdata->bklt_en_gpio), 1); - } - if (rc) { - pr_err("%s: unable to set dir for bklt gpio\n", - __func__); - goto exit; - } - } } if (gpio_is_valid(ctrl_pdata->lcd_mode_sel_gpio)) { @@ -410,15 +452,6 @@ int mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable) pr_debug("%s: Reset panel done\n", __func__); } } else { - if (gpio_is_valid(ctrl_pdata->bklt_en_gpio)) { - - if (ctrl_pdata->bklt_en_gpio_invert) - gpio_set_value((ctrl_pdata->bklt_en_gpio), 1); - else - gpio_set_value((ctrl_pdata->bklt_en_gpio), 0); - - gpio_free(ctrl_pdata->bklt_en_gpio); - } if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) { gpio_set_value((ctrl_pdata->disp_en_gpio), 0); gpio_free(ctrl_pdata->disp_en_gpio); @@ -801,6 +834,9 @@ static void mdss_dsi_panel_bl_ctrl(struct mdss_panel_data *pdata, if ((bl_level < pdata->panel_info.bl_min) && (bl_level != 0)) bl_level = pdata->panel_info.bl_min; + /* enable the backlight gpio if present */ + mdss_dsi_bl_gpio_ctrl(pdata, bl_level); + switch (ctrl_pdata->bklt_ctrl) { case BL_WLED: led_trigger_event(bl_led_trigger, bl_level); diff --git a/drivers/video/fbdev/msm/mdss_dsi_phy.h b/drivers/video/fbdev/msm/mdss_dsi_phy.h index 5fff3123b63f..03df17d81f69 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_phy.h +++ b/drivers/video/fbdev/msm/mdss_dsi_phy.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, 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 @@ -115,4 +115,13 @@ int mdss_dsi_phy_v3_wait_for_lanes_stop_state(struct mdss_dsi_ctrl_pdata *ctrl, * assumes that the link and core clocks are already on. */ int mdss_dsi_phy_v3_ulps_config(struct mdss_dsi_ctrl_pdata *ctrl, bool enable); + +/** + * mdss_dsi_phy_v3_idle_pc_exit() - Called after Idle Power Collapse exit + * @ctrl: pointer to DSI controller structure + * + * This function is called after Idle Power Collapse, so driver + * can perform any sequence required after the Idle PC exit. + */ +void mdss_dsi_phy_v3_idle_pc_exit(struct mdss_dsi_ctrl_pdata *ctrl); #endif /* MDSS_DSI_PHY_H */ diff --git a/drivers/video/fbdev/msm/mdss_dsi_phy_v3.c b/drivers/video/fbdev/msm/mdss_dsi_phy_v3.c index 992fd51606ca..bb9dbfa32e4e 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_phy_v3.c +++ b/drivers/video/fbdev/msm/mdss_dsi_phy_v3.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, 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 @@ -327,6 +327,7 @@ int mdss_dsi_phy_v3_ulps_config(struct mdss_dsi_ctrl_pdata *ctrl, bool enable) */ DSI_PHY_W32(ctrl->phy_io.base, CMN_DSI_LANE_CTRL3, active_lanes); + usleep_range(5, 15); DSI_PHY_W32(ctrl->phy_io.base, CMN_DSI_LANE_CTRL3, 0); @@ -340,6 +341,20 @@ error: return rc; } +void mdss_dsi_phy_v3_idle_pc_exit(struct mdss_dsi_ctrl_pdata *ctrl) +{ + u32 val = BIT(5); + u32 data; + + /* Reset phy pll after idle pc exit */ + data = DSI_PHY_R32(ctrl->phy_io.base, CMN_CTRL_1); + DSI_PHY_W32(ctrl->phy_io.base, CMN_CTRL_1, data | val); + usleep_range(10, 15); + + data = DSI_PHY_R32(ctrl->phy_io.base, CMN_CTRL_1); + data &= ~(BIT(5)); + DSI_PHY_W32(ctrl->phy_io.base, CMN_CTRL_1, data); +} int mdss_dsi_phy_v3_shutdown(struct mdss_dsi_ctrl_pdata *ctrl) { diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c index 698c5633cf6a..4eca9cb39223 100644 --- a/drivers/video/fbdev/msm/mdss_fb.c +++ b/drivers/video/fbdev/msm/mdss_fb.c @@ -2121,6 +2121,7 @@ static int mdss_fb_blank(int blank_mode, struct fb_info *info) mdss_mdp_enable_panel_disable_mode(mfd, false); ret = mdss_fb_blank_sub(blank_mode, info, mfd->op_enable); + MDSS_XLOG(blank_mode); end: mutex_unlock(&mfd->mdss_sysfs_lock); diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.c b/drivers/video/fbdev/msm/mdss_hdmi_edid.c index 599f6cb44c63..102c22cba7dd 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_edid.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.c @@ -1283,6 +1283,7 @@ static void hdmi_edid_extract_speaker_allocation_data( static void hdmi_edid_extract_sink_caps(struct hdmi_edid_ctrl *edid_ctrl, const u8 *in_buf) { + u8 len; const u8 *vsd = NULL; if (!edid_ctrl) { @@ -1297,13 +1298,29 @@ static void hdmi_edid_extract_sink_caps(struct hdmi_edid_ctrl *edid_ctrl, edid_ctrl->basic_audio_supp = false; pr_debug("%s: basic audio supported: %s\n", __func__, edid_ctrl->basic_audio_supp ? "true" : "false"); + vsd = hdmi_edid_find_block(in_buf, DBC_START_OFFSET, + VENDOR_SPECIFIC_DATA_BLOCK, &len); + + if (vsd == NULL || len == 0 || len > MAX_DATA_BLOCK_SIZE) + return; + + /* Max TMDS clock is in multiples of 5Mhz. */ + edid_ctrl->sink_caps.max_pclk_in_hz = vsd[7] * 5000000; vsd = hdmi_edid_find_hfvsdb(in_buf); if (vsd) { - /* Max pixel clock is in multiples of 5Mhz. */ - edid_ctrl->sink_caps.max_pclk_in_hz = - vsd[5]*5000000; + /* + * HF-VSDB define larger TMDS clock than VSDB. If sink + * supports TMDS Character Rates > 340M, the sink shall + * set Max_TMDS_Character_Rates appropriately and non-zero. + * Or, if sink dose not support TMDS Character Rates > 340M, + * the sink shall set this filed to 0. The max TMDS support + * clock Rate = Max_TMDS_Character_Rates * 5Mhz. + */ + if (vsd[5] != 0) + edid_ctrl->sink_caps.max_pclk_in_hz = + vsd[5] * 5000000; edid_ctrl->sink_caps.scdc_present = (vsd[6] & 0x80) ? true : false; edid_ctrl->sink_caps.scramble_support = @@ -2425,6 +2442,25 @@ bool hdmi_edid_is_dvi_mode(void *input) } /** + * hdmi_edid_get_sink_caps_max_tmds_clk() - get max tmds clock supported. + * Sink side's limitation should be concerned as well. + * @input: edid parser data + * + * Return: max tmds clock + */ +u32 hdmi_edid_get_sink_caps_max_tmds_clk(void *input) +{ + struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input; + + if (!edid_ctrl) { + DEV_ERR("%s: invalid input\n", __func__); + return 0; + } + + return edid_ctrl->sink_caps.max_pclk_in_hz; +} + +/** * hdmi_edid_get_deep_color() - get deep color info supported by sink * @input: edid parser data * diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.h b/drivers/video/fbdev/msm/mdss_hdmi_edid.h index 557e9326a81d..af802bb45f89 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_edid.h +++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.h @@ -81,5 +81,6 @@ void hdmi_edid_config_override(void *input, bool enable, struct hdmi_edid_override_data *data); void hdmi_edid_set_max_pclk_rate(void *input, u32 max_pclk_khz); bool hdmi_edid_is_audio_supported(void *input); +u32 hdmi_edid_get_sink_caps_max_tmds_clk(void *input); #endif /* __HDMI_EDID_H__ */ diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c index 42845f9ff192..8fa229aaa174 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c @@ -2224,6 +2224,14 @@ static int hdmi_tx_read_sink_info(struct hdmi_tx_ctrl *hdmi_ctrl) status = hdmi_edid_parser(data); if (status) DEV_ERR("%s: edid parse failed\n", __func__); + else + /* + * Updata HDMI max supported TMDS clock, consider + * both sink and source capicity. + */ + hdmi_edid_set_max_pclk_rate(data, + min(hdmi_edid_get_sink_caps_max_tmds_clk(data) / 1000, + hdmi_ctrl->max_pclk_khz)); } bail: if (hdmi_tx_enable_power(hdmi_ctrl, HDMI_TX_DDC_PM, false)) diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c index a645a3495593..d88d87bd2092 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.c +++ b/drivers/video/fbdev/msm/mdss_mdp.c @@ -1373,7 +1373,9 @@ static inline void __mdss_mdp_reg_access_clk_enable( mdss_mdp_clk_update(MDSS_CLK_AHB, 1); mdss_mdp_clk_update(MDSS_CLK_AXI, 1); mdss_mdp_clk_update(MDSS_CLK_MDP_CORE, 1); + mdss_mdp_clk_update(MDSS_CLK_THROTTLE_AXI, 1); } else { + mdss_mdp_clk_update(MDSS_CLK_THROTTLE_AXI, 0); mdss_mdp_clk_update(MDSS_CLK_MDP_CORE, 0); mdss_mdp_clk_update(MDSS_CLK_AXI, 0); mdss_mdp_clk_update(MDSS_CLK_AHB, 0); @@ -1415,6 +1417,7 @@ static void __mdss_mdp_clk_control(struct mdss_data_type *mdata, bool enable) mdss_mdp_clk_update(MDSS_CLK_AXI, 1); mdss_mdp_clk_update(MDSS_CLK_MDP_CORE, 1); mdss_mdp_clk_update(MDSS_CLK_MDP_LUT, 1); + mdss_mdp_clk_update(MDSS_CLK_THROTTLE_AXI, 1); if (mdata->vsync_ena) mdss_mdp_clk_update(MDSS_CLK_MDP_VSYNC, 1); } else { @@ -1430,6 +1433,7 @@ static void __mdss_mdp_clk_control(struct mdss_data_type *mdata, bool enable) mdss_mdp_clk_update(MDSS_CLK_AXI, 0); mdss_mdp_clk_update(MDSS_CLK_AHB, 0); mdss_mdp_clk_update(MDSS_CLK_MNOC_AHB, 0); + mdss_mdp_clk_update(MDSS_CLK_THROTTLE_AXI, 0); /* release iommu control */ mdss_iommu_ctrl(0); @@ -1915,8 +1919,7 @@ static int mdss_mdp_irq_clk_setup(struct mdss_data_type *mdata) if (mdss_mdp_irq_clk_register(mdata, "bus_clk", MDSS_CLK_AXI) || mdss_mdp_irq_clk_register(mdata, "iface_clk", MDSS_CLK_AHB) || - mdss_mdp_irq_clk_register(mdata, "core_clk", - MDSS_CLK_MDP_CORE)) + mdss_mdp_irq_clk_register(mdata, "core_clk", MDSS_CLK_MDP_CORE)) return -EINVAL; /* lut_clk is not present on all MDSS revisions */ @@ -1928,6 +1931,10 @@ static int mdss_mdp_irq_clk_setup(struct mdss_data_type *mdata) /* this clk is not present on all MDSS revisions */ mdss_mdp_irq_clk_register(mdata, "mnoc_clk", MDSS_CLK_MNOC_AHB); + /* this clk is not present on all MDSS revisions */ + mdss_mdp_irq_clk_register(mdata, "throttle_bus_clk", + MDSS_CLK_THROTTLE_AXI); + /* Setting the default clock rate to the max supported.*/ mdss_mdp_set_clk_rate(mdata->max_mdp_clk_rate, false); pr_debug("mdp clk rate=%ld\n", diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h index a2139f495f52..fd2c2cdb3820 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.h +++ b/drivers/video/fbdev/msm/mdss_mdp.h @@ -2023,6 +2023,8 @@ void mdss_mdp_set_supported_formats(struct mdss_data_type *mdata); int mdss_mdp_dest_scaler_setup_locked(struct mdss_mdp_mixer *mixer); void *mdss_mdp_intf_get_ctx_base(struct mdss_mdp_ctl *ctl, int intf_num); +int mdss_mdp_mixer_get_hw_num(struct mdss_mdp_mixer *mixer); + #ifdef CONFIG_FB_MSM_MDP_NONE struct mdss_data_type *mdss_mdp_get_mdata(void) { diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c index 2968d883c8cb..c062de3c1e59 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c +++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c @@ -4802,7 +4802,7 @@ static void __mdss_mdp_mixer_get_offsets(u32 mixer_num, offsets[2] = MDSS_MDP_REG_CTL_LAYER_EXTN2(mixer_num); } -static inline int __mdss_mdp_mixer_get_hw_num(struct mdss_mdp_mixer *mixer) +int mdss_mdp_mixer_get_hw_num(struct mdss_mdp_mixer *mixer) { struct mdss_data_type *mdata = mdss_mdp_get_mdata(); @@ -4858,7 +4858,7 @@ static void __mdss_mdp_mixer_write_cfg(struct mdss_mdp_mixer *mixer, if (!mixer) return; - mixer_num = __mdss_mdp_mixer_get_hw_num(mixer); + mixer_num = mdss_mdp_mixer_get_hw_num(mixer); if (cfg) { for (i = 0; i < NUM_MIXERCFG_REGS; i++) @@ -4905,7 +4905,7 @@ bool mdss_mdp_mixer_reg_has_pipe(struct mdss_mdp_mixer *mixer, memset(&mixercfg, 0, sizeof(mixercfg)); - mixer_num = __mdss_mdp_mixer_get_hw_num(mixer); + mixer_num = mdss_mdp_mixer_get_hw_num(mixer); __mdss_mdp_mixer_get_offsets(mixer_num, offs, NUM_MIXERCFG_REGS); for (i = 0; i < NUM_MIXERCFG_REGS; i++) @@ -5130,7 +5130,7 @@ static void mdss_mdp_mixer_setup(struct mdss_mdp_ctl *master_ctl, mixercfg.cursor_enabled = true; update_mixer: - mixer_num = __mdss_mdp_mixer_get_hw_num(mixer_hw); + mixer_num = mdss_mdp_mixer_get_hw_num(mixer_hw); ctl_hw->flush_bits |= BIT(mixer_num < 5 ? 6 + mixer_num : 20); /* Read GC enable/disable status on LM */ @@ -5775,6 +5775,7 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg, mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON); if (ctl->ops.avr_ctrl_fnc) { + /* avr_ctrl_fnc will configure both master & slave */ ret = ctl->ops.avr_ctrl_fnc(ctl, true); if (ret) { pr_err("error configuring avr ctrl registers ctl=%d err=%d\n", @@ -5784,16 +5785,6 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg, } } - if (sctl && sctl->ops.avr_ctrl_fnc) { - ret = sctl->ops.avr_ctrl_fnc(sctl, true); - if (ret) { - pr_err("error configuring avr ctrl registers sctl=%d err=%d\n", - sctl->num, ret); - mutex_unlock(&ctl->lock); - return ret; - } - } - mutex_lock(&ctl->flush_lock); /* @@ -6046,6 +6037,10 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg, ctl_flush_bits |= ctl->flush_bits; ATRACE_BEGIN("flush_kickoff"); + + MDSS_XLOG(ctl->intf_num, ctl_flush_bits, sctl_flush_bits, + mdss_mdp_ctl_read(ctl, MDSS_MDP_REG_CTL_FLUSH), split_lm_valid); + mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_FLUSH, ctl_flush_bits); if (sctl) { if (sctl_flush_bits) { @@ -6057,8 +6052,6 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg, } ctl->commit_in_progress = false; - MDSS_XLOG(ctl->intf_num, ctl_flush_bits, sctl_flush_bits, - split_lm_valid); wmb(); ctl->flush_reg_data = ctl_flush_bits; ctl->flush_bits = 0; @@ -6085,8 +6078,9 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg, /* update backlight in commit */ if (mdss_mdp_handle_backlight_extn(ctl)) { - if (!IS_CALIB_MODE_BL(ctl->mfd) && (!ctl->mfd->ext_bl_ctrl || - !ctl->mfd->bl_level)) { + if (ctl->mfd && !IS_CALIB_MODE_BL(ctl->mfd) && + (!ctl->mfd->ext_bl_ctrl || + !ctl->mfd->bl_level)) { mutex_lock(&ctl->mfd->bl_lock); mdss_fb_set_backlight(ctl->mfd, ctl->mfd->bl_extn_level); diff --git a/drivers/video/fbdev/msm/mdss_mdp_debug.c b/drivers/video/fbdev/msm/mdss_mdp_debug.c index 1ad6810a6bb6..1035d23fe9ce 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_debug.c +++ b/drivers/video/fbdev/msm/mdss_mdp_debug.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2017, 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 @@ -1757,6 +1757,8 @@ void mdss_mdp_hw_rev_debug_caps_init(struct mdss_data_type *mdata) break; case MDSS_MDP_HW_REV_300: case MDSS_MDP_HW_REV_301: + case MDSS_MDP_HW_REV_320: + case MDSS_MDP_HW_REV_330: mdata->dbg_bus = dbg_bus_msm8998; mdata->dbg_bus_size = ARRAY_SIZE(dbg_bus_msm8998); mdata->vbif_dbg_bus = vbif_dbg_bus_msm8998; diff --git a/drivers/video/fbdev/msm/mdss_mdp_hwio.h b/drivers/video/fbdev/msm/mdss_mdp_hwio.h index d9e2b042bfc3..78bfab7d8ce8 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_hwio.h +++ b/drivers/video/fbdev/msm/mdss_mdp_hwio.h @@ -829,6 +829,8 @@ enum mdss_mdp_pingpong_index { #define MMSS_VBIF_WR_LIM_CONF 0x0C0 #define MDSS_VBIF_WRITE_GATHER_EN 0x0AC +#define MMSS_VBIF_SRC_ERR 0x194 +#define MMSS_VBIF_ERR_INFO 0x1A0 #define MMSS_VBIF_XIN_HALT_CTRL0 0x200 #define MMSS_VBIF_XIN_HALT_CTRL1 0x204 #define MMSS_VBIF_AXI_HALT_CTRL0 0x208 diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c index 13c70822e266..587150bbc9fa 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c @@ -72,6 +72,7 @@ struct mdss_mdp_video_ctx { u8 ref_cnt; u8 timegen_en; + bool timegen_flush_pending; bool polling_en; u32 poll_cnt; struct completion vsync_comp; @@ -451,13 +452,32 @@ static int mdss_mdp_video_intf_recovery(void *data, int event) } } +static int mdss_mdp_video_wait_one_frame(struct mdss_mdp_ctl *ctl) +{ + u32 frame_time, frame_rate; + int ret = 0; + struct mdss_panel_data *pdata = ctl->panel_data; + + if (pdata == NULL) { + frame_rate = DEFAULT_FRAME_RATE; + } else { + frame_rate = mdss_panel_get_framerate(&pdata->panel_info); + if (!(frame_rate >= 24 && frame_rate <= 240)) + frame_rate = 24; + } + + frame_time = ((1000/frame_rate) + 1); + + msleep(frame_time); + + return ret; +} + static void mdss_mdp_video_avr_vtotal_setup(struct mdss_mdp_ctl *ctl, struct intf_timing_params *p, struct mdss_mdp_video_ctx *ctx) { struct mdss_data_type *mdata = ctl->mdata; - struct mdss_mdp_ctl *sctl = NULL; - struct mdss_mdp_video_ctx *sctx = NULL; if (test_bit(MDSS_CAPS_AVR_SUPPORTED, mdata->mdss_caps_map)) { struct mdss_panel_data *pdata = ctl->panel_data; @@ -484,14 +504,11 @@ static void mdss_mdp_video_avr_vtotal_setup(struct mdss_mdp_ctl *ctl, /* * Make sure config goes through + * and queue timegen flush */ wmb(); - sctl = mdss_mdp_get_split_ctl(ctl); - if (sctl) - sctx = (struct mdss_mdp_video_ctx *) - sctl->intf_ctx[MASTER_CTX]; - mdss_mdp_video_timegen_flush(ctl, sctx); + ctx->timegen_flush_pending = true; MDSS_XLOG(pinfo->min_fps, pinfo->default_fps, avr_vtotal); } @@ -687,8 +704,10 @@ static void mdss_mdp_video_timegen_flush(struct mdss_mdp_ctl *ctl, ctl_flush |= (BIT(31) >> (sctx->intf_num - MDSS_MDP_INTF0)); } + MDSS_XLOG(ctl->intf_num, sctx?sctx->intf_num:0xf00, ctl_flush, + mdss_mdp_ctl_read(ctl, MDSS_MDP_REG_CTL_FLUSH)); mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_FLUSH, ctl_flush); - MDSS_XLOG(ctl->intf_num, sctx?sctx->intf_num:0xf00, ctl_flush); + } static inline void video_vsync_irq_enable(struct mdss_mdp_ctl *ctl, bool clear) @@ -2464,25 +2483,41 @@ static int mdss_mdp_video_early_wake_up(struct mdss_mdp_ctl *ctl) static int mdss_mdp_video_avr_ctrl(struct mdss_mdp_ctl *ctl, bool enable) { struct mdss_mdp_video_ctx *ctx = NULL, *sctx = NULL; + struct mdss_mdp_ctl *sctl; ctx = (struct mdss_mdp_video_ctx *) ctl->intf_ctx[MASTER_CTX]; if (!ctx || !ctx->ref_cnt) { pr_err("invalid master ctx\n"); return -EINVAL; } - mdss_mdp_video_avr_ctrl_setup(ctx, ctl, ctl->is_master, - enable); - if (is_pingpong_split(ctl->mfd)) { + sctl = mdss_mdp_get_split_ctl(ctl); + if (sctl) { + sctx = (struct mdss_mdp_video_ctx *) sctl->intf_ctx[MASTER_CTX]; + } else if (is_pingpong_split(ctl->mfd)) { sctx = (struct mdss_mdp_video_ctx *) ctl->intf_ctx[SLAVE_CTX]; if (!sctx || !sctx->ref_cnt) { pr_err("invalid slave ctx\n"); return -EINVAL; } - mdss_mdp_video_avr_ctrl_setup(sctx, ctl, false, - enable); } + if (ctx->timegen_flush_pending) { + mdss_mdp_video_timegen_flush(ctl, sctx); + + /* wait a frame for flush to be completed */ + mdss_mdp_video_wait_one_frame(ctl); + + ctx->timegen_flush_pending = false; + if (sctx) + sctx->timegen_flush_pending = false; + } + + mdss_mdp_video_avr_ctrl_setup(ctx, ctl, ctl->is_master, enable); + + if (sctx) + mdss_mdp_video_avr_ctrl_setup(sctx, ctl, false, enable); + return 0; } diff --git a/drivers/video/fbdev/msm/mdss_mdp_layer.c b/drivers/video/fbdev/msm/mdss_mdp_layer.c index 09a34223c2a5..472f1e8e8e3b 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_layer.c +++ b/drivers/video/fbdev/msm/mdss_mdp_layer.c @@ -2160,115 +2160,157 @@ static int __multirect_validate_mode(struct msm_fb_data_type *mfd, return 0; } -static int __update_multirect_info(struct msm_fb_data_type *mfd, - struct mdss_mdp_validate_info_t *validate_info_list, - struct mdp_input_layer *layer_list, int ndx, int layer_cnt) +/* + * linear search for a layer with given source pipe and rectangle number. + * If rectangle number is invalid, it's dropped from search criteria + */ +static int find_layer(enum mdss_mdp_sspp_index pnum, + int rect_num, + struct mdp_input_layer *layer_list, + size_t layer_cnt, int start_index) { - struct mdss_data_type *mdata = mdss_mdp_get_mdata(); - struct mdss_mdp_validate_info_t *vinfo[MDSS_MDP_PIPE_MAX_RECTS]; - int i, ptype, max_rects, mode; - int cnt = 1; + int i; - mode = __multirect_layer_flags_to_mode(layer_list[ndx].flags); - if (IS_ERR_VALUE(mode)) - return mode; + if (start_index < 0) + start_index = 0; - pr_debug("layer #%d pipe_ndx=%d multirect mode=%d\n", - ndx, layer_list[ndx].pipe_ndx, mode); + if (start_index >= layer_cnt) + return -EINVAL; - vinfo[0] = &validate_info_list[ndx]; - vinfo[0]->layer = &layer_list[ndx]; - vinfo[0]->multirect.mode = mode; - vinfo[0]->multirect.num = MDSS_MDP_PIPE_RECT0; - vinfo[0]->multirect.next = NULL; + for (i = start_index; i < layer_cnt; i++) { + if (get_pipe_num_from_ndx(layer_list[i].pipe_ndx) == pnum && + (rect_num < MDSS_MDP_PIPE_RECT0 || + rect_num >= MDSS_MDP_PIPE_MAX_RECTS || + layer_list[i].rect_num == rect_num)) + return i; + } - /* nothing to be done if multirect is disabled */ - if (mode == MDSS_MDP_PIPE_MULTIRECT_NONE) - return cnt; + return -ENOENT; /* no match found */ +} - ptype = get_pipe_type_from_ndx(layer_list[ndx].pipe_ndx); - if (ptype == MDSS_MDP_PIPE_TYPE_INVALID) { - pr_err("invalid pipe ndx %d\n", layer_list[ndx].pipe_ndx); - return -EINVAL; - } +static int __validate_multirect_param(struct msm_fb_data_type *mfd, + struct mdss_mdp_validate_info_t *validate_info_list, + struct mdp_input_layer *layer_list, + int ndx, size_t layer_count) +{ + int multirect_mode; + int pnum; + int rect_num; + + /* populate v_info with default values */ + validate_info_list[ndx].layer = &layer_list[ndx]; + validate_info_list[ndx].multirect.max_rects = MDSS_MDP_PIPE_MAX_RECTS; + validate_info_list[ndx].multirect.next = NULL; + validate_info_list[ndx].multirect.num = MDSS_MDP_PIPE_RECT0; + validate_info_list[ndx].multirect.mode = MDSS_MDP_PIPE_MULTIRECT_NONE; + + multirect_mode = __multirect_layer_flags_to_mode( + layer_list[ndx].flags); + if (IS_ERR_VALUE(multirect_mode)) + return multirect_mode; - max_rects = mdata->rects_per_sspp[ptype] ? : 1; + /* nothing to be done if multirect is disabled */ + if (multirect_mode == MDSS_MDP_PIPE_MULTIRECT_NONE) + return 0; - for (i = ndx + 1; i < layer_cnt; i++) { - if (layer_list[ndx].pipe_ndx == layer_list[i].pipe_ndx) { - if (cnt >= max_rects) { - pr_err("more than %d layers of type %d with same pipe_ndx=%d indexes=%d %d\n", - max_rects, ptype, - layer_list[ndx].pipe_ndx, ndx, i); - return -EINVAL; - } + validate_info_list[ndx].multirect.mode = multirect_mode; - mode = __multirect_layer_flags_to_mode( - layer_list[i].flags); - if (IS_ERR_VALUE(mode)) - return mode; + pnum = get_pipe_num_from_ndx(layer_list[ndx].pipe_ndx); + if (get_pipe_type_from_num(pnum) != MDSS_MDP_PIPE_TYPE_DMA) { + pr_err("Multirect not supported on pipe ndx 0x%x\n", + layer_list[ndx].pipe_ndx); + return -EINVAL; + } - if (mode != vinfo[0]->multirect.mode) { - pr_err("unable to set different multirect modes for pipe_ndx=%d (%d %d)\n", - layer_list[ndx].pipe_ndx, ndx, i); - return -EINVAL; - } + rect_num = layer_list[ndx].rect_num; + if (rect_num >= MDSS_MDP_PIPE_MAX_RECTS) + return -EINVAL; + validate_info_list[ndx].multirect.num = rect_num; - pr_debug("found matching pair for pipe_ndx=%d (%d %d)\n", - layer_list[i].pipe_ndx, ndx, i); + return 0; +} - vinfo[cnt] = &validate_info_list[i]; - vinfo[cnt]->multirect.num = cnt; - vinfo[cnt]->multirect.next = vinfo[0]->layer; - vinfo[cnt]->multirect.mode = mode; - vinfo[cnt]->layer = &layer_list[i]; +static int __update_multirect_info(struct msm_fb_data_type *mfd, + struct mdss_mdp_validate_info_t *validate_info_list, + struct mdp_input_layer *layer_list, + int ndx, size_t layer_cnt, int is_rect_num_valid) +{ + int ret; + int pair_rect_num = -1; + int pair_index; + + if (!is_rect_num_valid) + layer_list[ndx].rect_num = MDSS_MDP_PIPE_RECT0; + + ret = __validate_multirect_param(mfd, validate_info_list, + layer_list, ndx, layer_cnt); + /* return if we hit error or multirectangle mode is disabled. */ + if (IS_ERR_VALUE(ret) || + (!ret && validate_info_list[ndx].multirect.mode == + MDSS_MDP_PIPE_MULTIRECT_NONE)) + return ret; - vinfo[cnt - 1]->multirect.next = vinfo[cnt]->layer; - cnt++; - } - } + if (is_rect_num_valid) + pair_rect_num = (validate_info_list[ndx].multirect.num == + MDSS_MDP_PIPE_RECT0) ? MDSS_MDP_PIPE_RECT1 : + MDSS_MDP_PIPE_RECT0; - if (cnt == 1) { - pr_err("multirect mode enabled but unable to find extra rects for pipe_ndx=%x\n", + pair_index = find_layer(get_pipe_num_from_ndx( + layer_list[ndx].pipe_ndx), pair_rect_num, + layer_list, layer_cnt, ndx + 1); + if (IS_ERR_VALUE(pair_index)) { + pr_err("Multirect pair not found for pipe ndx 0x%x\n", layer_list[ndx].pipe_ndx); return -EINVAL; } - return cnt; + if (!is_rect_num_valid) + layer_list[pair_index].rect_num = MDSS_MDP_PIPE_RECT1; + + ret = __validate_multirect_param(mfd, validate_info_list, + layer_list, pair_index, layer_cnt); + if (IS_ERR_VALUE(ret) || + (validate_info_list[ndx].multirect.mode != + validate_info_list[pair_index].multirect.mode)) + return -EINVAL; + + validate_info_list[ndx].multirect.next = &layer_list[pair_index]; + validate_info_list[pair_index].multirect.next = &layer_list[ndx]; + + return 0; } static int __validate_multirect(struct msm_fb_data_type *mfd, - struct mdss_mdp_validate_info_t *validate_info_list, - struct mdp_input_layer *layer_list, int ndx, int layer_cnt) + struct mdss_mdp_validate_info_t *validate_info_list, + struct mdp_input_layer *layer_list, + int ndx, size_t layer_cnt, int is_rect_num_valid) { - struct mdp_input_layer *layers[MDSS_MDP_PIPE_MAX_RECTS] = { 0 }; - int i, cnt, rc; - - cnt = __update_multirect_info(mfd, validate_info_list, - layer_list, ndx, layer_cnt); - if (IS_ERR_VALUE(cnt)) - return cnt; - - if (cnt <= 1) { - /* nothing to validate in single rect mode */ - return 0; - } else if (cnt > 2) { - pr_err("unsupported multirect configuration, multirect cnt=%d\n", - cnt); - return -EINVAL; - } + int ret; + int i; + struct mdp_input_layer *layers[MDSS_MDP_PIPE_MAX_RECTS]; + struct mdp_input_layer *pair_layer; + + ret = __update_multirect_info(mfd, validate_info_list, + layer_list, ndx, layer_cnt, is_rect_num_valid); + /* return if we hit error or multirectangle mode is disabled. */ + if (IS_ERR_VALUE(ret) || + (!ret && validate_info_list[ndx].multirect.mode == + MDSS_MDP_PIPE_MULTIRECT_NONE)) + return ret; - layers[0] = validate_info_list[ndx].layer; - layers[1] = validate_info_list[ndx].multirect.next; + layers[validate_info_list[ndx].multirect.num] = &layer_list[ndx]; + pair_layer = validate_info_list[ndx].multirect.next; + layers[pair_layer->rect_num] = pair_layer; + /* check against smart DMA v1.0 restrictions */ for (i = 0; i < ARRAY_SIZE(__multirect_validators); i++) { - if (!__multirect_validators[i](layers, cnt)) + if (!__multirect_validators[i](layers, + MDSS_MDP_PIPE_MAX_RECTS)) return -EINVAL; } - - rc = __multirect_validate_mode(mfd, layers, cnt); - if (IS_ERR_VALUE(rc)) - return rc; + ret = __multirect_validate_mode(mfd, layers, MDSS_MDP_PIPE_MAX_RECTS); + if (IS_ERR_VALUE(ret)) + return ret; return 0; } @@ -2416,14 +2458,14 @@ static int __validate_layers(struct msm_fb_data_type *mfd, if (!validate_info_list[i].layer) { ret = __validate_multirect(mfd, validate_info_list, - layer_list, i, layer_count); + layer_list, i, layer_count, + !!(commit->flags & MDP_COMMIT_RECT_NUM)); if (ret) { pr_err("error validating multirect config. ret=%d i=%d\n", ret, i); goto end; } } - rect_num = validate_info_list[i].multirect.num; BUG_ON(rect_num >= MDSS_MDP_PIPE_MAX_RECTS); @@ -2782,7 +2824,8 @@ int mdss_mdp_layer_pre_commit(struct msm_fb_data_type *mfd, for (i = 0; i < layer_count; i++) { if (!validate_info_list[i].layer) { ret = __update_multirect_info(mfd, validate_info_list, - layer_list, i, layer_count); + layer_list, i, layer_count, + !!(commit->flags & MDP_COMMIT_RECT_NUM)); if (IS_ERR_VALUE(ret)) { pr_err("error updating multirect config. ret=%d i=%d\n", ret, i); @@ -3035,6 +3078,12 @@ int mdss_mdp_layer_pre_commit_wfd(struct msm_fb_data_type *mfd, wfd = mdp5_data->wfd; output_layer = commit->output_layer; + if (output_layer->buffer.plane_count > MAX_PLANES) { + pr_err("Output buffer plane_count exceeds MAX_PLANES limit:%d\n", + output_layer->buffer.plane_count); + return -EINVAL; + } + data = mdss_mdp_wfd_add_data(wfd, output_layer); if (IS_ERR_OR_NULL(data)) return PTR_ERR(data); diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c index 8c612e2b83fb..87fff44af389 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c +++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c @@ -3053,6 +3053,13 @@ static void mdss_mdp_overlay_pan_display(struct msm_fb_data_type *mfd) goto pipe_release; } + if (l_pipe_allocated && + (l_pipe->multirect.num == MDSS_MDP_PIPE_RECT1)) { + pr_err("Invalid: L_Pipe-%d is assigned for RECT-%d\n", + l_pipe->num, l_pipe->multirect.num); + goto pipe_release; + } + if (mdss_mdp_pipe_map(l_pipe)) { pr_err("unable to map base pipe\n"); goto pipe_release; @@ -3100,6 +3107,16 @@ static void mdss_mdp_overlay_pan_display(struct msm_fb_data_type *mfd) goto iommu_disable; } + if (l_pipe_allocated && r_pipe_allocated && + (l_pipe->num != r_pipe->num) && + (r_pipe->multirect.num == + MDSS_MDP_PIPE_RECT1)) { + pr_err("Invalid: L_Pipe-%d,RECT-%d R_Pipe-%d,RECT-%d\n", + l_pipe->num, l_pipe->multirect.num, + r_pipe->num, l_pipe->multirect.num); + goto iommu_disable; + } + if (mdss_mdp_pipe_map(r_pipe)) { pr_err("unable to map right base pipe\n"); goto iommu_disable; diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.c b/drivers/video/fbdev/msm/mdss_mdp_pp.c index f128f82fab04..2e85072c4cf7 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pp.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pp.c @@ -2599,6 +2599,7 @@ int mdss_mdp_dest_scaler_setup_locked(struct mdss_mdp_mixer *mixer) u32 op_mode; u32 mask; char *ds_offset; + int mixer_num = 0; if (!mixer || !mixer->ctl || !mixer->ctl->mdata) return -EINVAL; @@ -2658,6 +2659,14 @@ int mdss_mdp_dest_scaler_setup_locked(struct mdss_mdp_mixer *mixer) pr_err("Failed setup destination scaler\n"); return ret; } + /* Set LM Flush in order to update DS registers */ + if (ds->flags & DS_SCALE_UPDATE) { + mutex_lock(&ctl->flush_lock); + mixer_num = mdss_mdp_mixer_get_hw_num(mixer); + ctl->flush_bits |= + BIT(mixer_num < 5 ? 6 + mixer_num : 20); + mutex_unlock(&ctl->flush_lock); + } /* * Clearing the flag because we don't need to program the block * for each commit if there is no change. diff --git a/drivers/video/fbdev/msm/mdss_mdp_util.c b/drivers/video/fbdev/msm/mdss_mdp_util.c index d0bf61679f61..22656175edf8 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_util.c +++ b/drivers/video/fbdev/msm/mdss_mdp_util.c @@ -1095,7 +1095,7 @@ static int mdss_mdp_get_img(struct msmfb_data *img, return ret; } } - if (!*start) { + if (start && !*start) { pr_err("start address is zero!\n"); mdss_mdp_put_img(data, rotator, dir); return -ENOMEM; diff --git a/drivers/video/fbdev/msm/mdss_mdp_wfd.c b/drivers/video/fbdev/msm/mdss_mdp_wfd.c index 71a07f6b7d39..7868dc0f1999 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_wfd.c +++ b/drivers/video/fbdev/msm/mdss_mdp_wfd.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, 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 @@ -322,6 +322,12 @@ int mdss_mdp_wb_import_data(struct device *device, if (wfd_data->layer.flags & MDP_LAYER_SECURE_SESSION) flags = MDP_SECURE_OVERLAY_SESSION; + if (buffer->plane_count > MAX_PLANES) { + pr_err("buffer plane_count exceeds MAX_PLANES limit:%d", + buffer->plane_count); + return -EINVAL; + } + memset(planes, 0, sizeof(planes)); for (i = 0; i < buffer->plane_count; i++) { diff --git a/drivers/video/fbdev/msm/mdss_rotator.c b/drivers/video/fbdev/msm/mdss_rotator.c index fdd1c0153ce0..399a12e3dcc8 100644 --- a/drivers/video/fbdev/msm/mdss_rotator.c +++ b/drivers/video/fbdev/msm/mdss_rotator.c @@ -501,6 +501,12 @@ static int mdss_rotator_import_buffer(struct mdp_layer_buffer *buffer, memset(planes, 0, sizeof(planes)); + if (buffer->plane_count > MAX_PLANES) { + pr_err("buffer plane_count exceeds MAX_PLANES limit:%d\n", + buffer->plane_count); + return -EINVAL; + } + for (i = 0; i < buffer->plane_count; i++) { planes[i].memory_id = buffer->planes[i].fd; planes[i].offset = buffer->planes[i].offset; @@ -2104,6 +2110,20 @@ struct mdss_rot_entry_container *mdss_rotator_req_init( struct mdss_rot_entry_container *req; int size, i; + /* + * Check input and output plane_count from each given item + * are within the MAX_PLANES limit + */ + for (i = 0 ; i < count; i++) { + if ((items[i].input.plane_count > MAX_PLANES) || + (items[i].output.plane_count > MAX_PLANES)) { + pr_err("Input/Output plane_count exceeds MAX_PLANES limit, input:%d, output:%d\n", + items[i].input.plane_count, + items[i].output.plane_count); + return ERR_PTR(-EINVAL); + } + } + size = sizeof(struct mdss_rot_entry_container); size += sizeof(struct mdss_rot_entry) * count; req = devm_kzalloc(&mgr->pdev->dev, size, GFP_KERNEL); diff --git a/drivers/video/fbdev/msm/mdss_smmu.c b/drivers/video/fbdev/msm/mdss_smmu.c index f56158446c0d..1b4765837c61 100644 --- a/drivers/video/fbdev/msm/mdss_smmu.c +++ b/drivers/video/fbdev/msm/mdss_smmu.c @@ -700,13 +700,13 @@ int mdss_smmu_init(struct mdss_data_type *mdata, struct device *dev) } static struct mdss_smmu_domain mdss_mdp_unsec = { - "mdp_0", MDSS_IOMMU_DOMAIN_UNSECURE, SZ_128K, (SZ_4G - SZ_128K)}; + "mdp_0", MDSS_IOMMU_DOMAIN_UNSECURE, SZ_128K, (SZ_4G - SZ_128M)}; static struct mdss_smmu_domain mdss_rot_unsec = { - NULL, MDSS_IOMMU_DOMAIN_ROT_UNSECURE, SZ_128K, (SZ_4G - SZ_128K)}; + NULL, MDSS_IOMMU_DOMAIN_ROT_UNSECURE, SZ_128K, (SZ_4G - SZ_128M)}; static struct mdss_smmu_domain mdss_mdp_sec = { - "mdp_1", MDSS_IOMMU_DOMAIN_SECURE, SZ_128K, (SZ_4G - SZ_128K)}; + "mdp_1", MDSS_IOMMU_DOMAIN_SECURE, SZ_128K, (SZ_4G - SZ_128M)}; static struct mdss_smmu_domain mdss_rot_sec = { - NULL, MDSS_IOMMU_DOMAIN_ROT_SECURE, SZ_128K, (SZ_4G - SZ_128K)}; + NULL, MDSS_IOMMU_DOMAIN_ROT_SECURE, SZ_128K, (SZ_4G - SZ_128M)}; static const struct of_device_id mdss_smmu_dt_match[] = { { .compatible = "qcom,smmu_mdp_unsec", .data = &mdss_mdp_unsec}, diff --git a/drivers/video/fbdev/msm/msm_dba/adv7533.c b/drivers/video/fbdev/msm/msm_dba/adv7533.c index 8503d84e0de4..63e178d76403 100644 --- a/drivers/video/fbdev/msm/msm_dba/adv7533.c +++ b/drivers/video/fbdev/msm/msm_dba/adv7533.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, 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,7 +31,7 @@ #define ADV7533_REG_CHIP_REVISION (0x00) #define ADV7533_DSI_CEC_I2C_ADDR_REG (0xE1) -#define ADV7533_RESET_DELAY (100) +#define ADV7533_RESET_DELAY (10) #define PINCTRL_STATE_ACTIVE "pmx_adv7533_active" #define PINCTRL_STATE_SUSPEND "pmx_adv7533_suspend" @@ -1539,14 +1539,14 @@ exit: static int adv7533_video_on(void *client, bool on, struct msm_dba_video_cfg *cfg, u32 flags) { - int ret = -EINVAL; + int ret = 0; u8 lanes; u8 reg_val = 0; struct adv7533 *pdata = adv7533_get_platform_data(client); if (!pdata || !cfg) { pr_err("%s: invalid platform data\n", __func__); - return ret; + return -EINVAL; } mutex_lock(&pdata->ops_mutex); diff --git a/drivers/video/fbdev/msm/msm_dba/msm_dba_helpers.c b/drivers/video/fbdev/msm/msm_dba/msm_dba_helpers.c index f6128ae01a75..a0b45bfccb3c 100644 --- a/drivers/video/fbdev/msm/msm_dba/msm_dba_helpers.c +++ b/drivers/video/fbdev/msm/msm_dba/msm_dba_helpers.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -123,7 +123,7 @@ int msm_dba_helper_i2c_write_byte(struct i2c_client *client, return -EINVAL; } - pr_debug("%s: [%s:0x02%x] : W[0x%02x, 0x%02x]\n", __func__, + pr_debug("%s: [%s:0x%02x] : W[0x%02x, 0x%02x]\n", __func__, client->name, addr, reg, val); client->addr = addr; diff --git a/drivers/video/fbdev/msm/msm_mdss_io_8974.c b/drivers/video/fbdev/msm/msm_mdss_io_8974.c index 87fe6d739acf..690d74fa5271 100644 --- a/drivers/video/fbdev/msm/msm_mdss_io_8974.c +++ b/drivers/video/fbdev/msm/msm_mdss_io_8974.c @@ -2137,6 +2137,20 @@ error: } /** + * mdss_dsi_phy_idle_pc_exit() - Called after exit Idle PC + * @ctrl: pointer to DSI controller structure + * + * Perform any programming needed after Idle PC exit. + */ +static int mdss_dsi_phy_idle_pc_exit(struct mdss_dsi_ctrl_pdata *ctrl) +{ + if (ctrl->shared_data->phy_rev == DSI_PHY_REV_30) + mdss_dsi_phy_v3_idle_pc_exit(ctrl); + + return 0; +} + +/** * mdss_dsi_clamp_ctrl_default() - Program DSI clamps * @ctrl: pointer to DSI controller structure * @enable: true to enable clamps, false to disable clamps @@ -2615,9 +2629,16 @@ int mdss_dsi_post_clkoff_cb(void *priv, pdata = &ctrl->panel_data; for (i = DSI_MAX_PM - 1; i >= DSI_CORE_PM; i--) { - if ((ctrl->ctrl_state & CTRL_STATE_DSI_ACTIVE) && - (i != DSI_CORE_PM)) - continue; + /** + * If DSI_CTRL is active, proceed to turn off + * supplies which support turning off in low power + * state + */ + if (ctrl->ctrl_state & CTRL_STATE_DSI_ACTIVE) + if (!sdata->power_data[i].vreg_config + ->lp_disable_allowed) + continue; + rc = msm_dss_enable_vreg( sdata->power_data[i].vreg_config, sdata->power_data[i].num_vreg, 0); @@ -2627,6 +2648,12 @@ int mdss_dsi_post_clkoff_cb(void *priv, __mdss_dsi_pm_name(i)); rc = 0; } else { + pr_debug("%s: disabled vreg for %s panel_state %d\n", + __func__, + __mdss_dsi_pm_name(i), + pdata->panel_info.panel_power_state); + sdata->power_data[i].vreg_config->disabled = + true; ctrl->core_power = false; } } @@ -2666,7 +2693,7 @@ int mdss_dsi_pre_clkon_cb(void *priv, for (i = DSI_CORE_PM; i < DSI_MAX_PM; i++) { if ((ctrl->ctrl_state & CTRL_STATE_DSI_ACTIVE) && (!pdata->panel_info.cont_splash_enabled) && - (i != DSI_CORE_PM)) + (!sdata->power_data[i].vreg_config->disabled)) continue; rc = msm_dss_enable_vreg( sdata->power_data[i].vreg_config, @@ -2676,11 +2703,21 @@ int mdss_dsi_pre_clkon_cb(void *priv, __func__, __mdss_dsi_pm_name(i)); } else { + pr_debug("%s: enabled vregs for %s\n", + __func__, + __mdss_dsi_pm_name(i)); + sdata->power_data[i].vreg_config->disabled = + false; ctrl->core_power = true; } } } + if ((clk_type & MDSS_DSI_LINK_CLK) && + (new_state == MDSS_DSI_CLK_ON) && + !ctrl->panel_data.panel_info.cont_splash_enabled) + mdss_dsi_phy_idle_pc_exit(ctrl); + return rc; } diff --git a/drivers/video/fbdev/xen-fbfront.c b/drivers/video/fbdev/xen-fbfront.c index 0567d517eed3..ea2f19f5fbde 100644 --- a/drivers/video/fbdev/xen-fbfront.c +++ b/drivers/video/fbdev/xen-fbfront.c @@ -644,7 +644,6 @@ static void xenfb_backend_changed(struct xenbus_device *dev, break; case XenbusStateInitWait: -InitWait: xenbus_switch_state(dev, XenbusStateConnected); break; @@ -655,7 +654,8 @@ InitWait: * get Connected twice here. */ if (dev->state != XenbusStateConnected) - goto InitWait; /* no InitWait seen yet, fudge it */ + /* no InitWait seen yet, fudge it */ + xenbus_switch_state(dev, XenbusStateConnected); if (xenbus_scanf(XBT_NIL, info->xbdev->otherend, "request-update", "%d", &val) < 0) diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 8959b320d472..84c6add93f1f 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -421,6 +421,8 @@ static int init_vqs(struct virtio_balloon *vb) * Prime this virtqueue with one buffer so the hypervisor can * use it to signal us later (it can't be broken yet!). */ + update_balloon_stats(vb); + sg_init_one(&sg, vb->stats, sizeof vb->stats); if (virtqueue_add_outbuf(vb->stats_vq, &sg, 1, vb, GFP_KERNEL) < 0) diff --git a/drivers/xen/xen-acpi-processor.c b/drivers/xen/xen-acpi-processor.c index 611f9c11da85..2e319d0c395d 100644 --- a/drivers/xen/xen-acpi-processor.c +++ b/drivers/xen/xen-acpi-processor.c @@ -27,10 +27,10 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/types.h> +#include <linux/syscore_ops.h> #include <linux/acpi.h> #include <acpi/processor.h> #include <xen/xen.h> -#include <xen/xen-ops.h> #include <xen/interface/platform.h> #include <asm/xen/hypercall.h> @@ -466,15 +466,33 @@ static int xen_upload_processor_pm_data(void) return rc; } -static int xen_acpi_processor_resume(struct notifier_block *nb, - unsigned long action, void *data) +static void xen_acpi_processor_resume_worker(struct work_struct *dummy) { + int rc; + bitmap_zero(acpi_ids_done, nr_acpi_bits); - return xen_upload_processor_pm_data(); + + rc = xen_upload_processor_pm_data(); + if (rc != 0) + pr_info("ACPI data upload failed, error = %d\n", rc); +} + +static void xen_acpi_processor_resume(void) +{ + static DECLARE_WORK(wq, xen_acpi_processor_resume_worker); + + /* + * xen_upload_processor_pm_data() calls non-atomic code. + * However, the context for xen_acpi_processor_resume is syscore + * with only the boot CPU online and in an atomic context. + * + * So defer the upload for some point safer. + */ + schedule_work(&wq); } -struct notifier_block xen_acpi_processor_resume_nb = { - .notifier_call = xen_acpi_processor_resume, +static struct syscore_ops xap_syscore_ops = { + .resume = xen_acpi_processor_resume, }; static int __init xen_acpi_processor_init(void) @@ -527,7 +545,7 @@ static int __init xen_acpi_processor_init(void) if (rc) goto err_unregister; - xen_resume_notifier_register(&xen_acpi_processor_resume_nb); + register_syscore_ops(&xap_syscore_ops); return 0; err_unregister: @@ -544,7 +562,7 @@ static void __exit xen_acpi_processor_exit(void) { int i; - xen_resume_notifier_unregister(&xen_acpi_processor_resume_nb); + unregister_syscore_ops(&xap_syscore_ops); kfree(acpi_ids_done); kfree(acpi_id_present); kfree(acpi_id_cst_present); diff --git a/fs/Makefile b/fs/Makefile index 3b54070cd629..dee237540bc0 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -41,7 +41,7 @@ obj-$(CONFIG_COMPAT_BINFMT_ELF) += compat_binfmt_elf.o obj-$(CONFIG_BINFMT_ELF_FDPIC) += binfmt_elf_fdpic.o obj-$(CONFIG_BINFMT_FLAT) += binfmt_flat.o -obj-$(CONFIG_FS_MBCACHE) += mbcache.o +obj-$(CONFIG_FS_MBCACHE) += mbcache.o mbcache2.o obj-$(CONFIG_FS_POSIX_ACL) += posix_acl.o obj-$(CONFIG_NFS_COMMON) += nfs_common/ obj-$(CONFIG_COREDUMP) += coredump.o diff --git a/fs/attr.c b/fs/attr.c index 11be2265a2d5..c86b37c38fb7 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -211,7 +211,7 @@ int notify_change2(struct vfsmount *mnt, struct dentry * dentry, struct iattr * return -EPERM; if (!inode_owner_or_capable(inode)) { - error = inode_permission(inode, MAY_WRITE); + error = inode_permission2(mnt, inode, MAY_WRITE); if (error) return error; } diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index b76883606e4b..94906aaa9b7c 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -906,7 +906,6 @@ struct cifs_tcon { bool use_persistent:1; /* use persistent instead of durable handles */ #ifdef CONFIG_CIFS_SMB2 bool print:1; /* set if connection to printer share */ - bool bad_network_name:1; /* set if ret status STATUS_BAD_NETWORK_NAME */ __le32 capabilities; __u32 share_flags; __u32 maximal_access; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 72f270d4bd17..a0c0a49b6620 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -2545,7 +2545,7 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from, wdata->credits = credits; if (!wdata->cfile->invalidHandle || - !cifs_reopen_file(wdata->cfile, false)) + !(rc = cifs_reopen_file(wdata->cfile, false))) rc = server->ops->async_writev(wdata, cifs_uncached_writedata_release); if (rc) { @@ -2958,7 +2958,7 @@ cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file, rdata->credits = credits; if (!rdata->cfile->invalidHandle || - !cifs_reopen_file(rdata->cfile, true)) + !(rc = cifs_reopen_file(rdata->cfile, true))) rc = server->ops->async_readv(rdata); error: if (rc) { @@ -3544,7 +3544,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, } if (!rdata->cfile->invalidHandle || - !cifs_reopen_file(rdata->cfile, true)) + !(rc = cifs_reopen_file(rdata->cfile, true))) rc = server->ops->async_readv(rdata); if (rc) { add_credits_and_wake_if(server, rdata->credits, 0); diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index fc537c29044e..87b87e091e8e 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c @@ -1015,6 +1015,15 @@ cifs_dir_needs_close(struct cifsFileInfo *cfile) return !cfile->srch_inf.endOfSearch && !cfile->invalidHandle; } +static bool +cifs_can_echo(struct TCP_Server_Info *server) +{ + if (server->tcpStatus == CifsGood) + return true; + + return false; +} + struct smb_version_operations smb1_operations = { .send_cancel = send_nt_cancel, .compare_fids = cifs_compare_fids, @@ -1049,6 +1058,7 @@ struct smb_version_operations smb1_operations = { .get_dfs_refer = CIFSGetDFSRefer, .qfs_tcon = cifs_qfs_tcon, .is_path_accessible = cifs_is_path_accessible, + .can_echo = cifs_can_echo, .query_path_info = cifs_query_path_info, .query_file_info = cifs_query_file_info, .get_srv_inum = cifs_get_srv_inum, diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 2fa754c5fd62..6cb2603f8a5c 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -932,9 +932,6 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, else return -EIO; - if (tcon && tcon->bad_network_name) - return -ENOENT; - if ((tcon && tcon->seal) && ((ses->server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) == 0)) { cifs_dbg(VFS, "encryption requested but no server support"); @@ -952,6 +949,10 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, return -EINVAL; } + /* SMB2 TREE_CONNECT request must be called with TreeId == 0 */ + if (tcon) + tcon->tid = 0; + rc = small_smb2_init(SMB2_TREE_CONNECT, tcon, (void **) &req); if (rc) { kfree(unc_path); @@ -1032,8 +1033,6 @@ tcon_exit: tcon_error_exit: if (rsp->hdr.Status == STATUS_BAD_NETWORK_NAME) { cifs_dbg(VFS, "BAD_NETWORK_NAME: %s\n", tree); - if (tcon) - tcon->bad_network_name = true; } goto tcon_exit; } diff --git a/fs/dcache.c b/fs/dcache.c index 7b8feb6d60c8..3a3b0f370304 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1364,7 +1364,7 @@ static enum d_walk_ret select_collect(void *_data, struct dentry *dentry) goto out; if (dentry->d_flags & DCACHE_SHRINK_LIST) { - data->found++; + goto out; } else { if (dentry->d_flags & DCACHE_LRU_LIST) d_lru_del(dentry); diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c index b591e6772f1b..85f7a289bdac 100644 --- a/fs/ecryptfs/main.c +++ b/fs/ecryptfs/main.c @@ -168,14 +168,13 @@ void ecryptfs_put_lower_file(struct inode *inode) get_events()->is_hw_crypt_cb()) clear_cache_needed = true; + filemap_write_and_wait(inode->i_mapping); if (clear_cache_needed) { ret = vfs_fsync(inode_info->lower_file, false); if (ret) pr_err("failed to sync file ret = %d.\n", ret); } - - filemap_write_and_wait(inode->i_mapping); fput(inode_info->lower_file); inode_info->lower_file = NULL; mutex_unlock(&inode_info->lower_file_mutex); diff --git a/fs/exec.c b/fs/exec.c index 8c58183eccb7..b1f5ddbf7d56 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1132,8 +1132,10 @@ EXPORT_SYMBOL(flush_old_exec); void would_dump(struct linux_binprm *bprm, struct file *file) { struct inode *inode = file_inode(file); + if (inode_permission2(file->f_path.mnt, inode, MAY_READ) < 0) { struct user_namespace *old, *user_ns; + bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP; /* Ensure mm->user_ns contains the executable */ diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h index 4c69c94cafd8..f98ce7e60a0f 100644 --- a/fs/ext2/ext2.h +++ b/fs/ext2/ext2.h @@ -61,6 +61,8 @@ struct ext2_block_alloc_info { #define rsv_start rsv_window._rsv_start #define rsv_end rsv_window._rsv_end +struct mb2_cache; + /* * second extended-fs super-block data in memory */ @@ -111,6 +113,7 @@ struct ext2_sb_info { * of the mount options. */ spinlock_t s_lock; + struct mb2_cache *s_mb_cache; }; static inline spinlock_t * diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 748d35afc902..111a31761ffa 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -131,7 +131,10 @@ static void ext2_put_super (struct super_block * sb) dquot_disable(sb, -1, DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED); - ext2_xattr_put_super(sb); + if (sbi->s_mb_cache) { + ext2_xattr_destroy_cache(sbi->s_mb_cache); + sbi->s_mb_cache = NULL; + } if (!(sb->s_flags & MS_RDONLY)) { struct ext2_super_block *es = sbi->s_es; @@ -1104,6 +1107,14 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) ext2_msg(sb, KERN_ERR, "error: insufficient memory"); goto failed_mount3; } + +#ifdef CONFIG_EXT2_FS_XATTR + sbi->s_mb_cache = ext2_xattr_create_cache(); + if (!sbi->s_mb_cache) { + ext2_msg(sb, KERN_ERR, "Failed to create an mb_cache"); + goto failed_mount3; + } +#endif /* * set up enough so that it can read an inode */ @@ -1149,6 +1160,8 @@ cantfind_ext2: sb->s_id); goto failed_mount; failed_mount3: + if (sbi->s_mb_cache) + ext2_xattr_destroy_cache(sbi->s_mb_cache); percpu_counter_destroy(&sbi->s_freeblocks_counter); percpu_counter_destroy(&sbi->s_freeinodes_counter); percpu_counter_destroy(&sbi->s_dirs_counter); @@ -1555,20 +1568,17 @@ MODULE_ALIAS_FS("ext2"); static int __init init_ext2_fs(void) { - int err = init_ext2_xattr(); - if (err) - return err; + int err; + err = init_inodecache(); if (err) - goto out1; + return err; err = register_filesystem(&ext2_fs_type); if (err) goto out; return 0; out: destroy_inodecache(); -out1: - exit_ext2_xattr(); return err; } @@ -1576,7 +1586,6 @@ static void __exit exit_ext2_fs(void) { unregister_filesystem(&ext2_fs_type); destroy_inodecache(); - exit_ext2_xattr(); } MODULE_AUTHOR("Remy Card and others"); diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c index fa70848afa8f..24736c8b3d51 100644 --- a/fs/ext2/xattr.c +++ b/fs/ext2/xattr.c @@ -56,7 +56,7 @@ #include <linux/buffer_head.h> #include <linux/init.h> #include <linux/slab.h> -#include <linux/mbcache.h> +#include <linux/mbcache2.h> #include <linux/quotaops.h> #include <linux/rwsem.h> #include <linux/security.h> @@ -92,14 +92,12 @@ static int ext2_xattr_set2(struct inode *, struct buffer_head *, struct ext2_xattr_header *); -static int ext2_xattr_cache_insert(struct buffer_head *); +static int ext2_xattr_cache_insert(struct mb2_cache *, struct buffer_head *); static struct buffer_head *ext2_xattr_cache_find(struct inode *, struct ext2_xattr_header *); static void ext2_xattr_rehash(struct ext2_xattr_header *, struct ext2_xattr_entry *); -static struct mb_cache *ext2_xattr_cache; - static const struct xattr_handler *ext2_xattr_handler_map[] = { [EXT2_XATTR_INDEX_USER] = &ext2_xattr_user_handler, #ifdef CONFIG_EXT2_FS_POSIX_ACL @@ -154,6 +152,7 @@ ext2_xattr_get(struct inode *inode, int name_index, const char *name, size_t name_len, size; char *end; int error; + struct mb2_cache *ext2_mb_cache = EXT2_SB(inode->i_sb)->s_mb_cache; ea_idebug(inode, "name=%d.%s, buffer=%p, buffer_size=%ld", name_index, name, buffer, (long)buffer_size); @@ -198,7 +197,7 @@ bad_block: ext2_error(inode->i_sb, "ext2_xattr_get", goto found; entry = next; } - if (ext2_xattr_cache_insert(bh)) + if (ext2_xattr_cache_insert(ext2_mb_cache, bh)) ea_idebug(inode, "cache insert failed"); error = -ENODATA; goto cleanup; @@ -211,7 +210,7 @@ found: le16_to_cpu(entry->e_value_offs) + size > inode->i_sb->s_blocksize) goto bad_block; - if (ext2_xattr_cache_insert(bh)) + if (ext2_xattr_cache_insert(ext2_mb_cache, bh)) ea_idebug(inode, "cache insert failed"); if (buffer) { error = -ERANGE; @@ -249,6 +248,7 @@ ext2_xattr_list(struct dentry *dentry, char *buffer, size_t buffer_size) char *end; size_t rest = buffer_size; int error; + struct mb2_cache *ext2_mb_cache = EXT2_SB(inode->i_sb)->s_mb_cache; ea_idebug(inode, "buffer=%p, buffer_size=%ld", buffer, (long)buffer_size); @@ -283,7 +283,7 @@ bad_block: ext2_error(inode->i_sb, "ext2_xattr_list", goto bad_block; entry = next; } - if (ext2_xattr_cache_insert(bh)) + if (ext2_xattr_cache_insert(ext2_mb_cache, bh)) ea_idebug(inode, "cache insert failed"); /* list the attribute names */ @@ -480,22 +480,23 @@ bad_block: ext2_error(sb, "ext2_xattr_set", /* Here we know that we can set the new attribute. */ if (header) { - struct mb_cache_entry *ce; - /* assert(header == HDR(bh)); */ - ce = mb_cache_entry_get(ext2_xattr_cache, bh->b_bdev, - bh->b_blocknr); lock_buffer(bh); if (header->h_refcount == cpu_to_le32(1)) { + __u32 hash = le32_to_cpu(header->h_hash); + ea_bdebug(bh, "modifying in-place"); - if (ce) - mb_cache_entry_free(ce); + /* + * This must happen under buffer lock for + * ext2_xattr_set2() to reliably detect modified block + */ + mb2_cache_entry_delete_block(EXT2_SB(sb)->s_mb_cache, + hash, bh->b_blocknr); + /* keep the buffer locked while modifying it. */ } else { int offset; - if (ce) - mb_cache_entry_release(ce); unlock_buffer(bh); ea_bdebug(bh, "cloning"); header = kmalloc(bh->b_size, GFP_KERNEL); @@ -623,6 +624,7 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh, struct super_block *sb = inode->i_sb; struct buffer_head *new_bh = NULL; int error; + struct mb2_cache *ext2_mb_cache = EXT2_SB(sb)->s_mb_cache; if (header) { new_bh = ext2_xattr_cache_find(inode, header); @@ -650,7 +652,7 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh, don't need to change the reference count. */ new_bh = old_bh; get_bh(new_bh); - ext2_xattr_cache_insert(new_bh); + ext2_xattr_cache_insert(ext2_mb_cache, new_bh); } else { /* We need to allocate a new block */ ext2_fsblk_t goal = ext2_group_first_block_no(sb, @@ -671,7 +673,7 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh, memcpy(new_bh->b_data, header, new_bh->b_size); set_buffer_uptodate(new_bh); unlock_buffer(new_bh); - ext2_xattr_cache_insert(new_bh); + ext2_xattr_cache_insert(ext2_mb_cache, new_bh); ext2_xattr_update_super_block(sb); } @@ -704,19 +706,21 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh, error = 0; if (old_bh && old_bh != new_bh) { - struct mb_cache_entry *ce; - /* * If there was an old block and we are no longer using it, * release the old block. */ - ce = mb_cache_entry_get(ext2_xattr_cache, old_bh->b_bdev, - old_bh->b_blocknr); lock_buffer(old_bh); if (HDR(old_bh)->h_refcount == cpu_to_le32(1)) { + __u32 hash = le32_to_cpu(HDR(old_bh)->h_hash); + + /* + * This must happen under buffer lock for + * ext2_xattr_set2() to reliably detect freed block + */ + mb2_cache_entry_delete_block(ext2_mb_cache, + hash, old_bh->b_blocknr); /* Free the old block. */ - if (ce) - mb_cache_entry_free(ce); ea_bdebug(old_bh, "freeing"); ext2_free_blocks(inode, old_bh->b_blocknr, 1); mark_inode_dirty(inode); @@ -727,8 +731,6 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh, } else { /* Decrement the refcount only. */ le32_add_cpu(&HDR(old_bh)->h_refcount, -1); - if (ce) - mb_cache_entry_release(ce); dquot_free_block_nodirty(inode, 1); mark_inode_dirty(inode); mark_buffer_dirty(old_bh); @@ -754,7 +756,6 @@ void ext2_xattr_delete_inode(struct inode *inode) { struct buffer_head *bh = NULL; - struct mb_cache_entry *ce; down_write(&EXT2_I(inode)->xattr_sem); if (!EXT2_I(inode)->i_file_acl) @@ -774,19 +775,22 @@ ext2_xattr_delete_inode(struct inode *inode) EXT2_I(inode)->i_file_acl); goto cleanup; } - ce = mb_cache_entry_get(ext2_xattr_cache, bh->b_bdev, bh->b_blocknr); lock_buffer(bh); if (HDR(bh)->h_refcount == cpu_to_le32(1)) { - if (ce) - mb_cache_entry_free(ce); + __u32 hash = le32_to_cpu(HDR(bh)->h_hash); + + /* + * This must happen under buffer lock for ext2_xattr_set2() to + * reliably detect freed block + */ + mb2_cache_entry_delete_block(EXT2_SB(inode->i_sb)->s_mb_cache, + hash, bh->b_blocknr); ext2_free_blocks(inode, EXT2_I(inode)->i_file_acl, 1); get_bh(bh); bforget(bh); unlock_buffer(bh); } else { le32_add_cpu(&HDR(bh)->h_refcount, -1); - if (ce) - mb_cache_entry_release(ce); ea_bdebug(bh, "refcount now=%d", le32_to_cpu(HDR(bh)->h_refcount)); unlock_buffer(bh); @@ -803,18 +807,6 @@ cleanup: } /* - * ext2_xattr_put_super() - * - * This is called when a file system is unmounted. - */ -void -ext2_xattr_put_super(struct super_block *sb) -{ - mb_cache_shrink(sb->s_bdev); -} - - -/* * ext2_xattr_cache_insert() * * Create a new entry in the extended attribute cache, and insert @@ -823,28 +815,20 @@ ext2_xattr_put_super(struct super_block *sb) * Returns 0, or a negative error number on failure. */ static int -ext2_xattr_cache_insert(struct buffer_head *bh) +ext2_xattr_cache_insert(struct mb2_cache *cache, struct buffer_head *bh) { __u32 hash = le32_to_cpu(HDR(bh)->h_hash); - struct mb_cache_entry *ce; int error; - ce = mb_cache_entry_alloc(ext2_xattr_cache, GFP_NOFS); - if (!ce) - return -ENOMEM; - error = mb_cache_entry_insert(ce, bh->b_bdev, bh->b_blocknr, hash); + error = mb2_cache_entry_create(cache, GFP_NOFS, hash, bh->b_blocknr); if (error) { - mb_cache_entry_free(ce); if (error == -EBUSY) { ea_bdebug(bh, "already in cache (%d cache entries)", atomic_read(&ext2_xattr_cache->c_entry_count)); error = 0; } - } else { - ea_bdebug(bh, "inserting [%x] (%d cache entries)", (int)hash, - atomic_read(&ext2_xattr_cache->c_entry_count)); - mb_cache_entry_release(ce); - } + } else + ea_bdebug(bh, "inserting [%x]", (int)hash); return error; } @@ -900,23 +884,17 @@ static struct buffer_head * ext2_xattr_cache_find(struct inode *inode, struct ext2_xattr_header *header) { __u32 hash = le32_to_cpu(header->h_hash); - struct mb_cache_entry *ce; + struct mb2_cache_entry *ce; + struct mb2_cache *ext2_mb_cache = EXT2_SB(inode->i_sb)->s_mb_cache; if (!header->h_hash) return NULL; /* never share */ ea_idebug(inode, "looking for cached blocks [%x]", (int)hash); again: - ce = mb_cache_entry_find_first(ext2_xattr_cache, inode->i_sb->s_bdev, - hash); + ce = mb2_cache_entry_find_first(ext2_mb_cache, hash); while (ce) { struct buffer_head *bh; - if (IS_ERR(ce)) { - if (PTR_ERR(ce) == -EAGAIN) - goto again; - break; - } - bh = sb_bread(inode->i_sb, ce->e_block); if (!bh) { ext2_error(inode->i_sb, "ext2_xattr_cache_find", @@ -924,7 +902,21 @@ again: inode->i_ino, (unsigned long) ce->e_block); } else { lock_buffer(bh); - if (le32_to_cpu(HDR(bh)->h_refcount) > + /* + * We have to be careful about races with freeing or + * rehashing of xattr block. Once we hold buffer lock + * xattr block's state is stable so we can check + * whether the block got freed / rehashed or not. + * Since we unhash mbcache entry under buffer lock when + * freeing / rehashing xattr block, checking whether + * entry is still hashed is reliable. + */ + if (hlist_bl_unhashed(&ce->e_hash_list)) { + mb2_cache_entry_put(ext2_mb_cache, ce); + unlock_buffer(bh); + brelse(bh); + goto again; + } else if (le32_to_cpu(HDR(bh)->h_refcount) > EXT2_XATTR_REFCOUNT_MAX) { ea_idebug(inode, "block %ld refcount %d>%d", (unsigned long) ce->e_block, @@ -933,13 +925,14 @@ again: } else if (!ext2_xattr_cmp(header, HDR(bh))) { ea_bdebug(bh, "b_count=%d", atomic_read(&(bh->b_count))); - mb_cache_entry_release(ce); + mb2_cache_entry_touch(ext2_mb_cache, ce); + mb2_cache_entry_put(ext2_mb_cache, ce); return bh; } unlock_buffer(bh); brelse(bh); } - ce = mb_cache_entry_find_next(ce, inode->i_sb->s_bdev, hash); + ce = mb2_cache_entry_find_next(ext2_mb_cache, ce); } return NULL; } @@ -1012,17 +1005,15 @@ static void ext2_xattr_rehash(struct ext2_xattr_header *header, #undef BLOCK_HASH_SHIFT -int __init -init_ext2_xattr(void) +#define HASH_BUCKET_BITS 10 + +struct mb2_cache *ext2_xattr_create_cache(void) { - ext2_xattr_cache = mb_cache_create("ext2_xattr", 6); - if (!ext2_xattr_cache) - return -ENOMEM; - return 0; + return mb2_cache_create(HASH_BUCKET_BITS); } -void -exit_ext2_xattr(void) +void ext2_xattr_destroy_cache(struct mb2_cache *cache) { - mb_cache_destroy(ext2_xattr_cache); + if (cache) + mb2_cache_destroy(cache); } diff --git a/fs/ext2/xattr.h b/fs/ext2/xattr.h index 60edf298644e..6ea38aa9563a 100644 --- a/fs/ext2/xattr.h +++ b/fs/ext2/xattr.h @@ -53,6 +53,8 @@ struct ext2_xattr_entry { #define EXT2_XATTR_SIZE(size) \ (((size) + EXT2_XATTR_ROUND) & ~EXT2_XATTR_ROUND) +struct mb2_cache; + # ifdef CONFIG_EXT2_FS_XATTR extern const struct xattr_handler ext2_xattr_user_handler; @@ -65,10 +67,9 @@ extern int ext2_xattr_get(struct inode *, int, const char *, void *, size_t); extern int ext2_xattr_set(struct inode *, int, const char *, const void *, size_t, int); extern void ext2_xattr_delete_inode(struct inode *); -extern void ext2_xattr_put_super(struct super_block *); -extern int init_ext2_xattr(void); -extern void exit_ext2_xattr(void); +extern struct mb2_cache *ext2_xattr_create_cache(void); +extern void ext2_xattr_destroy_cache(struct mb2_cache *cache); extern const struct xattr_handler *ext2_xattr_handlers[]; @@ -93,19 +94,7 @@ ext2_xattr_delete_inode(struct inode *inode) { } -static inline void -ext2_xattr_put_super(struct super_block *sb) -{ -} - -static inline int -init_ext2_xattr(void) -{ - return 0; -} - -static inline void -exit_ext2_xattr(void) +static inline void ext2_xattr_destroy_cache(struct mb2_cache *cache) { } diff --git a/fs/ext4/Kconfig b/fs/ext4/Kconfig index 95a49ef2781a..ebaff5ab93da 100644 --- a/fs/ext4/Kconfig +++ b/fs/ext4/Kconfig @@ -106,6 +106,7 @@ config EXT4_ENCRYPTION select CRYPTO_ECB select CRYPTO_XTS select CRYPTO_CTS + select CRYPTO_HEH select CRYPTO_CTR select CRYPTO_SHA256 select KEYS diff --git a/fs/ext4/crypto_fname.c b/fs/ext4/crypto_fname.c index 2fbef8a14760..e2645ca9b95e 100644 --- a/fs/ext4/crypto_fname.c +++ b/fs/ext4/crypto_fname.c @@ -44,7 +44,8 @@ static void ext4_dir_crypt_complete(struct crypto_async_request *req, int res) bool ext4_valid_filenames_enc_mode(uint32_t mode) { - return (mode == EXT4_ENCRYPTION_MODE_AES_256_CTS); + return (mode == EXT4_ENCRYPTION_MODE_AES_256_CTS || + mode == EXT4_ENCRYPTION_MODE_AES_256_HEH); } static unsigned max_name_len(struct inode *inode) diff --git a/fs/ext4/crypto_key.c b/fs/ext4/crypto_key.c index 15342bfff70d..363e4c6bf37f 100644 --- a/fs/ext4/crypto_key.c +++ b/fs/ext4/crypto_key.c @@ -30,16 +30,16 @@ static void derive_crypt_complete(struct crypto_async_request *req, int rc) } /** - * ext4_derive_key_aes() - Derive a key using AES-128-ECB + * ext4_derive_key_v1() - Derive a key using AES-128-ECB * @deriving_key: Encryption key used for derivation. * @source_key: Source key to which to apply derivation. * @derived_key: Derived key. * - * Return: Zero on success; non-zero otherwise. + * Return: 0 on success, -errno on failure */ -static int ext4_derive_key_aes(char deriving_key[EXT4_AES_128_ECB_KEY_SIZE], - char source_key[EXT4_AES_256_XTS_KEY_SIZE], - char derived_key[EXT4_AES_256_XTS_KEY_SIZE]) +static int ext4_derive_key_v1(const char deriving_key[EXT4_AES_128_ECB_KEY_SIZE], + const char source_key[EXT4_AES_256_XTS_KEY_SIZE], + char derived_key[EXT4_AES_256_XTS_KEY_SIZE]) { int res = 0; struct ablkcipher_request *req = NULL; @@ -84,6 +84,91 @@ out: return res; } +/** + * ext4_derive_key_v2() - Derive a key non-reversibly + * @nonce: the nonce associated with the file + * @master_key: the master key referenced by the file + * @derived_key: (output) the resulting derived key + * + * This function computes the following: + * derived_key[0:127] = AES-256-ENCRYPT(master_key[0:255], nonce) + * derived_key[128:255] = AES-256-ENCRYPT(master_key[0:255], nonce ^ 0x01) + * derived_key[256:383] = AES-256-ENCRYPT(master_key[256:511], nonce) + * derived_key[384:511] = AES-256-ENCRYPT(master_key[256:511], nonce ^ 0x01) + * + * 'nonce ^ 0x01' denotes flipping the low order bit of the last byte. + * + * Unlike the v1 algorithm, the v2 algorithm is "non-reversible", meaning that + * compromising a derived key does not also compromise the master key. + * + * Return: 0 on success, -errno on failure + */ +static int ext4_derive_key_v2(const char nonce[EXT4_KEY_DERIVATION_NONCE_SIZE], + const char master_key[EXT4_MAX_KEY_SIZE], + char derived_key[EXT4_MAX_KEY_SIZE]) +{ + const int noncelen = EXT4_KEY_DERIVATION_NONCE_SIZE; + struct crypto_cipher *tfm; + int err; + int i; + + /* + * Since we only use each transform for a small number of encryptions, + * requesting just "aes" turns out to be significantly faster than + * "ecb(aes)", by about a factor of two. + */ + tfm = crypto_alloc_cipher("aes", 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + BUILD_BUG_ON(4 * EXT4_KEY_DERIVATION_NONCE_SIZE != EXT4_MAX_KEY_SIZE); + BUILD_BUG_ON(2 * EXT4_AES_256_ECB_KEY_SIZE != EXT4_MAX_KEY_SIZE); + for (i = 0; i < 2; i++) { + memcpy(derived_key, nonce, noncelen); + memcpy(derived_key + noncelen, nonce, noncelen); + derived_key[2 * noncelen - 1] ^= 0x01; + err = crypto_cipher_setkey(tfm, master_key, + EXT4_AES_256_ECB_KEY_SIZE); + if (err) + break; + crypto_cipher_encrypt_one(tfm, derived_key, derived_key); + crypto_cipher_encrypt_one(tfm, derived_key + noncelen, + derived_key + noncelen); + master_key += EXT4_AES_256_ECB_KEY_SIZE; + derived_key += 2 * noncelen; + } + crypto_free_cipher(tfm); + return err; +} + +/** + * ext4_derive_key() - Derive a per-file key from a nonce and master key + * @ctx: the encryption context associated with the file + * @master_key: the master key referenced by the file + * @derived_key: (output) the resulting derived key + * + * Return: 0 on success, -errno on failure + */ +static int ext4_derive_key(const struct ext4_encryption_context *ctx, + const char master_key[EXT4_MAX_KEY_SIZE], + char derived_key[EXT4_MAX_KEY_SIZE]) +{ + BUILD_BUG_ON(EXT4_AES_128_ECB_KEY_SIZE != EXT4_KEY_DERIVATION_NONCE_SIZE); + BUILD_BUG_ON(EXT4_AES_256_XTS_KEY_SIZE != EXT4_MAX_KEY_SIZE); + + /* + * Although the key derivation algorithm is logically independent of the + * choice of encryption modes, in this kernel it is bundled with HEH + * encryption of filenames, which is another crypto improvement that + * requires an on-disk format change and requires userspace to specify + * different encryption policies. + */ + if (ctx->filenames_encryption_mode == EXT4_ENCRYPTION_MODE_AES_256_HEH) + return ext4_derive_key_v2(ctx->nonce, master_key, derived_key); + else + return ext4_derive_key_v1(ctx->nonce, master_key, derived_key); +} + void ext4_free_crypt_info(struct ext4_crypt_info *ci) { if (!ci) @@ -192,6 +277,8 @@ retry: break; case EXT4_ENCRYPTION_MODE_PRIVATE: cipher_str = "bugon"; + case EXT4_ENCRYPTION_MODE_AES_256_HEH: + cipher_str = "heh(aes)"; break; default: printk_once(KERN_WARNING @@ -242,7 +329,7 @@ retry: up_read(&keyring_key->sem); goto out; } - res = ext4_derive_key_aes(ctx.nonce, master_key->raw, + res = ext4_derive_key(&ctx, master_key->raw, crypt_info->ci_raw_key); up_read(&keyring_key->sem); if (res) diff --git a/fs/ext4/crypto_policy.c b/fs/ext4/crypto_policy.c index 8a9feb341f31..dd561f916f0b 100644 --- a/fs/ext4/crypto_policy.c +++ b/fs/ext4/crypto_policy.c @@ -156,6 +156,12 @@ int ext4_is_child_context_consistent_with_parent(struct inode *parent, WARN_ON(1); /* Should never happen */ return 0; } + + /* No restrictions on file types which are never encrypted */ + if (!S_ISREG(child->i_mode) && !S_ISDIR(child->i_mode) && + !S_ISLNK(child->i_mode)) + return 1; + /* no restrictions if the parent directory is not encrypted */ if (!ext4_encrypted_inode(parent)) return 1; diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 14d7f72f8092..6c910f127f1f 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -590,6 +590,7 @@ enum { #define EXT4_ENCRYPTION_MODE_AES_256_CBC 3 #define EXT4_ENCRYPTION_MODE_AES_256_CTS 4 #define EXT4_ENCRYPTION_MODE_PRIVATE 127 +#define EXT4_ENCRYPTION_MODE_AES_256_HEH 126 #include "ext4_crypto.h" @@ -1442,7 +1443,7 @@ struct ext4_sb_info { struct list_head s_es_list; /* List of inodes with reclaimable extents */ long s_es_nr_inode; struct ext4_es_stats s_es_stats; - struct mb_cache *s_mb_cache; + struct mb2_cache *s_mb_cache; spinlock_t s_es_lock ____cacheline_aligned_in_smp; /* Ratelimit ext4 messages. */ @@ -2339,19 +2340,6 @@ int _ext4_get_encryption_info(struct inode *inode); #ifdef CONFIG_EXT4_FS_ENCRYPTION int ext4_has_encryption_key(struct inode *inode); -static inline struct ext4_crypt_info *ext4_encryption_info(struct inode *inode) -{ - return EXT4_I(inode)->i_crypt_info; -} - -static inline int ext4_using_hardware_encryption(struct inode *inode) -{ - struct ext4_crypt_info *ci = ext4_encryption_info(inode); - - return S_ISREG(inode->i_mode) && ci && - ci->ci_data_mode == EXT4_ENCRYPTION_MODE_PRIVATE; -} - static inline int ext4_get_encryption_info(struct inode *inode) { struct ext4_crypt_info *ci = EXT4_I(inode)->i_crypt_info; @@ -2365,6 +2353,19 @@ static inline int ext4_get_encryption_info(struct inode *inode) return 0; } +static inline struct ext4_crypt_info *ext4_encryption_info(struct inode *inode) +{ + return EXT4_I(inode)->i_crypt_info; +} + +static inline int ext4_using_hardware_encryption(struct inode *inode) +{ + struct ext4_crypt_info *ci = ext4_encryption_info(inode); + + return S_ISREG(inode->i_mode) && ci && + ci->ci_data_mode == EXT4_ENCRYPTION_MODE_PRIVATE; +} + #else static inline int ext4_has_encryption_key(struct inode *inode) { diff --git a/fs/ext4/ext4_crypto.h b/fs/ext4/ext4_crypto.h index 95cbc9bc1995..e28cc5aab04a 100644 --- a/fs/ext4/ext4_crypto.h +++ b/fs/ext4/ext4_crypto.h @@ -59,8 +59,10 @@ struct ext4_encryption_context { #define EXT4_XTS_TWEAK_SIZE 16 #define EXT4_AES_128_ECB_KEY_SIZE 16 #define EXT4_AES_256_GCM_KEY_SIZE 32 +#define EXT4_AES_256_ECB_KEY_SIZE 32 #define EXT4_AES_256_CBC_KEY_SIZE 32 #define EXT4_AES_256_CTS_KEY_SIZE 32 +#define EXT4_AES_256_HEH_KEY_SIZE 32 #define EXT4_AES_256_XTS_KEY_SIZE 64 #define EXT4_PRIVATE_KEY_SIZE 64 #define EXT4_MAX_KEY_SIZE 64 @@ -127,6 +129,8 @@ static inline int ext4_encryption_key_size(int mode) return EXT4_AES_256_CBC_KEY_SIZE; case EXT4_ENCRYPTION_MODE_AES_256_CTS: return EXT4_AES_256_CTS_KEY_SIZE; + case EXT4_ENCRYPTION_MODE_AES_256_HEH: + return EXT4_AES_256_HEH_KEY_SIZE; default: BUG(); } diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c index db81acb686c0..280d67fe33a7 100644 --- a/fs/ext4/inline.c +++ b/fs/ext4/inline.c @@ -1171,10 +1171,9 @@ static int ext4_finish_convert_inline_dir(handle_t *handle, set_buffer_uptodate(dir_block); err = ext4_handle_dirty_dirent_node(handle, inode, dir_block); if (err) - goto out; + return err; set_buffer_verified(dir_block); -out: - return err; + return ext4_mark_inode_dirty(handle, inode); } static int ext4_convert_inline_data_nolock(handle_t *handle, diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 72b384931f66..4caa0c1f77d8 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -73,10 +73,9 @@ static __u32 ext4_inode_csum(struct inode *inode, struct ext4_inode *raw, csum = ext4_chksum(sbi, csum, (__u8 *)&dummy_csum, csum_size); offset += csum_size; - csum = ext4_chksum(sbi, csum, (__u8 *)raw + offset, - EXT4_INODE_SIZE(inode->i_sb) - - offset); } + csum = ext4_chksum(sbi, csum, (__u8 *)raw + offset, + EXT4_INODE_SIZE(inode->i_sb) - offset); } return csum; diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index 7e974878d9a9..3a2594665b44 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -637,8 +637,12 @@ resizefs_out: if (err) goto encryption_policy_out; + mutex_lock(&inode->i_mutex); + err = ext4_process_policy(&policy, inode); + mutex_unlock(&inode->i_mutex); + mnt_drop_write_file(filp); encryption_policy_out: return err; diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 6fe8e30eeb99..bd8831bfbafe 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -816,7 +816,6 @@ static void ext4_put_super(struct super_block *sb) ext4_release_system_zone(sb); ext4_mb_release(sb); ext4_ext_release(sb); - ext4_xattr_put_super(sb); if (!(sb->s_flags & MS_RDONLY) && !aborted) { ext4_clear_feature_journal_needs_recovery(sb); @@ -3666,7 +3665,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) db_count = (sbi->s_groups_count + EXT4_DESC_PER_BLOCK(sb) - 1) / EXT4_DESC_PER_BLOCK(sb); if (ext4_has_feature_meta_bg(sb)) { - if (le32_to_cpu(es->s_first_meta_bg) >= db_count) { + if (le32_to_cpu(es->s_first_meta_bg) > db_count) { ext4_msg(sb, KERN_WARNING, "first meta block group too large: %u " "(group descriptor block count %u)", @@ -3833,7 +3832,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) no_journal: if (ext4_mballoc_ready) { - sbi->s_mb_cache = ext4_xattr_create_cache(sb->s_id); + sbi->s_mb_cache = ext4_xattr_create_cache(); if (!sbi->s_mb_cache) { ext4_msg(sb, KERN_ERR, "Failed to create an mb_cache"); goto failed_mount_wq; @@ -4065,6 +4064,10 @@ failed_mount4: if (EXT4_SB(sb)->rsv_conversion_wq) destroy_workqueue(EXT4_SB(sb)->rsv_conversion_wq); failed_mount_wq: + if (sbi->s_mb_cache) { + ext4_xattr_destroy_cache(sbi->s_mb_cache); + sbi->s_mb_cache = NULL; + } if (sbi->s_journal) { jbd2_journal_destroy(sbi->s_journal); sbi->s_journal = NULL; diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index 7c23363ecf19..b310ed81c10e 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c @@ -53,7 +53,7 @@ #include <linux/init.h> #include <linux/fs.h> #include <linux/slab.h> -#include <linux/mbcache.h> +#include <linux/mbcache2.h> #include <linux/quotaops.h> #include "ext4_jbd2.h" #include "ext4.h" @@ -80,10 +80,10 @@ # define ea_bdebug(bh, fmt, ...) no_printk(fmt, ##__VA_ARGS__) #endif -static void ext4_xattr_cache_insert(struct mb_cache *, struct buffer_head *); +static void ext4_xattr_cache_insert(struct mb2_cache *, struct buffer_head *); static struct buffer_head *ext4_xattr_cache_find(struct inode *, struct ext4_xattr_header *, - struct mb_cache_entry **); + struct mb2_cache_entry **); static void ext4_xattr_rehash(struct ext4_xattr_header *, struct ext4_xattr_entry *); static int ext4_xattr_list(struct dentry *dentry, char *buffer, @@ -300,7 +300,7 @@ ext4_xattr_block_get(struct inode *inode, int name_index, const char *name, struct ext4_xattr_entry *entry; size_t size; int error; - struct mb_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode); + struct mb2_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode); ea_idebug(inode, "name=%d.%s, buffer=%p, buffer_size=%ld", name_index, name, buffer, (long)buffer_size); @@ -447,7 +447,7 @@ ext4_xattr_block_list(struct dentry *dentry, char *buffer, size_t buffer_size) struct inode *inode = d_inode(dentry); struct buffer_head *bh = NULL; int error; - struct mb_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode); + struct mb2_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode); ea_idebug(inode, "buffer=%p, buffer_size=%ld", buffer, (long)buffer_size); @@ -564,11 +564,8 @@ static void ext4_xattr_release_block(handle_t *handle, struct inode *inode, struct buffer_head *bh) { - struct mb_cache_entry *ce = NULL; int error = 0; - struct mb_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode); - ce = mb_cache_entry_get(ext4_mb_cache, bh->b_bdev, bh->b_blocknr); BUFFER_TRACE(bh, "get_write_access"); error = ext4_journal_get_write_access(handle, bh); if (error) @@ -576,9 +573,15 @@ ext4_xattr_release_block(handle_t *handle, struct inode *inode, lock_buffer(bh); if (BHDR(bh)->h_refcount == cpu_to_le32(1)) { + __u32 hash = le32_to_cpu(BHDR(bh)->h_hash); + ea_bdebug(bh, "refcount now=0; freeing"); - if (ce) - mb_cache_entry_free(ce); + /* + * This must happen under buffer lock for + * ext4_xattr_block_set() to reliably detect freed block + */ + mb2_cache_entry_delete_block(EXT4_GET_MB_CACHE(inode), hash, + bh->b_blocknr); get_bh(bh); unlock_buffer(bh); ext4_free_blocks(handle, inode, bh, 0, 1, @@ -586,8 +589,6 @@ ext4_xattr_release_block(handle_t *handle, struct inode *inode, EXT4_FREE_BLOCKS_FORGET); } else { le32_add_cpu(&BHDR(bh)->h_refcount, -1); - if (ce) - mb_cache_entry_release(ce); /* * Beware of this ugliness: Releasing of xattr block references * from different inodes can race and so we have to protect @@ -800,17 +801,15 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode, struct super_block *sb = inode->i_sb; struct buffer_head *new_bh = NULL; struct ext4_xattr_search *s = &bs->s; - struct mb_cache_entry *ce = NULL; + struct mb2_cache_entry *ce = NULL; int error = 0; - struct mb_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode); + struct mb2_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode); #define header(x) ((struct ext4_xattr_header *)(x)) if (i->value && i->value_len > sb->s_blocksize) return -ENOSPC; if (s->base) { - ce = mb_cache_entry_get(ext4_mb_cache, bs->bh->b_bdev, - bs->bh->b_blocknr); BUFFER_TRACE(bs->bh, "get_write_access"); error = ext4_journal_get_write_access(handle, bs->bh); if (error) @@ -818,10 +817,15 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode, lock_buffer(bs->bh); if (header(s->base)->h_refcount == cpu_to_le32(1)) { - if (ce) { - mb_cache_entry_free(ce); - ce = NULL; - } + __u32 hash = le32_to_cpu(BHDR(bs->bh)->h_hash); + + /* + * This must happen under buffer lock for + * ext4_xattr_block_set() to reliably detect modified + * block + */ + mb2_cache_entry_delete_block(ext4_mb_cache, hash, + bs->bh->b_blocknr); ea_bdebug(bs->bh, "modifying in-place"); error = ext4_xattr_set_entry(i, s); if (!error) { @@ -845,10 +849,6 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode, int offset = (char *)s->here - bs->bh->b_data; unlock_buffer(bs->bh); - if (ce) { - mb_cache_entry_release(ce); - ce = NULL; - } ea_bdebug(bs->bh, "cloning"); s->base = kmalloc(bs->bh->b_size, GFP_NOFS); error = -ENOMEM; @@ -903,6 +903,31 @@ inserted: if (error) goto cleanup_dquot; lock_buffer(new_bh); + /* + * We have to be careful about races with + * freeing or rehashing of xattr block. Once we + * hold buffer lock xattr block's state is + * stable so we can check whether the block got + * freed / rehashed or not. Since we unhash + * mbcache entry under buffer lock when freeing + * / rehashing xattr block, checking whether + * entry is still hashed is reliable. + */ + if (hlist_bl_unhashed(&ce->e_hash_list)) { + /* + * Undo everything and check mbcache + * again. + */ + unlock_buffer(new_bh); + dquot_free_block(inode, + EXT4_C2B(EXT4_SB(sb), + 1)); + brelse(new_bh); + mb2_cache_entry_put(ext4_mb_cache, ce); + ce = NULL; + new_bh = NULL; + goto inserted; + } le32_add_cpu(&BHDR(new_bh)->h_refcount, 1); ea_bdebug(new_bh, "reusing; refcount now=%d", le32_to_cpu(BHDR(new_bh)->h_refcount)); @@ -913,7 +938,8 @@ inserted: if (error) goto cleanup_dquot; } - mb_cache_entry_release(ce); + mb2_cache_entry_touch(ext4_mb_cache, ce); + mb2_cache_entry_put(ext4_mb_cache, ce); ce = NULL; } else if (bs->bh && s->base == bs->bh->b_data) { /* We were modifying this block in-place. */ @@ -978,7 +1004,7 @@ getblk_failed: cleanup: if (ce) - mb_cache_entry_release(ce); + mb2_cache_entry_put(ext4_mb_cache, ce); brelse(new_bh); if (!(bs->bh && s->base == bs->bh->b_data)) kfree(s->base); @@ -1543,17 +1569,6 @@ cleanup: } /* - * ext4_xattr_put_super() - * - * This is called when a file system is unmounted. - */ -void -ext4_xattr_put_super(struct super_block *sb) -{ - mb_cache_shrink(sb->s_bdev); -} - -/* * ext4_xattr_cache_insert() * * Create a new entry in the extended attribute cache, and insert @@ -1562,28 +1577,18 @@ ext4_xattr_put_super(struct super_block *sb) * Returns 0, or a negative error number on failure. */ static void -ext4_xattr_cache_insert(struct mb_cache *ext4_mb_cache, struct buffer_head *bh) +ext4_xattr_cache_insert(struct mb2_cache *ext4_mb_cache, struct buffer_head *bh) { __u32 hash = le32_to_cpu(BHDR(bh)->h_hash); - struct mb_cache_entry *ce; int error; - ce = mb_cache_entry_alloc(ext4_mb_cache, GFP_NOFS); - if (!ce) { - ea_bdebug(bh, "out of memory"); - return; - } - error = mb_cache_entry_insert(ce, bh->b_bdev, bh->b_blocknr, hash); + error = mb2_cache_entry_create(ext4_mb_cache, GFP_NOFS, hash, + bh->b_blocknr); if (error) { - mb_cache_entry_free(ce); - if (error == -EBUSY) { + if (error == -EBUSY) ea_bdebug(bh, "already in cache"); - error = 0; - } - } else { + } else ea_bdebug(bh, "inserting [%x]", (int)hash); - mb_cache_entry_release(ce); - } } /* @@ -1636,26 +1641,19 @@ ext4_xattr_cmp(struct ext4_xattr_header *header1, */ static struct buffer_head * ext4_xattr_cache_find(struct inode *inode, struct ext4_xattr_header *header, - struct mb_cache_entry **pce) + struct mb2_cache_entry **pce) { __u32 hash = le32_to_cpu(header->h_hash); - struct mb_cache_entry *ce; - struct mb_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode); + struct mb2_cache_entry *ce; + struct mb2_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode); if (!header->h_hash) return NULL; /* never share */ ea_idebug(inode, "looking for cached blocks [%x]", (int)hash); -again: - ce = mb_cache_entry_find_first(ext4_mb_cache, inode->i_sb->s_bdev, - hash); + ce = mb2_cache_entry_find_first(ext4_mb_cache, hash); while (ce) { struct buffer_head *bh; - if (IS_ERR(ce)) { - if (PTR_ERR(ce) == -EAGAIN) - goto again; - break; - } bh = sb_bread(inode->i_sb, ce->e_block); if (!bh) { EXT4_ERROR_INODE(inode, "block %lu read error", @@ -1671,7 +1669,7 @@ again: return bh; } brelse(bh); - ce = mb_cache_entry_find_next(ce, inode->i_sb->s_bdev, hash); + ce = mb2_cache_entry_find_next(ext4_mb_cache, ce); } return NULL; } @@ -1746,15 +1744,15 @@ static void ext4_xattr_rehash(struct ext4_xattr_header *header, #define HASH_BUCKET_BITS 10 -struct mb_cache * -ext4_xattr_create_cache(char *name) +struct mb2_cache * +ext4_xattr_create_cache(void) { - return mb_cache_create(name, HASH_BUCKET_BITS); + return mb2_cache_create(HASH_BUCKET_BITS); } -void ext4_xattr_destroy_cache(struct mb_cache *cache) +void ext4_xattr_destroy_cache(struct mb2_cache *cache) { if (cache) - mb_cache_destroy(cache); + mb2_cache_destroy(cache); } diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h index ddc0957760ba..10b0f7323ed6 100644 --- a/fs/ext4/xattr.h +++ b/fs/ext4/xattr.h @@ -108,7 +108,6 @@ extern int ext4_xattr_set(struct inode *, int, const char *, const void *, size_ extern int ext4_xattr_set_handle(handle_t *, struct inode *, int, const char *, const void *, size_t, int); extern void ext4_xattr_delete_inode(handle_t *, struct inode *); -extern void ext4_xattr_put_super(struct super_block *); extern int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize, struct ext4_inode *raw_inode, handle_t *handle); @@ -124,8 +123,8 @@ extern int ext4_xattr_ibody_inline_set(handle_t *handle, struct inode *inode, struct ext4_xattr_info *i, struct ext4_xattr_ibody_find *is); -extern struct mb_cache *ext4_xattr_create_cache(char *name); -extern void ext4_xattr_destroy_cache(struct mb_cache *); +extern struct mb2_cache *ext4_xattr_create_cache(void); +extern void ext4_xattr_destroy_cache(struct mb2_cache *); #ifdef CONFIG_EXT4_FS_SECURITY extern int ext4_init_security(handle_t *handle, struct inode *inode, diff --git a/fs/f2fs/crypto_policy.c b/fs/f2fs/crypto_policy.c index e504f548b64e..5bbd1989d5e6 100644 --- a/fs/f2fs/crypto_policy.c +++ b/fs/f2fs/crypto_policy.c @@ -149,6 +149,11 @@ int f2fs_is_child_context_consistent_with_parent(struct inode *parent, BUG_ON(1); } + /* No restrictions on file types which are never encrypted */ + if (!S_ISREG(child->i_mode) && !S_ISDIR(child->i_mode) && + !S_ISLNK(child->i_mode)) + return 1; + /* no restrictions if the parent directory is not encrypted */ if (!f2fs_encrypted_inode(parent)) return 1; diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index a197215ad52b..4b449d263333 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1535,12 +1535,19 @@ static int f2fs_ioc_set_encryption_policy(struct file *filp, unsigned long arg) #ifdef CONFIG_F2FS_FS_ENCRYPTION struct f2fs_encryption_policy policy; struct inode *inode = file_inode(filp); + int err; if (copy_from_user(&policy, (struct f2fs_encryption_policy __user *)arg, sizeof(policy))) return -EFAULT; - return f2fs_process_policy(&policy, inode); + mutex_lock(&inode->i_mutex); + + err = f2fs_process_policy(&policy, inode); + + mutex_unlock(&inode->i_mutex); + + return err; #else return -EOPNOTSUPP; #endif diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 106dda1e743d..d2e75fe2e072 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -918,6 +918,79 @@ loff_t max_file_size(unsigned bits) return result; } +static inline bool sanity_check_area_boundary(struct super_block *sb, + struct f2fs_super_block *raw_super) +{ + u32 segment0_blkaddr = le32_to_cpu(raw_super->segment0_blkaddr); + u32 cp_blkaddr = le32_to_cpu(raw_super->cp_blkaddr); + u32 sit_blkaddr = le32_to_cpu(raw_super->sit_blkaddr); + u32 nat_blkaddr = le32_to_cpu(raw_super->nat_blkaddr); + u32 ssa_blkaddr = le32_to_cpu(raw_super->ssa_blkaddr); + u32 main_blkaddr = le32_to_cpu(raw_super->main_blkaddr); + u32 segment_count_ckpt = le32_to_cpu(raw_super->segment_count_ckpt); + u32 segment_count_sit = le32_to_cpu(raw_super->segment_count_sit); + u32 segment_count_nat = le32_to_cpu(raw_super->segment_count_nat); + u32 segment_count_ssa = le32_to_cpu(raw_super->segment_count_ssa); + u32 segment_count_main = le32_to_cpu(raw_super->segment_count_main); + u32 segment_count = le32_to_cpu(raw_super->segment_count); + u32 log_blocks_per_seg = le32_to_cpu(raw_super->log_blocks_per_seg); + + if (segment0_blkaddr != cp_blkaddr) { + f2fs_msg(sb, KERN_INFO, + "Mismatch start address, segment0(%u) cp_blkaddr(%u)", + segment0_blkaddr, cp_blkaddr); + return true; + } + + if (cp_blkaddr + (segment_count_ckpt << log_blocks_per_seg) != + sit_blkaddr) { + f2fs_msg(sb, KERN_INFO, + "Wrong CP boundary, start(%u) end(%u) blocks(%u)", + cp_blkaddr, sit_blkaddr, + segment_count_ckpt << log_blocks_per_seg); + return true; + } + + if (sit_blkaddr + (segment_count_sit << log_blocks_per_seg) != + nat_blkaddr) { + f2fs_msg(sb, KERN_INFO, + "Wrong SIT boundary, start(%u) end(%u) blocks(%u)", + sit_blkaddr, nat_blkaddr, + segment_count_sit << log_blocks_per_seg); + return true; + } + + if (nat_blkaddr + (segment_count_nat << log_blocks_per_seg) != + ssa_blkaddr) { + f2fs_msg(sb, KERN_INFO, + "Wrong NAT boundary, start(%u) end(%u) blocks(%u)", + nat_blkaddr, ssa_blkaddr, + segment_count_nat << log_blocks_per_seg); + return true; + } + + if (ssa_blkaddr + (segment_count_ssa << log_blocks_per_seg) != + main_blkaddr) { + f2fs_msg(sb, KERN_INFO, + "Wrong SSA boundary, start(%u) end(%u) blocks(%u)", + ssa_blkaddr, main_blkaddr, + segment_count_ssa << log_blocks_per_seg); + return true; + } + + if (main_blkaddr + (segment_count_main << log_blocks_per_seg) != + segment0_blkaddr + (segment_count << log_blocks_per_seg)) { + f2fs_msg(sb, KERN_INFO, + "Wrong MAIN_AREA boundary, start(%u) end(%u) blocks(%u)", + main_blkaddr, + segment0_blkaddr + (segment_count << log_blocks_per_seg), + segment_count_main << log_blocks_per_seg); + return true; + } + + return false; +} + static int sanity_check_raw_super(struct super_block *sb, struct f2fs_super_block *raw_super) { @@ -947,6 +1020,14 @@ static int sanity_check_raw_super(struct super_block *sb, return 1; } + /* 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", + le32_to_cpu(raw_super->log_blocks_per_seg)); + return 1; + } + /* Currently, support 512/1024/2048/4096 bytes sector size */ if (le32_to_cpu(raw_super->log_sectorsize) > F2FS_MAX_LOG_SECTOR_SIZE || @@ -965,6 +1046,23 @@ static int sanity_check_raw_super(struct super_block *sb, le32_to_cpu(raw_super->log_sectorsize)); return 1; } + + /* check reserved ino info */ + if (le32_to_cpu(raw_super->node_ino) != 1 || + le32_to_cpu(raw_super->meta_ino) != 2 || + le32_to_cpu(raw_super->root_ino) != 3) { + f2fs_msg(sb, KERN_INFO, + "Invalid Fs Meta Ino: node(%u) meta(%u) root(%u)", + le32_to_cpu(raw_super->node_ino), + le32_to_cpu(raw_super->meta_ino), + le32_to_cpu(raw_super->root_ino)); + return 1; + } + + /* check CP/SIT/NAT/SSA/MAIN_AREA area boundary */ + if (sanity_check_area_boundary(sb, raw_super)) + return 1; + return 0; } diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c index ad8a5b757cc7..a443c6e54412 100644 --- a/fs/gfs2/dir.c +++ b/fs/gfs2/dir.c @@ -760,7 +760,7 @@ static int get_first_leaf(struct gfs2_inode *dip, u32 index, int error; error = get_leaf_nr(dip, index, &leaf_no); - if (!error) + if (!IS_ERR_VALUE(error)) error = get_leaf(dip, leaf_no, bh_out); return error; @@ -976,7 +976,7 @@ static int dir_split_leaf(struct inode *inode, const struct qstr *name) index = name->hash >> (32 - dip->i_depth); error = get_leaf_nr(dip, index, &leaf_no); - if (error) + if (IS_ERR_VALUE(error)) return error; /* Get the old leaf block */ diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index de7b4f97ac75..be519416c112 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -207,7 +207,7 @@ struct lm_lockname { struct gfs2_sbd *ln_sbd; u64 ln_number; unsigned int ln_type; -}; +} __packed __aligned(sizeof(int)); #define lm_name_equal(name1, name2) \ (((name1)->ln_number == (name2)->ln_number) && \ diff --git a/fs/mbcache.c b/fs/mbcache.c index 187477ded6b3..ab1da987d1ae 100644 --- a/fs/mbcache.c +++ b/fs/mbcache.c @@ -262,7 +262,6 @@ mb_cache_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) list_del_init(&ce->e_lru_list); if (ce->e_used || ce->e_queued || atomic_read(&ce->e_refcnt)) continue; - spin_unlock(&mb_cache_spinlock); /* Prevent any find or get operation on the entry */ hlist_bl_lock(ce->e_block_hash_p); hlist_bl_lock(ce->e_index_hash_p); @@ -271,10 +270,10 @@ mb_cache_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) !list_empty(&ce->e_lru_list)) { hlist_bl_unlock(ce->e_index_hash_p); hlist_bl_unlock(ce->e_block_hash_p); - spin_lock(&mb_cache_spinlock); continue; } __mb_cache_entry_unhash_unlock(ce); + spin_unlock(&mb_cache_spinlock); list_add_tail(&ce->e_lru_list, &free_list); spin_lock(&mb_cache_spinlock); } @@ -516,7 +515,6 @@ mb_cache_entry_alloc(struct mb_cache *cache, gfp_t gfp_flags) if (ce->e_used || ce->e_queued || atomic_read(&ce->e_refcnt)) continue; - spin_unlock(&mb_cache_spinlock); /* * Prevent any find or get operation on the * entry. @@ -530,13 +528,13 @@ mb_cache_entry_alloc(struct mb_cache *cache, gfp_t gfp_flags) hlist_bl_unlock(ce->e_index_hash_p); hlist_bl_unlock(ce->e_block_hash_p); l = &mb_cache_lru_list; - spin_lock(&mb_cache_spinlock); continue; } mb_assert(list_empty(&ce->e_lru_list)); mb_assert(!(ce->e_used || ce->e_queued || atomic_read(&ce->e_refcnt))); __mb_cache_entry_unhash_unlock(ce); + spin_unlock(&mb_cache_spinlock); goto found; } } @@ -670,6 +668,7 @@ mb_cache_entry_get(struct mb_cache *cache, struct block_device *bdev, cache->c_bucket_bits); block_hash_p = &cache->c_block_hash[bucket]; /* First serialize access to the block corresponding hash chain. */ + spin_lock(&mb_cache_spinlock); hlist_bl_lock(block_hash_p); hlist_bl_for_each_entry(ce, l, block_hash_p, e_block_list) { mb_assert(ce->e_block_hash_p == block_hash_p); @@ -678,9 +677,11 @@ mb_cache_entry_get(struct mb_cache *cache, struct block_device *bdev, * Prevent a free from removing the entry. */ atomic_inc(&ce->e_refcnt); + if (!list_empty(&ce->e_lru_list)) + list_del_init(&ce->e_lru_list); hlist_bl_unlock(block_hash_p); + spin_unlock(&mb_cache_spinlock); __spin_lock_mb_cache_entry(ce); - atomic_dec(&ce->e_refcnt); if (ce->e_used > 0) { DEFINE_WAIT(wait); while (ce->e_used > 0) { @@ -695,13 +696,9 @@ mb_cache_entry_get(struct mb_cache *cache, struct block_device *bdev, finish_wait(&mb_cache_queue, &wait); } ce->e_used += 1 + MB_CACHE_WRITER; + atomic_dec(&ce->e_refcnt); __spin_unlock_mb_cache_entry(ce); - if (!list_empty(&ce->e_lru_list)) { - spin_lock(&mb_cache_spinlock); - list_del_init(&ce->e_lru_list); - spin_unlock(&mb_cache_spinlock); - } if (!__mb_cache_entry_is_block_hashed(ce)) { __mb_cache_entry_release(ce); return NULL; @@ -710,6 +707,7 @@ mb_cache_entry_get(struct mb_cache *cache, struct block_device *bdev, } } hlist_bl_unlock(block_hash_p); + spin_unlock(&mb_cache_spinlock); return NULL; } diff --git a/fs/mbcache2.c b/fs/mbcache2.c new file mode 100644 index 000000000000..5c3e1a8c38f6 --- /dev/null +++ b/fs/mbcache2.c @@ -0,0 +1,359 @@ +#include <linux/spinlock.h> +#include <linux/slab.h> +#include <linux/list.h> +#include <linux/list_bl.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/mbcache2.h> + +/* + * Mbcache is a simple key-value store. Keys need not be unique, however + * key-value pairs are expected to be unique (we use this fact in + * mb2_cache_entry_delete_block()). + * + * Ext2 and ext4 use this cache for deduplication of extended attribute blocks. + * They use hash of a block contents as a key and block number as a value. + * That's why keys need not be unique (different xattr blocks may end up having + * the same hash). However block number always uniquely identifies a cache + * entry. + * + * We provide functions for creation and removal of entries, search by key, + * and a special "delete entry with given key-value pair" operation. Fixed + * size hash table is used for fast key lookups. + */ + +struct mb2_cache { + /* Hash table of entries */ + struct hlist_bl_head *c_hash; + /* log2 of hash table size */ + int c_bucket_bits; + /* Protects c_lru_list, c_entry_count */ + spinlock_t c_lru_list_lock; + struct list_head c_lru_list; + /* Number of entries in cache */ + unsigned long c_entry_count; + struct shrinker c_shrink; +}; + +static struct kmem_cache *mb2_entry_cache; + +/* + * mb2_cache_entry_create - create entry in cache + * @cache - cache where the entry should be created + * @mask - gfp mask with which the entry should be allocated + * @key - key of the entry + * @block - block that contains data + * + * Creates entry in @cache with key @key and records that data is stored in + * block @block. The function returns -EBUSY if entry with the same key + * and for the same block already exists in cache. Otherwise 0 is returned. + */ +int mb2_cache_entry_create(struct mb2_cache *cache, gfp_t mask, u32 key, + sector_t block) +{ + struct mb2_cache_entry *entry, *dup; + struct hlist_bl_node *dup_node; + struct hlist_bl_head *head; + + entry = kmem_cache_alloc(mb2_entry_cache, mask); + if (!entry) + return -ENOMEM; + + INIT_LIST_HEAD(&entry->e_lru_list); + /* One ref for hash, one ref returned */ + atomic_set(&entry->e_refcnt, 1); + entry->e_key = key; + entry->e_block = block; + head = &cache->c_hash[hash_32(key, cache->c_bucket_bits)]; + entry->e_hash_list_head = head; + hlist_bl_lock(head); + hlist_bl_for_each_entry(dup, dup_node, head, e_hash_list) { + if (dup->e_key == key && dup->e_block == block) { + hlist_bl_unlock(head); + kmem_cache_free(mb2_entry_cache, entry); + return -EBUSY; + } + } + hlist_bl_add_head(&entry->e_hash_list, head); + hlist_bl_unlock(head); + + spin_lock(&cache->c_lru_list_lock); + list_add_tail(&entry->e_lru_list, &cache->c_lru_list); + /* Grab ref for LRU list */ + atomic_inc(&entry->e_refcnt); + cache->c_entry_count++; + spin_unlock(&cache->c_lru_list_lock); + + return 0; +} +EXPORT_SYMBOL(mb2_cache_entry_create); + +void __mb2_cache_entry_free(struct mb2_cache_entry *entry) +{ + kmem_cache_free(mb2_entry_cache, entry); +} +EXPORT_SYMBOL(__mb2_cache_entry_free); + +static struct mb2_cache_entry *__entry_find(struct mb2_cache *cache, + struct mb2_cache_entry *entry, + u32 key) +{ + struct mb2_cache_entry *old_entry = entry; + struct hlist_bl_node *node; + struct hlist_bl_head *head; + + if (entry) + head = entry->e_hash_list_head; + else + head = &cache->c_hash[hash_32(key, cache->c_bucket_bits)]; + hlist_bl_lock(head); + if (entry && !hlist_bl_unhashed(&entry->e_hash_list)) + node = entry->e_hash_list.next; + else + node = hlist_bl_first(head); + while (node) { + entry = hlist_bl_entry(node, struct mb2_cache_entry, + e_hash_list); + if (entry->e_key == key) { + atomic_inc(&entry->e_refcnt); + goto out; + } + node = node->next; + } + entry = NULL; +out: + hlist_bl_unlock(head); + if (old_entry) + mb2_cache_entry_put(cache, old_entry); + + return entry; +} + +/* + * mb2_cache_entry_find_first - find the first entry in cache with given key + * @cache: cache where we should search + * @key: key to look for + * + * Search in @cache for entry with key @key. Grabs reference to the first + * entry found and returns the entry. + */ +struct mb2_cache_entry *mb2_cache_entry_find_first(struct mb2_cache *cache, + u32 key) +{ + return __entry_find(cache, NULL, key); +} +EXPORT_SYMBOL(mb2_cache_entry_find_first); + +/* + * mb2_cache_entry_find_next - find next entry in cache with the same + * @cache: cache where we should search + * @entry: entry to start search from + * + * Finds next entry in the hash chain which has the same key as @entry. + * If @entry is unhashed (which can happen when deletion of entry races + * with the search), finds the first entry in the hash chain. The function + * drops reference to @entry and returns with a reference to the found entry. + */ +struct mb2_cache_entry *mb2_cache_entry_find_next(struct mb2_cache *cache, + struct mb2_cache_entry *entry) +{ + return __entry_find(cache, entry, entry->e_key); +} +EXPORT_SYMBOL(mb2_cache_entry_find_next); + +/* mb2_cache_entry_delete_block - remove information about block from cache + * @cache - cache we work with + * @key - key of the entry to remove + * @block - block containing data for @key + * + * Remove entry from cache @cache with key @key with data stored in @block. + */ +void mb2_cache_entry_delete_block(struct mb2_cache *cache, u32 key, + sector_t block) +{ + struct hlist_bl_node *node; + struct hlist_bl_head *head; + struct mb2_cache_entry *entry; + + head = &cache->c_hash[hash_32(key, cache->c_bucket_bits)]; + hlist_bl_lock(head); + hlist_bl_for_each_entry(entry, node, head, e_hash_list) { + if (entry->e_key == key && entry->e_block == block) { + /* We keep hash list reference to keep entry alive */ + hlist_bl_del_init(&entry->e_hash_list); + hlist_bl_unlock(head); + spin_lock(&cache->c_lru_list_lock); + if (!list_empty(&entry->e_lru_list)) { + list_del_init(&entry->e_lru_list); + cache->c_entry_count--; + atomic_dec(&entry->e_refcnt); + } + spin_unlock(&cache->c_lru_list_lock); + mb2_cache_entry_put(cache, entry); + return; + } + } + hlist_bl_unlock(head); +} +EXPORT_SYMBOL(mb2_cache_entry_delete_block); + +/* mb2_cache_entry_touch - cache entry got used + * @cache - cache the entry belongs to + * @entry - entry that got used + * + * Move entry in lru list to reflect the fact that it was used. + */ +void mb2_cache_entry_touch(struct mb2_cache *cache, + struct mb2_cache_entry *entry) +{ + spin_lock(&cache->c_lru_list_lock); + if (!list_empty(&entry->e_lru_list)) + list_move_tail(&cache->c_lru_list, &entry->e_lru_list); + spin_unlock(&cache->c_lru_list_lock); +} +EXPORT_SYMBOL(mb2_cache_entry_touch); + +static unsigned long mb2_cache_count(struct shrinker *shrink, + struct shrink_control *sc) +{ + struct mb2_cache *cache = container_of(shrink, struct mb2_cache, + c_shrink); + + return cache->c_entry_count; +} + +/* Shrink number of entries in cache */ +static unsigned long mb2_cache_scan(struct shrinker *shrink, + struct shrink_control *sc) +{ + int nr_to_scan = sc->nr_to_scan; + struct mb2_cache *cache = container_of(shrink, struct mb2_cache, + c_shrink); + struct mb2_cache_entry *entry; + struct hlist_bl_head *head; + unsigned int shrunk = 0; + + spin_lock(&cache->c_lru_list_lock); + while (nr_to_scan-- && !list_empty(&cache->c_lru_list)) { + entry = list_first_entry(&cache->c_lru_list, + struct mb2_cache_entry, e_lru_list); + list_del_init(&entry->e_lru_list); + cache->c_entry_count--; + /* + * We keep LRU list reference so that entry doesn't go away + * from under us. + */ + spin_unlock(&cache->c_lru_list_lock); + head = entry->e_hash_list_head; + hlist_bl_lock(head); + if (!hlist_bl_unhashed(&entry->e_hash_list)) { + hlist_bl_del_init(&entry->e_hash_list); + atomic_dec(&entry->e_refcnt); + } + hlist_bl_unlock(head); + if (mb2_cache_entry_put(cache, entry)) + shrunk++; + cond_resched(); + spin_lock(&cache->c_lru_list_lock); + } + spin_unlock(&cache->c_lru_list_lock); + + return shrunk; +} + +/* + * mb2_cache_create - create cache + * @bucket_bits: log2 of the hash table size + * + * Create cache for keys with 2^bucket_bits hash entries. + */ +struct mb2_cache *mb2_cache_create(int bucket_bits) +{ + struct mb2_cache *cache; + int bucket_count = 1 << bucket_bits; + int i; + + if (!try_module_get(THIS_MODULE)) + return NULL; + + cache = kzalloc(sizeof(struct mb2_cache), GFP_KERNEL); + if (!cache) + goto err_out; + cache->c_bucket_bits = bucket_bits; + INIT_LIST_HEAD(&cache->c_lru_list); + spin_lock_init(&cache->c_lru_list_lock); + cache->c_hash = kmalloc(bucket_count * sizeof(struct hlist_bl_head), + GFP_KERNEL); + if (!cache->c_hash) { + kfree(cache); + goto err_out; + } + for (i = 0; i < bucket_count; i++) + INIT_HLIST_BL_HEAD(&cache->c_hash[i]); + + cache->c_shrink.count_objects = mb2_cache_count; + cache->c_shrink.scan_objects = mb2_cache_scan; + cache->c_shrink.seeks = DEFAULT_SEEKS; + register_shrinker(&cache->c_shrink); + + return cache; + +err_out: + module_put(THIS_MODULE); + return NULL; +} +EXPORT_SYMBOL(mb2_cache_create); + +/* + * mb2_cache_destroy - destroy cache + * @cache: the cache to destroy + * + * Free all entries in cache and cache itself. Caller must make sure nobody + * (except shrinker) can reach @cache when calling this. + */ +void mb2_cache_destroy(struct mb2_cache *cache) +{ + struct mb2_cache_entry *entry, *next; + + unregister_shrinker(&cache->c_shrink); + + /* + * We don't bother with any locking. Cache must not be used at this + * point. + */ + list_for_each_entry_safe(entry, next, &cache->c_lru_list, e_lru_list) { + if (!hlist_bl_unhashed(&entry->e_hash_list)) { + hlist_bl_del_init(&entry->e_hash_list); + atomic_dec(&entry->e_refcnt); + } else + WARN_ON(1); + list_del(&entry->e_lru_list); + WARN_ON(atomic_read(&entry->e_refcnt) != 1); + mb2_cache_entry_put(cache, entry); + } + kfree(cache->c_hash); + kfree(cache); + module_put(THIS_MODULE); +} +EXPORT_SYMBOL(mb2_cache_destroy); + +static int __init mb2cache_init(void) +{ + mb2_entry_cache = kmem_cache_create("mbcache", + sizeof(struct mb2_cache_entry), 0, + SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, NULL); + BUG_ON(!mb2_entry_cache); + return 0; +} + +static void __exit mb2cache_exit(void) +{ + kmem_cache_destroy(mb2_entry_cache); +} + +module_init(mb2cache_init) +module_exit(mb2cache_exit) + +MODULE_AUTHOR("Jan Kara <jack@suse.cz>"); +MODULE_DESCRIPTION("Meta block cache (for extended attributes)"); +MODULE_LICENSE("GPL"); diff --git a/fs/mount.h b/fs/mount.h index 3dc7dea5a357..13a4ebbbaa74 100644 --- a/fs/mount.h +++ b/fs/mount.h @@ -13,6 +13,8 @@ struct mnt_namespace { u64 seq; /* Sequence number to prevent loops */ wait_queue_head_t poll; u64 event; + unsigned int mounts; /* # of mounts in the namespace */ + unsigned int pending_mounts; }; struct mnt_pcp { diff --git a/fs/namespace.c b/fs/namespace.c index c1477882a853..a22959c97384 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -27,6 +27,9 @@ #include "pnode.h" #include "internal.h" +/* Maximum number of mounts in a mount namespace */ +unsigned int sysctl_mount_max __read_mostly = 100000; + static unsigned int m_hash_mask __read_mostly; static unsigned int m_hash_shift __read_mostly; static unsigned int mp_hash_mask __read_mostly; @@ -926,6 +929,9 @@ static void commit_tree(struct mount *mnt) list_splice(&head, n->list.prev); + n->mounts += n->pending_mounts; + n->pending_mounts = 0; + __attach_mnt(mnt, parent); touch_mnt_namespace(n); } @@ -1465,11 +1471,16 @@ static void umount_tree(struct mount *mnt, enum umount_tree_flags how) propagate_umount(&tmp_list); while (!list_empty(&tmp_list)) { + struct mnt_namespace *ns; bool disconnect; p = list_first_entry(&tmp_list, struct mount, mnt_list); list_del_init(&p->mnt_expire); list_del_init(&p->mnt_list); - __touch_mnt_namespace(p->mnt_ns); + ns = p->mnt_ns; + if (ns) { + ns->mounts--; + __touch_mnt_namespace(ns); + } p->mnt_ns = NULL; if (how & UMOUNT_SYNC) p->mnt.mnt_flags |= MNT_SYNC_UMOUNT; @@ -1870,6 +1881,28 @@ static int invent_group_ids(struct mount *mnt, bool recurse) return 0; } +int count_mounts(struct mnt_namespace *ns, struct mount *mnt) +{ + unsigned int max = READ_ONCE(sysctl_mount_max); + unsigned int mounts = 0, old, pending, sum; + struct mount *p; + + for (p = mnt; p; p = next_mnt(p, mnt)) + mounts++; + + old = ns->mounts; + pending = ns->pending_mounts; + sum = old + pending; + if ((old > sum) || + (pending > sum) || + (max < sum) || + (mounts > (max - sum))) + return -ENOSPC; + + ns->pending_mounts = pending + mounts; + return 0; +} + /* * @source_mnt : mount tree to be attached * @nd : place the mount tree @source_mnt is attached @@ -1939,6 +1972,7 @@ static int attach_recursive_mnt(struct mount *source_mnt, struct path *parent_path) { HLIST_HEAD(tree_list); + struct mnt_namespace *ns = dest_mnt->mnt_ns; struct mountpoint *smp; struct mount *child, *p; struct hlist_node *n; @@ -1951,6 +1985,13 @@ static int attach_recursive_mnt(struct mount *source_mnt, if (IS_ERR(smp)) return PTR_ERR(smp); + /* Is there space to add these mounts to the mount namespace? */ + if (!parent_path) { + err = count_mounts(ns, source_mnt); + if (err) + goto out; + } + if (IS_MNT_SHARED(dest_mnt)) { err = invent_group_ids(source_mnt, true); if (err) @@ -1990,11 +2031,14 @@ static int attach_recursive_mnt(struct mount *source_mnt, out_cleanup_ids: while (!hlist_empty(&tree_list)) { child = hlist_entry(tree_list.first, struct mount, mnt_hash); + child->mnt_parent->mnt_ns->pending_mounts = 0; umount_tree(child, UMOUNT_SYNC); } unlock_mount_hash(); cleanup_group_ids(source_mnt, NULL); out: + ns->pending_mounts = 0; + read_seqlock_excl(&mount_lock); put_mountpoint(smp); read_sequnlock_excl(&mount_lock); @@ -2830,6 +2874,8 @@ static struct mnt_namespace *alloc_mnt_ns(struct user_namespace *user_ns) init_waitqueue_head(&new_ns->poll); new_ns->event = 0; new_ns->user_ns = get_user_ns(user_ns); + new_ns->mounts = 0; + new_ns->pending_mounts = 0; return new_ns; } @@ -2879,6 +2925,7 @@ struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns, q = new; while (p) { q->mnt_ns = new_ns; + new_ns->mounts++; if (new_fs) { if (&p->mnt == new_fs->root.mnt) { new_fs->root.mnt = mntget(&q->mnt); @@ -2917,6 +2964,7 @@ static struct mnt_namespace *create_mnt_ns(struct vfsmount *m) struct mount *mnt = real_mount(m); mnt->mnt_ns = new_ns; new_ns->root = mnt; + new_ns->mounts++; list_add(&mnt->mnt_list, &new_ns->list); } else { mntput(m); diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index ad4e2377dd63..5be1fa6b676d 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -656,6 +656,37 @@ static __be32 map_new_errors(u32 vers, __be32 nfserr) return nfserr; } +/* + * A write procedure can have a large argument, and a read procedure can + * have a large reply, but no NFSv2 or NFSv3 procedure has argument and + * reply that can both be larger than a page. The xdr code has taken + * advantage of this assumption to be a sloppy about bounds checking in + * some cases. Pending a rewrite of the NFSv2/v3 xdr code to fix that + * problem, we enforce these assumptions here: + */ +static bool nfs_request_too_big(struct svc_rqst *rqstp, + struct svc_procedure *proc) +{ + /* + * The ACL code has more careful bounds-checking and is not + * susceptible to this problem: + */ + if (rqstp->rq_prog != NFS_PROGRAM) + return false; + /* + * Ditto NFSv4 (which can in theory have argument and reply both + * more than a page): + */ + if (rqstp->rq_vers >= 4) + return false; + /* The reply will be small, we're OK: */ + if (proc->pc_xdrressize > 0 && + proc->pc_xdrressize < XDR_QUADLEN(PAGE_SIZE)) + return false; + + return rqstp->rq_arg.len > PAGE_SIZE; +} + int nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp) { @@ -668,6 +699,11 @@ nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp) rqstp->rq_vers, rqstp->rq_proc); proc = rqstp->rq_procinfo; + if (nfs_request_too_big(rqstp, proc)) { + dprintk("nfsd: NFSv%d argument too large\n", rqstp->rq_vers); + *statp = rpc_garbage_args; + return 1; + } /* * Give the xdr decoder a chance to change this if it wants * (necessary in the NFSv4.0 compound case) diff --git a/fs/pnode.c b/fs/pnode.c index 1d16bb3bdf5e..b5f97c605d98 100644 --- a/fs/pnode.c +++ b/fs/pnode.c @@ -259,7 +259,7 @@ static int propagate_one(struct mount *m) read_sequnlock_excl(&mount_lock); } hlist_add_head(&child->mnt_hash, list); - return 0; + return count_mounts(m->mnt_ns, child); } /* diff --git a/fs/pnode.h b/fs/pnode.h index f41fc9a6c48e..a9a6576540ad 100644 --- a/fs/pnode.h +++ b/fs/pnode.h @@ -55,4 +55,5 @@ void mnt_change_mountpoint(struct mount *parent, struct mountpoint *mp, struct mount *copy_tree(struct mount *, struct dentry *, int); bool is_path_reachable(struct mount *, struct dentry *, const struct path *root); +int count_mounts(struct mnt_namespace *ns, struct mount *mnt); #endif /* _LINUX_PNODE_H */ diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 6a44fb94228c..fc5243c331c4 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -867,7 +867,14 @@ static inline void clear_soft_dirty(struct vm_area_struct *vma, static inline void clear_soft_dirty_pmd(struct vm_area_struct *vma, unsigned long addr, pmd_t *pmdp) { - pmd_t pmd = pmdp_huge_get_and_clear(vma->vm_mm, addr, pmdp); + pmd_t pmd = *pmdp; + + /* See comment in change_huge_pmd() */ + pmdp_invalidate(vma, addr, pmdp); + if (pmd_dirty(*pmdp)) + pmd = pmd_mkdirty(pmd); + if (pmd_young(*pmdp)) + pmd = pmd_mkyoung(pmd); pmd = pmd_wrprotect(pmd); pmd = pmd_clear_soft_dirty(pmd); diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index 971928ab6c21..7a19e77fce99 100644 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -46,7 +46,8 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) spin_unlock(&dentry->d_lock); /* check uninitialized obb_dentry and - * whether the base obbpath has been changed or not */ + * whether the base obbpath has been changed or not + */ if (is_obbpath_invalid(dentry)) { d_drop(dentry); return 0; @@ -59,6 +60,14 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) lower_dentry = lower_path.dentry; lower_cur_parent_dentry = dget_parent(lower_dentry); + if ((lower_dentry->d_flags & DCACHE_OP_REVALIDATE)) { + err = lower_dentry->d_op->d_revalidate(lower_dentry, flags); + if (err == 0) { + d_drop(dentry); + goto out; + } + } + spin_lock(&lower_dentry->d_lock); if (d_unhashed(lower_dentry)) { spin_unlock(&lower_dentry->d_lock); @@ -76,17 +85,13 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) if (dentry < lower_dentry) { spin_lock(&dentry->d_lock); - spin_lock(&lower_dentry->d_lock); + spin_lock_nested(&lower_dentry->d_lock, DENTRY_D_LOCK_NESTED); } else { spin_lock(&lower_dentry->d_lock); - spin_lock(&dentry->d_lock); + spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); } - if (dentry->d_name.len != lower_dentry->d_name.len) { - __d_drop(dentry); - err = 0; - } else if (strncasecmp(dentry->d_name.name, lower_dentry->d_name.name, - dentry->d_name.len) != 0) { + if (!qstr_case_eq(&dentry->d_name, &lower_dentry->d_name)) { __d_drop(dentry); err = 0; } @@ -110,12 +115,10 @@ out: static void sdcardfs_d_release(struct dentry *dentry) { /* release and reset the lower paths */ - if(has_graft_path(dentry)) { + if (has_graft_path(dentry)) sdcardfs_put_reset_orig_path(dentry); - } sdcardfs_put_reset_lower_path(dentry); free_dentry_private_data(dentry); - return; } static int sdcardfs_hash_ci(const struct dentry *dentry, @@ -132,12 +135,10 @@ static int sdcardfs_hash_ci(const struct dentry *dentry, unsigned long hash; name = qstr->name; - //len = vfat_striptail_len(qstr); len = qstr->len; hash = init_name_hash(); while (len--) - //hash = partial_name_hash(nls_tolower(t, *name++), hash); hash = partial_name_hash(tolower(*name++), hash); qstr->hash = end_name_hash(hash); @@ -151,35 +152,25 @@ static int sdcardfs_cmp_ci(const struct dentry *parent, const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { - /* This function is copy of vfat_cmpi */ - // FIXME Should we support national language? - //struct nls_table *t = MSDOS_SB(parent->d_sb)->nls_io; - //unsigned int alen, blen; + /* FIXME Should we support national language? */ - /* A filename cannot end in '.' or we treat it like it has none */ - /* - alen = vfat_striptail_len(name); - blen = __vfat_striptail_len(len, str); - if (alen == blen) { - if (nls_strnicmp(t, name->name, str, alen) == 0) - return 0; - } - */ if (name->len == len) { - if (strncasecmp(name->name, str, len) == 0) + if (str_n_case_eq(name->name, str, len)) return 0; } return 1; } -static void sdcardfs_canonical_path(const struct path *path, struct path *actual_path) { +static void sdcardfs_canonical_path(const struct path *path, + struct path *actual_path) +{ sdcardfs_get_real_lower(path->dentry, actual_path); } const struct dentry_operations sdcardfs_ci_dops = { .d_revalidate = sdcardfs_d_revalidate, .d_release = sdcardfs_d_release, - .d_hash = sdcardfs_hash_ci, + .d_hash = sdcardfs_hash_ci, .d_compare = sdcardfs_cmp_ci, .d_canonical_path = sdcardfs_canonical_path, }; diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 0bb442338a85..b4595aab5713 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -37,7 +37,8 @@ static void inherit_derived_state(struct inode *parent, struct inode *child) /* helper function for derived state */ void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, - uid_t uid, bool under_android, struct inode *top) + uid_t uid, bool under_android, + struct inode *top) { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); @@ -50,12 +51,17 @@ void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, set_top(info, top); } -/* While renaming, there is a point where we want the path from dentry, but the name from newdentry */ -void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name) +/* While renaming, there is a point where we want the path from dentry, + * but the name from newdentry + */ +void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, + const struct qstr *name) { struct sdcardfs_inode_info *info = SDCARDFS_I(d_inode(dentry)); - struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent)); + struct sdcardfs_inode_info *parent_info = SDCARDFS_I(d_inode(parent)); appid_t appid; + unsigned long user_num; + int err; struct qstr q_Android = QSTR_LITERAL("Android"); struct qstr q_data = QSTR_LITERAL("data"); struct qstr q_obb = QSTR_LITERAL("obb"); @@ -77,58 +83,61 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, co return; /* Derive custom permissions based on parent and current node */ switch (parent_info->perm) { - case PERM_INHERIT: - case PERM_ANDROID_PACKAGE_CACHE: - /* Already inherited above */ - break; - case PERM_PRE_ROOT: - /* Legacy internal layout places users at top level */ - info->perm = PERM_ROOT; - info->userid = simple_strtoul(name->name, NULL, 10); + case PERM_INHERIT: + case PERM_ANDROID_PACKAGE_CACHE: + /* Already inherited above */ + break; + case PERM_PRE_ROOT: + /* Legacy internal layout places users at top level */ + info->perm = PERM_ROOT; + err = kstrtoul(name->name, 10, &user_num); + if (err) + info->userid = 0; + else + info->userid = user_num; + set_top(info, &info->vfs_inode); + break; + case PERM_ROOT: + /* Assume masked off by default. */ + if (qstr_case_eq(name, &q_Android)) { + /* App-specific directories inside; let anyone traverse */ + info->perm = PERM_ANDROID; + info->under_android = true; set_top(info, &info->vfs_inode); - break; - case PERM_ROOT: - /* Assume masked off by default. */ - if (qstr_case_eq(name, &q_Android)) { - /* App-specific directories inside; let anyone traverse */ - info->perm = PERM_ANDROID; - info->under_android = true; - set_top(info, &info->vfs_inode); - } - break; - case PERM_ANDROID: - if (qstr_case_eq(name, &q_data)) { - /* App-specific directories inside; let anyone traverse */ - info->perm = PERM_ANDROID_DATA; - set_top(info, &info->vfs_inode); - } else if (qstr_case_eq(name, &q_obb)) { - /* App-specific directories inside; let anyone traverse */ - info->perm = PERM_ANDROID_OBB; - info->under_obb = true; - set_top(info, &info->vfs_inode); - /* Single OBB directory is always shared */ - } else if (qstr_case_eq(name, &q_media)) { - /* App-specific directories inside; let anyone traverse */ - info->perm = PERM_ANDROID_MEDIA; - set_top(info, &info->vfs_inode); - } - break; - case PERM_ANDROID_OBB: - case PERM_ANDROID_DATA: - case PERM_ANDROID_MEDIA: - info->perm = PERM_ANDROID_PACKAGE; - appid = get_appid(name->name); - if (appid != 0 && !is_excluded(name->name, parent_info->userid)) { - info->d_uid = multiuser_get_uid(parent_info->userid, appid); - } + } + break; + case PERM_ANDROID: + if (qstr_case_eq(name, &q_data)) { + /* App-specific directories inside; let anyone traverse */ + info->perm = PERM_ANDROID_DATA; set_top(info, &info->vfs_inode); - break; - case PERM_ANDROID_PACKAGE: - if (qstr_case_eq(name, &q_cache)) { - info->perm = PERM_ANDROID_PACKAGE_CACHE; - info->under_cache = true; - } - break; + } else if (qstr_case_eq(name, &q_obb)) { + /* App-specific directories inside; let anyone traverse */ + info->perm = PERM_ANDROID_OBB; + info->under_obb = true; + set_top(info, &info->vfs_inode); + /* Single OBB directory is always shared */ + } else if (qstr_case_eq(name, &q_media)) { + /* App-specific directories inside; let anyone traverse */ + info->perm = PERM_ANDROID_MEDIA; + set_top(info, &info->vfs_inode); + } + break; + case PERM_ANDROID_OBB: + case PERM_ANDROID_DATA: + case PERM_ANDROID_MEDIA: + info->perm = PERM_ANDROID_PACKAGE; + appid = get_appid(name->name); + if (appid != 0 && !is_excluded(name->name, parent_info->userid)) + info->d_uid = multiuser_get_uid(parent_info->userid, appid); + set_top(info, &info->vfs_inode); + break; + case PERM_ANDROID_PACKAGE: + if (qstr_case_eq(name, &q_cache)) { + info->perm = PERM_ANDROID_PACKAGE_CACHE; + info->under_cache = true; + } + break; } } @@ -137,7 +146,8 @@ void get_derived_permission(struct dentry *parent, struct dentry *dentry) get_derived_permission_new(parent, dentry, &dentry->d_name); } -static appid_t get_type(const char *name) { +static appid_t get_type(const char *name) +{ const char *ext = strrchr(name, '.'); appid_t id; @@ -149,7 +159,8 @@ static appid_t get_type(const char *name) { return AID_MEDIA_RW; } -void fixup_lower_ownership(struct dentry* dentry, const char *name) { +void fixup_lower_ownership(struct dentry *dentry, const char *name) +{ struct path path; struct inode *inode; struct inode *delegated_inode = NULL; @@ -175,49 +186,49 @@ void fixup_lower_ownership(struct dentry* dentry, const char *name) { } switch (perm) { - case PERM_ROOT: - case PERM_ANDROID: - case PERM_ANDROID_DATA: - case PERM_ANDROID_MEDIA: - case PERM_ANDROID_PACKAGE: - case PERM_ANDROID_PACKAGE_CACHE: - uid = multiuser_get_uid(info->userid, uid); - break; - case PERM_ANDROID_OBB: - uid = AID_MEDIA_OBB; - break; - case PERM_PRE_ROOT: - default: - break; + case PERM_ROOT: + case PERM_ANDROID: + case PERM_ANDROID_DATA: + case PERM_ANDROID_MEDIA: + case PERM_ANDROID_PACKAGE: + case PERM_ANDROID_PACKAGE_CACHE: + uid = multiuser_get_uid(info->userid, uid); + break; + case PERM_ANDROID_OBB: + uid = AID_MEDIA_OBB; + break; + case PERM_PRE_ROOT: + default: + break; } switch (perm) { - case PERM_ROOT: - case PERM_ANDROID: - case PERM_ANDROID_DATA: - case PERM_ANDROID_MEDIA: - if (S_ISDIR(d_inode(dentry)->i_mode)) - gid = multiuser_get_uid(info->userid, AID_MEDIA_RW); - else - gid = multiuser_get_uid(info->userid, get_type(name)); - break; - case PERM_ANDROID_OBB: - gid = AID_MEDIA_OBB; - break; - case PERM_ANDROID_PACKAGE: - if (info->d_uid != 0) - gid = multiuser_get_ext_gid(info->userid, info->d_uid); - else - gid = multiuser_get_uid(info->userid, uid); - break; - case PERM_ANDROID_PACKAGE_CACHE: - if (info->d_uid != 0) - gid = multiuser_get_cache_gid(info->userid, info->d_uid); - else - gid = multiuser_get_uid(info->userid, uid); - break; - case PERM_PRE_ROOT: - default: - break; + case PERM_ROOT: + case PERM_ANDROID: + case PERM_ANDROID_DATA: + case PERM_ANDROID_MEDIA: + if (S_ISDIR(d_inode(dentry)->i_mode)) + gid = multiuser_get_uid(info->userid, AID_MEDIA_RW); + else + gid = multiuser_get_uid(info->userid, get_type(name)); + break; + case PERM_ANDROID_OBB: + gid = AID_MEDIA_OBB; + break; + case PERM_ANDROID_PACKAGE: + if (uid_is_app(info->d_uid)) + gid = multiuser_get_ext_gid(info->d_uid); + else + gid = multiuser_get_uid(info->userid, AID_MEDIA_RW); + break; + case PERM_ANDROID_PACKAGE_CACHE: + if (uid_is_app(info->d_uid)) + gid = multiuser_get_ext_cache_gid(info->d_uid); + else + gid = multiuser_get_uid(info->userid, AID_MEDIA_RW); + break; + case PERM_PRE_ROOT: + default: + break; } sdcardfs_get_lower_path(dentry, &path); @@ -241,11 +252,13 @@ retry_deleg: goto retry_deleg; } if (error) - pr_err("sdcardfs: Failed to touch up lower fs gid/uid.\n"); + pr_debug("sdcardfs: Failed to touch up lower fs gid/uid for %s\n", name); } + sdcardfs_put_lower_path(dentry, &path); } -static int descendant_may_need_fixup(struct sdcardfs_inode_info *info, struct limit_search *limit) { +static int descendant_may_need_fixup(struct sdcardfs_inode_info *info, struct limit_search *limit) +{ if (info->perm == PERM_ROOT) return (limit->flags & BY_USERID)?info->userid == limit->userid:1; if (info->perm == PERM_PRE_ROOT || info->perm == PERM_ANDROID) @@ -253,85 +266,56 @@ static int descendant_may_need_fixup(struct sdcardfs_inode_info *info, struct li return 0; } -static int needs_fixup(perm_t perm) { +static int needs_fixup(perm_t perm) +{ if (perm == PERM_ANDROID_DATA || perm == PERM_ANDROID_OBB || perm == PERM_ANDROID_MEDIA) return 1; return 0; } -void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit) { +static void __fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit, int depth) +{ struct dentry *child; struct sdcardfs_inode_info *info; - if (!dget(dentry)) - return; + + /* + * All paths will terminate their recursion on hitting PERM_ANDROID_OBB, + * PERM_ANDROID_MEDIA, or PERM_ANDROID_DATA. This happens at a depth of + * at most 3. + */ + WARN(depth > 3, "%s: Max expected depth exceeded!\n", __func__); + spin_lock_nested(&dentry->d_lock, depth); if (!d_inode(dentry)) { - dput(dentry); + spin_unlock(&dentry->d_lock); return; } info = SDCARDFS_I(d_inode(dentry)); if (needs_fixup(info->perm)) { - spin_lock(&dentry->d_lock); list_for_each_entry(child, &dentry->d_subdirs, d_child) { - dget(child); - if (!(limit->flags & BY_NAME) || !strncasecmp(child->d_name.name, limit->name, limit->length)) { + spin_lock_nested(&child->d_lock, depth + 1); + if (!(limit->flags & BY_NAME) || qstr_case_eq(&child->d_name, &limit->name)) { if (d_inode(child)) { get_derived_permission(dentry, child); fixup_tmp_permissions(d_inode(child)); - dput(child); + spin_unlock(&child->d_lock); break; } } - dput(child); + spin_unlock(&child->d_lock); } - spin_unlock(&dentry->d_lock); - } else if (descendant_may_need_fixup(info, limit)) { - spin_lock(&dentry->d_lock); + } else if (descendant_may_need_fixup(info, limit)) { list_for_each_entry(child, &dentry->d_subdirs, d_child) { - fixup_perms_recursive(child, limit); - } - spin_unlock(&dentry->d_lock); - } - dput(dentry); -} - -void drop_recursive(struct dentry *parent) { - struct dentry *dentry; - struct sdcardfs_inode_info *info; - if (!d_inode(parent)) - return; - info = SDCARDFS_I(d_inode(parent)); - spin_lock(&parent->d_lock); - list_for_each_entry(dentry, &parent->d_subdirs, d_child) { - if (d_inode(dentry)) { - if (SDCARDFS_I(d_inode(parent))->top != SDCARDFS_I(d_inode(dentry))->top) { - drop_recursive(dentry); - d_drop(dentry); - } + __fixup_perms_recursive(child, limit, depth + 1); } } - spin_unlock(&parent->d_lock); + spin_unlock(&dentry->d_lock); } -void fixup_top_recursive(struct dentry *parent) { - struct dentry *dentry; - struct sdcardfs_inode_info *info; - - if (!d_inode(parent)) - return; - info = SDCARDFS_I(d_inode(parent)); - spin_lock(&parent->d_lock); - list_for_each_entry(dentry, &parent->d_subdirs, d_child) { - if (d_inode(dentry)) { - if (SDCARDFS_I(d_inode(parent))->top != SDCARDFS_I(d_inode(dentry))->top) { - get_derived_permission(parent, dentry); - fixup_tmp_permissions(d_inode(dentry)); - fixup_top_recursive(dentry); - } - } - } - spin_unlock(&parent->d_lock); +void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit) +{ + __fixup_perms_recursive(dentry, limit, 0); } /* main function for updating derived permission */ @@ -339,19 +323,17 @@ inline void update_derived_permission_lock(struct dentry *dentry) { struct dentry *parent; - if(!dentry || !d_inode(dentry)) { - printk(KERN_ERR "sdcardfs: %s: invalid dentry\n", __func__); + if (!dentry || !d_inode(dentry)) { + pr_err("sdcardfs: %s: invalid dentry\n", __func__); return; } /* FIXME: * 1. need to check whether the dentry is updated or not * 2. remove the root dentry update */ - if(IS_ROOT(dentry)) { - //setup_default_pre_root_state(d_inode(dentry)); - } else { + if (!IS_ROOT(dentry)) { parent = dget_parent(dentry); - if(parent) { + if (parent) { get_derived_permission(parent, dentry); dput(parent); } @@ -363,15 +345,15 @@ int need_graft_path(struct dentry *dentry) { int ret = 0; struct dentry *parent = dget_parent(dentry); - struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent)); + struct sdcardfs_inode_info *parent_info = SDCARDFS_I(d_inode(parent)); struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); struct qstr obb = QSTR_LITERAL("obb"); - if(parent_info->perm == PERM_ANDROID && + if (parent_info->perm == PERM_ANDROID && qstr_case_eq(&dentry->d_name, &obb)) { /* /Android/obb is the base obbpath of DERIVED_UNIFIED */ - if(!(sbi->options.multiuser == false + if (!(sbi->options.multiuser == false && parent_info->userid == 0)) { ret = 1; } @@ -386,22 +368,24 @@ int is_obbpath_invalid(struct dentry *dent) struct sdcardfs_dentry_info *di = SDCARDFS_D(dent); struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dent->d_sb); char *path_buf, *obbpath_s; + int need_put = 0; + struct path lower_path; /* check the base obbpath has been changed. * this routine can check an uninitialized obb dentry as well. - * regarding the uninitialized obb, refer to the sdcardfs_mkdir() */ + * regarding the uninitialized obb, refer to the sdcardfs_mkdir() + */ spin_lock(&di->lock); - if(di->orig_path.dentry) { - if(!di->lower_path.dentry) { + if (di->orig_path.dentry) { + if (!di->lower_path.dentry) { ret = 1; } else { path_get(&di->lower_path); - //lower_parent = lock_parent(lower_path->dentry); path_buf = kmalloc(PATH_MAX, GFP_ATOMIC); - if(!path_buf) { + if (!path_buf) { ret = 1; - printk(KERN_ERR "sdcardfs: fail to allocate path_buf in %s.\n", __func__); + pr_err("sdcardfs: fail to allocate path_buf in %s.\n", __func__); } else { obbpath_s = d_path(&di->lower_path, path_buf, PATH_MAX); if (d_unhashed(di->lower_path.dentry) || @@ -411,11 +395,13 @@ int is_obbpath_invalid(struct dentry *dent) kfree(path_buf); } - //unlock_dir(lower_parent); - path_put(&di->lower_path); + pathcpy(&lower_path, &di->lower_path); + need_put = 1; } } spin_unlock(&di->lock); + if (need_put) + path_put(&lower_path); return ret; } @@ -423,13 +409,13 @@ int is_base_obbpath(struct dentry *dentry) { int ret = 0; struct dentry *parent = dget_parent(dentry); - struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent)); + struct sdcardfs_inode_info *parent_info = SDCARDFS_I(d_inode(parent)); struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); struct qstr q_obb = QSTR_LITERAL("obb"); spin_lock(&SDCARDFS_D(dentry)->lock); if (sbi->options.multiuser) { - if(parent_info->perm == PERM_PRE_ROOT && + if (parent_info->perm == PERM_PRE_ROOT && qstr_case_eq(&dentry->d_name, &q_obb)) { ret = 1; } @@ -444,7 +430,8 @@ int is_base_obbpath(struct dentry *dentry) /* The lower_path will be stored to the dentry's orig_path * and the base obbpath will be copyed to the lower_path variable. * if an error returned, there's no change in the lower_path - * returns: -ERRNO if error (0: no error) */ + * returns: -ERRNO if error (0: no error) + */ int setup_obb_dentry(struct dentry *dentry, struct path *lower_path) { int err = 0; @@ -453,23 +440,24 @@ int setup_obb_dentry(struct dentry *dentry, struct path *lower_path) /* A local obb dentry must have its own orig_path to support rmdir * and mkdir of itself. Usually, we expect that the sbi->obbpath - * is avaiable on this stage. */ + * is avaiable on this stage. + */ sdcardfs_set_orig_path(dentry, lower_path); err = kern_path(sbi->obbpath_s, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &obbpath); - if(!err) { + if (!err) { /* the obbpath base has been found */ - printk(KERN_INFO "sdcardfs: the sbi->obbpath is found\n"); pathcpy(lower_path, &obbpath); } else { /* if the sbi->obbpath is not available, we can optionally * setup the lower_path with its orig_path. * but, the current implementation just returns an error * because the sdcard daemon also regards this case as - * a lookup fail. */ - printk(KERN_INFO "sdcardfs: the sbi->obbpath is not available\n"); + * a lookup fail. + */ + pr_info("sdcardfs: the sbi->obbpath is not available\n"); } return err; } diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 23f8cd7f8877..6076c342dae6 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -65,7 +65,7 @@ static ssize_t sdcardfs_write(struct file *file, const char __user *buf, /* check disk space */ if (!check_min_free_space(dentry, count, 0)) { - printk(KERN_INFO "No minimum free space.\n"); + pr_err("No minimum free space.\n"); return -ENOSPC; } @@ -113,6 +113,10 @@ static long sdcardfs_unlocked_ioctl(struct file *file, unsigned int cmd, if (lower_file->f_op->unlocked_ioctl) err = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg); + /* some ioctls can change inode attributes (EXT2_IOC_SETFLAGS) */ + if (!err) + sdcardfs_copy_and_fix_attrs(file_inode(file), + file_inode(lower_file)); out: return err; } @@ -160,8 +164,7 @@ static int sdcardfs_mmap(struct file *file, struct vm_area_struct *vma) lower_file = sdcardfs_lower_file(file); if (willwrite && !lower_file->f_mapping->a_ops->writepage) { err = -EINVAL; - printk(KERN_ERR "sdcardfs: lower file system does not " - "support writeable mmap\n"); + pr_err("sdcardfs: lower file system does not support writeable mmap\n"); goto out; } @@ -173,16 +176,10 @@ static int sdcardfs_mmap(struct file *file, struct vm_area_struct *vma) if (!SDCARDFS_F(file)->lower_vm_ops) { err = lower_file->f_op->mmap(lower_file, vma); if (err) { - printk(KERN_ERR "sdcardfs: lower mmap failed %d\n", err); + pr_err("sdcardfs: lower mmap failed %d\n", err); goto out; } saved_vm_ops = vma->vm_ops; /* save: came from lower ->mmap */ - err = do_munmap(current->mm, vma->vm_start, - vma->vm_end - vma->vm_start); - if (err) { - printk(KERN_ERR "sdcardfs: do_munmap failed %d\n", err); - goto out; - } } /* @@ -195,6 +192,9 @@ static int sdcardfs_mmap(struct file *file, struct vm_area_struct *vma) file->f_mapping->a_ops = &sdcardfs_aops; /* set our aops */ if (!SDCARDFS_F(file)->lower_vm_ops) /* save for our ->fault */ SDCARDFS_F(file)->lower_vm_ops = saved_vm_ops; + vma->vm_private_data = file; + get_file(lower_file); + vma->vm_file = lower_file; out: return err; @@ -216,10 +216,7 @@ static int sdcardfs_open(struct inode *inode, struct file *file) goto out_err; } - if(!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); + if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { err = -EACCES; goto out_err; } @@ -251,9 +248,8 @@ static int sdcardfs_open(struct inode *inode, struct file *file) if (err) kfree(SDCARDFS_F(file)); - else { + else sdcardfs_copy_and_fix_attrs(inode, sdcardfs_lower_inode(inode)); - } out_revert_cred: REVERT_CRED(saved_cred); @@ -323,6 +319,85 @@ static int sdcardfs_fasync(int fd, struct file *file, int flag) return err; } +/* + * Sdcardfs cannot use generic_file_llseek as ->llseek, because it would + * only set the offset of the upper file. So we have to implement our + * own method to set both the upper and lower file offsets + * consistently. + */ +static loff_t sdcardfs_file_llseek(struct file *file, loff_t offset, int whence) +{ + int err; + struct file *lower_file; + + err = generic_file_llseek(file, offset, whence); + if (err < 0) + goto out; + + lower_file = sdcardfs_lower_file(file); + err = generic_file_llseek(lower_file, offset, whence); + +out: + return err; +} + +/* + * Sdcardfs read_iter, redirect modified iocb to lower read_iter + */ +ssize_t sdcardfs_read_iter(struct kiocb *iocb, struct iov_iter *iter) +{ + int err; + struct file *file = iocb->ki_filp, *lower_file; + + lower_file = sdcardfs_lower_file(file); + if (!lower_file->f_op->read_iter) { + err = -EINVAL; + goto out; + } + + get_file(lower_file); /* prevent lower_file from being released */ + iocb->ki_filp = lower_file; + err = lower_file->f_op->read_iter(iocb, iter); + iocb->ki_filp = file; + fput(lower_file); + /* update upper inode atime as needed */ + if (err >= 0 || err == -EIOCBQUEUED) + fsstack_copy_attr_atime(file->f_path.dentry->d_inode, + file_inode(lower_file)); +out: + return err; +} + +/* + * Sdcardfs write_iter, redirect modified iocb to lower write_iter + */ +ssize_t sdcardfs_write_iter(struct kiocb *iocb, struct iov_iter *iter) +{ + int err; + struct file *file = iocb->ki_filp, *lower_file; + + lower_file = sdcardfs_lower_file(file); + if (!lower_file->f_op->write_iter) { + err = -EINVAL; + goto out; + } + + get_file(lower_file); /* prevent lower_file from being released */ + iocb->ki_filp = lower_file; + err = lower_file->f_op->write_iter(iocb, iter); + iocb->ki_filp = file; + fput(lower_file); + /* update upper inode times/sizes as needed */ + if (err >= 0 || err == -EIOCBQUEUED) { + fsstack_copy_inode_size(file->f_path.dentry->d_inode, + file_inode(lower_file)); + fsstack_copy_attr_times(file->f_path.dentry->d_inode, + file_inode(lower_file)); + } +out: + return err; +} + const struct file_operations sdcardfs_main_fops = { .llseek = generic_file_llseek, .read = sdcardfs_read, @@ -337,11 +412,13 @@ const struct file_operations sdcardfs_main_fops = { .release = sdcardfs_file_release, .fsync = sdcardfs_fsync, .fasync = sdcardfs_fasync, + .read_iter = sdcardfs_read_iter, + .write_iter = sdcardfs_write_iter, }; /* trimmed directory options */ const struct file_operations sdcardfs_dir_fops = { - .llseek = generic_file_llseek, + .llseek = sdcardfs_file_llseek, .read = generic_read_dir, .iterate = sdcardfs_readdir, .unlocked_ioctl = sdcardfs_unlocked_ioctl, diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 68e615045616..f15cb11ca8fd 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -20,12 +20,13 @@ #include "sdcardfs.h" #include <linux/fs_struct.h> +#include <linux/ratelimit.h> /* Do not directly use this function. Use OVERRIDE_CRED() instead. */ -const struct cred * override_fsids(struct sdcardfs_sb_info* sbi, struct sdcardfs_inode_info *info) +const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_inode_info *info) { - struct cred * cred; - const struct cred * old_cred; + struct cred *cred; + const struct cred *old_cred; uid_t uid; cred = prepare_creds(); @@ -45,9 +46,9 @@ const struct cred * override_fsids(struct sdcardfs_sb_info* sbi, struct sdcardfs } /* Do not directly use this function, use REVERT_CRED() instead. */ -void revert_fsids(const struct cred * old_cred) +void revert_fsids(const struct cred *old_cred) { - const struct cred * cur_cred; + const struct cred *cur_cred; cur_cred = current->cred; revert_creds(old_cred); @@ -66,10 +67,7 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, struct fs_struct *saved_fs; struct fs_struct *copied_fs; - if(!check_caller_access_to_name(dir, &dentry->d_name)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); + if (!check_caller_access_to_name(dir, &dentry->d_name)) { err = -EACCES; goto out_eacces; } @@ -168,10 +166,7 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) struct path lower_path; const struct cred *saved_cred = NULL; - if(!check_caller_access_to_name(dir, &dentry->d_name)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); + if (!check_caller_access_to_name(dir, &dentry->d_name)) { err = -EACCES; goto out_eacces; } @@ -245,14 +240,15 @@ out: } #endif -static int touch(char *abs_path, mode_t mode) { +static int touch(char *abs_path, mode_t mode) +{ struct file *filp = filp_open(abs_path, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW, mode); + if (IS_ERR(filp)) { if (PTR_ERR(filp) == -EEXIST) { return 0; - } - else { - printk(KERN_ERR "sdcardfs: failed to open(%s): %ld\n", + } else { + pr_err("sdcardfs: failed to open(%s): %ld\n", abs_path, PTR_ERR(filp)); return PTR_ERR(filp); } @@ -278,10 +274,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode struct qstr q_obb = QSTR_LITERAL("obb"); struct qstr q_data = QSTR_LITERAL("data"); - if(!check_caller_access_to_name(dir, &dentry->d_name)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); + if (!check_caller_access_to_name(dir, &dentry->d_name)) { err = -EACCES; goto out_eacces; } @@ -291,7 +284,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode /* check disk space */ if (!check_min_free_space(dentry, 0, 1)) { - printk(KERN_INFO "sdcardfs: No minimum free space.\n"); + pr_err("sdcardfs: No minimum free space.\n"); err = -ENOSPC; goto out_revert; } @@ -323,19 +316,21 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode } /* if it is a local obb dentry, setup it with the base obbpath */ - if(need_graft_path(dentry)) { + if (need_graft_path(dentry)) { err = setup_obb_dentry(dentry, &lower_path); - if(err) { + if (err) { /* if the sbi->obbpath is not available, the lower_path won't be * changed by setup_obb_dentry() but the lower path is saved to * its orig_path. this dentry will be revalidated later. - * but now, the lower_path should be NULL */ + * but now, the lower_path should be NULL + */ sdcardfs_put_reset_lower_path(dentry); /* the newly created lower path which saved to its orig_path or * the lower_path is the base obbpath. - * therefore, an additional path_get is required */ + * therefore, an additional path_get is required + */ path_get(&lower_path); } else make_nomedia_in_obb = 1; @@ -365,7 +360,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode set_fs_pwd(current->fs, &lower_path); touch_err = touch(".nomedia", 0664); if (touch_err) { - printk(KERN_ERR "sdcardfs: failed to create .nomedia in %s: %d\n", + pr_err("sdcardfs: failed to create .nomedia in %s: %d\n", lower_path.dentry->d_name.name, touch_err); goto out; } @@ -390,10 +385,7 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) struct path lower_path; const struct cred *saved_cred = NULL; - if(!check_caller_access_to_name(dir, &dentry->d_name)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); + if (!check_caller_access_to_name(dir, &dentry->d_name)) { err = -EACCES; goto out_eacces; } @@ -402,7 +394,8 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); /* sdcardfs_get_real_lower(): in case of remove an user's obb dentry - * the dentry on the original path should be deleted. */ + * the dentry on the original path should be deleted. + */ sdcardfs_get_real_lower(dentry, &lower_path); lower_dentry = lower_path.dentry; @@ -478,11 +471,8 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct path lower_old_path, lower_new_path; const struct cred *saved_cred = NULL; - if(!check_caller_access_to_name(old_dir, &old_dentry->d_name) || + if (!check_caller_access_to_name(old_dir, &old_dentry->d_name) || !check_caller_access_to_name(new_dir, &new_dentry->d_name)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " new_dentry: %s, task:%s\n", - __func__, new_dentry->d_name.name, current->comm); err = -EACCES; goto out_eacces; } @@ -528,7 +518,7 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, get_derived_permission_new(new_dentry->d_parent, old_dentry, &new_dentry->d_name); fixup_tmp_permissions(d_inode(old_dentry)); fixup_lower_ownership(old_dentry, new_dentry->d_name.name); - drop_recursive(old_dentry); /* Can't fixup ownership recursively :( */ + d_invalidate(old_dentry); /* Can't fixup ownership recursively :( */ out: unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); dput(lower_old_dir_dentry); @@ -599,7 +589,7 @@ static const char *sdcardfs_follow_link(struct dentry *dentry, void **cookie) static int sdcardfs_permission_wrn(struct inode *inode, int mask) { - WARN(1, "sdcardfs does not support permission. Use permission2.\n"); + WARN_RATELIMIT(1, "sdcardfs does not support permission. Use permission2.\n"); return -EINVAL; } @@ -652,7 +642,7 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma release_top(SDCARDFS_I(inode)); tmp.i_sb = inode->i_sb; if (IS_POSIXACL(inode)) - printk(KERN_WARNING "%s: This may be undefined behavior... \n", __func__); + pr_warn("%s: This may be undefined behavior...\n", __func__); err = generic_permission(&tmp, mask); /* XXX * Original sdcardfs code calls inode_permission(lower_inode,.. ) @@ -670,6 +660,7 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma * we check it with AID_MEDIA_RW permission */ struct inode *lower_inode; + OVERRIDE_CRED(SDCARDFS_SB(inode->sb)); lower_inode = sdcardfs_lower_inode(inode); @@ -684,7 +675,7 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma static int sdcardfs_setattr_wrn(struct dentry *dentry, struct iattr *ia) { - WARN(1, "sdcardfs does not support setattr. User setattr2.\n"); + WARN_RATELIMIT(1, "sdcardfs does not support setattr. User setattr2.\n"); return -EINVAL; } @@ -738,19 +729,16 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct /* prepare our own lower struct iattr (with the lower file) */ memcpy(&lower_ia, ia, sizeof(lower_ia)); /* Allow touch updating timestamps. A previous permission check ensures - * we have write access. Changes to mode, owner, and group are ignored*/ + * we have write access. Changes to mode, owner, and group are ignored + */ ia->ia_valid |= ATTR_FORCE; err = inode_change_ok(&tmp, ia); if (!err) { /* check the Android group ID */ parent = dget_parent(dentry); - if(!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); + if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) err = -EACCES; - } dput(parent); } @@ -828,10 +816,12 @@ out_err: return err; } -static int sdcardfs_fillattr(struct vfsmount *mnt, struct inode *inode, struct kstat *stat) +static int sdcardfs_fillattr(struct vfsmount *mnt, + struct inode *inode, struct kstat *stat) { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); struct inode *top = grab_top(info); + if (!top) return -EINVAL; @@ -855,33 +845,27 @@ static int sdcardfs_fillattr(struct vfsmount *mnt, struct inode *inode, struct k static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { - struct dentry *lower_dentry; - struct inode *inode; - struct inode *lower_inode; + struct kstat lower_stat; struct path lower_path; struct dentry *parent; int err; parent = dget_parent(dentry); - if(!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); + if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { dput(parent); return -EACCES; } dput(parent); - inode = d_inode(dentry); - sdcardfs_get_lower_path(dentry, &lower_path); - lower_dentry = lower_path.dentry; - lower_inode = sdcardfs_lower_inode(inode); - - sdcardfs_copy_and_fix_attrs(inode, lower_inode); - fsstack_copy_inode_size(inode, lower_inode); - - err = sdcardfs_fillattr(mnt, inode, stat); + err = vfs_getattr(&lower_path, &lower_stat); + if (err) + goto out; + sdcardfs_copy_and_fix_attrs(d_inode(dentry), + d_inode(lower_path.dentry)); + err = sdcardfs_fillattr(mnt, d_inode(dentry), stat); + stat->blocks = lower_stat.blocks; +out: sdcardfs_put_lower_path(dentry, &lower_path); return err; } diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index 9135866b7766..509d5fbcb472 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -36,8 +36,7 @@ int sdcardfs_init_dentry_cache(void) void sdcardfs_destroy_dentry_cache(void) { - if (sdcardfs_dentry_cachep) - kmem_cache_destroy(sdcardfs_dentry_cachep); + kmem_cache_destroy(sdcardfs_dentry_cachep); } void free_dentry_private_data(struct dentry *dentry) @@ -73,6 +72,7 @@ static int sdcardfs_inode_test(struct inode *inode, void *candidate_data/*void * { struct inode *current_lower_inode = sdcardfs_lower_inode(inode); userid_t current_userid = SDCARDFS_I(inode)->userid; + if (current_lower_inode == ((struct inode_data *)candidate_data)->lower_inode && current_userid == ((struct inode_data *)candidate_data)->id) return 1; /* found a match */ @@ -91,7 +91,9 @@ struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode, u struct sdcardfs_inode_info *info; struct inode_data data; struct inode *inode; /* the new inode to return */ - int err; + + if (!igrab(lower_inode)) + return ERR_PTR(-ESTALE); data.id = id; data.lower_inode = lower_inode; @@ -102,26 +104,23 @@ struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode, u * instead. */ lower_inode->i_ino, /* hashval */ - sdcardfs_inode_test, /* inode comparison function */ + sdcardfs_inode_test, /* inode comparison function */ sdcardfs_inode_set, /* inode init function */ &data); /* data passed to test+set fxns */ if (!inode) { - err = -EACCES; iput(lower_inode); - return ERR_PTR(err); + return ERR_PTR(-ENOMEM); } - /* if found a cached inode, then just return it */ - if (!(inode->i_state & I_NEW)) + /* if found a cached inode, then just return it (after iput) */ + if (!(inode->i_state & I_NEW)) { + iput(lower_inode); return inode; + } /* initialize new inode */ info = SDCARDFS_I(inode); inode->i_ino = lower_inode->i_ino; - if (!igrab(lower_inode)) { - err = -ESTALE; - return ERR_PTR(err); - } sdcardfs_set_lower_inode(inode, lower_inode); inode->i_version++; @@ -164,27 +163,25 @@ struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode, u } /* - * Connect a sdcardfs inode dentry/inode with several lower ones. This is - * the classic stackable file system "vnode interposition" action. - * - * @dentry: sdcardfs's dentry which interposes on lower one - * @sb: sdcardfs's super_block - * @lower_path: the lower path (caller does path_get/put) + * Helper interpose routine, called directly by ->lookup to handle + * spliced dentries. */ -int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, - struct path *lower_path, userid_t id) +static struct dentry *__sdcardfs_interpose(struct dentry *dentry, + struct super_block *sb, + struct path *lower_path, + userid_t id) { - int err = 0; struct inode *inode; struct inode *lower_inode; struct super_block *lower_sb; + struct dentry *ret_dentry; lower_inode = d_inode(lower_path->dentry); lower_sb = sdcardfs_lower_super(sb); /* check that the lower file system didn't cross a mount point */ if (lower_inode->i_sb != lower_sb) { - err = -EXDEV; + ret_dentry = ERR_PTR(-EXDEV); goto out; } @@ -196,14 +193,54 @@ int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, /* inherit lower inode number for sdcardfs's inode */ inode = sdcardfs_iget(sb, lower_inode, id); if (IS_ERR(inode)) { - err = PTR_ERR(inode); + ret_dentry = ERR_CAST(inode); goto out; } - d_add(dentry, inode); + ret_dentry = d_splice_alias(inode, dentry); + dentry = ret_dentry ?: dentry; update_derived_permission_lock(dentry); out: - return err; + return ret_dentry; +} + +/* + * Connect an sdcardfs inode dentry/inode with several lower ones. This is + * the classic stackable file system "vnode interposition" action. + * + * @dentry: sdcardfs's dentry which interposes on lower one + * @sb: sdcardfs's super_block + * @lower_path: the lower path (caller does path_get/put) + */ +int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, + struct path *lower_path, userid_t id) +{ + struct dentry *ret_dentry; + + ret_dentry = __sdcardfs_interpose(dentry, sb, lower_path, id); + return PTR_ERR(ret_dentry); +} + +struct sdcardfs_name_data { + struct dir_context ctx; + const struct qstr *to_find; + char *name; + bool found; +}; + +static int sdcardfs_name_match(struct dir_context *ctx, const char *name, + int namelen, loff_t offset, u64 ino, unsigned int d_type) +{ + struct sdcardfs_name_data *buf = container_of(ctx, struct sdcardfs_name_data, ctx); + struct qstr candidate = QSTR_INIT(name, namelen); + + if (qstr_case_eq(buf->to_find, &candidate)) { + memcpy(buf->name, name, namelen); + buf->name[namelen] = 0; + buf->found = true; + return 1; + } + return 0; } /* @@ -221,6 +258,8 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, struct dentry *lower_dentry; const struct qstr *name; struct path lower_path; + struct qstr dname; + struct dentry *ret_dentry = NULL; struct sdcardfs_sb_info *sbi; sbi = SDCARDFS_SB(dentry->d_sb); @@ -241,60 +280,79 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, &lower_path); /* check for other cases */ if (err == -ENOENT) { - struct dentry *child; - struct dentry *match = NULL; - mutex_lock(&d_inode(lower_dir_dentry)->i_mutex); - spin_lock(&lower_dir_dentry->d_lock); - list_for_each_entry(child, &lower_dir_dentry->d_subdirs, d_child) { - if (child && d_inode(child)) { - if (qstr_case_eq(&child->d_name, name)) { - match = dget(child); - break; - } - } + struct file *file; + const struct cred *cred = current_cred(); + + struct sdcardfs_name_data buffer = { + .ctx.actor = sdcardfs_name_match, + .to_find = name, + .name = __getname(), + .found = false, + }; + + if (!buffer.name) { + err = -ENOMEM; + goto out; } - spin_unlock(&lower_dir_dentry->d_lock); - mutex_unlock(&d_inode(lower_dir_dentry)->i_mutex); - if (match) { + file = dentry_open(lower_parent_path, O_RDONLY, cred); + if (IS_ERR(file)) { + err = PTR_ERR(file); + goto put_name; + } + err = iterate_dir(file, &buffer.ctx); + fput(file); + if (err) + goto put_name; + + if (buffer.found) err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, - match->d_name.name, 0, + buffer.name, 0, &lower_path); - dput(match); - } + else + err = -ENOENT; +put_name: + __putname(buffer.name); } /* no error: handle positive dentries */ if (!err) { /* check if the dentry is an obb dentry * if true, the lower_inode must be replaced with - * the inode of the graft path */ + * the inode of the graft path + */ - if(need_graft_path(dentry)) { + if (need_graft_path(dentry)) { /* setup_obb_dentry() - * The lower_path will be stored to the dentry's orig_path + * The lower_path will be stored to the dentry's orig_path * and the base obbpath will be copyed to the lower_path variable. * if an error returned, there's no change in the lower_path - * returns: -ERRNO if error (0: no error) */ + * returns: -ERRNO if error (0: no error) + */ err = setup_obb_dentry(dentry, &lower_path); - if(err) { + if (err) { /* if the sbi->obbpath is not available, we can optionally * setup the lower_path with its orig_path. * but, the current implementation just returns an error * because the sdcard daemon also regards this case as - * a lookup fail. */ - printk(KERN_INFO "sdcardfs: base obbpath is not available\n"); + * a lookup fail. + */ + pr_info("sdcardfs: base obbpath is not available\n"); sdcardfs_put_reset_orig_path(dentry); goto out; } } sdcardfs_set_lower_path(dentry, &lower_path); - err = sdcardfs_interpose(dentry, dentry->d_sb, &lower_path, id); - if (err) /* path_put underlying path on error */ + ret_dentry = + __sdcardfs_interpose(dentry, dentry->d_sb, &lower_path, id); + if (IS_ERR(ret_dentry)) { + err = PTR_ERR(ret_dentry); + /* path_put underlying path on error */ sdcardfs_put_reset_lower_path(dentry); + } goto out; } @@ -306,18 +364,24 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, goto out; /* instatiate a new negative dentry */ - lower_dentry = d_lookup(lower_dir_dentry, name); - if (lower_dentry) - goto setup_lower; + dname.name = name->name; + dname.len = name->len; - lower_dentry = d_alloc(lower_dir_dentry, name); + /* See if the low-level filesystem might want + * to use its own hash + */ + lower_dentry = d_hash_and_lookup(lower_dir_dentry, &dname); + if (IS_ERR(lower_dentry)) + return lower_dentry; if (!lower_dentry) { - err = -ENOMEM; + /* We called vfs_path_lookup earlier, and did not get a negative + * dentry then. Don't confuse the lower filesystem by forcing + * one on it now... + */ + err = -ENOENT; goto out; } - d_add(lower_dentry, NULL); /* instantiate and hash */ -setup_lower: lower_path.dentry = lower_dentry; lower_path.mnt = mntget(lower_dir_mnt); sdcardfs_set_lower_path(dentry, &lower_path); @@ -331,14 +395,16 @@ setup_lower: err = 0; out: - return ERR_PTR(err); + if (err) + return ERR_PTR(err); + return ret_dentry; } /* * On success: - * fills dentry object appropriate values and returns NULL. + * fills dentry object appropriate values and returns NULL. * On fail (== error) - * returns error ptr + * returns error ptr * * @dir : Parent inode. It is locked (dir->i_mutex) * @dentry : Target dentry to lookup. we should set each of fields. @@ -355,13 +421,10 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, parent = dget_parent(dentry); - if(!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { + if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { ret = ERR_PTR(-EACCES); - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); goto out_err; - } + } /* save current_cred and override it */ OVERRIDE_CRED_PTR(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); @@ -377,9 +440,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, ret = __sdcardfs_lookup(dentry, flags, &lower_parent_path, SDCARDFS_I(dir)->userid); if (IS_ERR(ret)) - { goto out; - } if (ret) dentry = ret; if (d_inode(dentry)) { diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 7a8eae29e44d..953d2156d2e9 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -29,7 +29,7 @@ enum { Opt_gid, Opt_debug, Opt_mask, - Opt_multiuser, // May need? + Opt_multiuser, Opt_userid, Opt_reserved_mb, Opt_err, @@ -72,6 +72,7 @@ static int parse_options(struct super_block *sb, char *options, int silent, while ((p = strsep(&options, ",")) != NULL) { int token; + if (!*p) continue; @@ -116,19 +117,17 @@ static int parse_options(struct super_block *sb, char *options, int silent, break; /* unknown option */ default: - if (!silent) { - printk( KERN_ERR "Unrecognized mount option \"%s\" " - "or missing value", p); - } + if (!silent) + pr_err("Unrecognized mount option \"%s\" or missing value", p); return -EINVAL; } } if (*debug) { - printk( KERN_INFO "sdcardfs : options - debug:%d\n", *debug); - printk( KERN_INFO "sdcardfs : options - uid:%d\n", + pr_info("sdcardfs : options - debug:%d\n", *debug); + pr_info("sdcardfs : options - uid:%d\n", opts->fs_low_uid); - printk( KERN_INFO "sdcardfs : options - gid:%d\n", + pr_info("sdcardfs : options - gid:%d\n", opts->fs_low_gid); } @@ -148,6 +147,7 @@ int parse_options_remount(struct super_block *sb, char *options, int silent, while ((p = strsep(&options, ",")) != NULL) { int token; + if (!*p) continue; @@ -173,22 +173,20 @@ int parse_options_remount(struct super_block *sb, char *options, int silent, case Opt_fsuid: case Opt_fsgid: case Opt_reserved_mb: - printk( KERN_WARNING "Option \"%s\" can't be changed during remount\n", p); + pr_warn("Option \"%s\" can't be changed during remount\n", p); break; /* unknown option */ default: - if (!silent) { - printk( KERN_ERR "Unrecognized mount option \"%s\" " - "or missing value", p); - } + if (!silent) + pr_err("Unrecognized mount option \"%s\" or missing value", p); return -EINVAL; } } if (debug) { - printk( KERN_INFO "sdcardfs : options - debug:%d\n", debug); - printk( KERN_INFO "sdcardfs : options - gid:%d\n", vfsopts->gid); - printk( KERN_INFO "sdcardfs : options - mask:%d\n", vfsopts->mask); + pr_info("sdcardfs : options - debug:%d\n", debug); + pr_info("sdcardfs : options - gid:%d\n", vfsopts->gid); + pr_info("sdcardfs : options - mask:%d\n", vfsopts->mask); } return 0; @@ -223,8 +221,8 @@ static struct dentry *sdcardfs_d_alloc_root(struct super_block *sb) #endif DEFINE_MUTEX(sdcardfs_super_list_lock); -LIST_HEAD(sdcardfs_super_list); EXPORT_SYMBOL_GPL(sdcardfs_super_list_lock); +LIST_HEAD(sdcardfs_super_list); EXPORT_SYMBOL_GPL(sdcardfs_super_list); /* @@ -242,31 +240,30 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, struct sdcardfs_vfsmount_options *mnt_opt = mnt->data; struct inode *inode; - printk(KERN_INFO "sdcardfs version 2.0\n"); + pr_info("sdcardfs version 2.0\n"); if (!dev_name) { - printk(KERN_ERR - "sdcardfs: read_super: missing dev_name argument\n"); + pr_err("sdcardfs: read_super: missing dev_name argument\n"); err = -EINVAL; goto out; } - printk(KERN_INFO "sdcardfs: dev_name -> %s\n", dev_name); - printk(KERN_INFO "sdcardfs: options -> %s\n", (char *)raw_data); - printk(KERN_INFO "sdcardfs: mnt -> %p\n", mnt); + pr_info("sdcardfs: dev_name -> %s\n", dev_name); + pr_info("sdcardfs: options -> %s\n", (char *)raw_data); + pr_info("sdcardfs: mnt -> %p\n", mnt); /* parse lower path */ err = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &lower_path); if (err) { - printk(KERN_ERR "sdcardfs: error accessing lower directory '%s'\n", dev_name); + pr_err("sdcardfs: error accessing lower directory '%s'\n", dev_name); goto out; } /* allocate superblock private data */ sb->s_fs_info = kzalloc(sizeof(struct sdcardfs_sb_info), GFP_KERNEL); if (!SDCARDFS_SB(sb)) { - printk(KERN_CRIT "sdcardfs: read_super: out of memory\n"); + pr_crit("sdcardfs: read_super: out of memory\n"); err = -ENOMEM; goto out_free; } @@ -275,7 +272,7 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, /* parse options */ err = parse_options(sb, raw_data, silent, &debug, mnt_opt, &sb_info->options); if (err) { - printk(KERN_ERR "sdcardfs: invalid options\n"); + pr_err("sdcardfs: invalid options\n"); goto out_freesbi; } @@ -328,14 +325,15 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, /* setup permission policy */ sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL); mutex_lock(&sdcardfs_super_list_lock); - if(sb_info->options.multiuser) { - setup_derived_state(d_inode(sb->s_root), PERM_PRE_ROOT, sb_info->options.fs_user_id, AID_ROOT, false, d_inode(sb->s_root)); + if (sb_info->options.multiuser) { + setup_derived_state(d_inode(sb->s_root), PERM_PRE_ROOT, + sb_info->options.fs_user_id, AID_ROOT, + false, d_inode(sb->s_root)); snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name); - /*err = prepare_dir(sb_info->obbpath_s, - sb_info->options.fs_low_uid, - sb_info->options.fs_low_gid, 00755);*/ } else { - setup_derived_state(d_inode(sb->s_root), PERM_ROOT, sb_info->options.fs_user_id, AID_ROOT, false, d_inode(sb->s_root)); + setup_derived_state(d_inode(sb->s_root), PERM_ROOT, + sb_info->options.fs_user_id, AID_ROOT, + false, d_inode(sb->s_root)); snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name); } fixup_tmp_permissions(d_inode(sb->s_root)); @@ -344,7 +342,7 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, mutex_unlock(&sdcardfs_super_list_lock); if (!silent) - printk(KERN_INFO "sdcardfs: mounted on top of %s type %s\n", + pr_info("sdcardfs: mounted on top of %s type %s\n", dev_name, lower_sb->s_type->name); goto out; /* all is well */ @@ -368,8 +366,10 @@ out: /* A feature which supports mount_nodev() with options */ static struct dentry *mount_nodev_with_options(struct vfsmount *mnt, - struct file_system_type *fs_type, int flags, const char *dev_name, void *data, - int (*fill_super)(struct vfsmount *, struct super_block *, const char *, void *, int)) + struct file_system_type *fs_type, int flags, + const char *dev_name, void *data, + int (*fill_super)(struct vfsmount *, struct super_block *, + const char *, void *, int)) { int error; @@ -401,19 +401,22 @@ static struct dentry *sdcardfs_mount(struct vfsmount *mnt, raw_data, sdcardfs_read_super); } -static struct dentry *sdcardfs_mount_wrn(struct file_system_type *fs_type, int flags, - const char *dev_name, void *raw_data) +static struct dentry *sdcardfs_mount_wrn(struct file_system_type *fs_type, + int flags, const char *dev_name, void *raw_data) { WARN(1, "sdcardfs does not support mount. Use mount2.\n"); return ERR_PTR(-EINVAL); } -void *sdcardfs_alloc_mnt_data(void) { +void *sdcardfs_alloc_mnt_data(void) +{ return kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL); } -void sdcardfs_kill_sb(struct super_block *sb) { +void sdcardfs_kill_sb(struct super_block *sb) +{ struct sdcardfs_sb_info *sbi; + if (sb->s_magic == SDCARDFS_SUPER_MAGIC) { sbi = SDCARDFS_SB(sb); mutex_lock(&sdcardfs_super_list_lock); @@ -432,6 +435,7 @@ static struct file_system_type sdcardfs_fs_type = { .kill_sb = sdcardfs_kill_sb, .fs_flags = 0, }; +MODULE_ALIAS_FS(SDCARDFS_NAME); static int __init init_sdcardfs_fs(void) { @@ -467,10 +471,15 @@ static void __exit exit_sdcardfs_fs(void) pr_info("Completed sdcardfs module unload\n"); } -MODULE_AUTHOR("Erez Zadok, Filesystems and Storage Lab, Stony Brook University" - " (http://www.fsl.cs.sunysb.edu/)"); -MODULE_DESCRIPTION("Wrapfs " SDCARDFS_VERSION - " (http://wrapfs.filesystems.org/)"); +/* Original wrapfs authors */ +MODULE_AUTHOR("Erez Zadok, Filesystems and Storage Lab, Stony Brook University (http://www.fsl.cs.sunysb.edu/)"); + +/* Original sdcardfs authors */ +MODULE_AUTHOR("Woojoong Lee, Daeho Jeong, Kitae Lee, Yeongjin Gil System Memory Lab., Samsung Electronics"); + +/* Current maintainer */ +MODULE_AUTHOR("Daniel Rosenberg, Google"); +MODULE_DESCRIPTION("Sdcardfs " SDCARDFS_VERSION); MODULE_LICENSE("GPL"); module_init(init_sdcardfs_fs); diff --git a/fs/sdcardfs/mmap.c b/fs/sdcardfs/mmap.c index e21f64675a80..b61f82275e7d 100644 --- a/fs/sdcardfs/mmap.c +++ b/fs/sdcardfs/mmap.c @@ -23,28 +23,46 @@ static int sdcardfs_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { int err; - struct file *file, *lower_file; + struct file *file; const struct vm_operations_struct *lower_vm_ops; - struct vm_area_struct lower_vma; - memcpy(&lower_vma, vma, sizeof(struct vm_area_struct)); - file = lower_vma.vm_file; + file = (struct file *)vma->vm_private_data; lower_vm_ops = SDCARDFS_F(file)->lower_vm_ops; BUG_ON(!lower_vm_ops); - lower_file = sdcardfs_lower_file(file); - /* - * XXX: vm_ops->fault may be called in parallel. Because we have to - * resort to temporarily changing the vma->vm_file to point to the - * lower file, a concurrent invocation of sdcardfs_fault could see a - * different value. In this workaround, we keep a different copy of - * the vma structure in our stack, so we never expose a different - * value of the vma->vm_file called to us, even temporarily. A - * better fix would be to change the calling semantics of ->fault to - * take an explicit file pointer. - */ - lower_vma.vm_file = lower_file; - err = lower_vm_ops->fault(&lower_vma, vmf); + err = lower_vm_ops->fault(vma, vmf); + return err; +} + +static void sdcardfs_vm_open(struct vm_area_struct *vma) +{ + struct file *file = (struct file *)vma->vm_private_data; + + get_file(file); +} + +static void sdcardfs_vm_close(struct vm_area_struct *vma) +{ + struct file *file = (struct file *)vma->vm_private_data; + + fput(file); +} + +static int sdcardfs_page_mkwrite(struct vm_area_struct *vma, + struct vm_fault *vmf) +{ + int err = 0; + struct file *file; + const struct vm_operations_struct *lower_vm_ops; + + file = (struct file *)vma->vm_private_data; + lower_vm_ops = SDCARDFS_F(file)->lower_vm_ops; + BUG_ON(!lower_vm_ops); + if (!lower_vm_ops->page_mkwrite) + goto out; + + err = lower_vm_ops->page_mkwrite(vma, vmf); +out: return err; } @@ -52,30 +70,20 @@ static ssize_t sdcardfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter, loff_t pos) { /* - * This function returns zero on purpose in order to support direct IO. - * __dentry_open checks a_ops->direct_IO and returns EINVAL if it is null. - * - * However, this function won't be called by certain file operations - * including generic fs functions. * reads and writes are delivered to - * the lower file systems and the direct IOs will be handled by them. - * - * NOTE: exceptionally, on the recent kernels (since Linux 3.8.x), - * swap_writepage invokes this function directly. + * This function should never be called directly. We need it + * to exist, to get past a check in open_check_o_direct(), + * which is called from do_last(). */ - printk(KERN_INFO "%s, operation is not supported\n", __func__); - return 0; + return -EINVAL; } -/* - * XXX: the default address_space_ops for sdcardfs is empty. We cannot set - * our inode->i_mapping->a_ops to NULL because too many code paths expect - * the a_ops vector to be non-NULL. - */ const struct address_space_operations sdcardfs_aops = { - /* empty on purpose */ .direct_IO = sdcardfs_direct_IO, }; const struct vm_operations_struct sdcardfs_vm_ops = { .fault = sdcardfs_fault, + .page_mkwrite = sdcardfs_page_mkwrite, + .open = sdcardfs_vm_open, + .close = sdcardfs_vm_close, }; diff --git a/fs/sdcardfs/multiuser.h b/fs/sdcardfs/multiuser.h index 52bc20080904..85341e753f8c 100644 --- a/fs/sdcardfs/multiuser.h +++ b/fs/sdcardfs/multiuser.h @@ -23,27 +23,31 @@ #define AID_APP_END 19999 /* last app user */ #define AID_CACHE_GID_START 20000 /* start of gids for apps to mark cached data */ #define AID_EXT_GID_START 30000 /* start of gids for apps to mark external data */ +#define AID_EXT_CACHE_GID_START 40000 /* start of gids for apps to mark external cached data */ +#define AID_EXT_CACHE_GID_END 49999 /* end of gids for apps to mark external cached data */ #define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */ typedef uid_t userid_t; typedef uid_t appid_t; -static inline uid_t multiuser_get_uid(userid_t user_id, appid_t app_id) { - return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET); +static inline uid_t multiuser_get_uid(userid_t user_id, appid_t app_id) +{ + return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET); } -static inline gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id) { - if (app_id >= AID_APP_START && app_id <= AID_APP_END) { - return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_CACHE_GID_START); - } else { - return -1; - } +static inline bool uid_is_app(uid_t uid) +{ + appid_t appid = uid % AID_USER_OFFSET; + + return appid >= AID_APP_START && appid <= AID_APP_END; +} + +static inline gid_t multiuser_get_ext_cache_gid(uid_t uid) +{ + return uid - AID_APP_START + AID_EXT_CACHE_GID_START; } -static inline gid_t multiuser_get_ext_gid(userid_t user_id, appid_t app_id) { - if (app_id >= AID_APP_START && app_id <= AID_APP_END) { - return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_EXT_GID_START); - } else { - return -1; - } +static inline gid_t multiuser_get_ext_gid(uid_t uid) +{ + return uid - AID_APP_START + AID_EXT_GID_START; } diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index d96fcde041cc..89196e31073e 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -20,6 +20,7 @@ #include "sdcardfs.h" #include <linux/hashtable.h> +#include <linux/ctype.h> #include <linux/delay.h> #include <linux/radix-tree.h> #include <linux/dcache.h> @@ -44,13 +45,24 @@ static DEFINE_HASHTABLE(ext_to_groupid, 8); static struct kmem_cache *hashtable_entry_cachep; -static void inline qstr_init(struct qstr *q, const char *name) { +static unsigned int full_name_case_hash(const unsigned char *name, unsigned int len) +{ + unsigned long hash = init_name_hash(); + + while (len--) + hash = partial_name_hash(tolower(*name++), hash); + return end_name_hash(hash); +} + +static inline void qstr_init(struct qstr *q, const char *name) +{ q->name = name; q->len = strlen(q->name); - q->hash = full_name_hash(q->name, q->len); + q->hash = full_name_case_hash(q->name, q->len); } -static inline int qstr_copy(const struct qstr *src, struct qstr *dest) { +static inline int qstr_copy(const struct qstr *src, struct qstr *dest) +{ dest->name = kstrdup(src->name, GFP_KERNEL); dest->hash_len = src->hash_len; return !!dest->name; @@ -78,6 +90,7 @@ static appid_t __get_appid(const struct qstr *key) appid_t get_appid(const char *key) { struct qstr q; + qstr_init(&q, key); return __get_appid(&q); } @@ -103,6 +116,7 @@ static appid_t __get_ext_gid(const struct qstr *key) appid_t get_ext_gid(const char *key) { struct qstr q; + qstr_init(&q, key); return __get_ext_gid(&q); } @@ -133,8 +147,10 @@ appid_t is_excluded(const char *key, userid_t user) /* Kernel has already enforced everything we returned through * derive_permissions_locked(), so this is used to lock down access - * even further, such as enforcing that apps hold sdcard_rw. */ -int check_caller_access_to_name(struct inode *parent_node, const struct qstr *name) { + * even further, such as enforcing that apps hold sdcard_rw. + */ +int check_caller_access_to_name(struct inode *parent_node, const struct qstr *name) +{ struct qstr q_autorun = QSTR_LITERAL("autorun.inf"); struct qstr q__android_secure = QSTR_LITERAL(".android_secure"); struct qstr q_android_secure = QSTR_LITERAL("android_secure"); @@ -149,26 +165,26 @@ int check_caller_access_to_name(struct inode *parent_node, const struct qstr *na } /* Root always has access; access for any other UIDs should always - * be controlled through packages.list. */ - if (from_kuid(&init_user_ns, current_fsuid()) == 0) { + * be controlled through packages.list. + */ + if (from_kuid(&init_user_ns, current_fsuid()) == 0) return 1; - } /* No extra permissions to enforce */ return 1; } /* This function is used when file opening. The open flags must be - * checked before calling check_caller_access_to_name() */ -int open_flags_to_access_mode(int open_flags) { - if((open_flags & O_ACCMODE) == O_RDONLY) { + * checked before calling check_caller_access_to_name() + */ +int open_flags_to_access_mode(int open_flags) +{ + if ((open_flags & O_ACCMODE) == O_RDONLY) return 0; /* R_OK */ - } else if ((open_flags & O_ACCMODE) == O_WRONLY) { + if ((open_flags & O_ACCMODE) == O_WRONLY) return 1; /* W_OK */ - } else { - /* Probably O_RDRW, but treat as default to be safe */ + /* Probably O_RDRW, but treat as default to be safe */ return 1; /* R_OK | W_OK */ - } } static struct hashtable_entry *alloc_hashtable_entry(const struct qstr *key, @@ -178,6 +194,8 @@ static struct hashtable_entry *alloc_hashtable_entry(const struct qstr *key, GFP_KERNEL); if (!ret) return NULL; + INIT_HLIST_NODE(&ret->dlist); + INIT_HLIST_NODE(&ret->hlist); if (!qstr_copy(key, &ret->key)) { kmem_cache_free(hashtable_entry_cachep, ret); @@ -249,8 +267,7 @@ static void fixup_all_perms_name(const struct qstr *key) struct sdcardfs_sb_info *sbinfo; struct limit_search limit = { .flags = BY_NAME, - .name = key->name, - .length = key->len, + .name = QSTR_INIT(key->name, key->len), }; list_for_each_entry(sbinfo, &sdcardfs_super_list, list) { if (sbinfo_has_sdcard_magic(sbinfo)) @@ -263,8 +280,7 @@ static void fixup_all_perms_name_userid(const struct qstr *key, userid_t userid) struct sdcardfs_sb_info *sbinfo; struct limit_search limit = { .flags = BY_NAME | BY_USERID, - .name = key->name, - .length = key->len, + .name = QSTR_INIT(key->name, key->len), .userid = userid, }; list_for_each_entry(sbinfo, &sdcardfs_super_list, list) { @@ -326,7 +342,6 @@ static int insert_userid_exclude_entry(const struct qstr *key, userid_t value) static void free_hashtable_entry(struct hashtable_entry *entry) { kfree(entry->key.name); - hash_del_rcu(&entry->dlist); kmem_cache_free(hashtable_entry_cachep, entry); } @@ -361,7 +376,6 @@ static void remove_packagelist_entry(const struct qstr *key) remove_packagelist_entry_locked(key); fixup_all_perms_name(key); mutex_unlock(&sdcardfs_super_list_lock); - return; } static void remove_ext_gid_entry_locked(const struct qstr *key, gid_t group) @@ -384,7 +398,6 @@ static void remove_ext_gid_entry(const struct qstr *key, gid_t group) mutex_lock(&sdcardfs_super_list_lock); remove_ext_gid_entry_locked(key, group); mutex_unlock(&sdcardfs_super_list_lock); - return; } static void remove_userid_all_entry_locked(userid_t userid) @@ -412,7 +425,6 @@ static void remove_userid_all_entry(userid_t userid) remove_userid_all_entry_locked(userid); fixup_all_perms_userid(userid); mutex_unlock(&sdcardfs_super_list_lock); - return; } static void remove_userid_exclude_entry_locked(const struct qstr *key, userid_t userid) @@ -437,7 +449,6 @@ static void remove_userid_exclude_entry(const struct qstr *key, userid_t userid) remove_userid_exclude_entry_locked(key, userid); fixup_all_perms_name_userid(key, userid); mutex_unlock(&sdcardfs_super_list_lock); - return; } static void packagelist_destroy(void) @@ -446,6 +457,7 @@ static void packagelist_destroy(void) struct hlist_node *h_t; HLIST_HEAD(free_list); int i; + mutex_lock(&sdcardfs_super_list_lock); hash_for_each_rcu(package_to_appid, i, hash_cur, hlist) { hash_del_rcu(&hash_cur->hlist); @@ -459,7 +471,7 @@ static void packagelist_destroy(void) hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist) free_hashtable_entry(hash_cur); mutex_unlock(&sdcardfs_super_list_lock); - printk(KERN_INFO "sdcardfs: destroyed packagelist pkgld\n"); + pr_info("sdcardfs: destroyed packagelist pkgld\n"); } #define SDCARDFS_CONFIGFS_ATTR(_pfx, _name) \ @@ -575,7 +587,8 @@ static ssize_t package_details_clear_userid_store(struct config_item *item, static void package_details_release(struct config_item *item) { struct package_details *package_details = to_package_details(item); - printk(KERN_INFO "sdcardfs: removing %s\n", package_details->name.name); + + pr_info("sdcardfs: removing %s\n", package_details->name.name); remove_packagelist_entry(&package_details->name); kfree(package_details->name.name); kfree(package_details); @@ -593,7 +606,7 @@ static struct configfs_attribute *package_details_attrs[] = { }; static struct configfs_item_operations package_details_item_ops = { - .release = package_details_release, + .release = package_details_release, }; static struct config_item_type package_appid_type = { @@ -627,7 +640,7 @@ static void extension_details_release(struct config_item *item) { struct extension_details *extension_details = to_extension_details(item); - printk(KERN_INFO "sdcardfs: No longer mapping %s files to gid %d\n", + pr_info("sdcardfs: No longer mapping %s files to gid %d\n", extension_details->name.name, extension_details->num); remove_ext_gid_entry(&extension_details->name, extension_details->num); kfree(extension_details->name.name); @@ -649,6 +662,7 @@ static struct config_item *extension_details_make_item(struct config_group *grou struct extension_details *extension_details = kzalloc(sizeof(struct extension_details), GFP_KERNEL); const char *tmp; int ret; + if (!extension_details) return ERR_PTR(-ENOMEM); @@ -703,7 +717,8 @@ static struct config_group *extensions_make_group(struct config_group *group, co static void extensions_drop_group(struct config_group *group, struct config_item *item) { struct extensions_value *value = to_extensions_value(item); - printk(KERN_INFO "sdcardfs: No longer mapping any files to gid %d\n", value->num); + + pr_info("sdcardfs: No longer mapping any files to gid %d\n", value->num); kfree(value); } @@ -837,14 +852,14 @@ static int configfs_sdcardfs_init(void) { int ret, i; struct configfs_subsystem *subsys = &sdcardfs_packages; - for (i = 0; sd_default_groups[i]; i++) { + + for (i = 0; sd_default_groups[i]; i++) config_group_init(sd_default_groups[i]); - } config_group_init(&subsys->su_group); mutex_init(&subsys->su_mutex); ret = configfs_register_subsystem(subsys); if (ret) { - printk(KERN_ERR "Error %d while registering subsystem %s\n", + pr_err("Error %d while registering subsystem %s\n", ret, subsys->su_group.cg_item.ci_namebuf); } @@ -862,18 +877,17 @@ int packagelist_init(void) kmem_cache_create("packagelist_hashtable_entry", sizeof(struct hashtable_entry), 0, 0, NULL); if (!hashtable_entry_cachep) { - printk(KERN_ERR "sdcardfs: failed creating pkgl_hashtable entry slab cache\n"); + pr_err("sdcardfs: failed creating pkgl_hashtable entry slab cache\n"); return -ENOMEM; } configfs_sdcardfs_init(); - return 0; + return 0; } void packagelist_exit(void) { configfs_sdcardfs_exit(); packagelist_destroy(); - if (hashtable_entry_cachep) - kmem_cache_destroy(hashtable_entry_cachep); + kmem_cache_destroy(hashtable_entry_cachep); } diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index f3cced313108..2b67b9a8ef9f 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -29,6 +29,7 @@ #include <linux/dcache.h> #include <linux/file.h> #include <linux/fs.h> +#include <linux/aio.h> #include <linux/mm.h> #include <linux/mount.h> #include <linux/namei.h> @@ -52,7 +53,7 @@ #define SDCARDFS_ROOT_INO 1 /* useful for tracking code reachability */ -#define UDBG printk(KERN_DEFAULT "DBG:%s:%s:%d\n", __FILE__, __func__, __LINE__) +#define UDBG pr_default("DBG:%s:%s:%d\n", __FILE__, __func__, __LINE__) #define SDCARDFS_DIRENT_SIZE 256 @@ -86,54 +87,56 @@ } while (0) /* OVERRIDE_CRED() and REVERT_CRED() - * OVERRID_CRED() - * backup original task->cred - * and modifies task->cred->fsuid/fsgid to specified value. + * OVERRIDE_CRED() + * backup original task->cred + * and modifies task->cred->fsuid/fsgid to specified value. * REVERT_CRED() - * restore original task->cred->fsuid/fsgid. + * restore original task->cred->fsuid/fsgid. * These two macro should be used in pair, and OVERRIDE_CRED() should be * placed at the beginning of a function, right after variable declaration. */ #define OVERRIDE_CRED(sdcardfs_sbi, saved_cred, info) \ + do { \ saved_cred = override_fsids(sdcardfs_sbi, info); \ - if (!saved_cred) { return -ENOMEM; } + if (!saved_cred) \ + return -ENOMEM; \ + } while (0) #define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred, info) \ + do { \ saved_cred = override_fsids(sdcardfs_sbi, info); \ - if (!saved_cred) { return ERR_PTR(-ENOMEM); } + if (!saved_cred) \ + return ERR_PTR(-ENOMEM); \ + } while (0) #define REVERT_CRED(saved_cred) revert_fsids(saved_cred) -#define DEBUG_CRED() \ - printk("KAKJAGI: %s:%d fsuid %d fsgid %d\n", \ - __FUNCTION__, __LINE__, \ - (int)current->cred->fsuid, \ - (int)current->cred->fsgid); - /* Android 5.0 support */ /* Permission mode for a specific node. Controls how file permissions - * are derived for children nodes. */ + * are derived for children nodes. + */ typedef enum { - /* Nothing special; this node should just inherit from its parent. */ - PERM_INHERIT, - /* This node is one level above a normal root; used for legacy layouts - * which use the first level to represent user_id. */ - PERM_PRE_ROOT, - /* This node is "/" */ - PERM_ROOT, - /* This node is "/Android" */ - PERM_ANDROID, - /* This node is "/Android/data" */ - PERM_ANDROID_DATA, - /* This node is "/Android/obb" */ - PERM_ANDROID_OBB, - /* This node is "/Android/media" */ - PERM_ANDROID_MEDIA, - /* This node is "/Android/[data|media|obb]/[package]" */ - PERM_ANDROID_PACKAGE, - /* This node is "/Android/[data|media|obb]/[package]/cache" */ - PERM_ANDROID_PACKAGE_CACHE, + /* Nothing special; this node should just inherit from its parent. */ + PERM_INHERIT, + /* This node is one level above a normal root; used for legacy layouts + * which use the first level to represent user_id. + */ + PERM_PRE_ROOT, + /* This node is "/" */ + PERM_ROOT, + /* This node is "/Android" */ + PERM_ANDROID, + /* This node is "/Android/data" */ + PERM_ANDROID_DATA, + /* This node is "/Android/obb" */ + PERM_ANDROID_OBB, + /* This node is "/Android/media" */ + PERM_ANDROID_MEDIA, + /* This node is "/Android/[data|media|obb]/[package]" */ + PERM_ANDROID_PACKAGE, + /* This node is "/Android/[data|media|obb]/[package]/cache" */ + PERM_ANDROID_PACKAGE_CACHE, } perm_t; struct sdcardfs_sb_info; @@ -141,9 +144,9 @@ struct sdcardfs_mount_options; struct sdcardfs_inode_info; /* Do not directly use this function. Use OVERRIDE_CRED() instead. */ -const struct cred * override_fsids(struct sdcardfs_sb_info* sbi, struct sdcardfs_inode_info *info); +const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_inode_info *info); /* Do not directly use this function, use REVERT_CRED() instead. */ -void revert_fsids(const struct cred * old_cred); +void revert_fsids(const struct cred *old_cred); /* operations vectors defined in specific files */ extern const struct file_operations sdcardfs_main_fops; @@ -220,7 +223,8 @@ struct sdcardfs_sb_info { struct super_block *sb; struct super_block *lower_sb; /* derived perm policy : some of options have been added - * to sdcardfs_mount_options (Android 4.4 support) */ + * to sdcardfs_mount_options (Android 4.4 support) + */ struct sdcardfs_mount_options options; spinlock_t lock; /* protects obbpath */ char *obbpath_s; @@ -331,7 +335,7 @@ static inline void sdcardfs_put_reset_##pname(const struct dentry *dent) \ { \ struct path pname; \ spin_lock(&SDCARDFS_D(dent)->lock); \ - if(SDCARDFS_D(dent)->pname.dentry) { \ + if (SDCARDFS_D(dent)->pname.dentry) { \ pathcpy(&pname, &SDCARDFS_D(dent)->pname); \ SDCARDFS_D(dent)->pname.dentry = NULL; \ SDCARDFS_D(dent)->pname.mnt = NULL; \ @@ -347,17 +351,17 @@ SDCARDFS_DENT_FUNC(orig_path) static inline bool sbinfo_has_sdcard_magic(struct sdcardfs_sb_info *sbinfo) { - return sbinfo && sbinfo->sb && sbinfo->sb->s_magic == SDCARDFS_SUPER_MAGIC; + return sbinfo && sbinfo->sb && sbinfo->sb->s_magic == SDCARDFS_SUPER_MAGIC; } /* grab a refererence if we aren't linking to ourself */ static inline void set_top(struct sdcardfs_inode_info *info, struct inode *top) { struct inode *old_top = NULL; + BUG_ON(IS_ERR_OR_NULL(top)); - if (info->top && info->top != &info->vfs_inode) { + if (info->top && info->top != &info->vfs_inode) old_top = info->top; - } if (top != &info->vfs_inode) igrab(top); info->top = top; @@ -367,11 +371,11 @@ static inline void set_top(struct sdcardfs_inode_info *info, struct inode *top) static inline struct inode *grab_top(struct sdcardfs_inode_info *info) { struct inode *top = info->top; - if (top) { + + if (top) return igrab(top); - } else { + else return NULL; - } } static inline void release_top(struct sdcardfs_inode_info *info) @@ -379,21 +383,24 @@ static inline void release_top(struct sdcardfs_inode_info *info) iput(info->top); } -static inline int get_gid(struct vfsmount *mnt, struct sdcardfs_inode_info *info) { +static inline int get_gid(struct vfsmount *mnt, struct sdcardfs_inode_info *info) +{ struct sdcardfs_vfsmount_options *opts = mnt->data; - if (opts->gid == AID_SDCARD_RW) { + if (opts->gid == AID_SDCARD_RW) /* As an optimization, certain trusted system components only run * as owner but operate across all users. Since we're now handing * out the sdcard_rw GID only to trusted apps, we're okay relaxing * the user boundary enforcement for the default view. The UIDs - * assigned to app directories are still multiuser aware. */ + * assigned to app directories are still multiuser aware. + */ return AID_SDCARD_RW; - } else { + else return multiuser_get_uid(info->userid, opts->gid); - } } -static inline int get_mode(struct vfsmount *mnt, struct sdcardfs_inode_info *info) { + +static inline int get_mode(struct vfsmount *mnt, struct sdcardfs_inode_info *info) +{ int owner_mode; int filtered_mode; struct sdcardfs_vfsmount_options *opts = mnt->data; @@ -402,17 +409,18 @@ static inline int get_mode(struct vfsmount *mnt, struct sdcardfs_inode_info *inf if (info->perm == PERM_PRE_ROOT) { /* Top of multi-user view should always be visible to ensure - * secondary users can traverse inside. */ + * secondary users can traverse inside. + */ visible_mode = 0711; } else if (info->under_android) { /* Block "other" access to Android directories, since only apps * belonging to a specific user should be in there; we still - * leave +x open for the default view. */ - if (opts->gid == AID_SDCARD_RW) { + * leave +x open for the default view. + */ + if (opts->gid == AID_SDCARD_RW) visible_mode = visible_mode & ~0006; - } else { + else visible_mode = visible_mode & ~0007; - } } owner_mode = info->lower_inode->i_mode & 0700; filtered_mode = visible_mode & (owner_mode | (owner_mode >> 3) | (owner_mode >> 6)); @@ -437,7 +445,7 @@ static inline void sdcardfs_get_real_lower(const struct dentry *dent, /* in case of a local obb dentry * the orig_path should be returned */ - if(has_graft_path(dent)) + if (has_graft_path(dent)) sdcardfs_get_orig_path(dent, real_lower); else sdcardfs_get_lower_path(dent, real_lower); @@ -446,7 +454,7 @@ static inline void sdcardfs_get_real_lower(const struct dentry *dent, static inline void sdcardfs_put_real_lower(const struct dentry *dent, struct path *real_lower) { - if(has_graft_path(dent)) + if (has_graft_path(dent)) sdcardfs_put_orig_path(dent, real_lower); else sdcardfs_put_lower_path(dent, real_lower); @@ -459,7 +467,7 @@ extern struct list_head sdcardfs_super_list; extern appid_t get_appid(const char *app_name); extern appid_t get_ext_gid(const char *app_name); extern appid_t is_excluded(const char *app_name, userid_t userid); -extern int check_caller_access_to_name(struct inode *parent_node, const struct qstr* name); +extern int check_caller_access_to_name(struct inode *parent_node, const struct qstr *name); extern int open_flags_to_access_mode(int open_flags); extern int packagelist_init(void); extern void packagelist_exit(void); @@ -469,8 +477,7 @@ extern void packagelist_exit(void); #define BY_USERID (1 << 1) struct limit_search { unsigned int flags; - const char *name; - size_t length; + struct qstr name; userid_t userid; }; @@ -478,12 +485,10 @@ extern void setup_derived_state(struct inode *inode, perm_t perm, userid_t useri uid_t uid, bool under_android, struct inode *top); extern void get_derived_permission(struct dentry *parent, struct dentry *dentry); extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name); -extern void drop_recursive(struct dentry *parent); -extern void fixup_top_recursive(struct dentry *parent); extern void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit); extern void update_derived_permission_lock(struct dentry *dentry); -void fixup_lower_ownership(struct dentry* dentry, const char *name); +void fixup_lower_ownership(struct dentry *dentry, const char *name); extern int need_graft_path(struct dentry *dentry); extern int is_base_obbpath(struct dentry *dentry); extern int is_obbpath_invalid(struct dentry *dentry); @@ -493,6 +498,7 @@ extern int setup_obb_dentry(struct dentry *dentry, struct path *lower_path); static inline struct dentry *lock_parent(struct dentry *dentry) { struct dentry *dir = dget_parent(dentry); + mutex_lock_nested(&d_inode(dir)->i_mutex, I_MUTEX_PARENT); return dir; } @@ -611,6 +617,11 @@ static inline bool str_case_eq(const char *s1, const char *s2) return !strcasecmp(s1, s2); } +static inline bool str_n_case_eq(const char *s1, const char *s2, size_t len) +{ + return !strncasecmp(s1, s2, len); +} + static inline bool qstr_case_eq(const struct qstr *q1, const struct qstr *q2) { return q1->len == q2->len && str_case_eq(q1->name, q2->name); diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index edda32b68dc0..a3393e959c63 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -36,7 +36,7 @@ static void sdcardfs_put_super(struct super_block *sb) if (!spd) return; - if(spd->obbpath_s) { + if (spd->obbpath_s) { kfree(spd->obbpath_s); path_put(&spd->obbpath); } @@ -64,7 +64,7 @@ static int sdcardfs_statfs(struct dentry *dentry, struct kstatfs *buf) if (sbi->options.reserved_mb) { /* Invalid statfs informations. */ if (buf->f_bsize == 0) { - printk(KERN_ERR "Returned block size is zero.\n"); + pr_err("Returned block size is zero.\n"); return -EINVAL; } @@ -100,8 +100,7 @@ static int sdcardfs_remount_fs(struct super_block *sb, int *flags, char *options * SILENT, but anything else left over is an error. */ if ((*flags & ~(MS_RDONLY | MS_MANDLOCK | MS_SILENT)) != 0) { - printk(KERN_ERR - "sdcardfs: remount flags 0x%x unsupported\n", *flags); + pr_err("sdcardfs: remount flags 0x%x unsupported\n", *flags); err = -EINVAL; } @@ -125,29 +124,33 @@ static int sdcardfs_remount_fs2(struct vfsmount *mnt, struct super_block *sb, * SILENT, but anything else left over is an error. */ if ((*flags & ~(MS_RDONLY | MS_MANDLOCK | MS_SILENT | MS_REMOUNT)) != 0) { - printk(KERN_ERR - "sdcardfs: remount flags 0x%x unsupported\n", *flags); + pr_err("sdcardfs: remount flags 0x%x unsupported\n", *flags); err = -EINVAL; } - printk(KERN_INFO "Remount options were %s for vfsmnt %p.\n", options, mnt); + pr_info("Remount options were %s for vfsmnt %p.\n", options, mnt); err = parse_options_remount(sb, options, *flags & ~MS_SILENT, mnt->data); return err; } -static void* sdcardfs_clone_mnt_data(void *data) { - struct sdcardfs_vfsmount_options* opt = kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL); - struct sdcardfs_vfsmount_options* old = data; - if(!opt) return NULL; +static void *sdcardfs_clone_mnt_data(void *data) +{ + struct sdcardfs_vfsmount_options *opt = kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL); + struct sdcardfs_vfsmount_options *old = data; + + if (!opt) + return NULL; opt->gid = old->gid; opt->mask = old->mask; return opt; } -static void sdcardfs_copy_mnt_data(void *data, void *newdata) { - struct sdcardfs_vfsmount_options* old = data; - struct sdcardfs_vfsmount_options* new = newdata; +static void sdcardfs_copy_mnt_data(void *data, void *newdata) +{ + struct sdcardfs_vfsmount_options *old = data; + struct sdcardfs_vfsmount_options *new = newdata; + old->gid = new->gid; old->mask = new->mask; } @@ -218,8 +221,7 @@ int sdcardfs_init_inode_cache(void) /* sdcardfs inode cache destructor */ void sdcardfs_destroy_inode_cache(void) { - if (sdcardfs_inode_cachep) - kmem_cache_destroy(sdcardfs_inode_cachep); + kmem_cache_destroy(sdcardfs_inode_cachep); } /* @@ -235,7 +237,8 @@ static void sdcardfs_umount_begin(struct super_block *sb) lower_sb->s_op->umount_begin(lower_sb); } -static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m, struct dentry *root) +static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m, + struct dentry *root) { struct sdcardfs_sb_info *sbi = SDCARDFS_SB(root->d_sb); struct sdcardfs_mount_options *opts = &sbi->options; @@ -248,7 +251,7 @@ static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m, struc if (vfsopts->gid != 0) seq_printf(m, ",gid=%u", vfsopts->gid); if (opts->multiuser) - seq_printf(m, ",multiuser"); + seq_puts(m, ",multiuser"); if (vfsopts->mask) seq_printf(m, ",mask=%u", vfsopts->mask); if (opts->fs_user_id) diff --git a/fs/squashfs/Kconfig b/fs/squashfs/Kconfig index ffb093e72b6c..6dd158a216f4 100644 --- a/fs/squashfs/Kconfig +++ b/fs/squashfs/Kconfig @@ -26,34 +26,6 @@ config SQUASHFS If unsure, say N. choice - prompt "File decompression options" - depends on SQUASHFS - help - Squashfs now supports two options for decompressing file - data. Traditionally Squashfs has decompressed into an - intermediate buffer and then memcopied it into the page cache. - Squashfs now supports the ability to decompress directly into - the page cache. - - If unsure, select "Decompress file data into an intermediate buffer" - -config SQUASHFS_FILE_CACHE - bool "Decompress file data into an intermediate buffer" - help - Decompress file data into an intermediate buffer and then - memcopy it into the page cache. - -config SQUASHFS_FILE_DIRECT - bool "Decompress files directly into the page cache" - help - Directly decompress file data into the page cache. - Doing so can significantly improve performance because - it eliminates a memcpy and it also removes the lock contention - on the single buffer. - -endchoice - -choice prompt "Decompressor parallelisation options" depends on SQUASHFS help diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile index 246a6f329d89..fe51f1507ed1 100644 --- a/fs/squashfs/Makefile +++ b/fs/squashfs/Makefile @@ -5,8 +5,7 @@ obj-$(CONFIG_SQUASHFS) += squashfs.o squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o squashfs-y += namei.o super.o symlink.o decompressor.o -squashfs-$(CONFIG_SQUASHFS_FILE_CACHE) += file_cache.o -squashfs-$(CONFIG_SQUASHFS_FILE_DIRECT) += file_direct.o page_actor.o +squashfs-y += file_direct.o page_actor.o squashfs-$(CONFIG_SQUASHFS_DECOMP_SINGLE) += decompressor_single.o squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI) += decompressor_multi.o squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU) += decompressor_multi_percpu.o diff --git a/fs/squashfs/block.c b/fs/squashfs/block.c index 0cea9b9236d0..2eb66decc5ab 100644 --- a/fs/squashfs/block.c +++ b/fs/squashfs/block.c @@ -28,9 +28,12 @@ #include <linux/fs.h> #include <linux/vfs.h> +#include <linux/bio.h> #include <linux/slab.h> #include <linux/string.h> +#include <linux/pagemap.h> #include <linux/buffer_head.h> +#include <linux/workqueue.h> #include "squashfs_fs.h" #include "squashfs_fs_sb.h" @@ -38,177 +41,434 @@ #include "decompressor.h" #include "page_actor.h" -/* - * Read the metadata block length, this is stored in the first two - * bytes of the metadata block. - */ -static struct buffer_head *get_block_length(struct super_block *sb, - u64 *cur_index, int *offset, int *length) +static struct workqueue_struct *squashfs_read_wq; + +struct squashfs_read_request { + struct super_block *sb; + u64 index; + int length; + int compressed; + int offset; + u64 read_end; + struct squashfs_page_actor *output; + enum { + SQUASHFS_COPY, + SQUASHFS_DECOMPRESS, + SQUASHFS_METADATA, + } data_processing; + bool synchronous; + + /* + * If the read is synchronous, it is possible to retrieve information + * about the request by setting these pointers. + */ + int *res; + int *bytes_read; + int *bytes_uncompressed; + + int nr_buffers; + struct buffer_head **bh; + struct work_struct offload; +}; + +struct squashfs_bio_request { + struct buffer_head **bh; + int nr_buffers; +}; + +static int squashfs_bio_submit(struct squashfs_read_request *req); + +int squashfs_init_read_wq(void) { - struct squashfs_sb_info *msblk = sb->s_fs_info; - struct buffer_head *bh; + squashfs_read_wq = create_workqueue("SquashFS read wq"); + return !!squashfs_read_wq; +} + +void squashfs_destroy_read_wq(void) +{ + flush_workqueue(squashfs_read_wq); + destroy_workqueue(squashfs_read_wq); +} + +static void free_read_request(struct squashfs_read_request *req, int error) +{ + if (!req->synchronous) + squashfs_page_actor_free(req->output, error); + if (req->res) + *(req->res) = error; + kfree(req->bh); + kfree(req); +} + +static void squashfs_process_blocks(struct squashfs_read_request *req) +{ + int error = 0; + int bytes, i, length; + struct squashfs_sb_info *msblk = req->sb->s_fs_info; + struct squashfs_page_actor *actor = req->output; + struct buffer_head **bh = req->bh; + int nr_buffers = req->nr_buffers; + + for (i = 0; i < nr_buffers; ++i) { + if (!bh[i]) + continue; + wait_on_buffer(bh[i]); + if (!buffer_uptodate(bh[i])) + error = -EIO; + } + if (error) + goto cleanup; + + if (req->data_processing == SQUASHFS_METADATA) { + /* Extract the length of the metadata block */ + if (req->offset != msblk->devblksize - 1) + length = *((u16 *)(bh[0]->b_data + req->offset)); + else { + length = bh[0]->b_data[req->offset]; + length |= bh[1]->b_data[0] << 8; + } + req->compressed = SQUASHFS_COMPRESSED(length); + req->data_processing = req->compressed ? SQUASHFS_DECOMPRESS + : SQUASHFS_COPY; + length = SQUASHFS_COMPRESSED_SIZE(length); + if (req->index + length + 2 > req->read_end) { + for (i = 0; i < nr_buffers; ++i) + put_bh(bh[i]); + kfree(bh); + req->length = length; + req->index += 2; + squashfs_bio_submit(req); + return; + } + req->length = length; + req->offset = (req->offset + 2) % PAGE_SIZE; + if (req->offset < 2) { + put_bh(bh[0]); + ++bh; + --nr_buffers; + } + } + if (req->bytes_read) + *(req->bytes_read) = req->length; - bh = sb_bread(sb, *cur_index); - if (bh == NULL) - return NULL; - - if (msblk->devblksize - *offset == 1) { - *length = (unsigned char) bh->b_data[*offset]; - put_bh(bh); - bh = sb_bread(sb, ++(*cur_index)); - if (bh == NULL) - return NULL; - *length |= (unsigned char) bh->b_data[0] << 8; - *offset = 1; - } else { - *length = (unsigned char) bh->b_data[*offset] | - (unsigned char) bh->b_data[*offset + 1] << 8; - *offset += 2; - - if (*offset == msblk->devblksize) { - put_bh(bh); - bh = sb_bread(sb, ++(*cur_index)); - if (bh == NULL) - return NULL; - *offset = 0; + if (req->data_processing == SQUASHFS_COPY) { + squashfs_bh_to_actor(bh, nr_buffers, req->output, req->offset, + req->length, msblk->devblksize); + } else if (req->data_processing == SQUASHFS_DECOMPRESS) { + req->length = squashfs_decompress(msblk, bh, nr_buffers, + req->offset, req->length, actor); + if (req->length < 0) { + error = -EIO; + goto cleanup; } } - return bh; + /* Last page may have trailing bytes not filled */ + bytes = req->length % PAGE_SIZE; + if (bytes && actor->page[actor->pages - 1]) + zero_user_segment(actor->page[actor->pages - 1], bytes, + PAGE_SIZE); + +cleanup: + if (req->bytes_uncompressed) + *(req->bytes_uncompressed) = req->length; + if (error) { + for (i = 0; i < nr_buffers; ++i) + if (bh[i]) + put_bh(bh[i]); + } + free_read_request(req, error); } +static void read_wq_handler(struct work_struct *work) +{ + squashfs_process_blocks(container_of(work, + struct squashfs_read_request, offload)); +} -/* - * Read and decompress a metadata block or datablock. Length is non-zero - * if a datablock is being read (the size is stored elsewhere in the - * filesystem), otherwise the length is obtained from the first two bytes of - * the metadata block. A bit in the length field indicates if the block - * is stored uncompressed in the filesystem (usually because compression - * generated a larger block - this does occasionally happen with compression - * algorithms). - */ -int squashfs_read_data(struct super_block *sb, u64 index, int length, - u64 *next_index, struct squashfs_page_actor *output) +static void squashfs_bio_end_io(struct bio *bio) { - struct squashfs_sb_info *msblk = sb->s_fs_info; - struct buffer_head **bh; - int offset = index & ((1 << msblk->devblksize_log2) - 1); - u64 cur_index = index >> msblk->devblksize_log2; - int bytes, compressed, b = 0, k = 0, avail, i; + int i; + int error = bio->bi_error; + struct squashfs_bio_request *bio_req = bio->bi_private; + + bio_put(bio); + + for (i = 0; i < bio_req->nr_buffers; ++i) { + if (!bio_req->bh[i]) + continue; + if (!error) + set_buffer_uptodate(bio_req->bh[i]); + else + clear_buffer_uptodate(bio_req->bh[i]); + unlock_buffer(bio_req->bh[i]); + } + kfree(bio_req); +} + +static int bh_is_optional(struct squashfs_read_request *req, int idx) +{ + int start_idx, end_idx; + struct squashfs_sb_info *msblk = req->sb->s_fs_info; - bh = kcalloc(((output->length + msblk->devblksize - 1) - >> msblk->devblksize_log2) + 1, sizeof(*bh), GFP_KERNEL); - if (bh == NULL) + start_idx = (idx * msblk->devblksize - req->offset) / PAGE_CACHE_SIZE; + end_idx = ((idx + 1) * msblk->devblksize - req->offset + 1) / PAGE_CACHE_SIZE; + if (start_idx >= req->output->pages) + return 1; + if (start_idx < 0) + start_idx = end_idx; + if (end_idx >= req->output->pages) + end_idx = start_idx; + return !req->output->page[start_idx] && !req->output->page[end_idx]; +} + +static int actor_getblks(struct squashfs_read_request *req, u64 block) +{ + int i; + + req->bh = kmalloc_array(req->nr_buffers, sizeof(*(req->bh)), GFP_NOIO); + if (!req->bh) return -ENOMEM; - if (length) { + for (i = 0; i < req->nr_buffers; ++i) { /* - * Datablock. + * When dealing with an uncompressed block, the actor may + * contains NULL pages. There's no need to read the buffers + * associated with these pages. */ - bytes = -offset; - compressed = SQUASHFS_COMPRESSED_BLOCK(length); - length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length); - if (next_index) - *next_index = index + length; - - TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n", - index, compressed ? "" : "un", length, output->length); - - if (length < 0 || length > output->length || - (index + length) > msblk->bytes_used) - goto read_failure; - - for (b = 0; bytes < length; b++, cur_index++) { - bh[b] = sb_getblk(sb, cur_index); - if (bh[b] == NULL) - goto block_release; - bytes += msblk->devblksize; + if (!req->compressed && bh_is_optional(req, i)) { + req->bh[i] = NULL; + continue; } - ll_rw_block(READ, b, bh); - } else { - /* - * Metadata block. - */ - if ((index + 2) > msblk->bytes_used) - goto read_failure; + req->bh[i] = sb_getblk(req->sb, block + i); + if (!req->bh[i]) { + while (--i) { + if (req->bh[i]) + put_bh(req->bh[i]); + } + return -1; + } + } + return 0; +} - bh[0] = get_block_length(sb, &cur_index, &offset, &length); - if (bh[0] == NULL) - goto read_failure; - b = 1; +static int squashfs_bio_submit(struct squashfs_read_request *req) +{ + struct bio *bio = NULL; + struct buffer_head *bh; + struct squashfs_bio_request *bio_req = NULL; + int b = 0, prev_block = 0; + struct squashfs_sb_info *msblk = req->sb->s_fs_info; - bytes = msblk->devblksize - offset; - compressed = SQUASHFS_COMPRESSED(length); - length = SQUASHFS_COMPRESSED_SIZE(length); - if (next_index) - *next_index = index + length + 2; + u64 read_start = round_down(req->index, msblk->devblksize); + u64 read_end = round_up(req->index + req->length, msblk->devblksize); + sector_t block = read_start >> msblk->devblksize_log2; + sector_t block_end = read_end >> msblk->devblksize_log2; + int offset = read_start - round_down(req->index, PAGE_SIZE); + int nr_buffers = block_end - block; + int blksz = msblk->devblksize; + int bio_max_pages = nr_buffers > BIO_MAX_PAGES ? BIO_MAX_PAGES + : nr_buffers; - TRACE("Block @ 0x%llx, %scompressed size %d\n", index, - compressed ? "" : "un", length); + /* Setup the request */ + req->read_end = read_end; + req->offset = req->index - read_start; + req->nr_buffers = nr_buffers; + if (actor_getblks(req, block) < 0) + goto getblk_failed; - if (length < 0 || length > output->length || - (index + length) > msblk->bytes_used) - goto block_release; + /* Create and submit the BIOs */ + for (b = 0; b < nr_buffers; ++b, offset += blksz) { + bh = req->bh[b]; + if (!bh || !trylock_buffer(bh)) + continue; + if (buffer_uptodate(bh)) { + unlock_buffer(bh); + continue; + } + offset %= PAGE_SIZE; - for (; bytes < length; b++) { - bh[b] = sb_getblk(sb, ++cur_index); - if (bh[b] == NULL) - goto block_release; - bytes += msblk->devblksize; + /* Append the buffer to the current BIO if it is contiguous */ + if (bio && bio_req && prev_block + 1 == b) { + if (bio_add_page(bio, bh->b_page, blksz, offset)) { + bio_req->nr_buffers += 1; + prev_block = b; + continue; + } } - ll_rw_block(READ, b - 1, bh + 1); + + /* Otherwise, submit the current BIO and create a new one */ + if (bio) + submit_bio(READ, bio); + bio_req = kcalloc(1, sizeof(struct squashfs_bio_request), + GFP_NOIO); + if (!bio_req) + goto req_alloc_failed; + bio_req->bh = &req->bh[b]; + bio = bio_alloc(GFP_NOIO, bio_max_pages); + if (!bio) + goto bio_alloc_failed; + bio->bi_bdev = req->sb->s_bdev; + bio->bi_iter.bi_sector = (block + b) + << (msblk->devblksize_log2 - 9); + bio->bi_private = bio_req; + bio->bi_end_io = squashfs_bio_end_io; + + bio_add_page(bio, bh->b_page, blksz, offset); + bio_req->nr_buffers += 1; + prev_block = b; } + if (bio) + submit_bio(READ, bio); - for (i = 0; i < b; i++) { - wait_on_buffer(bh[i]); - if (!buffer_uptodate(bh[i])) - goto block_release; + if (req->synchronous) + squashfs_process_blocks(req); + else { + INIT_WORK(&req->offload, read_wq_handler); + schedule_work(&req->offload); } + return 0; - if (compressed) { - length = squashfs_decompress(msblk, bh, b, offset, length, - output); - if (length < 0) - goto read_failure; - } else { - /* - * Block is uncompressed. - */ - int in, pg_offset = 0; - void *data = squashfs_first_page(output); - - for (bytes = length; k < b; k++) { - in = min(bytes, msblk->devblksize - offset); - bytes -= in; - while (in) { - if (pg_offset == PAGE_CACHE_SIZE) { - data = squashfs_next_page(output); - pg_offset = 0; - } - avail = min_t(int, in, PAGE_CACHE_SIZE - - pg_offset); - memcpy(data + pg_offset, bh[k]->b_data + offset, - avail); - in -= avail; - pg_offset += avail; - offset += avail; - } - offset = 0; - put_bh(bh[k]); - } - squashfs_finish_page(output); +bio_alloc_failed: + kfree(bio_req); +req_alloc_failed: + unlock_buffer(bh); + while (--nr_buffers >= b) + if (req->bh[nr_buffers]) + put_bh(req->bh[nr_buffers]); + while (--b >= 0) + if (req->bh[b]) + wait_on_buffer(req->bh[b]); +getblk_failed: + free_read_request(req, -ENOMEM); + return -ENOMEM; +} + +static int read_metadata_block(struct squashfs_read_request *req, + u64 *next_index) +{ + int ret, error, bytes_read = 0, bytes_uncompressed = 0; + struct squashfs_sb_info *msblk = req->sb->s_fs_info; + + if (req->index + 2 > msblk->bytes_used) { + free_read_request(req, -EINVAL); + return -EINVAL; + } + req->length = 2; + + /* Do not read beyond the end of the device */ + if (req->index + req->length > msblk->bytes_used) + req->length = msblk->bytes_used - req->index; + req->data_processing = SQUASHFS_METADATA; + + /* + * Reading metadata is always synchronous because we don't know the + * length in advance and the function is expected to update + * 'next_index' and return the length. + */ + req->synchronous = true; + req->res = &error; + req->bytes_read = &bytes_read; + req->bytes_uncompressed = &bytes_uncompressed; + + TRACE("Metadata block @ 0x%llx, %scompressed size %d, src size %d\n", + req->index, req->compressed ? "" : "un", bytes_read, + req->output->length); + + ret = squashfs_bio_submit(req); + if (ret) + return ret; + if (error) + return error; + if (next_index) + *next_index += 2 + bytes_read; + return bytes_uncompressed; +} + +static int read_data_block(struct squashfs_read_request *req, int length, + u64 *next_index, bool synchronous) +{ + int ret, error = 0, bytes_uncompressed = 0, bytes_read = 0; + + req->compressed = SQUASHFS_COMPRESSED_BLOCK(length); + req->length = length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length); + req->data_processing = req->compressed ? SQUASHFS_DECOMPRESS + : SQUASHFS_COPY; + + req->synchronous = synchronous; + if (synchronous) { + req->res = &error; + req->bytes_read = &bytes_read; + req->bytes_uncompressed = &bytes_uncompressed; + } + + TRACE("Data block @ 0x%llx, %scompressed size %d, src size %d\n", + req->index, req->compressed ? "" : "un", req->length, + req->output->length); + + ret = squashfs_bio_submit(req); + if (ret) + return ret; + if (synchronous) + ret = error ? error : bytes_uncompressed; + if (next_index) + *next_index += length; + return ret; +} + +/* + * Read and decompress a metadata block or datablock. Length is non-zero + * if a datablock is being read (the size is stored elsewhere in the + * filesystem), otherwise the length is obtained from the first two bytes of + * the metadata block. A bit in the length field indicates if the block + * is stored uncompressed in the filesystem (usually because compression + * generated a larger block - this does occasionally happen with compression + * algorithms). + */ +static int __squashfs_read_data(struct super_block *sb, u64 index, int length, + u64 *next_index, struct squashfs_page_actor *output, bool sync) +{ + struct squashfs_read_request *req; + + req = kcalloc(1, sizeof(struct squashfs_read_request), GFP_KERNEL); + if (!req) { + if (!sync) + squashfs_page_actor_free(output, -ENOMEM); + return -ENOMEM; + } + + req->sb = sb; + req->index = index; + req->output = output; + + if (next_index) + *next_index = index; + + if (length) + length = read_data_block(req, length, next_index, sync); + else + length = read_metadata_block(req, next_index); + + if (length < 0) { + ERROR("squashfs_read_data failed to read block 0x%llx\n", + (unsigned long long)index); + return -EIO; } - kfree(bh); return length; +} -block_release: - for (; k < b; k++) - put_bh(bh[k]); +int squashfs_read_data(struct super_block *sb, u64 index, int length, + u64 *next_index, struct squashfs_page_actor *output) +{ + return __squashfs_read_data(sb, index, length, next_index, output, + true); +} + +int squashfs_read_data_async(struct super_block *sb, u64 index, int length, + u64 *next_index, struct squashfs_page_actor *output) +{ -read_failure: - ERROR("squashfs_read_data failed to read block 0x%llx\n", - (unsigned long long) index); - kfree(bh); - return -EIO; + return __squashfs_read_data(sb, index, length, next_index, output, + false); } diff --git a/fs/squashfs/cache.c b/fs/squashfs/cache.c index 1cb70a0b2168..6785d086ab38 100644 --- a/fs/squashfs/cache.c +++ b/fs/squashfs/cache.c @@ -209,17 +209,14 @@ void squashfs_cache_put(struct squashfs_cache_entry *entry) */ void squashfs_cache_delete(struct squashfs_cache *cache) { - int i, j; + int i; if (cache == NULL) return; for (i = 0; i < cache->entries; i++) { - if (cache->entry[i].data) { - for (j = 0; j < cache->pages; j++) - kfree(cache->entry[i].data[j]); - kfree(cache->entry[i].data); - } + if (cache->entry[i].page) + free_page_array(cache->entry[i].page, cache->pages); kfree(cache->entry[i].actor); } @@ -236,7 +233,7 @@ void squashfs_cache_delete(struct squashfs_cache *cache) struct squashfs_cache *squashfs_cache_init(char *name, int entries, int block_size) { - int i, j; + int i; struct squashfs_cache *cache = kzalloc(sizeof(*cache), GFP_KERNEL); if (cache == NULL) { @@ -268,22 +265,13 @@ struct squashfs_cache *squashfs_cache_init(char *name, int entries, init_waitqueue_head(&cache->entry[i].wait_queue); entry->cache = cache; entry->block = SQUASHFS_INVALID_BLK; - entry->data = kcalloc(cache->pages, sizeof(void *), GFP_KERNEL); - if (entry->data == NULL) { + entry->page = alloc_page_array(cache->pages, GFP_KERNEL); + if (!entry->page) { ERROR("Failed to allocate %s cache entry\n", name); goto cleanup; } - - for (j = 0; j < cache->pages; j++) { - entry->data[j] = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL); - if (entry->data[j] == NULL) { - ERROR("Failed to allocate %s buffer\n", name); - goto cleanup; - } - } - - entry->actor = squashfs_page_actor_init(entry->data, - cache->pages, 0); + entry->actor = squashfs_page_actor_init(entry->page, + cache->pages, 0, NULL); if (entry->actor == NULL) { ERROR("Failed to allocate %s cache entry\n", name); goto cleanup; @@ -314,18 +302,20 @@ int squashfs_copy_data(void *buffer, struct squashfs_cache_entry *entry, return min(length, entry->length - offset); while (offset < entry->length) { - void *buff = entry->data[offset / PAGE_CACHE_SIZE] - + (offset % PAGE_CACHE_SIZE); + void *buff = kmap_atomic(entry->page[offset / PAGE_CACHE_SIZE]) + + (offset % PAGE_CACHE_SIZE); int bytes = min_t(int, entry->length - offset, PAGE_CACHE_SIZE - (offset % PAGE_CACHE_SIZE)); if (bytes >= remaining) { memcpy(buffer, buff, remaining); + kunmap_atomic(buff); remaining = 0; break; } memcpy(buffer, buff, bytes); + kunmap_atomic(buff); buffer += bytes; remaining -= bytes; offset += bytes; @@ -416,43 +406,38 @@ struct squashfs_cache_entry *squashfs_get_datablock(struct super_block *sb, void *squashfs_read_table(struct super_block *sb, u64 block, int length) { int pages = (length + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; - int i, res; - void *table, *buffer, **data; + struct page **page; + void *buff; + int res; struct squashfs_page_actor *actor; - table = buffer = kmalloc(length, GFP_KERNEL); - if (table == NULL) + page = alloc_page_array(pages, GFP_KERNEL); + if (!page) return ERR_PTR(-ENOMEM); - data = kcalloc(pages, sizeof(void *), GFP_KERNEL); - if (data == NULL) { - res = -ENOMEM; - goto failed; - } - - actor = squashfs_page_actor_init(data, pages, length); + actor = squashfs_page_actor_init(page, pages, length, NULL); if (actor == NULL) { res = -ENOMEM; - goto failed2; + goto failed; } - for (i = 0; i < pages; i++, buffer += PAGE_CACHE_SIZE) - data[i] = buffer; - res = squashfs_read_data(sb, block, length | SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, actor); - kfree(data); - kfree(actor); - if (res < 0) - goto failed; + goto failed2; - return table; + buff = kmalloc(length, GFP_KERNEL); + if (!buff) + goto failed2; + squashfs_actor_to_buf(actor, buff, length); + squashfs_page_actor_free(actor, 0); + free_page_array(page, pages); + return buff; failed2: - kfree(data); + squashfs_page_actor_free(actor, 0); failed: - kfree(table); + free_page_array(page, pages); return ERR_PTR(res); } diff --git a/fs/squashfs/decompressor.c b/fs/squashfs/decompressor.c index e9034bf6e5ae..7de35bf297aa 100644 --- a/fs/squashfs/decompressor.c +++ b/fs/squashfs/decompressor.c @@ -24,7 +24,8 @@ #include <linux/types.h> #include <linux/mutex.h> #include <linux/slab.h> -#include <linux/buffer_head.h> +#include <linux/highmem.h> +#include <linux/fs.h> #include "squashfs_fs.h" #include "squashfs_fs_sb.h" @@ -94,40 +95,44 @@ const struct squashfs_decompressor *squashfs_lookup_decompressor(int id) static void *get_comp_opts(struct super_block *sb, unsigned short flags) { struct squashfs_sb_info *msblk = sb->s_fs_info; - void *buffer = NULL, *comp_opts; + void *comp_opts, *buffer = NULL; + struct page *page; struct squashfs_page_actor *actor = NULL; int length = 0; + if (!SQUASHFS_COMP_OPTS(flags)) + return squashfs_comp_opts(msblk, buffer, length); + /* * Read decompressor specific options from file system if present */ - if (SQUASHFS_COMP_OPTS(flags)) { - buffer = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL); - if (buffer == NULL) { - comp_opts = ERR_PTR(-ENOMEM); - goto out; - } - - actor = squashfs_page_actor_init(&buffer, 1, 0); - if (actor == NULL) { - comp_opts = ERR_PTR(-ENOMEM); - goto out; - } - - length = squashfs_read_data(sb, - sizeof(struct squashfs_super_block), 0, NULL, actor); - - if (length < 0) { - comp_opts = ERR_PTR(length); - goto out; - } + + page = alloc_page(GFP_KERNEL); + if (!page) + return ERR_PTR(-ENOMEM); + + actor = squashfs_page_actor_init(&page, 1, 0, NULL); + if (actor == NULL) { + comp_opts = ERR_PTR(-ENOMEM); + goto actor_error; + } + + length = squashfs_read_data(sb, + sizeof(struct squashfs_super_block), 0, NULL, actor); + + if (length < 0) { + comp_opts = ERR_PTR(length); + goto read_error; } + buffer = kmap_atomic(page); comp_opts = squashfs_comp_opts(msblk, buffer, length); + kunmap_atomic(buffer); -out: - kfree(actor); - kfree(buffer); +read_error: + squashfs_page_actor_free(actor, 0); +actor_error: + __free_page(page); return comp_opts; } diff --git a/fs/squashfs/file.c b/fs/squashfs/file.c index e5c9689062ba..6f5ef8d7e55a 100644 --- a/fs/squashfs/file.c +++ b/fs/squashfs/file.c @@ -47,12 +47,16 @@ #include <linux/string.h> #include <linux/pagemap.h> #include <linux/mutex.h> +#include <linux/mm_inline.h> #include "squashfs_fs.h" #include "squashfs_fs_sb.h" #include "squashfs_fs_i.h" #include "squashfs.h" +// Backported from 4.5 +#define lru_to_page(head) (list_entry((head)->prev, struct page, lru)) + /* * Locate cache slot in range [offset, index] for specified inode. If * there's more than one return the slot closest to index. @@ -438,6 +442,21 @@ static int squashfs_readpage_fragment(struct page *page) return res; } +static int squashfs_readpages_fragment(struct page *page, + struct list_head *readahead_pages, struct address_space *mapping) +{ + if (!page) { + page = lru_to_page(readahead_pages); + list_del(&page->lru); + if (add_to_page_cache_lru(page, mapping, page->index, + mapping_gfp_constraint(mapping, GFP_KERNEL))) { + put_page(page); + return 0; + } + } + return squashfs_readpage_fragment(page); +} + static int squashfs_readpage_sparse(struct page *page, int index, int file_end) { struct inode *inode = page->mapping->host; @@ -450,54 +469,105 @@ static int squashfs_readpage_sparse(struct page *page, int index, int file_end) return 0; } -static int squashfs_readpage(struct file *file, struct page *page) +static int squashfs_readpages_sparse(struct page *page, + struct list_head *readahead_pages, int index, int file_end, + struct address_space *mapping) { - struct inode *inode = page->mapping->host; + if (!page) { + page = lru_to_page(readahead_pages); + list_del(&page->lru); + if (add_to_page_cache_lru(page, mapping, page->index, + mapping_gfp_constraint(mapping, GFP_KERNEL))) { + put_page(page); + return 0; + } + } + return squashfs_readpage_sparse(page, index, file_end); +} + +static int __squashfs_readpages(struct file *file, struct page *page, + struct list_head *readahead_pages, unsigned int nr_pages, + struct address_space *mapping) +{ + struct inode *inode = mapping->host; struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; - int index = page->index >> (msblk->block_log - PAGE_CACHE_SHIFT); int file_end = i_size_read(inode) >> msblk->block_log; int res; - void *pageaddr; - TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n", - page->index, squashfs_i(inode)->start); + do { + struct page *cur_page = page ? page + : lru_to_page(readahead_pages); + int page_index = cur_page->index; + int index = page_index >> (msblk->block_log - PAGE_CACHE_SHIFT); + + if (page_index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> + PAGE_CACHE_SHIFT)) + return 1; + + if (index < file_end || squashfs_i(inode)->fragment_block == + SQUASHFS_INVALID_BLK) { + u64 block = 0; + int bsize = read_blocklist(inode, index, &block); + + if (bsize < 0) + return -1; + + if (bsize == 0) { + res = squashfs_readpages_sparse(page, + readahead_pages, index, file_end, + mapping); + } else { + res = squashfs_readpages_block(page, + readahead_pages, &nr_pages, mapping, + page_index, block, bsize); + } + } else { + res = squashfs_readpages_fragment(page, + readahead_pages, mapping); + } + if (res) + return 0; + page = NULL; + } while (readahead_pages && !list_empty(readahead_pages)); - if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> - PAGE_CACHE_SHIFT)) - goto out; + return 0; +} - if (index < file_end || squashfs_i(inode)->fragment_block == - SQUASHFS_INVALID_BLK) { - u64 block = 0; - int bsize = read_blocklist(inode, index, &block); - if (bsize < 0) - goto error_out; +static int squashfs_readpage(struct file *file, struct page *page) +{ + int ret; - if (bsize == 0) - res = squashfs_readpage_sparse(page, index, file_end); + TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n", + page->index, squashfs_i(page->mapping->host)->start); + + get_page(page); + + ret = __squashfs_readpages(file, page, NULL, 1, page->mapping); + if (ret) { + flush_dcache_page(page); + if (ret < 0) + SetPageError(page); else - res = squashfs_readpage_block(page, block, bsize); - } else - res = squashfs_readpage_fragment(page); - - if (!res) - return 0; - -error_out: - SetPageError(page); -out: - pageaddr = kmap_atomic(page); - memset(pageaddr, 0, PAGE_CACHE_SIZE); - kunmap_atomic(pageaddr); - flush_dcache_page(page); - if (!PageError(page)) - SetPageUptodate(page); - unlock_page(page); + SetPageUptodate(page); + zero_user_segment(page, 0, PAGE_CACHE_SIZE); + unlock_page(page); + put_page(page); + } return 0; } +static int squashfs_readpages(struct file *file, struct address_space *mapping, + struct list_head *pages, unsigned int nr_pages) +{ + TRACE("Entered squashfs_readpages, %u pages, first page index %lx\n", + nr_pages, lru_to_page(pages)->index); + __squashfs_readpages(file, NULL, pages, nr_pages, mapping); + return 0; +} + const struct address_space_operations squashfs_aops = { - .readpage = squashfs_readpage + .readpage = squashfs_readpage, + .readpages = squashfs_readpages, }; diff --git a/fs/squashfs/file_cache.c b/fs/squashfs/file_cache.c deleted file mode 100644 index f2310d2a2019..000000000000 --- a/fs/squashfs/file_cache.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2013 - * Phillip Lougher <phillip@squashfs.org.uk> - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - */ - -#include <linux/fs.h> -#include <linux/vfs.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/string.h> -#include <linux/pagemap.h> -#include <linux/mutex.h> - -#include "squashfs_fs.h" -#include "squashfs_fs_sb.h" -#include "squashfs_fs_i.h" -#include "squashfs.h" - -/* Read separately compressed datablock and memcopy into page cache */ -int squashfs_readpage_block(struct page *page, u64 block, int bsize) -{ - struct inode *i = page->mapping->host; - struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb, - block, bsize); - int res = buffer->error; - - if (res) - ERROR("Unable to read page, block %llx, size %x\n", block, - bsize); - else - squashfs_copy_cache(page, buffer, buffer->length, 0); - - squashfs_cache_put(buffer); - return res; -} diff --git a/fs/squashfs/file_direct.c b/fs/squashfs/file_direct.c index 43e7a7eddac0..c97af4c6ccd0 100644 --- a/fs/squashfs/file_direct.c +++ b/fs/squashfs/file_direct.c @@ -13,6 +13,7 @@ #include <linux/string.h> #include <linux/pagemap.h> #include <linux/mutex.h> +#include <linux/mm_inline.h> #include "squashfs_fs.h" #include "squashfs_fs_sb.h" @@ -20,157 +21,139 @@ #include "squashfs.h" #include "page_actor.h" -static int squashfs_read_cache(struct page *target_page, u64 block, int bsize, - int pages, struct page **page); - -/* Read separately compressed datablock directly into page cache */ -int squashfs_readpage_block(struct page *target_page, u64 block, int bsize) +// Backported from 4.5 +#define lru_to_page(head) (list_entry((head)->prev, struct page, lru)) +static void release_actor_pages(struct page **page, int pages, int error) { - struct inode *inode = target_page->mapping->host; - struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; + int i; - int file_end = (i_size_read(inode) - 1) >> PAGE_CACHE_SHIFT; - int mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1; - int start_index = target_page->index & ~mask; - int end_index = start_index | mask; - int i, n, pages, missing_pages, bytes, res = -ENOMEM; + for (i = 0; i < pages; i++) { + if (!page[i]) + continue; + flush_dcache_page(page[i]); + if (!error) + SetPageUptodate(page[i]); + else { + SetPageError(page[i]); + zero_user_segment(page[i], 0, PAGE_CACHE_SIZE); + } + unlock_page(page[i]); + put_page(page[i]); + } + kfree(page); +} + +/* + * Create a "page actor" which will kmap and kunmap the + * page cache pages appropriately within the decompressor + */ +static struct squashfs_page_actor *actor_from_page_cache( + unsigned int actor_pages, struct page *target_page, + struct list_head *rpages, unsigned int *nr_pages, int start_index, + struct address_space *mapping) +{ struct page **page; struct squashfs_page_actor *actor; - void *pageaddr; - - if (end_index > file_end) - end_index = file_end; - - pages = end_index - start_index + 1; - - page = kmalloc_array(pages, sizeof(void *), GFP_KERNEL); - if (page == NULL) - return res; - - /* - * Create a "page actor" which will kmap and kunmap the - * page cache pages appropriately within the decompressor - */ - actor = squashfs_page_actor_init_special(page, pages, 0); - if (actor == NULL) - goto out; - - /* Try to grab all the pages covered by the Squashfs block */ - for (missing_pages = 0, i = 0, n = start_index; i < pages; i++, n++) { - page[i] = (n == target_page->index) ? target_page : - grab_cache_page_nowait(target_page->mapping, n); + int i, n; + gfp_t gfp = mapping_gfp_constraint(mapping, GFP_KERNEL); + + page = kmalloc_array(actor_pages, sizeof(void *), GFP_KERNEL); + if (!page) + return NULL; + + for (i = 0, n = start_index; i < actor_pages; i++, n++) { + if (target_page == NULL && rpages && !list_empty(rpages)) { + struct page *cur_page = lru_to_page(rpages); + + if (cur_page->index < start_index + actor_pages) { + list_del(&cur_page->lru); + --(*nr_pages); + if (add_to_page_cache_lru(cur_page, mapping, + cur_page->index, gfp)) + put_page(cur_page); + else + target_page = cur_page; + } else + rpages = NULL; + } - if (page[i] == NULL) { - missing_pages++; - continue; + if (target_page && target_page->index == n) { + page[i] = target_page; + target_page = NULL; + } else { + page[i] = grab_cache_page_nowait(mapping, n); + if (page[i] == NULL) + continue; } if (PageUptodate(page[i])) { unlock_page(page[i]); - page_cache_release(page[i]); + put_page(page[i]); page[i] = NULL; - missing_pages++; } } - if (missing_pages) { - /* - * Couldn't get one or more pages, this page has either - * been VM reclaimed, but others are still in the page cache - * and uptodate, or we're racing with another thread in - * squashfs_readpage also trying to grab them. Fall back to - * using an intermediate buffer. - */ - res = squashfs_read_cache(target_page, block, bsize, pages, - page); - if (res < 0) - goto mark_errored; - - goto out; + actor = squashfs_page_actor_init(page, actor_pages, 0, + release_actor_pages); + if (!actor) { + release_actor_pages(page, actor_pages, -ENOMEM); + kfree(page); + return NULL; } - - /* Decompress directly into the page cache buffers */ - res = squashfs_read_data(inode->i_sb, block, bsize, NULL, actor); - if (res < 0) - goto mark_errored; - - /* Last page may have trailing bytes not filled */ - bytes = res % PAGE_CACHE_SIZE; - if (bytes) { - pageaddr = kmap_atomic(page[pages - 1]); - memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); - kunmap_atomic(pageaddr); - } - - /* Mark pages as uptodate, unlock and release */ - for (i = 0; i < pages; i++) { - flush_dcache_page(page[i]); - SetPageUptodate(page[i]); - unlock_page(page[i]); - if (page[i] != target_page) - page_cache_release(page[i]); - } - - kfree(actor); - kfree(page); - - return 0; - -mark_errored: - /* Decompression failed, mark pages as errored. Target_page is - * dealt with by the caller - */ - for (i = 0; i < pages; i++) { - if (page[i] == NULL || page[i] == target_page) - continue; - flush_dcache_page(page[i]); - SetPageError(page[i]); - unlock_page(page[i]); - page_cache_release(page[i]); - } - -out: - kfree(actor); - kfree(page); - return res; + return actor; } +int squashfs_readpages_block(struct page *target_page, + struct list_head *readahead_pages, + unsigned int *nr_pages, + struct address_space *mapping, + int page_index, u64 block, int bsize) -static int squashfs_read_cache(struct page *target_page, u64 block, int bsize, - int pages, struct page **page) { - struct inode *i = target_page->mapping->host; - struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb, - block, bsize); - int bytes = buffer->length, res = buffer->error, n, offset = 0; - void *pageaddr; - - if (res) { - ERROR("Unable to read page, block %llx, size %x\n", block, - bsize); - goto out; - } - - for (n = 0; n < pages && bytes > 0; n++, - bytes -= PAGE_CACHE_SIZE, offset += PAGE_CACHE_SIZE) { - int avail = min_t(int, bytes, PAGE_CACHE_SIZE); - - if (page[n] == NULL) - continue; + struct squashfs_page_actor *actor; + struct inode *inode = mapping->host; + struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; + int start_index, end_index, file_end, actor_pages, res; + int mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1; - pageaddr = kmap_atomic(page[n]); - squashfs_copy_data(pageaddr, buffer, offset, avail); - memset(pageaddr + avail, 0, PAGE_CACHE_SIZE - avail); - kunmap_atomic(pageaddr); - flush_dcache_page(page[n]); - SetPageUptodate(page[n]); - unlock_page(page[n]); - if (page[n] != target_page) - page_cache_release(page[n]); + /* + * If readpage() is called on an uncompressed datablock, we can just + * read the pages instead of fetching the whole block. + * This greatly improves the performance when a process keep doing + * random reads because we only fetch the necessary data. + * The readahead algorithm will take care of doing speculative reads + * if necessary. + * We can't read more than 1 block even if readahead provides use more + * pages because we don't know yet if the next block is compressed or + * not. + */ + if (bsize && !SQUASHFS_COMPRESSED_BLOCK(bsize)) { + u64 block_end = block + msblk->block_size; + + block += (page_index & mask) * PAGE_CACHE_SIZE; + actor_pages = (block_end - block) / PAGE_CACHE_SIZE; + if (*nr_pages < actor_pages) + actor_pages = *nr_pages; + start_index = page_index; + bsize = min_t(int, bsize, (PAGE_CACHE_SIZE * actor_pages) + | SQUASHFS_COMPRESSED_BIT_BLOCK); + } else { + file_end = (i_size_read(inode) - 1) >> PAGE_CACHE_SHIFT; + start_index = page_index & ~mask; + end_index = start_index | mask; + if (end_index > file_end) + end_index = file_end; + actor_pages = end_index - start_index + 1; } -out: - squashfs_cache_put(buffer); - return res; + actor = actor_from_page_cache(actor_pages, target_page, + readahead_pages, nr_pages, start_index, + mapping); + if (!actor) + return -ENOMEM; + + res = squashfs_read_data_async(inode->i_sb, block, bsize, NULL, + actor); + return res < 0 ? res : 0; } diff --git a/fs/squashfs/lz4_wrapper.c b/fs/squashfs/lz4_wrapper.c index c31e2bc9c081..df4fa3c7ddd0 100644 --- a/fs/squashfs/lz4_wrapper.c +++ b/fs/squashfs/lz4_wrapper.c @@ -94,39 +94,17 @@ static int lz4_uncompress(struct squashfs_sb_info *msblk, void *strm, struct buffer_head **bh, int b, int offset, int length, struct squashfs_page_actor *output) { - struct squashfs_lz4 *stream = strm; - void *buff = stream->input, *data; - int avail, i, bytes = length, res; + int res; size_t dest_len = output->length; + struct squashfs_lz4 *stream = strm; - for (i = 0; i < b; i++) { - avail = min(bytes, msblk->devblksize - offset); - memcpy(buff, bh[i]->b_data + offset, avail); - buff += avail; - bytes -= avail; - offset = 0; - put_bh(bh[i]); - } - + squashfs_bh_to_buf(bh, b, stream->input, offset, length, + msblk->devblksize); res = lz4_decompress_unknownoutputsize(stream->input, length, stream->output, &dest_len); if (res) return -EIO; - - bytes = dest_len; - data = squashfs_first_page(output); - buff = stream->output; - while (data) { - if (bytes <= PAGE_CACHE_SIZE) { - memcpy(data, buff, bytes); - break; - } - memcpy(data, buff, PAGE_CACHE_SIZE); - buff += PAGE_CACHE_SIZE; - bytes -= PAGE_CACHE_SIZE; - data = squashfs_next_page(output); - } - squashfs_finish_page(output); + squashfs_buf_to_actor(stream->output, output, dest_len); return dest_len; } diff --git a/fs/squashfs/lzo_wrapper.c b/fs/squashfs/lzo_wrapper.c index 244b9fbfff7b..2c844d53a59e 100644 --- a/fs/squashfs/lzo_wrapper.c +++ b/fs/squashfs/lzo_wrapper.c @@ -79,45 +79,19 @@ static int lzo_uncompress(struct squashfs_sb_info *msblk, void *strm, struct buffer_head **bh, int b, int offset, int length, struct squashfs_page_actor *output) { - struct squashfs_lzo *stream = strm; - void *buff = stream->input, *data; - int avail, i, bytes = length, res; + int res; size_t out_len = output->length; + struct squashfs_lzo *stream = strm; - for (i = 0; i < b; i++) { - avail = min(bytes, msblk->devblksize - offset); - memcpy(buff, bh[i]->b_data + offset, avail); - buff += avail; - bytes -= avail; - offset = 0; - put_bh(bh[i]); - } - + squashfs_bh_to_buf(bh, b, stream->input, offset, length, + msblk->devblksize); res = lzo1x_decompress_safe(stream->input, (size_t)length, stream->output, &out_len); if (res != LZO_E_OK) - goto failed; + return -EIO; + squashfs_buf_to_actor(stream->output, output, out_len); - res = bytes = (int)out_len; - data = squashfs_first_page(output); - buff = stream->output; - while (data) { - if (bytes <= PAGE_CACHE_SIZE) { - memcpy(data, buff, bytes); - break; - } else { - memcpy(data, buff, PAGE_CACHE_SIZE); - buff += PAGE_CACHE_SIZE; - bytes -= PAGE_CACHE_SIZE; - data = squashfs_next_page(output); - } - } - squashfs_finish_page(output); - - return res; - -failed: - return -EIO; + return out_len; } const struct squashfs_decompressor squashfs_lzo_comp_ops = { diff --git a/fs/squashfs/page_actor.c b/fs/squashfs/page_actor.c index 5a1c11f56441..53863508e400 100644 --- a/fs/squashfs/page_actor.c +++ b/fs/squashfs/page_actor.c @@ -9,39 +9,11 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/pagemap.h> +#include <linux/buffer_head.h> #include "page_actor.h" -/* - * This file contains implementations of page_actor for decompressing into - * an intermediate buffer, and for decompressing directly into the - * page cache. - * - * Calling code should avoid sleeping between calls to squashfs_first_page() - * and squashfs_finish_page(). - */ - -/* Implementation of page_actor for decompressing into intermediate buffer */ -static void *cache_first_page(struct squashfs_page_actor *actor) -{ - actor->next_page = 1; - return actor->buffer[0]; -} - -static void *cache_next_page(struct squashfs_page_actor *actor) -{ - if (actor->next_page == actor->pages) - return NULL; - - return actor->buffer[actor->next_page++]; -} - -static void cache_finish_page(struct squashfs_page_actor *actor) -{ - /* empty */ -} - -struct squashfs_page_actor *squashfs_page_actor_init(void **buffer, - int pages, int length) +struct squashfs_page_actor *squashfs_page_actor_init(struct page **page, + int pages, int length, void (*release_pages)(struct page **, int, int)) { struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL); @@ -49,52 +21,133 @@ struct squashfs_page_actor *squashfs_page_actor_init(void **buffer, return NULL; actor->length = length ? : pages * PAGE_CACHE_SIZE; - actor->buffer = buffer; + actor->page = page; actor->pages = pages; actor->next_page = 0; - actor->squashfs_first_page = cache_first_page; - actor->squashfs_next_page = cache_next_page; - actor->squashfs_finish_page = cache_finish_page; + actor->pageaddr = NULL; + actor->release_pages = release_pages; return actor; } -/* Implementation of page_actor for decompressing directly into page cache. */ -static void *direct_first_page(struct squashfs_page_actor *actor) +void squashfs_page_actor_free(struct squashfs_page_actor *actor, int error) +{ + if (!actor) + return; + + if (actor->release_pages) + actor->release_pages(actor->page, actor->pages, error); + kfree(actor); +} + +void squashfs_actor_to_buf(struct squashfs_page_actor *actor, void *buf, + int length) { - actor->next_page = 1; - return actor->pageaddr = kmap_atomic(actor->page[0]); + void *pageaddr; + int pos = 0, avail, i; + + for (i = 0; i < actor->pages && pos < length; ++i) { + avail = min_t(int, length - pos, PAGE_CACHE_SIZE); + if (actor->page[i]) { + pageaddr = kmap_atomic(actor->page[i]); + memcpy(buf + pos, pageaddr, avail); + kunmap_atomic(pageaddr); + } + pos += avail; + } } -static void *direct_next_page(struct squashfs_page_actor *actor) +void squashfs_buf_to_actor(void *buf, struct squashfs_page_actor *actor, + int length) { - if (actor->pageaddr) - kunmap_atomic(actor->pageaddr); + void *pageaddr; + int pos = 0, avail, i; + + for (i = 0; i < actor->pages && pos < length; ++i) { + avail = min_t(int, length - pos, PAGE_CACHE_SIZE); + if (actor->page[i]) { + pageaddr = kmap_atomic(actor->page[i]); + memcpy(pageaddr, buf + pos, avail); + kunmap_atomic(pageaddr); + } + pos += avail; + } +} - return actor->pageaddr = actor->next_page == actor->pages ? NULL : - kmap_atomic(actor->page[actor->next_page++]); +void squashfs_bh_to_actor(struct buffer_head **bh, int nr_buffers, + struct squashfs_page_actor *actor, int offset, int length, int blksz) +{ + void *kaddr = NULL; + int bytes = 0, pgoff = 0, b = 0, p = 0, avail, i; + + while (bytes < length) { + if (actor->page[p]) { + kaddr = kmap_atomic(actor->page[p]); + while (pgoff < PAGE_CACHE_SIZE && bytes < length) { + avail = min_t(int, blksz - offset, + PAGE_CACHE_SIZE - pgoff); + memcpy(kaddr + pgoff, bh[b]->b_data + offset, + avail); + pgoff += avail; + bytes += avail; + offset = (offset + avail) % blksz; + if (!offset) { + put_bh(bh[b]); + ++b; + } + } + kunmap_atomic(kaddr); + pgoff = 0; + } else { + for (i = 0; i < PAGE_CACHE_SIZE / blksz; ++i) { + if (bh[b]) + put_bh(bh[b]); + ++b; + } + bytes += PAGE_CACHE_SIZE; + } + ++p; + } } -static void direct_finish_page(struct squashfs_page_actor *actor) +void squashfs_bh_to_buf(struct buffer_head **bh, int nr_buffers, void *buf, + int offset, int length, int blksz) { - if (actor->pageaddr) - kunmap_atomic(actor->pageaddr); + int i, avail, bytes = 0; + + for (i = 0; i < nr_buffers && bytes < length; ++i) { + avail = min_t(int, length - bytes, blksz - offset); + if (bh[i]) { + memcpy(buf + bytes, bh[i]->b_data + offset, avail); + put_bh(bh[i]); + } + bytes += avail; + offset = 0; + } } -struct squashfs_page_actor *squashfs_page_actor_init_special(struct page **page, - int pages, int length) +void free_page_array(struct page **page, int nr_pages) { - struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL); + int i; - if (actor == NULL) - return NULL; + for (i = 0; i < nr_pages; ++i) + __free_page(page[i]); + kfree(page); +} - actor->length = length ? : pages * PAGE_CACHE_SIZE; - actor->page = page; - actor->pages = pages; - actor->next_page = 0; - actor->pageaddr = NULL; - actor->squashfs_first_page = direct_first_page; - actor->squashfs_next_page = direct_next_page; - actor->squashfs_finish_page = direct_finish_page; - return actor; +struct page **alloc_page_array(int nr_pages, int gfp_mask) +{ + int i; + struct page **page; + + page = kcalloc(nr_pages, sizeof(struct page *), gfp_mask); + if (!page) + return NULL; + for (i = 0; i < nr_pages; ++i) { + page[i] = alloc_page(gfp_mask); + if (!page[i]) { + free_page_array(page, i); + return NULL; + } + } + return page; } diff --git a/fs/squashfs/page_actor.h b/fs/squashfs/page_actor.h index 26dd82008b82..aa1ed790b5a3 100644 --- a/fs/squashfs/page_actor.h +++ b/fs/squashfs/page_actor.h @@ -5,77 +5,61 @@ * Phillip Lougher <phillip@squashfs.org.uk> * * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. + * the COPYING file in the top-level squashfsory. */ -#ifndef CONFIG_SQUASHFS_FILE_DIRECT struct squashfs_page_actor { - void **page; + struct page **page; + void *pageaddr; int pages; int length; int next_page; + void (*release_pages)(struct page **, int, int); }; -static inline struct squashfs_page_actor *squashfs_page_actor_init(void **page, - int pages, int length) -{ - struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL); - - if (actor == NULL) - return NULL; +extern struct squashfs_page_actor *squashfs_page_actor_init(struct page **, + int, int, void (*)(struct page **, int, int)); +extern void squashfs_page_actor_free(struct squashfs_page_actor *, int); - actor->length = length ? : pages * PAGE_CACHE_SIZE; - actor->page = page; - actor->pages = pages; - actor->next_page = 0; - return actor; -} +extern void squashfs_actor_to_buf(struct squashfs_page_actor *, void *, int); +extern void squashfs_buf_to_actor(void *, struct squashfs_page_actor *, int); +extern void squashfs_bh_to_actor(struct buffer_head **, int, + struct squashfs_page_actor *, int, int, int); +extern void squashfs_bh_to_buf(struct buffer_head **, int, void *, int, int, + int); +/* + * Calling code should avoid sleeping between calls to squashfs_first_page() + * and squashfs_finish_page(). + */ static inline void *squashfs_first_page(struct squashfs_page_actor *actor) { actor->next_page = 1; - return actor->page[0]; + return actor->pageaddr = actor->page[0] ? kmap_atomic(actor->page[0]) + : NULL; } static inline void *squashfs_next_page(struct squashfs_page_actor *actor) { - return actor->next_page == actor->pages ? NULL : - actor->page[actor->next_page++]; -} + if (!IS_ERR_OR_NULL(actor->pageaddr)) + kunmap_atomic(actor->pageaddr); -static inline void squashfs_finish_page(struct squashfs_page_actor *actor) -{ - /* empty */ -} -#else -struct squashfs_page_actor { - union { - void **buffer; - struct page **page; - }; - void *pageaddr; - void *(*squashfs_first_page)(struct squashfs_page_actor *); - void *(*squashfs_next_page)(struct squashfs_page_actor *); - void (*squashfs_finish_page)(struct squashfs_page_actor *); - int pages; - int length; - int next_page; -}; + if (actor->next_page == actor->pages) + return actor->pageaddr = ERR_PTR(-ENODATA); -extern struct squashfs_page_actor *squashfs_page_actor_init(void **, int, int); -extern struct squashfs_page_actor *squashfs_page_actor_init_special(struct page - **, int, int); -static inline void *squashfs_first_page(struct squashfs_page_actor *actor) -{ - return actor->squashfs_first_page(actor); -} -static inline void *squashfs_next_page(struct squashfs_page_actor *actor) -{ - return actor->squashfs_next_page(actor); + actor->pageaddr = actor->page[actor->next_page] ? + kmap_atomic(actor->page[actor->next_page]) : NULL; + ++actor->next_page; + return actor->pageaddr; } + static inline void squashfs_finish_page(struct squashfs_page_actor *actor) { - actor->squashfs_finish_page(actor); + if (!IS_ERR_OR_NULL(actor->pageaddr)) + kunmap_atomic(actor->pageaddr); } -#endif + +extern struct page **alloc_page_array(int, int); +extern void free_page_array(struct page **, int); + #endif diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h index 887d6d270080..6093579c6c5d 100644 --- a/fs/squashfs/squashfs.h +++ b/fs/squashfs/squashfs.h @@ -28,8 +28,14 @@ #define WARNING(s, args...) pr_warn("SQUASHFS: "s, ## args) /* block.c */ +extern int squashfs_init_read_wq(void); +extern void squashfs_destroy_read_wq(void); extern int squashfs_read_data(struct super_block *, u64, int, u64 *, struct squashfs_page_actor *); +extern int squashfs_read_data(struct super_block *, u64, int, u64 *, + struct squashfs_page_actor *); +extern int squashfs_read_data_async(struct super_block *, u64, int, u64 *, + struct squashfs_page_actor *); /* cache.c */ extern struct squashfs_cache *squashfs_cache_init(char *, int, int); @@ -70,8 +76,9 @@ extern __le64 *squashfs_read_fragment_index_table(struct super_block *, void squashfs_copy_cache(struct page *, struct squashfs_cache_entry *, int, int); -/* file_xxx.c */ -extern int squashfs_readpage_block(struct page *, u64, int); +/* file_direct.c */ +extern int squashfs_readpages_block(struct page *, struct list_head *, + unsigned int *, struct address_space *, int, u64, int); /* id.c */ extern int squashfs_get_id(struct super_block *, unsigned int, unsigned int *); diff --git a/fs/squashfs/squashfs_fs_sb.h b/fs/squashfs/squashfs_fs_sb.h index 1da565cb50c3..8a6995de0277 100644 --- a/fs/squashfs/squashfs_fs_sb.h +++ b/fs/squashfs/squashfs_fs_sb.h @@ -49,7 +49,7 @@ struct squashfs_cache_entry { int num_waiters; wait_queue_head_t wait_queue; struct squashfs_cache *cache; - void **data; + struct page **page; struct squashfs_page_actor *actor; }; diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c index 5056babe00df..61cd0b39ed0e 100644 --- a/fs/squashfs/super.c +++ b/fs/squashfs/super.c @@ -444,9 +444,15 @@ static int __init init_squashfs_fs(void) if (err) return err; + if (!squashfs_init_read_wq()) { + destroy_inodecache(); + return -ENOMEM; + } + err = register_filesystem(&squashfs_fs_type); if (err) { destroy_inodecache(); + squashfs_destroy_read_wq(); return err; } @@ -460,6 +466,7 @@ static void __exit exit_squashfs_fs(void) { unregister_filesystem(&squashfs_fs_type); destroy_inodecache(); + squashfs_destroy_read_wq(); } diff --git a/fs/squashfs/xz_wrapper.c b/fs/squashfs/xz_wrapper.c index c609624e4b8a..14cd373e1897 100644 --- a/fs/squashfs/xz_wrapper.c +++ b/fs/squashfs/xz_wrapper.c @@ -55,7 +55,7 @@ static void *squashfs_xz_comp_opts(struct squashfs_sb_info *msblk, struct comp_opts *opts; int err = 0, n; - opts = kmalloc(sizeof(*opts), GFP_KERNEL); + opts = kmalloc(sizeof(*opts), GFP_ATOMIC); if (opts == NULL) { err = -ENOMEM; goto out2; @@ -136,6 +136,7 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm, enum xz_ret xz_err; int avail, total = 0, k = 0; struct squashfs_xz *stream = strm; + void *buf = NULL; xz_dec_reset(stream->state); stream->buf.in_pos = 0; @@ -156,12 +157,20 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm, if (stream->buf.out_pos == stream->buf.out_size) { stream->buf.out = squashfs_next_page(output); - if (stream->buf.out != NULL) { + if (!IS_ERR(stream->buf.out)) { stream->buf.out_pos = 0; total += PAGE_CACHE_SIZE; } } + if (!stream->buf.out) { + if (!buf) { + buf = kmalloc(PAGE_CACHE_SIZE, GFP_ATOMIC); + if (!buf) + goto out; + } + stream->buf.out = buf; + } xz_err = xz_dec_run(stream->state, &stream->buf); if (stream->buf.in_pos == stream->buf.in_size && k < b) @@ -173,11 +182,13 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm, if (xz_err != XZ_STREAM_END || k < b) goto out; + kfree(buf); return total + stream->buf.out_pos; out: for (; k < b; k++) put_bh(bh[k]); + kfree(buf); return -EIO; } diff --git a/fs/squashfs/zlib_wrapper.c b/fs/squashfs/zlib_wrapper.c index 8727caba6882..09c892b5308e 100644 --- a/fs/squashfs/zlib_wrapper.c +++ b/fs/squashfs/zlib_wrapper.c @@ -66,6 +66,7 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm, struct buffer_head **bh, int b, int offset, int length, struct squashfs_page_actor *output) { + void *buf = NULL; int zlib_err, zlib_init = 0, k = 0; z_stream *stream = strm; @@ -84,10 +85,19 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm, if (stream->avail_out == 0) { stream->next_out = squashfs_next_page(output); - if (stream->next_out != NULL) + if (!IS_ERR(stream->next_out)) stream->avail_out = PAGE_CACHE_SIZE; } + if (!stream->next_out) { + if (!buf) { + buf = kmalloc(PAGE_CACHE_SIZE, GFP_ATOMIC); + if (!buf) + goto out; + } + stream->next_out = buf; + } + if (!zlib_init) { zlib_err = zlib_inflateInit(stream); if (zlib_err != Z_OK) { @@ -115,11 +125,13 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm, if (k < b) goto out; + kfree(buf); return stream->total_out; out: for (; k < b; k++) put_bh(bh[k]); + kfree(buf); return -EIO; } diff --git a/fs/sync.c b/fs/sync.c index dd5d1711c7ac..452179e31c39 100644 --- a/fs/sync.c +++ b/fs/sync.c @@ -218,6 +218,7 @@ static int do_fsync(unsigned int fd, int datasync) if (f.file) { ret = vfs_fsync(f.file, datasync); fdput(f); + inc_syscfs(current); } return ret; } diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index b803213d1307..39c75a86c67f 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -108,7 +108,7 @@ static ssize_t sysfs_kf_read(struct kernfs_open_file *of, char *buf, { const struct sysfs_ops *ops = sysfs_file_ops(of->kn); struct kobject *kobj = of->kn->parent->priv; - size_t len; + ssize_t len; /* * If buf != of->prealloc_buf, we don't know how @@ -117,13 +117,15 @@ static ssize_t sysfs_kf_read(struct kernfs_open_file *of, char *buf, if (WARN_ON_ONCE(buf != of->prealloc_buf)) return 0; len = ops->show(kobj, of->kn->priv, buf); + if (len < 0) + return len; if (pos) { if (len <= pos) return 0; len -= pos; memmove(buf, buf + pos, len); } - return min(count, len); + return min_t(ssize_t, count, len); } /* kernfs write callback for regular sysfs files */ diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c index 1aabfda669b0..7183b7ea065b 100644 --- a/fs/xfs/libxfs/xfs_inode_buf.c +++ b/fs/xfs/libxfs/xfs_inode_buf.c @@ -299,6 +299,14 @@ xfs_dinode_verify( if (dip->di_magic != cpu_to_be16(XFS_DINODE_MAGIC)) return false; + /* don't allow invalid i_size */ + if (be64_to_cpu(dip->di_size) & (1ULL << 63)) + return false; + + /* No zero-length symlinks. */ + if (S_ISLNK(be16_to_cpu(dip->di_mode)) && dip->di_size == 0) + return false; + /* only version 3 or greater inodes are extensively verified here */ if (dip->di_version < 3) return true; diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c index dbae6490a79a..832764ee035a 100644 --- a/fs/xfs/xfs_bmap_util.c +++ b/fs/xfs/xfs_bmap_util.c @@ -1713,6 +1713,7 @@ xfs_swap_extents( xfs_trans_t *tp; xfs_bstat_t *sbp = &sxp->sx_stat; xfs_ifork_t *tempifp, *ifp, *tifp; + xfs_extnum_t nextents; int src_log_flags, target_log_flags; int error = 0; int aforkblks = 0; @@ -1899,7 +1900,8 @@ xfs_swap_extents( * pointer. Otherwise it's already NULL or * pointing to the extent. */ - if (ip->i_d.di_nextents <= XFS_INLINE_EXTS) { + nextents = ip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t); + if (nextents <= XFS_INLINE_EXTS) { ifp->if_u1.if_extents = ifp->if_u2.if_inline_ext; } @@ -1918,7 +1920,8 @@ xfs_swap_extents( * pointer. Otherwise it's already NULL or * pointing to the extent. */ - if (tip->i_d.di_nextents <= XFS_INLINE_EXTS) { + nextents = tip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t); + if (nextents <= XFS_INLINE_EXTS) { tifp->if_u1.if_extents = tifp->if_u2.if_inline_ext; } diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index eb1b8c8acfcb..8146b0cf20ce 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -375,6 +375,7 @@ retry: out_free_pages: for (i = 0; i < bp->b_page_count; i++) __free_page(bp->b_pages[i]); + bp->b_flags &= ~_XBF_PAGES; return error; } diff --git a/include/asm-generic/memory_model.h b/include/asm-generic/memory_model.h index 4b4b056a6eb0..5148150cc80b 100644 --- a/include/asm-generic/memory_model.h +++ b/include/asm-generic/memory_model.h @@ -1,6 +1,8 @@ #ifndef __ASM_MEMORY_MODEL_H #define __ASM_MEMORY_MODEL_H +#include <linux/pfn.h> + #ifndef __ASSEMBLY__ #if defined(CONFIG_FLATMEM) @@ -72,7 +74,7 @@ /* * Convert a physical address to a Page Frame Number and back */ -#define __phys_to_pfn(paddr) ((unsigned long)((paddr) >> PAGE_SHIFT)) +#define __phys_to_pfn(paddr) PHYS_PFN(paddr) #define __pfn_to_phys(pfn) PFN_PHYS(pfn) #define page_to_pfn __page_to_pfn diff --git a/include/crypto/algapi.h b/include/crypto/algapi.h index c9fe145f7dd3..04661e1fb625 100644 --- a/include/crypto/algapi.h +++ b/include/crypto/algapi.h @@ -202,6 +202,9 @@ int blkcipher_aead_walk_virt_block(struct blkcipher_desc *desc, struct blkcipher_walk *walk, struct crypto_aead *tfm, unsigned int blocksize); +int blkcipher_ablkcipher_walk_virt(struct blkcipher_desc *desc, + struct blkcipher_walk *walk, + struct crypto_ablkcipher *tfm); int ablkcipher_walk_done(struct ablkcipher_request *req, struct ablkcipher_walk *walk, int err); diff --git a/include/crypto/gf128mul.h b/include/crypto/gf128mul.h index da2530e34b26..230760aef93b 100644 --- a/include/crypto/gf128mul.h +++ b/include/crypto/gf128mul.h @@ -43,7 +43,7 @@ --------------------------------------------------------------------------- Issue Date: 31/01/2006 - An implementation of field multiplication in Galois Field GF(128) + An implementation of field multiplication in Galois Field GF(2^128) */ #ifndef _CRYPTO_GF128MUL_H @@ -65,7 +65,7 @@ * are left and the lsb's are right. char b[16] is an array and b[0] is * the first octet. * - * 80000000 00000000 00000000 00000000 .... 00000000 00000000 00000000 + * 10000000 00000000 00000000 00000000 .... 00000000 00000000 00000000 * b[0] b[1] b[2] b[3] b[13] b[14] b[15] * * Every bit is a coefficient of some power of X. We can store the bits @@ -99,21 +99,21 @@ * * bbe on a little endian machine u32 x[4]: * - * MS x[0] LS MS x[1] LS + * MS x[0] LS MS x[1] LS * ms ls ms ls ms ls ms ls ms ls ms ls ms ls ms ls * 103..96 111.104 119.112 127.120 71...64 79...72 87...80 95...88 * - * MS x[2] LS MS x[3] LS + * MS x[2] LS MS x[3] LS * ms ls ms ls ms ls ms ls ms ls ms ls ms ls ms ls * 39...32 47...40 55...48 63...56 07...00 15...08 23...16 31...24 * * ble on a little endian machine * - * MS x[0] LS MS x[1] LS + * MS x[0] LS MS x[1] LS * ms ls ms ls ms ls ms ls ms ls ms ls ms ls ms ls * 31...24 23...16 15...08 07...00 63...56 55...48 47...40 39...32 * - * MS x[2] LS MS x[3] LS + * MS x[2] LS MS x[3] LS * ms ls ms ls ms ls ms ls ms ls ms ls ms ls ms ls * 95...88 87...80 79...72 71...64 127.120 199.112 111.104 103..96 * @@ -127,7 +127,7 @@ * machines this will automatically aligned to wordsize and on a 64-bit * machine also. */ -/* Multiply a GF128 field element by x. Field elements are held in arrays +/* Multiply a GF128 field element by x. Field elements are held in arrays of bytes in which field bits 8n..8n + 7 are held in byte[n], with lower indexed bits placed in the more numerically significant bit positions within bytes. @@ -135,62 +135,65 @@ On little endian machines the bit indexes translate into the bit positions within four 32-bit words in the following way - MS x[0] LS MS x[1] LS + MS x[0] LS MS x[1] LS ms ls ms ls ms ls ms ls ms ls ms ls ms ls ms ls 24...31 16...23 08...15 00...07 56...63 48...55 40...47 32...39 - MS x[2] LS MS x[3] LS + MS x[2] LS MS x[3] LS ms ls ms ls ms ls ms ls ms ls ms ls ms ls ms ls 88...95 80...87 72...79 64...71 120.127 112.119 104.111 96..103 On big endian machines the bit indexes translate into the bit positions within four 32-bit words in the following way - MS x[0] LS MS x[1] LS + MS x[0] LS MS x[1] LS ms ls ms ls ms ls ms ls ms ls ms ls ms ls ms ls 00...07 08...15 16...23 24...31 32...39 40...47 48...55 56...63 - MS x[2] LS MS x[3] LS + MS x[2] LS MS x[3] LS ms ls ms ls ms ls ms ls ms ls ms ls ms ls ms ls 64...71 72...79 80...87 88...95 96..103 104.111 112.119 120.127 */ -/* A slow generic version of gf_mul, implemented for lle and bbe - * It multiplies a and b and puts the result in a */ +/* A slow generic version of gf_mul, implemented for lle, bbe, and ble. + * It multiplies a and b and puts the result in a + */ void gf128mul_lle(be128 *a, const be128 *b); - void gf128mul_bbe(be128 *a, const be128 *b); +void gf128mul_ble(be128 *a, const be128 *b); -/* multiply by x in ble format, needed by XTS */ +/* multiply by x in ble format, needed by XTS and HEH */ void gf128mul_x_ble(be128 *a, const be128 *b); /* 4k table optimization */ - struct gf128mul_4k { be128 t[256]; }; struct gf128mul_4k *gf128mul_init_4k_lle(const be128 *g); struct gf128mul_4k *gf128mul_init_4k_bbe(const be128 *g); +struct gf128mul_4k *gf128mul_init_4k_ble(const be128 *g); void gf128mul_4k_lle(be128 *a, struct gf128mul_4k *t); void gf128mul_4k_bbe(be128 *a, struct gf128mul_4k *t); +void gf128mul_4k_ble(be128 *a, struct gf128mul_4k *t); static inline void gf128mul_free_4k(struct gf128mul_4k *t) { - kfree(t); + kzfree(t); } -/* 64k table optimization, implemented for lle and bbe */ +/* 64k table optimization, implemented for lle, ble, and bbe */ struct gf128mul_64k { struct gf128mul_4k *t[16]; }; -/* first initialize with the constant factor with which you - * want to multiply and then call gf128_64k_lle with the other - * factor in the first argument, the table in the second and a - * scratch register in the third. Afterwards *a = *r. */ +/* First initialize with the constant factor with which you + * want to multiply and then call gf128mul_64k_bbe with the other + * factor in the first argument, and the table in the second. + * Afterwards, the result is stored in *a. + */ struct gf128mul_64k *gf128mul_init_64k_lle(const be128 *g); struct gf128mul_64k *gf128mul_init_64k_bbe(const be128 *g); void gf128mul_free_64k(struct gf128mul_64k *t); diff --git a/include/crypto/internal/hash.h b/include/crypto/internal/hash.h index 3b4af1d7c7e9..611b3d3bbab5 100644 --- a/include/crypto/internal/hash.h +++ b/include/crypto/internal/hash.h @@ -102,6 +102,8 @@ int shash_register_instance(struct crypto_template *tmpl, struct shash_instance *inst); void shash_free_instance(struct crypto_instance *inst); +int crypto_grab_shash(struct crypto_shash_spawn *spawn, + const char *name, u32 type, u32 mask); int crypto_init_shash_spawn(struct crypto_shash_spawn *spawn, struct shash_alg *alg, struct crypto_instance *inst); @@ -111,6 +113,12 @@ static inline void crypto_drop_shash(struct crypto_shash_spawn *spawn) crypto_drop_spawn(&spawn->base); } +static inline struct shash_alg *crypto_spawn_shash_alg( + struct crypto_shash_spawn *spawn) +{ + return container_of(spawn->base.alg, struct shash_alg, base); +} + struct shash_alg *shash_attr_alg(struct rtattr *rta, u32 type, u32 mask); int shash_ahash_update(struct ahash_request *req, struct shash_desc *desc); @@ -173,6 +181,16 @@ static inline struct ahash_instance *ahash_alloc_instance( return crypto_alloc_instance2(name, alg, ahash_instance_headroom()); } +static inline void ahash_request_complete(struct ahash_request *req, int err) +{ + req->base.complete(&req->base, err); +} + +static inline u32 ahash_request_flags(struct ahash_request *req) +{ + return req->base.flags; +} + static inline struct crypto_ahash *crypto_spawn_ahash( struct crypto_ahash_spawn *spawn) { diff --git a/include/drm/ttm/ttm_object.h b/include/drm/ttm/ttm_object.h index ed953f98f0e1..1487011fe057 100644 --- a/include/drm/ttm/ttm_object.h +++ b/include/drm/ttm/ttm_object.h @@ -229,6 +229,8 @@ extern void ttm_base_object_unref(struct ttm_base_object **p_base); * @ref_type: The type of reference. * @existed: Upon completion, indicates that an identical reference object * already existed, and the refcount was upped on that object instead. + * @require_existed: Fail with -EPERM if an identical ref object didn't + * already exist. * * Checks that the base object is shareable and adds a ref object to it. * @@ -243,7 +245,8 @@ extern void ttm_base_object_unref(struct ttm_base_object **p_base); */ extern int ttm_ref_object_add(struct ttm_object_file *tfile, struct ttm_base_object *base, - enum ttm_ref_type ref_type, bool *existed); + enum ttm_ref_type ref_type, bool *existed, + bool require_existed); extern bool ttm_ref_object_exists(struct ttm_object_file *tfile, struct ttm_base_object *base); diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index cb91b44f5f78..ad2bcf647b9a 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -528,6 +528,25 @@ static inline void pr_cont_cgroup_path(struct cgroup *cgrp) pr_cont_kernfs_path(cgrp->kn); } +static inline void cgroup_init_kthreadd(void) +{ + /* + * kthreadd is inherited by all kthreads, keep it in the root so + * that the new kthreads are guaranteed to stay in the root until + * initialization is finished. + */ + current->no_cgroup_migration = 1; +} + +static inline void cgroup_kthread_ready(void) +{ + /* + * This kthread finished initialization. The creator should have + * set PF_NO_SETAFFINITY if this kthread should stay in the root. + */ + current->no_cgroup_migration = 0; +} + #else /* !CONFIG_CGROUPS */ struct cgroup_subsys_state; @@ -551,6 +570,8 @@ static inline void cgroup_free(struct task_struct *p) {} static inline int cgroup_init_early(void) { return 0; } static inline int cgroup_init(void) { return 0; } +static inline void cgroup_init_kthreadd(void) {} +static inline void cgroup_kthread_ready(void) {} #endif /* !CONFIG_CGROUPS */ diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 702b6c53c12f..2043af2141c1 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -161,8 +161,8 @@ struct dentry_operations { struct vfsmount *(*d_automount)(struct path *); int (*d_manage)(struct dentry *, bool); struct inode *(*d_select_inode)(struct dentry *, unsigned); - void (*d_canonical_path)(const struct path *, struct path *); struct dentry *(*d_real)(struct dentry *, struct inode *); + void (*d_canonical_path)(const struct path *, struct path *); } ____cacheline_aligned; /* diff --git a/include/linux/dccp.h b/include/linux/dccp.h index 61d042bbbf60..68449293c4b6 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -163,6 +163,7 @@ struct dccp_request_sock { __u64 dreq_isr; __u64 dreq_gsr; __be32 dreq_service; + spinlock_t dreq_lock; struct list_head dreq_featneg; __u32 dreq_timestamp_echo; __u32 dreq_timestamp_time; diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index 5c720864db89..403a239b7aa1 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h @@ -415,6 +415,13 @@ union map_info *dm_get_rq_mapinfo(struct request *rq); struct queue_limits *dm_get_queue_limits(struct mapped_device *md); +void dm_lock_md_type(struct mapped_device *md); +void dm_unlock_md_type(struct mapped_device *md); +void dm_set_md_type(struct mapped_device *md, unsigned type); +unsigned dm_get_md_type(struct mapped_device *md); +int dm_setup_md_queue(struct mapped_device *md); +unsigned dm_table_get_type(struct dm_table *t); + /* * Geometry functions. */ diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index d8f4316167d8..6cfef2760e09 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1677,6 +1677,9 @@ enum ieee80211_statuscode { WLAN_STATUS_REJECT_DSE_BAND = 96, WLAN_STATUS_DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL = 99, WLAN_STATUS_DENIED_DUE_TO_SPECTRUM_MANAGEMENT = 103, + /* 802.11ai */ + WLAN_STATUS_FILS_AUTHENTICATION_FAILURE = 108, + WLAN_STATUS_UNKNOWN_AUTHENTICATION_SERVER = 109, }; @@ -2035,6 +2038,15 @@ enum ieee80211_key_len { #define IEEE80211_GCMP_MIC_LEN 16 #define IEEE80211_GCMP_PN_LEN 6 +#define FILS_NONCE_LEN 16 +#define FILS_MAX_KEK_LEN 64 + +#define FILS_ERP_MAX_USERNAME_LEN 16 +#define FILS_ERP_MAX_REALM_LEN 253 +#define FILS_ERP_MAX_RRK_LEN 64 + +#define PMK_MAX_LEN 48 + /* Public action codes */ enum ieee80211_pub_actioncode { WLAN_PUB_ACTION_EXT_CHANSW_ANN = 4, @@ -2248,31 +2260,37 @@ enum ieee80211_sa_query_action { }; +#define SUITE(oui, id) (((oui) << 8) | (id)) + /* cipher suite selectors */ -#define WLAN_CIPHER_SUITE_USE_GROUP 0x000FAC00 -#define WLAN_CIPHER_SUITE_WEP40 0x000FAC01 -#define WLAN_CIPHER_SUITE_TKIP 0x000FAC02 -/* reserved: 0x000FAC03 */ -#define WLAN_CIPHER_SUITE_CCMP 0x000FAC04 -#define WLAN_CIPHER_SUITE_WEP104 0x000FAC05 -#define WLAN_CIPHER_SUITE_AES_CMAC 0x000FAC06 -#define WLAN_CIPHER_SUITE_GCMP 0x000FAC08 -#define WLAN_CIPHER_SUITE_GCMP_256 0x000FAC09 -#define WLAN_CIPHER_SUITE_CCMP_256 0x000FAC0A -#define WLAN_CIPHER_SUITE_BIP_GMAC_128 0x000FAC0B -#define WLAN_CIPHER_SUITE_BIP_GMAC_256 0x000FAC0C -#define WLAN_CIPHER_SUITE_BIP_CMAC_256 0x000FAC0D - -#define WLAN_CIPHER_SUITE_SMS4 0x00147201 +#define WLAN_CIPHER_SUITE_USE_GROUP SUITE(0x000FAC, 0) +#define WLAN_CIPHER_SUITE_WEP40 SUITE(0x000FAC, 1) +#define WLAN_CIPHER_SUITE_TKIP SUITE(0x000FAC, 2) +/* reserved: SUITE(0x000FAC, 3) */ +#define WLAN_CIPHER_SUITE_CCMP SUITE(0x000FAC, 4) +#define WLAN_CIPHER_SUITE_WEP104 SUITE(0x000FAC, 5) +#define WLAN_CIPHER_SUITE_AES_CMAC SUITE(0x000FAC, 6) +#define WLAN_CIPHER_SUITE_GCMP SUITE(0x000FAC, 8) +#define WLAN_CIPHER_SUITE_GCMP_256 SUITE(0x000FAC, 9) +#define WLAN_CIPHER_SUITE_CCMP_256 SUITE(0x000FAC, 10) +#define WLAN_CIPHER_SUITE_BIP_GMAC_128 SUITE(0x000FAC, 11) +#define WLAN_CIPHER_SUITE_BIP_GMAC_256 SUITE(0x000FAC, 12) +#define WLAN_CIPHER_SUITE_BIP_CMAC_256 SUITE(0x000FAC, 13) + +#define WLAN_CIPHER_SUITE_SMS4 SUITE(0x001472, 1) /* AKM suite selectors */ -#define WLAN_AKM_SUITE_8021X 0x000FAC01 -#define WLAN_AKM_SUITE_PSK 0x000FAC02 -#define WLAN_AKM_SUITE_8021X_SHA256 0x000FAC05 -#define WLAN_AKM_SUITE_PSK_SHA256 0x000FAC06 -#define WLAN_AKM_SUITE_TDLS 0x000FAC07 -#define WLAN_AKM_SUITE_SAE 0x000FAC08 -#define WLAN_AKM_SUITE_FT_OVER_SAE 0x000FAC09 +#define WLAN_AKM_SUITE_8021X SUITE(0x000FAC, 1) +#define WLAN_AKM_SUITE_PSK SUITE(0x000FAC, 2) +#define WLAN_AKM_SUITE_8021X_SHA256 SUITE(0x000FAC, 5) +#define WLAN_AKM_SUITE_PSK_SHA256 SUITE(0x000FAC, 6) +#define WLAN_AKM_SUITE_TDLS SUITE(0x000FAC, 7) +#define WLAN_AKM_SUITE_SAE SUITE(0x000FAC, 8) +#define WLAN_AKM_SUITE_FT_OVER_SAE SUITE(0x000FAC, 9) +#define WLAN_AKM_SUITE_FILS_SHA256 SUITE(0x000FAC, 14) +#define WLAN_AKM_SUITE_FILS_SHA384 SUITE(0x000FAC, 15) +#define WLAN_AKM_SUITE_FT_FILS_SHA256 SUITE(0x000FAC, 16) +#define WLAN_AKM_SUITE_FT_FILS_SHA384 SUITE(0x000FAC, 17) #define WLAN_MAX_KEY_LEN 32 diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 55ae8b928411..2767fcdec5a1 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -235,6 +235,8 @@ struct iommu_ops { void (*tlbi_domain)(struct iommu_domain *domain); int (*enable_config_clocks)(struct iommu_domain *domain); void (*disable_config_clocks)(struct iommu_domain *domain); + uint64_t (*iova_to_pte)(struct iommu_domain *domain, + dma_addr_t iova); #ifdef CONFIG_OF_IOMMU int (*of_xlate)(struct device *dev, struct of_phandle_args *args); @@ -334,6 +336,9 @@ extern int iommu_domain_window_enable(struct iommu_domain *domain, u32 wnd_nr, phys_addr_t offset, u64 size, int prot); extern void iommu_domain_window_disable(struct iommu_domain *domain, u32 wnd_nr); + +extern uint64_t iommu_iova_to_pte(struct iommu_domain *domain, + dma_addr_t iova); /** * report_iommu_fault() - report about an IOMMU fault to the IOMMU framework * @domain: the iommu domain where the fault has happened diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 1129d68cb6cc..cc69810fcc32 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -18,6 +18,7 @@ struct ipv6_devconf { __s32 dad_transmits; __s32 rtr_solicits; __s32 rtr_solicit_interval; + __s32 rtr_solicit_max_interval; __s32 rtr_solicit_delay; __s32 force_mld_version; __s32 mldv1_unsolicited_report_interval; @@ -36,6 +37,7 @@ struct ipv6_devconf { __s32 accept_ra_rtr_pref; __s32 rtr_probe_interval; #ifdef CONFIG_IPV6_ROUTE_INFO + __s32 accept_ra_rt_info_min_plen; __s32 accept_ra_rt_info_max_plen; #endif #endif diff --git a/include/linux/kasan.h b/include/linux/kasan.h index 4b9f85c963d0..0fdc798e3ff7 100644 --- a/include/linux/kasan.h +++ b/include/linux/kasan.h @@ -1,6 +1,7 @@ #ifndef _LINUX_KASAN_H #define _LINUX_KASAN_H +#include <linux/sched.h> #include <linux/types.h> struct kmem_cache; @@ -13,7 +14,6 @@ struct vm_struct; #include <asm/kasan.h> #include <asm/pgtable.h> -#include <linux/sched.h> extern unsigned char kasan_zero_page[PAGE_SIZE]; extern pte_t kasan_zero_pte[PTRS_PER_PTE]; @@ -43,6 +43,8 @@ static inline void kasan_disable_current(void) void kasan_unpoison_shadow(const void *address, size_t size); +void kasan_unpoison_task_stack(struct task_struct *task); + void kasan_alloc_pages(struct page *page, unsigned int order); void kasan_free_pages(struct page *page, unsigned int order); @@ -66,6 +68,8 @@ void kasan_free_shadow(const struct vm_struct *vm); static inline void kasan_unpoison_shadow(const void *address, size_t size) {} +static inline void kasan_unpoison_task_stack(struct task_struct *task) {} + static inline void kasan_enable_current(void) {} static inline void kasan_disable_current(void) {} diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index c923350ca20a..d7ce4e3280db 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -182,8 +182,8 @@ int kvm_io_bus_read(struct kvm_vcpu *vcpu, enum kvm_bus bus_idx, gpa_t addr, int len, void *val); int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, int len, struct kvm_io_device *dev); -int kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx, - struct kvm_io_device *dev); +void kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx, + struct kvm_io_device *dev); #ifdef CONFIG_KVM_ASYNC_PF struct kvm_async_pf { diff --git a/include/linux/log2.h b/include/linux/log2.h index fd7ff3d91e6a..f38fae23bdac 100644 --- a/include/linux/log2.h +++ b/include/linux/log2.h @@ -16,12 +16,6 @@ #include <linux/bitops.h> /* - * deal with unrepresentable constant logarithms - */ -extern __attribute__((const, noreturn)) -int ____ilog2_NaN(void); - -/* * non-constant log of base 2 calculators * - the arch may override these in asm/bitops.h if they can be implemented * more efficiently than using fls() and fls64() @@ -85,7 +79,7 @@ unsigned long __rounddown_pow_of_two(unsigned long n) #define ilog2(n) \ ( \ __builtin_constant_p(n) ? ( \ - (n) < 1 ? ____ilog2_NaN() : \ + (n) < 2 ? 0 : \ (n) & (1ULL << 63) ? 63 : \ (n) & (1ULL << 62) ? 62 : \ (n) & (1ULL << 61) ? 61 : \ @@ -148,10 +142,7 @@ unsigned long __rounddown_pow_of_two(unsigned long n) (n) & (1ULL << 4) ? 4 : \ (n) & (1ULL << 3) ? 3 : \ (n) & (1ULL << 2) ? 2 : \ - (n) & (1ULL << 1) ? 1 : \ - (n) & (1ULL << 0) ? 0 : \ - ____ilog2_NaN() \ - ) : \ + 1 ) : \ (sizeof(n) <= 4) ? \ __ilog2_u32(n) : \ __ilog2_u64(n) \ diff --git a/include/linux/mbcache2.h b/include/linux/mbcache2.h new file mode 100644 index 000000000000..b6f160ff2533 --- /dev/null +++ b/include/linux/mbcache2.h @@ -0,0 +1,50 @@ +#ifndef _LINUX_MB2CACHE_H +#define _LINUX_MB2CACHE_H + +#include <linux/hash.h> +#include <linux/list_bl.h> +#include <linux/list.h> +#include <linux/atomic.h> +#include <linux/fs.h> + +struct mb2_cache; + +struct mb2_cache_entry { + /* LRU list - protected by cache->c_lru_list_lock */ + struct list_head e_lru_list; + /* Hash table list - protected by bitlock in e_hash_list_head */ + struct hlist_bl_node e_hash_list; + atomic_t e_refcnt; + /* Key in hash - stable during lifetime of the entry */ + u32 e_key; + /* Block number of hashed block - stable during lifetime of the entry */ + sector_t e_block; + /* Head of hash list (for list bit lock) - stable */ + struct hlist_bl_head *e_hash_list_head; +}; + +struct mb2_cache *mb2_cache_create(int bucket_bits); +void mb2_cache_destroy(struct mb2_cache *cache); + +int mb2_cache_entry_create(struct mb2_cache *cache, gfp_t mask, u32 key, + sector_t block); +void __mb2_cache_entry_free(struct mb2_cache_entry *entry); +static inline int mb2_cache_entry_put(struct mb2_cache *cache, + struct mb2_cache_entry *entry) +{ + if (!atomic_dec_and_test(&entry->e_refcnt)) + return 0; + __mb2_cache_entry_free(entry); + return 1; +} + +void mb2_cache_entry_delete_block(struct mb2_cache *cache, u32 key, + sector_t block); +struct mb2_cache_entry *mb2_cache_entry_find_first(struct mb2_cache *cache, + u32 key); +struct mb2_cache_entry *mb2_cache_entry_find_next(struct mb2_cache *cache, + struct mb2_cache_entry *entry); +void mb2_cache_entry_touch(struct mb2_cache *cache, + struct mb2_cache_entry *entry); + +#endif /* _LINUX_MB2CACHE_H */ diff --git a/include/linux/mdss_io_util.h b/include/linux/mdss_io_util.h index 5b2587b28737..3cca007e618c 100644 --- a/include/linux/mdss_io_util.h +++ b/include/linux/mdss_io_util.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012, 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012, 2016-2017, 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 @@ -58,6 +58,8 @@ struct dss_vreg { int post_on_sleep; int pre_off_sleep; int post_off_sleep; + bool lp_disable_allowed; + bool disabled; }; struct dss_gpio { diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 734169ff797e..e4937bbeae2c 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -523,6 +523,10 @@ struct mm_struct { #ifdef CONFIG_HUGETLB_PAGE atomic_long_t hugetlb_usage; #endif +#ifdef CONFIG_MSM_APP_SETTINGS + int app_setting; +#endif + }; static inline void mm_init_cpumask(struct mm_struct *mm) diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 31cc6f40baa5..8e76f46adde3 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -131,6 +131,9 @@ struct mmc_ext_csd { #define MMC_BKOPS_URGENCY_MASK 0x3 u8 raw_bkops_status; /* 246 */ u8 raw_sectors[4]; /* 212 - 4 bytes */ + u8 pre_eol_info; /* 267 */ + u8 device_life_time_est_typ_a; /* 268 */ + u8 device_life_time_est_typ_b; /* 269 */ u8 cmdq_depth; /* 307 */ u8 cmdq_support; /* 308 */ u8 barrier_support; /* 486 */ diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 9cd5afaef626..307a8e4be91f 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -282,6 +282,9 @@ struct _mmc_csd { #define EXT_CSD_CACHE_SIZE 249 /* RO, 4 bytes */ #define EXT_CSD_PWR_CL_DDR_200_360 253 /* RO */ #define EXT_CSD_FIRMWARE_VERSION 254 /* RO, 8 bytes */ +#define EXT_CSD_PRE_EOL_INFO 267 /* RO */ +#define EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_A 268 /* RO */ +#define EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_B 269 /* RO */ #define EXT_CSD_CMDQ_DEPTH 307 /* RO */ #define EXT_CSD_CMDQ_SUPPORT 308 /* RO */ #define EXT_CSD_BARRIER_SUPPORT 486 /* RO */ diff --git a/include/linux/mount.h b/include/linux/mount.h index 0e9b0977237a..b606d8f57adf 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -96,4 +96,6 @@ extern void mark_mounts_for_expiry(struct list_head *mounts); extern dev_t name_to_dev_t(const char *name); +extern unsigned int sysctl_mount_max; + #endif /* _LINUX_MOUNT_H */ diff --git a/include/linux/msm_ext_display.h b/include/linux/msm_ext_display.h index fc53e861eba4..9a03b79e09d7 100644 --- a/include/linux/msm_ext_display.h +++ b/include/linux/msm_ext_display.h @@ -114,6 +114,7 @@ struct msm_ext_disp_intf_ops { * @cable_status: cable connected/disconnected * @get_intf_id: id of connected interface * @acknowledge: acknowledge audio status + * @codec_ready: notify when codec is ready */ struct msm_ext_disp_audio_codec_ops { int (*audio_info_setup)(struct platform_device *pdev, @@ -124,6 +125,7 @@ struct msm_ext_disp_audio_codec_ops { int (*get_intf_id)(struct platform_device *pdev); void (*teardown_done)(struct platform_device *pdev); int (*acknowledge)(struct platform_device *pdev, u32 ack); + void (*codec_ready)(struct platform_device *pdev); }; /* diff --git a/include/linux/pfn.h b/include/linux/pfn.h index 7646637221f3..97f3e88aead4 100644 --- a/include/linux/pfn.h +++ b/include/linux/pfn.h @@ -9,5 +9,6 @@ #define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) #define PFN_DOWN(x) ((x) >> PAGE_SHIFT) #define PFN_PHYS(x) ((phys_addr_t)(x) << PAGE_SHIFT) +#define PHYS_PFN(x) ((unsigned long)((x) >> PAGE_SHIFT)) #endif diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 7488bb993d7a..aa8edf9928eb 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -247,6 +247,7 @@ enum power_supply_property { POWER_SUPPLY_PROP_CONNECTOR_HEALTH, POWER_SUPPLY_PROP_CTM_CURRENT_MAX, POWER_SUPPLY_PROP_HW_CURRENT_MAX, + POWER_SUPPLY_PROP_REAL_TYPE, /* Local extensions of type int64_t */ POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT, /* Properties of type `const char *' */ diff --git a/include/linux/regulator/onsemi-ncp6335d.h b/include/linux/regulator/onsemi-ncp6335d.h new file mode 100644 index 000000000000..399742f7e2ac --- /dev/null +++ b/include/linux/regulator/onsemi-ncp6335d.h @@ -0,0 +1,35 @@ +/* Copyright (c) 2012-2014, 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 __NCP6335D_H__ +#define __NCP6335D_H__ + +enum { + NCP6335D_VSEL0, + NCP6335D_VSEL1, +}; + +struct ncp6335d_platform_data { + struct regulator_init_data *init_data; + int default_vsel; + int slew_rate_ns; + int discharge_enable; + bool sleep_enable; +}; + +#ifdef CONFIG_REGULATOR_ONSEMI_NCP6335D +int __init ncp6335d_regulator_init(void); +#else +static inline int __init ncp6335d_regulator_init(void) { return 0; } +#endif + +#endif diff --git a/include/linux/sched.h b/include/linux/sched.h index 4b56a62a0a58..c71978453864 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1647,6 +1647,10 @@ struct task_struct { #ifdef CONFIG_COMPAT_BRK unsigned brk_randomized:1; #endif +#ifdef CONFIG_CGROUPS + /* disallow userland-initiated cgroup migration */ + unsigned no_cgroup_migration:1; +#endif unsigned long atomic_flags; /* Flags needing atomic access. */ @@ -3446,6 +3450,11 @@ static inline void inc_syscw(struct task_struct *tsk) { tsk->ioac.syscw++; } + +static inline void inc_syscfs(struct task_struct *tsk) +{ + tsk->ioac.syscfs++; +} #else static inline void add_rchar(struct task_struct *tsk, ssize_t amt) { @@ -3462,6 +3471,9 @@ static inline void inc_syscr(struct task_struct *tsk) static inline void inc_syscw(struct task_struct *tsk) { } +static inline void inc_syscfs(struct task_struct *tsk) +{ +} #endif #ifndef TASK_SIZE_OF diff --git a/include/linux/task_io_accounting.h b/include/linux/task_io_accounting.h index bdf855c2856f..2dd338fdf881 100644 --- a/include/linux/task_io_accounting.h +++ b/include/linux/task_io_accounting.h @@ -18,6 +18,8 @@ struct task_io_accounting { u64 syscr; /* # of write syscalls */ u64 syscw; + /* # of fsync syscalls */ + u64 syscfs; #endif /* CONFIG_TASK_XACCT */ #ifdef CONFIG_TASK_IO_ACCOUNTING diff --git a/include/linux/task_io_accounting_ops.h b/include/linux/task_io_accounting_ops.h index 4d090f9ee608..1b505c804af3 100644 --- a/include/linux/task_io_accounting_ops.h +++ b/include/linux/task_io_accounting_ops.h @@ -96,6 +96,7 @@ static inline void task_chr_io_accounting_add(struct task_io_accounting *dst, dst->wchar += src->wchar; dst->syscr += src->syscr; dst->syscw += src->syscw; + dst->syscfs += src->syscfs; } #else static inline void task_chr_io_accounting_add(struct task_io_accounting *dst, diff --git a/include/linux/usb.h b/include/linux/usb.h index 7e6e4665212f..a55f127d6836 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -371,14 +371,13 @@ struct usb_bus { int devnum_next; /* Next open device number in * round-robin allocation */ + struct mutex devnum_next_mutex; /* devnum_next mutex */ struct usb_devmap devmap; /* device address allocation map */ struct usb_device *root_hub; /* Root hub */ struct usb_bus *hs_companion; /* Companion EHCI bus, if any */ struct list_head bus_list; /* list of busses */ - struct mutex usb_address0_mutex; /* unaddressed device mutex */ - int bandwidth_allocated; /* on this bus: how much of the time * reserved for periodic (intr/iso) * requests is used, on average? diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index cef429cf3dce..e888eb9a2eb9 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -180,6 +180,7 @@ struct usb_hcd { * bandwidth_mutex should be dropped after a successful control message * to the device, or resetting the bandwidth after a failed attempt. */ + struct mutex *address0_mutex; struct mutex *bandwidth_mutex; struct usb_hcd *shared_hcd; struct usb_hcd *primary_hcd; diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h index 1d0043dc34e4..de2a722fe3cf 100644 --- a/include/linux/usb/quirks.h +++ b/include/linux/usb/quirks.h @@ -50,4 +50,10 @@ /* device can't handle Link Power Management */ #define USB_QUIRK_NO_LPM BIT(10) +/* + * Device reports its bInterval as linear frames instead of the + * USB 2.0 calculation. + */ +#define USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL BIT(11) + #endif /* __LINUX_USB_QUIRKS_H */ diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 3275ddf9f00d..d540657819ef 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -1,8 +1,9 @@ #ifndef _ADDRCONF_H #define _ADDRCONF_H -#define MAX_RTR_SOLICITATIONS 3 +#define MAX_RTR_SOLICITATIONS -1 /* unlimited */ #define RTR_SOLICITATION_INTERVAL (4*HZ) +#define RTR_SOLICITATION_MAX_INTERVAL (3600*HZ) /* 1 hour */ #define MIN_VALID_LIFETIME (2*3600) /* 2 hours */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index e9277e860bd7..3d11c7d26686 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -74,6 +74,11 @@ struct wiphy; #define CFG80211_CONNECT_TIMEOUT 1 #define CFG80211_CONNECT_TIMEOUT_REASON_CODE 1 +/* Indicate backport support for the new connect done api */ +#define CFG80211_CONNECT_DONE 1 +/* Indicate backport support for FILS SK offload in cfg80211 */ +#define CFG80211_FILS_SK_OFFLOAD_SUPPORT 1 + /* * wireless hardware capability structures */ @@ -1833,6 +1838,12 @@ enum cfg80211_assoc_req_flags { * @ht_capa_mask: The bits of ht_capa which are to be used. * @vht_capa: VHT capability override * @vht_capa_mask: VHT capability mask indicating which fields to use + * @fils_kek: FILS KEK for protecting (Re)Association Request/Response frame or + * %NULL if FILS is not used. + * @fils_kek_len: Length of fils_kek in octets + * @fils_nonces: FILS nonces (part of AAD) for protecting (Re)Association + * Request/Response frame or %NULL if FILS is not used. This field starts + * with 16 octets of STA Nonce followed by 16 octets of AP Nonce. */ struct cfg80211_assoc_request { struct cfg80211_bss *bss; @@ -1844,6 +1855,9 @@ struct cfg80211_assoc_request { struct ieee80211_ht_cap ht_capa; struct ieee80211_ht_cap ht_capa_mask; struct ieee80211_vht_cap vht_capa, vht_capa_mask; + const u8 *fils_kek; + size_t fils_kek_len; + const u8 *fils_nonces; }; /** @@ -1991,7 +2005,20 @@ struct cfg80211_bss_selection { * networks. * @bss_select: criteria to be used for BSS selection. * @prev_bssid: previous BSSID, if not %NULL use reassociate frame - */ + * @fils_erp_username: EAP re-authentication protocol (ERP) username part of the + * NAI or %NULL if not specified. This is used to construct FILS wrapped + * data IE. + * @fils_erp_username_len: Length of @fils_erp_username in octets. + * @fils_erp_realm: EAP re-authentication protocol (ERP) realm part of NAI or + * %NULL if not specified. This specifies the domain name of ER server and + * is used to construct FILS wrapped data IE. + * @fils_erp_realm_len: Length of @fils_erp_realm in octets. + * @fils_erp_next_seq_num: The next sequence number to use in the FILS ERP + * messages. This is also used to construct FILS wrapped data IE. + * @fils_erp_rrk: ERP re-authentication Root Key (rRK) used to derive additional + * keys in FILS or %NULL if not specified. + * @fils_erp_rrk_len: Length of @fils_erp_rrk in octets. +*/ struct cfg80211_connect_params { struct ieee80211_channel *channel; struct ieee80211_channel *channel_hint; @@ -2016,6 +2043,13 @@ struct cfg80211_connect_params { bool pbss; struct cfg80211_bss_selection bss_select; const u8 *prev_bssid; + const u8 *fils_erp_username; + size_t fils_erp_username_len; + const u8 *fils_erp_realm; + size_t fils_erp_realm_len; + u16 fils_erp_next_seq_num; + const u8 *fils_erp_rrk; + size_t fils_erp_rrk_len; }; /** @@ -2054,12 +2088,27 @@ enum wiphy_params_flags { * This structure is passed to the set/del_pmksa() method for PMKSA * caching. * - * @bssid: The AP's BSSID. - * @pmkid: The PMK material itself. + * @bssid: The AP's BSSID (may be %NULL). + * @pmkid: The identifier to refer a PMKSA. + * @pmk: The PMK for the PMKSA identified by @pmkid. This is used for key + * derivation by a FILS STA. Otherwise, %NULL. + * @pmk_len: Length of the @pmk. The length of @pmk can differ depending on + * the hash algorithm used to generate this. + * @ssid: SSID to specify the ESS within which a PMKSA is valid when using FILS + * cache identifier (may be %NULL). + * @ssid_len: Length of the @ssid in octets. + * @cache_id: 2-octet cache identifier advertized by a FILS AP identifying the + * scope of PMKSA. This is valid only if @ssid_len is non-zero (may be + * %NULL). */ struct cfg80211_pmksa { const u8 *bssid; const u8 *pmkid; + const u8 *pmk; + size_t pmk_len; + const u8 *ssid; + size_t ssid_len; + const u8 *cache_id; }; /** @@ -4819,6 +4868,78 @@ static inline void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp) #endif /** + * struct cfg80211_connect_resp_params - Connection response params + * @status: Status code, %WLAN_STATUS_SUCCESS for successful connection, use + * %WLAN_STATUS_UNSPECIFIED_FAILURE if your device cannot give you + * the real status code for failures. If this call is used to report a + * failure due to a timeout (e.g., not receiving an Authentication frame + * from the AP) instead of an explicit rejection by the AP, -1 is used to + * indicate that this is a failure, but without a status code. + * @timeout_reason is used to report the reason for the timeout in that + * case. + * @bssid: The BSSID of the AP (may be %NULL) + * @bss: Entry of bss to which STA got connected to, can be obtained through + * cfg80211_get_bss() (may be %NULL). Only one parameter among @bssid and + * @bss needs to be specified. + * @req_ie: Association request IEs (may be %NULL) + * @req_ie_len: Association request IEs length + * @resp_ie: Association response IEs (may be %NULL) + * @resp_ie_len: Association response IEs length + * @fils_kek: KEK derived from a successful FILS connection (may be %NULL) + * @fils_kek_len: Length of @fils_kek in octets + * @update_erp_next_seq_num: Boolean value to specify whether the value in + * @fils_erp_next_seq_num is valid. + * @fils_erp_next_seq_num: The next sequence number to use in ERP message in + * FILS Authentication. This value should be specified irrespective of the + * status for a FILS connection. + * @pmk: A new PMK if derived from a successful FILS connection (may be %NULL). + * @pmk_len: Length of @pmk in octets + * @pmkid: A new PMKID if derived from a successful FILS connection or the PMKID + * used for this FILS connection (may be %NULL). + * @timeout_reason: Reason for connection timeout. This is used when the + * connection fails due to a timeout instead of an explicit rejection from + * the AP. %NL80211_TIMEOUT_UNSPECIFIED is used when the timeout reason is + * not known. This value is used only if @status < 0 to indicate that the + * failure is due to a timeout and not due to explicit rejection by the AP. + * This value is ignored in other cases (@status >= 0). + */ +struct cfg80211_connect_resp_params { + int status; + const u8 *bssid; + struct cfg80211_bss *bss; + const u8 *req_ie; + size_t req_ie_len; + const u8 *resp_ie; + size_t resp_ie_len; + const u8 *fils_kek; + size_t fils_kek_len; + bool update_erp_next_seq_num; + u16 fils_erp_next_seq_num; + const u8 *pmk; + size_t pmk_len; + const u8 *pmkid; + enum nl80211_timeout_reason timeout_reason; +}; + +/** + * cfg80211_connect_done - notify cfg80211 of connection result + * + * @dev: network device + * @params: connection response parameters + * @gfp: allocation flags + * + * It should be called by the underlying driver once execution of the connection + * request from connect() has been completed. This is similar to + * cfg80211_connect_bss(), but takes a structure pointer for connection response + * parameters. Only one of the functions among cfg80211_connect_bss(), + * cfg80211_connect_result(), cfg80211_connect_timeout(), + * and cfg80211_connect_done() should be called. + */ +void cfg80211_connect_done(struct net_device *dev, + struct cfg80211_connect_resp_params *params, + gfp_t gfp); + +/** * cfg80211_connect_bss - notify cfg80211 of connection result * * @dev: network device @@ -4829,9 +4950,14 @@ static inline void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp) * @req_ie_len: association request IEs length * @resp_ie: association response IEs (may be %NULL) * @resp_ie_len: assoc response IEs length - * @status: status code, 0 for successful connection, use - * %WLAN_STATUS_UNSPECIFIED_FAILURE if your device cannot give you - * the real status code for failures. + * @status: status code, %WLAN_STATUS_SUCCESS for successful connection, use + * %WLAN_STATUS_UNSPECIFIED_FAILURE if your device cannot give you + * the real status code for failures. If this call is used to report a + * failure due to a timeout (e.g., not receiving an Authentication frame + * from the AP) instead of an explicit rejection by the AP, -1 is used to + * indicate that this is a failure, but without a status code. + * @timeout_reason is used to report the reason for the timeout in that + * case. * @gfp: allocation flags * @timeout_reason: reason for connection timeout. This is used when the * connection fails due to a timeout instead of an explicit rejection from @@ -4840,16 +4966,34 @@ static inline void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp) * failure is due to a timeout and not due to explicit rejection by the AP. * This value is ignored in other cases (@status >= 0). * - * It should be called by the underlying driver whenever connect() has - * succeeded. This is similar to cfg80211_connect_result(), but with the - * option of identifying the exact bss entry for the connection. Only one of - * these functions should be called. + * It should be called by the underlying driver once execution of the connection + * request from connect() has been completed. This is similar to + * cfg80211_connect_result(), but with the option of identifying the exact bss + * entry for the connection. Only one of the functions among + * cfg80211_connect_bss(), cfg80211_connect_result(), + * cfg80211_connect_timeout(), and cfg80211_connect_done() should be called. */ -void cfg80211_connect_bss(struct net_device *dev, const u8 *bssid, - struct cfg80211_bss *bss, const u8 *req_ie, - size_t req_ie_len, const u8 *resp_ie, - size_t resp_ie_len, int status, gfp_t gfp, - enum nl80211_timeout_reason timeout_reason); +static inline void +cfg80211_connect_bss(struct net_device *dev, const u8 *bssid, + struct cfg80211_bss *bss, const u8 *req_ie, + size_t req_ie_len, const u8 *resp_ie, + size_t resp_ie_len, int status, gfp_t gfp, + enum nl80211_timeout_reason timeout_reason) +{ + struct cfg80211_connect_resp_params params; + + memset(¶ms, 0, sizeof(params)); + params.status = status; + params.bssid = bssid; + params.bss = bss; + params.req_ie = req_ie; + params.req_ie_len = req_ie_len; + params.resp_ie = resp_ie; + params.resp_ie_len = resp_ie_len; + params.timeout_reason = timeout_reason; + + cfg80211_connect_done(dev, ¶ms, gfp); +} /** * cfg80211_connect_result - notify cfg80211 of connection result @@ -4860,13 +5004,16 @@ void cfg80211_connect_bss(struct net_device *dev, const u8 *bssid, * @req_ie_len: association request IEs length * @resp_ie: association response IEs (may be %NULL) * @resp_ie_len: assoc response IEs length - * @status: status code, 0 for successful connection, use + * @status: status code, %WLAN_STATUS_SUCCESS for successful connection, use * %WLAN_STATUS_UNSPECIFIED_FAILURE if your device cannot give you * the real status code for failures. * @gfp: allocation flags * - * It should be called by the underlying driver whenever connect() has - * succeeded. + * It should be called by the underlying driver once execution of the connection + * request from connect() has been completed. This is similar to + * cfg80211_connect_bss() which allows the exact bss entry to be specified. Only + * one of the functions among cfg80211_connect_bss(), cfg80211_connect_result(), + * cfg80211_connect_timeout(), and cfg80211_connect_done() should be called. */ static inline void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, @@ -4893,7 +5040,9 @@ cfg80211_connect_result(struct net_device *dev, const u8 *bssid, * in a sequence where no explicit authentication/association rejection was * received from the AP. This could happen, e.g., due to not being able to send * out the Authentication or Association Request frame or timing out while - * waiting for the response. + * waiting for the response. Only one of the functions among + * cfg80211_connect_bss(), cfg80211_connect_result(), + * cfg80211_connect_timeout(), and cfg80211_connect_done() should be called. */ static inline void cfg80211_connect_timeout(struct net_device *dev, const u8 *bssid, diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h index 1c8b6820b694..515352c6280a 100644 --- a/include/net/if_inet6.h +++ b/include/net/if_inet6.h @@ -201,6 +201,7 @@ struct inet6_dev { struct ipv6_devstat stats; struct timer_list rs_timer; + __s32 rs_interval; /* in jiffies */ __u8 rs_probes; __u8 addr_gen_mode; diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h index 4d1c46aac331..c7b1dc713cdd 100644 --- a/include/scsi/libiscsi.h +++ b/include/scsi/libiscsi.h @@ -196,6 +196,7 @@ struct iscsi_conn { struct iscsi_task *task; /* xmit task in progress */ /* xmit */ + spinlock_t taskqueuelock; /* protects the next three lists */ struct list_head mgmtqueue; /* mgmt (control) xmit queue */ struct list_head cmdqueue; /* data-path cmd queue */ struct list_head requeue; /* tasks needing another run */ diff --git a/include/soc/qcom/boot_stats.h b/include/soc/qcom/boot_stats.h index c81fc24ca8ca..b76d5676b555 100644 --- a/include/soc/qcom/boot_stats.h +++ b/include/soc/qcom/boot_stats.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2014,2016 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,7 +11,36 @@ */ #ifdef CONFIG_MSM_BOOT_STATS + +#define TIMER_KHZ 32768 +extern struct boot_stats __iomem *boot_stats; + +struct boot_stats { + uint32_t bootloader_start; + uint32_t bootloader_end; + uint32_t bootloader_display; + uint32_t bootloader_load_kernel; + uint32_t load_kernel_start; + uint32_t load_kernel_end; +#ifdef CONFIG_MSM_BOOT_TIME_MARKER + uint32_t bootloader_early_domain_start; + uint32_t bootloader_checksum; +#endif +}; + int boot_stats_init(void); +int boot_stats_exit(void); +unsigned long long int msm_timer_get_sclk_ticks(void); #else static inline int boot_stats_init(void) { return 0; } +unsigned long long int msm_timer_get_sclk_ticks(void) { return 0; } +#endif + +#ifdef CONFIG_MSM_BOOT_TIME_MARKER + +static inline int boot_marker_enabled(void) { return 1; } +void place_marker(const char *name); +#else +inline void place_marker(char *name); +static inline int boot_marker_enabled(void) { return 0; } #endif diff --git a/include/soc/qcom/minidump.h b/include/soc/qcom/minidump.h index 9d993a17fb89..2db61a40e2cc 100644 --- a/include/soc/qcom/minidump.h +++ b/include/soc/qcom/minidump.h @@ -37,7 +37,7 @@ struct md_region { */ #ifdef CONFIG_QCOM_MINIDUMP extern int msm_minidump_add_region(const struct md_region *entry); -extern bool msm_minidump_enabled(void); +extern bool minidump_enabled; #else static inline int msm_minidump_add_region(const struct md_region *entry) { diff --git a/include/soc/qcom/profiler.h b/include/soc/qcom/profiler.h index c53b510fc149..ffcf6ec91187 100644 --- a/include/soc/qcom/profiler.h +++ b/include/soc/qcom/profiler.h @@ -17,15 +17,15 @@ struct profiler_bw_cntrs_req { uint32_t total; - uint32_t gpu; uint32_t cpu; + uint32_t gpu; uint32_t cmd; }; struct compat_profiler_bw_cntrs_req { compat_uint_t total; - compat_uint_t gpu; compat_uint_t cpu; + compat_uint_t gpu; compat_uint_t cmd; }; diff --git a/include/soc/qcom/socinfo.h b/include/soc/qcom/socinfo.h index dfb0280d146c..2e8d71754c98 100644 --- a/include/soc/qcom/socinfo.h +++ b/include/soc/qcom/socinfo.h @@ -84,8 +84,6 @@ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msmtellurium") #define early_machine_is_msm8996() \ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msm8996") -#define early_machine_is_msm8996_auto() \ - of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msm8996-cdp") #define early_machine_is_msm8929() \ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msm8929") #define early_machine_is_msm8998() \ diff --git a/include/soc/qcom/subsystem_restart.h b/include/soc/qcom/subsystem_restart.h index 59749210001a..b08cc7ded26e 100644 --- a/include/soc/qcom/subsystem_restart.h +++ b/include/soc/qcom/subsystem_restart.h @@ -56,6 +56,8 @@ struct module; * @sysmon_shutdown_ret: Return value for the call to sysmon_send_shutdown * @system_debug: If "set", triggers a device restart when the * subsystem's wdog bite handler is invoked. + * @ignore_ssr_failure: SSR failures are usually fatal and results in panic. If + * set will ignore failure. * @edge: GLINK logical name of the subsystem */ struct subsys_desc { @@ -91,6 +93,7 @@ struct subsys_desc { u32 sysmon_pid; int sysmon_shutdown_ret; bool system_debug; + bool ignore_ssr_failure; const char *edge; }; diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h index bcb53faaf678..40cd9a752c53 100644 --- a/include/sound/apr_audio-v2.h +++ b/include/sound/apr_audio-v2.h @@ -15,6 +15,7 @@ #define _APR_AUDIO_V2_H_ #include <linux/qdsp6v2/apr.h> +#include <linux/msm_audio.h> /* size of header needed for passing data out of band */ #define APR_CMD_OB_HDR_SZ 12 @@ -447,6 +448,18 @@ struct adm_param_data_v5 { #define ASM_STREAM_PP_EVENT 0x00013214 #define DSP_STREAM_CMD "ADSP Stream Cmd" #define DSP_STREAM_CALLBACK "ADSP Stream Callback Event" +#define DSP_STREAM_CALLBACK_QUEUE_SIZE 1024 + +struct dsp_stream_callback_list { + struct list_head list; + struct msm_adsp_event_data event; +}; + +struct dsp_stream_callback_prtd { + uint16_t event_count; + struct list_head event_queue; + spinlock_t prtd_spin_lock; +}; /* set customized mixing on matrix mixer */ #define ADM_CMD_SET_PSPD_MTMX_STRTR_PARAMS_V5 0x00010344 @@ -10320,10 +10333,33 @@ struct asm_session_mtmx_strtr_param_clk_rec_t { u32 flags; } __packed; + +/* Parameter used by #ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC to + * realize smoother adjustment of audio session clock for a specified session. + * The desired audio session clock adjustment(in micro seconds) is specified + * using the command #ASM_SESSION_CMD_ADJUST_SESSION_CLOCK_V2. + * Delaying/Advancing the session clock would be implemented by inserting + * interpolated/dropping audio samples in the playback path respectively. + * Also, this parameter has to be configured before the Audio Session is put + * to RUN state to avoid cold start latency/glitches in the playback. + */ + +#define ASM_SESSION_MTMX_PARAM_ADJUST_SESSION_TIME_CTL 0x00013217 + +struct asm_session_mtmx_param_adjust_session_time_ctl_t { + /* Specifies whether the module is enabled or not + * @values + * 0 -- disabled + * 1 -- enabled + */ + u32 enable; +}; + union asm_session_mtmx_strtr_param_config { struct asm_session_mtmx_strtr_param_window_v2_t window_param; struct asm_session_mtmx_strtr_param_render_mode_t render_param; struct asm_session_mtmx_strtr_param_clk_rec_t clk_rec_param; + struct asm_session_mtmx_param_adjust_session_time_ctl_t adj_time_param; } __packed; struct asm_mtmx_strtr_params { diff --git a/include/sound/msm-dts-eagle.h b/include/sound/msm-dts-eagle.h deleted file mode 100644 index 2ef01136b7ce..000000000000 --- a/include/sound/msm-dts-eagle.h +++ /dev/null @@ -1,148 +0,0 @@ -/* Copyright (c) 2014, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef __MSM_DTS_EAGLE_H__ -#define __MSM_DTS_EAGLE_H__ - -#include <linux/compat.h> -#include <sound/soc.h> -#include <sound/devdep_params.h> -#include <sound/q6asm-v2.h> - -#ifdef CONFIG_COMPAT -enum { - DTS_EAGLE_IOCTL_GET_CACHE_SIZE32 = _IOR(0xF2, 0, __s32), - DTS_EAGLE_IOCTL_SET_CACHE_SIZE32 = _IOW(0xF2, 1, __s32), - DTS_EAGLE_IOCTL_GET_PARAM32 = _IOR(0xF2, 2, compat_uptr_t), - DTS_EAGLE_IOCTL_SET_PARAM32 = _IOW(0xF2, 3, compat_uptr_t), - DTS_EAGLE_IOCTL_SET_CACHE_BLOCK32 = - _IOW(0xF2, 4, compat_uptr_t), - DTS_EAGLE_IOCTL_SET_ACTIVE_DEVICE32 = - _IOW(0xF2, 5, compat_uptr_t), - DTS_EAGLE_IOCTL_GET_LICENSE32 = - _IOR(0xF2, 6, compat_uptr_t), - DTS_EAGLE_IOCTL_SET_LICENSE32 = - _IOW(0xF2, 7, compat_uptr_t), - DTS_EAGLE_IOCTL_SEND_LICENSE32 = _IOW(0xF2, 8, __s32), - DTS_EAGLE_IOCTL_SET_VOLUME_COMMANDS32 = _IOW(0xF2, 9, - compat_uptr_t), -}; -#endif - -#ifdef CONFIG_DTS_EAGLE -void msm_dts_ion_memmap(struct param_outband *po_); -int msm_dts_eagle_enable_asm(struct audio_client *ac, u32 enable, int module); -int msm_dts_eagle_enable_adm(int port_id, int copp_idx, u32 enable); -void msm_dts_eagle_add_controls(struct snd_soc_platform *platform); -int msm_dts_eagle_set_stream_gain(struct audio_client *ac, - int lgain, int rgain); -int msm_dts_eagle_handle_asm(struct dts_eagle_param_desc *depd, char *buf, - bool for_pre, bool get, struct audio_client *ac, - struct param_outband *po); -int msm_dts_eagle_handle_adm(struct dts_eagle_param_desc *depd, char *buf, - bool for_pre, bool get); -int msm_dts_eagle_ioctl(unsigned int cmd, unsigned long arg); -int msm_dts_eagle_is_hpx_on(void); -int msm_dts_eagle_init_pre(struct audio_client *ac); -int msm_dts_eagle_deinit_pre(struct audio_client *ac); -int msm_dts_eagle_init_post(int port_id, int copp_id); -int msm_dts_eagle_deinit_post(int port_id, int topology); -int msm_dts_eagle_init_master_module(struct audio_client *ac); -int msm_dts_eagle_deinit_master_module(struct audio_client *ac); -int msm_dts_eagle_pcm_new(struct snd_soc_pcm_runtime *runtime); -void msm_dts_eagle_pcm_free(struct snd_pcm *pcm); -int msm_dts_eagle_compat_ioctl(unsigned int cmd, unsigned long arg); -#else -static inline void msm_dts_ion_memmap(struct param_outband *po_) -{ - pr_debug("%s\n", __func__); -} -static inline int msm_dts_eagle_enable_asm(struct audio_client *ac, - u32 enable, int module) -{ - return 0; -} -static inline int msm_dts_eagle_enable_adm(int port_id, int copp_idx, - u32 enable) -{ - return 0; -} -static inline void msm_dts_eagle_add_controls(struct snd_soc_platform *platform) -{ -} -static inline int msm_dts_eagle_set_stream_gain(struct audio_client *ac, - int lgain, int rgain) -{ - pr_debug("%s\n", __func__); - return 0; -} -static inline int msm_dts_eagle_handle_asm(struct dts_eagle_param_desc *depd, - char *buf, bool for_pre, bool get, - struct audio_client *ac, - struct param_outband *po) -{ - return 0; -} -static inline int msm_dts_eagle_handle_adm(struct dts_eagle_param_desc *depd, - char *buf, bool for_pre, bool get) -{ - return 0; -} -static inline int msm_dts_eagle_ioctl(unsigned int cmd, unsigned long arg) -{ - return -EPERM; -} -static inline int msm_dts_eagle_is_hpx_on(void) -{ - return 0; -} -static inline int msm_dts_eagle_init_pre(struct audio_client *ac) -{ - return 0; -} -static inline int msm_dts_eagle_deinit_pre(struct audio_client *ac) -{ - return 0; -} -static inline int msm_dts_eagle_init_post(int port_id, int coppid) -{ - return 0; -} -static inline int msm_dts_eagle_deinit_post(int port_id, int topology) -{ - return 0; -} -static inline int msm_dts_eagle_init_master_module(struct audio_client *ac) -{ - return 0; -} -static inline int msm_dts_eagle_deinit_master_module(struct audio_client *ac) -{ - return 0; -} -static inline int msm_dts_eagle_pcm_new(struct snd_soc_pcm_runtime *runtime) -{ - pr_debug("%s\n", __func__); - return 0; -} -static inline void msm_dts_eagle_pcm_free(struct snd_pcm *pcm) -{ - pr_debug("%s\n", __func__); -} -static inline int msm_dts_eagle_compat_ioctl(unsigned int cmd, - unsigned long arg) -{ - return 0; -} -#endif - -#endif diff --git a/include/sound/q6adm-v2.h b/include/sound/q6adm-v2.h index f3443b79ba51..caabb66bd0b4 100644 --- a/include/sound/q6adm-v2.h +++ b/include/sound/q6adm-v2.h @@ -34,7 +34,6 @@ enum { ADM_AUDVOL_CAL, ADM_RTAC_INFO_CAL, ADM_RTAC_APR_CAL, - ADM_DTS_EAGLE, ADM_SRS_TRUMEDIA, ADM_RTAC_AUDVOL_CAL, ADM_MAX_CAL_TYPES diff --git a/include/sound/q6asm-v2.h b/include/sound/q6asm-v2.h index 41fcdeb50831..b2bb8d493839 100644 --- a/include/sound/q6asm-v2.h +++ b/include/sound/q6asm-v2.h @@ -655,6 +655,10 @@ int q6asm_send_mtmx_strtr_render_mode(struct audio_client *ac, int q6asm_send_mtmx_strtr_clk_rec_mode(struct audio_client *ac, uint32_t clk_rec_mode); +/* Enable adjust session clock in DSP */ +int q6asm_send_mtmx_strtr_enable_adjust_session_clock(struct audio_client *ac, + bool enable); + /* Retrieve the current DSP path delay */ int q6asm_get_path_delay(struct audio_client *ac); @@ -662,4 +666,8 @@ int q6asm_get_path_delay(struct audio_client *ac); uint8_t q6asm_get_buf_index_from_token(uint32_t token); uint8_t q6asm_get_stream_id_from_token(uint32_t token); +/* Adjust session clock in DSP */ +int q6asm_adjust_session_clock(struct audio_client *ac, + uint32_t adjust_time_lsw, + uint32_t adjust_time_msw); #endif /* __Q6_ASM_H__ */ diff --git a/include/trace/events/android_fs_template.h b/include/trace/events/android_fs_template.h index 4e61ffe7a814..b23d17b56c63 100644 --- a/include/trace/events/android_fs_template.h +++ b/include/trace/events/android_fs_template.h @@ -18,11 +18,18 @@ DECLARE_EVENT_CLASS(android_fs_data_start_template, ), TP_fast_assign( { + /* + * Replace the spaces in filenames and cmdlines + * because this screws up the tooling that parses + * the traces. + */ __assign_str(pathbuf, pathname); + (void)strreplace(__get_str(pathbuf), ' ', '_'); __entry->offset = offset; __entry->bytes = bytes; __entry->i_size = i_size_read(inode); __assign_str(cmdline, command); + (void)strreplace(__get_str(cmdline), ' ', '_'); __entry->pid = pid; __entry->ino = inode->i_ino; } diff --git a/include/trace/events/clk.h b/include/trace/events/clk.h index 758607226bfd..ad19e73ecb5d 100644 --- a/include/trace/events/clk.h +++ b/include/trace/events/clk.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2015, 2017, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -192,6 +192,42 @@ DEFINE_EVENT(clk_phase, clk_set_phase_complete, TP_ARGS(core, phase) ); +DECLARE_EVENT_CLASS(clk_state_dump, + + TP_PROTO(const char *name, unsigned int prepare_count, + unsigned int enable_count, unsigned long rate, unsigned int vdd_level), + + TP_ARGS(name, prepare_count, enable_count, rate, vdd_level), + + TP_STRUCT__entry( + __string(name, name) + __field(unsigned int, prepare_count) + __field(unsigned int, enable_count) + __field(unsigned long, rate) + __field(unsigned int, vdd_level) + ), + + TP_fast_assign( + __assign_str(name, name); + __entry->prepare_count = prepare_count; + __entry->enable_count = enable_count; + __entry->rate = rate; + __entry->vdd_level = vdd_level; + ), + + TP_printk("%s\tprepare:enable cnt [%u:%u]\trate: vdd_level [%lu:%u]", + __get_str(name), __entry->prepare_count, __entry->enable_count, + __entry->rate, __entry->vdd_level) +); + +DEFINE_EVENT(clk_state_dump, clk_state, + + TP_PROTO(const char *name, unsigned int prepare_count, + unsigned int enable_count, unsigned long rate, unsigned int vdd_level), + + TP_ARGS(name, prepare_count, enable_count, rate, vdd_level) +); + #endif /* _TRACE_CLK_H */ /* This part must be outside protection */ diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h index c974714a9abe..db65513ad656 100644 --- a/include/uapi/drm/msm_drm.h +++ b/include/uapi/drm/msm_drm.h @@ -323,6 +323,18 @@ struct drm_msm_counter_read { __u32 nr_ops; }; +#define MSM_GEM_SYNC_TO_DEV 0 +#define MSM_GEM_SYNC_TO_CPU 1 + +struct drm_msm_gem_syncop { + __u32 handle; + __u32 op; +}; + +struct drm_msm_gem_sync { + __u32 nr_ops; + __u64 __user ops; +}; #define DRM_MSM_GET_PARAM 0x00 /* placeholder: @@ -341,6 +353,7 @@ struct drm_msm_counter_read { #define DRM_MSM_COUNTER_GET 0x43 #define DRM_MSM_COUNTER_PUT 0x44 #define DRM_MSM_COUNTER_READ 0x45 +#define DRM_MSM_GEM_SYNC 0x46 /** * Currently DRM framework supports only VSYNC event. @@ -370,5 +383,6 @@ struct drm_msm_counter_read { #define DRM_IOCTL_MSM_COUNTER_READ \ DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_COUNTER_READ, \ struct drm_msm_counter_read) - +#define DRM_IOCTL_MSM_GEM_SYNC DRM_IOW(DRM_COMMAND_BASE + DRM_MSM_GEM_SYNC,\ + struct drm_msm_gem_sync) #endif /* __MSM_DRM_H__ */ diff --git a/include/uapi/linux/android/binder.h b/include/uapi/linux/android/binder.h index 51f891fb1b18..7668b5791c91 100644 --- a/include/uapi/linux/android/binder.h +++ b/include/uapi/linux/android/binder.h @@ -132,6 +132,7 @@ enum { /* struct binder_fd_array_object - object describing an array of fds in a buffer * @hdr: common header structure + * @pad: padding to ensure correct alignment * @num_fds: number of file descriptors in the buffer * @parent: index in offset array to buffer holding the fd array * @parent_offset: start offset of fd array in the buffer @@ -152,6 +153,7 @@ enum { */ struct binder_fd_array_object { struct binder_object_header hdr; + __u32 pad; binder_size_t num_fds; binder_size_t parent; binder_size_t parent_offset; diff --git a/include/uapi/linux/esoc_ctrl.h b/include/uapi/linux/esoc_ctrl.h index 57266ed29fb3..c0680f327073 100644 --- a/include/uapi/linux/esoc_ctrl.h +++ b/include/uapi/linux/esoc_ctrl.h @@ -7,6 +7,7 @@ #define ESOC_WAIT_FOR_REQ _IOR(ESOC_CODE, 2, unsigned int) #define ESOC_NOTIFY _IOW(ESOC_CODE, 3, unsigned int) #define ESOC_GET_STATUS _IOR(ESOC_CODE, 4, unsigned int) +#define ESOC_GET_ERR_FATAL _IOR(ESOC_CODE, 5, unsigned int) #define ESOC_WAIT_FOR_CRASH _IOR(ESOC_CODE, 6, unsigned int) #define ESOC_REG_REQ_ENG _IO(ESOC_CODE, 7) #define ESOC_REG_CMD_ENG _IO(ESOC_CODE, 8) @@ -15,6 +16,7 @@ #define HSIC "HSIC" #define HSICPCIe "HSIC+PCIe" #define PCIe "PCIe" +#define ESOC_REQ_SEND_SHUTDOWN ESOC_REQ_SEND_SHUTDOWN enum esoc_evt { ESOC_RUN_STATE = 0x1, @@ -55,6 +57,7 @@ enum esoc_req { ESOC_REQ_IMG = 1, ESOC_REQ_DEBUG, ESOC_REQ_SHUTDOWN, + ESOC_REQ_SEND_SHUTDOWN, }; #ifdef __KERNEL__ diff --git a/include/uapi/linux/hw_breakpoint.h b/include/uapi/linux/hw_breakpoint.h index b04000a2296a..2b65efd19a46 100644 --- a/include/uapi/linux/hw_breakpoint.h +++ b/include/uapi/linux/hw_breakpoint.h @@ -4,7 +4,11 @@ enum { HW_BREAKPOINT_LEN_1 = 1, HW_BREAKPOINT_LEN_2 = 2, + HW_BREAKPOINT_LEN_3 = 3, HW_BREAKPOINT_LEN_4 = 4, + HW_BREAKPOINT_LEN_5 = 5, + HW_BREAKPOINT_LEN_6 = 6, + HW_BREAKPOINT_LEN_7 = 7, HW_BREAKPOINT_LEN_8 = 8, }; diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h index 35bf6f1b186d..f0c24f24f0c3 100644 --- a/include/uapi/linux/ipv6.h +++ b/include/uapi/linux/ipv6.h @@ -176,6 +176,16 @@ enum { DEVCONF_ACCEPT_RA_MIN_HOP_LIMIT, DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN, DEVCONF_ACCEPT_RA_PREFIX_ROUTE, + DEVCONF_DROP_UNICAST_IN_L2_MULTICAST, + DEVCONF_DROP_UNSOLICITED_NA, + DEVCONF_KEEP_ADDR_ON_DOWN, + DEVCONF_RTR_SOLICIT_MAX_INTERVAL, + DEVCONF_SEG6_ENABLED, + DEVCONF_SEG6_REQUIRE_HMAC, + DEVCONF_ENHANCED_DAD, + DEVCONF_ADDR_GEN_MODE, + DEVCONF_DISABLE_POLICY, + DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN, DEVCONF_MAX }; diff --git a/include/uapi/linux/ipv6_route.h b/include/uapi/linux/ipv6_route.h index f6598d1c886e..316e838b7470 100644 --- a/include/uapi/linux/ipv6_route.h +++ b/include/uapi/linux/ipv6_route.h @@ -34,7 +34,7 @@ #define RTF_PREF(pref) ((pref) << 27) #define RTF_PREF_MASK 0x18000000 -#define RTF_PCPU 0x40000000 +#define RTF_PCPU 0x40000000 /* read-only: can not be set by user */ #define RTF_LOCAL 0x80000000 diff --git a/include/uapi/linux/msm_audio.h b/include/uapi/linux/msm_audio.h index 36b66c7cde76..f306949eb5e6 100644 --- a/include/uapi/linux/msm_audio.h +++ b/include/uapi/linux/msm_audio.h @@ -460,4 +460,14 @@ struct msm_hwacc_effects_config { __s32 topology; }; +#define ADSP_STREAM_PP_EVENT 0 +#define ADSP_STREAM_ENCDEC_EVENT 1 +#define ADSP_STREAM_EVENT_MAX 2 + +struct msm_adsp_event_data { + __u32 event_type; + __u32 payload_len; + __u8 payload[0]; +}; + #endif diff --git a/include/uapi/linux/msm_mdp_ext.h b/include/uapi/linux/msm_mdp_ext.h index 35029f227f8b..da9ee3bcc525 100644 --- a/include/uapi/linux/msm_mdp_ext.h +++ b/include/uapi/linux/msm_mdp_ext.h @@ -178,6 +178,12 @@ VALIDATE/COMMIT FLAG CONFIGURATION */ #define MDP_COMMIT_CWB_DSPP 0x1000 +/* + * Flag to indicate that rectangle number is being assigned + * by userspace in multi-rectangle mode + */ +#define MDP_COMMIT_RECT_NUM 0x2000 + #define MDP_COMMIT_VERSION_1_0 0x00010000 #define OUT_LAYER_COLOR_SPACE @@ -425,8 +431,14 @@ struct mdp_input_layer { */ int error_code; + /* + * For source pipes supporting multi-rectangle, this field identifies + * the rectangle index of the source pipe. + */ + uint32_t rect_num; + /* 32bits reserved value for future usage. */ - uint32_t reserved[6]; + uint32_t reserved[5]; }; struct mdp_output_layer { diff --git a/include/uapi/linux/netlink.h b/include/uapi/linux/netlink.h index ee4581d652f4..a9d1772199bf 100644 --- a/include/uapi/linux/netlink.h +++ b/include/uapi/linux/netlink.h @@ -107,8 +107,10 @@ struct nlmsgerr { #define NETLINK_PKTINFO 3 #define NETLINK_BROADCAST_ERROR 4 #define NETLINK_NO_ENOBUFS 5 +#ifndef __KERNEL__ #define NETLINK_RX_RING 6 #define NETLINK_TX_RING 7 +#endif #define NETLINK_LISTEN_ALL_NSID 8 #define NETLINK_LIST_MEMBERSHIPS 9 #define NETLINK_CAP_ACK 10 @@ -134,6 +136,7 @@ struct nl_mmap_hdr { __u32 nm_gid; }; +#ifndef __KERNEL__ enum nl_mmap_status { NL_MMAP_STATUS_UNUSED, NL_MMAP_STATUS_RESERVED, @@ -145,6 +148,7 @@ enum nl_mmap_status { #define NL_MMAP_MSG_ALIGNMENT NLMSG_ALIGNTO #define NL_MMAP_MSG_ALIGN(sz) __ALIGN_KERNEL(sz, NL_MMAP_MSG_ALIGNMENT) #define NL_MMAP_HDRLEN NL_MMAP_MSG_ALIGN(sizeof(struct nl_mmap_hdr)) +#endif #define NET_MAJOR 36 /* Major 36 is reserved for networking */ diff --git a/include/uapi/linux/netlink_diag.h b/include/uapi/linux/netlink_diag.h index f2159d30d1f5..d79399394b46 100644 --- a/include/uapi/linux/netlink_diag.h +++ b/include/uapi/linux/netlink_diag.h @@ -48,6 +48,8 @@ enum { #define NDIAG_SHOW_MEMINFO 0x00000001 /* show memory info of a socket */ #define NDIAG_SHOW_GROUPS 0x00000002 /* show groups of a netlink socket */ +#ifndef __KERNEL__ #define NDIAG_SHOW_RING_CFG 0x00000004 /* show ring configuration */ +#endif #endif diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 960aa84e3b61..3dd0091f4af3 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -172,6 +172,42 @@ */ /** + * DOC: FILS shared key authentication offload + * + * FILS shared key authentication offload can be advertized by drivers by + * setting @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD flag. The drivers that support + * FILS shared key authentication offload should be able to construct the + * authentication and association frames for FILS shared key authentication and + * eventually do a key derivation as per IEEE 802.11ai. The below additional + * parameters should be given to driver in %NL80211_CMD_CONNECT. + * %NL80211_ATTR_FILS_ERP_USERNAME - used to construct keyname_nai + * %NL80211_ATTR_FILS_ERP_REALM - used to construct keyname_nai + * %NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM - used to construct erp message + * %NL80211_ATTR_FILS_ERP_RRK - used to generate the rIK and rMSK + * rIK should be used to generate an authentication tag on the ERP message and + * rMSK should be used to derive a PMKSA. + * rIK, rMSK should be generated and keyname_nai, sequence number should be used + * as specified in IETF RFC 6696. + * + * When FILS shared key authentication is completed, driver needs to provide the + * below additional parameters to userspace. + * %NL80211_ATTR_FILS_KEK - used for key renewal + * %NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM - used in further EAP-RP exchanges + * %NL80211_ATTR_PMKID - used to identify the PMKSA used/generated + * %Nl80211_ATTR_PMK - used to update PMKSA cache in userspace + * The PMKSA can be maintained in userspace persistently so that it can be used + * later after reboots or wifi turn off/on also. + * + * %NL80211_ATTR_FILS_CACHE_ID is the cache identifier advertized by a FILS + * capable AP supporting PMK caching. It specifies the scope within which the + * PMKSAs are cached in an ESS. %NL80211_CMD_SET_PMKSA and + * %NL80211_CMD_DEL_PMKSA are enhanced to allow support for PMKSA caching based + * on FILS cache identifier. Additionally %NL80211_ATTR_PMK is used with + * %NL80211_SET_PMKSA to specify the PMK corresponding to a PMKSA for driver to + * use in a FILS shared key connection with PMKSA caching. + */ + +/** * enum nl80211_commands - supported nl80211 commands * * @NL80211_CMD_UNSPEC: unspecified command to catch errors @@ -369,10 +405,18 @@ * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to * NL80211_CMD_GET_SURVEY and on the "scan" multicast group) * - * @NL80211_CMD_SET_PMKSA: Add a PMKSA cache entry, using %NL80211_ATTR_MAC - * (for the BSSID) and %NL80211_ATTR_PMKID. + * @NL80211_CMD_SET_PMKSA: Add a PMKSA cache entry using %NL80211_ATTR_MAC + * (for the BSSID), %NL80211_ATTR_PMKID, and optionally %NL80211_ATTR_PMK + * (PMK is used for PTKSA derivation in case of FILS shared key offload) or + * using %NL80211_ATTR_SSID, %NL80211_ATTR_FILS_CACHE_ID, + * %NL80211_ATTR_PMKID, and %NL80211_ATTR_PMK in case of FILS + * authentication where %NL80211_ATTR_FILS_CACHE_ID is the identifier + * advertized by a FILS capable AP identifying the scope of PMKSA in an + * ESS. * @NL80211_CMD_DEL_PMKSA: Delete a PMKSA cache entry, using %NL80211_ATTR_MAC - * (for the BSSID) and %NL80211_ATTR_PMKID. + * (for the BSSID) and %NL80211_ATTR_PMKID or using %NL80211_ATTR_SSID, + * %NL80211_ATTR_FILS_CACHE_ID, and %NL80211_ATTR_PMKID in case of FILS + * authentication. * @NL80211_CMD_FLUSH_PMKSA: Flush all PMKSA cache entries. * * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain @@ -1965,6 +2009,31 @@ enum nl80211_commands { * u32 attribute with an &enum nl80211_timeout_reason value. This is used, * e.g., with %NL80211_CMD_CONNECT event. * + * @NL80211_ATTR_FILS_ERP_USERNAME: EAP Re-authentication Protocol (ERP) + * username part of NAI used to refer keys rRK and rIK. This is used with + * %NL80211_CMD_CONNECT. + * + * @NL80211_ATTR_FILS_ERP_REALM: EAP Re-authentication Protocol (ERP) realm part + * of NAI specifying the domain name of the ER server. This is used with + * %NL80211_CMD_CONNECT. + * + * @NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM: Unsigned 16-bit ERP next sequence number + * to use in ERP messages. This is used in generating the FILS wrapped data + * for FILS authentication and is used with %NL80211_CMD_CONNECT. + * + * @NL80211_ATTR_FILS_ERP_RRK: ERP re-authentication Root Key (rRK) for the + * NAI specified by %NL80211_ATTR_FILS_ERP_USERNAME and + * %NL80211_ATTR_FILS_ERP_REALM. This is used for generating rIK and rMSK + * from successful FILS authentication and is used with + * %NL80211_CMD_CONNECT. + * + * @NL80211_ATTR_FILS_CACHE_ID: A 2-octet identifier advertized by a FILS AP + * identifying the scope of PMKSAs. This is used with + * @NL80211_CMD_SET_PMKSA and @NL80211_CMD_DEL_PMKSA. + * + * @NL80211_ATTR_PMK: PMK for the PMKSA identified by %NL80211_ATTR_PMKID. + * This is used with @NL80211_CMD_SET_PMKSA. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -2376,6 +2445,14 @@ enum nl80211_attrs { NL80211_ATTR_TIMEOUT_REASON, + NL80211_ATTR_FILS_ERP_USERNAME, + NL80211_ATTR_FILS_ERP_REALM, + NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM, + NL80211_ATTR_FILS_ERP_RRK, + NL80211_ATTR_FILS_CACHE_ID, + + NL80211_ATTR_PMK, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -3856,7 +3933,10 @@ enum nl80211_ps_state { * @__NL80211_ATTR_CQM_INVALID: invalid * @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm. This value specifies * the threshold for the RSSI level at which an event will be sent. Zero - * to disable. + * to disable. Alternatively, if %NL80211_EXT_FEATURE_CQM_RSSI_LIST is + * set, multiple values can be supplied as a low-to-high sorted array of + * threshold values in dBm. Events will be sent when the RSSI value + * crosses any of the thresholds. * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm. This value specifies * the minimum amount the RSSI level must change after an event before a * new event may be issued (to reduce effects of RSSI oscillation). @@ -4664,6 +4744,11 @@ enum nl80211_feature_flags { * @NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI: The driver supports sched_scan * for reporting BSSs with better RSSI than the current connected BSS * (%NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI). + * @NL80211_EXT_FEATURE_CQM_RSSI_LIST: With this driver the + * %NL80211_ATTR_CQM_RSSI_THOLD attribute accepts a list of zero or more + * RSSI threshold values to monitor rather than exactly one threshold. + * @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD: Driver SME supports FILS shared key + * authentication with %NL80211_CMD_CONNECT. * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. @@ -4682,6 +4767,8 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA, NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED, NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI, + NL80211_EXT_FEATURE_CQM_RSSI_LIST, + NL80211_EXT_FEATURE_FILS_SK_OFFLOAD, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, diff --git a/include/uapi/linux/packet_diag.h b/include/uapi/linux/packet_diag.h index d08c63f3dd6f..0c5d5dd61b6a 100644 --- a/include/uapi/linux/packet_diag.h +++ b/include/uapi/linux/packet_diag.h @@ -64,7 +64,7 @@ struct packet_diag_mclist { __u32 pdmc_count; __u16 pdmc_type; __u16 pdmc_alen; - __u8 pdmc_addr[MAX_ADDR_LEN]; + __u8 pdmc_addr[32]; /* MAX_ADDR_LEN */ }; struct packet_diag_ring { diff --git a/include/uapi/linux/sysctl.h b/include/uapi/linux/sysctl.h index 33c6cde783ce..01eb22ca6b3d 100644 --- a/include/uapi/linux/sysctl.h +++ b/include/uapi/linux/sysctl.h @@ -571,6 +571,7 @@ enum { NET_IPV6_PROXY_NDP=23, NET_IPV6_ACCEPT_SOURCE_ROUTE=25, NET_IPV6_ACCEPT_RA_FROM_LOCAL=26, + NET_IPV6_ACCEPT_RA_RT_INFO_MIN_PLEN=27, __NET_IPV6_MAX }; diff --git a/include/uapi/media/msmb_ispif.h b/include/uapi/media/msmb_ispif.h index c3a6e006b2ff..d532037427cc 100644 --- a/include/uapi/media/msmb_ispif.h +++ b/include/uapi/media/msmb_ispif.h @@ -112,6 +112,7 @@ struct msm_ispif_param_data_ext { struct msm_ispif_pack_cfg pack_cfg[CID_MAX]; struct msm_ispif_right_param_entry right_entries[MAX_PARAM_ENTRIES]; uint32_t stereo_enable; + uint16_t line_width[VFE_MAX]; }; struct msm_ispif_param_data { @@ -143,6 +144,7 @@ enum ispif_cfg_type_t { ISPIF_ENABLE_REG_DUMP, ISPIF_SET_VFE_INFO, ISPIF_CFG2, + ISPIF_CFG_STEREO, }; struct ispif_cfg_data { @@ -165,6 +167,8 @@ struct ispif_cfg_data_ext { #define ISPIF_3D_SUPPORT 1 +#define ISPIF_LINE_WIDTH_SUPPORT 1 + #define VIDIOC_MSM_ISPIF_CFG \ _IOWR('V', BASE_VIDIOC_PRIVATE, struct ispif_cfg_data) diff --git a/include/uapi/sound/compress_offload.h b/include/uapi/sound/compress_offload.h index 30481056cce1..866ec3d2af69 100644 --- a/include/uapi/sound/compress_offload.h +++ b/include/uapi/sound/compress_offload.h @@ -149,6 +149,8 @@ struct snd_compr_audio_info { * @SNDRV_COMPRESS_CLK_REC_MODE: clock recovery mode ( none or auto) * @SNDRV_COMPRESS_RENDER_WINDOW: render window * @SNDRV_COMPRESS_START_DELAY: start delay + * @SNDRV_COMPRESS_ENABLE_ADJUST_SESSION_CLOCK: enable dsp drift correction + * @SNDRV_COMPRESS_ADJUST_SESSION_CLOCK: set drift correction value */ enum sndrv_compress_encoder { SNDRV_COMPRESS_ENCODER_PADDING = 1, @@ -160,6 +162,8 @@ enum sndrv_compress_encoder { SNDRV_COMPRESS_CLK_REC_MODE = 7, SNDRV_COMPRESS_RENDER_WINDOW = 8, SNDRV_COMPRESS_START_DELAY = 9, + SNDRV_COMPRESS_ENABLE_ADJUST_SESSION_CLOCK = 10, + SNDRV_COMPRESS_ADJUST_SESSION_CLOCK = 11, }; #define SNDRV_COMPRESS_PATH_DELAY SNDRV_COMPRESS_PATH_DELAY @@ -167,6 +171,9 @@ enum sndrv_compress_encoder { #define SNDRV_COMPRESS_CLK_REC_MODE SNDRV_COMPRESS_CLK_REC_MODE #define SNDRV_COMPRESS_RENDER_WINDOW SNDRV_COMPRESS_RENDER_WINDOW #define SNDRV_COMPRESS_START_DELAY SNDRV_COMPRESS_START_DELAY +#define SNDRV_COMPRESS_ENABLE_ADJUST_SESSION_CLOCK \ + SNDRV_COMPRESS_ENABLE_ADJUST_SESSION_CLOCK +#define SNDRV_COMPRESS_ADJUST_SESSION_CLOCK SNDRV_COMPRESS_ADJUST_SESSION_CLOCK /** * struct snd_compr_metadata - compressed stream metadata diff --git a/init/do_mounts_dm.c b/init/do_mounts_dm.c index ecda58df9a19..bce1c2fbb915 100644 --- a/init/do_mounts_dm.c +++ b/init/do_mounts_dm.c @@ -5,13 +5,17 @@ * * This file is released under the GPL. */ +#include <linux/async.h> +#include <linux/ctype.h> #include <linux/device-mapper.h> #include <linux/fs.h> #include <linux/string.h> +#include <linux/delay.h> #include "do_mounts.h" -#include "../drivers/md/dm.h" +#define DM_MAX_DEVICES 256 +#define DM_MAX_TARGETS 256 #define DM_MAX_NAME 32 #define DM_MAX_UUID 129 #define DM_NO_UUID "none" @@ -19,14 +23,47 @@ #define DM_MSG_PREFIX "init" /* Separators used for parsing the dm= argument. */ -#define DM_FIELD_SEP ' ' -#define DM_LINE_SEP ',' +#define DM_FIELD_SEP " " +#define DM_LINE_SEP "," +#define DM_ANY_SEP DM_FIELD_SEP DM_LINE_SEP /* * When the device-mapper and any targets are compiled into the kernel - * (not a module), one target may be created and used as the root device at - * boot time with the parameters given with the boot line dm=... - * The code for that is here. + * (not a module), one or more device-mappers may be created and used + * as the root device at boot time with the parameters given with the + * boot line dm=... + * + * Multiple device-mappers can be stacked specifing the number of + * devices. A device can have multiple targets if the the number of + * targets is specified. + * + * TODO(taysom:defect 32847) + * In the future, the <num> field will be mandatory. + * + * <device> ::= [<num>] <device-mapper>+ + * <device-mapper> ::= <head> "," <target>+ + * <head> ::= <name> <uuid> <mode> [<num>] + * <target> ::= <start> <length> <type> <options> "," + * <mode> ::= "ro" | "rw" + * <uuid> ::= xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | "none" + * <type> ::= "verity" | "bootcache" | ... + * + * Example: + * 2 vboot none ro 1, + * 0 1768000 bootcache + * device=aa55b119-2a47-8c45-946a-5ac57765011f+1 + * signature=76e9be054b15884a9fa85973e9cb274c93afadb6 + * cache_start=1768000 max_blocks=100000 size_limit=23 max_trace=20000, + * vroot none ro 1, + * 0 1740800 verity payload=254:0 hashtree=254:0 hashstart=1740800 alg=sha1 + * root_hexdigest=76e9be054b15884a9fa85973e9cb274c93afadb6 + * salt=5b3549d54d6c7a3837b9b81ed72e49463a64c03680c47835bef94d768e5646fe + * + * Notes: + * 1. uuid is a label for the device and we set it to "none". + * 2. The <num> field will be optional initially and assumed to be 1. + * Once all the scripts that set these fields have been set, it will + * be made mandatory. */ struct dm_setup_target { @@ -38,381 +75,388 @@ struct dm_setup_target { struct dm_setup_target *next; }; -static struct { +struct dm_device { int minor; int ro; char name[DM_MAX_NAME]; char uuid[DM_MAX_UUID]; - char *targets; + unsigned long num_targets; struct dm_setup_target *target; int target_count; + struct dm_device *next; +}; + +struct dm_option { + char *start; + char *next; + size_t len; + char delim; +}; + +static struct { + unsigned long num_devices; + char *str; } dm_setup_args __initdata; static __initdata int dm_early_setup; -static size_t __init get_dm_option(char *str, char **next, char sep) +static int __init get_dm_option(struct dm_option *opt, const char *accept) { - size_t len = 0; - char *endp = NULL; + char *str = opt->next; + char *endp; if (!str) return 0; - endp = strchr(str, sep); + str = skip_spaces(str); + opt->start = str; + endp = strpbrk(str, accept); if (!endp) { /* act like strchrnul */ - len = strlen(str); - endp = str + len; + opt->len = strlen(str); + endp = str + opt->len; } else { - len = endp - str; + opt->len = endp - str; } - - if (endp == str) - return 0; - - if (!next) - return len; - + opt->delim = *endp; if (*endp == 0) { /* Don't advance past the nul. */ - *next = endp; + opt->next = endp; } else { - *next = endp + 1; + opt->next = endp + 1; } - return len; -} - -static int __init dm_setup_args_init(void) -{ - dm_setup_args.minor = 0; - dm_setup_args.ro = 0; - dm_setup_args.target = NULL; - dm_setup_args.target_count = 0; - return 0; + return opt->len != 0; } -static int __init dm_setup_cleanup(void) +static int __init dm_setup_cleanup(struct dm_device *devices) { - struct dm_setup_target *target = dm_setup_args.target; - struct dm_setup_target *old_target = NULL; - while (target) { - kfree(target->type); - kfree(target->params); - old_target = target; - target = target->next; - kfree(old_target); - dm_setup_args.target_count--; + struct dm_device *dev = devices; + + while (dev) { + struct dm_device *old_dev = dev; + struct dm_setup_target *target = dev->target; + while (target) { + struct dm_setup_target *old_target = target; + kfree(target->type); + kfree(target->params); + target = target->next; + kfree(old_target); + dev->target_count--; + } + BUG_ON(dev->target_count); + dev = dev->next; + kfree(old_dev); } - BUG_ON(dm_setup_args.target_count); return 0; } -static char * __init dm_setup_parse_device_args(char *str) +static char * __init dm_parse_device(struct dm_device *dev, char *str) { - char *next = NULL; - size_t len = 0; + struct dm_option opt; + size_t len; /* Grab the logical name of the device to be exported to udev */ - len = get_dm_option(str, &next, DM_FIELD_SEP); - if (!len) { + opt.next = str; + if (!get_dm_option(&opt, DM_FIELD_SEP)) { DMERR("failed to parse device name"); goto parse_fail; } - len = min(len + 1, sizeof(dm_setup_args.name)); - strlcpy(dm_setup_args.name, str, len); /* includes nul */ - str = skip_spaces(next); + len = min(opt.len + 1, sizeof(dev->name)); + strlcpy(dev->name, opt.start, len); /* includes nul */ /* Grab the UUID value or "none" */ - len = get_dm_option(str, &next, DM_FIELD_SEP); - if (!len) { + if (!get_dm_option(&opt, DM_FIELD_SEP)) { DMERR("failed to parse device uuid"); goto parse_fail; } - len = min(len + 1, sizeof(dm_setup_args.uuid)); - strlcpy(dm_setup_args.uuid, str, len); - str = skip_spaces(next); + len = min(opt.len + 1, sizeof(dev->uuid)); + strlcpy(dev->uuid, opt.start, len); /* Determine if the table/device will be read only or read-write */ - if (!strncmp("ro,", str, 3)) { - dm_setup_args.ro = 1; - } else if (!strncmp("rw,", str, 3)) { - dm_setup_args.ro = 0; + get_dm_option(&opt, DM_ANY_SEP); + if (!strncmp("ro", opt.start, opt.len)) { + dev->ro = 1; + } else if (!strncmp("rw", opt.start, opt.len)) { + dev->ro = 0; } else { DMERR("failed to parse table mode"); goto parse_fail; } - str = skip_spaces(str + 3); - return str; + /* Optional number field */ + /* XXX: The <num> field will be mandatory in the next round */ + if (opt.delim == DM_FIELD_SEP[0]) { + if (!get_dm_option(&opt, DM_LINE_SEP)) + return NULL; + dev->num_targets = simple_strtoul(opt.start, NULL, 10); + } else { + dev->num_targets = 1; + } + if (dev->num_targets > DM_MAX_TARGETS) { + DMERR("too many targets %lu > %d", + dev->num_targets, DM_MAX_TARGETS); + } + return opt.next; parse_fail: return NULL; } -static void __init dm_substitute_devices(char *str, size_t str_len) +static char * __init dm_parse_targets(struct dm_device *dev, char *str) { - char *candidate = str; - char *candidate_end = str; - char old_char; - size_t len = 0; - dev_t dev; - - if (str_len < 3) - return; - - while (str && *str) { - candidate = strchr(str, '/'); - if (!candidate) - break; - - /* Avoid embedded slashes */ - if (candidate != str && *(candidate - 1) != DM_FIELD_SEP) { - str = strchr(candidate, DM_FIELD_SEP); - continue; - } - - len = get_dm_option(candidate, &candidate_end, DM_FIELD_SEP); - str = skip_spaces(candidate_end); - if (len < 3 || len > 37) /* name_to_dev_t max; maj:mix min */ - continue; - - /* Temporarily terminate with a nul */ - if (*candidate_end) - candidate_end--; - old_char = *candidate_end; - *candidate_end = '\0'; - - DMDEBUG("converting candidate device '%s' to dev_t", candidate); - /* Use the boot-time specific device naming */ - dev = name_to_dev_t(candidate); - *candidate_end = old_char; - - DMDEBUG(" -> %u", dev); - /* No suitable replacement found */ - if (!dev) - continue; - - /* Rewrite the /dev/path as a major:minor */ - len = snprintf(candidate, len, "%u:%u", MAJOR(dev), MINOR(dev)); - if (!len) { - DMERR("error substituting device major/minor."); - break; - } - candidate += len; - /* Pad out with spaces (fixing our nul) */ - while (candidate < candidate_end) - *(candidate++) = DM_FIELD_SEP; - } -} - -static int __init dm_setup_parse_targets(char *str) -{ - char *next = NULL; - size_t len = 0; - struct dm_setup_target **target = NULL; + struct dm_option opt; + struct dm_setup_target **target = &dev->target; + unsigned long num_targets = dev->num_targets; + unsigned long i; /* Targets are defined as per the table format but with a * comma as a newline separator. */ - target = &dm_setup_args.target; - while (str && *str) { + opt.next = str; + for (i = 0; i < num_targets; i++) { *target = kzalloc(sizeof(struct dm_setup_target), GFP_KERNEL); if (!*target) { - DMERR("failed to allocate memory for target %d", - dm_setup_args.target_count); + DMERR("failed to allocate memory for target %s<%ld>", + dev->name, i); goto parse_fail; } - dm_setup_args.target_count++; + dev->target_count++; - (*target)->begin = simple_strtoull(str, &next, 10); - if (!next || *next != DM_FIELD_SEP) { - DMERR("failed to parse starting sector for target %d", - dm_setup_args.target_count - 1); + if (!get_dm_option(&opt, DM_FIELD_SEP)) { + DMERR("failed to parse starting sector" + " for target %s<%ld>", dev->name, i); goto parse_fail; } - str = skip_spaces(next + 1); + (*target)->begin = simple_strtoull(opt.start, NULL, 10); - (*target)->length = simple_strtoull(str, &next, 10); - if (!next || *next != DM_FIELD_SEP) { - DMERR("failed to parse length for target %d", - dm_setup_args.target_count - 1); + if (!get_dm_option(&opt, DM_FIELD_SEP)) { + DMERR("failed to parse length for target %s<%ld>", + dev->name, i); goto parse_fail; } - str = skip_spaces(next + 1); - - len = get_dm_option(str, &next, DM_FIELD_SEP); - if (!len || - !((*target)->type = kstrndup(str, len, GFP_KERNEL))) { - DMERR("failed to parse type for target %d", - dm_setup_args.target_count - 1); + (*target)->length = simple_strtoull(opt.start, NULL, 10); + + if (get_dm_option(&opt, DM_FIELD_SEP)) + (*target)->type = kstrndup(opt.start, opt.len, + GFP_KERNEL); + if (!((*target)->type)) { + DMERR("failed to parse type for target %s<%ld>", + dev->name, i); goto parse_fail; } - str = skip_spaces(next); - - len = get_dm_option(str, &next, DM_LINE_SEP); - if (!len || - !((*target)->params = kstrndup(str, len, GFP_KERNEL))) { - DMERR("failed to parse params for target %d", - dm_setup_args.target_count - 1); + if (get_dm_option(&opt, DM_LINE_SEP)) + (*target)->params = kstrndup(opt.start, opt.len, + GFP_KERNEL); + if (!((*target)->params)) { + DMERR("failed to parse params for target %s<%ld>", + dev->name, i); goto parse_fail; } - str = skip_spaces(next); - - /* Before moving on, walk through the copied target and - * attempt to replace all /dev/xxx with the major:minor number. - * It may not be possible to resolve them traditionally at - * boot-time. */ - dm_substitute_devices((*target)->params, len); - target = &((*target)->next); } - DMDEBUG("parsed %d targets", dm_setup_args.target_count); + DMDEBUG("parsed %d targets", dev->target_count); - return 0; + return opt.next; parse_fail: - return 1; + return NULL; +} + +static struct dm_device * __init dm_parse_args(void) +{ + struct dm_device *devices = NULL; + struct dm_device **tail = &devices; + struct dm_device *dev; + char *str = dm_setup_args.str; + unsigned long num_devices = dm_setup_args.num_devices; + unsigned long i; + + if (!str) + return NULL; + for (i = 0; i < num_devices; i++) { + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + DMERR("failed to allocated memory for dev"); + goto error; + } + *tail = dev; + tail = &dev->next; + /* + * devices are given minor numbers 0 - n-1 + * in the order they are found in the arg + * string. + */ + dev->minor = i; + str = dm_parse_device(dev, str); + if (!str) /* NULL indicates error in parsing, bail */ + goto error; + + str = dm_parse_targets(dev, str); + if (!str) + goto error; + } + return devices; +error: + dm_setup_cleanup(devices); + return NULL; } /* * Parse the command-line parameters given our kernel, but do not * actually try to invoke the DM device now; that is handled by - * dm_setup_drive after the low-level disk drivers have initialised. - * dm format is as follows: - * dm="name uuid fmode,[table line 1],[table line 2],..." - * May be used with root=/dev/dm-0 as it always uses the first dm minor. + * dm_setup_drives after the low-level disk drivers have initialised. + * dm format is described at the top of the file. + * + * Because dm minor numbers are assigned in assending order starting with 0, + * You can assume the first device is /dev/dm-0, the next device is /dev/dm-1, + * and so forth. */ - static int __init dm_setup(char *str) { - dm_setup_args_init(); + struct dm_option opt; + unsigned long num_devices; - str = dm_setup_parse_device_args(str); if (!str) { DMDEBUG("str is NULL"); goto parse_fail; } - - /* Target parsing is delayed until we have dynamic memory */ - dm_setup_args.targets = str; - - printk(KERN_INFO "dm: will configure '%s' on dm-%d\n", - dm_setup_args.name, dm_setup_args.minor); - + opt.next = str; + if (!get_dm_option(&opt, DM_FIELD_SEP)) + goto parse_fail; + if (isdigit(opt.start[0])) { /* XXX: Optional number field */ + num_devices = simple_strtoul(opt.start, NULL, 10); + str = opt.next; + } else { + num_devices = 1; + /* Don't advance str */ + } + if (num_devices > DM_MAX_DEVICES) { + DMDEBUG("too many devices %lu > %d", + num_devices, DM_MAX_DEVICES); + } + dm_setup_args.str = str; + dm_setup_args.num_devices = num_devices; + DMINFO("will configure %lu devices", num_devices); dm_early_setup = 1; return 1; parse_fail: - printk(KERN_WARNING "dm: Invalid arguments supplied to dm=.\n"); + DMWARN("Invalid arguments supplied to dm=."); return 0; } - -static void __init dm_setup_drive(void) +static void __init dm_setup_drives(void) { struct mapped_device *md = NULL; struct dm_table *table = NULL; struct dm_setup_target *target; - char *uuid = dm_setup_args.uuid; + struct dm_device *dev; + char *uuid; fmode_t fmode = FMODE_READ; + struct dm_device *devices; - /* Finish parsing the targets. */ - if (dm_setup_parse_targets(dm_setup_args.targets)) - goto parse_fail; - - if (dm_create(dm_setup_args.minor, &md)) { - DMDEBUG("failed to create the device"); - goto dm_create_fail; - } - DMDEBUG("created device '%s'", dm_device_name(md)); - - /* In addition to flagging the table below, the disk must be - * set explicitly ro/rw. */ - set_disk_ro(dm_disk(md), dm_setup_args.ro); + devices = dm_parse_args(); - if (!dm_setup_args.ro) - fmode |= FMODE_WRITE; - if (dm_table_create(&table, fmode, dm_setup_args.target_count, md)) { - DMDEBUG("failed to create the table"); - goto dm_table_create_fail; - } + for (dev = devices; dev; dev = dev->next) { + if (dm_create(dev->minor, &md)) { + DMDEBUG("failed to create the device"); + goto dm_create_fail; + } + DMDEBUG("created device '%s'", dm_device_name(md)); + + /* + * In addition to flagging the table below, the disk must be + * set explicitly ro/rw. + */ + set_disk_ro(dm_disk(md), dev->ro); + + if (!dev->ro) + fmode |= FMODE_WRITE; + if (dm_table_create(&table, fmode, dev->target_count, md)) { + DMDEBUG("failed to create the table"); + goto dm_table_create_fail; + } - dm_lock_md_type(md); - target = dm_setup_args.target; - while (target) { - DMINFO("adding target '%llu %llu %s %s'", - (unsigned long long) target->begin, - (unsigned long long) target->length, target->type, - target->params); - if (dm_table_add_target(table, target->type, target->begin, - target->length, target->params)) { - DMDEBUG("failed to add the target to the table"); - goto add_target_fail; + dm_lock_md_type(md); + + for (target = dev->target; target; target = target->next) { + DMINFO("adding target '%llu %llu %s %s'", + (unsigned long long) target->begin, + (unsigned long long) target->length, + target->type, target->params); + if (dm_table_add_target(table, target->type, + target->begin, + target->length, + target->params)) { + DMDEBUG("failed to add the target" + " to the table"); + goto add_target_fail; + } + } + if (dm_table_complete(table)) { + DMDEBUG("failed to complete the table"); + goto table_complete_fail; } - target = target->next; - } - if (dm_table_complete(table)) { - DMDEBUG("failed to complete the table"); - goto table_complete_fail; - } + /* Suspend the device so that we can bind it to the table. */ + if (dm_suspend(md, 0)) { + DMDEBUG("failed to suspend the device pre-bind"); + goto suspend_fail; + } - if (dm_get_md_type(md) == DM_TYPE_NONE) { + /* Initial table load: acquire type of table. */ dm_set_md_type(md, dm_table_get_type(table)); + + /* Setup md->queue to reflect md's type. */ if (dm_setup_md_queue(md)) { DMWARN("unable to set up device queue for new table."); goto setup_md_queue_fail; } - } else if (dm_get_md_type(md) != dm_table_get_type(table)) { - DMWARN("can't change device type after initial table load."); - goto setup_md_queue_fail; - } - - /* Suspend the device so that we can bind it to the table. */ - if (dm_suspend(md, 0)) { - DMDEBUG("failed to suspend the device pre-bind"); - goto suspend_fail; - } - /* Bind the table to the device. This is the only way to associate - * md->map with the table and set the disk capacity directly. */ - if (dm_swap_table(md, table)) { /* should return NULL. */ - DMDEBUG("failed to bind the device to the table"); - goto table_bind_fail; - } + /* + * Bind the table to the device. This is the only way + * to associate md->map with the table and set the disk + * capacity directly. + */ + if (dm_swap_table(md, table)) { /* should return NULL. */ + DMDEBUG("failed to bind the device to the table"); + goto table_bind_fail; + } - /* Finally, resume and the device should be ready. */ - if (dm_resume(md)) { - DMDEBUG("failed to resume the device"); - goto resume_fail; - } + /* Finally, resume and the device should be ready. */ + if (dm_resume(md)) { + DMDEBUG("failed to resume the device"); + goto resume_fail; + } - /* Export the dm device via the ioctl interface */ - if (!strcmp(DM_NO_UUID, dm_setup_args.uuid)) - uuid = NULL; - if (dm_ioctl_export(md, dm_setup_args.name, uuid)) { - DMDEBUG("failed to export device with given name and uuid"); - goto export_fail; - } - printk(KERN_INFO "dm: dm-%d is ready\n", dm_setup_args.minor); + /* Export the dm device via the ioctl interface */ + if (!strcmp(DM_NO_UUID, dev->uuid)) + uuid = NULL; + if (dm_ioctl_export(md, dev->name, uuid)) { + DMDEBUG("failed to export device with given" + " name and uuid"); + goto export_fail; + } - dm_unlock_md_type(md); - dm_setup_cleanup(); + dm_unlock_md_type(md); + + DMINFO("dm-%d is ready", dev->minor); + } + dm_setup_cleanup(devices); return; export_fail: resume_fail: table_bind_fail: -suspend_fail: setup_md_queue_fail: +suspend_fail: table_complete_fail: add_target_fail: dm_unlock_md_type(md); dm_table_create_fail: dm_put(md); dm_create_fail: - dm_setup_cleanup(); -parse_fail: - printk(KERN_WARNING "dm: starting dm-%d (%s) failed\n", - dm_setup_args.minor, dm_setup_args.name); + DMWARN("starting dm-%d (%s) failed", + dev->minor, dev->name); + dm_setup_cleanup(devices); } __setup("dm=", dm_setup); @@ -421,6 +465,6 @@ void __init dm_run_setup(void) { if (!dm_early_setup) return; - printk(KERN_INFO "dm: attempting early device configuration.\n"); - dm_setup_drive(); + DMINFO("attempting early device configuration."); + dm_setup_drives(); } diff --git a/kernel/cgroup.c b/kernel/cgroup.c index b05fc202b548..25cf44889559 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -2693,7 +2693,7 @@ static int cgroup_procs_write_permission(struct task_struct *task, if (!uid_eq(cred->euid, GLOBAL_ROOT_UID) && !uid_eq(cred->euid, tcred->uid) && !uid_eq(cred->euid, tcred->suid) && - !ns_capable(tcred->user_ns, CAP_SYS_RESOURCE)) + !ns_capable(tcred->user_ns, CAP_SYS_NICE)) ret = -EACCES; if (!ret && cgroup_on_dfl(dst_cgrp)) { @@ -2757,11 +2757,12 @@ static ssize_t __cgroup_procs_write(struct kernfs_open_file *of, char *buf, tsk = tsk->group_leader; /* - * Workqueue threads may acquire PF_NO_SETAFFINITY and become - * trapped in a cpuset, or RT worker may be born in a cgroup - * with no rt_runtime allocated. Just say no. + * kthreads may acquire PF_NO_SETAFFINITY during initialization. + * If userland migrates such a kthread to a non-root cgroup, it can + * become trapped in a cpuset, or RT kthread may be born in a + * cgroup with no rt_runtime allocated. Just say no. */ - if (tsk == kthreadd_task || (tsk->flags & PF_NO_SETAFFINITY)) { + if (tsk->no_cgroup_migration || (tsk->flags & PF_NO_SETAFFINITY)) { ret = -EINVAL; goto out_unlock_rcu; } diff --git a/kernel/events/core.c b/kernel/events/core.c index f9c6f554460e..95c447e658f7 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -8454,6 +8454,37 @@ static int perf_event_set_clock(struct perf_event *event, clockid_t clk_id) return 0; } +/* + * Variation on perf_event_ctx_lock_nested(), except we take two context + * mutexes. + */ +static struct perf_event_context * +__perf_event_ctx_lock_double(struct perf_event *group_leader, + struct perf_event_context *ctx) +{ + struct perf_event_context *gctx; + +again: + rcu_read_lock(); + gctx = READ_ONCE(group_leader->ctx); + if (!atomic_inc_not_zero(&gctx->refcount)) { + rcu_read_unlock(); + goto again; + } + rcu_read_unlock(); + + mutex_lock_double(&gctx->mutex, &ctx->mutex); + + if (group_leader->ctx != gctx) { + mutex_unlock(&ctx->mutex); + mutex_unlock(&gctx->mutex); + put_ctx(gctx); + goto again; + } + + return gctx; +} + /** * sys_perf_event_open - open a performance event, associate it to a task/cpu * @@ -8707,8 +8738,26 @@ SYSCALL_DEFINE5(perf_event_open, } if (move_group) { - gctx = group_leader->ctx; - mutex_lock_double(&gctx->mutex, &ctx->mutex); + gctx = __perf_event_ctx_lock_double(group_leader, ctx); + + /* + * Check if we raced against another sys_perf_event_open() call + * moving the software group underneath us. + */ + if (!(group_leader->group_flags & PERF_GROUP_SOFTWARE)) { + /* + * If someone moved the group out from under us, check + * if this new event wound up on the same ctx, if so + * its the regular !move_group case, otherwise fail. + */ + if (gctx != ctx) { + err = -EINVAL; + goto err_locked; + } else { + perf_event_ctx_unlock(group_leader, gctx); + move_group = 0; + } + } } else { mutex_lock(&ctx->mutex); } @@ -8803,7 +8852,7 @@ SYSCALL_DEFINE5(perf_event_open, perf_unpin_context(ctx); if (move_group) - mutex_unlock(&gctx->mutex); + perf_event_ctx_unlock(group_leader, gctx); mutex_unlock(&ctx->mutex); if (group_leader) mutex_unlock(&group_leader->group_leader_mutex); @@ -8833,7 +8882,7 @@ SYSCALL_DEFINE5(perf_event_open, err_locked: if (move_group) - mutex_unlock(&gctx->mutex); + perf_event_ctx_unlock(group_leader, gctx); mutex_unlock(&ctx->mutex); /* err_file: */ fput(event_file); @@ -9455,7 +9504,7 @@ static int perf_event_init_context(struct task_struct *child, int ctxn) ret = inherit_task_group(event, parent, parent_ctx, child, ctxn, &inherited_all); if (ret) - break; + goto out_unlock; } /* @@ -9471,7 +9520,7 @@ static int perf_event_init_context(struct task_struct *child, int ctxn) ret = inherit_task_group(event, parent, parent_ctx, child, ctxn, &inherited_all); if (ret) - break; + goto out_unlock; } raw_spin_lock_irqsave(&parent_ctx->lock, flags); @@ -9499,6 +9548,7 @@ static int perf_event_init_context(struct task_struct *child, int ctxn) } raw_spin_unlock_irqrestore(&parent_ctx->lock, flags); +out_unlock: mutex_unlock(&parent_ctx->mutex); perf_unpin_context(parent_ctx); diff --git a/kernel/fork.c b/kernel/fork.c index 75573eeb49b2..622571d2a833 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -333,13 +333,14 @@ void set_task_stack_end_magic(struct task_struct *tsk) *stackend = STACK_END_MAGIC; /* for overflow detection */ } -static struct task_struct *dup_task_struct(struct task_struct *orig) +static struct task_struct *dup_task_struct(struct task_struct *orig, int node) { struct task_struct *tsk; struct thread_info *ti; - int node = tsk_fork_get_node(orig); int err; + if (node == NUMA_NO_NODE) + node = tsk_fork_get_node(orig); tsk = alloc_task_struct_node(node); if (!tsk) return NULL; @@ -1276,7 +1277,8 @@ static struct task_struct *copy_process(unsigned long clone_flags, int __user *child_tidptr, struct pid *pid, int trace, - unsigned long tls) + unsigned long tls, + int node) { int retval; struct task_struct *p; @@ -1329,7 +1331,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, goto fork_out; retval = -ENOMEM; - p = dup_task_struct(current); + p = dup_task_struct(current, node); if (!p) goto fork_out; @@ -1706,7 +1708,8 @@ static inline void init_idle_pids(struct pid_link *links) struct task_struct *fork_idle(int cpu) { struct task_struct *task; - task = copy_process(CLONE_VM, 0, 0, NULL, &init_struct_pid, 0, 0); + task = copy_process(CLONE_VM, 0, 0, NULL, &init_struct_pid, 0, 0, + cpu_to_node(cpu)); if (!IS_ERR(task)) { init_idle_pids(task->pids); init_idle(task, cpu, false); @@ -1751,7 +1754,7 @@ long _do_fork(unsigned long clone_flags, } p = copy_process(clone_flags, stack_start, stack_size, - child_tidptr, NULL, trace, tls); + child_tidptr, NULL, trace, tls, NUMA_NO_NODE); /* * Do this prior waking up the new thread - the thread pointer * might get invalid after that point, if the thread exits quickly. diff --git a/kernel/futex.c b/kernel/futex.c index beb042dcc332..af29863f3349 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -2690,7 +2690,6 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags, { struct hrtimer_sleeper timeout, *to = NULL; struct rt_mutex_waiter rt_waiter; - struct rt_mutex *pi_mutex = NULL; struct futex_hash_bucket *hb; union futex_key key2 = FUTEX_KEY_INIT; struct futex_q q = futex_q_init; @@ -2774,6 +2773,8 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags, if (q.pi_state && (q.pi_state->owner != current)) { spin_lock(q.lock_ptr); ret = fixup_pi_state_owner(uaddr2, &q, current); + if (ret && rt_mutex_owner(&q.pi_state->pi_mutex) == current) + rt_mutex_unlock(&q.pi_state->pi_mutex); /* * Drop the reference to the pi state which * the requeue_pi() code acquired for us. @@ -2782,6 +2783,8 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags, spin_unlock(q.lock_ptr); } } else { + struct rt_mutex *pi_mutex; + /* * We have been woken up by futex_unlock_pi(), a timeout, or a * signal. futex_unlock_pi() will not destroy the lock_ptr nor @@ -2805,18 +2808,19 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags, if (res) ret = (res < 0) ? res : 0; + /* + * If fixup_pi_state_owner() faulted and was unable to handle + * the fault, unlock the rt_mutex and return the fault to + * userspace. + */ + if (ret && rt_mutex_owner(pi_mutex) == current) + rt_mutex_unlock(pi_mutex); + /* Unqueue and drop the lock. */ unqueue_me_pi(&q); } - /* - * If fixup_pi_state_owner() faulted and was unable to handle the - * fault, unlock the rt_mutex and return the fault to userspace. - */ - if (ret == -EFAULT) { - if (pi_mutex && rt_mutex_owner(pi_mutex) == current) - rt_mutex_unlock(pi_mutex); - } else if (ret == -EINTR) { + if (ret == -EINTR) { /* * We've already been requeued, but cannot restart by calling * futex_lock_pi() directly. We could restart this syscall, but diff --git a/kernel/kthread.c b/kernel/kthread.c index 9ff173dca1ae..850b255649a2 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c @@ -18,6 +18,7 @@ #include <linux/freezer.h> #include <linux/ptrace.h> #include <linux/uaccess.h> +#include <linux/cgroup.h> #include <trace/events/sched.h> static DEFINE_SPINLOCK(kthread_create_lock); @@ -205,6 +206,7 @@ static int kthread(void *_create) ret = -EINTR; if (!test_bit(KTHREAD_SHOULD_STOP, &self.flags)) { + cgroup_kthread_ready(); __kthread_parkme(&self); ret = threadfn(data); } @@ -510,6 +512,7 @@ int kthreadd(void *unused) set_mems_allowed(node_states[N_MEMORY]); current->flags |= PF_NOFREEZE; + cgroup_init_kthreadd(); for (;;) { set_current_state(TASK_INTERRUPTIBLE); diff --git a/kernel/padata.c b/kernel/padata.c index b38bea9c466a..401227e3967c 100644 --- a/kernel/padata.c +++ b/kernel/padata.c @@ -189,19 +189,20 @@ static struct padata_priv *padata_get_next(struct parallel_data *pd) reorder = &next_queue->reorder; + spin_lock(&reorder->lock); if (!list_empty(&reorder->list)) { padata = list_entry(reorder->list.next, struct padata_priv, list); - spin_lock(&reorder->lock); list_del_init(&padata->list); atomic_dec(&pd->reorder_objects); - spin_unlock(&reorder->lock); pd->processed++; + spin_unlock(&reorder->lock); goto out; } + spin_unlock(&reorder->lock); if (__this_cpu_read(pd->pqueue->cpu_index) == next_queue->cpu_index) { padata = ERR_PTR(-ENODATA); diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 2fc1177383a0..1535c46808f1 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -647,7 +647,7 @@ static void power_down(void) */ int hibernate(void) { - int error; + int error, nr_calls = 0; if (!hibernation_available()) { pr_debug("PM: Hibernation not available.\n"); @@ -662,9 +662,11 @@ int hibernate(void) } pm_prepare_console(); - error = pm_notifier_call_chain(PM_HIBERNATION_PREPARE); - if (error) + error = __pm_notifier_call_chain(PM_HIBERNATION_PREPARE, -1, &nr_calls); + if (error) { + nr_calls--; goto Exit; + } printk(KERN_INFO "PM: Syncing filesystems ... "); sys_sync(); @@ -714,7 +716,7 @@ int hibernate(void) /* Don't bother checking whether freezer_test_done is true */ freezer_test_done = false; Exit: - pm_notifier_call_chain(PM_POST_HIBERNATION); + __pm_notifier_call_chain(PM_POST_HIBERNATION, nr_calls, NULL); pm_restore_console(); atomic_inc(&snapshot_device_available); Unlock: @@ -740,7 +742,7 @@ int hibernate(void) */ static int software_resume(void) { - int error; + int error, nr_calls = 0; unsigned int flags; /* @@ -827,9 +829,11 @@ static int software_resume(void) } pm_prepare_console(); - error = pm_notifier_call_chain(PM_RESTORE_PREPARE); - if (error) + error = __pm_notifier_call_chain(PM_RESTORE_PREPARE, -1, &nr_calls); + if (error) { + nr_calls--; goto Close_Finish; + } pr_debug("PM: Preparing processes for restore.\n"); error = freeze_processes(); @@ -855,7 +859,7 @@ static int software_resume(void) unlock_device_hotplug(); thaw_processes(); Finish: - pm_notifier_call_chain(PM_POST_RESTORE); + __pm_notifier_call_chain(PM_POST_RESTORE, nr_calls, NULL); pm_restore_console(); atomic_inc(&snapshot_device_available); /* For success case, the suspend path will release the lock */ diff --git a/kernel/power/main.c b/kernel/power/main.c index 27946975eff0..5ea50b1b7595 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -38,12 +38,19 @@ int unregister_pm_notifier(struct notifier_block *nb) } EXPORT_SYMBOL_GPL(unregister_pm_notifier); -int pm_notifier_call_chain(unsigned long val) +int __pm_notifier_call_chain(unsigned long val, int nr_to_call, int *nr_calls) { - int ret = blocking_notifier_call_chain(&pm_chain_head, val, NULL); + int ret; + + ret = __blocking_notifier_call_chain(&pm_chain_head, val, NULL, + nr_to_call, nr_calls); return notifier_to_errno(ret); } +int pm_notifier_call_chain(unsigned long val) +{ + return __pm_notifier_call_chain(val, -1, NULL); +} /* If set, devices may be suspended and resumed asynchronously. */ int pm_async_enabled = 1; diff --git a/kernel/power/power.h b/kernel/power/power.h index efe1b3b17c88..51f02ecaf125 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -200,6 +200,8 @@ static inline void suspend_test_finish(const char *label) {} #ifdef CONFIG_PM_SLEEP /* kernel/power/main.c */ +extern int __pm_notifier_call_chain(unsigned long val, int nr_to_call, + int *nr_calls); extern int pm_notifier_call_chain(unsigned long val); #endif diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 024411816ccf..58209d8bfc56 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -268,16 +268,18 @@ static int suspend_test(int level) */ static int suspend_prepare(suspend_state_t state) { - int error; + int error, nr_calls = 0; if (!sleep_state_supported(state)) return -EPERM; pm_prepare_console(); - error = pm_notifier_call_chain(PM_SUSPEND_PREPARE); - if (error) + error = __pm_notifier_call_chain(PM_SUSPEND_PREPARE, -1, &nr_calls); + if (error) { + nr_calls--; goto Finish; + } trace_suspend_resume(TPS("freeze_processes"), 0, true); error = suspend_freeze_processes(); @@ -288,7 +290,7 @@ static int suspend_prepare(suspend_state_t state) suspend_stats.failed_freeze++; dpm_save_failed_step(SUSPEND_FREEZE); Finish: - pm_notifier_call_chain(PM_POST_SUSPEND); + __pm_notifier_call_chain(PM_POST_SUSPEND, nr_calls, NULL); pm_restore_console(); return error; } diff --git a/kernel/power/user.c b/kernel/power/user.c index 526e8911460a..35310b627388 100644 --- a/kernel/power/user.c +++ b/kernel/power/user.c @@ -47,7 +47,7 @@ atomic_t snapshot_device_available = ATOMIC_INIT(1); static int snapshot_open(struct inode *inode, struct file *filp) { struct snapshot_data *data; - int error; + int error, nr_calls = 0; if (!hibernation_available()) return -EPERM; @@ -74,9 +74,9 @@ static int snapshot_open(struct inode *inode, struct file *filp) swap_type_of(swsusp_resume_device, 0, NULL) : -1; data->mode = O_RDONLY; data->free_bitmaps = false; - error = pm_notifier_call_chain(PM_HIBERNATION_PREPARE); + error = __pm_notifier_call_chain(PM_HIBERNATION_PREPARE, -1, &nr_calls); if (error) - pm_notifier_call_chain(PM_POST_HIBERNATION); + __pm_notifier_call_chain(PM_POST_HIBERNATION, --nr_calls, NULL); } else { /* * Resuming. We may need to wait for the image device to @@ -86,13 +86,15 @@ static int snapshot_open(struct inode *inode, struct file *filp) data->swap = -1; data->mode = O_WRONLY; - error = pm_notifier_call_chain(PM_RESTORE_PREPARE); + error = __pm_notifier_call_chain(PM_RESTORE_PREPARE, -1, &nr_calls); if (!error) { error = create_basic_memory_bitmaps(); data->free_bitmaps = !error; - } + } else + nr_calls--; + if (error) - pm_notifier_call_chain(PM_POST_RESTORE); + __pm_notifier_call_chain(PM_POST_RESTORE, nr_calls, NULL); } if (error) atomic_inc(&snapshot_device_available); diff --git a/kernel/ptrace.c b/kernel/ptrace.c index a46c40bfb5f6..c7e8ed99c953 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -151,11 +151,17 @@ static void ptrace_unfreeze_traced(struct task_struct *task) WARN_ON(!task->ptrace || task->parent != current); + /* + * PTRACE_LISTEN can allow ptrace_trap_notify to wake us up remotely. + * Recheck state under the lock to close this race. + */ spin_lock_irq(&task->sighand->siglock); - if (__fatal_signal_pending(task)) - wake_up_state(task, __TASK_TRACED); - else - task->state = TASK_TRACED; + if (task->state == __TASK_TRACED) { + if (__fatal_signal_pending(task)) + wake_up_state(task, __TASK_TRACED); + else + task->state = TASK_TRACED; + } spin_unlock_irq(&task->sighand->siglock); } diff --git a/kernel/sched/core.c b/kernel/sched/core.c index e107c4d6b385..2d2b8834dd3b 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -26,6 +26,7 @@ * Thomas Gleixner, Mike Kravetz */ +#include <linux/kasan.h> #include <linux/mm.h> #include <linux/module.h> #include <linux/nmi.h> @@ -84,6 +85,9 @@ #ifdef CONFIG_PARAVIRT #include <asm/paravirt.h> #endif +#ifdef CONFIG_MSM_APP_SETTINGS +#include <asm/app_api.h> +#endif #include "sched.h" #include "../workqueue_internal.h" @@ -2749,6 +2753,14 @@ prepare_task_switch(struct rq *rq, struct task_struct *prev, fire_sched_out_preempt_notifiers(prev, next); prepare_lock_switch(rq, next); prepare_arch_switch(next); + +#ifdef CONFIG_MSM_APP_SETTINGS + if (use_app_setting) + switch_app_setting_bit(prev, next); + + if (use_32bit_app_setting || use_32bit_app_setting_pro) + switch_32bit_app_setting_bit(prev, next); +#endif } /** @@ -5413,6 +5425,8 @@ void init_idle(struct task_struct *idle, int cpu, bool cpu_up) idle->state = TASK_RUNNING; idle->se.exec_start = sched_clock(); + kasan_unpoison_task_stack(idle); + #ifdef CONFIG_SMP /* * Its possible that init_idle() gets called multiple times on a task, diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c index 685ae83b2bfa..3d55ec89c400 100644 --- a/kernel/sched/deadline.c +++ b/kernel/sched/deadline.c @@ -1843,12 +1843,11 @@ static void switched_to_dl(struct rq *rq, struct task_struct *p) #ifdef CONFIG_SMP if (p->nr_cpus_allowed > 1 && rq->dl.overloaded) queue_push_tasks(rq); -#else +#endif if (dl_task(rq->curr)) check_preempt_curr_dl(rq, p, 0); else resched_curr(rq); -#endif } } diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 8d5353906c8d..4d96380b35e8 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6060,7 +6060,7 @@ long group_norm_util(struct energy_env *eenv, struct sched_group *sg) } static int find_new_capacity(struct energy_env *eenv, - const struct sched_group_energy const *sge) + const struct sched_group_energy * const sge) { int idx; unsigned long util = group_max_util(eenv); diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c index 07b2c63e4983..29345ed74069 100644 --- a/kernel/sched/rt.c +++ b/kernel/sched/rt.c @@ -2425,10 +2425,9 @@ static void switched_to_rt(struct rq *rq, struct task_struct *p) #ifdef CONFIG_SMP if (p->nr_cpus_allowed > 1 && rq->rt.overloaded) queue_push_tasks(rq); -#else +#endif /* CONFIG_SMP */ if (p->prio < rq->curr->prio) resched_curr(rq); -#endif /* CONFIG_SMP */ } } diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 75500042fd32..276a2387f06f 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -1011,7 +1011,7 @@ struct sched_group { unsigned int group_weight; struct sched_group_capacity *sgc; - const struct sched_group_energy const *sge; + const struct sched_group_energy *sge; /* * The CPUs this group covers. diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 8cc5167e4b04..816999804a16 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -65,6 +65,7 @@ #include <linux/sched/sysctl.h> #include <linux/kexec.h> #include <linux/bpf.h> +#include <linux/mount.h> #include <asm/uaccess.h> #include <asm/processor.h> @@ -2077,6 +2078,14 @@ static struct ctl_table fs_table[] = { .mode = 0644, .proc_handler = proc_doulongvec_minmax, }, + { + .procname = "mount-max", + .data = &sysctl_mount_max, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &one, + }, { } }; diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 3f743b147247..34b2a0d5cf1a 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -3677,23 +3677,24 @@ static void __enable_ftrace_function_probe(struct ftrace_ops_hash *old_hash) ftrace_probe_registered = 1; } -static void __disable_ftrace_function_probe(void) +static bool __disable_ftrace_function_probe(void) { int i; if (!ftrace_probe_registered) - return; + return false; for (i = 0; i < FTRACE_FUNC_HASHSIZE; i++) { struct hlist_head *hhd = &ftrace_func_hash[i]; if (hhd->first) - return; + return false; } /* no more funcs left */ ftrace_shutdown(&trace_probe_ops, 0); ftrace_probe_registered = 0; + return true; } @@ -3820,6 +3821,7 @@ static void __unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, void *data, int flags) { + struct ftrace_ops_hash old_hash_ops; struct ftrace_func_entry *rec_entry; struct ftrace_func_probe *entry; struct ftrace_func_probe *p; @@ -3831,6 +3833,7 @@ __unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, struct hlist_node *tmp; char str[KSYM_SYMBOL_LEN]; int i, ret; + bool disabled; if (glob && (strcmp(glob, "*") == 0 || !strlen(glob))) func_g.search = NULL; @@ -3849,6 +3852,10 @@ __unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, mutex_lock(&trace_probe_ops.func_hash->regex_lock); + old_hash_ops.filter_hash = old_hash; + /* Probes only have filters */ + old_hash_ops.notrace_hash = NULL; + hash = alloc_and_copy_ftrace_hash(FTRACE_HASH_DEFAULT_BITS, *orig_hash); if (!hash) /* Hmm, should report this somehow */ @@ -3886,12 +3893,17 @@ __unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, } } mutex_lock(&ftrace_lock); - __disable_ftrace_function_probe(); + disabled = __disable_ftrace_function_probe(); /* * Remove after the disable is called. Otherwise, if the last * probe is removed, a null hash means *all enabled*. */ ret = ftrace_hash_move(&trace_probe_ops, 1, orig_hash, hash); + + /* still need to update the function call sites */ + if (ftrace_enabled && !disabled) + ftrace_run_modify_code(&trace_probe_ops, FTRACE_UPDATE_CALLS, + &old_hash_ops); synchronize_sched(); if (!ret) free_ftrace_hash_rcu(old_hash); diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index acbb0e73d3a2..1275175b0946 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -3440,11 +3440,23 @@ EXPORT_SYMBOL_GPL(ring_buffer_iter_reset); int ring_buffer_iter_empty(struct ring_buffer_iter *iter) { struct ring_buffer_per_cpu *cpu_buffer; + struct buffer_page *reader; + struct buffer_page *head_page; + struct buffer_page *commit_page; + unsigned commit; cpu_buffer = iter->cpu_buffer; - return iter->head_page == cpu_buffer->commit_page && - iter->head == rb_commit_index(cpu_buffer); + /* Remember, trace recording is off when iterator is in use */ + reader = cpu_buffer->reader_page; + head_page = cpu_buffer->head_page; + commit_page = cpu_buffer->commit_page; + commit = rb_page_commit(commit_page); + + return ((iter->head_page == commit_page && iter->head == commit) || + (iter->head_page == reader && commit_page == head_page && + head_page->read == commit && + iter->head == rb_page_commit(cpu_buffer->reader_page))); } EXPORT_SYMBOL_GPL(ring_buffer_iter_empty); @@ -4875,9 +4887,9 @@ static __init int test_ringbuffer(void) rb_data[cpu].cnt = cpu; rb_threads[cpu] = kthread_create(rb_test, &rb_data[cpu], "rbtester/%d", cpu); - if (WARN_ON(!rb_threads[cpu])) { + if (WARN_ON(IS_ERR(rb_threads[cpu]))) { pr_cont("FAILED\n"); - ret = -1; + ret = PTR_ERR(rb_threads[cpu]); goto out_free; } @@ -4887,9 +4899,9 @@ static __init int test_ringbuffer(void) /* Now create the rb hammer! */ rb_hammer = kthread_run(rb_hammer_test, NULL, "rbhammer"); - if (WARN_ON(!rb_hammer)) { + if (WARN_ON(IS_ERR(rb_hammer))) { pr_cont("FAILED\n"); - ret = -1; + ret = PTR_ERR(rb_hammer); goto out_free; } diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index c0c10a335b3b..60d246c4eefa 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -6170,11 +6170,13 @@ ftrace_trace_snapshot_callback(struct ftrace_hash *hash, return ret; out_reg: - ret = register_ftrace_function_probe(glob, ops, count); + ret = alloc_snapshot(&global_trace); + if (ret < 0) + goto out; - if (ret >= 0) - alloc_snapshot(&global_trace); + ret = register_ftrace_function_probe(glob, ops, count); + out: return ret < 0 ? ret : 0; } diff --git a/mm/hugetlb.c b/mm/hugetlb.c index ea11123a9249..7294301d8495 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -4362,6 +4362,7 @@ follow_huge_pmd(struct mm_struct *mm, unsigned long address, { struct page *page = NULL; spinlock_t *ptl; + pte_t pte; retry: ptl = pmd_lockptr(mm, pmd); spin_lock(ptl); @@ -4371,12 +4372,13 @@ retry: */ if (!pmd_huge(*pmd)) goto out; - if (pmd_present(*pmd)) { + pte = huge_ptep_get((pte_t *)pmd); + if (pte_present(pte)) { page = pmd_page(*pmd) + ((address & ~PMD_MASK) >> PAGE_SHIFT); if (flags & FOLL_GET) get_page(page); } else { - if (is_hugetlb_entry_migration(huge_ptep_get((pte_t *)pmd))) { + if (is_hugetlb_entry_migration(pte)) { spin_unlock(ptl); __migration_entry_wait(mm, (pte_t *)pmd, ptl); goto retry; diff --git a/mm/kasan/kasan.c b/mm/kasan/kasan.c index bc0a8d8b8f42..1ad20ade8c91 100644 --- a/mm/kasan/kasan.c +++ b/mm/kasan/kasan.c @@ -20,6 +20,7 @@ #include <linux/init.h> #include <linux/kernel.h> #include <linux/kmemleak.h> +#include <linux/linkage.h> #include <linux/memblock.h> #include <linux/memory.h> #include <linux/mm.h> @@ -60,6 +61,25 @@ void kasan_unpoison_shadow(const void *address, size_t size) } } +static void __kasan_unpoison_stack(struct task_struct *task, void *sp) +{ + void *base = task_stack_page(task); + size_t size = sp - base; + + kasan_unpoison_shadow(base, size); +} + +/* Unpoison the entire stack for a task. */ +void kasan_unpoison_task_stack(struct task_struct *task) +{ + __kasan_unpoison_stack(task, task_stack_page(task) + THREAD_SIZE); +} + +/* Unpoison the stack for the current task beyond a watermark sp value. */ +asmlinkage void kasan_unpoison_remaining_stack(void *sp) +{ + __kasan_unpoison_stack(current, sp); +} /* * All functions below always inlined so compiler could diff --git a/mm/mempolicy.c b/mm/mempolicy.c index 9174ec544632..d56142b66171 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -1493,7 +1493,6 @@ COMPAT_SYSCALL_DEFINE5(get_mempolicy, int __user *, policy, COMPAT_SYSCALL_DEFINE3(set_mempolicy, int, mode, compat_ulong_t __user *, nmask, compat_ulong_t, maxnode) { - long err = 0; unsigned long __user *nm = NULL; unsigned long nr_bits, alloc_size; DECLARE_BITMAP(bm, MAX_NUMNODES); @@ -1502,14 +1501,13 @@ COMPAT_SYSCALL_DEFINE3(set_mempolicy, int, mode, compat_ulong_t __user *, nmask, alloc_size = ALIGN(nr_bits, BITS_PER_LONG) / 8; if (nmask) { - err = compat_get_bitmap(bm, nmask, nr_bits); + if (compat_get_bitmap(bm, nmask, nr_bits)) + return -EFAULT; nm = compat_alloc_user_space(alloc_size); - err |= copy_to_user(nm, bm, alloc_size); + if (copy_to_user(nm, bm, alloc_size)) + return -EFAULT; } - if (err) - return -EFAULT; - return sys_set_mempolicy(mode, nm, nr_bits+1); } @@ -1517,7 +1515,6 @@ COMPAT_SYSCALL_DEFINE6(mbind, compat_ulong_t, start, compat_ulong_t, len, compat_ulong_t, mode, compat_ulong_t __user *, nmask, compat_ulong_t, maxnode, compat_ulong_t, flags) { - long err = 0; unsigned long __user *nm = NULL; unsigned long nr_bits, alloc_size; nodemask_t bm; @@ -1526,14 +1523,13 @@ COMPAT_SYSCALL_DEFINE6(mbind, compat_ulong_t, start, compat_ulong_t, len, alloc_size = ALIGN(nr_bits, BITS_PER_LONG) / 8; if (nmask) { - err = compat_get_bitmap(nodes_addr(bm), nmask, nr_bits); + if (compat_get_bitmap(nodes_addr(bm), nmask, nr_bits)) + return -EFAULT; nm = compat_alloc_user_space(alloc_size); - err |= copy_to_user(nm, nodes_addr(bm), alloc_size); + if (copy_to_user(nm, nodes_addr(bm), alloc_size)) + return -EFAULT; } - if (err) - return -EFAULT; - return sys_mbind(start, len, mode, nm, nr_bits+1, flags); } diff --git a/mm/mmap.c b/mm/mmap.c index a089cca8d79a..ebc9d1923c3d 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -48,6 +48,10 @@ #include <asm/tlb.h> #include <asm/mmu_context.h> +#ifdef CONFIG_MSM_APP_SETTINGS +#include <asm/app_api.h> +#endif + #include "internal.h" #ifndef arch_mmap_check @@ -1297,6 +1301,11 @@ unsigned long do_mmap(struct file *file, unsigned long addr, if (!len) return -EINVAL; +#ifdef CONFIG_MSM_APP_SETTINGS + if (use_app_setting) + apply_app_setting_bit(file); +#endif + /* * Does the application expect PROT_READ to imply PROT_EXEC? * diff --git a/mm/percpu.c b/mm/percpu.c index 1f376bce413c..ef6353f0adbd 100644 --- a/mm/percpu.c +++ b/mm/percpu.c @@ -1012,8 +1012,11 @@ area_found: mutex_unlock(&pcpu_alloc_mutex); } - if (chunk != pcpu_reserved_chunk) + if (chunk != pcpu_reserved_chunk) { + spin_lock_irqsave(&pcpu_lock, flags); pcpu_nr_empty_pop_pages -= occ_pages; + spin_unlock_irqrestore(&pcpu_lock, flags); + } if (pcpu_nr_empty_pop_pages < PCPU_EMPTY_POP_PAGES_LOW) pcpu_schedule_balance_work(); diff --git a/net/9p/client.c b/net/9p/client.c index ea79ee9a7348..f5feac4ff4ec 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -2101,6 +2101,10 @@ int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset) trace_9p_protocol_dump(clnt, req->rc); goto free_and_error; } + if (rsize < count) { + pr_err("bogus RREADDIR count (%d > %d)\n", count, rsize); + count = rsize; + } p9_debug(P9_DEBUG_9P, "<<< RREADDIR count %d\n", count); diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index f7fba74108a9..e24754a0e052 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -29,6 +29,7 @@ EXPORT_SYMBOL(br_should_route_hook); static int br_netif_receive_skb(struct net *net, struct sock *sk, struct sk_buff *skb) { + br_drop_fake_rtable(skb); return netif_receive_skb(skb); } diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c index 7ddbe7ec81d6..97fc19f001bf 100644 --- a/net/bridge/br_netfilter_hooks.c +++ b/net/bridge/br_netfilter_hooks.c @@ -516,21 +516,6 @@ static unsigned int br_nf_pre_routing(void *priv, } -/* PF_BRIDGE/LOCAL_IN ************************************************/ -/* The packet is locally destined, which requires a real - * dst_entry, so detach the fake one. On the way up, the - * packet would pass through PRE_ROUTING again (which already - * took place when the packet entered the bridge), but we - * register an IPv4 PRE_ROUTING 'sabotage' hook that will - * prevent this from happening. */ -static unsigned int br_nf_local_in(void *priv, - struct sk_buff *skb, - const struct nf_hook_state *state) -{ - br_drop_fake_rtable(skb); - return NF_ACCEPT; -} - /* PF_BRIDGE/FORWARD *************************************************/ static int br_nf_forward_finish(struct net *net, struct sock *sk, struct sk_buff *skb) { @@ -901,12 +886,6 @@ static struct nf_hook_ops br_nf_ops[] __read_mostly = { .priority = NF_BR_PRI_BRNF, }, { - .hook = br_nf_local_in, - .pf = NFPROTO_BRIDGE, - .hooknum = NF_BR_LOCAL_IN, - .priority = NF_BR_PRI_BRNF, - }, - { .hook = br_nf_forward_ip, .pf = NFPROTO_BRIDGE, .hooknum = NF_BR_FORWARD, diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index b8d927c56494..a6b2f2138c9d 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -7,6 +7,7 @@ #include <linux/kthread.h> #include <linux/net.h> #include <linux/nsproxy.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/socket.h> #include <linux/string.h> @@ -478,11 +479,16 @@ static int ceph_tcp_connect(struct ceph_connection *con) { struct sockaddr_storage *paddr = &con->peer_addr.in_addr; struct socket *sock; + unsigned int noio_flag; int ret; BUG_ON(con->sock); + + /* sock_create_kern() allocates with GFP_KERNEL */ + noio_flag = memalloc_noio_save(); ret = sock_create_kern(read_pnet(&con->msgr->net), paddr->ss_family, SOCK_STREAM, IPPROTO_TCP, &sock); + memalloc_noio_restore(noio_flag); if (ret) return ret; sock->sk->sk_allocation = GFP_NOFS; diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c index ddc3573894b0..bc95e48d5cfb 100644 --- a/net/ceph/osdmap.c +++ b/net/ceph/osdmap.c @@ -1265,7 +1265,6 @@ static int decode_new_up_state_weight(void **p, void *end, if ((map->osd_state[osd] & CEPH_OSD_EXISTS) && (xorstate & CEPH_OSD_EXISTS)) { pr_info("osd%d does not exist\n", osd); - map->osd_weight[osd] = CEPH_OSD_IN; ret = set_primary_affinity(map, osd, CEPH_OSD_DEFAULT_PRIMARY_AFFINITY); if (ret) diff --git a/net/core/dev.c b/net/core/dev.c index 51aed87e8eec..2587d7f30191 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1679,27 +1679,54 @@ EXPORT_SYMBOL_GPL(net_dec_ingress_queue); static struct static_key netstamp_needed __read_mostly; #ifdef HAVE_JUMP_LABEL static atomic_t netstamp_needed_deferred; +static atomic_t netstamp_wanted; static void netstamp_clear(struct work_struct *work) { int deferred = atomic_xchg(&netstamp_needed_deferred, 0); + int wanted; - while (deferred--) - static_key_slow_dec(&netstamp_needed); + wanted = atomic_add_return(deferred, &netstamp_wanted); + if (wanted > 0) + static_key_enable(&netstamp_needed); + else + static_key_disable(&netstamp_needed); } static DECLARE_WORK(netstamp_work, netstamp_clear); #endif void net_enable_timestamp(void) { +#ifdef HAVE_JUMP_LABEL + int wanted; + + while (1) { + wanted = atomic_read(&netstamp_wanted); + if (wanted <= 0) + break; + if (atomic_cmpxchg(&netstamp_wanted, wanted, wanted + 1) == wanted) + return; + } + atomic_inc(&netstamp_needed_deferred); + schedule_work(&netstamp_work); +#else static_key_slow_inc(&netstamp_needed); +#endif } EXPORT_SYMBOL(net_enable_timestamp); void net_disable_timestamp(void) { #ifdef HAVE_JUMP_LABEL - /* net_disable_timestamp() can be called from non process context */ - atomic_inc(&netstamp_needed_deferred); + int wanted; + + while (1) { + wanted = atomic_read(&netstamp_wanted); + if (wanted <= 1) + break; + if (atomic_cmpxchg(&netstamp_wanted, wanted, wanted - 1) == wanted) + return; + } + atomic_dec(&netstamp_needed_deferred); schedule_work(&netstamp_work); #else static_key_slow_dec(&netstamp_needed); diff --git a/net/core/neighbour.c b/net/core/neighbour.c index f1e575d7f21a..54ab748238d1 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -859,7 +859,8 @@ static void neigh_probe(struct neighbour *neigh) if (skb) skb = skb_clone(skb, GFP_ATOMIC); write_unlock(&neigh->lock); - neigh->ops->solicit(neigh, skb); + if (neigh->ops->solicit) + neigh->ops->solicit(neigh, skb); atomic_inc(&neigh->probes); kfree_skb(skb); } diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 94acfc89ad97..440aa9f6e0a8 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -105,15 +105,21 @@ static void queue_process(struct work_struct *work) while ((skb = skb_dequeue(&npinfo->txq))) { struct net_device *dev = skb->dev; struct netdev_queue *txq; + unsigned int q_index; if (!netif_device_present(dev) || !netif_running(dev)) { kfree_skb(skb); continue; } - txq = skb_get_tx_queue(dev, skb); - local_irq_save(flags); + /* check if skb->queue_mapping is still valid */ + q_index = skb_get_queue_mapping(skb); + if (unlikely(q_index >= dev->real_num_tx_queues)) { + q_index = q_index % dev->real_num_tx_queues; + skb_set_queue_mapping(skb, q_index); + } + txq = netdev_get_tx_queue(dev, q_index); HARD_TX_LOCK(dev, txq, smp_processor_id()); if (netif_xmit_frozen_or_stopped(txq) || netpoll_start_xmit(skb, dev, txq) != NETDEV_TX_OK) { diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 5dd643d524d6..fedcee8263b6 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -3704,13 +3704,14 @@ void skb_complete_tx_timestamp(struct sk_buff *skb, if (!skb_may_tx_timestamp(sk, false)) return; - /* take a reference to prevent skb_orphan() from freeing the socket */ - sock_hold(sk); - - *skb_hwtstamps(skb) = *hwtstamps; - __skb_complete_tx_timestamp(skb, sk, SCM_TSTAMP_SND); - - sock_put(sk); + /* Take a reference to prevent skb_orphan() from freeing the socket, + * but only if the socket refcount is not zero. + */ + if (likely(atomic_inc_not_zero(&sk->sk_refcnt))) { + *skb_hwtstamps(skb) = *hwtstamps; + __skb_complete_tx_timestamp(skb, sk, SCM_TSTAMP_SND); + sock_put(sk); + } } EXPORT_SYMBOL_GPL(skb_complete_tx_timestamp); @@ -3761,7 +3762,7 @@ void skb_complete_wifi_ack(struct sk_buff *skb, bool acked) { struct sock *sk = skb->sk; struct sock_exterr_skb *serr; - int err; + int err = 1; skb->wifi_acked_valid = 1; skb->wifi_acked = acked; @@ -3771,14 +3772,15 @@ void skb_complete_wifi_ack(struct sk_buff *skb, bool acked) serr->ee.ee_errno = ENOMSG; serr->ee.ee_origin = SO_EE_ORIGIN_TXSTATUS; - /* take a reference to prevent skb_orphan() from freeing the socket */ - sock_hold(sk); - - err = sock_queue_err_skb(sk, skb); + /* Take a reference to prevent skb_orphan() from freeing the socket, + * but only if the socket refcount is not zero. + */ + if (likely(atomic_inc_not_zero(&sk->sk_refcnt))) { + err = sock_queue_err_skb(sk, skb); + sock_put(sk); + } if (err) kfree_skb(skb); - - sock_put(sk); } EXPORT_SYMBOL_GPL(skb_complete_wifi_ack); diff --git a/net/core/sock.c b/net/core/sock.c index a84a154cdf0c..4efaa3b6633d 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1463,6 +1463,11 @@ static void __sk_destruct(struct rcu_head *head) pr_debug("%s: optmem leakage (%d bytes) detected\n", __func__, atomic_read(&sk->sk_omem_alloc)); + if (sk->sk_frag.page) { + put_page(sk->sk_frag.page); + sk->sk_frag.page = NULL; + } + if (sk->sk_peer_cred) put_cred(sk->sk_peer_cred); put_pid(sk->sk_peer_pid); @@ -1564,6 +1569,12 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority) is_charged = sk_filter_charge(newsk, filter); if (unlikely(!is_charged || xfrm_sk_clone_policy(newsk, sk))) { + /* We need to make sure that we don't uncharge the new + * socket if we couldn't charge it in the first place + * as otherwise we uncharge the parent's filter. + */ + if (!is_charged) + RCU_INIT_POINTER(newsk->sk_filter, NULL); /* It is still raw copy of parent, so invalidate * destructor and make plain sk_free() */ newsk->sk_destruct = NULL; @@ -2706,11 +2717,6 @@ void sk_common_release(struct sock *sk) sk_refcnt_debug_release(sk); - if (sk->sk_frag.page) { - put_page(sk->sk_frag.page); - sk->sk_frag.page = NULL; - } - sock_put(sk); } EXPORT_SYMBOL(sk_common_release); diff --git a/net/dccp/ccids/ccid2.c b/net/dccp/ccids/ccid2.c index f053198e730c..5e3a7302f774 100644 --- a/net/dccp/ccids/ccid2.c +++ b/net/dccp/ccids/ccid2.c @@ -749,6 +749,7 @@ static void ccid2_hc_tx_exit(struct sock *sk) for (i = 0; i < hc->tx_seqbufc; i++) kfree(hc->tx_seqbuf[i]); hc->tx_seqbufc = 0; + dccp_ackvec_parsed_cleanup(&hc->tx_av_chunks); } static void ccid2_hc_rx_packet_recv(struct sock *sk, struct sk_buff *skb) diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 0759f5b9180e..6467bf392e1b 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -289,7 +289,8 @@ static void dccp_v4_err(struct sk_buff *skb, u32 info) switch (type) { case ICMP_REDIRECT: - dccp_do_redirect(skb, sk); + if (!sock_owned_by_user(sk)) + dccp_do_redirect(skb, sk); goto out; case ICMP_SOURCE_QUENCH: /* Just silently ignore these. */ diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 27c4e81efa24..8113ad58fcb4 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -122,10 +122,12 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, np = inet6_sk(sk); if (type == NDISC_REDIRECT) { - struct dst_entry *dst = __sk_dst_check(sk, np->dst_cookie); + if (!sock_owned_by_user(sk)) { + struct dst_entry *dst = __sk_dst_check(sk, np->dst_cookie); - if (dst) - dst->ops->redirect(dst, sk, skb); + if (dst) + dst->ops->redirect(dst, sk, skb); + } goto out; } diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c index 1994f8af646b..68eed344b471 100644 --- a/net/dccp/minisocks.c +++ b/net/dccp/minisocks.c @@ -122,6 +122,7 @@ struct sock *dccp_create_openreq_child(const struct sock *sk, /* It is still raw copy of parent, so invalidate * destructor and make plain sk_free() */ newsk->sk_destruct = NULL; + bh_unlock_sock(newsk); sk_free(newsk); return NULL; } @@ -145,6 +146,13 @@ struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb, struct dccp_request_sock *dreq = dccp_rsk(req); bool own_req; + /* TCP/DCCP listeners became lockless. + * DCCP stores complex state in its request_sock, so we need + * a protection for them, now this code runs without being protected + * by the parent (listener) lock. + */ + spin_lock_bh(&dreq->dreq_lock); + /* Check for retransmitted REQUEST */ if (dccp_hdr(skb)->dccph_type == DCCP_PKT_REQUEST) { @@ -159,7 +167,7 @@ struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb, inet_rtx_syn_ack(sk, req); } /* Network Duplicate, discard packet */ - return NULL; + goto out; } DCCP_SKB_CB(skb)->dccpd_reset_code = DCCP_RESET_CODE_PACKET_ERROR; @@ -185,20 +193,20 @@ struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb, child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL, req, &own_req); - if (!child) - goto listen_overflow; - - return inet_csk_complete_hashdance(sk, child, req, own_req); + if (child) { + child = inet_csk_complete_hashdance(sk, child, req, own_req); + goto out; + } -listen_overflow: - dccp_pr_debug("listen_overflow!\n"); DCCP_SKB_CB(skb)->dccpd_reset_code = DCCP_RESET_CODE_TOO_BUSY; drop: if (dccp_hdr(skb)->dccph_type != DCCP_PKT_RESET) req->rsk_ops->send_reset(sk, skb); inet_csk_reqsk_queue_drop(sk, req); - return NULL; +out: + spin_unlock_bh(&dreq->dreq_lock); + return child; } EXPORT_SYMBOL_GPL(dccp_check_req); @@ -249,6 +257,7 @@ int dccp_reqsk_init(struct request_sock *req, { struct dccp_request_sock *dreq = dccp_rsk(req); + spin_lock_init(&dreq->dreq_lock); inet_rsk(req)->ir_rmt_port = dccp_hdr(skb)->dccph_sport; inet_rsk(req)->ir_num = ntohs(dccp_hdr(skb)->dccph_dport); inet_rsk(req)->acked = 0; diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 7e30c7b50a28..a353d1d92f01 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -1081,7 +1081,8 @@ static void nl_fib_input(struct sk_buff *skb) net = sock_net(skb->sk); nlh = nlmsg_hdr(skb); - if (skb->len < NLMSG_HDRLEN || skb->len < nlh->nlmsg_len || + if (skb->len < nlmsg_total_size(sizeof(*frn)) || + skb->len < nlh->nlmsg_len || nlmsg_len(nlh) < sizeof(*frn)) return; diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 3a2b21e22629..72e1e831589a 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -154,17 +154,18 @@ void ping_hash(struct sock *sk) void ping_unhash(struct sock *sk) { struct inet_sock *isk = inet_sk(sk); + pr_debug("ping_unhash(isk=%p,isk->num=%u)\n", isk, isk->inet_num); + write_lock_bh(&ping_table.lock); if (sk_hashed(sk)) { - write_lock_bh(&ping_table.lock); hlist_nulls_del(&sk->sk_nulls_node); sk_nulls_node_init(&sk->sk_nulls_node); sock_put(sk); isk->inet_num = 0; isk->inet_sport = 0; sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); - write_unlock_bh(&ping_table.lock); } + write_unlock_bh(&ping_table.lock); } EXPORT_SYMBOL_GPL(ping_unhash); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 6ace04d14e30..7e31491e9396 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -792,6 +792,7 @@ static void ip_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buf struct rtable *rt; struct flowi4 fl4; const struct iphdr *iph = (const struct iphdr *) skb->data; + struct net *net = dev_net(skb->dev); int oif = skb->dev->ifindex; u8 tos = RT_TOS(iph->tos); u8 prot = iph->protocol; @@ -799,7 +800,7 @@ static void ip_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buf rt = (struct rtable *) dst; - __build_flow_key(sock_net(sk), &fl4, sk, iph, oif, tos, prot, mark, 0); + __build_flow_key(net, &fl4, sk, iph, oif, tos, prot, mark, 0); __ip_do_redirect(rt, skb, &fl4, true); } @@ -1963,6 +1964,7 @@ int ip_route_input_noref(struct sk_buff *skb, __be32 daddr, __be32 saddr, { int res; + tos &= IPTOS_RT_MASK; rcu_read_lock(); /* Multicast recognition logic is moved from route cache to here. @@ -2569,7 +2571,7 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) skb_reset_network_header(skb); /* Bugfix: need to give ip_route_input enough of an IP header to not gag. */ - ip_hdr(skb)->protocol = IPPROTO_ICMP; + ip_hdr(skb)->protocol = IPPROTO_UDP; skb_reserve(skb, MAX_HEADER + sizeof(struct iphdr)); src = tb[RTA_SRC] ? nla_get_in_addr(tb[RTA_SRC]) : 0; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 4ab7735e43ab..62815497f29c 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2269,6 +2269,7 @@ int tcp_disconnect(struct sock *sk, int flags) tcp_init_send_head(sk); memset(&tp->rx_opt, 0, sizeof(tp->rx_opt)); __sk_dst_reset(sk); + tcp_saved_syn_free(tp); WARN_ON(inet->inet_num && !icsk->icsk_bind_hash); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index e7e227f6760f..3f87c731477f 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5437,6 +5437,7 @@ void tcp_finish_connect(struct sock *sk, struct sk_buff *skb) struct inet_connection_sock *icsk = inet_csk(sk); tcp_set_state(sk, TCP_ESTABLISHED); + icsk->icsk_ack.lrcvtime = tcp_time_stamp; if (skb) { icsk->icsk_af_ops->sk_rx_dst_set(sk, skb); @@ -5649,7 +5650,6 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, * to stand against the temptation 8) --ANK */ inet_csk_schedule_ack(sk); - icsk->icsk_ack.lrcvtime = tcp_time_stamp; tcp_enter_quickack_mode(sk); inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK, TCP_DELACK_MAX, TCP_RTO_MAX); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 51a77e20f6c6..1660613ddae4 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -271,10 +271,13 @@ EXPORT_SYMBOL(tcp_v4_connect); */ void tcp_v4_mtu_reduced(struct sock *sk) { - struct dst_entry *dst; struct inet_sock *inet = inet_sk(sk); - u32 mtu = tcp_sk(sk)->mtu_info; + struct dst_entry *dst; + u32 mtu; + if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE)) + return; + mtu = tcp_sk(sk)->mtu_info; dst = inet_csk_update_pmtu(sk, mtu); if (!dst) return; @@ -420,7 +423,8 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) switch (type) { case ICMP_REDIRECT: - do_redirect(icmp_skb, sk); + if (!sock_owned_by_user(sk)) + do_redirect(icmp_skb, sk); goto out; case ICMP_SOURCE_QUENCH: /* Just silently ignore these. */ diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 9475a2748a9a..019db68bdb9f 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -472,6 +472,7 @@ struct sock *tcp_create_openreq_child(const struct sock *sk, newtp->mdev_us = jiffies_to_usecs(TCP_TIMEOUT_INIT); newtp->rtt_min[0].rtt = ~0U; newicsk->icsk_rto = TCP_TIMEOUT_INIT; + newicsk->icsk_ack.lrcvtime = tcp_time_stamp; newtp->packets_out = 0; newtp->retrans_out = 0; diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index ce20968de667..4aef80d30fab 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -257,7 +257,8 @@ void tcp_delack_timer_handler(struct sock *sk) sk_mem_reclaim_partial(sk); - if (sk->sk_state == TCP_CLOSE || !(icsk->icsk_ack.pending & ICSK_ACK_TIMER)) + if (((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN)) || + !(icsk->icsk_ack.pending & ICSK_ACK_TIMER)) goto out; if (time_after(icsk->icsk_ack.timeout, jiffies)) { @@ -538,7 +539,8 @@ void tcp_write_timer_handler(struct sock *sk) struct inet_connection_sock *icsk = inet_csk(sk); int event; - if (sk->sk_state == TCP_CLOSE || !icsk->icsk_pending) + if (((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN)) || + !icsk->icsk_pending) goto out; if (time_after(icsk->icsk_timeout, jiffies)) { diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 01455f492e17..3d72aeffa3f1 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -112,6 +112,27 @@ static inline u32 cstamp_delta(unsigned long cstamp) return (cstamp - INITIAL_JIFFIES) * 100UL / HZ; } +static inline s32 rfc3315_s14_backoff_init(s32 irt) +{ + /* multiply 'initial retransmission time' by 0.9 .. 1.1 */ + u64 tmp = (900000 + prandom_u32() % 200001) * (u64)irt; + do_div(tmp, 1000000); + return (s32)tmp; +} + +static inline s32 rfc3315_s14_backoff_update(s32 rt, s32 mrt) +{ + /* multiply 'retransmission timeout' by 1.9 .. 2.1 */ + u64 tmp = (1900000 + prandom_u32() % 200001) * (u64)rt; + do_div(tmp, 1000000); + if ((s32)tmp > mrt) { + /* multiply 'maximum retransmission time' by 0.9 .. 1.1 */ + tmp = (900000 + prandom_u32() % 200001) * (u64)mrt; + do_div(tmp, 1000000); + } + return (s32)tmp; +} + #ifdef CONFIG_SYSCTL static int addrconf_sysctl_register(struct inet6_dev *idev); static void addrconf_sysctl_unregister(struct inet6_dev *idev); @@ -187,6 +208,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { .dad_transmits = 1, .rtr_solicits = MAX_RTR_SOLICITATIONS, .rtr_solicit_interval = RTR_SOLICITATION_INTERVAL, + .rtr_solicit_max_interval = RTR_SOLICITATION_MAX_INTERVAL, .rtr_solicit_delay = MAX_RTR_SOLICITATION_DELAY, .use_tempaddr = 0, .temp_valid_lft = TEMP_VALID_LIFETIME, @@ -202,6 +224,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { .accept_ra_rtr_pref = 1, .rtr_probe_interval = 60 * HZ, #ifdef CONFIG_IPV6_ROUTE_INFO + .accept_ra_rt_info_min_plen = 0, .accept_ra_rt_info_max_plen = 0, #endif #endif @@ -233,6 +256,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { .dad_transmits = 1, .rtr_solicits = MAX_RTR_SOLICITATIONS, .rtr_solicit_interval = RTR_SOLICITATION_INTERVAL, + .rtr_solicit_max_interval = RTR_SOLICITATION_MAX_INTERVAL, .rtr_solicit_delay = MAX_RTR_SOLICITATION_DELAY, .use_tempaddr = 0, .temp_valid_lft = TEMP_VALID_LIFETIME, @@ -248,6 +272,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { .accept_ra_rtr_pref = 1, .rtr_probe_interval = 60 * HZ, #ifdef CONFIG_IPV6_ROUTE_INFO + .accept_ra_rt_info_min_plen = 0, .accept_ra_rt_info_max_plen = 0, #endif #endif @@ -3502,7 +3527,7 @@ static void addrconf_rs_timer(unsigned long data) if (idev->if_flags & IF_RA_RCVD) goto out; - if (idev->rs_probes++ < idev->cnf.rtr_solicits) { + if (idev->rs_probes++ < idev->cnf.rtr_solicits || idev->cnf.rtr_solicits < 0) { write_unlock(&idev->lock); if (!ipv6_get_lladdr(dev, &lladdr, IFA_F_TENTATIVE)) ndisc_send_rs(dev, &lladdr, @@ -3511,11 +3536,13 @@ static void addrconf_rs_timer(unsigned long data) goto put; write_lock(&idev->lock); + idev->rs_interval = rfc3315_s14_backoff_update( + idev->rs_interval, idev->cnf.rtr_solicit_max_interval); /* The wait after the last probe can be shorter */ addrconf_mod_rs_timer(idev, (idev->rs_probes == idev->cnf.rtr_solicits) ? idev->cnf.rtr_solicit_delay : - idev->cnf.rtr_solicit_interval); + idev->rs_interval); } else { /* * Note: we do not support deprecated "all on-link" @@ -3743,7 +3770,7 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp) send_mld = ifp->scope == IFA_LINK && ipv6_lonely_lladdr(ifp); send_rs = send_mld && ipv6_accept_ra(ifp->idev) && - ifp->idev->cnf.rtr_solicits > 0 && + ifp->idev->cnf.rtr_solicits != 0 && (dev->flags&IFF_LOOPBACK) == 0; read_unlock_bh(&ifp->idev->lock); @@ -3765,10 +3792,11 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp) write_lock_bh(&ifp->idev->lock); spin_lock(&ifp->lock); + ifp->idev->rs_interval = rfc3315_s14_backoff_init( + ifp->idev->cnf.rtr_solicit_interval); ifp->idev->rs_probes = 1; ifp->idev->if_flags |= IF_RS_SENT; - addrconf_mod_rs_timer(ifp->idev, - ifp->idev->cnf.rtr_solicit_interval); + addrconf_mod_rs_timer(ifp->idev, ifp->idev->rs_interval); spin_unlock(&ifp->lock); write_unlock_bh(&ifp->idev->lock); } @@ -4685,6 +4713,8 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, array[DEVCONF_RTR_SOLICITS] = cnf->rtr_solicits; array[DEVCONF_RTR_SOLICIT_INTERVAL] = jiffies_to_msecs(cnf->rtr_solicit_interval); + array[DEVCONF_RTR_SOLICIT_MAX_INTERVAL] = + jiffies_to_msecs(cnf->rtr_solicit_max_interval); array[DEVCONF_RTR_SOLICIT_DELAY] = jiffies_to_msecs(cnf->rtr_solicit_delay); array[DEVCONF_FORCE_MLD_VERSION] = cnf->force_mld_version; @@ -4706,6 +4736,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, array[DEVCONF_RTR_PROBE_INTERVAL] = jiffies_to_msecs(cnf->rtr_probe_interval); #ifdef CONFIG_IPV6_ROUTE_INFO + array[DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN] = cnf->accept_ra_rt_info_min_plen; array[DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN] = cnf->accept_ra_rt_info_max_plen; #endif #endif @@ -4893,7 +4924,7 @@ static int inet6_set_iftoken(struct inet6_dev *idev, struct in6_addr *token) return -EINVAL; if (!ipv6_accept_ra(idev)) return -EINVAL; - if (idev->cnf.rtr_solicits <= 0) + if (idev->cnf.rtr_solicits == 0) return -EINVAL; write_lock_bh(&idev->lock); @@ -4918,8 +4949,10 @@ static int inet6_set_iftoken(struct inet6_dev *idev, struct in6_addr *token) if (update_rs) { idev->if_flags |= IF_RS_SENT; + idev->rs_interval = rfc3315_s14_backoff_init( + idev->cnf.rtr_solicit_interval); idev->rs_probes = 1; - addrconf_mod_rs_timer(idev, idev->cnf.rtr_solicit_interval); + addrconf_mod_rs_timer(idev, idev->rs_interval); } /* Well, that's kinda nasty ... */ @@ -5557,6 +5590,13 @@ static struct addrconf_sysctl_table .proc_handler = proc_dointvec_jiffies, }, { + .procname = "router_solicitation_max_interval", + .data = &ipv6_devconf.rtr_solicit_max_interval, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_jiffies, + }, + { .procname = "router_solicitation_delay", .data = &ipv6_devconf.rtr_solicit_delay, .maxlen = sizeof(int), @@ -5666,6 +5706,13 @@ static struct addrconf_sysctl_table }, #ifdef CONFIG_IPV6_ROUTE_INFO { + .procname = "accept_ra_rt_info_min_plen", + .data = &ipv6_devconf.accept_ra_rt_info_min_plen, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, + { .procname = "accept_ra_rt_info_max_plen", .data = &ipv6_devconf.accept_ra_rt_info_max_plen, .maxlen = sizeof(int), diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 34cf46d74554..85bf86458706 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -903,6 +903,8 @@ add: ins = &rt->dst.rt6_next; iter = *ins; while (iter) { + if (iter->rt6i_metric > rt->rt6i_metric) + break; if (rt6_qualify_for_ecmp(iter)) { *ins = iter->dst.rt6_next; fib6_purge_rt(iter, fn, info->nl_net); diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index cd96a01032a2..cf90a9bf26a3 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -742,13 +742,14 @@ slow_path: * Fragment the datagram. */ - *prevhdr = NEXTHDR_FRAGMENT; troom = rt->dst.dev->needed_tailroom; /* * Keep copying data until we run out. */ while (left > 0) { + u8 *fragnexthdr_offset; + len = left; /* IF: it doesn't fit, use 'mtu' - the data space left */ if (len > mtu) @@ -793,6 +794,10 @@ slow_path: */ skb_copy_from_linear_data(skb, skb_network_header(frag), hlen); + fragnexthdr_offset = skb_network_header(frag); + fragnexthdr_offset += prevhdr - skb_network_header(skb); + *fragnexthdr_offset = NEXTHDR_FRAGMENT; + /* * Build fragment header. */ diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 8b11a49c7dd7..600975c5eacf 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -1049,7 +1049,7 @@ static int ip6_tnl_xmit2(struct sk_buff *skb, struct ip6_tnl *t = netdev_priv(dev); struct net *net = t->net; struct net_device_stats *stats = &t->dev->stats; - struct ipv6hdr *ipv6h = ipv6_hdr(skb); + struct ipv6hdr *ipv6h; struct ipv6_tel_txoption opt; struct dst_entry *dst = NULL, *ndst = NULL; struct net_device *tdev; @@ -1061,26 +1061,28 @@ static int ip6_tnl_xmit2(struct sk_buff *skb, /* NBMA tunnel */ if (ipv6_addr_any(&t->parms.raddr)) { - struct in6_addr *addr6; - struct neighbour *neigh; - int addr_type; + if (skb->protocol == htons(ETH_P_IPV6)) { + struct in6_addr *addr6; + struct neighbour *neigh; + int addr_type; - if (!skb_dst(skb)) - goto tx_err_link_failure; + if (!skb_dst(skb)) + goto tx_err_link_failure; - neigh = dst_neigh_lookup(skb_dst(skb), - &ipv6_hdr(skb)->daddr); - if (!neigh) - goto tx_err_link_failure; + neigh = dst_neigh_lookup(skb_dst(skb), + &ipv6_hdr(skb)->daddr); + if (!neigh) + goto tx_err_link_failure; - addr6 = (struct in6_addr *)&neigh->primary_key; - addr_type = ipv6_addr_type(addr6); + addr6 = (struct in6_addr *)&neigh->primary_key; + addr_type = ipv6_addr_type(addr6); - if (addr_type == IPV6_ADDR_ANY) - addr6 = &ipv6_hdr(skb)->daddr; + if (addr_type == IPV6_ADDR_ANY) + addr6 = &ipv6_hdr(skb)->daddr; - memcpy(&fl6->daddr, addr6, sizeof(fl6->daddr)); - neigh_release(neigh); + memcpy(&fl6->daddr, addr6, sizeof(fl6->daddr)); + neigh_release(neigh); + } } else if (!(t->parms.flags & (IP6_TNL_F_USE_ORIG_TCLASS | IP6_TNL_F_USE_ORIG_FWMARK))) { /* enable the cache only only if the routing decision does diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index 24fb9c0efd00..5b7433887eda 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -681,6 +681,10 @@ vti6_parm_to_user(struct ip6_tnl_parm2 *u, const struct __ip6_tnl_parm *p) u->link = p->link; u->i_key = p->i_key; u->o_key = p->o_key; + if (u->i_key) + u->i_flags |= GRE_KEY; + if (u->o_key) + u->o_flags |= GRE_KEY; u->proto = p->proto; memcpy(u->name, p->name, sizeof(u->name)); diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index d9843e5a667f..8361d73ab653 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -774,7 +774,8 @@ failure: * Delete a VIF entry */ -static int mif6_delete(struct mr6_table *mrt, int vifi, struct list_head *head) +static int mif6_delete(struct mr6_table *mrt, int vifi, int notify, + struct list_head *head) { struct mif_device *v; struct net_device *dev; @@ -820,7 +821,7 @@ static int mif6_delete(struct mr6_table *mrt, int vifi, struct list_head *head) dev->ifindex, &in6_dev->cnf); } - if (v->flags & MIFF_REGISTER) + if ((v->flags & MIFF_REGISTER) && !notify) unregister_netdevice_queue(dev, head); dev_put(dev); @@ -1330,7 +1331,6 @@ static int ip6mr_device_event(struct notifier_block *this, struct mr6_table *mrt; struct mif_device *v; int ct; - LIST_HEAD(list); if (event != NETDEV_UNREGISTER) return NOTIFY_DONE; @@ -1339,10 +1339,9 @@ static int ip6mr_device_event(struct notifier_block *this, v = &mrt->vif6_table[0]; for (ct = 0; ct < mrt->maxvif; ct++, v++) { if (v->dev == dev) - mif6_delete(mrt, ct, &list); + mif6_delete(mrt, ct, 1, NULL); } } - unregister_netdevice_many(&list); return NOTIFY_DONE; } @@ -1551,7 +1550,7 @@ static void mroute_clean_tables(struct mr6_table *mrt, bool all) for (i = 0; i < mrt->maxvif; i++) { if (!all && (mrt->vif6_table[i].flags & VIFF_STATIC)) continue; - mif6_delete(mrt, i, &list); + mif6_delete(mrt, i, 0, &list); } unregister_netdevice_many(&list); @@ -1704,7 +1703,7 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns if (copy_from_user(&mifi, optval, sizeof(mifi_t))) return -EFAULT; rtnl_lock(); - ret = mif6_delete(mrt, mifi, NULL); + ret = mif6_delete(mrt, mifi, 0, NULL); rtnl_unlock(); return ret; diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 84afb9a77278..3452f9037ad4 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1358,6 +1358,8 @@ skip_linkparms: if (ri->prefix_len == 0 && !in6_dev->cnf.accept_ra_defrtr) continue; + if (ri->prefix_len < in6_dev->cnf.accept_ra_rt_info_min_plen) + continue; if (ri->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen) continue; rt6_route_rcv(skb->dev, (u8 *)p, (p->nd_opt_len) << 3, diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index d503b7f373a3..6896830feabb 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -1145,8 +1145,7 @@ static int rawv6_ioctl(struct sock *sk, int cmd, unsigned long arg) spin_lock_bh(&sk->sk_receive_queue.lock); skb = skb_peek(&sk->sk_receive_queue); if (skb) - amount = skb_tail_pointer(skb) - - skb_transport_header(skb); + amount = skb->len; spin_unlock_bh(&sk->sk_receive_queue.lock); return put_user(amount, (int __user *)arg); } diff --git a/net/ipv6/route.c b/net/ipv6/route.c index add152e8352c..ff1499293938 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1756,6 +1756,10 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg) int addr_type; int err = -EINVAL; + /* RTF_PCPU is an internal flag; can not be set by userspace */ + if (cfg->fc_flags & RTF_PCPU) + goto out; + if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128) goto out; #ifndef CONFIG_IPV6_SUBTREES @@ -2082,6 +2086,8 @@ static int ip6_route_del(struct fib6_config *cfg) continue; if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric) continue; + if (cfg->fc_protocol && cfg->fc_protocol != rt->rt6i_protocol) + continue; dst_hold(&rt->dst); read_unlock_bh(&table->tb6_lock); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 108b39967694..8532768b4eaa 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -377,10 +377,12 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, np = inet6_sk(sk); if (type == NDISC_REDIRECT) { - struct dst_entry *dst = __sk_dst_check(sk, np->dst_cookie); + if (!sock_owned_by_user(sk)) { + struct dst_entry *dst = __sk_dst_check(sk, np->dst_cookie); - if (dst) - dst->ops->redirect(dst, sk, skb); + if (dst) + dst->ops->redirect(dst, sk, skb); + } goto out; } diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index ec17cbe8a02b..d3dec414fd44 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -278,7 +278,8 @@ struct l2tp_session *l2tp_session_find(struct net *net, struct l2tp_tunnel *tunn } EXPORT_SYMBOL_GPL(l2tp_session_find); -struct l2tp_session *l2tp_session_find_nth(struct l2tp_tunnel *tunnel, int nth) +struct l2tp_session *l2tp_session_get_nth(struct l2tp_tunnel *tunnel, int nth, + bool do_ref) { int hash; struct l2tp_session *session; @@ -288,6 +289,9 @@ struct l2tp_session *l2tp_session_find_nth(struct l2tp_tunnel *tunnel, int nth) for (hash = 0; hash < L2TP_HASH_SIZE; hash++) { hlist_for_each_entry(session, &tunnel->session_hlist[hash], hlist) { if (++count > nth) { + l2tp_session_inc_refcount(session); + if (do_ref && session->ref) + session->ref(session); read_unlock_bh(&tunnel->hlist_lock); return session; } @@ -298,7 +302,7 @@ struct l2tp_session *l2tp_session_find_nth(struct l2tp_tunnel *tunnel, int nth) return NULL; } -EXPORT_SYMBOL_GPL(l2tp_session_find_nth); +EXPORT_SYMBOL_GPL(l2tp_session_get_nth); /* Lookup a session by interface name. * This is very inefficient but is only used by management interfaces. diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h index 763e8e241ce3..555d962a62d2 100644 --- a/net/l2tp/l2tp_core.h +++ b/net/l2tp/l2tp_core.h @@ -243,7 +243,8 @@ out: struct l2tp_session *l2tp_session_find(struct net *net, struct l2tp_tunnel *tunnel, u32 session_id); -struct l2tp_session *l2tp_session_find_nth(struct l2tp_tunnel *tunnel, int nth); +struct l2tp_session *l2tp_session_get_nth(struct l2tp_tunnel *tunnel, int nth, + bool do_ref); struct l2tp_session *l2tp_session_find_by_ifname(struct net *net, char *ifname); struct l2tp_tunnel *l2tp_tunnel_find(struct net *net, u32 tunnel_id); struct l2tp_tunnel *l2tp_tunnel_find_nth(struct net *net, int nth); diff --git a/net/l2tp/l2tp_debugfs.c b/net/l2tp/l2tp_debugfs.c index 2d6760a2ae34..d100aed3d06f 100644 --- a/net/l2tp/l2tp_debugfs.c +++ b/net/l2tp/l2tp_debugfs.c @@ -53,7 +53,7 @@ static void l2tp_dfs_next_tunnel(struct l2tp_dfs_seq_data *pd) static void l2tp_dfs_next_session(struct l2tp_dfs_seq_data *pd) { - pd->session = l2tp_session_find_nth(pd->tunnel, pd->session_idx); + pd->session = l2tp_session_get_nth(pd->tunnel, pd->session_idx, true); pd->session_idx++; if (pd->session == NULL) { @@ -238,10 +238,14 @@ static int l2tp_dfs_seq_show(struct seq_file *m, void *v) } /* Show the tunnel or session context */ - if (pd->session == NULL) + if (!pd->session) { l2tp_dfs_seq_tunnel_show(m, pd->tunnel); - else + } else { l2tp_dfs_seq_session_show(m, pd->session); + if (pd->session->deref) + pd->session->deref(pd->session); + l2tp_session_dec_refcount(pd->session); + } out: return 0; diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c index 445b7cd0826a..48ab93842322 100644 --- a/net/l2tp/l2tp_ip.c +++ b/net/l2tp/l2tp_ip.c @@ -383,7 +383,7 @@ static int l2tp_ip_backlog_recv(struct sock *sk, struct sk_buff *skb) drop: IP_INC_STATS(sock_net(sk), IPSTATS_MIB_INDISCARDS); kfree_skb(skb); - return -1; + return 0; } /* Userspace will call sendmsg() on the tunnel socket to send L2TP diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c index 2caaa84ce92d..665cc74df5c5 100644 --- a/net/l2tp/l2tp_netlink.c +++ b/net/l2tp/l2tp_netlink.c @@ -827,7 +827,7 @@ static int l2tp_nl_cmd_session_dump(struct sk_buff *skb, struct netlink_callback goto out; } - session = l2tp_session_find_nth(tunnel, si); + session = l2tp_session_get_nth(tunnel, si, false); if (session == NULL) { ti++; tunnel = NULL; @@ -837,8 +837,11 @@ static int l2tp_nl_cmd_session_dump(struct sk_buff *skb, struct netlink_callback if (l2tp_nl_session_send(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, - session, L2TP_CMD_SESSION_GET) < 0) + session, L2TP_CMD_SESSION_GET) < 0) { + l2tp_session_dec_refcount(session); break; + } + l2tp_session_dec_refcount(session); si++; } diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c index 1ad18c55064c..8ab9c5d74416 100644 --- a/net/l2tp/l2tp_ppp.c +++ b/net/l2tp/l2tp_ppp.c @@ -467,6 +467,10 @@ static void pppol2tp_session_close(struct l2tp_session *session) static void pppol2tp_session_destruct(struct sock *sk) { struct l2tp_session *session = sk->sk_user_data; + + skb_queue_purge(&sk->sk_receive_queue); + skb_queue_purge(&sk->sk_write_queue); + if (session) { sk->sk_user_data = NULL; BUG_ON(session->magic != L2TP_SESSION_MAGIC); @@ -505,9 +509,6 @@ static int pppol2tp_release(struct socket *sock) l2tp_session_queue_purge(session); sock_put(sk); } - skb_queue_purge(&sk->sk_receive_queue); - skb_queue_purge(&sk->sk_write_queue); - release_sock(sk); /* This will delete the session context via @@ -1574,7 +1575,7 @@ static void pppol2tp_next_tunnel(struct net *net, struct pppol2tp_seq_data *pd) static void pppol2tp_next_session(struct net *net, struct pppol2tp_seq_data *pd) { - pd->session = l2tp_session_find_nth(pd->tunnel, pd->session_idx); + pd->session = l2tp_session_get_nth(pd->tunnel, pd->session_idx, true); pd->session_idx++; if (pd->session == NULL) { @@ -1701,10 +1702,14 @@ static int pppol2tp_seq_show(struct seq_file *m, void *v) /* Show the tunnel or session context. */ - if (pd->session == NULL) + if (!pd->session) { pppol2tp_seq_tunnel_show(m, pd->tunnel); - else + } else { pppol2tp_seq_session_show(m, pd->session); + if (pd->session->deref) + pd->session->deref(pd->session); + l2tp_session_dec_refcount(pd->session); + } out: return 0; @@ -1863,4 +1868,4 @@ MODULE_DESCRIPTION("PPP over L2TP over UDP"); MODULE_LICENSE("GPL"); MODULE_VERSION(PPPOL2TP_DRV_VERSION); MODULE_ALIAS("pppox-proto-" __stringify(PX_PROTO_OL2TP)); -MODULE_ALIAS_L2TP_PWTYPE(11); +MODULE_ALIAS_L2TP_PWTYPE(7); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 4f4c45ba7b70..25b5bacaa434 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -3397,6 +3397,27 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx) !ether_addr_equal(bssid, hdr->addr1)) return false; } + + /* + * 802.11-2016 Table 9-26 says that for data frames, A1 must be + * the BSSID - we've checked that already but may have accepted + * the wildcard (ff:ff:ff:ff:ff:ff). + * + * It also says: + * The BSSID of the Data frame is determined as follows: + * a) If the STA is contained within an AP or is associated + * with an AP, the BSSID is the address currently in use + * by the STA contained in the AP. + * + * So we should not accept data frames with an address that's + * multicast. + * + * Accepting it also opens a security problem because stations + * could encrypt it with the GTK and inject traffic that way. + */ + if (ieee80211_is_data(hdr->frame_control) && multicast) + return false; + return true; case NL80211_IFTYPE_WDS: if (bssid || !ieee80211_is_data(hdr->frame_control)) diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index 881bc2072809..52cfc4478511 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -1567,6 +1567,7 @@ static void mpls_net_exit(struct net *net) for (index = 0; index < platform_labels; index++) { struct mpls_route *rt = rtnl_dereference(platform_label[index]); RCU_INIT_POINTER(platform_label[index], NULL); + mpls_notify_route(net, index, rt, NULL, NULL); mpls_rt_free(rt); } rtnl_unlock(); diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index ececa65868ef..e0d8e9ad315b 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1802,8 +1802,11 @@ ret_res: } #ifdef DDEBUG -/* This function is not in xt_qtaguid_print.c because of locks visibility */ -static void prdebug_full_state(int indent_level, const char *fmt, ...) +/* + * This function is not in xt_qtaguid_print.c because of locks visibility. + * The lock of sock_tag_list must be aquired before calling this function + */ +static void prdebug_full_state_locked(int indent_level, const char *fmt, ...) { va_list args; char *fmt_buff; @@ -1824,16 +1827,12 @@ static void prdebug_full_state(int indent_level, const char *fmt, ...) kfree(buff); va_end(args); - spin_lock_bh(&sock_tag_list_lock); prdebug_sock_tag_tree(indent_level, &sock_tag_tree); - spin_unlock_bh(&sock_tag_list_lock); - spin_lock_bh(&sock_tag_list_lock); spin_lock_bh(&uid_tag_data_tree_lock); prdebug_uid_tag_data_tree(indent_level, &uid_tag_data_tree); prdebug_proc_qtu_data_tree(indent_level, &proc_qtu_data_tree); spin_unlock_bh(&uid_tag_data_tree_lock); - spin_unlock_bh(&sock_tag_list_lock); spin_lock_bh(&iface_stat_list_lock); prdebug_iface_stat_list(indent_level, &iface_stat_list); @@ -1842,7 +1841,7 @@ static void prdebug_full_state(int indent_level, const char *fmt, ...) pr_debug("qtaguid: %s(): }\n", __func__); } #else -static void prdebug_full_state(int indent_level, const char *fmt, ...) {} +static void prdebug_full_state_locked(int indent_level, const char *fmt, ...) {} #endif struct proc_ctrl_print_info { @@ -1965,8 +1964,11 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v) (u64)atomic64_read(&qtu_events.match_no_sk), (u64)atomic64_read(&qtu_events.match_no_sk_file)); - /* Count the following as part of the last item_index */ - prdebug_full_state(0, "proc ctrl"); + /* Count the following as part of the last item_index. No need + * to lock the sock_tag_list here since it is already locked when + * starting the seq_file operation + */ + prdebug_full_state_locked(0, "proc ctrl"); } return 0; @@ -2875,8 +2877,10 @@ static int qtudev_release(struct inode *inode, struct file *file) sock_tag_tree_erase(&st_to_free_tree); - prdebug_full_state(0, "%s(): pid=%u tgid=%u", __func__, + spin_lock_bh(&sock_tag_list_lock); + prdebug_full_state_locked(0, "%s(): pid=%u tgid=%u", __func__, current->pid, current->tgid); + spin_unlock_bh(&sock_tag_list_lock); return 0; } diff --git a/net/netlink/Kconfig b/net/netlink/Kconfig index 2c5e95e9bfbd..5d6e8c05b3d4 100644 --- a/net/netlink/Kconfig +++ b/net/netlink/Kconfig @@ -2,15 +2,6 @@ # Netlink Sockets # -config NETLINK_MMAP - bool "NETLINK: mmaped IO" - ---help--- - This option enables support for memory mapped netlink IO. This - reduces overhead by avoiding copying data between kernel- and - userspace. - - If unsure, say N. - config NETLINK_DIAG tristate "NETLINK: socket monitoring interface" default n diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index f4dd706c6cd1..96fe1c103bf9 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -225,7 +225,7 @@ static int __netlink_deliver_tap_skb(struct sk_buff *skb, dev_hold(dev); - if (netlink_skb_is_mmaped(skb) || is_vmalloc_addr(skb->head)) + if (is_vmalloc_addr(skb->head)) nskb = netlink_to_full_skb(skb, GFP_ATOMIC); else nskb = skb_clone(skb, GFP_ATOMIC); @@ -300,610 +300,8 @@ static void netlink_rcv_wake(struct sock *sk) wake_up_interruptible(&nlk->wait); } -#ifdef CONFIG_NETLINK_MMAP -static bool netlink_rx_is_mmaped(struct sock *sk) -{ - return nlk_sk(sk)->rx_ring.pg_vec != NULL; -} - -static bool netlink_tx_is_mmaped(struct sock *sk) -{ - return nlk_sk(sk)->tx_ring.pg_vec != NULL; -} - -static __pure struct page *pgvec_to_page(const void *addr) -{ - if (is_vmalloc_addr(addr)) - return vmalloc_to_page(addr); - else - return virt_to_page(addr); -} - -static void free_pg_vec(void **pg_vec, unsigned int order, unsigned int len) -{ - unsigned int i; - - for (i = 0; i < len; i++) { - if (pg_vec[i] != NULL) { - if (is_vmalloc_addr(pg_vec[i])) - vfree(pg_vec[i]); - else - free_pages((unsigned long)pg_vec[i], order); - } - } - kfree(pg_vec); -} - -static void *alloc_one_pg_vec_page(unsigned long order) -{ - void *buffer; - gfp_t gfp_flags = GFP_KERNEL | __GFP_COMP | __GFP_ZERO | - __GFP_NOWARN | __GFP_NORETRY; - - buffer = (void *)__get_free_pages(gfp_flags, order); - if (buffer != NULL) - return buffer; - - buffer = vzalloc((1 << order) * PAGE_SIZE); - if (buffer != NULL) - return buffer; - - gfp_flags &= ~__GFP_NORETRY; - return (void *)__get_free_pages(gfp_flags, order); -} - -static void **alloc_pg_vec(struct netlink_sock *nlk, - struct nl_mmap_req *req, unsigned int order) -{ - unsigned int block_nr = req->nm_block_nr; - unsigned int i; - void **pg_vec; - - pg_vec = kcalloc(block_nr, sizeof(void *), GFP_KERNEL); - if (pg_vec == NULL) - return NULL; - - for (i = 0; i < block_nr; i++) { - pg_vec[i] = alloc_one_pg_vec_page(order); - if (pg_vec[i] == NULL) - goto err1; - } - - return pg_vec; -err1: - free_pg_vec(pg_vec, order, block_nr); - return NULL; -} - - -static void -__netlink_set_ring(struct sock *sk, struct nl_mmap_req *req, bool tx_ring, void **pg_vec, - unsigned int order) -{ - struct netlink_sock *nlk = nlk_sk(sk); - struct sk_buff_head *queue; - struct netlink_ring *ring; - - queue = tx_ring ? &sk->sk_write_queue : &sk->sk_receive_queue; - ring = tx_ring ? &nlk->tx_ring : &nlk->rx_ring; - - spin_lock_bh(&queue->lock); - - ring->frame_max = req->nm_frame_nr - 1; - ring->head = 0; - ring->frame_size = req->nm_frame_size; - ring->pg_vec_pages = req->nm_block_size / PAGE_SIZE; - - swap(ring->pg_vec_len, req->nm_block_nr); - swap(ring->pg_vec_order, order); - swap(ring->pg_vec, pg_vec); - - __skb_queue_purge(queue); - spin_unlock_bh(&queue->lock); - - WARN_ON(atomic_read(&nlk->mapped)); - - if (pg_vec) - free_pg_vec(pg_vec, order, req->nm_block_nr); -} - -static int netlink_set_ring(struct sock *sk, struct nl_mmap_req *req, - bool tx_ring) -{ - struct netlink_sock *nlk = nlk_sk(sk); - struct netlink_ring *ring; - void **pg_vec = NULL; - unsigned int order = 0; - - ring = tx_ring ? &nlk->tx_ring : &nlk->rx_ring; - - if (atomic_read(&nlk->mapped)) - return -EBUSY; - if (atomic_read(&ring->pending)) - return -EBUSY; - - if (req->nm_block_nr) { - if (ring->pg_vec != NULL) - return -EBUSY; - - if ((int)req->nm_block_size <= 0) - return -EINVAL; - if (!PAGE_ALIGNED(req->nm_block_size)) - return -EINVAL; - if (req->nm_frame_size < NL_MMAP_HDRLEN) - return -EINVAL; - if (!IS_ALIGNED(req->nm_frame_size, NL_MMAP_MSG_ALIGNMENT)) - return -EINVAL; - - ring->frames_per_block = req->nm_block_size / - req->nm_frame_size; - if (ring->frames_per_block == 0) - return -EINVAL; - if (ring->frames_per_block * req->nm_block_nr != - req->nm_frame_nr) - return -EINVAL; - - order = get_order(req->nm_block_size); - pg_vec = alloc_pg_vec(nlk, req, order); - if (pg_vec == NULL) - return -ENOMEM; - } else { - if (req->nm_frame_nr) - return -EINVAL; - } - - mutex_lock(&nlk->pg_vec_lock); - if (atomic_read(&nlk->mapped) == 0) { - __netlink_set_ring(sk, req, tx_ring, pg_vec, order); - mutex_unlock(&nlk->pg_vec_lock); - return 0; - } - - mutex_unlock(&nlk->pg_vec_lock); - - if (pg_vec) - free_pg_vec(pg_vec, order, req->nm_block_nr); - - return -EBUSY; -} - -static void netlink_mm_open(struct vm_area_struct *vma) -{ - struct file *file = vma->vm_file; - struct socket *sock = file->private_data; - struct sock *sk = sock->sk; - - if (sk) - atomic_inc(&nlk_sk(sk)->mapped); -} - -static void netlink_mm_close(struct vm_area_struct *vma) -{ - struct file *file = vma->vm_file; - struct socket *sock = file->private_data; - struct sock *sk = sock->sk; - - if (sk) - atomic_dec(&nlk_sk(sk)->mapped); -} - -static const struct vm_operations_struct netlink_mmap_ops = { - .open = netlink_mm_open, - .close = netlink_mm_close, -}; - -static int netlink_mmap(struct file *file, struct socket *sock, - struct vm_area_struct *vma) -{ - struct sock *sk = sock->sk; - struct netlink_sock *nlk = nlk_sk(sk); - struct netlink_ring *ring; - unsigned long start, size, expected; - unsigned int i; - int err = -EINVAL; - - if (vma->vm_pgoff) - return -EINVAL; - - mutex_lock(&nlk->pg_vec_lock); - - expected = 0; - for (ring = &nlk->rx_ring; ring <= &nlk->tx_ring; ring++) { - if (ring->pg_vec == NULL) - continue; - expected += ring->pg_vec_len * ring->pg_vec_pages * PAGE_SIZE; - } - - if (expected == 0) - goto out; - - size = vma->vm_end - vma->vm_start; - if (size != expected) - goto out; - - start = vma->vm_start; - for (ring = &nlk->rx_ring; ring <= &nlk->tx_ring; ring++) { - if (ring->pg_vec == NULL) - continue; - - for (i = 0; i < ring->pg_vec_len; i++) { - struct page *page; - void *kaddr = ring->pg_vec[i]; - unsigned int pg_num; - - for (pg_num = 0; pg_num < ring->pg_vec_pages; pg_num++) { - page = pgvec_to_page(kaddr); - err = vm_insert_page(vma, start, page); - if (err < 0) - goto out; - start += PAGE_SIZE; - kaddr += PAGE_SIZE; - } - } - } - - atomic_inc(&nlk->mapped); - vma->vm_ops = &netlink_mmap_ops; - err = 0; -out: - mutex_unlock(&nlk->pg_vec_lock); - return err; -} - -static void netlink_frame_flush_dcache(const struct nl_mmap_hdr *hdr, unsigned int nm_len) -{ -#if ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE == 1 - struct page *p_start, *p_end; - - /* First page is flushed through netlink_{get,set}_status */ - p_start = pgvec_to_page(hdr + PAGE_SIZE); - p_end = pgvec_to_page((void *)hdr + NL_MMAP_HDRLEN + nm_len - 1); - while (p_start <= p_end) { - flush_dcache_page(p_start); - p_start++; - } -#endif -} - -static enum nl_mmap_status netlink_get_status(const struct nl_mmap_hdr *hdr) -{ - smp_rmb(); - flush_dcache_page(pgvec_to_page(hdr)); - return hdr->nm_status; -} - -static void netlink_set_status(struct nl_mmap_hdr *hdr, - enum nl_mmap_status status) -{ - smp_mb(); - hdr->nm_status = status; - flush_dcache_page(pgvec_to_page(hdr)); -} - -static struct nl_mmap_hdr * -__netlink_lookup_frame(const struct netlink_ring *ring, unsigned int pos) -{ - unsigned int pg_vec_pos, frame_off; - - pg_vec_pos = pos / ring->frames_per_block; - frame_off = pos % ring->frames_per_block; - - return ring->pg_vec[pg_vec_pos] + (frame_off * ring->frame_size); -} - -static struct nl_mmap_hdr * -netlink_lookup_frame(const struct netlink_ring *ring, unsigned int pos, - enum nl_mmap_status status) -{ - struct nl_mmap_hdr *hdr; - - hdr = __netlink_lookup_frame(ring, pos); - if (netlink_get_status(hdr) != status) - return NULL; - - return hdr; -} - -static struct nl_mmap_hdr * -netlink_current_frame(const struct netlink_ring *ring, - enum nl_mmap_status status) -{ - return netlink_lookup_frame(ring, ring->head, status); -} - -static void netlink_increment_head(struct netlink_ring *ring) -{ - ring->head = ring->head != ring->frame_max ? ring->head + 1 : 0; -} - -static void netlink_forward_ring(struct netlink_ring *ring) -{ - unsigned int head = ring->head; - const struct nl_mmap_hdr *hdr; - - do { - hdr = __netlink_lookup_frame(ring, ring->head); - if (hdr->nm_status == NL_MMAP_STATUS_UNUSED) - break; - if (hdr->nm_status != NL_MMAP_STATUS_SKIP) - break; - netlink_increment_head(ring); - } while (ring->head != head); -} - -static bool netlink_has_valid_frame(struct netlink_ring *ring) -{ - unsigned int head = ring->head, pos = head; - const struct nl_mmap_hdr *hdr; - - do { - hdr = __netlink_lookup_frame(ring, pos); - if (hdr->nm_status == NL_MMAP_STATUS_VALID) - return true; - pos = pos != 0 ? pos - 1 : ring->frame_max; - } while (pos != head); - - return false; -} - -static bool netlink_dump_space(struct netlink_sock *nlk) -{ - struct netlink_ring *ring = &nlk->rx_ring; - struct nl_mmap_hdr *hdr; - unsigned int n; - - hdr = netlink_current_frame(ring, NL_MMAP_STATUS_UNUSED); - if (hdr == NULL) - return false; - - n = ring->head + ring->frame_max / 2; - if (n > ring->frame_max) - n -= ring->frame_max; - - hdr = __netlink_lookup_frame(ring, n); - - return hdr->nm_status == NL_MMAP_STATUS_UNUSED; -} - -static unsigned int netlink_poll(struct file *file, struct socket *sock, - poll_table *wait) -{ - struct sock *sk = sock->sk; - struct netlink_sock *nlk = nlk_sk(sk); - unsigned int mask; - int err; - - if (nlk->rx_ring.pg_vec != NULL) { - /* Memory mapped sockets don't call recvmsg(), so flow control - * for dumps is performed here. A dump is allowed to continue - * if at least half the ring is unused. - */ - while (nlk->cb_running && netlink_dump_space(nlk)) { - err = netlink_dump(sk); - if (err < 0) { - sk->sk_err = -err; - sk->sk_error_report(sk); - break; - } - } - netlink_rcv_wake(sk); - } - - mask = datagram_poll(file, sock, wait); - - /* We could already have received frames in the normal receive - * queue, that will show up as NL_MMAP_STATUS_COPY in the ring, - * so if mask contains pollin/etc already, there's no point - * walking the ring. - */ - if ((mask & (POLLIN | POLLRDNORM)) != (POLLIN | POLLRDNORM)) { - spin_lock_bh(&sk->sk_receive_queue.lock); - if (nlk->rx_ring.pg_vec) { - if (netlink_has_valid_frame(&nlk->rx_ring)) - mask |= POLLIN | POLLRDNORM; - } - spin_unlock_bh(&sk->sk_receive_queue.lock); - } - - spin_lock_bh(&sk->sk_write_queue.lock); - if (nlk->tx_ring.pg_vec) { - if (netlink_current_frame(&nlk->tx_ring, NL_MMAP_STATUS_UNUSED)) - mask |= POLLOUT | POLLWRNORM; - } - spin_unlock_bh(&sk->sk_write_queue.lock); - - return mask; -} - -static struct nl_mmap_hdr *netlink_mmap_hdr(struct sk_buff *skb) -{ - return (struct nl_mmap_hdr *)(skb->head - NL_MMAP_HDRLEN); -} - -static void netlink_ring_setup_skb(struct sk_buff *skb, struct sock *sk, - struct netlink_ring *ring, - struct nl_mmap_hdr *hdr) -{ - unsigned int size; - void *data; - - size = ring->frame_size - NL_MMAP_HDRLEN; - data = (void *)hdr + NL_MMAP_HDRLEN; - - skb->head = data; - skb->data = data; - skb_reset_tail_pointer(skb); - skb->end = skb->tail + size; - skb->len = 0; - - skb->destructor = netlink_skb_destructor; - NETLINK_CB(skb).flags |= NETLINK_SKB_MMAPED; - NETLINK_CB(skb).sk = sk; -} - -static int netlink_mmap_sendmsg(struct sock *sk, struct msghdr *msg, - u32 dst_portid, u32 dst_group, - struct scm_cookie *scm) -{ - struct netlink_sock *nlk = nlk_sk(sk); - struct netlink_ring *ring; - struct nl_mmap_hdr *hdr; - struct sk_buff *skb; - unsigned int maxlen; - int err = 0, len = 0; - - mutex_lock(&nlk->pg_vec_lock); - - ring = &nlk->tx_ring; - maxlen = ring->frame_size - NL_MMAP_HDRLEN; - - do { - unsigned int nm_len; - - hdr = netlink_current_frame(ring, NL_MMAP_STATUS_VALID); - if (hdr == NULL) { - if (!(msg->msg_flags & MSG_DONTWAIT) && - atomic_read(&nlk->tx_ring.pending)) - schedule(); - continue; - } - - nm_len = ACCESS_ONCE(hdr->nm_len); - if (nm_len > maxlen) { - err = -EINVAL; - goto out; - } - - netlink_frame_flush_dcache(hdr, nm_len); - - skb = alloc_skb(nm_len, GFP_KERNEL); - if (skb == NULL) { - err = -ENOBUFS; - goto out; - } - __skb_put(skb, nm_len); - memcpy(skb->data, (void *)hdr + NL_MMAP_HDRLEN, nm_len); - netlink_set_status(hdr, NL_MMAP_STATUS_UNUSED); - - netlink_increment_head(ring); - - NETLINK_CB(skb).portid = nlk->portid; - NETLINK_CB(skb).dst_group = dst_group; - NETLINK_CB(skb).creds = scm->creds; - - err = security_netlink_send(sk, skb); - if (err) { - kfree_skb(skb); - goto out; - } - - if (unlikely(dst_group)) { - atomic_inc(&skb->users); - netlink_broadcast(sk, skb, dst_portid, dst_group, - GFP_KERNEL); - } - err = netlink_unicast(sk, skb, dst_portid, - msg->msg_flags & MSG_DONTWAIT); - if (err < 0) - goto out; - len += err; - - } while (hdr != NULL || - (!(msg->msg_flags & MSG_DONTWAIT) && - atomic_read(&nlk->tx_ring.pending))); - - if (len > 0) - err = len; -out: - mutex_unlock(&nlk->pg_vec_lock); - return err; -} - -static void netlink_queue_mmaped_skb(struct sock *sk, struct sk_buff *skb) -{ - struct nl_mmap_hdr *hdr; - - hdr = netlink_mmap_hdr(skb); - hdr->nm_len = skb->len; - hdr->nm_group = NETLINK_CB(skb).dst_group; - hdr->nm_pid = NETLINK_CB(skb).creds.pid; - hdr->nm_uid = from_kuid(sk_user_ns(sk), NETLINK_CB(skb).creds.uid); - hdr->nm_gid = from_kgid(sk_user_ns(sk), NETLINK_CB(skb).creds.gid); - netlink_frame_flush_dcache(hdr, hdr->nm_len); - netlink_set_status(hdr, NL_MMAP_STATUS_VALID); - - NETLINK_CB(skb).flags |= NETLINK_SKB_DELIVERED; - kfree_skb(skb); -} - -static void netlink_ring_set_copied(struct sock *sk, struct sk_buff *skb) -{ - struct netlink_sock *nlk = nlk_sk(sk); - struct netlink_ring *ring = &nlk->rx_ring; - struct nl_mmap_hdr *hdr; - - spin_lock_bh(&sk->sk_receive_queue.lock); - hdr = netlink_current_frame(ring, NL_MMAP_STATUS_UNUSED); - if (hdr == NULL) { - spin_unlock_bh(&sk->sk_receive_queue.lock); - kfree_skb(skb); - netlink_overrun(sk); - return; - } - netlink_increment_head(ring); - __skb_queue_tail(&sk->sk_receive_queue, skb); - spin_unlock_bh(&sk->sk_receive_queue.lock); - - hdr->nm_len = skb->len; - hdr->nm_group = NETLINK_CB(skb).dst_group; - hdr->nm_pid = NETLINK_CB(skb).creds.pid; - hdr->nm_uid = from_kuid(sk_user_ns(sk), NETLINK_CB(skb).creds.uid); - hdr->nm_gid = from_kgid(sk_user_ns(sk), NETLINK_CB(skb).creds.gid); - netlink_set_status(hdr, NL_MMAP_STATUS_COPY); -} - -#else /* CONFIG_NETLINK_MMAP */ -#define netlink_rx_is_mmaped(sk) false -#define netlink_tx_is_mmaped(sk) false -#define netlink_mmap sock_no_mmap -#define netlink_poll datagram_poll -#define netlink_mmap_sendmsg(sk, msg, dst_portid, dst_group, scm) 0 -#endif /* CONFIG_NETLINK_MMAP */ - static void netlink_skb_destructor(struct sk_buff *skb) { -#ifdef CONFIG_NETLINK_MMAP - struct nl_mmap_hdr *hdr; - struct netlink_ring *ring; - struct sock *sk; - - /* If a packet from the kernel to userspace was freed because of an - * error without being delivered to userspace, the kernel must reset - * the status. In the direction userspace to kernel, the status is - * always reset here after the packet was processed and freed. - */ - if (netlink_skb_is_mmaped(skb)) { - hdr = netlink_mmap_hdr(skb); - sk = NETLINK_CB(skb).sk; - - if (NETLINK_CB(skb).flags & NETLINK_SKB_TX) { - netlink_set_status(hdr, NL_MMAP_STATUS_UNUSED); - ring = &nlk_sk(sk)->tx_ring; - } else { - if (!(NETLINK_CB(skb).flags & NETLINK_SKB_DELIVERED)) { - hdr->nm_len = 0; - netlink_set_status(hdr, NL_MMAP_STATUS_VALID); - } - ring = &nlk_sk(sk)->rx_ring; - } - - WARN_ON(atomic_read(&ring->pending) == 0); - atomic_dec(&ring->pending); - sock_put(sk); - - skb->head = NULL; - } -#endif if (is_vmalloc_addr(skb->head)) { if (!skb->cloned || !atomic_dec_return(&(skb_shinfo(skb)->dataref))) @@ -927,18 +325,6 @@ static void netlink_skb_set_owner_r(struct sk_buff *skb, struct sock *sk) static void netlink_sock_destruct(struct sock *sk) { skb_queue_purge(&sk->sk_receive_queue); -#ifdef CONFIG_NETLINK_MMAP - if (1) { - struct nl_mmap_req req; - - memset(&req, 0, sizeof(req)); - if (nlk->rx_ring.pg_vec) - __netlink_set_ring(sk, &req, false, NULL, 0); - memset(&req, 0, sizeof(req)); - if (nlk->tx_ring.pg_vec) - __netlink_set_ring(sk, &req, true, NULL, 0); - } -#endif /* CONFIG_NETLINK_MMAP */ if (!sock_flag(sk, SOCK_DEAD)) { printk(KERN_ERR "Freeing alive netlink socket %p\n", sk); @@ -1185,9 +571,6 @@ static int __netlink_create(struct net *net, struct socket *sock, mutex_init(nlk->cb_mutex); } init_waitqueue_head(&nlk->wait); -#ifdef CONFIG_NETLINK_MMAP - mutex_init(&nlk->pg_vec_lock); -#endif sock_set_flag(sk, SOCK_RCU_FREE); sk->sk_destruct = netlink_sock_destruct; @@ -1725,8 +1108,7 @@ int netlink_attachskb(struct sock *sk, struct sk_buff *skb, nlk = nlk_sk(sk); if ((atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf || - test_bit(NETLINK_S_CONGESTED, &nlk->state)) && - !netlink_skb_is_mmaped(skb)) { + test_bit(NETLINK_S_CONGESTED, &nlk->state))) { DECLARE_WAITQUEUE(wait, current); if (!*timeo) { if (!ssk || netlink_is_kernel(ssk)) @@ -1764,14 +1146,7 @@ static int __netlink_sendskb(struct sock *sk, struct sk_buff *skb) netlink_deliver_tap(skb); -#ifdef CONFIG_NETLINK_MMAP - if (netlink_skb_is_mmaped(skb)) - netlink_queue_mmaped_skb(sk, skb); - else if (netlink_rx_is_mmaped(sk)) - netlink_ring_set_copied(sk, skb); - else -#endif /* CONFIG_NETLINK_MMAP */ - skb_queue_tail(&sk->sk_receive_queue, skb); + skb_queue_tail(&sk->sk_receive_queue, skb); sk->sk_data_ready(sk); return len; } @@ -1795,9 +1170,6 @@ static struct sk_buff *netlink_trim(struct sk_buff *skb, gfp_t allocation) int delta; WARN_ON(skb->sk != NULL); - if (netlink_skb_is_mmaped(skb)) - return skb; - delta = skb->end - skb->tail; if (is_vmalloc_addr(skb->head) || delta * 2 < skb->truesize) return skb; @@ -1877,71 +1249,6 @@ struct sk_buff *__netlink_alloc_skb(struct sock *ssk, unsigned int size, unsigned int ldiff, u32 dst_portid, gfp_t gfp_mask) { -#ifdef CONFIG_NETLINK_MMAP - unsigned int maxlen, linear_size; - struct sock *sk = NULL; - struct sk_buff *skb; - struct netlink_ring *ring; - struct nl_mmap_hdr *hdr; - - sk = netlink_getsockbyportid(ssk, dst_portid); - if (IS_ERR(sk)) - goto out; - - ring = &nlk_sk(sk)->rx_ring; - /* fast-path without atomic ops for common case: non-mmaped receiver */ - if (ring->pg_vec == NULL) - goto out_put; - - /* We need to account the full linear size needed as a ring - * slot cannot have non-linear parts. - */ - linear_size = size + ldiff; - if (ring->frame_size - NL_MMAP_HDRLEN < linear_size) - goto out_put; - - skb = alloc_skb_head(gfp_mask); - if (skb == NULL) - goto err1; - - spin_lock_bh(&sk->sk_receive_queue.lock); - /* check again under lock */ - if (ring->pg_vec == NULL) - goto out_free; - - /* check again under lock */ - maxlen = ring->frame_size - NL_MMAP_HDRLEN; - if (maxlen < linear_size) - goto out_free; - - netlink_forward_ring(ring); - hdr = netlink_current_frame(ring, NL_MMAP_STATUS_UNUSED); - if (hdr == NULL) - goto err2; - - netlink_ring_setup_skb(skb, sk, ring, hdr); - netlink_set_status(hdr, NL_MMAP_STATUS_RESERVED); - atomic_inc(&ring->pending); - netlink_increment_head(ring); - - spin_unlock_bh(&sk->sk_receive_queue.lock); - return skb; - -err2: - kfree_skb(skb); - spin_unlock_bh(&sk->sk_receive_queue.lock); - netlink_overrun(sk); -err1: - sock_put(sk); - return NULL; - -out_free: - kfree_skb(skb); - spin_unlock_bh(&sk->sk_receive_queue.lock); -out_put: - sock_put(sk); -out: -#endif return alloc_skb(size, gfp_mask); } EXPORT_SYMBOL_GPL(__netlink_alloc_skb); @@ -2222,8 +1529,7 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname, if (level != SOL_NETLINK) return -ENOPROTOOPT; - if (optname != NETLINK_RX_RING && optname != NETLINK_TX_RING && - optlen >= sizeof(int) && + if (optlen >= sizeof(int) && get_user(val, (unsigned int __user *)optval)) return -EFAULT; @@ -2276,25 +1582,6 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname, } err = 0; break; -#ifdef CONFIG_NETLINK_MMAP - case NETLINK_RX_RING: - case NETLINK_TX_RING: { - struct nl_mmap_req req; - - /* Rings might consume more memory than queue limits, require - * CAP_NET_ADMIN. - */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - if (optlen < sizeof(req)) - return -EINVAL; - if (copy_from_user(&req, optval, sizeof(req))) - return -EFAULT; - err = netlink_set_ring(sk, &req, - optname == NETLINK_TX_RING); - break; - } -#endif /* CONFIG_NETLINK_MMAP */ case NETLINK_LISTEN_ALL_NSID: if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_BROADCAST)) return -EPERM; @@ -2464,18 +1751,6 @@ static int netlink_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) smp_rmb(); } - /* It's a really convoluted way for userland to ask for mmaped - * sendmsg(), but that's what we've got... - */ - if (netlink_tx_is_mmaped(sk) && - iter_is_iovec(&msg->msg_iter) && - msg->msg_iter.nr_segs == 1 && - msg->msg_iter.iov->iov_base == NULL) { - err = netlink_mmap_sendmsg(sk, msg, dst_portid, dst_group, - &scm); - goto out; - } - err = -EMSGSIZE; if (len > sk->sk_sndbuf - 32) goto out; @@ -2792,8 +2067,7 @@ static int netlink_dump(struct sock *sk) goto errout_skb; } - if (!netlink_rx_is_mmaped(sk) && - atomic_read(&sk->sk_rmem_alloc) >= sk->sk_rcvbuf) + if (atomic_read(&sk->sk_rmem_alloc) >= sk->sk_rcvbuf) goto errout_skb; /* NLMSG_GOODSIZE is small to avoid high order allocations being @@ -2882,16 +2156,7 @@ int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb, struct netlink_sock *nlk; int ret; - /* Memory mapped dump requests need to be copied to avoid looping - * on the pending state in netlink_mmap_sendmsg() while the CB hold - * a reference to the skb. - */ - if (netlink_skb_is_mmaped(skb)) { - skb = skb_copy(skb, GFP_KERNEL); - if (skb == NULL) - return -ENOBUFS; - } else - atomic_inc(&skb->users); + atomic_inc(&skb->users); sk = netlink_lookup(sock_net(ssk), ssk->sk_protocol, NETLINK_CB(skb).portid); if (sk == NULL) { @@ -3235,7 +2500,7 @@ static const struct proto_ops netlink_ops = { .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = netlink_getname, - .poll = netlink_poll, + .poll = datagram_poll, .ioctl = sock_no_ioctl, .listen = sock_no_listen, .shutdown = sock_no_shutdown, @@ -3243,7 +2508,7 @@ static const struct proto_ops netlink_ops = { .getsockopt = netlink_getsockopt, .sendmsg = netlink_sendmsg, .recvmsg = netlink_recvmsg, - .mmap = netlink_mmap, + .mmap = sock_no_mmap, .sendpage = sock_no_sendpage, }; diff --git a/net/netlink/af_netlink.h b/net/netlink/af_netlink.h index b0c1ddc97260..15e62973cfc6 100644 --- a/net/netlink/af_netlink.h +++ b/net/netlink/af_netlink.h @@ -45,12 +45,6 @@ struct netlink_sock { int (*netlink_bind)(struct net *net, int group); void (*netlink_unbind)(struct net *net, int group); struct module *module; -#ifdef CONFIG_NETLINK_MMAP - struct mutex pg_vec_lock; - struct netlink_ring rx_ring; - struct netlink_ring tx_ring; - atomic_t mapped; -#endif /* CONFIG_NETLINK_MMAP */ struct rhash_head node; struct work_struct work; @@ -61,15 +55,6 @@ static inline struct netlink_sock *nlk_sk(struct sock *sk) return container_of(sk, struct netlink_sock, sk); } -static inline bool netlink_skb_is_mmaped(const struct sk_buff *skb) -{ -#ifdef CONFIG_NETLINK_MMAP - return NETLINK_CB(skb).flags & NETLINK_SKB_MMAPED; -#else - return false; -#endif /* CONFIG_NETLINK_MMAP */ -} - struct netlink_table { struct rhashtable hash; struct hlist_head mc_list; diff --git a/net/netlink/diag.c b/net/netlink/diag.c index 3ee63a3cff30..8dd836a8dd60 100644 --- a/net/netlink/diag.c +++ b/net/netlink/diag.c @@ -8,41 +8,6 @@ #include "af_netlink.h" -#ifdef CONFIG_NETLINK_MMAP -static int sk_diag_put_ring(struct netlink_ring *ring, int nl_type, - struct sk_buff *nlskb) -{ - struct netlink_diag_ring ndr; - - ndr.ndr_block_size = ring->pg_vec_pages << PAGE_SHIFT; - ndr.ndr_block_nr = ring->pg_vec_len; - ndr.ndr_frame_size = ring->frame_size; - ndr.ndr_frame_nr = ring->frame_max + 1; - - return nla_put(nlskb, nl_type, sizeof(ndr), &ndr); -} - -static int sk_diag_put_rings_cfg(struct sock *sk, struct sk_buff *nlskb) -{ - struct netlink_sock *nlk = nlk_sk(sk); - int ret; - - mutex_lock(&nlk->pg_vec_lock); - ret = sk_diag_put_ring(&nlk->rx_ring, NETLINK_DIAG_RX_RING, nlskb); - if (!ret) - ret = sk_diag_put_ring(&nlk->tx_ring, NETLINK_DIAG_TX_RING, - nlskb); - mutex_unlock(&nlk->pg_vec_lock); - - return ret; -} -#else -static int sk_diag_put_rings_cfg(struct sock *sk, struct sk_buff *nlskb) -{ - return 0; -} -#endif - static int sk_diag_dump_groups(struct sock *sk, struct sk_buff *nlskb) { struct netlink_sock *nlk = nlk_sk(sk); @@ -87,10 +52,6 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, sock_diag_put_meminfo(sk, skb, NETLINK_DIAG_MEMINFO)) goto out_nlmsg_trim; - if ((req->ndiag_show & NDIAG_SHOW_RING_CFG) && - sk_diag_put_rings_cfg(sk, skb)) - goto out_nlmsg_trim; - nlmsg_end(skb, nlh); return 0; diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index d1bd4a45ca2d..d26b28def310 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -588,7 +588,7 @@ static int ip_tun_from_nlattr(const struct nlattr *attr, ipv4 = true; break; case OVS_TUNNEL_KEY_ATTR_IPV6_SRC: - SW_FLOW_KEY_PUT(match, tun_key.u.ipv6.dst, + SW_FLOW_KEY_PUT(match, tun_key.u.ipv6.src, nla_get_in6_addr(a), is_mask); ipv6 = true; break; diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index d805cd577a60..f8d6a0ca9c03 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -3021,7 +3021,7 @@ static int packet_bind_spkt(struct socket *sock, struct sockaddr *uaddr, int addr_len) { struct sock *sk = sock->sk; - char name[15]; + char name[sizeof(uaddr->sa_data) + 1]; /* * Check legality @@ -3029,7 +3029,11 @@ static int packet_bind_spkt(struct socket *sock, struct sockaddr *uaddr, if (addr_len != sizeof(struct sockaddr)) return -EINVAL; - strlcpy(name, uaddr->sa_data, sizeof(name)); + /* uaddr->sa_data comes from the userspace, it's not guaranteed to be + * zero-terminated. + */ + memcpy(name, uaddr->sa_data, sizeof(uaddr->sa_data)); + name[sizeof(uaddr->sa_data)] = 0; return packet_do_bind(sk, name, 0, pkt_sk(sk)->num); } @@ -3622,6 +3626,8 @@ packet_setsockopt(struct socket *sock, int level, int optname, char __user *optv return -EBUSY; if (copy_from_user(&val, optval, sizeof(val))) return -EFAULT; + if (val > INT_MAX) + return -EINVAL; po->tp_reserve = val; return 0; } @@ -4134,8 +4140,8 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, if (unlikely(!PAGE_ALIGNED(req->tp_block_size))) goto out; if (po->tp_version >= TPACKET_V3 && - (int)(req->tp_block_size - - BLK_PLUS_PRIV(req_u->req3.tp_sizeof_priv)) <= 0) + req->tp_block_size <= + BLK_PLUS_PRIV((u64)req_u->req3.tp_sizeof_priv)) goto out; if (unlikely(req->tp_frame_size < po->tp_hdrlen + po->tp_reserve)) @@ -4146,6 +4152,8 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, rb->frames_per_block = req->tp_block_size / req->tp_frame_size; if (unlikely(rb->frames_per_block == 0)) goto out; + if (unlikely(req->tp_block_size > UINT_MAX / req->tp_block_nr)) + goto out; if (unlikely((rb->frames_per_block * req->tp_block_nr) != req->tp_frame_nr)) goto out; diff --git a/net/rds/cong.c b/net/rds/cong.c index e6144b8246fd..6641bcf7c185 100644 --- a/net/rds/cong.c +++ b/net/rds/cong.c @@ -299,7 +299,7 @@ void rds_cong_set_bit(struct rds_cong_map *map, __be16 port) i = be16_to_cpu(port) / RDS_CONG_MAP_PAGE_BITS; off = be16_to_cpu(port) % RDS_CONG_MAP_PAGE_BITS; - __set_bit_le(off, (void *)map->m_page_addrs[i]); + set_bit_le(off, (void *)map->m_page_addrs[i]); } void rds_cong_clear_bit(struct rds_cong_map *map, __be16 port) @@ -313,7 +313,7 @@ void rds_cong_clear_bit(struct rds_cong_map *map, __be16 port) i = be16_to_cpu(port) / RDS_CONG_MAP_PAGE_BITS; off = be16_to_cpu(port) % RDS_CONG_MAP_PAGE_BITS; - __clear_bit_le(off, (void *)map->m_page_addrs[i]); + clear_bit_le(off, (void *)map->m_page_addrs[i]); } static int rds_cong_test_bit(struct rds_cong_map *map, __be16 port) diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 06e7c4a37245..694a06f1e0d5 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -820,10 +820,8 @@ static int tca_action_flush(struct net *net, struct nlattr *nla, goto out_module_put; err = a.ops->walk(skb, &dcb, RTM_DELACTION, &a); - if (err < 0) + if (err <= 0) goto out_module_put; - if (err == 0) - goto noflush_out; nla_nest_end(skb, nest); @@ -840,7 +838,6 @@ static int tca_action_flush(struct net *net, struct nlattr *nla, out_module_put: module_put(a.ops->owner); err_out: -noflush_out: kfree_skb(skb); return err; } diff --git a/net/sched/act_connmark.c b/net/sched/act_connmark.c index bb41699c6c49..7ecb14f3db54 100644 --- a/net/sched/act_connmark.c +++ b/net/sched/act_connmark.c @@ -109,6 +109,9 @@ static int tcf_connmark_init(struct net *net, struct nlattr *nla, if (ret < 0) return ret; + if (!tb[TCA_CONNMARK_PARMS]) + return -EINVAL; + parm = nla_data(tb[TCA_CONNMARK_PARMS]); if (!tcf_hash_check(parm->index, a, bind)) { diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index e384d6aefa3a..1090a52c03cd 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -36,14 +36,15 @@ static DEFINE_SPINLOCK(mirred_list_lock); static void tcf_mirred_release(struct tc_action *a, int bind) { struct tcf_mirred *m = to_mirred(a); - struct net_device *dev = rcu_dereference_protected(m->tcfm_dev, 1); + struct net_device *dev; /* We could be called either in a RCU callback or with RTNL lock held. */ spin_lock_bh(&mirred_list_lock); list_del(&m->tcfm_list); - spin_unlock_bh(&mirred_list_lock); + dev = rcu_dereference_protected(m->tcfm_dev, 1); if (dev) dev_put(dev); + spin_unlock_bh(&mirred_list_lock); } static const struct nla_policy mirred_policy[TCA_MIRRED_MAX + 1] = { diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 138f2d667212..c96d666cef29 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -4422,6 +4422,12 @@ int sctp_do_peeloff(struct sock *sk, sctp_assoc_t id, struct socket **sockp) if (!asoc) return -EINVAL; + /* If there is a thread waiting on more sndbuf space for + * sending on this asoc, it cannot be peeled. + */ + if (waitqueue_active(&asoc->wait)) + return -EBUSY; + /* An association cannot be branched off from an already peeled-off * socket, nor is this supported for tcp style sockets. */ @@ -6388,6 +6394,9 @@ int sctp_inet_listen(struct socket *sock, int backlog) if (sock->state != SS_UNCONNECTED) goto out; + if (!sctp_sstate(sk, LISTENING) && !sctp_sstate(sk, CLOSED)) + goto out; + /* If backlog is zero, disable listening. */ if (!backlog) { if (sctp_sstate(sk, CLOSED)) @@ -6960,8 +6969,6 @@ static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p, */ release_sock(sk); current_timeo = schedule_timeout(current_timeo); - if (sk != asoc->base.sk) - goto do_error; lock_sock(sk); *timeo_p = current_timeo; diff --git a/net/socket.c b/net/socket.c index 11a2967eaebc..876bb6609806 100644 --- a/net/socket.c +++ b/net/socket.c @@ -533,7 +533,7 @@ static ssize_t sockfs_listxattr(struct dentry *dentry, char *buffer, return used; } -int sockfs_setattr(struct dentry *dentry, struct iattr *iattr) +static int sockfs_setattr(struct dentry *dentry, struct iattr *iattr) { int err = simple_setattr(dentry, iattr); diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 06095cc8815e..1f0687d8e3d7 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -541,9 +541,13 @@ gss_setup_upcall(struct gss_auth *gss_auth, struct rpc_cred *cred) return gss_new; gss_msg = gss_add_msg(gss_new); if (gss_msg == gss_new) { - int res = rpc_queue_upcall(gss_new->pipe, &gss_new->msg); + int res; + atomic_inc(&gss_msg->count); + res = rpc_queue_upcall(gss_new->pipe, &gss_new->msg); if (res) { gss_unhash_msg(gss_new); + atomic_dec(&gss_msg->count); + gss_release_msg(gss_new); gss_msg = ERR_PTR(res); } } else @@ -836,6 +840,7 @@ gss_pipe_destroy_msg(struct rpc_pipe_msg *msg) warn_gssd(); gss_release_msg(gss_msg); } + gss_release_msg(gss_msg); } static void gss_pipe_dentry_destroy(struct dentry *dir, diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c index 648f2a67f314..cb1381513c82 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c @@ -381,6 +381,10 @@ int tipc_enable_l2_media(struct net *net, struct tipc_bearer *b, dev = dev_get_by_name(net, driver_name); if (!dev) return -ENODEV; + if (tipc_mtu_bad(dev, 0)) { + dev_put(dev); + return -EINVAL; + } /* Associate TIPC bearer with L2 bearer */ rcu_assign_pointer(b->media_ptr, dev); @@ -570,14 +574,19 @@ static int tipc_l2_device_event(struct notifier_block *nb, unsigned long evt, if (!b_ptr) return NOTIFY_DONE; - b_ptr->mtu = dev->mtu; - switch (evt) { case NETDEV_CHANGE: if (netif_carrier_ok(dev)) break; case NETDEV_GOING_DOWN: + tipc_reset_bearer(net, b_ptr); + break; case NETDEV_CHANGEMTU: + if (tipc_mtu_bad(dev, 0)) { + bearer_disable(net, b_ptr); + break; + } + b_ptr->mtu = dev->mtu; tipc_reset_bearer(net, b_ptr); break; case NETDEV_CHANGEADDR: diff --git a/net/tipc/bearer.h b/net/tipc/bearer.h index 552185bc4773..5f11e18b1fa1 100644 --- a/net/tipc/bearer.h +++ b/net/tipc/bearer.h @@ -39,6 +39,7 @@ #include "netlink.h" #include "core.h" +#include "msg.h" #include <net/genetlink.h> #define MAX_MEDIA 3 @@ -61,6 +62,9 @@ #define TIPC_MEDIA_TYPE_IB 2 #define TIPC_MEDIA_TYPE_UDP 3 +/* minimum bearer MTU */ +#define TIPC_MIN_BEARER_MTU (MAX_H_SIZE + INT_H_SIZE) + /** * struct tipc_node_map - set of node identifiers * @count: # of nodes in set @@ -226,4 +230,13 @@ void tipc_bearer_xmit(struct net *net, u32 bearer_id, void tipc_bearer_bc_xmit(struct net *net, u32 bearer_id, struct sk_buff_head *xmitq); +/* check if device MTU is too low for tipc headers */ +static inline bool tipc_mtu_bad(struct net_device *dev, unsigned int reserve) +{ + if (dev->mtu >= TIPC_MIN_BEARER_MTU + reserve) + return false; + netdev_warn(dev, "MTU too low for tipc bearer\n"); + return true; +} + #endif /* _TIPC_BEARER_H */ diff --git a/net/tipc/core.c b/net/tipc/core.c index 03a842870c52..e2bdb07a49a2 100644 --- a/net/tipc/core.c +++ b/net/tipc/core.c @@ -69,6 +69,7 @@ static int __net_init tipc_init_net(struct net *net) if (err) goto out_nametbl; + INIT_LIST_HEAD(&tn->dist_queue); err = tipc_topsrv_start(net); if (err) goto out_subscr; diff --git a/net/tipc/core.h b/net/tipc/core.h index 18e95a8020cd..fe3b89e9cde4 100644 --- a/net/tipc/core.h +++ b/net/tipc/core.h @@ -103,6 +103,9 @@ struct tipc_net { spinlock_t nametbl_lock; struct name_table *nametbl; + /* Name dist queue */ + struct list_head dist_queue; + /* Topology subscription server */ struct tipc_server *topsrv; atomic_t subscription_count; diff --git a/net/tipc/name_distr.c b/net/tipc/name_distr.c index f51c8bdbea1c..c4c151bc000c 100644 --- a/net/tipc/name_distr.c +++ b/net/tipc/name_distr.c @@ -40,11 +40,6 @@ int sysctl_tipc_named_timeout __read_mostly = 2000; -/** - * struct tipc_dist_queue - queue holding deferred name table updates - */ -static struct list_head tipc_dist_queue = LIST_HEAD_INIT(tipc_dist_queue); - struct distr_queue_item { struct distr_item i; u32 dtype; @@ -67,6 +62,8 @@ static void publ_to_item(struct distr_item *i, struct publication *p) /** * named_prepare_buf - allocate & initialize a publication message + * + * The buffer returned is of size INT_H_SIZE + payload size */ static struct sk_buff *named_prepare_buf(struct net *net, u32 type, u32 size, u32 dest) @@ -171,9 +168,9 @@ static void named_distribute(struct net *net, struct sk_buff_head *list, struct publication *publ; struct sk_buff *skb = NULL; struct distr_item *item = NULL; - uint msg_dsz = (tipc_node_get_mtu(net, dnode, 0) / ITEM_SIZE) * - ITEM_SIZE; - uint msg_rem = msg_dsz; + u32 msg_dsz = ((tipc_node_get_mtu(net, dnode, 0) - INT_H_SIZE) / + ITEM_SIZE) * ITEM_SIZE; + u32 msg_rem = msg_dsz; list_for_each_entry(publ, pls, local_list) { /* Prepare next buffer: */ @@ -340,9 +337,11 @@ static bool tipc_update_nametbl(struct net *net, struct distr_item *i, * tipc_named_add_backlog - add a failed name table update to the backlog * */ -static void tipc_named_add_backlog(struct distr_item *i, u32 type, u32 node) +static void tipc_named_add_backlog(struct net *net, struct distr_item *i, + u32 type, u32 node) { struct distr_queue_item *e; + struct tipc_net *tn = net_generic(net, tipc_net_id); unsigned long now = get_jiffies_64(); e = kzalloc(sizeof(*e), GFP_ATOMIC); @@ -352,7 +351,7 @@ static void tipc_named_add_backlog(struct distr_item *i, u32 type, u32 node) e->node = node; e->expires = now + msecs_to_jiffies(sysctl_tipc_named_timeout); memcpy(e, i, sizeof(*i)); - list_add_tail(&e->next, &tipc_dist_queue); + list_add_tail(&e->next, &tn->dist_queue); } /** @@ -362,10 +361,11 @@ static void tipc_named_add_backlog(struct distr_item *i, u32 type, u32 node) void tipc_named_process_backlog(struct net *net) { struct distr_queue_item *e, *tmp; + struct tipc_net *tn = net_generic(net, tipc_net_id); char addr[16]; unsigned long now = get_jiffies_64(); - list_for_each_entry_safe(e, tmp, &tipc_dist_queue, next) { + list_for_each_entry_safe(e, tmp, &tn->dist_queue, next) { if (time_after(e->expires, now)) { if (!tipc_update_nametbl(net, &e->i, e->node, e->dtype)) continue; @@ -405,7 +405,7 @@ void tipc_named_rcv(struct net *net, struct sk_buff_head *inputq) node = msg_orignode(msg); while (count--) { if (!tipc_update_nametbl(net, item, node, mtype)) - tipc_named_add_backlog(item, mtype, node); + tipc_named_add_backlog(net, item, mtype, node); item++; } kfree_skb(skb); diff --git a/net/tipc/node.c b/net/tipc/node.c index 3926b561f873..2df0b98d4a32 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -102,9 +102,10 @@ static unsigned int tipc_hashfn(u32 addr) static void tipc_node_kref_release(struct kref *kref) { - struct tipc_node *node = container_of(kref, struct tipc_node, kref); + struct tipc_node *n = container_of(kref, struct tipc_node, kref); - tipc_node_delete(node); + kfree(n->bc_entry.link); + kfree_rcu(n, rcu); } void tipc_node_put(struct tipc_node *node) @@ -216,21 +217,20 @@ static void tipc_node_delete(struct tipc_node *node) { list_del_rcu(&node->list); hlist_del_rcu(&node->hash); - kfree(node->bc_entry.link); - kfree_rcu(node, rcu); + tipc_node_put(node); + + del_timer_sync(&node->timer); + tipc_node_put(node); } void tipc_node_stop(struct net *net) { - struct tipc_net *tn = net_generic(net, tipc_net_id); + struct tipc_net *tn = tipc_net(net); struct tipc_node *node, *t_node; spin_lock_bh(&tn->node_list_lock); - list_for_each_entry_safe(node, t_node, &tn->node_list, list) { - if (del_timer(&node->timer)) - tipc_node_put(node); - tipc_node_put(node); - } + list_for_each_entry_safe(node, t_node, &tn->node_list, list) + tipc_node_delete(node); spin_unlock_bh(&tn->node_list_lock); } @@ -313,9 +313,7 @@ static void tipc_node_timeout(unsigned long data) if (rc & TIPC_LINK_DOWN_EVT) tipc_node_link_down(n, bearer_id, false); } - if (!mod_timer(&n->timer, jiffies + n->keepalive_intv)) - tipc_node_get(n); - tipc_node_put(n); + mod_timer(&n->timer, jiffies + n->keepalive_intv); } /** @@ -730,7 +728,7 @@ static void tipc_node_fsm_evt(struct tipc_node *n, int evt) state = SELF_UP_PEER_UP; break; case SELF_LOST_CONTACT_EVT: - state = SELF_DOWN_PEER_LEAVING; + state = SELF_DOWN_PEER_DOWN; break; case SELF_ESTABL_CONTACT_EVT: case PEER_LOST_CONTACT_EVT: @@ -749,7 +747,7 @@ static void tipc_node_fsm_evt(struct tipc_node *n, int evt) state = SELF_UP_PEER_UP; break; case PEER_LOST_CONTACT_EVT: - state = SELF_LEAVING_PEER_DOWN; + state = SELF_DOWN_PEER_DOWN; break; case SELF_LOST_CONTACT_EVT: case PEER_ESTABL_CONTACT_EVT: diff --git a/net/tipc/socket.c b/net/tipc/socket.c index b26b7a127773..65171f8e8c45 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -777,9 +777,11 @@ void tipc_sk_mcast_rcv(struct net *net, struct sk_buff_head *arrvq, * @tsk: receiving socket * @skb: pointer to message buffer. */ -static void tipc_sk_proto_rcv(struct tipc_sock *tsk, struct sk_buff *skb) +static void tipc_sk_proto_rcv(struct tipc_sock *tsk, struct sk_buff *skb, + struct sk_buff_head *xmitq) { struct sock *sk = &tsk->sk; + u32 onode = tsk_own_node(tsk); struct tipc_msg *hdr = buf_msg(skb); int mtyp = msg_type(hdr); int conn_cong; @@ -792,7 +794,8 @@ static void tipc_sk_proto_rcv(struct tipc_sock *tsk, struct sk_buff *skb) if (mtyp == CONN_PROBE) { msg_set_type(hdr, CONN_PROBE_REPLY); - tipc_sk_respond(sk, skb, TIPC_OK); + if (tipc_msg_reverse(onode, &skb, TIPC_OK)) + __skb_queue_tail(xmitq, skb); return; } else if (mtyp == CONN_ACK) { conn_cong = tsk_conn_cong(tsk); @@ -1647,7 +1650,8 @@ static unsigned int rcvbuf_limit(struct sock *sk, struct sk_buff *buf) * * Returns true if message was added to socket receive queue, otherwise false */ -static bool filter_rcv(struct sock *sk, struct sk_buff *skb) +static bool filter_rcv(struct sock *sk, struct sk_buff *skb, + struct sk_buff_head *xmitq) { struct socket *sock = sk->sk_socket; struct tipc_sock *tsk = tipc_sk(sk); @@ -1657,7 +1661,7 @@ static bool filter_rcv(struct sock *sk, struct sk_buff *skb) int usr = msg_user(hdr); if (unlikely(msg_user(hdr) == CONN_MANAGER)) { - tipc_sk_proto_rcv(tsk, skb); + tipc_sk_proto_rcv(tsk, skb, xmitq); return false; } @@ -1700,7 +1704,8 @@ static bool filter_rcv(struct sock *sk, struct sk_buff *skb) return true; reject: - tipc_sk_respond(sk, skb, err); + if (tipc_msg_reverse(tsk_own_node(tsk), &skb, err)) + __skb_queue_tail(xmitq, skb); return false; } @@ -1716,9 +1721,24 @@ reject: static int tipc_backlog_rcv(struct sock *sk, struct sk_buff *skb) { unsigned int truesize = skb->truesize; + struct sk_buff_head xmitq; + u32 dnode, selector; - if (likely(filter_rcv(sk, skb))) + __skb_queue_head_init(&xmitq); + + if (likely(filter_rcv(sk, skb, &xmitq))) { atomic_add(truesize, &tipc_sk(sk)->dupl_rcvcnt); + return 0; + } + + if (skb_queue_empty(&xmitq)) + return 0; + + /* Send response/rejected message */ + skb = __skb_dequeue(&xmitq); + dnode = msg_destnode(buf_msg(skb)); + selector = msg_origport(buf_msg(skb)); + tipc_node_xmit_skb(sock_net(sk), skb, dnode, selector); return 0; } @@ -1732,12 +1752,13 @@ static int tipc_backlog_rcv(struct sock *sk, struct sk_buff *skb) * Caller must hold socket lock */ static void tipc_sk_enqueue(struct sk_buff_head *inputq, struct sock *sk, - u32 dport) + u32 dport, struct sk_buff_head *xmitq) { + unsigned long time_limit = jiffies + 2; + struct sk_buff *skb; unsigned int lim; atomic_t *dcnt; - struct sk_buff *skb; - unsigned long time_limit = jiffies + 2; + u32 onode; while (skb_queue_len(inputq)) { if (unlikely(time_after_eq(jiffies, time_limit))) @@ -1749,20 +1770,22 @@ static void tipc_sk_enqueue(struct sk_buff_head *inputq, struct sock *sk, /* Add message directly to receive queue if possible */ if (!sock_owned_by_user(sk)) { - filter_rcv(sk, skb); + filter_rcv(sk, skb, xmitq); continue; } /* Try backlog, compensating for double-counted bytes */ dcnt = &tipc_sk(sk)->dupl_rcvcnt; - if (sk->sk_backlog.len) + if (!sk->sk_backlog.len) atomic_set(dcnt, 0); lim = rcvbuf_limit(sk, skb) + atomic_read(dcnt); if (likely(!sk_add_backlog(sk, skb, lim))) continue; /* Overload => reject message back to sender */ - tipc_sk_respond(sk, skb, TIPC_ERR_OVERLOAD); + onode = tipc_own_addr(sock_net(sk)); + if (tipc_msg_reverse(onode, &skb, TIPC_ERR_OVERLOAD)) + __skb_queue_tail(xmitq, skb); break; } } @@ -1775,12 +1798,14 @@ static void tipc_sk_enqueue(struct sk_buff_head *inputq, struct sock *sk, */ void tipc_sk_rcv(struct net *net, struct sk_buff_head *inputq) { + struct sk_buff_head xmitq; u32 dnode, dport = 0; int err; struct tipc_sock *tsk; struct sock *sk; struct sk_buff *skb; + __skb_queue_head_init(&xmitq); while (skb_queue_len(inputq)) { dport = tipc_skb_peek_port(inputq, dport); tsk = tipc_sk_lookup(net, dport); @@ -1788,9 +1813,14 @@ void tipc_sk_rcv(struct net *net, struct sk_buff_head *inputq) if (likely(tsk)) { sk = &tsk->sk; if (likely(spin_trylock_bh(&sk->sk_lock.slock))) { - tipc_sk_enqueue(inputq, sk, dport); + tipc_sk_enqueue(inputq, sk, dport, &xmitq); spin_unlock_bh(&sk->sk_lock.slock); } + /* Send pending response/rejected messages, if any */ + while ((skb = __skb_dequeue(&xmitq))) { + dnode = msg_destnode(buf_msg(skb)); + tipc_node_xmit_skb(net, skb, dnode, dport); + } sock_put(sk); continue; } diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c index 6af78c6276b4..78d6b78de29d 100644 --- a/net/tipc/udp_media.c +++ b/net/tipc/udp_media.c @@ -52,7 +52,7 @@ /* IANA assigned UDP port */ #define UDP_PORT_DEFAULT 6118 -#define UDP_MIN_HEADROOM 28 +#define UDP_MIN_HEADROOM 48 static const struct nla_policy tipc_nl_udp_policy[TIPC_NLA_UDP_MAX + 1] = { [TIPC_NLA_UDP_UNSPEC] = {.type = NLA_UNSPEC}, @@ -376,6 +376,11 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b, udp_conf.local_ip.s_addr = htonl(INADDR_ANY); udp_conf.use_udp_checksums = false; ub->ifindex = dev->ifindex; + if (tipc_mtu_bad(dev, sizeof(struct iphdr) + + sizeof(struct udphdr))) { + err = -EINVAL; + goto err; + } b->mtu = dev->mtu - sizeof(struct iphdr) - sizeof(struct udphdr); #if IS_ENABLED(CONFIG_IPV6) diff --git a/net/unix/garbage.c b/net/unix/garbage.c index 6a0d48525fcf..c36757e72844 100644 --- a/net/unix/garbage.c +++ b/net/unix/garbage.c @@ -146,6 +146,7 @@ void unix_notinflight(struct user_struct *user, struct file *fp) if (s) { struct unix_sock *u = unix_sk(s); + BUG_ON(!atomic_long_read(&u->inflight)); BUG_ON(list_empty(&u->link)); if (atomic_long_dec_and_test(&u->inflight)) @@ -341,6 +342,14 @@ void unix_gc(void) } list_del(&cursor); + /* Now gc_candidates contains only garbage. Restore original + * inflight counters for these as well, and remove the skbuffs + * which are creating the cycle(s). + */ + skb_queue_head_init(&hitlist); + list_for_each_entry(u, &gc_candidates, link) + scan_children(&u->sk, inc_inflight, &hitlist); + /* not_cycle_list contains those sockets which do not make up a * cycle. Restore these to the inflight list. */ @@ -350,14 +359,6 @@ void unix_gc(void) list_move_tail(&u->link, &gc_inflight_list); } - /* Now gc_candidates contains only garbage. Restore original - * inflight counters for these as well, and remove the skbuffs - * which are creating the cycle(s). - */ - skb_queue_head_init(&hitlist); - list_for_each_entry(u, &gc_candidates, link) - scan_children(&u->sk, inc_inflight, &hitlist); - spin_unlock(&unix_gc_lock); /* Here we are. Hitlist is filled. Die. */ diff --git a/net/vmw_vsock/vmci_transport.c b/net/vmw_vsock/vmci_transport.c index 0a369bb440e7..662bdd20a748 100644 --- a/net/vmw_vsock/vmci_transport.c +++ b/net/vmw_vsock/vmci_transport.c @@ -842,7 +842,7 @@ static void vmci_transport_peer_detach_cb(u32 sub_id, * qp_handle. */ if (vmci_handle_is_invalid(e_payload->handle) || - vmci_handle_is_equal(trans->qp_handle, e_payload->handle)) + !vmci_handle_is_equal(trans->qp_handle, e_payload->handle)) return; /* We don't ask for delayed CBs when we subscribe to this event (we @@ -2154,7 +2154,7 @@ module_exit(vmci_transport_exit); MODULE_AUTHOR("VMware, Inc."); MODULE_DESCRIPTION("VMCI transport for Virtual Sockets"); -MODULE_VERSION("1.0.2.0-k"); +MODULE_VERSION("1.0.3.0-k"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("vmware_vsock"); MODULE_ALIAS_NETPROTO(PF_VSOCK); diff --git a/net/wireless/core.h b/net/wireless/core.h index 5cfe6fd72d52..be5ab8c13a39 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -208,16 +208,7 @@ struct cfg80211_event { enum cfg80211_event_type type; union { - struct { - u8 bssid[ETH_ALEN]; - const u8 *req_ie; - const u8 *resp_ie; - size_t req_ie_len; - size_t resp_ie_len; - struct cfg80211_bss *bss; - int status; /* -1 = failed; 0..65535 = status code */ - enum nl80211_timeout_reason timeout_reason; - } cr; + struct cfg80211_connect_resp_params cr; struct { const u8 *req_ie; const u8 *resp_ie; @@ -373,12 +364,9 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, struct cfg80211_connect_params *connect, struct cfg80211_cached_keys *connkeys, const u8 *prev_bssid); -void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, - int status, bool wextev, - struct cfg80211_bss *bss, - enum nl80211_timeout_reason timeout_reason); +void __cfg80211_connect_result(struct net_device *dev, + struct cfg80211_connect_resp_params *params, + bool wextev); void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, size_t ie_len, u16 reason, bool from_ap); int cfg80211_disconnect(struct cfg80211_registered_device *rdev, diff --git a/net/wireless/db.txt b/net/wireless/db.txt index a71f8801edb3..3c431e49aef1 100644 --- a/net/wireless/db.txt +++ b/net/wireless/db.txt @@ -59,6 +59,8 @@ country AR: (5490 - 5590 @ 80), (36) (5650 - 5730 @ 80), (36) (5735 - 5835 @ 80), (36) + # 60 gHz band channels 1-3 + (57240 - 63720 @ 2160), (40), NO-OUTDOOR country AS: DFS-FCC (2402 - 2472 @ 40), (30) @@ -82,7 +84,7 @@ country AT: DFS-ETSI (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR + (57000 - 66000 @ 2160), (40) country AU: DFS-FCC (2402 - 2482 @ 40), (20) @@ -91,6 +93,8 @@ country AU: DFS-FCC (5490 - 5590 @ 80), (24), DFS (5650 - 5730 @ 80), (24), DFS (5735 - 5835 @ 80), (30) + # 60 gHz band channels 1-4 + (57240 - 65880 @ 2160), (43), NO-OUTDOOR country AW: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -108,8 +112,6 @@ country BA: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR country BB: DFS-FCC (2402 - 2482 @ 40), (20) @@ -136,7 +138,7 @@ country BE: DFS-ETSI (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR + (57000 - 66000 @ 2160), (40) country BF: DFS-FCC (2402 - 2482 @ 40), (20) @@ -160,7 +162,7 @@ country BG: DFS-ETSI (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR + (57000 - 66000 @ 2160), (40) country BH: (2402 - 2482 @ 40), (20) @@ -190,6 +192,8 @@ country BO: DFS-ETSI (2402 - 2482 @ 40), (20) (5250 - 5330 @ 80), (30), DFS (5735 - 5835 @ 80), (30) + # 60 gHz band channels 1-3, FCC + (57240 - 63720 @ 2160), (40) country BR: DFS-FCC (2402 - 2482 @ 40), (30) @@ -197,6 +201,8 @@ country BR: DFS-FCC (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) + # 60 gHz band channels 1-3 + (57240 - 63720 @ 2160), (40) country BS: DFS-FCC (2402 - 2482 @ 40), (20) @@ -230,6 +236,8 @@ country CA: DFS-FCC (5490 - 5590 @ 80), (24), DFS (5650 - 5730 @ 80), (24), DFS (5735 - 5835 @ 80), (30) + # 60 gHz band channels 1-3 + (57240 - 63720 @ 2160), (40) country CF: DFS-FCC (2402 - 2482 @ 40), (20) @@ -252,6 +260,8 @@ country CH: DFS-ETSI (5890 - 5910 @ 10), (30) (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) + # 60 gHz band channels 1-4, ref: Etsi En 302 567 + (57000 - 66000 @ 2160), (40) country CI: DFS-FCC (2402 - 2482 @ 40), (20) @@ -264,17 +274,16 @@ country CL: (2402 - 2482 @ 40), (20) (5170 - 5330 @ 160), (20) (5735 - 5835 @ 80), (20) + # 60 gHz band channels 1-3 + (57240 - 63720 @ 2160), (50), NO-OUTDOOR country CN: DFS-FCC (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5735 - 5835 @ 80), (33) - # 60 gHz band channels 1,4: 28dBm, channels 2,3: 44dBm - # ref: http://www.miit.gov.cn/n11293472/n11505629/n11506593/n11960250/n11960606/n11960700/n12330791.files/n12330790.pdf - (57240 - 59400 @ 2160), (28) + # 60 gHz band channels 2,3: 44dBm (59400 - 63720 @ 2160), (44) - (63720 - 65880 @ 2160), (28) country CO: DFS-FCC (2402 - 2482 @ 40), (20) @@ -289,6 +298,8 @@ country CR: DFS-FCC (5250 - 5330 @ 20), (24), DFS (5490 - 5730 @ 20), (24), DFS (5735 - 5835 @ 20), (30) + # 60 gHz band channels 1-3 + (57240 - 63720 @ 2160), (30) country CX: DFS-FCC (2402 - 2482 @ 40), (20) @@ -312,7 +323,7 @@ country CY: DFS-ETSI (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR + (57000 - 66000 @ 2160), (40) # Data from http://www.ctu.eu/164/download/VOR/VOR-12-08-2005-34.pdf # and http://www.ctu.eu/164/download/VOR/VOR-12-05-2007-6-AN.pdf @@ -331,7 +342,7 @@ country CZ: DFS-ETSI (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR + (57000 - 66000 @ 2160), (40) # Data from "Frequenznutzungsplan" (as published in April 2008), downloaded from # http://www.bundesnetzagentur.de/cae/servlet/contentblob/38448/publicationFile/2659/Frequenznutzungsplan2008_Id17448pdf.pdf @@ -354,7 +365,7 @@ country DE: DFS-ETSI (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR + (57000 - 66000 @ 2160), (40) country DK: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -371,7 +382,7 @@ country DK: DFS-ETSI (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR + (57000 - 66000 @ 2160), (40) country DM: DFS-FCC (2402 - 2472 @ 40), (30) @@ -397,6 +408,8 @@ country EC: DFS-FCC (5250 - 5330 @ 20), (24), DFS (5490 - 5730 @ 20), (24), DFS (5735 - 5835 @ 20), (30) + # 60 gHz band channels 1-3, FCC + (57240 - 63720 @ 2160), (40) country EE: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -413,7 +426,7 @@ country EE: DFS-ETSI (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR + (57000 - 66000 @ 2160), (40) country EG: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -435,7 +448,7 @@ country ES: DFS-ETSI (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR + (57000 - 66000 @ 2160), (40) country ET: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -458,7 +471,7 @@ country FI: DFS-ETSI (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR + (57000 - 66000 @ 2160), (40) country FM: DFS-FCC (2402 - 2472 @ 40), (30) @@ -482,7 +495,7 @@ country FR: DFS-ETSI (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR + (57000 - 66000 @ 2160), (40) country GB: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -499,7 +512,7 @@ country GB: DFS-ETSI (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR + (57000 - 66000 @ 2160), (40) country GD: DFS-FCC (2402 - 2472 @ 40), (30) @@ -512,8 +525,6 @@ country GE: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (18), AUTO-BW (5250 - 5330 @ 80), (18), DFS, AUTO-BW - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR country GF: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -555,7 +566,7 @@ country GR: DFS-ETSI (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR + (57000 - 66000 @ 2160), (40) country GT: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -569,6 +580,8 @@ country GU: DFS-FCC (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) + # 60 gHz band channels 1-3, FCC + (57240 - 63720 @ 2160), (40) country GY: (2402 - 2482 @ 40), (30) @@ -580,12 +593,16 @@ country HK: DFS-FCC (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) + # 60 gHz band channels 1-4, ref: FCC/EU + (57240 - 65880 @ 2160), (40) country HN: (2402 - 2482 @ 40), (20) (5170 - 5330 @ 160), (24) (5490 - 5730 @ 160), (24) (5735 - 5835 @ 80), (30) + # 60 gHz band channels 1-3, FCC + (57240 - 63720 @ 2160), (40) country HR: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -602,7 +619,7 @@ country HR: DFS-ETSI (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR + (57000 - 66000 @ 2160), (40) country HT: DFS-FCC (2402 - 2472 @ 40), (30) @@ -626,7 +643,7 @@ country HU: DFS-ETSI (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR + (57000 - 66000 @ 2160), (40) country ID: # ref: http://www.postel.go.id/content/ID/regulasi/standardisasi/kepdir/bwa%205,8%20ghz.pdf @@ -638,7 +655,6 @@ country IE: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 60 gHz band channels 1-4, ref: Etsi En 302 567 # 5.9ghz band # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf (5850 - 5870 @ 10), (30) @@ -648,13 +664,15 @@ country IE: DFS-ETSI (5890 - 5910 @ 10), (30) (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR + # 60 gHz band channels 1-4, ref: Etsi En 302 567 + (57000 - 66000 @ 2160), (40) country IL: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW + # 60 gHz band channels 1-4, base on Etsi En 302 567 + (57000 - 66000 @ 2160), (40) country IN: (2402 - 2482 @ 40), (20) @@ -680,7 +698,7 @@ country IS: DFS-ETSI (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR + (57000 - 66000 @ 2160), (40) country IT: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -697,7 +715,7 @@ country IT: DFS-ETSI (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR + (57000 - 66000 @ 2160), (40) country JM: DFS-FCC (2402 - 2482 @ 40), (20) @@ -705,11 +723,15 @@ country JM: DFS-FCC (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) + # 60 gHz band channels 1-3, FCC + (57240 - 63720 @ 2160), (40) country JO: (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23) (5735 - 5835 @ 80), (23) + # 60 gHz band channels 1-4, ref: Etsi En 302 567 + ((57000 - 66000 @ 2160), (40) country JP: DFS-JP (2402 - 2482 @ 40), (20) @@ -717,9 +739,8 @@ country JP: DFS-JP (5170 - 5250 @ 80), (20), AUTO-BW, NO-OUTDOOR (5250 - 5330 @ 80), (20), DFS, AUTO-BW, NO-OUTDOOR (5490 - 5710 @ 160), (20), DFS - # 60 GHz band channels 2-4 at 10mW, - # ref: http://www.arib.or.jp/english/html/overview/doc/1-STD-T74v1_1.pdf - (59000 - 66000 @ 2160), (10 mW) + # 60 gHz band channels 1-4 + (57240 - 65880 @ 2160), (40) country KE: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -748,7 +769,7 @@ country KR: DFS-ETSI (5735 - 5835 @ 80), (30) # 60 GHz band channels 1-4, # ref: http://www.law.go.kr/%ED%96%89%EC%A0%95%EA%B7%9C%EC%B9%99/%EB%AC%B4%EC%84%A0%EC%84%A4%EB%B9%84%EA%B7%9C%EC%B9%99 - (57000 - 66000 @ 2160), (43) + (57240 - 65880 @ 2160), (43) country KP: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -800,6 +821,8 @@ country LI: DFS-ETSI (5890 - 5910 @ 10), (30) (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) + # 60 gHz band channels 1-4, ref: Etsi En 302 567 + (57000 - 66000 @ 2160), (40) country LK: DFS-FCC (2402 - 2482 @ 40), (20) @@ -829,7 +852,7 @@ country LT: DFS-ETSI (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR + (57000 - 66000 @ 2160), (40) country LU: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -846,7 +869,7 @@ country LU: DFS-ETSI (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR + (57000 - 66000 @ 2160), (40) country LV: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -863,12 +886,14 @@ country LV: DFS-ETSI (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR + (57000 - 66000 @ 2160), (40) country MA: DFS-ETSI (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW + # 60 gHz band channels 1-4, ref: Etsi En 302 567 + (57000 - 66000 @ 2160), (40) country MC: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -906,8 +931,6 @@ country MK: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR country MN: DFS-FCC (2402 - 2482 @ 40), (20) @@ -957,7 +980,7 @@ country MT: DFS-ETSI (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR + (57000 - 66000 @ 2160), (40) country MU: DFS-FCC (2402 - 2482 @ 40), (20) @@ -984,6 +1007,8 @@ country MX: DFS-FCC (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) + # 60 gHz band channels 1-3, FCC + (57240 - 63720 @ 2160), (40) country MY: DFS-FCC (2402 - 2482 @ 40), (20) @@ -991,6 +1016,8 @@ country MY: DFS-FCC (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5650 @ 160), (24), DFS (5735 - 5815 @ 80), (24) + # 60 gHz band channels 1-3 + (57240 - 63720 @ 2160), (40) country NA: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -1010,6 +1037,8 @@ country NI: DFS-FCC (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) + # 60 gHz band channels 1-3, FCC + (57240 - 63720 @ 2160), (40) country NL: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -1026,7 +1055,7 @@ country NL: DFS-ETSI (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR + (57000 - 66000 @ 2160), (40) country NO: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -1043,7 +1072,7 @@ country NO: DFS-ETSI (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR + (57000 - 66000 @ 2160), (40) country NP: (2402 - 2482 @ 40), (20) @@ -1056,6 +1085,8 @@ country NZ: DFS-FCC (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) + # 60 gHz band channels 1-4, ref: Etsi En 302 567 + (57000 - 66000 @ 2160), (40) country OM: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -1095,6 +1126,8 @@ country PH: DFS-FCC (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) + # 60 gHz band channels 1-4, ref: Etsi En 302 567 + (57000 - 66000 @ 2160), (40) country PK: (2402 - 2482 @ 40), (30) @@ -1115,7 +1148,7 @@ country PL: DFS-ETSI (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR + (57000 - 66000 @ 2160), (40) country PM: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -1152,7 +1185,7 @@ country PT: DFS-ETSI (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR + (57000 - 66000 @ 2160), (40) country PW: DFS-FCC (2402 - 2472 @ 40), (30) @@ -1167,6 +1200,8 @@ country PY: DFS-FCC (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) + # 60 gHz band channels 1-3, FCC + (57240 - 63720 @ 2160), (40) country QA: (2402 - 2482 @ 40), (20) @@ -1193,7 +1228,7 @@ country RO: DFS-ETSI (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR + (57000 - 66000 @ 2160), (40) # Source: # http://www.ratel.rs/upload/documents/Plan_namene/Plan_namene-sl_glasnik.pdf @@ -1203,13 +1238,15 @@ country RS: DFS-ETSI (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR + (57000 - 66000 @ 2160), (40) country RU: (2402 - 2482 @ 40), (20) (5170 - 5330 @ 160), (23) (5490 - 5730 @ 160), (30) (5735 - 5835 @ 80), (30) + # 60 gHz band channels 1-4 + (57240 - 65880 @ 2160), (40) country RW: DFS-FCC (2402 - 2482 @ 40), (20) @@ -1239,7 +1276,7 @@ country SE: DFS-ETSI (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR + (57000 - 66000 @ 2160), (40) country SG: DFS-FCC (2402 - 2482 @ 40), (20) @@ -1247,6 +1284,8 @@ country SG: DFS-FCC (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) + # 60 gHz band channels 1-4, ref: Etsi En 302 567 + (57000 - 66000 @ 2160), (40), NO-OUTDOOR country SI: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -1263,7 +1302,7 @@ country SI: DFS-ETSI (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR + (57000 - 66000 @ 2160), (40) country SK: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -1280,7 +1319,7 @@ country SK: DFS-ETSI (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR + (57000 - 66000 @ 2160), (40) country SN: (2402 - 2482 @ 40), (20) @@ -1328,6 +1367,8 @@ country TH: DFS-FCC (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) + # 60 gHz band channels 1-4 + (57240 - 65880 @ 2160), (40) country TN: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -1339,14 +1380,14 @@ country TR: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS - # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR country TT: (2402 - 2482 @ 40), (20) (5170 - 5330 @ 160), (27) (5490 - 5730 @ 160), (36) (5735 - 5835 @ 80), (36) + # 60 gHz band channels 1-3, FCC + (57240 - 63720 @ 2160), (40) country TW: DFS-FCC (2402 - 2472 @ 40), (30) @@ -1354,6 +1395,8 @@ country TW: DFS-FCC (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) + # 60 gHz band channels 1-3, FCC + (57240 - 63720 @ 2160), (40) country TZ: (2402 - 2482 @ 40), (20) @@ -1373,7 +1416,7 @@ country UA: DFS-ETSI (5490 - 5670 @ 160), (20), DFS (5735 - 5835 @ 80), (20) # 60 gHz band channels 1-4, ref: Etsi En 302 567 - (57240 - 65880 @ 2160), (40), NO-OUTDOOR + (57240 - 65880 @ 2160), (20) country UG: DFS-FCC (2402 - 2482 @ 40), (20) @@ -1402,14 +1445,16 @@ country US: DFS-FCC (5910 - 5930 @ 10), (30) # 60g band # reference: http://cfr.regstoday.com/47cfr15.aspx#47_CFR_15p255 - # channels 1,2,3, EIRP=40dBm(43dBm peak) - (57240 - 63720 @ 2160), (40) + # channels 1,2,3,4,5,6 EIRP=40dBm(43dBm peak) + (57240 - 70200 @ 2160), (40) country UY: DFS-FCC (2402 - 2482 @ 40), (20) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5735 - 5835 @ 80), (30) + # 60 gHz band channels 1-4 + (57240 - 65880 @ 2160), (40) country UZ: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -1441,6 +1486,8 @@ country VN: DFS-FCC (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) + # 60 gHz band channels 1-4 + (57240 - 65880 @ 2160), (40) country VU: DFS-FCC (2402 - 2482 @ 40), (20) @@ -1467,7 +1514,6 @@ country XA: DFS-JP (5170 - 5250 @ 80), (20), NO-IR, AUTO-BW, NO-OUTDOOR (5250 - 5330 @ 80), (20), DFS, AUTO-BW, NO-OUTDOOR (5490 - 5710 @ 160), (20), DFS - (59000 - 66000 @ 2160), (10 mW) country YE: (2402 - 2482 @ 40), (20) @@ -1484,6 +1530,8 @@ country ZA: DFS-FCC (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) + # 60 gHz band channels 1-4 + (57240 - 65880 @ 2160), (40), NO-OUTDOOR country ZW: DFS-ETSI (2402 - 2482 @ 40), (20) diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index e2b1333cc4e4..2bc6eaa766c7 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -26,9 +26,16 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss, struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; - u8 *ie = mgmt->u.assoc_resp.variable; - int ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); - u16 status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); + struct cfg80211_connect_resp_params cr; + + memset(&cr, 0, sizeof(cr)); + cr.status = (int)le16_to_cpu(mgmt->u.assoc_resp.status_code); + cr.bssid = mgmt->bssid; + cr.bss = bss; + cr.resp_ie = mgmt->u.assoc_resp.variable; + cr.resp_ie_len = + len - offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); + cr.timeout_reason = NL80211_TIMEOUT_UNSPECIFIED; trace_cfg80211_send_rx_assoc(dev, bss); @@ -38,7 +45,7 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss, * and got a reject -- we only try again with an assoc * frame instead of reassoc. */ - if (cfg80211_sme_rx_assoc_resp(wdev, status_code)) { + if (cfg80211_sme_rx_assoc_resp(wdev, cr.status)) { cfg80211_unhold_bss(bss_from_pub(bss)); cfg80211_put_bss(wiphy, bss); return; @@ -46,10 +53,7 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss, nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL, uapsd_queues); /* update current_bss etc., consumes the bss reference */ - __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs, - status_code, - status_code == WLAN_STATUS_SUCCESS, bss, - NL80211_TIMEOUT_UNSPECIFIED); + __cfg80211_connect_result(dev, &cr, cr.status == WLAN_STATUS_SUCCESS); } EXPORT_SYMBOL(cfg80211_rx_assoc_resp); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index adc62ec5507a..d0d09c290ff8 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -408,7 +408,19 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST] = { .len = sizeof(struct nl80211_bss_select_rssi_adjust) }, + [NL80211_ATTR_FILS_KEK] = { .type = NLA_BINARY, + .len = FILS_MAX_KEK_LEN }, + [NL80211_ATTR_FILS_NONCES] = { .len = 2 * FILS_NONCE_LEN }, [NL80211_ATTR_TIMEOUT_REASON] = { .type = NLA_U32 }, + [NL80211_ATTR_FILS_ERP_USERNAME] = { .type = NLA_BINARY, + .len = FILS_ERP_MAX_USERNAME_LEN }, + [NL80211_ATTR_FILS_ERP_REALM] = { .type = NLA_BINARY, + .len = FILS_ERP_MAX_REALM_LEN }, + [NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM] = { .type = NLA_U16 }, + [NL80211_ATTR_FILS_ERP_RRK] = { .type = NLA_BINARY, + .len = FILS_ERP_MAX_RRK_LEN }, + [NL80211_ATTR_FILS_CACHE_ID] = { .len = 2 }, + [NL80211_ATTR_PMK] = { .type = NLA_BINARY, .len = PMK_MAX_LEN }, }; /* policy for the key attributes */ @@ -509,21 +521,17 @@ static int nl80211_prepare_wdev_dump(struct sk_buff *skb, { int err; - rtnl_lock(); - if (!cb->args[0]) { err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, nl80211_fam.attrbuf, nl80211_fam.maxattr, nl80211_policy); if (err) - goto out_unlock; + return err; *wdev = __cfg80211_wdev_from_attrs(sock_net(skb->sk), nl80211_fam.attrbuf); - if (IS_ERR(*wdev)) { - err = PTR_ERR(*wdev); - goto out_unlock; - } + if (IS_ERR(*wdev)) + return PTR_ERR(*wdev); *rdev = wiphy_to_rdev((*wdev)->wiphy); /* 0 is the first index - add 1 to parse only once */ cb->args[0] = (*rdev)->wiphy_idx + 1; @@ -533,10 +541,8 @@ static int nl80211_prepare_wdev_dump(struct sk_buff *skb, struct wiphy *wiphy = wiphy_idx_to_wiphy(cb->args[0] - 1); struct wireless_dev *tmp; - if (!wiphy) { - err = -ENODEV; - goto out_unlock; - } + if (!wiphy) + return -ENODEV; *rdev = wiphy_to_rdev(wiphy); *wdev = NULL; @@ -547,21 +553,11 @@ static int nl80211_prepare_wdev_dump(struct sk_buff *skb, } } - if (!*wdev) { - err = -ENODEV; - goto out_unlock; - } + if (!*wdev) + return -ENODEV; } return 0; - out_unlock: - rtnl_unlock(); - return err; -} - -static void nl80211_finish_wdev_dump(struct cfg80211_registered_device *rdev) -{ - rtnl_unlock(); } /* IE validation */ @@ -3669,6 +3665,19 @@ static bool nl80211_valid_auth_type(struct cfg80211_registered_device *rdev, return false; return true; case NL80211_CMD_CONNECT: + /* SAE not supported yet */ + if (auth_type == NL80211_AUTHTYPE_SAE) + return false; + /* FILS with SK PFS or PK not supported yet */ + if (auth_type == NL80211_AUTHTYPE_FILS_SK_PFS || + auth_type == NL80211_AUTHTYPE_FILS_PK) + return false; + if (!wiphy_ext_feature_isset( + &rdev->wiphy, + NL80211_EXT_FEATURE_FILS_SK_OFFLOAD) && + auth_type == NL80211_AUTHTYPE_FILS_SK) + return false; + return true; case NL80211_CMD_START_AP: /* SAE not supported yet */ if (auth_type == NL80211_AUTHTYPE_SAE) @@ -4278,9 +4287,10 @@ static int nl80211_dump_station(struct sk_buff *skb, int sta_idx = cb->args[2]; int err; + rtnl_lock(); err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev); if (err) - return err; + goto out_err; if (!wdev->netdev) { err = -EINVAL; @@ -4316,7 +4326,7 @@ static int nl80211_dump_station(struct sk_buff *skb, cb->args[2] = sta_idx; err = skb->len; out_err: - nl80211_finish_wdev_dump(rdev); + rtnl_unlock(); return err; } @@ -5033,9 +5043,10 @@ static int nl80211_dump_mpath(struct sk_buff *skb, int path_idx = cb->args[2]; int err; + rtnl_lock(); err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev); if (err) - return err; + goto out_err; if (!rdev->ops->dump_mpath) { err = -EOPNOTSUPP; @@ -5069,7 +5080,7 @@ static int nl80211_dump_mpath(struct sk_buff *skb, cb->args[2] = path_idx; err = skb->len; out_err: - nl80211_finish_wdev_dump(rdev); + rtnl_unlock(); return err; } @@ -5229,9 +5240,10 @@ static int nl80211_dump_mpp(struct sk_buff *skb, int path_idx = cb->args[2]; int err; + rtnl_lock(); err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev); if (err) - return err; + goto out_err; if (!rdev->ops->dump_mpp) { err = -EOPNOTSUPP; @@ -5264,7 +5276,7 @@ static int nl80211_dump_mpp(struct sk_buff *skb, cb->args[2] = path_idx; err = skb->len; out_err: - nl80211_finish_wdev_dump(rdev); + rtnl_unlock(); return err; } @@ -7333,9 +7345,12 @@ static int nl80211_dump_scan(struct sk_buff *skb, struct netlink_callback *cb) int start = cb->args[2], idx = 0; int err; + rtnl_lock(); err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev); - if (err) + if (err) { + rtnl_unlock(); return err; + } wdev_lock(wdev); spin_lock_bh(&rdev->bss_lock); @@ -7358,7 +7373,7 @@ static int nl80211_dump_scan(struct sk_buff *skb, struct netlink_callback *cb) wdev_unlock(wdev); cb->args[2] = idx; - nl80211_finish_wdev_dump(rdev); + rtnl_unlock(); return skb->len; } @@ -7442,9 +7457,10 @@ static int nl80211_dump_survey(struct sk_buff *skb, struct netlink_callback *cb) int res; bool radio_stats; + rtnl_lock(); res = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev); if (res) - return res; + goto out_err; /* prepare_wdev_dump parsed the attributes */ radio_stats = nl80211_fam.attrbuf[NL80211_ATTR_SURVEY_RADIO_STATS]; @@ -7485,7 +7501,7 @@ static int nl80211_dump_survey(struct sk_buff *skb, struct netlink_callback *cb) cb->args[2] = survey_idx; res = skb->len; out_err: - nl80211_finish_wdev_dump(rdev); + rtnl_unlock(); return res; } @@ -7789,6 +7805,15 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) req.flags |= ASSOC_REQ_USE_RRM; } + if (info->attrs[NL80211_ATTR_FILS_KEK]) { + req.fils_kek = nla_data(info->attrs[NL80211_ATTR_FILS_KEK]); + req.fils_kek_len = nla_len(info->attrs[NL80211_ATTR_FILS_KEK]); + if (!info->attrs[NL80211_ATTR_FILS_NONCES]) + return -EINVAL; + req.fils_nonces = + nla_data(info->attrs[NL80211_ATTR_FILS_NONCES]); + } + err = nl80211_crypto_settings(rdev, info, &req.crypto, 1); if (!err) { wdev_lock(dev->ieee80211_ptr); @@ -8508,6 +8533,35 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) } } + if (wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_FILS_SK_OFFLOAD) && + info->attrs[NL80211_ATTR_FILS_ERP_USERNAME] && + info->attrs[NL80211_ATTR_FILS_ERP_REALM] && + info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM] && + info->attrs[NL80211_ATTR_FILS_ERP_RRK]) { + connect.fils_erp_username = + nla_data(info->attrs[NL80211_ATTR_FILS_ERP_USERNAME]); + connect.fils_erp_username_len = + nla_len(info->attrs[NL80211_ATTR_FILS_ERP_USERNAME]); + connect.fils_erp_realm = + nla_data(info->attrs[NL80211_ATTR_FILS_ERP_REALM]); + connect.fils_erp_realm_len = + nla_len(info->attrs[NL80211_ATTR_FILS_ERP_REALM]); + connect.fils_erp_next_seq_num = + nla_get_u16( + info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM]); + connect.fils_erp_rrk = + nla_data(info->attrs[NL80211_ATTR_FILS_ERP_RRK]); + connect.fils_erp_rrk_len = + nla_len(info->attrs[NL80211_ATTR_FILS_ERP_RRK]); + } else if (info->attrs[NL80211_ATTR_FILS_ERP_USERNAME] || + info->attrs[NL80211_ATTR_FILS_ERP_REALM] || + info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM] || + info->attrs[NL80211_ATTR_FILS_ERP_RRK]) { + kzfree(connkeys); + return -EINVAL; + } + wdev_lock(dev->ieee80211_ptr); err = cfg80211_connect(rdev, dev, &connect, connkeys, NULL); wdev_unlock(dev->ieee80211_ptr); @@ -8613,14 +8667,28 @@ static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info) memset(&pmksa, 0, sizeof(struct cfg80211_pmksa)); - if (!info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; - if (!info->attrs[NL80211_ATTR_PMKID]) return -EINVAL; pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]); - pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); + + if (info->attrs[NL80211_ATTR_MAC]) { + pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); + } else if (info->attrs[NL80211_ATTR_SSID] && + info->attrs[NL80211_ATTR_FILS_CACHE_ID] && + (info->genlhdr->cmd == NL80211_CMD_DEL_PMKSA || + info->attrs[NL80211_ATTR_PMK])) { + pmksa.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); + pmksa.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); + pmksa.cache_id = + nla_data(info->attrs[NL80211_ATTR_FILS_CACHE_ID]); + } else { + return -EINVAL; + } + if (info->attrs[NL80211_ATTR_PMK]) { + pmksa.pmk = nla_data(info->attrs[NL80211_ATTR_PMK]); + pmksa.pmk_len = nla_len(info->attrs[NL80211_ATTR_PMK]); + } if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) @@ -10561,17 +10629,13 @@ static int nl80211_prepare_vendor_dump(struct sk_buff *skb, void *data = NULL; unsigned int data_len = 0; - rtnl_lock(); - if (cb->args[0]) { /* subtract the 1 again here */ struct wiphy *wiphy = wiphy_idx_to_wiphy(cb->args[0] - 1); struct wireless_dev *tmp; - if (!wiphy) { - err = -ENODEV; - goto out_unlock; - } + if (!wiphy) + return -ENODEV; *rdev = wiphy_to_rdev(wiphy); *wdev = NULL; @@ -10592,13 +10656,11 @@ static int nl80211_prepare_vendor_dump(struct sk_buff *skb, nl80211_fam.attrbuf, nl80211_fam.maxattr, nl80211_policy); if (err) - goto out_unlock; + return err; if (!nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_ID] || - !nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_SUBCMD]) { - err = -EINVAL; - goto out_unlock; - } + !nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_SUBCMD]) + return -EINVAL; *wdev = __cfg80211_wdev_from_attrs(sock_net(skb->sk), nl80211_fam.attrbuf); @@ -10607,10 +10669,8 @@ static int nl80211_prepare_vendor_dump(struct sk_buff *skb, *rdev = __cfg80211_rdev_from_attrs(sock_net(skb->sk), nl80211_fam.attrbuf); - if (IS_ERR(*rdev)) { - err = PTR_ERR(*rdev); - goto out_unlock; - } + if (IS_ERR(*rdev)) + return PTR_ERR(*rdev); vid = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_ID]); subcmd = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_SUBCMD]); @@ -10623,19 +10683,15 @@ static int nl80211_prepare_vendor_dump(struct sk_buff *skb, if (vcmd->info.vendor_id != vid || vcmd->info.subcmd != subcmd) continue; - if (!vcmd->dumpit) { - err = -EOPNOTSUPP; - goto out_unlock; - } + if (!vcmd->dumpit) + return -EOPNOTSUPP; vcmd_idx = i; break; } - if (vcmd_idx < 0) { - err = -EOPNOTSUPP; - goto out_unlock; - } + if (vcmd_idx < 0) + return -EOPNOTSUPP; if (nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_DATA]) { data = nla_data(nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_DATA]); @@ -10652,9 +10708,6 @@ static int nl80211_prepare_vendor_dump(struct sk_buff *skb, /* keep rtnl locked in successful case */ return 0; - out_unlock: - rtnl_unlock(); - return err; } static int nl80211_vendor_cmd_dump(struct sk_buff *skb, @@ -10669,9 +10722,10 @@ static int nl80211_vendor_cmd_dump(struct sk_buff *skb, int err; struct nlattr *vendor_data; + rtnl_lock(); err = nl80211_prepare_vendor_dump(skb, cb, &rdev, &wdev); if (err) - return err; + goto out; vcmd_idx = cb->args[2]; data = (void *)cb->args[3]; @@ -10680,18 +10734,26 @@ static int nl80211_vendor_cmd_dump(struct sk_buff *skb, if (vcmd->flags & (WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV)) { - if (!wdev) - return -EINVAL; + if (!wdev) { + err = -EINVAL; + goto out; + } if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_NETDEV && - !wdev->netdev) - return -EINVAL; + !wdev->netdev) { + err = -EINVAL; + goto out; + } if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_RUNNING) { if (wdev->netdev && - !netif_running(wdev->netdev)) - return -ENETDOWN; - if (!wdev->netdev && !wdev->p2p_started) - return -ENETDOWN; + !netif_running(wdev->netdev)) { + err = -ENETDOWN; + goto out; + } + if (!wdev->netdev && !wdev->p2p_started) { + err = -ENETDOWN; + goto out; + } } } @@ -12135,7 +12197,7 @@ static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev, struct sk_buff *msg; void *hdr; - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); + msg = nlmsg_new(100 + len, gfp); if (!msg) return; @@ -12279,17 +12341,16 @@ void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev, } void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, - int status, - enum nl80211_timeout_reason timeout_reason, + struct net_device *netdev, + struct cfg80211_connect_resp_params *cr, gfp_t gfp) { struct sk_buff *msg; void *hdr; - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); + msg = nlmsg_new(100 + cr->req_ie_len + cr->resp_ie_len + + cr->fils_kek_len + cr->pmk_len + + (cr->pmkid ? WLAN_PMKID_LEN : 0), gfp); if (!msg) return; @@ -12301,17 +12362,31 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || - (bssid && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid)) || + (cr->bssid && + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, cr->bssid)) || nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, - status < 0 ? WLAN_STATUS_UNSPECIFIED_FAILURE : - status) || - (status < 0 && + cr->status < 0 ? WLAN_STATUS_UNSPECIFIED_FAILURE : + cr->status) || + (cr->status < 0 && (nla_put_flag(msg, NL80211_ATTR_TIMED_OUT) || - nla_put_u32(msg, NL80211_ATTR_TIMEOUT_REASON, timeout_reason))) || - (req_ie && - nla_put(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie)) || - (resp_ie && - nla_put(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie))) + nla_put_u32(msg, NL80211_ATTR_TIMEOUT_REASON, + cr->timeout_reason))) || + (cr->req_ie && + nla_put(msg, NL80211_ATTR_REQ_IE, cr->req_ie_len, cr->req_ie)) || + (cr->resp_ie && + nla_put(msg, NL80211_ATTR_RESP_IE, cr->resp_ie_len, + cr->resp_ie)) || + (cr->update_erp_next_seq_num && + nla_put_u16(msg, NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM, + cr->fils_erp_next_seq_num)) || + (cr->status == WLAN_STATUS_SUCCESS && + ((cr->fils_kek && + nla_put(msg, NL80211_ATTR_FILS_KEK, cr->fils_kek_len, + cr->fils_kek)) || + (cr->pmk && + nla_put(msg, NL80211_ATTR_PMK, cr->pmk_len, cr->pmk)) || + (cr->pmkid && + nla_put(msg, NL80211_ATTR_PMKID, WLAN_PMKID_LEN, cr->pmkid))))) goto nla_put_failure; genlmsg_end(msg, hdr); @@ -12334,7 +12409,7 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev, struct sk_buff *msg; void *hdr; - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); + msg = nlmsg_new(100 + req_ie_len + resp_ie_len, gfp); if (!msg) return; @@ -12372,7 +12447,7 @@ void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, struct sk_buff *msg; void *hdr; - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + msg = nlmsg_new(100 + ie_len, GFP_KERNEL); if (!msg) return; @@ -12449,7 +12524,7 @@ void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *addr, trace_cfg80211_notify_new_peer_candidate(dev, addr); - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); + msg = nlmsg_new(100 + ie_len, gfp); if (!msg) return; @@ -12818,7 +12893,7 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, struct sk_buff *msg; void *hdr; - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); + msg = nlmsg_new(100 + len, gfp); if (!msg) return -ENOMEM; @@ -12861,7 +12936,7 @@ void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie, trace_cfg80211_mgmt_tx_status(wdev, cookie, ack); - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); + msg = nlmsg_new(100 + len, gfp); if (!msg) return; @@ -13665,7 +13740,7 @@ void cfg80211_ft_event(struct net_device *netdev, if (!ft_event->target_ap) return; - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + msg = nlmsg_new(100 + ft_event->ric_ies_len, GFP_KERNEL); if (!msg) return; diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index a749c9be2836..79e9270d5067 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -52,11 +52,8 @@ void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *addr, gfp_t gfp); void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, - int status, - enum nl80211_timeout_reason timeout_reason, + struct net_device *netdev, + struct cfg80211_connect_resp_params *params, gfp_t gfp); void nl80211_send_roamed(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *bssid, diff --git a/net/wireless/sme.c b/net/wireless/sme.c index fe8a9062de98..85c12c7d0ed1 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -276,10 +276,13 @@ void cfg80211_conn_work(struct work_struct *work) } treason = NL80211_TIMEOUT_UNSPECIFIED; if (cfg80211_conn_do_work(wdev, &treason)) { - __cfg80211_connect_result( - wdev->netdev, bssid, - NULL, 0, NULL, 0, -1, false, NULL, - treason); + struct cfg80211_connect_resp_params cr; + + memset(&cr, 0, sizeof(cr)); + cr.status = -1; + cr.bssid = bssid; + cr.timeout_reason = treason; + __cfg80211_connect_result(wdev->netdev, &cr, false); } wdev_unlock(wdev); } @@ -382,10 +385,13 @@ void cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len) wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; schedule_work(&rdev->conn_work); } else if (status_code != WLAN_STATUS_SUCCESS) { - __cfg80211_connect_result(wdev->netdev, mgmt->bssid, - NULL, 0, NULL, 0, - status_code, false, NULL, - NL80211_TIMEOUT_UNSPECIFIED); + struct cfg80211_connect_resp_params cr; + + memset(&cr, 0, sizeof(cr)); + cr.status = status_code; + cr.bssid = mgmt->bssid; + cr.timeout_reason = NL80211_TIMEOUT_UNSPECIFIED; + __cfg80211_connect_result(wdev->netdev, &cr, false); } else if (wdev->conn->state == CFG80211_CONN_AUTHENTICATING) { wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; schedule_work(&rdev->conn_work); @@ -683,12 +689,9 @@ static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work); */ /* This method must consume bss one way or another */ -void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, - int status, bool wextev, - struct cfg80211_bss *bss, - enum nl80211_timeout_reason timeout_reason) +void __cfg80211_connect_result(struct net_device *dev, + struct cfg80211_connect_resp_params *cr, + bool wextev) { struct wireless_dev *wdev = dev->ieee80211_ptr; const u8 *country_ie; @@ -700,48 +703,48 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) { - cfg80211_put_bss(wdev->wiphy, bss); + cfg80211_put_bss(wdev->wiphy, cr->bss); return; } - nl80211_send_connect_result(wiphy_to_rdev(wdev->wiphy), dev, - bssid, req_ie, req_ie_len, - resp_ie, resp_ie_len, - status, timeout_reason, GFP_KERNEL); + nl80211_send_connect_result(wiphy_to_rdev(wdev->wiphy), dev, cr, + GFP_KERNEL); #ifdef CONFIG_CFG80211_WEXT if (wextev) { - if (req_ie && status == WLAN_STATUS_SUCCESS) { + if (cr->req_ie && cr->status == WLAN_STATUS_SUCCESS) { memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = req_ie_len; - wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, req_ie); + wrqu.data.length = cr->req_ie_len; + wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, + cr->req_ie); } - if (resp_ie && status == WLAN_STATUS_SUCCESS) { + if (cr->resp_ie && cr->status == WLAN_STATUS_SUCCESS) { memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = resp_ie_len; - wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie); + wrqu.data.length = cr->resp_ie_len; + wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, + cr->resp_ie); } memset(&wrqu, 0, sizeof(wrqu)); wrqu.ap_addr.sa_family = ARPHRD_ETHER; - if (bssid && status == WLAN_STATUS_SUCCESS) { - memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); - memcpy(wdev->wext.prev_bssid, bssid, ETH_ALEN); + if (cr->bssid && cr->status == WLAN_STATUS_SUCCESS) { + memcpy(wrqu.ap_addr.sa_data, cr->bssid, ETH_ALEN); + memcpy(wdev->wext.prev_bssid, cr->bssid, ETH_ALEN); wdev->wext.prev_bssid_valid = true; } wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); } #endif - if (!bss && (status == WLAN_STATUS_SUCCESS)) { + if (!cr->bss && (cr->status == WLAN_STATUS_SUCCESS)) { WARN_ON_ONCE(!wiphy_to_rdev(wdev->wiphy)->ops->connect); - bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, - wdev->ssid, wdev->ssid_len, - wdev->conn_bss_type, - IEEE80211_PRIVACY_ANY); - if (bss) - cfg80211_hold_bss(bss_from_pub(bss)); + cr->bss = cfg80211_get_bss(wdev->wiphy, NULL, cr->bssid, + wdev->ssid, wdev->ssid_len, + wdev->conn_bss_type, + IEEE80211_PRIVACY_ANY); + if (cr->bss) + cfg80211_hold_bss(bss_from_pub(cr->bss)); } if (wdev->current_bss) { @@ -750,27 +753,27 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, wdev->current_bss = NULL; } - if (status != WLAN_STATUS_SUCCESS) { + if (cr->status != WLAN_STATUS_SUCCESS) { kzfree(wdev->connect_keys); wdev->connect_keys = NULL; wdev->ssid_len = 0; - if (bss) { - cfg80211_unhold_bss(bss_from_pub(bss)); - cfg80211_put_bss(wdev->wiphy, bss); + if (cr->bss) { + cfg80211_unhold_bss(bss_from_pub(cr->bss)); + cfg80211_put_bss(wdev->wiphy, cr->bss); } cfg80211_sme_free(wdev); return; } - if (WARN_ON(!bss)) + if (WARN_ON(!cr->bss)) return; - wdev->current_bss = bss_from_pub(bss); + wdev->current_bss = bss_from_pub(cr->bss); cfg80211_upload_connect_keys(wdev); rcu_read_lock(); - country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); + country_ie = ieee80211_bss_get_ie(cr->bss, WLAN_EID_COUNTRY); if (!country_ie) { rcu_read_unlock(); return; @@ -787,64 +790,95 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, * - country_ie + 2, the start of the country ie data, and * - and country_ie[1] which is the IE length */ - regulatory_hint_country_ie(wdev->wiphy, bss->channel->band, + regulatory_hint_country_ie(wdev->wiphy, cr->bss->channel->band, country_ie + 2, country_ie[1]); kfree(country_ie); } /* Consumes bss object one way or another */ -void cfg80211_connect_bss(struct net_device *dev, const u8 *bssid, - struct cfg80211_bss *bss, const u8 *req_ie, - size_t req_ie_len, const u8 *resp_ie, - size_t resp_ie_len, int status, gfp_t gfp, - enum nl80211_timeout_reason timeout_reason) +void cfg80211_connect_done(struct net_device *dev, + struct cfg80211_connect_resp_params *params, + gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); struct cfg80211_event *ev; unsigned long flags; + u8 *next; - if (bss) { + if (params->bss) { /* Make sure the bss entry provided by the driver is valid. */ - struct cfg80211_internal_bss *ibss = bss_from_pub(bss); + struct cfg80211_internal_bss *ibss = bss_from_pub(params->bss); if (WARN_ON(list_empty(&ibss->list))) { - cfg80211_put_bss(wdev->wiphy, bss); + cfg80211_put_bss(wdev->wiphy, params->bss); return; } } - ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); + ev = kzalloc(sizeof(*ev) + (params->bssid ? ETH_ALEN : 0) + + params->req_ie_len + params->resp_ie_len + + params->fils_kek_len + params->pmk_len + + (params->pmkid ? WLAN_PMKID_LEN : 0), gfp); if (!ev) { - cfg80211_put_bss(wdev->wiphy, bss); + cfg80211_put_bss(wdev->wiphy, params->bss); return; } ev->type = EVENT_CONNECT_RESULT; - if (bssid) - memcpy(ev->cr.bssid, bssid, ETH_ALEN); - if (req_ie_len) { - ev->cr.req_ie = ((u8 *)ev) + sizeof(*ev); - ev->cr.req_ie_len = req_ie_len; - memcpy((void *)ev->cr.req_ie, req_ie, req_ie_len); + next = ((u8 *)ev) + sizeof(*ev); + if (params->bssid) { + ev->cr.bssid = next; + memcpy((void *)ev->cr.bssid, params->bssid, ETH_ALEN); + next += ETH_ALEN; } - if (resp_ie_len) { - ev->cr.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len; - ev->cr.resp_ie_len = resp_ie_len; - memcpy((void *)ev->cr.resp_ie, resp_ie, resp_ie_len); + if (params->req_ie_len) { + ev->cr.req_ie = next; + ev->cr.req_ie_len = params->req_ie_len; + memcpy((void *)ev->cr.req_ie, params->req_ie, + params->req_ie_len); + next += params->req_ie_len; } - if (bss) - cfg80211_hold_bss(bss_from_pub(bss)); - ev->cr.bss = bss; - ev->cr.status = status; - ev->cr.timeout_reason = timeout_reason; + if (params->resp_ie_len) { + ev->cr.resp_ie = next; + ev->cr.resp_ie_len = params->resp_ie_len; + memcpy((void *)ev->cr.resp_ie, params->resp_ie, + params->resp_ie_len); + next += params->resp_ie_len; + } + if (params->fils_kek_len) { + ev->cr.fils_kek = next; + ev->cr.fils_kek_len = params->fils_kek_len; + memcpy((void *)ev->cr.fils_kek, params->fils_kek, + params->fils_kek_len); + next += params->fils_kek_len; + } + if (params->pmk_len) { + ev->cr.pmk = next; + ev->cr.pmk_len = params->pmk_len; + memcpy((void *)ev->cr.pmk, params->pmk, params->pmk_len); + next += params->pmk_len; + } + if (params->pmkid) { + ev->cr.pmkid = next; + memcpy((void *)ev->cr.pmkid, params->pmkid, WLAN_PMKID_LEN); + next += WLAN_PMKID_LEN; + } + ev->cr.update_erp_next_seq_num = params->update_erp_next_seq_num; + if (params->update_erp_next_seq_num) + ev->cr.fils_erp_next_seq_num = params->fils_erp_next_seq_num; + if (params->bss) + cfg80211_hold_bss(bss_from_pub(params->bss)); + ev->cr.bss = params->bss; + ev->cr.status = params->status; + ev->cr.timeout_reason = params->timeout_reason; spin_lock_irqsave(&wdev->event_lock, flags); list_add_tail(&ev->list, &wdev->event_list); spin_unlock_irqrestore(&wdev->event_lock, flags); queue_work(cfg80211_wq, &rdev->event_work); } -EXPORT_SYMBOL(cfg80211_connect_bss); +EXPORT_SYMBOL(cfg80211_connect_done); /* Consumes bss object one way or another */ void __cfg80211_roamed(struct wireless_dev *wdev, diff --git a/net/wireless/util.c b/net/wireless/util.c index 305370cfd1e0..afdbc1200a1b 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -858,7 +858,6 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev) { struct cfg80211_event *ev; unsigned long flags; - const u8 *bssid = NULL; spin_lock_irqsave(&wdev->event_lock, flags); while (!list_empty(&wdev->event_list)) { @@ -870,15 +869,10 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev) wdev_lock(wdev); switch (ev->type) { case EVENT_CONNECT_RESULT: - if (!is_zero_ether_addr(ev->cr.bssid)) - bssid = ev->cr.bssid; __cfg80211_connect_result( - wdev->netdev, bssid, - ev->cr.req_ie, ev->cr.req_ie_len, - ev->cr.resp_ie, ev->cr.resp_ie_len, - ev->cr.status, - ev->cr.status == WLAN_STATUS_SUCCESS, - ev->cr.bss, ev->cr.timeout_reason); + wdev->netdev, + &ev->cr, + ev->cr.status == WLAN_STATUS_SUCCESS); break; case EVENT_ROAMED: __cfg80211_roamed(wdev, ev->rm.bss, ev->rm.req_ie, diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index cf0193b74ae3..d5b4ac7bf0d8 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -3027,6 +3027,11 @@ static int __net_init xfrm_net_init(struct net *net) { int rv; + /* Initialize the per-net locks here */ + spin_lock_init(&net->xfrm.xfrm_state_lock); + rwlock_init(&net->xfrm.xfrm_policy_lock); + mutex_init(&net->xfrm.xfrm_cfg_mutex); + rv = xfrm_statistics_init(net); if (rv < 0) goto out_statistics; @@ -3043,11 +3048,6 @@ static int __net_init xfrm_net_init(struct net *net) if (rv < 0) goto out; - /* Initialize the per-net locks here */ - spin_lock_init(&net->xfrm.xfrm_state_lock); - rwlock_init(&net->xfrm.xfrm_policy_lock); - mutex_init(&net->xfrm.xfrm_cfg_mutex); - return 0; out: diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 805681a7d356..7a5a64e70b4d 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -412,7 +412,14 @@ static inline int xfrm_replay_verify_len(struct xfrm_replay_state_esn *replay_es up = nla_data(rp); ulen = xfrm_replay_state_esn_len(up); - if (nla_len(rp) < ulen || xfrm_replay_state_esn_len(replay_esn) != ulen) + /* Check the overall length and the internal bitmap length to avoid + * potential overflow. */ + if (nla_len(rp) < ulen || + xfrm_replay_state_esn_len(replay_esn) != ulen || + replay_esn->bmp_len != up->bmp_len) + return -EINVAL; + + if (up->replay_window > up->bmp_len * sizeof(__u32) * 8) return -EINVAL; return 0; diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 1dcd4ed37103..c72bcb33496a 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -2500,6 +2500,7 @@ sub process { # Check for git id commit length and improperly formed commit descriptions if ($in_commit_log && !$commit_log_possible_stack_dump && + $line !~ /^This reverts commit [0-9a-f]{7,40}/ && ($line =~ /\bcommit\s+[0-9a-f]{5,}\b/i || ($line =~ /\b[0-9a-f]{12,40}\b/i && $line !~ /[\<\[][0-9a-f]{12,40}[\>\]]/i && diff --git a/security/keys/gc.c b/security/keys/gc.c index addf060399e0..9cb4fe4478a1 100644 --- a/security/keys/gc.c +++ b/security/keys/gc.c @@ -46,7 +46,7 @@ static unsigned long key_gc_flags; * immediately unlinked. */ struct key_type key_type_dead = { - .name = "dead", + .name = ".dead", }; /* diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 1c3872aeed14..442e350c209d 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -271,7 +271,8 @@ error: * Create and join an anonymous session keyring or join a named session * keyring, creating it if necessary. A named session keyring must have Search * permission for it to be joined. Session keyrings without this permit will - * be skipped over. + * be skipped over. It is not permitted for userspace to create or join + * keyrings whose name begin with a dot. * * If successful, the ID of the joined session keyring will be returned. */ @@ -288,12 +289,16 @@ long keyctl_join_session_keyring(const char __user *_name) ret = PTR_ERR(name); goto error; } + + ret = -EPERM; + if (name[0] == '.') + goto error_name; } /* join the session */ ret = join_session_keyring(name); +error_name: kfree(name); - error: return ret; } @@ -1223,8 +1228,8 @@ error: * Read or set the default keyring in which request_key() will cache keys and * return the old setting. * - * If a process keyring is specified then this will be created if it doesn't - * yet exist. The old setting will be returned if successful. + * If a thread or process keyring is specified then it will be created if it + * doesn't yet exist. The old setting will be returned if successful. */ long keyctl_set_reqkey_keyring(int reqkey_defl) { @@ -1249,11 +1254,8 @@ long keyctl_set_reqkey_keyring(int reqkey_defl) case KEY_REQKEY_DEFL_PROCESS_KEYRING: ret = install_process_keyring_to_cred(new); - if (ret < 0) { - if (ret != -EEXIST) - goto error; - ret = 0; - } + if (ret < 0) + goto error; goto set; case KEY_REQKEY_DEFL_DEFAULT: diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index e6d50172872f..4ed909142956 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -125,13 +125,18 @@ error: } /* - * Install a fresh thread keyring directly to new credentials. This keyring is - * allowed to overrun the quota. + * Install a thread keyring to the given credentials struct if it didn't have + * one already. This is allowed to overrun the quota. + * + * Return: 0 if a thread keyring is now present; -errno on failure. */ int install_thread_keyring_to_cred(struct cred *new) { struct key *keyring; + if (new->thread_keyring) + return 0; + keyring = keyring_alloc("_tid", new->uid, new->gid, new, KEY_POS_ALL | KEY_USR_VIEW, KEY_ALLOC_QUOTA_OVERRUN, NULL); @@ -143,7 +148,9 @@ int install_thread_keyring_to_cred(struct cred *new) } /* - * Install a fresh thread keyring, discarding the old one. + * Install a thread keyring to the current task if it didn't have one already. + * + * Return: 0 if a thread keyring is now present; -errno on failure. */ static int install_thread_keyring(void) { @@ -154,8 +161,6 @@ static int install_thread_keyring(void) if (!new) return -ENOMEM; - BUG_ON(new->thread_keyring); - ret = install_thread_keyring_to_cred(new); if (ret < 0) { abort_creds(new); @@ -166,17 +171,17 @@ static int install_thread_keyring(void) } /* - * Install a process keyring directly to a credentials struct. + * Install a process keyring to the given credentials struct if it didn't have + * one already. This is allowed to overrun the quota. * - * Returns -EEXIST if there was already a process keyring, 0 if one installed, - * and other value on any other error + * Return: 0 if a process keyring is now present; -errno on failure. */ int install_process_keyring_to_cred(struct cred *new) { struct key *keyring; if (new->process_keyring) - return -EEXIST; + return 0; keyring = keyring_alloc("_pid", new->uid, new->gid, new, KEY_POS_ALL | KEY_USR_VIEW, @@ -189,11 +194,9 @@ int install_process_keyring_to_cred(struct cred *new) } /* - * Make sure a process keyring is installed for the current process. The - * existing process keyring is not replaced. + * Install a process keyring to the current task if it didn't have one already. * - * Returns 0 if there is a process keyring by the end of this function, some - * error otherwise. + * Return: 0 if a process keyring is now present; -errno on failure. */ static int install_process_keyring(void) { @@ -207,14 +210,18 @@ static int install_process_keyring(void) ret = install_process_keyring_to_cred(new); if (ret < 0) { abort_creds(new); - return ret != -EEXIST ? ret : 0; + return ret; } return commit_creds(new); } /* - * Install a session keyring directly to a credentials struct. + * Install the given keyring as the session keyring of the given credentials + * struct, replacing the existing one if any. If the given keyring is NULL, + * then install a new anonymous session keyring. + * + * Return: 0 on success; -errno on failure. */ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring) { @@ -249,8 +256,11 @@ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring) } /* - * Install a session keyring, discarding the old one. If a keyring is not - * supplied, an empty one is invented. + * Install the given keyring as the session keyring of the current task, + * replacing the existing one if any. If the given keyring is NULL, then + * install a new anonymous session keyring. + * + * Return: 0 on success; -errno on failure. */ static int install_session_keyring(struct key *keyring) { diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 58e79e02f217..c67f9c212dd1 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -1921,6 +1921,7 @@ static int snd_seq_ioctl_set_client_pool(struct snd_seq_client *client, info.output_pool != client->pool->size)) { if (snd_seq_write_pool_allocated(client)) { /* remove all existing cells */ + snd_seq_pool_mark_closing(client->pool); snd_seq_queue_client_leave_cells(client->number); snd_seq_pool_done(client->pool); } diff --git a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c index 86240d02b530..3490d21ab9e7 100644 --- a/sound/core/seq/seq_fifo.c +++ b/sound/core/seq/seq_fifo.c @@ -70,6 +70,9 @@ void snd_seq_fifo_delete(struct snd_seq_fifo **fifo) return; *fifo = NULL; + if (f->pool) + snd_seq_pool_mark_closing(f->pool); + snd_seq_fifo_clear(f); /* wake up clients if any */ @@ -262,6 +265,10 @@ int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize) /* NOTE: overflow flag is not cleared */ spin_unlock_irqrestore(&f->lock, flags); + /* close the old pool and wait until all users are gone */ + snd_seq_pool_mark_closing(oldpool); + snd_use_lock_sync(&f->use_lock); + /* release cells in old pool */ for (cell = oldhead; cell; cell = next) { next = cell->next; diff --git a/sound/core/seq/seq_lock.c b/sound/core/seq/seq_lock.c index 3b693e924db7..12ba83367b1b 100644 --- a/sound/core/seq/seq_lock.c +++ b/sound/core/seq/seq_lock.c @@ -28,19 +28,16 @@ /* wait until all locks are released */ void snd_use_lock_sync_helper(snd_use_lock_t *lockp, const char *file, int line) { - int max_count = 5 * HZ; + int warn_count = 5 * HZ; if (atomic_read(lockp) < 0) { pr_warn("ALSA: seq_lock: lock trouble [counter = %d] in %s:%d\n", atomic_read(lockp), file, line); return; } while (atomic_read(lockp) > 0) { - if (max_count == 0) { - pr_warn("ALSA: seq_lock: timeout [%d left] in %s:%d\n", atomic_read(lockp), file, line); - break; - } + if (warn_count-- == 0) + pr_warn("ALSA: seq_lock: waiting [%d left] in %s:%d\n", atomic_read(lockp), file, line); schedule_timeout_uninterruptible(1); - max_count--; } } diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c index dfa5156f3585..5847c4475bf3 100644 --- a/sound/core/seq/seq_memory.c +++ b/sound/core/seq/seq_memory.c @@ -414,6 +414,18 @@ int snd_seq_pool_init(struct snd_seq_pool *pool) return 0; } +/* refuse the further insertion to the pool */ +void snd_seq_pool_mark_closing(struct snd_seq_pool *pool) +{ + unsigned long flags; + + if (snd_BUG_ON(!pool)) + return; + spin_lock_irqsave(&pool->lock, flags); + pool->closing = 1; + spin_unlock_irqrestore(&pool->lock, flags); +} + /* remove events */ int snd_seq_pool_done(struct snd_seq_pool *pool) { @@ -424,10 +436,6 @@ int snd_seq_pool_done(struct snd_seq_pool *pool) return -EINVAL; /* wait for closing all threads */ - spin_lock_irqsave(&pool->lock, flags); - pool->closing = 1; - spin_unlock_irqrestore(&pool->lock, flags); - if (waitqueue_active(&pool->output_sleep)) wake_up(&pool->output_sleep); @@ -484,6 +492,7 @@ int snd_seq_pool_delete(struct snd_seq_pool **ppool) *ppool = NULL; if (pool == NULL) return 0; + snd_seq_pool_mark_closing(pool); snd_seq_pool_done(pool); kfree(pool); return 0; diff --git a/sound/core/seq/seq_memory.h b/sound/core/seq/seq_memory.h index 4a2ec779b8a7..32f959c17786 100644 --- a/sound/core/seq/seq_memory.h +++ b/sound/core/seq/seq_memory.h @@ -84,6 +84,7 @@ static inline int snd_seq_total_cells(struct snd_seq_pool *pool) int snd_seq_pool_init(struct snd_seq_pool *pool); /* done pool - free events */ +void snd_seq_pool_mark_closing(struct snd_seq_pool *pool); int snd_seq_pool_done(struct snd_seq_pool *pool); /* create pool */ diff --git a/sound/firewire/lib.h b/sound/firewire/lib.h index f3f6f84c48d6..bb5f8cdea3e2 100644 --- a/sound/firewire/lib.h +++ b/sound/firewire/lib.h @@ -42,7 +42,7 @@ struct snd_fw_async_midi_port { struct snd_rawmidi_substream *substream; snd_fw_async_midi_port_fill fill; - unsigned int consume_bytes; + int consume_bytes; }; int snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port, diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index ab4cdab5cfa5..79edd88d5cd0 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -1905,7 +1905,7 @@ static int hw_card_start(struct hw *hw) return err; /* Set DMA transfer mask */ - if (dma_set_mask(&pci->dev, DMA_BIT_MASK(dma_bits))) { + if (!dma_set_mask(&pci->dev, DMA_BIT_MASK(dma_bits))) { dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(dma_bits)); } else { dma_set_mask(&pci->dev, DMA_BIT_MASK(32)); diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index cf0785ddbd14..46a34039ecdc 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4831,6 +4831,7 @@ enum { ALC292_FIXUP_DISABLE_AAMIX, ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK, ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE, ALC275_FIXUP_DELL_XPS, ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE, ALC293_FIXUP_LENOVO_SPK_NOISE, @@ -5429,6 +5430,15 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_HEADSET_MODE }, + [ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE + }, [ALC275_FIXUP_DELL_XPS] = { .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { @@ -5501,7 +5511,7 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc298_fixup_speaker_volume, .chained = true, - .chain_id = ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, + .chain_id = ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE, }, [ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER] = { .type = HDA_FIXUP_PINS, @@ -6040,6 +6050,8 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { ALC295_STANDARD_PINS, {0x17, 0x21014040}, {0x18, 0x21a19050}), + SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC295_STANDARD_PINS), SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, ALC298_STANDARD_PINS, {0x17, 0x90170110}), diff --git a/sound/soc/atmel/atmel-classd.c b/sound/soc/atmel/atmel-classd.c index 8276675730ef..78a985629607 100644 --- a/sound/soc/atmel/atmel-classd.c +++ b/sound/soc/atmel/atmel-classd.c @@ -343,7 +343,7 @@ static int atmel_classd_codec_dai_digital_mute(struct snd_soc_dai *codec_dai, } #define CLASSD_ACLK_RATE_11M2896_MPY_8 (112896 * 100 * 8) -#define CLASSD_ACLK_RATE_12M288_MPY_8 (12228 * 1000 * 8) +#define CLASSD_ACLK_RATE_12M288_MPY_8 (12288 * 1000 * 8) static struct { int rate; diff --git a/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c b/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c index c6074570bb50..6c702c8fc35f 100644 --- a/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c +++ b/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c @@ -30,6 +30,7 @@ #include "msm-digital-cdc.h" #include "msm-cdc-common.h" #include "../../msm/sdm660-common.h" +#include "../../../../drivers/base/regmap/internal.h" #define DRV_NAME "msm_digital_codec" #define MCLK_RATE_9P6MHZ 9600000 @@ -71,6 +72,8 @@ static int msm_digcdc_clock_control(bool flag) { int ret = -EINVAL; struct msm_asoc_mach_data *pdata = NULL; + struct msm_dig_priv *msm_dig_cdc = + snd_soc_codec_get_drvdata(registered_digcodec); pdata = snd_soc_card_get_drvdata(registered_digcodec->component.card); @@ -84,6 +87,12 @@ static int msm_digcdc_clock_control(bool flag) if (ret < 0) { pr_err("%s:failed to enable the MCLK\n", __func__); + /* + * Avoid access to lpass register + * as clock enable failed during SSR. + */ + if (ret == -ENODEV) + msm_dig_cdc->regmap->cache_only = true; mutex_unlock(&pdata->cdc_int_mclk0_mutex); return ret; } diff --git a/sound/soc/codecs/sdm660_cdc/sdm660-regmap.c b/sound/soc/codecs/sdm660_cdc/sdm660-regmap.c index fff1fdc5b421..c9babac8ffaf 100644 --- a/sound/soc/codecs/sdm660_cdc/sdm660-regmap.c +++ b/sound/soc/codecs/sdm660_cdc/sdm660-regmap.c @@ -452,8 +452,23 @@ bool msm89xx_cdc_core_readable_reg(struct device *dev, unsigned int reg) bool msm89xx_cdc_core_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { - /* cache bypass for initial version */ - default: + case MSM89XX_CDC_CORE_RX1_B1_CTL: + case MSM89XX_CDC_CORE_RX2_B1_CTL: + case MSM89XX_CDC_CORE_RX3_B1_CTL: + case MSM89XX_CDC_CORE_RX1_B6_CTL: + case MSM89XX_CDC_CORE_RX2_B6_CTL: + case MSM89XX_CDC_CORE_RX3_B6_CTL: + case MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG: + case MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG: + case MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG: + case MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG: + case MSM89XX_CDC_CORE_TX5_VOL_CTL_CFG: + case MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL: + case MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL: + case MSM89XX_CDC_CORE_CLK_MCLK_CTL: + case MSM89XX_CDC_CORE_CLK_PDM_CTL: return true; + default: + return false; } } diff --git a/sound/soc/msm/qdsp6v2/Makefile b/sound/soc/msm/qdsp6v2/Makefile index d4db55fe06a2..24672f4da788 100644 --- a/sound/soc/msm/qdsp6v2/Makefile +++ b/sound/soc/msm/qdsp6v2/Makefile @@ -9,7 +9,7 @@ snd-soc-qdsp6v2-objs += msm-dai-q6-v2.o msm-pcm-q6-v2.o msm-pcm-routing-v2.o \ obj-$(CONFIG_SND_SOC_QDSP6V2) += snd-soc-qdsp6v2.o msm-pcm-dtmf-v2.o \ msm-dai-stub-v2.o obj-$(CONFIG_SND_HWDEP) += msm-pcm-routing-devdep.o -obj-$(CONFIG_DTS_EAGLE) += msm-dts-eagle.o +obj-$(CONFIG_DOLBY_DAP) += msm-dolby-dap-config.o obj-$(CONFIG_DOLBY_DS2) += msm-ds2-dap-config.o obj-$(CONFIG_DOLBY_LICENSE) += msm-ds2-dap-config.o obj-$(CONFIG_DTS_SRS_TM) += msm-dts-srs-tm-config.o diff --git a/sound/soc/msm/qdsp6v2/audio_cal_utils.c b/sound/soc/msm/qdsp6v2/audio_cal_utils.c index b54cde4ef0c3..4f2531a3da63 100644 --- a/sound/soc/msm/qdsp6v2/audio_cal_utils.c +++ b/sound/soc/msm/qdsp6v2/audio_cal_utils.c @@ -158,9 +158,6 @@ size_t get_cal_info_size(int32_t cal_type) case ULP_LSM_CAL_TYPE: size = sizeof(struct audio_cal_info_lsm); break; - case DTS_EAGLE_CAL_TYPE: - size = 0; - break; case AUDIO_CORE_METAINFO_CAL_TYPE: size = sizeof(struct audio_cal_info_metainfo); break; @@ -307,9 +304,6 @@ size_t get_user_cal_type_size(int32_t cal_type) case ULP_LSM_CAL_TYPE: size = sizeof(struct audio_cal_type_lsm); break; - case DTS_EAGLE_CAL_TYPE: - size = 0; - break; case AUDIO_CORE_METAINFO_CAL_TYPE: size = sizeof(struct audio_cal_type_metainfo); break; @@ -649,7 +643,9 @@ done: return cal_block; err: kfree(cal_block->cal_info); + cal_block->cal_info = NULL; kfree(cal_block->client_info); + cal_block->client_info = NULL; kfree(cal_block); cal_block = NULL; return cal_block; 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 1c08842c7840..e312a879b86a 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-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, 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 @@ -15,7 +15,6 @@ #include <sound/q6asm-v2.h> #include <sound/compress_params.h> #include <sound/msm-audio-effects-q6-v2.h> -#include <sound/msm-dts-eagle.h> #include <sound/devdep_params.h> #define MAX_ENABLE_CMD_SIZE 32 @@ -49,26 +48,6 @@ bool msm_audio_effects_is_effmodule_supp_in_top(int effect_module, case EQ_MODULE: switch (topology) { case ASM_STREAM_POSTPROC_TOPO_ID_SA_PLUS: - case ASM_STREAM_POSTPROC_TOPO_ID_HPX_PLUS: - case ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER: - return true; - default: - return false; - } - case DTS_EAGLE_MODULE: - switch (topology) { - case ASM_STREAM_POSTPROC_TOPO_ID_DTS_HPX: - case ASM_STREAM_POSTPROC_TOPO_ID_HPX_PLUS: - case ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER: - return true; - default: - return false; - } - case SOFT_VOLUME2_MODULE: - case DTS_EAGLE_MODULE_ENABLE: - switch (topology) { - case ASM_STREAM_POSTPROC_TOPO_ID_HPX_PLUS: - case ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER: return true; default: return false; @@ -276,7 +255,7 @@ int msm_audio_effects_virtualizer_handler(struct audio_client *ac, break; } } - if (params_length && !msm_dts_eagle_is_hpx_on() && (rc == 0)) + if (params_length && (rc == 0)) q6asm_send_audio_effects_params(ac, params, params_length); else @@ -747,7 +726,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, break; } } - if (params_length && !msm_dts_eagle_is_hpx_on() && (rc == 0)) + if (params_length && (rc == 0)) q6asm_send_audio_effects_params(ac, params, params_length); else @@ -883,7 +862,7 @@ int msm_audio_effects_bass_boost_handler(struct audio_client *ac, break; } } - if (params_length && !msm_dts_eagle_is_hpx_on() && (rc == 0)) + if (params_length && (rc == 0)) q6asm_send_audio_effects_params(ac, params, params_length); else @@ -1223,7 +1202,7 @@ int msm_audio_effects_popless_eq_handler(struct audio_client *ac, break; } } - if (params_length && !msm_dts_eagle_is_hpx_on() && (rc == 0)) + if (params_length && (rc == 0)) q6asm_send_audio_effects_params(ac, params, params_length); else diff --git a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c index 8fc09130ab7d..426b4d0b0a35 100644 --- a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c @@ -42,8 +42,6 @@ #include <sound/compress_offload.h> #include <sound/compress_driver.h> #include <sound/msm-audio-effects-q6-v2.h> -#include <sound/msm-dts-eagle.h> - #include "msm-pcm-routing-v2.h" #include "msm-qti-pp-config.h" @@ -80,15 +78,6 @@ const DECLARE_TLV_DB_LINEAR(msm_compr_vol_gain, 0, #define MAX_NUMBER_OF_STREAMS 2 -/* - * Max size for getting DTS EAGLE Param through kcontrol - * Safe for both 32 and 64 bit platforms - * 64 = size of kcontrol value array on 64 bit platform - * 4 = size of parameters Eagle expects before cast to 64 bits - * 40 = size of dts_eagle_param_desc + module_id cast to 64 bits - */ -#define DTS_EAGLE_MAX_PARAM_SIZE_FOR_ALSA ((64 * 4) - 40) - struct msm_compr_gapless_state { bool set_next_stream_id; int32_t stream_opened[MAX_NUMBER_OF_STREAMS]; @@ -313,6 +302,39 @@ exit: return ret; } +static int msm_compr_enable_adjust_session_clock(struct audio_client *ac, + bool enable) +{ + int ret; + + pr_debug("%s, enable adjust_session %d\n", __func__, enable); + + ret = q6asm_send_mtmx_strtr_enable_adjust_session_clock(ac, enable); + if (ret) + pr_err("%s, adjust session clock can't be set error %d\n", + __func__, ret); + + return ret; +} + +static int msm_compr_adjust_session_clock(struct audio_client *ac, + uint32_t adjust_session_lsw, uint32_t adjust_session_msw) +{ + int ret; + + pr_debug("%s, adjust_session_time_msw 0x%x adjust_session_time_lsw 0x%x\n", + __func__, adjust_session_msw, adjust_session_lsw); + + ret = q6asm_adjust_session_clock(ac, + adjust_session_lsw, + adjust_session_msw); + if (ret) + pr_err("%s, adjust session clock can't be set error %d\n", + __func__, ret); + + return ret; +} + static int msm_compr_set_volume(struct snd_compr_stream *cstream, uint32_t volume_l, uint32_t volume_r) { @@ -378,11 +400,6 @@ static int msm_compr_set_volume(struct snd_compr_stream *cstream, if (rc < 0) pr_err("%s: Send vol gain command failed rc=%d\n", __func__, rc); - else - if (msm_dts_eagle_set_stream_gain(prtd->audio_client, - volume_l, volume_r)) - pr_debug("%s: DTS_EAGLE send stream gain failed\n", - __func__); return rc; } @@ -732,8 +749,7 @@ static void compr_event_handler(uint32_t opcode, return; } - ret = msm_adsp_inform_mixer_ctl(rtd, DSP_STREAM_CALLBACK, - payload); + ret = msm_adsp_inform_mixer_ctl(rtd, payload); if (ret) { pr_err("%s: failed to inform mixer ctrl. err = %d\n", __func__, ret); @@ -1215,26 +1231,6 @@ static int msm_compr_init_pp_params(struct snd_compr_stream *cstream, }; switch (ac->topology) { - case ASM_STREAM_POSTPROC_TOPO_ID_HPX_PLUS: /* HPX + SA+ topology */ - - ret = q6asm_set_softvolume_v2(ac, &softvol, - SOFT_VOLUME_INSTANCE_1); - if (ret < 0) - pr_err("%s: Send SoftVolume Param failed ret=%d\n", - __func__, ret); - - ret = q6asm_set_softvolume_v2(ac, &softvol, - SOFT_VOLUME_INSTANCE_2); - if (ret < 0) - pr_err("%s: Send SoftVolume2 Param failed ret=%d\n", - __func__, ret); - /* - * HPX module init is trigerred from HAL using ioctl - * DTS_EAGLE_MODULE_ENABLE when stream starts - */ - break; - case ASM_STREAM_POSTPROC_TOPO_ID_DTS_HPX: /* HPX topology */ - break; default: ret = q6asm_set_softvolume_v2(ac, &softvol, SOFT_VOLUME_INSTANCE_1); @@ -1586,6 +1582,7 @@ static int msm_compr_playback_open(struct snd_compr_stream *cstream) pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session); prtd->audio_client->perf_mode = false; prtd->session_id = prtd->audio_client->session; + msm_adsp_init_mixer_ctl_pp_event_queue(rtd); return 0; } @@ -1741,7 +1738,7 @@ static int msm_compr_playback_free(struct snd_compr_stream *cstream) q6asm_audio_client_buf_free_contiguous(dir, ac); q6asm_audio_client_free(ac); - + msm_adsp_clean_mixer_ctl_pp_event_queue(soc_prtd); kfree(pdata->audio_effects[soc_prtd->dai_link->be_id]); pdata->audio_effects[soc_prtd->dai_link->be_id] = NULL; kfree(pdata->dec_params[soc_prtd->dai_link->be_id]); @@ -2911,6 +2908,14 @@ static int msm_compr_set_metadata(struct snd_compr_stream *cstream, } else if (metadata->key == SNDRV_COMPRESS_START_DELAY) { prtd->start_delay_lsw = metadata->value[0]; prtd->start_delay_msw = metadata->value[1]; + } else if (metadata->key == + SNDRV_COMPRESS_ENABLE_ADJUST_SESSION_CLOCK) { + return msm_compr_enable_adjust_session_clock(ac, + metadata->value[0]); + } else if (metadata->key == SNDRV_COMPRESS_ADJUST_SESSION_CLOCK) { + return msm_compr_adjust_session_clock(ac, + metadata->value[0], + metadata->value[1]); } return 0; @@ -3133,23 +3138,6 @@ static int msm_compr_audio_effects_config_put(struct snd_kcontrol *kcontrol, &(audio_effects->equalizer), values); break; - case DTS_EAGLE_MODULE: - pr_debug("%s: DTS_EAGLE_MODULE\n", __func__); - if (!msm_audio_effects_is_effmodule_supp_in_top(effects_module, - prtd->audio_client->topology)) - return 0; - msm_dts_eagle_handle_asm(NULL, (void *)values, true, - false, prtd->audio_client, NULL); - break; - case DTS_EAGLE_MODULE_ENABLE: - pr_debug("%s: DTS_EAGLE_MODULE_ENABLE\n", __func__); - if (msm_audio_effects_is_effmodule_supp_in_top(effects_module, - prtd->audio_client->topology)) - msm_dts_eagle_enable_asm(prtd->audio_client, - (bool)values[0], - AUDPROC_MODULE_ID_DTS_HPX_PREMIX); - - break; case SOFT_VOLUME_MODULE: pr_debug("%s: SOFT_VOLUME_MODULE\n", __func__); break; @@ -3178,7 +3166,6 @@ static int msm_compr_audio_effects_config_get(struct snd_kcontrol *kcontrol, struct msm_compr_audio_effects *audio_effects = NULL; struct snd_compr_stream *cstream = NULL; struct msm_compr_audio *prtd = NULL; - long *values = &(ucontrol->value.integer.value[0]); pr_debug("%s\n", __func__); if (fe_id >= MSM_FRONTEND_DAI_MAX) { @@ -3198,28 +3185,6 @@ static int msm_compr_audio_effects_config_get(struct snd_kcontrol *kcontrol, return -EINVAL; } - switch (audio_effects->query.mod_id) { - case DTS_EAGLE_MODULE: - pr_debug("%s: DTS_EAGLE_MODULE handling queued get\n", - __func__); - values[0] = (long)audio_effects->query.mod_id; - values[1] = (long)audio_effects->query.parm_id; - values[2] = (long)audio_effects->query.size; - values[3] = (long)audio_effects->query.offset; - values[4] = (long)audio_effects->query.device; - if (values[2] > DTS_EAGLE_MAX_PARAM_SIZE_FOR_ALSA) { - pr_err("%s: DTS_EAGLE_MODULE parameter's requested size (%li) too large (max size is %i)\n", - __func__, values[2], - DTS_EAGLE_MAX_PARAM_SIZE_FOR_ALSA); - return -EINVAL; - } - msm_dts_eagle_handle_asm(NULL, (void *)&values[1], - true, true, prtd->audio_client, NULL); - break; - default: - pr_err("%s: Invalid effects config module\n", __func__); - return -EINVAL; - } return 0; } @@ -4013,7 +3978,6 @@ static int msm_compr_add_audio_adsp_stream_callback_control( .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .info = msm_adsp_stream_callback_info, .get = msm_adsp_stream_callback_get, - .put = msm_adsp_stream_callback_put, .private_value = 0, } }; @@ -4054,6 +4018,7 @@ static int msm_compr_add_audio_adsp_stream_callback_control( } kctl->private_data = NULL; + free_mixer_str: kfree(mixer_str); done: diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c index 26b7f3f26b26..45868f508a60 100644 --- a/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c @@ -174,7 +174,7 @@ static const struct snd_kcontrol_new hdmi_config_controls[] = { { .access = SNDRV_CTL_ELEM_ACCESS_READ, .iface = SNDRV_CTL_ELEM_IFACE_PCM, - .name = "HDMI RX Drift", + .name = "HDMI Drift", .info = msm_dai_q6_ext_disp_drift_info, .get = msm_dai_q6_ext_disp_drift_get, }, @@ -191,7 +191,7 @@ static const struct snd_kcontrol_new display_port_config_controls[] = { { .access = SNDRV_CTL_ELEM_ACCESS_READ, .iface = SNDRV_CTL_ELEM_IFACE_PCM, - .name = "DISPLAY Port RX Drift", + .name = "DISPLAY_PORT Drift", .info = msm_dai_q6_ext_disp_drift_info, .get = msm_dai_q6_ext_disp_drift_get, }, diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c index 201ca652c10b..9ed61288a3e4 100644 --- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c @@ -2321,6 +2321,44 @@ static const struct snd_kcontrol_new afe_enc_config_controls[] = { msm_dai_q6_afe_input_bit_format_put), }; +static int msm_dai_q6_slim_rx_drift_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = sizeof(struct afe_param_id_dev_timing_stats); + + return 0; +} + +static int msm_dai_q6_slim_rx_drift_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + struct afe_param_id_dev_timing_stats timing_stats; + struct snd_soc_dai *dai = kcontrol->private_data; + struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev); + + if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + pr_err("%s: afe port not started. dai_data->status_mask = %ld\n", + __func__, *dai_data->status_mask); + goto done; + } + + memset(&timing_stats, 0, sizeof(struct afe_param_id_dev_timing_stats)); + ret = afe_get_av_dev_drift(&timing_stats, dai->id); + if (ret) { + pr_err("%s: Error getting AFE Drift for port %d, err=%d\n", + __func__, dai->id, ret); + + goto done; + } + + memcpy(ucontrol->value.bytes.data, (void *)&timing_stats, + sizeof(struct afe_param_id_dev_timing_stats)); +done: + return ret; +} + static const char * const afe_cal_mode_text[] = { "CAL_MODE_DEFAULT", "CAL_MODE_NONE" }; @@ -2373,6 +2411,29 @@ static const struct snd_kcontrol_new usb_audio_cfg_controls[] = { msm_dai_q6_usb_audio_endian_cfg_put), }; +static const struct snd_kcontrol_new avd_drift_config_controls[] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "SLIMBUS_0_RX DRIFT", + .info = msm_dai_q6_slim_rx_drift_info, + .get = msm_dai_q6_slim_rx_drift_get, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "SLIMBUS_6_RX DRIFT", + .info = msm_dai_q6_slim_rx_drift_info, + .get = msm_dai_q6_slim_rx_drift_get, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "SLIMBUS_7_RX DRIFT", + .info = msm_dai_q6_slim_rx_drift_info, + .get = msm_dai_q6_slim_rx_drift_get, + }, +}; static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai) { struct msm_dai_q6_dai_data *dai_data; @@ -2422,6 +2483,9 @@ static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai) rc = snd_ctl_add(dai->component->card->snd_card, snd_ctl_new1(&afe_enc_config_controls[2], dai_data)); + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&avd_drift_config_controls[2], + dai)); break; case RT_PROXY_DAI_001_RX: rc = snd_ctl_add(dai->component->card->snd_card, @@ -2449,6 +2513,16 @@ static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai) snd_ctl_new1(&usb_audio_cfg_controls[3], dai_data)); break; + case SLIMBUS_0_RX: + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&avd_drift_config_controls[0], + dai)); + break; + case SLIMBUS_6_RX: + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&avd_drift_config_controls[1], + dai)); + break; } if (IS_ERR_VALUE(rc)) dev_err(dai->dev, "%s: err add config ctl, DAI = %s\n", diff --git a/sound/soc/msm/qdsp6v2/msm-dts-eagle.c b/sound/soc/msm/qdsp6v2/msm-dts-eagle.c deleted file mode 100644 index 15845b2e6dc9..000000000000 --- a/sound/soc/msm/qdsp6v2/msm-dts-eagle.c +++ /dev/null @@ -1,1625 +0,0 @@ -/* Copyright (c) 2014-2016, 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/init.h> -#include <linux/err.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/mutex.h> -#include <linux/slab.h> -#include <linux/msm_ion.h> -#include <linux/mm.h> -#include <linux/msm_audio_ion.h> -#include <linux/vmalloc.h> -#include <sound/core.h> -#include <sound/soc.h> -#include <sound/pcm.h> -#include <sound/q6adm-v2.h> -#include <sound/q6asm-v2.h> -#include <sound/apr_audio-v2.h> -#include <sound/q6audio-v2.h> -#include <sound/audio_effects.h> -#include <sound/hwdep.h> -#include <sound/msm-dts-eagle.h> -#include <sound/q6core.h> - -#include "msm-pcm-routing-v2.h" - -#define ION_MEM_SIZE 131072 -#define DEPC_MAX_SIZE 524288 - -#define MPST AUDPROC_MODULE_ID_DTS_HPX_POSTMIX -#define MPRE AUDPROC_MODULE_ID_DTS_HPX_PREMIX - -#define eagle_vol_dbg(fmt, ...) \ - pr_debug("DTS_EAGLE_DRIVER_VOLUME: " fmt "\n", ##__VA_ARGS__) -#define eagle_vol_err(fmt, ...) \ - pr_err("DTS_EAGLE_DRIVER_VOLUME: " fmt "\n", ##__VA_ARGS__) -#define eagle_drv_dbg(fmt, ...) \ - pr_debug("DTS_EAGLE_DRIVER: " fmt "\n", ##__VA_ARGS__) -#define eagle_drv_err(fmt, ...) \ - pr_err("DTS_EAGLE_DRIVER: " fmt "\n", ##__VA_ARGS__) -#define eagle_precache_dbg(fmt, ...) \ - pr_debug("DTS_EAGLE_DRIVER_SENDCACHE_PRE: " fmt "\n", ##__VA_ARGS__) -#define eagle_precache_err(fmt, ...) \ - pr_err("DTS_EAGLE_DRIVER_SENDCACHE_PRE: " fmt "\n", ##__VA_ARGS__) -#define eagle_postcache_dbg(fmt, ...) \ - pr_debug("DTS_EAGLE_DRIVER_SENDCACHE_POST: " fmt "\n", ##__VA_ARGS__) -#define eagle_postcache_err(fmt, ...) \ - pr_err("DTS_EAGLE_DRIVER_SENDCACHE_POST: " fmt "\n", ##__VA_ARGS__) -#define eagle_ioctl_dbg(fmt, ...) \ - pr_debug("DTS_EAGLE_DRIVER_IOCTL: " fmt "\n", ##__VA_ARGS__) -#define eagle_ioctl_err(fmt, ...) \ - pr_err("DTS_EAGLE_DRIVER_IOCTL: " fmt "\n", ##__VA_ARGS__) -#define eagle_asm_dbg(fmt, ...) \ - pr_debug("DTS_EAGLE_DRIVER_ASM: " fmt "\n", ##__VA_ARGS__) -#define eagle_asm_err(fmt, ...) \ - pr_err("DTS_EAGLE_DRIVER_ASM: " fmt "\n", ##__VA_ARGS__) -#define eagle_adm_dbg(fmt, ...) \ - pr_debug("DTS_EAGLE_DRIVER_ADM: " fmt "\n", ##__VA_ARGS__) -#define eagle_adm_err(fmt, ...) \ - pr_err("DTS_EAGLE_DRIVER_ADM: " fmt "\n", ##__VA_ARGS__) -#define eagle_enable_dbg(fmt, ...) \ - pr_debug("DTS_EAGLE_ENABLE: " fmt "\n", ##__VA_ARGS__) -#define eagle_enable_err(fmt, ...) \ - pr_err("DTS_EAGLE_ENABLE: " fmt "\n", ##__VA_ARGS__) -#define eagle_ioctl_info(fmt, ...) \ - pr_err("DTS_EAGLE_IOCTL: " fmt "\n", ##__VA_ARGS__) - -enum { - AUDIO_DEVICE_OUT_EARPIECE = 0, - AUDIO_DEVICE_OUT_SPEAKER, - AUDIO_DEVICE_OUT_WIRED_HEADSET, - AUDIO_DEVICE_OUT_WIRED_HEADPHONE, - AUDIO_DEVICE_OUT_BLUETOOTH_SCO, - AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, - AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT, - AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, - AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES, - AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER, - AUDIO_DEVICE_OUT_AUX_DIGITAL, - AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET, - AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET, - AUDIO_DEVICE_OUT_USB_ACCESSORY, - AUDIO_DEVICE_OUT_USB_DEVICE, - AUDIO_DEVICE_OUT_REMOTE_SUBMIX, - AUDIO_DEVICE_OUT_ANC_HEADSET, - AUDIO_DEVICE_OUT_ANC_HEADPHONE, - AUDIO_DEVICE_OUT_PROXY, - AUDIO_DEVICE_OUT_FM, - AUDIO_DEVICE_OUT_FM_TX, - - AUDIO_DEVICE_OUT_COUNT -}; - -#define AUDIO_DEVICE_COMBO 0x400000 /* bit 23 */ - -enum { /* cache block */ - CB_0 = 0, - CB_1, - CB_2, - CB_3, - CB_4, - CB_5, - CB_6, - CB_7, - - CB_COUNT -}; - -enum { /* cache block description */ - CBD_DEV_MASK = 0, - CBD_OFFSG, - CBD_CMD0, - CBD_SZ0, - CBD_OFFS1, - CBD_CMD1, - CBD_SZ1, - CBD_OFFS2, - CBD_CMD2, - CBD_SZ2, - CBD_OFFS3, - CBD_CMD3, - CBD_SZ3, - - CBD_COUNT, -}; - -static s32 _fx_logN(s32 x) -{ - s32 t, y = 0xa65af; - if (x < 0x00008000) { - x <<= 16; y -= 0xb1721; } - if (x < 0x00800000) { - x <<= 8; y -= 0x58b91; } - if (x < 0x08000000) { - x <<= 4; y -= 0x2c5c8; } - if (x < 0x20000000) { - x <<= 2; y -= 0x162e4; } - if (x < 0x40000000) { - x <<= 1; y -= 0x0b172; } - t = x + (x >> 1); - if ((t & 0x80000000) == 0) { - x = t; y -= 0x067cd; } - t = x + (x >> 2); - if ((t & 0x80000000) == 0) { - x = t; y -= 0x03920; } - t = x + (x >> 3); - if ((t & 0x80000000) == 0) { - x = t; y -= 0x01e27; } - t = x + (x >> 4); - if ((t & 0x80000000) == 0) { - x = t; y -= 0x00f85; } - t = x + (x >> 5); - if ((t & 0x80000000) == 0) { - x = t; y -= 0x007e1; } - t = x + (x >> 6); - if ((t & 0x80000000) == 0) { - x = t; y -= 0x003f8; } - t = x + (x >> 7); - if ((t & 0x80000000) == 0) { - x = t; y -= 0x001fe; } - x = 0x80000000 - x; - y -= x >> 15; - return y; -} - -static inline void *_getd(struct dts_eagle_param_desc *depd) -{ - return (void *)(((char *)depd) + sizeof(struct dts_eagle_param_desc)); -} - -static int _ref_cnt; -/* dts eagle parameter cache */ -static char *_depc; -static u32 _depc_size; -static s32 _c_bl[CB_COUNT][CBD_COUNT]; -static u32 _device_primary; -static u32 _device_all; -/* ION states */ -static struct ion_client *_ion_client; -static struct ion_handle *_ion_handle; -static struct param_outband _po; -static struct audio_client *_ac_NT; -static struct ion_client *_ion_client_NT; -static struct ion_handle *_ion_handle_NT; -static struct param_outband _po_NT; - -#define SEC_BLOB_MAX_CNT 10 -#define SEC_BLOB_MAX_SIZE 0x4004 /*extra 4 for size*/ -static char *_sec_blob[SEC_BLOB_MAX_CNT]; - -/* multi-copp support */ -static int _cidx[AFE_MAX_PORTS] = {-1}; - -/* volume controls */ -#define VOL_CMD_CNT_MAX 10 -static u32 _vol_cmd_cnt; -static s32 **_vol_cmds; -struct vol_cmds_d { - s32 d[4]; -}; -static struct vol_cmds_d *_vol_cmds_d; -static const s32 _log10_10_inv_x20 = 0x0008af84; - -/* hpx master control */ -static u32 _is_hpx_enabled; - -static void _volume_cmds_free(void) -{ - int i; - for (i = 0; i < _vol_cmd_cnt; i++) - kfree(_vol_cmds[i]); - _vol_cmd_cnt = 0; - kfree(_vol_cmds); - kfree(_vol_cmds_d); - _vol_cmds = NULL; - _vol_cmds_d = NULL; -} - -static s32 _volume_cmds_alloc1(s32 size) -{ - _volume_cmds_free(); - _vol_cmd_cnt = size; - _vol_cmds = kzalloc(_vol_cmd_cnt * sizeof(int *), GFP_KERNEL); - if (_vol_cmds) { - _vol_cmds_d = kzalloc(_vol_cmd_cnt * sizeof(struct vol_cmds_d), - GFP_KERNEL); - } else - _vol_cmd_cnt = 0; - if (_vol_cmds_d) - return 0; - _volume_cmds_free(); - return -ENOMEM; -} - -/* assumes size is equal or less than 0xFFF */ -static s32 _volume_cmds_alloc2(s32 idx, s32 size) -{ - kfree(_vol_cmds[idx]); - _vol_cmds[idx] = kzalloc(size, GFP_KERNEL); - if (_vol_cmds[idx]) - return 0; - _vol_cmds_d[idx].d[0] = 0; - return -ENOMEM; -} - -static void _init_cb_descs(void) -{ - int i; - for (i = 0; i < CB_COUNT; i++) { - _c_bl[i][CBD_DEV_MASK] = 0; - _c_bl[i][CBD_OFFSG] = _c_bl[i][CBD_OFFS1] = - _c_bl[i][CBD_OFFS2] = _c_bl[i][CBD_OFFS3] = - 0xFFFFFFFF; - _c_bl[i][CBD_CMD0] = _c_bl[i][CBD_SZ0] = - _c_bl[i][CBD_CMD1] = _c_bl[i][CBD_SZ1] = - _c_bl[i][CBD_CMD2] = _c_bl[i][CBD_SZ2] = - _c_bl[i][CBD_CMD3] = _c_bl[i][CBD_SZ3] = 0; - } -} - -static u32 _get_dev_mask_for_pid(int pid) -{ - switch (pid) { - case SLIMBUS_0_RX: - return (1 << AUDIO_DEVICE_OUT_EARPIECE) | - (1 << AUDIO_DEVICE_OUT_SPEAKER) | - (1 << AUDIO_DEVICE_OUT_WIRED_HEADSET) | - (1 << AUDIO_DEVICE_OUT_WIRED_HEADPHONE) | - (1 << AUDIO_DEVICE_OUT_ANC_HEADSET) | - (1 << AUDIO_DEVICE_OUT_ANC_HEADPHONE); - case INT_BT_SCO_RX: - return (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO) | - (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET) | - (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT); - case RT_PROXY_PORT_001_RX: - return (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP) | - (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES) | - (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER) | - (1 << AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET) | - (1 << AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET) | - (1 << AUDIO_DEVICE_OUT_USB_ACCESSORY) | - (1 << AUDIO_DEVICE_OUT_USB_DEVICE) | - (1 << AUDIO_DEVICE_OUT_PROXY); - case HDMI_RX: - return 1 << AUDIO_DEVICE_OUT_AUX_DIGITAL; - case INT_FM_RX: - return 1 << AUDIO_DEVICE_OUT_FM; - case INT_FM_TX: - return 1 << AUDIO_DEVICE_OUT_FM_TX; - default: - return 0; - } -} - -static int _get_pid_from_dev(u32 device) -{ - if (device & (1 << AUDIO_DEVICE_OUT_EARPIECE) || - device & (1 << AUDIO_DEVICE_OUT_SPEAKER) || - device & (1 << AUDIO_DEVICE_OUT_WIRED_HEADSET) || - device & (1 << AUDIO_DEVICE_OUT_WIRED_HEADPHONE) || - device & (1 << AUDIO_DEVICE_OUT_ANC_HEADSET) || - device & (1 << AUDIO_DEVICE_OUT_ANC_HEADPHONE)) { - return SLIMBUS_0_RX; - } else if (device & (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO) || - device & (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET) || - device & (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT)) { - return INT_BT_SCO_RX; - } else if (device & (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP) || - device & (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES) || - device & (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER) || - device & (1 << AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET) || - device & (1 << AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET) || - device & (1 << AUDIO_DEVICE_OUT_USB_ACCESSORY) || - device & (1 << AUDIO_DEVICE_OUT_USB_DEVICE) || - device & (1 << AUDIO_DEVICE_OUT_PROXY)) { - return RT_PROXY_PORT_001_RX; - } else if (device & (1 << AUDIO_DEVICE_OUT_AUX_DIGITAL)) { - return HDMI_RX; - } else if (device & (1 << AUDIO_DEVICE_OUT_FM)) { - return INT_FM_RX; - } else if (device & (1 << AUDIO_DEVICE_OUT_FM_TX)) { - return INT_FM_TX; - } - return 0; -} - -static s32 _get_cb_for_dev(int device) -{ - s32 i; - if (device & AUDIO_DEVICE_COMBO) { - for (i = 0; i < CB_COUNT; i++) { - if ((_c_bl[i][CBD_DEV_MASK] & device) == device) - return i; - } - } else { - for (i = 0; i < CB_COUNT; i++) { - if ((_c_bl[i][CBD_DEV_MASK] & device) && - !(_c_bl[i][CBD_DEV_MASK] & AUDIO_DEVICE_COMBO)) - return i; - } - } - eagle_drv_err("%s: device %i not found", __func__, device); - return -EINVAL; -} - -static int _is_port_open_and_eagle(int pid) -{ - if (msm_routing_check_backend_enabled(pid)) - return 1; - return 1; -} - -static int _isNTDevice(u32 device) -{ - if (device & - ((1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO) | - (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET) | - (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT) | - (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP) | - (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES) | - (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER) | - (1 << AUDIO_DEVICE_OUT_AUX_DIGITAL))) - return 1; - return 0; -} - -static void _reg_ion_mem(void) -{ - int rc; - rc = msm_audio_ion_alloc("DTS_EAGLE", &_ion_client, &_ion_handle, - ION_MEM_SIZE, &_po.paddr, &_po.size, &_po.kvaddr); - if (rc) - eagle_drv_err("%s: msm audio ion alloc failed with %i", - __func__, rc); -} - -static void _unreg_ion_mem(void) -{ - int rc; - rc = msm_audio_ion_free(_ion_client, _ion_handle); - if (rc) - eagle_drv_err("%s: msm audio ion alloc failed with %i", - __func__, rc); -} - -static void _reg_ion_mem_NT(void) -{ - int rc; - eagle_drv_dbg("%s: NT ion mem", __func__); - rc = msm_audio_ion_alloc("DTS_EAGLE", &_ion_client_NT, - &_ion_handle_NT, ION_MEM_SIZE, - &_po_NT.paddr, &_po_NT.size, &_po_NT.kvaddr); - if (rc) { - eagle_drv_err("%s: msm audio ion alloc failed", __func__); - return; - } - rc = q6asm_memory_map(_ac_NT, _po_NT.paddr, - IN, _po_NT.size, 1); - if (rc < 0) { - eagle_drv_err("%s: memory map failed", __func__); - msm_audio_ion_free(_ion_client_NT, _ion_handle_NT); - _ion_client_NT = NULL; - _ion_handle_NT = NULL; - } -} - -static void _unreg_ion_mem_NT(void) -{ - int rc; - rc = q6asm_memory_unmap(_ac_NT, _po_NT.paddr, IN); - if (rc < 0) - eagle_drv_err("%s: mem unmap failed", __func__); - rc = msm_audio_ion_free(_ion_client_NT, _ion_handle_NT); - if (rc < 0) - eagle_drv_err("%s: mem free failed", __func__); - - _ion_client_NT = NULL; - _ion_handle_NT = NULL; -} - -static struct audio_client *_getNTDeviceAC(void) -{ - return _ac_NT; -} - -static void _set_audioclient(struct audio_client *ac) -{ - _ac_NT = ac; - _reg_ion_mem_NT(); -} - -static void _clear_audioclient(void) -{ - _unreg_ion_mem_NT(); - _ac_NT = NULL; -} - - -static int _sendcache_pre(struct audio_client *ac) -{ - uint32_t offset, size; - int32_t cidx, cmd, err = 0; - cidx = _get_cb_for_dev(_device_primary); - if (cidx < 0) { - eagle_precache_err("%s: no cache for primary device %i found", - __func__, _device_primary); - return -EINVAL; - } - offset = _c_bl[cidx][CBD_OFFSG]; - cmd = _c_bl[cidx][CBD_CMD0]; - size = _c_bl[cidx][CBD_SZ0]; - /* check for integer overflow */ - if (offset > (UINT_MAX - size)) - err = -EINVAL; - if ((_depc_size == 0) || !_depc || (size == 0) || - cmd == 0 || ((offset + size) > _depc_size) || (err != 0)) { - eagle_precache_err("%s: primary device %i cache index %i general error - cache size = %u, cache ptr = %pK, offset = %u, size = %u, cmd = %i", - __func__, _device_primary, cidx, _depc_size, _depc, - offset, size, cmd); - return -EINVAL; - } - - if ((offset < (UINT_MAX - 124)) && ((offset + 124) < _depc_size)) - eagle_precache_dbg("%s: first 6 integers %i %i %i %i %i %i (30th %i)", - __func__, *((int *)&_depc[offset]), - *((int *)&_depc[offset+4]), - *((int *)&_depc[offset+8]), - *((int *)&_depc[offset+12]), - *((int *)&_depc[offset+16]), - *((int *)&_depc[offset+20]), - *((int *)&_depc[offset+120])); - eagle_precache_dbg("%s: sending full data block to port, with cache index = %d device mask 0x%X, param = 0x%X, offset = %u, and size = %u", - __func__, cidx, _c_bl[cidx][CBD_DEV_MASK], cmd, offset, size); - - if (q6asm_dts_eagle_set(ac, cmd, size, (void *)&_depc[offset], - NULL, MPRE)) - eagle_precache_err("%s: q6asm_dts_eagle_set failed with id = %d and size = %u", - __func__, cmd, size); - else - eagle_precache_dbg("%s: q6asm_dts_eagle_set succeeded with id = %d and size = %u", - __func__, cmd, size); - return 0; -} - -static int _sendcache_post(int port_id, int copp_idx, int topology) -{ - int cidx = -1, cmd, mask, index, err = 0; - uint32_t offset, size; - - if (port_id == -1) { - cidx = _get_cb_for_dev(_device_primary); - if (cidx < 0) { - eagle_postcache_err("%s: no cache for primary device %i found. Port id was 0x%X", - __func__, _device_primary, port_id); - return -EINVAL; - } - goto NT_MODE_GOTO; - } - - index = adm_validate_and_get_port_index(port_id); - if (index < 0) { - eagle_postcache_err("%s: Invalid port idx %d port_id %#x", - __func__, index, port_id); - return -EINVAL; - } - eagle_postcache_dbg("%s: valid port idx %d for port_id %#x set to %i", - __func__, index, port_id, copp_idx); - _cidx[index] = copp_idx; - - mask = _get_dev_mask_for_pid(port_id); - if (mask & _device_primary) { - cidx = _get_cb_for_dev(_device_primary); - if (cidx < 0) { - eagle_postcache_err("%s: no cache for primary device %i found. Port id was 0x%X", - __func__, _device_primary, port_id); - return -EINVAL; - } - } else if (mask & _device_all) { - cidx = _get_cb_for_dev(_device_all); - if (cidx < 0) { - eagle_postcache_err("%s: no cache for combo device %i found. Port id was 0x%X", - __func__, _device_all, port_id); - return -EINVAL; - } - } else { - eagle_postcache_err("%s: port id 0x%X not for primary or combo device %i", - __func__, port_id, _device_primary); - return -EINVAL; - } - -NT_MODE_GOTO: - offset = _c_bl[cidx][CBD_OFFSG] + _c_bl[cidx][CBD_OFFS2]; - cmd = _c_bl[cidx][CBD_CMD2]; - size = _c_bl[cidx][CBD_SZ2]; - - /* check for integer overflow */ - if (offset > (UINT_MAX - size)) - err = -EINVAL; - if ((_depc_size == 0) || !_depc || (err != 0) || (size == 0) || - (cmd == 0) || (offset + size) > _depc_size) { - eagle_postcache_err("%s: primary device %i cache index %i port_id 0x%X general error - cache size = %u, cache ptr = %pK, offset = %u, size = %u, cmd = %i", - __func__, _device_primary, cidx, port_id, - _depc_size, _depc, offset, size, cmd); - return -EINVAL; - } - - if ((offset < (UINT_MAX - 24)) && ((offset + 24) < _depc_size)) - eagle_postcache_dbg("%s: first 6 integers %i %i %i %i %i %i", - __func__, *((int *)&_depc[offset]), - *((int *)&_depc[offset+4]), - *((int *)&_depc[offset+8]), - *((int *)&_depc[offset+12]), - *((int *)&_depc[offset+16]), - *((int *)&_depc[offset+20])); - eagle_postcache_dbg("%s: sending full data block to port, with cache index = %d device mask 0x%X, port_id = 0x%X, param = 0x%X, offset = %u, and size = %u", - __func__, cidx, _c_bl[cidx][CBD_DEV_MASK], port_id, cmd, - offset, size); - - if (_ac_NT) { - eagle_postcache_dbg("%s: NT Route detected", __func__); - if (q6asm_dts_eagle_set(_getNTDeviceAC(), cmd, size, - (void *)&_depc[offset], - &_po_NT, MPST)) - eagle_postcache_err("%s: q6asm_dts_eagle_set failed with id = 0x%X and size = %u", - __func__, cmd, size); - } else if (adm_dts_eagle_set(port_id, copp_idx, cmd, - (void *)&_depc[offset], size) < 0) - eagle_postcache_err("%s: adm_dts_eagle_set failed with id = 0x%X and size = %u", - __func__, cmd, size); - else - eagle_postcache_dbg("%s: adm_dts_eagle_set succeeded with id = 0x%X and size = %u", - __func__, cmd, size); - return 0; -} - -static int _enable_post_get_control(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.integer.value[0] = _is_hpx_enabled; - return 0; -} - -static int _enable_post_put_control(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - int idx = 0, be_index = 0, port_id, topology; - int flag = ucontrol->value.integer.value[0]; - struct msm_pcm_routing_bdai_data msm_bedai; - eagle_drv_dbg("%s: flag %d", __func__, flag); - - _is_hpx_enabled = flag ? true : false; - msm_pcm_routing_acquire_lock(); - /* send cache postmix params when hpx is set On */ - for (be_index = 0; be_index < MSM_BACKEND_DAI_MAX; be_index++) { - msm_pcm_routing_get_bedai_info(be_index, &msm_bedai); - port_id = msm_bedai.port_id; - if (!(((port_id == SLIMBUS_0_RX) || - (port_id == RT_PROXY_PORT_001_RX)) && - msm_bedai.active)) - continue; - for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) { - topology = adm_get_topology_for_port_copp_idx( - port_id, idx); - if (topology == - ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX) { - msm_dts_eagle_enable_adm(port_id, idx, - _is_hpx_enabled); - } - } - } - msm_pcm_routing_release_lock(); - return 0; -} - -static const struct snd_kcontrol_new _hpx_enabled_controls[] = { - SOC_SINGLE_EXT("Set HPX OnOff", SND_SOC_NOPM, 0, 1, 0, - _enable_post_get_control, _enable_post_put_control) -}; - -/** - * msm_dts_ion_memmap() - helper function to map ION memory - * @po_: Out of band memory structure used as memory. - * - * Assign already allocated ION memory for mapping it to dsp. - * - * Return: No return value. - */ -void msm_dts_ion_memmap(struct param_outband *po_) -{ - po_->size = ION_MEM_SIZE; - po_->kvaddr = _po.kvaddr; - po_->paddr = _po.paddr; -} - -/** - * msm_dts_eagle_enable_asm() - Enable/disable dts module - * @ac: Enable/disable module in ASM session associated with this audio client. - * @enable: Enable/disable the dts module. - * @module: module id. - * - * Enable/disable specified dts module id in asm. - * - * Return: Return failure if any. - */ -int msm_dts_eagle_enable_asm(struct audio_client *ac, u32 enable, int module) -{ - int ret = 0; - eagle_enable_dbg("%s: enable = %i on module %i", - __func__, enable, module); - _is_hpx_enabled = enable; - ret = q6asm_dts_eagle_set(ac, AUDPROC_PARAM_ID_ENABLE, - sizeof(enable), &enable, - NULL, module); - if (_is_hpx_enabled) { - if (module == MPRE) - _sendcache_pre(ac); - else if (module == MPST) - _sendcache_post(-1, 0, 0); - } - return ret; -} - -/** - * msm_dts_eagle_enable_adm() - Enable/disable dts module in adm - * @port_id: Send enable/disable param to this port id. - * @copp_idx: Send enable/disable param to the relevant copp. - * @enable: Enable/disable the dts module. - * - * Enable/disable dts module in adm. - * - * Return: Return failure if any. - */ -int msm_dts_eagle_enable_adm(int port_id, int copp_idx, u32 enable) -{ - int ret = 0; - eagle_enable_dbg("%s: enable = %i", __func__, enable); - _is_hpx_enabled = enable; - ret = adm_dts_eagle_set(port_id, copp_idx, AUDPROC_PARAM_ID_ENABLE, - (char *)&enable, sizeof(enable)); - if (_is_hpx_enabled) - _sendcache_post(port_id, copp_idx, MPST); - return ret; -} - -/** - * msm_dts_eagle_add_controls() - Add mixer control to Enable/Disable DTS HPX - * @platform: Add mixer controls to this platform. - * - * Add mixer control to Enable/Disable DTS HPX module in ADM. - * - * Return: No return value. - */ -void msm_dts_eagle_add_controls(struct snd_soc_platform *platform) -{ - snd_soc_add_platform_controls(platform, _hpx_enabled_controls, - ARRAY_SIZE(_hpx_enabled_controls)); -} - -/** - * msm_dts_eagle_set_stream_gain() - Set stream gain to DTS Premix module - * @ac: Set stream gain to ASM session associated with this audio client. - * @lgain: Left gain value. - * @rgain: Right gain value. - * - * Set stream gain to DTS Premix module in ASM. - * - * Return: failure or success. - */ -int msm_dts_eagle_set_stream_gain(struct audio_client *ac, int lgain, int rgain) -{ - u32 i, val; - s32 idx, err = 0; - - eagle_vol_dbg("%s: - entry: vol_cmd_cnt = %u, lgain = %i, rgain = %i", - __func__, _vol_cmd_cnt, lgain, rgain); - - if (_depc_size == 0) { - eagle_vol_dbg("%s: driver cache not initialized", __func__); - return -EINVAL; - } - - for (i = 0; i < _vol_cmd_cnt; i++) { - if (_vol_cmds_d[i].d[0] & 0x8000) { - idx = (sizeof(struct dts_eagle_param_desc)/sizeof(int)) - + (_vol_cmds_d[i].d[0] & 0x3FF); - val = _fx_logN(((s32)(lgain+rgain)) << 2); - val = ((long long)val * _log10_10_inv_x20) >> 16; - _vol_cmds[i][idx] = (s32)clamp((int)(((long long)val * - _vol_cmds_d[i].d[1]) >> 16), - _vol_cmds_d[i].d[2], - _vol_cmds_d[i].d[3]); - eagle_vol_dbg("%s: loop %u cmd desc found %i, idx = %i. volume info: lgain = %i, rgain = %i, volume = %i (scale %i, min %i, max %i)", - __func__, i, _vol_cmds_d[i].d[0], idx, lgain, - rgain, _vol_cmds[i][idx], _vol_cmds_d[i].d[1], - _vol_cmds_d[i].d[2], _vol_cmds_d[i].d[3]); - } - idx = _get_cb_for_dev(_device_primary); - if (idx < 0) { - eagle_vol_err("%s: no cache for primary device %i found", - __func__, _device_primary); - return -EINVAL; - } - val = _c_bl[idx][CBD_OFFSG] + _vol_cmds[i][2]; - /* check for integer overflow */ - if (val > (UINT_MAX - _vol_cmds[i][1])) - err = -EINVAL; - if ((err != 0) || ((val + _vol_cmds[i][1]) > _depc_size)) { - eagle_vol_err("%s: volume size (%u) + offset (%i) out of bounds %i", - __func__, val, _vol_cmds[i][1], _depc_size); - return -EINVAL; - } - memcpy((void *)&_depc[val], &_vol_cmds[i][4], _vol_cmds[i][1]); - if (q6asm_dts_eagle_set(ac, _vol_cmds[i][0], - _vol_cmds[i][1], (void *)&_depc[val], NULL, MPRE)) - eagle_vol_err("%s: loop %u - volume set failed with id 0x%X, size %i, offset %i, cmd_desc %i, scale %i, min %i, max %i, data(...) %i", - __func__, i, _vol_cmds[i][0], _vol_cmds[i][1], - _vol_cmds[i][2], _vol_cmds_d[i].d[0], - _vol_cmds_d[i].d[1], _vol_cmds_d[i].d[2], - _vol_cmds_d[i].d[3], _vol_cmds[i][4]); - else - eagle_vol_dbg("%s: loop %u - volume set succeeded with id 0x%X, size %i, offset %i, cmd_desc %i, scale %i, min %i, max %i, data(...) %i", - __func__, i, _vol_cmds[i][0], _vol_cmds[i][1], - _vol_cmds[i][2], _vol_cmds_d[i].d[0], - _vol_cmds_d[i].d[1], _vol_cmds_d[i].d[2], - _vol_cmds_d[i].d[3], _vol_cmds[i][4]); - } - return 0; -} - -/** - * msm_dts_eagle_handle_asm() - Set or Get params from ASM - * @depd: DTS Eagle Params structure. - * @buf: Buffer to get queried param value. - * @for_pre: For premix module or postmix module. - * @get: Getting param from DSP or setting param. - * @ac: Set/Get from ASM session associated with this audio client. - * @po: Out of band memory to set or get postmix params. - * - * Set or Get params from modules in ASM session. - * - * Return: Return failure if any. - */ -int msm_dts_eagle_handle_asm(struct dts_eagle_param_desc *depd, char *buf, - bool for_pre, bool get, struct audio_client *ac, - struct param_outband *po) -{ - struct dts_eagle_param_desc depd_ = {0}; - s32 ret = 0, isALSA = 0, err = 0, i, mod = for_pre ? MPRE : MPST; - u32 offset; - - eagle_asm_dbg("%s: set/get asm", __func__); - - /* special handling for ALSA route, to accommodate 64 bit platforms */ - if (depd == NULL) { - long *arg_ = (long *)buf; - depd = &depd_; - depd->id = (u32)*arg_++; - depd->size = (u32)*arg_++; - depd->offset = (s32)*arg_++; - depd->device = (u32)*arg_++; - buf = (char *)arg_; - isALSA = 1; - } - - if (depd->size & 1) { - eagle_asm_err("%s: parameter size %u is not a multiple of 2", - __func__, depd->size); - return -EINVAL; - } - - if (get) { - void *buf_, *buf_m = NULL; - eagle_asm_dbg("%s: get requested", __func__); - if (depd->offset == -1) { - eagle_asm_dbg("%s: get from dsp requested", __func__); - if (depd->size > 0 && depd->size <= DEPC_MAX_SIZE) { - buf_ = buf_m = vzalloc(depd->size); - } else { - eagle_asm_err("%s: get size %u invalid", - __func__, depd->size); - return -EINVAL; - } - if (!buf_m) { - eagle_asm_err("%s: out of memory", __func__); - return -ENOMEM; - } else if (q6asm_dts_eagle_get(ac, depd->id, - depd->size, buf_m, - po, mod) < 0) { - eagle_asm_err("%s: asm get failed", __func__); - ret = -EFAULT; - goto DTS_EAGLE_IOCTL_GET_PARAM_PRE_EXIT; - } - eagle_asm_dbg("%s: get result: param id 0x%x value %d size %u", - __func__, depd->id, *(int *)buf_m, depd->size); - } else { - s32 tgt = _get_cb_for_dev(depd->device); - if (tgt < 0) { - eagle_asm_err("%s: no cache for device %u found", - __func__, depd->device); - return -EINVAL; - } - offset = _c_bl[tgt][CBD_OFFSG] + depd->offset; - /* check for integer overflow */ - if (offset > (UINT_MAX - depd->size)) - err = -EINVAL; - if ((err != 0) || (offset + depd->size) > _depc_size) { - eagle_asm_err("%s: invalid size %u and/or offset %u", - __func__, depd->size, offset); - return -EINVAL; - } - buf_ = (u32 *)&_depc[offset]; - } - if (isALSA) { - if (depd->size == 2) { - *(long *)buf = (long)*(__u16 *)buf_; - eagle_asm_dbg("%s: asm out 16 bit value %li", - __func__, *(long *)buf); - } else { - s32 *pbuf = (s32 *)buf_; - long *bufl = (long *)buf; - for (i = 0; i < (depd->size >> 2); i++) { - *bufl++ = (long)*pbuf++; - eagle_asm_dbg("%s: asm out value %li", - __func__, *(bufl-1)); - } - } - } else { - memcpy(buf, buf_, depd->size); - } -DTS_EAGLE_IOCTL_GET_PARAM_PRE_EXIT: - vfree(buf_m); - return (int)ret; - } else { - s32 tgt = _get_cb_for_dev(depd->device); - if (tgt < 0) { - eagle_asm_err("%s: no cache for device %u found", - __func__, depd->device); - return -EINVAL; - } - offset = _c_bl[tgt][CBD_OFFSG] + depd->offset; - /* check for integer overflow */ - if (offset > (UINT_MAX - depd->size)) - err = -EINVAL; - if ((err != 0) || ((offset + depd->size) > _depc_size)) { - eagle_asm_err("%s: invalid size %u and/or offset %u for parameter (cache is size %u)", - __func__, depd->size, offset, _depc_size); - return -EINVAL; - } - if (isALSA) { - if (depd->size == 2) { - *(__u16 *)&_depc[offset] = (__u16)*(long *)buf; - eagle_asm_dbg("%s: asm in 16 bit value %li", - __func__, *(long *)buf); - } else { - s32 *pbuf = (s32 *)&_depc[offset]; - long *bufl = (long *)buf; - for (i = 0; i < (depd->size >> 2); i++) { - *pbuf++ = (s32)*bufl++; - eagle_asm_dbg("%s: asm in value %i", - __func__, *(pbuf-1)); - } - } - } else { - memcpy(&_depc[offset], buf, depd->size); - } - eagle_asm_dbg("%s: param info: param = 0x%X, size = %u, offset = %i, device = %u, cache block %i, global offset = %u, first bytes as integer = %i", - __func__, depd->id, depd->size, depd->offset, - depd->device, - tgt, offset, *(int *)&_depc[offset]); - if (q6asm_dts_eagle_set(ac, depd->id, depd->size, - (void *)&_depc[offset], po, mod)) - eagle_asm_err("%s: q6asm_dts_eagle_set failed with id = 0x%X, size = %u, offset = %d", - __func__, depd->id, depd->size, depd->offset); - else - eagle_asm_dbg("%s: q6asm_dts_eagle_set succeeded with id = 0x%X, size = %u, offset = %d", - __func__, depd->id, depd->size, depd->offset); - } - return (int)ret; -} - -/** - * msm_dts_eagle_handle_adm() - Set or Get params from ADM - * @depd: DTS Eagle Params structure used to set or get. - * @buf: Buffer to get queried param value in NT mode. - * @for_pre: For premix module or postmix module. - * @get: Getting param from DSP or setting param. - * - * Set or Get params from modules in ADM session. - * - * Return: Return failure if any. - */ -int msm_dts_eagle_handle_adm(struct dts_eagle_param_desc *depd, char *buf, - bool for_pre, bool get) -{ - u32 pid = _get_pid_from_dev(depd->device), cidx; - s32 ret = 0; - - eagle_adm_dbg("%s: set/get adm", __func__); - - if (_isNTDevice(depd->device)) { - eagle_adm_dbg("%s: NT Route detected", __func__); - ret = msm_dts_eagle_handle_asm(depd, buf, for_pre, get, - _getNTDeviceAC(), &_po_NT); - if (ret < 0) - eagle_adm_err("%s: NT Route set failed with id = 0x%X, size = %u, offset = %i, device = %u", - __func__, depd->id, depd->size, depd->offset, - depd->device); - } else if (get) { - cidx = adm_validate_and_get_port_index(pid); - eagle_adm_dbg("%s: get from qdsp requested (port id 0x%X)", - __func__, pid); - if (adm_dts_eagle_get(pid, _cidx[cidx], depd->id, - buf, depd->size) < 0) { - eagle_adm_err("%s: get from qdsp via adm with port id 0x%X failed", - __func__, pid); - return -EFAULT; - } - } else if (_is_port_open_and_eagle(pid)) { - cidx = adm_validate_and_get_port_index(pid); - eagle_adm_dbg("%s: adm_dts_eagle_set called with id = 0x%X, size = %u, offset = %i, device = %u, port id = %u, copp index = %u", - __func__, depd->id, depd->size, depd->offset, - depd->device, pid, cidx); - ret = adm_dts_eagle_set(pid, _cidx[cidx], depd->id, - (void *)buf, depd->size); - if (ret < 0) - eagle_adm_err("%s: adm_dts_eagle_set failed", __func__); - else - eagle_adm_dbg("%s: adm_dts_eagle_set succeeded", - __func__); - } else { - ret = -EINVAL; - eagle_adm_dbg("%s: port id 0x%X not active or not Eagle", - __func__, pid); - } - return (int)ret; -} - -/** - * msm_dts_eagle_ioctl() - ioctl handler function - * @cmd: cmd to handle. - * @arg: argument to the cmd. - * - * Handle DTS Eagle ioctl cmds. - * - * Return: Return failure if any. - */ -int msm_dts_eagle_ioctl(unsigned int cmd, unsigned long arg) -{ - s32 ret = 0; - switch (cmd) { - case DTS_EAGLE_IOCTL_GET_CACHE_SIZE: { - eagle_ioctl_info("%s: called with control 0x%X (get param cache size)", - __func__, cmd); - if (copy_to_user((void *)arg, &_depc_size, - sizeof(_depc_size))) { - eagle_ioctl_err("%s: error writing size", __func__); - return -EFAULT; - } - break; - } - case DTS_EAGLE_IOCTL_SET_CACHE_SIZE: { - u32 size = 0; - eagle_ioctl_info("%s: called with control 0x%X (allocate param cache)", - __func__, cmd); - if (copy_from_user((void *)&size, (void *)arg, sizeof(size))) { - eagle_ioctl_err("%s: error copying size (src:%pK, tgt:%pK, size:%zu)", - __func__, (void *)arg, &size, sizeof(size)); - return -EFAULT; - } else if (size > DEPC_MAX_SIZE) { - eagle_ioctl_err("%s: cache size %u not allowed (min 0, max %u)", - __func__, size, DEPC_MAX_SIZE); - return -EINVAL; - } - if (_depc) { - eagle_ioctl_dbg("%s: previous param cache of size %u freed", - __func__, _depc_size); - _depc_size = 0; - vfree(_depc); - _depc = NULL; - } - if (size) - _depc = vzalloc(size); - else - eagle_ioctl_dbg("%s: %u bytes requested for param cache, nothing allocated", - __func__, size); - if (_depc) { - eagle_ioctl_dbg("%s: %u bytes allocated for param cache", - __func__, size); - _depc_size = size; - } else { - eagle_ioctl_err("%s: error allocating param cache (vzalloc failed on %u bytes)", - __func__, size); - _depc_size = 0; - return -ENOMEM; - } - break; - } - case DTS_EAGLE_IOCTL_GET_PARAM: { - struct dts_eagle_param_desc depd; - s32 for_pre = 0, get_from_core = 0, err = 0; - u32 offset; - void *buf, *buf_m = NULL; - eagle_ioctl_info("%s: control 0x%X (get param)", - __func__, cmd); - if (copy_from_user((void *)&depd, (void *)arg, sizeof(depd))) { - eagle_ioctl_err("%s: error copying dts_eagle_param_desc (src:%pK, tgt:%pK, size:%zu)", - __func__, (void *)arg, &depd, sizeof(depd)); - return -EFAULT; - } - if (depd.device & DTS_EAGLE_FLAG_IOCTL_PRE) { - eagle_ioctl_dbg("%s: using for premix", __func__); - for_pre = 1; - } - if (depd.device & DTS_EAGLE_FLAG_IOCTL_GETFROMCORE) { - eagle_ioctl_dbg("%s: 'get from core' requested", - __func__); - get_from_core = 1; - depd.offset = -1; - } - depd.device &= DTS_EAGLE_FLAG_IOCTL_MASK; - if (depd.offset == -1) { - if (depd.size > 0 && depd.size <= DEPC_MAX_SIZE) { - buf = buf_m = vzalloc(depd.size); - } else { - eagle_ioctl_err("%s: get size %u invalid", - __func__, depd.size); - return -EINVAL; - } - if (!buf_m) { - eagle_ioctl_err("%s: out of memory", __func__); - return -ENOMEM; - } - if (get_from_core) - ret = core_dts_eagle_get(depd.id, depd.size, - buf); - else - ret = msm_dts_eagle_handle_adm(&depd, buf, - for_pre, true); - } else { - s32 cb = _get_cb_for_dev(depd.device); - if (cb < 0) { - eagle_ioctl_err("%s: no cache for device %u found", - __func__, depd.device); - return -EINVAL; - } - offset = _c_bl[cb][CBD_OFFSG] + depd.offset; - /* check for integer overflow */ - if (offset > (UINT_MAX - depd.size)) - err = -EINVAL; - if ((err != 0) || - ((offset + depd.size) > _depc_size)) { - eagle_ioctl_err("%s: invalid size %u and/or offset %u", - __func__, depd.size, offset); - return -EINVAL; - } - buf = (void *)&_depc[offset]; - } - if (ret < 0) - eagle_ioctl_err("%s: error %i getting data", __func__, - ret); - else if (copy_to_user((void *)(((char *)arg)+sizeof(depd)), - buf, depd.size)) { - eagle_ioctl_err("%s: error copying get data", __func__); - ret = -EFAULT; - } - vfree(buf_m); - break; - } - case DTS_EAGLE_IOCTL_SET_PARAM: { - struct dts_eagle_param_desc depd; - s32 just_set_cache = 0, for_pre = 0, err = 0; - u32 offset; - s32 tgt; - eagle_ioctl_info("%s: control 0x%X (set param)", - __func__, cmd); - if (copy_from_user((void *)&depd, (void *)arg, sizeof(depd))) { - eagle_ioctl_err("%s: error copying dts_eagle_param_desc (src:%pK, tgt:%pK, size:%zu)", - __func__, (void *)arg, &depd, sizeof(depd)); - return -EFAULT; - } - if (depd.device & DTS_EAGLE_FLAG_IOCTL_PRE) { - eagle_ioctl_dbg("%s: using for premix", __func__); - for_pre = 1; - } - if (depd.device & DTS_EAGLE_FLAG_IOCTL_JUSTSETCACHE) { - eagle_ioctl_dbg("%s: 'just set cache' requested", - __func__); - just_set_cache = 1; - } - depd.device &= DTS_EAGLE_FLAG_IOCTL_MASK; - tgt = _get_cb_for_dev(depd.device); - if (tgt < 0) { - eagle_ioctl_err("%s: no cache for device %u found", - __func__, depd.device); - return -EINVAL; - } - offset = _c_bl[tgt][CBD_OFFSG] + depd.offset; - /* check for integer overflow */ - if (offset > (UINT_MAX - depd.size)) - err = -EINVAL; - if ((err != 0) || ((offset + depd.size) > _depc_size)) { - eagle_ioctl_err("%s: invalid size %u and/or offset %u for parameter (target cache block %i with offset %i, global cache is size %u)", - __func__, depd.size, offset, tgt, - _c_bl[tgt][CBD_OFFSG], _depc_size); - return -EINVAL; - } - if (copy_from_user((void *)&_depc[offset], - (void *)(((char *)arg)+sizeof(depd)), - depd.size)) { - eagle_ioctl_err("%s: error copying param to cache (src:%pK, tgt:%pK, size:%u)", - __func__, ((char *)arg)+sizeof(depd), - &_depc[offset], depd.size); - return -EFAULT; - } - eagle_ioctl_dbg("%s: param info: param = 0x%X, size = %u, offset = %i, device = %u, cache block %i, global offset = %u, first bytes as integer = %i", - __func__, depd.id, depd.size, depd.offset, - depd.device, tgt, offset, *(int *)&_depc[offset]); - if (!just_set_cache) { - ret = msm_dts_eagle_handle_adm(&depd, &_depc[offset], - for_pre, false); - } - break; - } - case DTS_EAGLE_IOCTL_SET_CACHE_BLOCK: { - u32 b_[CBD_COUNT+1], *b = &b_[1], cb; - eagle_ioctl_info("%s: with control 0x%X (set param cache block)", - __func__, cmd); - if (copy_from_user((void *)b_, (void *)arg, sizeof(b_))) { - eagle_ioctl_err("%s: error copying cache block data (src:%pK, tgt:%pK, size:%zu)", - __func__, (void *)arg, b_, sizeof(b_)); - return -EFAULT; - } - cb = b_[0]; - if (cb >= CB_COUNT) { - eagle_ioctl_err("%s: cache block %u out of range (max %u)", - __func__, cb, CB_COUNT-1); - return -EINVAL; - } - eagle_ioctl_dbg("%s: cache block %i set: devices 0x%X, global offset %i, offsets 1:%u 2:%u 3:%u, cmds/sizes 0:0x%X %u 1:0x%X %u 2:0x%X %u 3:0x%X %u", - __func__, cb, _c_bl[cb][CBD_DEV_MASK], _c_bl[cb][CBD_OFFSG], - _c_bl[cb][CBD_OFFS1], _c_bl[cb][CBD_OFFS2], - _c_bl[cb][CBD_OFFS3], _c_bl[cb][CBD_CMD0], _c_bl[cb][CBD_SZ0], - _c_bl[cb][CBD_CMD1], _c_bl[cb][CBD_SZ1], _c_bl[cb][CBD_CMD2], - _c_bl[cb][CBD_SZ2], _c_bl[cb][CBD_CMD3], _c_bl[cb][CBD_SZ3]); - if ((b[CBD_OFFSG]+b[CBD_OFFS1]+b[CBD_SZ1]) > _depc_size || - (b[CBD_OFFSG]+b[CBD_OFFS2]+b[CBD_SZ2]) > _depc_size || - (b[CBD_OFFSG]+b[CBD_OFFS3]+b[CBD_SZ3]) > _depc_size) { - eagle_ioctl_err("%s: cache block bounds out of range", - __func__); - return -EINVAL; - } - memcpy(_c_bl[cb], b, sizeof(_c_bl[cb])); - break; - } - case DTS_EAGLE_IOCTL_SET_ACTIVE_DEVICE: { - u32 data[2]; - eagle_ioctl_dbg("%s: with control 0x%X (set active device)", - __func__, cmd); - if (copy_from_user((void *)data, (void *)arg, sizeof(data))) { - eagle_ioctl_err("%s: error copying active device data (src:%pK, tgt:%pK, size:%zu)", - __func__, (void *)arg, data, sizeof(data)); - return -EFAULT; - } - if (data[1] != 0) { - _device_primary = data[0]; - eagle_ioctl_dbg("%s: primary device %i", __func__, - data[0]); - } else { - _device_all = data[0]; - eagle_ioctl_dbg("%s: all devices 0x%X", __func__, - data[0]); - } - break; - } - case DTS_EAGLE_IOCTL_GET_LICENSE: { - u32 target = 0, size = 0; - s32 size_only; - eagle_ioctl_dbg("%s: with control 0x%X (get license)", - __func__, cmd); - if (copy_from_user((void *)&target, (void *)arg, - sizeof(target))) { - eagle_ioctl_err("%s: error reading license index. (src:%pK, tgt:%pK, size:%zu)", - __func__, (void *)arg, &target, sizeof(target)); - return -EFAULT; - } - size_only = target & (1<<31) ? 1 : 0; - target &= 0x7FFFFFFF; - if (target >= SEC_BLOB_MAX_CNT) { - eagle_ioctl_err("%s: license index %u out of bounds (max index is %i)", - __func__, target, SEC_BLOB_MAX_CNT); - return -EINVAL; - } - if (_sec_blob[target] == NULL) { - eagle_ioctl_err("%s: license index %u never initialized", - __func__, target); - return -EINVAL; - } - size = ((u32 *)_sec_blob[target])[0]; - if ((size == 0) || (size > SEC_BLOB_MAX_SIZE)) { - eagle_ioctl_err("%s: license size %u for index %u invalid (min size is 1, max size is %u)", - __func__, size, target, SEC_BLOB_MAX_SIZE); - return -EINVAL; - } - if (size_only) { - eagle_ioctl_dbg("%s: reporting size of license data only", - __func__); - if (copy_to_user((void *)(((char *)arg)+sizeof(target)), - (void *)&size, sizeof(size))) { - eagle_ioctl_err("%s: error copying license size", - __func__); - return -EFAULT; - } - } else if (copy_to_user((void *)(((char *)arg)+sizeof(target)), - (void *)&(((s32 *)_sec_blob[target])[1]), size)) { - eagle_ioctl_err("%s: error copying license data", - __func__); - return -EFAULT; - } else - eagle_ioctl_info("%s: license file %u bytes long from license index %u returned to user", - __func__, size, target); - break; - } - case DTS_EAGLE_IOCTL_SET_LICENSE: { - u32 target[2] = {0, 0}; - eagle_ioctl_dbg("%s: control 0x%X (set license)", __func__, - cmd); - if (copy_from_user((void *)target, (void *)arg, - sizeof(target))) { - eagle_ioctl_err("%s: error reading license index (src:%pK, tgt:%pK, size:%zu)", - __func__, (void *)arg, target, sizeof(target)); - return -EFAULT; - } - if (target[0] >= SEC_BLOB_MAX_CNT) { - eagle_ioctl_err("%s: license index %u out of bounds (max index is %u)", - __func__, target[0], SEC_BLOB_MAX_CNT-1); - return -EINVAL; - } - if (target[1] == 0) { - eagle_ioctl_dbg("%s: request to free license index %u", - __func__, target[0]); - kfree(_sec_blob[target[0]]); - _sec_blob[target[0]] = NULL; - break; - } - if ((target[1] == 0) || (target[1] >= SEC_BLOB_MAX_SIZE)) { - eagle_ioctl_err("%s: license size %u for index %u invalid (min size is 1, max size is %u)", - __func__, target[1], target[0], - SEC_BLOB_MAX_SIZE); - return -EINVAL; - } - if (_sec_blob[target[0]] != NULL) { - if (((u32 *)_sec_blob[target[0]])[1] != target[1]) { - eagle_ioctl_dbg("%s: request new size for already allocated license index %u", - __func__, target[0]); - } - kfree(_sec_blob[target[0]]); - _sec_blob[target[0]] = NULL; - } - eagle_ioctl_dbg("%s: allocating %u bytes for license index %u", - __func__, target[1], target[0]); - _sec_blob[target[0]] = kzalloc(target[1] + 4, GFP_KERNEL); - if (!_sec_blob[target[0]]) { - eagle_ioctl_err("%s: error allocating license index %u (kzalloc failed on %u bytes)", - __func__, target[0], target[1]); - return -ENOMEM; - } - ((u32 *)_sec_blob[target[0]])[0] = target[1]; - if (copy_from_user( - (void *)&(((u32 *)_sec_blob[target[0]])[1]), - (void *)(((char *)arg)+sizeof(target)), - target[1])) { - eagle_ioctl_err("%s: error copying license to index %u, size %u (src:%pK, tgt:%pK, size:%u)", - __func__, target[0], target[1], - ((char *)arg)+sizeof(target), - &(((u32 *)_sec_blob[target[0]])[1]), - target[1]); - return -EFAULT; - } else - eagle_ioctl_info("%s: license file %u bytes long copied to index license index %u", - __func__, target[1], target[0]); - break; - } - case DTS_EAGLE_IOCTL_SEND_LICENSE: { - u32 target = 0; - eagle_ioctl_dbg("%s: control 0x%X (send license)", __func__, - cmd); - if (copy_from_user((void *)&target, (void *)arg, - sizeof(target))) { - eagle_ioctl_err("%s: error reading license index (src:%pK, tgt:%pK, size:%zu)", - __func__, (void *)arg, &target, sizeof(target)); - return -EFAULT; - } - if (target >= SEC_BLOB_MAX_CNT) { - eagle_ioctl_err("%s: license index %u out of bounds (max index is %i)", - __func__, target, SEC_BLOB_MAX_CNT-1); - return -EINVAL; - } - if (!_sec_blob[target] || - ((u32 *)_sec_blob[target])[0] == 0) { - eagle_ioctl_err("%s: license index %u is invalid", - __func__, target); - return -EINVAL; - } - if (core_dts_eagle_set(((s32 *)_sec_blob[target])[0], - (char *)&((s32 *)_sec_blob[target])[1]) < 0) - eagle_ioctl_err("%s: core_dts_eagle_set failed with id = %u", - __func__, target); - else - eagle_ioctl_info("%s: core_dts_eagle_set succeeded with id = %u", - __func__, target); - break; - } - case DTS_EAGLE_IOCTL_SET_VOLUME_COMMANDS: { - s32 spec = 0; - eagle_ioctl_info("%s: control 0x%X (set volume commands)", - __func__, cmd); - if (copy_from_user((void *)&spec, (void *)arg, - sizeof(spec))) { - eagle_ioctl_err("%s: error reading volume command specifier (src:%pK, tgt:%pK, size:%zu)", - __func__, (void *)arg, &spec, sizeof(spec)); - return -EFAULT; - } - if (spec & 0x80000000) { - u32 idx = (spec & 0x0000F000) >> 12; - s32 size = spec & 0x00000FFF; - eagle_ioctl_dbg("%s: setting volume command %i size: %i", - __func__, idx, size); - if (idx >= _vol_cmd_cnt) { - eagle_ioctl_err("%s: volume command index %u out of bounds (only %u allocated)", - __func__, idx, _vol_cmd_cnt); - return -EINVAL; - } - if (_volume_cmds_alloc2(idx, size) < 0) { - eagle_ioctl_err("%s: error allocating memory for volume controls", - __func__); - return -ENOMEM; - } - if (copy_from_user((void *)&_vol_cmds_d[idx], - (void *)(((char *)arg) + sizeof(int)), - sizeof(struct vol_cmds_d))) { - eagle_ioctl_err("%s: error reading volume command descriptor (src:%pK, tgt:%pK, size:%zu)", - __func__, ((char *)arg) + sizeof(int), - &_vol_cmds_d[idx], - sizeof(struct vol_cmds_d)); - return -EFAULT; - } - eagle_ioctl_dbg("%s: setting volume command %i spec (size %zu): %i %i %i %i", - __func__, idx, sizeof(struct vol_cmds_d), - _vol_cmds_d[idx].d[0], _vol_cmds_d[idx].d[1], - _vol_cmds_d[idx].d[2], _vol_cmds_d[idx].d[3]); - if (copy_from_user((void *)_vol_cmds[idx], - (void *)(((char *)arg) + (sizeof(int) + - sizeof(struct vol_cmds_d))), size)) { - eagle_ioctl_err("%s: error reading volume command string (src:%pK, tgt:%pK, size:%i)", - __func__, ((char *)arg) + (sizeof(int) + - sizeof(struct vol_cmds_d)), - _vol_cmds[idx], size); - return -EFAULT; - } - } else { - eagle_ioctl_dbg("%s: setting volume command size", - __func__); - if (spec < 0 || spec > VOL_CMD_CNT_MAX) { - eagle_ioctl_err("%s: volume command count %i out of bounds (min 0, max %i)", - __func__, spec, VOL_CMD_CNT_MAX); - return -EINVAL; - } else if (spec == 0) { - eagle_ioctl_dbg("%s: request to free volume commands", - __func__); - _volume_cmds_free(); - break; - } - eagle_ioctl_dbg("%s: setting volume command size requested = %i", - __func__, spec); - if (_volume_cmds_alloc1(spec) < 0) { - eagle_ioctl_err("%s: error allocating memory for volume controls", - __func__); - return -ENOMEM; - } - } - break; - } - default: { - eagle_ioctl_err("%s: control 0x%X (invalid control)", - __func__, cmd); - ret = -EINVAL; - } - } - return (int)ret; -} - -/** - * msm_dts_eagle_compat_ioctl() - To handle 32bit to 64bit ioctl compatibility - * @cmd: cmd to handle. - * @arg: argument to the cmd. - * - * Handle DTS Eagle ioctl cmds from 32bit userspace. - * - * Return: Return failure if any. - */ -#ifdef CONFIG_COMPAT -int msm_dts_eagle_compat_ioctl(unsigned int cmd, unsigned long arg) -{ - switch (cmd) { - case DTS_EAGLE_IOCTL_GET_CACHE_SIZE32: - cmd = DTS_EAGLE_IOCTL_GET_CACHE_SIZE; - break; - case DTS_EAGLE_IOCTL_SET_CACHE_SIZE32: - cmd = DTS_EAGLE_IOCTL_SET_CACHE_SIZE; - break; - case DTS_EAGLE_IOCTL_GET_PARAM32: - cmd = DTS_EAGLE_IOCTL_GET_PARAM; - break; - case DTS_EAGLE_IOCTL_SET_PARAM32: - cmd = DTS_EAGLE_IOCTL_SET_PARAM; - break; - case DTS_EAGLE_IOCTL_SET_CACHE_BLOCK32: - cmd = DTS_EAGLE_IOCTL_SET_CACHE_BLOCK; - break; - case DTS_EAGLE_IOCTL_SET_ACTIVE_DEVICE32: - cmd = DTS_EAGLE_IOCTL_SET_ACTIVE_DEVICE; - break; - case DTS_EAGLE_IOCTL_GET_LICENSE32: - cmd = DTS_EAGLE_IOCTL_GET_LICENSE; - break; - case DTS_EAGLE_IOCTL_SET_LICENSE32: - cmd = DTS_EAGLE_IOCTL_SET_LICENSE; - break; - case DTS_EAGLE_IOCTL_SEND_LICENSE32: - cmd = DTS_EAGLE_IOCTL_SEND_LICENSE; - break; - case DTS_EAGLE_IOCTL_SET_VOLUME_COMMANDS32: - cmd = DTS_EAGLE_IOCTL_SET_VOLUME_COMMANDS; - break; - default: - break; - } - return msm_dts_eagle_ioctl(cmd, arg); -} -#endif -/** - * msm_dts_eagle_init_pre() - Initialize DTS premix module - * @ac: Initialize premix module in the ASM session. - * - * Initialize DTS premix module on provided ASM session - * - * Return: Return failure if any. - */ -int msm_dts_eagle_init_pre(struct audio_client *ac) -{ - return msm_dts_eagle_enable_asm(ac, _is_hpx_enabled, - AUDPROC_MODULE_ID_DTS_HPX_PREMIX); -} - -/** - * msm_dts_eagle_deinit_pre() - Deinitialize DTS premix module - * @ac: Deinitialize premix module in the ASM session. - * - * Deinitialize DTS premix module on provided ASM session - * - * Return: Currently does nothing so 0. - */ -int msm_dts_eagle_deinit_pre(struct audio_client *ac) -{ - return 0; -} - -/** - * msm_dts_eagle_init_post() - Initialize DTS postmix module - * @port_id: Port id for the ADM session. - * @copp_idx: Copp idx for the ADM session. - * - * Initialize DTS postmix module on ADM session - * - * Return: Return failure if any. - */ -int msm_dts_eagle_init_post(int port_id, int copp_idx) -{ - return msm_dts_eagle_enable_adm(port_id, copp_idx, _is_hpx_enabled); -} - -/** - * msm_dts_eagle_deinit_post() - Deinitialize DTS postmix module - * @port_id: Port id for the ADM session. - * @topology: Topology in use. - * - * Deinitialize DTS postmix module on ADM session - * - * Return: Currently does nothing so 0. - */ -int msm_dts_eagle_deinit_post(int port_id, int topology) -{ - return 0; -} - -/** - * msm_dts_eagle_init_master_module() - Initialize both DTS modules - * @ac: Initialize modules in the ASM session. - * - * Initialize DTS modules on ASM session - * - * Return: Success. - */ -int msm_dts_eagle_init_master_module(struct audio_client *ac) -{ - _set_audioclient(ac); - msm_dts_eagle_enable_asm(ac, _is_hpx_enabled, - AUDPROC_MODULE_ID_DTS_HPX_PREMIX); - msm_dts_eagle_enable_asm(ac, _is_hpx_enabled, - AUDPROC_MODULE_ID_DTS_HPX_POSTMIX); - return 0; -} - -/** - * msm_dts_eagle_deinit_master_module() - Deinitialize both DTS modules - * @ac: Deinitialize modules in the ASM session. - * - * Deinitialize DTS modules on ASM session - * - * Return: Success. - */ -int msm_dts_eagle_deinit_master_module(struct audio_client *ac) -{ - msm_dts_eagle_deinit_pre(ac); - msm_dts_eagle_deinit_post(-1, 0); - _clear_audioclient(); - return 0; -} - -/** - * msm_dts_eagle_is_hpx_on() - Check if HPX effects are On - * - * Check if HPX effects are On - * - * Return: On/Off. - */ -int msm_dts_eagle_is_hpx_on(void) -{ - return _is_hpx_enabled; -} - -/** - * msm_dts_eagle_pcm_new() - Create hwdep node - * @runtime: snd_soc_pcm_runtime structure. - * - * Create hwdep node - * - * Return: Success. - */ -int msm_dts_eagle_pcm_new(struct snd_soc_pcm_runtime *runtime) -{ - if (!_ref_cnt++) { - _init_cb_descs(); - _reg_ion_mem(); - } - return 0; -} - -/** - * msm_dts_eagle_pcm_free() - remove hwdep node - * @runtime: snd_soc_pcm_runtime structure. - * - * Remove hwdep node - * - * Return: void. - */ -void msm_dts_eagle_pcm_free(struct snd_pcm *pcm) -{ - if (!--_ref_cnt) - _unreg_ion_mem(); - vfree(_depc); -} - -MODULE_DESCRIPTION("DTS EAGLE platform driver"); -MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.c b/sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.c index 7c35d19bb610..8fc49b29000c 100644 --- a/sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.c +++ b/sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.c @@ -1,4 +1,5 @@ -/* Copyright (c) 2012-2014, 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2014, 2016-2017, 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,7 +20,6 @@ #include <sound/control.h> #include <sound/q6adm-v2.h> #include <sound/asound.h> -#include <sound/msm-dts-eagle.h> #include "msm-dts-srs-tm-config.h" #include "msm-pcm-routing-v2.h" diff --git a/sound/soc/msm/qdsp6v2/msm-lsm-client.c b/sound/soc/msm/qdsp6v2/msm-lsm-client.c index 109e1a202ff2..1003dc8ddc5a 100644 --- a/sound/soc/msm/qdsp6v2/msm-lsm-client.c +++ b/sound/soc/msm/qdsp6v2/msm-lsm-client.c @@ -1312,7 +1312,7 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream, } size = sizeof(*user) + userarg32.payload_size; - user = kmalloc(size, GFP_KERNEL); + user = kzalloc(size, GFP_KERNEL); if (!user) { dev_err(rtd->dev, "%s: Allocation failed event status size %d\n", @@ -1333,7 +1333,7 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream, err = -EFAULT; } if (!err) { - user32 = kmalloc(size, GFP_KERNEL); + user32 = kzalloc(size, GFP_KERNEL); if (!user32) { dev_err(rtd->dev, "%s: Allocation event user status size %d\n", @@ -1378,7 +1378,7 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream, } size = sizeof(*user) + userarg32.payload_size; - user = kmalloc(size, GFP_KERNEL); + user = kzalloc(size, GFP_KERNEL); if (!user) { dev_err(rtd->dev, "%s: Allocation failed event status size %d\n", @@ -1398,7 +1398,7 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream, err = -EFAULT; } if (!err) { - user32 = kmalloc(size, GFP_KERNEL); + user32 = kzalloc(size, GFP_KERNEL); if (!user32) { dev_err(rtd->dev, "%s: Allocation event user status size %d\n", @@ -1813,7 +1813,7 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, size = sizeof(struct snd_lsm_event_status) + userarg.payload_size; - user = kmalloc(size, GFP_KERNEL); + user = kzalloc(size, GFP_KERNEL); if (!user) { dev_err(rtd->dev, "%s: Allocation failed event status size %d\n", @@ -1874,7 +1874,7 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, size = sizeof(struct snd_lsm_event_status_v3) + userarg.payload_size; - user = kmalloc(size, GFP_KERNEL); + user = kzalloc(size, GFP_KERNEL); if (!user) { dev_err(rtd->dev, "%s: Allocation failed event status size %d\n", diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c index 73eadfa4eebb..8f43aee3974c 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c @@ -239,8 +239,7 @@ static void event_handler(uint32_t opcode, return; } - ret = msm_adsp_inform_mixer_ctl(rtd, DSP_STREAM_CALLBACK, - payload); + ret = msm_adsp_inform_mixer_ctl(rtd, payload); if (ret) { pr_err("%s: failed to inform mixer ctl. err = %d\n", __func__, ret); @@ -691,6 +690,7 @@ static int msm_pcm_open(struct snd_pcm_substream *substream) prtd->set_channel_map = false; prtd->reset_event = false; runtime->private_data = prtd; + msm_adsp_init_mixer_ctl_pp_event_queue(soc_prtd); return 0; } @@ -833,6 +833,7 @@ static int msm_pcm_playback_close(struct snd_pcm_substream *substream) } msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id, SNDRV_PCM_STREAM_PLAYBACK); + msm_adsp_clean_mixer_ctl_pp_event_queue(soc_prtd); kfree(prtd); runtime->private_data = NULL; @@ -1187,7 +1188,6 @@ static int msm_pcm_add_audio_adsp_stream_callback_control( .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .info = msm_adsp_stream_callback_info, .get = msm_adsp_stream_callback_get, - .put = msm_adsp_stream_callback_put, .private_value = 0, } }; @@ -1231,6 +1231,7 @@ static int msm_pcm_add_audio_adsp_stream_callback_control( } kctl->private_data = NULL; + free_mixer_str: kfree(mixer_str); done: diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-devdep.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-devdep.c index 3263a5786290..1dc7d7218d42 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-devdep.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-devdep.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014, 2017 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,6 @@ #include <linux/module.h> #include <sound/hwdep.h> #include <sound/devdep_params.h> -#include <sound/msm-dts-eagle.h> - #include "msm-pcm-routing-devdep.h" #include "msm-ds2-dap-config.h" @@ -55,23 +53,6 @@ static int msm_pcm_routing_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, case SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER: ret = msm_ds2_dap_ioctl(hw, file, cmd, argp); break; - case DTS_EAGLE_IOCTL_GET_CACHE_SIZE: - case DTS_EAGLE_IOCTL_SET_CACHE_SIZE: - case DTS_EAGLE_IOCTL_GET_PARAM: - case DTS_EAGLE_IOCTL_SET_PARAM: - case DTS_EAGLE_IOCTL_SET_CACHE_BLOCK: - case DTS_EAGLE_IOCTL_SET_ACTIVE_DEVICE: - case DTS_EAGLE_IOCTL_GET_LICENSE: - case DTS_EAGLE_IOCTL_SET_LICENSE: - case DTS_EAGLE_IOCTL_SEND_LICENSE: - case DTS_EAGLE_IOCTL_SET_VOLUME_COMMANDS: - ret = msm_dts_eagle_ioctl(cmd, arg); - if (ret == -EPERM) { - pr_err("%s called with invalid control 0x%X\n", - __func__, cmd); - ret = -EINVAL; - } - break; default: pr_err("%s called with invalid control 0x%X\n", __func__, cmd); ret = -EINVAL; @@ -83,7 +64,6 @@ static int msm_pcm_routing_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, void msm_pcm_routing_hwdep_free(struct snd_pcm *pcm) { pr_debug("%s\n", __func__); - msm_dts_eagle_pcm_free(pcm); } #ifdef CONFIG_COMPAT @@ -107,23 +87,6 @@ static int msm_pcm_routing_hwdep_compat_ioctl(struct snd_hwdep *hw, case SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER32: ret = msm_ds2_dap_compat_ioctl(hw, file, cmd, argp); break; - case DTS_EAGLE_IOCTL_GET_CACHE_SIZE32: - case DTS_EAGLE_IOCTL_SET_CACHE_SIZE32: - case DTS_EAGLE_IOCTL_GET_PARAM32: - case DTS_EAGLE_IOCTL_SET_PARAM32: - case DTS_EAGLE_IOCTL_SET_CACHE_BLOCK32: - case DTS_EAGLE_IOCTL_SET_ACTIVE_DEVICE32: - case DTS_EAGLE_IOCTL_GET_LICENSE32: - case DTS_EAGLE_IOCTL_SET_LICENSE32: - case DTS_EAGLE_IOCTL_SEND_LICENSE32: - case DTS_EAGLE_IOCTL_SET_VOLUME_COMMANDS32: - ret = msm_dts_eagle_compat_ioctl(cmd, arg); - if (ret == -EPERM) { - pr_err("%s called with invalid control 0x%X\n", - __func__, cmd); - ret = -EINVAL; - } - break; default: pr_err("%s called with invalid control 0x%X\n", __func__, cmd); ret = -EINVAL; @@ -169,6 +132,6 @@ int msm_pcm_routing_hwdep_new(struct snd_soc_pcm_runtime *runtime, #ifdef CONFIG_COMPAT hwdep->ops.ioctl_compat = msm_pcm_routing_hwdep_compat_ioctl; #endif - return msm_dts_eagle_pcm_new(runtime); + return rc; } #endif diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c index f461f360d88d..0f63fd6bbd00 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c @@ -33,7 +33,6 @@ #include <sound/pcm_params.h> #include <sound/q6core.h> #include <sound/audio_cal_utils.h> -#include <sound/msm-dts-eagle.h> #include <sound/audio_effects.h> #include <sound/hwdep.h> @@ -208,10 +207,6 @@ static void msm_pcm_routing_cfg_pp(int port_id, int copp_idx, int topology, __func__, topology, port_id, rc); } break; - case ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX: - pr_debug("%s: DTS_EAGLE_COPP_TOPOLOGY_ID\n", __func__); - msm_dts_eagle_init_post(port_id, copp_idx); - break; case ADM_CMD_COPP_OPEN_TOPOLOGY_ID_AUDIOSPHERE: pr_debug("%s: TOPOLOGY_ID_AUDIOSPHERE\n", __func__); rc = msm_qti_pp_asphere_init(port_id, copp_idx); @@ -246,10 +241,6 @@ static void msm_pcm_routing_deinit_pp(int port_id, int topology) msm_dolby_dap_deinit(port_id); } break; - case ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX: - pr_debug("%s: DTS_EAGLE_COPP_TOPOLOGY_ID\n", __func__); - msm_dts_eagle_deinit_post(port_id, topology); - break; case ADM_CMD_COPP_OPEN_TOPOLOGY_ID_AUDIOSPHERE: pr_debug("%s: TOPOLOGY_ID_AUDIOSPHERE\n", __func__); msm_qti_pp_asphere_deinit(port_id); @@ -8124,6 +8115,9 @@ static const struct snd_kcontrol_new primary_mi2s_rx_port_mixer_controls[] = { SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_PRI_MI2S_RX, MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SLIM_8_TX", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), }; static const struct snd_kcontrol_new usb_rx_port_mixer_controls[] = { @@ -8154,6 +8148,9 @@ static const struct snd_kcontrol_new quat_mi2s_rx_port_mixer_controls[] = { SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SLIM_8_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), }; static const struct snd_kcontrol_new pri_tdm_rx_0_port_mixer_controls[] = { @@ -9244,6 +9241,9 @@ static const struct snd_kcontrol_new tert_mi2s_rx_port_mixer_controls[] = { SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SLIM_8_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), }; static const struct snd_kcontrol_new sec_mi2s_rx_port_mixer_controls[] = { @@ -9265,6 +9265,9 @@ static const struct snd_kcontrol_new sec_mi2s_rx_port_mixer_controls[] = { SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SLIM_8_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), }; static const struct snd_kcontrol_new lsm1_mixer_controls[] = { @@ -13890,6 +13893,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"PRI_MI2S_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, {"PRI_MI2S_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, {"PRI_MI2S_RX Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"PRI_MI2S_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, {"PRI_MI2S_RX", NULL, "PRI_MI2S_RX Port Mixer"}, {"SEC_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, @@ -13898,6 +13902,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"SEC_MI2S_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, {"SEC_MI2S_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, {"SEC_MI2S_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"SEC_MI2S_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, {"SEC_MI2S_RX", NULL, "SEC_MI2S_RX Port Mixer"}, {"TERT_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, @@ -13905,6 +13910,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"TERT_MI2S_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, {"TERT_MI2S_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, {"TERT_MI2S_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"TERT_MI2S_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, {"TERT_MI2S_RX", NULL, "TERT_MI2S_RX Port Mixer"}, {"QUAT_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, @@ -13914,6 +13920,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUAT_MI2S_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, {"QUAT_MI2S_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, {"QUAT_MI2S_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"QUAT_MI2S_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, {"QUAT_MI2S_RX", NULL, "QUAT_MI2S_RX Port Mixer"}, /* Backend Enablement */ @@ -14116,6 +14123,7 @@ static int msm_pcm_routing_close(struct snd_pcm_substream *substream) bedai->active = 0; bedai->sample_rate = 0; bedai->channel = 0; + bedai->passthr_mode = LEGACY_PCM; mutex_unlock(&routing_lock); return 0; @@ -14685,8 +14693,6 @@ static int msm_routing_probe(struct snd_soc_platform *platform) msm_routing_be_dai_name_table_mixer_controls, ARRAY_SIZE(msm_routing_be_dai_name_table_mixer_controls)); - msm_dts_eagle_add_controls(platform); - snd_soc_add_platform_controls(platform, msm_source_tracking_controls, ARRAY_SIZE(msm_source_tracking_controls)); snd_soc_add_platform_controls(platform, adm_channel_config_controls, diff --git a/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c index d4e78604f868..cac28f43e5ae 100644 --- a/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c +++ b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c @@ -45,21 +45,6 @@ enum { EQ_BAND_MAX, }; -struct msm_audio_eq_band { - uint16_t band_idx; /* The band index, 0 .. 11 */ - uint32_t filter_type; /* Filter band type */ - uint32_t center_freq_hz; /* Filter band center frequency */ - uint32_t filter_gain; /* Filter band initial gain (dB) */ - /* Range is +12 dB to -12 dB with 1dB increments. */ - uint32_t q_factor; -} __packed; - -struct msm_audio_eq_stream_config { - uint32_t enable; /* Number of consequtive bands specified */ - uint32_t num_bands; - struct msm_audio_eq_band eq_bands[EQ_BAND_MAX]; -} __packed; - /* Audio Sphere data structures */ struct msm_audio_pp_asphere_state_s { uint32_t enabled; @@ -817,18 +802,133 @@ static int msm_qti_pp_asphere_set(struct snd_kcontrol *kcontrol, return 0; } +int msm_adsp_init_mixer_ctl_pp_event_queue(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_kcontrol *kctl; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + const char *mixer_ctl_name = DSP_STREAM_CALLBACK; + struct dsp_stream_callback_prtd *kctl_prtd = NULL; + + if (!rtd) { + pr_err("%s: rtd is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -EINVAL; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, + rtd->pcm->device); + kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str); + kfree(mixer_str); + if (!kctl) { + pr_err("%s: failed to get kctl.\n", __func__); + ret = -EINVAL; + goto done; + } + + if (kctl->private_data != NULL) { + pr_err("%s: kctl_prtd is not NULL at initialization.\n", + __func__); + return -EINVAL; + } + + kctl_prtd = kzalloc(sizeof(struct dsp_stream_callback_prtd), + GFP_KERNEL); + if (!kctl_prtd) { + ret = -ENOMEM; + goto done; + } + + spin_lock_init(&kctl_prtd->prtd_spin_lock); + INIT_LIST_HEAD(&kctl_prtd->event_queue); + kctl_prtd->event_count = 0; + kctl->private_data = kctl_prtd; + +done: + return ret; +} + +int msm_adsp_clean_mixer_ctl_pp_event_queue(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_kcontrol *kctl; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + struct dsp_stream_callback_list *node, *n; + unsigned long spin_flags; + const char *mixer_ctl_name = DSP_STREAM_CALLBACK; + struct dsp_stream_callback_prtd *kctl_prtd = NULL; + + if (!rtd) { + pr_err("%s: rtd is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -EINVAL; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, + rtd->pcm->device); + kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str); + kfree(mixer_str); + if (!kctl) { + pr_err("%s: failed to get kctl.\n", __func__); + ret = -EINVAL; + goto done; + } + + kctl_prtd = (struct dsp_stream_callback_prtd *) + kctl->private_data; + if (kctl_prtd != NULL) { + spin_lock_irqsave(&kctl_prtd->prtd_spin_lock, spin_flags); + /* clean the queue */ + list_for_each_entry_safe(node, n, + &kctl_prtd->event_queue, list) { + list_del(&node->list); + kctl_prtd->event_count--; + pr_debug("%s: %d remaining events after del.\n", + __func__, kctl_prtd->event_count); + kfree(node); + } + spin_unlock_irqrestore(&kctl_prtd->prtd_spin_lock, spin_flags); + } + + kfree(kctl_prtd); + kctl->private_data = NULL; + +done: + return ret; +} int msm_adsp_inform_mixer_ctl(struct snd_soc_pcm_runtime *rtd, - const char *mixer_ctl_name, uint32_t *payload) { /* adsp pp event notifier */ struct snd_kcontrol *kctl; struct snd_ctl_elem_value control; - uint32_t payload_size = 0; const char *deviceNo = "NN"; char *mixer_str = NULL; int ctl_len = 0, ret = 0; + struct dsp_stream_callback_list *new_event; + struct dsp_stream_callback_list *oldest_event; + unsigned long spin_flags; + struct dsp_stream_callback_prtd *kctl_prtd = NULL; + struct msm_adsp_event_data *event_data = NULL; + const char *mixer_ctl_name = DSP_STREAM_CALLBACK; + struct snd_ctl_elem_info kctl_info; if (!rtd || !payload) { pr_err("%s: %s is NULL\n", __func__, @@ -837,6 +937,12 @@ int msm_adsp_inform_mixer_ctl(struct snd_soc_pcm_runtime *rtd, goto done; } + if (rtd->card->snd_card == NULL) { + pr_err("%s: snd_card is null.\n", __func__); + ret = -EINVAL; + goto done; + } + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; mixer_str = kzalloc(ctl_len, GFP_ATOMIC); if (!mixer_str) { @@ -854,21 +960,59 @@ int msm_adsp_inform_mixer_ctl(struct snd_soc_pcm_runtime *rtd, goto done; } - control.id = kctl->id; - payload_size = payload[0]; - /* Copy complete payload */ - memcpy(control.value.bytes.data, (void *)payload, - sizeof(payload_size) + payload_size); - kctl->put(kctl, &control); - if (rtd->card->snd_card == NULL) { - pr_err("%s: snd_card is null.\n", __func__); + event_data = (struct msm_adsp_event_data *)payload; + kctl->info(kctl, &kctl_info); + if (sizeof(struct msm_adsp_event_data) + + event_data->payload_len > kctl_info.count) { + pr_err("%s: payload length exceeds limit of %u bytes.\n", + __func__, kctl_info.count); ret = -EINVAL; goto done; } + kctl_prtd = (struct dsp_stream_callback_prtd *) + kctl->private_data; + if (kctl_prtd == NULL) { + /* queue is not initialized */ + ret = -EINVAL; + pr_err("%s: event queue is not initialized.\n", __func__); + goto done; + } + + new_event = kzalloc(sizeof(struct dsp_stream_callback_list) + + event_data->payload_len, + GFP_ATOMIC); + if (new_event == NULL) { + ret = -ENOMEM; + goto done; + } + memcpy((void *)&new_event->event, (void *)payload, + event_data->payload_len + + sizeof(struct msm_adsp_event_data)); + + spin_lock_irqsave(&kctl_prtd->prtd_spin_lock, spin_flags); + while (kctl_prtd->event_count >= DSP_STREAM_CALLBACK_QUEUE_SIZE) { + pr_info("%s: queue of size %d is full. delete oldest one.\n", + __func__, DSP_STREAM_CALLBACK_QUEUE_SIZE); + oldest_event = list_first_entry(&kctl_prtd->event_queue, + struct dsp_stream_callback_list, list); + pr_info("%s: event deleted: type %d length %d\n", + __func__, oldest_event->event.event_type, + oldest_event->event.payload_len); + list_del(&oldest_event->list); + kctl_prtd->event_count--; + kfree(oldest_event); + } + + list_add_tail(&new_event->list, &kctl_prtd->event_queue); + kctl_prtd->event_count++; + spin_unlock_irqrestore(&kctl_prtd->prtd_spin_lock, spin_flags); + + control.id = kctl->id; snd_ctl_notify(rtd->card->snd_card, SNDRV_CTL_EVENT_MASK_INFO, &control.id); + done: return ret; } @@ -877,44 +1021,8 @@ int msm_adsp_stream_cmd_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; - uinfo->count = 512; - - return 0; -} - -int msm_adsp_stream_callback_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - uint32_t payload_size = 0, last_payload_size = 0; - - /* fetch payload size in first four bytes */ - memcpy(&payload_size, ucontrol->value.bytes.data, sizeof(uint32_t)); - - if (kcontrol->private_data == NULL) { - /* buffer is empty */ - kcontrol->private_data = - kzalloc(payload_size + sizeof(payload_size), - GFP_ATOMIC); - if (kcontrol->private_data == NULL) - return -ENOMEM; - } else { - memcpy(&last_payload_size, kcontrol->private_data, - sizeof(uint32_t)); - if (last_payload_size < payload_size) { - /* new payload size exceeds old one. - * reallocate buffer - */ - kfree(kcontrol->private_data); - kcontrol->private_data = - kzalloc(payload_size + sizeof(payload_size), - GFP_ATOMIC); - if (kcontrol->private_data == NULL) - return -ENOMEM; - } - } - - memcpy(kcontrol->private_data, ucontrol->value.bytes.data, - sizeof(uint32_t) + payload_size); + uinfo->count = + sizeof(((struct snd_ctl_elem_value *)0)->value.bytes.data); return 0; } @@ -923,26 +1031,53 @@ int msm_adsp_stream_callback_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { uint32_t payload_size = 0; + struct dsp_stream_callback_list *oldest_event; + unsigned long spin_flags; + struct dsp_stream_callback_prtd *kctl_prtd = NULL; + int ret = 0; - if (kcontrol->private_data == NULL) { - pr_err("%s: ASM Stream PP Event Data Unavailable\n", __func__); - return -EINVAL; + kctl_prtd = (struct dsp_stream_callback_prtd *) + kcontrol->private_data; + if (kctl_prtd == NULL) { + pr_err("%s: ASM Stream PP event queue is not initialized.\n", + __func__); + ret = -EINVAL; + goto done; + } + + spin_lock_irqsave(&kctl_prtd->prtd_spin_lock, spin_flags); + pr_debug("%s: %d events in queue.\n", __func__, kctl_prtd->event_count); + if (list_empty(&kctl_prtd->event_queue)) { + pr_err("%s: ASM Stream PP event queue is empty.\n", __func__); + ret = -EINVAL; + spin_unlock_irqrestore(&kctl_prtd->prtd_spin_lock, spin_flags); + goto done; } - memcpy(&payload_size, kcontrol->private_data, sizeof(uint32_t)); - memcpy(ucontrol->value.bytes.data, kcontrol->private_data, - sizeof(uint32_t) + payload_size); - kfree(kcontrol->private_data); - kcontrol->private_data = NULL; + oldest_event = list_first_entry(&kctl_prtd->event_queue, + struct dsp_stream_callback_list, list); + list_del(&oldest_event->list); + kctl_prtd->event_count--; + spin_unlock_irqrestore(&kctl_prtd->prtd_spin_lock, spin_flags); - return 0; + payload_size = oldest_event->event.payload_len; + pr_debug("%s: event fetched: type %d length %d\n", + __func__, oldest_event->event.event_type, + oldest_event->event.payload_len); + memcpy(ucontrol->value.bytes.data, &oldest_event->event, + sizeof(struct msm_adsp_event_data) + payload_size); + kfree(oldest_event); + +done: + return ret; } int msm_adsp_stream_callback_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; - uinfo->count = 512; + uinfo->count = + sizeof(((struct snd_ctl_elem_value *)0)->value.bytes.data); return 0; } diff --git a/sound/soc/msm/qdsp6v2/msm-qti-pp-config.h b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.h index 70ce20fbd8f8..6ffcbdbd543e 100644 --- a/sound/soc/msm/qdsp6v2/msm-qti-pp-config.h +++ b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.h @@ -14,12 +14,11 @@ #include <sound/soc.h> int msm_adsp_inform_mixer_ctl(struct snd_soc_pcm_runtime *rtd, - const char *mixer_ctl_name, uint32_t *payload); +int msm_adsp_init_mixer_ctl_pp_event_queue(struct snd_soc_pcm_runtime *rtd); +int msm_adsp_clean_mixer_ctl_pp_event_queue(struct snd_soc_pcm_runtime *rtd); int msm_adsp_stream_cmd_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); -int msm_adsp_stream_callback_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); int msm_adsp_stream_callback_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int msm_adsp_stream_callback_info(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c index f026e82a73cc..9f00e1bc4a3d 100644 --- a/sound/soc/msm/qdsp6v2/q6adm.c +++ b/sound/soc/msm/qdsp6v2/q6adm.c @@ -24,7 +24,6 @@ #include <sound/q6afe-v2.h> #include <sound/audio_cal_utils.h> #include <sound/asound.h> -#include <sound/msm-dts-eagle.h> #include "msm-dts-srs-tm-config.h" #include <sound/adsp_err.h> @@ -262,222 +261,6 @@ static int adm_get_next_available_copp(int port_idx) return idx; } -int adm_dts_eagle_set(int port_id, int copp_idx, int param_id, - void *data, uint32_t size) -{ - struct adm_cmd_set_pp_params_v5 admp; - int p_idx, ret = 0, *ob_params; - - pr_debug("DTS_EAGLE_ADM: %s - port id %i, copp idx %i, param id 0x%X size %u\n", - __func__, port_id, copp_idx, param_id, size); - - port_id = afe_convert_virtual_to_portid(port_id); - p_idx = adm_validate_and_get_port_index(port_id); - pr_debug("DTS_EAGLE_ADM: %s - after lookup, port id %i, port idx %i\n", - __func__, port_id, p_idx); - - if (p_idx < 0) { - pr_err("DTS_EAGLE_ADM: %s: invalid port index 0x%x, port id 0x%x\n", - __func__, p_idx, port_id); - return -EINVAL; - } - - if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { - pr_err("DTS_EAGLE_ADM: %s: Invalid copp_idx: %d\n", __func__, - copp_idx); - return -EINVAL; - } - - ob_params = (int *)this_adm.outband_memmap.kvaddr; - if (ob_params == NULL) { - pr_err("DTS_EAGLE_ADM: %s - NULL memmap. Non Eagle topology selected?\n", - __func__); - ret = -EINVAL; - goto fail_cmd; - } - /* check for integer overflow */ - if (size > (UINT_MAX - APR_CMD_OB_HDR_SZ)) - ret = -EINVAL; - if ((ret < 0) || - (size + APR_CMD_OB_HDR_SZ > this_adm.outband_memmap.size)) { - pr_err("DTS_EAGLE_ADM - %s: ion alloc of size %zu too small for size requested %u\n", - __func__, this_adm.outband_memmap.size, - size + APR_CMD_OB_HDR_SZ); - ret = -EINVAL; - goto fail_cmd; - } - *ob_params++ = AUDPROC_MODULE_ID_DTS_HPX_POSTMIX; - *ob_params++ = param_id; - *ob_params++ = size; - memcpy(ob_params, data, size); - - admp.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, - APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); - admp.hdr.pkt_size = sizeof(admp); - admp.hdr.src_svc = APR_SVC_ADM; - admp.hdr.src_domain = APR_DOMAIN_APPS; - admp.hdr.src_port = port_id; - admp.hdr.dest_svc = APR_SVC_ADM; - admp.hdr.dest_domain = APR_DOMAIN_ADSP; - admp.hdr.dest_port = atomic_read(&this_adm.copp.id[p_idx][copp_idx]); - admp.hdr.token = p_idx << 16 | copp_idx; - admp.hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5; - admp.payload_addr_lsw = lower_32_bits(this_adm.outband_memmap.paddr); - admp.payload_addr_msw = msm_audio_populate_upper_32_bits( - this_adm.outband_memmap.paddr); - admp.mem_map_handle = atomic_read(&this_adm.mem_map_handles[ - ADM_DTS_EAGLE]); - admp.payload_size = size + sizeof(struct adm_param_data_v5); - - pr_debug("DTS_EAGLE_ADM: %s - Command was sent now check Q6 - port id = %d, size %d, module id %x, param id %x.\n", - __func__, admp.hdr.dest_port, - admp.payload_size, AUDPROC_MODULE_ID_DTS_HPX_POSTMIX, - param_id); - atomic_set(&this_adm.copp.stat[p_idx][copp_idx], -1); - ret = apr_send_pkt(this_adm.apr, (uint32_t *)&admp); - if (ret < 0) { - pr_err("DTS_EAGLE_ADM: %s - ADM enable for port %d failed\n", - __func__, port_id); - ret = -EINVAL; - goto fail_cmd; - } - ret = wait_event_timeout(this_adm.copp.wait[p_idx][copp_idx], - atomic_read(&this_adm.copp.stat - [p_idx][copp_idx]) >= 0, - msecs_to_jiffies(TIMEOUT_MS)); - if (!ret) { - pr_err("DTS_EAGLE_ADM: %s - set params timed out port = %d\n", - __func__, port_id); - ret = -EINVAL; - } else if (atomic_read(&this_adm.copp.stat - [p_idx][copp_idx]) > 0) { - pr_err("%s: DSP returned error[%s]\n", - __func__, adsp_err_get_err_str( - atomic_read(&this_adm.copp.stat - [p_idx][copp_idx]))); - ret = adsp_err_get_lnx_err_code( - atomic_read(&this_adm.copp.stat - [p_idx][copp_idx])); - } else { - ret = 0; - } - -fail_cmd: - return ret; -} - -int adm_dts_eagle_get(int port_id, int copp_idx, int param_id, - void *data, uint32_t size) -{ - struct adm_cmd_get_pp_params_v5 admp; - int p_idx, ret = 0, *ob_params; - uint32_t orig_size = size; - pr_debug("DTS_EAGLE_ADM: %s - port id %i, copp idx %i, param id 0x%X\n", - __func__, port_id, copp_idx, param_id); - - port_id = afe_convert_virtual_to_portid(port_id); - p_idx = adm_validate_and_get_port_index(port_id); - if (p_idx < 0) { - pr_err("DTS_EAGLE_ADM: %s - invalid port index %i, port id %i, copp idx %i\n", - __func__, p_idx, port_id, copp_idx); - return -EINVAL; - } - - if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { - pr_err("DTS_EAGLE_ADM: %s: Invalid copp_idx: %d\n", __func__, - copp_idx); - return -EINVAL; - } - - if ((size == 0) || !data) { - pr_err("DTS_EAGLE_ADM: %s - invalid size %u or pointer %pK.\n", - __func__, size, data); - return -EINVAL; - } - - size = (size+3) & 0xFFFFFFFC; - - ob_params = (int *)(this_adm.outband_memmap.kvaddr); - if (ob_params == NULL) { - pr_err("DTS_EAGLE_ADM: %s - NULL memmap. Non Eagle topology selected?", - __func__); - ret = -EINVAL; - goto fail_cmd; - } - /* check for integer overflow */ - if (size > (UINT_MAX - APR_CMD_OB_HDR_SZ)) - ret = -EINVAL; - if ((ret < 0) || - (size + APR_CMD_OB_HDR_SZ > this_adm.outband_memmap.size)) { - pr_err("DTS_EAGLE_ADM - %s: ion alloc of size %zu too small for size requested %u\n", - __func__, this_adm.outband_memmap.size, - size + APR_CMD_OB_HDR_SZ); - ret = -EINVAL; - goto fail_cmd; - } - *ob_params++ = AUDPROC_MODULE_ID_DTS_HPX_POSTMIX; - *ob_params++ = param_id; - *ob_params++ = size; - - admp.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, - APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); - admp.hdr.pkt_size = sizeof(admp); - admp.hdr.src_svc = APR_SVC_ADM; - admp.hdr.src_domain = APR_DOMAIN_APPS; - admp.hdr.src_port = port_id; - admp.hdr.dest_svc = APR_SVC_ADM; - admp.hdr.dest_domain = APR_DOMAIN_ADSP; - admp.hdr.dest_port = atomic_read(&this_adm.copp.id[p_idx][copp_idx]); - admp.hdr.token = p_idx << 16 | copp_idx; - admp.hdr.opcode = ADM_CMD_GET_PP_PARAMS_V5; - admp.data_payload_addr_lsw = - lower_32_bits(this_adm.outband_memmap.paddr); - admp.data_payload_addr_msw = - msm_audio_populate_upper_32_bits( - this_adm.outband_memmap.paddr); - admp.mem_map_handle = atomic_read(&this_adm.mem_map_handles[ - ADM_DTS_EAGLE]); - admp.module_id = AUDPROC_MODULE_ID_DTS_HPX_POSTMIX; - admp.param_id = param_id; - admp.param_max_size = size + sizeof(struct adm_param_data_v5); - admp.reserved = 0; - - atomic_set(&this_adm.copp.stat[p_idx][copp_idx], -1); - - ret = apr_send_pkt(this_adm.apr, (uint32_t *)&admp); - if (ret < 0) { - pr_err("DTS_EAGLE_ADM: %s - Failed to get EAGLE Params on port %d\n", - __func__, port_id); - ret = -EINVAL; - goto fail_cmd; - } - ret = wait_event_timeout(this_adm.copp.wait[p_idx][copp_idx], - atomic_read(&this_adm.copp.stat - [p_idx][copp_idx]) >= 0, - msecs_to_jiffies(TIMEOUT_MS)); - if (!ret) { - pr_err("DTS_EAGLE_ADM: %s - EAGLE get params timed out port = %d\n", - __func__, port_id); - ret = -EINVAL; - goto fail_cmd; - } else if (atomic_read(&this_adm.copp.stat - [p_idx][copp_idx]) > 0) { - pr_err("%s: DSP returned error[%s]\n", - __func__, adsp_err_get_err_str( - atomic_read(&this_adm.copp.stat - [p_idx][copp_idx]))); - ret = adsp_err_get_lnx_err_code( - atomic_read(&this_adm.copp.stat - [p_idx][copp_idx])); - goto fail_cmd; - } - - memcpy(data, ob_params, orig_size); - ret = 0; -fail_cmd: - return ret; -} - int srs_trumedia_open(int port_id, int copp_idx, __s32 srs_tech_id, void *srs_params) { @@ -2338,13 +2121,6 @@ int adm_open(int port_id, int path, int rate, int channel_mode, int topology, __func__, port_id, path, rate, channel_mode, perf_mode, topology); - /* For DTS EAGLE only, force 24 bit */ - if ((topology == ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX) && - (perf_mode == LEGACY_PCM_MODE)) { - bit_width = 24; - pr_debug("%s: Force open adm in 24-bit for DTS HPX topology 0x%x\n", - __func__, topology); - } port_id = q6audio_convert_virtual_to_portid(port_id); port_idx = adm_validate_and_get_port_index(port_id); if (port_idx < 0) { @@ -2366,8 +2142,7 @@ int adm_open(int port_id, int path, int rate, int channel_mode, int topology, flags = ADM_ULL_POST_PROCESSING_DEVICE_SESSION; if ((topology == DOLBY_ADM_COPP_TOPOLOGY_ID) || (topology == DS2_ADM_COPP_TOPOLOGY_ID) || - (topology == SRS_TRUMEDIA_TOPOLOGY_ID) || - (topology == ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX)) + (topology == SRS_TRUMEDIA_TOPOLOGY_ID)) topology = DEFAULT_COPP_TOPOLOGY; } else if (perf_mode == ULTRA_LOW_LATENCY_PCM_MODE) { flags = ADM_ULTRA_LOW_LATENCY_DEVICE_SESSION; @@ -2378,8 +2153,7 @@ int adm_open(int port_id, int path, int rate, int channel_mode, int topology, flags = ADM_LOW_LATENCY_DEVICE_SESSION; if ((topology == DOLBY_ADM_COPP_TOPOLOGY_ID) || (topology == DS2_ADM_COPP_TOPOLOGY_ID) || - (topology == SRS_TRUMEDIA_TOPOLOGY_ID) || - (topology == ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX)) + (topology == SRS_TRUMEDIA_TOPOLOGY_ID)) topology = DEFAULT_COPP_TOPOLOGY; } else { if (path == ADM_PATH_COMPRESSED_RX) @@ -2449,21 +2223,6 @@ int adm_open(int port_id, int path, int rate, int channel_mode, int topology, (uint32_t)this_adm.outband_memmap.size); } } - if ((topology == ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX) && - (perf_mode == LEGACY_PCM_MODE)) { - int res = 0; - atomic_set(&this_adm.mem_map_index, ADM_DTS_EAGLE); - msm_dts_ion_memmap(&this_adm.outband_memmap); - res = adm_memory_map_regions( - &this_adm.outband_memmap.paddr, - 0, - (uint32_t *)&this_adm.outband_memmap.size, - 1); - if (res < 0) - pr_err("%s: DTS_EAGLE mmap did not work!", - __func__); - } - memset(&open, 0, sizeof(struct adm_cmd_device_open_v5)); open.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); @@ -2809,10 +2568,6 @@ int adm_matrix_map(int path, struct route_payload payload_map, int perf_mode, __func__, port_idx, copp_idx); continue; } - if (atomic_read( - &this_adm.copp.topology[port_idx][copp_idx]) == - ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX) - continue; rtac_add_adm_device(payload_map.port_id[i], atomic_read(&this_adm.copp.id [port_idx][copp_idx]), @@ -2926,21 +2681,6 @@ int adm_close(int port_id, int perf_mode, int copp_idx) } } - if ((perf_mode == LEGACY_PCM_MODE) && - (this_adm.outband_memmap.paddr != 0) && - (atomic_read( - &this_adm.copp.topology[port_idx][copp_idx]) == - ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX)) { - atomic_set(&this_adm.mem_map_index, ADM_DTS_EAGLE); - ret = adm_memory_unmap_regions(); - if (ret < 0) { - pr_err("%s: adm mem unmmap err %d", - __func__, ret); - } else { - atomic_set(&this_adm.mem_map_handles - [ADM_DTS_EAGLE], 0); - } - } if ((afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_TX) && this_adm.sourceTrackingData.memmap.paddr) { @@ -3412,10 +3152,6 @@ static int adm_init_cal_data(void) {NULL, NULL, NULL, NULL, NULL, NULL} }, {NULL, NULL, cal_utils_match_buf_num} }, - {{DTS_EAGLE_CAL_TYPE, - {NULL, NULL, NULL, NULL, NULL, NULL} }, - {NULL, NULL, cal_utils_match_buf_num} }, - {{SRS_TRUMEDIA_CAL_TYPE, {NULL, NULL, NULL, NULL, NULL, NULL} }, {NULL, NULL, cal_utils_match_buf_num} }, diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c index a9241cfdfec7..4aebcb6fa77e 100644 --- a/sound/soc/msm/qdsp6v2/q6asm.c +++ b/sound/soc/msm/qdsp6v2/q6asm.c @@ -38,8 +38,8 @@ #include <sound/q6asm-v2.h> #include <sound/q6audio-v2.h> #include <sound/audio_cal_utils.h> -#include <sound/msm-dts-eagle.h> #include <sound/adsp_err.h> +#include <sound/compress_params.h> #define TRUE 0x01 #define FALSE 0x00 @@ -1031,12 +1031,14 @@ void q6asm_audio_client_free(struct audio_client *ac) } rtac_set_asm_handle(ac->session, NULL); - apr_deregister(ac->apr2); - apr_deregister(ac->apr); - q6asm_mmap_apr_dereg(); - ac->apr2 = NULL; - ac->apr = NULL; - ac->mmap_apr = NULL; + if (!atomic_read(&ac->reset)) { + apr_deregister(ac->apr2); + apr_deregister(ac->apr); + q6asm_mmap_apr_dereg(); + ac->apr2 = NULL; + ac->apr = NULL; + ac->mmap_apr = NULL; + } q6asm_session_free(ac); pr_debug("%s: APR De-Register\n", __func__); @@ -1507,7 +1509,6 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv) } pr_debug("%s: Clearing custom topology\n", __func__); } - this_mmap.apr = NULL; cal_utils_clear_cal_block_q6maps(ASM_MAX_CAL_TYPES, cal_data); common_client.mmap_apr = NULL; @@ -1674,7 +1675,7 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) int32_t ret = 0; union asm_token_struct asm_token; uint8_t buf_index; - char *pp_event_package = NULL; + struct msm_adsp_event_data *pp_event_package = NULL; uint32_t payload_size = 0; if (ac == NULL) { @@ -1708,8 +1709,10 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) if (data->opcode == RESET_EVENTS) { mutex_lock(&ac->cmd_lock); atomic_set(&ac->reset, 1); - if (ac->apr == NULL) + if (ac->apr == NULL) { ac->apr = ac->apr2; + ac->apr2 = NULL; + } pr_debug("%s: Reset event is received: %d %d apr[%pK]\n", __func__, data->reset_event, data->reset_proc, ac->apr); @@ -2043,22 +2046,29 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) pr_debug("%s: ASM_STREAM_PP_EVENT payload[0][0x%x] payload[1][0x%x]", __func__, payload[0], payload[1]); /* repack payload for asm_stream_pp_event - * package is composed of size + actual payload + * package is composed of event type + size + actual payload */ payload_size = data->payload_size; - pp_event_package = - kzalloc(payload_size + sizeof(payload_size), + pp_event_package = kzalloc(payload_size + + sizeof(struct msm_adsp_event_data), GFP_ATOMIC); if (!pp_event_package) return -ENOMEM; - memcpy((void *)pp_event_package, - &payload_size, sizeof(payload_size)); - memcpy((void *)pp_event_package + sizeof(payload_size), + + pp_event_package->event_type = ASM_STREAM_PP_EVENT; + pp_event_package->payload_len = payload_size; + memcpy((void *)pp_event_package->payload, data->payload, payload_size); ac->cb(data->opcode, data->token, (void *)pp_event_package, ac->priv); kfree(pp_event_package); return 0; + case ASM_SESSION_CMDRSP_ADJUST_SESSION_CLOCK_V2: + pr_debug("%s: ASM_SESSION_CMDRSP_ADJUST_SESSION_CLOCK_V2 sesion %d status 0x%x msw %u lsw %u\n", + __func__, ac->session, payload[0], payload[2], + payload[1]); + wake_up(&ac->cmd_wait); + break; case ASM_SESSION_CMDRSP_GET_PATH_DELAY_V2: pr_debug("%s: ASM_SESSION_CMDRSP_GET_PATH_DELAY_V2 session %d status 0x%x msw %u lsw %u\n", __func__, ac->session, payload[0], payload[2], @@ -2425,9 +2435,6 @@ static int __q6asm_open_read(struct audio_client *ac, open.src_endpointype = ASM_END_POINT_DEVICE_MATRIX; open.preprocopo_id = q6asm_get_asm_topology_cal(); - if ((open.preprocopo_id == ASM_STREAM_POSTPROC_TOPO_ID_DTS_HPX) || - (open.preprocopo_id == ASM_STREAM_POSTPROC_TOPO_ID_HPX_PLUS)) - open.preprocopo_id = ASM_STREAM_POSTPROCOPO_ID_NONE; open.bits_per_sample = bits_per_sample; open.mode_flags = 0x0; @@ -2602,7 +2609,9 @@ int q6asm_open_write_compressed(struct audio_client *ac, uint32_t format, case FORMAT_GEN_COMPR: open.fmt_id = ASM_MEDIA_FMT_GENERIC_COMPRESSED; break; - + case FORMAT_TRUEHD: + open.fmt_id = ASM_MEDIA_FMT_TRUEHD; + break; default: pr_err("%s: Invalid format[%d]\n", __func__, format); rc = -EINVAL; @@ -2712,16 +2721,9 @@ static int __q6asm_open_write(struct audio_client *ac, uint32_t format, open.bits_per_sample = bits_per_sample; open.postprocopo_id = q6asm_get_asm_topology_cal(); - if ((ac->perf_mode != LEGACY_PCM_MODE) && - ((open.postprocopo_id == ASM_STREAM_POSTPROC_TOPO_ID_DTS_HPX) || - (open.postprocopo_id == ASM_STREAM_POSTPROC_TOPO_ID_HPX_PLUS))) + if (ac->perf_mode != LEGACY_PCM_MODE) open.postprocopo_id = ASM_STREAM_POSTPROCOPO_ID_NONE; - /* For DTS EAGLE only, force 24 bit */ - if ((open.postprocopo_id == ASM_STREAM_POSTPROC_TOPO_ID_DTS_HPX) || - (open.postprocopo_id == ASM_STREAM_POSTPROC_TOPO_ID_HPX_PLUS)) - open.bits_per_sample = 24; - pr_debug("%s: perf_mode %d asm_topology 0x%x bps %d\n", __func__, ac->perf_mode, open.postprocopo_id, open.bits_per_sample); @@ -2782,9 +2784,6 @@ static int __q6asm_open_write(struct audio_client *ac, uint32_t format, case FORMAT_GEN_COMPR: open.dec_fmt_id = ASM_MEDIA_FMT_GENERIC_COMPRESSED; break; - case FORMAT_TRUEHD: - open.dec_fmt_id = ASM_MEDIA_FMT_TRUEHD; - break; default: pr_err("%s: Invalid format 0x%x\n", __func__, format); rc = -EINVAL; @@ -2948,10 +2947,6 @@ static int __q6asm_open_read_write(struct audio_client *ac, uint32_t rd_format, ac->topology = open.postprocopo_id; ac->app_type = q6asm_get_asm_app_type_cal(); - /* For DTS EAGLE only, force 24 bit */ - if ((open.postprocopo_id == ASM_STREAM_POSTPROC_TOPO_ID_DTS_HPX) || - (open.postprocopo_id == ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER)) - open.bits_per_sample = 24; switch (wr_format) { case FORMAT_LINEAR_PCM: @@ -6756,233 +6751,6 @@ fail_cmd: return rc; } -int q6asm_dts_eagle_set(struct audio_client *ac, int param_id, uint32_t size, - void *data, struct param_outband *po, int m_id) -{ - int rc = 0, *ob_params = NULL; - uint32_t sz = sizeof(struct asm_dts_eagle_param) + (po ? 0 : size); - struct asm_dts_eagle_param *ad; - - if (!ac || ac->apr == NULL || (size == 0) || !data) { - pr_err("DTS_EAGLE_ASM - %s: APR handle NULL, invalid size %u or pointer %pK.\n", - __func__, size, data); - return -EINVAL; - } - - ad = kzalloc(sz, GFP_KERNEL); - if (!ad) { - pr_err("DTS_EAGLE_ASM - %s: error allocating mem of size %u\n", - __func__, sz); - return -ENOMEM; - } - pr_debug("DTS_EAGLE_ASM - %s: ac %pK param_id 0x%x size %u data %pK m_id 0x%x\n", - __func__, ac, param_id, size, data, m_id); - q6asm_add_hdr_async(ac, &ad->hdr, sz, 1); - ad->hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2; - ad->param.data_payload_addr_lsw = 0; - ad->param.data_payload_addr_msw = 0; - - ad->param.mem_map_handle = 0; - ad->param.data_payload_size = size + - sizeof(struct asm_stream_param_data_v2); - ad->data.module_id = m_id; - ad->data.param_id = param_id; - ad->data.param_size = size; - ad->data.reserved = 0; - atomic_set(&ac->cmd_state_pp, -1); - - if (po) { - struct list_head *ptr, *next; - struct asm_buffer_node *node; - pr_debug("DTS_EAGLE_ASM - %s: using out of band memory (virtual %pK, physical %lu)\n", - __func__, po->kvaddr, (long)po->paddr); - ad->param.data_payload_addr_lsw = lower_32_bits(po->paddr); - ad->param.data_payload_addr_msw = - msm_audio_populate_upper_32_bits(po->paddr); - list_for_each_safe(ptr, next, &ac->port[IN].mem_map_handle) { - node = list_entry(ptr, struct asm_buffer_node, list); - if (node->buf_phys_addr == po->paddr) { - ad->param.mem_map_handle = node->mmap_hdl; - break; - } - } - if (ad->param.mem_map_handle == 0) { - pr_err("DTS_EAGLE_ASM - %s: mem map handle not found\n", - __func__); - rc = -EINVAL; - goto fail_cmd; - } - /* check for integer overflow */ - if (size > (UINT_MAX - APR_CMD_OB_HDR_SZ)) - rc = -EINVAL; - if ((rc < 0) || (size + APR_CMD_OB_HDR_SZ > po->size)) { - pr_err("DTS_EAGLE_ASM - %s: ion alloc of size %zu too small for size requested %u\n", - __func__, po->size, size + APR_CMD_OB_HDR_SZ); - rc = -EINVAL; - goto fail_cmd; - } - ob_params = (int *)po->kvaddr; - *ob_params++ = m_id; - *ob_params++ = param_id; - *ob_params++ = size; - memcpy(ob_params, data, size); - } else { - pr_debug("DTS_EAGLE_ASM - %s: using in band\n", __func__); - memcpy(((char *)ad) + sizeof(struct asm_dts_eagle_param), - data, size); - } - rc = apr_send_pkt(ac->apr, (uint32_t *)ad); - if (rc < 0) { - pr_err("DTS_EAGLE_ASM - %s: set-params send failed paramid[0x%x]\n", - __func__, ad->data.param_id); - rc = -EINVAL; - goto fail_cmd; - } - - rc = wait_event_timeout(ac->cmd_wait, - (atomic_read(&ac->cmd_state_pp) >= 0), 1*HZ); - if (!rc) { - pr_err("DTS_EAGLE_ASM - %s: timeout, set-params paramid[0x%x]\n", - __func__, ad->data.param_id); - rc = -ETIMEDOUT; - goto fail_cmd; - } - - if (atomic_read(&ac->cmd_state_pp) > 0) { - pr_err("%s: DSP returned error[%s]\n", - __func__, adsp_err_get_err_str( - atomic_read(&ac->cmd_state_pp))); - rc = adsp_err_get_lnx_err_code( - atomic_read(&ac->cmd_state_pp)); - goto fail_cmd; - } - rc = 0; -fail_cmd: - kfree(ad); - return rc; -} - -int q6asm_dts_eagle_get(struct audio_client *ac, int param_id, uint32_t size, - void *data, struct param_outband *po, int m_id) -{ - struct asm_dts_eagle_param_get *ad; - int rc = 0, *ob_params = NULL; - uint32_t sz = sizeof(struct asm_dts_eagle_param) + APR_CMD_GET_HDR_SZ + - (po ? 0 : size); - - if (!ac || ac->apr == NULL || (size == 0) || !data) { - pr_err("DTS_EAGLE_ASM - %s: APR handle NULL, invalid size %u or pointer %pK\n", - __func__, size, data); - return -EINVAL; - } - ad = kzalloc(sz, GFP_KERNEL); - if (!ad) { - pr_err("DTS_EAGLE_ASM - %s: error allocating memory of size %u\n", - __func__, sz); - return -ENOMEM; - } - pr_debug("DTS_EAGLE_ASM - %s: ac %pK param_id 0x%x size %u data %pK m_id 0x%x\n", - __func__, ac, param_id, size, data, m_id); - q6asm_add_hdr(ac, &ad->hdr, sz, TRUE); - ad->hdr.opcode = ASM_STREAM_CMD_GET_PP_PARAMS_V2; - ad->param.data_payload_addr_lsw = 0; - ad->param.data_payload_addr_msw = 0; - ad->param.mem_map_handle = 0; - ad->param.module_id = m_id; - ad->param.param_id = param_id; - ad->param.param_max_size = size + APR_CMD_GET_HDR_SZ; - ad->param.reserved = 0; - atomic_set(&ac->cmd_state, -1); - - generic_get_data = kzalloc(size + sizeof(struct generic_get_data_), - GFP_KERNEL); - if (!generic_get_data) { - pr_err("DTS_EAGLE_ASM - %s: error allocating mem of size %u\n", - __func__, size); - rc = -ENOMEM; - goto fail_cmd; - } - - if (po) { - struct list_head *ptr, *next; - struct asm_buffer_node *node; - pr_debug("DTS_EAGLE_ASM - %s: using out of band memory (virtual %pK, physical %lu)\n", - __func__, po->kvaddr, (long)po->paddr); - ad->param.data_payload_addr_lsw = lower_32_bits(po->paddr); - ad->param.data_payload_addr_msw = - msm_audio_populate_upper_32_bits(po->paddr); - list_for_each_safe(ptr, next, &ac->port[IN].mem_map_handle) { - node = list_entry(ptr, struct asm_buffer_node, list); - if (node->buf_phys_addr == po->paddr) { - ad->param.mem_map_handle = node->mmap_hdl; - break; - } - } - if (ad->param.mem_map_handle == 0) { - pr_err("DTS_EAGLE_ASM - %s: mem map handle not found\n", - __func__); - rc = -EINVAL; - goto fail_cmd; - } - /* check for integer overflow */ - if (size > (UINT_MAX - APR_CMD_OB_HDR_SZ)) - rc = -EINVAL; - if ((rc < 0) || (size + APR_CMD_OB_HDR_SZ > po->size)) { - pr_err("DTS_EAGLE_ASM - %s: ion alloc of size %zu too small for size requested %u\n", - __func__, po->size, size + APR_CMD_OB_HDR_SZ); - rc = -EINVAL; - goto fail_cmd; - } - ob_params = (int *)po->kvaddr; - *ob_params++ = m_id; - *ob_params++ = param_id; - *ob_params++ = size; - generic_get_data->is_inband = 0; - } else { - pr_debug("DTS_EAGLE_ASM - %s: using in band\n", __func__); - generic_get_data->is_inband = 1; - } - - rc = apr_send_pkt(ac->apr, (uint32_t *)ad); - if (rc < 0) { - pr_err("DTS_EAGLE_ASM - %s: Commmand 0x%x failed\n", __func__, - ad->hdr.opcode); - goto fail_cmd; - } - - rc = wait_event_timeout(ac->cmd_wait, - (atomic_read(&ac->cmd_state) >= 0), 1*HZ); - if (!rc) { - pr_err("DTS_EAGLE_ASM - %s: timeout in get\n", - __func__); - rc = -ETIMEDOUT; - goto fail_cmd; - } - - if (atomic_read(&ac->cmd_state) > 0) { - pr_err("%s: DSP returned error[%s]\n", - __func__, adsp_err_get_err_str( - atomic_read(&ac->cmd_state))); - rc = adsp_err_get_lnx_err_code( - atomic_read(&ac->cmd_state)); - goto fail_cmd; - } - - if (generic_get_data->valid) { - rc = 0; - memcpy(data, po ? ob_params : generic_get_data->ints, size); - } else { - rc = -EINVAL; - pr_err("DTS_EAGLE_ASM - %s: EAGLE get params problem getting data - check callback error value\n", - __func__); - } -fail_cmd: - kfree(ad); - kfree(generic_get_data); - generic_get_data = NULL; - return rc; -} - static int __q6asm_set_volume(struct audio_client *ac, int volume, int instance) { struct asm_volume_ctrl_master_gain vol; @@ -8277,6 +8045,80 @@ exit: return rc; } +int q6asm_send_mtmx_strtr_enable_adjust_session_clock(struct audio_client *ac, + bool enable) +{ + struct asm_mtmx_strtr_params matrix; + struct asm_session_mtmx_param_adjust_session_time_ctl_t adjust_time; + int sz = 0; + int rc = 0; + + pr_debug("%s: adjust session enable %d\n", __func__, enable); + + if (!ac) { + pr_err("%s: audio client handle is NULL\n", __func__); + rc = -EINVAL; + goto exit; + } + + if (ac->apr == NULL) { + pr_err("%s: ac->apr is NULL\n", __func__); + rc = -EINVAL; + goto exit; + } + + adjust_time.enable = enable; + memset(&matrix, 0, sizeof(struct asm_mtmx_strtr_params)); + sz = sizeof(struct asm_mtmx_strtr_params); + q6asm_add_hdr(ac, &matrix.hdr, sz, TRUE); + atomic_set(&ac->cmd_state, -1); + matrix.hdr.opcode = ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2; + + matrix.param.data_payload_addr_lsw = 0; + matrix.param.data_payload_addr_msw = 0; + matrix.param.mem_map_handle = 0; + matrix.param.data_payload_size = + sizeof(struct asm_stream_param_data_v2) + + sizeof(struct asm_session_mtmx_param_adjust_session_time_ctl_t); + matrix.param.direction = 0; /* RX */ + matrix.data.module_id = ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC; + matrix.data.param_id = ASM_SESSION_MTMX_PARAM_ADJUST_SESSION_TIME_CTL; + matrix.data.param_size = + sizeof(struct asm_session_mtmx_param_adjust_session_time_ctl_t); + matrix.data.reserved = 0; + matrix.config.adj_time_param.enable = adjust_time.enable; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &matrix); + if (rc < 0) { + pr_err("%s: enable adjust session failed failed paramid [0x%x]\n", + __func__, matrix.data.param_id); + rc = -EINVAL; + goto exit; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: enable adjust session failed failed paramid [0x%x]\n", + __func__, matrix.data.param_id); + rc = -ETIMEDOUT; + goto exit; + } + + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto exit; + } + rc = 0; +exit: + return rc; +} + + static int __q6asm_cmd(struct audio_client *ac, int cmd, uint32_t stream_id) { struct apr_hdr hdr; @@ -8666,6 +8508,68 @@ fail_cmd: return -EINVAL; } +int q6asm_adjust_session_clock(struct audio_client *ac, + uint32_t adjust_time_lsw, + uint32_t adjust_time_msw) +{ + int rc = 0; + int sz = 0; + struct asm_session_cmd_adjust_session_clock_v2 adjust_clock; + + pr_debug("%s: adjust_time_lsw is %x, adjust_time_msw is %x\n", __func__, + adjust_time_lsw, adjust_time_msw); + + if (!ac) { + pr_err("%s: audio client handle is NULL\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + + if (ac->apr == NULL) { + pr_err("%s: ac->apr is NULL", __func__); + rc = -EINVAL; + goto fail_cmd; + } + + sz = sizeof(struct asm_session_cmd_adjust_session_clock_v2); + q6asm_add_hdr(ac, &adjust_clock.hdr, sz, TRUE); + atomic_set(&ac->cmd_state, -1); + adjust_clock.hdr.opcode = ASM_SESSION_CMD_ADJUST_SESSION_CLOCK_V2; + + adjust_clock.adjustime_lsw = adjust_time_lsw; + adjust_clock.adjustime_msw = adjust_time_msw; + + + rc = apr_send_pkt(ac->apr, (uint32_t *) &adjust_clock); + if (rc < 0) { + pr_err("%s: adjust_clock send failed paramid [0x%x]\n", + __func__, adjust_clock.hdr.opcode); + rc = -EINVAL; + goto fail_cmd; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout, adjust_clock paramid[0x%x]\n", + __func__, adjust_clock.hdr.opcode); + rc = -ETIMEDOUT; + goto fail_cmd; + } + + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + rc = 0; +fail_cmd: + return rc; +} + /* * q6asm_get_path_delay() - get the path delay for an audio session * @ac: audio client handle diff --git a/sound/soc/msm/qdsp6v2/q6core.c b/sound/soc/msm/qdsp6v2/q6core.c index 5399a101ba62..4340d31c218c 100644 --- a/sound/soc/msm/qdsp6v2/q6core.c +++ b/sound/soc/msm/qdsp6v2/q6core.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, 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 @@ -169,7 +169,7 @@ static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv) generic_get_data->valid = 1; generic_get_data->size_in_ints = data->payload_size/sizeof(int); - pr_debug("DTS_EAGLE_CORE callback size = %i\n", + pr_debug("callback size = %i\n", data->payload_size); memcpy(generic_get_data->ints, data->payload, data->payload_size); @@ -353,119 +353,6 @@ fail_cmd: return ret; } -int core_dts_eagle_set(int size, char *data) -{ - struct adsp_dts_eagle *payload = NULL; - int rc = 0, size_aligned4byte; - - pr_debug("DTS_EAGLE_CORE - %s\n", __func__); - if (size <= 0 || !data) { - pr_err("DTS_EAGLE_CORE - %s: invalid size %i or pointer %pK.\n", - __func__, size, data); - return -EINVAL; - } - - size_aligned4byte = (size+3) & 0xFFFFFFFC; - mutex_lock(&(q6core_lcl.cmd_lock)); - ocm_core_open(); - if (q6core_lcl.core_handle_q) { - payload = kzalloc(sizeof(struct adsp_dts_eagle) + - size_aligned4byte, GFP_KERNEL); - if (!payload) { - rc = -ENOMEM; - goto exit; - } - payload->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT, - APR_HDR_LEN(APR_HDR_SIZE), - APR_PKT_VER); - payload->hdr.pkt_size = sizeof(struct adsp_dts_eagle) + - size_aligned4byte; - payload->hdr.src_port = 0; - payload->hdr.dest_port = 0; - payload->hdr.token = 0; - payload->hdr.opcode = ADSP_CMD_SET_DTS_EAGLE_DATA_ID; - payload->id = DTS_EAGLE_LICENSE_ID; - payload->overwrite = 1; - payload->size = size; - memcpy(payload->data, data, size); - rc = apr_send_pkt(q6core_lcl.core_handle_q, - (uint32_t *)payload); - if (rc < 0) { - pr_err("DTS_EAGLE_CORE - %s: failed op[0x%x]rc[%d]\n", - __func__, payload->hdr.opcode, rc); - } - kfree(payload); - } - -exit: - mutex_unlock(&(q6core_lcl.cmd_lock)); - return rc; -} - -int core_dts_eagle_get(int id, int size, char *data) -{ - struct apr_hdr ah; - int rc = 0; - - pr_debug("DTS_EAGLE_CORE - %s\n", __func__); - if (size <= 0 || !data) { - pr_err("DTS_EAGLE_CORE - %s: invalid size %i or pointer %pK.\n", - __func__, size, data); - return -EINVAL; - } - mutex_lock(&(q6core_lcl.cmd_lock)); - ocm_core_open(); - if (q6core_lcl.core_handle_q) { - ah.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT, - APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); - ah.pkt_size = sizeof(struct apr_hdr); - ah.src_port = 0; - ah.dest_port = 0; - ah.token = 0; - ah.opcode = id; - - q6core_lcl.bus_bw_resp_received = 0; - generic_get_data = kzalloc(sizeof(struct generic_get_data_) - + size, GFP_KERNEL); - if (!generic_get_data) { - rc = -ENOMEM; - goto exit; - } - - rc = apr_send_pkt(q6core_lcl.core_handle_q, - (uint32_t *)&ah); - if (rc < 0) { - pr_err("DTS_EAGLE_CORE - %s: failed op[0x%x]rc[%d]\n", - __func__, ah.opcode, rc); - goto exit; - } - - rc = wait_event_timeout(q6core_lcl.bus_bw_req_wait, - (q6core_lcl.bus_bw_resp_received == 1), - msecs_to_jiffies(TIMEOUT_MS)); - if (!rc) { - pr_err("DTS_EAGLE_CORE - %s: EAGLE get params timed out\n", - __func__); - rc = -EINVAL; - goto exit; - } - if (generic_get_data->valid) { - rc = 0; - memcpy(data, generic_get_data->ints, size); - } else { - rc = -EINVAL; - pr_err("DTS_EAGLE_CORE - %s: EAGLE get params problem getting data - check callback error value\n", - __func__); - } - } - -exit: - kfree(generic_get_data); - generic_get_data = NULL; - mutex_unlock(&(q6core_lcl.cmd_lock)); - return rc; -} - uint32_t core_set_dolby_manufacturer_id(int manufacturer_id) { struct adsp_dolby_manufacturer_id payload; @@ -511,21 +398,23 @@ bool q6core_is_adsp_ready(void) mutex_lock(&(q6core_lcl.cmd_lock)); ocm_core_open(); - q6core_lcl.bus_bw_resp_received = 0; - rc = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *)&hdr); - if (rc < 0) { - pr_err("%s: Get ADSP state APR packet send event %d\n", - __func__, rc); - goto bail; - } + if (q6core_lcl.core_handle_q) { + q6core_lcl.bus_bw_resp_received = 0; + rc = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *)&hdr); + if (rc < 0) { + pr_err("%s: Get ADSP state APR packet send event %d\n", + __func__, rc); + goto bail; + } - rc = wait_event_timeout(q6core_lcl.bus_bw_req_wait, - (q6core_lcl.bus_bw_resp_received == 1), - msecs_to_jiffies(Q6_READY_TIMEOUT_MS)); - if (rc > 0 && q6core_lcl.bus_bw_resp_received) { - /* ensure to read updated param by callback thread */ - rmb(); - ret = !!q6core_lcl.param; + rc = wait_event_timeout(q6core_lcl.bus_bw_req_wait, + (q6core_lcl.bus_bw_resp_received == 1), + msecs_to_jiffies(Q6_READY_TIMEOUT_MS)); + if (rc > 0 && q6core_lcl.bus_bw_resp_received) { + /* ensure to read updated param by callback thread */ + rmb(); + ret = !!q6core_lcl.param; + } } bail: pr_debug("%s: leave, rc %d, adsp ready %d\n", __func__, rc, ret); diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 0d9f48ec42bb..bc7adb84e679 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -1433,7 +1433,7 @@ int main(int argc, char *argv[]) openlog("KVP", 0, LOG_USER); syslog(LOG_INFO, "KVP starting; pid is:%d", getpid()); - kvp_fd = open("/dev/vmbus/hv_kvp", O_RDWR); + kvp_fd = open("/dev/vmbus/hv_kvp", O_RDWR | O_CLOEXEC); if (kvp_fd < 0) { syslog(LOG_ERR, "open /dev/vmbus/hv_kvp failed; error: %d %s", diff --git a/tools/include/linux/log2.h b/tools/include/linux/log2.h index 41446668ccce..d5677d39c1e4 100644 --- a/tools/include/linux/log2.h +++ b/tools/include/linux/log2.h @@ -13,12 +13,6 @@ #define _TOOLS_LINUX_LOG2_H /* - * deal with unrepresentable constant logarithms - */ -extern __attribute__((const, noreturn)) -int ____ilog2_NaN(void); - -/* * non-constant log of base 2 calculators * - the arch may override these in asm/bitops.h if they can be implemented * more efficiently than using fls() and fls64() @@ -78,7 +72,7 @@ unsigned long __rounddown_pow_of_two(unsigned long n) #define ilog2(n) \ ( \ __builtin_constant_p(n) ? ( \ - (n) < 1 ? ____ilog2_NaN() : \ + (n) < 2 ? 0 : \ (n) & (1ULL << 63) ? 63 : \ (n) & (1ULL << 62) ? 62 : \ (n) & (1ULL << 61) ? 61 : \ @@ -141,10 +135,7 @@ unsigned long __rounddown_pow_of_two(unsigned long n) (n) & (1ULL << 4) ? 4 : \ (n) & (1ULL << 3) ? 3 : \ (n) & (1ULL << 2) ? 2 : \ - (n) & (1ULL << 1) ? 1 : \ - (n) & (1ULL << 0) ? 0 : \ - ____ilog2_NaN() \ - ) : \ + 1 ) : \ (sizeof(n) <= 4) ? \ __ilog2_u32(n) : \ __ilog2_u64(n) \ diff --git a/virt/kvm/eventfd.c b/virt/kvm/eventfd.c index 46dbc0a7dfc1..49001fa84ead 100644 --- a/virt/kvm/eventfd.c +++ b/virt/kvm/eventfd.c @@ -868,7 +868,8 @@ kvm_deassign_ioeventfd_idx(struct kvm *kvm, enum kvm_bus bus_idx, continue; kvm_io_bus_unregister_dev(kvm, bus_idx, &p->dev); - kvm->buses[bus_idx]->ioeventfd_count--; + if (kvm->buses[bus_idx]) + kvm->buses[bus_idx]->ioeventfd_count--; ioeventfd_release(p); ret = 0; break; diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 336ed267c407..cb092bd9965b 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -654,8 +654,11 @@ static void kvm_destroy_vm(struct kvm *kvm) list_del(&kvm->vm_list); spin_unlock(&kvm_lock); kvm_free_irq_routing(kvm); - for (i = 0; i < KVM_NR_BUSES; i++) - kvm_io_bus_destroy(kvm->buses[i]); + for (i = 0; i < KVM_NR_BUSES; i++) { + if (kvm->buses[i]) + kvm_io_bus_destroy(kvm->buses[i]); + kvm->buses[i] = NULL; + } kvm_coalesced_mmio_free(kvm); #if defined(CONFIG_MMU_NOTIFIER) && defined(KVM_ARCH_WANT_MMU_NOTIFIER) mmu_notifier_unregister(&kvm->mmu_notifier, kvm->mm); @@ -3271,6 +3274,8 @@ int kvm_io_bus_write(struct kvm_vcpu *vcpu, enum kvm_bus bus_idx, gpa_t addr, }; bus = srcu_dereference(vcpu->kvm->buses[bus_idx], &vcpu->kvm->srcu); + if (!bus) + return -ENOMEM; r = __kvm_io_bus_write(vcpu, bus, &range, val); return r < 0 ? r : 0; } @@ -3288,6 +3293,8 @@ int kvm_io_bus_write_cookie(struct kvm_vcpu *vcpu, enum kvm_bus bus_idx, }; bus = srcu_dereference(vcpu->kvm->buses[bus_idx], &vcpu->kvm->srcu); + if (!bus) + return -ENOMEM; /* First try the device referenced by cookie. */ if ((cookie >= 0) && (cookie < bus->dev_count) && @@ -3338,6 +3345,8 @@ int kvm_io_bus_read(struct kvm_vcpu *vcpu, enum kvm_bus bus_idx, gpa_t addr, }; bus = srcu_dereference(vcpu->kvm->buses[bus_idx], &vcpu->kvm->srcu); + if (!bus) + return -ENOMEM; r = __kvm_io_bus_read(vcpu, bus, &range, val); return r < 0 ? r : 0; } @@ -3350,6 +3359,9 @@ int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, struct kvm_io_bus *new_bus, *bus; bus = kvm->buses[bus_idx]; + if (!bus) + return -ENOMEM; + /* exclude ioeventfd which is limited by maximum fd */ if (bus->dev_count - bus->ioeventfd_count > NR_IOBUS_DEVS - 1) return -ENOSPC; @@ -3369,37 +3381,41 @@ int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, } /* Caller must hold slots_lock. */ -int kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx, - struct kvm_io_device *dev) +void kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx, + struct kvm_io_device *dev) { - int i, r; + int i; struct kvm_io_bus *new_bus, *bus; bus = kvm->buses[bus_idx]; - r = -ENOENT; + if (!bus) + return; + for (i = 0; i < bus->dev_count; i++) if (bus->range[i].dev == dev) { - r = 0; break; } - if (r) - return r; + if (i == bus->dev_count) + return; new_bus = kmalloc(sizeof(*bus) + ((bus->dev_count - 1) * sizeof(struct kvm_io_range)), GFP_KERNEL); - if (!new_bus) - return -ENOMEM; + if (!new_bus) { + pr_err("kvm: failed to shrink bus, removing it completely\n"); + goto broken; + } memcpy(new_bus, bus, sizeof(*bus) + i * sizeof(struct kvm_io_range)); new_bus->dev_count--; memcpy(new_bus->range + i, bus->range + i + 1, (new_bus->dev_count - i) * sizeof(struct kvm_io_range)); +broken: rcu_assign_pointer(kvm->buses[bus_idx], new_bus); synchronize_srcu_expedited(&kvm->srcu); kfree(bus); - return r; + return; } static struct notifier_block kvm_cpu_notifier = { |
