diff options
537 files changed, 13983 insertions, 4792 deletions
diff --git a/Documentation/PCI/endpoint/index.rst b/Documentation/PCI/endpoint/index.rst index 38ea1f604b6d..4d2333e7ae06 100644 --- a/Documentation/PCI/endpoint/index.rst +++ b/Documentation/PCI/endpoint/index.rst @@ -13,6 +13,8 @@ PCI Endpoint Framework pci-test-howto pci-ntb-function pci-ntb-howto + pci-vntb-function + pci-vntb-howto function/binding/pci-test function/binding/pci-ntb diff --git a/Documentation/PCI/endpoint/pci-vntb-function.rst b/Documentation/PCI/endpoint/pci-vntb-function.rst new file mode 100644 index 000000000000..0c51f53ab972 --- /dev/null +++ b/Documentation/PCI/endpoint/pci-vntb-function.rst @@ -0,0 +1,129 @@ +.. SPDX-License-Identifier: GPL-2.0 + +================= +PCI vNTB Function +================= + +:Author: Frank Li <Frank.Li@nxp.com> + +The difference between PCI NTB function and PCI vNTB function is + +PCI NTB function need at two endpoint instances and connect HOST1 +and HOST2. + +PCI vNTB function only use one host and one endpoint(EP), use NTB +connect EP and PCI host + +.. code-block:: text + + + +------------+ +---------------------------------------+ + | | | | + +------------+ | +--------------+ + | NTB | | | NTB | + | NetDev | | | NetDev | + +------------+ | +--------------+ + | NTB | | | NTB | + | Transfer | | | Transfer | + +------------+ | +--------------+ + | | | | | + | PCI NTB | | | | + | EPF | | | | + | Driver | | | PCI Virtual | + | | +---------------+ | NTB Driver | + | | | PCI EP NTB |<------>| | + | | | FN Driver | | | + +------------+ +---------------+ +--------------+ + | | | | | | + | PCI BUS | <-----> | PCI EP BUS | | Virtual PCI | + | | PCI | | | BUS | + +------------+ +---------------+--------+--------------+ + PCI RC PCI EP + +Constructs used for Implementing vNTB +===================================== + + 1) Config Region + 2) Self Scratchpad Registers + 3) Peer Scratchpad Registers + 4) Doorbell (DB) Registers + 5) Memory Window (MW) + + +Config Region: +-------------- + +It is same as PCI NTB Function driver + +Scratchpad Registers: +--------------------- + +It is appended after Config region. + +.. code-block:: text + + + +--------------------------------------------------+ Base + | | + | | + | | + | Common Config Register | + | | + | | + | | + +-----------------------+--------------------------+ Base + span_offset + | | | + | Peer Span Space | Span Space | + | | | + | | | + +-----------------------+--------------------------+ Base + span_offset + | | | + span_count * 4 + | | | + | Span Space | Peer Span Space | + | | | + +-----------------------+--------------------------+ + Virtual PCI Pcie Endpoint + NTB Driver NTB Driver + + +Doorbell Registers: +------------------- + + Doorbell Registers are used by the hosts to interrupt each other. + +Memory Window: +-------------- + + Actual transfer of data between the two hosts will happen using the + memory window. + +Modeling Constructs: +==================== + +32-bit BARs. + +====== =============== +BAR NO CONSTRUCTS USED +====== =============== +BAR0 Config Region +BAR1 Doorbell +BAR2 Memory Window 1 +BAR3 Memory Window 2 +BAR4 Memory Window 3 +BAR5 Memory Window 4 +====== =============== + +64-bit BARs. + +====== =============================== +BAR NO CONSTRUCTS USED +====== =============================== +BAR0 Config Region + Scratchpad +BAR1 +BAR2 Doorbell +BAR3 +BAR4 Memory Window 1 +BAR5 +====== =============================== + + diff --git a/Documentation/PCI/endpoint/pci-vntb-howto.rst b/Documentation/PCI/endpoint/pci-vntb-howto.rst new file mode 100644 index 000000000000..4ab8e4a26d4b --- /dev/null +++ b/Documentation/PCI/endpoint/pci-vntb-howto.rst @@ -0,0 +1,167 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=================================================================== +PCI Non-Transparent Bridge (NTB) Endpoint Function (EPF) User Guide +=================================================================== + +:Author: Frank Li <Frank.Li@nxp.com> + +This document is a guide to help users use pci-epf-vntb function driver +and ntb_hw_epf host driver for NTB functionality. The list of steps to +be followed in the host side and EP side is given below. For the hardware +configuration and internals of NTB using configurable endpoints see +Documentation/PCI/endpoint/pci-vntb-function.rst + +Endpoint Device +=============== + +Endpoint Controller Devices +--------------------------- + +To find the list of endpoint controller devices in the system:: + + # ls /sys/class/pci_epc/ + 5f010000.pcie_ep + +If PCI_ENDPOINT_CONFIGFS is enabled:: + + # ls /sys/kernel/config/pci_ep/controllers + 5f010000.pcie_ep + +Endpoint Function Drivers +------------------------- + +To find the list of endpoint function drivers in the system:: + + # ls /sys/bus/pci-epf/drivers + pci_epf_ntb pci_epf_test pci_epf_vntb + +If PCI_ENDPOINT_CONFIGFS is enabled:: + + # ls /sys/kernel/config/pci_ep/functions + pci_epf_ntb pci_epf_test pci_epf_vntb + + +Creating pci-epf-vntb Device +---------------------------- + +PCI endpoint function device can be created using the configfs. To create +pci-epf-vntb device, the following commands can be used:: + + # mount -t configfs none /sys/kernel/config + # cd /sys/kernel/config/pci_ep/ + # mkdir functions/pci_epf_vntb/func1 + +The "mkdir func1" above creates the pci-epf-ntb function device that will +be probed by pci_epf_vntb driver. + +The PCI endpoint framework populates the directory with the following +configurable fields:: + + # ls functions/pci_epf_ntb/func1 + baseclass_code deviceid msi_interrupts pci-epf-ntb.0 + progif_code secondary subsys_id vendorid + cache_line_size interrupt_pin msix_interrupts primary + revid subclass_code subsys_vendor_id + +The PCI endpoint function driver populates these entries with default values +when the device is bound to the driver. The pci-epf-vntb driver populates +vendorid with 0xffff and interrupt_pin with 0x0001:: + + # cat functions/pci_epf_vntb/func1/vendorid + 0xffff + # cat functions/pci_epf_vntb/func1/interrupt_pin + 0x0001 + + +Configuring pci-epf-vntb Device +------------------------------- + +The user can configure the pci-epf-vntb device using its configfs entry. In order +to change the vendorid and the deviceid, the following +commands can be used:: + + # echo 0x1957 > functions/pci_epf_vntb/func1/vendorid + # echo 0x0809 > functions/pci_epf_vntb/func1/deviceid + +In order to configure NTB specific attributes, a new sub-directory to func1 +should be created:: + + # mkdir functions/pci_epf_vntb/func1/pci_epf_vntb.0/ + +The NTB function driver will populate this directory with various attributes +that can be configured by the user:: + + # ls functions/pci_epf_vntb/func1/pci_epf_vntb.0/ + db_count mw1 mw2 mw3 mw4 num_mws + spad_count + +A sample configuration for NTB function is given below:: + + # echo 4 > functions/pci_epf_vntb/func1/pci_epf_vntb.0/db_count + # echo 128 > functions/pci_epf_vntb/func1/pci_epf_vntb.0/spad_count + # echo 1 > functions/pci_epf_vntb/func1/pci_epf_vntb.0/num_mws + # echo 0x100000 > functions/pci_epf_vntb/func1/pci_epf_vntb.0/mw1 + +A sample configuration for virtual NTB driver for virutal PCI bus:: + + # echo 0x1957 > functions/pci_epf_vntb/func1/pci_epf_vntb.0/vntb_vid + # echo 0x080A > functions/pci_epf_vntb/func1/pci_epf_vntb.0/vntb_pid + # echo 0x10 > functions/pci_epf_vntb/func1/pci_epf_vntb.0/vbus_number + +Binding pci-epf-ntb Device to EP Controller +-------------------------------------------- + +NTB function device should be attached to PCI endpoint controllers +connected to the host. + + # ln -s controllers/5f010000.pcie_ep functions/pci-epf-ntb/func1/primary + +Once the above step is completed, the PCI endpoint controllers are ready to +establish a link with the host. + + +Start the Link +-------------- + +In order for the endpoint device to establish a link with the host, the _start_ +field should be populated with '1'. For NTB, both the PCI endpoint controllers +should establish link with the host (imx8 don't need this steps):: + + # echo 1 > controllers/5f010000.pcie_ep/start + +RootComplex Device +================== + +lspci Output at Host side +------------------------- + +Note that the devices listed here correspond to the values populated in +"Creating pci-epf-ntb Device" section above:: + + # lspci + 00:00.0 PCI bridge: Freescale Semiconductor Inc Device 0000 (rev 01) + 01:00.0 RAM memory: Freescale Semiconductor Inc Device 0809 + +Endpoint Device / Virtual PCI bus +================================= + +lspci Output at EP Side / Virtual PCI bus +----------------------------------------- + +Note that the devices listed here correspond to the values populated in +"Creating pci-epf-ntb Device" section above:: + + # lspci + 10:00.0 Unassigned class [ffff]: Dawicontrol Computersysteme GmbH Device 1234 (rev ff) + +Using ntb_hw_epf Device +----------------------- + +The host side software follows the standard NTB software architecture in Linux. +All the existing client side NTB utilities like NTB Transport Client and NTB +Netdev, NTB Ping Pong Test Client and NTB Tool Test Client can be used with NTB +function device. + +For more information on NTB see +:doc:`Non-Transparent Bridge <../../driver-api/ntb>` diff --git a/Documentation/bpf/bpf_design_QA.rst b/Documentation/bpf/bpf_design_QA.rst index 437de2a7a5de..a210b8a4df00 100644 --- a/Documentation/bpf/bpf_design_QA.rst +++ b/Documentation/bpf/bpf_design_QA.rst @@ -214,6 +214,12 @@ A: NO. Tracepoints are tied to internal implementation details hence they are subject to change and can break with newer kernels. BPF programs need to change accordingly when this happens. +Q: Are places where kprobes can attach part of the stable ABI? +-------------------------------------------------------------- +A: NO. The places to which kprobes can attach are internal implementation +details, which means that they are subject to change and can break with +newer kernels. BPF programs need to change accordingly when this happens. + Q: How much stack space a BPF program uses? ------------------------------------------- A: Currently all program types are limited to 512 bytes of stack @@ -273,3 +279,22 @@ cc (congestion-control) implementations. If any of these kernel functions has changed, both the in-tree and out-of-tree kernel tcp cc implementations have to be changed. The same goes for the bpf programs and they have to be adjusted accordingly. + +Q: Attaching to arbitrary kernel functions is an ABI? +----------------------------------------------------- +Q: BPF programs can be attached to many kernel functions. Do these +kernel functions become part of the ABI? + +A: NO. + +The kernel function prototypes will change, and BPF programs attaching to +them will need to change. The BPF compile-once-run-everywhere (CO-RE) +should be used in order to make it easier to adapt your BPF programs to +different versions of the kernel. + +Q: Marking a function with BTF_ID makes that function an ABI? +------------------------------------------------------------- +A: NO. + +The BTF_ID macro does not cause a function to become part of the ABI +any more than does the EXPORT_SYMBOL_GPL macro. diff --git a/Documentation/devicetree/bindings/Makefile b/Documentation/devicetree/bindings/Makefile index c9953f86b19d..1eaccf135b30 100644 --- a/Documentation/devicetree/bindings/Makefile +++ b/Documentation/devicetree/bindings/Makefile @@ -42,9 +42,7 @@ quiet_cmd_chk_bindings = CHKDT $@ quiet_cmd_mk_schema = SCHEMA $@ cmd_mk_schema = f=$$(mktemp) ; \ - $(if $(DT_MK_SCHEMA_FLAGS), \ - printf '%s\n' $(real-prereqs), \ - $(find_all_cmd)) > $$f ; \ + $(find_all_cmd) > $$f ; \ $(DT_MK_SCHEMA) -j $(DT_MK_SCHEMA_FLAGS) @$$f > $@ ; \ rm -f $$f diff --git a/Documentation/devicetree/bindings/arm/atmel-sysregs.txt b/Documentation/devicetree/bindings/arm/atmel-sysregs.txt index 16eef600d599..ab1b352344ae 100644 --- a/Documentation/devicetree/bindings/arm/atmel-sysregs.txt +++ b/Documentation/devicetree/bindings/arm/atmel-sysregs.txt @@ -25,21 +25,6 @@ System Timer (ST) required properties: Its subnodes can be: - watchdog: compatible should be "atmel,at91rm9200-wdt" -RSTC Reset Controller required properties: -- compatible: Should be "atmel,<chip>-rstc". - <chip> can be "at91sam9260", "at91sam9g45", "sama5d3" or "samx7" - it also can be "microchip,sam9x60-rstc" -- reg: Should contain registers location and length -- clocks: phandle to input clock. - -Example: - - rstc@fffffd00 { - compatible = "atmel,at91sam9260-rstc"; - reg = <0xfffffd00 0x10>; - clocks = <&clk32k>; - }; - RAMC SDRAM/DDR Controller required properties: - compatible: Should be "atmel,at91rm9200-sdramc", "syscon" "atmel,at91sam9260-sdramc", diff --git a/Documentation/devicetree/bindings/chrome/google,cros-ec-typec.yaml b/Documentation/devicetree/bindings/chrome/google,cros-ec-typec.yaml index 2d98f7c4d3bc..50ebd8c57795 100644 --- a/Documentation/devicetree/bindings/chrome/google,cros-ec-typec.yaml +++ b/Documentation/devicetree/bindings/chrome/google,cros-ec-typec.yaml @@ -20,13 +20,24 @@ properties: compatible: const: google,cros-ec-typec - connector: + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + +patternProperties: + '^connector@[0-9a-f]+$': $ref: /schemas/connector/usb-connector.yaml# + unevaluatedProperties: false + properties: + reg: + maxItems: 1 required: - compatible -additionalProperties: true #fixme +additionalProperties: false examples: - |+ diff --git a/Documentation/devicetree/bindings/display/ilitek,ili9341.txt b/Documentation/devicetree/bindings/display/ilitek,ili9341.txt deleted file mode 100644 index 169b32e4ee4e..000000000000 --- a/Documentation/devicetree/bindings/display/ilitek,ili9341.txt +++ /dev/null @@ -1,27 +0,0 @@ -Ilitek ILI9341 display panels - -This binding is for display panels using an Ilitek ILI9341 controller in SPI -mode. - -Required properties: -- compatible: "adafruit,yx240qv29", "ilitek,ili9341" -- dc-gpios: D/C pin -- reset-gpios: Reset pin - -The node for this driver must be a child node of a SPI controller, hence -all mandatory properties described in ../spi/spi-bus.txt must be specified. - -Optional properties: -- rotation: panel rotation in degrees counter clockwise (0,90,180,270) -- backlight: phandle of the backlight device attached to the panel - -Example: - display@0{ - compatible = "adafruit,yx240qv29", "ilitek,ili9341"; - reg = <0>; - spi-max-frequency = <32000000>; - dc-gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>; - reset-gpios = <&gpio0 8 GPIO_ACTIVE_HIGH>; - rotation = <270>; - backlight = <&backlight>; - }; diff --git a/Documentation/devicetree/bindings/display/panel/ilitek,ili9341.yaml b/Documentation/devicetree/bindings/display/panel/ilitek,ili9341.yaml index 6058948a9764..99e0cb9440cf 100644 --- a/Documentation/devicetree/bindings/display/panel/ilitek,ili9341.yaml +++ b/Documentation/devicetree/bindings/display/panel/ilitek,ili9341.yaml @@ -21,8 +21,10 @@ properties: compatible: items: - enum: + - adafruit,yx240qv29 # ili9341 240*320 Color on stm32f429-disco board - st,sf-tc240t-9370-t + - canaan,kd233-tft - const: ilitek,ili9341 reg: true @@ -47,31 +49,50 @@ properties: vddi-led-supply: description: Voltage supply for the LED driver (1.65 .. 3.3 V) -additionalProperties: false +unevaluatedProperties: false required: - compatible - reg - dc-gpios - - port + +if: + properties: + compatible: + contains: + enum: + - st,sf-tc240t-9370-t +then: + required: + - port examples: - |+ + #include <dt-bindings/gpio/gpio.h> spi { #address-cells = <1>; #size-cells = <0>; panel: display@0 { - compatible = "st,sf-tc240t-9370-t", - "ilitek,ili9341"; - reg = <0>; - spi-3wire; - spi-max-frequency = <10000000>; - dc-gpios = <&gpiod 13 0>; - port { - panel_in: endpoint { - remote-endpoint = <&display_out>; - }; - }; - }; + compatible = "st,sf-tc240t-9370-t", + "ilitek,ili9341"; + reg = <0>; + spi-3wire; + spi-max-frequency = <10000000>; + dc-gpios = <&gpiod 13 0>; + port { + panel_in: endpoint { + remote-endpoint = <&display_out>; + }; + }; + }; + display@1{ + compatible = "adafruit,yx240qv29", "ilitek,ili9341"; + reg = <1>; + spi-max-frequency = <10000000>; + dc-gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio0 8 GPIO_ACTIVE_HIGH>; + rotation = <270>; + backlight = <&backlight>; }; + }; ... diff --git a/Documentation/devicetree/bindings/display/simple-framebuffer.yaml b/Documentation/devicetree/bindings/display/simple-framebuffer.yaml index 27ba4323d221..1f905d85dd9c 100644 --- a/Documentation/devicetree/bindings/display/simple-framebuffer.yaml +++ b/Documentation/devicetree/bindings/display/simple-framebuffer.yaml @@ -7,7 +7,6 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Simple Framebuffer Device Tree Bindings maintainers: - - Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> - Hans de Goede <hdegoede@redhat.com> description: |+ diff --git a/Documentation/devicetree/bindings/gpio/sifive,gpio.yaml b/Documentation/devicetree/bindings/gpio/sifive,gpio.yaml index 939e31c48081..fc095646adea 100644 --- a/Documentation/devicetree/bindings/gpio/sifive,gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/sifive,gpio.yaml @@ -46,6 +46,10 @@ properties: maximum: 32 default: 16 + gpio-line-names: + minItems: 1 + maxItems: 32 + gpio-controller: true required: diff --git a/Documentation/devicetree/bindings/hwmon/adi,adm1177.yaml b/Documentation/devicetree/bindings/hwmon/adi,adm1177.yaml index 154bee851139..d794deb08bb7 100644 --- a/Documentation/devicetree/bindings/hwmon/adi,adm1177.yaml +++ b/Documentation/devicetree/bindings/hwmon/adi,adm1177.yaml @@ -8,7 +8,6 @@ title: Analog Devices ADM1177 Hot Swap Controller and Digital Power Monitor maintainers: - Michael Hennerich <michael.hennerich@analog.com> - - Beniamin Bia <beniamin.bia@analog.com> description: | Analog Devices ADM1177 Hot Swap Controller and Digital Power Monitor diff --git a/Documentation/devicetree/bindings/iio/accel/fsl,mma7455.yaml b/Documentation/devicetree/bindings/iio/accel/fsl,mma7455.yaml index 7c8f8bdc2333..9c7c66feeffc 100644 --- a/Documentation/devicetree/bindings/iio/accel/fsl,mma7455.yaml +++ b/Documentation/devicetree/bindings/iio/accel/fsl,mma7455.yaml @@ -7,7 +7,6 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Freescale MMA7455 and MMA7456 three axis accelerometers maintainers: - - Joachim Eastwood <manabian@gmail.com> - Jonathan Cameron <jic23@kernel.org> description: diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7091r5.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7091r5.yaml index 31ffa275f5fa..b97559f23b3a 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7091r5.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7091r5.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Analog Devices AD7091R5 4-Channel 12-Bit ADC maintainers: - - Beniamin Bia <beniamin.bia@analog.com> + - Michael Hennerich <michael.hennerich@analog.com> description: | Analog Devices AD7091R5 4-Channel 12-Bit ADC diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml index 73775174cf57..516fc24d3346 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml @@ -7,8 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Analog Devices AD7606 Simultaneous Sampling ADC maintainers: - - Beniamin Bia <beniamin.bia@analog.com> - - Stefan Popa <stefan.popa@analog.com> + - Michael Hennerich <michael.hennerich@analog.com> description: | Analog Devices AD7606 Simultaneous Sampling ADC diff --git a/Documentation/devicetree/bindings/iio/adc/nxp,lpc1850-adc.yaml b/Documentation/devicetree/bindings/iio/adc/nxp,lpc1850-adc.yaml index 6404fb73f8ed..43abb300fa3d 100644 --- a/Documentation/devicetree/bindings/iio/adc/nxp,lpc1850-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/nxp,lpc1850-adc.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: NXP LPC1850 ADC bindings maintainers: - - Joachim Eastwood <manabian@gmail.com> + - Jonathan Cameron <jic23@kernel.org> description: Supports the ADC found on the LPC1850 SoC. diff --git a/Documentation/devicetree/bindings/iio/adc/ti,adc108s102.yaml b/Documentation/devicetree/bindings/iio/adc/ti,adc108s102.yaml index 54955f03df93..ae5ce60987fe 100644 --- a/Documentation/devicetree/bindings/iio/adc/ti,adc108s102.yaml +++ b/Documentation/devicetree/bindings/iio/adc/ti,adc108s102.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Texas Instruments ADC108S102 and ADC128S102 maintainers: - - Bogdan Pricop <bogdan.pricop@emutex.com> + - Jonathan Cameron <jic23@kernel.org> description: | Family of 8 channel, 10/12 bit, SPI, single ended ADCs. diff --git a/Documentation/devicetree/bindings/iio/adc/ti,ads124s08.yaml b/Documentation/devicetree/bindings/iio/adc/ti,ads124s08.yaml index 9f5e96439c01..2e6abc9d746a 100644 --- a/Documentation/devicetree/bindings/iio/adc/ti,ads124s08.yaml +++ b/Documentation/devicetree/bindings/iio/adc/ti,ads124s08.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Texas Instruments' ads124s08 and ads124s06 ADC chip maintainers: - - Dan Murphy <dmurphy@ti.com> + - Andrew Davis <afd@ti.com> properties: compatible: diff --git a/Documentation/devicetree/bindings/iio/amplifiers/adi,hmc425a.yaml b/Documentation/devicetree/bindings/iio/amplifiers/adi,hmc425a.yaml index a557761d8016..9fda56fa49c3 100644 --- a/Documentation/devicetree/bindings/iio/amplifiers/adi,hmc425a.yaml +++ b/Documentation/devicetree/bindings/iio/amplifiers/adi,hmc425a.yaml @@ -8,7 +8,6 @@ title: HMC425A 6-bit Digital Step Attenuator maintainers: - Michael Hennerich <michael.hennerich@analog.com> - - Beniamin Bia <beniamin.bia@analog.com> description: | Digital Step Attenuator IIO device with gpio interface. diff --git a/Documentation/devicetree/bindings/iio/imu/nxp,fxos8700.yaml b/Documentation/devicetree/bindings/iio/imu/nxp,fxos8700.yaml index 479e7065d4eb..0203b83b8587 100644 --- a/Documentation/devicetree/bindings/iio/imu/nxp,fxos8700.yaml +++ b/Documentation/devicetree/bindings/iio/imu/nxp,fxos8700.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Freescale FXOS8700 Inertial Measurement Unit maintainers: - - Robert Jones <rjones@gateworks.com> + - Jonathan Cameron <jic23@kernel.org> description: | Accelerometer and magnetometer combo device with an i2c and SPI interface. diff --git a/Documentation/devicetree/bindings/input/adc-joystick.yaml b/Documentation/devicetree/bindings/input/adc-joystick.yaml index 2ee04e03bc22..64d961458ac7 100644 --- a/Documentation/devicetree/bindings/input/adc-joystick.yaml +++ b/Documentation/devicetree/bindings/input/adc-joystick.yaml @@ -45,6 +45,7 @@ additionalProperties: false patternProperties: "^axis@[0-9a-f]+$": type: object + $ref: input.yaml# description: > Represents a joystick axis bound to the given ADC channel. For each entry in the io-channels list, one axis subnode with a matching @@ -57,7 +58,6 @@ patternProperties: description: Index of an io-channels list entry bound to this axis. linux,code: - $ref: /schemas/types.yaml#/definitions/uint32 description: EV_ABS specific event code generated by the axis. abs-range: diff --git a/Documentation/devicetree/bindings/input/adc-keys.txt b/Documentation/devicetree/bindings/input/adc-keys.txt deleted file mode 100644 index 6c8be6a9ace2..000000000000 --- a/Documentation/devicetree/bindings/input/adc-keys.txt +++ /dev/null @@ -1,67 +0,0 @@ -ADC attached resistor ladder buttons ------------------------------------- - -Required properties: - - compatible: "adc-keys" - - io-channels: Phandle to an ADC channel - - io-channel-names = "buttons"; - - keyup-threshold-microvolt: Voltage above or equal to which all the keys are - considered up. - -Optional properties: - - poll-interval: Poll interval time in milliseconds - - autorepeat: Boolean, Enable auto repeat feature of Linux input - subsystem. - -Each button (key) is represented as a sub-node of "adc-keys": - -Required subnode-properties: - - label: Descriptive name of the key. - - linux,code: Keycode to emit. - - press-threshold-microvolt: voltage above or equal to which this key is - considered pressed. - -No two values of press-threshold-microvolt may be the same. -All values of press-threshold-microvolt must be less than -keyup-threshold-microvolt. - -Example: - -#include <dt-bindings/input/input.h> - - adc-keys { - compatible = "adc-keys"; - io-channels = <&lradc 0>; - io-channel-names = "buttons"; - keyup-threshold-microvolt = <2000000>; - - button-up { - label = "Volume Up"; - linux,code = <KEY_VOLUMEUP>; - press-threshold-microvolt = <1500000>; - }; - - button-down { - label = "Volume Down"; - linux,code = <KEY_VOLUMEDOWN>; - press-threshold-microvolt = <1000000>; - }; - - button-enter { - label = "Enter"; - linux,code = <KEY_ENTER>; - press-threshold-microvolt = <500000>; - }; - }; - -+--------------------------------+------------------------+ -| 2.000.000 <= value | no key pressed | -+--------------------------------+------------------------+ -| 1.500.000 <= value < 2.000.000 | KEY_VOLUMEUP pressed | -+--------------------------------+------------------------+ -| 1.000.000 <= value < 1.500.000 | KEY_VOLUMEDOWN pressed | -+--------------------------------+------------------------+ -| 500.000 <= value < 1.000.000 | KEY_ENTER pressed | -+--------------------------------+------------------------+ -| value < 500.000 | no key pressed | -+--------------------------------+------------------------+ diff --git a/Documentation/devicetree/bindings/input/adc-keys.yaml b/Documentation/devicetree/bindings/input/adc-keys.yaml new file mode 100644 index 000000000000..7aa078dead37 --- /dev/null +++ b/Documentation/devicetree/bindings/input/adc-keys.yaml @@ -0,0 +1,103 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/adc-keys.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ADC attached resistor ladder buttons + +maintainers: + - Alexandre Belloni <alexandre.belloni@bootlin.com> + +allOf: + - $ref: input.yaml# + +properties: + compatible: + const: adc-keys + + io-channels: + maxItems: 1 + + io-channel-names: + const: buttons + + keyup-threshold-microvolt: + description: + Voltage above or equal to which all the keys are considered up. + + poll-interval: true + autorepeat: true + +patternProperties: + '^button-': + type: object + $ref: input.yaml# + additionalProperties: false + description: + Each button (key) is represented as a sub-node. + + properties: + label: true + + linux,code: true + + press-threshold-microvolt: + description: + Voltage above or equal to which this key is considered pressed. No + two values of press-threshold-microvolt may be the same. All values + of press-threshold-microvolt must be less than + keyup-threshold-microvolt. + + required: + - linux,code + - press-threshold-microvolt + +required: + - compatible + - io-channels + - io-channel-names + - keyup-threshold-microvolt + +additionalProperties: false + +examples: + - | + #include <dt-bindings/input/input.h> + // +--------------------------------+------------------------+ + // | 2.000.000 <= value | no key pressed | + // +--------------------------------+------------------------+ + // | 1.500.000 <= value < 2.000.000 | KEY_VOLUMEUP pressed | + // +--------------------------------+------------------------+ + // | 1.000.000 <= value < 1.500.000 | KEY_VOLUMEDOWN pressed | + // +--------------------------------+------------------------+ + // | 500.000 <= value < 1.000.000 | KEY_ENTER pressed | + // +--------------------------------+------------------------+ + // | value < 500.000 | no key pressed | + // +--------------------------------+------------------------+ + + adc-keys { + compatible = "adc-keys"; + io-channels = <&lradc 0>; + io-channel-names = "buttons"; + keyup-threshold-microvolt = <2000000>; + + button-up { + label = "Volume Up"; + linux,code = <KEY_VOLUMEUP>; + press-threshold-microvolt = <1500000>; + }; + + button-down { + label = "Volume Down"; + linux,code = <KEY_VOLUMEDOWN>; + press-threshold-microvolt = <1000000>; + }; + + button-enter { + label = "Enter"; + linux,code = <KEY_ENTER>; + press-threshold-microvolt = <500000>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/input/allwinner,sun4i-a10-lradc-keys.yaml b/Documentation/devicetree/bindings/input/allwinner,sun4i-a10-lradc-keys.yaml index 3399fc288afb..9700dc468b25 100644 --- a/Documentation/devicetree/bindings/input/allwinner,sun4i-a10-lradc-keys.yaml +++ b/Documentation/devicetree/bindings/input/allwinner,sun4i-a10-lradc-keys.yaml @@ -44,14 +44,13 @@ properties: patternProperties: "^button-[0-9]+$": type: object + $ref: input.yaml# properties: label: $ref: /schemas/types.yaml#/definitions/string description: Descriptive name of the key - linux,code: - $ref: /schemas/types.yaml#/definitions/uint32 - description: Keycode to emit + linux,code: true channel: $ref: /schemas/types.yaml#/definitions/uint32 diff --git a/Documentation/devicetree/bindings/input/ariel-pwrbutton.yaml b/Documentation/devicetree/bindings/input/ariel-pwrbutton.yaml index b4ad829d7383..442f623bb294 100644 --- a/Documentation/devicetree/bindings/input/ariel-pwrbutton.yaml +++ b/Documentation/devicetree/bindings/input/ariel-pwrbutton.yaml @@ -17,6 +17,7 @@ description: | allOf: - $ref: input.yaml# + - $ref: /schemas/spi/spi-peripheral-props.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/input/azoteq,iqs7222.yaml b/Documentation/devicetree/bindings/input/azoteq,iqs7222.yaml index a3a1e5a65306..02e605fac408 100644 --- a/Documentation/devicetree/bindings/input/azoteq,iqs7222.yaml +++ b/Documentation/devicetree/bindings/input/azoteq,iqs7222.yaml @@ -37,10 +37,6 @@ properties: device is temporarily held in hardware reset prior to initialization if this property is present. - azoteq,rf-filt-enable: - type: boolean - description: Enables the device's internal RF filter. - azoteq,max-counts: $ref: /schemas/types.yaml#/definitions/uint32 enum: [0, 1, 2, 3] @@ -421,6 +417,7 @@ patternProperties: patternProperties: "^event-(prox|touch)$": type: object + $ref: input.yaml# description: Represents a proximity or touch event reported by the channel. @@ -467,14 +464,9 @@ patternProperties: The IQS7222B does not feature channel-specific timeouts; the time- out specified for any one channel applies to all channels. - linux,code: - $ref: /schemas/types.yaml#/definitions/uint32 - description: - Numeric key or switch code associated with the event. Specify - KEY_RESERVED (0) to opt out of event reporting. + linux,code: true linux,input-type: - $ref: /schemas/types.yaml#/definitions/uint32 enum: [1, 5] default: 1 description: @@ -537,9 +529,8 @@ patternProperties: azoteq,bottom-speed: $ref: /schemas/types.yaml#/definitions/uint32 - multipleOf: 4 minimum: 0 - maximum: 1020 + maximum: 255 description: Specifies the speed of movement after which coordinate filtering is linearly reduced. @@ -575,14 +566,13 @@ patternProperties: patternProperties: "^event-(press|tap|(swipe|flick)-(pos|neg))$": type: object + $ref: input.yaml# description: Represents a press or gesture (IQS7222A only) event reported by the slider. properties: - linux,code: - $ref: /schemas/types.yaml#/definitions/uint32 - description: Numeric key code associated with the event. + linux,code: true azoteq,gesture-max-ms: multipleOf: 4 @@ -616,16 +606,15 @@ patternProperties: azoteq,gpio-select: $ref: /schemas/types.yaml#/definitions/uint32-array minItems: 1 - maxItems: 1 + maxItems: 3 items: minimum: 0 - maximum: 0 + maximum: 2 description: | - Specifies an individual GPIO mapped to a tap, swipe or flick - gesture as follows: + Specifies one or more GPIO mapped to the event as follows: 0: GPIO0 - 1: GPIO3 (reserved) - 2: GPIO4 (reserved) + 1: GPIO3 (IQS7222C only) + 2: GPIO4 (IQS7222C only) Note that although multiple events can be mapped to a single GPIO, they must all be of the same type (proximity, touch or @@ -710,6 +699,14 @@ allOf: multipleOf: 4 maximum: 1020 + patternProperties: + "^event-(press|tap|(swipe|flick)-(pos|neg))$": + properties: + azoteq,gpio-select: + maxItems: 1 + items: + maximum: 0 + else: patternProperties: "^channel-([0-9]|1[0-9])$": @@ -726,8 +723,6 @@ allOf: azoteq,gesture-dist: false - azoteq,gpio-select: false - required: - compatible - reg diff --git a/Documentation/devicetree/bindings/input/fsl,mpr121-touchkey.yaml b/Documentation/devicetree/bindings/input/fsl,mpr121-touchkey.yaml index 878464f128dc..5139af287d3e 100644 --- a/Documentation/devicetree/bindings/input/fsl,mpr121-touchkey.yaml +++ b/Documentation/devicetree/bindings/input/fsl,mpr121-touchkey.yaml @@ -57,7 +57,7 @@ examples: #address-cells = <1>; #size-cells = <0>; - mpr121@5a { + touchkey@5a { compatible = "fsl,mpr121-touchkey"; reg = <0x5a>; interrupt-parent = <&gpio1>; @@ -77,7 +77,7 @@ examples: #address-cells = <1>; #size-cells = <0>; - mpr121@5a { + touchkey@5a { compatible = "fsl,mpr121-touchkey"; reg = <0x5a>; poll-interval = <20>; diff --git a/Documentation/devicetree/bindings/input/gpio-keys.yaml b/Documentation/devicetree/bindings/input/gpio-keys.yaml index 7fe1966ea28a..17ac9dff7972 100644 --- a/Documentation/devicetree/bindings/input/gpio-keys.yaml +++ b/Documentation/devicetree/bindings/input/gpio-keys.yaml @@ -15,107 +15,106 @@ properties: - gpio-keys - gpio-keys-polled + autorepeat: true + + label: + description: Name of entire device + + poll-interval: true + patternProperties: - ".*": - if: - type: object - then: - $ref: input.yaml# + "^(button|event|key|switch|(button|event|key|switch)-[a-z0-9-]+|[a-z0-9-]+-(button|event|key|switch))$": + $ref: input.yaml# - properties: - gpios: - maxItems: 1 + properties: + gpios: + maxItems: 1 + + interrupts: + maxItems: 1 - interrupts: - maxItems: 1 + label: + description: Descriptive name of the key. - label: - description: Descriptive name of the key. + linux,code: + description: Key / Axis code to emit. - linux,code: - description: Key / Axis code to emit. - $ref: /schemas/types.yaml#/definitions/uint32 + linux,input-type: + default: 1 # EV_KEY - linux,input-type: - description: - Specify event type this button/key generates. If not specified defaults to - <1> == EV_KEY. - $ref: /schemas/types.yaml#/definitions/uint32 + linux,input-value: + description: | + If linux,input-type is EV_ABS or EV_REL then this + value is sent for events this button generates when pressed. + EV_ABS/EV_REL axis will generate an event with a value of 0 + when all buttons with linux,input-type == type and + linux,code == axis are released. This value is interpreted + as a signed 32 bit value, e.g. to make a button generate a + value of -1 use: - default: 1 + linux,input-value = <0xffffffff>; /* -1 */ - linux,input-value: - description: | - If linux,input-type is EV_ABS or EV_REL then this - value is sent for events this button generates when pressed. - EV_ABS/EV_REL axis will generate an event with a value of 0 - when all buttons with linux,input-type == type and - linux,code == axis are released. This value is interpreted - as a signed 32 bit value, e.g. to make a button generate a - value of -1 use: + $ref: /schemas/types.yaml#/definitions/uint32 - linux,input-value = <0xffffffff>; /* -1 */ + debounce-interval: + description: + Debouncing interval time in milliseconds. If not specified defaults to 5. + $ref: /schemas/types.yaml#/definitions/uint32 - $ref: /schemas/types.yaml#/definitions/uint32 + default: 5 - debounce-interval: - description: - Debouncing interval time in milliseconds. If not specified defaults to 5. - $ref: /schemas/types.yaml#/definitions/uint32 + wakeup-source: + description: Button can wake-up the system. - default: 5 + wakeup-event-action: + description: | + Specifies whether the key should wake the system when asserted, when + deasserted, or both. This property is only valid for keys that wake up the + system (e.g., when the "wakeup-source" property is also provided). - wakeup-source: - description: Button can wake-up the system. + Supported values are defined in linux-event-codes.h: - wakeup-event-action: - description: | - Specifies whether the key should wake the system when asserted, when - deasserted, or both. This property is only valid for keys that wake up the - system (e.g., when the "wakeup-source" property is also provided). + EV_ACT_ANY - both asserted and deasserted + EV_ACT_ASSERTED - asserted + EV_ACT_DEASSERTED - deasserted + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2] - Supported values are defined in linux-event-codes.h: + linux,can-disable: + description: + Indicates that button is connected to dedicated (not shared) interrupt + which can be disabled to suppress events from the button. + type: boolean - EV_ACT_ANY - both asserted and deasserted - EV_ACT_ASSERTED - asserted - EV_ACT_DEASSERTED - deasserted - $ref: /schemas/types.yaml#/definitions/uint32 - enum: [0, 1, 2] + required: + - linux,code - linux,can-disable: - description: - Indicates that button is connected to dedicated (not shared) interrupt - which can be disabled to suppress events from the button. - type: boolean + anyOf: + - required: + - interrupts + - required: + - interrupts-extended + - required: + - gpios + dependencies: + wakeup-event-action: [ wakeup-source ] + linux,input-value: [ gpios ] + + unevaluatedProperties: false + +allOf: + - $ref: input.yaml# + - if: + properties: + compatible: + const: gpio-keys-polled + then: required: - - linux,code - - anyOf: - - required: - - interrupts - - required: - - gpios - - dependencies: - wakeup-event-action: [ wakeup-source ] - linux,input-value: [ gpios ] - - unevaluatedProperties: false - -if: - properties: - compatible: - const: gpio-keys-polled -then: - properties: - poll-interval: - description: - Poll interval time in milliseconds - $ref: /schemas/types.yaml#/definitions/uint32 - - required: - - poll-interval + - poll-interval + else: + properties: + poll-interval: false additionalProperties: false @@ -127,13 +126,13 @@ examples: compatible = "gpio-keys"; autorepeat; - up { + key-up { label = "GPIO Key UP"; linux,code = <103>; gpios = <&gpio1 0 1>; }; - down { + key-down { label = "GPIO Key DOWN"; linux,code = <108>; interrupts = <1 IRQ_TYPE_EDGE_FALLING>; diff --git a/Documentation/devicetree/bindings/input/input.yaml b/Documentation/devicetree/bindings/input/input.yaml index d41d8743aad4..17512f4347fd 100644 --- a/Documentation/devicetree/bindings/input/input.yaml +++ b/Documentation/devicetree/bindings/input/input.yaml @@ -21,7 +21,26 @@ properties: $ref: /schemas/types.yaml#/definitions/uint32-array items: minimum: 0 - maximum: 0xff + maximum: 0x2ff + + linux,code: + description: + Specifies a single numeric keycode value to be used for reporting + button/switch events. Specify KEY_RESERVED (0) to opt out of event + reporting. + $ref: /schemas/types.yaml#/definitions/uint32 + maximum: 0x2ff + + linux,input-type: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: + - 1 # EV_KEY + - 2 # EV_REL + - 3 # EV_ABS + - 5 # EV_SW + description: + Specifies whether the event is to be interpreted as a key, relative, + absolute, or switch. poll-interval: description: Poll interval time in milliseconds. @@ -39,4 +58,7 @@ properties: reset automatically. Device with key pressed reset feature can specify this property. +dependencies: + linux,input-type: [ "linux,code" ] + additionalProperties: true diff --git a/Documentation/devicetree/bindings/input/iqs269a.yaml b/Documentation/devicetree/bindings/input/iqs269a.yaml index 9c154e5e1a91..3c430d38594f 100644 --- a/Documentation/devicetree/bindings/input/iqs269a.yaml +++ b/Documentation/devicetree/bindings/input/iqs269a.yaml @@ -370,6 +370,7 @@ patternProperties: patternProperties: "^event-prox(-alt)?$": type: object + $ref: input.yaml# description: Represents a proximity event reported by the channel in response to a decrease in counts. Node names suffixed with '-alt' instead corre- @@ -396,14 +397,13 @@ patternProperties: default: 10 description: Specifies the threshold for the event. - linux,code: - $ref: /schemas/types.yaml#/definitions/uint32 - description: Numeric key or switch code associated with the event. + linux,code: true additionalProperties: false "^event-touch(-alt)?$": type: object + $ref: input.yaml# description: Represents a touch event reported by the channel. properties: @@ -421,14 +421,13 @@ patternProperties: default: 4 description: Specifies the hysteresis for the event. - linux,code: - $ref: /schemas/types.yaml#/definitions/uint32 - description: Numeric key or switch code associated with the event. + linux,code: true additionalProperties: false "^event-deep(-alt)?$": type: object + $ref: input.yaml# description: Represents a deep-touch event reported by the channel. properties: @@ -446,9 +445,7 @@ patternProperties: default: 0 description: Specifies the hysteresis for the event. - linux,code: - $ref: /schemas/types.yaml#/definitions/uint32 - description: Numeric key or switch code associated with the event. + linux,code: true additionalProperties: false @@ -475,7 +472,7 @@ examples: #address-cells = <1>; #size-cells = <0>; - iqs269a@44 { + touch@44 { #address-cells = <1>; #size-cells = <0>; diff --git a/Documentation/devicetree/bindings/input/iqs626a.yaml b/Documentation/devicetree/bindings/input/iqs626a.yaml index 0cb736c541c9..7a27502095f3 100644 --- a/Documentation/devicetree/bindings/input/iqs626a.yaml +++ b/Documentation/devicetree/bindings/input/iqs626a.yaml @@ -449,6 +449,7 @@ patternProperties: patternProperties: "^event-(prox|touch|deep)(-alt)?$": type: object + $ref: input.yaml# description: Represents a proximity, touch or deep-touch event reported by the channel in response to a decrease in counts. Node names suffixed with @@ -487,21 +488,15 @@ patternProperties: Specifies the hysteresis for the event (touch and deep-touch events only). - linux,code: - $ref: /schemas/types.yaml#/definitions/uint32 - description: Numeric key or switch code associated with the event. + linux,code: true linux,input-type: - $ref: /schemas/types.yaml#/definitions/uint32 enum: [1, 5] description: Specifies whether the event is to be interpreted as a key (1) or a switch (5). By default, Hall-channel events are interpreted as switches and all others are interpreted as keys. - dependencies: - linux,input-type: ["linux,code"] - additionalProperties: false dependencies: @@ -511,6 +506,7 @@ patternProperties: "^trackpad-3x[2-3]$": type: object + $ref: input.yaml# description: Represents all channels associated with the trackpad. The channels are collectively active if the trackpad is defined and inactive otherwise. @@ -679,7 +675,6 @@ patternProperties: Specifies the raw count filter strength during low-power mode. linux,keycodes: - $ref: /schemas/types.yaml#/definitions/uint32-array minItems: 1 maxItems: 6 description: | @@ -751,7 +746,7 @@ examples: #address-cells = <1>; #size-cells = <0>; - iqs626a@44 { + touch@44 { #address-cells = <1>; #size-cells = <0>; diff --git a/Documentation/devicetree/bindings/input/iqs62x-keys.yaml b/Documentation/devicetree/bindings/input/iqs62x-keys.yaml index 77fe3b545b35..0aa951f0ab92 100644 --- a/Documentation/devicetree/bindings/input/iqs62x-keys.yaml +++ b/Documentation/devicetree/bindings/input/iqs62x-keys.yaml @@ -9,6 +9,9 @@ title: Azoteq IQS620A/621/622/624/625 Keys and Switches maintainers: - Jeff LaBundy <jeff@labundy.com> +allOf: + - $ref: input.yaml# + description: | The Azoteq IQS620A, IQS621, IQS622, IQS624 and IQS625 multi-function sensors feature a variety of self-capacitive, mutual-inductive and Hall-effect sens- @@ -30,7 +33,6 @@ properties: - azoteq,iqs625-keys linux,keycodes: - $ref: /schemas/types.yaml#/definitions/uint32-array minItems: 1 maxItems: 16 description: | @@ -89,15 +91,14 @@ properties: patternProperties: "^hall-switch-(north|south)$": type: object + $ref: input.yaml# description: Represents north/south-field Hall-effect sensor touch or proximity events. Note that north/south-field orientation is reversed on the IQS620AXzCSR device due to its flip-chip package. properties: - linux,code: - $ref: /schemas/types.yaml#/definitions/uint32 - description: Numeric switch code associated with the event. + linux,code: true azoteq,use-prox: $ref: /schemas/types.yaml#/definitions/flag diff --git a/Documentation/devicetree/bindings/input/max77650-onkey.yaml b/Documentation/devicetree/bindings/input/max77650-onkey.yaml index 3a2ad6ec64db..48edc0c8c1dd 100644 --- a/Documentation/devicetree/bindings/input/max77650-onkey.yaml +++ b/Documentation/devicetree/bindings/input/max77650-onkey.yaml @@ -16,15 +16,15 @@ description: | The onkey controller is represented as a sub-node of the PMIC node on the device tree. +allOf: + - $ref: input.yaml# + properties: compatible: const: maxim,max77650-onkey linux,code: - $ref: /schemas/types.yaml#/definitions/uint32 - description: - The key-code to be reported when the key is pressed. Defaults - to KEY_POWER. + default: 116 # KEY_POWER maxim,onkey-slide: $ref: /schemas/types.yaml#/definitions/flag diff --git a/Documentation/devicetree/bindings/input/microchip,cap11xx.yaml b/Documentation/devicetree/bindings/input/microchip,cap11xx.yaml index d5d6bced3148..96358b12f9b2 100644 --- a/Documentation/devicetree/bindings/input/microchip,cap11xx.yaml +++ b/Documentation/devicetree/bindings/input/microchip,cap11xx.yaml @@ -112,7 +112,7 @@ examples: #address-cells = <1>; #size-cells = <0>; - cap1188@28 { + touch@28 { compatible = "microchip,cap1188"; interrupt-parent = <&gpio1>; interrupts = <0 0>; diff --git a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml index 2e8da7470513..46bc8c028fe6 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml +++ b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml @@ -85,6 +85,14 @@ properties: minimum: 0 maximum: 80 + report-rate-hz: + description: | + Allows setting the scan rate in Hertz. + M06 supports range from 30 to 140 Hz. + M12 supports range from 1 to 255 Hz. + minimum: 1 + maximum: 255 + touchscreen-size-x: true touchscreen-size-y: true touchscreen-fuzz-x: true diff --git a/Documentation/devicetree/bindings/leds/leds-class-multicolor.yaml b/Documentation/devicetree/bindings/leds/leds-class-multicolor.yaml index 12693483231f..31840e33dcf5 100644 --- a/Documentation/devicetree/bindings/leds/leds-class-multicolor.yaml +++ b/Documentation/devicetree/bindings/leds/leds-class-multicolor.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Common properties for the multicolor LED class. maintainers: - - Dan Murphy <dmurphy@ti.com> + - Andrew Davis <afd@ti.com> description: | Bindings for multi color LEDs show how to describe current outputs of diff --git a/Documentation/devicetree/bindings/leds/leds-lp50xx.yaml b/Documentation/devicetree/bindings/leds/leds-lp50xx.yaml index e0b658f07973..63da380748bf 100644 --- a/Documentation/devicetree/bindings/leds/leds-lp50xx.yaml +++ b/Documentation/devicetree/bindings/leds/leds-lp50xx.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: LED driver for LP50XX RGB LED from Texas Instruments. maintainers: - - Dan Murphy <dmurphy@ti.com> + - Andrew Davis <afd@ti.com> description: | The LP50XX is multi-channel, I2C RGB LED Drivers that can group RGB LEDs into diff --git a/Documentation/devicetree/bindings/mailbox/arm,mhu.yaml b/Documentation/devicetree/bindings/mailbox/arm,mhu.yaml index bd49c201477d..d9a4f4a02d7c 100644 --- a/Documentation/devicetree/bindings/mailbox/arm,mhu.yaml +++ b/Documentation/devicetree/bindings/mailbox/arm,mhu.yaml @@ -57,6 +57,7 @@ properties: maxItems: 1 interrupts: + minItems: 2 items: - description: low-priority non-secure - description: high-priority non-secure diff --git a/Documentation/devicetree/bindings/memory-controllers/canaan,k210-sram.yaml b/Documentation/devicetree/bindings/memory-controllers/canaan,k210-sram.yaml new file mode 100644 index 000000000000..f81fb866e319 --- /dev/null +++ b/Documentation/devicetree/bindings/memory-controllers/canaan,k210-sram.yaml @@ -0,0 +1,52 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/memory-controllers/canaan,k210-sram.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Canaan K210 SRAM memory controller + +description: + The Canaan K210 SRAM memory controller is responsible for the system's 8 MiB + of SRAM. The controller is initialised by the bootloader, which configures + its clocks, before OS bringup. + +maintainers: + - Conor Dooley <conor@kernel.org> + +properties: + compatible: + enum: + - canaan,k210-sram + + clocks: + minItems: 1 + items: + - description: sram0 clock + - description: sram1 clock + - description: aisram clock + + clock-names: + minItems: 1 + items: + - const: sram0 + - const: sram1 + - const: aisram + +required: + - compatible + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/k210-clk.h> + memory-controller { + compatible = "canaan,k210-sram"; + clocks = <&sysclk K210_CLK_SRAM0>, + <&sysclk K210_CLK_SRAM1>, + <&sysclk K210_CLK_AI>; + clock-names = "sram0", "sram1", "aisram"; + }; diff --git a/Documentation/devicetree/bindings/mfd/gateworks-gsc.yaml b/Documentation/devicetree/bindings/mfd/gateworks-gsc.yaml index 5a1e8d21f7a0..5e0fe3ebe1d2 100644 --- a/Documentation/devicetree/bindings/mfd/gateworks-gsc.yaml +++ b/Documentation/devicetree/bindings/mfd/gateworks-gsc.yaml @@ -19,7 +19,6 @@ description: | maintainers: - Tim Harvey <tharvey@gateworks.com> - - Robert Jones <rjones@gateworks.com> properties: $nodename: diff --git a/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.txt b/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.txt deleted file mode 100644 index eb78e3ae7703..000000000000 --- a/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.txt +++ /dev/null @@ -1,94 +0,0 @@ - Qualcomm SPMI PMICs multi-function device bindings - -The Qualcomm SPMI series presently includes PM8941, PM8841 and PMA8084 -PMICs. These PMICs use a QPNP scheme through SPMI interface. -QPNP is effectively a partitioning scheme for dividing the SPMI extended -register space up into logical pieces, and set of fixed register -locations/definitions within these regions, with some of these regions -specifically used for interrupt handling. - -The QPNP PMICs are used with the Qualcomm Snapdragon series SoCs, and are -interfaced to the chip via the SPMI (System Power Management Interface) bus. -Support for multiple independent functions are implemented by splitting the -16-bit SPMI slave address space into 256 smaller fixed-size regions, 256 bytes -each. A function can consume one or more of these fixed-size register regions. - -Required properties: -- compatible: Should contain one of: - "qcom,pm660", - "qcom,pm660l", - "qcom,pm7325", - "qcom,pm8004", - "qcom,pm8005", - "qcom,pm8019", - "qcom,pm8028", - "qcom,pm8110", - "qcom,pm8150", - "qcom,pm8150b", - "qcom,pm8150c", - "qcom,pm8150l", - "qcom,pm8226", - "qcom,pm8350c", - "qcom,pm8841", - "qcom,pm8901", - "qcom,pm8909", - "qcom,pm8916", - "qcom,pm8941", - "qcom,pm8950", - "qcom,pm8953", - "qcom,pm8994", - "qcom,pm8998", - "qcom,pma8084", - "qcom,pmd9635", - "qcom,pmi8950", - "qcom,pmi8962", - "qcom,pmi8994", - "qcom,pmi8998", - "qcom,pmk8002", - "qcom,pmk8350", - "qcom,pmr735a", - "qcom,smb2351", - or generalized "qcom,spmi-pmic". -- reg: Specifies the SPMI USID slave address for this device. - For more information see: - Documentation/devicetree/bindings/spmi/spmi.yaml - -Required properties for peripheral child nodes: -- compatible: Should contain "qcom,xxx", where "xxx" is a peripheral name. - -Optional properties for peripheral child nodes: -- interrupts: Interrupts are specified as a 4-tuple. For more information - see: - Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.yaml -- interrupt-names: Corresponding interrupt name to the interrupts property - -Each child node of SPMI slave id represents a function of the PMIC. In the -example below the rtc device node represents a peripheral of pm8941 -SID = 0. The regulator device node represents a peripheral of pm8941 SID = 1. - -Example: - - spmi { - compatible = "qcom,spmi-pmic-arb"; - - pm8941@0 { - compatible = "qcom,pm8941", "qcom,spmi-pmic"; - reg = <0x0 SPMI_USID>; - - rtc { - compatible = "qcom,rtc"; - interrupts = <0x0 0x61 0x1 IRQ_TYPE_EDGE_RISING>; - interrupt-names = "alarm"; - }; - }; - - pm8941@1 { - compatible = "qcom,pm8941", "qcom,spmi-pmic"; - reg = <0x1 SPMI_USID>; - - regulator { - compatible = "qcom,regulator"; - regulator-name = "8941_boost"; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml b/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml new file mode 100644 index 000000000000..65cbc6dee545 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml @@ -0,0 +1,190 @@ +# SPDX-License-Identifier: GPL-2.0-only +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/qcom,spmi-pmic.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm SPMI PMICs multi-function device + +description: | + Some Qualcomm PMICs used with the Snapdragon series SoCs are interfaced + to the chip via the SPMI (System Power Management Interface) bus. + Support for multiple independent functions are implemented by splitting the + 16-bit SPMI peripheral address space into 256 smaller fixed-size regions, 256 bytes + each. A function can consume one or more of these fixed-size register regions. + + The Qualcomm SPMI series includes the PM8941, PM8841, PMA8084, PM8998 and other + PMICs. These PMICs use a "QPNP" scheme through SPMI interface. + QPNP is effectively a partitioning scheme for dividing the SPMI extended + register space up into logical pieces, and set of fixed register + locations/definitions within these regions, with some of these regions + specifically used for interrupt handling. + +maintainers: + - Stephen Boyd <sboyd@kernel.org> + +properties: + $nodename: + oneOf: + - pattern: '^pmic@.*$' + - pattern: '^pm(a|s)?[0-9]*@.*$' + deprecated: true + + compatible: + items: + - enum: + - qcom,pm660 + - qcom,pm660l + - qcom,pm6150 + - qcom,pm6150l + - qcom,pm6350 + - qcom,pm7325 + - qcom,pm8004 + - qcom,pm8005 + - qcom,pm8009 + - qcom,pm8019 + - qcom,pm8110 + - qcom,pm8150 + - qcom,pm8150b + - qcom,pm8150l + - qcom,pm8226 + - qcom,pm8350 + - qcom,pm8350b + - qcom,pm8350c + - qcom,pm8841 + - qcom,pm8909 + - qcom,pm8916 + - qcom,pm8941 + - qcom,pm8950 + - qcom,pm8994 + - qcom,pm8998 + - qcom,pma8084 + - qcom,pmd9635 + - qcom,pmi8950 + - qcom,pmi8962 + - qcom,pmi8994 + - qcom,pmi8998 + - qcom,pmk8350 + - qcom,pmm8155au + - qcom,pmr735a + - qcom,pmr735b + - qcom,pms405 + - qcom,pmx55 + - qcom,pmx65 + - qcom,smb2351 + - const: qcom,spmi-pmic + + reg: + minItems: 1 + maxItems: 2 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + labibb: + type: object + $ref: /schemas/regulator/qcom-labibb-regulator.yaml# + + regulators: + type: object + $ref: /schemas/regulator/regulator.yaml# + +patternProperties: + "^adc@[0-9a-f]+$": + type: object + $ref: /schemas/iio/adc/qcom,spmi-vadc.yaml# + + "^adc-tm@[0-9a-f]+$": + type: object + $ref: /schemas/thermal/qcom-spmi-adc-tm5.yaml# + + "^audio-codec@[0-9a-f]+$": + type: object + additionalProperties: true # FIXME qcom,pm8916-wcd-analog-codec binding not converted yet + + "extcon@[0-9a-f]+$": + type: object + $ref: /schemas/extcon/qcom,pm8941-misc.yaml# + + "gpio(s)?@[0-9a-f]+$": + type: object + $ref: /schemas/pinctrl/qcom,pmic-gpio.yaml# + + "pon@[0-9a-f]+$": + type: object + $ref: /schemas/power/reset/qcom,pon.yaml# + + "pwm@[0-9a-f]+$": + type: object + $ref: /schemas/leds/leds-qcom-lpg.yaml# + + "^rtc@[0-9a-f]+$": + type: object + $ref: /schemas/rtc/qcom-pm8xxx-rtc.yaml# + + "^temp-alarm@[0-9a-f]+$": + type: object + $ref: /schemas/thermal/qcom,spmi-temp-alarm.yaml# + + "^vibrator@[0-9a-f]+$": + type: object + additionalProperties: true # FIXME qcom,pm8916-vib binding not converted yet + + "^mpps@[0-9a-f]+$": + type: object + $ref: /schemas/pinctrl/qcom,pmic-mpp.yaml# + + "(.*)?(wled|leds)@[0-9a-f]+$": + type: object + $ref: /schemas/leds/backlight/qcom-wled.yaml# + unevaluatedProperties: false + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include <dt-bindings/spmi/spmi.h> + #include <dt-bindings/interrupt-controller/irq.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + + spmi@c440000 { + compatible = "qcom,spmi-pmic-arb"; + reg = <0x0c440000 0x1100>, + <0x0c600000 0x2000000>, + <0x0e600000 0x100000>, + <0x0e700000 0xa0000>, + <0x0c40a000 0x26000>; + reg-names = "core", "chnls", "obsrvr", "intr", "cnfg"; + interrupt-names = "periph_irq"; + interrupts = <GIC_SPI 481 IRQ_TYPE_LEVEL_HIGH>; + qcom,ee = <0>; + qcom,channel = <0>; + #address-cells = <2>; + #size-cells = <0>; + interrupt-controller; + #interrupt-cells = <4>; + + pmi8998_lsid0: pmic@2 { + compatible = "qcom,pmi8998", "qcom,spmi-pmic"; + reg = <0x2 SPMI_USID>; + #address-cells = <1>; + #size-cells = <0>; + + pmi8998_gpio: gpios@c000 { + compatible = "qcom,pmi8998-gpio", "qcom,spmi-gpio"; + reg = <0xc000>; + gpio-controller; + gpio-ranges = <&pmi8998_gpio 0 0 14>; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml b/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml index b00578ae1dea..fc0e81c2066c 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml +++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml @@ -137,6 +137,8 @@ properties: max-frequency: true + operating-points-v2: true + patternProperties: '^opp-table(-[a-z0-9]+)?$': if: diff --git a/Documentation/devicetree/bindings/net/ti,dp83822.yaml b/Documentation/devicetree/bindings/net/ti,dp83822.yaml index 75e8712e903a..f2489a9c852f 100644 --- a/Documentation/devicetree/bindings/net/ti,dp83822.yaml +++ b/Documentation/devicetree/bindings/net/ti,dp83822.yaml @@ -8,7 +8,7 @@ $schema: "http://devicetree.org/meta-schemas/core.yaml#" title: TI DP83822 ethernet PHY maintainers: - - Dan Murphy <dmurphy@ti.com> + - Andrew Davis <afd@ti.com> description: | The DP83822 is a low-power, single-port, 10/100 Mbps Ethernet PHY. It diff --git a/Documentation/devicetree/bindings/net/ti,dp83867.yaml b/Documentation/devicetree/bindings/net/ti,dp83867.yaml index 76ff08a477ba..b8c0e4b5b494 100644 --- a/Documentation/devicetree/bindings/net/ti,dp83867.yaml +++ b/Documentation/devicetree/bindings/net/ti,dp83867.yaml @@ -11,7 +11,7 @@ allOf: - $ref: "ethernet-controller.yaml#" maintainers: - - Dan Murphy <dmurphy@ti.com> + - Andrew Davis <afd@ti.com> description: | The DP83867 device is a robust, low power, fully featured Physical Layer diff --git a/Documentation/devicetree/bindings/net/ti,dp83869.yaml b/Documentation/devicetree/bindings/net/ti,dp83869.yaml index 1b780dce61ab..b04ff0014a59 100644 --- a/Documentation/devicetree/bindings/net/ti,dp83869.yaml +++ b/Documentation/devicetree/bindings/net/ti,dp83869.yaml @@ -11,7 +11,7 @@ allOf: - $ref: "ethernet-phy.yaml#" maintainers: - - Dan Murphy <dmurphy@ti.com> + - Andrew Davis <afd@ti.com> description: | The DP83869HM device is a robust, fully-featured Gigabit (PHY) transceiver diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,ipq6018-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,ipq6018-pinctrl.yaml index b83c7f476e19..931e5c190ead 100644 --- a/Documentation/devicetree/bindings/pinctrl/qcom,ipq6018-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/qcom,ipq6018-pinctrl.yaml @@ -144,7 +144,7 @@ examples: #interrupt-cells = <2>; gpio-controller; #gpio-cells = <2>; - gpio-ranges = <&tlmm 0 80>; + gpio-ranges = <&tlmm 0 0 80>; serial3-pinmux { pins = "gpio44", "gpio45"; diff --git a/Documentation/devicetree/bindings/power/reset/msm-poweroff.txt b/Documentation/devicetree/bindings/power/reset/msm-poweroff.txt deleted file mode 100644 index ce44ad357565..000000000000 --- a/Documentation/devicetree/bindings/power/reset/msm-poweroff.txt +++ /dev/null @@ -1,17 +0,0 @@ -MSM Restart Driver - -A power supply hold (ps-hold) bit is set to power the msm chipsets. -Clearing that bit allows us to restart/poweroff. The difference -between poweroff and restart is determined by unique power manager IC -settings. - -Required Properties: --compatible: "qcom,pshold" --reg: Specifies the physical address of the ps-hold register - -Example: - - restart@fc4ab000 { - compatible = "qcom,pshold"; - reg = <0xfc4ab000 0x4>; - }; diff --git a/Documentation/devicetree/bindings/power/reset/qcom,pshold.yaml b/Documentation/devicetree/bindings/power/reset/qcom,pshold.yaml new file mode 100644 index 000000000000..527962d54a8f --- /dev/null +++ b/Documentation/devicetree/bindings/power/reset/qcom,pshold.yaml @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/power/reset/qcom,pshold.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm SoC restart and power off + +maintainers: + - Bjorn Andersson <bjorn.andersson@linaro.org> + +description: + A power supply hold (ps-hold) bit is set to power the Qualcomm chipsets. + Clearing that bit allows us to restart/power off. The difference between + power off and restart is determined by unique power manager IC settings. + +properties: + compatible: + const: qcom,pshold + + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + reset-controller@fc4ab000 { + compatible = "qcom,pshold"; + reg = <0xfc4ab000 0x4>; + }; diff --git a/Documentation/devicetree/bindings/power/supply/bq24190.yaml b/Documentation/devicetree/bindings/power/supply/bq24190.yaml index 21a9dadc1c6a..4884ec90e2b8 100644 --- a/Documentation/devicetree/bindings/power/supply/bq24190.yaml +++ b/Documentation/devicetree/bindings/power/supply/bq24190.yaml @@ -28,7 +28,7 @@ properties: maxItems: 1 usb-otg-vbus: - type: object + $ref: /schemas/regulator/regulator.yaml# description: | Regulator that is used to control the VBUS voltage direction for either USB host mode or for charging on the OTG port diff --git a/Documentation/devicetree/bindings/power/supply/bq2515x.yaml b/Documentation/devicetree/bindings/power/supply/bq2515x.yaml index 27db38577822..1a1b240034ef 100644 --- a/Documentation/devicetree/bindings/power/supply/bq2515x.yaml +++ b/Documentation/devicetree/bindings/power/supply/bq2515x.yaml @@ -8,8 +8,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: TI bq2515x 500-mA Linear charger family maintainers: - - Dan Murphy <dmurphy@ti.com> - - Ricardo Rivera-Matos <r-rivera-matos@ti.com> + - Andrew Davis <afd@ti.com> description: | The BQ2515x family is a highly integrated battery charge management IC that diff --git a/Documentation/devicetree/bindings/power/supply/bq256xx.yaml b/Documentation/devicetree/bindings/power/supply/bq256xx.yaml index 91abe5733c41..82f382a7ffb3 100644 --- a/Documentation/devicetree/bindings/power/supply/bq256xx.yaml +++ b/Documentation/devicetree/bindings/power/supply/bq256xx.yaml @@ -8,7 +8,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: TI bq256xx Switch Mode Buck Charger maintainers: - - Ricardo Rivera-Matos <r-rivera-matos@ti.com> + - Andrew Davis <afd@ti.com> description: | The bq256xx devices are a family of highly-integrated battery charge diff --git a/Documentation/devicetree/bindings/power/supply/bq25980.yaml b/Documentation/devicetree/bindings/power/supply/bq25980.yaml index 4883527ab5c7..b687b8bcd705 100644 --- a/Documentation/devicetree/bindings/power/supply/bq25980.yaml +++ b/Documentation/devicetree/bindings/power/supply/bq25980.yaml @@ -8,8 +8,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: TI BQ25980 Flash Charger maintainers: - - Dan Murphy <dmurphy@ti.com> - - Ricardo Rivera-Matos <r-rivera-matos@ti.com> + - Andrew Davis <afd@ti.com> description: | The BQ25980, BQ25975, and BQ25960 are a series of flash chargers intended diff --git a/Documentation/devicetree/bindings/power/supply/qcom,pm8941-charger.yaml b/Documentation/devicetree/bindings/power/supply/qcom,pm8941-charger.yaml index caeff68c66d5..cbac55d3cb92 100644 --- a/Documentation/devicetree/bindings/power/supply/qcom,pm8941-charger.yaml +++ b/Documentation/devicetree/bindings/power/supply/qcom,pm8941-charger.yaml @@ -117,11 +117,18 @@ properties: be done externally to fully comply with the JEITA safety guidelines if this flag is set. + usb-charge-current-limit: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 100000 + maximum: 2500000 + description: | + Default USB charge current limit in uA. + usb-otg-in-supply: description: Reference to the regulator supplying power to the USB_OTG_IN pin. otg-vbus: - type: object + $ref: /schemas/regulator/regulator.yaml# description: | This node defines a regulator used to control the direction of VBUS voltage. Specifically whether to supply voltage to VBUS for host mode operation of the OTG port, diff --git a/Documentation/devicetree/bindings/power/supply/summit,smb347-charger.yaml b/Documentation/devicetree/bindings/power/supply/summit,smb347-charger.yaml index 0581497448ce..2d552becbfe6 100644 --- a/Documentation/devicetree/bindings/power/supply/summit,smb347-charger.yaml +++ b/Documentation/devicetree/bindings/power/supply/summit,smb347-charger.yaml @@ -82,7 +82,7 @@ properties: - 1 # SMB3XX_SYSOK_INOK_ACTIVE_HIGH usb-vbus: - $ref: "../../regulator/regulator.yaml#" + $ref: /schemas/regulator/regulator.yaml# type: object properties: diff --git a/Documentation/devicetree/bindings/reset/atmel,at91sam9260-reset.yaml b/Documentation/devicetree/bindings/reset/atmel,at91sam9260-reset.yaml new file mode 100644 index 000000000000..98465d26949e --- /dev/null +++ b/Documentation/devicetree/bindings/reset/atmel,at91sam9260-reset.yaml @@ -0,0 +1,68 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/reset/atmel,at91sam9260-reset.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Atmel/Microchip System Reset Controller + +maintainers: + - Claudiu Beznea <claudiu.beznea@microchip.com> + +description: | + The system reset controller can be used to reset the CPU. In case of + SAMA7G5 it can also reset some devices (e.g. USB PHYs). + +properties: + compatible: + oneOf: + - items: + - enum: + - atmel,at91sam9260-rstc + - atmel,at91sam9g45-rstc + - atmel,sama5d3-rstc + - microchip,sam9x60-rstc + - microchip,sama7g5-rstc + - items: + - const: atmel,sama5d3-rstc + - const: atmel,at91sam9g45-rstc + + reg: + minItems: 1 + items: + - description: base registers for system reset control + - description: registers for device specific reset control + + clocks: + maxItems: 1 + + "#reset-cells": + const: 1 + +required: + - compatible + - reg + - clocks + +allOf: + - if: + properties: + compatible: + contains: + enum: + - microchip,sama7g5-rstc + then: + required: + - "#reset-cells" + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/at91.h> + + reset-controller@fffffd00 { + compatible = "atmel,at91sam9260-rstc"; + reg = <0xfffffd00 0x10>; + clocks = <&pmc PMC_TYPE_CORE PMC_SLOW>; + }; diff --git a/Documentation/devicetree/bindings/riscv/cpus.yaml b/Documentation/devicetree/bindings/riscv/cpus.yaml index d632ac76532e..873dd12f6e89 100644 --- a/Documentation/devicetree/bindings/riscv/cpus.yaml +++ b/Documentation/devicetree/bindings/riscv/cpus.yaml @@ -63,6 +63,11 @@ properties: - riscv,sv48 - riscv,none + riscv,cbom-block-size: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + The blocksize in bytes for the Zicbom cache operations. + riscv,isa: description: Identifies the specific RISC-V instruction set architecture diff --git a/Documentation/devicetree/bindings/riscv/sifive-l2-cache.yaml b/Documentation/devicetree/bindings/riscv/sifive-l2-cache.yaml index e2d330bd4608..69cdab18d629 100644 --- a/Documentation/devicetree/bindings/riscv/sifive-l2-cache.yaml +++ b/Documentation/devicetree/bindings/riscv/sifive-l2-cache.yaml @@ -46,7 +46,7 @@ properties: const: 2 cache-sets: - const: 1024 + enum: [1024, 2048] cache-size: const: 2097152 @@ -84,6 +84,8 @@ then: description: | Must contain entries for DirError, DataError and DataFail signals. maxItems: 3 + cache-sets: + const: 1024 else: properties: @@ -91,6 +93,8 @@ else: description: | Must contain entries for DirError, DataError, DataFail, DirFail signals. minItems: 4 + cache-sets: + const: 2048 additionalProperties: false diff --git a/Documentation/devicetree/bindings/rtc/microcrystal,rv3032.yaml b/Documentation/devicetree/bindings/rtc/microcrystal,rv3032.yaml index 9593840a4a2b..60f9027e8299 100644 --- a/Documentation/devicetree/bindings/rtc/microcrystal,rv3032.yaml +++ b/Documentation/devicetree/bindings/rtc/microcrystal,rv3032.yaml @@ -32,6 +32,7 @@ properties: - 11000 trickle-voltage-millivolt: + $ref: /schemas/types.yaml#/definitions/uint32 enum: - 1750 - 3000 diff --git a/Documentation/devicetree/bindings/rtc/nuvoton,nct3018y.yaml b/Documentation/devicetree/bindings/rtc/nuvoton,nct3018y.yaml new file mode 100644 index 000000000000..7a1857f5caa8 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/nuvoton,nct3018y.yaml @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/rtc/nuvoton,nct3018y.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NUVOTON NCT3018Y Real Time Clock + +allOf: + - $ref: "rtc.yaml#" + +maintainers: + - Medad CChien <ctcchien@nuvoton.com> + - Mia Lin <mimi05633@gmail.com> + +properties: + compatible: + const: nuvoton,nct3018y + + reg: + maxItems: 1 + + start-year: true + + reset-source: true + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + rtc@6f { + compatible = "nuvoton,nct3018y"; + reg = <0x6f>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/rtc/nxp,pcf85063.txt b/Documentation/devicetree/bindings/rtc/nxp,pcf85063.txt deleted file mode 100644 index 217b7cd06c11..000000000000 --- a/Documentation/devicetree/bindings/rtc/nxp,pcf85063.txt +++ /dev/null @@ -1,32 +0,0 @@ -* NXP PCF85063 Real Time Clock - -Required properties: -- compatible: Should one of contain: - "nxp,pca85073a", - "nxp,pcf85063", - "nxp,pcf85063a", - "nxp,pcf85063tp", - "microcrystal,rv8263" -- reg: I2C address for chip. - -Optional property: -- quartz-load-femtofarads: The capacitive load of the quartz(x-tal), - expressed in femto Farad (fF). Valid values are 7000 and 12500. - Default value (if no value is specified) is 7000fF. - -Optional child node: -- clock: Provide this if the square wave pin is used as boot-enabled fixed clock. - -Example: - -pcf85063: rtc@51 { - compatible = "nxp,pcf85063"; - reg = <0x51>; - quartz-load-femtofarads = <12500>; - - clock { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <32768>; - }; -}; diff --git a/Documentation/devicetree/bindings/rtc/nxp,pcf85063.yaml b/Documentation/devicetree/bindings/rtc/nxp,pcf85063.yaml new file mode 100644 index 000000000000..2f892f8640d1 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/nxp,pcf85063.yaml @@ -0,0 +1,92 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/rtc/nxp,pcf85063.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP PCF85063 Real Time Clock + +maintainers: + - Alexander Stein <alexander.stein@ew.tq-group.com> + +properties: + compatible: + enum: + - microcrystal,rv8263 + - nxp,pcf85063 + - nxp,pcf85063a + - nxp,pcf85063tp + - nxp,pca85073a + + reg: + maxItems: 1 + + "#clock-cells": + const: 0 + + clock-output-names: + maxItems: 1 + + interrupts: + maxItems: 1 + + quartz-load-femtofarads: + description: + The capacitive load of the quartz(x-tal). + enum: [7000, 12500] + default: 7000 + + clock: + $ref: /schemas/clock/fixed-clock.yaml + description: + Provide this if the square wave pin is used as boot-enabled + fixed clock. + + wakeup-source: true + +allOf: + - $ref: rtc.yaml# + - if: + properties: + compatible: + contains: + enum: + - microcrystal,rv8263 + then: + properties: + quartz-load-femtofarads: false + - if: + properties: + compatible: + contains: + enum: + - nxp,pcf85063 + then: + properties: + quartz-load-femtofarads: + const: 7000 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + rtc@51 { + compatible = "nxp,pcf85063a"; + reg = <0x51>; + quartz-load-femtofarads = <12500>; + + clock { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/rtc/qcom-pm8xxx-rtc.yaml b/Documentation/devicetree/bindings/rtc/qcom-pm8xxx-rtc.yaml index 6fa7d9fc2dc7..23ab5bb4f395 100644 --- a/Documentation/devicetree/bindings/rtc/qcom-pm8xxx-rtc.yaml +++ b/Documentation/devicetree/bindings/rtc/qcom-pm8xxx-rtc.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Qualcomm PM8xxx PMIC RTC device maintainers: - - Satya Priya <skakit@codeaurora.org> + - Satya Priya <quic_c_skakit@quicinc.com> properties: compatible: diff --git a/Documentation/devicetree/bindings/rtc/rtc-mt6397.txt b/Documentation/devicetree/bindings/rtc/rtc-mt6397.txt index 55a0c8874c03..7212076a8f1b 100644 --- a/Documentation/devicetree/bindings/rtc/rtc-mt6397.txt +++ b/Documentation/devicetree/bindings/rtc/rtc-mt6397.txt @@ -14,6 +14,8 @@ For MediaTek PMIC wrapper bus bindings, see: Required properties: - compatible: Should be one of follows "mediatek,mt6323-rtc": for MT6323 PMIC + "mediatek,mt6358-rtc": for MT6358 PMIC + "mediatek,mt6366-rtc", "mediatek,mt6358-rtc": for MT6366 PMIC "mediatek,mt6397-rtc": for MT6397 PMIC Example: diff --git a/Documentation/devicetree/bindings/rtc/ti,k3-rtc.yaml b/Documentation/devicetree/bindings/rtc/ti,k3-rtc.yaml new file mode 100644 index 000000000000..d995ef04a6eb --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/ti,k3-rtc.yaml @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/rtc/ti,k3-rtc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments K3 Real Time Clock + +maintainers: + - Nishanth Menon <nm@ti.com> + +description: | + This RTC appears in the AM62x family of SoCs. + +allOf: + - $ref: "rtc.yaml#" + +properties: + compatible: + enum: + - ti,am62-rtc + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: VBUS Interface clock + - description: 32k Clock source (external or internal). + + clock-names: + items: + - const: vbus + - const: osc32k + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + rtc@2b1f0000 { + compatible = "ti,am62-rtc"; + reg = <0x2b1f0000 0x100>; + interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>; + power-domains = <&bar 0>; + clocks = <&foo 0>, <&foo 1>; + clock-names = "vbus", "osc32k"; + wakeup-source; + }; diff --git a/Documentation/devicetree/bindings/rtc/xlnx,zynqmp-rtc.yaml b/Documentation/devicetree/bindings/rtc/xlnx,zynqmp-rtc.yaml index bdb72d3ddf2a..7ed0230f6c67 100644 --- a/Documentation/devicetree/bindings/rtc/xlnx,zynqmp-rtc.yaml +++ b/Documentation/devicetree/bindings/rtc/xlnx,zynqmp-rtc.yaml @@ -23,8 +23,15 @@ properties: reg: maxItems: 1 + clocks: + maxItems: 1 + + clock-names: + items: + - const: rtc + interrupts: - minItems: 2 + maxItems: 2 interrupt-names: items: @@ -39,6 +46,7 @@ properties: minimum: 0x1 maximum: 0x1FFFFF default: 0x198233 + deprecated: true required: - compatible @@ -61,5 +69,7 @@ examples: interrupts = <0 26 4>, <0 27 4>; interrupt-names = "alarm", "sec"; calibration = <0x198233>; + clock-names = "rtc"; + clocks = <&rtc_clk>; }; }; diff --git a/Documentation/devicetree/bindings/sound/tas2562.yaml b/Documentation/devicetree/bindings/sound/tas2562.yaml index 5f7dd5d6cbca..30f6b029ac08 100644 --- a/Documentation/devicetree/bindings/sound/tas2562.yaml +++ b/Documentation/devicetree/bindings/sound/tas2562.yaml @@ -8,7 +8,7 @@ $schema: "http://devicetree.org/meta-schemas/core.yaml#" title: Texas Instruments TAS2562 Smart PA maintainers: - - Dan Murphy <dmurphy@ti.com> + - Andrew Davis <afd@ti.com> description: | The TAS2562 is a mono, digital input Class-D audio amplifier optimized for diff --git a/Documentation/devicetree/bindings/sound/tlv320adcx140.yaml b/Documentation/devicetree/bindings/sound/tlv320adcx140.yaml index bc2fb1a80ed7..ee698614862e 100644 --- a/Documentation/devicetree/bindings/sound/tlv320adcx140.yaml +++ b/Documentation/devicetree/bindings/sound/tlv320adcx140.yaml @@ -8,7 +8,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Texas Instruments TLV320ADCX140 Quad Channel Analog-to-Digital Converter maintainers: - - Dan Murphy <dmurphy@ti.com> + - Andrew Davis <afd@ti.com> description: | The TLV320ADCX140 are multichannel (4-ch analog recording or 8-ch digital diff --git a/Documentation/devicetree/bindings/virtio/mmio.yaml b/Documentation/devicetree/bindings/virtio/mmio.yaml index 10c22b5bd16a..0aa8433f0a5e 100644 --- a/Documentation/devicetree/bindings/virtio/mmio.yaml +++ b/Documentation/devicetree/bindings/virtio/mmio.yaml @@ -33,6 +33,10 @@ properties: description: Required for devices making accesses thru an IOMMU. maxItems: 1 + wakeup-source: + type: boolean + description: Required for setting irq of a virtio_mmio device as wakeup source. + required: - compatible - reg diff --git a/Documentation/loongarch/introduction.rst b/Documentation/loongarch/introduction.rst index 216b3f390e80..6c9160c4e9be 100644 --- a/Documentation/loongarch/introduction.rst +++ b/Documentation/loongarch/introduction.rst @@ -221,7 +221,7 @@ I26 Opcode + I26L + I26H =========== ========================== Rd is the destination register operand, while Rj, Rk and Ra ("a" stands for -"additional") are the source register operands. I8/I12/I16/I21/I26 are +"additional") are the source register operands. I8/I12/I14/I16/I21/I26 are immediate operands of respective width. The longer I21 and I26 are stored in separate higher and lower parts in the instruction word, denoted by the "L" and "H" suffixes. diff --git a/Documentation/networking/bonding.rst b/Documentation/networking/bonding.rst index 53a18ff7cf23..7823a069a903 100644 --- a/Documentation/networking/bonding.rst +++ b/Documentation/networking/bonding.rst @@ -1982,15 +1982,6 @@ uses the response as an indication that the link is operating. This gives some assurance that traffic is actually flowing to and from one or more peers on the local network. -The ARP monitor relies on the device driver itself to verify -that traffic is flowing. In particular, the driver must keep up to -date the last receive time, dev->last_rx. Drivers that use NETIF_F_LLTX -flag must also update netdev_queue->trans_start. If they do not, then the -ARP monitor will immediately fail any slaves using that driver, and -those slaves will stay down. If networking monitoring (tcpdump, etc) -shows the ARP requests and replies on the network, then it may be that -your device driver is not updating last_rx and trans_start. - 7.2 Configuring Multiple ARP Targets ------------------------------------ diff --git a/Documentation/translations/zh_CN/loongarch/introduction.rst b/Documentation/translations/zh_CN/loongarch/introduction.rst index 11686ee0caeb..128878f5bb70 100644 --- a/Documentation/translations/zh_CN/loongarch/introduction.rst +++ b/Documentation/translations/zh_CN/loongarch/introduction.rst @@ -190,8 +190,8 @@ I26 Opcode + I26L + I26H =========== ========================== Opcode是指令操作码,Rj和Rk是源操作数(寄存器),Rd是目标操作数(寄存器),Ra是 -4R-type格式特有的附加操作数(寄存器)。I8/I12/I16/I21/I26分别是8位/12位/16位/ -21位/26位的立即数。其中较长的21位和26位立即数在指令字中被分割为高位部分与低位 +4R-type格式特有的附加操作数(寄存器)。I8/I12/I14/I16/I21/I26分别是8位/12位/14位/ +16位/21位/26位的立即数。其中较长的21位和26位立即数在指令字中被分割为高位部分与低位 部分,所以你们在这里的格式描述中能够看到I21L/I21H和I26L/I26H这样带后缀的表述。 指令列表 diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index 9788b19f9ff7..abd7c32126ce 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -8262,15 +8262,15 @@ dump related UV data. Also the vcpu ioctl `KVM_S390_PV_CPU_COMMAND` is available and supports the `KVM_PV_DUMP_CPU` subcommand. 8.38 KVM_CAP_VM_DISABLE_NX_HUGE_PAGES ---------------------------- +------------------------------------- -:Capability KVM_CAP_VM_DISABLE_NX_HUGE_PAGES +:Capability: KVM_CAP_VM_DISABLE_NX_HUGE_PAGES :Architectures: x86 :Type: vm :Parameters: arg[0] must be 0. -:Returns 0 on success, -EPERM if the userspace process does not - have CAP_SYS_BOOT, -EINVAL if args[0] is not 0 or any vCPUs have been - created. +:Returns: 0 on success, -EPERM if the userspace process does not + have CAP_SYS_BOOT, -EINVAL if args[0] is not 0 or any vCPUs have been + created. This capability disables the NX huge pages mitigation for iTLB MULTIHIT. diff --git a/MAINTAINERS b/MAINTAINERS index 67a6c6d06aa9..11e113b32a2e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -264,6 +264,11 @@ W: http://www.adaptec.com/ F: Documentation/scsi/aacraid.rst F: drivers/scsi/aacraid/ +AB8500 BATTERY AND CHARGER DRIVERS +M: Linus Walleij <linus.walleij@linaro.org> +F: Documentation/devicetree/bindings/power/supply/*ab8500* +F: drivers/power/supply/*ab8500* + ABI/API L: linux-api@vger.kernel.org F: include/linux/syscalls.h @@ -2481,11 +2486,13 @@ S: Supported F: Documentation/devicetree/bindings/*/*/*npcm* F: Documentation/devicetree/bindings/*/*npcm* F: Documentation/devicetree/bindings/arm/npcm/* +F: Documentation/devicetree/bindings/rtc/nuvoton,nct3018y.yaml F: arch/arm/boot/dts/nuvoton-npcm* F: arch/arm/mach-npcm/ F: arch/arm64/boot/dts/nuvoton/ F: drivers/*/*npcm* F: drivers/*/*/*npcm* +F: drivers/rtc/rtc-nct3018y.c F: include/dt-bindings/clock/nuvoton,npcm7xx-clock.h F: include/dt-bindings/clock/nuvoton,npcm845-clk.h @@ -9694,7 +9701,7 @@ F: arch/powerpc/platforms/powernv/copy-paste.h F: arch/powerpc/platforms/powernv/vas* IBM Power Virtual Ethernet Device Driver -M: Cristobal Forno <cforno12@linux.ibm.com> +M: Nick Child <nnac123@linux.ibm.com> L: netdev@vger.kernel.org S: Supported F: drivers/net/ethernet/ibm/ibmveth.* @@ -12843,7 +12850,7 @@ F: Documentation/devicetree/bindings/net/wireless/mediatek,mt76.yaml F: drivers/net/wireless/mediatek/mt76/ MEDIATEK MT7601U WIRELESS LAN DRIVER -M: Jakub Kicinski <kubakici@wp.pl> +M: Jakub Kicinski <kuba@kernel.org> L: linux-wireless@vger.kernel.org S: Maintained F: drivers/net/wireless/mediatek/mt7601u/ @@ -14461,6 +14468,7 @@ W: https://github.com/jonmason/ntb/wiki T: git git://github.com/jonmason/ntb.git F: drivers/net/ntb_netdev.c F: drivers/ntb/ +F: drivers/pci/endpoint/functions/pci-epf-*ntb.c F: include/linux/ntb.h F: include/linux/ntb_transport.h F: tools/testing/selftests/ntb/ @@ -17524,6 +17532,7 @@ F: drivers/char/hw_random/mpfs-rng.c F: drivers/clk/microchip/clk-mpfs.c F: drivers/mailbox/mailbox-mpfs.c F: drivers/pci/controller/pcie-microchip-host.c +F: drivers/rtc/rtc-mpfs.c F: drivers/soc/microchip/ F: drivers/spi/spi-microchip-core.c F: drivers/usb/musb/mpfs.c diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c index 7ca8779ae34f..389623ae5a91 100644 --- a/arch/arm64/net/bpf_jit_comp.c +++ b/arch/arm64/net/bpf_jit_comp.c @@ -1496,7 +1496,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) memset(&ctx, 0, sizeof(ctx)); ctx.prog = prog; - ctx.offset = kcalloc(prog->len + 1, sizeof(int), GFP_KERNEL); + ctx.offset = kvcalloc(prog->len + 1, sizeof(int), GFP_KERNEL); if (ctx.offset == NULL) { prog = orig_prog; goto out_off; @@ -1601,7 +1601,7 @@ skip_init_ctx: ctx.offset[i] *= AARCH64_INSN_SIZE; bpf_prog_fill_jited_linfo(prog, ctx.offset + 1); out_off: - kfree(ctx.offset); + kvfree(ctx.offset); kfree(jit_data); prog->aux->jit_data = NULL; } @@ -1643,7 +1643,7 @@ static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_link *l, int args_off, int retval_off, int run_ctx_off, bool save_ret) { - u32 *branch; + __le32 *branch; u64 enter_prog; u64 exit_prog; struct bpf_prog *p = l->link.prog; @@ -1698,7 +1698,7 @@ static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_link *l, if (ctx->image) { int offset = &ctx->image[ctx->idx] - branch; - *branch = A64_CBZ(1, A64_R(0), offset); + *branch = cpu_to_le32(A64_CBZ(1, A64_R(0), offset)); } /* arg1: prog */ @@ -1713,7 +1713,7 @@ static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_link *l, static void invoke_bpf_mod_ret(struct jit_ctx *ctx, struct bpf_tramp_links *tl, int args_off, int retval_off, int run_ctx_off, - u32 **branches) + __le32 **branches) { int i; @@ -1784,7 +1784,7 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, struct bpf_tramp_links *fexit = &tlinks[BPF_TRAMP_FEXIT]; struct bpf_tramp_links *fmod_ret = &tlinks[BPF_TRAMP_MODIFY_RETURN]; bool save_ret; - u32 **branches = NULL; + __le32 **branches = NULL; /* trampoline stack layout: * [ parent ip ] @@ -1892,7 +1892,7 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, flags & BPF_TRAMP_F_RET_FENTRY_RET); if (fmod_ret->nr_links) { - branches = kcalloc(fmod_ret->nr_links, sizeof(u32 *), + branches = kcalloc(fmod_ret->nr_links, sizeof(__le32 *), GFP_KERNEL); if (!branches) return -ENOMEM; @@ -1916,7 +1916,7 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, /* update the branches saved in invoke_bpf_mod_ret with cbnz */ for (i = 0; i < fmod_ret->nr_links && ctx->image != NULL; i++) { int offset = &ctx->image[ctx->idx] - branches[i]; - *branches[i] = A64_CBNZ(1, A64_R(10), offset); + *branches[i] = cpu_to_le32(A64_CBNZ(1, A64_R(10), offset)); } for (i = 0; i < fexit->nr_links; i++) diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index ae15610a9427..4abc9a28aba4 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -2,7 +2,9 @@ config LOONGARCH bool default y + select ACPI select ACPI_GENERIC_GSI if ACPI + select ACPI_MCFG if ACPI select ACPI_SYSTEM_POWER_STATES_SUPPORT if ACPI select ARCH_BINFMT_ELF_STATE select ARCH_ENABLE_MEMORY_HOTPLUG @@ -40,6 +42,7 @@ config LOONGARCH select ARCH_MIGHT_HAVE_PC_PARPORT select ARCH_MIGHT_HAVE_PC_SERIO select ARCH_SPARSEMEM_ENABLE + select ARCH_STACKWALK select ARCH_SUPPORTS_ACPI select ARCH_SUPPORTS_ATOMIC_RMW select ARCH_SUPPORTS_HUGETLBFS @@ -51,6 +54,7 @@ config LOONGARCH select ARCH_WANTS_NO_INSTR select BUILDTIME_TABLE_SORT select COMMON_CLK + select EFI select GENERIC_CLOCKEVENTS select GENERIC_CMOS_UPDATE select GENERIC_CPU_AUTOPROBE @@ -86,6 +90,7 @@ config LOONGARCH select HAVE_IRQ_TIME_ACCOUNTING select HAVE_MOD_ARCH_SPECIFIC select HAVE_NMI + select HAVE_PCI select HAVE_PERF_EVENTS select HAVE_REGS_AND_STACK_ACCESS_API select HAVE_RSEQ @@ -95,20 +100,27 @@ config LOONGARCH select HAVE_VIRT_CPU_ACCOUNTING_GEN if !SMP select IRQ_FORCED_THREADING select IRQ_LOONGARCH_CPU + select MMU_GATHER_MERGE_VMAS if MMU select MODULES_USE_ELF_RELA if MODULES select NEED_PER_CPU_EMBED_FIRST_CHUNK select NEED_PER_CPU_PAGE_FIRST_CHUNK select OF select OF_EARLY_FLATTREE + select PCI + select PCI_DOMAINS_GENERIC + select PCI_ECAM if ACPI + select PCI_LOONGSON + select PCI_MSI_ARCH_FALLBACKS select PERF_USE_VMALLOC select RTC_LIB + select SMP select SPARSE_IRQ select SYSCTL_EXCEPTION_TRACE select SWIOTLB select TRACE_IRQFLAGS_SUPPORT select USE_PERCPU_NUMA_NODE_ID + select USER_STACKTRACE_SUPPORT select ZONE_DMA32 - select MMU_GATHER_MERGE_VMAS if MMU config 32BIT bool @@ -141,6 +153,10 @@ config LOCKDEP_SUPPORT bool default y +config STACKTRACE_SUPPORT + bool + default y + # MACH_LOONGSON32 and MACH_LOONGSON64 are delibrately carried over from the # MIPS Loongson code, to preserve Loongson-specific code paths in drivers that # are shared between architectures, and specifically expecting the symbols. diff --git a/arch/loongarch/Kconfig.debug b/arch/loongarch/Kconfig.debug index e69de29bb2d1..8d36aab53008 100644 --- a/arch/loongarch/Kconfig.debug +++ b/arch/loongarch/Kconfig.debug @@ -0,0 +1,29 @@ +choice + prompt "Choose kernel unwinder" + default UNWINDER_PROLOGUE if KALLSYMS + help + This determines which method will be used for unwinding kernel stack + traces for panics, oopses, bugs, warnings, perf, /proc/<pid>/stack, + lockdep, and more. + +config UNWINDER_GUESS + bool "Guess unwinder" + help + This option enables the "guess" unwinder for unwinding kernel stack + traces. It scans the stack and reports every kernel text address it + finds. Some of the addresses it reports may be incorrect. + + While this option often produces false positives, it can still be + useful in many cases. + +config UNWINDER_PROLOGUE + bool "Prologue unwinder" + depends on KALLSYMS + help + This option enables the "prologue" unwinder for unwinding kernel stack + traces. It unwind the stack frame based on prologue code analyze. Symbol + information is needed, at least the address and length of each function. + Some of the addresses it reports may be incorrect (but better than the + Guess unwinder). + +endchoice diff --git a/arch/loongarch/Makefile b/arch/loongarch/Makefile index fbe4277e6404..ec3de6191276 100644 --- a/arch/loongarch/Makefile +++ b/arch/loongarch/Makefile @@ -47,6 +47,8 @@ cflags-y += $(call cc-option, -mno-check-zero-division) load-y = 0x9000000000200000 bootvars-y = VMLINUX_LOAD_ADDRESS=$(load-y) +drivers-$(CONFIG_PCI) += arch/loongarch/pci/ + KBUILD_AFLAGS += $(cflags-y) KBUILD_CFLAGS += $(cflags-y) KBUILD_CPPFLAGS += -DVMLINUX_LOAD_ADDRESS=$(load-y) diff --git a/arch/loongarch/configs/loongson3_defconfig b/arch/loongarch/configs/loongson3_defconfig index eb9149786b6b..3712552e18d3 100644 --- a/arch/loongarch/configs/loongson3_defconfig +++ b/arch/loongarch/configs/loongson3_defconfig @@ -278,6 +278,8 @@ CONFIG_NET_ACT_IPT=m CONFIG_NET_ACT_NAT=m CONFIG_NET_ACT_BPF=m CONFIG_OPENVSWITCH=m +CONFIG_VSOCKETS=m +CONFIG_VIRTIO_VSOCKETS=m CONFIG_NETLINK_DIAG=y CONFIG_CGROUP_NET_PRIO=y CONFIG_BT=m @@ -289,6 +291,7 @@ CONFIG_MAC80211=m CONFIG_RFKILL=m CONFIG_RFKILL_INPUT=y CONFIG_NET_9P=y +CONFIG_NET_9P_VIRTIO=y CONFIG_CEPH_LIB=m CONFIG_PCIEPORTBUS=y CONFIG_HOTPLUG_PCI_PCIE=y @@ -308,6 +311,8 @@ CONFIG_RAPIDIO_MPORT_CDEV=m CONFIG_UEVENT_HELPER=y CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y +CONFIG_FW_LOADER_COMPRESS=y +CONFIG_FW_LOADER_COMPRESS_ZSTD=y CONFIG_MTD=m CONFIG_MTD_BLOCK=m CONFIG_MTD_CFI=m @@ -328,8 +333,19 @@ CONFIG_BLK_DEV_CRYPTOLOOP=y CONFIG_BLK_DEV_NBD=m CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_VIRTIO_BLK=y CONFIG_BLK_DEV_RBD=m CONFIG_BLK_DEV_NVME=y +CONFIG_NVME_MULTIPATH=y +CONFIG_NVME_RDMA=m +CONFIG_NVME_FC=m +CONFIG_NVME_TCP=m +CONFIG_NVME_TARGET=m +CONFIG_NVME_TARGET_PASSTHRU=y +CONFIG_NVME_TARGET_LOOP=m +CONFIG_NVME_TARGET_RDMA=m +CONFIG_NVME_TARGET_FC=m +CONFIG_NVME_TARGET_TCP=m CONFIG_EEPROM_AT24=m CONFIG_BLK_DEV_SD=y CONFIG_BLK_DEV_SR=y @@ -359,6 +375,7 @@ CONFIG_SCSI_QLA_FC=m CONFIG_TCM_QLA2XXX=m CONFIG_SCSI_QLA_ISCSI=m CONFIG_SCSI_LPFC=m +CONFIG_SCSI_VIRTIO=m CONFIG_ATA=y CONFIG_SATA_AHCI=y CONFIG_SATA_AHCI_PLATFORM=y @@ -403,6 +420,7 @@ CONFIG_VXLAN=y CONFIG_RIONET=m CONFIG_TUN=m CONFIG_VETH=m +CONFIG_VIRTIO_NET=m # CONFIG_NET_VENDOR_3COM is not set # CONFIG_NET_VENDOR_ADAPTEC is not set # CONFIG_NET_VENDOR_AGERE is not set @@ -527,10 +545,12 @@ CONFIG_SERIAL_8250_SHARE_IRQ=y CONFIG_SERIAL_8250_RSA=y CONFIG_SERIAL_NONSTANDARD=y CONFIG_PRINTER=m +CONFIG_VIRTIO_CONSOLE=y CONFIG_IPMI_HANDLER=m CONFIG_IPMI_DEVICE_INTERFACE=m CONFIG_IPMI_SI=m CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_VIRTIO=m CONFIG_I2C_CHARDEV=y CONFIG_I2C_PIIX4=y CONFIG_I2C_GPIO=y @@ -568,6 +588,8 @@ CONFIG_DRM_AMDGPU_SI=y CONFIG_DRM_AMDGPU_CIK=y CONFIG_DRM_AMDGPU_USERPTR=y CONFIG_DRM_AST=y +CONFIG_DRM_QXL=m +CONFIG_DRM_VIRTIO_GPU=m CONFIG_FB=y CONFIG_FB_EFI=y CONFIG_FB_RADEON=y @@ -637,7 +659,16 @@ CONFIG_UIO=m CONFIG_UIO_PDRV_GENIRQ=m CONFIG_UIO_DMEM_GENIRQ=m CONFIG_UIO_PCI_GENERIC=m -# CONFIG_VIRTIO_MENU is not set +CONFIG_VFIO=m +CONFIG_VFIO_PCI=m +CONFIG_VIRTIO_PCI=y +CONFIG_VIRTIO_BALLOON=m +CONFIG_VIRTIO_INPUT=m +CONFIG_VIRTIO_MMIO=m +CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y +CONFIG_VHOST_NET=m +CONFIG_VHOST_SCSI=m +CONFIG_VHOST_VSOCK=m CONFIG_COMEDI=m CONFIG_COMEDI_PCI_DRIVERS=m CONFIG_COMEDI_8255_PCI=m @@ -762,6 +793,7 @@ CONFIG_CRYPTO_USER_API_HASH=m CONFIG_CRYPTO_USER_API_SKCIPHER=m CONFIG_CRYPTO_USER_API_RNG=m CONFIG_CRYPTO_USER_API_AEAD=m +CONFIG_CRYPTO_DEV_VIRTIO=m CONFIG_PRINTK_TIME=y CONFIG_STRIP_ASM_SYMS=y CONFIG_MAGIC_SYSRQ=y diff --git a/arch/loongarch/include/asm/bootinfo.h b/arch/loongarch/include/asm/bootinfo.h index 9b8d49d9e61b..e02ac4af7f6e 100644 --- a/arch/loongarch/include/asm/bootinfo.h +++ b/arch/loongarch/include/asm/bootinfo.h @@ -28,10 +28,10 @@ struct loongson_board_info { struct loongson_system_configuration { int nr_cpus; int nr_nodes; - int nr_io_pics; int boot_cpu_id; int cores_per_node; int cores_per_package; + unsigned long cores_io_master; const char *cpuname; }; diff --git a/arch/loongarch/include/asm/dma.h b/arch/loongarch/include/asm/dma.h new file mode 100644 index 000000000000..1a8866319fe2 --- /dev/null +++ b/arch/loongarch/include/asm/dma.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ +#ifndef __ASM_DMA_H +#define __ASM_DMA_H + +#define MAX_DMA_ADDRESS PAGE_OFFSET +#define MAX_DMA32_PFN (1UL << (32 - PAGE_SHIFT)) + +#endif diff --git a/arch/loongarch/include/asm/inst.h b/arch/loongarch/include/asm/inst.h index 575d1bb66ffb..7b07cbb3188c 100644 --- a/arch/loongarch/include/asm/inst.h +++ b/arch/loongarch/include/asm/inst.h @@ -23,12 +23,33 @@ enum reg1i20_op { lu32id_op = 0x0b, }; +enum reg1i21_op { + beqz_op = 0x10, + bnez_op = 0x11, +}; + enum reg2i12_op { + addiw_op = 0x0a, + addid_op = 0x0b, lu52id_op = 0x0c, + ldb_op = 0xa0, + ldh_op = 0xa1, + ldw_op = 0xa2, + ldd_op = 0xa3, + stb_op = 0xa4, + sth_op = 0xa5, + stw_op = 0xa6, + std_op = 0xa7, }; enum reg2i16_op { jirl_op = 0x13, + beq_op = 0x16, + bne_op = 0x17, + blt_op = 0x18, + bge_op = 0x19, + bltu_op = 0x1a, + bgeu_op = 0x1b, }; struct reg0i26_format { @@ -110,6 +131,37 @@ enum loongarch_gpr { LOONGARCH_GPR_MAX }; +#define is_imm12_negative(val) is_imm_negative(val, 12) + +static inline bool is_imm_negative(unsigned long val, unsigned int bit) +{ + return val & (1UL << (bit - 1)); +} + +static inline bool is_branch_ins(union loongarch_instruction *ip) +{ + return ip->reg1i21_format.opcode >= beqz_op && + ip->reg1i21_format.opcode <= bgeu_op; +} + +static inline bool is_ra_save_ins(union loongarch_instruction *ip) +{ + /* st.d $ra, $sp, offset */ + return ip->reg2i12_format.opcode == std_op && + ip->reg2i12_format.rj == LOONGARCH_GPR_SP && + ip->reg2i12_format.rd == LOONGARCH_GPR_RA && + !is_imm12_negative(ip->reg2i12_format.immediate); +} + +static inline bool is_stack_alloc_ins(union loongarch_instruction *ip) +{ + /* addi.d $sp, $sp, -imm */ + return ip->reg2i12_format.opcode == addid_op && + ip->reg2i12_format.rj == LOONGARCH_GPR_SP && + ip->reg2i12_format.rd == LOONGARCH_GPR_SP && + is_imm12_negative(ip->reg2i12_format.immediate); +} + u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm); u32 larch_insn_gen_lu52id(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm); u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loongarch_gpr rj, unsigned long pc, unsigned long dest); diff --git a/arch/loongarch/include/asm/irq.h b/arch/loongarch/include/asm/irq.h index 149b2123e7f4..4b130199ceae 100644 --- a/arch/loongarch/include/asm/irq.h +++ b/arch/loongarch/include/asm/irq.h @@ -82,8 +82,6 @@ extern struct acpi_vector_group msi_group[MAX_IO_PICS]; #define GSI_MAX_PCH_IRQ (LOONGSON_PCH_IRQ_BASE + 256 - 1) extern int find_pch_pic(u32 gsi); -extern int eiointc_get_node(int id); - struct acpi_madt_lio_pic; struct acpi_madt_eio_pic; struct acpi_madt_ht_pic; @@ -100,16 +98,8 @@ struct irq_domain *htvec_acpi_init(struct irq_domain *parent, struct acpi_madt_ht_pic *acpi_htvec); int pch_lpc_acpi_init(struct irq_domain *parent, struct acpi_madt_lpc_pic *acpi_pchlpc); -#if IS_ENABLED(CONFIG_LOONGSON_PCH_MSI) int pch_msi_acpi_init(struct irq_domain *parent, struct acpi_madt_msi_pic *acpi_pchmsi); -#else -static inline int pch_msi_acpi_init(struct irq_domain *parent, - struct acpi_madt_msi_pic *acpi_pchmsi) -{ - return 0; -} -#endif int pch_pic_acpi_init(struct irq_domain *parent, struct acpi_madt_bio_pic *acpi_pchpic); int find_pch_pic(u32 gsi); diff --git a/arch/loongarch/include/asm/page.h b/arch/loongarch/include/asm/page.h index dc47fc724fa1..a37324ac460b 100644 --- a/arch/loongarch/include/asm/page.h +++ b/arch/loongarch/include/asm/page.h @@ -33,8 +33,6 @@ #include <linux/kernel.h> #include <linux/pfn.h> -#define MAX_DMA32_PFN (1UL << (32 - PAGE_SHIFT)) - /* * It's normally defined only for FLATMEM config but it's * used in our early mem init code for all memory models. diff --git a/arch/loongarch/include/asm/pci.h b/arch/loongarch/include/asm/pci.h new file mode 100644 index 000000000000..846909d7e831 --- /dev/null +++ b/arch/loongarch/include/asm/pci.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ +#ifndef _ASM_PCI_H +#define _ASM_PCI_H + +#include <linux/ioport.h> +#include <linux/list.h> +#include <linux/types.h> +#include <asm/io.h> + +#define PCIBIOS_MIN_IO 0x4000 +#define PCIBIOS_MIN_MEM 0x20000000 +#define PCIBIOS_MIN_CARDBUS_IO 0x4000 + +#define HAVE_PCI_MMAP +#define pcibios_assign_all_busses() 0 + +extern phys_addr_t mcfg_addr_init(int node); + +/* generic pci stuff */ +#include <asm-generic/pci.h> + +#endif /* _ASM_PCI_H */ diff --git a/arch/loongarch/include/asm/processor.h b/arch/loongarch/include/asm/processor.h index 57ec45aa078e..1c4b4308378d 100644 --- a/arch/loongarch/include/asm/processor.h +++ b/arch/loongarch/include/asm/processor.h @@ -101,6 +101,10 @@ struct thread_struct { unsigned long reg23, reg24, reg25, reg26; /* s0-s3 */ unsigned long reg27, reg28, reg29, reg30, reg31; /* s4-s8 */ + /* __schedule() return address / call frame address */ + unsigned long sched_ra; + unsigned long sched_cfa; + /* CSR registers */ unsigned long csr_prmd; unsigned long csr_crmd; @@ -129,6 +133,9 @@ struct thread_struct { struct loongarch_fpu fpu FPU_ALIGN; }; +#define thread_saved_ra(tsk) (tsk->thread.sched_ra) +#define thread_saved_fp(tsk) (tsk->thread.sched_cfa) + #define INIT_THREAD { \ /* \ * Main processor registers \ @@ -145,6 +152,8 @@ struct thread_struct { .reg29 = 0, \ .reg30 = 0, \ .reg31 = 0, \ + .sched_ra = 0, \ + .sched_cfa = 0, \ .csr_crmd = 0, \ .csr_prmd = 0, \ .csr_euen = 0, \ diff --git a/arch/loongarch/include/asm/stacktrace.h b/arch/loongarch/include/asm/stacktrace.h index 6b5c2a7aa706..f23adb15f418 100644 --- a/arch/loongarch/include/asm/stacktrace.h +++ b/arch/loongarch/include/asm/stacktrace.h @@ -10,6 +10,26 @@ #include <asm/loongarch.h> #include <linux/stringify.h> +enum stack_type { + STACK_TYPE_UNKNOWN, + STACK_TYPE_IRQ, + STACK_TYPE_TASK, +}; + +struct stack_info { + enum stack_type type; + unsigned long begin, end, next_sp; +}; + +struct stack_frame { + unsigned long fp; + unsigned long ra; +}; + +bool in_irq_stack(unsigned long stack, struct stack_info *info); +bool in_task_stack(unsigned long stack, struct task_struct *task, struct stack_info *info); +int get_stack_info(unsigned long stack, struct task_struct *task, struct stack_info *info); + #define STR_LONG_L __stringify(LONG_L) #define STR_LONG_S __stringify(LONG_S) #define STR_LONGSIZE __stringify(LONGSIZE) diff --git a/arch/loongarch/include/asm/switch_to.h b/arch/loongarch/include/asm/switch_to.h index 2a8d04375574..43a5ab162d38 100644 --- a/arch/loongarch/include/asm/switch_to.h +++ b/arch/loongarch/include/asm/switch_to.h @@ -15,12 +15,15 @@ struct task_struct; * @prev: The task previously executed. * @next: The task to begin executing. * @next_ti: task_thread_info(next). + * @sched_ra: __schedule return address. + * @sched_cfa: __schedule call frame address. * * This function is used whilst scheduling to save the context of prev & load * the context of next. Returns prev. */ extern asmlinkage struct task_struct *__switch_to(struct task_struct *prev, - struct task_struct *next, struct thread_info *next_ti); + struct task_struct *next, struct thread_info *next_ti, + void *sched_ra, void *sched_cfa); /* * For newly created kernel threads switch_to() will return to @@ -28,10 +31,11 @@ extern asmlinkage struct task_struct *__switch_to(struct task_struct *prev, * That is, everything following __switch_to() will be skipped for new threads. * So everything that matters to new threads should be placed before __switch_to(). */ -#define switch_to(prev, next, last) \ -do { \ - lose_fpu_inatomic(1, prev); \ - (last) = __switch_to(prev, next, task_thread_info(next)); \ +#define switch_to(prev, next, last) \ +do { \ + lose_fpu_inatomic(1, prev); \ + (last) = __switch_to(prev, next, task_thread_info(next), \ + __builtin_return_address(0), __builtin_frame_address(0)); \ } while (0) #endif /* _ASM_SWITCH_TO_H */ diff --git a/arch/loongarch/include/asm/uaccess.h b/arch/loongarch/include/asm/uaccess.h index 2b44edc604a2..a8ae2af4025a 100644 --- a/arch/loongarch/include/asm/uaccess.h +++ b/arch/loongarch/include/asm/uaccess.h @@ -229,13 +229,13 @@ extern unsigned long __copy_user(void *to, const void *from, __kernel_size_t n); static inline unsigned long __must_check raw_copy_from_user(void *to, const void __user *from, unsigned long n) { - return __copy_user(to, from, n); + return __copy_user(to, (__force const void *)from, n); } static inline unsigned long __must_check raw_copy_to_user(void __user *to, const void *from, unsigned long n) { - return __copy_user(to, from, n); + return __copy_user((__force void *)to, from, n); } #define INLINE_COPY_FROM_USER diff --git a/arch/loongarch/include/asm/unwind.h b/arch/loongarch/include/asm/unwind.h new file mode 100644 index 000000000000..6af4718bdf01 --- /dev/null +++ b/arch/loongarch/include/asm/unwind.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Most of this ideas comes from x86. + * + * Copyright (C) 2022 Loongson Technology Corporation Limited + */ +#ifndef _ASM_UNWIND_H +#define _ASM_UNWIND_H + +#include <linux/sched.h> + +#include <asm/stacktrace.h> + +enum unwinder_type { + UNWINDER_GUESS, + UNWINDER_PROLOGUE, +}; + +struct unwind_state { + char type; /* UNWINDER_XXX */ + struct stack_info stack_info; + struct task_struct *task; + bool first, error; + unsigned long sp, pc, ra; +}; + +void unwind_start(struct unwind_state *state, + struct task_struct *task, struct pt_regs *regs); +bool unwind_next_frame(struct unwind_state *state); +unsigned long unwind_get_return_address(struct unwind_state *state); + +static inline bool unwind_done(struct unwind_state *state) +{ + return state->stack_info.type == STACK_TYPE_UNKNOWN; +} + +static inline bool unwind_error(struct unwind_state *state) +{ + return state->error; +} + +#endif /* _ASM_UNWIND_H */ diff --git a/arch/loongarch/include/asm/vdso.h b/arch/loongarch/include/asm/vdso.h index 8f8a0f9a4953..d3ba35eb23e7 100644 --- a/arch/loongarch/include/asm/vdso.h +++ b/arch/loongarch/include/asm/vdso.h @@ -7,6 +7,7 @@ #ifndef __ASM_VDSO_H #define __ASM_VDSO_H +#include <linux/mm.h> #include <linux/mm_types.h> #include <vdso/datapage.h> diff --git a/arch/loongarch/include/asm/vdso/vdso.h b/arch/loongarch/include/asm/vdso/vdso.h index 5a01643a65b3..3b55d32a0619 100644 --- a/arch/loongarch/include/asm/vdso/vdso.h +++ b/arch/loongarch/include/asm/vdso/vdso.h @@ -8,6 +8,18 @@ #include <asm/asm.h> #include <asm/page.h> +#include <asm/vdso.h> + +struct vdso_pcpu_data { + u32 node; +} ____cacheline_aligned_in_smp; + +struct loongarch_vdso_data { + struct vdso_pcpu_data pdata[NR_CPUS]; + struct vdso_data data[CS_BASES]; /* Arch-independent data */ +}; + +#define VDSO_DATA_SIZE PAGE_ALIGN(sizeof(struct loongarch_vdso_data)) static inline unsigned long get_vdso_base(void) { @@ -24,7 +36,8 @@ static inline unsigned long get_vdso_base(void) static inline const struct vdso_data *get_vdso_data(void) { - return (const struct vdso_data *)(get_vdso_base() - PAGE_SIZE); + return (const struct vdso_data *)(get_vdso_base() + - VDSO_DATA_SIZE + SMP_CACHE_BYTES * NR_CPUS); } #endif /* __ASSEMBLY__ */ diff --git a/arch/loongarch/kernel/Makefile b/arch/loongarch/kernel/Makefile index 940de9173542..e5be17009fe8 100644 --- a/arch/loongarch/kernel/Makefile +++ b/arch/loongarch/kernel/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_EFI) += efi.o obj-$(CONFIG_CPU_HAS_FPU) += fpu.o obj-$(CONFIG_MODULES) += module.o module-sections.o +obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-$(CONFIG_PROC_FS) += proc.o @@ -22,4 +23,7 @@ obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_NUMA) += numa.o +obj-$(CONFIG_UNWINDER_GUESS) += unwind_guess.o +obj-$(CONFIG_UNWINDER_PROLOGUE) += unwind_prologue.o + CPPFLAGS_vmlinux.lds := $(KBUILD_CFLAGS) diff --git a/arch/loongarch/kernel/acpi.c b/arch/loongarch/kernel/acpi.c index 03aa14581d0a..f1c928648a4a 100644 --- a/arch/loongarch/kernel/acpi.c +++ b/arch/loongarch/kernel/acpi.c @@ -104,6 +104,39 @@ static int set_processor_mask(u32 id, u32 flags) } #endif +static int __init +acpi_parse_processor(union acpi_subtable_headers *header, const unsigned long end) +{ + struct acpi_madt_core_pic *processor = NULL; + + processor = (struct acpi_madt_core_pic *)header; + if (BAD_MADT_ENTRY(processor, end)) + return -EINVAL; + + acpi_table_print_madt_entry(&header->common); +#ifdef CONFIG_SMP + set_processor_mask(processor->core_id, processor->flags); +#endif + + return 0; +} + +static int __init +acpi_parse_eio_master(union acpi_subtable_headers *header, const unsigned long end) +{ + static int core = 0; + struct acpi_madt_eio_pic *eiointc = NULL; + + eiointc = (struct acpi_madt_eio_pic *)header; + if (BAD_MADT_ENTRY(eiointc, end)) + return -EINVAL; + + core = eiointc->node * CORES_PER_EIO_NODE; + set_bit(core, &(loongson_sysconf.cores_io_master)); + + return 0; +} + static void __init acpi_process_madt(void) { #ifdef CONFIG_SMP @@ -114,6 +147,11 @@ static void __init acpi_process_madt(void) __cpu_logical_map[i] = -1; } #endif + acpi_table_parse_madt(ACPI_MADT_TYPE_CORE_PIC, + acpi_parse_processor, MAX_CORE_PIC); + + acpi_table_parse_madt(ACPI_MADT_TYPE_EIO_PIC, + acpi_parse_eio_master, MAX_IO_PICS); loongson_sysconf.nr_cpus = num_processors; } diff --git a/arch/loongarch/kernel/asm-offsets.c b/arch/loongarch/kernel/asm-offsets.c index d256b81c397a..bdd88eda9513 100644 --- a/arch/loongarch/kernel/asm-offsets.c +++ b/arch/loongarch/kernel/asm-offsets.c @@ -103,6 +103,8 @@ void output_thread_defines(void) OFFSET(THREAD_REG29, task_struct, thread.reg29); OFFSET(THREAD_REG30, task_struct, thread.reg30); OFFSET(THREAD_REG31, task_struct, thread.reg31); + OFFSET(THREAD_SCHED_RA, task_struct, thread.sched_ra); + OFFSET(THREAD_SCHED_CFA, task_struct, thread.sched_cfa); OFFSET(THREAD_CSRCRMD, task_struct, thread.csr_crmd); OFFSET(THREAD_CSRPRMD, task_struct, diff --git a/arch/loongarch/kernel/head.S b/arch/loongarch/kernel/head.S index 7062cdf0e33e..c60eb66793e3 100644 --- a/arch/loongarch/kernel/head.S +++ b/arch/loongarch/kernel/head.S @@ -21,6 +21,12 @@ SYM_CODE_START(kernel_entry) # kernel entry point csrwr t0, LOONGARCH_CSR_DMWIN0 li.d t0, CSR_DMW1_INIT # CA, PLV0, 0x9000 xxxx xxxx xxxx csrwr t0, LOONGARCH_CSR_DMWIN1 + + /* We might not get launched at the address the kernel is linked to, + so we jump there. */ + la.abs t0, 0f + jr t0 +0: /* Enable PG */ li.w t0, 0xb0 # PLV=0, IE=0, PG=1 csrwr t0, LOONGARCH_CSR_CRMD @@ -29,11 +35,6 @@ SYM_CODE_START(kernel_entry) # kernel entry point li.w t0, 0x00 # FPE=0, SXE=0, ASXE=0, BTE=0 csrwr t0, LOONGARCH_CSR_EUEN - /* We might not get launched at the address the kernel is linked to, - so we jump there. */ - la.abs t0, 0f - jr t0 -0: la t0, __bss_start # clear .bss st.d zero, t0, 0 la t1, __bss_stop - LONGSIZE @@ -74,6 +75,11 @@ SYM_CODE_START(smpboot_entry) csrwr t0, LOONGARCH_CSR_DMWIN0 li.d t0, CSR_DMW1_INIT # CA, PLV0 csrwr t0, LOONGARCH_CSR_DMWIN1 + + la.abs t0, 0f + jr t0 +0: + /* Enable PG */ li.w t0, 0xb0 # PLV=0, IE=0, PG=1 csrwr t0, LOONGARCH_CSR_CRMD li.w t0, 0x04 # PLV=0, PIE=1, PWE=0 @@ -85,9 +91,6 @@ SYM_CODE_START(smpboot_entry) ld.d sp, t0, CPU_BOOT_STACK ld.d tp, t0, CPU_BOOT_TINFO - la.abs t0, 0f - jr t0 -0: bl start_secondary SYM_CODE_END(smpboot_entry) diff --git a/arch/loongarch/kernel/proc.c b/arch/loongarch/kernel/proc.c index 1effc73850fe..5c67cc4fd56d 100644 --- a/arch/loongarch/kernel/proc.c +++ b/arch/loongarch/kernel/proc.c @@ -106,7 +106,7 @@ static void *c_start(struct seq_file *m, loff_t *pos) { unsigned long i = *pos; - return i < NR_CPUS ? (void *)(i + 1) : NULL; + return i < nr_cpu_ids ? (void *)(i + 1) : NULL; } static void *c_next(struct seq_file *m, void *v, loff_t *pos) diff --git a/arch/loongarch/kernel/process.c b/arch/loongarch/kernel/process.c index bfa0dfe8b7d7..660492f064e7 100644 --- a/arch/loongarch/kernel/process.c +++ b/arch/loongarch/kernel/process.c @@ -44,6 +44,7 @@ #include <asm/pgtable.h> #include <asm/processor.h> #include <asm/reg.h> +#include <asm/unwind.h> #include <asm/vdso.h> /* @@ -134,6 +135,7 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args) childregs = (struct pt_regs *) childksp - 1; /* Put the stack after the struct pt_regs. */ childksp = (unsigned long) childregs; + p->thread.sched_cfa = 0; p->thread.csr_euen = 0; p->thread.csr_crmd = csr_read32(LOONGARCH_CSR_CRMD); p->thread.csr_prmd = csr_read32(LOONGARCH_CSR_PRMD); @@ -144,6 +146,7 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args) p->thread.reg23 = (unsigned long)args->fn; p->thread.reg24 = (unsigned long)args->fn_arg; p->thread.reg01 = (unsigned long)ret_from_kernel_thread; + p->thread.sched_ra = (unsigned long)ret_from_kernel_thread; memset(childregs, 0, sizeof(struct pt_regs)); childregs->csr_euen = p->thread.csr_euen; childregs->csr_crmd = p->thread.csr_crmd; @@ -160,6 +163,7 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args) p->thread.reg03 = (unsigned long) childregs; p->thread.reg01 = (unsigned long) ret_from_fork; + p->thread.sched_ra = (unsigned long) ret_from_fork; /* * New tasks lose permission to use the fpu. This accelerates context @@ -180,7 +184,91 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args) unsigned long __get_wchan(struct task_struct *task) { - return 0; + unsigned long pc; + struct unwind_state state; + + if (!try_get_task_stack(task)) + return 0; + + unwind_start(&state, task, NULL); + state.sp = thread_saved_fp(task); + get_stack_info(state.sp, state.task, &state.stack_info); + state.pc = thread_saved_ra(task); +#ifdef CONFIG_UNWINDER_PROLOGUE + state.type = UNWINDER_PROLOGUE; +#endif + for (; !unwind_done(&state); unwind_next_frame(&state)) { + pc = unwind_get_return_address(&state); + if (!pc) + break; + if (in_sched_functions(pc)) + continue; + break; + } + + put_task_stack(task); + + return pc; +} + +bool in_irq_stack(unsigned long stack, struct stack_info *info) +{ + unsigned long nextsp; + unsigned long begin = (unsigned long)this_cpu_read(irq_stack); + unsigned long end = begin + IRQ_STACK_START; + + if (stack < begin || stack >= end) + return false; + + nextsp = *(unsigned long *)end; + if (nextsp & (SZREG - 1)) + return false; + + info->begin = begin; + info->end = end; + info->next_sp = nextsp; + info->type = STACK_TYPE_IRQ; + + return true; +} + +bool in_task_stack(unsigned long stack, struct task_struct *task, + struct stack_info *info) +{ + unsigned long begin = (unsigned long)task_stack_page(task); + unsigned long end = begin + THREAD_SIZE - 32; + + if (stack < begin || stack >= end) + return false; + + info->begin = begin; + info->end = end; + info->next_sp = 0; + info->type = STACK_TYPE_TASK; + + return true; +} + +int get_stack_info(unsigned long stack, struct task_struct *task, + struct stack_info *info) +{ + task = task ? : current; + + if (!stack || stack & (SZREG - 1)) + goto unknown; + + if (in_task_stack(stack, task, info)) + return 0; + + if (task != current) + goto unknown; + + if (in_irq_stack(stack, info)) + return 0; + +unknown: + info->type = STACK_TYPE_UNKNOWN; + return -EINVAL; } unsigned long stack_top(void) diff --git a/arch/loongarch/kernel/smp.c b/arch/loongarch/kernel/smp.c index 09743103d9b3..b5fab308dcf2 100644 --- a/arch/loongarch/kernel/smp.c +++ b/arch/loongarch/kernel/smp.c @@ -242,10 +242,7 @@ void loongson3_smp_finish(void) static bool io_master(int cpu) { - if (cpu == 0) - return true; - - return false; + return test_bit(cpu, &loongson_sysconf.cores_io_master); } int loongson3_cpu_disable(void) diff --git a/arch/loongarch/kernel/stacktrace.c b/arch/loongarch/kernel/stacktrace.c new file mode 100644 index 000000000000..3a690f96f00c --- /dev/null +++ b/arch/loongarch/kernel/stacktrace.c @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Stack trace management functions + * + * Copyright (C) 2022 Loongson Technology Corporation Limited + */ +#include <linux/sched.h> +#include <linux/stacktrace.h> +#include <linux/uaccess.h> + +#include <asm/stacktrace.h> +#include <asm/unwind.h> + +void arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie, + struct task_struct *task, struct pt_regs *regs) +{ + unsigned long addr; + struct pt_regs dummyregs; + struct unwind_state state; + + regs = &dummyregs; + + if (task == current) { + regs->regs[3] = (unsigned long)__builtin_frame_address(0); + regs->csr_era = (unsigned long)__builtin_return_address(0); + } else { + regs->regs[3] = thread_saved_fp(task); + regs->csr_era = thread_saved_ra(task); + } + + regs->regs[1] = 0; + for (unwind_start(&state, task, regs); + !unwind_done(&state); unwind_next_frame(&state)) { + addr = unwind_get_return_address(&state); + if (!addr || !consume_entry(cookie, addr)) + break; + } +} + +static int +copy_stack_frame(unsigned long fp, struct stack_frame *frame) +{ + int ret = 1; + unsigned long err; + unsigned long __user *user_frame_tail; + + user_frame_tail = (unsigned long *)(fp - sizeof(struct stack_frame)); + if (!access_ok(user_frame_tail, sizeof(*frame))) + return 0; + + pagefault_disable(); + err = (__copy_from_user_inatomic(frame, user_frame_tail, sizeof(*frame))); + if (err || (unsigned long)user_frame_tail >= frame->fp) + ret = 0; + pagefault_enable(); + + return ret; +} + +void arch_stack_walk_user(stack_trace_consume_fn consume_entry, void *cookie, + const struct pt_regs *regs) +{ + unsigned long fp = regs->regs[22]; + + while (fp && !((unsigned long)fp & 0xf)) { + struct stack_frame frame; + + frame.fp = 0; + frame.ra = 0; + if (!copy_stack_frame(fp, &frame)) + break; + if (!frame.ra) + break; + if (!consume_entry(cookie, frame.ra)) + break; + fp = frame.fp; + } +} diff --git a/arch/loongarch/kernel/switch.S b/arch/loongarch/kernel/switch.S index 37e84ac8ffc2..43ebbc3990f7 100644 --- a/arch/loongarch/kernel/switch.S +++ b/arch/loongarch/kernel/switch.S @@ -21,6 +21,8 @@ SYM_FUNC_START(__switch_to) cpu_save_nonscratch a0 stptr.d ra, a0, THREAD_REG01 + stptr.d a3, a0, THREAD_SCHED_RA + stptr.d a4, a0, THREAD_SCHED_CFA move tp, a2 cpu_restore_nonscratch a1 diff --git a/arch/loongarch/kernel/time.c b/arch/loongarch/kernel/time.c index 79dc5eddf504..786735dcc8d6 100644 --- a/arch/loongarch/kernel/time.c +++ b/arch/loongarch/kernel/time.c @@ -135,7 +135,7 @@ static int get_timer_irq(void) int constant_clockevent_init(void) { - unsigned int irq; + int irq; unsigned int cpu = smp_processor_id(); unsigned long min_delta = 0x600; unsigned long max_delta = (1UL << 48) - 1; diff --git a/arch/loongarch/kernel/traps.c b/arch/loongarch/kernel/traps.c index 1bf58c65e2bf..aa1c95aaf595 100644 --- a/arch/loongarch/kernel/traps.c +++ b/arch/loongarch/kernel/traps.c @@ -43,6 +43,7 @@ #include <asm/stacktrace.h> #include <asm/tlb.h> #include <asm/types.h> +#include <asm/unwind.h> #include "access-helper.h" @@ -64,19 +65,20 @@ static void show_backtrace(struct task_struct *task, const struct pt_regs *regs, const char *loglvl, bool user) { unsigned long addr; - unsigned long *sp = (unsigned long *)(regs->regs[3] & ~3); + struct unwind_state state; + struct pt_regs *pregs = (struct pt_regs *)regs; + + if (!task) + task = current; + + if (user_mode(regs)) + state.type = UNWINDER_GUESS; printk("%sCall Trace:", loglvl); -#ifdef CONFIG_KALLSYMS - printk("%s\n", loglvl); -#endif - while (!kstack_end(sp)) { - if (__get_addr(&addr, sp++, user)) { - printk("%s (Bad stack address)", loglvl); - break; - } - if (__kernel_text_address(addr)) - print_ip_sym(loglvl, addr); + for (unwind_start(&state, task, pregs); + !unwind_done(&state); unwind_next_frame(&state)) { + addr = unwind_get_return_address(&state); + print_ip_sym(loglvl, addr); } printk("%s\n", loglvl); } diff --git a/arch/loongarch/kernel/unwind_guess.c b/arch/loongarch/kernel/unwind_guess.c new file mode 100644 index 000000000000..5afa6064d73e --- /dev/null +++ b/arch/loongarch/kernel/unwind_guess.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 Loongson Technology Corporation Limited + */ +#include <linux/kernel.h> + +#include <asm/unwind.h> + +unsigned long unwind_get_return_address(struct unwind_state *state) +{ + if (unwind_done(state)) + return 0; + else if (state->first) + return state->pc; + + return *(unsigned long *)(state->sp); +} +EXPORT_SYMBOL_GPL(unwind_get_return_address); + +void unwind_start(struct unwind_state *state, struct task_struct *task, + struct pt_regs *regs) +{ + memset(state, 0, sizeof(*state)); + + if (regs) { + state->sp = regs->regs[3]; + state->pc = regs->csr_era; + } + + state->task = task; + state->first = true; + + get_stack_info(state->sp, state->task, &state->stack_info); + + if (!unwind_done(state) && !__kernel_text_address(state->pc)) + unwind_next_frame(state); +} +EXPORT_SYMBOL_GPL(unwind_start); + +bool unwind_next_frame(struct unwind_state *state) +{ + struct stack_info *info = &state->stack_info; + unsigned long addr; + + if (unwind_done(state)) + return false; + + if (state->first) + state->first = false; + + do { + for (state->sp += sizeof(unsigned long); + state->sp < info->end; + state->sp += sizeof(unsigned long)) { + addr = *(unsigned long *)(state->sp); + + if (__kernel_text_address(addr)) + return true; + } + + state->sp = info->next_sp; + + } while (!get_stack_info(state->sp, state->task, info)); + + return false; +} +EXPORT_SYMBOL_GPL(unwind_next_frame); diff --git a/arch/loongarch/kernel/unwind_prologue.c b/arch/loongarch/kernel/unwind_prologue.c new file mode 100644 index 000000000000..b206d9159205 --- /dev/null +++ b/arch/loongarch/kernel/unwind_prologue.c @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 Loongson Technology Corporation Limited + */ +#include <linux/kallsyms.h> + +#include <asm/inst.h> +#include <asm/ptrace.h> +#include <asm/unwind.h> + +unsigned long unwind_get_return_address(struct unwind_state *state) +{ + + if (unwind_done(state)) + return 0; + else if (state->type) + return state->pc; + else if (state->first) + return state->pc; + + return *(unsigned long *)(state->sp); + +} +EXPORT_SYMBOL_GPL(unwind_get_return_address); + +static bool unwind_by_guess(struct unwind_state *state) +{ + struct stack_info *info = &state->stack_info; + unsigned long addr; + + for (state->sp += sizeof(unsigned long); + state->sp < info->end; + state->sp += sizeof(unsigned long)) { + addr = *(unsigned long *)(state->sp); + if (__kernel_text_address(addr)) + return true; + } + + return false; +} + +static bool unwind_by_prologue(struct unwind_state *state) +{ + struct stack_info *info = &state->stack_info; + union loongarch_instruction *ip, *ip_end; + unsigned long frame_size = 0, frame_ra = -1; + unsigned long size, offset, pc = state->pc; + + if (state->sp >= info->end || state->sp < info->begin) + return false; + + if (!kallsyms_lookup_size_offset(pc, &size, &offset)) + return false; + + ip = (union loongarch_instruction *)(pc - offset); + ip_end = (union loongarch_instruction *)pc; + + while (ip < ip_end) { + if (is_stack_alloc_ins(ip)) { + frame_size = (1 << 12) - ip->reg2i12_format.immediate; + ip++; + break; + } + ip++; + } + + if (!frame_size) { + if (state->first) + goto first; + + return false; + } + + while (ip < ip_end) { + if (is_ra_save_ins(ip)) { + frame_ra = ip->reg2i12_format.immediate; + break; + } + if (is_branch_ins(ip)) + break; + ip++; + } + + if (frame_ra < 0) { + if (state->first) { + state->sp = state->sp + frame_size; + goto first; + } + return false; + } + + if (state->first) + state->first = false; + + state->pc = *(unsigned long *)(state->sp + frame_ra); + state->sp = state->sp + frame_size; + return !!__kernel_text_address(state->pc); + +first: + state->first = false; + if (state->pc == state->ra) + return false; + + state->pc = state->ra; + + return !!__kernel_text_address(state->ra); +} + +void unwind_start(struct unwind_state *state, struct task_struct *task, + struct pt_regs *regs) +{ + memset(state, 0, sizeof(*state)); + + if (regs && __kernel_text_address(regs->csr_era)) { + state->pc = regs->csr_era; + state->sp = regs->regs[3]; + state->ra = regs->regs[1]; + state->type = UNWINDER_PROLOGUE; + } + + state->task = task; + state->first = true; + + get_stack_info(state->sp, state->task, &state->stack_info); + + if (!unwind_done(state) && !__kernel_text_address(state->pc)) + unwind_next_frame(state); +} +EXPORT_SYMBOL_GPL(unwind_start); + +bool unwind_next_frame(struct unwind_state *state) +{ + struct stack_info *info = &state->stack_info; + struct pt_regs *regs; + unsigned long pc; + + if (unwind_done(state)) + return false; + + do { + switch (state->type) { + case UNWINDER_GUESS: + state->first = false; + if (unwind_by_guess(state)) + return true; + break; + + case UNWINDER_PROLOGUE: + if (unwind_by_prologue(state)) + return true; + + if (info->type == STACK_TYPE_IRQ && + info->end == state->sp) { + regs = (struct pt_regs *)info->next_sp; + pc = regs->csr_era; + + if (user_mode(regs) || !__kernel_text_address(pc)) + return false; + + state->pc = pc; + state->sp = regs->regs[3]; + state->ra = regs->regs[1]; + state->first = true; + get_stack_info(state->sp, state->task, info); + + return true; + } + } + + state->sp = info->next_sp; + + } while (!get_stack_info(state->sp, state->task, info)); + + return false; +} +EXPORT_SYMBOL_GPL(unwind_next_frame); diff --git a/arch/loongarch/kernel/vdso.c b/arch/loongarch/kernel/vdso.c index e20c8ca87473..f32c38abd791 100644 --- a/arch/loongarch/kernel/vdso.c +++ b/arch/loongarch/kernel/vdso.c @@ -25,12 +25,14 @@ extern char vdso_start[], vdso_end[]; /* Kernel-provided data used by the VDSO. */ -static union loongarch_vdso_data { - u8 page[PAGE_SIZE]; - struct vdso_data data[CS_BASES]; +static union { + u8 page[VDSO_DATA_SIZE]; + struct loongarch_vdso_data vdata; } loongarch_vdso_data __page_aligned_data; -struct vdso_data *vdso_data = loongarch_vdso_data.data; + static struct page *vdso_pages[] = { NULL }; +struct vdso_data *vdso_data = loongarch_vdso_data.vdata.data; +struct vdso_pcpu_data *vdso_pdata = loongarch_vdso_data.vdata.pdata; static int vdso_mremap(const struct vm_special_mapping *sm, struct vm_area_struct *new_vma) { @@ -55,11 +57,14 @@ struct loongarch_vdso_info vdso_info = { static int __init init_vdso(void) { - unsigned long i, pfn; + unsigned long i, cpu, pfn; BUG_ON(!PAGE_ALIGNED(vdso_info.vdso)); BUG_ON(!PAGE_ALIGNED(vdso_info.size)); + for_each_possible_cpu(cpu) + vdso_pdata[cpu].node = cpu_to_node(cpu); + pfn = __phys_to_pfn(__pa_symbol(vdso_info.vdso)); for (i = 0; i < vdso_info.size / PAGE_SIZE; i++) vdso_info.code_mapping.pages[i] = pfn_to_page(pfn + i); @@ -93,9 +98,9 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) /* * Determine total area size. This includes the VDSO data itself - * and the data page. + * and the data pages. */ - vvar_size = PAGE_SIZE; + vvar_size = VDSO_DATA_SIZE; size = vvar_size + info->size; data_addr = get_unmapped_area(NULL, vdso_base(), size, 0, 0); @@ -103,7 +108,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) ret = data_addr; goto out; } - vdso_addr = data_addr + PAGE_SIZE; + vdso_addr = data_addr + VDSO_DATA_SIZE; vma = _install_special_mapping(mm, data_addr, vvar_size, VM_READ | VM_MAYREAD, @@ -115,8 +120,8 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) /* Map VDSO data page. */ ret = remap_pfn_range(vma, data_addr, - virt_to_phys(vdso_data) >> PAGE_SHIFT, - PAGE_SIZE, PAGE_READONLY); + virt_to_phys(&loongarch_vdso_data) >> PAGE_SHIFT, + vvar_size, PAGE_READONLY); if (ret) goto out; diff --git a/arch/loongarch/pci/acpi.c b/arch/loongarch/pci/acpi.c new file mode 100644 index 000000000000..bf921487333c --- /dev/null +++ b/arch/loongarch/pci/acpi.c @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ +#include <linux/pci.h> +#include <linux/acpi.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/slab.h> +#include <linux/pci-acpi.h> +#include <linux/pci-ecam.h> + +#include <asm/pci.h> +#include <asm/numa.h> +#include <asm/loongson.h> + +struct pci_root_info { + struct acpi_pci_root_info common; + struct pci_config_window *cfg; +}; + +void pcibios_add_bus(struct pci_bus *bus) +{ + acpi_pci_add_bus(bus); +} + +int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge) +{ + struct pci_config_window *cfg = bridge->bus->sysdata; + struct acpi_device *adev = to_acpi_device(cfg->parent); + struct device *bus_dev = &bridge->bus->dev; + + ACPI_COMPANION_SET(&bridge->dev, adev); + set_dev_node(bus_dev, pa_to_nid(cfg->res.start)); + + return 0; +} + +int acpi_pci_bus_find_domain_nr(struct pci_bus *bus) +{ + struct pci_config_window *cfg = bus->sysdata; + struct acpi_device *adev = to_acpi_device(cfg->parent); + struct acpi_pci_root *root = acpi_driver_data(adev); + + return root->segment; +} + +static void acpi_release_root_info(struct acpi_pci_root_info *ci) +{ + struct pci_root_info *info; + + info = container_of(ci, struct pci_root_info, common); + pci_ecam_free(info->cfg); + kfree(ci->ops); + kfree(info); +} + +static int acpi_prepare_root_resources(struct acpi_pci_root_info *ci) +{ + int status; + struct resource_entry *entry, *tmp; + struct acpi_device *device = ci->bridge; + + status = acpi_pci_probe_root_resources(ci); + if (status > 0) { + resource_list_for_each_entry_safe(entry, tmp, &ci->resources) { + if (entry->res->flags & IORESOURCE_MEM) { + entry->offset = ci->root->mcfg_addr & GENMASK_ULL(63, 40); + entry->res->start |= entry->offset; + entry->res->end |= entry->offset; + } + } + return status; + } + + resource_list_for_each_entry_safe(entry, tmp, &ci->resources) { + dev_dbg(&device->dev, + "host bridge window %pR (ignored)\n", entry->res); + resource_list_destroy_entry(entry); + } + + return 0; +} + +/* + * Lookup the bus range for the domain in MCFG, and set up config space + * mapping. + */ +static struct pci_config_window * +pci_acpi_setup_ecam_mapping(struct acpi_pci_root *root) +{ + int ret, bus_shift; + u16 seg = root->segment; + struct device *dev = &root->device->dev; + struct resource cfgres; + struct resource *bus_res = &root->secondary; + struct pci_config_window *cfg; + const struct pci_ecam_ops *ecam_ops; + + ret = pci_mcfg_lookup(root, &cfgres, &ecam_ops); + if (ret < 0) { + dev_err(dev, "%04x:%pR ECAM region not found, use default value\n", seg, bus_res); + ecam_ops = &loongson_pci_ecam_ops; + root->mcfg_addr = mcfg_addr_init(0); + } + + bus_shift = ecam_ops->bus_shift ? : 20; + + cfgres.start = root->mcfg_addr + (bus_res->start << bus_shift); + cfgres.end = cfgres.start + (resource_size(bus_res) << bus_shift) - 1; + cfgres.flags = IORESOURCE_MEM; + + cfg = pci_ecam_create(dev, &cfgres, bus_res, ecam_ops); + if (IS_ERR(cfg)) { + dev_err(dev, "%04x:%pR error %ld mapping ECAM\n", seg, bus_res, PTR_ERR(cfg)); + return NULL; + } + + return cfg; +} + +struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) +{ + struct pci_bus *bus; + struct pci_root_info *info; + struct acpi_pci_root_ops *root_ops; + int domain = root->segment; + int busnum = root->secondary.start; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + pr_warn("pci_bus %04x:%02x: ignored (out of memory)\n", domain, busnum); + return NULL; + } + + root_ops = kzalloc(sizeof(*root_ops), GFP_KERNEL); + if (!root_ops) { + kfree(info); + return NULL; + } + + info->cfg = pci_acpi_setup_ecam_mapping(root); + if (!info->cfg) { + kfree(info); + kfree(root_ops); + return NULL; + } + + root_ops->release_info = acpi_release_root_info; + root_ops->prepare_resources = acpi_prepare_root_resources; + root_ops->pci_ops = (struct pci_ops *)&info->cfg->ops->pci_ops; + + bus = pci_find_bus(domain, busnum); + if (bus) { + memcpy(bus->sysdata, info->cfg, sizeof(struct pci_config_window)); + kfree(info); + } else { + struct pci_bus *child; + + bus = acpi_pci_root_create(root, root_ops, + &info->common, info->cfg); + if (!bus) { + kfree(info); + kfree(root_ops); + return NULL; + } + + pci_bus_size_bridges(bus); + pci_bus_assign_resources(bus); + list_for_each_entry(child, &bus->children, node) + pcie_bus_configure_settings(child); + } + + return bus; +} diff --git a/arch/loongarch/pci/pci.c b/arch/loongarch/pci/pci.c new file mode 100644 index 000000000000..e9b7c34d9b6d --- /dev/null +++ b/arch/loongarch/pci/pci.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ +#include <linux/kernel.h> +#include <linux/export.h> +#include <linux/init.h> +#include <linux/acpi.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/vgaarb.h> +#include <asm/loongson.h> + +#define PCI_DEVICE_ID_LOONGSON_HOST 0x7a00 +#define PCI_DEVICE_ID_LOONGSON_DC1 0x7a06 +#define PCI_DEVICE_ID_LOONGSON_DC2 0x7a36 + +int raw_pci_read(unsigned int domain, unsigned int bus, unsigned int devfn, + int reg, int len, u32 *val) +{ + struct pci_bus *bus_tmp = pci_find_bus(domain, bus); + + if (bus_tmp) + return bus_tmp->ops->read(bus_tmp, devfn, reg, len, val); + return -EINVAL; +} + +int raw_pci_write(unsigned int domain, unsigned int bus, unsigned int devfn, + int reg, int len, u32 val) +{ + struct pci_bus *bus_tmp = pci_find_bus(domain, bus); + + if (bus_tmp) + return bus_tmp->ops->write(bus_tmp, devfn, reg, len, val); + return -EINVAL; +} + +phys_addr_t mcfg_addr_init(int node) +{ + return (((u64)node << 44) | MCFG_EXT_PCICFG_BASE); +} + +static int __init pcibios_init(void) +{ + unsigned int lsize; + + /* + * Set PCI cacheline size to that of the highest level in the + * cache hierarchy. + */ + lsize = cpu_dcache_line_size(); + lsize = cpu_vcache_line_size() ? : lsize; + lsize = cpu_scache_line_size() ? : lsize; + + BUG_ON(!lsize); + + pci_dfl_cache_line_size = lsize >> 2; + + pr_debug("PCI: pci_cache_line_size set to %d bytes\n", lsize); + + return 0; +} + +subsys_initcall(pcibios_init); + +int pcibios_device_add(struct pci_dev *dev) +{ + int id; + struct irq_domain *dom; + + id = pci_domain_nr(dev->bus); + dom = irq_find_matching_fwnode(get_pch_msi_handle(id), DOMAIN_BUS_PCI_MSI); + dev_set_msi_domain(&dev->dev, dom); + + return 0; +} + +int pcibios_alloc_irq(struct pci_dev *dev) +{ + if (acpi_disabled) + return 0; + if (pci_dev_msi_enabled(dev)) + return 0; + return acpi_pci_irq_enable(dev); +} + +static void pci_fixup_vgadev(struct pci_dev *pdev) +{ + struct pci_dev *devp = NULL; + + while ((devp = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, devp))) { + if (devp->vendor != PCI_VENDOR_ID_LOONGSON) { + vga_set_default_device(devp); + dev_info(&pdev->dev, + "Overriding boot device as %X:%X\n", + devp->vendor, devp->device); + } + } +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_DC1, pci_fixup_vgadev); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_DC2, pci_fixup_vgadev); diff --git a/arch/loongarch/vdso/Makefile b/arch/loongarch/vdso/Makefile index 92e404032257..d89e2ac75f7b 100644 --- a/arch/loongarch/vdso/Makefile +++ b/arch/loongarch/vdso/Makefile @@ -6,7 +6,7 @@ ARCH_REL_TYPE_ABS := R_LARCH_32|R_LARCH_64|R_LARCH_MARK_LA|R_LARCH_JUMP_SLOT include $(srctree)/lib/vdso/Makefile -obj-vdso-y := elf.o vgettimeofday.o sigreturn.o +obj-vdso-y := elf.o vgetcpu.o vgettimeofday.o sigreturn.o # Common compiler flags between ABIs. ccflags-vdso := \ diff --git a/arch/loongarch/vdso/vdso.lds.S b/arch/loongarch/vdso/vdso.lds.S index 955f02de4a2d..56ad855896de 100644 --- a/arch/loongarch/vdso/vdso.lds.S +++ b/arch/loongarch/vdso/vdso.lds.S @@ -58,6 +58,7 @@ VERSION { LINUX_5.10 { global: + __vdso_getcpu; __vdso_clock_getres; __vdso_clock_gettime; __vdso_gettimeofday; diff --git a/arch/loongarch/vdso/vgetcpu.c b/arch/loongarch/vdso/vgetcpu.c new file mode 100644 index 000000000000..43a0078e4418 --- /dev/null +++ b/arch/loongarch/vdso/vgetcpu.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Fast user context implementation of getcpu() + */ + +#include <asm/vdso.h> +#include <linux/getcpu.h> + +static __always_inline int read_cpu_id(void) +{ + int cpu_id; + + __asm__ __volatile__( + " rdtime.d $zero, %0\n" + : "=r" (cpu_id) + : + : "memory"); + + return cpu_id; +} + +static __always_inline const struct vdso_pcpu_data *get_pcpu_data(void) +{ + return (struct vdso_pcpu_data *)(get_vdso_base() - VDSO_DATA_SIZE); +} + +int __vdso_getcpu(unsigned int *cpu, unsigned int *node, struct getcpu_cache *unused) +{ + int cpu_id; + const struct vdso_pcpu_data *data; + + cpu_id = read_cpu_id(); + + if (cpu) + *cpu = cpu_id; + + if (node) { + data = get_pcpu_data(); + *node = data[cpu_id].node; + } + + return 0; +} diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 5c52a82e83a8..ed66c31e4655 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -113,6 +113,7 @@ config RISCV select MODULES_USE_ELF_RELA if MODULES select MODULE_SECTIONS if MODULES select OF + select OF_DMA_DEFAULT_COHERENT select OF_EARLY_FLATTREE select OF_IRQ select PCI_DOMAINS_GENERIC if PCI @@ -218,6 +219,14 @@ config PGTABLE_LEVELS config LOCKDEP_SUPPORT def_bool y +config RISCV_DMA_NONCOHERENT + bool + select ARCH_HAS_DMA_PREP_COHERENT + select ARCH_HAS_SYNC_DMA_FOR_DEVICE + select ARCH_HAS_SYNC_DMA_FOR_CPU + select ARCH_HAS_SETUP_DMA_OPS + select DMA_DIRECT_REMAP + source "arch/riscv/Kconfig.socs" source "arch/riscv/Kconfig.erratas" @@ -392,6 +401,28 @@ config RISCV_ISA_SVPBMT If you don't know what to do here, say Y. +config CC_HAS_ZICBOM + bool + default y if 64BIT && $(cc-option,-mabi=lp64 -march=rv64ima_zicbom) + default y if 32BIT && $(cc-option,-mabi=ilp32 -march=rv32ima_zicbom) + +config RISCV_ISA_ZICBOM + bool "Zicbom extension support for non-coherent DMA operation" + depends on CC_HAS_ZICBOM + depends on !XIP_KERNEL && MMU + select RISCV_DMA_NONCOHERENT + select RISCV_ALTERNATIVE + default y + help + Adds support to dynamically detect the presence of the ZICBOM + extension (Cache Block Management Operations) and enable its + usage. + + The Zicbom extension can be used to handle for example + non-coherent DMA support on devices that need it. + + If you don't know what to do here, say Y. + config FPU bool "FPU support" default y @@ -463,7 +494,6 @@ config KEXEC_FILE config ARCH_HAS_KEXEC_PURGATORY def_bool KEXEC_FILE - select BUILD_BIN2C depends on CRYPTO=y depends on CRYPTO_SHA256=y diff --git a/arch/riscv/Kconfig.erratas b/arch/riscv/Kconfig.erratas index f62b62807e85..6850e9389930 100644 --- a/arch/riscv/Kconfig.erratas +++ b/arch/riscv/Kconfig.erratas @@ -55,4 +55,15 @@ config ERRATA_THEAD_PBMT If you don't know what to do here, say "Y". +config ERRATA_THEAD_CMO + bool "Apply T-Head cache management errata" + depends on ERRATA_THEAD + select RISCV_DMA_NONCOHERENT + default y + help + This will apply the cache management errata to handle the + non-standard handling on non-coherent operations on T-Head SoCs. + + If you don't know what to do here, say "Y". + endmenu # "CPU errata selection" diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile index 81029d40a672..3fa8ef336822 100644 --- a/arch/riscv/Makefile +++ b/arch/riscv/Makefile @@ -56,6 +56,14 @@ riscv-march-$(CONFIG_RISCV_ISA_C) := $(riscv-march-y)c toolchain-need-zicsr-zifencei := $(call cc-option-yn, -march=$(riscv-march-y)_zicsr_zifencei) riscv-march-$(toolchain-need-zicsr-zifencei) := $(riscv-march-y)_zicsr_zifencei +# Check if the toolchain supports Zicbom extension +toolchain-supports-zicbom := $(call cc-option-yn, -march=$(riscv-march-y)_zicbom) +riscv-march-$(toolchain-supports-zicbom) := $(riscv-march-y)_zicbom + +# Check if the toolchain supports Zihintpause extension +toolchain-supports-zihintpause := $(call cc-option-yn, -march=$(riscv-march-y)_zihintpause) +riscv-march-$(toolchain-supports-zihintpause) := $(riscv-march-y)_zihintpause + KBUILD_CFLAGS += -march=$(subst fd,,$(riscv-march-y)) KBUILD_AFLAGS += -march=$(riscv-march-y) diff --git a/arch/riscv/boot/dts/canaan/Makefile b/arch/riscv/boot/dts/canaan/Makefile index c61b08ac8554..befe4eb7527b 100644 --- a/arch/riscv/boot/dts/canaan/Makefile +++ b/arch/riscv/boot/dts/canaan/Makefile @@ -1,3 +1,9 @@ # SPDX-License-Identifier: GPL-2.0 -dtb-$(CONFIG_SOC_CANAAN_K210_DTB_BUILTIN) += $(addsuffix .dtb, $(CONFIG_SOC_CANAAN_K210_DTB_SOURCE)) -obj-$(CONFIG_SOC_CANAAN_K210_DTB_BUILTIN) += $(addsuffix .o, $(dtb-y)) +dtb-$(CONFIG_SOC_CANAAN) += canaan_kd233.dtb +dtb-$(CONFIG_SOC_CANAAN) += k210_generic.dtb +dtb-$(CONFIG_SOC_CANAAN) += sipeed_maix_bit.dtb +dtb-$(CONFIG_SOC_CANAAN) += sipeed_maix_dock.dtb +dtb-$(CONFIG_SOC_CANAAN) += sipeed_maix_go.dtb +dtb-$(CONFIG_SOC_CANAAN) += sipeed_maixduino.dtb + +obj-$(CONFIG_SOC_CANAAN_K210_DTB_BUILTIN) += $(addsuffix .dtb.o, $(CONFIG_SOC_CANAAN_K210_DTB_SOURCE)) diff --git a/arch/riscv/boot/dts/canaan/canaan_kd233.dts b/arch/riscv/boot/dts/canaan/canaan_kd233.dts index f72540bd14a3..8df4cf3656f2 100644 --- a/arch/riscv/boot/dts/canaan/canaan_kd233.dts +++ b/arch/riscv/boot/dts/canaan/canaan_kd233.dts @@ -127,10 +127,10 @@ cs-gpios = <&gpio0 20 GPIO_ACTIVE_HIGH>; panel@0 { - compatible = "ilitek,ili9341"; + compatible = "canaan,kd233-tft", "ilitek,ili9341"; reg = <0>; dc-gpios = <&gpio0 21 GPIO_ACTIVE_HIGH>; - spi-max-frequency = <15000000>; + spi-max-frequency = <10000000>; status = "disabled"; }; }; @@ -142,7 +142,7 @@ cs-gpios = <&gpio0 16 GPIO_ACTIVE_LOW>; status = "okay"; - slot@0 { + mmc@0 { compatible = "mmc-spi-slot"; reg = <0>; voltage-ranges = <3300 3300>; diff --git a/arch/riscv/boot/dts/canaan/k210.dtsi b/arch/riscv/boot/dts/canaan/k210.dtsi index ec944d1537dc..07e2e2649604 100644 --- a/arch/riscv/boot/dts/canaan/k210.dtsi +++ b/arch/riscv/boot/dts/canaan/k210.dtsi @@ -81,11 +81,13 @@ sram: memory@80000000 { device_type = "memory"; + reg = <0x80000000 0x400000>, /* sram0 4 MiB */ + <0x80400000 0x200000>, /* sram1 2 MiB */ + <0x80600000 0x200000>; /* aisram 2 MiB */ + }; + + sram_controller: memory-controller { compatible = "canaan,k210-sram"; - reg = <0x80000000 0x400000>, - <0x80400000 0x200000>, - <0x80600000 0x200000>; - reg-names = "sram0", "sram1", "aisram"; clocks = <&sysclk K210_CLK_SRAM0>, <&sysclk K210_CLK_SRAM1>, <&sysclk K210_CLK_AI>; @@ -173,7 +175,7 @@ #address-cells = <1>; #size-cells = <1>; compatible = "simple-pm-bus"; - ranges; + ranges = <0x50200000 0x50200000 0x200000>; clocks = <&sysclk K210_CLK_APB0>; gpio1: gpio@50200000 { @@ -261,7 +263,7 @@ }; i2s0: i2s@50250000 { - compatible = "snps,designware-i2s"; + compatible = "canaan,k210-i2s", "snps,designware-i2s"; reg = <0x50250000 0x200>; interrupts = <5>; clocks = <&sysclk K210_CLK_I2S0>; @@ -270,7 +272,7 @@ }; i2s1: i2s@50260000 { - compatible = "snps,designware-i2s"; + compatible = "canaan,k210-i2s", "snps,designware-i2s"; reg = <0x50260000 0x200>; interrupts = <6>; clocks = <&sysclk K210_CLK_I2S1>; @@ -279,7 +281,7 @@ }; i2s2: i2s@50270000 { - compatible = "snps,designware-i2s"; + compatible = "canaan,k210-i2s", "snps,designware-i2s"; reg = <0x50270000 0x200>; interrupts = <7>; clocks = <&sysclk K210_CLK_I2S2>; @@ -329,28 +331,58 @@ timer0: timer@502d0000 { compatible = "snps,dw-apb-timer"; - reg = <0x502D0000 0x100>; - interrupts = <14>, <15>; + reg = <0x502D0000 0x14>; + interrupts = <14>; clocks = <&sysclk K210_CLK_TIMER0>, <&sysclk K210_CLK_APB0>; clock-names = "timer", "pclk"; resets = <&sysrst K210_RST_TIMER0>; }; - timer1: timer@502e0000 { + timer1: timer@502d0014 { + compatible = "snps,dw-apb-timer"; + reg = <0x502D0014 0x14>; + interrupts = <15>; + clocks = <&sysclk K210_CLK_TIMER0>, + <&sysclk K210_CLK_APB0>; + clock-names = "timer", "pclk"; + resets = <&sysrst K210_RST_TIMER0>; + }; + + timer2: timer@502e0000 { + compatible = "snps,dw-apb-timer"; + reg = <0x502E0000 0x14>; + interrupts = <16>; + clocks = <&sysclk K210_CLK_TIMER1>, + <&sysclk K210_CLK_APB0>; + clock-names = "timer", "pclk"; + resets = <&sysrst K210_RST_TIMER1>; + }; + + timer3: timer@502e0014 { compatible = "snps,dw-apb-timer"; - reg = <0x502E0000 0x100>; - interrupts = <16>, <17>; + reg = <0x502E0014 0x114>; + interrupts = <17>; clocks = <&sysclk K210_CLK_TIMER1>, <&sysclk K210_CLK_APB0>; clock-names = "timer", "pclk"; resets = <&sysrst K210_RST_TIMER1>; }; - timer2: timer@502f0000 { + timer4: timer@502f0000 { compatible = "snps,dw-apb-timer"; - reg = <0x502F0000 0x100>; - interrupts = <18>, <19>; + reg = <0x502F0000 0x14>; + interrupts = <18>; + clocks = <&sysclk K210_CLK_TIMER2>, + <&sysclk K210_CLK_APB0>; + clock-names = "timer", "pclk"; + resets = <&sysrst K210_RST_TIMER2>; + }; + + timer5: timer@502f0014 { + compatible = "snps,dw-apb-timer"; + reg = <0x502F0014 0x14>; + interrupts = <19>; clocks = <&sysclk K210_CLK_TIMER2>, <&sysclk K210_CLK_APB0>; clock-names = "timer", "pclk"; @@ -362,7 +394,7 @@ #address-cells = <1>; #size-cells = <1>; compatible = "simple-pm-bus"; - ranges; + ranges = <0x50400000 0x50400000 0x40100>; clocks = <&sysclk K210_CLK_APB1>; wdt0: watchdog@50400000 { @@ -417,7 +449,7 @@ #address-cells = <1>; #size-cells = <1>; compatible = "simple-pm-bus"; - ranges; + ranges = <0x52000000 0x52000000 0x2000200>; clocks = <&sysclk K210_CLK_APB2>; spi0: spi@52000000 { @@ -431,7 +463,6 @@ clock-names = "ssi_clk", "pclk"; resets = <&sysrst K210_RST_SPI0>; reset-names = "spi"; - spi-max-frequency = <25000000>; num-cs = <4>; reg-io-width = <4>; }; @@ -447,7 +478,6 @@ clock-names = "ssi_clk", "pclk"; resets = <&sysrst K210_RST_SPI1>; reset-names = "spi"; - spi-max-frequency = <25000000>; num-cs = <4>; reg-io-width = <4>; }; @@ -463,8 +493,7 @@ clock-names = "ssi_clk", "pclk"; resets = <&sysrst K210_RST_SPI3>; reset-names = "spi"; - /* Could possibly go up to 200 MHz */ - spi-max-frequency = <100000000>; + num-cs = <4>; reg-io-width = <4>; }; diff --git a/arch/riscv/boot/dts/canaan/sipeed_maix_bit.dts b/arch/riscv/boot/dts/canaan/sipeed_maix_bit.dts index 8abdbe26a1d0..6d25bf07481a 100644 --- a/arch/riscv/boot/dts/canaan/sipeed_maix_bit.dts +++ b/arch/riscv/boot/dts/canaan/sipeed_maix_bit.dts @@ -189,7 +189,7 @@ cs-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>; status = "okay"; - slot@0 { + mmc@0 { compatible = "mmc-spi-slot"; reg = <0>; voltage-ranges = <3300 3300>; diff --git a/arch/riscv/boot/dts/canaan/sipeed_maix_dock.dts b/arch/riscv/boot/dts/canaan/sipeed_maix_dock.dts index 3c6df1ecf76f..f4f4d8d5e8b8 100644 --- a/arch/riscv/boot/dts/canaan/sipeed_maix_dock.dts +++ b/arch/riscv/boot/dts/canaan/sipeed_maix_dock.dts @@ -191,7 +191,7 @@ cs-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>; status = "okay"; - slot@0 { + mmc@0 { compatible = "mmc-spi-slot"; reg = <0>; voltage-ranges = <3300 3300>; diff --git a/arch/riscv/boot/dts/canaan/sipeed_maix_go.dts b/arch/riscv/boot/dts/canaan/sipeed_maix_go.dts index 03c9843d503e..0d86df47e1ed 100644 --- a/arch/riscv/boot/dts/canaan/sipeed_maix_go.dts +++ b/arch/riscv/boot/dts/canaan/sipeed_maix_go.dts @@ -199,7 +199,7 @@ cs-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>; status = "okay"; - slot@0 { + mmc@0 { compatible = "mmc-spi-slot"; reg = <0>; voltage-ranges = <3300 3300>; diff --git a/arch/riscv/boot/dts/canaan/sipeed_maixduino.dts b/arch/riscv/boot/dts/canaan/sipeed_maixduino.dts index 7164ad063178..5c05c498e2b8 100644 --- a/arch/riscv/boot/dts/canaan/sipeed_maixduino.dts +++ b/arch/riscv/boot/dts/canaan/sipeed_maixduino.dts @@ -164,7 +164,7 @@ cs-gpios = <&gpio1_0 2 GPIO_ACTIVE_LOW>; status = "okay"; - slot@0 { + mmc@0 { compatible = "mmc-spi-slot"; reg = <0>; voltage-ranges = <3300 3300>; diff --git a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts index 1f386b07a832..07387f9c135c 100644 --- a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts +++ b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts @@ -4,6 +4,8 @@ #include "fu740-c000.dtsi" #include <dt-bindings/gpio/gpio.h> #include <dt-bindings/interrupt-controller/irq.h> +#include <dt-bindings/leds/common.h> +#include <dt-bindings/pwm/pwm.h> /* Clock frequency (in Hz) of the PCB crystal for rtcclk */ #define RTCCLK_FREQ 1000000 @@ -44,6 +46,46 @@ compatible = "gpio-poweroff"; gpios = <&gpio 2 GPIO_ACTIVE_LOW>; }; + + led-controller-1 { + compatible = "pwm-leds"; + + led-d12 { + pwms = <&pwm0 0 7812500 PWM_POLARITY_INVERTED>; + active-low; + color = <LED_COLOR_ID_GREEN>; + max-brightness = <255>; + label = "d12"; + }; + }; + + led-controller-2 { + compatible = "pwm-leds-multicolor"; + + multi-led { + color = <LED_COLOR_ID_RGB>; + max-brightness = <255>; + label = "d2"; + + led-red { + pwms = <&pwm0 2 7812500 PWM_POLARITY_INVERTED>; + active-low; + color = <LED_COLOR_ID_RED>; + }; + + led-green { + pwms = <&pwm0 1 7812500 PWM_POLARITY_INVERTED>; + active-low; + color = <LED_COLOR_ID_GREEN>; + }; + + led-blue { + pwms = <&pwm0 3 7812500 PWM_POLARITY_INVERTED>; + active-low; + color = <LED_COLOR_ID_BLUE>; + }; + }; + }; }; &uart0 { diff --git a/arch/riscv/boot/dts/starfive/jh7100.dtsi b/arch/riscv/boot/dts/starfive/jh7100.dtsi index c617a61e26e2..000447482aca 100644 --- a/arch/riscv/boot/dts/starfive/jh7100.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7100.dtsi @@ -130,7 +130,7 @@ interrupt-controller; #address-cells = <0>; #interrupt-cells = <1>; - riscv,ndev = <127>; + riscv,ndev = <133>; }; clkgen: clock-controller@11800000 { diff --git a/arch/riscv/errata/thead/errata.c b/arch/riscv/errata/thead/errata.c index b37b6fedd53b..202c83f677b2 100644 --- a/arch/riscv/errata/thead/errata.c +++ b/arch/riscv/errata/thead/errata.c @@ -27,6 +27,23 @@ static bool errata_probe_pbmt(unsigned int stage, return false; } +static bool errata_probe_cmo(unsigned int stage, + unsigned long arch_id, unsigned long impid) +{ +#ifdef CONFIG_ERRATA_THEAD_CMO + if (arch_id != 0 || impid != 0) + return false; + + if (stage == RISCV_ALTERNATIVES_EARLY_BOOT) + return false; + + riscv_noncoherent_supported(); + return true; +#else + return false; +#endif +} + static u32 thead_errata_probe(unsigned int stage, unsigned long archid, unsigned long impid) { @@ -35,6 +52,9 @@ static u32 thead_errata_probe(unsigned int stage, if (errata_probe_pbmt(stage, archid, impid)) cpu_req_errata |= (1U << ERRATA_THEAD_PBMT); + if (errata_probe_cmo(stage, archid, impid)) + cpu_req_errata |= (1U << ERRATA_THEAD_CMO); + return cpu_req_errata; } diff --git a/arch/riscv/include/asm/cache.h b/arch/riscv/include/asm/cache.h index 9b58b104559e..d3036df23ccb 100644 --- a/arch/riscv/include/asm/cache.h +++ b/arch/riscv/include/asm/cache.h @@ -11,6 +11,10 @@ #define L1_CACHE_BYTES (1 << L1_CACHE_SHIFT) +#ifdef CONFIG_RISCV_DMA_NONCOHERENT +#define ARCH_DMA_MINALIGN L1_CACHE_BYTES +#endif + /* * RISC-V requires the stack pointer to be 16-byte aligned, so ensure that * the flat loader aligns it accordingly. diff --git a/arch/riscv/include/asm/cacheflush.h b/arch/riscv/include/asm/cacheflush.h index 23ff70350992..a60acaecfeda 100644 --- a/arch/riscv/include/asm/cacheflush.h +++ b/arch/riscv/include/asm/cacheflush.h @@ -42,6 +42,16 @@ void flush_icache_mm(struct mm_struct *mm, bool local); #endif /* CONFIG_SMP */ +#ifdef CONFIG_RISCV_ISA_ZICBOM +void riscv_init_cbom_blocksize(void); +#else +static inline void riscv_init_cbom_blocksize(void) { } +#endif + +#ifdef CONFIG_RISCV_DMA_NONCOHERENT +void riscv_noncoherent_supported(void); +#endif + /* * Bits in sys_riscv_flush_icache()'s flags argument. */ diff --git a/arch/riscv/include/asm/cpu_ops.h b/arch/riscv/include/asm/cpu_ops.h index 134590f1b843..aa128466c4d4 100644 --- a/arch/riscv/include/asm/cpu_ops.h +++ b/arch/riscv/include/asm/cpu_ops.h @@ -38,6 +38,7 @@ struct cpu_operations { #endif }; +extern const struct cpu_operations cpu_ops_spinwait; extern const struct cpu_operations *cpu_ops[NR_CPUS]; void __init cpu_set_ops(int cpu); diff --git a/arch/riscv/include/asm/cpu_ops_sbi.h b/arch/riscv/include/asm/cpu_ops_sbi.h index 56e4b76d09ff..d6e4665b3195 100644 --- a/arch/riscv/include/asm/cpu_ops_sbi.h +++ b/arch/riscv/include/asm/cpu_ops_sbi.h @@ -10,6 +10,8 @@ #include <linux/sched.h> #include <linux/threads.h> +extern const struct cpu_operations cpu_ops_sbi; + /** * struct sbi_hart_boot_data - Hart specific boot used during booting and * cpu hotplug. diff --git a/arch/riscv/include/asm/csr.h b/arch/riscv/include/asm/csr.h index 17516afc389a..0e571f6483d9 100644 --- a/arch/riscv/include/asm/csr.h +++ b/arch/riscv/include/asm/csr.h @@ -247,6 +247,9 @@ #define CSR_SIP 0x144 #define CSR_SATP 0x180 +#define CSR_STIMECMP 0x14D +#define CSR_STIMECMPH 0x15D + #define CSR_VSSTATUS 0x200 #define CSR_VSIE 0x204 #define CSR_VSTVEC 0x205 @@ -256,6 +259,8 @@ #define CSR_VSTVAL 0x243 #define CSR_VSIP 0x244 #define CSR_VSATP 0x280 +#define CSR_VSTIMECMP 0x24D +#define CSR_VSTIMECMPH 0x25D #define CSR_HSTATUS 0x600 #define CSR_HEDELEG 0x602 diff --git a/arch/riscv/include/asm/errata_list.h b/arch/riscv/include/asm/errata_list.h index 398e351e7002..19a771085781 100644 --- a/arch/riscv/include/asm/errata_list.h +++ b/arch/riscv/include/asm/errata_list.h @@ -16,11 +16,13 @@ #ifdef CONFIG_ERRATA_THEAD #define ERRATA_THEAD_PBMT 0 -#define ERRATA_THEAD_NUMBER 1 +#define ERRATA_THEAD_CMO 1 +#define ERRATA_THEAD_NUMBER 2 #endif #define CPUFEATURE_SVPBMT 0 -#define CPUFEATURE_NUMBER 1 +#define CPUFEATURE_ZICBOM 1 +#define CPUFEATURE_NUMBER 2 #ifdef __ASSEMBLY__ @@ -87,6 +89,59 @@ asm volatile(ALTERNATIVE( \ #define ALT_THEAD_PMA(_val) #endif +/* + * dcache.ipa rs1 (invalidate, physical address) + * | 31 - 25 | 24 - 20 | 19 - 15 | 14 - 12 | 11 - 7 | 6 - 0 | + * 0000001 01010 rs1 000 00000 0001011 + * dache.iva rs1 (invalida, virtual address) + * 0000001 00110 rs1 000 00000 0001011 + * + * dcache.cpa rs1 (clean, physical address) + * | 31 - 25 | 24 - 20 | 19 - 15 | 14 - 12 | 11 - 7 | 6 - 0 | + * 0000001 01001 rs1 000 00000 0001011 + * dcache.cva rs1 (clean, virtual address) + * 0000001 00100 rs1 000 00000 0001011 + * + * dcache.cipa rs1 (clean then invalidate, physical address) + * | 31 - 25 | 24 - 20 | 19 - 15 | 14 - 12 | 11 - 7 | 6 - 0 | + * 0000001 01011 rs1 000 00000 0001011 + * dcache.civa rs1 (... virtual address) + * 0000001 00111 rs1 000 00000 0001011 + * + * sync.s (make sure all cache operations finished) + * | 31 - 25 | 24 - 20 | 19 - 15 | 14 - 12 | 11 - 7 | 6 - 0 | + * 0000000 11001 00000 000 00000 0001011 + */ +#define THEAD_inval_A0 ".long 0x0265000b" +#define THEAD_clean_A0 ".long 0x0245000b" +#define THEAD_flush_A0 ".long 0x0275000b" +#define THEAD_SYNC_S ".long 0x0190000b" + +#define ALT_CMO_OP(_op, _start, _size, _cachesize) \ +asm volatile(ALTERNATIVE_2( \ + __nops(6), \ + "mv a0, %1\n\t" \ + "j 2f\n\t" \ + "3:\n\t" \ + "cbo." __stringify(_op) " (a0)\n\t" \ + "add a0, a0, %0\n\t" \ + "2:\n\t" \ + "bltu a0, %2, 3b\n\t" \ + "nop", 0, CPUFEATURE_ZICBOM, CONFIG_RISCV_ISA_ZICBOM, \ + "mv a0, %1\n\t" \ + "j 2f\n\t" \ + "3:\n\t" \ + THEAD_##_op##_A0 "\n\t" \ + "add a0, a0, %0\n\t" \ + "2:\n\t" \ + "bltu a0, %2, 3b\n\t" \ + THEAD_SYNC_S, THEAD_VENDOR_ID, \ + ERRATA_THEAD_CMO, CONFIG_ERRATA_THEAD_CMO) \ + : : "r"(_cachesize), \ + "r"((unsigned long)(_start) & ~((_cachesize) - 1UL)), \ + "r"((unsigned long)(_start) + (_size)) \ + : "a0") + #endif /* __ASSEMBLY__ */ #endif diff --git a/arch/riscv/include/asm/hwcap.h b/arch/riscv/include/asm/hwcap.h index e48eebdd2631..6f59ec64175e 100644 --- a/arch/riscv/include/asm/hwcap.h +++ b/arch/riscv/include/asm/hwcap.h @@ -8,6 +8,7 @@ #ifndef _ASM_RISCV_HWCAP_H #define _ASM_RISCV_HWCAP_H +#include <asm/errno.h> #include <linux/bits.h> #include <uapi/asm/hwcap.h> @@ -54,6 +55,9 @@ extern unsigned long elf_hwcap; enum riscv_isa_ext_id { RISCV_ISA_EXT_SSCOFPMF = RISCV_ISA_EXT_BASE, RISCV_ISA_EXT_SVPBMT, + RISCV_ISA_EXT_ZICBOM, + RISCV_ISA_EXT_ZIHINTPAUSE, + RISCV_ISA_EXT_SSTC, RISCV_ISA_EXT_ID_MAX = RISCV_ISA_EXT_MAX, }; @@ -64,6 +68,7 @@ enum riscv_isa_ext_id { */ enum riscv_isa_ext_key { RISCV_ISA_EXT_KEY_FPU, /* For 'F' and 'D' */ + RISCV_ISA_EXT_KEY_ZIHINTPAUSE, RISCV_ISA_EXT_KEY_MAX, }; @@ -83,6 +88,8 @@ static __always_inline int riscv_isa_ext2key(int num) return RISCV_ISA_EXT_KEY_FPU; case RISCV_ISA_EXT_d: return RISCV_ISA_EXT_KEY_FPU; + case RISCV_ISA_EXT_ZIHINTPAUSE: + return RISCV_ISA_EXT_KEY_ZIHINTPAUSE; default: return -EINVAL; } diff --git a/arch/riscv/include/asm/kvm_vcpu_timer.h b/arch/riscv/include/asm/kvm_vcpu_timer.h index 50138e2eb91b..0d8fdb8ec63a 100644 --- a/arch/riscv/include/asm/kvm_vcpu_timer.h +++ b/arch/riscv/include/asm/kvm_vcpu_timer.h @@ -28,6 +28,11 @@ struct kvm_vcpu_timer { u64 next_cycles; /* Underlying hrtimer instance */ struct hrtimer hrt; + + /* Flag to check if sstc is enabled or not */ + bool sstc_enabled; + /* A function pointer to switch between stimecmp or hrtimer at runtime */ + int (*timer_next_event)(struct kvm_vcpu *vcpu, u64 ncycles); }; int kvm_riscv_vcpu_timer_next_event(struct kvm_vcpu *vcpu, u64 ncycles); @@ -40,5 +45,7 @@ int kvm_riscv_vcpu_timer_deinit(struct kvm_vcpu *vcpu); int kvm_riscv_vcpu_timer_reset(struct kvm_vcpu *vcpu); void kvm_riscv_vcpu_timer_restore(struct kvm_vcpu *vcpu); void kvm_riscv_guest_timer_init(struct kvm *kvm); +void kvm_riscv_vcpu_timer_save(struct kvm_vcpu *vcpu); +bool kvm_riscv_vcpu_timer_pending(struct kvm_vcpu *vcpu); #endif diff --git a/arch/riscv/include/asm/sbi.h b/arch/riscv/include/asm/sbi.h index 9e3c2cf1edaf..2a0ef738695e 100644 --- a/arch/riscv/include/asm/sbi.h +++ b/arch/riscv/include/asm/sbi.h @@ -122,7 +122,21 @@ enum sbi_ext_pmu_fid { SBI_EXT_PMU_COUNTER_FW_READ, }; -#define RISCV_PMU_RAW_EVENT_MASK GENMASK_ULL(55, 0) +union sbi_pmu_ctr_info { + unsigned long value; + struct { + unsigned long csr:12; + unsigned long width:6; +#if __riscv_xlen == 32 + unsigned long reserved:13; +#else + unsigned long reserved:45; +#endif + unsigned long type:1; + }; +}; + +#define RISCV_PMU_RAW_EVENT_MASK GENMASK_ULL(47, 0) #define RISCV_PMU_RAW_EVENT_IDX 0x20000 /** General pmu event codes specified in SBI PMU extension */ @@ -189,12 +203,26 @@ enum sbi_pmu_ctr_type { SBI_PMU_CTR_TYPE_FW, }; +/* Helper macros to decode event idx */ +#define SBI_PMU_EVENT_IDX_OFFSET 20 +#define SBI_PMU_EVENT_IDX_MASK 0xFFFFF +#define SBI_PMU_EVENT_IDX_CODE_MASK 0xFFFF +#define SBI_PMU_EVENT_IDX_TYPE_MASK 0xF0000 +#define SBI_PMU_EVENT_RAW_IDX 0x20000 +#define SBI_PMU_FIXED_CTR_MASK 0x07 + +#define SBI_PMU_EVENT_CACHE_ID_CODE_MASK 0xFFF8 +#define SBI_PMU_EVENT_CACHE_OP_ID_CODE_MASK 0x06 +#define SBI_PMU_EVENT_CACHE_RESULT_ID_CODE_MASK 0x01 + +#define SBI_PMU_EVENT_IDX_INVALID 0xFFFFFFFF + /* Flags defined for config matching function */ #define SBI_PMU_CFG_FLAG_SKIP_MATCH (1 << 0) #define SBI_PMU_CFG_FLAG_CLEAR_VALUE (1 << 1) #define SBI_PMU_CFG_FLAG_AUTO_START (1 << 2) #define SBI_PMU_CFG_FLAG_SET_VUINH (1 << 3) -#define SBI_PMU_CFG_FLAG_SET_VSNH (1 << 4) +#define SBI_PMU_CFG_FLAG_SET_VSINH (1 << 4) #define SBI_PMU_CFG_FLAG_SET_UINH (1 << 5) #define SBI_PMU_CFG_FLAG_SET_SINH (1 << 6) #define SBI_PMU_CFG_FLAG_SET_MINH (1 << 7) diff --git a/arch/riscv/include/asm/vdso/processor.h b/arch/riscv/include/asm/vdso/processor.h index 134388cbaaa1..1e4f8b4aef79 100644 --- a/arch/riscv/include/asm/vdso/processor.h +++ b/arch/riscv/include/asm/vdso/processor.h @@ -4,15 +4,30 @@ #ifndef __ASSEMBLY__ +#include <linux/jump_label.h> #include <asm/barrier.h> +#include <asm/hwcap.h> static inline void cpu_relax(void) { + if (!static_branch_likely(&riscv_isa_ext_keys[RISCV_ISA_EXT_KEY_ZIHINTPAUSE])) { #ifdef __riscv_muldiv - int dummy; - /* In lieu of a halt instruction, induce a long-latency stall. */ - __asm__ __volatile__ ("div %0, %0, zero" : "=r" (dummy)); + int dummy; + /* In lieu of a halt instruction, induce a long-latency stall. */ + __asm__ __volatile__ ("div %0, %0, zero" : "=r" (dummy)); #endif + } else { + /* + * Reduce instruction retirement. + * This assumes the PC changes. + */ +#ifdef __riscv_zihintpause + __asm__ __volatile__ ("pause"); +#else + /* Encoding of the pause instruction */ + __asm__ __volatile__ (".4byte 0x100000F"); +#endif + } barrier(); } diff --git a/arch/riscv/include/uapi/asm/kvm.h b/arch/riscv/include/uapi/asm/kvm.h index 24b2a6e27698..7351417afd62 100644 --- a/arch/riscv/include/uapi/asm/kvm.h +++ b/arch/riscv/include/uapi/asm/kvm.h @@ -97,6 +97,7 @@ enum KVM_RISCV_ISA_EXT_ID { KVM_RISCV_ISA_EXT_I, KVM_RISCV_ISA_EXT_M, KVM_RISCV_ISA_EXT_SVPBMT, + KVM_RISCV_ISA_EXT_SSTC, KVM_RISCV_ISA_EXT_MAX, }; diff --git a/arch/riscv/kernel/cpu.c b/arch/riscv/kernel/cpu.c index 022fd1861992..0be8a2403212 100644 --- a/arch/riscv/kernel/cpu.c +++ b/arch/riscv/kernel/cpu.c @@ -93,6 +93,9 @@ int riscv_of_parent_hartid(struct device_node *node, unsigned long *hartid) static struct riscv_isa_ext_data isa_ext_arr[] = { __RISCV_ISA_EXT_DATA(sscofpmf, RISCV_ISA_EXT_SSCOFPMF), __RISCV_ISA_EXT_DATA(svpbmt, RISCV_ISA_EXT_SVPBMT), + __RISCV_ISA_EXT_DATA(zicbom, RISCV_ISA_EXT_ZICBOM), + __RISCV_ISA_EXT_DATA(zihintpause, RISCV_ISA_EXT_ZIHINTPAUSE), + __RISCV_ISA_EXT_DATA(sstc, RISCV_ISA_EXT_SSTC), __RISCV_ISA_EXT_DATA("", RISCV_ISA_EXT_MAX), }; diff --git a/arch/riscv/kernel/cpu_ops.c b/arch/riscv/kernel/cpu_ops.c index 170d07e57721..8275f237a59d 100644 --- a/arch/riscv/kernel/cpu_ops.c +++ b/arch/riscv/kernel/cpu_ops.c @@ -9,15 +9,14 @@ #include <linux/string.h> #include <linux/sched.h> #include <asm/cpu_ops.h> +#include <asm/cpu_ops_sbi.h> #include <asm/sbi.h> #include <asm/smp.h> const struct cpu_operations *cpu_ops[NR_CPUS] __ro_after_init; extern const struct cpu_operations cpu_ops_sbi; -#ifdef CONFIG_RISCV_BOOT_SPINWAIT -extern const struct cpu_operations cpu_ops_spinwait; -#else +#ifndef CONFIG_RISCV_BOOT_SPINWAIT const struct cpu_operations cpu_ops_spinwait = { .name = "", .cpu_prepare = NULL, diff --git a/arch/riscv/kernel/cpu_ops_spinwait.c b/arch/riscv/kernel/cpu_ops_spinwait.c index 3ade9152a3c7..d98d19226b5f 100644 --- a/arch/riscv/kernel/cpu_ops_spinwait.c +++ b/arch/riscv/kernel/cpu_ops_spinwait.c @@ -11,6 +11,8 @@ #include <asm/sbi.h> #include <asm/smp.h> +#include "head.h" + const struct cpu_operations cpu_ops_spinwait; void *__cpu_spinwait_stack_pointer[NR_CPUS] __section(".data"); void *__cpu_spinwait_task_pointer[NR_CPUS] __section(".data"); diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c index e233fe154c96..553d755483ed 100644 --- a/arch/riscv/kernel/cpufeature.c +++ b/arch/riscv/kernel/cpufeature.c @@ -12,6 +12,7 @@ #include <linux/module.h> #include <linux/of.h> #include <asm/alternative.h> +#include <asm/cacheflush.h> #include <asm/errata_list.h> #include <asm/hwcap.h> #include <asm/patch.h> @@ -200,6 +201,9 @@ void __init riscv_fill_hwcap(void) } else { SET_ISA_EXT_MAP("sscofpmf", RISCV_ISA_EXT_SSCOFPMF); SET_ISA_EXT_MAP("svpbmt", RISCV_ISA_EXT_SVPBMT); + SET_ISA_EXT_MAP("zicbom", RISCV_ISA_EXT_ZICBOM); + SET_ISA_EXT_MAP("zihintpause", RISCV_ISA_EXT_ZIHINTPAUSE); + SET_ISA_EXT_MAP("sstc", RISCV_ISA_EXT_SSTC); } #undef SET_ISA_EXT_MAP } @@ -261,6 +265,25 @@ static bool __init_or_module cpufeature_probe_svpbmt(unsigned int stage) return false; } +static bool __init_or_module cpufeature_probe_zicbom(unsigned int stage) +{ +#ifdef CONFIG_RISCV_ISA_ZICBOM + switch (stage) { + case RISCV_ALTERNATIVES_EARLY_BOOT: + return false; + default: + if (riscv_isa_extension_available(NULL, ZICBOM)) { + riscv_noncoherent_supported(); + return true; + } else { + return false; + } + } +#endif + + return false; +} + /* * Probe presence of individual extensions. * @@ -275,6 +298,9 @@ static u32 __init_or_module cpufeature_probe(unsigned int stage) if (cpufeature_probe_svpbmt(stage)) cpu_req_feature |= (1U << CPUFEATURE_SVPBMT); + if (cpufeature_probe_zicbom(stage)) + cpu_req_feature |= (1U << CPUFEATURE_ZICBOM); + return cpu_req_feature; } diff --git a/arch/riscv/kernel/crash_save_regs.S b/arch/riscv/kernel/crash_save_regs.S index 7832fb763aba..b2a1908c0463 100644 --- a/arch/riscv/kernel/crash_save_regs.S +++ b/arch/riscv/kernel/crash_save_regs.S @@ -44,7 +44,7 @@ SYM_CODE_START(riscv_crash_save_regs) REG_S t6, PT_T6(a0) /* x31 */ csrr t1, CSR_STATUS - csrr t2, CSR_EPC + auipc t2, 0x0 csrr t3, CSR_TVAL csrr t4, CSR_CAUSE diff --git a/arch/riscv/kernel/machine_kexec.c b/arch/riscv/kernel/machine_kexec.c index df8e24559035..ee79e6839b86 100644 --- a/arch/riscv/kernel/machine_kexec.c +++ b/arch/riscv/kernel/machine_kexec.c @@ -138,19 +138,37 @@ void machine_shutdown(void) #endif } +/* Override the weak function in kernel/panic.c */ +void crash_smp_send_stop(void) +{ + static int cpus_stopped; + + /* + * This function can be called twice in panic path, but obviously + * we execute this only once. + */ + if (cpus_stopped) + return; + + smp_send_stop(); + cpus_stopped = 1; +} + /* * machine_crash_shutdown - Prepare to kexec after a kernel crash * * This function is called by crash_kexec just before machine_kexec - * below and its goal is similar to machine_shutdown, but in case of - * a kernel crash. Since we don't handle such cases yet, this function - * is empty. + * and its goal is to shutdown non-crashing cpus and save registers. */ void machine_crash_shutdown(struct pt_regs *regs) { + local_irq_disable(); + + /* shutdown non-crashing cpus */ + crash_smp_send_stop(); + crash_save_cpu(regs, smp_processor_id()); - machine_shutdown(); pr_info("Starting crashdump kernel...\n"); } @@ -171,7 +189,7 @@ machine_kexec(struct kimage *image) struct kimage_arch *internal = &image->arch; unsigned long jump_addr = (unsigned long) image->start; unsigned long first_ind_entry = (unsigned long) &image->head; - unsigned long this_cpu_id = smp_processor_id(); + unsigned long this_cpu_id = __smp_processor_id(); unsigned long this_hart_id = cpuid_to_hartid_map(this_cpu_id); unsigned long fdt_addr = internal->fdt_addr; void *control_code_buffer = page_address(image->control_code_page); diff --git a/arch/riscv/kernel/probes/uprobes.c b/arch/riscv/kernel/probes/uprobes.c index 7a057b5f0adc..c976a21cd4bd 100644 --- a/arch/riscv/kernel/probes/uprobes.c +++ b/arch/riscv/kernel/probes/uprobes.c @@ -59,8 +59,6 @@ int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) instruction_pointer_set(regs, utask->xol_vaddr); - regs->status &= ~SR_SPIE; - return 0; } @@ -72,8 +70,6 @@ int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) instruction_pointer_set(regs, utask->vaddr + auprobe->insn_size); - regs->status |= SR_SPIE; - return 0; } @@ -111,8 +107,6 @@ void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) * address. */ instruction_pointer_set(regs, utask->vaddr); - - regs->status &= ~SR_SPIE; } bool arch_uretprobe_is_alive(struct return_instance *ret, enum rp_check ctx, diff --git a/arch/riscv/kernel/setup.c b/arch/riscv/kernel/setup.c index f0f36a4a0e9b..95ef6e2bf45c 100644 --- a/arch/riscv/kernel/setup.c +++ b/arch/riscv/kernel/setup.c @@ -22,6 +22,7 @@ #include <linux/crash_dump.h> #include <asm/alternative.h> +#include <asm/cacheflush.h> #include <asm/cpu_ops.h> #include <asm/early_ioremap.h> #include <asm/pgtable.h> @@ -296,6 +297,7 @@ void __init setup_arch(char **cmdline_p) #endif riscv_fill_hwcap(); + riscv_init_cbom_blocksize(); apply_boot_alternatives(); } diff --git a/arch/riscv/kernel/traps_misaligned.c b/arch/riscv/kernel/traps_misaligned.c index 46c4dafe3ba0..378f5b151443 100644 --- a/arch/riscv/kernel/traps_misaligned.c +++ b/arch/riscv/kernel/traps_misaligned.c @@ -7,6 +7,7 @@ #include <linux/mm.h> #include <linux/module.h> #include <linux/irq.h> +#include <linux/stringify.h> #include <asm/processor.h> #include <asm/ptrace.h> @@ -150,9 +151,6 @@ #define PRECISION_S 0 #define PRECISION_D 1 -#define STR(x) XSTR(x) -#define XSTR(x) #x - #define DECLARE_UNPRIVILEGED_LOAD_FUNCTION(type, insn) \ static inline type load_##type(const type *addr) \ { \ @@ -207,9 +205,9 @@ static inline ulong get_insn(ulong mepc) asm ("and %[tmp], %[addr], 2\n" "bnez %[tmp], 1f\n" #if defined(CONFIG_64BIT) - STR(LWU) " %[insn], (%[addr])\n" + __stringify(LWU) " %[insn], (%[addr])\n" #else - STR(LW) " %[insn], (%[addr])\n" + __stringify(LW) " %[insn], (%[addr])\n" #endif "and %[tmp], %[insn], %[rvc_mask]\n" "beq %[tmp], %[rvc_mask], 2f\n" diff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c index 5d271b597613..d0f08d5b4282 100644 --- a/arch/riscv/kvm/vcpu.c +++ b/arch/riscv/kvm/vcpu.c @@ -52,6 +52,7 @@ static const unsigned long kvm_isa_ext_arr[] = { RISCV_ISA_EXT_i, RISCV_ISA_EXT_m, RISCV_ISA_EXT_SVPBMT, + RISCV_ISA_EXT_SSTC, }; static unsigned long kvm_riscv_vcpu_base2isa_ext(unsigned long base_ext) @@ -85,6 +86,7 @@ static bool kvm_riscv_vcpu_isa_disable_allowed(unsigned long ext) case KVM_RISCV_ISA_EXT_C: case KVM_RISCV_ISA_EXT_I: case KVM_RISCV_ISA_EXT_M: + case KVM_RISCV_ISA_EXT_SSTC: return false; default: break; @@ -203,7 +205,7 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu) { - return kvm_riscv_vcpu_has_interrupts(vcpu, 1UL << IRQ_VS_TIMER); + return kvm_riscv_vcpu_timer_pending(vcpu); } void kvm_arch_vcpu_blocking(struct kvm_vcpu *vcpu) @@ -785,6 +787,8 @@ static void kvm_riscv_vcpu_update_config(const unsigned long *isa) if (__riscv_isa_extension_available(isa, RISCV_ISA_EXT_SVPBMT)) henvcfg |= ENVCFG_PBMTE; + if (__riscv_isa_extension_available(isa, RISCV_ISA_EXT_SSTC)) + henvcfg |= ENVCFG_STCE; csr_write(CSR_HENVCFG, henvcfg); #ifdef CONFIG_32BIT csr_write(CSR_HENVCFGH, henvcfg >> 32); @@ -828,6 +832,8 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) vcpu->arch.isa); kvm_riscv_vcpu_host_fp_restore(&vcpu->arch.host_context); + kvm_riscv_vcpu_timer_save(vcpu); + csr->vsstatus = csr_read(CSR_VSSTATUS); csr->vsie = csr_read(CSR_VSIE); csr->vstvec = csr_read(CSR_VSTVEC); diff --git a/arch/riscv/kvm/vcpu_timer.c b/arch/riscv/kvm/vcpu_timer.c index 595043857049..16f50c46ba39 100644 --- a/arch/riscv/kvm/vcpu_timer.c +++ b/arch/riscv/kvm/vcpu_timer.c @@ -69,7 +69,18 @@ static int kvm_riscv_vcpu_timer_cancel(struct kvm_vcpu_timer *t) return 0; } -int kvm_riscv_vcpu_timer_next_event(struct kvm_vcpu *vcpu, u64 ncycles) +static int kvm_riscv_vcpu_update_vstimecmp(struct kvm_vcpu *vcpu, u64 ncycles) +{ +#if defined(CONFIG_32BIT) + csr_write(CSR_VSTIMECMP, ncycles & 0xFFFFFFFF); + csr_write(CSR_VSTIMECMPH, ncycles >> 32); +#else + csr_write(CSR_VSTIMECMP, ncycles); +#endif + return 0; +} + +static int kvm_riscv_vcpu_update_hrtimer(struct kvm_vcpu *vcpu, u64 ncycles) { struct kvm_vcpu_timer *t = &vcpu->arch.timer; struct kvm_guest_timer *gt = &vcpu->kvm->arch.timer; @@ -88,6 +99,65 @@ int kvm_riscv_vcpu_timer_next_event(struct kvm_vcpu *vcpu, u64 ncycles) return 0; } +int kvm_riscv_vcpu_timer_next_event(struct kvm_vcpu *vcpu, u64 ncycles) +{ + struct kvm_vcpu_timer *t = &vcpu->arch.timer; + + return t->timer_next_event(vcpu, ncycles); +} + +static enum hrtimer_restart kvm_riscv_vcpu_vstimer_expired(struct hrtimer *h) +{ + u64 delta_ns; + struct kvm_vcpu_timer *t = container_of(h, struct kvm_vcpu_timer, hrt); + struct kvm_vcpu *vcpu = container_of(t, struct kvm_vcpu, arch.timer); + struct kvm_guest_timer *gt = &vcpu->kvm->arch.timer; + + if (kvm_riscv_current_cycles(gt) < t->next_cycles) { + delta_ns = kvm_riscv_delta_cycles2ns(t->next_cycles, gt, t); + hrtimer_forward_now(&t->hrt, ktime_set(0, delta_ns)); + return HRTIMER_RESTART; + } + + t->next_set = false; + kvm_vcpu_kick(vcpu); + + return HRTIMER_NORESTART; +} + +bool kvm_riscv_vcpu_timer_pending(struct kvm_vcpu *vcpu) +{ + struct kvm_vcpu_timer *t = &vcpu->arch.timer; + struct kvm_guest_timer *gt = &vcpu->kvm->arch.timer; + + if (!kvm_riscv_delta_cycles2ns(t->next_cycles, gt, t) || + kvm_riscv_vcpu_has_interrupts(vcpu, 1UL << IRQ_VS_TIMER)) + return true; + else + return false; +} + +static void kvm_riscv_vcpu_timer_blocking(struct kvm_vcpu *vcpu) +{ + struct kvm_vcpu_timer *t = &vcpu->arch.timer; + struct kvm_guest_timer *gt = &vcpu->kvm->arch.timer; + u64 delta_ns; + + if (!t->init_done) + return; + + delta_ns = kvm_riscv_delta_cycles2ns(t->next_cycles, gt, t); + if (delta_ns) { + hrtimer_start(&t->hrt, ktime_set(0, delta_ns), HRTIMER_MODE_REL); + t->next_set = true; + } +} + +static void kvm_riscv_vcpu_timer_unblocking(struct kvm_vcpu *vcpu) +{ + kvm_riscv_vcpu_timer_cancel(&vcpu->arch.timer); +} + int kvm_riscv_vcpu_get_reg_timer(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) { @@ -180,10 +250,20 @@ int kvm_riscv_vcpu_timer_init(struct kvm_vcpu *vcpu) return -EINVAL; hrtimer_init(&t->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - t->hrt.function = kvm_riscv_vcpu_hrtimer_expired; t->init_done = true; t->next_set = false; + /* Enable sstc for every vcpu if available in hardware */ + if (riscv_isa_extension_available(NULL, SSTC)) { + t->sstc_enabled = true; + t->hrt.function = kvm_riscv_vcpu_vstimer_expired; + t->timer_next_event = kvm_riscv_vcpu_update_vstimecmp; + } else { + t->sstc_enabled = false; + t->hrt.function = kvm_riscv_vcpu_hrtimer_expired; + t->timer_next_event = kvm_riscv_vcpu_update_hrtimer; + } + return 0; } @@ -199,21 +279,73 @@ int kvm_riscv_vcpu_timer_deinit(struct kvm_vcpu *vcpu) int kvm_riscv_vcpu_timer_reset(struct kvm_vcpu *vcpu) { + struct kvm_vcpu_timer *t = &vcpu->arch.timer; + + t->next_cycles = -1ULL; return kvm_riscv_vcpu_timer_cancel(&vcpu->arch.timer); } -void kvm_riscv_vcpu_timer_restore(struct kvm_vcpu *vcpu) +static void kvm_riscv_vcpu_update_timedelta(struct kvm_vcpu *vcpu) { struct kvm_guest_timer *gt = &vcpu->kvm->arch.timer; -#ifdef CONFIG_64BIT - csr_write(CSR_HTIMEDELTA, gt->time_delta); -#else +#if defined(CONFIG_32BIT) csr_write(CSR_HTIMEDELTA, (u32)(gt->time_delta)); csr_write(CSR_HTIMEDELTAH, (u32)(gt->time_delta >> 32)); +#else + csr_write(CSR_HTIMEDELTA, gt->time_delta); #endif } +void kvm_riscv_vcpu_timer_restore(struct kvm_vcpu *vcpu) +{ + struct kvm_vcpu_csr *csr; + struct kvm_vcpu_timer *t = &vcpu->arch.timer; + + kvm_riscv_vcpu_update_timedelta(vcpu); + + if (!t->sstc_enabled) + return; + + csr = &vcpu->arch.guest_csr; +#if defined(CONFIG_32BIT) + csr_write(CSR_VSTIMECMP, (u32)t->next_cycles); + csr_write(CSR_VSTIMECMPH, (u32)(t->next_cycles >> 32)); +#else + csr_write(CSR_VSTIMECMP, t->next_cycles); +#endif + + /* timer should be enabled for the remaining operations */ + if (unlikely(!t->init_done)) + return; + + kvm_riscv_vcpu_timer_unblocking(vcpu); +} + +void kvm_riscv_vcpu_timer_save(struct kvm_vcpu *vcpu) +{ + struct kvm_vcpu_csr *csr; + struct kvm_vcpu_timer *t = &vcpu->arch.timer; + + if (!t->sstc_enabled) + return; + + csr = &vcpu->arch.guest_csr; + t = &vcpu->arch.timer; +#if defined(CONFIG_32BIT) + t->next_cycles = csr_read(CSR_VSTIMECMP); + t->next_cycles |= (u64)csr_read(CSR_VSTIMECMPH) << 32; +#else + t->next_cycles = csr_read(CSR_VSTIMECMP); +#endif + /* timer should be enabled for the remaining operations */ + if (unlikely(!t->init_done)) + return; + + if (kvm_vcpu_is_blocking(vcpu)) + kvm_riscv_vcpu_timer_blocking(vcpu); +} + void kvm_riscv_guest_timer_init(struct kvm *kvm) { struct kvm_guest_timer *gt = &kvm->arch.timer; diff --git a/arch/riscv/lib/uaccess.S b/arch/riscv/lib/uaccess.S index 8c475f4da308..ec486e5369d9 100644 --- a/arch/riscv/lib/uaccess.S +++ b/arch/riscv/lib/uaccess.S @@ -175,7 +175,7 @@ ENTRY(__asm_copy_from_user) /* Exception fixup code */ 10: /* Disable access to user memory */ - csrs CSR_STATUS, t6 + csrc CSR_STATUS, t6 mv a0, t5 ret ENDPROC(__asm_copy_to_user) @@ -227,7 +227,7 @@ ENTRY(__clear_user) /* Exception fixup code */ 11: /* Disable access to user memory */ - csrs CSR_STATUS, t6 + csrc CSR_STATUS, t6 mv a0, a1 ret ENDPROC(__clear_user) diff --git a/arch/riscv/mm/Makefile b/arch/riscv/mm/Makefile index ac7a25298a04..d76aabf4b94d 100644 --- a/arch/riscv/mm/Makefile +++ b/arch/riscv/mm/Makefile @@ -30,3 +30,4 @@ endif endif obj-$(CONFIG_DEBUG_VIRTUAL) += physaddr.o +obj-$(CONFIG_RISCV_DMA_NONCOHERENT) += dma-noncoherent.o diff --git a/arch/riscv/mm/dma-noncoherent.c b/arch/riscv/mm/dma-noncoherent.c new file mode 100644 index 000000000000..cd2225304c82 --- /dev/null +++ b/arch/riscv/mm/dma-noncoherent.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * RISC-V specific functions to support DMA for non-coherent devices + * + * Copyright (c) 2021 Western Digital Corporation or its affiliates. + */ + +#include <linux/dma-direct.h> +#include <linux/dma-map-ops.h> +#include <linux/mm.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <asm/cacheflush.h> + +static unsigned int riscv_cbom_block_size = L1_CACHE_BYTES; +static bool noncoherent_supported; + +void arch_sync_dma_for_device(phys_addr_t paddr, size_t size, + enum dma_data_direction dir) +{ + void *vaddr = phys_to_virt(paddr); + + switch (dir) { + case DMA_TO_DEVICE: + ALT_CMO_OP(clean, vaddr, size, riscv_cbom_block_size); + break; + case DMA_FROM_DEVICE: + ALT_CMO_OP(clean, vaddr, size, riscv_cbom_block_size); + break; + case DMA_BIDIRECTIONAL: + ALT_CMO_OP(flush, vaddr, size, riscv_cbom_block_size); + break; + default: + break; + } +} + +void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size, + enum dma_data_direction dir) +{ + void *vaddr = phys_to_virt(paddr); + + switch (dir) { + case DMA_TO_DEVICE: + break; + case DMA_FROM_DEVICE: + case DMA_BIDIRECTIONAL: + ALT_CMO_OP(flush, vaddr, size, riscv_cbom_block_size); + break; + default: + break; + } +} + +void arch_dma_prep_coherent(struct page *page, size_t size) +{ + void *flush_addr = page_address(page); + + ALT_CMO_OP(flush, flush_addr, size, riscv_cbom_block_size); +} + +void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size, + const struct iommu_ops *iommu, bool coherent) +{ + WARN_TAINT(!coherent && riscv_cbom_block_size > ARCH_DMA_MINALIGN, + TAINT_CPU_OUT_OF_SPEC, + "%s %s: ARCH_DMA_MINALIGN smaller than riscv,cbom-block-size (%d < %d)", + dev_driver_string(dev), dev_name(dev), + ARCH_DMA_MINALIGN, riscv_cbom_block_size); + + WARN_TAINT(!coherent && !noncoherent_supported, TAINT_CPU_OUT_OF_SPEC, + "%s %s: device non-coherent but no non-coherent operations supported", + dev_driver_string(dev), dev_name(dev)); + + dev->dma_coherent = coherent; +} + +#ifdef CONFIG_RISCV_ISA_ZICBOM +void riscv_init_cbom_blocksize(void) +{ + struct device_node *node; + int ret; + u32 val; + + for_each_of_cpu_node(node) { + unsigned long hartid; + int cbom_hartid; + + ret = riscv_of_processor_hartid(node, &hartid); + if (ret) + continue; + + if (hartid < 0) + continue; + + /* set block-size for cbom extension if available */ + ret = of_property_read_u32(node, "riscv,cbom-block-size", &val); + if (ret) + continue; + + if (!riscv_cbom_block_size) { + riscv_cbom_block_size = val; + cbom_hartid = hartid; + } else { + if (riscv_cbom_block_size != val) + pr_warn("cbom-block-size mismatched between harts %d and %lu\n", + cbom_hartid, hartid); + } + } +} +#endif + +void riscv_noncoherent_supported(void) +{ + noncoherent_supported = true; +} diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index a88b7dc31a68..b56a0a75533f 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -135,6 +135,10 @@ static void __init print_vm_layout(void) (unsigned long)VMEMMAP_END); print_ml("vmalloc", (unsigned long)VMALLOC_START, (unsigned long)VMALLOC_END); +#ifdef CONFIG_64BIT + print_ml("modules", (unsigned long)MODULES_VADDR, + (unsigned long)MODULES_END); +#endif print_ml("lowmem", (unsigned long)PAGE_OFFSET, (unsigned long)high_memory); if (IS_ENABLED(CONFIG_64BIT)) { diff --git a/arch/riscv/purgatory/.gitignore b/arch/riscv/purgatory/.gitignore index 38d7d1bda4d7..6e4dfb024ad2 100644 --- a/arch/riscv/purgatory/.gitignore +++ b/arch/riscv/purgatory/.gitignore @@ -1,4 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only purgatory.chk purgatory.ro -kexec-purgatory.c diff --git a/arch/riscv/purgatory/Makefile b/arch/riscv/purgatory/Makefile index d4df200f7edf..dd58e1d99397 100644 --- a/arch/riscv/purgatory/Makefile +++ b/arch/riscv/purgatory/Makefile @@ -84,12 +84,6 @@ $(obj)/purgatory.ro: $(PURGATORY_OBJS) FORCE $(obj)/purgatory.chk: $(obj)/purgatory.ro FORCE $(call if_changed,ld) -targets += kexec-purgatory.c +$(obj)/kexec-purgatory.o: $(obj)/purgatory.ro $(obj)/purgatory.chk -quiet_cmd_bin2c = BIN2C $@ - cmd_bin2c = $(objtree)/scripts/bin2c kexec_purgatory < $< > $@ - -$(obj)/kexec-purgatory.c: $(obj)/purgatory.ro $(obj)/purgatory.chk FORCE - $(call if_changed,bin2c) - -obj-$(CONFIG_ARCH_HAS_KEXEC_PURGATORY) += kexec-purgatory.o +obj-y += kexec-purgatory.o diff --git a/arch/riscv/purgatory/kexec-purgatory.S b/arch/riscv/purgatory/kexec-purgatory.S new file mode 100644 index 000000000000..0e9188815718 --- /dev/null +++ b/arch/riscv/purgatory/kexec-purgatory.S @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + + .section .rodata, "a" + + .align 8 +kexec_purgatory: + .globl kexec_purgatory + .incbin "arch/riscv/purgatory/purgatory.ro" +.Lkexec_purgatroy_end: + + .align 8 +kexec_purgatory_size: + .globl kexec_purgatory_size + .quad .Lkexec_purgatroy_end - kexec_purgatory diff --git a/arch/um/drivers/virtio_uml.c b/arch/um/drivers/virtio_uml.c index 82ff3785bf69..79e38afd4b91 100644 --- a/arch/um/drivers/virtio_uml.c +++ b/arch/um/drivers/virtio_uml.c @@ -958,6 +958,7 @@ static struct virtqueue *vu_setup_vq(struct virtio_device *vdev, goto error_create; } vq->priv = info; + vq->num_max = num; num = virtqueue_get_vring_size(vq); if (vu_dev->protocol_features & @@ -1010,7 +1011,7 @@ error_kzalloc: static int vu_find_vqs(struct virtio_device *vdev, unsigned nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], - const char * const names[], const bool *ctx, + const char * const names[], u32 sizes[], const bool *ctx, struct irq_affinity *desc) { struct virtio_uml_device *vu_dev = to_virtio_uml_device(vdev); diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index e8281d64a431..5ffa578cafe1 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1704,7 +1704,7 @@ static inline int kvm_arch_flush_remote_tlb(struct kvm *kvm) #define kvm_arch_pmi_in_guest(vcpu) \ ((vcpu) && (vcpu)->arch.handling_intr_from_guest) -void kvm_mmu_x86_module_init(void); +void __init kvm_mmu_x86_module_init(void); int kvm_mmu_vendor_module_init(void); void kvm_mmu_vendor_module_exit(void); diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 047c583596bb..b4eeb7c75dfa 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -4578,6 +4578,10 @@ static const struct mode_dual mode_dual_63 = { N, I(DstReg | SrcMem32 | ModRM | Mov, em_movsxd) }; +static const struct instr_dual instr_dual_8d = { + D(DstReg | SrcMem | ModRM | NoAccess), N +}; + static const struct opcode opcode_table[256] = { /* 0x00 - 0x07 */ F6ALU(Lock, em_add), @@ -4634,7 +4638,7 @@ static const struct opcode opcode_table[256] = { I2bv(DstMem | SrcReg | ModRM | Mov | PageTable, em_mov), I2bv(DstReg | SrcMem | ModRM | Mov, em_mov), I(DstMem | SrcNone | ModRM | Mov | PageTable, em_mov_rm_sreg), - D(ModRM | SrcMem | NoAccess | DstReg), + ID(0, &instr_dual_8d), I(ImplicitOps | SrcMem16 | ModRM, em_mov_sreg_rm), G(0, group1A), /* 0x90 - 0x97 */ diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index e2ce3556915e..9dda989a1cf0 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -2284,10 +2284,12 @@ void kvm_apic_write_nodecode(struct kvm_vcpu *vcpu, u32 offset) struct kvm_lapic *apic = vcpu->arch.apic; u64 val; - if (apic_x2apic_mode(apic)) - kvm_lapic_msr_read(apic, offset, &val); - else + if (apic_x2apic_mode(apic)) { + if (KVM_BUG_ON(kvm_lapic_msr_read(apic, offset, &val), vcpu->kvm)) + return; + } else { val = kvm_lapic_get_reg(apic, offset); + } /* * ICR is a single 64-bit register when x2APIC is enabled. For legacy diff --git a/arch/x86/kvm/mmu.h b/arch/x86/kvm/mmu.h index a99acec925eb..6bdaacb6faa0 100644 --- a/arch/x86/kvm/mmu.h +++ b/arch/x86/kvm/mmu.h @@ -6,6 +6,8 @@ #include "kvm_cache_regs.h" #include "cpuid.h" +extern bool __read_mostly enable_mmio_caching; + #define PT_WRITABLE_SHIFT 1 #define PT_USER_SHIFT 2 diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 06ac8c7cef67..eccddb136954 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -4164,7 +4164,7 @@ static int kvm_faultin_pfn(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault) if (!fault->prefetch && kvm_can_do_async_pf(vcpu)) { trace_kvm_try_async_get_page(fault->addr, fault->gfn); if (kvm_find_async_pf_gfn(vcpu, fault->gfn)) { - trace_kvm_async_pf_doublefault(fault->addr, fault->gfn); + trace_kvm_async_pf_repeated_fault(fault->addr, fault->gfn); kvm_make_request(KVM_REQ_APF_HALT, vcpu); return RET_PF_RETRY; } else if (kvm_arch_setup_async_pf(vcpu, fault->addr, fault->gfn)) { @@ -6697,11 +6697,15 @@ static int set_nx_huge_pages(const char *val, const struct kernel_param *kp) /* * nx_huge_pages needs to be resolved to true/false when kvm.ko is loaded, as * its default value of -1 is technically undefined behavior for a boolean. + * Forward the module init call to SPTE code so that it too can handle module + * params that need to be resolved/snapshot. */ -void kvm_mmu_x86_module_init(void) +void __init kvm_mmu_x86_module_init(void) { if (nx_huge_pages == -1) __set_nx_huge_pages(get_nx_auto_mode()); + + kvm_mmu_spte_module_init(); } /* diff --git a/arch/x86/kvm/mmu/spte.c b/arch/x86/kvm/mmu/spte.c index 7314d27d57a4..2e08b2a45361 100644 --- a/arch/x86/kvm/mmu/spte.c +++ b/arch/x86/kvm/mmu/spte.c @@ -20,7 +20,9 @@ #include <asm/vmx.h> bool __read_mostly enable_mmio_caching = true; +static bool __ro_after_init allow_mmio_caching; module_param_named(mmio_caching, enable_mmio_caching, bool, 0444); +EXPORT_SYMBOL_GPL(enable_mmio_caching); u64 __read_mostly shadow_host_writable_mask; u64 __read_mostly shadow_mmu_writable_mask; @@ -43,6 +45,18 @@ u64 __read_mostly shadow_nonpresent_or_rsvd_lower_gfn_mask; u8 __read_mostly shadow_phys_bits; +void __init kvm_mmu_spte_module_init(void) +{ + /* + * Snapshot userspace's desire to allow MMIO caching. Whether or not + * KVM can actually enable MMIO caching depends on vendor-specific + * hardware capabilities and other module params that can't be resolved + * until the vendor module is loaded, i.e. enable_mmio_caching can and + * will change when the vendor module is (re)loaded. + */ + allow_mmio_caching = enable_mmio_caching; +} + static u64 generation_mmio_spte_mask(u64 gen) { u64 mask; @@ -340,10 +354,24 @@ void kvm_mmu_set_mmio_spte_mask(u64 mmio_value, u64 mmio_mask, u64 access_mask) BUG_ON((u64)(unsigned)access_mask != access_mask); WARN_ON(mmio_value & shadow_nonpresent_or_rsvd_lower_gfn_mask); + /* + * Reset to the original module param value to honor userspace's desire + * to (dis)allow MMIO caching. Update the param itself so that + * userspace can see whether or not KVM is actually using MMIO caching. + */ + enable_mmio_caching = allow_mmio_caching; if (!enable_mmio_caching) mmio_value = 0; /* + * The mask must contain only bits that are carved out specifically for + * the MMIO SPTE mask, e.g. to ensure there's no overlap with the MMIO + * generation. + */ + if (WARN_ON(mmio_mask & ~SPTE_MMIO_ALLOWED_MASK)) + mmio_value = 0; + + /* * Disable MMIO caching if the MMIO value collides with the bits that * are used to hold the relocated GFN when the L1TF mitigation is * enabled. This should never fire as there is no known hardware that diff --git a/arch/x86/kvm/mmu/spte.h b/arch/x86/kvm/mmu/spte.h index cabe3fbb4f39..f3744eea45f5 100644 --- a/arch/x86/kvm/mmu/spte.h +++ b/arch/x86/kvm/mmu/spte.h @@ -5,8 +5,6 @@ #include "mmu_internal.h" -extern bool __read_mostly enable_mmio_caching; - /* * A MMU present SPTE is backed by actual memory and may or may not be present * in hardware. E.g. MMIO SPTEs are not considered present. Use bit 11, as it @@ -125,6 +123,20 @@ static_assert(!(EPT_SPTE_MMU_WRITABLE & SHADOW_ACC_TRACK_SAVED_MASK)); static_assert(!(SPTE_MMU_PRESENT_MASK & (MMIO_SPTE_GEN_LOW_MASK | MMIO_SPTE_GEN_HIGH_MASK))); +/* + * The SPTE MMIO mask must NOT overlap the MMIO generation bits or the + * MMU-present bit. The generation obviously co-exists with the magic MMIO + * mask/value, and MMIO SPTEs are considered !MMU-present. + * + * The SPTE MMIO mask is allowed to use hardware "present" bits (i.e. all EPT + * RWX bits), all physical address bits (legal PA bits are used for "fast" MMIO + * and so they're off-limits for generation; additional checks ensure the mask + * doesn't overlap legal PA bits), and bit 63 (carved out for future usage). + */ +#define SPTE_MMIO_ALLOWED_MASK (BIT_ULL(63) | GENMASK_ULL(51, 12) | GENMASK_ULL(2, 0)) +static_assert(!(SPTE_MMIO_ALLOWED_MASK & + (SPTE_MMU_PRESENT_MASK | MMIO_SPTE_GEN_LOW_MASK | MMIO_SPTE_GEN_HIGH_MASK))); + #define MMIO_SPTE_GEN_LOW_BITS (MMIO_SPTE_GEN_LOW_END - MMIO_SPTE_GEN_LOW_START + 1) #define MMIO_SPTE_GEN_HIGH_BITS (MMIO_SPTE_GEN_HIGH_END - MMIO_SPTE_GEN_HIGH_START + 1) @@ -450,6 +462,7 @@ static inline u64 restore_acc_track_spte(u64 spte) u64 kvm_mmu_changed_pte_notifier_make_spte(u64 old_spte, kvm_pfn_t new_pfn); +void __init kvm_mmu_spte_module_init(void); void kvm_mmu_reset_all_pte_masks(void); #endif diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index b0e793e7d85c..28064060413a 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -22,6 +22,7 @@ #include <asm/trapnr.h> #include <asm/fpu/xcr.h> +#include "mmu.h" #include "x86.h" #include "svm.h" #include "svm_ops.h" @@ -2221,6 +2222,15 @@ void __init sev_hardware_setup(void) if (!sev_es_enabled) goto out; + /* + * SEV-ES requires MMIO caching as KVM doesn't have access to the guest + * instruction stream, i.e. can't emulate in response to a #NPF and + * instead relies on #NPF(RSVD) being reflected into the guest as #VC + * (the guest can then do a #VMGEXIT to request MMIO emulation). + */ + if (!enable_mmio_caching) + goto out; + /* Does the CPU support SEV-ES? */ if (!boot_cpu_has(X86_FEATURE_SEV_ES)) goto out; diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 38f873cb6f2c..f3813dbacb9f 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -5034,13 +5034,16 @@ static __init int svm_hardware_setup(void) /* Setup shadow_me_value and shadow_me_mask */ kvm_mmu_set_me_spte_mask(sme_me_mask, sme_me_mask); - /* Note, SEV setup consumes npt_enabled. */ + svm_adjust_mmio_mask(); + + /* + * Note, SEV setup consumes npt_enabled and enable_mmio_caching (which + * may be modified by svm_adjust_mmio_mask()). + */ sev_hardware_setup(); svm_hv_hardware_setup(); - svm_adjust_mmio_mask(); - for_each_possible_cpu(cpu) { r = svm_cpu_init(cpu); if (r) diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c index 862c1a4d971b..c399637a3a79 100644 --- a/arch/x86/kvm/vmx/pmu_intel.c +++ b/arch/x86/kvm/vmx/pmu_intel.c @@ -171,13 +171,6 @@ static inline struct kvm_pmc *get_fw_gp_pmc(struct kvm_pmu *pmu, u32 msr) return get_gp_pmc(pmu, msr, MSR_IA32_PMC0); } -bool intel_pmu_lbr_is_enabled(struct kvm_vcpu *vcpu) -{ - struct x86_pmu_lbr *lbr = vcpu_to_lbr_records(vcpu); - - return lbr->nr && (vcpu_get_perf_capabilities(vcpu) & PMU_CAP_LBR_FMT); -} - static bool intel_pmu_is_valid_lbr_msr(struct kvm_vcpu *vcpu, u32 index) { struct x86_pmu_lbr *records = vcpu_to_lbr_records(vcpu); @@ -592,7 +585,9 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu) bitmap_set(pmu->all_valid_pmc_idx, INTEL_PMC_MAX_GENERIC, pmu->nr_arch_fixed_counters); - if (cpuid_model_is_consistent(vcpu)) + perf_capabilities = vcpu_get_perf_capabilities(vcpu); + if (cpuid_model_is_consistent(vcpu) && + (perf_capabilities & PMU_CAP_LBR_FMT)) x86_perf_get_lbr(&lbr_desc->records); else lbr_desc->records.nr = 0; @@ -600,7 +595,6 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu) if (lbr_desc->records.nr) bitmap_set(pmu->all_valid_pmc_idx, INTEL_PMC_IDX_FIXED_VLBR, 1); - perf_capabilities = vcpu_get_perf_capabilities(vcpu); if (perf_capabilities & PERF_CAP_PEBS_FORMAT) { if (perf_capabilities & PERF_CAP_PEBS_BASELINE) { pmu->pebs_enable_mask = counter_mask; diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h index fb8e3480a9d7..24d58c2ffaa3 100644 --- a/arch/x86/kvm/vmx/vmx.h +++ b/arch/x86/kvm/vmx/vmx.h @@ -6,6 +6,7 @@ #include <asm/kvm.h> #include <asm/intel_pt.h> +#include <asm/perf_event.h> #include "capabilities.h" #include "../kvm_cache_regs.h" @@ -104,15 +105,6 @@ static inline bool intel_pmu_has_perf_global_ctrl(struct kvm_pmu *pmu) return pmu->version > 1; } -#define vcpu_to_lbr_desc(vcpu) (&to_vmx(vcpu)->lbr_desc) -#define vcpu_to_lbr_records(vcpu) (&to_vmx(vcpu)->lbr_desc.records) - -void intel_pmu_cross_mapped_check(struct kvm_pmu *pmu); -bool intel_pmu_lbr_is_enabled(struct kvm_vcpu *vcpu); - -int intel_pmu_create_guest_lbr_event(struct kvm_vcpu *vcpu); -void vmx_passthrough_lbr_msrs(struct kvm_vcpu *vcpu); - struct lbr_desc { /* Basic info about guest LBR records. */ struct x86_pmu_lbr records; @@ -542,6 +534,25 @@ static inline struct vcpu_vmx *to_vmx(struct kvm_vcpu *vcpu) return container_of(vcpu, struct vcpu_vmx, vcpu); } +static inline struct lbr_desc *vcpu_to_lbr_desc(struct kvm_vcpu *vcpu) +{ + return &to_vmx(vcpu)->lbr_desc; +} + +static inline struct x86_pmu_lbr *vcpu_to_lbr_records(struct kvm_vcpu *vcpu) +{ + return &vcpu_to_lbr_desc(vcpu)->records; +} + +static inline bool intel_pmu_lbr_is_enabled(struct kvm_vcpu *vcpu) +{ + return !!vcpu_to_lbr_records(vcpu)->nr; +} + +void intel_pmu_cross_mapped_check(struct kvm_pmu *pmu); +int intel_pmu_create_guest_lbr_event(struct kvm_vcpu *vcpu); +void vmx_passthrough_lbr_msrs(struct kvm_vcpu *vcpu); + static inline unsigned long vmx_get_exit_qual(struct kvm_vcpu *vcpu) { struct vcpu_vmx *vmx = to_vmx(vcpu); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 79a8a74b6b2a..205ebdc2b11b 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -3413,6 +3413,7 @@ static void record_steal_time(struct kvm_vcpu *vcpu) struct gfn_to_hva_cache *ghc = &vcpu->arch.st.cache; struct kvm_steal_time __user *st; struct kvm_memslots *slots; + gpa_t gpa = vcpu->arch.st.msr_val & KVM_STEAL_VALID_BITS; u64 steal; u32 version; @@ -3430,13 +3431,12 @@ static void record_steal_time(struct kvm_vcpu *vcpu) slots = kvm_memslots(vcpu->kvm); if (unlikely(slots->generation != ghc->generation || + gpa != ghc->gpa || kvm_is_error_hva(ghc->hva) || !ghc->memslot)) { - gfn_t gfn = vcpu->arch.st.msr_val & KVM_STEAL_VALID_BITS; - /* We rely on the fact that it fits in a single page. */ BUILD_BUG_ON((sizeof(*st) - 1) & KVM_STEAL_VALID_BITS); - if (kvm_gfn_to_hva_cache_init(vcpu->kvm, ghc, gfn, sizeof(*st)) || + if (kvm_gfn_to_hva_cache_init(vcpu->kvm, ghc, gpa, sizeof(*st)) || kvm_is_error_hva(ghc->hva) || !ghc->memslot) return; } @@ -3545,9 +3545,9 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) return 1; vcpu->arch.perf_capabilities = data; - + kvm_pmu_refresh(vcpu); return 0; - } + } case MSR_EFER: return set_efer(vcpu, msr_info); case MSR_K7_HWCR: @@ -4714,6 +4714,7 @@ static void kvm_steal_time_set_preempted(struct kvm_vcpu *vcpu) struct kvm_steal_time __user *st; struct kvm_memslots *slots; static const u8 preempted = KVM_VCPU_PREEMPTED; + gpa_t gpa = vcpu->arch.st.msr_val & KVM_STEAL_VALID_BITS; /* * The vCPU can be marked preempted if and only if the VM-Exit was on @@ -4741,6 +4742,7 @@ static void kvm_steal_time_set_preempted(struct kvm_vcpu *vcpu) slots = kvm_memslots(vcpu->kvm); if (unlikely(slots->generation != ghc->generation || + gpa != ghc->gpa || kvm_is_error_hva(ghc->hva) || !ghc->memslot)) return; @@ -13019,6 +13021,7 @@ void kvm_fixup_and_inject_pf_error(struct kvm_vcpu *vcpu, gva_t gva, u16 error_c fault.error_code = error_code; fault.nested_page_fault = false; fault.address = gva; + fault.async_page_fault = false; } vcpu->arch.walk_mmu->inject_page_fault(vcpu, &fault); } diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c index a0c05ccbf4b1..280cb5dc7341 100644 --- a/arch/x86/kvm/xen.c +++ b/arch/x86/kvm/xen.c @@ -707,23 +707,24 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data) break; case KVM_XEN_VCPU_ATTR_TYPE_TIMER: - if (data->u.timer.port) { - if (data->u.timer.priority != KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL) { - r = -EINVAL; - break; - } - vcpu->arch.xen.timer_virq = data->u.timer.port; + if (data->u.timer.port && + data->u.timer.priority != KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL) { + r = -EINVAL; + break; + } + + if (!vcpu->arch.xen.timer.function) kvm_xen_init_timer(vcpu); - /* Restart the timer if it's set */ - if (data->u.timer.expires_ns) - kvm_xen_start_timer(vcpu, data->u.timer.expires_ns, - data->u.timer.expires_ns - - get_kvmclock_ns(vcpu->kvm)); - } else if (kvm_xen_timer_enabled(vcpu)) { - kvm_xen_stop_timer(vcpu); - vcpu->arch.xen.timer_virq = 0; - } + /* Stop the timer (if it's running) before changing the vector */ + kvm_xen_stop_timer(vcpu); + vcpu->arch.xen.timer_virq = data->u.timer.port; + + /* Start the timer if the new value has a valid vector+expiry. */ + if (data->u.timer.port && data->u.timer.expires_ns) + kvm_xen_start_timer(vcpu, data->u.timer.expires_ns, + data->u.timer.expires_ns - + get_kvmclock_ns(vcpu->kvm)); r = 0; break; diff --git a/block/genhd.c b/block/genhd.c index b901fea1d55a..d36fabf0abc1 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -1341,7 +1341,7 @@ struct gendisk *__alloc_disk_node(struct request_queue *q, int node_id, disk = kzalloc_node(sizeof(struct gendisk), GFP_KERNEL, node_id); if (!disk) - goto out_put_queue; + return NULL; if (bioset_init(&disk->bio_split, BIO_POOL_SIZE, 0, 0)) goto out_free_disk; @@ -1390,8 +1390,6 @@ out_free_bioset: bioset_exit(&disk->bio_split); out_free_disk: kfree(disk); -out_put_queue: - blk_put_queue(q); return NULL; } diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index e764f9ac9cf8..7b3ad8ed2f4e 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -55,14 +55,19 @@ static const guid_t ads_guid = GUID_INIT(0xdbb8e3e6, 0x5886, 0x4ba6, 0x87, 0x95, 0x13, 0x19, 0xf5, 0x2a, 0x96, 0x6b); +static const guid_t buffer_prop_guid = + GUID_INIT(0xedb12dd0, 0x363d, 0x4085, + 0xa3, 0xd2, 0x49, 0x52, 0x2c, 0xa1, 0x60, 0xc4); + static bool acpi_enumerate_nondev_subnodes(acpi_handle scope, - const union acpi_object *desc, + union acpi_object *desc, struct acpi_device_data *data, struct fwnode_handle *parent); -static bool acpi_extract_properties(const union acpi_object *desc, +static bool acpi_extract_properties(acpi_handle handle, + union acpi_object *desc, struct acpi_device_data *data); -static bool acpi_nondev_subnode_extract(const union acpi_object *desc, +static bool acpi_nondev_subnode_extract(union acpi_object *desc, acpi_handle handle, const union acpi_object *link, struct list_head *list, @@ -81,7 +86,7 @@ static bool acpi_nondev_subnode_extract(const union acpi_object *desc, INIT_LIST_HEAD(&dn->data.properties); INIT_LIST_HEAD(&dn->data.subnodes); - result = acpi_extract_properties(desc, &dn->data); + result = acpi_extract_properties(handle, desc, &dn->data); if (handle) { acpi_handle scope; @@ -155,16 +160,16 @@ static bool acpi_nondev_subnode_ok(acpi_handle scope, return acpi_nondev_subnode_data_ok(handle, link, list, parent); } -static int acpi_add_nondev_subnodes(acpi_handle scope, - const union acpi_object *links, - struct list_head *list, - struct fwnode_handle *parent) +static bool acpi_add_nondev_subnodes(acpi_handle scope, + union acpi_object *links, + struct list_head *list, + struct fwnode_handle *parent) { bool ret = false; int i; for (i = 0; i < links->package.count; i++) { - const union acpi_object *link, *desc; + union acpi_object *link, *desc; acpi_handle handle; bool result; @@ -204,7 +209,7 @@ static int acpi_add_nondev_subnodes(acpi_handle scope, } static bool acpi_enumerate_nondev_subnodes(acpi_handle scope, - const union acpi_object *desc, + union acpi_object *desc, struct acpi_device_data *data, struct fwnode_handle *parent) { @@ -212,7 +217,8 @@ static bool acpi_enumerate_nondev_subnodes(acpi_handle scope, /* Look for the ACPI data subnodes GUID. */ for (i = 0; i < desc->package.count; i += 2) { - const union acpi_object *guid, *links; + const union acpi_object *guid; + union acpi_object *links; guid = &desc->package.elements[i]; links = &desc->package.elements[i + 1]; @@ -325,7 +331,7 @@ static bool acpi_is_property_guid(const guid_t *guid) struct acpi_device_properties * acpi_data_add_props(struct acpi_device_data *data, const guid_t *guid, - const union acpi_object *properties) + union acpi_object *properties) { struct acpi_device_properties *props; @@ -340,7 +346,141 @@ acpi_data_add_props(struct acpi_device_data *data, const guid_t *guid, return props; } -static bool acpi_extract_properties(const union acpi_object *desc, +static void acpi_nondev_subnode_tag(acpi_handle handle, void *context) +{ +} + +static void acpi_untie_nondev_subnodes(struct acpi_device_data *data) +{ + struct acpi_data_node *dn; + + list_for_each_entry(dn, &data->subnodes, sibling) { + acpi_detach_data(dn->handle, acpi_nondev_subnode_tag); + + acpi_untie_nondev_subnodes(&dn->data); + } +} + +static bool acpi_tie_nondev_subnodes(struct acpi_device_data *data) +{ + struct acpi_data_node *dn; + + list_for_each_entry(dn, &data->subnodes, sibling) { + acpi_status status; + bool ret; + + status = acpi_attach_data(dn->handle, acpi_nondev_subnode_tag, dn); + if (ACPI_FAILURE(status)) { + acpi_handle_err(dn->handle, "Can't tag data node\n"); + return false; + } + + ret = acpi_tie_nondev_subnodes(&dn->data); + if (!ret) + return ret; + } + + return true; +} + +static void acpi_data_add_buffer_props(acpi_handle handle, + struct acpi_device_data *data, + union acpi_object *properties) +{ + struct acpi_device_properties *props; + union acpi_object *package; + size_t alloc_size; + unsigned int i; + u32 *count; + + if (check_mul_overflow((size_t)properties->package.count, + sizeof(*package) + sizeof(void *), + &alloc_size) || + check_add_overflow(sizeof(*props) + sizeof(*package), alloc_size, + &alloc_size)) { + acpi_handle_warn(handle, + "can't allocate memory for %u buffer props", + properties->package.count); + return; + } + + props = kvzalloc(alloc_size, GFP_KERNEL); + if (!props) + return; + + props->guid = &buffer_prop_guid; + props->bufs = (void *)(props + 1); + props->properties = (void *)(props->bufs + properties->package.count); + + /* Outer package */ + package = props->properties; + package->type = ACPI_TYPE_PACKAGE; + package->package.elements = package + 1; + count = &package->package.count; + *count = 0; + + /* Inner packages */ + package++; + + for (i = 0; i < properties->package.count; i++) { + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; + union acpi_object *property = &properties->package.elements[i]; + union acpi_object *prop, *obj, *buf_obj; + acpi_status status; + + if (property->type != ACPI_TYPE_PACKAGE || + property->package.count != 2) { + acpi_handle_warn(handle, + "buffer property %u has %u entries\n", + i, property->package.count); + continue; + } + + prop = &property->package.elements[0]; + obj = &property->package.elements[1]; + + if (prop->type != ACPI_TYPE_STRING || + obj->type != ACPI_TYPE_STRING) { + acpi_handle_warn(handle, + "wrong object types %u and %u\n", + prop->type, obj->type); + continue; + } + + status = acpi_evaluate_object_typed(handle, obj->string.pointer, + NULL, &buf, + ACPI_TYPE_BUFFER); + if (ACPI_FAILURE(status)) { + acpi_handle_warn(handle, + "can't evaluate \"%*pE\" as buffer\n", + obj->string.length, + obj->string.pointer); + continue; + } + + package->type = ACPI_TYPE_PACKAGE; + package->package.elements = prop; + package->package.count = 2; + + buf_obj = buf.pointer; + + /* Replace the string object with a buffer object */ + obj->type = ACPI_TYPE_BUFFER; + obj->buffer.length = buf_obj->buffer.length; + obj->buffer.pointer = buf_obj->buffer.pointer; + + props->bufs[i] = buf.pointer; + package++; + (*count)++; + } + + if (*count) + list_add(&props->list, &data->properties); + else + kvfree(props); +} + +static bool acpi_extract_properties(acpi_handle scope, union acpi_object *desc, struct acpi_device_data *data) { int i; @@ -350,7 +490,8 @@ static bool acpi_extract_properties(const union acpi_object *desc, /* Look for the device properties GUID. */ for (i = 0; i < desc->package.count; i += 2) { - const union acpi_object *guid, *properties; + const union acpi_object *guid; + union acpi_object *properties; guid = &desc->package.elements[i]; properties = &desc->package.elements[i + 1]; @@ -364,6 +505,12 @@ static bool acpi_extract_properties(const union acpi_object *desc, properties->type != ACPI_TYPE_PACKAGE) break; + if (guid_equal((guid_t *)guid->buffer.pointer, + &buffer_prop_guid)) { + acpi_data_add_buffer_props(scope, data, properties); + continue; + } + if (!acpi_is_property_guid((guid_t *)guid->buffer.pointer)) continue; @@ -410,7 +557,7 @@ void acpi_init_properties(struct acpi_device *adev) if (ACPI_FAILURE(status)) goto out; - if (acpi_extract_properties(buf.pointer, &adev->data)) { + if (acpi_extract_properties(adev->handle, buf.pointer, &adev->data)) { adev->data.pointer = buf.pointer; if (acpi_of) acpi_init_of_compatible(adev); @@ -422,6 +569,9 @@ void acpi_init_properties(struct acpi_device *adev) if (!adev->data.pointer) { acpi_handle_debug(adev->handle, "Invalid _DSD data, skipping\n"); ACPI_FREE(buf.pointer); + } else { + if (!acpi_tie_nondev_subnodes(&adev->data)) + acpi_untie_nondev_subnodes(&adev->data); } out: @@ -438,8 +588,14 @@ static void acpi_free_device_properties(struct list_head *list) struct acpi_device_properties *props, *tmp; list_for_each_entry_safe(props, tmp, list, list) { + u32 i; + list_del(&props->list); - kfree(props); + /* Buffer data properties were separately allocated */ + if (props->bufs) + for (i = 0; i < props->properties->package.count; i++) + ACPI_FREE(props->bufs[i]); + kvfree(props); } } @@ -462,6 +618,7 @@ static void acpi_destroy_nondev_subnodes(struct list_head *list) void acpi_free_properties(struct acpi_device *adev) { + acpi_untie_nondev_subnodes(&adev->data); acpi_destroy_nondev_subnodes(&adev->data.subnodes); ACPI_FREE((void *)adev->data.pointer); adev->data.of_compatible = NULL; @@ -633,6 +790,58 @@ acpi_fwnode_get_named_child_node(const struct fwnode_handle *fwnode, return NULL; } +static int acpi_get_ref_args(struct fwnode_reference_args *args, + struct fwnode_handle *ref_fwnode, + const union acpi_object **element, + const union acpi_object *end, size_t num_args) +{ + u32 nargs = 0, i; + + /* + * Find the referred data extension node under the + * referred device node. + */ + for (; *element < end && (*element)->type == ACPI_TYPE_STRING; + (*element)++) { + const char *child_name = (*element)->string.pointer; + + ref_fwnode = acpi_fwnode_get_named_child_node(ref_fwnode, child_name); + if (!ref_fwnode) + return -EINVAL; + } + + /* + * Assume the following integer elements are all args. Stop counting on + * the first reference or end of the package arguments. In case of + * neither reference, nor integer, return an error, we can't parse it. + */ + for (i = 0; (*element) + i < end && i < num_args; i++) { + acpi_object_type type = (*element)[i].type; + + if (type == ACPI_TYPE_LOCAL_REFERENCE) + break; + + if (type == ACPI_TYPE_INTEGER) + nargs++; + else + return -EINVAL; + } + + if (nargs > NR_FWNODE_REFERENCE_ARGS) + return -EINVAL; + + if (args) { + args->fwnode = ref_fwnode; + args->nargs = nargs; + for (i = 0; i < nargs; i++) + args->args[i] = (*element)[i].integer.value; + } + + (*element) += nargs; + + return 0; +} + /** * __acpi_node_get_property_reference - returns handle to the referenced object * @fwnode: Firmware node to get the property from @@ -686,11 +895,9 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode, if (ret) return ret == -EINVAL ? -ENOENT : -EINVAL; - /* - * The simplest case is when the value is a single reference. Just - * return that reference then. - */ - if (obj->type == ACPI_TYPE_LOCAL_REFERENCE) { + switch (obj->type) { + case ACPI_TYPE_LOCAL_REFERENCE: + /* Plain single reference without arguments. */ if (index) return -ENOENT; @@ -701,19 +908,21 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode, args->fwnode = acpi_fwnode_handle(device); args->nargs = 0; return 0; + case ACPI_TYPE_PACKAGE: + /* + * If it is not a single reference, then it is a package of + * references followed by number of ints as follows: + * + * Package () { REF, INT, REF, INT, INT } + * + * The index argument is then used to determine which reference + * the caller wants (along with the arguments). + */ + break; + default: + return -EINVAL; } - /* - * If it is not a single reference, then it is a package of - * references followed by number of ints as follows: - * - * Package () { REF, INT, REF, INT, INT } - * - * The index argument is then used to determine which reference - * the caller wants (along with the arguments). - */ - if (obj->type != ACPI_TYPE_PACKAGE) - return -EINVAL; if (index >= obj->package.count) return -ENOENT; @@ -721,66 +930,30 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode, end = element + obj->package.count; while (element < end) { - u32 nargs, i; - - if (element->type == ACPI_TYPE_LOCAL_REFERENCE) { - struct fwnode_handle *ref_fwnode; - + switch (element->type) { + case ACPI_TYPE_LOCAL_REFERENCE: device = acpi_fetch_acpi_dev(element->reference.handle); if (!device) return -EINVAL; - nargs = 0; element++; - /* - * Find the referred data extension node under the - * referred device node. - */ - for (ref_fwnode = acpi_fwnode_handle(device); - element < end && element->type == ACPI_TYPE_STRING; - element++) { - ref_fwnode = acpi_fwnode_get_named_child_node( - ref_fwnode, element->string.pointer); - if (!ref_fwnode) - return -EINVAL; - } - - /* - * Assume the following integer elements are all args. - * Stop counting on the first reference or end of the - * package arguments. In case of neither reference, - * nor integer, return an error, we can't parse it. - */ - for (i = 0; element + i < end && i < num_args; i++) { - int type = element[i].type; - - if (type == ACPI_TYPE_LOCAL_REFERENCE) - break; - if (type == ACPI_TYPE_INTEGER) - nargs++; - else - return -EINVAL; - } - - if (nargs > NR_FWNODE_REFERENCE_ARGS) - return -EINVAL; - - if (idx == index) { - args->fwnode = ref_fwnode; - args->nargs = nargs; - for (i = 0; i < nargs; i++) - args->args[i] = element[i].integer.value; + ret = acpi_get_ref_args(idx == index ? args : NULL, + acpi_fwnode_handle(device), + &element, end, num_args); + if (ret < 0) + return ret; + if (idx == index) return 0; - } - element += nargs; - } else if (element->type == ACPI_TYPE_INTEGER) { + break; + case ACPI_TYPE_INTEGER: if (idx == index) return -ENOENT; element++; - } else { + break; + default: return -EINVAL; } @@ -852,67 +1025,37 @@ static int acpi_data_prop_read_single(const struct acpi_device_data *data, return ret; } -static int acpi_copy_property_array_u8(const union acpi_object *items, u8 *val, - size_t nval) -{ - int i; - - for (i = 0; i < nval; i++) { - if (items[i].type != ACPI_TYPE_INTEGER) - return -EPROTO; - if (items[i].integer.value > U8_MAX) - return -EOVERFLOW; - - val[i] = items[i].integer.value; - } - return 0; -} - -static int acpi_copy_property_array_u16(const union acpi_object *items, - u16 *val, size_t nval) -{ - int i; - - for (i = 0; i < nval; i++) { - if (items[i].type != ACPI_TYPE_INTEGER) - return -EPROTO; - if (items[i].integer.value > U16_MAX) - return -EOVERFLOW; - - val[i] = items[i].integer.value; - } - return 0; -} - -static int acpi_copy_property_array_u32(const union acpi_object *items, - u32 *val, size_t nval) -{ - int i; - - for (i = 0; i < nval; i++) { - if (items[i].type != ACPI_TYPE_INTEGER) - return -EPROTO; - if (items[i].integer.value > U32_MAX) - return -EOVERFLOW; - - val[i] = items[i].integer.value; - } - return 0; -} - -static int acpi_copy_property_array_u64(const union acpi_object *items, - u64 *val, size_t nval) -{ - int i; - - for (i = 0; i < nval; i++) { - if (items[i].type != ACPI_TYPE_INTEGER) - return -EPROTO; - - val[i] = items[i].integer.value; - } - return 0; -} +#define acpi_copy_property_array_uint(items, val, nval) \ + ({ \ + typeof(items) __items = items; \ + typeof(val) __val = val; \ + typeof(nval) __nval = nval; \ + size_t i; \ + int ret = 0; \ + \ + for (i = 0; i < __nval; i++) { \ + if (__items->type == ACPI_TYPE_BUFFER) { \ + __val[i] = __items->buffer.pointer[i]; \ + continue; \ + } \ + if (__items[i].type != ACPI_TYPE_INTEGER) { \ + ret = -EPROTO; \ + break; \ + } \ + if (__items[i].integer.value > _Generic(__val, \ + u8: U8_MAX, \ + u16: U16_MAX, \ + u32: U32_MAX, \ + u64: U64_MAX, \ + default: 0U)) { \ + ret = -EOVERFLOW; \ + break; \ + } \ + \ + __val[i] = __items[i].integer.value; \ + } \ + ret; \ + }) static int acpi_copy_property_array_string(const union acpi_object *items, char **val, size_t nval) @@ -954,31 +1097,54 @@ static int acpi_data_prop_read(const struct acpi_device_data *data, } ret = acpi_data_get_property_array(data, propname, ACPI_TYPE_ANY, &obj); + if (ret && proptype >= DEV_PROP_U8 && proptype <= DEV_PROP_U64) + ret = acpi_data_get_property(data, propname, ACPI_TYPE_BUFFER, + &obj); if (ret) return ret; - if (!val) + if (!val) { + if (obj->type == ACPI_TYPE_BUFFER) + return obj->buffer.length; + return obj->package.count; + } - if (proptype != DEV_PROP_STRING && nval > obj->package.count) - return -EOVERFLOW; + switch (proptype) { + case DEV_PROP_STRING: + break; + case DEV_PROP_U8 ... DEV_PROP_U64: + if (obj->type == ACPI_TYPE_BUFFER) { + if (nval > obj->buffer.length) + return -EOVERFLOW; + break; + } + fallthrough; + default: + if (nval > obj->package.count) + return -EOVERFLOW; + break; + } if (nval == 0) return -EINVAL; - items = obj->package.elements; + if (obj->type != ACPI_TYPE_BUFFER) + items = obj->package.elements; + else + items = obj; switch (proptype) { case DEV_PROP_U8: - ret = acpi_copy_property_array_u8(items, (u8 *)val, nval); + ret = acpi_copy_property_array_uint(items, (u8 *)val, nval); break; case DEV_PROP_U16: - ret = acpi_copy_property_array_u16(items, (u16 *)val, nval); + ret = acpi_copy_property_array_uint(items, (u16 *)val, nval); break; case DEV_PROP_U32: - ret = acpi_copy_property_array_u32(items, (u32 *)val, nval); + ret = acpi_copy_property_array_uint(items, (u32 *)val, nval); break; case DEV_PROP_U64: - ret = acpi_copy_property_array_u64(items, (u64 *)val, nval); + ret = acpi_copy_property_array_uint(items, (u64 *)val, nval); break; case DEV_PROP_STRING: ret = acpi_copy_property_array_string( diff --git a/drivers/acpi/viot.c b/drivers/acpi/viot.c index 647f11cf165d..6132092dab2a 100644 --- a/drivers/acpi/viot.c +++ b/drivers/acpi/viot.c @@ -88,7 +88,7 @@ static int __init viot_get_pci_iommu_fwnode(struct viot_iommu *viommu, return -ENODEV; } - fwnode = pdev->dev.fwnode; + fwnode = dev_fwnode(&pdev->dev); if (!fwnode) { /* * PCI devices aren't necessarily described by ACPI. Create a @@ -101,7 +101,7 @@ static int __init viot_get_pci_iommu_fwnode(struct viot_iommu *viommu, } set_primary_fwnode(&pdev->dev, fwnode); } - viommu->fwnode = pdev->dev.fwnode; + viommu->fwnode = dev_fwnode(&pdev->dev); pci_dev_put(pdev); return 0; } @@ -314,7 +314,7 @@ static int viot_dev_iommu_init(struct device *dev, struct viot_iommu *viommu, return -ENODEV; /* We're not translating ourself */ - if (viommu->fwnode == dev->fwnode) + if (device_match_fwnode(dev, viommu->fwnode)) return -EINVAL; ops = iommu_ops_from_fwnode(viommu->fwnode); diff --git a/drivers/atm/idt77252.c b/drivers/atm/idt77252.c index 81ce81a75fc6..681cb3786794 100644 --- a/drivers/atm/idt77252.c +++ b/drivers/atm/idt77252.c @@ -3752,6 +3752,7 @@ static void __exit idt77252_exit(void) card = idt77252_chain; dev = card->atmdev; idt77252_chain = card->next; + del_timer_sync(&card->tst_timer); if (dev->phy->stop) dev->phy->stop(dev); diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 0d8ec2fe5740..f9e39301c4af 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1297,7 +1297,7 @@ static void rbd_osd_submit(struct ceph_osd_request *osd_req) dout("%s osd_req %p for obj_req %p objno %llu %llu~%llu\n", __func__, osd_req, obj_req, obj_req->ex.oe_objno, obj_req->ex.oe_off, obj_req->ex.oe_len); - ceph_osdc_start_request(osd_req->r_osdc, osd_req, false); + ceph_osdc_start_request(osd_req->r_osdc, osd_req); } /* @@ -2081,7 +2081,7 @@ static int rbd_object_map_update(struct rbd_obj_request *obj_req, u64 snap_id, if (ret) return ret; - ceph_osdc_start_request(osdc, req, false); + ceph_osdc_start_request(osdc, req); return 0; } @@ -4768,7 +4768,7 @@ static int rbd_obj_read_sync(struct rbd_device *rbd_dev, if (ret) goto out_req; - ceph_osdc_start_request(osdc, req, false); + ceph_osdc_start_request(osdc, req); ret = ceph_osdc_wait_request(osdc, req); if (ret >= 0) ceph_copy_from_page_vector(pages, buf, 0, ret); diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index d7d72e8f6e55..30255fcaf181 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -101,6 +101,14 @@ static inline blk_status_t virtblk_result(struct virtblk_req *vbr) } } +static inline struct virtio_blk_vq *get_virtio_blk_vq(struct blk_mq_hw_ctx *hctx) +{ + struct virtio_blk *vblk = hctx->queue->queuedata; + struct virtio_blk_vq *vq = &vblk->vqs[hctx->queue_num]; + + return vq; +} + static int virtblk_add_req(struct virtqueue *vq, struct virtblk_req *vbr) { struct scatterlist hdr, status, *sgs[3]; @@ -416,7 +424,7 @@ static void virtio_queue_rqs(struct request **rqlist) struct request *requeue_list = NULL; rq_list_for_each_safe(rqlist, req, next) { - struct virtio_blk_vq *vq = req->mq_hctx->driver_data; + struct virtio_blk_vq *vq = get_virtio_blk_vq(req->mq_hctx); bool kick; if (!virtblk_prep_rq_batch(req)) { @@ -837,7 +845,7 @@ static void virtblk_complete_batch(struct io_comp_batch *iob) static int virtblk_poll(struct blk_mq_hw_ctx *hctx, struct io_comp_batch *iob) { struct virtio_blk *vblk = hctx->queue->queuedata; - struct virtio_blk_vq *vq = hctx->driver_data; + struct virtio_blk_vq *vq = get_virtio_blk_vq(hctx); struct virtblk_req *vbr; unsigned long flags; unsigned int len; @@ -862,22 +870,10 @@ static int virtblk_poll(struct blk_mq_hw_ctx *hctx, struct io_comp_batch *iob) return found; } -static int virtblk_init_hctx(struct blk_mq_hw_ctx *hctx, void *data, - unsigned int hctx_idx) -{ - struct virtio_blk *vblk = data; - struct virtio_blk_vq *vq = &vblk->vqs[hctx_idx]; - - WARN_ON(vblk->tag_set.tags[hctx_idx] != hctx->tags); - hctx->driver_data = vq; - return 0; -} - static const struct blk_mq_ops virtio_mq_ops = { .queue_rq = virtio_queue_rq, .queue_rqs = virtio_queue_rqs, .commit_rqs = virtio_commit_rqs, - .init_hctx = virtblk_init_hctx, .complete = virtblk_request_done, .map_queues = virtblk_map_queues, .poll = virtblk_poll, diff --git a/drivers/clocksource/timer-riscv.c b/drivers/clocksource/timer-riscv.c index e460df7ed799..969a552da8d2 100644 --- a/drivers/clocksource/timer-riscv.c +++ b/drivers/clocksource/timer-riscv.c @@ -7,6 +7,9 @@ * either be read from the "time" and "timeh" CSRs, and can use the SBI to * setup events, or directly accessed using MMIO registers. */ + +#define pr_fmt(fmt) "riscv-timer: " fmt + #include <linux/clocksource.h> #include <linux/clockchips.h> #include <linux/cpu.h> @@ -20,14 +23,28 @@ #include <linux/of_irq.h> #include <clocksource/timer-riscv.h> #include <asm/smp.h> +#include <asm/hwcap.h> #include <asm/sbi.h> #include <asm/timex.h> +static DEFINE_STATIC_KEY_FALSE(riscv_sstc_available); + static int riscv_clock_next_event(unsigned long delta, struct clock_event_device *ce) { + u64 next_tval = get_cycles64() + delta; + csr_set(CSR_IE, IE_TIE); - sbi_set_timer(get_cycles64() + delta); + if (static_branch_likely(&riscv_sstc_available)) { +#if defined(CONFIG_32BIT) + csr_write(CSR_STIMECMP, next_tval & 0xFFFFFFFF); + csr_write(CSR_STIMECMPH, next_tval >> 32); +#else + csr_write(CSR_STIMECMP, next_tval); +#endif + } else + sbi_set_timer(next_tval); + return 0; } @@ -166,6 +183,12 @@ static int __init riscv_timer_init_dt(struct device_node *n) if (error) pr_err("cpu hp setup state failed for RISCV timer [%d]\n", error); + + if (riscv_isa_extension_available(NULL, SSTC)) { + pr_info("Timer interrupt in S-mode is available via sstc extension\n"); + static_branch_enable(&riscv_sstc_available); + } + return error; } diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index eb0c2d041f13..86d670c71286 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -1226,7 +1226,7 @@ retry: ret = dma_resv_lock_slow_interruptible(obj->resv, acquire_ctx); if (ret) { - ww_acquire_done(acquire_ctx); + ww_acquire_fini(acquire_ctx); return ret; } } @@ -1251,7 +1251,7 @@ retry: goto retry; } - ww_acquire_done(acquire_ctx); + ww_acquire_fini(acquire_ctx); return ret; } } diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c index 8ad0e02991ca..904fc893c905 100644 --- a/drivers/gpu/drm/drm_gem_shmem_helper.c +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c @@ -302,6 +302,7 @@ static int drm_gem_shmem_vmap_locked(struct drm_gem_shmem_object *shmem, ret = dma_buf_vmap(obj->import_attach->dmabuf, map); if (!ret) { if (WARN_ON(map->is_iomem)) { + dma_buf_vunmap(obj->import_attach->dmabuf, map); ret = -EIO; goto err_put_pages; } diff --git a/drivers/input/input-core-private.h b/drivers/input/input-core-private.h new file mode 100644 index 000000000000..116834cf8868 --- /dev/null +++ b/drivers/input/input-core-private.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _INPUT_CORE_PRIVATE_H +#define _INPUT_CORE_PRIVATE_H + +/* + * Functions and definitions that are private to input core, + * should not be used by input drivers or handlers. + */ + +struct input_dev; + +void input_mt_release_slots(struct input_dev *dev); +void input_handle_event(struct input_dev *dev, + unsigned int type, unsigned int code, int value); + +#endif /* _INPUT_CORE_PRIVATE_H */ diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c index 44fe6f2f063c..14b53dac1253 100644 --- a/drivers/input/input-mt.c +++ b/drivers/input/input-mt.c @@ -8,6 +8,7 @@ #include <linux/input/mt.h> #include <linux/export.h> #include <linux/slab.h> +#include "input-core-private.h" #define TRKID_SGN ((TRKID_MAX + 1) >> 1) @@ -259,10 +260,13 @@ static void __input_mt_drop_unused(struct input_dev *dev, struct input_mt *mt) { int i; + lockdep_assert_held(&dev->event_lock); + for (i = 0; i < mt->num_slots; i++) { - if (!input_mt_is_used(mt, &mt->slots[i])) { - input_mt_slot(dev, i); - input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); + if (input_mt_is_active(&mt->slots[i]) && + !input_mt_is_used(mt, &mt->slots[i])) { + input_handle_event(dev, EV_ABS, ABS_MT_SLOT, i); + input_handle_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); } } } @@ -278,13 +282,44 @@ void input_mt_drop_unused(struct input_dev *dev) struct input_mt *mt = dev->mt; if (mt) { + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); + __input_mt_drop_unused(dev, mt); mt->frame++; + + spin_unlock_irqrestore(&dev->event_lock, flags); } } EXPORT_SYMBOL(input_mt_drop_unused); /** + * input_mt_release_slots() - Deactivate all slots + * @dev: input device with allocated MT slots + * + * Lift all active slots. + */ +void input_mt_release_slots(struct input_dev *dev) +{ + struct input_mt *mt = dev->mt; + + lockdep_assert_held(&dev->event_lock); + + if (mt) { + /* This will effectively mark all slots unused. */ + mt->frame++; + + __input_mt_drop_unused(dev, mt); + + if (test_bit(ABS_PRESSURE, dev->absbit)) + input_handle_event(dev, EV_ABS, ABS_PRESSURE, 0); + + mt->frame++; + } +} + +/** * input_mt_sync_frame() - synchronize mt frame * @dev: input device with allocated MT slots * @@ -300,8 +335,13 @@ void input_mt_sync_frame(struct input_dev *dev) if (!mt) return; - if (mt->flags & INPUT_MT_DROP_UNUSED) + if (mt->flags & INPUT_MT_DROP_UNUSED) { + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); __input_mt_drop_unused(dev, mt); + spin_unlock_irqrestore(&dev->event_lock, flags); + } if ((mt->flags & INPUT_MT_POINTER) && !(mt->flags & INPUT_MT_SEMI_MT)) use_count = true; diff --git a/drivers/input/input.c b/drivers/input/input.c index 1365c9dfb5f2..ebb2b7f0f8ff 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -24,6 +24,7 @@ #include <linux/mutex.h> #include <linux/rcupdate.h> #include "input-compat.h" +#include "input-core-private.h" #include "input-poller.h" MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); @@ -142,6 +143,8 @@ static void input_pass_values(struct input_dev *dev, struct input_handle *handle; struct input_value *v; + lockdep_assert_held(&dev->event_lock); + if (!count) return; @@ -174,44 +177,6 @@ static void input_pass_values(struct input_dev *dev, } } -static void input_pass_event(struct input_dev *dev, - unsigned int type, unsigned int code, int value) -{ - struct input_value vals[] = { { type, code, value } }; - - input_pass_values(dev, vals, ARRAY_SIZE(vals)); -} - -/* - * Generate software autorepeat event. Note that we take - * dev->event_lock here to avoid racing with input_event - * which may cause keys get "stuck". - */ -static void input_repeat_key(struct timer_list *t) -{ - struct input_dev *dev = from_timer(dev, t, timer); - unsigned long flags; - - spin_lock_irqsave(&dev->event_lock, flags); - - if (test_bit(dev->repeat_key, dev->key) && - is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) { - struct input_value vals[] = { - { EV_KEY, dev->repeat_key, 2 }, - input_value_sync - }; - - input_set_timestamp(dev, ktime_get()); - input_pass_values(dev, vals, ARRAY_SIZE(vals)); - - if (dev->rep[REP_PERIOD]) - mod_timer(&dev->timer, jiffies + - msecs_to_jiffies(dev->rep[REP_PERIOD])); - } - - spin_unlock_irqrestore(&dev->event_lock, flags); -} - #define INPUT_IGNORE_EVENT 0 #define INPUT_PASS_TO_HANDLERS 1 #define INPUT_PASS_TO_DEVICE 2 @@ -275,6 +240,10 @@ static int input_get_disposition(struct input_dev *dev, int disposition = INPUT_IGNORE_EVENT; int value = *pval; + /* filter-out events from inhibited devices */ + if (dev->inhibited) + return INPUT_IGNORE_EVENT; + switch (type) { case EV_SYN: @@ -375,19 +344,9 @@ static int input_get_disposition(struct input_dev *dev, return disposition; } -static void input_handle_event(struct input_dev *dev, - unsigned int type, unsigned int code, int value) +static void input_event_dispose(struct input_dev *dev, int disposition, + unsigned int type, unsigned int code, int value) { - int disposition; - - /* filter-out events from inhibited devices */ - if (dev->inhibited) - return; - - disposition = input_get_disposition(dev, type, code, &value); - if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN) - add_input_randomness(type, code, value); - if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event) dev->event(dev, type, code, value); @@ -426,7 +385,22 @@ static void input_handle_event(struct input_dev *dev, input_pass_values(dev, dev->vals, dev->num_vals); dev->num_vals = 0; } +} +void input_handle_event(struct input_dev *dev, + unsigned int type, unsigned int code, int value) +{ + int disposition; + + lockdep_assert_held(&dev->event_lock); + + disposition = input_get_disposition(dev, type, code, &value); + if (disposition != INPUT_IGNORE_EVENT) { + if (type != EV_SYN) + add_input_randomness(type, code, value); + + input_event_dispose(dev, disposition, type, code, value); + } } /** @@ -613,7 +587,7 @@ static void __input_release_device(struct input_handle *handle) lockdep_is_held(&dev->mutex)); if (grabber == handle) { rcu_assign_pointer(dev->grab, NULL); - /* Make sure input_pass_event() notices that grab is gone */ + /* Make sure input_pass_values() notices that grab is gone */ synchronize_rcu(); list_for_each_entry(handle, &dev->h_list, d_node) @@ -736,7 +710,7 @@ void input_close_device(struct input_handle *handle) if (!--handle->open) { /* - * synchronize_rcu() makes sure that input_pass_event() + * synchronize_rcu() makes sure that input_pass_values() * completed and that no more input events are delivered * through this handle */ @@ -751,22 +725,21 @@ EXPORT_SYMBOL(input_close_device); * Simulate keyup events for all keys that are marked as pressed. * The function must be called with dev->event_lock held. */ -static void input_dev_release_keys(struct input_dev *dev) +static bool input_dev_release_keys(struct input_dev *dev) { bool need_sync = false; int code; + lockdep_assert_held(&dev->event_lock); + if (is_event_supported(EV_KEY, dev->evbit, EV_MAX)) { for_each_set_bit(code, dev->key, KEY_CNT) { - input_pass_event(dev, EV_KEY, code, 0); + input_handle_event(dev, EV_KEY, code, 0); need_sync = true; } - - if (need_sync) - input_pass_event(dev, EV_SYN, SYN_REPORT, 1); - - memset(dev->key, 0, sizeof(dev->key)); } + + return need_sync; } /* @@ -793,7 +766,8 @@ static void input_disconnect_device(struct input_dev *dev) * generate events even after we done here but they will not * reach any handlers. */ - input_dev_release_keys(dev); + if (input_dev_release_keys(dev)) + input_handle_event(dev, EV_SYN, SYN_REPORT, 1); list_for_each_entry(handle, &dev->h_list, d_node) handle->open = 0; @@ -1004,12 +978,16 @@ int input_set_keycode(struct input_dev *dev, } else if (test_bit(EV_KEY, dev->evbit) && !is_event_supported(old_keycode, dev->keybit, KEY_MAX) && __test_and_clear_bit(old_keycode, dev->key)) { - struct input_value vals[] = { - { EV_KEY, old_keycode, 0 }, - input_value_sync - }; - - input_pass_values(dev, vals, ARRAY_SIZE(vals)); + /* + * We have to use input_event_dispose() here directly instead + * of input_handle_event() because the key we want to release + * here is considered no longer supported by the device and + * input_handle_event() will ignore it. + */ + input_event_dispose(dev, INPUT_PASS_TO_HANDLERS, + EV_KEY, old_keycode, 0); + input_event_dispose(dev, INPUT_PASS_TO_HANDLERS | INPUT_FLUSH, + EV_SYN, SYN_REPORT, 1); } out: @@ -1784,7 +1762,8 @@ void input_reset_device(struct input_dev *dev) spin_lock_irqsave(&dev->event_lock, flags); input_dev_toggle(dev, true); - input_dev_release_keys(dev); + if (input_dev_release_keys(dev)) + input_handle_event(dev, EV_SYN, SYN_REPORT, 1); spin_unlock_irqrestore(&dev->event_lock, flags); mutex_unlock(&dev->mutex); @@ -1806,7 +1785,9 @@ static int input_inhibit_device(struct input_dev *dev) } spin_lock_irq(&dev->event_lock); + input_mt_release_slots(dev); input_dev_release_keys(dev); + input_handle_event(dev, EV_SYN, SYN_REPORT, 1); input_dev_toggle(dev, false); spin_unlock_irq(&dev->event_lock); @@ -1857,7 +1838,8 @@ static int input_dev_suspend(struct device *dev) * Keys that are pressed now are unlikely to be * still pressed when we resume. */ - input_dev_release_keys(input_dev); + if (input_dev_release_keys(input_dev)) + input_handle_event(input_dev, EV_SYN, SYN_REPORT, 1); /* Turn off LEDs and sounds, if any are active. */ input_dev_toggle(input_dev, false); @@ -1891,7 +1873,8 @@ static int input_dev_freeze(struct device *dev) * Keys that are pressed now are unlikely to be * still pressed when we resume. */ - input_dev_release_keys(input_dev); + if (input_dev_release_keys(input_dev)) + input_handle_event(input_dev, EV_SYN, SYN_REPORT, 1); spin_unlock_irq(&input_dev->event_lock); @@ -2259,6 +2242,34 @@ static void devm_input_device_unregister(struct device *dev, void *res) __input_unregister_device(input); } +/* + * Generate software autorepeat event. Note that we take + * dev->event_lock here to avoid racing with input_event + * which may cause keys get "stuck". + */ +static void input_repeat_key(struct timer_list *t) +{ + struct input_dev *dev = from_timer(dev, t, timer); + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); + + if (!dev->inhibited && + test_bit(dev->repeat_key, dev->key) && + is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) { + + input_set_timestamp(dev, ktime_get()); + input_handle_event(dev, EV_KEY, dev->repeat_key, 2); + input_handle_event(dev, EV_SYN, SYN_REPORT, 1); + + if (dev->rep[REP_PERIOD]) + mod_timer(&dev->timer, jiffies + + msecs_to_jiffies(dev->rep[REP_PERIOD])); + } + + spin_unlock_irqrestore(&dev->event_lock, flags); +} + /** * input_enable_softrepeat - enable software autorepeat * @dev: input device diff --git a/drivers/input/joystick/adc-joystick.c b/drivers/input/joystick/adc-joystick.c index 78ebca7d400a..e0cfdc84763f 100644 --- a/drivers/input/joystick/adc-joystick.c +++ b/drivers/input/joystick/adc-joystick.c @@ -222,13 +222,6 @@ static int adc_joystick_probe(struct platform_device *pdev) if (error) return error; - input_set_drvdata(input, joy); - error = input_register_device(input); - if (error) { - dev_err(dev, "Unable to register input device\n"); - return error; - } - joy->buffer = iio_channel_get_all_cb(dev, adc_joystick_handle, joy); if (IS_ERR(joy->buffer)) { dev_err(dev, "Unable to allocate callback buffer\n"); @@ -241,6 +234,14 @@ static int adc_joystick_probe(struct platform_device *pdev) return error; } + input_set_drvdata(input, joy); + + error = input_register_device(input); + if (error) { + dev_err(dev, "Unable to register input device\n"); + return error; + } + return 0; } diff --git a/drivers/input/joystick/sensehat-joystick.c b/drivers/input/joystick/sensehat-joystick.c index 5ad1fe4ff496..a84df39d3b2f 100644 --- a/drivers/input/joystick/sensehat-joystick.c +++ b/drivers/input/joystick/sensehat-joystick.c @@ -98,10 +98,8 @@ static int sensehat_joystick_probe(struct platform_device *pdev) } irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "Could not retrieve interrupt request"); + if (irq < 0) return irq; - } error = devm_request_threaded_irq(&pdev->dev, irq, NULL, sensehat_joystick_report, diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 4ea79db8f134..a20ee693b22b 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -795,7 +795,7 @@ config KEYBOARD_MT6779 config KEYBOARD_MTK_PMIC tristate "MediaTek PMIC keys support" - depends on MFD_MT6397 + depends on MFD_MT6397 || COMPILE_TEST help Say Y here if you want to use the pmic keys (powerkey/homekey). diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c index 1592da4de336..1a1a05d7cd42 100644 --- a/drivers/input/keyboard/adp5588-keys.c +++ b/drivers/input/keyboard/adp5588-keys.c @@ -8,17 +8,19 @@ * Copyright (C) 2008-2010 Analog Devices Inc. */ -#include <linux/module.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/gpio/driver.h> +#include <linux/i2c.h> +#include <linux/input.h> #include <linux/interrupt.h> #include <linux/irq.h> -#include <linux/workqueue.h> -#include <linux/errno.h> -#include <linux/pm.h> +#include <linux/ktime.h> +#include <linux/module.h> #include <linux/platform_device.h> -#include <linux/input.h> -#include <linux/i2c.h> -#include <linux/gpio/driver.h> +#include <linux/pm.h> #include <linux/slab.h> +#include <linux/timekeeping.h> #include <linux/platform_data/adp5588.h> @@ -36,18 +38,18 @@ * asserted. */ #define WA_DELAYED_READOUT_REVID(rev) ((rev) < 4) +#define WA_DELAYED_READOUT_TIME 25 struct adp5588_kpad { struct i2c_client *client; struct input_dev *input; - struct delayed_work work; + ktime_t irq_time; unsigned long delay; unsigned short keycode[ADP5588_KEYMAPSIZE]; const struct adp5588_gpi_map *gpimap; unsigned short gpimapsize; #ifdef CONFIG_GPIOLIB unsigned char gpiomap[ADP5588_MAXGPIO]; - bool export_gpio; struct gpio_chip gc; struct mutex gpio_lock; /* Protect cached dir, dat_out */ u8 dat_out[3]; @@ -179,6 +181,21 @@ static int adp5588_build_gpiomap(struct adp5588_kpad *kpad, return n_unused; } +static void adp5588_gpio_do_teardown(void *_kpad) +{ + struct adp5588_kpad *kpad = _kpad; + struct device *dev = &kpad->client->dev; + const struct adp5588_kpad_platform_data *pdata = dev_get_platdata(dev); + const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data; + int error; + + error = gpio_data->teardown(kpad->client, + kpad->gc.base, kpad->gc.ngpio, + gpio_data->context); + if (error) + dev_warn(&kpad->client->dev, "teardown failed %d\n", error); +} + static int adp5588_gpio_add(struct adp5588_kpad *kpad) { struct device *dev = &kpad->client->dev; @@ -195,8 +212,6 @@ static int adp5588_gpio_add(struct adp5588_kpad *kpad) return 0; } - kpad->export_gpio = true; - kpad->gc.direction_input = adp5588_gpio_direction_input; kpad->gc.direction_output = adp5588_gpio_direction_output; kpad->gc.get = adp5588_gpio_get_value; @@ -210,9 +225,9 @@ static int adp5588_gpio_add(struct adp5588_kpad *kpad) mutex_init(&kpad->gpio_lock); - error = gpiochip_add_data(&kpad->gc, kpad); + error = devm_gpiochip_add_data(dev, &kpad->gc, kpad); if (error) { - dev_err(dev, "gpiochip_add failed, err: %d\n", error); + dev_err(dev, "gpiochip_add failed: %d\n", error); return error; } @@ -227,41 +242,24 @@ static int adp5588_gpio_add(struct adp5588_kpad *kpad) kpad->gc.base, kpad->gc.ngpio, gpio_data->context); if (error) - dev_warn(dev, "setup failed, %d\n", error); + dev_warn(dev, "setup failed: %d\n", error); } - return 0; -} - -static void adp5588_gpio_remove(struct adp5588_kpad *kpad) -{ - struct device *dev = &kpad->client->dev; - const struct adp5588_kpad_platform_data *pdata = dev_get_platdata(dev); - const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data; - int error; - - if (!kpad->export_gpio) - return; - if (gpio_data->teardown) { - error = gpio_data->teardown(kpad->client, - kpad->gc.base, kpad->gc.ngpio, - gpio_data->context); + error = devm_add_action(dev, adp5588_gpio_do_teardown, kpad); if (error) - dev_warn(dev, "teardown failed %d\n", error); + dev_warn(dev, "failed to schedule teardown: %d\n", + error); } - gpiochip_remove(&kpad->gc); + return 0; } + #else static inline int adp5588_gpio_add(struct adp5588_kpad *kpad) { return 0; } - -static inline void adp5588_gpio_remove(struct adp5588_kpad *kpad) -{ -} #endif static void adp5588_report_events(struct adp5588_kpad *kpad, int ev_cnt) @@ -289,13 +287,36 @@ static void adp5588_report_events(struct adp5588_kpad *kpad, int ev_cnt) } } -static void adp5588_work(struct work_struct *work) +static irqreturn_t adp5588_hard_irq(int irq, void *handle) { - struct adp5588_kpad *kpad = container_of(work, - struct adp5588_kpad, work.work); + struct adp5588_kpad *kpad = handle; + + kpad->irq_time = ktime_get(); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t adp5588_thread_irq(int irq, void *handle) +{ + struct adp5588_kpad *kpad = handle; struct i2c_client *client = kpad->client; + ktime_t target_time, now; + unsigned long delay; int status, ev_cnt; + /* + * Readout needs to wait for at least 25ms after the notification + * for REVID < 4. + */ + if (kpad->delay) { + target_time = ktime_add_ms(kpad->irq_time, kpad->delay); + now = ktime_get(); + if (ktime_before(now, target_time)) { + delay = ktime_to_us(ktime_sub(target_time, now)); + usleep_range(delay, delay + 1000); + } + } + status = adp5588_read(client, INT_STAT); if (status & ADP5588_OVR_FLOW_INT) /* Unlikely and should never happen */ @@ -308,20 +329,8 @@ static void adp5588_work(struct work_struct *work) input_sync(kpad->input); } } - adp5588_write(client, INT_STAT, status); /* Status is W1C */ -} - -static irqreturn_t adp5588_irq(int irq, void *handle) -{ - struct adp5588_kpad *kpad = handle; - /* - * use keventd context to read the event fifo registers - * Schedule readout at least 25ms after notification for - * REVID < 4 - */ - - schedule_delayed_work(&kpad->work, kpad->delay); + adp5588_write(client, INT_STAT, status); /* Status is W1C */ return IRQ_HANDLED; } @@ -496,30 +505,27 @@ static int adp5588_probe(struct i2c_client *client, return -EINVAL; } - kpad = kzalloc(sizeof(*kpad), GFP_KERNEL); - input = input_allocate_device(); - if (!kpad || !input) { - error = -ENOMEM; - goto err_free_mem; - } + kpad = devm_kzalloc(&client->dev, sizeof(*kpad), GFP_KERNEL); + if (!kpad) + return -ENOMEM; + + input = devm_input_allocate_device(&client->dev); + if (!input) + return -ENOMEM; kpad->client = client; kpad->input = input; - INIT_DELAYED_WORK(&kpad->work, adp5588_work); ret = adp5588_read(client, DEV_ID); - if (ret < 0) { - error = ret; - goto err_free_mem; - } + if (ret < 0) + return ret; revid = (u8) ret & ADP5588_DEVICE_ID_MASK; if (WA_DELAYED_READOUT_REVID(revid)) - kpad->delay = msecs_to_jiffies(30); + kpad->delay = msecs_to_jiffies(WA_DELAYED_READOUT_TIME); input->name = client->name; input->phys = "adp5588-keys/input0"; - input->dev.parent = &client->dev; input_set_drvdata(input, kpad); @@ -556,95 +562,63 @@ static int adp5588_probe(struct i2c_client *client, error = input_register_device(input); if (error) { - dev_err(&client->dev, "unable to register input device\n"); - goto err_free_mem; + dev_err(&client->dev, "unable to register input device: %d\n", + error); + return error; } - error = request_irq(client->irq, adp5588_irq, - IRQF_TRIGGER_FALLING, - client->dev.driver->name, kpad); + error = devm_request_threaded_irq(&client->dev, client->irq, + adp5588_hard_irq, adp5588_thread_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + client->dev.driver->name, kpad); if (error) { - dev_err(&client->dev, "irq %d busy?\n", client->irq); - goto err_unreg_dev; + dev_err(&client->dev, "failed to request irq %d: %d\n", + client->irq, error); + return error; } error = adp5588_setup(client); if (error) - goto err_free_irq; + return error; if (kpad->gpimapsize) adp5588_report_switch_state(kpad); error = adp5588_gpio_add(kpad); if (error) - goto err_free_irq; - - device_init_wakeup(&client->dev, 1); - i2c_set_clientdata(client, kpad); + return error; dev_info(&client->dev, "Rev.%d keypad, irq %d\n", revid, client->irq); return 0; - - err_free_irq: - free_irq(client->irq, kpad); - cancel_delayed_work_sync(&kpad->work); - err_unreg_dev: - input_unregister_device(input); - input = NULL; - err_free_mem: - input_free_device(input); - kfree(kpad); - - return error; } static int adp5588_remove(struct i2c_client *client) { - struct adp5588_kpad *kpad = i2c_get_clientdata(client); - adp5588_write(client, CFG, 0); - free_irq(client->irq, kpad); - cancel_delayed_work_sync(&kpad->work); - input_unregister_device(kpad->input); - adp5588_gpio_remove(kpad); - kfree(kpad); + /* all resources will be freed by devm */ return 0; } -#ifdef CONFIG_PM -static int adp5588_suspend(struct device *dev) +static int __maybe_unused adp5588_suspend(struct device *dev) { - struct adp5588_kpad *kpad = dev_get_drvdata(dev); - struct i2c_client *client = kpad->client; + struct i2c_client *client = to_i2c_client(dev); disable_irq(client->irq); - cancel_delayed_work_sync(&kpad->work); - - if (device_may_wakeup(&client->dev)) - enable_irq_wake(client->irq); return 0; } -static int adp5588_resume(struct device *dev) +static int __maybe_unused adp5588_resume(struct device *dev) { - struct adp5588_kpad *kpad = dev_get_drvdata(dev); - struct i2c_client *client = kpad->client; - - if (device_may_wakeup(&client->dev)) - disable_irq_wake(client->irq); + struct i2c_client *client = to_i2c_client(dev); enable_irq(client->irq); return 0; } -static const struct dev_pm_ops adp5588_dev_pm_ops = { - .suspend = adp5588_suspend, - .resume = adp5588_resume, -}; -#endif +static SIMPLE_DEV_PM_OPS(adp5588_dev_pm_ops, adp5588_suspend, adp5588_resume); static const struct i2c_device_id adp5588_id[] = { { "adp5588-keys", 0 }, @@ -656,9 +630,7 @@ MODULE_DEVICE_TABLE(i2c, adp5588_id); static struct i2c_driver adp5588_driver = { .driver = { .name = KBUILD_MODNAME, -#ifdef CONFIG_PM .pm = &adp5588_dev_pm_ops, -#endif }, .probe = adp5588_probe, .remove = adp5588_remove, diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index cc73a149da28..c14136b733a9 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -12,6 +12,7 @@ // expensive. #include <linux/module.h> +#include <linux/acpi.h> #include <linux/bitops.h> #include <linux/i2c.h> #include <linux/input.h> @@ -518,6 +519,50 @@ static int cros_ec_keyb_register_bs(struct cros_ec_keyb *ckdev, return 0; } +static void cros_ec_keyb_parse_vivaldi_physmap(struct cros_ec_keyb *ckdev) +{ + u32 *physmap = ckdev->vdata.function_row_physmap; + unsigned int row, col, scancode; + int n_physmap; + int error; + int i; + + n_physmap = device_property_count_u32(ckdev->dev, + "function-row-physmap"); + if (n_physmap <= 0) + return; + + if (n_physmap >= VIVALDI_MAX_FUNCTION_ROW_KEYS) { + dev_warn(ckdev->dev, + "only up to %d top row keys is supported (%d specified)\n", + VIVALDI_MAX_FUNCTION_ROW_KEYS, n_physmap); + n_physmap = VIVALDI_MAX_FUNCTION_ROW_KEYS; + } + + error = device_property_read_u32_array(ckdev->dev, + "function-row-physmap", + physmap, n_physmap); + if (error) { + dev_warn(ckdev->dev, + "failed to parse function-row-physmap property: %d\n", + error); + return; + } + + /* + * Convert (in place) from row/column encoding to matrix "scancode" + * used by the driver. + */ + for (i = 0; i < n_physmap; i++) { + row = KEY_ROW(physmap[i]); + col = KEY_COL(physmap[i]); + scancode = MATRIX_SCAN_CODE(row, col, ckdev->row_shift); + physmap[i] = scancode; + } + + ckdev->vdata.num_function_row_keys = n_physmap; +} + /** * cros_ec_keyb_register_matrix - Register matrix keys * @@ -534,11 +579,6 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev) struct input_dev *idev; const char *phys; int err; - struct property *prop; - const __be32 *p; - u32 *physmap; - u32 key_pos; - unsigned int row, col, scancode, n_physmap; err = matrix_keypad_parse_properties(dev, &ckdev->rows, &ckdev->cols); if (err) @@ -573,7 +613,7 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev) idev->id.product = 0; idev->dev.parent = dev; - ckdev->ghost_filter = of_property_read_bool(dev->of_node, + ckdev->ghost_filter = device_property_read_bool(dev, "google,needs-ghost-filter"); err = matrix_keypad_build_keymap(NULL, NULL, ckdev->rows, ckdev->cols, @@ -589,22 +629,7 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev) input_set_drvdata(idev, ckdev); ckdev->idev = idev; cros_ec_keyb_compute_valid_keys(ckdev); - - physmap = ckdev->vdata.function_row_physmap; - n_physmap = 0; - of_property_for_each_u32(dev->of_node, "function-row-physmap", - prop, p, key_pos) { - if (n_physmap == VIVALDI_MAX_FUNCTION_ROW_KEYS) { - dev_warn(dev, "Only support up to %d top row keys\n", - VIVALDI_MAX_FUNCTION_ROW_KEYS); - break; - } - row = KEY_ROW(key_pos); - col = KEY_COL(key_pos); - scancode = MATRIX_SCAN_CODE(row, col, ckdev->row_shift); - physmap[n_physmap++] = scancode; - } - ckdev->vdata.num_function_row_keys = n_physmap; + cros_ec_keyb_parse_vivaldi_physmap(ckdev); err = input_register_device(ckdev->idev); if (err) { @@ -653,14 +678,19 @@ static const struct attribute_group cros_ec_keyb_attr_group = { static int cros_ec_keyb_probe(struct platform_device *pdev) { - struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); + struct cros_ec_device *ec; struct device *dev = &pdev->dev; struct cros_ec_keyb *ckdev; bool buttons_switches_only = device_get_match_data(dev); int err; - if (!dev->of_node) - return -ENODEV; + /* + * If the parent ec device has not been probed yet, defer the probe of + * this keyboard/button driver until later. + */ + ec = dev_get_drvdata(pdev->dev.parent); + if (!ec) + return -EPROBE_DEFER; ckdev = devm_kzalloc(dev, sizeof(*ckdev), GFP_KERNEL); if (!ckdev) @@ -713,6 +743,14 @@ static int cros_ec_keyb_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_ACPI +static const struct acpi_device_id cros_ec_keyb_acpi_match[] = { + { "GOOG0007", true }, + { } +}; +MODULE_DEVICE_TABLE(acpi, cros_ec_keyb_acpi_match); +#endif + #ifdef CONFIG_OF static const struct of_device_id cros_ec_keyb_of_match[] = { { .compatible = "google,cros-ec-keyb" }, @@ -730,6 +768,7 @@ static struct platform_driver cros_ec_keyb_driver = { .driver = { .name = "cros-ec-keyb", .of_match_table = of_match_ptr(cros_ec_keyb_of_match), + .acpi_match_table = ACPI_PTR(cros_ec_keyb_acpi_match), .pm = &cros_ec_keyb_pm_ops, }, }; diff --git a/drivers/input/keyboard/mt6779-keypad.c b/drivers/input/keyboard/mt6779-keypad.c index 2e7c9187c10f..bf447bf598fb 100644 --- a/drivers/input/keyboard/mt6779-keypad.c +++ b/drivers/input/keyboard/mt6779-keypad.c @@ -17,6 +17,11 @@ #define MTK_KPD_DEBOUNCE 0x0018 #define MTK_KPD_DEBOUNCE_MASK GENMASK(13, 0) #define MTK_KPD_DEBOUNCE_MAX_MS 256 +#define MTK_KPD_SEL 0x0020 +#define MTK_KPD_SEL_COL GENMASK(15, 10) +#define MTK_KPD_SEL_ROW GENMASK(9, 4) +#define MTK_KPD_SEL_COLMASK(c) GENMASK((c) + 9, 10) +#define MTK_KPD_SEL_ROWMASK(r) GENMASK((r) + 3, 4) #define MTK_KPD_NUM_MEMS 5 #define MTK_KPD_NUM_BITS 136 /* 4*32+8 MEM5 only use 8 BITS */ @@ -42,7 +47,7 @@ static irqreturn_t mt6779_keypad_irq_handler(int irq, void *dev_id) const unsigned short *keycode = keypad->input_dev->keycode; DECLARE_BITMAP(new_state, MTK_KPD_NUM_BITS); DECLARE_BITMAP(change, MTK_KPD_NUM_BITS); - unsigned int bit_nr; + unsigned int bit_nr, key; unsigned int row, col; unsigned int scancode; unsigned int row_shift = get_count_order(keypad->n_cols); @@ -61,8 +66,10 @@ static irqreturn_t mt6779_keypad_irq_handler(int irq, void *dev_id) if (bit_nr % 32 >= 16) continue; - row = bit_nr / 32; - col = bit_nr % 32; + key = bit_nr / 32 * 16 + bit_nr % 32; + row = key / 9; + col = key % 9; + scancode = MATRIX_SCAN_CODE(row, col, row_shift); /* 1: not pressed, 0: pressed */ pressed = !test_bit(bit_nr, new_state); @@ -159,6 +166,11 @@ static int mt6779_keypad_pdrv_probe(struct platform_device *pdev) regmap_write(keypad->regmap, MTK_KPD_DEBOUNCE, (debounce * (1 << 5)) & MTK_KPD_DEBOUNCE_MASK); + regmap_update_bits(keypad->regmap, MTK_KPD_SEL, MTK_KPD_SEL_ROW, + MTK_KPD_SEL_ROWMASK(keypad->n_rows)); + regmap_update_bits(keypad->regmap, MTK_KPD_SEL, MTK_KPD_SEL_COL, + MTK_KPD_SEL_COLMASK(keypad->n_cols)); + keypad->clk = devm_clk_get(&pdev->dev, "kpd"); if (IS_ERR(keypad->clk)) return PTR_ERR(keypad->clk); diff --git a/drivers/input/keyboard/mtk-pmic-keys.c b/drivers/input/keyboard/mtk-pmic-keys.c index c31ab4368388..6404081253ea 100644 --- a/drivers/input/keyboard/mtk-pmic-keys.c +++ b/drivers/input/keyboard/mtk-pmic-keys.c @@ -18,17 +18,9 @@ #include <linux/platform_device.h> #include <linux/regmap.h> -#define MTK_PMIC_PWRKEY_RST_EN_MASK 0x1 -#define MTK_PMIC_PWRKEY_RST_EN_SHIFT 6 -#define MTK_PMIC_HOMEKEY_RST_EN_MASK 0x1 -#define MTK_PMIC_HOMEKEY_RST_EN_SHIFT 5 -#define MTK_PMIC_RST_DU_MASK 0x3 -#define MTK_PMIC_RST_DU_SHIFT 8 - -#define MTK_PMIC_PWRKEY_RST \ - (MTK_PMIC_PWRKEY_RST_EN_MASK << MTK_PMIC_PWRKEY_RST_EN_SHIFT) -#define MTK_PMIC_HOMEKEY_RST \ - (MTK_PMIC_HOMEKEY_RST_EN_MASK << MTK_PMIC_HOMEKEY_RST_EN_SHIFT) +#define MTK_PMIC_RST_DU_MASK GENMASK(9, 8) +#define MTK_PMIC_PWRKEY_RST BIT(6) +#define MTK_PMIC_HOMEKEY_RST BIT(5) #define MTK_PMIC_PWRKEY_INDEX 0 #define MTK_PMIC_HOMEKEY_INDEX 1 @@ -39,50 +31,58 @@ struct mtk_pmic_keys_regs { u32 deb_mask; u32 intsel_reg; u32 intsel_mask; + u32 rst_en_mask; }; #define MTK_PMIC_KEYS_REGS(_deb_reg, _deb_mask, \ - _intsel_reg, _intsel_mask) \ + _intsel_reg, _intsel_mask, _rst_mask) \ { \ .deb_reg = _deb_reg, \ .deb_mask = _deb_mask, \ .intsel_reg = _intsel_reg, \ .intsel_mask = _intsel_mask, \ + .rst_en_mask = _rst_mask, \ } struct mtk_pmic_regs { const struct mtk_pmic_keys_regs keys_regs[MTK_PMIC_MAX_KEY_COUNT]; u32 pmic_rst_reg; + u32 rst_lprst_mask; /* Long-press reset timeout bitmask */ }; static const struct mtk_pmic_regs mt6397_regs = { .keys_regs[MTK_PMIC_PWRKEY_INDEX] = MTK_PMIC_KEYS_REGS(MT6397_CHRSTATUS, - 0x8, MT6397_INT_RSV, 0x10), + 0x8, MT6397_INT_RSV, 0x10, MTK_PMIC_PWRKEY_RST), .keys_regs[MTK_PMIC_HOMEKEY_INDEX] = MTK_PMIC_KEYS_REGS(MT6397_OCSTATUS2, - 0x10, MT6397_INT_RSV, 0x8), + 0x10, MT6397_INT_RSV, 0x8, MTK_PMIC_HOMEKEY_RST), .pmic_rst_reg = MT6397_TOP_RST_MISC, + .rst_lprst_mask = MTK_PMIC_RST_DU_MASK, }; static const struct mtk_pmic_regs mt6323_regs = { .keys_regs[MTK_PMIC_PWRKEY_INDEX] = MTK_PMIC_KEYS_REGS(MT6323_CHRSTATUS, - 0x2, MT6323_INT_MISC_CON, 0x10), + 0x2, MT6323_INT_MISC_CON, 0x10, MTK_PMIC_PWRKEY_RST), .keys_regs[MTK_PMIC_HOMEKEY_INDEX] = MTK_PMIC_KEYS_REGS(MT6323_CHRSTATUS, - 0x4, MT6323_INT_MISC_CON, 0x8), + 0x4, MT6323_INT_MISC_CON, 0x8, MTK_PMIC_HOMEKEY_RST), .pmic_rst_reg = MT6323_TOP_RST_MISC, + .rst_lprst_mask = MTK_PMIC_RST_DU_MASK, }; static const struct mtk_pmic_regs mt6358_regs = { .keys_regs[MTK_PMIC_PWRKEY_INDEX] = MTK_PMIC_KEYS_REGS(MT6358_TOPSTATUS, - 0x2, MT6358_PSC_TOP_INT_CON0, 0x5), + 0x2, MT6358_PSC_TOP_INT_CON0, 0x5, + MTK_PMIC_PWRKEY_RST), .keys_regs[MTK_PMIC_HOMEKEY_INDEX] = MTK_PMIC_KEYS_REGS(MT6358_TOPSTATUS, - 0x8, MT6358_PSC_TOP_INT_CON0, 0xa), + 0x8, MT6358_PSC_TOP_INT_CON0, 0xa, + MTK_PMIC_HOMEKEY_RST), .pmic_rst_reg = MT6358_TOP_RST_MISC, + .rst_lprst_mask = MTK_PMIC_RST_DU_MASK, }; struct mtk_pmic_keys_info { @@ -108,53 +108,49 @@ enum mtk_pmic_keys_lp_mode { }; static void mtk_pmic_keys_lp_reset_setup(struct mtk_pmic_keys *keys, - u32 pmic_rst_reg) + const struct mtk_pmic_regs *regs) { - int ret; + const struct mtk_pmic_keys_regs *kregs_home, *kregs_pwr; u32 long_press_mode, long_press_debounce; + u32 value, mask; + int error; + + kregs_home = keys->keys[MTK_PMIC_HOMEKEY_INDEX].regs; + kregs_pwr = keys->keys[MTK_PMIC_PWRKEY_INDEX].regs; - ret = of_property_read_u32(keys->dev->of_node, - "power-off-time-sec", &long_press_debounce); - if (ret) + error = of_property_read_u32(keys->dev->of_node, "power-off-time-sec", + &long_press_debounce); + if (error) long_press_debounce = 0; - regmap_update_bits(keys->regmap, pmic_rst_reg, - MTK_PMIC_RST_DU_MASK << MTK_PMIC_RST_DU_SHIFT, - long_press_debounce << MTK_PMIC_RST_DU_SHIFT); + mask = regs->rst_lprst_mask; + value = long_press_debounce << (ffs(regs->rst_lprst_mask) - 1); - ret = of_property_read_u32(keys->dev->of_node, - "mediatek,long-press-mode", &long_press_mode); - if (ret) + error = of_property_read_u32(keys->dev->of_node, + "mediatek,long-press-mode", + &long_press_mode); + if (error) long_press_mode = LP_DISABLE; switch (long_press_mode) { - case LP_ONEKEY: - regmap_update_bits(keys->regmap, pmic_rst_reg, - MTK_PMIC_PWRKEY_RST, - MTK_PMIC_PWRKEY_RST); - regmap_update_bits(keys->regmap, pmic_rst_reg, - MTK_PMIC_HOMEKEY_RST, - 0); - break; case LP_TWOKEY: - regmap_update_bits(keys->regmap, pmic_rst_reg, - MTK_PMIC_PWRKEY_RST, - MTK_PMIC_PWRKEY_RST); - regmap_update_bits(keys->regmap, pmic_rst_reg, - MTK_PMIC_HOMEKEY_RST, - MTK_PMIC_HOMEKEY_RST); - break; + value |= kregs_home->rst_en_mask; + fallthrough; + + case LP_ONEKEY: + value |= kregs_pwr->rst_en_mask; + fallthrough; + case LP_DISABLE: - regmap_update_bits(keys->regmap, pmic_rst_reg, - MTK_PMIC_PWRKEY_RST, - 0); - regmap_update_bits(keys->regmap, pmic_rst_reg, - MTK_PMIC_HOMEKEY_RST, - 0); + mask |= kregs_home->rst_en_mask; + mask |= kregs_pwr->rst_en_mask; break; + default: break; } + + regmap_update_bits(keys->regmap, regs->pmic_rst_reg, mask, value); } static irqreturn_t mtk_pmic_keys_irq_handler_thread(int irq, void *data) @@ -358,7 +354,7 @@ static int mtk_pmic_keys_probe(struct platform_device *pdev) return error; } - mtk_pmic_keys_lp_reset_setup(keys, mtk_pmic_regs->pmic_rst_reg); + mtk_pmic_keys_lp_reset_setup(keys, mtk_pmic_regs); platform_set_drvdata(pdev, keys); diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index 8a7ce41b8c56..ee9d04a3f0d5 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -179,11 +179,9 @@ static irqreturn_t omap4_keypad_irq_thread_fn(int irq, void *dev_id) int error; u64 keys; - error = pm_runtime_get_sync(dev); - if (error < 0) { - pm_runtime_put_noidle(dev); + error = pm_runtime_resume_and_get(dev); + if (error) return IRQ_NONE; - } low = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE31_0); high = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE63_32); @@ -207,11 +205,9 @@ static int omap4_keypad_open(struct input_dev *input) struct device *dev = input->dev.parent; int error; - error = pm_runtime_get_sync(dev); - if (error < 0) { - pm_runtime_put_noidle(dev); + error = pm_runtime_resume_and_get(dev); + if (error) return error; - } disable_irq(keypad_data->irq); @@ -254,9 +250,10 @@ static void omap4_keypad_close(struct input_dev *input) struct device *dev = input->dev.parent; int error; - error = pm_runtime_get_sync(dev); - if (error < 0) - pm_runtime_put_noidle(dev); + error = pm_runtime_resume_and_get(dev); + if (error) + dev_err(dev, "%s: pm_runtime_resume_and_get() failed: %d\n", + __func__, error); disable_irq(keypad_data->irq); omap4_keypad_stop(keypad_data); @@ -392,10 +389,9 @@ static int omap4_keypad_probe(struct platform_device *pdev) * Enable clocks for the keypad module so that we can read * revision register. */ - error = pm_runtime_get_sync(dev); - if (error < 0) { - dev_err(dev, "pm_runtime_get_sync() failed\n"); - pm_runtime_put_noidle(dev); + error = pm_runtime_resume_and_get(dev); + if (error) { + dev_err(dev, "pm_runtime_resume_and_get() failed\n"); return error; } diff --git a/drivers/input/misc/iqs7222.c b/drivers/input/misc/iqs7222.c index 6b4138771a3f..b2e8097a2e6d 100644 --- a/drivers/input/misc/iqs7222.c +++ b/drivers/input/misc/iqs7222.c @@ -40,7 +40,6 @@ #define IQS7222_SLDR_SETUP_2_RES_MASK GENMASK(15, 8) #define IQS7222_SLDR_SETUP_2_RES_SHIFT 8 #define IQS7222_SLDR_SETUP_2_TOP_SPEED_MASK GENMASK(7, 0) -#define IQS7222_SLDR_SETUP_3_CHAN_SEL_MASK GENMASK(9, 0) #define IQS7222_GPIO_SETUP_0_GPIO_EN BIT(0) @@ -54,6 +53,9 @@ #define IQS7222_SYS_SETUP_ACK_RESET BIT(0) #define IQS7222_EVENT_MASK_ATI BIT(12) +#define IQS7222_EVENT_MASK_SLDR BIT(10) +#define IQS7222_EVENT_MASK_TOUCH BIT(1) +#define IQS7222_EVENT_MASK_PROX BIT(0) #define IQS7222_COMMS_HOLD BIT(0) #define IQS7222_COMMS_ERROR 0xEEEE @@ -92,11 +94,11 @@ enum iqs7222_reg_key_id { enum iqs7222_reg_grp_id { IQS7222_REG_GRP_STAT, + IQS7222_REG_GRP_FILT, IQS7222_REG_GRP_CYCLE, IQS7222_REG_GRP_GLBL, IQS7222_REG_GRP_BTN, IQS7222_REG_GRP_CHAN, - IQS7222_REG_GRP_FILT, IQS7222_REG_GRP_SLDR, IQS7222_REG_GRP_GPIO, IQS7222_REG_GRP_SYS, @@ -135,12 +137,12 @@ struct iqs7222_event_desc { static const struct iqs7222_event_desc iqs7222_kp_events[] = { { .name = "event-prox", - .enable = BIT(0), + .enable = IQS7222_EVENT_MASK_PROX, .reg_key = IQS7222_REG_KEY_PROX, }, { .name = "event-touch", - .enable = BIT(1), + .enable = IQS7222_EVENT_MASK_TOUCH, .reg_key = IQS7222_REG_KEY_TOUCH, }, }; @@ -556,13 +558,6 @@ static const struct iqs7222_prop_desc iqs7222_props[] = { .label = "current reference trim", }, { - .name = "azoteq,rf-filt-enable", - .reg_grp = IQS7222_REG_GRP_GLBL, - .reg_offset = 0, - .reg_shift = 15, - .reg_width = 1, - }, - { .name = "azoteq,max-counts", .reg_grp = IQS7222_REG_GRP_GLBL, .reg_offset = 0, @@ -1272,9 +1267,22 @@ static int iqs7222_ati_trigger(struct iqs7222_private *iqs7222) struct i2c_client *client = iqs7222->client; ktime_t ati_timeout; u16 sys_status = 0; - u16 sys_setup = iqs7222->sys_setup[0] & ~IQS7222_SYS_SETUP_ACK_RESET; + u16 sys_setup; int error, i; + /* + * The reserved fields of the system setup register may have changed + * as a result of other registers having been written. As such, read + * the register's latest value to avoid unexpected behavior when the + * register is written in the loop that follows. + */ + error = iqs7222_read_word(iqs7222, IQS7222_SYS_SETUP, &sys_setup); + if (error) + return error; + + sys_setup &= ~IQS7222_SYS_SETUP_INTF_MODE_MASK; + sys_setup &= ~IQS7222_SYS_SETUP_PWR_MODE_MASK; + for (i = 0; i < IQS7222_NUM_RETRIES; i++) { /* * Trigger ATI from streaming and normal-power modes so that @@ -1299,12 +1307,15 @@ static int iqs7222_ati_trigger(struct iqs7222_private *iqs7222) if (error) return error; - if (sys_status & IQS7222_SYS_STATUS_ATI_ACTIVE) - continue; + if (sys_status & IQS7222_SYS_STATUS_RESET) + return 0; if (sys_status & IQS7222_SYS_STATUS_ATI_ERROR) break; + if (sys_status & IQS7222_SYS_STATUS_ATI_ACTIVE) + continue; + /* * Use stream-in-touch mode if either slider reports * absolute position. @@ -1321,7 +1332,7 @@ static int iqs7222_ati_trigger(struct iqs7222_private *iqs7222) dev_err(&client->dev, "ATI attempt %d of %d failed with status 0x%02X, %s\n", i + 1, IQS7222_NUM_RETRIES, (u8)sys_status, - i < IQS7222_NUM_RETRIES ? "retrying..." : "stopping"); + i + 1 < IQS7222_NUM_RETRIES ? "retrying" : "stopping"); } return -ETIMEDOUT; @@ -1334,6 +1345,34 @@ static int iqs7222_dev_init(struct iqs7222_private *iqs7222, int dir) int error, i, j, k; /* + * Acknowledge reset before writing any registers in case the device + * suffers a spurious reset during initialization. Because this step + * may change the reserved fields of the second filter beta register, + * its cache must be updated. + * + * Writing the second filter beta register, in turn, may clobber the + * system status register. As such, the filter beta register pair is + * written first to protect against this hazard. + */ + if (dir == WRITE) { + u16 reg = dev_desc->reg_grps[IQS7222_REG_GRP_FILT].base + 1; + u16 filt_setup; + + error = iqs7222_write_word(iqs7222, IQS7222_SYS_SETUP, + iqs7222->sys_setup[0] | + IQS7222_SYS_SETUP_ACK_RESET); + if (error) + return error; + + error = iqs7222_read_word(iqs7222, reg, &filt_setup); + if (error) + return error; + + iqs7222->filt_setup[1] &= GENMASK(7, 0); + iqs7222->filt_setup[1] |= (filt_setup & ~GENMASK(7, 0)); + } + + /* * Take advantage of the stop-bit disable function, if available, to * save the trouble of having to reopen a communication window after * each burst read or write. @@ -1957,8 +1996,8 @@ static int iqs7222_parse_sldr(struct iqs7222_private *iqs7222, int sldr_index) int num_chan = dev_desc->reg_grps[IQS7222_REG_GRP_CHAN].num_row; int ext_chan = rounddown(num_chan, 10); int count, error, reg_offset, i; + u16 *event_mask = &iqs7222->sys_setup[dev_desc->event_offset]; u16 *sldr_setup = iqs7222->sldr_setup[sldr_index]; - u16 *sys_setup = iqs7222->sys_setup; unsigned int chan_sel[4], val; error = iqs7222_parse_props(iqs7222, &sldr_node, sldr_index, @@ -2003,7 +2042,7 @@ static int iqs7222_parse_sldr(struct iqs7222_private *iqs7222, int sldr_index) reg_offset = dev_desc->sldr_res < U16_MAX ? 0 : 1; sldr_setup[0] |= count; - sldr_setup[3 + reg_offset] &= ~IQS7222_SLDR_SETUP_3_CHAN_SEL_MASK; + sldr_setup[3 + reg_offset] &= ~GENMASK(ext_chan - 1, 0); for (i = 0; i < ARRAY_SIZE(chan_sel); i++) { sldr_setup[5 + reg_offset + i] = 0; @@ -2081,17 +2120,19 @@ static int iqs7222_parse_sldr(struct iqs7222_private *iqs7222, int sldr_index) sldr_setup[0] |= dev_desc->wheel_enable; } + /* + * The absence of a register offset makes it safe to assume the device + * supports gestures, each of which is first disabled until explicitly + * enabled. + */ + if (!reg_offset) + for (i = 0; i < ARRAY_SIZE(iqs7222_sl_events); i++) + sldr_setup[9] &= ~iqs7222_sl_events[i].enable; + for (i = 0; i < ARRAY_SIZE(iqs7222_sl_events); i++) { const char *event_name = iqs7222_sl_events[i].name; struct fwnode_handle *event_node; - /* - * The absence of a register offset means the remaining fields - * in the group represent gesture settings. - */ - if (iqs7222_sl_events[i].enable && !reg_offset) - sldr_setup[9] &= ~iqs7222_sl_events[i].enable; - event_node = fwnode_get_named_child_node(sldr_node, event_name); if (!event_node) continue; @@ -2104,6 +2145,22 @@ static int iqs7222_parse_sldr(struct iqs7222_private *iqs7222, int sldr_index) if (error) return error; + /* + * The press/release event does not expose a direct GPIO link, + * but one can be emulated by tying each of the participating + * channels to the same GPIO. + */ + error = iqs7222_gpio_select(iqs7222, event_node, + i ? iqs7222_sl_events[i].enable + : sldr_setup[3 + reg_offset], + i ? 1568 + sldr_index * 30 + : sldr_setup[4 + reg_offset]); + if (error) + return error; + + if (!reg_offset) + sldr_setup[9] |= iqs7222_sl_events[i].enable; + error = fwnode_property_read_u32(event_node, "linux,code", &val); if (error) { @@ -2115,26 +2172,20 @@ static int iqs7222_parse_sldr(struct iqs7222_private *iqs7222, int sldr_index) iqs7222->sl_code[sldr_index][i] = val; input_set_capability(iqs7222->keypad, EV_KEY, val); - /* - * The press/release event is determined based on whether the - * coordinate field reports 0xFFFF and has no explicit enable - * control. - */ - if (!iqs7222_sl_events[i].enable || reg_offset) - continue; - - sldr_setup[9] |= iqs7222_sl_events[i].enable; - - error = iqs7222_gpio_select(iqs7222, event_node, - iqs7222_sl_events[i].enable, - 1568 + sldr_index * 30); - if (error) - return error; - if (!dev_desc->event_offset) continue; - sys_setup[dev_desc->event_offset] |= BIT(10 + sldr_index); + /* + * The press/release event is determined based on whether the + * coordinate field reports 0xFFFF and solely relies on touch + * or proximity interrupts to be unmasked. + */ + if (i && !reg_offset) + *event_mask |= (IQS7222_EVENT_MASK_SLDR << sldr_index); + else if (sldr_setup[4 + reg_offset] == dev_desc->touch_link) + *event_mask |= IQS7222_EVENT_MASK_TOUCH; + else + *event_mask |= IQS7222_EVENT_MASK_PROX; } /* @@ -2227,11 +2278,6 @@ static int iqs7222_parse_all(struct iqs7222_private *iqs7222) return error; } - sys_setup[0] &= ~IQS7222_SYS_SETUP_INTF_MODE_MASK; - sys_setup[0] &= ~IQS7222_SYS_SETUP_PWR_MODE_MASK; - - sys_setup[0] |= IQS7222_SYS_SETUP_ACK_RESET; - return iqs7222_parse_props(iqs7222, NULL, 0, IQS7222_REG_GRP_SYS, IQS7222_REG_KEY_NONE); } @@ -2299,29 +2345,37 @@ static int iqs7222_report(struct iqs7222_private *iqs7222) input_report_abs(iqs7222->keypad, iqs7222->sl_axis[i], sldr_pos); - for (j = 0; j < ARRAY_SIZE(iqs7222_sl_events); j++) { - u16 mask = iqs7222_sl_events[j].mask; - u16 val = iqs7222_sl_events[j].val; + input_report_key(iqs7222->keypad, iqs7222->sl_code[i][0], + sldr_pos < dev_desc->sldr_res); - if (!iqs7222_sl_events[j].enable) { - input_report_key(iqs7222->keypad, - iqs7222->sl_code[i][j], - sldr_pos < dev_desc->sldr_res); - continue; - } + /* + * A maximum resolution indicates the device does not support + * gestures, in which case the remaining fields are ignored. + */ + if (dev_desc->sldr_res == U16_MAX) + continue; - /* - * The remaining offsets represent gesture state, and - * are discarded in the case of IQS7222C because only - * absolute position is reported. - */ - if (num_stat < IQS7222_MAX_COLS_STAT) - continue; + if (!(le16_to_cpu(status[1]) & IQS7222_EVENT_MASK_SLDR << i)) + continue; + + /* + * Skip the press/release event, as it does not have separate + * status fields and is handled separately. + */ + for (j = 1; j < ARRAY_SIZE(iqs7222_sl_events); j++) { + u16 mask = iqs7222_sl_events[j].mask; + u16 val = iqs7222_sl_events[j].val; input_report_key(iqs7222->keypad, iqs7222->sl_code[i][j], (state & mask) == val); } + + input_sync(iqs7222->keypad); + + for (j = 1; j < ARRAY_SIZE(iqs7222_sl_events); j++) + input_report_key(iqs7222->keypad, + iqs7222->sl_code[i][j], 0); } input_sync(iqs7222->keypad); diff --git a/drivers/input/mouse/cyapa_gen6.c b/drivers/input/mouse/cyapa_gen6.c index 812edfced86e..0caaf3e64215 100644 --- a/drivers/input/mouse/cyapa_gen6.c +++ b/drivers/input/mouse/cyapa_gen6.c @@ -57,7 +57,7 @@ struct pip_app_resp_head { * The value of data_status can be the first byte of data or * the command status or the unsupported command code depending on the * requested command code. - */ + */ u8 data_status; } __packed; diff --git a/drivers/input/mouse/gpio_mouse.c b/drivers/input/mouse/gpio_mouse.c index 23507fce3a2b..18ccbd45004a 100644 --- a/drivers/input/mouse/gpio_mouse.c +++ b/drivers/input/mouse/gpio_mouse.c @@ -41,7 +41,7 @@ struct gpio_mouse { /* * Timer function which is run every scan_ms ms when the device is opened. - * The dev input variable is set to the the input_dev pointer. + * The dev input variable is set to the input_dev pointer. */ static void gpio_mouse_scan(struct input_dev *input) { diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index 148a7c5fd0e2..4fbec7bbecca 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -67,25 +67,84 @@ static inline void i8042_write_command(int val) #include <linux/dmi.h> -static const struct dmi_system_id __initconst i8042_dmi_noloop_table[] = { +#define SERIO_QUIRK_NOKBD BIT(0) +#define SERIO_QUIRK_NOAUX BIT(1) +#define SERIO_QUIRK_NOMUX BIT(2) +#define SERIO_QUIRK_FORCEMUX BIT(3) +#define SERIO_QUIRK_UNLOCK BIT(4) +#define SERIO_QUIRK_PROBE_DEFER BIT(5) +#define SERIO_QUIRK_RESET_ALWAYS BIT(6) +#define SERIO_QUIRK_RESET_NEVER BIT(7) +#define SERIO_QUIRK_DIECT BIT(8) +#define SERIO_QUIRK_DUMBKBD BIT(9) +#define SERIO_QUIRK_NOLOOP BIT(10) +#define SERIO_QUIRK_NOTIMEOUT BIT(11) +#define SERIO_QUIRK_KBDRESET BIT(12) +#define SERIO_QUIRK_DRITEK BIT(13) +#define SERIO_QUIRK_NOPNP BIT(14) + +/* Quirk table for different mainboards. Options similar or identical to i8042 + * module parameters. + * ORDERING IS IMPORTANT! The first match will be apllied and the rest ignored. + * This allows entries to overwrite vendor wide quirks on a per device basis. + * Where this is irrelevant, entries are sorted case sensitive by DMI_SYS_VENDOR + * and/or DMI_BOARD_VENDOR to make it easier to avoid dublicate entries. + */ +static const struct dmi_system_id i8042_dmi_quirk_table[] __initconst = { { - /* - * Arima-Rioworks HDAMB - - * AUX LOOP command does not raise AUX IRQ - */ .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "RIOWORKS"), - DMI_MATCH(DMI_BOARD_NAME, "HDAMB"), - DMI_MATCH(DMI_BOARD_VERSION, "Rev E"), + DMI_MATCH(DMI_SYS_VENDOR, "ALIENWARE"), + DMI_MATCH(DMI_PRODUCT_NAME, "Sentia"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* ASUS G1S */ .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc."), - DMI_MATCH(DMI_BOARD_NAME, "G1S"), - DMI_MATCH(DMI_BOARD_VERSION, "1.0"), + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X750LN"), + }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) + }, + { + /* Asus X450LCP */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X450LCP"), + }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_NEVER) + }, + { + /* ASUS ZenBook UX425UA */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX425UA"), }, + .driver_data = (void *)(SERIO_QUIRK_PROBE_DEFER | SERIO_QUIRK_RESET_NEVER) + }, + { + /* ASUS ZenBook UM325UA */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX325UA_UM325UA"), + }, + .driver_data = (void *)(SERIO_QUIRK_PROBE_DEFER | SERIO_QUIRK_RESET_NEVER) + }, + /* + * On some Asus laptops, just running self tests cause problems. + */ + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */ + }, + .driver_data = (void *)(SERIO_QUIRK_RESET_NEVER) + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_CHASSIS_TYPE, "31"), /* Convertible Notebook */ + }, + .driver_data = (void *)(SERIO_QUIRK_RESET_NEVER) }, { /* ASUS P65UP5 - AUX LOOP command does not raise AUX IRQ */ @@ -94,585 +153,689 @@ static const struct dmi_system_id __initconst i8042_dmi_noloop_table[] = { DMI_MATCH(DMI_BOARD_NAME, "P/I-P65UP5"), DMI_MATCH(DMI_BOARD_VERSION, "REV 2.X"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { + /* ASUS G1S */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "X750LN"), + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_BOARD_NAME, "G1S"), + DMI_MATCH(DMI_BOARD_VERSION, "1.0"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Compaq"), - DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"), - DMI_MATCH(DMI_PRODUCT_VERSION, "8500"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1360"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { + /* Acer Aspire 5710 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Compaq"), - DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"), - DMI_MATCH(DMI_PRODUCT_VERSION, "DL760"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5710"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Dell Embedded Box PC 3000 */ + /* Acer Aspire 7738 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Embedded Box PC 3000"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7738"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* OQO Model 01 */ + /* Acer Aspire 5536 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "OQO"), - DMI_MATCH(DMI_PRODUCT_NAME, "ZEPTO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "00"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5536"), + DMI_MATCH(DMI_PRODUCT_VERSION, "0100"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* ULI EV4873 - AUX LOOP does not work properly */ + /* + * Acer Aspire 5738z + * Touchpad stops working in mux mode when dis- + re-enabled + * with the touchpad enable/disable toggle hotkey + */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ULI"), - DMI_MATCH(DMI_PRODUCT_NAME, "EV4873"), - DMI_MATCH(DMI_PRODUCT_VERSION, "5a"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5738"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Microsoft Virtual Machine */ + /* Acer Aspire One 150 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), - DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"), - DMI_MATCH(DMI_PRODUCT_VERSION, "VS2005R2"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "AOA150"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* Medion MAM 2070 */ + /* Acer Aspire One 532h */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Notebook"), - DMI_MATCH(DMI_PRODUCT_NAME, "MAM 2070"), - DMI_MATCH(DMI_PRODUCT_VERSION, "5a"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "AO532h"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, { - /* Medion Akoya E7225 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Medion"), - DMI_MATCH(DMI_PRODUCT_NAME, "Akoya E7225"), - DMI_MATCH(DMI_PRODUCT_VERSION, "1.0"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire A114-31"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* Blue FB5601 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "blue"), - DMI_MATCH(DMI_PRODUCT_NAME, "FB5601"), - DMI_MATCH(DMI_PRODUCT_VERSION, "M606"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire A314-31"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* Gigabyte M912 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), - DMI_MATCH(DMI_PRODUCT_NAME, "M912"), - DMI_MATCH(DMI_PRODUCT_VERSION, "01"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire A315-31"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* Gigabyte M1022M netbook */ .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co.,Ltd."), - DMI_MATCH(DMI_BOARD_NAME, "M1022E"), - DMI_MATCH(DMI_BOARD_VERSION, "1.02"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire ES1-132"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* Gigabyte Spring Peak - defines wrong chassis type */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), - DMI_MATCH(DMI_PRODUCT_NAME, "Spring Peak"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire ES1-332"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* Gigabyte T1005 - defines wrong chassis type ("Other") */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), - DMI_MATCH(DMI_PRODUCT_NAME, "T1005"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire ES1-432"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* Gigabyte T1005M/P - defines wrong chassis type ("Other") */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), - DMI_MATCH(DMI_PRODUCT_NAME, "T1005M/P"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate Spin B118-RN"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, + /* + * Some Wistron based laptops need us to explicitly enable the 'Dritek + * keyboard extension' to make their extra keys start generating scancodes. + * Originally, this was just confined to older laptops, but a few Acer laptops + * have turned up in 2007 that also need this again. + */ { + /* Acer Aspire 5100 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv9700"), - DMI_MATCH(DMI_PRODUCT_VERSION, "Rev 1"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, { + /* Acer Aspire 5610 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "PEGATRON CORPORATION"), - DMI_MATCH(DMI_PRODUCT_NAME, "C15B"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5610"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, { + /* Acer Aspire 5630 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ByteSpeed LLC"), - DMI_MATCH(DMI_PRODUCT_NAME, "ByteSpeed Laptop C15B"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5630"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, - { } -}; - -/* - * Some Fujitsu notebooks are having trouble with touchpads if - * active multiplexing mode is activated. Luckily they don't have - * external PS/2 ports so we can safely disable it. - * ... apparently some Toshibas don't like MUX mode either and - * die horrible death on reboot. - */ -static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = { { - /* Fujitsu Lifebook P7010/P7010D */ + /* Acer Aspire 5650 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "P7010"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5650"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, { - /* Fujitsu Lifebook P7010 */ + /* Acer Aspire 5680 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), - DMI_MATCH(DMI_PRODUCT_NAME, "0000000000"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5680"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, { - /* Fujitsu Lifebook P5020D */ + /* Acer Aspire 5720 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P Series"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5720"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, { - /* Fujitsu Lifebook S2000 */ + /* Acer Aspire 9110 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S Series"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 9110"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, { - /* Fujitsu Lifebook S6230 */ + /* Acer TravelMate 660 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S6230"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 660"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, { - /* Fujitsu Lifebook T725 laptop */ + /* Acer TravelMate 2490 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T725"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2490"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, { - /* Fujitsu Lifebook U745 */ + /* Acer TravelMate 4280 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U745"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4280"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, { - /* Fujitsu T70H */ + /* Amoi M636/A737 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "FMVLT70H"), + DMI_MATCH(DMI_SYS_VENDOR, "Amoi Electronics CO.,LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "M636/A737 platform"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Fujitsu-Siemens Lifebook T3010 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), - DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T3010"), + DMI_MATCH(DMI_SYS_VENDOR, "ByteSpeed LLC"), + DMI_MATCH(DMI_PRODUCT_NAME, "ByteSpeed Laptop C15B"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* Fujitsu-Siemens Lifebook E4010 */ + /* Compal HEL80I */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), - DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E4010"), + DMI_MATCH(DMI_SYS_VENDOR, "COMPAL"), + DMI_MATCH(DMI_PRODUCT_NAME, "HEL80I"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Fujitsu-Siemens Amilo Pro 2010 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), - DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro V2010"), + DMI_MATCH(DMI_SYS_VENDOR, "Compaq"), + DMI_MATCH(DMI_PRODUCT_NAME, "ProLiant"), + DMI_MATCH(DMI_PRODUCT_VERSION, "8500"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* Fujitsu-Siemens Amilo Pro 2030 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), - DMI_MATCH(DMI_PRODUCT_NAME, "AMILO PRO V2030"), + DMI_MATCH(DMI_SYS_VENDOR, "Compaq"), + DMI_MATCH(DMI_PRODUCT_NAME, "ProLiant"), + DMI_MATCH(DMI_PRODUCT_VERSION, "DL760"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* - * No data is coming from the touchscreen unless KBC - * is in legacy mode. - */ - /* Panasonic CF-29 */ + /* Advent 4211 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"), - DMI_MATCH(DMI_PRODUCT_NAME, "CF-29"), + DMI_MATCH(DMI_SYS_VENDOR, "DIXONSXP"), + DMI_MATCH(DMI_PRODUCT_NAME, "Advent 4211"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* - * HP Pavilion DV4017EA - - * errors on MUX ports are reported without raising AUXDATA - * causing "spurious NAK" messages. - */ + /* Dell Embedded Box PC 3000 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EA032EA#ABF)"), + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Embedded Box PC 3000"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* - * HP Pavilion ZT1000 - - * like DV4017EA does not raise AUXERR for errors on MUX ports. - */ + /* Dell XPS M1530 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion Notebook PC"), - DMI_MATCH(DMI_PRODUCT_VERSION, "HP Pavilion Notebook ZT1000"), + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "XPS M1530"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* - * HP Pavilion DV4270ca - - * like DV4017EA does not raise AUXERR for errors on MUX ports. - */ + /* Dell Vostro 1510 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EH476UA#ABL)"), + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro1510"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { + /* Dell Vostro V13 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), - DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P10"), + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V13"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_NOTIMEOUT) }, { + /* Dell Vostro 1320 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), - DMI_MATCH(DMI_PRODUCT_NAME, "EQUIUM A110"), + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1320"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { + /* Dell Vostro 1520 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), - DMI_MATCH(DMI_PRODUCT_NAME, "SATELLITE C850D"), + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1520"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { + /* Dell Vostro 1720 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ALIENWARE"), - DMI_MATCH(DMI_PRODUCT_NAME, "Sentia"), + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1720"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* Sharp Actius MM20 */ + /* Entroware Proteus */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "SHARP"), - DMI_MATCH(DMI_PRODUCT_NAME, "PC-MM20 Series"), + DMI_MATCH(DMI_SYS_VENDOR, "Entroware"), + DMI_MATCH(DMI_PRODUCT_NAME, "Proteus"), + DMI_MATCH(DMI_PRODUCT_VERSION, "EL07R4"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS) }, + /* + * Some Fujitsu notebooks are having trouble with touchpads if + * active multiplexing mode is activated. Luckily they don't have + * external PS/2 ports so we can safely disable it. + * ... apparently some Toshibas don't like MUX mode either and + * die horrible death on reboot. + */ { - /* Sony Vaio FS-115b */ + /* Fujitsu Lifebook P7010/P7010D */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), - DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FS115B"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "P7010"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* - * Sony Vaio FZ-240E - - * reset and GET ID commands issued via KBD port are - * sometimes being delivered to AUX3. - */ + /* Fujitsu Lifebook P5020D */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), - DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FZ240E"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P Series"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* - * Most (all?) VAIOs do not have external PS/2 ports nor - * they implement active multiplexing properly, and - * MUX discovery usually messes up keyboard/touchpad. - */ + /* Fujitsu Lifebook S2000 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), - DMI_MATCH(DMI_BOARD_NAME, "VAIO"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S Series"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Amoi M636/A737 */ + /* Fujitsu Lifebook S6230 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Amoi Electronics CO.,LTD."), - DMI_MATCH(DMI_PRODUCT_NAME, "M636/A737 platform"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S6230"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Lenovo 3000 n100 */ + /* Fujitsu Lifebook T725 laptop */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_NAME, "076804U"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T725"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_NOTIMEOUT) }, { - /* Lenovo XiaoXin Air 12 */ + /* Fujitsu Lifebook U745 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_NAME, "80UN"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U745"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { + /* Fujitsu T70H */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1360"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "FMVLT70H"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Acer Aspire 5710 */ + /* Fujitsu A544 laptop */ + /* https://bugzilla.redhat.com/show_bug.cgi?id=1111138 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5710"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK A544"), }, + .driver_data = (void *)(SERIO_QUIRK_NOTIMEOUT) }, { - /* Acer Aspire 7738 */ + /* Fujitsu AH544 laptop */ + /* https://bugzilla.kernel.org/show_bug.cgi?id=69731 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7738"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK AH544"), }, + .driver_data = (void *)(SERIO_QUIRK_NOTIMEOUT) }, { - /* Gericom Bellagio */ + /* Fujitsu U574 laptop */ + /* https://bugzilla.kernel.org/show_bug.cgi?id=69731 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Gericom"), - DMI_MATCH(DMI_PRODUCT_NAME, "N34AS6"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U574"), }, + .driver_data = (void *)(SERIO_QUIRK_NOTIMEOUT) }, { - /* IBM 2656 */ + /* Fujitsu UH554 laptop */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "IBM"), - DMI_MATCH(DMI_PRODUCT_NAME, "2656"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK UH544"), }, + .driver_data = (void *)(SERIO_QUIRK_NOTIMEOUT) }, { - /* Dell XPS M1530 */ + /* Fujitsu Lifebook P7010 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "XPS M1530"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "0000000000"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Compal HEL80I */ + /* Fujitsu-Siemens Lifebook T3010 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "COMPAL"), - DMI_MATCH(DMI_PRODUCT_NAME, "HEL80I"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T3010"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Dell Vostro 1510 */ + /* Fujitsu-Siemens Lifebook E4010 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Vostro1510"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E4010"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Acer Aspire 5536 */ + /* Fujitsu-Siemens Amilo Pro 2010 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5536"), - DMI_MATCH(DMI_PRODUCT_VERSION, "0100"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro V2010"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Dell Vostro V13 */ + /* Fujitsu-Siemens Amilo Pro 2030 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V13"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO PRO V2030"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Newer HP Pavilion dv4 models */ + /* Gigabyte M912 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv4 Notebook PC"), + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "M912"), + DMI_MATCH(DMI_PRODUCT_VERSION, "01"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* Asus X450LCP */ + /* Gigabyte Spring Peak - defines wrong chassis type */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "X450LCP"), + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "Spring Peak"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* Avatar AVIU-145A6 */ + /* Gigabyte T1005 - defines wrong chassis type ("Other") */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Intel"), - DMI_MATCH(DMI_PRODUCT_NAME, "IC4I"), + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "T1005"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* TUXEDO BU1406 */ + /* Gigabyte T1005M/P - defines wrong chassis type ("Other") */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Notebook"), - DMI_MATCH(DMI_PRODUCT_NAME, "N24_25BU"), + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "T1005M/P"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, + /* + * Some laptops need keyboard reset before probing for the trackpad to get + * it detected, initialised & finally work. + */ { - /* Lenovo LaVie Z */ + /* Gigabyte P35 v2 - Elantech touchpad */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo LaVie Z"), + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "P35V2"), }, + .driver_data = (void *)(SERIO_QUIRK_KBDRESET) + }, + { + /* Aorus branded Gigabyte X3 Plus - Elantech touchpad */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "X3"), + }, + .driver_data = (void *)(SERIO_QUIRK_KBDRESET) }, { - /* - * Acer Aspire 5738z - * Touchpad stops working in mux mode when dis- + re-enabled - * with the touchpad enable/disable toggle hotkey - */ + /* Gigabyte P34 - Elantech touchpad */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5738"), + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "P34"), }, + .driver_data = (void *)(SERIO_QUIRK_KBDRESET) }, { - /* Entroware Proteus */ + /* Gigabyte P57 - Elantech touchpad */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Entroware"), - DMI_MATCH(DMI_PRODUCT_NAME, "Proteus"), - DMI_MATCH(DMI_PRODUCT_VERSION, "EL07R4"), + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "P57"), }, + .driver_data = (void *)(SERIO_QUIRK_KBDRESET) + }, + { + /* Gericom Bellagio */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Gericom"), + DMI_MATCH(DMI_PRODUCT_NAME, "N34AS6"), + }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) + }, + { + /* Gigabyte M1022M netbook */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co.,Ltd."), + DMI_MATCH(DMI_BOARD_NAME, "M1022E"), + DMI_MATCH(DMI_BOARD_VERSION, "1.02"), + }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv9700"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Rev 1"), + }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, - { } -}; - -static const struct dmi_system_id i8042_dmi_forcemux_table[] __initconst = { { /* - * Sony Vaio VGN-CS series require MUX or the touch sensor - * buttons will disturb touchpad operation + * HP Pavilion DV4017EA - + * errors on MUX ports are reported without raising AUXDATA + * causing "spurious NAK" messages. */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), - DMI_MATCH(DMI_PRODUCT_NAME, "VGN-CS"), + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EA032EA#ABF)"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, - { } -}; - -/* - * On some Asus laptops, just running self tests cause problems. - */ -static const struct dmi_system_id i8042_dmi_noselftest_table[] = { { + /* + * HP Pavilion ZT1000 - + * like DV4017EA does not raise AUXERR for errors on MUX ports. + */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */ + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion Notebook PC"), + DMI_MATCH(DMI_PRODUCT_VERSION, "HP Pavilion Notebook ZT1000"), }, - }, { + .driver_data = (void *)(SERIO_QUIRK_NOMUX) + }, + { + /* + * HP Pavilion DV4270ca - + * like DV4017EA does not raise AUXERR for errors on MUX ports. + */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_CHASSIS_TYPE, "31"), /* Convertible Notebook */ + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EH476UA#ABL)"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, - { } -}; -static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = { { - /* MSI Wind U-100 */ + /* Newer HP Pavilion dv4 models */ .matches = { - DMI_MATCH(DMI_BOARD_NAME, "U-100"), - DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"), + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv4 Notebook PC"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_NOTIMEOUT) }, { - /* LG Electronics X110 */ + /* IBM 2656 */ .matches = { - DMI_MATCH(DMI_BOARD_NAME, "X110"), - DMI_MATCH(DMI_BOARD_VENDOR, "LG Electronics Inc."), + DMI_MATCH(DMI_SYS_VENDOR, "IBM"), + DMI_MATCH(DMI_PRODUCT_NAME, "2656"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Acer Aspire One 150 */ + /* Avatar AVIU-145A6 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "AOA150"), + DMI_MATCH(DMI_SYS_VENDOR, "Intel"), + DMI_MATCH(DMI_PRODUCT_NAME, "IC4I"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { + /* Intel MBO Desktop D845PESV */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire A114-31"), + DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "D845PESV"), }, + .driver_data = (void *)(SERIO_QUIRK_NOPNP) }, { + /* + * Intel NUC D54250WYK - does not have i8042 controller but + * declares PS/2 devices in DSDT. + */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire A314-31"), + DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "D54250WYK"), }, + .driver_data = (void *)(SERIO_QUIRK_NOPNP) }, { + /* Lenovo 3000 n100 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire A315-31"), + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "076804U"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { + /* Lenovo XiaoXin Air 12 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire ES1-132"), + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "80UN"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { + /* Lenovo LaVie Z */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire ES1-332"), + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo LaVie Z"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { + /* Lenovo Ideapad U455 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire ES1-432"), + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "20046"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { + /* Lenovo ThinkPad L460 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate Spin B118-RN"), + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad L460"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* Advent 4211 */ + /* Lenovo ThinkPad Twist S230u */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "DIXONSXP"), - DMI_MATCH(DMI_PRODUCT_NAME, "Advent 4211"), + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "33474HU"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) + }, + { + /* LG Electronics X110 */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LG Electronics Inc."), + DMI_MATCH(DMI_BOARD_NAME, "X110"), + }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { /* Medion Akoya Mini E1210 */ @@ -680,6 +843,7 @@ static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "MEDION"), DMI_MATCH(DMI_PRODUCT_NAME, "E1210"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { /* Medion Akoya E1222 */ @@ -687,331 +851,434 @@ static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "MEDION"), DMI_MATCH(DMI_PRODUCT_NAME, "E122X"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* Mivvy M310 */ + /* MSI Wind U-100 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "VIOOO"), - DMI_MATCH(DMI_PRODUCT_NAME, "N10"), + DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"), + DMI_MATCH(DMI_BOARD_NAME, "U-100"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS | SERIO_QUIRK_NOPNP) }, { - /* Dell Vostro 1320 */ + /* + * No data is coming from the touchscreen unless KBC + * is in legacy mode. + */ + /* Panasonic CF-29 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1320"), + DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"), + DMI_MATCH(DMI_PRODUCT_NAME, "CF-29"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Dell Vostro 1520 */ + /* Medion Akoya E7225 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1520"), + DMI_MATCH(DMI_SYS_VENDOR, "Medion"), + DMI_MATCH(DMI_PRODUCT_NAME, "Akoya E7225"), + DMI_MATCH(DMI_PRODUCT_VERSION, "1.0"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* Dell Vostro 1720 */ + /* Microsoft Virtual Machine */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1720"), + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"), + DMI_MATCH(DMI_PRODUCT_VERSION, "VS2005R2"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* Lenovo Ideapad U455 */ + /* Medion MAM 2070 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_NAME, "20046"), + DMI_MATCH(DMI_SYS_VENDOR, "Notebook"), + DMI_MATCH(DMI_PRODUCT_NAME, "MAM 2070"), + DMI_MATCH(DMI_PRODUCT_VERSION, "5a"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* Lenovo ThinkPad L460 */ + /* TUXEDO BU1406 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad L460"), + DMI_MATCH(DMI_SYS_VENDOR, "Notebook"), + DMI_MATCH(DMI_PRODUCT_NAME, "N24_25BU"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Clevo P650RS, 650RP6, Sager NP8152-S, and others */ + /* OQO Model 01 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Notebook"), - DMI_MATCH(DMI_PRODUCT_NAME, "P65xRP"), + DMI_MATCH(DMI_SYS_VENDOR, "OQO"), + DMI_MATCH(DMI_PRODUCT_NAME, "ZEPTO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "00"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* Lenovo ThinkPad Twist S230u */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_NAME, "33474HU"), + DMI_MATCH(DMI_SYS_VENDOR, "PEGATRON CORPORATION"), + DMI_MATCH(DMI_PRODUCT_NAME, "C15B"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* Entroware Proteus */ + /* Acer Aspire 5 A515 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Entroware"), - DMI_MATCH(DMI_PRODUCT_NAME, "Proteus"), - DMI_MATCH(DMI_PRODUCT_VERSION, "EL07R4"), + DMI_MATCH(DMI_BOARD_VENDOR, "PK"), + DMI_MATCH(DMI_BOARD_NAME, "Grumpy_PK"), }, + .driver_data = (void *)(SERIO_QUIRK_NOPNP) }, - { } -}; - -#ifdef CONFIG_PNP -static const struct dmi_system_id __initconst i8042_dmi_nopnp_table[] = { { - /* Intel MBO Desktop D845PESV */ + /* ULI EV4873 - AUX LOOP does not work properly */ .matches = { - DMI_MATCH(DMI_BOARD_NAME, "D845PESV"), - DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_SYS_VENDOR, "ULI"), + DMI_MATCH(DMI_PRODUCT_NAME, "EV4873"), + DMI_MATCH(DMI_PRODUCT_VERSION, "5a"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { /* - * Intel NUC D54250WYK - does not have i8042 controller but - * declares PS/2 devices in DSDT. + * Arima-Rioworks HDAMB - + * AUX LOOP command does not raise AUX IRQ */ .matches = { - DMI_MATCH(DMI_BOARD_NAME, "D54250WYK"), - DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_BOARD_VENDOR, "RIOWORKS"), + DMI_MATCH(DMI_BOARD_NAME, "HDAMB"), + DMI_MATCH(DMI_BOARD_VERSION, "Rev E"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* MSI Wind U-100 */ + /* Sharp Actius MM20 */ .matches = { - DMI_MATCH(DMI_BOARD_NAME, "U-100"), - DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"), + DMI_MATCH(DMI_SYS_VENDOR, "SHARP"), + DMI_MATCH(DMI_PRODUCT_NAME, "PC-MM20 Series"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Acer Aspire 5 A515 */ + /* + * Sony Vaio FZ-240E - + * reset and GET ID commands issued via KBD port are + * sometimes being delivered to AUX3. + */ .matches = { - DMI_MATCH(DMI_BOARD_NAME, "Grumpy_PK"), - DMI_MATCH(DMI_BOARD_VENDOR, "PK"), + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FZ240E"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) + }, + { + /* + * Most (all?) VAIOs do not have external PS/2 ports nor + * they implement active multiplexing properly, and + * MUX discovery usually messes up keyboard/touchpad. + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "VAIO"), + }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, - { } -}; - -static const struct dmi_system_id __initconst i8042_dmi_laptop_table[] = { { + /* Sony Vaio FS-115b */ .matches = { - DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */ + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FS115B"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { + /* + * Sony Vaio VGN-CS series require MUX or the touch sensor + * buttons will disturb touchpad operation + */ .matches = { - DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /* Laptop */ + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-CS"), }, + .driver_data = (void *)(SERIO_QUIRK_FORCEMUX) }, { .matches = { - DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */ + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P10"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { .matches = { - DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */ + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "EQUIUM A110"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, - { } -}; -#endif - -static const struct dmi_system_id __initconst i8042_dmi_notimeout_table[] = { { - /* Dell Vostro V13 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V13"), + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "SATELLITE C850D"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, + /* + * A lot of modern Clevo barebones have touchpad and/or keyboard issues + * after suspend fixable with nomux + reset + noloop + nopnp. Luckily, + * none of them have an external PS/2 port so this can safely be set for + * all of them. These two are based on a Clevo design, but have the + * board_name changed. + */ { - /* Newer HP Pavilion dv4 models */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv4 Notebook PC"), + DMI_MATCH(DMI_BOARD_VENDOR, "TUXEDO"), + DMI_MATCH(DMI_BOARD_NAME, "AURA1501"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Fujitsu A544 laptop */ - /* https://bugzilla.redhat.com/show_bug.cgi?id=1111138 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK A544"), + DMI_MATCH(DMI_BOARD_VENDOR, "TUXEDO"), + DMI_MATCH(DMI_BOARD_NAME, "EDUBOOK1502"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Fujitsu AH544 laptop */ - /* https://bugzilla.kernel.org/show_bug.cgi?id=69731 */ + /* Mivvy M310 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK AH544"), + DMI_MATCH(DMI_SYS_VENDOR, "VIOOO"), + DMI_MATCH(DMI_PRODUCT_NAME, "N10"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, + /* + * Some laptops need keyboard reset before probing for the trackpad to get + * it detected, initialised & finally work. + */ { - /* Fujitsu Lifebook T725 laptop */ + /* Schenker XMG C504 - Elantech touchpad */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T725"), + DMI_MATCH(DMI_SYS_VENDOR, "XMG"), + DMI_MATCH(DMI_PRODUCT_NAME, "C504"), }, + .driver_data = (void *)(SERIO_QUIRK_KBDRESET) }, { - /* Fujitsu U574 laptop */ - /* https://bugzilla.kernel.org/show_bug.cgi?id=69731 */ + /* Blue FB5601 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U574"), + DMI_MATCH(DMI_SYS_VENDOR, "blue"), + DMI_MATCH(DMI_PRODUCT_NAME, "FB5601"), + DMI_MATCH(DMI_PRODUCT_VERSION, "M606"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, + /* + * A lot of modern Clevo barebones have touchpad and/or keyboard issues + * after suspend fixable with nomux + reset + noloop + nopnp. Luckily, + * none of them have an external PS/2 port so this can safely be set for + * all of them. + * Clevo barebones come with board_vendor and/or system_vendor set to + * either the very generic string "Notebook" and/or a different value + * for each individual reseller. The only somewhat universal way to + * identify them is by board_name. + */ { - /* Fujitsu UH554 laptop */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK UH544"), + DMI_MATCH(DMI_BOARD_NAME, "LAPQC71A"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, - { } -}; - -/* - * Some Wistron based laptops need us to explicitly enable the 'Dritek - * keyboard extension' to make their extra keys start generating scancodes. - * Originally, this was just confined to older laptops, but a few Acer laptops - * have turned up in 2007 that also need this again. - */ -static const struct dmi_system_id __initconst i8042_dmi_dritek_table[] = { { - /* Acer Aspire 5100 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"), + DMI_MATCH(DMI_BOARD_NAME, "LAPQC71B"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Acer Aspire 5610 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5610"), + DMI_MATCH(DMI_BOARD_NAME, "N140CU"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Acer Aspire 5630 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5630"), + DMI_MATCH(DMI_BOARD_NAME, "N141CU"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Acer Aspire 5650 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5650"), + DMI_MATCH(DMI_BOARD_NAME, "NH5xAx"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Acer Aspire 5680 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5680"), + DMI_MATCH(DMI_BOARD_NAME, "NL5xRU"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, + /* + * At least one modern Clevo barebone has the touchpad connected both + * via PS/2 and i2c interface. This causes a race condition between the + * psmouse and i2c-hid driver. Since the full capability of the touchpad + * is available via the i2c interface and the device has no external + * PS/2 port, it is safe to just ignore all ps2 mouses here to avoid + * this issue. The known affected device is the + * TUXEDO InfinityBook S17 Gen6 / Clevo NS70MU which comes with one of + * the two different dmi strings below. NS50MU is not a typo! + */ { - /* Acer Aspire 5720 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5720"), + DMI_MATCH(DMI_BOARD_NAME, "NS50MU"), }, + .driver_data = (void *)(SERIO_QUIRK_NOAUX | SERIO_QUIRK_NOMUX | + SERIO_QUIRK_RESET_ALWAYS | SERIO_QUIRK_NOLOOP | + SERIO_QUIRK_NOPNP) }, { - /* Acer Aspire 9110 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 9110"), + DMI_MATCH(DMI_BOARD_NAME, "NS50_70MU"), }, + .driver_data = (void *)(SERIO_QUIRK_NOAUX | SERIO_QUIRK_NOMUX | + SERIO_QUIRK_RESET_ALWAYS | SERIO_QUIRK_NOLOOP | + SERIO_QUIRK_NOPNP) }, { - /* Acer TravelMate 660 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 660"), + DMI_MATCH(DMI_BOARD_NAME, "NJ50_70CU"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Acer TravelMate 2490 */ + /* + * This is only a partial board_name and might be followed by + * another letter or number. DMI_MATCH however does do partial + * matching. + */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2490"), + DMI_MATCH(DMI_PRODUCT_NAME, "P65xH"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Acer TravelMate 4280 */ + /* Clevo P650RS, 650RP6, Sager NP8152-S, and others */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4280"), + DMI_MATCH(DMI_PRODUCT_NAME, "P65xRP"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, - { } -}; - -/* - * Some laptops need keyboard reset before probing for the trackpad to get - * it detected, initialised & finally work. - */ -static const struct dmi_system_id __initconst i8042_dmi_kbdreset_table[] = { { - /* Gigabyte P35 v2 - Elantech touchpad */ + /* + * This is only a partial board_name and might be followed by + * another letter or number. DMI_MATCH however does do partial + * matching. + */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), - DMI_MATCH(DMI_PRODUCT_NAME, "P35V2"), + DMI_MATCH(DMI_PRODUCT_NAME, "P65_P67H"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, - { - /* Aorus branded Gigabyte X3 Plus - Elantech touchpad */ + { + /* + * This is only a partial board_name and might be followed by + * another letter or number. DMI_MATCH however does do partial + * matching. + */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), - DMI_MATCH(DMI_PRODUCT_NAME, "X3"), + DMI_MATCH(DMI_PRODUCT_NAME, "P65_67RP"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Gigabyte P34 - Elantech touchpad */ + /* + * This is only a partial board_name and might be followed by + * another letter or number. DMI_MATCH however does do partial + * matching. + */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), - DMI_MATCH(DMI_PRODUCT_NAME, "P34"), + DMI_MATCH(DMI_PRODUCT_NAME, "P65_67RS"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Gigabyte P57 - Elantech touchpad */ + /* + * This is only a partial board_name and might be followed by + * another letter or number. DMI_MATCH however does do partial + * matching. + */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), - DMI_MATCH(DMI_PRODUCT_NAME, "P57"), + DMI_MATCH(DMI_PRODUCT_NAME, "P67xRP"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Schenker XMG C504 - Elantech touchpad */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "XMG"), - DMI_MATCH(DMI_PRODUCT_NAME, "C504"), + DMI_MATCH(DMI_BOARD_NAME, "PB50_70DFx,DDx"), + }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "X170SM"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "X170KM-G"), + }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { } }; -static const struct dmi_system_id i8042_dmi_probe_defer_table[] __initconst = { +#ifdef CONFIG_PNP +static const struct dmi_system_id i8042_dmi_laptop_table[] __initconst = { { - /* ASUS ZenBook UX425UA */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX425UA"), + DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */ }, }, { - /* ASUS ZenBook UM325UA */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX325UA_UM325UA"), + DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /* Laptop */ + }, + }, + { + .matches = { + DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */ + }, + }, + { + .matches = { + DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */ }, }, { } }; +#endif #endif /* CONFIG_X86 */ @@ -1167,11 +1434,6 @@ static int __init i8042_pnp_init(void) bool pnp_data_busted = false; int err; -#ifdef CONFIG_X86 - if (dmi_check_system(i8042_dmi_nopnp_table)) - i8042_nopnp = true; -#endif - if (i8042_nopnp) { pr_info("PNP detection disabled\n"); return 0; @@ -1275,6 +1537,59 @@ static inline int i8042_pnp_init(void) { return 0; } static inline void i8042_pnp_exit(void) { } #endif /* CONFIG_PNP */ + +#ifdef CONFIG_X86 +static void __init i8042_check_quirks(void) +{ + const struct dmi_system_id *device_quirk_info; + uintptr_t quirks; + + device_quirk_info = dmi_first_match(i8042_dmi_quirk_table); + if (!device_quirk_info) + return; + + quirks = (uintptr_t)device_quirk_info->driver_data; + + if (quirks & SERIO_QUIRK_NOKBD) + i8042_nokbd = true; + if (quirks & SERIO_QUIRK_NOAUX) + i8042_noaux = true; + if (quirks & SERIO_QUIRK_NOMUX) + i8042_nomux = true; + if (quirks & SERIO_QUIRK_FORCEMUX) + i8042_nomux = false; + if (quirks & SERIO_QUIRK_UNLOCK) + i8042_unlock = true; + if (quirks & SERIO_QUIRK_PROBE_DEFER) + i8042_probe_defer = true; + /* Honor module parameter when value is not default */ + if (i8042_reset == I8042_RESET_DEFAULT) { + if (quirks & SERIO_QUIRK_RESET_ALWAYS) + i8042_reset = I8042_RESET_ALWAYS; + if (quirks & SERIO_QUIRK_RESET_NEVER) + i8042_reset = I8042_RESET_NEVER; + } + if (quirks & SERIO_QUIRK_DIECT) + i8042_direct = true; + if (quirks & SERIO_QUIRK_DUMBKBD) + i8042_dumbkbd = true; + if (quirks & SERIO_QUIRK_NOLOOP) + i8042_noloop = true; + if (quirks & SERIO_QUIRK_NOTIMEOUT) + i8042_notimeout = true; + if (quirks & SERIO_QUIRK_KBDRESET) + i8042_kbdreset = true; + if (quirks & SERIO_QUIRK_DRITEK) + i8042_dritek = true; +#ifdef CONFIG_PNP + if (quirks & SERIO_QUIRK_NOPNP) + i8042_nopnp = true; +#endif +} +#else +static inline void i8042_check_quirks(void) {} +#endif + static int __init i8042_platform_init(void) { int retval; @@ -1297,45 +1612,42 @@ static int __init i8042_platform_init(void) i8042_kbd_irq = I8042_MAP_IRQ(1); i8042_aux_irq = I8042_MAP_IRQ(12); - retval = i8042_pnp_init(); - if (retval) - return retval; - #if defined(__ia64__) - i8042_reset = I8042_RESET_ALWAYS; + i8042_reset = I8042_RESET_ALWAYS; #endif + i8042_check_quirks(); + + pr_debug("Active quirks (empty means none):%s%s%s%s%s%s%s%s%s%s%s%s%s\n", + i8042_nokbd ? " nokbd" : "", + i8042_noaux ? " noaux" : "", + i8042_nomux ? " nomux" : "", + i8042_unlock ? " unlock" : "", + i8042_probe_defer ? "probe_defer" : "", + i8042_reset == I8042_RESET_DEFAULT ? + "" : i8042_reset == I8042_RESET_ALWAYS ? + " reset_always" : " reset_never", + i8042_direct ? " direct" : "", + i8042_dumbkbd ? " dumbkbd" : "", + i8042_noloop ? " noloop" : "", + i8042_notimeout ? " notimeout" : "", + i8042_kbdreset ? " kbdreset" : "", #ifdef CONFIG_X86 - /* Honor module parameter when value is not default */ - if (i8042_reset == I8042_RESET_DEFAULT) { - if (dmi_check_system(i8042_dmi_reset_table)) - i8042_reset = I8042_RESET_ALWAYS; - - if (dmi_check_system(i8042_dmi_noselftest_table)) - i8042_reset = I8042_RESET_NEVER; - } - - if (dmi_check_system(i8042_dmi_noloop_table)) - i8042_noloop = true; - - if (dmi_check_system(i8042_dmi_nomux_table)) - i8042_nomux = true; - - if (dmi_check_system(i8042_dmi_forcemux_table)) - i8042_nomux = false; - - if (dmi_check_system(i8042_dmi_notimeout_table)) - i8042_notimeout = true; - - if (dmi_check_system(i8042_dmi_dritek_table)) - i8042_dritek = true; - - if (dmi_check_system(i8042_dmi_kbdreset_table)) - i8042_kbdreset = true; + i8042_dritek ? " dritek" : "", +#else + "", +#endif +#ifdef CONFIG_PNP + i8042_nopnp ? " nopnp" : ""); +#else + ""); +#endif - if (dmi_check_system(i8042_dmi_probe_defer_table)) - i8042_probe_defer = true; + retval = i8042_pnp_init(); + if (retval) + return retval; +#ifdef CONFIG_X86 /* * A20 was already enabled during early kernel init. But some buggy * BIOSes (in MSI Laptops) require A20 to be enabled using 8042 to diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index bb2e1cbffba7..82beddb28761 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -24,6 +24,7 @@ #include <linux/irq.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/property.h> #include <linux/ratelimit.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> @@ -47,6 +48,8 @@ #define M09_REGISTER_NUM_X 0x94 #define M09_REGISTER_NUM_Y 0x95 +#define M12_REGISTER_REPORT_RATE 0x88 + #define EV_REGISTER_THRESHOLD 0x40 #define EV_REGISTER_GAIN 0x41 #define EV_REGISTER_OFFSET_Y 0x45 @@ -127,9 +130,12 @@ struct edt_ft5x06_ts_data { int max_support_points; char name[EDT_NAME_LEN]; + char fw_version[EDT_NAME_LEN]; struct edt_reg_addr reg_addr; enum edt_ver version; + unsigned int crc_errors; + unsigned int header_errors; }; struct edt_i2c_chip_data { @@ -178,6 +184,7 @@ static bool edt_ft5x06_ts_check_crc(struct edt_ft5x06_ts_data *tsdata, crc ^= buf[i]; if (crc != buf[buflen-1]) { + tsdata->crc_errors++; dev_err_ratelimited(&tsdata->client->dev, "crc error: 0x%02x expected, got 0x%02x\n", crc, buf[buflen-1]); @@ -235,6 +242,7 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) if (tsdata->version == EDT_M06) { if (rdbuf[0] != 0xaa || rdbuf[1] != 0xaa || rdbuf[2] != datalen) { + tsdata->header_errors++; dev_err_ratelimited(dev, "Unexpected header: %02x%02x%02x!\n", rdbuf[0], rdbuf[1], rdbuf[2]); @@ -523,9 +531,55 @@ static EDT_ATTR(offset_y, S_IWUSR | S_IRUGO, NO_REGISTER, NO_REGISTER, /* m06: range 20 to 80, m09: range 0 to 30, m12: range 1 to 255... */ static EDT_ATTR(threshold, S_IWUSR | S_IRUGO, WORK_REGISTER_THRESHOLD, M09_REGISTER_THRESHOLD, EV_REGISTER_THRESHOLD, 0, 255); -/* m06: range 3 to 14, m12: (0x64: 100Hz) */ +/* m06: range 3 to 14, m12: range 1 to 255 */ static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, WORK_REGISTER_REPORT_RATE, - NO_REGISTER, NO_REGISTER, 0, 255); + M12_REGISTER_REPORT_RATE, NO_REGISTER, 0, 255); + +static ssize_t model_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); + + return sysfs_emit(buf, "%s\n", tsdata->name); +} + +static DEVICE_ATTR_RO(model); + +static ssize_t fw_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); + + return sysfs_emit(buf, "%s\n", tsdata->fw_version); +} + +static DEVICE_ATTR_RO(fw_version); + +/* m06 only */ +static ssize_t header_errors_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); + + return sysfs_emit(buf, "%d\n", tsdata->header_errors); +} + +static DEVICE_ATTR_RO(header_errors); + +/* m06 only */ +static ssize_t crc_errors_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); + + return sysfs_emit(buf, "%d\n", tsdata->crc_errors); +} + +static DEVICE_ATTR_RO(crc_errors); static struct attribute *edt_ft5x06_attrs[] = { &edt_ft5x06_attr_gain.dattr.attr, @@ -534,6 +588,10 @@ static struct attribute *edt_ft5x06_attrs[] = { &edt_ft5x06_attr_offset_y.dattr.attr, &edt_ft5x06_attr_threshold.dattr.attr, &edt_ft5x06_attr_report_rate.dattr.attr, + &dev_attr_model.attr, + &dev_attr_fw_version.attr, + &dev_attr_header_errors.attr, + &dev_attr_crc_errors.attr, NULL }; @@ -820,13 +878,13 @@ static void edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) #endif /* CONFIG_DEBUGFS */ static int edt_ft5x06_ts_identify(struct i2c_client *client, - struct edt_ft5x06_ts_data *tsdata, - char *fw_version) + struct edt_ft5x06_ts_data *tsdata) { u8 rdbuf[EDT_NAME_LEN]; char *p; int error; char *model_name = tsdata->name; + char *fw_version = tsdata->fw_version; /* see what we find if we assume it is a M06 * * if we get less than EDT_NAME_LEN, we don't want @@ -1030,7 +1088,8 @@ static void edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata) case EDT_M09: case EDT_M12: reg_addr->reg_threshold = M09_REGISTER_THRESHOLD; - reg_addr->reg_report_rate = NO_REGISTER; + reg_addr->reg_report_rate = tsdata->version == EDT_M12 ? + M12_REGISTER_REPORT_RATE : NO_REGISTER; reg_addr->reg_gain = M09_REGISTER_GAIN; reg_addr->reg_offset = M09_REGISTER_OFFSET; reg_addr->reg_offset_x = NO_REGISTER; @@ -1081,7 +1140,7 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, struct input_dev *input; unsigned long irq_flags; int error; - char fw_version[EDT_NAME_LEN]; + u32 report_rate; dev_dbg(&client->dev, "probing for EDT FT5x06 I2C\n"); @@ -1194,7 +1253,7 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, tsdata->input = input; tsdata->factory_mode = false; - error = edt_ft5x06_ts_identify(client, tsdata, fw_version); + error = edt_ft5x06_ts_identify(client, tsdata); if (error) { dev_err(&client->dev, "touchscreen probe failed\n"); return error; @@ -1210,9 +1269,30 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, edt_ft5x06_ts_get_defaults(&client->dev, tsdata); edt_ft5x06_ts_get_parameters(tsdata); + if (tsdata->reg_addr.reg_report_rate != NO_REGISTER && + !device_property_read_u32(&client->dev, + "report-rate-hz", &report_rate)) { + if (tsdata->version == EDT_M06) + tsdata->report_rate = clamp_val(report_rate, 30, 140); + else + tsdata->report_rate = clamp_val(report_rate, 1, 255); + + if (report_rate != tsdata->report_rate) + dev_warn(&client->dev, + "report-rate %dHz is unsupported, use %dHz\n", + report_rate, tsdata->report_rate); + + if (tsdata->version == EDT_M06) + tsdata->report_rate /= 10; + + edt_ft5x06_register_write(tsdata, + tsdata->reg_addr.reg_report_rate, + tsdata->report_rate); + } + dev_dbg(&client->dev, "Model \"%s\", Rev. \"%s\", %dx%d sensors\n", - tsdata->name, fw_version, tsdata->num_x, tsdata->num_y); + tsdata->name, tsdata->fw_version, tsdata->num_x, tsdata->num_y); input->name = tsdata->name; input->id.bustype = BUS_I2C; diff --git a/drivers/input/touchscreen/exc3000.c b/drivers/input/touchscreen/exc3000.c index cbe0dd412912..4b7eee01c6aa 100644 --- a/drivers/input/touchscreen/exc3000.c +++ b/drivers/input/touchscreen/exc3000.c @@ -220,6 +220,7 @@ static int exc3000_vendor_data_request(struct exc3000_data *data, u8 *request, { u8 buf[EXC3000_LEN_VENDOR_REQUEST] = { 0x67, 0x00, 0x42, 0x00, 0x03 }; int ret; + unsigned long time_left; mutex_lock(&data->query_lock); @@ -233,9 +234,9 @@ static int exc3000_vendor_data_request(struct exc3000_data *data, u8 *request, goto out_unlock; if (response) { - ret = wait_for_completion_timeout(&data->wait_event, - timeout * HZ); - if (ret <= 0) { + time_left = wait_for_completion_timeout(&data->wait_event, + timeout * HZ); + if (time_left == 0) { ret = -ETIMEDOUT; goto out_unlock; } diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index aa45a9fee6a0..d016505fc081 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -822,22 +822,16 @@ static int goodix_resource(struct acpi_resource *ares, void *data) struct device *dev = &ts->client->dev; struct acpi_resource_gpio *gpio; - switch (ares->type) { - case ACPI_RESOURCE_TYPE_GPIO: - gpio = &ares->data.gpio; - if (gpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT) { - if (ts->gpio_int_idx == -1) { - ts->gpio_int_idx = ts->gpio_count; - } else { - dev_err(dev, "More then one GpioInt resource, ignoring ACPI GPIO resources\n"); - ts->gpio_int_idx = -2; - } + if (acpi_gpio_get_irq_resource(ares, &gpio)) { + if (ts->gpio_int_idx == -1) { + ts->gpio_int_idx = ts->gpio_count; + } else { + dev_err(dev, "More then one GpioInt resource, ignoring ACPI GPIO resources\n"); + ts->gpio_int_idx = -2; } ts->gpio_count++; - break; - default: - break; - } + } else if (acpi_gpio_get_io_resource(ares, &gpio)) + ts->gpio_count++; return 0; } diff --git a/drivers/input/touchscreen/zinitix.c b/drivers/input/touchscreen/zinitix.c index 8bd03278ad9a..52f9e9eaab14 100644 --- a/drivers/input/touchscreen/zinitix.c +++ b/drivers/input/touchscreen/zinitix.c @@ -15,75 +15,75 @@ /* Register Map */ -#define BT541_SWRESET_CMD 0x0000 -#define BT541_WAKEUP_CMD 0x0001 +#define ZINITIX_SWRESET_CMD 0x0000 +#define ZINITIX_WAKEUP_CMD 0x0001 -#define BT541_IDLE_CMD 0x0004 -#define BT541_SLEEP_CMD 0x0005 +#define ZINITIX_IDLE_CMD 0x0004 +#define ZINITIX_SLEEP_CMD 0x0005 -#define BT541_CLEAR_INT_STATUS_CMD 0x0003 -#define BT541_CALIBRATE_CMD 0x0006 -#define BT541_SAVE_STATUS_CMD 0x0007 -#define BT541_SAVE_CALIBRATION_CMD 0x0008 -#define BT541_RECALL_FACTORY_CMD 0x000f +#define ZINITIX_CLEAR_INT_STATUS_CMD 0x0003 +#define ZINITIX_CALIBRATE_CMD 0x0006 +#define ZINITIX_SAVE_STATUS_CMD 0x0007 +#define ZINITIX_SAVE_CALIBRATION_CMD 0x0008 +#define ZINITIX_RECALL_FACTORY_CMD 0x000f -#define BT541_THRESHOLD 0x0020 +#define ZINITIX_THRESHOLD 0x0020 -#define BT541_LARGE_PALM_REJECT_AREA_TH 0x003F +#define ZINITIX_LARGE_PALM_REJECT_AREA_TH 0x003F -#define BT541_DEBUG_REG 0x0115 /* 0~7 */ +#define ZINITIX_DEBUG_REG 0x0115 /* 0~7 */ -#define BT541_TOUCH_MODE 0x0010 -#define BT541_CHIP_REVISION 0x0011 -#define BT541_FIRMWARE_VERSION 0x0012 +#define ZINITIX_TOUCH_MODE 0x0010 +#define ZINITIX_CHIP_REVISION 0x0011 +#define ZINITIX_FIRMWARE_VERSION 0x0012 #define ZINITIX_USB_DETECT 0x116 -#define BT541_MINOR_FW_VERSION 0x0121 +#define ZINITIX_MINOR_FW_VERSION 0x0121 -#define BT541_VENDOR_ID 0x001C -#define BT541_HW_ID 0x0014 +#define ZINITIX_VENDOR_ID 0x001C +#define ZINITIX_HW_ID 0x0014 -#define BT541_DATA_VERSION_REG 0x0013 -#define BT541_SUPPORTED_FINGER_NUM 0x0015 -#define BT541_EEPROM_INFO 0x0018 -#define BT541_INITIAL_TOUCH_MODE 0x0019 +#define ZINITIX_DATA_VERSION_REG 0x0013 +#define ZINITIX_SUPPORTED_FINGER_NUM 0x0015 +#define ZINITIX_EEPROM_INFO 0x0018 +#define ZINITIX_INITIAL_TOUCH_MODE 0x0019 -#define BT541_TOTAL_NUMBER_OF_X 0x0060 -#define BT541_TOTAL_NUMBER_OF_Y 0x0061 +#define ZINITIX_TOTAL_NUMBER_OF_X 0x0060 +#define ZINITIX_TOTAL_NUMBER_OF_Y 0x0061 -#define BT541_DELAY_RAW_FOR_HOST 0x007f +#define ZINITIX_DELAY_RAW_FOR_HOST 0x007f -#define BT541_BUTTON_SUPPORTED_NUM 0x00B0 -#define BT541_BUTTON_SENSITIVITY 0x00B2 -#define BT541_DUMMY_BUTTON_SENSITIVITY 0X00C8 +#define ZINITIX_BUTTON_SUPPORTED_NUM 0x00B0 +#define ZINITIX_BUTTON_SENSITIVITY 0x00B2 +#define ZINITIX_DUMMY_BUTTON_SENSITIVITY 0X00C8 -#define BT541_X_RESOLUTION 0x00C0 -#define BT541_Y_RESOLUTION 0x00C1 +#define ZINITIX_X_RESOLUTION 0x00C0 +#define ZINITIX_Y_RESOLUTION 0x00C1 -#define BT541_POINT_STATUS_REG 0x0080 -#define BT541_ICON_STATUS_REG 0x00AA +#define ZINITIX_POINT_STATUS_REG 0x0080 +#define ZINITIX_ICON_STATUS_REG 0x00AA -#define BT541_POINT_COORD_REG (BT541_POINT_STATUS_REG + 2) +#define ZINITIX_POINT_COORD_REG (ZINITIX_POINT_STATUS_REG + 2) -#define BT541_AFE_FREQUENCY 0x0100 -#define BT541_DND_N_COUNT 0x0122 -#define BT541_DND_U_COUNT 0x0135 +#define ZINITIX_AFE_FREQUENCY 0x0100 +#define ZINITIX_DND_N_COUNT 0x0122 +#define ZINITIX_DND_U_COUNT 0x0135 -#define BT541_RAWDATA_REG 0x0200 +#define ZINITIX_RAWDATA_REG 0x0200 -#define BT541_EEPROM_INFO_REG 0x0018 +#define ZINITIX_EEPROM_INFO_REG 0x0018 -#define BT541_INT_ENABLE_FLAG 0x00f0 -#define BT541_PERIODICAL_INTERRUPT_INTERVAL 0x00f1 +#define ZINITIX_INT_ENABLE_FLAG 0x00f0 +#define ZINITIX_PERIODICAL_INTERRUPT_INTERVAL 0x00f1 -#define BT541_BTN_WIDTH 0x016d +#define ZINITIX_BTN_WIDTH 0x016d -#define BT541_CHECKSUM_RESULT 0x012c +#define ZINITIX_CHECKSUM_RESULT 0x012c -#define BT541_INIT_FLASH 0x01d0 -#define BT541_WRITE_FLASH 0x01d1 -#define BT541_READ_FLASH 0x01d2 +#define ZINITIX_INIT_FLASH 0x01d0 +#define ZINITIX_WRITE_FLASH 0x01d1 +#define ZINITIX_READ_FLASH 0x01d2 #define ZINITIX_INTERNAL_FLAG_02 0x011e #define ZINITIX_INTERNAL_FLAG_03 0x011f @@ -196,13 +196,13 @@ static int zinitix_init_touch(struct bt541_ts_data *bt541) int i; int error; - error = zinitix_write_cmd(client, BT541_SWRESET_CMD); + error = zinitix_write_cmd(client, ZINITIX_SWRESET_CMD); if (error) { dev_err(&client->dev, "Failed to write reset command\n"); return error; } - error = zinitix_write_u16(client, BT541_INT_ENABLE_FLAG, 0x0); + error = zinitix_write_u16(client, ZINITIX_INT_ENABLE_FLAG, 0x0); if (error) { dev_err(&client->dev, "Failed to reset interrupt enable flag\n"); @@ -210,32 +210,32 @@ static int zinitix_init_touch(struct bt541_ts_data *bt541) } /* initialize */ - error = zinitix_write_u16(client, BT541_X_RESOLUTION, + error = zinitix_write_u16(client, ZINITIX_X_RESOLUTION, bt541->prop.max_x); if (error) return error; - error = zinitix_write_u16(client, BT541_Y_RESOLUTION, + error = zinitix_write_u16(client, ZINITIX_Y_RESOLUTION, bt541->prop.max_y); if (error) return error; - error = zinitix_write_u16(client, BT541_SUPPORTED_FINGER_NUM, + error = zinitix_write_u16(client, ZINITIX_SUPPORTED_FINGER_NUM, MAX_SUPPORTED_FINGER_NUM); if (error) return error; - error = zinitix_write_u16(client, BT541_INITIAL_TOUCH_MODE, + error = zinitix_write_u16(client, ZINITIX_INITIAL_TOUCH_MODE, bt541->zinitix_mode); if (error) return error; - error = zinitix_write_u16(client, BT541_TOUCH_MODE, + error = zinitix_write_u16(client, ZINITIX_TOUCH_MODE, bt541->zinitix_mode); if (error) return error; - error = zinitix_write_u16(client, BT541_INT_ENABLE_FLAG, + error = zinitix_write_u16(client, ZINITIX_INT_ENABLE_FLAG, BIT_PT_CNT_CHANGE | BIT_DOWN | BIT_MOVE | BIT_UP); if (error) @@ -243,7 +243,7 @@ static int zinitix_init_touch(struct bt541_ts_data *bt541) /* clear queue */ for (i = 0; i < 10; i++) { - zinitix_write_cmd(client, BT541_CLEAR_INT_STATUS_CMD); + zinitix_write_cmd(client, ZINITIX_CLEAR_INT_STATUS_CMD); udelay(10); } @@ -361,7 +361,7 @@ static irqreturn_t zinitix_ts_irq_handler(int irq, void *bt541_handler) memset(&touch_event, 0, sizeof(struct touch_event)); - error = zinitix_read_data(bt541->client, BT541_POINT_STATUS_REG, + error = zinitix_read_data(bt541->client, ZINITIX_POINT_STATUS_REG, &touch_event, sizeof(struct touch_event)); if (error) { dev_err(&client->dev, "Failed to read in touchpoint struct\n"); @@ -381,7 +381,7 @@ static irqreturn_t zinitix_ts_irq_handler(int irq, void *bt541_handler) input_sync(bt541->input_dev); out: - zinitix_write_cmd(bt541->client, BT541_CLEAR_INT_STATUS_CMD); + zinitix_write_cmd(bt541->client, ZINITIX_CLEAR_INT_STATUS_CMD); return IRQ_HANDLED; } diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c index acd6d6b47434..09c7ed2650ca 100644 --- a/drivers/md/dm-bufio.c +++ b/drivers/md/dm-bufio.c @@ -83,7 +83,7 @@ struct dm_bufio_client { struct mutex lock; spinlock_t spinlock; - unsigned long spinlock_flags; + bool no_sleep; struct list_head lru[LIST_SIZE]; unsigned long n_buffers[LIST_SIZE]; @@ -93,8 +93,6 @@ struct dm_bufio_client { s8 sectors_per_block_bits; void (*alloc_callback)(struct dm_buffer *); void (*write_callback)(struct dm_buffer *); - bool no_sleep; - struct kmem_cache *slab_buffer; struct kmem_cache *slab_cache; struct dm_io_client *dm_io; @@ -174,7 +172,7 @@ static DEFINE_STATIC_KEY_FALSE(no_sleep_enabled); static void dm_bufio_lock(struct dm_bufio_client *c) { if (static_branch_unlikely(&no_sleep_enabled) && c->no_sleep) - spin_lock_irqsave_nested(&c->spinlock, c->spinlock_flags, dm_bufio_in_request()); + spin_lock_bh(&c->spinlock); else mutex_lock_nested(&c->lock, dm_bufio_in_request()); } @@ -182,7 +180,7 @@ static void dm_bufio_lock(struct dm_bufio_client *c) static int dm_bufio_trylock(struct dm_bufio_client *c) { if (static_branch_unlikely(&no_sleep_enabled) && c->no_sleep) - return spin_trylock_irqsave(&c->spinlock, c->spinlock_flags); + return spin_trylock_bh(&c->spinlock); else return mutex_trylock(&c->lock); } @@ -190,7 +188,7 @@ static int dm_bufio_trylock(struct dm_bufio_client *c) static void dm_bufio_unlock(struct dm_bufio_client *c) { if (static_branch_unlikely(&no_sleep_enabled) && c->no_sleep) - spin_unlock_irqrestore(&c->spinlock, c->spinlock_flags); + spin_unlock_bh(&c->spinlock); else mutex_unlock(&c->lock); } @@ -817,6 +815,10 @@ static struct dm_buffer *__get_unclaimed_buffer(struct dm_bufio_client *c) BUG_ON(test_bit(B_WRITING, &b->state)); BUG_ON(test_bit(B_DIRTY, &b->state)); + if (static_branch_unlikely(&no_sleep_enabled) && c->no_sleep && + unlikely(test_bit(B_READING, &b->state))) + continue; + if (!b->hold_count) { __make_buffer_clean(b); __unlink_buffer(b); @@ -825,6 +827,9 @@ static struct dm_buffer *__get_unclaimed_buffer(struct dm_bufio_client *c) cond_resched(); } + if (static_branch_unlikely(&no_sleep_enabled) && c->no_sleep) + return NULL; + list_for_each_entry_reverse(b, &c->lru[LIST_DIRTY], lru_list) { BUG_ON(test_bit(B_READING, &b->state)); @@ -1632,7 +1637,8 @@ static void drop_buffers(struct dm_bufio_client *c) */ static bool __try_evict_buffer(struct dm_buffer *b, gfp_t gfp) { - if (!(gfp & __GFP_FS)) { + if (!(gfp & __GFP_FS) || + (static_branch_unlikely(&no_sleep_enabled) && b->c->no_sleep)) { if (test_bit(B_READING, &b->state) || test_bit(B_WRITING, &b->state) || test_bit(B_DIRTY, &b->state)) diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index 2347e83902f1..94b6cb599db4 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -38,7 +38,7 @@ #define DM_VERITY_OPT_AT_MOST_ONCE "check_at_most_once" #define DM_VERITY_OPT_TASKLET_VERIFY "try_verify_in_tasklet" -#define DM_VERITY_OPTS_MAX (3 + DM_VERITY_OPTS_FEC + \ +#define DM_VERITY_OPTS_MAX (4 + DM_VERITY_OPTS_FEC + \ DM_VERITY_ROOT_HASH_VERIFICATION_OPTS) static unsigned dm_verity_prefetch_cluster = DM_VERITY_DEFAULT_PREFETCH_SIZE; @@ -1053,7 +1053,7 @@ static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v, struct dm_verity_sig_opts *verify_args, bool only_modifier_opts) { - int r; + int r = 0; unsigned argc; struct dm_target *ti = v->ti; const char *arg_name; @@ -1123,8 +1123,18 @@ static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v, if (r) return r; continue; + + } else if (only_modifier_opts) { + /* + * Ignore unrecognized opt, could easily be an extra + * argument to an option whose parsing was skipped. + * Normal parsing (@only_modifier_opts=false) will + * properly parse all options (and their extra args). + */ + continue; } + DMERR("Unrecognized verity feature request: %s", arg_name); ti->error = "Unrecognized verity feature request"; return -EINVAL; } while (argc && !r); diff --git a/drivers/md/dm-writecache.c b/drivers/md/dm-writecache.c index 1fc161d65673..96a003eb7323 100644 --- a/drivers/md/dm-writecache.c +++ b/drivers/md/dm-writecache.c @@ -1594,7 +1594,8 @@ done: default: BUG(); - return -1; + wc_unlock(wc); + return DM_MAPIO_KILL; } } diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index 007d43e46dcb..b9dbad3a8af8 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -653,6 +653,7 @@ static struct slave *rlb_choose_channel(struct sk_buff *skb, static struct slave *rlb_arp_xmit(struct sk_buff *skb, struct bonding *bond) { struct slave *tx_slave = NULL; + struct net_device *dev; struct arp_pkt *arp; if (!pskb_network_may_pull(skb, sizeof(*arp))) @@ -665,6 +666,15 @@ static struct slave *rlb_arp_xmit(struct sk_buff *skb, struct bonding *bond) if (!bond_slave_has_mac_rx(bond, arp->mac_src)) return NULL; + dev = ip_dev_find(dev_net(bond->dev), arp->ip_src); + if (dev) { + if (netif_is_bridge_master(dev)) { + dev_put(dev); + return NULL; + } + dev_put(dev); + } + if (arp->op_code == htons(ARPOP_REPLY)) { /* the arp must be sent on the selected rx channel */ tx_slave = rlb_choose_channel(skb, bond, arp); diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index e75acb14d066..50e60843020c 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -2001,6 +2001,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev, for (i = 0; i < BOND_MAX_ARP_TARGETS; i++) new_slave->target_last_arp_rx[i] = new_slave->last_rx; + new_slave->last_tx = new_slave->last_rx; + if (bond->params.miimon && !bond->params.use_carrier) { link_reporting = bond_check_dev_link(bond, slave_dev, 1); @@ -2884,8 +2886,11 @@ static void bond_arp_send(struct slave *slave, int arp_op, __be32 dest_ip, return; } - if (bond_handle_vlan(slave, tags, skb)) + if (bond_handle_vlan(slave, tags, skb)) { + slave_update_last_tx(slave); arp_xmit(skb); + } + return; } @@ -3074,8 +3079,7 @@ static int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond, curr_active_slave->last_link_up)) bond_validate_arp(bond, slave, tip, sip); else if (curr_arp_slave && (arp->ar_op == htons(ARPOP_REPLY)) && - bond_time_in_interval(bond, - dev_trans_start(curr_arp_slave->dev), 1)) + bond_time_in_interval(bond, slave_last_tx(curr_arp_slave), 1)) bond_validate_arp(bond, slave, sip, tip); out_unlock: @@ -3103,8 +3107,10 @@ static void bond_ns_send(struct slave *slave, const struct in6_addr *daddr, } addrconf_addr_solict_mult(daddr, &mcaddr); - if (bond_handle_vlan(slave, tags, skb)) + if (bond_handle_vlan(slave, tags, skb)) { + slave_update_last_tx(slave); ndisc_send_skb(skb, &mcaddr, saddr); + } } static void bond_ns_send_all(struct bonding *bond, struct slave *slave) @@ -3246,8 +3252,7 @@ static int bond_na_rcv(const struct sk_buff *skb, struct bonding *bond, curr_active_slave->last_link_up)) bond_validate_ns(bond, slave, saddr, daddr); else if (curr_arp_slave && - bond_time_in_interval(bond, - dev_trans_start(curr_arp_slave->dev), 1)) + bond_time_in_interval(bond, slave_last_tx(curr_arp_slave), 1)) bond_validate_ns(bond, slave, saddr, daddr); out: @@ -3335,12 +3340,12 @@ static void bond_loadbalance_arp_mon(struct bonding *bond) * so it can wait */ bond_for_each_slave_rcu(bond, slave, iter) { - unsigned long trans_start = dev_trans_start(slave->dev); + unsigned long last_tx = slave_last_tx(slave); bond_propose_link_state(slave, BOND_LINK_NOCHANGE); if (slave->link != BOND_LINK_UP) { - if (bond_time_in_interval(bond, trans_start, 1) && + if (bond_time_in_interval(bond, last_tx, 1) && bond_time_in_interval(bond, slave->last_rx, 1)) { bond_propose_link_state(slave, BOND_LINK_UP); @@ -3365,7 +3370,7 @@ static void bond_loadbalance_arp_mon(struct bonding *bond) * when the source ip is 0, so don't take the link down * if we don't know our ip yet */ - if (!bond_time_in_interval(bond, trans_start, bond->params.missed_max) || + if (!bond_time_in_interval(bond, last_tx, bond->params.missed_max) || !bond_time_in_interval(bond, slave->last_rx, bond->params.missed_max)) { bond_propose_link_state(slave, BOND_LINK_DOWN); @@ -3431,7 +3436,7 @@ re_arm: */ static int bond_ab_arp_inspect(struct bonding *bond) { - unsigned long trans_start, last_rx; + unsigned long last_tx, last_rx; struct list_head *iter; struct slave *slave; int commit = 0; @@ -3482,9 +3487,9 @@ static int bond_ab_arp_inspect(struct bonding *bond) * - (more than missed_max*delta since receive AND * the bond has an IP address) */ - trans_start = dev_trans_start(slave->dev); + last_tx = slave_last_tx(slave); if (bond_is_active_slave(slave) && - (!bond_time_in_interval(bond, trans_start, bond->params.missed_max) || + (!bond_time_in_interval(bond, last_tx, bond->params.missed_max) || !bond_time_in_interval(bond, last_rx, bond->params.missed_max))) { bond_propose_link_state(slave, BOND_LINK_DOWN); commit++; @@ -3501,8 +3506,8 @@ static int bond_ab_arp_inspect(struct bonding *bond) */ static void bond_ab_arp_commit(struct bonding *bond) { - unsigned long trans_start; struct list_head *iter; + unsigned long last_tx; struct slave *slave; bond_for_each_slave(bond, slave, iter) { @@ -3511,10 +3516,10 @@ static void bond_ab_arp_commit(struct bonding *bond) continue; case BOND_LINK_UP: - trans_start = dev_trans_start(slave->dev); + last_tx = slave_last_tx(slave); if (rtnl_dereference(bond->curr_active_slave) != slave || (!rtnl_dereference(bond->curr_active_slave) && - bond_time_in_interval(bond, trans_start, 1))) { + bond_time_in_interval(bond, last_tx, 1))) { struct slave *current_arp_slave; current_arp_slave = rtnl_dereference(bond->current_arp_slave); @@ -5333,8 +5338,14 @@ static struct net_device *bond_sk_get_lower_dev(struct net_device *dev, static netdev_tx_t bond_tls_device_xmit(struct bonding *bond, struct sk_buff *skb, struct net_device *dev) { - if (likely(bond_get_slave_by_dev(bond, tls_get_ctx(skb->sk)->netdev))) - return bond_dev_queue_xmit(bond, skb, tls_get_ctx(skb->sk)->netdev); + struct net_device *tls_netdev = rcu_dereference(tls_get_ctx(skb->sk)->netdev); + + /* tls_netdev might become NULL, even if tls_is_sk_tx_device_offloaded + * was true, if tls_device_down is running in parallel, but it's OK, + * because bond_get_slave_by_dev has a NULL check. + */ + if (likely(bond_get_slave_by_dev(bond, tls_netdev))) + return bond_dev_queue_xmit(bond, skb, tls_netdev); return bond_tx_drop(dev, skb); } #endif diff --git a/drivers/net/can/spi/mcp251x.c b/drivers/net/can/spi/mcp251x.c index e750d13c8841..c320de474f40 100644 --- a/drivers/net/can/spi/mcp251x.c +++ b/drivers/net/can/spi/mcp251x.c @@ -1070,9 +1070,6 @@ static irqreturn_t mcp251x_can_ist(int irq, void *dev_id) mcp251x_read_2regs(spi, CANINTF, &intf, &eflag); - /* mask out flags we don't care about */ - intf &= CANINTF_RX | CANINTF_TX | CANINTF_ERR; - /* receive buffer 0 */ if (intf & CANINTF_RX0IF) { mcp251x_hw_rx(spi, 0); @@ -1082,6 +1079,18 @@ static irqreturn_t mcp251x_can_ist(int irq, void *dev_id) if (mcp251x_is_2510(spi)) mcp251x_write_bits(spi, CANINTF, CANINTF_RX0IF, 0x00); + + /* check if buffer 1 is already known to be full, no need to re-read */ + if (!(intf & CANINTF_RX1IF)) { + u8 intf1, eflag1; + + /* intf needs to be read again to avoid a race condition */ + mcp251x_read_2regs(spi, CANINTF, &intf1, &eflag1); + + /* combine flags from both operations for error handling */ + intf |= intf1; + eflag |= eflag1; + } } /* receive buffer 1 */ @@ -1092,6 +1101,9 @@ static irqreturn_t mcp251x_can_ist(int irq, void *dev_id) clear_intf |= CANINTF_RX1IF; } + /* mask out flags we don't care about */ + intf &= CANINTF_RX | CANINTF_TX | CANINTF_ERR; + /* any error or tx interrupt we need to clear? */ if (intf & (CANINTF_ERR | CANINTF_TX)) clear_intf |= intf & (CANINTF_ERR | CANINTF_TX); diff --git a/drivers/net/can/usb/ems_usb.c b/drivers/net/can/usb/ems_usb.c index d1e1a459c045..d31191686a54 100644 --- a/drivers/net/can/usb/ems_usb.c +++ b/drivers/net/can/usb/ems_usb.c @@ -195,7 +195,7 @@ struct __packed ems_cpc_msg { __le32 ts_sec; /* timestamp in seconds */ __le32 ts_nsec; /* timestamp in nano seconds */ - union { + union __packed { u8 generic[64]; struct cpc_can_msg can_msg; struct cpc_can_params can_params; diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 859196898a7d..aadb0bd7c24f 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -610,6 +610,9 @@ static int felix_change_tag_protocol(struct dsa_switch *ds, old_proto_ops = felix->tag_proto_ops; + if (proto_ops == old_proto_ops) + return 0; + err = proto_ops->setup(ds); if (err) goto setup_failed; diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c index 61ed317602e7..b4034b78c0ca 100644 --- a/drivers/net/dsa/ocelot/felix_vsc9959.c +++ b/drivers/net/dsa/ocelot/felix_vsc9959.c @@ -1137,6 +1137,7 @@ static void vsc9959_tas_min_gate_lengths(struct tc_taprio_qopt_offload *taprio, { struct tc_taprio_sched_entry *entry; u64 gate_len[OCELOT_NUM_TC]; + u8 gates_ever_opened = 0; int tc, i, n; /* Initialize arrays */ @@ -1164,16 +1165,28 @@ static void vsc9959_tas_min_gate_lengths(struct tc_taprio_qopt_offload *taprio, for (tc = 0; tc < OCELOT_NUM_TC; tc++) { if (entry->gate_mask & BIT(tc)) { gate_len[tc] += entry->interval; + gates_ever_opened |= BIT(tc); } else { /* Gate closes now, record a potential new * minimum and reinitialize length */ - if (min_gate_len[tc] > gate_len[tc]) + if (min_gate_len[tc] > gate_len[tc] && + gate_len[tc]) min_gate_len[tc] = gate_len[tc]; gate_len[tc] = 0; } } } + + /* min_gate_len[tc] actually tracks minimum *open* gate time, so for + * permanently closed gates, min_gate_len[tc] will still be U64_MAX. + * Therefore they are currently indistinguishable from permanently + * open gates. Overwrite the gate len with 0 when we know they're + * actually permanently closed, i.e. after the loop above. + */ + for (tc = 0; tc < OCELOT_NUM_TC; tc++) + if (!(gates_ever_opened & BIT(tc))) + min_gate_len[tc] = 0; } /* Update QSYS_PORT_MAX_SDU to make sure the static guard bands added by the diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c index e11cc29d3264..06508eebb585 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c @@ -265,12 +265,10 @@ static void aq_nic_service_timer_cb(struct timer_list *t) static void aq_nic_polling_timer_cb(struct timer_list *t) { struct aq_nic_s *self = from_timer(self, t, polling_timer); - struct aq_vec_s *aq_vec = NULL; unsigned int i = 0U; - for (i = 0U, aq_vec = self->aq_vec[0]; - self->aq_vecs > i; ++i, aq_vec = self->aq_vec[i]) - aq_vec_isr(i, (void *)aq_vec); + for (i = 0U; self->aq_vecs > i; ++i) + aq_vec_isr(i, (void *)self->aq_vec[i]); mod_timer(&self->polling_timer, jiffies + AQ_CFG_POLLING_TIMER_INTERVAL); @@ -1014,7 +1012,6 @@ int aq_nic_get_regs_count(struct aq_nic_s *self) u64 *aq_nic_get_stats(struct aq_nic_s *self, u64 *data) { - struct aq_vec_s *aq_vec = NULL; struct aq_stats_s *stats; unsigned int count = 0U; unsigned int i = 0U; @@ -1064,11 +1061,11 @@ u64 *aq_nic_get_stats(struct aq_nic_s *self, u64 *data) data += i; for (tc = 0U; tc < self->aq_nic_cfg.tcs; tc++) { - for (i = 0U, aq_vec = self->aq_vec[0]; - aq_vec && self->aq_vecs > i; - ++i, aq_vec = self->aq_vec[i]) { + for (i = 0U; self->aq_vecs > i; ++i) { + if (!self->aq_vec[i]) + break; data += count; - count = aq_vec_get_sw_stats(aq_vec, tc, data); + count = aq_vec_get_sw_stats(self->aq_vec[i], tc, data); } } @@ -1382,7 +1379,6 @@ int aq_nic_set_loopback(struct aq_nic_s *self) int aq_nic_stop(struct aq_nic_s *self) { - struct aq_vec_s *aq_vec = NULL; unsigned int i = 0U; netif_tx_disable(self->ndev); @@ -1400,9 +1396,8 @@ int aq_nic_stop(struct aq_nic_s *self) aq_ptp_irq_free(self); - for (i = 0U, aq_vec = self->aq_vec[0]; - self->aq_vecs > i; ++i, aq_vec = self->aq_vec[i]) - aq_vec_stop(aq_vec); + for (i = 0U; self->aq_vecs > i; ++i) + aq_vec_stop(self->aq_vec[i]); aq_ptp_ring_stop(self); diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c index 2dfc1e32bbb3..93580484a3f4 100644 --- a/drivers/net/ethernet/broadcom/bgmac.c +++ b/drivers/net/ethernet/broadcom/bgmac.c @@ -189,8 +189,8 @@ static netdev_tx_t bgmac_dma_tx_add(struct bgmac *bgmac, } slot->skb = skb; - ring->end += nr_frags + 1; netdev_sent_queue(net_dev, skb->len); + ring->end += nr_frags + 1; wmb(); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c index 14df8cfc2946..059f96f7a96f 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c @@ -21,7 +21,6 @@ #include "bnxt_ptp.h" #include "bnxt_coredump.h" #include "bnxt_nvm_defs.h" -#include "bnxt_ethtool.h" static void __bnxt_fw_recover(struct bnxt *bp) { diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c index c888ddee1fc4..7ded559842e8 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmmii.c +++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c @@ -393,6 +393,9 @@ int bcmgenet_mii_probe(struct net_device *dev) if (priv->internal_phy && !GENET_IS_V5(priv)) dev->phydev->irq = PHY_MAC_INTERRUPT; + /* Indicate that the MAC is responsible for PHY PM */ + dev->phydev->mac_managed_pm = true; + return 0; } diff --git a/drivers/net/ethernet/chelsio/inline_crypto/ch_ktls/chcr_ktls.c b/drivers/net/ethernet/chelsio/inline_crypto/ch_ktls/chcr_ktls.c index bfee0e4e54b1..da9973b711f4 100644 --- a/drivers/net/ethernet/chelsio/inline_crypto/ch_ktls/chcr_ktls.c +++ b/drivers/net/ethernet/chelsio/inline_crypto/ch_ktls/chcr_ktls.c @@ -1932,6 +1932,7 @@ static int chcr_ktls_xmit(struct sk_buff *skb, struct net_device *dev) int data_len, qidx, ret = 0, mss; struct tls_record_info *record; struct chcr_ktls_info *tx_info; + struct net_device *tls_netdev; struct tls_context *tls_ctx; struct sge_eth_txq *q; struct adapter *adap; @@ -1945,7 +1946,12 @@ static int chcr_ktls_xmit(struct sk_buff *skb, struct net_device *dev) mss = skb_is_gso(skb) ? skb_shinfo(skb)->gso_size : data_len; tls_ctx = tls_get_ctx(skb->sk); - if (unlikely(tls_ctx->netdev != dev)) + tls_netdev = rcu_dereference_bh(tls_ctx->netdev); + /* Don't quit on NULL: if tls_device_down is running in parallel, + * netdev might become NULL, even if tls_is_sk_tx_device_offloaded was + * true. Rather continue processing this packet. + */ + if (unlikely(tls_netdev && tls_netdev != dev)) goto out; tx_ctx = chcr_get_ktls_tx_context(tls_ctx); diff --git a/drivers/net/ethernet/engleder/tsnep_main.c b/drivers/net/ethernet/engleder/tsnep_main.c index cb069a0af7b9..a5f7152a1716 100644 --- a/drivers/net/ethernet/engleder/tsnep_main.c +++ b/drivers/net/ethernet/engleder/tsnep_main.c @@ -340,14 +340,14 @@ static int tsnep_tx_map(struct sk_buff *skb, struct tsnep_tx *tx, int count) return 0; } -static void tsnep_tx_unmap(struct tsnep_tx *tx, int count) +static void tsnep_tx_unmap(struct tsnep_tx *tx, int index, int count) { struct device *dmadev = tx->adapter->dmadev; struct tsnep_tx_entry *entry; int i; for (i = 0; i < count; i++) { - entry = &tx->entry[(tx->read + i) % TSNEP_RING_SIZE]; + entry = &tx->entry[(index + i) % TSNEP_RING_SIZE]; if (entry->len) { if (i == 0) @@ -395,7 +395,7 @@ static netdev_tx_t tsnep_xmit_frame_ring(struct sk_buff *skb, retval = tsnep_tx_map(skb, tx, count); if (retval != 0) { - tsnep_tx_unmap(tx, count); + tsnep_tx_unmap(tx, tx->write, count); dev_kfree_skb_any(entry->skb); entry->skb = NULL; @@ -464,7 +464,7 @@ static bool tsnep_tx_poll(struct tsnep_tx *tx, int napi_budget) if (skb_shinfo(entry->skb)->nr_frags > 0) count += skb_shinfo(entry->skb)->nr_frags; - tsnep_tx_unmap(tx, count); + tsnep_tx_unmap(tx, tx->read, count); if ((skb_shinfo(entry->skb)->tx_flags & SKBTX_IN_PROGRESS) && (__le32_to_cpu(entry->desc_wb->properties) & @@ -1282,7 +1282,7 @@ MODULE_DEVICE_TABLE(of, tsnep_of_match); static struct platform_driver tsnep_driver = { .driver = { .name = TSNEP, - .of_match_table = of_match_ptr(tsnep_of_match), + .of_match_table = tsnep_of_match, }, .probe = tsnep_probe, .remove = tsnep_remove, diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c index cd9ec80522e7..75d51572693d 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c @@ -1660,8 +1660,8 @@ static int dpaa2_eth_add_bufs(struct dpaa2_eth_priv *priv, buf_array[i] = addr; /* tracing point */ - trace_dpaa2_eth_buf_seed(priv->net_dev, - page, DPAA2_ETH_RX_BUF_RAW_SIZE, + trace_dpaa2_eth_buf_seed(priv->net_dev, page_address(page), + DPAA2_ETH_RX_BUF_RAW_SIZE, addr, priv->rx_buf_size, bpid); } diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c index 6809b8b4c556..7282a826d81e 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c @@ -2580,6 +2580,12 @@ static void __rvu_flr_handler(struct rvu *rvu, u16 pcifunc) rvu_blklf_teardown(rvu, pcifunc, BLKADDR_NPA); rvu_reset_lmt_map_tbl(rvu, pcifunc); rvu_detach_rsrcs(rvu, NULL, pcifunc); + /* In scenarios where PF/VF drivers detach NIXLF without freeing MCAM + * entries, check and free the MCAM entries explicitly to avoid leak. + * Since LF is detached use LF number as -1. + */ + rvu_npc_free_mcam_entries(rvu, pcifunc, -1); + mutex_unlock(&rvu->flr_lock); } diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c index 583ead4dd246..1e348fd0d930 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c @@ -1097,6 +1097,9 @@ static void npc_enadis_default_entries(struct rvu *rvu, u16 pcifunc, void rvu_npc_disable_default_entries(struct rvu *rvu, u16 pcifunc, int nixlf) { + if (nixlf < 0) + return; + npc_enadis_default_entries(rvu, pcifunc, nixlf, false); /* Delete multicast and promisc MCAM entries */ @@ -1136,6 +1139,9 @@ bool rvu_npc_enable_mcam_by_entry_index(struct rvu *rvu, int entry, int intf, bo void rvu_npc_enable_default_entries(struct rvu *rvu, u16 pcifunc, int nixlf) { + if (nixlf < 0) + return; + /* Enables only broadcast match entry. Promisc/Allmulti are enabled * in set_rx_mode mbox handler. */ @@ -1675,7 +1681,7 @@ static void npc_load_kpu_profile(struct rvu *rvu) * Firmware database method. * Default KPU profile. */ - if (!request_firmware(&fw, kpu_profile, rvu->dev)) { + if (!request_firmware_direct(&fw, kpu_profile, rvu->dev)) { dev_info(rvu->dev, "Loading KPU profile from firmware: %s\n", kpu_profile); rvu->kpu_fwdata = kzalloc(fw->size, GFP_KERNEL); @@ -1939,6 +1945,7 @@ static void rvu_npc_hw_init(struct rvu *rvu, int blkaddr) static void rvu_npc_setup_interfaces(struct rvu *rvu, int blkaddr) { + struct npc_mcam_kex *mkex = rvu->kpu.mkex; struct npc_mcam *mcam = &rvu->hw->mcam; struct rvu_hwinfo *hw = rvu->hw; u64 nibble_ena, rx_kex, tx_kex; @@ -1951,15 +1958,15 @@ static void rvu_npc_setup_interfaces(struct rvu *rvu, int blkaddr) mcam->counters.max--; mcam->rx_miss_act_cntr = mcam->counters.max; - rx_kex = npc_mkex_default.keyx_cfg[NIX_INTF_RX]; - tx_kex = npc_mkex_default.keyx_cfg[NIX_INTF_TX]; + rx_kex = mkex->keyx_cfg[NIX_INTF_RX]; + tx_kex = mkex->keyx_cfg[NIX_INTF_TX]; nibble_ena = FIELD_GET(NPC_PARSE_NIBBLE, rx_kex); nibble_ena = rvu_npc_get_tx_nibble_cfg(rvu, nibble_ena); if (nibble_ena) { tx_kex &= ~NPC_PARSE_NIBBLE; tx_kex |= FIELD_PREP(NPC_PARSE_NIBBLE, nibble_ena); - npc_mkex_default.keyx_cfg[NIX_INTF_TX] = tx_kex; + mkex->keyx_cfg[NIX_INTF_TX] = tx_kex; } /* Configure RX interfaces */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c index a400aa22da79..7c4e1acd0f77 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c @@ -467,7 +467,8 @@ do { \ NPC_SCAN_HDR(NPC_VLAN_TAG1, NPC_LID_LB, NPC_LT_LB_CTAG, 2, 2); NPC_SCAN_HDR(NPC_VLAN_TAG2, NPC_LID_LB, NPC_LT_LB_STAG_QINQ, 2, 2); NPC_SCAN_HDR(NPC_DMAC, NPC_LID_LA, la_ltype, la_start, 6); - NPC_SCAN_HDR(NPC_SMAC, NPC_LID_LA, la_ltype, la_start, 6); + /* SMAC follows the DMAC(which is 6 bytes) */ + NPC_SCAN_HDR(NPC_SMAC, NPC_LID_LA, la_ltype, la_start + 6, 6); /* PF_FUNC is 2 bytes at 0th byte of NPC_LT_LA_IH_NIX_ETHER */ NPC_SCAN_HDR(NPC_PF_FUNC, NPC_LID_LA, NPC_LT_LA_IH_NIX_ETHER, 0, 2); } diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c index fb8db5888d2f..d686c7b6252f 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c @@ -632,6 +632,12 @@ int otx2_txschq_config(struct otx2_nic *pfvf, int lvl) req->num_regs++; req->reg[1] = NIX_AF_TL3X_SCHEDULE(schq); req->regval[1] = dwrr_val; + if (lvl == hw->txschq_link_cfg_lvl) { + req->num_regs++; + req->reg[2] = NIX_AF_TL3_TL2X_LINKX_CFG(schq, hw->tx_link); + /* Enable this queue and backpressure */ + req->regval[2] = BIT_ULL(13) | BIT_ULL(12); + } } else if (lvl == NIX_TXSCH_LVL_TL2) { parent = hw->txschq_list[NIX_TXSCH_LVL_TL1][0]; req->reg[0] = NIX_AF_TL2X_PARENT(schq); @@ -641,11 +647,12 @@ int otx2_txschq_config(struct otx2_nic *pfvf, int lvl) req->reg[1] = NIX_AF_TL2X_SCHEDULE(schq); req->regval[1] = TXSCH_TL1_DFLT_RR_PRIO << 24 | dwrr_val; - req->num_regs++; - req->reg[2] = NIX_AF_TL3_TL2X_LINKX_CFG(schq, hw->tx_link); - /* Enable this queue and backpressure */ - req->regval[2] = BIT_ULL(13) | BIT_ULL(12); - + if (lvl == hw->txschq_link_cfg_lvl) { + req->num_regs++; + req->reg[2] = NIX_AF_TL3_TL2X_LINKX_CFG(schq, hw->tx_link); + /* Enable this queue and backpressure */ + req->regval[2] = BIT_ULL(13) | BIT_ULL(12); + } } else if (lvl == NIX_TXSCH_LVL_TL1) { /* Default config for TL1. * For VF this is always ignored. @@ -1591,6 +1598,8 @@ void mbox_handler_nix_txsch_alloc(struct otx2_nic *pf, for (schq = 0; schq < rsp->schq[lvl]; schq++) pf->hw.txschq_list[lvl][schq] = rsp->schq_list[lvl][schq]; + + pf->hw.txschq_link_cfg_lvl = rsp->link_cfg_lvl; } EXPORT_SYMBOL(mbox_handler_nix_txsch_alloc); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h index e795f9ee76dd..b28029cc4316 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h @@ -195,6 +195,7 @@ struct otx2_hw { u16 sqb_size; /* NIX */ + u8 txschq_link_cfg_lvl; u16 txschq_list[NIX_TXSCH_LVL_CNT][MAX_TXSCHQ_PER_FUNC]; u16 matchall_ipolicer; u32 dwrr_mtu; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.c index d87bbb0be7c8..e6f64d890fb3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.c @@ -506,7 +506,7 @@ int mlx5e_tc_tun_create_header_ipv6(struct mlx5e_priv *priv, int err; attr.ttl = tun_key->ttl; - attr.fl.fl6.flowlabel = ip6_make_flowinfo(RT_TOS(tun_key->tos), tun_key->label); + attr.fl.fl6.flowlabel = ip6_make_flowinfo(tun_key->tos, tun_key->label); attr.fl.fl6.daddr = tun_key->u.ipv6.dst; attr.fl.fl6.saddr = tun_key->u.ipv6.src; @@ -620,7 +620,7 @@ int mlx5e_tc_tun_update_header_ipv6(struct mlx5e_priv *priv, attr.ttl = tun_key->ttl; - attr.fl.fl6.flowlabel = ip6_make_flowinfo(RT_TOS(tun_key->tos), tun_key->label); + attr.fl.fl6.flowlabel = ip6_make_flowinfo(tun_key->tos, tun_key->label); attr.fl.fl6.daddr = tun_key->u.ipv6.dst; attr.fl.fl6.saddr = tun_key->u.ipv6.src; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c index 6b6c7044b64a..0aef69527226 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c @@ -808,6 +808,7 @@ bool mlx5e_ktls_handle_tx_skb(struct net_device *netdev, struct mlx5e_txqsq *sq, { struct mlx5e_ktls_offload_context_tx *priv_tx; struct mlx5e_sq_stats *stats = sq->stats; + struct net_device *tls_netdev; struct tls_context *tls_ctx; int datalen; u32 seq; @@ -819,7 +820,12 @@ bool mlx5e_ktls_handle_tx_skb(struct net_device *netdev, struct mlx5e_txqsq *sq, mlx5e_tx_mpwqe_ensure_complete(sq); tls_ctx = tls_get_ctx(skb->sk); - if (WARN_ON_ONCE(tls_ctx->netdev != netdev)) + tls_netdev = rcu_dereference_bh(tls_ctx->netdev); + /* Don't WARN on NULL: if tls_device_down is running in parallel, + * netdev might become NULL, even if tls_is_sk_tx_device_offloaded was + * true. Rather continue processing this packet. + */ + if (WARN_ON_ONCE(tls_netdev && tls_netdev != netdev)) goto err_out; priv_tx = mlx5e_get_ktls_tx_priv_ctx(tls_ctx); diff --git a/drivers/net/ethernet/mellanox/mlxsw/minimal.c b/drivers/net/ethernet/mellanox/mlxsw/minimal.c index d9bf584234a6..bb1cd4bae82e 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/minimal.c +++ b/drivers/net/ethernet/mellanox/mlxsw/minimal.c @@ -328,7 +328,6 @@ static void mlxsw_m_port_module_unmap(struct mlxsw_m *mlxsw_m, u8 module) static int mlxsw_m_ports_create(struct mlxsw_m *mlxsw_m) { unsigned int max_ports = mlxsw_core_max_ports(mlxsw_m->core); - struct devlink *devlink = priv_to_devlink(mlxsw_m->core); u8 last_module = max_ports; int i; int err; @@ -357,7 +356,6 @@ static int mlxsw_m_ports_create(struct mlxsw_m *mlxsw_m) } /* Create port objects for each valid entry */ - devl_lock(devlink); for (i = 0; i < mlxsw_m->max_ports; i++) { if (mlxsw_m->module_to_port[i] > 0) { err = mlxsw_m_port_create(mlxsw_m, @@ -367,7 +365,6 @@ static int mlxsw_m_ports_create(struct mlxsw_m *mlxsw_m) goto err_module_to_port_create; } } - devl_unlock(devlink); return 0; @@ -377,7 +374,6 @@ err_module_to_port_create: mlxsw_m_port_remove(mlxsw_m, mlxsw_m->module_to_port[i]); } - devl_unlock(devlink); i = max_ports; err_module_to_port_map: for (i--; i > 0; i--) @@ -390,10 +386,8 @@ err_module_to_port_alloc: static void mlxsw_m_ports_remove(struct mlxsw_m *mlxsw_m) { - struct devlink *devlink = priv_to_devlink(mlxsw_m->core); int i; - devl_lock(devlink); for (i = 0; i < mlxsw_m->max_ports; i++) { if (mlxsw_m->module_to_port[i] > 0) { mlxsw_m_port_remove(mlxsw_m, @@ -401,7 +395,6 @@ static void mlxsw_m_ports_remove(struct mlxsw_m *mlxsw_m) mlxsw_m_port_module_unmap(mlxsw_m, i); } } - devl_unlock(devlink); kfree(mlxsw_m->module_to_port); kfree(mlxsw_m->ports); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index c922dfab8080..eeb1455a4e5d 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -1395,6 +1395,8 @@ nfp_port_get_module_info(struct net_device *netdev, u8 data; port = nfp_port_from_netdev(netdev); + /* update port state to get latest interface */ + set_bit(NFP_PORT_CHANGED, &port->flags); eth_port = nfp_port_get_eth_port(port); if (!eth_port) return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c index 34c0d2ddf9ef..a8286d0032d1 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c @@ -874,7 +874,6 @@ area_cache_get(struct nfp_cpp *cpp, u32 id, } /* Adjust the start address to be cache size aligned */ - cache->id = id; cache->addr = addr & ~(u64)(cache->size - 1); /* Re-init to the new ID and address */ @@ -894,6 +893,8 @@ area_cache_get(struct nfp_cpp *cpp, u32 id, return NULL; } + cache->id = id; + exit: /* Adjust offset */ *offset = addr - cache->addr; diff --git a/drivers/net/ethernet/wangxun/Kconfig b/drivers/net/ethernet/wangxun/Kconfig index baa1f0a5cc37..b4a4fa0a58f8 100644 --- a/drivers/net/ethernet/wangxun/Kconfig +++ b/drivers/net/ethernet/wangxun/Kconfig @@ -7,12 +7,12 @@ config NET_VENDOR_WANGXUN bool "Wangxun devices" default y help - If you have a network (Ethernet) card belonging to this class, say Y. + If you have a network (Ethernet) card from Wangxun(R), say Y. Note that the answer to this question doesn't directly affect the kernel: saying N will just cause the configurator to skip all - the questions about Intel cards. If you say Y, you will be asked for - your specific card in the following questions. + the questions about Wangxun(R) cards. If you say Y, you will + be asked for your specific card in the following questions. if NET_VENDOR_WANGXUN diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 018d365f9deb..7962c37b3f14 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -797,7 +797,8 @@ static struct rtable *geneve_get_v4_rt(struct sk_buff *skb, struct geneve_sock *gs4, struct flowi4 *fl4, const struct ip_tunnel_info *info, - __be16 dport, __be16 sport) + __be16 dport, __be16 sport, + __u8 *full_tos) { bool use_cache = ip_tunnel_dst_cache_usable(skb, info); struct geneve_dev *geneve = netdev_priv(dev); @@ -823,6 +824,8 @@ static struct rtable *geneve_get_v4_rt(struct sk_buff *skb, use_cache = false; } fl4->flowi4_tos = RT_TOS(tos); + if (full_tos) + *full_tos = tos; dst_cache = (struct dst_cache *)&info->dst_cache; if (use_cache) { @@ -876,8 +879,7 @@ static struct dst_entry *geneve_get_v6_dst(struct sk_buff *skb, use_cache = false; } - fl6->flowlabel = ip6_make_flowinfo(RT_TOS(prio), - info->key.label); + fl6->flowlabel = ip6_make_flowinfo(prio, info->key.label); dst_cache = (struct dst_cache *)&info->dst_cache; if (use_cache) { dst = dst_cache_get_ip6(dst_cache, &fl6->saddr); @@ -911,6 +913,7 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, const struct ip_tunnel_key *key = &info->key; struct rtable *rt; struct flowi4 fl4; + __u8 full_tos; __u8 tos, ttl; __be16 df = 0; __be16 sport; @@ -921,7 +924,7 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true); rt = geneve_get_v4_rt(skb, dev, gs4, &fl4, info, - geneve->cfg.info.key.tp_dst, sport); + geneve->cfg.info.key.tp_dst, sport, &full_tos); if (IS_ERR(rt)) return PTR_ERR(rt); @@ -965,7 +968,7 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, df = key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0; } else { - tos = ip_tunnel_ecn_encap(fl4.flowi4_tos, ip_hdr(skb), skb); + tos = ip_tunnel_ecn_encap(full_tos, ip_hdr(skb), skb); if (geneve->cfg.ttl_inherit) ttl = ip_tunnel_get_ttl(ip_hdr(skb), skb); else @@ -1149,7 +1152,7 @@ static int geneve_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb) 1, USHRT_MAX, true); rt = geneve_get_v4_rt(skb, dev, gs4, &fl4, info, - geneve->cfg.info.key.tp_dst, sport); + geneve->cfg.info.key.tp_dst, sport, NULL); if (IS_ERR(rt)) return PTR_ERR(rt); diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index f1683ce6b561..ee6087e7b2bf 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -162,6 +162,19 @@ static struct macsec_rx_sa *macsec_rxsa_get(struct macsec_rx_sa __rcu *ptr) return sa; } +static struct macsec_rx_sa *macsec_active_rxsa_get(struct macsec_rx_sc *rx_sc) +{ + struct macsec_rx_sa *sa = NULL; + int an; + + for (an = 0; an < MACSEC_NUM_AN; an++) { + sa = macsec_rxsa_get(rx_sc->sa[an]); + if (sa) + break; + } + return sa; +} + static void free_rx_sc_rcu(struct rcu_head *head) { struct macsec_rx_sc *rx_sc = container_of(head, struct macsec_rx_sc, rcu_head); @@ -500,18 +513,28 @@ static void macsec_encrypt_finish(struct sk_buff *skb, struct net_device *dev) skb->protocol = eth_hdr(skb)->h_proto; } +static unsigned int macsec_msdu_len(struct sk_buff *skb) +{ + struct macsec_dev *macsec = macsec_priv(skb->dev); + struct macsec_secy *secy = &macsec->secy; + bool sci_present = macsec_skb_cb(skb)->has_sci; + + return skb->len - macsec_hdr_len(sci_present) - secy->icv_len; +} + static void macsec_count_tx(struct sk_buff *skb, struct macsec_tx_sc *tx_sc, struct macsec_tx_sa *tx_sa) { + unsigned int msdu_len = macsec_msdu_len(skb); struct pcpu_tx_sc_stats *txsc_stats = this_cpu_ptr(tx_sc->stats); u64_stats_update_begin(&txsc_stats->syncp); if (tx_sc->encrypt) { - txsc_stats->stats.OutOctetsEncrypted += skb->len; + txsc_stats->stats.OutOctetsEncrypted += msdu_len; txsc_stats->stats.OutPktsEncrypted++; this_cpu_inc(tx_sa->stats->OutPktsEncrypted); } else { - txsc_stats->stats.OutOctetsProtected += skb->len; + txsc_stats->stats.OutOctetsProtected += msdu_len; txsc_stats->stats.OutPktsProtected++; this_cpu_inc(tx_sa->stats->OutPktsProtected); } @@ -541,9 +564,10 @@ static void macsec_encrypt_done(struct crypto_async_request *base, int err) aead_request_free(macsec_skb_cb(skb)->req); rcu_read_lock_bh(); - macsec_encrypt_finish(skb, dev); macsec_count_tx(skb, &macsec->secy.tx_sc, macsec_skb_cb(skb)->tx_sa); - len = skb->len; + /* packet is encrypted/protected so tx_bytes must be calculated */ + len = macsec_msdu_len(skb) + 2 * ETH_ALEN; + macsec_encrypt_finish(skb, dev); ret = dev_queue_xmit(skb); count_tx(dev, ret, len); rcu_read_unlock_bh(); @@ -702,6 +726,7 @@ static struct sk_buff *macsec_encrypt(struct sk_buff *skb, macsec_skb_cb(skb)->req = req; macsec_skb_cb(skb)->tx_sa = tx_sa; + macsec_skb_cb(skb)->has_sci = sci_present; aead_request_set_callback(req, 0, macsec_encrypt_done, skb); dev_hold(skb->dev); @@ -743,15 +768,17 @@ static bool macsec_post_decrypt(struct sk_buff *skb, struct macsec_secy *secy, u u64_stats_update_begin(&rxsc_stats->syncp); rxsc_stats->stats.InPktsLate++; u64_stats_update_end(&rxsc_stats->syncp); + secy->netdev->stats.rx_dropped++; return false; } if (secy->validate_frames != MACSEC_VALIDATE_DISABLED) { + unsigned int msdu_len = macsec_msdu_len(skb); u64_stats_update_begin(&rxsc_stats->syncp); if (hdr->tci_an & MACSEC_TCI_E) - rxsc_stats->stats.InOctetsDecrypted += skb->len; + rxsc_stats->stats.InOctetsDecrypted += msdu_len; else - rxsc_stats->stats.InOctetsValidated += skb->len; + rxsc_stats->stats.InOctetsValidated += msdu_len; u64_stats_update_end(&rxsc_stats->syncp); } @@ -764,6 +791,8 @@ static bool macsec_post_decrypt(struct sk_buff *skb, struct macsec_secy *secy, u u64_stats_update_begin(&rxsc_stats->syncp); rxsc_stats->stats.InPktsNotValid++; u64_stats_update_end(&rxsc_stats->syncp); + this_cpu_inc(rx_sa->stats->InPktsNotValid); + secy->netdev->stats.rx_errors++; return false; } @@ -856,9 +885,9 @@ static void macsec_decrypt_done(struct crypto_async_request *base, int err) macsec_finalize_skb(skb, macsec->secy.icv_len, macsec_extra_len(macsec_skb_cb(skb)->has_sci)); + len = skb->len; macsec_reset_skb(skb, macsec->secy.netdev); - len = skb->len; if (gro_cells_receive(&macsec->gro_cells, skb) == NET_RX_SUCCESS) count_rx(dev, len); @@ -1049,6 +1078,7 @@ static enum rx_handler_result handle_not_macsec(struct sk_buff *skb) u64_stats_update_begin(&secy_stats->syncp); secy_stats->stats.InPktsNoTag++; u64_stats_update_end(&secy_stats->syncp); + macsec->secy.netdev->stats.rx_dropped++; continue; } @@ -1158,6 +1188,7 @@ static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb) u64_stats_update_begin(&secy_stats->syncp); secy_stats->stats.InPktsBadTag++; u64_stats_update_end(&secy_stats->syncp); + secy->netdev->stats.rx_errors++; goto drop_nosa; } @@ -1168,11 +1199,15 @@ static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb) /* If validateFrames is Strict or the C bit in the * SecTAG is set, discard */ + struct macsec_rx_sa *active_rx_sa = macsec_active_rxsa_get(rx_sc); if (hdr->tci_an & MACSEC_TCI_C || secy->validate_frames == MACSEC_VALIDATE_STRICT) { u64_stats_update_begin(&rxsc_stats->syncp); rxsc_stats->stats.InPktsNotUsingSA++; u64_stats_update_end(&rxsc_stats->syncp); + secy->netdev->stats.rx_errors++; + if (active_rx_sa) + this_cpu_inc(active_rx_sa->stats->InPktsNotUsingSA); goto drop_nosa; } @@ -1182,6 +1217,8 @@ static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb) u64_stats_update_begin(&rxsc_stats->syncp); rxsc_stats->stats.InPktsUnusedSA++; u64_stats_update_end(&rxsc_stats->syncp); + if (active_rx_sa) + this_cpu_inc(active_rx_sa->stats->InPktsUnusedSA); goto deliver; } @@ -1202,6 +1239,7 @@ static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb) u64_stats_update_begin(&rxsc_stats->syncp); rxsc_stats->stats.InPktsLate++; u64_stats_update_end(&rxsc_stats->syncp); + macsec->secy.netdev->stats.rx_dropped++; goto drop; } } @@ -1230,6 +1268,7 @@ static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb) deliver: macsec_finalize_skb(skb, secy->icv_len, macsec_extra_len(macsec_skb_cb(skb)->has_sci)); + len = skb->len; macsec_reset_skb(skb, secy->netdev); if (rx_sa) @@ -1237,7 +1276,6 @@ deliver: macsec_rxsc_put(rx_sc); skb_orphan(skb); - len = skb->len; ret = gro_cells_receive(&macsec->gro_cells, skb); if (ret == NET_RX_SUCCESS) count_rx(dev, len); @@ -1279,6 +1317,7 @@ nosci: u64_stats_update_begin(&secy_stats->syncp); secy_stats->stats.InPktsNoSCI++; u64_stats_update_end(&secy_stats->syncp); + macsec->secy.netdev->stats.rx_errors++; continue; } @@ -3404,6 +3443,7 @@ static netdev_tx_t macsec_start_xmit(struct sk_buff *skb, return NETDEV_TX_OK; } + len = skb->len; skb = macsec_encrypt(skb, dev); if (IS_ERR(skb)) { if (PTR_ERR(skb) != -EINPROGRESS) @@ -3414,7 +3454,6 @@ static netdev_tx_t macsec_start_xmit(struct sk_buff *skb, macsec_count_tx(skb, &macsec->secy.tx_sc, macsec_skb_cb(skb)->tx_sa); macsec_encrypt_finish(skb, dev); - len = skb->len; ret = dev_queue_xmit(skb); count_tx(dev, ret, len); return ret; @@ -3662,6 +3701,7 @@ static void macsec_get_stats64(struct net_device *dev, s->rx_dropped = dev->stats.rx_dropped; s->tx_dropped = dev->stats.tx_dropped; + s->rx_errors = dev->stats.rx_errors; } static int macsec_get_iflink(const struct net_device *dev) diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c index 1e38039c5c56..6939563d3b7c 100644 --- a/drivers/net/phy/dp83867.c +++ b/drivers/net/phy/dp83867.c @@ -535,7 +535,7 @@ static int dp83867_of_init_io_impedance(struct phy_device *phydev) cell = of_nvmem_cell_get(of_node, "io_impedance_ctrl"); if (IS_ERR(cell)) { ret = PTR_ERR(cell); - if (ret != -ENOENT) + if (ret != -ENOENT && ret != -EOPNOTSUPP) return phydev_err_probe(phydev, ret, "failed to get nvmem cell io_impedance_ctrl\n"); diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c index 29b1df03f3e8..a87a4b3ffce4 100644 --- a/drivers/net/phy/phy-c45.c +++ b/drivers/net/phy/phy-c45.c @@ -190,44 +190,42 @@ EXPORT_SYMBOL_GPL(genphy_c45_pma_setup_forced); */ static int genphy_c45_baset1_an_config_aneg(struct phy_device *phydev) { + u16 adv_l_mask, adv_l = 0; + u16 adv_m_mask, adv_m = 0; int changed = 0; - u16 adv_l = 0; - u16 adv_m = 0; int ret; + adv_l_mask = MDIO_AN_T1_ADV_L_FORCE_MS | MDIO_AN_T1_ADV_L_PAUSE_CAP | + MDIO_AN_T1_ADV_L_PAUSE_ASYM; + adv_m_mask = MDIO_AN_T1_ADV_M_MST | MDIO_AN_T1_ADV_M_B10L; + switch (phydev->master_slave_set) { case MASTER_SLAVE_CFG_MASTER_FORCE: + adv_m |= MDIO_AN_T1_ADV_M_MST; + fallthrough; case MASTER_SLAVE_CFG_SLAVE_FORCE: adv_l |= MDIO_AN_T1_ADV_L_FORCE_MS; break; case MASTER_SLAVE_CFG_MASTER_PREFERRED: + adv_m |= MDIO_AN_T1_ADV_M_MST; + fallthrough; case MASTER_SLAVE_CFG_SLAVE_PREFERRED: break; case MASTER_SLAVE_CFG_UNKNOWN: case MASTER_SLAVE_CFG_UNSUPPORTED: - return 0; + /* if master/slave role is not specified, do not overwrite it */ + adv_l_mask &= ~MDIO_AN_T1_ADV_L_FORCE_MS; + adv_m_mask &= ~MDIO_AN_T1_ADV_M_MST; + break; default: phydev_warn(phydev, "Unsupported Master/Slave mode\n"); return -EOPNOTSUPP; } - switch (phydev->master_slave_set) { - case MASTER_SLAVE_CFG_MASTER_FORCE: - case MASTER_SLAVE_CFG_MASTER_PREFERRED: - adv_m |= MDIO_AN_T1_ADV_M_MST; - break; - case MASTER_SLAVE_CFG_SLAVE_FORCE: - case MASTER_SLAVE_CFG_SLAVE_PREFERRED: - break; - default: - break; - } - adv_l |= linkmode_adv_to_mii_t1_adv_l_t(phydev->advertising); ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_T1_ADV_L, - (MDIO_AN_T1_ADV_L_FORCE_MS | MDIO_AN_T1_ADV_L_PAUSE_CAP - | MDIO_AN_T1_ADV_L_PAUSE_ASYM), adv_l); + adv_l_mask, adv_l); if (ret < 0) return ret; if (ret > 0) @@ -236,7 +234,7 @@ static int genphy_c45_baset1_an_config_aneg(struct phy_device *phydev) adv_m |= linkmode_adv_to_mii_t1_adv_m_t(phydev->advertising); ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_T1_ADV_M, - MDIO_AN_T1_ADV_M_MST | MDIO_AN_T1_ADV_M_B10L, adv_m); + adv_m_mask, adv_m); if (ret < 0) return ret; if (ret > 0) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index a74b320f5b27..0c6efd792690 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -316,6 +316,12 @@ static __maybe_unused int mdio_bus_phy_resume(struct device *dev) phydev->suspended_by_mdio_bus = 0; + /* If we managed to get here with the PHY state machine in a state other + * than PHY_HALTED this is an indication that something went wrong and + * we should most likely be using MAC managed PM and we are not. + */ + WARN_ON(phydev->state != PHY_HALTED && !phydev->mac_managed_pm); + ret = phy_init_hw(phydev); if (ret < 0) return ret; diff --git a/drivers/net/plip/plip.c b/drivers/net/plip/plip.c index dafd3e9ebbf8..c8791e9b451d 100644 --- a/drivers/net/plip/plip.c +++ b/drivers/net/plip/plip.c @@ -1111,7 +1111,7 @@ plip_open(struct net_device *dev) /* Any address will do - we take the first. We already have the first two bytes filled with 0xfc, from plip_init_dev(). */ - const struct in_ifaddr *ifa = rcu_dereference(in_dev->ifa_list); + const struct in_ifaddr *ifa = rtnl_dereference(in_dev->ifa_list); if (ifa != NULL) { dev_addr_mod(dev, 2, &ifa->ifa_local, 4); } diff --git a/drivers/net/tap.c b/drivers/net/tap.c index c3d42062559d..9e75ed3f08ce 100644 --- a/drivers/net/tap.c +++ b/drivers/net/tap.c @@ -716,10 +716,20 @@ static ssize_t tap_get_user(struct tap_queue *q, void *msg_control, skb_reset_mac_header(skb); skb->protocol = eth_hdr(skb)->h_proto; + rcu_read_lock(); + tap = rcu_dereference(q->tap); + if (!tap) { + kfree_skb(skb); + rcu_read_unlock(); + return total_len; + } + skb->dev = tap->dev; + if (vnet_hdr_len) { err = virtio_net_hdr_to_skb(skb, &vnet_hdr, tap_is_little_endian(q)); if (err) { + rcu_read_unlock(); drop_reason = SKB_DROP_REASON_DEV_HDR; goto err_kfree; } @@ -732,8 +742,6 @@ static ssize_t tap_get_user(struct tap_queue *q, void *msg_control, __vlan_get_protocol(skb, skb->protocol, &depth) != 0) skb_set_network_header(skb, depth); - rcu_read_lock(); - tap = rcu_dereference(q->tap); /* copy skb_ubuf_info for callback when skb has no error */ if (zerocopy) { skb_zcopy_init(skb, msg_control); @@ -742,14 +750,8 @@ static ssize_t tap_get_user(struct tap_queue *q, void *msg_control, uarg->callback(NULL, uarg, false); } - if (tap) { - skb->dev = tap->dev; - dev_queue_xmit(skb); - } else { - kfree_skb(skb); - } + dev_queue_xmit(skb); rcu_read_unlock(); - return total_len; err_kfree: diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c index 0ad468a00064..aff39bf3161d 100644 --- a/drivers/net/usb/ax88179_178a.c +++ b/drivers/net/usb/ax88179_178a.c @@ -1680,7 +1680,7 @@ static const struct driver_info ax88179_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1693,7 +1693,7 @@ static const struct driver_info ax88178a_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1706,7 +1706,7 @@ static const struct driver_info cypress_GX3_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1719,7 +1719,7 @@ static const struct driver_info dlink_dub1312_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1732,7 +1732,7 @@ static const struct driver_info sitecom_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1745,7 +1745,7 @@ static const struct driver_info samsung_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1758,7 +1758,7 @@ static const struct driver_info lenovo_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1771,7 +1771,7 @@ static const struct driver_info belkin_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1784,7 +1784,7 @@ static const struct driver_info toshiba_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1797,7 +1797,7 @@ static const struct driver_info mct_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1810,7 +1810,7 @@ static const struct driver_info at_umc2000_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1823,7 +1823,7 @@ static const struct driver_info at_umc200_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1836,7 +1836,7 @@ static const struct driver_info at_umc2000sp_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 571a399c195d..709e3c59e340 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -1390,6 +1390,8 @@ static const struct usb_device_id products[] = { {QMI_QUIRK_SET_DTR(0x1e2d, 0x00b0, 4)}, /* Cinterion CLS8 */ {QMI_FIXED_INTF(0x1e2d, 0x00b7, 0)}, /* Cinterion MV31 RmNet */ {QMI_FIXED_INTF(0x1e2d, 0x00b9, 0)}, /* Cinterion MV31 RmNet based on new baseline */ + {QMI_FIXED_INTF(0x1e2d, 0x00f3, 0)}, /* Cinterion MV32-W-A RmNet */ + {QMI_FIXED_INTF(0x1e2d, 0x00f4, 0)}, /* Cinterion MV32-W-B RmNet */ {QMI_FIXED_INTF(0x413c, 0x81a2, 8)}, /* Dell Wireless 5806 Gobi(TM) 4G LTE Mobile Broadband Card */ {QMI_FIXED_INTF(0x413c, 0x81a3, 8)}, /* Dell Wireless 5570 HSPA+ (42Mbps) Mobile Broadband Card */ {QMI_FIXED_INTF(0x413c, 0x81a4, 8)}, /* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */ diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 2cb833b3006a..466da01ba2e3 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -312,7 +312,6 @@ static bool veth_skb_is_eligible_for_gro(const struct net_device *dev, static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev) { struct veth_priv *rcv_priv, *priv = netdev_priv(dev); - struct netdev_queue *queue = NULL; struct veth_rq *rq = NULL; struct net_device *rcv; int length = skb->len; @@ -330,7 +329,6 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev) rxq = skb_get_queue_mapping(skb); if (rxq < rcv->real_num_rx_queues) { rq = &rcv_priv->rq[rxq]; - queue = netdev_get_tx_queue(dev, rxq); /* The napi pointer is available when an XDP program is * attached or when GRO is enabled @@ -342,8 +340,6 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev) skb_tx_timestamp(skb); if (likely(veth_forward_skb(rcv, skb, rq, use_napi) == NET_RX_SUCCESS)) { - if (queue) - txq_trans_cond_update(queue); if (!use_napi) dev_lstats_add(dev, length); } else { diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index ec8e1b3108c3..d934774e9733 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -135,6 +135,9 @@ struct send_queue { struct virtnet_sq_stats stats; struct napi_struct napi; + + /* Record whether sq is in reset state. */ + bool reset; }; /* Internal representation of a receive virtqueue */ @@ -267,6 +270,12 @@ struct virtnet_info { u8 duplex; u32 speed; + /* Interrupt coalescing settings */ + u32 tx_usecs; + u32 rx_usecs; + u32 tx_max_packets; + u32 rx_max_packets; + unsigned long guest_offloads; unsigned long guest_offloads_capable; @@ -284,6 +293,9 @@ struct padded_vnet_hdr { char padding[12]; }; +static void virtnet_rq_free_unused_buf(struct virtqueue *vq, void *buf); +static void virtnet_sq_free_unused_buf(struct virtqueue *vq, void *buf); + static bool is_xdp_frame(void *ptr) { return (unsigned long)ptr & VIRTIO_XDP_FLAG; @@ -1057,8 +1069,11 @@ static struct sk_buff *receive_mergeable(struct net_device *dev, case XDP_TX: stats->xdp_tx++; xdpf = xdp_convert_buff_to_frame(&xdp); - if (unlikely(!xdpf)) + if (unlikely(!xdpf)) { + if (unlikely(xdp_page != page)) + put_page(xdp_page); goto err_xdp; + } err = virtnet_xdp_xmit(dev, 1, &xdpf, 0); if (unlikely(!err)) { xdp_return_frame_rx_napi(xdpf); @@ -1625,6 +1640,11 @@ static void virtnet_poll_cleantx(struct receive_queue *rq) return; if (__netif_tx_trylock(txq)) { + if (sq->reset) { + __netif_tx_unlock(txq); + return; + } + do { virtqueue_disable_cb(sq->vq); free_old_xmit_skbs(sq, true); @@ -1872,6 +1892,70 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; } +static int virtnet_rx_resize(struct virtnet_info *vi, + struct receive_queue *rq, u32 ring_num) +{ + bool running = netif_running(vi->dev); + int err, qindex; + + qindex = rq - vi->rq; + + if (running) + napi_disable(&rq->napi); + + err = virtqueue_resize(rq->vq, ring_num, virtnet_rq_free_unused_buf); + if (err) + netdev_err(vi->dev, "resize rx fail: rx queue index: %d err: %d\n", qindex, err); + + if (!try_fill_recv(vi, rq, GFP_KERNEL)) + schedule_delayed_work(&vi->refill, 0); + + if (running) + virtnet_napi_enable(rq->vq, &rq->napi); + return err; +} + +static int virtnet_tx_resize(struct virtnet_info *vi, + struct send_queue *sq, u32 ring_num) +{ + bool running = netif_running(vi->dev); + struct netdev_queue *txq; + int err, qindex; + + qindex = sq - vi->sq; + + if (running) + virtnet_napi_tx_disable(&sq->napi); + + txq = netdev_get_tx_queue(vi->dev, qindex); + + /* 1. wait all ximt complete + * 2. fix the race of netif_stop_subqueue() vs netif_start_subqueue() + */ + __netif_tx_lock_bh(txq); + + /* Prevent rx poll from accessing sq. */ + sq->reset = true; + + /* Prevent the upper layer from trying to send packets. */ + netif_stop_subqueue(vi->dev, qindex); + + __netif_tx_unlock_bh(txq); + + err = virtqueue_resize(sq->vq, ring_num, virtnet_sq_free_unused_buf); + if (err) + netdev_err(vi->dev, "resize tx fail: tx queue index: %d err: %d\n", qindex, err); + + __netif_tx_lock_bh(txq); + sq->reset = false; + netif_tx_wake_queue(txq); + __netif_tx_unlock_bh(txq); + + if (running) + virtnet_napi_tx_enable(vi, sq->vq, &sq->napi); + return err; +} + /* * Send command via the control virtqueue and check status. Commands * supported by the hypervisor, as indicated by feature bits, should @@ -2282,10 +2366,57 @@ static void virtnet_get_ringparam(struct net_device *dev, { struct virtnet_info *vi = netdev_priv(dev); - ring->rx_max_pending = virtqueue_get_vring_size(vi->rq[0].vq); - ring->tx_max_pending = virtqueue_get_vring_size(vi->sq[0].vq); - ring->rx_pending = ring->rx_max_pending; - ring->tx_pending = ring->tx_max_pending; + ring->rx_max_pending = vi->rq[0].vq->num_max; + ring->tx_max_pending = vi->sq[0].vq->num_max; + ring->rx_pending = virtqueue_get_vring_size(vi->rq[0].vq); + ring->tx_pending = virtqueue_get_vring_size(vi->sq[0].vq); +} + +static int virtnet_set_ringparam(struct net_device *dev, + struct ethtool_ringparam *ring, + struct kernel_ethtool_ringparam *kernel_ring, + struct netlink_ext_ack *extack) +{ + struct virtnet_info *vi = netdev_priv(dev); + u32 rx_pending, tx_pending; + struct receive_queue *rq; + struct send_queue *sq; + int i, err; + + if (ring->rx_mini_pending || ring->rx_jumbo_pending) + return -EINVAL; + + rx_pending = virtqueue_get_vring_size(vi->rq[0].vq); + tx_pending = virtqueue_get_vring_size(vi->sq[0].vq); + + if (ring->rx_pending == rx_pending && + ring->tx_pending == tx_pending) + return 0; + + if (ring->rx_pending > vi->rq[0].vq->num_max) + return -EINVAL; + + if (ring->tx_pending > vi->sq[0].vq->num_max) + return -EINVAL; + + for (i = 0; i < vi->max_queue_pairs; i++) { + rq = vi->rq + i; + sq = vi->sq + i; + + if (ring->tx_pending != tx_pending) { + err = virtnet_tx_resize(vi, sq, ring->tx_pending); + if (err) + return err; + } + + if (ring->rx_pending != rx_pending) { + err = virtnet_rx_resize(vi, rq, ring->rx_pending); + if (err) + return err; + } + } + + return 0; } static bool virtnet_commit_rss_command(struct virtnet_info *vi) @@ -2615,27 +2746,89 @@ static int virtnet_get_link_ksettings(struct net_device *dev, return 0; } +static int virtnet_send_notf_coal_cmds(struct virtnet_info *vi, + struct ethtool_coalesce *ec) +{ + struct scatterlist sgs_tx, sgs_rx; + struct virtio_net_ctrl_coal_tx coal_tx; + struct virtio_net_ctrl_coal_rx coal_rx; + + coal_tx.tx_usecs = cpu_to_le32(ec->tx_coalesce_usecs); + coal_tx.tx_max_packets = cpu_to_le32(ec->tx_max_coalesced_frames); + sg_init_one(&sgs_tx, &coal_tx, sizeof(coal_tx)); + + if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_NOTF_COAL, + VIRTIO_NET_CTRL_NOTF_COAL_TX_SET, + &sgs_tx)) + return -EINVAL; + + /* Save parameters */ + vi->tx_usecs = ec->tx_coalesce_usecs; + vi->tx_max_packets = ec->tx_max_coalesced_frames; + + coal_rx.rx_usecs = cpu_to_le32(ec->rx_coalesce_usecs); + coal_rx.rx_max_packets = cpu_to_le32(ec->rx_max_coalesced_frames); + sg_init_one(&sgs_rx, &coal_rx, sizeof(coal_rx)); + + if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_NOTF_COAL, + VIRTIO_NET_CTRL_NOTF_COAL_RX_SET, + &sgs_rx)) + return -EINVAL; + + /* Save parameters */ + vi->rx_usecs = ec->rx_coalesce_usecs; + vi->rx_max_packets = ec->rx_max_coalesced_frames; + + return 0; +} + +static int virtnet_coal_params_supported(struct ethtool_coalesce *ec) +{ + /* usecs coalescing is supported only if VIRTIO_NET_F_NOTF_COAL + * feature is negotiated. + */ + if (ec->rx_coalesce_usecs || ec->tx_coalesce_usecs) + return -EOPNOTSUPP; + + if (ec->tx_max_coalesced_frames > 1 || + ec->rx_max_coalesced_frames != 1) + return -EINVAL; + + return 0; +} + static int virtnet_set_coalesce(struct net_device *dev, struct ethtool_coalesce *ec, struct kernel_ethtool_coalesce *kernel_coal, struct netlink_ext_ack *extack) { struct virtnet_info *vi = netdev_priv(dev); - int i, napi_weight; - - if (ec->tx_max_coalesced_frames > 1 || - ec->rx_max_coalesced_frames != 1) - return -EINVAL; + int ret, i, napi_weight; + bool update_napi = false; + /* Can't change NAPI weight if the link is up */ napi_weight = ec->tx_max_coalesced_frames ? NAPI_POLL_WEIGHT : 0; if (napi_weight ^ vi->sq[0].napi.weight) { if (dev->flags & IFF_UP) return -EBUSY; + else + update_napi = true; + } + + if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_NOTF_COAL)) + ret = virtnet_send_notf_coal_cmds(vi, ec); + else + ret = virtnet_coal_params_supported(ec); + + if (ret) + return ret; + + if (update_napi) { for (i = 0; i < vi->max_queue_pairs; i++) vi->sq[i].napi.weight = napi_weight; } - return 0; + return ret; } static int virtnet_get_coalesce(struct net_device *dev, @@ -2643,16 +2836,19 @@ static int virtnet_get_coalesce(struct net_device *dev, struct kernel_ethtool_coalesce *kernel_coal, struct netlink_ext_ack *extack) { - struct ethtool_coalesce ec_default = { - .cmd = ETHTOOL_GCOALESCE, - .rx_max_coalesced_frames = 1, - }; struct virtnet_info *vi = netdev_priv(dev); - memcpy(ec, &ec_default, sizeof(ec_default)); + if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_NOTF_COAL)) { + ec->rx_coalesce_usecs = vi->rx_usecs; + ec->tx_coalesce_usecs = vi->tx_usecs; + ec->tx_max_coalesced_frames = vi->tx_max_packets; + ec->rx_max_coalesced_frames = vi->rx_max_packets; + } else { + ec->rx_max_coalesced_frames = 1; - if (vi->sq[0].napi.weight) - ec->tx_max_coalesced_frames = 1; + if (vi->sq[0].napi.weight) + ec->tx_max_coalesced_frames = 1; + } return 0; } @@ -2771,10 +2967,12 @@ static int virtnet_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info) } static const struct ethtool_ops virtnet_ethtool_ops = { - .supported_coalesce_params = ETHTOOL_COALESCE_MAX_FRAMES, + .supported_coalesce_params = ETHTOOL_COALESCE_MAX_FRAMES | + ETHTOOL_COALESCE_USECS, .get_drvinfo = virtnet_get_drvinfo, .get_link = ethtool_op_get_link, .get_ringparam = virtnet_get_ringparam, + .set_ringparam = virtnet_set_ringparam, .get_strings = virtnet_get_strings, .get_sset_count = virtnet_get_sset_count, .get_ethtool_stats = virtnet_get_ethtool_stats, @@ -3168,6 +3366,27 @@ static void free_receive_page_frags(struct virtnet_info *vi) put_page(vi->rq[i].alloc_frag.page); } +static void virtnet_sq_free_unused_buf(struct virtqueue *vq, void *buf) +{ + if (!is_xdp_frame(buf)) + dev_kfree_skb(buf); + else + xdp_return_frame(ptr_to_xdp(buf)); +} + +static void virtnet_rq_free_unused_buf(struct virtqueue *vq, void *buf) +{ + struct virtnet_info *vi = vq->vdev->priv; + int i = vq2rxq(vq); + + if (vi->mergeable_rx_bufs) + put_page(virt_to_head_page(buf)); + else if (vi->big_packets) + give_pages(&vi->rq[i], buf); + else + put_page(virt_to_head_page(buf)); +} + static void free_unused_bufs(struct virtnet_info *vi) { void *buf; @@ -3175,26 +3394,14 @@ static void free_unused_bufs(struct virtnet_info *vi) for (i = 0; i < vi->max_queue_pairs; i++) { struct virtqueue *vq = vi->sq[i].vq; - while ((buf = virtqueue_detach_unused_buf(vq)) != NULL) { - if (!is_xdp_frame(buf)) - dev_kfree_skb(buf); - else - xdp_return_frame(ptr_to_xdp(buf)); - } + while ((buf = virtqueue_detach_unused_buf(vq)) != NULL) + virtnet_sq_free_unused_buf(vq, buf); } for (i = 0; i < vi->max_queue_pairs; i++) { struct virtqueue *vq = vi->rq[i].vq; - - while ((buf = virtqueue_detach_unused_buf(vq)) != NULL) { - if (vi->mergeable_rx_bufs) { - put_page(virt_to_head_page(buf)); - } else if (vi->big_packets) { - give_pages(&vi->rq[i], buf); - } else { - put_page(virt_to_head_page(buf)); - } - } + while ((buf = virtqueue_detach_unused_buf(vq)) != NULL) + virtnet_rq_free_unused_buf(vq, buf); } } @@ -3225,6 +3432,29 @@ static unsigned int mergeable_min_buf_len(struct virtnet_info *vi, struct virtqu (unsigned int)GOOD_PACKET_LEN); } +static void virtnet_config_sizes(struct virtnet_info *vi, u32 *sizes) +{ + u32 i, rx_size, tx_size; + + if (vi->speed == SPEED_UNKNOWN || vi->speed < SPEED_10000) { + rx_size = 1024; + tx_size = 1024; + + } else if (vi->speed < SPEED_40000) { + rx_size = 1024 * 4; + tx_size = 1024 * 4; + + } else { + rx_size = 1024 * 8; + tx_size = 1024 * 8; + } + + for (i = 0; i < vi->max_queue_pairs; i++) { + sizes[rxq2vq(i)] = rx_size; + sizes[txq2vq(i)] = tx_size; + } +} + static int virtnet_find_vqs(struct virtnet_info *vi) { vq_callback_t **callbacks; @@ -3232,6 +3462,7 @@ static int virtnet_find_vqs(struct virtnet_info *vi) int ret = -ENOMEM; int i, total_vqs; const char **names; + u32 *sizes; bool *ctx; /* We expect 1 RX virtqueue followed by 1 TX virtqueue, followed by @@ -3259,10 +3490,15 @@ static int virtnet_find_vqs(struct virtnet_info *vi) ctx = NULL; } + sizes = kmalloc_array(total_vqs, sizeof(*sizes), GFP_KERNEL); + if (!sizes) + goto err_sizes; + /* Parameters for control virtqueue, if any */ if (vi->has_cvq) { callbacks[total_vqs - 1] = NULL; names[total_vqs - 1] = "control"; + sizes[total_vqs - 1] = 64; } /* Allocate/initialize parameters for send/receive virtqueues */ @@ -3277,8 +3513,10 @@ static int virtnet_find_vqs(struct virtnet_info *vi) ctx[rxq2vq(i)] = true; } - ret = virtio_find_vqs_ctx(vi->vdev, total_vqs, vqs, callbacks, - names, ctx, NULL); + virtnet_config_sizes(vi, sizes); + + ret = virtio_find_vqs_ctx_size(vi->vdev, total_vqs, vqs, callbacks, + names, sizes, ctx, NULL); if (ret) goto err_find; @@ -3298,6 +3536,8 @@ static int virtnet_find_vqs(struct virtnet_info *vi) err_find: + kfree(sizes); +err_sizes: kfree(ctx); err_ctx: kfree(names); @@ -3441,6 +3681,8 @@ static bool virtnet_validate_features(struct virtio_device *vdev) VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_RSS, "VIRTIO_NET_F_CTRL_VQ") || VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_HASH_REPORT, + "VIRTIO_NET_F_CTRL_VQ") || + VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_NOTF_COAL, "VIRTIO_NET_F_CTRL_VQ"))) { return false; } @@ -3577,6 +3819,13 @@ static int virtnet_probe(struct virtio_device *vdev) if (virtio_has_feature(vdev, VIRTIO_NET_F_MRG_RXBUF)) vi->mergeable_rx_bufs = true; + if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_NOTF_COAL)) { + vi->rx_usecs = 0; + vi->tx_usecs = 0; + vi->tx_max_packets = 0; + vi->rx_max_packets = 0; + } + if (virtio_has_feature(vdev, VIRTIO_NET_F_HASH_REPORT)) vi->has_rss_hash_report = true; @@ -3648,6 +3897,9 @@ static int virtnet_probe(struct virtio_device *vdev) vi->curr_queue_pairs = num_online_cpus(); vi->max_queue_pairs = max_queue_pairs; + virtnet_init_settings(dev); + virtnet_update_settings(vi); + /* Allocate/initialize the rx/tx queues, and invoke find_vqs */ err = init_vqs(vi); if (err) @@ -3660,8 +3912,6 @@ static int virtnet_probe(struct virtio_device *vdev) netif_set_real_num_tx_queues(dev, vi->curr_queue_pairs); netif_set_real_num_rx_queues(dev, vi->curr_queue_pairs); - virtnet_init_settings(dev); - if (virtio_has_feature(vdev, VIRTIO_NET_F_STANDBY)) { vi->failover = net_failover_create(vi->dev); if (IS_ERR(vi->failover)) { @@ -3811,7 +4061,7 @@ static struct virtio_device_id id_table[] = { VIRTIO_NET_F_CTRL_MAC_ADDR, \ VIRTIO_NET_F_MTU, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, \ VIRTIO_NET_F_SPEED_DUPLEX, VIRTIO_NET_F_STANDBY, \ - VIRTIO_NET_F_RSS, VIRTIO_NET_F_HASH_REPORT + VIRTIO_NET_F_RSS, VIRTIO_NET_F_HASH_REPORT, VIRTIO_NET_F_NOTF_COAL static unsigned int features[] = { VIRTNET_FEATURES, diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index 90811ab851fd..c3285242f74f 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -2321,7 +2321,7 @@ static struct dst_entry *vxlan6_get_route(struct vxlan_dev *vxlan, fl6.flowi6_oif = oif; fl6.daddr = *daddr; fl6.saddr = *saddr; - fl6.flowlabel = ip6_make_flowinfo(RT_TOS(tos), label); + fl6.flowlabel = ip6_make_flowinfo(tos, label); fl6.flowi6_mark = skb->mark; fl6.flowi6_proto = IPPROTO_UDP; fl6.fl6_dport = dport; diff --git a/drivers/net/wireless/microchip/wilc1000/hif.c b/drivers/net/wireless/microchip/wilc1000/hif.c index b89519ab9205..eb1d1ba3a443 100644 --- a/drivers/net/wireless/microchip/wilc1000/hif.c +++ b/drivers/net/wireless/microchip/wilc1000/hif.c @@ -635,7 +635,7 @@ static inline void host_int_parse_assoc_resp_info(struct wilc_vif *vif, conn_info->req_ies_len = 0; } -inline void wilc_handle_disconnect(struct wilc_vif *vif) +void wilc_handle_disconnect(struct wilc_vif *vif) { struct host_if_drv *hif_drv = vif->hif_drv; diff --git a/drivers/net/wireless/microchip/wilc1000/hif.h b/drivers/net/wireless/microchip/wilc1000/hif.h index 69ba1d469e9f..baa2881f4465 100644 --- a/drivers/net/wireless/microchip/wilc1000/hif.h +++ b/drivers/net/wireless/microchip/wilc1000/hif.h @@ -215,5 +215,6 @@ void wilc_gnrl_async_info_received(struct wilc *wilc, u8 *buffer, u32 length); void *wilc_parse_join_bss_param(struct cfg80211_bss *bss, struct cfg80211_crypto_settings *crypto); int wilc_set_default_mgmt_key_index(struct wilc_vif *vif, u8 index); -inline void wilc_handle_disconnect(struct wilc_vif *vif); +void wilc_handle_disconnect(struct wilc_vif *vif); + #endif diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c index b019755e4e21..3ece49cb18ff 100644 --- a/drivers/ntb/hw/epf/ntb_hw_epf.c +++ b/drivers/ntb/hw/epf/ntb_hw_epf.c @@ -45,7 +45,6 @@ #define NTB_EPF_MIN_DB_COUNT 3 #define NTB_EPF_MAX_DB_COUNT 31 -#define NTB_EPF_MW_OFFSET 2 #define NTB_EPF_COMMAND_TIMEOUT 1000 /* 1 Sec */ @@ -67,6 +66,7 @@ struct ntb_epf_dev { enum pci_barno ctrl_reg_bar; enum pci_barno peer_spad_reg_bar; enum pci_barno db_reg_bar; + enum pci_barno mw_bar; unsigned int mw_count; unsigned int spad_count; @@ -92,6 +92,8 @@ struct ntb_epf_data { enum pci_barno peer_spad_reg_bar; /* BAR that contains Doorbell region and Memory window '1' */ enum pci_barno db_reg_bar; + /* BAR that contains memory windows*/ + enum pci_barno mw_bar; }; static int ntb_epf_send_command(struct ntb_epf_dev *ndev, u32 command, @@ -411,7 +413,7 @@ static int ntb_epf_mw_set_trans(struct ntb_dev *ntb, int pidx, int idx, return -EINVAL; } - bar = idx + NTB_EPF_MW_OFFSET; + bar = idx + ndev->mw_bar; mw_size = pci_resource_len(ntb->pdev, bar); @@ -453,7 +455,7 @@ static int ntb_epf_peer_mw_get_addr(struct ntb_dev *ntb, int idx, if (idx == 0) offset = readl(ndev->ctrl_reg + NTB_EPF_MW1_OFFSET); - bar = idx + NTB_EPF_MW_OFFSET; + bar = idx + ndev->mw_bar; if (base) *base = pci_resource_start(ndev->ntb.pdev, bar) + offset; @@ -565,6 +567,7 @@ static int ntb_epf_init_pci(struct ntb_epf_dev *ndev, struct pci_dev *pdev) { struct device *dev = ndev->dev; + size_t spad_sz, spad_off; int ret; pci_set_drvdata(pdev, ndev); @@ -599,10 +602,16 @@ static int ntb_epf_init_pci(struct ntb_epf_dev *ndev, goto err_dma_mask; } - ndev->peer_spad_reg = pci_iomap(pdev, ndev->peer_spad_reg_bar, 0); - if (!ndev->peer_spad_reg) { - ret = -EIO; - goto err_dma_mask; + if (ndev->peer_spad_reg_bar) { + ndev->peer_spad_reg = pci_iomap(pdev, ndev->peer_spad_reg_bar, 0); + if (!ndev->peer_spad_reg) { + ret = -EIO; + goto err_dma_mask; + } + } else { + spad_sz = 4 * readl(ndev->ctrl_reg + NTB_EPF_SPAD_COUNT); + spad_off = readl(ndev->ctrl_reg + NTB_EPF_SPAD_OFFSET); + ndev->peer_spad_reg = ndev->ctrl_reg + spad_off + spad_sz; } ndev->db_reg = pci_iomap(pdev, ndev->db_reg_bar, 0); @@ -657,6 +666,7 @@ static int ntb_epf_pci_probe(struct pci_dev *pdev, enum pci_barno peer_spad_reg_bar = BAR_1; enum pci_barno ctrl_reg_bar = BAR_0; enum pci_barno db_reg_bar = BAR_2; + enum pci_barno mw_bar = BAR_2; struct device *dev = &pdev->dev; struct ntb_epf_data *data; struct ntb_epf_dev *ndev; @@ -671,17 +681,16 @@ static int ntb_epf_pci_probe(struct pci_dev *pdev, data = (struct ntb_epf_data *)id->driver_data; if (data) { - if (data->peer_spad_reg_bar) - peer_spad_reg_bar = data->peer_spad_reg_bar; - if (data->ctrl_reg_bar) - ctrl_reg_bar = data->ctrl_reg_bar; - if (data->db_reg_bar) - db_reg_bar = data->db_reg_bar; + peer_spad_reg_bar = data->peer_spad_reg_bar; + ctrl_reg_bar = data->ctrl_reg_bar; + db_reg_bar = data->db_reg_bar; + mw_bar = data->mw_bar; } ndev->peer_spad_reg_bar = peer_spad_reg_bar; ndev->ctrl_reg_bar = ctrl_reg_bar; ndev->db_reg_bar = db_reg_bar; + ndev->mw_bar = mw_bar; ndev->dev = dev; ntb_epf_init_struct(ndev, pdev); @@ -729,6 +738,14 @@ static const struct ntb_epf_data j721e_data = { .ctrl_reg_bar = BAR_0, .peer_spad_reg_bar = BAR_1, .db_reg_bar = BAR_2, + .mw_bar = BAR_2, +}; + +static const struct ntb_epf_data mx8_data = { + .ctrl_reg_bar = BAR_0, + .peer_spad_reg_bar = BAR_0, + .db_reg_bar = BAR_2, + .mw_bar = BAR_4, }; static const struct pci_device_id ntb_epf_pci_tbl[] = { @@ -737,6 +754,11 @@ static const struct pci_device_id ntb_epf_pci_tbl[] = { .class = PCI_CLASS_MEMORY_RAM << 8, .class_mask = 0xffff00, .driver_data = (kernel_ulong_t)&j721e_data, }, + { + PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, 0x0809), + .class = PCI_CLASS_MEMORY_RAM << 8, .class_mask = 0xffff00, + .driver_data = (kernel_ulong_t)&mx8_data, + }, { }, }; diff --git a/drivers/ntb/hw/idt/ntb_hw_idt.c b/drivers/ntb/hw/idt/ntb_hw_idt.c index 733557231ed0..0ed6f809ff2e 100644 --- a/drivers/ntb/hw/idt/ntb_hw_idt.c +++ b/drivers/ntb/hw/idt/ntb_hw_idt.c @@ -2406,7 +2406,7 @@ static ssize_t idt_dbgfs_info_read(struct file *filp, char __user *ubuf, "\t%hhu.\t", idx); else off += scnprintf(strbuf + off, size - off, - "\t%hhu-%hhu.\t", idx, idx + cnt - 1); + "\t%hhu-%d.\t", idx, idx + cnt - 1); off += scnprintf(strbuf + off, size - off, "%s BAR%hhu, ", idt_get_mw_name(data), ndev->mws[idx].bar); @@ -2435,7 +2435,7 @@ static ssize_t idt_dbgfs_info_read(struct file *filp, char __user *ubuf, "\t%hhu.\t", idx); else off += scnprintf(strbuf + off, size - off, - "\t%hhu-%hhu.\t", idx, idx + cnt - 1); + "\t%hhu-%d.\t", idx, idx + cnt - 1); off += scnprintf(strbuf + off, size - off, "%s BAR%hhu, ", idt_get_mw_name(data), @@ -2480,7 +2480,7 @@ static ssize_t idt_dbgfs_info_read(struct file *filp, char __user *ubuf, int src; data = idt_ntb_msg_read(&ndev->ntb, &src, idx); off += scnprintf(strbuf + off, size - off, - "\t%hhu. 0x%08x from peer %hhu (Port %hhu)\n", + "\t%hhu. 0x%08x from peer %d (Port %hhu)\n", idx, data, src, ndev->peers[src].port); } off += scnprintf(strbuf + off, size - off, "\n"); diff --git a/drivers/ntb/hw/intel/ntb_hw_gen1.c b/drivers/ntb/hw/intel/ntb_hw_gen1.c index e5f14e20a9ff..84772013812b 100644 --- a/drivers/ntb/hw/intel/ntb_hw_gen1.c +++ b/drivers/ntb/hw/intel/ntb_hw_gen1.c @@ -763,7 +763,7 @@ static ssize_t ndev_debugfs_read(struct file *filp, char __user *ubuf, return ndev_ntb_debugfs_read(filp, ubuf, count, offp); else if (pdev_is_gen3(ndev->ntb.pdev)) return ndev_ntb3_debugfs_read(filp, ubuf, count, offp); - else if (pdev_is_gen4(ndev->ntb.pdev)) + else if (pdev_is_gen4(ndev->ntb.pdev) || pdev_is_gen5(ndev->ntb.pdev)) return ndev_ntb4_debugfs_read(filp, ubuf, count, offp); return -ENXIO; @@ -1874,7 +1874,7 @@ static int intel_ntb_pci_probe(struct pci_dev *pdev, rc = gen3_init_dev(ndev); if (rc) goto err_init_dev; - } else if (pdev_is_gen4(pdev)) { + } else if (pdev_is_gen4(pdev) || pdev_is_gen5(pdev)) { ndev->ntb.ops = &intel_ntb4_ops; rc = intel_ntb_init_pci(ndev, pdev); if (rc) @@ -1904,7 +1904,8 @@ static int intel_ntb_pci_probe(struct pci_dev *pdev, err_register: ndev_deinit_debugfs(ndev); - if (pdev_is_gen1(pdev) || pdev_is_gen3(pdev) || pdev_is_gen4(pdev)) + if (pdev_is_gen1(pdev) || pdev_is_gen3(pdev) || + pdev_is_gen4(pdev) || pdev_is_gen5(pdev)) xeon_deinit_dev(ndev); err_init_dev: intel_ntb_deinit_pci(ndev); @@ -1920,7 +1921,8 @@ static void intel_ntb_pci_remove(struct pci_dev *pdev) ntb_unregister_device(&ndev->ntb); ndev_deinit_debugfs(ndev); - if (pdev_is_gen1(pdev) || pdev_is_gen3(pdev) || pdev_is_gen4(pdev)) + if (pdev_is_gen1(pdev) || pdev_is_gen3(pdev) || + pdev_is_gen4(pdev) || pdev_is_gen5(pdev)) xeon_deinit_dev(ndev); intel_ntb_deinit_pci(ndev); kfree(ndev); @@ -2047,6 +2049,8 @@ static const struct pci_device_id intel_ntb_pci_tbl[] = { /* GEN4 */ {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_B2B_ICX)}, + /* GEN5 PCIe */ + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_B2B_GNR)}, {0} }; MODULE_DEVICE_TABLE(pci, intel_ntb_pci_tbl); diff --git a/drivers/ntb/hw/intel/ntb_hw_gen4.c b/drivers/ntb/hw/intel/ntb_hw_gen4.c index 4081fc538ff4..22cac7975b3c 100644 --- a/drivers/ntb/hw/intel/ntb_hw_gen4.c +++ b/drivers/ntb/hw/intel/ntb_hw_gen4.c @@ -197,7 +197,7 @@ int gen4_init_dev(struct intel_ntb_dev *ndev) ppd1 = ioread32(ndev->self_mmio + GEN4_PPD1_OFFSET); if (pdev_is_ICX(pdev)) ndev->ntb.topo = gen4_ppd_topo(ndev, ppd1); - else if (pdev_is_SPR(pdev)) + else if (pdev_is_SPR(pdev) || pdev_is_gen5(pdev)) ndev->ntb.topo = spr_ppd_topo(ndev, ppd1); dev_dbg(&pdev->dev, "ppd %#x topo %s\n", ppd1, ntb_topo_string(ndev->ntb.topo)); diff --git a/drivers/ntb/hw/intel/ntb_hw_intel.h b/drivers/ntb/hw/intel/ntb_hw_intel.h index b233d1c6ba2d..da4d5fe55bab 100644 --- a/drivers/ntb/hw/intel/ntb_hw_intel.h +++ b/drivers/ntb/hw/intel/ntb_hw_intel.h @@ -70,6 +70,7 @@ #define PCI_DEVICE_ID_INTEL_NTB_SS_BDX 0x6F0F #define PCI_DEVICE_ID_INTEL_NTB_B2B_SKX 0x201C #define PCI_DEVICE_ID_INTEL_NTB_B2B_ICX 0x347e +#define PCI_DEVICE_ID_INTEL_NTB_B2B_GNR 0x0db4 /* Ntb control and link status */ #define NTB_CTL_CFG_LOCK BIT(0) @@ -228,4 +229,10 @@ static inline int pdev_is_gen4(struct pci_dev *pdev) return 0; } + +static inline int pdev_is_gen5(struct pci_dev *pdev) +{ + return pdev->device == PCI_DEVICE_ID_INTEL_NTB_B2B_GNR; +} + #endif diff --git a/drivers/ntb/test/ntb_tool.c b/drivers/ntb/test/ntb_tool.c index b7bf3f863d79..5ee0afa621a9 100644 --- a/drivers/ntb/test/ntb_tool.c +++ b/drivers/ntb/test/ntb_tool.c @@ -367,14 +367,16 @@ static ssize_t tool_fn_write(struct tool_ctx *tc, u64 bits; int n; + if (*offp) + return 0; + buf = kmalloc(size + 1, GFP_KERNEL); if (!buf) return -ENOMEM; - ret = simple_write_to_buffer(buf, size, offp, ubuf, size); - if (ret < 0) { + if (copy_from_user(buf, ubuf, size)) { kfree(buf); - return ret; + return -EFAULT; } buf[size] = 0; diff --git a/drivers/nvdimm/virtio_pmem.c b/drivers/nvdimm/virtio_pmem.c index 995b6cdc67ed..20da455d2ef6 100644 --- a/drivers/nvdimm/virtio_pmem.c +++ b/drivers/nvdimm/virtio_pmem.c @@ -81,17 +81,24 @@ static int virtio_pmem_probe(struct virtio_device *vdev) ndr_desc.res = &res; ndr_desc.numa_node = nid; ndr_desc.flush = async_pmem_flush; + ndr_desc.provider_data = vdev; set_bit(ND_REGION_PAGEMAP, &ndr_desc.flags); set_bit(ND_REGION_ASYNC, &ndr_desc.flags); + /* + * The NVDIMM region could be available before the + * virtio_device_ready() that is called by + * virtio_dev_probe(), so we set device ready here. + */ + virtio_device_ready(vdev); nd_region = nvdimm_pmem_region_create(vpmem->nvdimm_bus, &ndr_desc); if (!nd_region) { dev_err(&vdev->dev, "failed to create nvdimm region\n"); err = -ENXIO; goto out_nd; } - nd_region->provider_data = dev_to_virtio(nd_region->dev.parent->parent); return 0; out_nd: + virtio_reset_device(vdev); nvdimm_bus_unregister(vpmem->nvdimm_bus); out_vq: vdev->config->del_vqs(vdev); diff --git a/drivers/nvme/host/fabrics.c b/drivers/nvme/host/fabrics.c index 5207a2348257..10cc4a814602 100644 --- a/drivers/nvme/host/fabrics.c +++ b/drivers/nvme/host/fabrics.c @@ -270,6 +270,12 @@ static void nvmf_log_connect_error(struct nvme_ctrl *ctrl, { int err_sctype = errval & ~NVME_SC_DNR; + if (errval < 0) { + dev_err(ctrl->device, + "Connect command failed, errno: %d\n", errval); + return; + } + switch (err_sctype) { case NVME_SC_CONNECT_INVALID_PARAM: if (offset >> 16) { @@ -1230,7 +1236,7 @@ static int __init nvmf_init(void) nvmf_device = device_create(nvmf_class, NULL, MKDEV(0, 0), NULL, "ctl"); if (IS_ERR(nvmf_device)) { - pr_err("couldn't create nvme-fabris device!\n"); + pr_err("couldn't create nvme-fabrics device!\n"); ret = PTR_ERR(nvmf_device); goto out_destroy_class; } diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c index 9987797620b6..127abaf9ba5d 100644 --- a/drivers/nvme/host/fc.c +++ b/drivers/nvme/host/fc.c @@ -2533,6 +2533,8 @@ __nvme_fc_abort_outstanding_ios(struct nvme_fc_ctrl *ctrl, bool start_queues) blk_mq_tagset_busy_iter(&ctrl->admin_tag_set, nvme_fc_terminate_exchange, &ctrl->ctrl); blk_mq_tagset_wait_completed_request(&ctrl->admin_tag_set); + if (start_queues) + nvme_start_admin_queue(&ctrl->ctrl); } static void @@ -3878,6 +3880,7 @@ static int fc_parse_cgrpid(const char *buf, u64 *id) static ssize_t fc_appid_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { + size_t orig_count = count; u64 cgrp_id; int appid_len = 0; int cgrpid_len = 0; @@ -3902,7 +3905,7 @@ static ssize_t fc_appid_store(struct device *dev, ret = blkcg_set_fc_appid(app_id, cgrp_id, sizeof(app_id)); if (ret < 0) return ret; - return count; + return orig_count; } static DEVICE_ATTR(appid_store, 0200, NULL, fc_appid_store); #endif /* CONFIG_BLK_CGROUP_FC_APPID */ diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index de1b4463142d..3a1c37f32f30 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -3511,6 +3511,8 @@ static const struct pci_device_id nvme_id_table[] = { .driver_data = NVME_QUIRK_BOGUS_NID, }, { PCI_DEVICE(0x1cc1, 0x5350), /* ADATA XPG GAMMIX S50 */ .driver_data = NVME_QUIRK_BOGUS_NID, }, + { PCI_DEVICE(0x1dbe, 0x5236), /* ADATA XPG GAMMIX S70 */ + .driver_data = NVME_QUIRK_BOGUS_NID, }, { PCI_DEVICE(0x1e49, 0x0041), /* ZHITAI TiPro7000 NVMe SSD */ .driver_data = NVME_QUIRK_NO_DEEPEST_PS, }, { PCI_DEVICE(0xc0a9, 0x540a), /* Crucial P2 */ diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c index e82dcfcda29b..044da18c06f5 100644 --- a/drivers/nvme/host/tcp.c +++ b/drivers/nvme/host/tcp.c @@ -1660,6 +1660,9 @@ static void nvme_tcp_stop_queue(struct nvme_ctrl *nctrl, int qid) struct nvme_tcp_ctrl *ctrl = to_tcp_ctrl(nctrl); struct nvme_tcp_queue *queue = &ctrl->queues[qid]; + if (!test_bit(NVME_TCP_Q_ALLOCATED, &queue->flags)) + return; + mutex_lock(&queue->queue_lock); if (test_and_clear_bit(NVME_TCP_Q_LIVE, &queue->flags)) __nvme_tcp_stop_queue(queue); diff --git a/drivers/nvme/target/fabrics-cmd-auth.c b/drivers/nvme/target/fabrics-cmd-auth.c index c851814d6cb0..ebdf9aa81041 100644 --- a/drivers/nvme/target/fabrics-cmd-auth.c +++ b/drivers/nvme/target/fabrics-cmd-auth.c @@ -160,10 +160,10 @@ static u16 nvmet_auth_reply(struct nvmet_req *req, void *d) pr_debug("%s: ctrl %d qid %d host authenticated\n", __func__, ctrl->cntlid, req->sq->qid); if (data->cvalid) { - req->sq->dhchap_c2 = kmalloc(data->hl, GFP_KERNEL); + req->sq->dhchap_c2 = kmemdup(data->rval + data->hl, data->hl, + GFP_KERNEL); if (!req->sq->dhchap_c2) return NVME_AUTH_DHCHAP_FAILURE_FAILED; - memcpy(req->sq->dhchap_c2, data->rval + data->hl, data->hl); pr_debug("%s: ctrl %d qid %d challenge %*ph\n", __func__, ctrl->cntlid, req->sq->qid, data->hl, diff --git a/drivers/of/address.c b/drivers/of/address.c index 94f017d808c4..96f0a12e507c 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -1045,26 +1045,29 @@ phys_addr_t __init of_dma_get_max_cpu_address(struct device_node *np) * * It returns true if "dma-coherent" property was found * for this device in the DT, or if DMA is coherent by - * default for OF devices on the current platform. + * default for OF devices on the current platform and no + * "dma-noncoherent" property was found for this device. */ bool of_dma_is_coherent(struct device_node *np) { struct device_node *node; - - if (IS_ENABLED(CONFIG_OF_DMA_DEFAULT_COHERENT)) - return true; + bool is_coherent = IS_ENABLED(CONFIG_OF_DMA_DEFAULT_COHERENT); node = of_node_get(np); while (node) { if (of_property_read_bool(node, "dma-coherent")) { - of_node_put(node); - return true; + is_coherent = true; + break; + } + if (of_property_read_bool(node, "dma-noncoherent")) { + is_coherent = false; + break; } node = of_get_next_dma_parent(node); } of_node_put(node); - return false; + return is_coherent; } EXPORT_SYMBOL_GPL(of_dma_is_coherent); diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index cf1627679716..83ddb190292e 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -161,7 +161,11 @@ static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type, u32 free_win; struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - free_win = find_first_zero_bit(ep->ib_window_map, pci->num_ib_windows); + if (!ep->bar_to_atu[bar]) + free_win = find_first_zero_bit(ep->ib_window_map, pci->num_ib_windows); + else + free_win = ep->bar_to_atu[bar]; + if (free_win >= pci->num_ib_windows) { dev_err(pci->dev, "No free inbound window\n"); return -EINVAL; @@ -218,6 +222,7 @@ static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, atu_index); clear_bit(atu_index, ep->ib_window_map); ep->epf_bar[bar] = NULL; + ep->bar_to_atu[bar] = 0; } static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, @@ -245,6 +250,9 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, if (ret) return ret; + if (ep->epf_bar[bar]) + return 0; + dw_pcie_dbi_ro_wr_en(pci); dw_pcie_writel_dbi2(pci, reg, lower_32_bits(size - 1)); diff --git a/drivers/pci/endpoint/functions/Kconfig b/drivers/pci/endpoint/functions/Kconfig index 5f1242ca2f4e..295a033ee9a2 100644 --- a/drivers/pci/endpoint/functions/Kconfig +++ b/drivers/pci/endpoint/functions/Kconfig @@ -25,3 +25,15 @@ config PCI_EPF_NTB device tree. If in doubt, say "N" to disable Endpoint NTB driver. + +config PCI_EPF_VNTB + tristate "PCI Endpoint NTB driver" + depends on PCI_ENDPOINT + depends on NTB + select CONFIGFS_FS + help + Select this configuration option to enable the Non-Transparent + Bridge (NTB) driver for PCIe Endpoint. NTB driver implements NTB + between PCI Root Port and PCIe Endpoint. + + If in doubt, say "N" to disable Endpoint NTB driver. diff --git a/drivers/pci/endpoint/functions/Makefile b/drivers/pci/endpoint/functions/Makefile index 96ab932a537a..5c13001deaba 100644 --- a/drivers/pci/endpoint/functions/Makefile +++ b/drivers/pci/endpoint/functions/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_PCI_EPF_TEST) += pci-epf-test.o obj-$(CONFIG_PCI_EPF_NTB) += pci-epf-ntb.o +obj-$(CONFIG_PCI_EPF_VNTB) += pci-epf-vntb.o diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c new file mode 100644 index 000000000000..0ea85e1d292e --- /dev/null +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c @@ -0,0 +1,1442 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Endpoint Function Driver to implement Non-Transparent Bridge functionality + * Between PCI RC and EP + * + * Copyright (C) 2020 Texas Instruments + * Copyright (C) 2022 NXP + * + * Based on pci-epf-ntb.c + * Author: Frank Li <Frank.Li@nxp.com> + * Author: Kishon Vijay Abraham I <kishon@ti.com> + */ + +/** + * +------------+ +---------------------------------------+ + * | | | | + * +------------+ | +--------------+ + * | NTB | | | NTB | + * | NetDev | | | NetDev | + * +------------+ | +--------------+ + * | NTB | | | NTB | + * | Transfer | | | Transfer | + * +------------+ | +--------------+ + * | | | | | + * | PCI NTB | | | | + * | EPF | | | | + * | Driver | | | PCI Virtual | + * | | +---------------+ | NTB Driver | + * | | | PCI EP NTB |<------>| | + * | | | FN Driver | | | + * +------------+ +---------------+ +--------------+ + * | | | | | | + * | PCI Bus | <-----> | PCI EP Bus | | Virtual PCI | + * | | PCI | | | Bus | + * +------------+ +---------------+--------+--------------+ + * PCIe Root Port PCI EP + */ + +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/slab.h> + +#include <linux/pci-epc.h> +#include <linux/pci-epf.h> +#include <linux/ntb.h> + +static struct workqueue_struct *kpcintb_workqueue; + +#define COMMAND_CONFIGURE_DOORBELL 1 +#define COMMAND_TEARDOWN_DOORBELL 2 +#define COMMAND_CONFIGURE_MW 3 +#define COMMAND_TEARDOWN_MW 4 +#define COMMAND_LINK_UP 5 +#define COMMAND_LINK_DOWN 6 + +#define COMMAND_STATUS_OK 1 +#define COMMAND_STATUS_ERROR 2 + +#define LINK_STATUS_UP BIT(0) + +#define SPAD_COUNT 64 +#define DB_COUNT 4 +#define NTB_MW_OFFSET 2 +#define DB_COUNT_MASK GENMASK(15, 0) +#define MSIX_ENABLE BIT(16) +#define MAX_DB_COUNT 32 +#define MAX_MW 4 + +enum epf_ntb_bar { + BAR_CONFIG, + BAR_DB, + BAR_MW0, + BAR_MW1, + BAR_MW2, +}; + +/* + * +--------------------------------------------------+ Base + * | | + * | | + * | | + * | Common Control Register | + * | | + * | | + * | | + * +-----------------------+--------------------------+ Base+span_offset + * | | | + * | Peer Span Space | Span Space | + * | | | + * | | | + * +-----------------------+--------------------------+ Base+span_offset + * | | | +span_count * 4 + * | | | + * | Span Space | Peer Span Space | + * | | | + * +-----------------------+--------------------------+ + * Virtual PCI PCIe Endpoint + * NTB Driver NTB Driver + */ +struct epf_ntb_ctrl { + u32 command; + u32 argument; + u16 command_status; + u16 link_status; + u32 topology; + u64 addr; + u64 size; + u32 num_mws; + u32 reserved; + u32 spad_offset; + u32 spad_count; + u32 db_entry_size; + u32 db_data[MAX_DB_COUNT]; + u32 db_offset[MAX_DB_COUNT]; +} __packed; + +struct epf_ntb { + struct ntb_dev ntb; + struct pci_epf *epf; + struct config_group group; + + u32 num_mws; + u32 db_count; + u32 spad_count; + u64 mws_size[MAX_MW]; + u64 db; + u32 vbus_number; + u16 vntb_pid; + u16 vntb_vid; + + bool linkup; + u32 spad_size; + + enum pci_barno epf_ntb_bar[6]; + + struct epf_ntb_ctrl *reg; + + phys_addr_t epf_db_phy; + void __iomem *epf_db; + + phys_addr_t vpci_mw_phy[MAX_MW]; + void __iomem *vpci_mw_addr[MAX_MW]; + + struct delayed_work cmd_handler; +}; + +#define to_epf_ntb(epf_group) container_of((epf_group), struct epf_ntb, group) +#define ntb_ndev(__ntb) container_of(__ntb, struct epf_ntb, ntb) + +static struct pci_epf_header epf_ntb_header = { + .vendorid = PCI_ANY_ID, + .deviceid = PCI_ANY_ID, + .baseclass_code = PCI_BASE_CLASS_MEMORY, + .interrupt_pin = PCI_INTERRUPT_INTA, +}; + +/** + * epf_ntb_link_up() - Raise link_up interrupt to Virtual Host + * @ntb: NTB device that facilitates communication between HOST and VHOST + * @link_up: true or false indicating Link is UP or Down + * + * Once NTB function in HOST invoke ntb_link_enable(), + * this NTB function driver will trigger a link event to vhost. + */ +static int epf_ntb_link_up(struct epf_ntb *ntb, bool link_up) +{ + if (link_up) + ntb->reg->link_status |= LINK_STATUS_UP; + else + ntb->reg->link_status &= ~LINK_STATUS_UP; + + ntb_link_event(&ntb->ntb); + return 0; +} + +/** + * epf_ntb_configure_mw() - Configure the Outbound Address Space for vhost + * to access the memory window of host + * @ntb: NTB device that facilitates communication between host and vhost + * @mw: Index of the memory window (either 0, 1, 2 or 3) + * + * EP Outbound Window + * +--------+ +-----------+ + * | | | | + * | | | | + * | | | | + * | | | | + * | | +-----------+ + * | Virtual| | Memory Win| + * | NTB | -----------> | | + * | Driver | | | + * | | +-----------+ + * | | | | + * | | | | + * +--------+ +-----------+ + * VHost PCI EP + */ +static int epf_ntb_configure_mw(struct epf_ntb *ntb, u32 mw) +{ + phys_addr_t phys_addr; + u8 func_no, vfunc_no; + u64 addr, size; + int ret = 0; + + phys_addr = ntb->vpci_mw_phy[mw]; + addr = ntb->reg->addr; + size = ntb->reg->size; + + func_no = ntb->epf->func_no; + vfunc_no = ntb->epf->vfunc_no; + + ret = pci_epc_map_addr(ntb->epf->epc, func_no, vfunc_no, phys_addr, addr, size); + if (ret) + dev_err(&ntb->epf->epc->dev, + "Failed to map memory window %d address\n", mw); + return ret; +} + +/** + * epf_ntb_teardown_mw() - Teardown the configured OB ATU + * @ntb: NTB device that facilitates communication between HOST and vHOST + * @mw: Index of the memory window (either 0, 1, 2 or 3) + * + * Teardown the configured OB ATU configured in epf_ntb_configure_mw() using + * pci_epc_unmap_addr() + */ +static void epf_ntb_teardown_mw(struct epf_ntb *ntb, u32 mw) +{ + pci_epc_unmap_addr(ntb->epf->epc, + ntb->epf->func_no, + ntb->epf->vfunc_no, + ntb->vpci_mw_phy[mw]); +} + +/** + * epf_ntb_cmd_handler() - Handle commands provided by the NTB Host + * @work: work_struct for the epf_ntb_epc + * + * Workqueue function that gets invoked for the two epf_ntb_epc + * periodically (once every 5ms) to see if it has received any commands + * from NTB host. The host can send commands to configure doorbell or + * configure memory window or to update link status. + */ +static void epf_ntb_cmd_handler(struct work_struct *work) +{ + struct epf_ntb_ctrl *ctrl; + u32 command, argument; + struct epf_ntb *ntb; + struct device *dev; + int ret; + int i; + + ntb = container_of(work, struct epf_ntb, cmd_handler.work); + + for (i = 1; i < ntb->db_count; i++) { + if (readl(ntb->epf_db + i * 4)) { + if (readl(ntb->epf_db + i * 4)) + ntb->db |= 1 << (i - 1); + + ntb_db_event(&ntb->ntb, i); + writel(0, ntb->epf_db + i * 4); + } + } + + ctrl = ntb->reg; + command = ctrl->command; + if (!command) + goto reset_handler; + argument = ctrl->argument; + + ctrl->command = 0; + ctrl->argument = 0; + + ctrl = ntb->reg; + dev = &ntb->epf->dev; + + switch (command) { + case COMMAND_CONFIGURE_DOORBELL: + ctrl->command_status = COMMAND_STATUS_OK; + break; + case COMMAND_TEARDOWN_DOORBELL: + ctrl->command_status = COMMAND_STATUS_OK; + break; + case COMMAND_CONFIGURE_MW: + ret = epf_ntb_configure_mw(ntb, argument); + if (ret < 0) + ctrl->command_status = COMMAND_STATUS_ERROR; + else + ctrl->command_status = COMMAND_STATUS_OK; + break; + case COMMAND_TEARDOWN_MW: + epf_ntb_teardown_mw(ntb, argument); + ctrl->command_status = COMMAND_STATUS_OK; + break; + case COMMAND_LINK_UP: + ntb->linkup = true; + ret = epf_ntb_link_up(ntb, true); + if (ret < 0) + ctrl->command_status = COMMAND_STATUS_ERROR; + else + ctrl->command_status = COMMAND_STATUS_OK; + goto reset_handler; + case COMMAND_LINK_DOWN: + ntb->linkup = false; + ret = epf_ntb_link_up(ntb, false); + if (ret < 0) + ctrl->command_status = COMMAND_STATUS_ERROR; + else + ctrl->command_status = COMMAND_STATUS_OK; + break; + default: + dev_err(dev, "UNKNOWN command: %d\n", command); + break; + } + +reset_handler: + queue_delayed_work(kpcintb_workqueue, &ntb->cmd_handler, + msecs_to_jiffies(5)); +} + +/** + * epf_ntb_config_sspad_bar_clear() - Clear Config + Self scratchpad BAR + * @ntb_epc: EPC associated with one of the HOST which holds peer's outbound + * address. + * + * Clear BAR0 of EP CONTROLLER 1 which contains the HOST1's config and + * self scratchpad region (removes inbound ATU configuration). While BAR0 is + * the default self scratchpad BAR, an NTB could have other BARs for self + * scratchpad (because of reserved BARs). This function can get the exact BAR + * used for self scratchpad from epf_ntb_bar[BAR_CONFIG]. + * + * Please note the self scratchpad region and config region is combined to + * a single region and mapped using the same BAR. Also note HOST2's peer + * scratchpad is HOST1's self scratchpad. + */ +static void epf_ntb_config_sspad_bar_clear(struct epf_ntb *ntb) +{ + struct pci_epf_bar *epf_bar; + enum pci_barno barno; + + barno = ntb->epf_ntb_bar[BAR_CONFIG]; + epf_bar = &ntb->epf->bar[barno]; + + pci_epc_clear_bar(ntb->epf->epc, ntb->epf->func_no, ntb->epf->vfunc_no, epf_bar); +} + +/** + * epf_ntb_config_sspad_bar_set() - Set Config + Self scratchpad BAR + * @ntb: NTB device that facilitates communication between HOST and vHOST + * + * Map BAR0 of EP CONTROLLER 1 which contains the HOST1's config and + * self scratchpad region. + * + * Please note the self scratchpad region and config region is combined to + * a single region and mapped using the same BAR. + */ +static int epf_ntb_config_sspad_bar_set(struct epf_ntb *ntb) +{ + struct pci_epf_bar *epf_bar; + enum pci_barno barno; + u8 func_no, vfunc_no; + struct device *dev; + int ret; + + dev = &ntb->epf->dev; + func_no = ntb->epf->func_no; + vfunc_no = ntb->epf->vfunc_no; + barno = ntb->epf_ntb_bar[BAR_CONFIG]; + epf_bar = &ntb->epf->bar[barno]; + + ret = pci_epc_set_bar(ntb->epf->epc, func_no, vfunc_no, epf_bar); + if (ret) { + dev_err(dev, "inft: Config/Status/SPAD BAR set failed\n"); + return ret; + } + return 0; +} + +/** + * epf_ntb_config_spad_bar_free() - Free the physical memory associated with + * config + scratchpad region + * @ntb: NTB device that facilitates communication between HOST and vHOST + */ +static void epf_ntb_config_spad_bar_free(struct epf_ntb *ntb) +{ + enum pci_barno barno; + + barno = ntb->epf_ntb_bar[BAR_CONFIG]; + pci_epf_free_space(ntb->epf, ntb->reg, barno, 0); +} + +/** + * epf_ntb_config_spad_bar_alloc() - Allocate memory for config + scratchpad + * region + * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * + * Allocate the Local Memory mentioned in the above diagram. The size of + * CONFIG REGION is sizeof(struct epf_ntb_ctrl) and size of SCRATCHPAD REGION + * is obtained from "spad-count" configfs entry. + */ +static int epf_ntb_config_spad_bar_alloc(struct epf_ntb *ntb) +{ + size_t align; + enum pci_barno barno; + struct epf_ntb_ctrl *ctrl; + u32 spad_size, ctrl_size; + u64 size; + struct pci_epf *epf = ntb->epf; + struct device *dev = &epf->dev; + u32 spad_count; + void *base; + int i; + const struct pci_epc_features *epc_features = pci_epc_get_features(epf->epc, + epf->func_no, + epf->vfunc_no); + barno = ntb->epf_ntb_bar[BAR_CONFIG]; + size = epc_features->bar_fixed_size[barno]; + align = epc_features->align; + + if ((!IS_ALIGNED(size, align))) + return -EINVAL; + + spad_count = ntb->spad_count; + + ctrl_size = sizeof(struct epf_ntb_ctrl); + spad_size = 2 * spad_count * 4; + + if (!align) { + ctrl_size = roundup_pow_of_two(ctrl_size); + spad_size = roundup_pow_of_two(spad_size); + } else { + ctrl_size = ALIGN(ctrl_size, align); + spad_size = ALIGN(spad_size, align); + } + + if (!size) + size = ctrl_size + spad_size; + else if (size < ctrl_size + spad_size) + return -EINVAL; + + base = pci_epf_alloc_space(epf, size, barno, align, 0); + if (!base) { + dev_err(dev, "Config/Status/SPAD alloc region fail\n"); + return -ENOMEM; + } + + ntb->reg = base; + + ctrl = ntb->reg; + ctrl->spad_offset = ctrl_size; + + ctrl->spad_count = spad_count; + ctrl->num_mws = ntb->num_mws; + ntb->spad_size = spad_size; + + ctrl->db_entry_size = 4; + + for (i = 0; i < ntb->db_count; i++) { + ntb->reg->db_data[i] = 1 + i; + ntb->reg->db_offset[i] = 0; + } + + return 0; +} + +/** + * epf_ntb_configure_interrupt() - Configure MSI/MSI-X capaiblity + * @ntb: NTB device that facilitates communication between HOST and vHOST + * + * Configure MSI/MSI-X capability for each interface with number of + * interrupts equal to "db_count" configfs entry. + */ +static int epf_ntb_configure_interrupt(struct epf_ntb *ntb) +{ + const struct pci_epc_features *epc_features; + struct device *dev; + u32 db_count; + int ret; + + dev = &ntb->epf->dev; + + epc_features = pci_epc_get_features(ntb->epf->epc, ntb->epf->func_no, ntb->epf->vfunc_no); + + if (!(epc_features->msix_capable || epc_features->msi_capable)) { + dev_err(dev, "MSI or MSI-X is required for doorbell\n"); + return -EINVAL; + } + + db_count = ntb->db_count; + if (db_count > MAX_DB_COUNT) { + dev_err(dev, "DB count cannot be more than %d\n", MAX_DB_COUNT); + return -EINVAL; + } + + ntb->db_count = db_count; + + if (epc_features->msi_capable) { + ret = pci_epc_set_msi(ntb->epf->epc, + ntb->epf->func_no, + ntb->epf->vfunc_no, + 16); + if (ret) { + dev_err(dev, "MSI configuration failed\n"); + return ret; + } + } + + return 0; +} + +/** + * epf_ntb_db_bar_init() - Configure Doorbell window BARs + * @ntb: NTB device that facilitates communication between HOST and vHOST + */ +static int epf_ntb_db_bar_init(struct epf_ntb *ntb) +{ + const struct pci_epc_features *epc_features; + u32 align; + struct device *dev = &ntb->epf->dev; + int ret; + struct pci_epf_bar *epf_bar; + void __iomem *mw_addr; + enum pci_barno barno; + size_t size = 4 * ntb->db_count; + + epc_features = pci_epc_get_features(ntb->epf->epc, + ntb->epf->func_no, + ntb->epf->vfunc_no); + align = epc_features->align; + + if (size < 128) + size = 128; + + if (align) + size = ALIGN(size, align); + else + size = roundup_pow_of_two(size); + + barno = ntb->epf_ntb_bar[BAR_DB]; + + mw_addr = pci_epf_alloc_space(ntb->epf, size, barno, align, 0); + if (!mw_addr) { + dev_err(dev, "Failed to allocate OB address\n"); + return -ENOMEM; + } + + ntb->epf_db = mw_addr; + + epf_bar = &ntb->epf->bar[barno]; + + ret = pci_epc_set_bar(ntb->epf->epc, ntb->epf->func_no, ntb->epf->vfunc_no, epf_bar); + if (ret) { + dev_err(dev, "Doorbell BAR set failed\n"); + goto err_alloc_peer_mem; + } + return ret; + +err_alloc_peer_mem: + pci_epc_mem_free_addr(ntb->epf->epc, epf_bar->phys_addr, mw_addr, epf_bar->size); + return -1; +} + +static void epf_ntb_mw_bar_clear(struct epf_ntb *ntb, int num_mws); + +/** + * epf_ntb_db_bar_clear() - Clear doorbell BAR and free memory + * allocated in peer's outbound address space + * @ntb: NTB device that facilitates communication between HOST and vHOST + */ +static void epf_ntb_db_bar_clear(struct epf_ntb *ntb) +{ + enum pci_barno barno; + + barno = ntb->epf_ntb_bar[BAR_DB]; + pci_epf_free_space(ntb->epf, ntb->epf_db, barno, 0); + pci_epc_clear_bar(ntb->epf->epc, + ntb->epf->func_no, + ntb->epf->vfunc_no, + &ntb->epf->bar[barno]); +} + +/** + * epf_ntb_mw_bar_init() - Configure Memory window BARs + * @ntb: NTB device that facilitates communication between HOST and vHOST + * + */ +static int epf_ntb_mw_bar_init(struct epf_ntb *ntb) +{ + int ret = 0; + int i; + u64 size; + enum pci_barno barno; + struct device *dev = &ntb->epf->dev; + + for (i = 0; i < ntb->num_mws; i++) { + size = ntb->mws_size[i]; + barno = ntb->epf_ntb_bar[BAR_MW0 + i]; + + ntb->epf->bar[barno].barno = barno; + ntb->epf->bar[barno].size = size; + ntb->epf->bar[barno].addr = NULL; + ntb->epf->bar[barno].phys_addr = 0; + ntb->epf->bar[barno].flags |= upper_32_bits(size) ? + PCI_BASE_ADDRESS_MEM_TYPE_64 : + PCI_BASE_ADDRESS_MEM_TYPE_32; + + ret = pci_epc_set_bar(ntb->epf->epc, + ntb->epf->func_no, + ntb->epf->vfunc_no, + &ntb->epf->bar[barno]); + if (ret) { + dev_err(dev, "MW set failed\n"); + goto err_alloc_mem; + } + + /* Allocate EPC outbound memory windows to vpci vntb device */ + ntb->vpci_mw_addr[i] = pci_epc_mem_alloc_addr(ntb->epf->epc, + &ntb->vpci_mw_phy[i], + size); + if (!ntb->vpci_mw_addr[i]) { + ret = -ENOMEM; + dev_err(dev, "Failed to allocate source address\n"); + goto err_set_bar; + } + } + + return ret; + +err_set_bar: + pci_epc_clear_bar(ntb->epf->epc, + ntb->epf->func_no, + ntb->epf->vfunc_no, + &ntb->epf->bar[barno]); +err_alloc_mem: + epf_ntb_mw_bar_clear(ntb, i); + return ret; +} + +/** + * epf_ntb_mw_bar_clear() - Clear Memory window BARs + * @ntb: NTB device that facilitates communication between HOST and vHOST + */ +static void epf_ntb_mw_bar_clear(struct epf_ntb *ntb, int num_mws) +{ + enum pci_barno barno; + int i; + + for (i = 0; i < num_mws; i++) { + barno = ntb->epf_ntb_bar[BAR_MW0 + i]; + pci_epc_clear_bar(ntb->epf->epc, + ntb->epf->func_no, + ntb->epf->vfunc_no, + &ntb->epf->bar[barno]); + + pci_epc_mem_free_addr(ntb->epf->epc, + ntb->vpci_mw_phy[i], + ntb->vpci_mw_addr[i], + ntb->mws_size[i]); + } +} + +/** + * epf_ntb_epc_destroy() - Cleanup NTB EPC interface + * @ntb: NTB device that facilitates communication between HOST and vHOST + * + * Wrapper for epf_ntb_epc_destroy_interface() to cleanup all the NTB interfaces + */ +static void epf_ntb_epc_destroy(struct epf_ntb *ntb) +{ + pci_epc_remove_epf(ntb->epf->epc, ntb->epf, 0); + pci_epc_put(ntb->epf->epc); +} + +/** + * epf_ntb_init_epc_bar() - Identify BARs to be used for each of the NTB + * constructs (scratchpad region, doorbell, memorywindow) + * @ntb: NTB device that facilitates communication between HOST and vHOST + */ +static int epf_ntb_init_epc_bar(struct epf_ntb *ntb) +{ + const struct pci_epc_features *epc_features; + enum pci_barno barno; + enum epf_ntb_bar bar; + struct device *dev; + u32 num_mws; + int i; + + barno = BAR_0; + num_mws = ntb->num_mws; + dev = &ntb->epf->dev; + epc_features = pci_epc_get_features(ntb->epf->epc, ntb->epf->func_no, ntb->epf->vfunc_no); + + /* These are required BARs which are mandatory for NTB functionality */ + for (bar = BAR_CONFIG; bar <= BAR_MW0; bar++, barno++) { + barno = pci_epc_get_next_free_bar(epc_features, barno); + if (barno < 0) { + dev_err(dev, "Fail to get NTB function BAR\n"); + return barno; + } + ntb->epf_ntb_bar[bar] = barno; + } + + /* These are optional BARs which don't impact NTB functionality */ + for (bar = BAR_MW1, i = 1; i < num_mws; bar++, barno++, i++) { + barno = pci_epc_get_next_free_bar(epc_features, barno); + if (barno < 0) { + ntb->num_mws = i; + dev_dbg(dev, "BAR not available for > MW%d\n", i + 1); + } + ntb->epf_ntb_bar[bar] = barno; + } + + return 0; +} + +/** + * epf_ntb_epc_init() - Initialize NTB interface + * @ntb: NTB device that facilitates communication between HOST and vHOST2 + * + * Wrapper to initialize a particular EPC interface and start the workqueue + * to check for commands from host. This function will write to the + * EP controller HW for configuring it. + */ +static int epf_ntb_epc_init(struct epf_ntb *ntb) +{ + u8 func_no, vfunc_no; + struct pci_epc *epc; + struct pci_epf *epf; + struct device *dev; + int ret; + + epf = ntb->epf; + dev = &epf->dev; + epc = epf->epc; + func_no = ntb->epf->func_no; + vfunc_no = ntb->epf->vfunc_no; + + ret = epf_ntb_config_sspad_bar_set(ntb); + if (ret) { + dev_err(dev, "Config/self SPAD BAR init failed"); + return ret; + } + + ret = epf_ntb_configure_interrupt(ntb); + if (ret) { + dev_err(dev, "Interrupt configuration failed\n"); + goto err_config_interrupt; + } + + ret = epf_ntb_db_bar_init(ntb); + if (ret) { + dev_err(dev, "DB BAR init failed\n"); + goto err_db_bar_init; + } + + ret = epf_ntb_mw_bar_init(ntb); + if (ret) { + dev_err(dev, "MW BAR init failed\n"); + goto err_mw_bar_init; + } + + if (vfunc_no <= 1) { + ret = pci_epc_write_header(epc, func_no, vfunc_no, epf->header); + if (ret) { + dev_err(dev, "Configuration header write failed\n"); + goto err_write_header; + } + } + + INIT_DELAYED_WORK(&ntb->cmd_handler, epf_ntb_cmd_handler); + queue_work(kpcintb_workqueue, &ntb->cmd_handler.work); + + return 0; + +err_write_header: + epf_ntb_mw_bar_clear(ntb, ntb->num_mws); +err_mw_bar_init: + epf_ntb_db_bar_clear(ntb); +err_db_bar_init: +err_config_interrupt: + epf_ntb_config_sspad_bar_clear(ntb); + + return ret; +} + + +/** + * epf_ntb_epc_cleanup() - Cleanup all NTB interfaces + * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * + * Wrapper to cleanup all NTB interfaces. + */ +static void epf_ntb_epc_cleanup(struct epf_ntb *ntb) +{ + epf_ntb_db_bar_clear(ntb); + epf_ntb_mw_bar_clear(ntb, ntb->num_mws); +} + +#define EPF_NTB_R(_name) \ +static ssize_t epf_ntb_##_name##_show(struct config_item *item, \ + char *page) \ +{ \ + struct config_group *group = to_config_group(item); \ + struct epf_ntb *ntb = to_epf_ntb(group); \ + \ + return sprintf(page, "%d\n", ntb->_name); \ +} + +#define EPF_NTB_W(_name) \ +static ssize_t epf_ntb_##_name##_store(struct config_item *item, \ + const char *page, size_t len) \ +{ \ + struct config_group *group = to_config_group(item); \ + struct epf_ntb *ntb = to_epf_ntb(group); \ + u32 val; \ + int ret; \ + \ + ret = kstrtou32(page, 0, &val); \ + if (ret) \ + return ret; \ + \ + ntb->_name = val; \ + \ + return len; \ +} + +#define EPF_NTB_MW_R(_name) \ +static ssize_t epf_ntb_##_name##_show(struct config_item *item, \ + char *page) \ +{ \ + struct config_group *group = to_config_group(item); \ + struct epf_ntb *ntb = to_epf_ntb(group); \ + struct device *dev = &ntb->epf->dev; \ + int win_no; \ + \ + if (sscanf(#_name, "mw%d", &win_no) != 1) \ + return -EINVAL; \ + \ + if (win_no <= 0 || win_no > ntb->num_mws) { \ + dev_err(dev, "Invalid num_nws: %d value\n", ntb->num_mws); \ + return -EINVAL; \ + } \ + \ + return sprintf(page, "%lld\n", ntb->mws_size[win_no - 1]); \ +} + +#define EPF_NTB_MW_W(_name) \ +static ssize_t epf_ntb_##_name##_store(struct config_item *item, \ + const char *page, size_t len) \ +{ \ + struct config_group *group = to_config_group(item); \ + struct epf_ntb *ntb = to_epf_ntb(group); \ + struct device *dev = &ntb->epf->dev; \ + int win_no; \ + u64 val; \ + int ret; \ + \ + ret = kstrtou64(page, 0, &val); \ + if (ret) \ + return ret; \ + \ + if (sscanf(#_name, "mw%d", &win_no) != 1) \ + return -EINVAL; \ + \ + if (win_no <= 0 || win_no > ntb->num_mws) { \ + dev_err(dev, "Invalid num_nws: %d value\n", ntb->num_mws); \ + return -EINVAL; \ + } \ + \ + ntb->mws_size[win_no - 1] = val; \ + \ + return len; \ +} + +static ssize_t epf_ntb_num_mws_store(struct config_item *item, + const char *page, size_t len) +{ + struct config_group *group = to_config_group(item); + struct epf_ntb *ntb = to_epf_ntb(group); + u32 val; + int ret; + + ret = kstrtou32(page, 0, &val); + if (ret) + return ret; + + if (val > MAX_MW) + return -EINVAL; + + ntb->num_mws = val; + + return len; +} + +EPF_NTB_R(spad_count) +EPF_NTB_W(spad_count) +EPF_NTB_R(db_count) +EPF_NTB_W(db_count) +EPF_NTB_R(num_mws) +EPF_NTB_R(vbus_number) +EPF_NTB_W(vbus_number) +EPF_NTB_R(vntb_pid) +EPF_NTB_W(vntb_pid) +EPF_NTB_R(vntb_vid) +EPF_NTB_W(vntb_vid) +EPF_NTB_MW_R(mw1) +EPF_NTB_MW_W(mw1) +EPF_NTB_MW_R(mw2) +EPF_NTB_MW_W(mw2) +EPF_NTB_MW_R(mw3) +EPF_NTB_MW_W(mw3) +EPF_NTB_MW_R(mw4) +EPF_NTB_MW_W(mw4) + +CONFIGFS_ATTR(epf_ntb_, spad_count); +CONFIGFS_ATTR(epf_ntb_, db_count); +CONFIGFS_ATTR(epf_ntb_, num_mws); +CONFIGFS_ATTR(epf_ntb_, mw1); +CONFIGFS_ATTR(epf_ntb_, mw2); +CONFIGFS_ATTR(epf_ntb_, mw3); +CONFIGFS_ATTR(epf_ntb_, mw4); +CONFIGFS_ATTR(epf_ntb_, vbus_number); +CONFIGFS_ATTR(epf_ntb_, vntb_pid); +CONFIGFS_ATTR(epf_ntb_, vntb_vid); + +static struct configfs_attribute *epf_ntb_attrs[] = { + &epf_ntb_attr_spad_count, + &epf_ntb_attr_db_count, + &epf_ntb_attr_num_mws, + &epf_ntb_attr_mw1, + &epf_ntb_attr_mw2, + &epf_ntb_attr_mw3, + &epf_ntb_attr_mw4, + &epf_ntb_attr_vbus_number, + &epf_ntb_attr_vntb_pid, + &epf_ntb_attr_vntb_vid, + NULL, +}; + +static const struct config_item_type ntb_group_type = { + .ct_attrs = epf_ntb_attrs, + .ct_owner = THIS_MODULE, +}; + +/** + * epf_ntb_add_cfs() - Add configfs directory specific to NTB + * @epf: NTB endpoint function device + * @group: A pointer to the config_group structure referencing a group of + * config_items of a specific type that belong to a specific sub-system. + * + * Add configfs directory specific to NTB. This directory will hold + * NTB specific properties like db_count, spad_count, num_mws etc., + */ +static struct config_group *epf_ntb_add_cfs(struct pci_epf *epf, + struct config_group *group) +{ + struct epf_ntb *ntb = epf_get_drvdata(epf); + struct config_group *ntb_group = &ntb->group; + struct device *dev = &epf->dev; + + config_group_init_type_name(ntb_group, dev_name(dev), &ntb_group_type); + + return ntb_group; +} + +/*==== virtual PCI bus driver, which only load virtual NTB PCI driver ====*/ + +static u32 pci_space[] = { + 0xffffffff, /*DeviceID, Vendor ID*/ + 0, /*Status, Command*/ + 0xffffffff, /*Class code, subclass, prog if, revision id*/ + 0x40, /*bist, header type, latency Timer, cache line size*/ + 0, /*BAR 0*/ + 0, /*BAR 1*/ + 0, /*BAR 2*/ + 0, /*BAR 3*/ + 0, /*BAR 4*/ + 0, /*BAR 5*/ + 0, /*Cardbus cis point*/ + 0, /*Subsystem ID Subystem vendor id*/ + 0, /*ROM Base Address*/ + 0, /*Reserved, Cap. Point*/ + 0, /*Reserved,*/ + 0, /*Max Lat, Min Gnt, interrupt pin, interrupt line*/ +}; + +static int pci_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val) +{ + if (devfn == 0) { + memcpy(val, ((u8 *)pci_space) + where, size); + return PCIBIOS_SUCCESSFUL; + } + return PCIBIOS_DEVICE_NOT_FOUND; +} + +static int pci_write(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val) +{ + return 0; +} + +static struct pci_ops vpci_ops = { + .read = pci_read, + .write = pci_write, +}; + +static int vpci_scan_bus(void *sysdata) +{ + struct pci_bus *vpci_bus; + struct epf_ntb *ndev = sysdata; + + vpci_bus = pci_scan_bus(ndev->vbus_number, &vpci_ops, sysdata); + if (vpci_bus) + pr_err("create pci bus\n"); + + pci_bus_add_devices(vpci_bus); + + return 0; +} + +/*==================== Virtual PCIe NTB driver ==========================*/ + +static int vntb_epf_mw_count(struct ntb_dev *ntb, int pidx) +{ + struct epf_ntb *ndev = ntb_ndev(ntb); + + return ndev->num_mws; +} + +static int vntb_epf_spad_count(struct ntb_dev *ntb) +{ + return ntb_ndev(ntb)->spad_count; +} + +static int vntb_epf_peer_mw_count(struct ntb_dev *ntb) +{ + return ntb_ndev(ntb)->num_mws; +} + +static u64 vntb_epf_db_valid_mask(struct ntb_dev *ntb) +{ + return BIT_ULL(ntb_ndev(ntb)->db_count) - 1; +} + +static int vntb_epf_db_set_mask(struct ntb_dev *ntb, u64 db_bits) +{ + return 0; +} + +static int vntb_epf_mw_set_trans(struct ntb_dev *ndev, int pidx, int idx, + dma_addr_t addr, resource_size_t size) +{ + struct epf_ntb *ntb = ntb_ndev(ndev); + struct pci_epf_bar *epf_bar; + enum pci_barno barno; + int ret; + struct device *dev; + + dev = &ntb->ntb.dev; + barno = ntb->epf_ntb_bar[BAR_MW0 + idx]; + epf_bar = &ntb->epf->bar[barno]; + epf_bar->phys_addr = addr; + epf_bar->barno = barno; + epf_bar->size = size; + + ret = pci_epc_set_bar(ntb->epf->epc, 0, 0, epf_bar); + if (ret) { + dev_err(dev, "failure set mw trans\n"); + return ret; + } + return 0; +} + +static int vntb_epf_mw_clear_trans(struct ntb_dev *ntb, int pidx, int idx) +{ + return 0; +} + +static int vntb_epf_peer_mw_get_addr(struct ntb_dev *ndev, int idx, + phys_addr_t *base, resource_size_t *size) +{ + + struct epf_ntb *ntb = ntb_ndev(ndev); + + if (base) + *base = ntb->vpci_mw_phy[idx]; + + if (size) + *size = ntb->mws_size[idx]; + + return 0; +} + +static int vntb_epf_link_enable(struct ntb_dev *ntb, + enum ntb_speed max_speed, + enum ntb_width max_width) +{ + return 0; +} + +static u32 vntb_epf_spad_read(struct ntb_dev *ndev, int idx) +{ + struct epf_ntb *ntb = ntb_ndev(ndev); + int off = ntb->reg->spad_offset, ct = ntb->reg->spad_count * 4; + u32 val; + void __iomem *base = ntb->reg; + + val = readl(base + off + ct + idx * 4); + return val; +} + +static int vntb_epf_spad_write(struct ntb_dev *ndev, int idx, u32 val) +{ + struct epf_ntb *ntb = ntb_ndev(ndev); + struct epf_ntb_ctrl *ctrl = ntb->reg; + int off = ctrl->spad_offset, ct = ctrl->spad_count * 4; + void __iomem *base = ntb->reg; + + writel(val, base + off + ct + idx * 4); + return 0; +} + +static u32 vntb_epf_peer_spad_read(struct ntb_dev *ndev, int pidx, int idx) +{ + struct epf_ntb *ntb = ntb_ndev(ndev); + struct epf_ntb_ctrl *ctrl = ntb->reg; + int off = ctrl->spad_offset; + void __iomem *base = ntb->reg; + u32 val; + + val = readl(base + off + idx * 4); + return val; +} + +static int vntb_epf_peer_spad_write(struct ntb_dev *ndev, int pidx, int idx, u32 val) +{ + struct epf_ntb *ntb = ntb_ndev(ndev); + struct epf_ntb_ctrl *ctrl = ntb->reg; + int off = ctrl->spad_offset; + void __iomem *base = ntb->reg; + + writel(val, base + off + idx * 4); + return 0; +} + +static int vntb_epf_peer_db_set(struct ntb_dev *ndev, u64 db_bits) +{ + u32 interrupt_num = ffs(db_bits) + 1; + struct epf_ntb *ntb = ntb_ndev(ndev); + u8 func_no, vfunc_no; + int ret; + + func_no = ntb->epf->func_no; + vfunc_no = ntb->epf->vfunc_no; + + ret = pci_epc_raise_irq(ntb->epf->epc, + func_no, + vfunc_no, + PCI_EPC_IRQ_MSI, + interrupt_num + 1); + if (ret) + dev_err(&ntb->ntb.dev, "Failed to raise IRQ\n"); + + return ret; +} + +static u64 vntb_epf_db_read(struct ntb_dev *ndev) +{ + struct epf_ntb *ntb = ntb_ndev(ndev); + + return ntb->db; +} + +static int vntb_epf_mw_get_align(struct ntb_dev *ndev, int pidx, int idx, + resource_size_t *addr_align, + resource_size_t *size_align, + resource_size_t *size_max) +{ + struct epf_ntb *ntb = ntb_ndev(ndev); + + if (addr_align) + *addr_align = SZ_4K; + + if (size_align) + *size_align = 1; + + if (size_max) + *size_max = ntb->mws_size[idx]; + + return 0; +} + +static u64 vntb_epf_link_is_up(struct ntb_dev *ndev, + enum ntb_speed *speed, + enum ntb_width *width) +{ + struct epf_ntb *ntb = ntb_ndev(ndev); + + return ntb->reg->link_status; +} + +static int vntb_epf_db_clear_mask(struct ntb_dev *ndev, u64 db_bits) +{ + return 0; +} + +static int vntb_epf_db_clear(struct ntb_dev *ndev, u64 db_bits) +{ + struct epf_ntb *ntb = ntb_ndev(ndev); + + ntb->db &= ~db_bits; + return 0; +} + +static int vntb_epf_link_disable(struct ntb_dev *ntb) +{ + return 0; +} + +static const struct ntb_dev_ops vntb_epf_ops = { + .mw_count = vntb_epf_mw_count, + .spad_count = vntb_epf_spad_count, + .peer_mw_count = vntb_epf_peer_mw_count, + .db_valid_mask = vntb_epf_db_valid_mask, + .db_set_mask = vntb_epf_db_set_mask, + .mw_set_trans = vntb_epf_mw_set_trans, + .mw_clear_trans = vntb_epf_mw_clear_trans, + .peer_mw_get_addr = vntb_epf_peer_mw_get_addr, + .link_enable = vntb_epf_link_enable, + .spad_read = vntb_epf_spad_read, + .spad_write = vntb_epf_spad_write, + .peer_spad_read = vntb_epf_peer_spad_read, + .peer_spad_write = vntb_epf_peer_spad_write, + .peer_db_set = vntb_epf_peer_db_set, + .db_read = vntb_epf_db_read, + .mw_get_align = vntb_epf_mw_get_align, + .link_is_up = vntb_epf_link_is_up, + .db_clear_mask = vntb_epf_db_clear_mask, + .db_clear = vntb_epf_db_clear, + .link_disable = vntb_epf_link_disable, +}; + +static int pci_vntb_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + int ret; + struct epf_ntb *ndev = (struct epf_ntb *)pdev->sysdata; + struct device *dev = &pdev->dev; + + ndev->ntb.pdev = pdev; + ndev->ntb.topo = NTB_TOPO_NONE; + ndev->ntb.ops = &vntb_epf_ops; + + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(dev, "Cannot set DMA mask\n"); + return -EINVAL; + } + + ret = ntb_register_device(&ndev->ntb); + if (ret) { + dev_err(dev, "Failed to register NTB device\n"); + goto err_register_dev; + } + + dev_dbg(dev, "PCI Virtual NTB driver loaded\n"); + return 0; + +err_register_dev: + return -EINVAL; +} + +static struct pci_device_id pci_vntb_table[] = { + { + PCI_DEVICE(0xffff, 0xffff), + }, + {}, +}; + +static struct pci_driver vntb_pci_driver = { + .name = "pci-vntb", + .id_table = pci_vntb_table, + .probe = pci_vntb_probe, +}; + +/* ============ PCIe EPF Driver Bind ====================*/ + +/** + * epf_ntb_bind() - Initialize endpoint controller to provide NTB functionality + * @epf: NTB endpoint function device + * + * Initialize both the endpoint controllers associated with NTB function device. + * Invoked when a primary interface or secondary interface is bound to EPC + * device. This function will succeed only when EPC is bound to both the + * interfaces. + */ +static int epf_ntb_bind(struct pci_epf *epf) +{ + struct epf_ntb *ntb = epf_get_drvdata(epf); + struct device *dev = &epf->dev; + int ret; + + if (!epf->epc) { + dev_dbg(dev, "PRIMARY EPC interface not yet bound\n"); + return 0; + } + + ret = epf_ntb_init_epc_bar(ntb); + if (ret) { + dev_err(dev, "Failed to create NTB EPC\n"); + goto err_bar_init; + } + + ret = epf_ntb_config_spad_bar_alloc(ntb); + if (ret) { + dev_err(dev, "Failed to allocate BAR memory\n"); + goto err_bar_alloc; + } + + ret = epf_ntb_epc_init(ntb); + if (ret) { + dev_err(dev, "Failed to initialize EPC\n"); + goto err_bar_alloc; + } + + epf_set_drvdata(epf, ntb); + + pci_space[0] = (ntb->vntb_pid << 16) | ntb->vntb_vid; + pci_vntb_table[0].vendor = ntb->vntb_vid; + pci_vntb_table[0].device = ntb->vntb_pid; + + ret = pci_register_driver(&vntb_pci_driver); + if (ret) { + dev_err(dev, "failure register vntb pci driver\n"); + goto err_bar_alloc; + } + + vpci_scan_bus(ntb); + + return 0; + +err_bar_alloc: + epf_ntb_config_spad_bar_free(ntb); + +err_bar_init: + epf_ntb_epc_destroy(ntb); + + return ret; +} + +/** + * epf_ntb_unbind() - Cleanup the initialization from epf_ntb_bind() + * @epf: NTB endpoint function device + * + * Cleanup the initialization from epf_ntb_bind() + */ +static void epf_ntb_unbind(struct pci_epf *epf) +{ + struct epf_ntb *ntb = epf_get_drvdata(epf); + + epf_ntb_epc_cleanup(ntb); + epf_ntb_config_spad_bar_free(ntb); + epf_ntb_epc_destroy(ntb); + + pci_unregister_driver(&vntb_pci_driver); +} + +// EPF driver probe +static struct pci_epf_ops epf_ntb_ops = { + .bind = epf_ntb_bind, + .unbind = epf_ntb_unbind, + .add_cfs = epf_ntb_add_cfs, +}; + +/** + * epf_ntb_probe() - Probe NTB function driver + * @epf: NTB endpoint function device + * + * Probe NTB function driver when endpoint function bus detects a NTB + * endpoint function. + */ +static int epf_ntb_probe(struct pci_epf *epf) +{ + struct epf_ntb *ntb; + struct device *dev; + + dev = &epf->dev; + + ntb = devm_kzalloc(dev, sizeof(*ntb), GFP_KERNEL); + if (!ntb) + return -ENOMEM; + + epf->header = &epf_ntb_header; + ntb->epf = epf; + ntb->vbus_number = 0xff; + epf_set_drvdata(epf, ntb); + + dev_info(dev, "pci-ep epf driver loaded\n"); + return 0; +} + +static const struct pci_epf_device_id epf_ntb_ids[] = { + { + .name = "pci_epf_vntb", + }, + {}, +}; + +static struct pci_epf_driver epf_ntb_driver = { + .driver.name = "pci_epf_vntb", + .probe = epf_ntb_probe, + .id_table = epf_ntb_ids, + .ops = &epf_ntb_ops, + .owner = THIS_MODULE, +}; + +static int __init epf_ntb_init(void) +{ + int ret; + + kpcintb_workqueue = alloc_workqueue("kpcintb", WQ_MEM_RECLAIM | + WQ_HIGHPRI, 0); + ret = pci_epf_register_driver(&epf_ntb_driver); + if (ret) { + destroy_workqueue(kpcintb_workqueue); + pr_err("Failed to register pci epf ntb driver --> %d\n", ret); + return ret; + } + + return 0; +} +module_init(epf_ntb_init); + +static void __exit epf_ntb_exit(void) +{ + pci_epf_unregister_driver(&epf_ntb_driver); + destroy_workqueue(kpcintb_workqueue); +} +module_exit(epf_ntb_exit); + +MODULE_DESCRIPTION("PCI EPF NTB DRIVER"); +MODULE_AUTHOR("Frank Li <Frank.li@nxp.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/perf/riscv_pmu.c b/drivers/perf/riscv_pmu.c index 2c961839903d..ebca5eab9c9b 100644 --- a/drivers/perf/riscv_pmu.c +++ b/drivers/perf/riscv_pmu.c @@ -170,7 +170,6 @@ int riscv_pmu_event_set_period(struct perf_event *event) left = (max_period >> 1); local64_set(&hwc->prev_count, (u64)-left); - perf_event_update_userpage(event); return overflow; } diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c index 79a3de515ece..6f6681bbfd36 100644 --- a/drivers/perf/riscv_pmu_sbi.c +++ b/drivers/perf/riscv_pmu_sbi.c @@ -41,20 +41,6 @@ static const struct attribute_group *riscv_pmu_attr_groups[] = { NULL, }; -union sbi_pmu_ctr_info { - unsigned long value; - struct { - unsigned long csr:12; - unsigned long width:6; -#if __riscv_xlen == 32 - unsigned long reserved:13; -#else - unsigned long reserved:45; -#endif - unsigned long type:1; - }; -}; - /* * RISC-V doesn't have hetergenous harts yet. This need to be part of * per_cpu in case of harts with different pmu counters @@ -294,8 +280,13 @@ static int pmu_sbi_ctr_get_idx(struct perf_event *event) cflags |= SBI_PMU_CFG_FLAG_SET_UINH; /* retrieve the available counter index */ +#if defined(CONFIG_32BIT) + ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_CFG_MATCH, cbase, cmask, + cflags, hwc->event_base, hwc->config, hwc->config >> 32); +#else ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_CFG_MATCH, cbase, cmask, cflags, hwc->event_base, hwc->config, 0); +#endif if (ret.error) { pr_debug("Not able to find a counter for event %lx config %llx\n", hwc->event_base, hwc->config); @@ -437,8 +428,13 @@ static void pmu_sbi_ctr_start(struct perf_event *event, u64 ival) struct hw_perf_event *hwc = &event->hw; unsigned long flag = SBI_PMU_START_FLAG_SET_INIT_VALUE; +#if defined(CONFIG_32BIT) ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_START, hwc->idx, 1, flag, ival, ival >> 32, 0); +#else + ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_START, hwc->idx, + 1, flag, ival, 0, 0); +#endif if (ret.error && (ret.error != SBI_ERR_ALREADY_STARTED)) pr_err("Starting counter idx %d failed with error %d\n", hwc->idx, sbi_err_map_linux_errno(ret.error)); @@ -545,8 +541,14 @@ static inline void pmu_sbi_start_overflow_mask(struct riscv_pmu *pmu, hwc = &event->hw; max_period = riscv_pmu_ctr_get_width_mask(event); init_val = local64_read(&hwc->prev_count) & max_period; +#if defined(CONFIG_32BIT) + sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_START, idx, 1, + flag, init_val, init_val >> 32, 0); +#else sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_START, idx, 1, flag, init_val, 0, 0); +#endif + perf_event_update_userpage(event); } ctr_ovf_mask = ctr_ovf_mask >> 1; idx++; diff --git a/drivers/platform/mellanox/mlxbf-tmfifo.c b/drivers/platform/mellanox/mlxbf-tmfifo.c index 38800e86ed8a..8be13d416f48 100644 --- a/drivers/platform/mellanox/mlxbf-tmfifo.c +++ b/drivers/platform/mellanox/mlxbf-tmfifo.c @@ -928,6 +928,7 @@ static int mlxbf_tmfifo_virtio_find_vqs(struct virtio_device *vdev, struct virtqueue *vqs[], vq_callback_t *callbacks[], const char * const names[], + u32 sizes[], const bool *ctx, struct irq_affinity *desc) { @@ -959,6 +960,8 @@ static int mlxbf_tmfifo_virtio_find_vqs(struct virtio_device *vdev, goto error; } + vq->num_max = vring->num; + vqs[i] = vq; vring->vq = vq; vq->priv = vring; diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 4b563db3ab3e..a8c46ba5878f 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -297,4 +297,10 @@ config NVMEM_REBOOT_MODE then the bootloader can read it and take different action according to the mode. +config POWER_MLXBF + tristate "Mellanox BlueField power handling driver" + depends on (GPIO_MLXBF2 && ACPI) + help + This driver supports reset or low power mode handling for Mellanox BlueField. + endif diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index f606a2f60539..0a39424fc558 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -35,3 +35,4 @@ obj-$(CONFIG_REBOOT_MODE) += reboot-mode.o obj-$(CONFIG_SYSCON_REBOOT_MODE) += syscon-reboot-mode.o obj-$(CONFIG_POWER_RESET_SC27XX) += sc27xx-poweroff.o obj-$(CONFIG_NVMEM_REBOOT_MODE) += nvmem-reboot-mode.o +obj-$(CONFIG_POWER_MLXBF) += pwr-mlxbf.o diff --git a/drivers/power/reset/at91-reset.c b/drivers/power/reset/at91-reset.c index 64def79d557a..741e44a017c3 100644 --- a/drivers/power/reset/at91-reset.c +++ b/drivers/power/reset/at91-reset.c @@ -17,10 +17,13 @@ #include <linux/of_address.h> #include <linux/platform_device.h> #include <linux/reboot.h> +#include <linux/reset-controller.h> #include <soc/at91/at91sam9_ddrsdr.h> #include <soc/at91/at91sam9_sdramc.h> +#include <dt-bindings/reset/sama7g5-reset.h> + #define AT91_RSTC_CR 0x00 /* Reset Controller Control Register */ #define AT91_RSTC_PROCRST BIT(0) /* Processor Reset */ #define AT91_RSTC_PERRST BIT(2) /* Peripheral Reset */ @@ -39,6 +42,17 @@ #define AT91_RSTC_URSTIEN BIT(4) /* User Reset Interrupt Enable */ #define AT91_RSTC_ERSTL GENMASK(11, 8) /* External Reset Length */ +/** + * enum reset_type - reset types + * @RESET_TYPE_GENERAL: first power-up reset + * @RESET_TYPE_WAKEUP: return from backup mode + * @RESET_TYPE_WATCHDOG: watchdog fault + * @RESET_TYPE_SOFTWARE: processor reset required by software + * @RESET_TYPE_USER: NRST pin detected low + * @RESET_TYPE_CPU_FAIL: CPU clock failure detection + * @RESET_TYPE_XTAL_FAIL: 32KHz crystal failure dectection fault + * @RESET_TYPE_ULP2: ULP2 reset + */ enum reset_type { RESET_TYPE_GENERAL = 0, RESET_TYPE_WAKEUP = 1, @@ -50,15 +64,48 @@ enum reset_type { RESET_TYPE_ULP2 = 8, }; +/** + * struct at91_reset - AT91 reset specific data structure + * @rstc_base: base address for system reset + * @ramc_base: array with base addresses of RAM controllers + * @dev_base: base address for devices reset + * @sclk: slow clock + * @data: platform specific reset data + * @rcdev: reset controller device + * @lock: lock for devices reset register access + * @nb: reset notifier block + * @args: SoC specific system reset arguments + * @ramc_lpr: SDRAM Controller Low Power Register + */ struct at91_reset { void __iomem *rstc_base; void __iomem *ramc_base[2]; + void __iomem *dev_base; struct clk *sclk; + const struct at91_reset_data *data; + struct reset_controller_dev rcdev; + spinlock_t lock; struct notifier_block nb; u32 args; u32 ramc_lpr; }; +#define to_at91_reset(r) container_of(r, struct at91_reset, rcdev) + +/** + * struct at91_reset_data - AT91 reset data + * @reset_args: SoC specific system reset arguments + * @n_device_reset: number of device resets + * @device_reset_min_id: min id for device reset + * @device_reset_max_id: max id for device reset + */ +struct at91_reset_data { + u32 reset_args; + u32 n_device_reset; + u8 device_reset_min_id; + u8 device_reset_max_id; +}; + /* * unless the SDRAM is cleanly shutdown before we hit the * reset register it can be left driving the data bus and @@ -95,7 +142,7 @@ static int at91_reset(struct notifier_block *this, unsigned long mode, "r" (reset->rstc_base), "r" (1), "r" cpu_to_le32(AT91_DDRSDRC_LPCB_POWER_DOWN), - "r" (reset->args), + "r" (reset->data->reset_args), "r" (reset->ramc_lpr) : "r4"); @@ -153,34 +200,133 @@ static const struct of_device_id at91_ramc_of_match[] = { { /* sentinel */ } }; +static const struct at91_reset_data sam9260 = { + .reset_args = AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST, +}; + +static const struct at91_reset_data samx7 = { + .reset_args = AT91_RSTC_KEY | AT91_RSTC_PROCRST, +}; + +static const struct at91_reset_data sama7g5 = { + .reset_args = AT91_RSTC_KEY | AT91_RSTC_PROCRST, + .n_device_reset = 3, + .device_reset_min_id = SAMA7G5_RESET_USB_PHY1, + .device_reset_max_id = SAMA7G5_RESET_USB_PHY3, +}; + static const struct of_device_id at91_reset_of_match[] = { { .compatible = "atmel,at91sam9260-rstc", - .data = (void *)(AT91_RSTC_KEY | AT91_RSTC_PERRST | - AT91_RSTC_PROCRST), + .data = &sam9260, }, { .compatible = "atmel,at91sam9g45-rstc", - .data = (void *)(AT91_RSTC_KEY | AT91_RSTC_PERRST | - AT91_RSTC_PROCRST) + .data = &sam9260, }, { .compatible = "atmel,sama5d3-rstc", - .data = (void *)(AT91_RSTC_KEY | AT91_RSTC_PERRST | - AT91_RSTC_PROCRST) + .data = &sam9260, }, { .compatible = "atmel,samx7-rstc", - .data = (void *)(AT91_RSTC_KEY | AT91_RSTC_PROCRST) + .data = &samx7, }, { .compatible = "microchip,sam9x60-rstc", - .data = (void *)(AT91_RSTC_KEY | AT91_RSTC_PROCRST) + .data = &samx7, + }, + { + .compatible = "microchip,sama7g5-rstc", + .data = &sama7g5, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, at91_reset_of_match); +static int at91_reset_update(struct reset_controller_dev *rcdev, + unsigned long id, bool assert) +{ + struct at91_reset *reset = to_at91_reset(rcdev); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&reset->lock, flags); + val = readl_relaxed(reset->dev_base); + if (assert) + val |= BIT(id); + else + val &= ~BIT(id); + writel_relaxed(val, reset->dev_base); + spin_unlock_irqrestore(&reset->lock, flags); + + return 0; +} + +static int at91_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return at91_reset_update(rcdev, id, true); +} + +static int at91_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return at91_reset_update(rcdev, id, false); +} + +static int at91_reset_dev_status(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct at91_reset *reset = to_at91_reset(rcdev); + u32 val; + + val = readl_relaxed(reset->dev_base); + + return !!(val & BIT(id)); +} + +static const struct reset_control_ops at91_reset_ops = { + .assert = at91_reset_assert, + .deassert = at91_reset_deassert, + .status = at91_reset_dev_status, +}; + +static int at91_reset_of_xlate(struct reset_controller_dev *rcdev, + const struct of_phandle_args *reset_spec) +{ + struct at91_reset *reset = to_at91_reset(rcdev); + + if (!reset->data->n_device_reset || + (reset_spec->args[0] < reset->data->device_reset_min_id || + reset_spec->args[0] > reset->data->device_reset_max_id)) + return -EINVAL; + + return reset_spec->args[0]; +} + +static int at91_rcdev_init(struct at91_reset *reset, + struct platform_device *pdev) +{ + if (!reset->data->n_device_reset) + return 0; + + reset->dev_base = devm_of_iomap(&pdev->dev, pdev->dev.of_node, 1, + NULL); + if (IS_ERR(reset->dev_base)) + return -ENODEV; + + spin_lock_init(&reset->lock); + reset->rcdev.ops = &at91_reset_ops; + reset->rcdev.owner = THIS_MODULE; + reset->rcdev.of_node = pdev->dev.of_node; + reset->rcdev.nr_resets = reset->data->n_device_reset; + reset->rcdev.of_reset_n_cells = 1; + reset->rcdev.of_xlate = at91_reset_of_xlate; + + return devm_reset_controller_register(&pdev->dev, &reset->rcdev); +} + static int __init at91_reset_probe(struct platform_device *pdev) { const struct of_device_id *match; @@ -212,10 +358,12 @@ static int __init at91_reset_probe(struct platform_device *pdev) } } - match = of_match_node(at91_reset_of_match, pdev->dev.of_node); + reset->data = device_get_match_data(&pdev->dev); + if (!reset->data) + return -ENODEV; + reset->nb.notifier_call = at91_reset; reset->nb.priority = 192; - reset->args = (u32)match->data; reset->sclk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(reset->sclk)) @@ -229,6 +377,10 @@ static int __init at91_reset_probe(struct platform_device *pdev) platform_set_drvdata(pdev, reset); + ret = at91_rcdev_init(reset, pdev); + if (ret) + goto disable_clk; + if (of_device_is_compatible(pdev->dev.of_node, "microchip,sam9x60-rstc")) { u32 val = readl(reset->rstc_base + AT91_RSTC_MR); @@ -237,14 +389,16 @@ static int __init at91_reset_probe(struct platform_device *pdev) } ret = register_restart_handler(&reset->nb); - if (ret) { - clk_disable_unprepare(reset->sclk); - return ret; - } + if (ret) + goto disable_clk; at91_reset_status(pdev, reset->rstc_base); return 0; + +disable_clk: + clk_disable_unprepare(reset->sclk); + return ret; } static int __exit at91_reset_remove(struct platform_device *pdev) diff --git a/drivers/power/reset/pwr-mlxbf.c b/drivers/power/reset/pwr-mlxbf.c new file mode 100644 index 000000000000..12dedf841a44 --- /dev/null +++ b/drivers/power/reset/pwr-mlxbf.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0-only or BSD-3-Clause + +/* + * Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. + */ + +#include <linux/acpi.h> +#include <linux/device.h> +#include <linux/devm-helpers.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/reboot.h> +#include <linux/types.h> + +struct pwr_mlxbf { + struct work_struct send_work; + const char *hid; +}; + +static void pwr_mlxbf_send_work(struct work_struct *work) +{ + acpi_bus_generate_netlink_event("button/power.*", "Power Button", 0x80, 1); +} + +static irqreturn_t pwr_mlxbf_irq(int irq, void *ptr) +{ + const char *rst_pwr_hid = "MLNXBF24"; + const char *low_pwr_hid = "MLNXBF29"; + struct pwr_mlxbf *priv = ptr; + + if (!strncmp(priv->hid, rst_pwr_hid, 8)) + emergency_restart(); + + if (!strncmp(priv->hid, low_pwr_hid, 8)) + schedule_work(&priv->send_work); + + return IRQ_HANDLED; +} + +static int pwr_mlxbf_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct acpi_device *adev; + struct pwr_mlxbf *priv; + const char *hid; + int irq, err; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + adev = ACPI_COMPANION(dev); + if (!adev) + return -ENXIO; + + hid = acpi_device_hid(adev); + priv->hid = hid; + + irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), 0); + if (irq < 0) + return dev_err_probe(dev, irq, "Error getting %s irq.\n", priv->hid); + + err = devm_work_autocancel(dev, &priv->send_work, pwr_mlxbf_send_work); + if (err) + return err; + + err = devm_request_irq(dev, irq, pwr_mlxbf_irq, 0, hid, priv); + if (err) + dev_err(dev, "Failed request of %s irq\n", priv->hid); + + return err; +} + +static const struct acpi_device_id __maybe_unused pwr_mlxbf_acpi_match[] = { + { "MLNXBF24", 0 }, + { "MLNXBF29", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, pwr_mlxbf_acpi_match); + +static struct platform_driver pwr_mlxbf_driver = { + .driver = { + .name = "pwr_mlxbf", + .acpi_match_table = pwr_mlxbf_acpi_match, + }, + .probe = pwr_mlxbf_probe, +}; + +module_platform_driver(pwr_mlxbf_driver); + +MODULE_DESCRIPTION("Mellanox BlueField power driver"); +MODULE_AUTHOR("Asmaa Mnebhi <asmaa@nvidia.com>"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/power/supply/ab8500-chargalg.h b/drivers/power/supply/ab8500-chargalg.h index f47a0061c36a..8534d067ba95 100644 --- a/drivers/power/supply/ab8500-chargalg.h +++ b/drivers/power/supply/ab8500-chargalg.h @@ -34,7 +34,6 @@ struct ux500_charger_ops { * @max_out_volt_uv maximum output charger voltage in uV * @max_out_curr_ua maximum output charger current in uA * @enabled indicates if this charger is used or not - * @external external charger unit (pm2xxx) */ struct ux500_charger { struct power_supply *psy; @@ -43,9 +42,6 @@ struct ux500_charger { int max_out_curr_ua; int wdt_refresh; bool enabled; - bool external; }; -extern struct blocking_notifier_head charger_notifier_list; - #endif /* _AB8500_CHARGALG_H_ */ diff --git a/drivers/power/supply/ab8500_btemp.c b/drivers/power/supply/ab8500_btemp.c index b7e842dff567..863fabe05bdc 100644 --- a/drivers/power/supply/ab8500_btemp.c +++ b/drivers/power/supply/ab8500_btemp.c @@ -697,7 +697,6 @@ static void ab8500_btemp_unbind(struct device *dev, struct device *master, /* Delete the work queue */ destroy_workqueue(di->btemp_wq); - flush_scheduled_work(); } static const struct component_ops ab8500_btemp_component_ops = { diff --git a/drivers/power/supply/ab8500_chargalg.c b/drivers/power/supply/ab8500_chargalg.c index 431bbc352d1b..ae4be553f424 100644 --- a/drivers/power/supply/ab8500_chargalg.c +++ b/drivers/power/supply/ab8500_chargalg.c @@ -246,9 +246,6 @@ struct ab8500_chargalg { struct kobject chargalg_kobject; }; -/*External charger prepare notifier*/ -BLOCKING_NOTIFIER_HEAD(charger_notifier_list); - /* Main battery properties */ static enum power_supply_property ab8500_chargalg_props[] = { POWER_SUPPLY_PROP_STATUS, @@ -343,8 +340,7 @@ static int ab8500_chargalg_check_charger_enable(struct ab8500_chargalg *di) return di->usb_chg->ops.check_enable(di->usb_chg, bi->constant_charge_voltage_max_uv, bi->constant_charge_current_max_ua); - } else if ((di->chg_info.charger_type & AC_CHG) && - !(di->ac_chg->external)) { + } else if (di->chg_info.charger_type & AC_CHG) { return di->ac_chg->ops.check_enable(di->ac_chg, bi->constant_charge_voltage_max_uv, bi->constant_charge_current_max_ua); @@ -473,15 +469,6 @@ static int ab8500_chargalg_kick_watchdog(struct ab8500_chargalg *di) /* Check if charger exists and kick watchdog if charging */ if (di->ac_chg && di->ac_chg->ops.kick_wd && di->chg_info.online_chg & AC_CHG) { - /* - * If AB charger watchdog expired, pm2xxx charging - * gets disabled. To be safe, kick both AB charger watchdog - * and pm2xxx watchdog. - */ - if (di->ac_chg->external && - di->usb_chg && di->usb_chg->ops.kick_wd) - di->usb_chg->ops.kick_wd(di->usb_chg); - return di->ac_chg->ops.kick_wd(di->ac_chg); } else if (di->usb_chg && di->usb_chg->ops.kick_wd && di->chg_info.online_chg & USB_CHG) @@ -517,14 +504,6 @@ static int ab8500_chargalg_ac_en(struct ab8500_chargalg *di, int enable, di->chg_info.ac_iset_ua = iset_ua; di->chg_info.ac_vset_uv = vset_uv; - /* Enable external charger */ - if (enable && di->ac_chg->external && - !ab8500_chargalg_ex_ac_enable_toggle) { - blocking_notifier_call_chain(&charger_notifier_list, - 0, di->dev); - ab8500_chargalg_ex_ac_enable_toggle++; - } - return di->ac_chg->ops.enable(di->ac_chg, enable, vset_uv, iset_ua); } @@ -1217,6 +1196,34 @@ static void ab8500_chargalg_external_power_changed(struct power_supply *psy) } /** + * ab8500_chargalg_time_to_restart() - time to restart CC/CV charging? + * @di: charging algorithm state + * + * This checks if the voltage or capacity of the battery has fallen so + * low that we need to restart the CC/CV charge cycle. + */ +static bool ab8500_chargalg_time_to_restart(struct ab8500_chargalg *di) +{ + struct power_supply_battery_info *bi = di->bm->bi; + + /* Sanity check - these need to have some reasonable values */ + if (!di->batt_data.volt_uv || !di->batt_data.percent) + return false; + + /* Some batteries tell us at which voltage we should restart charging */ + if (bi->charge_restart_voltage_uv > 0) { + if (di->batt_data.volt_uv <= bi->charge_restart_voltage_uv) + return true; + /* Else we restart as we reach a certain capacity */ + } else { + if (di->batt_data.percent <= AB8500_RECHARGE_CAP) + return true; + } + + return false; +} + +/** * ab8500_chargalg_algorithm() - Main function for the algorithm * @di: pointer to the ab8500_chargalg structure * @@ -1459,7 +1466,7 @@ static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di) fallthrough; case STATE_WAIT_FOR_RECHARGE: - if (di->batt_data.percent <= AB8500_RECHARGE_CAP) + if (ab8500_chargalg_time_to_restart(di)) ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); break; @@ -1486,6 +1493,14 @@ static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di) ab8500_chargalg_stop_maintenance_timer(di); ab8500_chargalg_state_to(di, STATE_MAINTENANCE_B_INIT); } + /* + * This happens if the voltage drops too quickly during + * maintenance charging, especially in older batteries. + */ + if (ab8500_chargalg_time_to_restart(di)) { + ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); + dev_info(di->dev, "restarted charging from maintenance state A - battery getting old?\n"); + } break; case STATE_MAINTENANCE_B_INIT: @@ -1510,6 +1525,14 @@ static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di) ab8500_chargalg_stop_maintenance_timer(di); ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); } + /* + * This happens if the voltage drops too quickly during + * maintenance charging, especially in older batteries. + */ + if (ab8500_chargalg_time_to_restart(di)) { + ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); + dev_info(di->dev, "restarted charging from maintenance state B - battery getting old?\n"); + } break; case STATE_TEMP_LOWHIGH_INIT: @@ -1746,7 +1769,6 @@ static void ab8500_chargalg_unbind(struct device *dev, struct device *master, /* Delete the work queue */ destroy_workqueue(di->chargalg_wq); - flush_scheduled_work(); } static const struct component_ops ab8500_chargalg_component_ops = { diff --git a/drivers/power/supply/ab8500_charger.c b/drivers/power/supply/ab8500_charger.c index d04d087caa50..c19c50442761 100644 --- a/drivers/power/supply/ab8500_charger.c +++ b/drivers/power/supply/ab8500_charger.c @@ -1716,29 +1716,6 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger, return ret; } -static int ab8500_external_charger_prepare(struct notifier_block *charger_nb, - unsigned long event, void *data) -{ - int ret; - struct device *dev = data; - /*Toggle External charger control pin*/ - ret = abx500_set_register_interruptible(dev, AB8500_SYS_CTRL1_BLOCK, - AB8500_SYS_CHARGER_CONTROL_REG, - EXTERNAL_CHARGER_DISABLE_REG_VAL); - if (ret < 0) { - dev_err(dev, "write reg failed %d\n", ret); - goto out; - } - ret = abx500_set_register_interruptible(dev, AB8500_SYS_CTRL1_BLOCK, - AB8500_SYS_CHARGER_CONTROL_REG, - EXTERNAL_CHARGER_ENABLE_REG_VAL); - if (ret < 0) - dev_err(dev, "Write reg failed %d\n", ret); - -out: - return ret; -} - /** * ab8500_charger_usb_check_enable() - enable usb charging * @charger: pointer to the ux500_charger structure @@ -3316,10 +3293,6 @@ static int __maybe_unused ab8500_charger_suspend(struct device *dev) return 0; } -static struct notifier_block charger_nb = { - .notifier_call = ab8500_external_charger_prepare, -}; - static char *supply_interface[] = { "ab8500_chargalg", "ab8500_fg", @@ -3378,6 +3351,7 @@ static int ab8500_charger_bind(struct device *dev) ret = component_bind_all(dev, di); if (ret) { dev_err(dev, "can't bind component devices\n"); + destroy_workqueue(di->charger_wq); return ret; } @@ -3404,8 +3378,6 @@ static void ab8500_charger_unbind(struct device *dev) /* Delete the work queue */ destroy_workqueue(di->charger_wq); - flush_scheduled_work(); - /* Unbind fg, btemp, algorithm */ component_unbind_all(dev, di); } @@ -3540,7 +3512,6 @@ static int ab8500_charger_probe(struct platform_device *pdev) */ if (!is_ab8505(di->parent)) di->ac_chg.enabled = true; - di->ac_chg.external = false; /* USB supply */ /* ux500_charger sub-class */ @@ -3553,7 +3524,6 @@ static int ab8500_charger_probe(struct platform_device *pdev) di->usb_chg.max_out_curr_ua = ab8500_charge_output_curr_map[ARRAY_SIZE(ab8500_charge_output_curr_map) - 1]; di->usb_chg.wdt_refresh = CHG_WD_INTERVAL; - di->usb_chg.external = false; di->usb_state.usb_current_ua = -1; mutex_init(&di->charger_attached_mutex); @@ -3677,17 +3647,11 @@ static int ab8500_charger_probe(struct platform_device *pdev) goto remove_ab8500_bm; } - /* Notifier for external charger enabling */ - if (!di->ac_chg.enabled) - blocking_notifier_chain_register( - &charger_notifier_list, &charger_nb); - - di->usb_phy = usb_get_phy(USB_PHY_TYPE_USB2); if (IS_ERR_OR_NULL(di->usb_phy)) { dev_err(dev, "failed to get usb transceiver\n"); ret = -EINVAL; - goto out_charger_notifier; + goto remove_ab8500_bm; } di->nb.notifier_call = ab8500_charger_usb_notifier_call; ret = usb_register_notifier(di->usb_phy, &di->nb); @@ -3696,7 +3660,6 @@ static int ab8500_charger_probe(struct platform_device *pdev) goto put_usb_phy; } - ret = component_master_add_with_match(&pdev->dev, &ab8500_charger_comp_ops, match); @@ -3711,10 +3674,6 @@ free_notifier: usb_unregister_notifier(di->usb_phy, &di->nb); put_usb_phy: usb_put_phy(di->usb_phy); -out_charger_notifier: - if (!di->ac_chg.enabled) - blocking_notifier_chain_unregister( - &charger_notifier_list, &charger_nb); remove_ab8500_bm: ab8500_bm_of_remove(di->usb_chg.psy, di->bm); return ret; @@ -3729,9 +3688,6 @@ static int ab8500_charger_remove(struct platform_device *pdev) usb_unregister_notifier(di->usb_phy, &di->nb); ab8500_bm_of_remove(di->usb_chg.psy, di->bm); usb_put_phy(di->usb_phy); - if (!di->ac_chg.enabled) - blocking_notifier_chain_unregister( - &charger_notifier_list, &charger_nb); return 0; } diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c index 4339fa9ff009..c6c9804280db 100644 --- a/drivers/power/supply/ab8500_fg.c +++ b/drivers/power/supply/ab8500_fg.c @@ -412,7 +412,7 @@ static int ab8500_fg_add_cap_sample(struct ab8500_fg *di, int sample) * ab8500_fg_clear_cap_samples() - Clear average filter * @di: pointer to the ab8500_fg structure * - * The capacity filter is is reset to zero. + * The capacity filter is reset to zero. */ static void ab8500_fg_clear_cap_samples(struct ab8500_fg *di) { @@ -3234,7 +3234,6 @@ static int ab8500_fg_remove(struct platform_device *pdev) struct ab8500_fg *di = platform_get_drvdata(pdev); destroy_workqueue(di->fg_wq); - flush_scheduled_work(); component_del(&pdev->dev, &ab8500_fg_component_ops); list_del(&di->node); ab8500_fg_sysfs_exit(di); diff --git a/drivers/power/supply/bq24257_charger.c b/drivers/power/supply/bq24257_charger.c index 96cb3290bcaa..ecba9ab86faf 100644 --- a/drivers/power/supply/bq24257_charger.c +++ b/drivers/power/supply/bq24257_charger.c @@ -287,7 +287,7 @@ static int bq24257_set_input_current_limit(struct bq24257_device *bq, { /* * Address the case where the user manually sets an input current limit - * while the charger auto-detection mechanism is is active. In this + * while the charger auto-detection mechanism is active. In this * case we want to abort and go straight to the user-specified value. */ if (bq->iilimit_autoset_enable) diff --git a/drivers/power/supply/cros_peripheral_charger.c b/drivers/power/supply/cros_peripheral_charger.c index 9fe6d826148d..1379afd9698d 100644 --- a/drivers/power/supply/cros_peripheral_charger.c +++ b/drivers/power/supply/cros_peripheral_charger.c @@ -63,7 +63,7 @@ static int cros_pchg_ec_command(const struct charger_data *charger, struct cros_ec_command *msg; int ret; - msg = kzalloc(sizeof(*msg) + max(outsize, insize), GFP_KERNEL); + msg = kzalloc(struct_size(msg, data, max(outsize, insize)), GFP_KERNEL); if (!msg) return -ENOMEM; diff --git a/drivers/power/supply/goldfish_battery.c b/drivers/power/supply/goldfish_battery.c index bf1754355c9f..a58d713d75ce 100644 --- a/drivers/power/supply/goldfish_battery.c +++ b/drivers/power/supply/goldfish_battery.c @@ -221,10 +221,8 @@ static int goldfish_battery_probe(struct platform_device *pdev) } data->irq = platform_get_irq(pdev, 0); - if (data->irq < 0) { - dev_err(&pdev->dev, "platform_get_irq failed\n"); + if (data->irq < 0) return -ENODEV; - } ret = devm_request_irq(&pdev->dev, data->irq, goldfish_battery_interrupt, diff --git a/drivers/power/supply/lp8788-charger.c b/drivers/power/supply/lp8788-charger.c index 397e5a03b7d9..56c57529c228 100644 --- a/drivers/power/supply/lp8788-charger.c +++ b/drivers/power/supply/lp8788-charger.c @@ -376,7 +376,7 @@ static int lp8788_update_charger_params(struct platform_device *pdev, return 0; } - /* settting charging parameters */ + /* setting charging parameters */ for (i = 0; i < pdata->num_chg_params; i++) { param = pdata->chg_params + i; diff --git a/drivers/power/supply/max77976_charger.c b/drivers/power/supply/max77976_charger.c index 8b6c8cfa7503..4fed74511931 100644 --- a/drivers/power/supply/max77976_charger.c +++ b/drivers/power/supply/max77976_charger.c @@ -3,7 +3,7 @@ * max77976_charger.c - Driver for the Maxim MAX77976 battery charger * * Copyright (C) 2021 Luca Ceresoli - * Author: Luca Ceresoli <luca@lucaceresoli.net> + * Author: Luca Ceresoli <luca.ceresoli@bootlin.com> */ #include <linux/i2c.h> @@ -504,6 +504,6 @@ static struct i2c_driver max77976_driver = { }; module_i2c_driver(max77976_driver); -MODULE_AUTHOR("Luca Ceresoli <luca@lucaceresoli.net>"); +MODULE_AUTHOR("Luca Ceresoli <luca.ceresoli@bootlin.com>"); MODULE_DESCRIPTION("Maxim MAX77976 charger driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/supply/olpc_battery.c b/drivers/power/supply/olpc_battery.c index e0476ec06601..a5da20ffd685 100644 --- a/drivers/power/supply/olpc_battery.c +++ b/drivers/power/supply/olpc_battery.c @@ -635,6 +635,7 @@ static int olpc_battery_probe(struct platform_device *pdev) struct power_supply_config bat_psy_cfg = {}; struct power_supply_config ac_psy_cfg = {}; struct olpc_battery_data *data; + struct device_node *np; uint8_t status; uint8_t ecver; int ret; @@ -649,7 +650,9 @@ static int olpc_battery_probe(struct platform_device *pdev) if (ret) return ret; - if (of_find_compatible_node(NULL, NULL, "olpc,xo1.75-ec")) { + np = of_find_compatible_node(NULL, NULL, "olpc,xo1.75-ec"); + if (np) { + of_node_put(np); /* XO 1.75 */ data->new_proto = true; data->little_endian = true; diff --git a/drivers/power/supply/pm2301_charger.h b/drivers/power/supply/pm2301_charger.h deleted file mode 100644 index 74397e377982..000000000000 --- a/drivers/power/supply/pm2301_charger.h +++ /dev/null @@ -1,492 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) ST-Ericsson SA 2012 - * - * PM2301 power supply interface - */ - -#ifndef PM2301_CHARGER_H -#define PM2301_CHARGER_H - -/* Watchdog timeout constant */ -#define WD_TIMER 0x30 /* 4min */ -#define WD_KICK_INTERVAL (30 * HZ) - -#define PM2XXX_NUM_INT_REG 0x6 - -/* Constant voltage/current */ -#define PM2XXX_CONST_CURR 0x0 -#define PM2XXX_CONST_VOLT 0x1 - -/* Lowest charger voltage is 3.39V -> 0x4E */ -#define LOW_VOLT_REG 0x4E - -#define PM2XXX_BATT_CTRL_REG1 0x00 -#define PM2XXX_BATT_CTRL_REG2 0x01 -#define PM2XXX_BATT_CTRL_REG3 0x02 -#define PM2XXX_BATT_CTRL_REG4 0x03 -#define PM2XXX_BATT_CTRL_REG5 0x04 -#define PM2XXX_BATT_CTRL_REG6 0x05 -#define PM2XXX_BATT_CTRL_REG7 0x06 -#define PM2XXX_BATT_CTRL_REG8 0x07 -#define PM2XXX_NTC_CTRL_REG1 0x08 -#define PM2XXX_NTC_CTRL_REG2 0x09 -#define PM2XXX_BATT_CTRL_REG9 0x0A -#define PM2XXX_BATT_STAT_REG1 0x0B -#define PM2XXX_INP_VOLT_VPWR2 0x11 -#define PM2XXX_INP_DROP_VPWR2 0x13 -#define PM2XXX_INP_VOLT_VPWR1 0x15 -#define PM2XXX_INP_DROP_VPWR1 0x17 -#define PM2XXX_INP_MODE_VPWR 0x18 -#define PM2XXX_BATT_WD_KICK 0x70 -#define PM2XXX_DEV_VER_STAT 0x0C -#define PM2XXX_THERM_WARN_CTRL_REG 0x20 -#define PM2XXX_BATT_DISC_REG 0x21 -#define PM2XXX_BATT_LOW_LEV_COMP_REG 0x22 -#define PM2XXX_BATT_LOW_LEV_VAL_REG 0x23 -#define PM2XXX_I2C_PAD_CTRL_REG 0x24 -#define PM2XXX_SW_CTRL_REG 0x26 -#define PM2XXX_LED_CTRL_REG 0x28 - -#define PM2XXX_REG_INT1 0x40 -#define PM2XXX_MASK_REG_INT1 0x50 -#define PM2XXX_SRCE_REG_INT1 0x60 -#define PM2XXX_REG_INT2 0x41 -#define PM2XXX_MASK_REG_INT2 0x51 -#define PM2XXX_SRCE_REG_INT2 0x61 -#define PM2XXX_REG_INT3 0x42 -#define PM2XXX_MASK_REG_INT3 0x52 -#define PM2XXX_SRCE_REG_INT3 0x62 -#define PM2XXX_REG_INT4 0x43 -#define PM2XXX_MASK_REG_INT4 0x53 -#define PM2XXX_SRCE_REG_INT4 0x63 -#define PM2XXX_REG_INT5 0x44 -#define PM2XXX_MASK_REG_INT5 0x54 -#define PM2XXX_SRCE_REG_INT5 0x64 -#define PM2XXX_REG_INT6 0x45 -#define PM2XXX_MASK_REG_INT6 0x55 -#define PM2XXX_SRCE_REG_INT6 0x65 - -#define VPWR_OVV 0x0 -#define VSYSTEM_OVV 0x1 - -/* control Reg 1 */ -#define PM2XXX_CH_RESUME_EN 0x1 -#define PM2XXX_CH_RESUME_DIS 0x0 - -/* control Reg 2 */ -#define PM2XXX_CH_AUTO_RESUME_EN 0X2 -#define PM2XXX_CH_AUTO_RESUME_DIS 0X0 -#define PM2XXX_CHARGER_ENA 0x4 -#define PM2XXX_CHARGER_DIS 0x0 - -/* control Reg 3 */ -#define PM2XXX_CH_WD_CC_PHASE_OFF 0x0 -#define PM2XXX_CH_WD_CC_PHASE_5MIN 0x1 -#define PM2XXX_CH_WD_CC_PHASE_10MIN 0x2 -#define PM2XXX_CH_WD_CC_PHASE_30MIN 0x3 -#define PM2XXX_CH_WD_CC_PHASE_60MIN 0x4 -#define PM2XXX_CH_WD_CC_PHASE_120MIN 0x5 -#define PM2XXX_CH_WD_CC_PHASE_240MIN 0x6 -#define PM2XXX_CH_WD_CC_PHASE_360MIN 0x7 - -#define PM2XXX_CH_WD_CV_PHASE_OFF (0x0<<3) -#define PM2XXX_CH_WD_CV_PHASE_5MIN (0x1<<3) -#define PM2XXX_CH_WD_CV_PHASE_10MIN (0x2<<3) -#define PM2XXX_CH_WD_CV_PHASE_30MIN (0x3<<3) -#define PM2XXX_CH_WD_CV_PHASE_60MIN (0x4<<3) -#define PM2XXX_CH_WD_CV_PHASE_120MIN (0x5<<3) -#define PM2XXX_CH_WD_CV_PHASE_240MIN (0x6<<3) -#define PM2XXX_CH_WD_CV_PHASE_360MIN (0x7<<3) - -/* control Reg 4 */ -#define PM2XXX_CH_WD_PRECH_PHASE_OFF 0x0 -#define PM2XXX_CH_WD_PRECH_PHASE_1MIN 0x1 -#define PM2XXX_CH_WD_PRECH_PHASE_5MIN 0x2 -#define PM2XXX_CH_WD_PRECH_PHASE_10MIN 0x3 -#define PM2XXX_CH_WD_PRECH_PHASE_30MIN 0x4 -#define PM2XXX_CH_WD_PRECH_PHASE_60MIN 0x5 -#define PM2XXX_CH_WD_PRECH_PHASE_120MIN 0x6 -#define PM2XXX_CH_WD_PRECH_PHASE_240MIN 0x7 - -/* control Reg 5 */ -#define PM2XXX_CH_WD_AUTO_TIMEOUT_NONE 0x0 -#define PM2XXX_CH_WD_AUTO_TIMEOUT_20MIN 0x1 - -/* control Reg 6 */ -#define PM2XXX_DIR_CH_CC_CURRENT_MASK 0x0F -#define PM2XXX_DIR_CH_CC_CURRENT_200MA 0x0 -#define PM2XXX_DIR_CH_CC_CURRENT_400MA 0x2 -#define PM2XXX_DIR_CH_CC_CURRENT_600MA 0x3 -#define PM2XXX_DIR_CH_CC_CURRENT_800MA 0x4 -#define PM2XXX_DIR_CH_CC_CURRENT_1000MA 0x5 -#define PM2XXX_DIR_CH_CC_CURRENT_1200MA 0x6 -#define PM2XXX_DIR_CH_CC_CURRENT_1400MA 0x7 -#define PM2XXX_DIR_CH_CC_CURRENT_1600MA 0x8 -#define PM2XXX_DIR_CH_CC_CURRENT_1800MA 0x9 -#define PM2XXX_DIR_CH_CC_CURRENT_2000MA 0xA -#define PM2XXX_DIR_CH_CC_CURRENT_2200MA 0xB -#define PM2XXX_DIR_CH_CC_CURRENT_2400MA 0xC -#define PM2XXX_DIR_CH_CC_CURRENT_2600MA 0xD -#define PM2XXX_DIR_CH_CC_CURRENT_2800MA 0xE -#define PM2XXX_DIR_CH_CC_CURRENT_3000MA 0xF - -#define PM2XXX_CH_PRECH_CURRENT_MASK 0x30 -#define PM2XXX_CH_PRECH_CURRENT_25MA (0x0<<4) -#define PM2XXX_CH_PRECH_CURRENT_50MA (0x1<<4) -#define PM2XXX_CH_PRECH_CURRENT_75MA (0x2<<4) -#define PM2XXX_CH_PRECH_CURRENT_100MA (0x3<<4) - -#define PM2XXX_CH_EOC_CURRENT_MASK 0xC0 -#define PM2XXX_CH_EOC_CURRENT_100MA (0x0<<6) -#define PM2XXX_CH_EOC_CURRENT_150MA (0x1<<6) -#define PM2XXX_CH_EOC_CURRENT_300MA (0x2<<6) -#define PM2XXX_CH_EOC_CURRENT_400MA (0x3<<6) - -/* control Reg 7 */ -#define PM2XXX_CH_PRECH_VOL_2_5 0x0 -#define PM2XXX_CH_PRECH_VOL_2_7 0x1 -#define PM2XXX_CH_PRECH_VOL_2_9 0x2 -#define PM2XXX_CH_PRECH_VOL_3_1 0x3 - -#define PM2XXX_CH_VRESUME_VOL_3_2 (0x0<<2) -#define PM2XXX_CH_VRESUME_VOL_3_4 (0x1<<2) -#define PM2XXX_CH_VRESUME_VOL_3_6 (0x2<<2) -#define PM2XXX_CH_VRESUME_VOL_3_8 (0x3<<2) - -/* control Reg 8 */ -#define PM2XXX_CH_VOLT_MASK 0x3F -#define PM2XXX_CH_VOLT_3_5 0x0 -#define PM2XXX_CH_VOLT_3_5225 0x1 -#define PM2XXX_CH_VOLT_3_6 0x4 -#define PM2XXX_CH_VOLT_3_7 0x8 -#define PM2XXX_CH_VOLT_4_0 0x14 -#define PM2XXX_CH_VOLT_4_175 0x1B -#define PM2XXX_CH_VOLT_4_2 0x1C -#define PM2XXX_CH_VOLT_4_275 0x1F -#define PM2XXX_CH_VOLT_4_3 0x20 - -/*NTC control register 1*/ -#define PM2XXX_BTEMP_HIGH_TH_45 0x0 -#define PM2XXX_BTEMP_HIGH_TH_50 0x1 -#define PM2XXX_BTEMP_HIGH_TH_55 0x2 -#define PM2XXX_BTEMP_HIGH_TH_60 0x3 -#define PM2XXX_BTEMP_HIGH_TH_65 0x4 - -#define PM2XXX_BTEMP_LOW_TH_N5 (0x0<<3) -#define PM2XXX_BTEMP_LOW_TH_0 (0x1<<3) -#define PM2XXX_BTEMP_LOW_TH_5 (0x2<<3) -#define PM2XXX_BTEMP_LOW_TH_10 (0x3<<3) - -/*NTC control register 2*/ -#define PM2XXX_NTC_BETA_COEFF_3477 0x0 -#define PM2XXX_NTC_BETA_COEFF_3964 0x1 - -#define PM2XXX_NTC_RES_10K (0x0<<2) -#define PM2XXX_NTC_RES_47K (0x1<<2) -#define PM2XXX_NTC_RES_100K (0x2<<2) -#define PM2XXX_NTC_RES_NO_NTC (0x3<<2) - -/* control Reg 9 */ -#define PM2XXX_CH_CC_MODEDROP_EN 1 -#define PM2XXX_CH_CC_MODEDROP_DIS 0 - -#define PM2XXX_CH_CC_REDUCED_CURRENT_100MA (0x0<<1) -#define PM2XXX_CH_CC_REDUCED_CURRENT_200MA (0x1<<1) -#define PM2XXX_CH_CC_REDUCED_CURRENT_400MA (0x2<<1) -#define PM2XXX_CH_CC_REDUCED_CURRENT_IDENT (0x3<<1) - -#define PM2XXX_CHARCHING_INFO_DIS (0<<3) -#define PM2XXX_CHARCHING_INFO_EN (1<<3) - -#define PM2XXX_CH_150MV_DROP_300MV (0<<4) -#define PM2XXX_CH_150MV_DROP_150MV (1<<4) - - -/* charger status register */ -#define PM2XXX_CHG_STATUS_OFF 0x0 -#define PM2XXX_CHG_STATUS_ON 0x1 -#define PM2XXX_CHG_STATUS_FULL 0x2 -#define PM2XXX_CHG_STATUS_ERR 0x3 -#define PM2XXX_CHG_STATUS_WAIT 0x4 -#define PM2XXX_CHG_STATUS_NOBAT 0x5 - -/* Input charger voltage VPWR2 */ -#define PM2XXX_VPWR2_OVV_6_0 0x0 -#define PM2XXX_VPWR2_OVV_6_3 0x1 -#define PM2XXX_VPWR2_OVV_10 0x2 -#define PM2XXX_VPWR2_OVV_NONE 0x3 - -/* Input charger drop VPWR2 */ -#define PM2XXX_VPWR2_HW_OPT_EN (0x1<<4) -#define PM2XXX_VPWR2_HW_OPT_DIS (0x0<<4) - -#define PM2XXX_VPWR2_VALID_EN (0x1<<3) -#define PM2XXX_VPWR2_VALID_DIS (0x0<<3) - -#define PM2XXX_VPWR2_DROP_EN (0x1<<2) -#define PM2XXX_VPWR2_DROP_DIS (0x0<<2) - -/* Input charger voltage VPWR1 */ -#define PM2XXX_VPWR1_OVV_6_0 0x0 -#define PM2XXX_VPWR1_OVV_6_3 0x1 -#define PM2XXX_VPWR1_OVV_10 0x2 -#define PM2XXX_VPWR1_OVV_NONE 0x3 - -/* Input charger drop VPWR1 */ -#define PM2XXX_VPWR1_HW_OPT_EN (0x1<<4) -#define PM2XXX_VPWR1_HW_OPT_DIS (0x0<<4) - -#define PM2XXX_VPWR1_VALID_EN (0x1<<3) -#define PM2XXX_VPWR1_VALID_DIS (0x0<<3) - -#define PM2XXX_VPWR1_DROP_EN (0x1<<2) -#define PM2XXX_VPWR1_DROP_DIS (0x0<<2) - -/* Battery low level comparator control register */ -#define PM2XXX_VBAT_LOW_MONITORING_DIS 0x0 -#define PM2XXX_VBAT_LOW_MONITORING_ENA 0x1 - -/* Battery low level value control register */ -#define PM2XXX_VBAT_LOW_LEVEL_2_3 0x0 -#define PM2XXX_VBAT_LOW_LEVEL_2_4 0x1 -#define PM2XXX_VBAT_LOW_LEVEL_2_5 0x2 -#define PM2XXX_VBAT_LOW_LEVEL_2_6 0x3 -#define PM2XXX_VBAT_LOW_LEVEL_2_7 0x4 -#define PM2XXX_VBAT_LOW_LEVEL_2_8 0x5 -#define PM2XXX_VBAT_LOW_LEVEL_2_9 0x6 -#define PM2XXX_VBAT_LOW_LEVEL_3_0 0x7 -#define PM2XXX_VBAT_LOW_LEVEL_3_1 0x8 -#define PM2XXX_VBAT_LOW_LEVEL_3_2 0x9 -#define PM2XXX_VBAT_LOW_LEVEL_3_3 0xA -#define PM2XXX_VBAT_LOW_LEVEL_3_4 0xB -#define PM2XXX_VBAT_LOW_LEVEL_3_5 0xC -#define PM2XXX_VBAT_LOW_LEVEL_3_6 0xD -#define PM2XXX_VBAT_LOW_LEVEL_3_7 0xE -#define PM2XXX_VBAT_LOW_LEVEL_3_8 0xF -#define PM2XXX_VBAT_LOW_LEVEL_3_9 0x10 -#define PM2XXX_VBAT_LOW_LEVEL_4_0 0x11 -#define PM2XXX_VBAT_LOW_LEVEL_4_1 0x12 -#define PM2XXX_VBAT_LOW_LEVEL_4_2 0x13 - -/* SW CTRL */ -#define PM2XXX_SWCTRL_HW 0x0 -#define PM2XXX_SWCTRL_SW 0x1 - - -/* LED Driver Control */ -#define PM2XXX_LED_CURRENT_MASK 0x0C -#define PM2XXX_LED_CURRENT_2_5MA (0X0<<2) -#define PM2XXX_LED_CURRENT_1MA (0X1<<2) -#define PM2XXX_LED_CURRENT_5MA (0X2<<2) -#define PM2XXX_LED_CURRENT_10MA (0X3<<2) - -#define PM2XXX_LED_SELECT_MASK 0x02 -#define PM2XXX_LED_SELECT_EN (0X0<<1) -#define PM2XXX_LED_SELECT_DIS (0X1<<1) - -#define PM2XXX_ANTI_OVERSHOOT_MASK 0x01 -#define PM2XXX_ANTI_OVERSHOOT_DIS 0X0 -#define PM2XXX_ANTI_OVERSHOOT_EN 0X1 - -enum pm2xxx_reg_int1 { - PM2XXX_INT1_ITVBATDISCONNECT = 0x02, - PM2XXX_INT1_ITVBATLOWR = 0x04, - PM2XXX_INT1_ITVBATLOWF = 0x08, -}; - -enum pm2xxx_mask_reg_int1 { - PM2XXX_INT1_M_ITVBATDISCONNECT = 0x02, - PM2XXX_INT1_M_ITVBATLOWR = 0x04, - PM2XXX_INT1_M_ITVBATLOWF = 0x08, -}; - -enum pm2xxx_source_reg_int1 { - PM2XXX_INT1_S_ITVBATDISCONNECT = 0x02, - PM2XXX_INT1_S_ITVBATLOWR = 0x04, - PM2XXX_INT1_S_ITVBATLOWF = 0x08, -}; - -enum pm2xxx_reg_int2 { - PM2XXX_INT2_ITVPWR2PLUG = 0x01, - PM2XXX_INT2_ITVPWR2UNPLUG = 0x02, - PM2XXX_INT2_ITVPWR1PLUG = 0x04, - PM2XXX_INT2_ITVPWR1UNPLUG = 0x08, -}; - -enum pm2xxx_mask_reg_int2 { - PM2XXX_INT2_M_ITVPWR2PLUG = 0x01, - PM2XXX_INT2_M_ITVPWR2UNPLUG = 0x02, - PM2XXX_INT2_M_ITVPWR1PLUG = 0x04, - PM2XXX_INT2_M_ITVPWR1UNPLUG = 0x08, -}; - -enum pm2xxx_source_reg_int2 { - PM2XXX_INT2_S_ITVPWR2PLUG = 0x03, - PM2XXX_INT2_S_ITVPWR1PLUG = 0x0c, -}; - -enum pm2xxx_reg_int3 { - PM2XXX_INT3_ITCHPRECHARGEWD = 0x01, - PM2XXX_INT3_ITCHCCWD = 0x02, - PM2XXX_INT3_ITCHCVWD = 0x04, - PM2XXX_INT3_ITAUTOTIMEOUTWD = 0x08, -}; - -enum pm2xxx_mask_reg_int3 { - PM2XXX_INT3_M_ITCHPRECHARGEWD = 0x01, - PM2XXX_INT3_M_ITCHCCWD = 0x02, - PM2XXX_INT3_M_ITCHCVWD = 0x04, - PM2XXX_INT3_M_ITAUTOTIMEOUTWD = 0x08, -}; - -enum pm2xxx_source_reg_int3 { - PM2XXX_INT3_S_ITCHPRECHARGEWD = 0x01, - PM2XXX_INT3_S_ITCHCCWD = 0x02, - PM2XXX_INT3_S_ITCHCVWD = 0x04, - PM2XXX_INT3_S_ITAUTOTIMEOUTWD = 0x08, -}; - -enum pm2xxx_reg_int4 { - PM2XXX_INT4_ITBATTEMPCOLD = 0x01, - PM2XXX_INT4_ITBATTEMPHOT = 0x02, - PM2XXX_INT4_ITVPWR2OVV = 0x04, - PM2XXX_INT4_ITVPWR1OVV = 0x08, - PM2XXX_INT4_ITCHARGINGON = 0x10, - PM2XXX_INT4_ITVRESUME = 0x20, - PM2XXX_INT4_ITBATTFULL = 0x40, - PM2XXX_INT4_ITCVPHASE = 0x80, -}; - -enum pm2xxx_mask_reg_int4 { - PM2XXX_INT4_M_ITBATTEMPCOLD = 0x01, - PM2XXX_INT4_M_ITBATTEMPHOT = 0x02, - PM2XXX_INT4_M_ITVPWR2OVV = 0x04, - PM2XXX_INT4_M_ITVPWR1OVV = 0x08, - PM2XXX_INT4_M_ITCHARGINGON = 0x10, - PM2XXX_INT4_M_ITVRESUME = 0x20, - PM2XXX_INT4_M_ITBATTFULL = 0x40, - PM2XXX_INT4_M_ITCVPHASE = 0x80, -}; - -enum pm2xxx_source_reg_int4 { - PM2XXX_INT4_S_ITBATTEMPCOLD = 0x01, - PM2XXX_INT4_S_ITBATTEMPHOT = 0x02, - PM2XXX_INT4_S_ITVPWR2OVV = 0x04, - PM2XXX_INT4_S_ITVPWR1OVV = 0x08, - PM2XXX_INT4_S_ITCHARGINGON = 0x10, - PM2XXX_INT4_S_ITVRESUME = 0x20, - PM2XXX_INT4_S_ITBATTFULL = 0x40, - PM2XXX_INT4_S_ITCVPHASE = 0x80, -}; - -enum pm2xxx_reg_int5 { - PM2XXX_INT5_ITTHERMALSHUTDOWNRISE = 0x01, - PM2XXX_INT5_ITTHERMALSHUTDOWNFALL = 0x02, - PM2XXX_INT5_ITTHERMALWARNINGRISE = 0x04, - PM2XXX_INT5_ITTHERMALWARNINGFALL = 0x08, - PM2XXX_INT5_ITVSYSTEMOVV = 0x10, -}; - -enum pm2xxx_mask_reg_int5 { - PM2XXX_INT5_M_ITTHERMALSHUTDOWNRISE = 0x01, - PM2XXX_INT5_M_ITTHERMALSHUTDOWNFALL = 0x02, - PM2XXX_INT5_M_ITTHERMALWARNINGRISE = 0x04, - PM2XXX_INT5_M_ITTHERMALWARNINGFALL = 0x08, - PM2XXX_INT5_M_ITVSYSTEMOVV = 0x10, -}; - -enum pm2xxx_source_reg_int5 { - PM2XXX_INT5_S_ITTHERMALSHUTDOWNRISE = 0x01, - PM2XXX_INT5_S_ITTHERMALSHUTDOWNFALL = 0x02, - PM2XXX_INT5_S_ITTHERMALWARNINGRISE = 0x04, - PM2XXX_INT5_S_ITTHERMALWARNINGFALL = 0x08, - PM2XXX_INT5_S_ITVSYSTEMOVV = 0x10, -}; - -enum pm2xxx_reg_int6 { - PM2XXX_INT6_ITVPWR2DROP = 0x01, - PM2XXX_INT6_ITVPWR1DROP = 0x02, - PM2XXX_INT6_ITVPWR2VALIDRISE = 0x04, - PM2XXX_INT6_ITVPWR2VALIDFALL = 0x08, - PM2XXX_INT6_ITVPWR1VALIDRISE = 0x10, - PM2XXX_INT6_ITVPWR1VALIDFALL = 0x20, -}; - -enum pm2xxx_mask_reg_int6 { - PM2XXX_INT6_M_ITVPWR2DROP = 0x01, - PM2XXX_INT6_M_ITVPWR1DROP = 0x02, - PM2XXX_INT6_M_ITVPWR2VALIDRISE = 0x04, - PM2XXX_INT6_M_ITVPWR2VALIDFALL = 0x08, - PM2XXX_INT6_M_ITVPWR1VALIDRISE = 0x10, - PM2XXX_INT6_M_ITVPWR1VALIDFALL = 0x20, -}; - -enum pm2xxx_source_reg_int6 { - PM2XXX_INT6_S_ITVPWR2DROP = 0x01, - PM2XXX_INT6_S_ITVPWR1DROP = 0x02, - PM2XXX_INT6_S_ITVPWR2VALIDRISE = 0x04, - PM2XXX_INT6_S_ITVPWR2VALIDFALL = 0x08, - PM2XXX_INT6_S_ITVPWR1VALIDRISE = 0x10, - PM2XXX_INT6_S_ITVPWR1VALIDFALL = 0x20, -}; - -struct pm2xxx_charger_info { - int charger_connected; - int charger_online; - int cv_active; - bool wd_expired; -}; - -struct pm2xxx_charger_event_flags { - bool mainextchnotok; - bool main_thermal_prot; - bool ovv; - bool chgwdexp; -}; - -struct pm2xxx_interrupts { - u8 reg[PM2XXX_NUM_INT_REG]; - int (*handler[PM2XXX_NUM_INT_REG])(void *, int); -}; - -struct pm2xxx_config { - struct i2c_client *pm2xxx_i2c; - struct i2c_device_id *pm2xxx_id; -}; - -struct pm2xxx_irq { - char *name; - irqreturn_t (*isr)(int irq, void *data); -}; - -struct pm2xxx_charger { - struct device *dev; - u8 chip_id; - bool vddadc_en_ac; - struct pm2xxx_config config; - bool ac_conn; - unsigned int gpio_irq; - int vbat; - int old_vbat; - int failure_case; - int failure_input_ovv; - unsigned int lpn_pin; - struct pm2xxx_interrupts *pm2_int; - struct regulator *regu; - struct pm2xxx_bm_data *bat; - struct mutex lock; - struct ab8500 *parent; - struct pm2xxx_charger_info ac; - struct pm2xxx_charger_platform_data *pdata; - struct workqueue_struct *charger_wq; - struct delayed_work check_vbat_work; - struct work_struct ac_work; - struct work_struct check_main_thermal_prot_work; - struct delayed_work check_hw_failure_work; - struct ux500_charger ac_chg; - struct power_supply_desc ac_chg_desc; - struct pm2xxx_charger_event_flags flags; -}; - -#endif /* PM2301_CHARGER_H */ diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index 470253c337c7..4b5fb172fa99 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -263,13 +263,13 @@ static int power_supply_check_supplies(struct power_supply *psy) return 0; /* All supplies found, allocate char ** array for filling */ - psy->supplied_from = devm_kzalloc(&psy->dev, sizeof(psy->supplied_from), + psy->supplied_from = devm_kzalloc(&psy->dev, sizeof(*psy->supplied_from), GFP_KERNEL); if (!psy->supplied_from) return -ENOMEM; *psy->supplied_from = devm_kcalloc(&psy->dev, - cnt - 1, sizeof(char *), + cnt - 1, sizeof(**psy->supplied_from), GFP_KERNEL); if (!*psy->supplied_from) return -ENOMEM; diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 89832399e028..e5279ed9a8d7 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -335,7 +335,7 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) size_t size; /* actual size of vring (in bytes) */ - size = PAGE_ALIGN(vring_size(rvring->len, rvring->align)); + size = PAGE_ALIGN(vring_size(rvring->num, rvring->align)); rsc = (void *)rproc->table_ptr + rvdev->rsc_offset; @@ -402,7 +402,7 @@ rproc_parse_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int i) return -EINVAL; } - rvring->len = vring->num; + rvring->num = vring->num; rvring->align = vring->align; rvring->rvdev = rvdev; diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c index 70ab496d0431..81c4f5776109 100644 --- a/drivers/remoteproc/remoteproc_virtio.c +++ b/drivers/remoteproc/remoteproc_virtio.c @@ -87,7 +87,7 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev, struct fw_rsc_vdev *rsc; struct virtqueue *vq; void *addr; - int len, size; + int num, size; /* we're temporarily limited to two virtqueues per rvdev */ if (id >= ARRAY_SIZE(rvdev->vring)) @@ -104,20 +104,20 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev, rvring = &rvdev->vring[id]; addr = mem->va; - len = rvring->len; + num = rvring->num; /* zero vring */ - size = vring_size(len, rvring->align); + size = vring_size(num, rvring->align); memset(addr, 0, size); dev_dbg(dev, "vring%d: va %pK qsz %d notifyid %d\n", - id, addr, len, rvring->notifyid); + id, addr, num, rvring->notifyid); /* * Create the new vq, and tell virtio we're not interested in * the 'weak' smp barriers, since we're talking with a real device. */ - vq = vring_new_virtqueue(id, len, rvring->align, vdev, false, ctx, + vq = vring_new_virtqueue(id, num, rvring->align, vdev, false, ctx, addr, rproc_virtio_notify, callback, name); if (!vq) { dev_err(dev, "vring_new_virtqueue %s failed\n", name); @@ -125,6 +125,8 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev, return ERR_PTR(-ENOMEM); } + vq->num_max = num; + rvring->vq = vq; vq->priv = rvring; @@ -156,6 +158,7 @@ static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned int nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], const char * const names[], + u32 sizes[], const bool * ctx, struct irq_affinity *desc) { diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index a00f901b5c1d..b8de25118ad0 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -383,6 +383,16 @@ config RTC_DRV_MAX77686 This driver can also be built as a module. If so, the module will be called rtc-max77686. +config RTC_DRV_NCT3018Y + tristate "Nuvoton NCT3018Y" + depends on OF + help + If you say yes here you get support for the Nuvoton NCT3018Y I2C RTC + chip. + + This driver can also be built as a module, if so, the module will be + called "rtc-nct3018y". + config RTC_DRV_RK808 tristate "Rockchip RK805/RK808/RK809/RK817/RK818 RTC" depends on MFD_RK808 @@ -1478,16 +1488,6 @@ config RTC_DRV_SUNPLUS This driver can also be built as a module. If so, the module will be called rtc-sunplus. -config RTC_DRV_VR41XX - tristate "NEC VR41XX" - depends on CPU_VR41XX || COMPILE_TEST - help - If you say Y here you will get access to the real time clock - built into your NEC VR41XX CPU. - - To compile this driver as a module, choose M here: the - module will be called rtc-vr41xx. - config RTC_DRV_PL030 tristate "ARM AMBA PL030 RTC" depends on ARM_AMBA @@ -1929,6 +1929,17 @@ config RTC_DRV_ASPEED This driver can also be built as a module, if so, the module will be called "rtc-aspeed". +config RTC_DRV_TI_K3 + tristate "TI K3 RTC" + depends on ARCH_K3 || COMPILE_TEST + select REGMAP_MMIO + help + If you say yes here you get support for the Texas Instruments's + Real Time Clock for K3 architecture. + + This driver can also be built as a module, if so, the module + will be called "rtc-ti-k3". + comment "HID Sensor RTC drivers" config RTC_DRV_HID_SENSOR_TIME @@ -1973,4 +1984,14 @@ config RTC_DRV_MSC313 This driver can also be built as a module, if so, the module will be called "rtc-msc313". +config RTC_DRV_POLARFIRE_SOC + tristate "Microchip PolarFire SoC built-in RTC" + depends on SOC_MICROCHIP_POLARFIRE + help + If you say yes here you will get support for the + built-in RTC on Polarfire SoC. + + This driver can also be built as a module, if so, the module + will be called "rtc-mpfs". + endif # RTC_CLASS diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index fb04467b652d..aab22bc63432 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -112,6 +112,7 @@ obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o obj-$(CONFIG_RTC_DRV_MXC) += rtc-mxc.o obj-$(CONFIG_RTC_DRV_MXC_V2) += rtc-mxc_v2.o obj-$(CONFIG_RTC_DRV_GAMECUBE) += rtc-gamecube.o +obj-$(CONFIG_RTC_DRV_NCT3018Y) += rtc-nct3018y.o obj-$(CONFIG_RTC_DRV_NTXEC) += rtc-ntxec.o obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o obj-$(CONFIG_RTC_DRV_OPAL) += rtc-opal.o @@ -130,6 +131,7 @@ obj-$(CONFIG_RTC_DRV_PIC32) += rtc-pic32.o obj-$(CONFIG_RTC_DRV_PL030) += rtc-pl030.o obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o obj-$(CONFIG_RTC_DRV_PM8XXX) += rtc-pm8xxx.o +obj-$(CONFIG_RTC_DRV_POLARFIRE_SOC) += rtc-mpfs.o obj-$(CONFIG_RTC_DRV_PS3) += rtc-ps3.o obj-$(CONFIG_RTC_DRV_PXA) += rtc-pxa.o obj-$(CONFIG_RTC_DRV_R7301) += rtc-r7301.o @@ -172,11 +174,11 @@ obj-$(CONFIG_RTC_DRV_SUNPLUS) += rtc-sunplus.o obj-$(CONFIG_RTC_DRV_SUNXI) += rtc-sunxi.o obj-$(CONFIG_RTC_DRV_TEGRA) += rtc-tegra.o obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o +obj-$(CONFIG_RTC_DRV_TI_K3) += rtc-ti-k3.o obj-$(CONFIG_RTC_DRV_TPS6586X) += rtc-tps6586x.o obj-$(CONFIG_RTC_DRV_TPS65910) += rtc-tps65910.o obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl.o obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o -obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o obj-$(CONFIG_RTC_DRV_VT8500) += rtc-vt8500.o obj-$(CONFIG_RTC_DRV_WILCO_EC) += rtc-wilco-ec.o obj-$(CONFIG_RTC_DRV_WM831X) += rtc-wm831x.o diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 3c8eec2218df..e48223c00c67 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -36,7 +36,7 @@ static void rtc_device_release(struct device *dev) cancel_work_sync(&rtc->irqwork); - ida_simple_remove(&rtc_ida, rtc->id); + ida_free(&rtc_ida, rtc->id); mutex_destroy(&rtc->ops_lock); kfree(rtc); } @@ -262,7 +262,7 @@ static int rtc_device_get_id(struct device *dev) } if (id < 0) - id = ida_simple_get(&rtc_ida, 0, 0, GFP_KERNEL); + id = ida_alloc(&rtc_ida, GFP_KERNEL); return id; } @@ -368,7 +368,7 @@ struct rtc_device *devm_rtc_allocate_device(struct device *dev) rtc = rtc_allocate_device(); if (!rtc) { - ida_simple_remove(&rtc_ida, id); + ida_free(&rtc_ida, id); return ERR_PTR(-ENOMEM); } diff --git a/drivers/rtc/dev.c b/drivers/rtc/dev.c index 69325aeede1a..4aad9bb99868 100644 --- a/drivers/rtc/dev.c +++ b/drivers/rtc/dev.c @@ -96,7 +96,7 @@ static int clear_uie(struct rtc_device *rtc) } if (rtc->uie_task_active) { spin_unlock_irq(&rtc->irq_lock); - flush_scheduled_work(); + flush_work(&rtc->uie_task); spin_lock_irq(&rtc->irq_lock); } rtc->uie_irq_active = 0; @@ -566,9 +566,3 @@ void __init rtc_dev_init(void) if (err < 0) pr_err("failed to allocate char dev region\n"); } - -void __exit rtc_dev_exit(void) -{ - if (rtc_devt) - unregister_chrdev_region(rtc_devt, RTC_DEV_MAX); -} diff --git a/drivers/rtc/rtc-ab-b5ze-s3.c b/drivers/rtc/rtc-ab-b5ze-s3.c index 6e3e320dc727..f2b0971d2c65 100644 --- a/drivers/rtc/rtc-ab-b5ze-s3.c +++ b/drivers/rtc/rtc-ab-b5ze-s3.c @@ -817,8 +817,7 @@ static const struct regmap_config abb5zes3_rtc_regmap_config = { .val_bits = 8, }; -static int abb5zes3_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int abb5zes3_probe(struct i2c_client *client) { struct abb5zes3_rtc_data *data = NULL; struct device *dev = &client->dev; @@ -945,7 +944,7 @@ static struct i2c_driver abb5zes3_driver = { .pm = &abb5zes3_rtc_pm_ops, .of_match_table = of_match_ptr(abb5zes3_dt_match), }, - .probe = abb5zes3_probe, + .probe_new = abb5zes3_probe, .id_table = abb5zes3_id, }; module_i2c_driver(abb5zes3_driver); diff --git a/drivers/rtc/rtc-ab-eoz9.c b/drivers/rtc/rtc-ab-eoz9.c index e188ab517f1e..2f8deb8c4cd3 100644 --- a/drivers/rtc/rtc-ab-eoz9.c +++ b/drivers/rtc/rtc-ab-eoz9.c @@ -495,8 +495,7 @@ static void abeoz9_hwmon_register(struct device *dev, #endif -static int abeoz9_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int abeoz9_probe(struct i2c_client *client) { struct abeoz9_rtc_data *data = NULL; struct device *dev = &client->dev; @@ -580,7 +579,7 @@ static struct i2c_driver abeoz9_driver = { .name = "rtc-ab-eoz9", .of_match_table = of_match_ptr(abeoz9_dt_match), }, - .probe = abeoz9_probe, + .probe_new = abeoz9_probe, .id_table = abeoz9_id, }; diff --git a/drivers/rtc/rtc-bq32k.c b/drivers/rtc/rtc-bq32k.c index 2235c968842d..e0bbb11d912e 100644 --- a/drivers/rtc/rtc-bq32k.c +++ b/drivers/rtc/rtc-bq32k.c @@ -249,8 +249,7 @@ static void bq32k_sysfs_unregister(struct device *dev) device_remove_file(dev, &dev_attr_trickle_charge_bypass); } -static int bq32k_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int bq32k_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct rtc_device *rtc; @@ -322,7 +321,7 @@ static struct i2c_driver bq32k_driver = { .name = "bq32k", .of_match_table = of_match_ptr(bq32k_of_match), }, - .probe = bq32k_probe, + .probe_new = bq32k_probe, .remove = bq32k_remove, .id_table = bq32k_id, }; diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index 7c006c2b125f..bdb1df843c78 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -1260,9 +1260,6 @@ static void use_acpi_alarm_quirks(void) if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL) return; - if (!(acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0)) - return; - if (!is_hpet_enabled()) return; diff --git a/drivers/rtc/rtc-core.h b/drivers/rtc/rtc-core.h index 0abf98983e13..4b10a1b8f370 100644 --- a/drivers/rtc/rtc-core.h +++ b/drivers/rtc/rtc-core.h @@ -2,7 +2,6 @@ #ifdef CONFIG_RTC_INTF_DEV extern void __init rtc_dev_init(void); -extern void __exit rtc_dev_exit(void); extern void rtc_dev_prepare(struct rtc_device *rtc); #else @@ -11,10 +10,6 @@ static inline void rtc_dev_init(void) { } -static inline void rtc_dev_exit(void) -{ -} - static inline void rtc_dev_prepare(struct rtc_device *rtc) { } diff --git a/drivers/rtc/rtc-cros-ec.c b/drivers/rtc/rtc-cros-ec.c index 70626793ca69..887f5193e253 100644 --- a/drivers/rtc/rtc-cros-ec.c +++ b/drivers/rtc/rtc-cros-ec.c @@ -375,10 +375,8 @@ static int cros_ec_rtc_remove(struct platform_device *pdev) ret = blocking_notifier_chain_unregister( &cros_ec_rtc->cros_ec->event_notifier, &cros_ec_rtc->notifier); - if (ret) { + if (ret) dev_err(dev, "failed to unregister notifier\n"); - return ret; - } return 0; } diff --git a/drivers/rtc/rtc-ds1374.c b/drivers/rtc/rtc-ds1374.c index 8db5a631bca8..b19de5100b1a 100644 --- a/drivers/rtc/rtc-ds1374.c +++ b/drivers/rtc/rtc-ds1374.c @@ -467,8 +467,7 @@ static const struct watchdog_ops ds1374_wdt_ops = { * ***************************************************************************** */ -static int ds1374_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ds1374_probe(struct i2c_client *client) { struct ds1374 *ds1374; int ret; @@ -575,7 +574,7 @@ static struct i2c_driver ds1374_driver = { .of_match_table = of_match_ptr(ds1374_of_match), .pm = &ds1374_pm, }, - .probe = ds1374_probe, + .probe_new = ds1374_probe, .remove = ds1374_remove, .id_table = ds1374_id, }; diff --git a/drivers/rtc/rtc-ds1672.c b/drivers/rtc/rtc-ds1672.c index 4cd8efbef6cf..a3bb2cd9c881 100644 --- a/drivers/rtc/rtc-ds1672.c +++ b/drivers/rtc/rtc-ds1672.c @@ -106,8 +106,7 @@ static const struct rtc_class_ops ds1672_rtc_ops = { .set_time = ds1672_set_time, }; -static int ds1672_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ds1672_probe(struct i2c_client *client) { int err = 0; struct rtc_device *rtc; @@ -150,7 +149,7 @@ static struct i2c_driver ds1672_driver = { .name = "rtc-ds1672", .of_match_table = of_match_ptr(ds1672_of_match), }, - .probe = &ds1672_probe, + .probe_new = ds1672_probe, .id_table = ds1672_id, }; diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c index 168bc27f1f5a..dd31a60c1fc6 100644 --- a/drivers/rtc/rtc-ds3232.c +++ b/drivers/rtc/rtc-ds3232.c @@ -566,8 +566,7 @@ static const struct dev_pm_ops ds3232_pm_ops = { #if IS_ENABLED(CONFIG_I2C) -static int ds3232_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ds3232_i2c_probe(struct i2c_client *client) { struct regmap *regmap; static const struct regmap_config config = { @@ -604,7 +603,7 @@ static struct i2c_driver ds3232_driver = { .of_match_table = of_match_ptr(ds3232_of_match), .pm = &ds3232_pm_ops, }, - .probe = ds3232_i2c_probe, + .probe_new = ds3232_i2c_probe, .id_table = ds3232_id, }; diff --git a/drivers/rtc/rtc-em3027.c b/drivers/rtc/rtc-em3027.c index 9f176bce48ba..53f9f9391a5f 100644 --- a/drivers/rtc/rtc-em3027.c +++ b/drivers/rtc/rtc-em3027.c @@ -111,8 +111,7 @@ static const struct rtc_class_ops em3027_rtc_ops = { .set_time = em3027_set_time, }; -static int em3027_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int em3027_probe(struct i2c_client *client) { struct rtc_device *rtc; @@ -148,7 +147,7 @@ static struct i2c_driver em3027_driver = { .name = "rtc-em3027", .of_match_table = of_match_ptr(em3027_of_match), }, - .probe = &em3027_probe, + .probe_new = em3027_probe, .id_table = em3027_id, }; diff --git a/drivers/rtc/rtc-fm3130.c b/drivers/rtc/rtc-fm3130.c index 677ec2da13d8..f59bb81f23c0 100644 --- a/drivers/rtc/rtc-fm3130.c +++ b/drivers/rtc/rtc-fm3130.c @@ -340,8 +340,7 @@ static const struct rtc_class_ops fm3130_rtc_ops = { static struct i2c_driver fm3130_driver; -static int fm3130_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int fm3130_probe(struct i2c_client *client) { struct fm3130 *fm3130; int err = -ENODEV; @@ -518,7 +517,7 @@ static struct i2c_driver fm3130_driver = { .driver = { .name = "rtc-fm3130", }, - .probe = fm3130_probe, + .probe_new = fm3130_probe, .id_table = fm3130_id, }; diff --git a/drivers/rtc/rtc-hym8563.c b/drivers/rtc/rtc-hym8563.c index 90e602e99d03..cc710d682121 100644 --- a/drivers/rtc/rtc-hym8563.c +++ b/drivers/rtc/rtc-hym8563.c @@ -495,8 +495,7 @@ static int hym8563_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(hym8563_pm_ops, hym8563_suspend, hym8563_resume); -static int hym8563_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int hym8563_probe(struct i2c_client *client) { struct hym8563 *hym8563; int ret; @@ -572,7 +571,7 @@ static struct i2c_driver hym8563_driver = { .pm = &hym8563_pm_ops, .of_match_table = hym8563_dt_idtable, }, - .probe = hym8563_probe, + .probe_new = hym8563_probe, .id_table = hym8563_id, }; diff --git a/drivers/rtc/rtc-isl12022.c b/drivers/rtc/rtc-isl12022.c index 961bd5d1d109..79461ded1a48 100644 --- a/drivers/rtc/rtc-isl12022.c +++ b/drivers/rtc/rtc-isl12022.c @@ -232,8 +232,7 @@ static const struct rtc_class_ops isl12022_rtc_ops = { .set_time = isl12022_rtc_set_time, }; -static int isl12022_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int isl12022_probe(struct i2c_client *client) { struct isl12022 *isl12022; @@ -275,7 +274,7 @@ static struct i2c_driver isl12022_driver = { .of_match_table = of_match_ptr(isl12022_dt_match), #endif }, - .probe = isl12022_probe, + .probe_new = isl12022_probe, .id_table = isl12022_id, }; diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c index 182dfa605515..f448a525333e 100644 --- a/drivers/rtc/rtc-isl1208.c +++ b/drivers/rtc/rtc-isl1208.c @@ -880,10 +880,14 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) if (rc) return rc; - if (client->irq > 0) + if (client->irq > 0) { rc = isl1208_setup_irq(client, client->irq); - if (rc) - return rc; + if (rc) + return rc; + + } else { + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, isl1208->rtc->features); + } if (evdet_irq > 0 && evdet_irq != client->irq) rc = isl1208_setup_irq(client, evdet_irq); diff --git a/drivers/rtc/rtc-max6900.c b/drivers/rtc/rtc-max6900.c index 4beadfa41644..0a33851cc51f 100644 --- a/drivers/rtc/rtc-max6900.c +++ b/drivers/rtc/rtc-max6900.c @@ -197,8 +197,7 @@ static const struct rtc_class_ops max6900_rtc_ops = { .set_time = max6900_rtc_set_time, }; -static int -max6900_probe(struct i2c_client *client, const struct i2c_device_id *id) +static int max6900_probe(struct i2c_client *client) { struct rtc_device *rtc; @@ -225,7 +224,7 @@ static struct i2c_driver max6900_driver = { .driver = { .name = "rtc-max6900", }, - .probe = max6900_probe, + .probe_new = max6900_probe, .id_table = max6900_id, }; diff --git a/drivers/rtc/rtc-mc146818-lib.c b/drivers/rtc/rtc-mc146818-lib.c index 522449b25921..f1c09f1db044 100644 --- a/drivers/rtc/rtc-mc146818-lib.c +++ b/drivers/rtc/rtc-mc146818-lib.c @@ -21,13 +21,13 @@ bool mc146818_avoid_UIP(void (*callback)(unsigned char seconds, void *param), unsigned long flags; unsigned char seconds; - for (i = 0; i < 10; i++) { + for (i = 0; i < 100; i++) { spin_lock_irqsave(&rtc_lock, flags); /* * Check whether there is an update in progress during which the * readout is unspecified. The maximum update time is ~2ms. Poll - * every msec for completion. + * every 100 usec for completion. * * Store the second value before checking UIP so a long lasting * NMI which happens to hit after the UIP check cannot make @@ -37,7 +37,7 @@ bool mc146818_avoid_UIP(void (*callback)(unsigned char seconds, void *param), if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) { spin_unlock_irqrestore(&rtc_lock, flags); - mdelay(1); + udelay(100); continue; } @@ -56,7 +56,7 @@ bool mc146818_avoid_UIP(void (*callback)(unsigned char seconds, void *param), */ if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) { spin_unlock_irqrestore(&rtc_lock, flags); - mdelay(1); + udelay(100); continue; } diff --git a/drivers/rtc/rtc-mpfs.c b/drivers/rtc/rtc-mpfs.c new file mode 100644 index 000000000000..f14d1925e0c9 --- /dev/null +++ b/drivers/rtc/rtc-mpfs.c @@ -0,0 +1,323 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Microchip MPFS RTC driver + * + * Copyright (c) 2021-2022 Microchip Corporation. All rights reserved. + * + * Author: Daire McNamara <daire.mcnamara@microchip.com> + * & Conor Dooley <conor.dooley@microchip.com> + */ +#include "linux/bits.h" +#include "linux/iopoll.h" +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm_wakeirq.h> +#include <linux/slab.h> +#include <linux/rtc.h> + +#define CONTROL_REG 0x00 +#define MODE_REG 0x04 +#define PRESCALER_REG 0x08 +#define ALARM_LOWER_REG 0x0c +#define ALARM_UPPER_REG 0x10 +#define COMPARE_LOWER_REG 0x14 +#define COMPARE_UPPER_REG 0x18 +#define DATETIME_LOWER_REG 0x20 +#define DATETIME_UPPER_REG 0x24 + +#define CONTROL_RUNNING_BIT BIT(0) +#define CONTROL_START_BIT BIT(0) +#define CONTROL_STOP_BIT BIT(1) +#define CONTROL_ALARM_ON_BIT BIT(2) +#define CONTROL_ALARM_OFF_BIT BIT(3) +#define CONTROL_RESET_BIT BIT(4) +#define CONTROL_UPLOAD_BIT BIT(5) +#define CONTROL_DOWNLOAD_BIT BIT(6) +#define CONTROL_MATCH_BIT BIT(7) +#define CONTROL_WAKEUP_CLR_BIT BIT(8) +#define CONTROL_WAKEUP_SET_BIT BIT(9) +#define CONTROL_UPDATED_BIT BIT(10) + +#define MODE_CLOCK_CALENDAR BIT(0) +#define MODE_WAKE_EN BIT(1) +#define MODE_WAKE_RESET BIT(2) +#define MODE_WAKE_CONTINUE BIT(3) + +#define MAX_PRESCALER_COUNT GENMASK(25, 0) +#define DATETIME_UPPER_MASK GENMASK(29, 0) +#define ALARM_UPPER_MASK GENMASK(10, 0) + +#define UPLOAD_TIMEOUT_US 50 + +struct mpfs_rtc_dev { + struct rtc_device *rtc; + void __iomem *base; +}; + +static void mpfs_rtc_start(struct mpfs_rtc_dev *rtcdev) +{ + u32 ctrl; + + ctrl = readl(rtcdev->base + CONTROL_REG); + ctrl &= ~CONTROL_STOP_BIT; + ctrl |= CONTROL_START_BIT; + writel(ctrl, rtcdev->base + CONTROL_REG); +} + +static void mpfs_rtc_clear_irq(struct mpfs_rtc_dev *rtcdev) +{ + u32 val = readl(rtcdev->base + CONTROL_REG); + + val &= ~(CONTROL_ALARM_ON_BIT | CONTROL_STOP_BIT); + val |= CONTROL_ALARM_OFF_BIT; + writel(val, rtcdev->base + CONTROL_REG); + /* + * Ensure that the posted write to the CONTROL_REG register completed before + * returning from this function. Not doing this may result in the interrupt + * only being cleared some time after this function returns. + */ + (void)readl(rtcdev->base + CONTROL_REG); +} + +static int mpfs_rtc_readtime(struct device *dev, struct rtc_time *tm) +{ + struct mpfs_rtc_dev *rtcdev = dev_get_drvdata(dev); + u64 time; + + time = readl(rtcdev->base + DATETIME_LOWER_REG); + time |= ((u64)readl(rtcdev->base + DATETIME_UPPER_REG) & DATETIME_UPPER_MASK) << 32; + rtc_time64_to_tm(time, tm); + + return 0; +} + +static int mpfs_rtc_settime(struct device *dev, struct rtc_time *tm) +{ + struct mpfs_rtc_dev *rtcdev = dev_get_drvdata(dev); + u32 ctrl, prog; + u64 time; + int ret; + + time = rtc_tm_to_time64(tm); + + writel((u32)time, rtcdev->base + DATETIME_LOWER_REG); + writel((u32)(time >> 32) & DATETIME_UPPER_MASK, rtcdev->base + DATETIME_UPPER_REG); + + ctrl = readl(rtcdev->base + CONTROL_REG); + ctrl &= ~CONTROL_STOP_BIT; + ctrl |= CONTROL_UPLOAD_BIT; + writel(ctrl, rtcdev->base + CONTROL_REG); + + ret = read_poll_timeout(readl, prog, prog & CONTROL_UPLOAD_BIT, 0, UPLOAD_TIMEOUT_US, + false, rtcdev->base + CONTROL_REG); + if (ret) { + dev_err(dev, "timed out uploading time to rtc"); + return ret; + } + mpfs_rtc_start(rtcdev); + + return 0; +} + +static int mpfs_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct mpfs_rtc_dev *rtcdev = dev_get_drvdata(dev); + u32 mode = readl(rtcdev->base + MODE_REG); + u64 time; + + alrm->enabled = mode & MODE_WAKE_EN; + + time = (u64)readl(rtcdev->base + ALARM_LOWER_REG) << 32; + time |= (readl(rtcdev->base + ALARM_UPPER_REG) & ALARM_UPPER_MASK); + rtc_time64_to_tm(time, &alrm->time); + + return 0; +} + +static int mpfs_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct mpfs_rtc_dev *rtcdev = dev_get_drvdata(dev); + u32 mode, ctrl; + u64 time; + + /* Disable the alarm before updating */ + ctrl = readl(rtcdev->base + CONTROL_REG); + ctrl |= CONTROL_ALARM_OFF_BIT; + writel(ctrl, rtcdev->base + CONTROL_REG); + + time = rtc_tm_to_time64(&alrm->time); + + writel((u32)time, rtcdev->base + ALARM_LOWER_REG); + writel((u32)(time >> 32) & ALARM_UPPER_MASK, rtcdev->base + ALARM_UPPER_REG); + + /* Bypass compare register in alarm mode */ + writel(GENMASK(31, 0), rtcdev->base + COMPARE_LOWER_REG); + writel(GENMASK(29, 0), rtcdev->base + COMPARE_UPPER_REG); + + /* Configure the RTC to enable the alarm. */ + ctrl = readl(rtcdev->base + CONTROL_REG); + mode = readl(rtcdev->base + MODE_REG); + if (alrm->enabled) { + mode = MODE_WAKE_EN | MODE_WAKE_CONTINUE; + /* Enable the alarm */ + ctrl &= ~CONTROL_ALARM_OFF_BIT; + ctrl |= CONTROL_ALARM_ON_BIT; + } + ctrl &= ~CONTROL_STOP_BIT; + ctrl |= CONTROL_START_BIT; + writel(ctrl, rtcdev->base + CONTROL_REG); + writel(mode, rtcdev->base + MODE_REG); + + return 0; +} + +static int mpfs_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct mpfs_rtc_dev *rtcdev = dev_get_drvdata(dev); + u32 ctrl; + + ctrl = readl(rtcdev->base + CONTROL_REG); + ctrl &= ~(CONTROL_ALARM_ON_BIT | CONTROL_ALARM_OFF_BIT | CONTROL_STOP_BIT); + + if (enabled) + ctrl |= CONTROL_ALARM_ON_BIT; + else + ctrl |= CONTROL_ALARM_OFF_BIT; + + writel(ctrl, rtcdev->base + CONTROL_REG); + + return 0; +} + +static inline struct clk *mpfs_rtc_init_clk(struct device *dev) +{ + struct clk *clk; + int ret; + + clk = devm_clk_get(dev, "rtc"); + if (IS_ERR(clk)) + return clk; + + ret = clk_prepare_enable(clk); + if (ret) + return ERR_PTR(ret); + + devm_add_action_or_reset(dev, (void (*) (void *))clk_disable_unprepare, clk); + return clk; +} + +static irqreturn_t mpfs_rtc_wakeup_irq_handler(int irq, void *dev) +{ + struct mpfs_rtc_dev *rtcdev = dev; + + mpfs_rtc_clear_irq(rtcdev); + + rtc_update_irq(rtcdev->rtc, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops mpfs_rtc_ops = { + .read_time = mpfs_rtc_readtime, + .set_time = mpfs_rtc_settime, + .read_alarm = mpfs_rtc_readalarm, + .set_alarm = mpfs_rtc_setalarm, + .alarm_irq_enable = mpfs_rtc_alarm_irq_enable, +}; + +static int mpfs_rtc_probe(struct platform_device *pdev) +{ + struct mpfs_rtc_dev *rtcdev; + struct clk *clk; + u32 prescaler; + int wakeup_irq, ret; + + rtcdev = devm_kzalloc(&pdev->dev, sizeof(struct mpfs_rtc_dev), GFP_KERNEL); + if (!rtcdev) + return -ENOMEM; + + platform_set_drvdata(pdev, rtcdev); + + rtcdev->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtcdev->rtc)) + return PTR_ERR(rtcdev->rtc); + + rtcdev->rtc->ops = &mpfs_rtc_ops; + + /* range is capped by alarm max, lower reg is 31:0 & upper is 10:0 */ + rtcdev->rtc->range_max = GENMASK_ULL(42, 0); + + clk = mpfs_rtc_init_clk(&pdev->dev); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + rtcdev->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rtcdev->base)) { + dev_dbg(&pdev->dev, "invalid ioremap resources\n"); + return PTR_ERR(rtcdev->base); + } + + wakeup_irq = platform_get_irq(pdev, 0); + if (wakeup_irq <= 0) { + dev_dbg(&pdev->dev, "could not get wakeup irq\n"); + return wakeup_irq; + } + ret = devm_request_irq(&pdev->dev, wakeup_irq, mpfs_rtc_wakeup_irq_handler, 0, + dev_name(&pdev->dev), rtcdev); + if (ret) { + dev_dbg(&pdev->dev, "could not request wakeup irq\n"); + return ret; + } + + /* prescaler hardware adds 1 to reg value */ + prescaler = clk_get_rate(devm_clk_get(&pdev->dev, "rtcref")) - 1; + + if (prescaler > MAX_PRESCALER_COUNT) { + dev_dbg(&pdev->dev, "invalid prescaler %d\n", prescaler); + return -EINVAL; + } + + writel(prescaler, rtcdev->base + PRESCALER_REG); + dev_info(&pdev->dev, "prescaler set to: 0x%X \r\n", prescaler); + + device_init_wakeup(&pdev->dev, true); + ret = dev_pm_set_wake_irq(&pdev->dev, wakeup_irq); + if (ret) + dev_err(&pdev->dev, "failed to enable irq wake\n"); + + return devm_rtc_register_device(rtcdev->rtc); +} + +static int mpfs_rtc_remove(struct platform_device *pdev) +{ + dev_pm_clear_wake_irq(&pdev->dev); + + return 0; +} + +static const struct of_device_id mpfs_rtc_of_match[] = { + { .compatible = "microchip,mpfs-rtc" }, + { } +}; + +MODULE_DEVICE_TABLE(of, mpfs_rtc_of_match); + +static struct platform_driver mpfs_rtc_driver = { + .probe = mpfs_rtc_probe, + .remove = mpfs_rtc_remove, + .driver = { + .name = "mpfs_rtc", + .of_match_table = mpfs_rtc_of_match, + }, +}; + +module_platform_driver(mpfs_rtc_driver); + +MODULE_DESCRIPTION("Real time clock for Microchip Polarfire SoC"); +MODULE_AUTHOR("Daire McNamara <daire.mcnamara@microchip.com>"); +MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-nct3018y.c b/drivers/rtc/rtc-nct3018y.c new file mode 100644 index 000000000000..d43acd3920ed --- /dev/null +++ b/drivers/rtc/rtc-nct3018y.c @@ -0,0 +1,553 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2022 Nuvoton Technology Corporation + +#include <linux/bcd.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/rtc.h> +#include <linux/slab.h> + +#define NCT3018Y_REG_SC 0x00 /* seconds */ +#define NCT3018Y_REG_SCA 0x01 /* alarm */ +#define NCT3018Y_REG_MN 0x02 +#define NCT3018Y_REG_MNA 0x03 /* alarm */ +#define NCT3018Y_REG_HR 0x04 +#define NCT3018Y_REG_HRA 0x05 /* alarm */ +#define NCT3018Y_REG_DW 0x06 +#define NCT3018Y_REG_DM 0x07 +#define NCT3018Y_REG_MO 0x08 +#define NCT3018Y_REG_YR 0x09 +#define NCT3018Y_REG_CTRL 0x0A /* timer control */ +#define NCT3018Y_REG_ST 0x0B /* status */ +#define NCT3018Y_REG_CLKO 0x0C /* clock out */ + +#define NCT3018Y_BIT_AF BIT(7) +#define NCT3018Y_BIT_ST BIT(7) +#define NCT3018Y_BIT_DM BIT(6) +#define NCT3018Y_BIT_HF BIT(5) +#define NCT3018Y_BIT_DSM BIT(4) +#define NCT3018Y_BIT_AIE BIT(3) +#define NCT3018Y_BIT_OFIE BIT(2) +#define NCT3018Y_BIT_CIE BIT(1) +#define NCT3018Y_BIT_TWO BIT(0) + +#define NCT3018Y_REG_BAT_MASK 0x07 +#define NCT3018Y_REG_CLKO_F_MASK 0x03 /* frequenc mask */ +#define NCT3018Y_REG_CLKO_CKE 0x80 /* clock out enabled */ + +struct nct3018y { + struct rtc_device *rtc; + struct i2c_client *client; +#ifdef CONFIG_COMMON_CLK + struct clk_hw clkout_hw; +#endif +}; + +static int nct3018y_set_alarm_mode(struct i2c_client *client, bool on) +{ + int err, flags; + + dev_dbg(&client->dev, "%s:on:%d\n", __func__, on); + + flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_CTRL); + if (flags < 0) { + dev_dbg(&client->dev, + "Failed to read NCT3018Y_REG_CTRL\n"); + return flags; + } + + if (on) + flags |= NCT3018Y_BIT_AIE; + else + flags &= ~NCT3018Y_BIT_AIE; + + flags |= NCT3018Y_BIT_CIE; + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_CTRL, flags); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_CTRL\n"); + return err; + } + + flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_ST); + if (flags < 0) { + dev_dbg(&client->dev, + "Failed to read NCT3018Y_REG_ST\n"); + return flags; + } + + flags &= ~(NCT3018Y_BIT_AF); + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_ST, flags); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_ST\n"); + return err; + } + + return 0; +} + +static int nct3018y_get_alarm_mode(struct i2c_client *client, unsigned char *alarm_enable, + unsigned char *alarm_flag) +{ + int flags; + + if (alarm_enable) { + dev_dbg(&client->dev, "%s:NCT3018Y_REG_CTRL\n", __func__); + flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_CTRL); + if (flags < 0) + return flags; + *alarm_enable = flags & NCT3018Y_BIT_AIE; + } + + if (alarm_flag) { + dev_dbg(&client->dev, "%s:NCT3018Y_REG_ST\n", __func__); + flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_ST); + if (flags < 0) + return flags; + *alarm_flag = flags & NCT3018Y_BIT_AF; + } + + dev_dbg(&client->dev, "%s:alarm_enable:%x alarm_flag:%x\n", + __func__, *alarm_enable, *alarm_flag); + + return 0; +} + +static irqreturn_t nct3018y_irq(int irq, void *dev_id) +{ + struct nct3018y *nct3018y = i2c_get_clientdata(dev_id); + struct i2c_client *client = nct3018y->client; + int err; + unsigned char alarm_flag; + unsigned char alarm_enable; + + dev_dbg(&client->dev, "%s:irq:%d\n", __func__, irq); + err = nct3018y_get_alarm_mode(nct3018y->client, &alarm_enable, &alarm_flag); + if (err) + return IRQ_NONE; + + if (alarm_flag) { + dev_dbg(&client->dev, "%s:alarm flag:%x\n", + __func__, alarm_flag); + rtc_update_irq(nct3018y->rtc, 1, RTC_IRQF | RTC_AF); + nct3018y_set_alarm_mode(nct3018y->client, 0); + dev_dbg(&client->dev, "%s:IRQ_HANDLED\n", __func__); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +/* + * In the routines that deal directly with the nct3018y hardware, we use + * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch. + */ +static int nct3018y_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned char buf[10]; + int err; + + err = i2c_smbus_read_i2c_block_data(client, NCT3018Y_REG_ST, 1, buf); + if (err < 0) + return err; + + if (!buf[0]) { + dev_dbg(&client->dev, " voltage <=1.7, date/time is not reliable.\n"); + return -EINVAL; + } + + err = i2c_smbus_read_i2c_block_data(client, NCT3018Y_REG_SC, sizeof(buf), buf); + if (err < 0) + return err; + + tm->tm_sec = bcd2bin(buf[0] & 0x7F); + tm->tm_min = bcd2bin(buf[2] & 0x7F); + tm->tm_hour = bcd2bin(buf[4] & 0x3F); + tm->tm_wday = buf[6] & 0x07; + tm->tm_mday = bcd2bin(buf[7] & 0x3F); + tm->tm_mon = bcd2bin(buf[8] & 0x1F) - 1; + tm->tm_year = bcd2bin(buf[9]) + 100; + + return 0; +} + +static int nct3018y_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned char buf[4] = {0}; + int err; + + buf[0] = bin2bcd(tm->tm_sec); + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_SC, buf[0]); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_SC\n"); + return err; + } + + buf[0] = bin2bcd(tm->tm_min); + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_MN, buf[0]); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_MN\n"); + return err; + } + + buf[0] = bin2bcd(tm->tm_hour); + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_HR, buf[0]); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_HR\n"); + return err; + } + + buf[0] = tm->tm_wday & 0x07; + buf[1] = bin2bcd(tm->tm_mday); + buf[2] = bin2bcd(tm->tm_mon + 1); + buf[3] = bin2bcd(tm->tm_year - 100); + err = i2c_smbus_write_i2c_block_data(client, NCT3018Y_REG_DW, + sizeof(buf), buf); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write for day and mon and year\n"); + return -EIO; + } + + return err; +} + +static int nct3018y_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned char buf[5]; + int err; + + err = i2c_smbus_read_i2c_block_data(client, NCT3018Y_REG_SCA, + sizeof(buf), buf); + if (err < 0) { + dev_dbg(&client->dev, "Unable to read date\n"); + return -EIO; + } + + dev_dbg(&client->dev, "%s: raw data is sec=%02x, min=%02x hr=%02x\n", + __func__, buf[0], buf[2], buf[4]); + + tm->time.tm_sec = bcd2bin(buf[0] & 0x7F); + tm->time.tm_min = bcd2bin(buf[2] & 0x7F); + tm->time.tm_hour = bcd2bin(buf[4] & 0x3F); + + err = nct3018y_get_alarm_mode(client, &tm->enabled, &tm->pending); + if (err < 0) + return err; + + dev_dbg(&client->dev, "%s:s=%d m=%d, hr=%d, enabled=%d, pending=%d\n", + __func__, tm->time.tm_sec, tm->time.tm_min, + tm->time.tm_hour, tm->enabled, tm->pending); + + return 0; +} + +static int nct3018y_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + int err; + + dev_dbg(dev, "%s, sec=%d, min=%d hour=%d tm->enabled:%d\n", + __func__, tm->time.tm_sec, tm->time.tm_min, tm->time.tm_hour, + tm->enabled); + + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_SCA, bin2bcd(tm->time.tm_sec)); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_SCA\n"); + return err; + } + + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_MNA, bin2bcd(tm->time.tm_min)); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_MNA\n"); + return err; + } + + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_HRA, bin2bcd(tm->time.tm_hour)); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_HRA\n"); + return err; + } + + return nct3018y_set_alarm_mode(client, tm->enabled); +} + +static int nct3018y_irq_enable(struct device *dev, unsigned int enabled) +{ + dev_dbg(dev, "%s: alarm enable=%d\n", __func__, enabled); + + return nct3018y_set_alarm_mode(to_i2c_client(dev), enabled); +} + +static int nct3018y_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + struct i2c_client *client = to_i2c_client(dev); + int status, flags = 0; + + switch (cmd) { + case RTC_VL_READ: + status = i2c_smbus_read_byte_data(client, NCT3018Y_REG_ST); + if (status < 0) + return status; + + if (!(status & NCT3018Y_REG_BAT_MASK)) + flags |= RTC_VL_DATA_INVALID; + + return put_user(flags, (unsigned int __user *)arg); + + default: + return -ENOIOCTLCMD; + } +} + +#ifdef CONFIG_COMMON_CLK +/* + * Handling of the clkout + */ + +#define clkout_hw_to_nct3018y(_hw) container_of(_hw, struct nct3018y, clkout_hw) + +static const int clkout_rates[] = { + 32768, + 1024, + 32, + 1, +}; + +static unsigned long nct3018y_clkout_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct nct3018y *nct3018y = clkout_hw_to_nct3018y(hw); + struct i2c_client *client = nct3018y->client; + int flags; + + flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_CLKO); + if (flags < 0) + return 0; + + flags &= NCT3018Y_REG_CLKO_F_MASK; + return clkout_rates[flags]; +} + +static long nct3018y_clkout_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(clkout_rates); i++) + if (clkout_rates[i] <= rate) + return clkout_rates[i]; + + return 0; +} + +static int nct3018y_clkout_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct nct3018y *nct3018y = clkout_hw_to_nct3018y(hw); + struct i2c_client *client = nct3018y->client; + int i, flags; + + flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_CLKO); + if (flags < 0) + return flags; + + for (i = 0; i < ARRAY_SIZE(clkout_rates); i++) + if (clkout_rates[i] == rate) { + flags &= ~NCT3018Y_REG_CLKO_F_MASK; + flags |= i; + return i2c_smbus_write_byte_data(client, NCT3018Y_REG_CLKO, flags); + } + + return -EINVAL; +} + +static int nct3018y_clkout_control(struct clk_hw *hw, bool enable) +{ + struct nct3018y *nct3018y = clkout_hw_to_nct3018y(hw); + struct i2c_client *client = nct3018y->client; + int flags; + + flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_CLKO); + if (flags < 0) + return flags; + + if (enable) + flags |= NCT3018Y_REG_CLKO_CKE; + else + flags &= ~NCT3018Y_REG_CLKO_CKE; + + return i2c_smbus_write_byte_data(client, NCT3018Y_REG_CLKO, flags); +} + +static int nct3018y_clkout_prepare(struct clk_hw *hw) +{ + return nct3018y_clkout_control(hw, 1); +} + +static void nct3018y_clkout_unprepare(struct clk_hw *hw) +{ + nct3018y_clkout_control(hw, 0); +} + +static int nct3018y_clkout_is_prepared(struct clk_hw *hw) +{ + struct nct3018y *nct3018y = clkout_hw_to_nct3018y(hw); + struct i2c_client *client = nct3018y->client; + int flags; + + flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_CLKO); + if (flags < 0) + return flags; + + return flags & NCT3018Y_REG_CLKO_CKE; +} + +static const struct clk_ops nct3018y_clkout_ops = { + .prepare = nct3018y_clkout_prepare, + .unprepare = nct3018y_clkout_unprepare, + .is_prepared = nct3018y_clkout_is_prepared, + .recalc_rate = nct3018y_clkout_recalc_rate, + .round_rate = nct3018y_clkout_round_rate, + .set_rate = nct3018y_clkout_set_rate, +}; + +static struct clk *nct3018y_clkout_register_clk(struct nct3018y *nct3018y) +{ + struct i2c_client *client = nct3018y->client; + struct device_node *node = client->dev.of_node; + struct clk *clk; + struct clk_init_data init; + + init.name = "nct3018y-clkout"; + init.ops = &nct3018y_clkout_ops; + init.flags = 0; + init.parent_names = NULL; + init.num_parents = 0; + nct3018y->clkout_hw.init = &init; + + /* optional override of the clockname */ + of_property_read_string(node, "clock-output-names", &init.name); + + /* register the clock */ + clk = devm_clk_register(&client->dev, &nct3018y->clkout_hw); + + if (!IS_ERR(clk)) + of_clk_add_provider(node, of_clk_src_simple_get, clk); + + return clk; +} +#endif + +static const struct rtc_class_ops nct3018y_rtc_ops = { + .read_time = nct3018y_rtc_read_time, + .set_time = nct3018y_rtc_set_time, + .read_alarm = nct3018y_rtc_read_alarm, + .set_alarm = nct3018y_rtc_set_alarm, + .alarm_irq_enable = nct3018y_irq_enable, + .ioctl = nct3018y_ioctl, +}; + +static int nct3018y_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct nct3018y *nct3018y; + int err, flags; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | + I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BLOCK_DATA)) + return -ENODEV; + + nct3018y = devm_kzalloc(&client->dev, sizeof(struct nct3018y), + GFP_KERNEL); + if (!nct3018y) + return -ENOMEM; + + i2c_set_clientdata(client, nct3018y); + nct3018y->client = client; + device_set_wakeup_capable(&client->dev, 1); + + flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_CTRL); + if (flags < 0) { + dev_dbg(&client->dev, "%s: read error\n", __func__); + return flags; + } else if (flags & NCT3018Y_BIT_TWO) { + dev_dbg(&client->dev, "%s: NCT3018Y_BIT_TWO is set\n", __func__); + } + + flags = NCT3018Y_BIT_TWO; + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_CTRL, flags); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_CTRL\n"); + return err; + } + + flags = 0; + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_ST, flags); + if (err < 0) { + dev_dbg(&client->dev, "%s: write error\n", __func__); + return err; + } + + nct3018y->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(nct3018y->rtc)) + return PTR_ERR(nct3018y->rtc); + + nct3018y->rtc->ops = &nct3018y_rtc_ops; + nct3018y->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + nct3018y->rtc->range_max = RTC_TIMESTAMP_END_2099; + + if (client->irq > 0) { + err = devm_request_threaded_irq(&client->dev, client->irq, + NULL, nct3018y_irq, + IRQF_ONESHOT | IRQF_TRIGGER_FALLING, + "nct3018y", client); + if (err) { + dev_dbg(&client->dev, "unable to request IRQ %d\n", client->irq); + return err; + } + } else { + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, nct3018y->rtc->features); + clear_bit(RTC_FEATURE_ALARM, nct3018y->rtc->features); + } + +#ifdef CONFIG_COMMON_CLK + /* register clk in common clk framework */ + nct3018y_clkout_register_clk(nct3018y); +#endif + + return devm_rtc_register_device(nct3018y->rtc); +} + +static const struct i2c_device_id nct3018y_id[] = { + { "nct3018y", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, nct3018y_id); + +static const struct of_device_id nct3018y_of_match[] = { + { .compatible = "nuvoton,nct3018y" }, + {} +}; +MODULE_DEVICE_TABLE(of, nct3018y_of_match); + +static struct i2c_driver nct3018y_driver = { + .driver = { + .name = "rtc-nct3018y", + .of_match_table = of_match_ptr(nct3018y_of_match), + }, + .probe = nct3018y_probe, + .id_table = nct3018y_id, +}; + +module_i2c_driver(nct3018y_driver); + +MODULE_AUTHOR("Medad CChien <ctcchien@nuvoton.com>"); +MODULE_AUTHOR("Mia Lin <mimi05633@gmail.com>"); +MODULE_DESCRIPTION("Nuvoton NCT3018Y RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-pcf8523.c b/drivers/rtc/rtc-pcf8523.c index b1b1943de844..6174b3fd4b98 100644 --- a/drivers/rtc/rtc-pcf8523.c +++ b/drivers/rtc/rtc-pcf8523.c @@ -390,8 +390,7 @@ static const struct regmap_config regmap_config = { .max_register = 0x13, }; -static int pcf8523_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int pcf8523_probe(struct i2c_client *client) { struct pcf8523 *pcf8523; struct rtc_device *rtc; @@ -485,7 +484,7 @@ static struct i2c_driver pcf8523_driver = { .name = "rtc-pcf8523", .of_match_table = pcf8523_of_match, }, - .probe = pcf8523_probe, + .probe_new = pcf8523_probe, .id_table = pcf8523_id, }; module_i2c_driver(pcf8523_driver); diff --git a/drivers/rtc/rtc-pcf85363.c b/drivers/rtc/rtc-pcf85363.c index bb3e9ba75f6c..c05b722f0060 100644 --- a/drivers/rtc/rtc-pcf85363.c +++ b/drivers/rtc/rtc-pcf85363.c @@ -350,8 +350,7 @@ static const struct pcf85x63_config pcf_85363_config = { .num_nvram = 2 }; -static int pcf85363_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int pcf85363_probe(struct i2c_client *client) { struct pcf85363 *pcf85363; const struct pcf85x63_config *config = &pcf_85363_config; @@ -436,7 +435,7 @@ static struct i2c_driver pcf85363_driver = { .name = "pcf85363", .of_match_table = of_match_ptr(dev_ids), }, - .probe = pcf85363_probe, + .probe_new = pcf85363_probe, }; module_i2c_driver(pcf85363_driver); diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index 9d06813e2e6d..11fa9788558b 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c @@ -509,8 +509,7 @@ static const struct rtc_class_ops pcf8563_rtc_ops = { .alarm_irq_enable = pcf8563_irq_enable, }; -static int pcf8563_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int pcf8563_probe(struct i2c_client *client) { struct pcf8563 *pcf8563; int err; @@ -606,7 +605,7 @@ static struct i2c_driver pcf8563_driver = { .name = "rtc-pcf8563", .of_match_table = of_match_ptr(pcf8563_of_match), }, - .probe = pcf8563_probe, + .probe_new = pcf8563_probe, .id_table = pcf8563_id, }; diff --git a/drivers/rtc/rtc-pcf8583.c b/drivers/rtc/rtc-pcf8583.c index c80ca20e5d8d..87074d178274 100644 --- a/drivers/rtc/rtc-pcf8583.c +++ b/drivers/rtc/rtc-pcf8583.c @@ -275,8 +275,7 @@ static const struct rtc_class_ops pcf8583_rtc_ops = { .set_time = pcf8583_rtc_set_time, }; -static int pcf8583_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int pcf8583_probe(struct i2c_client *client) { struct pcf8583 *pcf8583; @@ -307,7 +306,7 @@ static struct i2c_driver pcf8583_driver = { .driver = { .name = "pcf8583", }, - .probe = pcf8583_probe, + .probe_new = pcf8583_probe, .id_table = pcf8583_id, }; diff --git a/drivers/rtc/rtc-rv3029c2.c b/drivers/rtc/rtc-rv3029c2.c index 8cb84c9595fc..eb483a30bd92 100644 --- a/drivers/rtc/rtc-rv3029c2.c +++ b/drivers/rtc/rtc-rv3029c2.c @@ -784,8 +784,7 @@ static const struct regmap_config config = { #if IS_ENABLED(CONFIG_I2C) -static int rv3029_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int rv3029_i2c_probe(struct i2c_client *client) { struct regmap *regmap; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK | @@ -819,7 +818,7 @@ static struct i2c_driver rv3029_driver = { .name = "rv3029", .of_match_table = of_match_ptr(rv3029_of_match), }, - .probe = rv3029_i2c_probe, + .probe_new = rv3029_i2c_probe, .id_table = rv3029_id, }; diff --git a/drivers/rtc/rtc-rv8803.c b/drivers/rtc/rtc-rv8803.c index f69e0b1137cd..3527a0521e9b 100644 --- a/drivers/rtc/rtc-rv8803.c +++ b/drivers/rtc/rtc-rv8803.c @@ -9,6 +9,7 @@ #include <linux/bcd.h> #include <linux/bitops.h> +#include <linux/bitfield.h> #include <linux/log2.h> #include <linux/i2c.h> #include <linux/interrupt.h> @@ -33,6 +34,7 @@ #define RV8803_EXT 0x0D #define RV8803_FLAG 0x0E #define RV8803_CTRL 0x0F +#define RV8803_OSC_OFFSET 0x2C #define RV8803_EXT_WADA BIT(6) @@ -49,12 +51,15 @@ #define RV8803_CTRL_TIE BIT(4) #define RV8803_CTRL_UIE BIT(5) +#define RX8803_CTRL_CSEL GENMASK(7, 6) + #define RX8900_BACKUP_CTRL 0x18 #define RX8900_FLAG_SWOFF BIT(2) #define RX8900_FLAG_VDETOFF BIT(3) enum rv8803_type { rv_8803, + rx_8803, rx_8804, rx_8900 }; @@ -64,6 +69,7 @@ struct rv8803_data { struct rtc_device *rtc; struct mutex flags_lock; u8 ctrl; + u8 backup; enum rv8803_type type; }; @@ -136,6 +142,44 @@ static int rv8803_write_regs(const struct i2c_client *client, return ret; } +static int rv8803_regs_init(struct rv8803_data *rv8803) +{ + int ret; + + ret = rv8803_write_reg(rv8803->client, RV8803_OSC_OFFSET, 0x00); + if (ret) + return ret; + + ret = rv8803_write_reg(rv8803->client, RV8803_CTRL, + FIELD_PREP(RX8803_CTRL_CSEL, 1)); /* 2s */ + if (ret) + return ret; + + ret = rv8803_write_regs(rv8803->client, RV8803_ALARM_MIN, 3, + (u8[]){ 0, 0, 0 }); + if (ret) + return ret; + + return rv8803_write_reg(rv8803->client, RV8803_RAM, 0x00); +} + +static int rv8803_regs_configure(struct rv8803_data *rv8803); + +static int rv8803_regs_reset(struct rv8803_data *rv8803) +{ + /* + * The RV-8803 resets all registers to POR defaults after voltage-loss, + * the Epson RTCs don't, so we manually reset the remainder here. + */ + if (rv8803->type == rx_8803 || rv8803->type == rx_8900) { + int ret = rv8803_regs_init(rv8803); + if (ret) + return ret; + } + + return rv8803_regs_configure(rv8803); +} + static irqreturn_t rv8803_handle_irq(int irq, void *dev_id) { struct i2c_client *client = dev_id; @@ -269,6 +313,14 @@ static int rv8803_set_time(struct device *dev, struct rtc_time *tm) return flags; } + if (flags & RV8803_FLAG_V2F) { + ret = rv8803_regs_reset(rv8803); + if (ret) { + mutex_unlock(&rv8803->flags_lock); + return ret; + } + } + ret = rv8803_write_reg(rv8803->client, RV8803_FLAG, flags & ~(RV8803_FLAG_V1F | RV8803_FLAG_V2F)); @@ -498,18 +550,32 @@ static int rx8900_trickle_charger_init(struct rv8803_data *rv8803) if (err < 0) return err; - flags = ~(RX8900_FLAG_VDETOFF | RX8900_FLAG_SWOFF) & (u8)err; - - if (of_property_read_bool(node, "epson,vdet-disable")) - flags |= RX8900_FLAG_VDETOFF; - - if (of_property_read_bool(node, "trickle-diode-disable")) - flags |= RX8900_FLAG_SWOFF; + flags = (u8)err; + flags &= ~(RX8900_FLAG_VDETOFF | RX8900_FLAG_SWOFF); + flags |= rv8803->backup; return i2c_smbus_write_byte_data(rv8803->client, RX8900_BACKUP_CTRL, flags); } +/* configure registers with values different than the Power-On reset defaults */ +static int rv8803_regs_configure(struct rv8803_data *rv8803) +{ + int err; + + err = rv8803_write_reg(rv8803->client, RV8803_EXT, RV8803_EXT_WADA); + if (err) + return err; + + err = rx8900_trickle_charger_init(rv8803); + if (err) { + dev_err(&rv8803->client->dev, "failed to init charger\n"); + return err; + } + + return 0; +} + static int rv8803_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -576,15 +642,15 @@ static int rv8803_probe(struct i2c_client *client, if (!client->irq) clear_bit(RTC_FEATURE_ALARM, rv8803->rtc->features); - err = rv8803_write_reg(rv8803->client, RV8803_EXT, RV8803_EXT_WADA); - if (err) - return err; + if (of_property_read_bool(client->dev.of_node, "epson,vdet-disable")) + rv8803->backup |= RX8900_FLAG_VDETOFF; - err = rx8900_trickle_charger_init(rv8803); - if (err) { - dev_err(&client->dev, "failed to init charger\n"); + if (of_property_read_bool(client->dev.of_node, "trickle-diode-disable")) + rv8803->backup |= RX8900_FLAG_SWOFF; + + err = rv8803_regs_configure(rv8803); + if (err) return err; - } rv8803->rtc->ops = &rv8803_rtc_ops; rv8803->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; @@ -603,7 +669,7 @@ static int rv8803_probe(struct i2c_client *client, static const struct i2c_device_id rv8803_id[] = { { "rv8803", rv_8803 }, { "rv8804", rx_8804 }, - { "rx8803", rv_8803 }, + { "rx8803", rx_8803 }, { "rx8900", rx_8900 }, { } }; @@ -616,7 +682,7 @@ static const __maybe_unused struct of_device_id rv8803_of_match[] = { }, { .compatible = "epson,rx8803", - .data = (void *)rv_8803 + .data = (void *)rx_8803 }, { .compatible = "epson,rx8804", diff --git a/drivers/rtc/rtc-rx6110.c b/drivers/rtc/rtc-rx6110.c index 758fd6e11a15..cc634558b928 100644 --- a/drivers/rtc/rtc-rx6110.c +++ b/drivers/rtc/rtc-rx6110.c @@ -419,8 +419,7 @@ static struct regmap_config regmap_i2c_config = { .read_flag_mask = 0x80, }; -static int rx6110_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int rx6110_i2c_probe(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; struct rx6110_data *rx6110; @@ -464,7 +463,7 @@ static struct i2c_driver rx6110_i2c_driver = { .name = RX6110_DRIVER_NAME, .acpi_match_table = rx6110_i2c_acpi_match, }, - .probe = rx6110_i2c_probe, + .probe_new = rx6110_i2c_probe, .id_table = rx6110_i2c_id, }; diff --git a/drivers/rtc/rtc-rx8025.c b/drivers/rtc/rtc-rx8025.c index b32117ccd74b..dde86f3e2a4b 100644 --- a/drivers/rtc/rtc-rx8025.c +++ b/drivers/rtc/rtc-rx8025.c @@ -55,6 +55,8 @@ #define RX8025_BIT_CTRL2_XST BIT(5) #define RX8025_BIT_CTRL2_VDET BIT(6) +#define RX8035_BIT_HOUR_1224 BIT(7) + /* Clock precision adjustment */ #define RX8025_ADJ_RESOLUTION 3050 /* in ppb */ #define RX8025_ADJ_DATA_MAX 62 @@ -78,6 +80,7 @@ struct rx8025_data { struct rtc_device *rtc; enum rx_model model; u8 ctrl1; + int is_24; }; static s32 rx8025_read_reg(const struct i2c_client *client, u8 number) @@ -226,7 +229,7 @@ static int rx8025_get_time(struct device *dev, struct rtc_time *dt) dt->tm_sec = bcd2bin(date[RX8025_REG_SEC] & 0x7f); dt->tm_min = bcd2bin(date[RX8025_REG_MIN] & 0x7f); - if (rx8025->ctrl1 & RX8025_BIT_CTRL1_1224) + if (rx8025->is_24) dt->tm_hour = bcd2bin(date[RX8025_REG_HOUR] & 0x3f); else dt->tm_hour = bcd2bin(date[RX8025_REG_HOUR] & 0x1f) % 12 @@ -254,7 +257,7 @@ static int rx8025_set_time(struct device *dev, struct rtc_time *dt) */ date[RX8025_REG_SEC] = bin2bcd(dt->tm_sec); date[RX8025_REG_MIN] = bin2bcd(dt->tm_min); - if (rx8025->ctrl1 & RX8025_BIT_CTRL1_1224) + if (rx8025->is_24) date[RX8025_REG_HOUR] = bin2bcd(dt->tm_hour); else date[RX8025_REG_HOUR] = (dt->tm_hour >= 12 ? 0x20 : 0) @@ -279,6 +282,7 @@ static int rx8025_init_client(struct i2c_client *client) struct rx8025_data *rx8025 = i2c_get_clientdata(client); u8 ctrl[2], ctrl2; int need_clear = 0; + int hour_reg; int err; err = rx8025_read_regs(client, RX8025_REG_CTRL1, 2, ctrl); @@ -303,6 +307,16 @@ static int rx8025_init_client(struct i2c_client *client) err = rx8025_write_reg(client, RX8025_REG_CTRL2, ctrl2); } + + if (rx8025->model == model_rx_8035) { + /* In RX-8035, 12/24 flag is in the hour register */ + hour_reg = rx8025_read_reg(client, RX8025_REG_HOUR); + if (hour_reg < 0) + return hour_reg; + rx8025->is_24 = (hour_reg & RX8035_BIT_HOUR_1224); + } else { + rx8025->is_24 = (ctrl[1] & RX8025_BIT_CTRL1_1224); + } out: return err; } @@ -329,7 +343,7 @@ static int rx8025_read_alarm(struct device *dev, struct rtc_wkalrm *t) /* Hardware alarms precision is 1 minute! */ t->time.tm_sec = 0; t->time.tm_min = bcd2bin(ald[0] & 0x7f); - if (rx8025->ctrl1 & RX8025_BIT_CTRL1_1224) + if (rx8025->is_24) t->time.tm_hour = bcd2bin(ald[1] & 0x3f); else t->time.tm_hour = bcd2bin(ald[1] & 0x1f) % 12 @@ -350,7 +364,7 @@ static int rx8025_set_alarm(struct device *dev, struct rtc_wkalrm *t) int err; ald[0] = bin2bcd(t->time.tm_min); - if (rx8025->ctrl1 & RX8025_BIT_CTRL1_1224) + if (rx8025->is_24) ald[1] = bin2bcd(t->time.tm_hour); else ald[1] = (t->time.tm_hour >= 12 ? 0x20 : 0) diff --git a/drivers/rtc/rtc-rx8581.c b/drivers/rtc/rtc-rx8581.c index aed4898a0ff4..14edb7534c97 100644 --- a/drivers/rtc/rtc-rx8581.c +++ b/drivers/rtc/rtc-rx8581.c @@ -248,8 +248,7 @@ static const struct rx85x1_config rx8571_config = { .num_nvram = 2 }; -static int rx8581_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int rx8581_probe(struct i2c_client *client) { struct rx8581 *rx8581; const struct rx85x1_config *config = &rx8581_config; @@ -326,7 +325,7 @@ static struct i2c_driver rx8581_driver = { .name = "rtc-rx8581", .of_match_table = of_match_ptr(rx8581_of_match), }, - .probe = rx8581_probe, + .probe_new = rx8581_probe, .id_table = rx8581_id, }; diff --git a/drivers/rtc/rtc-s35390a.c b/drivers/rtc/rtc-s35390a.c index 26278c770731..81d97b1d3159 100644 --- a/drivers/rtc/rtc-s35390a.c +++ b/drivers/rtc/rtc-s35390a.c @@ -420,8 +420,7 @@ static const struct rtc_class_ops s35390a_rtc_ops = { .ioctl = s35390a_rtc_ioctl, }; -static int s35390a_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int s35390a_probe(struct i2c_client *client) { int err, err_read; unsigned int i; @@ -502,7 +501,7 @@ static struct i2c_driver s35390a_driver = { .name = "rtc-s35390a", .of_match_table = of_match_ptr(s35390a_of_match), }, - .probe = s35390a_probe, + .probe_new = s35390a_probe, .id_table = s35390a_id, }; diff --git a/drivers/rtc/rtc-sd3078.c b/drivers/rtc/rtc-sd3078.c index 24e8528e23ec..e2f90d768ca8 100644 --- a/drivers/rtc/rtc-sd3078.c +++ b/drivers/rtc/rtc-sd3078.c @@ -163,8 +163,7 @@ static const struct regmap_config regmap_config = { .max_register = 0x11, }; -static int sd3078_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int sd3078_probe(struct i2c_client *client) { int ret; struct sd3078 *sd3078; @@ -218,7 +217,7 @@ static struct i2c_driver sd3078_driver = { .name = "sd3078", .of_match_table = of_match_ptr(rtc_dt_match), }, - .probe = sd3078_probe, + .probe_new = sd3078_probe, .id_table = sd3078_id, }; diff --git a/drivers/rtc/rtc-spear.c b/drivers/rtc/rtc-spear.c index d4777b01ab22..736fe535cd45 100644 --- a/drivers/rtc/rtc-spear.c +++ b/drivers/rtc/rtc-spear.c @@ -388,7 +388,7 @@ static int spear_rtc_probe(struct platform_device *pdev) config->rtc->ops = &spear_rtc_ops; config->rtc->range_min = RTC_TIMESTAMP_BEGIN_0000; - config->rtc->range_min = RTC_TIMESTAMP_END_9999; + config->rtc->range_max = RTC_TIMESTAMP_END_9999; status = devm_rtc_register_device(config->rtc); if (status) diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c index 57540727ce1c..ed5516089e9a 100644 --- a/drivers/rtc/rtc-sun6i.c +++ b/drivers/rtc/rtc-sun6i.c @@ -875,6 +875,8 @@ static const struct of_device_id sun6i_rtc_dt_ids[] = { { .compatible = "allwinner,sun50i-h6-rtc" }, { .compatible = "allwinner,sun50i-h616-rtc", .data = (void *)RTC_LINEAR_DAY }, + { .compatible = "allwinner,sun50i-r329-rtc", + .data = (void *)RTC_LINEAR_DAY }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids); diff --git a/drivers/rtc/rtc-ti-k3.c b/drivers/rtc/rtc-ti-k3.c new file mode 100644 index 000000000000..7a0f181d3fef --- /dev/null +++ b/drivers/rtc/rtc-ti-k3.c @@ -0,0 +1,680 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Texas Instruments K3 RTC driver + * + * Copyright (C) 2021-2022 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/rtc.h> + +/* Registers */ +#define REG_K3RTC_S_CNT_LSW 0x08 +#define REG_K3RTC_S_CNT_MSW 0x0c +#define REG_K3RTC_COMP 0x10 +#define REG_K3RTC_ON_OFF_S_CNT_LSW 0x20 +#define REG_K3RTC_ON_OFF_S_CNT_MSW 0x24 +#define REG_K3RTC_SCRATCH0 0x30 +#define REG_K3RTC_SCRATCH7 0x4c +#define REG_K3RTC_GENERAL_CTL 0x50 +#define REG_K3RTC_IRQSTATUS_RAW_SYS 0x54 +#define REG_K3RTC_IRQSTATUS_SYS 0x58 +#define REG_K3RTC_IRQENABLE_SET_SYS 0x5c +#define REG_K3RTC_IRQENABLE_CLR_SYS 0x60 +#define REG_K3RTC_SYNCPEND 0x68 +#define REG_K3RTC_KICK0 0x70 +#define REG_K3RTC_KICK1 0x74 + +/* Freeze when lsw is read and unfreeze when msw is read */ +#define K3RTC_CNT_FMODE_S_CNT_VALUE (0x2 << 24) + +/* Magic values for lock/unlock */ +#define K3RTC_KICK0_UNLOCK_VALUE 0x83e70b13 +#define K3RTC_KICK1_UNLOCK_VALUE 0x95a4f1e0 + +/* Multiplier for ppb conversions */ +#define K3RTC_PPB_MULT (1000000000LL) +/* Min and max values supported with 'offset' interface (swapped sign) */ +#define K3RTC_MIN_OFFSET (-277761) +#define K3RTC_MAX_OFFSET (277778) + +/** + * struct ti_k3_rtc_soc_data - Private of compatible data for ti-k3-rtc + * @unlock_irq_erratum: Has erratum for unlock infinite IRQs (erratum i2327) + */ +struct ti_k3_rtc_soc_data { + const bool unlock_irq_erratum; +}; + +static const struct regmap_config ti_k3_rtc_regmap_config = { + .name = "peripheral-registers", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = REG_K3RTC_KICK1, +}; + +enum ti_k3_rtc_fields { + K3RTC_KICK0, + K3RTC_KICK1, + K3RTC_S_CNT_LSW, + K3RTC_S_CNT_MSW, + K3RTC_O32K_OSC_DEP_EN, + K3RTC_UNLOCK, + K3RTC_CNT_FMODE, + K3RTC_PEND, + K3RTC_RELOAD_FROM_BBD, + K3RTC_COMP, + + K3RTC_ALM_S_CNT_LSW, + K3RTC_ALM_S_CNT_MSW, + K3RTC_IRQ_STATUS_RAW, + K3RTC_IRQ_STATUS, + K3RTC_IRQ_ENABLE_SET, + K3RTC_IRQ_ENABLE_CLR, + + K3RTC_IRQ_STATUS_ALT, + K3RTC_IRQ_ENABLE_CLR_ALT, + + K3_RTC_MAX_FIELDS +}; + +static const struct reg_field ti_rtc_reg_fields[] = { + [K3RTC_KICK0] = REG_FIELD(REG_K3RTC_KICK0, 0, 31), + [K3RTC_KICK1] = REG_FIELD(REG_K3RTC_KICK1, 0, 31), + [K3RTC_S_CNT_LSW] = REG_FIELD(REG_K3RTC_S_CNT_LSW, 0, 31), + [K3RTC_S_CNT_MSW] = REG_FIELD(REG_K3RTC_S_CNT_MSW, 0, 15), + [K3RTC_O32K_OSC_DEP_EN] = REG_FIELD(REG_K3RTC_GENERAL_CTL, 21, 21), + [K3RTC_UNLOCK] = REG_FIELD(REG_K3RTC_GENERAL_CTL, 23, 23), + [K3RTC_CNT_FMODE] = REG_FIELD(REG_K3RTC_GENERAL_CTL, 24, 25), + [K3RTC_PEND] = REG_FIELD(REG_K3RTC_SYNCPEND, 0, 1), + [K3RTC_RELOAD_FROM_BBD] = REG_FIELD(REG_K3RTC_SYNCPEND, 31, 31), + [K3RTC_COMP] = REG_FIELD(REG_K3RTC_COMP, 0, 31), + + /* We use on to off as alarm trigger */ + [K3RTC_ALM_S_CNT_LSW] = REG_FIELD(REG_K3RTC_ON_OFF_S_CNT_LSW, 0, 31), + [K3RTC_ALM_S_CNT_MSW] = REG_FIELD(REG_K3RTC_ON_OFF_S_CNT_MSW, 0, 15), + [K3RTC_IRQ_STATUS_RAW] = REG_FIELD(REG_K3RTC_IRQSTATUS_RAW_SYS, 0, 0), + [K3RTC_IRQ_STATUS] = REG_FIELD(REG_K3RTC_IRQSTATUS_SYS, 0, 0), + [K3RTC_IRQ_ENABLE_SET] = REG_FIELD(REG_K3RTC_IRQENABLE_SET_SYS, 0, 0), + [K3RTC_IRQ_ENABLE_CLR] = REG_FIELD(REG_K3RTC_IRQENABLE_CLR_SYS, 0, 0), + /* Off to on is alternate */ + [K3RTC_IRQ_STATUS_ALT] = REG_FIELD(REG_K3RTC_IRQSTATUS_SYS, 1, 1), + [K3RTC_IRQ_ENABLE_CLR_ALT] = REG_FIELD(REG_K3RTC_IRQENABLE_CLR_SYS, 1, 1), +}; + +/** + * struct ti_k3_rtc - Private data for ti-k3-rtc + * @irq: IRQ + * @sync_timeout_us: data sync timeout period in uSec + * @rate_32k: 32k clock rate in Hz + * @rtc_dev: rtc device + * @regmap: rtc mmio regmap + * @r_fields: rtc register fields + * @soc: SoC compatible match data + */ +struct ti_k3_rtc { + unsigned int irq; + u32 sync_timeout_us; + unsigned long rate_32k; + struct rtc_device *rtc_dev; + struct regmap *regmap; + struct regmap_field *r_fields[K3_RTC_MAX_FIELDS]; + const struct ti_k3_rtc_soc_data *soc; +}; + +static int k3rtc_field_read(struct ti_k3_rtc *priv, enum ti_k3_rtc_fields f) +{ + int ret; + int val; + + ret = regmap_field_read(priv->r_fields[f], &val); + /* + * We shouldn't be seeing regmap fail on us for mmio reads + * This is possible if clock context fails, but that isn't the case for us + */ + if (WARN_ON_ONCE(ret)) + return ret; + return val; +} + +static void k3rtc_field_write(struct ti_k3_rtc *priv, enum ti_k3_rtc_fields f, u32 val) +{ + regmap_field_write(priv->r_fields[f], val); +} + +/** + * k3rtc_fence - Ensure a register sync took place between the two domains + * @priv: pointer to priv data + * + * Return: 0 if the sync took place, else returns -ETIMEDOUT + */ +static int k3rtc_fence(struct ti_k3_rtc *priv) +{ + int ret; + + ret = regmap_field_read_poll_timeout(priv->r_fields[K3RTC_PEND], ret, + !ret, 2, priv->sync_timeout_us); + + return ret; +} + +static inline int k3rtc_check_unlocked(struct ti_k3_rtc *priv) +{ + int ret; + + ret = k3rtc_field_read(priv, K3RTC_UNLOCK); + if (ret < 0) + return ret; + + return (ret) ? 0 : 1; +} + +static int k3rtc_unlock_rtc(struct ti_k3_rtc *priv) +{ + int ret; + + ret = k3rtc_check_unlocked(priv); + if (!ret) + return ret; + + k3rtc_field_write(priv, K3RTC_KICK0, K3RTC_KICK0_UNLOCK_VALUE); + k3rtc_field_write(priv, K3RTC_KICK1, K3RTC_KICK1_UNLOCK_VALUE); + + /* Skip fence since we are going to check the unlock bit as fence */ + ret = regmap_field_read_poll_timeout(priv->r_fields[K3RTC_UNLOCK], ret, + !ret, 2, priv->sync_timeout_us); + + return ret; +} + +static int k3rtc_configure(struct device *dev) +{ + int ret; + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + + /* + * HWBUG: The compare state machine is broken if the RTC module + * is NOT unlocked in under one second of boot - which is pretty long + * time from the perspective of Linux driver (module load, u-boot + * shell all can take much longer than this. + * + * In such occurrence, it is assumed that the RTC module is unusable + */ + if (priv->soc->unlock_irq_erratum) { + ret = k3rtc_check_unlocked(priv); + /* If there is an error OR if we are locked, return error */ + if (ret) { + dev_err(dev, + HW_ERR "Erratum i2327 unlock QUIRK! Cannot operate!!\n"); + return -EFAULT; + } + } else { + /* May need to explicitly unlock first time */ + ret = k3rtc_unlock_rtc(priv); + if (ret) { + dev_err(dev, "Failed to unlock(%d)!\n", ret); + return ret; + } + } + + /* Enable Shadow register sync on 32k clock boundary */ + k3rtc_field_write(priv, K3RTC_O32K_OSC_DEP_EN, 0x1); + + /* + * Wait at least clock sync time before proceeding further programming. + * This ensures that the 32k based sync is active. + */ + usleep_range(priv->sync_timeout_us, priv->sync_timeout_us + 5); + + /* We need to ensure fence here to make sure sync here */ + ret = k3rtc_fence(priv); + if (ret) { + dev_err(dev, + "Failed fence osc_dep enable(%d) - is 32k clk working?!\n", ret); + return ret; + } + + /* + * FMODE setting: Reading lower seconds will freeze value on higher + * seconds. This also implies that we must *ALWAYS* read lower seconds + * prior to reading higher seconds + */ + k3rtc_field_write(priv, K3RTC_CNT_FMODE, K3RTC_CNT_FMODE_S_CNT_VALUE); + + /* Clear any spurious IRQ sources if any */ + k3rtc_field_write(priv, K3RTC_IRQ_STATUS_ALT, 0x1); + k3rtc_field_write(priv, K3RTC_IRQ_STATUS, 0x1); + /* Disable all IRQs */ + k3rtc_field_write(priv, K3RTC_IRQ_ENABLE_CLR_ALT, 0x1); + k3rtc_field_write(priv, K3RTC_IRQ_ENABLE_CLR, 0x1); + + /* And.. Let us Sync the writes in */ + return k3rtc_fence(priv); +} + +static int ti_k3_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + u32 seconds_lo, seconds_hi; + + seconds_lo = k3rtc_field_read(priv, K3RTC_S_CNT_LSW); + seconds_hi = k3rtc_field_read(priv, K3RTC_S_CNT_MSW); + + rtc_time64_to_tm((((time64_t)seconds_hi) << 32) | (time64_t)seconds_lo, tm); + + return 0; +} + +static int ti_k3_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + time64_t seconds; + + seconds = rtc_tm_to_time64(tm); + + /* + * Read operation on LSW will freeze the RTC, so to update + * the time, we cannot use field operations. Just write since the + * reserved bits are ignored. + */ + regmap_write(priv->regmap, REG_K3RTC_S_CNT_LSW, seconds); + regmap_write(priv->regmap, REG_K3RTC_S_CNT_MSW, seconds >> 32); + + return k3rtc_fence(priv); +} + +static int ti_k3_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + u32 reg; + u32 offset = enabled ? K3RTC_IRQ_ENABLE_SET : K3RTC_IRQ_ENABLE_CLR; + + reg = k3rtc_field_read(priv, K3RTC_IRQ_ENABLE_SET); + if ((enabled && reg) || (!enabled && !reg)) + return 0; + + k3rtc_field_write(priv, offset, 0x1); + + /* + * Ensure the write sync is through - NOTE: it should be OK to have + * ISR to fire as we are checking sync (which should be done in a 32k + * cycle or so). + */ + return k3rtc_fence(priv); +} + +static int ti_k3_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + u32 seconds_lo, seconds_hi; + + seconds_lo = k3rtc_field_read(priv, K3RTC_ALM_S_CNT_LSW); + seconds_hi = k3rtc_field_read(priv, K3RTC_ALM_S_CNT_MSW); + + rtc_time64_to_tm((((time64_t)seconds_hi) << 32) | (time64_t)seconds_lo, &alarm->time); + + alarm->enabled = k3rtc_field_read(priv, K3RTC_IRQ_ENABLE_SET); + + return 0; +} + +static int ti_k3_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + time64_t seconds; + int ret; + + seconds = rtc_tm_to_time64(&alarm->time); + + k3rtc_field_write(priv, K3RTC_ALM_S_CNT_LSW, seconds); + k3rtc_field_write(priv, K3RTC_ALM_S_CNT_MSW, (seconds >> 32)); + + /* Make sure the alarm time is synced in */ + ret = k3rtc_fence(priv); + if (ret) { + dev_err(dev, "Failed to fence(%d)! Potential config issue?\n", ret); + return ret; + } + + /* Alarm IRQ enable will do a sync */ + return ti_k3_rtc_alarm_irq_enable(dev, alarm->enabled); +} + +static int ti_k3_rtc_read_offset(struct device *dev, long *offset) +{ + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + u32 ticks_per_hr = priv->rate_32k * 3600; + int comp; + s64 tmp; + + comp = k3rtc_field_read(priv, K3RTC_COMP); + + /* Convert from RTC calibration register format to ppb format */ + tmp = comp * (s64)K3RTC_PPB_MULT; + if (tmp < 0) + tmp -= ticks_per_hr / 2LL; + else + tmp += ticks_per_hr / 2LL; + tmp = div_s64(tmp, ticks_per_hr); + + /* Offset value operates in negative way, so swap sign */ + *offset = (long)-tmp; + + return 0; +} + +static int ti_k3_rtc_set_offset(struct device *dev, long offset) +{ + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + u32 ticks_per_hr = priv->rate_32k * 3600; + int comp; + s64 tmp; + + /* Make sure offset value is within supported range */ + if (offset < K3RTC_MIN_OFFSET || offset > K3RTC_MAX_OFFSET) + return -ERANGE; + + /* Convert from ppb format to RTC calibration register format */ + tmp = offset * (s64)ticks_per_hr; + if (tmp < 0) + tmp -= K3RTC_PPB_MULT / 2LL; + else + tmp += K3RTC_PPB_MULT / 2LL; + tmp = div_s64(tmp, K3RTC_PPB_MULT); + + /* Offset value operates in negative way, so swap sign */ + comp = (int)-tmp; + + k3rtc_field_write(priv, K3RTC_COMP, comp); + + return k3rtc_fence(priv); +} + +static irqreturn_t ti_k3_rtc_interrupt(s32 irq, void *dev_id) +{ + struct device *dev = dev_id; + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + u32 reg; + int ret; + + /* + * IRQ assertion can be very fast, however, the IRQ Status clear + * de-assert depends on 32k clock edge in the 32k domain + * If we clear the status prior to the first 32k clock edge, + * the status bit is cleared, but the IRQ stays re-asserted. + * + * To prevent this condition, we need to wait for clock sync time. + * We can either do that by polling the 32k observability signal for + * a toggle OR we could just sleep and let the processor do other + * stuff. + */ + usleep_range(priv->sync_timeout_us, priv->sync_timeout_us + 2); + + /* Lets make sure that this is a valid interrupt */ + reg = k3rtc_field_read(priv, K3RTC_IRQ_STATUS); + + if (!reg) { + u32 raw = k3rtc_field_read(priv, K3RTC_IRQ_STATUS_RAW); + + dev_err(dev, + HW_ERR + "Erratum i2327/IRQ trig: status: 0x%08x / 0x%08x\n", reg, raw); + return IRQ_NONE; + } + + /* + * Write 1 to clear status reg + * We cannot use a field operation here due to a potential race between + * 32k domain and vbus domain. + */ + regmap_write(priv->regmap, REG_K3RTC_IRQSTATUS_SYS, 0x1); + + /* Sync the write in */ + ret = k3rtc_fence(priv); + if (ret) { + dev_err(dev, "Failed to fence irq status clr(%d)!\n", ret); + return IRQ_NONE; + } + + /* + * Force the 32k status to be reloaded back in to ensure status is + * reflected back correctly. + */ + k3rtc_field_write(priv, K3RTC_RELOAD_FROM_BBD, 0x1); + + /* Ensure the write sync is through */ + ret = k3rtc_fence(priv); + if (ret) { + dev_err(dev, "Failed to fence reload from bbd(%d)!\n", ret); + return IRQ_NONE; + } + + /* Now we ensure that the status bit is cleared */ + ret = regmap_field_read_poll_timeout(priv->r_fields[K3RTC_IRQ_STATUS], + ret, !ret, 2, priv->sync_timeout_us); + if (ret) { + dev_err(dev, "Time out waiting for status clear\n"); + return IRQ_NONE; + } + + /* Notify RTC core on event */ + rtc_update_irq(priv->rtc_dev, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops ti_k3_rtc_ops = { + .read_time = ti_k3_rtc_read_time, + .set_time = ti_k3_rtc_set_time, + .read_alarm = ti_k3_rtc_read_alarm, + .set_alarm = ti_k3_rtc_set_alarm, + .read_offset = ti_k3_rtc_read_offset, + .set_offset = ti_k3_rtc_set_offset, + .alarm_irq_enable = ti_k3_rtc_alarm_irq_enable, +}; + +static int ti_k3_rtc_scratch_read(void *priv_data, unsigned int offset, + void *val, size_t bytes) +{ + struct ti_k3_rtc *priv = (struct ti_k3_rtc *)priv_data; + + return regmap_bulk_read(priv->regmap, REG_K3RTC_SCRATCH0 + offset, val, bytes / 4); +} + +static int ti_k3_rtc_scratch_write(void *priv_data, unsigned int offset, + void *val, size_t bytes) +{ + struct ti_k3_rtc *priv = (struct ti_k3_rtc *)priv_data; + int ret; + + ret = regmap_bulk_write(priv->regmap, REG_K3RTC_SCRATCH0 + offset, val, bytes / 4); + if (ret) + return ret; + + return k3rtc_fence(priv); +} + +static struct nvmem_config ti_k3_rtc_nvmem_config = { + .name = "ti_k3_rtc_scratch", + .word_size = 4, + .stride = 4, + .size = REG_K3RTC_SCRATCH7 - REG_K3RTC_SCRATCH0 + 4, + .reg_read = ti_k3_rtc_scratch_read, + .reg_write = ti_k3_rtc_scratch_write, +}; + +static int k3rtc_get_32kclk(struct device *dev, struct ti_k3_rtc *priv) +{ + int ret; + struct clk *clk; + + clk = devm_clk_get(dev, "osc32k"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + ret = clk_prepare_enable(clk); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, (void (*)(void *))clk_disable_unprepare, clk); + if (ret) + return ret; + + priv->rate_32k = clk_get_rate(clk); + + /* Make sure we are exact 32k clock. Else, try to compensate delay */ + if (priv->rate_32k != 32768) + dev_warn(dev, "Clock rate %ld is not 32768! Could misbehave!\n", + priv->rate_32k); + + /* + * Sync timeout should be two 32k clk sync cycles = ~61uS. We double + * it to comprehend intermediate bus segment and cpu frequency + * deltas + */ + priv->sync_timeout_us = (u32)(DIV_ROUND_UP_ULL(1000000, priv->rate_32k) * 4); + + return ret; +} + +static int k3rtc_get_vbusclk(struct device *dev, struct ti_k3_rtc *priv) +{ + int ret; + struct clk *clk; + + /* Note: VBUS isn't a context clock, it is needed for hardware operation */ + clk = devm_clk_get(dev, "vbus"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + ret = clk_prepare_enable(clk); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, (void (*)(void *))clk_disable_unprepare, clk); +} + +static int ti_k3_rtc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ti_k3_rtc *priv; + void __iomem *rtc_base; + int ret; + + priv = devm_kzalloc(dev, sizeof(struct ti_k3_rtc), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + rtc_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rtc_base)) + return PTR_ERR(rtc_base); + + priv->regmap = devm_regmap_init_mmio(dev, rtc_base, &ti_k3_rtc_regmap_config); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + ret = devm_regmap_field_bulk_alloc(dev, priv->regmap, priv->r_fields, + ti_rtc_reg_fields, K3_RTC_MAX_FIELDS); + if (ret) + return ret; + + ret = k3rtc_get_32kclk(dev, priv); + if (ret) + return ret; + ret = k3rtc_get_vbusclk(dev, priv); + if (ret) + return ret; + + ret = platform_get_irq(pdev, 0); + if (ret < 0) + return ret; + priv->irq = (unsigned int)ret; + + priv->rtc_dev = devm_rtc_allocate_device(dev); + if (IS_ERR(priv->rtc_dev)) + return PTR_ERR(priv->rtc_dev); + + priv->soc = of_device_get_match_data(dev); + + priv->rtc_dev->ops = &ti_k3_rtc_ops; + priv->rtc_dev->range_max = (1ULL << 48) - 1; /* 48Bit seconds */ + ti_k3_rtc_nvmem_config.priv = priv; + + ret = devm_request_threaded_irq(dev, priv->irq, NULL, + ti_k3_rtc_interrupt, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + dev_name(dev), dev); + if (ret) { + dev_err(dev, "Could not request IRQ: %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, priv); + + ret = k3rtc_configure(dev); + if (ret) + return ret; + + if (device_property_present(dev, "wakeup-source")) + device_init_wakeup(dev, true); + else + device_set_wakeup_capable(dev, true); + + ret = devm_rtc_register_device(priv->rtc_dev); + if (ret) + return ret; + + return devm_rtc_nvmem_register(priv->rtc_dev, &ti_k3_rtc_nvmem_config); +} + +static const struct ti_k3_rtc_soc_data ti_k3_am62_data = { + .unlock_irq_erratum = true, +}; + +static const struct of_device_id ti_k3_rtc_of_match_table[] = { + {.compatible = "ti,am62-rtc", .data = &ti_k3_am62_data}, + {} +}; +MODULE_DEVICE_TABLE(of, ti_k3_rtc_of_match_table); + +static int __maybe_unused ti_k3_rtc_suspend(struct device *dev) +{ + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(priv->irq); + return 0; +} + +static int __maybe_unused ti_k3_rtc_resume(struct device *dev) +{ + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(priv->irq); + return 0; +} + +static SIMPLE_DEV_PM_OPS(ti_k3_rtc_pm_ops, ti_k3_rtc_suspend, ti_k3_rtc_resume); + +static struct platform_driver ti_k3_rtc_driver = { + .probe = ti_k3_rtc_probe, + .driver = { + .name = "rtc-ti-k3", + .of_match_table = ti_k3_rtc_of_match_table, + .pm = &ti_k3_rtc_pm_ops, + }, +}; +module_platform_driver(ti_k3_rtc_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("TI K3 RTC driver"); +MODULE_AUTHOR("Nishanth Menon"); diff --git a/drivers/rtc/rtc-vr41xx.c b/drivers/rtc/rtc-vr41xx.c deleted file mode 100644 index 5a9f9ad86d32..000000000000 --- a/drivers/rtc/rtc-vr41xx.c +++ /dev/null @@ -1,363 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Driver for NEC VR4100 series Real Time Clock unit. - * - * Copyright (C) 2003-2008 Yoichi Yuasa <yuasa@linux-mips.org> - */ -#include <linux/compat.h> -#include <linux/err.h> -#include <linux/fs.h> -#include <linux/init.h> -#include <linux/io.h> -#include <linux/ioport.h> -#include <linux/interrupt.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/rtc.h> -#include <linux/spinlock.h> -#include <linux/types.h> -#include <linux/uaccess.h> -#include <linux/log2.h> - -#include <asm/div64.h> - -MODULE_AUTHOR("Yoichi Yuasa <yuasa@linux-mips.org>"); -MODULE_DESCRIPTION("NEC VR4100 series RTC driver"); -MODULE_LICENSE("GPL v2"); - -/* RTC 1 registers */ -#define ETIMELREG 0x00 -#define ETIMEMREG 0x02 -#define ETIMEHREG 0x04 -/* RFU */ -#define ECMPLREG 0x08 -#define ECMPMREG 0x0a -#define ECMPHREG 0x0c -/* RFU */ -#define RTCL1LREG 0x10 -#define RTCL1HREG 0x12 -#define RTCL1CNTLREG 0x14 -#define RTCL1CNTHREG 0x16 -#define RTCL2LREG 0x18 -#define RTCL2HREG 0x1a -#define RTCL2CNTLREG 0x1c -#define RTCL2CNTHREG 0x1e - -/* RTC 2 registers */ -#define TCLKLREG 0x00 -#define TCLKHREG 0x02 -#define TCLKCNTLREG 0x04 -#define TCLKCNTHREG 0x06 -/* RFU */ -#define RTCINTREG 0x1e - #define TCLOCK_INT 0x08 - #define RTCLONG2_INT 0x04 - #define RTCLONG1_INT 0x02 - #define ELAPSEDTIME_INT 0x01 - -#define RTC_FREQUENCY 32768 -#define MAX_PERIODIC_RATE 6553 - -static void __iomem *rtc1_base; -static void __iomem *rtc2_base; - -#define rtc1_read(offset) readw(rtc1_base + (offset)) -#define rtc1_write(offset, value) writew((value), rtc1_base + (offset)) - -#define rtc2_read(offset) readw(rtc2_base + (offset)) -#define rtc2_write(offset, value) writew((value), rtc2_base + (offset)) - -/* 32-bit compat for ioctls that nobody else uses */ -#define RTC_EPOCH_READ32 _IOR('p', 0x0d, __u32) - -static unsigned long epoch = 1970; /* Jan 1 1970 00:00:00 */ - -static DEFINE_SPINLOCK(rtc_lock); -static char rtc_name[] = "RTC"; -static unsigned long periodic_count; -static unsigned int alarm_enabled; -static int aie_irq; -static int pie_irq; - -static inline time64_t read_elapsed_second(void) -{ - - unsigned long first_low, first_mid, first_high; - - unsigned long second_low, second_mid, second_high; - - do { - first_low = rtc1_read(ETIMELREG); - first_mid = rtc1_read(ETIMEMREG); - first_high = rtc1_read(ETIMEHREG); - second_low = rtc1_read(ETIMELREG); - second_mid = rtc1_read(ETIMEMREG); - second_high = rtc1_read(ETIMEHREG); - } while (first_low != second_low || first_mid != second_mid || - first_high != second_high); - - return ((u64)first_high << 17) | (first_mid << 1) | (first_low >> 15); -} - -static inline void write_elapsed_second(time64_t sec) -{ - spin_lock_irq(&rtc_lock); - - rtc1_write(ETIMELREG, (uint16_t)(sec << 15)); - rtc1_write(ETIMEMREG, (uint16_t)(sec >> 1)); - rtc1_write(ETIMEHREG, (uint16_t)(sec >> 17)); - - spin_unlock_irq(&rtc_lock); -} - -static int vr41xx_rtc_read_time(struct device *dev, struct rtc_time *time) -{ - time64_t epoch_sec, elapsed_sec; - - epoch_sec = mktime64(epoch, 1, 1, 0, 0, 0); - elapsed_sec = read_elapsed_second(); - - rtc_time64_to_tm(epoch_sec + elapsed_sec, time); - - return 0; -} - -static int vr41xx_rtc_set_time(struct device *dev, struct rtc_time *time) -{ - time64_t epoch_sec, current_sec; - - epoch_sec = mktime64(epoch, 1, 1, 0, 0, 0); - current_sec = rtc_tm_to_time64(time); - - write_elapsed_second(current_sec - epoch_sec); - - return 0; -} - -static int vr41xx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) -{ - unsigned long low, mid, high; - struct rtc_time *time = &wkalrm->time; - - spin_lock_irq(&rtc_lock); - - low = rtc1_read(ECMPLREG); - mid = rtc1_read(ECMPMREG); - high = rtc1_read(ECMPHREG); - wkalrm->enabled = alarm_enabled; - - spin_unlock_irq(&rtc_lock); - - rtc_time64_to_tm((high << 17) | (mid << 1) | (low >> 15), time); - - return 0; -} - -static int vr41xx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) -{ - time64_t alarm_sec; - - alarm_sec = rtc_tm_to_time64(&wkalrm->time); - - spin_lock_irq(&rtc_lock); - - if (alarm_enabled) - disable_irq(aie_irq); - - rtc1_write(ECMPLREG, (uint16_t)(alarm_sec << 15)); - rtc1_write(ECMPMREG, (uint16_t)(alarm_sec >> 1)); - rtc1_write(ECMPHREG, (uint16_t)(alarm_sec >> 17)); - - if (wkalrm->enabled) - enable_irq(aie_irq); - - alarm_enabled = wkalrm->enabled; - - spin_unlock_irq(&rtc_lock); - - return 0; -} - -static int vr41xx_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) -{ - switch (cmd) { - case RTC_EPOCH_READ: - return put_user(epoch, (unsigned long __user *)arg); -#ifdef CONFIG_64BIT - case RTC_EPOCH_READ32: - return put_user(epoch, (unsigned int __user *)arg); -#endif - case RTC_EPOCH_SET: - /* Doesn't support before 1900 */ - if (arg < 1900) - return -EINVAL; - epoch = arg; - break; - default: - return -ENOIOCTLCMD; - } - - return 0; -} - -static int vr41xx_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) -{ - spin_lock_irq(&rtc_lock); - if (enabled) { - if (!alarm_enabled) { - enable_irq(aie_irq); - alarm_enabled = 1; - } - } else { - if (alarm_enabled) { - disable_irq(aie_irq); - alarm_enabled = 0; - } - } - spin_unlock_irq(&rtc_lock); - return 0; -} - -static irqreturn_t elapsedtime_interrupt(int irq, void *dev_id) -{ - struct platform_device *pdev = (struct platform_device *)dev_id; - struct rtc_device *rtc = platform_get_drvdata(pdev); - - rtc2_write(RTCINTREG, ELAPSEDTIME_INT); - - rtc_update_irq(rtc, 1, RTC_AF); - - return IRQ_HANDLED; -} - -static irqreturn_t rtclong1_interrupt(int irq, void *dev_id) -{ - struct platform_device *pdev = (struct platform_device *)dev_id; - struct rtc_device *rtc = platform_get_drvdata(pdev); - unsigned long count = periodic_count; - - rtc2_write(RTCINTREG, RTCLONG1_INT); - - rtc1_write(RTCL1LREG, count); - rtc1_write(RTCL1HREG, count >> 16); - - rtc_update_irq(rtc, 1, RTC_PF); - - return IRQ_HANDLED; -} - -static const struct rtc_class_ops vr41xx_rtc_ops = { - .ioctl = vr41xx_rtc_ioctl, - .read_time = vr41xx_rtc_read_time, - .set_time = vr41xx_rtc_set_time, - .read_alarm = vr41xx_rtc_read_alarm, - .set_alarm = vr41xx_rtc_set_alarm, - .alarm_irq_enable = vr41xx_rtc_alarm_irq_enable, -}; - -static int rtc_probe(struct platform_device *pdev) -{ - struct resource *res; - struct rtc_device *rtc; - int retval; - - if (pdev->num_resources != 4) - return -EBUSY; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -EBUSY; - - rtc1_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (!rtc1_base) - return -EBUSY; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!res) { - retval = -EBUSY; - goto err_rtc1_iounmap; - } - - rtc2_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (!rtc2_base) { - retval = -EBUSY; - goto err_rtc1_iounmap; - } - - rtc = devm_rtc_allocate_device(&pdev->dev); - if (IS_ERR(rtc)) { - retval = PTR_ERR(rtc); - goto err_iounmap_all; - } - - rtc->ops = &vr41xx_rtc_ops; - - /* 48-bit counter at 32.768 kHz */ - rtc->range_max = (1ULL << 33) - 1; - rtc->max_user_freq = MAX_PERIODIC_RATE; - - spin_lock_irq(&rtc_lock); - - rtc1_write(ECMPLREG, 0); - rtc1_write(ECMPMREG, 0); - rtc1_write(ECMPHREG, 0); - rtc1_write(RTCL1LREG, 0); - rtc1_write(RTCL1HREG, 0); - - spin_unlock_irq(&rtc_lock); - - aie_irq = platform_get_irq(pdev, 0); - if (aie_irq <= 0) { - retval = -EBUSY; - goto err_iounmap_all; - } - - retval = devm_request_irq(&pdev->dev, aie_irq, elapsedtime_interrupt, 0, - "elapsed_time", pdev); - if (retval < 0) - goto err_iounmap_all; - - pie_irq = platform_get_irq(pdev, 1); - if (pie_irq <= 0) { - retval = -EBUSY; - goto err_iounmap_all; - } - - retval = devm_request_irq(&pdev->dev, pie_irq, rtclong1_interrupt, 0, - "rtclong1", pdev); - if (retval < 0) - goto err_iounmap_all; - - platform_set_drvdata(pdev, rtc); - - disable_irq(aie_irq); - disable_irq(pie_irq); - - dev_info(&pdev->dev, "Real Time Clock of NEC VR4100 series\n"); - - retval = devm_rtc_register_device(rtc); - if (retval) - goto err_iounmap_all; - - return 0; - -err_iounmap_all: - rtc2_base = NULL; - -err_rtc1_iounmap: - rtc1_base = NULL; - - return retval; -} - -/* work with hotplug and coldplug */ -MODULE_ALIAS("platform:RTC"); - -static struct platform_driver rtc_platform_driver = { - .probe = rtc_probe, - .driver = { - .name = rtc_name, - }, -}; - -module_platform_driver(rtc_platform_driver); diff --git a/drivers/rtc/rtc-x1205.c b/drivers/rtc/rtc-x1205.c index d1d5a44d9122..ba0d22a5b421 100644 --- a/drivers/rtc/rtc-x1205.c +++ b/drivers/rtc/rtc-x1205.c @@ -614,8 +614,7 @@ static void x1205_sysfs_unregister(struct device *dev) } -static int x1205_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int x1205_probe(struct i2c_client *client) { int err = 0; unsigned char sr; @@ -681,7 +680,7 @@ static struct i2c_driver x1205_driver = { .name = "rtc-x1205", .of_match_table = x1205_dt_ids, }, - .probe = x1205_probe, + .probe_new = x1205_probe, .remove = x1205_remove, .id_table = x1205_id, }; diff --git a/drivers/rtc/rtc-zynqmp.c b/drivers/rtc/rtc-zynqmp.c index f440bb52be92..c9b85c838ebe 100644 --- a/drivers/rtc/rtc-zynqmp.c +++ b/drivers/rtc/rtc-zynqmp.c @@ -6,6 +6,7 @@ * */ +#include <linux/clk.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/io.h> @@ -36,17 +37,23 @@ #define RTC_OSC_EN BIT(24) #define RTC_BATT_EN BIT(31) -#define RTC_CALIB_DEF 0x198233 +#define RTC_CALIB_DEF 0x7FFF #define RTC_CALIB_MASK 0x1FFFFF #define RTC_ALRM_MASK BIT(1) #define RTC_MSEC 1000 +#define RTC_FR_MASK 0xF0000 +#define RTC_FR_MAX_TICKS 16 +#define RTC_PPB 1000000000LL +#define RTC_MIN_OFFSET -32768000 +#define RTC_MAX_OFFSET 32767000 struct xlnx_rtc_dev { struct rtc_device *rtc; void __iomem *reg_base; int alarm_irq; int sec_irq; - unsigned int calibval; + struct clk *rtc_clk; + unsigned int freq; }; static int xlnx_rtc_set_time(struct device *dev, struct rtc_time *tm) @@ -61,13 +68,6 @@ static int xlnx_rtc_set_time(struct device *dev, struct rtc_time *tm) */ new_time = rtc_tm_to_time64(tm) + 1; - /* - * Writing into calibration register will clear the Tick Counter and - * force the next second to be signaled exactly in 1 second period - */ - xrtcdev->calibval &= RTC_CALIB_MASK; - writel(xrtcdev->calibval, (xrtcdev->reg_base + RTC_CALIB_WR)); - writel(new_time, xrtcdev->reg_base + RTC_SET_TM_WR); /* @@ -173,15 +173,76 @@ static void xlnx_init_rtc(struct xlnx_rtc_dev *xrtcdev) rtc_ctrl = readl(xrtcdev->reg_base + RTC_CTRL); rtc_ctrl |= RTC_BATT_EN; writel(rtc_ctrl, xrtcdev->reg_base + RTC_CTRL); +} - /* - * Based on crystal freq of 33.330 KHz - * set the seconds counter and enable, set fractions counter - * to default value suggested as per design spec - * to correct RTC delay in frequency over period of time. +static int xlnx_rtc_read_offset(struct device *dev, long *offset) +{ + struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev); + unsigned long long rtc_ppb = RTC_PPB; + unsigned int tick_mult = do_div(rtc_ppb, xrtcdev->freq); + unsigned int calibval; + long offset_val; + + calibval = readl(xrtcdev->reg_base + RTC_CALIB_RD); + /* Offset with seconds ticks */ + offset_val = calibval & RTC_TICK_MASK; + offset_val = offset_val - RTC_CALIB_DEF; + offset_val = offset_val * tick_mult; + + /* Offset with fractional ticks */ + if (calibval & RTC_FR_EN) + offset_val += ((calibval & RTC_FR_MASK) >> RTC_FR_DATSHIFT) + * (tick_mult / RTC_FR_MAX_TICKS); + *offset = offset_val; + + return 0; +} + +static int xlnx_rtc_set_offset(struct device *dev, long offset) +{ + struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev); + unsigned long long rtc_ppb = RTC_PPB; + unsigned int tick_mult = do_div(rtc_ppb, xrtcdev->freq); + unsigned char fract_tick = 0; + unsigned int calibval; + short int max_tick; + int fract_offset; + + if (offset < RTC_MIN_OFFSET || offset > RTC_MAX_OFFSET) + return -ERANGE; + + /* Number ticks for given offset */ + max_tick = div_s64_rem(offset, tick_mult, &fract_offset); + + /* Number fractional ticks for given offset */ + if (fract_offset) { + if (fract_offset < 0) { + fract_offset = fract_offset + tick_mult; + max_tick--; + } + if (fract_offset > (tick_mult / RTC_FR_MAX_TICKS)) { + for (fract_tick = 1; fract_tick < 16; fract_tick++) { + if (fract_offset <= + (fract_tick * + (tick_mult / RTC_FR_MAX_TICKS))) + break; + } + } + } + + /* Zynqmp RTC uses second and fractional tick + * counters for compensation */ - xrtcdev->calibval &= RTC_CALIB_MASK; - writel(xrtcdev->calibval, (xrtcdev->reg_base + RTC_CALIB_WR)); + calibval = max_tick + RTC_CALIB_DEF; + + if (fract_tick) + calibval |= RTC_FR_EN; + + calibval |= (fract_tick << RTC_FR_DATSHIFT); + + writel(calibval, (xrtcdev->reg_base + RTC_CALIB_WR)); + + return 0; } static const struct rtc_class_ops xlnx_rtc_ops = { @@ -190,6 +251,8 @@ static const struct rtc_class_ops xlnx_rtc_ops = { .read_alarm = xlnx_rtc_read_alarm, .set_alarm = xlnx_rtc_set_alarm, .alarm_irq_enable = xlnx_rtc_alarm_irq_enable, + .read_offset = xlnx_rtc_read_offset, + .set_offset = xlnx_rtc_set_offset, }; static irqreturn_t xlnx_rtc_interrupt(int irq, void *id) @@ -255,10 +318,22 @@ static int xlnx_rtc_probe(struct platform_device *pdev) return ret; } - ret = of_property_read_u32(pdev->dev.of_node, "calibration", - &xrtcdev->calibval); - if (ret) - xrtcdev->calibval = RTC_CALIB_DEF; + /* Getting the rtc_clk info */ + xrtcdev->rtc_clk = devm_clk_get_optional(&pdev->dev, "rtc_clk"); + if (IS_ERR(xrtcdev->rtc_clk)) { + if (PTR_ERR(xrtcdev->rtc_clk) != -EPROBE_DEFER) + dev_warn(&pdev->dev, "Device clock not found.\n"); + } + xrtcdev->freq = clk_get_rate(xrtcdev->rtc_clk); + if (!xrtcdev->freq) { + ret = of_property_read_u32(pdev->dev.of_node, "calibration", + &xrtcdev->freq); + if (ret) + xrtcdev->freq = RTC_CALIB_DEF; + } + ret = readl(xrtcdev->reg_base + RTC_CALIB_RD); + if (!ret) + writel(xrtcdev->freq, (xrtcdev->reg_base + RTC_CALIB_WR)); xlnx_init_rtc(xrtcdev); diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 35d4b398c197..8bd9fd51208c 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -763,6 +763,49 @@ static void qeth_issue_ipa_msg(struct qeth_ipa_cmd *cmd, int rc, ipa_name, com, CARD_DEVID(card)); } +static void qeth_default_link_info(struct qeth_card *card) +{ + struct qeth_link_info *link_info = &card->info.link_info; + + QETH_CARD_TEXT(card, 2, "dftlinfo"); + link_info->duplex = DUPLEX_FULL; + + if (IS_IQD(card) || IS_VM_NIC(card)) { + link_info->speed = SPEED_10000; + link_info->port = PORT_FIBRE; + link_info->link_mode = QETH_LINK_MODE_FIBRE_SHORT; + } else { + switch (card->info.link_type) { + case QETH_LINK_TYPE_FAST_ETH: + case QETH_LINK_TYPE_LANE_ETH100: + link_info->speed = SPEED_100; + link_info->port = PORT_TP; + break; + case QETH_LINK_TYPE_GBIT_ETH: + case QETH_LINK_TYPE_LANE_ETH1000: + link_info->speed = SPEED_1000; + link_info->port = PORT_FIBRE; + break; + case QETH_LINK_TYPE_10GBIT_ETH: + link_info->speed = SPEED_10000; + link_info->port = PORT_FIBRE; + break; + case QETH_LINK_TYPE_25GBIT_ETH: + link_info->speed = SPEED_25000; + link_info->port = PORT_FIBRE; + break; + default: + dev_info(&card->gdev->dev, + "Unknown link type %x\n", + card->info.link_type); + link_info->speed = SPEED_UNKNOWN; + link_info->port = PORT_OTHER; + } + + link_info->link_mode = QETH_LINK_MODE_UNKNOWN; + } +} + static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card, struct qeth_ipa_cmd *cmd) { @@ -790,6 +833,7 @@ static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card, netdev_name(card->dev), card->info.chpid); qeth_issue_ipa_msg(cmd, cmd->hdr.return_code, card); netif_carrier_off(card->dev); + qeth_default_link_info(card); } return NULL; case IPA_CMD_STARTLAN: @@ -4744,92 +4788,6 @@ out_free: return rc; } -static int qeth_query_card_info_cb(struct qeth_card *card, - struct qeth_reply *reply, unsigned long data) -{ - struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *)data; - struct qeth_link_info *link_info = reply->param; - struct qeth_query_card_info *card_info; - - QETH_CARD_TEXT(card, 2, "qcrdincb"); - if (qeth_setadpparms_inspect_rc(cmd)) - return -EIO; - - card_info = &cmd->data.setadapterparms.data.card_info; - netdev_dbg(card->dev, - "card info: card_type=0x%02x, port_mode=0x%04x, port_speed=0x%08x\n", - card_info->card_type, card_info->port_mode, - card_info->port_speed); - - switch (card_info->port_mode) { - case CARD_INFO_PORTM_FULLDUPLEX: - link_info->duplex = DUPLEX_FULL; - break; - case CARD_INFO_PORTM_HALFDUPLEX: - link_info->duplex = DUPLEX_HALF; - break; - default: - link_info->duplex = DUPLEX_UNKNOWN; - } - - switch (card_info->card_type) { - case CARD_INFO_TYPE_1G_COPPER_A: - case CARD_INFO_TYPE_1G_COPPER_B: - link_info->speed = SPEED_1000; - link_info->port = PORT_TP; - break; - case CARD_INFO_TYPE_1G_FIBRE_A: - case CARD_INFO_TYPE_1G_FIBRE_B: - link_info->speed = SPEED_1000; - link_info->port = PORT_FIBRE; - break; - case CARD_INFO_TYPE_10G_FIBRE_A: - case CARD_INFO_TYPE_10G_FIBRE_B: - link_info->speed = SPEED_10000; - link_info->port = PORT_FIBRE; - break; - default: - switch (card_info->port_speed) { - case CARD_INFO_PORTS_10M: - link_info->speed = SPEED_10; - break; - case CARD_INFO_PORTS_100M: - link_info->speed = SPEED_100; - break; - case CARD_INFO_PORTS_1G: - link_info->speed = SPEED_1000; - break; - case CARD_INFO_PORTS_10G: - link_info->speed = SPEED_10000; - break; - case CARD_INFO_PORTS_25G: - link_info->speed = SPEED_25000; - break; - default: - link_info->speed = SPEED_UNKNOWN; - } - - link_info->port = PORT_OTHER; - } - - return 0; -} - -int qeth_query_card_info(struct qeth_card *card, - struct qeth_link_info *link_info) -{ - struct qeth_cmd_buffer *iob; - - QETH_CARD_TEXT(card, 2, "qcrdinfo"); - if (!qeth_adp_supported(card, IPA_SETADP_QUERY_CARD_INFO)) - return -EOPNOTSUPP; - iob = qeth_get_adapter_cmd(card, IPA_SETADP_QUERY_CARD_INFO, 0); - if (!iob) - return -ENOMEM; - - return qeth_send_ipa_cmd(card, iob, qeth_query_card_info_cb, link_info); -} - static int qeth_init_link_info_oat_cb(struct qeth_card *card, struct qeth_reply *reply_priv, unsigned long data) @@ -4839,6 +4797,7 @@ static int qeth_init_link_info_oat_cb(struct qeth_card *card, struct qeth_query_oat_physical_if *phys_if; struct qeth_query_oat_reply *reply; + QETH_CARD_TEXT(card, 2, "qoatincb"); if (qeth_setadpparms_inspect_rc(cmd)) return -EIO; @@ -4918,41 +4877,7 @@ static int qeth_init_link_info_oat_cb(struct qeth_card *card, static void qeth_init_link_info(struct qeth_card *card) { - card->info.link_info.duplex = DUPLEX_FULL; - - if (IS_IQD(card) || IS_VM_NIC(card)) { - card->info.link_info.speed = SPEED_10000; - card->info.link_info.port = PORT_FIBRE; - card->info.link_info.link_mode = QETH_LINK_MODE_FIBRE_SHORT; - } else { - switch (card->info.link_type) { - case QETH_LINK_TYPE_FAST_ETH: - case QETH_LINK_TYPE_LANE_ETH100: - card->info.link_info.speed = SPEED_100; - card->info.link_info.port = PORT_TP; - break; - case QETH_LINK_TYPE_GBIT_ETH: - case QETH_LINK_TYPE_LANE_ETH1000: - card->info.link_info.speed = SPEED_1000; - card->info.link_info.port = PORT_FIBRE; - break; - case QETH_LINK_TYPE_10GBIT_ETH: - card->info.link_info.speed = SPEED_10000; - card->info.link_info.port = PORT_FIBRE; - break; - case QETH_LINK_TYPE_25GBIT_ETH: - card->info.link_info.speed = SPEED_25000; - card->info.link_info.port = PORT_FIBRE; - break; - default: - dev_info(&card->gdev->dev, "Unknown link type %x\n", - card->info.link_type); - card->info.link_info.speed = SPEED_UNKNOWN; - card->info.link_info.port = PORT_OTHER; - } - - card->info.link_info.link_mode = QETH_LINK_MODE_UNKNOWN; - } + qeth_default_link_info(card); /* Get more accurate data via QUERY OAT: */ if (qeth_adp_supported(card, IPA_SETADP_QUERY_OAT)) { @@ -5461,6 +5386,7 @@ int qeth_set_offline(struct qeth_card *card, const struct qeth_discipline *disc, qeth_clear_working_pool_list(card); qeth_flush_local_addrs(card); card->info.promisc_mode = 0; + qeth_default_link_info(card); rc = qeth_stop_channel(&card->data); rc2 = qeth_stop_channel(&card->write); diff --git a/drivers/s390/net/qeth_ethtool.c b/drivers/s390/net/qeth_ethtool.c index b0b36b2132fe..9eba0a32e9f9 100644 --- a/drivers/s390/net/qeth_ethtool.c +++ b/drivers/s390/net/qeth_ethtool.c @@ -428,8 +428,8 @@ static int qeth_get_link_ksettings(struct net_device *netdev, struct ethtool_link_ksettings *cmd) { struct qeth_card *card = netdev->ml_priv; - struct qeth_link_info link_info; + QETH_CARD_TEXT(card, 4, "ethtglks"); cmd->base.speed = card->info.link_info.speed; cmd->base.duplex = card->info.link_info.duplex; cmd->base.port = card->info.link_info.port; @@ -439,16 +439,6 @@ static int qeth_get_link_ksettings(struct net_device *netdev, cmd->base.eth_tp_mdix = ETH_TP_MDI_INVALID; cmd->base.eth_tp_mdix_ctrl = ETH_TP_MDI_INVALID; - /* Check if we can obtain more accurate information. */ - if (!qeth_query_card_info(card, &link_info)) { - if (link_info.speed != SPEED_UNKNOWN) - cmd->base.speed = link_info.speed; - if (link_info.duplex != DUPLEX_UNKNOWN) - cmd->base.duplex = link_info.duplex; - if (link_info.port != PORT_OTHER) - cmd->base.port = link_info.port; - } - qeth_set_ethtool_link_modes(cmd, card->info.link_info.link_mode); return 0; diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c index 511bf8e0a436..b61acbb09be3 100644 --- a/drivers/s390/scsi/zfcp_fc.c +++ b/drivers/s390/scsi/zfcp_fc.c @@ -145,27 +145,33 @@ void zfcp_fc_enqueue_event(struct zfcp_adapter *adapter, static int zfcp_fc_wka_port_get(struct zfcp_fc_wka_port *wka_port) { + int ret = -EIO; + if (mutex_lock_interruptible(&wka_port->mutex)) return -ERESTARTSYS; if (wka_port->status == ZFCP_FC_WKA_PORT_OFFLINE || wka_port->status == ZFCP_FC_WKA_PORT_CLOSING) { wka_port->status = ZFCP_FC_WKA_PORT_OPENING; - if (zfcp_fsf_open_wka_port(wka_port)) + if (zfcp_fsf_open_wka_port(wka_port)) { + /* could not even send request, nothing to wait for */ wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE; + goto out; + } } - mutex_unlock(&wka_port->mutex); - - wait_event(wka_port->completion_wq, + wait_event(wka_port->opened, wka_port->status == ZFCP_FC_WKA_PORT_ONLINE || wka_port->status == ZFCP_FC_WKA_PORT_OFFLINE); if (wka_port->status == ZFCP_FC_WKA_PORT_ONLINE) { atomic_inc(&wka_port->refcount); - return 0; + ret = 0; + goto out; } - return -EIO; +out: + mutex_unlock(&wka_port->mutex); + return ret; } static void zfcp_fc_wka_port_offline(struct work_struct *work) @@ -181,9 +187,12 @@ static void zfcp_fc_wka_port_offline(struct work_struct *work) wka_port->status = ZFCP_FC_WKA_PORT_CLOSING; if (zfcp_fsf_close_wka_port(wka_port)) { + /* could not even send request, nothing to wait for */ wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE; - wake_up(&wka_port->completion_wq); + goto out; } + wait_event(wka_port->closed, + wka_port->status == ZFCP_FC_WKA_PORT_OFFLINE); out: mutex_unlock(&wka_port->mutex); } @@ -193,13 +202,15 @@ static void zfcp_fc_wka_port_put(struct zfcp_fc_wka_port *wka_port) if (atomic_dec_return(&wka_port->refcount) != 0) return; /* wait 10 milliseconds, other reqs might pop in */ - schedule_delayed_work(&wka_port->work, HZ / 100); + queue_delayed_work(wka_port->adapter->work_queue, &wka_port->work, + msecs_to_jiffies(10)); } static void zfcp_fc_wka_port_init(struct zfcp_fc_wka_port *wka_port, u32 d_id, struct zfcp_adapter *adapter) { - init_waitqueue_head(&wka_port->completion_wq); + init_waitqueue_head(&wka_port->opened); + init_waitqueue_head(&wka_port->closed); wka_port->adapter = adapter; wka_port->d_id = d_id; diff --git a/drivers/s390/scsi/zfcp_fc.h b/drivers/s390/scsi/zfcp_fc.h index 8aaf409ce9cb..97755407ce1b 100644 --- a/drivers/s390/scsi/zfcp_fc.h +++ b/drivers/s390/scsi/zfcp_fc.h @@ -185,7 +185,8 @@ enum zfcp_fc_wka_status { /** * struct zfcp_fc_wka_port - representation of well-known-address (WKA) FC port * @adapter: Pointer to adapter structure this WKA port belongs to - * @completion_wq: Wait for completion of open/close command + * @opened: Wait for completion of open command + * @closed: Wait for completion of close command * @status: Current status of WKA port * @refcount: Reference count to keep port open as long as it is in use * @d_id: FC destination id or well-known-address @@ -195,7 +196,8 @@ enum zfcp_fc_wka_status { */ struct zfcp_fc_wka_port { struct zfcp_adapter *adapter; - wait_queue_head_t completion_wq; + wait_queue_head_t opened; + wait_queue_head_t closed; enum zfcp_fc_wka_status status; atomic_t refcount; u32 d_id; diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 4f1e4385ce58..19223b075568 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -1907,7 +1907,7 @@ static void zfcp_fsf_open_wka_port_handler(struct zfcp_fsf_req *req) wka_port->status = ZFCP_FC_WKA_PORT_ONLINE; } out: - wake_up(&wka_port->completion_wq); + wake_up(&wka_port->opened); } /** @@ -1966,7 +1966,7 @@ static void zfcp_fsf_close_wka_port_handler(struct zfcp_fsf_req *req) } wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE; - wake_up(&wka_port->completion_wq); + wake_up(&wka_port->closed); } /** diff --git a/drivers/s390/virtio/virtio_ccw.c b/drivers/s390/virtio/virtio_ccw.c index aa96f67dd0b1..896896e32664 100644 --- a/drivers/s390/virtio/virtio_ccw.c +++ b/drivers/s390/virtio/virtio_ccw.c @@ -532,6 +532,9 @@ static struct virtqueue *virtio_ccw_setup_vq(struct virtio_device *vdev, err = -ENOMEM; goto out_err; } + + vq->num_max = info->num; + /* it may have been reduced */ info->num = virtqueue_get_vring_size(vq); @@ -634,6 +637,7 @@ static int virtio_ccw_find_vqs(struct virtio_device *vdev, unsigned nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], const char * const names[], + u32 sizes[], const bool *ctx, struct irq_affinity *desc) { diff --git a/drivers/scsi/FlashPoint.c b/drivers/scsi/FlashPoint.c index 90253208a72f..3d9c56ac8224 100644 --- a/drivers/scsi/FlashPoint.c +++ b/drivers/scsi/FlashPoint.c @@ -1712,7 +1712,7 @@ static unsigned char FlashPoint_InterruptPending(void *pCurrCard) static int FlashPoint_HandleInterrupt(void *pcard) { struct sccb *currSCCB; - unsigned char thisCard, result, bm_status, bm_int_st; + unsigned char thisCard, result, bm_status; unsigned short hp_int; unsigned char i, target; struct sccb_card *pCurrCard = pcard; @@ -1723,7 +1723,7 @@ static int FlashPoint_HandleInterrupt(void *pcard) MDISABLE_INT(ioport); - if ((bm_int_st = RD_HARPOON(ioport + hp_int_status)) & EXT_STATUS_ON) + if (RD_HARPOON(ioport + hp_int_status) & EXT_STATUS_ON) bm_status = RD_HARPOON(ioport + hp_ext_status) & (unsigned char)BAD_EXT_STATUS; else diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 26bf3b153595..0738238ed6cc 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -190,6 +190,15 @@ void scsi_remove_host(struct Scsi_Host *shost) transport_unregister_device(&shost->shost_gendev); device_unregister(&shost->shost_dev); device_del(&shost->shost_gendev); + + /* + * After scsi_remove_host() has returned the scsi LLD module can be + * unloaded and/or the host resources can be released. Hence wait until + * the dependent SCSI targets and devices are gone before returning. + */ + wait_event(shost->targets_wq, atomic_read(&shost->target_count) == 0); + + scsi_mq_destroy_tags(shost); } EXPORT_SYMBOL(scsi_remove_host); @@ -300,8 +309,8 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev, return error; /* - * Any host allocation in this function will be freed in - * scsi_host_dev_release(). + * Any resources associated with the SCSI host in this function except + * the tag set will be freed by scsi_host_dev_release(). */ out_del_dev: device_del(&shost->shost_dev); @@ -317,6 +326,7 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev, pm_runtime_disable(&shost->shost_gendev); pm_runtime_set_suspended(&shost->shost_gendev); pm_runtime_put_noidle(&shost->shost_gendev); + scsi_mq_destroy_tags(shost); fail: return error; } @@ -350,9 +360,6 @@ static void scsi_host_dev_release(struct device *dev) kfree(dev_name(&shost->shost_dev)); } - if (shost->tag_set.tags) - scsi_mq_destroy_tags(shost); - kfree(shost->shost_data); ida_free(&host_index_ida, shost->host_no); @@ -399,6 +406,7 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) INIT_LIST_HEAD(&shost->starved_list); init_waitqueue_head(&shost->host_wait); mutex_init(&shost->scan_mutex); + init_waitqueue_head(&shost->targets_wq); index = ida_alloc(&host_index_ida, GFP_KERNEL); if (index < 0) { diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 4a0eadd1c22c..c69c5a0979ec 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -7948,6 +7948,8 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) /* The lpfc_wq workqueue for deferred irq use */ phba->wq = alloc_workqueue("lpfc_wq", WQ_MEM_RECLAIM, 0); + if (!phba->wq) + return -ENOMEM; /* * Initialize timers used by driver diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c index 5b5885d9732b..e48d4261d0bc 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -3199,7 +3199,6 @@ megasas_build_io_fusion(struct megasas_instance *instance, struct megasas_cmd_fusion *cmd) { int sge_count; - u8 cmd_type; u16 pd_index = 0; u8 drive_type = 0; struct MPI2_RAID_SCSI_IO_REQUEST *io_request = cmd->io_request; @@ -3225,7 +3224,7 @@ megasas_build_io_fusion(struct megasas_instance *instance, */ io_request->IoFlags = cpu_to_le16(scp->cmd_len); - switch (cmd_type = megasas_cmd_type(scp)) { + switch (megasas_cmd_type(scp)) { case READ_WRITE_LDIO: megasas_build_ldio_fusion(instance, scp, cmd); break; diff --git a/drivers/scsi/pm8001/pm8001_hwi.c b/drivers/scsi/pm8001/pm8001_hwi.c index 4acaff479916..91d78d0a38fe 100644 --- a/drivers/scsi/pm8001/pm8001_hwi.c +++ b/drivers/scsi/pm8001/pm8001_hwi.c @@ -3138,7 +3138,7 @@ int pm8001_mpi_local_phy_ctl(struct pm8001_hba_info *pm8001_ha, void *piomb) * * when HBA driver received the identify done event or initiate FIS received * event(for SATA), it will invoke this function to notify the sas layer that - * the sas toplogy has formed, please discover the the whole sas domain, + * the sas toplogy has formed, please discover the whole sas domain, * while receive a broadcast(change) primitive just tell the sas * layer to discover the changed domain rather than the whole domain. */ diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index c59eac7a32f2..086ec5b5862d 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -586,10 +586,13 @@ EXPORT_SYMBOL(scsi_device_get); */ void scsi_device_put(struct scsi_device *sdev) { - struct module *mod = sdev->host->hostt->module; - + /* + * Decreasing the module reference count before the device reference + * count is safe since scsi_remove_host() only returns after all + * devices have been removed. + */ + module_put(sdev->host->hostt->module); put_device(&sdev->sdev_gendev); - module_put(mod); } EXPORT_SYMBOL(scsi_device_put); diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 91ac901a6682..ac6059702d13 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -406,9 +406,14 @@ static void scsi_target_destroy(struct scsi_target *starget) static void scsi_target_dev_release(struct device *dev) { struct device *parent = dev->parent; + struct Scsi_Host *shost = dev_to_shost(parent); struct scsi_target *starget = to_scsi_target(dev); kfree(starget); + + if (atomic_dec_return(&shost->target_count) == 0) + wake_up(&shost->targets_wq); + put_device(parent); } @@ -521,6 +526,10 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, starget->state = STARGET_CREATED; starget->scsi_level = SCSI_2; starget->max_target_blocked = SCSI_DEFAULT_TARGET_BLOCKED; + init_waitqueue_head(&starget->sdev_wq); + + atomic_inc(&shost->target_count); + retry: spin_lock_irqsave(shost->host_lock, flags); diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index aa70d9282161..9dad2fd5297f 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -443,18 +443,15 @@ static void scsi_device_cls_release(struct device *class_dev) static void scsi_device_dev_release_usercontext(struct work_struct *work) { - struct scsi_device *sdev; + struct scsi_device *sdev = container_of(work, struct scsi_device, + ew.work); + struct scsi_target *starget = sdev->sdev_target; struct device *parent; struct list_head *this, *tmp; struct scsi_vpd *vpd_pg80 = NULL, *vpd_pg83 = NULL; struct scsi_vpd *vpd_pg0 = NULL, *vpd_pg89 = NULL; struct scsi_vpd *vpd_pgb0 = NULL, *vpd_pgb1 = NULL, *vpd_pgb2 = NULL; unsigned long flags; - struct module *mod; - - sdev = container_of(work, struct scsi_device, ew.work); - - mod = sdev->host->hostt->module; scsi_dh_release_device(sdev); @@ -516,19 +513,16 @@ static void scsi_device_dev_release_usercontext(struct work_struct *work) kfree(sdev->inquiry); kfree(sdev); + if (starget && atomic_dec_return(&starget->sdev_count) == 0) + wake_up(&starget->sdev_wq); + if (parent) put_device(parent); - module_put(mod); } static void scsi_device_dev_release(struct device *dev) { struct scsi_device *sdp = to_scsi_device(dev); - - /* Set module pointer as NULL in case of module unloading */ - if (!try_module_get(sdp->host->hostt->module)) - sdp->host->hostt->module = NULL; - execute_in_process_context(scsi_device_dev_release_usercontext, &sdp->ew); } @@ -1535,6 +1529,14 @@ static void __scsi_remove_target(struct scsi_target *starget) goto restart; } spin_unlock_irqrestore(shost->host_lock, flags); + + /* + * After scsi_remove_target() returns its caller can remove resources + * associated with @starget, e.g. an rport or session. Wait until all + * devices associated with @starget have been removed to prevent that + * a SCSI error handling callback function triggers a use-after-free. + */ + wait_event(starget->sdev_wq, atomic_read(&starget->sdev_count) == 0); } /** @@ -1645,6 +1647,9 @@ void scsi_sysfs_device_initialize(struct scsi_device *sdev) list_add_tail(&sdev->same_target_siblings, &starget->devices); list_add_tail(&sdev->siblings, &shost->__devices); spin_unlock_irqrestore(shost->host_lock, flags); + + atomic_inc(&starget->sdev_count); + /* * device can now only be removed via __scsi_remove_device() so hold * the target. Target will be held in CREATED state until something diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c index 58df0145e8d0..fb91423a4e2e 100644 --- a/drivers/target/target_core_alua.c +++ b/drivers/target/target_core_alua.c @@ -934,8 +934,7 @@ static void core_alua_queue_state_change_ua(struct t10_alua_tg_pt_gp *tg_pt_gp) spin_lock(&lun->lun_deve_lock); list_for_each_entry(se_deve, &lun->lun_deve_list, lun_link) { - lacl = rcu_dereference_check(se_deve->se_lun_acl, - lockdep_is_held(&lun->lun_deve_lock)); + lacl = se_deve->se_lun_acl; /* * spc4r37 p.242: diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index 086ac9c9343c..b7f16ee8aa0e 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -75,7 +75,7 @@ transport_lookup_cmd_lun(struct se_cmd *se_cmd) return TCM_WRITE_PROTECTED; } - se_lun = rcu_dereference(deve->se_lun); + se_lun = deve->se_lun; if (!percpu_ref_tryget_live(&se_lun->lun_ref)) { se_lun = NULL; @@ -152,7 +152,7 @@ int transport_lookup_tmr_lun(struct se_cmd *se_cmd) rcu_read_lock(); deve = target_nacl_find_deve(nacl, se_cmd->orig_fe_lun); if (deve) { - se_lun = rcu_dereference(deve->se_lun); + se_lun = deve->se_lun; if (!percpu_ref_tryget_live(&se_lun->lun_ref)) { se_lun = NULL; @@ -216,7 +216,7 @@ struct se_dev_entry *core_get_se_deve_from_rtpi( rcu_read_lock(); hlist_for_each_entry_rcu(deve, &nacl->lun_entry_hlist, link) { - lun = rcu_dereference(deve->se_lun); + lun = deve->se_lun; if (!lun) { pr_err("%s device entries device pointer is" " NULL, but Initiator has access.\n", @@ -243,11 +243,8 @@ void core_free_device_list_for_node( struct se_dev_entry *deve; mutex_lock(&nacl->lun_entry_mutex); - hlist_for_each_entry_rcu(deve, &nacl->lun_entry_hlist, link) { - struct se_lun *lun = rcu_dereference_check(deve->se_lun, - lockdep_is_held(&nacl->lun_entry_mutex)); - core_disable_device_list_for_node(lun, deve, nacl, tpg); - } + hlist_for_each_entry_rcu(deve, &nacl->lun_entry_hlist, link) + core_disable_device_list_for_node(deve->se_lun, deve, nacl, tpg); mutex_unlock(&nacl->lun_entry_mutex); } @@ -334,8 +331,7 @@ int core_enable_device_list_for_node( mutex_lock(&nacl->lun_entry_mutex); orig = target_nacl_find_deve(nacl, mapped_lun); if (orig && orig->se_lun) { - struct se_lun *orig_lun = rcu_dereference_check(orig->se_lun, - lockdep_is_held(&nacl->lun_entry_mutex)); + struct se_lun *orig_lun = orig->se_lun; if (orig_lun != lun) { pr_err("Existing orig->se_lun doesn't match new lun" @@ -355,8 +351,8 @@ int core_enable_device_list_for_node( return -EINVAL; } - rcu_assign_pointer(new->se_lun, lun); - rcu_assign_pointer(new->se_lun_acl, lun_acl); + new->se_lun = lun; + new->se_lun_acl = lun_acl; hlist_del_rcu(&orig->link); hlist_add_head_rcu(&new->link, &nacl->lun_entry_hlist); mutex_unlock(&nacl->lun_entry_mutex); @@ -374,8 +370,8 @@ int core_enable_device_list_for_node( return 0; } - rcu_assign_pointer(new->se_lun, lun); - rcu_assign_pointer(new->se_lun_acl, lun_acl); + new->se_lun = lun; + new->se_lun_acl = lun_acl; hlist_add_head_rcu(&new->link, &nacl->lun_entry_hlist); mutex_unlock(&nacl->lun_entry_mutex); @@ -434,9 +430,6 @@ void core_disable_device_list_for_node( kref_put(&orig->pr_kref, target_pr_kref_release); wait_for_completion(&orig->pr_comp); - rcu_assign_pointer(orig->se_lun, NULL); - rcu_assign_pointer(orig->se_lun_acl, NULL); - kfree_rcu(orig, rcu_head); core_scsi3_free_pr_reg_from_nacl(dev, nacl); @@ -457,10 +450,7 @@ void core_clear_lun_from_tpg(struct se_lun *lun, struct se_portal_group *tpg) mutex_lock(&nacl->lun_entry_mutex); hlist_for_each_entry_rcu(deve, &nacl->lun_entry_hlist, link) { - struct se_lun *tmp_lun = rcu_dereference_check(deve->se_lun, - lockdep_is_held(&nacl->lun_entry_mutex)); - - if (lun != tmp_lun) + if (lun != deve->se_lun) continue; core_disable_device_list_for_node(lun, deve, nacl, tpg); diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index 3829b61b56c1..a1d67554709f 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -739,8 +739,7 @@ static struct t10_pr_registration *__core_scsi3_alloc_registration( if (!deve_tmp->se_lun_acl) continue; - lacl_tmp = rcu_dereference_check(deve_tmp->se_lun_acl, - lockdep_is_held(&lun_tmp->lun_deve_lock)); + lacl_tmp = deve_tmp->se_lun_acl; nacl_tmp = lacl_tmp->se_lun_nacl; /* * Skip the matching struct se_node_acl that is allocated @@ -784,8 +783,7 @@ static struct t10_pr_registration *__core_scsi3_alloc_registration( * the original *pr_reg is processed in * __core_scsi3_add_registration() */ - dest_lun = rcu_dereference_check(deve_tmp->se_lun, - kref_read(&deve_tmp->pr_kref) != 0); + dest_lun = deve_tmp->se_lun; pr_reg_atp = __core_scsi3_do_alloc_registration(dev, nacl_tmp, dest_lun, deve_tmp, @@ -1437,34 +1435,26 @@ static void core_scsi3_nodeacl_undepend_item(struct se_node_acl *nacl) static int core_scsi3_lunacl_depend_item(struct se_dev_entry *se_deve) { - struct se_lun_acl *lun_acl; - /* * For nacl->dynamic_node_acl=1 */ - lun_acl = rcu_dereference_check(se_deve->se_lun_acl, - kref_read(&se_deve->pr_kref) != 0); - if (!lun_acl) + if (!se_deve->se_lun_acl) return 0; - return target_depend_item(&lun_acl->se_lun_group.cg_item); + return target_depend_item(&se_deve->se_lun_acl->se_lun_group.cg_item); } static void core_scsi3_lunacl_undepend_item(struct se_dev_entry *se_deve) { - struct se_lun_acl *lun_acl; - /* * For nacl->dynamic_node_acl=1 */ - lun_acl = rcu_dereference_check(se_deve->se_lun_acl, - kref_read(&se_deve->pr_kref) != 0); - if (!lun_acl) { + if (!se_deve->se_lun_acl) { kref_put(&se_deve->pr_kref, target_pr_kref_release); return; } - target_undepend_item(&lun_acl->se_lun_group.cg_item); + target_undepend_item(&se_deve->se_lun_acl->se_lun_group.cg_item); kref_put(&se_deve->pr_kref, target_pr_kref_release); } @@ -1751,8 +1741,7 @@ core_scsi3_decode_spec_i_port( * and then call __core_scsi3_add_registration() in the * 2nd loop which will never fail. */ - dest_lun = rcu_dereference_check(dest_se_deve->se_lun, - kref_read(&dest_se_deve->pr_kref) != 0); + dest_lun = dest_se_deve->se_lun; dest_pr_reg = __core_scsi3_alloc_registration(cmd->se_dev, dest_node_acl, dest_lun, dest_se_deve, @@ -3446,8 +3435,7 @@ after_iport_check: dest_pr_reg = __core_scsi3_locate_pr_reg(dev, dest_node_acl, iport_ptr); if (!dest_pr_reg) { - struct se_lun *dest_lun = rcu_dereference_check(dest_se_deve->se_lun, - kref_read(&dest_se_deve->pr_kref) != 0); + struct se_lun *dest_lun = dest_se_deve->se_lun; spin_unlock(&dev->dev_reservation_lock); if (core_scsi3_alloc_registration(cmd->se_dev, dest_node_acl, diff --git a/drivers/target/target_core_stat.c b/drivers/target/target_core_stat.c index 62d15bcc3d93..f85ee5b0fd80 100644 --- a/drivers/target/target_core_stat.c +++ b/drivers/target/target_core_stat.c @@ -877,7 +877,6 @@ static ssize_t target_stat_auth_dev_show(struct config_item *item, struct se_lun_acl *lacl = auth_to_lacl(item); struct se_node_acl *nacl = lacl->se_lun_nacl; struct se_dev_entry *deve; - struct se_lun *lun; ssize_t ret; rcu_read_lock(); @@ -886,9 +885,9 @@ static ssize_t target_stat_auth_dev_show(struct config_item *item, rcu_read_unlock(); return -ENODEV; } - lun = rcu_dereference(deve->se_lun); + /* scsiDeviceIndex */ - ret = snprintf(page, PAGE_SIZE, "%u\n", lun->lun_index); + ret = snprintf(page, PAGE_SIZE, "%u\n", deve->se_lun->lun_index); rcu_read_unlock(); return ret; } @@ -1217,7 +1216,6 @@ static ssize_t target_stat_iport_dev_show(struct config_item *item, struct se_lun_acl *lacl = iport_to_lacl(item); struct se_node_acl *nacl = lacl->se_lun_nacl; struct se_dev_entry *deve; - struct se_lun *lun; ssize_t ret; rcu_read_lock(); @@ -1226,9 +1224,9 @@ static ssize_t target_stat_iport_dev_show(struct config_item *item, rcu_read_unlock(); return -ENODEV; } - lun = rcu_dereference(deve->se_lun); + /* scsiDeviceIndex */ - ret = snprintf(page, PAGE_SIZE, "%u\n", lun->lun_index); + ret = snprintf(page, PAGE_SIZE, "%u\n", deve->se_lun->lun_index); rcu_read_unlock(); return ret; } diff --git a/drivers/target/target_core_xcopy.c b/drivers/target/target_core_xcopy.c index 6bb20aa9c5bc..8713cda0c2fb 100644 --- a/drivers/target/target_core_xcopy.c +++ b/drivers/target/target_core_xcopy.c @@ -88,7 +88,7 @@ static int target_xcopy_locate_se_dev_e4(struct se_session *sess, struct se_device *this_dev; int rc; - this_lun = rcu_dereference(deve->se_lun); + this_lun = deve->se_lun; this_dev = rcu_dereference_raw(this_lun->lun_se_dev); rc = target_xcopy_locate_se_dev_e4_iter(this_dev, dev_wwn); diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index 3bc0709a5dc2..6bc679d22927 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -8326,6 +8326,7 @@ static struct scsi_host_template ufshcd_driver_template = { .cmd_per_lun = UFSHCD_CMD_PER_LUN, .can_queue = UFSHCD_CAN_QUEUE, .max_segment_size = PRDT_DATA_BYTE_COUNT_MAX, + .max_sectors = (1 << 20) / SECTOR_SIZE, /* 1 MiB */ .max_host_blocked = 1, .track_queue_depth = 1, .sdev_groups = ufshcd_driver_groups, @@ -9508,12 +9509,8 @@ EXPORT_SYMBOL(ufshcd_runtime_resume); int ufshcd_shutdown(struct ufs_hba *hba) { if (ufshcd_is_ufs_dev_poweroff(hba) && ufshcd_is_link_off(hba)) - goto out; - - pm_runtime_get_sync(hba->dev); + ufshcd_suspend(hba); - ufshcd_suspend(hba); -out: hba->is_powered = false; /* allow force shutdown even in case of errors */ return 0; diff --git a/drivers/ufs/host/ufshcd-pci.c b/drivers/ufs/host/ufshcd-pci.c index 24af1f389bf2..1c91f43e15c8 100644 --- a/drivers/ufs/host/ufshcd-pci.c +++ b/drivers/ufs/host/ufshcd-pci.c @@ -24,7 +24,7 @@ struct ufs_host { void (*late_init)(struct ufs_hba *hba); }; -enum { +enum intel_ufs_dsm_func_id { INTEL_DSM_FNS = 0, INTEL_DSM_RESET = 1, }; @@ -42,6 +42,15 @@ static const guid_t intel_dsm_guid = GUID_INIT(0x1A4832A0, 0x7D03, 0x43CA, 0xB0, 0x20, 0xF6, 0xDC, 0xD1, 0x2A, 0x19, 0x50); +static bool __intel_dsm_supported(struct intel_host *host, + enum intel_ufs_dsm_func_id fn) +{ + return fn < 32 && fn >= 0 && (host->dsm_fns & (1u << fn)); +} + +#define INTEL_DSM_SUPPORTED(host, name) \ + __intel_dsm_supported(host, INTEL_DSM_##name) + static int __intel_dsm(struct intel_host *intel_host, struct device *dev, unsigned int fn, u32 *result) { @@ -71,7 +80,7 @@ out: static int intel_dsm(struct intel_host *intel_host, struct device *dev, unsigned int fn, u32 *result) { - if (fn > 31 || !(intel_host->dsm_fns & (1 << fn))) + if (!__intel_dsm_supported(intel_host, fn)) return -EOPNOTSUPP; return __intel_dsm(intel_host, dev, fn, result); @@ -300,7 +309,7 @@ static int ufs_intel_device_reset(struct ufs_hba *hba) { struct intel_host *host = ufshcd_get_variant(hba); - if (host->dsm_fns & INTEL_DSM_RESET) { + if (INTEL_DSM_SUPPORTED(host, RESET)) { u32 result = 0; int err; @@ -342,7 +351,7 @@ static int ufs_intel_common_init(struct ufs_hba *hba) return -ENOMEM; ufshcd_set_variant(hba, host); intel_dsm_init(host, hba->dev); - if (host->dsm_fns & INTEL_DSM_RESET) { + if (INTEL_DSM_SUPPORTED(host, RESET)) { if (hba->vops->device_reset) hba->caps |= UFSHCD_CAP_DEEPSLEEP; } else { diff --git a/drivers/vdpa/ifcvf/ifcvf_base.c b/drivers/vdpa/ifcvf/ifcvf_base.c index 48c4dadb0c7c..75a703b803a2 100644 --- a/drivers/vdpa/ifcvf/ifcvf_base.c +++ b/drivers/vdpa/ifcvf/ifcvf_base.c @@ -29,7 +29,6 @@ u16 ifcvf_set_config_vector(struct ifcvf_hw *hw, int vector) { struct virtio_pci_common_cfg __iomem *cfg = hw->common_cfg; - cfg = hw->common_cfg; vp_iowrite16(vector, &cfg->msix_config); return vp_ioread16(&cfg->msix_config); @@ -128,6 +127,7 @@ int ifcvf_init_hw(struct ifcvf_hw *hw, struct pci_dev *pdev) break; case VIRTIO_PCI_CAP_DEVICE_CFG: hw->dev_cfg = get_cap_addr(hw, &cap); + hw->cap_dev_config_size = le32_to_cpu(cap.length); IFCVF_DBG(pdev, "hw->dev_cfg = %p\n", hw->dev_cfg); break; } @@ -233,15 +233,23 @@ int ifcvf_verify_min_features(struct ifcvf_hw *hw, u64 features) u32 ifcvf_get_config_size(struct ifcvf_hw *hw) { struct ifcvf_adapter *adapter; + u32 net_config_size = sizeof(struct virtio_net_config); + u32 blk_config_size = sizeof(struct virtio_blk_config); + u32 cap_size = hw->cap_dev_config_size; u32 config_size; adapter = vf_to_adapter(hw); + /* If the onboard device config space size is greater than + * the size of struct virtio_net/blk_config, only the spec + * implementing contents size is returned, this is very + * unlikely, defensive programming. + */ switch (hw->dev_type) { case VIRTIO_ID_NET: - config_size = sizeof(struct virtio_net_config); + config_size = min(cap_size, net_config_size); break; case VIRTIO_ID_BLOCK: - config_size = sizeof(struct virtio_blk_config); + config_size = min(cap_size, blk_config_size); break; default: config_size = 0; diff --git a/drivers/vdpa/ifcvf/ifcvf_base.h b/drivers/vdpa/ifcvf/ifcvf_base.h index 115b61f4924b..f5563f665cc6 100644 --- a/drivers/vdpa/ifcvf/ifcvf_base.h +++ b/drivers/vdpa/ifcvf/ifcvf_base.h @@ -87,6 +87,8 @@ struct ifcvf_hw { int config_irq; int vqs_reused_irq; u16 nr_vring; + /* VIRTIO_PCI_CAP_DEVICE_CFG size */ + u32 cap_dev_config_size; }; struct ifcvf_adapter { diff --git a/drivers/vdpa/ifcvf/ifcvf_main.c b/drivers/vdpa/ifcvf/ifcvf_main.c index 0a5670729412..f9c0044c6442 100644 --- a/drivers/vdpa/ifcvf/ifcvf_main.c +++ b/drivers/vdpa/ifcvf/ifcvf_main.c @@ -685,7 +685,7 @@ static struct vdpa_notification_area ifcvf_get_vq_notification(struct vdpa_devic } /* - * IFCVF currently does't have on-chip IOMMU, so not + * IFCVF currently doesn't have on-chip IOMMU, so not * implemented set_map()/dma_map()/dma_unmap() */ static const struct vdpa_config_ops ifc_vdpa_ops = { @@ -752,59 +752,36 @@ static int ifcvf_vdpa_dev_add(struct vdpa_mgmt_dev *mdev, const char *name, { struct ifcvf_vdpa_mgmt_dev *ifcvf_mgmt_dev; struct ifcvf_adapter *adapter; + struct vdpa_device *vdpa_dev; struct pci_dev *pdev; struct ifcvf_hw *vf; - struct device *dev; - int ret, i; + int ret; ifcvf_mgmt_dev = container_of(mdev, struct ifcvf_vdpa_mgmt_dev, mdev); - if (ifcvf_mgmt_dev->adapter) + if (!ifcvf_mgmt_dev->adapter) return -EOPNOTSUPP; - pdev = ifcvf_mgmt_dev->pdev; - dev = &pdev->dev; - adapter = vdpa_alloc_device(struct ifcvf_adapter, vdpa, - dev, &ifc_vdpa_ops, 1, 1, name, false); - if (IS_ERR(adapter)) { - IFCVF_ERR(pdev, "Failed to allocate vDPA structure"); - return PTR_ERR(adapter); - } - - ifcvf_mgmt_dev->adapter = adapter; - + adapter = ifcvf_mgmt_dev->adapter; vf = &adapter->vf; - vf->dev_type = get_dev_type(pdev); - vf->base = pcim_iomap_table(pdev); + pdev = adapter->pdev; + vdpa_dev = &adapter->vdpa; - adapter->pdev = pdev; - adapter->vdpa.dma_dev = &pdev->dev; - - ret = ifcvf_init_hw(vf, pdev); - if (ret) { - IFCVF_ERR(pdev, "Failed to init IFCVF hw\n"); - goto err; - } - - for (i = 0; i < vf->nr_vring; i++) - vf->vring[i].irq = -EINVAL; - - vf->hw_features = ifcvf_get_hw_features(vf); - vf->config_size = ifcvf_get_config_size(vf); + if (name) + ret = dev_set_name(&vdpa_dev->dev, "%s", name); + else + ret = dev_set_name(&vdpa_dev->dev, "vdpa%u", vdpa_dev->index); - adapter->vdpa.mdev = &ifcvf_mgmt_dev->mdev; ret = _vdpa_register_device(&adapter->vdpa, vf->nr_vring); if (ret) { + put_device(&adapter->vdpa.dev); IFCVF_ERR(pdev, "Failed to register to vDPA bus"); - goto err; + return ret; } return 0; - -err: - put_device(&adapter->vdpa.dev); - return ret; } + static void ifcvf_vdpa_dev_del(struct vdpa_mgmt_dev *mdev, struct vdpa_device *dev) { struct ifcvf_vdpa_mgmt_dev *ifcvf_mgmt_dev; @@ -823,61 +800,94 @@ static int ifcvf_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct ifcvf_vdpa_mgmt_dev *ifcvf_mgmt_dev; struct device *dev = &pdev->dev; + struct ifcvf_adapter *adapter; + struct ifcvf_hw *vf; u32 dev_type; - int ret; - - ifcvf_mgmt_dev = kzalloc(sizeof(struct ifcvf_vdpa_mgmt_dev), GFP_KERNEL); - if (!ifcvf_mgmt_dev) { - IFCVF_ERR(pdev, "Failed to alloc memory for the vDPA management device\n"); - return -ENOMEM; - } - - dev_type = get_dev_type(pdev); - switch (dev_type) { - case VIRTIO_ID_NET: - ifcvf_mgmt_dev->mdev.id_table = id_table_net; - break; - case VIRTIO_ID_BLOCK: - ifcvf_mgmt_dev->mdev.id_table = id_table_blk; - break; - default: - IFCVF_ERR(pdev, "VIRTIO ID %u not supported\n", dev_type); - ret = -EOPNOTSUPP; - goto err; - } - - ifcvf_mgmt_dev->mdev.ops = &ifcvf_vdpa_mgmt_dev_ops; - ifcvf_mgmt_dev->mdev.device = dev; - ifcvf_mgmt_dev->pdev = pdev; + int ret, i; ret = pcim_enable_device(pdev); if (ret) { IFCVF_ERR(pdev, "Failed to enable device\n"); - goto err; + return ret; } - ret = pcim_iomap_regions(pdev, BIT(0) | BIT(2) | BIT(4), IFCVF_DRIVER_NAME); if (ret) { IFCVF_ERR(pdev, "Failed to request MMIO region\n"); - goto err; + return ret; } ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); if (ret) { IFCVF_ERR(pdev, "No usable DMA configuration\n"); - goto err; + return ret; } ret = devm_add_action_or_reset(dev, ifcvf_free_irq_vectors, pdev); if (ret) { IFCVF_ERR(pdev, "Failed for adding devres for freeing irq vectors\n"); - goto err; + return ret; } pci_set_master(pdev); + adapter = vdpa_alloc_device(struct ifcvf_adapter, vdpa, + dev, &ifc_vdpa_ops, 1, 1, NULL, false); + if (IS_ERR(adapter)) { + IFCVF_ERR(pdev, "Failed to allocate vDPA structure"); + return PTR_ERR(adapter); + } + + vf = &adapter->vf; + vf->dev_type = get_dev_type(pdev); + vf->base = pcim_iomap_table(pdev); + + adapter->pdev = pdev; + adapter->vdpa.dma_dev = &pdev->dev; + + ret = ifcvf_init_hw(vf, pdev); + if (ret) { + IFCVF_ERR(pdev, "Failed to init IFCVF hw\n"); + return ret; + } + + for (i = 0; i < vf->nr_vring; i++) + vf->vring[i].irq = -EINVAL; + + vf->hw_features = ifcvf_get_hw_features(vf); + vf->config_size = ifcvf_get_config_size(vf); + + ifcvf_mgmt_dev = kzalloc(sizeof(struct ifcvf_vdpa_mgmt_dev), GFP_KERNEL); + if (!ifcvf_mgmt_dev) { + IFCVF_ERR(pdev, "Failed to alloc memory for the vDPA management device\n"); + return -ENOMEM; + } + + ifcvf_mgmt_dev->mdev.ops = &ifcvf_vdpa_mgmt_dev_ops; + ifcvf_mgmt_dev->mdev.device = dev; + ifcvf_mgmt_dev->adapter = adapter; + + dev_type = get_dev_type(pdev); + switch (dev_type) { + case VIRTIO_ID_NET: + ifcvf_mgmt_dev->mdev.id_table = id_table_net; + break; + case VIRTIO_ID_BLOCK: + ifcvf_mgmt_dev->mdev.id_table = id_table_blk; + break; + default: + IFCVF_ERR(pdev, "VIRTIO ID %u not supported\n", dev_type); + ret = -EOPNOTSUPP; + goto err; + } + + ifcvf_mgmt_dev->mdev.max_supported_vqs = vf->nr_vring; + ifcvf_mgmt_dev->mdev.supported_features = vf->hw_features; + + adapter->vdpa.mdev = &ifcvf_mgmt_dev->mdev; + + ret = vdpa_mgmtdev_register(&ifcvf_mgmt_dev->mdev); if (ret) { IFCVF_ERR(pdev, diff --git a/drivers/vdpa/mlx5/core/mlx5_vdpa.h b/drivers/vdpa/mlx5/core/mlx5_vdpa.h index 44104093163b..6af9fdbb86b7 100644 --- a/drivers/vdpa/mlx5/core/mlx5_vdpa.h +++ b/drivers/vdpa/mlx5/core/mlx5_vdpa.h @@ -70,6 +70,16 @@ struct mlx5_vdpa_wq_ent { struct mlx5_vdpa_dev *mvdev; }; +enum { + MLX5_VDPA_DATAVQ_GROUP, + MLX5_VDPA_CVQ_GROUP, + MLX5_VDPA_NUMVQ_GROUPS +}; + +enum { + MLX5_VDPA_NUM_AS = MLX5_VDPA_NUMVQ_GROUPS +}; + struct mlx5_vdpa_dev { struct vdpa_device vdev; struct mlx5_core_dev *mdev; @@ -85,6 +95,7 @@ struct mlx5_vdpa_dev { struct mlx5_vdpa_mr mr; struct mlx5_control_vq cvq; struct workqueue_struct *wq; + unsigned int group2asid[MLX5_VDPA_NUMVQ_GROUPS]; }; int mlx5_vdpa_alloc_pd(struct mlx5_vdpa_dev *dev, u32 *pdn, u16 uid); diff --git a/drivers/vdpa/mlx5/net/mlx5_vnet.c b/drivers/vdpa/mlx5/net/mlx5_vnet.c index e85c1d71f4ed..ed100a35e596 100644 --- a/drivers/vdpa/mlx5/net/mlx5_vnet.c +++ b/drivers/vdpa/mlx5/net/mlx5_vnet.c @@ -164,6 +164,7 @@ struct mlx5_vdpa_net { bool setup; u32 cur_num_vqs; u32 rqt_size; + bool nb_registered; struct notifier_block nb; struct vdpa_callback config_cb; struct mlx5_vdpa_wq_ent cvq_ent; @@ -895,6 +896,7 @@ static int create_virtqueue(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtque if (err) goto err_cmd; + mvq->fw_state = MLX5_VIRTIO_NET_Q_OBJECT_STATE_INIT; kfree(in); mvq->virtq_id = MLX5_GET(general_obj_out_cmd_hdr, out, obj_id); @@ -922,6 +924,7 @@ static void destroy_virtqueue(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtq mlx5_vdpa_warn(&ndev->mvdev, "destroy virtqueue 0x%x\n", mvq->virtq_id); return; } + mvq->fw_state = MLX5_VIRTIO_NET_Q_OBJECT_NONE; umems_destroy(ndev, mvq); } @@ -1121,6 +1124,20 @@ err_cmd: return err; } +static bool is_valid_state_change(int oldstate, int newstate) +{ + switch (oldstate) { + case MLX5_VIRTIO_NET_Q_OBJECT_STATE_INIT: + return newstate == MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY; + case MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY: + return newstate == MLX5_VIRTIO_NET_Q_OBJECT_STATE_SUSPEND; + case MLX5_VIRTIO_NET_Q_OBJECT_STATE_SUSPEND: + case MLX5_VIRTIO_NET_Q_OBJECT_STATE_ERR: + default: + return false; + } +} + static int modify_virtqueue(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq, int state) { int inlen = MLX5_ST_SZ_BYTES(modify_virtio_net_q_in); @@ -1130,6 +1147,12 @@ static int modify_virtqueue(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtque void *in; int err; + if (mvq->fw_state == MLX5_VIRTIO_NET_Q_OBJECT_NONE) + return 0; + + if (!is_valid_state_change(mvq->fw_state, state)) + return -EINVAL; + in = kzalloc(inlen, GFP_KERNEL); if (!in) return -ENOMEM; @@ -1440,7 +1463,7 @@ static int mlx5_vdpa_add_mac_vlan_rules(struct mlx5_vdpa_net *ndev, u8 *mac, headers_v = MLX5_ADDR_OF(fte_match_param, spec->match_value, outer_headers); dmac_c = MLX5_ADDR_OF(fte_match_param, headers_c, outer_headers.dmac_47_16); dmac_v = MLX5_ADDR_OF(fte_match_param, headers_v, outer_headers.dmac_47_16); - memset(dmac_c, 0xff, ETH_ALEN); + eth_broadcast_addr(dmac_c); ether_addr_copy(dmac_v, mac); MLX5_SET(fte_match_set_lyr_2_4, headers_c, cvlan_tag, 1); if (tagged) { @@ -1992,6 +2015,7 @@ static void mlx5_vdpa_set_vq_ready(struct vdpa_device *vdev, u16 idx, bool ready struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev); struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev); struct mlx5_vdpa_virtqueue *mvq; + int err; if (!mvdev->actual_features) return; @@ -2005,8 +2029,16 @@ static void mlx5_vdpa_set_vq_ready(struct vdpa_device *vdev, u16 idx, bool ready } mvq = &ndev->vqs[idx]; - if (!ready) + if (!ready) { suspend_vq(ndev, mvq); + } else { + err = modify_virtqueue(ndev, mvq, MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY); + if (err) { + mlx5_vdpa_warn(mvdev, "modify VQ %d to ready failed (%d)\n", idx, err); + ready = false; + } + } + mvq->ready = ready; } @@ -2095,9 +2127,14 @@ static u32 mlx5_vdpa_get_vq_align(struct vdpa_device *vdev) return PAGE_SIZE; } -static u32 mlx5_vdpa_get_vq_group(struct vdpa_device *vdpa, u16 idx) +static u32 mlx5_vdpa_get_vq_group(struct vdpa_device *vdev, u16 idx) { - return 0; + struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev); + + if (is_ctrl_vq_idx(mvdev, idx)) + return MLX5_VDPA_CVQ_GROUP; + + return MLX5_VDPA_DATAVQ_GROUP; } enum { MLX5_VIRTIO_NET_F_GUEST_CSUM = 1 << 9, @@ -2511,6 +2548,15 @@ err_clear: up_write(&ndev->reslock); } +static void init_group_to_asid_map(struct mlx5_vdpa_dev *mvdev) +{ + int i; + + /* default mapping all groups are mapped to asid 0 */ + for (i = 0; i < MLX5_VDPA_NUMVQ_GROUPS; i++) + mvdev->group2asid[i] = 0; +} + static int mlx5_vdpa_reset(struct vdpa_device *vdev) { struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev); @@ -2529,7 +2575,9 @@ static int mlx5_vdpa_reset(struct vdpa_device *vdev) ndev->mvdev.cvq.completed_desc = 0; memset(ndev->event_cbs, 0, sizeof(*ndev->event_cbs) * (mvdev->max_vqs + 1)); ndev->mvdev.actual_features = 0; + init_group_to_asid_map(mvdev); ++mvdev->generation; + if (MLX5_CAP_GEN(mvdev->mdev, umem_uid_0)) { if (mlx5_vdpa_create_mr(mvdev, NULL)) mlx5_vdpa_warn(mvdev, "create MR failed\n"); @@ -2567,26 +2615,63 @@ static u32 mlx5_vdpa_get_generation(struct vdpa_device *vdev) return mvdev->generation; } -static int mlx5_vdpa_set_map(struct vdpa_device *vdev, unsigned int asid, - struct vhost_iotlb *iotlb) +static int set_map_control(struct mlx5_vdpa_dev *mvdev, struct vhost_iotlb *iotlb) +{ + u64 start = 0ULL, last = 0ULL - 1; + struct vhost_iotlb_map *map; + int err = 0; + + spin_lock(&mvdev->cvq.iommu_lock); + vhost_iotlb_reset(mvdev->cvq.iotlb); + + for (map = vhost_iotlb_itree_first(iotlb, start, last); map; + map = vhost_iotlb_itree_next(map, start, last)) { + err = vhost_iotlb_add_range(mvdev->cvq.iotlb, map->start, + map->last, map->addr, map->perm); + if (err) + goto out; + } + +out: + spin_unlock(&mvdev->cvq.iommu_lock); + return err; +} + +static int set_map_data(struct mlx5_vdpa_dev *mvdev, struct vhost_iotlb *iotlb) { - struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev); - struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev); bool change_map; int err; - down_write(&ndev->reslock); - err = mlx5_vdpa_handle_set_map(mvdev, iotlb, &change_map); if (err) { mlx5_vdpa_warn(mvdev, "set map failed(%d)\n", err); - goto err; + return err; } if (change_map) err = mlx5_vdpa_change_map(mvdev, iotlb); -err: + return err; +} + +static int mlx5_vdpa_set_map(struct vdpa_device *vdev, unsigned int asid, + struct vhost_iotlb *iotlb) +{ + struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev); + struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev); + int err = -EINVAL; + + down_write(&ndev->reslock); + if (mvdev->group2asid[MLX5_VDPA_DATAVQ_GROUP] == asid) { + err = set_map_data(mvdev, iotlb); + if (err) + goto out; + } + + if (mvdev->group2asid[MLX5_VDPA_CVQ_GROUP] == asid) + err = set_map_control(mvdev, iotlb); + +out: up_write(&ndev->reslock); return err; } @@ -2733,6 +2818,49 @@ out_err: return err; } +static void mlx5_vdpa_cvq_suspend(struct mlx5_vdpa_dev *mvdev) +{ + struct mlx5_control_vq *cvq; + + if (!(mvdev->actual_features & BIT_ULL(VIRTIO_NET_F_CTRL_VQ))) + return; + + cvq = &mvdev->cvq; + cvq->ready = false; +} + +static int mlx5_vdpa_suspend(struct vdpa_device *vdev) +{ + struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev); + struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev); + struct mlx5_vdpa_virtqueue *mvq; + int i; + + down_write(&ndev->reslock); + mlx5_notifier_unregister(mvdev->mdev, &ndev->nb); + ndev->nb_registered = false; + flush_workqueue(ndev->mvdev.wq); + for (i = 0; i < ndev->cur_num_vqs; i++) { + mvq = &ndev->vqs[i]; + suspend_vq(ndev, mvq); + } + mlx5_vdpa_cvq_suspend(mvdev); + up_write(&ndev->reslock); + return 0; +} + +static int mlx5_set_group_asid(struct vdpa_device *vdev, u32 group, + unsigned int asid) +{ + struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev); + + if (group >= MLX5_VDPA_NUMVQ_GROUPS) + return -EINVAL; + + mvdev->group2asid[group] = asid; + return 0; +} + static const struct vdpa_config_ops mlx5_vdpa_ops = { .set_vq_address = mlx5_vdpa_set_vq_address, .set_vq_num = mlx5_vdpa_set_vq_num, @@ -2762,7 +2890,9 @@ static const struct vdpa_config_ops mlx5_vdpa_ops = { .set_config = mlx5_vdpa_set_config, .get_generation = mlx5_vdpa_get_generation, .set_map = mlx5_vdpa_set_map, + .set_group_asid = mlx5_set_group_asid, .free = mlx5_vdpa_free, + .suspend = mlx5_vdpa_suspend, }; static int query_mtu(struct mlx5_core_dev *mdev, u16 *mtu) @@ -2828,6 +2958,7 @@ static void init_mvqs(struct mlx5_vdpa_net *ndev) mvq->index = i; mvq->ndev = ndev; mvq->fwqp.fw = true; + mvq->fw_state = MLX5_VIRTIO_NET_Q_OBJECT_NONE; } for (; i < ndev->mvdev.max_vqs; i++) { mvq = &ndev->vqs[i]; @@ -2902,13 +3033,21 @@ static int event_handler(struct notifier_block *nb, unsigned long event, void *p switch (eqe->sub_type) { case MLX5_PORT_CHANGE_SUBTYPE_DOWN: case MLX5_PORT_CHANGE_SUBTYPE_ACTIVE: + down_read(&ndev->reslock); + if (!ndev->nb_registered) { + up_read(&ndev->reslock); + return NOTIFY_DONE; + } wqent = kzalloc(sizeof(*wqent), GFP_ATOMIC); - if (!wqent) + if (!wqent) { + up_read(&ndev->reslock); return NOTIFY_DONE; + } wqent->mvdev = &ndev->mvdev; INIT_WORK(&wqent->work, update_carrier); queue_work(ndev->mvdev.wq, &wqent->work); + up_read(&ndev->reslock); ret = NOTIFY_OK; break; default: @@ -2982,7 +3121,7 @@ static int mlx5_vdpa_dev_add(struct vdpa_mgmt_dev *v_mdev, const char *name, } ndev = vdpa_alloc_device(struct mlx5_vdpa_net, mvdev.vdev, mdev->device, &mlx5_vdpa_ops, - 1, 1, name, false); + MLX5_VDPA_NUMVQ_GROUPS, MLX5_VDPA_NUM_AS, name, false); if (IS_ERR(ndev)) return PTR_ERR(ndev); @@ -3062,6 +3201,7 @@ static int mlx5_vdpa_dev_add(struct vdpa_mgmt_dev *v_mdev, const char *name, ndev->nb.notifier_call = event_handler; mlx5_notifier_register(mdev, &ndev->nb); + ndev->nb_registered = true; mvdev->vdev.mdev = &mgtdev->mgtdev; err = _vdpa_register_device(&mvdev->vdev, max_vqs + 1); if (err) @@ -3093,7 +3233,10 @@ static void mlx5_vdpa_dev_del(struct vdpa_mgmt_dev *v_mdev, struct vdpa_device * struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev); struct workqueue_struct *wq; - mlx5_notifier_unregister(mvdev->mdev, &ndev->nb); + if (ndev->nb_registered) { + mlx5_notifier_unregister(mvdev->mdev, &ndev->nb); + ndev->nb_registered = false; + } wq = mvdev->wq; mvdev->wq = NULL; destroy_workqueue(wq); diff --git a/drivers/vdpa/vdpa.c b/drivers/vdpa/vdpa.c index ebf2f363fbe7..c06c02704461 100644 --- a/drivers/vdpa/vdpa.c +++ b/drivers/vdpa/vdpa.c @@ -824,11 +824,11 @@ static int vdpa_dev_net_config_fill(struct vdpa_device *vdev, struct sk_buff *ms config.mac)) return -EMSGSIZE; - val_u16 = le16_to_cpu(config.status); + val_u16 = __virtio16_to_cpu(true, config.status); if (nla_put_u16(msg, VDPA_ATTR_DEV_NET_STATUS, val_u16)) return -EMSGSIZE; - val_u16 = le16_to_cpu(config.mtu); + val_u16 = __virtio16_to_cpu(true, config.mtu); if (nla_put_u16(msg, VDPA_ATTR_DEV_NET_CFG_MTU, val_u16)) return -EMSGSIZE; @@ -846,17 +846,9 @@ vdpa_dev_config_fill(struct vdpa_device *vdev, struct sk_buff *msg, u32 portid, { u32 device_id; void *hdr; - u8 status; int err; down_read(&vdev->cf_lock); - status = vdev->config->get_status(vdev); - if (!(status & VIRTIO_CONFIG_S_FEATURES_OK)) { - NL_SET_ERR_MSG_MOD(extack, "Features negotiation not completed"); - err = -EAGAIN; - goto out; - } - hdr = genlmsg_put(msg, portid, seq, &vdpa_nl_family, flags, VDPA_CMD_DEV_CONFIG_GET); if (!hdr) { @@ -913,7 +905,7 @@ static int vdpa_fill_stats_rec(struct vdpa_device *vdev, struct sk_buff *msg, } vdpa_get_config_unlocked(vdev, 0, &config, sizeof(config)); - max_vqp = le16_to_cpu(config.max_virtqueue_pairs); + max_vqp = __virtio16_to_cpu(true, config.max_virtqueue_pairs); if (nla_put_u16(msg, VDPA_ATTR_DEV_NET_CFG_MAX_VQP, max_vqp)) return -EMSGSIZE; diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim.c b/drivers/vdpa/vdpa_sim/vdpa_sim.c index 0f2865899647..225b7f5d8be3 100644 --- a/drivers/vdpa/vdpa_sim/vdpa_sim.c +++ b/drivers/vdpa/vdpa_sim/vdpa_sim.c @@ -33,7 +33,7 @@ MODULE_PARM_DESC(batch_mapping, "Batched mapping 1 -Enable; 0 - Disable"); static int max_iotlb_entries = 2048; module_param(max_iotlb_entries, int, 0444); MODULE_PARM_DESC(max_iotlb_entries, - "Maximum number of iotlb entries. 0 means unlimited. (default: 2048)"); + "Maximum number of iotlb entries for each address space. 0 means unlimited. (default: 2048)"); #define VDPASIM_QUEUE_ALIGN PAGE_SIZE #define VDPASIM_QUEUE_MAX 256 @@ -107,6 +107,7 @@ static void vdpasim_do_reset(struct vdpasim *vdpasim) for (i = 0; i < vdpasim->dev_attr.nas; i++) vhost_iotlb_reset(&vdpasim->iommu[i]); + vdpasim->running = true; spin_unlock(&vdpasim->iommu_lock); vdpasim->features = 0; @@ -291,7 +292,7 @@ struct vdpasim *vdpasim_create(struct vdpasim_dev_attr *dev_attr) goto err_iommu; for (i = 0; i < vdpasim->dev_attr.nas; i++) - vhost_iotlb_init(&vdpasim->iommu[i], 0, 0); + vhost_iotlb_init(&vdpasim->iommu[i], max_iotlb_entries, 0); vdpasim->buffer = kvmalloc(dev_attr->buffer_size, GFP_KERNEL); if (!vdpasim->buffer) @@ -505,6 +506,17 @@ static int vdpasim_reset(struct vdpa_device *vdpa) return 0; } +static int vdpasim_suspend(struct vdpa_device *vdpa) +{ + struct vdpasim *vdpasim = vdpa_to_sim(vdpa); + + spin_lock(&vdpasim->lock); + vdpasim->running = false; + spin_unlock(&vdpasim->lock); + + return 0; +} + static size_t vdpasim_get_config_size(struct vdpa_device *vdpa) { struct vdpasim *vdpasim = vdpa_to_sim(vdpa); @@ -694,6 +706,7 @@ static const struct vdpa_config_ops vdpasim_config_ops = { .get_status = vdpasim_get_status, .set_status = vdpasim_set_status, .reset = vdpasim_reset, + .suspend = vdpasim_suspend, .get_config_size = vdpasim_get_config_size, .get_config = vdpasim_get_config, .set_config = vdpasim_set_config, @@ -726,6 +739,7 @@ static const struct vdpa_config_ops vdpasim_batch_config_ops = { .get_status = vdpasim_get_status, .set_status = vdpasim_set_status, .reset = vdpasim_reset, + .suspend = vdpasim_suspend, .get_config_size = vdpasim_get_config_size, .get_config = vdpasim_get_config, .set_config = vdpasim_set_config, diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim.h b/drivers/vdpa/vdpa_sim/vdpa_sim.h index 622782e92239..061986f30911 100644 --- a/drivers/vdpa/vdpa_sim/vdpa_sim.h +++ b/drivers/vdpa/vdpa_sim/vdpa_sim.h @@ -66,6 +66,7 @@ struct vdpasim { u32 generation; u64 features; u32 groups; + bool running; /* spinlock to synchronize iommu table */ spinlock_t iommu_lock; }; diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c b/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c index 42d401d43911..c8bfea3b7db2 100644 --- a/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c +++ b/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c @@ -25,31 +25,49 @@ #define DRV_LICENSE "GPL v2" #define VDPASIM_BLK_FEATURES (VDPASIM_FEATURES | \ + (1ULL << VIRTIO_BLK_F_FLUSH) | \ (1ULL << VIRTIO_BLK_F_SIZE_MAX) | \ (1ULL << VIRTIO_BLK_F_SEG_MAX) | \ (1ULL << VIRTIO_BLK_F_BLK_SIZE) | \ (1ULL << VIRTIO_BLK_F_TOPOLOGY) | \ - (1ULL << VIRTIO_BLK_F_MQ)) + (1ULL << VIRTIO_BLK_F_MQ) | \ + (1ULL << VIRTIO_BLK_F_DISCARD) | \ + (1ULL << VIRTIO_BLK_F_WRITE_ZEROES)) #define VDPASIM_BLK_CAPACITY 0x40000 #define VDPASIM_BLK_SIZE_MAX 0x1000 #define VDPASIM_BLK_SEG_MAX 32 +#define VDPASIM_BLK_DWZ_MAX_SECTORS UINT_MAX + +/* 1 virtqueue, 1 address space, 1 virtqueue group */ #define VDPASIM_BLK_VQ_NUM 1 +#define VDPASIM_BLK_AS_NUM 1 +#define VDPASIM_BLK_GROUP_NUM 1 static char vdpasim_blk_id[VIRTIO_BLK_ID_BYTES] = "vdpa_blk_sim"; -static bool vdpasim_blk_check_range(u64 start_sector, size_t range_size) +static bool vdpasim_blk_check_range(struct vdpasim *vdpasim, u64 start_sector, + u64 num_sectors, u64 max_sectors) { - u64 range_sectors = range_size >> SECTOR_SHIFT; - - if (range_size > VDPASIM_BLK_SIZE_MAX * VDPASIM_BLK_SEG_MAX) - return false; + if (start_sector > VDPASIM_BLK_CAPACITY) { + dev_dbg(&vdpasim->vdpa.dev, + "starting sector exceeds the capacity - start: 0x%llx capacity: 0x%x\n", + start_sector, VDPASIM_BLK_CAPACITY); + } - if (start_sector > VDPASIM_BLK_CAPACITY) + if (num_sectors > max_sectors) { + dev_dbg(&vdpasim->vdpa.dev, + "number of sectors exceeds the max allowed in a request - num: 0x%llx max: 0x%llx\n", + num_sectors, max_sectors); return false; + } - if (range_sectors > VDPASIM_BLK_CAPACITY - start_sector) + if (num_sectors > VDPASIM_BLK_CAPACITY - start_sector) { + dev_dbg(&vdpasim->vdpa.dev, + "request exceeds the capacity - start: 0x%llx num: 0x%llx capacity: 0x%x\n", + start_sector, num_sectors, VDPASIM_BLK_CAPACITY); return false; + } return true; } @@ -63,6 +81,7 @@ static bool vdpasim_blk_handle_req(struct vdpasim *vdpasim, { size_t pushed = 0, to_pull, to_push; struct virtio_blk_outhdr hdr; + bool handled = false; ssize_t bytes; loff_t offset; u64 sector; @@ -76,14 +95,14 @@ static bool vdpasim_blk_handle_req(struct vdpasim *vdpasim, return false; if (vq->out_iov.used < 1 || vq->in_iov.used < 1) { - dev_err(&vdpasim->vdpa.dev, "missing headers - out_iov: %u in_iov %u\n", + dev_dbg(&vdpasim->vdpa.dev, "missing headers - out_iov: %u in_iov %u\n", vq->out_iov.used, vq->in_iov.used); - return false; + goto err; } if (vq->in_iov.iov[vq->in_iov.used - 1].iov_len < 1) { - dev_err(&vdpasim->vdpa.dev, "request in header too short\n"); - return false; + dev_dbg(&vdpasim->vdpa.dev, "request in header too short\n"); + goto err; } /* The last byte is the status and we checked if the last iov has @@ -96,8 +115,8 @@ static bool vdpasim_blk_handle_req(struct vdpasim *vdpasim, bytes = vringh_iov_pull_iotlb(&vq->vring, &vq->out_iov, &hdr, sizeof(hdr)); if (bytes != sizeof(hdr)) { - dev_err(&vdpasim->vdpa.dev, "request out header too short\n"); - return false; + dev_dbg(&vdpasim->vdpa.dev, "request out header too short\n"); + goto err; } to_pull -= bytes; @@ -107,12 +126,20 @@ static bool vdpasim_blk_handle_req(struct vdpasim *vdpasim, offset = sector << SECTOR_SHIFT; status = VIRTIO_BLK_S_OK; + if (type != VIRTIO_BLK_T_IN && type != VIRTIO_BLK_T_OUT && + sector != 0) { + dev_dbg(&vdpasim->vdpa.dev, + "sector must be 0 for %u request - sector: 0x%llx\n", + type, sector); + status = VIRTIO_BLK_S_IOERR; + goto err_status; + } + switch (type) { case VIRTIO_BLK_T_IN: - if (!vdpasim_blk_check_range(sector, to_push)) { - dev_err(&vdpasim->vdpa.dev, - "reading over the capacity - offset: 0x%llx len: 0x%zx\n", - offset, to_push); + if (!vdpasim_blk_check_range(vdpasim, sector, + to_push >> SECTOR_SHIFT, + VDPASIM_BLK_SIZE_MAX * VDPASIM_BLK_SEG_MAX)) { status = VIRTIO_BLK_S_IOERR; break; } @@ -121,7 +148,7 @@ static bool vdpasim_blk_handle_req(struct vdpasim *vdpasim, vdpasim->buffer + offset, to_push); if (bytes < 0) { - dev_err(&vdpasim->vdpa.dev, + dev_dbg(&vdpasim->vdpa.dev, "vringh_iov_push_iotlb() error: %zd offset: 0x%llx len: 0x%zx\n", bytes, offset, to_push); status = VIRTIO_BLK_S_IOERR; @@ -132,10 +159,9 @@ static bool vdpasim_blk_handle_req(struct vdpasim *vdpasim, break; case VIRTIO_BLK_T_OUT: - if (!vdpasim_blk_check_range(sector, to_pull)) { - dev_err(&vdpasim->vdpa.dev, - "writing over the capacity - offset: 0x%llx len: 0x%zx\n", - offset, to_pull); + if (!vdpasim_blk_check_range(vdpasim, sector, + to_pull >> SECTOR_SHIFT, + VDPASIM_BLK_SIZE_MAX * VDPASIM_BLK_SEG_MAX)) { status = VIRTIO_BLK_S_IOERR; break; } @@ -144,7 +170,7 @@ static bool vdpasim_blk_handle_req(struct vdpasim *vdpasim, vdpasim->buffer + offset, to_pull); if (bytes < 0) { - dev_err(&vdpasim->vdpa.dev, + dev_dbg(&vdpasim->vdpa.dev, "vringh_iov_pull_iotlb() error: %zd offset: 0x%llx len: 0x%zx\n", bytes, offset, to_pull); status = VIRTIO_BLK_S_IOERR; @@ -157,7 +183,7 @@ static bool vdpasim_blk_handle_req(struct vdpasim *vdpasim, vdpasim_blk_id, VIRTIO_BLK_ID_BYTES); if (bytes < 0) { - dev_err(&vdpasim->vdpa.dev, + dev_dbg(&vdpasim->vdpa.dev, "vringh_iov_push_iotlb() error: %zd\n", bytes); status = VIRTIO_BLK_S_IOERR; break; @@ -166,13 +192,76 @@ static bool vdpasim_blk_handle_req(struct vdpasim *vdpasim, pushed += bytes; break; + case VIRTIO_BLK_T_FLUSH: + /* nothing to do */ + break; + + case VIRTIO_BLK_T_DISCARD: + case VIRTIO_BLK_T_WRITE_ZEROES: { + struct virtio_blk_discard_write_zeroes range; + u32 num_sectors, flags; + + if (to_pull != sizeof(range)) { + dev_dbg(&vdpasim->vdpa.dev, + "discard/write_zeroes header len: 0x%zx [expected: 0x%zx]\n", + to_pull, sizeof(range)); + status = VIRTIO_BLK_S_IOERR; + break; + } + + bytes = vringh_iov_pull_iotlb(&vq->vring, &vq->out_iov, &range, + to_pull); + if (bytes < 0) { + dev_dbg(&vdpasim->vdpa.dev, + "vringh_iov_pull_iotlb() error: %zd offset: 0x%llx len: 0x%zx\n", + bytes, offset, to_pull); + status = VIRTIO_BLK_S_IOERR; + break; + } + + sector = le64_to_cpu(range.sector); + offset = sector << SECTOR_SHIFT; + num_sectors = le32_to_cpu(range.num_sectors); + flags = le32_to_cpu(range.flags); + + if (type == VIRTIO_BLK_T_DISCARD && flags != 0) { + dev_dbg(&vdpasim->vdpa.dev, + "discard unexpected flags set - flags: 0x%x\n", + flags); + status = VIRTIO_BLK_S_UNSUPP; + break; + } + + if (type == VIRTIO_BLK_T_WRITE_ZEROES && + flags & ~VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP) { + dev_dbg(&vdpasim->vdpa.dev, + "write_zeroes unexpected flags set - flags: 0x%x\n", + flags); + status = VIRTIO_BLK_S_UNSUPP; + break; + } + + if (!vdpasim_blk_check_range(vdpasim, sector, num_sectors, + VDPASIM_BLK_DWZ_MAX_SECTORS)) { + status = VIRTIO_BLK_S_IOERR; + break; + } + + if (type == VIRTIO_BLK_T_WRITE_ZEROES) { + memset(vdpasim->buffer + offset, 0, + num_sectors << SECTOR_SHIFT); + } + + break; + } default: - dev_warn(&vdpasim->vdpa.dev, - "Unsupported request type %d\n", type); + dev_dbg(&vdpasim->vdpa.dev, + "Unsupported request type %d\n", type); status = VIRTIO_BLK_S_IOERR; break; } +err_status: /* If some operations fail, we need to skip the remaining bytes * to put the status in the last byte */ @@ -182,21 +271,25 @@ static bool vdpasim_blk_handle_req(struct vdpasim *vdpasim, /* Last byte is the status */ bytes = vringh_iov_push_iotlb(&vq->vring, &vq->in_iov, &status, 1); if (bytes != 1) - return false; + goto err; pushed += bytes; /* Make sure data is wrote before advancing index */ smp_wmb(); + handled = true; + +err: vringh_complete_iotlb(&vq->vring, vq->head, pushed); - return true; + return handled; } static void vdpasim_blk_work(struct work_struct *work) { struct vdpasim *vdpasim = container_of(work, struct vdpasim, work); + bool reschedule = false; int i; spin_lock(&vdpasim->lock); @@ -204,8 +297,12 @@ static void vdpasim_blk_work(struct work_struct *work) if (!(vdpasim->status & VIRTIO_CONFIG_S_DRIVER_OK)) goto out; + if (!vdpasim->running) + goto out; + for (i = 0; i < VDPASIM_BLK_VQ_NUM; i++) { struct vdpasim_virtqueue *vq = &vdpasim->vqs[i]; + int reqs = 0; if (!vq->ready) continue; @@ -218,10 +315,18 @@ static void vdpasim_blk_work(struct work_struct *work) if (vringh_need_notify_iotlb(&vq->vring) > 0) vringh_notify(&vq->vring); local_bh_enable(); + + if (++reqs > 4) { + reschedule = true; + break; + } } } out: spin_unlock(&vdpasim->lock); + + if (reschedule) + schedule_work(&vdpasim->work); } static void vdpasim_blk_get_config(struct vdpasim *vdpasim, void *config) @@ -237,6 +342,17 @@ static void vdpasim_blk_get_config(struct vdpasim *vdpasim, void *config) blk_config->min_io_size = cpu_to_vdpasim16(vdpasim, 1); blk_config->opt_io_size = cpu_to_vdpasim32(vdpasim, 1); blk_config->blk_size = cpu_to_vdpasim32(vdpasim, SECTOR_SIZE); + /* VIRTIO_BLK_F_DISCARD */ + blk_config->discard_sector_alignment = + cpu_to_vdpasim32(vdpasim, SECTOR_SIZE); + blk_config->max_discard_sectors = + cpu_to_vdpasim32(vdpasim, VDPASIM_BLK_DWZ_MAX_SECTORS); + blk_config->max_discard_seg = cpu_to_vdpasim32(vdpasim, 1); + /* VIRTIO_BLK_F_WRITE_ZEROES */ + blk_config->max_write_zeroes_sectors = + cpu_to_vdpasim32(vdpasim, VDPASIM_BLK_DWZ_MAX_SECTORS); + blk_config->max_write_zeroes_seg = cpu_to_vdpasim32(vdpasim, 1); + } static void vdpasim_blk_mgmtdev_release(struct device *dev) @@ -260,6 +376,8 @@ static int vdpasim_blk_dev_add(struct vdpa_mgmt_dev *mdev, const char *name, dev_attr.id = VIRTIO_ID_BLOCK; dev_attr.supported_features = VDPASIM_BLK_FEATURES; dev_attr.nvqs = VDPASIM_BLK_VQ_NUM; + dev_attr.ngroups = VDPASIM_BLK_GROUP_NUM; + dev_attr.nas = VDPASIM_BLK_AS_NUM; dev_attr.config_size = sizeof(struct virtio_blk_config); dev_attr.get_config = vdpasim_blk_get_config; dev_attr.work_fn = vdpasim_blk_work; diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim_net.c b/drivers/vdpa/vdpa_sim/vdpa_sim_net.c index 5125976a4df8..886449e88502 100644 --- a/drivers/vdpa/vdpa_sim/vdpa_sim_net.c +++ b/drivers/vdpa/vdpa_sim/vdpa_sim_net.c @@ -154,6 +154,9 @@ static void vdpasim_net_work(struct work_struct *work) spin_lock(&vdpasim->lock); + if (!vdpasim->running) + goto out; + if (!(vdpasim->status & VIRTIO_CONFIG_S_DRIVER_OK)) goto out; diff --git a/drivers/vdpa/vdpa_user/iova_domain.c b/drivers/vdpa/vdpa_user/iova_domain.c index 6daa3978d290..e682bc7ee6c9 100644 --- a/drivers/vdpa/vdpa_user/iova_domain.c +++ b/drivers/vdpa/vdpa_user/iova_domain.c @@ -138,18 +138,17 @@ static void do_bounce(phys_addr_t orig, void *addr, size_t size, { unsigned long pfn = PFN_DOWN(orig); unsigned int offset = offset_in_page(orig); - char *buffer; + struct page *page; unsigned int sz = 0; while (size) { sz = min_t(size_t, PAGE_SIZE - offset, size); - buffer = kmap_atomic(pfn_to_page(pfn)); + page = pfn_to_page(pfn); if (dir == DMA_TO_DEVICE) - memcpy(addr, buffer + offset, sz); + memcpy_from_page(addr, page, offset, sz); else - memcpy(buffer + offset, addr, sz); - kunmap_atomic(buffer); + memcpy_to_page(page, offset, addr, sz); size -= sz; pfn++; @@ -179,8 +178,9 @@ static void vduse_domain_bounce(struct vduse_iova_domain *domain, map->orig_phys == INVALID_PHYS_ADDR)) return; - addr = page_address(map->bounce_page) + offset; - do_bounce(map->orig_phys + offset, addr, sz, dir); + addr = kmap_local_page(map->bounce_page); + do_bounce(map->orig_phys + offset, addr + offset, sz, dir); + kunmap_local(addr); size -= sz; iova += sz; } @@ -213,21 +213,21 @@ vduse_domain_get_bounce_page(struct vduse_iova_domain *domain, u64 iova) struct vduse_bounce_map *map; struct page *page = NULL; - spin_lock(&domain->iotlb_lock); + read_lock(&domain->bounce_lock); map = &domain->bounce_maps[iova >> PAGE_SHIFT]; - if (!map->bounce_page) + if (domain->user_bounce_pages || !map->bounce_page) goto out; page = map->bounce_page; get_page(page); out: - spin_unlock(&domain->iotlb_lock); + read_unlock(&domain->bounce_lock); return page; } static void -vduse_domain_free_bounce_pages(struct vduse_iova_domain *domain) +vduse_domain_free_kernel_bounce_pages(struct vduse_iova_domain *domain) { struct vduse_bounce_map *map; unsigned long pfn, bounce_pfns; @@ -247,6 +247,73 @@ vduse_domain_free_bounce_pages(struct vduse_iova_domain *domain) } } +int vduse_domain_add_user_bounce_pages(struct vduse_iova_domain *domain, + struct page **pages, int count) +{ + struct vduse_bounce_map *map; + int i, ret; + + /* Now we don't support partial mapping */ + if (count != (domain->bounce_size >> PAGE_SHIFT)) + return -EINVAL; + + write_lock(&domain->bounce_lock); + ret = -EEXIST; + if (domain->user_bounce_pages) + goto out; + + for (i = 0; i < count; i++) { + map = &domain->bounce_maps[i]; + if (map->bounce_page) { + /* Copy kernel page to user page if it's in use */ + if (map->orig_phys != INVALID_PHYS_ADDR) + memcpy_to_page(pages[i], 0, + page_address(map->bounce_page), + PAGE_SIZE); + __free_page(map->bounce_page); + } + map->bounce_page = pages[i]; + get_page(pages[i]); + } + domain->user_bounce_pages = true; + ret = 0; +out: + write_unlock(&domain->bounce_lock); + + return ret; +} + +void vduse_domain_remove_user_bounce_pages(struct vduse_iova_domain *domain) +{ + struct vduse_bounce_map *map; + unsigned long i, count; + + write_lock(&domain->bounce_lock); + if (!domain->user_bounce_pages) + goto out; + + count = domain->bounce_size >> PAGE_SHIFT; + for (i = 0; i < count; i++) { + struct page *page = NULL; + + map = &domain->bounce_maps[i]; + if (WARN_ON(!map->bounce_page)) + continue; + + /* Copy user page to kernel page if it's in use */ + if (map->orig_phys != INVALID_PHYS_ADDR) { + page = alloc_page(GFP_ATOMIC | __GFP_NOFAIL); + memcpy_from_page(page_address(page), + map->bounce_page, 0, PAGE_SIZE); + } + put_page(map->bounce_page); + map->bounce_page = page; + } + domain->user_bounce_pages = false; +out: + write_unlock(&domain->bounce_lock); +} + void vduse_domain_reset_bounce_map(struct vduse_iova_domain *domain) { if (!domain->bounce_map) @@ -322,13 +389,18 @@ dma_addr_t vduse_domain_map_page(struct vduse_iova_domain *domain, if (vduse_domain_init_bounce_map(domain)) goto err; + read_lock(&domain->bounce_lock); if (vduse_domain_map_bounce_page(domain, (u64)iova, (u64)size, pa)) - goto err; + goto err_unlock; if (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL) vduse_domain_bounce(domain, iova, size, DMA_TO_DEVICE); + read_unlock(&domain->bounce_lock); + return iova; +err_unlock: + read_unlock(&domain->bounce_lock); err: vduse_domain_free_iova(iovad, iova, size); return DMA_MAPPING_ERROR; @@ -340,10 +412,12 @@ void vduse_domain_unmap_page(struct vduse_iova_domain *domain, { struct iova_domain *iovad = &domain->stream_iovad; + read_lock(&domain->bounce_lock); if (dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL) vduse_domain_bounce(domain, dma_addr, size, DMA_FROM_DEVICE); vduse_domain_unmap_bounce_page(domain, (u64)dma_addr, (u64)size); + read_unlock(&domain->bounce_lock); vduse_domain_free_iova(iovad, dma_addr, size); } @@ -451,7 +525,8 @@ static int vduse_domain_release(struct inode *inode, struct file *file) spin_lock(&domain->iotlb_lock); vduse_iotlb_del_range(domain, 0, ULLONG_MAX); - vduse_domain_free_bounce_pages(domain); + vduse_domain_remove_user_bounce_pages(domain); + vduse_domain_free_kernel_bounce_pages(domain); spin_unlock(&domain->iotlb_lock); put_iova_domain(&domain->stream_iovad); put_iova_domain(&domain->consistent_iovad); @@ -511,6 +586,7 @@ vduse_domain_create(unsigned long iova_limit, size_t bounce_size) goto err_file; domain->file = file; + rwlock_init(&domain->bounce_lock); spin_lock_init(&domain->iotlb_lock); init_iova_domain(&domain->stream_iovad, PAGE_SIZE, IOVA_START_PFN); diff --git a/drivers/vdpa/vdpa_user/iova_domain.h b/drivers/vdpa/vdpa_user/iova_domain.h index 2722d9b8e21a..4e0e50e7ac15 100644 --- a/drivers/vdpa/vdpa_user/iova_domain.h +++ b/drivers/vdpa/vdpa_user/iova_domain.h @@ -14,6 +14,7 @@ #include <linux/iova.h> #include <linux/dma-mapping.h> #include <linux/vhost_iotlb.h> +#include <linux/rwlock.h> #define IOVA_START_PFN 1 @@ -34,6 +35,8 @@ struct vduse_iova_domain { struct vhost_iotlb *iotlb; spinlock_t iotlb_lock; struct file *file; + bool user_bounce_pages; + rwlock_t bounce_lock; }; int vduse_domain_set_map(struct vduse_iova_domain *domain, @@ -61,6 +64,11 @@ void vduse_domain_free_coherent(struct vduse_iova_domain *domain, size_t size, void vduse_domain_reset_bounce_map(struct vduse_iova_domain *domain); +int vduse_domain_add_user_bounce_pages(struct vduse_iova_domain *domain, + struct page **pages, int count); + +void vduse_domain_remove_user_bounce_pages(struct vduse_iova_domain *domain); + void vduse_domain_destroy(struct vduse_iova_domain *domain); struct vduse_iova_domain *vduse_domain_create(unsigned long iova_limit, diff --git a/drivers/vdpa/vdpa_user/vduse_dev.c b/drivers/vdpa/vdpa_user/vduse_dev.c index 3bc27de58f46..41c0b29739f1 100644 --- a/drivers/vdpa/vdpa_user/vduse_dev.c +++ b/drivers/vdpa/vdpa_user/vduse_dev.c @@ -21,6 +21,8 @@ #include <linux/uio.h> #include <linux/vdpa.h> #include <linux/nospec.h> +#include <linux/vmalloc.h> +#include <linux/sched/mm.h> #include <uapi/linux/vduse.h> #include <uapi/linux/vdpa.h> #include <uapi/linux/virtio_config.h> @@ -64,6 +66,13 @@ struct vduse_vdpa { struct vduse_dev *dev; }; +struct vduse_umem { + unsigned long iova; + unsigned long npages; + struct page **pages; + struct mm_struct *mm; +}; + struct vduse_dev { struct vduse_vdpa *vdev; struct device *dev; @@ -95,6 +104,8 @@ struct vduse_dev { u8 status; u32 vq_num; u32 vq_align; + struct vduse_umem *umem; + struct mutex mem_lock; }; struct vduse_dev_msg { @@ -917,6 +928,102 @@ unlock: return ret; } +static int vduse_dev_dereg_umem(struct vduse_dev *dev, + u64 iova, u64 size) +{ + int ret; + + mutex_lock(&dev->mem_lock); + ret = -ENOENT; + if (!dev->umem) + goto unlock; + + ret = -EINVAL; + if (dev->umem->iova != iova || size != dev->domain->bounce_size) + goto unlock; + + vduse_domain_remove_user_bounce_pages(dev->domain); + unpin_user_pages_dirty_lock(dev->umem->pages, + dev->umem->npages, true); + atomic64_sub(dev->umem->npages, &dev->umem->mm->pinned_vm); + mmdrop(dev->umem->mm); + vfree(dev->umem->pages); + kfree(dev->umem); + dev->umem = NULL; + ret = 0; +unlock: + mutex_unlock(&dev->mem_lock); + return ret; +} + +static int vduse_dev_reg_umem(struct vduse_dev *dev, + u64 iova, u64 uaddr, u64 size) +{ + struct page **page_list = NULL; + struct vduse_umem *umem = NULL; + long pinned = 0; + unsigned long npages, lock_limit; + int ret; + + if (!dev->domain->bounce_map || + size != dev->domain->bounce_size || + iova != 0 || uaddr & ~PAGE_MASK) + return -EINVAL; + + mutex_lock(&dev->mem_lock); + ret = -EEXIST; + if (dev->umem) + goto unlock; + + ret = -ENOMEM; + npages = size >> PAGE_SHIFT; + page_list = __vmalloc(array_size(npages, sizeof(struct page *)), + GFP_KERNEL_ACCOUNT); + umem = kzalloc(sizeof(*umem), GFP_KERNEL); + if (!page_list || !umem) + goto unlock; + + mmap_read_lock(current->mm); + + lock_limit = PFN_DOWN(rlimit(RLIMIT_MEMLOCK)); + if (npages + atomic64_read(¤t->mm->pinned_vm) > lock_limit) + goto out; + + pinned = pin_user_pages(uaddr, npages, FOLL_LONGTERM | FOLL_WRITE, + page_list, NULL); + if (pinned != npages) { + ret = pinned < 0 ? pinned : -ENOMEM; + goto out; + } + + ret = vduse_domain_add_user_bounce_pages(dev->domain, + page_list, pinned); + if (ret) + goto out; + + atomic64_add(npages, ¤t->mm->pinned_vm); + + umem->pages = page_list; + umem->npages = pinned; + umem->iova = iova; + umem->mm = current->mm; + mmgrab(current->mm); + + dev->umem = umem; +out: + if (ret && pinned > 0) + unpin_user_pages(page_list, pinned); + + mmap_read_unlock(current->mm); +unlock: + if (ret) { + vfree(page_list); + kfree(umem); + } + mutex_unlock(&dev->mem_lock); + return ret; +} + static long vduse_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -1089,6 +1196,77 @@ static long vduse_dev_ioctl(struct file *file, unsigned int cmd, ret = vduse_dev_queue_irq_work(dev, &dev->vqs[index].inject); break; } + case VDUSE_IOTLB_REG_UMEM: { + struct vduse_iova_umem umem; + + ret = -EFAULT; + if (copy_from_user(&umem, argp, sizeof(umem))) + break; + + ret = -EINVAL; + if (!is_mem_zero((const char *)umem.reserved, + sizeof(umem.reserved))) + break; + + ret = vduse_dev_reg_umem(dev, umem.iova, + umem.uaddr, umem.size); + break; + } + case VDUSE_IOTLB_DEREG_UMEM: { + struct vduse_iova_umem umem; + + ret = -EFAULT; + if (copy_from_user(&umem, argp, sizeof(umem))) + break; + + ret = -EINVAL; + if (!is_mem_zero((const char *)umem.reserved, + sizeof(umem.reserved))) + break; + + ret = vduse_dev_dereg_umem(dev, umem.iova, + umem.size); + break; + } + case VDUSE_IOTLB_GET_INFO: { + struct vduse_iova_info info; + struct vhost_iotlb_map *map; + struct vduse_iova_domain *domain = dev->domain; + + ret = -EFAULT; + if (copy_from_user(&info, argp, sizeof(info))) + break; + + ret = -EINVAL; + if (info.start > info.last) + break; + + if (!is_mem_zero((const char *)info.reserved, + sizeof(info.reserved))) + break; + + spin_lock(&domain->iotlb_lock); + map = vhost_iotlb_itree_first(domain->iotlb, + info.start, info.last); + if (map) { + info.start = map->start; + info.last = map->last; + info.capability = 0; + if (domain->bounce_map && map->start == 0 && + map->last == domain->bounce_size - 1) + info.capability |= VDUSE_IOVA_CAP_UMEM; + } + spin_unlock(&domain->iotlb_lock); + if (!map) + break; + + ret = -EFAULT; + if (copy_to_user(argp, &info, sizeof(info))) + break; + + ret = 0; + break; + } default: ret = -ENOIOCTLCMD; break; @@ -1101,6 +1279,7 @@ static int vduse_dev_release(struct inode *inode, struct file *file) { struct vduse_dev *dev = file->private_data; + vduse_dev_dereg_umem(dev, 0, dev->domain->bounce_size); spin_lock(&dev->msg_lock); /* Make sure the inflight messages can processed after reconncection */ list_splice_init(&dev->recv_list, &dev->send_list); @@ -1163,6 +1342,7 @@ static struct vduse_dev *vduse_dev_create(void) return NULL; mutex_init(&dev->lock); + mutex_init(&dev->mem_lock); spin_lock_init(&dev->msg_lock); INIT_LIST_HEAD(&dev->send_list); INIT_LIST_HEAD(&dev->recv_list); diff --git a/drivers/vfio/Makefile b/drivers/vfio/Makefile index fee73f3d9480..1a32357592e3 100644 --- a/drivers/vfio/Makefile +++ b/drivers/vfio/Makefile @@ -1,6 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 vfio_virqfd-y := virqfd.o +vfio-y += vfio_main.o + obj-$(CONFIG_VFIO) += vfio.o obj-$(CONFIG_VFIO_VIRQFD) += vfio_virqfd.o obj-$(CONFIG_VFIO_IOMMU_TYPE1) += vfio_iommu_type1.o diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio_main.c index 7cb56c382c97..7cb56c382c97 100644 --- a/drivers/vfio/vfio.c +++ b/drivers/vfio/vfio_main.c diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index 9b65509424dc..7ebf106d50c1 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -159,9 +159,13 @@ enum { }; #define VHOST_SCSI_MAX_TARGET 256 -#define VHOST_SCSI_MAX_VQ 128 +#define VHOST_SCSI_MAX_IO_VQ 1024 #define VHOST_SCSI_MAX_EVENT 128 +static unsigned vhost_scsi_max_io_vqs = 128; +module_param_named(max_io_vqs, vhost_scsi_max_io_vqs, uint, 0644); +MODULE_PARM_DESC(max_io_vqs, "Set the max number of IO virtqueues a vhost scsi device can support. The default is 128. The max is 1024."); + struct vhost_scsi_virtqueue { struct vhost_virtqueue vq; /* @@ -186,7 +190,9 @@ struct vhost_scsi { char vs_vhost_wwpn[TRANSPORT_IQN_LEN]; struct vhost_dev dev; - struct vhost_scsi_virtqueue vqs[VHOST_SCSI_MAX_VQ]; + struct vhost_scsi_virtqueue *vqs; + unsigned long *compl_bitmap; + struct vhost_scsi_inflight **old_inflight; struct vhost_work vs_completion_work; /* cmd completion work item */ struct llist_head vs_completion_list; /* cmd completion queue */ @@ -245,7 +251,7 @@ static void vhost_scsi_init_inflight(struct vhost_scsi *vs, struct vhost_virtqueue *vq; int idx, i; - for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) { + for (i = 0; i < vs->dev.nvqs; i++) { vq = &vs->vqs[i].vq; mutex_lock(&vq->mutex); @@ -533,7 +539,6 @@ static void vhost_scsi_complete_cmd_work(struct vhost_work *work) { struct vhost_scsi *vs = container_of(work, struct vhost_scsi, vs_completion_work); - DECLARE_BITMAP(signal, VHOST_SCSI_MAX_VQ); struct virtio_scsi_cmd_resp v_rsp; struct vhost_scsi_cmd *cmd, *t; struct llist_node *llnode; @@ -541,7 +546,7 @@ static void vhost_scsi_complete_cmd_work(struct vhost_work *work) struct iov_iter iov_iter; int ret, vq; - bitmap_zero(signal, VHOST_SCSI_MAX_VQ); + bitmap_zero(vs->compl_bitmap, vs->dev.nvqs); llnode = llist_del_all(&vs->vs_completion_list); llist_for_each_entry_safe(cmd, t, llnode, tvc_completion_list) { se_cmd = &cmd->tvc_se_cmd; @@ -566,7 +571,7 @@ static void vhost_scsi_complete_cmd_work(struct vhost_work *work) vhost_add_used(cmd->tvc_vq, cmd->tvc_vq_desc, 0); q = container_of(cmd->tvc_vq, struct vhost_scsi_virtqueue, vq); vq = q - vs->vqs; - __set_bit(vq, signal); + __set_bit(vq, vs->compl_bitmap); } else pr_err("Faulted on virtio_scsi_cmd_resp\n"); @@ -574,8 +579,8 @@ static void vhost_scsi_complete_cmd_work(struct vhost_work *work) } vq = -1; - while ((vq = find_next_bit(signal, VHOST_SCSI_MAX_VQ, vq + 1)) - < VHOST_SCSI_MAX_VQ) + while ((vq = find_next_bit(vs->compl_bitmap, vs->dev.nvqs, vq + 1)) + < vs->dev.nvqs) vhost_signal(&vs->dev, &vs->vqs[vq].vq); } @@ -1419,26 +1424,25 @@ static void vhost_scsi_handle_kick(struct vhost_work *work) /* Callers must hold dev mutex */ static void vhost_scsi_flush(struct vhost_scsi *vs) { - struct vhost_scsi_inflight *old_inflight[VHOST_SCSI_MAX_VQ]; int i; /* Init new inflight and remember the old inflight */ - vhost_scsi_init_inflight(vs, old_inflight); + vhost_scsi_init_inflight(vs, vs->old_inflight); /* * The inflight->kref was initialized to 1. We decrement it here to * indicate the start of the flush operation so that it will reach 0 * when all the reqs are finished. */ - for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) - kref_put(&old_inflight[i]->kref, vhost_scsi_done_inflight); + for (i = 0; i < vs->dev.nvqs; i++) + kref_put(&vs->old_inflight[i]->kref, vhost_scsi_done_inflight); /* Flush both the vhost poll and vhost work */ vhost_dev_flush(&vs->dev); /* Wait for all reqs issued before the flush to be finished */ - for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) - wait_for_completion(&old_inflight[i]->comp); + for (i = 0; i < vs->dev.nvqs; i++) + wait_for_completion(&vs->old_inflight[i]->comp); } static void vhost_scsi_destroy_vq_cmds(struct vhost_virtqueue *vq) @@ -1601,7 +1605,7 @@ vhost_scsi_set_endpoint(struct vhost_scsi *vs, memcpy(vs->vs_vhost_wwpn, t->vhost_wwpn, sizeof(vs->vs_vhost_wwpn)); - for (i = VHOST_SCSI_VQ_IO; i < VHOST_SCSI_MAX_VQ; i++) { + for (i = VHOST_SCSI_VQ_IO; i < vs->dev.nvqs; i++) { vq = &vs->vqs[i].vq; if (!vhost_vq_is_setup(vq)) continue; @@ -1611,7 +1615,7 @@ vhost_scsi_set_endpoint(struct vhost_scsi *vs, goto destroy_vq_cmds; } - for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) { + for (i = 0; i < vs->dev.nvqs; i++) { vq = &vs->vqs[i].vq; mutex_lock(&vq->mutex); vhost_vq_set_backend(vq, vs_tpg); @@ -1713,7 +1717,7 @@ vhost_scsi_clear_endpoint(struct vhost_scsi *vs, target_undepend_item(&se_tpg->tpg_group.cg_item); } if (match) { - for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) { + for (i = 0; i < vs->dev.nvqs; i++) { vq = &vs->vqs[i].vq; mutex_lock(&vq->mutex); vhost_vq_set_backend(vq, NULL); @@ -1722,7 +1726,7 @@ vhost_scsi_clear_endpoint(struct vhost_scsi *vs, /* Make sure cmds are not running before tearing them down. */ vhost_scsi_flush(vs); - for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) { + for (i = 0; i < vs->dev.nvqs; i++) { vq = &vs->vqs[i].vq; vhost_scsi_destroy_vq_cmds(vq); } @@ -1762,7 +1766,7 @@ static int vhost_scsi_set_features(struct vhost_scsi *vs, u64 features) return -EFAULT; } - for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) { + for (i = 0; i < vs->dev.nvqs; i++) { vq = &vs->vqs[i].vq; mutex_lock(&vq->mutex); vq->acked_features = features; @@ -1776,16 +1780,40 @@ static int vhost_scsi_open(struct inode *inode, struct file *f) { struct vhost_scsi *vs; struct vhost_virtqueue **vqs; - int r = -ENOMEM, i; + int r = -ENOMEM, i, nvqs = vhost_scsi_max_io_vqs; vs = kvzalloc(sizeof(*vs), GFP_KERNEL); if (!vs) goto err_vs; - vqs = kmalloc_array(VHOST_SCSI_MAX_VQ, sizeof(*vqs), GFP_KERNEL); - if (!vqs) + if (nvqs > VHOST_SCSI_MAX_IO_VQ) { + pr_err("Invalid max_io_vqs of %d. Using %d.\n", nvqs, + VHOST_SCSI_MAX_IO_VQ); + nvqs = VHOST_SCSI_MAX_IO_VQ; + } else if (nvqs == 0) { + pr_err("Invalid max_io_vqs of %d. Using 1.\n", nvqs); + nvqs = 1; + } + nvqs += VHOST_SCSI_VQ_IO; + + vs->compl_bitmap = bitmap_alloc(nvqs, GFP_KERNEL); + if (!vs->compl_bitmap) + goto err_compl_bitmap; + + vs->old_inflight = kmalloc_array(nvqs, sizeof(*vs->old_inflight), + GFP_KERNEL | __GFP_ZERO); + if (!vs->old_inflight) + goto err_inflight; + + vs->vqs = kmalloc_array(nvqs, sizeof(*vs->vqs), + GFP_KERNEL | __GFP_ZERO); + if (!vs->vqs) goto err_vqs; + vqs = kmalloc_array(nvqs, sizeof(*vqs), GFP_KERNEL); + if (!vqs) + goto err_local_vqs; + vhost_work_init(&vs->vs_completion_work, vhost_scsi_complete_cmd_work); vhost_work_init(&vs->vs_event_work, vhost_scsi_evt_work); @@ -1796,11 +1824,11 @@ static int vhost_scsi_open(struct inode *inode, struct file *f) vqs[VHOST_SCSI_VQ_EVT] = &vs->vqs[VHOST_SCSI_VQ_EVT].vq; vs->vqs[VHOST_SCSI_VQ_CTL].vq.handle_kick = vhost_scsi_ctl_handle_kick; vs->vqs[VHOST_SCSI_VQ_EVT].vq.handle_kick = vhost_scsi_evt_handle_kick; - for (i = VHOST_SCSI_VQ_IO; i < VHOST_SCSI_MAX_VQ; i++) { + for (i = VHOST_SCSI_VQ_IO; i < nvqs; i++) { vqs[i] = &vs->vqs[i].vq; vs->vqs[i].vq.handle_kick = vhost_scsi_handle_kick; } - vhost_dev_init(&vs->dev, vqs, VHOST_SCSI_MAX_VQ, UIO_MAXIOV, + vhost_dev_init(&vs->dev, vqs, nvqs, UIO_MAXIOV, VHOST_SCSI_WEIGHT, 0, true, NULL); vhost_scsi_init_inflight(vs, NULL); @@ -1808,7 +1836,13 @@ static int vhost_scsi_open(struct inode *inode, struct file *f) f->private_data = vs; return 0; +err_local_vqs: + kfree(vs->vqs); err_vqs: + kfree(vs->old_inflight); +err_inflight: + bitmap_free(vs->compl_bitmap); +err_compl_bitmap: kvfree(vs); err_vs: return r; @@ -1826,6 +1860,9 @@ static int vhost_scsi_release(struct inode *inode, struct file *f) vhost_dev_stop(&vs->dev); vhost_dev_cleanup(&vs->dev); kfree(vs->dev.vqs); + kfree(vs->vqs); + kfree(vs->old_inflight); + bitmap_free(vs->compl_bitmap); kvfree(vs); return 0; } diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c index 23dcbfdfa13b..166044642fd5 100644 --- a/drivers/vhost/vdpa.c +++ b/drivers/vhost/vdpa.c @@ -347,6 +347,14 @@ static long vhost_vdpa_set_config(struct vhost_vdpa *v, return 0; } +static bool vhost_vdpa_can_suspend(const struct vhost_vdpa *v) +{ + struct vdpa_device *vdpa = v->vdpa; + const struct vdpa_config_ops *ops = vdpa->config; + + return ops->suspend; +} + static long vhost_vdpa_get_features(struct vhost_vdpa *v, u64 __user *featurep) { struct vdpa_device *vdpa = v->vdpa; @@ -470,6 +478,22 @@ static long vhost_vdpa_get_vqs_count(struct vhost_vdpa *v, u32 __user *argp) return 0; } +/* After a successful return of ioctl the device must not process more + * virtqueue descriptors. The device can answer to read or writes of config + * fields as if it were not suspended. In particular, writing to "queue_enable" + * with a value of 1 will not make the device start processing buffers. + */ +static long vhost_vdpa_suspend(struct vhost_vdpa *v) +{ + struct vdpa_device *vdpa = v->vdpa; + const struct vdpa_config_ops *ops = vdpa->config; + + if (!ops->suspend) + return -EOPNOTSUPP; + + return ops->suspend(vdpa); +} + static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd, void __user *argp) { @@ -577,7 +601,11 @@ static long vhost_vdpa_unlocked_ioctl(struct file *filep, if (cmd == VHOST_SET_BACKEND_FEATURES) { if (copy_from_user(&features, featurep, sizeof(features))) return -EFAULT; - if (features & ~VHOST_VDPA_BACKEND_FEATURES) + if (features & ~(VHOST_VDPA_BACKEND_FEATURES | + BIT_ULL(VHOST_BACKEND_F_SUSPEND))) + return -EOPNOTSUPP; + if ((features & BIT_ULL(VHOST_BACKEND_F_SUSPEND)) && + !vhost_vdpa_can_suspend(v)) return -EOPNOTSUPP; vhost_set_backend_features(&v->vdev, features); return 0; @@ -628,6 +656,8 @@ static long vhost_vdpa_unlocked_ioctl(struct file *filep, break; case VHOST_GET_BACKEND_FEATURES: features = VHOST_VDPA_BACKEND_FEATURES; + if (vhost_vdpa_can_suspend(v)) + features |= BIT_ULL(VHOST_BACKEND_F_SUSPEND); if (copy_to_user(featurep, &features, sizeof(features))) r = -EFAULT; break; @@ -640,6 +670,9 @@ static long vhost_vdpa_unlocked_ioctl(struct file *filep, case VHOST_VDPA_GET_VQS_COUNT: r = vhost_vdpa_get_vqs_count(v, argp); break; + case VHOST_VDPA_SUSPEND: + r = vhost_vdpa_suspend(v); + break; default: r = vhost_dev_ioctl(&v->vdev, cmd, argp); if (r == -ENOIOCTLCMD) @@ -1076,7 +1109,7 @@ static int vhost_vdpa_alloc_domain(struct vhost_vdpa *v) if (!bus) return -EFAULT; - if (!iommu_capable(bus, IOMMU_CAP_CACHE_COHERENCY)) + if (!device_iommu_capable(dma_dev, IOMMU_CAP_CACHE_COHERENCY)) return -ENOTSUPP; v->domain = iommu_domain_alloc(bus); @@ -1363,6 +1396,7 @@ static int vhost_vdpa_probe(struct vdpa_device *vdpa) err: put_device(&v->dev); + ida_simple_remove(&vhost_vdpa_ida, v->minor); return r; } diff --git a/drivers/vhost/vringh.c b/drivers/vhost/vringh.c index eab55accf381..11f59dd06a74 100644 --- a/drivers/vhost/vringh.c +++ b/drivers/vhost/vringh.c @@ -1095,7 +1095,8 @@ EXPORT_SYMBOL(vringh_need_notify_kern); #if IS_REACHABLE(CONFIG_VHOST_IOTLB) static int iotlb_translate(const struct vringh *vrh, - u64 addr, u64 len, struct bio_vec iov[], + u64 addr, u64 len, u64 *translated, + struct bio_vec iov[], int iov_size, u32 perm) { struct vhost_iotlb_map *map; @@ -1136,43 +1137,76 @@ static int iotlb_translate(const struct vringh *vrh, spin_unlock(vrh->iotlb_lock); + if (translated) + *translated = min(len, s); + return ret; } static inline int copy_from_iotlb(const struct vringh *vrh, void *dst, void *src, size_t len) { - struct iov_iter iter; - struct bio_vec iov[16]; - int ret; + u64 total_translated = 0; - ret = iotlb_translate(vrh, (u64)(uintptr_t)src, - len, iov, 16, VHOST_MAP_RO); - if (ret < 0) - return ret; + while (total_translated < len) { + struct bio_vec iov[16]; + struct iov_iter iter; + u64 translated; + int ret; - iov_iter_bvec(&iter, READ, iov, ret, len); + ret = iotlb_translate(vrh, (u64)(uintptr_t)src, + len - total_translated, &translated, + iov, ARRAY_SIZE(iov), VHOST_MAP_RO); + if (ret == -ENOBUFS) + ret = ARRAY_SIZE(iov); + else if (ret < 0) + return ret; - ret = copy_from_iter(dst, len, &iter); + iov_iter_bvec(&iter, READ, iov, ret, translated); - return ret; + ret = copy_from_iter(dst, translated, &iter); + if (ret < 0) + return ret; + + src += translated; + dst += translated; + total_translated += translated; + } + + return total_translated; } static inline int copy_to_iotlb(const struct vringh *vrh, void *dst, void *src, size_t len) { - struct iov_iter iter; - struct bio_vec iov[16]; - int ret; + u64 total_translated = 0; - ret = iotlb_translate(vrh, (u64)(uintptr_t)dst, - len, iov, 16, VHOST_MAP_WO); - if (ret < 0) - return ret; + while (total_translated < len) { + struct bio_vec iov[16]; + struct iov_iter iter; + u64 translated; + int ret; + + ret = iotlb_translate(vrh, (u64)(uintptr_t)dst, + len - total_translated, &translated, + iov, ARRAY_SIZE(iov), VHOST_MAP_WO); + if (ret == -ENOBUFS) + ret = ARRAY_SIZE(iov); + else if (ret < 0) + return ret; - iov_iter_bvec(&iter, WRITE, iov, ret, len); + iov_iter_bvec(&iter, WRITE, iov, ret, translated); + + ret = copy_to_iter(src, translated, &iter); + if (ret < 0) + return ret; + + src += translated; + dst += translated; + total_translated += translated; + } - return copy_to_iter(src, len, &iter); + return total_translated; } static inline int getu16_iotlb(const struct vringh *vrh, @@ -1183,7 +1217,7 @@ static inline int getu16_iotlb(const struct vringh *vrh, int ret; /* Atomic read is needed for getu16 */ - ret = iotlb_translate(vrh, (u64)(uintptr_t)p, sizeof(*p), + ret = iotlb_translate(vrh, (u64)(uintptr_t)p, sizeof(*p), NULL, &iov, 1, VHOST_MAP_RO); if (ret < 0) return ret; @@ -1204,7 +1238,7 @@ static inline int putu16_iotlb(const struct vringh *vrh, int ret; /* Atomic write is needed for putu16 */ - ret = iotlb_translate(vrh, (u64)(uintptr_t)p, sizeof(*p), + ret = iotlb_translate(vrh, (u64)(uintptr_t)p, sizeof(*p), NULL, &iov, 1, VHOST_MAP_WO); if (ret < 0) return ret; diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig index 56c77f63cd22..0a53a61231c2 100644 --- a/drivers/virtio/Kconfig +++ b/drivers/virtio/Kconfig @@ -35,11 +35,12 @@ if VIRTIO_MENU config VIRTIO_HARDEN_NOTIFICATION bool "Harden virtio notification" + depends on BROKEN help Enable this to harden the device notifications and suppress those that happen at a time where notifications are illegal. - Experimental: Note that several drivers still have bugs that + Experimental: Note that several drivers still have issues that may cause crashes or hangs when correct handling of notifications is enforced; depending on the subset of drivers and devices you use, this may or may not work. @@ -126,9 +127,11 @@ config VIRTIO_MEM This driver provides access to virtio-mem paravirtualized memory devices, allowing to hotplug and hotunplug memory. - This driver was only tested under x86-64 and arm64, but should - theoretically work on all architectures that support memory hotplug - and hotremove. + This driver currently only supports x86-64 and arm64. Although it + should compile on other architectures that implement memory + hot(un)plug, architecture-specific and/or common + code changes may be required for virtio-mem, kdump and kexec to work as + expected. If unsure, say M. diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c index 14c142d77fba..828ced060742 100644 --- a/drivers/virtio/virtio.c +++ b/drivers/virtio/virtio.c @@ -428,7 +428,9 @@ int register_virtio_device(struct virtio_device *dev) goto out; dev->index = err; - dev_set_name(&dev->dev, "virtio%u", dev->index); + err = dev_set_name(&dev->dev, "virtio%u", dev->index); + if (err) + goto out_ida_remove; err = virtio_device_of_init(dev); if (err) diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c index 083ff1eb743d..c492a57531c6 100644 --- a/drivers/virtio/virtio_mmio.c +++ b/drivers/virtio/virtio_mmio.c @@ -360,7 +360,7 @@ static void vm_synchronize_cbs(struct virtio_device *vdev) static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, unsigned int index, void (*callback)(struct virtqueue *vq), - const char *name, bool ctx) + const char *name, u32 size, bool ctx) { struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); struct virtio_mmio_vq_info *info; @@ -395,14 +395,19 @@ static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, unsigned int in goto error_new_virtqueue; } + if (!size || size > num) + size = num; + /* Create the vring */ - vq = vring_create_virtqueue(index, num, VIRTIO_MMIO_VRING_ALIGN, vdev, + vq = vring_create_virtqueue(index, size, VIRTIO_MMIO_VRING_ALIGN, vdev, true, true, ctx, vm_notify, callback, name); if (!vq) { err = -ENOMEM; goto error_new_virtqueue; } + vq->num_max = num; + /* Activate the queue */ writel(virtqueue_get_vring_size(vq), vm_dev->base + VIRTIO_MMIO_QUEUE_NUM); if (vm_dev->version == 1) { @@ -472,6 +477,7 @@ static int vm_find_vqs(struct virtio_device *vdev, unsigned int nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], const char * const names[], + u32 sizes[], const bool *ctx, struct irq_affinity *desc) { @@ -487,6 +493,9 @@ static int vm_find_vqs(struct virtio_device *vdev, unsigned int nvqs, if (err) return err; + if (of_property_read_bool(vm_dev->pdev->dev.of_node, "wakeup-source")) + enable_irq_wake(irq); + for (i = 0; i < nvqs; ++i) { if (!names[i]) { vqs[i] = NULL; @@ -494,6 +503,7 @@ static int vm_find_vqs(struct virtio_device *vdev, unsigned int nvqs, } vqs[i] = vm_setup_vq(vdev, queue_idx++, callbacks[i], names[i], + sizes ? sizes[i] : 0, ctx ? ctx[i] : false); if (IS_ERR(vqs[i])) { vm_del_vqs(vdev); diff --git a/drivers/virtio/virtio_pci_common.c b/drivers/virtio/virtio_pci_common.c index ca51fcc9daab..00ad476a815d 100644 --- a/drivers/virtio/virtio_pci_common.c +++ b/drivers/virtio/virtio_pci_common.c @@ -174,6 +174,7 @@ error: static struct virtqueue *vp_setup_vq(struct virtio_device *vdev, unsigned int index, void (*callback)(struct virtqueue *vq), const char *name, + u32 size, bool ctx, u16 msix_vec) { @@ -186,7 +187,7 @@ static struct virtqueue *vp_setup_vq(struct virtio_device *vdev, unsigned int in if (!info) return ERR_PTR(-ENOMEM); - vq = vp_dev->setup_vq(vp_dev, info, index, callback, name, ctx, + vq = vp_dev->setup_vq(vp_dev, info, index, callback, name, size, ctx, msix_vec); if (IS_ERR(vq)) goto out_info; @@ -214,9 +215,15 @@ static void vp_del_vq(struct virtqueue *vq) struct virtio_pci_vq_info *info = vp_dev->vqs[vq->index]; unsigned long flags; - spin_lock_irqsave(&vp_dev->lock, flags); - list_del(&info->node); - spin_unlock_irqrestore(&vp_dev->lock, flags); + /* + * If it fails during re-enable reset vq. This way we won't rejoin + * info->node to the queue. Prevent unexpected irqs. + */ + if (!vq->reset) { + spin_lock_irqsave(&vp_dev->lock, flags); + list_del(&info->node); + spin_unlock_irqrestore(&vp_dev->lock, flags); + } vp_dev->del_vq(info); kfree(info); @@ -277,7 +284,7 @@ void vp_del_vqs(struct virtio_device *vdev) static int vp_find_vqs_msix(struct virtio_device *vdev, unsigned int nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], - const char * const names[], bool per_vq_vectors, + const char * const names[], u32 sizes[], bool per_vq_vectors, const bool *ctx, struct irq_affinity *desc) { @@ -320,8 +327,8 @@ static int vp_find_vqs_msix(struct virtio_device *vdev, unsigned int nvqs, else msix_vec = VP_MSIX_VQ_VECTOR; vqs[i] = vp_setup_vq(vdev, queue_idx++, callbacks[i], names[i], - ctx ? ctx[i] : false, - msix_vec); + sizes ? sizes[i] : 0, + ctx ? ctx[i] : false, msix_vec); if (IS_ERR(vqs[i])) { err = PTR_ERR(vqs[i]); goto error_find; @@ -351,7 +358,7 @@ error_find: static int vp_find_vqs_intx(struct virtio_device *vdev, unsigned int nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], - const char * const names[], const bool *ctx) + const char * const names[], u32 sizes[], const bool *ctx) { struct virtio_pci_device *vp_dev = to_vp_device(vdev); int i, err, queue_idx = 0; @@ -373,6 +380,7 @@ static int vp_find_vqs_intx(struct virtio_device *vdev, unsigned int nvqs, continue; } vqs[i] = vp_setup_vq(vdev, queue_idx++, callbacks[i], names[i], + sizes ? sizes[i] : 0, ctx ? ctx[i] : false, VIRTIO_MSI_NO_VECTOR); if (IS_ERR(vqs[i])) { @@ -390,21 +398,21 @@ out_del_vqs: /* the config->find_vqs() implementation */ int vp_find_vqs(struct virtio_device *vdev, unsigned int nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], - const char * const names[], const bool *ctx, + const char * const names[], u32 sizes[], const bool *ctx, struct irq_affinity *desc) { int err; /* Try MSI-X with one vector per queue. */ - err = vp_find_vqs_msix(vdev, nvqs, vqs, callbacks, names, true, ctx, desc); + err = vp_find_vqs_msix(vdev, nvqs, vqs, callbacks, names, sizes, true, ctx, desc); if (!err) return 0; /* Fallback: MSI-X with one vector for config, one shared for queues. */ - err = vp_find_vqs_msix(vdev, nvqs, vqs, callbacks, names, false, ctx, desc); + err = vp_find_vqs_msix(vdev, nvqs, vqs, callbacks, names, sizes, false, ctx, desc); if (!err) return 0; /* Finally fall back to regular interrupts. */ - return vp_find_vqs_intx(vdev, nvqs, vqs, callbacks, names, ctx); + return vp_find_vqs_intx(vdev, nvqs, vqs, callbacks, names, sizes, ctx); } const char *vp_bus_name(struct virtio_device *vdev) diff --git a/drivers/virtio/virtio_pci_common.h b/drivers/virtio/virtio_pci_common.h index 23112d84218f..c0448378b698 100644 --- a/drivers/virtio/virtio_pci_common.h +++ b/drivers/virtio/virtio_pci_common.h @@ -80,6 +80,7 @@ struct virtio_pci_device { unsigned int idx, void (*callback)(struct virtqueue *vq), const char *name, + u32 size, bool ctx, u16 msix_vec); void (*del_vq)(struct virtio_pci_vq_info *info); @@ -110,7 +111,7 @@ void vp_del_vqs(struct virtio_device *vdev); /* the config->find_vqs() implementation */ int vp_find_vqs(struct virtio_device *vdev, unsigned int nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], - const char * const names[], const bool *ctx, + const char * const names[], u32 sizes[], const bool *ctx, struct irq_affinity *desc); const char *vp_bus_name(struct virtio_device *vdev); diff --git a/drivers/virtio/virtio_pci_legacy.c b/drivers/virtio/virtio_pci_legacy.c index a5e5721145c7..d75e5c4e637f 100644 --- a/drivers/virtio/virtio_pci_legacy.c +++ b/drivers/virtio/virtio_pci_legacy.c @@ -112,6 +112,7 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev, unsigned int index, void (*callback)(struct virtqueue *vq), const char *name, + u32 size, bool ctx, u16 msix_vec) { @@ -125,16 +126,21 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev, if (!num || vp_legacy_get_queue_enable(&vp_dev->ldev, index)) return ERR_PTR(-ENOENT); + if (!size || size > num) + size = num; + info->msix_vector = msix_vec; /* create the vring */ - vq = vring_create_virtqueue(index, num, + vq = vring_create_virtqueue(index, size, VIRTIO_PCI_VRING_ALIGN, &vp_dev->vdev, true, false, ctx, vp_notify, callback, name); if (!vq) return ERR_PTR(-ENOMEM); + vq->num_max = num; + q_pfn = virtqueue_get_desc_addr(vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT; if (q_pfn >> 32) { dev_err(&vp_dev->pci_dev->dev, diff --git a/drivers/virtio/virtio_pci_modern.c b/drivers/virtio/virtio_pci_modern.c index 623906b4996c..f7965c5dd36b 100644 --- a/drivers/virtio/virtio_pci_modern.c +++ b/drivers/virtio/virtio_pci_modern.c @@ -34,6 +34,9 @@ static void vp_transport_features(struct virtio_device *vdev, u64 features) if ((features & BIT_ULL(VIRTIO_F_SR_IOV)) && pci_find_ext_capability(pci_dev, PCI_EXT_CAP_ID_SRIOV)) __virtio_set_bit(vdev, VIRTIO_F_SR_IOV); + + if (features & BIT_ULL(VIRTIO_F_RING_RESET)) + __virtio_set_bit(vdev, VIRTIO_F_RING_RESET); } /* virtio config->finalize_features() implementation */ @@ -176,6 +179,110 @@ static void vp_reset(struct virtio_device *vdev) vp_synchronize_vectors(vdev); } +static int vp_active_vq(struct virtqueue *vq, u16 msix_vec) +{ + struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev); + struct virtio_pci_modern_device *mdev = &vp_dev->mdev; + unsigned long index; + + index = vq->index; + + /* activate the queue */ + vp_modern_set_queue_size(mdev, index, virtqueue_get_vring_size(vq)); + vp_modern_queue_address(mdev, index, virtqueue_get_desc_addr(vq), + virtqueue_get_avail_addr(vq), + virtqueue_get_used_addr(vq)); + + if (msix_vec != VIRTIO_MSI_NO_VECTOR) { + msix_vec = vp_modern_queue_vector(mdev, index, msix_vec); + if (msix_vec == VIRTIO_MSI_NO_VECTOR) + return -EBUSY; + } + + return 0; +} + +static int vp_modern_disable_vq_and_reset(struct virtqueue *vq) +{ + struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev); + struct virtio_pci_modern_device *mdev = &vp_dev->mdev; + struct virtio_pci_vq_info *info; + unsigned long flags; + + if (!virtio_has_feature(vq->vdev, VIRTIO_F_RING_RESET)) + return -ENOENT; + + vp_modern_set_queue_reset(mdev, vq->index); + + info = vp_dev->vqs[vq->index]; + + /* delete vq from irq handler */ + spin_lock_irqsave(&vp_dev->lock, flags); + list_del(&info->node); + spin_unlock_irqrestore(&vp_dev->lock, flags); + + INIT_LIST_HEAD(&info->node); + +#ifdef CONFIG_VIRTIO_HARDEN_NOTIFICATION + __virtqueue_break(vq); +#endif + + /* For the case where vq has an exclusive irq, call synchronize_irq() to + * wait for completion. + * + * note: We can't use disable_irq() since it conflicts with the affinity + * managed IRQ that is used by some drivers. + */ + if (vp_dev->per_vq_vectors && info->msix_vector != VIRTIO_MSI_NO_VECTOR) + synchronize_irq(pci_irq_vector(vp_dev->pci_dev, info->msix_vector)); + + vq->reset = true; + + return 0; +} + +static int vp_modern_enable_vq_after_reset(struct virtqueue *vq) +{ + struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev); + struct virtio_pci_modern_device *mdev = &vp_dev->mdev; + struct virtio_pci_vq_info *info; + unsigned long flags, index; + int err; + + if (!vq->reset) + return -EBUSY; + + index = vq->index; + info = vp_dev->vqs[index]; + + if (vp_modern_get_queue_reset(mdev, index)) + return -EBUSY; + + if (vp_modern_get_queue_enable(mdev, index)) + return -EBUSY; + + err = vp_active_vq(vq, info->msix_vector); + if (err) + return err; + + if (vq->callback) { + spin_lock_irqsave(&vp_dev->lock, flags); + list_add(&info->node, &vp_dev->virtqueues); + spin_unlock_irqrestore(&vp_dev->lock, flags); + } else { + INIT_LIST_HEAD(&info->node); + } + +#ifdef CONFIG_VIRTIO_HARDEN_NOTIFICATION + __virtqueue_unbreak(vq); +#endif + + vp_modern_set_queue_enable(&vp_dev->mdev, index, true); + vq->reset = false; + + return 0; +} + static u16 vp_config_vector(struct virtio_pci_device *vp_dev, u16 vector) { return vp_modern_config_vector(&vp_dev->mdev, vector); @@ -186,6 +293,7 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev, unsigned int index, void (*callback)(struct virtqueue *vq), const char *name, + u32 size, bool ctx, u16 msix_vec) { @@ -203,47 +311,39 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev, if (!num || vp_modern_get_queue_enable(mdev, index)) return ERR_PTR(-ENOENT); - if (num & (num - 1)) { - dev_warn(&vp_dev->pci_dev->dev, "bad queue size %u", num); + if (!size || size > num) + size = num; + + if (size & (size - 1)) { + dev_warn(&vp_dev->pci_dev->dev, "bad queue size %u", size); return ERR_PTR(-EINVAL); } info->msix_vector = msix_vec; /* create the vring */ - vq = vring_create_virtqueue(index, num, + vq = vring_create_virtqueue(index, size, SMP_CACHE_BYTES, &vp_dev->vdev, true, true, ctx, vp_notify, callback, name); if (!vq) return ERR_PTR(-ENOMEM); - /* activate the queue */ - vp_modern_set_queue_size(mdev, index, virtqueue_get_vring_size(vq)); - vp_modern_queue_address(mdev, index, virtqueue_get_desc_addr(vq), - virtqueue_get_avail_addr(vq), - virtqueue_get_used_addr(vq)); + vq->num_max = num; + + err = vp_active_vq(vq, msix_vec); + if (err) + goto err; vq->priv = (void __force *)vp_modern_map_vq_notify(mdev, index, NULL); if (!vq->priv) { err = -ENOMEM; - goto err_map_notify; - } - - if (msix_vec != VIRTIO_MSI_NO_VECTOR) { - msix_vec = vp_modern_queue_vector(mdev, index, msix_vec); - if (msix_vec == VIRTIO_MSI_NO_VECTOR) { - err = -EBUSY; - goto err_assign_vector; - } + goto err; } return vq; -err_assign_vector: - if (!mdev->notify_base) - pci_iounmap(mdev->pci_dev, (void __iomem __force *)vq->priv); -err_map_notify: +err: vring_del_virtqueue(vq); return ERR_PTR(err); } @@ -251,12 +351,15 @@ err_map_notify: static int vp_modern_find_vqs(struct virtio_device *vdev, unsigned int nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], - const char * const names[], const bool *ctx, + const char * const names[], + u32 sizes[], + const bool *ctx, struct irq_affinity *desc) { struct virtio_pci_device *vp_dev = to_vp_device(vdev); struct virtqueue *vq; - int rc = vp_find_vqs(vdev, nvqs, vqs, callbacks, names, ctx, desc); + int rc = vp_find_vqs(vdev, nvqs, vqs, callbacks, names, sizes, ctx, + desc); if (rc) return rc; @@ -401,6 +504,8 @@ static const struct virtio_config_ops virtio_pci_config_nodev_ops = { .set_vq_affinity = vp_set_vq_affinity, .get_vq_affinity = vp_get_vq_affinity, .get_shm_region = vp_get_shm_region, + .disable_vq_and_reset = vp_modern_disable_vq_and_reset, + .enable_vq_after_reset = vp_modern_enable_vq_after_reset, }; static const struct virtio_config_ops virtio_pci_config_ops = { @@ -419,6 +524,8 @@ static const struct virtio_config_ops virtio_pci_config_ops = { .set_vq_affinity = vp_set_vq_affinity, .get_vq_affinity = vp_get_vq_affinity, .get_shm_region = vp_get_shm_region, + .disable_vq_and_reset = vp_modern_disable_vq_and_reset, + .enable_vq_after_reset = vp_modern_enable_vq_after_reset, }; /* the PCI probing function */ diff --git a/drivers/virtio/virtio_pci_modern_dev.c b/drivers/virtio/virtio_pci_modern_dev.c index fa2a9445bb18..869cb46bef96 100644 --- a/drivers/virtio/virtio_pci_modern_dev.c +++ b/drivers/virtio/virtio_pci_modern_dev.c @@ -3,6 +3,7 @@ #include <linux/virtio_pci_modern.h> #include <linux/module.h> #include <linux/pci.h> +#include <linux/delay.h> /* * vp_modern_map_capability - map a part of virtio pci capability @@ -475,6 +476,44 @@ void vp_modern_set_status(struct virtio_pci_modern_device *mdev, EXPORT_SYMBOL_GPL(vp_modern_set_status); /* + * vp_modern_get_queue_reset - get the queue reset status + * @mdev: the modern virtio-pci device + * @index: queue index + */ +int vp_modern_get_queue_reset(struct virtio_pci_modern_device *mdev, u16 index) +{ + struct virtio_pci_modern_common_cfg __iomem *cfg; + + cfg = (struct virtio_pci_modern_common_cfg __iomem *)mdev->common; + + vp_iowrite16(index, &cfg->cfg.queue_select); + return vp_ioread16(&cfg->queue_reset); +} +EXPORT_SYMBOL_GPL(vp_modern_get_queue_reset); + +/* + * vp_modern_set_queue_reset - reset the queue + * @mdev: the modern virtio-pci device + * @index: queue index + */ +void vp_modern_set_queue_reset(struct virtio_pci_modern_device *mdev, u16 index) +{ + struct virtio_pci_modern_common_cfg __iomem *cfg; + + cfg = (struct virtio_pci_modern_common_cfg __iomem *)mdev->common; + + vp_iowrite16(index, &cfg->cfg.queue_select); + vp_iowrite16(1, &cfg->queue_reset); + + while (vp_ioread16(&cfg->queue_reset)) + msleep(1); + + while (vp_ioread16(&cfg->cfg.queue_enable)) + msleep(1); +} +EXPORT_SYMBOL_GPL(vp_modern_set_queue_reset); + +/* * vp_modern_queue_vector - set the MSIX vector for a specific virtqueue * @mdev: the modern virtio-pci device * @index: queue index diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index 643ca779fcc6..d66c8e6d0ef3 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -85,6 +85,71 @@ struct vring_desc_extra { u16 next; /* The next desc state in a list. */ }; +struct vring_virtqueue_split { + /* Actual memory layout for this queue. */ + struct vring vring; + + /* Last written value to avail->flags */ + u16 avail_flags_shadow; + + /* + * Last written value to avail->idx in + * guest byte order. + */ + u16 avail_idx_shadow; + + /* Per-descriptor state. */ + struct vring_desc_state_split *desc_state; + struct vring_desc_extra *desc_extra; + + /* DMA address and size information */ + dma_addr_t queue_dma_addr; + size_t queue_size_in_bytes; + + /* + * The parameters for creating vrings are reserved for creating new + * vring. + */ + u32 vring_align; + bool may_reduce_num; +}; + +struct vring_virtqueue_packed { + /* Actual memory layout for this queue. */ + struct { + unsigned int num; + struct vring_packed_desc *desc; + struct vring_packed_desc_event *driver; + struct vring_packed_desc_event *device; + } vring; + + /* Driver ring wrap counter. */ + bool avail_wrap_counter; + + /* Avail used flags. */ + u16 avail_used_flags; + + /* Index of the next avail descriptor. */ + u16 next_avail_idx; + + /* + * Last written value to driver->flags in + * guest byte order. + */ + u16 event_flags_shadow; + + /* Per-descriptor state. */ + struct vring_desc_state_packed *desc_state; + struct vring_desc_extra *desc_extra; + + /* DMA address and size information */ + dma_addr_t ring_dma_addr; + dma_addr_t driver_event_dma_addr; + dma_addr_t device_event_dma_addr; + size_t ring_size_in_bytes; + size_t event_size_in_bytes; +}; + struct vring_virtqueue { struct virtqueue vq; @@ -124,64 +189,10 @@ struct vring_virtqueue { union { /* Available for split ring */ - struct { - /* Actual memory layout for this queue. */ - struct vring vring; - - /* Last written value to avail->flags */ - u16 avail_flags_shadow; - - /* - * Last written value to avail->idx in - * guest byte order. - */ - u16 avail_idx_shadow; - - /* Per-descriptor state. */ - struct vring_desc_state_split *desc_state; - struct vring_desc_extra *desc_extra; - - /* DMA address and size information */ - dma_addr_t queue_dma_addr; - size_t queue_size_in_bytes; - } split; + struct vring_virtqueue_split split; /* Available for packed ring */ - struct { - /* Actual memory layout for this queue. */ - struct { - unsigned int num; - struct vring_packed_desc *desc; - struct vring_packed_desc_event *driver; - struct vring_packed_desc_event *device; - } vring; - - /* Driver ring wrap counter. */ - bool avail_wrap_counter; - - /* Avail used flags. */ - u16 avail_used_flags; - - /* Index of the next avail descriptor. */ - u16 next_avail_idx; - - /* - * Last written value to driver->flags in - * guest byte order. - */ - u16 event_flags_shadow; - - /* Per-descriptor state. */ - struct vring_desc_state_packed *desc_state; - struct vring_desc_extra *desc_extra; - - /* DMA address and size information */ - dma_addr_t ring_dma_addr; - dma_addr_t driver_event_dma_addr; - dma_addr_t device_event_dma_addr; - size_t ring_size_in_bytes; - size_t event_size_in_bytes; - } packed; + struct vring_virtqueue_packed packed; }; /* How to notify other side. FIXME: commonalize hcalls! */ @@ -200,6 +211,16 @@ struct vring_virtqueue { #endif }; +static struct virtqueue *__vring_new_virtqueue(unsigned int index, + struct vring_virtqueue_split *vring_split, + struct virtio_device *vdev, + bool weak_barriers, + bool context, + bool (*notify)(struct virtqueue *), + void (*callback)(struct virtqueue *), + const char *name); +static struct vring_desc_extra *vring_alloc_desc_extra(unsigned int num); +static void vring_free(struct virtqueue *_vq); /* * Helpers. @@ -364,6 +385,24 @@ static int vring_mapping_error(const struct vring_virtqueue *vq, return dma_mapping_error(vring_dma_dev(vq), addr); } +static void virtqueue_init(struct vring_virtqueue *vq, u32 num) +{ + vq->vq.num_free = num; + + if (vq->packed_ring) + vq->last_used_idx = 0 | (1 << VRING_PACKED_EVENT_F_WRAP_CTR); + else + vq->last_used_idx = 0; + + vq->event_triggered = false; + vq->num_added = 0; + +#ifdef DEBUG + vq->in_use = false; + vq->last_add_time_valid = false; +#endif +} + /* * Split ring specific functions - *_split(). @@ -907,28 +946,107 @@ static void *virtqueue_detach_unused_buf_split(struct virtqueue *_vq) return NULL; } -static struct virtqueue *vring_create_virtqueue_split( - unsigned int index, - unsigned int num, - unsigned int vring_align, - struct virtio_device *vdev, - bool weak_barriers, - bool may_reduce_num, - bool context, - bool (*notify)(struct virtqueue *), - void (*callback)(struct virtqueue *), - const char *name) +static void virtqueue_vring_init_split(struct vring_virtqueue_split *vring_split, + struct vring_virtqueue *vq) +{ + struct virtio_device *vdev; + + vdev = vq->vq.vdev; + + vring_split->avail_flags_shadow = 0; + vring_split->avail_idx_shadow = 0; + + /* No callback? Tell other side not to bother us. */ + if (!vq->vq.callback) { + vring_split->avail_flags_shadow |= VRING_AVAIL_F_NO_INTERRUPT; + if (!vq->event) + vring_split->vring.avail->flags = cpu_to_virtio16(vdev, + vring_split->avail_flags_shadow); + } +} + +static void virtqueue_reinit_split(struct vring_virtqueue *vq) +{ + int num; + + num = vq->split.vring.num; + + vq->split.vring.avail->flags = 0; + vq->split.vring.avail->idx = 0; + + /* reset avail event */ + vq->split.vring.avail->ring[num] = 0; + + vq->split.vring.used->flags = 0; + vq->split.vring.used->idx = 0; + + /* reset used event */ + *(__virtio16 *)&(vq->split.vring.used->ring[num]) = 0; + + virtqueue_init(vq, num); + + virtqueue_vring_init_split(&vq->split, vq); +} + +static void virtqueue_vring_attach_split(struct vring_virtqueue *vq, + struct vring_virtqueue_split *vring_split) +{ + vq->split = *vring_split; + + /* Put everything in free lists. */ + vq->free_head = 0; +} + +static int vring_alloc_state_extra_split(struct vring_virtqueue_split *vring_split) +{ + struct vring_desc_state_split *state; + struct vring_desc_extra *extra; + u32 num = vring_split->vring.num; + + state = kmalloc_array(num, sizeof(struct vring_desc_state_split), GFP_KERNEL); + if (!state) + goto err_state; + + extra = vring_alloc_desc_extra(num); + if (!extra) + goto err_extra; + + memset(state, 0, num * sizeof(struct vring_desc_state_split)); + + vring_split->desc_state = state; + vring_split->desc_extra = extra; + return 0; + +err_extra: + kfree(state); +err_state: + return -ENOMEM; +} + +static void vring_free_split(struct vring_virtqueue_split *vring_split, + struct virtio_device *vdev) +{ + vring_free_queue(vdev, vring_split->queue_size_in_bytes, + vring_split->vring.desc, + vring_split->queue_dma_addr); + + kfree(vring_split->desc_state); + kfree(vring_split->desc_extra); +} + +static int vring_alloc_queue_split(struct vring_virtqueue_split *vring_split, + struct virtio_device *vdev, + u32 num, + unsigned int vring_align, + bool may_reduce_num) { - struct virtqueue *vq; void *queue = NULL; dma_addr_t dma_addr; - size_t queue_size_in_bytes; - struct vring vring; /* We assume num is a power of 2. */ if (num & (num - 1)) { dev_warn(&vdev->dev, "Bad virtqueue length %u\n", num); - return NULL; + return -EINVAL; } /* TODO: allocate each queue chunk individually */ @@ -939,11 +1057,11 @@ static struct virtqueue *vring_create_virtqueue_split( if (queue) break; if (!may_reduce_num) - return NULL; + return -ENOMEM; } if (!num) - return NULL; + return -ENOMEM; if (!queue) { /* Try to get a single page. You are my only hope! */ @@ -951,26 +1069,85 @@ static struct virtqueue *vring_create_virtqueue_split( &dma_addr, GFP_KERNEL|__GFP_ZERO); } if (!queue) - return NULL; + return -ENOMEM; + + vring_init(&vring_split->vring, num, queue, vring_align); + + vring_split->queue_dma_addr = dma_addr; + vring_split->queue_size_in_bytes = vring_size(num, vring_align); - queue_size_in_bytes = vring_size(num, vring_align); - vring_init(&vring, num, queue, vring_align); + vring_split->vring_align = vring_align; + vring_split->may_reduce_num = may_reduce_num; - vq = __vring_new_virtqueue(index, vring, vdev, weak_barriers, context, - notify, callback, name); + return 0; +} + +static struct virtqueue *vring_create_virtqueue_split( + unsigned int index, + unsigned int num, + unsigned int vring_align, + struct virtio_device *vdev, + bool weak_barriers, + bool may_reduce_num, + bool context, + bool (*notify)(struct virtqueue *), + void (*callback)(struct virtqueue *), + const char *name) +{ + struct vring_virtqueue_split vring_split = {}; + struct virtqueue *vq; + int err; + + err = vring_alloc_queue_split(&vring_split, vdev, num, vring_align, + may_reduce_num); + if (err) + return NULL; + + vq = __vring_new_virtqueue(index, &vring_split, vdev, weak_barriers, + context, notify, callback, name); if (!vq) { - vring_free_queue(vdev, queue_size_in_bytes, queue, - dma_addr); + vring_free_split(&vring_split, vdev); return NULL; } - to_vvq(vq)->split.queue_dma_addr = dma_addr; - to_vvq(vq)->split.queue_size_in_bytes = queue_size_in_bytes; to_vvq(vq)->we_own_ring = true; return vq; } +static int virtqueue_resize_split(struct virtqueue *_vq, u32 num) +{ + struct vring_virtqueue_split vring_split = {}; + struct vring_virtqueue *vq = to_vvq(_vq); + struct virtio_device *vdev = _vq->vdev; + int err; + + err = vring_alloc_queue_split(&vring_split, vdev, num, + vq->split.vring_align, + vq->split.may_reduce_num); + if (err) + goto err; + + err = vring_alloc_state_extra_split(&vring_split); + if (err) + goto err_state_extra; + + vring_free(&vq->vq); + + virtqueue_vring_init_split(&vring_split, vq); + + virtqueue_init(vq, vring_split.vring.num); + virtqueue_vring_attach_split(vq, &vring_split); + + return 0; + +err_state_extra: + vring_free_split(&vring_split, vdev); +err: + virtqueue_reinit_split(vq); + return -ENOMEM; +} + /* * Packed ring specific functions - *_packed(). @@ -1637,8 +1814,7 @@ static void *virtqueue_detach_unused_buf_packed(struct virtqueue *_vq) return NULL; } -static struct vring_desc_extra *vring_alloc_desc_extra(struct vring_virtqueue *vq, - unsigned int num) +static struct vring_desc_extra *vring_alloc_desc_extra(unsigned int num) { struct vring_desc_extra *desc_extra; unsigned int i; @@ -1656,19 +1832,32 @@ static struct vring_desc_extra *vring_alloc_desc_extra(struct vring_virtqueue *v return desc_extra; } -static struct virtqueue *vring_create_virtqueue_packed( - unsigned int index, - unsigned int num, - unsigned int vring_align, - struct virtio_device *vdev, - bool weak_barriers, - bool may_reduce_num, - bool context, - bool (*notify)(struct virtqueue *), - void (*callback)(struct virtqueue *), - const char *name) +static void vring_free_packed(struct vring_virtqueue_packed *vring_packed, + struct virtio_device *vdev) +{ + if (vring_packed->vring.desc) + vring_free_queue(vdev, vring_packed->ring_size_in_bytes, + vring_packed->vring.desc, + vring_packed->ring_dma_addr); + + if (vring_packed->vring.driver) + vring_free_queue(vdev, vring_packed->event_size_in_bytes, + vring_packed->vring.driver, + vring_packed->driver_event_dma_addr); + + if (vring_packed->vring.device) + vring_free_queue(vdev, vring_packed->event_size_in_bytes, + vring_packed->vring.device, + vring_packed->device_event_dma_addr); + + kfree(vring_packed->desc_state); + kfree(vring_packed->desc_extra); +} + +static int vring_alloc_queue_packed(struct vring_virtqueue_packed *vring_packed, + struct virtio_device *vdev, + u32 num) { - struct vring_virtqueue *vq; struct vring_packed_desc *ring; struct vring_packed_desc_event *driver, *device; dma_addr_t ring_dma_addr, driver_event_dma_addr, device_event_dma_addr; @@ -1680,7 +1869,11 @@ static struct virtqueue *vring_create_virtqueue_packed( &ring_dma_addr, GFP_KERNEL|__GFP_NOWARN|__GFP_ZERO); if (!ring) - goto err_ring; + goto err; + + vring_packed->vring.desc = ring; + vring_packed->ring_dma_addr = ring_dma_addr; + vring_packed->ring_size_in_bytes = ring_size_in_bytes; event_size_in_bytes = sizeof(struct vring_packed_desc_event); @@ -1688,13 +1881,112 @@ static struct virtqueue *vring_create_virtqueue_packed( &driver_event_dma_addr, GFP_KERNEL|__GFP_NOWARN|__GFP_ZERO); if (!driver) - goto err_driver; + goto err; + + vring_packed->vring.driver = driver; + vring_packed->event_size_in_bytes = event_size_in_bytes; + vring_packed->driver_event_dma_addr = driver_event_dma_addr; device = vring_alloc_queue(vdev, event_size_in_bytes, &device_event_dma_addr, GFP_KERNEL|__GFP_NOWARN|__GFP_ZERO); if (!device) - goto err_device; + goto err; + + vring_packed->vring.device = device; + vring_packed->device_event_dma_addr = device_event_dma_addr; + + vring_packed->vring.num = num; + + return 0; + +err: + vring_free_packed(vring_packed, vdev); + return -ENOMEM; +} + +static int vring_alloc_state_extra_packed(struct vring_virtqueue_packed *vring_packed) +{ + struct vring_desc_state_packed *state; + struct vring_desc_extra *extra; + u32 num = vring_packed->vring.num; + + state = kmalloc_array(num, sizeof(struct vring_desc_state_packed), GFP_KERNEL); + if (!state) + goto err_desc_state; + + memset(state, 0, num * sizeof(struct vring_desc_state_packed)); + + extra = vring_alloc_desc_extra(num); + if (!extra) + goto err_desc_extra; + + vring_packed->desc_state = state; + vring_packed->desc_extra = extra; + + return 0; + +err_desc_extra: + kfree(state); +err_desc_state: + return -ENOMEM; +} + +static void virtqueue_vring_init_packed(struct vring_virtqueue_packed *vring_packed, + bool callback) +{ + vring_packed->next_avail_idx = 0; + vring_packed->avail_wrap_counter = 1; + vring_packed->event_flags_shadow = 0; + vring_packed->avail_used_flags = 1 << VRING_PACKED_DESC_F_AVAIL; + + /* No callback? Tell other side not to bother us. */ + if (!callback) { + vring_packed->event_flags_shadow = VRING_PACKED_EVENT_FLAG_DISABLE; + vring_packed->vring.driver->flags = + cpu_to_le16(vring_packed->event_flags_shadow); + } +} + +static void virtqueue_vring_attach_packed(struct vring_virtqueue *vq, + struct vring_virtqueue_packed *vring_packed) +{ + vq->packed = *vring_packed; + + /* Put everything in free lists. */ + vq->free_head = 0; +} + +static void virtqueue_reinit_packed(struct vring_virtqueue *vq) +{ + memset(vq->packed.vring.device, 0, vq->packed.event_size_in_bytes); + memset(vq->packed.vring.driver, 0, vq->packed.event_size_in_bytes); + + /* we need to reset the desc.flags. For more, see is_used_desc_packed() */ + memset(vq->packed.vring.desc, 0, vq->packed.ring_size_in_bytes); + + virtqueue_init(vq, vq->packed.vring.num); + virtqueue_vring_init_packed(&vq->packed, !!vq->vq.callback); +} + +static struct virtqueue *vring_create_virtqueue_packed( + unsigned int index, + unsigned int num, + unsigned int vring_align, + struct virtio_device *vdev, + bool weak_barriers, + bool may_reduce_num, + bool context, + bool (*notify)(struct virtqueue *), + void (*callback)(struct virtqueue *), + const char *name) +{ + struct vring_virtqueue_packed vring_packed = {}; + struct vring_virtqueue *vq; + int err; + + if (vring_alloc_queue_packed(&vring_packed, vdev, num)) + goto err_ring; vq = kmalloc(sizeof(*vq), GFP_KERNEL); if (!vq) @@ -1703,8 +1995,8 @@ static struct virtqueue *vring_create_virtqueue_packed( vq->vq.callback = callback; vq->vq.vdev = vdev; vq->vq.name = name; - vq->vq.num_free = num; vq->vq.index = index; + vq->vq.reset = false; vq->we_own_ring = true; vq->notify = notify; vq->weak_barriers = weak_barriers; @@ -1713,15 +2005,8 @@ static struct virtqueue *vring_create_virtqueue_packed( #else vq->broken = false; #endif - vq->last_used_idx = 0 | (1 << VRING_PACKED_EVENT_F_WRAP_CTR); - vq->event_triggered = false; - vq->num_added = 0; vq->packed_ring = true; vq->use_dma_api = vring_use_dma_api(vdev); -#ifdef DEBUG - vq->in_use = false; - vq->last_add_time_valid = false; -#endif vq->indirect = virtio_has_feature(vdev, VIRTIO_RING_F_INDIRECT_DESC) && !context; @@ -1730,65 +2015,58 @@ static struct virtqueue *vring_create_virtqueue_packed( if (virtio_has_feature(vdev, VIRTIO_F_ORDER_PLATFORM)) vq->weak_barriers = false; - vq->packed.ring_dma_addr = ring_dma_addr; - vq->packed.driver_event_dma_addr = driver_event_dma_addr; - vq->packed.device_event_dma_addr = device_event_dma_addr; - - vq->packed.ring_size_in_bytes = ring_size_in_bytes; - vq->packed.event_size_in_bytes = event_size_in_bytes; - - vq->packed.vring.num = num; - vq->packed.vring.desc = ring; - vq->packed.vring.driver = driver; - vq->packed.vring.device = device; - - vq->packed.next_avail_idx = 0; - vq->packed.avail_wrap_counter = 1; - vq->packed.event_flags_shadow = 0; - vq->packed.avail_used_flags = 1 << VRING_PACKED_DESC_F_AVAIL; - - vq->packed.desc_state = kmalloc_array(num, - sizeof(struct vring_desc_state_packed), - GFP_KERNEL); - if (!vq->packed.desc_state) - goto err_desc_state; - - memset(vq->packed.desc_state, 0, - num * sizeof(struct vring_desc_state_packed)); - - /* Put everything in free lists. */ - vq->free_head = 0; + err = vring_alloc_state_extra_packed(&vring_packed); + if (err) + goto err_state_extra; - vq->packed.desc_extra = vring_alloc_desc_extra(vq, num); - if (!vq->packed.desc_extra) - goto err_desc_extra; + virtqueue_vring_init_packed(&vring_packed, !!callback); - /* No callback? Tell other side not to bother us. */ - if (!callback) { - vq->packed.event_flags_shadow = VRING_PACKED_EVENT_FLAG_DISABLE; - vq->packed.vring.driver->flags = - cpu_to_le16(vq->packed.event_flags_shadow); - } + virtqueue_init(vq, num); + virtqueue_vring_attach_packed(vq, &vring_packed); spin_lock(&vdev->vqs_list_lock); list_add_tail(&vq->vq.list, &vdev->vqs); spin_unlock(&vdev->vqs_list_lock); return &vq->vq; -err_desc_extra: - kfree(vq->packed.desc_state); -err_desc_state: +err_state_extra: kfree(vq); err_vq: - vring_free_queue(vdev, event_size_in_bytes, device, device_event_dma_addr); -err_device: - vring_free_queue(vdev, event_size_in_bytes, driver, driver_event_dma_addr); -err_driver: - vring_free_queue(vdev, ring_size_in_bytes, ring, ring_dma_addr); + vring_free_packed(&vring_packed, vdev); err_ring: return NULL; } +static int virtqueue_resize_packed(struct virtqueue *_vq, u32 num) +{ + struct vring_virtqueue_packed vring_packed = {}; + struct vring_virtqueue *vq = to_vvq(_vq); + struct virtio_device *vdev = _vq->vdev; + int err; + + if (vring_alloc_queue_packed(&vring_packed, vdev, num)) + goto err_ring; + + err = vring_alloc_state_extra_packed(&vring_packed); + if (err) + goto err_state_extra; + + vring_free(&vq->vq); + + virtqueue_vring_init_packed(&vring_packed, !!vq->vq.callback); + + virtqueue_init(vq, vring_packed.vring.num); + virtqueue_vring_attach_packed(vq, &vring_packed); + + return 0; + +err_state_extra: + vring_free_packed(&vring_packed, vdev); +err_ring: + virtqueue_reinit_packed(vq); + return -ENOMEM; +} + /* * Generic functions and exported symbols. @@ -2131,8 +2409,8 @@ EXPORT_SYMBOL_GPL(virtqueue_enable_cb_delayed); * @_vq: the struct virtqueue we're talking about. * * Returns NULL or the "data" token handed to virtqueue_add_*(). - * This is not valid on an active queue; it is useful only for device - * shutdown. + * This is not valid on an active queue; it is useful for device + * shutdown or the reset queue. */ void *virtqueue_detach_unused_buf(struct virtqueue *_vq) { @@ -2180,16 +2458,17 @@ irqreturn_t vring_interrupt(int irq, void *_vq) EXPORT_SYMBOL_GPL(vring_interrupt); /* Only available for split ring */ -struct virtqueue *__vring_new_virtqueue(unsigned int index, - struct vring vring, - struct virtio_device *vdev, - bool weak_barriers, - bool context, - bool (*notify)(struct virtqueue *), - void (*callback)(struct virtqueue *), - const char *name) +static struct virtqueue *__vring_new_virtqueue(unsigned int index, + struct vring_virtqueue_split *vring_split, + struct virtio_device *vdev, + bool weak_barriers, + bool context, + bool (*notify)(struct virtqueue *), + void (*callback)(struct virtqueue *), + const char *name) { struct vring_virtqueue *vq; + int err; if (virtio_has_feature(vdev, VIRTIO_F_RING_PACKED)) return NULL; @@ -2202,8 +2481,8 @@ struct virtqueue *__vring_new_virtqueue(unsigned int index, vq->vq.callback = callback; vq->vq.vdev = vdev; vq->vq.name = name; - vq->vq.num_free = vring.num; vq->vq.index = index; + vq->vq.reset = false; vq->we_own_ring = false; vq->notify = notify; vq->weak_barriers = weak_barriers; @@ -2212,14 +2491,7 @@ struct virtqueue *__vring_new_virtqueue(unsigned int index, #else vq->broken = false; #endif - vq->last_used_idx = 0; - vq->event_triggered = false; - vq->num_added = 0; vq->use_dma_api = vring_use_dma_api(vdev); -#ifdef DEBUG - vq->in_use = false; - vq->last_add_time_valid = false; -#endif vq->indirect = virtio_has_feature(vdev, VIRTIO_RING_F_INDIRECT_DESC) && !context; @@ -2228,47 +2500,22 @@ struct virtqueue *__vring_new_virtqueue(unsigned int index, if (virtio_has_feature(vdev, VIRTIO_F_ORDER_PLATFORM)) vq->weak_barriers = false; - vq->split.queue_dma_addr = 0; - vq->split.queue_size_in_bytes = 0; - - vq->split.vring = vring; - vq->split.avail_flags_shadow = 0; - vq->split.avail_idx_shadow = 0; - - /* No callback? Tell other side not to bother us. */ - if (!callback) { - vq->split.avail_flags_shadow |= VRING_AVAIL_F_NO_INTERRUPT; - if (!vq->event) - vq->split.vring.avail->flags = cpu_to_virtio16(vdev, - vq->split.avail_flags_shadow); + err = vring_alloc_state_extra_split(vring_split); + if (err) { + kfree(vq); + return NULL; } - vq->split.desc_state = kmalloc_array(vring.num, - sizeof(struct vring_desc_state_split), GFP_KERNEL); - if (!vq->split.desc_state) - goto err_state; - - vq->split.desc_extra = vring_alloc_desc_extra(vq, vring.num); - if (!vq->split.desc_extra) - goto err_extra; + virtqueue_vring_init_split(vring_split, vq); - /* Put everything in free lists. */ - vq->free_head = 0; - memset(vq->split.desc_state, 0, vring.num * - sizeof(struct vring_desc_state_split)); + virtqueue_init(vq, vring_split->vring.num); + virtqueue_vring_attach_split(vq, vring_split); spin_lock(&vdev->vqs_list_lock); list_add_tail(&vq->vq.list, &vdev->vqs); spin_unlock(&vdev->vqs_list_lock); return &vq->vq; - -err_extra: - kfree(vq->split.desc_state); -err_state: - kfree(vq); - return NULL; } -EXPORT_SYMBOL_GPL(__vring_new_virtqueue); struct virtqueue *vring_create_virtqueue( unsigned int index, @@ -2294,6 +2541,75 @@ struct virtqueue *vring_create_virtqueue( } EXPORT_SYMBOL_GPL(vring_create_virtqueue); +/** + * virtqueue_resize - resize the vring of vq + * @_vq: the struct virtqueue we're talking about. + * @num: new ring num + * @recycle: callback for recycle the useless buffer + * + * When it is really necessary to create a new vring, it will set the current vq + * into the reset state. Then call the passed callback to recycle the buffer + * that is no longer used. Only after the new vring is successfully created, the + * old vring will be released. + * + * Caller must ensure we don't call this with other virtqueue operations + * at the same time (except where noted). + * + * Returns zero or a negative error. + * 0: success. + * -ENOMEM: Failed to allocate a new ring, fall back to the original ring size. + * vq can still work normally + * -EBUSY: Failed to sync with device, vq may not work properly + * -ENOENT: Transport or device not supported + * -E2BIG/-EINVAL: num error + * -EPERM: Operation not permitted + * + */ +int virtqueue_resize(struct virtqueue *_vq, u32 num, + void (*recycle)(struct virtqueue *vq, void *buf)) +{ + struct vring_virtqueue *vq = to_vvq(_vq); + struct virtio_device *vdev = vq->vq.vdev; + void *buf; + int err; + + if (!vq->we_own_ring) + return -EPERM; + + if (num > vq->vq.num_max) + return -E2BIG; + + if (!num) + return -EINVAL; + + if ((vq->packed_ring ? vq->packed.vring.num : vq->split.vring.num) == num) + return 0; + + if (!vdev->config->disable_vq_and_reset) + return -ENOENT; + + if (!vdev->config->enable_vq_after_reset) + return -ENOENT; + + err = vdev->config->disable_vq_and_reset(_vq); + if (err) + return err; + + while ((buf = virtqueue_detach_unused_buf(_vq)) != NULL) + recycle(_vq, buf); + + if (vq->packed_ring) + err = virtqueue_resize_packed(_vq, num); + else + err = virtqueue_resize_split(_vq, num); + + if (vdev->config->enable_vq_after_reset(_vq)) + return -EBUSY; + + return err; +} +EXPORT_SYMBOL_GPL(virtqueue_resize); + /* Only available for split ring */ struct virtqueue *vring_new_virtqueue(unsigned int index, unsigned int num, @@ -2306,25 +2622,21 @@ struct virtqueue *vring_new_virtqueue(unsigned int index, void (*callback)(struct virtqueue *vq), const char *name) { - struct vring vring; + struct vring_virtqueue_split vring_split = {}; if (virtio_has_feature(vdev, VIRTIO_F_RING_PACKED)) return NULL; - vring_init(&vring, num, pages, vring_align); - return __vring_new_virtqueue(index, vring, vdev, weak_barriers, context, - notify, callback, name); + vring_init(&vring_split.vring, num, pages, vring_align); + return __vring_new_virtqueue(index, &vring_split, vdev, weak_barriers, + context, notify, callback, name); } EXPORT_SYMBOL_GPL(vring_new_virtqueue); -void vring_del_virtqueue(struct virtqueue *_vq) +static void vring_free(struct virtqueue *_vq) { struct vring_virtqueue *vq = to_vvq(_vq); - spin_lock(&vq->vq.vdev->vqs_list_lock); - list_del(&_vq->list); - spin_unlock(&vq->vq.vdev->vqs_list_lock); - if (vq->we_own_ring) { if (vq->packed_ring) { vring_free_queue(vq->vq.vdev, @@ -2355,6 +2667,18 @@ void vring_del_virtqueue(struct virtqueue *_vq) kfree(vq->split.desc_state); kfree(vq->split.desc_extra); } +} + +void vring_del_virtqueue(struct virtqueue *_vq) +{ + struct vring_virtqueue *vq = to_vvq(_vq); + + spin_lock(&vq->vq.vdev->vqs_list_lock); + list_del(&_vq->list); + spin_unlock(&vq->vq.vdev->vqs_list_lock); + + vring_free(_vq); + kfree(vq); } EXPORT_SYMBOL_GPL(vring_del_virtqueue); @@ -2402,6 +2726,30 @@ unsigned int virtqueue_get_vring_size(struct virtqueue *_vq) } EXPORT_SYMBOL_GPL(virtqueue_get_vring_size); +/* + * This function should only be called by the core, not directly by the driver. + */ +void __virtqueue_break(struct virtqueue *_vq) +{ + struct vring_virtqueue *vq = to_vvq(_vq); + + /* Pairs with READ_ONCE() in virtqueue_is_broken(). */ + WRITE_ONCE(vq->broken, true); +} +EXPORT_SYMBOL_GPL(__virtqueue_break); + +/* + * This function should only be called by the core, not directly by the driver. + */ +void __virtqueue_unbreak(struct virtqueue *_vq) +{ + struct vring_virtqueue *vq = to_vvq(_vq); + + /* Pairs with READ_ONCE() in virtqueue_is_broken(). */ + WRITE_ONCE(vq->broken, false); +} +EXPORT_SYMBOL_GPL(__virtqueue_unbreak); + bool virtqueue_is_broken(struct virtqueue *_vq) { struct vring_virtqueue *vq = to_vvq(_vq); diff --git a/drivers/virtio/virtio_vdpa.c b/drivers/virtio/virtio_vdpa.c index c40f7deb6b5a..9bc4d110b800 100644 --- a/drivers/virtio/virtio_vdpa.c +++ b/drivers/virtio/virtio_vdpa.c @@ -131,7 +131,7 @@ static irqreturn_t virtio_vdpa_virtqueue_cb(void *private) static struct virtqueue * virtio_vdpa_setup_vq(struct virtio_device *vdev, unsigned int index, void (*callback)(struct virtqueue *vq), - const char *name, bool ctx) + const char *name, u32 size, bool ctx) { struct virtio_vdpa_device *vd_dev = to_virtio_vdpa_device(vdev); struct vdpa_device *vdpa = vd_get_vdpa(vdev); @@ -168,14 +168,17 @@ virtio_vdpa_setup_vq(struct virtio_device *vdev, unsigned int index, goto error_new_virtqueue; } + if (!size || size > max_num) + size = max_num; + if (ops->get_vq_num_min) min_num = ops->get_vq_num_min(vdpa); - may_reduce_num = (max_num == min_num) ? false : true; + may_reduce_num = (size == min_num) ? false : true; /* Create the vring */ align = ops->get_vq_align(vdpa); - vq = vring_create_virtqueue(index, max_num, align, vdev, + vq = vring_create_virtqueue(index, size, align, vdev, true, may_reduce_num, ctx, virtio_vdpa_notify, callback, name); if (!vq) { @@ -183,6 +186,8 @@ virtio_vdpa_setup_vq(struct virtio_device *vdev, unsigned int index, goto error_new_virtqueue; } + vq->num_max = max_num; + /* Setup virtqueue callback */ cb.callback = callback ? virtio_vdpa_virtqueue_cb : NULL; cb.private = info; @@ -267,6 +272,7 @@ static int virtio_vdpa_find_vqs(struct virtio_device *vdev, unsigned int nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], const char * const names[], + u32 sizes[], const bool *ctx, struct irq_affinity *desc) { @@ -282,9 +288,9 @@ static int virtio_vdpa_find_vqs(struct virtio_device *vdev, unsigned int nvqs, continue; } - vqs[i] = virtio_vdpa_setup_vq(vdev, queue_idx++, - callbacks[i], names[i], ctx ? - ctx[i] : false); + vqs[i] = virtio_vdpa_setup_vq(vdev, queue_idx++, callbacks[i], + names[i], sizes ? sizes[i] : 0, + ctx ? ctx[i] : false); if (IS_ERR(vqs[i])) { err = PTR_ERR(vqs[i]); goto err_setup_vq; diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index 2c3a9b5b4b74..dcf701b05cc1 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -122,7 +122,7 @@ static bool ceph_dirty_folio(struct address_space *mapping, struct folio *folio) * Reference snap context in folio->private. Also set * PagePrivate so that we get invalidate_folio callback. */ - VM_BUG_ON_FOLIO(folio_test_private(folio), folio); + VM_WARN_ON_FOLIO(folio->private, folio); folio_attach_private(folio, snapc); return ceph_fscache_dirty_folio(mapping, folio); @@ -237,7 +237,7 @@ static void finish_netfs_read(struct ceph_osd_request *req) if (err >= 0 && err < subreq->len) __set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags); - netfs_subreq_terminated(subreq, err, true); + netfs_subreq_terminated(subreq, err, false); num_pages = calc_pages_for(osd_data->alignment, osd_data->length); ceph_put_page_vector(osd_data->pages, num_pages, false); @@ -313,8 +313,7 @@ static void ceph_netfs_issue_read(struct netfs_io_subrequest *subreq) int err = 0; u64 len = subreq->len; - if (ci->i_inline_version != CEPH_INLINE_NONE && - ceph_netfs_issue_op_inline(subreq)) + if (ceph_has_inline_data(ci) && ceph_netfs_issue_op_inline(subreq)) return; req = ceph_osdc_new_request(&fsc->client->osdc, &ci->i_layout, vino, subreq->start, &len, @@ -338,6 +337,7 @@ static void ceph_netfs_issue_read(struct netfs_io_subrequest *subreq) /* should always give us a page-aligned read */ WARN_ON_ONCE(page_off); len = err; + err = 0; osd_req_op_extent_osd_data_pages(req, 0, pages, len, 0, false, false); req->r_callback = finish_netfs_read; @@ -345,9 +345,7 @@ static void ceph_netfs_issue_read(struct netfs_io_subrequest *subreq) req->r_inode = inode; ihold(inode); - err = ceph_osdc_start_request(req->r_osdc, req, false); - if (err) - iput(inode); + ceph_osdc_start_request(req->r_osdc, req); out: ceph_osdc_put_request(req); if (err) @@ -621,9 +619,8 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc) dout("writepage %llu~%llu (%llu bytes)\n", page_off, len, len); req->r_mtime = inode->i_mtime; - err = ceph_osdc_start_request(osdc, req, true); - if (!err) - err = ceph_osdc_wait_request(osdc, req); + ceph_osdc_start_request(osdc, req); + err = ceph_osdc_wait_request(osdc, req); ceph_update_write_metrics(&fsc->mdsc->metric, req->r_start_latency, req->r_end_latency, len, err); @@ -1151,8 +1148,7 @@ new_request: } req->r_mtime = inode->i_mtime; - rc = ceph_osdc_start_request(&fsc->client->osdc, req, true); - BUG_ON(rc); + ceph_osdc_start_request(&fsc->client->osdc, req); req = NULL; wbc->nr_to_write -= i; @@ -1327,16 +1323,13 @@ static int ceph_write_begin(struct file *file, struct address_space *mapping, int r; r = netfs_write_begin(&ci->netfs, file, inode->i_mapping, pos, len, &folio, NULL); - if (r == 0) - folio_wait_fscache(folio); - if (r < 0) { - if (folio) - folio_put(folio); - } else { - WARN_ON_ONCE(!folio_test_locked(folio)); - *pagep = &folio->page; - } - return r; + if (r < 0) + return r; + + folio_wait_fscache(folio); + WARN_ON_ONCE(!folio_test_locked(folio)); + *pagep = &folio->page; + return 0; } /* @@ -1439,7 +1432,7 @@ static vm_fault_t ceph_filemap_fault(struct vm_fault *vmf) inode, off, ceph_cap_string(got)); if ((got & (CEPH_CAP_FILE_CACHE | CEPH_CAP_FILE_LAZYIO)) || - ci->i_inline_version == CEPH_INLINE_NONE) { + !ceph_has_inline_data(ci)) { CEPH_DEFINE_RW_CONTEXT(rw_ctx, got); ceph_add_rw_context(fi, &rw_ctx); ret = filemap_fault(vmf); @@ -1696,9 +1689,8 @@ int ceph_uninline_data(struct file *file) } req->r_mtime = inode->i_mtime; - err = ceph_osdc_start_request(&fsc->client->osdc, req, false); - if (!err) - err = ceph_osdc_wait_request(&fsc->client->osdc, req); + ceph_osdc_start_request(&fsc->client->osdc, req); + err = ceph_osdc_wait_request(&fsc->client->osdc, req); ceph_osdc_put_request(req); if (err < 0) goto out_unlock; @@ -1739,9 +1731,8 @@ int ceph_uninline_data(struct file *file) } req->r_mtime = inode->i_mtime; - err = ceph_osdc_start_request(&fsc->client->osdc, req, false); - if (!err) - err = ceph_osdc_wait_request(&fsc->client->osdc, req); + ceph_osdc_start_request(&fsc->client->osdc, req); + err = ceph_osdc_wait_request(&fsc->client->osdc, req); ceph_update_write_metrics(&fsc->mdsc->metric, req->r_start_latency, req->r_end_latency, len, err); @@ -1912,15 +1903,13 @@ static int __ceph_pool_perm_get(struct ceph_inode_info *ci, osd_req_op_raw_data_in_pages(rd_req, 0, pages, PAGE_SIZE, 0, false, true); - err = ceph_osdc_start_request(&fsc->client->osdc, rd_req, false); + ceph_osdc_start_request(&fsc->client->osdc, rd_req); wr_req->r_mtime = ci->netfs.inode.i_mtime; - err2 = ceph_osdc_start_request(&fsc->client->osdc, wr_req, false); + ceph_osdc_start_request(&fsc->client->osdc, wr_req); - if (!err) - err = ceph_osdc_wait_request(&fsc->client->osdc, rd_req); - if (!err2) - err2 = ceph_osdc_wait_request(&fsc->client->osdc, wr_req); + err = ceph_osdc_wait_request(&fsc->client->osdc, rd_req); + err2 = ceph_osdc_wait_request(&fsc->client->osdc, wr_req); if (err >= 0 || err == -ENOENT) have |= POOL_READ; diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index ac8fd5e7f540..53cfe026b3ea 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -602,8 +602,8 @@ static void __check_cap_issue(struct ceph_inode_info *ci, struct ceph_cap *cap, * @ci: inode to be moved * @session: new auth caps session */ -static void change_auth_cap_ses(struct ceph_inode_info *ci, - struct ceph_mds_session *session) +void change_auth_cap_ses(struct ceph_inode_info *ci, + struct ceph_mds_session *session) { lockdep_assert_held(&ci->i_ceph_lock); @@ -1978,14 +1978,15 @@ retry: } dout("check_caps %llx.%llx file_want %s used %s dirty %s flushing %s" - " issued %s revoking %s retain %s %s%s\n", ceph_vinop(inode), + " issued %s revoking %s retain %s %s%s%s\n", ceph_vinop(inode), ceph_cap_string(file_wanted), ceph_cap_string(used), ceph_cap_string(ci->i_dirty_caps), ceph_cap_string(ci->i_flushing_caps), ceph_cap_string(issued), ceph_cap_string(revoking), ceph_cap_string(retain), (flags & CHECK_CAPS_AUTHONLY) ? " AUTHONLY" : "", - (flags & CHECK_CAPS_FLUSH) ? " FLUSH" : ""); + (flags & CHECK_CAPS_FLUSH) ? " FLUSH" : "", + (flags & CHECK_CAPS_NOINVAL) ? " NOINVAL" : ""); /* * If we no longer need to hold onto old our caps, and we may @@ -3005,7 +3006,7 @@ int ceph_get_caps(struct file *filp, int need, int want, loff_t endoff, int *got } if (S_ISREG(ci->netfs.inode.i_mode) && - ci->i_inline_version != CEPH_INLINE_NONE && + ceph_has_inline_data(ci) && (_got & (CEPH_CAP_FILE_CACHE|CEPH_CAP_FILE_LAZYIO)) && i_size_read(inode) > 0) { struct page *page = @@ -3578,24 +3579,23 @@ static void handle_cap_grant(struct inode *inode, fill_inline = true; } - if (ci->i_auth_cap == cap && - le32_to_cpu(grant->op) == CEPH_CAP_OP_IMPORT) { - if (newcaps & ~extra_info->issued) - wake = true; + if (le32_to_cpu(grant->op) == CEPH_CAP_OP_IMPORT) { + if (ci->i_auth_cap == cap) { + if (newcaps & ~extra_info->issued) + wake = true; - if (ci->i_requested_max_size > max_size || - !(le32_to_cpu(grant->wanted) & CEPH_CAP_ANY_FILE_WR)) { - /* re-request max_size if necessary */ - ci->i_requested_max_size = 0; - wake = true; - } + if (ci->i_requested_max_size > max_size || + !(le32_to_cpu(grant->wanted) & CEPH_CAP_ANY_FILE_WR)) { + /* re-request max_size if necessary */ + ci->i_requested_max_size = 0; + wake = true; + } - ceph_kick_flushing_inode_caps(session, ci); - spin_unlock(&ci->i_ceph_lock); + ceph_kick_flushing_inode_caps(session, ci); + } up_read(&session->s_mdsc->snap_rwsem); - } else { - spin_unlock(&ci->i_ceph_lock); } + spin_unlock(&ci->i_ceph_lock); if (fill_inline) ceph_fill_inline_data(inode, NULL, extra_info->inline_data, diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index eae417d71136..e7e2ebac330d 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -856,6 +856,10 @@ static int ceph_mknod(struct user_namespace *mnt_userns, struct inode *dir, if (ceph_snap(dir) != CEPH_NOSNAP) return -EROFS; + err = ceph_wait_on_conflict_unlink(dentry); + if (err) + return err; + if (ceph_quota_is_max_files_exceeded(dir)) { err = -EDQUOT; goto out; @@ -918,6 +922,10 @@ static int ceph_symlink(struct user_namespace *mnt_userns, struct inode *dir, if (ceph_snap(dir) != CEPH_NOSNAP) return -EROFS; + err = ceph_wait_on_conflict_unlink(dentry); + if (err) + return err; + if (ceph_quota_is_max_files_exceeded(dir)) { err = -EDQUOT; goto out; @@ -968,9 +976,13 @@ static int ceph_mkdir(struct user_namespace *mnt_userns, struct inode *dir, struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(dir->i_sb); struct ceph_mds_request *req; struct ceph_acl_sec_ctx as_ctx = {}; - int err = -EROFS; + int err; int op; + err = ceph_wait_on_conflict_unlink(dentry); + if (err) + return err; + if (ceph_snap(dir) == CEPH_SNAPDIR) { /* mkdir .snap/foo is a MKSNAP */ op = CEPH_MDS_OP_MKSNAP; @@ -980,6 +992,7 @@ static int ceph_mkdir(struct user_namespace *mnt_userns, struct inode *dir, dout("mkdir dir %p dn %p mode 0%ho\n", dir, dentry, mode); op = CEPH_MDS_OP_MKDIR; } else { + err = -EROFS; goto out; } @@ -1037,6 +1050,10 @@ static int ceph_link(struct dentry *old_dentry, struct inode *dir, struct ceph_mds_request *req; int err; + err = ceph_wait_on_conflict_unlink(dentry); + if (err) + return err; + if (ceph_snap(dir) != CEPH_NOSNAP) return -EROFS; @@ -1071,9 +1088,27 @@ static int ceph_link(struct dentry *old_dentry, struct inode *dir, static void ceph_async_unlink_cb(struct ceph_mds_client *mdsc, struct ceph_mds_request *req) { + struct dentry *dentry = req->r_dentry; + struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb); + struct ceph_dentry_info *di = ceph_dentry(dentry); int result = req->r_err ? req->r_err : le32_to_cpu(req->r_reply_info.head->result); + if (!test_bit(CEPH_DENTRY_ASYNC_UNLINK_BIT, &di->flags)) + pr_warn("%s dentry %p:%pd async unlink bit is not set\n", + __func__, dentry, dentry); + + spin_lock(&fsc->async_unlink_conflict_lock); + hash_del_rcu(&di->hnode); + spin_unlock(&fsc->async_unlink_conflict_lock); + + spin_lock(&dentry->d_lock); + di->flags &= ~CEPH_DENTRY_ASYNC_UNLINK; + wake_up_bit(&di->flags, CEPH_DENTRY_ASYNC_UNLINK_BIT); + spin_unlock(&dentry->d_lock); + + synchronize_rcu(); + if (result == -EJUKEBOX) goto out; @@ -1081,7 +1116,7 @@ static void ceph_async_unlink_cb(struct ceph_mds_client *mdsc, if (result) { int pathlen = 0; u64 base = 0; - char *path = ceph_mdsc_build_path(req->r_dentry, &pathlen, + char *path = ceph_mdsc_build_path(dentry, &pathlen, &base, 0); /* mark error on parent + clear complete */ @@ -1089,13 +1124,13 @@ static void ceph_async_unlink_cb(struct ceph_mds_client *mdsc, ceph_dir_clear_complete(req->r_parent); /* drop the dentry -- we don't know its status */ - if (!d_unhashed(req->r_dentry)) - d_drop(req->r_dentry); + if (!d_unhashed(dentry)) + d_drop(dentry); /* mark inode itself for an error (since metadata is bogus) */ mapping_set_error(req->r_old_inode->i_mapping, result); - pr_warn("ceph: async unlink failure path=(%llx)%s result=%d!\n", + pr_warn("async unlink failure path=(%llx)%s result=%d!\n", base, IS_ERR(path) ? "<<bad>>" : path, result); ceph_mdsc_free_path(path, pathlen); } @@ -1180,6 +1215,8 @@ retry: if (try_async && op == CEPH_MDS_OP_UNLINK && (req->r_dir_caps = get_caps_for_async_unlink(dir, dentry))) { + struct ceph_dentry_info *di = ceph_dentry(dentry); + dout("async unlink on %llu/%.*s caps=%s", ceph_ino(dir), dentry->d_name.len, dentry->d_name.name, ceph_cap_string(req->r_dir_caps)); @@ -1187,6 +1224,16 @@ retry: req->r_callback = ceph_async_unlink_cb; req->r_old_inode = d_inode(dentry); ihold(req->r_old_inode); + + spin_lock(&dentry->d_lock); + di->flags |= CEPH_DENTRY_ASYNC_UNLINK; + spin_unlock(&dentry->d_lock); + + spin_lock(&fsc->async_unlink_conflict_lock); + hash_add_rcu(fsc->async_unlink_conflict, &di->hnode, + dentry->d_name.hash); + spin_unlock(&fsc->async_unlink_conflict_lock); + err = ceph_mdsc_submit_request(mdsc, dir, req); if (!err) { /* @@ -1195,10 +1242,20 @@ retry: */ drop_nlink(inode); d_delete(dentry); - } else if (err == -EJUKEBOX) { - try_async = false; - ceph_mdsc_put_request(req); - goto retry; + } else { + spin_lock(&fsc->async_unlink_conflict_lock); + hash_del_rcu(&di->hnode); + spin_unlock(&fsc->async_unlink_conflict_lock); + + spin_lock(&dentry->d_lock); + di->flags &= ~CEPH_DENTRY_ASYNC_UNLINK; + spin_unlock(&dentry->d_lock); + + if (err == -EJUKEBOX) { + try_async = false; + ceph_mdsc_put_request(req); + goto retry; + } } } else { set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags); @@ -1237,6 +1294,10 @@ static int ceph_rename(struct user_namespace *mnt_userns, struct inode *old_dir, (!ceph_quota_is_same_realm(old_dir, new_dir))) return -EXDEV; + err = ceph_wait_on_conflict_unlink(new_dentry); + if (err) + return err; + dout("rename dir %p dentry %p to dir %p dentry %p\n", old_dir, old_dentry, new_dir, new_dentry); req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS); diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 284d2fda663d..04fd34557de8 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -240,8 +240,7 @@ static int ceph_init_file_info(struct inode *inode, struct file *file, INIT_LIST_HEAD(&fi->rw_contexts); fi->filp_gen = READ_ONCE(ceph_inode_to_client(inode)->filp_gen); - if ((file->f_mode & FMODE_WRITE) && - ci->i_inline_version != CEPH_INLINE_NONE) { + if ((file->f_mode & FMODE_WRITE) && ceph_has_inline_data(ci)) { ret = ceph_uninline_data(file); if (ret < 0) goto error; @@ -568,7 +567,7 @@ static void ceph_async_create_cb(struct ceph_mds_client *mdsc, char *path = ceph_mdsc_build_path(req->r_dentry, &pathlen, &base, 0); - pr_warn("ceph: async create failure path=(%llx)%s result=%d!\n", + pr_warn("async create failure path=(%llx)%s result=%d!\n", base, IS_ERR(path) ? "<<bad>>" : path, result); ceph_mdsc_free_path(path, pathlen); @@ -611,6 +610,7 @@ static int ceph_finish_async_create(struct inode *dir, struct dentry *dentry, struct ceph_mds_reply_inode in = { }; struct ceph_mds_reply_info_in iinfo = { .in = &in }; struct ceph_inode_info *ci = ceph_inode(dir); + struct ceph_dentry_info *di = ceph_dentry(dentry); struct inode *inode; struct timespec64 now; struct ceph_string *pool_ns; @@ -709,6 +709,12 @@ static int ceph_finish_async_create(struct inode *dir, struct dentry *dentry, file->f_mode |= FMODE_CREATED; ret = finish_open(file, dentry, ceph_open); } + + spin_lock(&dentry->d_lock); + di->flags &= ~CEPH_DENTRY_ASYNC_CREATE; + wake_up_bit(&di->flags, CEPH_DENTRY_ASYNC_CREATE_BIT); + spin_unlock(&dentry->d_lock); + return ret; } @@ -735,6 +741,15 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry, if (dentry->d_name.len > NAME_MAX) return -ENAMETOOLONG; + err = ceph_wait_on_conflict_unlink(dentry); + if (err) + return err; + /* + * Do not truncate the file, since atomic_open is called before the + * permission check. The caller will do the truncation afterward. + */ + flags &= ~O_TRUNC; + if (flags & O_CREAT) { if (ceph_quota_is_max_files_exceeded(dir)) return -EDQUOT; @@ -781,9 +796,16 @@ retry: (req->r_dir_caps = try_prep_async_create(dir, dentry, &lo, &req->r_deleg_ino))) { + struct ceph_dentry_info *di = ceph_dentry(dentry); + set_bit(CEPH_MDS_R_ASYNC, &req->r_req_flags); req->r_args.open.flags |= cpu_to_le32(CEPH_O_EXCL); req->r_callback = ceph_async_create_cb; + + spin_lock(&dentry->d_lock); + di->flags |= CEPH_DENTRY_ASYNC_CREATE; + spin_unlock(&dentry->d_lock); + err = ceph_mdsc_submit_request(mdsc, dir, req); if (!err) { err = ceph_finish_async_create(dir, dentry, @@ -802,9 +824,7 @@ retry: } set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags); - err = ceph_mdsc_do_request(mdsc, - (flags & (O_CREAT|O_TRUNC)) ? dir : NULL, - req); + err = ceph_mdsc_do_request(mdsc, (flags & O_CREAT) ? dir : NULL, req); if (err == -ENOENT) { dentry = ceph_handle_snapdir(req, dentry); if (IS_ERR(dentry)) { @@ -960,9 +980,8 @@ static ssize_t ceph_sync_read(struct kiocb *iocb, struct iov_iter *to, osd_req_op_extent_osd_data_pages(req, 0, pages, len, page_off, false, false); - ret = ceph_osdc_start_request(osdc, req, false); - if (!ret) - ret = ceph_osdc_wait_request(osdc, req); + ceph_osdc_start_request(osdc, req); + ret = ceph_osdc_wait_request(osdc, req); ceph_update_read_metrics(&fsc->mdsc->metric, req->r_start_latency, @@ -1225,7 +1244,7 @@ static void ceph_aio_retry_work(struct work_struct *work) req->r_inode = inode; req->r_priv = aio_req; - ret = ceph_osdc_start_request(req->r_osdc, req, false); + ceph_osdc_start_request(req->r_osdc, req); out: if (ret < 0) { req->r_result = ret; @@ -1362,9 +1381,8 @@ ceph_direct_read_write(struct kiocb *iocb, struct iov_iter *iter, continue; } - ret = ceph_osdc_start_request(req->r_osdc, req, false); - if (!ret) - ret = ceph_osdc_wait_request(&fsc->client->osdc, req); + ceph_osdc_start_request(req->r_osdc, req); + ret = ceph_osdc_wait_request(&fsc->client->osdc, req); if (write) ceph_update_write_metrics(metric, req->r_start_latency, @@ -1427,8 +1445,7 @@ ceph_direct_read_write(struct kiocb *iocb, struct iov_iter *iter, r_private_item); list_del_init(&req->r_private_item); if (ret >= 0) - ret = ceph_osdc_start_request(req->r_osdc, - req, false); + ceph_osdc_start_request(req->r_osdc, req); if (ret < 0) { req->r_result = ret; ceph_aio_complete_req(req); @@ -1541,9 +1558,8 @@ ceph_sync_write(struct kiocb *iocb, struct iov_iter *from, loff_t pos, false, true); req->r_mtime = mtime; - ret = ceph_osdc_start_request(&fsc->client->osdc, req, false); - if (!ret) - ret = ceph_osdc_wait_request(&fsc->client->osdc, req); + ceph_osdc_start_request(&fsc->client->osdc, req); + ret = ceph_osdc_wait_request(&fsc->client->osdc, req); ceph_update_write_metrics(&fsc->mdsc->metric, req->r_start_latency, req->r_end_latency, len, ret); @@ -1627,7 +1643,7 @@ again: inode, ceph_vinop(inode), iocb->ki_pos, (unsigned)len, ceph_cap_string(got)); - if (ci->i_inline_version == CEPH_INLINE_NONE) { + if (!ceph_has_inline_data(ci)) { if (!retry_op && (iocb->ki_flags & IOCB_DIRECT)) { ret = ceph_direct_read_write(iocb, to, NULL, NULL); @@ -1890,7 +1906,7 @@ retry_snap: if (dirty) __mark_inode_dirty(inode, dirty); if (ceph_quota_is_max_bytes_approaching(inode, iocb->ki_pos)) - ceph_check_caps(ci, 0, NULL); + ceph_check_caps(ci, CHECK_CAPS_FLUSH, NULL); } dout("aio_write %p %llx.%llx %llu~%u dropping cap refs on %s\n", @@ -1930,57 +1946,15 @@ out_unlocked: */ static loff_t ceph_llseek(struct file *file, loff_t offset, int whence) { - struct inode *inode = file->f_mapping->host; - struct ceph_fs_client *fsc = ceph_inode_to_client(inode); - loff_t i_size; - loff_t ret; - - inode_lock(inode); - if (whence == SEEK_END || whence == SEEK_DATA || whence == SEEK_HOLE) { + struct inode *inode = file_inode(file); + int ret; + ret = ceph_do_getattr(inode, CEPH_STAT_CAP_SIZE, false); if (ret < 0) - goto out; - } - - i_size = i_size_read(inode); - switch (whence) { - case SEEK_END: - offset += i_size; - break; - case SEEK_CUR: - /* - * Here we special-case the lseek(fd, 0, SEEK_CUR) - * position-querying operation. Avoid rewriting the "same" - * f_pos value back to the file because a concurrent read(), - * write() or lseek() might have altered it - */ - if (offset == 0) { - ret = file->f_pos; - goto out; - } - offset += file->f_pos; - break; - case SEEK_DATA: - if (offset < 0 || offset >= i_size) { - ret = -ENXIO; - goto out; - } - break; - case SEEK_HOLE: - if (offset < 0 || offset >= i_size) { - ret = -ENXIO; - goto out; - } - offset = i_size; - break; + return ret; } - - ret = vfs_setpos(file, offset, max(i_size, fsc->max_file_size)); - -out: - inode_unlock(inode); - return ret; + return generic_file_llseek(file, offset, whence); } static inline void ceph_zero_partial_page( @@ -2049,12 +2023,10 @@ static int ceph_zero_partial_object(struct inode *inode, } req->r_mtime = inode->i_mtime; - ret = ceph_osdc_start_request(&fsc->client->osdc, req, false); - if (!ret) { - ret = ceph_osdc_wait_request(&fsc->client->osdc, req); - if (ret == -ENOENT) - ret = 0; - } + ceph_osdc_start_request(&fsc->client->osdc, req); + ret = ceph_osdc_wait_request(&fsc->client->osdc, req); + if (ret == -ENOENT) + ret = 0; ceph_osdc_put_request(req); out: @@ -2356,7 +2328,7 @@ static ssize_t ceph_do_objects_copy(struct ceph_inode_info *src_ci, u64 *src_off if (IS_ERR(req)) ret = PTR_ERR(req); else { - ceph_osdc_start_request(osdc, req, false); + ceph_osdc_start_request(osdc, req); ret = ceph_osdc_wait_request(osdc, req); ceph_update_copyfrom_metrics(&fsc->mdsc->metric, req->r_start_latency, @@ -2549,7 +2521,8 @@ static ssize_t __ceph_copy_file_range(struct file *src_file, loff_t src_off, /* Let the MDS know about dst file size change */ if (ceph_inode_set_size(dst_inode, dst_off) || ceph_quota_is_max_bytes_approaching(dst_inode, dst_off)) - ceph_check_caps(dst_ci, CHECK_CAPS_AUTHONLY, NULL); + ceph_check_caps(dst_ci, CHECK_CAPS_AUTHONLY | CHECK_CAPS_FLUSH, + NULL); } /* Mark Fw dirty */ spin_lock(&dst_ci->i_ceph_lock); diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 56c53ab3618e..42351d7a0dd6 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -1049,7 +1049,7 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page, iinfo->inline_version >= ci->i_inline_version) { int cache_caps = CEPH_CAP_FILE_CACHE | CEPH_CAP_FILE_LAZYIO; ci->i_inline_version = iinfo->inline_version; - if (ci->i_inline_version != CEPH_INLINE_NONE && + if (ceph_has_inline_data(ci) && (locked_page || (info_caps & cache_caps))) fill_inline = true; } @@ -2275,9 +2275,15 @@ int ceph_try_to_choose_auth_mds(struct inode *inode, int mask) * * This cost much when doing the Locker state transition and * usually will need to revoke caps from clients. + * + * And for the 'Xs' caps for getxattr we will also choose the + * auth MDS, because the MDS side code is buggy due to setxattr + * won't notify the replica MDSes when the values changed and + * the replica MDS will return the old values. Though we will + * fix it in MDS code, but this still makes sense for old ceph. */ if (((mask & CEPH_CAP_ANY_SHARED) && (issued & CEPH_CAP_ANY_EXCL)) - || (mask & CEPH_STAT_RSTAT)) + || (mask & (CEPH_STAT_RSTAT | CEPH_STAT_CAP_XATTR))) return USE_AUTH_MDS; else return USE_ANY_MDS; @@ -2321,7 +2327,8 @@ int __ceph_do_getattr(struct inode *inode, struct page *locked_page, if (inline_version == 0) { /* the reply is supposed to contain inline data */ err = -EINVAL; - } else if (inline_version == CEPH_INLINE_NONE) { + } else if (inline_version == CEPH_INLINE_NONE || + inline_version == 1) { err = -ENODATA; } else { err = req->r_reply_info.targeti.inline_len; diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 33f517d549ce..80f8b9ec1a31 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -456,7 +456,7 @@ static int ceph_parse_deleg_inos(void **p, void *end, dout("added delegated inode 0x%llx\n", start - 1); } else if (err == -EBUSY) { - pr_warn("ceph: MDS delegated inode 0x%llx more than once.\n", + pr_warn("MDS delegated inode 0x%llx more than once.\n", start - 1); } else { return err; @@ -655,6 +655,79 @@ static void destroy_reply_info(struct ceph_mds_reply_info_parsed *info) free_pages((unsigned long)info->dir_entries, get_order(info->dir_buf_size)); } +/* + * In async unlink case the kclient won't wait for the first reply + * from MDS and just drop all the links and unhash the dentry and then + * succeeds immediately. + * + * For any new create/link/rename,etc requests followed by using the + * same file names we must wait for the first reply of the inflight + * unlink request, or the MDS possibly will fail these following + * requests with -EEXIST if the inflight async unlink request was + * delayed for some reasons. + * + * And the worst case is that for the none async openc request it will + * successfully open the file if the CDentry hasn't been unlinked yet, + * but later the previous delayed async unlink request will remove the + * CDenty. That means the just created file is possiblly deleted later + * by accident. + * + * We need to wait for the inflight async unlink requests to finish + * when creating new files/directories by using the same file names. + */ +int ceph_wait_on_conflict_unlink(struct dentry *dentry) +{ + struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb); + struct dentry *pdentry = dentry->d_parent; + struct dentry *udentry, *found = NULL; + struct ceph_dentry_info *di; + struct qstr dname; + u32 hash = dentry->d_name.hash; + int err; + + dname.name = dentry->d_name.name; + dname.len = dentry->d_name.len; + + rcu_read_lock(); + hash_for_each_possible_rcu(fsc->async_unlink_conflict, di, + hnode, hash) { + udentry = di->dentry; + + spin_lock(&udentry->d_lock); + if (udentry->d_name.hash != hash) + goto next; + if (unlikely(udentry->d_parent != pdentry)) + goto next; + if (!hash_hashed(&di->hnode)) + goto next; + + if (!test_bit(CEPH_DENTRY_ASYNC_UNLINK_BIT, &di->flags)) + pr_warn("%s dentry %p:%pd async unlink bit is not set\n", + __func__, dentry, dentry); + + if (!d_same_name(udentry, pdentry, &dname)) + goto next; + + spin_unlock(&udentry->d_lock); + found = dget(udentry); + break; +next: + spin_unlock(&udentry->d_lock); + } + rcu_read_unlock(); + + if (likely(!found)) + return 0; + + dout("%s dentry %p:%pd conflict with old %p:%pd\n", __func__, + dentry, dentry, found, found); + + err = wait_on_bit(&di->flags, CEPH_DENTRY_ASYNC_UNLINK_BIT, + TASK_KILLABLE); + dput(found); + return err; +} + /* * sessions @@ -1220,14 +1293,17 @@ static int encode_supported_features(void **p, void *end) if (count > 0) { size_t i; size_t size = FEATURE_BYTES(count); + unsigned long bit; if (WARN_ON_ONCE(*p + 4 + size > end)) return -ERANGE; ceph_encode_32(p, size); memset(*p, 0, size); - for (i = 0; i < count; i++) - ((unsigned char*)(*p))[i / 8] |= BIT(feature_bits[i] % 8); + for (i = 0; i < count; i++) { + bit = feature_bits[i]; + ((unsigned char *)(*p))[bit / 8] |= BIT(bit % 8); + } *p += size; } else { if (WARN_ON_ONCE(*p + 4 > end)) @@ -2884,6 +2960,64 @@ static void __do_request(struct ceph_mds_client *mdsc, if (req->r_request_started == 0) /* note request start time */ req->r_request_started = jiffies; + /* + * For async create we will choose the auth MDS of frag in parent + * directory to send the request and ususally this works fine, but + * if the migrated the dirtory to another MDS before it could handle + * it the request will be forwarded. + * + * And then the auth cap will be changed. + */ + if (test_bit(CEPH_MDS_R_ASYNC, &req->r_req_flags) && req->r_num_fwd) { + struct ceph_dentry_info *di = ceph_dentry(req->r_dentry); + struct ceph_inode_info *ci; + struct ceph_cap *cap; + + /* + * The request maybe handled very fast and the new inode + * hasn't been linked to the dentry yet. We need to wait + * for the ceph_finish_async_create(), which shouldn't be + * stuck too long or fail in thoery, to finish when forwarding + * the request. + */ + if (!d_inode(req->r_dentry)) { + err = wait_on_bit(&di->flags, CEPH_DENTRY_ASYNC_CREATE_BIT, + TASK_KILLABLE); + if (err) { + mutex_lock(&req->r_fill_mutex); + set_bit(CEPH_MDS_R_ABORTED, &req->r_req_flags); + mutex_unlock(&req->r_fill_mutex); + goto out_session; + } + } + + ci = ceph_inode(d_inode(req->r_dentry)); + + spin_lock(&ci->i_ceph_lock); + cap = ci->i_auth_cap; + if (ci->i_ceph_flags & CEPH_I_ASYNC_CREATE && mds != cap->mds) { + dout("do_request session changed for auth cap %d -> %d\n", + cap->session->s_mds, session->s_mds); + + /* Remove the auth cap from old session */ + spin_lock(&cap->session->s_cap_lock); + cap->session->s_nr_caps--; + list_del_init(&cap->session_caps); + spin_unlock(&cap->session->s_cap_lock); + + /* Add the auth cap to the new session */ + cap->mds = mds; + cap->session = session; + spin_lock(&session->s_cap_lock); + session->s_nr_caps++; + list_add_tail(&cap->session_caps, &session->s_caps); + spin_unlock(&session->s_cap_lock); + + change_auth_cap_ses(ci, session); + } + spin_unlock(&ci->i_ceph_lock); + } + err = __send_request(session, req, false); out_session: @@ -3464,11 +3598,26 @@ static void handle_session(struct ceph_mds_session *session, case CEPH_SESSION_OPEN: if (session->s_state == CEPH_MDS_SESSION_RECONNECTING) pr_info("mds%d reconnect success\n", session->s_mds); - session->s_state = CEPH_MDS_SESSION_OPEN; - session->s_features = features; - renewed_caps(mdsc, session, 0); - if (test_bit(CEPHFS_FEATURE_METRIC_COLLECT, &session->s_features)) - metric_schedule_delayed(&mdsc->metric); + + if (session->s_state == CEPH_MDS_SESSION_OPEN) { + pr_notice("mds%d is already opened\n", session->s_mds); + } else { + session->s_state = CEPH_MDS_SESSION_OPEN; + session->s_features = features; + renewed_caps(mdsc, session, 0); + if (test_bit(CEPHFS_FEATURE_METRIC_COLLECT, + &session->s_features)) + metric_schedule_delayed(&mdsc->metric); + } + + /* + * The connection maybe broken and the session in client + * side has been reinitialized, need to update the seq + * anyway. + */ + if (!session->s_seq && seq) + session->s_seq = seq; + wake = 1; if (mdsc->stopping) __close_session(mdsc, session); diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index 1140aecd82ce..256e3eada6c1 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -29,14 +29,12 @@ enum ceph_feature_type { CEPHFS_FEATURE_MULTI_RECONNECT, CEPHFS_FEATURE_DELEG_INO, CEPHFS_FEATURE_METRIC_COLLECT, + CEPHFS_FEATURE_ALTERNATE_NAME, + CEPHFS_FEATURE_NOTIFY_SESSION_STATE, - CEPHFS_FEATURE_MAX = CEPHFS_FEATURE_METRIC_COLLECT, + CEPHFS_FEATURE_MAX = CEPHFS_FEATURE_NOTIFY_SESSION_STATE, }; -/* - * This will always have the highest feature bit value - * as the last element of the array. - */ #define CEPHFS_FEATURES_CLIENT_SUPPORTED { \ 0, 1, 2, 3, 4, 5, 6, 7, \ CEPHFS_FEATURE_MIMIC, \ @@ -45,10 +43,8 @@ enum ceph_feature_type { CEPHFS_FEATURE_MULTI_RECONNECT, \ CEPHFS_FEATURE_DELEG_INO, \ CEPHFS_FEATURE_METRIC_COLLECT, \ - \ - CEPHFS_FEATURE_MAX, \ + CEPHFS_FEATURE_NOTIFY_SESSION_STATE, \ } -#define CEPHFS_FEATURES_CLIENT_REQUIRED {} /* * Some lock dependencies: @@ -582,6 +578,7 @@ static inline int ceph_wait_on_async_create(struct inode *inode) TASK_KILLABLE); } +extern int ceph_wait_on_conflict_unlink(struct dentry *dentry); extern u64 ceph_get_deleg_ino(struct ceph_mds_session *session); extern int ceph_restore_deleg_ino(struct ceph_mds_session *session, u64 ino); #endif diff --git a/fs/ceph/mdsmap.c b/fs/ceph/mdsmap.c index 30387733765d..8d0a6d2c2da4 100644 --- a/fs/ceph/mdsmap.c +++ b/fs/ceph/mdsmap.c @@ -352,12 +352,10 @@ struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end, bool msgr2) __decode_and_drop_type(p, end, u8, bad_ext); } if (mdsmap_ev >= 8) { - u32 name_len; /* enabled */ ceph_decode_8_safe(p, end, m->m_enabled, bad_ext); - ceph_decode_32_safe(p, end, name_len, bad_ext); - ceph_decode_need(p, end, name_len, bad_ext); - *p += name_len; + /* fs_name */ + ceph_decode_skip_string(p, end, bad_ext); } /* damaged */ if (mdsmap_ev >= 9) { @@ -370,6 +368,22 @@ struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end, bool msgr2) } else { m->m_damaged = false; } + if (mdsmap_ev >= 17) { + /* balancer */ + ceph_decode_skip_string(p, end, bad_ext); + /* standby_count_wanted */ + ceph_decode_skip_32(p, end, bad_ext); + /* old_max_mds */ + ceph_decode_skip_32(p, end, bad_ext); + /* min_compat_client */ + ceph_decode_skip_8(p, end, bad_ext); + /* required_client_features */ + ceph_decode_skip_set(p, end, 64, bad_ext); + ceph_decode_64_safe(p, end, m->m_max_xattr_size, bad_ext); + } else { + /* This forces the usage of the (sync) SETXATTR Op */ + m->m_max_xattr_size = 0; + } bad_ext: dout("mdsmap_decode m_enabled: %d, m_damaged: %d, m_num_laggy: %d\n", !!m->m_enabled, !!m->m_damaged, m->m_num_laggy); diff --git a/fs/ceph/super.c b/fs/ceph/super.c index 40140805bdcf..3fc48b43cab0 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -72,15 +72,9 @@ static int ceph_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_type = CEPH_SUPER_MAGIC; /* ?? */ /* - * express utilization in terms of large blocks to avoid + * Express utilization in terms of large blocks to avoid * overflow on 32-bit machines. - * - * NOTE: for the time being, we make bsize == frsize to humor - * not-yet-ancient versions of glibc that are broken. - * Someday, we will probably want to report a real block - * size... whatever that may mean for a network file system! */ - buf->f_bsize = 1 << CEPH_BLOCK_SHIFT; buf->f_frsize = 1 << CEPH_BLOCK_SHIFT; /* @@ -95,6 +89,14 @@ static int ceph_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_bavail = le64_to_cpu(st.kb_avail) >> (CEPH_BLOCK_SHIFT-10); } + /* + * NOTE: for the time being, we make bsize == frsize to humor + * not-yet-ancient versions of glibc that are broken. + * Someday, we will probably want to report a real block + * size... whatever that may mean for a network file system! + */ + buf->f_bsize = buf->f_frsize; + buf->f_files = le64_to_cpu(st.num_objects); buf->f_ffree = -1; buf->f_namelen = NAME_MAX; @@ -816,6 +818,9 @@ static struct ceph_fs_client *create_fs_client(struct ceph_mount_options *fsopt, if (!fsc->cap_wq) goto fail_inode_wq; + hash_init(fsc->async_unlink_conflict); + spin_lock_init(&fsc->async_unlink_conflict_lock); + spin_lock(&ceph_fsc_lock); list_add_tail(&fsc->metric_wakeup, &ceph_fsc_list); spin_unlock(&ceph_fsc_lock); diff --git a/fs/ceph/super.h b/fs/ceph/super.h index f59dac66955b..40630e6f691c 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -19,6 +19,7 @@ #include <linux/security.h> #include <linux/netfs.h> #include <linux/fscache.h> +#include <linux/hashtable.h> #include <linux/ceph/libceph.h> @@ -99,6 +100,8 @@ struct ceph_mount_options { char *mon_addr; }; +#define CEPH_ASYNC_CREATE_CONFLICT_BITS 8 + struct ceph_fs_client { struct super_block *sb; @@ -124,6 +127,9 @@ struct ceph_fs_client { struct workqueue_struct *inode_wq; struct workqueue_struct *cap_wq; + DECLARE_HASHTABLE(async_unlink_conflict, CEPH_ASYNC_CREATE_CONFLICT_BITS); + spinlock_t async_unlink_conflict_lock; + #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_dentry_lru, *debugfs_caps; struct dentry *debugfs_congestion_kb; @@ -280,7 +286,8 @@ struct ceph_dentry_info { struct dentry *dentry; struct ceph_mds_session *lease_session; struct list_head lease_list; - unsigned flags; + struct hlist_node hnode; + unsigned long flags; int lease_shared_gen; u32 lease_gen; u32 lease_seq; @@ -289,10 +296,14 @@ struct ceph_dentry_info { u64 offset; }; -#define CEPH_DENTRY_REFERENCED 1 -#define CEPH_DENTRY_LEASE_LIST 2 -#define CEPH_DENTRY_SHRINK_LIST 4 -#define CEPH_DENTRY_PRIMARY_LINK 8 +#define CEPH_DENTRY_REFERENCED (1 << 0) +#define CEPH_DENTRY_LEASE_LIST (1 << 1) +#define CEPH_DENTRY_SHRINK_LIST (1 << 2) +#define CEPH_DENTRY_PRIMARY_LINK (1 << 3) +#define CEPH_DENTRY_ASYNC_UNLINK_BIT (4) +#define CEPH_DENTRY_ASYNC_UNLINK (1 << CEPH_DENTRY_ASYNC_UNLINK_BIT) +#define CEPH_DENTRY_ASYNC_CREATE_BIT (5) +#define CEPH_DENTRY_ASYNC_CREATE (1 << CEPH_DENTRY_ASYNC_CREATE_BIT) struct ceph_inode_xattrs_info { /* @@ -758,6 +769,8 @@ extern void ceph_unreserve_caps(struct ceph_mds_client *mdsc, extern void ceph_reservation_status(struct ceph_fs_client *client, int *total, int *avail, int *used, int *reserved, int *min); +extern void change_auth_cap_ses(struct ceph_inode_info *ci, + struct ceph_mds_session *session); @@ -1218,6 +1231,14 @@ extern int ceph_pool_perm_check(struct inode *inode, int need); extern void ceph_pool_perm_destroy(struct ceph_mds_client* mdsc); int ceph_purge_inode_cap(struct inode *inode, struct ceph_cap *cap, bool *invalidate); +static inline bool ceph_has_inline_data(struct ceph_inode_info *ci) +{ + if (ci->i_inline_version == CEPH_INLINE_NONE || + ci->i_inline_version == 1) /* initial version, no data */ + return false; + return true; +} + /* file.c */ extern const struct file_operations ceph_file_fops; diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index f141f5246163..f31350cda960 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c @@ -1086,7 +1086,7 @@ static int ceph_sync_setxattr(struct inode *inode, const char *name, flags |= CEPH_XATTR_REMOVE; } - dout("setxattr value=%.*s\n", (int)size, value); + dout("setxattr value size: %zu\n", size); /* do request */ req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS); @@ -1184,8 +1184,14 @@ int __ceph_setxattr(struct inode *inode, const char *name, spin_lock(&ci->i_ceph_lock); retry: issued = __ceph_caps_issued(ci, NULL); - if (ci->i_xattrs.version == 0 || !(issued & CEPH_CAP_XATTR_EXCL)) + required_blob_size = __get_required_blob_size(ci, name_len, val_len); + if ((ci->i_xattrs.version == 0) || !(issued & CEPH_CAP_XATTR_EXCL) || + (required_blob_size > mdsc->mdsmap->m_max_xattr_size)) { + dout("%s do sync setxattr: version: %llu size: %d max: %llu\n", + __func__, ci->i_xattrs.version, required_blob_size, + mdsc->mdsmap->m_max_xattr_size); goto do_sync; + } if (!lock_snap_rwsem && !ci->i_head_snapc) { lock_snap_rwsem = true; @@ -1201,8 +1207,6 @@ retry: ceph_cap_string(issued)); __build_xattrs(inode); - required_blob_size = __get_required_blob_size(ci, name_len, val_len); - if (!ci->i_xattrs.prealloc_blob || required_blob_size > ci->i_xattrs.prealloc_blob->alloc_len) { struct ceph_buffer *blob; diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index 14e0ef5e9a20..12bd61d20f69 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -86,7 +86,8 @@ static inline bool fscrypt_is_dot_dotdot(const struct qstr *str) /** * fscrypt_fname_encrypt() - encrypt a filename * @inode: inode of the parent directory (for regular filenames) - * or of the symlink (for symlink targets) + * or of the symlink (for symlink targets). Key must already be + * set up. * @iname: the filename to encrypt * @out: (output) the encrypted filename * @olen: size of the encrypted filename. It must be at least @iname->len. @@ -137,6 +138,7 @@ int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname, return 0; } +EXPORT_SYMBOL_GPL(fscrypt_fname_encrypt); /** * fname_decrypt() - decrypt a filename @@ -264,9 +266,9 @@ static int fscrypt_base64url_decode(const char *src, int srclen, u8 *dst) return bp - dst; } -bool fscrypt_fname_encrypted_size(const union fscrypt_policy *policy, - u32 orig_len, u32 max_len, - u32 *encrypted_len_ret) +bool __fscrypt_fname_encrypted_size(const union fscrypt_policy *policy, + u32 orig_len, u32 max_len, + u32 *encrypted_len_ret) { int padding = 4 << (fscrypt_policy_flags(policy) & FSCRYPT_POLICY_FLAGS_PAD_MASK); @@ -281,6 +283,29 @@ bool fscrypt_fname_encrypted_size(const union fscrypt_policy *policy, } /** + * fscrypt_fname_encrypted_size() - calculate length of encrypted filename + * @inode: parent inode of dentry name being encrypted. Key must + * already be set up. + * @orig_len: length of the original filename + * @max_len: maximum length to return + * @encrypted_len_ret: where calculated length should be returned (on success) + * + * Filenames that are shorter than the maximum length may have their lengths + * increased slightly by encryption, due to padding that is applied. + * + * Return: false if the orig_len is greater than max_len. Otherwise, true and + * fill out encrypted_len_ret with the length (up to max_len). + */ +bool fscrypt_fname_encrypted_size(const struct inode *inode, u32 orig_len, + u32 max_len, u32 *encrypted_len_ret) +{ + return __fscrypt_fname_encrypted_size(&inode->i_crypt_info->ci_policy, + orig_len, max_len, + encrypted_len_ret); +} +EXPORT_SYMBOL_GPL(fscrypt_fname_encrypted_size); + +/** * fscrypt_fname_alloc_buffer() - allocate a buffer for presented filenames * @max_encrypted_len: maximum length of encrypted filenames the buffer will be * used to present @@ -435,8 +460,7 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, return ret; if (fscrypt_has_encryption_key(dir)) { - if (!fscrypt_fname_encrypted_size(&dir->i_crypt_info->ci_policy, - iname->len, NAME_MAX, + if (!fscrypt_fname_encrypted_size(dir, iname->len, NAME_MAX, &fname->crypto_buf.len)) return -ENAMETOOLONG; fname->crypto_buf.name = kmalloc(fname->crypto_buf.len, diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index f5be777d8279..3afdaa084773 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -297,14 +297,11 @@ void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num, const struct fscrypt_info *ci); /* fname.c */ -int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname, - u8 *out, unsigned int olen); -bool fscrypt_fname_encrypted_size(const union fscrypt_policy *policy, - u32 orig_len, u32 max_len, - u32 *encrypted_len_ret); +bool __fscrypt_fname_encrypted_size(const union fscrypt_policy *policy, + u32 orig_len, u32 max_len, + u32 *encrypted_len_ret); /* hkdf.c */ - struct fscrypt_hkdf { struct crypto_shash *hmac_tfm; }; diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c index af74599ae1cf..7c01025879b3 100644 --- a/fs/crypto/hooks.c +++ b/fs/crypto/hooks.c @@ -228,9 +228,9 @@ int fscrypt_prepare_symlink(struct inode *dir, const char *target, * counting it (even though it is meaningless for ciphertext) is simpler * for now since filesystems will assume it is there and subtract it. */ - if (!fscrypt_fname_encrypted_size(policy, len, - max_len - sizeof(struct fscrypt_symlink_data), - &disk_link->len)) + if (!__fscrypt_fname_encrypted_size(policy, len, + max_len - sizeof(struct fscrypt_symlink_data), + &disk_link->len)) return -ENAMETOOLONG; disk_link->len += sizeof(struct fscrypt_symlink_data); diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index 8a054e6d1e68..80b8ca0f340b 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -694,6 +694,32 @@ const union fscrypt_policy *fscrypt_policy_to_inherit(struct inode *dir) } /** + * fscrypt_context_for_new_inode() - create an encryption context for a new inode + * @ctx: where context should be written + * @inode: inode from which to fetch policy and nonce + * + * Given an in-core "prepared" (via fscrypt_prepare_new_inode) inode, + * generate a new context and write it to ctx. ctx _must_ be at least + * FSCRYPT_SET_CONTEXT_MAX_SIZE bytes. + * + * Return: size of the resulting context or a negative error code. + */ +int fscrypt_context_for_new_inode(void *ctx, struct inode *inode) +{ + struct fscrypt_info *ci = inode->i_crypt_info; + + BUILD_BUG_ON(sizeof(union fscrypt_context) != + FSCRYPT_SET_CONTEXT_MAX_SIZE); + + /* fscrypt_prepare_new_inode() should have set up the key already. */ + if (WARN_ON_ONCE(!ci)) + return -ENOKEY; + + return fscrypt_new_context(ctx, &ci->ci_policy, ci->ci_nonce); +} +EXPORT_SYMBOL_GPL(fscrypt_context_for_new_inode); + +/** * fscrypt_set_context() - Set the fscrypt context of a new inode * @inode: a new inode * @fs_data: private data given by FS and passed to ->set_context() @@ -709,12 +735,9 @@ int fscrypt_set_context(struct inode *inode, void *fs_data) union fscrypt_context ctx; int ctxsize; - /* fscrypt_prepare_new_inode() should have set up the key already. */ - if (WARN_ON_ONCE(!ci)) - return -ENOKEY; - - BUILD_BUG_ON(sizeof(ctx) != FSCRYPT_SET_CONTEXT_MAX_SIZE); - ctxsize = fscrypt_new_context(&ctx, &ci->ci_policy, ci->ci_nonce); + ctxsize = fscrypt_context_for_new_inode(&ctx, inode); + if (ctxsize < 0) + return ctxsize; /* * This may be the first time the inode number is available, so do any diff --git a/fs/dcache.c b/fs/dcache.c index ea5cdec24ea7..c5dc32a59c76 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -2248,10 +2248,16 @@ struct dentry *d_add_ci(struct dentry *dentry, struct inode *inode, } EXPORT_SYMBOL(d_add_ci); - -static inline bool d_same_name(const struct dentry *dentry, - const struct dentry *parent, - const struct qstr *name) +/** + * d_same_name - compare dentry name with case-exact name + * @parent: parent dentry + * @dentry: the negative dentry that was passed to the parent's lookup func + * @name: the case-exact name to be associated with the returned dentry + * + * Return: true if names are same, or false + */ +bool d_same_name(const struct dentry *dentry, const struct dentry *parent, + const struct qstr *name) { if (likely(!(parent->d_flags & DCACHE_OP_COMPARE))) { if (dentry->d_name.len != name->len) @@ -2262,6 +2268,7 @@ static inline bool d_same_name(const struct dentry *dentry, dentry->d_name.len, dentry->d_name.name, name) == 0; } +EXPORT_SYMBOL_GPL(d_same_name); /** * __d_lookup_rcu - search for a dentry (racy, store-free) diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index 57ff883d432c..05bee80ac7de 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -82,31 +82,6 @@ static int gfs2_get_block_noalloc(struct inode *inode, sector_t lblock, } /** - * gfs2_writepage - Write page for writeback mappings - * @page: The page - * @wbc: The writeback control - */ -static int gfs2_writepage(struct page *page, struct writeback_control *wbc) -{ - struct inode *inode = page->mapping->host; - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_sbd *sdp = GFS2_SB(inode); - struct iomap_writepage_ctx wpc = { }; - - if (gfs2_assert_withdraw(sdp, gfs2_glock_is_held_excl(ip->i_gl))) - goto out; - if (current->journal_info) - goto redirty; - return iomap_writepage(page, wbc, &wpc, &gfs2_writeback_ops); - -redirty: - redirty_page_for_writepage(wbc, page); -out: - unlock_page(page); - return 0; -} - -/** * gfs2_write_jdata_page - gfs2 jdata-specific version of block_write_full_page * @page: The page to write * @wbc: The writeback control @@ -765,7 +740,6 @@ cannot_release: } static const struct address_space_operations gfs2_aops = { - .writepage = gfs2_writepage, .writepages = gfs2_writepages, .read_folio = gfs2_read_folio, .readahead = gfs2_readahead, diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c index eec4159b08aa..723639376ae2 100644 --- a/fs/gfs2/log.c +++ b/fs/gfs2/log.c @@ -131,7 +131,7 @@ __acquires(&sdp->sd_ail_lock) if (!mapping) continue; spin_unlock(&sdp->sd_ail_lock); - ret = generic_writepages(mapping, wbc); + ret = filemap_fdatawrite_wbc(mapping, wbc); if (need_resched()) { blk_finish_plug(plug); cond_resched(); @@ -222,8 +222,7 @@ out: spin_unlock(&sdp->sd_ail_lock); blk_finish_plug(&plug); if (ret) { - gfs2_lm(sdp, "gfs2_ail1_start_one (generic_writepages) " - "returned: %d\n", ret); + gfs2_lm(sdp, "gfs2_ail1_start_one returned: %d\n", ret); gfs2_withdraw(sdp); } trace_gfs2_ail_flush(sdp, wbc, 0); diff --git a/fs/inode.c b/fs/inode.c index 9c3cd540c665..6462276dfdf0 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -422,6 +422,7 @@ void inode_init_once(struct inode *inode) INIT_LIST_HEAD(&inode->i_io_list); INIT_LIST_HEAD(&inode->i_wb_list); INIT_LIST_HEAD(&inode->i_lru); + INIT_LIST_HEAD(&inode->i_sb_list); __address_space_init_once(&inode->i_data); i_size_ordered_init(inode); } @@ -1021,7 +1022,6 @@ struct inode *new_inode_pseudo(struct super_block *sb) spin_lock(&inode->i_lock); inode->i_state = 0; spin_unlock(&inode->i_lock); - INIT_LIST_HEAD(&inode->i_sb_list); } return inode; } @@ -1165,7 +1165,6 @@ struct inode *inode_insert5(struct inode *inode, unsigned long hashval, { struct hlist_head *head = inode_hashtable + hash(inode->i_sb, hashval); struct inode *old; - bool creating = inode->i_state & I_CREATING; again: spin_lock(&inode_hash_lock); @@ -1199,7 +1198,12 @@ again: inode->i_state |= I_NEW; hlist_add_head_rcu(&inode->i_hash, head); spin_unlock(&inode->i_lock); - if (!creating) + + /* + * Add inode to the sb list if it's not already. It has I_NEW at this + * point, so it should be safe to test i_sb_list locklessly. + */ + if (list_empty(&inode->i_sb_list)) inode_sb_list_add(inode); unlock: spin_unlock(&inode_hash_lock); diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c index 2b82c7f1de88..ca5c62901541 100644 --- a/fs/iomap/buffered-io.c +++ b/fs/iomap/buffered-io.c @@ -1529,21 +1529,6 @@ unlock: } int -iomap_writepage(struct page *page, struct writeback_control *wbc, - struct iomap_writepage_ctx *wpc, - const struct iomap_writeback_ops *ops) -{ - int ret; - - wpc->ops = ops; - ret = iomap_do_writepage(page, wbc, wpc); - if (!wpc->ioend) - return ret; - return iomap_submit_ioend(wpc, wpc->ioend, ret); -} -EXPORT_SYMBOL_GPL(iomap_writepage); - -int iomap_writepages(struct address_space *mapping, struct writeback_control *wbc, struct iomap_writepage_ctx *wpc, const struct iomap_writeback_ops *ops) diff --git a/fs/xfs/libxfs/xfs_trans_resv.c b/fs/xfs/libxfs/xfs_trans_resv.c index e9913c2c5a24..2c4ad6e4bb14 100644 --- a/fs/xfs/libxfs/xfs_trans_resv.c +++ b/fs/xfs/libxfs/xfs_trans_resv.c @@ -515,7 +515,7 @@ xfs_calc_remove_reservation( { return XFS_DQUOT_LOGRES(mp) + xfs_calc_iunlink_add_reservation(mp) + - max((xfs_calc_inode_res(mp, 1) + + max((xfs_calc_inode_res(mp, 2) + xfs_calc_buf_res(XFS_DIROP_LOG_COUNT(mp), XFS_FSB_TO_B(mp, 1))), (xfs_calc_buf_res(4, mp->m_sb.sb_sectsize) + diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index aa7e458ab169..c6c80265c0b2 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -143,7 +143,7 @@ xfs_file_fsync( { struct xfs_inode *ip = XFS_I(file->f_mapping->host); struct xfs_mount *mp = ip->i_mount; - int error = 0; + int error, err2; int log_flushed = 0; trace_xfs_file_fsync(ip); @@ -164,18 +164,21 @@ xfs_file_fsync( * inode size in case of an extending write. */ if (XFS_IS_REALTIME_INODE(ip)) - blkdev_issue_flush(mp->m_rtdev_targp->bt_bdev); + error = blkdev_issue_flush(mp->m_rtdev_targp->bt_bdev); else if (mp->m_logdev_targp != mp->m_ddev_targp) - blkdev_issue_flush(mp->m_ddev_targp->bt_bdev); + error = blkdev_issue_flush(mp->m_ddev_targp->bt_bdev); /* * Any inode that has dirty modifications in the log is pinned. The - * racy check here for a pinned inode while not catch modifications + * racy check here for a pinned inode will not catch modifications * that happen concurrently to the fsync call, but fsync semantics * only require to sync previously completed I/O. */ - if (xfs_ipincount(ip)) - error = xfs_fsync_flush_log(ip, datasync, &log_flushed); + if (xfs_ipincount(ip)) { + err2 = xfs_fsync_flush_log(ip, datasync, &log_flushed); + if (err2 && !error) + error = err2; + } /* * If we only have a single device, and the log force about was @@ -185,8 +188,11 @@ xfs_file_fsync( * commit. */ if (!log_flushed && !XFS_IS_REALTIME_INODE(ip) && - mp->m_logdev_targp == mp->m_ddev_targp) - blkdev_issue_flush(mp->m_ddev_targp->bt_bdev); + mp->m_logdev_targp == mp->m_ddev_targp) { + err2 = blkdev_issue_flush(mp->m_ddev_targp->bt_bdev); + if (err2 && !error) + error = err2; + } return error; } diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c index 4b1c0a9c6368..386b0307aed8 100644 --- a/fs/xfs/xfs_log.c +++ b/fs/xfs/xfs_log.c @@ -1925,9 +1925,17 @@ xlog_write_iclog( * device cache first to ensure all metadata writeback covered * by the LSN in this iclog is on stable storage. This is slow, * but it *must* complete before we issue the external log IO. + * + * If the flush fails, we cannot conclude that past metadata + * writeback from the log succeeded. Repeating the flush is + * not possible, hence we must shut down with log IO error to + * avoid shutdown re-entering this path and erroring out again. */ - if (log->l_targ != log->l_mp->m_ddev_targp) - blkdev_issue_flush(log->l_mp->m_ddev_targp->bt_bdev); + if (log->l_targ != log->l_mp->m_ddev_targp && + blkdev_issue_flush(log->l_mp->m_ddev_targp->bt_bdev)) { + xlog_force_shutdown(log, SHUTDOWN_LOG_IO_ERROR); + return; + } } if (iclog->ic_flags & XLOG_ICL_NEED_FUA) iclog->ic_bio.bi_opf |= REQ_FUA; diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index fbff7924ff3f..18bb4ec4d7c9 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -1235,6 +1235,11 @@ xfs_qm_flush_one( if (error) goto out_unlock; + if (!(bp->b_flags & _XBF_DELWRI_Q)) { + error = -EAGAIN; + xfs_buf_relse(bp); + goto out_unlock; + } xfs_buf_unlock(bp); xfs_buf_delwri_pushbuf(bp, buffer_list); diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c index e17a84e8b527..251f20ddd368 100644 --- a/fs/xfs/xfs_reflink.c +++ b/fs/xfs/xfs_reflink.c @@ -341,9 +341,41 @@ xfs_find_trim_cow_extent( return 0; } -/* Allocate all CoW reservations covering a range of blocks in a file. */ -int -xfs_reflink_allocate_cow( +static int +xfs_reflink_convert_unwritten( + struct xfs_inode *ip, + struct xfs_bmbt_irec *imap, + struct xfs_bmbt_irec *cmap, + bool convert_now) +{ + xfs_fileoff_t offset_fsb = imap->br_startoff; + xfs_filblks_t count_fsb = imap->br_blockcount; + int error; + + /* + * cmap might larger than imap due to cowextsize hint. + */ + xfs_trim_extent(cmap, offset_fsb, count_fsb); + + /* + * COW fork extents are supposed to remain unwritten until we're ready + * to initiate a disk write. For direct I/O we are going to write the + * data and need the conversion, but for buffered writes we're done. + */ + if (!convert_now || cmap->br_state == XFS_EXT_NORM) + return 0; + + trace_xfs_reflink_convert_cow(ip, cmap); + + error = xfs_reflink_convert_cow_locked(ip, offset_fsb, count_fsb); + if (!error) + cmap->br_state = XFS_EXT_NORM; + + return error; +} + +static int +xfs_reflink_fill_cow_hole( struct xfs_inode *ip, struct xfs_bmbt_irec *imap, struct xfs_bmbt_irec *cmap, @@ -352,25 +384,12 @@ xfs_reflink_allocate_cow( bool convert_now) { struct xfs_mount *mp = ip->i_mount; - xfs_fileoff_t offset_fsb = imap->br_startoff; - xfs_filblks_t count_fsb = imap->br_blockcount; struct xfs_trans *tp; - int nimaps, error = 0; - bool found; xfs_filblks_t resaligned; - xfs_extlen_t resblks = 0; - - ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); - if (!ip->i_cowfp) { - ASSERT(!xfs_is_reflink_inode(ip)); - xfs_ifork_init_cow(ip); - } - - error = xfs_find_trim_cow_extent(ip, imap, cmap, shared, &found); - if (error || !*shared) - return error; - if (found) - goto convert; + xfs_extlen_t resblks; + int nimaps; + int error; + bool found; resaligned = xfs_aligned_fsb_count(imap->br_startoff, imap->br_blockcount, xfs_get_cowextsz_hint(ip)); @@ -386,17 +405,17 @@ xfs_reflink_allocate_cow( *lockmode = XFS_ILOCK_EXCL; - /* - * Check for an overlapping extent again now that we dropped the ilock. - */ error = xfs_find_trim_cow_extent(ip, imap, cmap, shared, &found); if (error || !*shared) goto out_trans_cancel; + if (found) { xfs_trans_cancel(tp); goto convert; } + ASSERT(cmap->br_startoff > imap->br_startoff); + /* Allocate the entire reservation as unwritten blocks. */ nimaps = 1; error = xfs_bmapi_write(tp, ip, imap->br_startoff, imap->br_blockcount, @@ -416,26 +435,135 @@ xfs_reflink_allocate_cow( */ if (nimaps == 0) return -ENOSPC; + convert: - xfs_trim_extent(cmap, offset_fsb, count_fsb); - /* - * COW fork extents are supposed to remain unwritten until we're ready - * to initiate a disk write. For direct I/O we are going to write the - * data and need the conversion, but for buffered writes we're done. - */ - if (!convert_now || cmap->br_state == XFS_EXT_NORM) - return 0; - trace_xfs_reflink_convert_cow(ip, cmap); - error = xfs_reflink_convert_cow_locked(ip, offset_fsb, count_fsb); - if (!error) - cmap->br_state = XFS_EXT_NORM; + return xfs_reflink_convert_unwritten(ip, imap, cmap, convert_now); + +out_trans_cancel: + xfs_trans_cancel(tp); return error; +} + +static int +xfs_reflink_fill_delalloc( + struct xfs_inode *ip, + struct xfs_bmbt_irec *imap, + struct xfs_bmbt_irec *cmap, + bool *shared, + uint *lockmode, + bool convert_now) +{ + struct xfs_mount *mp = ip->i_mount; + struct xfs_trans *tp; + int nimaps; + int error; + bool found; + + do { + xfs_iunlock(ip, *lockmode); + *lockmode = 0; + + error = xfs_trans_alloc_inode(ip, &M_RES(mp)->tr_write, 0, 0, + false, &tp); + if (error) + return error; + + *lockmode = XFS_ILOCK_EXCL; + + error = xfs_find_trim_cow_extent(ip, imap, cmap, shared, + &found); + if (error || !*shared) + goto out_trans_cancel; + + if (found) { + xfs_trans_cancel(tp); + break; + } + + ASSERT(isnullstartblock(cmap->br_startblock) || + cmap->br_startblock == DELAYSTARTBLOCK); + + /* + * Replace delalloc reservation with an unwritten extent. + */ + nimaps = 1; + error = xfs_bmapi_write(tp, ip, cmap->br_startoff, + cmap->br_blockcount, + XFS_BMAPI_COWFORK | XFS_BMAPI_PREALLOC, 0, + cmap, &nimaps); + if (error) + goto out_trans_cancel; + + xfs_inode_set_cowblocks_tag(ip); + error = xfs_trans_commit(tp); + if (error) + return error; + + /* + * Allocation succeeded but the requested range was not even + * partially satisfied? Bail out! + */ + if (nimaps == 0) + return -ENOSPC; + } while (cmap->br_startoff + cmap->br_blockcount <= imap->br_startoff); + + return xfs_reflink_convert_unwritten(ip, imap, cmap, convert_now); out_trans_cancel: xfs_trans_cancel(tp); return error; } +/* Allocate all CoW reservations covering a range of blocks in a file. */ +int +xfs_reflink_allocate_cow( + struct xfs_inode *ip, + struct xfs_bmbt_irec *imap, + struct xfs_bmbt_irec *cmap, + bool *shared, + uint *lockmode, + bool convert_now) +{ + int error; + bool found; + + ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); + if (!ip->i_cowfp) { + ASSERT(!xfs_is_reflink_inode(ip)); + xfs_ifork_init_cow(ip); + } + + error = xfs_find_trim_cow_extent(ip, imap, cmap, shared, &found); + if (error || !*shared) + return error; + + /* CoW fork has a real extent */ + if (found) + return xfs_reflink_convert_unwritten(ip, imap, cmap, + convert_now); + + /* + * CoW fork does not have an extent and data extent is shared. + * Allocate a real extent in the CoW fork. + */ + if (cmap->br_startoff > imap->br_startoff) + return xfs_reflink_fill_cow_hole(ip, imap, cmap, shared, + lockmode, convert_now); + + /* + * CoW fork has a delalloc reservation. Replace it with a real extent. + * There may or may not be a data fork mapping. + */ + if (isnullstartblock(cmap->br_startblock) || + cmap->br_startblock == DELAYSTARTBLOCK) + return xfs_reflink_fill_delalloc(ip, imap, cmap, shared, + lockmode, convert_now); + + /* Shouldn't get here. */ + ASSERT(0); + return -EFSCORRUPTED; +} + /* * Cancel CoW reservations for some block range of an inode. * diff --git a/fs/zonefs/super.c b/fs/zonefs/super.c index 511bb9fa3750..860f0b1032c6 100644 --- a/fs/zonefs/super.c +++ b/fs/zonefs/super.c @@ -231,13 +231,6 @@ static const struct iomap_writeback_ops zonefs_writeback_ops = { .map_blocks = zonefs_write_map_blocks, }; -static int zonefs_writepage(struct page *page, struct writeback_control *wbc) -{ - struct iomap_writepage_ctx wpc = { }; - - return iomap_writepage(page, wbc, &wpc, &zonefs_writeback_ops); -} - static int zonefs_writepages(struct address_space *mapping, struct writeback_control *wbc) { @@ -265,7 +258,6 @@ static int zonefs_swap_activate(struct swap_info_struct *sis, static const struct address_space_operations zonefs_file_aops = { .read_folio = zonefs_read_folio, .readahead = zonefs_readahead, - .writepage = zonefs_writepage, .writepages = zonefs_writepages, .dirty_folio = filemap_dirty_folio, .release_folio = iomap_release_folio, diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 48f0fd499274..e7d27373ff71 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -344,8 +344,9 @@ struct acpi_device_physical_node { struct acpi_device_properties { const guid_t *guid; - const union acpi_object *properties; + union acpi_object *properties; struct list_head list; + void **bufs; }; /* ACPI Device Specific Data (_DSD) */ diff --git a/include/dt-bindings/reset/sama7g5-reset.h b/include/dt-bindings/reset/sama7g5-reset.h new file mode 100644 index 000000000000..2116f41d04e0 --- /dev/null +++ b/include/dt-bindings/reset/sama7g5-reset.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ + +#ifndef __DT_BINDINGS_RESET_SAMA7G5_H +#define __DT_BINDINGS_RESET_SAMA7G5_H + +#define SAMA7G5_RESET_USB_PHY1 4 +#define SAMA7G5_RESET_USB_PHY2 5 +#define SAMA7G5_RESET_USB_PHY3 6 + +#endif /* __DT_BINDINGS_RESET_SAMA7G5_H */ diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 7ee10b2848d5..6f64b2f3dc54 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -1251,7 +1251,7 @@ static inline bool acpi_dev_has_props(const struct acpi_device *adev) struct acpi_device_properties * acpi_data_add_props(struct acpi_device_data *data, const guid_t *guid, - const union acpi_object *properties); + union acpi_object *properties); int acpi_node_prop_get(const struct fwnode_handle *fwnode, const char *propname, void **valptr); diff --git a/include/linux/audit.h b/include/linux/audit.h index 00f7a80f1a3e..3608992848d3 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -285,7 +285,6 @@ static inline int audit_signal_info(int sig, struct task_struct *t) /* These are defined in auditsc.c */ /* Public API */ extern int audit_alloc(struct task_struct *task); -extern int audit_alloc_kernel(struct task_struct *task); extern void __audit_free(struct task_struct *task); extern void __audit_uring_entry(u8 op); extern void __audit_uring_exit(int success, long code); @@ -578,10 +577,6 @@ static inline int audit_alloc(struct task_struct *task) { return 0; } -static inline int audit_alloc_kernel(struct task_struct *task) -{ - return 0; -} static inline void audit_free(struct task_struct *task) { } static inline void audit_uring_entry(u8 op) diff --git a/include/linux/bpfptr.h b/include/linux/bpfptr.h index 46e1757d06a3..79b2f78eec1a 100644 --- a/include/linux/bpfptr.h +++ b/include/linux/bpfptr.h @@ -49,7 +49,9 @@ static inline void bpfptr_add(bpfptr_t *bpfptr, size_t val) static inline int copy_from_bpfptr_offset(void *dst, bpfptr_t src, size_t offset, size_t size) { - return copy_from_sockptr_offset(dst, (sockptr_t) src, offset, size); + if (!bpfptr_is_kernel(src)) + return copy_from_user(dst, src.user + offset, size); + return copy_from_kernel_nofault(dst, src.kernel + offset, size); } static inline int copy_from_bpfptr(void *dst, bpfptr_t src, size_t size) @@ -78,7 +80,9 @@ static inline void *kvmemdup_bpfptr(bpfptr_t src, size_t len) static inline long strncpy_from_bpfptr(char *dst, bpfptr_t src, size_t count) { - return strncpy_from_sockptr(dst, (sockptr_t) src, count); + if (bpfptr_is_kernel(src)) + return strncpy_from_kernel_nofault(dst, src.kernel, count); + return strncpy_from_user(dst, src.user, count); } #endif /* _LINUX_BPFPTR_H */ diff --git a/include/linux/ceph/ceph_fs.h b/include/linux/ceph/ceph_fs.h index 86bf82dbd8b8..49586ff26152 100644 --- a/include/linux/ceph/ceph_fs.h +++ b/include/linux/ceph/ceph_fs.h @@ -433,9 +433,9 @@ union ceph_mds_request_args { __le32 stripe_unit; /* layout for newly created file */ __le32 stripe_count; /* ... */ __le32 object_size; - __le32 file_replication; - __le32 mask; /* CEPH_CAP_* */ - __le32 old_size; + __le32 pool; + __le32 mask; /* CEPH_CAP_* */ + __le64 old_size; } __attribute__ ((packed)) open; struct { __le32 flags; @@ -768,7 +768,7 @@ struct ceph_mds_caps { __le32 xattr_len; __le64 xattr_version; - /* filelock */ + /* a union of non-export and export bodies. */ __le64 size, max_size, truncate_size; __le32 truncate_seq; struct ceph_timespec mtime, atime, ctime; diff --git a/include/linux/ceph/mdsmap.h b/include/linux/ceph/mdsmap.h index 523fd0452856..4c3e0648dc27 100644 --- a/include/linux/ceph/mdsmap.h +++ b/include/linux/ceph/mdsmap.h @@ -25,6 +25,7 @@ struct ceph_mdsmap { u32 m_session_timeout; /* seconds */ u32 m_session_autoclose; /* seconds */ u64 m_max_file_size; + u64 m_max_xattr_size; /* maximum size for xattrs blob */ u32 m_max_mds; /* expected up:active mds number */ u32 m_num_active_mds; /* actual up:active mds number */ u32 possible_max_rank; /* possible max rank index */ diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index cba8a6ffc329..fb6be72104df 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -507,9 +507,8 @@ extern struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *, extern void ceph_osdc_get_request(struct ceph_osd_request *req); extern void ceph_osdc_put_request(struct ceph_osd_request *req); -extern int ceph_osdc_start_request(struct ceph_osd_client *osdc, - struct ceph_osd_request *req, - bool nofail); +void ceph_osdc_start_request(struct ceph_osd_client *osdc, + struct ceph_osd_request *req); extern void ceph_osdc_cancel_request(struct ceph_osd_request *req); extern int ceph_osdc_wait_request(struct ceph_osd_client *osdc, struct ceph_osd_request *req); diff --git a/include/linux/dcache.h b/include/linux/dcache.h index c73e5e327e76..92c78ed02b54 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -233,6 +233,8 @@ extern struct dentry * d_alloc_parallel(struct dentry *, const struct qstr *, wait_queue_head_t *); extern struct dentry * d_splice_alias(struct inode *, struct dentry *); extern struct dentry * d_add_ci(struct dentry *, struct inode *, struct qstr *); +extern bool d_same_name(const struct dentry *dentry, const struct dentry *parent, + const struct qstr *name); extern struct dentry * d_exact_alias(struct dentry *, struct inode *); extern struct dentry *d_find_any_alias(struct inode *inode); extern struct dentry * d_obtain_alias(struct inode *); diff --git a/include/linux/fs.h b/include/linux/fs.h index 5113f65c786f..9eced4cc286e 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -340,17 +340,12 @@ enum rw_hint { struct kiocb { struct file *ki_filp; - - /* The 'ki_filp' pointer is shared in a union for aio */ - randomized_struct_fields_start - loff_t ki_pos; void (*ki_complete)(struct kiocb *iocb, long ret); void *private; int ki_flags; u16 ki_ioprio; /* See linux/ioprio.h */ struct wait_page_queue *ki_waitq; /* for async buffered IO */ - randomized_struct_fields_end }; static inline bool is_sync_kiocb(struct kiocb *kiocb) diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index e60d57c99cb6..7d2f1e0f23b1 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -284,6 +284,7 @@ int fscrypt_ioctl_get_policy(struct file *filp, void __user *arg); int fscrypt_ioctl_get_policy_ex(struct file *filp, void __user *arg); int fscrypt_ioctl_get_nonce(struct file *filp, void __user *arg); int fscrypt_has_permitted_context(struct inode *parent, struct inode *child); +int fscrypt_context_for_new_inode(void *ctx, struct inode *inode); int fscrypt_set_context(struct inode *inode, void *fs_data); struct fscrypt_dummy_policy { @@ -327,6 +328,10 @@ void fscrypt_free_inode(struct inode *inode); int fscrypt_drop_inode(struct inode *inode); /* fname.c */ +int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname, + u8 *out, unsigned int olen); +bool fscrypt_fname_encrypted_size(const struct inode *inode, u32 orig_len, + u32 max_len, u32 *encrypted_len_ret); int fscrypt_setup_filename(struct inode *inode, const struct qstr *iname, int lookup, struct fscrypt_name *fname); diff --git a/include/linux/io_uring_types.h b/include/linux/io_uring_types.h index f7fab3758cb9..677a25d44d7f 100644 --- a/include/linux/io_uring_types.h +++ b/include/linux/io_uring_types.h @@ -491,7 +491,14 @@ struct io_cmd_data { __u8 data[56]; }; -#define io_kiocb_to_cmd(req) ((void *) &(req)->cmd) +static inline void io_kiocb_cmd_sz_check(size_t cmd_sz) +{ + BUILD_BUG_ON(cmd_sz > sizeof(struct io_cmd_data)); +} +#define io_kiocb_to_cmd(req, cmd_type) ( \ + io_kiocb_cmd_sz_check(sizeof(cmd_type)) , \ + ((cmd_type *)&(req)->cmd) \ +) #define cmd_to_io_kiocb(ptr) ((struct io_kiocb *) ptr) struct io_kiocb { diff --git a/include/linux/iomap.h b/include/linux/iomap.h index 25ac28175e4f..238a03087e17 100644 --- a/include/linux/iomap.h +++ b/include/linux/iomap.h @@ -297,9 +297,6 @@ void iomap_finish_ioends(struct iomap_ioend *ioend, int error); void iomap_ioend_try_merge(struct iomap_ioend *ioend, struct list_head *more_ioends); void iomap_sort_ioends(struct list_head *ioend_list); -int iomap_writepage(struct page *page, struct writeback_control *wbc, - struct iomap_writepage_ctx *wpc, - const struct iomap_writeback_ops *ops); int iomap_writepages(struct address_space *mapping, struct writeback_control *wbc, struct iomap_writepage_ctx *wpc, const struct iomap_writeback_ops *ops); diff --git a/include/linux/mlx5/mlx5_ifc_vdpa.h b/include/linux/mlx5/mlx5_ifc_vdpa.h index 4414ed5b6ed2..9becdc3fa503 100644 --- a/include/linux/mlx5/mlx5_ifc_vdpa.h +++ b/include/linux/mlx5/mlx5_ifc_vdpa.h @@ -150,6 +150,14 @@ enum { MLX5_VIRTIO_NET_Q_OBJECT_STATE_ERR = 0x3, }; +/* This indicates that the object was not created or has already + * been desroyed. It is very safe to assume that this object will never + * have so many states + */ +enum { + MLX5_VIRTIO_NET_Q_OBJECT_NONE = 0xffffffff +}; + enum { MLX5_RQTC_LIST_Q_TYPE_RQ = 0x0, MLX5_RQTC_LIST_Q_TYPE_VIRTIO_NET_Q = 0x1, diff --git a/include/linux/mmdebug.h b/include/linux/mmdebug.h index d7285f8148a3..15ae78cd2853 100644 --- a/include/linux/mmdebug.h +++ b/include/linux/mmdebug.h @@ -54,6 +54,15 @@ void dump_mm(const struct mm_struct *mm); } \ unlikely(__ret_warn_once); \ }) +#define VM_WARN_ON_FOLIO(cond, folio) ({ \ + int __ret_warn = !!(cond); \ + \ + if (unlikely(__ret_warn)) { \ + dump_page(&folio->page, "VM_WARN_ON_FOLIO(" __stringify(cond)")");\ + WARN_ON(1); \ + } \ + unlikely(__ret_warn); \ +}) #define VM_WARN_ON_ONCE_FOLIO(cond, folio) ({ \ static bool __section(".data.once") __warned; \ int __ret_warn_once = !!(cond); \ @@ -79,6 +88,7 @@ void dump_mm(const struct mm_struct *mm); #define VM_WARN_ON(cond) BUILD_BUG_ON_INVALID(cond) #define VM_WARN_ON_ONCE(cond) BUILD_BUG_ON_INVALID(cond) #define VM_WARN_ON_ONCE_PAGE(cond, page) BUILD_BUG_ON_INVALID(cond) +#define VM_WARN_ON_FOLIO(cond, folio) BUILD_BUG_ON_INVALID(cond) #define VM_WARN_ON_ONCE_FOLIO(cond, folio) BUILD_BUG_ON_INVALID(cond) #define VM_WARN_ONCE(cond, format...) BUILD_BUG_ON_INVALID(cond) #define VM_WARN(cond, format...) BUILD_BUG_ON_INVALID(cond) diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index 7c943f0a2fc4..aea79c77db0f 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -597,7 +597,7 @@ struct rproc_subdev { /** * struct rproc_vring - remoteproc vring state * @va: virtual address - * @len: length, in bytes + * @num: vring size * @da: device address * @align: vring alignment * @notifyid: rproc-specific unique vring index @@ -606,7 +606,7 @@ struct rproc_subdev { */ struct rproc_vring { void *va; - int len; + int num; u32 da; u32 align; int notifyid; diff --git a/include/linux/skmsg.h b/include/linux/skmsg.h index 153b6dec9b6a..48f4b645193b 100644 --- a/include/linux/skmsg.h +++ b/include/linux/skmsg.h @@ -278,7 +278,8 @@ static inline void sk_msg_sg_copy_clear(struct sk_msg *msg, u32 start) static inline struct sk_psock *sk_psock(const struct sock *sk) { - return rcu_dereference_sk_user_data(sk); + return __rcu_dereference_sk_user_data_with_flags(sk, + SK_USER_DATA_PSOCK); } static inline void sk_psock_set_state(struct sk_psock *psock, diff --git a/include/linux/vdpa.h b/include/linux/vdpa.h index 7b4a13d3bd91..d282f464d2f1 100644 --- a/include/linux/vdpa.h +++ b/include/linux/vdpa.h @@ -218,6 +218,9 @@ struct vdpa_map_file { * @reset: Reset device * @vdev: vdpa device * Returns integer: success (0) or error (< 0) + * @suspend: Suspend or resume the device (optional) + * @vdev: vdpa device + * Returns integer: success (0) or error (< 0) * @get_config_size: Get the size of the configuration space includes * fields that are conditional on feature bits. * @vdev: vdpa device @@ -319,6 +322,7 @@ struct vdpa_config_ops { u8 (*get_status)(struct vdpa_device *vdev); void (*set_status)(struct vdpa_device *vdev, u8 status); int (*reset)(struct vdpa_device *vdev); + int (*suspend)(struct vdpa_device *vdev); size_t (*get_config_size)(struct vdpa_device *vdev); void (*get_config)(struct vdpa_device *vdev, unsigned int offset, void *buf, unsigned int len); diff --git a/include/linux/virtio.h b/include/linux/virtio.h index d8fdf170637c..a3f73bb6733e 100644 --- a/include/linux/virtio.h +++ b/include/linux/virtio.h @@ -19,6 +19,8 @@ * @priv: a pointer for the virtqueue implementation to use. * @index: the zero-based ordinal number for this queue. * @num_free: number of elements we expect to be able to fit. + * @num_max: the maximum number of elements supported by the device. + * @reset: vq is in reset state or not. * * A note on @num_free: with indirect buffers, each buffer needs one * element in the queue, otherwise a buffer will need one element per @@ -31,7 +33,9 @@ struct virtqueue { struct virtio_device *vdev; unsigned int index; unsigned int num_free; + unsigned int num_max; void *priv; + bool reset; }; int virtqueue_add_outbuf(struct virtqueue *vq, @@ -89,6 +93,9 @@ dma_addr_t virtqueue_get_desc_addr(struct virtqueue *vq); dma_addr_t virtqueue_get_avail_addr(struct virtqueue *vq); dma_addr_t virtqueue_get_used_addr(struct virtqueue *vq); +int virtqueue_resize(struct virtqueue *vq, u32 num, + void (*recycle)(struct virtqueue *vq, void *buf)); + /** * virtio_device - representation of a device using virtio * @index: unique position on the virtio bus @@ -133,6 +140,9 @@ bool is_virtio_device(struct device *dev); void virtio_break_device(struct virtio_device *dev); void __virtio_unbreak_device(struct virtio_device *dev); +void __virtqueue_break(struct virtqueue *_vq); +void __virtqueue_unbreak(struct virtqueue *_vq); + void virtio_config_changed(struct virtio_device *dev); #ifdef CONFIG_PM_SLEEP int virtio_device_freeze(struct virtio_device *dev); diff --git a/include/linux/virtio_config.h b/include/linux/virtio_config.h index b47c2e7ed0ee..6adff09f7170 100644 --- a/include/linux/virtio_config.h +++ b/include/linux/virtio_config.h @@ -55,6 +55,7 @@ struct virtio_shm_region { * include a NULL entry for vqs that do not need a callback * names: array of virtqueue names (mainly for debugging) * include a NULL entry for vqs unused by driver + * sizes: array of virtqueue sizes * Returns 0 on success or error status * @del_vqs: free virtqueues found by find_vqs(). * @synchronize_cbs: synchronize with the virtqueue callbacks (optional) @@ -78,6 +79,18 @@ struct virtio_shm_region { * @set_vq_affinity: set the affinity for a virtqueue (optional). * @get_vq_affinity: get the affinity for a virtqueue (optional). * @get_shm_region: get a shared memory region based on the index. + * @disable_vq_and_reset: reset a queue individually (optional). + * vq: the virtqueue + * Returns 0 on success or error status + * disable_vq_and_reset will guarantee that the callbacks are disabled and + * synchronized. + * Except for the callback, the caller should guarantee that the vring is + * not accessed by any functions of virtqueue. + * @enable_vq_after_reset: enable a reset queue + * vq: the virtqueue + * Returns 0 on success or error status + * If disable_vq_and_reset is set, then enable_vq_after_reset must also be + * set. */ typedef void vq_callback_t(struct virtqueue *); struct virtio_config_ops { @@ -91,7 +104,9 @@ struct virtio_config_ops { void (*reset)(struct virtio_device *vdev); int (*find_vqs)(struct virtio_device *, unsigned nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], - const char * const names[], const bool *ctx, + const char * const names[], + u32 sizes[], + const bool *ctx, struct irq_affinity *desc); void (*del_vqs)(struct virtio_device *); void (*synchronize_cbs)(struct virtio_device *); @@ -104,6 +119,8 @@ struct virtio_config_ops { int index); bool (*get_shm_region)(struct virtio_device *vdev, struct virtio_shm_region *region, u8 id); + int (*disable_vq_and_reset)(struct virtqueue *vq); + int (*enable_vq_after_reset)(struct virtqueue *vq); }; /* If driver didn't advertise the feature, it will never appear. */ @@ -198,7 +215,7 @@ struct virtqueue *virtio_find_single_vq(struct virtio_device *vdev, const char *names[] = { n }; struct virtqueue *vq; int err = vdev->config->find_vqs(vdev, 1, &vq, callbacks, names, NULL, - NULL); + NULL, NULL); if (err < 0) return ERR_PTR(err); return vq; @@ -210,7 +227,8 @@ int virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs, const char * const names[], struct irq_affinity *desc) { - return vdev->config->find_vqs(vdev, nvqs, vqs, callbacks, names, NULL, desc); + return vdev->config->find_vqs(vdev, nvqs, vqs, callbacks, names, NULL, + NULL, desc); } static inline @@ -219,8 +237,20 @@ int virtio_find_vqs_ctx(struct virtio_device *vdev, unsigned nvqs, const char * const names[], const bool *ctx, struct irq_affinity *desc) { - return vdev->config->find_vqs(vdev, nvqs, vqs, callbacks, names, ctx, - desc); + return vdev->config->find_vqs(vdev, nvqs, vqs, callbacks, names, NULL, + ctx, desc); +} + +static inline +int virtio_find_vqs_ctx_size(struct virtio_device *vdev, u32 nvqs, + struct virtqueue *vqs[], + vq_callback_t *callbacks[], + const char * const names[], + u32 sizes[], + const bool *ctx, struct irq_affinity *desc) +{ + return vdev->config->find_vqs(vdev, nvqs, vqs, callbacks, names, sizes, + ctx, desc); } /** diff --git a/include/linux/virtio_pci_modern.h b/include/linux/virtio_pci_modern.h index eb2bd9b4077d..c4eeb79b0139 100644 --- a/include/linux/virtio_pci_modern.h +++ b/include/linux/virtio_pci_modern.h @@ -5,6 +5,13 @@ #include <linux/pci.h> #include <linux/virtio_pci.h> +struct virtio_pci_modern_common_cfg { + struct virtio_pci_common_cfg cfg; + + __le16 queue_notify_data; /* read-write */ + __le16 queue_reset; /* read-write */ +}; + struct virtio_pci_modern_device { struct pci_dev *pci_dev; @@ -106,4 +113,6 @@ void __iomem * vp_modern_map_vq_notify(struct virtio_pci_modern_device *mdev, u16 index, resource_size_t *pa); int vp_modern_probe(struct virtio_pci_modern_device *mdev); void vp_modern_remove(struct virtio_pci_modern_device *mdev); +int vp_modern_get_queue_reset(struct virtio_pci_modern_device *mdev, u16 index); +void vp_modern_set_queue_reset(struct virtio_pci_modern_device *mdev, u16 index); #endif diff --git a/include/linux/virtio_ring.h b/include/linux/virtio_ring.h index b485b13fa50b..8b8af1a38991 100644 --- a/include/linux/virtio_ring.h +++ b/include/linux/virtio_ring.h @@ -76,16 +76,6 @@ struct virtqueue *vring_create_virtqueue(unsigned int index, void (*callback)(struct virtqueue *vq), const char *name); -/* Creates a virtqueue with a custom layout. */ -struct virtqueue *__vring_new_virtqueue(unsigned int index, - struct vring vring, - struct virtio_device *vdev, - bool weak_barriers, - bool ctx, - bool (*notify)(struct virtqueue *), - void (*callback)(struct virtqueue *), - const char *name); - /* * Creates a virtqueue with a standard layout but a caller-allocated * ring. diff --git a/include/net/ax88796.h b/include/net/ax88796.h index b658471f97f0..303100f08ab8 100644 --- a/include/net/ax88796.h +++ b/include/net/ax88796.h @@ -34,8 +34,8 @@ struct ax_plat_data { const unsigned char *buf, int star_page); void (*block_input)(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset); - /* returns nonzero if a pending interrupt request might by caused by - * the ax88786. Handles all interrupts if set to NULL + /* returns nonzero if a pending interrupt request might be caused by + * the ax88796. Handles all interrupts if set to NULL */ int (*check_irq)(struct platform_device *pdev); }; diff --git a/include/net/bonding.h b/include/net/bonding.h index 6e78d657aa05..afd606df149a 100644 --- a/include/net/bonding.h +++ b/include/net/bonding.h @@ -161,8 +161,9 @@ struct slave { struct net_device *dev; /* first - useful for panic debug */ struct bonding *bond; /* our master */ int delay; - /* all three in jiffies */ + /* all 4 in jiffies */ unsigned long last_link_up; + unsigned long last_tx; unsigned long last_rx; unsigned long target_last_arp_rx[BOND_MAX_ARP_TARGETS]; s8 link; /* one of BOND_LINK_XXXX */ @@ -540,6 +541,16 @@ static inline unsigned long slave_last_rx(struct bonding *bond, return slave->last_rx; } +static inline void slave_update_last_tx(struct slave *slave) +{ + WRITE_ONCE(slave->last_tx, jiffies); +} + +static inline unsigned long slave_last_tx(struct slave *slave) +{ + return READ_ONCE(slave->last_tx); +} + #ifdef CONFIG_NET_POLL_CONTROLLER static inline netdev_tx_t bond_netpoll_send_skb(const struct slave *slave, struct sk_buff *skb) diff --git a/include/net/genetlink.h b/include/net/genetlink.h index 7cb3fa8310ed..56a50e1c51b9 100644 --- a/include/net/genetlink.h +++ b/include/net/genetlink.h @@ -11,6 +11,7 @@ /** * struct genl_multicast_group - generic netlink multicast group * @name: name of the multicast group, names are per-family + * @flags: GENL_* flags (%GENL_ADMIN_PERM or %GENL_UNS_ADMIN_PERM) */ struct genl_multicast_group { char name[GENL_NAMSIZ]; @@ -116,7 +117,7 @@ enum genl_validate_flags { * struct genl_small_ops - generic netlink operations (small version) * @cmd: command identifier * @internal_flags: flags used by the family - * @flags: flags + * @flags: GENL_* flags (%GENL_ADMIN_PERM or %GENL_UNS_ADMIN_PERM) * @validate: validation flags from enum genl_validate_flags * @doit: standard command callback * @dumpit: callback for dumpers @@ -137,7 +138,7 @@ struct genl_small_ops { * struct genl_ops - generic netlink operations * @cmd: command identifier * @internal_flags: flags used by the family - * @flags: flags + * @flags: GENL_* flags (%GENL_ADMIN_PERM or %GENL_UNS_ADMIN_PERM) * @maxattr: maximum number of attributes supported * @policy: netlink policy (takes precedence over family policy) * @validate: validation flags from enum genl_validate_flags diff --git a/include/net/mptcp.h b/include/net/mptcp.h index ac9cf7271d46..412479ebf5ad 100644 --- a/include/net/mptcp.h +++ b/include/net/mptcp.h @@ -291,4 +291,8 @@ struct mptcp_sock *bpf_mptcp_sock_from_subflow(struct sock *sk); static inline struct mptcp_sock *bpf_mptcp_sock_from_subflow(struct sock *sk) { return NULL; } #endif +#if !IS_ENABLED(CONFIG_MPTCP) +struct mptcp_sock { }; +#endif + #endif /* __NET_MPTCP_H */ diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 8bfb9c74afbf..99aae36c04b9 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -221,13 +221,18 @@ struct nft_ctx { bool report; }; +enum nft_data_desc_flags { + NFT_DATA_DESC_SETELEM = (1 << 0), +}; + struct nft_data_desc { enum nft_data_types type; + unsigned int size; unsigned int len; + unsigned int flags; }; -int nft_data_init(const struct nft_ctx *ctx, - struct nft_data *data, unsigned int size, +int nft_data_init(const struct nft_ctx *ctx, struct nft_data *data, struct nft_data_desc *desc, const struct nlattr *nla); void nft_data_hold(const struct nft_data *data, enum nft_data_types type); void nft_data_release(const struct nft_data *data, enum nft_data_types type); @@ -651,6 +656,7 @@ extern const struct nft_set_ext_type nft_set_ext_types[]; struct nft_set_ext_tmpl { u16 len; u8 offset[NFT_SET_EXT_NUM]; + u8 ext_len[NFT_SET_EXT_NUM]; }; /** @@ -680,7 +686,8 @@ static inline int nft_set_ext_add_length(struct nft_set_ext_tmpl *tmpl, u8 id, return -EINVAL; tmpl->offset[id] = tmpl->len; - tmpl->len += nft_set_ext_types[id].len + len; + tmpl->ext_len[id] = nft_set_ext_types[id].len + len; + tmpl->len += tmpl->ext_len[id]; return 0; } diff --git a/include/net/sock.h b/include/net/sock.h index a7273b289188..05a1bbdf5805 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -545,14 +545,26 @@ enum sk_pacing { SK_PACING_FQ = 2, }; -/* Pointer stored in sk_user_data might not be suitable for copying - * when cloning the socket. For instance, it can point to a reference - * counted object. sk_user_data bottom bit is set if pointer must not - * be copied. +/* flag bits in sk_user_data + * + * - SK_USER_DATA_NOCOPY: Pointer stored in sk_user_data might + * not be suitable for copying when cloning the socket. For instance, + * it can point to a reference counted object. sk_user_data bottom + * bit is set if pointer must not be copied. + * + * - SK_USER_DATA_BPF: Mark whether sk_user_data field is + * managed/owned by a BPF reuseport array. This bit should be set + * when sk_user_data's sk is added to the bpf's reuseport_array. + * + * - SK_USER_DATA_PSOCK: Mark whether pointer stored in + * sk_user_data points to psock type. This bit should be set + * when sk_user_data is assigned to a psock object. */ #define SK_USER_DATA_NOCOPY 1UL -#define SK_USER_DATA_BPF 2UL /* Managed by BPF */ -#define SK_USER_DATA_PTRMASK ~(SK_USER_DATA_NOCOPY | SK_USER_DATA_BPF) +#define SK_USER_DATA_BPF 2UL +#define SK_USER_DATA_PSOCK 4UL +#define SK_USER_DATA_PTRMASK ~(SK_USER_DATA_NOCOPY | SK_USER_DATA_BPF |\ + SK_USER_DATA_PSOCK) /** * sk_user_data_is_nocopy - Test if sk_user_data pointer must not be copied @@ -565,24 +577,40 @@ static inline bool sk_user_data_is_nocopy(const struct sock *sk) #define __sk_user_data(sk) ((*((void __rcu **)&(sk)->sk_user_data))) +/** + * __rcu_dereference_sk_user_data_with_flags - return the pointer + * only if argument flags all has been set in sk_user_data. Otherwise + * return NULL + * + * @sk: socket + * @flags: flag bits + */ +static inline void * +__rcu_dereference_sk_user_data_with_flags(const struct sock *sk, + uintptr_t flags) +{ + uintptr_t sk_user_data = (uintptr_t)rcu_dereference(__sk_user_data(sk)); + + WARN_ON_ONCE(flags & SK_USER_DATA_PTRMASK); + + if ((sk_user_data & flags) == flags) + return (void *)(sk_user_data & SK_USER_DATA_PTRMASK); + return NULL; +} + #define rcu_dereference_sk_user_data(sk) \ + __rcu_dereference_sk_user_data_with_flags(sk, 0) +#define __rcu_assign_sk_user_data_with_flags(sk, ptr, flags) \ ({ \ - void *__tmp = rcu_dereference(__sk_user_data((sk))); \ - (void *)((uintptr_t)__tmp & SK_USER_DATA_PTRMASK); \ -}) -#define rcu_assign_sk_user_data(sk, ptr) \ -({ \ - uintptr_t __tmp = (uintptr_t)(ptr); \ - WARN_ON_ONCE(__tmp & ~SK_USER_DATA_PTRMASK); \ - rcu_assign_pointer(__sk_user_data((sk)), __tmp); \ -}) -#define rcu_assign_sk_user_data_nocopy(sk, ptr) \ -({ \ - uintptr_t __tmp = (uintptr_t)(ptr); \ - WARN_ON_ONCE(__tmp & ~SK_USER_DATA_PTRMASK); \ + uintptr_t __tmp1 = (uintptr_t)(ptr), \ + __tmp2 = (uintptr_t)(flags); \ + WARN_ON_ONCE(__tmp1 & ~SK_USER_DATA_PTRMASK); \ + WARN_ON_ONCE(__tmp2 & SK_USER_DATA_PTRMASK); \ rcu_assign_pointer(__sk_user_data((sk)), \ - __tmp | SK_USER_DATA_NOCOPY); \ + __tmp1 | __tmp2); \ }) +#define rcu_assign_sk_user_data(sk, ptr) \ + __rcu_assign_sk_user_data_with_flags(sk, ptr, 0) static inline struct net *sock_net(const struct sock *sk) diff --git a/include/net/tls.h b/include/net/tls.h index b75b5727abdb..cb205f9d9473 100644 --- a/include/net/tls.h +++ b/include/net/tls.h @@ -237,7 +237,7 @@ struct tls_context { void *priv_ctx_tx; void *priv_ctx_rx; - struct net_device *netdev; + struct net_device __rcu *netdev; /* rw cache line */ struct cipher_context tx; diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 2493bd65351a..3113471ca375 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -309,6 +309,8 @@ struct scsi_target { struct list_head devices; struct device dev; struct kref reap_ref; /* last put renders target invisible */ + atomic_t sdev_count; + wait_queue_head_t sdev_wq; unsigned int channel; unsigned int id; /* target id ... replace * scsi_device.id eventually */ diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index b6e41ee3d566..aa7b7496c93a 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -690,6 +690,9 @@ struct Scsi_Host { /* ldm bits */ struct device shost_gendev, shost_dev; + atomic_t target_count; + wait_queue_head_t targets_wq; + /* * Points to the transport data (if any) which is allocated * separately diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index c2b36f7d917d..8c920456edd9 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -665,9 +665,9 @@ struct se_dev_entry { /* Used for PR SPEC_I_PT=1 and REGISTER_AND_MOVE */ struct kref pr_kref; struct completion pr_comp; - struct se_lun_acl __rcu *se_lun_acl; + struct se_lun_acl *se_lun_acl; spinlock_t ua_lock; - struct se_lun __rcu *se_lun; + struct se_lun *se_lun; #define DEF_PR_REG_ACTIVE 1 unsigned long deve_flags; struct list_head alua_port_list; diff --git a/include/trace/events/kvm.h b/include/trace/events/kvm.h index 37e1e1a2d67d..3bd31ea23fee 100644 --- a/include/trace/events/kvm.h +++ b/include/trace/events/kvm.h @@ -282,7 +282,7 @@ DEFINE_EVENT(kvm_async_get_page_class, kvm_try_async_get_page, TP_ARGS(gva, gfn) ); -DEFINE_EVENT(kvm_async_get_page_class, kvm_async_pf_doublefault, +DEFINE_EVENT(kvm_async_get_page_class, kvm_async_pf_repeated_fault, TP_PROTO(u64 gva, u64 gfn), diff --git a/include/uapi/linux/atm_zatm.h b/include/uapi/linux/atm_zatm.h new file mode 100644 index 000000000000..5135027b93c1 --- /dev/null +++ b/include/uapi/linux/atm_zatm.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* atm_zatm.h - Driver-specific declarations of the ZATM driver (for use by + driver-specific utilities) */ + +/* Written 1995-1999 by Werner Almesberger, EPFL LRC/ICA */ + + +#ifndef LINUX_ATM_ZATM_H +#define LINUX_ATM_ZATM_H + +/* + * Note: non-kernel programs including this file must also include + * sys/types.h for struct timeval + */ + +#include <linux/atmapi.h> +#include <linux/atmioc.h> + +#define ZATM_GETPOOL _IOW('a',ATMIOC_SARPRV+1,struct atmif_sioc) + /* get pool statistics */ +#define ZATM_GETPOOLZ _IOW('a',ATMIOC_SARPRV+2,struct atmif_sioc) + /* get statistics and zero */ +#define ZATM_SETPOOL _IOW('a',ATMIOC_SARPRV+3,struct atmif_sioc) + /* set pool parameters */ + +struct zatm_pool_info { + int ref_count; /* free buffer pool usage counters */ + int low_water,high_water; /* refill parameters */ + int rqa_count,rqu_count; /* queue condition counters */ + int offset,next_off; /* alignment optimizations: offset */ + int next_cnt,next_thres; /* repetition counter and threshold */ +}; + +struct zatm_pool_req { + int pool_num; /* pool number */ + struct zatm_pool_info info; /* actual information */ +}; + +#define ZATM_OAM_POOL 0 /* free buffer pool for OAM cells */ +#define ZATM_AAL0_POOL 1 /* free buffer pool for AAL0 cells */ +#define ZATM_AAL5_POOL_BASE 2 /* first AAL5 free buffer pool */ +#define ZATM_LAST_POOL ZATM_AAL5_POOL_BASE+10 /* max. 64 kB */ + +#define ZATM_TIMER_HISTORY_SIZE 16 /* number of timer adjustments to + record; must be 2^n */ + +#endif diff --git a/include/uapi/linux/genetlink.h b/include/uapi/linux/genetlink.h index d83f214b4134..ddba3ca01e39 100644 --- a/include/uapi/linux/genetlink.h +++ b/include/uapi/linux/genetlink.h @@ -87,6 +87,8 @@ enum { __CTRL_ATTR_MCAST_GRP_MAX, }; +#define CTRL_ATTR_MCAST_GRP_MAX (__CTRL_ATTR_MCAST_GRP_MAX - 1) + enum { CTRL_ATTR_POLICY_UNSPEC, CTRL_ATTR_POLICY_DO, @@ -96,7 +98,6 @@ enum { CTRL_ATTR_POLICY_DUMP_MAX = __CTRL_ATTR_POLICY_DUMP_MAX - 1 }; -#define CTRL_ATTR_MCAST_GRP_MAX (__CTRL_ATTR_MCAST_GRP_MAX - 1) - +#define CTRL_ATTR_POLICY_MAX (__CTRL_ATTR_POLICY_DUMP_MAX - 1) #endif /* _UAPI__LINUX_GENERIC_NETLINK_H */ diff --git a/include/uapi/linux/netfilter_ipv6/ip6t_LOG.h b/include/uapi/linux/netfilter_ipv6/ip6t_LOG.h index 23e91a9c2583..0b7b16dbdec2 100644 --- a/include/uapi/linux/netfilter_ipv6/ip6t_LOG.h +++ b/include/uapi/linux/netfilter_ipv6/ip6t_LOG.h @@ -17,4 +17,4 @@ struct ip6t_log_info { char prefix[30]; }; -#endif /*_IPT_LOG_H*/ +#endif /* _IP6T_LOG_H */ diff --git a/include/uapi/linux/vduse.h b/include/uapi/linux/vduse.h index 7cfe1c1280c0..11bd48c72c6c 100644 --- a/include/uapi/linux/vduse.h +++ b/include/uapi/linux/vduse.h @@ -210,6 +210,53 @@ struct vduse_vq_eventfd { */ #define VDUSE_VQ_INJECT_IRQ _IOW(VDUSE_BASE, 0x17, __u32) +/** + * struct vduse_iova_umem - userspace memory configuration for one IOVA region + * @uaddr: start address of userspace memory, it must be aligned to page size + * @iova: start of the IOVA region + * @size: size of the IOVA region + * @reserved: for future use, needs to be initialized to zero + * + * Structure used by VDUSE_IOTLB_REG_UMEM and VDUSE_IOTLB_DEREG_UMEM + * ioctls to register/de-register userspace memory for IOVA regions + */ +struct vduse_iova_umem { + __u64 uaddr; + __u64 iova; + __u64 size; + __u64 reserved[3]; +}; + +/* Register userspace memory for IOVA regions */ +#define VDUSE_IOTLB_REG_UMEM _IOW(VDUSE_BASE, 0x18, struct vduse_iova_umem) + +/* De-register the userspace memory. Caller should set iova and size field. */ +#define VDUSE_IOTLB_DEREG_UMEM _IOW(VDUSE_BASE, 0x19, struct vduse_iova_umem) + +/** + * struct vduse_iova_info - information of one IOVA region + * @start: start of the IOVA region + * @last: last of the IOVA region + * @capability: capability of the IOVA regsion + * @reserved: for future use, needs to be initialized to zero + * + * Structure used by VDUSE_IOTLB_GET_INFO ioctl to get information of + * one IOVA region. + */ +struct vduse_iova_info { + __u64 start; + __u64 last; +#define VDUSE_IOVA_CAP_UMEM (1 << 0) + __u64 capability; + __u64 reserved[3]; +}; + +/* + * Find the first IOVA region that overlaps with the range [start, last] + * and return some information on it. Caller should set start and last fields. + */ +#define VDUSE_IOTLB_GET_INFO _IOWR(VDUSE_BASE, 0x1a, struct vduse_iova_info) + /* The control messages definition for read(2)/write(2) on /dev/vduse/$NAME */ /** diff --git a/include/uapi/linux/vhost.h b/include/uapi/linux/vhost.h index cab645d4a645..f9f115a7c75b 100644 --- a/include/uapi/linux/vhost.h +++ b/include/uapi/linux/vhost.h @@ -171,4 +171,13 @@ #define VHOST_VDPA_SET_GROUP_ASID _IOW(VHOST_VIRTIO, 0x7C, \ struct vhost_vring_state) +/* Suspend a device so it does not process virtqueue requests anymore + * + * After the return of ioctl the device must preserve all the necessary state + * (the virtqueue vring base plus the possible device specific states) that is + * required for restoring in the future. The device must not change its + * configuration after that point. + */ +#define VHOST_VDPA_SUSPEND _IO(VHOST_VIRTIO, 0x7D) + #endif diff --git a/include/uapi/linux/vhost_types.h b/include/uapi/linux/vhost_types.h index 391331a10879..53601ce2c20a 100644 --- a/include/uapi/linux/vhost_types.h +++ b/include/uapi/linux/vhost_types.h @@ -161,5 +161,7 @@ struct vhost_vdpa_iova_range { * message */ #define VHOST_BACKEND_F_IOTLB_ASID 0x3 +/* Device can be suspended */ +#define VHOST_BACKEND_F_SUSPEND 0x4 #endif diff --git a/include/uapi/linux/virtio_config.h b/include/uapi/linux/virtio_config.h index f0fb0ae021c0..3c05162bc988 100644 --- a/include/uapi/linux/virtio_config.h +++ b/include/uapi/linux/virtio_config.h @@ -52,7 +52,7 @@ * rest are per-device feature bits. */ #define VIRTIO_TRANSPORT_F_START 28 -#define VIRTIO_TRANSPORT_F_END 38 +#define VIRTIO_TRANSPORT_F_END 41 #ifndef VIRTIO_CONFIG_NO_LEGACY /* Do we get callbacks when the ring is completely used, even if we've @@ -98,4 +98,9 @@ * Does the device support Single Root I/O Virtualization? */ #define VIRTIO_F_SR_IOV 37 + +/* + * This feature indicates that the driver can reset a queue individually. + */ +#define VIRTIO_F_RING_RESET 40 #endif /* _UAPI_LINUX_VIRTIO_CONFIG_H */ diff --git a/include/uapi/linux/virtio_net.h b/include/uapi/linux/virtio_net.h index 3f55a4215f11..29ced55514d4 100644 --- a/include/uapi/linux/virtio_net.h +++ b/include/uapi/linux/virtio_net.h @@ -56,7 +56,7 @@ #define VIRTIO_NET_F_MQ 22 /* Device supports Receive Flow * Steering */ #define VIRTIO_NET_F_CTRL_MAC_ADDR 23 /* Set MAC address */ - +#define VIRTIO_NET_F_NOTF_COAL 53 /* Guest can handle notifications coalescing */ #define VIRTIO_NET_F_HASH_REPORT 57 /* Supports hash report */ #define VIRTIO_NET_F_RSS 60 /* Supports RSS RX steering */ #define VIRTIO_NET_F_RSC_EXT 61 /* extended coalescing info */ @@ -355,4 +355,36 @@ struct virtio_net_hash_config { #define VIRTIO_NET_CTRL_GUEST_OFFLOADS 5 #define VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET 0 +/* + * Control notifications coalescing. + * + * Request the device to change the notifications coalescing parameters. + * + * Available with the VIRTIO_NET_F_NOTF_COAL feature bit. + */ +#define VIRTIO_NET_CTRL_NOTF_COAL 6 +/* + * Set the tx-usecs/tx-max-packets patameters. + * tx-usecs - Maximum number of usecs to delay a TX notification. + * tx-max-packets - Maximum number of packets to send before a TX notification. + */ +struct virtio_net_ctrl_coal_tx { + __le32 tx_max_packets; + __le32 tx_usecs; +}; + +#define VIRTIO_NET_CTRL_NOTF_COAL_TX_SET 0 + +/* + * Set the rx-usecs/rx-max-packets patameters. + * rx-usecs - Maximum number of usecs to delay a RX notification. + * rx-max-frames - Maximum number of packets to receive before a RX notification. + */ +struct virtio_net_ctrl_coal_rx { + __le32 rx_max_packets; + __le32 rx_usecs; +}; + +#define VIRTIO_NET_CTRL_NOTF_COAL_RX_SET 1 + #endif /* _UAPI_LINUX_VIRTIO_NET_H */ diff --git a/include/uapi/linux/virtio_pci.h b/include/uapi/linux/virtio_pci.h index 3a86f36d7e3d..f703afc7ad31 100644 --- a/include/uapi/linux/virtio_pci.h +++ b/include/uapi/linux/virtio_pci.h @@ -202,6 +202,8 @@ struct virtio_pci_cfg_cap { #define VIRTIO_PCI_COMMON_Q_AVAILHI 44 #define VIRTIO_PCI_COMMON_Q_USEDLO 48 #define VIRTIO_PCI_COMMON_Q_USEDHI 52 +#define VIRTIO_PCI_COMMON_Q_NDATA 56 +#define VIRTIO_PCI_COMMON_Q_RESET 58 #endif /* VIRTIO_PCI_NO_MODERN */ diff --git a/io_uring/advise.c b/io_uring/advise.c index 581956934c0b..449c6f14649f 100644 --- a/io_uring/advise.c +++ b/io_uring/advise.c @@ -31,7 +31,7 @@ struct io_madvise { int io_madvise_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { #if defined(CONFIG_ADVISE_SYSCALLS) && defined(CONFIG_MMU) - struct io_madvise *ma = io_kiocb_to_cmd(req); + struct io_madvise *ma = io_kiocb_to_cmd(req, struct io_madvise); if (sqe->buf_index || sqe->off || sqe->splice_fd_in) return -EINVAL; @@ -48,7 +48,7 @@ int io_madvise_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_madvise(struct io_kiocb *req, unsigned int issue_flags) { #if defined(CONFIG_ADVISE_SYSCALLS) && defined(CONFIG_MMU) - struct io_madvise *ma = io_kiocb_to_cmd(req); + struct io_madvise *ma = io_kiocb_to_cmd(req, struct io_madvise); int ret; if (issue_flags & IO_URING_F_NONBLOCK) @@ -64,7 +64,7 @@ int io_madvise(struct io_kiocb *req, unsigned int issue_flags) int io_fadvise_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_fadvise *fa = io_kiocb_to_cmd(req); + struct io_fadvise *fa = io_kiocb_to_cmd(req, struct io_fadvise); if (sqe->buf_index || sqe->addr || sqe->splice_fd_in) return -EINVAL; @@ -77,7 +77,7 @@ int io_fadvise_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_fadvise(struct io_kiocb *req, unsigned int issue_flags) { - struct io_fadvise *fa = io_kiocb_to_cmd(req); + struct io_fadvise *fa = io_kiocb_to_cmd(req, struct io_fadvise); int ret; if (issue_flags & IO_URING_F_NONBLOCK) { diff --git a/io_uring/cancel.c b/io_uring/cancel.c index 8435a1eba59a..e4e1dc0325f0 100644 --- a/io_uring/cancel.c +++ b/io_uring/cancel.c @@ -107,7 +107,7 @@ int io_try_cancel(struct io_uring_task *tctx, struct io_cancel_data *cd, int io_async_cancel_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_cancel *cancel = io_kiocb_to_cmd(req); + struct io_cancel *cancel = io_kiocb_to_cmd(req, struct io_cancel); if (unlikely(req->flags & REQ_F_BUFFER_SELECT)) return -EINVAL; @@ -164,7 +164,7 @@ static int __io_async_cancel(struct io_cancel_data *cd, int io_async_cancel(struct io_kiocb *req, unsigned int issue_flags) { - struct io_cancel *cancel = io_kiocb_to_cmd(req); + struct io_cancel *cancel = io_kiocb_to_cmd(req, struct io_cancel); struct io_cancel_data cd = { .ctx = req->ctx, .data = cancel->addr, diff --git a/io_uring/epoll.c b/io_uring/epoll.c index a8b794471d6b..9aa74d2c80bc 100644 --- a/io_uring/epoll.c +++ b/io_uring/epoll.c @@ -23,7 +23,7 @@ struct io_epoll { int io_epoll_ctl_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_epoll *epoll = io_kiocb_to_cmd(req); + struct io_epoll *epoll = io_kiocb_to_cmd(req, struct io_epoll); pr_warn_once("%s: epoll_ctl support in io_uring is deprecated and will " "be removed in a future Linux kernel version.\n", @@ -49,7 +49,7 @@ int io_epoll_ctl_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_epoll_ctl(struct io_kiocb *req, unsigned int issue_flags) { - struct io_epoll *ie = io_kiocb_to_cmd(req); + struct io_epoll *ie = io_kiocb_to_cmd(req, struct io_epoll); int ret; bool force_nonblock = issue_flags & IO_URING_F_NONBLOCK; diff --git a/io_uring/fs.c b/io_uring/fs.c index 0de4f549bb7d..7100c293c13a 100644 --- a/io_uring/fs.c +++ b/io_uring/fs.c @@ -49,7 +49,7 @@ struct io_link { int io_renameat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_rename *ren = io_kiocb_to_cmd(req); + struct io_rename *ren = io_kiocb_to_cmd(req, struct io_rename); const char __user *oldf, *newf; if (sqe->buf_index || sqe->splice_fd_in) @@ -79,7 +79,7 @@ int io_renameat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_renameat(struct io_kiocb *req, unsigned int issue_flags) { - struct io_rename *ren = io_kiocb_to_cmd(req); + struct io_rename *ren = io_kiocb_to_cmd(req, struct io_rename); int ret; if (issue_flags & IO_URING_F_NONBLOCK) @@ -95,7 +95,7 @@ int io_renameat(struct io_kiocb *req, unsigned int issue_flags) void io_renameat_cleanup(struct io_kiocb *req) { - struct io_rename *ren = io_kiocb_to_cmd(req); + struct io_rename *ren = io_kiocb_to_cmd(req, struct io_rename); putname(ren->oldpath); putname(ren->newpath); @@ -103,7 +103,7 @@ void io_renameat_cleanup(struct io_kiocb *req) int io_unlinkat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_unlink *un = io_kiocb_to_cmd(req); + struct io_unlink *un = io_kiocb_to_cmd(req, struct io_unlink); const char __user *fname; if (sqe->off || sqe->len || sqe->buf_index || sqe->splice_fd_in) @@ -128,7 +128,7 @@ int io_unlinkat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_unlinkat(struct io_kiocb *req, unsigned int issue_flags) { - struct io_unlink *un = io_kiocb_to_cmd(req); + struct io_unlink *un = io_kiocb_to_cmd(req, struct io_unlink); int ret; if (issue_flags & IO_URING_F_NONBLOCK) @@ -146,14 +146,14 @@ int io_unlinkat(struct io_kiocb *req, unsigned int issue_flags) void io_unlinkat_cleanup(struct io_kiocb *req) { - struct io_unlink *ul = io_kiocb_to_cmd(req); + struct io_unlink *ul = io_kiocb_to_cmd(req, struct io_unlink); putname(ul->filename); } int io_mkdirat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_mkdir *mkd = io_kiocb_to_cmd(req); + struct io_mkdir *mkd = io_kiocb_to_cmd(req, struct io_mkdir); const char __user *fname; if (sqe->off || sqe->rw_flags || sqe->buf_index || sqe->splice_fd_in) @@ -175,7 +175,7 @@ int io_mkdirat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_mkdirat(struct io_kiocb *req, unsigned int issue_flags) { - struct io_mkdir *mkd = io_kiocb_to_cmd(req); + struct io_mkdir *mkd = io_kiocb_to_cmd(req, struct io_mkdir); int ret; if (issue_flags & IO_URING_F_NONBLOCK) @@ -190,14 +190,14 @@ int io_mkdirat(struct io_kiocb *req, unsigned int issue_flags) void io_mkdirat_cleanup(struct io_kiocb *req) { - struct io_mkdir *md = io_kiocb_to_cmd(req); + struct io_mkdir *md = io_kiocb_to_cmd(req, struct io_mkdir); putname(md->filename); } int io_symlinkat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_link *sl = io_kiocb_to_cmd(req); + struct io_link *sl = io_kiocb_to_cmd(req, struct io_link); const char __user *oldpath, *newpath; if (sqe->len || sqe->rw_flags || sqe->buf_index || sqe->splice_fd_in) @@ -225,7 +225,7 @@ int io_symlinkat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_symlinkat(struct io_kiocb *req, unsigned int issue_flags) { - struct io_link *sl = io_kiocb_to_cmd(req); + struct io_link *sl = io_kiocb_to_cmd(req, struct io_link); int ret; if (issue_flags & IO_URING_F_NONBLOCK) @@ -240,7 +240,7 @@ int io_symlinkat(struct io_kiocb *req, unsigned int issue_flags) int io_linkat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_link *lnk = io_kiocb_to_cmd(req); + struct io_link *lnk = io_kiocb_to_cmd(req, struct io_link); const char __user *oldf, *newf; if (sqe->rw_flags || sqe->buf_index || sqe->splice_fd_in) @@ -270,7 +270,7 @@ int io_linkat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_linkat(struct io_kiocb *req, unsigned int issue_flags) { - struct io_link *lnk = io_kiocb_to_cmd(req); + struct io_link *lnk = io_kiocb_to_cmd(req, struct io_link); int ret; if (issue_flags & IO_URING_F_NONBLOCK) @@ -286,7 +286,7 @@ int io_linkat(struct io_kiocb *req, unsigned int issue_flags) void io_link_cleanup(struct io_kiocb *req) { - struct io_link *sl = io_kiocb_to_cmd(req); + struct io_link *sl = io_kiocb_to_cmd(req, struct io_link); putname(sl->oldpath); putname(sl->newpath); diff --git a/io_uring/io-wq.c b/io_uring/io-wq.c index 77df5b43bf52..c6536d4b2da0 100644 --- a/io_uring/io-wq.c +++ b/io_uring/io-wq.c @@ -624,8 +624,6 @@ static int io_wqe_worker(void *data) snprintf(buf, sizeof(buf), "iou-wrk-%d", wq->task->pid); set_task_comm(current, buf); - audit_alloc_kernel(current); - while (!test_bit(IO_WQ_BIT_EXIT, &wq->state)) { long ret; @@ -660,7 +658,6 @@ static int io_wqe_worker(void *data) if (test_bit(IO_WQ_BIT_EXIT, &wq->state)) io_worker_handle_work(worker); - audit_free(current); io_worker_exit(worker); return 0; } diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c index b54218da075c..ebfdb2212ec2 100644 --- a/io_uring/io_uring.c +++ b/io_uring/io_uring.c @@ -3885,13 +3885,15 @@ out_fput: static int __init io_uring_init(void) { -#define __BUILD_BUG_VERIFY_ELEMENT(stype, eoffset, etype, ename) do { \ +#define __BUILD_BUG_VERIFY_OFFSET_SIZE(stype, eoffset, esize, ename) do { \ BUILD_BUG_ON(offsetof(stype, ename) != eoffset); \ - BUILD_BUG_ON(sizeof(etype) != sizeof_field(stype, ename)); \ + BUILD_BUG_ON(sizeof_field(stype, ename) != esize); \ } while (0) #define BUILD_BUG_SQE_ELEM(eoffset, etype, ename) \ - __BUILD_BUG_VERIFY_ELEMENT(struct io_uring_sqe, eoffset, etype, ename) + __BUILD_BUG_VERIFY_OFFSET_SIZE(struct io_uring_sqe, eoffset, sizeof(etype), ename) +#define BUILD_BUG_SQE_ELEM_SIZE(eoffset, esize, ename) \ + __BUILD_BUG_VERIFY_OFFSET_SIZE(struct io_uring_sqe, eoffset, esize, ename) BUILD_BUG_ON(sizeof(struct io_uring_sqe) != 64); BUILD_BUG_SQE_ELEM(0, __u8, opcode); BUILD_BUG_SQE_ELEM(1, __u8, flags); @@ -3899,6 +3901,8 @@ static int __init io_uring_init(void) BUILD_BUG_SQE_ELEM(4, __s32, fd); BUILD_BUG_SQE_ELEM(8, __u64, off); BUILD_BUG_SQE_ELEM(8, __u64, addr2); + BUILD_BUG_SQE_ELEM(8, __u32, cmd_op); + BUILD_BUG_SQE_ELEM(12, __u32, __pad1); BUILD_BUG_SQE_ELEM(16, __u64, addr); BUILD_BUG_SQE_ELEM(16, __u64, splice_off_in); BUILD_BUG_SQE_ELEM(24, __u32, len); @@ -3917,13 +3921,22 @@ static int __init io_uring_init(void) BUILD_BUG_SQE_ELEM(28, __u32, statx_flags); BUILD_BUG_SQE_ELEM(28, __u32, fadvise_advice); BUILD_BUG_SQE_ELEM(28, __u32, splice_flags); + BUILD_BUG_SQE_ELEM(28, __u32, rename_flags); + BUILD_BUG_SQE_ELEM(28, __u32, unlink_flags); + BUILD_BUG_SQE_ELEM(28, __u32, hardlink_flags); + BUILD_BUG_SQE_ELEM(28, __u32, xattr_flags); + BUILD_BUG_SQE_ELEM(28, __u32, msg_ring_flags); BUILD_BUG_SQE_ELEM(32, __u64, user_data); BUILD_BUG_SQE_ELEM(40, __u16, buf_index); BUILD_BUG_SQE_ELEM(40, __u16, buf_group); BUILD_BUG_SQE_ELEM(42, __u16, personality); BUILD_BUG_SQE_ELEM(44, __s32, splice_fd_in); BUILD_BUG_SQE_ELEM(44, __u32, file_index); + BUILD_BUG_SQE_ELEM(44, __u16, notification_idx); + BUILD_BUG_SQE_ELEM(46, __u16, addr_len); BUILD_BUG_SQE_ELEM(48, __u64, addr3); + BUILD_BUG_SQE_ELEM_SIZE(48, 0, cmd); + BUILD_BUG_SQE_ELEM(56, __u64, __pad2); BUILD_BUG_ON(sizeof(struct io_uring_files_update) != sizeof(struct io_uring_rsrc_update)); diff --git a/io_uring/kbuf.c b/io_uring/kbuf.c index e538fa7cb727..25cd724ade18 100644 --- a/io_uring/kbuf.c +++ b/io_uring/kbuf.c @@ -272,7 +272,7 @@ void io_destroy_buffers(struct io_ring_ctx *ctx) int io_remove_buffers_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_provide_buf *p = io_kiocb_to_cmd(req); + struct io_provide_buf *p = io_kiocb_to_cmd(req, struct io_provide_buf); u64 tmp; if (sqe->rw_flags || sqe->addr || sqe->len || sqe->off || @@ -291,7 +291,7 @@ int io_remove_buffers_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_remove_buffers(struct io_kiocb *req, unsigned int issue_flags) { - struct io_provide_buf *p = io_kiocb_to_cmd(req); + struct io_provide_buf *p = io_kiocb_to_cmd(req, struct io_provide_buf); struct io_ring_ctx *ctx = req->ctx; struct io_buffer_list *bl; int ret = 0; @@ -319,7 +319,7 @@ int io_remove_buffers(struct io_kiocb *req, unsigned int issue_flags) int io_provide_buffers_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { unsigned long size, tmp_check; - struct io_provide_buf *p = io_kiocb_to_cmd(req); + struct io_provide_buf *p = io_kiocb_to_cmd(req, struct io_provide_buf); u64 tmp; if (sqe->rw_flags || sqe->splice_fd_in) @@ -421,7 +421,7 @@ static int io_add_buffers(struct io_ring_ctx *ctx, struct io_provide_buf *pbuf, int io_provide_buffers(struct io_kiocb *req, unsigned int issue_flags) { - struct io_provide_buf *p = io_kiocb_to_cmd(req); + struct io_provide_buf *p = io_kiocb_to_cmd(req, struct io_provide_buf); struct io_ring_ctx *ctx = req->ctx; struct io_buffer_list *bl; int ret = 0; @@ -436,7 +436,7 @@ int io_provide_buffers(struct io_kiocb *req, unsigned int issue_flags) bl = io_buffer_get_list(ctx, p->bgid); if (unlikely(!bl)) { - bl = kzalloc(sizeof(*bl), GFP_KERNEL); + bl = kzalloc(sizeof(*bl), GFP_KERNEL_ACCOUNT); if (!bl) { ret = -ENOMEM; goto err; diff --git a/io_uring/msg_ring.c b/io_uring/msg_ring.c index 753d16734319..976c4ba68ee7 100644 --- a/io_uring/msg_ring.c +++ b/io_uring/msg_ring.c @@ -26,7 +26,7 @@ struct io_msg { static int io_msg_ring_data(struct io_kiocb *req) { struct io_ring_ctx *target_ctx = req->file->private_data; - struct io_msg *msg = io_kiocb_to_cmd(req); + struct io_msg *msg = io_kiocb_to_cmd(req, struct io_msg); if (msg->src_fd || msg->dst_fd || msg->flags) return -EINVAL; @@ -76,7 +76,7 @@ static int io_double_lock_ctx(struct io_ring_ctx *ctx, static int io_msg_send_fd(struct io_kiocb *req, unsigned int issue_flags) { struct io_ring_ctx *target_ctx = req->file->private_data; - struct io_msg *msg = io_kiocb_to_cmd(req); + struct io_msg *msg = io_kiocb_to_cmd(req, struct io_msg); struct io_ring_ctx *ctx = req->ctx; unsigned long file_ptr; struct file *src_file; @@ -122,7 +122,7 @@ out_unlock: int io_msg_ring_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_msg *msg = io_kiocb_to_cmd(req); + struct io_msg *msg = io_kiocb_to_cmd(req, struct io_msg); if (unlikely(sqe->buf_index || sqe->personality)) return -EINVAL; @@ -141,7 +141,7 @@ int io_msg_ring_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_msg_ring(struct io_kiocb *req, unsigned int issue_flags) { - struct io_msg *msg = io_kiocb_to_cmd(req); + struct io_msg *msg = io_kiocb_to_cmd(req, struct io_msg); int ret; ret = -EBADFD; diff --git a/io_uring/net.c b/io_uring/net.c index 32fc3da04e41..6d71748e2c5a 100644 --- a/io_uring/net.c +++ b/io_uring/net.c @@ -70,13 +70,14 @@ struct io_sendzc { unsigned flags; unsigned addr_len; void __user *addr; + size_t done_io; }; #define IO_APOLL_MULTI_POLLED (REQ_F_APOLL_MULTISHOT | REQ_F_POLLED) int io_shutdown_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_shutdown *shutdown = io_kiocb_to_cmd(req); + struct io_shutdown *shutdown = io_kiocb_to_cmd(req, struct io_shutdown); if (unlikely(sqe->off || sqe->addr || sqe->rw_flags || sqe->buf_index || sqe->splice_fd_in)) @@ -88,7 +89,7 @@ int io_shutdown_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_shutdown(struct io_kiocb *req, unsigned int issue_flags) { - struct io_shutdown *shutdown = io_kiocb_to_cmd(req); + struct io_shutdown *shutdown = io_kiocb_to_cmd(req, struct io_shutdown); struct socket *sock; int ret; @@ -173,7 +174,7 @@ static int io_setup_async_msg(struct io_kiocb *req, static int io_sendmsg_copy_hdr(struct io_kiocb *req, struct io_async_msghdr *iomsg) { - struct io_sr_msg *sr = io_kiocb_to_cmd(req); + struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg); iomsg->msg.msg_name = &iomsg->addr; iomsg->free_iov = iomsg->fast_iov; @@ -200,7 +201,7 @@ void io_sendmsg_recvmsg_cleanup(struct io_kiocb *req) int io_sendmsg_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_sr_msg *sr = io_kiocb_to_cmd(req); + struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg); if (unlikely(sqe->file_index || sqe->addr2)) return -EINVAL; @@ -224,7 +225,7 @@ int io_sendmsg_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_sendmsg(struct io_kiocb *req, unsigned int issue_flags) { - struct io_sr_msg *sr = io_kiocb_to_cmd(req); + struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg); struct io_async_msghdr iomsg, *kmsg; struct socket *sock; unsigned flags; @@ -283,7 +284,7 @@ int io_sendmsg(struct io_kiocb *req, unsigned int issue_flags) int io_send(struct io_kiocb *req, unsigned int issue_flags) { - struct io_sr_msg *sr = io_kiocb_to_cmd(req); + struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg); struct msghdr msg; struct iovec iov; struct socket *sock; @@ -357,7 +358,7 @@ static bool io_recvmsg_multishot_overflow(struct io_async_msghdr *iomsg) static int __io_recvmsg_copy_hdr(struct io_kiocb *req, struct io_async_msghdr *iomsg) { - struct io_sr_msg *sr = io_kiocb_to_cmd(req); + struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg); struct user_msghdr msg; int ret; @@ -404,7 +405,7 @@ static int __io_recvmsg_copy_hdr(struct io_kiocb *req, static int __io_compat_recvmsg_copy_hdr(struct io_kiocb *req, struct io_async_msghdr *iomsg) { - struct io_sr_msg *sr = io_kiocb_to_cmd(req); + struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg); struct compat_msghdr msg; struct compat_iovec __user *uiov; int ret; @@ -482,7 +483,7 @@ int io_recvmsg_prep_async(struct io_kiocb *req) int io_recvmsg_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_sr_msg *sr = io_kiocb_to_cmd(req); + struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg); if (unlikely(sqe->file_index || sqe->addr2)) return -EINVAL; @@ -517,7 +518,7 @@ int io_recvmsg_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) static inline void io_recv_prep_retry(struct io_kiocb *req) { - struct io_sr_msg *sr = io_kiocb_to_cmd(req); + struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg); sr->done_io = 0; sr->len = 0; /* get from the provided buffer */ @@ -575,12 +576,12 @@ static int io_recvmsg_prep_multishot(struct io_async_msghdr *kmsg, if (kmsg->controllen) { unsigned long control = ubuf + hdr - kmsg->controllen; - kmsg->msg.msg_control_user = (void *) control; + kmsg->msg.msg_control_user = (void __user *) control; kmsg->msg.msg_controllen = kmsg->controllen; } sr->buf = *buf; /* stash for later copy */ - *buf = (void *) (ubuf + hdr); + *buf = (void __user *) (ubuf + hdr); kmsg->payloadlen = *len = *len - hdr; return 0; } @@ -646,7 +647,7 @@ static int io_recvmsg_multishot(struct socket *sock, struct io_sr_msg *io, int io_recvmsg(struct io_kiocb *req, unsigned int issue_flags) { - struct io_sr_msg *sr = io_kiocb_to_cmd(req); + struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg); struct io_async_msghdr iomsg, *kmsg; struct socket *sock; unsigned int cflags; @@ -758,7 +759,7 @@ retry_multishot: int io_recv(struct io_kiocb *req, unsigned int issue_flags) { - struct io_sr_msg *sr = io_kiocb_to_cmd(req); + struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg); struct msghdr msg; struct socket *sock; struct iovec iov; @@ -849,7 +850,7 @@ out_free: int io_sendzc_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_sendzc *zc = io_kiocb_to_cmd(req); + struct io_sendzc *zc = io_kiocb_to_cmd(req, struct io_sendzc); struct io_ring_ctx *ctx = req->ctx; if (READ_ONCE(sqe->__pad2[0]) || READ_ONCE(sqe->addr3)) @@ -878,6 +879,7 @@ int io_sendzc_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) zc->addr = u64_to_user_ptr(READ_ONCE(sqe->addr2)); zc->addr_len = READ_ONCE(sqe->addr_len); + zc->done_io = 0; #ifdef CONFIG_COMPAT if (req->ctx->compat) @@ -944,7 +946,7 @@ int io_sendzc(struct io_kiocb *req, unsigned int issue_flags) { struct sockaddr_storage address; struct io_ring_ctx *ctx = req->ctx; - struct io_sendzc *zc = io_kiocb_to_cmd(req); + struct io_sendzc *zc = io_kiocb_to_cmd(req, struct io_sendzc); struct io_notif_slot *notif_slot; struct io_kiocb *notif; struct msghdr msg; @@ -1012,18 +1014,30 @@ int io_sendzc(struct io_kiocb *req, unsigned int issue_flags) if (unlikely(ret < min_ret)) { if (ret == -EAGAIN && (issue_flags & IO_URING_F_NONBLOCK)) return -EAGAIN; - return ret == -ERESTARTSYS ? -EINTR : ret; + if (ret > 0 && io_net_retry(sock, msg.msg_flags)) { + zc->len -= ret; + zc->buf += ret; + zc->done_io += ret; + req->flags |= REQ_F_PARTIAL_IO; + return -EAGAIN; + } + if (ret == -ERESTARTSYS) + ret = -EINTR; + } else if (zc->flags & IORING_RECVSEND_NOTIF_FLUSH) { + io_notif_slot_flush_submit(notif_slot, 0); } - if (zc->flags & IORING_RECVSEND_NOTIF_FLUSH) - io_notif_slot_flush_submit(notif_slot, 0); + if (ret >= 0) + ret += zc->done_io; + else if (zc->done_io) + ret = zc->done_io; io_req_set_res(req, ret, 0); return IOU_OK; } int io_accept_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_accept *accept = io_kiocb_to_cmd(req); + struct io_accept *accept = io_kiocb_to_cmd(req, struct io_accept); unsigned flags; if (sqe->len || sqe->buf_index) @@ -1057,7 +1071,7 @@ int io_accept_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_accept(struct io_kiocb *req, unsigned int issue_flags) { struct io_ring_ctx *ctx = req->ctx; - struct io_accept *accept = io_kiocb_to_cmd(req); + struct io_accept *accept = io_kiocb_to_cmd(req, struct io_accept); bool force_nonblock = issue_flags & IO_URING_F_NONBLOCK; unsigned int file_flags = force_nonblock ? O_NONBLOCK : 0; bool fixed = !!accept->file_slot; @@ -1115,7 +1129,7 @@ retry: int io_socket_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_socket *sock = io_kiocb_to_cmd(req); + struct io_socket *sock = io_kiocb_to_cmd(req, struct io_socket); if (sqe->addr || sqe->rw_flags || sqe->buf_index) return -EINVAL; @@ -1136,7 +1150,7 @@ int io_socket_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_socket(struct io_kiocb *req, unsigned int issue_flags) { - struct io_socket *sock = io_kiocb_to_cmd(req); + struct io_socket *sock = io_kiocb_to_cmd(req, struct io_socket); bool fixed = !!sock->file_slot; struct file *file; int ret, fd; @@ -1170,14 +1184,14 @@ int io_socket(struct io_kiocb *req, unsigned int issue_flags) int io_connect_prep_async(struct io_kiocb *req) { struct io_async_connect *io = req->async_data; - struct io_connect *conn = io_kiocb_to_cmd(req); + struct io_connect *conn = io_kiocb_to_cmd(req, struct io_connect); return move_addr_to_kernel(conn->addr, conn->addr_len, &io->address); } int io_connect_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_connect *conn = io_kiocb_to_cmd(req); + struct io_connect *conn = io_kiocb_to_cmd(req, struct io_connect); if (sqe->len || sqe->buf_index || sqe->rw_flags || sqe->splice_fd_in) return -EINVAL; @@ -1189,7 +1203,7 @@ int io_connect_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_connect(struct io_kiocb *req, unsigned int issue_flags) { - struct io_connect *connect = io_kiocb_to_cmd(req); + struct io_connect *connect = io_kiocb_to_cmd(req, struct io_connect); struct io_async_connect __io, *io; unsigned file_flags; int ret; diff --git a/io_uring/notif.c b/io_uring/notif.c index b5f989dff9de..977736e82c1a 100644 --- a/io_uring/notif.c +++ b/io_uring/notif.c @@ -100,7 +100,7 @@ __cold int io_notif_unregister(struct io_ring_ctx *ctx) if (!notif) continue; - nd = io_kiocb_to_cmd(notif); + nd = io_notif_to_data(notif); slot->notif = NULL; if (!refcount_dec_and_test(&nd->uarg.refcnt)) continue; @@ -123,8 +123,6 @@ __cold int io_notif_register(struct io_ring_ctx *ctx, struct io_uring_notification_register reg; unsigned i; - BUILD_BUG_ON(sizeof(struct io_notif_data) > 64); - if (ctx->nr_notif_slots) return -EBUSY; if (size != sizeof(reg)) diff --git a/io_uring/notif.h b/io_uring/notif.h index 0819304d7e00..65f0b42f2555 100644 --- a/io_uring/notif.h +++ b/io_uring/notif.h @@ -46,7 +46,7 @@ struct io_kiocb *io_alloc_notif(struct io_ring_ctx *ctx, static inline struct io_notif_data *io_notif_to_data(struct io_kiocb *notif) { - return io_kiocb_to_cmd(notif); + return io_kiocb_to_cmd(notif, struct io_notif_data); } static inline struct io_kiocb *io_get_notif(struct io_ring_ctx *ctx, diff --git a/io_uring/openclose.c b/io_uring/openclose.c index d1818ec9169b..67178e4bb282 100644 --- a/io_uring/openclose.c +++ b/io_uring/openclose.c @@ -33,7 +33,7 @@ struct io_close { static int __io_openat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_open *open = io_kiocb_to_cmd(req); + struct io_open *open = io_kiocb_to_cmd(req, struct io_open); const char __user *fname; int ret; @@ -66,7 +66,7 @@ static int __io_openat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe int io_openat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_open *open = io_kiocb_to_cmd(req); + struct io_open *open = io_kiocb_to_cmd(req, struct io_open); u64 mode = READ_ONCE(sqe->len); u64 flags = READ_ONCE(sqe->open_flags); @@ -76,7 +76,7 @@ int io_openat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_openat2_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_open *open = io_kiocb_to_cmd(req); + struct io_open *open = io_kiocb_to_cmd(req, struct io_open); struct open_how __user *how; size_t len; int ret; @@ -95,7 +95,7 @@ int io_openat2_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_openat2(struct io_kiocb *req, unsigned int issue_flags) { - struct io_open *open = io_kiocb_to_cmd(req); + struct io_open *open = io_kiocb_to_cmd(req, struct io_open); struct open_flags op; struct file *file; bool resolve_nonblock, nonblock_set; @@ -167,7 +167,7 @@ int io_openat(struct io_kiocb *req, unsigned int issue_flags) void io_open_cleanup(struct io_kiocb *req) { - struct io_open *open = io_kiocb_to_cmd(req); + struct io_open *open = io_kiocb_to_cmd(req, struct io_open); if (open->filename) putname(open->filename); @@ -187,14 +187,14 @@ int __io_close_fixed(struct io_ring_ctx *ctx, unsigned int issue_flags, static inline int io_close_fixed(struct io_kiocb *req, unsigned int issue_flags) { - struct io_close *close = io_kiocb_to_cmd(req); + struct io_close *close = io_kiocb_to_cmd(req, struct io_close); return __io_close_fixed(req->ctx, issue_flags, close->file_slot - 1); } int io_close_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_close *close = io_kiocb_to_cmd(req); + struct io_close *close = io_kiocb_to_cmd(req, struct io_close); if (sqe->off || sqe->addr || sqe->len || sqe->rw_flags || sqe->buf_index) return -EINVAL; @@ -212,7 +212,7 @@ int io_close_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_close(struct io_kiocb *req, unsigned int issue_flags) { struct files_struct *files = current->files; - struct io_close *close = io_kiocb_to_cmd(req); + struct io_close *close = io_kiocb_to_cmd(req, struct io_close); struct fdtable *fdt; struct file *file; int ret = -EBADF; diff --git a/io_uring/poll.c b/io_uring/poll.c index dadd293749b0..d5bad0bea6e4 100644 --- a/io_uring/poll.c +++ b/io_uring/poll.c @@ -85,7 +85,7 @@ static struct io_poll *io_poll_get_double(struct io_kiocb *req) static struct io_poll *io_poll_get_single(struct io_kiocb *req) { if (req->opcode == IORING_OP_POLL_ADD) - return io_kiocb_to_cmd(req); + return io_kiocb_to_cmd(req, struct io_poll); return &req->apoll->poll; } @@ -274,7 +274,7 @@ static void io_poll_task_func(struct io_kiocb *req, bool *locked) return; if (ret == IOU_POLL_DONE) { - struct io_poll *poll = io_kiocb_to_cmd(req); + struct io_poll *poll = io_kiocb_to_cmd(req, struct io_poll); req->cqe.res = mangle_poll(req->cqe.res & poll->events); } else if (ret != IOU_POLL_REMOVE_POLL_USE_RES) { req->cqe.res = ret; @@ -475,7 +475,7 @@ static void io_poll_queue_proc(struct file *file, struct wait_queue_head *head, struct poll_table_struct *p) { struct io_poll_table *pt = container_of(p, struct io_poll_table, pt); - struct io_poll *poll = io_kiocb_to_cmd(pt->req); + struct io_poll *poll = io_kiocb_to_cmd(pt->req, struct io_poll); __io_queue_proc(poll, pt, head, (struct io_poll **) &pt->req->async_data); @@ -821,7 +821,7 @@ static __poll_t io_poll_parse_events(const struct io_uring_sqe *sqe, int io_poll_remove_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_poll_update *upd = io_kiocb_to_cmd(req); + struct io_poll_update *upd = io_kiocb_to_cmd(req, struct io_poll_update); u32 flags; if (sqe->buf_index || sqe->splice_fd_in) @@ -851,7 +851,7 @@ int io_poll_remove_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_poll_add_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_poll *poll = io_kiocb_to_cmd(req); + struct io_poll *poll = io_kiocb_to_cmd(req, struct io_poll); u32 flags; if (sqe->buf_index || sqe->off || sqe->addr) @@ -868,7 +868,7 @@ int io_poll_add_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_poll_add(struct io_kiocb *req, unsigned int issue_flags) { - struct io_poll *poll = io_kiocb_to_cmd(req); + struct io_poll *poll = io_kiocb_to_cmd(req, struct io_poll); struct io_poll_table ipt; int ret; @@ -891,7 +891,7 @@ int io_poll_add(struct io_kiocb *req, unsigned int issue_flags) int io_poll_remove(struct io_kiocb *req, unsigned int issue_flags) { - struct io_poll_update *poll_update = io_kiocb_to_cmd(req); + struct io_poll_update *poll_update = io_kiocb_to_cmd(req, struct io_poll_update); struct io_cancel_data cd = { .data = poll_update->old_user_data, }; struct io_ring_ctx *ctx = req->ctx; struct io_hash_bucket *bucket; @@ -930,7 +930,7 @@ found: if (poll_update->update_events || poll_update->update_user_data) { /* only mask one event flags, keep behavior flags */ if (poll_update->update_events) { - struct io_poll *poll = io_kiocb_to_cmd(preq); + struct io_poll *poll = io_kiocb_to_cmd(preq, struct io_poll); poll->events &= ~0xffff; poll->events |= poll_update->events & 0xffff; diff --git a/io_uring/rsrc.c b/io_uring/rsrc.c index 59704b9ac537..71359a4d0bd4 100644 --- a/io_uring/rsrc.c +++ b/io_uring/rsrc.c @@ -657,7 +657,7 @@ __cold int io_register_rsrc(struct io_ring_ctx *ctx, void __user *arg, int io_rsrc_update_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_rsrc_update *up = io_kiocb_to_cmd(req); + struct io_rsrc_update *up = io_kiocb_to_cmd(req, struct io_rsrc_update); if (unlikely(req->flags & (REQ_F_FIXED_FILE | REQ_F_BUFFER_SELECT))) return -EINVAL; @@ -676,7 +676,7 @@ int io_rsrc_update_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) static int io_files_update_with_index_alloc(struct io_kiocb *req, unsigned int issue_flags) { - struct io_rsrc_update *up = io_kiocb_to_cmd(req); + struct io_rsrc_update *up = io_kiocb_to_cmd(req, struct io_rsrc_update); __s32 __user *fds = u64_to_user_ptr(up->arg); unsigned int done; struct file *file; @@ -714,7 +714,7 @@ static int io_files_update_with_index_alloc(struct io_kiocb *req, static int io_files_update(struct io_kiocb *req, unsigned int issue_flags) { - struct io_rsrc_update *up = io_kiocb_to_cmd(req); + struct io_rsrc_update *up = io_kiocb_to_cmd(req, struct io_rsrc_update); struct io_ring_ctx *ctx = req->ctx; struct io_uring_rsrc_update2 up2; int ret; @@ -743,7 +743,7 @@ static int io_files_update(struct io_kiocb *req, unsigned int issue_flags) static int io_notif_update(struct io_kiocb *req, unsigned int issue_flags) { - struct io_rsrc_update *up = io_kiocb_to_cmd(req); + struct io_rsrc_update *up = io_kiocb_to_cmd(req, struct io_rsrc_update); struct io_ring_ctx *ctx = req->ctx; unsigned len = up->nr_args; unsigned idx_end, idx = up->offset; @@ -778,7 +778,7 @@ out: int io_rsrc_update(struct io_kiocb *req, unsigned int issue_flags) { - struct io_rsrc_update *up = io_kiocb_to_cmd(req); + struct io_rsrc_update *up = io_kiocb_to_cmd(req, struct io_rsrc_update); switch (up->type) { case IORING_RSRC_UPDATE_FILES: diff --git a/io_uring/rw.c b/io_uring/rw.c index b20ba87e4926..1babd77da79c 100644 --- a/io_uring/rw.c +++ b/io_uring/rw.c @@ -35,7 +35,7 @@ static inline bool io_file_supports_nowait(struct io_kiocb *req) int io_prep_rw(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_rw *rw = io_kiocb_to_cmd(req); + struct io_rw *rw = io_kiocb_to_cmd(req, struct io_rw); unsigned ioprio; int ret; @@ -102,7 +102,7 @@ static inline void io_rw_done(struct kiocb *kiocb, ssize_t ret) static inline loff_t *io_kiocb_update_pos(struct io_kiocb *req) { - struct io_rw *rw = io_kiocb_to_cmd(req); + struct io_rw *rw = io_kiocb_to_cmd(req, struct io_rw); if (rw->kiocb.ki_pos != -1) return &rw->kiocb.ki_pos; @@ -186,7 +186,7 @@ static void kiocb_end_write(struct io_kiocb *req) static bool __io_complete_rw_common(struct io_kiocb *req, long res) { - struct io_rw *rw = io_kiocb_to_cmd(req); + struct io_rw *rw = io_kiocb_to_cmd(req, struct io_rw); if (rw->kiocb.ki_flags & IOCB_WRITE) { kiocb_end_write(req); @@ -241,7 +241,7 @@ static int kiocb_done(struct io_kiocb *req, ssize_t ret, unsigned int issue_flags) { struct io_async_rw *io = req->async_data; - struct io_rw *rw = io_kiocb_to_cmd(req); + struct io_rw *rw = io_kiocb_to_cmd(req, struct io_rw); /* add previously done IO, if any */ if (req_has_async_data(req) && io->bytes_done > 0) { @@ -277,7 +277,7 @@ static int kiocb_done(struct io_kiocb *req, ssize_t ret, static ssize_t io_compat_import(struct io_kiocb *req, struct iovec *iov, unsigned int issue_flags) { - struct io_rw *rw = io_kiocb_to_cmd(req); + struct io_rw *rw = io_kiocb_to_cmd(req, struct io_rw); struct compat_iovec __user *uiov; compat_ssize_t clen; void __user *buf; @@ -305,7 +305,7 @@ static ssize_t io_compat_import(struct io_kiocb *req, struct iovec *iov, static ssize_t __io_iov_buffer_select(struct io_kiocb *req, struct iovec *iov, unsigned int issue_flags) { - struct io_rw *rw = io_kiocb_to_cmd(req); + struct io_rw *rw = io_kiocb_to_cmd(req, struct io_rw); struct iovec __user *uiov = u64_to_user_ptr(rw->addr); void __user *buf; ssize_t len; @@ -328,7 +328,7 @@ static ssize_t __io_iov_buffer_select(struct io_kiocb *req, struct iovec *iov, static ssize_t io_iov_buffer_select(struct io_kiocb *req, struct iovec *iov, unsigned int issue_flags) { - struct io_rw *rw = io_kiocb_to_cmd(req); + struct io_rw *rw = io_kiocb_to_cmd(req, struct io_rw); if (req->flags & (REQ_F_BUFFER_SELECTED|REQ_F_BUFFER_RING)) { iov[0].iov_base = u64_to_user_ptr(rw->addr); @@ -350,7 +350,7 @@ static struct iovec *__io_import_iovec(int ddir, struct io_kiocb *req, struct io_rw_state *s, unsigned int issue_flags) { - struct io_rw *rw = io_kiocb_to_cmd(req); + struct io_rw *rw = io_kiocb_to_cmd(req, struct io_rw); struct iov_iter *iter = &s->iter; u8 opcode = req->opcode; struct iovec *iovec; @@ -571,7 +571,7 @@ static int io_async_buf_func(struct wait_queue_entry *wait, unsigned mode, { struct wait_page_queue *wpq; struct io_kiocb *req = wait->private; - struct io_rw *rw = io_kiocb_to_cmd(req); + struct io_rw *rw = io_kiocb_to_cmd(req, struct io_rw); struct wait_page_key *key = arg; wpq = container_of(wait, struct wait_page_queue, wait); @@ -601,7 +601,7 @@ static bool io_rw_should_retry(struct io_kiocb *req) { struct io_async_rw *io = req->async_data; struct wait_page_queue *wait = &io->wpq; - struct io_rw *rw = io_kiocb_to_cmd(req); + struct io_rw *rw = io_kiocb_to_cmd(req, struct io_rw); struct kiocb *kiocb = &rw->kiocb; /* never retry for NOWAIT, we just complete with -EAGAIN */ @@ -649,7 +649,7 @@ static bool need_complete_io(struct io_kiocb *req) static int io_rw_init_file(struct io_kiocb *req, fmode_t mode) { - struct io_rw *rw = io_kiocb_to_cmd(req); + struct io_rw *rw = io_kiocb_to_cmd(req, struct io_rw); struct kiocb *kiocb = &rw->kiocb; struct io_ring_ctx *ctx = req->ctx; struct file *file = req->file; @@ -694,7 +694,7 @@ static int io_rw_init_file(struct io_kiocb *req, fmode_t mode) int io_read(struct io_kiocb *req, unsigned int issue_flags) { - struct io_rw *rw = io_kiocb_to_cmd(req); + struct io_rw *rw = io_kiocb_to_cmd(req, struct io_rw); struct io_rw_state __s, *s = &__s; struct iovec *iovec; struct kiocb *kiocb = &rw->kiocb; @@ -839,7 +839,7 @@ done: int io_write(struct io_kiocb *req, unsigned int issue_flags) { - struct io_rw *rw = io_kiocb_to_cmd(req); + struct io_rw *rw = io_kiocb_to_cmd(req, struct io_rw); struct io_rw_state __s, *s = &__s; struct iovec *iovec; struct kiocb *kiocb = &rw->kiocb; @@ -994,7 +994,7 @@ int io_do_iopoll(struct io_ring_ctx *ctx, bool force_nonspin) wq_list_for_each(pos, start, &ctx->iopoll_list) { struct io_kiocb *req = container_of(pos, struct io_kiocb, comp_list); - struct io_rw *rw = io_kiocb_to_cmd(req); + struct io_rw *rw = io_kiocb_to_cmd(req, struct io_rw); int ret; /* diff --git a/io_uring/splice.c b/io_uring/splice.c index b013ba34bffa..53e4232d0866 100644 --- a/io_uring/splice.c +++ b/io_uring/splice.c @@ -26,7 +26,7 @@ struct io_splice { static int __io_splice_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_splice *sp = io_kiocb_to_cmd(req); + struct io_splice *sp = io_kiocb_to_cmd(req, struct io_splice); unsigned int valid_flags = SPLICE_F_FD_IN_FIXED | SPLICE_F_ALL; sp->len = READ_ONCE(sqe->len); @@ -46,7 +46,7 @@ int io_tee_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_tee(struct io_kiocb *req, unsigned int issue_flags) { - struct io_splice *sp = io_kiocb_to_cmd(req); + struct io_splice *sp = io_kiocb_to_cmd(req, struct io_splice); struct file *out = sp->file_out; unsigned int flags = sp->flags & ~SPLICE_F_FD_IN_FIXED; struct file *in; @@ -78,7 +78,7 @@ done: int io_splice_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_splice *sp = io_kiocb_to_cmd(req); + struct io_splice *sp = io_kiocb_to_cmd(req, struct io_splice); sp->off_in = READ_ONCE(sqe->splice_off_in); sp->off_out = READ_ONCE(sqe->off); @@ -87,7 +87,7 @@ int io_splice_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_splice(struct io_kiocb *req, unsigned int issue_flags) { - struct io_splice *sp = io_kiocb_to_cmd(req); + struct io_splice *sp = io_kiocb_to_cmd(req, struct io_splice); struct file *out = sp->file_out; unsigned int flags = sp->flags & ~SPLICE_F_FD_IN_FIXED; loff_t *poff_in, *poff_out; diff --git a/io_uring/sqpoll.c b/io_uring/sqpoll.c index 76d4d70c733a..559652380672 100644 --- a/io_uring/sqpoll.c +++ b/io_uring/sqpoll.c @@ -235,8 +235,6 @@ static int io_sq_thread(void *data) set_cpus_allowed_ptr(current, cpu_online_mask); current->flags |= PF_NO_SETAFFINITY; - audit_alloc_kernel(current); - mutex_lock(&sqd->lock); while (1) { bool cap_entries, sqt_spin = false; @@ -310,8 +308,6 @@ static int io_sq_thread(void *data) io_run_task_work(); mutex_unlock(&sqd->lock); - audit_free(current); - complete(&sqd->exited); do_exit(0); } diff --git a/io_uring/statx.c b/io_uring/statx.c index 6056cd7f4876..d8fc933d3f59 100644 --- a/io_uring/statx.c +++ b/io_uring/statx.c @@ -22,7 +22,7 @@ struct io_statx { int io_statx_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_statx *sx = io_kiocb_to_cmd(req); + struct io_statx *sx = io_kiocb_to_cmd(req, struct io_statx); const char __user *path; if (sqe->buf_index || sqe->splice_fd_in) @@ -53,7 +53,7 @@ int io_statx_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_statx(struct io_kiocb *req, unsigned int issue_flags) { - struct io_statx *sx = io_kiocb_to_cmd(req); + struct io_statx *sx = io_kiocb_to_cmd(req, struct io_statx); int ret; if (issue_flags & IO_URING_F_NONBLOCK) @@ -66,7 +66,7 @@ int io_statx(struct io_kiocb *req, unsigned int issue_flags) void io_statx_cleanup(struct io_kiocb *req) { - struct io_statx *sx = io_kiocb_to_cmd(req); + struct io_statx *sx = io_kiocb_to_cmd(req, struct io_statx); if (sx->filename) putname(sx->filename); diff --git a/io_uring/sync.c b/io_uring/sync.c index f2102afa79ca..64e87ea2b8fb 100644 --- a/io_uring/sync.c +++ b/io_uring/sync.c @@ -24,7 +24,7 @@ struct io_sync { int io_sfr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_sync *sync = io_kiocb_to_cmd(req); + struct io_sync *sync = io_kiocb_to_cmd(req, struct io_sync); if (unlikely(sqe->addr || sqe->buf_index || sqe->splice_fd_in)) return -EINVAL; @@ -37,7 +37,7 @@ int io_sfr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_sync_file_range(struct io_kiocb *req, unsigned int issue_flags) { - struct io_sync *sync = io_kiocb_to_cmd(req); + struct io_sync *sync = io_kiocb_to_cmd(req, struct io_sync); int ret; /* sync_file_range always requires a blocking context */ @@ -51,7 +51,7 @@ int io_sync_file_range(struct io_kiocb *req, unsigned int issue_flags) int io_fsync_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_sync *sync = io_kiocb_to_cmd(req); + struct io_sync *sync = io_kiocb_to_cmd(req, struct io_sync); if (unlikely(sqe->addr || sqe->buf_index || sqe->splice_fd_in)) return -EINVAL; @@ -67,7 +67,7 @@ int io_fsync_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_fsync(struct io_kiocb *req, unsigned int issue_flags) { - struct io_sync *sync = io_kiocb_to_cmd(req); + struct io_sync *sync = io_kiocb_to_cmd(req, struct io_sync); loff_t end = sync->off + sync->len; int ret; @@ -83,7 +83,7 @@ int io_fsync(struct io_kiocb *req, unsigned int issue_flags) int io_fallocate_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_sync *sync = io_kiocb_to_cmd(req); + struct io_sync *sync = io_kiocb_to_cmd(req, struct io_sync); if (sqe->buf_index || sqe->rw_flags || sqe->splice_fd_in) return -EINVAL; @@ -96,7 +96,7 @@ int io_fallocate_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_fallocate(struct io_kiocb *req, unsigned int issue_flags) { - struct io_sync *sync = io_kiocb_to_cmd(req); + struct io_sync *sync = io_kiocb_to_cmd(req, struct io_sync); int ret; /* fallocate always requiring blocking context */ diff --git a/io_uring/timeout.c b/io_uring/timeout.c index 2f9e56935479..78ea2c64b70e 100644 --- a/io_uring/timeout.c +++ b/io_uring/timeout.c @@ -36,7 +36,7 @@ struct io_timeout_rem { static inline bool io_is_timeout_noseq(struct io_kiocb *req) { - struct io_timeout *timeout = io_kiocb_to_cmd(req); + struct io_timeout *timeout = io_kiocb_to_cmd(req, struct io_timeout); return !timeout->off; } @@ -56,7 +56,7 @@ static bool io_kill_timeout(struct io_kiocb *req, int status) struct io_timeout_data *io = req->async_data; if (hrtimer_try_to_cancel(&io->timer) != -1) { - struct io_timeout *timeout = io_kiocb_to_cmd(req); + struct io_timeout *timeout = io_kiocb_to_cmd(req, struct io_timeout); if (status) req_set_fail(req); @@ -188,7 +188,7 @@ struct io_kiocb *__io_disarm_linked_timeout(struct io_kiocb *req, __must_hold(&req->ctx->timeout_lock) { struct io_timeout_data *io = link->async_data; - struct io_timeout *timeout = io_kiocb_to_cmd(link); + struct io_timeout *timeout = io_kiocb_to_cmd(link, struct io_timeout); io_remove_next_linked(req); timeout->head = NULL; @@ -205,7 +205,7 @@ static enum hrtimer_restart io_timeout_fn(struct hrtimer *timer) struct io_timeout_data *data = container_of(timer, struct io_timeout_data, timer); struct io_kiocb *req = data->req; - struct io_timeout *timeout = io_kiocb_to_cmd(req); + struct io_timeout *timeout = io_kiocb_to_cmd(req, struct io_timeout); struct io_ring_ctx *ctx = req->ctx; unsigned long flags; @@ -252,7 +252,7 @@ static struct io_kiocb *io_timeout_extract(struct io_ring_ctx *ctx, io = req->async_data; if (hrtimer_try_to_cancel(&io->timer) == -1) return ERR_PTR(-EALREADY); - timeout = io_kiocb_to_cmd(req); + timeout = io_kiocb_to_cmd(req, struct io_timeout); list_del_init(&timeout->list); return req; } @@ -275,7 +275,7 @@ int io_timeout_cancel(struct io_ring_ctx *ctx, struct io_cancel_data *cd) static void io_req_task_link_timeout(struct io_kiocb *req, bool *locked) { unsigned issue_flags = *locked ? 0 : IO_URING_F_UNLOCKED; - struct io_timeout *timeout = io_kiocb_to_cmd(req); + struct io_timeout *timeout = io_kiocb_to_cmd(req, struct io_timeout); struct io_kiocb *prev = timeout->prev; int ret = -ENOENT; @@ -302,7 +302,7 @@ static enum hrtimer_restart io_link_timeout_fn(struct hrtimer *timer) struct io_timeout_data *data = container_of(timer, struct io_timeout_data, timer); struct io_kiocb *prev, *req = data->req; - struct io_timeout *timeout = io_kiocb_to_cmd(req); + struct io_timeout *timeout = io_kiocb_to_cmd(req, struct io_timeout); struct io_ring_ctx *ctx = req->ctx; unsigned long flags; @@ -378,7 +378,7 @@ static int io_timeout_update(struct io_ring_ctx *ctx, __u64 user_data, { struct io_cancel_data cd = { .data = user_data, }; struct io_kiocb *req = io_timeout_extract(ctx, &cd); - struct io_timeout *timeout = io_kiocb_to_cmd(req); + struct io_timeout *timeout = io_kiocb_to_cmd(req, struct io_timeout); struct io_timeout_data *data; if (IS_ERR(req)) @@ -395,7 +395,7 @@ static int io_timeout_update(struct io_ring_ctx *ctx, __u64 user_data, int io_timeout_remove_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_timeout_rem *tr = io_kiocb_to_cmd(req); + struct io_timeout_rem *tr = io_kiocb_to_cmd(req, struct io_timeout_rem); if (unlikely(req->flags & (REQ_F_FIXED_FILE | REQ_F_BUFFER_SELECT))) return -EINVAL; @@ -435,7 +435,7 @@ static inline enum hrtimer_mode io_translate_timeout_mode(unsigned int flags) */ int io_timeout_remove(struct io_kiocb *req, unsigned int issue_flags) { - struct io_timeout_rem *tr = io_kiocb_to_cmd(req); + struct io_timeout_rem *tr = io_kiocb_to_cmd(req, struct io_timeout_rem); struct io_ring_ctx *ctx = req->ctx; int ret; @@ -466,7 +466,7 @@ static int __io_timeout_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe, bool is_timeout_link) { - struct io_timeout *timeout = io_kiocb_to_cmd(req); + struct io_timeout *timeout = io_kiocb_to_cmd(req, struct io_timeout); struct io_timeout_data *data; unsigned flags; u32 off = READ_ONCE(sqe->off); @@ -532,7 +532,7 @@ int io_link_timeout_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_timeout(struct io_kiocb *req, unsigned int issue_flags) { - struct io_timeout *timeout = io_kiocb_to_cmd(req); + struct io_timeout *timeout = io_kiocb_to_cmd(req, struct io_timeout); struct io_ring_ctx *ctx = req->ctx; struct io_timeout_data *data = req->async_data; struct list_head *entry; @@ -583,7 +583,7 @@ add: void io_queue_linked_timeout(struct io_kiocb *req) { - struct io_timeout *timeout = io_kiocb_to_cmd(req); + struct io_timeout *timeout = io_kiocb_to_cmd(req, struct io_timeout); struct io_ring_ctx *ctx = req->ctx; spin_lock_irq(&ctx->timeout_lock); diff --git a/io_uring/uring_cmd.c b/io_uring/uring_cmd.c index 0a421ed51e7e..8e0cc2d9205e 100644 --- a/io_uring/uring_cmd.c +++ b/io_uring/uring_cmd.c @@ -11,7 +11,7 @@ static void io_uring_cmd_work(struct io_kiocb *req, bool *locked) { - struct io_uring_cmd *ioucmd = io_kiocb_to_cmd(req); + struct io_uring_cmd *ioucmd = io_kiocb_to_cmd(req, struct io_uring_cmd); ioucmd->task_work_cb(ioucmd); } @@ -46,7 +46,7 @@ void io_uring_cmd_done(struct io_uring_cmd *ioucmd, ssize_t ret, ssize_t res2) if (ret < 0) req_set_fail(req); - io_req_set_res(req, 0, ret); + io_req_set_res(req, ret, 0); if (req->ctx->flags & IORING_SETUP_CQE32) io_req_set_cqe32_extra(req, res2, 0); __io_req_complete(req, 0); @@ -55,9 +55,12 @@ EXPORT_SYMBOL_GPL(io_uring_cmd_done); int io_uring_cmd_prep_async(struct io_kiocb *req) { - struct io_uring_cmd *ioucmd = io_kiocb_to_cmd(req); + struct io_uring_cmd *ioucmd = io_kiocb_to_cmd(req, struct io_uring_cmd); size_t cmd_size; + BUILD_BUG_ON(uring_cmd_pdu_size(0) != 16); + BUILD_BUG_ON(uring_cmd_pdu_size(1) != 80); + cmd_size = uring_cmd_pdu_size(req->ctx->flags & IORING_SETUP_SQE128); memcpy(req->async_data, ioucmd->cmd, cmd_size); @@ -66,7 +69,7 @@ int io_uring_cmd_prep_async(struct io_kiocb *req) int io_uring_cmd_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_uring_cmd *ioucmd = io_kiocb_to_cmd(req); + struct io_uring_cmd *ioucmd = io_kiocb_to_cmd(req, struct io_uring_cmd); if (sqe->rw_flags || sqe->__pad1) return -EINVAL; @@ -77,7 +80,7 @@ int io_uring_cmd_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_uring_cmd(struct io_kiocb *req, unsigned int issue_flags) { - struct io_uring_cmd *ioucmd = io_kiocb_to_cmd(req); + struct io_uring_cmd *ioucmd = io_kiocb_to_cmd(req, struct io_uring_cmd); struct io_ring_ctx *ctx = req->ctx; struct file *file = req->file; int ret; @@ -106,7 +109,9 @@ int io_uring_cmd(struct io_kiocb *req, unsigned int issue_flags) } if (ret != -EIOCBQUEUED) { - io_uring_cmd_done(ioucmd, ret, 0); + if (ret < 0) + req_set_fail(req); + io_req_set_res(req, ret, 0); return IOU_OK; } diff --git a/io_uring/xattr.c b/io_uring/xattr.c index b179f9acd5ac..84180afd090b 100644 --- a/io_uring/xattr.c +++ b/io_uring/xattr.c @@ -24,7 +24,7 @@ struct io_xattr { void io_xattr_cleanup(struct io_kiocb *req) { - struct io_xattr *ix = io_kiocb_to_cmd(req); + struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr); if (ix->filename) putname(ix->filename); @@ -44,7 +44,7 @@ static void io_xattr_finish(struct io_kiocb *req, int ret) static int __io_getxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_xattr *ix = io_kiocb_to_cmd(req); + struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr); const char __user *name; int ret; @@ -85,7 +85,7 @@ int io_fgetxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_getxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_xattr *ix = io_kiocb_to_cmd(req); + struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr); const char __user *path; int ret; @@ -106,7 +106,7 @@ int io_getxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_fgetxattr(struct io_kiocb *req, unsigned int issue_flags) { - struct io_xattr *ix = io_kiocb_to_cmd(req); + struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr); int ret; if (issue_flags & IO_URING_F_NONBLOCK) @@ -122,7 +122,7 @@ int io_fgetxattr(struct io_kiocb *req, unsigned int issue_flags) int io_getxattr(struct io_kiocb *req, unsigned int issue_flags) { - struct io_xattr *ix = io_kiocb_to_cmd(req); + struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr); unsigned int lookup_flags = LOOKUP_FOLLOW; struct path path; int ret; @@ -151,7 +151,7 @@ retry: static int __io_setxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_xattr *ix = io_kiocb_to_cmd(req); + struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr); const char __user *name; int ret; @@ -181,7 +181,7 @@ static int __io_setxattr_prep(struct io_kiocb *req, int io_setxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - struct io_xattr *ix = io_kiocb_to_cmd(req); + struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr); const char __user *path; int ret; @@ -208,7 +208,7 @@ int io_fsetxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) static int __io_setxattr(struct io_kiocb *req, unsigned int issue_flags, struct path *path) { - struct io_xattr *ix = io_kiocb_to_cmd(req); + struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr); int ret; ret = mnt_want_write(path->mnt); @@ -234,7 +234,7 @@ int io_fsetxattr(struct io_kiocb *req, unsigned int issue_flags) int io_setxattr(struct io_kiocb *req, unsigned int issue_flags) { - struct io_xattr *ix = io_kiocb_to_cmd(req); + struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr); unsigned int lookup_flags = LOOKUP_FOLLOW; struct path path; int ret; diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 3a8c9d744800..dd8d9ab747c3 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1073,31 +1073,6 @@ int audit_alloc(struct task_struct *tsk) return 0; } -/** - * audit_alloc_kernel - allocate an audit_context for a kernel task - * @tsk: the kernel task - * - * Similar to the audit_alloc() function, but intended for kernel private - * threads. Returns zero on success, negative values on failure. - */ -int audit_alloc_kernel(struct task_struct *tsk) -{ - /* - * At the moment we are just going to call into audit_alloc() to - * simplify the code, but there two things to keep in mind with this - * approach: - * - * 1. Filtering internal kernel tasks is a bit laughable in almost all - * cases, but there is at least one case where there is a benefit: - * the '-a task,never' case allows the admin to effectively disable - * task auditing at runtime. - * - * 2. The {set,clear}_task_syscall_work() ops likely have zero effect - * on these internal kernel tasks, but they probably don't hurt either. - */ - return audit_alloc(tsk); -} - static inline void audit_free_context(struct audit_context *context) { /* resetting is extra work, but it is likely just noise */ diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c index d3e734bf8056..624527401d4d 100644 --- a/kernel/bpf/arraymap.c +++ b/kernel/bpf/arraymap.c @@ -649,6 +649,11 @@ static int bpf_iter_init_array_map(void *priv_data, seq_info->percpu_value_buf = value_buf; } + /* bpf_iter_attach_map() acquires a map uref, and the uref may be + * released before or in the middle of iterating map elements, so + * acquire an extra map uref for iterator. + */ + bpf_map_inc_with_uref(map); seq_info->map = map; return 0; } @@ -657,6 +662,7 @@ static void bpf_iter_fini_array_map(void *priv_data) { struct bpf_iter_seq_array_map_info *seq_info = priv_data; + bpf_map_put_with_uref(seq_info->map); kfree(seq_info->percpu_value_buf); } diff --git a/kernel/bpf/bpf_iter.c b/kernel/bpf/bpf_iter.c index 2726a5950cfa..24b755eca0b3 100644 --- a/kernel/bpf/bpf_iter.c +++ b/kernel/bpf/bpf_iter.c @@ -68,13 +68,18 @@ static void bpf_iter_done_stop(struct seq_file *seq) iter_priv->done_stop = true; } +static inline bool bpf_iter_target_support_resched(const struct bpf_iter_target_info *tinfo) +{ + return tinfo->reg_info->feature & BPF_ITER_RESCHED; +} + static bool bpf_iter_support_resched(struct seq_file *seq) { struct bpf_iter_priv_data *iter_priv; iter_priv = container_of(seq->private, struct bpf_iter_priv_data, target_private); - return iter_priv->tinfo->reg_info->feature & BPF_ITER_RESCHED; + return bpf_iter_target_support_resched(iter_priv->tinfo); } /* maximum visited objects before bailing out */ @@ -537,6 +542,10 @@ int bpf_iter_link_attach(const union bpf_attr *attr, bpfptr_t uattr, if (!tinfo) return -ENOENT; + /* Only allow sleepable program for resched-able iterator */ + if (prog->aux->sleepable && !bpf_iter_target_support_resched(tinfo)) + return -EINVAL; + link = kzalloc(sizeof(*link), GFP_USER | __GFP_NOWARN); if (!link) return -ENOMEM; diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c index da7578426a46..6c530a5e560a 100644 --- a/kernel/bpf/hashtab.c +++ b/kernel/bpf/hashtab.c @@ -311,12 +311,8 @@ static struct htab_elem *prealloc_lru_pop(struct bpf_htab *htab, void *key, struct htab_elem *l; if (node) { - u32 key_size = htab->map.key_size; - l = container_of(node, struct htab_elem, lru_node); - memcpy(l->key, key, key_size); - check_and_init_map_value(&htab->map, - l->key + round_up(key_size, 8)); + memcpy(l->key, key, htab->map.key_size); return l; } @@ -2064,6 +2060,7 @@ static int bpf_iter_init_hash_map(void *priv_data, seq_info->percpu_value_buf = value_buf; } + bpf_map_inc_with_uref(map); seq_info->map = map; seq_info->htab = container_of(map, struct bpf_htab, map); return 0; @@ -2073,6 +2070,7 @@ static void bpf_iter_fini_hash_map(void *priv_data) { struct bpf_iter_seq_hash_map_info *seq_info = priv_data; + bpf_map_put_with_uref(seq_info->map); kfree(seq_info->percpu_value_buf); } diff --git a/kernel/bpf/reuseport_array.c b/kernel/bpf/reuseport_array.c index e2618fb5870e..85fa9dbfa8bf 100644 --- a/kernel/bpf/reuseport_array.c +++ b/kernel/bpf/reuseport_array.c @@ -21,14 +21,11 @@ static struct reuseport_array *reuseport_array(struct bpf_map *map) /* The caller must hold the reuseport_lock */ void bpf_sk_reuseport_detach(struct sock *sk) { - uintptr_t sk_user_data; + struct sock __rcu **socks; write_lock_bh(&sk->sk_callback_lock); - sk_user_data = (uintptr_t)sk->sk_user_data; - if (sk_user_data & SK_USER_DATA_BPF) { - struct sock __rcu **socks; - - socks = (void *)(sk_user_data & SK_USER_DATA_PTRMASK); + socks = __rcu_dereference_sk_user_data_with_flags(sk, SK_USER_DATA_BPF); + if (socks) { WRITE_ONCE(sk->sk_user_data, NULL); /* * Do not move this NULL assignment outside of diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 83c7136c5788..a4d40d98428a 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -3886,6 +3886,7 @@ static int bpf_prog_get_info_by_fd(struct file *file, union bpf_attr __user *uattr) { struct bpf_prog_info __user *uinfo = u64_to_user_ptr(attr->info.info); + struct btf *attach_btf = bpf_prog_get_target_btf(prog); struct bpf_prog_info info; u32 info_len = attr->info.info_len; struct bpf_prog_kstats stats; @@ -4088,10 +4089,8 @@ static int bpf_prog_get_info_by_fd(struct file *file, if (prog->aux->btf) info.btf_id = btf_obj_id(prog->aux->btf); info.attach_btf_id = prog->aux->attach_btf_id; - if (prog->aux->attach_btf) - info.attach_btf_obj_id = btf_obj_id(prog->aux->attach_btf); - else if (prog->aux->dst_prog) - info.attach_btf_obj_id = btf_obj_id(prog->aux->dst_prog->aux->attach_btf); + if (attach_btf) + info.attach_btf_obj_id = btf_obj_id(attach_btf); ulen = info.nr_func_info; info.nr_func_info = prog->aux->func_info_cnt; @@ -5072,9 +5071,6 @@ static bool syscall_prog_is_valid_access(int off, int size, BPF_CALL_3(bpf_sys_bpf, int, cmd, union bpf_attr *, attr, u32, attr_size) { - struct bpf_prog * __maybe_unused prog; - struct bpf_tramp_run_ctx __maybe_unused run_ctx; - switch (cmd) { case BPF_MAP_CREATE: case BPF_MAP_UPDATE_ELEM: @@ -5084,6 +5080,26 @@ BPF_CALL_3(bpf_sys_bpf, int, cmd, union bpf_attr *, attr, u32, attr_size) case BPF_LINK_CREATE: case BPF_RAW_TRACEPOINT_OPEN: break; + default: + return -EINVAL; + } + return __sys_bpf(cmd, KERNEL_BPFPTR(attr), attr_size); +} + + +/* To shut up -Wmissing-prototypes. + * This function is used by the kernel light skeleton + * to load bpf programs when modules are loaded or during kernel boot. + * See tools/lib/bpf/skel_internal.h + */ +int kern_sys_bpf(int cmd, union bpf_attr *attr, unsigned int size); + +int kern_sys_bpf(int cmd, union bpf_attr *attr, unsigned int size) +{ + struct bpf_prog * __maybe_unused prog; + struct bpf_tramp_run_ctx __maybe_unused run_ctx; + + switch (cmd) { #ifdef CONFIG_BPF_JIT /* __bpf_prog_enter_sleepable used by trampoline and JIT */ case BPF_PROG_TEST_RUN: if (attr->test.data_in || attr->test.data_out || @@ -5114,11 +5130,10 @@ BPF_CALL_3(bpf_sys_bpf, int, cmd, union bpf_attr *, attr, u32, attr_size) return 0; #endif default: - return -EINVAL; + return ____bpf_sys_bpf(cmd, attr, size); } - return __sys_bpf(cmd, KERNEL_BPFPTR(attr), attr_size); } -EXPORT_SYMBOL(bpf_sys_bpf); +EXPORT_SYMBOL(kern_sys_bpf); static const struct bpf_func_proto bpf_sys_bpf_proto = { .func = bpf_sys_bpf, diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c index 0f532e6a717f..ff87e38af8a7 100644 --- a/kernel/bpf/trampoline.c +++ b/kernel/bpf/trampoline.c @@ -841,7 +841,10 @@ void bpf_trampoline_put(struct bpf_trampoline *tr) * multiple rcu callbacks. */ hlist_del(&tr->hlist); - kfree(tr->fops); + if (tr->fops) { + ftrace_free_filter(tr->fops); + kfree(tr->fops); + } kfree(tr); out: mutex_unlock(&trampoline_mutex); diff --git a/lib/nodemask.c b/lib/nodemask.c deleted file mode 100644 index b8a433d16b51..000000000000 --- a/lib/nodemask.c +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include <linux/nodemask.h> -#include <linux/module.h> -#include <linux/random.h> - -EXPORT_SYMBOL(__next_node_in); - -#ifdef CONFIG_NUMA -/* - * Return the bit number of a random bit set in the nodemask. - * (returns NUMA_NO_NODE if nodemask is empty) - */ -int node_random(const nodemask_t *maskp) -{ - int w, bit = NUMA_NO_NODE; - - w = nodes_weight(*maskp); - if (w) - bit = bitmap_ord_to_pos(maskp->bits, - get_random_int() % w, MAX_NUMNODES); - return bit; -} -#endif diff --git a/net/ax25/ax25_timer.c b/net/ax25/ax25_timer.c index 85865ebfdfa2..9f7cb0a7c73f 100644 --- a/net/ax25/ax25_timer.c +++ b/net/ax25/ax25_timer.c @@ -108,10 +108,12 @@ int ax25_t1timer_running(ax25_cb *ax25) unsigned long ax25_display_timer(struct timer_list *timer) { + long delta = timer->expires - jiffies; + if (!timer_pending(timer)) return 0; - return timer->expires - jiffies; + return max(0L, delta); } EXPORT_SYMBOL(ax25_display_timer); diff --git a/net/bluetooth/aosp.c b/net/bluetooth/aosp.c index 432ae3aac9e3..1d67836e95e1 100644 --- a/net/bluetooth/aosp.c +++ b/net/bluetooth/aosp.c @@ -54,7 +54,10 @@ void aosp_do_open(struct hci_dev *hdev) /* LE Get Vendor Capabilities Command */ skb = __hci_cmd_sync(hdev, hci_opcode_pack(0x3f, 0x153), 0, NULL, HCI_CMD_TIMEOUT); - if (IS_ERR(skb)) { + if (IS_ERR_OR_NULL(skb)) { + if (!skb) + skb = ERR_PTR(-EIO); + bt_dev_err(hdev, "AOSP get vendor capabilities (%ld)", PTR_ERR(skb)); return; @@ -152,7 +155,10 @@ static int enable_quality_report(struct hci_dev *hdev) skb = __hci_cmd_sync(hdev, BQR_OPCODE, sizeof(cp), &cp, HCI_CMD_TIMEOUT); - if (IS_ERR(skb)) { + if (IS_ERR_OR_NULL(skb)) { + if (!skb) + skb = ERR_PTR(-EIO); + bt_dev_err(hdev, "Enabling Android BQR failed (%ld)", PTR_ERR(skb)); return PTR_ERR(skb); @@ -171,7 +177,10 @@ static int disable_quality_report(struct hci_dev *hdev) skb = __hci_cmd_sync(hdev, BQR_OPCODE, sizeof(cp), &cp, HCI_CMD_TIMEOUT); - if (IS_ERR(skb)) { + if (IS_ERR_OR_NULL(skb)) { + if (!skb) + skb = ERR_PTR(-EIO); + bt_dev_err(hdev, "Disabling Android BQR failed (%ld)", PTR_ERR(skb)); return PTR_ERR(skb); diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index f54864e19866..9777e7b109ee 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -1551,8 +1551,8 @@ static void cis_add(struct iso_list_data *d, struct bt_iso_qos *qos) cis->cis_id = qos->cis; cis->c_sdu = cpu_to_le16(qos->out.sdu); cis->p_sdu = cpu_to_le16(qos->in.sdu); - cis->c_phy = qos->out.phy; - cis->p_phy = qos->in.phy; + cis->c_phy = qos->out.phy ? qos->out.phy : qos->in.phy; + cis->p_phy = qos->in.phy ? qos->in.phy : qos->out.phy; cis->c_rtn = qos->out.rtn; cis->p_rtn = qos->in.rtn; @@ -1735,13 +1735,6 @@ struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst, if (!qos->in.latency) qos->in.latency = qos->out.latency; - /* Mirror PHYs that are disabled as SDU will be set to 0 */ - if (!qos->in.phy) - qos->in.phy = qos->out.phy; - - if (!qos->out.phy) - qos->out.phy = qos->in.phy; - if (!hci_le_set_cig_params(cis, qos)) { hci_conn_drop(cis); return ERR_PTR(-EINVAL); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index ea33dd0cd478..485c814cf44a 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -328,14 +328,17 @@ static u8 hci_cc_delete_stored_link_key(struct hci_dev *hdev, void *data, struct sk_buff *skb) { struct hci_rp_delete_stored_link_key *rp = data; + u16 num_keys; bt_dev_dbg(hdev, "status 0x%2.2x", rp->status); if (rp->status) return rp->status; - if (rp->num_keys <= hdev->stored_num_keys) - hdev->stored_num_keys -= le16_to_cpu(rp->num_keys); + num_keys = le16_to_cpu(rp->num_keys); + + if (num_keys <= hdev->stored_num_keys) + hdev->stored_num_keys -= num_keys; else hdev->stored_num_keys = 0; diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index ff09c353e64e..ced8ad4fed4f 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -44,6 +44,9 @@ static void iso_sock_kill(struct sock *sk); /* ----- ISO socket info ----- */ #define iso_pi(sk) ((struct iso_pinfo *)sk) +#define EIR_SERVICE_DATA_LENGTH 4 +#define BASE_MAX_LENGTH (HCI_MAX_PER_AD_LENGTH - EIR_SERVICE_DATA_LENGTH) + struct iso_pinfo { struct bt_sock bt; bdaddr_t src; @@ -57,7 +60,7 @@ struct iso_pinfo { __u32 flags; struct bt_iso_qos qos; __u8 base_len; - __u8 base[HCI_MAX_PER_AD_LENGTH]; + __u8 base[BASE_MAX_LENGTH]; struct iso_conn *conn; }; @@ -370,15 +373,24 @@ done: return err; } +static struct bt_iso_qos *iso_sock_get_qos(struct sock *sk) +{ + if (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONNECT2) + return &iso_pi(sk)->conn->hcon->iso_qos; + + return &iso_pi(sk)->qos; +} + static int iso_send_frame(struct sock *sk, struct sk_buff *skb) { struct iso_conn *conn = iso_pi(sk)->conn; + struct bt_iso_qos *qos = iso_sock_get_qos(sk); struct hci_iso_data_hdr *hdr; int len = 0; BT_DBG("sk %p len %d", sk, skb->len); - if (skb->len > iso_pi(sk)->qos.out.sdu) + if (skb->len > qos->out.sdu) return -EMSGSIZE; len = skb->len; @@ -1177,8 +1189,10 @@ static int iso_sock_setsockopt(struct socket *sock, int level, int optname, } len = min_t(unsigned int, sizeof(qos), optlen); - if (len != sizeof(qos)) - return -EINVAL; + if (len != sizeof(qos)) { + err = -EINVAL; + break; + } memset(&qos, 0, sizeof(qos)); @@ -1233,7 +1247,7 @@ static int iso_sock_getsockopt(struct socket *sock, int level, int optname, { struct sock *sk = sock->sk; int len, err = 0; - struct bt_iso_qos qos; + struct bt_iso_qos *qos; u8 base_len; u8 *base; @@ -1246,7 +1260,7 @@ static int iso_sock_getsockopt(struct socket *sock, int level, int optname, switch (optname) { case BT_DEFER_SETUP: - if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { + if (sk->sk_state == BT_CONNECTED) { err = -EINVAL; break; } @@ -1258,13 +1272,10 @@ static int iso_sock_getsockopt(struct socket *sock, int level, int optname, break; case BT_ISO_QOS: - if (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONNECT2) - qos = iso_pi(sk)->conn->hcon->iso_qos; - else - qos = iso_pi(sk)->qos; + qos = iso_sock_get_qos(sk); - len = min_t(unsigned int, len, sizeof(qos)); - if (copy_to_user(optval, (char *)&qos, len)) + len = min_t(unsigned int, len, sizeof(*qos)); + if (copy_to_user(optval, qos, len)) err = -EFAULT; break; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 77c0aac14539..cbe0cae73434 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1970,11 +1970,11 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm, bdaddr_t *dst, u8 link_type) { - struct l2cap_chan *c, *c1 = NULL; + struct l2cap_chan *c, *tmp, *c1 = NULL; read_lock(&chan_list_lock); - list_for_each_entry(c, &chan_list, global_l) { + list_for_each_entry_safe(c, tmp, &chan_list, global_l) { if (state && c->state != state) continue; @@ -1993,11 +1993,10 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm, dst_match = !bacmp(&c->dst, dst); if (src_match && dst_match) { c = l2cap_chan_hold_unless_zero(c); - if (!c) - continue; - - read_unlock(&chan_list_lock); - return c; + if (c) { + read_unlock(&chan_list_lock); + return c; + } } /* Closest match */ diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 646d10401b80..6e31023b84f5 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3819,7 +3819,7 @@ static int set_blocked_keys(struct sock *sk, struct hci_dev *hdev, void *data, hci_blocked_keys_clear(hdev); - for (i = 0; i < keys->key_count; ++i) { + for (i = 0; i < key_count; ++i) { struct blocked_key *b = kzalloc(sizeof(*b), GFP_KERNEL); if (!b) { @@ -4624,8 +4624,7 @@ static int set_device_flags(struct sock *sk, struct hci_dev *hdev, void *data, u32 current_flags = __le32_to_cpu(cp->current_flags); bt_dev_dbg(hdev, "Set device flags %pMR (type 0x%x) = 0x%x", - &cp->addr.bdaddr, cp->addr.type, - __le32_to_cpu(current_flags)); + &cp->addr.bdaddr, cp->addr.type, current_flags); // We should take hci_dev_lock() early, I think.. conn_flags can change supported_flags = hdev->conn_flags; @@ -8936,6 +8935,8 @@ void mgmt_index_removed(struct hci_dev *hdev) HCI_MGMT_EXT_INDEX_EVENTS); /* Cancel any remaining timed work */ + if (!hci_dev_test_flag(hdev, HCI_MGMT)) + return; cancel_delayed_work_sync(&hdev->discov_off); cancel_delayed_work_sync(&hdev->service_cache); cancel_delayed_work_sync(&hdev->rpa_expired); diff --git a/net/bluetooth/msft.c b/net/bluetooth/msft.c index 14975769f678..bee6a4c656be 100644 --- a/net/bluetooth/msft.c +++ b/net/bluetooth/msft.c @@ -120,7 +120,10 @@ static bool read_supported_features(struct hci_dev *hdev, skb = __hci_cmd_sync(hdev, hdev->msft_opcode, sizeof(cp), &cp, HCI_CMD_TIMEOUT); - if (IS_ERR(skb)) { + if (IS_ERR_OR_NULL(skb)) { + if (!skb) + skb = ERR_PTR(-EIO); + bt_dev_err(hdev, "Failed to read MSFT supported features (%ld)", PTR_ERR(skb)); return false; @@ -319,8 +322,11 @@ static int msft_remove_monitor_sync(struct hci_dev *hdev, skb = __hci_cmd_sync(hdev, hdev->msft_opcode, sizeof(cp), &cp, HCI_CMD_TIMEOUT); - if (IS_ERR(skb)) + if (IS_ERR_OR_NULL(skb)) { + if (!skb) + return -EIO; return PTR_ERR(skb); + } return msft_le_cancel_monitor_advertisement_cb(hdev, hdev->msft_opcode, monitor, skb); @@ -432,8 +438,11 @@ static int msft_add_monitor_sync(struct hci_dev *hdev, HCI_CMD_TIMEOUT); kfree(cp); - if (IS_ERR(skb)) + if (IS_ERR_OR_NULL(skb)) { + if (!skb) + return -EIO; return PTR_ERR(skb); + } return msft_le_monitor_advertisement_cb(hdev, hdev->msft_opcode, monitor, skb); diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c index cbc9cd5058cb..d11209367dd0 100644 --- a/net/bpf/test_run.c +++ b/net/bpf/test_run.c @@ -1628,6 +1628,7 @@ static int __init bpf_prog_test_run_init(void) int ret; ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, &bpf_prog_test_kfunc_set); + ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &bpf_prog_test_kfunc_set); return ret ?: register_btf_id_dtor_kfuncs(bpf_prog_test_dtor_kfunc, ARRAY_SIZE(bpf_prog_test_dtor_kfunc), THIS_MODULE); diff --git a/net/can/j1939/socket.c b/net/can/j1939/socket.c index f5ecfdcf57b2..b670ba03a675 100644 --- a/net/can/j1939/socket.c +++ b/net/can/j1939/socket.c @@ -178,7 +178,10 @@ activate_next: if (!first) return; - if (WARN_ON_ONCE(j1939_session_activate(first))) { + if (j1939_session_activate(first)) { + netdev_warn_once(first->priv->ndev, + "%s: 0x%p: Identical session is already activated.\n", + __func__, first); first->err = -EBUSY; goto activate_next; } else { diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c index 307ee1174a6e..d7d86c944d76 100644 --- a/net/can/j1939/transport.c +++ b/net/can/j1939/transport.c @@ -260,6 +260,8 @@ static void __j1939_session_drop(struct j1939_session *session) static void j1939_session_destroy(struct j1939_session *session) { + struct sk_buff *skb; + if (session->transmission) { if (session->err) j1939_sk_errqueue(session, J1939_ERRQUEUE_TX_ABORT); @@ -274,7 +276,11 @@ static void j1939_session_destroy(struct j1939_session *session) WARN_ON_ONCE(!list_empty(&session->sk_session_queue_entry)); WARN_ON_ONCE(!list_empty(&session->active_session_list_entry)); - skb_queue_purge(&session->skb_queue); + while ((skb = skb_dequeue(&session->skb_queue)) != NULL) { + /* drop ref taken in j1939_session_skb_queue() */ + skb_unref(skb); + kfree_skb(skb); + } __j1939_session_drop(session); j1939_priv_put(session->priv); kfree(session); diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 9d82bb42e958..87b883c7bfd6 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -4578,15 +4578,12 @@ bad: /* * Register request, send initial attempt. */ -int ceph_osdc_start_request(struct ceph_osd_client *osdc, - struct ceph_osd_request *req, - bool nofail) +void ceph_osdc_start_request(struct ceph_osd_client *osdc, + struct ceph_osd_request *req) { down_read(&osdc->lock); submit_request(req, false); up_read(&osdc->lock); - - return 0; } EXPORT_SYMBOL(ceph_osdc_start_request); @@ -4756,7 +4753,7 @@ int ceph_osdc_unwatch(struct ceph_osd_client *osdc, if (ret) goto out_put_req; - ceph_osdc_start_request(osdc, req, false); + ceph_osdc_start_request(osdc, req); linger_cancel(lreq); linger_put(lreq); ret = wait_request_timeout(req, opts->mount_timeout); @@ -4827,7 +4824,7 @@ int ceph_osdc_notify_ack(struct ceph_osd_client *osdc, if (ret) goto out_put_req; - ceph_osdc_start_request(osdc, req, false); + ceph_osdc_start_request(osdc, req); ret = ceph_osdc_wait_request(osdc, req); out_put_req: @@ -5043,7 +5040,7 @@ int ceph_osdc_list_watchers(struct ceph_osd_client *osdc, if (ret) goto out_put_req; - ceph_osdc_start_request(osdc, req, false); + ceph_osdc_start_request(osdc, req); ret = ceph_osdc_wait_request(osdc, req); if (ret >= 0) { void *p = page_address(pages[0]); @@ -5120,7 +5117,7 @@ int ceph_osdc_call(struct ceph_osd_client *osdc, if (ret) goto out_put_req; - ceph_osdc_start_request(osdc, req, false); + ceph_osdc_start_request(osdc, req); ret = ceph_osdc_wait_request(osdc, req); if (ret >= 0) { ret = req->r_ops[0].rval; diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c index 2823bb3cff55..295098873861 100644 --- a/net/ceph/osdmap.c +++ b/net/ceph/osdmap.c @@ -11,6 +11,22 @@ #include <linux/crush/hash.h> #include <linux/crush/mapper.h> +static __printf(2, 3) +void osdmap_info(const struct ceph_osdmap *map, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + + printk(KERN_INFO "%s (%pU e%u): %pV", KBUILD_MODNAME, &map->fsid, + map->epoch, &vaf); + + va_end(args); +} + char *ceph_osdmap_state_str(char *str, int len, u32 state) { if (!len) @@ -571,10 +587,10 @@ static struct crush_map *crush_decode(void *pbyval, void *end) goto bad; #endif r = kmalloc(struct_size(r, steps, yes), GFP_NOFS); - c->rules[i] = r; if (r == NULL) goto badmem; dout(" rule %d is at %p\n", i, r); + c->rules[i] = r; r->len = yes; ceph_decode_copy_safe(p, end, &r->mask, 4, bad); /* 4 u8's */ ceph_decode_need(p, end, r->len*3*sizeof(u32), bad); @@ -1566,7 +1582,7 @@ static int decode_new_primary_affinity(void **p, void *end, if (ret) return ret; - pr_info("osd%d primary-affinity 0x%x\n", osd, aff); + osdmap_info(map, "osd%d primary-affinity 0x%x\n", osd, aff); } return 0; @@ -1864,9 +1880,9 @@ static int decode_new_up_state_weight(void **p, void *end, u8 struct_v, osd = ceph_decode_32(p); w = ceph_decode_32(p); BUG_ON(osd >= map->max_osd); - pr_info("osd%d weight 0x%x %s\n", osd, w, - w == CEPH_OSD_IN ? "(in)" : - (w == CEPH_OSD_OUT ? "(out)" : "")); + osdmap_info(map, "osd%d weight 0x%x %s\n", osd, w, + w == CEPH_OSD_IN ? "(in)" : + (w == CEPH_OSD_OUT ? "(out)" : "")); map->osd_weight[osd] = w; /* @@ -1898,10 +1914,10 @@ static int decode_new_up_state_weight(void **p, void *end, u8 struct_v, BUG_ON(osd >= map->max_osd); if ((map->osd_state[osd] & CEPH_OSD_UP) && (xorstate & CEPH_OSD_UP)) - pr_info("osd%d down\n", osd); + osdmap_info(map, "osd%d down\n", osd); if ((map->osd_state[osd] & CEPH_OSD_EXISTS) && (xorstate & CEPH_OSD_EXISTS)) { - pr_info("osd%d does not exist\n", osd); + osdmap_info(map, "osd%d does not exist\n", osd); ret = set_primary_affinity(map, osd, CEPH_OSD_DEFAULT_PRIMARY_AFFINITY); if (ret) @@ -1931,7 +1947,7 @@ static int decode_new_up_state_weight(void **p, void *end, u8 struct_v, dout("%s osd%d addr %s\n", __func__, osd, ceph_pr_addr(&addr)); - pr_info("osd%d up\n", osd); + osdmap_info(map, "osd%d up\n", osd); map->osd_state[osd] |= CEPH_OSD_EXISTS | CEPH_OSD_UP; map->osd_addr[osd] = addr; } diff --git a/net/ceph/pagelist.c b/net/ceph/pagelist.c index 65e34f78b05d..74622b278d57 100644 --- a/net/ceph/pagelist.c +++ b/net/ceph/pagelist.c @@ -96,7 +96,7 @@ int ceph_pagelist_append(struct ceph_pagelist *pl, const void *buf, size_t len) EXPORT_SYMBOL(ceph_pagelist_append); /* Allocate enough pages for a pagelist to append the given amount - * of data without without allocating. + * of data without allocating. * Returns: 0 on success, -ENOMEM on error. */ int ceph_pagelist_reserve(struct ceph_pagelist *pl, size_t space) diff --git a/net/core/bpf_sk_storage.c b/net/core/bpf_sk_storage.c index a25ec93729b9..1b7f385643b4 100644 --- a/net/core/bpf_sk_storage.c +++ b/net/core/bpf_sk_storage.c @@ -875,10 +875,18 @@ static int bpf_iter_init_sk_storage_map(void *priv_data, { struct bpf_iter_seq_sk_storage_map_info *seq_info = priv_data; + bpf_map_inc_with_uref(aux->map); seq_info->map = aux->map; return 0; } +static void bpf_iter_fini_sk_storage_map(void *priv_data) +{ + struct bpf_iter_seq_sk_storage_map_info *seq_info = priv_data; + + bpf_map_put_with_uref(seq_info->map); +} + static int bpf_iter_attach_map(struct bpf_prog *prog, union bpf_iter_link_info *linfo, struct bpf_iter_aux_info *aux) @@ -896,7 +904,7 @@ static int bpf_iter_attach_map(struct bpf_prog *prog, if (map->map_type != BPF_MAP_TYPE_SK_STORAGE) goto put_map; - if (prog->aux->max_rdonly_access > map->value_size) { + if (prog->aux->max_rdwr_access > map->value_size) { err = -EACCES; goto put_map; } @@ -924,7 +932,7 @@ static const struct seq_operations bpf_sk_storage_map_seq_ops = { static const struct bpf_iter_seq_info iter_seq_info = { .seq_ops = &bpf_sk_storage_map_seq_ops, .init_seq_private = bpf_iter_init_sk_storage_map, - .fini_seq_private = NULL, + .fini_seq_private = bpf_iter_fini_sk_storage_map, .seq_priv_size = sizeof(struct bpf_iter_seq_sk_storage_map_info), }; diff --git a/net/core/devlink.c b/net/core/devlink.c index 5da5c7cca98a..b50bcc18b8d9 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -5147,7 +5147,7 @@ static int devlink_param_get(struct devlink *devlink, const struct devlink_param *param, struct devlink_param_gset_ctx *ctx) { - if (!param->get) + if (!param->get || devlink->reload_failed) return -EOPNOTSUPP; return param->get(devlink, param->id, ctx); } @@ -5156,7 +5156,7 @@ static int devlink_param_set(struct devlink *devlink, const struct devlink_param *param, struct devlink_param_gset_ctx *ctx) { - if (!param->set) + if (!param->set || devlink->reload_failed) return -EOPNOTSUPP; return param->set(devlink, param->id, ctx); } diff --git a/net/core/filter.c b/net/core/filter.c index 5669248aff25..e8508aaafd27 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -5063,7 +5063,10 @@ static int __bpf_setsockopt(struct sock *sk, int level, int optname, case SO_RCVLOWAT: if (val < 0) val = INT_MAX; - WRITE_ONCE(sk->sk_rcvlowat, val ? : 1); + if (sk->sk_socket && sk->sk_socket->ops->set_rcvlowat) + ret = sk->sk_socket->ops->set_rcvlowat(sk, val); + else + WRITE_ONCE(sk->sk_rcvlowat, val ? : 1); break; case SO_MARK: if (sk->sk_mark != val) { diff --git a/net/core/skmsg.c b/net/core/skmsg.c index cf3c24c8610d..f47338d89d5d 100644 --- a/net/core/skmsg.c +++ b/net/core/skmsg.c @@ -738,7 +738,9 @@ struct sk_psock *sk_psock_init(struct sock *sk, int node) sk_psock_set_state(psock, SK_PSOCK_TX_ENABLED); refcount_set(&psock->refcnt, 1); - rcu_assign_sk_user_data_nocopy(sk, psock); + __rcu_assign_sk_user_data_with_flags(sk, psock, + SK_USER_DATA_NOCOPY | + SK_USER_DATA_PSOCK); sock_hold(sk); out: diff --git a/net/core/sock_map.c b/net/core/sock_map.c index 028813dfecb0..9a9fb9487d63 100644 --- a/net/core/sock_map.c +++ b/net/core/sock_map.c @@ -783,13 +783,22 @@ static int sock_map_init_seq_private(void *priv_data, { struct sock_map_seq_info *info = priv_data; + bpf_map_inc_with_uref(aux->map); info->map = aux->map; return 0; } +static void sock_map_fini_seq_private(void *priv_data) +{ + struct sock_map_seq_info *info = priv_data; + + bpf_map_put_with_uref(info->map); +} + static const struct bpf_iter_seq_info sock_map_iter_seq_info = { .seq_ops = &sock_map_seq_ops, .init_seq_private = sock_map_init_seq_private, + .fini_seq_private = sock_map_fini_seq_private, .seq_priv_size = sizeof(struct sock_map_seq_info), }; @@ -1369,18 +1378,27 @@ static const struct seq_operations sock_hash_seq_ops = { }; static int sock_hash_init_seq_private(void *priv_data, - struct bpf_iter_aux_info *aux) + struct bpf_iter_aux_info *aux) { struct sock_hash_seq_info *info = priv_data; + bpf_map_inc_with_uref(aux->map); info->map = aux->map; info->htab = container_of(aux->map, struct bpf_shtab, map); return 0; } +static void sock_hash_fini_seq_private(void *priv_data) +{ + struct sock_hash_seq_info *info = priv_data; + + bpf_map_put_with_uref(info->map); +} + static const struct bpf_iter_seq_info sock_hash_iter_seq_info = { .seq_ops = &sock_hash_seq_ops, .init_seq_private = sock_hash_init_seq_private, + .fini_seq_private = sock_hash_fini_seq_private, .seq_priv_size = sizeof(struct sock_hash_seq_info), }; diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 897ca4f9b791..f152e51242cb 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1311,8 +1311,7 @@ struct dst_entry *ip6_dst_lookup_tunnel(struct sk_buff *skb, fl6.daddr = info->key.u.ipv6.dst; fl6.saddr = info->key.u.ipv6.src; prio = info->key.tos; - fl6.flowlabel = ip6_make_flowinfo(RT_TOS(prio), - info->key.label); + fl6.flowlabel = ip6_make_flowinfo(prio, info->key.label); dst = ipv6_stub->ipv6_dst_lookup_flow(net, sock->sk, &fl6, NULL); diff --git a/net/ipv6/seg6_local.c b/net/ipv6/seg6_local.c index 2cd4a8d3b30a..b7de5e46fdd8 100644 --- a/net/ipv6/seg6_local.c +++ b/net/ipv6/seg6_local.c @@ -1614,7 +1614,7 @@ static void __destroy_attrs(unsigned long parsed_attrs, int max_parsed, * callback. If the callback is not available, then we skip to the next * attribute; otherwise, we call the destroy() callback. */ - for (i = 0; i < max_parsed; ++i) { + for (i = SEG6_LOCAL_SRH; i < max_parsed; ++i) { if (!(parsed_attrs & SEG6_F_ATTR(i))) continue; @@ -1643,7 +1643,7 @@ static int parse_nla_optional_attrs(struct nlattr **attrs, struct seg6_action_param *param; int err, i; - for (i = 0; i < SEG6_LOCAL_MAX + 1; ++i) { + for (i = SEG6_LOCAL_SRH; i < SEG6_LOCAL_MAX + 1; ++i) { if (!(desc->optattrs & SEG6_F_ATTR(i)) || !attrs[i]) continue; @@ -1742,7 +1742,7 @@ static int parse_nla_action(struct nlattr **attrs, struct seg6_local_lwt *slwt) } /* parse the required attributes */ - for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { + for (i = SEG6_LOCAL_SRH; i < SEG6_LOCAL_MAX + 1; i++) { if (desc->attrs & SEG6_F_ATTR(i)) { if (!attrs[i]) return -EINVAL; @@ -1847,7 +1847,7 @@ static int seg6_local_fill_encap(struct sk_buff *skb, attrs = slwt->desc->attrs | slwt->parsed_optattrs; - for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { + for (i = SEG6_LOCAL_SRH; i < SEG6_LOCAL_MAX + 1; i++) { if (attrs & SEG6_F_ATTR(i)) { param = &seg6_action_params[i]; err = param->put(skb, slwt); @@ -1927,7 +1927,7 @@ static int seg6_local_cmp_encap(struct lwtunnel_state *a, if (attrs_a != attrs_b) return 1; - for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { + for (i = SEG6_LOCAL_SRH; i < SEG6_LOCAL_MAX + 1; i++) { if (attrs_a & SEG6_F_ATTR(i)) { param = &seg6_action_params[i]; if (param->cmp(slwt_a, slwt_b)) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index a3f1c1461874..da4257504fad 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -1240,6 +1240,9 @@ static int mptcp_sendmsg_frag(struct sock *sk, struct sock *ssk, info->limit > dfrag->data_len)) return 0; + if (unlikely(!__tcp_can_send(ssk))) + return -EAGAIN; + /* compute send limit */ info->mss_now = tcp_send_mss(ssk, &info->size_goal, info->flags); copy = info->size_goal; @@ -1413,7 +1416,8 @@ static struct sock *mptcp_subflow_get_send(struct mptcp_sock *msk) if (__mptcp_check_fallback(msk)) { if (!msk->first) return NULL; - return sk_stream_memory_free(msk->first) ? msk->first : NULL; + return __tcp_can_send(msk->first) && + sk_stream_memory_free(msk->first) ? msk->first : NULL; } /* re-use last subflow, if the burst allow that */ @@ -1564,6 +1568,8 @@ void __mptcp_push_pending(struct sock *sk, unsigned int flags) ret = mptcp_sendmsg_frag(sk, ssk, dfrag, &info); if (ret <= 0) { + if (ret == -EAGAIN) + continue; mptcp_push_release(ssk, &info); goto out; } @@ -2769,30 +2775,16 @@ static void __mptcp_wr_shutdown(struct sock *sk) static void __mptcp_destroy_sock(struct sock *sk) { - struct mptcp_subflow_context *subflow, *tmp; struct mptcp_sock *msk = mptcp_sk(sk); - LIST_HEAD(conn_list); pr_debug("msk=%p", msk); might_sleep(); - /* join list will be eventually flushed (with rst) at sock lock release time*/ - list_splice_init(&msk->conn_list, &conn_list); - mptcp_stop_timer(sk); sk_stop_timer(sk, &sk->sk_timer); msk->pm.status = 0; - /* clears msk->subflow, allowing the following loop to close - * even the initial subflow - */ - mptcp_dispose_initial_subflow(msk); - list_for_each_entry_safe(subflow, tmp, &conn_list, node) { - struct sock *ssk = mptcp_subflow_tcp_sock(subflow); - __mptcp_close_ssk(sk, ssk, subflow, 0); - } - sk->sk_prot->destroy(sk); WARN_ON_ONCE(msk->rmem_fwd_alloc); @@ -2884,24 +2876,20 @@ static void mptcp_copy_inaddrs(struct sock *msk, const struct sock *ssk) static int mptcp_disconnect(struct sock *sk, int flags) { - struct mptcp_subflow_context *subflow, *tmp; struct mptcp_sock *msk = mptcp_sk(sk); inet_sk_state_store(sk, TCP_CLOSE); - list_for_each_entry_safe(subflow, tmp, &msk->conn_list, node) { - struct sock *ssk = mptcp_subflow_tcp_sock(subflow); - - __mptcp_close_ssk(sk, ssk, subflow, MPTCP_CF_FASTCLOSE); - } - mptcp_stop_timer(sk); sk_stop_timer(sk, &sk->sk_timer); if (mptcp_sk(sk)->token) mptcp_event(MPTCP_EVENT_CLOSED, mptcp_sk(sk), NULL, GFP_KERNEL); - mptcp_destroy_common(msk); + /* msk->subflow is still intact, the following will not free the first + * subflow + */ + mptcp_destroy_common(msk, MPTCP_CF_FASTCLOSE); msk->last_snd = NULL; WRITE_ONCE(msk->flags, 0); msk->cb_flags = 0; @@ -3051,12 +3039,17 @@ out: return newsk; } -void mptcp_destroy_common(struct mptcp_sock *msk) +void mptcp_destroy_common(struct mptcp_sock *msk, unsigned int flags) { + struct mptcp_subflow_context *subflow, *tmp; struct sock *sk = (struct sock *)msk; __mptcp_clear_xmit(sk); + /* join list will be eventually flushed (with rst) at sock lock release time */ + list_for_each_entry_safe(subflow, tmp, &msk->conn_list, node) + __mptcp_close_ssk(sk, mptcp_subflow_tcp_sock(subflow), subflow, flags); + /* move to sk_receive_queue, sk_stream_kill_queues will purge it */ mptcp_data_lock(sk); skb_queue_splice_tail_init(&msk->receive_queue, &sk->sk_receive_queue); @@ -3078,7 +3071,11 @@ static void mptcp_destroy(struct sock *sk) { struct mptcp_sock *msk = mptcp_sk(sk); - mptcp_destroy_common(msk); + /* clears msk->subflow, allowing the following to close + * even the initial subflow + */ + mptcp_dispose_initial_subflow(msk); + mptcp_destroy_common(msk, 0); sk_sockets_allocated_dec(sk); } diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index 5d6043c16b09..132d50833df1 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -624,16 +624,19 @@ void mptcp_info2sockaddr(const struct mptcp_addr_info *info, struct sockaddr_storage *addr, unsigned short family); -static inline bool __mptcp_subflow_active(struct mptcp_subflow_context *subflow) +static inline bool __tcp_can_send(const struct sock *ssk) { - struct sock *ssk = mptcp_subflow_tcp_sock(subflow); + /* only send if our side has not closed yet */ + return ((1 << inet_sk_state_load(ssk)) & (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)); +} +static inline bool __mptcp_subflow_active(struct mptcp_subflow_context *subflow) +{ /* can't send if JOIN hasn't completed yet (i.e. is usable for mptcp) */ if (subflow->request_join && !subflow->fully_established) return false; - /* only send if our side has not closed yet */ - return ((1 << ssk->sk_state) & (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)); + return __tcp_can_send(mptcp_subflow_tcp_sock(subflow)); } void mptcp_subflow_set_active(struct mptcp_subflow_context *subflow); @@ -717,7 +720,7 @@ static inline void mptcp_write_space(struct sock *sk) } } -void mptcp_destroy_common(struct mptcp_sock *msk); +void mptcp_destroy_common(struct mptcp_sock *msk, unsigned int flags); #define MPTCP_TOKEN_MAX_RETRIES 4 diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index 901c763dcdbb..c7d49fb6e7bd 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -621,7 +621,8 @@ static void mptcp_sock_destruct(struct sock *sk) sock_orphan(sk); } - mptcp_destroy_common(mptcp_sk(sk)); + /* We don't need to clear msk->subflow, as it's still NULL at this point */ + mptcp_destroy_common(mptcp_sk(sk), 0); inet_sock_destruct(sk); } diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index df6abbfe0079..22f15ebf6045 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -736,9 +736,8 @@ config NF_FLOW_TABLE config NF_FLOW_TABLE_PROCFS bool "Supply flow table statistics in procfs" - default y + depends on NF_FLOW_TABLE depends on PROC_FS - depends on SYSCTL help This option enables for the flow table offload statistics to be shown in procfs under net/netfilter/nf_flowtable. diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 9f976b11d896..3cc88998b879 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -153,6 +153,7 @@ static struct nft_trans *nft_trans_alloc_gfp(const struct nft_ctx *ctx, if (trans == NULL) return NULL; + INIT_LIST_HEAD(&trans->list); trans->msg_type = msg_type; trans->ctx = *ctx; @@ -2472,6 +2473,7 @@ err: } static struct nft_chain *nft_chain_lookup_byid(const struct net *net, + const struct nft_table *table, const struct nlattr *nla) { struct nftables_pernet *nft_net = nft_pernet(net); @@ -2482,6 +2484,7 @@ static struct nft_chain *nft_chain_lookup_byid(const struct net *net, struct nft_chain *chain = trans->ctx.chain; if (trans->msg_type == NFT_MSG_NEWCHAIN && + chain->table == table && id == nft_trans_chain_id(trans)) return chain; } @@ -3371,6 +3374,7 @@ static int nft_table_validate(struct net *net, const struct nft_table *table) } static struct nft_rule *nft_rule_lookup_byid(const struct net *net, + const struct nft_chain *chain, const struct nlattr *nla); #define NFT_RULE_MAXEXPRS 128 @@ -3417,7 +3421,7 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info, return -EOPNOTSUPP; } else if (nla[NFTA_RULE_CHAIN_ID]) { - chain = nft_chain_lookup_byid(net, nla[NFTA_RULE_CHAIN_ID]); + chain = nft_chain_lookup_byid(net, table, nla[NFTA_RULE_CHAIN_ID]); if (IS_ERR(chain)) { NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN_ID]); return PTR_ERR(chain); @@ -3459,7 +3463,7 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info, return PTR_ERR(old_rule); } } else if (nla[NFTA_RULE_POSITION_ID]) { - old_rule = nft_rule_lookup_byid(net, nla[NFTA_RULE_POSITION_ID]); + old_rule = nft_rule_lookup_byid(net, chain, nla[NFTA_RULE_POSITION_ID]); if (IS_ERR(old_rule)) { NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_POSITION_ID]); return PTR_ERR(old_rule); @@ -3604,6 +3608,7 @@ err_release_expr: } static struct nft_rule *nft_rule_lookup_byid(const struct net *net, + const struct nft_chain *chain, const struct nlattr *nla) { struct nftables_pernet *nft_net = nft_pernet(net); @@ -3614,6 +3619,7 @@ static struct nft_rule *nft_rule_lookup_byid(const struct net *net, struct nft_rule *rule = nft_trans_rule(trans); if (trans->msg_type == NFT_MSG_NEWRULE && + trans->ctx.chain == chain && id == nft_trans_rule_id(trans)) return rule; } @@ -3663,7 +3669,7 @@ static int nf_tables_delrule(struct sk_buff *skb, const struct nfnl_info *info, err = nft_delrule(&ctx, rule); } else if (nla[NFTA_RULE_ID]) { - rule = nft_rule_lookup_byid(net, nla[NFTA_RULE_ID]); + rule = nft_rule_lookup_byid(net, chain, nla[NFTA_RULE_ID]); if (IS_ERR(rule)) { NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_ID]); return PTR_ERR(rule); @@ -3842,6 +3848,7 @@ static struct nft_set *nft_set_lookup_byhandle(const struct nft_table *table, } static struct nft_set *nft_set_lookup_byid(const struct net *net, + const struct nft_table *table, const struct nlattr *nla, u8 genmask) { struct nftables_pernet *nft_net = nft_pernet(net); @@ -3853,6 +3860,7 @@ static struct nft_set *nft_set_lookup_byid(const struct net *net, struct nft_set *set = nft_trans_set(trans); if (id == nft_trans_set_id(trans) && + set->table == table && nft_active_genmask(set, genmask)) return set; } @@ -3873,7 +3881,7 @@ struct nft_set *nft_set_lookup_global(const struct net *net, if (!nla_set_id) return set; - set = nft_set_lookup_byid(net, nla_set_id, genmask); + set = nft_set_lookup_byid(net, table, nla_set_id, genmask); } return set; } @@ -5195,19 +5203,13 @@ static int nft_setelem_parse_flags(const struct nft_set *set, static int nft_setelem_parse_key(struct nft_ctx *ctx, struct nft_set *set, struct nft_data *key, struct nlattr *attr) { - struct nft_data_desc desc; - int err; - - err = nft_data_init(ctx, key, NFT_DATA_VALUE_MAXLEN, &desc, attr); - if (err < 0) - return err; - - if (desc.type != NFT_DATA_VALUE || desc.len != set->klen) { - nft_data_release(key, desc.type); - return -EINVAL; - } + struct nft_data_desc desc = { + .type = NFT_DATA_VALUE, + .size = NFT_DATA_VALUE_MAXLEN, + .len = set->klen, + }; - return 0; + return nft_data_init(ctx, key, &desc, attr); } static int nft_setelem_parse_data(struct nft_ctx *ctx, struct nft_set *set, @@ -5216,24 +5218,18 @@ static int nft_setelem_parse_data(struct nft_ctx *ctx, struct nft_set *set, struct nlattr *attr) { u32 dtype; - int err; - - err = nft_data_init(ctx, data, NFT_DATA_VALUE_MAXLEN, desc, attr); - if (err < 0) - return err; if (set->dtype == NFT_DATA_VERDICT) dtype = NFT_DATA_VERDICT; else dtype = NFT_DATA_VALUE; - if (dtype != desc->type || - set->dlen != desc->len) { - nft_data_release(data, desc->type); - return -EINVAL; - } + desc->type = dtype; + desc->size = NFT_DATA_VALUE_MAXLEN; + desc->len = set->dlen; + desc->flags = NFT_DATA_DESC_SETELEM; - return 0; + return nft_data_init(ctx, data, desc, attr); } static void *nft_setelem_catchall_get(const struct net *net, @@ -5467,6 +5463,27 @@ err_set_elem_expr: return ERR_PTR(err); } +static int nft_set_ext_check(const struct nft_set_ext_tmpl *tmpl, u8 id, u32 len) +{ + len += nft_set_ext_types[id].len; + if (len > tmpl->ext_len[id] || + len > U8_MAX) + return -1; + + return 0; +} + +static int nft_set_ext_memcpy(const struct nft_set_ext_tmpl *tmpl, u8 id, + void *to, const void *from, u32 len) +{ + if (nft_set_ext_check(tmpl, id, len) < 0) + return -1; + + memcpy(to, from, len); + + return 0; +} + void *nft_set_elem_init(const struct nft_set *set, const struct nft_set_ext_tmpl *tmpl, const u32 *key, const u32 *key_end, @@ -5477,17 +5494,26 @@ void *nft_set_elem_init(const struct nft_set *set, elem = kzalloc(set->ops->elemsize + tmpl->len, gfp); if (elem == NULL) - return NULL; + return ERR_PTR(-ENOMEM); ext = nft_set_elem_ext(set, elem); nft_set_ext_init(ext, tmpl); - if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY)) - memcpy(nft_set_ext_key(ext), key, set->klen); - if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END)) - memcpy(nft_set_ext_key_end(ext), key_end, set->klen); - if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA)) - memcpy(nft_set_ext_data(ext), data, set->dlen); + if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY) && + nft_set_ext_memcpy(tmpl, NFT_SET_EXT_KEY, + nft_set_ext_key(ext), key, set->klen) < 0) + goto err_ext_check; + + if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END) && + nft_set_ext_memcpy(tmpl, NFT_SET_EXT_KEY_END, + nft_set_ext_key_end(ext), key_end, set->klen) < 0) + goto err_ext_check; + + if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA) && + nft_set_ext_memcpy(tmpl, NFT_SET_EXT_DATA, + nft_set_ext_data(ext), data, set->dlen) < 0) + goto err_ext_check; + if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) { *nft_set_ext_expiration(ext) = get_jiffies_64() + expiration; if (expiration == 0) @@ -5497,6 +5523,11 @@ void *nft_set_elem_init(const struct nft_set *set, *nft_set_ext_timeout(ext) = timeout; return elem; + +err_ext_check: + kfree(elem); + + return ERR_PTR(-EINVAL); } static void __nft_set_elem_expr_destroy(const struct nft_ctx *ctx, @@ -5584,14 +5615,25 @@ err_expr: } static int nft_set_elem_expr_setup(struct nft_ctx *ctx, + const struct nft_set_ext_tmpl *tmpl, const struct nft_set_ext *ext, struct nft_expr *expr_array[], u32 num_exprs) { struct nft_set_elem_expr *elem_expr = nft_set_ext_expr(ext); + u32 len = sizeof(struct nft_set_elem_expr); struct nft_expr *expr; int i, err; + if (num_exprs == 0) + return 0; + + for (i = 0; i < num_exprs; i++) + len += expr_array[i]->ops->size; + + if (nft_set_ext_check(tmpl, NFT_SET_EXT_EXPRESSIONS, len) < 0) + return -EINVAL; + for (i = 0; i < num_exprs; i++) { expr = nft_setelem_expr_at(elem_expr, elem_expr->size); err = nft_expr_clone(expr, expr_array[i]); @@ -6054,17 +6096,23 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, } } - err = -ENOMEM; elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data, elem.key_end.val.data, elem.data.val.data, timeout, expiration, GFP_KERNEL_ACCOUNT); - if (elem.priv == NULL) + if (IS_ERR(elem.priv)) { + err = PTR_ERR(elem.priv); goto err_parse_data; + } ext = nft_set_elem_ext(set, elem.priv); if (flags) *nft_set_ext_flags(ext) = flags; + if (ulen > 0) { + if (nft_set_ext_check(&tmpl, NFT_SET_EXT_USERDATA, ulen) < 0) { + err = -EINVAL; + goto err_elem_userdata; + } udata = nft_set_ext_userdata(ext); udata->len = ulen - 1; nla_memcpy(&udata->data, nla[NFTA_SET_ELEM_USERDATA], ulen); @@ -6073,14 +6121,14 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, *nft_set_ext_obj(ext) = obj; obj->use++; } - err = nft_set_elem_expr_setup(ctx, ext, expr_array, num_exprs); + err = nft_set_elem_expr_setup(ctx, &tmpl, ext, expr_array, num_exprs); if (err < 0) - goto err_elem_expr; + goto err_elem_free; trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set); if (trans == NULL) { err = -ENOMEM; - goto err_elem_expr; + goto err_elem_free; } ext->genmask = nft_genmask_cur(ctx->net) | NFT_SET_ELEM_BUSY_MASK; @@ -6126,10 +6174,10 @@ err_set_full: nft_setelem_remove(ctx->net, set, &elem); err_element_clash: kfree(trans); -err_elem_expr: +err_elem_free: if (obj) obj->use--; - +err_elem_userdata: nf_tables_set_elem_destroy(ctx, set, elem.priv); err_parse_data: if (nla[NFTA_SET_ELEM_DATA] != NULL) @@ -6311,8 +6359,10 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set, elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data, elem.key_end.val.data, NULL, 0, 0, GFP_KERNEL_ACCOUNT); - if (elem.priv == NULL) + if (IS_ERR(elem.priv)) { + err = PTR_ERR(elem.priv); goto fail_elem_key_end; + } ext = nft_set_elem_ext(set, elem.priv); if (flags) @@ -9605,7 +9655,7 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data, tb[NFTA_VERDICT_CHAIN], genmask); } else if (tb[NFTA_VERDICT_CHAIN_ID]) { - chain = nft_chain_lookup_byid(ctx->net, + chain = nft_chain_lookup_byid(ctx->net, ctx->table, tb[NFTA_VERDICT_CHAIN_ID]); if (IS_ERR(chain)) return PTR_ERR(chain); @@ -9617,6 +9667,9 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data, return PTR_ERR(chain); if (nft_is_base_chain(chain)) return -EOPNOTSUPP; + if (desc->flags & NFT_DATA_DESC_SETELEM && + chain->flags & NFT_CHAIN_BINDING) + return -EINVAL; chain->use++; data->verdict.chain = chain; @@ -9624,7 +9677,7 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data, } desc->len = sizeof(data->verdict); - desc->type = NFT_DATA_VERDICT; + return 0; } @@ -9677,20 +9730,25 @@ nla_put_failure: } static int nft_value_init(const struct nft_ctx *ctx, - struct nft_data *data, unsigned int size, - struct nft_data_desc *desc, const struct nlattr *nla) + struct nft_data *data, struct nft_data_desc *desc, + const struct nlattr *nla) { unsigned int len; len = nla_len(nla); if (len == 0) return -EINVAL; - if (len > size) + if (len > desc->size) return -EOVERFLOW; + if (desc->len) { + if (len != desc->len) + return -EINVAL; + } else { + desc->len = len; + } nla_memcpy(data->data, nla, len); - desc->type = NFT_DATA_VALUE; - desc->len = len; + return 0; } @@ -9710,7 +9768,6 @@ static const struct nla_policy nft_data_policy[NFTA_DATA_MAX + 1] = { * * @ctx: context of the expression using the data * @data: destination struct nft_data - * @size: maximum data length * @desc: data description * @nla: netlink attribute containing data * @@ -9720,24 +9777,35 @@ static const struct nla_policy nft_data_policy[NFTA_DATA_MAX + 1] = { * The caller can indicate that it only wants to accept data of type * NFT_DATA_VALUE by passing NULL for the ctx argument. */ -int nft_data_init(const struct nft_ctx *ctx, - struct nft_data *data, unsigned int size, +int nft_data_init(const struct nft_ctx *ctx, struct nft_data *data, struct nft_data_desc *desc, const struct nlattr *nla) { struct nlattr *tb[NFTA_DATA_MAX + 1]; int err; + if (WARN_ON_ONCE(!desc->size)) + return -EINVAL; + err = nla_parse_nested_deprecated(tb, NFTA_DATA_MAX, nla, nft_data_policy, NULL); if (err < 0) return err; - if (tb[NFTA_DATA_VALUE]) - return nft_value_init(ctx, data, size, desc, - tb[NFTA_DATA_VALUE]); - if (tb[NFTA_DATA_VERDICT] && ctx != NULL) - return nft_verdict_init(ctx, data, desc, tb[NFTA_DATA_VERDICT]); - return -EINVAL; + if (tb[NFTA_DATA_VALUE]) { + if (desc->type != NFT_DATA_VALUE) + return -EINVAL; + + err = nft_value_init(ctx, data, desc, tb[NFTA_DATA_VALUE]); + } else if (tb[NFTA_DATA_VERDICT] && ctx != NULL) { + if (desc->type != NFT_DATA_VERDICT) + return -EINVAL; + + err = nft_verdict_init(ctx, data, desc, tb[NFTA_DATA_VERDICT]); + } else { + err = -EINVAL; + } + + return err; } EXPORT_SYMBOL_GPL(nft_data_init); diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c index 3ddce24ac76d..cee3e4e905ec 100644 --- a/net/netfilter/nf_tables_core.c +++ b/net/netfilter/nf_tables_core.c @@ -34,25 +34,23 @@ static noinline void __nft_trace_packet(struct nft_traceinfo *info, nft_trace_notify(info); } -static inline void nft_trace_packet(struct nft_traceinfo *info, +static inline void nft_trace_packet(const struct nft_pktinfo *pkt, + struct nft_traceinfo *info, const struct nft_chain *chain, const struct nft_rule_dp *rule, enum nft_trace_types type) { if (static_branch_unlikely(&nft_trace_enabled)) { - const struct nft_pktinfo *pkt = info->pkt; - info->nf_trace = pkt->skb->nf_trace; info->rule = rule; __nft_trace_packet(info, chain, type); } } -static inline void nft_trace_copy_nftrace(struct nft_traceinfo *info) +static inline void nft_trace_copy_nftrace(const struct nft_pktinfo *pkt, + struct nft_traceinfo *info) { if (static_branch_unlikely(&nft_trace_enabled)) { - const struct nft_pktinfo *pkt = info->pkt; - if (info->trace) info->nf_trace = pkt->skb->nf_trace; } @@ -96,7 +94,6 @@ static noinline void __nft_trace_verdict(struct nft_traceinfo *info, const struct nft_chain *chain, const struct nft_regs *regs) { - const struct nft_pktinfo *pkt = info->pkt; enum nft_trace_types type; switch (regs->verdict.code) { @@ -110,7 +107,9 @@ static noinline void __nft_trace_verdict(struct nft_traceinfo *info, break; default: type = NFT_TRACETYPE_RULE; - info->nf_trace = pkt->skb->nf_trace; + + if (info->trace) + info->nf_trace = info->pkt->skb->nf_trace; break; } @@ -271,10 +270,10 @@ next_rule: switch (regs.verdict.code) { case NFT_BREAK: regs.verdict.code = NFT_CONTINUE; - nft_trace_copy_nftrace(&info); + nft_trace_copy_nftrace(pkt, &info); continue; case NFT_CONTINUE: - nft_trace_packet(&info, chain, rule, + nft_trace_packet(pkt, &info, chain, rule, NFT_TRACETYPE_RULE); continue; } @@ -318,7 +317,7 @@ next_rule: goto next_rule; } - nft_trace_packet(&info, basechain, NULL, NFT_TRACETYPE_POLICY); + nft_trace_packet(pkt, &info, basechain, NULL, NFT_TRACETYPE_POLICY); if (static_branch_unlikely(&nft_counters_enabled)) nft_update_chain_stats(basechain, pkt); diff --git a/net/netfilter/nft_bitwise.c b/net/netfilter/nft_bitwise.c index 83590afe3768..e6e402b247d0 100644 --- a/net/netfilter/nft_bitwise.c +++ b/net/netfilter/nft_bitwise.c @@ -93,7 +93,16 @@ static const struct nla_policy nft_bitwise_policy[NFTA_BITWISE_MAX + 1] = { static int nft_bitwise_init_bool(struct nft_bitwise *priv, const struct nlattr *const tb[]) { - struct nft_data_desc mask, xor; + struct nft_data_desc mask = { + .type = NFT_DATA_VALUE, + .size = sizeof(priv->mask), + .len = priv->len, + }; + struct nft_data_desc xor = { + .type = NFT_DATA_VALUE, + .size = sizeof(priv->xor), + .len = priv->len, + }; int err; if (tb[NFTA_BITWISE_DATA]) @@ -103,37 +112,30 @@ static int nft_bitwise_init_bool(struct nft_bitwise *priv, !tb[NFTA_BITWISE_XOR]) return -EINVAL; - err = nft_data_init(NULL, &priv->mask, sizeof(priv->mask), &mask, - tb[NFTA_BITWISE_MASK]); + err = nft_data_init(NULL, &priv->mask, &mask, tb[NFTA_BITWISE_MASK]); if (err < 0) return err; - if (mask.type != NFT_DATA_VALUE || mask.len != priv->len) { - err = -EINVAL; - goto err_mask_release; - } - err = nft_data_init(NULL, &priv->xor, sizeof(priv->xor), &xor, - tb[NFTA_BITWISE_XOR]); + err = nft_data_init(NULL, &priv->xor, &xor, tb[NFTA_BITWISE_XOR]); if (err < 0) - goto err_mask_release; - if (xor.type != NFT_DATA_VALUE || xor.len != priv->len) { - err = -EINVAL; - goto err_xor_release; - } + goto err_xor_err; return 0; -err_xor_release: - nft_data_release(&priv->xor, xor.type); -err_mask_release: +err_xor_err: nft_data_release(&priv->mask, mask.type); + return err; } static int nft_bitwise_init_shift(struct nft_bitwise *priv, const struct nlattr *const tb[]) { - struct nft_data_desc d; + struct nft_data_desc desc = { + .type = NFT_DATA_VALUE, + .size = sizeof(priv->data), + .len = sizeof(u32), + }; int err; if (tb[NFTA_BITWISE_MASK] || @@ -143,13 +145,12 @@ static int nft_bitwise_init_shift(struct nft_bitwise *priv, if (!tb[NFTA_BITWISE_DATA]) return -EINVAL; - err = nft_data_init(NULL, &priv->data, sizeof(priv->data), &d, - tb[NFTA_BITWISE_DATA]); + err = nft_data_init(NULL, &priv->data, &desc, tb[NFTA_BITWISE_DATA]); if (err < 0) return err; - if (d.type != NFT_DATA_VALUE || d.len != sizeof(u32) || - priv->data.data[0] >= BITS_PER_TYPE(u32)) { - nft_data_release(&priv->data, d.type); + + if (priv->data.data[0] >= BITS_PER_TYPE(u32)) { + nft_data_release(&priv->data, desc.type); return -EINVAL; } @@ -339,22 +340,21 @@ static const struct nft_expr_ops nft_bitwise_ops = { static int nft_bitwise_extract_u32_data(const struct nlattr * const tb, u32 *out) { - struct nft_data_desc desc; struct nft_data data; - int err = 0; + struct nft_data_desc desc = { + .type = NFT_DATA_VALUE, + .size = sizeof(data), + .len = sizeof(u32), + }; + int err; - err = nft_data_init(NULL, &data, sizeof(data), &desc, tb); + err = nft_data_init(NULL, &data, &desc, tb); if (err < 0) return err; - if (desc.type != NFT_DATA_VALUE || desc.len != sizeof(u32)) { - err = -EINVAL; - goto err; - } *out = data.data[0]; -err: - nft_data_release(&data, desc.type); - return err; + + return 0; } static int nft_bitwise_fast_init(const struct nft_ctx *ctx, diff --git a/net/netfilter/nft_cmp.c b/net/netfilter/nft_cmp.c index 777f09e4dc60..963cf831799c 100644 --- a/net/netfilter/nft_cmp.c +++ b/net/netfilter/nft_cmp.c @@ -73,20 +73,16 @@ static int nft_cmp_init(const struct nft_ctx *ctx, const struct nft_expr *expr, const struct nlattr * const tb[]) { struct nft_cmp_expr *priv = nft_expr_priv(expr); - struct nft_data_desc desc; + struct nft_data_desc desc = { + .type = NFT_DATA_VALUE, + .size = sizeof(priv->data), + }; int err; - err = nft_data_init(NULL, &priv->data, sizeof(priv->data), &desc, - tb[NFTA_CMP_DATA]); + err = nft_data_init(NULL, &priv->data, &desc, tb[NFTA_CMP_DATA]); if (err < 0) return err; - if (desc.type != NFT_DATA_VALUE) { - err = -EINVAL; - nft_data_release(&priv->data, desc.type); - return err; - } - err = nft_parse_register_load(tb[NFTA_CMP_SREG], &priv->sreg, desc.len); if (err < 0) return err; @@ -214,12 +210,14 @@ static int nft_cmp_fast_init(const struct nft_ctx *ctx, const struct nlattr * const tb[]) { struct nft_cmp_fast_expr *priv = nft_expr_priv(expr); - struct nft_data_desc desc; struct nft_data data; + struct nft_data_desc desc = { + .type = NFT_DATA_VALUE, + .size = sizeof(data), + }; int err; - err = nft_data_init(NULL, &data, sizeof(data), &desc, - tb[NFTA_CMP_DATA]); + err = nft_data_init(NULL, &data, &desc, tb[NFTA_CMP_DATA]); if (err < 0) return err; @@ -313,11 +311,13 @@ static int nft_cmp16_fast_init(const struct nft_ctx *ctx, const struct nlattr * const tb[]) { struct nft_cmp16_fast_expr *priv = nft_expr_priv(expr); - struct nft_data_desc desc; + struct nft_data_desc desc = { + .type = NFT_DATA_VALUE, + .size = sizeof(priv->data), + }; int err; - err = nft_data_init(NULL, &priv->data, sizeof(priv->data), &desc, - tb[NFTA_CMP_DATA]); + err = nft_data_init(NULL, &priv->data, &desc, tb[NFTA_CMP_DATA]); if (err < 0) return err; @@ -380,8 +380,11 @@ const struct nft_expr_ops nft_cmp16_fast_ops = { static const struct nft_expr_ops * nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[]) { - struct nft_data_desc desc; struct nft_data data; + struct nft_data_desc desc = { + .type = NFT_DATA_VALUE, + .size = sizeof(data), + }; enum nft_cmp_ops op; u8 sreg; int err; @@ -404,14 +407,10 @@ nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[]) return ERR_PTR(-EINVAL); } - err = nft_data_init(NULL, &data, sizeof(data), &desc, - tb[NFTA_CMP_DATA]); + err = nft_data_init(NULL, &data, &desc, tb[NFTA_CMP_DATA]); if (err < 0) return ERR_PTR(err); - if (desc.type != NFT_DATA_VALUE) - goto err1; - sreg = ntohl(nla_get_be32(tb[NFTA_CMP_SREG])); if (op == NFT_CMP_EQ || op == NFT_CMP_NEQ) { @@ -423,9 +422,6 @@ nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[]) return &nft_cmp16_fast_ops; } return &nft_cmp_ops; -err1: - nft_data_release(&data, desc.type); - return ERR_PTR(-EINVAL); } struct nft_expr_type nft_cmp_type __read_mostly = { diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c index 22f70b543fa2..6983e6ddeef9 100644 --- a/net/netfilter/nft_dynset.c +++ b/net/netfilter/nft_dynset.c @@ -60,7 +60,7 @@ static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr, ®s->data[priv->sreg_key], NULL, ®s->data[priv->sreg_data], timeout, 0, GFP_ATOMIC); - if (elem == NULL) + if (IS_ERR(elem)) goto err1; ext = nft_set_elem_ext(set, elem); diff --git a/net/netfilter/nft_immediate.c b/net/netfilter/nft_immediate.c index b80f7b507349..5f28b21abc7d 100644 --- a/net/netfilter/nft_immediate.c +++ b/net/netfilter/nft_immediate.c @@ -29,20 +29,36 @@ static const struct nla_policy nft_immediate_policy[NFTA_IMMEDIATE_MAX + 1] = { [NFTA_IMMEDIATE_DATA] = { .type = NLA_NESTED }, }; +static enum nft_data_types nft_reg_to_type(const struct nlattr *nla) +{ + enum nft_data_types type; + u8 reg; + + reg = ntohl(nla_get_be32(nla)); + if (reg == NFT_REG_VERDICT) + type = NFT_DATA_VERDICT; + else + type = NFT_DATA_VALUE; + + return type; +} + static int nft_immediate_init(const struct nft_ctx *ctx, const struct nft_expr *expr, const struct nlattr * const tb[]) { struct nft_immediate_expr *priv = nft_expr_priv(expr); - struct nft_data_desc desc; + struct nft_data_desc desc = { + .size = sizeof(priv->data), + }; int err; if (tb[NFTA_IMMEDIATE_DREG] == NULL || tb[NFTA_IMMEDIATE_DATA] == NULL) return -EINVAL; - err = nft_data_init(ctx, &priv->data, sizeof(priv->data), &desc, - tb[NFTA_IMMEDIATE_DATA]); + desc.type = nft_reg_to_type(tb[NFTA_IMMEDIATE_DREG]); + err = nft_data_init(ctx, &priv->data, &desc, tb[NFTA_IMMEDIATE_DATA]); if (err < 0) return err; diff --git a/net/netfilter/nft_range.c b/net/netfilter/nft_range.c index 66f77484c227..832f0d725a9e 100644 --- a/net/netfilter/nft_range.c +++ b/net/netfilter/nft_range.c @@ -51,7 +51,14 @@ static int nft_range_init(const struct nft_ctx *ctx, const struct nft_expr *expr const struct nlattr * const tb[]) { struct nft_range_expr *priv = nft_expr_priv(expr); - struct nft_data_desc desc_from, desc_to; + struct nft_data_desc desc_from = { + .type = NFT_DATA_VALUE, + .size = sizeof(priv->data_from), + }; + struct nft_data_desc desc_to = { + .type = NFT_DATA_VALUE, + .size = sizeof(priv->data_to), + }; int err; u32 op; @@ -61,26 +68,16 @@ static int nft_range_init(const struct nft_ctx *ctx, const struct nft_expr *expr !tb[NFTA_RANGE_TO_DATA]) return -EINVAL; - err = nft_data_init(NULL, &priv->data_from, sizeof(priv->data_from), - &desc_from, tb[NFTA_RANGE_FROM_DATA]); + err = nft_data_init(NULL, &priv->data_from, &desc_from, + tb[NFTA_RANGE_FROM_DATA]); if (err < 0) return err; - if (desc_from.type != NFT_DATA_VALUE) { - err = -EINVAL; - goto err1; - } - - err = nft_data_init(NULL, &priv->data_to, sizeof(priv->data_to), - &desc_to, tb[NFTA_RANGE_TO_DATA]); + err = nft_data_init(NULL, &priv->data_to, &desc_to, + tb[NFTA_RANGE_TO_DATA]); if (err < 0) goto err1; - if (desc_to.type != NFT_DATA_VALUE) { - err = -EINVAL; - goto err2; - } - if (desc_from.len != desc_to.len) { err = -EINVAL; goto err2; diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c index 8490e46359ae..0555dffd80e0 100644 --- a/net/netlabel/netlabel_unlabeled.c +++ b/net/netlabel/netlabel_unlabeled.c @@ -885,7 +885,7 @@ static int netlbl_unlabel_staticadd(struct sk_buff *skb, /* Don't allow users to add both IPv4 and IPv6 addresses for a * single entry. However, allow users to create two entries, one each - * for IPv4 and IPv4, with the same LSM security context which should + * for IPv4 and IPv6, with the same LSM security context which should * achieve the same result. */ if (!info->attrs[NLBL_UNLABEL_A_SECCTX] || !info->attrs[NLBL_UNLABEL_A_IFACE] || diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index a35ab8c27866..3f935cbbaff6 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c @@ -526,7 +526,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb, rcu_assign_pointer(f->next, f1); rcu_assign_pointer(*fp, f); - if (fold && fold->handle && f->handle != fold->handle) { + if (fold) { th = to_hash(fold->handle); h = from_hash(fold->handle >> 16); b = rtnl_dereference(head->table[th]); diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index cc6eabee2830..d47b9689eba6 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -427,14 +427,10 @@ void __qdisc_run(struct Qdisc *q) unsigned long dev_trans_start(struct net_device *dev) { - unsigned long val, res; + unsigned long res = READ_ONCE(netdev_get_tx_queue(dev, 0)->trans_start); + unsigned long val; unsigned int i; - if (is_vlan_dev(dev)) - dev = vlan_dev_real_dev(dev); - else if (netif_is_macvlan(dev)) - dev = macvlan_dev_real_dev(dev); - res = READ_ONCE(netdev_get_tx_queue(dev, 0)->trans_start); for (i = 1; i < dev->num_tx_queues; i++) { val = READ_ONCE(netdev_get_tx_queue(dev, i)->trans_start); if (val && time_after(val, res)) diff --git a/net/tls/tls_device.c b/net/tls/tls_device.c index e3e6cf75aa03..0f983e5f7dde 100644 --- a/net/tls/tls_device.c +++ b/net/tls/tls_device.c @@ -71,7 +71,13 @@ static void tls_device_tx_del_task(struct work_struct *work) struct tls_offload_context_tx *offload_ctx = container_of(work, struct tls_offload_context_tx, destruct_work); struct tls_context *ctx = offload_ctx->ctx; - struct net_device *netdev = ctx->netdev; + struct net_device *netdev; + + /* Safe, because this is the destroy flow, refcount is 0, so + * tls_device_down can't store this field in parallel. + */ + netdev = rcu_dereference_protected(ctx->netdev, + !refcount_read(&ctx->refcount)); netdev->tlsdev_ops->tls_dev_del(netdev, ctx, TLS_OFFLOAD_CTX_DIR_TX); dev_put(netdev); @@ -81,6 +87,7 @@ static void tls_device_tx_del_task(struct work_struct *work) static void tls_device_queue_ctx_destruction(struct tls_context *ctx) { + struct net_device *netdev; unsigned long flags; bool async_cleanup; @@ -91,7 +98,14 @@ static void tls_device_queue_ctx_destruction(struct tls_context *ctx) } list_del(&ctx->list); /* Remove from tls_device_list / tls_device_down_list */ - async_cleanup = ctx->netdev && ctx->tx_conf == TLS_HW; + + /* Safe, because this is the destroy flow, refcount is 0, so + * tls_device_down can't store this field in parallel. + */ + netdev = rcu_dereference_protected(ctx->netdev, + !refcount_read(&ctx->refcount)); + + async_cleanup = netdev && ctx->tx_conf == TLS_HW; if (async_cleanup) { struct tls_offload_context_tx *offload_ctx = tls_offload_ctx_tx(ctx); @@ -229,7 +243,8 @@ static void tls_device_resync_tx(struct sock *sk, struct tls_context *tls_ctx, trace_tls_device_tx_resync_send(sk, seq, rcd_sn); down_read(&device_offload_lock); - netdev = tls_ctx->netdev; + netdev = rcu_dereference_protected(tls_ctx->netdev, + lockdep_is_held(&device_offload_lock)); if (netdev) err = netdev->tlsdev_ops->tls_dev_resync(netdev, sk, seq, rcd_sn, @@ -710,7 +725,7 @@ static void tls_device_resync_rx(struct tls_context *tls_ctx, trace_tls_device_rx_resync_send(sk, seq, rcd_sn, rx_ctx->resync_type); rcu_read_lock(); - netdev = READ_ONCE(tls_ctx->netdev); + netdev = rcu_dereference(tls_ctx->netdev); if (netdev) netdev->tlsdev_ops->tls_dev_resync(netdev, sk, seq, rcd_sn, TLS_OFFLOAD_CTX_DIR_RX); @@ -984,11 +999,17 @@ int tls_device_decrypted(struct sock *sk, struct tls_context *tls_ctx) int is_decrypted = skb->decrypted; int is_encrypted = !is_decrypted; struct sk_buff *skb_iter; + int left; + left = rxm->full_len - skb->len; /* Check if all the data is decrypted already */ - skb_walk_frags(skb, skb_iter) { + skb_iter = skb_shinfo(skb)->frag_list; + while (skb_iter && left > 0) { is_decrypted &= skb_iter->decrypted; is_encrypted &= !skb_iter->decrypted; + + left -= skb_iter->len; + skb_iter = skb_iter->next; } trace_tls_device_decrypted(sk, tcp_sk(sk)->copied_seq - rxm->full_len, @@ -1029,7 +1050,7 @@ static void tls_device_attach(struct tls_context *ctx, struct sock *sk, if (sk->sk_destruct != tls_device_sk_destruct) { refcount_set(&ctx->refcount, 1); dev_hold(netdev); - ctx->netdev = netdev; + RCU_INIT_POINTER(ctx->netdev, netdev); spin_lock_irq(&tls_device_lock); list_add_tail(&ctx->list, &tls_device_list); spin_unlock_irq(&tls_device_lock); @@ -1300,7 +1321,8 @@ void tls_device_offload_cleanup_rx(struct sock *sk) struct net_device *netdev; down_read(&device_offload_lock); - netdev = tls_ctx->netdev; + netdev = rcu_dereference_protected(tls_ctx->netdev, + lockdep_is_held(&device_offload_lock)); if (!netdev) goto out; @@ -1309,7 +1331,7 @@ void tls_device_offload_cleanup_rx(struct sock *sk) if (tls_ctx->tx_conf != TLS_HW) { dev_put(netdev); - tls_ctx->netdev = NULL; + rcu_assign_pointer(tls_ctx->netdev, NULL); } else { set_bit(TLS_RX_DEV_CLOSED, &tls_ctx->flags); } @@ -1329,7 +1351,11 @@ static int tls_device_down(struct net_device *netdev) spin_lock_irqsave(&tls_device_lock, flags); list_for_each_entry_safe(ctx, tmp, &tls_device_list, list) { - if (ctx->netdev != netdev || + struct net_device *ctx_netdev = + rcu_dereference_protected(ctx->netdev, + lockdep_is_held(&device_offload_lock)); + + if (ctx_netdev != netdev || !refcount_inc_not_zero(&ctx->refcount)) continue; @@ -1346,7 +1372,7 @@ static int tls_device_down(struct net_device *netdev) /* Stop the RX and TX resync. * tls_dev_resync must not be called after tls_dev_del. */ - WRITE_ONCE(ctx->netdev, NULL); + rcu_assign_pointer(ctx->netdev, NULL); /* Start skipping the RX resync logic completely. */ set_bit(TLS_RX_DEV_DEGRADED, &ctx->flags); diff --git a/net/tls/tls_device_fallback.c b/net/tls/tls_device_fallback.c index 618cee704217..7dfc8023e0f1 100644 --- a/net/tls/tls_device_fallback.c +++ b/net/tls/tls_device_fallback.c @@ -426,7 +426,8 @@ struct sk_buff *tls_validate_xmit_skb(struct sock *sk, struct net_device *dev, struct sk_buff *skb) { - if (dev == tls_get_ctx(sk)->netdev || netif_is_bond_master(dev)) + if (dev == rcu_dereference_bh(tls_get_ctx(sk)->netdev) || + netif_is_bond_master(dev)) return skb; return tls_sw_fallback(sk, skb); diff --git a/net/tls/tls_strp.c b/net/tls/tls_strp.c index f0b7c9122fba..9b79e334dbd9 100644 --- a/net/tls/tls_strp.c +++ b/net/tls/tls_strp.c @@ -41,7 +41,7 @@ static struct sk_buff *tls_strp_msg_make_copy(struct tls_strparser *strp) struct sk_buff *skb; int i, err, offset; - skb = alloc_skb_with_frags(0, strp->anchor->len, TLS_PAGE_ORDER, + skb = alloc_skb_with_frags(0, strp->stm.full_len, TLS_PAGE_ORDER, &err, strp->sk->sk_allocation); if (!skb) return NULL; diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c index f04abf662ec6..b4ee163154a6 100644 --- a/net/vmw_vsock/af_vsock.c +++ b/net/vmw_vsock/af_vsock.c @@ -1286,6 +1286,7 @@ static void vsock_connect_timeout(struct work_struct *work) if (sk->sk_state == TCP_SYN_SENT && (sk->sk_shutdown != SHUTDOWN_MASK)) { sk->sk_state = TCP_CLOSE; + sk->sk_socket->state = SS_UNCONNECTED; sk->sk_err = ETIMEDOUT; sk_error_report(sk); vsock_transport_cancel_pkt(vsk); @@ -1391,7 +1392,14 @@ static int vsock_connect(struct socket *sock, struct sockaddr *addr, * timeout fires. */ sock_hold(sk); - schedule_delayed_work(&vsk->connect_work, timeout); + + /* If the timeout function is already scheduled, + * reschedule it, then ungrab the socket refcount to + * keep it balanced. + */ + if (mod_delayed_work(system_wq, &vsk->connect_work, + timeout)) + sock_put(sk); /* Skip ahead to preserve error code set above. */ goto out_wait; diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 62c773cf1b8d..27fb2a0c4052 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -782,9 +782,11 @@ void __cfg80211_connect_result(struct net_device *dev, #endif if (cr->status == WLAN_STATUS_SUCCESS) { - for_each_valid_link(cr, link) { - if (WARN_ON_ONCE(!cr->links[link].bss)) - break; + if (!wiphy_to_rdev(wdev->wiphy)->ops->connect) { + for_each_valid_link(cr, link) { + if (WARN_ON_ONCE(!cr->links[link].bss)) + break; + } } for_each_valid_link(cr, link) { diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index 6bc2ac8d8146..3b55502b2965 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -719,6 +719,11 @@ static int x25_wait_for_connection_establishment(struct sock *sk) sk->sk_socket->state = SS_UNCONNECTED; break; } + rc = -ENOTCONN; + if (sk->sk_state == TCP_CLOSE) { + sk->sk_socket->state = SS_UNCONNECTED; + break; + } rc = 0; if (sk->sk_state != TCP_ESTABLISHED) { release_sock(sk); diff --git a/scripts/Makefile.extrawarn b/scripts/Makefile.extrawarn index 9bbaf7112a9b..f5f0d6f09053 100644 --- a/scripts/Makefile.extrawarn +++ b/scripts/Makefile.extrawarn @@ -47,6 +47,7 @@ else ifdef CONFIG_CC_IS_CLANG KBUILD_CFLAGS += -Wno-initializer-overrides +KBUILD_CFLAGS += -Wno-format KBUILD_CFLAGS += -Wno-sign-compare KBUILD_CFLAGS += -Wno-format-zero-length KBUILD_CFLAGS += $(call cc-disable-warning, pointer-to-enum-cast) diff --git a/scripts/remove-stale-files b/scripts/remove-stale-files index 51e5c76bcd07..ccadfa3afb2b 100755 --- a/scripts/remove-stale-files +++ b/scripts/remove-stale-files @@ -42,6 +42,8 @@ if [ -n "${building_out_of_srctree}" ]; then done fi +rm -f arch/riscv/purgatory/kexec-purgatory.c + rm -f scripts/extract-cert rm -f arch/x86/purgatory/kexec-purgatory.c diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 7b2e62fa82d5..384426d7e9dd 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -2940,8 +2940,7 @@ static int hda_codec_runtime_suspend(struct device *dev) if (!codec->card) return 0; - if (!codec->bus->jackpoll_in_suspend) - cancel_delayed_work_sync(&codec->jackpoll_work); + cancel_delayed_work_sync(&codec->jackpoll_work); state = hda_call_codec_suspend(codec); if (codec->link_down_at_suspend || @@ -2949,6 +2948,11 @@ static int hda_codec_runtime_suspend(struct device *dev) (state & AC_PWRST_CLK_STOP_OK))) snd_hdac_codec_link_down(&codec->core); snd_hda_codec_display_power(codec, false); + + if (codec->bus->jackpoll_in_suspend && + (dev->power.power_state.event != PM_EVENT_SUSPEND)) + schedule_delayed_work(&codec->jackpoll_work, + codec->jackpoll_interval); return 0; } @@ -2972,6 +2976,9 @@ static int hda_codec_runtime_resume(struct device *dev) #ifdef CONFIG_PM_SLEEP static int hda_codec_pm_prepare(struct device *dev) { + struct hda_codec *codec = dev_to_hda_codec(dev); + + cancel_delayed_work_sync(&codec->jackpoll_work); dev->power.power_state = PMSG_SUSPEND; return pm_runtime_suspended(dev); } @@ -2991,9 +2998,6 @@ static void hda_codec_pm_complete(struct device *dev) static int hda_codec_pm_suspend(struct device *dev) { - struct hda_codec *codec = dev_to_hda_codec(dev); - - cancel_delayed_work_sync(&codec->jackpoll_work); dev->power.power_state = PMSG_SUSPEND; return pm_runtime_force_suspend(dev); } diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 678fbcaf2a3b..6807b4708a17 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -395,6 +395,7 @@ static const struct snd_pci_quirk cs420x_fixup_tbl[] = { /* codec SSID */ SND_PCI_QUIRK(0x106b, 0x0600, "iMac 14,1", CS420X_IMAC27_122), + SND_PCI_QUIRK(0x106b, 0x0900, "iMac 12,1", CS420X_IMAC27_122), SND_PCI_QUIRK(0x106b, 0x1c00, "MacBookPro 8,1", CS420X_MBP81), SND_PCI_QUIRK(0x106b, 0x2000, "iMac 12,2", CS420X_IMAC27_122), SND_PCI_QUIRK(0x106b, 0x2800, "MacBookPro 10,1", CS420X_MBP101), diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 83ae21a01bbf..7b1a30a551f6 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -222,6 +222,7 @@ enum { CXT_PINCFG_LEMOTE_A1205, CXT_PINCFG_COMPAQ_CQ60, CXT_FIXUP_STEREO_DMIC, + CXT_PINCFG_LENOVO_NOTEBOOK, CXT_FIXUP_INC_MIC_BOOST, CXT_FIXUP_HEADPHONE_MIC_PIN, CXT_FIXUP_HEADPHONE_MIC, @@ -772,6 +773,14 @@ static const struct hda_fixup cxt_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = cxt_fixup_stereo_dmic, }, + [CXT_PINCFG_LENOVO_NOTEBOOK] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1a, 0x05d71030 }, + { } + }, + .chain_id = CXT_FIXUP_STEREO_DMIC, + }, [CXT_FIXUP_INC_MIC_BOOST] = { .type = HDA_FIXUP_FUNC, .v.func = cxt5066_increase_mic_boost, @@ -971,7 +980,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = { SND_PCI_QUIRK(0x17aa, 0x3905, "Lenovo G50-30", CXT_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x17aa, 0x390b, "Lenovo G50-80", CXT_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC), - SND_PCI_QUIRK(0x17aa, 0x3977, "Lenovo IdeaPad U310", CXT_FIXUP_STEREO_DMIC), + SND_PCI_QUIRK(0x17aa, 0x3977, "Lenovo IdeaPad U310", CXT_PINCFG_LENOVO_NOTEBOOK), SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo G50-70", CXT_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x17aa, 0x397b, "Lenovo S205", CXT_FIXUP_STEREO_DMIC), SND_PCI_QUIRK_VENDOR(0x17aa, "Thinkpad", CXT_FIXUP_THINKPAD_ACPI), diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 8a57636f622e..fd630d62b5a0 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -6909,6 +6909,7 @@ enum { ALC269_FIXUP_LIMIT_INT_MIC_BOOST, ALC269VB_FIXUP_ASUS_ZENBOOK, ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A, + ALC269VB_FIXUP_ASUS_MIC_NO_PRESENCE, ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED, ALC269VB_FIXUP_ORDISSIMO_EVE2, ALC283_FIXUP_CHROME_BOOK, @@ -7497,6 +7498,15 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269VB_FIXUP_ASUS_ZENBOOK, }, + [ALC269VB_FIXUP_ASUS_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x18, 0x01a110f0 }, /* use as headset mic */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MIC + }, [ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED] = { .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_limit_int_mic_boost, @@ -9203,6 +9213,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { ALC285_FIXUP_HP_GPIO_AMP_INIT), SND_PCI_QUIRK(0x103c, 0x8783, "HP ZBook Fury 15 G7 Mobile Workstation", ALC285_FIXUP_HP_GPIO_AMP_INIT), + SND_PCI_QUIRK(0x103c, 0x8786, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8787, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8788, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x87c8, "HP", ALC287_FIXUP_HP_GPIO_LED), @@ -9274,6 +9285,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x12a0, "ASUS X441UV", ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x12e0, "ASUS X541SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x12f0, "ASUS X541UV", ALC256_FIXUP_ASUS_MIC), + SND_PCI_QUIRK(0x1043, 0x1313, "Asus K42JZ", ALC269VB_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x13b0, "ASUS Z550SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_ASUS_ZENBOOK), SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A), diff --git a/sound/pci/ice1712/quartet.c b/sound/pci/ice1712/quartet.c index 0dfa093f7dca..20b3e8f94719 100644 --- a/sound/pci/ice1712/quartet.c +++ b/sound/pci/ice1712/quartet.c @@ -566,7 +566,7 @@ static int qtet_ain12_sw_put(struct snd_kcontrol *kcontrol, { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); unsigned int old, new, tmp, masked_old; - old = new = get_scr(ice); + old = get_scr(ice); masked_old = old & (SCR_AIN12_SEL1 | SCR_AIN12_SEL0); tmp = ucontrol->value.integer.value[0]; if (tmp == 2) diff --git a/sound/usb/card.c b/sound/usb/card.c index 0fff96a5d3ab..d356743de2ff 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -387,6 +387,14 @@ static const struct usb_audio_device_name usb_audio_names[] = { DEVICE_NAME(0x05e1, 0x0408, "Syntek", "STK1160"), DEVICE_NAME(0x05e1, 0x0480, "Hauppauge", "Woodbury"), + /* ASUS ROG Zenith II: this machine has also two devices, one for + * the front headphone and another for the rest + */ + PROFILE_NAME(0x0b05, 0x1915, "ASUS", "Zenith II Front Headphone", + "Zenith-II-Front-Headphone"), + PROFILE_NAME(0x0b05, 0x1916, "ASUS", "Zenith II Main Audio", + "Zenith-II-Main-Audio"), + /* ASUS ROG Strix */ PROFILE_NAME(0x0b05, 0x1917, "Realtek", "ALC1220-VB-DT", "Realtek-ALC1220-VB-Desktop"), diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c index 3c795675f048..f4bd1e8ae4b6 100644 --- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -374,13 +374,28 @@ static const struct usbmix_name_map corsair_virtuoso_map[] = { { 0 } }; -/* Some mobos shipped with a dummy HD-audio show the invalid GET_MIN/GET_MAX - * response for Input Gain Pad (id=19, control=12) and the connector status - * for SPDIF terminal (id=18). Skip them. - */ -static const struct usbmix_name_map asus_rog_map[] = { - { 18, NULL }, /* OT, connector control */ - { 19, NULL, 12 }, /* FU, Input Gain Pad */ +/* ASUS ROG Zenith II with Realtek ALC1220-VB */ +static const struct usbmix_name_map asus_zenith_ii_map[] = { + { 19, NULL, 12 }, /* FU, Input Gain Pad - broken response, disabled */ + { 16, "Speaker" }, /* OT */ + { 22, "Speaker Playback" }, /* FU */ + { 7, "Line" }, /* IT */ + { 19, "Line Capture" }, /* FU */ + { 8, "Mic" }, /* IT */ + { 20, "Mic Capture" }, /* FU */ + { 9, "Front Mic" }, /* IT */ + { 21, "Front Mic Capture" }, /* FU */ + { 17, "IEC958" }, /* OT */ + { 23, "IEC958 Playback" }, /* FU */ + {} +}; + +static const struct usbmix_connector_map asus_zenith_ii_connector_map[] = { + { 10, 16 }, /* (Back) Speaker */ + { 11, 17 }, /* SPDIF */ + { 13, 7 }, /* Line */ + { 14, 8 }, /* Mic */ + { 15, 9 }, /* Front Mic */ {} }; @@ -611,9 +626,10 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = { .map = gigabyte_b450_map, .connector_map = gigabyte_b450_connector_map, }, - { /* ASUS ROG Zenith II */ + { /* ASUS ROG Zenith II (main audio) */ .id = USB_ID(0x0b05, 0x1916), - .map = asus_rog_map, + .map = asus_zenith_ii_map, + .connector_map = asus_zenith_ii_connector_map, }, { /* ASUS ROG Strix */ .id = USB_ID(0x0b05, 0x1917), diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index c06d6dfa8139..ab0d459f4271 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -3420,6 +3420,7 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) case USB_ID(0x1235, 0x8213): /* Focusrite Scarlett 8i6 3rd Gen */ case USB_ID(0x1235, 0x8214): /* Focusrite Scarlett 18i8 3rd Gen */ case USB_ID(0x1235, 0x8215): /* Focusrite Scarlett 18i20 3rd Gen */ + case USB_ID(0x1235, 0x820c): /* Focusrite Clarett+ 8Pre */ err = snd_scarlett_gen2_init(mixer); break; diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index 69a2cd429ee2..9d11bb08667e 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -1,13 +1,15 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Focusrite Scarlett Gen 2/3 Driver for ALSA + * Focusrite Scarlett Gen 2/3 and Clarett+ Driver for ALSA * * Supported models: * - 6i6/18i8/18i20 Gen 2 * - Solo/2i2/4i4/8i6/18i8/18i20 Gen 3 + * - Clarett+ 8Pre * * Copyright (c) 2018-2022 by Geoffrey D. Bennett <g at b4.vu> * Copyright (c) 2020-2021 by Vladimir Sadovnikov <sadko4u@gmail.com> + * Copyright (c) 2022 by Christian Colglazier <christian@cacolglazier.com> * * Based on the Scarlett (Gen 1) Driver for ALSA: * @@ -51,6 +53,9 @@ * Support for phantom power, direct monitoring, speaker switching, * and talkback added in May-June 2021. * + * Support for Clarett+ 8Pre added in Aug 2022 by Christian + * Colglazier. + * * This ALSA mixer gives access to (model-dependent): * - input, output, mixer-matrix muxes * - mixer-matrix gain stages @@ -203,7 +208,8 @@ enum { SCARLETT2_CONFIG_SET_NO_MIXER = 0, SCARLETT2_CONFIG_SET_GEN_2 = 1, SCARLETT2_CONFIG_SET_GEN_3 = 2, - SCARLETT2_CONFIG_SET_COUNT = 3 + SCARLETT2_CONFIG_SET_CLARETT = 3, + SCARLETT2_CONFIG_SET_COUNT = 4 }; /* Hardware port types: @@ -841,6 +847,61 @@ static const struct scarlett2_device_info s18i20_gen3_info = { } }, }; +static const struct scarlett2_device_info clarett_8pre_info = { + .usb_id = USB_ID(0x1235, 0x820c), + + .config_set = SCARLETT2_CONFIG_SET_CLARETT, + .line_out_hw_vol = 1, + .level_input_count = 2, + .air_input_count = 8, + + .line_out_descrs = { + "Monitor L", + "Monitor R", + NULL, + NULL, + NULL, + NULL, + "Headphones 1 L", + "Headphones 1 R", + "Headphones 2 L", + "Headphones 2 R", + }, + + .port_count = { + [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 }, + [SCARLETT2_PORT_TYPE_ANALOGUE] = { 8, 10 }, + [SCARLETT2_PORT_TYPE_SPDIF] = { 2, 2 }, + [SCARLETT2_PORT_TYPE_ADAT] = { 8, 8 }, + [SCARLETT2_PORT_TYPE_MIX] = { 10, 18 }, + [SCARLETT2_PORT_TYPE_PCM] = { 20, 18 }, + }, + + .mux_assignment = { { + { SCARLETT2_PORT_TYPE_PCM, 0, 18 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 10 }, + { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, + { SCARLETT2_PORT_TYPE_ADAT, 0, 8 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, + { SCARLETT2_PORT_TYPE_NONE, 0, 8 }, + { 0, 0, 0 }, + }, { + { SCARLETT2_PORT_TYPE_PCM, 0, 14 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 10 }, + { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, + { SCARLETT2_PORT_TYPE_ADAT, 0, 4 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, + { SCARLETT2_PORT_TYPE_NONE, 0, 8 }, + { 0, 0, 0 }, + }, { + { SCARLETT2_PORT_TYPE_PCM, 0, 12 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 10 }, + { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, + { SCARLETT2_PORT_TYPE_NONE, 0, 22 }, + { 0, 0, 0 }, + } }, +}; + static const struct scarlett2_device_info *scarlett2_devices[] = { /* Supported Gen 2 devices */ &s6i6_gen2_info, @@ -855,6 +916,9 @@ static const struct scarlett2_device_info *scarlett2_devices[] = { &s18i8_gen3_info, &s18i20_gen3_info, + /* Supported Clarett+ devices */ + &clarett_8pre_info, + /* End of list */ NULL }; @@ -1047,6 +1111,29 @@ static const struct scarlett2_config [SCARLETT2_CONFIG_TALKBACK_MAP] = { .offset = 0xb0, .size = 16, .activate = 10 }, + +/* Clarett+ 8Pre */ +}, { + [SCARLETT2_CONFIG_DIM_MUTE] = { + .offset = 0x31, .size = 8, .activate = 2 }, + + [SCARLETT2_CONFIG_LINE_OUT_VOLUME] = { + .offset = 0x34, .size = 16, .activate = 1 }, + + [SCARLETT2_CONFIG_MUTE_SWITCH] = { + .offset = 0x5c, .size = 8, .activate = 1 }, + + [SCARLETT2_CONFIG_SW_HW_SWITCH] = { + .offset = 0x66, .size = 8, .activate = 3 }, + + [SCARLETT2_CONFIG_LEVEL_SWITCH] = { + .offset = 0x7c, .size = 8, .activate = 7 }, + + [SCARLETT2_CONFIG_AIR_SWITCH] = { + .offset = 0x95, .size = 8, .activate = 8 }, + + [SCARLETT2_CONFIG_STANDALONE_SWITCH] = { + .offset = 0x8d, .size = 8, .activate = 6 }, } }; /* proprietary request/response format */ diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index e692ae04436a..d45d1d7e6664 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -1269,7 +1269,7 @@ static inline void fill_playback_urb_dsd_dop(struct snd_usb_substream *subs, unsigned int wrap = subs->buffer_bytes; u8 *dst = urb->transfer_buffer; u8 *src = runtime->dma_area; - u8 marker[] = { 0x05, 0xfa }; + static const u8 marker[] = { 0x05, 0xfa }; unsigned int queued = 0; /* diff --git a/tools/lib/bpf/skel_internal.h b/tools/lib/bpf/skel_internal.h index bd6f4505e7b1..70adf7b119b9 100644 --- a/tools/lib/bpf/skel_internal.h +++ b/tools/lib/bpf/skel_internal.h @@ -66,13 +66,13 @@ struct bpf_load_and_run_opts { const char *errstr; }; -long bpf_sys_bpf(__u32 cmd, void *attr, __u32 attr_size); +long kern_sys_bpf(__u32 cmd, void *attr, __u32 attr_size); static inline int skel_sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr, unsigned int size) { #ifdef __KERNEL__ - return bpf_sys_bpf(cmd, attr, size); + return kern_sys_bpf(cmd, attr, size); #else return syscall(__NR_bpf, cmd, attr, size); #endif diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_iter.c b/tools/testing/selftests/bpf/prog_tests/bpf_iter.c index a33874b081b6..e89685bd587c 100644 --- a/tools/testing/selftests/bpf/prog_tests/bpf_iter.c +++ b/tools/testing/selftests/bpf/prog_tests/bpf_iter.c @@ -28,6 +28,7 @@ #include "bpf_iter_test_kern6.skel.h" #include "bpf_iter_bpf_link.skel.h" #include "bpf_iter_ksym.skel.h" +#include "bpf_iter_sockmap.skel.h" static int duration; @@ -67,6 +68,50 @@ free_link: bpf_link__destroy(link); } +static void do_read_map_iter_fd(struct bpf_object_skeleton **skel, struct bpf_program *prog, + struct bpf_map *map) +{ + DECLARE_LIBBPF_OPTS(bpf_iter_attach_opts, opts); + union bpf_iter_link_info linfo; + struct bpf_link *link; + char buf[16] = {}; + int iter_fd, len; + + memset(&linfo, 0, sizeof(linfo)); + linfo.map.map_fd = bpf_map__fd(map); + opts.link_info = &linfo; + opts.link_info_len = sizeof(linfo); + link = bpf_program__attach_iter(prog, &opts); + if (!ASSERT_OK_PTR(link, "attach_map_iter")) + return; + + iter_fd = bpf_iter_create(bpf_link__fd(link)); + if (!ASSERT_GE(iter_fd, 0, "create_map_iter")) { + bpf_link__destroy(link); + return; + } + + /* Close link and map fd prematurely */ + bpf_link__destroy(link); + bpf_object__destroy_skeleton(*skel); + *skel = NULL; + + /* Try to let map free work to run first if map is freed */ + usleep(100); + /* Memory used by both sock map and sock local storage map are + * freed after two synchronize_rcu() calls, so wait for it + */ + kern_sync_rcu(); + kern_sync_rcu(); + + /* Read after both map fd and link fd are closed */ + while ((len = read(iter_fd, buf, sizeof(buf))) > 0) + ; + ASSERT_GE(len, 0, "read_iterator"); + + close(iter_fd); +} + static int read_fd_into_buffer(int fd, char *buf, int size) { int bufleft = size; @@ -634,6 +679,12 @@ static void test_bpf_hash_map(void) goto out; } + /* Sleepable program is prohibited for hash map iterator */ + linfo.map.map_fd = map_fd; + link = bpf_program__attach_iter(skel->progs.sleepable_dummy_dump, &opts); + if (!ASSERT_ERR_PTR(link, "attach_sleepable_prog_to_iter")) + goto out; + linfo.map.map_fd = map_fd; link = bpf_program__attach_iter(skel->progs.dump_bpf_hash_map, &opts); if (!ASSERT_OK_PTR(link, "attach_iter")) @@ -827,6 +878,20 @@ out: bpf_iter_bpf_array_map__destroy(skel); } +static void test_bpf_array_map_iter_fd(void) +{ + struct bpf_iter_bpf_array_map *skel; + + skel = bpf_iter_bpf_array_map__open_and_load(); + if (!ASSERT_OK_PTR(skel, "bpf_iter_bpf_array_map__open_and_load")) + return; + + do_read_map_iter_fd(&skel->skeleton, skel->progs.dump_bpf_array_map, + skel->maps.arraymap1); + + bpf_iter_bpf_array_map__destroy(skel); +} + static void test_bpf_percpu_array_map(void) { DECLARE_LIBBPF_OPTS(bpf_iter_attach_opts, opts); @@ -1009,6 +1074,20 @@ out: bpf_iter_bpf_sk_storage_helpers__destroy(skel); } +static void test_bpf_sk_stoarge_map_iter_fd(void) +{ + struct bpf_iter_bpf_sk_storage_map *skel; + + skel = bpf_iter_bpf_sk_storage_map__open_and_load(); + if (!ASSERT_OK_PTR(skel, "bpf_iter_bpf_sk_storage_map__open_and_load")) + return; + + do_read_map_iter_fd(&skel->skeleton, skel->progs.rw_bpf_sk_storage_map, + skel->maps.sk_stg_map); + + bpf_iter_bpf_sk_storage_map__destroy(skel); +} + static void test_bpf_sk_storage_map(void) { DECLARE_LIBBPF_OPTS(bpf_iter_attach_opts, opts); @@ -1044,7 +1123,15 @@ static void test_bpf_sk_storage_map(void) linfo.map.map_fd = map_fd; opts.link_info = &linfo; opts.link_info_len = sizeof(linfo); - link = bpf_program__attach_iter(skel->progs.dump_bpf_sk_storage_map, &opts); + link = bpf_program__attach_iter(skel->progs.oob_write_bpf_sk_storage_map, &opts); + err = libbpf_get_error(link); + if (!ASSERT_EQ(err, -EACCES, "attach_oob_write_iter")) { + if (!err) + bpf_link__destroy(link); + goto out; + } + + link = bpf_program__attach_iter(skel->progs.rw_bpf_sk_storage_map, &opts); if (!ASSERT_OK_PTR(link, "attach_iter")) goto out; @@ -1052,6 +1139,7 @@ static void test_bpf_sk_storage_map(void) if (!ASSERT_GE(iter_fd, 0, "create_iter")) goto free_link; + skel->bss->to_add_val = time(NULL); /* do some tests */ while ((len = read(iter_fd, buf, sizeof(buf))) > 0) ; @@ -1065,6 +1153,13 @@ static void test_bpf_sk_storage_map(void) if (!ASSERT_EQ(skel->bss->val_sum, expected_val, "val_sum")) goto close_iter; + for (i = 0; i < num_sockets; i++) { + err = bpf_map_lookup_elem(map_fd, &sock_fd[i], &val); + if (!ASSERT_OK(err, "map_lookup") || + !ASSERT_EQ(val, i + 1 + skel->bss->to_add_val, "check_map_value")) + break; + } + close_iter: close(iter_fd); free_link: @@ -1217,6 +1312,19 @@ out: bpf_iter_task_vma__destroy(skel); } +void test_bpf_sockmap_map_iter_fd(void) +{ + struct bpf_iter_sockmap *skel; + + skel = bpf_iter_sockmap__open_and_load(); + if (!ASSERT_OK_PTR(skel, "bpf_iter_sockmap__open_and_load")) + return; + + do_read_map_iter_fd(&skel->skeleton, skel->progs.copy, skel->maps.sockmap); + + bpf_iter_sockmap__destroy(skel); +} + void test_bpf_iter(void) { if (test__start_subtest("btf_id_or_null")) @@ -1267,10 +1375,14 @@ void test_bpf_iter(void) test_bpf_percpu_hash_map(); if (test__start_subtest("bpf_array_map")) test_bpf_array_map(); + if (test__start_subtest("bpf_array_map_iter_fd")) + test_bpf_array_map_iter_fd(); if (test__start_subtest("bpf_percpu_array_map")) test_bpf_percpu_array_map(); if (test__start_subtest("bpf_sk_storage_map")) test_bpf_sk_storage_map(); + if (test__start_subtest("bpf_sk_storage_map_iter_fd")) + test_bpf_sk_stoarge_map_iter_fd(); if (test__start_subtest("bpf_sk_storage_delete")) test_bpf_sk_storage_delete(); if (test__start_subtest("bpf_sk_storage_get")) @@ -1283,4 +1395,6 @@ void test_bpf_iter(void) test_link_iter(); if (test__start_subtest("ksym")) test_ksym_iter(); + if (test__start_subtest("bpf_sockmap_map_iter_fd")) + test_bpf_sockmap_map_iter_fd(); } diff --git a/tools/testing/selftests/bpf/prog_tests/fexit_bpf2bpf.c b/tools/testing/selftests/bpf/prog_tests/fexit_bpf2bpf.c index 02bb8cbf9194..da860b07abb5 100644 --- a/tools/testing/selftests/bpf/prog_tests/fexit_bpf2bpf.c +++ b/tools/testing/selftests/bpf/prog_tests/fexit_bpf2bpf.c @@ -3,6 +3,7 @@ #include <test_progs.h> #include <network_helpers.h> #include <bpf/btf.h> +#include "bind4_prog.skel.h" typedef int (*test_cb)(struct bpf_object *obj); @@ -407,6 +408,98 @@ static void test_func_replace_global_func(void) prog_name, false, NULL); } +static int find_prog_btf_id(const char *name, __u32 attach_prog_fd) +{ + struct bpf_prog_info info = {}; + __u32 info_len = sizeof(info); + struct btf *btf; + int ret; + + ret = bpf_obj_get_info_by_fd(attach_prog_fd, &info, &info_len); + if (ret) + return ret; + + if (!info.btf_id) + return -EINVAL; + + btf = btf__load_from_kernel_by_id(info.btf_id); + ret = libbpf_get_error(btf); + if (ret) + return ret; + + ret = btf__find_by_name_kind(btf, name, BTF_KIND_FUNC); + btf__free(btf); + return ret; +} + +static int load_fentry(int attach_prog_fd, int attach_btf_id) +{ + LIBBPF_OPTS(bpf_prog_load_opts, opts, + .expected_attach_type = BPF_TRACE_FENTRY, + .attach_prog_fd = attach_prog_fd, + .attach_btf_id = attach_btf_id, + ); + struct bpf_insn insns[] = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }; + + return bpf_prog_load(BPF_PROG_TYPE_TRACING, + "bind4_fentry", + "GPL", + insns, + ARRAY_SIZE(insns), + &opts); +} + +static void test_fentry_to_cgroup_bpf(void) +{ + struct bind4_prog *skel = NULL; + struct bpf_prog_info info = {}; + __u32 info_len = sizeof(info); + int cgroup_fd = -1; + int fentry_fd = -1; + int btf_id; + + cgroup_fd = test__join_cgroup("/fentry_to_cgroup_bpf"); + if (!ASSERT_GE(cgroup_fd, 0, "cgroup_fd")) + return; + + skel = bind4_prog__open_and_load(); + if (!ASSERT_OK_PTR(skel, "skel")) + goto cleanup; + + skel->links.bind_v4_prog = bpf_program__attach_cgroup(skel->progs.bind_v4_prog, cgroup_fd); + if (!ASSERT_OK_PTR(skel->links.bind_v4_prog, "bpf_program__attach_cgroup")) + goto cleanup; + + btf_id = find_prog_btf_id("bind_v4_prog", bpf_program__fd(skel->progs.bind_v4_prog)); + if (!ASSERT_GE(btf_id, 0, "find_prog_btf_id")) + goto cleanup; + + fentry_fd = load_fentry(bpf_program__fd(skel->progs.bind_v4_prog), btf_id); + if (!ASSERT_GE(fentry_fd, 0, "load_fentry")) + goto cleanup; + + /* Make sure bpf_obj_get_info_by_fd works correctly when attaching + * to another BPF program. + */ + + ASSERT_OK(bpf_obj_get_info_by_fd(fentry_fd, &info, &info_len), + "bpf_obj_get_info_by_fd"); + + ASSERT_EQ(info.btf_id, 0, "info.btf_id"); + ASSERT_EQ(info.attach_btf_id, btf_id, "info.attach_btf_id"); + ASSERT_GT(info.attach_btf_obj_id, 0, "info.attach_btf_obj_id"); + +cleanup: + if (cgroup_fd >= 0) + close(cgroup_fd); + if (fentry_fd >= 0) + close(fentry_fd); + bind4_prog__destroy(skel); +} + /* NOTE: affect other tests, must run in serial mode */ void serial_test_fexit_bpf2bpf(void) { @@ -430,4 +523,6 @@ void serial_test_fexit_bpf2bpf(void) test_fmod_ret_freplace(); if (test__start_subtest("func_replace_global_func")) test_func_replace_global_func(); + if (test__start_subtest("fentry_to_cgroup_bpf")) + test_fentry_to_cgroup_bpf(); } diff --git a/tools/testing/selftests/bpf/prog_tests/lru_bug.c b/tools/testing/selftests/bpf/prog_tests/lru_bug.c new file mode 100644 index 000000000000..3c7822390827 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/lru_bug.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <test_progs.h> + +#include "lru_bug.skel.h" + +void test_lru_bug(void) +{ + struct lru_bug *skel; + int ret; + + skel = lru_bug__open_and_load(); + if (!ASSERT_OK_PTR(skel, "lru_bug__open_and_load")) + return; + ret = lru_bug__attach(skel); + if (!ASSERT_OK(ret, "lru_bug__attach")) + goto end; + usleep(1); + ASSERT_OK(skel->data->result, "prealloc_lru_pop doesn't call check_and_init_map_value"); +end: + lru_bug__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/progs/bpf_iter_bpf_hash_map.c b/tools/testing/selftests/bpf/progs/bpf_iter_bpf_hash_map.c index 0aa3cd34cbe3..d7a69217fb68 100644 --- a/tools/testing/selftests/bpf/progs/bpf_iter_bpf_hash_map.c +++ b/tools/testing/selftests/bpf/progs/bpf_iter_bpf_hash_map.c @@ -112,3 +112,12 @@ int dump_bpf_hash_map(struct bpf_iter__bpf_map_elem *ctx) return 0; } + +SEC("iter.s/bpf_map_elem") +int sleepable_dummy_dump(struct bpf_iter__bpf_map_elem *ctx) +{ + if (ctx->meta->seq_num == 0) + BPF_SEQ_PRINTF(ctx->meta->seq, "map dump starts\n"); + + return 0; +} diff --git a/tools/testing/selftests/bpf/progs/bpf_iter_bpf_sk_storage_map.c b/tools/testing/selftests/bpf/progs/bpf_iter_bpf_sk_storage_map.c index 6b70ccaba301..c7b8e006b171 100644 --- a/tools/testing/selftests/bpf/progs/bpf_iter_bpf_sk_storage_map.c +++ b/tools/testing/selftests/bpf/progs/bpf_iter_bpf_sk_storage_map.c @@ -16,19 +16,37 @@ struct { __u32 val_sum = 0; __u32 ipv6_sk_count = 0; +__u32 to_add_val = 0; SEC("iter/bpf_sk_storage_map") -int dump_bpf_sk_storage_map(struct bpf_iter__bpf_sk_storage_map *ctx) +int rw_bpf_sk_storage_map(struct bpf_iter__bpf_sk_storage_map *ctx) { struct sock *sk = ctx->sk; __u32 *val = ctx->value; - if (sk == (void *)0 || val == (void *)0) + if (sk == NULL || val == NULL) return 0; if (sk->sk_family == AF_INET6) ipv6_sk_count++; val_sum += *val; + + *val += to_add_val; + + return 0; +} + +SEC("iter/bpf_sk_storage_map") +int oob_write_bpf_sk_storage_map(struct bpf_iter__bpf_sk_storage_map *ctx) +{ + struct sock *sk = ctx->sk; + __u32 *val = ctx->value; + + if (sk == NULL || val == NULL) + return 0; + + *(val + 1) = 0xdeadbeef; + return 0; } diff --git a/tools/testing/selftests/bpf/progs/lru_bug.c b/tools/testing/selftests/bpf/progs/lru_bug.c new file mode 100644 index 000000000000..687081a724b3 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/lru_bug.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <vmlinux.h> +#include <bpf/bpf_tracing.h> +#include <bpf/bpf_helpers.h> + +struct map_value { + struct task_struct __kptr *ptr; +}; + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __uint(max_entries, 1); + __type(key, int); + __type(value, struct map_value); +} lru_map SEC(".maps"); + +int pid = 0; +int result = 1; + +SEC("fentry/bpf_ktime_get_ns") +int printk(void *ctx) +{ + struct map_value v = {}; + + if (pid == bpf_get_current_task_btf()->pid) + bpf_map_update_elem(&lru_map, &(int){0}, &v, 0); + return 0; +} + +SEC("fentry/do_nanosleep") +int nanosleep(void *ctx) +{ + struct map_value val = {}, *v; + struct task_struct *current; + + bpf_map_update_elem(&lru_map, &(int){0}, &val, 0); + v = bpf_map_lookup_elem(&lru_map, &(int){0}); + if (!v) + return 0; + bpf_map_delete_elem(&lru_map, &(int){0}); + current = bpf_get_current_task_btf(); + v->ptr = current; + pid = current->pid; + bpf_ktime_get_ns(); + result = !v->ptr; + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile index c7f47429d6cd..4c122f1b1737 100644 --- a/tools/testing/selftests/kvm/Makefile +++ b/tools/testing/selftests/kvm/Makefile @@ -4,6 +4,8 @@ include ../../../build/Build.include all: top_srcdir = ../../../.. +include $(top_srcdir)/scripts/subarch.include +ARCH ?= $(SUBARCH) # For cross-builds to work, UNAME_M has to map to ARCH and arch specific # directories and targets in this Makefile. "uname -m" doesn't map to @@ -197,7 +199,8 @@ endif CFLAGS += -Wall -Wstrict-prototypes -Wuninitialized -O2 -g -std=gnu99 \ -fno-stack-protector -fno-PIE -I$(LINUX_TOOL_INCLUDE) \ -I$(LINUX_TOOL_ARCH_INCLUDE) -I$(LINUX_HDR_PATH) -Iinclude \ - -I$(<D) -Iinclude/$(UNAME_M) -I.. $(EXTRA_CFLAGS) $(KHDR_INCLUDES) + -I$(<D) -Iinclude/$(UNAME_M) -I ../rseq -I.. $(EXTRA_CFLAGS) \ + $(KHDR_INCLUDES) no-pie-option := $(call try-run, echo 'int main() { return 0; }' | \ $(CC) -Werror -no-pie -x c - -o "$$TMP", -no-pie) @@ -206,7 +209,7 @@ no-pie-option := $(call try-run, echo 'int main() { return 0; }' | \ pgste-option = $(call try-run, echo 'int main() { return 0; }' | \ $(CC) -Werror -Wl$(comma)--s390-pgste -x c - -o "$$TMP",-Wl$(comma)--s390-pgste) - +LDLIBS += -ldl LDFLAGS += -pthread $(no-pie-option) $(pgste-option) # After inclusion, $(OUTPUT) is defined and diff --git a/tools/testing/selftests/kvm/rseq_test.c b/tools/testing/selftests/kvm/rseq_test.c index a54d4d05a058..fac248a43666 100644 --- a/tools/testing/selftests/kvm/rseq_test.c +++ b/tools/testing/selftests/kvm/rseq_test.c @@ -20,15 +20,7 @@ #include "processor.h" #include "test_util.h" -static __thread volatile struct rseq __rseq = { - .cpu_id = RSEQ_CPU_ID_UNINITIALIZED, -}; - -/* - * Use an arbitrary, bogus signature for configuring rseq, this test does not - * actually enter an rseq critical section. - */ -#define RSEQ_SIG 0xdeadbeef +#include "../rseq/rseq.c" /* * Any bug related to task migration is likely to be timing-dependent; perform @@ -49,12 +41,16 @@ static void guest_code(void) GUEST_SYNC(0); } -static void sys_rseq(int flags) +/* + * We have to perform direct system call for getcpu() because it's + * not available until glic 2.29. + */ +static void sys_getcpu(unsigned *cpu) { int r; - r = syscall(__NR_rseq, &__rseq, sizeof(__rseq), flags, RSEQ_SIG); - TEST_ASSERT(!r, "rseq failed, errno = %d (%s)", errno, strerror(errno)); + r = syscall(__NR_getcpu, cpu, NULL, NULL); + TEST_ASSERT(!r, "getcpu failed, errno = %d (%s)", errno, strerror(errno)); } static int next_cpu(int cpu) @@ -101,7 +97,7 @@ static void *migration_worker(void *__rseq_tid) atomic_inc(&seq_cnt); /* - * Ensure the odd count is visible while sched_getcpu() isn't + * Ensure the odd count is visible while getcpu() isn't * stable, i.e. while changing affinity is in-progress. */ smp_wmb(); @@ -142,10 +138,10 @@ static void *migration_worker(void *__rseq_tid) * check completes. * * 3. To ensure the read-side makes efficient forward progress, - * e.g. if sched_getcpu() involves a syscall. Stalling the - * read-side means the test will spend more time waiting for - * sched_getcpu() to stabilize and less time trying to hit - * the timing-dependent bug. + * e.g. if getcpu() involves a syscall. Stalling the read-side + * means the test will spend more time waiting for getcpu() + * to stabilize and less time trying to hit the timing-dependent + * bug. * * Because any bug in this area is likely to be timing-dependent, * run with a range of delays at 1us intervals from 1us to 10us @@ -218,7 +214,9 @@ int main(int argc, char *argv[]) calc_min_max_cpu(); - sys_rseq(0); + r = rseq_register_current_thread(); + TEST_ASSERT(!r, "rseq_register_current_thread failed, errno = %d (%s)", + errno, strerror(errno)); /* * Create and run a dummy VM that immediately exits to userspace via @@ -238,9 +236,9 @@ int main(int argc, char *argv[]) /* * Verify rseq's CPU matches sched's CPU. Ensure migration - * doesn't occur between sched_getcpu() and reading the rseq - * cpu_id by rereading both if the sequence count changes, or - * if the count is odd (migration in-progress). + * doesn't occur between getcpu() and reading the rseq cpu_id + * by rereading both if the sequence count changes, or if the + * count is odd (migration in-progress). */ do { /* @@ -250,13 +248,13 @@ int main(int argc, char *argv[]) snapshot = atomic_read(&seq_cnt) & ~1; /* - * Ensure reading sched_getcpu() and rseq.cpu_id - * complete in a single "no migration" window, i.e. are - * not reordered across the seq_cnt reads. + * Ensure calling getcpu() and reading rseq.cpu_id complete + * in a single "no migration" window, i.e. are not reordered + * across the seq_cnt reads. */ smp_rmb(); - cpu = sched_getcpu(); - rseq_cpu = READ_ONCE(__rseq.cpu_id); + sys_getcpu(&cpu); + rseq_cpu = rseq_current_cpu_raw(); smp_rmb(); } while (snapshot != atomic_read(&seq_cnt)); @@ -267,9 +265,9 @@ int main(int argc, char *argv[]) /* * Sanity check that the test was able to enter the guest a reasonable * number of times, e.g. didn't get stalled too often/long waiting for - * sched_getcpu() to stabilize. A 2:1 migration:KVM_RUN ratio is a - * fairly conservative ratio on x86-64, which can do _more_ KVM_RUNs - * than migrations given the 1us+ delay in the migration task. + * getcpu() to stabilize. A 2:1 migration:KVM_RUN ratio is a fairly + * conservative ratio on x86-64, which can do _more_ KVM_RUNs than + * migrations given the 1us+ delay in the migration task. */ TEST_ASSERT(i > (NR_TASK_MIGRATIONS / 2), "Only performed %d KVM_RUNs, task stalled too much?\n", i); @@ -278,7 +276,7 @@ int main(int argc, char *argv[]) kvm_vm_free(vm); - sys_rseq(RSEQ_FLAG_UNREGISTER); + rseq_unregister_current_thread(); return 0; } diff --git a/tools/testing/selftests/kvm/x86_64/vmx_pmu_caps_test.c b/tools/testing/selftests/kvm/x86_64/vmx_pmu_caps_test.c index 6ec901dab61e..069589c52f41 100644 --- a/tools/testing/selftests/kvm/x86_64/vmx_pmu_caps_test.c +++ b/tools/testing/selftests/kvm/x86_64/vmx_pmu_caps_test.c @@ -59,6 +59,7 @@ int main(int argc, char *argv[]) int ret; union cpuid10_eax eax; union perf_capabilities host_cap; + uint64_t val; host_cap.capabilities = kvm_get_feature_msr(MSR_IA32_PERF_CAPABILITIES); host_cap.capabilities &= (PMU_CAP_FW_WRITES | PMU_CAP_LBR_FMT); @@ -91,11 +92,17 @@ int main(int argc, char *argv[]) vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, host_cap.lbr_format); ASSERT_EQ(vcpu_get_msr(vcpu, MSR_IA32_PERF_CAPABILITIES), (u64)host_cap.lbr_format); - /* testcase 3, check invalid LBR format is rejected */ - /* Note, on Arch LBR capable platforms, LBR_FMT in perf capability msr is 0x3f, - * to avoid the failure, use a true invalid format 0x30 for the test. */ - ret = _vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, 0x30); - TEST_ASSERT(ret == 0, "Bad PERF_CAPABILITIES didn't fail."); + /* + * Testcase 3, check that an "invalid" LBR format is rejected. Only an + * exact match of the host's format (and 0/disabled) is allowed. + */ + for (val = 1; val <= PMU_CAP_LBR_FMT; val++) { + if (val == (host_cap.capabilities & PMU_CAP_LBR_FMT)) + continue; + + ret = _vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, val); + TEST_ASSERT(!ret, "Bad LBR FMT = 0x%lx didn't fail", val); + } printf("Completed perf capability tests.\n"); kvm_vm_free(vm); diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore index 892306bdb47d..0e5751af6247 100644 --- a/tools/testing/selftests/net/.gitignore +++ b/tools/testing/selftests/net/.gitignore @@ -38,4 +38,5 @@ ioam6_parser toeplitz tun cmsg_sender -unix_connect
\ No newline at end of file +unix_connect +tap
\ No newline at end of file diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index e2dfef8b78a7..c0ee2955fe54 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -57,7 +57,7 @@ TEST_GEN_FILES += ipsec TEST_GEN_FILES += ioam6_parser TEST_GEN_FILES += gro TEST_GEN_PROGS = reuseport_bpf reuseport_bpf_cpu reuseport_bpf_numa -TEST_GEN_PROGS += reuseport_dualstack reuseaddr_conflict tls tun +TEST_GEN_PROGS += reuseport_dualstack reuseaddr_conflict tls tun tap TEST_GEN_FILES += toeplitz TEST_GEN_FILES += cmsg_sender TEST_GEN_FILES += stress_reuseport_listen diff --git a/tools/testing/selftests/net/forwarding/custom_multipath_hash.sh b/tools/testing/selftests/net/forwarding/custom_multipath_hash.sh index a15d21dc035a..56eb83d1a3bd 100755 --- a/tools/testing/selftests/net/forwarding/custom_multipath_hash.sh +++ b/tools/testing/selftests/net/forwarding/custom_multipath_hash.sh @@ -181,37 +181,43 @@ ping_ipv6() send_src_ipv4() { - $MZ $h1 -q -p 64 -A "198.51.100.2-198.51.100.253" -B 203.0.113.2 \ + ip vrf exec v$h1 $MZ $h1 -q -p 64 \ + -A "198.51.100.2-198.51.100.253" -B 203.0.113.2 \ -d 1msec -c 50 -t udp "sp=20000,dp=30000" } send_dst_ipv4() { - $MZ $h1 -q -p 64 -A 198.51.100.2 -B "203.0.113.2-203.0.113.253" \ + ip vrf exec v$h1 $MZ $h1 -q -p 64 \ + -A 198.51.100.2 -B "203.0.113.2-203.0.113.253" \ -d 1msec -c 50 -t udp "sp=20000,dp=30000" } send_src_udp4() { - $MZ $h1 -q -p 64 -A 198.51.100.2 -B 203.0.113.2 \ + ip vrf exec v$h1 $MZ $h1 -q -p 64 \ + -A 198.51.100.2 -B 203.0.113.2 \ -d 1msec -t udp "sp=0-32768,dp=30000" } send_dst_udp4() { - $MZ $h1 -q -p 64 -A 198.51.100.2 -B 203.0.113.2 \ + ip vrf exec v$h1 $MZ $h1 -q -p 64 \ + -A 198.51.100.2 -B 203.0.113.2 \ -d 1msec -t udp "sp=20000,dp=0-32768" } send_src_ipv6() { - $MZ -6 $h1 -q -p 64 -A "2001:db8:1::2-2001:db8:1::fd" -B 2001:db8:4::2 \ + ip vrf exec v$h1 $MZ -6 $h1 -q -p 64 \ + -A "2001:db8:1::2-2001:db8:1::fd" -B 2001:db8:4::2 \ -d 1msec -c 50 -t udp "sp=20000,dp=30000" } send_dst_ipv6() { - $MZ -6 $h1 -q -p 64 -A 2001:db8:1::2 -B "2001:db8:4::2-2001:db8:4::fd" \ + ip vrf exec v$h1 $MZ -6 $h1 -q -p 64 \ + -A 2001:db8:1::2 -B "2001:db8:4::2-2001:db8:4::fd" \ -d 1msec -c 50 -t udp "sp=20000,dp=30000" } @@ -226,13 +232,15 @@ send_flowlabel() send_src_udp6() { - $MZ -6 $h1 -q -p 64 -A 2001:db8:1::2 -B 2001:db8:4::2 \ + ip vrf exec v$h1 $MZ -6 $h1 -q -p 64 \ + -A 2001:db8:1::2 -B 2001:db8:4::2 \ -d 1msec -t udp "sp=0-32768,dp=30000" } send_dst_udp6() { - $MZ -6 $h1 -q -p 64 -A 2001:db8:1::2 -B 2001:db8:4::2 \ + ip vrf exec v$h1 $MZ -6 $h1 -q -p 64 \ + -A 2001:db8:1::2 -B 2001:db8:4::2 \ -d 1msec -t udp "sp=20000,dp=0-32768" } diff --git a/tools/testing/selftests/net/forwarding/gre_custom_multipath_hash.sh b/tools/testing/selftests/net/forwarding/gre_custom_multipath_hash.sh index a73f52efcb6c..0446db9c6f74 100755 --- a/tools/testing/selftests/net/forwarding/gre_custom_multipath_hash.sh +++ b/tools/testing/selftests/net/forwarding/gre_custom_multipath_hash.sh @@ -276,37 +276,43 @@ ping_ipv6() send_src_ipv4() { - $MZ $h1 -q -p 64 -A "198.51.100.2-198.51.100.253" -B 203.0.113.2 \ + ip vrf exec v$h1 $MZ $h1 -q -p 64 \ + -A "198.51.100.2-198.51.100.253" -B 203.0.113.2 \ -d 1msec -c 50 -t udp "sp=20000,dp=30000" } send_dst_ipv4() { - $MZ $h1 -q -p 64 -A 198.51.100.2 -B "203.0.113.2-203.0.113.253" \ + ip vrf exec v$h1 $MZ $h1 -q -p 64 \ + -A 198.51.100.2 -B "203.0.113.2-203.0.113.253" \ -d 1msec -c 50 -t udp "sp=20000,dp=30000" } send_src_udp4() { - $MZ $h1 -q -p 64 -A 198.51.100.2 -B 203.0.113.2 \ + ip vrf exec v$h1 $MZ $h1 -q -p 64 \ + -A 198.51.100.2 -B 203.0.113.2 \ -d 1msec -t udp "sp=0-32768,dp=30000" } send_dst_udp4() { - $MZ $h1 -q -p 64 -A 198.51.100.2 -B 203.0.113.2 \ + ip vrf exec v$h1 $MZ $h1 -q -p 64 \ + -A 198.51.100.2 -B 203.0.113.2 \ -d 1msec -t udp "sp=20000,dp=0-32768" } send_src_ipv6() { - $MZ -6 $h1 -q -p 64 -A "2001:db8:1::2-2001:db8:1::fd" -B 2001:db8:2::2 \ + ip vrf exec v$h1 $MZ -6 $h1 -q -p 64 \ + -A "2001:db8:1::2-2001:db8:1::fd" -B 2001:db8:2::2 \ -d 1msec -c 50 -t udp "sp=20000,dp=30000" } send_dst_ipv6() { - $MZ -6 $h1 -q -p 64 -A 2001:db8:1::2 -B "2001:db8:2::2-2001:db8:2::fd" \ + ip vrf exec v$h1 $MZ -6 $h1 -q -p 64 \ + -A 2001:db8:1::2 -B "2001:db8:2::2-2001:db8:2::fd" \ -d 1msec -c 50 -t udp "sp=20000,dp=30000" } @@ -321,13 +327,15 @@ send_flowlabel() send_src_udp6() { - $MZ -6 $h1 -q -p 64 -A 2001:db8:1::2 -B 2001:db8:2::2 \ + ip vrf exec v$h1 $MZ -6 $h1 -q -p 64 \ + -A 2001:db8:1::2 -B 2001:db8:2::2 \ -d 1msec -t udp "sp=0-32768,dp=30000" } send_dst_udp6() { - $MZ -6 $h1 -q -p 64 -A 2001:db8:1::2 -B 2001:db8:2::2 \ + ip vrf exec v$h1 $MZ -6 $h1 -q -p 64 \ + -A 2001:db8:1::2 -B 2001:db8:2::2 \ -d 1msec -t udp "sp=20000,dp=0-32768" } diff --git a/tools/testing/selftests/net/forwarding/ip6gre_custom_multipath_hash.sh b/tools/testing/selftests/net/forwarding/ip6gre_custom_multipath_hash.sh index 8fea2c2e0b25..d40183b4eccc 100755 --- a/tools/testing/selftests/net/forwarding/ip6gre_custom_multipath_hash.sh +++ b/tools/testing/selftests/net/forwarding/ip6gre_custom_multipath_hash.sh @@ -278,37 +278,43 @@ ping_ipv6() send_src_ipv4() { - $MZ $h1 -q -p 64 -A "198.51.100.2-198.51.100.253" -B 203.0.113.2 \ + ip vrf exec v$h1 $MZ $h1 -q -p 64 \ + -A "198.51.100.2-198.51.100.253" -B 203.0.113.2 \ -d 1msec -c 50 -t udp "sp=20000,dp=30000" } send_dst_ipv4() { - $MZ $h1 -q -p 64 -A 198.51.100.2 -B "203.0.113.2-203.0.113.253" \ + ip vrf exec v$h1 $MZ $h1 -q -p 64 \ + -A 198.51.100.2 -B "203.0.113.2-203.0.113.253" \ -d 1msec -c 50 -t udp "sp=20000,dp=30000" } send_src_udp4() { - $MZ $h1 -q -p 64 -A 198.51.100.2 -B 203.0.113.2 \ + ip vrf exec v$h1 $MZ $h1 -q -p 64 \ + -A 198.51.100.2 -B 203.0.113.2 \ -d 1msec -t udp "sp=0-32768,dp=30000" } send_dst_udp4() { - $MZ $h1 -q -p 64 -A 198.51.100.2 -B 203.0.113.2 \ + ip vrf exec v$h1 $MZ $h1 -q -p 64 \ + -A 198.51.100.2 -B 203.0.113.2 \ -d 1msec -t udp "sp=20000,dp=0-32768" } send_src_ipv6() { - $MZ -6 $h1 -q -p 64 -A "2001:db8:1::2-2001:db8:1::fd" -B 2001:db8:2::2 \ + ip vrf exec v$h1 $MZ -6 $h1 -q -p 64 \ + -A "2001:db8:1::2-2001:db8:1::fd" -B 2001:db8:2::2 \ -d 1msec -c 50 -t udp "sp=20000,dp=30000" } send_dst_ipv6() { - $MZ -6 $h1 -q -p 64 -A 2001:db8:1::2 -B "2001:db8:2::2-2001:db8:2::fd" \ + ip vrf exec v$h1 $MZ -6 $h1 -q -p 64 \ + -A 2001:db8:1::2 -B "2001:db8:2::2-2001:db8:2::fd" \ -d 1msec -c 50 -t udp "sp=20000,dp=30000" } @@ -323,13 +329,15 @@ send_flowlabel() send_src_udp6() { - $MZ -6 $h1 -q -p 64 -A 2001:db8:1::2 -B 2001:db8:2::2 \ + ip vrf exec v$h1 $MZ -6 $h1 -q -p 64 \ + -A 2001:db8:1::2 -B 2001:db8:2::2 \ -d 1msec -t udp "sp=0-32768,dp=30000" } send_dst_udp6() { - $MZ -6 $h1 -q -p 64 -A 2001:db8:1::2 -B 2001:db8:2::2 \ + ip vrf exec v$h1 $MZ -6 $h1 -q -p 64 \ + -A 2001:db8:1::2 -B 2001:db8:2::2 \ -d 1msec -t udp "sp=20000,dp=0-32768" } diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.c b/tools/testing/selftests/net/mptcp/mptcp_connect.c index e2ea6c126c99..24d4e9cb617e 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_connect.c +++ b/tools/testing/selftests/net/mptcp/mptcp_connect.c @@ -553,6 +553,18 @@ static void set_nonblock(int fd, bool nonblock) fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); } +static void shut_wr(int fd) +{ + /* Close our write side, ev. give some time + * for address notification and/or checking + * the current status + */ + if (cfg_wait) + usleep(cfg_wait); + + shutdown(fd, SHUT_WR); +} + static int copyfd_io_poll(int infd, int peerfd, int outfd, bool *in_closed_after_out) { struct pollfd fds = { @@ -630,14 +642,7 @@ static int copyfd_io_poll(int infd, int peerfd, int outfd, bool *in_closed_after /* ... and peer also closed already */ break; - /* ... but we still receive. - * Close our write side, ev. give some time - * for address notification and/or checking - * the current status - */ - if (cfg_wait) - usleep(cfg_wait); - shutdown(peerfd, SHUT_WR); + shut_wr(peerfd); } else { if (errno == EINTR) continue; @@ -767,7 +772,7 @@ static int copyfd_io_mmap(int infd, int peerfd, int outfd, if (err) return err; - shutdown(peerfd, SHUT_WR); + shut_wr(peerfd); err = do_recvfile(peerfd, outfd); *in_closed_after_out = true; @@ -791,6 +796,9 @@ static int copyfd_io_sendfile(int infd, int peerfd, int outfd, err = do_sendfile(infd, peerfd, size); if (err) return err; + + shut_wr(peerfd); + err = do_recvfile(peerfd, outfd); *in_closed_after_out = true; } diff --git a/tools/testing/selftests/net/tap.c b/tools/testing/selftests/net/tap.c new file mode 100644 index 000000000000..247c3b3ac1c9 --- /dev/null +++ b/tools/testing/selftests/net/tap.c @@ -0,0 +1,434 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define _GNU_SOURCE + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <net/if.h> +#include <linux/if_tun.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <linux/virtio_net.h> +#include <netinet/ip.h> +#include <netinet/udp.h> +#include "../kselftest_harness.h" + +static const char param_dev_tap_name[] = "xmacvtap0"; +static const char param_dev_dummy_name[] = "xdummy0"; +static unsigned char param_hwaddr_src[] = { 0x00, 0xfe, 0x98, 0x14, 0x22, 0x42 }; +static unsigned char param_hwaddr_dest[] = { + 0x00, 0xfe, 0x98, 0x94, 0xd2, 0x43 +}; + +#define MAX_RTNL_PAYLOAD (2048) +#define PKT_DATA 0xCB +#define TEST_PACKET_SZ (sizeof(struct virtio_net_hdr) + ETH_HLEN + ETH_MAX_MTU) + +static struct rtattr *rtattr_add(struct nlmsghdr *nh, unsigned short type, + unsigned short len) +{ + struct rtattr *rta = + (struct rtattr *)((uint8_t *)nh + RTA_ALIGN(nh->nlmsg_len)); + rta->rta_type = type; + rta->rta_len = RTA_LENGTH(len); + nh->nlmsg_len = RTA_ALIGN(nh->nlmsg_len) + RTA_ALIGN(rta->rta_len); + return rta; +} + +static struct rtattr *rtattr_begin(struct nlmsghdr *nh, unsigned short type) +{ + return rtattr_add(nh, type, 0); +} + +static void rtattr_end(struct nlmsghdr *nh, struct rtattr *attr) +{ + uint8_t *end = (uint8_t *)nh + nh->nlmsg_len; + + attr->rta_len = end - (uint8_t *)attr; +} + +static struct rtattr *rtattr_add_str(struct nlmsghdr *nh, unsigned short type, + const char *s) +{ + struct rtattr *rta = rtattr_add(nh, type, strlen(s)); + + memcpy(RTA_DATA(rta), s, strlen(s)); + return rta; +} + +static struct rtattr *rtattr_add_strsz(struct nlmsghdr *nh, unsigned short type, + const char *s) +{ + struct rtattr *rta = rtattr_add(nh, type, strlen(s) + 1); + + strcpy(RTA_DATA(rta), s); + return rta; +} + +static struct rtattr *rtattr_add_any(struct nlmsghdr *nh, unsigned short type, + const void *arr, size_t len) +{ + struct rtattr *rta = rtattr_add(nh, type, len); + + memcpy(RTA_DATA(rta), arr, len); + return rta; +} + +static int dev_create(const char *dev, const char *link_type, + int (*fill_rtattr)(struct nlmsghdr *nh), + int (*fill_info_data)(struct nlmsghdr *nh)) +{ + struct { + struct nlmsghdr nh; + struct ifinfomsg info; + unsigned char data[MAX_RTNL_PAYLOAD]; + } req; + struct rtattr *link_info, *info_data; + int ret, rtnl; + + rtnl = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (rtnl < 0) { + fprintf(stderr, "%s: socket %s\n", __func__, strerror(errno)); + return 1; + } + + memset(&req, 0, sizeof(req)); + req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.info)); + req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE; + req.nh.nlmsg_type = RTM_NEWLINK; + + req.info.ifi_family = AF_UNSPEC; + req.info.ifi_type = 1; + req.info.ifi_index = 0; + req.info.ifi_flags = IFF_BROADCAST | IFF_UP; + req.info.ifi_change = 0xffffffff; + + rtattr_add_str(&req.nh, IFLA_IFNAME, dev); + + if (fill_rtattr) { + ret = fill_rtattr(&req.nh); + if (ret) + return ret; + } + + link_info = rtattr_begin(&req.nh, IFLA_LINKINFO); + + rtattr_add_strsz(&req.nh, IFLA_INFO_KIND, link_type); + + if (fill_info_data) { + info_data = rtattr_begin(&req.nh, IFLA_INFO_DATA); + ret = fill_info_data(&req.nh); + if (ret) + return ret; + rtattr_end(&req.nh, info_data); + } + + rtattr_end(&req.nh, link_info); + + ret = send(rtnl, &req, req.nh.nlmsg_len, 0); + if (ret < 0) + fprintf(stderr, "%s: send %s\n", __func__, strerror(errno)); + ret = (unsigned int)ret != req.nh.nlmsg_len; + + close(rtnl); + return ret; +} + +static int dev_delete(const char *dev) +{ + struct { + struct nlmsghdr nh; + struct ifinfomsg info; + unsigned char data[MAX_RTNL_PAYLOAD]; + } req; + int ret, rtnl; + + rtnl = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (rtnl < 0) { + fprintf(stderr, "%s: socket %s\n", __func__, strerror(errno)); + return 1; + } + + memset(&req, 0, sizeof(req)); + req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.info)); + req.nh.nlmsg_flags = NLM_F_REQUEST; + req.nh.nlmsg_type = RTM_DELLINK; + + req.info.ifi_family = AF_UNSPEC; + + rtattr_add_str(&req.nh, IFLA_IFNAME, dev); + + ret = send(rtnl, &req, req.nh.nlmsg_len, 0); + if (ret < 0) + fprintf(stderr, "%s: send %s\n", __func__, strerror(errno)); + + ret = (unsigned int)ret != req.nh.nlmsg_len; + + close(rtnl); + return ret; +} + +static int macvtap_fill_rtattr(struct nlmsghdr *nh) +{ + int ifindex; + + ifindex = if_nametoindex(param_dev_dummy_name); + if (ifindex == 0) { + fprintf(stderr, "%s: ifindex %s\n", __func__, strerror(errno)); + return -errno; + } + + rtattr_add_any(nh, IFLA_LINK, &ifindex, sizeof(ifindex)); + rtattr_add_any(nh, IFLA_ADDRESS, param_hwaddr_src, ETH_ALEN); + + return 0; +} + +static int opentap(const char *devname) +{ + int ifindex; + char buf[256]; + int fd; + struct ifreq ifr; + + ifindex = if_nametoindex(devname); + if (ifindex == 0) { + fprintf(stderr, "%s: ifindex %s\n", __func__, strerror(errno)); + return -errno; + } + + sprintf(buf, "/dev/tap%d", ifindex); + fd = open(buf, O_RDWR | O_NONBLOCK); + if (fd < 0) { + fprintf(stderr, "%s: open %s\n", __func__, strerror(errno)); + return -errno; + } + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, devname); + ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_VNET_HDR | IFF_MULTI_QUEUE; + if (ioctl(fd, TUNSETIFF, &ifr, sizeof(ifr)) < 0) + return -errno; + return fd; +} + +size_t build_eth(uint8_t *buf, uint16_t proto) +{ + struct ethhdr *eth = (struct ethhdr *)buf; + + eth->h_proto = htons(proto); + memcpy(eth->h_source, param_hwaddr_src, ETH_ALEN); + memcpy(eth->h_dest, param_hwaddr_dest, ETH_ALEN); + + return ETH_HLEN; +} + +static uint32_t add_csum(const uint8_t *buf, int len) +{ + uint32_t sum = 0; + uint16_t *sbuf = (uint16_t *)buf; + + while (len > 1) { + sum += *sbuf++; + len -= 2; + } + + if (len) + sum += *(uint8_t *)sbuf; + + return sum; +} + +static uint16_t finish_ip_csum(uint32_t sum) +{ + uint16_t lo = sum & 0xffff; + uint16_t hi = sum >> 16; + + return ~(lo + hi); + +} + +static uint16_t build_ip_csum(const uint8_t *buf, int len, + uint32_t sum) +{ + sum += add_csum(buf, len); + return finish_ip_csum(sum); +} + +static int build_ipv4_header(uint8_t *buf, int payload_len) +{ + struct iphdr *iph = (struct iphdr *)buf; + + iph->ihl = 5; + iph->version = 4; + iph->ttl = 8; + iph->tot_len = + htons(sizeof(*iph) + sizeof(struct udphdr) + payload_len); + iph->id = htons(1337); + iph->protocol = IPPROTO_UDP; + iph->saddr = htonl((172 << 24) | (17 << 16) | 2); + iph->daddr = htonl((172 << 24) | (17 << 16) | 1); + iph->check = build_ip_csum(buf, iph->ihl << 2, 0); + + return iph->ihl << 2; +} + +static int build_udp_packet(uint8_t *buf, int payload_len, bool csum_off) +{ + const int ip4alen = sizeof(uint32_t); + struct udphdr *udph = (struct udphdr *)buf; + int len = sizeof(*udph) + payload_len; + uint32_t sum = 0; + + udph->source = htons(22); + udph->dest = htons(58822); + udph->len = htons(len); + + memset(buf + sizeof(struct udphdr), PKT_DATA, payload_len); + + sum = add_csum(buf - 2 * ip4alen, 2 * ip4alen); + sum += htons(IPPROTO_UDP) + udph->len; + + if (!csum_off) + sum += add_csum(buf, len); + + udph->check = finish_ip_csum(sum); + + return sizeof(*udph) + payload_len; +} + +size_t build_test_packet_valid_udp_gso(uint8_t *buf, size_t payload_len) +{ + uint8_t *cur = buf; + struct virtio_net_hdr *vh = (struct virtio_net_hdr *)buf; + + vh->hdr_len = ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr); + vh->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; + vh->csum_start = ETH_HLEN + sizeof(struct iphdr); + vh->csum_offset = __builtin_offsetof(struct udphdr, check); + vh->gso_type = VIRTIO_NET_HDR_GSO_UDP; + vh->gso_size = ETH_DATA_LEN - sizeof(struct iphdr); + cur += sizeof(*vh); + + cur += build_eth(cur, ETH_P_IP); + cur += build_ipv4_header(cur, payload_len); + cur += build_udp_packet(cur, payload_len, true); + + return cur - buf; +} + +size_t build_test_packet_valid_udp_csum(uint8_t *buf, size_t payload_len) +{ + uint8_t *cur = buf; + struct virtio_net_hdr *vh = (struct virtio_net_hdr *)buf; + + vh->flags = VIRTIO_NET_HDR_F_DATA_VALID; + vh->gso_type = VIRTIO_NET_HDR_GSO_NONE; + cur += sizeof(*vh); + + cur += build_eth(cur, ETH_P_IP); + cur += build_ipv4_header(cur, payload_len); + cur += build_udp_packet(cur, payload_len, false); + + return cur - buf; +} + +size_t build_test_packet_crash_tap_invalid_eth_proto(uint8_t *buf, + size_t payload_len) +{ + uint8_t *cur = buf; + struct virtio_net_hdr *vh = (struct virtio_net_hdr *)buf; + + vh->hdr_len = ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr); + vh->flags = 0; + vh->gso_type = VIRTIO_NET_HDR_GSO_UDP; + vh->gso_size = ETH_DATA_LEN - sizeof(struct iphdr); + cur += sizeof(*vh); + + cur += build_eth(cur, 0); + cur += sizeof(struct iphdr) + sizeof(struct udphdr); + cur += build_ipv4_header(cur, payload_len); + cur += build_udp_packet(cur, payload_len, true); + cur += payload_len; + + return cur - buf; +} + +FIXTURE(tap) +{ + int fd; +}; + +FIXTURE_SETUP(tap) +{ + int ret; + + ret = dev_create(param_dev_dummy_name, "dummy", NULL, NULL); + EXPECT_EQ(ret, 0); + + ret = dev_create(param_dev_tap_name, "macvtap", macvtap_fill_rtattr, + NULL); + EXPECT_EQ(ret, 0); + + self->fd = opentap(param_dev_tap_name); + ASSERT_GE(self->fd, 0); +} + +FIXTURE_TEARDOWN(tap) +{ + int ret; + + if (self->fd != -1) + close(self->fd); + + ret = dev_delete(param_dev_tap_name); + EXPECT_EQ(ret, 0); + + ret = dev_delete(param_dev_dummy_name); + EXPECT_EQ(ret, 0); +} + +TEST_F(tap, test_packet_valid_udp_gso) +{ + uint8_t pkt[TEST_PACKET_SZ]; + size_t off; + int ret; + + memset(pkt, 0, sizeof(pkt)); + off = build_test_packet_valid_udp_gso(pkt, 1021); + ret = write(self->fd, pkt, off); + ASSERT_EQ(ret, off); +} + +TEST_F(tap, test_packet_valid_udp_csum) +{ + uint8_t pkt[TEST_PACKET_SZ]; + size_t off; + int ret; + + memset(pkt, 0, sizeof(pkt)); + off = build_test_packet_valid_udp_csum(pkt, 1024); + ret = write(self->fd, pkt, off); + ASSERT_EQ(ret, off); +} + +TEST_F(tap, test_packet_crash_tap_invalid_eth_proto) +{ + uint8_t pkt[TEST_PACKET_SZ]; + size_t off; + int ret; + + memset(pkt, 0, sizeof(pkt)); + off = build_test_packet_crash_tap_invalid_eth_proto(pkt, 1024); + ret = write(self->fd, pkt, off); + ASSERT_EQ(ret, -1); + ASSERT_EQ(errno, EINVAL); +} + +TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/netfilter/nft_trans_stress.sh b/tools/testing/selftests/netfilter/nft_trans_stress.sh index f1affd12c4b1..a7f62ad4f661 100755 --- a/tools/testing/selftests/netfilter/nft_trans_stress.sh +++ b/tools/testing/selftests/netfilter/nft_trans_stress.sh @@ -9,8 +9,27 @@ # Kselftest framework requirement - SKIP code is 4. ksft_skip=4 -testns=testns1 +testns=testns-$(mktemp -u "XXXXXXXX") + tables="foo bar baz quux" +global_ret=0 +eret=0 +lret=0 + +check_result() +{ + local r=$1 + local OK="PASS" + + if [ $r -ne 0 ] ;then + OK="FAIL" + global_ret=$r + fi + + echo "$OK: nft $2 test returned $r" + + eret=0 +} nft --version > /dev/null 2>&1 if [ $? -ne 0 ];then @@ -59,16 +78,66 @@ done) sleep 1 +ip netns exec "$testns" nft -f "$tmp" for i in $(seq 1 10) ; do ip netns exec "$testns" nft -f "$tmp" & done for table in $tables;do - randsleep=$((RANDOM%10)) + randsleep=$((RANDOM%2)) sleep $randsleep - ip netns exec "$testns" nft delete table inet $table 2>/dev/null + ip netns exec "$testns" nft delete table inet $table + lret=$? + if [ $lret -ne 0 ]; then + eret=$lret + fi done -randsleep=$((RANDOM%10)) -sleep $randsleep +check_result $eret "add/delete" + +for i in $(seq 1 10) ; do + (echo "flush ruleset"; cat "$tmp") | ip netns exec "$testns" nft -f /dev/stdin + + lret=$? + if [ $lret -ne 0 ]; then + eret=$lret + fi +done + +check_result $eret "reload" + +for i in $(seq 1 10) ; do + (echo "flush ruleset"; cat "$tmp" + echo "insert rule inet foo INPUT meta nftrace set 1" + echo "insert rule inet foo OUTPUT meta nftrace set 1" + ) | ip netns exec "$testns" nft -f /dev/stdin + lret=$? + if [ $lret -ne 0 ]; then + eret=$lret + fi + + (echo "flush ruleset"; cat "$tmp" + ) | ip netns exec "$testns" nft -f /dev/stdin + + lret=$? + if [ $lret -ne 0 ]; then + eret=$lret + fi +done + +check_result $eret "add/delete with nftrace enabled" + +echo "insert rule inet foo INPUT meta nftrace set 1" >> $tmp +echo "insert rule inet foo OUTPUT meta nftrace set 1" >> $tmp + +for i in $(seq 1 10) ; do + (echo "flush ruleset"; cat "$tmp") | ip netns exec "$testns" nft -f /dev/stdin + + lret=$? + if [ $lret -ne 0 ]; then + eret=1 + fi +done + +check_result $lret "add/delete with nftrace enabled" pkill -9 ping @@ -76,3 +145,5 @@ wait rm -f "$tmp" ip netns del "$testns" + +exit $global_ret diff --git a/tools/testing/selftests/wireguard/qemu/arch/riscv32.config b/tools/testing/selftests/wireguard/qemu/arch/riscv32.config index 0bd0e72d95d4..2fc36efb166d 100644 --- a/tools/testing/selftests/wireguard/qemu/arch/riscv32.config +++ b/tools/testing/selftests/wireguard/qemu/arch/riscv32.config @@ -1,3 +1,4 @@ +CONFIG_NONPORTABLE=y CONFIG_ARCH_RV32I=y CONFIG_MMU=y CONFIG_FPU=y diff --git a/tools/virtio/linux/kernel.h b/tools/virtio/linux/kernel.h index 0b493542e61a..21593bf97755 100644 --- a/tools/virtio/linux/kernel.h +++ b/tools/virtio/linux/kernel.h @@ -29,7 +29,6 @@ #define READ 0 #define WRITE 1 -typedef unsigned long long phys_addr_t; typedef unsigned long long dma_addr_t; typedef size_t __kernel_size_t; typedef unsigned int __wsum; @@ -136,6 +135,7 @@ static inline void *krealloc_array(void *p, size_t new_n, size_t new_size, gfp_t #endif #define dev_err(dev, format, ...) fprintf (stderr, format, ## __VA_ARGS__) #define dev_warn(dev, format, ...) fprintf (stderr, format, ## __VA_ARGS__) +#define dev_warn_once(dev, format, ...) fprintf (stderr, format, ## __VA_ARGS__) #define min(x, y) ({ \ typeof(x) _min1 = (x); \ diff --git a/tools/virtio/linux/vringh.h b/tools/virtio/linux/vringh.h index 9348957be56e..e11c6aece734 100644 --- a/tools/virtio/linux/vringh.h +++ b/tools/virtio/linux/vringh.h @@ -1 +1,2 @@ +#include <limits.h> #include "../../../include/linux/vringh.h" diff --git a/tools/virtio/virtio_test.c b/tools/virtio/virtio_test.c index 23f142af544a..86a410ddcedd 100644 --- a/tools/virtio/virtio_test.c +++ b/tools/virtio/virtio_test.c @@ -102,8 +102,8 @@ static void vq_reset(struct vq_info *info, int num, struct virtio_device *vdev) memset(info->ring, 0, vring_size(num, 4096)); vring_init(&info->vring, num, info->ring, 4096); - info->vq = __vring_new_virtqueue(info->idx, info->vring, vdev, true, - false, vq_notify, vq_callback, "test"); + info->vq = vring_new_virtqueue(info->idx, num, 4096, vdev, true, false, + info->ring, vq_notify, vq_callback, "test"); assert(info->vq); info->vq->priv = info; } diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 32896c845ffe..515dfe9d3bcf 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -484,6 +484,10 @@ static void kvm_vcpu_init(struct kvm_vcpu *vcpu, struct kvm *kvm, unsigned id) vcpu->ready = false; preempt_notifier_init(&vcpu->preempt_notifier, &kvm_preempt_ops); vcpu->last_used_slot = NULL; + + /* Fill the stats id string for the vcpu */ + snprintf(vcpu->stats_id, sizeof(vcpu->stats_id), "kvm-%d/vcpu-%d", + task_pid_nr(current), id); } static void kvm_vcpu_destroy(struct kvm_vcpu *vcpu) @@ -1017,21 +1021,21 @@ static void kvm_destroy_vm_debugfs(struct kvm *kvm) } } -static int kvm_create_vm_debugfs(struct kvm *kvm, int fd) +static int kvm_create_vm_debugfs(struct kvm *kvm, const char *fdname) { static DEFINE_MUTEX(kvm_debugfs_lock); struct dentry *dent; char dir_name[ITOA_MAX_LEN * 2]; struct kvm_stat_data *stat_data; const struct _kvm_stats_desc *pdesc; - int i, ret; + int i, ret = -ENOMEM; int kvm_debugfs_num_entries = kvm_vm_stats_header.num_desc + kvm_vcpu_stats_header.num_desc; if (!debugfs_initialized()) return 0; - snprintf(dir_name, sizeof(dir_name), "%d-%d", task_pid_nr(current), fd); + snprintf(dir_name, sizeof(dir_name), "%d-%s", task_pid_nr(current), fdname); mutex_lock(&kvm_debugfs_lock); dent = debugfs_lookup(dir_name, kvm_debugfs_dir); if (dent) { @@ -1050,13 +1054,13 @@ static int kvm_create_vm_debugfs(struct kvm *kvm, int fd) sizeof(*kvm->debugfs_stat_data), GFP_KERNEL_ACCOUNT); if (!kvm->debugfs_stat_data) - return -ENOMEM; + goto out_err; for (i = 0; i < kvm_vm_stats_header.num_desc; ++i) { pdesc = &kvm_vm_stats_desc[i]; stat_data = kzalloc(sizeof(*stat_data), GFP_KERNEL_ACCOUNT); if (!stat_data) - return -ENOMEM; + goto out_err; stat_data->kvm = kvm; stat_data->desc = pdesc; @@ -1071,7 +1075,7 @@ static int kvm_create_vm_debugfs(struct kvm *kvm, int fd) pdesc = &kvm_vcpu_stats_desc[i]; stat_data = kzalloc(sizeof(*stat_data), GFP_KERNEL_ACCOUNT); if (!stat_data) - return -ENOMEM; + goto out_err; stat_data->kvm = kvm; stat_data->desc = pdesc; @@ -1083,12 +1087,13 @@ static int kvm_create_vm_debugfs(struct kvm *kvm, int fd) } ret = kvm_arch_create_vm_debugfs(kvm); - if (ret) { - kvm_destroy_vm_debugfs(kvm); - return i; - } + if (ret) + goto out_err; return 0; +out_err: + kvm_destroy_vm_debugfs(kvm); + return ret; } /* @@ -1119,7 +1124,7 @@ int __weak kvm_arch_create_vm_debugfs(struct kvm *kvm) return 0; } -static struct kvm *kvm_create_vm(unsigned long type) +static struct kvm *kvm_create_vm(unsigned long type, const char *fdname) { struct kvm *kvm = kvm_arch_alloc_vm(); struct kvm_memslots *slots; @@ -1155,6 +1160,9 @@ static struct kvm *kvm_create_vm(unsigned long type) */ kvm->debugfs_dentry = ERR_PTR(-ENOENT); + snprintf(kvm->stats_id, sizeof(kvm->stats_id), "kvm-%d", + task_pid_nr(current)); + if (init_srcu_struct(&kvm->srcu)) goto out_err_no_srcu; if (init_srcu_struct(&kvm->irq_srcu)) @@ -1205,7 +1213,7 @@ static struct kvm *kvm_create_vm(unsigned long type) r = kvm_arch_post_init_vm(kvm); if (r) - goto out_err; + goto out_err_mmu_notifier; mutex_lock(&kvm_lock); list_add(&kvm->vm_list, &vm_list); @@ -1221,12 +1229,18 @@ static struct kvm *kvm_create_vm(unsigned long type) */ if (!try_module_get(kvm_chardev_ops.owner)) { r = -ENODEV; - goto out_err; + goto out_err_mmu_notifier; } + r = kvm_create_vm_debugfs(kvm, fdname); + if (r) + goto out_err; + return kvm; out_err: + module_put(kvm_chardev_ops.owner); +out_err_mmu_notifier: #if defined(CONFIG_MMU_NOTIFIER) && defined(KVM_ARCH_WANT_MMU_NOTIFIER) if (kvm->mmu_notifier.ops) mmu_notifier_unregister(&kvm->mmu_notifier, current->mm); @@ -3916,10 +3930,6 @@ static int kvm_vm_ioctl_create_vcpu(struct kvm *kvm, u32 id) if (r) goto unlock_vcpu_destroy; - /* Fill the stats id string for the vcpu */ - snprintf(vcpu->stats_id, sizeof(vcpu->stats_id), "kvm-%d/vcpu-%d", - task_pid_nr(current), id); - /* Now it's all set up, let userspace reach it */ kvm_get_kvm(kvm); r = create_vcpu_fd(vcpu); @@ -4886,28 +4896,30 @@ EXPORT_SYMBOL_GPL(file_is_kvm); static int kvm_dev_ioctl_create_vm(unsigned long type) { - int r; + char fdname[ITOA_MAX_LEN + 1]; + int r, fd; struct kvm *kvm; struct file *file; - kvm = kvm_create_vm(type); - if (IS_ERR(kvm)) - return PTR_ERR(kvm); + fd = get_unused_fd_flags(O_CLOEXEC); + if (fd < 0) + return fd; + + snprintf(fdname, sizeof(fdname), "%d", fd); + + kvm = kvm_create_vm(type, fdname); + if (IS_ERR(kvm)) { + r = PTR_ERR(kvm); + goto put_fd; + } + #ifdef CONFIG_KVM_MMIO r = kvm_coalesced_mmio_init(kvm); if (r < 0) goto put_kvm; #endif - r = get_unused_fd_flags(O_CLOEXEC); - if (r < 0) - goto put_kvm; - - snprintf(kvm->stats_id, sizeof(kvm->stats_id), - "kvm-%d", task_pid_nr(current)); - file = anon_inode_getfile("kvm-vm", &kvm_vm_fops, kvm, O_RDWR); if (IS_ERR(file)) { - put_unused_fd(r); r = PTR_ERR(file); goto put_kvm; } @@ -4918,18 +4930,15 @@ static int kvm_dev_ioctl_create_vm(unsigned long type) * cases it will be called by the final fput(file) and will take * care of doing kvm_put_kvm(kvm). */ - if (kvm_create_vm_debugfs(kvm, r) < 0) { - put_unused_fd(r); - fput(file); - return -ENOMEM; - } kvm_uevent_notify_change(KVM_EVENT_CREATE_VM, kvm); - fd_install(r, file); - return r; + fd_install(fd, file); + return fd; put_kvm: kvm_put_kvm(kvm); +put_fd: + put_unused_fd(fd); return r; } |