diff options
604 files changed, 7265 insertions, 2869 deletions
@@ -199,7 +199,8 @@ Elliot Berman <quic_eberman@quicinc.com> <eberman@codeaurora.org> Enric Balletbo i Serra <eballetbo@kernel.org> <enric.balletbo@collabora.com> Enric Balletbo i Serra <eballetbo@kernel.org> <eballetbo@iseebcn.com> Erik Kaneda <erik.kaneda@intel.com> <erik.schmauss@intel.com> -Eugen Hristev <eugen.hristev@collabora.com> <eugen.hristev@microchip.com> +Eugen Hristev <eugen.hristev@linaro.org> <eugen.hristev@microchip.com> +Eugen Hristev <eugen.hristev@linaro.org> <eugen.hristev@collabora.com> Evgeniy Polyakov <johnpol@2ka.mipt.ru> Ezequiel Garcia <ezequiel@vanguardiasur.com.ar> <ezequiel@collabora.com> Faith Ekstrand <faith.ekstrand@collabora.com> <jason@jlekstrand.net> @@ -282,7 +283,7 @@ Jan Glauber <jan.glauber@gmail.com> <jglauber@cavium.com> Jan Kuliga <jtkuliga.kdev@gmail.com> <jankul@alatek.krakow.pl> Jarkko Sakkinen <jarkko@kernel.org> <jarkko.sakkinen@linux.intel.com> Jarkko Sakkinen <jarkko@kernel.org> <jarkko@profian.com> -Jarkko Sakkinen <jarkko@kernel.org> <jarkko.sakkinen@tuni.fi> +Jarkko Sakkinen <jarkko@kernel.org> <jarkko.sakkinen@parity.io> Jason Gunthorpe <jgg@ziepe.ca> <jgg@mellanox.com> Jason Gunthorpe <jgg@ziepe.ca> <jgg@nvidia.com> Jason Gunthorpe <jgg@ziepe.ca> <jgunthorpe@obsidianresearch.com> diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uvc b/Documentation/ABI/testing/configfs-usb-gadget-uvc index 4feb692c4c1d..b6720768d63d 100644 --- a/Documentation/ABI/testing/configfs-usb-gadget-uvc +++ b/Documentation/ABI/testing/configfs-usb-gadget-uvc @@ -342,6 +342,70 @@ Description: Specific uncompressed frame descriptors support ========================= ===================================== +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/framebased +Date: Sept 2024 +KernelVersion: 5.15 +Description: Framebased format descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/framebased/name +Date: Sept 2024 +KernelVersion: 5.15 +Description: Specific framebased format descriptors + + ================== ======================================= + bFormatIndex unique id for this format descriptor; + only defined after parent header is + linked into the streaming class; + read-only + bmaControls this format's data for bmaControls in + the streaming header + bmInterlaceFlags specifies interlace information, + read-only + bAspectRatioY the X dimension of the picture aspect + ratio, read-only + bAspectRatioX the Y dimension of the picture aspect + ratio, read-only + bDefaultFrameIndex optimum frame index for this stream + bBitsPerPixel number of bits per pixel used to + specify color in the decoded video + frame + guidFormat globally unique id used to identify + stream-encoding format + ================== ======================================= + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/framebased/name/name +Date: Sept 2024 +KernelVersion: 5.15 +Description: Specific framebased frame descriptors + + ========================= ===================================== + bFrameIndex unique id for this framedescriptor; + only defined after parent format is + linked into the streaming header; + read-only + dwFrameInterval indicates how frame interval can be + programmed; a number of values + separated by newline can be specified + dwDefaultFrameInterval the frame interval the device would + like to use as default + dwBytesPerLine Specifies the number of bytes per line + of video for packed fixed frame size + formats, allowing the receiver to + perform stride alignment of the video. + If the bVariableSize value (above) is + TRUE (1), or if the format does not + permit such alignment, this value shall + be set to zero (0). + dwMaxBitRate the maximum bit rate at the shortest + frame interval in bps + dwMinBitRate the minimum bit rate at the longest + frame interval in bps + wHeight height of decoded bitmap frame in px + wWidth width of decoded bitmam frame in px + bmCapabilities still image support, fixed frame-rate + support + ========================= ===================================== + What: /config/usb-gadget/gadget/functions/uvc.name/streaming/header Date: Dec 2014 KernelVersion: 4.0 diff --git a/Documentation/ABI/testing/sysfs-class-typec b/Documentation/ABI/testing/sysfs-class-typec index 281b995beb05..38e101c17a00 100644 --- a/Documentation/ABI/testing/sysfs-class-typec +++ b/Documentation/ABI/testing/sysfs-class-typec @@ -149,6 +149,19 @@ Description: advertise to the partner. The currently used capabilities are in brackets. Selection happens by writing to the file. +What: /sys/class/typec/<port>/usb_capability +Date: November 2024 +Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> +Description: Lists the supported USB Modes. The default USB mode that is used + next time with the Enter_USB Message is in brackets. The default + mode can be changed by writing to the file when supported by the + driver. + + Valid values: + - usb2 (USB 2.0) + - usb3 (USB 3.2) + - usb4 (USB4) + USB Type-C partner devices (eg. /sys/class/typec/port0-partner/) What: /sys/class/typec/<port>-partner/accessory_mode @@ -220,6 +233,20 @@ Description: directory exists, it will have an attribute file for every VDO in Discover Identity command result. +What: /sys/class/typec/<port>-partner/usb_mode +Date: November 2024 +Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> +Description: The USB Modes that the partner device supports. The active mode + is displayed in brackets. The active USB mode can be changed by + writing to this file when the port driver is able to send Data + Reset Message to the partner. That requires USB Power Delivery + contract between the partner and the port. + + Valid values: + - usb2 (USB 2.0) + - usb3 (USB 3.2) + - usb4 (USB4) + USB Type-C cable devices (eg. /sys/class/typec/port0-cable/) Note: Electronically Marked Cables will have a device also for one cable plug diff --git a/Documentation/devicetree/bindings/connector/usb-connector.yaml b/Documentation/devicetree/bindings/connector/usb-connector.yaml index fb216ce68bb3..21a0c58c65cd 100644 --- a/Documentation/devicetree/bindings/connector/usb-connector.yaml +++ b/Documentation/devicetree/bindings/connector/usb-connector.yaml @@ -253,6 +253,36 @@ properties: additionalProperties: false + sink-wait-cap-time-ms: + description: Represents the max time in ms that USB Type-C port (in sink + role) should wait for the port partner (source role) to send source caps. + SinkWaitCap timer starts when port in sink role attaches to the source. + This timer will stop when sink receives PD source cap advertisement before + timeout in which case it'll move to capability negotiation stage. A + timeout leads to a hard reset message by the port. + minimum: 310 + maximum: 620 + default: 310 + + ps-source-off-time-ms: + description: Represents the max time in ms that a DRP in source role should + take to turn off power after the PsSourceOff timer starts. PsSourceOff + timer starts when a sink's PHY layer receives EOP of the GoodCRC message + (corresponding to an Accept message sent in response to a PR_Swap or a + FR_Swap request). This timer stops when last bit of GoodCRC EOP + corresponding to the received PS_RDY message is transmitted by the PHY + layer. A timeout shall lead to error recovery in the type-c port. + minimum: 750 + maximum: 920 + default: 920 + + cc-debounce-time-ms: + description: Represents the max time in ms that a port shall wait to + determine if it's attached to a partner. + minimum: 100 + maximum: 200 + default: 200 + dependencies: sink-vdos-v1: [ sink-vdos ] sink-vdos: [ sink-vdos-v1 ] @@ -380,7 +410,7 @@ examples: }; # USB-C connector attached to a typec port controller(ptn5110), which has - # power delivery support and enables drp. + # power delivery support, explicitly defines time properties and enables drp. - | #include <dt-bindings/usb/pd.h> typec: ptn5110 { @@ -393,6 +423,9 @@ examples: sink-pdos = <PDO_FIXED(5000, 2000, PDO_FIXED_USB_COMM) PDO_VAR(5000, 12000, 2000)>; op-sink-microwatt = <10000000>; + sink-wait-cap-time-ms = <465>; + ps-source-off-time-ms = <835>; + cc-debounce-time-ms = <101>; }; }; diff --git a/Documentation/devicetree/bindings/display/mediatek/mediatek,dpi.yaml b/Documentation/devicetree/bindings/display/mediatek/mediatek,dpi.yaml index 3a82aec9021c..497c0eb4ed0b 100644 --- a/Documentation/devicetree/bindings/display/mediatek/mediatek,dpi.yaml +++ b/Documentation/devicetree/bindings/display/mediatek/mediatek,dpi.yaml @@ -63,6 +63,16 @@ properties: - const: sleep power-domains: + description: | + The MediaTek DPI module is typically associated with one of the + following multimedia power domains: + POWER_DOMAIN_DISPLAY + POWER_DOMAIN_VDOSYS + POWER_DOMAIN_MM + The specific power domain used varies depending on the SoC design. + + It is recommended to explicitly add the appropriate power domain + property to the DPI node in the device tree. maxItems: 1 port: @@ -79,20 +89,6 @@ required: - clock-names - port -allOf: - - if: - not: - properties: - compatible: - contains: - enum: - - mediatek,mt6795-dpi - - mediatek,mt8173-dpi - - mediatek,mt8186-dpi - then: - properties: - power-domains: false - additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/display/mediatek/mediatek,split.yaml b/Documentation/devicetree/bindings/display/mediatek/mediatek,split.yaml index e4affc854f3d..4b6ff546757e 100644 --- a/Documentation/devicetree/bindings/display/mediatek/mediatek,split.yaml +++ b/Documentation/devicetree/bindings/display/mediatek/mediatek,split.yaml @@ -38,6 +38,7 @@ properties: description: A phandle and PM domain specifier as defined by bindings of the power controller specified by phandle. See Documentation/devicetree/bindings/power/power-domain.yaml for details. + maxItems: 1 mediatek,gce-client-reg: description: @@ -57,6 +58,9 @@ properties: clocks: items: - description: SPLIT Clock + - description: Used for interfacing with the HDMI RX signal source. + - description: Paired with receiving HDMI RX metadata. + minItems: 1 required: - compatible @@ -72,9 +76,24 @@ allOf: const: mediatek,mt8195-mdp3-split then: + properties: + clocks: + minItems: 3 + required: - mediatek,gce-client-reg + - if: + properties: + compatible: + contains: + const: mediatek,mt8173-disp-split + + then: + properties: + clocks: + maxItems: 1 + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml index bd19abb867d9..0065d6508824 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml @@ -67,6 +67,10 @@ properties: A 2.5V to 3.3V supply for the external reference voltage. When omitted, the internal 2.5V reference is used. + refin-supply: + description: + A 2.5V to 3.3V supply for external reference voltage, for ad7380-4 only. + aina-supply: description: The common mode voltage supply for the AINA- pin on pseudo-differential @@ -135,6 +139,23 @@ allOf: ainc-supply: false aind-supply: false + # ad7380-4 uses refin-supply as external reference. + # All other chips from ad738x family use refio as optional external reference. + # When refio-supply is omitted, internal reference is used. + - if: + properties: + compatible: + enum: + - adi,ad7380-4 + then: + properties: + refio-supply: false + required: + - refin-supply + else: + properties: + refin-supply: false + examples: - | #include <dt-bindings/interrupt-controller/irq.h> diff --git a/Documentation/devicetree/bindings/phy/fsl,imx8mq-usb-phy.yaml b/Documentation/devicetree/bindings/phy/fsl,imx8mq-usb-phy.yaml index dc3a3f709fea..6d6d211883ae 100644 --- a/Documentation/devicetree/bindings/phy/fsl,imx8mq-usb-phy.yaml +++ b/Documentation/devicetree/bindings/phy/fsl,imx8mq-usb-phy.yaml @@ -11,12 +11,17 @@ maintainers: properties: compatible: - enum: - - fsl,imx8mq-usb-phy - - fsl,imx8mp-usb-phy + oneOf: + - enum: + - fsl,imx8mq-usb-phy + - fsl,imx8mp-usb-phy + - items: + - const: fsl,imx95-usb-phy + - const: fsl,imx8mp-usb-phy reg: - maxItems: 1 + minItems: 1 + maxItems: 2 "#phy-cells": const: 0 @@ -89,7 +94,34 @@ required: - clocks - clock-names -additionalProperties: false +allOf: + - if: + properties: + compatible: + contains: + enum: + - fsl,imx95-usb-phy + then: + properties: + reg: + items: + - description: USB PHY Control range + - description: USB PHY TCA Block range + else: + properties: + reg: + maxItems: 1 + + - if: + properties: + compatible: + contains: + enum: + - fsl,imx95-usb-phy + then: + $ref: /schemas/usb/usb-switch.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/phy/qcom,msm8998-qmp-usb3-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,msm8998-qmp-usb3-phy.yaml index f1f4e4f83352..1636285fbe53 100644 --- a/Documentation/devicetree/bindings/phy/qcom,msm8998-qmp-usb3-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,msm8998-qmp-usb3-phy.yaml @@ -18,6 +18,7 @@ properties: enum: - qcom,msm8998-qmp-usb3-phy - qcom,qcm2290-qmp-usb3-phy + - qcom,qcs615-qmp-usb3-phy - qcom,sdm660-qmp-usb3-phy - qcom,sm6115-qmp-usb3-phy @@ -96,6 +97,7 @@ allOf: contains: enum: - qcom,msm8998-qmp-usb3-phy + - qcom,qcs615-qmp-usb3-phy - qcom,sdm660-qmp-usb3-phy then: properties: diff --git a/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml index 95eecbaef05c..4aed4b5d65ec 100644 --- a/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml @@ -25,6 +25,7 @@ properties: - qcom,msm8996-qusb2-phy - qcom,msm8998-qusb2-phy - qcom,qcm2290-qusb2-phy + - qcom,qcs615-qusb2-phy - qcom,sdm660-qusb2-phy - qcom,sm4250-qusb2-phy - qcom,sm6115-qusb2-phy diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml index dcf4fa55fbba..380a9222a51d 100644 --- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml @@ -154,8 +154,6 @@ allOf: - qcom,sm8550-qmp-gen4x2-pcie-phy - qcom,sm8650-qmp-gen3x2-pcie-phy - qcom,sm8650-qmp-gen4x2-pcie-phy - - qcom,x1e80100-qmp-gen3x2-pcie-phy - - qcom,x1e80100-qmp-gen4x2-pcie-phy then: properties: clocks: @@ -171,6 +169,8 @@ allOf: - qcom,sc8280xp-qmp-gen3x1-pcie-phy - qcom,sc8280xp-qmp-gen3x2-pcie-phy - qcom,sc8280xp-qmp-gen3x4-pcie-phy + - qcom,x1e80100-qmp-gen3x2-pcie-phy + - qcom,x1e80100-qmp-gen4x2-pcie-phy - qcom,x1e80100-qmp-gen4x4-pcie-phy then: properties: @@ -201,6 +201,7 @@ allOf: - qcom,sm8550-qmp-gen4x2-pcie-phy - qcom,sm8650-qmp-gen4x2-pcie-phy - qcom,x1e80100-qmp-gen4x2-pcie-phy + - qcom,x1e80100-qmp-gen4x4-pcie-phy then: properties: resets: diff --git a/Documentation/devicetree/bindings/sound/rockchip,rk3308-codec.yaml b/Documentation/devicetree/bindings/sound/rockchip,rk3308-codec.yaml index ecf3d7d968c8..2cf229a076f0 100644 --- a/Documentation/devicetree/bindings/sound/rockchip,rk3308-codec.yaml +++ b/Documentation/devicetree/bindings/sound/rockchip,rk3308-codec.yaml @@ -48,6 +48,10 @@ properties: - const: mclk_rx - const: hclk + port: + $ref: audio-graph-port.yaml# + unevaluatedProperties: false + resets: maxItems: 1 diff --git a/Documentation/devicetree/bindings/usb/cypress,cypd4226.yaml b/Documentation/devicetree/bindings/usb/cypress,cypd4226.yaml index 89fc9a434d05..0620d82508c1 100644 --- a/Documentation/devicetree/bindings/usb/cypress,cypd4226.yaml +++ b/Documentation/devicetree/bindings/usb/cypress,cypd4226.yaml @@ -61,18 +61,15 @@ additionalProperties: false examples: - | - #include <dt-bindings/gpio/tegra194-gpio.h> #include <dt-bindings/interrupt-controller/arm-gic.h> i2c { #address-cells = <1>; #size-cells = <0>; - #interrupt-cells = <2>; typec@8 { compatible = "cypress,cypd4226"; reg = <0x08>; - interrupt-parent = <&gpio_aon>; - interrupts = <TEGRA194_AON_GPIO(BB, 2) IRQ_TYPE_LEVEL_LOW>; + interrupts = <2 IRQ_TYPE_LEVEL_LOW>; firmware-name = "nvidia,jetson-agx-xavier"; #address-cells = <1>; #size-cells = <0>; diff --git a/Documentation/devicetree/bindings/usb/fsl,imx8mp-dwc3.yaml b/Documentation/devicetree/bindings/usb/fsl,imx8mp-dwc3.yaml index 9ea1e4cd0709..baf130669c38 100644 --- a/Documentation/devicetree/bindings/usb/fsl,imx8mp-dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/fsl,imx8mp-dwc3.yaml @@ -12,7 +12,11 @@ maintainers: properties: compatible: - const: fsl,imx8mp-dwc3 + oneOf: + - items: + - const: fsl,imx95-dwc3 + - const: fsl,imx8mp-dwc3 + - const: fsl,imx8mp-dwc3 reg: items: diff --git a/Documentation/devicetree/bindings/usb/genesys,gl850g.yaml b/Documentation/devicetree/bindings/usb/genesys,gl850g.yaml index fc833363cfb4..6fe2d356dcbd 100644 --- a/Documentation/devicetree/bindings/usb/genesys,gl850g.yaml +++ b/Documentation/devicetree/bindings/usb/genesys,gl850g.yaml @@ -62,7 +62,14 @@ allOf: peer-hub: true vdd-supply: true -additionalProperties: false +patternProperties: + "^.*@[0-9a-f]{1,2}$": + description: The hard wired USB devices + type: object + $ref: /schemas/usb/usb-device.yaml + additionalProperties: true + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/usb/microchip,mpfs-musb.yaml b/Documentation/devicetree/bindings/usb/microchip,mpfs-musb.yaml index 27b909de4992..a812317d8089 100644 --- a/Documentation/devicetree/bindings/usb/microchip,mpfs-musb.yaml +++ b/Documentation/devicetree/bindings/usb/microchip,mpfs-musb.yaml @@ -14,8 +14,11 @@ maintainers: properties: compatible: - enum: - - microchip,mpfs-musb + oneOf: + - items: + - const: microchip,pic64gx-musb + - const: microchip,mpfs-musb + - const: microchip,mpfs-musb dr_mode: true diff --git a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml index 18758efb8d29..42603e0f4d84 100644 --- a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml @@ -28,6 +28,7 @@ properties: - qcom,qcs404-dwc3 - qcom,qdu1000-dwc3 - qcom,sa8775p-dwc3 + - qcom,sar2130p-dwc3 - qcom,sc7180-dwc3 - qcom,sc7280-dwc3 - qcom,sc8180x-dwc3 @@ -338,6 +339,7 @@ allOf: contains: enum: - qcom,qcm2290-dwc3 + - qcom,sar2130p-dwc3 - qcom,sc8180x-dwc3 - qcom,sc8180x-dwc3-mp - qcom,sm6115-dwc3 diff --git a/Documentation/devicetree/bindings/usb/renesas,usbhs.yaml b/Documentation/devicetree/bindings/usb/renesas,usbhs.yaml index c63db3ebd07b..b23ef29bf794 100644 --- a/Documentation/devicetree/bindings/usb/renesas,usbhs.yaml +++ b/Documentation/devicetree/bindings/usb/renesas,usbhs.yaml @@ -76,6 +76,10 @@ properties: Integer to use BUSWAIT register. renesas,enable-gpio: + deprecated: true + maxItems: 1 + + renesas,enable-gpios: maxItems: 1 description: | gpio specifier to check GPIO determining if USB function should be diff --git a/Documentation/devicetree/bindings/usb/rockchip,dwc3.yaml b/Documentation/devicetree/bindings/usb/rockchip,dwc3.yaml index c4924113f9bd..a21cc098542d 100644 --- a/Documentation/devicetree/bindings/usb/rockchip,dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/rockchip,dwc3.yaml @@ -27,6 +27,7 @@ select: enum: - rockchip,rk3328-dwc3 - rockchip,rk3568-dwc3 + - rockchip,rk3576-dwc3 - rockchip,rk3588-dwc3 required: - compatible @@ -37,6 +38,7 @@ properties: - enum: - rockchip,rk3328-dwc3 - rockchip,rk3568-dwc3 + - rockchip,rk3576-dwc3 - rockchip,rk3588-dwc3 - const: snps,dwc3 @@ -113,7 +115,9 @@ allOf: properties: compatible: contains: - const: rockchip,rk3568-dwc3 + enum: + - rockchip,rk3568-dwc3 + - rockchip,rk3576-dwc3 then: properties: clocks: diff --git a/Documentation/devicetree/bindings/usb/ti,tusb1046.yaml b/Documentation/devicetree/bindings/usb/ti,tusb1046.yaml new file mode 100644 index 000000000000..f713cac4a8ac --- /dev/null +++ b/Documentation/devicetree/bindings/usb/ti,tusb1046.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/ti,tusb1046.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments TUSB1046-DCI Type-C crosspoint switch + +maintainers: + - Romain Gantois <romain.gantois@bootlin.com> + +allOf: + - $ref: usb-switch.yaml# + +properties: + compatible: + const: ti,tusb1046 + + reg: + maxItems: 1 + +required: + - compatible + - reg + - port + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + typec-mux@44 { + compatible = "ti,tusb1046"; + reg = <0x44>; + + mode-switch; + orientation-switch; + + port { + endpoint { + remote-endpoint = <&typec_controller>; + }; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/usb/ti,tusb73x0-pci.yaml b/Documentation/devicetree/bindings/usb/ti,tusb73x0-pci.yaml new file mode 100644 index 000000000000..ddda734f36fb --- /dev/null +++ b/Documentation/devicetree/bindings/usb/ti,tusb73x0-pci.yaml @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/ti,tusb73x0-pci.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TUSB73x0 USB 3.0 xHCI Host Controller (PCIe) + +maintainers: + - Francesco Dolcini <francesco.dolcini@toradex.com> + +description: + TUSB73x0 USB 3.0 xHCI Host Controller via PCIe x1 Gen2 interface. + The TUSB7320 supports up to two downstream ports, the TUSB7340 supports up + to four downstream ports, both variants share the same PCI device ID. + +properties: + compatible: + const: pci104c,8241 + + reg: + maxItems: 1 + + ti,pwron-active-high: + $ref: /schemas/types.yaml#/definitions/flag + description: + Configure the polarity of the PWRONx# signals. When this is present, the + PWRONx# pins are active high and their internal pull-down resistors are + disabled. When this is absent, the PWRONx# pins are active low (default) + and their internal pull-down resistors are enabled. + +required: + - compatible + - reg + +allOf: + - $ref: usb-xhci.yaml + +additionalProperties: false + +examples: + - | + pcie@0 { + reg = <0x0 0x1000>; + ranges = <0x02000000 0x0 0x100000 0x10000000 0x0 0x0>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + + usb@0 { + compatible = "pci104c,8241"; + reg = <0x0 0x0 0x0 0x0 0x0>; + ti,pwron-active-high; + }; + }; diff --git a/Documentation/filesystems/caching/cachefiles.rst b/Documentation/filesystems/caching/cachefiles.rst index e04a27bdbe19..b3ccc782cb3b 100644 --- a/Documentation/filesystems/caching/cachefiles.rst +++ b/Documentation/filesystems/caching/cachefiles.rst @@ -115,7 +115,7 @@ set up cache ready for use. The following script commands are available: This mask can also be set through sysfs, eg:: - echo 5 >/sys/modules/cachefiles/parameters/debug + echo 5 > /sys/module/cachefiles/parameters/debug Starting the Cache diff --git a/Documentation/iio/ad7380.rst b/Documentation/iio/ad7380.rst index 9c784c1e652e..6f70b49b9ef2 100644 --- a/Documentation/iio/ad7380.rst +++ b/Documentation/iio/ad7380.rst @@ -41,13 +41,22 @@ supports only 1 SDO line. Reference voltage ----------------- -2 possible reference voltage sources are supported: +ad7380-4 +~~~~~~~~ + +ad7380-4 supports only an external reference voltage (2.5V to 3.3V). It must be +declared in the device tree as ``refin-supply``. + +All other devices from ad738x family +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +All other devices from ad738x support 2 possible reference voltage sources: - Internal reference (2.5V) - External reference (2.5V to 3.3V) The source is determined by the device tree. If ``refio-supply`` is present, -then the external reference is used, else the internal reference is used. +then it is used as external reference, else the internal reference is used. Oversampling and resolution boost --------------------------------- diff --git a/Documentation/networking/packet_mmap.rst b/Documentation/networking/packet_mmap.rst index dca15d15feaf..02370786e77b 100644 --- a/Documentation/networking/packet_mmap.rst +++ b/Documentation/networking/packet_mmap.rst @@ -16,7 +16,7 @@ ii) transmit network traffic, or any other that needs raw Howto can be found at: - https://sites.google.com/site/packetmmap/ + https://web.archive.org/web/20220404160947/https://sites.google.com/site/packetmmap/ Please send your comments to - Ulisses Alonso Camaró <uaca@i.hate.spam.alumni.uv.es> @@ -166,7 +166,8 @@ As capture, each frame contains two parts:: /* bind socket to eth0 */ bind(this->socket, (struct sockaddr *)&my_addr, sizeof(struct sockaddr_ll)); - A complete tutorial is available at: https://sites.google.com/site/packetmmap/ + A complete tutorial is available at: + https://web.archive.org/web/20220404160947/https://sites.google.com/site/packetmmap/ By default, the user should put data at:: diff --git a/Documentation/rust/arch-support.rst b/Documentation/rust/arch-support.rst index 750ff371570a..54be7ddf3e57 100644 --- a/Documentation/rust/arch-support.rst +++ b/Documentation/rust/arch-support.rst @@ -17,7 +17,7 @@ Architecture Level of support Constraints ============= ================ ============================================== ``arm64`` Maintained Little Endian only. ``loongarch`` Maintained \- -``riscv`` Maintained ``riscv64`` only. +``riscv`` Maintained ``riscv64`` and LLVM/Clang only. ``um`` Maintained \- ``x86`` Maintained ``x86_64`` only. ============= ================ ============================================== diff --git a/Documentation/userspace-api/mseal.rst b/Documentation/userspace-api/mseal.rst index 4132eec995a3..41102f74c5e2 100644 --- a/Documentation/userspace-api/mseal.rst +++ b/Documentation/userspace-api/mseal.rst @@ -23,177 +23,166 @@ applications can additionally seal security critical data at runtime. A similar feature already exists in the XNU kernel with the VM_FLAGS_PERMANENT flag [1] and on OpenBSD with the mimmutable syscall [2]. -User API -======== -mseal() ------------ -The mseal() syscall has the following signature: - -``int mseal(void addr, size_t len, unsigned long flags)`` - -**addr/len**: virtual memory address range. - -The address range set by ``addr``/``len`` must meet: - - The start address must be in an allocated VMA. - - The start address must be page aligned. - - The end address (``addr`` + ``len``) must be in an allocated VMA. - - no gap (unallocated memory) between start and end address. - -The ``len`` will be paged aligned implicitly by the kernel. - -**flags**: reserved for future use. - -**return values**: - -- ``0``: Success. - -- ``-EINVAL``: - - Invalid input ``flags``. - - The start address (``addr``) is not page aligned. - - Address range (``addr`` + ``len``) overflow. - -- ``-ENOMEM``: - - The start address (``addr``) is not allocated. - - The end address (``addr`` + ``len``) is not allocated. - - A gap (unallocated memory) between start and end address. - -- ``-EPERM``: - - sealing is supported only on 64-bit CPUs, 32-bit is not supported. - -- For above error cases, users can expect the given memory range is - unmodified, i.e. no partial update. - -- There might be other internal errors/cases not listed here, e.g. - error during merging/splitting VMAs, or the process reaching the max - number of supported VMAs. In those cases, partial updates to the given - memory range could happen. However, those cases should be rare. - -**Blocked operations after sealing**: - Unmapping, moving to another location, and shrinking the size, - via munmap() and mremap(), can leave an empty space, therefore - can be replaced with a VMA with a new set of attributes. - - Moving or expanding a different VMA into the current location, - via mremap(). - - Modifying a VMA via mmap(MAP_FIXED). - - Size expansion, via mremap(), does not appear to pose any - specific risks to sealed VMAs. It is included anyway because - the use case is unclear. In any case, users can rely on - merging to expand a sealed VMA. - - mprotect() and pkey_mprotect(). - - Some destructive madvice() behaviors (e.g. MADV_DONTNEED) - for anonymous memory, when users don't have write permission to the - memory. Those behaviors can alter region contents by discarding pages, - effectively a memset(0) for anonymous memory. - - Kernel will return -EPERM for blocked operations. - - For blocked operations, one can expect the given address is unmodified, - i.e. no partial update. Note, this is different from existing mm - system call behaviors, where partial updates are made till an error is - found and returned to userspace. To give an example: - - Assume following code sequence: - - - ptr = mmap(null, 8192, PROT_NONE); - - munmap(ptr + 4096, 4096); - - ret1 = mprotect(ptr, 8192, PROT_READ); - - mseal(ptr, 4096); - - ret2 = mprotect(ptr, 8192, PROT_NONE); - - ret1 will be -ENOMEM, the page from ptr is updated to PROT_READ. - - ret2 will be -EPERM, the page remains to be PROT_READ. - -**Note**: - -- mseal() only works on 64-bit CPUs, not 32-bit CPU. - -- users can call mseal() multiple times, mseal() on an already sealed memory - is a no-action (not error). - -- munseal() is not supported. - -Use cases: -========== +SYSCALL +======= +mseal syscall signature +----------------------- + ``int mseal(void \* addr, size_t len, unsigned long flags)`` + + **addr**/**len**: virtual memory address range. + The address range set by **addr**/**len** must meet: + - The start address must be in an allocated VMA. + - The start address must be page aligned. + - The end address (**addr** + **len**) must be in an allocated VMA. + - no gap (unallocated memory) between start and end address. + + The ``len`` will be paged aligned implicitly by the kernel. + + **flags**: reserved for future use. + + **Return values**: + - **0**: Success. + - **-EINVAL**: + * Invalid input ``flags``. + * The start address (``addr``) is not page aligned. + * Address range (``addr`` + ``len``) overflow. + - **-ENOMEM**: + * The start address (``addr``) is not allocated. + * The end address (``addr`` + ``len``) is not allocated. + * A gap (unallocated memory) between start and end address. + - **-EPERM**: + * sealing is supported only on 64-bit CPUs, 32-bit is not supported. + + **Note about error return**: + - For above error cases, users can expect the given memory range is + unmodified, i.e. no partial update. + - There might be other internal errors/cases not listed here, e.g. + error during merging/splitting VMAs, or the process reaching the maximum + number of supported VMAs. In those cases, partial updates to the given + memory range could happen. However, those cases should be rare. + + **Architecture support**: + mseal only works on 64-bit CPUs, not 32-bit CPUs. + + **Idempotent**: + users can call mseal multiple times. mseal on an already sealed memory + is a no-action (not error). + + **no munseal** + Once mapping is sealed, it can't be unsealed. The kernel should never + have munseal, this is consistent with other sealing feature, e.g. + F_SEAL_SEAL for file. + +Blocked mm syscall for sealed mapping +------------------------------------- + It might be important to note: **once the mapping is sealed, it will + stay in the process's memory until the process terminates**. + + Example:: + + *ptr = mmap(0, 4096, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); + rc = mseal(ptr, 4096, 0); + /* munmap will fail */ + rc = munmap(ptr, 4096); + assert(rc < 0); + + Blocked mm syscall: + - munmap + - mmap + - mremap + - mprotect and pkey_mprotect + - some destructive madvise behaviors: MADV_DONTNEED, MADV_FREE, + MADV_DONTNEED_LOCKED, MADV_FREE, MADV_DONTFORK, MADV_WIPEONFORK + + The first set of syscalls to block is munmap, mremap, mmap. They can + either leave an empty space in the address space, therefore allowing + replacement with a new mapping with new set of attributes, or can + overwrite the existing mapping with another mapping. + + mprotect and pkey_mprotect are blocked because they changes the + protection bits (RWX) of the mapping. + + Certain destructive madvise behaviors, specifically MADV_DONTNEED, + MADV_FREE, MADV_DONTNEED_LOCKED, and MADV_WIPEONFORK, can introduce + risks when applied to anonymous memory by threads lacking write + permissions. Consequently, these operations are prohibited under such + conditions. The aforementioned behaviors have the potential to modify + region contents by discarding pages, effectively performing a memset(0) + operation on the anonymous memory. + + Kernel will return -EPERM for blocked syscalls. + + When blocked syscall return -EPERM due to sealing, the memory regions may + or may not be changed, depends on the syscall being blocked: + + - munmap: munmap is atomic. If one of VMAs in the given range is + sealed, none of VMAs are updated. + - mprotect, pkey_mprotect, madvise: partial update might happen, e.g. + when mprotect over multiple VMAs, mprotect might update the beginning + VMAs before reaching the sealed VMA and return -EPERM. + - mmap and mremap: undefined behavior. + +Use cases +========= - glibc: The dynamic linker, during loading ELF executables, can apply sealing to - non-writable memory segments. - -- Chrome browser: protect some security sensitive data-structures. + mapping segments. -Notes on which memory to seal: -============================== +- Chrome browser: protect some security sensitive data structures. -It might be important to note that sealing changes the lifetime of a mapping, -i.e. the sealed mapping won’t be unmapped till the process terminates or the -exec system call is invoked. Applications can apply sealing to any virtual -memory region from userspace, but it is crucial to thoroughly analyze the -mapping's lifetime prior to apply the sealing. +When not to use mseal +===================== +Applications can apply sealing to any virtual memory region from userspace, +but it is *crucial to thoroughly analyze the mapping's lifetime* prior to +apply the sealing. This is because the sealed mapping *won’t be unmapped* +until the process terminates or the exec system call is invoked. For example: + - aio/shm + aio/shm can call mmap and munmap on behalf of userspace, e.g. + ksys_shmdt() in shm.c. The lifetimes of those mapping are not tied to + the lifetime of the process. If those memories are sealed from userspace, + then munmap will fail, causing leaks in VMA address space during the + lifetime of the process. + + - ptr allocated by malloc (heap) + Don't use mseal on the memory ptr return from malloc(). + malloc() is implemented by allocator, e.g. by glibc. Heap manager might + allocate a ptr from brk or mapping created by mmap. + If an app calls mseal on a ptr returned from malloc(), this can affect + the heap manager's ability to manage the mappings; the outcome is + non-deterministic. + + Example:: + + ptr = malloc(size); + /* don't call mseal on ptr return from malloc. */ + mseal(ptr, size); + /* free will success, allocator can't shrink heap lower than ptr */ + free(ptr); + +mseal doesn't block +=================== +In a nutshell, mseal blocks certain mm syscall from modifying some of VMA's +attributes, such as protection bits (RWX). Sealed mappings doesn't mean the +memory is immutable. -- aio/shm - - aio/shm can call mmap()/munmap() on behalf of userspace, e.g. ksys_shmdt() in - shm.c. The lifetime of those mapping are not tied to the lifetime of the - process. If those memories are sealed from userspace, then munmap() will fail, - causing leaks in VMA address space during the lifetime of the process. - -- Brk (heap) - - Currently, userspace applications can seal parts of the heap by calling - malloc() and mseal(). - let's assume following calls from user space: - - - ptr = malloc(size); - - mprotect(ptr, size, RO); - - mseal(ptr, size); - - free(ptr); - - Technically, before mseal() is added, the user can change the protection of - the heap by calling mprotect(RO). As long as the user changes the protection - back to RW before free(), the memory range can be reused. - - Adding mseal() into the picture, however, the heap is then sealed partially, - the user can still free it, but the memory remains to be RO. If the address - is re-used by the heap manager for another malloc, the process might crash - soon after. Therefore, it is important not to apply sealing to any memory - that might get recycled. - - Furthermore, even if the application never calls the free() for the ptr, - the heap manager may invoke the brk system call to shrink the size of the - heap. In the kernel, the brk-shrink will call munmap(). Consequently, - depending on the location of the ptr, the outcome of brk-shrink is - nondeterministic. - - -Additional notes: -================= As Jann Horn pointed out in [3], there are still a few ways to write -to RO memory, which is, in a way, by design. Those cases are not covered -by mseal(). If applications want to block such cases, sandbox tools (such as -seccomp, LSM, etc) might be considered. +to RO memory, which is, in a way, by design. And those could be blocked +by different security measures. Those cases are: -- Write to read-only memory through /proc/self/mem interface. -- Write to read-only memory through ptrace (such as PTRACE_POKETEXT). -- userfaultfd. + - Write to read-only memory through /proc/self/mem interface (FOLL_FORCE). + - Write to read-only memory through ptrace (such as PTRACE_POKETEXT). + - userfaultfd. The idea that inspired this patch comes from Stephen Röttger’s work in V8 CFI [4]. Chrome browser in ChromeOS will be the first user of this API. -Reference: -========== -[1] https://github.com/apple-oss-distributions/xnu/blob/1031c584a5e37aff177559b9f69dbd3c8c3fd30a/osfmk/mach/vm_statistics.h#L274 - -[2] https://man.openbsd.org/mimmutable.2 - -[3] https://lore.kernel.org/lkml/CAG48ez3ShUYey+ZAFsU2i1RpQn0a5eOs2hzQ426FkcgnfUGLvA@mail.gmail.com - -[4] https://docs.google.com/document/d/1O2jwK4dxI3nRcOJuPYkonhTkNQfbmwdvxQMyXgeaRHo/edit#heading=h.bvaojj9fu6hc +Reference +========= +- [1] https://github.com/apple-oss-distributions/xnu/blob/1031c584a5e37aff177559b9f69dbd3c8c3fd30a/osfmk/mach/vm_statistics.h#L274 +- [2] https://man.openbsd.org/mimmutable.2 +- [3] https://lore.kernel.org/lkml/CAG48ez3ShUYey+ZAFsU2i1RpQn0a5eOs2hzQ426FkcgnfUGLvA@mail.gmail.com +- [4] https://docs.google.com/document/d/1O2jwK4dxI3nRcOJuPYkonhTkNQfbmwdvxQMyXgeaRHo/edit#heading=h.bvaojj9fu6hc diff --git a/MAINTAINERS b/MAINTAINERS index a27407950242..cd6aa609deba 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14141,6 +14141,15 @@ S: Maintained T: git git://linuxtv.org/media_tree.git F: drivers/media/platform/nxp/imx-pxp.[ch] +MEDIA DRIVERS FOR ASCOT2E +M: Abylay Ospan <aospan@amazon.com> +L: linux-media@vger.kernel.org +S: Supported +W: https://linuxtv.org +W: http://netup.tv/ +T: git git://linuxtv.org/media_tree.git +F: drivers/media/dvb-frontends/ascot2e* + MEDIA DRIVERS FOR CXD2099AR CI CONTROLLERS M: Jasmin Jessich <jasmin@anw.at> L: linux-media@vger.kernel.org @@ -14149,6 +14158,15 @@ W: https://linuxtv.org T: git git://linuxtv.org/media_tree.git F: drivers/media/dvb-frontends/cxd2099* +MEDIA DRIVERS FOR CXD2841ER +M: Abylay Ospan <aospan@amazon.com> +L: linux-media@vger.kernel.org +S: Supported +W: https://linuxtv.org +W: http://netup.tv/ +T: git git://linuxtv.org/media_tree.git +F: drivers/media/dvb-frontends/cxd2841er* + MEDIA DRIVERS FOR CXD2880 M: Yasunari Takiguchi <Yasunari.Takiguchi@sony.com> L: linux-media@vger.kernel.org @@ -14193,6 +14211,33 @@ F: drivers/media/platform/nxp/imx-mipi-csis.c F: drivers/media/platform/nxp/imx7-media-csi.c F: drivers/media/platform/nxp/imx8mq-mipi-csi2.c +MEDIA DRIVERS FOR HELENE +M: Abylay Ospan <aospan@amazon.com> +L: linux-media@vger.kernel.org +S: Supported +W: https://linuxtv.org +W: http://netup.tv/ +T: git git://linuxtv.org/media_tree.git +F: drivers/media/dvb-frontends/helene* + +MEDIA DRIVERS FOR HORUS3A +M: Abylay Ospan <aospan@amazon.com> +L: linux-media@vger.kernel.org +S: Supported +W: https://linuxtv.org +W: http://netup.tv/ +T: git git://linuxtv.org/media_tree.git +F: drivers/media/dvb-frontends/horus3a* + +MEDIA DRIVERS FOR LNBH25 +M: Abylay Ospan <aospan@amazon.com> +L: linux-media@vger.kernel.org +S: Supported +W: https://linuxtv.org +W: http://netup.tv/ +T: git git://linuxtv.org/media_tree.git +F: drivers/media/dvb-frontends/lnbh25* + MEDIA DRIVERS FOR MXL5XX TUNER DEMODULATORS L: linux-media@vger.kernel.org S: Orphan @@ -14200,6 +14245,15 @@ W: https://linuxtv.org T: git git://linuxtv.org/media_tree.git F: drivers/media/dvb-frontends/mxl5xx* +MEDIA DRIVERS FOR NETUP PCI UNIVERSAL DVB devices +M: Abylay Ospan <aospan@amazon.com> +L: linux-media@vger.kernel.org +S: Supported +W: https://linuxtv.org +W: http://netup.tv/ +T: git git://linuxtv.org/media_tree.git +F: drivers/media/pci/netup_unidvb/* + MEDIA DRIVERS FOR NVIDIA TEGRA - VDE M: Dmitry Osipenko <digetx@gmail.com> L: linux-media@vger.kernel.org @@ -24003,6 +24057,13 @@ L: linux-usb@vger.kernel.org S: Orphan F: drivers/usb/typec/tcpm/ +USB TYPEC TUSB1046 MUX DRIVER +M: Romain Gantois <romain.gantois@bootlin.com> +L: linux-usb@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/usb/ti,tusb1046.yaml +F: drivers/usb/typec/mux/tusb1046.c + USB UHCI DRIVER M: Alan Stern <stern@rowland.harvard.edu> L: linux-usb@vger.kernel.org @@ -2,7 +2,7 @@ VERSION = 6 PATCHLEVEL = 12 SUBLEVEL = 0 -EXTRAVERSION = -rc5 +EXTRAVERSION = -rc6 NAME = Baby Opossum Posse # *DOCUMENTATION* diff --git a/arch/Kconfig b/arch/Kconfig index 00163e4a237c..bd9f095d69fa 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -855,14 +855,14 @@ config HAVE_CFI_ICALL_NORMALIZE_INTEGERS_CLANG def_bool y depends on $(cc-option,-fsanitize=kcfi -fsanitize-cfi-icall-experimental-normalize-integers) # With GCOV/KASAN we need this fix: https://github.com/llvm/llvm-project/pull/104826 - depends on CLANG_VERSION >= 190000 || (!GCOV_KERNEL && !KASAN_GENERIC && !KASAN_SW_TAGS) + depends on CLANG_VERSION >= 190103 || (!GCOV_KERNEL && !KASAN_GENERIC && !KASAN_SW_TAGS) config HAVE_CFI_ICALL_NORMALIZE_INTEGERS_RUSTC def_bool y depends on HAVE_CFI_ICALL_NORMALIZE_INTEGERS_CLANG depends on RUSTC_VERSION >= 107900 # With GCOV/KASAN we need this fix: https://github.com/rust-lang/rust/pull/129373 - depends on (RUSTC_LLVM_VERSION >= 190000 && RUSTC_VERSION >= 108200) || \ + depends on (RUSTC_LLVM_VERSION >= 190103 && RUSTC_VERSION >= 108200) || \ (!GCOV_KERNEL && !KASAN_GENERIC && !KASAN_SW_TAGS) config CFI_PERMISSIVE diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c index 561986947530..c7d311d8b92a 100644 --- a/arch/arm64/kernel/signal.c +++ b/arch/arm64/kernel/signal.c @@ -19,6 +19,7 @@ #include <linux/ratelimit.h> #include <linux/rseq.h> #include <linux/syscalls.h> +#include <linux/pkeys.h> #include <asm/daifflags.h> #include <asm/debug-monitors.h> @@ -66,10 +67,63 @@ struct rt_sigframe_user_layout { unsigned long end_offset; }; +/* + * Holds any EL0-controlled state that influences unprivileged memory accesses. + * This includes both accesses done in userspace and uaccess done in the kernel. + * + * This state needs to be carefully managed to ensure that it doesn't cause + * uaccess to fail when setting up the signal frame, and the signal handler + * itself also expects a well-defined state when entered. + */ +struct user_access_state { + u64 por_el0; +}; + #define BASE_SIGFRAME_SIZE round_up(sizeof(struct rt_sigframe), 16) #define TERMINATOR_SIZE round_up(sizeof(struct _aarch64_ctx), 16) #define EXTRA_CONTEXT_SIZE round_up(sizeof(struct extra_context), 16) +/* + * Save the user access state into ua_state and reset it to disable any + * restrictions. + */ +static void save_reset_user_access_state(struct user_access_state *ua_state) +{ + if (system_supports_poe()) { + u64 por_enable_all = 0; + + for (int pkey = 0; pkey < arch_max_pkey(); pkey++) + por_enable_all |= POE_RXW << (pkey * POR_BITS_PER_PKEY); + + ua_state->por_el0 = read_sysreg_s(SYS_POR_EL0); + write_sysreg_s(por_enable_all, SYS_POR_EL0); + /* Ensure that any subsequent uaccess observes the updated value */ + isb(); + } +} + +/* + * Set the user access state for invoking the signal handler. + * + * No uaccess should be done after that function is called. + */ +static void set_handler_user_access_state(void) +{ + if (system_supports_poe()) + write_sysreg_s(POR_EL0_INIT, SYS_POR_EL0); +} + +/* + * Restore the user access state to the values saved in ua_state. + * + * No uaccess should be done after that function is called. + */ +static void restore_user_access_state(const struct user_access_state *ua_state) +{ + if (system_supports_poe()) + write_sysreg_s(ua_state->por_el0, SYS_POR_EL0); +} + static void init_user_layout(struct rt_sigframe_user_layout *user) { const size_t reserved_size = @@ -261,18 +315,20 @@ static int restore_fpmr_context(struct user_ctxs *user) return err; } -static int preserve_poe_context(struct poe_context __user *ctx) +static int preserve_poe_context(struct poe_context __user *ctx, + const struct user_access_state *ua_state) { int err = 0; __put_user_error(POE_MAGIC, &ctx->head.magic, err); __put_user_error(sizeof(*ctx), &ctx->head.size, err); - __put_user_error(read_sysreg_s(SYS_POR_EL0), &ctx->por_el0, err); + __put_user_error(ua_state->por_el0, &ctx->por_el0, err); return err; } -static int restore_poe_context(struct user_ctxs *user) +static int restore_poe_context(struct user_ctxs *user, + struct user_access_state *ua_state) { u64 por_el0; int err = 0; @@ -282,7 +338,7 @@ static int restore_poe_context(struct user_ctxs *user) __get_user_error(por_el0, &(user->poe->por_el0), err); if (!err) - write_sysreg_s(por_el0, SYS_POR_EL0); + ua_state->por_el0 = por_el0; return err; } @@ -850,7 +906,8 @@ invalid: } static int restore_sigframe(struct pt_regs *regs, - struct rt_sigframe __user *sf) + struct rt_sigframe __user *sf, + struct user_access_state *ua_state) { sigset_t set; int i, err; @@ -899,7 +956,7 @@ static int restore_sigframe(struct pt_regs *regs, err = restore_zt_context(&user); if (err == 0 && system_supports_poe() && user.poe) - err = restore_poe_context(&user); + err = restore_poe_context(&user, ua_state); return err; } @@ -908,6 +965,7 @@ SYSCALL_DEFINE0(rt_sigreturn) { struct pt_regs *regs = current_pt_regs(); struct rt_sigframe __user *frame; + struct user_access_state ua_state; /* Always make any pending restarted system calls return -EINTR */ current->restart_block.fn = do_no_restart_syscall; @@ -924,12 +982,14 @@ SYSCALL_DEFINE0(rt_sigreturn) if (!access_ok(frame, sizeof (*frame))) goto badframe; - if (restore_sigframe(regs, frame)) + if (restore_sigframe(regs, frame, &ua_state)) goto badframe; if (restore_altstack(&frame->uc.uc_stack)) goto badframe; + restore_user_access_state(&ua_state); + return regs->regs[0]; badframe: @@ -1035,7 +1095,8 @@ static int setup_sigframe_layout(struct rt_sigframe_user_layout *user, } static int setup_sigframe(struct rt_sigframe_user_layout *user, - struct pt_regs *regs, sigset_t *set) + struct pt_regs *regs, sigset_t *set, + const struct user_access_state *ua_state) { int i, err = 0; struct rt_sigframe __user *sf = user->sigframe; @@ -1097,10 +1158,9 @@ static int setup_sigframe(struct rt_sigframe_user_layout *user, struct poe_context __user *poe_ctx = apply_user_offset(user, user->poe_offset); - err |= preserve_poe_context(poe_ctx); + err |= preserve_poe_context(poe_ctx, ua_state); } - /* ZA state if present */ if (system_supports_sme() && err == 0 && user->za_offset) { struct za_context __user *za_ctx = @@ -1237,9 +1297,6 @@ static void setup_return(struct pt_regs *regs, struct k_sigaction *ka, sme_smstop(); } - if (system_supports_poe()) - write_sysreg_s(POR_EL0_INIT, SYS_POR_EL0); - if (ka->sa.sa_flags & SA_RESTORER) sigtramp = ka->sa.sa_restorer; else @@ -1253,6 +1310,7 @@ static int setup_rt_frame(int usig, struct ksignal *ksig, sigset_t *set, { struct rt_sigframe_user_layout user; struct rt_sigframe __user *frame; + struct user_access_state ua_state; int err = 0; fpsimd_signal_preserve_current_state(); @@ -1260,13 +1318,14 @@ static int setup_rt_frame(int usig, struct ksignal *ksig, sigset_t *set, if (get_sigframe(&user, ksig, regs)) return 1; + save_reset_user_access_state(&ua_state); frame = user.sigframe; __put_user_error(0, &frame->uc.uc_flags, err); __put_user_error(NULL, &frame->uc.uc_link, err); err |= __save_altstack(&frame->uc.uc_stack, regs->sp); - err |= setup_sigframe(&user, regs, set); + err |= setup_sigframe(&user, regs, set, &ua_state); if (err == 0) { setup_return(regs, &ksig->ka, &user, usig); if (ksig->ka.sa.sa_flags & SA_SIGINFO) { @@ -1276,6 +1335,11 @@ static int setup_rt_frame(int usig, struct ksignal *ksig, sigset_t *set, } } + if (err == 0) + set_handler_user_access_state(); + else + restore_user_access_state(&ua_state); + return err; } diff --git a/arch/mips/kernel/cmpxchg.c b/arch/mips/kernel/cmpxchg.c index e974a4954df8..c371def2302d 100644 --- a/arch/mips/kernel/cmpxchg.c +++ b/arch/mips/kernel/cmpxchg.c @@ -102,3 +102,4 @@ unsigned long __cmpxchg_small(volatile void *ptr, unsigned long old, return old; } } +EXPORT_SYMBOL(__cmpxchg_small); diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 62545946ecf4..f4c570538d55 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -177,7 +177,7 @@ config RISCV select HAVE_REGS_AND_STACK_ACCESS_API select HAVE_RETHOOK if !XIP_KERNEL select HAVE_RSEQ - select HAVE_RUST if RUSTC_SUPPORTS_RISCV + select HAVE_RUST if RUSTC_SUPPORTS_RISCV && CC_IS_CLANG select HAVE_SAMPLE_FTRACE_DIRECT select HAVE_SAMPLE_FTRACE_DIRECT_MULTI select HAVE_STACKPROTECTOR diff --git a/arch/riscv/errata/Makefile b/arch/riscv/errata/Makefile index 8a2739485123..f0da9d7b39c3 100644 --- a/arch/riscv/errata/Makefile +++ b/arch/riscv/errata/Makefile @@ -2,6 +2,12 @@ ifdef CONFIG_RELOCATABLE KBUILD_CFLAGS += -fno-pie endif +ifdef CONFIG_RISCV_ALTERNATIVE_EARLY +ifdef CONFIG_FORTIFY_SOURCE +KBUILD_CFLAGS += -D__NO_FORTIFY +endif +endif + obj-$(CONFIG_ERRATA_ANDES) += andes/ obj-$(CONFIG_ERRATA_SIFIVE) += sifive/ obj-$(CONFIG_ERRATA_THEAD) += thead/ diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile index 7f88cc4931f5..69dc8aaab3fb 100644 --- a/arch/riscv/kernel/Makefile +++ b/arch/riscv/kernel/Makefile @@ -36,6 +36,11 @@ KASAN_SANITIZE_alternative.o := n KASAN_SANITIZE_cpufeature.o := n KASAN_SANITIZE_sbi_ecall.o := n endif +ifdef CONFIG_FORTIFY_SOURCE +CFLAGS_alternative.o += -D__NO_FORTIFY +CFLAGS_cpufeature.o += -D__NO_FORTIFY +CFLAGS_sbi_ecall.o += -D__NO_FORTIFY +endif endif extra-y += vmlinux.lds diff --git a/arch/riscv/kernel/acpi.c b/arch/riscv/kernel/acpi.c index 6e0d333f57e5..2fd29695a788 100644 --- a/arch/riscv/kernel/acpi.c +++ b/arch/riscv/kernel/acpi.c @@ -210,7 +210,7 @@ void __init __iomem *__acpi_map_table(unsigned long phys, unsigned long size) if (!size) return NULL; - return early_ioremap(phys, size); + return early_memremap(phys, size); } void __init __acpi_unmap_table(void __iomem *map, unsigned long size) @@ -218,7 +218,7 @@ void __init __acpi_unmap_table(void __iomem *map, unsigned long size) if (!map || !size) return; - early_iounmap(map, size); + early_memunmap(map, size); } void __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size) diff --git a/arch/riscv/kernel/asm-offsets.c b/arch/riscv/kernel/asm-offsets.c index e94180ba432f..c2f3129a8e5c 100644 --- a/arch/riscv/kernel/asm-offsets.c +++ b/arch/riscv/kernel/asm-offsets.c @@ -4,8 +4,6 @@ * Copyright (C) 2017 SiFive */ -#define GENERATING_ASM_OFFSETS - #include <linux/kbuild.h> #include <linux/mm.h> #include <linux/sched.h> diff --git a/arch/riscv/kernel/cacheinfo.c b/arch/riscv/kernel/cacheinfo.c index b320b1d9aa01..2d40736fc37c 100644 --- a/arch/riscv/kernel/cacheinfo.c +++ b/arch/riscv/kernel/cacheinfo.c @@ -80,8 +80,7 @@ int populate_cache_leaves(unsigned int cpu) { struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); struct cacheinfo *this_leaf = this_cpu_ci->info_list; - struct device_node *np = of_cpu_device_node_get(cpu); - struct device_node *prev = NULL; + struct device_node *np, *prev; int levels = 1, level = 1; if (!acpi_disabled) { @@ -105,6 +104,10 @@ int populate_cache_leaves(unsigned int cpu) return 0; } + np = of_cpu_device_node_get(cpu); + if (!np) + return -ENOENT; + if (of_property_read_bool(np, "cache-size")) ci_leaf_init(this_leaf++, CACHE_TYPE_UNIFIED, level); if (of_property_read_bool(np, "i-cache-size")) diff --git a/arch/riscv/kernel/cpu-hotplug.c b/arch/riscv/kernel/cpu-hotplug.c index 28b58fc5ad19..a1e38ecfc8be 100644 --- a/arch/riscv/kernel/cpu-hotplug.c +++ b/arch/riscv/kernel/cpu-hotplug.c @@ -58,7 +58,7 @@ void arch_cpuhp_cleanup_dead_cpu(unsigned int cpu) if (cpu_ops->cpu_is_stopped) ret = cpu_ops->cpu_is_stopped(cpu); if (ret) - pr_warn("CPU%d may not have stopped: %d\n", cpu, ret); + pr_warn("CPU%u may not have stopped: %d\n", cpu, ret); } /* diff --git a/arch/riscv/kernel/efi-header.S b/arch/riscv/kernel/efi-header.S index 515b2dfbca75..c5f17c2710b5 100644 --- a/arch/riscv/kernel/efi-header.S +++ b/arch/riscv/kernel/efi-header.S @@ -64,7 +64,7 @@ extra_header_fields: .long efi_header_end - _start // SizeOfHeaders .long 0 // CheckSum .short IMAGE_SUBSYSTEM_EFI_APPLICATION // Subsystem - .short 0 // DllCharacteristics + .short IMAGE_DLL_CHARACTERISTICS_NX_COMPAT // DllCharacteristics .quad 0 // SizeOfStackReserve .quad 0 // SizeOfStackCommit .quad 0 // SizeOfHeapReserve diff --git a/arch/riscv/kernel/pi/Makefile b/arch/riscv/kernel/pi/Makefile index d5bf1bc7de62..81d69d45c06c 100644 --- a/arch/riscv/kernel/pi/Makefile +++ b/arch/riscv/kernel/pi/Makefile @@ -16,8 +16,12 @@ KBUILD_CFLAGS := $(filter-out $(CC_FLAGS_LTO), $(KBUILD_CFLAGS)) KBUILD_CFLAGS += -mcmodel=medany CFLAGS_cmdline_early.o += -D__NO_FORTIFY -CFLAGS_lib-fdt_ro.o += -D__NO_FORTIFY CFLAGS_fdt_early.o += -D__NO_FORTIFY +# lib/string.c already defines __NO_FORTIFY +CFLAGS_ctype.o += -D__NO_FORTIFY +CFLAGS_lib-fdt.o += -D__NO_FORTIFY +CFLAGS_lib-fdt_ro.o += -D__NO_FORTIFY +CFLAGS_archrandom_early.o += -D__NO_FORTIFY $(obj)/%.pi.o: OBJCOPYFLAGS := --prefix-symbols=__pi_ \ --remove-section=.note.gnu.property \ diff --git a/arch/riscv/kernel/traps_misaligned.c b/arch/riscv/kernel/traps_misaligned.c index d4fd8af7aaf5..1b9867136b61 100644 --- a/arch/riscv/kernel/traps_misaligned.c +++ b/arch/riscv/kernel/traps_misaligned.c @@ -136,8 +136,6 @@ #define REG_PTR(insn, pos, regs) \ (ulong *)((ulong)(regs) + REG_OFFSET(insn, pos)) -#define GET_RM(insn) (((insn) >> 12) & 7) - #define GET_RS1(insn, regs) (*REG_PTR(insn, SH_RS1, regs)) #define GET_RS2(insn, regs) (*REG_PTR(insn, SH_RS2, regs)) #define GET_RS1S(insn, regs) (*REG_PTR(RVC_RS1S(insn), 0, regs)) diff --git a/arch/riscv/kernel/vdso/Makefile b/arch/riscv/kernel/vdso/Makefile index 960feb1526ca..3f1c4b2d0b06 100644 --- a/arch/riscv/kernel/vdso/Makefile +++ b/arch/riscv/kernel/vdso/Makefile @@ -18,6 +18,7 @@ obj-vdso = $(patsubst %, %.o, $(vdso-syms)) note.o ccflags-y := -fno-stack-protector ccflags-y += -DDISABLE_BRANCH_PROFILING +ccflags-y += -fno-builtin ifneq ($(c-gettimeofday-y),) CFLAGS_vgettimeofday.o += -fPIC -include $(c-gettimeofday-y) diff --git a/arch/x86/include/asm/amd_nb.h b/arch/x86/include/asm/amd_nb.h index 6f3b6aef47ba..d0caac26533f 100644 --- a/arch/x86/include/asm/amd_nb.h +++ b/arch/x86/include/asm/amd_nb.h @@ -116,7 +116,10 @@ static inline bool amd_gart_present(void) #define amd_nb_num(x) 0 #define amd_nb_has_feature(x) false -#define node_to_amd_nb(x) NULL +static inline struct amd_northbridge *node_to_amd_nb(int node) +{ + return NULL; +} #define amd_gart_present(x) false #endif diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index d05392db5d0f..2dbadf347b5f 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -261,12 +261,6 @@ static noinstr bool handle_bug(struct pt_regs *regs) int ud_type; u32 imm; - /* - * Normally @regs are unpoisoned by irqentry_enter(), but handle_bug() - * is a rare case that uses @regs without passing them to - * irqentry_enter(). - */ - kmsan_unpoison_entry_regs(regs); ud_type = decode_bug(regs->ip, &imm); if (ud_type == BUG_NONE) return handled; @@ -276,6 +270,12 @@ static noinstr bool handle_bug(struct pt_regs *regs) */ instrumentation_begin(); /* + * Normally @regs are unpoisoned by irqentry_enter(), but handle_bug() + * is a rare case that uses @regs without passing them to + * irqentry_enter(). + */ + kmsan_unpoison_entry_regs(regs); + /* * Since we're emulating a CALL with exceptions, restore the interrupt * state to what it was at the exception site. */ diff --git a/block/blk-map.c b/block/blk-map.c index 6ef2ec1f7d78..b5fd1d857461 100644 --- a/block/blk-map.c +++ b/block/blk-map.c @@ -561,55 +561,33 @@ EXPORT_SYMBOL(blk_rq_append_bio); /* Prepare bio for passthrough IO given ITER_BVEC iter */ static int blk_rq_map_user_bvec(struct request *rq, const struct iov_iter *iter) { - struct request_queue *q = rq->q; - size_t nr_iter = iov_iter_count(iter); - size_t nr_segs = iter->nr_segs; - struct bio_vec *bvecs, *bvprvp = NULL; - const struct queue_limits *lim = &q->limits; - unsigned int nsegs = 0, bytes = 0; + const struct queue_limits *lim = &rq->q->limits; + unsigned int max_bytes = lim->max_hw_sectors << SECTOR_SHIFT; + unsigned int nsegs; struct bio *bio; - size_t i; + int ret; - if (!nr_iter || (nr_iter >> SECTOR_SHIFT) > queue_max_hw_sectors(q)) - return -EINVAL; - if (nr_segs > queue_max_segments(q)) + if (!iov_iter_count(iter) || iov_iter_count(iter) > max_bytes) return -EINVAL; - /* no iovecs to alloc, as we already have a BVEC iterator */ + /* reuse the bvecs from the iterator instead of allocating new ones */ bio = blk_rq_map_bio_alloc(rq, 0, GFP_KERNEL); - if (bio == NULL) + if (!bio) return -ENOMEM; - bio_iov_bvec_set(bio, (struct iov_iter *)iter); - blk_rq_bio_prep(rq, bio, nr_segs); - - /* loop to perform a bunch of sanity checks */ - bvecs = (struct bio_vec *)iter->bvec; - for (i = 0; i < nr_segs; i++) { - struct bio_vec *bv = &bvecs[i]; - - /* - * If the queue doesn't support SG gaps and adding this - * offset would create a gap, fallback to copy. - */ - if (bvprvp && bvec_gap_to_prev(lim, bvprvp, bv->bv_offset)) { - blk_mq_map_bio_put(bio); - return -EREMOTEIO; - } - /* check full condition */ - if (nsegs >= nr_segs || bytes > UINT_MAX - bv->bv_len) - goto put_bio; - if (bytes + bv->bv_len > nr_iter) - break; - nsegs++; - bytes += bv->bv_len; - bvprvp = bv; + /* check that the data layout matches the hardware restrictions */ + ret = bio_split_rw_at(bio, lim, &nsegs, max_bytes); + if (ret) { + /* if we would have to split the bio, copy instead */ + if (ret > 0) + ret = -EREMOTEIO; + blk_mq_map_bio_put(bio); + return ret; } + + blk_rq_bio_prep(rq, bio, nsegs); return 0; -put_bio: - blk_mq_map_bio_put(bio); - return -EINVAL; } /** diff --git a/drivers/accel/ivpu/ivpu_debugfs.c b/drivers/accel/ivpu/ivpu_debugfs.c index 6f86f8df30db..8d50981594d1 100644 --- a/drivers/accel/ivpu/ivpu_debugfs.c +++ b/drivers/accel/ivpu/ivpu_debugfs.c @@ -108,6 +108,14 @@ static int reset_pending_show(struct seq_file *s, void *v) return 0; } +static int firewall_irq_counter_show(struct seq_file *s, void *v) +{ + struct ivpu_device *vdev = seq_to_ivpu(s); + + seq_printf(s, "%d\n", atomic_read(&vdev->hw->firewall_irq_counter)); + return 0; +} + static const struct drm_debugfs_info vdev_debugfs_list[] = { {"bo_list", bo_list_show, 0}, {"fw_name", fw_name_show, 0}, @@ -116,6 +124,7 @@ static const struct drm_debugfs_info vdev_debugfs_list[] = { {"last_bootmode", last_bootmode_show, 0}, {"reset_counter", reset_counter_show, 0}, {"reset_pending", reset_pending_show, 0}, + {"firewall_irq_counter", firewall_irq_counter_show, 0}, }; static ssize_t diff --git a/drivers/accel/ivpu/ivpu_hw.c b/drivers/accel/ivpu/ivpu_hw.c index 27f0fe4d54e0..e69c0613513f 100644 --- a/drivers/accel/ivpu/ivpu_hw.c +++ b/drivers/accel/ivpu/ivpu_hw.c @@ -249,6 +249,7 @@ int ivpu_hw_init(struct ivpu_device *vdev) platform_init(vdev); wa_init(vdev); timeouts_init(vdev); + atomic_set(&vdev->hw->firewall_irq_counter, 0); return 0; } diff --git a/drivers/accel/ivpu/ivpu_hw.h b/drivers/accel/ivpu/ivpu_hw.h index 1c0c98e3afb8..a96a05b2acda 100644 --- a/drivers/accel/ivpu/ivpu_hw.h +++ b/drivers/accel/ivpu/ivpu_hw.h @@ -52,6 +52,7 @@ struct ivpu_hw_info { int dma_bits; ktime_t d0i3_entry_host_ts; u64 d0i3_entry_vpu_ts; + atomic_t firewall_irq_counter; }; int ivpu_hw_init(struct ivpu_device *vdev); diff --git a/drivers/accel/ivpu/ivpu_hw_ip.c b/drivers/accel/ivpu/ivpu_hw_ip.c index dfd2f4a5b526..60b33fc59d96 100644 --- a/drivers/accel/ivpu/ivpu_hw_ip.c +++ b/drivers/accel/ivpu/ivpu_hw_ip.c @@ -1062,7 +1062,10 @@ static void irq_wdt_mss_handler(struct ivpu_device *vdev) static void irq_noc_firewall_handler(struct ivpu_device *vdev) { - ivpu_pm_trigger_recovery(vdev, "NOC Firewall IRQ"); + atomic_inc(&vdev->hw->firewall_irq_counter); + + ivpu_dbg(vdev, IRQ, "NOC Firewall interrupt detected, counter %d\n", + atomic_read(&vdev->hw->firewall_irq_counter)); } /* Handler for IRQs from NPU core */ diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index c3fc2c05d868..1a40f0514eaa 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -867,7 +867,7 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) /* Store CPU Logical ID */ cpc_ptr->cpu_id = pr->id; - spin_lock_init(&cpc_ptr->rmw_lock); + raw_spin_lock_init(&cpc_ptr->rmw_lock); /* Parse PSD data for this CPU */ ret = acpi_get_psd(cpc_ptr, handle); @@ -1087,6 +1087,7 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val) int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu); struct cpc_reg *reg = ®_res->cpc_entry.reg; struct cpc_desc *cpc_desc; + unsigned long flags; size = GET_BIT_WIDTH(reg); @@ -1126,7 +1127,7 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val) return -ENODEV; } - spin_lock(&cpc_desc->rmw_lock); + raw_spin_lock_irqsave(&cpc_desc->rmw_lock, flags); switch (size) { case 8: prev_val = readb_relaxed(vaddr); @@ -1141,7 +1142,7 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val) prev_val = readq_relaxed(vaddr); break; default: - spin_unlock(&cpc_desc->rmw_lock); + raw_spin_unlock_irqrestore(&cpc_desc->rmw_lock, flags); return -EFAULT; } val = MASK_VAL_WRITE(reg, prev_val, val); @@ -1174,7 +1175,7 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val) } if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) - spin_unlock(&cpc_desc->rmw_lock); + raw_spin_unlock_irqrestore(&cpc_desc->rmw_lock, flags); return ret_val; } diff --git a/drivers/base/core.c b/drivers/base/core.c index a4c853411a6b..048ff98dbdfd 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -26,7 +26,6 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/pm_runtime.h> -#include <linux/rcupdate.h> #include <linux/sched/mm.h> #include <linux/sched/signal.h> #include <linux/slab.h> @@ -2634,7 +2633,6 @@ static const char *dev_uevent_name(const struct kobject *kobj) static int dev_uevent(const struct kobject *kobj, struct kobj_uevent_env *env) { const struct device *dev = kobj_to_dev(kobj); - struct device_driver *driver; int retval = 0; /* add device node properties if present */ @@ -2663,12 +2661,8 @@ static int dev_uevent(const struct kobject *kobj, struct kobj_uevent_env *env) if (dev->type && dev->type->name) add_uevent_var(env, "DEVTYPE=%s", dev->type->name); - /* Synchronize with module_remove_driver() */ - rcu_read_lock(); - driver = READ_ONCE(dev->driver); - if (driver) - add_uevent_var(env, "DRIVER=%s", driver->name); - rcu_read_unlock(); + if (dev->driver) + add_uevent_var(env, "DRIVER=%s", dev->driver->name); /* Add common DT information about the device */ of_device_uevent(dev, env); @@ -2738,8 +2732,11 @@ static ssize_t uevent_show(struct device *dev, struct device_attribute *attr, if (!env) return -ENOMEM; + /* Synchronize with really_probe() */ + device_lock(dev); /* let the kset specific function add its keys */ retval = kset->uevent_ops->uevent(&dev->kobj, env); + device_unlock(dev); if (retval) goto out; @@ -4038,6 +4035,41 @@ int device_for_each_child_reverse(struct device *parent, void *data, EXPORT_SYMBOL_GPL(device_for_each_child_reverse); /** + * device_for_each_child_reverse_from - device child iterator in reversed order. + * @parent: parent struct device. + * @from: optional starting point in child list + * @fn: function to be called for each device. + * @data: data for the callback. + * + * Iterate over @parent's child devices, starting at @from, and call @fn + * for each, passing it @data. This helper is identical to + * device_for_each_child_reverse() when @from is NULL. + * + * @fn is checked each iteration. If it returns anything other than 0, + * iteration stop and that value is returned to the caller of + * device_for_each_child_reverse_from(); + */ +int device_for_each_child_reverse_from(struct device *parent, + struct device *from, const void *data, + int (*fn)(struct device *, const void *)) +{ + struct klist_iter i; + struct device *child; + int error = 0; + + if (!parent->p) + return 0; + + klist_iter_init_node(&parent->p->klist_children, &i, + (from ? &from->p->knode_parent : NULL)); + while ((child = prev_device(&i)) && !error) + error = fn(child, data); + klist_iter_exit(&i); + return error; +} +EXPORT_SYMBOL_GPL(device_for_each_child_reverse_from); + +/** * device_find_child - device iterator for locating a particular device. * @parent: parent struct device * @match: Callback function to check device diff --git a/drivers/base/module.c b/drivers/base/module.c index c4eaa1158d54..5bc71bea883a 100644 --- a/drivers/base/module.c +++ b/drivers/base/module.c @@ -7,7 +7,6 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/string.h> -#include <linux/rcupdate.h> #include "base.h" static char *make_driver_name(const struct device_driver *drv) @@ -102,9 +101,6 @@ void module_remove_driver(const struct device_driver *drv) if (!drv) return; - /* Synchronize with dev_uevent() */ - synchronize_rcu(); - sysfs_remove_link(&drv->p->kobj, "module"); if (drv->owner) diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 854546000c92..1ff99a7091bb 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -674,6 +674,16 @@ EXPORT_SYMBOL_GPL(tpm_chip_register); */ void tpm_chip_unregister(struct tpm_chip *chip) { +#ifdef CONFIG_TCG_TPM2_HMAC + int rc; + + rc = tpm_try_get_ops(chip); + if (!rc) { + tpm2_end_auth_session(chip); + tpm_put_ops(chip); + } +#endif + tpm_del_legacy_sysfs(chip); if (tpm_is_hwrng_enabled(chip)) hwrng_unregister(&chip->hwrng); diff --git a/drivers/char/tpm/tpm-dev-common.c b/drivers/char/tpm/tpm-dev-common.c index c3fbbf4d3db7..48ff87444f85 100644 --- a/drivers/char/tpm/tpm-dev-common.c +++ b/drivers/char/tpm/tpm-dev-common.c @@ -27,6 +27,9 @@ static ssize_t tpm_dev_transmit(struct tpm_chip *chip, struct tpm_space *space, struct tpm_header *header = (void *)buf; ssize_t ret, len; + if (chip->flags & TPM_CHIP_FLAG_TPM2) + tpm2_end_auth_session(chip); + ret = tpm2_prepare_space(chip, space, buf, bufsiz); /* If the command is not implemented by the TPM, synthesize a * response with a TPM2_RC_COMMAND_CODE return for user-space. diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 5da134f12c9a..8134f002b121 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -379,10 +379,12 @@ int tpm_pm_suspend(struct device *dev) rc = tpm_try_get_ops(chip); if (!rc) { - if (chip->flags & TPM_CHIP_FLAG_TPM2) + if (chip->flags & TPM_CHIP_FLAG_TPM2) { + tpm2_end_auth_session(chip); tpm2_shutdown(chip, TPM2_SU_STATE); - else + } else { rc = tpm1_pm_suspend(chip, tpm_suspend_pcr); + } tpm_put_ops(chip); } diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c index 511c67061728..0739830904b2 100644 --- a/drivers/char/tpm/tpm2-sessions.c +++ b/drivers/char/tpm/tpm2-sessions.c @@ -333,6 +333,9 @@ void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf, } #ifdef CONFIG_TCG_TPM2_HMAC + /* The first write to /dev/tpm{rm0} will flush the session. */ + attributes |= TPM2_SA_CONTINUE_SESSION; + /* * The Architecture Guide requires us to strip trailing zeros * before computing the HMAC @@ -484,7 +487,8 @@ static void tpm2_KDFe(u8 z[EC_PT_SZ], const char *str, u8 *pt_u, u8 *pt_v, sha256_final(&sctx, out); } -static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip) +static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip, + struct tpm2_auth *auth) { struct crypto_kpp *kpp; struct kpp_request *req; @@ -543,7 +547,7 @@ static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip) sg_set_buf(&s[0], chip->null_ec_key_x, EC_PT_SZ); sg_set_buf(&s[1], chip->null_ec_key_y, EC_PT_SZ); kpp_request_set_input(req, s, EC_PT_SZ*2); - sg_init_one(d, chip->auth->salt, EC_PT_SZ); + sg_init_one(d, auth->salt, EC_PT_SZ); kpp_request_set_output(req, d, EC_PT_SZ); crypto_kpp_compute_shared_secret(req); kpp_request_free(req); @@ -554,8 +558,7 @@ static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip) * This works because KDFe fully consumes the secret before it * writes the salt */ - tpm2_KDFe(chip->auth->salt, "SECRET", x, chip->null_ec_key_x, - chip->auth->salt); + tpm2_KDFe(auth->salt, "SECRET", x, chip->null_ec_key_x, auth->salt); out: crypto_free_kpp(kpp); @@ -853,7 +856,9 @@ int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf, if (rc) /* manually close the session if it wasn't consumed */ tpm2_flush_context(chip, auth->handle); - memzero_explicit(auth, sizeof(*auth)); + + kfree_sensitive(auth); + chip->auth = NULL; } else { /* reset for next use */ auth->session = TPM_HEADER_SIZE; @@ -881,7 +886,8 @@ void tpm2_end_auth_session(struct tpm_chip *chip) return; tpm2_flush_context(chip, auth->handle); - memzero_explicit(auth, sizeof(*auth)); + kfree_sensitive(auth); + chip->auth = NULL; } EXPORT_SYMBOL(tpm2_end_auth_session); @@ -915,33 +921,37 @@ static int tpm2_parse_start_auth_session(struct tpm2_auth *auth, static int tpm2_load_null(struct tpm_chip *chip, u32 *null_key) { - int rc; unsigned int offset = 0; /* dummy offset for null seed context */ u8 name[SHA256_DIGEST_SIZE + 2]; + u32 tmp_null_key; + int rc; rc = tpm2_load_context(chip, chip->null_key_context, &offset, - null_key); - if (rc != -EINVAL) - return rc; + &tmp_null_key); + if (rc != -EINVAL) { + if (!rc) + *null_key = tmp_null_key; + goto err; + } - /* an integrity failure may mean the TPM has been reset */ - dev_err(&chip->dev, "NULL key integrity failure!\n"); - /* check the null name against what we know */ - tpm2_create_primary(chip, TPM2_RH_NULL, NULL, name); - if (memcmp(name, chip->null_key_name, sizeof(name)) == 0) - /* name unchanged, assume transient integrity failure */ - return rc; - /* - * Fatal TPM failure: the NULL seed has actually changed, so - * the TPM must have been illegally reset. All in-kernel TPM - * operations will fail because the NULL primary can't be - * loaded to salt the sessions, but disable the TPM anyway so - * userspace programmes can't be compromised by it. - */ - dev_err(&chip->dev, "NULL name has changed, disabling TPM due to interference\n"); + /* Try to re-create null key, given the integrity failure: */ + rc = tpm2_create_primary(chip, TPM2_RH_NULL, &tmp_null_key, name); + if (rc) + goto err; + + /* Return null key if the name has not been changed: */ + if (!memcmp(name, chip->null_key_name, sizeof(name))) { + *null_key = tmp_null_key; + return 0; + } + + /* Deduce from the name change TPM interference: */ + dev_err(&chip->dev, "null key integrity check failed\n"); + tpm2_flush_context(chip, tmp_null_key); chip->flags |= TPM_CHIP_FLAG_DISABLE; - return rc; +err: + return rc ? -ENODEV : 0; } /** @@ -958,16 +968,20 @@ static int tpm2_load_null(struct tpm_chip *chip, u32 *null_key) */ int tpm2_start_auth_session(struct tpm_chip *chip) { + struct tpm2_auth *auth; struct tpm_buf buf; - struct tpm2_auth *auth = chip->auth; - int rc; u32 null_key; + int rc; - if (!auth) { - dev_warn_once(&chip->dev, "auth session is not active\n"); + if (chip->auth) { + dev_warn_once(&chip->dev, "auth session is active\n"); return 0; } + auth = kzalloc(sizeof(*auth), GFP_KERNEL); + if (!auth) + return -ENOMEM; + rc = tpm2_load_null(chip, &null_key); if (rc) goto out; @@ -988,7 +1002,7 @@ int tpm2_start_auth_session(struct tpm_chip *chip) tpm_buf_append(&buf, auth->our_nonce, sizeof(auth->our_nonce)); /* append encrypted salt and squirrel away unencrypted in auth */ - tpm_buf_append_salt(&buf, chip); + tpm_buf_append_salt(&buf, chip, auth); /* session type (HMAC, audit or policy) */ tpm_buf_append_u8(&buf, TPM2_SE_HMAC); @@ -1010,10 +1024,13 @@ int tpm2_start_auth_session(struct tpm_chip *chip) tpm_buf_destroy(&buf); - if (rc) - goto out; + if (rc == TPM2_RC_SUCCESS) { + chip->auth = auth; + return 0; + } - out: +out: + kfree_sensitive(auth); return rc; } EXPORT_SYMBOL(tpm2_start_auth_session); @@ -1347,18 +1364,21 @@ static int tpm2_create_null_primary(struct tpm_chip *chip) * * Derive and context save the null primary and allocate memory in the * struct tpm_chip for the authorizations. + * + * Return: + * * 0 - OK + * * -errno - A system error + * * TPM_RC - A TPM error */ int tpm2_sessions_init(struct tpm_chip *chip) { int rc; rc = tpm2_create_null_primary(chip); - if (rc) - dev_err(&chip->dev, "TPM: security failed (NULL seed derivation): %d\n", rc); - - chip->auth = kmalloc(sizeof(*chip->auth), GFP_KERNEL); - if (!chip->auth) - return -ENOMEM; + if (rc) { + dev_err(&chip->dev, "null key creation failed with %d\n", rc); + return rc; + } return rc; } diff --git a/drivers/cxl/Kconfig b/drivers/cxl/Kconfig index 29c192f20082..876469e23f7a 100644 --- a/drivers/cxl/Kconfig +++ b/drivers/cxl/Kconfig @@ -60,6 +60,7 @@ config CXL_ACPI default CXL_BUS select ACPI_TABLE_LIB select ACPI_HMAT + select CXL_PORT help Enable support for host managed device memory (HDM) resources published by a platform's ACPI CXL memory layout description. See diff --git a/drivers/cxl/Makefile b/drivers/cxl/Makefile index db321f48ba52..2caa90fa4bf2 100644 --- a/drivers/cxl/Makefile +++ b/drivers/cxl/Makefile @@ -1,13 +1,21 @@ # SPDX-License-Identifier: GPL-2.0 + +# Order is important here for the built-in case: +# - 'core' first for fundamental init +# - 'port' before platform root drivers like 'acpi' so that CXL-root ports +# are immediately enabled +# - 'mem' and 'pmem' before endpoint drivers so that memdevs are +# immediately enabled +# - 'pci' last, also mirrors the hardware enumeration hierarchy obj-y += core/ -obj-$(CONFIG_CXL_PCI) += cxl_pci.o -obj-$(CONFIG_CXL_MEM) += cxl_mem.o +obj-$(CONFIG_CXL_PORT) += cxl_port.o obj-$(CONFIG_CXL_ACPI) += cxl_acpi.o obj-$(CONFIG_CXL_PMEM) += cxl_pmem.o -obj-$(CONFIG_CXL_PORT) += cxl_port.o +obj-$(CONFIG_CXL_MEM) += cxl_mem.o +obj-$(CONFIG_CXL_PCI) += cxl_pci.o -cxl_mem-y := mem.o -cxl_pci-y := pci.o +cxl_port-y := port.o cxl_acpi-y := acpi.o cxl_pmem-y := pmem.o security.o -cxl_port-y := port.o +cxl_mem-y := mem.o +cxl_pci-y := pci.o diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c index 82b78e331d8e..432b7cfd12a8 100644 --- a/drivers/cxl/acpi.c +++ b/drivers/cxl/acpi.c @@ -924,6 +924,13 @@ static void __exit cxl_acpi_exit(void) /* load before dax_hmem sees 'Soft Reserved' CXL ranges */ subsys_initcall(cxl_acpi_init); + +/* + * Arrange for host-bridge ports to be active synchronous with + * cxl_acpi_probe() exit. + */ +MODULE_SOFTDEP("pre: cxl_port"); + module_exit(cxl_acpi_exit); MODULE_DESCRIPTION("CXL ACPI: Platform Support"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/cxl/core/cdat.c b/drivers/cxl/core/cdat.c index ef1621d40f05..e9cd7939c407 100644 --- a/drivers/cxl/core/cdat.c +++ b/drivers/cxl/core/cdat.c @@ -641,6 +641,9 @@ static int cxl_endpoint_gather_bandwidth(struct cxl_region *cxlr, void *ptr; int rc; + if (!dev_is_pci(cxlds->dev)) + return -ENODEV; + if (cxlds->rcd) return -ENODEV; diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c index 3df10517a327..223c273c0cd1 100644 --- a/drivers/cxl/core/hdm.c +++ b/drivers/cxl/core/hdm.c @@ -712,7 +712,44 @@ static int cxl_decoder_commit(struct cxl_decoder *cxld) return 0; } -static int cxl_decoder_reset(struct cxl_decoder *cxld) +static int commit_reap(struct device *dev, const void *data) +{ + struct cxl_port *port = to_cxl_port(dev->parent); + struct cxl_decoder *cxld; + + if (!is_switch_decoder(dev) && !is_endpoint_decoder(dev)) + return 0; + + cxld = to_cxl_decoder(dev); + if (port->commit_end == cxld->id && + ((cxld->flags & CXL_DECODER_F_ENABLE) == 0)) { + port->commit_end--; + dev_dbg(&port->dev, "reap: %s commit_end: %d\n", + dev_name(&cxld->dev), port->commit_end); + } + + return 0; +} + +void cxl_port_commit_reap(struct cxl_decoder *cxld) +{ + struct cxl_port *port = to_cxl_port(cxld->dev.parent); + + lockdep_assert_held_write(&cxl_region_rwsem); + + /* + * Once the highest committed decoder is disabled, free any other + * decoders that were pinned allocated by out-of-order release. + */ + port->commit_end--; + dev_dbg(&port->dev, "reap: %s commit_end: %d\n", dev_name(&cxld->dev), + port->commit_end); + device_for_each_child_reverse_from(&port->dev, &cxld->dev, NULL, + commit_reap); +} +EXPORT_SYMBOL_NS_GPL(cxl_port_commit_reap, CXL); + +static void cxl_decoder_reset(struct cxl_decoder *cxld) { struct cxl_port *port = to_cxl_port(cxld->dev.parent); struct cxl_hdm *cxlhdm = dev_get_drvdata(&port->dev); @@ -721,14 +758,14 @@ static int cxl_decoder_reset(struct cxl_decoder *cxld) u32 ctrl; if ((cxld->flags & CXL_DECODER_F_ENABLE) == 0) - return 0; + return; - if (port->commit_end != id) { + if (port->commit_end == id) + cxl_port_commit_reap(cxld); + else dev_dbg(&port->dev, "%s: out of order reset, expected decoder%d.%d\n", dev_name(&cxld->dev), port->id, port->commit_end); - return -EBUSY; - } down_read(&cxl_dpa_rwsem); ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(id)); @@ -741,7 +778,6 @@ static int cxl_decoder_reset(struct cxl_decoder *cxld) writel(0, hdm + CXL_HDM_DECODER0_BASE_LOW_OFFSET(id)); up_read(&cxl_dpa_rwsem); - port->commit_end--; cxld->flags &= ~CXL_DECODER_F_ENABLE; /* Userspace is now responsible for reconfiguring this decoder */ @@ -751,8 +787,6 @@ static int cxl_decoder_reset(struct cxl_decoder *cxld) cxled = to_cxl_endpoint_decoder(&cxld->dev); cxled->state = CXL_DECODER_STATE_MANUAL; } - - return 0; } static int cxl_setup_hdm_decoder_from_dvsec( diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c index e666ec6a9085..af92c67bc954 100644 --- a/drivers/cxl/core/port.c +++ b/drivers/cxl/core/port.c @@ -2084,11 +2084,18 @@ static void cxl_bus_remove(struct device *dev) static struct workqueue_struct *cxl_bus_wq; -static void cxl_bus_rescan_queue(struct work_struct *w) +static int cxl_rescan_attach(struct device *dev, void *data) { - int rc = bus_rescan_devices(&cxl_bus_type); + int rc = device_attach(dev); + + dev_vdbg(dev, "rescan: %s\n", rc ? "attach" : "detached"); - pr_debug("CXL bus rescan result: %d\n", rc); + return 0; +} + +static void cxl_bus_rescan_queue(struct work_struct *w) +{ + bus_for_each_dev(&cxl_bus_type, NULL, NULL, cxl_rescan_attach); } void cxl_bus_rescan(void) diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index e701e4b04032..dff618c708dc 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -232,8 +232,8 @@ static int cxl_region_invalidate_memregion(struct cxl_region *cxlr) "Bypassing cpu_cache_invalidate_memregion() for testing!\n"); return 0; } else { - dev_err(&cxlr->dev, - "Failed to synchronize CPU cache state\n"); + dev_WARN(&cxlr->dev, + "Failed to synchronize CPU cache state\n"); return -ENXIO; } } @@ -242,19 +242,17 @@ static int cxl_region_invalidate_memregion(struct cxl_region *cxlr) return 0; } -static int cxl_region_decode_reset(struct cxl_region *cxlr, int count) +static void cxl_region_decode_reset(struct cxl_region *cxlr, int count) { struct cxl_region_params *p = &cxlr->params; - int i, rc = 0; + int i; /* - * Before region teardown attempt to flush, and if the flush - * fails cancel the region teardown for data consistency - * concerns + * Before region teardown attempt to flush, evict any data cached for + * this region, or scream loudly about missing arch / platform support + * for CXL teardown. */ - rc = cxl_region_invalidate_memregion(cxlr); - if (rc) - return rc; + cxl_region_invalidate_memregion(cxlr); for (i = count - 1; i >= 0; i--) { struct cxl_endpoint_decoder *cxled = p->targets[i]; @@ -277,23 +275,17 @@ static int cxl_region_decode_reset(struct cxl_region *cxlr, int count) cxl_rr = cxl_rr_load(iter, cxlr); cxld = cxl_rr->decoder; if (cxld->reset) - rc = cxld->reset(cxld); - if (rc) - return rc; + cxld->reset(cxld); set_bit(CXL_REGION_F_NEEDS_RESET, &cxlr->flags); } endpoint_reset: - rc = cxled->cxld.reset(&cxled->cxld); - if (rc) - return rc; + cxled->cxld.reset(&cxled->cxld); set_bit(CXL_REGION_F_NEEDS_RESET, &cxlr->flags); } /* all decoders associated with this region have been torn down */ clear_bit(CXL_REGION_F_NEEDS_RESET, &cxlr->flags); - - return 0; } static int commit_decoder(struct cxl_decoder *cxld) @@ -409,16 +401,8 @@ static ssize_t commit_store(struct device *dev, struct device_attribute *attr, * still pending. */ if (p->state == CXL_CONFIG_RESET_PENDING) { - rc = cxl_region_decode_reset(cxlr, p->interleave_ways); - /* - * Revert to committed since there may still be active - * decoders associated with this region, or move forward - * to active to mark the reset successful - */ - if (rc) - p->state = CXL_CONFIG_COMMIT; - else - p->state = CXL_CONFIG_ACTIVE; + cxl_region_decode_reset(cxlr, p->interleave_ways); + p->state = CXL_CONFIG_ACTIVE; } } @@ -794,26 +778,50 @@ out: return rc; } +static int check_commit_order(struct device *dev, const void *data) +{ + struct cxl_decoder *cxld = to_cxl_decoder(dev); + + /* + * if port->commit_end is not the only free decoder, then out of + * order shutdown has occurred, block further allocations until + * that is resolved + */ + if (((cxld->flags & CXL_DECODER_F_ENABLE) == 0)) + return -EBUSY; + return 0; +} + static int match_free_decoder(struct device *dev, void *data) { + struct cxl_port *port = to_cxl_port(dev->parent); struct cxl_decoder *cxld; - int *id = data; + int rc; if (!is_switch_decoder(dev)) return 0; cxld = to_cxl_decoder(dev); - /* enforce ordered allocation */ - if (cxld->id != *id) + if (cxld->id != port->commit_end + 1) return 0; - if (!cxld->region) - return 1; - - (*id)++; + if (cxld->region) { + dev_dbg(dev->parent, + "next decoder to commit (%s) is already reserved (%s)\n", + dev_name(dev), dev_name(&cxld->region->dev)); + return 0; + } - return 0; + rc = device_for_each_child_reverse_from(dev->parent, dev, NULL, + check_commit_order); + if (rc) { + dev_dbg(dev->parent, + "unable to allocate %s due to out of order shutdown\n", + dev_name(dev)); + return 0; + } + return 1; } static int match_auto_decoder(struct device *dev, void *data) @@ -840,7 +848,6 @@ cxl_region_find_decoder(struct cxl_port *port, struct cxl_region *cxlr) { struct device *dev; - int id = 0; if (port == cxled_to_port(cxled)) return &cxled->cxld; @@ -849,7 +856,7 @@ cxl_region_find_decoder(struct cxl_port *port, dev = device_find_child(&port->dev, &cxlr->params, match_auto_decoder); else - dev = device_find_child(&port->dev, &id, match_free_decoder); + dev = device_find_child(&port->dev, NULL, match_free_decoder); if (!dev) return NULL; /* @@ -2054,13 +2061,7 @@ static int cxl_region_detach(struct cxl_endpoint_decoder *cxled) get_device(&cxlr->dev); if (p->state > CXL_CONFIG_ACTIVE) { - /* - * TODO: tear down all impacted regions if a device is - * removed out of order - */ - rc = cxl_region_decode_reset(cxlr, p->interleave_ways); - if (rc) - goto out; + cxl_region_decode_reset(cxlr, p->interleave_ways); p->state = CXL_CONFIG_ACTIVE; } diff --git a/drivers/cxl/core/trace.h b/drivers/cxl/core/trace.h index 8672b42ee4d1..8389a94adb1a 100644 --- a/drivers/cxl/core/trace.h +++ b/drivers/cxl/core/trace.h @@ -279,7 +279,7 @@ TRACE_EVENT(cxl_generic_event, #define CXL_GMER_MEM_EVT_TYPE_ECC_ERROR 0x00 #define CXL_GMER_MEM_EVT_TYPE_INV_ADDR 0x01 #define CXL_GMER_MEM_EVT_TYPE_DATA_PATH_ERROR 0x02 -#define show_mem_event_type(type) __print_symbolic(type, \ +#define show_gmer_mem_event_type(type) __print_symbolic(type, \ { CXL_GMER_MEM_EVT_TYPE_ECC_ERROR, "ECC Error" }, \ { CXL_GMER_MEM_EVT_TYPE_INV_ADDR, "Invalid Address" }, \ { CXL_GMER_MEM_EVT_TYPE_DATA_PATH_ERROR, "Data Path Error" } \ @@ -373,7 +373,7 @@ TRACE_EVENT(cxl_general_media, "hpa=%llx region=%s region_uuid=%pUb", __entry->dpa, show_dpa_flags(__entry->dpa_flags), show_event_desc_flags(__entry->descriptor), - show_mem_event_type(__entry->type), + show_gmer_mem_event_type(__entry->type), show_trans_type(__entry->transaction_type), __entry->channel, __entry->rank, __entry->device, __print_hex(__entry->comp_id, CXL_EVENT_GEN_MED_COMP_ID_SIZE), @@ -391,6 +391,17 @@ TRACE_EVENT(cxl_general_media, * DRAM Event Record defines many fields the same as the General Media Event * Record. Reuse those definitions as appropriate. */ +#define CXL_DER_MEM_EVT_TYPE_ECC_ERROR 0x00 +#define CXL_DER_MEM_EVT_TYPE_SCRUB_MEDIA_ECC_ERROR 0x01 +#define CXL_DER_MEM_EVT_TYPE_INV_ADDR 0x02 +#define CXL_DER_MEM_EVT_TYPE_DATA_PATH_ERROR 0x03 +#define show_dram_mem_event_type(type) __print_symbolic(type, \ + { CXL_DER_MEM_EVT_TYPE_ECC_ERROR, "ECC Error" }, \ + { CXL_DER_MEM_EVT_TYPE_SCRUB_MEDIA_ECC_ERROR, "Scrub Media ECC Error" }, \ + { CXL_DER_MEM_EVT_TYPE_INV_ADDR, "Invalid Address" }, \ + { CXL_DER_MEM_EVT_TYPE_DATA_PATH_ERROR, "Data Path Error" } \ +) + #define CXL_DER_VALID_CHANNEL BIT(0) #define CXL_DER_VALID_RANK BIT(1) #define CXL_DER_VALID_NIBBLE BIT(2) @@ -477,7 +488,7 @@ TRACE_EVENT(cxl_dram, "hpa=%llx region=%s region_uuid=%pUb", __entry->dpa, show_dpa_flags(__entry->dpa_flags), show_event_desc_flags(__entry->descriptor), - show_mem_event_type(__entry->type), + show_dram_mem_event_type(__entry->type), show_trans_type(__entry->transaction_type), __entry->channel, __entry->rank, __entry->nibble_mask, __entry->bank_group, __entry->bank, diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h index 0d8b810a51f0..5406e3ab3d4a 100644 --- a/drivers/cxl/cxl.h +++ b/drivers/cxl/cxl.h @@ -359,7 +359,7 @@ struct cxl_decoder { struct cxl_region *region; unsigned long flags; int (*commit)(struct cxl_decoder *cxld); - int (*reset)(struct cxl_decoder *cxld); + void (*reset)(struct cxl_decoder *cxld); }; /* @@ -730,6 +730,7 @@ static inline bool is_cxl_root(struct cxl_port *port) int cxl_num_decoders_committed(struct cxl_port *port); bool is_cxl_port(const struct device *dev); struct cxl_port *to_cxl_port(const struct device *dev); +void cxl_port_commit_reap(struct cxl_decoder *cxld); struct pci_bus; int devm_cxl_register_pci_bus(struct device *host, struct device *uport_dev, struct pci_bus *bus); diff --git a/drivers/cxl/port.c b/drivers/cxl/port.c index 861dde65768f..9dc394295e1f 100644 --- a/drivers/cxl/port.c +++ b/drivers/cxl/port.c @@ -208,7 +208,22 @@ static struct cxl_driver cxl_port_driver = { }, }; -module_cxl_driver(cxl_port_driver); +static int __init cxl_port_init(void) +{ + return cxl_driver_register(&cxl_port_driver); +} +/* + * Be ready to immediately enable ports emitted by the platform CXL root + * (e.g. cxl_acpi) when CONFIG_CXL_PORT=y. + */ +subsys_initcall(cxl_port_init); + +static void __exit cxl_port_exit(void) +{ + cxl_driver_unregister(&cxl_port_driver); +} +module_exit(cxl_port_exit); + MODULE_DESCRIPTION("CXL: Port enumeration and services"); MODULE_LICENSE("GPL v2"); MODULE_IMPORT_NS(CXL); diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c index 65a27c5a7bce..811389fc9cb8 100644 --- a/drivers/dma/sh/rz-dmac.c +++ b/drivers/dma/sh/rz-dmac.c @@ -601,22 +601,25 @@ static int rz_dmac_config(struct dma_chan *chan, struct rz_dmac_chan *channel = to_rz_dmac_chan(chan); u32 val; - channel->src_per_address = config->src_addr; channel->dst_per_address = config->dst_addr; - - val = rz_dmac_ds_to_val_mapping(config->dst_addr_width); - if (val == CHCFG_DS_INVALID) - return -EINVAL; - channel->chcfg &= ~CHCFG_FILL_DDS_MASK; - channel->chcfg |= FIELD_PREP(CHCFG_FILL_DDS_MASK, val); + if (channel->dst_per_address) { + val = rz_dmac_ds_to_val_mapping(config->dst_addr_width); + if (val == CHCFG_DS_INVALID) + return -EINVAL; - val = rz_dmac_ds_to_val_mapping(config->src_addr_width); - if (val == CHCFG_DS_INVALID) - return -EINVAL; + channel->chcfg |= FIELD_PREP(CHCFG_FILL_DDS_MASK, val); + } + channel->src_per_address = config->src_addr; channel->chcfg &= ~CHCFG_FILL_SDS_MASK; - channel->chcfg |= FIELD_PREP(CHCFG_FILL_SDS_MASK, val); + if (channel->src_per_address) { + val = rz_dmac_ds_to_val_mapping(config->src_addr_width); + if (val == CHCFG_DS_INVALID) + return -EINVAL; + + channel->chcfg |= FIELD_PREP(CHCFG_FILL_SDS_MASK, val); + } return 0; } diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c index 406ee199c2ac..b3f27b3f9209 100644 --- a/drivers/dma/ti/k3-udma.c +++ b/drivers/dma/ti/k3-udma.c @@ -3185,27 +3185,40 @@ static int udma_configure_statictr(struct udma_chan *uc, struct udma_desc *d, d->static_tr.elcnt = elcnt; - /* - * PDMA must to close the packet when the channel is in packet mode. - * For TR mode when the channel is not cyclic we also need PDMA to close - * the packet otherwise the transfer will stall because PDMA holds on - * the data it has received from the peripheral. - */ if (uc->config.pkt_mode || !uc->cyclic) { + /* + * PDMA must close the packet when the channel is in packet mode. + * For TR mode when the channel is not cyclic we also need PDMA + * to close the packet otherwise the transfer will stall because + * PDMA holds on the data it has received from the peripheral. + */ unsigned int div = dev_width * elcnt; if (uc->cyclic) d->static_tr.bstcnt = d->residue / d->sglen / div; else d->static_tr.bstcnt = d->residue / div; + } else if (uc->ud->match_data->type == DMA_TYPE_BCDMA && + uc->config.dir == DMA_DEV_TO_MEM && + uc->cyclic) { + /* + * For cyclic mode with BCDMA we have to set EOP in each TR to + * prevent short packet errors seen on channel teardown. So the + * PDMA must close the packet after every TR transfer by setting + * burst count equal to the number of bytes transferred. + */ + struct cppi5_tr_type1_t *tr_req = d->hwdesc[0].tr_req_base; - if (uc->config.dir == DMA_DEV_TO_MEM && - d->static_tr.bstcnt > uc->ud->match_data->statictr_z_mask) - return -EINVAL; + d->static_tr.bstcnt = + (tr_req->icnt0 * tr_req->icnt1) / dev_width; } else { d->static_tr.bstcnt = 0; } + if (uc->config.dir == DMA_DEV_TO_MEM && + d->static_tr.bstcnt > uc->ud->match_data->statictr_z_mask) + return -EINVAL; + return 0; } @@ -3450,8 +3463,9 @@ udma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, /* static TR for remote PDMA */ if (udma_configure_statictr(uc, d, dev_width, burst)) { dev_err(uc->ud->dev, - "%s: StaticTR Z is limited to maximum 4095 (%u)\n", - __func__, d->static_tr.bstcnt); + "%s: StaticTR Z is limited to maximum %u (%u)\n", + __func__, uc->ud->match_data->statictr_z_mask, + d->static_tr.bstcnt); udma_free_hwdesc(uc, d); kfree(d); @@ -3476,6 +3490,7 @@ udma_prep_dma_cyclic_tr(struct udma_chan *uc, dma_addr_t buf_addr, u16 tr0_cnt0, tr0_cnt1, tr1_cnt0; unsigned int i; int num_tr; + u32 period_csf = 0; num_tr = udma_get_tr_counters(period_len, __ffs(buf_addr), &tr0_cnt0, &tr0_cnt1, &tr1_cnt0); @@ -3498,6 +3513,20 @@ udma_prep_dma_cyclic_tr(struct udma_chan *uc, dma_addr_t buf_addr, period_addr = buf_addr | ((u64)uc->config.asel << K3_ADDRESS_ASEL_SHIFT); + /* + * For BCDMA <-> PDMA transfers, the EOP flag needs to be set on the + * last TR of a descriptor, to mark the packet as complete. + * This is required for getting the teardown completion message in case + * of TX, and to avoid short-packet error in case of RX. + * + * As we are in cyclic mode, we do not know which period might be the + * last one, so set the flag for each period. + */ + if (uc->config.ep_type == PSIL_EP_PDMA_XY && + uc->ud->match_data->type == DMA_TYPE_BCDMA) { + period_csf = CPPI5_TR_CSF_EOP; + } + for (i = 0; i < periods; i++) { int tr_idx = i * num_tr; @@ -3525,8 +3554,10 @@ udma_prep_dma_cyclic_tr(struct udma_chan *uc, dma_addr_t buf_addr, } if (!(flags & DMA_PREP_INTERRUPT)) - cppi5_tr_csf_set(&tr_req[tr_idx].flags, - CPPI5_TR_CSF_SUPR_EVT); + period_csf |= CPPI5_TR_CSF_SUPR_EVT; + + if (period_csf) + cppi5_tr_csf_set(&tr_req[tr_idx].flags, period_csf); period_addr += period_len; } @@ -3655,8 +3686,9 @@ udma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, /* static TR for remote PDMA */ if (udma_configure_statictr(uc, d, dev_width, burst)) { dev_err(uc->ud->dev, - "%s: StaticTR Z is limited to maximum 4095 (%u)\n", - __func__, d->static_tr.bstcnt); + "%s: StaticTR Z is limited to maximum %u (%u)\n", + __func__, uc->ud->match_data->statictr_z_mask, + d->static_tr.bstcnt); udma_free_hwdesc(uc, d); kfree(d); diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c index 285fe7ad490d..3e8051fe8296 100644 --- a/drivers/firmware/arm_sdei.c +++ b/drivers/firmware/arm_sdei.c @@ -763,7 +763,7 @@ static int sdei_device_freeze(struct device *dev) int err; /* unregister private events */ - cpuhp_remove_state(sdei_entry_point); + cpuhp_remove_state(sdei_hp_state); err = sdei_unregister_shared(); if (err) diff --git a/drivers/gpio/gpio-sloppy-logic-analyzer.c b/drivers/gpio/gpio-sloppy-logic-analyzer.c index 07e0d7180579..59a8f3a5c4e4 100644 --- a/drivers/gpio/gpio-sloppy-logic-analyzer.c +++ b/drivers/gpio/gpio-sloppy-logic-analyzer.c @@ -234,7 +234,9 @@ static int gpio_la_poll_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - devm_mutex_init(dev, &priv->blob_lock); + ret = devm_mutex_init(dev, &priv->blob_lock); + if (ret) + return ret; fops_buf_size_set(priv, GPIO_LA_DEFAULT_BUF_SIZE); diff --git a/drivers/gpio/gpiolib-swnode.c b/drivers/gpio/gpiolib-swnode.c index 2b2dd7e92211..51d2475c05c5 100644 --- a/drivers/gpio/gpiolib-swnode.c +++ b/drivers/gpio/gpiolib-swnode.c @@ -64,7 +64,7 @@ struct gpio_desc *swnode_find_gpio(struct fwnode_handle *fwnode, struct fwnode_reference_args args; struct gpio_desc *desc; char propname[32]; /* 32 is max size of property name */ - int ret; + int ret = 0; swnode = to_software_node(fwnode); if (!swnode) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index d5952ab7752c..2b02655abb56 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -4926,6 +4926,8 @@ static void *gpiolib_seq_start(struct seq_file *s, loff_t *pos) return NULL; s->private = priv; + if (*pos > 0) + priv->newline = true; priv->idx = srcu_read_lock(&gpio_devices_srcu); list_for_each_entry_srcu(gdev, &gpio_devices, list, @@ -4969,7 +4971,7 @@ static int gpiolib_seq_show(struct seq_file *s, void *v) gc = srcu_dereference(gdev->chip, &gdev->srcu); if (!gc) { - seq_printf(s, "%s%s: (dangling chip)", + seq_printf(s, "%s%s: (dangling chip)\n", priv->newline ? "\n" : "", dev_name(&gdev->dev)); return 0; diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml2_policy.c b/drivers/gpu/drm/amd/display/dc/dml2/dml2_policy.c index 11c904ae2958..c4c52173ef22 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2/dml2_policy.c +++ b/drivers/gpu/drm/amd/display/dc/dml2/dml2_policy.c @@ -303,6 +303,7 @@ void build_unoptimized_policy_settings(enum dml_project_id project, struct dml_m if (project == dml_project_dcn35 || project == dml_project_dcn351) { policy->DCCProgrammingAssumesScanDirectionUnknownFinal = false; + policy->EnhancedPrefetchScheduleAccelerationFinal = 0; policy->AllowForPStateChangeOrStutterInVBlankFinal = dml_prefetch_support_uclk_fclk_and_stutter_if_possible; /*new*/ policy->UseOnlyMaxPrefetchModes = 1; } diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c index 22737b11b1bf..1fe020f1f4db 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c @@ -242,7 +242,9 @@ static int vangogh_tables_init(struct smu_context *smu) goto err0_out; smu_table->metrics_time = 0; - smu_table->gpu_metrics_table_size = max(sizeof(struct gpu_metrics_v2_3), sizeof(struct gpu_metrics_v2_2)); + smu_table->gpu_metrics_table_size = sizeof(struct gpu_metrics_v2_2); + smu_table->gpu_metrics_table_size = max(smu_table->gpu_metrics_table_size, sizeof(struct gpu_metrics_v2_3)); + smu_table->gpu_metrics_table_size = max(smu_table->gpu_metrics_table_size, sizeof(struct gpu_metrics_v2_4)); smu_table->gpu_metrics_table = kzalloc(smu_table->gpu_metrics_table_size, GFP_KERNEL); if (!smu_table->gpu_metrics_table) goto err1_out; diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c index cb923e33fd6f..d53e162dcd8d 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c @@ -2485,7 +2485,7 @@ static int smu_v13_0_0_set_power_profile_mode(struct smu_context *smu, DpmActivityMonitorCoeffInt_t *activity_monitor = &(activity_monitor_external.DpmActivityMonitorCoeffInt); int workload_type, ret = 0; - u32 workload_mask; + u32 workload_mask, selected_workload_mask; smu->power_profile_mode = input[size]; @@ -2552,7 +2552,7 @@ static int smu_v13_0_0_set_power_profile_mode(struct smu_context *smu, if (workload_type < 0) return -EINVAL; - workload_mask = 1 << workload_type; + selected_workload_mask = workload_mask = 1 << workload_type; /* Add optimizations for SMU13.0.0/10. Reuse the power saving profile */ if ((amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(13, 0, 0) && @@ -2572,7 +2572,7 @@ static int smu_v13_0_0_set_power_profile_mode(struct smu_context *smu, workload_mask, NULL); if (!ret) - smu->workload_mask = workload_mask; + smu->workload_mask = selected_workload_mask; return ret; } diff --git a/drivers/gpu/drm/mediatek/mtk_crtc.c b/drivers/gpu/drm/mediatek/mtk_crtc.c index 175b00e5a253..eb0e1233ad04 100644 --- a/drivers/gpu/drm/mediatek/mtk_crtc.c +++ b/drivers/gpu/drm/mediatek/mtk_crtc.c @@ -127,9 +127,8 @@ static void mtk_crtc_destroy(struct drm_crtc *crtc) mtk_mutex_put(mtk_crtc->mutex); #if IS_REACHABLE(CONFIG_MTK_CMDQ) - cmdq_pkt_destroy(&mtk_crtc->cmdq_client, &mtk_crtc->cmdq_handle); - if (mtk_crtc->cmdq_client.chan) { + cmdq_pkt_destroy(&mtk_crtc->cmdq_client, &mtk_crtc->cmdq_handle); mbox_free_channel(mtk_crtc->cmdq_client.chan); mtk_crtc->cmdq_client.chan = NULL; } @@ -913,6 +912,7 @@ static int mtk_crtc_init_comp_planes(struct drm_device *drm_dev, BIT(pipe), mtk_crtc_plane_type(mtk_crtc->layer_nr, num_planes), mtk_ddp_comp_supported_rotations(comp), + mtk_ddp_comp_get_blend_modes(comp), mtk_ddp_comp_get_formats(comp), mtk_ddp_comp_get_num_formats(comp), i); if (ret) diff --git a/drivers/gpu/drm/mediatek/mtk_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_ddp_comp.c index be66d94be361..edc6417639e6 100644 --- a/drivers/gpu/drm/mediatek/mtk_ddp_comp.c +++ b/drivers/gpu/drm/mediatek/mtk_ddp_comp.c @@ -363,6 +363,7 @@ static const struct mtk_ddp_comp_funcs ddp_ovl = { .layer_config = mtk_ovl_layer_config, .bgclr_in_on = mtk_ovl_bgclr_in_on, .bgclr_in_off = mtk_ovl_bgclr_in_off, + .get_blend_modes = mtk_ovl_get_blend_modes, .get_formats = mtk_ovl_get_formats, .get_num_formats = mtk_ovl_get_num_formats, }; @@ -416,6 +417,7 @@ static const struct mtk_ddp_comp_funcs ddp_ovl_adaptor = { .disconnect = mtk_ovl_adaptor_disconnect, .add = mtk_ovl_adaptor_add_comp, .remove = mtk_ovl_adaptor_remove_comp, + .get_blend_modes = mtk_ovl_adaptor_get_blend_modes, .get_formats = mtk_ovl_adaptor_get_formats, .get_num_formats = mtk_ovl_adaptor_get_num_formats, .mode_valid = mtk_ovl_adaptor_mode_valid, diff --git a/drivers/gpu/drm/mediatek/mtk_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_ddp_comp.h index ecf6dc283cd7..39720b27f4e9 100644 --- a/drivers/gpu/drm/mediatek/mtk_ddp_comp.h +++ b/drivers/gpu/drm/mediatek/mtk_ddp_comp.h @@ -80,6 +80,7 @@ struct mtk_ddp_comp_funcs { void (*ctm_set)(struct device *dev, struct drm_crtc_state *state); struct device * (*dma_dev_get)(struct device *dev); + u32 (*get_blend_modes)(struct device *dev); const u32 *(*get_formats)(struct device *dev); size_t (*get_num_formats)(struct device *dev); void (*connect)(struct device *dev, struct device *mmsys_dev, unsigned int next); @@ -267,6 +268,15 @@ static inline struct device *mtk_ddp_comp_dma_dev_get(struct mtk_ddp_comp *comp) } static inline +u32 mtk_ddp_comp_get_blend_modes(struct mtk_ddp_comp *comp) +{ + if (comp->funcs && comp->funcs->get_blend_modes) + return comp->funcs->get_blend_modes(comp->dev); + + return 0; +} + +static inline const u32 *mtk_ddp_comp_get_formats(struct mtk_ddp_comp *comp) { if (comp->funcs && comp->funcs->get_formats) diff --git a/drivers/gpu/drm/mediatek/mtk_disp_drv.h b/drivers/gpu/drm/mediatek/mtk_disp_drv.h index 082ac18fe04a..04154db9085c 100644 --- a/drivers/gpu/drm/mediatek/mtk_disp_drv.h +++ b/drivers/gpu/drm/mediatek/mtk_disp_drv.h @@ -103,6 +103,7 @@ void mtk_ovl_register_vblank_cb(struct device *dev, void mtk_ovl_unregister_vblank_cb(struct device *dev); void mtk_ovl_enable_vblank(struct device *dev); void mtk_ovl_disable_vblank(struct device *dev); +u32 mtk_ovl_get_blend_modes(struct device *dev); const u32 *mtk_ovl_get_formats(struct device *dev); size_t mtk_ovl_get_num_formats(struct device *dev); @@ -131,6 +132,7 @@ void mtk_ovl_adaptor_start(struct device *dev); void mtk_ovl_adaptor_stop(struct device *dev); unsigned int mtk_ovl_adaptor_layer_nr(struct device *dev); struct device *mtk_ovl_adaptor_dma_dev_get(struct device *dev); +u32 mtk_ovl_adaptor_get_blend_modes(struct device *dev); const u32 *mtk_ovl_adaptor_get_formats(struct device *dev); size_t mtk_ovl_adaptor_get_num_formats(struct device *dev); enum drm_mode_status mtk_ovl_adaptor_mode_valid(struct device *dev, diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c index 89b439dcf3a6..e0c0bb01f65a 100644 --- a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c +++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c @@ -65,8 +65,8 @@ #define OVL_CON_CLRFMT_RGB (1 << 12) #define OVL_CON_CLRFMT_ARGB8888 (2 << 12) #define OVL_CON_CLRFMT_RGBA8888 (3 << 12) -#define OVL_CON_CLRFMT_ABGR8888 (OVL_CON_CLRFMT_RGBA8888 | OVL_CON_BYTE_SWAP) -#define OVL_CON_CLRFMT_BGRA8888 (OVL_CON_CLRFMT_ARGB8888 | OVL_CON_BYTE_SWAP) +#define OVL_CON_CLRFMT_ABGR8888 (OVL_CON_CLRFMT_ARGB8888 | OVL_CON_BYTE_SWAP) +#define OVL_CON_CLRFMT_BGRA8888 (OVL_CON_CLRFMT_RGBA8888 | OVL_CON_BYTE_SWAP) #define OVL_CON_CLRFMT_UYVY (4 << 12) #define OVL_CON_CLRFMT_YUYV (5 << 12) #define OVL_CON_MTX_YUV_TO_RGB (6 << 16) @@ -146,6 +146,7 @@ struct mtk_disp_ovl_data { bool fmt_rgb565_is_0; bool smi_id_en; bool supports_afbc; + const u32 blend_modes; const u32 *formats; size_t num_formats; bool supports_clrfmt_ext; @@ -214,6 +215,13 @@ void mtk_ovl_disable_vblank(struct device *dev) writel_relaxed(0x0, ovl->regs + DISP_REG_OVL_INTEN); } +u32 mtk_ovl_get_blend_modes(struct device *dev) +{ + struct mtk_disp_ovl *ovl = dev_get_drvdata(dev); + + return ovl->data->blend_modes; +} + const u32 *mtk_ovl_get_formats(struct device *dev) { struct mtk_disp_ovl *ovl = dev_get_drvdata(dev); @@ -386,14 +394,27 @@ void mtk_ovl_layer_off(struct device *dev, unsigned int idx, DISP_REG_OVL_RDMA_CTRL(idx)); } -static unsigned int ovl_fmt_convert(struct mtk_disp_ovl *ovl, unsigned int fmt, - unsigned int blend_mode) +static unsigned int mtk_ovl_fmt_convert(struct mtk_disp_ovl *ovl, + struct mtk_plane_state *state) { - /* The return value in switch "MEM_MODE_INPUT_FORMAT_XXX" - * is defined in mediatek HW data sheet. - * The alphabet order in XXX is no relation to data - * arrangement in memory. + unsigned int fmt = state->pending.format; + unsigned int blend_mode = DRM_MODE_BLEND_COVERAGE; + + /* + * For the platforms where OVL_CON_CLRFMT_MAN is defined in the hardware data sheet + * and supports premultiplied color formats, such as OVL_CON_CLRFMT_PARGB8888. + * + * Check blend_modes in the driver data to see if premultiplied mode is supported. + * If not, use coverage mode instead to set it to the supported color formats. + * + * Current DRM assumption is that alpha is default premultiplied, so the bitmask of + * blend_modes must include BIT(DRM_MODE_BLEND_PREMULTI). Otherwise, mtk_plane_init() + * will get an error return from drm_plane_create_blend_mode_property() and + * state->base.pixel_blend_mode should not be used. */ + if (ovl->data->blend_modes & BIT(DRM_MODE_BLEND_PREMULTI)) + blend_mode = state->base.pixel_blend_mode; + switch (fmt) { default: case DRM_FORMAT_RGB565: @@ -471,20 +492,26 @@ void mtk_ovl_layer_config(struct device *dev, unsigned int idx, return; } - con = ovl_fmt_convert(ovl, fmt, blend_mode); + con = mtk_ovl_fmt_convert(ovl, state); if (state->base.fb) { - con |= OVL_CON_AEN; con |= state->base.alpha & OVL_CON_ALPHA; - } - /* CONST_BLD must be enabled for XRGB formats although the alpha channel - * can be ignored, or OVL will still read the value from memory. - * For RGB888 related formats, whether CONST_BLD is enabled or not won't - * affect the result. Therefore we use !has_alpha as the condition. - */ - if ((state->base.fb && !state->base.fb->format->has_alpha) || - blend_mode == DRM_MODE_BLEND_PIXEL_NONE) - ignore_pixel_alpha = OVL_CONST_BLEND; + /* + * For blend_modes supported SoCs, always enable alpha blending. + * For blend_modes unsupported SoCs, enable alpha blending when has_alpha is set. + */ + if (blend_mode || state->base.fb->format->has_alpha) + con |= OVL_CON_AEN; + + /* + * Although the alpha channel can be ignored, CONST_BLD must be enabled + * for XRGB format, otherwise OVL will still read the value from memory. + * For RGB888 related formats, whether CONST_BLD is enabled or not won't + * affect the result. Therefore we use !has_alpha as the condition. + */ + if (blend_mode == DRM_MODE_BLEND_PIXEL_NONE || !state->base.fb->format->has_alpha) + ignore_pixel_alpha = OVL_CONST_BLEND; + } if (pending->rotation & DRM_MODE_REFLECT_Y) { con |= OVL_CON_VIRT_FLIP; @@ -663,6 +690,9 @@ static const struct mtk_disp_ovl_data mt8192_ovl_driver_data = { .layer_nr = 4, .fmt_rgb565_is_0 = true, .smi_id_en = true, + .blend_modes = BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE) | + BIT(DRM_MODE_BLEND_PIXEL_NONE), .formats = mt8173_formats, .num_formats = ARRAY_SIZE(mt8173_formats), }; @@ -673,6 +703,9 @@ static const struct mtk_disp_ovl_data mt8192_ovl_2l_driver_data = { .layer_nr = 2, .fmt_rgb565_is_0 = true, .smi_id_en = true, + .blend_modes = BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE) | + BIT(DRM_MODE_BLEND_PIXEL_NONE), .formats = mt8173_formats, .num_formats = ARRAY_SIZE(mt8173_formats), }; @@ -684,6 +717,9 @@ static const struct mtk_disp_ovl_data mt8195_ovl_driver_data = { .fmt_rgb565_is_0 = true, .smi_id_en = true, .supports_afbc = true, + .blend_modes = BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE) | + BIT(DRM_MODE_BLEND_PIXEL_NONE), .formats = mt8195_formats, .num_formats = ARRAY_SIZE(mt8195_formats), .supports_clrfmt_ext = true, diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c index c6768210b08b..bf2546c4681a 100644 --- a/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c +++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c @@ -400,6 +400,13 @@ void mtk_ovl_adaptor_disable_vblank(struct device *dev) mtk_ethdr_disable_vblank(ovl_adaptor->ovl_adaptor_comp[OVL_ADAPTOR_ETHDR0]); } +u32 mtk_ovl_adaptor_get_blend_modes(struct device *dev) +{ + struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev); + + return mtk_ethdr_get_blend_modes(ovl_adaptor->ovl_adaptor_comp[OVL_ADAPTOR_ETHDR0]); +} + const u32 *mtk_ovl_adaptor_get_formats(struct device *dev) { struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev); diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c index d8796a904eca..f2bee617f063 100644 --- a/drivers/gpu/drm/mediatek/mtk_dp.c +++ b/drivers/gpu/drm/mediatek/mtk_dp.c @@ -145,6 +145,89 @@ struct mtk_dp_data { u16 audio_m_div2_bit; }; +static const struct mtk_dp_efuse_fmt mt8188_dp_efuse_fmt[MTK_DP_CAL_MAX] = { + [MTK_DP_CAL_GLB_BIAS_TRIM] = { + .idx = 0, + .shift = 10, + .mask = 0x1f, + .min_val = 1, + .max_val = 0x1e, + .default_val = 0xf, + }, + [MTK_DP_CAL_CLKTX_IMPSE] = { + .idx = 0, + .shift = 15, + .mask = 0xf, + .min_val = 1, + .max_val = 0xe, + .default_val = 0x8, + }, + [MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0] = { + .idx = 1, + .shift = 0, + .mask = 0xf, + .min_val = 1, + .max_val = 0xe, + .default_val = 0x8, + }, + [MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1] = { + .idx = 1, + .shift = 8, + .mask = 0xf, + .min_val = 1, + .max_val = 0xe, + .default_val = 0x8, + }, + [MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2] = { + .idx = 1, + .shift = 16, + .mask = 0xf, + .min_val = 1, + .max_val = 0xe, + .default_val = 0x8, + }, + [MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3] = { + .idx = 1, + .shift = 24, + .mask = 0xf, + .min_val = 1, + .max_val = 0xe, + .default_val = 0x8, + }, + [MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0] = { + .idx = 1, + .shift = 4, + .mask = 0xf, + .min_val = 1, + .max_val = 0xe, + .default_val = 0x8, + }, + [MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1] = { + .idx = 1, + .shift = 12, + .mask = 0xf, + .min_val = 1, + .max_val = 0xe, + .default_val = 0x8, + }, + [MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2] = { + .idx = 1, + .shift = 20, + .mask = 0xf, + .min_val = 1, + .max_val = 0xe, + .default_val = 0x8, + }, + [MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3] = { + .idx = 1, + .shift = 28, + .mask = 0xf, + .min_val = 1, + .max_val = 0xe, + .default_val = 0x8, + }, +}; + static const struct mtk_dp_efuse_fmt mt8195_edp_efuse_fmt[MTK_DP_CAL_MAX] = { [MTK_DP_CAL_GLB_BIAS_TRIM] = { .idx = 3, @@ -2771,7 +2854,7 @@ static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend, mtk_dp_resume); static const struct mtk_dp_data mt8188_dp_data = { .bridge_type = DRM_MODE_CONNECTOR_DisplayPort, .smc_cmd = MTK_DP_SIP_ATF_VIDEO_UNMUTE, - .efuse_fmt = mt8195_dp_efuse_fmt, + .efuse_fmt = mt8188_dp_efuse_fmt, .audio_supported = true, .audio_pkt_in_hblank_area = true, .audio_m_div2_bit = MT8188_AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2, diff --git a/drivers/gpu/drm/mediatek/mtk_ethdr.c b/drivers/gpu/drm/mediatek/mtk_ethdr.c index d1d9cf8b10e1..0f22e7d337cb 100644 --- a/drivers/gpu/drm/mediatek/mtk_ethdr.c +++ b/drivers/gpu/drm/mediatek/mtk_ethdr.c @@ -145,6 +145,13 @@ static irqreturn_t mtk_ethdr_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } +u32 mtk_ethdr_get_blend_modes(struct device *dev) +{ + return BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE) | + BIT(DRM_MODE_BLEND_PIXEL_NONE); +} + void mtk_ethdr_layer_config(struct device *dev, unsigned int idx, struct mtk_plane_state *state, struct cmdq_pkt *cmdq_pkt) diff --git a/drivers/gpu/drm/mediatek/mtk_ethdr.h b/drivers/gpu/drm/mediatek/mtk_ethdr.h index 81af9edea3f7..a72aeee46829 100644 --- a/drivers/gpu/drm/mediatek/mtk_ethdr.h +++ b/drivers/gpu/drm/mediatek/mtk_ethdr.h @@ -13,6 +13,7 @@ void mtk_ethdr_clk_disable(struct device *dev); void mtk_ethdr_config(struct device *dev, unsigned int w, unsigned int h, unsigned int vrefresh, unsigned int bpc, struct cmdq_pkt *cmdq_pkt); +u32 mtk_ethdr_get_blend_modes(struct device *dev); void mtk_ethdr_layer_config(struct device *dev, unsigned int idx, struct mtk_plane_state *state, struct cmdq_pkt *cmdq_pkt); diff --git a/drivers/gpu/drm/mediatek/mtk_plane.c b/drivers/gpu/drm/mediatek/mtk_plane.c index 7d2cb4e0fafa..8a48b3b0a956 100644 --- a/drivers/gpu/drm/mediatek/mtk_plane.c +++ b/drivers/gpu/drm/mediatek/mtk_plane.c @@ -320,8 +320,8 @@ static const struct drm_plane_helper_funcs mtk_plane_helper_funcs = { int mtk_plane_init(struct drm_device *dev, struct drm_plane *plane, unsigned long possible_crtcs, enum drm_plane_type type, - unsigned int supported_rotations, const u32 *formats, - size_t num_formats, unsigned int plane_idx) + unsigned int supported_rotations, const u32 blend_modes, + const u32 *formats, size_t num_formats, unsigned int plane_idx) { int err; @@ -366,12 +366,11 @@ int mtk_plane_init(struct drm_device *dev, struct drm_plane *plane, if (err) DRM_ERROR("failed to create property: alpha\n"); - err = drm_plane_create_blend_mode_property(plane, - BIT(DRM_MODE_BLEND_PREMULTI) | - BIT(DRM_MODE_BLEND_COVERAGE) | - BIT(DRM_MODE_BLEND_PIXEL_NONE)); - if (err) - DRM_ERROR("failed to create property: blend_mode\n"); + if (blend_modes) { + err = drm_plane_create_blend_mode_property(plane, blend_modes); + if (err) + DRM_ERROR("failed to create property: blend_mode\n"); + } drm_plane_helper_add(plane, &mtk_plane_helper_funcs); diff --git a/drivers/gpu/drm/mediatek/mtk_plane.h b/drivers/gpu/drm/mediatek/mtk_plane.h index 5b177eac67b7..3b13b89989c7 100644 --- a/drivers/gpu/drm/mediatek/mtk_plane.h +++ b/drivers/gpu/drm/mediatek/mtk_plane.h @@ -48,6 +48,6 @@ to_mtk_plane_state(struct drm_plane_state *state) int mtk_plane_init(struct drm_device *dev, struct drm_plane *plane, unsigned long possible_crtcs, enum drm_plane_type type, - unsigned int supported_rotations, const u32 *formats, - size_t num_formats, unsigned int plane_idx); + unsigned int supported_rotations, const u32 blend_modes, + const u32 *formats, size_t num_formats, unsigned int plane_idx); #endif diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c index ef232c0c2049..4e2d3a02ea06 100644 --- a/drivers/gpu/drm/panthor/panthor_fw.c +++ b/drivers/gpu/drm/panthor/panthor_fw.c @@ -487,6 +487,7 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev, struct panthor_fw_binary_iter *iter, u32 ehdr) { + ssize_t vm_pgsz = panthor_vm_page_size(ptdev->fw->vm); struct panthor_fw_binary_section_entry_hdr hdr; struct panthor_fw_section *section; u32 section_size; @@ -515,8 +516,7 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev, return -EINVAL; } - if ((hdr.va.start & ~PAGE_MASK) != 0 || - (hdr.va.end & ~PAGE_MASK) != 0) { + if (!IS_ALIGNED(hdr.va.start, vm_pgsz) || !IS_ALIGNED(hdr.va.end, vm_pgsz)) { drm_err(&ptdev->base, "Firmware corrupted, virtual addresses not page aligned: 0x%x-0x%x\n", hdr.va.start, hdr.va.end); return -EINVAL; diff --git a/drivers/gpu/drm/panthor/panthor_gem.c b/drivers/gpu/drm/panthor/panthor_gem.c index 38f560864879..be97d56bc011 100644 --- a/drivers/gpu/drm/panthor/panthor_gem.c +++ b/drivers/gpu/drm/panthor/panthor_gem.c @@ -44,8 +44,7 @@ void panthor_kernel_bo_destroy(struct panthor_kernel_bo *bo) to_panthor_bo(bo->obj)->exclusive_vm_root_gem != panthor_vm_root_gem(vm))) goto out_free_bo; - ret = panthor_vm_unmap_range(vm, bo->va_node.start, - panthor_kernel_bo_size(bo)); + ret = panthor_vm_unmap_range(vm, bo->va_node.start, bo->va_node.size); if (ret) goto out_free_bo; @@ -95,10 +94,16 @@ panthor_kernel_bo_create(struct panthor_device *ptdev, struct panthor_vm *vm, } bo = to_panthor_bo(&obj->base); - size = obj->base.size; kbo->obj = &obj->base; bo->flags = bo_flags; + /* The system and GPU MMU page size might differ, which becomes a + * problem for FW sections that need to be mapped at explicit address + * since our PAGE_SIZE alignment might cover a VA range that's + * expected to be used for another section. + * Make sure we never map more than we need. + */ + size = ALIGN(size, panthor_vm_page_size(vm)); ret = panthor_vm_alloc_va(vm, gpu_va, size, &kbo->va_node); if (ret) goto err_put_obj; diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c index 3cd2bce59edc..5d5e25b1be95 100644 --- a/drivers/gpu/drm/panthor/panthor_mmu.c +++ b/drivers/gpu/drm/panthor/panthor_mmu.c @@ -826,6 +826,14 @@ void panthor_vm_idle(struct panthor_vm *vm) mutex_unlock(&ptdev->mmu->as.slots_lock); } +u32 panthor_vm_page_size(struct panthor_vm *vm) +{ + const struct io_pgtable *pgt = io_pgtable_ops_to_pgtable(vm->pgtbl_ops); + u32 pg_shift = ffs(pgt->cfg.pgsize_bitmap) - 1; + + return 1u << pg_shift; +} + static void panthor_vm_stop(struct panthor_vm *vm) { drm_sched_stop(&vm->sched, NULL); @@ -1025,12 +1033,13 @@ int panthor_vm_alloc_va(struct panthor_vm *vm, u64 va, u64 size, struct drm_mm_node *va_node) { + ssize_t vm_pgsz = panthor_vm_page_size(vm); int ret; - if (!size || (size & ~PAGE_MASK)) + if (!size || !IS_ALIGNED(size, vm_pgsz)) return -EINVAL; - if (va != PANTHOR_VM_KERNEL_AUTO_VA && (va & ~PAGE_MASK)) + if (va != PANTHOR_VM_KERNEL_AUTO_VA && !IS_ALIGNED(va, vm_pgsz)) return -EINVAL; mutex_lock(&vm->mm_lock); @@ -2366,11 +2375,12 @@ panthor_vm_bind_prepare_op_ctx(struct drm_file *file, const struct drm_panthor_vm_bind_op *op, struct panthor_vm_op_ctx *op_ctx) { + ssize_t vm_pgsz = panthor_vm_page_size(vm); struct drm_gem_object *gem; int ret; /* Aligned on page size. */ - if ((op->va | op->size) & ~PAGE_MASK) + if (!IS_ALIGNED(op->va | op->size, vm_pgsz)) return -EINVAL; switch (op->flags & DRM_PANTHOR_VM_BIND_OP_TYPE_MASK) { diff --git a/drivers/gpu/drm/panthor/panthor_mmu.h b/drivers/gpu/drm/panthor/panthor_mmu.h index 6788771071e3..8d21e83d8aba 100644 --- a/drivers/gpu/drm/panthor/panthor_mmu.h +++ b/drivers/gpu/drm/panthor/panthor_mmu.h @@ -30,6 +30,7 @@ panthor_vm_get_bo_for_va(struct panthor_vm *vm, u64 va, u64 *bo_offset); int panthor_vm_active(struct panthor_vm *vm); void panthor_vm_idle(struct panthor_vm *vm); +u32 panthor_vm_page_size(struct panthor_vm *vm); int panthor_vm_as(struct panthor_vm *vm); int panthor_vm_flush_all(struct panthor_vm *vm); diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c index aee362abb710..9929e22f4d8d 100644 --- a/drivers/gpu/drm/panthor/panthor_sched.c +++ b/drivers/gpu/drm/panthor/panthor_sched.c @@ -589,10 +589,11 @@ struct panthor_group { * @timedout: True when a timeout occurred on any of the queues owned by * this group. * - * Timeouts can be reported by drm_sched or by the FW. In any case, any - * timeout situation is unrecoverable, and the group becomes useless. - * We simply wait for all references to be dropped so we can release the - * group object. + * Timeouts can be reported by drm_sched or by the FW. If a reset is required, + * and the group can't be suspended, this also leads to a timeout. In any case, + * any timeout situation is unrecoverable, and the group becomes useless. We + * simply wait for all references to be dropped so we can release the group + * object. */ bool timedout; @@ -2640,6 +2641,12 @@ void panthor_sched_suspend(struct panthor_device *ptdev) csgs_upd_ctx_init(&upd_ctx); while (slot_mask) { u32 csg_id = ffs(slot_mask) - 1; + struct panthor_csg_slot *csg_slot = &sched->csg_slots[csg_id]; + + /* We consider group suspension failures as fatal and flag the + * group as unusable by setting timedout=true. + */ + csg_slot->group->timedout = true; csgs_upd_ctx_queue_reqs(ptdev, &upd_ctx, csg_id, CSG_STATE_TERMINATE, @@ -3409,6 +3416,11 @@ panthor_job_create(struct panthor_file *pfile, goto err_put_job; } + if (!group_can_run(job->group)) { + ret = -EINVAL; + goto err_put_job; + } + if (job->queue_idx >= job->group->queue_count || !job->group->queues[job->queue_idx]) { ret = -EINVAL; diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c index eaef20f41786..e97c6c60bc96 100644 --- a/drivers/gpu/drm/scheduler/sched_main.c +++ b/drivers/gpu/drm/scheduler/sched_main.c @@ -1276,10 +1276,11 @@ int drm_sched_init(struct drm_gpu_scheduler *sched, sched->own_submit_wq = false; } else { #ifdef CONFIG_LOCKDEP - sched->submit_wq = alloc_ordered_workqueue_lockdep_map(name, 0, + sched->submit_wq = alloc_ordered_workqueue_lockdep_map(name, + WQ_MEM_RECLAIM, &drm_sched_lockdep_map); #else - sched->submit_wq = alloc_ordered_workqueue(name, 0); + sched->submit_wq = alloc_ordered_workqueue(name, WQ_MEM_RECLAIM); #endif if (!sched->submit_wq) return -ENOMEM; diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index c9eb329665ec..34d22ba210b0 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -1153,8 +1153,8 @@ static int host1x_drm_probe(struct host1x_device *dev) if (host1x_drm_wants_iommu(dev) && device_iommu_mapped(dma_dev)) { tegra->domain = iommu_paging_domain_alloc(dma_dev); - if (!tegra->domain) { - err = -ENOMEM; + if (IS_ERR(tegra->domain)) { + err = PTR_ERR(tegra->domain); goto free; } diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c index 15e36a8db685..6bba97d0be88 100644 --- a/drivers/gpu/drm/tests/drm_connector_test.c +++ b/drivers/gpu/drm/tests/drm_connector_test.c @@ -996,7 +996,7 @@ static void drm_test_drm_hdmi_compute_mode_clock_rgb(struct kunit *test) unsigned long long rate; struct drm_device *drm = &priv->drm; - mode = drm_display_mode_from_cea_vic(drm, 16); + mode = drm_kunit_display_mode_from_cea_vic(test, drm, 16); KUNIT_ASSERT_NOT_NULL(test, mode); KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK); @@ -1017,7 +1017,7 @@ static void drm_test_drm_hdmi_compute_mode_clock_rgb_10bpc(struct kunit *test) unsigned long long rate; struct drm_device *drm = &priv->drm; - mode = drm_display_mode_from_cea_vic(drm, 16); + mode = drm_kunit_display_mode_from_cea_vic(test, drm, 16); KUNIT_ASSERT_NOT_NULL(test, mode); KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK); @@ -1038,7 +1038,7 @@ static void drm_test_drm_hdmi_compute_mode_clock_rgb_10bpc_vic_1(struct kunit *t unsigned long long rate; struct drm_device *drm = &priv->drm; - mode = drm_display_mode_from_cea_vic(drm, 1); + mode = drm_kunit_display_mode_from_cea_vic(test, drm, 1); KUNIT_ASSERT_NOT_NULL(test, mode); rate = drm_hdmi_compute_mode_clock(mode, 10, HDMI_COLORSPACE_RGB); @@ -1056,7 +1056,7 @@ static void drm_test_drm_hdmi_compute_mode_clock_rgb_12bpc(struct kunit *test) unsigned long long rate; struct drm_device *drm = &priv->drm; - mode = drm_display_mode_from_cea_vic(drm, 16); + mode = drm_kunit_display_mode_from_cea_vic(test, drm, 16); KUNIT_ASSERT_NOT_NULL(test, mode); KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK); @@ -1077,7 +1077,7 @@ static void drm_test_drm_hdmi_compute_mode_clock_rgb_12bpc_vic_1(struct kunit *t unsigned long long rate; struct drm_device *drm = &priv->drm; - mode = drm_display_mode_from_cea_vic(drm, 1); + mode = drm_kunit_display_mode_from_cea_vic(test, drm, 1); KUNIT_ASSERT_NOT_NULL(test, mode); rate = drm_hdmi_compute_mode_clock(mode, 12, HDMI_COLORSPACE_RGB); @@ -1095,7 +1095,7 @@ static void drm_test_drm_hdmi_compute_mode_clock_rgb_double(struct kunit *test) unsigned long long rate; struct drm_device *drm = &priv->drm; - mode = drm_display_mode_from_cea_vic(drm, 6); + mode = drm_kunit_display_mode_from_cea_vic(test, drm, 6); KUNIT_ASSERT_NOT_NULL(test, mode); KUNIT_ASSERT_TRUE(test, mode->flags & DRM_MODE_FLAG_DBLCLK); @@ -1118,7 +1118,7 @@ static void drm_test_connector_hdmi_compute_mode_clock_yuv420_valid(struct kunit unsigned long long rate; unsigned int vic = *(unsigned int *)test->param_value; - mode = drm_display_mode_from_cea_vic(drm, vic); + mode = drm_kunit_display_mode_from_cea_vic(test, drm, vic); KUNIT_ASSERT_NOT_NULL(test, mode); KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK); @@ -1155,7 +1155,7 @@ static void drm_test_connector_hdmi_compute_mode_clock_yuv420_10_bpc(struct kuni drm_hdmi_compute_mode_clock_yuv420_vic_valid_tests[0]; unsigned long long rate; - mode = drm_display_mode_from_cea_vic(drm, vic); + mode = drm_kunit_display_mode_from_cea_vic(test, drm, vic); KUNIT_ASSERT_NOT_NULL(test, mode); KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK); @@ -1180,7 +1180,7 @@ static void drm_test_connector_hdmi_compute_mode_clock_yuv420_12_bpc(struct kuni drm_hdmi_compute_mode_clock_yuv420_vic_valid_tests[0]; unsigned long long rate; - mode = drm_display_mode_from_cea_vic(drm, vic); + mode = drm_kunit_display_mode_from_cea_vic(test, drm, vic); KUNIT_ASSERT_NOT_NULL(test, mode); KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK); @@ -1203,7 +1203,7 @@ static void drm_test_connector_hdmi_compute_mode_clock_yuv422_8_bpc(struct kunit struct drm_device *drm = &priv->drm; unsigned long long rate; - mode = drm_display_mode_from_cea_vic(drm, 16); + mode = drm_kunit_display_mode_from_cea_vic(test, drm, 16); KUNIT_ASSERT_NOT_NULL(test, mode); KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK); @@ -1225,7 +1225,7 @@ static void drm_test_connector_hdmi_compute_mode_clock_yuv422_10_bpc(struct kuni struct drm_device *drm = &priv->drm; unsigned long long rate; - mode = drm_display_mode_from_cea_vic(drm, 16); + mode = drm_kunit_display_mode_from_cea_vic(test, drm, 16); KUNIT_ASSERT_NOT_NULL(test, mode); KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK); @@ -1247,7 +1247,7 @@ static void drm_test_connector_hdmi_compute_mode_clock_yuv422_12_bpc(struct kuni struct drm_device *drm = &priv->drm; unsigned long long rate; - mode = drm_display_mode_from_cea_vic(drm, 16); + mode = drm_kunit_display_mode_from_cea_vic(test, drm, 16); KUNIT_ASSERT_NOT_NULL(test, mode); KUNIT_ASSERT_FALSE(test, mode->flags & DRM_MODE_FLAG_DBLCLK); diff --git a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c index 34ee95d41f29..294773342e71 100644 --- a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c +++ b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c @@ -441,7 +441,7 @@ static void drm_test_check_broadcast_rgb_auto_cea_mode_vic_1(struct kunit *test) ctx = drm_kunit_helper_acquire_ctx_alloc(test); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); - mode = drm_display_mode_from_cea_vic(drm, 1); + mode = drm_kunit_display_mode_from_cea_vic(test, drm, 1); KUNIT_ASSERT_NOT_NULL(test, mode); drm = &priv->drm; @@ -555,7 +555,7 @@ static void drm_test_check_broadcast_rgb_full_cea_mode_vic_1(struct kunit *test) ctx = drm_kunit_helper_acquire_ctx_alloc(test); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); - mode = drm_display_mode_from_cea_vic(drm, 1); + mode = drm_kunit_display_mode_from_cea_vic(test, drm, 1); KUNIT_ASSERT_NOT_NULL(test, mode); drm = &priv->drm; @@ -671,7 +671,7 @@ static void drm_test_check_broadcast_rgb_limited_cea_mode_vic_1(struct kunit *te ctx = drm_kunit_helper_acquire_ctx_alloc(test); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); - mode = drm_display_mode_from_cea_vic(drm, 1); + mode = drm_kunit_display_mode_from_cea_vic(test, drm, 1); KUNIT_ASSERT_NOT_NULL(test, mode); drm = &priv->drm; @@ -1263,7 +1263,7 @@ static void drm_test_check_output_bpc_format_vic_1(struct kunit *test) ctx = drm_kunit_helper_acquire_ctx_alloc(test); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); - mode = drm_display_mode_from_cea_vic(drm, 1); + mode = drm_kunit_display_mode_from_cea_vic(test, drm, 1); KUNIT_ASSERT_NOT_NULL(test, mode); /* diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.c b/drivers/gpu/drm/tests/drm_kunit_helpers.c index aa62719dab0e..04a6b8cc62ac 100644 --- a/drivers/gpu/drm/tests/drm_kunit_helpers.c +++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c @@ -3,6 +3,7 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_drv.h> +#include <drm/drm_edid.h> #include <drm/drm_fourcc.h> #include <drm/drm_kunit_helpers.h> #include <drm/drm_managed.h> @@ -311,6 +312,47 @@ drm_kunit_helper_create_crtc(struct kunit *test, } EXPORT_SYMBOL_GPL(drm_kunit_helper_create_crtc); +static void kunit_action_drm_mode_destroy(void *ptr) +{ + struct drm_display_mode *mode = ptr; + + drm_mode_destroy(NULL, mode); +} + +/** + * drm_kunit_display_mode_from_cea_vic() - return a mode for CEA VIC + for a KUnit test + * @test: The test context object + * @dev: DRM device + * @video_code: CEA VIC of the mode + * + * Creates a new mode matching the specified CEA VIC for a KUnit test. + * + * Resources will be cleaned up automatically. + * + * Returns: A new drm_display_mode on success or NULL on failure + */ +struct drm_display_mode * +drm_kunit_display_mode_from_cea_vic(struct kunit *test, struct drm_device *dev, + u8 video_code) +{ + struct drm_display_mode *mode; + int ret; + + mode = drm_display_mode_from_cea_vic(dev, video_code); + if (!mode) + return NULL; + + ret = kunit_add_action_or_reset(test, + kunit_action_drm_mode_destroy, + mode); + if (ret) + return NULL; + + return mode; +} +EXPORT_SYMBOL_GPL(drm_kunit_display_mode_from_cea_vic); + MODULE_AUTHOR("Maxime Ripard <maxime@cerno.tech>"); MODULE_DESCRIPTION("KUnit test suite helper functions"); MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c index 75736faf2a80..c6e0c8d77a70 100644 --- a/drivers/gpu/drm/xe/display/xe_display.c +++ b/drivers/gpu/drm/xe/display/xe_display.c @@ -309,18 +309,7 @@ static void xe_display_flush_cleanup_work(struct xe_device *xe) } /* TODO: System and runtime suspend/resume sequences will be sanitized as a follow-up. */ -void xe_display_pm_runtime_suspend(struct xe_device *xe) -{ - if (!xe->info.probe_display) - return; - - if (xe->d3cold.allowed) - xe_display_pm_suspend(xe, true); - - intel_hpd_poll_enable(xe); -} - -void xe_display_pm_suspend(struct xe_device *xe, bool runtime) +static void __xe_display_pm_suspend(struct xe_device *xe, bool runtime) { struct intel_display *display = &xe->display; bool s2idle = suspend_to_idle(); @@ -353,28 +342,38 @@ void xe_display_pm_suspend(struct xe_device *xe, bool runtime) intel_opregion_suspend(display, s2idle ? PCI_D1 : PCI_D3cold); intel_dmc_suspend(xe); + + if (runtime && has_display(xe)) + intel_hpd_poll_enable(xe); } -void xe_display_pm_suspend_late(struct xe_device *xe) +void xe_display_pm_suspend(struct xe_device *xe) +{ + __xe_display_pm_suspend(xe, false); +} + +void xe_display_pm_runtime_suspend(struct xe_device *xe) { - bool s2idle = suspend_to_idle(); if (!xe->info.probe_display) return; - intel_power_domains_suspend(xe, s2idle); + if (xe->d3cold.allowed) { + __xe_display_pm_suspend(xe, true); + return; + } - intel_display_power_suspend_late(xe); + intel_hpd_poll_enable(xe); } -void xe_display_pm_runtime_resume(struct xe_device *xe) +void xe_display_pm_suspend_late(struct xe_device *xe) { + bool s2idle = suspend_to_idle(); if (!xe->info.probe_display) return; - intel_hpd_poll_disable(xe); + intel_power_domains_suspend(xe, s2idle); - if (xe->d3cold.allowed) - xe_display_pm_resume(xe, true); + intel_display_power_suspend_late(xe); } void xe_display_pm_resume_early(struct xe_device *xe) @@ -387,7 +386,7 @@ void xe_display_pm_resume_early(struct xe_device *xe) intel_power_domains_resume(xe); } -void xe_display_pm_resume(struct xe_device *xe, bool runtime) +static void __xe_display_pm_resume(struct xe_device *xe, bool runtime) { struct intel_display *display = &xe->display; @@ -411,9 +410,11 @@ void xe_display_pm_resume(struct xe_device *xe, bool runtime) intel_display_driver_resume(xe); drm_kms_helper_poll_enable(&xe->drm); intel_display_driver_enable_user_access(xe); - intel_hpd_poll_disable(xe); } + if (has_display(xe)) + intel_hpd_poll_disable(xe); + intel_opregion_resume(display); intel_fbdev_set_suspend(&xe->drm, FBINFO_STATE_RUNNING, false); @@ -421,6 +422,26 @@ void xe_display_pm_resume(struct xe_device *xe, bool runtime) intel_power_domains_enable(xe); } +void xe_display_pm_resume(struct xe_device *xe) +{ + __xe_display_pm_resume(xe, false); +} + +void xe_display_pm_runtime_resume(struct xe_device *xe) +{ + if (!xe->info.probe_display) + return; + + if (xe->d3cold.allowed) { + __xe_display_pm_resume(xe, true); + return; + } + + intel_hpd_init(xe); + intel_hpd_poll_disable(xe); +} + + static void display_device_remove(struct drm_device *dev, void *arg) { struct xe_device *xe = arg; diff --git a/drivers/gpu/drm/xe/display/xe_display.h b/drivers/gpu/drm/xe/display/xe_display.h index 53d727fd792b..bed55fd26f30 100644 --- a/drivers/gpu/drm/xe/display/xe_display.h +++ b/drivers/gpu/drm/xe/display/xe_display.h @@ -34,10 +34,10 @@ void xe_display_irq_enable(struct xe_device *xe, u32 gu_misc_iir); void xe_display_irq_reset(struct xe_device *xe); void xe_display_irq_postinstall(struct xe_device *xe, struct xe_gt *gt); -void xe_display_pm_suspend(struct xe_device *xe, bool runtime); +void xe_display_pm_suspend(struct xe_device *xe); void xe_display_pm_suspend_late(struct xe_device *xe); void xe_display_pm_resume_early(struct xe_device *xe); -void xe_display_pm_resume(struct xe_device *xe, bool runtime); +void xe_display_pm_resume(struct xe_device *xe); void xe_display_pm_runtime_suspend(struct xe_device *xe); void xe_display_pm_runtime_resume(struct xe_device *xe); @@ -65,10 +65,10 @@ static inline void xe_display_irq_enable(struct xe_device *xe, u32 gu_misc_iir) static inline void xe_display_irq_reset(struct xe_device *xe) {} static inline void xe_display_irq_postinstall(struct xe_device *xe, struct xe_gt *gt) {} -static inline void xe_display_pm_suspend(struct xe_device *xe, bool runtime) {} +static inline void xe_display_pm_suspend(struct xe_device *xe) {} static inline void xe_display_pm_suspend_late(struct xe_device *xe) {} static inline void xe_display_pm_resume_early(struct xe_device *xe) {} -static inline void xe_display_pm_resume(struct xe_device *xe, bool runtime) {} +static inline void xe_display_pm_resume(struct xe_device *xe) {} static inline void xe_display_pm_runtime_suspend(struct xe_device *xe) {} static inline void xe_display_pm_runtime_resume(struct xe_device *xe) {} diff --git a/drivers/gpu/drm/xe/xe_ggtt.c b/drivers/gpu/drm/xe/xe_ggtt.c index 2895f154654c..ff19eca5d358 100644 --- a/drivers/gpu/drm/xe/xe_ggtt.c +++ b/drivers/gpu/drm/xe/xe_ggtt.c @@ -397,6 +397,16 @@ static void ggtt_invalidate_gt_tlb(struct xe_gt *gt) static void xe_ggtt_invalidate(struct xe_ggtt *ggtt) { + struct xe_device *xe = tile_to_xe(ggtt->tile); + + /* + * XXX: Barrier for GGTT pages. Unsure exactly why this required but + * without this LNL is having issues with the GuC reading scratch page + * vs. correct GGTT page. Not particularly a hot code path so blindly + * do a mmio read here which results in GuC reading correct GGTT page. + */ + xe_mmio_read32(xe_root_mmio_gt(xe), VF_CAP_REG); + /* Each GT in a tile has its own TLB to cache GGTT lookups */ ggtt_invalidate_gt_tlb(ggtt->tile->primary_gt); ggtt_invalidate_gt_tlb(ggtt->tile->media_gt); diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c index d333be9c4227..f903b0772722 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.c +++ b/drivers/gpu/drm/xe/xe_guc_submit.c @@ -916,12 +916,22 @@ static void xe_guc_exec_queue_lr_cleanup(struct work_struct *w) static bool check_timeout(struct xe_exec_queue *q, struct xe_sched_job *job) { struct xe_gt *gt = guc_to_gt(exec_queue_to_guc(q)); - u32 ctx_timestamp = xe_lrc_ctx_timestamp(q->lrc[0]); - u32 ctx_job_timestamp = xe_lrc_ctx_job_timestamp(q->lrc[0]); + u32 ctx_timestamp, ctx_job_timestamp; u32 timeout_ms = q->sched_props.job_timeout_ms; u32 diff; u64 running_time_ms; + if (!xe_sched_job_started(job)) { + xe_gt_warn(gt, "Check job timeout: seqno=%u, lrc_seqno=%u, guc_id=%d, not started", + xe_sched_job_seqno(job), xe_sched_job_lrc_seqno(job), + q->guc->id); + + return xe_sched_invalidate_job(job, 2); + } + + ctx_timestamp = xe_lrc_ctx_timestamp(q->lrc[0]); + ctx_job_timestamp = xe_lrc_ctx_job_timestamp(q->lrc[0]); + /* * Counter wraps at ~223s at the usual 19.2MHz, be paranoid catch * possible overflows with a high timeout. @@ -1049,10 +1059,6 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job) exec_queue_killed_or_banned_or_wedged(q) || exec_queue_destroyed(q); - /* Job hasn't started, can't be timed out */ - if (!skip_timeout_check && !xe_sched_job_started(job)) - goto rearm; - /* * XXX: Sampling timeout doesn't work in wedged mode as we have to * modify scheduling state to read timestamp. We could read the diff --git a/drivers/gpu/drm/xe/xe_pm.c b/drivers/gpu/drm/xe/xe_pm.c index 7cf2160fe040..33eb039053e4 100644 --- a/drivers/gpu/drm/xe/xe_pm.c +++ b/drivers/gpu/drm/xe/xe_pm.c @@ -123,7 +123,7 @@ int xe_pm_suspend(struct xe_device *xe) for_each_gt(gt, xe, id) xe_gt_suspend_prepare(gt); - xe_display_pm_suspend(xe, false); + xe_display_pm_suspend(xe); /* FIXME: Super racey... */ err = xe_bo_evict_all(xe); @@ -133,7 +133,7 @@ int xe_pm_suspend(struct xe_device *xe) for_each_gt(gt, xe, id) { err = xe_gt_suspend(gt); if (err) { - xe_display_pm_resume(xe, false); + xe_display_pm_resume(xe); goto err; } } @@ -187,7 +187,7 @@ int xe_pm_resume(struct xe_device *xe) for_each_gt(gt, xe, id) xe_gt_resume(gt); - xe_display_pm_resume(xe, false); + xe_display_pm_resume(xe); err = xe_bo_restore_user(xe); if (err) diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c index a5d91933f505..b79c48d46ccc 100644 --- a/drivers/iio/adc/ad7124.c +++ b/drivers/iio/adc/ad7124.c @@ -637,7 +637,7 @@ static int ad7124_write_raw(struct iio_dev *indio_dev, switch (info) { case IIO_CHAN_INFO_SAMP_FREQ: - if (val2 != 0) { + if (val2 != 0 || val == 0) { ret = -EINVAL; break; } diff --git a/drivers/iio/adc/ad7380.c b/drivers/iio/adc/ad7380.c index e8bddfb0d07d..fb728570debe 100644 --- a/drivers/iio/adc/ad7380.c +++ b/drivers/iio/adc/ad7380.c @@ -75,6 +75,7 @@ #define T_CONVERT_NS 190 /* conversion time */ #define T_CONVERT_0_NS 10 /* 1st conversion start time (oversampling) */ #define T_CONVERT_X_NS 500 /* xth conversion start time (oversampling) */ +#define T_POWERUP_US 5000 /* Power up */ struct ad7380_timing_specs { const unsigned int t_csh_ns; /* CS minimum high time */ @@ -86,6 +87,9 @@ struct ad7380_chip_info { unsigned int num_channels; unsigned int num_simult_channels; bool has_mux; + const char * const *supplies; + unsigned int num_supplies; + bool external_ref_only; const char * const *vcm_supplies; unsigned int num_vcm_supplies; const unsigned long *available_scan_masks; @@ -243,6 +247,10 @@ DEFINE_AD7380_8_CHANNEL(ad7386_4_channels, 16, 0, u); DEFINE_AD7380_8_CHANNEL(ad7387_4_channels, 14, 0, u); DEFINE_AD7380_8_CHANNEL(ad7388_4_channels, 12, 0, u); +static const char * const ad7380_supplies[] = { + "vcc", "vlogic", +}; + static const char * const ad7380_2_channel_vcm_supplies[] = { "aina", "ainb", }; @@ -338,6 +346,8 @@ static const struct ad7380_chip_info ad7380_chip_info = { .channels = ad7380_channels, .num_channels = ARRAY_SIZE(ad7380_channels), .num_simult_channels = 2, + .supplies = ad7380_supplies, + .num_supplies = ARRAY_SIZE(ad7380_supplies), .available_scan_masks = ad7380_2_channel_scan_masks, .timing_specs = &ad7380_timing, }; @@ -347,6 +357,8 @@ static const struct ad7380_chip_info ad7381_chip_info = { .channels = ad7381_channels, .num_channels = ARRAY_SIZE(ad7381_channels), .num_simult_channels = 2, + .supplies = ad7380_supplies, + .num_supplies = ARRAY_SIZE(ad7380_supplies), .available_scan_masks = ad7380_2_channel_scan_masks, .timing_specs = &ad7380_timing, }; @@ -356,6 +368,8 @@ static const struct ad7380_chip_info ad7383_chip_info = { .channels = ad7383_channels, .num_channels = ARRAY_SIZE(ad7383_channels), .num_simult_channels = 2, + .supplies = ad7380_supplies, + .num_supplies = ARRAY_SIZE(ad7380_supplies), .vcm_supplies = ad7380_2_channel_vcm_supplies, .num_vcm_supplies = ARRAY_SIZE(ad7380_2_channel_vcm_supplies), .available_scan_masks = ad7380_2_channel_scan_masks, @@ -367,6 +381,8 @@ static const struct ad7380_chip_info ad7384_chip_info = { .channels = ad7384_channels, .num_channels = ARRAY_SIZE(ad7384_channels), .num_simult_channels = 2, + .supplies = ad7380_supplies, + .num_supplies = ARRAY_SIZE(ad7380_supplies), .vcm_supplies = ad7380_2_channel_vcm_supplies, .num_vcm_supplies = ARRAY_SIZE(ad7380_2_channel_vcm_supplies), .available_scan_masks = ad7380_2_channel_scan_masks, @@ -378,6 +394,8 @@ static const struct ad7380_chip_info ad7386_chip_info = { .channels = ad7386_channels, .num_channels = ARRAY_SIZE(ad7386_channels), .num_simult_channels = 2, + .supplies = ad7380_supplies, + .num_supplies = ARRAY_SIZE(ad7380_supplies), .has_mux = true, .available_scan_masks = ad7380_2x2_channel_scan_masks, .timing_specs = &ad7380_timing, @@ -388,6 +406,8 @@ static const struct ad7380_chip_info ad7387_chip_info = { .channels = ad7387_channels, .num_channels = ARRAY_SIZE(ad7387_channels), .num_simult_channels = 2, + .supplies = ad7380_supplies, + .num_supplies = ARRAY_SIZE(ad7380_supplies), .has_mux = true, .available_scan_masks = ad7380_2x2_channel_scan_masks, .timing_specs = &ad7380_timing, @@ -398,6 +418,8 @@ static const struct ad7380_chip_info ad7388_chip_info = { .channels = ad7388_channels, .num_channels = ARRAY_SIZE(ad7388_channels), .num_simult_channels = 2, + .supplies = ad7380_supplies, + .num_supplies = ARRAY_SIZE(ad7380_supplies), .has_mux = true, .available_scan_masks = ad7380_2x2_channel_scan_masks, .timing_specs = &ad7380_timing, @@ -408,6 +430,9 @@ static const struct ad7380_chip_info ad7380_4_chip_info = { .channels = ad7380_4_channels, .num_channels = ARRAY_SIZE(ad7380_4_channels), .num_simult_channels = 4, + .supplies = ad7380_supplies, + .num_supplies = ARRAY_SIZE(ad7380_supplies), + .external_ref_only = true, .available_scan_masks = ad7380_4_channel_scan_masks, .timing_specs = &ad7380_4_timing, }; @@ -417,6 +442,8 @@ static const struct ad7380_chip_info ad7381_4_chip_info = { .channels = ad7381_4_channels, .num_channels = ARRAY_SIZE(ad7381_4_channels), .num_simult_channels = 4, + .supplies = ad7380_supplies, + .num_supplies = ARRAY_SIZE(ad7380_supplies), .available_scan_masks = ad7380_4_channel_scan_masks, .timing_specs = &ad7380_4_timing, }; @@ -426,6 +453,8 @@ static const struct ad7380_chip_info ad7383_4_chip_info = { .channels = ad7383_4_channels, .num_channels = ARRAY_SIZE(ad7383_4_channels), .num_simult_channels = 4, + .supplies = ad7380_supplies, + .num_supplies = ARRAY_SIZE(ad7380_supplies), .vcm_supplies = ad7380_4_channel_vcm_supplies, .num_vcm_supplies = ARRAY_SIZE(ad7380_4_channel_vcm_supplies), .available_scan_masks = ad7380_4_channel_scan_masks, @@ -437,6 +466,8 @@ static const struct ad7380_chip_info ad7384_4_chip_info = { .channels = ad7384_4_channels, .num_channels = ARRAY_SIZE(ad7384_4_channels), .num_simult_channels = 4, + .supplies = ad7380_supplies, + .num_supplies = ARRAY_SIZE(ad7380_supplies), .vcm_supplies = ad7380_4_channel_vcm_supplies, .num_vcm_supplies = ARRAY_SIZE(ad7380_4_channel_vcm_supplies), .available_scan_masks = ad7380_4_channel_scan_masks, @@ -448,6 +479,8 @@ static const struct ad7380_chip_info ad7386_4_chip_info = { .channels = ad7386_4_channels, .num_channels = ARRAY_SIZE(ad7386_4_channels), .num_simult_channels = 4, + .supplies = ad7380_supplies, + .num_supplies = ARRAY_SIZE(ad7380_supplies), .has_mux = true, .available_scan_masks = ad7380_2x4_channel_scan_masks, .timing_specs = &ad7380_4_timing, @@ -458,6 +491,8 @@ static const struct ad7380_chip_info ad7387_4_chip_info = { .channels = ad7387_4_channels, .num_channels = ARRAY_SIZE(ad7387_4_channels), .num_simult_channels = 4, + .supplies = ad7380_supplies, + .num_supplies = ARRAY_SIZE(ad7380_supplies), .has_mux = true, .available_scan_masks = ad7380_2x4_channel_scan_masks, .timing_specs = &ad7380_4_timing, @@ -468,6 +503,8 @@ static const struct ad7380_chip_info ad7388_4_chip_info = { .channels = ad7388_4_channels, .num_channels = ARRAY_SIZE(ad7388_4_channels), .num_simult_channels = 4, + .supplies = ad7380_supplies, + .num_supplies = ARRAY_SIZE(ad7380_supplies), .has_mux = true, .available_scan_masks = ad7380_2x4_channel_scan_masks, .timing_specs = &ad7380_4_timing, @@ -956,7 +993,7 @@ static const struct iio_info ad7380_info = { .debugfs_reg_access = &ad7380_debugfs_reg_access, }; -static int ad7380_init(struct ad7380_state *st, struct regulator *vref) +static int ad7380_init(struct ad7380_state *st, bool external_ref_en) { int ret; @@ -968,13 +1005,13 @@ static int ad7380_init(struct ad7380_state *st, struct regulator *vref) if (ret < 0) return ret; - /* select internal or external reference voltage */ - ret = regmap_update_bits(st->regmap, AD7380_REG_ADDR_CONFIG1, - AD7380_CONFIG1_REFSEL, - FIELD_PREP(AD7380_CONFIG1_REFSEL, - vref ? 1 : 0)); - if (ret < 0) - return ret; + if (external_ref_en) { + /* select external reference voltage */ + ret = regmap_set_bits(st->regmap, AD7380_REG_ADDR_CONFIG1, + AD7380_CONFIG1_REFSEL); + if (ret < 0) + return ret; + } /* This is the default value after reset. */ st->oversampling_ratio = 1; @@ -987,16 +1024,11 @@ static int ad7380_init(struct ad7380_state *st, struct regulator *vref) FIELD_PREP(AD7380_CONFIG2_SDO, 1)); } -static void ad7380_regulator_disable(void *p) -{ - regulator_disable(p); -} - static int ad7380_probe(struct spi_device *spi) { struct iio_dev *indio_dev; struct ad7380_state *st; - struct regulator *vref; + bool external_ref_en; int ret, i; indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); @@ -1009,36 +1041,38 @@ static int ad7380_probe(struct spi_device *spi) if (!st->chip_info) return dev_err_probe(&spi->dev, -EINVAL, "missing match data\n"); - vref = devm_regulator_get_optional(&spi->dev, "refio"); - if (IS_ERR(vref)) { - if (PTR_ERR(vref) != -ENODEV) - return dev_err_probe(&spi->dev, PTR_ERR(vref), - "Failed to get refio regulator\n"); - - vref = NULL; - } + ret = devm_regulator_bulk_get_enable(&spi->dev, st->chip_info->num_supplies, + st->chip_info->supplies); - /* - * If there is no REFIO supply, then it means that we are using - * the internal 2.5V reference, otherwise REFIO is reference voltage. - */ - if (vref) { - ret = regulator_enable(vref); - if (ret) - return ret; - - ret = devm_add_action_or_reset(&spi->dev, - ad7380_regulator_disable, vref); - if (ret) - return ret; + if (ret) + return dev_err_probe(&spi->dev, ret, + "Failed to enable power supplies\n"); + fsleep(T_POWERUP_US); - ret = regulator_get_voltage(vref); + if (st->chip_info->external_ref_only) { + ret = devm_regulator_get_enable_read_voltage(&spi->dev, + "refin"); if (ret < 0) - return ret; + return dev_err_probe(&spi->dev, ret, + "Failed to get refin regulator\n"); st->vref_mv = ret / 1000; + + /* these chips don't have a register bit for this */ + external_ref_en = false; } else { - st->vref_mv = AD7380_INTERNAL_REF_MV; + /* + * If there is no REFIO supply, then it means that we are using + * the internal reference, otherwise REFIO is reference voltage. + */ + ret = devm_regulator_get_enable_read_voltage(&spi->dev, + "refio"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(&spi->dev, ret, + "Failed to get refio regulator\n"); + + external_ref_en = ret != -ENODEV; + st->vref_mv = external_ref_en ? ret / 1000 : AD7380_INTERNAL_REF_MV; } if (st->chip_info->num_vcm_supplies > ARRAY_SIZE(st->vcm_mv)) @@ -1050,27 +1084,13 @@ static int ad7380_probe(struct spi_device *spi) * input pin. */ for (i = 0; i < st->chip_info->num_vcm_supplies; i++) { - struct regulator *vcm; - - vcm = devm_regulator_get(&spi->dev, - st->chip_info->vcm_supplies[i]); - if (IS_ERR(vcm)) - return dev_err_probe(&spi->dev, PTR_ERR(vcm), - "Failed to get %s regulator\n", - st->chip_info->vcm_supplies[i]); + const char *vcm = st->chip_info->vcm_supplies[i]; - ret = regulator_enable(vcm); - if (ret) - return ret; - - ret = devm_add_action_or_reset(&spi->dev, - ad7380_regulator_disable, vcm); - if (ret) - return ret; - - ret = regulator_get_voltage(vcm); + ret = devm_regulator_get_enable_read_voltage(&spi->dev, vcm); if (ret < 0) - return ret; + return dev_err_probe(&spi->dev, ret, + "Failed to get %s regulator\n", + vcm); st->vcm_mv[i] = ret / 1000; } @@ -1135,7 +1155,7 @@ static int ad7380_probe(struct spi_device *spi) if (ret) return ret; - ret = ad7380_init(st, vref); + ret = ad7380_init(st, external_ref_en); if (ret) return ret; diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 45e337c6d256..9f5d5ebb8653 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -380,7 +380,7 @@ config LTC2632 config LTC2664 tristate "Analog Devices LTC2664 and LTC2672 DAC SPI driver" depends on SPI - select REGMAP + select REGMAP_SPI help Say yes here to build support for Analog Devices LTC2664 and LTC2672 converters (DAC). diff --git a/drivers/iio/industrialio-gts-helper.c b/drivers/iio/industrialio-gts-helper.c index 59d7615c0f56..5f131bc1a01e 100644 --- a/drivers/iio/industrialio-gts-helper.c +++ b/drivers/iio/industrialio-gts-helper.c @@ -307,13 +307,15 @@ static int iio_gts_build_avail_scale_table(struct iio_gts *gts) if (ret) goto err_free_out; + for (i = 0; i < gts->num_itime; i++) + kfree(per_time_gains[i]); kfree(per_time_gains); gts->per_time_avail_scale_tables = per_time_scales; return 0; err_free_out: - for (i--; i; i--) { + for (i--; i >= 0; i--) { kfree(per_time_scales[i]); kfree(per_time_gains[i]); } diff --git a/drivers/iio/light/veml6030.c b/drivers/iio/light/veml6030.c index 9630de1c578e..621428885455 100644 --- a/drivers/iio/light/veml6030.c +++ b/drivers/iio/light/veml6030.c @@ -522,7 +522,7 @@ static int veml6030_read_raw(struct iio_dev *indio_dev, } if (mask == IIO_CHAN_INFO_PROCESSED) { *val = (reg * data->cur_resolution) / 10000; - *val2 = (reg * data->cur_resolution) % 10000; + *val2 = (reg * data->cur_resolution) % 10000 * 100; return IIO_VAL_INT_PLUS_MICRO; } *val = reg; diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.c b/drivers/infiniband/hw/bnxt_re/qplib_fp.c index 2ebcb2de962b..7ad83566ab0f 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_fp.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.c @@ -1532,9 +1532,11 @@ int bnxt_qplib_destroy_qp(struct bnxt_qplib_res *res, u32 tbl_indx; int rc; + spin_lock_bh(&rcfw->tbl_lock); tbl_indx = map_qp_id_to_tbl_indx(qp->id, rcfw); rcfw->qp_tbl[tbl_indx].qp_id = BNXT_QPLIB_QP_ID_INVALID; rcfw->qp_tbl[tbl_indx].qp_handle = NULL; + spin_unlock_bh(&rcfw->tbl_lock); bnxt_qplib_rcfw_cmd_prep((struct cmdq_base *)&req, CMDQ_BASE_OPCODE_DESTROY_QP, @@ -1545,8 +1547,10 @@ int bnxt_qplib_destroy_qp(struct bnxt_qplib_res *res, sizeof(resp), 0); rc = bnxt_qplib_rcfw_send_message(rcfw, &msg); if (rc) { + spin_lock_bh(&rcfw->tbl_lock); rcfw->qp_tbl[tbl_indx].qp_id = qp->id; rcfw->qp_tbl[tbl_indx].qp_handle = qp; + spin_unlock_bh(&rcfw->tbl_lock); return rc; } diff --git a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c index 7294221b3316..e82bd37158ad 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c @@ -290,7 +290,6 @@ static int __send_message(struct bnxt_qplib_rcfw *rcfw, struct bnxt_qplib_hwq *hwq; u32 sw_prod, cmdq_prod; struct pci_dev *pdev; - unsigned long flags; u16 cookie; u8 *preq; @@ -301,7 +300,7 @@ static int __send_message(struct bnxt_qplib_rcfw *rcfw, /* Cmdq are in 16-byte units, each request can consume 1 or more * cmdqe */ - spin_lock_irqsave(&hwq->lock, flags); + spin_lock_bh(&hwq->lock); required_slots = bnxt_qplib_get_cmd_slots(msg->req); free_slots = HWQ_FREE_SLOTS(hwq); cookie = cmdq->seq_num & RCFW_MAX_COOKIE_VALUE; @@ -311,7 +310,7 @@ static int __send_message(struct bnxt_qplib_rcfw *rcfw, dev_info_ratelimited(&pdev->dev, "CMDQ is full req/free %d/%d!", required_slots, free_slots); - spin_unlock_irqrestore(&hwq->lock, flags); + spin_unlock_bh(&hwq->lock); return -EAGAIN; } if (msg->block) @@ -367,7 +366,7 @@ static int __send_message(struct bnxt_qplib_rcfw *rcfw, wmb(); writel(cmdq_prod, cmdq->cmdq_mbox.prod); writel(RCFW_CMDQ_TRIG_VAL, cmdq->cmdq_mbox.db); - spin_unlock_irqrestore(&hwq->lock, flags); + spin_unlock_bh(&hwq->lock); /* Return the CREQ response pointer */ return 0; } @@ -486,7 +485,6 @@ static int __bnxt_qplib_rcfw_send_message(struct bnxt_qplib_rcfw *rcfw, { struct creq_qp_event *evnt = (struct creq_qp_event *)msg->resp; struct bnxt_qplib_crsqe *crsqe; - unsigned long flags; u16 cookie; int rc; u8 opcode; @@ -512,12 +510,12 @@ static int __bnxt_qplib_rcfw_send_message(struct bnxt_qplib_rcfw *rcfw, rc = __poll_for_resp(rcfw, cookie); if (rc) { - spin_lock_irqsave(&rcfw->cmdq.hwq.lock, flags); + spin_lock_bh(&rcfw->cmdq.hwq.lock); crsqe = &rcfw->crsqe_tbl[cookie]; crsqe->is_waiter_alive = false; if (rc == -ENODEV) set_bit(FIRMWARE_STALL_DETECTED, &rcfw->cmdq.flags); - spin_unlock_irqrestore(&rcfw->cmdq.hwq.lock, flags); + spin_unlock_bh(&rcfw->cmdq.hwq.lock); return -ETIMEDOUT; } @@ -628,7 +626,6 @@ static int bnxt_qplib_process_qp_event(struct bnxt_qplib_rcfw *rcfw, u16 cookie, blocked = 0; bool is_waiter_alive; struct pci_dev *pdev; - unsigned long flags; u32 wait_cmds = 0; int rc = 0; @@ -637,17 +634,21 @@ static int bnxt_qplib_process_qp_event(struct bnxt_qplib_rcfw *rcfw, case CREQ_QP_EVENT_EVENT_QP_ERROR_NOTIFICATION: err_event = (struct creq_qp_error_notification *)qp_event; qp_id = le32_to_cpu(err_event->xid); + spin_lock(&rcfw->tbl_lock); tbl_indx = map_qp_id_to_tbl_indx(qp_id, rcfw); qp = rcfw->qp_tbl[tbl_indx].qp_handle; + if (!qp) { + spin_unlock(&rcfw->tbl_lock); + break; + } + bnxt_qplib_mark_qp_error(qp); + rc = rcfw->creq.aeq_handler(rcfw, qp_event, qp); + spin_unlock(&rcfw->tbl_lock); dev_dbg(&pdev->dev, "Received QP error notification\n"); dev_dbg(&pdev->dev, "qpid 0x%x, req_err=0x%x, resp_err=0x%x\n", qp_id, err_event->req_err_state_reason, err_event->res_err_state_reason); - if (!qp) - break; - bnxt_qplib_mark_qp_error(qp); - rc = rcfw->creq.aeq_handler(rcfw, qp_event, qp); break; default: /* @@ -659,8 +660,7 @@ static int bnxt_qplib_process_qp_event(struct bnxt_qplib_rcfw *rcfw, * */ - spin_lock_irqsave_nested(&hwq->lock, flags, - SINGLE_DEPTH_NESTING); + spin_lock_nested(&hwq->lock, SINGLE_DEPTH_NESTING); cookie = le16_to_cpu(qp_event->cookie); blocked = cookie & RCFW_CMD_IS_BLOCKING; cookie &= RCFW_MAX_COOKIE_VALUE; @@ -672,7 +672,7 @@ static int bnxt_qplib_process_qp_event(struct bnxt_qplib_rcfw *rcfw, dev_info(&pdev->dev, "rcfw timedout: cookie = %#x, free_slots = %d", cookie, crsqe->free_slots); - spin_unlock_irqrestore(&hwq->lock, flags); + spin_unlock(&hwq->lock); return rc; } @@ -720,7 +720,7 @@ static int bnxt_qplib_process_qp_event(struct bnxt_qplib_rcfw *rcfw, __destroy_timedout_ah(rcfw, (struct creq_create_ah_resp *) qp_event); - spin_unlock_irqrestore(&hwq->lock, flags); + spin_unlock(&hwq->lock); } *num_wait += wait_cmds; return rc; @@ -734,12 +734,11 @@ static void bnxt_qplib_service_creq(struct tasklet_struct *t) u32 type, budget = CREQ_ENTRY_POLL_BUDGET; struct bnxt_qplib_hwq *hwq = &creq->hwq; struct creq_base *creqe; - unsigned long flags; u32 num_wakeup = 0; u32 hw_polled = 0; /* Service the CREQ until budget is over */ - spin_lock_irqsave(&hwq->lock, flags); + spin_lock_bh(&hwq->lock); while (budget > 0) { creqe = bnxt_qplib_get_qe(hwq, hwq->cons, NULL); if (!CREQ_CMP_VALID(creqe, creq->creq_db.dbinfo.flags)) @@ -782,7 +781,7 @@ static void bnxt_qplib_service_creq(struct tasklet_struct *t) if (hw_polled) bnxt_qplib_ring_nq_db(&creq->creq_db.dbinfo, rcfw->res->cctx, true); - spin_unlock_irqrestore(&hwq->lock, flags); + spin_unlock_bh(&hwq->lock); if (num_wakeup) wake_up_nr(&rcfw->cmdq.waitq, num_wakeup); } @@ -978,6 +977,7 @@ int bnxt_qplib_alloc_rcfw_channel(struct bnxt_qplib_res *res, GFP_KERNEL); if (!rcfw->qp_tbl) goto fail; + spin_lock_init(&rcfw->tbl_lock); rcfw->max_timeout = res->cctx->hwrm_cmd_max_timeout; diff --git a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h index 45996e60a0d0..07779aeb7575 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h +++ b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h @@ -224,6 +224,8 @@ struct bnxt_qplib_rcfw { struct bnxt_qplib_crsqe *crsqe_tbl; int qp_tbl_size; struct bnxt_qplib_qp_node *qp_tbl; + /* To synchronize the qp-handle hash table */ + spinlock_t tbl_lock; u64 oos_prev; u32 init_oos_stats; u32 cmdq_depth; diff --git a/drivers/infiniband/hw/cxgb4/provider.c b/drivers/infiniband/hw/cxgb4/provider.c index 10a4c738b59f..e059f92d90fd 100644 --- a/drivers/infiniband/hw/cxgb4/provider.c +++ b/drivers/infiniband/hw/cxgb4/provider.c @@ -473,6 +473,7 @@ static const struct ib_device_ops c4iw_dev_ops = { .fill_res_cq_entry = c4iw_fill_res_cq_entry, .fill_res_cm_id_entry = c4iw_fill_res_cm_id_entry, .fill_res_mr_entry = c4iw_fill_res_mr_entry, + .fill_res_qp_entry = c4iw_fill_res_qp_entry, .get_dev_fw_str = get_dev_fw_str, .get_dma_mr = c4iw_get_dma_mr, .get_hw_stats = c4iw_get_mib, diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index e39b1a101e97..10ce3b44f645 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -4268,14 +4268,14 @@ static int __mlx5_ib_modify_qp(struct ib_qp *ibqp, MLX5_SET(qpc, qpc, retry_count, attr->retry_cnt); if (attr_mask & IB_QP_MAX_QP_RD_ATOMIC && attr->max_rd_atomic) - MLX5_SET(qpc, qpc, log_sra_max, ilog2(attr->max_rd_atomic)); + MLX5_SET(qpc, qpc, log_sra_max, fls(attr->max_rd_atomic - 1)); if (attr_mask & IB_QP_SQ_PSN) MLX5_SET(qpc, qpc, next_send_psn, attr->sq_psn); if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC && attr->max_dest_rd_atomic) MLX5_SET(qpc, qpc, log_rra_max, - ilog2(attr->max_dest_rd_atomic)); + fls(attr->max_dest_rd_atomic - 1)); if (attr_mask & (IB_QP_ACCESS_FLAGS | IB_QP_MAX_DEST_RD_ATOMIC)) { err = set_qpc_atomic_flags(qp, attr, attr_mask, qpc); diff --git a/drivers/input/input.c b/drivers/input/input.c index 47fac29cf7c3..c51858f1cdc5 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -119,12 +119,12 @@ static void input_pass_values(struct input_dev *dev, handle = rcu_dereference(dev->grab); if (handle) { - count = handle->handler->events(handle, vals, count); + count = handle->handle_events(handle, vals, count); } else { list_for_each_entry_rcu(handle, &dev->h_list, d_node) if (handle->open) { - count = handle->handler->events(handle, vals, - count); + count = handle->handle_events(handle, vals, + count); if (!count) break; } @@ -2534,57 +2534,6 @@ static int input_handler_check_methods(const struct input_handler *handler) return 0; } -/* - * An implementation of input_handler's events() method that simply - * invokes handler->event() method for each event one by one. - */ -static unsigned int input_handler_events_default(struct input_handle *handle, - struct input_value *vals, - unsigned int count) -{ - struct input_handler *handler = handle->handler; - struct input_value *v; - - for (v = vals; v != vals + count; v++) - handler->event(handle, v->type, v->code, v->value); - - return count; -} - -/* - * An implementation of input_handler's events() method that invokes - * handler->filter() method for each event one by one and removes events - * that were filtered out from the "vals" array. - */ -static unsigned int input_handler_events_filter(struct input_handle *handle, - struct input_value *vals, - unsigned int count) -{ - struct input_handler *handler = handle->handler; - struct input_value *end = vals; - struct input_value *v; - - for (v = vals; v != vals + count; v++) { - if (handler->filter(handle, v->type, v->code, v->value)) - continue; - if (end != v) - *end = *v; - end++; - } - - return end - vals; -} - -/* - * An implementation of input_handler's events() method that does nothing. - */ -static unsigned int input_handler_events_null(struct input_handle *handle, - struct input_value *vals, - unsigned int count) -{ - return count; -} - /** * input_register_handler - register a new input handler * @handler: handler to be registered @@ -2604,13 +2553,6 @@ int input_register_handler(struct input_handler *handler) INIT_LIST_HEAD(&handler->h_list); - if (handler->filter) - handler->events = input_handler_events_filter; - else if (handler->event) - handler->events = input_handler_events_default; - else if (!handler->events) - handler->events = input_handler_events_null; - error = mutex_lock_interruptible(&input_mutex); if (error) return error; @@ -2684,6 +2626,75 @@ int input_handler_for_each_handle(struct input_handler *handler, void *data, } EXPORT_SYMBOL(input_handler_for_each_handle); +/* + * An implementation of input_handle's handle_events() method that simply + * invokes handler->event() method for each event one by one. + */ +static unsigned int input_handle_events_default(struct input_handle *handle, + struct input_value *vals, + unsigned int count) +{ + struct input_handler *handler = handle->handler; + struct input_value *v; + + for (v = vals; v != vals + count; v++) + handler->event(handle, v->type, v->code, v->value); + + return count; +} + +/* + * An implementation of input_handle's handle_events() method that invokes + * handler->filter() method for each event one by one and removes events + * that were filtered out from the "vals" array. + */ +static unsigned int input_handle_events_filter(struct input_handle *handle, + struct input_value *vals, + unsigned int count) +{ + struct input_handler *handler = handle->handler; + struct input_value *end = vals; + struct input_value *v; + + for (v = vals; v != vals + count; v++) { + if (handler->filter(handle, v->type, v->code, v->value)) + continue; + if (end != v) + *end = *v; + end++; + } + + return end - vals; +} + +/* + * An implementation of input_handle's handle_events() method that does nothing. + */ +static unsigned int input_handle_events_null(struct input_handle *handle, + struct input_value *vals, + unsigned int count) +{ + return count; +} + +/* + * Sets up appropriate handle->event_handler based on the input_handler + * associated with the handle. + */ +static void input_handle_setup_event_handler(struct input_handle *handle) +{ + struct input_handler *handler = handle->handler; + + if (handler->filter) + handle->handle_events = input_handle_events_filter; + else if (handler->event) + handle->handle_events = input_handle_events_default; + else if (handler->events) + handle->handle_events = handler->events; + else + handle->handle_events = input_handle_events_null; +} + /** * input_register_handle - register a new input handle * @handle: handle to register @@ -2701,6 +2712,7 @@ int input_register_handle(struct input_handle *handle) struct input_dev *dev = handle->dev; int error; + input_handle_setup_event_handler(handle); /* * We take dev->mutex here to prevent race with * input_release_device(). diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c index d25d63a807f2..dc734974ce06 100644 --- a/drivers/input/keyboard/adp5588-keys.c +++ b/drivers/input/keyboard/adp5588-keys.c @@ -822,7 +822,8 @@ static int adp5588_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); - disable_irq(client->irq); + if (client->irq) + disable_irq(client->irq); return 0; } @@ -831,7 +832,8 @@ static int adp5588_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); - enable_irq(client->irq); + if (client->irq) + enable_irq(client->irq); return 0; } diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index fda49b2fe088..85c6d8ce003f 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -1121,6 +1121,14 @@ static void edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata) } } +static void edt_ft5x06_exit_regmap(void *arg) +{ + struct edt_ft5x06_ts_data *data = arg; + + if (!IS_ERR_OR_NULL(data->regmap)) + regmap_exit(data->regmap); +} + static void edt_ft5x06_disable_regulators(void *arg) { struct edt_ft5x06_ts_data *data = arg; @@ -1154,6 +1162,16 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client) return PTR_ERR(tsdata->regmap); } + /* + * We are not using devm_regmap_init_i2c() and instead install a + * custom action because we may replace regmap with M06-specific one + * and we need to make sure that it will not be released too early. + */ + error = devm_add_action_or_reset(&client->dev, edt_ft5x06_exit_regmap, + tsdata); + if (error) + return error; + chip_data = device_get_match_data(&client->dev); if (!chip_data) chip_data = (const struct edt_i2c_chip_data *)id->driver_data; @@ -1347,7 +1365,6 @@ static void edt_ft5x06_ts_remove(struct i2c_client *client) struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); edt_ft5x06_ts_teardown_debugfs(tsdata); - regmap_exit(tsdata->regmap); } static int edt_ft5x06_ts_suspend(struct device *dev) diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index ab597e74ba08..52f625e07658 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -3810,8 +3810,18 @@ static int its_vpe_set_affinity(struct irq_data *d, * Check if we're racing against a VPE being destroyed, for * which we don't want to allow a VMOVP. */ - if (!atomic_read(&vpe->vmapp_count)) - return -EINVAL; + if (!atomic_read(&vpe->vmapp_count)) { + if (gic_requires_eager_mapping()) + return -EINVAL; + + /* + * If we lazily map the VPEs, this isn't an error and + * we can exit cleanly. + */ + cpu = cpumask_first(mask_val); + irq_data_update_effective_affinity(d, cpumask_of(cpu)); + return IRQ_SET_MASK_OK_DONE; + } /* * Changing affinity is mega expensive, so let's be as lazy as diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 9d090fa07516..be011cef12e5 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -321,7 +321,7 @@ void mei_io_cb_free(struct mei_cl_cb *cb) return; list_del(&cb->list); - kfree(cb->buf.data); + kvfree(cb->buf.data); kfree(cb->ext_hdr); kfree(cb); } @@ -497,7 +497,7 @@ struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length, if (length == 0) return cb; - cb->buf.data = kmalloc(roundup(length, MEI_SLOT_SIZE), GFP_KERNEL); + cb->buf.data = kvmalloc(roundup(length, MEI_SLOT_SIZE), GFP_KERNEL); if (!cb->buf.data) { mei_io_cb_free(cb); return NULL; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c index 807eb3bbb11c..841e5af7b2be 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c @@ -1293,8 +1293,10 @@ static ssize_t hns3_dbg_read(struct file *filp, char __user *buffer, /* save the buffer addr until the last read operation */ *save_buf = read_buf; + } - /* get data ready for the first time to read */ + /* get data ready for the first time to read */ + if (!*ppos) { ret = hns3_dbg_read_cmd(dbg_data, hns3_dbg_cmd[index].cmd, read_buf, hns3_dbg_cmd[index].buf_len); if (ret) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c index 4cbc4d069a1f..b09f0cca34dc 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c @@ -11,6 +11,7 @@ #include <linux/irq.h> #include <linux/ip.h> #include <linux/ipv6.h> +#include <linux/iommu.h> #include <linux/module.h> #include <linux/pci.h> #include <linux/skbuff.h> @@ -380,6 +381,24 @@ static const struct hns3_rx_ptype hns3_rx_ptype_tbl[] = { #define HNS3_INVALID_PTYPE \ ARRAY_SIZE(hns3_rx_ptype_tbl) +static void hns3_dma_map_sync(struct device *dev, unsigned long iova) +{ + struct iommu_domain *domain = iommu_get_domain_for_dev(dev); + struct iommu_iotlb_gather iotlb_gather; + size_t granule; + + if (!domain || !iommu_is_dma_domain(domain)) + return; + + granule = 1 << __ffs(domain->pgsize_bitmap); + iova = ALIGN_DOWN(iova, granule); + iotlb_gather.start = iova; + iotlb_gather.end = iova + granule - 1; + iotlb_gather.pgsize = granule; + + iommu_iotlb_sync(domain, &iotlb_gather); +} + static irqreturn_t hns3_irq_handle(int irq, void *vector) { struct hns3_enet_tqp_vector *tqp_vector = vector; @@ -1032,6 +1051,8 @@ static bool hns3_can_use_tx_sgl(struct hns3_enet_ring *ring, static void hns3_init_tx_spare_buffer(struct hns3_enet_ring *ring) { u32 alloc_size = ring->tqp->handle->kinfo.tx_spare_buf_size; + struct net_device *netdev = ring_to_netdev(ring); + struct hns3_nic_priv *priv = netdev_priv(netdev); struct hns3_tx_spare *tx_spare; struct page *page; dma_addr_t dma; @@ -1073,6 +1094,7 @@ static void hns3_init_tx_spare_buffer(struct hns3_enet_ring *ring) tx_spare->buf = page_address(page); tx_spare->len = PAGE_SIZE << order; ring->tx_spare = tx_spare; + ring->tx_copybreak = priv->tx_copybreak; return; dma_mapping_error: @@ -1724,7 +1746,9 @@ static int hns3_map_and_fill_desc(struct hns3_enet_ring *ring, void *priv, unsigned int type) { struct hns3_desc_cb *desc_cb = &ring->desc_cb[ring->next_to_use]; + struct hnae3_handle *handle = ring->tqp->handle; struct device *dev = ring_to_dev(ring); + struct hnae3_ae_dev *ae_dev; unsigned int size; dma_addr_t dma; @@ -1756,6 +1780,13 @@ static int hns3_map_and_fill_desc(struct hns3_enet_ring *ring, void *priv, return -ENOMEM; } + /* Add a SYNC command to sync io-pgtale to avoid errors in pgtable + * prefetch + */ + ae_dev = hns3_get_ae_dev(handle); + if (ae_dev->dev_version >= HNAE3_DEVICE_VERSION_V3) + hns3_dma_map_sync(dev, dma); + desc_cb->priv = priv; desc_cb->length = size; desc_cb->dma = dma; @@ -2452,7 +2483,6 @@ static int hns3_nic_set_features(struct net_device *netdev, return ret; } - netdev->features = features; return 0; } @@ -4868,6 +4898,30 @@ static void hns3_nic_dealloc_vector_data(struct hns3_nic_priv *priv) devm_kfree(&pdev->dev, priv->tqp_vector); } +static void hns3_update_tx_spare_buf_config(struct hns3_nic_priv *priv) +{ +#define HNS3_MIN_SPARE_BUF_SIZE (2 * 1024 * 1024) +#define HNS3_MAX_PACKET_SIZE (64 * 1024) + + struct iommu_domain *domain = iommu_get_domain_for_dev(priv->dev); + struct hnae3_ae_dev *ae_dev = hns3_get_ae_dev(priv->ae_handle); + struct hnae3_handle *handle = priv->ae_handle; + + if (ae_dev->dev_version < HNAE3_DEVICE_VERSION_V3) + return; + + if (!(domain && iommu_is_dma_domain(domain))) + return; + + priv->min_tx_copybreak = HNS3_MAX_PACKET_SIZE; + priv->min_tx_spare_buf_size = HNS3_MIN_SPARE_BUF_SIZE; + + if (priv->tx_copybreak < priv->min_tx_copybreak) + priv->tx_copybreak = priv->min_tx_copybreak; + if (handle->kinfo.tx_spare_buf_size < priv->min_tx_spare_buf_size) + handle->kinfo.tx_spare_buf_size = priv->min_tx_spare_buf_size; +} + static void hns3_ring_get_cfg(struct hnae3_queue *q, struct hns3_nic_priv *priv, unsigned int ring_type) { @@ -5101,6 +5155,7 @@ int hns3_init_all_ring(struct hns3_nic_priv *priv) int i, j; int ret; + hns3_update_tx_spare_buf_config(priv); for (i = 0; i < ring_num; i++) { ret = hns3_alloc_ring_memory(&priv->ring[i]); if (ret) { @@ -5305,6 +5360,8 @@ static int hns3_client_init(struct hnae3_handle *handle) priv->ae_handle = handle; priv->tx_timeout_count = 0; priv->max_non_tso_bd_num = ae_dev->dev_specs.max_non_tso_bd_num; + priv->min_tx_copybreak = 0; + priv->min_tx_spare_buf_size = 0; set_bit(HNS3_NIC_STATE_DOWN, &priv->state); handle->msg_enable = netif_msg_init(debug, DEFAULT_MSG_LEVEL); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h index d36c4ed16d8d..caf7a4df8585 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h @@ -596,6 +596,8 @@ struct hns3_nic_priv { struct hns3_enet_coalesce rx_coal; u32 tx_copybreak; u32 rx_copybreak; + u32 min_tx_copybreak; + u32 min_tx_spare_buf_size; }; union l3_hdr_info { diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c index b1e988347347..97eaeec1952b 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c @@ -1933,6 +1933,31 @@ static int hns3_set_tx_spare_buf_size(struct net_device *netdev, return ret; } +static int hns3_check_tx_copybreak(struct net_device *netdev, u32 copybreak) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + + if (copybreak < priv->min_tx_copybreak) { + netdev_err(netdev, "tx copybreak %u should be no less than %u!\n", + copybreak, priv->min_tx_copybreak); + return -EINVAL; + } + return 0; +} + +static int hns3_check_tx_spare_buf_size(struct net_device *netdev, u32 buf_size) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + + if (buf_size < priv->min_tx_spare_buf_size) { + netdev_err(netdev, + "tx spare buf size %u should be no less than %u!\n", + buf_size, priv->min_tx_spare_buf_size); + return -EINVAL; + } + return 0; +} + static int hns3_set_tunable(struct net_device *netdev, const struct ethtool_tunable *tuna, const void *data) @@ -1949,6 +1974,10 @@ static int hns3_set_tunable(struct net_device *netdev, switch (tuna->id) { case ETHTOOL_TX_COPYBREAK: + ret = hns3_check_tx_copybreak(netdev, *(u32 *)data); + if (ret) + return ret; + priv->tx_copybreak = *(u32 *)data; for (i = 0; i < h->kinfo.num_tqps; i++) @@ -1963,6 +1992,10 @@ static int hns3_set_tunable(struct net_device *netdev, break; case ETHTOOL_TX_COPYBREAK_BUF_SIZE: + ret = hns3_check_tx_spare_buf_size(netdev, *(u32 *)data); + if (ret) + return ret; + old_tx_spare_buf_size = h->kinfo.tx_spare_buf_size; new_tx_spare_buf_size = *(u32 *)data; netdev_info(netdev, "request to set tx spare buf size from %u to %u\n", diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index bd86efd92a5a..728f4777e51f 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -6,6 +6,7 @@ #include <linux/etherdevice.h> #include <linux/init.h> #include <linux/interrupt.h> +#include <linux/irq.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/netdevice.h> @@ -3584,6 +3585,17 @@ static int hclge_set_vf_link_state(struct hnae3_handle *handle, int vf, return ret; } +static void hclge_set_reset_pending(struct hclge_dev *hdev, + enum hnae3_reset_type reset_type) +{ + /* When an incorrect reset type is executed, the get_reset_level + * function generates the HNAE3_NONE_RESET flag. As a result, this + * type do not need to pending. + */ + if (reset_type != HNAE3_NONE_RESET) + set_bit(reset_type, &hdev->reset_pending); +} + static u32 hclge_check_event_cause(struct hclge_dev *hdev, u32 *clearval) { u32 cmdq_src_reg, msix_src_reg, hw_err_src_reg; @@ -3604,7 +3616,7 @@ static u32 hclge_check_event_cause(struct hclge_dev *hdev, u32 *clearval) */ if (BIT(HCLGE_VECTOR0_IMPRESET_INT_B) & msix_src_reg) { dev_info(&hdev->pdev->dev, "IMP reset interrupt\n"); - set_bit(HNAE3_IMP_RESET, &hdev->reset_pending); + hclge_set_reset_pending(hdev, HNAE3_IMP_RESET); set_bit(HCLGE_COMM_STATE_CMD_DISABLE, &hdev->hw.hw.comm_state); *clearval = BIT(HCLGE_VECTOR0_IMPRESET_INT_B); hdev->rst_stats.imp_rst_cnt++; @@ -3614,7 +3626,7 @@ static u32 hclge_check_event_cause(struct hclge_dev *hdev, u32 *clearval) if (BIT(HCLGE_VECTOR0_GLOBALRESET_INT_B) & msix_src_reg) { dev_info(&hdev->pdev->dev, "global reset interrupt\n"); set_bit(HCLGE_COMM_STATE_CMD_DISABLE, &hdev->hw.hw.comm_state); - set_bit(HNAE3_GLOBAL_RESET, &hdev->reset_pending); + hclge_set_reset_pending(hdev, HNAE3_GLOBAL_RESET); *clearval = BIT(HCLGE_VECTOR0_GLOBALRESET_INT_B); hdev->rst_stats.global_rst_cnt++; return HCLGE_VECTOR0_EVENT_RST; @@ -3769,7 +3781,7 @@ static int hclge_misc_irq_init(struct hclge_dev *hdev) snprintf(hdev->misc_vector.name, HNAE3_INT_NAME_LEN, "%s-misc-%s", HCLGE_NAME, pci_name(hdev->pdev)); ret = request_irq(hdev->misc_vector.vector_irq, hclge_misc_irq_handle, - 0, hdev->misc_vector.name, hdev); + IRQ_NOAUTOEN, hdev->misc_vector.name, hdev); if (ret) { hclge_free_vector(hdev, 0); dev_err(&hdev->pdev->dev, "request misc irq(%d) fail\n", @@ -4062,7 +4074,7 @@ static void hclge_do_reset(struct hclge_dev *hdev) case HNAE3_FUNC_RESET: dev_info(&pdev->dev, "PF reset requested\n"); /* schedule again to check later */ - set_bit(HNAE3_FUNC_RESET, &hdev->reset_pending); + hclge_set_reset_pending(hdev, HNAE3_FUNC_RESET); hclge_reset_task_schedule(hdev); break; default: @@ -4096,6 +4108,8 @@ static enum hnae3_reset_type hclge_get_reset_level(struct hnae3_ae_dev *ae_dev, clear_bit(HNAE3_FLR_RESET, addr); } + clear_bit(HNAE3_NONE_RESET, addr); + if (hdev->reset_type != HNAE3_NONE_RESET && rst_level < hdev->reset_type) return HNAE3_NONE_RESET; @@ -4237,7 +4251,7 @@ static bool hclge_reset_err_handle(struct hclge_dev *hdev) return false; } else if (hdev->rst_stats.reset_fail_cnt < MAX_RESET_FAIL_CNT) { hdev->rst_stats.reset_fail_cnt++; - set_bit(hdev->reset_type, &hdev->reset_pending); + hclge_set_reset_pending(hdev, hdev->reset_type); dev_info(&hdev->pdev->dev, "re-schedule reset task(%u)\n", hdev->rst_stats.reset_fail_cnt); @@ -4480,8 +4494,20 @@ static void hclge_reset_event(struct pci_dev *pdev, struct hnae3_handle *handle) static void hclge_set_def_reset_request(struct hnae3_ae_dev *ae_dev, enum hnae3_reset_type rst_type) { +#define HCLGE_SUPPORT_RESET_TYPE \ + (BIT(HNAE3_FLR_RESET) | BIT(HNAE3_FUNC_RESET) | \ + BIT(HNAE3_GLOBAL_RESET) | BIT(HNAE3_IMP_RESET)) + struct hclge_dev *hdev = ae_dev->priv; + if (!(BIT(rst_type) & HCLGE_SUPPORT_RESET_TYPE)) { + /* To prevent reset triggered by hclge_reset_event */ + set_bit(HNAE3_NONE_RESET, &hdev->default_reset_request); + dev_warn(&hdev->pdev->dev, "unsupported reset type %d\n", + rst_type); + return; + } + set_bit(rst_type, &hdev->default_reset_request); } @@ -11891,9 +11917,6 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev) hclge_init_rxd_adv_layout(hdev); - /* Enable MISC vector(vector0) */ - hclge_enable_vector(&hdev->misc_vector, true); - ret = hclge_init_wol(hdev); if (ret) dev_warn(&pdev->dev, @@ -11906,6 +11929,10 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev) hclge_state_init(hdev); hdev->last_reset_time = jiffies; + /* Enable MISC vector(vector0) */ + enable_irq(hdev->misc_vector.vector_irq); + hclge_enable_vector(&hdev->misc_vector, true); + dev_info(&hdev->pdev->dev, "%s driver initialization finished.\n", HCLGE_DRIVER_NAME); @@ -12311,7 +12338,7 @@ static void hclge_uninit_ae_dev(struct hnae3_ae_dev *ae_dev) /* Disable MISC vector(vector0) */ hclge_enable_vector(&hdev->misc_vector, false); - synchronize_irq(hdev->misc_vector.vector_irq); + disable_irq(hdev->misc_vector.vector_irq); /* Disable all hw interrupts */ hclge_config_mac_tnl_int(hdev, false); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ptp.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ptp.c index 5505caea88e9..bab16c2191b2 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ptp.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ptp.c @@ -58,6 +58,9 @@ bool hclge_ptp_set_tx_info(struct hnae3_handle *handle, struct sk_buff *skb) struct hclge_dev *hdev = vport->back; struct hclge_ptp *ptp = hdev->ptp; + if (!ptp) + return false; + if (!test_bit(HCLGE_PTP_FLAG_TX_EN, &ptp->flags) || test_and_set_bit(HCLGE_STATE_PTP_TX_HANDLING, &hdev->state)) { ptp->tx_skipped++; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_regs.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_regs.c index 43c1c18fa81f..8c057192aae6 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_regs.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_regs.c @@ -510,9 +510,9 @@ out: static int hclge_fetch_pf_reg(struct hclge_dev *hdev, void *data, struct hnae3_knic_private_info *kinfo) { -#define HCLGE_RING_REG_OFFSET 0x200 #define HCLGE_RING_INT_REG_OFFSET 0x4 + struct hnae3_queue *tqp; int i, j, reg_num; int data_num_sum; u32 *reg = data; @@ -533,10 +533,11 @@ static int hclge_fetch_pf_reg(struct hclge_dev *hdev, void *data, reg_num = ARRAY_SIZE(ring_reg_addr_list); for (j = 0; j < kinfo->num_tqps; j++) { reg += hclge_reg_get_tlv(HCLGE_REG_TAG_RING, reg_num, reg); + tqp = kinfo->tqp[j]; for (i = 0; i < reg_num; i++) - *reg++ = hclge_read_dev(&hdev->hw, - ring_reg_addr_list[i] + - HCLGE_RING_REG_OFFSET * j); + *reg++ = readl_relaxed(tqp->io_base - + HCLGE_TQP_REG_OFFSET + + ring_reg_addr_list[i]); } data_num_sum += (reg_num + HCLGE_REG_TLV_SPACE) * kinfo->num_tqps; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c index 094a7c7b5592..896f1eb172d3 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c @@ -1395,6 +1395,17 @@ static int hclgevf_notify_roce_client(struct hclgevf_dev *hdev, return ret; } +static void hclgevf_set_reset_pending(struct hclgevf_dev *hdev, + enum hnae3_reset_type reset_type) +{ + /* When an incorrect reset type is executed, the get_reset_level + * function generates the HNAE3_NONE_RESET flag. As a result, this + * type do not need to pending. + */ + if (reset_type != HNAE3_NONE_RESET) + set_bit(reset_type, &hdev->reset_pending); +} + static int hclgevf_reset_wait(struct hclgevf_dev *hdev) { #define HCLGEVF_RESET_WAIT_US 20000 @@ -1544,7 +1555,7 @@ static void hclgevf_reset_err_handle(struct hclgevf_dev *hdev) hdev->rst_stats.rst_fail_cnt); if (hdev->rst_stats.rst_fail_cnt < HCLGEVF_RESET_MAX_FAIL_CNT) - set_bit(hdev->reset_type, &hdev->reset_pending); + hclgevf_set_reset_pending(hdev, hdev->reset_type); if (hclgevf_is_reset_pending(hdev)) { set_bit(HCLGEVF_RESET_PENDING, &hdev->reset_state); @@ -1664,6 +1675,8 @@ static enum hnae3_reset_type hclgevf_get_reset_level(unsigned long *addr) clear_bit(HNAE3_FLR_RESET, addr); } + clear_bit(HNAE3_NONE_RESET, addr); + return rst_level; } @@ -1673,14 +1686,15 @@ static void hclgevf_reset_event(struct pci_dev *pdev, struct hnae3_ae_dev *ae_dev = pci_get_drvdata(pdev); struct hclgevf_dev *hdev = ae_dev->priv; - dev_info(&hdev->pdev->dev, "received reset request from VF enet\n"); - if (hdev->default_reset_request) hdev->reset_level = hclgevf_get_reset_level(&hdev->default_reset_request); else hdev->reset_level = HNAE3_VF_FUNC_RESET; + dev_info(&hdev->pdev->dev, "received reset request from VF enet, reset level is %d\n", + hdev->reset_level); + /* reset of this VF requested */ set_bit(HCLGEVF_RESET_REQUESTED, &hdev->reset_state); hclgevf_reset_task_schedule(hdev); @@ -1691,8 +1705,20 @@ static void hclgevf_reset_event(struct pci_dev *pdev, static void hclgevf_set_def_reset_request(struct hnae3_ae_dev *ae_dev, enum hnae3_reset_type rst_type) { +#define HCLGEVF_SUPPORT_RESET_TYPE \ + (BIT(HNAE3_VF_RESET) | BIT(HNAE3_VF_FUNC_RESET) | \ + BIT(HNAE3_VF_PF_FUNC_RESET) | BIT(HNAE3_VF_FULL_RESET) | \ + BIT(HNAE3_FLR_RESET) | BIT(HNAE3_VF_EXP_RESET)) + struct hclgevf_dev *hdev = ae_dev->priv; + if (!(BIT(rst_type) & HCLGEVF_SUPPORT_RESET_TYPE)) { + /* To prevent reset triggered by hclge_reset_event */ + set_bit(HNAE3_NONE_RESET, &hdev->default_reset_request); + dev_info(&hdev->pdev->dev, "unsupported reset type %d\n", + rst_type); + return; + } set_bit(rst_type, &hdev->default_reset_request); } @@ -1849,14 +1875,14 @@ static void hclgevf_reset_service_task(struct hclgevf_dev *hdev) */ if (hdev->reset_attempts > HCLGEVF_MAX_RESET_ATTEMPTS_CNT) { /* prepare for full reset of stack + pcie interface */ - set_bit(HNAE3_VF_FULL_RESET, &hdev->reset_pending); + hclgevf_set_reset_pending(hdev, HNAE3_VF_FULL_RESET); /* "defer" schedule the reset task again */ set_bit(HCLGEVF_RESET_PENDING, &hdev->reset_state); } else { hdev->reset_attempts++; - set_bit(hdev->reset_level, &hdev->reset_pending); + hclgevf_set_reset_pending(hdev, hdev->reset_level); set_bit(HCLGEVF_RESET_PENDING, &hdev->reset_state); } hclgevf_reset_task_schedule(hdev); @@ -1979,7 +2005,7 @@ static enum hclgevf_evt_cause hclgevf_check_evt_cause(struct hclgevf_dev *hdev, rst_ing_reg = hclgevf_read_dev(&hdev->hw, HCLGEVF_RST_ING); dev_info(&hdev->pdev->dev, "receive reset interrupt 0x%x!\n", rst_ing_reg); - set_bit(HNAE3_VF_RESET, &hdev->reset_pending); + hclgevf_set_reset_pending(hdev, HNAE3_VF_RESET); set_bit(HCLGEVF_RESET_PENDING, &hdev->reset_state); set_bit(HCLGE_COMM_STATE_CMD_DISABLE, &hdev->hw.hw.comm_state); *clearval = ~(1U << HCLGEVF_VECTOR0_RST_INT_B); @@ -2289,6 +2315,7 @@ static void hclgevf_state_init(struct hclgevf_dev *hdev) clear_bit(HCLGEVF_STATE_RST_FAIL, &hdev->state); INIT_DELAYED_WORK(&hdev->service_task, hclgevf_service_task); + timer_setup(&hdev->reset_timer, hclgevf_reset_timer, 0); mutex_init(&hdev->mbx_resp.mbx_mutex); sema_init(&hdev->reset_sem, 1); @@ -2988,7 +3015,6 @@ static int hclgevf_init_hdev(struct hclgevf_dev *hdev) HCLGEVF_DRIVER_NAME); hclgevf_task_schedule(hdev, round_jiffies_relative(HZ)); - timer_setup(&hdev->reset_timer, hclgevf_reset_timer, 0); return 0; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_regs.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_regs.c index 6db415d8b917..7d9d9dbc7560 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_regs.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_regs.c @@ -123,10 +123,10 @@ int hclgevf_get_regs_len(struct hnae3_handle *handle) void hclgevf_get_regs(struct hnae3_handle *handle, u32 *version, void *data) { -#define HCLGEVF_RING_REG_OFFSET 0x200 #define HCLGEVF_RING_INT_REG_OFFSET 0x4 struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle); + struct hnae3_queue *tqp; int i, j, reg_um; u32 *reg = data; @@ -147,10 +147,11 @@ void hclgevf_get_regs(struct hnae3_handle *handle, u32 *version, reg_um = ARRAY_SIZE(ring_reg_addr_list); for (j = 0; j < hdev->num_tqps; j++) { reg += hclgevf_reg_get_tlv(HCLGEVF_REG_TAG_RING, reg_um, reg); + tqp = &hdev->htqp[j].q; for (i = 0; i < reg_um; i++) - *reg++ = hclgevf_read_dev(&hdev->hw, - ring_reg_addr_list[i] + - HCLGEVF_RING_REG_OFFSET * j); + *reg++ = readl_relaxed(tqp->io_base - + HCLGEVF_TQP_REG_OFFSET + + ring_reg_addr_list[i]); } reg_um = ARRAY_SIZE(tqp_intr_reg_addr_list); diff --git a/drivers/net/ethernet/intel/ice/devlink/devlink_port.c b/drivers/net/ethernet/intel/ice/devlink/devlink_port.c index 928c8bdb6649..c6779d9dffff 100644 --- a/drivers/net/ethernet/intel/ice/devlink/devlink_port.c +++ b/drivers/net/ethernet/intel/ice/devlink/devlink_port.c @@ -989,5 +989,11 @@ ice_devlink_port_new(struct devlink *devlink, if (err) return err; + if (!ice_is_eswitch_mode_switchdev(pf)) { + NL_SET_ERR_MSG_MOD(extack, + "SF ports are only supported in eswitch switchdev mode"); + return -EOPNOTSUPP; + } + return ice_alloc_dynamic_port(pf, new_attr, extack, devlink_port); } diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c index 74c0e7319a4c..d5ad6d84007c 100644 --- a/drivers/net/ethernet/intel/ice/ice_dpll.c +++ b/drivers/net/ethernet/intel/ice/ice_dpll.c @@ -10,6 +10,7 @@ #define ICE_DPLL_PIN_IDX_INVALID 0xff #define ICE_DPLL_RCLK_NUM_PER_PF 1 #define ICE_DPLL_PIN_ESYNC_PULSE_HIGH_PERCENT 25 +#define ICE_DPLL_PIN_GEN_RCLK_FREQ 1953125 /** * enum ice_dpll_pin_type - enumerate ice pin types: @@ -2064,6 +2065,73 @@ static int ice_dpll_init_worker(struct ice_pf *pf) } /** + * ice_dpll_init_info_pins_generic - initializes generic pins info + * @pf: board private structure + * @input: if input pins initialized + * + * Init information for generic pins, cache them in PF's pins structures. + * + * Return: + * * 0 - success + * * negative - init failure reason + */ +static int ice_dpll_init_info_pins_generic(struct ice_pf *pf, bool input) +{ + struct ice_dpll *de = &pf->dplls.eec, *dp = &pf->dplls.pps; + static const char labels[][sizeof("99")] = { + "0", "1", "2", "3", "4", "5", "6", "7", "8", + "9", "10", "11", "12", "13", "14", "15" }; + u32 cap = DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE; + enum ice_dpll_pin_type pin_type; + int i, pin_num, ret = -EINVAL; + struct ice_dpll_pin *pins; + u32 phase_adj_max; + + if (input) { + pin_num = pf->dplls.num_inputs; + pins = pf->dplls.inputs; + phase_adj_max = pf->dplls.input_phase_adj_max; + pin_type = ICE_DPLL_PIN_TYPE_INPUT; + cap |= DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE; + } else { + pin_num = pf->dplls.num_outputs; + pins = pf->dplls.outputs; + phase_adj_max = pf->dplls.output_phase_adj_max; + pin_type = ICE_DPLL_PIN_TYPE_OUTPUT; + } + if (pin_num > ARRAY_SIZE(labels)) + return ret; + + for (i = 0; i < pin_num; i++) { + pins[i].idx = i; + pins[i].prop.board_label = labels[i]; + pins[i].prop.phase_range.min = phase_adj_max; + pins[i].prop.phase_range.max = -phase_adj_max; + pins[i].prop.capabilities = cap; + pins[i].pf = pf; + ret = ice_dpll_pin_state_update(pf, &pins[i], pin_type, NULL); + if (ret) + break; + if (input && pins[i].freq == ICE_DPLL_PIN_GEN_RCLK_FREQ) + pins[i].prop.type = DPLL_PIN_TYPE_MUX; + else + pins[i].prop.type = DPLL_PIN_TYPE_EXT; + if (!input) + continue; + ret = ice_aq_get_cgu_ref_prio(&pf->hw, de->dpll_idx, i, + &de->input_prio[i]); + if (ret) + break; + ret = ice_aq_get_cgu_ref_prio(&pf->hw, dp->dpll_idx, i, + &dp->input_prio[i]); + if (ret) + break; + } + + return ret; +} + +/** * ice_dpll_init_info_direct_pins - initializes direct pins info * @pf: board private structure * @pin_type: type of pins being initialized @@ -2101,6 +2169,8 @@ ice_dpll_init_info_direct_pins(struct ice_pf *pf, default: return -EINVAL; } + if (num_pins != ice_cgu_get_num_pins(hw, input)) + return ice_dpll_init_info_pins_generic(pf, input); for (i = 0; i < num_pins; i++) { caps = 0; diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c index 3a33e6b9b313..ec8db830ac73 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c @@ -34,7 +34,6 @@ static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_inputs[] = { ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common }, { "GNSS-1PPS", ZL_REF4P, DPLL_PIN_TYPE_GNSS, ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz }, - { "OCXO", ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR, 0, }, }; static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_inputs[] = { @@ -52,7 +51,6 @@ static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_inputs[] = { ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common }, { "GNSS-1PPS", ZL_REF4P, DPLL_PIN_TYPE_GNSS, ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz }, - { "OCXO", ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR, }, }; static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_outputs[] = { @@ -5965,6 +5963,25 @@ ice_cgu_get_pin_desc(struct ice_hw *hw, bool input, int *size) } /** + * ice_cgu_get_num_pins - get pin description array size + * @hw: pointer to the hw struct + * @input: if request is done against input or output pins + * + * Return: size of pin description array for given hw. + */ +int ice_cgu_get_num_pins(struct ice_hw *hw, bool input) +{ + const struct ice_cgu_pin_desc *t; + int size; + + t = ice_cgu_get_pin_desc(hw, input, &size); + if (t) + return size; + + return 0; +} + +/** * ice_cgu_get_pin_type - get pin's type * @hw: pointer to the hw struct * @pin: pin index diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h index 0852a34ade91..6cedc1a906af 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h +++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h @@ -404,6 +404,7 @@ int ice_read_sma_ctrl_e810t(struct ice_hw *hw, u8 *data); int ice_write_sma_ctrl_e810t(struct ice_hw *hw, u8 data); int ice_read_pca9575_reg_e810t(struct ice_hw *hw, u8 offset, u8 *data); bool ice_is_pca9575_present(struct ice_hw *hw); +int ice_cgu_get_num_pins(struct ice_hw *hw, bool input); enum dpll_pin_type ice_cgu_get_pin_type(struct ice_hw *hw, u8 pin, bool input); struct dpll_pin_frequency * ice_cgu_get_pin_freq_supp(struct ice_hw *hw, u8 pin, bool input, u8 *num); diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index f1d088168723..b83df5f94b1f 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -907,7 +907,7 @@ static int igb_request_msix(struct igb_adapter *adapter) int i, err = 0, vector = 0, free_vector = 0; err = request_irq(adapter->msix_entries[vector].vector, - igb_msix_other, 0, netdev->name, adapter); + igb_msix_other, IRQF_NO_THREAD, netdev->name, adapter); if (err) goto err_out; diff --git a/drivers/net/ethernet/mediatek/mtk_wed_wo.h b/drivers/net/ethernet/mediatek/mtk_wed_wo.h index 87a67fa3868d..c01b1e8428f6 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed_wo.h +++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.h @@ -91,8 +91,8 @@ enum mtk_wed_dummy_cr_idx { #define MT7981_FIRMWARE_WO "mediatek/mt7981_wo.bin" #define MT7986_FIRMWARE_WO0 "mediatek/mt7986_wo_0.bin" #define MT7986_FIRMWARE_WO1 "mediatek/mt7986_wo_1.bin" -#define MT7988_FIRMWARE_WO0 "mediatek/mt7988_wo_0.bin" -#define MT7988_FIRMWARE_WO1 "mediatek/mt7988_wo_1.bin" +#define MT7988_FIRMWARE_WO0 "mediatek/mt7988/mt7988_wo_0.bin" +#define MT7988_FIRMWARE_WO1 "mediatek/mt7988/mt7988_wo_1.bin" #define MTK_WO_MCU_CFG_LS_BASE 0 #define MTK_WO_MCU_CFG_LS_HW_VER_ADDR (MTK_WO_MCU_CFG_LS_BASE + 0x000) diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c index 060e5b939211..d6f37456fb31 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.c +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c @@ -389,15 +389,27 @@ static void mlxsw_pci_wqe_frag_unmap(struct mlxsw_pci *mlxsw_pci, char *wqe, dma_unmap_single(&pdev->dev, mapaddr, frag_len, direction); } -static struct sk_buff *mlxsw_pci_rdq_build_skb(struct page *pages[], +static struct sk_buff *mlxsw_pci_rdq_build_skb(struct mlxsw_pci_queue *q, + struct page *pages[], u16 byte_count) { + struct mlxsw_pci_queue *cq = q->u.rdq.cq; unsigned int linear_data_size; + struct page_pool *page_pool; struct sk_buff *skb; int page_index = 0; bool linear_only; void *data; + linear_only = byte_count + MLXSW_PCI_RX_BUF_SW_OVERHEAD <= PAGE_SIZE; + linear_data_size = linear_only ? byte_count : + PAGE_SIZE - + MLXSW_PCI_RX_BUF_SW_OVERHEAD; + + page_pool = cq->u.cq.page_pool; + page_pool_dma_sync_for_cpu(page_pool, pages[page_index], + MLXSW_PCI_SKB_HEADROOM, linear_data_size); + data = page_address(pages[page_index]); net_prefetch(data); @@ -405,11 +417,6 @@ static struct sk_buff *mlxsw_pci_rdq_build_skb(struct page *pages[], if (unlikely(!skb)) return ERR_PTR(-ENOMEM); - linear_only = byte_count + MLXSW_PCI_RX_BUF_SW_OVERHEAD <= PAGE_SIZE; - linear_data_size = linear_only ? byte_count : - PAGE_SIZE - - MLXSW_PCI_RX_BUF_SW_OVERHEAD; - skb_reserve(skb, MLXSW_PCI_SKB_HEADROOM); skb_put(skb, linear_data_size); @@ -425,6 +432,7 @@ static struct sk_buff *mlxsw_pci_rdq_build_skb(struct page *pages[], page = pages[page_index]; frag_size = min(byte_count, PAGE_SIZE); + page_pool_dma_sync_for_cpu(page_pool, page, 0, frag_size); skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, 0, frag_size, PAGE_SIZE); byte_count -= frag_size; @@ -760,7 +768,7 @@ static void mlxsw_pci_cqe_rdq_handle(struct mlxsw_pci *mlxsw_pci, if (err) goto out; - skb = mlxsw_pci_rdq_build_skb(pages, byte_count); + skb = mlxsw_pci_rdq_build_skb(q, pages, byte_count); if (IS_ERR(skb)) { dev_err_ratelimited(&pdev->dev, "Failed to build skb for RDQ\n"); mlxsw_pci_rdq_pages_recycle(q, pages, num_sg_entries); @@ -988,12 +996,13 @@ static int mlxsw_pci_cq_page_pool_init(struct mlxsw_pci_queue *q, if (cq_type != MLXSW_PCI_CQ_RDQ) return 0; - pp_params.flags = PP_FLAG_DMA_MAP; + pp_params.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV; pp_params.pool_size = MLXSW_PCI_WQE_COUNT * mlxsw_pci->num_sg_entries; pp_params.nid = dev_to_node(&mlxsw_pci->pdev->dev); pp_params.dev = &mlxsw_pci->pdev->dev; pp_params.napi = &q->u.cq.napi; pp_params.dma_dir = DMA_FROM_DEVICE; + pp_params.max_len = PAGE_SIZE; page_pool = page_pool_create(&pp_params); if (IS_ERR(page_pool)) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c index d761a1235994..7ea798a4949e 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c @@ -481,11 +481,33 @@ mlxsw_sp_ipip_ol_netdev_change_gre6(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_ipip_entry *ipip_entry, struct netlink_ext_ack *extack) { + u32 new_kvdl_index, old_kvdl_index = ipip_entry->dip_kvdl_index; + struct in6_addr old_addr6 = ipip_entry->parms.daddr.addr6; struct mlxsw_sp_ipip_parms new_parms; + int err; new_parms = mlxsw_sp_ipip_netdev_parms_init_gre6(ipip_entry->ol_dev); - return mlxsw_sp_ipip_ol_netdev_change_gre(mlxsw_sp, ipip_entry, - &new_parms, extack); + + err = mlxsw_sp_ipv6_addr_kvdl_index_get(mlxsw_sp, + &new_parms.daddr.addr6, + &new_kvdl_index); + if (err) + return err; + ipip_entry->dip_kvdl_index = new_kvdl_index; + + err = mlxsw_sp_ipip_ol_netdev_change_gre(mlxsw_sp, ipip_entry, + &new_parms, extack); + if (err) + goto err_change_gre; + + mlxsw_sp_ipv6_addr_put(mlxsw_sp, &old_addr6); + + return 0; + +err_change_gre: + ipip_entry->dip_kvdl_index = old_kvdl_index; + mlxsw_sp_ipv6_addr_put(mlxsw_sp, &new_parms.daddr.addr6); + return err; } static int diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c index 5b174cb95eb8..d94081c7658e 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c @@ -16,6 +16,7 @@ #include "spectrum.h" #include "spectrum_ptp.h" #include "core.h" +#include "txheader.h" #define MLXSW_SP1_PTP_CLOCK_CYCLES_SHIFT 29 #define MLXSW_SP1_PTP_CLOCK_FREQ_KHZ 156257 /* 6.4nSec */ @@ -1684,6 +1685,12 @@ int mlxsw_sp_ptp_txhdr_construct(struct mlxsw_core *mlxsw_core, struct sk_buff *skb, const struct mlxsw_tx_info *tx_info) { + if (skb_cow_head(skb, MLXSW_TXHDR_LEN)) { + this_cpu_inc(mlxsw_sp_port->pcpu_stats->tx_dropped); + dev_kfree_skb_any(skb); + return -ENOMEM; + } + mlxsw_sp_txhdr_construct(skb, tx_info); return 0; } diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c index e0165358c4ac..77b35abc6f6f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c @@ -203,8 +203,12 @@ static void _dwmac4_dump_dma_regs(struct stmmac_priv *priv, readl(ioaddr + DMA_CHAN_TX_CONTROL(dwmac4_addrs, channel)); reg_space[DMA_CHAN_RX_CONTROL(default_addrs, channel) / 4] = readl(ioaddr + DMA_CHAN_RX_CONTROL(dwmac4_addrs, channel)); + reg_space[DMA_CHAN_TX_BASE_ADDR_HI(default_addrs, channel) / 4] = + readl(ioaddr + DMA_CHAN_TX_BASE_ADDR_HI(dwmac4_addrs, channel)); reg_space[DMA_CHAN_TX_BASE_ADDR(default_addrs, channel) / 4] = readl(ioaddr + DMA_CHAN_TX_BASE_ADDR(dwmac4_addrs, channel)); + reg_space[DMA_CHAN_RX_BASE_ADDR_HI(default_addrs, channel) / 4] = + readl(ioaddr + DMA_CHAN_RX_BASE_ADDR_HI(dwmac4_addrs, channel)); reg_space[DMA_CHAN_RX_BASE_ADDR(default_addrs, channel) / 4] = readl(ioaddr + DMA_CHAN_RX_BASE_ADDR(dwmac4_addrs, channel)); reg_space[DMA_CHAN_TX_END_ADDR(default_addrs, channel) / 4] = @@ -225,8 +229,12 @@ static void _dwmac4_dump_dma_regs(struct stmmac_priv *priv, readl(ioaddr + DMA_CHAN_CUR_TX_DESC(dwmac4_addrs, channel)); reg_space[DMA_CHAN_CUR_RX_DESC(default_addrs, channel) / 4] = readl(ioaddr + DMA_CHAN_CUR_RX_DESC(dwmac4_addrs, channel)); + reg_space[DMA_CHAN_CUR_TX_BUF_ADDR_HI(default_addrs, channel) / 4] = + readl(ioaddr + DMA_CHAN_CUR_TX_BUF_ADDR_HI(dwmac4_addrs, channel)); reg_space[DMA_CHAN_CUR_TX_BUF_ADDR(default_addrs, channel) / 4] = readl(ioaddr + DMA_CHAN_CUR_TX_BUF_ADDR(dwmac4_addrs, channel)); + reg_space[DMA_CHAN_CUR_RX_BUF_ADDR_HI(default_addrs, channel) / 4] = + readl(ioaddr + DMA_CHAN_CUR_RX_BUF_ADDR_HI(dwmac4_addrs, channel)); reg_space[DMA_CHAN_CUR_RX_BUF_ADDR(default_addrs, channel) / 4] = readl(ioaddr + DMA_CHAN_CUR_RX_BUF_ADDR(dwmac4_addrs, channel)); reg_space[DMA_CHAN_STATUS(default_addrs, channel) / 4] = diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h index 17d9120db5fe..4f980dcd3958 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h @@ -127,7 +127,9 @@ static inline u32 dma_chanx_base_addr(const struct dwmac4_addrs *addrs, #define DMA_CHAN_SLOT_CTRL_STATUS(addrs, x) (dma_chanx_base_addr(addrs, x) + 0x3c) #define DMA_CHAN_CUR_TX_DESC(addrs, x) (dma_chanx_base_addr(addrs, x) + 0x44) #define DMA_CHAN_CUR_RX_DESC(addrs, x) (dma_chanx_base_addr(addrs, x) + 0x4c) +#define DMA_CHAN_CUR_TX_BUF_ADDR_HI(addrs, x) (dma_chanx_base_addr(addrs, x) + 0x50) #define DMA_CHAN_CUR_TX_BUF_ADDR(addrs, x) (dma_chanx_base_addr(addrs, x) + 0x54) +#define DMA_CHAN_CUR_RX_BUF_ADDR_HI(addrs, x) (dma_chanx_base_addr(addrs, x) + 0x58) #define DMA_CHAN_CUR_RX_BUF_ADDR(addrs, x) (dma_chanx_base_addr(addrs, x) + 0x5c) #define DMA_CHAN_STATUS(addrs, x) (dma_chanx_base_addr(addrs, x) + 0x60) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index d3895d7eecfc..208dbc68aaf9 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -4304,11 +4304,6 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) if (dma_mapping_error(priv->device, des)) goto dma_map_err; - tx_q->tx_skbuff_dma[first_entry].buf = des; - tx_q->tx_skbuff_dma[first_entry].len = skb_headlen(skb); - tx_q->tx_skbuff_dma[first_entry].map_as_page = false; - tx_q->tx_skbuff_dma[first_entry].buf_type = STMMAC_TXBUF_T_SKB; - if (priv->dma_cap.addr64 <= 32) { first->des0 = cpu_to_le32(des); @@ -4327,6 +4322,23 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) stmmac_tso_allocator(priv, des, tmp_pay_len, (nfrags == 0), queue); + /* In case two or more DMA transmit descriptors are allocated for this + * non-paged SKB data, the DMA buffer address should be saved to + * tx_q->tx_skbuff_dma[].buf corresponding to the last descriptor, + * and leave the other tx_q->tx_skbuff_dma[].buf as NULL to guarantee + * that stmmac_tx_clean() does not unmap the entire DMA buffer too early + * since the tail areas of the DMA buffer can be accessed by DMA engine + * sooner or later. + * By saving the DMA buffer address to tx_q->tx_skbuff_dma[].buf + * corresponding to the last descriptor, stmmac_tx_clean() will unmap + * this DMA buffer right after the DMA engine completely finishes the + * full buffer transmission. + */ + tx_q->tx_skbuff_dma[tx_q->cur_tx].buf = des; + tx_q->tx_skbuff_dma[tx_q->cur_tx].len = skb_headlen(skb); + tx_q->tx_skbuff_dma[tx_q->cur_tx].map_as_page = false; + tx_q->tx_skbuff_dma[tx_q->cur_tx].buf_type = STMMAC_TXBUF_T_SKB; + /* Prepare fragments */ for (i = 0; i < nfrags; i++) { const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index a60bfb1abb7f..70f981887518 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -1702,20 +1702,24 @@ static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[]) return -EINVAL; if (data[IFLA_GTP_FD0]) { - u32 fd0 = nla_get_u32(data[IFLA_GTP_FD0]); + int fd0 = nla_get_u32(data[IFLA_GTP_FD0]); - sk0 = gtp_encap_enable_socket(fd0, UDP_ENCAP_GTP0, gtp); - if (IS_ERR(sk0)) - return PTR_ERR(sk0); + if (fd0 >= 0) { + sk0 = gtp_encap_enable_socket(fd0, UDP_ENCAP_GTP0, gtp); + if (IS_ERR(sk0)) + return PTR_ERR(sk0); + } } if (data[IFLA_GTP_FD1]) { - u32 fd1 = nla_get_u32(data[IFLA_GTP_FD1]); + int fd1 = nla_get_u32(data[IFLA_GTP_FD1]); - sk1u = gtp_encap_enable_socket(fd1, UDP_ENCAP_GTP1U, gtp); - if (IS_ERR(sk1u)) { - gtp_encap_disable_sock(sk0); - return PTR_ERR(sk1u); + if (fd1 >= 0) { + sk1u = gtp_encap_enable_socket(fd1, UDP_ENCAP_GTP1U, gtp); + if (IS_ERR(sk1u)) { + gtp_encap_disable_sock(sk0); + return PTR_ERR(sk1u); + } } } diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index 26034f80d4a4..ee2159282573 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -3798,8 +3798,7 @@ static void macsec_free_netdev(struct net_device *dev) { struct macsec_dev *macsec = macsec_priv(dev); - if (macsec->secy.tx_sc.md_dst) - metadata_dst_free(macsec->secy.tx_sc.md_dst); + dst_release(&macsec->secy.tx_sc.md_dst->dst); free_percpu(macsec->stats); free_percpu(macsec->secy.tx_sc.stats); diff --git a/drivers/net/mctp/mctp-i2c.c b/drivers/net/mctp/mctp-i2c.c index 4dc057c121f5..e70fb6687994 100644 --- a/drivers/net/mctp/mctp-i2c.c +++ b/drivers/net/mctp/mctp-i2c.c @@ -588,6 +588,9 @@ static int mctp_i2c_header_create(struct sk_buff *skb, struct net_device *dev, if (len > MCTP_I2C_MAXMTU) return -EMSGSIZE; + if (!daddr || !saddr) + return -EINVAL; + lldst = *((u8 *)daddr); llsrc = *((u8 *)saddr); diff --git a/drivers/net/netdevsim/fib.c b/drivers/net/netdevsim/fib.c index 41e80f78b316..16c382c42227 100644 --- a/drivers/net/netdevsim/fib.c +++ b/drivers/net/netdevsim/fib.c @@ -1377,10 +1377,12 @@ static ssize_t nsim_nexthop_bucket_activity_write(struct file *file, if (pos != 0) return -EINVAL; - if (size > sizeof(buf)) + if (size > sizeof(buf) - 1) return -EINVAL; if (copy_from_user(buf, user_buf, size)) return -EFAULT; + buf[size] = 0; + if (sscanf(buf, "%u %hu", &nhid, &bucket_index) != 2) return -EINVAL; diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index f137c82f1c0f..0c011d8f5d4d 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -1076,6 +1076,7 @@ static const struct usb_device_id products[] = { USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x581d, USB_CLASS_VENDOR_SPEC, 1, 7), .driver_info = (unsigned long)&qmi_wwan_info, }, + {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0122)}, /* Quectel RG650V */ {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0125)}, /* Quectel EC25, EC20 R2.0 Mini PCIe */ {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0306)}, /* Quectel EP06/EG06/EM06 */ {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0512)}, /* Quectel EG12/EM12 */ diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index a5612c799f5e..468c73974046 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -10069,6 +10069,7 @@ static const struct usb_device_id rtl8152_table[] = { { USB_DEVICE(VENDOR_ID_LENOVO, 0x3062) }, { USB_DEVICE(VENDOR_ID_LENOVO, 0x3069) }, { USB_DEVICE(VENDOR_ID_LENOVO, 0x3082) }, + { USB_DEVICE(VENDOR_ID_LENOVO, 0x3098) }, { USB_DEVICE(VENDOR_ID_LENOVO, 0x7205) }, { USB_DEVICE(VENDOR_ID_LENOVO, 0x720c) }, { USB_DEVICE(VENDOR_ID_LENOVO, 0x7214) }, diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index dbaf26d6a7a6..16d07d619b4d 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -3043,9 +3043,14 @@ ath10k_wmi_tlv_op_cleanup_mgmt_tx_send(struct ath10k *ar, struct sk_buff *msdu) { struct ath10k_skb_cb *cb = ATH10K_SKB_CB(msdu); + struct ath10k_mgmt_tx_pkt_addr *pkt_addr; struct ath10k_wmi *wmi = &ar->wmi; - idr_remove(&wmi->mgmt_pending_tx, cb->msdu_id); + spin_lock_bh(&ar->data_lock); + pkt_addr = idr_remove(&wmi->mgmt_pending_tx, cb->msdu_id); + spin_unlock_bh(&ar->data_lock); + + kfree(pkt_addr); return 0; } diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 4861179b2217..5e061f7525a6 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -2441,6 +2441,7 @@ wmi_process_mgmt_tx_comp(struct ath10k *ar, struct mgmt_tx_compl_params *param) dma_unmap_single(ar->dev, pkt_addr->paddr, msdu->len, DMA_TO_DEVICE); info = IEEE80211_SKB_CB(msdu); + kfree(pkt_addr); if (param->status) { info->flags &= ~IEEE80211_TX_STAT_ACK; @@ -9612,6 +9613,7 @@ static int ath10k_wmi_mgmt_tx_clean_up_pending(int msdu_id, void *ptr, dma_unmap_single(ar->dev, pkt_addr->paddr, msdu->len, DMA_TO_DEVICE); ieee80211_free_txskb(ar->hw, msdu); + kfree(pkt_addr); return 0; } diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index c087d8a0f5b2..40088e62572e 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -5291,8 +5291,11 @@ int ath11k_dp_rx_process_mon_status(struct ath11k_base *ab, int mac_id, hal_status == HAL_TLV_STATUS_PPDU_DONE) { rx_mon_stats->status_ppdu_done++; pmon->mon_ppdu_status = DP_PPDU_STATUS_DONE; - ath11k_dp_rx_mon_dest_process(ar, mac_id, budget, napi); - pmon->mon_ppdu_status = DP_PPDU_STATUS_START; + if (!ab->hw_params.full_monitor_mode) { + ath11k_dp_rx_mon_dest_process(ar, mac_id, + budget, napi); + pmon->mon_ppdu_status = DP_PPDU_STATUS_START; + } } if (ppdu_info->peer_id == HAL_INVALID_PEERID || diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index f29ac6de7139..19702b6f09c3 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -306,7 +306,7 @@ static void wil_rx_add_radiotap_header(struct wil6210_priv *wil, struct sk_buff *skb) { struct wil6210_rtap { - struct ieee80211_radiotap_header rthdr; + struct ieee80211_radiotap_header_fixed rthdr; /* fields should be in the order of bits in rthdr.it_present */ /* flags */ u8 flags; diff --git a/drivers/net/wireless/broadcom/brcm80211/Kconfig b/drivers/net/wireless/broadcom/brcm80211/Kconfig index 3a1a35b5672f..19d0c003f626 100644 --- a/drivers/net/wireless/broadcom/brcm80211/Kconfig +++ b/drivers/net/wireless/broadcom/brcm80211/Kconfig @@ -27,6 +27,7 @@ source "drivers/net/wireless/broadcom/brcm80211/brcmfmac/Kconfig" config BRCM_TRACING bool "Broadcom device tracing" depends on BRCMSMAC || BRCMFMAC + depends on TRACING help If you say Y here, the Broadcom wireless drivers will register with ftrace to dump event information into the trace ringbuffer. diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2100.c b/drivers/net/wireless/intel/ipw2x00/ipw2100.c index b6636002c7d2..fe75941c584d 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2100.c @@ -2518,7 +2518,7 @@ static void isr_rx_monitor(struct ipw2100_priv *priv, int i, * to build this manually element by element, we can write it much * more efficiently than we can parse it. ORDER MATTERS HERE */ struct ipw_rt_hdr { - struct ieee80211_radiotap_header rt_hdr; + struct ieee80211_radiotap_header_fixed rt_hdr; s8 rt_dbmsignal; /* signal in dbM, kluged to signed */ } *ipw_rt; diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2200.h b/drivers/net/wireless/intel/ipw2x00/ipw2200.h index 8ebf09121e17..226286cb7eb8 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2200.h +++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.h @@ -1143,7 +1143,7 @@ struct ipw_prom_priv { * structure is provided regardless of any bits unset. */ struct ipw_rt_hdr { - struct ieee80211_radiotap_header rt_hdr; + struct ieee80211_radiotap_header_fixed rt_hdr; u64 rt_tsf; /* TSF */ /* XXX */ u8 rt_flags; /* radiotap packet flags */ u8 rt_rate; /* rate in 500kb/s */ diff --git a/drivers/net/wireless/intel/iwlegacy/common.c b/drivers/net/wireless/intel/iwlegacy/common.c index 9d33a66a49b5..958dd4f9bc69 100644 --- a/drivers/net/wireless/intel/iwlegacy/common.c +++ b/drivers/net/wireless/intel/iwlegacy/common.c @@ -3122,6 +3122,7 @@ il_enqueue_hcmd(struct il_priv *il, struct il_host_cmd *cmd) struct il_cmd_meta *out_meta; dma_addr_t phys_addr; unsigned long flags; + u8 *out_payload; u32 idx; u16 fix_size; @@ -3157,6 +3158,16 @@ il_enqueue_hcmd(struct il_priv *il, struct il_host_cmd *cmd) out_cmd = txq->cmd[idx]; out_meta = &txq->meta[idx]; + /* The payload is in the same place in regular and huge + * command buffers, but we need to let the compiler know when + * we're using a larger payload buffer to avoid "field- + * spanning write" warnings at run-time for huge commands. + */ + if (cmd->flags & CMD_SIZE_HUGE) + out_payload = ((struct il_device_cmd_huge *)out_cmd)->cmd.payload; + else + out_payload = out_cmd->cmd.payload; + if (WARN_ON(out_meta->flags & CMD_MAPPED)) { spin_unlock_irqrestore(&il->hcmd_lock, flags); return -ENOSPC; @@ -3170,7 +3181,7 @@ il_enqueue_hcmd(struct il_priv *il, struct il_host_cmd *cmd) out_meta->callback = cmd->callback; out_cmd->hdr.cmd = cmd->id; - memcpy(&out_cmd->cmd.payload, cmd->data, cmd->len); + memcpy(out_payload, cmd->data, cmd->len); /* At this point, the out_cmd now has all of the incoming cmd * information */ @@ -4962,6 +4973,8 @@ il_pci_resume(struct device *device) */ pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00); + _il_wr(il, CSR_INT, 0xffffffff); + _il_wr(il, CSR_FH_INT_STATUS, 0xffffffff); il_enable_interrupts(il); if (!(_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)) diff --git a/drivers/net/wireless/intel/iwlegacy/common.h b/drivers/net/wireless/intel/iwlegacy/common.h index 2147781b5fff..725c2a88ddb7 100644 --- a/drivers/net/wireless/intel/iwlegacy/common.h +++ b/drivers/net/wireless/intel/iwlegacy/common.h @@ -560,6 +560,18 @@ struct il_device_cmd { #define TFD_MAX_PAYLOAD_SIZE (sizeof(struct il_device_cmd)) +/** + * struct il_device_cmd_huge + * + * For use when sending huge commands. + */ +struct il_device_cmd_huge { + struct il_cmd_header hdr; /* uCode API */ + union { + u8 payload[IL_MAX_CMD_SIZE - sizeof(struct il_cmd_header)]; + } __packed cmd; +} __packed; + struct il_host_cmd { const void *data; unsigned long reply_page; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index a7cea0a55b35..0bc32291815e 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -429,38 +429,28 @@ out_free: return ret; } -static int iwl_acpi_sar_set_profile(union acpi_object *table, - struct iwl_sar_profile *profile, - bool enabled, u8 num_chains, - u8 num_sub_bands) +static int +iwl_acpi_parse_chains_table(union acpi_object *table, + struct iwl_sar_profile_chain *chains, + u8 num_chains, u8 num_sub_bands) { - int i, j, idx = 0; - - /* - * The table from ACPI is flat, but we store it in a - * structured array. - */ - for (i = 0; i < BIOS_SAR_MAX_CHAINS_PER_PROFILE; i++) { - for (j = 0; j < BIOS_SAR_MAX_SUB_BANDS_NUM; j++) { + for (u8 chain = 0; chain < num_chains; chain++) { + for (u8 subband = 0; subband < BIOS_SAR_MAX_SUB_BANDS_NUM; + subband++) { /* if we don't have the values, use the default */ - if (i >= num_chains || j >= num_sub_bands) { - profile->chains[i].subbands[j] = 0; + if (subband >= num_sub_bands) { + chains[chain].subbands[subband] = 0; + } else if (table->type != ACPI_TYPE_INTEGER || + table->integer.value > U8_MAX) { + return -EINVAL; } else { - if (table[idx].type != ACPI_TYPE_INTEGER || - table[idx].integer.value > U8_MAX) - return -EINVAL; - - profile->chains[i].subbands[j] = - table[idx].integer.value; - - idx++; + chains[chain].subbands[subband] = + table->integer.value; + table++; } } } - /* Only if all values were valid can the profile be enabled */ - profile->enabled = enabled; - return 0; } @@ -543,9 +533,11 @@ read_table: /* The profile from WRDS is officially profile 1, but goes * into sar_profiles[0] (because we don't have a profile 0). */ - ret = iwl_acpi_sar_set_profile(table, &fwrt->sar_profiles[0], - flags & IWL_SAR_ENABLE_MSK, - num_chains, num_sub_bands); + ret = iwl_acpi_parse_chains_table(table, fwrt->sar_profiles[0].chains, + num_chains, num_sub_bands); + if (!ret && flags & IWL_SAR_ENABLE_MSK) + fwrt->sar_profiles[0].enabled = true; + out_free: kfree(data); return ret; @@ -557,7 +549,7 @@ int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt) bool enabled; int i, n_profiles, tbl_rev, pos; int ret = 0; - u8 num_chains, num_sub_bands; + u8 num_sub_bands; data = iwl_acpi_get_object(fwrt->dev, ACPI_EWRD_METHOD); if (IS_ERR(data)) @@ -573,7 +565,6 @@ int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt) goto out_free; } - num_chains = ACPI_SAR_NUM_CHAINS_REV2; num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV2; goto read_table; @@ -589,7 +580,6 @@ int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt) goto out_free; } - num_chains = ACPI_SAR_NUM_CHAINS_REV1; num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV1; goto read_table; @@ -605,7 +595,6 @@ int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt) goto out_free; } - num_chains = ACPI_SAR_NUM_CHAINS_REV0; num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV0; goto read_table; @@ -637,23 +626,54 @@ read_table: /* the tables start at element 3 */ pos = 3; + BUILD_BUG_ON(ACPI_SAR_NUM_CHAINS_REV0 != ACPI_SAR_NUM_CHAINS_REV1); + BUILD_BUG_ON(ACPI_SAR_NUM_CHAINS_REV2 != 2 * ACPI_SAR_NUM_CHAINS_REV0); + + /* parse non-cdb chains for all profiles */ for (i = 0; i < n_profiles; i++) { union acpi_object *table = &wifi_pkg->package.elements[pos]; + /* The EWRD profiles officially go from 2 to 4, but we * save them in sar_profiles[1-3] (because we don't * have profile 0). So in the array we start from 1. */ - ret = iwl_acpi_sar_set_profile(table, - &fwrt->sar_profiles[i + 1], - enabled, num_chains, - num_sub_bands); + ret = iwl_acpi_parse_chains_table(table, + fwrt->sar_profiles[i + 1].chains, + ACPI_SAR_NUM_CHAINS_REV0, + num_sub_bands); if (ret < 0) - break; + goto out_free; /* go to the next table */ - pos += num_chains * num_sub_bands; + pos += ACPI_SAR_NUM_CHAINS_REV0 * num_sub_bands; } + /* non-cdb table revisions */ + if (tbl_rev < 2) + goto set_enabled; + + /* parse cdb chains for all profiles */ + for (i = 0; i < n_profiles; i++) { + struct iwl_sar_profile_chain *chains; + union acpi_object *table; + + table = &wifi_pkg->package.elements[pos]; + chains = &fwrt->sar_profiles[i + 1].chains[ACPI_SAR_NUM_CHAINS_REV0]; + ret = iwl_acpi_parse_chains_table(table, + chains, + ACPI_SAR_NUM_CHAINS_REV0, + num_sub_bands); + if (ret < 0) + goto out_free; + + /* go to the next table */ + pos += ACPI_SAR_NUM_CHAINS_REV0 * num_sub_bands; + } + +set_enabled: + for (i = 0; i < n_profiles; i++) + fwrt->sar_profiles[i + 1].enabled = enabled; + out_free: kfree(data); return ret; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/init.c b/drivers/net/wireless/intel/iwlwifi/fw/init.c index d8b083be5b6b..de87e0e3e072 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/init.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/init.c @@ -39,10 +39,12 @@ void iwl_fw_runtime_init(struct iwl_fw_runtime *fwrt, struct iwl_trans *trans, } IWL_EXPORT_SYMBOL(iwl_fw_runtime_init); +/* Assumes the appropriate lock is held by the caller */ void iwl_fw_runtime_suspend(struct iwl_fw_runtime *fwrt) { iwl_fw_suspend_timestamp(fwrt); - iwl_dbg_tlv_time_point(fwrt, IWL_FW_INI_TIME_POINT_HOST_D3_START, NULL); + iwl_dbg_tlv_time_point_sync(fwrt, IWL_FW_INI_TIME_POINT_HOST_D3_START, + NULL); } IWL_EXPORT_SYMBOL(iwl_fw_runtime_suspend); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index 2abfc986701f..c620911a1193 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -1413,25 +1413,35 @@ _iwl_op_mode_start(struct iwl_drv *drv, struct iwlwifi_opmode_table *op) const struct iwl_op_mode_ops *ops = op->ops; struct dentry *dbgfs_dir = NULL; struct iwl_op_mode *op_mode = NULL; + int retry, max_retry = !!iwlwifi_mod_params.fw_restart * IWL_MAX_INIT_RETRY; /* also protects start/stop from racing against each other */ lockdep_assert_held(&iwlwifi_opmode_table_mtx); + for (retry = 0; retry <= max_retry; retry++) { + #ifdef CONFIG_IWLWIFI_DEBUGFS - drv->dbgfs_op_mode = debugfs_create_dir(op->name, - drv->dbgfs_drv); - dbgfs_dir = drv->dbgfs_op_mode; + drv->dbgfs_op_mode = debugfs_create_dir(op->name, + drv->dbgfs_drv); + dbgfs_dir = drv->dbgfs_op_mode; #endif - op_mode = ops->start(drv->trans, drv->trans->cfg, - &drv->fw, dbgfs_dir); - if (op_mode) - return op_mode; + op_mode = ops->start(drv->trans, drv->trans->cfg, + &drv->fw, dbgfs_dir); + + if (op_mode) + return op_mode; + + if (test_bit(STATUS_TRANS_DEAD, &drv->trans->status)) + break; + + IWL_ERR(drv, "retry init count %d\n", retry); #ifdef CONFIG_IWLWIFI_DEBUGFS - debugfs_remove_recursive(drv->dbgfs_op_mode); - drv->dbgfs_op_mode = NULL; + debugfs_remove_recursive(drv->dbgfs_op_mode); + drv->dbgfs_op_mode = NULL; #endif + } return NULL; } diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.h b/drivers/net/wireless/intel/iwlwifi/iwl-drv.h index 1549ff429549..6a1d31892417 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.h @@ -98,6 +98,9 @@ void iwl_drv_stop(struct iwl_drv *drv); #define VISIBLE_IF_IWLWIFI_KUNIT static #endif +/* max retry for init flow */ +#define IWL_MAX_INIT_RETRY 2 + #define FW_NAME_PRE_BUFSIZE 64 struct iwl_trans; const char *iwl_drv_get_fwname_pre(struct iwl_trans *trans, char *buf); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 49a6aff42376..244ca8cab9d1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -1398,7 +1398,9 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) iwl_mvm_pause_tcm(mvm, true); + mutex_lock(&mvm->mutex); iwl_fw_runtime_suspend(&mvm->fwrt); + mutex_unlock(&mvm->mutex); return __iwl_mvm_suspend(hw, wowlan, false); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 08546e673cf5..f30b0fc8eca9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1307,8 +1307,8 @@ static void iwl_mvm_disconnect_iterator(void *data, u8 *mac, void iwl_mvm_send_recovery_cmd(struct iwl_mvm *mvm, u32 flags) { u32 error_log_size = mvm->fw->ucode_capa.error_log_size; + u32 status = 0; int ret; - u32 resp; struct iwl_fw_error_recovery_cmd recovery_cmd = { .flags = cpu_to_le32(flags), @@ -1316,7 +1316,6 @@ void iwl_mvm_send_recovery_cmd(struct iwl_mvm *mvm, u32 flags) }; struct iwl_host_cmd host_cmd = { .id = WIDE_ID(SYSTEM_GROUP, FW_ERROR_RECOVERY_CMD), - .flags = CMD_WANT_SKB, .data = {&recovery_cmd, }, .len = {sizeof(recovery_cmd), }, }; @@ -1336,7 +1335,7 @@ void iwl_mvm_send_recovery_cmd(struct iwl_mvm *mvm, u32 flags) recovery_cmd.buf_size = cpu_to_le32(error_log_size); } - ret = iwl_mvm_send_cmd(mvm, &host_cmd); + ret = iwl_mvm_send_cmd_status(mvm, &host_cmd, &status); kfree(mvm->error_recovery_buf); mvm->error_recovery_buf = NULL; @@ -1347,11 +1346,10 @@ void iwl_mvm_send_recovery_cmd(struct iwl_mvm *mvm, u32 flags) /* skb respond is only relevant in ERROR_RECOVERY_UPDATE_DB */ if (flags & ERROR_RECOVERY_UPDATE_DB) { - resp = le32_to_cpu(*(__le32 *)host_cmd.resp_pkt->data); - if (resp) { + if (status) { IWL_ERR(mvm, "Failed to send recovery cmd blob was invalid %d\n", - resp); + status); ieee80211_iterate_interfaces(mvm->hw, 0, iwl_mvm_disconnect_iterator, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index a327893c6dce..80b9a115245f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1293,12 +1293,14 @@ int iwl_mvm_mac_start(struct ieee80211_hw *hw) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); int ret; + int retry, max_retry = 0; mutex_lock(&mvm->mutex); /* we are starting the mac not in error flow, and restart is enabled */ if (!test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, &mvm->status) && iwlwifi_mod_params.fw_restart) { + max_retry = IWL_MAX_INIT_RETRY; /* * This will prevent mac80211 recovery flows to trigger during * init failures @@ -1306,7 +1308,13 @@ int iwl_mvm_mac_start(struct ieee80211_hw *hw) set_bit(IWL_MVM_STATUS_STARTING, &mvm->status); } - ret = __iwl_mvm_mac_start(mvm); + for (retry = 0; retry <= max_retry; retry++) { + ret = __iwl_mvm_mac_start(mvm); + if (!ret) + break; + + IWL_ERR(mvm, "mac start retry %d\n", retry); + } clear_bit(IWL_MVM_STATUS_STARTING, &mvm->status); mutex_unlock(&mvm->mutex); @@ -1970,7 +1978,6 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, mvm->p2p_device_vif = NULL; } - iwl_mvm_unset_link_mapping(mvm, vif, &vif->bss_conf); iwl_mvm_mac_ctxt_remove(mvm, vif); RCU_INIT_POINTER(mvm->vif_id_to_mac[mvmvif->id], NULL); @@ -1979,6 +1986,7 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, mvm->monitor_on = false; out: + iwl_mvm_unset_link_mapping(mvm, vif, &vif->bss_conf); if (vif->type == NL80211_IFTYPE_AP || vif->type == NL80211_IFTYPE_ADHOC) { iwl_mvm_dealloc_int_sta(mvm, &mvmvif->deflink.mcast_sta); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c index f2378e0fb2fb..e252f0dcea20 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c @@ -41,8 +41,6 @@ static int iwl_mvm_mld_mac_add_interface(struct ieee80211_hw *hw, /* reset deflink MLO parameters */ mvmvif->deflink.fw_link_id = IWL_MVM_FW_LINK_ID_INVALID; mvmvif->deflink.active = 0; - /* the first link always points to the default one */ - mvmvif->link[0] = &mvmvif->deflink; ret = iwl_mvm_mld_mac_ctxt_add(mvm, vif); if (ret) @@ -60,9 +58,19 @@ static int iwl_mvm_mld_mac_add_interface(struct ieee80211_hw *hw, IEEE80211_VIF_SUPPORTS_CQM_RSSI; } - ret = iwl_mvm_add_link(mvm, vif, &vif->bss_conf); - if (ret) - goto out_free_bf; + /* We want link[0] to point to the default link, unless we have MLO and + * in this case this will be modified later by .change_vif_links() + * If we are in the restart flow with an MLD connection, we will wait + * to .change_vif_links() to setup the links. + */ + if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) || + !ieee80211_vif_is_mld(vif)) { + mvmvif->link[0] = &mvmvif->deflink; + + ret = iwl_mvm_add_link(mvm, vif, &vif->bss_conf); + if (ret) + goto out_free_bf; + } /* Save a pointer to p2p device vif, so it can later be used to * update the p2p device MAC when a GO is started/stopped @@ -350,11 +358,6 @@ __iwl_mvm_mld_assign_vif_chanctx(struct iwl_mvm *mvm, rcu_read_unlock(); } - if (vif->type == NL80211_IFTYPE_STATION) - iwl_mvm_send_ap_tx_power_constraint_cmd(mvm, vif, - link_conf, - false); - /* then activate */ ret = iwl_mvm_link_changed(mvm, vif, link_conf, LINK_CONTEXT_MODIFY_ACTIVE | @@ -363,6 +366,11 @@ __iwl_mvm_mld_assign_vif_chanctx(struct iwl_mvm *mvm, if (ret) goto out; + if (vif->type == NL80211_IFTYPE_STATION) + iwl_mvm_send_ap_tx_power_constraint_cmd(mvm, vif, + link_conf, + false); + /* * Power state must be updated before quotas, * otherwise fw will complain. @@ -1194,7 +1202,11 @@ iwl_mvm_mld_change_vif_links(struct ieee80211_hw *hw, mutex_lock(&mvm->mutex); - if (old_links == 0) { + /* If we're in RESTART flow, the default link wasn't added in + * drv_add_interface(), and link[0] doesn't point to it. + */ + if (old_links == 0 && !test_bit(IWL_MVM_STATUS_IN_HW_RESTART, + &mvm->status)) { err = iwl_mvm_disable_link(mvm, vif, &vif->bss_conf); if (err) goto out_err; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index 3ce9150213a7..ddcbd80a49fb 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -1774,7 +1774,7 @@ iwl_mvm_umac_scan_cfg_channels_v7_6g(struct iwl_mvm *mvm, &cp->channel_config[ch_cnt]; u32 s_ssid_bitmap = 0, bssid_bitmap = 0, flags = 0; - u8 j, k, n_s_ssids = 0, n_bssids = 0; + u8 k, n_s_ssids = 0, n_bssids = 0; u8 max_s_ssids, max_bssids; bool force_passive = false, found = false, allow_passive = true, unsolicited_probe_on_chan = false, psc_no_listen = false; @@ -1799,7 +1799,7 @@ iwl_mvm_umac_scan_cfg_channels_v7_6g(struct iwl_mvm *mvm, cfg->v5.iter_count = 1; cfg->v5.iter_interval = 0; - for (j = 0; j < params->n_6ghz_params; j++) { + for (u32 j = 0; j < params->n_6ghz_params; j++) { s8 tmp_psd_20; if (!(scan_6ghz_params[j].channel_idx == i)) @@ -1873,7 +1873,7 @@ iwl_mvm_umac_scan_cfg_channels_v7_6g(struct iwl_mvm *mvm, * SSID. * TODO: improve this logic */ - for (j = 0; j < params->n_6ghz_params; j++) { + for (u32 j = 0; j < params->n_6ghz_params; j++) { if (!(scan_6ghz_params[j].channel_idx == i)) continue; diff --git a/drivers/net/wireless/marvell/libertas/radiotap.h b/drivers/net/wireless/marvell/libertas/radiotap.h index 1ed5608d353f..d543bfe739dc 100644 --- a/drivers/net/wireless/marvell/libertas/radiotap.h +++ b/drivers/net/wireless/marvell/libertas/radiotap.h @@ -2,7 +2,7 @@ #include <net/ieee80211_radiotap.h> struct tx_radiotap_hdr { - struct ieee80211_radiotap_header hdr; + struct ieee80211_radiotap_header_fixed hdr; u8 rate; u8 txpower; u8 rts_retries; @@ -31,7 +31,7 @@ struct tx_radiotap_hdr { #define IEEE80211_FC_DSTODS 0x0300 struct rx_radiotap_hdr { - struct ieee80211_radiotap_header hdr; + struct ieee80211_radiotap_header_fixed hdr; u8 flags; u8 rate; u8 antsignal; diff --git a/drivers/net/wireless/mediatek/mt76/mcu.c b/drivers/net/wireless/mediatek/mt76/mcu.c index 98da82b74094..3353012e8542 100644 --- a/drivers/net/wireless/mediatek/mt76/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mcu.c @@ -84,13 +84,16 @@ int mt76_mcu_skb_send_and_get_msg(struct mt76_dev *dev, struct sk_buff *skb, mutex_lock(&dev->mcu.mutex); if (dev->mcu_ops->mcu_skb_prepare_msg) { + orig_skb = skb; ret = dev->mcu_ops->mcu_skb_prepare_msg(dev, skb, cmd, &seq); if (ret < 0) goto out; } retry: - orig_skb = skb_get(skb); + /* orig skb might be needed for retry, mcu_skb_send_msg consumes it */ + if (orig_skb) + skb_get(orig_skb); ret = dev->mcu_ops->mcu_skb_send_msg(dev, skb, cmd, &seq); if (ret < 0) goto out; @@ -105,7 +108,7 @@ retry: do { skb = mt76_mcu_get_response(dev, expires); if (!skb && !test_bit(MT76_MCU_RESET, &dev->phy.state) && - retry++ < dev->mcu_ops->max_retry) { + orig_skb && retry++ < dev->mcu_ops->max_retry) { dev_err(dev->dev, "Retry message %08x (seq %d)\n", cmd, seq); skb = orig_skb; diff --git a/drivers/net/wireless/microchip/wilc1000/mon.c b/drivers/net/wireless/microchip/wilc1000/mon.c index 03b7229a0ff5..c3d27aaec297 100644 --- a/drivers/net/wireless/microchip/wilc1000/mon.c +++ b/drivers/net/wireless/microchip/wilc1000/mon.c @@ -7,12 +7,12 @@ #include "cfg80211.h" struct wilc_wfi_radiotap_hdr { - struct ieee80211_radiotap_header hdr; + struct ieee80211_radiotap_header_fixed hdr; u8 rate; } __packed; struct wilc_wfi_radiotap_cb_hdr { - struct ieee80211_radiotap_header hdr; + struct ieee80211_radiotap_header_fixed hdr; u8 rate; u8 dump; u16 tx_flags; diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192du/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192du/sw.c index d069a81ac617..cc699efa9c79 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192du/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192du/sw.c @@ -352,7 +352,6 @@ static const struct usb_device_id rtl8192d_usb_ids[] = { {RTL_USB_DEVICE(USB_VENDOR_ID_REALTEK, 0x8194, rtl92du_hal_cfg)}, {RTL_USB_DEVICE(USB_VENDOR_ID_REALTEK, 0x8111, rtl92du_hal_cfg)}, {RTL_USB_DEVICE(USB_VENDOR_ID_REALTEK, 0x0193, rtl92du_hal_cfg)}, - {RTL_USB_DEVICE(USB_VENDOR_ID_REALTEK, 0x8171, rtl92du_hal_cfg)}, {RTL_USB_DEVICE(USB_VENDOR_ID_REALTEK, 0xe194, rtl92du_hal_cfg)}, {RTL_USB_DEVICE(0x2019, 0xab2c, rtl92du_hal_cfg)}, {RTL_USB_DEVICE(0x2019, 0xab2d, rtl92du_hal_cfg)}, diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c index e83ab6fb83f5..b17a429bcd29 100644 --- a/drivers/net/wireless/realtek/rtw88/usb.c +++ b/drivers/net/wireless/realtek/rtw88/usb.c @@ -771,7 +771,6 @@ static void rtw_usb_dynamic_rx_agg_v1(struct rtw_dev *rtwdev, bool enable) u8 size, timeout; u16 val16; - rtw_write32_set(rtwdev, REG_RXDMA_AGG_PG_TH, BIT_EN_PRE_CALC); rtw_write8_set(rtwdev, REG_TXDMA_PQ_MAP, BIT_RXDMA_AGG_EN); rtw_write8_clr(rtwdev, REG_RXDMA_AGG_PG_TH + 3, BIT(7)); diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index df51b29142aa..8d27374db83c 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -6445,6 +6445,8 @@ static void _update_wl_info_v7(struct rtw89_dev *rtwdev, u8 rid) /* todo DBCC related event */ rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC] wl_info phy_now=%d\n", phy_now); + rtw89_debug(rtwdev, RTW89_DBG_BTC, + "[BTC] rlink cnt_2g=%d cnt_5g=%d\n", cnt_2g, cnt_5g); if (wl_rinfo->dbcc_en != rtwdev->dbcc_en) { wl_rinfo->dbcc_chg = 1; diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c index 02afeb3acce4..5aef7fa37878 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.c +++ b/drivers/net/wireless/realtek/rtw89/pci.c @@ -3026,24 +3026,54 @@ static void rtw89_pci_declaim_device(struct rtw89_dev *rtwdev, pci_disable_device(pdev); } -static void rtw89_pci_cfg_dac(struct rtw89_dev *rtwdev) +static bool rtw89_pci_chip_is_manual_dac(struct rtw89_dev *rtwdev) { - struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv; const struct rtw89_chip_info *chip = rtwdev->chip; - if (!rtwpci->enable_dac) - return; - switch (chip->chip_id) { case RTL8852A: case RTL8852B: case RTL8851B: case RTL8852BT: - break; + return true; default: - return; + return false; + } +} + +static bool rtw89_pci_is_dac_compatible_bridge(struct rtw89_dev *rtwdev) +{ + struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv; + struct pci_dev *bridge = pci_upstream_bridge(rtwpci->pdev); + + if (!rtw89_pci_chip_is_manual_dac(rtwdev)) + return true; + + if (!bridge) + return false; + + switch (bridge->vendor) { + case PCI_VENDOR_ID_INTEL: + return true; + case PCI_VENDOR_ID_ASMEDIA: + if (bridge->device == 0x2806) + return true; + break; } + return false; +} + +static void rtw89_pci_cfg_dac(struct rtw89_dev *rtwdev) +{ + struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv; + + if (!rtwpci->enable_dac) + return; + + if (!rtw89_pci_chip_is_manual_dac(rtwdev)) + return; + rtw89_pci_config_byte_set(rtwdev, RTW89_PCIE_L1_CTRL, RTW89_PCIE_BIT_EN_64BITS); } @@ -3061,6 +3091,9 @@ static int rtw89_pci_setup_mapping(struct rtw89_dev *rtwdev, goto err; } + if (!rtw89_pci_is_dac_compatible_bridge(rtwdev)) + goto no_dac; + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(36)); if (!ret) { rtwpci->enable_dac = true; @@ -3073,6 +3106,7 @@ static int rtw89_pci_setup_mapping(struct rtw89_dev *rtwdev, goto err_release_regions; } } +no_dac: resource_len = pci_resource_len(pdev, bar_id); rtwpci->mmap = pci_iomap(pdev, bar_id, resource_len); diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c index f0e528abb1b4..3f424f14de4e 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim.c @@ -763,7 +763,7 @@ static const struct rhashtable_params hwsim_rht_params = { }; struct hwsim_radiotap_hdr { - struct ieee80211_radiotap_header hdr; + struct ieee80211_radiotap_header_fixed hdr; __le64 rt_tsft; u8 rt_flags; u8 rt_rate; @@ -772,7 +772,7 @@ struct hwsim_radiotap_hdr { } __packed; struct hwsim_radiotap_ack_hdr { - struct ieee80211_radiotap_header hdr; + struct ieee80211_radiotap_header_fixed hdr; u8 rt_flags; u8 pad; __le16 rt_channel; diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 84cb859a911d..b149b638453f 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -92,6 +92,17 @@ MODULE_PARM_DESC(apst_secondary_latency_tol_us, "secondary APST latency tolerance in us"); /* + * Older kernels didn't enable protection information if it was at an offset. + * Newer kernels do, so it breaks reads on the upgrade if such formats were + * used in prior kernels since the metadata written did not contain a valid + * checksum. + */ +static bool disable_pi_offsets = false; +module_param(disable_pi_offsets, bool, 0444); +MODULE_PARM_DESC(disable_pi_offsets, + "disable protection information if it has an offset"); + +/* * nvme_wq - hosts nvme related works that are not reset or delete * nvme_reset_wq - hosts nvme reset works * nvme_delete_wq - hosts nvme delete works @@ -1390,17 +1401,30 @@ static void nvme_update_keep_alive(struct nvme_ctrl *ctrl, nvme_start_keep_alive(ctrl); } -/* - * In NVMe 1.0 the CNS field was just a binary controller or namespace - * flag, thus sending any new CNS opcodes has a big chance of not working. - * Qemu unfortunately had that bug after reporting a 1.1 version compliance - * (but not for any later version). - */ -static bool nvme_ctrl_limited_cns(struct nvme_ctrl *ctrl) +static bool nvme_id_cns_ok(struct nvme_ctrl *ctrl, u8 cns) { - if (ctrl->quirks & NVME_QUIRK_IDENTIFY_CNS) - return ctrl->vs < NVME_VS(1, 2, 0); - return ctrl->vs < NVME_VS(1, 1, 0); + /* + * The CNS field occupies a full byte starting with NVMe 1.2 + */ + if (ctrl->vs >= NVME_VS(1, 2, 0)) + return true; + + /* + * NVMe 1.1 expanded the CNS value to two bits, which means values + * larger than that could get truncated and treated as an incorrect + * value. + * + * Qemu implemented 1.0 behavior for controllers claiming 1.1 + * compliance, so they need to be quirked here. + */ + if (ctrl->vs >= NVME_VS(1, 1, 0) && + !(ctrl->quirks & NVME_QUIRK_IDENTIFY_CNS)) + return cns <= 3; + + /* + * NVMe 1.0 used a single bit for the CNS value. + */ + return cns <= 1; } static int nvme_identify_ctrl(struct nvme_ctrl *dev, struct nvme_id_ctrl **id) @@ -1913,8 +1937,12 @@ static void nvme_configure_metadata(struct nvme_ctrl *ctrl, if (head->pi_size && head->ms >= head->pi_size) head->pi_type = id->dps & NVME_NS_DPS_PI_MASK; - if (!(id->dps & NVME_NS_DPS_PI_FIRST)) - info->pi_offset = head->ms - head->pi_size; + if (!(id->dps & NVME_NS_DPS_PI_FIRST)) { + if (disable_pi_offsets) + head->pi_type = 0; + else + info->pi_offset = head->ms - head->pi_size; + } if (ctrl->ops->flags & NVME_F_FABRICS) { /* @@ -3104,7 +3132,7 @@ static int nvme_init_non_mdts_limits(struct nvme_ctrl *ctrl) ctrl->max_zeroes_sectors = 0; if (ctrl->subsys->subtype != NVME_NQN_NVME || - nvme_ctrl_limited_cns(ctrl) || + !nvme_id_cns_ok(ctrl, NVME_ID_CNS_CS_CTRL) || test_bit(NVME_CTRL_SKIP_ID_CNS_CS, &ctrl->flags)) return 0; @@ -4200,7 +4228,7 @@ static void nvme_scan_work(struct work_struct *work) } mutex_lock(&ctrl->scan_lock); - if (nvme_ctrl_limited_cns(ctrl)) { + if (!nvme_id_cns_ok(ctrl, NVME_ID_CNS_NS_ACTIVE_LIST)) { nvme_scan_ns_sequential(ctrl); } else { /* diff --git a/drivers/nvme/host/ioctl.c b/drivers/nvme/host/ioctl.c index b9b79ccfabf8..a96976b22fa7 100644 --- a/drivers/nvme/host/ioctl.c +++ b/drivers/nvme/host/ioctl.c @@ -421,10 +421,13 @@ static enum rq_end_io_ret nvme_uring_cmd_end_io(struct request *req, struct io_uring_cmd *ioucmd = req->end_io_data; struct nvme_uring_cmd_pdu *pdu = nvme_uring_cmd_pdu(ioucmd); - if (nvme_req(req)->flags & NVME_REQ_CANCELLED) + if (nvme_req(req)->flags & NVME_REQ_CANCELLED) { pdu->status = -EINTR; - else + } else { pdu->status = nvme_req(req)->status; + if (!pdu->status) + pdu->status = blk_status_to_errno(err); + } pdu->result = le64_to_cpu(nvme_req(req)->result.u64); /* diff --git a/drivers/nvme/target/auth.c b/drivers/nvme/target/auth.c index 29f8639cfe7f..b47d675232d2 100644 --- a/drivers/nvme/target/auth.c +++ b/drivers/nvme/target/auth.c @@ -115,6 +115,7 @@ int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, u8 dhgroup_id) pr_debug("%s: ctrl %d failed to generate private key, err %d\n", __func__, ctrl->cntlid, ret); kfree_sensitive(ctrl->dh_key); + ctrl->dh_key = NULL; return ret; } ctrl->dh_keysize = crypto_kpp_maxsize(ctrl->dh_tfm); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 7d85c04fbba2..225a6cd2e9ca 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1067,8 +1067,15 @@ static void pci_std_enable_acs(struct pci_dev *dev, struct pci_acs *caps) static void pci_enable_acs(struct pci_dev *dev) { struct pci_acs caps; + bool enable_acs = false; int pos; + /* If an iommu is present we start with kernel default caps */ + if (pci_acs_enable) { + if (pci_dev_specific_enable_acs(dev)) + enable_acs = true; + } + pos = dev->acs_cap; if (!pos) return; @@ -1077,11 +1084,8 @@ static void pci_enable_acs(struct pci_dev *dev) pci_read_config_word(dev, pos + PCI_ACS_CTRL, &caps.ctrl); caps.fw_ctrl = caps.ctrl; - /* If an iommu is present we start with kernel default caps */ - if (pci_acs_enable) { - if (pci_dev_specific_enable_acs(dev)) - pci_std_enable_acs(dev, &caps); - } + if (enable_acs) + pci_std_enable_acs(dev, &caps); /* * Always apply caps from the command line, even if there is no iommu. diff --git a/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c index 4c10cafded4e..950b7ae1d1a8 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c +++ b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c @@ -153,7 +153,9 @@ static void xhci_soft_reset(struct brcm_usb_init_params *params, } else { USB_CTRL_SET(ctrl, USB_PM, XHC_SOFT_RESETB); /* Required for COMMONONN to be set */ - USB_XHCI_GBL_UNSET(xhci_gbl, GUSB2PHYCFG, U2_FREECLK_EXISTS); + if (params->supported_port_modes != USB_CTLR_MODE_DRD) + USB_XHCI_GBL_UNSET(xhci_gbl, GUSB2PHYCFG, + U2_FREECLK_EXISTS); } } @@ -328,8 +330,12 @@ static void usb_init_common_7216(struct brcm_usb_init_params *params) /* 1 millisecond - for USB clocks to settle down */ usleep_range(1000, 2000); - /* Disable PHY when port is suspended */ - USB_CTRL_SET(ctrl, P0_U2PHY_CFG1, COMMONONN); + /* + * Disable PHY when port is suspended + * Does not work in DRD mode + */ + if (params->supported_port_modes != USB_CTLR_MODE_DRD) + USB_CTRL_SET(ctrl, P0_U2PHY_CFG1, COMMONONN); usb_wake_enable_7216(params, false); usb_init_common(params); diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.c b/drivers/phy/broadcom/phy-brcm-usb-init.c index 39536b6d96a9..5ebb3a616115 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init.c +++ b/drivers/phy/broadcom/phy-brcm-usb-init.c @@ -220,6 +220,8 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = { 0, /* USB_CTRL_SETUP_SCB2_EN_MASK */ 0, /* USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK */ 0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */ + 0, /* USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK */ + 0, /* USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK */ 0, /* USB_CTRL_SETUP_OC3_DISABLE_MASK */ 0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */ 0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */ diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index aeec6eb6be23..dfc4f55d112e 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -174,8 +174,9 @@ #define SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG 0x150 #define SIERRA_DEQ_TAU_CTRL2_PREG 0x151 #define SIERRA_DEQ_TAU_CTRL3_PREG 0x152 -#define SIERRA_DEQ_OPENEYE_CTRL_PREG 0x158 +#define SIERRA_DEQ_TAU_EPIOFFSET_MODE_PREG 0x158 #define SIERRA_DEQ_CONCUR_EPIOFFSET_MODE_PREG 0x159 +#define SIERRA_DEQ_OPENEYE_CTRL_PREG 0x15C #define SIERRA_DEQ_PICTRL_PREG 0x161 #define SIERRA_CPICAL_TMRVAL_MODE1_PREG 0x170 #define SIERRA_CPICAL_TMRVAL_MODE0_PREG 0x171 @@ -1733,7 +1734,7 @@ static const struct cdns_reg_pairs ml_pcie_100_no_ssc_ln_regs[] = { {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG}, {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG}, {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG}, - {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG}, + {0x5E82, SIERRA_DEQ_TAU_EPIOFFSET_MODE_PREG}, {0x002B, SIERRA_CPI_TRIM_PREG}, {0x0003, SIERRA_EPI_CTRL_PREG}, {0x803F, SIERRA_SDFILT_H2L_A_PREG}, @@ -1797,7 +1798,7 @@ static const struct cdns_reg_pairs ti_ml_pcie_100_no_ssc_ln_regs[] = { {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG}, {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG}, {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG}, - {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG}, + {0x5E82, SIERRA_DEQ_TAU_EPIOFFSET_MODE_PREG}, {0x002B, SIERRA_CPI_TRIM_PREG}, {0x0003, SIERRA_EPI_CTRL_PREG}, {0x803F, SIERRA_SDFILT_H2L_A_PREG}, @@ -1874,7 +1875,7 @@ static const struct cdns_reg_pairs ml_pcie_100_int_ssc_ln_regs[] = { {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG}, {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG}, {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG}, - {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG}, + {0x5E82, SIERRA_DEQ_TAU_EPIOFFSET_MODE_PREG}, {0x002B, SIERRA_CPI_TRIM_PREG}, {0x0003, SIERRA_EPI_CTRL_PREG}, {0x803F, SIERRA_SDFILT_H2L_A_PREG}, @@ -1941,7 +1942,7 @@ static const struct cdns_reg_pairs ti_ml_pcie_100_int_ssc_ln_regs[] = { {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG}, {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG}, {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG}, - {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG}, + {0x5E82, SIERRA_DEQ_TAU_EPIOFFSET_MODE_PREG}, {0x002B, SIERRA_CPI_TRIM_PREG}, {0x0003, SIERRA_EPI_CTRL_PREG}, {0x803F, SIERRA_SDFILT_H2L_A_PREG}, @@ -2012,7 +2013,7 @@ static const struct cdns_reg_pairs ml_pcie_100_ext_ssc_ln_regs[] = { {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG}, {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG}, {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG}, - {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG}, + {0x5E82, SIERRA_DEQ_TAU_EPIOFFSET_MODE_PREG}, {0x002B, SIERRA_CPI_TRIM_PREG}, {0x0003, SIERRA_EPI_CTRL_PREG}, {0x803F, SIERRA_SDFILT_H2L_A_PREG}, @@ -2079,7 +2080,7 @@ static const struct cdns_reg_pairs ti_ml_pcie_100_ext_ssc_ln_regs[] = { {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG}, {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG}, {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG}, - {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG}, + {0x5E82, SIERRA_DEQ_TAU_EPIOFFSET_MODE_PREG}, {0x002B, SIERRA_CPI_TRIM_PREG}, {0x0003, SIERRA_EPI_CTRL_PREG}, {0x803F, SIERRA_SDFILT_H2L_A_PREG}, @@ -2140,7 +2141,7 @@ static const struct cdns_reg_pairs cdns_pcie_ln_regs_no_ssc[] = { {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG}, {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG}, {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG}, - {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG}, + {0x5E82, SIERRA_DEQ_TAU_EPIOFFSET_MODE_PREG}, {0x002B, SIERRA_CPI_TRIM_PREG}, {0x0003, SIERRA_EPI_CTRL_PREG}, {0x803F, SIERRA_SDFILT_H2L_A_PREG}, @@ -2215,7 +2216,7 @@ static const struct cdns_reg_pairs cdns_pcie_ln_regs_int_ssc[] = { {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG}, {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG}, {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG}, - {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG}, + {0x5E82, SIERRA_DEQ_TAU_EPIOFFSET_MODE_PREG}, {0x002B, SIERRA_CPI_TRIM_PREG}, {0x0003, SIERRA_EPI_CTRL_PREG}, {0x803F, SIERRA_SDFILT_H2L_A_PREG}, @@ -2284,7 +2285,7 @@ static const struct cdns_reg_pairs cdns_pcie_ln_regs_ext_ssc[] = { {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG}, {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG}, {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG}, - {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG}, + {0x5E82, SIERRA_DEQ_TAU_EPIOFFSET_MODE_PREG}, {0x002B, SIERRA_CPI_TRIM_PREG}, {0x0003, SIERRA_EPI_CTRL_PREG}, {0x803F, SIERRA_SDFILT_H2L_A_PREG}, diff --git a/drivers/phy/freescale/phy-fsl-imx8m-pcie.c b/drivers/phy/freescale/phy-fsl-imx8m-pcie.c index 11fcb1867118..e98361dcdead 100644 --- a/drivers/phy/freescale/phy-fsl-imx8m-pcie.c +++ b/drivers/phy/freescale/phy-fsl-imx8m-pcie.c @@ -141,11 +141,6 @@ static int imx8_pcie_phy_power_on(struct phy *phy) IMX8MM_GPR_PCIE_REF_CLK_PLL); usleep_range(100, 200); - /* Do the PHY common block reset */ - regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14, - IMX8MM_GPR_PCIE_CMN_RST, - IMX8MM_GPR_PCIE_CMN_RST); - switch (imx8_phy->drvdata->variant) { case IMX8MP: reset_control_deassert(imx8_phy->perst); @@ -156,6 +151,11 @@ static int imx8_pcie_phy_power_on(struct phy *phy) break; } + /* Do the PHY common block reset */ + regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14, + IMX8MM_GPR_PCIE_CMN_RST, + IMX8MM_GPR_PCIE_CMN_RST); + /* Polling to check the phy is ready or not. */ ret = readl_poll_timeout(imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG075, val, val == ANA_PLL_DONE, 10, 20000); diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c index a8adc3214bfe..643045c9024e 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c @@ -3673,6 +3673,7 @@ static int qmp_combo_probe(struct platform_device *pdev) return -ENOMEM; qmp->dev = dev; + dev_set_drvdata(dev, qmp); qmp->orientation = TYPEC_ORIENTATION_NORMAL; @@ -3749,8 +3750,6 @@ static int qmp_combo_probe(struct platform_device *pdev) phy_set_drvdata(qmp->dp_phy, qmp); - dev_set_drvdata(dev, qmp); - if (usb_np == dev->of_node) phy_provider = devm_of_phy_provider_register(dev, qmp_combo_phy_xlate); else diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c index f71787fb4d7e..36aaac34e6c6 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c @@ -3661,8 +3661,8 @@ static const struct qmp_phy_cfg x1e80100_qmp_gen4x2_pciephy_cfg = { .reset_list = sdm845_pciephy_reset_l, .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), - .vreg_list = sm8550_qmp_phy_vreg_l, - .num_vregs = ARRAY_SIZE(sm8550_qmp_phy_vreg_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), .regs = pciephy_v6_regs_layout, .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, @@ -3695,8 +3695,8 @@ static const struct qmp_phy_cfg x1e80100_qmp_gen4x4_pciephy_cfg = { .reset_list = sdm845_pciephy_reset_l, .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), - .vreg_list = sm8550_qmp_phy_vreg_l, - .num_vregs = ARRAY_SIZE(sm8550_qmp_phy_vreg_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), .regs = pciephy_v6_regs_layout, .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usb-legacy.c b/drivers/phy/qualcomm/phy-qcom-qmp-usb-legacy.c index 6d0ba39c1943..8bf951b0490c 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-usb-legacy.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-usb-legacy.c @@ -1248,6 +1248,7 @@ static int qmp_usb_legacy_probe(struct platform_device *pdev) return -ENOMEM; qmp->dev = dev; + dev_set_drvdata(dev, qmp); qmp->cfg = of_device_get_match_data(dev); if (!qmp->cfg) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usb.c b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c index 2fd49355aa37..1246d3bc8b92 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-usb.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c @@ -2179,6 +2179,7 @@ static int qmp_usb_probe(struct platform_device *pdev) return -ENOMEM; qmp->dev = dev; + dev_set_drvdata(dev, qmp); qmp->cfg = of_device_get_match_data(dev); if (!qmp->cfg) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c b/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c index d4fa1063ea61..cf12a6f12134 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c @@ -1050,6 +1050,7 @@ static int qmp_usbc_probe(struct platform_device *pdev) return -ENOMEM; qmp->dev = dev; + dev_set_drvdata(dev, qmp); qmp->orientation = TYPEC_ORIENTATION_NORMAL; diff --git a/drivers/phy/realtek/phy-rtk-usb2.c b/drivers/phy/realtek/phy-rtk-usb2.c index e3ad7cea5109..e8ca2ec5998f 100644 --- a/drivers/phy/realtek/phy-rtk-usb2.c +++ b/drivers/phy/realtek/phy-rtk-usb2.c @@ -1023,6 +1023,8 @@ static int rtk_usb2phy_probe(struct platform_device *pdev) rtk_phy->dev = &pdev->dev; rtk_phy->phy_cfg = devm_kzalloc(dev, sizeof(*phy_cfg), GFP_KERNEL); + if (!rtk_phy->phy_cfg) + return -ENOMEM; memcpy(rtk_phy->phy_cfg, phy_cfg, sizeof(*phy_cfg)); diff --git a/drivers/phy/realtek/phy-rtk-usb3.c b/drivers/phy/realtek/phy-rtk-usb3.c index dfcf4b921bba..96af483e5444 100644 --- a/drivers/phy/realtek/phy-rtk-usb3.c +++ b/drivers/phy/realtek/phy-rtk-usb3.c @@ -577,6 +577,8 @@ static int rtk_usb3phy_probe(struct platform_device *pdev) rtk_phy->dev = &pdev->dev; rtk_phy->phy_cfg = devm_kzalloc(dev, sizeof(*phy_cfg), GFP_KERNEL); + if (!rtk_phy->phy_cfg) + return -ENOMEM; memcpy(rtk_phy->phy_cfg, phy_cfg, sizeof(*phy_cfg)); diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig index 490263375057..2f7a05f21dc5 100644 --- a/drivers/phy/rockchip/Kconfig +++ b/drivers/phy/rockchip/Kconfig @@ -86,6 +86,7 @@ config PHY_ROCKCHIP_PCIE config PHY_ROCKCHIP_SAMSUNG_HDPTX tristate "Rockchip Samsung HDMI/eDP Combo PHY driver" depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF + depends on COMMON_CLK depends on HAS_IOMEM select GENERIC_PHY select MFD_SYSCON diff --git a/drivers/phy/starfive/phy-jh7110-usb.c b/drivers/phy/starfive/phy-jh7110-usb.c index 633912f8a05d..cb5454fbe2c8 100644 --- a/drivers/phy/starfive/phy-jh7110-usb.c +++ b/drivers/phy/starfive/phy-jh7110-usb.c @@ -10,18 +10,24 @@ #include <linux/clk.h> #include <linux/err.h> #include <linux/io.h> +#include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> +#include <linux/regmap.h> #include <linux/usb/of.h> #define USB_125M_CLK_RATE 125000000 #define USB_LS_KEEPALIVE_OFF 0x4 #define USB_LS_KEEPALIVE_ENABLE BIT(4) +#define USB_PDRSTN_SPLIT BIT(17) +#define SYSCON_USB_SPLIT_OFFSET 0x18 + struct jh7110_usb2_phy { struct phy *phy; void __iomem *regs; + struct regmap *sys_syscon; struct clk *usb_125m_clk; struct clk *app_125m; enum phy_mode mode; @@ -61,6 +67,10 @@ static int usb2_phy_set_mode(struct phy *_phy, usb2_set_ls_keepalive(phy, (mode != PHY_MODE_USB_DEVICE)); } + /* Connect usb 2.0 phy mode */ + regmap_update_bits(phy->sys_syscon, SYSCON_USB_SPLIT_OFFSET, + USB_PDRSTN_SPLIT, USB_PDRSTN_SPLIT); + return 0; } @@ -129,6 +139,12 @@ static int jh7110_usb_phy_probe(struct platform_device *pdev) phy_set_drvdata(phy->phy, phy); phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + phy->sys_syscon = + syscon_regmap_lookup_by_compatible("starfive,jh7110-sys-syscon"); + if (IS_ERR(phy->sys_syscon)) + return dev_err_probe(dev, PTR_ERR(phy->sys_syscon), + "Failed to get sys-syscon\n"); + return PTR_ERR_OR_ZERO(phy_provider); } diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c index cfdb54b6070a..342f5ccf611d 100644 --- a/drivers/phy/tegra/xusb.c +++ b/drivers/phy/tegra/xusb.c @@ -699,6 +699,8 @@ static int tegra_xusb_setup_usb_role_switch(struct tegra_xusb_port *port) return -ENOMEM; lane = tegra_xusb_find_lane(port->padctl, "usb2", port->index); + if (IS_ERR(lane)) + return PTR_ERR(lane); /* * Assign phy dev to usb-phy dev. Host/device drivers can use phy diff --git a/drivers/phy/ti/phy-j721e-wiz.c b/drivers/phy/ti/phy-j721e-wiz.c index a6c0c5607ffd..c6e846d385d2 100644 --- a/drivers/phy/ti/phy-j721e-wiz.c +++ b/drivers/phy/ti/phy-j721e-wiz.c @@ -450,8 +450,8 @@ static int wiz_mode_select(struct wiz *wiz) } else if (wiz->lane_phy_type[i] == PHY_TYPE_USXGMII) { ret = regmap_field_write(wiz->p0_mac_src_sel[i], 0x3); ret = regmap_field_write(wiz->p0_rxfclk_sel[i], 0x3); - ret = regmap_field_write(wiz->p0_refclk_sel[i], 0x3); - mode = LANE_MODE_GEN1; + ret = regmap_field_write(wiz->p0_refclk_sel[i], 0x2); + mode = LANE_MODE_GEN2; } else { continue; } diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index de15fc0df104..b52513eeeafa 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -3651,7 +3651,7 @@ static int do_device_access(struct sdeb_store_info *sip, struct scsi_cmnd *scp, enum dma_data_direction dir; struct scsi_data_buffer *sdb = &scp->sdb; u8 *fsp; - int i; + int i, total = 0; /* * Even though reads are inherently atomic (in this driver), we expect @@ -3688,18 +3688,16 @@ static int do_device_access(struct sdeb_store_info *sip, struct scsi_cmnd *scp, fsp + (block * sdebug_sector_size), sdebug_sector_size, sg_skip, do_write); sdeb_data_sector_unlock(sip, do_write); - if (ret != sdebug_sector_size) { - ret += (i * sdebug_sector_size); + total += ret; + if (ret != sdebug_sector_size) break; - } sg_skip += sdebug_sector_size; if (++block >= sdebug_store_sectors) block = 0; } - ret = num * sdebug_sector_size; sdeb_data_unlock(sip, atomic); - return ret; + return total; } /* Returns number of bytes copied or -1 if error. */ diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 191de1917f83..3fa990fb59c7 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -1003,6 +1003,7 @@ static int dspi_setup(struct spi_device *spi) u32 cs_sck_delay = 0, sck_cs_delay = 0; struct fsl_dspi_platform_data *pdata; unsigned char pasc = 0, asc = 0; + struct gpio_desc *gpio_cs; struct chip_data *chip; unsigned long clkrate; bool cs = true; @@ -1077,7 +1078,10 @@ static int dspi_setup(struct spi_device *spi) chip->ctar_val |= SPI_CTAR_LSBFE; } - gpiod_direction_output(spi_get_csgpiod(spi, 0), false); + gpio_cs = spi_get_csgpiod(spi, 0); + if (gpio_cs) + gpiod_direction_output(gpio_cs, false); + dspi_deassert_cs(spi, &cs); spi_set_ctldata(spi, chip); diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index f6e40f90418f..768d7482102a 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -1116,6 +1116,11 @@ static int spi_geni_probe(struct platform_device *pdev) init_completion(&mas->tx_reset_done); init_completion(&mas->rx_reset_done); spin_lock_init(&mas->lock); + + ret = geni_icc_get(&mas->se, NULL); + if (ret) + return ret; + pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, 250); ret = devm_pm_runtime_enable(dev); @@ -1125,9 +1130,6 @@ static int spi_geni_probe(struct platform_device *pdev) if (device_property_read_bool(&pdev->dev, "spi-slave")) spi->target = true; - ret = geni_icc_get(&mas->se, NULL); - if (ret) - return ret; /* Set the bus quota to a reasonable value for register access */ mas->se.icc_paths[GENI_TO_CORE].avg_bw = Bps_to_icc(CORE_2X_50_MHZ); mas->se.icc_paths[CPU_TO_GENI].avg_bw = GENI_DEFAULT_BW; diff --git a/drivers/spi/spi-mtk-snfi.c b/drivers/spi/spi-mtk-snfi.c index ddd98ddb7913..c5677fd94e5e 100644 --- a/drivers/spi/spi-mtk-snfi.c +++ b/drivers/spi/spi-mtk-snfi.c @@ -1187,7 +1187,7 @@ cleanup: /** * mtk_snand_is_page_ops() - check if the op is a controller supported page op. - * @op spi-mem op to check + * @op: spi-mem op to check * * Check whether op can be executed with read_from_cache or program_load * mode in the controller. diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index 4c4ff074e3f6..fc72a89fb3a7 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -2044,6 +2044,7 @@ static const struct stm32_spi_cfg stm32mp25_spi_cfg = { .baud_rate_div_max = STM32H7_SPI_MBR_DIV_MAX, .has_fifo = true, .prevent_dma_burst = true, + .has_device_mode = true, }; static const struct of_device_id stm32_spi_of_match[] = { diff --git a/drivers/staging/iio/frequency/ad9832.c b/drivers/staging/iio/frequency/ad9832.c index 6c390c4eb26d..492612e8f8ba 100644 --- a/drivers/staging/iio/frequency/ad9832.c +++ b/drivers/staging/iio/frequency/ad9832.c @@ -129,12 +129,15 @@ static unsigned long ad9832_calc_freqreg(unsigned long mclk, unsigned long fout) static int ad9832_write_frequency(struct ad9832_state *st, unsigned int addr, unsigned long fout) { + unsigned long clk_freq; unsigned long regval; - if (fout > (clk_get_rate(st->mclk) / 2)) + clk_freq = clk_get_rate(st->mclk); + + if (!clk_freq || fout > (clk_freq / 2)) return -EINVAL; - regval = ad9832_calc_freqreg(clk_get_rate(st->mclk), fout); + regval = ad9832_calc_freqreg(clk_freq, fout); st->freq_data[0] = cpu_to_be16((AD9832_CMD_FRE8BITSW << CMD_SHIFT) | (addr << ADD_SHIFT) | diff --git a/drivers/thunderbolt/retimer.c b/drivers/thunderbolt/retimer.c index 721319329afa..7db9869a9f3f 100644 --- a/drivers/thunderbolt/retimer.c +++ b/drivers/thunderbolt/retimer.c @@ -516,7 +516,7 @@ int tb_retimer_scan(struct tb_port *port, bool add) */ tb_retimer_set_inbound_sbtx(port); - for (i = 1; i <= TB_MAX_RETIMER_INDEX; i++) { + for (max = 1, i = 1; i <= TB_MAX_RETIMER_INDEX; i++) { /* * Last retimer is true only for the last on-board * retimer (the one connected directly to the Type-C @@ -527,9 +527,10 @@ int tb_retimer_scan(struct tb_port *port, bool add) last_idx = i; else if (ret < 0) break; + + max = i; } - max = i; ret = 0; /* Add retimers if they do not exist already */ diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 10e719dd837c..4f777788e917 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -288,6 +288,24 @@ static void tb_increase_tmu_accuracy(struct tb_tunnel *tunnel) device_for_each_child(&sw->dev, NULL, tb_increase_switch_tmu_accuracy); } +static int tb_switch_tmu_hifi_uni_required(struct device *dev, void *not_used) +{ + struct tb_switch *sw = tb_to_switch(dev); + + if (sw && tb_switch_tmu_is_enabled(sw) && + tb_switch_tmu_is_configured(sw, TB_SWITCH_TMU_MODE_HIFI_UNI)) + return 1; + + return device_for_each_child(dev, NULL, + tb_switch_tmu_hifi_uni_required); +} + +static bool tb_tmu_hifi_uni_required(struct tb *tb) +{ + return device_for_each_child(&tb->dev, NULL, + tb_switch_tmu_hifi_uni_required) == 1; +} + static int tb_enable_tmu(struct tb_switch *sw) { int ret; @@ -302,12 +320,30 @@ static int tb_enable_tmu(struct tb_switch *sw) ret = tb_switch_tmu_configure(sw, TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI); if (ret == -EOPNOTSUPP) { - if (tb_switch_clx_is_enabled(sw, TB_CL1)) - ret = tb_switch_tmu_configure(sw, - TB_SWITCH_TMU_MODE_LOWRES); - else - ret = tb_switch_tmu_configure(sw, - TB_SWITCH_TMU_MODE_HIFI_BI); + if (tb_switch_clx_is_enabled(sw, TB_CL1)) { + /* + * Figure out uni-directional HiFi TMU requirements + * currently in the domain. If there are no + * uni-directional HiFi requirements we can put the TMU + * into LowRes mode. + * + * Deliberately skip bi-directional HiFi links + * as these work independently of other links + * (and they do not allow any CL states anyway). + */ + if (tb_tmu_hifi_uni_required(sw->tb)) + ret = tb_switch_tmu_configure(sw, + TB_SWITCH_TMU_MODE_HIFI_UNI); + else + ret = tb_switch_tmu_configure(sw, + TB_SWITCH_TMU_MODE_LOWRES); + } else { + ret = tb_switch_tmu_configure(sw, TB_SWITCH_TMU_MODE_HIFI_BI); + } + + /* If not supported, fallback to bi-directional HiFi */ + if (ret == -EOPNOTSUPP) + ret = tb_switch_tmu_configure(sw, TB_SWITCH_TMU_MODE_HIFI_BI); } if (ret) return ret; diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index a63dcf48e59d..f5846598d80e 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -8219,7 +8219,7 @@ static void ufshcd_update_rtc(struct ufs_hba *hba) err = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_WRITE_ATTR, QUERY_ATTR_IDN_SECONDS_PASSED, 0, 0, &val); - ufshcd_rpm_put_sync(hba); + ufshcd_rpm_put(hba); if (err) dev_err(hba->dev, "%s: Failed to update rtc %d\n", __func__, err); diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index e8e43c38aa1b..cd0f7b4bd82a 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -808,7 +808,7 @@ static int check_dsp_e4(const u8 *dsp, int len) if (l > len) return 1; - /* zero is zero regardless endianes */ + /* zero is zero regardless endianness */ } while (blockidx->NotLastBlock); } @@ -1276,7 +1276,7 @@ static void uea_set_bulk_timeout(struct uea_softc *sc, u32 dsrate) sc->stats.phy.dsrate == dsrate) return; - /* Original timming (1Mbit/s) from ADI (used in windows driver) */ + /* Original timing (1Mbit/s) from ADI (used in windows driver) */ timeout = (dsrate <= 1024*1024) ? 0 : 1; ret = uea_request(sc, UEA_SET_TIMEOUT, timeout, 0, NULL); uea_info(INS_TO_USBDEV(sc), "setting new timeout %d%s\n", @@ -1972,7 +1972,7 @@ static void uea_dispatch_cmv_e1(struct uea_softc *sc, struct intr_pkt *intr) if (cmv->bDirection != E1_MODEMTOHOST) goto bad1; - /* FIXME : ADI930 reply wrong preambule (func = 2, sub = 2) to + /* FIXME : ADI930 reply wrong preamble (func = 2, sub = 2) to * the first MEMACCESS cmv. Ignore it... */ if (cmv->bFunction != dsc->function) { diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c index 2da6615fbb6f..d1e622bb1406 100644 --- a/drivers/usb/atm/usbatm.c +++ b/drivers/usb/atm/usbatm.c @@ -1158,7 +1158,7 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id, if (i >= num_rcv_urbs) list_add_tail(&urb->urb_list, &channel->list); - vdbg(&intf->dev, "%s: alloced buffer 0x%p buf size %u urb 0x%p", + vdbg(&intf->dev, "%s: allocated buffer 0x%p buf size %u urb 0x%p", __func__, urb->transfer_buffer, urb->transfer_buffer_length, urb); } diff --git a/drivers/usb/c67x00/c67x00-drv.c b/drivers/usb/c67x00/c67x00-drv.c index bb9d5d7ffefc..8f38e2c5369a 100644 --- a/drivers/usb/c67x00/c67x00-drv.c +++ b/drivers/usb/c67x00/c67x00-drv.c @@ -201,7 +201,7 @@ static void c67x00_drv_remove(struct platform_device *pdev) static struct platform_driver c67x00_driver = { .probe = c67x00_drv_probe, - .remove_new = c67x00_drv_remove, + .remove = c67x00_drv_remove, .driver = { .name = "c67x00", }, diff --git a/drivers/usb/cdns3/cdns3-imx.c b/drivers/usb/cdns3/cdns3-imx.c index 281de47e2a3b..a2f041e1707c 100644 --- a/drivers/usb/cdns3/cdns3-imx.c +++ b/drivers/usb/cdns3/cdns3-imx.c @@ -422,7 +422,7 @@ MODULE_DEVICE_TABLE(of, cdns_imx_of_match); static struct platform_driver cdns_imx_driver = { .probe = cdns_imx_probe, - .remove_new = cdns_imx_remove, + .remove = cdns_imx_remove, .driver = { .name = "cdns3-imx", .of_match_table = cdns_imx_of_match, diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c index 3ef8e3c872a3..59ec505e198a 100644 --- a/drivers/usb/cdns3/cdns3-plat.c +++ b/drivers/usb/cdns3/cdns3-plat.c @@ -327,7 +327,7 @@ MODULE_DEVICE_TABLE(of, of_cdns3_match); static struct platform_driver cdns3_driver = { .probe = cdns3_plat_probe, - .remove_new = cdns3_plat_remove, + .remove = cdns3_plat_remove, .driver = { .name = "cdns-usb3", .of_match_table = of_match_ptr(of_cdns3_match), diff --git a/drivers/usb/cdns3/cdns3-starfive.c b/drivers/usb/cdns3/cdns3-starfive.c index c04d196abd87..2ff7f2b48cc2 100644 --- a/drivers/usb/cdns3/cdns3-starfive.c +++ b/drivers/usb/cdns3/cdns3-starfive.c @@ -230,7 +230,7 @@ MODULE_DEVICE_TABLE(of, cdns_starfive_of_match); static struct platform_driver cdns_starfive_driver = { .probe = cdns_starfive_probe, - .remove_new = cdns_starfive_remove, + .remove = cdns_starfive_remove, .driver = { .name = "cdns3-starfive", .of_match_table = cdns_starfive_of_match, diff --git a/drivers/usb/cdns3/cdns3-ti.c b/drivers/usb/cdns3/cdns3-ti.c index cfabc12ee0e3..040bb91e9c01 100644 --- a/drivers/usb/cdns3/cdns3-ti.c +++ b/drivers/usb/cdns3/cdns3-ti.c @@ -233,7 +233,7 @@ MODULE_DEVICE_TABLE(of, cdns_ti_of_match); static struct platform_driver cdns_ti_driver = { .probe = cdns_ti_probe, - .remove_new = cdns_ti_remove, + .remove = cdns_ti_remove, .driver = { .name = "cdns3-ti", .of_match_table = cdns_ti_of_match, diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 2a38e1eb6546..97437de52ef6 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -25,6 +25,7 @@ #define TD_PAGE_COUNT 5 #define CI_HDRC_PAGE_SIZE 4096ul /* page size for TD's */ #define ENDPT_MAX 32 +#define CI_MAX_REQ_SIZE (4 * CI_HDRC_PAGE_SIZE) #define CI_MAX_BUF_SIZE (TD_PAGE_COUNT * CI_HDRC_PAGE_SIZE) /****************************************************************************** @@ -260,6 +261,7 @@ struct ci_hdrc { bool b_sess_valid_event; bool imx28_write_fix; bool has_portsc_pec_bug; + bool has_short_pkt_limit; bool supports_runtime_pm; bool in_lpm; bool wakeup_int; diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index c64ab0e07ea0..f2801700be8e 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -342,6 +342,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) struct ci_hdrc_platform_data pdata = { .name = dev_name(&pdev->dev), .capoffset = DEF_CAPOFFSET, + .flags = CI_HDRC_HAS_SHORT_PKT_LIMIT, .notify_event = ci_hdrc_imx_notify_event, }; int ret; @@ -675,7 +676,7 @@ static const struct dev_pm_ops ci_hdrc_imx_pm_ops = { }; static struct platform_driver ci_hdrc_imx_driver = { .probe = ci_hdrc_imx_probe, - .remove_new = ci_hdrc_imx_remove, + .remove = ci_hdrc_imx_remove, .shutdown = ci_hdrc_imx_shutdown, .driver = { .name = "imx_usb", diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c index 1661639cd2eb..3ab3daa78e34 100644 --- a/drivers/usb/chipidea/ci_hdrc_msm.c +++ b/drivers/usb/chipidea/ci_hdrc_msm.c @@ -292,7 +292,7 @@ MODULE_DEVICE_TABLE(of, msm_ci_dt_match); static struct platform_driver ci_hdrc_msm_driver = { .probe = ci_hdrc_msm_probe, - .remove_new = ci_hdrc_msm_remove, + .remove = ci_hdrc_msm_remove, .driver = { .name = "msm_hsusb", .of_match_table = msm_ci_dt_match, diff --git a/drivers/usb/chipidea/ci_hdrc_npcm.c b/drivers/usb/chipidea/ci_hdrc_npcm.c index 3e5e05dbda89..e52a2b05cbe2 100644 --- a/drivers/usb/chipidea/ci_hdrc_npcm.c +++ b/drivers/usb/chipidea/ci_hdrc_npcm.c @@ -98,7 +98,7 @@ MODULE_DEVICE_TABLE(of, npcm_udc_dt_match); static struct platform_driver npcm_udc_driver = { .probe = npcm_udc_probe, - .remove_new = npcm_udc_remove, + .remove = npcm_udc_remove, .driver = { .name = "npcm_udc", .of_match_table = npcm_udc_dt_match, diff --git a/drivers/usb/chipidea/ci_hdrc_tegra.c b/drivers/usb/chipidea/ci_hdrc_tegra.c index 9538d425f0a0..372788f0f970 100644 --- a/drivers/usb/chipidea/ci_hdrc_tegra.c +++ b/drivers/usb/chipidea/ci_hdrc_tegra.c @@ -406,7 +406,7 @@ static struct platform_driver tegra_usb_driver = { .pm = pm_ptr(&tegra_usb_pm), }, .probe = tegra_usb_probe, - .remove_new = tegra_usb_remove, + .remove = tegra_usb_remove, }; module_platform_driver(tegra_usb_driver); diff --git a/drivers/usb/chipidea/ci_hdrc_usb2.c b/drivers/usb/chipidea/ci_hdrc_usb2.c index 97379f653b06..8ffa1e95d8e8 100644 --- a/drivers/usb/chipidea/ci_hdrc_usb2.c +++ b/drivers/usb/chipidea/ci_hdrc_usb2.c @@ -116,7 +116,7 @@ static void ci_hdrc_usb2_remove(struct platform_device *pdev) static struct platform_driver ci_hdrc_usb2_driver = { .probe = ci_hdrc_usb2_probe, - .remove_new = ci_hdrc_usb2_remove, + .remove = ci_hdrc_usb2_remove, .driver = { .name = "chipidea-usb2", .of_match_table = ci_hdrc_usb2_of_match, diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 835bf2428dc6..694b4a8e4e1d 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -765,7 +765,7 @@ static int ci_get_platdata(struct device *dev, ext_id = ERR_PTR(-ENODEV); ext_vbus = ERR_PTR(-ENODEV); - if (of_property_read_bool(dev->of_node, "extcon")) { + if (of_property_present(dev->of_node, "extcon")) { /* Each one of them is not mandatory */ ext_vbus = extcon_get_edev_by_phandle(dev, 0); if (IS_ERR(ext_vbus) && PTR_ERR(ext_vbus) != -ENODEV) @@ -1076,6 +1076,8 @@ static int ci_hdrc_probe(struct platform_device *pdev) CI_HDRC_SUPPORTS_RUNTIME_PM); ci->has_portsc_pec_bug = !!(ci->platdata->flags & CI_HDRC_HAS_PORTSC_PEC_MISSED); + ci->has_short_pkt_limit = !!(ci->platdata->flags & + CI_HDRC_HAS_SHORT_PKT_LIMIT); platform_set_drvdata(pdev, ci); ret = hw_device_init(ci, base); @@ -1495,7 +1497,7 @@ static const struct dev_pm_ops ci_pm_ops = { static struct platform_driver ci_hdrc_driver = { .probe = ci_hdrc_probe, - .remove_new = ci_hdrc_remove, + .remove = ci_hdrc_remove, .driver = { .name = "ci_hdrc", .pm = &ci_pm_ops, diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 69ef3cd8d4f8..8a9b31fd5c89 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -10,6 +10,7 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/dmapool.h> +#include <linux/dma-direct.h> #include <linux/err.h> #include <linux/irqreturn.h> #include <linux/kernel.h> @@ -540,6 +541,126 @@ static int prepare_td_for_sg(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) return ret; } +/* + * Verify if the scatterlist is valid by iterating each sg entry. + * Return invalid sg entry index which is less than num_sgs. + */ +static int sglist_get_invalid_entry(struct device *dma_dev, u8 dir, + struct usb_request *req) +{ + int i; + struct scatterlist *s = req->sg; + + if (req->num_sgs == 1) + return 1; + + dir = dir ? DMA_TO_DEVICE : DMA_FROM_DEVICE; + + for (i = 0; i < req->num_sgs; i++, s = sg_next(s)) { + /* Only small sg (generally last sg) may be bounced. If + * that happens. we can't ensure the addr is page-aligned + * after dma map. + */ + if (dma_kmalloc_needs_bounce(dma_dev, s->length, dir)) + break; + + /* Make sure each sg start address (except first sg) is + * page-aligned and end address (except last sg) is also + * page-aligned. + */ + if (i == 0) { + if (!IS_ALIGNED(s->offset + s->length, + CI_HDRC_PAGE_SIZE)) + break; + } else { + if (s->offset) + break; + if (!sg_is_last(s) && !IS_ALIGNED(s->length, + CI_HDRC_PAGE_SIZE)) + break; + } + } + + return i; +} + +static int sglist_do_bounce(struct ci_hw_req *hwreq, int index, + bool copy, unsigned int *bounced) +{ + void *buf; + int i, ret, nents, num_sgs; + unsigned int rest, rounded; + struct scatterlist *sg, *src, *dst; + + nents = index + 1; + ret = sg_alloc_table(&hwreq->sgt, nents, GFP_KERNEL); + if (ret) + return ret; + + sg = src = hwreq->req.sg; + num_sgs = hwreq->req.num_sgs; + rest = hwreq->req.length; + dst = hwreq->sgt.sgl; + + for (i = 0; i < index; i++) { + memcpy(dst, src, sizeof(*src)); + rest -= src->length; + src = sg_next(src); + dst = sg_next(dst); + } + + /* create one bounce buffer */ + rounded = round_up(rest, CI_HDRC_PAGE_SIZE); + buf = kmalloc(rounded, GFP_KERNEL); + if (!buf) { + sg_free_table(&hwreq->sgt); + return -ENOMEM; + } + + sg_set_buf(dst, buf, rounded); + + hwreq->req.sg = hwreq->sgt.sgl; + hwreq->req.num_sgs = nents; + hwreq->sgt.sgl = sg; + hwreq->sgt.nents = num_sgs; + + if (copy) + sg_copy_to_buffer(src, num_sgs - index, buf, rest); + + *bounced = rest; + + return 0; +} + +static void sglist_do_debounce(struct ci_hw_req *hwreq, bool copy) +{ + void *buf; + int i, nents, num_sgs; + struct scatterlist *sg, *src, *dst; + + sg = hwreq->req.sg; + num_sgs = hwreq->req.num_sgs; + src = sg_last(sg, num_sgs); + buf = sg_virt(src); + + if (copy) { + dst = hwreq->sgt.sgl; + for (i = 0; i < num_sgs - 1; i++) + dst = sg_next(dst); + + nents = hwreq->sgt.nents - num_sgs + 1; + sg_copy_from_buffer(dst, nents, buf, sg_dma_len(src)); + } + + hwreq->req.sg = hwreq->sgt.sgl; + hwreq->req.num_sgs = hwreq->sgt.nents; + hwreq->sgt.sgl = sg; + hwreq->sgt.nents = num_sgs; + + kfree(buf); + sg_free_table(&hwreq->sgt); +} + /** * _hardware_enqueue: configures a request at hardware level * @hwep: endpoint @@ -552,6 +673,8 @@ static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) struct ci_hdrc *ci = hwep->ci; int ret = 0; struct td_node *firstnode, *lastnode; + unsigned int bounced_size; + struct scatterlist *sg; /* don't queue twice */ if (hwreq->req.status == -EALREADY) @@ -559,11 +682,29 @@ static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) hwreq->req.status = -EALREADY; + if (hwreq->req.num_sgs && hwreq->req.length && + ci->has_short_pkt_limit) { + ret = sglist_get_invalid_entry(ci->dev->parent, hwep->dir, + &hwreq->req); + if (ret < hwreq->req.num_sgs) { + ret = sglist_do_bounce(hwreq, ret, hwep->dir == TX, + &bounced_size); + if (ret) + return ret; + } + } + ret = usb_gadget_map_request_by_dev(ci->dev->parent, &hwreq->req, hwep->dir); if (ret) return ret; + if (hwreq->sgt.sgl) { + /* We've mapped a bigger buffer, now recover the actual size */ + sg = sg_last(hwreq->req.sg, hwreq->req.num_sgs); + sg_dma_len(sg) = min(sg_dma_len(sg), bounced_size); + } + if (hwreq->req.num_mapped_sgs) ret = prepare_td_for_sg(hwep, hwreq); else @@ -612,10 +753,17 @@ static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) do { hw_write(ci, OP_USBCMD, USBCMD_ATDTW, USBCMD_ATDTW); tmp_stat = hw_read(ci, OP_ENDPTSTAT, BIT(n)); - } while (!hw_read(ci, OP_USBCMD, USBCMD_ATDTW)); + } while (!hw_read(ci, OP_USBCMD, USBCMD_ATDTW) && tmp_stat); hw_write(ci, OP_USBCMD, USBCMD_ATDTW, 0); if (tmp_stat) goto done; + + /* OP_ENDPTSTAT will be clear by HW when the endpoint met + * err. This dTD don't push to dQH if current dTD point is + * not the last one in previous request. + */ + if (hwep->qh.ptr->curr != cpu_to_le32(prevlastnode->dma)) + goto done; } /* QH configuration */ @@ -676,6 +824,7 @@ static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) unsigned remaining_length; unsigned actual = hwreq->req.length; struct ci_hdrc *ci = hwep->ci; + bool is_isoc = hwep->type == USB_ENDPOINT_XFER_ISOC; if (hwreq->req.status != -EALREADY) return -EINVAL; @@ -689,7 +838,7 @@ static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) int n = hw_ep_bit(hwep->num, hwep->dir); if (ci->rev == CI_REVISION_24 || - ci->rev == CI_REVISION_22) + ci->rev == CI_REVISION_22 || is_isoc) if (!hw_read(ci, OP_ENDPTSTAT, BIT(n))) reprime_dtd(ci, hwep, node); hwreq->req.status = -EALREADY; @@ -708,11 +857,15 @@ static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) hwreq->req.status = -EPROTO; break; } else if ((TD_STATUS_TR_ERR & hwreq->req.status)) { - hwreq->req.status = -EILSEQ; - break; + if (is_isoc) { + hwreq->req.status = 0; + } else { + hwreq->req.status = -EILSEQ; + break; + } } - if (remaining_length) { + if (remaining_length && !is_isoc) { if (hwep->dir == TX) { hwreq->req.status = -EPROTO; break; @@ -733,6 +886,10 @@ static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) usb_gadget_unmap_request_by_dev(hwep->ci->dev->parent, &hwreq->req, hwep->dir); + /* sglist bounced */ + if (hwreq->sgt.sgl) + sglist_do_debounce(hwreq, hwep->dir == RX); + hwreq->req.actual += actual; if (hwreq->req.status) @@ -960,6 +1117,12 @@ static int _ep_queue(struct usb_ep *ep, struct usb_request *req, return -EMSGSIZE; } + if (ci->has_short_pkt_limit && + hwreq->req.length > CI_MAX_REQ_SIZE) { + dev_err(hwep->ci->dev, "request length too big (max 16KB)\n"); + return -EMSGSIZE; + } + /* first nuke then test link, e.g. previous status has not sent */ if (!list_empty(&hwreq->queue)) { dev_err(hwep->ci->dev, "request already in queue\n"); @@ -1574,6 +1737,9 @@ static int ep_dequeue(struct usb_ep *ep, struct usb_request *req) usb_gadget_unmap_request(&hwep->ci->gadget, req, hwep->dir); + if (hwreq->sgt.sgl) + sglist_do_debounce(hwreq, false); + req->status = -ECONNRESET; if (hwreq->req.complete != NULL) { @@ -2063,7 +2229,7 @@ static irqreturn_t udc_irq(struct ci_hdrc *ci) } } - if (USBi_UI & intr) + if ((USBi_UI | USBi_UEI) & intr) isr_tr_complete_handler(ci); if ((USBi_SLI & intr) && !(ci->suspended)) { diff --git a/drivers/usb/chipidea/udc.h b/drivers/usb/chipidea/udc.h index 5193df1e18c7..c8a47389a46b 100644 --- a/drivers/usb/chipidea/udc.h +++ b/drivers/usb/chipidea/udc.h @@ -69,11 +69,13 @@ struct td_node { * @req: request structure for gadget drivers * @queue: link to QH list * @tds: link to TD list + * @sgt: hold original sglist when bounce sglist */ struct ci_hw_req { struct usb_request req; struct list_head queue; struct list_head tds; + struct sg_table sgt; }; #ifdef CONFIG_USB_CHIPIDEA_UDC diff --git a/drivers/usb/common/usb-conn-gpio.c b/drivers/usb/common/usb-conn-gpio.c index 501e8bc9738e..c84b4a700084 100644 --- a/drivers/usb/common/usb-conn-gpio.c +++ b/drivers/usb/common/usb-conn-gpio.c @@ -340,7 +340,7 @@ MODULE_DEVICE_TABLE(of, usb_conn_dt_match); static struct platform_driver usb_conn_driver = { .probe = usb_conn_probe, - .remove_new = usb_conn_remove, + .remove = usb_conn_remove, .driver = { .name = "usb-conn-gpio", .pm = &usb_conn_pm_ops, diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 3beb6a862e80..5363468a282f 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -238,6 +238,9 @@ static int usbdev_mmap(struct file *file, struct vm_area_struct *vma) dma_addr_t dma_handle = DMA_MAPPING_ERROR; int ret; + if (!(file->f_mode & FMODE_WRITE)) + return -EPERM; + ret = usbfs_increase_memory_usage(size + sizeof(struct usb_memory)); if (ret) goto error; diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c index 4b38b87a1343..e48399401608 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c @@ -14,6 +14,7 @@ #include <linux/kernel.h> #include <linux/spinlock.h> #include <linux/slab.h> +#include <linux/sysfs.h> #include <linux/usb.h> #include "usb.h" @@ -39,7 +40,7 @@ static ssize_t field##_show(struct device *dev, \ char *buf) \ { \ struct ep_device *ep = to_ep_device(dev); \ - return sprintf(buf, format_string, ep->desc->field); \ + return sysfs_emit(buf, format_string, ep->desc->field); \ } \ static DEVICE_ATTR_RO(field) @@ -52,7 +53,7 @@ static ssize_t wMaxPacketSize_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ep_device *ep = to_ep_device(dev); - return sprintf(buf, "%04x\n", usb_endpoint_maxp(ep->desc)); + return sysfs_emit(buf, "%04x\n", usb_endpoint_maxp(ep->desc)); } static DEVICE_ATTR_RO(wMaxPacketSize); @@ -76,7 +77,7 @@ static ssize_t type_show(struct device *dev, struct device_attribute *attr, type = "Interrupt"; break; } - return sprintf(buf, "%s\n", type); + return sysfs_emit(buf, "%s\n", type); } static DEVICE_ATTR_RO(type); @@ -95,7 +96,7 @@ static ssize_t interval_show(struct device *dev, struct device_attribute *attr, interval /= 1000; } - return sprintf(buf, "%d%cs\n", interval, unit); + return sysfs_emit(buf, "%d%cs\n", interval, unit); } static DEVICE_ATTR_RO(interval); @@ -111,7 +112,7 @@ static ssize_t direction_show(struct device *dev, struct device_attribute *attr, direction = "in"; else direction = "out"; - return sprintf(buf, "%s\n", direction); + return sysfs_emit(buf, "%s\n", direction); } static DEVICE_ATTR_RO(direction); diff --git a/drivers/usb/core/ledtrig-usbport.c b/drivers/usb/core/ledtrig-usbport.c index 85c999f71ad7..5e3c515991f3 100644 --- a/drivers/usb/core/ledtrig-usbport.c +++ b/drivers/usb/core/ledtrig-usbport.c @@ -10,6 +10,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/slab.h> +#include <linux/sysfs.h> #include <linux/usb.h> #include <linux/usb/of.h> @@ -87,7 +88,7 @@ static ssize_t usbport_trig_port_show(struct device *dev, struct usbport_trig_port, attr); - return sprintf(buf, "%d\n", port->observed) + 1; + return sysfs_emit(buf, "%d\n", port->observed) + 1; } static ssize_t usbport_trig_port_store(struct device *dev, diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index e7da2fca11a4..45d7af00f8d1 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -9,6 +9,7 @@ #include <linux/kstrtox.h> #include <linux/slab.h> +#include <linux/sysfs.h> #include <linux/pm_qos.h> #include <linux/component.h> #include <linux/usb/of.h> @@ -166,7 +167,7 @@ static ssize_t location_show(struct device *dev, { struct usb_port *port_dev = to_usb_port(dev); - return sprintf(buf, "0x%08x\n", port_dev->location); + return sysfs_emit(buf, "0x%08x\n", port_dev->location); } static DEVICE_ATTR_RO(location); @@ -191,7 +192,7 @@ static ssize_t connect_type_show(struct device *dev, break; } - return sprintf(buf, "%s\n", result); + return sysfs_emit(buf, "%s\n", result); } static DEVICE_ATTR_RO(connect_type); @@ -210,7 +211,7 @@ static ssize_t over_current_count_show(struct device *dev, { struct usb_port *port_dev = to_usb_port(dev); - return sprintf(buf, "%u\n", port_dev->over_current_count); + return sysfs_emit(buf, "%u\n", port_dev->over_current_count); } static DEVICE_ATTR_RO(over_current_count); @@ -219,7 +220,7 @@ static ssize_t quirks_show(struct device *dev, { struct usb_port *port_dev = to_usb_port(dev); - return sprintf(buf, "%08x\n", port_dev->quirks); + return sysfs_emit(buf, "%08x\n", port_dev->quirks); } static ssize_t quirks_store(struct device *dev, struct device_attribute *attr, @@ -254,7 +255,7 @@ static ssize_t usb3_lpm_permit_show(struct device *dev, p = "0"; } - return sprintf(buf, "%s\n", p); + return sysfs_emit(buf, "%s\n", p); } static ssize_t usb3_lpm_permit_store(struct device *dev, diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c index 21585ed89ef8..03c22114214b 100644 --- a/drivers/usb/core/usb-acpi.c +++ b/drivers/usb/core/usb-acpi.c @@ -170,11 +170,11 @@ static int usb_acpi_add_usb4_devlink(struct usb_device *udev) struct fwnode_handle *nhi_fwnode __free(fwnode_handle) = fwnode_find_reference(dev_fwnode(&port_dev->dev), "usb4-host-interface", 0); - if (IS_ERR(nhi_fwnode)) + if (IS_ERR(nhi_fwnode) || !nhi_fwnode->dev) return 0; link = device_link_add(&port_dev->child->dev, nhi_fwnode->dev, - DL_FLAG_AUTOREMOVE_CONSUMER | + DL_FLAG_STATELESS | DL_FLAG_RPM_ACTIVE | DL_FLAG_PM_RUNTIME); if (!link) { diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index 68226defdc60..4d73fae80b12 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -23,7 +23,6 @@ static void dwc2_set_bcm_params(struct dwc2_hsotg *hsotg) p->max_transfer_size = 65535; p->max_packet_count = 511; p->ahbcfg = 0x10; - p->no_clock_gating = true; } static void dwc2_set_his_params(struct dwc2_hsotg *hsotg) diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index c1b7209b9483..91c80a92d9b8 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -756,7 +756,7 @@ static struct platform_driver dwc2_platform_driver = { .pm = &dwc2_dev_pm_ops, }, .probe = dwc2_driver_probe, - .remove_new = dwc2_driver_remove, + .remove = dwc2_driver_remove, .shutdown = dwc2_driver_shutdown, }; diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 427e5660f87c..9b888d33e64d 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1409,7 +1409,7 @@ static int dwc3_core_init(struct dwc3 *dwc) /* * When configured in HOST mode, after issuing U3/L2 exit controller - * fails to send proper CRC checksum in CRC5 feild. Because of this + * fails to send proper CRC checksum in CRC5 field. Because of this * behaviour Transaction Error is generated, resulting in reset and * re-enumeration of usb device attached. All the termsel, xcvrsel, * opmode becomes 0 during end of resume. Enabling bit 10 of GUCTL1 @@ -1941,7 +1941,7 @@ static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc) struct extcon_dev *edev = NULL; const char *name; - if (device_property_read_bool(dev, "extcon")) + if (device_property_present(dev, "extcon")) return extcon_get_edev_by_phandle(dev, 0); /* @@ -2652,7 +2652,7 @@ MODULE_DEVICE_TABLE(acpi, dwc3_acpi_match); static struct platform_driver dwc3_driver = { .probe = dwc3_probe, - .remove_new = dwc3_remove, + .remove = dwc3_remove, .driver = { .name = "dwc3", .of_match_table = of_match_ptr(of_dwc3_match), diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index eab81dfdcc35..eaa55c0cf62f 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -81,7 +81,7 @@ #define DWC3_GSNPSREV_MASK 0xffff #define DWC3_GSNPS_ID(p) (((p) & DWC3_GSNPSID_MASK) >> 16) -/* DWC3 registers memory space boundries */ +/* DWC3 registers memory space boundaries */ #define DWC3_XHCI_REGS_START 0x0 #define DWC3_XHCI_REGS_END 0x7fff #define DWC3_GLOBALS_REGS_START 0xc100 diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c index fad151e78fd6..5e3d1741701f 100644 --- a/drivers/usb/dwc3/dwc3-am62.c +++ b/drivers/usb/dwc3/dwc3-am62.c @@ -377,7 +377,7 @@ MODULE_DEVICE_TABLE(of, dwc3_ti_of_match); static struct platform_driver dwc3_ti_driver = { .probe = dwc3_ti_probe, - .remove_new = dwc3_ti_remove, + .remove = dwc3_ti_remove, .driver = { .name = "dwc3-am62", .pm = DEV_PM_OPS, diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index 9a6e988d165a..f5d963fae9e0 100644 --- a/drivers/usb/dwc3/dwc3-exynos.c +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -243,7 +243,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(dwc3_exynos_dev_pm_ops, static struct platform_driver dwc3_exynos_driver = { .probe = dwc3_exynos_probe, - .remove_new = dwc3_exynos_remove, + .remove = dwc3_exynos_remove, .driver = { .name = "exynos-dwc3", .of_match_table = exynos_dwc3_match, diff --git a/drivers/usb/dwc3/dwc3-imx8mp.c b/drivers/usb/dwc3/dwc3-imx8mp.c index 64c0cd1995aa..356812cbcd88 100644 --- a/drivers/usb/dwc3/dwc3-imx8mp.c +++ b/drivers/usb/dwc3/dwc3-imx8mp.c @@ -400,7 +400,7 @@ MODULE_DEVICE_TABLE(of, dwc3_imx8mp_of_match); static struct platform_driver dwc3_imx8mp_driver = { .probe = dwc3_imx8mp_probe, - .remove_new = dwc3_imx8mp_remove, + .remove = dwc3_imx8mp_remove, .driver = { .name = "imx8mp-dwc3", .pm = pm_ptr(&dwc3_imx8mp_dev_pm_ops), diff --git a/drivers/usb/dwc3/dwc3-keystone.c b/drivers/usb/dwc3/dwc3-keystone.c index 8899348b6276..7ee1610162b9 100644 --- a/drivers/usb/dwc3/dwc3-keystone.c +++ b/drivers/usb/dwc3/dwc3-keystone.c @@ -208,7 +208,7 @@ MODULE_DEVICE_TABLE(of, kdwc3_of_match); static struct platform_driver kdwc3_driver = { .probe = kdwc3_probe, - .remove_new = kdwc3_remove, + .remove = kdwc3_remove, .driver = { .name = "keystone-dwc3", .of_match_table = kdwc3_of_match, diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c index 2c07c038b584..7d80bf7b18b0 100644 --- a/drivers/usb/dwc3/dwc3-meson-g12a.c +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -968,7 +968,7 @@ MODULE_DEVICE_TABLE(of, dwc3_meson_g12a_match); static struct platform_driver dwc3_meson_g12a_driver = { .probe = dwc3_meson_g12a_probe, - .remove_new = dwc3_meson_g12a_remove, + .remove = dwc3_meson_g12a_remove, .driver = { .name = "dwc3-meson-g12a", .of_match_table = dwc3_meson_g12a_match, diff --git a/drivers/usb/dwc3/dwc3-octeon.c b/drivers/usb/dwc3/dwc3-octeon.c index 1a3b205367fd..42bfc14ae0c4 100644 --- a/drivers/usb/dwc3/dwc3-octeon.c +++ b/drivers/usb/dwc3/dwc3-octeon.c @@ -520,7 +520,7 @@ MODULE_DEVICE_TABLE(of, dwc3_octeon_of_match); static struct platform_driver dwc3_octeon_driver = { .probe = dwc3_octeon_probe, - .remove_new = dwc3_octeon_remove, + .remove = dwc3_octeon_remove, .driver = { .name = "dwc3-octeon", .of_match_table = dwc3_octeon_of_match, diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c index be7be00ecb34..a4954a21be93 100644 --- a/drivers/usb/dwc3/dwc3-of-simple.c +++ b/drivers/usb/dwc3/dwc3-of-simple.c @@ -180,7 +180,7 @@ MODULE_DEVICE_TABLE(of, of_dwc3_simple_match); static struct platform_driver dwc3_of_simple_driver = { .probe = dwc3_of_simple_probe, - .remove_new = dwc3_of_simple_remove, + .remove = dwc3_of_simple_remove, .shutdown = dwc3_of_simple_shutdown, .driver = { .name = "dwc3-of-simple", diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 2a11fc0ee84f..b261c46124c6 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -416,7 +416,7 @@ static int dwc3_omap_extcon_register(struct dwc3_omap *omap) struct device_node *node = omap->dev->of_node; struct extcon_dev *edev; - if (of_property_read_bool(node, "extcon")) { + if (of_property_present(node, "extcon")) { edev = extcon_get_edev_by_phandle(omap->dev, 0); if (IS_ERR(edev)) { dev_vdbg(omap->dev, "couldn't get extcon device\n"); @@ -611,7 +611,7 @@ static const struct dev_pm_ops dwc3_omap_dev_pm_ops = { static struct platform_driver dwc3_omap_driver = { .probe = dwc3_omap_probe, - .remove_new = dwc3_omap_remove, + .remove = dwc3_omap_remove, .driver = { .name = "omap-dwc3", .of_match_table = of_dwc3_match, diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index c1d4b52f25b0..58683bb672e9 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -161,7 +161,7 @@ static int dwc3_qcom_register_extcon(struct dwc3_qcom *qcom) struct extcon_dev *host_edev; int ret; - if (!of_property_read_bool(dev->of_node, "extcon")) + if (!of_property_present(dev->of_node, "extcon")) return 0; qcom->edev = extcon_get_edev_by_phandle(dev, 0); @@ -921,7 +921,7 @@ MODULE_DEVICE_TABLE(of, dwc3_qcom_of_match); static struct platform_driver dwc3_qcom_driver = { .probe = dwc3_qcom_probe, - .remove_new = dwc3_qcom_remove, + .remove = dwc3_qcom_remove, .driver = { .name = "dwc3-qcom", .pm = &dwc3_qcom_dev_pm_ops, diff --git a/drivers/usb/dwc3/dwc3-rtk.c b/drivers/usb/dwc3/dwc3-rtk.c index e9c8b032c72c..56c53e0c0257 100644 --- a/drivers/usb/dwc3/dwc3-rtk.c +++ b/drivers/usb/dwc3/dwc3-rtk.c @@ -441,7 +441,7 @@ static const struct dev_pm_ops dwc3_rtk_dev_pm_ops = { static struct platform_driver dwc3_rtk_driver = { .probe = dwc3_rtk_probe, - .remove_new = dwc3_rtk_remove, + .remove = dwc3_rtk_remove, .driver = { .name = "rtk-dwc3", .of_match_table = rtk_dwc3_match, diff --git a/drivers/usb/dwc3/dwc3-st.c b/drivers/usb/dwc3/dwc3-st.c index 2841021f3557..e16c3237180e 100644 --- a/drivers/usb/dwc3/dwc3-st.c +++ b/drivers/usb/dwc3/dwc3-st.c @@ -356,7 +356,7 @@ MODULE_DEVICE_TABLE(of, st_dwc3_match); static struct platform_driver st_dwc3_driver = { .probe = st_dwc3_probe, - .remove_new = st_dwc3_remove, + .remove = st_dwc3_remove, .driver = { .name = "usb-st-dwc3", .of_match_table = st_dwc3_match, diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c index b5e5be424ce9..e3738e1610db 100644 --- a/drivers/usb/dwc3/dwc3-xilinx.c +++ b/drivers/usb/dwc3/dwc3-xilinx.c @@ -420,7 +420,7 @@ static const struct dev_pm_ops dwc3_xlnx_dev_pm_ops = { static struct platform_driver dwc3_xlnx_driver = { .probe = dwc3_xlnx_probe, - .remove_new = dwc3_xlnx_remove, + .remove = dwc3_xlnx_remove, .driver = { .name = "dwc3-xilinx", .of_match_table = dwc3_xlnx_of_match, diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index c9533a99e47c..f3d97ad5156e 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -145,7 +145,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, * Unfortunately we have uncovered a limitation wrt the Data Phase. * * Section 9.4 says we can wait for the XferNotReady(DATA) event to - * come before issueing Start Transfer command, but if we do, we will + * come before issuing Start Transfer command, but if we do, we will * miss situations where the host starts another SETUP phase instead of * the DATA phase. Such cases happen at least on TD.7.6 of the Link * Layer Compliance Suite. diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 4959c26d3b71..2fed2aa01407 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -775,15 +775,30 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep) ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7); - if ((dep->endpoint.maxburst > 1 && - usb_endpoint_xfer_bulk(dep->endpoint.desc)) || - usb_endpoint_xfer_isoc(dep->endpoint.desc)) - num_fifos = 3; - - if (dep->endpoint.maxburst > 6 && - (usb_endpoint_xfer_bulk(dep->endpoint.desc) || - usb_endpoint_xfer_isoc(dep->endpoint.desc)) && DWC3_IP_IS(DWC31)) - num_fifos = dwc->tx_fifo_resize_max_num; + switch (dwc->gadget->speed) { + case USB_SPEED_SUPER_PLUS: + case USB_SPEED_SUPER: + if (usb_endpoint_xfer_bulk(dep->endpoint.desc) || + usb_endpoint_xfer_isoc(dep->endpoint.desc)) + num_fifos = min_t(unsigned int, + dep->endpoint.maxburst, + dwc->tx_fifo_resize_max_num); + break; + case USB_SPEED_HIGH: + if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { + num_fifos = min_t(unsigned int, + usb_endpoint_maxp_mult(dep->endpoint.desc) + 1, + dwc->tx_fifo_resize_max_num); + break; + } + fallthrough; + case USB_SPEED_FULL: + if (usb_endpoint_xfer_bulk(dep->endpoint.desc)) + num_fifos = 2; + break; + default: + break; + } /* FIFO size for a single buffer */ fifo = dwc3_gadget_calc_tx_fifo_size(dwc, 1); diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index e0533cee6870..b48e108fc8fe 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -35,7 +35,7 @@ static void dwc3_power_off_all_roothub_ports(struct dwc3 *dwc) u32 reg; int i; - /* xhci regs is not mapped yet, do it temperary here */ + /* xhci regs are not mapped yet, do it temporarily here */ if (dwc->xhci_resources[0].start) { xhci_regs = ioremap(dwc->xhci_resources[0].start, DWC3_XHCI_REGS_END); if (!xhci_regs) { diff --git a/drivers/usb/fotg210/fotg210-core.c b/drivers/usb/fotg210/fotg210-core.c index 0655afe7f977..49f25a70b32e 100644 --- a/drivers/usb/fotg210/fotg210-core.c +++ b/drivers/usb/fotg210/fotg210-core.c @@ -195,7 +195,7 @@ static struct platform_driver fotg210_driver = { .of_match_table = of_match_ptr(fotg210_of_match), }, .probe = fotg210_probe, - .remove_new = fotg210_remove, + .remove = fotg210_remove, }; static int __init fotg210_init(void) diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c index b1f625245713..95f144a54ed9 100644 --- a/drivers/usb/gadget/config.c +++ b/drivers/usb/gadget/config.c @@ -57,11 +57,11 @@ EXPORT_SYMBOL_GPL(usb_descriptor_fillbuf); * usb_gadget_config_buf - builts a complete configuration descriptor * @config: Header for the descriptor, including characteristics such * as power requirements and number of interfaces. - * @desc: Null-terminated vector of pointers to the descriptors (interface, - * endpoint, etc) defining all functions in this device configuration. * @buf: Buffer for the resulting configuration descriptor. * @length: Length of buffer. If this is not big enough to hold the * entire configuration descriptor, an error code will be returned. + * @desc: Null-terminated vector of pointers to the descriptors (interface, + * endpoint, etc) defining all functions in this device configuration. * * This copies descriptors into the response buffer, building a descriptor * for that configuration. It returns the buffer length or a negative diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index 87917a7d4a9b..7ce1637276f0 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -41,6 +41,10 @@ obj-$(CONFIG_USB_F_UAC1_LEGACY) += usb_f_uac1_legacy.o usb_f_uac2-y := f_uac2.o obj-$(CONFIG_USB_F_UAC2) += usb_f_uac2.o usb_f_uvc-y := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_configfs.o +ifneq ($(CONFIG_TRACING),) + CFLAGS_uvc_trace.o := -I$(src) + usb_f_uvc-y += uvc_trace.o +endif obj-$(CONFIG_USB_F_UVC) += usb_f_uvc.o usb_f_midi-y := f_midi.o obj-$(CONFIG_USB_F_MIDI) += usb_f_midi.o diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 1067847cc079..837fcdfa3840 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -819,9 +819,9 @@ static int f_midi_register_card(struct f_midi *midi) goto fail; } - strcpy(card->driver, f_midi_longname); - strcpy(card->longname, f_midi_longname); - strcpy(card->shortname, f_midi_shortname); + strscpy(card->driver, f_midi_longname); + strscpy(card->longname, f_midi_longname); + strscpy(card->shortname, f_midi_shortname); /* Set up rawmidi */ snd_component_add(card, "MIDI"); @@ -833,7 +833,7 @@ static int f_midi_register_card(struct f_midi *midi) } midi->rmidi = rmidi; midi->in_last_port = 0; - strcpy(rmidi->name, card->shortname); + strscpy(rmidi->name, card->shortname); rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c index 8285df9ed6fd..ee3d9e3578f7 100644 --- a/drivers/usb/gadget/function/f_midi2.c +++ b/drivers/usb/gadget/function/f_midi2.c @@ -1285,10 +1285,8 @@ static int f_midi2_set_alt(struct usb_function *fn, unsigned int intf, if (alt == 0) op_mode = MIDI_OP_MODE_MIDI1; - else if (alt == 1) - op_mode = MIDI_OP_MODE_MIDI2; else - op_mode = MIDI_OP_MODE_UNSET; + op_mode = MIDI_OP_MODE_MIDI2; if (midi2->operation_mode == op_mode) return 0; diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 40187b7112e7..aa6ab666741a 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -465,7 +465,7 @@ uvc_register_video(struct uvc_device *uvc) memcpy(mem, desc, (desc)->bLength); \ *(dst)++ = mem; \ mem += (desc)->bLength; \ - } while (0); + } while (0) #define UVC_COPY_DESCRIPTORS(mem, dst, src) \ do { \ @@ -991,6 +991,8 @@ static void uvc_function_unbind(struct usb_configuration *c, uvcg_info(f, "%s()\n", __func__); + kthread_cancel_work_sync(&video->hw_submit); + if (video->async_wq) destroy_workqueue(video->async_wq); diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index cb35687b11e7..6f44dd732315 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -71,6 +71,11 @@ extern unsigned int uvc_gadget_trace_param; #define UVCG_REQUEST_HEADER_LEN 12 +#define UVCG_REQ_MAX_INT_COUNT 16 +#define UVCG_REQ_MAX_ZERO_COUNT (2 * UVCG_REQ_MAX_INT_COUNT) + +#define UVCG_STREAMING_MIN_BUFFERS 2 + /* ------------------------------------------------------------------------ * Structures */ @@ -91,16 +96,24 @@ struct uvc_video { struct work_struct pump; struct workqueue_struct *async_wq; + struct kthread_worker *kworker; + struct kthread_work hw_submit; + + atomic_t queued; + /* Frame parameters */ u8 bpp; u32 fcc; unsigned int width; unsigned int height; unsigned int imagesize; + unsigned int interval; struct mutex mutex; /* protects frame parameters */ unsigned int uvc_num_requests; + unsigned int reqs_per_frame; + /* Requests */ bool is_enabled; /* tracks whether video stream is enabled */ unsigned int req_size; diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index 6fac696ea846..f131943254a4 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -1566,11 +1566,13 @@ static const struct uvcg_config_group_type uvcg_control_grp_type = { /* ----------------------------------------------------------------------------- * streaming/uncompressed * streaming/mjpeg + * streaming/framebased */ static const char * const uvcg_format_names[] = { "uncompressed", "mjpeg", + "framebased", }; static struct uvcg_color_matching * @@ -1777,6 +1779,9 @@ static int uvcg_streaming_header_allow_link(struct config_item *src, target_fmt = container_of(to_config_group(target), struct uvcg_format, group); + if (!target_fmt) + goto out; + uvcg_format_set_indices(to_config_group(target)); format_ptr = kzalloc(sizeof(*format_ptr), GFP_KERNEL); @@ -1816,6 +1821,9 @@ static void uvcg_streaming_header_drop_link(struct config_item *src, target_fmt = container_of(to_config_group(target), struct uvcg_format, group); + if (!target_fmt) + goto out; + list_for_each_entry_safe(format_ptr, tmp, &src_hdr->formats, entry) if (format_ptr->fmt == target_fmt) { list_del(&format_ptr->entry); @@ -1826,6 +1834,7 @@ static void uvcg_streaming_header_drop_link(struct config_item *src, --target_fmt->linked; +out: mutex_unlock(&opts->lock); mutex_unlock(su_mutex); } @@ -2022,6 +2031,7 @@ UVCG_FRAME_ATTR(dw_min_bit_rate, dwMinBitRate, 32); UVCG_FRAME_ATTR(dw_max_bit_rate, dwMaxBitRate, 32); UVCG_FRAME_ATTR(dw_max_video_frame_buffer_size, dwMaxVideoFrameBufferSize, 32); UVCG_FRAME_ATTR(dw_default_frame_interval, dwDefaultFrameInterval, 32); +UVCG_FRAME_ATTR(dw_bytes_perline, dwBytesPerLine, 32); #undef UVCG_FRAME_ATTR @@ -2035,7 +2045,7 @@ static ssize_t uvcg_frame_dw_frame_interval_show(struct config_item *item, int result, i; char *pg = page; - mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ opts_item = frm->item.ci_parent->ci_parent->ci_parent->ci_parent; opts = to_f_uvc_opts(opts_item); @@ -2105,7 +2115,7 @@ end: UVC_ATTR(uvcg_frame_, dw_frame_interval, dwFrameInterval); -static struct configfs_attribute *uvcg_frame_attrs[] = { +static struct configfs_attribute *uvcg_frame_attrs1[] = { &uvcg_frame_attr_b_frame_index, &uvcg_frame_attr_bm_capabilities, &uvcg_frame_attr_w_width, @@ -2118,12 +2128,31 @@ static struct configfs_attribute *uvcg_frame_attrs[] = { NULL, }; -static const struct config_item_type uvcg_frame_type = { +static struct configfs_attribute *uvcg_frame_attrs2[] = { + &uvcg_frame_attr_b_frame_index, + &uvcg_frame_attr_bm_capabilities, + &uvcg_frame_attr_w_width, + &uvcg_frame_attr_w_height, + &uvcg_frame_attr_dw_min_bit_rate, + &uvcg_frame_attr_dw_max_bit_rate, + &uvcg_frame_attr_dw_default_frame_interval, + &uvcg_frame_attr_dw_frame_interval, + &uvcg_frame_attr_dw_bytes_perline, + NULL, +}; + +static const struct config_item_type uvcg_frame_type1 = { .ct_item_ops = &uvcg_config_item_ops, - .ct_attrs = uvcg_frame_attrs, + .ct_attrs = uvcg_frame_attrs1, .ct_owner = THIS_MODULE, }; +static const struct config_item_type uvcg_frame_type2 = { + .ct_item_ops = &uvcg_config_item_ops, + .ct_attrs = uvcg_frame_attrs2, + .ct_owner = THIS_MODULE, +}; + static struct config_item *uvcg_frame_make(struct config_group *group, const char *name) { @@ -2145,6 +2174,7 @@ static struct config_item *uvcg_frame_make(struct config_group *group, h->frame.dw_max_bit_rate = 55296000; h->frame.dw_max_video_frame_buffer_size = 460800; h->frame.dw_default_frame_interval = 666666; + h->frame.dw_bytes_perline = 0; opts_item = group->cg_item.ci_parent->ci_parent->ci_parent; opts = to_f_uvc_opts(opts_item); @@ -2157,6 +2187,9 @@ static struct config_item *uvcg_frame_make(struct config_group *group, } else if (fmt->type == UVCG_MJPEG) { h->frame.b_descriptor_subtype = UVC_VS_FRAME_MJPEG; h->fmt_type = UVCG_MJPEG; + } else if (fmt->type == UVCG_FRAMEBASED) { + h->frame.b_descriptor_subtype = UVC_VS_FRAME_FRAME_BASED; + h->fmt_type = UVCG_FRAMEBASED; } else { mutex_unlock(&opts->lock); kfree(h); @@ -2175,7 +2208,10 @@ static struct config_item *uvcg_frame_make(struct config_group *group, ++fmt->num_frames; mutex_unlock(&opts->lock); - config_item_init_type_name(&h->item, name, &uvcg_frame_type); + if (fmt->type == UVCG_FRAMEBASED) + config_item_init_type_name(&h->item, name, &uvcg_frame_type2); + else + config_item_init_type_name(&h->item, name, &uvcg_frame_type1); return &h->item; } @@ -2215,9 +2251,6 @@ static void uvcg_format_set_indices(struct config_group *fmt) list_for_each_entry(ci, &fmt->cg_children, ci_entry) { struct uvcg_frame *frm; - if (ci->ci_type != &uvcg_frame_type) - continue; - frm = to_uvcg_frame(ci); frm->frame.b_frame_index = i++; } @@ -2678,6 +2711,251 @@ static const struct uvcg_config_group_type uvcg_mjpeg_grp_type = { }; /* ----------------------------------------------------------------------------- + * streaming/framebased/<NAME> + */ + +static struct configfs_group_operations uvcg_framebased_group_ops = { + .make_item = uvcg_frame_make, + .drop_item = uvcg_frame_drop, +}; + +#define UVCG_FRAMEBASED_ATTR_RO(cname, aname, bits) \ +static ssize_t uvcg_framebased_##cname##_show(struct config_item *item, \ + char *page) \ +{ \ + struct uvcg_framebased *u = to_uvcg_framebased(item); \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%u\n", le##bits##_to_cpu(u->desc.aname));\ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + return result; \ +} \ + \ +UVC_ATTR_RO(uvcg_framebased_, cname, aname) + +#define UVCG_FRAMEBASED_ATTR(cname, aname, bits) \ +static ssize_t uvcg_framebased_##cname##_show(struct config_item *item, \ + char *page) \ +{ \ + struct uvcg_framebased *u = to_uvcg_framebased(item); \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%u\n", le##bits##_to_cpu(u->desc.aname));\ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + return result; \ +} \ + \ +static ssize_t \ +uvcg_framebased_##cname##_store(struct config_item *item, \ + const char *page, size_t len) \ +{ \ + struct uvcg_framebased *u = to_uvcg_framebased(item); \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ + int ret; \ + u8 num; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + if (u->fmt.linked || opts->refcnt) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + ret = kstrtou8(page, 0, &num); \ + if (ret) \ + goto end; \ + \ + if (num > 255) { \ + ret = -EINVAL; \ + goto end; \ + } \ + u->desc.aname = num; \ + ret = len; \ +end: \ + mutex_unlock(&opts->lock); \ + mutex_unlock(su_mutex); \ + return ret; \ +} \ + \ +UVC_ATTR(uvcg_framebased_, cname, aname) + +UVCG_FRAMEBASED_ATTR_RO(b_format_index, bFormatIndex, 8); +UVCG_FRAMEBASED_ATTR_RO(b_bits_per_pixel, bBitsPerPixel, 8); +UVCG_FRAMEBASED_ATTR(b_default_frame_index, bDefaultFrameIndex, 8); +UVCG_FRAMEBASED_ATTR_RO(b_aspect_ratio_x, bAspectRatioX, 8); +UVCG_FRAMEBASED_ATTR_RO(b_aspect_ratio_y, bAspectRatioY, 8); +UVCG_FRAMEBASED_ATTR_RO(bm_interface_flags, bmInterfaceFlags, 8); + +#undef UVCG_FRAMEBASED_ATTR +#undef UVCG_FRAMEBASED_ATTR_RO + +static ssize_t uvcg_framebased_guid_format_show(struct config_item *item, + char *page) +{ + struct uvcg_framebased *ch = to_uvcg_framebased(item); + struct f_uvc_opts *opts; + struct config_item *opts_item; + struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = ch->fmt.group.cg_item.ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + memcpy(page, ch->desc.guidFormat, sizeof(ch->desc.guidFormat)); + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + + return sizeof(ch->desc.guidFormat); +} + +static ssize_t uvcg_framebased_guid_format_store(struct config_item *item, + const char *page, size_t len) +{ + struct uvcg_framebased *ch = to_uvcg_framebased(item); + struct f_uvc_opts *opts; + struct config_item *opts_item; + struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex; + int ret; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = ch->fmt.group.cg_item.ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + if (ch->fmt.linked || opts->refcnt) { + ret = -EBUSY; + goto end; + } + + memcpy(ch->desc.guidFormat, page, + min(sizeof(ch->desc.guidFormat), len)); + ret = sizeof(ch->desc.guidFormat); + +end: + mutex_unlock(&opts->lock); + mutex_unlock(su_mutex); + return ret; +} + +UVC_ATTR(uvcg_framebased_, guid_format, guidFormat); + +static inline ssize_t +uvcg_framebased_bma_controls_show(struct config_item *item, char *page) +{ + struct uvcg_framebased *u = to_uvcg_framebased(item); + + return uvcg_format_bma_controls_show(&u->fmt, page); +} + +static inline ssize_t +uvcg_framebased_bma_controls_store(struct config_item *item, + const char *page, size_t len) +{ + struct uvcg_framebased *u = to_uvcg_framebased(item); + + return uvcg_format_bma_controls_store(&u->fmt, page, len); +} + +UVC_ATTR(uvcg_framebased_, bma_controls, bmaControls); + +static struct configfs_attribute *uvcg_framebased_attrs[] = { + &uvcg_framebased_attr_b_format_index, + &uvcg_framebased_attr_b_default_frame_index, + &uvcg_framebased_attr_b_bits_per_pixel, + &uvcg_framebased_attr_b_aspect_ratio_x, + &uvcg_framebased_attr_b_aspect_ratio_y, + &uvcg_framebased_attr_bm_interface_flags, + &uvcg_framebased_attr_bma_controls, + &uvcg_framebased_attr_guid_format, + NULL, +}; + +static const struct config_item_type uvcg_framebased_type = { + .ct_item_ops = &uvcg_config_item_ops, + .ct_group_ops = &uvcg_framebased_group_ops, + .ct_attrs = uvcg_framebased_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_group *uvcg_framebased_make(struct config_group *group, + const char *name) +{ + static char guid[] = { /*Declear frame based as H264 format*/ + 'H', '2', '6', '4', 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 + }; + struct uvcg_framebased *h; + + h = kzalloc(sizeof(*h), GFP_KERNEL); + if (!h) + return ERR_PTR(-ENOMEM); + + h->desc.bLength = UVC_DT_FORMAT_FRAMEBASED_SIZE; + h->desc.bDescriptorType = USB_DT_CS_INTERFACE; + h->desc.bDescriptorSubType = UVC_VS_FORMAT_FRAME_BASED; + memcpy(h->desc.guidFormat, guid, sizeof(guid)); + h->desc.bBitsPerPixel = 0; + h->desc.bDefaultFrameIndex = 1; + h->desc.bAspectRatioX = 0; + h->desc.bAspectRatioY = 0; + h->desc.bmInterfaceFlags = 0; + h->desc.bCopyProtect = 0; + h->desc.bVariableSize = 1; + + INIT_LIST_HEAD(&h->fmt.frames); + h->fmt.type = UVCG_FRAMEBASED; + config_group_init_type_name(&h->fmt.group, name, + &uvcg_framebased_type); + + return &h->fmt.group; +} + +static struct configfs_group_operations uvcg_framebased_grp_ops = { + .make_group = uvcg_framebased_make, +}; + +static const struct uvcg_config_group_type uvcg_framebased_grp_type = { + .type = { + .ct_item_ops = &uvcg_config_item_ops, + .ct_group_ops = &uvcg_framebased_grp_ops, + .ct_owner = THIS_MODULE, + }, + .name = "framebased", +}; + +/* ----------------------------------------------------------------------------- * streaming/color_matching/default */ @@ -2912,6 +3190,7 @@ static int __uvcg_iter_strm_cls(struct uvcg_streaming_header *h, if (ret) return ret; grp = &f->fmt->group; + j = 0; list_for_each_entry(item, &grp->cg_children, ci_entry) { frm = to_uvcg_frame(item); ret = fun(frm, priv2, priv3, j++, UVCG_FRAME); @@ -2965,6 +3244,11 @@ static int __uvcg_cnt_strm(void *priv1, void *priv2, void *priv3, int n, container_of(fmt, struct uvcg_mjpeg, fmt); *size += sizeof(m->desc); + } else if (fmt->type == UVCG_FRAMEBASED) { + struct uvcg_framebased *f = + container_of(fmt, struct uvcg_framebased, fmt); + + *size += sizeof(f->desc); } else { return -EINVAL; } @@ -2975,6 +3259,11 @@ static int __uvcg_cnt_strm(void *priv1, void *priv2, void *priv3, int n, int sz = sizeof(frm->dw_frame_interval); *size += sizeof(frm->frame); + /* + * framebased has duplicate member with uncompressed and + * mjpeg, so minus it + */ + *size -= sizeof(u32); *size += frm->frame.b_frame_interval_type * sz; } break; @@ -2991,6 +3280,27 @@ static int __uvcg_cnt_strm(void *priv1, void *priv2, void *priv3, int n, return 0; } +static int __uvcg_copy_framebased_desc(void *dest, struct uvcg_frame *frm, + int sz) +{ + struct uvc_frame_framebased *desc = dest; + + desc->bLength = frm->frame.b_length; + desc->bDescriptorType = frm->frame.b_descriptor_type; + desc->bDescriptorSubType = frm->frame.b_descriptor_subtype; + desc->bFrameIndex = frm->frame.b_frame_index; + desc->bmCapabilities = frm->frame.bm_capabilities; + desc->wWidth = frm->frame.w_width; + desc->wHeight = frm->frame.w_height; + desc->dwMinBitRate = frm->frame.dw_min_bit_rate; + desc->dwMaxBitRate = frm->frame.dw_max_bit_rate; + desc->dwDefaultFrameInterval = frm->frame.dw_default_frame_interval; + desc->bFrameIntervalType = frm->frame.b_frame_interval_type; + desc->dwBytesPerLine = frm->frame.dw_bytes_perline; + + return 0; +} + /* * Fill an array of streaming descriptors. * @@ -3045,6 +3355,15 @@ static int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n, m->desc.bNumFrameDescriptors = fmt->num_frames; memcpy(*dest, &m->desc, sizeof(m->desc)); *dest += sizeof(m->desc); + } else if (fmt->type == UVCG_FRAMEBASED) { + struct uvcg_framebased *f = + container_of(fmt, struct uvcg_framebased, + fmt); + + f->desc.bFormatIndex = n + 1; + f->desc.bNumFrameDescriptors = fmt->num_frames; + memcpy(*dest, &f->desc, sizeof(f->desc)); + *dest += sizeof(f->desc); } else { return -EINVAL; } @@ -3054,8 +3373,11 @@ static int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n, struct uvcg_frame *frm = priv1; struct uvc_descriptor_header *h = *dest; - sz = sizeof(frm->frame); - memcpy(*dest, &frm->frame, sz); + sz = sizeof(frm->frame) - 4; + if (frm->fmt_type != UVCG_FRAMEBASED) + memcpy(*dest, &frm->frame, sz); + else + __uvcg_copy_framebased_desc(*dest, frm, sz); *dest += sz; sz = frm->frame.b_frame_interval_type * sizeof(*frm->dw_frame_interval); @@ -3066,7 +3388,10 @@ static int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n, frm->frame.b_frame_interval_type); else if (frm->fmt_type == UVCG_MJPEG) h->bLength = UVC_DT_FRAME_MJPEG_SIZE( - frm->frame.b_frame_interval_type); + frm->frame.b_frame_interval_type); + else if (frm->fmt_type == UVCG_FRAMEBASED) + h->bLength = UVC_DT_FRAME_FRAMEBASED_SIZE( + frm->frame.b_frame_interval_type); } break; case UVCG_COLOR_MATCHING: { @@ -3285,6 +3610,7 @@ static const struct uvcg_config_group_type uvcg_streaming_grp_type = { &uvcg_streaming_header_grp_type, &uvcg_uncompressed_grp_type, &uvcg_mjpeg_grp_type, + &uvcg_framebased_grp_type, &uvcg_color_matching_grp_type, &uvcg_streaming_class_grp_type, NULL, diff --git a/drivers/usb/gadget/function/uvc_configfs.h b/drivers/usb/gadget/function/uvc_configfs.h index c6a690158138..2f78cd4f396f 100644 --- a/drivers/usb/gadget/function/uvc_configfs.h +++ b/drivers/usb/gadget/function/uvc_configfs.h @@ -49,6 +49,7 @@ container_of(group_ptr, struct uvcg_color_matching, group) enum uvcg_format_type { UVCG_UNCOMPRESSED = 0, UVCG_MJPEG, + UVCG_FRAMEBASED, }; struct uvcg_format { @@ -105,6 +106,7 @@ struct uvcg_frame { u32 dw_max_video_frame_buffer_size; u32 dw_default_frame_interval; u8 b_frame_interval_type; + u32 dw_bytes_perline; } __attribute__((packed)) frame; u32 *dw_frame_interval; }; @@ -143,6 +145,20 @@ static inline struct uvcg_mjpeg *to_uvcg_mjpeg(struct config_item *item) } /* ----------------------------------------------------------------------------- + * streaming/framebased/<NAME> + */ + +struct uvcg_framebased { + struct uvcg_format fmt; + struct uvc_format_framebased desc; +}; + +static inline struct uvcg_framebased *to_uvcg_framebased(struct config_item *item) +{ + return container_of(to_uvcg_format(item), struct uvcg_framebased, fmt); +} + +/* ----------------------------------------------------------------------------- * control/extensions/<NAME> */ diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c index 0aa3d7e1f3cc..5eaeae3e2441 100644 --- a/drivers/usb/gadget/function/uvc_queue.c +++ b/drivers/usb/gadget/function/uvc_queue.c @@ -21,6 +21,7 @@ #include <media/videobuf2-vmalloc.h> #include "uvc.h" +#include "uvc_video.h" /* ------------------------------------------------------------------------ * Video buffers queue management. @@ -44,33 +45,23 @@ static int uvc_queue_setup(struct vb2_queue *vq, { struct uvc_video_queue *queue = vb2_get_drv_priv(vq); struct uvc_video *video = container_of(queue, struct uvc_video, queue); - unsigned int req_size; - unsigned int nreq; if (*nbuffers > UVC_MAX_VIDEO_BUFFERS) *nbuffers = UVC_MAX_VIDEO_BUFFERS; + if (*nbuffers < UVCG_STREAMING_MIN_BUFFERS) + *nbuffers = UVCG_STREAMING_MIN_BUFFERS; *nplanes = 1; sizes[0] = video->imagesize; - req_size = video->ep->maxpacket - * max_t(unsigned int, video->ep->maxburst, 1) - * (video->ep->mult); - - /* We divide by two, to increase the chance to run - * into fewer requests for smaller framesizes. - */ - nreq = DIV_ROUND_UP(DIV_ROUND_UP(sizes[0], 2), req_size); - nreq = clamp(nreq, 4U, 64U); - video->uvc_num_requests = nreq; - return 0; } static int uvc_buffer_prepare(struct vb2_buffer *vb) { struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue); + struct uvc_video *video = container_of(queue, struct uvc_video, queue); struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct uvc_buffer *buf = container_of(vbuf, struct uvc_buffer, buf); @@ -91,10 +82,15 @@ static int uvc_buffer_prepare(struct vb2_buffer *vb) buf->mem = vb2_plane_vaddr(vb, 0); } buf->length = vb2_plane_size(vb, 0); - if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { buf->bytesused = 0; - else + } else { buf->bytesused = vb2_get_plane_payload(vb, 0); + buf->req_payload_size = + DIV_ROUND_UP(buf->bytesused + + (video->reqs_per_frame * UVCG_REQUEST_HEADER_LEN), + video->reqs_per_frame); + } return 0; } diff --git a/drivers/usb/gadget/function/uvc_queue.h b/drivers/usb/gadget/function/uvc_queue.h index 41f87b917f6b..b54becc570a3 100644 --- a/drivers/usb/gadget/function/uvc_queue.h +++ b/drivers/usb/gadget/function/uvc_queue.h @@ -39,6 +39,8 @@ struct uvc_buffer { unsigned int offset; unsigned int length; unsigned int bytesused; + /* req_payload_size: only used with isoc */ + unsigned int req_payload_size; }; #define UVC_QUEUE_DISCONNECTED (1 << 0) diff --git a/drivers/usb/gadget/function/uvc_trace.c b/drivers/usb/gadget/function/uvc_trace.c new file mode 100644 index 000000000000..d384f6d8221a --- /dev/null +++ b/drivers/usb/gadget/function/uvc_trace.c @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * trace.c - USB UVC Gadget Trace Support + * + * Copyright (C) 2024 Pengutronix e.K. + * + * Author: Michael Grzeschik <m.grzeschik@pengutronix.de> + */ + +#define CREATE_TRACE_POINTS +#include "uvc_trace.h" diff --git a/drivers/usb/gadget/function/uvc_trace.h b/drivers/usb/gadget/function/uvc_trace.h new file mode 100644 index 000000000000..04c33cf43cc2 --- /dev/null +++ b/drivers/usb/gadget/function/uvc_trace.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * trace.h - USB UVC Gadget Trace Support + * + * Copyright (C) 2024 Pengutronix e.K. + * + * Author: Michael Grzeschik <m.grzeschik@pengutronix.de> + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM uvcg + +#if !defined(__UVCG_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define __UVCG_TRACE_H + +#include <linux/types.h> +#include <linux/tracepoint.h> +#include <linux/usb/gadget.h> +#include <asm/byteorder.h> + +DECLARE_EVENT_CLASS(uvcg_video_req, + TP_PROTO(struct usb_request *req, u32 queued), + TP_ARGS(req, queued), + TP_STRUCT__entry( + __field(struct usb_request *, req) + __field(u32, length) + __field(u32, queued) + ), + TP_fast_assign( + __entry->req = req; + __entry->length = req->length; + __entry->queued = queued; + ), + TP_printk("req %p length %u queued %u", + __entry->req, + __entry->length, + __entry->queued) +); + +DEFINE_EVENT(uvcg_video_req, uvcg_video_complete, + TP_PROTO(struct usb_request *req, u32 queued), + TP_ARGS(req, queued) +); + +DEFINE_EVENT(uvcg_video_req, uvcg_video_queue, + TP_PROTO(struct usb_request *req, u32 queued), + TP_ARGS(req, queued) +); + +#endif /* __UVCG_TRACE_H */ + +/* this part has to be here */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE uvc_trace + +#include <trace/define_trace.h> diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index de1736f834e6..fc9a8d31a1e9 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -31,14 +31,23 @@ static const struct uvc_format_desc *to_uvc_format(struct uvcg_format *uformat) { char guid[16] = UVC_GUID_FORMAT_MJPEG; const struct uvc_format_desc *format; - struct uvcg_uncompressed *unc; if (uformat->type == UVCG_UNCOMPRESSED) { + struct uvcg_uncompressed *unc; + unc = to_uvcg_uncompressed(&uformat->group.cg_item); if (!unc) return ERR_PTR(-EINVAL); memcpy(guid, unc->desc.guidFormat, sizeof(guid)); + } else if (uformat->type == UVCG_FRAMEBASED) { + struct uvcg_framebased *unc; + + unc = to_uvcg_framebased(&uformat->group.cg_item); + if (!unc) + return ERR_PTR(-EINVAL); + + memcpy(guid, unc->desc.guidFormat, sizeof(guid)); } format = uvc_format_by_guid(guid); @@ -314,6 +323,56 @@ uvc_v4l2_set_format(struct file *file, void *fh, struct v4l2_format *fmt) return ret; } +static int uvc_v4l2_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *parm) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_video *video = &uvc->video; + struct v4l2_fract timeperframe; + + if (!V4L2_TYPE_IS_OUTPUT(parm->type)) + return -EINVAL; + + /* Return the actual frame period. */ + timeperframe.numerator = video->interval; + timeperframe.denominator = 10000000; + v4l2_simplify_fraction(&timeperframe.numerator, + &timeperframe.denominator, 8, 333); + + uvcg_dbg(&uvc->func, "Getting frame interval of %u/%u (%u)\n", + timeperframe.numerator, timeperframe.denominator, + video->interval); + + parm->parm.output.timeperframe = timeperframe; + parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + + return 0; +} + +static int uvc_v4l2_s_parm(struct file *file, void *fh, + struct v4l2_streamparm *parm) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_video *video = &uvc->video; + struct v4l2_fract timeperframe; + + if (!V4L2_TYPE_IS_OUTPUT(parm->type)) + return -EINVAL; + + timeperframe = parm->parm.output.timeperframe; + + video->interval = v4l2_fraction_to_interval(timeperframe.numerator, + timeperframe.denominator); + + uvcg_dbg(&uvc->func, "Setting frame interval to %u/%u (%u)\n", + timeperframe.numerator, timeperframe.denominator, + video->interval); + + return 0; +} + static int uvc_v4l2_enum_frameintervals(struct file *file, void *fh, struct v4l2_frmivalenum *fival) @@ -496,6 +555,9 @@ uvc_v4l2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) if (ret < 0) return ret; + if (uvc->state != UVC_STATE_STREAMING) + return 0; + uvc->state = UVC_STATE_CONNECTED; uvc_function_setup_continue(uvc, 1); return 0; @@ -587,6 +649,8 @@ const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops = { .vidioc_dqbuf = uvc_v4l2_dqbuf, .vidioc_streamon = uvc_v4l2_streamon, .vidioc_streamoff = uvc_v4l2_streamoff, + .vidioc_s_parm = uvc_v4l2_s_parm, + .vidioc_g_parm = uvc_v4l2_g_parm, .vidioc_subscribe_event = uvc_v4l2_subscribe_event, .vidioc_unsubscribe_event = uvc_v4l2_unsubscribe_event, .vidioc_default = uvc_v4l2_ioctl_default, diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index 57a851151225..23fad3bc72c0 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -19,6 +19,7 @@ #include "uvc.h" #include "uvc_queue.h" #include "uvc_video.h" +#include "uvc_trace.h" /* -------------------------------------------------------------------------- * Video codecs @@ -136,7 +137,7 @@ uvc_video_encode_isoc_sg(struct usb_request *req, struct uvc_video *video, unsigned int pending = buf->bytesused - video->queue.buf_used; struct uvc_request *ureq = req->context; struct scatterlist *sg, *iter; - unsigned int len = video->req_size; + unsigned int len = buf->req_payload_size; unsigned int sg_left, part = 0; unsigned int i; int header_len; @@ -146,15 +147,15 @@ uvc_video_encode_isoc_sg(struct usb_request *req, struct uvc_video *video, /* Init the header. */ header_len = uvc_video_encode_header(video, buf, ureq->header, - video->req_size); + buf->req_payload_size); sg_set_buf(sg, ureq->header, header_len); len -= header_len; if (pending <= len) len = pending; - req->length = (len == pending) ? - len + header_len : video->req_size; + req->length = (len == pending) ? len + header_len : + buf->req_payload_size; /* Init the pending sgs with payload */ sg = sg_next(sg); @@ -202,7 +203,7 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video, { void *mem = req->buf; struct uvc_request *ureq = req->context; - int len = video->req_size; + int len = buf->req_payload_size; int ret; /* Add the header. */ @@ -214,7 +215,7 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video, ret = uvc_video_encode_data(video, buf, mem, len); len -= ret; - req->length = video->req_size - len; + req->length = buf->req_payload_size - len; if (buf->bytesused == video->queue.buf_used || video->queue.flags & UVC_QUEUE_DROP_INCOMPLETE) { @@ -269,6 +270,10 @@ static int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req) } } + atomic_inc(&video->queued); + + trace_uvcg_video_queue(req, atomic_read(&video->queued)); + return ret; } @@ -304,7 +309,7 @@ static int uvcg_video_usb_req_queue(struct uvc_video *video, */ if (list_empty(&video->req_free) || ureq->last_buf || !(video->req_int_count % - DIV_ROUND_UP(video->uvc_num_requests, 4))) { + min(DIV_ROUND_UP(video->uvc_num_requests, 4), UVCG_REQ_MAX_INT_COUNT))) { video->req_int_count = 0; req->no_interrupt = 0; } else { @@ -322,50 +327,6 @@ static int uvcg_video_usb_req_queue(struct uvc_video *video, return 0; } -/* - * Must only be called from uvcg_video_enable - since after that we only want to - * queue requests to the endpoint from the uvc_video_complete complete handler. - * This function is needed in order to 'kick start' the flow of requests from - * gadget driver to the usb controller. - */ -static void uvc_video_ep_queue_initial_requests(struct uvc_video *video) -{ - struct usb_request *req = NULL; - unsigned long flags = 0; - unsigned int count = 0; - int ret = 0; - - /* - * We only queue half of the free list since we still want to have - * some free usb_requests in the free list for the video_pump async_wq - * thread to encode uvc buffers into. Otherwise we could get into a - * situation where the free list does not have any usb requests to - * encode into - we always end up queueing 0 length requests to the - * end point. - */ - unsigned int half_list_size = video->uvc_num_requests / 2; - - spin_lock_irqsave(&video->req_lock, flags); - /* - * Take these requests off the free list and queue them all to the - * endpoint. Since we queue 0 length requests with the req_lock held, - * there isn't any 'data' race involved here with the complete handler. - */ - while (count < half_list_size) { - req = list_first_entry(&video->req_free, struct usb_request, - list); - list_del(&req->list); - req->length = 0; - ret = uvcg_video_ep_queue(video, req); - if (ret < 0) { - uvcg_queue_cancel(&video->queue, 0); - break; - } - count++; - } - spin_unlock_irqrestore(&video->req_lock, flags); -} - static void uvc_video_complete(struct usb_ep *ep, struct usb_request *req) { @@ -373,12 +334,10 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) struct uvc_video *video = ureq->video; struct uvc_video_queue *queue = &video->queue; struct uvc_buffer *last_buf; - struct usb_request *to_queue = req; unsigned long flags; - bool is_bulk = video->max_payload_size; - int ret = 0; spin_lock_irqsave(&video->req_lock, flags); + atomic_dec(&video->queued); if (!video->is_enabled) { /* * When is_enabled is false, uvcg_video_disable() ensures @@ -438,51 +397,87 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) return; } + list_add_tail(&req->list, &video->req_free); /* - * Here we check whether any request is available in the ready - * list. If it is, queue it to the ep and add the current - * usb_request to the req_free list - for video_pump to fill in. - * Otherwise, just use the current usb_request to queue a 0 - * length request to the ep. Since we always add to the req_free - * list if we dequeue from the ready list, there will never - * be a situation where the req_free list is completely out of - * requests and cannot recover. + * Queue work to the wq as well since it is possible that a + * buffer may not have been completely encoded with the set of + * in-flight usb requests for whih the complete callbacks are + * firing. + * In that case, if we do not queue work to the worker thread, + * the buffer will never be marked as complete - and therefore + * not be returned to userpsace. As a result, + * dequeue -> queue -> dequeue flow of uvc buffers will not + * happen. Since there are is a new free request wake up the pump. */ - to_queue->length = 0; - if (!list_empty(&video->req_ready)) { - to_queue = list_first_entry(&video->req_ready, - struct usb_request, list); - list_del(&to_queue->list); - list_add_tail(&req->list, &video->req_free); + queue_work(video->async_wq, &video->pump); + + trace_uvcg_video_complete(req, atomic_read(&video->queued)); + + spin_unlock_irqrestore(&video->req_lock, flags); + + kthread_queue_work(video->kworker, &video->hw_submit); +} + +static void uvcg_video_hw_submit(struct kthread_work *work) +{ + struct uvc_video *video = container_of(work, struct uvc_video, hw_submit); + bool is_bulk = video->max_payload_size; + unsigned long flags; + struct usb_request *req; + int ret = 0; + + while (true) { + if (!video->ep->enabled) + return; + spin_lock_irqsave(&video->req_lock, flags); /* - * Queue work to the wq as well since it is possible that a - * buffer may not have been completely encoded with the set of - * in-flight usb requests for whih the complete callbacks are - * firing. - * In that case, if we do not queue work to the worker thread, - * the buffer will never be marked as complete - and therefore - * not be returned to userpsace. As a result, - * dequeue -> queue -> dequeue flow of uvc buffers will not - * happen. + * Here we check whether any request is available in the ready + * list. If it is, queue it to the ep and add the current + * usb_request to the req_free list - for video_pump to fill in. + * Otherwise, just use the current usb_request to queue a 0 + * length request to the ep. Since we always add to the req_free + * list if we dequeue from the ready list, there will never + * be a situation where the req_free list is completely out of + * requests and cannot recover. */ - queue_work(video->async_wq, &video->pump); - } - /* - * Queue to the endpoint. The actual queueing to ep will - * only happen on one thread - the async_wq for bulk endpoints - * and this thread for isoc endpoints. - */ - ret = uvcg_video_usb_req_queue(video, to_queue, !is_bulk); - if (ret < 0) { + if (!list_empty(&video->req_ready)) { + req = list_first_entry(&video->req_ready, + struct usb_request, list); + } else { + if (list_empty(&video->req_free) || + (atomic_read(&video->queued) > UVCG_REQ_MAX_ZERO_COUNT)) { + spin_unlock_irqrestore(&video->req_lock, flags); + + return; + } + req = list_first_entry(&video->req_free, struct usb_request, + list); + req->length = 0; + } + list_del(&req->list); + /* - * Endpoint error, but the stream is still enabled. - * Put request back in req_free for it to be cleaned - * up later. + * Queue to the endpoint. The actual queueing to ep will + * only happen on one thread - the async_wq for bulk endpoints + * and this thread for isoc endpoints. */ - list_add_tail(&to_queue->list, &video->req_free); - } + ret = uvcg_video_usb_req_queue(video, req, !is_bulk); + if (ret < 0) { + /* + * Endpoint error, but the stream is still enabled. + * Put request back in req_free for it to be cleaned + * up later. + */ + list_add_tail(&req->list, &video->req_free); + /* + * There is a new free request - wake up the pump. + */ + queue_work(video->async_wq, &video->pump); - spin_unlock_irqrestore(&video->req_lock, flags); + } + + spin_unlock_irqrestore(&video->req_lock, flags); + } } static int @@ -496,23 +491,71 @@ uvc_video_free_requests(struct uvc_video *video) INIT_LIST_HEAD(&video->ureqs); INIT_LIST_HEAD(&video->req_free); INIT_LIST_HEAD(&video->req_ready); - video->req_size = 0; return 0; } +static void +uvc_video_prep_requests(struct uvc_video *video) +{ + struct uvc_device *uvc = container_of(video, struct uvc_device, video); + struct usb_composite_dev *cdev = uvc->func.config->cdev; + unsigned int interval_duration = video->ep->desc->bInterval * 1250; + unsigned int max_req_size, req_size, header_size; + unsigned int nreq; + + max_req_size = video->ep->maxpacket + * max_t(unsigned int, video->ep->maxburst, 1) + * (video->ep->mult); + + if (!usb_endpoint_xfer_isoc(video->ep->desc)) { + video->req_size = max_req_size; + video->reqs_per_frame = video->uvc_num_requests = + DIV_ROUND_UP(video->imagesize, max_req_size); + + return; + } + + if (cdev->gadget->speed < USB_SPEED_HIGH) + interval_duration = video->ep->desc->bInterval * 10000; + + nreq = DIV_ROUND_UP(video->interval, interval_duration); + + header_size = nreq * UVCG_REQUEST_HEADER_LEN; + + req_size = DIV_ROUND_UP(video->imagesize + header_size, nreq); + + if (req_size > max_req_size) { + /* The prepared interval length and expected buffer size + * is not possible to stream with the currently configured + * isoc bandwidth. Fallback to the maximum. + */ + req_size = max_req_size; + } + video->req_size = req_size; + + /* We need to compensate the amount of requests to be + * allocated with the maximum amount of zero length requests. + * Since it is possible that hw_submit will initially + * enqueue some zero length requests and we then will not be + * able to fully encode one frame. + */ + video->uvc_num_requests = nreq + UVCG_REQ_MAX_ZERO_COUNT; + video->reqs_per_frame = nreq; +} + static int uvc_video_alloc_requests(struct uvc_video *video) { struct uvc_request *ureq; - unsigned int req_size; unsigned int i; int ret = -ENOMEM; - BUG_ON(video->req_size); - - req_size = video->ep->maxpacket - * max_t(unsigned int, video->ep->maxburst, 1) - * (video->ep->mult); + /* + * calculate in uvc_video_prep_requests + * - video->uvc_num_requests + * - video->req_size + */ + uvc_video_prep_requests(video); for (i = 0; i < video->uvc_num_requests; i++) { ureq = kzalloc(sizeof(struct uvc_request), GFP_KERNEL); @@ -523,7 +566,7 @@ uvc_video_alloc_requests(struct uvc_video *video) list_add_tail(&ureq->list, &video->ureqs); - ureq->req_buffer = kmalloc(req_size, GFP_KERNEL); + ureq->req_buffer = kmalloc(video->req_size, GFP_KERNEL); if (ureq->req_buffer == NULL) goto error; @@ -541,12 +584,10 @@ uvc_video_alloc_requests(struct uvc_video *video) list_add_tail(&ureq->req->list, &video->req_free); /* req_size/PAGE_SIZE + 1 for overruns and + 1 for header */ sg_alloc_table(&ureq->sgt, - DIV_ROUND_UP(req_size - UVCG_REQUEST_HEADER_LEN, + DIV_ROUND_UP(video->req_size - UVCG_REQUEST_HEADER_LEN, PAGE_SIZE) + 2, GFP_KERNEL); } - video->req_size = req_size; - return 0; error: @@ -699,7 +740,6 @@ uvcg_video_disable(struct uvc_video *video) INIT_LIST_HEAD(&video->ureqs); INIT_LIST_HEAD(&video->req_free); INIT_LIST_HEAD(&video->req_ready); - video->req_size = 0; spin_unlock_irqrestore(&video->req_lock, flags); /* @@ -752,7 +792,9 @@ int uvcg_video_enable(struct uvc_video *video) video->req_int_count = 0; - uvc_video_ep_queue_initial_requests(video); + atomic_set(&video->queued, 0); + + kthread_queue_work(video->kworker, &video->hw_submit); queue_work(video->async_wq, &video->pump); return ret; @@ -775,12 +817,24 @@ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc) if (!video->async_wq) return -EINVAL; + /* Allocate a kthread for asynchronous hw submit handler. */ + video->kworker = kthread_create_worker(0, "UVCG"); + if (IS_ERR(video->kworker)) { + uvcg_err(&video->uvc->func, "failed to create UVCG kworker\n"); + return PTR_ERR(video->kworker); + } + + kthread_init_work(&video->hw_submit, uvcg_video_hw_submit); + + sched_set_fifo(video->kworker->task); + video->uvc = uvc; video->fcc = V4L2_PIX_FMT_YUYV; video->bpp = 16; video->width = 320; video->height = 240; video->imagesize = 320 * 240 * 2; + video->interval = 666666; /* Initialize the video buffers queue. */ uvcg_queue_init(&video->queue, uvc->v4l2_dev.dev->parent, diff --git a/drivers/usb/gadget/legacy/hid.c b/drivers/usb/gadget/legacy/hid.c index 4ca67b2f8f24..3684546e8801 100644 --- a/drivers/usb/gadget/legacy/hid.c +++ b/drivers/usb/gadget/legacy/hid.c @@ -261,7 +261,7 @@ static struct usb_composite_driver hidg_driver = { }; static struct platform_driver hidg_plat_driver = { - .remove_new = hidg_plat_driver_remove, + .remove = hidg_plat_driver_remove, .driver = { .name = "hidg", }, diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/aspeed-vhub/core.c index f60a019bb173..f2685f89b3e5 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/core.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c @@ -428,7 +428,7 @@ MODULE_DEVICE_TABLE(of, ast_vhub_dt_ids); static struct platform_driver ast_vhub_driver = { .probe = ast_vhub_probe, - .remove_new = ast_vhub_remove, + .remove = ast_vhub_remove, .driver = { .name = KBUILD_MODNAME, .of_match_table = ast_vhub_dt_ids, diff --git a/drivers/usb/gadget/udc/aspeed_udc.c b/drivers/usb/gadget/udc/aspeed_udc.c index f4781e611aaa..353bfb1ff0a1 100644 --- a/drivers/usb/gadget/udc/aspeed_udc.c +++ b/drivers/usb/gadget/udc/aspeed_udc.c @@ -156,7 +156,7 @@ #define AST_EP_DMA_DESC_PID_DATA1 (2 << 14) #define AST_EP_DMA_DESC_PID_MDATA (3 << 14) #define EP_DESC1_IN_LEN(x) ((x) & 0x1fff) -#define AST_EP_DMA_DESC_MAX_LEN (7680) /* Max packet length for trasmit in 1 desc */ +#define AST_EP_DMA_DESC_MAX_LEN (7680) /* Max packet length for transmit in 1 desc */ struct ast_udc_request { struct usb_request req; @@ -1590,7 +1590,7 @@ MODULE_DEVICE_TABLE(of, ast_udc_of_dt_ids); static struct platform_driver ast_udc_driver = { .probe = ast_udc_probe, - .remove_new = ast_udc_remove, + .remove = ast_udc_remove, .driver = { .name = KBUILD_MODNAME, .of_match_table = ast_udc_of_dt_ids, diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c index e3bf17a98b38..e3af4ec3794e 100644 --- a/drivers/usb/gadget/udc/at91_udc.c +++ b/drivers/usb/gadget/udc/at91_udc.c @@ -2002,7 +2002,7 @@ static int at91udc_resume(struct platform_device *pdev) static struct platform_driver at91_udc_driver = { .probe = at91udc_probe, - .remove_new = at91udc_remove, + .remove = at91udc_remove, .shutdown = at91udc_shutdown, .suspend = at91udc_suspend, .resume = at91udc_resume, diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 4928eba19327..0c6f2ad81d37 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -2444,7 +2444,7 @@ static SIMPLE_DEV_PM_OPS(usba_udc_pm_ops, usba_udc_suspend, usba_udc_resume); static struct platform_driver udc_driver = { .probe = usba_udc_probe, - .remove_new = usba_udc_remove, + .remove = usba_udc_remove, .driver = { .name = "atmel_usba_udc", .pm = &usba_udc_pm_ops, diff --git a/drivers/usb/gadget/udc/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c index da7011d906e0..502612a5650e 100644 --- a/drivers/usb/gadget/udc/bcm63xx_udc.c +++ b/drivers/usb/gadget/udc/bcm63xx_udc.c @@ -2367,7 +2367,7 @@ static void bcm63xx_udc_remove(struct platform_device *pdev) static struct platform_driver bcm63xx_udc_driver = { .probe = bcm63xx_udc_probe, - .remove_new = bcm63xx_udc_remove, + .remove = bcm63xx_udc_remove, .driver = { .name = DRV_MODULE_NAME, }, diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c index 5149e2b7f050..5c3d8b64c0e7 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_core.c +++ b/drivers/usb/gadget/udc/bdc/bdc_core.c @@ -648,7 +648,7 @@ static struct platform_driver bdc_driver = { .of_match_table = bdc_of_match, }, .probe = bdc_probe, - .remove_new = bdc_remove, + .remove = bdc_remove, }; module_platform_driver(bdc_driver); diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index 081ac7683c0b..a7e8fa45776b 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -80,7 +80,7 @@ module_param_named(num, mod_data.num, uint, S_IRUGO); MODULE_PARM_DESC(num, "number of emulated controllers"); /*-------------------------------------------------------------------------*/ -/* gadget side driver data structres */ +/* gadget side driver data structures */ struct dummy_ep { struct list_head queue; unsigned long last_io; /* jiffies timestamp */ @@ -1152,7 +1152,7 @@ static int dummy_udc_resume(struct platform_device *pdev) static struct platform_driver dummy_udc_driver = { .probe = dummy_udc_probe, - .remove_new = dummy_udc_remove, + .remove = dummy_udc_remove, .suspend = dummy_udc_suspend, .resume = dummy_udc_resume, .driver = { @@ -2769,7 +2769,7 @@ static int dummy_hcd_resume(struct platform_device *pdev) static struct platform_driver dummy_hcd_driver = { .probe = dummy_hcd_probe, - .remove_new = dummy_hcd_remove, + .remove = dummy_hcd_remove, .suspend = dummy_hcd_suspend, .resume = dummy_hcd_resume, .driver = { diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c index 4e88681a79b6..aacfde06387c 100644 --- a/drivers/usb/gadget/udc/fsl_qe_udc.c +++ b/drivers/usb/gadget/udc/fsl_qe_udc.c @@ -511,7 +511,7 @@ static int qe_ep_register_init(struct qe_udc *udc, unsigned char pipe_num) out_8(&epparam->tbmr, rtfcr); tmp = (u16)(ep->ep.maxpacket + USB_CRC_SIZE); - /* MRBLR must be divisble by 4 */ + /* MRBLR must be divisible by 4 */ tmp = (u16)(((tmp >> 2) << 2) + 4); out_be16(&epparam->mrblr, tmp); @@ -1413,7 +1413,7 @@ static int ep_txframe_handle(struct qe_ep *ep) return 0; } -/* confirm the already trainsmited bd */ +/* confirm the already transmitted bd */ static int qe_ep_txconf(struct qe_ep *ep) { struct qe_bd __iomem *bd; @@ -2196,7 +2196,7 @@ static int tx_irq(struct qe_udc *udc) } -/* setup packect's rx is handle in the function too */ +/* setup packet's rx is handle in the function too */ static void rx_irq(struct qe_udc *udc) { struct qe_ep *ep; @@ -2704,7 +2704,7 @@ static struct platform_driver udc_driver = { .of_match_table = qe_udc_match, }, .probe = qe_udc_probe, - .remove_new = qe_udc_remove, + .remove = qe_udc_remove, #ifdef CONFIG_PM .suspend = qe_udc_suspend, .resume = qe_udc_resume, diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index 0cabd4eee6ac..8b7f7f961774 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -2685,7 +2685,7 @@ MODULE_DEVICE_TABLE(of, fsl_udc_dt_ids); static struct platform_driver udc_driver = { .probe = fsl_udc_probe, - .remove_new = fsl_udc_remove, + .remove = fsl_udc_remove, .id_table = fsl_udc_devtype, /* these suspend and resume are not usb suspend and resume */ .suspend = fsl_udc_suspend, diff --git a/drivers/usb/gadget/udc/fusb300_udc.c b/drivers/usb/gadget/udc/fusb300_udc.c index 873265634ccc..5e94a99b3e53 100644 --- a/drivers/usb/gadget/udc/fusb300_udc.c +++ b/drivers/usb/gadget/udc/fusb300_udc.c @@ -1297,7 +1297,7 @@ static void init_controller(struct fusb300 *fusb300) reg |= val; iowrite32(reg, fusb300->reg + FUSB300_OFFSET_HSCR); - /*set u1 u2 timmer*/ + /*set u1 u2 timer*/ fusb300_set_u2_timeout(fusb300, 0xff); fusb300_set_u1_timeout(fusb300, 0xff); @@ -1507,7 +1507,7 @@ clean_up: static struct platform_driver fusb300_driver = { .probe = fusb300_probe, - .remove_new = fusb300_remove, + .remove = fusb300_remove, .driver = { .name = udc_name, }, diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c index fb901be5dac1..bf5b3c964c18 100644 --- a/drivers/usb/gadget/udc/gr_udc.c +++ b/drivers/usb/gadget/udc/gr_udc.c @@ -2249,7 +2249,7 @@ static struct platform_driver gr_driver = { .of_match_table = gr_match, }, .probe = gr_probe, - .remove_new = gr_remove, + .remove = gr_remove, }; module_platform_driver(gr_driver); diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c index 3bfd889ed56a..89d6daf2bda7 100644 --- a/drivers/usb/gadget/udc/lpc32xx_udc.c +++ b/drivers/usb/gadget/udc/lpc32xx_udc.c @@ -3249,7 +3249,7 @@ MODULE_DEVICE_TABLE(of, lpc32xx_udc_of_match); static struct platform_driver lpc32xx_udc_driver = { .probe = lpc32xx_udc_probe, - .remove_new = lpc32xx_udc_remove, + .remove = lpc32xx_udc_remove, .shutdown = lpc32xx_udc_shutdown, .suspend = lpc32xx_udc_suspend, .resume = lpc32xx_udc_resume, diff --git a/drivers/usb/gadget/udc/m66592-udc.c b/drivers/usb/gadget/udc/m66592-udc.c index bfaa5291e6c8..a938b2af0944 100644 --- a/drivers/usb/gadget/udc/m66592-udc.c +++ b/drivers/usb/gadget/udc/m66592-udc.c @@ -1688,7 +1688,7 @@ clean_up: /*-------------------------------------------------------------------------*/ static struct platform_driver m66592_driver = { .probe = m66592_probe, - .remove_new = m66592_remove, + .remove = m66592_remove, .driver = { .name = udc_name, }, diff --git a/drivers/usb/gadget/udc/mv_u3d_core.c b/drivers/usb/gadget/udc/mv_u3d_core.c index e1dd5cf25d08..062f43e146aa 100644 --- a/drivers/usb/gadget/udc/mv_u3d_core.c +++ b/drivers/usb/gadget/udc/mv_u3d_core.c @@ -2047,7 +2047,7 @@ static void mv_u3d_shutdown(struct platform_device *dev) static struct platform_driver mv_u3d_driver = { .probe = mv_u3d_probe, - .remove_new = mv_u3d_remove, + .remove = mv_u3d_remove, .shutdown = mv_u3d_shutdown, .driver = { .name = "mv-u3d", diff --git a/drivers/usb/gadget/udc/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c index 71012b282891..ff103e6b3048 100644 --- a/drivers/usb/gadget/udc/mv_udc_core.c +++ b/drivers/usb/gadget/udc/mv_udc_core.c @@ -2409,7 +2409,7 @@ static void mv_udc_shutdown(struct platform_device *pdev) static struct platform_driver udc_driver = { .probe = mv_udc_probe, - .remove_new = mv_udc_remove, + .remove = mv_udc_remove, .shutdown = mv_udc_shutdown, .driver = { .name = "mv-udc", diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c index 9230db57dab7..7ecddbf5c90d 100644 --- a/drivers/usb/gadget/udc/net2272.c +++ b/drivers/usb/gadget/udc/net2272.c @@ -2097,7 +2097,7 @@ static irqreturn_t net2272_irq(int irq, void *_dev) } /* check dma interrupts */ #endif - /* Platform/devcice interrupt handler */ + /* Platform/device interrupt handler */ #if !defined(PLX_PCI_RDK) net2272_handle_stat1_irqs(dev, net2272_read(dev, IRQSTAT1)); net2272_handle_stat0_irqs(dev, net2272_read(dev, IRQSTAT0)); @@ -2685,7 +2685,7 @@ net2272_plat_remove(struct platform_device *pdev) static struct platform_driver net2272_plat_driver = { .probe = net2272_plat_probe, - .remove_new = net2272_plat_remove, + .remove = net2272_plat_remove, .driver = { .name = driver_name, }, diff --git a/drivers/usb/gadget/udc/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c index 61a45e4657d5..cd3d9bd69708 100644 --- a/drivers/usb/gadget/udc/omap_udc.c +++ b/drivers/usb/gadget/udc/omap_udc.c @@ -2980,7 +2980,7 @@ static int omap_udc_resume(struct platform_device *dev) static struct platform_driver udc_driver = { .probe = omap_udc_probe, - .remove_new = omap_udc_remove, + .remove = omap_udc_remove, .suspend = omap_udc_suspend, .resume = omap_udc_resume, .driver = { diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c index 7c96fc9f680f..1e9998024aaa 100644 --- a/drivers/usb/gadget/udc/pxa25x_udc.c +++ b/drivers/usb/gadget/udc/pxa25x_udc.c @@ -2474,7 +2474,7 @@ static int pxa25x_udc_resume(struct platform_device *dev) static struct platform_driver udc_driver = { .shutdown = pxa25x_udc_shutdown, .probe = pxa25x_udc_probe, - .remove_new = pxa25x_udc_remove, + .remove = pxa25x_udc_remove, .suspend = pxa25x_udc_suspend, .resume = pxa25x_udc_resume, .driver = { diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index 1a6317e4b2a3..7d3a8a5f3796 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -2538,7 +2538,7 @@ static struct platform_driver udc_driver = { .of_match_table = of_match_ptr(udc_pxa_dt_ids), }, .probe = pxa_udc_probe, - .remove_new = pxa_udc_remove, + .remove = pxa_udc_remove, .shutdown = pxa_udc_shutdown, #ifdef CONFIG_PM .suspend = pxa_udc_suspend, diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c index db4a10a979f9..ff6f846b1d41 100644 --- a/drivers/usb/gadget/udc/r8a66597-udc.c +++ b/drivers/usb/gadget/udc/r8a66597-udc.c @@ -1965,7 +1965,7 @@ clean_up2: /*-------------------------------------------------------------------------*/ static struct platform_driver r8a66597_driver = { .probe = r8a66597_probe, - .remove_new = r8a66597_remove, + .remove = r8a66597_remove, .driver = { .name = udc_name, }, diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index 3b01734ce1b7..fce5c41d9f29 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -3013,7 +3013,7 @@ static SIMPLE_DEV_PM_OPS(renesas_usb3_pm_ops, renesas_usb3_suspend, static struct platform_driver renesas_usb3_driver = { .probe = renesas_usb3_probe, - .remove_new = renesas_usb3_remove, + .remove = renesas_usb3_remove, .driver = { .name = udc_name, .pm = &renesas_usb3_pm_ops, diff --git a/drivers/usb/gadget/udc/renesas_usbf.c b/drivers/usb/gadget/udc/renesas_usbf.c index 657f265ac7cc..14f4b2cf05a4 100644 --- a/drivers/usb/gadget/udc/renesas_usbf.c +++ b/drivers/usb/gadget/udc/renesas_usbf.c @@ -2482,7 +2482,7 @@ static int usbf_handle_ep0_setup(struct usbf_ep *ep0) ep0->delayed_status = 0; if ((crq.ctrlreq.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) { - /* This is not a USB standard request -> delelate */ + /* This is not a USB standard request -> delegate */ goto delegate; } @@ -3381,7 +3381,7 @@ static struct platform_driver udc_driver = { .of_match_table = usbf_match, }, .probe = usbf_probe, - .remove_new = usbf_remove, + .remove = usbf_remove, }; module_platform_driver(udc_driver); diff --git a/drivers/usb/gadget/udc/rzv2m_usb3drd.c b/drivers/usb/gadget/udc/rzv2m_usb3drd.c index 36f4ff00d22f..4692eae89f44 100644 --- a/drivers/usb/gadget/udc/rzv2m_usb3drd.c +++ b/drivers/usb/gadget/udc/rzv2m_usb3drd.c @@ -127,7 +127,7 @@ static struct platform_driver rzv2m_usb3drd_driver = { .of_match_table = rzv2m_usb3drd_of_match, }, .probe = rzv2m_usb3drd_probe, - .remove_new = rzv2m_usb3drd_remove, + .remove = rzv2m_usb3drd_remove, }; module_platform_driver(rzv2m_usb3drd_driver); diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c index cd89532adec2..1f8a99d2a643 100644 --- a/drivers/usb/gadget/udc/snps_udc_core.c +++ b/drivers/usb/gadget/udc/snps_udc_core.c @@ -2707,7 +2707,7 @@ static irqreturn_t udc_control_in_isr(struct udc *dev) /* write fifo */ udc_txfifo_write(ep, &req->req); - /* lengh bytes transferred */ + /* length bytes transferred */ len = req->req.length - req->req.actual; if (len > ep->ep.maxpacket) len = ep->ep.maxpacket; diff --git a/drivers/usb/gadget/udc/snps_udc_plat.c b/drivers/usb/gadget/udc/snps_udc_plat.c index ba5a06690507..db842a6de643 100644 --- a/drivers/usb/gadget/udc/snps_udc_plat.c +++ b/drivers/usb/gadget/udc/snps_udc_plat.c @@ -309,7 +309,7 @@ MODULE_DEVICE_TABLE(of, of_udc_match); static struct platform_driver udc_plat_driver = { .probe = udc_plat_probe, - .remove_new = udc_plat_remove, + .remove = udc_plat_remove, .driver = { .name = "snps-udc-plat", .of_match_table = of_udc_match, diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c index 7aa46d426f31..c7fdbc55fb0b 100644 --- a/drivers/usb/gadget/udc/tegra-xudc.c +++ b/drivers/usb/gadget/udc/tegra-xudc.c @@ -4071,7 +4071,7 @@ static const struct dev_pm_ops tegra_xudc_pm_ops = { static struct platform_driver tegra_xudc_driver = { .probe = tegra_xudc_probe, - .remove_new = tegra_xudc_remove, + .remove = tegra_xudc_remove, .driver = { .name = "tegra-xudc", .pm = &tegra_xudc_pm_ops, diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c index ebc45565c33e..ae2aeb271897 100644 --- a/drivers/usb/gadget/udc/udc-xilinx.c +++ b/drivers/usb/gadget/udc/udc-xilinx.c @@ -2258,7 +2258,7 @@ static struct platform_driver xudc_driver = { .pm = &xudc_pm_ops, }, .probe = xudc_probe, - .remove_new = xudc_remove, + .remove = xudc_remove, }; module_platform_driver(xudc_driver); diff --git a/drivers/usb/host/bcma-hcd.c b/drivers/usb/host/bcma-hcd.c index 7558cc4d90cc..519386255886 100644 --- a/drivers/usb/host/bcma-hcd.c +++ b/drivers/usb/host/bcma-hcd.c @@ -25,7 +25,6 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/of_platform.h> #include <linux/usb/ehci_pdriver.h> #include <linux/usb/ohci_pdriver.h> diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c index 6a6e1c510b28..65747270fd88 100644 --- a/drivers/usb/host/ehci-atmel.c +++ b/drivers/usb/host/ehci-atmel.c @@ -220,7 +220,7 @@ static SIMPLE_DEV_PM_OPS(ehci_atmel_pm_ops, ehci_atmel_drv_suspend, static struct platform_driver ehci_atmel_driver = { .probe = ehci_atmel_drv_probe, - .remove_new = ehci_atmel_drv_remove, + .remove = ehci_atmel_drv_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "atmel-ehci", diff --git a/drivers/usb/host/ehci-brcm.c b/drivers/usb/host/ehci-brcm.c index 68cad0620f1a..888e8f6670d2 100644 --- a/drivers/usb/host/ehci-brcm.c +++ b/drivers/usb/host/ehci-brcm.c @@ -250,7 +250,7 @@ MODULE_DEVICE_TABLE(of, brcm_ehci_of_match); static struct platform_driver ehci_brcm_driver = { .probe = ehci_brcm_probe, - .remove_new = ehci_brcm_remove, + .remove = ehci_brcm_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "ehci-brcm", diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c index e3a961d3f5fc..d2a5bedf736a 100644 --- a/drivers/usb/host/ehci-exynos.c +++ b/drivers/usb/host/ehci-exynos.c @@ -288,7 +288,7 @@ MODULE_DEVICE_TABLE(of, exynos_ehci_match); static struct platform_driver exynos_ehci_driver = { .probe = exynos_ehci_probe, - .remove_new = exynos_ehci_remove, + .remove = exynos_ehci_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "exynos-ehci", diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 5b1ce394a417..26f13278d4d6 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -706,7 +706,7 @@ static void fsl_ehci_drv_remove(struct platform_device *pdev) static struct platform_driver ehci_fsl_driver = { .probe = fsl_ehci_drv_probe, - .remove_new = fsl_ehci_drv_remove, + .remove = fsl_ehci_drv_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = DRV_NAME, diff --git a/drivers/usb/host/ehci-grlib.c b/drivers/usb/host/ehci-grlib.c index 14150e4d3382..bd9762eaa135 100644 --- a/drivers/usb/host/ehci-grlib.c +++ b/drivers/usb/host/ehci-grlib.c @@ -168,7 +168,7 @@ MODULE_DEVICE_TABLE(of, ehci_hcd_grlib_of_match); static struct platform_driver ehci_grlib_driver = { .probe = ehci_hcd_grlib_probe, - .remove_new = ehci_hcd_grlib_remove, + .remove = ehci_hcd_grlib_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "grlib-ehci", diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index 2f1fc7eb8b72..cbabbe107172 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -279,7 +279,7 @@ static const struct of_device_id ehci_mv_dt_ids[] = { static struct platform_driver ehci_mv_driver = { .probe = mv_ehci_probe, - .remove_new = mv_ehci_remove, + .remove = mv_ehci_remove, .shutdown = mv_ehci_shutdown, .driver = { .name = "mv-ehci", diff --git a/drivers/usb/host/ehci-npcm7xx.c b/drivers/usb/host/ehci-npcm7xx.c index 3d3317a1a0b3..f1c7034c1e80 100644 --- a/drivers/usb/host/ehci-npcm7xx.c +++ b/drivers/usb/host/ehci-npcm7xx.c @@ -122,7 +122,7 @@ MODULE_DEVICE_TABLE(of, npcm7xx_ehci_id_table); static struct platform_driver npcm7xx_ehci_hcd_driver = { .probe = npcm7xx_ehci_hcd_drv_probe, - .remove_new = npcm7xx_ehci_hcd_drv_remove, + .remove = npcm7xx_ehci_hcd_drv_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "npcm7xx-ehci", diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index b24f371a46f3..db4a1acb27da 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -264,7 +264,7 @@ MODULE_DEVICE_TABLE(of, omap_ehci_dt_ids); static struct platform_driver ehci_hcd_omap_driver = { .probe = ehci_hcd_omap_probe, - .remove_new = ehci_hcd_omap_remove, + .remove = ehci_hcd_omap_remove, .shutdown = usb_hcd_platform_shutdown, /*.suspend = ehci_hcd_omap_suspend, */ /*.resume = ehci_hcd_omap_resume, */ diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index ad145a54ca74..34abff8669f8 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -352,7 +352,7 @@ MODULE_DEVICE_TABLE(of, ehci_orion_dt_ids); static struct platform_driver ehci_orion_driver = { .probe = ehci_orion_drv_probe, - .remove_new = ehci_orion_drv_remove, + .remove = ehci_orion_drv_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "orion-ehci", diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index 98b073185e1c..cdf41886e8ca 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -508,7 +508,7 @@ static SIMPLE_DEV_PM_OPS(ehci_platform_pm_ops, ehci_platform_suspend, static struct platform_driver ehci_platform_driver = { .id_table = ehci_platform_table, .probe = ehci_platform_probe, - .remove_new = ehci_platform_remove, + .remove = ehci_platform_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "ehci-platform", diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c index 7fd83e806ae4..8063b9d3aebd 100644 --- a/drivers/usb/host/ehci-ppc-of.c +++ b/drivers/usb/host/ehci-ppc-of.c @@ -230,7 +230,7 @@ MODULE_DEVICE_TABLE(of, ehci_hcd_ppc_of_match); static struct platform_driver ehci_hcd_ppc_of_driver = { .probe = ehci_hcd_ppc_of_probe, - .remove_new = ehci_hcd_ppc_of_remove, + .remove = ehci_hcd_ppc_of_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "ppc-of-ehci", diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c index d31d9506e41a..5d0d972fb7b1 100644 --- a/drivers/usb/host/ehci-sh.c +++ b/drivers/usb/host/ehci-sh.c @@ -169,7 +169,7 @@ static void ehci_hcd_sh_shutdown(struct platform_device *pdev) static struct platform_driver ehci_hcd_sh_driver = { .probe = ehci_hcd_sh_probe, - .remove_new = ehci_hcd_sh_remove, + .remove = ehci_hcd_sh_remove, .shutdown = ehci_hcd_sh_shutdown, .driver = { .name = "sh_ehci", diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c index d0e94e4c9fe2..0b1fc2563dd6 100644 --- a/drivers/usb/host/ehci-spear.c +++ b/drivers/usb/host/ehci-spear.c @@ -143,7 +143,7 @@ MODULE_DEVICE_TABLE(of, spear_ehci_id_table); static struct platform_driver spear_ehci_hcd_driver = { .probe = spear_ehci_hcd_drv_probe, - .remove_new = spear_ehci_hcd_drv_remove, + .remove = spear_ehci_hcd_drv_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "spear-ehci", diff --git a/drivers/usb/host/ehci-st.c b/drivers/usb/host/ehci-st.c index 2dbb0d86daaa..58867d816af7 100644 --- a/drivers/usb/host/ehci-st.c +++ b/drivers/usb/host/ehci-st.c @@ -320,7 +320,7 @@ MODULE_DEVICE_TABLE(of, st_ehci_ids); static struct platform_driver ehci_platform_driver = { .probe = st_ehci_platform_probe, - .remove_new = st_ehci_platform_remove, + .remove = st_ehci_platform_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "st-ehci", diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c index a2112c28f631..1d16cfefabd7 100644 --- a/drivers/usb/host/ehci-xilinx-of.c +++ b/drivers/usb/host/ehci-xilinx-of.c @@ -220,7 +220,7 @@ MODULE_DEVICE_TABLE(of, ehci_hcd_xilinx_of_match); static struct platform_driver ehci_hcd_xilinx_of_driver = { .probe = ehci_hcd_xilinx_of_probe, - .remove_new = ehci_hcd_xilinx_of_remove, + .remove = ehci_hcd_xilinx_of_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "xilinx-of-ehci", diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c index 9a1b5224f239..22a0942f0bce 100644 --- a/drivers/usb/host/fhci-hcd.c +++ b/drivers/usb/host/fhci-hcd.c @@ -791,7 +791,7 @@ static struct platform_driver of_fhci_driver = { .of_match_table = of_fhci_match, }, .probe = of_fhci_probe, - .remove_new = of_fhci_remove, + .remove = of_fhci_remove, }; module_platform_driver(of_fhci_driver); diff --git a/drivers/usb/host/fhci-sched.c b/drivers/usb/host/fhci-sched.c index a45ede80edfc..c3acd410ce94 100644 --- a/drivers/usb/host/fhci-sched.c +++ b/drivers/usb/host/fhci-sched.c @@ -158,7 +158,7 @@ static int add_packet(struct fhci_usb *usb, struct ed *ed, struct td *td) struct packet *pkt; u8 *data = NULL; - /* calcalate data address,len and toggle and then add the transaction */ + /* calculate data address,len and toggle and then add the transaction */ if (td->toggle == USB_TD_TOGGLE_CARRY) td->toggle = ed->toggle_carry; @@ -679,7 +679,7 @@ static void process_done_list(unsigned long data) DECLARE_TASKLET_OLD(fhci_tasklet, process_done_list); -/* transfer complted callback */ +/* transfer completed callback */ u32 fhci_transfer_confirm_callback(struct fhci_hcd *fhci) { if (!fhci->process_done_task->state) diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c index 6cdc3d805c32..4e67b9471986 100644 --- a/drivers/usb/host/fsl-mph-dr-of.c +++ b/drivers/usb/host/fsl-mph-dr-of.c @@ -362,7 +362,7 @@ static struct platform_driver fsl_usb2_mph_dr_driver = { .of_match_table = fsl_usb2_mph_dr_of_match, }, .probe = fsl_usb2_mph_dr_of_probe, - .remove_new = fsl_usb2_mph_dr_of_remove, + .remove = fsl_usb2_mph_dr_of_remove, }; module_platform_driver(fsl_usb2_mph_dr_driver); diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index a82d8926e922..71c22c4bd163 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -1684,7 +1684,7 @@ MODULE_ALIAS("platform:isp116x-hcd"); static struct platform_driver isp116x_driver = { .probe = isp116x_probe, - .remove_new = isp116x_remove, + .remove = isp116x_remove, .suspend = isp116x_suspend, .resume = isp116x_resume, .driver = { diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c index 31059c8f94e6..2d3a082cb52f 100644 --- a/drivers/usb/host/isp1362-hcd.c +++ b/drivers/usb/host/isp1362-hcd.c @@ -2757,7 +2757,7 @@ static int isp1362_resume(struct platform_device *pdev) static struct platform_driver isp1362_driver = { .probe = isp1362_probe, - .remove_new = isp1362_remove, + .remove = isp1362_remove, .suspend = isp1362_suspend, .resume = isp1362_resume, diff --git a/drivers/usb/host/octeon-hcd.c b/drivers/usb/host/octeon-hcd.c index 19d5777f5db2..361d33b0c4d2 100644 --- a/drivers/usb/host/octeon-hcd.c +++ b/drivers/usb/host/octeon-hcd.c @@ -3346,7 +3346,7 @@ static int octeon_usb_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, break; case USB_PORT_FEAT_INDICATOR: dev_dbg(dev, " INDICATOR\n"); - /* Port inidicator not supported */ + /* Port indicator not supported */ break; case USB_PORT_FEAT_C_CONNECTION: dev_dbg(dev, " C_CONNECTION\n"); @@ -3711,8 +3711,8 @@ static struct platform_driver octeon_usb_driver = { .name = "octeon-hcd", .of_match_table = octeon_usb_match, }, - .probe = octeon_usb_probe, - .remove_new = octeon_usb_remove, + .probe = octeon_usb_probe, + .remove = octeon_usb_remove, }; static int __init octeon_usb_driver_init(void) diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index f691cd98a574..5df793dcb25d 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -685,7 +685,7 @@ static SIMPLE_DEV_PM_OPS(ohci_hcd_at91_pm_ops, ohci_hcd_at91_drv_suspend, static struct platform_driver ohci_hcd_at91_driver = { .probe = ohci_hcd_at91_drv_probe, - .remove_new = ohci_hcd_at91_drv_remove, + .remove = ohci_hcd_at91_drv_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "at91_ohci", diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c index d2b67da76762..3c5ca2d7c92e 100644 --- a/drivers/usb/host/ohci-da8xx.c +++ b/drivers/usb/host/ohci-da8xx.c @@ -531,7 +531,7 @@ static const struct ohci_driver_overrides da8xx_overrides __initconst = { */ static struct platform_driver ohci_hcd_da8xx_driver = { .probe = ohci_da8xx_probe, - .remove_new = ohci_da8xx_remove, + .remove = ohci_da8xx_remove, .shutdown = usb_hcd_platform_shutdown, #ifdef CONFIG_PM .suspend = ohci_da8xx_suspend, diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c index 1379e03644b2..cc5cb0900988 100644 --- a/drivers/usb/host/ohci-exynos.c +++ b/drivers/usb/host/ohci-exynos.c @@ -262,7 +262,7 @@ MODULE_DEVICE_TABLE(of, exynos_ohci_match); static struct platform_driver exynos_ohci_driver = { .probe = exynos_ohci_probe, - .remove_new = exynos_ohci_remove, + .remove = exynos_ohci_remove, .shutdown = exynos_ohci_shutdown, .driver = { .name = "exynos-ohci", diff --git a/drivers/usb/host/ohci-nxp.c b/drivers/usb/host/ohci-nxp.c index 5b775e1ea527..24d5a1dc5056 100644 --- a/drivers/usb/host/ohci-nxp.c +++ b/drivers/usb/host/ohci-nxp.c @@ -254,7 +254,7 @@ static struct platform_driver ohci_hcd_nxp_driver = { .of_match_table = of_match_ptr(ohci_hcd_nxp_match), }, .probe = ohci_hcd_nxp_probe, - .remove_new = ohci_hcd_nxp_remove, + .remove = ohci_hcd_nxp_remove, }; static int __init ohci_nxp_init(void) diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 21a6f6c55e07..f6e56c4b9914 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -152,7 +152,7 @@ static int ohci_omap_reset(struct usb_hcd *hcd) rh &= ~RH_A_NOCP; - /* gpio9 for overcurrent detction */ + /* gpio9 for overcurrent detection */ omap_cfg_reg(W8_1610_GPIO9); /* for paranoia's sake: disable USB.PUEN */ @@ -390,7 +390,7 @@ static int ohci_omap_resume(struct platform_device *dev) */ static struct platform_driver ohci_hcd_omap_driver = { .probe = ohci_hcd_omap_probe, - .remove_new = ohci_hcd_omap_remove, + .remove = ohci_hcd_omap_remove, .shutdown = usb_hcd_platform_shutdown, #ifdef CONFIG_PM .suspend = ohci_omap_suspend, diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index 4a75507325dd..f47ae12cde6a 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c @@ -344,7 +344,7 @@ static const struct dev_pm_ops ohci_platform_pm_ops = { static struct platform_driver ohci_platform_driver = { .id_table = ohci_platform_table, .probe = ohci_platform_probe, - .remove_new = ohci_platform_remove, + .remove = ohci_platform_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "ohci-platform", diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c index a6be061f8653..acd0a0e398a4 100644 --- a/drivers/usb/host/ohci-ppc-of.c +++ b/drivers/usb/host/ohci-ppc-of.c @@ -219,7 +219,7 @@ MODULE_DEVICE_TABLE(of, ohci_hcd_ppc_of_match); static struct platform_driver ohci_hcd_ppc_of_driver = { .probe = ohci_hcd_ppc_of_probe, - .remove_new = ohci_hcd_ppc_of_remove, + .remove = ohci_hcd_ppc_of_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "ppc-of-ohci", diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c index 3348c25ddb18..45d026e85168 100644 --- a/drivers/usb/host/ohci-pxa27x.c +++ b/drivers/usb/host/ohci-pxa27x.c @@ -569,7 +569,7 @@ static const struct dev_pm_ops ohci_hcd_pxa27x_pm_ops = { static struct platform_driver ohci_hcd_pxa27x_driver = { .probe = ohci_hcd_pxa27x_probe, - .remove_new = ohci_hcd_pxa27x_remove, + .remove = ohci_hcd_pxa27x_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "pxa27x-ohci", diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c index c5c9b4cbcb9a..66d970854357 100644 --- a/drivers/usb/host/ohci-s3c2410.c +++ b/drivers/usb/host/ohci-s3c2410.c @@ -457,7 +457,7 @@ MODULE_DEVICE_TABLE(of, ohci_hcd_s3c2410_dt_ids); static struct platform_driver ohci_hcd_s3c2410_driver = { .probe = ohci_hcd_s3c2410_probe, - .remove_new = ohci_hcd_s3c2410_remove, + .remove = ohci_hcd_s3c2410_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "s3c2410-ohci", diff --git a/drivers/usb/host/ohci-sm501.c b/drivers/usb/host/ohci-sm501.c index 4b39e9d6f33a..843a5378764e 100644 --- a/drivers/usb/host/ohci-sm501.c +++ b/drivers/usb/host/ohci-sm501.c @@ -252,7 +252,7 @@ static int ohci_sm501_resume(struct platform_device *pdev) */ static struct platform_driver ohci_hcd_sm501_driver = { .probe = ohci_hcd_sm501_drv_probe, - .remove_new = ohci_hcd_sm501_drv_remove, + .remove = ohci_hcd_sm501_drv_remove, .shutdown = usb_hcd_platform_shutdown, .suspend = ohci_sm501_suspend, .resume = ohci_sm501_resume, diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c index 993f347c5c28..d7131e5a4477 100644 --- a/drivers/usb/host/ohci-spear.c +++ b/drivers/usb/host/ohci-spear.c @@ -157,7 +157,7 @@ MODULE_DEVICE_TABLE(of, spear_ohci_id_table); /* Driver definition to register with the platform bus */ static struct platform_driver spear_ohci_hcd_driver = { .probe = spear_ohci_hcd_drv_probe, - .remove_new = spear_ohci_hcd_drv_remove, + .remove = spear_ohci_hcd_drv_remove, #ifdef CONFIG_PM .suspend = spear_ohci_hcd_drv_suspend, .resume = spear_ohci_hcd_drv_resume, diff --git a/drivers/usb/host/ohci-st.c b/drivers/usb/host/ohci-st.c index 214342013f7e..d1656fce5400 100644 --- a/drivers/usb/host/ohci-st.c +++ b/drivers/usb/host/ohci-st.c @@ -298,7 +298,7 @@ MODULE_DEVICE_TABLE(of, st_ohci_platform_ids); static struct platform_driver ohci_platform_driver = { .probe = st_ohci_platform_probe, - .remove_new = st_ohci_platform_remove, + .remove = st_ohci_platform_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "st-ohci", diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c index ca3859463ba1..14195896ad62 100644 --- a/drivers/usb/host/oxu210hp-hcd.c +++ b/drivers/usb/host/oxu210hp-hcd.c @@ -885,7 +885,7 @@ static int oxu_buf_alloc(struct oxu_hcd *oxu, struct ehci_qtd *qtd, int len) int a_blocks; /* blocks allocated */ int i, j; - /* Don't allocte bigger than supported */ + /* Don't allocate bigger than supported */ if (len > BUFFER_SIZE * BUFFER_NUM) { oxu_err(oxu, "buffer too big (%d)\n", len); return -ENOMEM; @@ -4289,7 +4289,7 @@ static int oxu_drv_resume(struct device *dev) static struct platform_driver oxu_driver = { .probe = oxu_drv_probe, - .remove_new = oxu_drv_remove, + .remove = oxu_drv_remove, .shutdown = oxu_drv_shutdown, .suspend = oxu_drv_suspend, .resume = oxu_drv_resume, diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 6576515a29cd..af7bf6e6627f 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -759,7 +759,7 @@ static void enable_r8a66597_pipe_dma(struct r8a66597 *r8a66597, struct r8a66597_pipe_info *info = &pipe->info; unsigned short mbw = mbw_value(r8a66597); - /* pipe dma is only for external controlles */ + /* pipe dma is only for external controllers */ if (r8a66597->pdata->on_chip) return; @@ -2510,7 +2510,7 @@ clean_up: static struct platform_driver r8a66597_driver = { .probe = r8a66597_probe, - .remove_new = r8a66597_remove, + .remove = r8a66597_remove, .driver = { .name = hcd_name, .pm = R8A66597_DEV_PM_OPS, diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index 92f2d1238448..036f5fd6d159 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -1784,7 +1784,7 @@ sl811h_resume(struct platform_device *dev) /* this driver is exported so sl811_cs can depend on it */ struct platform_driver sl811h_driver = { .probe = sl811h_probe, - .remove_new = sl811h_remove, + .remove = sl811h_remove, .suspend = sl811h_suspend, .resume = sl811h_resume, diff --git a/drivers/usb/host/uhci-grlib.c b/drivers/usb/host/uhci-grlib.c index cfebb833668e..8a1f6d1b5b56 100644 --- a/drivers/usb/host/uhci-grlib.c +++ b/drivers/usb/host/uhci-grlib.c @@ -184,7 +184,7 @@ MODULE_DEVICE_TABLE(of, uhci_hcd_grlib_of_match); static struct platform_driver uhci_grlib_driver = { .probe = uhci_hcd_grlib_probe, - .remove_new = uhci_hcd_grlib_remove, + .remove = uhci_hcd_grlib_remove, .shutdown = uhci_hcd_grlib_shutdown, .driver = { .name = "grlib-uhci", diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c index 3dec5dd3a0d5..a7c934404ebc 100644 --- a/drivers/usb/host/uhci-platform.c +++ b/drivers/usb/host/uhci-platform.c @@ -184,7 +184,7 @@ MODULE_DEVICE_TABLE(of, platform_uhci_ids); static struct platform_driver uhci_platform_driver = { .probe = uhci_hcd_platform_probe, - .remove_new = uhci_hcd_platform_remove, + .remove = uhci_hcd_platform_remove, .shutdown = uhci_hcd_platform_shutdown, .driver = { .name = "platform-uhci", diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c index 241d7aa1fbc2..227e513867dd 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c @@ -248,8 +248,9 @@ xhci_dbc_queue_trb(struct xhci_ring *ring, u32 field1, trb->generic.field[2] = cpu_to_le32(field3); trb->generic.field[3] = cpu_to_le32(field4); - trace_xhci_dbc_gadget_ep_queue(ring, &trb->generic); - + trace_xhci_dbc_gadget_ep_queue(ring, &trb->generic, + xhci_trb_virt_to_dma(ring->enq_seg, + ring->enqueue)); ring->num_trbs_free--; next = ++(ring->enqueue); if (TRB_TYPE_LINK_LE32(next->link.control)) { @@ -471,7 +472,7 @@ xhci_dbc_ring_alloc(struct device *dev, enum xhci_ring_type type, gfp_t flags) trb->link.control = cpu_to_le32(LINK_TOGGLE | TRB_TYPE(TRB_LINK)); } INIT_LIST_HEAD(&ring->td_list); - xhci_initialize_ring_info(ring, 1); + xhci_initialize_ring_info(ring); return ring; dma_fail: kfree(seg); @@ -747,7 +748,7 @@ static void dbc_handle_xfer_event(struct xhci_dbc *dbc, union xhci_trb *event) return; } - trace_xhci_dbc_handle_transfer(ring, &req->trb->generic); + trace_xhci_dbc_handle_transfer(ring, &req->trb->generic, req->trb_dma); switch (comp_code) { case COMP_SUCCESS: @@ -898,7 +899,9 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc) */ rmb(); - trace_xhci_dbc_handle_event(dbc->ring_evt, &evt->generic); + trace_xhci_dbc_handle_event(dbc->ring_evt, &evt->generic, + xhci_trb_virt_to_dma(dbc->ring_evt->deq_seg, + dbc->ring_evt->dequeue)); switch (le32_to_cpu(evt->event_cmd.flags) & TRB_TYPE_BITMASK) { case TRB_TYPE(TRB_PORT_STATUS): diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c index f8ba15e7c225..4f0c1b96e208 100644 --- a/drivers/usb/host/xhci-debugfs.c +++ b/drivers/usb/host/xhci-debugfs.c @@ -214,14 +214,11 @@ static void xhci_ring_dump_segment(struct seq_file *s, static int xhci_ring_trb_show(struct seq_file *s, void *unused) { - int i; struct xhci_ring *ring = *(struct xhci_ring **)s->private; struct xhci_segment *seg = ring->first_seg; - for (i = 0; i < ring->num_segs; i++) { + xhci_for_each_ring_seg(ring->first_seg, seg) xhci_ring_dump_segment(s, seg); - seg = seg->next; - } return 0; } @@ -291,12 +288,13 @@ static int xhci_endpoint_context_show(struct seq_file *s, void *unused) for (ep_index = 0; ep_index < 31; ep_index++) { ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index); dma = dev->out_ctx->dma + (ep_index + 1) * CTX_SIZE(xhci->hcc_params); - seq_printf(s, "%pad: %s\n", &dma, + seq_printf(s, "%pad: %s, virt_state:%#x\n", &dma, xhci_decode_ep_context(str, le32_to_cpu(ep_ctx->ep_info), le32_to_cpu(ep_ctx->ep_info2), le64_to_cpu(ep_ctx->deq), - le32_to_cpu(ep_ctx->tx_info))); + le32_to_cpu(ep_ctx->tx_info)), + dev->eps[ep_index].ep_state); } return 0; diff --git a/drivers/usb/host/xhci-histb.c b/drivers/usb/host/xhci-histb.c index f9a4a4b0eb57..8a7d46dae62c 100644 --- a/drivers/usb/host/xhci-histb.c +++ b/drivers/usb/host/xhci-histb.c @@ -373,7 +373,7 @@ MODULE_DEVICE_TABLE(of, histb_xhci_of_match); static struct platform_driver histb_xhci_driver = { .probe = xhci_histb_probe, - .remove_new = xhci_histb_remove, + .remove = xhci_histb_remove, .driver = { .name = "xhci-histb", .pm = DEV_PM_OPS, diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 8d774f19271e..9693464c0520 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -946,9 +946,9 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, } /* did port event handler already start resume timing? */ if (!port->resume_timestamp) { - /* If not, maybe we are in a host initated resume? */ + /* If not, maybe we are in a host initiated resume? */ if (test_bit(wIndex, &bus_state->resuming_ports)) { - /* Host initated resume doesn't time the resume + /* Host initiated resume doesn't time the resume * signalling using resume_done[]. * It manually sets RESUME state, sleeps 20ms * and sets U0 state. This should probably be @@ -1924,7 +1924,7 @@ int xhci_bus_resume(struct usb_hcd *hcd) /* resume already initiated */ break; default: - /* not in a resumeable state, ignore it */ + /* not in a resumable state, ignore it */ clear_bit(port_index, &bus_state->bus_suspended); break; diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index d2900197a49e..15db90c54a45 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -27,14 +27,12 @@ * "All components of all Command and Transfer TRBs shall be initialized to '0'" */ static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci, - unsigned int cycle_state, unsigned int max_packet, unsigned int num, gfp_t flags) { struct xhci_segment *seg; dma_addr_t dma; - int i; struct device *dev = xhci_to_hcd(xhci)->self.sysdev; seg = kzalloc_node(sizeof(*seg), flags, dev_to_node(dev)); @@ -56,11 +54,6 @@ static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci, return NULL; } } - /* If the cycle state is 0, set the cycle bit to 1 for all the TRBs */ - if (cycle_state == 0) { - for (i = 0; i < TRBS_PER_SEGMENT; i++) - seg->trbs[i].link.control = cpu_to_le32(TRB_CYCLE); - } seg->num = num; seg->dma = dma; seg->next = NULL; @@ -78,82 +71,104 @@ static void xhci_segment_free(struct xhci_hcd *xhci, struct xhci_segment *seg) kfree(seg); } -static void xhci_free_segments_for_ring(struct xhci_hcd *xhci, - struct xhci_segment *first) +static void xhci_ring_segments_free(struct xhci_hcd *xhci, struct xhci_ring *ring) { - struct xhci_segment *seg; + struct xhci_segment *seg, *next; + + ring->last_seg->next = NULL; + seg = ring->first_seg; - seg = first->next; - while (seg && seg != first) { - struct xhci_segment *next = seg->next; + while (seg) { + next = seg->next; xhci_segment_free(xhci, seg); seg = next; } - xhci_segment_free(xhci, first); } /* - * Make the prev segment point to the next segment. + * Only for transfer and command rings where driver is the producer, not for + * event rings. * - * Change the last TRB in the prev segment to be a Link TRB which points to the + * Change the last TRB in the segment to be a Link TRB which points to the * DMA address of the next segment. The caller needs to set any Link TRB * related flags, such as End TRB, Toggle Cycle, and no snoop. */ -static void xhci_link_segments(struct xhci_segment *prev, - struct xhci_segment *next, - enum xhci_ring_type type, bool chain_links) +static void xhci_set_link_trb(struct xhci_segment *seg, bool chain_links) { + union xhci_trb *trb; u32 val; - if (!prev || !next) + if (!seg || !seg->next) return; - prev->next = next; - if (type != TYPE_EVENT) { - prev->trbs[TRBS_PER_SEGMENT-1].link.segment_ptr = - cpu_to_le64(next->dma); - /* Set the last TRB in the segment to have a TRB type ID of Link TRB */ - val = le32_to_cpu(prev->trbs[TRBS_PER_SEGMENT-1].link.control); - val &= ~TRB_TYPE_BITMASK; - val |= TRB_TYPE(TRB_LINK); - if (chain_links) - val |= TRB_CHAIN; - prev->trbs[TRBS_PER_SEGMENT-1].link.control = cpu_to_le32(val); - } + trb = &seg->trbs[TRBS_PER_SEGMENT - 1]; + + /* Set the last TRB in the segment to have a TRB type ID of Link TRB */ + val = le32_to_cpu(trb->link.control); + val &= ~TRB_TYPE_BITMASK; + val |= TRB_TYPE(TRB_LINK); + if (chain_links) + val |= TRB_CHAIN; + trb->link.control = cpu_to_le32(val); + trb->link.segment_ptr = cpu_to_le64(seg->next->dma); +} + +static void xhci_initialize_ring_segments(struct xhci_hcd *xhci, struct xhci_ring *ring) +{ + struct xhci_segment *seg; + bool chain_links; + + if (ring->type == TYPE_EVENT) + return; + + chain_links = xhci_link_chain_quirk(xhci, ring->type); + xhci_for_each_ring_seg(ring->first_seg, seg) + xhci_set_link_trb(seg, chain_links); + + /* See section 4.9.2.1 and 6.4.4.1 */ + ring->last_seg->trbs[TRBS_PER_SEGMENT - 1].link.control |= cpu_to_le32(LINK_TOGGLE); } /* - * Link the ring to the new segments. + * Link the src ring segments to the dst ring. * Set Toggle Cycle for the new ring if needed. */ -static void xhci_link_rings(struct xhci_hcd *xhci, struct xhci_ring *ring, - struct xhci_segment *first, struct xhci_segment *last, - unsigned int num_segs) +static void xhci_link_rings(struct xhci_hcd *xhci, struct xhci_ring *src, struct xhci_ring *dst) { - struct xhci_segment *next, *seg; + struct xhci_segment *seg; bool chain_links; - if (!ring || !first || !last) + if (!src || !dst) return; - chain_links = xhci_link_chain_quirk(xhci, ring->type); + /* If the cycle state is 0, set the cycle bit to 1 for all the TRBs */ + if (dst->cycle_state == 0) { + xhci_for_each_ring_seg(src->first_seg, seg) { + for (int i = 0; i < TRBS_PER_SEGMENT; i++) + seg->trbs[i].link.control |= cpu_to_le32(TRB_CYCLE); + } + } - next = ring->enq_seg->next; - xhci_link_segments(ring->enq_seg, first, ring->type, chain_links); - xhci_link_segments(last, next, ring->type, chain_links); - ring->num_segs += num_segs; + src->last_seg->next = dst->enq_seg->next; + dst->enq_seg->next = src->first_seg; + if (dst->type != TYPE_EVENT) { + chain_links = xhci_link_chain_quirk(xhci, dst->type); + xhci_set_link_trb(dst->enq_seg, chain_links); + xhci_set_link_trb(src->last_seg, chain_links); + } + dst->num_segs += src->num_segs; - if (ring->enq_seg == ring->last_seg) { - if (ring->type != TYPE_EVENT) { - ring->last_seg->trbs[TRBS_PER_SEGMENT-1].link.control + if (dst->enq_seg == dst->last_seg) { + if (dst->type != TYPE_EVENT) + dst->last_seg->trbs[TRBS_PER_SEGMENT-1].link.control &= ~cpu_to_le32(LINK_TOGGLE); - last->trbs[TRBS_PER_SEGMENT-1].link.control - |= cpu_to_le32(LINK_TOGGLE); - } - ring->last_seg = last; + + dst->last_seg = src->last_seg; + } else if (dst->type != TYPE_EVENT) { + src->last_seg->trbs[TRBS_PER_SEGMENT-1].link.control &= ~cpu_to_le32(LINK_TOGGLE); } - for (seg = ring->enq_seg; seg != ring->last_seg; seg = seg->next) + for (seg = dst->enq_seg; seg != dst->last_seg; seg = seg->next) seg->next->num = seg->num + 1; } @@ -224,7 +239,6 @@ static int xhci_update_stream_segment_mapping( struct radix_tree_root *trb_address_map, struct xhci_ring *ring, struct xhci_segment *first_seg, - struct xhci_segment *last_seg, gfp_t mem_flags) { struct xhci_segment *seg; @@ -234,28 +248,22 @@ static int xhci_update_stream_segment_mapping( if (WARN_ON_ONCE(trb_address_map == NULL)) return 0; - seg = first_seg; - do { + xhci_for_each_ring_seg(first_seg, seg) { ret = xhci_insert_segment_mapping(trb_address_map, ring, seg, mem_flags); if (ret) goto remove_streams; - if (seg == last_seg) - return 0; - seg = seg->next; - } while (seg != first_seg); + } return 0; remove_streams: failed_seg = seg; - seg = first_seg; - do { + xhci_for_each_ring_seg(first_seg, seg) { xhci_remove_segment_mapping(trb_address_map, seg); if (seg == failed_seg) return ret; - seg = seg->next; - } while (seg != first_seg); + } return ret; } @@ -267,17 +275,14 @@ static void xhci_remove_stream_mapping(struct xhci_ring *ring) if (WARN_ON_ONCE(ring->trb_address_map == NULL)) return; - seg = ring->first_seg; - do { + xhci_for_each_ring_seg(ring->first_seg, seg) xhci_remove_segment_mapping(ring->trb_address_map, seg); - seg = seg->next; - } while (seg != ring->first_seg); } static int xhci_update_stream_mapping(struct xhci_ring *ring, gfp_t mem_flags) { return xhci_update_stream_segment_mapping(ring->trb_address_map, ring, - ring->first_seg, ring->last_seg, mem_flags); + ring->first_seg, mem_flags); } /* XXX: Do we need the hcd structure in all these functions? */ @@ -291,14 +296,13 @@ void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring) if (ring->first_seg) { if (ring->type == TYPE_STREAM) xhci_remove_stream_mapping(ring); - xhci_free_segments_for_ring(xhci, ring->first_seg); + xhci_ring_segments_free(xhci, ring); } kfree(ring); } -void xhci_initialize_ring_info(struct xhci_ring *ring, - unsigned int cycle_state) +void xhci_initialize_ring_info(struct xhci_ring *ring) { /* The ring is empty, so the enqueue pointer == dequeue pointer */ ring->enqueue = ring->first_seg->trbs; @@ -312,7 +316,7 @@ void xhci_initialize_ring_info(struct xhci_ring *ring, * New rings are initialized with cycle state equal to 1; if we are * handling ring expansion, set the cycle state equal to the old ring. */ - ring->cycle_state = cycle_state; + ring->cycle_state = 1; /* * Each segment has a link TRB, and leave an extra TRB for SW @@ -323,46 +327,36 @@ void xhci_initialize_ring_info(struct xhci_ring *ring, EXPORT_SYMBOL_GPL(xhci_initialize_ring_info); /* Allocate segments and link them for a ring */ -static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci, - struct xhci_segment **first, - struct xhci_segment **last, - unsigned int num_segs, - unsigned int cycle_state, - enum xhci_ring_type type, - unsigned int max_packet, - gfp_t flags) +static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci, struct xhci_ring *ring, gfp_t flags) { struct xhci_segment *prev; unsigned int num = 0; - bool chain_links; - - chain_links = xhci_link_chain_quirk(xhci, type); - prev = xhci_segment_alloc(xhci, cycle_state, max_packet, num, flags); + prev = xhci_segment_alloc(xhci, ring->bounce_buf_len, num, flags); if (!prev) return -ENOMEM; num++; - *first = prev; - while (num < num_segs) { + ring->first_seg = prev; + while (num < ring->num_segs) { struct xhci_segment *next; - next = xhci_segment_alloc(xhci, cycle_state, max_packet, num, - flags); + next = xhci_segment_alloc(xhci, ring->bounce_buf_len, num, flags); if (!next) goto free_segments; - xhci_link_segments(prev, next, type, chain_links); + prev->next = next; prev = next; num++; } - xhci_link_segments(prev, *first, type, chain_links); - *last = prev; + ring->last_seg = prev; + ring->last_seg->next = ring->first_seg; return 0; free_segments: - xhci_free_segments_for_ring(xhci, *first); + ring->last_seg = prev; + xhci_ring_segments_free(xhci, ring); return -ENOMEM; } @@ -373,9 +367,8 @@ free_segments: * Set the end flag and the cycle toggle bit on the last segment. * See section 4.9.1 and figures 15 and 16. */ -struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, - unsigned int num_segs, unsigned int cycle_state, - enum xhci_ring_type type, unsigned int max_packet, gfp_t flags) +struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, unsigned int num_segs, + enum xhci_ring_type type, unsigned int max_packet, gfp_t flags) { struct xhci_ring *ring; int ret; @@ -392,18 +385,12 @@ struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, if (num_segs == 0) return ring; - ret = xhci_alloc_segments_for_ring(xhci, &ring->first_seg, &ring->last_seg, num_segs, - cycle_state, type, max_packet, flags); + ret = xhci_alloc_segments_for_ring(xhci, ring, flags); if (ret) goto fail; - /* Only event ring does not use link TRB */ - if (type != TYPE_EVENT) { - /* See section 4.9.2.1 and 6.4.4.1 */ - ring->last_seg->trbs[TRBS_PER_SEGMENT - 1].link.control |= - cpu_to_le32(LINK_TOGGLE); - } - xhci_initialize_ring_info(ring, cycle_state); + xhci_initialize_ring_segments(xhci, ring); + xhci_initialize_ring_info(ring); trace_xhci_ring_alloc(ring); return ring; @@ -427,23 +414,29 @@ void xhci_free_endpoint_ring(struct xhci_hcd *xhci, int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring, unsigned int num_new_segs, gfp_t flags) { - struct xhci_segment *first; - struct xhci_segment *last; - int ret; + struct xhci_ring new_ring; + int ret; + + if (num_new_segs == 0) + return 0; - ret = xhci_alloc_segments_for_ring(xhci, &first, &last, num_new_segs, ring->cycle_state, - ring->type, ring->bounce_buf_len, flags); + new_ring.num_segs = num_new_segs; + new_ring.bounce_buf_len = ring->bounce_buf_len; + new_ring.type = ring->type; + ret = xhci_alloc_segments_for_ring(xhci, &new_ring, flags); if (ret) return -ENOMEM; + xhci_initialize_ring_segments(xhci, &new_ring); + if (ring->type == TYPE_STREAM) { - ret = xhci_update_stream_segment_mapping(ring->trb_address_map, - ring, first, last, flags); + ret = xhci_update_stream_segment_mapping(ring->trb_address_map, ring, + new_ring.first_seg, flags); if (ret) goto free_segments; } - xhci_link_rings(xhci, ring, first, last, num_new_segs); + xhci_link_rings(xhci, ring, &new_ring); trace_xhci_ring_expansion(ring); xhci_dbg_trace(xhci, trace_xhci_dbg_ring_expansion, "ring expansion succeed, now has %d segments", @@ -452,7 +445,7 @@ int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring, return 0; free_segments: - xhci_free_segments_for_ring(xhci, first); + xhci_ring_segments_free(xhci, &new_ring); return ret; } @@ -642,8 +635,7 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, for (cur_stream = 1; cur_stream < num_streams; cur_stream++) { stream_info->stream_rings[cur_stream] = - xhci_ring_alloc(xhci, 2, 1, TYPE_STREAM, max_packet, - mem_flags); + xhci_ring_alloc(xhci, 2, TYPE_STREAM, max_packet, mem_flags); cur_ring = stream_info->stream_rings[cur_stream]; if (!cur_ring) goto cleanup_rings; @@ -658,6 +650,8 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, xhci_dbg(xhci, "Setting stream %d ring ptr to 0x%08llx\n", cur_stream, addr); ret = xhci_update_stream_mapping(cur_ring, mem_flags); + + trace_xhci_alloc_stream_info_ctx(stream_info, cur_stream); if (ret) { xhci_ring_free(xhci, cur_ring); stream_info->stream_rings[cur_stream] = NULL; @@ -984,7 +978,7 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, } /* Allocate endpoint 0 ring */ - dev->eps[0].ring = xhci_ring_alloc(xhci, 2, 1, TYPE_CTRL, 0, flags); + dev->eps[0].ring = xhci_ring_alloc(xhci, 2, TYPE_CTRL, 0, flags); if (!dev->eps[0].ring) goto fail; @@ -1461,7 +1455,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, /* Set up the endpoint ring */ virt_dev->eps[ep_index].new_ring = - xhci_ring_alloc(xhci, 2, 1, ring_type, max_packet, mem_flags); + xhci_ring_alloc(xhci, 2, ring_type, max_packet, mem_flags); if (!virt_dev->eps[ep_index].new_ring) return -ENOMEM; @@ -2271,7 +2265,7 @@ xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int segs, gfp_t flags) if (!ir) return NULL; - ir->event_ring = xhci_ring_alloc(xhci, segs, 1, TYPE_EVENT, 0, flags); + ir->event_ring = xhci_ring_alloc(xhci, segs, TYPE_EVENT, 0, flags); if (!ir->event_ring) { xhci_warn(xhci, "Failed to allocate interrupter event ring\n"); kfree(ir); @@ -2473,7 +2467,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) goto fail; /* Set up the command ring to have one segments for now. */ - xhci->cmd_ring = xhci_ring_alloc(xhci, 1, 1, TYPE_COMMAND, 0, flags); + xhci->cmd_ring = xhci_ring_alloc(xhci, 1, TYPE_COMMAND, 0, flags); if (!xhci->cmd_ring) goto fail; xhci_dbg_trace(xhci, trace_xhci_dbg_init, @@ -2518,11 +2512,6 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) ir->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX; - /* - * XXX: Might need to set the Interrupter Moderation Register to - * something other than the default (~1ms minimum between interrupts). - * See section 5.5.1.2. - */ for (i = 0; i < MAX_HC_SLOTS; i++) xhci->devs[i] = NULL; diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c index 3252e3d2d79c..904831344440 100644 --- a/drivers/usb/host/xhci-mtk.c +++ b/drivers/usb/host/xhci-mtk.c @@ -853,7 +853,7 @@ MODULE_DEVICE_TABLE(of, mtk_xhci_of_match); static struct platform_driver mtk_xhci_driver = { .probe = xhci_mtk_probe, - .remove_new = xhci_mtk_remove, + .remove = xhci_mtk_remove, .driver = { .name = "xhci-mtk", .pm = DEV_PM_OPS, diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 7e538194a0a4..47c4f70793e4 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -28,18 +28,18 @@ #define SPARSE_CNTL_ENABLE 0xC12C /* Device for a quirk */ -#define PCI_VENDOR_ID_FRESCO_LOGIC 0x1b73 -#define PCI_DEVICE_ID_FRESCO_LOGIC_PDK 0x1000 +#define PCI_VENDOR_ID_FRESCO_LOGIC 0x1b73 +#define PCI_DEVICE_ID_FRESCO_LOGIC_PDK 0x1000 #define PCI_DEVICE_ID_FRESCO_LOGIC_FL1009 0x1009 #define PCI_DEVICE_ID_FRESCO_LOGIC_FL1100 0x1100 #define PCI_DEVICE_ID_FRESCO_LOGIC_FL1400 0x1400 -#define PCI_VENDOR_ID_ETRON 0x1b6f -#define PCI_DEVICE_ID_EJ168 0x7023 -#define PCI_DEVICE_ID_EJ188 0x7052 +#define PCI_VENDOR_ID_ETRON 0x1b6f +#define PCI_DEVICE_ID_ETRON_EJ168 0x7023 +#define PCI_DEVICE_ID_ETRON_EJ188 0x7052 -#define PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI 0x8c31 -#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 +#define PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI 0x8c31 +#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 #define PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_XHCI 0x9cb1 #define PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI 0x22b5 #define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI 0xa12f @@ -82,8 +82,7 @@ #define PCI_DEVICE_ID_ASMEDIA_3042_XHCI 0x3042 #define PCI_DEVICE_ID_ASMEDIA_3242_XHCI 0x3242 -#define PCI_DEVICE_ID_CADENCE 0x17CD -#define PCI_DEVICE_ID_CADENCE_SSP 0x0200 +#define PCI_DEVICE_ID_CDNS_SSP 0x0200 static const char hcd_name[] = "xhci_hcd"; @@ -150,14 +149,11 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd) hcd->irq = 0; /* - * calculate number of MSI-X vectors supported. - * - HCS_MAX_INTRS: the max number of interrupts the host can handle, - * with max number of interrupters based on the xhci HCSPARAMS1. - * - num_online_cpus: maximum MSI-X vectors per CPUs core. - * Add additional 1 vector to ensure always available interrupt. + * Calculate number of MSI/MSI-X vectors supported. + * - max_interrupters: the max number of interrupts requested, capped to xhci HCSPARAMS1. + * - num_online_cpus: one vector per CPUs core, with at least one overall. */ - xhci->nvecs = min(num_online_cpus() + 1, - HCS_MAX_INTRS(xhci->hcs_params1)); + xhci->nvecs = min(num_online_cpus() + 1, xhci->max_interrupters); /* TODO: Check with MSI Soc for sysdev */ xhci->nvecs = pci_alloc_irq_vectors(pdev, 1, xhci->nvecs, @@ -395,14 +391,12 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) xhci->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW; if (pdev->vendor == PCI_VENDOR_ID_ETRON && - pdev->device == PCI_DEVICE_ID_EJ168) { - xhci->quirks |= XHCI_RESET_ON_RESUME; - xhci->quirks |= XHCI_BROKEN_STREAMS; - } - if (pdev->vendor == PCI_VENDOR_ID_ETRON && - pdev->device == PCI_DEVICE_ID_EJ188) { + (pdev->device == PCI_DEVICE_ID_ETRON_EJ168 || + pdev->device == PCI_DEVICE_ID_ETRON_EJ188)) { + xhci->quirks |= XHCI_ETRON_HOST; xhci->quirks |= XHCI_RESET_ON_RESUME; xhci->quirks |= XHCI_BROKEN_STREAMS; + xhci->quirks |= XHCI_NO_SOFT_RETRY; } if (pdev->vendor == PCI_VENDOR_ID_RENESAS && @@ -481,9 +475,8 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) if (pdev->device == 0x9203) xhci->quirks |= XHCI_ZHAOXIN_TRB_FETCH; } - - if (pdev->vendor == PCI_DEVICE_ID_CADENCE && - pdev->device == PCI_DEVICE_ID_CADENCE_SSP) + if (pdev->vendor == PCI_VENDOR_ID_CDNS && + pdev->device == PCI_DEVICE_ID_CDNS_SSP) xhci->quirks |= XHCI_CDNS_SCTX_QUIRK; /* xHC spec requires PCI devices to support D3hot and D3cold */ @@ -640,12 +633,15 @@ int xhci_pci_common_probe(struct pci_dev *dev, const struct pci_device_id *id) pm_runtime_put_noidle(&dev->dev); if (pci_choose_state(dev, PMSG_SUSPEND) == PCI_D0) - pm_runtime_forbid(&dev->dev); + pm_runtime_get(&dev->dev); else if (xhci->quirks & XHCI_DEFAULT_PM_RUNTIME_ALLOW) pm_runtime_allow(&dev->dev); dma_set_max_seg_size(&dev->dev, UINT_MAX); + if (device_property_read_bool(&dev->dev, "ti,pwron-active-high")) + pci_clear_and_set_config_dword(dev, 0xE0, 0, 1 << 22); + return 0; put_usb3_hcd: @@ -683,7 +679,9 @@ void xhci_pci_remove(struct pci_dev *dev) xhci->xhc_state |= XHCI_STATE_REMOVING; - if (xhci->quirks & XHCI_DEFAULT_PM_RUNTIME_ALLOW) + if (pci_choose_state(dev, PMSG_SUSPEND) == PCI_D0) + pm_runtime_put(&dev->dev); + else if (xhci->quirks & XHCI_DEFAULT_PM_RUNTIME_ALLOW) pm_runtime_forbid(&dev->dev); if (xhci->shared_hcd) { diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index ecaa75718e59..e6c9006bd568 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -573,7 +573,7 @@ MODULE_DEVICE_TABLE(acpi, usb_xhci_acpi_match); static struct platform_driver usb_generic_xhci_driver = { .probe = xhci_generic_plat_probe, - .remove_new = xhci_plat_remove, + .remove = xhci_plat_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "xhci-hcd", diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c index 8b357647728c..1cc082a3b793 100644 --- a/drivers/usb/host/xhci-rcar.c +++ b/drivers/usb/host/xhci-rcar.c @@ -274,7 +274,7 @@ static int xhci_renesas_probe(struct platform_device *pdev) static struct platform_driver usb_xhci_renesas_driver = { .probe = xhci_renesas_probe, - .remove_new = xhci_plat_remove, + .remove = xhci_plat_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "xhci-renesas-hcd", diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index b6eb928e260f..4cf5363875c7 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -52,6 +52,7 @@ * endpoint rings; it generates events on the event ring for these. */ +#include <linux/jiffies.h> #include <linux/scatterlist.h> #include <linux/slab.h> #include <linux/dma-mapping.h> @@ -145,10 +146,8 @@ static void trb_to_noop(union xhci_trb *trb, u32 noop_type) * TRB is in a new segment. This does not skip over link TRBs, and it does not * effect the ring dequeue or enqueue pointers. */ -static void next_trb(struct xhci_hcd *xhci, - struct xhci_ring *ring, - struct xhci_segment **seg, - union xhci_trb **trb) +static void next_trb(struct xhci_segment **seg, + union xhci_trb **trb) { if (trb_is_link(*trb) || last_trb_on_seg(*seg, *trb)) { *seg = (*seg)->next; @@ -169,13 +168,16 @@ void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring) if (ring->type == TYPE_EVENT) { if (!last_trb_on_seg(ring->deq_seg, ring->dequeue)) { ring->dequeue++; - goto out; + return; } if (last_trb_on_ring(ring, ring->deq_seg, ring->dequeue)) ring->cycle_state ^= 1; ring->deq_seg = ring->deq_seg->next; ring->dequeue = ring->deq_seg->trbs; - goto out; + + trace_xhci_inc_deq(ring); + + return; } /* All other rings have link trbs */ @@ -190,14 +192,13 @@ void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring) ring->deq_seg = ring->deq_seg->next; ring->dequeue = ring->deq_seg->trbs; + trace_xhci_inc_deq(ring); + if (link_trb_count++ > ring->num_segs) { xhci_warn(xhci, "Ring is an endless link TRB loop\n"); break; } } -out: - trace_xhci_inc_deq(ring); - return; } @@ -266,13 +267,13 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, ring->enqueue = ring->enq_seg->trbs; next = ring->enqueue; + trace_xhci_inc_enq(ring); + if (link_trb_count++ > ring->num_segs) { xhci_warn(xhci, "%s: Ring link TRB loop\n", __func__); break; } } - - trace_xhci_inc_enq(ring); } /* @@ -426,7 +427,7 @@ static void xhci_handle_stopped_cmd_ring(struct xhci_hcd *xhci, } } -/* Must be called with xhci->lock held, releases and aquires lock back */ +/* Must be called with xhci->lock held, releases and acquires lock back */ static int xhci_abort_cmd_ring(struct xhci_hcd *xhci, unsigned long flags) { struct xhci_segment *new_seg = xhci->cmd_ring->deq_seg; @@ -446,9 +447,9 @@ static int xhci_abort_cmd_ring(struct xhci_hcd *xhci, unsigned long flags) * avoiding corrupting the command ring pointer in case the command ring * is stopped by the time the upper dword is written. */ - next_trb(xhci, NULL, &new_seg, &new_deq); + next_trb(&new_seg, &new_deq); if (trb_is_link(new_deq)) - next_trb(xhci, NULL, &new_seg, &new_deq); + next_trb(&new_seg, &new_deq); crcr = xhci_trb_virt_to_dma(new_seg, new_deq); xhci_write_64(xhci, crcr | CMD_RING_ABORT, &xhci->op_regs->cmd_ring); @@ -660,8 +661,8 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci, /* * We want to find the pointer, segment and cycle state of the new trb - * (the one after current TD's last_trb). We know the cycle state at - * hw_dequeue, so walk the ring until both hw_dequeue and last_trb are + * (the one after current TD's end_trb). We know the cycle state at + * hw_dequeue, so walk the ring until both hw_dequeue and end_trb are * found. */ do { @@ -671,14 +672,14 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci, if (td_last_trb_found) break; } - if (new_deq == td->last_trb) + if (new_deq == td->end_trb) td_last_trb_found = true; if (cycle_found && trb_is_link(new_deq) && link_trb_toggles_cycle(new_deq)) new_cycle ^= 0x1; - next_trb(xhci, ep_ring, &new_seg, &new_deq); + next_trb(&new_seg, &new_deq); /* Search wrapped around, bail out */ if (new_deq == ep->ring->dequeue) { @@ -740,23 +741,22 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci, * (The last TRB actually points to the ring enqueue pointer, which is not part * of this TD.) This is used to remove partially enqueued isoc TDs from a ring. */ -static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, - struct xhci_td *td, bool flip_cycle) +static void td_to_noop(struct xhci_td *td, bool flip_cycle) { struct xhci_segment *seg = td->start_seg; - union xhci_trb *trb = td->first_trb; + union xhci_trb *trb = td->start_trb; while (1) { trb_to_noop(trb, TRB_TR_NOOP); /* flip cycle if asked to */ - if (flip_cycle && trb != td->first_trb && trb != td->last_trb) + if (flip_cycle && trb != td->start_trb && trb != td->end_trb) trb->generic.field[3] ^= cpu_to_le32(TRB_CYCLE); - if (trb == td->last_trb) + if (trb == td->end_trb) break; - next_trb(xhci, ep_ring, &seg, &trb); + next_trb(&seg, &trb); } } @@ -799,7 +799,7 @@ static void xhci_unmap_td_bounce_buffer(struct xhci_hcd *xhci, dma_unmap_single(dev, seg->bounce_dma, ring->bounce_buf_len, DMA_FROM_DEVICE); - /* for in tranfers we need to copy the data from bounce to sg */ + /* for in transfers we need to copy the data from bounce to sg */ if (urb->num_sgs) { len = sg_pcopy_from_buffer(urb->sg, urb->num_sgs, seg->bounce_buf, seg->bounce_len, seg->bounce_offs); @@ -814,8 +814,8 @@ static void xhci_unmap_td_bounce_buffer(struct xhci_hcd *xhci, seg->bounce_offs = 0; } -static int xhci_td_cleanup(struct xhci_hcd *xhci, struct xhci_td *td, - struct xhci_ring *ep_ring, int status) +static void xhci_td_cleanup(struct xhci_hcd *xhci, struct xhci_td *td, + struct xhci_ring *ep_ring, int status) { struct urb *urb = NULL; @@ -858,10 +858,18 @@ static int xhci_td_cleanup(struct xhci_hcd *xhci, struct xhci_td *td, status = 0; xhci_giveback_urb_in_irq(xhci, td, status); } - - return 0; } +/* Give back previous TD and move on to the next TD. */ +static void xhci_dequeue_td(struct xhci_hcd *xhci, struct xhci_td *td, struct xhci_ring *ring, + u32 status) +{ + ring->dequeue = td->end_trb; + ring->deq_seg = td->end_seg; + inc_deq(xhci, ring); + + xhci_td_cleanup(xhci, td, ring, status); +} /* Complete the cancelled URBs we unlinked from td_list. */ static void xhci_giveback_invalidated_tds(struct xhci_virt_ep *ep) @@ -972,13 +980,20 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) unsigned int slot_id = ep->vdev->slot_id; int err; + /* + * This is not going to work if the hardware is changing its dequeue + * pointers as we look at them. Completion handler will call us later. + */ + if (ep->ep_state & SET_DEQ_PENDING) + return 0; + xhci = ep->xhci; list_for_each_entry_safe(td, tmp_td, &ep->cancelled_td_list, cancelled_td_list) { xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, "Removing canceled TD starting at 0x%llx (dma) in stream %u URB %p", (unsigned long long)xhci_trb_virt_to_dma( - td->start_seg, td->first_trb), + td->start_seg, td->start_trb), td->urb->stream_id, td->urb); list_del_init(&td->td_list); ring = xhci_urb_to_transfer_ring(xhci, td->urb); @@ -1020,16 +1035,16 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) "Found multiple active URBs %p and %p in stream %u?\n", td->urb, cached_td->urb, td->urb->stream_id); - td_to_noop(xhci, ring, cached_td, false); + td_to_noop(cached_td, false); cached_td->cancel_status = TD_CLEARED; } - td_to_noop(xhci, ring, td, false); + td_to_noop(td, false); td->cancel_status = TD_CLEARING_CACHE; cached_td = td; break; } } else { - td_to_noop(xhci, ring, td, false); + td_to_noop(td, false); td->cancel_status = TD_CLEARED; } } @@ -1054,7 +1069,7 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) continue; xhci_warn(xhci, "Failed to clear cancelled cached URB %p, mark clear anyway\n", td->urb); - td_to_noop(xhci, ring, td, false); + td_to_noop(td, false); td->cancel_status = TD_CLEARED; } } @@ -1062,6 +1077,19 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) } /* + * Erase queued TDs from transfer ring(s) and give back those the xHC didn't + * stop on. If necessary, queue commands to move the xHC off cancelled TDs it + * stopped on. Those will be given back later when the commands complete. + * + * Call under xhci->lock on a stopped endpoint. + */ +void xhci_process_cancelled_tds(struct xhci_virt_ep *ep) +{ + xhci_invalidate_cancelled_tds(ep); + xhci_giveback_invalidated_tds(ep); +} + +/* * Returns the TD the endpoint ring halted on. * Only call for non-running rings without streams. */ @@ -1151,16 +1179,35 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id, return; case EP_STATE_STOPPED: /* - * NEC uPD720200 sometimes sets this state and fails with - * Context Error while continuing to process TRBs. - * Be conservative and trust EP_CTX_STATE on other chips. + * Per xHCI 4.6.9, Stop Endpoint command on a Stopped + * EP is a Context State Error, and EP stays Stopped. + * + * But maybe it failed on Halted, and somebody ran Reset + * Endpoint later. EP state is now Stopped and EP_HALTED + * still set because Reset EP handler will run after us. + */ + if (ep->ep_state & EP_HALTED) + break; + /* + * On some HCs EP state remains Stopped for some tens of + * us to a few ms or more after a doorbell ring, and any + * new Stop Endpoint fails without aborting the restart. + * This handler may run quickly enough to still see this + * Stopped state, but it will soon change to Running. + * + * Assume this bug on unexpected Stop Endpoint failures. + * Keep retrying until the EP starts and stops again, on + * chips where this is known to help. Wait for 100ms. */ if (!(xhci->quirks & XHCI_NEC_HOST)) break; + if (time_is_before_jiffies(ep->stop_time + msecs_to_jiffies(100))) + break; fallthrough; case EP_STATE_RUNNING: /* Race, HW handled stop ep cmd before ep was running */ - xhci_dbg(xhci, "Stop ep completion ctx error, ep is running\n"); + xhci_dbg(xhci, "Stop ep completion ctx error, ctx_state %d\n", + GET_EP_CTX_STATE(ep_ctx)); command = xhci_alloc_command(xhci, false, GFP_ATOMIC); if (!command) { @@ -1338,8 +1385,8 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, struct xhci_virt_ep *ep; struct xhci_ep_ctx *ep_ctx; struct xhci_slot_ctx *slot_ctx; + struct xhci_stream_ctx *stream_ctx; struct xhci_td *td, *tmp_td; - bool deferred = false; ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3])); stream_id = TRB_TO_STREAM_ID(le32_to_cpu(trb->generic.field[2])); @@ -1360,6 +1407,11 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, trace_xhci_handle_cmd_set_deq(slot_ctx); trace_xhci_handle_cmd_set_deq_ep(ep_ctx); + if (ep->ep_state & EP_HAS_STREAMS) { + stream_ctx = &ep->stream_info->stream_ctx_array[stream_id]; + trace_xhci_handle_cmd_set_deq_stream(ep->stream_info, stream_id); + } + if (cmd_comp_code != COMP_SUCCESS) { unsigned int ep_state; unsigned int slot_state; @@ -1396,9 +1448,7 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, u64 deq; /* 4.6.10 deq ptr is written to the stream ctx for streams */ if (ep->ep_state & EP_HAS_STREAMS) { - struct xhci_stream_ctx *ctx = - &ep->stream_info->stream_ctx_array[stream_id]; - deq = le64_to_cpu(ctx->stream_ring) & SCTX_DEQ_MASK; + deq = le64_to_cpu(stream_ctx->stream_ring) & SCTX_DEQ_MASK; /* * Cadence xHCI controllers store some endpoint state @@ -1410,8 +1460,8 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, * To fix this issue driver must clear Rsvd0 field. */ if (xhci->quirks & XHCI_CDNS_SCTX_QUIRK) { - ctx->reserved[0] = 0; - ctx->reserved[1] = 0; + stream_ctx->reserved[0] = 0; + stream_ctx->reserved[1] = 0; } } else { deq = le64_to_cpu(ep_ctx->deq) & ~EP_CTX_CYCLE_MASK; @@ -1440,8 +1490,6 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, xhci_dbg(ep->xhci, "%s: Giveback cancelled URB %p TD\n", __func__, td->urb); xhci_td_cleanup(ep->xhci, td, ep_ring, td->status); - } else if (td->cancel_status == TD_CLEARING_CACHE_DEFERRED) { - deferred = true; } else { xhci_dbg(ep->xhci, "%s: Keep cancelled URB %p TD as cancel_status is %d\n", __func__, td->urb, td->cancel_status); @@ -1452,11 +1500,15 @@ cleanup: ep->queued_deq_seg = NULL; ep->queued_deq_ptr = NULL; - if (deferred) { - /* We have more streams to clear */ + /* Check for deferred or newly cancelled TDs */ + if (!list_empty(&ep->cancelled_td_list)) { xhci_dbg(ep->xhci, "%s: Pending TDs to clear, continuing with invalidation\n", __func__); xhci_invalidate_cancelled_tds(ep); + /* Try to restart the endpoint if all is done */ + ring_doorbell_for_active_rings(xhci, slot_id, ep_index); + /* Start giving back any TDs invalidated above */ + xhci_giveback_invalidated_tds(ep); } else { /* Restart any rings with pending URBs */ xhci_dbg(ep->xhci, "%s: All TDs cleared, ring doorbell\n", __func__); @@ -1716,7 +1768,15 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, cmd_dma = le64_to_cpu(event->cmd_trb); cmd_trb = xhci->cmd_ring->dequeue; - trace_xhci_handle_command(xhci->cmd_ring, &cmd_trb->generic); + trace_xhci_handle_command(xhci->cmd_ring, &cmd_trb->generic, cmd_dma); + + cmd_comp_code = GET_COMP_CODE(le32_to_cpu(event->status)); + + /* If CMD ring stopped we own the trbs between enqueue and dequeue */ + if (cmd_comp_code == COMP_COMMAND_RING_STOPPED) { + complete_all(&xhci->cmd_ring_stop_completion); + return; + } cmd_dequeue_dma = xhci_trb_virt_to_dma(xhci->cmd_ring->deq_seg, cmd_trb); @@ -1734,14 +1794,6 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, cancel_delayed_work(&xhci->cmd_timer); - cmd_comp_code = GET_COMP_CODE(le32_to_cpu(event->status)); - - /* If CMD ring stopped we own the trbs between enqueue and dequeue */ - if (cmd_comp_code == COMP_COMMAND_RING_STOPPED) { - complete_all(&xhci->cmd_ring_stop_completion); - return; - } - if (cmd->command_trb != xhci->cmd_ring->dequeue) { xhci_err(xhci, "Command completion event does not match command\n"); @@ -2074,7 +2126,7 @@ struct xhci_segment *trb_in_td(struct xhci_hcd *xhci, struct xhci_td *td, dma_ad dma_addr_t end_trb_dma; struct xhci_segment *cur_seg; - start_dma = xhci_trb_virt_to_dma(td->start_seg, td->first_trb); + start_dma = xhci_trb_virt_to_dma(td->start_seg, td->start_trb); cur_seg = td->start_seg; do { @@ -2084,7 +2136,7 @@ struct xhci_segment *trb_in_td(struct xhci_hcd *xhci, struct xhci_td *td, dma_ad end_seg_dma = xhci_trb_virt_to_dma(cur_seg, &cur_seg->trbs[TRBS_PER_SEGMENT - 1]); /* If the end TRB isn't in this segment, this is set to 0 */ - end_trb_dma = xhci_trb_virt_to_dma(cur_seg, td->last_trb); + end_trb_dma = xhci_trb_virt_to_dma(cur_seg, td->end_trb); if (debug) xhci_warn(xhci, @@ -2184,9 +2236,9 @@ int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code) return 0; } -static int finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, - struct xhci_ring *ep_ring, struct xhci_td *td, - u32 trb_comp_code) +static void finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, + struct xhci_ring *ep_ring, struct xhci_td *td, + u32 trb_comp_code) { struct xhci_ep_ctx *ep_ctx; @@ -2201,7 +2253,7 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, * stopped TDs. A stopped TD may be restarted, so don't update * the ring dequeue pointer or take this TD off any lists yet. */ - return 0; + return; case COMP_USB_TRANSACTION_ERROR: case COMP_BABBLE_DETECTED_ERROR: case COMP_SPLIT_TRANSACTION_ERROR: @@ -2226,8 +2278,8 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, !list_empty(&td->cancelled_td_list)) { xhci_dbg(xhci, "Already resolving halted ep for 0x%llx\n", (unsigned long long)xhci_trb_virt_to_dma( - td->start_seg, td->first_trb)); - return 0; + td->start_seg, td->start_trb)); + return; } /* endpoint not halted, don't reset it */ break; @@ -2235,7 +2287,7 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, /* Almost same procedure as for STALL_ERROR below */ xhci_clear_hub_tt_buffer(xhci, td, ep); xhci_handle_halted_endpoint(xhci, ep, td, EP_HARD_RESET); - return 0; + return; case COMP_STALL_ERROR: /* * xhci internal endpoint state will go to a "halt" state for @@ -2252,28 +2304,22 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, xhci_handle_halted_endpoint(xhci, ep, td, EP_HARD_RESET); - return 0; /* xhci_handle_halted_endpoint marked td cancelled */ + return; /* xhci_handle_halted_endpoint marked td cancelled */ default: break; } - /* Update ring dequeue pointer */ - ep_ring->dequeue = td->last_trb; - ep_ring->deq_seg = td->last_trb_seg; - inc_deq(xhci, ep_ring); - - return xhci_td_cleanup(xhci, td, ep_ring, td->status); + xhci_dequeue_td(xhci, td, ep_ring, td->status); } -/* sum trb lengths from ring dequeue up to stop_trb, _excluding_ stop_trb */ -static int sum_trb_lengths(struct xhci_hcd *xhci, struct xhci_ring *ring, - union xhci_trb *stop_trb) +/* sum trb lengths from the first trb up to stop_trb, _excluding_ stop_trb */ +static u32 sum_trb_lengths(struct xhci_td *td, union xhci_trb *stop_trb) { u32 sum; - union xhci_trb *trb = ring->dequeue; - struct xhci_segment *seg = ring->deq_seg; + union xhci_trb *trb = td->start_trb; + struct xhci_segment *seg = td->start_seg; - for (sum = 0; trb != stop_trb; next_trb(xhci, ring, &seg, &trb)) { + for (sum = 0; trb != stop_trb; next_trb(&seg, &trb)) { if (!trb_is_noop(trb) && !trb_is_link(trb)) sum += TRB_LEN(le32_to_cpu(trb->generic.field[2])); } @@ -2283,9 +2329,9 @@ static int sum_trb_lengths(struct xhci_hcd *xhci, struct xhci_ring *ring, /* * Process control tds, update urb status and actual_length. */ -static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, - struct xhci_ring *ep_ring, struct xhci_td *td, - union xhci_trb *ep_trb, struct xhci_transfer_event *event) +static void process_ctrl_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, + struct xhci_ring *ep_ring, struct xhci_td *td, + union xhci_trb *ep_trb, struct xhci_transfer_event *event) { struct xhci_ep_ctx *ep_ctx; u32 trb_comp_code; @@ -2364,7 +2410,7 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, td->urb_length_set = true; td->urb->actual_length = requested - remaining; xhci_dbg(xhci, "Waiting for status stage event\n"); - return 0; + return; } /* at status stage */ @@ -2372,15 +2418,15 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, td->urb->actual_length = requested; finish_td: - return finish_td(xhci, ep, ep_ring, td, trb_comp_code); + finish_td(xhci, ep, ep_ring, td, trb_comp_code); } /* * Process isochronous tds, update urb packet status and actual_length. */ -static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, - struct xhci_ring *ep_ring, struct xhci_td *td, - union xhci_trb *ep_trb, struct xhci_transfer_event *event) +static void process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, + struct xhci_ring *ep_ring, struct xhci_td *td, + union xhci_trb *ep_trb, struct xhci_transfer_event *event) { struct urb_priv *urb_priv; int idx; @@ -2425,7 +2471,7 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, fallthrough; case COMP_ISOCH_BUFFER_OVERRUN: frame->status = -EOVERFLOW; - if (ep_trb != td->last_trb) + if (ep_trb != td->end_trb) td->error_mid_td = true; break; case COMP_INCOMPATIBLE_DEVICE_ERROR: @@ -2435,14 +2481,14 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, case COMP_USB_TRANSACTION_ERROR: frame->status = -EPROTO; sum_trbs_for_length = true; - if (ep_trb != td->last_trb) + if (ep_trb != td->end_trb) td->error_mid_td = true; break; case COMP_STOPPED: sum_trbs_for_length = true; break; case COMP_STOPPED_SHORT_PACKET: - /* field normally containing residue now contains tranferred */ + /* field normally containing residue now contains transferred */ frame->status = short_framestatus; requested = remaining; break; @@ -2462,7 +2508,7 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, goto finish_td; if (sum_trbs_for_length) - frame->actual_length = sum_trb_lengths(xhci, ep->ring, ep_trb) + + frame->actual_length = sum_trb_lengths(td, ep_trb) + ep_trb_len - remaining; else frame->actual_length = requested; @@ -2471,17 +2517,16 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, finish_td: /* Don't give back TD yet if we encountered an error mid TD */ - if (td->error_mid_td && ep_trb != td->last_trb) { + if (td->error_mid_td && ep_trb != td->end_trb) { xhci_dbg(xhci, "Error mid isoc TD, wait for final completion event\n"); td->urb_length_set = true; - return 0; + return; } - - return finish_td(xhci, ep, ep_ring, td, trb_comp_code); + finish_td(xhci, ep, ep_ring, td, trb_comp_code); } -static int skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td, - struct xhci_virt_ep *ep, int status) +static void skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td, + struct xhci_virt_ep *ep, int status) { struct urb_priv *urb_priv; struct usb_iso_packet_descriptor *frame; @@ -2497,20 +2542,15 @@ static int skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td, /* calc actual length */ frame->actual_length = 0; - /* Update ring dequeue pointer */ - ep->ring->dequeue = td->last_trb; - ep->ring->deq_seg = td->last_trb_seg; - inc_deq(xhci, ep->ring); - - return xhci_td_cleanup(xhci, td, ep->ring, status); + xhci_dequeue_td(xhci, td, ep->ring, status); } /* * Process bulk and interrupt tds, update urb status and actual_length. */ -static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, - struct xhci_ring *ep_ring, struct xhci_td *td, - union xhci_trb *ep_trb, struct xhci_transfer_event *event) +static void process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, + struct xhci_ring *ep_ring, struct xhci_td *td, + union xhci_trb *ep_trb, struct xhci_transfer_event *event) { struct xhci_slot_ctx *slot_ctx; u32 trb_comp_code; @@ -2526,7 +2566,7 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, case COMP_SUCCESS: ep->err_count = 0; /* handle success with untransferred data as short packet */ - if (ep_trb != td->last_trb || remaining) { + if (ep_trb != td->end_trb || remaining) { xhci_warn(xhci, "WARN Successful completion on short TX\n"); xhci_dbg(xhci, "ep %#x - asked for %d bytes, %d bytes untransferred\n", td->urb->ep->desc.bEndpointAddress, @@ -2542,7 +2582,7 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, goto finish_td; case COMP_STOPPED_LENGTH_INVALID: /* stopped on ep trb with invalid length, exclude it */ - td->urb->actual_length = sum_trb_lengths(xhci, ep_ring, ep_trb); + td->urb->actual_length = sum_trb_lengths(td, ep_trb); goto finish_td; case COMP_USB_TRANSACTION_ERROR: if (xhci->quirks & XHCI_NO_SOFT_RETRY || @@ -2553,17 +2593,17 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, td->status = 0; xhci_handle_halted_endpoint(xhci, ep, td, EP_SOFT_RESET); - return 0; + return; default: /* do nothing */ break; } - if (ep_trb == td->last_trb) + if (ep_trb == td->end_trb) td->urb->actual_length = requested - remaining; else td->urb->actual_length = - sum_trb_lengths(xhci, ep_ring, ep_trb) + + sum_trb_lengths(td, ep_trb) + ep_trb_len - remaining; finish_td: if (remaining > requested) { @@ -2572,7 +2612,7 @@ finish_td: td->urb->actual_length = 0; } - return finish_td(xhci, ep, ep_ring, td, trb_comp_code); + finish_td(xhci, ep, ep_ring, td, trb_comp_code); } /* Transfer events which don't point to a transfer TRB, see xhci 4.17.4 */ @@ -2792,10 +2832,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, if (td && td->error_mid_td && !trb_in_td(xhci, td, ep_trb_dma, false)) { xhci_dbg(xhci, "Missing TD completion event after mid TD error\n"); - ep_ring->dequeue = td->last_trb; - ep_ring->deq_seg = td->last_trb_seg; - inc_deq(xhci, ep_ring); - xhci_td_cleanup(xhci, td, ep_ring, td->status); + xhci_dequeue_td(xhci, td, ep_ring, td->status); } if (list_empty(&ep_ring->td_list)) { @@ -2889,7 +2926,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, ep_ring->last_td_was_short = false; ep_trb = &ep_seg->trbs[(ep_trb_dma - ep_seg->dma) / sizeof(*ep_trb)]; - trace_xhci_handle_transfer(ep_ring, (struct xhci_generic_trb *) ep_trb); + trace_xhci_handle_transfer(ep_ring, (struct xhci_generic_trb *) ep_trb, ep_trb_dma); /* * No-op TRB could trigger interrupts in a case where a URB was killed @@ -2939,7 +2976,9 @@ static int xhci_handle_event_trb(struct xhci_hcd *xhci, struct xhci_interrupter { u32 trb_type; - trace_xhci_handle_event(ir->event_ring, &event->generic); + trace_xhci_handle_event(ir->event_ring, &event->generic, + xhci_trb_virt_to_dma(ir->event_ring->deq_seg, + ir->event_ring->dequeue)); /* * Barrier between reading the TRB_CYCLE (valid) flag before, and any @@ -3162,7 +3201,8 @@ static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring, wmb(); trb->field[3] = cpu_to_le32(field4); - trace_xhci_queue_trb(ring, trb); + trace_xhci_queue_trb(ring, trb, + xhci_trb_virt_to_dma(ring->enq_seg, ring->enqueue)); inc_enq(xhci, ring, more_trbs_coming); } @@ -3302,7 +3342,7 @@ static int prepare_transfer(struct xhci_hcd *xhci, /* Add this TD to the tail of the endpoint ring's TD list */ list_add_tail(&td->td_list, &ep_ring->td_list); td->start_seg = ep_ring->enq_seg; - td->first_trb = ep_ring->enqueue; + td->start_trb = ep_ring->enqueue; return 0; } @@ -3641,8 +3681,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, field &= ~TRB_CHAIN; field |= TRB_IOC; more_trbs_coming = false; - td->last_trb = ring->enqueue; - td->last_trb_seg = ring->enq_seg; + td->end_trb = ring->enqueue; + td->end_seg = ring->enq_seg; if (xhci_urb_suitable_for_idt(urb)) { memcpy(&send_addr, urb->transfer_buffer, trb_buff_len); @@ -3690,8 +3730,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index, urb->stream_id, 1, urb, 1, mem_flags); - urb_priv->td[1].last_trb = ring->enqueue; - urb_priv->td[1].last_trb_seg = ring->enq_seg; + urb_priv->td[1].end_trb = ring->enqueue; + urb_priv->td[1].end_seg = ring->enq_seg; field = TRB_TYPE(TRB_NORMAL) | ring->cycle_state | TRB_IOC; queue_trb(xhci, ring, 0, 0, 0, TRB_INTR_TARGET(0), field); } @@ -3727,6 +3767,20 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, if (!urb->setup_packet) return -EINVAL; + if ((xhci->quirks & XHCI_ETRON_HOST) && + urb->dev->speed >= USB_SPEED_SUPER) { + /* + * If next available TRB is the Link TRB in the ring segment then + * enqueue a No Op TRB, this can prevent the Setup and Data Stage + * TRB to be breaked by the Link TRB. + */ + if (trb_is_link(ep_ring->enqueue + 1)) { + field = TRB_TYPE(TRB_TR_NOOP) | ep_ring->cycle_state; + queue_trb(xhci, ep_ring, false, 0, 0, + TRB_INTR_TARGET(0), field); + } + } + /* 1 TRB for setup, 1 for status */ num_trbs = 2; /* @@ -3815,8 +3869,8 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, } /* Save the DMA address of the last TRB in the TD */ - td->last_trb = ep_ring->enqueue; - td->last_trb_seg = ep_ring->enq_seg; + td->end_trb = ep_ring->enqueue; + td->end_seg = ep_ring->enq_seg; /* Queue status TRB - see Table 7 and sections 4.11.2.2 and 6.4.1.2.3 */ /* If the device sent data, the status stage is an OUT transfer */ @@ -4101,8 +4155,8 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, field |= TRB_CHAIN; } else { more_trbs_coming = false; - td->last_trb = ep_ring->enqueue; - td->last_trb_seg = ep_ring->enq_seg; + td->end_trb = ep_ring->enqueue; + td->end_seg = ep_ring->enq_seg; field |= TRB_IOC; if (trb_block_event_intr(xhci, num_tds, i, ir)) field |= TRB_BEI; @@ -4168,14 +4222,14 @@ cleanup: /* Use the first TD as a temporary variable to turn the TDs we've queued * into No-ops with a software-owned cycle bit. That way the hardware * won't accidentally start executing bogus TDs when we partially - * overwrite them. td->first_trb and td->start_seg are already set. + * overwrite them. td->start_trb and td->start_seg are already set. */ - urb_priv->td[0].last_trb = ep_ring->enqueue; + urb_priv->td[0].end_trb = ep_ring->enqueue; /* Every TRB except the first & last will have its cycle bit flipped. */ - td_to_noop(xhci, ep_ring, &urb_priv->td[0], true); + td_to_noop(&urb_priv->td[0], true); /* Reset the ring enqueue back to the first TRB and its cycle bit. */ - ep_ring->enqueue = urb_priv->td[0].first_trb; + ep_ring->enqueue = urb_priv->td[0].start_trb; ep_ring->enq_seg = urb_priv->td[0].start_seg; ep_ring->cycle_state = start_cycle; usb_hcd_unlink_urb_from_ep(bus_to_hcd(urb->dev->bus), urb); diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 76f228e7443c..06ae193ec874 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -2664,7 +2664,7 @@ MODULE_DEVICE_TABLE(of, tegra_xusb_of_match); static struct platform_driver tegra_xusb_driver = { .probe = tegra_xusb_probe, - .remove_new = tegra_xusb_remove, + .remove = tegra_xusb_remove, .shutdown = tegra_xusb_shutdown, .driver = { .name = "tegra-xusb", diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h index 24405315ffe6..bfb5c5c17012 100644 --- a/drivers/usb/host/xhci-trace.h +++ b/drivers/usb/host/xhci-trace.h @@ -108,9 +108,10 @@ DEFINE_EVENT(xhci_log_ctx, xhci_address_ctx, ); DECLARE_EVENT_CLASS(xhci_log_trb, - TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb), - TP_ARGS(ring, trb), + TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb, dma_addr_t dma), + TP_ARGS(ring, trb, dma), TP_STRUCT__entry( + __field(dma_addr_t, dma) __field(u32, type) __field(u32, field0) __field(u32, field1) @@ -118,51 +119,54 @@ DECLARE_EVENT_CLASS(xhci_log_trb, __field(u32, field3) ), TP_fast_assign( + __entry->dma = dma; __entry->type = ring->type; __entry->field0 = le32_to_cpu(trb->field[0]); __entry->field1 = le32_to_cpu(trb->field[1]); __entry->field2 = le32_to_cpu(trb->field[2]); __entry->field3 = le32_to_cpu(trb->field[3]); ), - TP_printk("%s: %s", xhci_ring_type_string(__entry->type), + TP_printk("%s: @%pad %s", + xhci_ring_type_string(__entry->type), &__entry->dma, xhci_decode_trb(__get_buf(XHCI_MSG_MAX), XHCI_MSG_MAX, __entry->field0, __entry->field1, __entry->field2, __entry->field3) ) ); DEFINE_EVENT(xhci_log_trb, xhci_handle_event, - TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb), - TP_ARGS(ring, trb) + TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb, dma_addr_t dma), + TP_ARGS(ring, trb, dma) ); DEFINE_EVENT(xhci_log_trb, xhci_handle_command, - TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb), - TP_ARGS(ring, trb) + TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb, dma_addr_t dma), + TP_ARGS(ring, trb, dma) ); DEFINE_EVENT(xhci_log_trb, xhci_handle_transfer, - TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb), - TP_ARGS(ring, trb) + TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb, dma_addr_t dma), + TP_ARGS(ring, trb, dma) ); DEFINE_EVENT(xhci_log_trb, xhci_queue_trb, - TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb), - TP_ARGS(ring, trb) + TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb, dma_addr_t dma), + TP_ARGS(ring, trb, dma) + ); DEFINE_EVENT(xhci_log_trb, xhci_dbc_handle_event, - TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb), - TP_ARGS(ring, trb) + TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb, dma_addr_t dma), + TP_ARGS(ring, trb, dma) ); DEFINE_EVENT(xhci_log_trb, xhci_dbc_handle_transfer, - TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb), - TP_ARGS(ring, trb) + TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb, dma_addr_t dma), + TP_ARGS(ring, trb, dma) ); DEFINE_EVENT(xhci_log_trb, xhci_dbc_gadget_ep_queue, - TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb), - TP_ARGS(ring, trb) + TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb, dma_addr_t dma), + TP_ARGS(ring, trb, dma) ); DECLARE_EVENT_CLASS(xhci_log_free_virt_dev, @@ -310,6 +314,37 @@ DEFINE_EVENT(xhci_log_urb, xhci_urb_dequeue, TP_ARGS(urb) ); +DECLARE_EVENT_CLASS(xhci_log_stream_ctx, + TP_PROTO(struct xhci_stream_info *info, unsigned int stream_id), + TP_ARGS(info, stream_id), + TP_STRUCT__entry( + __field(unsigned int, stream_id) + __field(u64, stream_ring) + __field(dma_addr_t, ctx_array_dma) + + ), + TP_fast_assign( + __entry->stream_id = stream_id; + __entry->stream_ring = le64_to_cpu(info->stream_ctx_array[stream_id].stream_ring); + __entry->ctx_array_dma = info->ctx_array_dma + stream_id * 16; + + ), + TP_printk("stream %u ctx @%pad: SCT %llu deq %llx", __entry->stream_id, + &__entry->ctx_array_dma, CTX_TO_SCT(__entry->stream_ring), + __entry->stream_ring + ) +); + +DEFINE_EVENT(xhci_log_stream_ctx, xhci_alloc_stream_info_ctx, + TP_PROTO(struct xhci_stream_info *info, unsigned int stream_id), + TP_ARGS(info, stream_id) +); + +DEFINE_EVENT(xhci_log_stream_ctx, xhci_handle_cmd_set_deq_stream, + TP_PROTO(struct xhci_stream_info *info, unsigned int stream_id), + TP_ARGS(info, stream_id) +); + DECLARE_EVENT_CLASS(xhci_log_ep_ctx, TP_PROTO(struct xhci_ep_ctx *ctx), TP_ARGS(ctx), @@ -454,8 +489,6 @@ DECLARE_EVENT_CLASS(xhci_log_ring, __field(void *, ring) __field(dma_addr_t, enq) __field(dma_addr_t, deq) - __field(dma_addr_t, enq_seg) - __field(dma_addr_t, deq_seg) __field(unsigned int, num_segs) __field(unsigned int, stream_id) __field(unsigned int, cycle_state) @@ -466,17 +499,15 @@ DECLARE_EVENT_CLASS(xhci_log_ring, __entry->type = ring->type; __entry->num_segs = ring->num_segs; __entry->stream_id = ring->stream_id; - __entry->enq_seg = ring->enq_seg->dma; - __entry->deq_seg = ring->deq_seg->dma; __entry->cycle_state = ring->cycle_state; __entry->bounce_buf_len = ring->bounce_buf_len; __entry->enq = xhci_trb_virt_to_dma(ring->enq_seg, ring->enqueue); __entry->deq = xhci_trb_virt_to_dma(ring->deq_seg, ring->dequeue); ), - TP_printk("%s %p: enq %pad(%pad) deq %pad(%pad) segs %d stream %d bounce %d cycle %d", + TP_printk("%s %p: enq %pad deq %pad segs %d stream %d bounce %d cycle %d", xhci_ring_type_string(__entry->type), __entry->ring, - &__entry->enq, &__entry->enq_seg, - &__entry->deq, &__entry->deq_seg, + &__entry->enq, + &__entry->deq, __entry->num_segs, __entry->stream_id, __entry->bounce_buf_len, diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 899c0effb5d3..5ebde8cae4fc 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -8,6 +8,7 @@ * Some code borrowed from the Linux EHCI driver. */ +#include <linux/jiffies.h> #include <linux/pci.h> #include <linux/iommu.h> #include <linux/iopoll.h> @@ -40,15 +41,15 @@ MODULE_PARM_DESC(quirks, "Bit flags for quirks to be enabled as default"); static bool td_on_ring(struct xhci_td *td, struct xhci_ring *ring) { - struct xhci_segment *seg = ring->first_seg; + struct xhci_segment *seg; if (!td || !td->start_seg) return false; - do { + + xhci_for_each_ring_seg(ring->first_seg, seg) { if (seg == td->start_seg) return true; - seg = seg->next; - } while (seg && seg != ring->first_seg); + } return false; } @@ -473,14 +474,7 @@ static int xhci_init(struct usb_hcd *hcd) xhci_dbg_trace(xhci, trace_xhci_dbg_init, "xhci_init"); spin_lock_init(&xhci->lock); - if (xhci->hci_version == 0x95 && link_quirk) { - xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, - "QUIRK: Not clearing Link TRB chain bits."); - xhci->quirks |= XHCI_LINK_TRB_QUIRK; - } else { - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "xHCI doesn't need link TRB QUIRK"); - } + retval = xhci_mem_init(xhci, GFP_KERNEL); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Finished xhci_init"); @@ -785,16 +779,10 @@ static void xhci_clear_command_ring(struct xhci_hcd *xhci) struct xhci_segment *seg; ring = xhci->cmd_ring; - seg = ring->deq_seg; - do { - memset(seg->trbs, 0, - sizeof(union xhci_trb) * (TRBS_PER_SEGMENT - 1)); - seg->trbs[TRBS_PER_SEGMENT - 1].link.control &= - cpu_to_le32(~TRB_CYCLE); - seg = seg->next; - } while (seg != ring->deq_seg); - - xhci_initialize_ring_info(ring, 1); + xhci_for_each_ring_seg(ring->first_seg, seg) + memset(seg->trbs, 0, sizeof(union xhci_trb) * (TRBS_PER_SEGMENT - 1)); + + xhci_initialize_ring_info(ring); /* * Reset the hardware dequeue pointer. * Yes, this will need to be re-written after resume, but we're paranoid @@ -1756,7 +1744,7 @@ static int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) urb->ep->desc.bEndpointAddress, (unsigned long long) xhci_trb_virt_to_dma( urb_priv->td[i].start_seg, - urb_priv->td[i].first_trb)); + urb_priv->td[i].start_trb)); for (; i < urb_priv->num_tds; i++) { td = &urb_priv->td[i]; @@ -1768,15 +1756,27 @@ static int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) } } - /* Queue a stop endpoint command, but only if this is - * the first cancellation to be handled. - */ - if (!(ep->ep_state & EP_STOP_CMD_PENDING)) { + /* These completion handlers will sort out cancelled TDs for us */ + if (ep->ep_state & (EP_STOP_CMD_PENDING | EP_HALTED | SET_DEQ_PENDING)) { + xhci_dbg(xhci, "Not queuing Stop Endpoint on slot %d ep %d in state 0x%x\n", + urb->dev->slot_id, ep_index, ep->ep_state); + goto done; + } + + /* In this case no commands are pending but the endpoint is stopped */ + if (ep->ep_state & EP_CLEARING_TT) { + /* and cancelled TDs can be given back right away */ + xhci_dbg(xhci, "Invalidating TDs instantly on slot %d ep %d in state 0x%x\n", + urb->dev->slot_id, ep_index, ep->ep_state); + xhci_process_cancelled_tds(ep); + } else { + /* Otherwise, queue a new Stop Endpoint command */ command = xhci_alloc_command(xhci, false, GFP_ATOMIC); if (!command) { ret = -ENOMEM; goto done; } + ep->stop_time = jiffies; ep->ep_state |= EP_STOP_CMD_PENDING; xhci_queue_stop_endpoint(xhci, command, urb->dev->slot_id, ep_index, 0); @@ -2794,6 +2794,51 @@ static int xhci_reserve_bandwidth(struct xhci_hcd *xhci, return -ENOMEM; } +/* + * Synchronous XHCI stop endpoint helper. Issues the stop endpoint command and + * waits for the command completion before returning. This does not call + * xhci_handle_cmd_stop_ep(), which has additional handling for 'context error' + * cases, along with transfer ring cleanup. + * + * xhci_stop_endpoint_sync() is intended to be utilized by clients that manage + * their own transfer ring, such as offload situations. + */ +int xhci_stop_endpoint_sync(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, int suspend, + gfp_t gfp_flags) +{ + struct xhci_command *command; + unsigned long flags; + int ret; + + command = xhci_alloc_command(xhci, true, gfp_flags); + if (!command) + return -ENOMEM; + + spin_lock_irqsave(&xhci->lock, flags); + ret = xhci_queue_stop_endpoint(xhci, command, ep->vdev->slot_id, + ep->ep_index, suspend); + if (ret < 0) { + spin_unlock_irqrestore(&xhci->lock, flags); + goto out; + } + + xhci_ring_cmd_db(xhci); + spin_unlock_irqrestore(&xhci->lock, flags); + + wait_for_completion(command->completion); + + /* No handling for COMP_CONTEXT_STATE_ERROR done at command completion*/ + if (command->status == COMP_COMMAND_ABORTED || + command->status == COMP_COMMAND_RING_STOPPED) { + xhci_warn(xhci, "Timeout while waiting for stop endpoint command\n"); + ret = -ETIME; + } +out: + xhci_free_command(xhci, command); + + return ret; +} +EXPORT_SYMBOL_GPL(xhci_stop_endpoint_sync); /* Issue a configure endpoint command or evaluate context command * and wait for it to finish. @@ -3692,6 +3737,8 @@ void xhci_free_device_endpoint_resources(struct xhci_hcd *xhci, xhci->num_active_eps); } +static void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev); + /* * This submits a Reset Device Command, which will set the device state to 0, * set the device address to 0, and disable all the endpoints except the default @@ -3762,6 +3809,23 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd, SLOT_STATE_DISABLED) return 0; + if (xhci->quirks & XHCI_ETRON_HOST) { + /* + * Obtaining a new device slot to inform the xHCI host that + * the USB device has been reset. + */ + ret = xhci_disable_slot(xhci, udev->slot_id); + xhci_free_virt_device(xhci, udev->slot_id); + if (!ret) { + ret = xhci_alloc_dev(hcd, udev); + if (ret == 1) + ret = 0; + else + ret = -EINVAL; + } + return ret; + } + trace_xhci_discover_or_reset_device(slot_ctx); xhci_dbg(xhci, "Resetting device with slot ID %u\n", slot_id); @@ -5251,6 +5315,11 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) if (xhci->hci_version > 0x96) xhci->quirks |= XHCI_SPURIOUS_SUCCESS; + if (xhci->hci_version == 0x95 && link_quirk) { + xhci_dbg(xhci, "QUIRK: Not clearing Link TRB chain bits"); + xhci->quirks |= XHCI_LINK_TRB_QUIRK; + } + /* Make sure the HC is halted. */ retval = xhci_halt(xhci); if (retval) diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index f0fb696d5619..4914f0a10cff 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -554,6 +554,7 @@ struct xhci_stream_ctx { /* Stream Context Types (section 6.4.1) - bits 3:1 of stream ctx deq ptr */ #define SCT_FOR_CTX(p) (((p) & 0x7) << 1) +#define CTX_TO_SCT(p) (((p) >> 1) & 0x7) /* Secondary stream array type, dequeue pointer is to a transfer ring */ #define SCT_SEC_TR 0 /* Primary stream array type, dequeue pointer is to a transfer ring */ @@ -690,6 +691,7 @@ struct xhci_virt_ep { /* Bandwidth checking storage */ struct xhci_bw_info bw_info; struct list_head bw_endpoint_list; + unsigned long stop_time; /* Isoch Frame ID checking storage */ int next_frame_id; /* Use new Isoch TRB layout needed for extended TBC support */ @@ -1023,9 +1025,6 @@ enum xhci_setup_dev { /* Interrupter Target - which MSI-X vector to target the completion event at */ #define TRB_INTR_TARGET(p) (((p) & 0x3ff) << 22) #define GET_INTR_TARGET(p) (((p) >> 22) & 0x3ff) -/* Total burst count field, Rsvdz on xhci 1.1 with Extended TBC enabled (ETE) */ -#define TRB_TBC(p) (((p) & 0x3) << 7) -#define TRB_TLBPC(p) (((p) & 0xf) << 16) /* Cycle bit - indicates TRB ownership by HC or HCD */ #define TRB_CYCLE (1<<0) @@ -1059,6 +1058,12 @@ enum xhci_setup_dev { /* Isochronous TRB specific fields */ #define TRB_SIA (1<<31) #define TRB_FRAME_ID(p) (((p) & 0x7ff) << 20) +#define GET_FRAME_ID(p) (((p) >> 20) & 0x7ff) +/* Total burst count field, Rsvdz on xhci 1.1 with Extended TBC enabled (ETE) */ +#define TRB_TBC(p) (((p) & 0x3) << 7) +#define GET_TBC(p) (((p) >> 7) & 0x3) +#define TRB_TLBPC(p) (((p) & 0xf) << 16) +#define GET_TLBPC(p) (((p) >> 16) & 0xf) /* TRB cache size for xHC with TRB cache */ #define TRB_CACHE_SIZE_HS 8 @@ -1259,6 +1264,9 @@ static inline const char *xhci_trb_type_string(u8 type) #define AVOID_BEI_INTERVAL_MIN 8 #define AVOID_BEI_INTERVAL_MAX 32 +#define xhci_for_each_ring_seg(head, seg) \ + for (seg = head; seg != NULL; seg = (seg->next != head ? seg->next : NULL)) + struct xhci_segment { union xhci_trb *trbs; /* private to HCD */ @@ -1287,9 +1295,9 @@ struct xhci_td { enum xhci_cancelled_td_status cancel_status; struct urb *urb; struct xhci_segment *start_seg; - union xhci_trb *first_trb; - union xhci_trb *last_trb; - struct xhci_segment *last_trb_seg; + union xhci_trb *start_trb; + struct xhci_segment *end_seg; + union xhci_trb *end_trb; struct xhci_segment *bounce_seg; /* actual_length of the URB has already been set */ bool urb_length_set; @@ -1624,6 +1632,7 @@ struct xhci_hcd { #define XHCI_ZHAOXIN_HOST BIT_ULL(46) #define XHCI_WRITE_64_HI_LO BIT_ULL(47) #define XHCI_CDNS_SCTX_QUIRK BIT_ULL(48) +#define XHCI_ETRON_HOST BIT_ULL(49) unsigned int num_active_eps; unsigned int limit_active_eps; @@ -1788,14 +1797,12 @@ void xhci_slot_copy(struct xhci_hcd *xhci, int xhci_endpoint_init(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, struct usb_device *udev, struct usb_host_endpoint *ep, gfp_t mem_flags); -struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, - unsigned int num_segs, unsigned int cycle_state, +struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, unsigned int num_segs, enum xhci_ring_type type, unsigned int max_packet, gfp_t flags); void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring); int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring, unsigned int num_trbs, gfp_t flags); -void xhci_initialize_ring_info(struct xhci_ring *ring, - unsigned int cycle_state); +void xhci_initialize_ring_info(struct xhci_ring *ring); void xhci_free_endpoint_ring(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, unsigned int ep_index); @@ -1913,6 +1920,9 @@ void xhci_ring_doorbell_for_active_rings(struct xhci_hcd *xhci, void xhci_cleanup_command_queue(struct xhci_hcd *xhci); void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring); unsigned int count_trbs(u64 addr, u64 len); +int xhci_stop_endpoint_sync(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, + int suspend, gfp_t gfp_flags); +void xhci_process_cancelled_tds(struct xhci_virt_ep *ep); /* xHCI roothub code */ void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port, @@ -2070,7 +2080,6 @@ static inline const char *xhci_decode_trb(char *str, size_t size, field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_NORMAL: - case TRB_ISOC: case TRB_EVENT_DATA: case TRB_TR_NOOP: snprintf(str, size, @@ -2087,7 +2096,25 @@ static inline const char *xhci_decode_trb(char *str, size_t size, field3 & TRB_ENT ? 'E' : 'e', field3 & TRB_CYCLE ? 'C' : 'c'); break; - + case TRB_ISOC: + snprintf(str, size, + "Buffer %08x%08x length %d TD size/TBC %d intr %d type '%s' TBC %u TLBPC %u frame_id %u flags %c:%c:%c:%c:%c:%c:%c:%c:%c", + field1, field0, TRB_LEN(field2), GET_TD_SIZE(field2), + GET_INTR_TARGET(field2), + xhci_trb_type_string(type), + GET_TBC(field3), + GET_TLBPC(field3), + GET_FRAME_ID(field3), + field3 & TRB_SIA ? 'S' : 's', + field3 & TRB_BEI ? 'B' : 'b', + field3 & TRB_IDT ? 'I' : 'i', + field3 & TRB_IOC ? 'I' : 'i', + field3 & TRB_CHAIN ? 'C' : 'c', + field3 & TRB_NO_SNOOP ? 'S' : 's', + field3 & TRB_ISP ? 'I' : 'i', + field3 & TRB_ENT ? 'E' : 'e', + field3 & TRB_CYCLE ? 'C' : 'c'); + break; case TRB_CMD_NOOP: case TRB_ENABLE_SLOT: snprintf(str, size, diff --git a/drivers/usb/isp1760/isp1760-if.c b/drivers/usb/isp1760/isp1760-if.c index fe1e3985419a..a64190addba6 100644 --- a/drivers/usb/isp1760/isp1760-if.c +++ b/drivers/usb/isp1760/isp1760-if.c @@ -263,7 +263,7 @@ MODULE_DEVICE_TABLE(of, isp1760_of_match); static struct platform_driver isp1760_plat_driver = { .probe = isp1760_plat_probe, - .remove_new = isp1760_plat_remove, + .remove = isp1760_plat_remove, .driver = { .name = "isp1760", .of_match_table = of_match_ptr(isp1760_of_match), diff --git a/drivers/usb/misc/chaoskey.c b/drivers/usb/misc/chaoskey.c index 6fb5140e29b9..225863321dc4 100644 --- a/drivers/usb/misc/chaoskey.c +++ b/drivers/usb/misc/chaoskey.c @@ -27,6 +27,8 @@ static struct usb_class_driver chaoskey_class; static int chaoskey_rng_read(struct hwrng *rng, void *data, size_t max, bool wait); +static DEFINE_MUTEX(chaoskey_list_lock); + #define usb_dbg(usb_if, format, arg...) \ dev_dbg(&(usb_if)->dev, format, ## arg) @@ -233,6 +235,7 @@ static void chaoskey_disconnect(struct usb_interface *interface) usb_deregister_dev(interface, &chaoskey_class); usb_set_intfdata(interface, NULL); + mutex_lock(&chaoskey_list_lock); mutex_lock(&dev->lock); dev->present = false; @@ -244,6 +247,7 @@ static void chaoskey_disconnect(struct usb_interface *interface) } else mutex_unlock(&dev->lock); + mutex_unlock(&chaoskey_list_lock); usb_dbg(interface, "disconnect done"); } @@ -251,6 +255,7 @@ static int chaoskey_open(struct inode *inode, struct file *file) { struct chaoskey *dev; struct usb_interface *interface; + int rv = 0; /* get the interface from minor number and driver information */ interface = usb_find_interface(&chaoskey_driver, iminor(inode)); @@ -266,18 +271,23 @@ static int chaoskey_open(struct inode *inode, struct file *file) } file->private_data = dev; + mutex_lock(&chaoskey_list_lock); mutex_lock(&dev->lock); - ++dev->open; + if (dev->present) + ++dev->open; + else + rv = -ENODEV; mutex_unlock(&dev->lock); + mutex_unlock(&chaoskey_list_lock); - usb_dbg(interface, "open success"); - return 0; + return rv; } static int chaoskey_release(struct inode *inode, struct file *file) { struct chaoskey *dev = file->private_data; struct usb_interface *interface; + int rv = 0; if (dev == NULL) return -ENODEV; @@ -286,14 +296,15 @@ static int chaoskey_release(struct inode *inode, struct file *file) usb_dbg(interface, "release"); + mutex_lock(&chaoskey_list_lock); mutex_lock(&dev->lock); usb_dbg(interface, "open count at release is %d", dev->open); if (dev->open <= 0) { usb_dbg(interface, "invalid open count (%d)", dev->open); - mutex_unlock(&dev->lock); - return -ENODEV; + rv = -ENODEV; + goto bail; } --dev->open; @@ -302,13 +313,15 @@ static int chaoskey_release(struct inode *inode, struct file *file) if (dev->open == 0) { mutex_unlock(&dev->lock); chaoskey_free(dev); - } else - mutex_unlock(&dev->lock); - } else - mutex_unlock(&dev->lock); - + goto destruction; + } + } +bail: + mutex_unlock(&dev->lock); +destruction: + mutex_unlock(&chaoskey_list_lock); usb_dbg(interface, "release success"); - return 0; + return rv; } static void chaos_read_callback(struct urb *urb) diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index 6d28467ce352..365c10069345 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -277,28 +277,45 @@ static ssize_t iowarrior_read(struct file *file, char __user *buffer, struct iowarrior *dev; int read_idx; int offset; + int retval; dev = file->private_data; + if (file->f_flags & O_NONBLOCK) { + retval = mutex_trylock(&dev->mutex); + if (!retval) + return -EAGAIN; + } else { + retval = mutex_lock_interruptible(&dev->mutex); + if (retval) + return -ERESTARTSYS; + } + /* verify that the device wasn't unplugged */ - if (!dev || !dev->present) - return -ENODEV; + if (!dev->present) { + retval = -ENODEV; + goto exit; + } dev_dbg(&dev->interface->dev, "minor %d, count = %zd\n", dev->minor, count); /* read count must be packet size (+ time stamp) */ if ((count != dev->report_size) - && (count != (dev->report_size + 1))) - return -EINVAL; + && (count != (dev->report_size + 1))) { + retval = -EINVAL; + goto exit; + } /* repeat until no buffer overrun in callback handler occur */ do { atomic_set(&dev->overflow_flag, 0); if ((read_idx = read_index(dev)) == -1) { /* queue empty */ - if (file->f_flags & O_NONBLOCK) - return -EAGAIN; + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + goto exit; + } else { //next line will return when there is either new data, or the device is unplugged int r = wait_event_interruptible(dev->read_wait, @@ -309,28 +326,37 @@ static ssize_t iowarrior_read(struct file *file, char __user *buffer, -1)); if (r) { //we were interrupted by a signal - return -ERESTART; + retval = -ERESTART; + goto exit; } if (!dev->present) { //The device was unplugged - return -ENODEV; + retval = -ENODEV; + goto exit; } if (read_idx == -1) { // Can this happen ??? - return 0; + retval = 0; + goto exit; } } } offset = read_idx * (dev->report_size + 1); if (copy_to_user(buffer, dev->read_queue + offset, count)) { - return -EFAULT; + retval = -EFAULT; + goto exit; } } while (atomic_read(&dev->overflow_flag)); read_idx = ++read_idx == MAX_INTERRUPT_BUFFER ? 0 : read_idx; atomic_set(&dev->read_idx, read_idx); + mutex_unlock(&dev->mutex); return count; + +exit: + mutex_unlock(&dev->mutex); + return retval; } /* @@ -885,7 +911,6 @@ error: static void iowarrior_disconnect(struct usb_interface *interface) { struct iowarrior *dev = usb_get_intfdata(interface); - int minor = dev->minor; usb_deregister_dev(interface, &iowarrior_class); @@ -909,9 +934,6 @@ static void iowarrior_disconnect(struct usb_interface *interface) mutex_unlock(&dev->mutex); iowarrior_delete(dev); } - - dev_info(&interface->dev, "I/O-Warror #%d now disconnected\n", - minor - IOWARRIOR_MINOR_BASE); } /* usb specific object needed to register this driver with the usb subsystem */ diff --git a/drivers/usb/misc/onboard_usb_dev.c b/drivers/usb/misc/onboard_usb_dev.c index 75dfdca04ff1..36b11127280f 100644 --- a/drivers/usb/misc/onboard_usb_dev.c +++ b/drivers/usb/misc/onboard_usb_dev.c @@ -473,7 +473,7 @@ static const struct dev_pm_ops __maybe_unused onboard_dev_pm_ops = { static struct platform_driver onboard_dev_driver = { .probe = onboard_dev_probe, - .remove_new = onboard_dev_remove, + .remove = onboard_dev_remove, .driver = { .name = "onboard-usb-dev", diff --git a/drivers/usb/misc/qcom_eud.c b/drivers/usb/misc/qcom_eud.c index 19906301a4eb..83079c414b4f 100644 --- a/drivers/usb/misc/qcom_eud.c +++ b/drivers/usb/misc/qcom_eud.c @@ -239,7 +239,7 @@ MODULE_DEVICE_TABLE(of, eud_dt_match); static struct platform_driver eud_driver = { .probe = eud_probe, - .remove_new = eud_remove, + .remove = eud_remove, .driver = { .name = "qcom_eud", .dev_groups = eud_groups, diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index 3b33e4878c60..322e59381b78 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -423,7 +423,7 @@ static struct platform_driver usb3503_platform_driver = { .pm = pm_ptr(&usb3503_platform_pm_ops), }, .probe = usb3503_platform_probe, - .remove_new = usb3503_platform_remove, + .remove = usb3503_platform_remove, }; static int __init usb3503_init(void) diff --git a/drivers/usb/misc/yurex.c b/drivers/usb/misc/yurex.c index 6aebc736a80c..70dff0db5354 100644 --- a/drivers/usb/misc/yurex.c +++ b/drivers/usb/misc/yurex.c @@ -441,7 +441,10 @@ static ssize_t yurex_write(struct file *file, const char __user *user_buffer, if (count == 0) goto error; - mutex_lock(&dev->io_mutex); + retval = mutex_lock_interruptible(&dev->io_mutex); + if (retval < 0) + return -EINTR; + if (dev->disconnected) { /* already disconnected */ mutex_unlock(&dev->io_mutex); retval = -ENODEV; diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c index 6858ed9fc3b2..7b5a431acb56 100644 --- a/drivers/usb/mtu3/mtu3_plat.c +++ b/drivers/usb/mtu3/mtu3_plat.c @@ -307,7 +307,7 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) if (otg_sx->role_sw_used || otg_sx->manual_drd_enabled) goto out; - if (of_property_read_bool(node, "extcon")) { + if (of_property_present(node, "extcon")) { otg_sx->edev = extcon_get_edev_by_phandle(ssusb->dev, 0); if (IS_ERR(otg_sx->edev)) { return dev_err_probe(dev, PTR_ERR(otg_sx->edev), @@ -621,7 +621,7 @@ MODULE_DEVICE_TABLE(of, mtu3_of_match); static struct platform_driver mtu3_driver = { .probe = mtu3_probe, - .remove_new = mtu3_remove, + .remove = mtu3_remove, .driver = { .name = MTU3_DRIVER_NAME, .pm = DEV_PM_OPS, diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 953094c1930c..f772aa272bea 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -633,7 +633,7 @@ MODULE_DEVICE_TABLE(of, da8xx_id_table); static struct platform_driver da8xx_driver = { .probe = da8xx_probe, - .remove_new = da8xx_remove, + .remove = da8xx_remove, .driver = { .name = "musb-da8xx", .pm = &da8xx_pm_ops, diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c index b38df9226278..acdeb1117cd3 100644 --- a/drivers/usb/musb/jz4740.c +++ b/drivers/usb/musb/jz4740.c @@ -325,7 +325,7 @@ MODULE_DEVICE_TABLE(of, jz4740_musb_of_match); static struct platform_driver jz4740_driver = { .probe = jz4740_probe, - .remove_new = jz4740_remove, + .remove = jz4740_remove, .driver = { .name = "musb-jz4740", .of_match_table = jz4740_musb_of_match, diff --git a/drivers/usb/musb/mediatek.c b/drivers/usb/musb/mediatek.c index 63c86c046b98..aa988d74b58d 100644 --- a/drivers/usb/musb/mediatek.c +++ b/drivers/usb/musb/mediatek.c @@ -523,7 +523,7 @@ MODULE_DEVICE_TABLE(of, mtk_musb_match); static struct platform_driver mtk_musb_driver = { .probe = mtk_musb_probe, - .remove_new = mtk_musb_remove, + .remove = mtk_musb_remove, .driver = { .name = "musb-mtk", .of_match_table = of_match_ptr(mtk_musb_match), diff --git a/drivers/usb/musb/mpfs.c b/drivers/usb/musb/mpfs.c index 00e13214aa76..7edc8429b274 100644 --- a/drivers/usb/musb/mpfs.c +++ b/drivers/usb/musb/mpfs.c @@ -369,7 +369,7 @@ MODULE_DEVICE_TABLE(of, mpfs_id_table); static struct platform_driver mpfs_musb_driver = { .probe = mpfs_probe, - .remove_new = mpfs_remove, + .remove = mpfs_remove, .driver = { .name = "mpfs-musb", .of_match_table = of_match_ptr(mpfs_id_table) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index b24adb5b399f..25fafde6003c 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -2953,7 +2953,7 @@ static struct platform_driver musb_driver = { .dev_groups = musb_groups, }, .probe = musb_probe, - .remove_new = musb_remove, + .remove = musb_remove, }; module_platform_driver(musb_driver); diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 9c7a8bbc0542..2542239ec64e 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -1032,7 +1032,7 @@ static SIMPLE_DEV_PM_OPS(dsps_pm_ops, dsps_suspend, dsps_resume); static struct platform_driver dsps_usbss_driver = { .probe = dsps_probe, - .remove_new = dsps_remove, + .remove = dsps_remove, .driver = { .name = "musb-dsps", .pm = &dsps_pm_ops, diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index b4a4c1df4e0d..2970967a4fd2 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -608,7 +608,7 @@ MODULE_DEVICE_TABLE(of, omap2430_id_table); static struct platform_driver omap2430_driver = { .probe = omap2430_probe, - .remove_new = omap2430_remove, + .remove = omap2430_remove, .driver = { .name = "musb-omap2430", .pm = DEV_PM_OPS, diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c index d54283fd026b..113d026ac2f8 100644 --- a/drivers/usb/musb/sunxi.c +++ b/drivers/usb/musb/sunxi.c @@ -859,7 +859,7 @@ MODULE_DEVICE_TABLE(of, sunxi_musb_match); static struct platform_driver sunxi_musb_driver = { .probe = sunxi_musb_probe, - .remove_new = sunxi_musb_remove, + .remove = sunxi_musb_remove, .driver = { .name = "musb-sunxi", .of_match_table = sunxi_musb_match, diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 461587629bf2..90b760a95e4e 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -1290,7 +1290,7 @@ static void tusb_remove(struct platform_device *pdev) static struct platform_driver tusb_driver = { .probe = tusb_probe, - .remove_new = tusb_remove, + .remove = tusb_remove, .driver = { .name = "musb-tusb", }, diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index c8d9d2a1d2f0..8c2a43d992f5 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -355,7 +355,7 @@ MODULE_DEVICE_TABLE(of, ux500_match); static struct platform_driver ux500_driver = { .probe = ux500_probe, - .remove_new = ux500_remove, + .remove = ux500_remove, .driver = { .name = "musb-ux500", .pm = &ux500_pm_ops, diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 408f47e39025..6a98aeeeae31 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -987,7 +987,7 @@ MODULE_DEVICE_TABLE(platform, ab8500_usb_devtype); static struct platform_driver ab8500_usb_driver = { .probe = ab8500_usb_probe, - .remove_new = ab8500_usb_remove, + .remove = ab8500_usb_remove, .id_table = ab8500_usb_devtype, .driver = { .name = "abx5x0-usb", diff --git a/drivers/usb/phy/phy-am335x.c b/drivers/usb/phy/phy-am335x.c index 6db88e00f127..ca9353dad71a 100644 --- a/drivers/usb/phy/phy-am335x.c +++ b/drivers/usb/phy/phy-am335x.c @@ -133,7 +133,7 @@ MODULE_DEVICE_TABLE(of, am335x_phy_ids); static struct platform_driver am335x_phy_driver = { .probe = am335x_phy_probe, - .remove_new = am335x_phy_remove, + .remove = am335x_phy_remove, .driver = { .name = "am335x-phy-driver", .pm = &am335x_pm_ops, diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c index c5c6b818998e..42c42e193232 100644 --- a/drivers/usb/phy/phy-fsl-usb.c +++ b/drivers/usb/phy/phy-fsl-usb.c @@ -1002,7 +1002,7 @@ static void fsl_otg_remove(struct platform_device *pdev) struct platform_driver fsl_otg_driver = { .probe = fsl_otg_probe, - .remove_new = fsl_otg_remove, + .remove = fsl_otg_remove, .driver = { .name = driver_name, }, diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c index e7d50e0a1612..6c3ececf9137 100644 --- a/drivers/usb/phy/phy-generic.c +++ b/drivers/usb/phy/phy-generic.c @@ -345,7 +345,7 @@ MODULE_DEVICE_TABLE(of, nop_xceiv_dt_ids); static struct platform_driver usb_phy_generic_driver = { .probe = usb_phy_generic_probe, - .remove_new = usb_phy_generic_remove, + .remove = usb_phy_generic_remove, .driver = { .name = "usb_phy_generic", .of_match_table = nop_xceiv_dt_ids, diff --git a/drivers/usb/phy/phy-gpio-vbus-usb.c b/drivers/usb/phy/phy-gpio-vbus-usb.c index 5428b2b67de1..ce09e789afd8 100644 --- a/drivers/usb/phy/phy-gpio-vbus-usb.c +++ b/drivers/usb/phy/phy-gpio-vbus-usb.c @@ -385,7 +385,7 @@ static struct platform_driver gpio_vbus_driver = { .of_match_table = gpio_vbus_of_match, }, .probe = gpio_vbus_probe, - .remove_new = gpio_vbus_remove, + .remove = gpio_vbus_remove, }; module_platform_driver(gpio_vbus_driver); diff --git a/drivers/usb/phy/phy-isp1301.c b/drivers/usb/phy/phy-isp1301.c index 993d7525a102..f9b5c411aee4 100644 --- a/drivers/usb/phy/phy-isp1301.c +++ b/drivers/usb/phy/phy-isp1301.c @@ -25,7 +25,7 @@ struct isp1301 { #define phy_to_isp(p) (container_of((p), struct isp1301, phy)) static const struct i2c_device_id isp1301_id[] = { - { "isp1301", 0 }, + { "isp1301" }, { } }; MODULE_DEVICE_TABLE(i2c, isp1301_id); diff --git a/drivers/usb/phy/phy-keystone.c b/drivers/usb/phy/phy-keystone.c index bd9a98ad1b30..51155c5513d3 100644 --- a/drivers/usb/phy/phy-keystone.c +++ b/drivers/usb/phy/phy-keystone.c @@ -103,7 +103,7 @@ MODULE_DEVICE_TABLE(of, keystone_usbphy_ids); static struct platform_driver keystone_usbphy_driver = { .probe = keystone_usbphy_probe, - .remove_new = keystone_usbphy_remove, + .remove = keystone_usbphy_remove, .driver = { .name = "keystone-usbphy", .of_match_table = keystone_usbphy_ids, diff --git a/drivers/usb/phy/phy-mv-usb.c b/drivers/usb/phy/phy-mv-usb.c index df7c27474a75..a7a102f2e163 100644 --- a/drivers/usb/phy/phy-mv-usb.c +++ b/drivers/usb/phy/phy-mv-usb.c @@ -867,7 +867,7 @@ static int mv_otg_resume(struct platform_device *pdev) static struct platform_driver mv_otg_driver = { .probe = mv_otg_probe, - .remove_new = mv_otg_remove, + .remove = mv_otg_remove, .driver = { .name = driver_name, .dev_groups = mv_otg_groups, diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index cc4156c1b148..7490f1798b46 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -952,7 +952,7 @@ static SIMPLE_DEV_PM_OPS(mxs_phy_pm, mxs_phy_system_suspend, static struct platform_driver mxs_phy_driver = { .probe = mxs_phy_probe, - .remove_new = mxs_phy_remove, + .remove = mxs_phy_remove, .driver = { .name = DRIVER_NAME, .of_match_table = mxs_phy_dt_ids, diff --git a/drivers/usb/phy/phy-tahvo.c b/drivers/usb/phy/phy-tahvo.c index 5cac31c6029b..ae7bf3ff89ee 100644 --- a/drivers/usb/phy/phy-tahvo.c +++ b/drivers/usb/phy/phy-tahvo.c @@ -424,7 +424,7 @@ static void tahvo_usb_remove(struct platform_device *pdev) static struct platform_driver tahvo_usb_driver = { .probe = tahvo_usb_probe, - .remove_new = tahvo_usb_remove, + .remove = tahvo_usb_remove, .driver = { .name = "tahvo-usb", .dev_groups = tahvo_groups, diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 4ea47e6f835b..bee222967f6b 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -1495,7 +1495,7 @@ static void tegra_usb_phy_remove(struct platform_device *pdev) static struct platform_driver tegra_usb_phy_driver = { .probe = tegra_usb_phy_probe, - .remove_new = tegra_usb_phy_remove, + .remove = tegra_usb_phy_remove, .driver = { .name = "tegra-phy", .of_match_table = tegra_usb_phy_id_table, diff --git a/drivers/usb/phy/phy-twl6030-usb.c b/drivers/usb/phy/phy-twl6030-usb.c index da09cff55abc..49d79c1257f3 100644 --- a/drivers/usb/phy/phy-twl6030-usb.c +++ b/drivers/usb/phy/phy-twl6030-usb.c @@ -432,7 +432,7 @@ MODULE_DEVICE_TABLE(of, twl6030_usb_id_table); static struct platform_driver twl6030_usb_driver = { .probe = twl6030_usb_probe, - .remove_new = twl6030_usb_remove, + .remove = twl6030_usb_remove, .driver = { .name = "twl6030_usb", .of_match_table = of_match_ptr(twl6030_usb_id_table), diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c index 06e0fb23566c..1ce134505cee 100644 --- a/drivers/usb/phy/phy.c +++ b/drivers/usb/phy/phy.c @@ -365,7 +365,7 @@ static int usb_add_extcon(struct usb_phy *x) { int ret; - if (of_property_read_bool(x->dev->of_node, "extcon")) { + if (of_property_present(x->dev->of_node, "extcon")) { x->edev = extcon_get_edev_by_phandle(x->dev, 0); if (IS_ERR(x->edev)) return PTR_ERR(x->edev); @@ -628,7 +628,7 @@ void devm_usb_put_phy(struct device *dev, struct usb_phy *phy) { int r; - r = devres_destroy(dev, devm_usb_phy_release, devm_usb_phy_match, phy); + r = devres_release(dev, devm_usb_phy_release, devm_usb_phy_match, phy); dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n"); } EXPORT_SYMBOL_GPL(devm_usb_put_phy); diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index edc43f169d49..935fc496fe94 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -632,7 +632,7 @@ static int usbhs_probe(struct platform_device *pdev) if (IS_ERR(priv->base)) return PTR_ERR(priv->base); - if (of_property_read_bool(dev_of_node(dev), "extcon")) { + if (of_property_present(dev_of_node(dev), "extcon")) { priv->edev = extcon_get_edev_by_phandle(dev, 0); if (IS_ERR(priv->edev)) return PTR_ERR(priv->edev); @@ -835,7 +835,7 @@ static struct platform_driver renesas_usbhs_driver = { .of_match_table = usbhs_of_match, }, .probe = usbhs_probe, - .remove_new = usbhs_remove, + .remove = usbhs_remove, }; module_platform_driver(renesas_usbhs_driver); diff --git a/drivers/usb/roles/intel-xhci-usb-role-switch.c b/drivers/usb/roles/intel-xhci-usb-role-switch.c index e5c6c413a075..cd2e026dcb22 100644 --- a/drivers/usb/roles/intel-xhci-usb-role-switch.c +++ b/drivers/usb/roles/intel-xhci-usb-role-switch.c @@ -217,7 +217,7 @@ static struct platform_driver intel_xhci_usb_driver = { }, .id_table = intel_xhci_usb_table, .probe = intel_xhci_usb_probe, - .remove_new = intel_xhci_usb_remove, + .remove = intel_xhci_usb_remove, }; module_platform_driver(intel_xhci_usb_driver); diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c index a4bfbecbf16c..fd46e81388d2 100644 --- a/drivers/usb/storage/ene_ub6250.c +++ b/drivers/usb/storage/ene_ub6250.c @@ -737,7 +737,7 @@ static int sd_scsi_write(struct us_data *us, struct scsi_cmnd *srb) memset(bcb, 0, sizeof(struct bulk_cb_wrap)); bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = blenByte; - bcb->Flags = 0x00; + bcb->Flags = US_BULK_FLAG_OUT; bcb->CDB[0] = 0xF0; bcb->CDB[5] = (unsigned char)(bnByte); bcb->CDB[4] = (unsigned char)(bnByte>>8); @@ -1163,7 +1163,7 @@ static int ms_read_copyblock(struct us_data *us, u16 oldphy, u16 newphy, memset(bcb, 0, sizeof(struct bulk_cb_wrap)); bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = 0x200*len; - bcb->Flags = 0x00; + bcb->Flags = US_BULK_FLAG_OUT; bcb->CDB[0] = 0xF0; bcb->CDB[1] = 0x08; bcb->CDB[4] = (unsigned char)(oldphy); @@ -1759,7 +1759,7 @@ static int ms_scsi_write(struct us_data *us, struct scsi_cmnd *srb) memset(bcb, 0, sizeof(struct bulk_cb_wrap)); bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = blenByte; - bcb->Flags = 0x00; + bcb->Flags = US_BULK_FLAG_OUT; bcb->CDB[0] = 0xF0; bcb->CDB[1] = 0x04; bcb->CDB[5] = (unsigned char)(bn); @@ -1931,7 +1931,7 @@ static int ene_load_bincode(struct us_data *us, unsigned char flag) memset(bcb, 0, sizeof(struct bulk_cb_wrap)); bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = sd_fw->size; - bcb->Flags = 0x00; + bcb->Flags = US_BULK_FLAG_OUT; bcb->CDB[0] = 0xEF; result = ene_send_scsi_cmd(us, FDIR_WRITE, buf, 0); diff --git a/drivers/usb/storage/realtek_cr.c b/drivers/usb/storage/realtek_cr.c index 0c423916d7bf..54ffff86c6fa 100644 --- a/drivers/usb/storage/realtek_cr.c +++ b/drivers/usb/storage/realtek_cr.c @@ -212,7 +212,7 @@ static int rts51x_bulk_transport(struct us_data *us, u8 lun, /* set up the command wrapper */ bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = cpu_to_le32(buf_len); - bcb->Flags = (dir == DMA_FROM_DEVICE) ? US_BULK_FLAG_IN : 0; + bcb->Flags = (dir == DMA_FROM_DEVICE) ? US_BULK_FLAG_IN : US_BULK_FLAG_OUT; bcb->Tag = ++us->tag; bcb->Lun = lun; bcb->Length = cmd_len; @@ -301,7 +301,7 @@ static int rts51x_bulk_transport_special(struct us_data *us, u8 lun, /* set up the command wrapper */ bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = cpu_to_le32(buf_len); - bcb->Flags = (dir == DMA_FROM_DEVICE) ? US_BULK_FLAG_IN : 0; + bcb->Flags = (dir == DMA_FROM_DEVICE) ? US_BULK_FLAG_IN : US_BULK_FLAG_OUT; bcb->Tag = ++us->tag; bcb->Lun = lun; bcb->Length = cmd_len; diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 7449e379077a..9d767f6bf722 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -1133,7 +1133,7 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us) bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = cpu_to_le32(transfer_length); bcb->Flags = srb->sc_data_direction == DMA_FROM_DEVICE ? - US_BULK_FLAG_IN : 0; + US_BULK_FLAG_IN : US_BULK_FLAG_OUT; bcb->Tag = ++us->tag; bcb->Lun = srb->device->lun; if (us->fflags & US_FL_SCM_MULT_TARG) diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c index 92cc1b136120..2f03190a9873 100644 --- a/drivers/usb/typec/altmodes/displayport.c +++ b/drivers/usb/typec/altmodes/displayport.c @@ -729,7 +729,7 @@ int dp_altmode_probe(struct typec_altmode *alt) /* FIXME: Port can only be DFP_U. */ - /* Make sure we have compatiple pin configurations */ + /* Make sure we have compatible pin configurations */ if (!(DP_CAP_PIN_ASSIGN_DFP_D(port->vdo) & DP_CAP_PIN_ASSIGN_UFP_D(alt->vdo)) && !(DP_CAP_PIN_ASSIGN_UFP_D(port->vdo) & diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index d61b4c74648d..4b3047e055a3 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -219,6 +219,13 @@ static ssize_t usb_power_delivery_revision_show(struct device *dev, char *buf); static DEVICE_ATTR_RO(usb_power_delivery_revision); +static const char * const usb_modes[] = { + [USB_MODE_NONE] = "none", + [USB_MODE_USB2] = "usb2", + [USB_MODE_USB3] = "usb3", + [USB_MODE_USB4] = "usb4" +}; + /* ------------------------------------------------------------------------- */ /* Alternate Modes */ @@ -614,6 +621,75 @@ EXPORT_SYMBOL_GPL(typec_unregister_altmode); /* ------------------------------------------------------------------------- */ /* Type-C Partners */ +/** + * typec_partner_set_usb_mode - Assign active USB Mode for the partner + * @partner: USB Type-C partner + * @mode: USB Mode (USB2, USB3 or USB4) + * + * The port drivers can use this function to assign the active USB Mode to + * @partner. The USB Mode can change for example due to Data Reset. + */ +void typec_partner_set_usb_mode(struct typec_partner *partner, enum usb_mode mode) +{ + if (!partner || partner->usb_mode == mode) + return; + + partner->usb_capability |= BIT(mode - 1); + partner->usb_mode = mode; + sysfs_notify(&partner->dev.kobj, NULL, "usb_mode"); +} +EXPORT_SYMBOL_GPL(typec_partner_set_usb_mode); + +static ssize_t +usb_mode_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct typec_partner *partner = to_typec_partner(dev); + int len = 0; + int i; + + for (i = USB_MODE_USB2; i < USB_MODE_USB4 + 1; i++) { + if (!(BIT(i - 1) & partner->usb_capability)) + continue; + + if (i == partner->usb_mode) + len += sysfs_emit_at(buf, len, "[%s] ", usb_modes[i]); + else + len += sysfs_emit_at(buf, len, "%s ", usb_modes[i]); + } + + sysfs_emit_at(buf, len - 1, "\n"); + + return len; +} + +static ssize_t usb_mode_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct typec_partner *partner = to_typec_partner(dev); + struct typec_port *port = to_typec_port(dev->parent); + int mode; + int ret; + + if (!port->ops || !port->ops->enter_usb_mode) + return -EOPNOTSUPP; + + mode = sysfs_match_string(usb_modes, buf); + if (mode < 0) + return mode; + + if (mode == partner->usb_mode) + return size; + + ret = port->ops->enter_usb_mode(port, mode); + if (ret) + return ret; + + typec_partner_set_usb_mode(partner, mode); + + return size; +} +static DEVICE_ATTR_RW(usb_mode); + static ssize_t accessory_mode_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -660,6 +736,7 @@ static struct attribute *typec_partner_attrs[] = { &dev_attr_supports_usb_power_delivery.attr, &dev_attr_number_of_alternate_modes.attr, &dev_attr_type.attr, + &dev_attr_usb_mode.attr, &dev_attr_usb_power_delivery_revision.attr, NULL }; @@ -667,6 +744,14 @@ static struct attribute *typec_partner_attrs[] = { static umode_t typec_partner_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) { struct typec_partner *partner = to_typec_partner(kobj_to_dev(kobj)); + struct typec_port *port = to_typec_port(partner->dev.parent); + + if (attr == &dev_attr_usb_mode.attr) { + if (!partner->usb_capability) + return 0; + if (!port->ops || !port->ops->enter_usb_mode) + return 0444; + } if (attr == &dev_attr_number_of_alternate_modes.attr) { if (partner->num_altmodes < 0) @@ -740,10 +825,33 @@ static void typec_partner_unlink_device(struct typec_partner *partner, struct de */ int typec_partner_set_identity(struct typec_partner *partner) { - if (!partner->identity) + u8 usb_capability = partner->usb_capability; + struct device *dev = &partner->dev; + struct usb_pd_identity *id; + + id = get_pd_identity(dev); + if (!id) return -EINVAL; - typec_report_identity(&partner->dev); + if (to_typec_port(dev->parent)->data_role == TYPEC_HOST) { + u32 devcap = PD_VDO_UFP_DEVCAP(id->vdo[0]); + + if (devcap & (DEV_USB2_CAPABLE | DEV_USB2_BILLBOARD)) + usb_capability |= USB_CAPABILITY_USB2; + if (devcap & DEV_USB3_CAPABLE) + usb_capability |= USB_CAPABILITY_USB3; + if (devcap & DEV_USB4_CAPABLE) + usb_capability |= USB_CAPABILITY_USB4; + } else { + usb_capability = PD_VDO_DFP_HOSTCAP(id->vdo[0]); + } + + if (partner->usb_capability != usb_capability) { + partner->usb_capability = usb_capability; + sysfs_notify(&dev->kobj, NULL, "usb_mode"); + } + + typec_report_identity(dev); return 0; } EXPORT_SYMBOL_GPL(typec_partner_set_identity); @@ -913,6 +1021,7 @@ struct typec_partner *typec_register_partner(struct typec_port *port, partner->usb_pd = desc->usb_pd; partner->accessory = desc->accessory; partner->num_altmodes = -1; + partner->usb_capability = desc->usb_capability; partner->pd_revision = desc->pd_revision; partner->svdm_version = port->cap->svdm_version; partner->attach = desc->attach; @@ -932,6 +1041,15 @@ struct typec_partner *typec_register_partner(struct typec_port *port, partner->dev.type = &typec_partner_dev_type; dev_set_name(&partner->dev, "%s-partner", dev_name(&port->dev)); + if (port->usb2_dev) { + partner->usb_capability |= USB_CAPABILITY_USB2; + partner->usb_mode = USB_MODE_USB2; + } + if (port->usb3_dev) { + partner->usb_capability |= USB_CAPABILITY_USB2 | USB_CAPABILITY_USB3; + partner->usb_mode = USB_MODE_USB3; + } + ret = device_register(&partner->dev); if (ret) { dev_err(&port->dev, "failed to register partner (%d)\n", ret); @@ -1293,6 +1411,67 @@ EXPORT_SYMBOL_GPL(typec_unregister_cable); /* USB Type-C ports */ /** + * typec_port_set_usb_mode - Set the operational USB mode for the port + * @port: USB Type-C port + * @mode: USB Mode (USB2, USB3 or USB4) + * + * @mode will be used with the next Enter_USB message. Existing connections are + * not affected. + */ +void typec_port_set_usb_mode(struct typec_port *port, enum usb_mode mode) +{ + port->usb_mode = mode; +} +EXPORT_SYMBOL_GPL(typec_port_set_usb_mode); + +static ssize_t +usb_capability_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct typec_port *port = to_typec_port(dev); + int len = 0; + int i; + + for (i = USB_MODE_USB2; i < USB_MODE_USB4 + 1; i++) { + if (!(BIT(i - 1) & port->cap->usb_capability)) + continue; + + if (i == port->usb_mode) + len += sysfs_emit_at(buf, len, "[%s] ", usb_modes[i]); + else + len += sysfs_emit_at(buf, len, "%s ", usb_modes[i]); + } + + sysfs_emit_at(buf, len - 1, "\n"); + + return len; +} + +static ssize_t +usb_capability_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct typec_port *port = to_typec_port(dev); + int ret = 0; + int mode; + + if (!port->ops || !port->ops->default_usb_mode_set) + return -EOPNOTSUPP; + + mode = sysfs_match_string(usb_modes, buf); + if (mode < 0) + return mode; + + ret = port->ops->default_usb_mode_set(port, mode); + if (ret) + return ret; + + port->usb_mode = mode; + + return size; +} +static DEVICE_ATTR_RW(usb_capability); + +/** * typec_port_set_usb_power_delivery - Assign USB PD for port. * @port: USB Type-C port. * @pd: USB PD instance. @@ -1760,6 +1939,7 @@ static struct attribute *typec_attrs[] = { &dev_attr_vconn_source.attr, &dev_attr_port_type.attr, &dev_attr_orientation.attr, + &dev_attr_usb_capability.attr, NULL, }; @@ -1793,6 +1973,11 @@ static umode_t typec_attr_is_visible(struct kobject *kobj, if (port->cap->orientation_aware) return 0444; return 0; + } else if (attr == &dev_attr_usb_capability.attr) { + if (!port->cap->usb_capability) + return 0; + if (!port->ops || !port->ops->default_usb_mode_set) + return 0444; } return attr->mode; @@ -1864,13 +2049,18 @@ static void typec_partner_attach(struct typec_connector *con, struct device *dev struct typec_port *port = container_of(con, struct typec_port, con); struct typec_partner *partner = typec_get_partner(port); struct usb_device *udev = to_usb_device(dev); + enum usb_mode usb_mode; - if (udev->speed < USB_SPEED_SUPER) + if (udev->speed < USB_SPEED_SUPER) { + usb_mode = USB_MODE_USB2; port->usb2_dev = dev; - else + } else { + usb_mode = USB_MODE_USB3; port->usb3_dev = dev; + } if (partner) { + typec_partner_set_usb_mode(partner, usb_mode); typec_partner_link_device(partner, dev); put_device(&partner->dev); } @@ -2293,7 +2483,7 @@ void typec_port_register_altmodes(struct typec_port *port, const struct typec_altmode_ops *ops, void *drvdata, struct typec_altmode **altmodes, size_t n) { - struct fwnode_handle *altmodes_node, *child; + struct fwnode_handle *child; struct typec_altmode_desc desc; struct typec_altmode *alt; size_t index = 0; @@ -2301,7 +2491,9 @@ void typec_port_register_altmodes(struct typec_port *port, u32 vdo; int ret; - altmodes_node = device_get_named_child_node(&port->dev, "altmodes"); + struct fwnode_handle *altmodes_node __free(fwnode_handle) = + device_get_named_child_node(&port->dev, "altmodes"); + if (!altmodes_node) return; /* No altmodes specified */ @@ -2431,6 +2623,13 @@ struct typec_port *typec_register_port(struct device *parent, port->con.attach = typec_partner_attach; port->con.deattach = typec_partner_deattach; + if (cap->usb_capability & USB_CAPABILITY_USB4) + port->usb_mode = USB_MODE_USB4; + else if (cap->usb_capability & USB_CAPABILITY_USB3) + port->usb_mode = USB_MODE_USB3; + else if (cap->usb_capability & USB_CAPABILITY_USB2) + port->usb_mode = USB_MODE_USB2; + device_initialize(&port->dev); port->dev.class = &typec_class; port->dev.parent = parent; diff --git a/drivers/usb/typec/class.h b/drivers/usb/typec/class.h index 7485cdb9dd20..b3076a24ad2e 100644 --- a/drivers/usb/typec/class.h +++ b/drivers/usb/typec/class.h @@ -35,6 +35,8 @@ struct typec_partner { int num_altmodes; u16 pd_revision; /* 0300H = "3.0" */ enum usb_pd_svdm_ver svdm_version; + enum usb_mode usb_mode; + u8 usb_capability; struct usb_power_delivery *pd; @@ -55,6 +57,7 @@ struct typec_port { enum typec_role vconn_role; enum typec_pwr_opmode pwr_opmode; enum typec_port_type port_type; + enum usb_mode usb_mode; struct mutex port_type_lock; enum typec_orientation orientation; diff --git a/drivers/usb/typec/mux/Kconfig b/drivers/usb/typec/mux/Kconfig index ce7db6ad3057..67381b4ef4f6 100644 --- a/drivers/usb/typec/mux/Kconfig +++ b/drivers/usb/typec/mux/Kconfig @@ -66,6 +66,15 @@ config TYPEC_MUX_PTN36502 Say Y or M if your system has a NXP PTN36502 Type-C redriver chip found on some devices with a Type-C port. +config TYPEC_MUX_TUSB1046 + tristate "TI TUSB1046 Type-C crosspoint switch driver" + depends on I2C + help + Driver for the Texas Instruments TUSB1046-DCI crosspoint switch. + Supports flipping USB-C SuperSpeed lanes to adapt to orientation + changes, as well as muxing DisplayPort and sideband signals to a + common Type-C connector. + config TYPEC_MUX_WCD939X_USBSS tristate "Qualcomm WCD939x USBSS Analog Audio Switch driver" depends on I2C diff --git a/drivers/usb/typec/mux/Makefile b/drivers/usb/typec/mux/Makefile index bb96f30267af..60879446da93 100644 --- a/drivers/usb/typec/mux/Makefile +++ b/drivers/usb/typec/mux/Makefile @@ -7,4 +7,5 @@ obj-$(CONFIG_TYPEC_MUX_INTEL_PMC) += intel_pmc_mux.o obj-$(CONFIG_TYPEC_MUX_IT5205) += it5205.o obj-$(CONFIG_TYPEC_MUX_NB7VPQ904M) += nb7vpq904m.o obj-$(CONFIG_TYPEC_MUX_PTN36502) += ptn36502.o +obj-$(CONFIG_TYPEC_MUX_TUSB1046) += tusb1046.o obj-$(CONFIG_TYPEC_MUX_WCD939X_USBSS) += wcd939x-usbss.o diff --git a/drivers/usb/typec/mux/gpio-sbu-mux.c b/drivers/usb/typec/mux/gpio-sbu-mux.c index 8902102c05a8..1834f1a2dd9d 100644 --- a/drivers/usb/typec/mux/gpio-sbu-mux.c +++ b/drivers/usb/typec/mux/gpio-sbu-mux.c @@ -159,7 +159,7 @@ MODULE_DEVICE_TABLE(of, gpio_sbu_mux_match); static struct platform_driver gpio_sbu_mux_driver = { .probe = gpio_sbu_mux_probe, - .remove_new = gpio_sbu_mux_remove, + .remove = gpio_sbu_mux_remove, .driver = { .name = "gpio_sbu_mux", .of_match_table = gpio_sbu_mux_match, diff --git a/drivers/usb/typec/mux/intel_pmc_mux.c b/drivers/usb/typec/mux/intel_pmc_mux.c index 46b4c5c3a6be..5dfe95754394 100644 --- a/drivers/usb/typec/mux/intel_pmc_mux.c +++ b/drivers/usb/typec/mux/intel_pmc_mux.c @@ -828,7 +828,7 @@ static struct platform_driver pmc_usb_driver = { .acpi_match_table = ACPI_PTR(pmc_usb_acpi_ids), }, .probe = pmc_usb_probe, - .remove_new = pmc_usb_remove, + .remove = pmc_usb_remove, }; static int __init pmc_usb_init(void) diff --git a/drivers/usb/typec/mux/tusb1046.c b/drivers/usb/typec/mux/tusb1046.c new file mode 100644 index 000000000000..b4f45c217b59 --- /dev/null +++ b/drivers/usb/typec/mux/tusb1046.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for the TUSB1046-DCI USB Type-C crosspoint switch + * + * Copyright (C) 2024 Bootlin + */ + +#include <linux/bits.h> +#include <linux/i2c.h> +#include <linux/usb/typec_mux.h> +#include <linux/usb/typec_dp.h> +#include <linux/usb/typec_altmode.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/err.h> +#include <linux/of_device.h> +#include <linux/device.h> +#include <linux/mutex.h> + +#define TUSB1046_REG_GENERAL 0xa + +/* General register bits */ +#define TUSB1046_GENERAL_FLIPSEL BIT(2) +#define TUSB1046_GENERAL_CTLSEL GENMASK(1, 0) + +/* Mux modes */ +#define TUSB1046_CTLSEL_DISABLED 0x0 +#define TUSB1046_CTLSEL_USB3 0x1 +#define TUSB1046_CTLSEL_4LANE_DP 0x2 +#define TUSB1046_CTLSEL_USB3_AND_2LANE_DP 0x3 + +struct tusb1046_priv { + struct i2c_client *client; + struct typec_switch_dev *sw; + struct typec_mux_dev *mux; + + /* Lock General register during accesses */ + struct mutex general_reg_lock; +}; + +static int tusb1046_mux_set(struct typec_mux_dev *mux, + struct typec_mux_state *state) +{ + struct tusb1046_priv *priv = typec_mux_get_drvdata(mux); + struct i2c_client *client = priv->client; + struct device *dev = &client->dev; + int mode, val, ret = 0; + + if (state->mode >= TYPEC_STATE_MODAL && + state->alt->svid != USB_TYPEC_DP_SID) + return -EINVAL; + + dev_dbg(dev, "mux mode requested: %lu\n", state->mode); + + mutex_lock(&priv->general_reg_lock); + + val = i2c_smbus_read_byte_data(client, TUSB1046_REG_GENERAL); + if (val < 0) { + dev_err(dev, "failed to read ctlsel status, err %d\n", val); + ret = val; + goto out_unlock; + } + + switch (state->mode) { + case TYPEC_STATE_USB: + mode = TUSB1046_CTLSEL_USB3; + break; + case TYPEC_DP_STATE_C: + case TYPEC_DP_STATE_E: + mode = TUSB1046_CTLSEL_4LANE_DP; + break; + case TYPEC_DP_STATE_D: + mode = TUSB1046_CTLSEL_USB3_AND_2LANE_DP; + break; + case TYPEC_STATE_SAFE: + default: + mode = TUSB1046_CTLSEL_DISABLED; + break; + } + + val &= ~TUSB1046_GENERAL_CTLSEL; + val |= mode; + + ret = i2c_smbus_write_byte_data(client, TUSB1046_REG_GENERAL, val); + +out_unlock: + mutex_unlock(&priv->general_reg_lock); + return ret; +} + +static int tusb1046_switch_set(struct typec_switch_dev *sw, + enum typec_orientation orientation) +{ + struct tusb1046_priv *priv = typec_switch_get_drvdata(sw); + struct i2c_client *client = priv->client; + struct device *dev = &client->dev; + int val, ret = 0; + + dev_dbg(dev, "setting USB3.0 lane flip for orientation %d\n", orientation); + + mutex_lock(&priv->general_reg_lock); + + val = i2c_smbus_read_byte_data(client, TUSB1046_REG_GENERAL); + if (val < 0) { + dev_err(dev, "failed to read flipsel status, err %d\n", val); + ret = val; + goto out_unlock; + } + + if (orientation == TYPEC_ORIENTATION_REVERSE) + val |= TUSB1046_GENERAL_FLIPSEL; + else + val &= ~TUSB1046_GENERAL_FLIPSEL; + + ret = i2c_smbus_write_byte_data(client, TUSB1046_REG_GENERAL, val); + +out_unlock: + mutex_unlock(&priv->general_reg_lock); + return ret; +} + +static int tusb1046_i2c_probe(struct i2c_client *client) +{ + struct typec_switch_desc sw_desc = { }; + struct typec_mux_desc mux_desc = { }; + struct device *dev = &client->dev; + struct tusb1046_priv *priv; + int ret = 0; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return dev_err_probe(dev, -ENOMEM, "failed to allocate driver data\n"); + + priv->client = client; + + mutex_init(&priv->general_reg_lock); + + sw_desc.drvdata = priv; + sw_desc.fwnode = dev_fwnode(dev); + sw_desc.set = tusb1046_switch_set; + + priv->sw = typec_switch_register(dev, &sw_desc); + if (IS_ERR(priv->sw)) { + ret = dev_err_probe(dev, PTR_ERR(priv->sw), "failed to register type-c switch\n"); + goto err_destroy_mutex; + } + + mux_desc.drvdata = priv; + mux_desc.fwnode = dev_fwnode(dev); + mux_desc.set = tusb1046_mux_set; + + priv->mux = typec_mux_register(dev, &mux_desc); + if (IS_ERR(priv->mux)) { + ret = dev_err_probe(dev, PTR_ERR(priv->mux), "failed to register type-c mux\n"); + goto err_unregister_switch; + } + + i2c_set_clientdata(client, priv); + + return 0; + +err_unregister_switch: + typec_switch_unregister(priv->sw); +err_destroy_mutex: + mutex_destroy(&priv->general_reg_lock); + return ret; +} + +static void tusb1046_i2c_remove(struct i2c_client *client) +{ + struct tusb1046_priv *priv = i2c_get_clientdata(client); + + typec_switch_unregister(priv->sw); + typec_mux_unregister(priv->mux); + mutex_destroy(&priv->general_reg_lock); +} + +static const struct of_device_id tusb1046_match_table[] = { + {.compatible = "ti,tusb1046"}, + {}, +}; + +static struct i2c_driver tusb1046_driver = { + .driver = { + .name = "tusb1046", + .of_match_table = tusb1046_match_table, + }, + .probe = tusb1046_i2c_probe, + .remove = tusb1046_i2c_remove, +}; + +module_i2c_driver(tusb1046_driver); + +MODULE_DESCRIPTION("TUSB1046 USB Type-C switch driver"); +MODULE_AUTHOR("Romain Gantois <romain.gantois@bootlin.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/typec/stusb160x.c b/drivers/usb/typec/stusb160x.c index f3140fc04c12..6d85b25b40bc 100644 --- a/drivers/usb/typec/stusb160x.c +++ b/drivers/usb/typec/stusb160x.c @@ -633,9 +633,8 @@ MODULE_DEVICE_TABLE(of, stusb160x_of_match); static int stusb160x_probe(struct i2c_client *client) { + const struct regmap_config *regmap_config; struct stusb160x *chip; - const struct of_device_id *match; - struct regmap_config *regmap_config; struct fwnode_handle *fwnode; int ret; @@ -645,8 +644,8 @@ static int stusb160x_probe(struct i2c_client *client) i2c_set_clientdata(client, chip); - match = i2c_of_match_device(stusb160x_of_match, client); - regmap_config = (struct regmap_config *)match->data; + regmap_config = i2c_get_match_data(client); + chip->regmap = devm_regmap_init_i2c(client, regmap_config); if (IS_ERR(chip->regmap)) { ret = PTR_ERR(chip->regmap); diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c index 501eddb294e4..3766790c1548 100644 --- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c +++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c @@ -93,8 +93,10 @@ static int qcom_pmic_typec_probe(struct platform_device *pdev) return -EINVAL; bridge_dev = devm_drm_dp_hpd_bridge_alloc(tcpm->dev, to_of_node(tcpm->tcpc.fwnode)); - if (IS_ERR(bridge_dev)) - return PTR_ERR(bridge_dev); + if (IS_ERR(bridge_dev)) { + ret = PTR_ERR(bridge_dev); + goto fwnode_remove; + } tcpm->tcpm_port = tcpm_register_port(tcpm->dev, &tcpm->tcpc); if (IS_ERR(tcpm->tcpm_port)) { @@ -123,7 +125,7 @@ port_stop: port_unregister: tcpm_unregister_port(tcpm->tcpm_port); fwnode_remove: - fwnode_remove_software_node(tcpm->tcpc.fwnode); + fwnode_handle_put(tcpm->tcpc.fwnode); return ret; } @@ -135,7 +137,7 @@ static void qcom_pmic_typec_remove(struct platform_device *pdev) tcpm->pdphy_stop(tcpm); tcpm->port_stop(tcpm); tcpm_unregister_port(tcpm->tcpm_port); - fwnode_remove_software_node(tcpm->tcpc.fwnode); + fwnode_handle_put(tcpm->tcpc.fwnode); } static const struct pmic_typec_resources pm8150b_typec_res = { @@ -161,7 +163,7 @@ static struct platform_driver qcom_pmic_typec_driver = { .of_match_table = qcom_pmic_typec_table, }, .probe = qcom_pmic_typec_probe, - .remove_new = qcom_pmic_typec_remove, + .remove = qcom_pmic_typec_remove, }; module_platform_driver(qcom_pmic_typec_driver); diff --git a/drivers/usb/typec/tcpm/tcpci_mt6360.c b/drivers/usb/typec/tcpm/tcpci_mt6360.c index 02b7fd302265..881ffacbfd77 100644 --- a/drivers/usb/typec/tcpm/tcpci_mt6360.c +++ b/drivers/usb/typec/tcpm/tcpci_mt6360.c @@ -221,7 +221,7 @@ static struct platform_driver mt6360_tcpc_driver = { .of_match_table = mt6360_tcpc_of_id, }, .probe = mt6360_tcpc_probe, - .remove_new = mt6360_tcpc_remove, + .remove = mt6360_tcpc_remove, }; module_platform_driver(mt6360_tcpc_driver); diff --git a/drivers/usb/typec/tcpm/tcpci_mt6370.c b/drivers/usb/typec/tcpm/tcpci_mt6370.c index 9cda1005ef01..1479f961772d 100644 --- a/drivers/usb/typec/tcpm/tcpci_mt6370.c +++ b/drivers/usb/typec/tcpm/tcpci_mt6370.c @@ -196,7 +196,7 @@ static struct platform_driver mt6370_tcpc_driver = { .of_match_table = mt6370_tcpc_devid_table, }, .probe = mt6370_tcpc_probe, - .remove_new = mt6370_tcpc_remove, + .remove = mt6370_tcpc_remove, }; module_platform_driver(mt6370_tcpc_driver); diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index fc619478200f..0334a41c710c 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -310,6 +310,17 @@ struct pd_data { unsigned int operating_snk_mw; }; +/* + * @sink_wait_cap_time: Deadline (in ms) for tTypeCSinkWaitCap timer + * @ps_src_wait_off_time: Deadline (in ms) for tPSSourceOff timer + * @cc_debounce_time: Deadline (in ms) for tCCDebounce timer + */ +struct pd_timings { + u32 sink_wait_cap_time; + u32 ps_src_off_time; + u32 cc_debounce_time; +}; + struct tcpm_port { struct device *dev; @@ -552,6 +563,9 @@ struct tcpm_port { */ unsigned int message_id_prime; unsigned int rx_msgid_prime; + + /* Timer deadline values configured at runtime */ + struct pd_timings timings; #ifdef CONFIG_DEBUG_FS struct dentry *dentry; struct mutex logbuffer_lock; /* log buffer access lock */ @@ -4515,7 +4529,8 @@ static inline enum tcpm_state hard_reset_state(struct tcpm_port *port) return ERROR_RECOVERY; if (port->pwr_role == TYPEC_SOURCE) return SRC_UNATTACHED; - if (port->state == SNK_WAIT_CAPABILITIES_TIMEOUT) + if (port->state == SNK_WAIT_CAPABILITIES || + port->state == SNK_WAIT_CAPABILITIES_TIMEOUT) return SNK_READY; return SNK_UNATTACHED; } @@ -4639,15 +4654,15 @@ static void run_state_machine(struct tcpm_port *port) case SRC_ATTACH_WAIT: if (tcpm_port_is_debug(port)) tcpm_set_state(port, DEBUG_ACC_ATTACHED, - PD_T_CC_DEBOUNCE); + port->timings.cc_debounce_time); else if (tcpm_port_is_audio(port)) tcpm_set_state(port, AUDIO_ACC_ATTACHED, - PD_T_CC_DEBOUNCE); + port->timings.cc_debounce_time); else if (tcpm_port_is_source(port) && port->vbus_vsafe0v) tcpm_set_state(port, tcpm_try_snk(port) ? SNK_TRY : SRC_ATTACHED, - PD_T_CC_DEBOUNCE); + port->timings.cc_debounce_time); break; case SNK_TRY: @@ -4698,7 +4713,7 @@ static void run_state_machine(struct tcpm_port *port) } break; case SRC_TRYWAIT_DEBOUNCE: - tcpm_set_state(port, SRC_ATTACHED, PD_T_CC_DEBOUNCE); + tcpm_set_state(port, SRC_ATTACHED, port->timings.cc_debounce_time); break; case SRC_TRYWAIT_UNATTACHED: tcpm_set_state(port, SNK_UNATTACHED, 0); @@ -4901,7 +4916,7 @@ static void run_state_machine(struct tcpm_port *port) (port->cc1 != TYPEC_CC_OPEN && port->cc2 == TYPEC_CC_OPEN)) tcpm_set_state(port, SNK_DEBOUNCED, - PD_T_CC_DEBOUNCE); + port->timings.cc_debounce_time); else if (tcpm_port_is_disconnected(port)) tcpm_set_state(port, SNK_UNATTACHED, PD_T_PD_DEBOUNCE); @@ -4941,7 +4956,7 @@ static void run_state_machine(struct tcpm_port *port) break; case SNK_TRYWAIT: tcpm_set_cc(port, TYPEC_CC_RD); - tcpm_set_state(port, SNK_TRYWAIT_VBUS, PD_T_CC_DEBOUNCE); + tcpm_set_state(port, SNK_TRYWAIT_VBUS, port->timings.cc_debounce_time); break; case SNK_TRYWAIT_VBUS: /* @@ -5014,7 +5029,7 @@ static void run_state_machine(struct tcpm_port *port) break; case SNK_DISCOVERY_DEBOUNCE: tcpm_set_state(port, SNK_DISCOVERY_DEBOUNCE_DONE, - PD_T_CC_DEBOUNCE); + port->timings.cc_debounce_time); break; case SNK_DISCOVERY_DEBOUNCE_DONE: if (!tcpm_port_is_disconnected(port) && @@ -5041,10 +5056,14 @@ static void run_state_machine(struct tcpm_port *port) if (port->vbus_never_low) { port->vbus_never_low = false; tcpm_set_state(port, SNK_SOFT_RESET, - PD_T_SINK_WAIT_CAP); + port->timings.sink_wait_cap_time); } else { + if (!port->self_powered) + upcoming_state = SNK_WAIT_CAPABILITIES_TIMEOUT; + else + upcoming_state = hard_reset_state(port); tcpm_set_state(port, SNK_WAIT_CAPABILITIES_TIMEOUT, - PD_T_SINK_WAIT_CAP); + port->timings.sink_wait_cap_time); } break; case SNK_WAIT_CAPABILITIES_TIMEOUT: @@ -5066,7 +5085,8 @@ static void run_state_machine(struct tcpm_port *port) if (tcpm_pd_send_control(port, PD_CTRL_GET_SOURCE_CAP, TCPC_TX_SOP)) tcpm_set_state_cond(port, hard_reset_state(port), 0); else - tcpm_set_state(port, hard_reset_state(port), PD_T_SINK_WAIT_CAP); + tcpm_set_state(port, hard_reset_state(port), + port->timings.sink_wait_cap_time); break; case SNK_NEGOTIATE_CAPABILITIES: port->pd_capable = true; @@ -5203,7 +5223,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, ACC_UNATTACHED, 0); break; case AUDIO_ACC_DEBOUNCE: - tcpm_set_state(port, ACC_UNATTACHED, PD_T_CC_DEBOUNCE); + tcpm_set_state(port, ACC_UNATTACHED, port->timings.cc_debounce_time); break; /* Hard_Reset states */ @@ -5270,7 +5290,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, SRC_UNATTACHED, PD_T_PS_SOURCE_ON); break; case SNK_HARD_RESET_SINK_OFF: - /* Do not discharge/disconnect during hard reseet */ + /* Do not discharge/disconnect during hard reset */ tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB, false, 0); memset(&port->pps_data, 0, sizeof(port->pps_data)); tcpm_set_vconn(port, false); @@ -5420,7 +5440,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, ERROR_RECOVERY, 0); break; case FR_SWAP_SNK_SRC_TRANSITION_TO_OFF: - tcpm_set_state(port, ERROR_RECOVERY, PD_T_PS_SOURCE_OFF); + tcpm_set_state(port, ERROR_RECOVERY, port->timings.ps_src_off_time); break; case FR_SWAP_SNK_SRC_NEW_SINK_READY: if (port->vbus_source) @@ -5475,7 +5495,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_cc(port, TYPEC_CC_RD); /* allow CC debounce */ tcpm_set_state(port, PR_SWAP_SRC_SNK_SOURCE_OFF_CC_DEBOUNCED, - PD_T_CC_DEBOUNCE); + port->timings.cc_debounce_time); break; case PR_SWAP_SRC_SNK_SOURCE_OFF_CC_DEBOUNCED: /* @@ -5510,7 +5530,7 @@ static void run_state_machine(struct tcpm_port *port) port->pps_data.active, 0); tcpm_set_charge(port, false); tcpm_set_state(port, hard_reset_state(port), - PD_T_PS_SOURCE_OFF); + port->timings.ps_src_off_time); break; case PR_SWAP_SNK_SRC_SOURCE_ON: tcpm_enable_auto_vbus_discharge(port, true); @@ -5666,7 +5686,7 @@ static void run_state_machine(struct tcpm_port *port) case PORT_RESET_WAIT_OFF: tcpm_set_state(port, tcpm_default_state(port), - port->vbus_present ? PD_T_PS_SOURCE_OFF : 0); + port->vbus_present ? port->timings.ps_src_off_time : 0); break; /* AMS intermediate state */ @@ -6066,7 +6086,7 @@ static void _tcpm_pd_vbus_off(struct tcpm_port *port) break; case SNK_ATTACH_WAIT: case SNK_DEBOUNCED: - /* Do nothing, as TCPM is still waiting for vbus to reaach VSAFE5V to connect */ + /* Do nothing, as TCPM is still waiting for vbus to reach VSAFE5V to connect */ break; case SNK_NEGOTIATE_CAPABILITIES: @@ -6157,7 +6177,7 @@ static void _tcpm_pd_vbus_vsafe0v(struct tcpm_port *port) case SRC_ATTACH_WAIT: if (tcpm_port_is_source(port)) tcpm_set_state(port, tcpm_try_snk(port) ? SNK_TRY : SRC_ATTACHED, - PD_T_CC_DEBOUNCE); + port->timings.cc_debounce_time); break; case SRC_STARTUP: case SRC_SEND_CAPABILITIES: @@ -7053,6 +7073,30 @@ err_unregister: return ret; } +static void tcpm_fw_get_timings(struct tcpm_port *port, struct fwnode_handle *fwnode) +{ + int ret; + u32 val; + + ret = fwnode_property_read_u32(fwnode, "sink-wait-cap-time-ms", &val); + if (!ret) + port->timings.sink_wait_cap_time = val; + else + port->timings.sink_wait_cap_time = PD_T_SINK_WAIT_CAP; + + ret = fwnode_property_read_u32(fwnode, "ps-source-off-time-ms", &val); + if (!ret) + port->timings.ps_src_off_time = val; + else + port->timings.ps_src_off_time = PD_T_PS_SOURCE_OFF; + + ret = fwnode_property_read_u32(fwnode, "cc-debounce-time-ms", &val); + if (!ret) + port->timings.cc_debounce_time = val; + else + port->timings.cc_debounce_time = PD_T_CC_DEBOUNCE; +} + static int tcpm_fw_get_caps(struct tcpm_port *port, struct fwnode_handle *fwnode) { struct fwnode_handle *capabilities, *child, *caps = NULL; @@ -7367,7 +7411,7 @@ static int tcpm_psy_get_input_power_limit(struct tcpm_port *port, src_mv = pdo_fixed_voltage(pdo); src_ma = pdo_max_current(pdo); tmp = src_mv * src_ma; - max_src_uw = tmp > max_src_uw ? tmp : max_src_uw; + max_src_uw = max(tmp, max_src_uw); } } @@ -7610,6 +7654,8 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) if (err < 0) goto out_destroy_wq; + tcpm_fw_get_timings(port, tcpc->fwnode); + port->try_role = port->typec_caps.prefer_role; port->typec_caps.revision = 0x0120; /* Type-C spec release 1.2 */ diff --git a/drivers/usb/typec/tcpm/wcove.c b/drivers/usb/typec/tcpm/wcove.c index cf719307b3f6..8eb1db0c0e36 100644 --- a/drivers/usb/typec/tcpm/wcove.c +++ b/drivers/usb/typec/tcpm/wcove.c @@ -691,7 +691,7 @@ static struct platform_driver wcove_typec_driver = { .name = "bxt_wcove_usbc", }, .probe = wcove_typec_probe, - .remove_new = wcove_typec_remove, + .remove = wcove_typec_remove, }; module_platform_driver(wcove_typec_driver); diff --git a/drivers/usb/typec/ucsi/debugfs.c b/drivers/usb/typec/ucsi/debugfs.c index f67733cecfdf..83ff23086d79 100644 --- a/drivers/usb/typec/ucsi/debugfs.c +++ b/drivers/usb/typec/ucsi/debugfs.c @@ -32,6 +32,7 @@ static int ucsi_cmd(void *data, u64 val) case UCSI_SET_UOR: case UCSI_SET_PDR: case UCSI_CONNECTOR_RESET: + case UCSI_SET_SINK_PATH: ret = ucsi_send_command(ucsi, val, NULL, 0); break; case UCSI_GET_CAPABILITY: diff --git a/drivers/usb/typec/ucsi/psy.c b/drivers/usb/typec/ucsi/psy.c index 1c631c7855a9..62ac69730405 100644 --- a/drivers/usb/typec/ucsi/psy.c +++ b/drivers/usb/typec/ucsi/psy.c @@ -55,8 +55,8 @@ static int ucsi_psy_get_online(struct ucsi_connector *con, union power_supply_propval *val) { val->intval = UCSI_PSY_OFFLINE; - if (con->status.flags & UCSI_CONSTAT_CONNECTED && - (con->status.flags & UCSI_CONSTAT_PWR_DIR) == TYPEC_SINK) + if (UCSI_CONSTAT(con, CONNECTED) && + (UCSI_CONSTAT(con, PWR_DIR) == TYPEC_SINK)) val->intval = UCSI_PSY_FIXED_ONLINE; return 0; } @@ -66,7 +66,7 @@ static int ucsi_psy_get_voltage_min(struct ucsi_connector *con, { u32 pdo; - switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) { + switch (UCSI_CONSTAT(con, PWR_OPMODE)) { case UCSI_CONSTAT_PWR_OPMODE_PD: pdo = con->src_pdos[0]; val->intval = pdo_fixed_voltage(pdo) * 1000; @@ -89,7 +89,7 @@ static int ucsi_psy_get_voltage_max(struct ucsi_connector *con, { u32 pdo; - switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) { + switch (UCSI_CONSTAT(con, PWR_OPMODE)) { case UCSI_CONSTAT_PWR_OPMODE_PD: if (con->num_pdos > 0) { pdo = con->src_pdos[con->num_pdos - 1]; @@ -117,7 +117,7 @@ static int ucsi_psy_get_voltage_now(struct ucsi_connector *con, int index; u32 pdo; - switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) { + switch (UCSI_CONSTAT(con, PWR_OPMODE)) { case UCSI_CONSTAT_PWR_OPMODE_PD: index = rdo_index(con->rdo); if (index > 0) { @@ -145,7 +145,7 @@ static int ucsi_psy_get_current_max(struct ucsi_connector *con, { u32 pdo; - switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) { + switch (UCSI_CONSTAT(con, PWR_OPMODE)) { case UCSI_CONSTAT_PWR_OPMODE_PD: if (con->num_pdos > 0) { pdo = con->src_pdos[con->num_pdos - 1]; @@ -173,9 +173,7 @@ static int ucsi_psy_get_current_max(struct ucsi_connector *con, static int ucsi_psy_get_current_now(struct ucsi_connector *con, union power_supply_propval *val) { - u16 flags = con->status.flags; - - if (UCSI_CONSTAT_PWR_OPMODE(flags) == UCSI_CONSTAT_PWR_OPMODE_PD) + if (UCSI_CONSTAT(con, PWR_OPMODE) == UCSI_CONSTAT_PWR_OPMODE_PD) val->intval = rdo_op_current(con->rdo) * 1000; else val->intval = 0; @@ -185,11 +183,9 @@ static int ucsi_psy_get_current_now(struct ucsi_connector *con, static int ucsi_psy_get_usb_type(struct ucsi_connector *con, union power_supply_propval *val) { - u16 flags = con->status.flags; - val->intval = POWER_SUPPLY_USB_TYPE_C; - if (flags & UCSI_CONSTAT_CONNECTED && - UCSI_CONSTAT_PWR_OPMODE(flags) == UCSI_CONSTAT_PWR_OPMODE_PD) + if (UCSI_CONSTAT(con, CONNECTED) && + UCSI_CONSTAT(con, PWR_OPMODE) == UCSI_CONSTAT_PWR_OPMODE_PD) val->intval = POWER_SUPPLY_USB_TYPE_PD; return 0; @@ -197,18 +193,18 @@ static int ucsi_psy_get_usb_type(struct ucsi_connector *con, static int ucsi_psy_get_charge_type(struct ucsi_connector *con, union power_supply_propval *val) { - if (!(con->status.flags & UCSI_CONSTAT_CONNECTED)) { + if (!(UCSI_CONSTAT(con, CONNECTED))) { val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; return 0; } /* The Battery Charging Cabability Status field is only valid in sink role. */ - if ((con->status.flags & UCSI_CONSTAT_PWR_DIR) != TYPEC_SINK) { + if (UCSI_CONSTAT(con, PWR_DIR) != TYPEC_SINK) { val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; return 0; } - switch (UCSI_CONSTAT_BC_STATUS(con->status.pwr_status)) { + switch (UCSI_CONSTAT(con, BC_STATUS)) { case UCSI_CONSTAT_BC_NOMINAL_CHARGING: val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD; break; diff --git a/drivers/usb/typec/ucsi/trace.h b/drivers/usb/typec/ucsi/trace.h index a0d3a934d3d9..41701dee7056 100644 --- a/drivers/usb/typec/ucsi/trace.h +++ b/drivers/usb/typec/ucsi/trace.h @@ -40,8 +40,8 @@ DEFINE_EVENT(ucsi_log_command, ucsi_reset_ppm, ); DECLARE_EVENT_CLASS(ucsi_log_connector_status, - TP_PROTO(int port, struct ucsi_connector_status *status), - TP_ARGS(port, status), + TP_PROTO(int port, struct ucsi_connector *con), + TP_ARGS(port, con), TP_STRUCT__entry( __field(int, port) __field(u16, change) @@ -55,14 +55,14 @@ DECLARE_EVENT_CLASS(ucsi_log_connector_status, ), TP_fast_assign( __entry->port = port - 1; - __entry->change = status->change; - __entry->opmode = UCSI_CONSTAT_PWR_OPMODE(status->flags); - __entry->connected = !!(status->flags & UCSI_CONSTAT_CONNECTED); - __entry->pwr_dir = !!(status->flags & UCSI_CONSTAT_PWR_DIR); - __entry->partner_flags = UCSI_CONSTAT_PARTNER_FLAGS(status->flags); - __entry->partner_type = UCSI_CONSTAT_PARTNER_TYPE(status->flags); - __entry->request_data_obj = status->request_data_obj; - __entry->bc_status = UCSI_CONSTAT_BC_STATUS(status->pwr_status); + __entry->change = UCSI_CONSTAT(con, CHANGE); + __entry->opmode = UCSI_CONSTAT(con, PWR_OPMODE); + __entry->connected = UCSI_CONSTAT(con, CONNECTED); + __entry->pwr_dir = UCSI_CONSTAT(con, PWR_DIR); + __entry->partner_flags = UCSI_CONSTAT(con, PARTNER_FLAGS); + __entry->partner_type = UCSI_CONSTAT(con, PARTNER_TYPE); + __entry->request_data_obj = UCSI_CONSTAT(con, RDO); + __entry->bc_status = UCSI_CONSTAT(con, BC_STATUS); ), TP_printk("port%d status: change=%04x, opmode=%x, connected=%d, " "sourcing=%d, partner_flags=%x, partner_type=%x, " @@ -73,13 +73,13 @@ DECLARE_EVENT_CLASS(ucsi_log_connector_status, ); DEFINE_EVENT(ucsi_log_connector_status, ucsi_connector_change, - TP_PROTO(int port, struct ucsi_connector_status *status), - TP_ARGS(port, status) + TP_PROTO(int port, struct ucsi_connector *con), + TP_ARGS(port, con) ); DEFINE_EVENT(ucsi_log_connector_status, ucsi_register_port, - TP_PROTO(int port, struct ucsi_connector_status *status), - TP_ARGS(port, status) + TP_PROTO(int port, struct ucsi_connector *con), + TP_ARGS(port, con) ); DECLARE_EVENT_CLASS(ucsi_log_register_altmode, diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index e0f3925e401b..974a441155e1 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -648,6 +648,17 @@ static void ucsi_unregister_altmodes(struct ucsi_connector *con, u8 recipient) } } +static int ucsi_get_connector_status(struct ucsi_connector *con, bool conn_ack) +{ + u64 command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num); + size_t size = min(UCSI_GET_CONNECTOR_STATUS_SIZE, UCSI_MAX_DATA_LENGTH(con->ucsi)); + int ret; + + ret = ucsi_send_command_common(con->ucsi, command, &con->status, size, conn_ack); + + return ret < 0 ? ret : 0; +} + static int ucsi_read_pdos(struct ucsi_connector *con, enum typec_role role, int is_partner, u32 *pdos, int offset, int num_pdos) @@ -658,8 +669,7 @@ static int ucsi_read_pdos(struct ucsi_connector *con, if (is_partner && ucsi->quirks & UCSI_NO_PARTNER_PDOS && - ((con->status.flags & UCSI_CONSTAT_PWR_DIR) || - !is_source(role))) + (UCSI_CONSTAT(con, PWR_DIR) || !is_source(role))) return 0; command = UCSI_COMMAND(UCSI_GET_PDOS) | UCSI_CONNECTOR_NUMBER(con->num); @@ -973,6 +983,7 @@ static void ucsi_unregister_cable(struct ucsi_connector *con) static int ucsi_check_connector_capability(struct ucsi_connector *con) { + u64 pd_revision; u64 command; int ret; @@ -986,17 +997,17 @@ static int ucsi_check_connector_capability(struct ucsi_connector *con) return ret; } - typec_partner_set_pd_revision(con->partner, - UCSI_CONCAP_FLAG_PARTNER_PD_MAJOR_REV_AS_BCD(con->cap.flags)); + pd_revision = UCSI_CONCAP(con, PARTNER_PD_REVISION_V2_1); + typec_partner_set_pd_revision(con->partner, UCSI_SPEC_REVISION_TO_BCD(pd_revision)); return ret; } static void ucsi_pwr_opmode_change(struct ucsi_connector *con) { - switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) { + switch (UCSI_CONSTAT(con, PWR_OPMODE)) { case UCSI_CONSTAT_PWR_OPMODE_PD: - con->rdo = con->status.request_data_obj; + con->rdo = UCSI_CONSTAT(con, RDO); typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_PD); ucsi_partner_task(con, ucsi_get_src_pdos, 30, 0); ucsi_partner_task(con, ucsi_check_altmodes, 30, HZ); @@ -1020,7 +1031,7 @@ static void ucsi_pwr_opmode_change(struct ucsi_connector *con) static int ucsi_register_partner(struct ucsi_connector *con) { - u8 pwr_opmode = UCSI_CONSTAT_PWR_OPMODE(con->status.flags); + u8 pwr_opmode = UCSI_CONSTAT(con, PWR_OPMODE); struct typec_partner_desc desc; struct typec_partner *partner; @@ -1029,7 +1040,7 @@ static int ucsi_register_partner(struct ucsi_connector *con) memset(&desc, 0, sizeof(desc)); - switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) { + switch (UCSI_CONSTAT(con, PARTNER_TYPE)) { case UCSI_CONSTAT_PARTNER_TYPE_DEBUG: desc.accessory = TYPEC_ACCESSORY_DEBUG; break; @@ -1047,6 +1058,11 @@ static int ucsi_register_partner(struct ucsi_connector *con) desc.identity = &con->partner_identity; desc.usb_pd = pwr_opmode == UCSI_CONSTAT_PWR_OPMODE_PD; + if (con->ucsi->version >= UCSI_VERSION_2_1) { + u64 pd_revision = UCSI_CONCAP(con, PARTNER_PD_REVISION_V2_1); + desc.pd_revision = UCSI_SPEC_REVISION_TO_BCD(pd_revision); + } + partner = typec_register_partner(con->port, &desc); if (IS_ERR(partner)) { dev_err(con->ucsi->dev, @@ -1057,6 +1073,13 @@ static int ucsi_register_partner(struct ucsi_connector *con) con->partner = partner; + if (con->ucsi->version >= UCSI_VERSION_3_0 && + UCSI_CONSTAT(con, PARTNER_FLAG_USB4_GEN4)) + typec_partner_set_usb_mode(partner, USB_MODE_USB4); + else if (con->ucsi->version >= UCSI_VERSION_2_0 && + UCSI_CONSTAT(con, PARTNER_FLAG_USB4_GEN3)) + typec_partner_set_usb_mode(partner, USB_MODE_USB4); + return 0; } @@ -1081,7 +1104,7 @@ static void ucsi_partner_change(struct ucsi_connector *con) enum usb_role u_role = USB_ROLE_NONE; int ret; - switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) { + switch (UCSI_CONSTAT(con, PARTNER_TYPE)) { case UCSI_CONSTAT_PARTNER_TYPE_UFP: case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP: u_role = USB_ROLE_HOST; @@ -1097,8 +1120,8 @@ static void ucsi_partner_change(struct ucsi_connector *con) break; } - if (con->status.flags & UCSI_CONSTAT_CONNECTED) { - switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) { + if (UCSI_CONSTAT(con, CONNECTED)) { + switch (UCSI_CONSTAT(con, PARTNER_TYPE)) { case UCSI_CONSTAT_PARTNER_TYPE_DEBUG: typec_set_mode(con->port, TYPEC_MODE_DEBUG); break; @@ -1106,14 +1129,13 @@ static void ucsi_partner_change(struct ucsi_connector *con) typec_set_mode(con->port, TYPEC_MODE_AUDIO); break; default: - if (UCSI_CONSTAT_PARTNER_FLAGS(con->status.flags) == - UCSI_CONSTAT_PARTNER_FLAG_USB) + if (UCSI_CONSTAT(con, PARTNER_FLAG_USB)) typec_set_mode(con->port, TYPEC_STATE_USB); } } /* Only notify USB controller if partner supports USB data */ - if (!(UCSI_CONSTAT_PARTNER_FLAGS(con->status.flags) & UCSI_CONSTAT_PARTNER_FLAG_USB)) + if (!(UCSI_CONSTAT(con, PARTNER_FLAG_USB))) u_role = USB_ROLE_NONE; ret = usb_role_switch_set_role(con->usb_role_sw, u_role); @@ -1124,21 +1146,18 @@ static void ucsi_partner_change(struct ucsi_connector *con) static int ucsi_check_connection(struct ucsi_connector *con) { - u8 prev_flags = con->status.flags; - u64 command; + u8 prev_state = UCSI_CONSTAT(con, CONNECTED); int ret; - command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num); - ret = ucsi_send_command(con->ucsi, command, &con->status, sizeof(con->status)); - if (ret < 0) { + ret = ucsi_get_connector_status(con, false); + if (ret) { dev_err(con->ucsi->dev, "GET_CONNECTOR_STATUS failed (%d)\n", ret); return ret; } - if (con->status.flags == prev_flags) - return 0; - - if (con->status.flags & UCSI_CONSTAT_CONNECTED) { + if (UCSI_CONSTAT(con, CONNECTED)) { + if (prev_state) + return 0; ucsi_register_partner(con); ucsi_pwr_opmode_change(con); ucsi_partner_change(con); @@ -1194,7 +1213,7 @@ static void ucsi_handle_connector_change(struct work_struct *work) work); struct ucsi *ucsi = con->ucsi; enum typec_role role; - u64 command; + u16 change; int ret; mutex_lock(&con->lock); @@ -1203,25 +1222,23 @@ static void ucsi_handle_connector_change(struct work_struct *work) dev_err_once(ucsi->dev, "%s entered without EVENT_PENDING\n", __func__); - command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num); - - ret = ucsi_send_command_common(ucsi, command, &con->status, - sizeof(con->status), true); - if (ret < 0) { + ret = ucsi_get_connector_status(con, true); + if (ret) { dev_err(ucsi->dev, "%s: GET_CONNECTOR_STATUS failed (%d)\n", __func__, ret); clear_bit(EVENT_PENDING, &con->ucsi->flags); goto out_unlock; } - trace_ucsi_connector_change(con->num, &con->status); + trace_ucsi_connector_change(con->num, con); if (ucsi->ops->connector_status) ucsi->ops->connector_status(con); - role = !!(con->status.flags & UCSI_CONSTAT_PWR_DIR); + change = UCSI_CONSTAT(con, CHANGE); + role = UCSI_CONSTAT(con, PWR_DIR); - if (con->status.change & UCSI_CONSTAT_POWER_DIR_CHANGE) { + if (change & UCSI_CONSTAT_POWER_DIR_CHANGE) { typec_set_pwr_role(con->port, role); /* Complete pending power role swap */ @@ -1229,12 +1246,12 @@ static void ucsi_handle_connector_change(struct work_struct *work) complete(&con->complete); } - if (con->status.change & UCSI_CONSTAT_CONNECT_CHANGE) { + if (change & UCSI_CONSTAT_CONNECT_CHANGE) { typec_set_pwr_role(con->port, role); ucsi_port_psy_changed(con); ucsi_partner_change(con); - if (con->status.flags & UCSI_CONSTAT_CONNECTED) { + if (UCSI_CONSTAT(con, CONNECTED)) { ucsi_register_partner(con); ucsi_partner_task(con, ucsi_check_connection, 1, HZ); if (con->ucsi->cap.features & UCSI_CAP_GET_PD_MESSAGE) @@ -1242,8 +1259,7 @@ static void ucsi_handle_connector_change(struct work_struct *work) if (con->ucsi->cap.features & UCSI_CAP_CABLE_DETAILS) ucsi_partner_task(con, ucsi_check_cable, 1, HZ); - if (UCSI_CONSTAT_PWR_OPMODE(con->status.flags) == - UCSI_CONSTAT_PWR_OPMODE_PD) { + if (UCSI_CONSTAT(con, PWR_OPMODE) == UCSI_CONSTAT_PWR_OPMODE_PD) { ucsi_partner_task(con, ucsi_register_partner_pdos, 1, HZ); ucsi_partner_task(con, ucsi_check_connector_capability, 1, HZ); } @@ -1252,11 +1268,10 @@ static void ucsi_handle_connector_change(struct work_struct *work) } } - if (con->status.change & UCSI_CONSTAT_POWER_OPMODE_CHANGE || - con->status.change & UCSI_CONSTAT_POWER_LEVEL_CHANGE) + if (change & (UCSI_CONSTAT_POWER_OPMODE_CHANGE | UCSI_CONSTAT_POWER_LEVEL_CHANGE)) ucsi_pwr_opmode_change(con); - if (con->partner && con->status.change & UCSI_CONSTAT_PARTNER_CHANGE) { + if (con->partner && (change & UCSI_CONSTAT_PARTNER_CHANGE)) { ucsi_partner_change(con); /* Complete pending data role swap */ @@ -1264,10 +1279,10 @@ static void ucsi_handle_connector_change(struct work_struct *work) complete(&con->complete); } - if (con->status.change & UCSI_CONSTAT_CAM_CHANGE) + if (change & UCSI_CONSTAT_CAM_CHANGE) ucsi_partner_task(con, ucsi_check_altmodes, 1, HZ); - if (con->status.change & UCSI_CONSTAT_BC_CHANGE) + if (change & UCSI_CONSTAT_BC_CHANGE) ucsi_port_psy_changed(con); out_unlock: @@ -1427,7 +1442,7 @@ static int ucsi_dr_swap(struct typec_port *port, enum typec_data_role role) goto out_unlock; } - partner_type = UCSI_CONSTAT_PARTNER_TYPE(con->status.flags); + partner_type = UCSI_CONSTAT(con, PARTNER_TYPE); if ((partner_type == UCSI_CONSTAT_PARTNER_TYPE_DFP && role == TYPEC_DEVICE) || (partner_type == UCSI_CONSTAT_PARTNER_TYPE_UFP && @@ -1471,7 +1486,7 @@ static int ucsi_pr_swap(struct typec_port *port, enum typec_role role) goto out_unlock; } - cur_role = !!(con->status.flags & UCSI_CONSTAT_PWR_DIR); + cur_role = UCSI_CONSTAT(con, PWR_DIR); if (cur_role == role) goto out_unlock; @@ -1494,8 +1509,7 @@ static int ucsi_pr_swap(struct typec_port *port, enum typec_role role) mutex_lock(&con->lock); /* Something has gone wrong while swapping the role */ - if (UCSI_CONSTAT_PWR_OPMODE(con->status.flags) != - UCSI_CONSTAT_PWR_OPMODE_PD) { + if (UCSI_CONSTAT(con, PWR_OPMODE) != UCSI_CONSTAT_PWR_OPMODE_PD) { ucsi_reset_connector(con, true); ret = -EPROTO; } @@ -1563,19 +1577,18 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) if (ret < 0) goto out_unlock; - if (con->cap.op_mode & UCSI_CONCAP_OPMODE_DRP) + if (UCSI_CONCAP(con, OPMODE_DRP)) cap->data = TYPEC_PORT_DRD; - else if (con->cap.op_mode & UCSI_CONCAP_OPMODE_DFP) + else if (UCSI_CONCAP(con, OPMODE_DFP)) cap->data = TYPEC_PORT_DFP; - else if (con->cap.op_mode & UCSI_CONCAP_OPMODE_UFP) + else if (UCSI_CONCAP(con, OPMODE_UFP)) cap->data = TYPEC_PORT_UFP; - if ((con->cap.flags & UCSI_CONCAP_FLAG_PROVIDER) && - (con->cap.flags & UCSI_CONCAP_FLAG_CONSUMER)) + if (UCSI_CONCAP(con, PROVIDER) && UCSI_CONCAP(con, CONSUMER)) cap->type = TYPEC_PORT_DRP; - else if (con->cap.flags & UCSI_CONCAP_FLAG_PROVIDER) + else if (UCSI_CONCAP(con, PROVIDER)) cap->type = TYPEC_PORT_SRC; - else if (con->cap.flags & UCSI_CONCAP_FLAG_CONSUMER) + else if (UCSI_CONCAP(con, CONSUMER)) cap->type = TYPEC_PORT_SNK; cap->revision = ucsi->cap.typec_version; @@ -1583,11 +1596,18 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) cap->svdm_version = SVDM_VER_2_0; cap->prefer_role = TYPEC_NO_PREFERRED_ROLE; - if (con->cap.op_mode & UCSI_CONCAP_OPMODE_AUDIO_ACCESSORY) + if (UCSI_CONCAP(con, OPMODE_AUDIO_ACCESSORY)) *accessory++ = TYPEC_ACCESSORY_AUDIO; - if (con->cap.op_mode & UCSI_CONCAP_OPMODE_DEBUG_ACCESSORY) + if (UCSI_CONCAP(con, OPMODE_DEBUG_ACCESSORY)) *accessory = TYPEC_ACCESSORY_DEBUG; + if (UCSI_CONCAP_USB2_SUPPORT(con)) + cap->usb_capability |= USB_CAPABILITY_USB2; + if (UCSI_CONCAP_USB3_SUPPORT(con)) + cap->usb_capability |= USB_CAPABILITY_USB3; + if (UCSI_CONCAP_USB4_SUPPORT(con)) + cap->usb_capability |= USB_CAPABILITY_USB4; + cap->driver_data = con; cap->ops = &ucsi_ops; @@ -1617,19 +1637,16 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) } /* Get the status */ - command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num); - ret = ucsi_send_command(ucsi, command, &con->status, sizeof(con->status)); - if (ret < 0) { + ret = ucsi_get_connector_status(con, false); + if (ret) { dev_err(ucsi->dev, "con%d: failed to get status\n", con->num); - ret = 0; goto out; } - ret = 0; /* ucsi_send_command() returns length on success */ if (ucsi->ops->connector_status) ucsi->ops->connector_status(con); - switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) { + switch (UCSI_CONSTAT(con, PARTNER_TYPE)) { case UCSI_CONSTAT_PARTNER_TYPE_UFP: case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP: u_role = USB_ROLE_HOST; @@ -1646,9 +1663,8 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) } /* Check if there is already something connected */ - if (con->status.flags & UCSI_CONSTAT_CONNECTED) { - typec_set_pwr_role(con->port, - !!(con->status.flags & UCSI_CONSTAT_PWR_DIR)); + if (UCSI_CONSTAT(con, CONNECTED)) { + typec_set_pwr_role(con->port, UCSI_CONSTAT(con, PWR_DIR)); ucsi_register_partner(con); ucsi_pwr_opmode_change(con); ucsi_port_psy_changed(con); @@ -1659,7 +1675,7 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) } /* Only notify USB controller if partner supports USB data */ - if (!(UCSI_CONSTAT_PARTNER_FLAGS(con->status.flags) & UCSI_CONSTAT_PARTNER_FLAG_USB)) + if (!(UCSI_CONSTAT(con, PARTNER_FLAG_USB))) u_role = USB_ROLE_NONE; ret = usb_role_switch_set_role(con->usb_role_sw, u_role); @@ -1669,16 +1685,14 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) ret = 0; } - if (con->partner && - UCSI_CONSTAT_PWR_OPMODE(con->status.flags) == - UCSI_CONSTAT_PWR_OPMODE_PD) { + if (con->partner && UCSI_CONSTAT(con, PWR_OPMODE) == UCSI_CONSTAT_PWR_OPMODE_PD) { ucsi_register_device_pdos(con); ucsi_get_src_pdos(con); ucsi_check_altmodes(con); ucsi_check_connector_capability(con); } - trace_ucsi_register_port(con->num, &con->status); + trace_ucsi_register_port(con->num, con); out: fwnode_handle_put(cap->fwnode); @@ -1761,7 +1775,7 @@ static int ucsi_init(struct ucsi *ucsi) /* Get PPM capabilities */ command = UCSI_GET_CAPABILITY; - ret = ucsi_send_command(ucsi, command, &ucsi->cap, sizeof(ucsi->cap)); + ret = ucsi_send_command(ucsi, command, &ucsi->cap, UCSI_GET_CAPABILITY_SIZE); if (ret < 0) goto err_reset; diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index 1cf5aad4c23a..5ff369c24a2f 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -4,6 +4,7 @@ #define __DRIVER_USB_TYPEC_UCSI_H #include <linux/bitops.h> +#include <linux/bitmap.h> #include <linux/completion.h> #include <linux/device.h> #include <linux/power_supply.h> @@ -95,26 +96,31 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num); /* -------------------------------------------------------------------------- */ /* Commands */ -#define UCSI_PPM_RESET 0x01 -#define UCSI_CANCEL 0x02 -#define UCSI_CONNECTOR_RESET 0x03 -#define UCSI_ACK_CC_CI 0x04 -#define UCSI_SET_NOTIFICATION_ENABLE 0x05 -#define UCSI_GET_CAPABILITY 0x06 -#define UCSI_GET_CONNECTOR_CAPABILITY 0x07 -#define UCSI_SET_UOM 0x08 -#define UCSI_SET_UOR 0x09 -#define UCSI_SET_PDM 0x0a -#define UCSI_SET_PDR 0x0b -#define UCSI_GET_ALTERNATE_MODES 0x0c -#define UCSI_GET_CAM_SUPPORTED 0x0d -#define UCSI_GET_CURRENT_CAM 0x0e -#define UCSI_SET_NEW_CAM 0x0f -#define UCSI_GET_PDOS 0x10 -#define UCSI_GET_CABLE_PROPERTY 0x11 -#define UCSI_GET_CONNECTOR_STATUS 0x12 -#define UCSI_GET_ERROR_STATUS 0x13 -#define UCSI_GET_PD_MESSAGE 0x15 +#define UCSI_PPM_RESET 0x01 +#define UCSI_CANCEL 0x02 +#define UCSI_CONNECTOR_RESET 0x03 +#define UCSI_ACK_CC_CI 0x04 +#define UCSI_SET_NOTIFICATION_ENABLE 0x05 +#define UCSI_GET_CAPABILITY 0x06 +#define UCSI_GET_CAPABILITY_SIZE 128 +#define UCSI_GET_CONNECTOR_CAPABILITY 0x07 +#define UCSI_GET_CONNECTOR_CAPABILITY_SIZE 32 +#define UCSI_SET_UOM 0x08 +#define UCSI_SET_UOR 0x09 +#define UCSI_SET_PDM 0x0a +#define UCSI_SET_PDR 0x0b +#define UCSI_GET_ALTERNATE_MODES 0x0c +#define UCSI_GET_CAM_SUPPORTED 0x0d +#define UCSI_GET_CURRENT_CAM 0x0e +#define UCSI_SET_NEW_CAM 0x0f +#define UCSI_GET_PDOS 0x10 +#define UCSI_GET_CABLE_PROPERTY 0x11 +#define UCSI_GET_CABLE_PROPERTY_SIZE 64 +#define UCSI_GET_CONNECTOR_STATUS 0x12 +#define UCSI_GET_CONNECTOR_STATUS_SIZE 152 +#define UCSI_GET_ERROR_STATUS 0x13 +#define UCSI_GET_PD_MESSAGE 0x15 +#define UCSI_SET_SINK_PATH 0x1c #define UCSI_CONNECTOR_NUMBER(_num_) ((u64)(_num_) << 16) #define UCSI_COMMAND(_cmd_) ((_cmd_) & 0xff) @@ -126,7 +132,6 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num); #define UCSI_CONNECTOR_RESET_HARD_VER_1_0 BIT(23) /* Deprecated in v1.1 */ #define UCSI_CONNECTOR_RESET_DATA_VER_2_0 BIT(23) /* Redefined in v2.0 */ - /* ACK_CC_CI bits */ #define UCSI_ACK_CONNECTOR_CHANGE BIT(16) #define UCSI_ACK_COMMAND_COMPLETE BIT(17) @@ -250,42 +255,6 @@ struct ucsi_capability { u16 typec_version; } __packed; -/* Data structure filled by PPM in response to GET_CONNECTOR_CAPABILITY cmd. */ -struct ucsi_connector_capability { - u8 op_mode; -#define UCSI_CONCAP_OPMODE_DFP BIT(0) -#define UCSI_CONCAP_OPMODE_UFP BIT(1) -#define UCSI_CONCAP_OPMODE_DRP BIT(2) -#define UCSI_CONCAP_OPMODE_AUDIO_ACCESSORY BIT(3) -#define UCSI_CONCAP_OPMODE_DEBUG_ACCESSORY BIT(4) -#define UCSI_CONCAP_OPMODE_USB2 BIT(5) -#define UCSI_CONCAP_OPMODE_USB3 BIT(6) -#define UCSI_CONCAP_OPMODE_ALT_MODE BIT(7) - u32 flags; -#define UCSI_CONCAP_FLAG_PROVIDER BIT(0) -#define UCSI_CONCAP_FLAG_CONSUMER BIT(1) -#define UCSI_CONCAP_FLAG_SWAP_TO_DFP BIT(2) -#define UCSI_CONCAP_FLAG_SWAP_TO_UFP BIT(3) -#define UCSI_CONCAP_FLAG_SWAP_TO_SRC BIT(4) -#define UCSI_CONCAP_FLAG_SWAP_TO_SINK BIT(5) -#define UCSI_CONCAP_FLAG_EX_OP_MODE(_f_) \ - (((_f_) & GENMASK(13, 6)) >> 6) -#define UCSI_CONCAP_EX_OP_MODE_USB4_GEN2 BIT(0) -#define UCSI_CONCAP_EX_OP_MODE_EPR_SRC BIT(1) -#define UCSI_CONCAP_EX_OP_MODE_EPR_SINK BIT(2) -#define UCSI_CONCAP_EX_OP_MODE_USB4_GEN3 BIT(3) -#define UCSI_CONCAP_EX_OP_MODE_USB4_GEN4 BIT(4) -#define UCSI_CONCAP_FLAG_MISC_CAPS(_f_) \ - (((_f_) & GENMASK(17, 14)) >> 14) -#define UCSI_CONCAP_MISC_CAP_FW_UPDATE BIT(0) -#define UCSI_CONCAP_MISC_CAP_SECURITY BIT(1) -#define UCSI_CONCAP_FLAG_REV_CURR_PROT_SUPPORT BIT(18) -#define UCSI_CONCAP_FLAG_PARTNER_PD_MAJOR_REV(_f_) \ - (((_f_) & GENMASK(20, 19)) >> 19) -#define UCSI_CONCAP_FLAG_PARTNER_PD_MAJOR_REV_AS_BCD(_f_) \ - UCSI_SPEC_REVISION_TO_BCD(UCSI_CONCAP_FLAG_PARTNER_PD_MAJOR_REV(_f_)) -} __packed; - struct ucsi_altmode { u16 svid; u32 mid; @@ -311,49 +280,143 @@ struct ucsi_cable_property { u8 latency; } __packed; -/* Data structure filled by PPM in response to GET_CONNECTOR_STATUS command. */ -struct ucsi_connector_status { - u16 change; -#define UCSI_CONSTAT_EXT_SUPPLY_CHANGE BIT(1) -#define UCSI_CONSTAT_POWER_OPMODE_CHANGE BIT(2) -#define UCSI_CONSTAT_PDOS_CHANGE BIT(5) -#define UCSI_CONSTAT_POWER_LEVEL_CHANGE BIT(6) -#define UCSI_CONSTAT_PD_RESET_COMPLETE BIT(7) -#define UCSI_CONSTAT_CAM_CHANGE BIT(8) -#define UCSI_CONSTAT_BC_CHANGE BIT(9) -#define UCSI_CONSTAT_PARTNER_CHANGE BIT(11) -#define UCSI_CONSTAT_POWER_DIR_CHANGE BIT(12) -#define UCSI_CONSTAT_CONNECT_CHANGE BIT(14) -#define UCSI_CONSTAT_ERROR BIT(15) - u16 flags; -#define UCSI_CONSTAT_PWR_OPMODE(_f_) ((_f_) & GENMASK(2, 0)) +/* Get Connector Capability Fields. */ +#define UCSI_CONCAP_OPMODE UCSI_DECLARE_BITFIELD(0, 0, 8) +#define UCSI_CONCAP_OPMODE_DFP UCSI_DECLARE_BITFIELD(0, 0, 1) +#define UCSI_CONCAP_OPMODE_UFP UCSI_DECLARE_BITFIELD(0, 1, 1) +#define UCSI_CONCAP_OPMODE_DRP UCSI_DECLARE_BITFIELD(0, 2, 1) +#define UCSI_CONCAP_OPMODE_AUDIO_ACCESSORY UCSI_DECLARE_BITFIELD(0, 3, 1) +#define UCSI_CONCAP_OPMODE_DEBUG_ACCESSORY UCSI_DECLARE_BITFIELD(0, 4, 1) +#define UCSI_CONCAP_OPMODE_USB2 UCSI_DECLARE_BITFIELD(0, 5, 1) +#define UCSI_CONCAP_OPMODE_USB3 UCSI_DECLARE_BITFIELD(0, 6, 1) +#define UCSI_CONCAP_OPMODE_ALT_MODE UCSI_DECLARE_BITFIELD(0, 7, 1) +#define UCSI_CONCAP_PROVIDER UCSI_DECLARE_BITFIELD(0, 8, 1) +#define UCSI_CONCAP_CONSUMER UCSI_DECLARE_BITFIELD(0, 9, 1) +#define UCSI_CONCAP_SWAP_TO_DFP_V1_1 UCSI_DECLARE_BITFIELD_V1_1(10, 1) +#define UCSI_CONCAP_SWAP_TO_UFP_V1_1 UCSI_DECLARE_BITFIELD_V1_1(11, 1) +#define UCSI_CONCAP_SWAP_TO_SRC_V1_1 UCSI_DECLARE_BITFIELD_V1_1(12, 1) +#define UCSI_CONCAP_SWAP_TO_SNK_V1_1 UCSI_DECLARE_BITFIELD_V1_1(13, 1) +#define UCSI_CONCAP_EXT_OPMODE_V2_0 UCSI_DECLARE_BITFIELD_V2_0(14, 8) +#define UCSI_CONCAP_EXT_OPMODE_USB4_GEN2_V2_0 UCSI_DECLARE_BITFIELD_V2_0(14, 1) +#define UCSI_CONCAP_EXT_OPMODE_EPR_SRC_V2_0 UCSI_DECLARE_BITFIELD_V2_0(15, 1) +#define UCSI_CONCAP_EXT_OPMODE_EPR_SINK_V2_0 UCSI_DECLARE_BITFIELD_V2_0(16, 1) +#define UCSI_CONCAP_EXT_OPMODE_USB4_GEN3_V2_0 UCSI_DECLARE_BITFIELD_V2_0(17, 1) +#define UCSI_CONCAP_EXT_OPMODE_USB4_GEN4_V2_0 UCSI_DECLARE_BITFIELD_V2_0(18, 1) +#define UCSI_CONCAP_MISC_V2_0 UCSI_DECLARE_BITFIELD_V2_0(22, 4) +#define UCSI_CONCAP_MISC_FW_UPDATE_V2_0 UCSI_DECLARE_BITFIELD_V2_0(22, 1) +#define UCSI_CONCAP_MISC_SECURITY_V2_0 UCSI_DECLARE_BITFIELD_V2_0(23, 1) +#define UCSI_CONCAP_REV_CURR_PROT_SUPPORT_V2_0 UCSI_DECLARE_BITFIELD_V2_0(26, 1) +#define UCSI_CONCAP_PARTNER_PD_REVISION_V2_1 UCSI_DECLARE_BITFIELD_V2_1(27, 2) + +/* Helpers for USB capability checks. */ +#define UCSI_CONCAP_USB2_SUPPORT(_con_) UCSI_CONCAP((_con_), OPMODE_USB2) +#define UCSI_CONCAP_USB3_SUPPORT(_con_) UCSI_CONCAP((_con_), OPMODE_USB3) +#define UCSI_CONCAP_USB4_SUPPORT(_con_) \ + ((_con_)->ucsi->version >= UCSI_VERSION_2_0 && \ + (UCSI_CONCAP((_con_), EXT_OPMODE_USB4_GEN2_V2_0) | \ + UCSI_CONCAP((_con_), EXT_OPMODE_USB4_GEN3_V2_0) | \ + UCSI_CONCAP((_con_), EXT_OPMODE_USB4_GEN4_V2_0))) + +/* Get Connector Status Fields. */ +#define UCSI_CONSTAT_CHANGE UCSI_DECLARE_BITFIELD(0, 0, 16) +#define UCSI_CONSTAT_PWR_OPMODE UCSI_DECLARE_BITFIELD(0, 16, 3) #define UCSI_CONSTAT_PWR_OPMODE_NONE 0 #define UCSI_CONSTAT_PWR_OPMODE_DEFAULT 1 #define UCSI_CONSTAT_PWR_OPMODE_BC 2 #define UCSI_CONSTAT_PWR_OPMODE_PD 3 #define UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5 4 #define UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0 5 -#define UCSI_CONSTAT_CONNECTED BIT(3) -#define UCSI_CONSTAT_PWR_DIR BIT(4) -#define UCSI_CONSTAT_PARTNER_FLAGS(_f_) (((_f_) & GENMASK(12, 5)) >> 5) -#define UCSI_CONSTAT_PARTNER_FLAG_USB 1 -#define UCSI_CONSTAT_PARTNER_FLAG_ALT_MODE 2 -#define UCSI_CONSTAT_PARTNER_TYPE(_f_) (((_f_) & GENMASK(15, 13)) >> 13) +#define UCSI_CONSTAT_CONNECTED UCSI_DECLARE_BITFIELD(0, 19, 1) +#define UCSI_CONSTAT_PWR_DIR UCSI_DECLARE_BITFIELD(0, 20, 1) +#define UCSI_CONSTAT_PARTNER_FLAGS UCSI_DECLARE_BITFIELD(0, 21, 8) +#define UCSI_CONSTAT_PARTNER_FLAG_USB UCSI_DECLARE_BITFIELD(0, 21, 1) +#define UCSI_CONSTAT_PARTNER_FLAG_ALT_MODE UCSI_DECLARE_BITFIELD(0, 22, 1) +#define UCSI_CONSTAT_PARTNER_FLAG_USB4_GEN3 UCSI_DECLARE_BITFIELD(0, 23, 1) +#define UCSI_CONSTAT_PARTNER_FLAG_USB4_GEN4 UCSI_DECLARE_BITFIELD(0, 24, 1) +#define UCSI_CONSTAT_PARTNER_TYPE UCSI_DECLARE_BITFIELD(0, 29, 3) #define UCSI_CONSTAT_PARTNER_TYPE_DFP 1 #define UCSI_CONSTAT_PARTNER_TYPE_UFP 2 -#define UCSI_CONSTAT_PARTNER_TYPE_CABLE 3 /* Powered Cable */ -#define UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP 4 /* Powered Cable */ +#define UCSI_CONSTAT_PARTNER_TYPE_CABLE 3 /* Powered Cable */ +#define UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP 4 /* Powered Cable */ #define UCSI_CONSTAT_PARTNER_TYPE_DEBUG 5 #define UCSI_CONSTAT_PARTNER_TYPE_AUDIO 6 - u32 request_data_obj; - - u8 pwr_status; -#define UCSI_CONSTAT_BC_STATUS(_p_) ((_p_) & GENMASK(1, 0)) +#define UCSI_CONSTAT_RDO UCSI_DECLARE_BITFIELD(0, 32, 32) +#define UCSI_CONSTAT_BC_STATUS UCSI_DECLARE_BITFIELD(0, 64, 2) #define UCSI_CONSTAT_BC_NOT_CHARGING 0 #define UCSI_CONSTAT_BC_NOMINAL_CHARGING 1 #define UCSI_CONSTAT_BC_SLOW_CHARGING 2 #define UCSI_CONSTAT_BC_TRICKLE_CHARGING 3 -} __packed; +#define UCSI_CONSTAT_PD_VERSION_V1_2 UCSI_DECLARE_BITFIELD_V1_2(70, 16) + +/* Connector Status Change Bits. */ +#define UCSI_CONSTAT_EXT_SUPPLY_CHANGE BIT(1) +#define UCSI_CONSTAT_POWER_OPMODE_CHANGE BIT(2) +#define UCSI_CONSTAT_PDOS_CHANGE BIT(5) +#define UCSI_CONSTAT_POWER_LEVEL_CHANGE BIT(6) +#define UCSI_CONSTAT_PD_RESET_COMPLETE BIT(7) +#define UCSI_CONSTAT_CAM_CHANGE BIT(8) +#define UCSI_CONSTAT_BC_CHANGE BIT(9) +#define UCSI_CONSTAT_PARTNER_CHANGE BIT(11) +#define UCSI_CONSTAT_POWER_DIR_CHANGE BIT(12) +#define UCSI_CONSTAT_CONNECT_CHANGE BIT(14) +#define UCSI_CONSTAT_ERROR BIT(15) + +#define UCSI_DECLARE_BITFIELD_V1_1(_offset_, _size_) \ + UCSI_DECLARE_BITFIELD(UCSI_VERSION_1_1, (_offset_), (_size_)) +#define UCSI_DECLARE_BITFIELD_V1_2(_offset_, _size_) \ + UCSI_DECLARE_BITFIELD(UCSI_VERSION_1_2, (_offset_), (_size_)) +#define UCSI_DECLARE_BITFIELD_V2_0(_offset_, _size_) \ + UCSI_DECLARE_BITFIELD(UCSI_VERSION_2_0, (_offset_), (_size_)) +#define UCSI_DECLARE_BITFIELD_V2_1(_offset_, _size_) \ + UCSI_DECLARE_BITFIELD(UCSI_VERSION_2_1, (_offset_), (_size_)) +#define UCSI_DECLARE_BITFIELD_V3_0(_offset_, _size_) \ + UCSI_DECLARE_BITFIELD(UCSI_VERSION_3_0, (_offset_), (_size_)) + +#define UCSI_DECLARE_BITFIELD(_ver_, _offset_, _size_) \ +(struct ucsi_bitfield) { \ + .version = _ver_, \ + .offset = _offset_, \ + .size = _size_, \ +} + +struct ucsi_bitfield { + const u16 version; + const u8 offset; + const u8 size; +}; + +/** + * ucsi_bitfield_read - Read a field from UCSI command response + * @_map_: UCSI command response + * @_field_: The field offset in the response data structure + * @_ver_: UCSI version where the field was introduced + * + * Reads the fields in the command responses by first checking that the field is + * valid with the UCSI interface version that is used in the system. + * @_ver_ is the minimum UCSI version for the @_field_. If the UCSI interface is + * older than @_ver_, a warning is generated. + * + * Caveats: + * - Removed fields are not checked - @_ver_ is just the minimum UCSI version. + * + * Returns the value of @_field_, or 0 when the UCSI interface is older than + * @_ver_. + */ +#define ucsi_bitfield_read(_map_, _field_, _ver_) \ +({ \ + struct ucsi_bitfield f = (_field_); \ + WARN((_ver_) < f.version, \ + "Access to unsupported field at offset 0x%x (need version %04x)", \ + f.offset, f.version) ? 0 : \ + bitmap_read((_map_), f.offset, f.size); \ +}) + +/* Helpers to access cached command responses. */ +#define UCSI_CONCAP(_con_, _field_) \ + ucsi_bitfield_read((_con_)->cap, UCSI_CONCAP_##_field_, (_con_)->ucsi->version) + +#define UCSI_CONSTAT(_con_, _field_) \ + ucsi_bitfield_read((_con_)->status, UCSI_CONSTAT_##_field_, (_con_)->ucsi->version) /* -------------------------------------------------------------------------- */ @@ -433,8 +496,10 @@ struct ucsi_connector { struct typec_capability typec_cap; - struct ucsi_connector_status status; - struct ucsi_connector_capability cap; + /* Cached command responses. */ + DECLARE_BITMAP(cap, UCSI_GET_CONNECTOR_CAPABILITY_SIZE); + DECLARE_BITMAP(status, UCSI_GET_CONNECTOR_STATUS_SIZE); + struct power_supply *psy; struct power_supply_desc psy_desc; u32 rdo; diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c index 7a5dff8d9cc6..5c5515551963 100644 --- a/drivers/usb/typec/ucsi/ucsi_acpi.c +++ b/drivers/usb/typec/ucsi/ucsi_acpi.c @@ -61,9 +61,11 @@ static int ucsi_acpi_read_cci(struct ucsi *ucsi, u32 *cci) struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); int ret; - ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ); - if (ret) - return ret; + if (UCSI_COMMAND(ua->cmd) == UCSI_PPM_RESET) { + ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ); + if (ret) + return ret; + } memcpy(cci, ua->base + UCSI_CCI, sizeof(*cci)); @@ -73,11 +75,6 @@ static int ucsi_acpi_read_cci(struct ucsi *ucsi, u32 *cci) static int ucsi_acpi_read_message_in(struct ucsi *ucsi, void *val, size_t val_len) { struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); - int ret; - - ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ); - if (ret) - return ret; memcpy(val, ua->base + UCSI_MESSAGE_IN, val_len); @@ -102,48 +99,11 @@ static const struct ucsi_operations ucsi_acpi_ops = { .async_control = ucsi_acpi_async_control }; -static int -ucsi_zenbook_read_cci(struct ucsi *ucsi, u32 *cci) -{ - struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); - int ret; - - if (UCSI_COMMAND(ua->cmd) == UCSI_PPM_RESET) { - ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ); - if (ret) - return ret; - } - - memcpy(cci, ua->base + UCSI_CCI, sizeof(*cci)); - - return 0; -} - -static int -ucsi_zenbook_read_message_in(struct ucsi *ucsi, void *val, size_t val_len) -{ - struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); - - /* UCSI_MESSAGE_IN is never read for PPM_RESET, return stored data */ - memcpy(val, ua->base + UCSI_MESSAGE_IN, val_len); - - return 0; -} - -static const struct ucsi_operations ucsi_zenbook_ops = { - .read_version = ucsi_acpi_read_version, - .read_cci = ucsi_zenbook_read_cci, - .read_message_in = ucsi_zenbook_read_message_in, - .sync_control = ucsi_sync_control_common, - .async_control = ucsi_acpi_async_control -}; - static int ucsi_gram_read_message_in(struct ucsi *ucsi, void *val, size_t val_len) { u16 bogus_change = UCSI_CONSTAT_POWER_LEVEL_CHANGE | UCSI_CONSTAT_PDOS_CHANGE; struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); - struct ucsi_connector_status *status; int ret; ret = ucsi_acpi_read_message_in(ucsi, val, val_len); @@ -152,11 +112,9 @@ static int ucsi_gram_read_message_in(struct ucsi *ucsi, void *val, size_t val_le if (UCSI_COMMAND(ua->cmd) == UCSI_GET_CONNECTOR_STATUS && ua->check_bogus_event) { - status = (struct ucsi_connector_status *)val; - /* Clear the bogus change */ - if (status->change == bogus_change) - status->change = 0; + if (*(u16 *)val == bogus_change) + *(u16 *)val = 0; ua->check_bogus_event = false; } @@ -192,13 +150,6 @@ static const struct ucsi_operations ucsi_gram_ops = { static const struct dmi_system_id ucsi_acpi_quirks[] = { { .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX325UA_UM325UA"), - }, - .driver_data = (void *)&ucsi_zenbook_ops, - }, - { - .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"), DMI_MATCH(DMI_PRODUCT_FAMILY, "LG gram PC"), DMI_MATCH(DMI_PRODUCT_NAME, "90Q"), @@ -320,7 +271,7 @@ static struct platform_driver ucsi_acpi_platform_driver = { .acpi_match_table = ACPI_PTR(ucsi_acpi_match), }, .probe = ucsi_acpi_probe, - .remove_new = ucsi_acpi_remove, + .remove = ucsi_acpi_remove, }; module_platform_driver(ucsi_acpi_platform_driver); diff --git a/drivers/usb/typec/ucsi/ucsi_glink.c b/drivers/usb/typec/ucsi/ucsi_glink.c index 03c0fa8edc8d..90948cd6d297 100644 --- a/drivers/usb/typec/ucsi/ucsi_glink.c +++ b/drivers/usb/typec/ucsi/ucsi_glink.c @@ -172,12 +172,12 @@ static int pmic_glink_ucsi_async_control(struct ucsi *__ucsi, u64 command) static void pmic_glink_ucsi_update_connector(struct ucsi_connector *con) { struct pmic_glink_ucsi *ucsi = ucsi_get_drvdata(con->ucsi); - int i; - for (i = 0; i < PMIC_GLINK_MAX_PORTS; i++) { - if (ucsi->port_orientation[i]) - con->typec_cap.orientation_aware = true; - } + if (con->num > PMIC_GLINK_MAX_PORTS || + !ucsi->port_orientation[con->num - 1]) + return; + + con->typec_cap.orientation_aware = true; } static void pmic_glink_ucsi_connector_status(struct ucsi_connector *con) @@ -185,7 +185,7 @@ static void pmic_glink_ucsi_connector_status(struct ucsi_connector *con) struct pmic_glink_ucsi *ucsi = ucsi_get_drvdata(con->ucsi); int orientation; - if (con->num >= PMIC_GLINK_MAX_PORTS || + if (con->num > PMIC_GLINK_MAX_PORTS || !ucsi->port_orientation[con->num - 1]) return; @@ -322,7 +322,6 @@ static int pmic_glink_ucsi_probe(struct auxiliary_device *adev, struct pmic_glink_ucsi *ucsi; struct device *dev = &adev->dev; const struct of_device_id *match; - struct fwnode_handle *fwnode; int ret; ucsi = devm_kzalloc(dev, sizeof(*ucsi), GFP_KERNEL); @@ -354,14 +353,13 @@ static int pmic_glink_ucsi_probe(struct auxiliary_device *adev, ucsi_set_drvdata(ucsi->ucsi, ucsi); - device_for_each_child_node(dev, fwnode) { + device_for_each_child_node_scoped(dev, fwnode) { struct gpio_desc *desc; u32 port; ret = fwnode_property_read_u32(fwnode, "reg", &port); if (ret < 0) { dev_err(dev, "missing reg property of %pOFn\n", fwnode); - fwnode_handle_put(fwnode); return ret; } @@ -376,11 +374,10 @@ static int pmic_glink_ucsi_probe(struct auxiliary_device *adev, if (!desc) continue; - if (IS_ERR(desc)) { - fwnode_handle_put(fwnode); + if (IS_ERR(desc)) return dev_err_probe(dev, PTR_ERR(desc), "unable to acquire orientation gpio\n"); - } + ucsi->port_orientation[port] = desc; } diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c index 8dac1edc74d4..b03e5021c25b 100644 --- a/drivers/usb/usbip/vhci_hcd.c +++ b/drivers/usb/usbip/vhci_hcd.c @@ -1487,7 +1487,7 @@ static int vhci_hcd_resume(struct platform_device *pdev) static struct platform_driver vhci_driver = { .probe = vhci_hcd_probe, - .remove_new = vhci_hcd_remove, + .remove = vhci_hcd_remove, .suspend = vhci_hcd_suspend, .resume = vhci_hcd_resume, .driver = { diff --git a/drivers/usb/usbip/vudc_main.c b/drivers/usb/usbip/vudc_main.c index 8bee553e4894..993e721cb840 100644 --- a/drivers/usb/usbip/vudc_main.c +++ b/drivers/usb/usbip/vudc_main.c @@ -19,7 +19,7 @@ MODULE_PARM_DESC(num, "number of emulated controllers"); static struct platform_driver vudc_driver = { .probe = vudc_probe, - .remove_new = vudc_remove, + .remove = vudc_remove, .driver = { .name = GADGET_NAME, .dev_groups = vudc_groups, diff --git a/fs/afs/dir.c b/fs/afs/dir.c index f8622ed72e08..ada363af5aab 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -12,6 +12,7 @@ #include <linux/swap.h> #include <linux/ctype.h> #include <linux/sched.h> +#include <linux/iversion.h> #include <linux/task_io_accounting_ops.h> #include "internal.h" #include "afs_fs.h" @@ -1823,6 +1824,8 @@ error: static void afs_rename_success(struct afs_operation *op) { + struct afs_vnode *vnode = AFS_FS_I(d_inode(op->dentry)); + _enter("op=%08x", op->debug_id); op->ctime = op->file[0].scb.status.mtime_client; @@ -1832,6 +1835,22 @@ static void afs_rename_success(struct afs_operation *op) op->ctime = op->file[1].scb.status.mtime_client; afs_vnode_commit_status(op, &op->file[1]); } + + /* If we're moving a subdir between dirs, we need to update + * its DV counter too as the ".." will be altered. + */ + if (S_ISDIR(vnode->netfs.inode.i_mode) && + op->file[0].vnode != op->file[1].vnode) { + u64 new_dv; + + write_seqlock(&vnode->cb_lock); + + new_dv = vnode->status.data_version + 1; + vnode->status.data_version = new_dv; + inode_set_iversion_raw(&vnode->netfs.inode, new_dv); + + write_sequnlock(&vnode->cb_lock); + } } static void afs_rename_edit_dir(struct afs_operation *op) @@ -1873,6 +1892,12 @@ static void afs_rename_edit_dir(struct afs_operation *op) &vnode->fid, afs_edit_dir_for_rename_2); } + if (S_ISDIR(vnode->netfs.inode.i_mode) && + new_dvnode != orig_dvnode && + test_bit(AFS_VNODE_DIR_VALID, &vnode->flags)) + afs_edit_dir_update_dotdot(vnode, new_dvnode, + afs_edit_dir_for_rename_sub); + new_inode = d_inode(new_dentry); if (new_inode) { spin_lock(&new_inode->i_lock); diff --git a/fs/afs/dir_edit.c b/fs/afs/dir_edit.c index a71bff10496b..fe223fb78111 100644 --- a/fs/afs/dir_edit.c +++ b/fs/afs/dir_edit.c @@ -127,10 +127,10 @@ static struct folio *afs_dir_get_folio(struct afs_vnode *vnode, pgoff_t index) /* * Scan a directory block looking for a dirent of the right name. */ -static int afs_dir_scan_block(union afs_xdr_dir_block *block, struct qstr *name, +static int afs_dir_scan_block(const union afs_xdr_dir_block *block, const struct qstr *name, unsigned int blocknum) { - union afs_xdr_dirent *de; + const union afs_xdr_dirent *de; u64 bitmap; int d, len, n; @@ -492,3 +492,90 @@ error: clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); goto out_unmap; } + +/* + * Edit a subdirectory that has been moved between directories to update the + * ".." entry. + */ +void afs_edit_dir_update_dotdot(struct afs_vnode *vnode, struct afs_vnode *new_dvnode, + enum afs_edit_dir_reason why) +{ + union afs_xdr_dir_block *block; + union afs_xdr_dirent *de; + struct folio *folio; + unsigned int nr_blocks, b; + pgoff_t index; + loff_t i_size; + int slot; + + _enter(""); + + i_size = i_size_read(&vnode->netfs.inode); + if (i_size < AFS_DIR_BLOCK_SIZE) { + clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); + return; + } + nr_blocks = i_size / AFS_DIR_BLOCK_SIZE; + + /* Find a block that has sufficient slots available. Each folio + * contains two or more directory blocks. + */ + for (b = 0; b < nr_blocks; b++) { + index = b / AFS_DIR_BLOCKS_PER_PAGE; + folio = afs_dir_get_folio(vnode, index); + if (!folio) + goto error; + + block = kmap_local_folio(folio, b * AFS_DIR_BLOCK_SIZE - folio_pos(folio)); + + /* Abandon the edit if we got a callback break. */ + if (!test_bit(AFS_VNODE_DIR_VALID, &vnode->flags)) + goto invalidated; + + slot = afs_dir_scan_block(block, &dotdot_name, b); + if (slot >= 0) + goto found_dirent; + + kunmap_local(block); + folio_unlock(folio); + folio_put(folio); + } + + /* Didn't find the dirent to clobber. Download the directory again. */ + trace_afs_edit_dir(vnode, why, afs_edit_dir_update_nodd, + 0, 0, 0, 0, ".."); + clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); + goto out; + +found_dirent: + de = &block->dirents[slot]; + de->u.vnode = htonl(new_dvnode->fid.vnode); + de->u.unique = htonl(new_dvnode->fid.unique); + + trace_afs_edit_dir(vnode, why, afs_edit_dir_update_dd, b, slot, + ntohl(de->u.vnode), ntohl(de->u.unique), ".."); + + kunmap_local(block); + folio_unlock(folio); + folio_put(folio); + inode_set_iversion_raw(&vnode->netfs.inode, vnode->status.data_version); + +out: + _leave(""); + return; + +invalidated: + kunmap_local(block); + folio_unlock(folio); + folio_put(folio); + trace_afs_edit_dir(vnode, why, afs_edit_dir_update_inval, + 0, 0, 0, 0, ".."); + clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); + goto out; + +error: + trace_afs_edit_dir(vnode, why, afs_edit_dir_update_error, + 0, 0, 0, 0, ".."); + clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); + goto out; +} diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 52aab09a32a9..c9d620175e80 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -1073,6 +1073,8 @@ extern void afs_check_for_remote_deletion(struct afs_operation *); extern void afs_edit_dir_add(struct afs_vnode *, struct qstr *, struct afs_fid *, enum afs_edit_dir_reason); extern void afs_edit_dir_remove(struct afs_vnode *, struct qstr *, enum afs_edit_dir_reason); +void afs_edit_dir_update_dotdot(struct afs_vnode *vnode, struct afs_vnode *new_dvnode, + enum afs_edit_dir_reason why); /* * dir_silly.c diff --git a/fs/autofs/dev-ioctl.c b/fs/autofs/dev-ioctl.c index f011e026358e..6d57efbb8110 100644 --- a/fs/autofs/dev-ioctl.c +++ b/fs/autofs/dev-ioctl.c @@ -110,6 +110,7 @@ static inline void free_dev_ioctl(struct autofs_dev_ioctl *param) */ static int validate_dev_ioctl(int cmd, struct autofs_dev_ioctl *param) { + unsigned int inr = _IOC_NR(cmd); int err; err = check_dev_ioctl_version(cmd, param); @@ -133,7 +134,7 @@ static int validate_dev_ioctl(int cmd, struct autofs_dev_ioctl *param) * check_name() return for AUTOFS_DEV_IOCTL_TIMEOUT_CMD. */ err = check_name(param->path); - if (cmd == AUTOFS_DEV_IOCTL_TIMEOUT_CMD) + if (inr == AUTOFS_DEV_IOCTL_TIMEOUT_CMD) err = err ? 0 : -EINVAL; if (err) { pr_warn("invalid path supplied for cmd(0x%08x)\n", @@ -141,8 +142,6 @@ static int validate_dev_ioctl(int cmd, struct autofs_dev_ioctl *param) goto out; } } else { - unsigned int inr = _IOC_NR(cmd); - if (inr == AUTOFS_DEV_IOCTL_OPENMOUNT_CMD || inr == AUTOFS_DEV_IOCTL_REQUESTER_CMD || inr == AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD) { diff --git a/fs/bcachefs/alloc_background.h b/fs/bcachefs/alloc_background.h index f8e87c6721b1..163a67b97a40 100644 --- a/fs/bcachefs/alloc_background.h +++ b/fs/bcachefs/alloc_background.h @@ -168,6 +168,9 @@ static inline bool data_type_movable(enum bch_data_type type) static inline u64 alloc_lru_idx_fragmentation(struct bch_alloc_v4 a, struct bch_dev *ca) { + if (a.data_type >= BCH_DATA_NR) + return 0; + if (!data_type_movable(a.data_type) || !bch2_bucket_sectors_fragmented(ca, a)) return 0; diff --git a/fs/bcachefs/alloc_foreground.c b/fs/bcachefs/alloc_foreground.c index 5836870ab882..372178c8d416 100644 --- a/fs/bcachefs/alloc_foreground.c +++ b/fs/bcachefs/alloc_foreground.c @@ -162,6 +162,10 @@ static void open_bucket_free_unused(struct bch_fs *c, struct open_bucket *ob) ARRAY_SIZE(c->open_buckets_partial)); spin_lock(&c->freelist_lock); + rcu_read_lock(); + bch2_dev_rcu(c, ob->dev)->nr_partial_buckets++; + rcu_read_unlock(); + ob->on_partial_list = true; c->open_buckets_partial[c->open_buckets_partial_nr++] = ob - c->open_buckets; @@ -972,7 +976,7 @@ static int bucket_alloc_set_partial(struct bch_fs *c, u64 avail; bch2_dev_usage_read_fast(ca, &usage); - avail = dev_buckets_free(ca, usage, watermark); + avail = dev_buckets_free(ca, usage, watermark) + ca->nr_partial_buckets; if (!avail) continue; @@ -981,6 +985,10 @@ static int bucket_alloc_set_partial(struct bch_fs *c, i); ob->on_partial_list = false; + rcu_read_lock(); + bch2_dev_rcu(c, ob->dev)->nr_partial_buckets--; + rcu_read_unlock(); + ret = add_new_bucket(c, ptrs, devs_may_alloc, nr_replicas, nr_effective, have_cache, ob); @@ -1191,7 +1199,13 @@ void bch2_open_buckets_stop(struct bch_fs *c, struct bch_dev *ca, --c->open_buckets_partial_nr; swap(c->open_buckets_partial[i], c->open_buckets_partial[c->open_buckets_partial_nr]); + ob->on_partial_list = false; + + rcu_read_lock(); + bch2_dev_rcu(c, ob->dev)->nr_partial_buckets--; + rcu_read_unlock(); + spin_unlock(&c->freelist_lock); bch2_open_bucket_put(c, ob); spin_lock(&c->freelist_lock); @@ -1610,8 +1624,7 @@ void bch2_open_buckets_to_text(struct printbuf *out, struct bch_fs *c, ob < c->open_buckets + ARRAY_SIZE(c->open_buckets); ob++) { spin_lock(&ob->lock); - if (ob->valid && !ob->on_partial_list && - (!ca || ob->dev == ca->dev_idx)) + if (ob->valid && (!ca || ob->dev == ca->dev_idx)) bch2_open_bucket_to_text(out, c, ob); spin_unlock(&ob->lock); } diff --git a/fs/bcachefs/bcachefs.h b/fs/bcachefs/bcachefs.h index f4151ee51b03..e94a83b8113e 100644 --- a/fs/bcachefs/bcachefs.h +++ b/fs/bcachefs/bcachefs.h @@ -555,6 +555,7 @@ struct bch_dev { u64 alloc_cursor[3]; unsigned nr_open_buckets; + unsigned nr_partial_buckets; unsigned nr_btree_reserve; size_t inc_gen_needs_gc; diff --git a/fs/bcachefs/btree_iter.c b/fs/bcachefs/btree_iter.c index 0883cf6e1a3e..eef9b89c561d 100644 --- a/fs/bcachefs/btree_iter.c +++ b/fs/bcachefs/btree_iter.c @@ -882,6 +882,18 @@ static noinline int btree_node_iter_and_journal_peek(struct btree_trans *trans, __bch2_btree_and_journal_iter_init_node_iter(trans, &jiter, l->b, l->iter, path->pos); k = bch2_btree_and_journal_iter_peek(&jiter); + if (!k.k) { + struct printbuf buf = PRINTBUF; + + prt_str(&buf, "node not found at pos "); + bch2_bpos_to_text(&buf, path->pos); + prt_str(&buf, " at btree "); + bch2_btree_pos_to_text(&buf, c, l->b); + + ret = bch2_fs_topology_error(c, "%s", buf.buf); + printbuf_exit(&buf); + goto err; + } bch2_bkey_buf_reassemble(out, c, k); @@ -889,6 +901,7 @@ static noinline int btree_node_iter_and_journal_peek(struct btree_trans *trans, c->opts.btree_node_prefetch) ret = btree_path_prefetch_j(trans, path, &jiter); +err: bch2_btree_and_journal_iter_exit(&jiter); return ret; } diff --git a/fs/bcachefs/data_update.c b/fs/bcachefs/data_update.c index a6ee0beee6b0..8e75a852b358 100644 --- a/fs/bcachefs/data_update.c +++ b/fs/bcachefs/data_update.c @@ -236,7 +236,8 @@ static int __bch2_data_update_index_update(struct btree_trans *trans, if (((1U << i) & m->data_opts.rewrite_ptrs) && (ptr = bch2_extent_has_ptr(old, p, bkey_i_to_s(insert))) && !ptr->cached) { - bch2_extent_ptr_set_cached(bkey_i_to_s(insert), ptr); + bch2_extent_ptr_set_cached(c, &m->op.opts, + bkey_i_to_s(insert), ptr); rewrites_found |= 1U << i; } i++; @@ -284,7 +285,8 @@ restart_drop_extra_replicas: durability - ptr_durability >= m->op.opts.data_replicas) { durability -= ptr_durability; - bch2_extent_ptr_set_cached(bkey_i_to_s(insert), &entry->ptr); + bch2_extent_ptr_set_cached(c, &m->op.opts, + bkey_i_to_s(insert), &entry->ptr); goto restart_drop_extra_replicas; } } @@ -295,7 +297,7 @@ restart_drop_extra_replicas: bch2_extent_ptr_decoded_append(insert, &p); bch2_bkey_narrow_crcs(insert, (struct bch_extent_crc_unpacked) { 0 }); - bch2_extent_normalize(c, bkey_i_to_s(insert)); + bch2_extent_normalize_by_opts(c, &m->op.opts, bkey_i_to_s(insert)); ret = bch2_sum_sector_overwrites(trans, &iter, insert, &should_check_enospc, @@ -558,7 +560,8 @@ void bch2_data_update_to_text(struct printbuf *out, struct data_update *m) int bch2_extent_drop_ptrs(struct btree_trans *trans, struct btree_iter *iter, struct bkey_s_c k, - struct data_update_opts data_opts) + struct bch_io_opts *io_opts, + struct data_update_opts *data_opts) { struct bch_fs *c = trans->c; struct bkey_i *n; @@ -569,11 +572,11 @@ int bch2_extent_drop_ptrs(struct btree_trans *trans, if (ret) return ret; - while (data_opts.kill_ptrs) { - unsigned i = 0, drop = __fls(data_opts.kill_ptrs); + while (data_opts->kill_ptrs) { + unsigned i = 0, drop = __fls(data_opts->kill_ptrs); bch2_bkey_drop_ptrs_noerror(bkey_i_to_s(n), ptr, i++ == drop); - data_opts.kill_ptrs ^= 1U << drop; + data_opts->kill_ptrs ^= 1U << drop; } /* @@ -581,7 +584,7 @@ int bch2_extent_drop_ptrs(struct btree_trans *trans, * will do the appropriate thing with it (turning it into a * KEY_TYPE_error key, or just a discard if it was a cached extent) */ - bch2_extent_normalize(c, bkey_i_to_s(n)); + bch2_extent_normalize_by_opts(c, io_opts, bkey_i_to_s(n)); /* * Since we're not inserting through an extent iterator @@ -720,7 +723,7 @@ int bch2_data_update_init(struct btree_trans *trans, m->data_opts.rewrite_ptrs = 0; /* if iter == NULL, it's just a promote */ if (iter) - ret = bch2_extent_drop_ptrs(trans, iter, k, m->data_opts); + ret = bch2_extent_drop_ptrs(trans, iter, k, &io_opts, &m->data_opts); goto out; } diff --git a/fs/bcachefs/data_update.h b/fs/bcachefs/data_update.h index 8d36365bdea8..e4b50723428e 100644 --- a/fs/bcachefs/data_update.h +++ b/fs/bcachefs/data_update.h @@ -40,7 +40,8 @@ void bch2_data_update_read_done(struct data_update *, int bch2_extent_drop_ptrs(struct btree_trans *, struct btree_iter *, struct bkey_s_c, - struct data_update_opts); + struct bch_io_opts *, + struct data_update_opts *); void bch2_data_update_exit(struct data_update *); int bch2_data_update_init(struct btree_trans *, struct btree_iter *, diff --git a/fs/bcachefs/ec.c b/fs/bcachefs/ec.c index a0aa5bb467d9..749dcf368841 100644 --- a/fs/bcachefs/ec.c +++ b/fs/bcachefs/ec.c @@ -1870,6 +1870,10 @@ __bch2_ec_stripe_head_get(struct btree_trans *trans, } h = ec_new_stripe_head_alloc(c, disk_label, algo, redundancy, watermark); + if (!h) { + h = ERR_PTR(-BCH_ERR_ENOMEM_stripe_head_alloc); + goto err; + } found: if (h->rw_devs_change_count != c->rw_devs_change_count) ec_stripe_head_devs_update(c, h); diff --git a/fs/bcachefs/errcode.h b/fs/bcachefs/errcode.h index 649263516ab1..a1bc6c7a8ba0 100644 --- a/fs/bcachefs/errcode.h +++ b/fs/bcachefs/errcode.h @@ -83,6 +83,7 @@ x(ENOMEM, ENOMEM_fs_other_alloc) \ x(ENOMEM, ENOMEM_dev_alloc) \ x(ENOMEM, ENOMEM_disk_accounting) \ + x(ENOMEM, ENOMEM_stripe_head_alloc) \ x(ENOSPC, ENOSPC_disk_reservation) \ x(ENOSPC, ENOSPC_bucket_alloc) \ x(ENOSPC, ENOSPC_disk_label_add) \ @@ -222,6 +223,7 @@ x(BCH_ERR_invalid_sb_layout, invalid_sb_layout_type) \ x(BCH_ERR_invalid_sb_layout, invalid_sb_layout_nr_superblocks) \ x(BCH_ERR_invalid_sb_layout, invalid_sb_layout_superblocks_overlap) \ + x(BCH_ERR_invalid_sb_layout, invalid_sb_layout_sb_max_size_bits) \ x(BCH_ERR_invalid_sb, invalid_sb_members_missing) \ x(BCH_ERR_invalid_sb, invalid_sb_members) \ x(BCH_ERR_invalid_sb, invalid_sb_disk_groups) \ diff --git a/fs/bcachefs/extents.c b/fs/bcachefs/extents.c index cc0d22085aef..c4e91d123849 100644 --- a/fs/bcachefs/extents.c +++ b/fs/bcachefs/extents.c @@ -978,31 +978,54 @@ bch2_extent_has_ptr(struct bkey_s_c k1, struct extent_ptr_decoded p1, struct bke return NULL; } -void bch2_extent_ptr_set_cached(struct bkey_s k, struct bch_extent_ptr *ptr) +static bool want_cached_ptr(struct bch_fs *c, struct bch_io_opts *opts, + struct bch_extent_ptr *ptr) +{ + if (!opts->promote_target || + !bch2_dev_in_target(c, ptr->dev, opts->promote_target)) + return false; + + struct bch_dev *ca = bch2_dev_rcu_noerror(c, ptr->dev); + + return ca && bch2_dev_is_readable(ca) && !dev_ptr_stale_rcu(ca, ptr); +} + +void bch2_extent_ptr_set_cached(struct bch_fs *c, + struct bch_io_opts *opts, + struct bkey_s k, + struct bch_extent_ptr *ptr) { struct bkey_ptrs ptrs = bch2_bkey_ptrs(k); union bch_extent_entry *entry; - union bch_extent_entry *ec = NULL; + struct extent_ptr_decoded p; - bkey_extent_entry_for_each(ptrs, entry) { + rcu_read_lock(); + if (!want_cached_ptr(c, opts, ptr)) { + bch2_bkey_drop_ptr_noerror(k, ptr); + goto out; + } + + /* + * Stripes can't contain cached data, for - reasons. + * + * Possibly something we can fix in the future? + */ + bkey_for_each_ptr_decode(k.k, ptrs, p, entry) if (&entry->ptr == ptr) { - ptr->cached = true; - if (ec) - extent_entry_drop(k, ec); - return; + if (p.has_ec) + bch2_bkey_drop_ptr_noerror(k, ptr); + else + ptr->cached = true; + goto out; } - if (extent_entry_is_stripe_ptr(entry)) - ec = entry; - else if (extent_entry_is_ptr(entry)) - ec = NULL; - } - BUG(); +out: + rcu_read_unlock(); } /* - * bch_extent_normalize - clean up an extent, dropping stale pointers etc. + * bch2_extent_normalize - clean up an extent, dropping stale pointers etc. * * Returns true if @k should be dropped entirely * @@ -1016,8 +1039,39 @@ bool bch2_extent_normalize(struct bch_fs *c, struct bkey_s k) rcu_read_lock(); bch2_bkey_drop_ptrs(k, ptr, ptr->cached && - (ca = bch2_dev_rcu(c, ptr->dev)) && - dev_ptr_stale_rcu(ca, ptr) > 0); + (!(ca = bch2_dev_rcu(c, ptr->dev)) || + dev_ptr_stale_rcu(ca, ptr) > 0)); + rcu_read_unlock(); + + return bkey_deleted(k.k); +} + +/* + * bch2_extent_normalize_by_opts - clean up an extent, dropping stale pointers etc. + * + * Like bch2_extent_normalize(), but also only keeps a single cached pointer on + * the promote target. + */ +bool bch2_extent_normalize_by_opts(struct bch_fs *c, + struct bch_io_opts *opts, + struct bkey_s k) +{ + struct bkey_ptrs ptrs; + bool have_cached_ptr; + + rcu_read_lock(); +restart_drop_ptrs: + ptrs = bch2_bkey_ptrs(k); + have_cached_ptr = false; + + bkey_for_each_ptr(ptrs, ptr) + if (ptr->cached) { + if (have_cached_ptr || !want_cached_ptr(c, opts, ptr)) { + bch2_bkey_drop_ptr(k, ptr); + goto restart_drop_ptrs; + } + have_cached_ptr = true; + } rcu_read_unlock(); return bkey_deleted(k.k); diff --git a/fs/bcachefs/extents.h b/fs/bcachefs/extents.h index 923a5f1849a8..bcffcf60aaaf 100644 --- a/fs/bcachefs/extents.h +++ b/fs/bcachefs/extents.h @@ -686,9 +686,12 @@ bool bch2_extents_match(struct bkey_s_c, struct bkey_s_c); struct bch_extent_ptr * bch2_extent_has_ptr(struct bkey_s_c, struct extent_ptr_decoded, struct bkey_s); -void bch2_extent_ptr_set_cached(struct bkey_s, struct bch_extent_ptr *); +void bch2_extent_ptr_set_cached(struct bch_fs *, struct bch_io_opts *, + struct bkey_s, struct bch_extent_ptr *); +bool bch2_extent_normalize_by_opts(struct bch_fs *, struct bch_io_opts *, struct bkey_s); bool bch2_extent_normalize(struct bch_fs *, struct bkey_s); + void bch2_extent_ptr_to_text(struct printbuf *out, struct bch_fs *, const struct bch_extent_ptr *); void bch2_bkey_ptrs_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c); diff --git a/fs/bcachefs/fs-io.c b/fs/bcachefs/fs-io.c index 15d3f073b824..2456c41b215e 100644 --- a/fs/bcachefs/fs-io.c +++ b/fs/bcachefs/fs-io.c @@ -587,7 +587,7 @@ static noinline int __bchfs_fallocate(struct bch_inode_info *inode, int mode, POS(inode->v.i_ino, start_sector), BTREE_ITER_slots|BTREE_ITER_intent); - while (!ret && bkey_lt(iter.pos, end_pos)) { + while (!ret) { s64 i_sectors_delta = 0; struct quota_res quota_res = { 0 }; struct bkey_s_c k; @@ -598,6 +598,9 @@ static noinline int __bchfs_fallocate(struct bch_inode_info *inode, int mode, bch2_trans_begin(trans); + if (bkey_ge(iter.pos, end_pos)) + break; + ret = bch2_subvolume_get_snapshot(trans, inode->ei_inum.subvol, &snapshot); if (ret) @@ -634,12 +637,15 @@ static noinline int __bchfs_fallocate(struct bch_inode_info *inode, int mode, if (bch2_clamp_data_hole(&inode->v, &hole_start, &hole_end, - opts.data_replicas, true)) + opts.data_replicas, true)) { ret = drop_locks_do(trans, (bch2_clamp_data_hole(&inode->v, &hole_start, &hole_end, opts.data_replicas, false), 0)); + if (ret) + goto bkey_err; + } bch2_btree_iter_set_pos(&iter, POS(iter.pos.inode, hole_start)); if (ret) @@ -667,10 +673,13 @@ static noinline int __bchfs_fallocate(struct bch_inode_info *inode, int mode, bch2_i_sectors_acct(c, inode, "a_res, i_sectors_delta); if (bch2_mark_pagecache_reserved(inode, &hole_start, - iter.pos.offset, true)) - drop_locks_do(trans, + iter.pos.offset, true)) { + ret = drop_locks_do(trans, bch2_mark_pagecache_reserved(inode, &hole_start, iter.pos.offset, false)); + if (ret) + goto bkey_err; + } bkey_err: bch2_quota_reservation_put(c, inode, "a_res); if (bch2_err_matches(ret, BCH_ERR_transaction_restart)) diff --git a/fs/bcachefs/move.c b/fs/bcachefs/move.c index 8c456d8b8b99..0ef4a86850bb 100644 --- a/fs/bcachefs/move.c +++ b/fs/bcachefs/move.c @@ -266,7 +266,7 @@ int bch2_move_extent(struct moving_context *ctxt, if (!data_opts.rewrite_ptrs && !data_opts.extra_replicas) { if (data_opts.kill_ptrs) - return bch2_extent_drop_ptrs(trans, iter, k, data_opts); + return bch2_extent_drop_ptrs(trans, iter, k, &io_opts, &data_opts); return 0; } diff --git a/fs/bcachefs/recovery.c b/fs/bcachefs/recovery.c index 454b5a32dd7f..32d15aacc069 100644 --- a/fs/bcachefs/recovery.c +++ b/fs/bcachefs/recovery.c @@ -94,11 +94,10 @@ static void bch2_reconstruct_alloc(struct bch_fs *c) __set_bit_le64(BCH_FSCK_ERR_accounting_mismatch, ext->errors_silent); c->sb.compat &= ~(1ULL << BCH_COMPAT_alloc_info); - bch2_write_super(c); - mutex_unlock(&c->sb_lock); - c->opts.recovery_passes |= bch2_recovery_passes_from_stable(le64_to_cpu(ext->recovery_passes_required[0])); + bch2_write_super(c); + mutex_unlock(&c->sb_lock); bch2_shoot_down_journal_keys(c, BTREE_ID_alloc, 0, BTREE_MAX_DEPTH, POS_MIN, SPOS_MAX); @@ -1002,6 +1001,7 @@ int bch2_fs_initialize(struct bch_fs *c) struct bch_inode_unpacked root_inode, lostfound_inode; struct bkey_inode_buf packed_inode; struct qstr lostfound = QSTR("lost+found"); + struct bch_member *m; int ret; bch_notice(c, "initializing new filesystem"); @@ -1018,6 +1018,14 @@ int bch2_fs_initialize(struct bch_fs *c) SET_BCH_SB_VERSION_UPGRADE_COMPLETE(c->disk_sb.sb, bcachefs_metadata_version_current); bch2_write_super(c); } + + for_each_member_device(c, ca) { + m = bch2_members_v2_get_mut(c->disk_sb.sb, ca->dev_idx); + SET_BCH_MEMBER_FREESPACE_INITIALIZED(m, false); + ca->mi = bch2_mi_to_cpu(m); + } + + bch2_write_super(c); mutex_unlock(&c->sb_lock); c->curr_recovery_pass = BCH_RECOVERY_PASS_NR; diff --git a/fs/bcachefs/sb-downgrade.c b/fs/bcachefs/sb-downgrade.c index ae715ff658e8..8767c33c2b51 100644 --- a/fs/bcachefs/sb-downgrade.c +++ b/fs/bcachefs/sb-downgrade.c @@ -143,6 +143,9 @@ UPGRADE_TABLE() static int have_stripes(struct bch_fs *c) { + if (IS_ERR_OR_NULL(c->btree_roots_known[BTREE_ID_stripes].b)) + return 0; + return !btree_node_fake(c->btree_roots_known[BTREE_ID_stripes].b); } diff --git a/fs/bcachefs/super-io.c b/fs/bcachefs/super-io.c index ce7410d72089..7c71594f6a8b 100644 --- a/fs/bcachefs/super-io.c +++ b/fs/bcachefs/super-io.c @@ -287,6 +287,11 @@ static int validate_sb_layout(struct bch_sb_layout *layout, struct printbuf *out return -BCH_ERR_invalid_sb_layout_nr_superblocks; } + if (layout->sb_max_size_bits > BCH_SB_LAYOUT_SIZE_BITS_MAX) { + prt_printf(out, "Invalid superblock layout: max_size_bits too high"); + return -BCH_ERR_invalid_sb_layout_sb_max_size_bits; + } + max_sectors = 1 << layout->sb_max_size_bits; prev_offset = le64_to_cpu(layout->sb_offset[0]); diff --git a/fs/btrfs/bio.c b/fs/btrfs/bio.c index fec5c6cde0a7..7e0f9600b80c 100644 --- a/fs/btrfs/bio.c +++ b/fs/btrfs/bio.c @@ -49,6 +49,7 @@ void btrfs_bio_init(struct btrfs_bio *bbio, struct btrfs_fs_info *fs_info, bbio->end_io = end_io; bbio->private = private; atomic_set(&bbio->pending_ios, 1); + WRITE_ONCE(bbio->status, BLK_STS_OK); } /* @@ -113,41 +114,29 @@ static void __btrfs_bio_end_io(struct btrfs_bio *bbio) } } -static void btrfs_orig_write_end_io(struct bio *bio); - -static void btrfs_bbio_propagate_error(struct btrfs_bio *bbio, - struct btrfs_bio *orig_bbio) -{ - /* - * For writes we tolerate nr_mirrors - 1 write failures, so we can't - * just blindly propagate a write failure here. Instead increment the - * error count in the original I/O context so that it is guaranteed to - * be larger than the error tolerance. - */ - if (bbio->bio.bi_end_io == &btrfs_orig_write_end_io) { - struct btrfs_io_stripe *orig_stripe = orig_bbio->bio.bi_private; - struct btrfs_io_context *orig_bioc = orig_stripe->bioc; - - atomic_add(orig_bioc->max_errors, &orig_bioc->error); - } else { - orig_bbio->bio.bi_status = bbio->bio.bi_status; - } -} - void btrfs_bio_end_io(struct btrfs_bio *bbio, blk_status_t status) { bbio->bio.bi_status = status; if (bbio->bio.bi_pool == &btrfs_clone_bioset) { struct btrfs_bio *orig_bbio = bbio->private; - if (bbio->bio.bi_status) - btrfs_bbio_propagate_error(bbio, orig_bbio); btrfs_cleanup_bio(bbio); bbio = orig_bbio; } - if (atomic_dec_and_test(&bbio->pending_ios)) + /* + * At this point, bbio always points to the original btrfs_bio. Save + * the first error in it. + */ + if (status != BLK_STS_OK) + cmpxchg(&bbio->status, BLK_STS_OK, status); + + if (atomic_dec_and_test(&bbio->pending_ios)) { + /* Load split bio's error which might be set above. */ + if (status == BLK_STS_OK) + bbio->bio.bi_status = READ_ONCE(bbio->status); __btrfs_bio_end_io(bbio); + } } static int next_repair_mirror(struct btrfs_failed_bio *fbio, int cur_mirror) diff --git a/fs/btrfs/bio.h b/fs/btrfs/bio.h index e48612340745..e2fe16074ad6 100644 --- a/fs/btrfs/bio.h +++ b/fs/btrfs/bio.h @@ -79,6 +79,9 @@ struct btrfs_bio { /* File system that this I/O operates on. */ struct btrfs_fs_info *fs_info; + /* Save the first error status of split bio. */ + blk_status_t status; + /* * This member must come last, bio_alloc_bioset will allocate enough * bytes for entire btrfs_bio but relies on bio being last. diff --git a/fs/btrfs/defrag.c b/fs/btrfs/defrag.c index b95ef44c326b..968dae953948 100644 --- a/fs/btrfs/defrag.c +++ b/fs/btrfs/defrag.c @@ -763,12 +763,12 @@ static struct extent_map *defrag_lookup_extent(struct inode *inode, u64 start, * We can get a merged extent, in that case, we need to re-search * tree to get the original em for defrag. * - * If @newer_than is 0 or em::generation < newer_than, we can trust - * this em, as either we don't care about the generation, or the - * merged extent map will be rejected anyway. + * This is because even if we have adjacent extents that are contiguous + * and compatible (same type and flags), we still want to defrag them + * so that we use less metadata (extent items in the extent tree and + * file extent items in the inode's subvolume tree). */ - if (em && (em->flags & EXTENT_FLAG_MERGED) && - newer_than && em->generation >= newer_than) { + if (em && (em->flags & EXTENT_FLAG_MERGED)) { free_extent_map(em); em = NULL; } diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c index 668c617444a5..1d93e1202c33 100644 --- a/fs/btrfs/extent_map.c +++ b/fs/btrfs/extent_map.c @@ -230,7 +230,12 @@ static bool mergeable_maps(const struct extent_map *prev, const struct extent_ma if (extent_map_end(prev) != next->start) return false; - if (prev->flags != next->flags) + /* + * The merged flag is not an on-disk flag, it just indicates we had the + * extent maps of 2 (or more) adjacent extents merged, so factor it out. + */ + if ((prev->flags & ~EXTENT_FLAG_MERGED) != + (next->flags & ~EXTENT_FLAG_MERGED)) return false; if (next->disk_bytenr < EXTENT_MAP_LAST_BYTE - 1) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 8f340ad1d938..eb51b609190f 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1105,6 +1105,7 @@ static void btrfs_close_one_device(struct btrfs_device *device) if (device->bdev) { fs_devices->open_devices--; device->bdev = NULL; + device->bdev_file = NULL; } clear_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state); btrfs_destroy_dev_zone_info(device); @@ -1262,35 +1262,46 @@ static s64 dax_unshare_iter(struct iomap_iter *iter) { struct iomap *iomap = &iter->iomap; const struct iomap *srcmap = iomap_iter_srcmap(iter); - loff_t pos = iter->pos; - loff_t length = iomap_length(iter); + loff_t copy_pos = iter->pos; + u64 copy_len = iomap_length(iter); + u32 mod; int id = 0; s64 ret = 0; void *daddr = NULL, *saddr = NULL; - /* don't bother with blocks that are not shared to start with */ - if (!(iomap->flags & IOMAP_F_SHARED)) - return length; + if (!iomap_want_unshare_iter(iter)) + return iomap_length(iter); + + /* + * Extend the file range to be aligned to fsblock/pagesize, because + * we need to copy entire blocks, not just the byte range specified. + * Invalidate the mapping because we're about to CoW. + */ + mod = offset_in_page(copy_pos); + if (mod) { + copy_len += mod; + copy_pos -= mod; + } + + mod = offset_in_page(copy_pos + copy_len); + if (mod) + copy_len += PAGE_SIZE - mod; + + invalidate_inode_pages2_range(iter->inode->i_mapping, + copy_pos >> PAGE_SHIFT, + (copy_pos + copy_len - 1) >> PAGE_SHIFT); id = dax_read_lock(); - ret = dax_iomap_direct_access(iomap, pos, length, &daddr, NULL); + ret = dax_iomap_direct_access(iomap, copy_pos, copy_len, &daddr, NULL); if (ret < 0) goto out_unlock; - /* zero the distance if srcmap is HOLE or UNWRITTEN */ - if (srcmap->flags & IOMAP_F_SHARED || srcmap->type == IOMAP_UNWRITTEN) { - memset(daddr, 0, length); - dax_flush(iomap->dax_dev, daddr, length); - ret = length; - goto out_unlock; - } - - ret = dax_iomap_direct_access(srcmap, pos, length, &saddr, NULL); + ret = dax_iomap_direct_access(srcmap, copy_pos, copy_len, &saddr, NULL); if (ret < 0) goto out_unlock; - if (copy_mc_to_kernel(daddr, saddr, length) == 0) - ret = length; + if (copy_mc_to_kernel(daddr, saddr, copy_len) == 0) + ret = iomap_length(iter); else ret = -EIO; diff --git a/fs/erofs/super.c b/fs/erofs/super.c index 320d586c3896..bed3dbe5b7cb 100644 --- a/fs/erofs/super.c +++ b/fs/erofs/super.c @@ -709,7 +709,9 @@ static int erofs_fc_get_tree(struct fs_context *fc) if (IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND) && sbi->fsid) return get_tree_nodev(fc, erofs_fc_fill_super); - ret = get_tree_bdev(fc, erofs_fc_fill_super); + ret = get_tree_bdev_flags(fc, erofs_fc_fill_super, + IS_ENABLED(CONFIG_EROFS_FS_BACKED_BY_FILE) ? + GET_TREE_BDEV_QUIET_LOOKUP : 0); #ifdef CONFIG_EROFS_FS_BACKED_BY_FILE if (ret == -ENOTBLK) { if (!fc->source) diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c index aa587b2142e2..ef0b68bccbb6 100644 --- a/fs/iomap/buffered-io.c +++ b/fs/iomap/buffered-io.c @@ -1277,22 +1277,7 @@ static loff_t iomap_unshare_iter(struct iomap_iter *iter) loff_t length = iomap_length(iter); loff_t written = 0; - /* Don't bother with blocks that are not shared to start with. */ - if (!(iomap->flags & IOMAP_F_SHARED)) - return length; - - /* - * Don't bother with delalloc reservations, holes or unwritten extents. - * - * Note that we use srcmap directly instead of iomap_iter_srcmap as - * unsharing requires providing a separate source map, and the presence - * of one is a good indicator that unsharing is needed, unlike - * IOMAP_F_SHARED which can be set for any data that goes into the COW - * fork for XFS. - */ - if (iter->srcmap.type == IOMAP_HOLE || - iter->srcmap.type == IOMAP_DELALLOC || - iter->srcmap.type == IOMAP_UNWRITTEN) + if (!iomap_want_unshare_iter(iter)) return length; do { diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index b5a6bf4f459f..d32f2dfd148f 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -1841,14 +1841,12 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (!async_copy) goto out_err; async_copy->cp_nn = nn; + INIT_LIST_HEAD(&async_copy->copies); + refcount_set(&async_copy->refcount, 1); /* Arbitrary cap on number of pending async copy operations */ if (atomic_inc_return(&nn->pending_async_copies) > - (int)rqstp->rq_pool->sp_nrthreads) { - atomic_dec(&nn->pending_async_copies); + (int)rqstp->rq_pool->sp_nrthreads) goto out_err; - } - INIT_LIST_HEAD(&async_copy->copies); - refcount_set(&async_copy->refcount, 1); async_copy->cp_src = kmalloc(sizeof(*async_copy->cp_src), GFP_KERNEL); if (!async_copy->cp_src) goto out_err; diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c index 4905063790c5..9b108052d9f7 100644 --- a/fs/nilfs2/namei.c +++ b/fs/nilfs2/namei.c @@ -157,6 +157,9 @@ static int nilfs_symlink(struct mnt_idmap *idmap, struct inode *dir, /* slow symlink */ inode->i_op = &nilfs_symlink_inode_operations; inode_nohighmem(inode); + mapping_set_gfp_mask(inode->i_mapping, + mapping_gfp_constraint(inode->i_mapping, + ~__GFP_FS)); inode->i_mapping->a_ops = &nilfs_aops; err = page_symlink(inode, symname, l); if (err) diff --git a/fs/nilfs2/page.c b/fs/nilfs2/page.c index 5436eb0424bd..10def4b55995 100644 --- a/fs/nilfs2/page.c +++ b/fs/nilfs2/page.c @@ -401,6 +401,7 @@ void nilfs_clear_folio_dirty(struct folio *folio) folio_clear_uptodate(folio); folio_clear_mappedtodisk(folio); + folio_clear_checked(folio); head = folio_buffers(folio); if (head) { diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 58887456e3c5..06af21982c16 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -1787,6 +1787,14 @@ int ocfs2_remove_inode_range(struct inode *inode, return 0; if (OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL) { + int id_count = ocfs2_max_inline_data_with_xattr(inode->i_sb, di); + + if (byte_start > id_count || byte_start + byte_len > id_count) { + ret = -EINVAL; + mlog_errno(ret); + goto out; + } + ret = ocfs2_truncate_inline(inode, di_bh, byte_start, byte_start + byte_len, 0); if (ret) { diff --git a/fs/squashfs/file_direct.c b/fs/squashfs/file_direct.c index 22251743fadf..d19d4db74af8 100644 --- a/fs/squashfs/file_direct.c +++ b/fs/squashfs/file_direct.c @@ -30,7 +30,8 @@ int squashfs_readpage_block(struct page *target_page, u64 block, int bsize, int mask = (1 << (msblk->block_log - PAGE_SHIFT)) - 1; loff_t start_index = folio->index & ~mask; loff_t end_index = start_index | mask; - int i, n, pages, bytes, res = -ENOMEM; + loff_t index; + int i, pages, bytes, res = -ENOMEM; struct page **page, *last_page; struct squashfs_page_actor *actor; void *pageaddr; @@ -45,9 +46,9 @@ int squashfs_readpage_block(struct page *target_page, u64 block, int bsize, return res; /* Try to grab all the pages covered by the Squashfs block */ - for (i = 0, n = start_index; n <= end_index; n++) { - page[i] = (n == folio->index) ? target_page : - grab_cache_page_nowait(target_page->mapping, n); + for (i = 0, index = start_index; index <= end_index; index++) { + page[i] = (index == folio->index) ? target_page : + grab_cache_page_nowait(target_page->mapping, index); if (page[i] == NULL) continue; diff --git a/fs/super.c b/fs/super.c index 1db230432960..c9c7223bc2a2 100644 --- a/fs/super.c +++ b/fs/super.c @@ -1596,13 +1596,14 @@ int setup_bdev_super(struct super_block *sb, int sb_flags, EXPORT_SYMBOL_GPL(setup_bdev_super); /** - * get_tree_bdev - Get a superblock based on a single block device + * get_tree_bdev_flags - Get a superblock based on a single block device * @fc: The filesystem context holding the parameters * @fill_super: Helper to initialise a new superblock + * @flags: GET_TREE_BDEV_* flags */ -int get_tree_bdev(struct fs_context *fc, - int (*fill_super)(struct super_block *, - struct fs_context *)) +int get_tree_bdev_flags(struct fs_context *fc, + int (*fill_super)(struct super_block *sb, + struct fs_context *fc), unsigned int flags) { struct super_block *s; int error = 0; @@ -1613,10 +1614,10 @@ int get_tree_bdev(struct fs_context *fc, error = lookup_bdev(fc->source, &dev); if (error) { - errorf(fc, "%s: Can't lookup blockdev", fc->source); + if (!(flags & GET_TREE_BDEV_QUIET_LOOKUP)) + errorf(fc, "%s: Can't lookup blockdev", fc->source); return error; } - fc->sb_flags |= SB_NOSEC; s = sget_dev(fc, dev); if (IS_ERR(s)) @@ -1644,6 +1645,19 @@ int get_tree_bdev(struct fs_context *fc, fc->root = dget(s->s_root); return 0; } +EXPORT_SYMBOL_GPL(get_tree_bdev_flags); + +/** + * get_tree_bdev - Get a superblock based on a single block device + * @fc: The filesystem context holding the parameters + * @fill_super: Helper to initialise a new superblock + */ +int get_tree_bdev(struct fs_context *fc, + int (*fill_super)(struct super_block *, + struct fs_context *)) +{ + return get_tree_bdev_flags(fc, fill_super, 0); +} EXPORT_SYMBOL(get_tree_bdev); static int test_bdev_super(struct super_block *s, void *data) diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c index 68cdd89c97a3..7c0bd0b55f88 100644 --- a/fs/userfaultfd.c +++ b/fs/userfaultfd.c @@ -692,6 +692,34 @@ void dup_userfaultfd_complete(struct list_head *fcs) } } +void dup_userfaultfd_fail(struct list_head *fcs) +{ + struct userfaultfd_fork_ctx *fctx, *n; + + /* + * An error has occurred on fork, we will tear memory down, but have + * allocated memory for fctx's and raised reference counts for both the + * original and child contexts (and on the mm for each as a result). + * + * These would ordinarily be taken care of by a user handling the event, + * but we are no longer doing so, so manually clean up here. + * + * mm tear down will take care of cleaning up VMA contexts. + */ + list_for_each_entry_safe(fctx, n, fcs, list) { + struct userfaultfd_ctx *octx = fctx->orig; + struct userfaultfd_ctx *ctx = fctx->new; + + atomic_dec(&octx->mmap_changing); + VM_BUG_ON(atomic_read(&octx->mmap_changing) < 0); + userfaultfd_ctx_put(octx); + userfaultfd_ctx_put(ctx); + + list_del(&fctx->list); + kfree(fctx); + } +} + void mremap_userfaultfd_prep(struct vm_area_struct *vma, struct vm_userfaultfd_ctx *vm_ctx) { diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c index 04f64cf9777e..22bdbb3e9980 100644 --- a/fs/xfs/libxfs/xfs_alloc.c +++ b/fs/xfs/libxfs/xfs_alloc.c @@ -1923,7 +1923,7 @@ restart: error = -EFSCORRUPTED; goto error0; } - if (flen < bestrlen) + if (flen <= bestrlen) break; busy = xfs_alloc_compute_aligned(args, fbno, flen, &rbno, &rlen, &busy_gen); diff --git a/fs/xfs/xfs_filestream.c b/fs/xfs/xfs_filestream.c index e3aaa0555597..290ba8887d29 100644 --- a/fs/xfs/xfs_filestream.c +++ b/fs/xfs/xfs_filestream.c @@ -64,25 +64,31 @@ xfs_filestream_pick_ag( struct xfs_perag *pag; struct xfs_perag *max_pag = NULL; xfs_extlen_t minlen = *longest; - xfs_extlen_t free = 0, minfree, maxfree = 0; + xfs_extlen_t minfree, maxfree = 0; xfs_agnumber_t agno; bool first_pass = true; - int err; /* 2% of an AG's blocks must be free for it to be chosen. */ minfree = mp->m_sb.sb_agblocks / 50; restart: for_each_perag_wrap(mp, start_agno, agno, pag) { + int err; + trace_xfs_filestream_scan(pag, pino); + *longest = 0; err = xfs_bmap_longest_free_extent(pag, NULL, longest); if (err) { - if (err != -EAGAIN) - break; - /* Couldn't lock the AGF, skip this AG. */ - err = 0; - continue; + if (err == -EAGAIN) { + /* Couldn't lock the AGF, skip this AG. */ + err = 0; + continue; + } + xfs_perag_rele(pag); + if (max_pag) + xfs_perag_rele(max_pag); + return err; } /* Keep track of the AG with the most free blocks. */ @@ -107,8 +113,9 @@ restart: !(flags & XFS_PICK_USERDATA) || (flags & XFS_PICK_LOWSPACE))) { /* Break out, retaining the reference on the AG. */ - free = pag->pagf_freeblks; - break; + if (max_pag) + xfs_perag_rele(max_pag); + goto done; } } @@ -116,57 +123,47 @@ restart: atomic_dec(&pag->pagf_fstrms); } - if (err) { - xfs_perag_rele(pag); - if (max_pag) - xfs_perag_rele(max_pag); - return err; + /* + * Allow a second pass to give xfs_bmap_longest_free_extent() another + * attempt at locking AGFs that it might have skipped over before we + * fail. + */ + if (first_pass) { + first_pass = false; + goto restart; } - if (!pag) { - /* - * Allow a second pass to give xfs_bmap_longest_free_extent() - * another attempt at locking AGFs that it might have skipped - * over before we fail. - */ - if (first_pass) { - first_pass = false; - goto restart; - } + /* + * We must be low on data space, so run a final lowspace optimised + * selection pass if we haven't already. + */ + if (!(flags & XFS_PICK_LOWSPACE)) { + flags |= XFS_PICK_LOWSPACE; + goto restart; + } - /* - * We must be low on data space, so run a final lowspace - * optimised selection pass if we haven't already. - */ - if (!(flags & XFS_PICK_LOWSPACE)) { - flags |= XFS_PICK_LOWSPACE; - goto restart; + /* + * No unassociated AGs are available, so select the AG with the most + * free space, regardless of whether it's already in use by another + * filestream. It none suit, just use whatever AG we can grab. + */ + if (!max_pag) { + for_each_perag_wrap(args->mp, 0, start_agno, pag) { + max_pag = pag; + break; } - /* - * No unassociated AGs are available, so select the AG with the - * most free space, regardless of whether it's already in use by - * another filestream. It none suit, just use whatever AG we can - * grab. - */ - if (!max_pag) { - for_each_perag_wrap(args->mp, 0, start_agno, args->pag) - break; - atomic_inc(&args->pag->pagf_fstrms); - *longest = 0; - } else { - pag = max_pag; - free = maxfree; - atomic_inc(&pag->pagf_fstrms); - } - } else if (max_pag) { - xfs_perag_rele(max_pag); + /* Bail if there are no AGs at all to select from. */ + if (!max_pag) + return -ENOSPC; } - trace_xfs_filestream_pick(pag, pino, free); + pag = max_pag; + atomic_inc(&pag->pagf_fstrms); +done: + trace_xfs_filestream_pick(pag, pino); args->pag = pag; return 0; - } static struct xfs_inode * diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index bcc277fc0a83..19dcb569a3e7 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -1409,7 +1409,7 @@ xfs_inactive( if (S_ISREG(VFS_I(ip)->i_mode) && (ip->i_disk_size != 0 || XFS_ISIZE(ip) != 0 || - ip->i_df.if_nextents > 0 || ip->i_delayed_blks > 0)) + xfs_inode_has_filedata(ip))) truncate = 1; if (xfs_iflags_test(ip, XFS_IQUOTAUNCHECKED)) { diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index 97ed912306fd..03944b6c5fba 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -292,6 +292,11 @@ static inline bool xfs_is_cow_inode(struct xfs_inode *ip) return xfs_is_reflink_inode(ip) || xfs_is_always_cow_inode(ip); } +static inline bool xfs_inode_has_filedata(const struct xfs_inode *ip) +{ + return ip->i_df.if_nextents > 0 || ip->i_delayed_blks > 0; +} + /* * Check if an inode has any data in the COW fork. This might be often false * even for inodes with the reflink flag when there is no pending COW operation. diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c index a20d426ef021..2567fd2a0994 100644 --- a/fs/xfs/xfs_ioctl.c +++ b/fs/xfs/xfs_ioctl.c @@ -481,7 +481,7 @@ xfs_ioctl_setattr_xflags( if (rtflag != XFS_IS_REALTIME_INODE(ip)) { /* Can't change realtime flag if any extents are allocated. */ - if (ip->i_df.if_nextents || ip->i_delayed_blks) + if (xfs_inode_has_filedata(ip)) return -EINVAL; /* @@ -602,7 +602,7 @@ xfs_ioctl_setattr_check_extsize( if (!fa->fsx_valid) return 0; - if (S_ISREG(VFS_I(ip)->i_mode) && ip->i_df.if_nextents && + if (S_ISREG(VFS_I(ip)->i_mode) && xfs_inode_has_filedata(ip) && XFS_FSB_TO_B(mp, ip->i_extsize) != fa->fsx_extsize) return -EINVAL; diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c index 916531d9f83c..86da16f54be9 100644 --- a/fs/xfs/xfs_iomap.c +++ b/fs/xfs/xfs_iomap.c @@ -707,7 +707,7 @@ imap_needs_cow( return false; /* when zeroing we don't have to COW holes or unwritten extents */ - if (flags & IOMAP_ZERO) { + if (flags & (IOMAP_UNSHARE | IOMAP_ZERO)) { if (!nimaps || imap->br_startblock == HOLESTARTBLOCK || imap->br_state == XFS_EXT_UNWRITTEN) diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index ee9f0b1f548d..fcb2bad4f76e 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -691,8 +691,8 @@ DEFINE_FILESTREAM_EVENT(xfs_filestream_lookup); DEFINE_FILESTREAM_EVENT(xfs_filestream_scan); TRACE_EVENT(xfs_filestream_pick, - TP_PROTO(struct xfs_perag *pag, xfs_ino_t ino, xfs_extlen_t free), - TP_ARGS(pag, ino, free), + TP_PROTO(struct xfs_perag *pag, xfs_ino_t ino), + TP_ARGS(pag, ino), TP_STRUCT__entry( __field(dev_t, dev) __field(xfs_ino_t, ino) @@ -703,14 +703,9 @@ TRACE_EVENT(xfs_filestream_pick, TP_fast_assign( __entry->dev = pag->pag_mount->m_super->s_dev; __entry->ino = ino; - if (pag) { - __entry->agno = pag->pag_agno; - __entry->streams = atomic_read(&pag->pagf_fstrms); - } else { - __entry->agno = NULLAGNUMBER; - __entry->streams = 0; - } - __entry->free = free; + __entry->agno = pag->pag_agno; + __entry->streams = atomic_read(&pag->pagf_fstrms); + __entry->free = pag->pagf_freeblks; ), TP_printk("dev %d:%d ino 0x%llx agno 0x%x streams %d free %d", MAJOR(__entry->dev), MINOR(__entry->dev), diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h index 76e44e102780..62d368bcd9ec 100644 --- a/include/acpi/cppc_acpi.h +++ b/include/acpi/cppc_acpi.h @@ -65,7 +65,7 @@ struct cpc_desc { int write_cmd_status; int write_cmd_id; /* Lock used for RMW operations in cpc_write() */ - spinlock_t rmw_lock; + raw_spinlock_t rmw_lock; struct cpc_register_resource cpc_regs[MAX_CPC_REG_ENT]; struct acpi_psd_package domain_info; struct kobject kobj; diff --git a/include/drm/drm_kunit_helpers.h b/include/drm/drm_kunit_helpers.h index e7cc17ee4934..afdd46ef04f7 100644 --- a/include/drm/drm_kunit_helpers.h +++ b/include/drm/drm_kunit_helpers.h @@ -120,4 +120,8 @@ drm_kunit_helper_create_crtc(struct kunit *test, const struct drm_crtc_funcs *funcs, const struct drm_crtc_helper_funcs *helper_funcs); +struct drm_display_mode * +drm_kunit_display_mode_from_cea_vic(struct kunit *test, struct drm_device *dev, + u8 video_code); + #endif // DRM_KUNIT_HELPERS_H_ diff --git a/include/linux/alloc_tag.h b/include/linux/alloc_tag.h index 1f0a9ff23a2c..941deffc590d 100644 --- a/include/linux/alloc_tag.h +++ b/include/linux/alloc_tag.h @@ -135,18 +135,21 @@ static inline void alloc_tag_sub_check(union codetag_ref *ref) {} #endif /* Caller should verify both ref and tag to be valid */ -static inline void __alloc_tag_ref_set(union codetag_ref *ref, struct alloc_tag *tag) +static inline bool __alloc_tag_ref_set(union codetag_ref *ref, struct alloc_tag *tag) { alloc_tag_add_check(ref, tag); if (!ref || !tag) - return; + return false; ref->ct = &tag->ct; + return true; } -static inline void alloc_tag_ref_set(union codetag_ref *ref, struct alloc_tag *tag) +static inline bool alloc_tag_ref_set(union codetag_ref *ref, struct alloc_tag *tag) { - __alloc_tag_ref_set(ref, tag); + if (unlikely(!__alloc_tag_ref_set(ref, tag))) + return false; + /* * We need in increment the call counter every time we have a new * allocation or when we split a large allocation into smaller ones. @@ -154,12 +157,13 @@ static inline void alloc_tag_ref_set(union codetag_ref *ref, struct alloc_tag *t * counter because when we free each part the counter will be decremented. */ this_cpu_inc(tag->counters->calls); + return true; } static inline void alloc_tag_add(union codetag_ref *ref, struct alloc_tag *tag, size_t bytes) { - alloc_tag_ref_set(ref, tag); - this_cpu_add(tag->counters->bytes, bytes); + if (likely(alloc_tag_ref_set(ref, tag))) + this_cpu_add(tag->counters->bytes, bytes); } static inline void alloc_tag_sub(union codetag_ref *ref, size_t bytes) diff --git a/include/linux/bpf_mem_alloc.h b/include/linux/bpf_mem_alloc.h index aaf004d94322..e45162ef59bb 100644 --- a/include/linux/bpf_mem_alloc.h +++ b/include/linux/bpf_mem_alloc.h @@ -33,6 +33,9 @@ int bpf_mem_alloc_percpu_init(struct bpf_mem_alloc *ma, struct obj_cgroup *objcg int bpf_mem_alloc_percpu_unit_init(struct bpf_mem_alloc *ma, int size); void bpf_mem_alloc_destroy(struct bpf_mem_alloc *ma); +/* Check the allocation size for kmalloc equivalent allocator */ +int bpf_mem_alloc_check_size(bool percpu, size_t size); + /* kmalloc/kfree equivalent: */ void *bpf_mem_alloc(struct bpf_mem_alloc *ma, size_t size); void bpf_mem_free(struct bpf_mem_alloc *ma, void *ptr); diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h index f805adaa316e..cd6f9aae311f 100644 --- a/include/linux/compiler-gcc.h +++ b/include/linux/compiler-gcc.h @@ -80,7 +80,11 @@ #define __noscs __attribute__((__no_sanitize__("shadow-call-stack"))) #endif +#ifdef __SANITIZE_HWADDRESS__ +#define __no_sanitize_address __attribute__((__no_sanitize__("hwaddress"))) +#else #define __no_sanitize_address __attribute__((__no_sanitize_address__)) +#endif #if defined(__SANITIZE_THREAD__) #define __no_sanitize_thread __attribute__((__no_sanitize_thread__)) diff --git a/include/linux/device.h b/include/linux/device.h index b4bde8d22697..667cb6db9019 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -1078,6 +1078,9 @@ int device_for_each_child(struct device *dev, void *data, int (*fn)(struct device *dev, void *data)); int device_for_each_child_reverse(struct device *dev, void *data, int (*fn)(struct device *dev, void *data)); +int device_for_each_child_reverse_from(struct device *parent, + struct device *from, const void *data, + int (*fn)(struct device *, const void *)); struct device *device_find_child(struct device *dev, void *data, int (*match)(struct device *dev, void *data)); struct device *device_find_child_by_name(struct device *parent, diff --git a/include/linux/fs_context.h b/include/linux/fs_context.h index c13e99cbbf81..4b4bfef6f053 100644 --- a/include/linux/fs_context.h +++ b/include/linux/fs_context.h @@ -160,6 +160,12 @@ extern int get_tree_keyed(struct fs_context *fc, int setup_bdev_super(struct super_block *sb, int sb_flags, struct fs_context *fc); + +#define GET_TREE_BDEV_QUIET_LOOKUP 0x0001 +int get_tree_bdev_flags(struct fs_context *fc, + int (*fill_super)(struct super_block *sb, + struct fs_context *fc), unsigned int flags); + extern int get_tree_bdev(struct fs_context *fc, int (*fill_super)(struct super_block *sb, struct fs_context *fc)); diff --git a/include/linux/input.h b/include/linux/input.h index 89a0be6ee0e2..cd866b020a01 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -339,12 +339,16 @@ struct input_handler { * @name: name given to the handle by handler that created it * @dev: input device the handle is attached to * @handler: handler that works with the device through this handle + * @handle_events: event sequence handler. It is set up by the input core + * according to event handling method specified in the @handler. See + * input_handle_setup_event_handler(). + * This method is being called by the input core with interrupts disabled + * and dev->event_lock spinlock held and so it may not sleep. * @d_node: used to put the handle on device's list of attached handles * @h_node: used to put the handle on handler's list of handles from which * it gets events */ struct input_handle { - void *private; int open; @@ -353,6 +357,10 @@ struct input_handle { struct input_dev *dev; struct input_handler *handler; + unsigned int (*handle_events)(struct input_handle *handle, + struct input_value *vals, + unsigned int count); + struct list_head d_node; struct list_head h_node; }; diff --git a/include/linux/iomap.h b/include/linux/iomap.h index d0420e962ffd..f61407e3b121 100644 --- a/include/linux/iomap.h +++ b/include/linux/iomap.h @@ -270,6 +270,25 @@ static inline loff_t iomap_last_written_block(struct inode *inode, loff_t pos, return round_up(pos + written, i_blocksize(inode)); } +/* + * Check if the range needs to be unshared for a FALLOC_FL_UNSHARE_RANGE + * operation. + * + * Don't bother with blocks that are not shared to start with; or mappings that + * cannot be shared, such as inline data, delalloc reservations, holes or + * unwritten extents. + * + * Note that we use srcmap directly instead of iomap_iter_srcmap as unsharing + * requires providing a separate source map, and the presence of one is a good + * indicator that unsharing is needed, unlike IOMAP_F_SHARED which can be set + * for any data that goes into the COW fork for XFS. + */ +static inline bool iomap_want_unshare_iter(const struct iomap_iter *iter) +{ + return (iter->iomap.flags & IOMAP_F_SHARED) && + iter->srcmap.type == IOMAP_MAPPED; +} + ssize_t iomap_file_buffered_write(struct kiocb *iocb, struct iov_iter *from, const struct iomap_ops *ops, void *private); int iomap_read_folio(struct folio *folio, const struct iomap_ops *ops); diff --git a/include/linux/ksm.h b/include/linux/ksm.h index 11690dacd986..ec9c05044d4f 100644 --- a/include/linux/ksm.h +++ b/include/linux/ksm.h @@ -54,12 +54,11 @@ static inline long mm_ksm_zero_pages(struct mm_struct *mm) return atomic_long_read(&mm->ksm_zero_pages); } -static inline int ksm_fork(struct mm_struct *mm, struct mm_struct *oldmm) +static inline void ksm_fork(struct mm_struct *mm, struct mm_struct *oldmm) { + /* Adding mm to ksm is best effort on fork. */ if (test_bit(MMF_VM_MERGEABLE, &oldmm->flags)) - return __ksm_enter(mm); - - return 0; + __ksm_enter(mm); } static inline int ksm_execve(struct mm_struct *mm) @@ -107,9 +106,8 @@ static inline int ksm_disable(struct mm_struct *mm) return 0; } -static inline int ksm_fork(struct mm_struct *mm, struct mm_struct *oldmm) +static inline void ksm_fork(struct mm_struct *mm, struct mm_struct *oldmm) { - return 0; } static inline int ksm_execve(struct mm_struct *mm) diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 17506e4a2835..5b1c984daf45 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -458,9 +458,7 @@ struct lru_gen_folio { enum { MM_LEAF_TOTAL, /* total leaf entries */ - MM_LEAF_OLD, /* old leaf entries */ MM_LEAF_YOUNG, /* young leaf entries */ - MM_NONLEAF_TOTAL, /* total non-leaf entries */ MM_NONLEAF_FOUND, /* non-leaf entries found in Bloom filters */ MM_NONLEAF_ADDED, /* non-leaf entries added to Bloom filters */ NR_MM_STATS @@ -557,7 +555,7 @@ struct lru_gen_memcg { void lru_gen_init_pgdat(struct pglist_data *pgdat); void lru_gen_init_lruvec(struct lruvec *lruvec); -void lru_gen_look_around(struct page_vma_mapped_walk *pvmw); +bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw); void lru_gen_init_memcg(struct mem_cgroup *memcg); void lru_gen_exit_memcg(struct mem_cgroup *memcg); @@ -576,8 +574,9 @@ static inline void lru_gen_init_lruvec(struct lruvec *lruvec) { } -static inline void lru_gen_look_around(struct page_vma_mapped_walk *pvmw) +static inline bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw) { + return false; } static inline void lru_gen_init_memcg(struct mem_cgroup *memcg) diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index 1b3a76710487..cc839e4365c1 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h @@ -975,12 +975,16 @@ static __always_inline bool folio_test_##fname(const struct folio *folio) \ } \ static __always_inline void __folio_set_##fname(struct folio *folio) \ { \ + if (folio_test_##fname(folio)) \ + return; \ VM_BUG_ON_FOLIO(data_race(folio->page.page_type) != UINT_MAX, \ folio); \ folio->page.page_type = (unsigned int)PGTY_##lname << 24; \ } \ static __always_inline void __folio_clear_##fname(struct folio *folio) \ { \ + if (folio->page.page_type == UINT_MAX) \ + return; \ VM_BUG_ON_FOLIO(!folio_test_##fname(folio), folio); \ folio->page.page_type = UINT_MAX; \ } @@ -993,11 +997,15 @@ static __always_inline int Page##uname(const struct page *page) \ } \ static __always_inline void __SetPage##uname(struct page *page) \ { \ + if (Page##uname(page)) \ + return; \ VM_BUG_ON_PAGE(data_race(page->page_type) != UINT_MAX, page); \ page->page_type = (unsigned int)PGTY_##lname << 24; \ } \ static __always_inline void __ClearPage##uname(struct page *page) \ { \ + if (page->page_type == UINT_MAX) \ + return; \ VM_BUG_ON_PAGE(!Page##uname(page), page); \ page->page_type = UINT_MAX; \ } diff --git a/include/linux/swap.h b/include/linux/swap.h index ca533b478c21..f3e0ac20c2e8 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -335,6 +335,7 @@ struct swap_info_struct { * list. */ struct work_struct discard_work; /* discard worker */ + struct work_struct reclaim_work; /* reclaim worker */ struct list_head discard_clusters; /* discard clusters list */ struct plist_node avail_lists[]; /* * entries in swap_avail_heads, one diff --git a/include/linux/tick.h b/include/linux/tick.h index 72744638c5b0..99c9c5a7252a 100644 --- a/include/linux/tick.h +++ b/include/linux/tick.h @@ -251,12 +251,19 @@ static inline void tick_dep_set_task(struct task_struct *tsk, if (tick_nohz_full_enabled()) tick_nohz_dep_set_task(tsk, bit); } + static inline void tick_dep_clear_task(struct task_struct *tsk, enum tick_dep_bits bit) { if (tick_nohz_full_enabled()) tick_nohz_dep_clear_task(tsk, bit); } + +static inline void tick_dep_init_task(struct task_struct *tsk) +{ + atomic_set(&tsk->tick_dep_mask, 0); +} + static inline void tick_dep_set_signal(struct task_struct *tsk, enum tick_dep_bits bit) { @@ -290,6 +297,7 @@ static inline void tick_dep_set_task(struct task_struct *tsk, enum tick_dep_bits bit) { } static inline void tick_dep_clear_task(struct task_struct *tsk, enum tick_dep_bits bit) { } +static inline void tick_dep_init_task(struct task_struct *tsk) { } static inline void tick_dep_set_signal(struct task_struct *tsk, enum tick_dep_bits bit) { } static inline void tick_dep_clear_signal(struct signal_struct *signal, diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h index 39c7cf82b0c2..43844510d5d0 100644 --- a/include/linux/uaccess.h +++ b/include/linux/uaccess.h @@ -38,6 +38,7 @@ #else #define can_do_masked_user_access() 0 #define masked_user_access_begin(src) NULL + #define mask_user_address(src) (src) #endif /* @@ -159,19 +160,27 @@ _inline_copy_from_user(void *to, const void __user *from, unsigned long n) { unsigned long res = n; might_fault(); - if (!should_fail_usercopy() && likely(access_ok(from, n))) { + if (should_fail_usercopy()) + goto fail; + if (can_do_masked_user_access()) + from = mask_user_address(from); + else { + if (!access_ok(from, n)) + goto fail; /* * Ensure that bad access_ok() speculation will not * lead to nasty side effects *after* the copy is * finished: */ barrier_nospec(); - instrument_copy_from_user_before(to, from, n); - res = raw_copy_from_user(to, from, n); - instrument_copy_from_user_after(to, from, n, res); } - if (unlikely(res)) - memset(to + (n - res), 0, res); + instrument_copy_from_user_before(to, from, n); + res = raw_copy_from_user(to, from, n); + instrument_copy_from_user_after(to, from, n, res); + if (likely(!res)) + return 0; +fail: + memset(to + (n - res), 0, res); return res; } extern __must_check unsigned long diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h index 5a7f96684ea2..ebdfef124b2b 100644 --- a/include/linux/usb/chipidea.h +++ b/include/linux/usb/chipidea.h @@ -65,6 +65,7 @@ struct ci_hdrc_platform_data { #define CI_HDRC_PHY_VBUS_CONTROL BIT(16) #define CI_HDRC_HAS_PORTSC_PEC_MISSED BIT(17) #define CI_HDRC_FORCE_VBUS_ACTIVE_ALWAYS BIT(18) +#define CI_HDRC_HAS_SHORT_PKT_LIMIT BIT(19) enum usb_dr_mode dr_mode; #define CI_HDRC_CONTROLLER_RESET_EVENT 0 #define CI_HDRC_CONTROLLER_STOPPED_EVENT 1 diff --git a/include/linux/usb/storage.h b/include/linux/usb/storage.h index 2827ce72e502..8539956bc2be 100644 --- a/include/linux/usb/storage.h +++ b/include/linux/usb/storage.h @@ -53,7 +53,7 @@ struct bulk_cb_wrap { __le32 Signature; /* contains 'USBC' */ __u32 Tag; /* unique per command id */ __le32 DataTransferLength; /* size of data */ - __u8 Flags; /* direction in bit 0 */ + __u8 Flags; /* direction in bit 7 */ __u8 Lun; /* LUN normally 0 */ __u8 Length; /* length of the CDB */ __u8 CDB[16]; /* max command */ diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index 549275f8ac1b..d616b8807000 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -87,6 +87,17 @@ enum typec_orientation { TYPEC_ORIENTATION_REVERSE, }; +enum usb_mode { + USB_MODE_NONE, + USB_MODE_USB2, + USB_MODE_USB3, + USB_MODE_USB4 +}; + +#define USB_CAPABILITY_USB2 BIT(0) +#define USB_CAPABILITY_USB3 BIT(1) +#define USB_CAPABILITY_USB4 BIT(2) + /* * struct enter_usb_data - Enter_USB Message details * @eudo: Enter_USB Data Object @@ -209,6 +220,7 @@ struct typec_cable_desc { * @accessory: Audio, Debug or none. * @identity: Discover Identity command data * @pd_revision: USB Power Delivery Specification Revision if supported + * @usb_capability: Supported USB Modes * @attach: Notification about attached USB device * @deattach: Notification about removed USB device * @@ -226,6 +238,7 @@ struct typec_partner_desc { enum typec_accessory accessory; struct usb_pd_identity *identity; u16 pd_revision; /* 0300H = "3.0" */ + u8 usb_capability; void (*attach)(struct typec_partner *partner, struct device *dev); void (*deattach)(struct typec_partner *partner, struct device *dev); @@ -240,6 +253,8 @@ struct typec_partner_desc { * @port_type_set: Set port type * @pd_get: Get available USB Power Delivery Capabilities. * @pd_set: Set USB Power Delivery Capabilities. + * @default_usb_mode_set: USB Mode to be used by default with Enter_USB Message + * @enter_usb_mode: Change the active USB Mode */ struct typec_operations { int (*try_role)(struct typec_port *port, int role); @@ -250,6 +265,8 @@ struct typec_operations { enum typec_port_type type); struct usb_power_delivery **(*pd_get)(struct typec_port *port); int (*pd_set)(struct typec_port *port, struct usb_power_delivery *pd); + int (*default_usb_mode_set)(struct typec_port *port, enum usb_mode mode); + int (*enter_usb_mode)(struct typec_port *port, enum usb_mode mode); }; enum usb_pd_svdm_ver { @@ -267,6 +284,7 @@ enum usb_pd_svdm_ver { * @svdm_version: USB PD Structured VDM version if supported * @prefer_role: Initial role preference (DRP ports). * @accessory: Supported Accessory Modes + * @usb_capability: Supported USB Modes * @fwnode: Optional fwnode of the port * @driver_data: Private pointer for driver specific info * @pd: Optional USB Power Delivery Support @@ -283,6 +301,7 @@ struct typec_capability { int prefer_role; enum typec_accessory accessory[TYPEC_MAX_ACCESSORY]; unsigned int orientation_aware:1; + u8 usb_capability; struct fwnode_handle *fwnode; void *driver_data; @@ -350,6 +369,9 @@ int typec_port_set_usb_power_delivery(struct typec_port *port, struct usb_power_ int typec_partner_set_usb_power_delivery(struct typec_partner *partner, struct usb_power_delivery *pd); +void typec_partner_set_usb_mode(struct typec_partner *partner, enum usb_mode usb_mode); +void typec_port_set_usb_mode(struct typec_port *port, enum usb_mode mode); + /** * struct typec_connector - Representation of Type-C port for external drivers * @attach: notification about device removal diff --git a/include/linux/userfaultfd_k.h b/include/linux/userfaultfd_k.h index 9fc6ce15c499..cb40f1a1d081 100644 --- a/include/linux/userfaultfd_k.h +++ b/include/linux/userfaultfd_k.h @@ -249,6 +249,7 @@ static inline bool vma_can_userfault(struct vm_area_struct *vma, extern int dup_userfaultfd(struct vm_area_struct *, struct list_head *); extern void dup_userfaultfd_complete(struct list_head *); +void dup_userfaultfd_fail(struct list_head *); extern void mremap_userfaultfd_prep(struct vm_area_struct *, struct vm_userfaultfd_ctx *); @@ -351,6 +352,10 @@ static inline void dup_userfaultfd_complete(struct list_head *l) { } +static inline void dup_userfaultfd_fail(struct list_head *l) +{ +} + static inline void mremap_userfaultfd_prep(struct vm_area_struct *vma, struct vm_userfaultfd_ctx *ctx) { diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 69ec1eb41a09..941dc62f3027 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -6130,6 +6130,50 @@ void wiphy_delayed_work_flush(struct wiphy *wiphy, struct wiphy_delayed_work *dwork); /** + * wiphy_delayed_work_pending - Find out whether a wiphy delayable + * work item is currently pending. + * + * @wiphy: the wiphy, for debug purposes + * @dwork: the delayed work in question + * + * Return: true if timer is pending, false otherwise + * + * How wiphy_delayed_work_queue() works is by setting a timer which + * when it expires calls wiphy_work_queue() to queue the wiphy work. + * Because wiphy_delayed_work_queue() uses mod_timer(), if it is + * called twice and the second call happens before the first call + * deadline, the work will rescheduled for the second deadline and + * won't run before that. + * + * wiphy_delayed_work_pending() can be used to detect if calling + * wiphy_work_delayed_work_queue() would start a new work schedule + * or delayed a previous one. As seen below it cannot be used to + * detect precisely if the work has finished to execute nor if it + * is currently executing. + * + * CPU0 CPU1 + * wiphy_delayed_work_queue(wk) + * mod_timer(wk->timer) + * wiphy_delayed_work_pending(wk) -> true + * + * [...] + * expire_timers(wk->timer) + * detach_timer(wk->timer) + * wiphy_delayed_work_pending(wk) -> false + * wk->timer->function() | + * wiphy_work_queue(wk) | delayed work pending + * list_add_tail() | returns false but + * queue_work(cfg80211_wiphy_work) | wk->func() has not + * | been run yet + * [...] | + * cfg80211_wiphy_work() | + * wk->func() V + * + */ +bool wiphy_delayed_work_pending(struct wiphy *wiphy, + struct wiphy_delayed_work *dwork); + +/** * enum ieee80211_ap_reg_power - regulatory power for an Access Point * * @IEEE80211_REG_UNSET_AP: Access Point has no regulatory power mode diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h index 02fbc036f34e..813e163ce27c 100644 --- a/include/net/ieee80211_radiotap.h +++ b/include/net/ieee80211_radiotap.h @@ -24,25 +24,27 @@ * struct ieee80211_radiotap_header - base radiotap header */ struct ieee80211_radiotap_header { - /** - * @it_version: radiotap version, always 0 - */ - uint8_t it_version; - - /** - * @it_pad: padding (or alignment) - */ - uint8_t it_pad; - - /** - * @it_len: overall radiotap header length - */ - __le16 it_len; - - /** - * @it_present: (first) present word - */ - __le32 it_present; + __struct_group(ieee80211_radiotap_header_fixed, hdr, __packed, + /** + * @it_version: radiotap version, always 0 + */ + uint8_t it_version; + + /** + * @it_pad: padding (or alignment) + */ + uint8_t it_pad; + + /** + * @it_len: overall radiotap header length + */ + __le16 it_len; + + /** + * @it_present: (first) present word + */ + __le32 it_present; + ); /** * @it_optional: all remaining presence bitmaps @@ -50,6 +52,9 @@ struct ieee80211_radiotap_header { __le32 it_optional[]; } __packed; +static_assert(offsetof(struct ieee80211_radiotap_header, it_optional) == sizeof(struct ieee80211_radiotap_header_fixed), + "struct member likely outside of __struct_group()"); + /* version is always 0 */ #define PKTHDR_RADIOTAP_VERSION 0 diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h index 6194fbb564c6..6a070478254d 100644 --- a/include/net/ip_tunnels.h +++ b/include/net/ip_tunnels.h @@ -354,7 +354,7 @@ static inline void ip_tunnel_init_flow(struct flowi4 *fl4, memset(fl4, 0, sizeof(*fl4)); if (oif) { - fl4->flowi4_l3mdev = l3mdev_master_upper_ifindex_by_index_rcu(net, oif); + fl4->flowi4_l3mdev = l3mdev_master_upper_ifindex_by_index(net, oif); /* Legacy VRF/l3mdev use case */ fl4->flowi4_oif = fl4->flowi4_l3mdev ? 0 : oif; } diff --git a/include/trace/events/afs.h b/include/trace/events/afs.h index 450c44c83a5d..a0aed1a428a1 100644 --- a/include/trace/events/afs.h +++ b/include/trace/events/afs.h @@ -331,7 +331,11 @@ enum yfs_cm_operation { EM(afs_edit_dir_delete, "delete") \ EM(afs_edit_dir_delete_error, "d_err ") \ EM(afs_edit_dir_delete_inval, "d_invl") \ - E_(afs_edit_dir_delete_noent, "d_nent") + EM(afs_edit_dir_delete_noent, "d_nent") \ + EM(afs_edit_dir_update_dd, "u_ddot") \ + EM(afs_edit_dir_update_error, "u_fail") \ + EM(afs_edit_dir_update_inval, "u_invl") \ + E_(afs_edit_dir_update_nodd, "u_nodd") #define afs_edit_dir_reasons \ EM(afs_edit_dir_for_create, "Create") \ @@ -340,6 +344,7 @@ enum yfs_cm_operation { EM(afs_edit_dir_for_rename_0, "Renam0") \ EM(afs_edit_dir_for_rename_1, "Renam1") \ EM(afs_edit_dir_for_rename_2, "Renam2") \ + EM(afs_edit_dir_for_rename_sub, "RnmSub") \ EM(afs_edit_dir_for_rmdir, "RmDir ") \ EM(afs_edit_dir_for_silly_0, "S_Ren0") \ EM(afs_edit_dir_for_silly_1, "S_Ren1") \ diff --git a/include/uapi/linux/usb/video.h b/include/uapi/linux/usb/video.h index 2ff0e8a3a683..526b5155e23c 100644 --- a/include/uapi/linux/usb/video.h +++ b/include/uapi/linux/usb/video.h @@ -597,5 +597,63 @@ struct UVC_FRAME_MJPEG(n) { \ __le32 dwFrameInterval[n]; \ } __attribute__ ((packed)) +/* Frame Based Payload - 3.1.1. Frame Based Video Format Descriptor */ +struct uvc_format_framebased { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + __u8 bFormatIndex; + __u8 bNumFrameDescriptors; + __u8 guidFormat[16]; + __u8 bBitsPerPixel; + __u8 bDefaultFrameIndex; + __u8 bAspectRatioX; + __u8 bAspectRatioY; + __u8 bmInterfaceFlags; + __u8 bCopyProtect; + __u8 bVariableSize; +} __attribute__((__packed__)); + +#define UVC_DT_FORMAT_FRAMEBASED_SIZE 28 + +/* Frame Based Payload - 3.1.2. Frame Based Video Frame Descriptor */ +struct uvc_frame_framebased { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + __u8 bFrameIndex; + __u8 bmCapabilities; + __u16 wWidth; + __u16 wHeight; + __u32 dwMinBitRate; + __u32 dwMaxBitRate; + __u32 dwDefaultFrameInterval; + __u8 bFrameIntervalType; + __u32 dwBytesPerLine; + __u32 dwFrameInterval[]; +} __attribute__((__packed__)); + +#define UVC_DT_FRAME_FRAMEBASED_SIZE(n) (26+4*(n)) + +#define UVC_FRAME_FRAMEBASED(n) \ + uvc_frame_framebased_##n + +#define DECLARE_UVC_FRAME_FRAMEBASED(n) \ +struct UVC_FRAME_FRAMEBASED(n) { \ + __u8 bLength; \ + __u8 bDescriptorType; \ + __u8 bDescriptorSubType; \ + __u8 bFrameIndex; \ + __u8 bmCapabilities; \ + __u16 wWidth; \ + __u16 wHeight; \ + __u32 dwMinBitRate; \ + __u32 dwMaxBitRate; \ + __u32 dwDefaultFrameInterval; \ + __u8 bFrameIntervalType; \ + __u32 dwBytesPerLine; \ + __u32 dwFrameInterval[n]; \ +} __attribute__ ((packed)) + #endif /* __LINUX_USB_VIDEO_H */ diff --git a/io_uring/rw.c b/io_uring/rw.c index 354c4e175654..155938f10093 100644 --- a/io_uring/rw.c +++ b/io_uring/rw.c @@ -1014,6 +1014,25 @@ int io_read_mshot(struct io_kiocb *req, unsigned int issue_flags) return IOU_OK; } +static bool io_kiocb_start_write(struct io_kiocb *req, struct kiocb *kiocb) +{ + struct inode *inode; + bool ret; + + if (!(req->flags & REQ_F_ISREG)) + return true; + if (!(kiocb->ki_flags & IOCB_NOWAIT)) { + kiocb_start_write(kiocb); + return true; + } + + inode = file_inode(kiocb->ki_filp); + ret = sb_start_write_trylock(inode->i_sb); + if (ret) + __sb_writers_release(inode->i_sb, SB_FREEZE_WRITE); + return ret; +} + int io_write(struct io_kiocb *req, unsigned int issue_flags) { bool force_nonblock = issue_flags & IO_URING_F_NONBLOCK; @@ -1051,8 +1070,8 @@ int io_write(struct io_kiocb *req, unsigned int issue_flags) if (unlikely(ret)) return ret; - if (req->flags & REQ_F_ISREG) - kiocb_start_write(kiocb); + if (unlikely(!io_kiocb_start_write(req, kiocb))) + return -EAGAIN; kiocb->ki_flags |= IOCB_WRITE; if (likely(req->file->f_op->write_iter)) diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index e7113d700b87..025d7e2214ae 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -24,6 +24,23 @@ DEFINE_STATIC_KEY_ARRAY_FALSE(cgroup_bpf_enabled_key, MAX_CGROUP_BPF_ATTACH_TYPE); EXPORT_SYMBOL(cgroup_bpf_enabled_key); +/* + * cgroup bpf destruction makes heavy use of work items and there can be a lot + * of concurrent destructions. Use a separate workqueue so that cgroup bpf + * destruction work items don't end up filling up max_active of system_wq + * which may lead to deadlock. + */ +static struct workqueue_struct *cgroup_bpf_destroy_wq; + +static int __init cgroup_bpf_wq_init(void) +{ + cgroup_bpf_destroy_wq = alloc_workqueue("cgroup_bpf_destroy", 0, 1); + if (!cgroup_bpf_destroy_wq) + panic("Failed to alloc workqueue for cgroup bpf destroy.\n"); + return 0; +} +core_initcall(cgroup_bpf_wq_init); + /* __always_inline is necessary to prevent indirect call through run_prog * function pointer. */ @@ -334,7 +351,7 @@ static void cgroup_bpf_release_fn(struct percpu_ref *ref) struct cgroup *cgrp = container_of(ref, struct cgroup, bpf.refcnt); INIT_WORK(&cgrp->bpf.release_work, cgroup_bpf_release); - queue_work(system_wq, &cgrp->bpf.release_work); + queue_work(cgroup_bpf_destroy_wq, &cgrp->bpf.release_work); } /* Get underlying bpf_prog of bpf_prog_list entry, regardless if it's through diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index ca3f0a2e5ed5..3d45ebe8afb4 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -2851,21 +2851,47 @@ struct bpf_iter_bits { __u64 __opaque[2]; } __aligned(8); +#define BITS_ITER_NR_WORDS_MAX 511 + struct bpf_iter_bits_kern { union { - unsigned long *bits; - unsigned long bits_copy; + __u64 *bits; + __u64 bits_copy; }; - u32 nr_bits; + int nr_bits; int bit; } __aligned(8); +/* On 64-bit hosts, unsigned long and u64 have the same size, so passing + * a u64 pointer and an unsigned long pointer to find_next_bit() will + * return the same result, as both point to the same 8-byte area. + * + * For 32-bit little-endian hosts, using a u64 pointer or unsigned long + * pointer also makes no difference. This is because the first iterated + * unsigned long is composed of bits 0-31 of the u64 and the second unsigned + * long is composed of bits 32-63 of the u64. + * + * However, for 32-bit big-endian hosts, this is not the case. The first + * iterated unsigned long will be bits 32-63 of the u64, so swap these two + * ulong values within the u64. + */ +static void swap_ulong_in_u64(u64 *bits, unsigned int nr) +{ +#if (BITS_PER_LONG == 32) && defined(__BIG_ENDIAN) + unsigned int i; + + for (i = 0; i < nr; i++) + bits[i] = (bits[i] >> 32) | ((u64)(u32)bits[i] << 32); +#endif +} + /** * bpf_iter_bits_new() - Initialize a new bits iterator for a given memory area * @it: The new bpf_iter_bits to be created * @unsafe_ptr__ign: A pointer pointing to a memory area to be iterated over * @nr_words: The size of the specified memory area, measured in 8-byte units. - * Due to the limitation of memalloc, it can't be greater than 512. + * The maximum value of @nr_words is @BITS_ITER_NR_WORDS_MAX. This limit may be + * further reduced by the BPF memory allocator implementation. * * This function initializes a new bpf_iter_bits structure for iterating over * a memory area which is specified by the @unsafe_ptr__ign and @nr_words. It @@ -2892,6 +2918,8 @@ bpf_iter_bits_new(struct bpf_iter_bits *it, const u64 *unsafe_ptr__ign, u32 nr_w if (!unsafe_ptr__ign || !nr_words) return -EINVAL; + if (nr_words > BITS_ITER_NR_WORDS_MAX) + return -E2BIG; /* Optimization for u64 mask */ if (nr_bits == 64) { @@ -2899,10 +2927,15 @@ bpf_iter_bits_new(struct bpf_iter_bits *it, const u64 *unsafe_ptr__ign, u32 nr_w if (err) return -EFAULT; + swap_ulong_in_u64(&kit->bits_copy, nr_words); + kit->nr_bits = nr_bits; return 0; } + if (bpf_mem_alloc_check_size(false, nr_bytes)) + return -E2BIG; + /* Fallback to memalloc */ kit->bits = bpf_mem_alloc(&bpf_global_ma, nr_bytes); if (!kit->bits) @@ -2914,6 +2947,8 @@ bpf_iter_bits_new(struct bpf_iter_bits *it, const u64 *unsafe_ptr__ign, u32 nr_w return err; } + swap_ulong_in_u64(kit->bits, nr_words); + kit->nr_bits = nr_bits; return 0; } @@ -2930,17 +2965,16 @@ bpf_iter_bits_new(struct bpf_iter_bits *it, const u64 *unsafe_ptr__ign, u32 nr_w __bpf_kfunc int *bpf_iter_bits_next(struct bpf_iter_bits *it) { struct bpf_iter_bits_kern *kit = (void *)it; - u32 nr_bits = kit->nr_bits; - const unsigned long *bits; - int bit; + int bit = kit->bit, nr_bits = kit->nr_bits; + const void *bits; - if (nr_bits == 0) + if (!nr_bits || bit >= nr_bits) return NULL; bits = nr_bits == 64 ? &kit->bits_copy : kit->bits; - bit = find_next_bit(bits, nr_bits, kit->bit + 1); + bit = find_next_bit(bits, nr_bits, bit + 1); if (bit >= nr_bits) { - kit->nr_bits = 0; + kit->bit = bit; return NULL; } diff --git a/kernel/bpf/lpm_trie.c b/kernel/bpf/lpm_trie.c index 0218a5132ab5..9b60eda0f727 100644 --- a/kernel/bpf/lpm_trie.c +++ b/kernel/bpf/lpm_trie.c @@ -655,7 +655,7 @@ static int trie_get_next_key(struct bpf_map *map, void *_key, void *_next_key) if (!key || key->prefixlen > trie->max_prefixlen) goto find_leftmost; - node_stack = kmalloc_array(trie->max_prefixlen, + node_stack = kmalloc_array(trie->max_prefixlen + 1, sizeof(struct lpm_trie_node *), GFP_ATOMIC | __GFP_NOWARN); if (!node_stack) diff --git a/kernel/bpf/memalloc.c b/kernel/bpf/memalloc.c index b3858a76e0b3..146f5b57cfb1 100644 --- a/kernel/bpf/memalloc.c +++ b/kernel/bpf/memalloc.c @@ -35,6 +35,8 @@ */ #define LLIST_NODE_SZ sizeof(struct llist_node) +#define BPF_MEM_ALLOC_SIZE_MAX 4096 + /* similar to kmalloc, but sizeof == 8 bucket is gone */ static u8 size_index[24] __ro_after_init = { 3, /* 8 */ @@ -65,7 +67,7 @@ static u8 size_index[24] __ro_after_init = { static int bpf_mem_cache_idx(size_t size) { - if (!size || size > 4096) + if (!size || size > BPF_MEM_ALLOC_SIZE_MAX) return -1; if (size <= 192) @@ -1005,3 +1007,13 @@ void notrace *bpf_mem_cache_alloc_flags(struct bpf_mem_alloc *ma, gfp_t flags) return !ret ? NULL : ret + LLIST_NODE_SZ; } + +int bpf_mem_alloc_check_size(bool percpu, size_t size) +{ + /* The size of percpu allocation doesn't have LLIST_NODE_SZ overhead */ + if ((percpu && size > BPF_MEM_ALLOC_SIZE_MAX) || + (!percpu && size > BPF_MEM_ALLOC_SIZE_MAX - LLIST_NODE_SZ)) + return -E2BIG; + + return 0; +} diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 587a6c76e564..bb99bada7e2e 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -6804,20 +6804,10 @@ static int check_stack_slot_within_bounds(struct bpf_verifier_env *env, struct bpf_func_state *state, enum bpf_access_type t) { - struct bpf_insn_aux_data *aux = &env->insn_aux_data[env->insn_idx]; - int min_valid_off, max_bpf_stack; - - /* If accessing instruction is a spill/fill from bpf_fastcall pattern, - * add room for all caller saved registers below MAX_BPF_STACK. - * In case if bpf_fastcall rewrite won't happen maximal stack depth - * would be checked by check_max_stack_depth_subprog(). - */ - max_bpf_stack = MAX_BPF_STACK; - if (aux->fastcall_pattern) - max_bpf_stack += CALLER_SAVED_REGS * BPF_REG_SIZE; + int min_valid_off; if (t == BPF_WRITE || env->allow_uninit_stack) - min_valid_off = -max_bpf_stack; + min_valid_off = -MAX_BPF_STACK; else min_valid_off = -state->allocated_stack; @@ -17886,9 +17876,11 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx) struct bpf_verifier_state_list *sl, **pprev; struct bpf_verifier_state *cur = env->cur_state, *new, *loop_entry; int i, j, n, err, states_cnt = 0; - bool force_new_state = env->test_state_freq || is_force_checkpoint(env, insn_idx); - bool add_new_state = force_new_state; - bool force_exact; + bool force_new_state, add_new_state, force_exact; + + force_new_state = env->test_state_freq || is_force_checkpoint(env, insn_idx) || + /* Avoid accumulating infinitely long jmp history */ + cur->jmp_history_cnt > 40; /* bpf progs typically have pruning point every 4 instructions * http://vger.kernel.org/bpfconf2019.html#session-1 @@ -17898,6 +17890,7 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx) * In tests that amounts to up to 50% reduction into total verifier * memory consumption and 20% verifier time speedup. */ + add_new_state = force_new_state; if (env->jmps_processed - env->prev_jmps_processed >= 2 && env->insn_processed - env->prev_insn_processed >= 8) add_new_state = true; diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c index 5886b95c6eae..044c7ba1cc48 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c @@ -5789,7 +5789,7 @@ static bool cgroup_check_hierarchy_limits(struct cgroup *parent) { struct cgroup *cgroup; int ret = false; - int level = 1; + int level = 0; lockdep_assert_held(&cgroup_mutex); @@ -5797,7 +5797,7 @@ static bool cgroup_check_hierarchy_limits(struct cgroup *parent) if (cgroup->nr_descendants >= cgroup->max_descendants) goto fail; - if (level > cgroup->max_depth) + if (level >= cgroup->max_depth) goto fail; level++; diff --git a/kernel/events/core.c b/kernel/events/core.c index cdd09769e6c5..df27d08a7232 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -13959,7 +13959,7 @@ static void perf_event_clear_cpumask(unsigned int cpu) } /* migrate */ - list_for_each_entry_rcu(pmu, &pmus, entry, lockdep_is_held(&pmus_srcu)) { + list_for_each_entry(pmu, &pmus, entry) { if (pmu->scope == PERF_PMU_SCOPE_NONE || WARN_ON_ONCE(pmu->scope >= PERF_PMU_MAX_SCOPE)) continue; diff --git a/kernel/fork.c b/kernel/fork.c index 89ceb4a68af2..22f43721d031 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -105,6 +105,7 @@ #include <linux/rseq.h> #include <uapi/linux/pidfd.h> #include <linux/pidfs.h> +#include <linux/tick.h> #include <asm/pgalloc.h> #include <linux/uaccess.h> @@ -653,11 +654,6 @@ static __latent_entropy int dup_mmap(struct mm_struct *mm, mm->exec_vm = oldmm->exec_vm; mm->stack_vm = oldmm->stack_vm; - retval = ksm_fork(mm, oldmm); - if (retval) - goto out; - khugepaged_fork(mm, oldmm); - /* Use __mt_dup() to efficiently build an identical maple tree. */ retval = __mt_dup(&oldmm->mm_mt, &mm->mm_mt, GFP_KERNEL); if (unlikely(retval)) @@ -760,6 +756,8 @@ loop_out: vma_iter_free(&vmi); if (!retval) { mt_set_in_rcu(vmi.mas.tree); + ksm_fork(mm, oldmm); + khugepaged_fork(mm, oldmm); } else if (mpnt) { /* * The entire maple tree has already been duplicated. If the @@ -775,7 +773,10 @@ out: mmap_write_unlock(mm); flush_tlb_mm(oldmm); mmap_write_unlock(oldmm); - dup_userfaultfd_complete(&uf); + if (!retval) + dup_userfaultfd_complete(&uf); + else + dup_userfaultfd_fail(&uf); fail_uprobe_end: uprobe_end_dup_mmap(); return retval; @@ -2292,6 +2293,7 @@ __latent_entropy struct task_struct *copy_process( acct_clear_integrals(p); posix_cputimers_init(&p->posix_cputimers); + tick_dep_init_task(p); p->io_context = NULL; audit_set_context(p, NULL); diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 3a24d6b5f559..396a067a8a56 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -718,7 +718,7 @@ static int msi_domain_alloc(struct irq_domain *domain, unsigned int virq, ret = ops->msi_init(domain, info, virq + i, hwirq + i, arg); if (ret < 0) { if (ops->msi_free) { - for (i--; i > 0; i--) + for (i--; i >= 0; i--) ops->msi_free(domain, info, virq + i); } irq_domain_free_irqs_top(domain, virq, nr_irqs); diff --git a/kernel/resource.c b/kernel/resource.c index b730bd28b422..4101016e8b20 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -459,9 +459,7 @@ int walk_system_ram_res_rev(u64 start, u64 end, void *arg, rams_size += 16; } - rams[i].start = res.start; - rams[i++].end = res.end; - + rams[i++] = res; start = res.end + 1; } diff --git a/kernel/sched/core.c b/kernel/sched/core.c index dbfb5717d6af..719e0ed1e976 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -4711,7 +4711,7 @@ int sched_fork(unsigned long clone_flags, struct task_struct *p) if (rt_prio(p->prio)) { p->sched_class = &rt_sched_class; #ifdef CONFIG_SCHED_CLASS_EXT - } else if (task_should_scx(p)) { + } else if (task_should_scx(p->policy)) { p->sched_class = &ext_sched_class; #endif } else { @@ -7025,7 +7025,7 @@ int default_wake_function(wait_queue_entry_t *curr, unsigned mode, int wake_flag } EXPORT_SYMBOL(default_wake_function); -const struct sched_class *__setscheduler_class(struct task_struct *p, int prio) +const struct sched_class *__setscheduler_class(int policy, int prio) { if (dl_prio(prio)) return &dl_sched_class; @@ -7034,7 +7034,7 @@ const struct sched_class *__setscheduler_class(struct task_struct *p, int prio) return &rt_sched_class; #ifdef CONFIG_SCHED_CLASS_EXT - if (task_should_scx(p)) + if (task_should_scx(policy)) return &ext_sched_class; #endif @@ -7142,7 +7142,7 @@ void rt_mutex_setprio(struct task_struct *p, struct task_struct *pi_task) queue_flag &= ~DEQUEUE_MOVE; prev_class = p->sched_class; - next_class = __setscheduler_class(p, prio); + next_class = __setscheduler_class(p->policy, prio); if (prev_class != next_class && p->se.sched_delayed) dequeue_task(rq, p, DEQUEUE_SLEEP | DEQUEUE_DELAYED | DEQUEUE_NOCLOCK); diff --git a/kernel/sched/ext.c b/kernel/sched/ext.c index 5900b06fd036..b5f4b1a5ae98 100644 --- a/kernel/sched/ext.c +++ b/kernel/sched/ext.c @@ -862,7 +862,8 @@ static DEFINE_MUTEX(scx_ops_enable_mutex); DEFINE_STATIC_KEY_FALSE(__scx_ops_enabled); DEFINE_STATIC_PERCPU_RWSEM(scx_fork_rwsem); static atomic_t scx_ops_enable_state_var = ATOMIC_INIT(SCX_OPS_DISABLED); -static atomic_t scx_ops_bypass_depth = ATOMIC_INIT(0); +static int scx_ops_bypass_depth; +static DEFINE_RAW_SPINLOCK(__scx_ops_bypass_lock); static bool scx_ops_init_task_enabled; static bool scx_switching_all; DEFINE_STATIC_KEY_FALSE(__scx_switched_all); @@ -4256,14 +4257,14 @@ static const struct kset_uevent_ops scx_uevent_ops = { * Used by sched_fork() and __setscheduler_prio() to pick the matching * sched_class. dl/rt are already handled. */ -bool task_should_scx(struct task_struct *p) +bool task_should_scx(int policy) { if (!scx_enabled() || unlikely(scx_ops_enable_state() == SCX_OPS_DISABLING)) return false; if (READ_ONCE(scx_switching_all)) return true; - return p->policy == SCHED_EXT; + return policy == SCHED_EXT; } /** @@ -4298,18 +4299,20 @@ bool task_should_scx(struct task_struct *p) */ static void scx_ops_bypass(bool bypass) { - int depth, cpu; + int cpu; + unsigned long flags; + raw_spin_lock_irqsave(&__scx_ops_bypass_lock, flags); if (bypass) { - depth = atomic_inc_return(&scx_ops_bypass_depth); - WARN_ON_ONCE(depth <= 0); - if (depth != 1) - return; + scx_ops_bypass_depth++; + WARN_ON_ONCE(scx_ops_bypass_depth <= 0); + if (scx_ops_bypass_depth != 1) + goto unlock; } else { - depth = atomic_dec_return(&scx_ops_bypass_depth); - WARN_ON_ONCE(depth < 0); - if (depth != 0) - return; + scx_ops_bypass_depth--; + WARN_ON_ONCE(scx_ops_bypass_depth < 0); + if (scx_ops_bypass_depth != 0) + goto unlock; } /* @@ -4326,7 +4329,7 @@ static void scx_ops_bypass(bool bypass) struct rq_flags rf; struct task_struct *p, *n; - rq_lock_irqsave(rq, &rf); + rq_lock(rq, &rf); if (bypass) { WARN_ON_ONCE(rq->scx.flags & SCX_RQ_BYPASSING); @@ -4362,11 +4365,13 @@ static void scx_ops_bypass(bool bypass) sched_enq_and_set_task(&ctx); } - rq_unlock_irqrestore(rq, &rf); + rq_unlock(rq, &rf); /* resched to restore ticks and idle state */ resched_cpu(cpu); } +unlock: + raw_spin_unlock_irqrestore(&__scx_ops_bypass_lock, flags); } static void free_exit_info(struct scx_exit_info *ei) @@ -4489,11 +4494,16 @@ static void scx_ops_disable_workfn(struct kthread_work *work) scx_task_iter_start(&sti); while ((p = scx_task_iter_next_locked(&sti))) { const struct sched_class *old_class = p->sched_class; + const struct sched_class *new_class = + __setscheduler_class(p->policy, p->prio); struct sched_enq_and_set_ctx ctx; + if (old_class != new_class && p->se.sched_delayed) + dequeue_task(task_rq(p), p, DEQUEUE_SLEEP | DEQUEUE_DELAYED); + sched_deq_and_put_task(p, DEQUEUE_SAVE | DEQUEUE_MOVE, &ctx); - p->sched_class = __setscheduler_class(p, p->prio); + p->sched_class = new_class; check_class_changing(task_rq(p), p, old_class); sched_enq_and_set_task(&ctx); @@ -5199,12 +5209,17 @@ static int scx_ops_enable(struct sched_ext_ops *ops, struct bpf_link *link) scx_task_iter_start(&sti); while ((p = scx_task_iter_next_locked(&sti))) { const struct sched_class *old_class = p->sched_class; + const struct sched_class *new_class = + __setscheduler_class(p->policy, p->prio); struct sched_enq_and_set_ctx ctx; + if (old_class != new_class && p->se.sched_delayed) + dequeue_task(task_rq(p), p, DEQUEUE_SLEEP | DEQUEUE_DELAYED); + sched_deq_and_put_task(p, DEQUEUE_SAVE | DEQUEUE_MOVE, &ctx); p->scx.slice = SCX_SLICE_DFL; - p->sched_class = __setscheduler_class(p, p->prio); + p->sched_class = new_class; check_class_changing(task_rq(p), p, old_class); sched_enq_and_set_task(&ctx); diff --git a/kernel/sched/ext.h b/kernel/sched/ext.h index 246019519231..b1675bb59fc4 100644 --- a/kernel/sched/ext.h +++ b/kernel/sched/ext.h @@ -18,7 +18,7 @@ bool scx_can_stop_tick(struct rq *rq); void scx_rq_activate(struct rq *rq); void scx_rq_deactivate(struct rq *rq); int scx_check_setscheduler(struct task_struct *p, int policy); -bool task_should_scx(struct task_struct *p); +bool task_should_scx(int policy); void init_sched_ext_class(void); static inline u32 scx_cpuperf_target(s32 cpu) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index c157d4860a3b..2d16c8545c71 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -3369,7 +3369,7 @@ retry_pids: vma = vma_next(&vmi); } - do { + for (; vma; vma = vma_next(&vmi)) { if (!vma_migratable(vma) || !vma_policy_mof(vma) || is_vm_hugetlb_page(vma) || (vma->vm_flags & VM_MIXEDMAP)) { trace_sched_skip_vma_numa(mm, vma, NUMAB_SKIP_UNSUITABLE); @@ -3491,7 +3491,7 @@ retry_pids: */ if (vma_pids_forced) break; - } for_each_vma(vmi, vma); + } /* * If no VMAs are remaining and VMAs were skipped due to the PID @@ -5625,8 +5625,9 @@ pick_next_entity(struct rq *rq, struct cfs_rq *cfs_rq) struct sched_entity *se = pick_eevdf(cfs_rq); if (se->sched_delayed) { dequeue_entities(rq, se, DEQUEUE_SLEEP | DEQUEUE_DELAYED); - SCHED_WARN_ON(se->sched_delayed); - SCHED_WARN_ON(se->on_rq); + /* + * Must not reference @se again, see __block_task(). + */ return NULL; } return se; @@ -7176,7 +7177,11 @@ static int dequeue_entities(struct rq *rq, struct sched_entity *se, int flags) /* Fix-up what dequeue_task_fair() skipped */ hrtick_update(rq); - /* Fix-up what block_task() skipped. */ + /* + * Fix-up what block_task() skipped. + * + * Must be last, @p might not be valid after this. + */ __block_task(rq, p); } @@ -7193,12 +7198,14 @@ static bool dequeue_task_fair(struct rq *rq, struct task_struct *p, int flags) if (!(p->se.sched_delayed && (task_on_rq_migrating(p) || (flags & DEQUEUE_SAVE)))) util_est_dequeue(&rq->cfs, p); - if (dequeue_entities(rq, &p->se, flags) < 0) { - util_est_update(&rq->cfs, p, DEQUEUE_SLEEP); + util_est_update(&rq->cfs, p, flags & DEQUEUE_SLEEP); + if (dequeue_entities(rq, &p->se, flags) < 0) return false; - } - util_est_update(&rq->cfs, p, flags & DEQUEUE_SLEEP); + /* + * Must not reference @p after dequeue_entities(DEQUEUE_DELAYED). + */ + hrtick_update(rq); return true; } diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 081519ffab46..6c54a57275cc 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -2769,8 +2769,6 @@ static inline void sub_nr_running(struct rq *rq, unsigned count) static inline void __block_task(struct rq *rq, struct task_struct *p) { - WRITE_ONCE(p->on_rq, 0); - ASSERT_EXCLUSIVE_WRITER(p->on_rq); if (p->sched_contributes_to_load) rq->nr_uninterruptible++; @@ -2778,6 +2776,38 @@ static inline void __block_task(struct rq *rq, struct task_struct *p) atomic_inc(&rq->nr_iowait); delayacct_blkio_start(); } + + ASSERT_EXCLUSIVE_WRITER(p->on_rq); + + /* + * The moment this write goes through, ttwu() can swoop in and migrate + * this task, rendering our rq->__lock ineffective. + * + * __schedule() try_to_wake_up() + * LOCK rq->__lock LOCK p->pi_lock + * pick_next_task() + * pick_next_task_fair() + * pick_next_entity() + * dequeue_entities() + * __block_task() + * RELEASE p->on_rq = 0 if (p->on_rq && ...) + * break; + * + * ACQUIRE (after ctrl-dep) + * + * cpu = select_task_rq(); + * set_task_cpu(p, cpu); + * ttwu_queue() + * ttwu_do_activate() + * LOCK rq->__lock + * activate_task() + * STORE p->on_rq = 1 + * UNLOCK rq->__lock + * + * Callers must ensure to not reference @p after this -- we no longer + * own it. + */ + smp_store_release(&p->on_rq, 0); } extern void activate_task(struct rq *rq, struct task_struct *p, int flags); @@ -3800,7 +3830,7 @@ static inline int rt_effective_prio(struct task_struct *p, int prio) extern int __sched_setscheduler(struct task_struct *p, const struct sched_attr *attr, bool user, bool pi); extern int __sched_setaffinity(struct task_struct *p, struct affinity_context *ctx); -extern const struct sched_class *__setscheduler_class(struct task_struct *p, int prio); +extern const struct sched_class *__setscheduler_class(int policy, int prio); extern void set_load_weight(struct task_struct *p, bool update_load); extern void enqueue_task(struct rq *rq, struct task_struct *p, int flags); extern bool dequeue_task(struct rq *rq, struct task_struct *p, int flags); diff --git a/kernel/sched/syscalls.c b/kernel/sched/syscalls.c index 0470bcc3d204..24f9f90b6574 100644 --- a/kernel/sched/syscalls.c +++ b/kernel/sched/syscalls.c @@ -707,7 +707,7 @@ change: } prev_class = p->sched_class; - next_class = __setscheduler_class(p, newprio); + next_class = __setscheduler_class(policy, newprio); if (prev_class != next_class && p->se.sched_delayed) dequeue_task(rq, p, DEQUEUE_SLEEP | DEQUEUE_DELAYED | DEQUEUE_NOCLOCK); diff --git a/lib/Kconfig.kasan b/lib/Kconfig.kasan index 233ab2096924..98016e137b7f 100644 --- a/lib/Kconfig.kasan +++ b/lib/Kconfig.kasan @@ -22,11 +22,8 @@ config ARCH_DISABLE_KASAN_INLINE config CC_HAS_KASAN_GENERIC def_bool $(cc-option, -fsanitize=kernel-address) -# GCC appears to ignore no_sanitize_address when -fsanitize=kernel-hwaddress -# is passed. See https://bugzilla.kernel.org/show_bug.cgi?id=218854 (and -# the linked LKML thread) for more details. config CC_HAS_KASAN_SW_TAGS - def_bool !CC_IS_GCC && $(cc-option, -fsanitize=kernel-hwaddress) + def_bool $(cc-option, -fsanitize=kernel-hwaddress) # This option is only required for software KASAN modes. # Old GCC versions do not have proper support for no_sanitize_address. @@ -101,7 +98,7 @@ config KASAN_SW_TAGS help Enables Software Tag-Based KASAN. - Requires Clang. + Requires GCC 11+ or Clang. Supported only on arm64 CPUs and relies on Top Byte Ignore. diff --git a/lib/iov_iter.c b/lib/iov_iter.c index 1abb32c0da50..908e75a28d90 100644 --- a/lib/iov_iter.c +++ b/lib/iov_iter.c @@ -461,6 +461,8 @@ size_t copy_page_from_iter_atomic(struct page *page, size_t offset, size_t bytes, struct iov_iter *i) { size_t n, copied = 0; + bool uses_kmap = IS_ENABLED(CONFIG_DEBUG_KMAP_LOCAL_FORCE_MAP) || + PageHighMem(page); if (!page_copy_sane(page, offset, bytes)) return 0; @@ -471,7 +473,7 @@ size_t copy_page_from_iter_atomic(struct page *page, size_t offset, char *p; n = bytes - copied; - if (PageHighMem(page)) { + if (uses_kmap) { page += offset / PAGE_SIZE; offset %= PAGE_SIZE; n = min_t(size_t, n, PAGE_SIZE - offset); @@ -482,7 +484,7 @@ size_t copy_page_from_iter_atomic(struct page *page, size_t offset, kunmap_atomic(p); copied += n; offset += n; - } while (PageHighMem(page) && copied != bytes && n > 0); + } while (uses_kmap && copied != bytes && n > 0); return copied; } @@ -1021,15 +1023,18 @@ static ssize_t iter_folioq_get_pages(struct iov_iter *iter, size_t offset = iov_offset, fsize = folioq_folio_size(folioq, slot); size_t part = PAGE_SIZE - offset % PAGE_SIZE; - part = umin(part, umin(maxsize - extracted, fsize - offset)); - count -= part; - iov_offset += part; - extracted += part; + if (offset < fsize) { + part = umin(part, umin(maxsize - extracted, fsize - offset)); + count -= part; + iov_offset += part; + extracted += part; + + *pages = folio_page(folio, offset / PAGE_SIZE); + get_page(*pages); + pages++; + maxpages--; + } - *pages = folio_page(folio, offset / PAGE_SIZE); - get_page(*pages); - pages++; - maxpages--; if (maxpages == 0 || extracted >= maxsize) break; diff --git a/lib/slub_kunit.c b/lib/slub_kunit.c index 80e39f003344..33564f965958 100644 --- a/lib/slub_kunit.c +++ b/lib/slub_kunit.c @@ -141,7 +141,7 @@ static void test_kmalloc_redzone_access(struct kunit *test) { struct kmem_cache *s = test_kmem_cache_create("TestSlub_RZ_kmalloc", 32, SLAB_KMALLOC|SLAB_STORE_USER|SLAB_RED_ZONE); - u8 *p = __kmalloc_cache_noprof(s, GFP_KERNEL, 18); + u8 *p = alloc_hooks(__kmalloc_cache_noprof(s, GFP_KERNEL, 18)); kasan_disable_current(); diff --git a/mm/Kconfig b/mm/Kconfig index 4c9f5ea13271..33fa51d608dc 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -1085,7 +1085,6 @@ config HMM_MIRROR depends on MMU config GET_FREE_REGION - depends on SPARSEMEM bool config DEVICE_PRIVATE @@ -2394,20 +2394,25 @@ err: } /* - * Check whether all folios are *allowed* to be pinned indefinitely (longterm). + * Check whether all folios are *allowed* to be pinned indefinitely (long term). * Rather confusingly, all folios in the range are required to be pinned via * FOLL_PIN, before calling this routine. * - * If any folios in the range are not allowed to be pinned, then this routine - * will migrate those folios away, unpin all the folios in the range and return - * -EAGAIN. The caller should re-pin the entire range with FOLL_PIN and then - * call this routine again. + * Return values: * - * If an error other than -EAGAIN occurs, this indicates a migration failure. - * The caller should give up, and propagate the error back up the call stack. - * - * If everything is OK and all folios in the range are allowed to be pinned, + * 0: if everything is OK and all folios in the range are allowed to be pinned, * then this routine leaves all folios pinned and returns zero for success. + * + * -EAGAIN: if any folios in the range are not allowed to be pinned, then this + * routine will migrate those folios away, unpin all the folios in the range. If + * migration of the entire set of folios succeeds, then -EAGAIN is returned. The + * caller should re-pin the entire range with FOLL_PIN and then call this + * routine again. + * + * -ENOMEM, or any other -errno: if an error *other* than -EAGAIN occurs, this + * indicates a migration failure. The caller should give up, and propagate the + * error back up the call stack. The caller does not need to unpin any folios in + * that case, because this routine will do the unpinning. */ static long check_and_migrate_movable_folios(unsigned long nr_folios, struct folio **folios) @@ -2425,10 +2430,8 @@ static long check_and_migrate_movable_folios(unsigned long nr_folios, } /* - * This routine just converts all the pages in the @pages array to folios and - * calls check_and_migrate_movable_folios() to do the heavy lifting. - * - * Please see the check_and_migrate_movable_folios() documentation for details. + * Return values and behavior are the same as those for + * check_and_migrate_movable_folios(). */ static long check_and_migrate_movable_pages(unsigned long nr_pages, struct page **pages) @@ -2437,8 +2440,10 @@ static long check_and_migrate_movable_pages(unsigned long nr_pages, long i, ret; folios = kmalloc_array(nr_pages, sizeof(*folios), GFP_KERNEL); - if (!folios) + if (!folios) { + unpin_user_pages(pages, nr_pages); return -ENOMEM; + } for (i = 0; i < nr_pages; i++) folios[i] = page_folio(pages[i]); diff --git a/mm/kasan/kasan_test_c.c b/mm/kasan/kasan_test_c.c index a181e4780d9d..d8fb281e439d 100644 --- a/mm/kasan/kasan_test_c.c +++ b/mm/kasan/kasan_test_c.c @@ -1810,32 +1810,6 @@ static void vm_map_ram_tags(struct kunit *test) free_pages((unsigned long)p_ptr, 1); } -static void vmalloc_percpu(struct kunit *test) -{ - char __percpu *ptr; - int cpu; - - /* - * This test is specifically crafted for the software tag-based mode, - * the only tag-based mode that poisons percpu mappings. - */ - KASAN_TEST_NEEDS_CONFIG_ON(test, CONFIG_KASAN_SW_TAGS); - - ptr = __alloc_percpu(PAGE_SIZE, PAGE_SIZE); - - for_each_possible_cpu(cpu) { - char *c_ptr = per_cpu_ptr(ptr, cpu); - - KUNIT_EXPECT_GE(test, (u8)get_tag(c_ptr), (u8)KASAN_TAG_MIN); - KUNIT_EXPECT_LT(test, (u8)get_tag(c_ptr), (u8)KASAN_TAG_KERNEL); - - /* Make sure that in-bounds accesses don't crash the kernel. */ - *c_ptr = 0; - } - - free_percpu(ptr); -} - /* * Check that the assigned pointer tag falls within the [KASAN_TAG_MIN, * KASAN_TAG_KERNEL) range (note: excluding the match-all tag) for tag-based @@ -2023,7 +1997,6 @@ static struct kunit_case kasan_kunit_test_cases[] = { KUNIT_CASE(vmalloc_oob), KUNIT_CASE(vmap_tags), KUNIT_CASE(vm_map_ram_tags), - KUNIT_CASE(vmalloc_percpu), KUNIT_CASE(match_all_not_assigned), KUNIT_CASE(match_all_ptr_tag), KUNIT_CASE(match_all_mem_tag), diff --git a/mm/memory.c b/mm/memory.c index 3ccee51adfbb..bdf77a3ec47b 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -4187,6 +4187,8 @@ static struct folio *alloc_swap_folio(struct vm_fault *vmf) } #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ +static DECLARE_WAIT_QUEUE_HEAD(swapcache_wq); + /* * We enter with non-exclusive mmap_lock (to exclude vma changes, * but allow concurrent faults), and pte mapped but not yet locked. @@ -4199,6 +4201,7 @@ vm_fault_t do_swap_page(struct vm_fault *vmf) { struct vm_area_struct *vma = vmf->vma; struct folio *swapcache, *folio = NULL; + DECLARE_WAITQUEUE(wait, current); struct page *page; struct swap_info_struct *si = NULL; rmap_t rmap_flags = RMAP_NONE; @@ -4297,7 +4300,9 @@ vm_fault_t do_swap_page(struct vm_fault *vmf) * Relax a bit to prevent rapid * repeated page faults. */ + add_wait_queue(&swapcache_wq, &wait); schedule_timeout_uninterruptible(1); + remove_wait_queue(&swapcache_wq, &wait); goto out_page; } need_clear_cache = true; @@ -4604,8 +4609,11 @@ unlock: pte_unmap_unlock(vmf->pte, vmf->ptl); out: /* Clear the swap cache pin for direct swapin after PTL unlock */ - if (need_clear_cache) + if (need_clear_cache) { swapcache_clear(si, entry, nr_pages); + if (waitqueue_active(&swapcache_wq)) + wake_up(&swapcache_wq); + } if (si) put_swap_device(si); return ret; @@ -4620,8 +4628,11 @@ out_release: folio_unlock(swapcache); folio_put(swapcache); } - if (need_clear_cache) + if (need_clear_cache) { swapcache_clear(si, entry, nr_pages); + if (waitqueue_active(&swapcache_wq)) + wake_up(&swapcache_wq); + } if (si) put_swap_device(si); return ret; diff --git a/mm/migrate.c b/mm/migrate.c index df91248755e4..fab84a776088 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -206,7 +206,8 @@ static bool try_to_map_unused_to_zeropage(struct page_vma_mapped_walk *pvmw, pte_t newpte; void *addr; - VM_BUG_ON_PAGE(PageCompound(page), page); + if (PageCompound(page)) + return false; VM_BUG_ON_PAGE(!PageAnon(page), page); VM_BUG_ON_PAGE(!PageLocked(page), page); VM_BUG_ON_PAGE(pte_present(*pvmw->pte), page); @@ -1177,7 +1178,7 @@ static void migrate_folio_done(struct folio *src, * not accounted to NR_ISOLATED_*. They can be recognized * as __folio_test_movable */ - if (likely(!__folio_test_movable(src))) + if (likely(!__folio_test_movable(src)) && reason != MR_DEMOTION) mod_node_page_state(folio_pgdat(src), NR_ISOLATED_ANON + folio_is_file_lru(src), -folio_nr_pages(src)); diff --git a/mm/mmap.c b/mm/mmap.c index 9c0fb43064b5..9841b41e3c76 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -900,7 +900,8 @@ __get_unmapped_area(struct file *file, unsigned long addr, unsigned long len, if (get_area) { addr = get_area(file, addr, len, pgoff, flags); - } else if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) { + } else if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) + && IS_ALIGNED(len, PMD_SIZE)) { /* Ensures that larger anonymous mappings are THP aligned. */ addr = thp_get_unmapped_area_vmflags(file, addr, len, pgoff, flags, vm_flags); @@ -1418,6 +1419,13 @@ unsigned long mmap_region(struct file *file, unsigned long addr, vmg.flags = vm_flags; } + /* + * clear PTEs while the vma is still in the tree so that rmap + * cannot race with the freeing later in the truncate scenario. + * This is also needed for call_mmap(), which is why vm_ops + * close function is called. + */ + vms_clean_up_area(&vms, &mas_detach); vma = vma_merge_new_range(&vmg); if (vma) goto expanded; @@ -1439,11 +1447,6 @@ unsigned long mmap_region(struct file *file, unsigned long addr, if (file) { vma->vm_file = get_file(file); - /* - * call_mmap() may map PTE, so ensure there are no existing PTEs - * and call the vm_ops close function if one exists. - */ - vms_clean_up_area(&vms, &mas_detach); error = call_mmap(file, vma); if (error) goto unmap_and_free_vma; @@ -1640,6 +1643,7 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size, unsigned long populate = 0; unsigned long ret = -EINVAL; struct file *file; + vm_flags_t vm_flags; pr_warn_once("%s (%d) uses deprecated remap_file_pages() syscall. See Documentation/mm/remap_file_pages.rst.\n", current->comm, current->pid); @@ -1656,12 +1660,60 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size, if (pgoff + (size >> PAGE_SHIFT) < pgoff) return ret; - if (mmap_write_lock_killable(mm)) + if (mmap_read_lock_killable(mm)) + return -EINTR; + + /* + * Look up VMA under read lock first so we can perform the security + * without holding locks (which can be problematic). We reacquire a + * write lock later and check nothing changed underneath us. + */ + vma = vma_lookup(mm, start); + + if (!vma || !(vma->vm_flags & VM_SHARED)) { + mmap_read_unlock(mm); + return -EINVAL; + } + + prot |= vma->vm_flags & VM_READ ? PROT_READ : 0; + prot |= vma->vm_flags & VM_WRITE ? PROT_WRITE : 0; + prot |= vma->vm_flags & VM_EXEC ? PROT_EXEC : 0; + + flags &= MAP_NONBLOCK; + flags |= MAP_SHARED | MAP_FIXED | MAP_POPULATE; + if (vma->vm_flags & VM_LOCKED) + flags |= MAP_LOCKED; + + /* Save vm_flags used to calculate prot and flags, and recheck later. */ + vm_flags = vma->vm_flags; + file = get_file(vma->vm_file); + + mmap_read_unlock(mm); + + /* Call outside mmap_lock to be consistent with other callers. */ + ret = security_mmap_file(file, prot, flags); + if (ret) { + fput(file); + return ret; + } + + ret = -EINVAL; + + /* OK security check passed, take write lock + let it rip. */ + if (mmap_write_lock_killable(mm)) { + fput(file); return -EINTR; + } vma = vma_lookup(mm, start); - if (!vma || !(vma->vm_flags & VM_SHARED)) + if (!vma) + goto out; + + /* Make sure things didn't change under us. */ + if (vma->vm_flags != vm_flags) + goto out; + if (vma->vm_file != file) goto out; if (start + size > vma->vm_end) { @@ -1689,25 +1741,11 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size, goto out; } - prot |= vma->vm_flags & VM_READ ? PROT_READ : 0; - prot |= vma->vm_flags & VM_WRITE ? PROT_WRITE : 0; - prot |= vma->vm_flags & VM_EXEC ? PROT_EXEC : 0; - - flags &= MAP_NONBLOCK; - flags |= MAP_SHARED | MAP_FIXED | MAP_POPULATE; - if (vma->vm_flags & VM_LOCKED) - flags |= MAP_LOCKED; - - file = get_file(vma->vm_file); - ret = security_mmap_file(vma->vm_file, prot, flags); - if (ret) - goto out_fput; ret = do_mmap(vma->vm_file, start, size, prot, flags, 0, pgoff, &populate, NULL); -out_fput: - fput(file); out: mmap_write_unlock(mm); + fput(file); if (populate) mm_populate(ret, populate); if (!IS_ERR_VALUE(ret)) @@ -1754,7 +1792,8 @@ static int do_brk_flags(struct vma_iterator *vmi, struct vm_area_struct *vma, VMG_STATE(vmg, mm, vmi, addr, addr + len, flags, PHYS_PFN(addr)); vmg.prev = vma; - vma_iter_next_range(vmi); + /* vmi is positioned at prev, which this mode expects. */ + vmg.merge_flags = VMG_FLAG_JUST_EXPAND; if (vma_merge_new_range(&vmg)) goto out; diff --git a/mm/numa_memblks.c b/mm/numa_memblks.c index be52b93a9c58..a3877e9bc878 100644 --- a/mm/numa_memblks.c +++ b/mm/numa_memblks.c @@ -349,7 +349,7 @@ static void __init numa_clear_kernel_node_hotplug(void) for_each_reserved_mem_region(mb_region) { int nid = memblock_get_region_node(mb_region); - if (nid != MAX_NUMNODES) + if (numa_valid_node(nid)) node_set(nid, reserved_nodemask); } diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 8afab64814dc..94a2ffe28008 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2893,12 +2893,12 @@ struct page *rmqueue_buddy(struct zone *preferred_zone, struct zone *zone, page = __rmqueue(zone, order, migratetype, alloc_flags); /* - * If the allocation fails, allow OOM handling access - * to HIGHATOMIC reserves as failing now is worse than - * failing a high-order atomic allocation in the - * future. + * If the allocation fails, allow OOM handling and + * order-0 (atomic) allocs access to HIGHATOMIC + * reserves as failing now is worse than failing a + * high-order atomic allocation in the future. */ - if (!page && (alloc_flags & ALLOC_OOM)) + if (!page && (alloc_flags & (ALLOC_OOM|ALLOC_NON_BLOCK))) page = __rmqueue_smallest(zone, order, MIGRATE_HIGHATOMIC); if (!page) { diff --git a/mm/page_io.c b/mm/page_io.c index 78bc88acee79..69536a2b3c13 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -570,7 +570,7 @@ static void swap_read_folio_bdev_sync(struct folio *folio, * attempt to access it in the page fault retry time check. */ get_task_struct(current); - count_vm_event(PSWPIN); + count_vm_events(PSWPIN, folio_nr_pages(folio)); submit_bio_wait(&bio); __end_swap_bio_read(&bio); put_task_struct(current); @@ -585,7 +585,7 @@ static void swap_read_folio_bdev_async(struct folio *folio, bio->bi_iter.bi_sector = swap_folio_sector(folio); bio->bi_end_io = end_swap_bio_read; bio_add_folio_nofail(bio, folio, folio_size(folio), 0); - count_vm_event(PSWPIN); + count_vm_events(PSWPIN, folio_nr_pages(folio)); submit_bio(bio); } diff --git a/mm/pagewalk.c b/mm/pagewalk.c index 461ea3bbd8d9..5f9f01532e67 100644 --- a/mm/pagewalk.c +++ b/mm/pagewalk.c @@ -744,7 +744,8 @@ struct folio *folio_walk_start(struct folio_walk *fw, pud = pudp_get(pudp); if (pud_none(pud)) goto not_found; - if (IS_ENABLED(CONFIG_PGTABLE_HAS_HUGE_LEAVES) && pud_leaf(pud)) { + if (IS_ENABLED(CONFIG_PGTABLE_HAS_HUGE_LEAVES) && + (!pud_present(pud) || pud_leaf(pud))) { ptl = pud_lock(vma->vm_mm, pudp); pud = pudp_get(pudp); @@ -753,6 +754,10 @@ struct folio *folio_walk_start(struct folio_walk *fw, fw->pudp = pudp; fw->pud = pud; + /* + * TODO: FW_MIGRATION support for PUD migration entries + * once there are relevant users. + */ if (!pud_present(pud) || pud_devmap(pud) || pud_special(pud)) { spin_unlock(ptl); goto not_found; @@ -769,12 +774,13 @@ struct folio *folio_walk_start(struct folio_walk *fw, } pmd_table: - VM_WARN_ON_ONCE(pud_leaf(*pudp)); + VM_WARN_ON_ONCE(!pud_present(pud) || pud_leaf(pud)); pmdp = pmd_offset(pudp, addr); pmd = pmdp_get_lockless(pmdp); if (pmd_none(pmd)) goto not_found; - if (IS_ENABLED(CONFIG_PGTABLE_HAS_HUGE_LEAVES) && pmd_leaf(pmd)) { + if (IS_ENABLED(CONFIG_PGTABLE_HAS_HUGE_LEAVES) && + (!pmd_present(pmd) || pmd_leaf(pmd))) { ptl = pmd_lock(vma->vm_mm, pmdp); pmd = pmdp_get(pmdp); @@ -786,7 +792,7 @@ pmd_table: if (pmd_none(pmd)) { spin_unlock(ptl); goto not_found; - } else if (!pmd_leaf(pmd)) { + } else if (pmd_present(pmd) && !pmd_leaf(pmd)) { spin_unlock(ptl); goto pte_table; } else if (pmd_present(pmd)) { @@ -812,7 +818,7 @@ pmd_table: } pte_table: - VM_WARN_ON_ONCE(pmd_leaf(pmdp_get_lockless(pmdp))); + VM_WARN_ON_ONCE(!pmd_present(pmd) || pmd_leaf(pmd)); ptep = pte_offset_map_lock(vma->vm_mm, pmdp, addr, &ptl); if (!ptep) goto not_found; diff --git a/mm/rmap.c b/mm/rmap.c index a8797d1b3d49..73d5998677d4 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -885,13 +885,10 @@ static bool folio_referenced_one(struct folio *folio, return false; } - if (pvmw.pte) { - if (lru_gen_enabled() && - pte_young(ptep_get(pvmw.pte))) { - lru_gen_look_around(&pvmw); + if (lru_gen_enabled() && pvmw.pte) { + if (lru_gen_look_around(&pvmw)) referenced++; - } - + } else if (pvmw.pte) { if (ptep_clear_flush_young_notify(vma, address, pvmw.pte)) referenced++; diff --git a/mm/shmem.c b/mm/shmem.c index c5adb987b23c..4ba1d00fabda 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1166,7 +1166,9 @@ static int shmem_getattr(struct mnt_idmap *idmap, stat->attributes_mask |= (STATX_ATTR_APPEND | STATX_ATTR_IMMUTABLE | STATX_ATTR_NODUMP); + inode_lock_shared(inode); generic_fillattr(idmap, request_mask, inode, stat); + inode_unlock_shared(inode); if (shmem_huge_global_enabled(inode, 0, 0, false, NULL, 0)) stat->blksize = HPAGE_PMD_SIZE; diff --git a/mm/shrinker.c b/mm/shrinker.c index dc5d2a6fcfc4..4a93fd433689 100644 --- a/mm/shrinker.c +++ b/mm/shrinker.c @@ -76,19 +76,21 @@ void free_shrinker_info(struct mem_cgroup *memcg) int alloc_shrinker_info(struct mem_cgroup *memcg) { - struct shrinker_info *info; int nid, ret = 0; int array_size = 0; mutex_lock(&shrinker_mutex); array_size = shrinker_unit_size(shrinker_nr_max); for_each_node(nid) { - info = kvzalloc_node(sizeof(*info) + array_size, GFP_KERNEL, nid); + struct shrinker_info *info = kvzalloc_node(sizeof(*info) + array_size, + GFP_KERNEL, nid); if (!info) goto err; info->map_nr_max = shrinker_nr_max; - if (shrinker_unit_alloc(info, NULL, nid)) + if (shrinker_unit_alloc(info, NULL, nid)) { + kvfree(info); goto err; + } rcu_assign_pointer(memcg->nodeinfo[nid]->shrinker_info, info); } mutex_unlock(&shrinker_mutex); diff --git a/mm/slab_common.c b/mm/slab_common.c index 3d26c257ed8b..552b92dfdac7 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -1209,7 +1209,7 @@ __do_krealloc(const void *p, size_t new_size, gfp_t flags) /* Zero out spare memory. */ if (want_init_on_alloc(flags)) { kasan_disable_current(); - memset((void *)p + new_size, 0, ks - new_size); + memset(kasan_reset_tag(p) + new_size, 0, ks - new_size); kasan_enable_current(); } diff --git a/mm/swapfile.c b/mm/swapfile.c index b0915f3fab31..46bd4b1a3c07 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -731,15 +731,16 @@ done: return offset; } -static void swap_reclaim_full_clusters(struct swap_info_struct *si) +/* Return true if reclaimed a whole cluster */ +static void swap_reclaim_full_clusters(struct swap_info_struct *si, bool force) { long to_scan = 1; unsigned long offset, end; struct swap_cluster_info *ci; unsigned char *map = si->swap_map; - int nr_reclaim, total_reclaimed = 0; + int nr_reclaim; - if (atomic_long_read(&nr_swap_pages) <= SWAPFILE_CLUSTER) + if (force) to_scan = si->inuse_pages / SWAPFILE_CLUSTER; while (!list_empty(&si->full_clusters)) { @@ -749,28 +750,36 @@ static void swap_reclaim_full_clusters(struct swap_info_struct *si) end = min(si->max, offset + SWAPFILE_CLUSTER); to_scan--; + spin_unlock(&si->lock); while (offset < end) { if (READ_ONCE(map[offset]) == SWAP_HAS_CACHE) { - spin_unlock(&si->lock); nr_reclaim = __try_to_reclaim_swap(si, offset, TTRS_ANYWAY | TTRS_DIRECT); - spin_lock(&si->lock); - if (nr_reclaim > 0) { - offset += nr_reclaim; - total_reclaimed += nr_reclaim; - continue; - } else if (nr_reclaim < 0) { - offset += -nr_reclaim; + if (nr_reclaim) { + offset += abs(nr_reclaim); continue; } } offset++; } - if (to_scan <= 0 || total_reclaimed) + spin_lock(&si->lock); + + if (to_scan <= 0) break; } } +static void swap_reclaim_work(struct work_struct *work) +{ + struct swap_info_struct *si; + + si = container_of(work, struct swap_info_struct, reclaim_work); + + spin_lock(&si->lock); + swap_reclaim_full_clusters(si, true); + spin_unlock(&si->lock); +} + /* * Try to get swap entries with specified order from current cpu's swap entry * pool (a cluster). This might involve allocating a new cluster for current CPU @@ -800,6 +809,10 @@ new_cluster: goto done; } + /* Try reclaim from full clusters if free clusters list is drained */ + if (vm_swap_full()) + swap_reclaim_full_clusters(si, false); + if (order < PMD_ORDER) { unsigned int frags = 0; @@ -881,13 +894,6 @@ new_cluster: } done: - /* Try reclaim from full clusters if device is nearfull */ - if (vm_swap_full() && (!found || (si->pages - si->inuse_pages) < SWAPFILE_CLUSTER)) { - swap_reclaim_full_clusters(si); - if (!found && !order && si->pages != si->inuse_pages) - goto new_cluster; - } - cluster->next[order] = offset; return found; } @@ -922,6 +928,9 @@ static void swap_range_alloc(struct swap_info_struct *si, unsigned long offset, si->lowest_bit = si->max; si->highest_bit = 0; del_from_avail_list(si); + + if (vm_swap_full()) + schedule_work(&si->reclaim_work); } } @@ -2816,6 +2825,7 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile) wait_for_completion(&p->comp); flush_work(&p->discard_work); + flush_work(&p->reclaim_work); destroy_swap_extents(p); if (p->flags & SWP_CONTINUED) @@ -3376,6 +3386,7 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) return PTR_ERR(si); INIT_WORK(&si->discard_work, swap_discard_work); + INIT_WORK(&si->reclaim_work, swap_reclaim_work); name = getname(specialfile); if (IS_ERR(name)) { @@ -917,6 +917,7 @@ struct vm_area_struct *vma_merge_new_range(struct vma_merge_struct *vmg) pgoff_t pgoff = vmg->pgoff; pgoff_t pglen = PHYS_PFN(end - start); bool can_merge_left, can_merge_right; + bool just_expand = vmg->merge_flags & VMG_FLAG_JUST_EXPAND; mmap_assert_write_locked(vmg->mm); VM_WARN_ON(vmg->vma); @@ -930,7 +931,7 @@ struct vm_area_struct *vma_merge_new_range(struct vma_merge_struct *vmg) return NULL; can_merge_left = can_vma_merge_left(vmg); - can_merge_right = can_vma_merge_right(vmg, can_merge_left); + can_merge_right = !just_expand && can_vma_merge_right(vmg, can_merge_left); /* If we can merge with the next VMA, adjust vmg accordingly. */ if (can_merge_right) { @@ -953,7 +954,11 @@ struct vm_area_struct *vma_merge_new_range(struct vma_merge_struct *vmg) if (can_merge_right && !can_merge_remove_vma(next)) vmg->end = end; - vma_prev(vmg->vmi); /* Equivalent to going to the previous range */ + /* In expand-only case we are already positioned at prev. */ + if (!just_expand) { + /* Equivalent to going to the previous range. */ + vma_prev(vmg->vmi); + } } /* @@ -967,12 +972,14 @@ struct vm_area_struct *vma_merge_new_range(struct vma_merge_struct *vmg) } /* If expansion failed, reset state. Allows us to retry merge later. */ - vmg->vma = NULL; - vmg->start = start; - vmg->end = end; - vmg->pgoff = pgoff; - if (vmg->vma == prev) - vma_iter_set(vmg->vmi, start); + if (!just_expand) { + vmg->vma = NULL; + vmg->start = start; + vmg->end = end; + vmg->pgoff = pgoff; + if (vmg->vma == prev) + vma_iter_set(vmg->vmi, start); + } return NULL; } @@ -59,6 +59,17 @@ enum vma_merge_state { VMA_MERGE_SUCCESS, }; +enum vma_merge_flags { + VMG_FLAG_DEFAULT = 0, + /* + * If we can expand, simply do so. We know there is nothing to merge to + * the right. Does not reset state upon failure to merge. The VMA + * iterator is assumed to be positioned at the previous VMA, rather than + * at the gap. + */ + VMG_FLAG_JUST_EXPAND = 1 << 0, +}; + /* Represents a VMA merge operation. */ struct vma_merge_struct { struct mm_struct *mm; @@ -75,6 +86,7 @@ struct vma_merge_struct { struct mempolicy *policy; struct vm_userfaultfd_ctx uffd_ctx; struct anon_vma_name *anon_name; + enum vma_merge_flags merge_flags; enum vma_merge_state state; }; @@ -99,6 +111,7 @@ static inline pgoff_t vma_pgoff_offset(struct vm_area_struct *vma, .flags = flags_, \ .pgoff = pgoff_, \ .state = VMA_MERGE_START, \ + .merge_flags = VMG_FLAG_DEFAULT, \ } #define VMG_VMA_STATE(name, vmi_, prev_, vma_, start_, end_) \ @@ -118,6 +131,7 @@ static inline pgoff_t vma_pgoff_offset(struct vm_area_struct *vma, .uffd_ctx = vma_->vm_userfaultfd_ctx, \ .anon_name = anon_vma_name(vma_), \ .state = VMA_MERGE_START, \ + .merge_flags = VMG_FLAG_DEFAULT, \ } #ifdef CONFIG_DEBUG_VM_MAPLE_TREE @@ -241,15 +255,9 @@ static inline void vms_abort_munmap_vmas(struct vma_munmap_struct *vms, * failure method of leaving a gap where the MAP_FIXED mapping failed. */ mas_set_range(mas, vms->start, vms->end - 1); - if (unlikely(mas_store_gfp(mas, NULL, GFP_KERNEL))) { - pr_warn_once("%s: (%d) Unable to abort munmap() operation\n", - current->comm, current->pid); - /* Leaving vmas detached and in-tree may hamper recovery */ - reattach_vmas(mas_detach); - } else { - /* Clean up the insertion of the unfortunate gap */ - vms_complete_munmap_vmas(vms, mas_detach); - } + mas_store_gfp(mas, NULL, GFP_KERNEL|__GFP_NOFAIL); + /* Clean up the insertion of the unfortunate gap */ + vms_complete_munmap_vmas(vms, mas_detach); } int diff --git a/mm/vmscan.c b/mm/vmscan.c index eb4e8440c507..ddaaff67642e 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -56,6 +56,7 @@ #include <linux/khugepaged.h> #include <linux/rculist_nulls.h> #include <linux/random.h> +#include <linux/mmu_notifier.h> #include <asm/tlbflush.h> #include <asm/div64.h> @@ -3294,7 +3295,8 @@ static bool get_next_vma(unsigned long mask, unsigned long size, struct mm_walk return false; } -static unsigned long get_pte_pfn(pte_t pte, struct vm_area_struct *vma, unsigned long addr) +static unsigned long get_pte_pfn(pte_t pte, struct vm_area_struct *vma, unsigned long addr, + struct pglist_data *pgdat) { unsigned long pfn = pte_pfn(pte); @@ -3306,13 +3308,20 @@ static unsigned long get_pte_pfn(pte_t pte, struct vm_area_struct *vma, unsigned if (WARN_ON_ONCE(pte_devmap(pte) || pte_special(pte))) return -1; + if (!pte_young(pte) && !mm_has_notifiers(vma->vm_mm)) + return -1; + if (WARN_ON_ONCE(!pfn_valid(pfn))) return -1; + if (pfn < pgdat->node_start_pfn || pfn >= pgdat_end_pfn(pgdat)) + return -1; + return pfn; } -static unsigned long get_pmd_pfn(pmd_t pmd, struct vm_area_struct *vma, unsigned long addr) +static unsigned long get_pmd_pfn(pmd_t pmd, struct vm_area_struct *vma, unsigned long addr, + struct pglist_data *pgdat) { unsigned long pfn = pmd_pfn(pmd); @@ -3324,9 +3333,15 @@ static unsigned long get_pmd_pfn(pmd_t pmd, struct vm_area_struct *vma, unsigned if (WARN_ON_ONCE(pmd_devmap(pmd))) return -1; + if (!pmd_young(pmd) && !mm_has_notifiers(vma->vm_mm)) + return -1; + if (WARN_ON_ONCE(!pfn_valid(pfn))) return -1; + if (pfn < pgdat->node_start_pfn || pfn >= pgdat_end_pfn(pgdat)) + return -1; + return pfn; } @@ -3335,10 +3350,6 @@ static struct folio *get_pfn_folio(unsigned long pfn, struct mem_cgroup *memcg, { struct folio *folio; - /* try to avoid unnecessary memory loads */ - if (pfn < pgdat->node_start_pfn || pfn >= pgdat_end_pfn(pgdat)) - return NULL; - folio = pfn_folio(pfn); if (folio_nid(folio) != pgdat->node_id) return NULL; @@ -3394,21 +3405,16 @@ restart: total++; walk->mm_stats[MM_LEAF_TOTAL]++; - pfn = get_pte_pfn(ptent, args->vma, addr); + pfn = get_pte_pfn(ptent, args->vma, addr, pgdat); if (pfn == -1) continue; - if (!pte_young(ptent)) { - walk->mm_stats[MM_LEAF_OLD]++; - continue; - } - folio = get_pfn_folio(pfn, memcg, pgdat, walk->can_swap); if (!folio) continue; - if (!ptep_test_and_clear_young(args->vma, addr, pte + i)) - VM_WARN_ON_ONCE(true); + if (!ptep_clear_young_notify(args->vma, addr, pte + i)) + continue; young++; walk->mm_stats[MM_LEAF_YOUNG]++; @@ -3474,21 +3480,25 @@ static void walk_pmd_range_locked(pud_t *pud, unsigned long addr, struct vm_area /* don't round down the first address */ addr = i ? (*first & PMD_MASK) + i * PMD_SIZE : *first; - pfn = get_pmd_pfn(pmd[i], vma, addr); - if (pfn == -1) + if (!pmd_present(pmd[i])) goto next; if (!pmd_trans_huge(pmd[i])) { - if (!walk->force_scan && should_clear_pmd_young()) + if (!walk->force_scan && should_clear_pmd_young() && + !mm_has_notifiers(args->mm)) pmdp_test_and_clear_young(vma, addr, pmd + i); goto next; } + pfn = get_pmd_pfn(pmd[i], vma, addr, pgdat); + if (pfn == -1) + goto next; + folio = get_pfn_folio(pfn, memcg, pgdat, walk->can_swap); if (!folio) goto next; - if (!pmdp_test_and_clear_young(vma, addr, pmd + i)) + if (!pmdp_clear_young_notify(vma, addr, pmd + i)) goto next; walk->mm_stats[MM_LEAF_YOUNG]++; @@ -3546,27 +3556,18 @@ restart: } if (pmd_trans_huge(val)) { - unsigned long pfn = pmd_pfn(val); struct pglist_data *pgdat = lruvec_pgdat(walk->lruvec); + unsigned long pfn = get_pmd_pfn(val, vma, addr, pgdat); walk->mm_stats[MM_LEAF_TOTAL]++; - if (!pmd_young(val)) { - walk->mm_stats[MM_LEAF_OLD]++; - continue; - } - - /* try to avoid unnecessary memory loads */ - if (pfn < pgdat->node_start_pfn || pfn >= pgdat_end_pfn(pgdat)) - continue; - - walk_pmd_range_locked(pud, addr, vma, args, bitmap, &first); + if (pfn != -1) + walk_pmd_range_locked(pud, addr, vma, args, bitmap, &first); continue; } - walk->mm_stats[MM_NONLEAF_TOTAL]++; - - if (!walk->force_scan && should_clear_pmd_young()) { + if (!walk->force_scan && should_clear_pmd_young() && + !mm_has_notifiers(args->mm)) { if (!pmd_young(val)) continue; @@ -4040,13 +4041,13 @@ static void lru_gen_age_node(struct pglist_data *pgdat, struct scan_control *sc) * the PTE table to the Bloom filter. This forms a feedback loop between the * eviction and the aging. */ -void lru_gen_look_around(struct page_vma_mapped_walk *pvmw) +bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw) { int i; unsigned long start; unsigned long end; struct lru_gen_mm_walk *walk; - int young = 0; + int young = 1; pte_t *pte = pvmw->pte; unsigned long addr = pvmw->address; struct vm_area_struct *vma = pvmw->vma; @@ -4062,12 +4063,15 @@ void lru_gen_look_around(struct page_vma_mapped_walk *pvmw) lockdep_assert_held(pvmw->ptl); VM_WARN_ON_ONCE_FOLIO(folio_test_lru(folio), folio); + if (!ptep_clear_young_notify(vma, addr, pte)) + return false; + if (spin_is_contended(pvmw->ptl)) - return; + return true; /* exclude special VMAs containing anon pages from COW */ if (vma->vm_flags & VM_SPECIAL) - return; + return true; /* avoid taking the LRU lock under the PTL when possible */ walk = current->reclaim_state ? current->reclaim_state->mm_walk : NULL; @@ -4075,6 +4079,9 @@ void lru_gen_look_around(struct page_vma_mapped_walk *pvmw) start = max(addr & PMD_MASK, vma->vm_start); end = min(addr | ~PMD_MASK, vma->vm_end - 1) + 1; + if (end - start == PAGE_SIZE) + return true; + if (end - start > MIN_LRU_BATCH * PAGE_SIZE) { if (addr - start < MIN_LRU_BATCH * PAGE_SIZE / 2) end = start + MIN_LRU_BATCH * PAGE_SIZE; @@ -4088,7 +4095,7 @@ void lru_gen_look_around(struct page_vma_mapped_walk *pvmw) /* folio_update_gen() requires stable folio_memcg() */ if (!mem_cgroup_trylock_pages(memcg)) - return; + return true; arch_enter_lazy_mmu_mode(); @@ -4098,19 +4105,16 @@ void lru_gen_look_around(struct page_vma_mapped_walk *pvmw) unsigned long pfn; pte_t ptent = ptep_get(pte + i); - pfn = get_pte_pfn(ptent, vma, addr); + pfn = get_pte_pfn(ptent, vma, addr, pgdat); if (pfn == -1) continue; - if (!pte_young(ptent)) - continue; - folio = get_pfn_folio(pfn, memcg, pgdat, can_swap); if (!folio) continue; - if (!ptep_test_and_clear_young(vma, addr, pte + i)) - VM_WARN_ON_ONCE(true); + if (!ptep_clear_young_notify(vma, addr, pte + i)) + continue; young++; @@ -4140,6 +4144,8 @@ void lru_gen_look_around(struct page_vma_mapped_walk *pvmw) /* feedback from rmap walkers to page table walkers */ if (mm_state && suitable_to_scan(i, young)) update_bloom_filter(mm_state, max_seq, pvmw->pmd); + + return true; } /****************************************************************************** @@ -5254,11 +5260,11 @@ static void lru_gen_seq_show_full(struct seq_file *m, struct lruvec *lruvec, for (tier = 0; tier < MAX_NR_TIERS; tier++) { seq_printf(m, " %10d", tier); for (type = 0; type < ANON_AND_FILE; type++) { - const char *s = " "; + const char *s = "xxx"; unsigned long n[3] = {}; if (seq == max_seq) { - s = "RT "; + s = "RTx"; n[0] = READ_ONCE(lrugen->avg_refaulted[type][tier]); n[1] = READ_ONCE(lrugen->avg_total[type][tier]); } else if (seq == min_seq[type] || NR_HIST_GENS > 1) { @@ -5280,14 +5286,14 @@ static void lru_gen_seq_show_full(struct seq_file *m, struct lruvec *lruvec, seq_puts(m, " "); for (i = 0; i < NR_MM_STATS; i++) { - const char *s = " "; + const char *s = "xxxx"; unsigned long n = 0; if (seq == max_seq && NR_HIST_GENS == 1) { - s = "LOYNFA"; + s = "TYFA"; n = READ_ONCE(mm_state->stats[hist][i]); } else if (seq != max_seq && NR_HIST_GENS > 1) { - s = "loynfa"; + s = "tyfa"; n = READ_ONCE(mm_state->stats[hist][i]); } diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index ae7a5817883a..c0203a2b5107 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -206,6 +206,12 @@ struct sk_buff *__hci_cmd_sync_sk(struct hci_dev *hdev, u16 opcode, u32 plen, return ERR_PTR(err); } + /* If command return a status event skb will be set to NULL as there are + * no parameters. + */ + if (!skb) + return ERR_PTR(-ENODATA); + return skb; } EXPORT_SYMBOL(__hci_cmd_sync_sk); @@ -255,6 +261,11 @@ int __hci_cmd_sync_status_sk(struct hci_dev *hdev, u16 opcode, u32 plen, u8 status; skb = __hci_cmd_sync_sk(hdev, opcode, plen, param, event, timeout, sk); + + /* If command return a status event, skb will be set to -ENODATA */ + if (skb == ERR_PTR(-ENODATA)) + return 0; + if (IS_ERR(skb)) { if (!event) bt_dev_err(hdev, "Opcode 0x%4.4x failed: %ld", opcode, @@ -262,13 +273,6 @@ int __hci_cmd_sync_status_sk(struct hci_dev *hdev, u16 opcode, u32 plen, return PTR_ERR(skb); } - /* If command return a status event skb will be set to NULL as there are - * no parameters, in case of failure IS_ERR(skb) would have be set to - * the actual error would be found with PTR_ERR(skb). - */ - if (!skb) - return 0; - status = skb->data[0]; kfree_skb(skb); diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c index 6d7a442ceb89..501ec4249fed 100644 --- a/net/bpf/test_run.c +++ b/net/bpf/test_run.c @@ -246,6 +246,7 @@ static void reset_ctx(struct xdp_page_head *head) head->ctx.data_meta = head->orig_ctx.data_meta; head->ctx.data_end = head->orig_ctx.data_end; xdp_update_frame_from_buff(&head->ctx, head->frame); + head->frame->mem = head->orig_ctx.rxq->mem; } static int xdp_recv_frames(struct xdp_frame **frames, int nframes, diff --git a/net/core/dev.c b/net/core/dev.c index ea5fbcd133ae..8453e14d301b 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3639,6 +3639,9 @@ int skb_csum_hwoffload_help(struct sk_buff *skb, return 0; if (features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) { + if (vlan_get_protocol(skb) == htons(ETH_P_IPV6) && + skb_network_header_len(skb) != sizeof(struct ipv6hdr)) + goto sw_checksum; switch (skb->csum_offset) { case offsetof(struct tcphdr, check): case offsetof(struct udphdr, check): @@ -3646,6 +3649,7 @@ int skb_csum_hwoffload_help(struct sk_buff *skb, } } +sw_checksum: return skb_checksum_help(skb); } EXPORT_SYMBOL(skb_csum_hwoffload_help); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index e30e7ea0207d..2ba5cd965d3f 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -2032,7 +2032,7 @@ static const struct nla_policy ifla_policy[IFLA_MAX+1] = { [IFLA_NUM_TX_QUEUES] = { .type = NLA_U32 }, [IFLA_NUM_RX_QUEUES] = { .type = NLA_U32 }, [IFLA_GSO_MAX_SEGS] = { .type = NLA_U32 }, - [IFLA_GSO_MAX_SIZE] = { .type = NLA_U32 }, + [IFLA_GSO_MAX_SIZE] = NLA_POLICY_MIN(NLA_U32, MAX_TCP_HEADER + 1), [IFLA_PHYS_PORT_ID] = { .type = NLA_BINARY, .len = MAX_PHYS_ITEM_ID_LEN }, [IFLA_CARRIER_CHANGES] = { .type = NLA_U32 }, /* ignored */ [IFLA_PHYS_SWITCH_ID] = { .type = NLA_BINARY, .len = MAX_PHYS_ITEM_ID_LEN }, @@ -2057,7 +2057,7 @@ static const struct nla_policy ifla_policy[IFLA_MAX+1] = { [IFLA_TSO_MAX_SIZE] = { .type = NLA_REJECT }, [IFLA_TSO_MAX_SEGS] = { .type = NLA_REJECT }, [IFLA_ALLMULTI] = { .type = NLA_REJECT }, - [IFLA_GSO_IPV4_MAX_SIZE] = { .type = NLA_U32 }, + [IFLA_GSO_IPV4_MAX_SIZE] = NLA_POLICY_MIN(NLA_U32, MAX_TCP_HEADER + 1), [IFLA_GRO_IPV4_MAX_SIZE] = { .type = NLA_U32 }, }; diff --git a/net/core/sock_map.c b/net/core/sock_map.c index 07d6aa4e39ef..78347d7d25ef 100644 --- a/net/core/sock_map.c +++ b/net/core/sock_map.c @@ -1760,6 +1760,10 @@ static int sock_map_link_update_prog(struct bpf_link *link, ret = -EINVAL; goto out; } + if (!sockmap_link->map) { + ret = -ENOLINK; + goto out; + } ret = sock_map_prog_link_lookup(sockmap_link->map, &pprog, &plink, sockmap_link->attach_type); diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index d591c73e2c0e..25505f9b724c 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -218,7 +218,7 @@ static struct ip_tunnel *ip_tunnel_find(struct ip_tunnel_net *itn, ip_tunnel_flags_copy(flags, parms->i_flags); - hlist_for_each_entry_rcu(t, head, hash_node) { + hlist_for_each_entry_rcu(t, head, hash_node, lockdep_rtnl_is_held()) { if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr && link == READ_ONCE(t->parms.link) && diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c index e7658c5d6b79..370993c03d31 100644 --- a/net/ipv4/tcp_bpf.c +++ b/net/ipv4/tcp_bpf.c @@ -221,11 +221,11 @@ static int tcp_bpf_recvmsg_parser(struct sock *sk, int flags, int *addr_len) { - struct tcp_sock *tcp = tcp_sk(sk); int peek = flags & MSG_PEEK; - u32 seq = tcp->copied_seq; struct sk_psock *psock; + struct tcp_sock *tcp; int copied = 0; + u32 seq; if (unlikely(flags & MSG_ERRQUEUE)) return inet_recv_error(sk, msg, len, addr_len); @@ -238,7 +238,8 @@ static int tcp_bpf_recvmsg_parser(struct sock *sk, return tcp_recvmsg(sk, msg, len, flags, addr_len); lock_sock(sk); - + tcp = tcp_sk(sk); + seq = tcp->copied_seq; /* We may have received data on the sk_receive_queue pre-accept and * then we can not use read_skb in this context because we haven't * assigned a sk_socket yet so have no link to the ops. The work-around diff --git a/net/ipv6/netfilter/nf_reject_ipv6.c b/net/ipv6/netfilter/nf_reject_ipv6.c index 7db0437140bf..9ae2b2725bf9 100644 --- a/net/ipv6/netfilter/nf_reject_ipv6.c +++ b/net/ipv6/netfilter/nf_reject_ipv6.c @@ -268,12 +268,12 @@ static int nf_reject6_fill_skb_dst(struct sk_buff *skb_in) void nf_send_reset6(struct net *net, struct sock *sk, struct sk_buff *oldskb, int hook) { - struct sk_buff *nskb; - struct tcphdr _otcph; - const struct tcphdr *otcph; - unsigned int otcplen, hh_len; const struct ipv6hdr *oip6h = ipv6_hdr(oldskb); struct dst_entry *dst = NULL; + const struct tcphdr *otcph; + struct sk_buff *nskb; + struct tcphdr _otcph; + unsigned int otcplen; struct flowi6 fl6; if ((!(ipv6_addr_type(&oip6h->saddr) & IPV6_ADDR_UNICAST)) || @@ -312,9 +312,8 @@ void nf_send_reset6(struct net *net, struct sock *sk, struct sk_buff *oldskb, if (IS_ERR(dst)) return; - hh_len = (dst->dev->hard_header_len + 15)&~15; - nskb = alloc_skb(hh_len + 15 + dst->header_len + sizeof(struct ipv6hdr) - + sizeof(struct tcphdr) + dst->trailer_len, + nskb = alloc_skb(LL_MAX_HEADER + sizeof(struct ipv6hdr) + + sizeof(struct tcphdr) + dst->trailer_len, GFP_ATOMIC); if (!nskb) { @@ -327,7 +326,7 @@ void nf_send_reset6(struct net *net, struct sock *sk, struct sk_buff *oldskb, nskb->mark = fl6.flowi6_mark; - skb_reserve(nskb, hh_len + dst->header_len); + skb_reserve(nskb, LL_MAX_HEADER); nf_reject_ip6hdr_put(nskb, oldskb, IPPROTO_TCP, ip6_dst_hoplimit(dst)); nf_reject_ip6_tcphdr_put(nskb, oldskb, otcph, otcplen); diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index 13438cc0a6b1..cf0f7780fb10 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -96,7 +96,7 @@ config MAC80211_DEBUGFS config MAC80211_MESSAGE_TRACING bool "Trace all mac80211 debug messages" - depends on MAC80211 + depends on MAC80211 && TRACING help Select this option to have mac80211 register the mac80211_msg trace subsystem with tracepoints to diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 847304a3a29a..6dfc61a9acd4 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -3046,6 +3046,7 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy, enum nl80211_tx_power_setting txp_type = type; bool update_txp_type = false; bool has_monitor = false; + int old_power = local->user_power_level; lockdep_assert_wiphy(local->hw.wiphy); @@ -3128,6 +3129,10 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy, } } + if (local->emulate_chanctx && + (old_power != local->user_power_level)) + ieee80211_hw_conf_chan(local); + return 0; } @@ -3138,7 +3143,8 @@ static int ieee80211_get_tx_power(struct wiphy *wiphy, struct ieee80211_local *local = wiphy_priv(wiphy); struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); - if (local->ops->get_txpower) + if (local->ops->get_txpower && + (sdata->flags & IEEE80211_SDATA_IN_DRIVER)) return drv_get_txpower(local, sdata, dbm); if (local->emulate_chanctx) @@ -4826,12 +4832,12 @@ void ieee80211_color_change_finalize_work(struct wiphy *wiphy, ieee80211_color_change_finalize(link); } -void ieee80211_color_collision_detection_work(struct work_struct *work) +void ieee80211_color_collision_detection_work(struct wiphy *wiphy, + struct wiphy_work *work) { - struct delayed_work *delayed_work = to_delayed_work(work); struct ieee80211_link_data *link = - container_of(delayed_work, struct ieee80211_link_data, - color_collision_detect_work); + container_of(work, struct ieee80211_link_data, + color_collision_detect_work.work); struct ieee80211_sub_if_data *sdata = link->sdata; cfg80211_obss_color_collision_notify(sdata->dev, link->color_bitmap, @@ -4884,7 +4890,8 @@ ieee80211_obss_color_collision_notify(struct ieee80211_vif *vif, return; } - if (delayed_work_pending(&link->color_collision_detect_work)) { + if (wiphy_delayed_work_pending(sdata->local->hw.wiphy, + &link->color_collision_detect_work)) { rcu_read_unlock(); return; } @@ -4893,9 +4900,9 @@ ieee80211_obss_color_collision_notify(struct ieee80211_vif *vif, /* queue the color collision detection event every 500 ms in order to * avoid sending too much netlink messages to userspace. */ - ieee80211_queue_delayed_work(&sdata->local->hw, - &link->color_collision_detect_work, - msecs_to_jiffies(500)); + wiphy_delayed_work_queue(sdata->local->hw.wiphy, + &link->color_collision_detect_work, + msecs_to_jiffies(500)); rcu_read_unlock(); } diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 4f0390918b60..3d3c9139ff5e 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -892,9 +892,10 @@ struct ieee80211_chanctx { /* temporary data for search algorithm etc. */ struct ieee80211_chan_req req; - struct ieee80211_chanctx_conf conf; - bool radar_detected; + + /* MUST be last - ends in a flexible-array member. */ + struct ieee80211_chanctx_conf conf; }; struct mac80211_qos_map { @@ -1053,7 +1054,7 @@ struct ieee80211_link_data { } csa; struct wiphy_work color_change_finalize_work; - struct delayed_work color_collision_detect_work; + struct wiphy_delayed_work color_collision_detect_work; u64 color_bitmap; /* context reservation -- protected with wiphy mutex */ @@ -2005,7 +2006,8 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, /* color change handling */ void ieee80211_color_change_finalize_work(struct wiphy *wiphy, struct wiphy_work *work); -void ieee80211_color_collision_detection_work(struct work_struct *work); +void ieee80211_color_collision_detection_work(struct wiphy *wiphy, + struct wiphy_work *work); /* interface handling */ #define MAC80211_SUPPORTED_FEATURES_TX (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | \ diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 6fac3ba00d88..67ecfea22982 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -987,6 +987,26 @@ void ieee80211_reenable_keys(struct ieee80211_sub_if_data *sdata) } } +static void +ieee80211_key_iter(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_key *key, + void (*iter)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, + void *data), + void *iter_data) +{ + /* skip keys of station in removal process */ + if (key->sta && key->sta->removed) + return; + if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) + return; + iter(hw, vif, key->sta ? &key->sta->sta : NULL, + &key->conf, iter_data); +} + void ieee80211_iter_keys(struct ieee80211_hw *hw, struct ieee80211_vif *vif, void (*iter)(struct ieee80211_hw *hw, @@ -1005,16 +1025,13 @@ void ieee80211_iter_keys(struct ieee80211_hw *hw, if (vif) { sdata = vif_to_sdata(vif); list_for_each_entry_safe(key, tmp, &sdata->key_list, list) - iter(hw, &sdata->vif, - key->sta ? &key->sta->sta : NULL, - &key->conf, iter_data); + ieee80211_key_iter(hw, vif, key, iter, iter_data); } else { list_for_each_entry(sdata, &local->interfaces, list) list_for_each_entry_safe(key, tmp, &sdata->key_list, list) - iter(hw, &sdata->vif, - key->sta ? &key->sta->sta : NULL, - &key->conf, iter_data); + ieee80211_key_iter(hw, &sdata->vif, key, + iter, iter_data); } } EXPORT_SYMBOL(ieee80211_iter_keys); @@ -1031,17 +1048,8 @@ _ieee80211_iter_keys_rcu(struct ieee80211_hw *hw, { struct ieee80211_key *key; - list_for_each_entry_rcu(key, &sdata->key_list, list) { - /* skip keys of station in removal process */ - if (key->sta && key->sta->removed) - continue; - if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) - continue; - - iter(hw, &sdata->vif, - key->sta ? &key->sta->sta : NULL, - &key->conf, iter_data); - } + list_for_each_entry_rcu(key, &sdata->key_list, list) + ieee80211_key_iter(hw, &sdata->vif, key, iter, iter_data); } void ieee80211_iter_keys_rcu(struct ieee80211_hw *hw, diff --git a/net/mac80211/link.c b/net/mac80211/link.c index 0bbac64d5fa0..46092fbcde90 100644 --- a/net/mac80211/link.c +++ b/net/mac80211/link.c @@ -41,8 +41,8 @@ void ieee80211_link_init(struct ieee80211_sub_if_data *sdata, ieee80211_csa_finalize_work); wiphy_work_init(&link->color_change_finalize_work, ieee80211_color_change_finalize_work); - INIT_DELAYED_WORK(&link->color_collision_detect_work, - ieee80211_color_collision_detection_work); + wiphy_delayed_work_init(&link->color_collision_detect_work, + ieee80211_color_collision_detection_work); INIT_LIST_HEAD(&link->assigned_chanctx_list); INIT_LIST_HEAD(&link->reserved_chanctx_list); wiphy_delayed_work_init(&link->dfs_cac_timer_work, @@ -72,7 +72,8 @@ void ieee80211_link_stop(struct ieee80211_link_data *link) if (link->sdata->vif.type == NL80211_IFTYPE_STATION) ieee80211_mgd_stop_link(link); - cancel_delayed_work_sync(&link->color_collision_detect_work); + wiphy_delayed_work_cancel(link->sdata->local->hw.wiphy, + &link->color_collision_detect_work); wiphy_work_cancel(link->sdata->local->hw.wiphy, &link->color_change_finalize_work); wiphy_work_cancel(link->sdata->local->hw.wiphy, diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 89084690350f..ee1211a213d7 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -167,6 +167,8 @@ static u32 ieee80211_calc_hw_conf_chan(struct ieee80211_local *local, } power = ieee80211_chandef_max_power(&chandef); + if (local->user_power_level != IEEE80211_UNSET_POWER_LEVEL) + power = min(local->user_power_level, power); rcu_read_lock(); list_for_each_entry_rcu(sdata, &local->interfaces, list) { diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 6d0e201c3eb2..d263091659e0 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -2864,8 +2864,10 @@ static int mptcp_init_sock(struct sock *sk) if (unlikely(!net->mib.mptcp_statistics) && !mptcp_mib_alloc(net)) return -ENOMEM; + rcu_read_lock(); ret = mptcp_init_sched(mptcp_sk(sk), mptcp_sched_find(mptcp_get_scheduler(net))); + rcu_read_unlock(); if (ret) return ret; diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c index 330609a76fb2..7dfc5343dae4 100644 --- a/net/netfilter/nft_payload.c +++ b/net/netfilter/nft_payload.c @@ -904,6 +904,9 @@ static void nft_payload_set_eval(const struct nft_expr *expr, ((priv->base != NFT_PAYLOAD_TRANSPORT_HEADER && priv->base != NFT_PAYLOAD_INNER_HEADER) || skb->ip_summed != CHECKSUM_PARTIAL)) { + if (offset + priv->len > skb->len) + goto err; + fsum = skb_checksum(skb, offset, priv->len, 0); tsum = csum_partial(src, priv->len, 0); diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index da5d929c7c85..709840612f0d 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -1269,7 +1269,7 @@ struct xt_table *xt_find_table_lock(struct net *net, u_int8_t af, /* and once again: */ list_for_each_entry(t, &xt_net->tables[af], list) - if (strcmp(t->name, name) == 0) + if (strcmp(t->name, name) == 0 && owner == t->me) return t; module_put(owner); diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 17d97bbe890f..bbc778c233c8 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -1518,6 +1518,7 @@ int tcf_block_get_ext(struct tcf_block **p_block, struct Qdisc *q, return 0; err_dev_insert: + tcf_block_offload_unbind(block, q, ei); err_block_offload_bind: tcf_chain0_head_change_cb_del(block, ei); err_chain0_head_change_cb_add: diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 2eefa4783879..a1d27bc039a3 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -791,7 +791,7 @@ void qdisc_tree_reduce_backlog(struct Qdisc *sch, int n, int len) drops = max_t(int, n, 0); rcu_read_lock(); while ((parentid = sch->parent)) { - if (TC_H_MAJ(parentid) == TC_H_MAJ(TC_H_INGRESS)) + if (parentid == TC_H_ROOT) break; if (sch->flags & TCQ_F_NOPARENT) diff --git a/net/sunrpc/xprtrdma/ib_client.c b/net/sunrpc/xprtrdma/ib_client.c index 8507cd4d8921..28c68b5f6823 100644 --- a/net/sunrpc/xprtrdma/ib_client.c +++ b/net/sunrpc/xprtrdma/ib_client.c @@ -153,6 +153,7 @@ static void rpcrdma_remove_one(struct ib_device *device, } trace_rpcrdma_client_remove_one_done(device); + xa_destroy(&rd->rd_xa); kfree(rd); } diff --git a/net/wireless/core.c b/net/wireless/core.c index 661adfc77644..74ca18833df1 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -1236,6 +1236,7 @@ static void _cfg80211_unregister_wdev(struct wireless_dev *wdev, /* deleted from the list, so can't be found from nl80211 any more */ cqm_config = rcu_access_pointer(wdev->cqm_config); kfree_rcu(cqm_config, rcu_head); + RCU_INIT_POINTER(wdev->cqm_config, NULL); /* * Ensure that all events have been processed and @@ -1704,6 +1705,13 @@ void wiphy_delayed_work_flush(struct wiphy *wiphy, } EXPORT_SYMBOL_GPL(wiphy_delayed_work_flush); +bool wiphy_delayed_work_pending(struct wiphy *wiphy, + struct wiphy_delayed_work *dwork) +{ + return timer_pending(&dwork->timer); +} +EXPORT_SYMBOL_GPL(wiphy_delayed_work_pending); + static int __init cfg80211_init(void) { int err; diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 59a90bf3c0d6..d0aed41ded2f 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -3050,6 +3050,10 @@ cfg80211_parse_ml_elem_sta_data(struct wiphy *wiphy, freq = ieee80211_channel_to_freq_khz(ap_info->channel, band); data.channel = ieee80211_get_channel_khz(wiphy, freq); + /* Skip if RNR element specifies an unsupported channel */ + if (!data.channel) + continue; + /* Skip if BSS entry generated from MBSSID or DIRECT source * frame data available already. */ diff --git a/scripts/Makefile.package b/scripts/Makefile.package index 11d53f240a2b..74bcb9e7f7a4 100644 --- a/scripts/Makefile.package +++ b/scripts/Makefile.package @@ -62,6 +62,10 @@ rpm-sources: linux.tar.gz PHONY += rpm-pkg srcrpm-pkg binrpm-pkg +ifneq ($(CC),$(HOSTCC)) +rpm-no-devel = --without=devel +endif + rpm-pkg: private build-type := a srcrpm-pkg: private build-type := s binrpm-pkg: private build-type := b @@ -72,7 +76,8 @@ rpm-pkg srcrpm-pkg binrpm-pkg: rpmbuild/SPECS/kernel.spec --define='_topdir $(abspath rpmbuild)' \ $(if $(filter a b, $(build-type)), \ --target $(UTS_MACHINE)-linux --build-in-place --noprep --define='_smp_mflags %{nil}' \ - $$(rpm -q rpm >/dev/null 2>&1 || echo --nodeps)) \ + $$(rpm -q rpm >/dev/null 2>&1 || echo --nodeps) \ + $(rpm-no-devel)) \ $(RPMOPTS)) # deb-pkg srcdeb-pkg bindeb-pkg diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index 4addd33749bb..6587ac86d0d5 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -533,6 +533,7 @@ bool menu_is_empty(struct menu *menu) bool menu_is_visible(struct menu *menu) { + struct menu *child; struct symbol *sym; tristate visible; @@ -551,7 +552,17 @@ bool menu_is_visible(struct menu *menu) } else visible = menu->prompt->visible.tri = expr_calc_value(menu->prompt->visible.expr); - return visible != no; + if (visible != no) + return true; + + if (!sym || sym_get_tristate_value(menu->sym) == no) + return false; + + for (child = menu->list; child; child = child->next) + if (menu_is_visible(child)) + return true; + + return false; } const char *menu_get_prompt(const struct menu *menu) diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 99dce93a4188..c4cc11aa558f 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -567,12 +567,12 @@ static int do_acpi_entry(const char *filename, void *symval, char *alias) { DEF_FIELD_ADDR(symval, acpi_device_id, id); - DEF_FIELD_ADDR(symval, acpi_device_id, cls); - DEF_FIELD_ADDR(symval, acpi_device_id, cls_msk); + DEF_FIELD(symval, acpi_device_id, cls); + DEF_FIELD(symval, acpi_device_id, cls_msk); if (id && strlen((const char *)*id)) sprintf(alias, "acpi*:%s:*", *id); - else if (cls) { + else { int i, byte_shift, cnt = 0; unsigned int msk; @@ -580,10 +580,10 @@ static int do_acpi_entry(const char *filename, cnt = 6; for (i = 1; i <= 3; i++) { byte_shift = 8 * (3-i); - msk = (*cls_msk >> byte_shift) & 0xFF; + msk = (cls_msk >> byte_shift) & 0xFF; if (msk) sprintf(&alias[cnt], "%02x", - (*cls >> byte_shift) & 0xFF); + (cls >> byte_shift) & 0xFF); else sprintf(&alias[cnt], "??"); cnt += 2; @@ -743,7 +743,7 @@ static void do_input(char *alias, for (i = min / BITS_PER_LONG; i < max / BITS_PER_LONG + 1; i++) arr[i] = TO_NATIVE(arr[i]); for (i = min; i < max; i++) - if (arr[i / BITS_PER_LONG] & (1L << (i%BITS_PER_LONG))) + if (arr[i / BITS_PER_LONG] & (1ULL << (i%BITS_PER_LONG))) sprintf(alias + strlen(alias), "%X,*", i); } diff --git a/scripts/mod/sumversion.c b/scripts/mod/sumversion.c index e7d2da45b0df..6de9af17599d 100644 --- a/scripts/mod/sumversion.c +++ b/scripts/mod/sumversion.c @@ -392,7 +392,7 @@ out_file: /* Calc and record src checksum. */ void get_src_version(const char *modname, char sum[], unsigned sumlen) { - char *buf; + char *buf, *pos; struct md4_ctx md; char *fname; char filelist[PATH_MAX + 1]; @@ -401,9 +401,10 @@ void get_src_version(const char *modname, char sum[], unsigned sumlen) snprintf(filelist, sizeof(filelist), "%s.mod", modname); buf = read_text_file(filelist); + pos = buf; md4_init(&md); - while ((fname = strsep(&buf, "\n"))) { + while ((fname = strsep(&pos, "\n"))) { if (!*fname) continue; if (!(is_static_library(fname)) && diff --git a/scripts/package/builddeb b/scripts/package/builddeb index 404587fc71fe..441b0bb66e0d 100755 --- a/scripts/package/builddeb +++ b/scripts/package/builddeb @@ -123,7 +123,7 @@ install_kernel_headers () { pdir=debian/$1 version=${1#linux-headers-} - "${srctree}/scripts/package/install-extmod-build" "${pdir}/usr/src/linux-headers-${version}" + CC="${DEB_HOST_GNU_TYPE}-gcc" "${srctree}/scripts/package/install-extmod-build" "${pdir}/usr/src/linux-headers-${version}" mkdir -p $pdir/lib/modules/$version/ ln -s /usr/src/linux-headers-$version $pdir/lib/modules/$version/build diff --git a/scripts/package/install-extmod-build b/scripts/package/install-extmod-build index d2c9cacecc0c..7ec1f061a519 100755 --- a/scripts/package/install-extmod-build +++ b/scripts/package/install-extmod-build @@ -44,13 +44,11 @@ mkdir -p "${destdir}" fi } | tar -c -f - -T - | tar -xf - -C "${destdir}" -# When ${CC} and ${HOSTCC} differ, we are likely cross-compiling. Rebuild host -# programs using ${CC}. This assumes CC=${CROSS_COMPILE}gcc, which is usually -# the case for package building. It does not cross-compile when CC=clang. +# When ${CC} and ${HOSTCC} differ, rebuild host programs using ${CC}. # # This caters to host programs that participate in Kbuild. objtool and # resolve_btfids are out of scope. -if [ "${CC}" != "${HOSTCC}" ] && is_enabled CONFIG_CC_CAN_LINK; then +if [ "${CC}" != "${HOSTCC}" ]; then echo "Rebuilding host programs with ${CC}..." cat <<-'EOF' > "${destdir}/Kbuild" diff --git a/scripts/package/mkdebian b/scripts/package/mkdebian index 10637d403777..fc3b7fa709fc 100755 --- a/scripts/package/mkdebian +++ b/scripts/package/mkdebian @@ -179,6 +179,8 @@ fi echo $debarch > debian/arch +host_gnu=$(dpkg-architecture -a "${debarch}" -q DEB_HOST_GNU_TYPE | sed 's/_/-/g') + # Generate a simple changelog template cat <<EOF > debian/changelog $sourcename ($packageversion) $distribution; urgency=low @@ -196,7 +198,11 @@ Priority: optional Maintainer: $maintainer Rules-Requires-Root: no Build-Depends: debhelper-compat (= 12) -Build-Depends-Arch: bc, bison, cpio, flex, kmod, libelf-dev:native, libssl-dev:native, rsync +Build-Depends-Arch: bc, bison, cpio, flex, + gcc-${host_gnu} <!pkg.${sourcename}.nokernelheaders>, + kmod, libelf-dev:native, + libssl-dev:native, libssl-dev <!pkg.${sourcename}.nokernelheaders>, + rsync Homepage: https://www.kernel.org/ Package: $packagename-$version @@ -224,6 +230,7 @@ cat <<EOF >> debian/control Package: linux-headers-$version Architecture: $debarch +Build-Profiles: <!pkg.${sourcename}.nokernelheaders> Description: Linux kernel headers for $version on $debarch This package provides kernel header files for $version on $debarch . @@ -238,6 +245,7 @@ cat <<EOF >> debian/control Package: linux-image-$version-dbg Section: debug Architecture: $debarch +Build-Profiles: <!pkg.${sourcename}.nokerneldbg> Description: Linux kernel debugging symbols for $version This package will come in handy if you need to debug the kernel. It provides all the necessary debug symbols for the kernel and its modules. diff --git a/scripts/rustc-llvm-version.sh b/scripts/rustc-llvm-version.sh index b6063cbe5bdc..a500d1ae3101 100755 --- a/scripts/rustc-llvm-version.sh +++ b/scripts/rustc-llvm-version.sh @@ -13,7 +13,7 @@ get_canonical_version() echo $((10000 * $1 + 100 * $2 + $3)) } -if output=$("$@" --version --verbose 2>/dev/null | grep LLVM); then +if output=$("$@" --version --verbose 2>/dev/null | grep -E 'LLVM.*[0-9]+\.[0-9]+\.[0-9]+'); then set -- $output get_canonical_version $3 else diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/intel-dsp-config.c index f018bd779862..9f849e05ce79 100644 --- a/sound/hda/intel-dsp-config.c +++ b/sound/hda/intel-dsp-config.c @@ -723,6 +723,10 @@ static const struct config_entry acpi_config_table[] = { /* BayTrail */ { .flags = FLAG_SST_OR_SOF_BYT, + .acpi_hid = "LPE0F28", + }, + { + .flags = FLAG_SST_OR_SOF_BYT, .acpi_hid = "80860F28", }, /* CherryTrail */ diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 3567b14b52b7..571fa8a6c9e1 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -7521,6 +7521,7 @@ enum { ALC286_FIXUP_SONY_MIC_NO_PRESENCE, ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT, ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC269_FIXUP_DELL1_LIMIT_INT_MIC_BOOST, ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, ALC269_FIXUP_DELL3_MIC_NO_PRESENCE, ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, @@ -7551,10 +7552,12 @@ enum { ALC290_FIXUP_SUBWOOFER_HSJACK, ALC269_FIXUP_THINKPAD_ACPI, ALC269_FIXUP_DMIC_THINKPAD_ACPI, + ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13, ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO, ALC255_FIXUP_ACER_MIC_NO_PRESENCE, ALC255_FIXUP_ASUS_MIC_NO_PRESENCE, ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC255_FIXUP_DELL1_LIMIT_INT_MIC_BOOST, ALC255_FIXUP_DELL2_MIC_NO_PRESENCE, ALC255_FIXUP_HEADSET_MODE, ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC, @@ -7996,6 +7999,16 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_pincfg_U7x7_headset_mic, }, + [ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x90170151 }, /* use as internal speaker (LFE) */ + { 0x1b, 0x90170152 }, /* use as internal speaker (back) */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST + }, [ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -8114,6 +8127,12 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_HEADSET_MODE }, + [ALC269_FIXUP_DELL1_LIMIT_INT_MIC_BOOST] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_limit_int_mic_boost, + .chained = true, + .chain_id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE + }, [ALC269_FIXUP_DELL2_MIC_NO_PRESENCE] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -8394,6 +8413,12 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC255_FIXUP_HEADSET_MODE }, + [ALC255_FIXUP_DELL1_LIMIT_INT_MIC_BOOST] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_limit_int_mic_boost, + .chained = true, + .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE + }, [ALC255_FIXUP_DELL2_MIC_NO_PRESENCE] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -10725,6 +10750,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1558, 0x1404, "Clevo N150CU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x14a1, "Clevo L141MU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x2624, "Clevo L240TU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x28c1, "Clevo V370VND", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1558, 0x4018, "Clevo NV40M[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x4019, "Clevo NV40MZ", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x4020, "Clevo NV40MB", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), @@ -10982,6 +11008,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1d05, 0x115c, "TongFang GMxTGxx", ALC269_FIXUP_NO_SHUTUP), SND_PCI_QUIRK(0x1d05, 0x121b, "TongFang GMxAGxx", ALC269_FIXUP_NO_SHUTUP), SND_PCI_QUIRK(0x1d05, 0x1387, "TongFang GMxIXxx", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1d05, 0x1409, "TongFang GMxIXxx", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1d17, 0x3288, "Haier Boyue G42", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS), SND_PCI_QUIRK(0x1d72, 0x1602, "RedmiBook", ALC255_FIXUP_XIAOMI_HEADSET_MIC), SND_PCI_QUIRK(0x1d72, 0x1701, "XiaomiNotebook Pro", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE), @@ -10989,6 +11016,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1d72, 0x1945, "Redmi G", ALC256_FIXUP_ASUS_HEADSET_MIC), SND_PCI_QUIRK(0x1d72, 0x1947, "RedmiBook Air", ALC255_FIXUP_XIAOMI_HEADSET_MIC), SND_PCI_QUIRK(0x2782, 0x0214, "VAIO VJFE-CL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x2782, 0x0228, "Infinix ZERO BOOK 13", ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13), SND_PCI_QUIRK(0x2782, 0x0232, "CHUWI CoreBook XPro", ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO), SND_PCI_QUIRK(0x2782, 0x1707, "Vaio VJFE-ADL", ALC298_FIXUP_SPK_VOLUME), SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC), @@ -11076,6 +11104,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, .name = "dell-headset-dock"}, {.id = ALC269_FIXUP_DELL3_MIC_NO_PRESENCE, .name = "dell-headset3"}, {.id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, .name = "dell-headset4"}, + {.id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET, .name = "dell-headset4-quiet"}, {.id = ALC283_FIXUP_CHROME_BOOK, .name = "alc283-dac-wcaps"}, {.id = ALC283_FIXUP_SENSE_COMBO_JACK, .name = "alc283-sense-combo"}, {.id = ALC292_FIXUP_TPT440_DOCK, .name = "tpt440-dock"}, @@ -11630,16 +11659,16 @@ static const struct snd_hda_pin_quirk alc269_fallback_pin_fixup_tbl[] = { SND_HDA_PIN_QUIRK(0x10ec0289, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, {0x19, 0x40000000}, {0x1b, 0x40000000}), - SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, + SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET, {0x19, 0x40000000}, {0x1b, 0x40000000}), SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, {0x19, 0x40000000}, {0x1a, 0x40000000}), - SND_HDA_PIN_QUIRK(0x10ec0236, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + SND_HDA_PIN_QUIRK(0x10ec0236, 0x1028, "Dell", ALC255_FIXUP_DELL1_LIMIT_INT_MIC_BOOST, {0x19, 0x40000000}, {0x1a, 0x40000000}), - SND_HDA_PIN_QUIRK(0x10ec0274, 0x1028, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB, + SND_HDA_PIN_QUIRK(0x10ec0274, 0x1028, "Dell", ALC269_FIXUP_DELL1_LIMIT_INT_MIC_BOOST, {0x19, 0x40000000}, {0x1a, 0x40000000}), SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC2XX_FIXUP_HEADSET_MIC, diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c index e4827b8c2bde..6e51954bdb1e 100644 --- a/sound/soc/codecs/cs42l51.c +++ b/sound/soc/codecs/cs42l51.c @@ -747,8 +747,10 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap) cs42l51->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(cs42l51->reset_gpio)) - return PTR_ERR(cs42l51->reset_gpio); + if (IS_ERR(cs42l51->reset_gpio)) { + ret = PTR_ERR(cs42l51->reset_gpio); + goto error; + } if (cs42l51->reset_gpio) { dev_dbg(dev, "Release reset gpio\n"); @@ -780,6 +782,7 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap) return 0; error: + gpiod_set_value_cansleep(cs42l51->reset_gpio, 1); regulator_bulk_disable(ARRAY_SIZE(cs42l51->supplies), cs42l51->supplies); return ret; diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 16f3425a3e35..855139348edb 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -2419,10 +2419,20 @@ static irqreturn_t rt5640_jd_gpio_irq(int irq, void *data) return IRQ_HANDLED; } -static void rt5640_cancel_work(void *data) +static void rt5640_disable_irq_and_cancel_work(void *data) { struct rt5640_priv *rt5640 = data; + if (rt5640->jd_gpio_irq_requested) { + free_irq(rt5640->jd_gpio_irq, rt5640); + rt5640->jd_gpio_irq_requested = false; + } + + if (rt5640->irq_requested) { + free_irq(rt5640->irq, rt5640); + rt5640->irq_requested = false; + } + cancel_delayed_work_sync(&rt5640->jack_work); cancel_delayed_work_sync(&rt5640->bp_work); } @@ -2463,13 +2473,7 @@ static void rt5640_disable_jack_detect(struct snd_soc_component *component) if (!rt5640->jack) return; - if (rt5640->jd_gpio_irq_requested) - free_irq(rt5640->jd_gpio_irq, rt5640); - - if (rt5640->irq_requested) - free_irq(rt5640->irq, rt5640); - - rt5640_cancel_work(rt5640); + rt5640_disable_irq_and_cancel_work(rt5640); if (rt5640->jack->status & SND_JACK_MICROPHONE) { rt5640_disable_micbias1_ovcd_irq(component); @@ -2477,8 +2481,6 @@ static void rt5640_disable_jack_detect(struct snd_soc_component *component) snd_soc_jack_report(rt5640->jack, 0, SND_JACK_BTN_0); } - rt5640->jd_gpio_irq_requested = false; - rt5640->irq_requested = false; rt5640->jd_gpio = NULL; rt5640->jack = NULL; } @@ -2798,7 +2800,8 @@ static int rt5640_suspend(struct snd_soc_component *component) if (rt5640->jack) { /* disable jack interrupts during system suspend */ disable_irq(rt5640->irq); - rt5640_cancel_work(rt5640); + cancel_delayed_work_sync(&rt5640->jack_work); + cancel_delayed_work_sync(&rt5640->bp_work); } snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF); @@ -3032,7 +3035,7 @@ static int rt5640_i2c_probe(struct i2c_client *i2c) INIT_DELAYED_WORK(&rt5640->jack_work, rt5640_jack_work); /* Make sure work is stopped on probe-error / remove */ - ret = devm_add_action_or_reset(&i2c->dev, rt5640_cancel_work, rt5640); + ret = devm_add_action_or_reset(&i2c->dev, rt5640_disable_irq_and_cancel_work, rt5640); if (ret) return ret; diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c index 45f32d281908..08fb13a334a4 100644 --- a/sound/soc/codecs/wcd937x.c +++ b/sound/soc/codecs/wcd937x.c @@ -715,12 +715,17 @@ static int wcd937x_codec_enable_aux_pa(struct snd_soc_dapm_widget *w, struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); int hph_mode = wcd937x->hph_mode; + u8 val; switch (event) { case SND_SOC_DAPM_PRE_PMU: + val = WCD937X_DIGITAL_PDM_WD_CTL2_EN | + WCD937X_DIGITAL_PDM_WD_CTL2_TIMEOUT_SEL | + WCD937X_DIGITAL_PDM_WD_CTL2_HOLD_OFF; snd_soc_component_update_bits(component, WCD937X_DIGITAL_PDM_WD_CTL2, - BIT(0), BIT(0)); + WCD937X_DIGITAL_PDM_WD_CTL2_MASK, + val); break; case SND_SOC_DAPM_POST_PMU: usleep_range(1000, 1010); @@ -741,7 +746,8 @@ static int wcd937x_codec_enable_aux_pa(struct snd_soc_dapm_widget *w, hph_mode); snd_soc_component_update_bits(component, WCD937X_DIGITAL_PDM_WD_CTL2, - BIT(0), 0x00); + WCD937X_DIGITAL_PDM_WD_CTL2_MASK, + 0x00); break; } @@ -2049,6 +2055,8 @@ static const struct snd_kcontrol_new wcd937x_snd_controls[] = { wcd937x_get_swr_port, wcd937x_set_swr_port), SOC_SINGLE_EXT("HPHR Switch", WCD937X_HPH_R, 0, 1, 0, wcd937x_get_swr_port, wcd937x_set_swr_port), + SOC_SINGLE_EXT("LO Switch", WCD937X_LO, 0, 1, 0, + wcd937x_get_swr_port, wcd937x_set_swr_port), SOC_SINGLE_EXT("ADC1 Switch", WCD937X_ADC1, 1, 1, 0, wcd937x_get_swr_port, wcd937x_set_swr_port), diff --git a/sound/soc/codecs/wcd937x.h b/sound/soc/codecs/wcd937x.h index 35f3d48bd7dd..4afa48dcaf74 100644 --- a/sound/soc/codecs/wcd937x.h +++ b/sound/soc/codecs/wcd937x.h @@ -391,6 +391,10 @@ #define WCD937X_DIGITAL_PDM_WD_CTL0 0x3465 #define WCD937X_DIGITAL_PDM_WD_CTL1 0x3466 #define WCD937X_DIGITAL_PDM_WD_CTL2 0x3467 +#define WCD937X_DIGITAL_PDM_WD_CTL2_HOLD_OFF BIT(2) +#define WCD937X_DIGITAL_PDM_WD_CTL2_TIMEOUT_SEL BIT(1) +#define WCD937X_DIGITAL_PDM_WD_CTL2_EN BIT(0) +#define WCD937X_DIGITAL_PDM_WD_CTL2_MASK GENMASK(2, 0) #define WCD937X_DIGITAL_INTR_MODE 0x346A #define WCD937X_DIGITAL_INTR_MASK_0 0x346B #define WCD937X_DIGITAL_INTR_MASK_1 0x346C diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c index 9956dc63db74..257180630475 100644 --- a/sound/soc/intel/atom/sst/sst_acpi.c +++ b/sound/soc/intel/atom/sst/sst_acpi.c @@ -125,6 +125,28 @@ static const struct sst_res_info bytcr_res_info = { .acpi_ipc_irq_index = 0 }; +/* For "LPE0F28" ACPI device found on some Android factory OS models */ +static const struct sst_res_info lpe8086_res_info = { + .shim_offset = 0x140000, + .shim_size = 0x000100, + .shim_phy_addr = SST_BYT_SHIM_PHY_ADDR, + .ssp0_offset = 0xa0000, + .ssp0_size = 0x1000, + .dma0_offset = 0x98000, + .dma0_size = 0x4000, + .dma1_offset = 0x9c000, + .dma1_size = 0x4000, + .iram_offset = 0x0c0000, + .iram_size = 0x14000, + .dram_offset = 0x100000, + .dram_size = 0x28000, + .mbox_offset = 0x144000, + .mbox_size = 0x1000, + .acpi_lpe_res_index = 1, + .acpi_ddr_index = 0, + .acpi_ipc_irq_index = 0 +}; + static struct sst_platform_info byt_rvp_platform_data = { .probe_data = &byt_fwparse_info, .ipc_info = &byt_ipc_info, @@ -268,10 +290,38 @@ static int sst_acpi_probe(struct platform_device *pdev) mach->pdata = &chv_platform_data; pdata = mach->pdata; - ret = kstrtouint(id->id, 16, &dev_id); - if (ret < 0) { - dev_err(dev, "Unique device id conversion error: %d\n", ret); - return ret; + if (!strcmp(id->id, "LPE0F28")) { + struct resource *rsrc; + + /* Use regular BYT SST PCI VID:PID */ + dev_id = 0x80860F28; + byt_rvp_platform_data.res_info = &lpe8086_res_info; + + /* + * The "LPE0F28" ACPI device has separate IO-mem resources for: + * DDR, SHIM, MBOX, IRAM, DRAM, CFG + * None of which covers the entire LPE base address range. + * lpe8086_res_info.acpi_lpe_res_index points to the SHIM. + * Patch this to cover the entire base address range as expected + * by sst_platform_get_resources(). + */ + rsrc = platform_get_resource(pdev, IORESOURCE_MEM, + pdata->res_info->acpi_lpe_res_index); + if (!rsrc) { + dev_err(dev, "Invalid SHIM base\n"); + return -EIO; + } + rsrc->start -= pdata->res_info->shim_offset; + rsrc->end = rsrc->start + 0x200000 - 1; + } else { + ret = kstrtouint(id->id, 16, &dev_id); + if (ret < 0) { + dev_err(dev, "Unique device id conversion error: %d\n", ret); + return ret; + } + + if (soc_intel_is_byt_cr(pdev)) + byt_rvp_platform_data.res_info = &bytcr_res_info; } dev_dbg(dev, "ACPI device id: %x\n", dev_id); @@ -280,11 +330,6 @@ static int sst_acpi_probe(struct platform_device *pdev) if (ret < 0) return ret; - if (soc_intel_is_byt_cr(pdev)) { - /* override resource info */ - byt_rvp_platform_data.res_info = &bytcr_res_info; - } - /* update machine parameters */ mach->mach_params.acpi_ipc_irq_index = pdata->res_info->acpi_ipc_irq_index; @@ -344,6 +389,7 @@ static void sst_acpi_remove(struct platform_device *pdev) } static const struct acpi_device_id sst_acpi_ids[] = { + { "LPE0F28", (unsigned long)&snd_soc_acpi_intel_baytrail_machines}, { "80860F28", (unsigned long)&snd_soc_acpi_intel_baytrail_machines}, { "808622A8", (unsigned long)&snd_soc_acpi_intel_cherrytrail_machines}, { }, diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 2ed49acb4e36..54f77f57ec8e 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -17,6 +17,7 @@ #include <linux/acpi.h> #include <linux/clk.h> #include <linux/device.h> +#include <linux/device/bus.h> #include <linux/dmi.h> #include <linux/gpio/consumer.h> #include <linux/gpio/machine.h> @@ -32,6 +33,8 @@ #include "../atom/sst-atom-controls.h" #include "../common/soc-intel-quirks.h" +#define BYT_RT5640_FALLBACK_CODEC_DEV_NAME "i2c-rt5640" + enum { BYT_RT5640_DMIC1_MAP, BYT_RT5640_DMIC2_MAP, @@ -1129,6 +1132,21 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { BYT_RT5640_SSP0_AIF2 | BYT_RT5640_MCLK_EN), }, + { /* Vexia Edu Atla 10 tablet */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"), + /* Above strings are too generic, also match on BIOS date */ + DMI_MATCH(DMI_BIOS_DATE, "08/25/2014"), + }, + .driver_data = (void *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_JD_SRC_JD2_IN4N | + BYT_RT5640_OVCD_TH_2000UA | + BYT_RT5640_OVCD_SF_0P75 | + BYT_RT5640_DIFF_MIC | + BYT_RT5640_SSP0_AIF2 | + BYT_RT5640_MCLK_EN), + }, { /* Voyo Winpad A15 */ .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), @@ -1698,9 +1716,33 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) codec_dev = acpi_get_first_physical_node(adev); acpi_dev_put(adev); - if (!codec_dev) - return -EPROBE_DEFER; - priv->codec_dev = get_device(codec_dev); + + if (codec_dev) { + priv->codec_dev = get_device(codec_dev); + } else { + /* + * Special case for Android tablets where the codec i2c_client + * has been manually instantiated by x86_android_tablets.ko due + * to a broken DSDT. + */ + codec_dev = bus_find_device_by_name(&i2c_bus_type, NULL, + BYT_RT5640_FALLBACK_CODEC_DEV_NAME); + if (!codec_dev) + return -EPROBE_DEFER; + + if (!i2c_verify_client(codec_dev)) { + dev_err(dev, "Error '%s' is not an i2c_client\n", + BYT_RT5640_FALLBACK_CODEC_DEV_NAME); + put_device(codec_dev); + } + + /* fixup codec name */ + strscpy(byt_rt5640_codec_name, BYT_RT5640_FALLBACK_CODEC_DEV_NAME, + sizeof(byt_rt5640_codec_name)); + + /* bus_find_device() returns a reference no need to get() */ + priv->codec_dev = codec_dev; + } /* * swap SSP0 if bytcr is detected diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index c34934c31ffe..99521c784a9b 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1147,6 +1147,8 @@ static int dapm_widget_list_create(struct snd_soc_dapm_widget_list **list, if (*list == NULL) return -ENOMEM; + (*list)->num_widgets = size; + list_for_each_entry(w, widgets, work_list) (*list)->widgets[i++] = w; diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 2a9594f34dac..6456e87e2f39 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -4042,6 +4042,9 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) break; err = dell_dock_mixer_init(mixer); break; + case USB_ID(0x0bda, 0x402e): /* Dell WD19 dock */ + err = dell_dock_mixer_create(mixer); + break; case USB_ID(0x2a39, 0x3fd2): /* RME ADI-2 Pro */ case USB_ID(0x2a39, 0x3fd3): /* RME ADI-2 DAC */ diff --git a/tools/arch/arm64/include/asm/cputype.h b/tools/arch/arm64/include/asm/cputype.h index 5a7dfeb8e8eb..488f8e751349 100644 --- a/tools/arch/arm64/include/asm/cputype.h +++ b/tools/arch/arm64/include/asm/cputype.h @@ -94,6 +94,7 @@ #define ARM_CPU_PART_NEOVERSE_V3 0xD84 #define ARM_CPU_PART_CORTEX_X925 0xD85 #define ARM_CPU_PART_CORTEX_A725 0xD87 +#define ARM_CPU_PART_NEOVERSE_N3 0xD8E #define APM_CPU_PART_XGENE 0x000 #define APM_CPU_VAR_POTENZA 0x00 @@ -176,6 +177,7 @@ #define MIDR_NEOVERSE_V3 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_V3) #define MIDR_CORTEX_X925 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X925) #define MIDR_CORTEX_A725 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A725) +#define MIDR_NEOVERSE_N3 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_N3) #define MIDR_THUNDERX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX) #define MIDR_THUNDERX_81XX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX_81XX) #define MIDR_THUNDERX_83XX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX_83XX) diff --git a/tools/arch/x86/include/asm/msr-index.h b/tools/arch/x86/include/asm/msr-index.h index a7c06a46fb76..3ae84c3b8e6d 100644 --- a/tools/arch/x86/include/asm/msr-index.h +++ b/tools/arch/x86/include/asm/msr-index.h @@ -36,6 +36,20 @@ #define EFER_FFXSR (1<<_EFER_FFXSR) #define EFER_AUTOIBRS (1<<_EFER_AUTOIBRS) +/* + * Architectural memory types that are common to MTRRs, PAT, VMX MSRs, etc. + * Most MSRs support/allow only a subset of memory types, but the values + * themselves are common across all relevant MSRs. + */ +#define X86_MEMTYPE_UC 0ull /* Uncacheable, a.k.a. Strong Uncacheable */ +#define X86_MEMTYPE_WC 1ull /* Write Combining */ +/* RESERVED 2 */ +/* RESERVED 3 */ +#define X86_MEMTYPE_WT 4ull /* Write Through */ +#define X86_MEMTYPE_WP 5ull /* Write Protected */ +#define X86_MEMTYPE_WB 6ull /* Write Back */ +#define X86_MEMTYPE_UC_MINUS 7ull /* Weak Uncacheabled (PAT only) */ + /* FRED MSRs */ #define MSR_IA32_FRED_RSP0 0x1cc /* Level 0 stack pointer */ #define MSR_IA32_FRED_RSP1 0x1cd /* Level 1 stack pointer */ @@ -365,6 +379,12 @@ #define MSR_IA32_CR_PAT 0x00000277 +#define PAT_VALUE(p0, p1, p2, p3, p4, p5, p6, p7) \ + ((X86_MEMTYPE_ ## p0) | (X86_MEMTYPE_ ## p1 << 8) | \ + (X86_MEMTYPE_ ## p2 << 16) | (X86_MEMTYPE_ ## p3 << 24) | \ + (X86_MEMTYPE_ ## p4 << 32) | (X86_MEMTYPE_ ## p5 << 40) | \ + (X86_MEMTYPE_ ## p6 << 48) | (X86_MEMTYPE_ ## p7 << 56)) + #define MSR_IA32_DEBUGCTLMSR 0x000001d9 #define MSR_IA32_LASTBRANCHFROMIP 0x000001db #define MSR_IA32_LASTBRANCHTOIP 0x000001dc @@ -1159,15 +1179,6 @@ #define MSR_IA32_VMX_VMFUNC 0x00000491 #define MSR_IA32_VMX_PROCBASED_CTLS3 0x00000492 -/* VMX_BASIC bits and bitmasks */ -#define VMX_BASIC_VMCS_SIZE_SHIFT 32 -#define VMX_BASIC_TRUE_CTLS (1ULL << 55) -#define VMX_BASIC_64 0x0001000000000000LLU -#define VMX_BASIC_MEM_TYPE_SHIFT 50 -#define VMX_BASIC_MEM_TYPE_MASK 0x003c000000000000LLU -#define VMX_BASIC_MEM_TYPE_WB 6LLU -#define VMX_BASIC_INOUT 0x0040000000000000LLU - /* Resctrl MSRs: */ /* - Intel: */ #define MSR_IA32_L3_QOS_CFG 0xc81 @@ -1185,11 +1196,6 @@ #define MSR_IA32_SMBA_BW_BASE 0xc0000280 #define MSR_IA32_EVT_CFG_BASE 0xc0000400 -/* MSR_IA32_VMX_MISC bits */ -#define MSR_IA32_VMX_MISC_INTEL_PT (1ULL << 14) -#define MSR_IA32_VMX_MISC_VMWRITE_SHADOW_RO_FIELDS (1ULL << 29) -#define MSR_IA32_VMX_MISC_PREEMPTION_TIMER_SCALE 0x1F - /* AMD-V MSRs */ #define MSR_VM_CR 0xc0010114 #define MSR_VM_IGNNE 0xc0010115 diff --git a/tools/arch/x86/include/uapi/asm/kvm.h b/tools/arch/x86/include/uapi/asm/kvm.h index bf57a824f722..a8debbf2f702 100644 --- a/tools/arch/x86/include/uapi/asm/kvm.h +++ b/tools/arch/x86/include/uapi/asm/kvm.h @@ -439,6 +439,7 @@ struct kvm_sync_regs { #define KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT (1 << 4) #define KVM_X86_QUIRK_FIX_HYPERCALL_INSN (1 << 5) #define KVM_X86_QUIRK_MWAIT_NEVER_UD_FAULTS (1 << 6) +#define KVM_X86_QUIRK_SLOT_ZAP_ALL (1 << 7) #define KVM_STATE_NESTED_FORMAT_VMX 0 #define KVM_STATE_NESTED_FORMAT_SVM 1 diff --git a/tools/arch/x86/include/uapi/asm/unistd_32.h b/tools/arch/x86/include/uapi/asm/unistd_32.h index 9de35df1afc3..63182a023e9d 100644 --- a/tools/arch/x86/include/uapi/asm/unistd_32.h +++ b/tools/arch/x86/include/uapi/asm/unistd_32.h @@ -11,6 +11,9 @@ #ifndef __NR_getpgid #define __NR_getpgid 132 #endif +#ifndef __NR_capget +#define __NR_capget 184 +#endif #ifndef __NR_gettid #define __NR_gettid 224 #endif diff --git a/tools/arch/x86/include/uapi/asm/unistd_64.h b/tools/arch/x86/include/uapi/asm/unistd_64.h index d0f2043d7132..77311e8d1b5d 100644 --- a/tools/arch/x86/include/uapi/asm/unistd_64.h +++ b/tools/arch/x86/include/uapi/asm/unistd_64.h @@ -11,6 +11,9 @@ #ifndef __NR_getpgid #define __NR_getpgid 121 #endif +#ifndef __NR_capget +#define __NR_capget 125 +#endif #ifndef __NR_gettid #define __NR_gettid 186 #endif diff --git a/tools/include/linux/bits.h b/tools/include/linux/bits.h index 0eb24d21aac2..60044b608817 100644 --- a/tools/include/linux/bits.h +++ b/tools/include/linux/bits.h @@ -36,4 +36,19 @@ #define GENMASK_ULL(h, l) \ (GENMASK_INPUT_CHECK(h, l) + __GENMASK_ULL(h, l)) +#if !defined(__ASSEMBLY__) +/* + * Missing asm support + * + * __GENMASK_U128() depends on _BIT128() which would not work + * in the asm code, as it shifts an 'unsigned __init128' data + * type instead of direct representation of 128 bit constants + * such as long and unsigned long. The fundamental problem is + * that a 128 bit constant will get silently truncated by the + * gcc compiler. + */ +#define GENMASK_U128(h, l) \ + (GENMASK_INPUT_CHECK(h, l) + __GENMASK_U128(h, l)) +#endif + #endif /* __LINUX_BITS_H */ diff --git a/tools/include/linux/unaligned.h b/tools/include/linux/unaligned.h index bc0633bc4650..395a4464fe73 100644 --- a/tools/include/linux/unaligned.h +++ b/tools/include/linux/unaligned.h @@ -9,16 +9,7 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpacked" #pragma GCC diagnostic ignored "-Wattributes" - -#define __get_unaligned_t(type, ptr) ({ \ - const struct { type x; } __packed *__pptr = (typeof(__pptr))(ptr); \ - __pptr->x; \ -}) - -#define __put_unaligned_t(type, val, ptr) do { \ - struct { type x; } __packed *__pptr = (typeof(__pptr))(ptr); \ - __pptr->x = (val); \ -} while (0) +#include <vdso/unaligned.h> #define get_unaligned(ptr) __get_unaligned_t(typeof(*(ptr)), (ptr)) #define put_unaligned(val, ptr) __put_unaligned_t(typeof(*(ptr)), (val), (ptr)) diff --git a/tools/include/uapi/linux/bits.h b/tools/include/uapi/linux/bits.h index 3c2a101986a3..5ee30f882736 100644 --- a/tools/include/uapi/linux/bits.h +++ b/tools/include/uapi/linux/bits.h @@ -12,4 +12,7 @@ (((~_ULL(0)) - (_ULL(1) << (l)) + 1) & \ (~_ULL(0) >> (__BITS_PER_LONG_LONG - 1 - (h)))) +#define __GENMASK_U128(h, l) \ + ((_BIT128((h)) << 1) - (_BIT128(l))) + #endif /* _UAPI_LINUX_BITS_H */ diff --git a/tools/include/uapi/linux/const.h b/tools/include/uapi/linux/const.h index a429381e7ca5..e16be0d37746 100644 --- a/tools/include/uapi/linux/const.h +++ b/tools/include/uapi/linux/const.h @@ -28,6 +28,23 @@ #define _BITUL(x) (_UL(1) << (x)) #define _BITULL(x) (_ULL(1) << (x)) +#if !defined(__ASSEMBLY__) +/* + * Missing asm support + * + * __BIT128() would not work in the asm code, as it shifts an + * 'unsigned __init128' data type as direct representation of + * 128 bit constants is not supported in the gcc compiler, as + * they get silently truncated. + * + * TODO: Please revisit this implementation when gcc compiler + * starts representing 128 bit constants directly like long + * and unsigned long etc. Subsequently drop the comment for + * GENMASK_U128() which would then start supporting asm code. + */ +#define _BIT128(x) ((unsigned __int128)(1) << (x)) +#endif + #define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (__typeof__(x))(a) - 1) #define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask)) diff --git a/tools/include/vdso/unaligned.h b/tools/include/vdso/unaligned.h new file mode 100644 index 000000000000..eee3d2a4dbe4 --- /dev/null +++ b/tools/include/vdso/unaligned.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __VDSO_UNALIGNED_H +#define __VDSO_UNALIGNED_H + +#define __get_unaligned_t(type, ptr) ({ \ + const struct { type x; } __packed *__pptr = (typeof(__pptr))(ptr); \ + __pptr->x; \ +}) + +#define __put_unaligned_t(type, val, ptr) do { \ + struct { type x; } __packed *__pptr = (typeof(__pptr))(ptr); \ + __pptr->x = (val); \ +} while (0) + +#endif /* __VDSO_UNALIGNED_H */ diff --git a/tools/mm/page-types.c b/tools/mm/page-types.c index fa050d5a48cd..6eb17cc1a06c 100644 --- a/tools/mm/page-types.c +++ b/tools/mm/page-types.c @@ -22,6 +22,7 @@ #include <time.h> #include <setjmp.h> #include <signal.h> +#include <inttypes.h> #include <sys/types.h> #include <sys/errno.h> #include <sys/fcntl.h> @@ -391,9 +392,9 @@ static void show_page_range(unsigned long voffset, unsigned long offset, if (opt_file) printf("%lx\t", voff); if (opt_list_cgroup) - printf("@%llu\t", (unsigned long long)cgroup0); + printf("@%" PRIu64 "\t", cgroup0); if (opt_list_mapcnt) - printf("%lu\t", mapcnt0); + printf("%" PRIu64 "\t", mapcnt0); printf("%lx\t%lx\t%s\n", index, count, page_flag_name(flags0)); } @@ -419,9 +420,9 @@ static void show_page(unsigned long voffset, unsigned long offset, if (opt_file) printf("%lx\t", voffset); if (opt_list_cgroup) - printf("@%llu\t", (unsigned long long)cgroup); + printf("@%" PRIu64 "\t", cgroup) if (opt_list_mapcnt) - printf("%lu\t", mapcnt); + printf("%" PRIu64 "\t", mapcnt); printf("%lx\t%s\n", offset, page_flag_name(flags)); } diff --git a/tools/mm/slabinfo.c b/tools/mm/slabinfo.c index cfaeaea71042..04e9e6ba86ea 100644 --- a/tools/mm/slabinfo.c +++ b/tools/mm/slabinfo.c @@ -1297,7 +1297,9 @@ static void read_slab_dir(void) slab->cpu_partial_free = get_obj("cpu_partial_free"); slab->alloc_node_mismatch = get_obj("alloc_node_mismatch"); slab->deactivate_bypass = get_obj("deactivate_bypass"); - chdir(".."); + if (chdir("..")) + fatal("Unable to chdir from slab ../%s\n", + slab->name); if (slab->name[0] == ':') alias_targets++; slab++; diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 4ddb27a48eed..d4332675babb 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -704,8 +704,8 @@ ifeq ($(BUILD_BPF_SKEL),1) BUILD_BPF_SKEL := 0 else CLANG_VERSION := $(shell $(CLANG) --version | head -1 | sed 's/.*clang version \([[:digit:]]\+.[[:digit:]]\+.[[:digit:]]\+\).*/\1/g') - ifeq ($(call version-lt3,$(CLANG_VERSION),16.0.6),1) - $(warning Warning: Disabled BPF skeletons as at least $(CLANG) version 16.0.6 is reported to be a working setup with the current of BPF based perf features) + ifeq ($(call version-lt3,$(CLANG_VERSION),12.0.1),1) + $(warning Warning: Disabled BPF skeletons as reliable BTF generation needs at least $(CLANG) version 12.0.1) BUILD_BPF_SKEL := 0 endif endif diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index f6e847529073..d3f11b90d025 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1399,7 +1399,7 @@ static const struct syscall_fmt syscall_fmts[] = { .arg = { [2] = { .scnprintf = SCA_WAITID_OPTIONS, /* options */ }, }, }, { .name = "waitid", .errpid = true, .arg = { [3] = { .scnprintf = SCA_WAITID_OPTIONS, /* options */ }, }, }, - { .name = "write", .errpid = true, + { .name = "write", .arg = { [1] = { .scnprintf = SCA_BUF /* buf */, .from_user = true, }, }, }, }; diff --git a/tools/perf/check-headers.sh b/tools/perf/check-headers.sh index 29adbb423327..a05c1c105c51 100755 --- a/tools/perf/check-headers.sh +++ b/tools/perf/check-headers.sh @@ -22,6 +22,7 @@ FILES=( "include/vdso/bits.h" "include/linux/const.h" "include/vdso/const.h" + "include/vdso/unaligned.h" "include/linux/hash.h" "include/linux/list-sort.h" "include/uapi/linux/hw_breakpoint.h" diff --git a/tools/perf/tests/shell/base_probe/test_adding_blacklisted.sh b/tools/perf/tests/shell/base_probe/test_adding_blacklisted.sh index b5dc10b2a738..bead723e34af 100755 --- a/tools/perf/tests/shell/base_probe/test_adding_blacklisted.sh +++ b/tools/perf/tests/shell/base_probe/test_adding_blacklisted.sh @@ -19,35 +19,74 @@ TEST_RESULT=0 # skip if not supported -BLACKFUNC=`head -n 1 /sys/kernel/debug/kprobes/blacklist 2> /dev/null | cut -f2` -if [ -z "$BLACKFUNC" ]; then +BLACKFUNC_LIST=`head -n 5 /sys/kernel/debug/kprobes/blacklist 2> /dev/null | cut -f2` +if [ -z "$BLACKFUNC_LIST" ]; then print_overall_skipped exit 0 fi +# try to find vmlinux with DWARF debug info +VMLINUX_FILE=$(perf probe -v random_probe |& grep "Using.*for symbols" | sed -r 's/^Using (.*) for symbols$/\1/') + # remove all previously added probes clear_all_probes ### adding blacklisted function - -# functions from blacklist should be skipped by perf probe -! $CMD_PERF probe $BLACKFUNC > $LOGS_DIR/adding_blacklisted.log 2> $LOGS_DIR/adding_blacklisted.err -PERF_EXIT_CODE=$? - REGEX_SCOPE_FAIL="Failed to find scope of probe point" REGEX_SKIP_MESSAGE=" is blacklisted function, skip it\." -REGEX_NOT_FOUND_MESSAGE="Probe point \'$BLACKFUNC\' not found." +REGEX_NOT_FOUND_MESSAGE="Probe point \'$RE_EVENT\' not found." REGEX_ERROR_MESSAGE="Error: Failed to add events." REGEX_INVALID_ARGUMENT="Failed to write event: Invalid argument" REGEX_SYMBOL_FAIL="Failed to find symbol at $RE_ADDRESS" -REGEX_OUT_SECTION="$BLACKFUNC is out of \.\w+, skip it" -../common/check_all_lines_matched.pl "$REGEX_SKIP_MESSAGE" "$REGEX_NOT_FOUND_MESSAGE" "$REGEX_ERROR_MESSAGE" "$REGEX_SCOPE_FAIL" "$REGEX_INVALID_ARGUMENT" "$REGEX_SYMBOL_FAIL" "$REGEX_OUT_SECTION" < $LOGS_DIR/adding_blacklisted.err -CHECK_EXIT_CODE=$? - -print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "adding blacklisted function $BLACKFUNC" -(( TEST_RESULT += $? )) - +REGEX_OUT_SECTION="$RE_EVENT is out of \.\w+, skip it" +REGEX_MISSING_DECL_LINE="A function DIE doesn't have decl_line. Maybe broken DWARF?" + +BLACKFUNC="" +SKIP_DWARF=0 + +for BLACKFUNC in $BLACKFUNC_LIST; do + echo "Probing $BLACKFUNC" + + # functions from blacklist should be skipped by perf probe + ! $CMD_PERF probe $BLACKFUNC > $LOGS_DIR/adding_blacklisted.log 2> $LOGS_DIR/adding_blacklisted.err + PERF_EXIT_CODE=$? + + # check for bad DWARF polluting the result + ../common/check_all_patterns_found.pl "$REGEX_MISSING_DECL_LINE" >/dev/null < $LOGS_DIR/adding_blacklisted.err + + if [ $? -eq 0 ]; then + SKIP_DWARF=1 + echo "Result polluted by broken DWARF, trying another probe" + + # confirm that the broken DWARF comes from assembler + if [ -n "$VMLINUX_FILE" ]; then + readelf -wi "$VMLINUX_FILE" | + awk -v probe="$BLACKFUNC" '/DW_AT_language/ { comp_lang = $0 } + $0 ~ probe { if (comp_lang) { print comp_lang }; exit }' | + grep -q "MIPS assembler" + + CHECK_EXIT_CODE=$? + if [ $CHECK_EXIT_CODE -ne 0 ]; then + SKIP_DWARF=0 # broken DWARF while available + break + fi + fi + else + ../common/check_all_lines_matched.pl "$REGEX_SKIP_MESSAGE" "$REGEX_NOT_FOUND_MESSAGE" "$REGEX_ERROR_MESSAGE" "$REGEX_SCOPE_FAIL" "$REGEX_INVALID_ARGUMENT" "$REGEX_SYMBOL_FAIL" "$REGEX_OUT_SECTION" < $LOGS_DIR/adding_blacklisted.err + CHECK_EXIT_CODE=$? + + SKIP_DWARF=0 + break + fi +done + +if [ $SKIP_DWARF -eq 1 ]; then + print_testcase_skipped "adding blacklisted function $BLACKFUNC" +else + print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "adding blacklisted function $BLACKFUNC" + (( TEST_RESULT += $? )) +fi ### listing not-added probe diff --git a/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c b/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c index b2f17cca014b..4a62ed593e84 100644 --- a/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c +++ b/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c @@ -288,6 +288,10 @@ int sys_enter_rename(struct syscall_enter_args *args) augmented_args->arg.size = PERF_ALIGN(oldpath_len + 1, sizeof(u64)); len += augmented_args->arg.size; + /* Every read from userspace is limited to value size */ + if (augmented_args->arg.size > sizeof(augmented_args->arg.value)) + return 1; /* Failure: don't filter */ + struct augmented_arg *arg2 = (void *)&augmented_args->arg.value + augmented_args->arg.size; newpath_len = augmented_arg__read_str(arg2, newpath_arg, sizeof(augmented_args->arg.value)); @@ -315,6 +319,10 @@ int sys_enter_renameat2(struct syscall_enter_args *args) augmented_args->arg.size = PERF_ALIGN(oldpath_len + 1, sizeof(u64)); len += augmented_args->arg.size; + /* Every read from userspace is limited to value size */ + if (augmented_args->arg.size > sizeof(augmented_args->arg.value)) + return 1; /* Failure: don't filter */ + struct augmented_arg *arg2 = (void *)&augmented_args->arg.value + augmented_args->arg.size; newpath_len = augmented_arg__read_str(arg2, newpath_arg, sizeof(augmented_args->arg.value)); @@ -423,8 +431,9 @@ static bool pid_filter__has(struct pids_filtered *pids, pid_t pid) static int augment_sys_enter(void *ctx, struct syscall_enter_args *args) { bool augmented, do_output = false; - int zero = 0, size, aug_size, index, output = 0, + int zero = 0, size, aug_size, index, value_size = sizeof(struct augmented_arg) - offsetof(struct augmented_arg, value); + u64 output = 0; /* has to be u64, otherwise it won't pass the verifier */ unsigned int nr, *beauty_map; struct beauty_payload_enter *payload; void *arg, *payload_offset; @@ -477,6 +486,8 @@ static int augment_sys_enter(void *ctx, struct syscall_enter_args *args) augmented = true; } else if (size < 0 && size >= -6) { /* buffer */ index = -(size + 1); + barrier_var(index); // Prevent clang (noticed with v18) from removing the &= 7 trick. + index &= 7; // Satisfy the bounds checking with the verifier in some kernels. aug_size = args->args[index]; if (aug_size > TRACE_AUG_MAX_BUF) @@ -488,10 +499,17 @@ static int augment_sys_enter(void *ctx, struct syscall_enter_args *args) } } + /* Augmented data size is limited to sizeof(augmented_arg->unnamed union with value field) */ + if (aug_size > value_size) + aug_size = value_size; + /* write data to payload */ if (augmented) { int written = offsetof(struct augmented_arg, value) + aug_size; + if (written < 0 || written > sizeof(struct augmented_arg)) + return 1; + ((struct augmented_arg *)payload_offset)->size = aug_size; output += written; payload_offset += written; @@ -499,7 +517,7 @@ static int augment_sys_enter(void *ctx, struct syscall_enter_args *args) } } - if (!do_output) + if (!do_output || (sizeof(struct syscall_enter_args) + output) > sizeof(struct beauty_payload_enter)) return 1; return augmented__beauty_output(ctx, payload, sizeof(struct syscall_enter_args) + output); diff --git a/tools/perf/util/cap.c b/tools/perf/util/cap.c index 7574a67651bc..69d9a2bcd40b 100644 --- a/tools/perf/util/cap.c +++ b/tools/perf/util/cap.c @@ -7,13 +7,9 @@ #include "debug.h" #include <errno.h> #include <string.h> -#include <unistd.h> #include <linux/capability.h> #include <sys/syscall.h> - -#ifndef SYS_capget -#define SYS_capget 90 -#endif +#include <unistd.h> #define MAX_LINUX_CAPABILITY_U32S _LINUX_CAPABILITY_U32S_3 @@ -21,9 +17,9 @@ bool perf_cap__capable(int cap, bool *used_root) { struct __user_cap_header_struct header = { .version = _LINUX_CAPABILITY_VERSION_3, - .pid = getpid(), + .pid = 0, }; - struct __user_cap_data_struct data[MAX_LINUX_CAPABILITY_U32S]; + struct __user_cap_data_struct data[MAX_LINUX_CAPABILITY_U32S] = {}; __u32 cap_val; *used_root = false; diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 31a223eaf8e6..ee3d43a7ba45 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -19,6 +19,7 @@ #include "util/bpf-filter.h" #include "util/env.h" #include "util/kvm-stat.h" +#include "util/stat.h" #include "util/kwork.h" #include "util/sample.h" #include "util/lock-contention.h" @@ -1355,6 +1356,7 @@ error: unsigned int scripting_max_stack = PERF_MAX_STACK_DEPTH; +#ifdef HAVE_KVM_STAT_SUPPORT bool kvm_entry_event(struct evsel *evsel __maybe_unused) { return false; @@ -1384,6 +1386,7 @@ void exit_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused, char *decode __maybe_unused) { } +#endif // HAVE_KVM_STAT_SUPPORT int find_scripts(char **scripts_array __maybe_unused, char **scripts_path_array __maybe_unused, int num __maybe_unused, int pathlen __maybe_unused) diff --git a/tools/perf/util/syscalltbl.c b/tools/perf/util/syscalltbl.c index 7c15dec6900d..6c45ded922b6 100644 --- a/tools/perf/util/syscalltbl.c +++ b/tools/perf/util/syscalltbl.c @@ -46,6 +46,11 @@ static const char *const *syscalltbl_native = syscalltbl_mips_n64; #include <asm/syscalls.c> const int syscalltbl_native_max_id = SYSCALLTBL_LOONGARCH_MAX_ID; static const char *const *syscalltbl_native = syscalltbl_loongarch; +#else +const int syscalltbl_native_max_id = 0; +static const char *const syscalltbl_native[] = { + [0] = "unknown", +}; #endif struct syscall { @@ -182,6 +187,11 @@ int syscalltbl__id(struct syscalltbl *tbl, const char *name) return audit_name_to_syscall(name, tbl->audit_machine); } +int syscalltbl__id_at_idx(struct syscalltbl *tbl __maybe_unused, int idx) +{ + return idx; +} + int syscalltbl__strglobmatch_next(struct syscalltbl *tbl __maybe_unused, const char *syscall_glob __maybe_unused, int *idx __maybe_unused) { diff --git a/tools/sched_ext/include/scx/common.bpf.h b/tools/sched_ext/include/scx/common.bpf.h index 27749c51c3ec..248ab790d143 100644 --- a/tools/sched_ext/include/scx/common.bpf.h +++ b/tools/sched_ext/include/scx/common.bpf.h @@ -320,7 +320,7 @@ u32 bpf_cpumask_weight(const struct cpumask *cpumask) __ksym; /* * Access a cpumask in read-only mode (typically to check bits). */ -const struct cpumask *cast_mask(struct bpf_cpumask *mask) +static __always_inline const struct cpumask *cast_mask(struct bpf_cpumask *mask) { return (const struct cpumask *)mask; } diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c index 90d5afd52dd0..050725afa45d 100644 --- a/tools/testing/cxl/test/cxl.c +++ b/tools/testing/cxl/test/cxl.c @@ -693,26 +693,22 @@ static int mock_decoder_commit(struct cxl_decoder *cxld) return 0; } -static int mock_decoder_reset(struct cxl_decoder *cxld) +static void mock_decoder_reset(struct cxl_decoder *cxld) { struct cxl_port *port = to_cxl_port(cxld->dev.parent); int id = cxld->id; if ((cxld->flags & CXL_DECODER_F_ENABLE) == 0) - return 0; + return; dev_dbg(&port->dev, "%s reset\n", dev_name(&cxld->dev)); - if (port->commit_end != id) { + if (port->commit_end == id) + cxl_port_commit_reap(cxld); + else dev_dbg(&port->dev, "%s: out of order reset, expected decoder%d.%d\n", dev_name(&cxld->dev), port->id, port->commit_end); - return -EBUSY; - } - - port->commit_end--; cxld->flags &= ~CXL_DECODER_F_ENABLE; - - return 0; } static void default_mock_decoder(struct cxl_decoder *cxld) @@ -1062,7 +1058,7 @@ static void mock_companion(struct acpi_device *adev, struct device *dev) #define SZ_64G (SZ_32G * 2) #endif -static __init int cxl_rch_init(void) +static __init int cxl_rch_topo_init(void) { int rc, i; @@ -1090,30 +1086,8 @@ static __init int cxl_rch_init(void) goto err_bridge; } - for (i = 0; i < ARRAY_SIZE(cxl_rcd); i++) { - int idx = NR_MEM_MULTI + NR_MEM_SINGLE + i; - struct platform_device *rch = cxl_rch[i]; - struct platform_device *pdev; - - pdev = platform_device_alloc("cxl_rcd", idx); - if (!pdev) - goto err_mem; - pdev->dev.parent = &rch->dev; - set_dev_node(&pdev->dev, i % 2); - - rc = platform_device_add(pdev); - if (rc) { - platform_device_put(pdev); - goto err_mem; - } - cxl_rcd[i] = pdev; - } - return 0; -err_mem: - for (i = ARRAY_SIZE(cxl_rcd) - 1; i >= 0; i--) - platform_device_unregister(cxl_rcd[i]); err_bridge: for (i = ARRAY_SIZE(cxl_rch) - 1; i >= 0; i--) { struct platform_device *pdev = cxl_rch[i]; @@ -1127,12 +1101,10 @@ err_bridge: return rc; } -static void cxl_rch_exit(void) +static void cxl_rch_topo_exit(void) { int i; - for (i = ARRAY_SIZE(cxl_rcd) - 1; i >= 0; i--) - platform_device_unregister(cxl_rcd[i]); for (i = ARRAY_SIZE(cxl_rch) - 1; i >= 0; i--) { struct platform_device *pdev = cxl_rch[i]; @@ -1143,7 +1115,7 @@ static void cxl_rch_exit(void) } } -static __init int cxl_single_init(void) +static __init int cxl_single_topo_init(void) { int i, rc; @@ -1228,29 +1200,8 @@ static __init int cxl_single_init(void) cxl_swd_single[i] = pdev; } - for (i = 0; i < ARRAY_SIZE(cxl_mem_single); i++) { - struct platform_device *dport = cxl_swd_single[i]; - struct platform_device *pdev; - - pdev = platform_device_alloc("cxl_mem", NR_MEM_MULTI + i); - if (!pdev) - goto err_mem; - pdev->dev.parent = &dport->dev; - set_dev_node(&pdev->dev, i % 2); - - rc = platform_device_add(pdev); - if (rc) { - platform_device_put(pdev); - goto err_mem; - } - cxl_mem_single[i] = pdev; - } - return 0; -err_mem: - for (i = ARRAY_SIZE(cxl_mem_single) - 1; i >= 0; i--) - platform_device_unregister(cxl_mem_single[i]); err_dport: for (i = ARRAY_SIZE(cxl_swd_single) - 1; i >= 0; i--) platform_device_unregister(cxl_swd_single[i]); @@ -1273,12 +1224,10 @@ err_bridge: return rc; } -static void cxl_single_exit(void) +static void cxl_single_topo_exit(void) { int i; - for (i = ARRAY_SIZE(cxl_mem_single) - 1; i >= 0; i--) - platform_device_unregister(cxl_mem_single[i]); for (i = ARRAY_SIZE(cxl_swd_single) - 1; i >= 0; i--) platform_device_unregister(cxl_swd_single[i]); for (i = ARRAY_SIZE(cxl_swu_single) - 1; i >= 0; i--) @@ -1295,6 +1244,91 @@ static void cxl_single_exit(void) } } +static void cxl_mem_exit(void) +{ + int i; + + for (i = ARRAY_SIZE(cxl_rcd) - 1; i >= 0; i--) + platform_device_unregister(cxl_rcd[i]); + for (i = ARRAY_SIZE(cxl_mem_single) - 1; i >= 0; i--) + platform_device_unregister(cxl_mem_single[i]); + for (i = ARRAY_SIZE(cxl_mem) - 1; i >= 0; i--) + platform_device_unregister(cxl_mem[i]); +} + +static int cxl_mem_init(void) +{ + int i, rc; + + for (i = 0; i < ARRAY_SIZE(cxl_mem); i++) { + struct platform_device *dport = cxl_switch_dport[i]; + struct platform_device *pdev; + + pdev = platform_device_alloc("cxl_mem", i); + if (!pdev) + goto err_mem; + pdev->dev.parent = &dport->dev; + set_dev_node(&pdev->dev, i % 2); + + rc = platform_device_add(pdev); + if (rc) { + platform_device_put(pdev); + goto err_mem; + } + cxl_mem[i] = pdev; + } + + for (i = 0; i < ARRAY_SIZE(cxl_mem_single); i++) { + struct platform_device *dport = cxl_swd_single[i]; + struct platform_device *pdev; + + pdev = platform_device_alloc("cxl_mem", NR_MEM_MULTI + i); + if (!pdev) + goto err_single; + pdev->dev.parent = &dport->dev; + set_dev_node(&pdev->dev, i % 2); + + rc = platform_device_add(pdev); + if (rc) { + platform_device_put(pdev); + goto err_single; + } + cxl_mem_single[i] = pdev; + } + + for (i = 0; i < ARRAY_SIZE(cxl_rcd); i++) { + int idx = NR_MEM_MULTI + NR_MEM_SINGLE + i; + struct platform_device *rch = cxl_rch[i]; + struct platform_device *pdev; + + pdev = platform_device_alloc("cxl_rcd", idx); + if (!pdev) + goto err_rcd; + pdev->dev.parent = &rch->dev; + set_dev_node(&pdev->dev, i % 2); + + rc = platform_device_add(pdev); + if (rc) { + platform_device_put(pdev); + goto err_rcd; + } + cxl_rcd[i] = pdev; + } + + return 0; + +err_rcd: + for (i = ARRAY_SIZE(cxl_rcd) - 1; i >= 0; i--) + platform_device_unregister(cxl_rcd[i]); +err_single: + for (i = ARRAY_SIZE(cxl_mem_single) - 1; i >= 0; i--) + platform_device_unregister(cxl_mem_single[i]); +err_mem: + for (i = ARRAY_SIZE(cxl_mem) - 1; i >= 0; i--) + platform_device_unregister(cxl_mem[i]); + return rc; +} + static __init int cxl_test_init(void) { int rc, i; @@ -1407,29 +1441,11 @@ static __init int cxl_test_init(void) cxl_switch_dport[i] = pdev; } - for (i = 0; i < ARRAY_SIZE(cxl_mem); i++) { - struct platform_device *dport = cxl_switch_dport[i]; - struct platform_device *pdev; - - pdev = platform_device_alloc("cxl_mem", i); - if (!pdev) - goto err_mem; - pdev->dev.parent = &dport->dev; - set_dev_node(&pdev->dev, i % 2); - - rc = platform_device_add(pdev); - if (rc) { - platform_device_put(pdev); - goto err_mem; - } - cxl_mem[i] = pdev; - } - - rc = cxl_single_init(); + rc = cxl_single_topo_init(); if (rc) - goto err_mem; + goto err_dport; - rc = cxl_rch_init(); + rc = cxl_rch_topo_init(); if (rc) goto err_single; @@ -1442,19 +1458,20 @@ static __init int cxl_test_init(void) rc = platform_device_add(cxl_acpi); if (rc) - goto err_add; + goto err_root; + + rc = cxl_mem_init(); + if (rc) + goto err_root; return 0; -err_add: +err_root: platform_device_put(cxl_acpi); err_rch: - cxl_rch_exit(); + cxl_rch_topo_exit(); err_single: - cxl_single_exit(); -err_mem: - for (i = ARRAY_SIZE(cxl_mem) - 1; i >= 0; i--) - platform_device_unregister(cxl_mem[i]); + cxl_single_topo_exit(); err_dport: for (i = ARRAY_SIZE(cxl_switch_dport) - 1; i >= 0; i--) platform_device_unregister(cxl_switch_dport[i]); @@ -1486,11 +1503,10 @@ static __exit void cxl_test_exit(void) { int i; + cxl_mem_exit(); platform_device_unregister(cxl_acpi); - cxl_rch_exit(); - cxl_single_exit(); - for (i = ARRAY_SIZE(cxl_mem) - 1; i >= 0; i--) - platform_device_unregister(cxl_mem[i]); + cxl_rch_topo_exit(); + cxl_single_topo_exit(); for (i = ARRAY_SIZE(cxl_switch_dport) - 1; i >= 0; i--) platform_device_unregister(cxl_switch_dport[i]); for (i = ARRAY_SIZE(cxl_switch_uport) - 1; i >= 0; i--) diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c index ad5c4c18c5c6..71916e0e1546 100644 --- a/tools/testing/cxl/test/mem.c +++ b/tools/testing/cxl/test/mem.c @@ -1673,6 +1673,7 @@ static struct platform_driver cxl_mock_mem_driver = { .name = KBUILD_MODNAME, .dev_groups = cxl_mock_mem_groups, .groups = cxl_mock_mem_core_groups, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, }; diff --git a/tools/testing/selftests/bpf/map_tests/lpm_trie_map_get_next_key.c b/tools/testing/selftests/bpf/map_tests/lpm_trie_map_get_next_key.c new file mode 100644 index 000000000000..0ba015686492 --- /dev/null +++ b/tools/testing/selftests/bpf/map_tests/lpm_trie_map_get_next_key.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define _GNU_SOURCE +#include <linux/bpf.h> +#include <stdio.h> +#include <stdbool.h> +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <pthread.h> + +#include <bpf/bpf.h> +#include <bpf/libbpf.h> + +#include <test_maps.h> + +struct test_lpm_key { + __u32 prefix; + __u32 data; +}; + +struct get_next_key_ctx { + struct test_lpm_key key; + bool start; + bool stop; + int map_fd; + int loop; +}; + +static void *get_next_key_fn(void *arg) +{ + struct get_next_key_ctx *ctx = arg; + struct test_lpm_key next_key; + int i = 0; + + while (!ctx->start) + usleep(1); + + while (!ctx->stop && i++ < ctx->loop) + bpf_map_get_next_key(ctx->map_fd, &ctx->key, &next_key); + + return NULL; +} + +static void abort_get_next_key(struct get_next_key_ctx *ctx, pthread_t *tids, + unsigned int nr) +{ + unsigned int i; + + ctx->stop = true; + ctx->start = true; + for (i = 0; i < nr; i++) + pthread_join(tids[i], NULL); +} + +/* This test aims to prevent regression of future. As long as the kernel does + * not panic, it is considered as success. + */ +void test_lpm_trie_map_get_next_key(void) +{ +#define MAX_NR_THREADS 8 + LIBBPF_OPTS(bpf_map_create_opts, create_opts, + .map_flags = BPF_F_NO_PREALLOC); + struct test_lpm_key key = {}; + __u32 val = 0; + int map_fd; + const __u32 max_prefixlen = 8 * (sizeof(key) - sizeof(key.prefix)); + const __u32 max_entries = max_prefixlen + 1; + unsigned int i, nr = MAX_NR_THREADS, loop = 65536; + pthread_t tids[MAX_NR_THREADS]; + struct get_next_key_ctx ctx; + int err; + + map_fd = bpf_map_create(BPF_MAP_TYPE_LPM_TRIE, "lpm_trie_map", + sizeof(struct test_lpm_key), sizeof(__u32), + max_entries, &create_opts); + CHECK(map_fd == -1, "bpf_map_create()", "error:%s\n", + strerror(errno)); + + for (i = 0; i <= max_prefixlen; i++) { + key.prefix = i; + err = bpf_map_update_elem(map_fd, &key, &val, BPF_ANY); + CHECK(err, "bpf_map_update_elem()", "error:%s\n", + strerror(errno)); + } + + ctx.start = false; + ctx.stop = false; + ctx.map_fd = map_fd; + ctx.loop = loop; + memcpy(&ctx.key, &key, sizeof(key)); + + for (i = 0; i < nr; i++) { + err = pthread_create(&tids[i], NULL, get_next_key_fn, &ctx); + if (err) { + abort_get_next_key(&ctx, tids, i); + CHECK(err, "pthread_create", "error %d\n", err); + } + } + + ctx.start = true; + for (i = 0; i < nr; i++) + pthread_join(tids[i], NULL); + + printf("%s:PASS\n", __func__); + + close(map_fd); +} diff --git a/tools/testing/selftests/bpf/progs/verifier_bits_iter.c b/tools/testing/selftests/bpf/progs/verifier_bits_iter.c index f4da4d508ddb..156cc278e2fc 100644 --- a/tools/testing/selftests/bpf/progs/verifier_bits_iter.c +++ b/tools/testing/selftests/bpf/progs/verifier_bits_iter.c @@ -15,6 +15,8 @@ int bpf_iter_bits_new(struct bpf_iter_bits *it, const u64 *unsafe_ptr__ign, int *bpf_iter_bits_next(struct bpf_iter_bits *it) __ksym __weak; void bpf_iter_bits_destroy(struct bpf_iter_bits *it) __ksym __weak; +u64 bits_array[511] = {}; + SEC("iter.s/cgroup") __description("bits iter without destroy") __failure __msg("Unreleased reference") @@ -110,16 +112,16 @@ int bit_index(void) } SEC("syscall") -__description("bits nomem") +__description("bits too big") __success __retval(0) -int bits_nomem(void) +int bits_too_big(void) { u64 data[4]; int nr = 0; int *bit; __builtin_memset(&data, 0xff, sizeof(data)); - bpf_for_each(bits, bit, &data[0], 513) /* Be greater than 512 */ + bpf_for_each(bits, bit, &data[0], 512) /* Be greater than 511 */ nr++; return nr; } @@ -151,3 +153,56 @@ int zero_words(void) nr++; return nr; } + +SEC("syscall") +__description("huge words") +__success __retval(0) +int huge_words(void) +{ + u64 data[8] = {0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1}; + int nr = 0; + int *bit; + + bpf_for_each(bits, bit, &data[0], 67108865) + nr++; + return nr; +} + +SEC("syscall") +__description("max words") +__success __retval(4) +int max_words(void) +{ + volatile int nr = 0; + int *bit; + + bits_array[0] = (1ULL << 63) | 1U; + bits_array[510] = (1ULL << 33) | (1ULL << 32); + + bpf_for_each(bits, bit, bits_array, 511) { + if (nr == 0 && *bit != 0) + break; + if (nr == 2 && *bit != 32672) + break; + nr++; + } + return nr; +} + +SEC("syscall") +__description("bad words") +__success __retval(0) +int bad_words(void) +{ + void *bad_addr = (void *)(3UL << 30); + int nr = 0; + int *bit; + + bpf_for_each(bits, bit, bad_addr, 1) + nr++; + + bpf_for_each(bits, bit, bad_addr, 4) + nr++; + + return nr; +} diff --git a/tools/testing/selftests/bpf/progs/verifier_bpf_fastcall.c b/tools/testing/selftests/bpf/progs/verifier_bpf_fastcall.c index 9da97d2efcd9..5094c288cfd7 100644 --- a/tools/testing/selftests/bpf/progs/verifier_bpf_fastcall.c +++ b/tools/testing/selftests/bpf/progs/verifier_bpf_fastcall.c @@ -790,61 +790,6 @@ __naked static void cumulative_stack_depth_subprog(void) :: __imm(bpf_get_smp_processor_id) : __clobber_all); } -SEC("raw_tp") -__arch_x86_64 -__log_level(4) -__msg("stack depth 512") -__xlated("0: r1 = 42") -__xlated("1: *(u64 *)(r10 -512) = r1") -__xlated("2: w0 = ") -__xlated("3: r0 = &(void __percpu *)(r0)") -__xlated("4: r0 = *(u32 *)(r0 +0)") -__xlated("5: exit") -__success -__naked int bpf_fastcall_max_stack_ok(void) -{ - asm volatile( - "r1 = 42;" - "*(u64 *)(r10 - %[max_bpf_stack]) = r1;" - "*(u64 *)(r10 - %[max_bpf_stack_8]) = r1;" - "call %[bpf_get_smp_processor_id];" - "r1 = *(u64 *)(r10 - %[max_bpf_stack_8]);" - "exit;" - : - : __imm_const(max_bpf_stack, MAX_BPF_STACK), - __imm_const(max_bpf_stack_8, MAX_BPF_STACK + 8), - __imm(bpf_get_smp_processor_id) - : __clobber_all - ); -} - -SEC("raw_tp") -__arch_x86_64 -__log_level(4) -__msg("stack depth 520") -__failure -__naked int bpf_fastcall_max_stack_fail(void) -{ - asm volatile( - "r1 = 42;" - "*(u64 *)(r10 - %[max_bpf_stack]) = r1;" - "*(u64 *)(r10 - %[max_bpf_stack_8]) = r1;" - "call %[bpf_get_smp_processor_id];" - "r1 = *(u64 *)(r10 - %[max_bpf_stack_8]);" - /* call to prandom blocks bpf_fastcall rewrite */ - "*(u64 *)(r10 - %[max_bpf_stack_8]) = r1;" - "call %[bpf_get_prandom_u32];" - "r1 = *(u64 *)(r10 - %[max_bpf_stack_8]);" - "exit;" - : - : __imm_const(max_bpf_stack, MAX_BPF_STACK), - __imm_const(max_bpf_stack_8, MAX_BPF_STACK + 8), - __imm(bpf_get_smp_processor_id), - __imm(bpf_get_prandom_u32) - : __clobber_all - ); -} - SEC("cgroup/getsockname_unix") __xlated("0: r2 = 1") /* bpf_cast_to_kern_ctx is replaced by a single assignment */ diff --git a/tools/testing/selftests/bpf/progs/verifier_search_pruning.c b/tools/testing/selftests/bpf/progs/verifier_search_pruning.c index 5a14498d352f..f40e57251e94 100644 --- a/tools/testing/selftests/bpf/progs/verifier_search_pruning.c +++ b/tools/testing/selftests/bpf/progs/verifier_search_pruning.c @@ -2,6 +2,7 @@ /* Converted from tools/testing/selftests/bpf/verifier/search_pruning.c */ #include <linux/bpf.h> +#include <../../../include/linux/filter.h> #include <bpf/bpf_helpers.h> #include "bpf_misc.h" @@ -336,4 +337,26 @@ l0_%=: r1 = 42; \ : __clobber_all); } +/* Without checkpoint forcibly inserted at the back-edge a loop this + * test would take a very long time to verify. + */ +SEC("kprobe") +__failure __log_level(4) +__msg("BPF program is too large.") +__naked void short_loop1(void) +{ + asm volatile ( + " r7 = *(u16 *)(r1 +0);" + "1: r7 += 0x1ab064b9;" + " .8byte %[jset];" /* same as 'if r7 & 0x702000 goto 1b;' */ + " r7 &= 0x1ee60e;" + " r7 += r1;" + " if r7 s> 0x37d2 goto +0;" + " r0 = 0;" + " exit;" + : + : __imm_insn(jset, BPF_JMP_IMM(BPF_JSET, BPF_REG_7, 0x702000, -2)) + : __clobber_all); +} + char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/veristat.cfg b/tools/testing/selftests/bpf/veristat.cfg index 1a385061618d..e661ffdcaadf 100644 --- a/tools/testing/selftests/bpf/veristat.cfg +++ b/tools/testing/selftests/bpf/veristat.cfg @@ -15,3 +15,4 @@ test_usdt* test_verif_scale* test_xdp_noinline* xdp_synproxy* +verifier_search_pruning* diff --git a/tools/testing/selftests/intel_pstate/run.sh b/tools/testing/selftests/intel_pstate/run.sh index e7008f614ad7..6a3b8503264e 100755 --- a/tools/testing/selftests/intel_pstate/run.sh +++ b/tools/testing/selftests/intel_pstate/run.sh @@ -44,6 +44,11 @@ if [ $UID != 0 ] && [ $EVALUATE_ONLY == 0 ]; then exit $ksft_skip fi +if ! command -v cpupower &> /dev/null; then + echo $msg cpupower could not be found, please install it >&2 + exit $ksft_skip +fi + max_cpus=$(($(nproc)-1)) function run_test () { @@ -87,9 +92,9 @@ mkt_freq=${_mkt_freq}0 # Get the ranges from cpupower _min_freq=$(cpupower frequency-info -l | tail -1 | awk ' { print $1 } ') -min_freq=$(($_min_freq / 1000)) +min_freq=$((_min_freq / 1000)) _max_freq=$(cpupower frequency-info -l | tail -1 | awk ' { print $2 } ') -max_freq=$(($_max_freq / 1000)) +max_freq=$((_max_freq / 1000)) [ $EVALUATE_ONLY -eq 0 ] && for freq in `seq $max_freq -100 $min_freq` diff --git a/tools/testing/selftests/mm/uffd-common.c b/tools/testing/selftests/mm/uffd-common.c index 852e7281026e..717539eddf98 100644 --- a/tools/testing/selftests/mm/uffd-common.c +++ b/tools/testing/selftests/mm/uffd-common.c @@ -18,7 +18,7 @@ bool test_uffdio_wp = true; unsigned long long *count_verify; uffd_test_ops_t *uffd_test_ops; uffd_test_case_ops_t *uffd_test_case_ops; -pthread_barrier_t ready_for_fork; +atomic_bool ready_for_fork; static int uffd_mem_fd_create(off_t mem_size, bool hugetlb) { @@ -519,8 +519,7 @@ void *uffd_poll_thread(void *arg) pollfd[1].fd = pipefd[cpu*2]; pollfd[1].events = POLLIN; - /* Ready for parent thread to fork */ - pthread_barrier_wait(&ready_for_fork); + ready_for_fork = true; for (;;) { ret = poll(pollfd, 2, -1); diff --git a/tools/testing/selftests/mm/uffd-common.h b/tools/testing/selftests/mm/uffd-common.h index 3e6228d8e0dc..a70ae10b5f62 100644 --- a/tools/testing/selftests/mm/uffd-common.h +++ b/tools/testing/selftests/mm/uffd-common.h @@ -33,6 +33,7 @@ #include <inttypes.h> #include <stdint.h> #include <sys/random.h> +#include <stdatomic.h> #include "../kselftest.h" #include "vm_util.h" @@ -104,7 +105,7 @@ extern bool map_shared; extern bool test_uffdio_wp; extern unsigned long long *count_verify; extern volatile bool test_uffdio_copy_eexist; -extern pthread_barrier_t ready_for_fork; +extern atomic_bool ready_for_fork; extern uffd_test_ops_t anon_uffd_test_ops; extern uffd_test_ops_t shmem_uffd_test_ops; diff --git a/tools/testing/selftests/mm/uffd-unit-tests.c b/tools/testing/selftests/mm/uffd-unit-tests.c index c8a3b1c7edff..a2e71b1636e7 100644 --- a/tools/testing/selftests/mm/uffd-unit-tests.c +++ b/tools/testing/selftests/mm/uffd-unit-tests.c @@ -241,8 +241,7 @@ static void *fork_event_consumer(void *data) fork_event_args *args = data; struct uffd_msg msg = { 0 }; - /* Ready for parent thread to fork */ - pthread_barrier_wait(&ready_for_fork); + ready_for_fork = true; /* Read until a full msg received */ while (uffd_read_msg(args->parent_uffd, &msg)); @@ -311,12 +310,11 @@ static int pagemap_test_fork(int uffd, bool with_event, bool test_pin) /* Prepare a thread to resolve EVENT_FORK */ if (with_event) { - pthread_barrier_init(&ready_for_fork, NULL, 2); + ready_for_fork = false; if (pthread_create(&thread, NULL, fork_event_consumer, &args)) err("pthread_create()"); - /* Wait for child thread to start before forking */ - pthread_barrier_wait(&ready_for_fork); - pthread_barrier_destroy(&ready_for_fork); + while (!ready_for_fork) + ; /* Wait for the poll_thread to start executing before forking */ } child = fork(); @@ -781,7 +779,7 @@ static void uffd_sigbus_test_common(bool wp) char c; struct uffd_args args = { 0 }; - pthread_barrier_init(&ready_for_fork, NULL, 2); + ready_for_fork = false; fcntl(uffd, F_SETFL, uffd_flags | O_NONBLOCK); @@ -798,9 +796,8 @@ static void uffd_sigbus_test_common(bool wp) if (pthread_create(&uffd_mon, NULL, uffd_poll_thread, &args)) err("uffd_poll_thread create"); - /* Wait for child thread to start before forking */ - pthread_barrier_wait(&ready_for_fork); - pthread_barrier_destroy(&ready_for_fork); + while (!ready_for_fork) + ; /* Wait for the poll_thread to start executing before forking */ pid = fork(); if (pid < 0) @@ -841,7 +838,7 @@ static void uffd_events_test_common(bool wp) char c; struct uffd_args args = { 0 }; - pthread_barrier_init(&ready_for_fork, NULL, 2); + ready_for_fork = false; fcntl(uffd, F_SETFL, uffd_flags | O_NONBLOCK); if (uffd_register(uffd, area_dst, nr_pages * page_size, @@ -852,9 +849,8 @@ static void uffd_events_test_common(bool wp) if (pthread_create(&uffd_mon, NULL, uffd_poll_thread, &args)) err("uffd_poll_thread create"); - /* Wait for child thread to start before forking */ - pthread_barrier_wait(&ready_for_fork); - pthread_barrier_destroy(&ready_for_fork); + while (!ready_for_fork) + ; /* Wait for the poll_thread to start executing before forking */ pid = fork(); if (pid < 0) diff --git a/tools/testing/selftests/mount_setattr/mount_setattr_test.c b/tools/testing/selftests/mount_setattr/mount_setattr_test.c index c6a8c732b802..68801e1a9ec2 100644 --- a/tools/testing/selftests/mount_setattr/mount_setattr_test.c +++ b/tools/testing/selftests/mount_setattr/mount_setattr_test.c @@ -1414,6 +1414,13 @@ TEST_F(mount_setattr_idmapped, idmap_mount_tree_invalid) ASSERT_EQ(expected_uid_gid(-EBADF, "/tmp/B/b", 0, 0, 0), 0); ASSERT_EQ(expected_uid_gid(-EBADF, "/tmp/B/BB/b", 0, 0, 0), 0); + ASSERT_EQ(mount("testing", "/mnt/A", "ramfs", MS_NOATIME | MS_NODEV, + "size=100000,mode=700"), 0); + + ASSERT_EQ(mkdir("/mnt/A/AA", 0777), 0); + + ASSERT_EQ(mount("/tmp", "/mnt/A/AA", NULL, MS_BIND | MS_REC, NULL), 0); + open_tree_fd = sys_open_tree(-EBADF, "/mnt/A", AT_RECURSIVE | AT_EMPTY_PATH | @@ -1433,6 +1440,8 @@ TEST_F(mount_setattr_idmapped, idmap_mount_tree_invalid) ASSERT_EQ(expected_uid_gid(-EBADF, "/tmp/B/BB/b", 0, 0, 0), 0); ASSERT_EQ(expected_uid_gid(open_tree_fd, "B/b", 0, 0, 0), 0); ASSERT_EQ(expected_uid_gid(open_tree_fd, "B/BB/b", 0, 0, 0), 0); + + (void)umount2("/mnt/A", MNT_DETACH); } TEST_F(mount_setattr, mount_attr_nosymfollow) diff --git a/tools/testing/selftests/net/forwarding/ip6gre_flat.sh b/tools/testing/selftests/net/forwarding/ip6gre_flat.sh index 96c97064f2d3..becc7c3fc809 100755 --- a/tools/testing/selftests/net/forwarding/ip6gre_flat.sh +++ b/tools/testing/selftests/net/forwarding/ip6gre_flat.sh @@ -8,6 +8,7 @@ ALL_TESTS=" gre_flat gre_mtu_change + gre_flat_remote_change " NUM_NETIFS=6 @@ -44,6 +45,19 @@ gre_mtu_change() test_mtu_change } +gre_flat_remote_change() +{ + flat_remote_change + + test_traffic_ip4ip6 "GRE flat IPv4-in-IPv6 (new remote)" + test_traffic_ip6ip6 "GRE flat IPv6-in-IPv6 (new remote)" + + flat_remote_restore + + test_traffic_ip4ip6 "GRE flat IPv4-in-IPv6 (old remote)" + test_traffic_ip6ip6 "GRE flat IPv6-in-IPv6 (old remote)" +} + cleanup() { pre_cleanup diff --git a/tools/testing/selftests/net/forwarding/ip6gre_flat_key.sh b/tools/testing/selftests/net/forwarding/ip6gre_flat_key.sh index ff9fb0db9bd1..e5335116a2fd 100755 --- a/tools/testing/selftests/net/forwarding/ip6gre_flat_key.sh +++ b/tools/testing/selftests/net/forwarding/ip6gre_flat_key.sh @@ -8,6 +8,7 @@ ALL_TESTS=" gre_flat gre_mtu_change + gre_flat_remote_change " NUM_NETIFS=6 @@ -44,6 +45,19 @@ gre_mtu_change() test_mtu_change } +gre_flat_remote_change() +{ + flat_remote_change + + test_traffic_ip4ip6 "GRE flat IPv4-in-IPv6 with key (new remote)" + test_traffic_ip6ip6 "GRE flat IPv6-in-IPv6 with key (new remote)" + + flat_remote_restore + + test_traffic_ip4ip6 "GRE flat IPv4-in-IPv6 with key (old remote)" + test_traffic_ip6ip6 "GRE flat IPv6-in-IPv6 with key (old remote)" +} + cleanup() { pre_cleanup diff --git a/tools/testing/selftests/net/forwarding/ip6gre_flat_keys.sh b/tools/testing/selftests/net/forwarding/ip6gre_flat_keys.sh index 12c138785242..7e0cbfdefab0 100755 --- a/tools/testing/selftests/net/forwarding/ip6gre_flat_keys.sh +++ b/tools/testing/selftests/net/forwarding/ip6gre_flat_keys.sh @@ -8,6 +8,7 @@ ALL_TESTS=" gre_flat gre_mtu_change + gre_flat_remote_change " NUM_NETIFS=6 @@ -44,6 +45,19 @@ gre_mtu_change() test_mtu_change gre } +gre_flat_remote_change() +{ + flat_remote_change + + test_traffic_ip4ip6 "GRE flat IPv4-in-IPv6 with ikey/okey (new remote)" + test_traffic_ip6ip6 "GRE flat IPv6-in-IPv6 with ikey/okey (new remote)" + + flat_remote_restore + + test_traffic_ip4ip6 "GRE flat IPv4-in-IPv6 with ikey/okey (old remote)" + test_traffic_ip6ip6 "GRE flat IPv6-in-IPv6 with ikey/okey (old remote)" +} + cleanup() { pre_cleanup diff --git a/tools/testing/selftests/net/forwarding/ip6gre_hier.sh b/tools/testing/selftests/net/forwarding/ip6gre_hier.sh index 83b55c30a5c3..e0844495f3d1 100755 --- a/tools/testing/selftests/net/forwarding/ip6gre_hier.sh +++ b/tools/testing/selftests/net/forwarding/ip6gre_hier.sh @@ -8,6 +8,7 @@ ALL_TESTS=" gre_hier gre_mtu_change + gre_hier_remote_change " NUM_NETIFS=6 @@ -44,6 +45,19 @@ gre_mtu_change() test_mtu_change gre } +gre_hier_remote_change() +{ + hier_remote_change + + test_traffic_ip4ip6 "GRE hierarchical IPv4-in-IPv6 (new remote)" + test_traffic_ip6ip6 "GRE hierarchical IPv6-in-IPv6 (new remote)" + + hier_remote_restore + + test_traffic_ip4ip6 "GRE hierarchical IPv4-in-IPv6 (old remote)" + test_traffic_ip6ip6 "GRE hierarchical IPv6-in-IPv6 (old remote)" +} + cleanup() { pre_cleanup diff --git a/tools/testing/selftests/net/forwarding/ip6gre_hier_key.sh b/tools/testing/selftests/net/forwarding/ip6gre_hier_key.sh index 256607916d92..741bc9c928eb 100755 --- a/tools/testing/selftests/net/forwarding/ip6gre_hier_key.sh +++ b/tools/testing/selftests/net/forwarding/ip6gre_hier_key.sh @@ -8,6 +8,7 @@ ALL_TESTS=" gre_hier gre_mtu_change + gre_hier_remote_change " NUM_NETIFS=6 @@ -44,6 +45,19 @@ gre_mtu_change() test_mtu_change gre } +gre_hier_remote_change() +{ + hier_remote_change + + test_traffic_ip4ip6 "GRE hierarchical IPv4-in-IPv6 with key (new remote)" + test_traffic_ip6ip6 "GRE hierarchical IPv6-in-IPv6 with key (new remote)" + + hier_remote_restore + + test_traffic_ip4ip6 "GRE hierarchical IPv4-in-IPv6 with key (old remote)" + test_traffic_ip6ip6 "GRE hierarchical IPv6-in-IPv6 with key (old remote)" +} + cleanup() { pre_cleanup diff --git a/tools/testing/selftests/net/forwarding/ip6gre_hier_keys.sh b/tools/testing/selftests/net/forwarding/ip6gre_hier_keys.sh index ad1bcd6334a8..ad9eab4b1367 100755 --- a/tools/testing/selftests/net/forwarding/ip6gre_hier_keys.sh +++ b/tools/testing/selftests/net/forwarding/ip6gre_hier_keys.sh @@ -8,6 +8,7 @@ ALL_TESTS=" gre_hier gre_mtu_change + gre_hier_remote_change " NUM_NETIFS=6 @@ -44,6 +45,19 @@ gre_mtu_change() test_mtu_change gre } +gre_hier_remote_change() +{ + hier_remote_change + + test_traffic_ip4ip6 "GRE hierarchical IPv4-in-IPv6 with ikey/okey (new remote)" + test_traffic_ip6ip6 "GRE hierarchical IPv6-in-IPv6 with ikey/okey (new remote)" + + hier_remote_restore + + test_traffic_ip4ip6 "GRE hierarchical IPv4-in-IPv6 with ikey/okey (old remote)" + test_traffic_ip6ip6 "GRE hierarchical IPv6-in-IPv6 with ikey/okey (old remote)" +} + cleanup() { pre_cleanup diff --git a/tools/testing/selftests/net/forwarding/ip6gre_lib.sh b/tools/testing/selftests/net/forwarding/ip6gre_lib.sh index 24f4ab328bd2..2d91281dc5b7 100644 --- a/tools/testing/selftests/net/forwarding/ip6gre_lib.sh +++ b/tools/testing/selftests/net/forwarding/ip6gre_lib.sh @@ -436,3 +436,83 @@ test_mtu_change() check_err $? log_test "ping GRE IPv6, packet size 1800 after MTU change" } + +topo_flat_remote_change() +{ + local old1=$1; shift + local new1=$1; shift + local old2=$1; shift + local new2=$1; shift + + ip link set dev g1a type ip6gre local $new1 remote $new2 + __addr_add_del g1a add "$new1/128" + __addr_add_del g1a del "$old1/128" + ip -6 route add $new2/128 via 2001:db8:10::2 + ip -6 route del $old2/128 + + ip link set dev g2a type ip6gre local $new2 remote $new1 + __addr_add_del g2a add "$new2/128" + __addr_add_del g2a del "$old2/128" + ip -6 route add vrf v$ol2 $new1/128 via 2001:db8:10::1 + ip -6 route del vrf v$ol2 $old1/128 +} + +flat_remote_change() +{ + local old1=2001:db8:3::1 + local new1=2001:db8:3::10 + local old2=2001:db8:3::2 + local new2=2001:db8:3::20 + + topo_flat_remote_change $old1 $new1 $old2 $new2 +} + +flat_remote_restore() +{ + local old1=2001:db8:3::10 + local new1=2001:db8:3::1 + local old2=2001:db8:3::20 + local new2=2001:db8:3::2 + + topo_flat_remote_change $old1 $new1 $old2 $new2 +} + +topo_hier_remote_change() +{ + local old1=$1; shift + local new1=$1; shift + local old2=$1; shift + local new2=$1; shift + + __addr_add_del dummy1 del "$old1/64" + __addr_add_del dummy1 add "$new1/64" + ip link set dev g1a type ip6gre local $new1 remote $new2 + ip -6 route add vrf v$ul1 $new2/128 via 2001:db8:10::2 + ip -6 route del vrf v$ul1 $old2/128 + + __addr_add_del dummy2 del "$old2/64" + __addr_add_del dummy2 add "$new2/64" + ip link set dev g2a type ip6gre local $new2 remote $new1 + ip -6 route add vrf v$ul2 $new1/128 via 2001:db8:10::1 + ip -6 route del vrf v$ul2 $old1/128 +} + +hier_remote_change() +{ + local old1=2001:db8:3::1 + local new1=2001:db8:3::10 + local old2=2001:db8:3::2 + local new2=2001:db8:3::20 + + topo_hier_remote_change $old1 $new1 $old2 $new2 +} + +hier_remote_restore() +{ + local old1=2001:db8:3::10 + local new1=2001:db8:3::1 + local old2=2001:db8:3::20 + local new2=2001:db8:3::2 + + topo_hier_remote_change $old1 $new1 $old2 $new2 +} diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.sh b/tools/testing/selftests/net/mptcp/mptcp_connect.sh index 57325d57e4c6..b48b4e56826a 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_connect.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_connect.sh @@ -259,6 +259,15 @@ check_mptcp_disabled() mptcp_lib_ns_init disabled_ns print_larger_title "New MPTCP socket can be blocked via sysctl" + + # mainly to cover more code + if ! ip netns exec ${disabled_ns} sysctl net.mptcp >/dev/null; then + mptcp_lib_pr_fail "not able to list net.mptcp sysctl knobs" + mptcp_lib_result_fail "not able to list net.mptcp sysctl knobs" + ret=${KSFT_FAIL} + return 1 + fi + # net.mptcp.enabled should be enabled by default if [ "$(ip netns exec ${disabled_ns} sysctl net.mptcp.enabled | awk '{ print $3 }')" -ne 1 ]; then mptcp_lib_pr_fail "net.mptcp.enabled sysctl is not 1 by default" diff --git a/tools/testing/selftests/net/netfilter/conntrack_dump_flush.c b/tools/testing/selftests/net/netfilter/conntrack_dump_flush.c index dc056fec993b..254ff03297f0 100644 --- a/tools/testing/selftests/net/netfilter/conntrack_dump_flush.c +++ b/tools/testing/selftests/net/netfilter/conntrack_dump_flush.c @@ -98,7 +98,7 @@ static int conntrack_data_insert(struct mnl_socket *sock, struct nlmsghdr *nlh, char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *rplnlh; unsigned int portid; - int err, ret; + int ret; portid = mnl_socket_get_portid(sock); @@ -217,7 +217,7 @@ static int conntracK_count_zone(struct mnl_socket *sock, uint16_t zone) struct nfgenmsg *nfh; struct nlattr *nest; unsigned int portid; - int err, ret; + int ret; portid = mnl_socket_get_portid(sock); @@ -264,7 +264,7 @@ static int conntrack_flush_zone(struct mnl_socket *sock, uint16_t zone) struct nfgenmsg *nfh; struct nlattr *nest; unsigned int portid; - int err, ret; + int ret; portid = mnl_socket_get_portid(sock); diff --git a/tools/testing/selftests/net/netfilter/nft_flowtable.sh b/tools/testing/selftests/net/netfilter/nft_flowtable.sh index b3995550856a..a4ee5496f2a1 100755 --- a/tools/testing/selftests/net/netfilter/nft_flowtable.sh +++ b/tools/testing/selftests/net/netfilter/nft_flowtable.sh @@ -71,6 +71,8 @@ omtu=9000 lmtu=1500 rmtu=2000 +filesize=$((2 * 1024 * 1024)) + usage(){ echo "nft_flowtable.sh [OPTIONS]" echo @@ -81,12 +83,13 @@ usage(){ exit 1 } -while getopts "o:l:r:" o +while getopts "o:l:r:s:" o do case $o in o) omtu=$OPTARG;; l) lmtu=$OPTARG;; r) rmtu=$OPTARG;; + s) filesize=$OPTARG;; *) usage;; esac done @@ -217,18 +220,10 @@ ns2out=$(mktemp) make_file() { - name=$1 - - SIZE=$((RANDOM % (1024 * 128))) - SIZE=$((SIZE + (1024 * 8))) - TSIZE=$((SIZE * 1024)) - - dd if=/dev/urandom of="$name" bs=1024 count=$SIZE 2> /dev/null + name="$1" + sz="$2" - SIZE=$((RANDOM % 1024)) - SIZE=$((SIZE + 128)) - TSIZE=$((TSIZE + SIZE)) - dd if=/dev/urandom conf=notrunc of="$name" bs=1 count=$SIZE 2> /dev/null + head -c "$sz" < /dev/urandom > "$name" } check_counters() @@ -246,18 +241,18 @@ check_counters() local fs fs=$(du -sb "$nsin") local max_orig=${fs%%/*} - local max_repl=$((max_orig/4)) + local max_repl=$((max_orig)) # flowtable fastpath should bypass normal routing one, i.e. the counters in forward hook # should always be lower than the size of the transmitted file (max_orig). if [ "$orig_cnt" -gt "$max_orig" ];then - echo "FAIL: $what: original counter $orig_cnt exceeds expected value $max_orig" 1>&2 + echo "FAIL: $what: original counter $orig_cnt exceeds expected value $max_orig, reply counter $repl_cnt" 1>&2 ret=1 ok=0 fi if [ "$repl_cnt" -gt $max_repl ];then - echo "FAIL: $what: reply counter $repl_cnt exceeds expected value $max_repl" 1>&2 + echo "FAIL: $what: reply counter $repl_cnt exceeds expected value $max_repl, original counter $orig_cnt" 1>&2 ret=1 ok=0 fi @@ -455,7 +450,7 @@ test_tcp_forwarding_nat() return $lret } -make_file "$nsin" +make_file "$nsin" "$filesize" # First test: # No PMTU discovery, nsr1 is expected to fragment packets from ns1 to ns2 as needed. @@ -664,8 +659,16 @@ if [ "$1" = "" ]; then l=$(((RANDOM%mtu) + low)) r=$(((RANDOM%mtu) + low)) - echo "re-run with random mtus: -o $o -l $l -r $r" - $0 -o "$o" -l "$l" -r "$r" + MINSIZE=$((2 * 1000 * 1000)) + MAXSIZE=$((64 * 1000 * 1000)) + + filesize=$(((RANDOM * RANDOM) % MAXSIZE)) + if [ "$filesize" -lt "$MINSIZE" ]; then + filesize=$((filesize+MINSIZE)) + fi + + echo "re-run with random mtus and file size: -o $o -l $l -r $r -s $filesize" + $0 -o "$o" -l "$l" -r "$r" -s "$filesize" fi exit $ret diff --git a/tools/testing/selftests/sched_ext/Makefile b/tools/testing/selftests/sched_ext/Makefile index 06ae9c107049..011762224600 100644 --- a/tools/testing/selftests/sched_ext/Makefile +++ b/tools/testing/selftests/sched_ext/Makefile @@ -184,7 +184,7 @@ auto-test-targets := \ testcase-targets := $(addsuffix .o,$(addprefix $(SCXOBJ_DIR)/,$(auto-test-targets))) -$(SCXOBJ_DIR)/runner.o: runner.c | $(SCXOBJ_DIR) +$(SCXOBJ_DIR)/runner.o: runner.c | $(SCXOBJ_DIR) $(BPFOBJ) $(CC) $(CFLAGS) -c $< -o $@ # Create all of the test targets object files, whose testcase objects will be diff --git a/tools/testing/selftests/sched_ext/create_dsq.bpf.c b/tools/testing/selftests/sched_ext/create_dsq.bpf.c index 23f79ed343f0..2cfc4ffd60e2 100644 --- a/tools/testing/selftests/sched_ext/create_dsq.bpf.c +++ b/tools/testing/selftests/sched_ext/create_dsq.bpf.c @@ -51,8 +51,8 @@ s32 BPF_STRUCT_OPS_SLEEPABLE(create_dsq_init) SEC(".struct_ops.link") struct sched_ext_ops create_dsq_ops = { - .init_task = create_dsq_init_task, - .exit_task = create_dsq_exit_task, - .init = create_dsq_init, + .init_task = (void *) create_dsq_init_task, + .exit_task = (void *) create_dsq_exit_task, + .init = (void *) create_dsq_init, .name = "create_dsq", }; diff --git a/tools/testing/selftests/sched_ext/ddsp_bogus_dsq_fail.bpf.c b/tools/testing/selftests/sched_ext/ddsp_bogus_dsq_fail.bpf.c index e97ad41d354a..37d9bf6fb745 100644 --- a/tools/testing/selftests/sched_ext/ddsp_bogus_dsq_fail.bpf.c +++ b/tools/testing/selftests/sched_ext/ddsp_bogus_dsq_fail.bpf.c @@ -35,8 +35,8 @@ void BPF_STRUCT_OPS(ddsp_bogus_dsq_fail_exit, struct scx_exit_info *ei) SEC(".struct_ops.link") struct sched_ext_ops ddsp_bogus_dsq_fail_ops = { - .select_cpu = ddsp_bogus_dsq_fail_select_cpu, - .exit = ddsp_bogus_dsq_fail_exit, + .select_cpu = (void *) ddsp_bogus_dsq_fail_select_cpu, + .exit = (void *) ddsp_bogus_dsq_fail_exit, .name = "ddsp_bogus_dsq_fail", .timeout_ms = 1000U, }; diff --git a/tools/testing/selftests/sched_ext/ddsp_vtimelocal_fail.bpf.c b/tools/testing/selftests/sched_ext/ddsp_vtimelocal_fail.bpf.c index dde7e7dafbfb..dffc97d9cdf1 100644 --- a/tools/testing/selftests/sched_ext/ddsp_vtimelocal_fail.bpf.c +++ b/tools/testing/selftests/sched_ext/ddsp_vtimelocal_fail.bpf.c @@ -32,8 +32,8 @@ void BPF_STRUCT_OPS(ddsp_vtimelocal_fail_exit, struct scx_exit_info *ei) SEC(".struct_ops.link") struct sched_ext_ops ddsp_vtimelocal_fail_ops = { - .select_cpu = ddsp_vtimelocal_fail_select_cpu, - .exit = ddsp_vtimelocal_fail_exit, + .select_cpu = (void *) ddsp_vtimelocal_fail_select_cpu, + .exit = (void *) ddsp_vtimelocal_fail_exit, .name = "ddsp_vtimelocal_fail", .timeout_ms = 1000U, }; diff --git a/tools/testing/selftests/sched_ext/dsp_local_on.bpf.c b/tools/testing/selftests/sched_ext/dsp_local_on.bpf.c index efb4672decb4..6a7db1502c29 100644 --- a/tools/testing/selftests/sched_ext/dsp_local_on.bpf.c +++ b/tools/testing/selftests/sched_ext/dsp_local_on.bpf.c @@ -56,10 +56,10 @@ void BPF_STRUCT_OPS(dsp_local_on_exit, struct scx_exit_info *ei) SEC(".struct_ops.link") struct sched_ext_ops dsp_local_on_ops = { - .select_cpu = dsp_local_on_select_cpu, - .enqueue = dsp_local_on_enqueue, - .dispatch = dsp_local_on_dispatch, - .exit = dsp_local_on_exit, + .select_cpu = (void *) dsp_local_on_select_cpu, + .enqueue = (void *) dsp_local_on_enqueue, + .dispatch = (void *) dsp_local_on_dispatch, + .exit = (void *) dsp_local_on_exit, .name = "dsp_local_on", .timeout_ms = 1000U, }; diff --git a/tools/testing/selftests/sched_ext/enq_last_no_enq_fails.bpf.c b/tools/testing/selftests/sched_ext/enq_last_no_enq_fails.bpf.c index b0b99531d5d5..e1bd13e48889 100644 --- a/tools/testing/selftests/sched_ext/enq_last_no_enq_fails.bpf.c +++ b/tools/testing/selftests/sched_ext/enq_last_no_enq_fails.bpf.c @@ -12,10 +12,18 @@ char _license[] SEC("license") = "GPL"; +u32 exit_kind; + +void BPF_STRUCT_OPS_SLEEPABLE(enq_last_no_enq_fails_exit, struct scx_exit_info *info) +{ + exit_kind = info->kind; +} + SEC(".struct_ops.link") struct sched_ext_ops enq_last_no_enq_fails_ops = { .name = "enq_last_no_enq_fails", /* Need to define ops.enqueue() with SCX_OPS_ENQ_LAST */ .flags = SCX_OPS_ENQ_LAST, + .exit = (void *) enq_last_no_enq_fails_exit, .timeout_ms = 1000U, }; diff --git a/tools/testing/selftests/sched_ext/enq_last_no_enq_fails.c b/tools/testing/selftests/sched_ext/enq_last_no_enq_fails.c index 2a3eda5e2c0b..73e679953e27 100644 --- a/tools/testing/selftests/sched_ext/enq_last_no_enq_fails.c +++ b/tools/testing/selftests/sched_ext/enq_last_no_enq_fails.c @@ -31,8 +31,12 @@ static enum scx_test_status run(void *ctx) struct bpf_link *link; link = bpf_map__attach_struct_ops(skel->maps.enq_last_no_enq_fails_ops); - if (link) { - SCX_ERR("Incorrectly succeeded in to attaching scheduler"); + if (!link) { + SCX_ERR("Incorrectly failed at attaching scheduler"); + return SCX_TEST_FAIL; + } + if (!skel->bss->exit_kind) { + SCX_ERR("Incorrectly stayed loaded"); return SCX_TEST_FAIL; } @@ -50,7 +54,7 @@ static void cleanup(void *ctx) struct scx_test enq_last_no_enq_fails = { .name = "enq_last_no_enq_fails", - .description = "Verify we fail to load a scheduler if we specify " + .description = "Verify we eject a scheduler if we specify " "the SCX_OPS_ENQ_LAST flag without defining " "ops.enqueue()", .setup = setup, diff --git a/tools/testing/selftests/sched_ext/enq_select_cpu_fails.bpf.c b/tools/testing/selftests/sched_ext/enq_select_cpu_fails.bpf.c index b3dfc1033cd6..1efb50d61040 100644 --- a/tools/testing/selftests/sched_ext/enq_select_cpu_fails.bpf.c +++ b/tools/testing/selftests/sched_ext/enq_select_cpu_fails.bpf.c @@ -36,8 +36,8 @@ void BPF_STRUCT_OPS(enq_select_cpu_fails_enqueue, struct task_struct *p, SEC(".struct_ops.link") struct sched_ext_ops enq_select_cpu_fails_ops = { - .select_cpu = enq_select_cpu_fails_select_cpu, - .enqueue = enq_select_cpu_fails_enqueue, + .select_cpu = (void *) enq_select_cpu_fails_select_cpu, + .enqueue = (void *) enq_select_cpu_fails_enqueue, .name = "enq_select_cpu_fails", .timeout_ms = 1000U, }; diff --git a/tools/testing/selftests/sched_ext/exit.bpf.c b/tools/testing/selftests/sched_ext/exit.bpf.c index ae12ddaac921..d75d4faf07f6 100644 --- a/tools/testing/selftests/sched_ext/exit.bpf.c +++ b/tools/testing/selftests/sched_ext/exit.bpf.c @@ -15,6 +15,8 @@ UEI_DEFINE(uei); #define EXIT_CLEANLY() scx_bpf_exit(exit_point, "%d", exit_point) +#define DSQ_ID 0 + s32 BPF_STRUCT_OPS(exit_select_cpu, struct task_struct *p, s32 prev_cpu, u64 wake_flags) { @@ -31,7 +33,7 @@ void BPF_STRUCT_OPS(exit_enqueue, struct task_struct *p, u64 enq_flags) if (exit_point == EXIT_ENQUEUE) EXIT_CLEANLY(); - scx_bpf_dispatch(p, SCX_DSQ_GLOBAL, SCX_SLICE_DFL, enq_flags); + scx_bpf_dispatch(p, DSQ_ID, SCX_SLICE_DFL, enq_flags); } void BPF_STRUCT_OPS(exit_dispatch, s32 cpu, struct task_struct *p) @@ -39,7 +41,7 @@ void BPF_STRUCT_OPS(exit_dispatch, s32 cpu, struct task_struct *p) if (exit_point == EXIT_DISPATCH) EXIT_CLEANLY(); - scx_bpf_consume(SCX_DSQ_GLOBAL); + scx_bpf_consume(DSQ_ID); } void BPF_STRUCT_OPS(exit_enable, struct task_struct *p) @@ -67,18 +69,18 @@ s32 BPF_STRUCT_OPS_SLEEPABLE(exit_init) if (exit_point == EXIT_INIT) EXIT_CLEANLY(); - return 0; + return scx_bpf_create_dsq(DSQ_ID, -1); } SEC(".struct_ops.link") struct sched_ext_ops exit_ops = { - .select_cpu = exit_select_cpu, - .enqueue = exit_enqueue, - .dispatch = exit_dispatch, - .init_task = exit_init_task, - .enable = exit_enable, - .exit = exit_exit, - .init = exit_init, + .select_cpu = (void *) exit_select_cpu, + .enqueue = (void *) exit_enqueue, + .dispatch = (void *) exit_dispatch, + .init_task = (void *) exit_init_task, + .enable = (void *) exit_enable, + .exit = (void *) exit_exit, + .init = (void *) exit_init, .name = "exit", .timeout_ms = 1000U, }; diff --git a/tools/testing/selftests/sched_ext/hotplug.bpf.c b/tools/testing/selftests/sched_ext/hotplug.bpf.c index 8f2601db39f3..6c9f25c9bf53 100644 --- a/tools/testing/selftests/sched_ext/hotplug.bpf.c +++ b/tools/testing/selftests/sched_ext/hotplug.bpf.c @@ -46,16 +46,16 @@ void BPF_STRUCT_OPS_SLEEPABLE(hotplug_cpu_offline, s32 cpu) SEC(".struct_ops.link") struct sched_ext_ops hotplug_cb_ops = { - .cpu_online = hotplug_cpu_online, - .cpu_offline = hotplug_cpu_offline, - .exit = hotplug_exit, + .cpu_online = (void *) hotplug_cpu_online, + .cpu_offline = (void *) hotplug_cpu_offline, + .exit = (void *) hotplug_exit, .name = "hotplug_cbs", .timeout_ms = 1000U, }; SEC(".struct_ops.link") struct sched_ext_ops hotplug_nocb_ops = { - .exit = hotplug_exit, + .exit = (void *) hotplug_exit, .name = "hotplug_nocbs", .timeout_ms = 1000U, }; diff --git a/tools/testing/selftests/sched_ext/init_enable_count.bpf.c b/tools/testing/selftests/sched_ext/init_enable_count.bpf.c index 47ea89a626c3..5eb9edb1837d 100644 --- a/tools/testing/selftests/sched_ext/init_enable_count.bpf.c +++ b/tools/testing/selftests/sched_ext/init_enable_count.bpf.c @@ -45,9 +45,9 @@ void BPF_STRUCT_OPS(cnt_disable, struct task_struct *p) SEC(".struct_ops.link") struct sched_ext_ops init_enable_count_ops = { - .init_task = cnt_init_task, - .exit_task = cnt_exit_task, - .enable = cnt_enable, - .disable = cnt_disable, + .init_task = (void *) cnt_init_task, + .exit_task = (void *) cnt_exit_task, + .enable = (void *) cnt_enable, + .disable = (void *) cnt_disable, .name = "init_enable_count", }; diff --git a/tools/testing/selftests/sched_ext/maximal.bpf.c b/tools/testing/selftests/sched_ext/maximal.bpf.c index 00bfa9cb95d3..4d4cd8d966db 100644 --- a/tools/testing/selftests/sched_ext/maximal.bpf.c +++ b/tools/testing/selftests/sched_ext/maximal.bpf.c @@ -131,34 +131,34 @@ void BPF_STRUCT_OPS(maximal_exit, struct scx_exit_info *info) SEC(".struct_ops.link") struct sched_ext_ops maximal_ops = { - .select_cpu = maximal_select_cpu, - .enqueue = maximal_enqueue, - .dequeue = maximal_dequeue, - .dispatch = maximal_dispatch, - .runnable = maximal_runnable, - .running = maximal_running, - .stopping = maximal_stopping, - .quiescent = maximal_quiescent, - .yield = maximal_yield, - .core_sched_before = maximal_core_sched_before, - .set_weight = maximal_set_weight, - .set_cpumask = maximal_set_cpumask, - .update_idle = maximal_update_idle, - .cpu_acquire = maximal_cpu_acquire, - .cpu_release = maximal_cpu_release, - .cpu_online = maximal_cpu_online, - .cpu_offline = maximal_cpu_offline, - .init_task = maximal_init_task, - .enable = maximal_enable, - .exit_task = maximal_exit_task, - .disable = maximal_disable, - .cgroup_init = maximal_cgroup_init, - .cgroup_exit = maximal_cgroup_exit, - .cgroup_prep_move = maximal_cgroup_prep_move, - .cgroup_move = maximal_cgroup_move, - .cgroup_cancel_move = maximal_cgroup_cancel_move, - .cgroup_set_weight = maximal_cgroup_set_weight, - .init = maximal_init, - .exit = maximal_exit, + .select_cpu = (void *) maximal_select_cpu, + .enqueue = (void *) maximal_enqueue, + .dequeue = (void *) maximal_dequeue, + .dispatch = (void *) maximal_dispatch, + .runnable = (void *) maximal_runnable, + .running = (void *) maximal_running, + .stopping = (void *) maximal_stopping, + .quiescent = (void *) maximal_quiescent, + .yield = (void *) maximal_yield, + .core_sched_before = (void *) maximal_core_sched_before, + .set_weight = (void *) maximal_set_weight, + .set_cpumask = (void *) maximal_set_cpumask, + .update_idle = (void *) maximal_update_idle, + .cpu_acquire = (void *) maximal_cpu_acquire, + .cpu_release = (void *) maximal_cpu_release, + .cpu_online = (void *) maximal_cpu_online, + .cpu_offline = (void *) maximal_cpu_offline, + .init_task = (void *) maximal_init_task, + .enable = (void *) maximal_enable, + .exit_task = (void *) maximal_exit_task, + .disable = (void *) maximal_disable, + .cgroup_init = (void *) maximal_cgroup_init, + .cgroup_exit = (void *) maximal_cgroup_exit, + .cgroup_prep_move = (void *) maximal_cgroup_prep_move, + .cgroup_move = (void *) maximal_cgroup_move, + .cgroup_cancel_move = (void *) maximal_cgroup_cancel_move, + .cgroup_set_weight = (void *) maximal_cgroup_set_weight, + .init = (void *) maximal_init, + .exit = (void *) maximal_exit, .name = "maximal", }; diff --git a/tools/testing/selftests/sched_ext/maybe_null.bpf.c b/tools/testing/selftests/sched_ext/maybe_null.bpf.c index 27d0f386acfb..cf4ae870cd4e 100644 --- a/tools/testing/selftests/sched_ext/maybe_null.bpf.c +++ b/tools/testing/selftests/sched_ext/maybe_null.bpf.c @@ -29,8 +29,8 @@ bool BPF_STRUCT_OPS(maybe_null_success_yield, struct task_struct *from, SEC(".struct_ops.link") struct sched_ext_ops maybe_null_success = { - .dispatch = maybe_null_success_dispatch, - .yield = maybe_null_success_yield, - .enable = maybe_null_running, + .dispatch = (void *) maybe_null_success_dispatch, + .yield = (void *) maybe_null_success_yield, + .enable = (void *) maybe_null_running, .name = "minimal", }; diff --git a/tools/testing/selftests/sched_ext/maybe_null_fail_dsp.bpf.c b/tools/testing/selftests/sched_ext/maybe_null_fail_dsp.bpf.c index c0641050271d..ec724d7b33d1 100644 --- a/tools/testing/selftests/sched_ext/maybe_null_fail_dsp.bpf.c +++ b/tools/testing/selftests/sched_ext/maybe_null_fail_dsp.bpf.c @@ -19,7 +19,7 @@ void BPF_STRUCT_OPS(maybe_null_fail_dispatch, s32 cpu, struct task_struct *p) SEC(".struct_ops.link") struct sched_ext_ops maybe_null_fail = { - .dispatch = maybe_null_fail_dispatch, - .enable = maybe_null_running, + .dispatch = (void *) maybe_null_fail_dispatch, + .enable = (void *) maybe_null_running, .name = "maybe_null_fail_dispatch", }; diff --git a/tools/testing/selftests/sched_ext/maybe_null_fail_yld.bpf.c b/tools/testing/selftests/sched_ext/maybe_null_fail_yld.bpf.c index 3c1740028e3b..e6552cace020 100644 --- a/tools/testing/selftests/sched_ext/maybe_null_fail_yld.bpf.c +++ b/tools/testing/selftests/sched_ext/maybe_null_fail_yld.bpf.c @@ -22,7 +22,7 @@ bool BPF_STRUCT_OPS(maybe_null_fail_yield, struct task_struct *from, SEC(".struct_ops.link") struct sched_ext_ops maybe_null_fail = { - .yield = maybe_null_fail_yield, - .enable = maybe_null_running, + .yield = (void *) maybe_null_fail_yield, + .enable = (void *) maybe_null_running, .name = "maybe_null_fail_yield", }; diff --git a/tools/testing/selftests/sched_ext/prog_run.bpf.c b/tools/testing/selftests/sched_ext/prog_run.bpf.c index 6a4d7c48e3f2..00c267626a68 100644 --- a/tools/testing/selftests/sched_ext/prog_run.bpf.c +++ b/tools/testing/selftests/sched_ext/prog_run.bpf.c @@ -28,6 +28,6 @@ void BPF_STRUCT_OPS(prog_run_exit, struct scx_exit_info *ei) SEC(".struct_ops.link") struct sched_ext_ops prog_run_ops = { - .exit = prog_run_exit, + .exit = (void *) prog_run_exit, .name = "prog_run", }; diff --git a/tools/testing/selftests/sched_ext/select_cpu_dfl.bpf.c b/tools/testing/selftests/sched_ext/select_cpu_dfl.bpf.c index 2ed2991afafe..f171ac470970 100644 --- a/tools/testing/selftests/sched_ext/select_cpu_dfl.bpf.c +++ b/tools/testing/selftests/sched_ext/select_cpu_dfl.bpf.c @@ -35,6 +35,6 @@ void BPF_STRUCT_OPS(select_cpu_dfl_enqueue, struct task_struct *p, SEC(".struct_ops.link") struct sched_ext_ops select_cpu_dfl_ops = { - .enqueue = select_cpu_dfl_enqueue, + .enqueue = (void *) select_cpu_dfl_enqueue, .name = "select_cpu_dfl", }; diff --git a/tools/testing/selftests/sched_ext/select_cpu_dfl_nodispatch.bpf.c b/tools/testing/selftests/sched_ext/select_cpu_dfl_nodispatch.bpf.c index 4bb5abb2d369..9efdbb7da928 100644 --- a/tools/testing/selftests/sched_ext/select_cpu_dfl_nodispatch.bpf.c +++ b/tools/testing/selftests/sched_ext/select_cpu_dfl_nodispatch.bpf.c @@ -82,8 +82,8 @@ s32 BPF_STRUCT_OPS(select_cpu_dfl_nodispatch_init_task, SEC(".struct_ops.link") struct sched_ext_ops select_cpu_dfl_nodispatch_ops = { - .select_cpu = select_cpu_dfl_nodispatch_select_cpu, - .enqueue = select_cpu_dfl_nodispatch_enqueue, - .init_task = select_cpu_dfl_nodispatch_init_task, + .select_cpu = (void *) select_cpu_dfl_nodispatch_select_cpu, + .enqueue = (void *) select_cpu_dfl_nodispatch_enqueue, + .init_task = (void *) select_cpu_dfl_nodispatch_init_task, .name = "select_cpu_dfl_nodispatch", }; diff --git a/tools/testing/selftests/sched_ext/select_cpu_dispatch.bpf.c b/tools/testing/selftests/sched_ext/select_cpu_dispatch.bpf.c index f0b96a4a04b2..59bfc4f36167 100644 --- a/tools/testing/selftests/sched_ext/select_cpu_dispatch.bpf.c +++ b/tools/testing/selftests/sched_ext/select_cpu_dispatch.bpf.c @@ -35,7 +35,7 @@ dispatch: SEC(".struct_ops.link") struct sched_ext_ops select_cpu_dispatch_ops = { - .select_cpu = select_cpu_dispatch_select_cpu, + .select_cpu = (void *) select_cpu_dispatch_select_cpu, .name = "select_cpu_dispatch", .timeout_ms = 1000U, }; diff --git a/tools/testing/selftests/sched_ext/select_cpu_dispatch_bad_dsq.bpf.c b/tools/testing/selftests/sched_ext/select_cpu_dispatch_bad_dsq.bpf.c index 7b42ddce0f56..3bbd5fcdfb18 100644 --- a/tools/testing/selftests/sched_ext/select_cpu_dispatch_bad_dsq.bpf.c +++ b/tools/testing/selftests/sched_ext/select_cpu_dispatch_bad_dsq.bpf.c @@ -30,8 +30,8 @@ void BPF_STRUCT_OPS(select_cpu_dispatch_bad_dsq_exit, struct scx_exit_info *ei) SEC(".struct_ops.link") struct sched_ext_ops select_cpu_dispatch_bad_dsq_ops = { - .select_cpu = select_cpu_dispatch_bad_dsq_select_cpu, - .exit = select_cpu_dispatch_bad_dsq_exit, + .select_cpu = (void *) select_cpu_dispatch_bad_dsq_select_cpu, + .exit = (void *) select_cpu_dispatch_bad_dsq_exit, .name = "select_cpu_dispatch_bad_dsq", .timeout_ms = 1000U, }; diff --git a/tools/testing/selftests/sched_ext/select_cpu_dispatch_dbl_dsp.bpf.c b/tools/testing/selftests/sched_ext/select_cpu_dispatch_dbl_dsp.bpf.c index 653e3dc0b4dc..0fda57fe0ecf 100644 --- a/tools/testing/selftests/sched_ext/select_cpu_dispatch_dbl_dsp.bpf.c +++ b/tools/testing/selftests/sched_ext/select_cpu_dispatch_dbl_dsp.bpf.c @@ -31,8 +31,8 @@ void BPF_STRUCT_OPS(select_cpu_dispatch_dbl_dsp_exit, struct scx_exit_info *ei) SEC(".struct_ops.link") struct sched_ext_ops select_cpu_dispatch_dbl_dsp_ops = { - .select_cpu = select_cpu_dispatch_dbl_dsp_select_cpu, - .exit = select_cpu_dispatch_dbl_dsp_exit, + .select_cpu = (void *) select_cpu_dispatch_dbl_dsp_select_cpu, + .exit = (void *) select_cpu_dispatch_dbl_dsp_exit, .name = "select_cpu_dispatch_dbl_dsp", .timeout_ms = 1000U, }; diff --git a/tools/testing/selftests/sched_ext/select_cpu_vtime.bpf.c b/tools/testing/selftests/sched_ext/select_cpu_vtime.bpf.c index 7f3ebf4fc2ea..e6c67bcf5e6e 100644 --- a/tools/testing/selftests/sched_ext/select_cpu_vtime.bpf.c +++ b/tools/testing/selftests/sched_ext/select_cpu_vtime.bpf.c @@ -81,12 +81,12 @@ s32 BPF_STRUCT_OPS_SLEEPABLE(select_cpu_vtime_init) SEC(".struct_ops.link") struct sched_ext_ops select_cpu_vtime_ops = { - .select_cpu = select_cpu_vtime_select_cpu, - .dispatch = select_cpu_vtime_dispatch, - .running = select_cpu_vtime_running, - .stopping = select_cpu_vtime_stopping, - .enable = select_cpu_vtime_enable, - .init = select_cpu_vtime_init, + .select_cpu = (void *) select_cpu_vtime_select_cpu, + .dispatch = (void *) select_cpu_vtime_dispatch, + .running = (void *) select_cpu_vtime_running, + .stopping = (void *) select_cpu_vtime_stopping, + .enable = (void *) select_cpu_vtime_enable, + .init = (void *) select_cpu_vtime_init, .name = "select_cpu_vtime", .timeout_ms = 1000U, }; diff --git a/tools/testing/selftests/watchdog/watchdog-test.c b/tools/testing/selftests/watchdog/watchdog-test.c index bc71cbca0dde..a1f506ba5578 100644 --- a/tools/testing/selftests/watchdog/watchdog-test.c +++ b/tools/testing/selftests/watchdog/watchdog-test.c @@ -334,7 +334,13 @@ int main(int argc, char *argv[]) printf("Watchdog Ticking Away!\n"); + /* + * Register the signals + */ signal(SIGINT, term); + signal(SIGTERM, term); + signal(SIGKILL, term); + signal(SIGQUIT, term); while (1) { keep_alive(); diff --git a/tools/testing/vma/vma.c b/tools/testing/vma/vma.c index c53f220eb6cc..b33b47342d41 100644 --- a/tools/testing/vma/vma.c +++ b/tools/testing/vma/vma.c @@ -1522,6 +1522,45 @@ static bool test_copy_vma(void) return true; } +static bool test_expand_only_mode(void) +{ + unsigned long flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE; + struct mm_struct mm = {}; + VMA_ITERATOR(vmi, &mm, 0); + struct vm_area_struct *vma_prev, *vma; + VMG_STATE(vmg, &mm, &vmi, 0x5000, 0x9000, flags, 5); + + /* + * Place a VMA prior to the one we're expanding so we assert that we do + * not erroneously try to traverse to the previous VMA even though we + * have, through the use of VMG_FLAG_JUST_EXPAND, indicated we do not + * need to do so. + */ + alloc_and_link_vma(&mm, 0, 0x2000, 0, flags); + + /* + * We will be positioned at the prev VMA, but looking to expand to + * 0x9000. + */ + vma_iter_set(&vmi, 0x3000); + vma_prev = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, flags); + vmg.prev = vma_prev; + vmg.merge_flags = VMG_FLAG_JUST_EXPAND; + + vma = vma_merge_new_range(&vmg); + ASSERT_NE(vma, NULL); + ASSERT_EQ(vma, vma_prev); + ASSERT_EQ(vmg.state, VMA_MERGE_SUCCESS); + ASSERT_EQ(vma->vm_start, 0x3000); + ASSERT_EQ(vma->vm_end, 0x9000); + ASSERT_EQ(vma->vm_pgoff, 3); + ASSERT_TRUE(vma_write_started(vma)); + ASSERT_EQ(vma_iter_addr(&vmi), 0x3000); + + cleanup_mm(&mm, &vmi); + return true; +} + int main(void) { int num_tests = 0, num_fail = 0; @@ -1553,6 +1592,7 @@ int main(void) TEST(vmi_prealloc_fail); TEST(merge_extend); TEST(copy_vma); + TEST(expand_only_mode); #undef TEST diff --git a/tools/usb/usbip/src/usbip_detach.c b/tools/usb/usbip/src/usbip_detach.c index b29101986b5a..6b78d4a81e95 100644 --- a/tools/usb/usbip/src/usbip_detach.c +++ b/tools/usb/usbip/src/usbip_detach.c @@ -68,6 +68,7 @@ static int detach_port(char *port) } if (!found) { + ret = -1; err("Invalid port %s > maxports %d", port, vhci_driver->nports); goto call_driver_close; |
